simplewebrtc.bundle.js 288 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802
  1. (function(e){if("function"==typeof bootstrap)bootstrap("simplewebrtc",e);else if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeSimpleWebRTC=e}else"undefined"!=typeof window?window.SimpleWebRTC=e():global.SimpleWebRTC=e()})(function(){var define,ses,bootstrap,module,exports;
  2. return (function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0].call(u.exports,function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s<n.length;s++)i(n[s]);return i})({1:[function(require,module,exports){
  3. var WebRTC = require('webrtc');
  4. var WildEmitter = require('wildemitter');
  5. var webrtcSupport = require('webrtcsupport');
  6. var attachMediaStream = require('attachmediastream');
  7. var mockconsole = require('mockconsole');
  8. var SocketIoConnection = require('./socketioconnection');
  9. function SimpleWebRTC(opts) {
  10. var self = this;
  11. var options = opts || {};
  12. var config = this.config = {
  13. url: 'https://signaling.simplewebrtc.com:443/',
  14. socketio: {/* 'force new connection':true*/},
  15. connection: null,
  16. debug: false,
  17. localVideoEl: '',
  18. remoteVideosEl: '',
  19. enableDataChannels: true,
  20. autoRequestMedia: false,
  21. autoRemoveVideos: true,
  22. adjustPeerVolume: true,
  23. peerVolumeWhenSpeaking: 0.25,
  24. media: {
  25. video: true,
  26. audio: true
  27. },
  28. receiveMedia: { // FIXME: remove old chrome <= 37 constraints format
  29. mandatory: {
  30. OfferToReceiveAudio: true,
  31. OfferToReceiveVideo: true
  32. }
  33. },
  34. localVideo: {
  35. autoplay: true,
  36. mirror: true,
  37. muted: true
  38. }
  39. };
  40. var item, connection;
  41. // We also allow a 'logger' option. It can be any object that implements
  42. // log, warn, and error methods.
  43. // We log nothing by default, following "the rule of silence":
  44. // http://www.linfo.org/rule_of_silence.html
  45. this.logger = function () {
  46. // we assume that if you're in debug mode and you didn't
  47. // pass in a logger, you actually want to log as much as
  48. // possible.
  49. if (opts.debug) {
  50. return opts.logger || console;
  51. } else {
  52. // or we'll use your logger which should have its own logic
  53. // for output. Or we'll return the no-op.
  54. return opts.logger || mockconsole;
  55. }
  56. }();
  57. // set our config from options
  58. for (item in options) {
  59. this.config[item] = options[item];
  60. }
  61. // attach detected support for convenience
  62. this.capabilities = webrtcSupport;
  63. // call WildEmitter constructor
  64. WildEmitter.call(this);
  65. // create default SocketIoConnection if it's not passed in
  66. if (this.config.connection === null) {
  67. connection = this.connection = new SocketIoConnection(this.config);
  68. } else {
  69. connection = this.connection = this.config.connection;
  70. }
  71. connection.on('connect', function () {
  72. self.emit('connectionReady', connection.getSessionid());
  73. self.sessionReady = true;
  74. self.testReadiness();
  75. });
  76. connection.on('message', function (message) {
  77. var peers = self.webrtc.getPeers(message.from, message.roomType);
  78. var peer;
  79. if (message.type === 'offer') {
  80. if (peers.length) {
  81. peers.forEach(function (p) {
  82. if (p.sid == message.sid) peer = p;
  83. });
  84. //if (!peer) peer = peers[0]; // fallback for old protocol versions
  85. }
  86. if (!peer) {
  87. peer = self.webrtc.createPeer({
  88. id: message.from,
  89. sid: message.sid,
  90. type: message.roomType,
  91. enableDataChannels: self.config.enableDataChannels && message.roomType !== 'screen',
  92. sharemyscreen: message.roomType === 'screen' && !message.broadcaster,
  93. broadcaster: message.roomType === 'screen' && !message.broadcaster ? self.connection.getSessionid() : null
  94. });
  95. self.emit('createdPeer', peer);
  96. }
  97. peer.handleMessage(message);
  98. } else if (peers.length) {
  99. peers.forEach(function (peer) {
  100. if (message.sid) {
  101. if (peer.sid === message.sid) {
  102. peer.handleMessage(message);
  103. }
  104. } else {
  105. peer.handleMessage(message);
  106. }
  107. });
  108. }
  109. });
  110. connection.on('remove', function (room) {
  111. if (room.id !== self.connection.getSessionid()) {
  112. self.webrtc.removePeers(room.id, room.type);
  113. }
  114. });
  115. // instantiate our main WebRTC helper
  116. // using same logger from logic here
  117. opts.logger = this.logger;
  118. opts.debug = false;
  119. this.webrtc = new WebRTC(opts);
  120. // attach a few methods from underlying lib to simple.
  121. ['mute', 'unmute', 'pauseVideo', 'resumeVideo', 'pause', 'resume', 'sendToAll', 'sendDirectlyToAll'].forEach(function (method) {
  122. self[method] = self.webrtc[method].bind(self.webrtc);
  123. });
  124. // proxy events from WebRTC
  125. this.webrtc.on('*', function () {
  126. self.emit.apply(self, arguments);
  127. });
  128. // log all events in debug mode
  129. if (config.debug) {
  130. this.on('*', this.logger.log.bind(this.logger, 'SimpleWebRTC event:'));
  131. }
  132. // check for readiness
  133. this.webrtc.on('localStream', function () {
  134. self.testReadiness();
  135. });
  136. this.webrtc.on('message', function (payload) {
  137. self.connection.emit('message', payload);
  138. });
  139. this.webrtc.on('peerStreamAdded', this.handlePeerStreamAdded.bind(this));
  140. this.webrtc.on('peerStreamRemoved', this.handlePeerStreamRemoved.bind(this));
  141. // echo cancellation attempts
  142. if (this.config.adjustPeerVolume) {
  143. this.webrtc.on('speaking', this.setVolumeForAll.bind(this, this.config.peerVolumeWhenSpeaking));
  144. this.webrtc.on('stoppedSpeaking', this.setVolumeForAll.bind(this, 1));
  145. }
  146. connection.on('stunservers', function (args) {
  147. // resets/overrides the config
  148. self.webrtc.config.peerConnectionConfig.iceServers = args;
  149. self.emit('stunservers', args);
  150. });
  151. connection.on('turnservers', function (args) {
  152. // appends to the config
  153. self.webrtc.config.peerConnectionConfig.iceServers = self.webrtc.config.peerConnectionConfig.iceServers.concat(args);
  154. self.emit('turnservers', args);
  155. });
  156. this.webrtc.on('iceFailed', function (peer) {
  157. // local ice failure
  158. });
  159. this.webrtc.on('connectivityError', function (peer) {
  160. // remote ice failure
  161. });
  162. // sending mute/unmute to all peers
  163. this.webrtc.on('audioOn', function () {
  164. self.webrtc.sendToAll('unmute', {name: 'audio'});
  165. });
  166. this.webrtc.on('audioOff', function () {
  167. self.webrtc.sendToAll('mute', {name: 'audio'});
  168. });
  169. this.webrtc.on('videoOn', function () {
  170. self.webrtc.sendToAll('unmute', {name: 'video'});
  171. });
  172. this.webrtc.on('videoOff', function () {
  173. self.webrtc.sendToAll('mute', {name: 'video'});
  174. });
  175. // screensharing events
  176. this.webrtc.on('localScreen', function (stream) {
  177. var item,
  178. el = document.createElement('video'),
  179. container = self.getRemoteVideoContainer();
  180. el.oncontextmenu = function () { return false; };
  181. el.id = 'localScreen';
  182. attachMediaStream(stream, el);
  183. if (container) {
  184. container.appendChild(el);
  185. }
  186. self.emit('localScreenAdded', el);
  187. self.connection.emit('shareScreen');
  188. self.webrtc.peers.forEach(function (existingPeer) {
  189. var peer;
  190. if (existingPeer.type === 'video') {
  191. peer = self.webrtc.createPeer({
  192. id: existingPeer.id,
  193. type: 'screen',
  194. sharemyscreen: true,
  195. enableDataChannels: false,
  196. receiveMedia: {
  197. mandatory: {
  198. OfferToReceiveAudio: false,
  199. OfferToReceiveVideo: false
  200. }
  201. },
  202. broadcaster: self.connection.getSessionid(),
  203. });
  204. self.emit('createdPeer', peer);
  205. peer.start();
  206. }
  207. });
  208. });
  209. this.webrtc.on('localScreenStopped', function (stream) {
  210. self.stopScreenShare();
  211. /*
  212. self.connection.emit('unshareScreen');
  213. self.webrtc.peers.forEach(function (peer) {
  214. if (peer.sharemyscreen) {
  215. peer.end();
  216. }
  217. });
  218. */
  219. });
  220. this.webrtc.on('channelMessage', function (peer, label, data) {
  221. if (data.type == 'volume') {
  222. self.emit('remoteVolumeChange', peer, data.volume);
  223. }
  224. });
  225. if (this.config.autoRequestMedia) this.startLocalVideo();
  226. }
  227. SimpleWebRTC.prototype = Object.create(WildEmitter.prototype, {
  228. constructor: {
  229. value: SimpleWebRTC
  230. }
  231. });
  232. SimpleWebRTC.prototype.leaveRoom = function () {
  233. if (this.roomName) {
  234. this.connection.emit('leave');
  235. this.webrtc.peers.forEach(function (peer) {
  236. peer.end();
  237. });
  238. if (this.getLocalScreen()) {
  239. this.stopScreenShare();
  240. }
  241. this.emit('leftRoom', this.roomName);
  242. this.roomName = undefined;
  243. }
  244. };
  245. SimpleWebRTC.prototype.disconnect = function () {
  246. this.connection.disconnect();
  247. delete this.connection;
  248. };
  249. SimpleWebRTC.prototype.handlePeerStreamAdded = function (peer) {
  250. var self = this;
  251. var container = this.getRemoteVideoContainer();
  252. var video = attachMediaStream(peer.stream);
  253. // store video element as part of peer for easy removal
  254. peer.videoEl = video;
  255. video.id = this.getDomId(peer);
  256. if (container) container.appendChild(video);
  257. this.emit('videoAdded', video, peer);
  258. // send our mute status to new peer if we're muted
  259. // currently called with a small delay because it arrives before
  260. // the video element is created otherwise (which happens after
  261. // the async setRemoteDescription-createAnswer)
  262. window.setTimeout(function () {
  263. if (!self.webrtc.isAudioEnabled()) {
  264. peer.send('mute', {name: 'audio'});
  265. }
  266. if (!self.webrtc.isVideoEnabled()) {
  267. peer.send('mute', {name: 'video'});
  268. }
  269. }, 250);
  270. };
  271. SimpleWebRTC.prototype.handlePeerStreamRemoved = function (peer) {
  272. var container = this.getRemoteVideoContainer();
  273. var videoEl = peer.videoEl;
  274. if (this.config.autoRemoveVideos && container && videoEl) {
  275. container.removeChild(videoEl);
  276. }
  277. if (videoEl) this.emit('videoRemoved', videoEl, peer);
  278. };
  279. SimpleWebRTC.prototype.getDomId = function (peer) {
  280. return [peer.id, peer.type, peer.broadcaster ? 'broadcasting' : 'incoming'].join('_');
  281. };
  282. // set volume on video tag for all peers takse a value between 0 and 1
  283. SimpleWebRTC.prototype.setVolumeForAll = function (volume) {
  284. this.webrtc.peers.forEach(function (peer) {
  285. if (peer.videoEl) peer.videoEl.volume = volume;
  286. });
  287. };
  288. SimpleWebRTC.prototype.joinRoom = function (name, cb) {
  289. var self = this;
  290. this.roomName = name;
  291. this.connection.emit('join', name, function (err, roomDescription) {
  292. if (err) {
  293. self.emit('error', err);
  294. } else {
  295. var id,
  296. client,
  297. type,
  298. peer;
  299. for (id in roomDescription.clients) {
  300. client = roomDescription.clients[id];
  301. for (type in client) {
  302. if (client[type]) {
  303. peer = self.webrtc.createPeer({
  304. id: id,
  305. type: type,
  306. enableDataChannels: self.config.enableDataChannels && type !== 'screen',
  307. receiveMedia: {
  308. mandatory: {
  309. OfferToReceiveAudio: type !== 'screen' && self.config.receiveMedia.mandatory.OfferToReceiveAudio,
  310. OfferToReceiveVideo: self.config.receiveMedia.mandatory.OfferToReceiveVideo
  311. }
  312. }
  313. });
  314. self.emit('createdPeer', peer);
  315. peer.start();
  316. }
  317. }
  318. }
  319. }
  320. if (cb) cb(err, roomDescription);
  321. self.emit('joinedRoom', name);
  322. });
  323. };
  324. SimpleWebRTC.prototype.getEl = function (idOrEl) {
  325. if (typeof idOrEl === 'string') {
  326. return document.getElementById(idOrEl);
  327. } else {
  328. return idOrEl;
  329. }
  330. };
  331. SimpleWebRTC.prototype.startLocalVideo = function () {
  332. var self = this;
  333. this.webrtc.startLocalMedia(this.config.media, function (err, stream) {
  334. if (err) {
  335. self.emit('localMediaError', err);
  336. } else {
  337. attachMediaStream(stream, self.getLocalVideoContainer(), self.config.localVideo);
  338. }
  339. });
  340. };
  341. SimpleWebRTC.prototype.stopLocalVideo = function () {
  342. this.webrtc.stopLocalMedia();
  343. };
  344. // this accepts either element ID or element
  345. // and either the video tag itself or a container
  346. // that will be used to put the video tag into.
  347. SimpleWebRTC.prototype.getLocalVideoContainer = function () {
  348. var el = this.getEl(this.config.localVideoEl);
  349. if (el && el.tagName === 'VIDEO') {
  350. el.oncontextmenu = function () { return false; };
  351. return el;
  352. } else if (el) {
  353. var video = document.createElement('video');
  354. video.oncontextmenu = function () { return false; };
  355. el.appendChild(video);
  356. return video;
  357. } else {
  358. return;
  359. }
  360. };
  361. SimpleWebRTC.prototype.getRemoteVideoContainer = function () {
  362. return this.getEl(this.config.remoteVideosEl);
  363. };
  364. SimpleWebRTC.prototype.shareScreen = function (cb) {
  365. this.webrtc.startScreenShare(cb);
  366. };
  367. SimpleWebRTC.prototype.getLocalScreen = function () {
  368. return this.webrtc.localScreen;
  369. };
  370. SimpleWebRTC.prototype.stopScreenShare = function () {
  371. this.connection.emit('unshareScreen');
  372. var videoEl = document.getElementById('localScreen');
  373. var container = this.getRemoteVideoContainer();
  374. var stream = this.getLocalScreen();
  375. if (this.config.autoRemoveVideos && container && videoEl) {
  376. container.removeChild(videoEl);
  377. }
  378. // a hack to emit the event the removes the video
  379. // element that we want
  380. if (videoEl) this.emit('videoRemoved', videoEl);
  381. if (stream) stream.stop();
  382. this.webrtc.peers.forEach(function (peer) {
  383. if (peer.broadcaster) {
  384. peer.end();
  385. }
  386. });
  387. //delete this.webrtc.localScreen;
  388. };
  389. SimpleWebRTC.prototype.testReadiness = function () {
  390. var self = this;
  391. if (this.webrtc.localStream && this.sessionReady) {
  392. self.emit('readyToCall', self.connection.getSessionid());
  393. }
  394. };
  395. SimpleWebRTC.prototype.createRoom = function (name, cb) {
  396. if (arguments.length === 2) {
  397. this.connection.emit('create', name, cb);
  398. } else {
  399. this.connection.emit('create', name);
  400. }
  401. };
  402. SimpleWebRTC.prototype.sendFile = function () {
  403. if (!webrtcSupport.dataChannel) {
  404. return this.emit('error', new Error('DataChannelNotSupported'));
  405. }
  406. };
  407. module.exports = SimpleWebRTC;
  408. },{"./socketioconnection":2,"attachmediastream":6,"mockconsole":7,"webrtc":4,"webrtcsupport":5,"wildemitter":3}],3:[function(require,module,exports){
  409. /*
  410. WildEmitter.js is a slim little event emitter by @henrikjoreteg largely based
  411. on @visionmedia's Emitter from UI Kit.
  412. Why? I wanted it standalone.
  413. I also wanted support for wildcard emitters like this:
  414. emitter.on('*', function (eventName, other, event, payloads) {
  415. });
  416. emitter.on('somenamespace*', function (eventName, payloads) {
  417. });
  418. Please note that callbacks triggered by wildcard registered events also get
  419. the event name as the first argument.
  420. */
  421. module.exports = WildEmitter;
  422. function WildEmitter() {
  423. this.callbacks = {};
  424. }
  425. // Listen on the given `event` with `fn`. Store a group name if present.
  426. WildEmitter.prototype.on = function (event, groupName, fn) {
  427. var hasGroup = (arguments.length === 3),
  428. group = hasGroup ? arguments[1] : undefined,
  429. func = hasGroup ? arguments[2] : arguments[1];
  430. func._groupName = group;
  431. (this.callbacks[event] = this.callbacks[event] || []).push(func);
  432. return this;
  433. };
  434. // Adds an `event` listener that will be invoked a single
  435. // time then automatically removed.
  436. WildEmitter.prototype.once = function (event, groupName, fn) {
  437. var self = this,
  438. hasGroup = (arguments.length === 3),
  439. group = hasGroup ? arguments[1] : undefined,
  440. func = hasGroup ? arguments[2] : arguments[1];
  441. function on() {
  442. self.off(event, on);
  443. func.apply(this, arguments);
  444. }
  445. this.on(event, group, on);
  446. return this;
  447. };
  448. // Unbinds an entire group
  449. WildEmitter.prototype.releaseGroup = function (groupName) {
  450. var item, i, len, handlers;
  451. for (item in this.callbacks) {
  452. handlers = this.callbacks[item];
  453. for (i = 0, len = handlers.length; i < len; i++) {
  454. if (handlers[i]._groupName === groupName) {
  455. //console.log('removing');
  456. // remove it and shorten the array we're looping through
  457. handlers.splice(i, 1);
  458. i--;
  459. len--;
  460. }
  461. }
  462. }
  463. return this;
  464. };
  465. // Remove the given callback for `event` or all
  466. // registered callbacks.
  467. WildEmitter.prototype.off = function (event, fn) {
  468. var callbacks = this.callbacks[event],
  469. i;
  470. if (!callbacks) return this;
  471. // remove all handlers
  472. if (arguments.length === 1) {
  473. delete this.callbacks[event];
  474. return this;
  475. }
  476. // remove specific handler
  477. i = callbacks.indexOf(fn);
  478. callbacks.splice(i, 1);
  479. return this;
  480. };
  481. /// Emit `event` with the given args.
  482. // also calls any `*` handlers
  483. WildEmitter.prototype.emit = function (event) {
  484. var args = [].slice.call(arguments, 1),
  485. callbacks = this.callbacks[event],
  486. specialCallbacks = this.getWildcardCallbacks(event),
  487. i,
  488. len,
  489. item,
  490. listeners;
  491. if (callbacks) {
  492. listeners = callbacks.slice();
  493. for (i = 0, len = listeners.length; i < len; ++i) {
  494. if (listeners[i]) {
  495. listeners[i].apply(this, args);
  496. } else {
  497. break;
  498. }
  499. }
  500. }
  501. if (specialCallbacks) {
  502. len = specialCallbacks.length;
  503. listeners = specialCallbacks.slice();
  504. for (i = 0, len = listeners.length; i < len; ++i) {
  505. if (listeners[i]) {
  506. listeners[i].apply(this, [event].concat(args));
  507. } else {
  508. break;
  509. }
  510. }
  511. }
  512. return this;
  513. };
  514. // Helper for for finding special wildcard event handlers that match the event
  515. WildEmitter.prototype.getWildcardCallbacks = function (eventName) {
  516. var item,
  517. split,
  518. result = [];
  519. for (item in this.callbacks) {
  520. split = item.split('*');
  521. if (item === '*' || (split.length === 2 && eventName.slice(0, split[0].length) === split[0])) {
  522. result = result.concat(this.callbacks[item]);
  523. }
  524. }
  525. return result;
  526. };
  527. },{}],5:[function(require,module,exports){
  528. // created by @HenrikJoreteg
  529. var prefix;
  530. if (window.mozRTCPeerConnection || navigator.mozGetUserMedia) {
  531. prefix = 'moz';
  532. } else if (window.webkitRTCPeerConnection || navigator.webkitGetUserMedia) {
  533. prefix = 'webkit';
  534. }
  535. var PC = window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
  536. var IceCandidate = window.mozRTCIceCandidate || window.RTCIceCandidate;
  537. var SessionDescription = window.mozRTCSessionDescription || window.RTCSessionDescription;
  538. var MediaStream = window.webkitMediaStream || window.MediaStream;
  539. var screenSharing = window.location.protocol === 'https:' &&
  540. ((window.navigator.userAgent.match('Chrome') && parseInt(window.navigator.userAgent.match(/Chrome\/(.*) /)[1], 10) >= 26) ||
  541. (window.navigator.userAgent.match('Firefox') && parseInt(window.navigator.userAgent.match(/Firefox\/(.*)/)[1], 10) >= 33));
  542. var AudioContext = window.AudioContext || window.webkitAudioContext;
  543. var videoEl = document.createElement('video');
  544. var supportVp8 = videoEl && videoEl.canPlayType && videoEl.canPlayType('video/webm; codecs="vp8", vorbis') === "probably";
  545. var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.msGetUserMedia || navigator.mozGetUserMedia;
  546. // export support flags and constructors.prototype && PC
  547. module.exports = {
  548. prefix: prefix,
  549. support: !!PC && supportVp8 && !!getUserMedia,
  550. // new support style
  551. supportRTCPeerConnection: !!PC,
  552. supportVp8: supportVp8,
  553. supportGetUserMedia: !!getUserMedia,
  554. supportDataChannel: !!(PC && PC.prototype && PC.prototype.createDataChannel),
  555. supportWebAudio: !!(AudioContext && AudioContext.prototype.createMediaStreamSource),
  556. supportMediaStream: !!(MediaStream && MediaStream.prototype.removeTrack),
  557. supportScreenSharing: !!screenSharing,
  558. // old deprecated style. Dont use this anymore
  559. dataChannel: !!(PC && PC.prototype && PC.prototype.createDataChannel),
  560. webAudio: !!(AudioContext && AudioContext.prototype.createMediaStreamSource),
  561. mediaStream: !!(MediaStream && MediaStream.prototype.removeTrack),
  562. screenSharing: !!screenSharing,
  563. // constructors
  564. AudioContext: AudioContext,
  565. PeerConnection: PC,
  566. SessionDescription: SessionDescription,
  567. IceCandidate: IceCandidate,
  568. MediaStream: MediaStream,
  569. getUserMedia: getUserMedia
  570. };
  571. },{}],6:[function(require,module,exports){
  572. module.exports = function (stream, el, options) {
  573. var URL = window.URL;
  574. var opts = {
  575. autoplay: true,
  576. mirror: false,
  577. muted: false
  578. };
  579. var element = el || document.createElement('video');
  580. var item;
  581. if (options) {
  582. for (item in options) {
  583. opts[item] = options[item];
  584. }
  585. }
  586. if (opts.autoplay) element.autoplay = 'autoplay';
  587. if (opts.muted) element.muted = true;
  588. if (opts.mirror) {
  589. ['', 'moz', 'webkit', 'o', 'ms'].forEach(function (prefix) {
  590. var styleName = prefix ? prefix + 'Transform' : 'transform';
  591. element.style[styleName] = 'scaleX(-1)';
  592. });
  593. }
  594. // this first one should work most everywhere now
  595. // but we have a few fallbacks just in case.
  596. if (URL && URL.createObjectURL) {
  597. element.src = URL.createObjectURL(stream);
  598. } else if (element.srcObject) {
  599. element.srcObject = stream;
  600. } else if (element.mozSrcObject) {
  601. element.mozSrcObject = stream;
  602. } else {
  603. return false;
  604. }
  605. return element;
  606. };
  607. },{}],7:[function(require,module,exports){
  608. var methods = "assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,time,timeEnd,trace,warn".split(",");
  609. var l = methods.length;
  610. var fn = function () {};
  611. var mockconsole = {};
  612. while (l--) {
  613. mockconsole[methods[l]] = fn;
  614. }
  615. module.exports = mockconsole;
  616. },{}],2:[function(require,module,exports){
  617. var io = require('socket.io-client');
  618. function SocketIoConnection(config) {
  619. this.connection = io.connect(config.url, config.socketio);
  620. }
  621. SocketIoConnection.prototype.on = function (ev, fn) {
  622. this.connection.on(ev, fn);
  623. };
  624. SocketIoConnection.prototype.emit = function () {
  625. this.connection.emit.apply(this.connection, arguments);
  626. };
  627. SocketIoConnection.prototype.getSessionid = function () {
  628. return this.connection.socket.sessionid;
  629. };
  630. SocketIoConnection.prototype.disconnect = function () {
  631. return this.connection.disconnect();
  632. };
  633. module.exports = SocketIoConnection;
  634. },{"socket.io-client":8}],8:[function(require,module,exports){
  635. /*! Socket.IO.js build:0.9.16, development. Copyright(c) 2011 LearnBoost <dev@learnboost.com> MIT Licensed */
  636. var io = ('undefined' === typeof module ? {} : module.exports);
  637. (function() {
  638. /**
  639. * socket.io
  640. * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
  641. * MIT Licensed
  642. */
  643. (function (exports, global) {
  644. /**
  645. * IO namespace.
  646. *
  647. * @namespace
  648. */
  649. var io = exports;
  650. /**
  651. * Socket.IO version
  652. *
  653. * @api public
  654. */
  655. io.version = '0.9.16';
  656. /**
  657. * Protocol implemented.
  658. *
  659. * @api public
  660. */
  661. io.protocol = 1;
  662. /**
  663. * Available transports, these will be populated with the available transports
  664. *
  665. * @api public
  666. */
  667. io.transports = [];
  668. /**
  669. * Keep track of jsonp callbacks.
  670. *
  671. * @api private
  672. */
  673. io.j = [];
  674. /**
  675. * Keep track of our io.Sockets
  676. *
  677. * @api private
  678. */
  679. io.sockets = {};
  680. /**
  681. * Manages connections to hosts.
  682. *
  683. * @param {String} uri
  684. * @Param {Boolean} force creation of new socket (defaults to false)
  685. * @api public
  686. */
  687. io.connect = function (host, details) {
  688. var uri = io.util.parseUri(host)
  689. , uuri
  690. , socket;
  691. if (global && global.location) {
  692. uri.protocol = uri.protocol || global.location.protocol.slice(0, -1);
  693. uri.host = uri.host || (global.document
  694. ? global.document.domain : global.location.hostname);
  695. uri.port = uri.port || global.location.port;
  696. }
  697. uuri = io.util.uniqueUri(uri);
  698. var options = {
  699. host: uri.host
  700. , secure: 'https' == uri.protocol
  701. , port: uri.port || ('https' == uri.protocol ? 443 : 80)
  702. , query: uri.query || ''
  703. };
  704. io.util.merge(options, details);
  705. if (options['force new connection'] || !io.sockets[uuri]) {
  706. socket = new io.Socket(options);
  707. }
  708. if (!options['force new connection'] && socket) {
  709. io.sockets[uuri] = socket;
  710. }
  711. socket = socket || io.sockets[uuri];
  712. // if path is different from '' or /
  713. return socket.of(uri.path.length > 1 ? uri.path : '');
  714. };
  715. })('object' === typeof module ? module.exports : (this.io = {}), this);
  716. /**
  717. * socket.io
  718. * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
  719. * MIT Licensed
  720. */
  721. (function (exports, global) {
  722. /**
  723. * Utilities namespace.
  724. *
  725. * @namespace
  726. */
  727. var util = exports.util = {};
  728. /**
  729. * Parses an URI
  730. *
  731. * @author Steven Levithan <stevenlevithan.com> (MIT license)
  732. * @api public
  733. */
  734. var re = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;
  735. var parts = ['source', 'protocol', 'authority', 'userInfo', 'user', 'password',
  736. 'host', 'port', 'relative', 'path', 'directory', 'file', 'query',
  737. 'anchor'];
  738. util.parseUri = function (str) {
  739. var m = re.exec(str || '')
  740. , uri = {}
  741. , i = 14;
  742. while (i--) {
  743. uri[parts[i]] = m[i] || '';
  744. }
  745. return uri;
  746. };
  747. /**
  748. * Produces a unique url that identifies a Socket.IO connection.
  749. *
  750. * @param {Object} uri
  751. * @api public
  752. */
  753. util.uniqueUri = function (uri) {
  754. var protocol = uri.protocol
  755. , host = uri.host
  756. , port = uri.port;
  757. if ('document' in global) {
  758. host = host || document.domain;
  759. port = port || (protocol == 'https'
  760. && document.location.protocol !== 'https:' ? 443 : document.location.port);
  761. } else {
  762. host = host || 'localhost';
  763. if (!port && protocol == 'https') {
  764. port = 443;
  765. }
  766. }
  767. return (protocol || 'http') + '://' + host + ':' + (port || 80);
  768. };
  769. /**
  770. * Mergest 2 query strings in to once unique query string
  771. *
  772. * @param {String} base
  773. * @param {String} addition
  774. * @api public
  775. */
  776. util.query = function (base, addition) {
  777. var query = util.chunkQuery(base || '')
  778. , components = [];
  779. util.merge(query, util.chunkQuery(addition || ''));
  780. for (var part in query) {
  781. if (query.hasOwnProperty(part)) {
  782. components.push(part + '=' + query[part]);
  783. }
  784. }
  785. return components.length ? '?' + components.join('&') : '';
  786. };
  787. /**
  788. * Transforms a querystring in to an object
  789. *
  790. * @param {String} qs
  791. * @api public
  792. */
  793. util.chunkQuery = function (qs) {
  794. var query = {}
  795. , params = qs.split('&')
  796. , i = 0
  797. , l = params.length
  798. , kv;
  799. for (; i < l; ++i) {
  800. kv = params[i].split('=');
  801. if (kv[0]) {
  802. query[kv[0]] = kv[1];
  803. }
  804. }
  805. return query;
  806. };
  807. /**
  808. * Executes the given function when the page is loaded.
  809. *
  810. * io.util.load(function () { console.log('page loaded'); });
  811. *
  812. * @param {Function} fn
  813. * @api public
  814. */
  815. var pageLoaded = false;
  816. util.load = function (fn) {
  817. if ('document' in global && document.readyState === 'complete' || pageLoaded) {
  818. return fn();
  819. }
  820. util.on(global, 'load', fn, false);
  821. };
  822. /**
  823. * Adds an event.
  824. *
  825. * @api private
  826. */
  827. util.on = function (element, event, fn, capture) {
  828. if (element.attachEvent) {
  829. element.attachEvent('on' + event, fn);
  830. } else if (element.addEventListener) {
  831. element.addEventListener(event, fn, capture);
  832. }
  833. };
  834. /**
  835. * Generates the correct `XMLHttpRequest` for regular and cross domain requests.
  836. *
  837. * @param {Boolean} [xdomain] Create a request that can be used cross domain.
  838. * @returns {XMLHttpRequest|false} If we can create a XMLHttpRequest.
  839. * @api private
  840. */
  841. util.request = function (xdomain) {
  842. if (xdomain && 'undefined' != typeof XDomainRequest && !util.ua.hasCORS) {
  843. return new XDomainRequest();
  844. }
  845. if ('undefined' != typeof XMLHttpRequest && (!xdomain || util.ua.hasCORS)) {
  846. return new XMLHttpRequest();
  847. }
  848. if (!xdomain) {
  849. try {
  850. return new window[(['Active'].concat('Object').join('X'))]('Microsoft.XMLHTTP');
  851. } catch(e) { }
  852. }
  853. return null;
  854. };
  855. /**
  856. * XHR based transport constructor.
  857. *
  858. * @constructor
  859. * @api public
  860. */
  861. /**
  862. * Change the internal pageLoaded value.
  863. */
  864. if ('undefined' != typeof window) {
  865. util.load(function () {
  866. pageLoaded = true;
  867. });
  868. }
  869. /**
  870. * Defers a function to ensure a spinner is not displayed by the browser
  871. *
  872. * @param {Function} fn
  873. * @api public
  874. */
  875. util.defer = function (fn) {
  876. if (!util.ua.webkit || 'undefined' != typeof importScripts) {
  877. return fn();
  878. }
  879. util.load(function () {
  880. setTimeout(fn, 100);
  881. });
  882. };
  883. /**
  884. * Merges two objects.
  885. *
  886. * @api public
  887. */
  888. util.merge = function merge (target, additional, deep, lastseen) {
  889. var seen = lastseen || []
  890. , depth = typeof deep == 'undefined' ? 2 : deep
  891. , prop;
  892. for (prop in additional) {
  893. if (additional.hasOwnProperty(prop) && util.indexOf(seen, prop) < 0) {
  894. if (typeof target[prop] !== 'object' || !depth) {
  895. target[prop] = additional[prop];
  896. seen.push(additional[prop]);
  897. } else {
  898. util.merge(target[prop], additional[prop], depth - 1, seen);
  899. }
  900. }
  901. }
  902. return target;
  903. };
  904. /**
  905. * Merges prototypes from objects
  906. *
  907. * @api public
  908. */
  909. util.mixin = function (ctor, ctor2) {
  910. util.merge(ctor.prototype, ctor2.prototype);
  911. };
  912. /**
  913. * Shortcut for prototypical and static inheritance.
  914. *
  915. * @api private
  916. */
  917. util.inherit = function (ctor, ctor2) {
  918. function f() {};
  919. f.prototype = ctor2.prototype;
  920. ctor.prototype = new f;
  921. };
  922. /**
  923. * Checks if the given object is an Array.
  924. *
  925. * io.util.isArray([]); // true
  926. * io.util.isArray({}); // false
  927. *
  928. * @param Object obj
  929. * @api public
  930. */
  931. util.isArray = Array.isArray || function (obj) {
  932. return Object.prototype.toString.call(obj) === '[object Array]';
  933. };
  934. /**
  935. * Intersects values of two arrays into a third
  936. *
  937. * @api public
  938. */
  939. util.intersect = function (arr, arr2) {
  940. var ret = []
  941. , longest = arr.length > arr2.length ? arr : arr2
  942. , shortest = arr.length > arr2.length ? arr2 : arr;
  943. for (var i = 0, l = shortest.length; i < l; i++) {
  944. if (~util.indexOf(longest, shortest[i]))
  945. ret.push(shortest[i]);
  946. }
  947. return ret;
  948. };
  949. /**
  950. * Array indexOf compatibility.
  951. *
  952. * @see bit.ly/a5Dxa2
  953. * @api public
  954. */
  955. util.indexOf = function (arr, o, i) {
  956. for (var j = arr.length, i = i < 0 ? i + j < 0 ? 0 : i + j : i || 0;
  957. i < j && arr[i] !== o; i++) {}
  958. return j <= i ? -1 : i;
  959. };
  960. /**
  961. * Converts enumerables to array.
  962. *
  963. * @api public
  964. */
  965. util.toArray = function (enu) {
  966. var arr = [];
  967. for (var i = 0, l = enu.length; i < l; i++)
  968. arr.push(enu[i]);
  969. return arr;
  970. };
  971. /**
  972. * UA / engines detection namespace.
  973. *
  974. * @namespace
  975. */
  976. util.ua = {};
  977. /**
  978. * Whether the UA supports CORS for XHR.
  979. *
  980. * @api public
  981. */
  982. util.ua.hasCORS = 'undefined' != typeof XMLHttpRequest && (function () {
  983. try {
  984. var a = new XMLHttpRequest();
  985. } catch (e) {
  986. return false;
  987. }
  988. return a.withCredentials != undefined;
  989. })();
  990. /**
  991. * Detect webkit.
  992. *
  993. * @api public
  994. */
  995. util.ua.webkit = 'undefined' != typeof navigator
  996. && /webkit/i.test(navigator.userAgent);
  997. /**
  998. * Detect iPad/iPhone/iPod.
  999. *
  1000. * @api public
  1001. */
  1002. util.ua.iDevice = 'undefined' != typeof navigator
  1003. && /iPad|iPhone|iPod/i.test(navigator.userAgent);
  1004. })('undefined' != typeof io ? io : module.exports, this);
  1005. /**
  1006. * socket.io
  1007. * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
  1008. * MIT Licensed
  1009. */
  1010. (function (exports, io) {
  1011. /**
  1012. * Expose constructor.
  1013. */
  1014. exports.EventEmitter = EventEmitter;
  1015. /**
  1016. * Event emitter constructor.
  1017. *
  1018. * @api public.
  1019. */
  1020. function EventEmitter () {};
  1021. /**
  1022. * Adds a listener
  1023. *
  1024. * @api public
  1025. */
  1026. EventEmitter.prototype.on = function (name, fn) {
  1027. if (!this.$events) {
  1028. this.$events = {};
  1029. }
  1030. if (!this.$events[name]) {
  1031. this.$events[name] = fn;
  1032. } else if (io.util.isArray(this.$events[name])) {
  1033. this.$events[name].push(fn);
  1034. } else {
  1035. this.$events[name] = [this.$events[name], fn];
  1036. }
  1037. return this;
  1038. };
  1039. EventEmitter.prototype.addListener = EventEmitter.prototype.on;
  1040. /**
  1041. * Adds a volatile listener.
  1042. *
  1043. * @api public
  1044. */
  1045. EventEmitter.prototype.once = function (name, fn) {
  1046. var self = this;
  1047. function on () {
  1048. self.removeListener(name, on);
  1049. fn.apply(this, arguments);
  1050. };
  1051. on.listener = fn;
  1052. this.on(name, on);
  1053. return this;
  1054. };
  1055. /**
  1056. * Removes a listener.
  1057. *
  1058. * @api public
  1059. */
  1060. EventEmitter.prototype.removeListener = function (name, fn) {
  1061. if (this.$events && this.$events[name]) {
  1062. var list = this.$events[name];
  1063. if (io.util.isArray(list)) {
  1064. var pos = -1;
  1065. for (var i = 0, l = list.length; i < l; i++) {
  1066. if (list[i] === fn || (list[i].listener && list[i].listener === fn)) {
  1067. pos = i;
  1068. break;
  1069. }
  1070. }
  1071. if (pos < 0) {
  1072. return this;
  1073. }
  1074. list.splice(pos, 1);
  1075. if (!list.length) {
  1076. delete this.$events[name];
  1077. }
  1078. } else if (list === fn || (list.listener && list.listener === fn)) {
  1079. delete this.$events[name];
  1080. }
  1081. }
  1082. return this;
  1083. };
  1084. /**
  1085. * Removes all listeners for an event.
  1086. *
  1087. * @api public
  1088. */
  1089. EventEmitter.prototype.removeAllListeners = function (name) {
  1090. if (name === undefined) {
  1091. this.$events = {};
  1092. return this;
  1093. }
  1094. if (this.$events && this.$events[name]) {
  1095. this.$events[name] = null;
  1096. }
  1097. return this;
  1098. };
  1099. /**
  1100. * Gets all listeners for a certain event.
  1101. *
  1102. * @api publci
  1103. */
  1104. EventEmitter.prototype.listeners = function (name) {
  1105. if (!this.$events) {
  1106. this.$events = {};
  1107. }
  1108. if (!this.$events[name]) {
  1109. this.$events[name] = [];
  1110. }
  1111. if (!io.util.isArray(this.$events[name])) {
  1112. this.$events[name] = [this.$events[name]];
  1113. }
  1114. return this.$events[name];
  1115. };
  1116. /**
  1117. * Emits an event.
  1118. *
  1119. * @api public
  1120. */
  1121. EventEmitter.prototype.emit = function (name) {
  1122. if (!this.$events) {
  1123. return false;
  1124. }
  1125. var handler = this.$events[name];
  1126. if (!handler) {
  1127. return false;
  1128. }
  1129. var args = Array.prototype.slice.call(arguments, 1);
  1130. if ('function' == typeof handler) {
  1131. handler.apply(this, args);
  1132. } else if (io.util.isArray(handler)) {
  1133. var listeners = handler.slice();
  1134. for (var i = 0, l = listeners.length; i < l; i++) {
  1135. listeners[i].apply(this, args);
  1136. }
  1137. } else {
  1138. return false;
  1139. }
  1140. return true;
  1141. };
  1142. })(
  1143. 'undefined' != typeof io ? io : module.exports
  1144. , 'undefined' != typeof io ? io : module.parent.exports
  1145. );
  1146. /**
  1147. * socket.io
  1148. * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
  1149. * MIT Licensed
  1150. */
  1151. /**
  1152. * Based on JSON2 (http://www.JSON.org/js.html).
  1153. */
  1154. (function (exports, nativeJSON) {
  1155. "use strict";
  1156. // use native JSON if it's available
  1157. if (nativeJSON && nativeJSON.parse){
  1158. return exports.JSON = {
  1159. parse: nativeJSON.parse
  1160. , stringify: nativeJSON.stringify
  1161. };
  1162. }
  1163. var JSON = exports.JSON = {};
  1164. function f(n) {
  1165. // Format integers to have at least two digits.
  1166. return n < 10 ? '0' + n : n;
  1167. }
  1168. function date(d, key) {
  1169. return isFinite(d.valueOf()) ?
  1170. d.getUTCFullYear() + '-' +
  1171. f(d.getUTCMonth() + 1) + '-' +
  1172. f(d.getUTCDate()) + 'T' +
  1173. f(d.getUTCHours()) + ':' +
  1174. f(d.getUTCMinutes()) + ':' +
  1175. f(d.getUTCSeconds()) + 'Z' : null;
  1176. };
  1177. var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
  1178. escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
  1179. gap,
  1180. indent,
  1181. meta = { // table of character substitutions
  1182. '\b': '\\b',
  1183. '\t': '\\t',
  1184. '\n': '\\n',
  1185. '\f': '\\f',
  1186. '\r': '\\r',
  1187. '"' : '\\"',
  1188. '\\': '\\\\'
  1189. },
  1190. rep;
  1191. function quote(string) {
  1192. // If the string contains no control characters, no quote characters, and no
  1193. // backslash characters, then we can safely slap some quotes around it.
  1194. // Otherwise we must also replace the offending characters with safe escape
  1195. // sequences.
  1196. escapable.lastIndex = 0;
  1197. return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
  1198. var c = meta[a];
  1199. return typeof c === 'string' ? c :
  1200. '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
  1201. }) + '"' : '"' + string + '"';
  1202. }
  1203. function str(key, holder) {
  1204. // Produce a string from holder[key].
  1205. var i, // The loop counter.
  1206. k, // The member key.
  1207. v, // The member value.
  1208. length,
  1209. mind = gap,
  1210. partial,
  1211. value = holder[key];
  1212. // If the value has a toJSON method, call it to obtain a replacement value.
  1213. if (value instanceof Date) {
  1214. value = date(key);
  1215. }
  1216. // If we were called with a replacer function, then call the replacer to
  1217. // obtain a replacement value.
  1218. if (typeof rep === 'function') {
  1219. value = rep.call(holder, key, value);
  1220. }
  1221. // What happens next depends on the value's type.
  1222. switch (typeof value) {
  1223. case 'string':
  1224. return quote(value);
  1225. case 'number':
  1226. // JSON numbers must be finite. Encode non-finite numbers as null.
  1227. return isFinite(value) ? String(value) : 'null';
  1228. case 'boolean':
  1229. case 'null':
  1230. // If the value is a boolean or null, convert it to a string. Note:
  1231. // typeof null does not produce 'null'. The case is included here in
  1232. // the remote chance that this gets fixed someday.
  1233. return String(value);
  1234. // If the type is 'object', we might be dealing with an object or an array or
  1235. // null.
  1236. case 'object':
  1237. // Due to a specification blunder in ECMAScript, typeof null is 'object',
  1238. // so watch out for that case.
  1239. if (!value) {
  1240. return 'null';
  1241. }
  1242. // Make an array to hold the partial results of stringifying this object value.
  1243. gap += indent;
  1244. partial = [];
  1245. // Is the value an array?
  1246. if (Object.prototype.toString.apply(value) === '[object Array]') {
  1247. // The value is an array. Stringify every element. Use null as a placeholder
  1248. // for non-JSON values.
  1249. length = value.length;
  1250. for (i = 0; i < length; i += 1) {
  1251. partial[i] = str(i, value) || 'null';
  1252. }
  1253. // Join all of the elements together, separated with commas, and wrap them in
  1254. // brackets.
  1255. v = partial.length === 0 ? '[]' : gap ?
  1256. '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
  1257. '[' + partial.join(',') + ']';
  1258. gap = mind;
  1259. return v;
  1260. }
  1261. // If the replacer is an array, use it to select the members to be stringified.
  1262. if (rep && typeof rep === 'object') {
  1263. length = rep.length;
  1264. for (i = 0; i < length; i += 1) {
  1265. if (typeof rep[i] === 'string') {
  1266. k = rep[i];
  1267. v = str(k, value);
  1268. if (v) {
  1269. partial.push(quote(k) + (gap ? ': ' : ':') + v);
  1270. }
  1271. }
  1272. }
  1273. } else {
  1274. // Otherwise, iterate through all of the keys in the object.
  1275. for (k in value) {
  1276. if (Object.prototype.hasOwnProperty.call(value, k)) {
  1277. v = str(k, value);
  1278. if (v) {
  1279. partial.push(quote(k) + (gap ? ': ' : ':') + v);
  1280. }
  1281. }
  1282. }
  1283. }
  1284. // Join all of the member texts together, separated with commas,
  1285. // and wrap them in braces.
  1286. v = partial.length === 0 ? '{}' : gap ?
  1287. '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
  1288. '{' + partial.join(',') + '}';
  1289. gap = mind;
  1290. return v;
  1291. }
  1292. }
  1293. // If the JSON object does not yet have a stringify method, give it one.
  1294. JSON.stringify = function (value, replacer, space) {
  1295. // The stringify method takes a value and an optional replacer, and an optional
  1296. // space parameter, and returns a JSON text. The replacer can be a function
  1297. // that can replace values, or an array of strings that will select the keys.
  1298. // A default replacer method can be provided. Use of the space parameter can
  1299. // produce text that is more easily readable.
  1300. var i;
  1301. gap = '';
  1302. indent = '';
  1303. // If the space parameter is a number, make an indent string containing that
  1304. // many spaces.
  1305. if (typeof space === 'number') {
  1306. for (i = 0; i < space; i += 1) {
  1307. indent += ' ';
  1308. }
  1309. // If the space parameter is a string, it will be used as the indent string.
  1310. } else if (typeof space === 'string') {
  1311. indent = space;
  1312. }
  1313. // If there is a replacer, it must be a function or an array.
  1314. // Otherwise, throw an error.
  1315. rep = replacer;
  1316. if (replacer && typeof replacer !== 'function' &&
  1317. (typeof replacer !== 'object' ||
  1318. typeof replacer.length !== 'number')) {
  1319. throw new Error('JSON.stringify');
  1320. }
  1321. // Make a fake root object containing our value under the key of ''.
  1322. // Return the result of stringifying the value.
  1323. return str('', {'': value});
  1324. };
  1325. // If the JSON object does not yet have a parse method, give it one.
  1326. JSON.parse = function (text, reviver) {
  1327. // The parse method takes a text and an optional reviver function, and returns
  1328. // a JavaScript value if the text is a valid JSON text.
  1329. var j;
  1330. function walk(holder, key) {
  1331. // The walk method is used to recursively walk the resulting structure so
  1332. // that modifications can be made.
  1333. var k, v, value = holder[key];
  1334. if (value && typeof value === 'object') {
  1335. for (k in value) {
  1336. if (Object.prototype.hasOwnProperty.call(value, k)) {
  1337. v = walk(value, k);
  1338. if (v !== undefined) {
  1339. value[k] = v;
  1340. } else {
  1341. delete value[k];
  1342. }
  1343. }
  1344. }
  1345. }
  1346. return reviver.call(holder, key, value);
  1347. }
  1348. // Parsing happens in four stages. In the first stage, we replace certain
  1349. // Unicode characters with escape sequences. JavaScript handles many characters
  1350. // incorrectly, either silently deleting them, or treating them as line endings.
  1351. text = String(text);
  1352. cx.lastIndex = 0;
  1353. if (cx.test(text)) {
  1354. text = text.replace(cx, function (a) {
  1355. return '\\u' +
  1356. ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
  1357. });
  1358. }
  1359. // In the second stage, we run the text against regular expressions that look
  1360. // for non-JSON patterns. We are especially concerned with '()' and 'new'
  1361. // because they can cause invocation, and '=' because it can cause mutation.
  1362. // But just to be safe, we want to reject all unexpected forms.
  1363. // We split the second stage into 4 regexp operations in order to work around
  1364. // crippling inefficiencies in IE's and Safari's regexp engines. First we
  1365. // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
  1366. // replace all simple value tokens with ']' characters. Third, we delete all
  1367. // open brackets that follow a colon or comma or that begin the text. Finally,
  1368. // we look to see that the remaining characters are only whitespace or ']' or
  1369. // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
  1370. if (/^[\],:{}\s]*$/
  1371. .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
  1372. .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
  1373. .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
  1374. // In the third stage we use the eval function to compile the text into a
  1375. // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
  1376. // in JavaScript: it can begin a block or an object literal. We wrap the text
  1377. // in parens to eliminate the ambiguity.
  1378. j = eval('(' + text + ')');
  1379. // In the optional fourth stage, we recursively walk the new structure, passing
  1380. // each name/value pair to a reviver function for possible transformation.
  1381. return typeof reviver === 'function' ?
  1382. walk({'': j}, '') : j;
  1383. }
  1384. // If the text is not JSON parseable, then a SyntaxError is thrown.
  1385. throw new SyntaxError('JSON.parse');
  1386. };
  1387. })(
  1388. 'undefined' != typeof io ? io : module.exports
  1389. , typeof JSON !== 'undefined' ? JSON : undefined
  1390. );
  1391. /**
  1392. * socket.io
  1393. * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
  1394. * MIT Licensed
  1395. */
  1396. (function (exports, io) {
  1397. /**
  1398. * Parser namespace.
  1399. *
  1400. * @namespace
  1401. */
  1402. var parser = exports.parser = {};
  1403. /**
  1404. * Packet types.
  1405. */
  1406. var packets = parser.packets = [
  1407. 'disconnect'
  1408. , 'connect'
  1409. , 'heartbeat'
  1410. , 'message'
  1411. , 'json'
  1412. , 'event'
  1413. , 'ack'
  1414. , 'error'
  1415. , 'noop'
  1416. ];
  1417. /**
  1418. * Errors reasons.
  1419. */
  1420. var reasons = parser.reasons = [
  1421. 'transport not supported'
  1422. , 'client not handshaken'
  1423. , 'unauthorized'
  1424. ];
  1425. /**
  1426. * Errors advice.
  1427. */
  1428. var advice = parser.advice = [
  1429. 'reconnect'
  1430. ];
  1431. /**
  1432. * Shortcuts.
  1433. */
  1434. var JSON = io.JSON
  1435. , indexOf = io.util.indexOf;
  1436. /**
  1437. * Encodes a packet.
  1438. *
  1439. * @api private
  1440. */
  1441. parser.encodePacket = function (packet) {
  1442. var type = indexOf(packets, packet.type)
  1443. , id = packet.id || ''
  1444. , endpoint = packet.endpoint || ''
  1445. , ack = packet.ack
  1446. , data = null;
  1447. switch (packet.type) {
  1448. case 'error':
  1449. var reason = packet.reason ? indexOf(reasons, packet.reason) : ''
  1450. , adv = packet.advice ? indexOf(advice, packet.advice) : '';
  1451. if (reason !== '' || adv !== '')
  1452. data = reason + (adv !== '' ? ('+' + adv) : '');
  1453. break;
  1454. case 'message':
  1455. if (packet.data !== '')
  1456. data = packet.data;
  1457. break;
  1458. case 'event':
  1459. var ev = { name: packet.name };
  1460. if (packet.args && packet.args.length) {
  1461. ev.args = packet.args;
  1462. }
  1463. data = JSON.stringify(ev);
  1464. break;
  1465. case 'json':
  1466. data = JSON.stringify(packet.data);
  1467. break;
  1468. case 'connect':
  1469. if (packet.qs)
  1470. data = packet.qs;
  1471. break;
  1472. case 'ack':
  1473. data = packet.ackId
  1474. + (packet.args && packet.args.length
  1475. ? '+' + JSON.stringify(packet.args) : '');
  1476. break;
  1477. }
  1478. // construct packet with required fragments
  1479. var encoded = [
  1480. type
  1481. , id + (ack == 'data' ? '+' : '')
  1482. , endpoint
  1483. ];
  1484. // data fragment is optional
  1485. if (data !== null && data !== undefined)
  1486. encoded.push(data);
  1487. return encoded.join(':');
  1488. };
  1489. /**
  1490. * Encodes multiple messages (payload).
  1491. *
  1492. * @param {Array} messages
  1493. * @api private
  1494. */
  1495. parser.encodePayload = function (packets) {
  1496. var decoded = '';
  1497. if (packets.length == 1)
  1498. return packets[0];
  1499. for (var i = 0, l = packets.length; i < l; i++) {
  1500. var packet = packets[i];
  1501. decoded += '\ufffd' + packet.length + '\ufffd' + packets[i];
  1502. }
  1503. return decoded;
  1504. };
  1505. /**
  1506. * Decodes a packet
  1507. *
  1508. * @api private
  1509. */
  1510. var regexp = /([^:]+):([0-9]+)?(\+)?:([^:]+)?:?([\s\S]*)?/;
  1511. parser.decodePacket = function (data) {
  1512. var pieces = data.match(regexp);
  1513. if (!pieces) return {};
  1514. var id = pieces[2] || ''
  1515. , data = pieces[5] || ''
  1516. , packet = {
  1517. type: packets[pieces[1]]
  1518. , endpoint: pieces[4] || ''
  1519. };
  1520. // whether we need to acknowledge the packet
  1521. if (id) {
  1522. packet.id = id;
  1523. if (pieces[3])
  1524. packet.ack = 'data';
  1525. else
  1526. packet.ack = true;
  1527. }
  1528. // handle different packet types
  1529. switch (packet.type) {
  1530. case 'error':
  1531. var pieces = data.split('+');
  1532. packet.reason = reasons[pieces[0]] || '';
  1533. packet.advice = advice[pieces[1]] || '';
  1534. break;
  1535. case 'message':
  1536. packet.data = data || '';
  1537. break;
  1538. case 'event':
  1539. try {
  1540. var opts = JSON.parse(data);
  1541. packet.name = opts.name;
  1542. packet.args = opts.args;
  1543. } catch (e) { }
  1544. packet.args = packet.args || [];
  1545. break;
  1546. case 'json':
  1547. try {
  1548. packet.data = JSON.parse(data);
  1549. } catch (e) { }
  1550. break;
  1551. case 'connect':
  1552. packet.qs = data || '';
  1553. break;
  1554. case 'ack':
  1555. var pieces = data.match(/^([0-9]+)(\+)?(.*)/);
  1556. if (pieces) {
  1557. packet.ackId = pieces[1];
  1558. packet.args = [];
  1559. if (pieces[3]) {
  1560. try {
  1561. packet.args = pieces[3] ? JSON.parse(pieces[3]) : [];
  1562. } catch (e) { }
  1563. }
  1564. }
  1565. break;
  1566. case 'disconnect':
  1567. case 'heartbeat':
  1568. break;
  1569. };
  1570. return packet;
  1571. };
  1572. /**
  1573. * Decodes data payload. Detects multiple messages
  1574. *
  1575. * @return {Array} messages
  1576. * @api public
  1577. */
  1578. parser.decodePayload = function (data) {
  1579. // IE doesn't like data[i] for unicode chars, charAt works fine
  1580. if (data.charAt(0) == '\ufffd') {
  1581. var ret = [];
  1582. for (var i = 1, length = ''; i < data.length; i++) {
  1583. if (data.charAt(i) == '\ufffd') {
  1584. ret.push(parser.decodePacket(data.substr(i + 1).substr(0, length)));
  1585. i += Number(length) + 1;
  1586. length = '';
  1587. } else {
  1588. length += data.charAt(i);
  1589. }
  1590. }
  1591. return ret;
  1592. } else {
  1593. return [parser.decodePacket(data)];
  1594. }
  1595. };
  1596. })(
  1597. 'undefined' != typeof io ? io : module.exports
  1598. , 'undefined' != typeof io ? io : module.parent.exports
  1599. );
  1600. /**
  1601. * socket.io
  1602. * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
  1603. * MIT Licensed
  1604. */
  1605. (function (exports, io) {
  1606. /**
  1607. * Expose constructor.
  1608. */
  1609. exports.Transport = Transport;
  1610. /**
  1611. * This is the transport template for all supported transport methods.
  1612. *
  1613. * @constructor
  1614. * @api public
  1615. */
  1616. function Transport (socket, sessid) {
  1617. this.socket = socket;
  1618. this.sessid = sessid;
  1619. };
  1620. /**
  1621. * Apply EventEmitter mixin.
  1622. */
  1623. io.util.mixin(Transport, io.EventEmitter);
  1624. /**
  1625. * Indicates whether heartbeats is enabled for this transport
  1626. *
  1627. * @api private
  1628. */
  1629. Transport.prototype.heartbeats = function () {
  1630. return true;
  1631. };
  1632. /**
  1633. * Handles the response from the server. When a new response is received
  1634. * it will automatically update the timeout, decode the message and
  1635. * forwards the response to the onMessage function for further processing.
  1636. *
  1637. * @param {String} data Response from the server.
  1638. * @api private
  1639. */
  1640. Transport.prototype.onData = function (data) {
  1641. this.clearCloseTimeout();
  1642. // If the connection in currently open (or in a reopening state) reset the close
  1643. // timeout since we have just received data. This check is necessary so
  1644. // that we don't reset the timeout on an explicitly disconnected connection.
  1645. if (this.socket.connected || this.socket.connecting || this.socket.reconnecting) {
  1646. this.setCloseTimeout();
  1647. }
  1648. if (data !== '') {
  1649. // todo: we should only do decodePayload for xhr transports
  1650. var msgs = io.parser.decodePayload(data);
  1651. if (msgs && msgs.length) {
  1652. for (var i = 0, l = msgs.length; i < l; i++) {
  1653. this.onPacket(msgs[i]);
  1654. }
  1655. }
  1656. }
  1657. return this;
  1658. };
  1659. /**
  1660. * Handles packets.
  1661. *
  1662. * @api private
  1663. */
  1664. Transport.prototype.onPacket = function (packet) {
  1665. this.socket.setHeartbeatTimeout();
  1666. if (packet.type == 'heartbeat') {
  1667. return this.onHeartbeat();
  1668. }
  1669. if (packet.type == 'connect' && packet.endpoint == '') {
  1670. this.onConnect();
  1671. }
  1672. if (packet.type == 'error' && packet.advice == 'reconnect') {
  1673. this.isOpen = false;
  1674. }
  1675. this.socket.onPacket(packet);
  1676. return this;
  1677. };
  1678. /**
  1679. * Sets close timeout
  1680. *
  1681. * @api private
  1682. */
  1683. Transport.prototype.setCloseTimeout = function () {
  1684. if (!this.closeTimeout) {
  1685. var self = this;
  1686. this.closeTimeout = setTimeout(function () {
  1687. self.onDisconnect();
  1688. }, this.socket.closeTimeout);
  1689. }
  1690. };
  1691. /**
  1692. * Called when transport disconnects.
  1693. *
  1694. * @api private
  1695. */
  1696. Transport.prototype.onDisconnect = function () {
  1697. if (this.isOpen) this.close();
  1698. this.clearTimeouts();
  1699. this.socket.onDisconnect();
  1700. return this;
  1701. };
  1702. /**
  1703. * Called when transport connects
  1704. *
  1705. * @api private
  1706. */
  1707. Transport.prototype.onConnect = function () {
  1708. this.socket.onConnect();
  1709. return this;
  1710. };
  1711. /**
  1712. * Clears close timeout
  1713. *
  1714. * @api private
  1715. */
  1716. Transport.prototype.clearCloseTimeout = function () {
  1717. if (this.closeTimeout) {
  1718. clearTimeout(this.closeTimeout);
  1719. this.closeTimeout = null;
  1720. }
  1721. };
  1722. /**
  1723. * Clear timeouts
  1724. *
  1725. * @api private
  1726. */
  1727. Transport.prototype.clearTimeouts = function () {
  1728. this.clearCloseTimeout();
  1729. if (this.reopenTimeout) {
  1730. clearTimeout(this.reopenTimeout);
  1731. }
  1732. };
  1733. /**
  1734. * Sends a packet
  1735. *
  1736. * @param {Object} packet object.
  1737. * @api private
  1738. */
  1739. Transport.prototype.packet = function (packet) {
  1740. this.send(io.parser.encodePacket(packet));
  1741. };
  1742. /**
  1743. * Send the received heartbeat message back to server. So the server
  1744. * knows we are still connected.
  1745. *
  1746. * @param {String} heartbeat Heartbeat response from the server.
  1747. * @api private
  1748. */
  1749. Transport.prototype.onHeartbeat = function (heartbeat) {
  1750. this.packet({ type: 'heartbeat' });
  1751. };
  1752. /**
  1753. * Called when the transport opens.
  1754. *
  1755. * @api private
  1756. */
  1757. Transport.prototype.onOpen = function () {
  1758. this.isOpen = true;
  1759. this.clearCloseTimeout();
  1760. this.socket.onOpen();
  1761. };
  1762. /**
  1763. * Notifies the base when the connection with the Socket.IO server
  1764. * has been disconnected.
  1765. *
  1766. * @api private
  1767. */
  1768. Transport.prototype.onClose = function () {
  1769. var self = this;
  1770. /* FIXME: reopen delay causing a infinit loop
  1771. this.reopenTimeout = setTimeout(function () {
  1772. self.open();
  1773. }, this.socket.options['reopen delay']);*/
  1774. this.isOpen = false;
  1775. this.socket.onClose();
  1776. this.onDisconnect();
  1777. };
  1778. /**
  1779. * Generates a connection url based on the Socket.IO URL Protocol.
  1780. * See <https://github.com/learnboost/socket.io-node/> for more details.
  1781. *
  1782. * @returns {String} Connection url
  1783. * @api private
  1784. */
  1785. Transport.prototype.prepareUrl = function () {
  1786. var options = this.socket.options;
  1787. return this.scheme() + '://'
  1788. + options.host + ':' + options.port + '/'
  1789. + options.resource + '/' + io.protocol
  1790. + '/' + this.name + '/' + this.sessid;
  1791. };
  1792. /**
  1793. * Checks if the transport is ready to start a connection.
  1794. *
  1795. * @param {Socket} socket The socket instance that needs a transport
  1796. * @param {Function} fn The callback
  1797. * @api private
  1798. */
  1799. Transport.prototype.ready = function (socket, fn) {
  1800. fn.call(this);
  1801. };
  1802. })(
  1803. 'undefined' != typeof io ? io : module.exports
  1804. , 'undefined' != typeof io ? io : module.parent.exports
  1805. );
  1806. /**
  1807. * socket.io
  1808. * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
  1809. * MIT Licensed
  1810. */
  1811. (function (exports, io, global) {
  1812. /**
  1813. * Expose constructor.
  1814. */
  1815. exports.Socket = Socket;
  1816. /**
  1817. * Create a new `Socket.IO client` which can establish a persistent
  1818. * connection with a Socket.IO enabled server.
  1819. *
  1820. * @api public
  1821. */
  1822. function Socket (options) {
  1823. this.options = {
  1824. port: 80
  1825. , secure: false
  1826. , document: 'document' in global ? document : false
  1827. , resource: 'socket.io'
  1828. , transports: io.transports
  1829. , 'connect timeout': 10000
  1830. , 'try multiple transports': true
  1831. , 'reconnect': true
  1832. , 'reconnection delay': 500
  1833. , 'reconnection limit': Infinity
  1834. , 'reopen delay': 3000
  1835. , 'max reconnection attempts': 10
  1836. , 'sync disconnect on unload': false
  1837. , 'auto connect': true
  1838. , 'flash policy port': 10843
  1839. , 'manualFlush': false
  1840. };
  1841. io.util.merge(this.options, options);
  1842. this.connected = false;
  1843. this.open = false;
  1844. this.connecting = false;
  1845. this.reconnecting = false;
  1846. this.namespaces = {};
  1847. this.buffer = [];
  1848. this.doBuffer = false;
  1849. if (this.options['sync disconnect on unload'] &&
  1850. (!this.isXDomain() || io.util.ua.hasCORS)) {
  1851. var self = this;
  1852. io.util.on(global, 'beforeunload', function () {
  1853. self.disconnectSync();
  1854. }, false);
  1855. }
  1856. if (this.options['auto connect']) {
  1857. this.connect();
  1858. }
  1859. };
  1860. /**
  1861. * Apply EventEmitter mixin.
  1862. */
  1863. io.util.mixin(Socket, io.EventEmitter);
  1864. /**
  1865. * Returns a namespace listener/emitter for this socket
  1866. *
  1867. * @api public
  1868. */
  1869. Socket.prototype.of = function (name) {
  1870. if (!this.namespaces[name]) {
  1871. this.namespaces[name] = new io.SocketNamespace(this, name);
  1872. if (name !== '') {
  1873. this.namespaces[name].packet({ type: 'connect' });
  1874. }
  1875. }
  1876. return this.namespaces[name];
  1877. };
  1878. /**
  1879. * Emits the given event to the Socket and all namespaces
  1880. *
  1881. * @api private
  1882. */
  1883. Socket.prototype.publish = function () {
  1884. this.emit.apply(this, arguments);
  1885. var nsp;
  1886. for (var i in this.namespaces) {
  1887. if (this.namespaces.hasOwnProperty(i)) {
  1888. nsp = this.of(i);
  1889. nsp.$emit.apply(nsp, arguments);
  1890. }
  1891. }
  1892. };
  1893. /**
  1894. * Performs the handshake
  1895. *
  1896. * @api private
  1897. */
  1898. function empty () { };
  1899. Socket.prototype.handshake = function (fn) {
  1900. var self = this
  1901. , options = this.options;
  1902. function complete (data) {
  1903. if (data instanceof Error) {
  1904. self.connecting = false;
  1905. self.onError(data.message);
  1906. } else {
  1907. fn.apply(null, data.split(':'));
  1908. }
  1909. };
  1910. var url = [
  1911. 'http' + (options.secure ? 's' : '') + ':/'
  1912. , options.host + ':' + options.port
  1913. , options.resource
  1914. , io.protocol
  1915. , io.util.query(this.options.query, 't=' + +new Date)
  1916. ].join('/');
  1917. if (this.isXDomain() && !io.util.ua.hasCORS) {
  1918. var insertAt = document.getElementsByTagName('script')[0]
  1919. , script = document.createElement('script');
  1920. script.src = url + '&jsonp=' + io.j.length;
  1921. insertAt.parentNode.insertBefore(script, insertAt);
  1922. io.j.push(function (data) {
  1923. complete(data);
  1924. script.parentNode.removeChild(script);
  1925. });
  1926. } else {
  1927. var xhr = io.util.request();
  1928. xhr.open('GET', url, true);
  1929. if (this.isXDomain()) {
  1930. xhr.withCredentials = true;
  1931. }
  1932. xhr.onreadystatechange = function () {
  1933. if (xhr.readyState == 4) {
  1934. xhr.onreadystatechange = empty;
  1935. if (xhr.status == 200) {
  1936. complete(xhr.responseText);
  1937. } else if (xhr.status == 403) {
  1938. self.onError(xhr.responseText);
  1939. } else {
  1940. self.connecting = false;
  1941. !self.reconnecting && self.onError(xhr.responseText);
  1942. }
  1943. }
  1944. };
  1945. xhr.send(null);
  1946. }
  1947. };
  1948. /**
  1949. * Find an available transport based on the options supplied in the constructor.
  1950. *
  1951. * @api private
  1952. */
  1953. Socket.prototype.getTransport = function (override) {
  1954. var transports = override || this.transports, match;
  1955. for (var i = 0, transport; transport = transports[i]; i++) {
  1956. if (io.Transport[transport]
  1957. && io.Transport[transport].check(this)
  1958. && (!this.isXDomain() || io.Transport[transport].xdomainCheck(this))) {
  1959. return new io.Transport[transport](this, this.sessionid);
  1960. }
  1961. }
  1962. return null;
  1963. };
  1964. /**
  1965. * Connects to the server.
  1966. *
  1967. * @param {Function} [fn] Callback.
  1968. * @returns {io.Socket}
  1969. * @api public
  1970. */
  1971. Socket.prototype.connect = function (fn) {
  1972. if (this.connecting) {
  1973. return this;
  1974. }
  1975. var self = this;
  1976. self.connecting = true;
  1977. this.handshake(function (sid, heartbeat, close, transports) {
  1978. self.sessionid = sid;
  1979. self.closeTimeout = close * 1000;
  1980. self.heartbeatTimeout = heartbeat * 1000;
  1981. if(!self.transports)
  1982. self.transports = self.origTransports = (transports ? io.util.intersect(
  1983. transports.split(',')
  1984. , self.options.transports
  1985. ) : self.options.transports);
  1986. self.setHeartbeatTimeout();
  1987. function connect (transports){
  1988. if (self.transport) self.transport.clearTimeouts();
  1989. self.transport = self.getTransport(transports);
  1990. if (!self.transport) return self.publish('connect_failed');
  1991. // once the transport is ready
  1992. self.transport.ready(self, function () {
  1993. self.connecting = true;
  1994. self.publish('connecting', self.transport.name);
  1995. self.transport.open();
  1996. if (self.options['connect timeout']) {
  1997. self.connectTimeoutTimer = setTimeout(function () {
  1998. if (!self.connected) {
  1999. self.connecting = false;
  2000. if (self.options['try multiple transports']) {
  2001. var remaining = self.transports;
  2002. while (remaining.length > 0 && remaining.splice(0,1)[0] !=
  2003. self.transport.name) {}
  2004. if (remaining.length){
  2005. connect(remaining);
  2006. } else {
  2007. self.publish('connect_failed');
  2008. }
  2009. }
  2010. }
  2011. }, self.options['connect timeout']);
  2012. }
  2013. });
  2014. }
  2015. connect(self.transports);
  2016. self.once('connect', function (){
  2017. clearTimeout(self.connectTimeoutTimer);
  2018. fn && typeof fn == 'function' && fn();
  2019. });
  2020. });
  2021. return this;
  2022. };
  2023. /**
  2024. * Clears and sets a new heartbeat timeout using the value given by the
  2025. * server during the handshake.
  2026. *
  2027. * @api private
  2028. */
  2029. Socket.prototype.setHeartbeatTimeout = function () {
  2030. clearTimeout(this.heartbeatTimeoutTimer);
  2031. if(this.transport && !this.transport.heartbeats()) return;
  2032. var self = this;
  2033. this.heartbeatTimeoutTimer = setTimeout(function () {
  2034. self.transport.onClose();
  2035. }, this.heartbeatTimeout);
  2036. };
  2037. /**
  2038. * Sends a message.
  2039. *
  2040. * @param {Object} data packet.
  2041. * @returns {io.Socket}
  2042. * @api public
  2043. */
  2044. Socket.prototype.packet = function (data) {
  2045. if (this.connected && !this.doBuffer) {
  2046. this.transport.packet(data);
  2047. } else {
  2048. this.buffer.push(data);
  2049. }
  2050. return this;
  2051. };
  2052. /**
  2053. * Sets buffer state
  2054. *
  2055. * @api private
  2056. */
  2057. Socket.prototype.setBuffer = function (v) {
  2058. this.doBuffer = v;
  2059. if (!v && this.connected && this.buffer.length) {
  2060. if (!this.options['manualFlush']) {
  2061. this.flushBuffer();
  2062. }
  2063. }
  2064. };
  2065. /**
  2066. * Flushes the buffer data over the wire.
  2067. * To be invoked manually when 'manualFlush' is set to true.
  2068. *
  2069. * @api public
  2070. */
  2071. Socket.prototype.flushBuffer = function() {
  2072. this.transport.payload(this.buffer);
  2073. this.buffer = [];
  2074. };
  2075. /**
  2076. * Disconnect the established connect.
  2077. *
  2078. * @returns {io.Socket}
  2079. * @api public
  2080. */
  2081. Socket.prototype.disconnect = function () {
  2082. if (this.connected || this.connecting) {
  2083. if (this.open) {
  2084. this.of('').packet({ type: 'disconnect' });
  2085. }
  2086. // handle disconnection immediately
  2087. this.onDisconnect('booted');
  2088. }
  2089. return this;
  2090. };
  2091. /**
  2092. * Disconnects the socket with a sync XHR.
  2093. *
  2094. * @api private
  2095. */
  2096. Socket.prototype.disconnectSync = function () {
  2097. // ensure disconnection
  2098. var xhr = io.util.request();
  2099. var uri = [
  2100. 'http' + (this.options.secure ? 's' : '') + ':/'
  2101. , this.options.host + ':' + this.options.port
  2102. , this.options.resource
  2103. , io.protocol
  2104. , ''
  2105. , this.sessionid
  2106. ].join('/') + '/?disconnect=1';
  2107. xhr.open('GET', uri, false);
  2108. xhr.send(null);
  2109. // handle disconnection immediately
  2110. this.onDisconnect('booted');
  2111. };
  2112. /**
  2113. * Check if we need to use cross domain enabled transports. Cross domain would
  2114. * be a different port or different domain name.
  2115. *
  2116. * @returns {Boolean}
  2117. * @api private
  2118. */
  2119. Socket.prototype.isXDomain = function () {
  2120. var port = global.location.port ||
  2121. ('https:' == global.location.protocol ? 443 : 80);
  2122. return this.options.host !== global.location.hostname
  2123. || this.options.port != port;
  2124. };
  2125. /**
  2126. * Called upon handshake.
  2127. *
  2128. * @api private
  2129. */
  2130. Socket.prototype.onConnect = function () {
  2131. if (!this.connected) {
  2132. this.connected = true;
  2133. this.connecting = false;
  2134. if (!this.doBuffer) {
  2135. // make sure to flush the buffer
  2136. this.setBuffer(false);
  2137. }
  2138. this.emit('connect');
  2139. }
  2140. };
  2141. /**
  2142. * Called when the transport opens
  2143. *
  2144. * @api private
  2145. */
  2146. Socket.prototype.onOpen = function () {
  2147. this.open = true;
  2148. };
  2149. /**
  2150. * Called when the transport closes.
  2151. *
  2152. * @api private
  2153. */
  2154. Socket.prototype.onClose = function () {
  2155. this.open = false;
  2156. clearTimeout(this.heartbeatTimeoutTimer);
  2157. };
  2158. /**
  2159. * Called when the transport first opens a connection
  2160. *
  2161. * @param text
  2162. */
  2163. Socket.prototype.onPacket = function (packet) {
  2164. this.of(packet.endpoint).onPacket(packet);
  2165. };
  2166. /**
  2167. * Handles an error.
  2168. *
  2169. * @api private
  2170. */
  2171. Socket.prototype.onError = function (err) {
  2172. if (err && err.advice) {
  2173. if (err.advice === 'reconnect' && (this.connected || this.connecting)) {
  2174. this.disconnect();
  2175. if (this.options.reconnect) {
  2176. this.reconnect();
  2177. }
  2178. }
  2179. }
  2180. this.publish('error', err && err.reason ? err.reason : err);
  2181. };
  2182. /**
  2183. * Called when the transport disconnects.
  2184. *
  2185. * @api private
  2186. */
  2187. Socket.prototype.onDisconnect = function (reason) {
  2188. var wasConnected = this.connected
  2189. , wasConnecting = this.connecting;
  2190. this.connected = false;
  2191. this.connecting = false;
  2192. this.open = false;
  2193. if (wasConnected || wasConnecting) {
  2194. this.transport.close();
  2195. this.transport.clearTimeouts();
  2196. if (wasConnected) {
  2197. this.publish('disconnect', reason);
  2198. if ('booted' != reason && this.options.reconnect && !this.reconnecting) {
  2199. this.reconnect();
  2200. }
  2201. }
  2202. }
  2203. };
  2204. /**
  2205. * Called upon reconnection.
  2206. *
  2207. * @api private
  2208. */
  2209. Socket.prototype.reconnect = function () {
  2210. this.reconnecting = true;
  2211. this.reconnectionAttempts = 0;
  2212. this.reconnectionDelay = this.options['reconnection delay'];
  2213. var self = this
  2214. , maxAttempts = this.options['max reconnection attempts']
  2215. , tryMultiple = this.options['try multiple transports']
  2216. , limit = this.options['reconnection limit'];
  2217. function reset () {
  2218. if (self.connected) {
  2219. for (var i in self.namespaces) {
  2220. if (self.namespaces.hasOwnProperty(i) && '' !== i) {
  2221. self.namespaces[i].packet({ type: 'connect' });
  2222. }
  2223. }
  2224. self.publish('reconnect', self.transport.name, self.reconnectionAttempts);
  2225. }
  2226. clearTimeout(self.reconnectionTimer);
  2227. self.removeListener('connect_failed', maybeReconnect);
  2228. self.removeListener('connect', maybeReconnect);
  2229. self.reconnecting = false;
  2230. delete self.reconnectionAttempts;
  2231. delete self.reconnectionDelay;
  2232. delete self.reconnectionTimer;
  2233. delete self.redoTransports;
  2234. self.options['try multiple transports'] = tryMultiple;
  2235. };
  2236. function maybeReconnect () {
  2237. if (!self.reconnecting) {
  2238. return;
  2239. }
  2240. if (self.connected) {
  2241. return reset();
  2242. };
  2243. if (self.connecting && self.reconnecting) {
  2244. return self.reconnectionTimer = setTimeout(maybeReconnect, 1000);
  2245. }
  2246. if (self.reconnectionAttempts++ >= maxAttempts) {
  2247. if (!self.redoTransports) {
  2248. self.on('connect_failed', maybeReconnect);
  2249. self.options['try multiple transports'] = true;
  2250. self.transports = self.origTransports;
  2251. self.transport = self.getTransport();
  2252. self.redoTransports = true;
  2253. self.connect();
  2254. } else {
  2255. self.publish('reconnect_failed');
  2256. reset();
  2257. }
  2258. } else {
  2259. if (self.reconnectionDelay < limit) {
  2260. self.reconnectionDelay *= 2; // exponential back off
  2261. }
  2262. self.connect();
  2263. self.publish('reconnecting', self.reconnectionDelay, self.reconnectionAttempts);
  2264. self.reconnectionTimer = setTimeout(maybeReconnect, self.reconnectionDelay);
  2265. }
  2266. };
  2267. this.options['try multiple transports'] = false;
  2268. this.reconnectionTimer = setTimeout(maybeReconnect, this.reconnectionDelay);
  2269. this.on('connect', maybeReconnect);
  2270. };
  2271. })(
  2272. 'undefined' != typeof io ? io : module.exports
  2273. , 'undefined' != typeof io ? io : module.parent.exports
  2274. , this
  2275. );
  2276. /**
  2277. * socket.io
  2278. * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
  2279. * MIT Licensed
  2280. */
  2281. (function (exports, io) {
  2282. /**
  2283. * Expose constructor.
  2284. */
  2285. exports.SocketNamespace = SocketNamespace;
  2286. /**
  2287. * Socket namespace constructor.
  2288. *
  2289. * @constructor
  2290. * @api public
  2291. */
  2292. function SocketNamespace (socket, name) {
  2293. this.socket = socket;
  2294. this.name = name || '';
  2295. this.flags = {};
  2296. this.json = new Flag(this, 'json');
  2297. this.ackPackets = 0;
  2298. this.acks = {};
  2299. };
  2300. /**
  2301. * Apply EventEmitter mixin.
  2302. */
  2303. io.util.mixin(SocketNamespace, io.EventEmitter);
  2304. /**
  2305. * Copies emit since we override it
  2306. *
  2307. * @api private
  2308. */
  2309. SocketNamespace.prototype.$emit = io.EventEmitter.prototype.emit;
  2310. /**
  2311. * Creates a new namespace, by proxying the request to the socket. This
  2312. * allows us to use the synax as we do on the server.
  2313. *
  2314. * @api public
  2315. */
  2316. SocketNamespace.prototype.of = function () {
  2317. return this.socket.of.apply(this.socket, arguments);
  2318. };
  2319. /**
  2320. * Sends a packet.
  2321. *
  2322. * @api private
  2323. */
  2324. SocketNamespace.prototype.packet = function (packet) {
  2325. packet.endpoint = this.name;
  2326. this.socket.packet(packet);
  2327. this.flags = {};
  2328. return this;
  2329. };
  2330. /**
  2331. * Sends a message
  2332. *
  2333. * @api public
  2334. */
  2335. SocketNamespace.prototype.send = function (data, fn) {
  2336. var packet = {
  2337. type: this.flags.json ? 'json' : 'message'
  2338. , data: data
  2339. };
  2340. if ('function' == typeof fn) {
  2341. packet.id = ++this.ackPackets;
  2342. packet.ack = true;
  2343. this.acks[packet.id] = fn;
  2344. }
  2345. return this.packet(packet);
  2346. };
  2347. /**
  2348. * Emits an event
  2349. *
  2350. * @api public
  2351. */
  2352. SocketNamespace.prototype.emit = function (name) {
  2353. var args = Array.prototype.slice.call(arguments, 1)
  2354. , lastArg = args[args.length - 1]
  2355. , packet = {
  2356. type: 'event'
  2357. , name: name
  2358. };
  2359. if ('function' == typeof lastArg) {
  2360. packet.id = ++this.ackPackets;
  2361. packet.ack = 'data';
  2362. this.acks[packet.id] = lastArg;
  2363. args = args.slice(0, args.length - 1);
  2364. }
  2365. packet.args = args;
  2366. return this.packet(packet);
  2367. };
  2368. /**
  2369. * Disconnects the namespace
  2370. *
  2371. * @api private
  2372. */
  2373. SocketNamespace.prototype.disconnect = function () {
  2374. if (this.name === '') {
  2375. this.socket.disconnect();
  2376. } else {
  2377. this.packet({ type: 'disconnect' });
  2378. this.$emit('disconnect');
  2379. }
  2380. return this;
  2381. };
  2382. /**
  2383. * Handles a packet
  2384. *
  2385. * @api private
  2386. */
  2387. SocketNamespace.prototype.onPacket = function (packet) {
  2388. var self = this;
  2389. function ack () {
  2390. self.packet({
  2391. type: 'ack'
  2392. , args: io.util.toArray(arguments)
  2393. , ackId: packet.id
  2394. });
  2395. };
  2396. switch (packet.type) {
  2397. case 'connect':
  2398. this.$emit('connect');
  2399. break;
  2400. case 'disconnect':
  2401. if (this.name === '') {
  2402. this.socket.onDisconnect(packet.reason || 'booted');
  2403. } else {
  2404. this.$emit('disconnect', packet.reason);
  2405. }
  2406. break;
  2407. case 'message':
  2408. case 'json':
  2409. var params = ['message', packet.data];
  2410. if (packet.ack == 'data') {
  2411. params.push(ack);
  2412. } else if (packet.ack) {
  2413. this.packet({ type: 'ack', ackId: packet.id });
  2414. }
  2415. this.$emit.apply(this, params);
  2416. break;
  2417. case 'event':
  2418. var params = [packet.name].concat(packet.args);
  2419. if (packet.ack == 'data')
  2420. params.push(ack);
  2421. this.$emit.apply(this, params);
  2422. break;
  2423. case 'ack':
  2424. if (this.acks[packet.ackId]) {
  2425. this.acks[packet.ackId].apply(this, packet.args);
  2426. delete this.acks[packet.ackId];
  2427. }
  2428. break;
  2429. case 'error':
  2430. if (packet.advice){
  2431. this.socket.onError(packet);
  2432. } else {
  2433. if (packet.reason == 'unauthorized') {
  2434. this.$emit('connect_failed', packet.reason);
  2435. } else {
  2436. this.$emit('error', packet.reason);
  2437. }
  2438. }
  2439. break;
  2440. }
  2441. };
  2442. /**
  2443. * Flag interface.
  2444. *
  2445. * @api private
  2446. */
  2447. function Flag (nsp, name) {
  2448. this.namespace = nsp;
  2449. this.name = name;
  2450. };
  2451. /**
  2452. * Send a message
  2453. *
  2454. * @api public
  2455. */
  2456. Flag.prototype.send = function () {
  2457. this.namespace.flags[this.name] = true;
  2458. this.namespace.send.apply(this.namespace, arguments);
  2459. };
  2460. /**
  2461. * Emit an event
  2462. *
  2463. * @api public
  2464. */
  2465. Flag.prototype.emit = function () {
  2466. this.namespace.flags[this.name] = true;
  2467. this.namespace.emit.apply(this.namespace, arguments);
  2468. };
  2469. })(
  2470. 'undefined' != typeof io ? io : module.exports
  2471. , 'undefined' != typeof io ? io : module.parent.exports
  2472. );
  2473. /**
  2474. * socket.io
  2475. * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
  2476. * MIT Licensed
  2477. */
  2478. (function (exports, io, global) {
  2479. /**
  2480. * Expose constructor.
  2481. */
  2482. exports.websocket = WS;
  2483. /**
  2484. * The WebSocket transport uses the HTML5 WebSocket API to establish an
  2485. * persistent connection with the Socket.IO server. This transport will also
  2486. * be inherited by the FlashSocket fallback as it provides a API compatible
  2487. * polyfill for the WebSockets.
  2488. *
  2489. * @constructor
  2490. * @extends {io.Transport}
  2491. * @api public
  2492. */
  2493. function WS (socket) {
  2494. io.Transport.apply(this, arguments);
  2495. };
  2496. /**
  2497. * Inherits from Transport.
  2498. */
  2499. io.util.inherit(WS, io.Transport);
  2500. /**
  2501. * Transport name
  2502. *
  2503. * @api public
  2504. */
  2505. WS.prototype.name = 'websocket';
  2506. /**
  2507. * Initializes a new `WebSocket` connection with the Socket.IO server. We attach
  2508. * all the appropriate listeners to handle the responses from the server.
  2509. *
  2510. * @returns {Transport}
  2511. * @api public
  2512. */
  2513. WS.prototype.open = function () {
  2514. var query = io.util.query(this.socket.options.query)
  2515. , self = this
  2516. , Socket
  2517. if (!Socket) {
  2518. Socket = global.MozWebSocket || global.WebSocket;
  2519. }
  2520. this.websocket = new Socket(this.prepareUrl() + query);
  2521. this.websocket.onopen = function () {
  2522. self.onOpen();
  2523. self.socket.setBuffer(false);
  2524. };
  2525. this.websocket.onmessage = function (ev) {
  2526. self.onData(ev.data);
  2527. };
  2528. this.websocket.onclose = function () {
  2529. self.onClose();
  2530. self.socket.setBuffer(true);
  2531. };
  2532. this.websocket.onerror = function (e) {
  2533. self.onError(e);
  2534. };
  2535. return this;
  2536. };
  2537. /**
  2538. * Send a message to the Socket.IO server. The message will automatically be
  2539. * encoded in the correct message format.
  2540. *
  2541. * @returns {Transport}
  2542. * @api public
  2543. */
  2544. // Do to a bug in the current IDevices browser, we need to wrap the send in a
  2545. // setTimeout, when they resume from sleeping the browser will crash if
  2546. // we don't allow the browser time to detect the socket has been closed
  2547. if (io.util.ua.iDevice) {
  2548. WS.prototype.send = function (data) {
  2549. var self = this;
  2550. setTimeout(function() {
  2551. self.websocket.send(data);
  2552. },0);
  2553. return this;
  2554. };
  2555. } else {
  2556. WS.prototype.send = function (data) {
  2557. this.websocket.send(data);
  2558. return this;
  2559. };
  2560. }
  2561. /**
  2562. * Payload
  2563. *
  2564. * @api private
  2565. */
  2566. WS.prototype.payload = function (arr) {
  2567. for (var i = 0, l = arr.length; i < l; i++) {
  2568. this.packet(arr[i]);
  2569. }
  2570. return this;
  2571. };
  2572. /**
  2573. * Disconnect the established `WebSocket` connection.
  2574. *
  2575. * @returns {Transport}
  2576. * @api public
  2577. */
  2578. WS.prototype.close = function () {
  2579. this.websocket.close();
  2580. return this;
  2581. };
  2582. /**
  2583. * Handle the errors that `WebSocket` might be giving when we
  2584. * are attempting to connect or send messages.
  2585. *
  2586. * @param {Error} e The error.
  2587. * @api private
  2588. */
  2589. WS.prototype.onError = function (e) {
  2590. this.socket.onError(e);
  2591. };
  2592. /**
  2593. * Returns the appropriate scheme for the URI generation.
  2594. *
  2595. * @api private
  2596. */
  2597. WS.prototype.scheme = function () {
  2598. return this.socket.options.secure ? 'wss' : 'ws';
  2599. };
  2600. /**
  2601. * Checks if the browser has support for native `WebSockets` and that
  2602. * it's not the polyfill created for the FlashSocket transport.
  2603. *
  2604. * @return {Boolean}
  2605. * @api public
  2606. */
  2607. WS.check = function () {
  2608. return ('WebSocket' in global && !('__addTask' in WebSocket))
  2609. || 'MozWebSocket' in global;
  2610. };
  2611. /**
  2612. * Check if the `WebSocket` transport support cross domain communications.
  2613. *
  2614. * @returns {Boolean}
  2615. * @api public
  2616. */
  2617. WS.xdomainCheck = function () {
  2618. return true;
  2619. };
  2620. /**
  2621. * Add the transport to your public io.transports array.
  2622. *
  2623. * @api private
  2624. */
  2625. io.transports.push('websocket');
  2626. })(
  2627. 'undefined' != typeof io ? io.Transport : module.exports
  2628. , 'undefined' != typeof io ? io : module.parent.exports
  2629. , this
  2630. );
  2631. /**
  2632. * socket.io
  2633. * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
  2634. * MIT Licensed
  2635. */
  2636. (function (exports, io) {
  2637. /**
  2638. * Expose constructor.
  2639. */
  2640. exports.flashsocket = Flashsocket;
  2641. /**
  2642. * The FlashSocket transport. This is a API wrapper for the HTML5 WebSocket
  2643. * specification. It uses a .swf file to communicate with the server. If you want
  2644. * to serve the .swf file from a other server than where the Socket.IO script is
  2645. * coming from you need to use the insecure version of the .swf. More information
  2646. * about this can be found on the github page.
  2647. *
  2648. * @constructor
  2649. * @extends {io.Transport.websocket}
  2650. * @api public
  2651. */
  2652. function Flashsocket () {
  2653. io.Transport.websocket.apply(this, arguments);
  2654. };
  2655. /**
  2656. * Inherits from Transport.
  2657. */
  2658. io.util.inherit(Flashsocket, io.Transport.websocket);
  2659. /**
  2660. * Transport name
  2661. *
  2662. * @api public
  2663. */
  2664. Flashsocket.prototype.name = 'flashsocket';
  2665. /**
  2666. * Disconnect the established `FlashSocket` connection. This is done by adding a
  2667. * new task to the FlashSocket. The rest will be handled off by the `WebSocket`
  2668. * transport.
  2669. *
  2670. * @returns {Transport}
  2671. * @api public
  2672. */
  2673. Flashsocket.prototype.open = function () {
  2674. var self = this
  2675. , args = arguments;
  2676. WebSocket.__addTask(function () {
  2677. io.Transport.websocket.prototype.open.apply(self, args);
  2678. });
  2679. return this;
  2680. };
  2681. /**
  2682. * Sends a message to the Socket.IO server. This is done by adding a new
  2683. * task to the FlashSocket. The rest will be handled off by the `WebSocket`
  2684. * transport.
  2685. *
  2686. * @returns {Transport}
  2687. * @api public
  2688. */
  2689. Flashsocket.prototype.send = function () {
  2690. var self = this, args = arguments;
  2691. WebSocket.__addTask(function () {
  2692. io.Transport.websocket.prototype.send.apply(self, args);
  2693. });
  2694. return this;
  2695. };
  2696. /**
  2697. * Disconnects the established `FlashSocket` connection.
  2698. *
  2699. * @returns {Transport}
  2700. * @api public
  2701. */
  2702. Flashsocket.prototype.close = function () {
  2703. WebSocket.__tasks.length = 0;
  2704. io.Transport.websocket.prototype.close.call(this);
  2705. return this;
  2706. };
  2707. /**
  2708. * The WebSocket fall back needs to append the flash container to the body
  2709. * element, so we need to make sure we have access to it. Or defer the call
  2710. * until we are sure there is a body element.
  2711. *
  2712. * @param {Socket} socket The socket instance that needs a transport
  2713. * @param {Function} fn The callback
  2714. * @api private
  2715. */
  2716. Flashsocket.prototype.ready = function (socket, fn) {
  2717. function init () {
  2718. var options = socket.options
  2719. , port = options['flash policy port']
  2720. , path = [
  2721. 'http' + (options.secure ? 's' : '') + ':/'
  2722. , options.host + ':' + options.port
  2723. , options.resource
  2724. , 'static/flashsocket'
  2725. , 'WebSocketMain' + (socket.isXDomain() ? 'Insecure' : '') + '.swf'
  2726. ];
  2727. // Only start downloading the swf file when the checked that this browser
  2728. // actually supports it
  2729. if (!Flashsocket.loaded) {
  2730. if (typeof WEB_SOCKET_SWF_LOCATION === 'undefined') {
  2731. // Set the correct file based on the XDomain settings
  2732. WEB_SOCKET_SWF_LOCATION = path.join('/');
  2733. }
  2734. if (port !== 843) {
  2735. WebSocket.loadFlashPolicyFile('xmlsocket://' + options.host + ':' + port);
  2736. }
  2737. WebSocket.__initialize();
  2738. Flashsocket.loaded = true;
  2739. }
  2740. fn.call(self);
  2741. }
  2742. var self = this;
  2743. if (document.body) return init();
  2744. io.util.load(init);
  2745. };
  2746. /**
  2747. * Check if the FlashSocket transport is supported as it requires that the Adobe
  2748. * Flash Player plug-in version `10.0.0` or greater is installed. And also check if
  2749. * the polyfill is correctly loaded.
  2750. *
  2751. * @returns {Boolean}
  2752. * @api public
  2753. */
  2754. Flashsocket.check = function () {
  2755. if (
  2756. typeof WebSocket == 'undefined'
  2757. || !('__initialize' in WebSocket) || !swfobject
  2758. ) return false;
  2759. return swfobject.getFlashPlayerVersion().major >= 10;
  2760. };
  2761. /**
  2762. * Check if the FlashSocket transport can be used as cross domain / cross origin
  2763. * transport. Because we can't see which type (secure or insecure) of .swf is used
  2764. * we will just return true.
  2765. *
  2766. * @returns {Boolean}
  2767. * @api public
  2768. */
  2769. Flashsocket.xdomainCheck = function () {
  2770. return true;
  2771. };
  2772. /**
  2773. * Disable AUTO_INITIALIZATION
  2774. */
  2775. if (typeof window != 'undefined') {
  2776. WEB_SOCKET_DISABLE_AUTO_INITIALIZATION = true;
  2777. }
  2778. /**
  2779. * Add the transport to your public io.transports array.
  2780. *
  2781. * @api private
  2782. */
  2783. io.transports.push('flashsocket');
  2784. })(
  2785. 'undefined' != typeof io ? io.Transport : module.exports
  2786. , 'undefined' != typeof io ? io : module.parent.exports
  2787. );
  2788. /* SWFObject v2.2 <http://code.google.com/p/swfobject/>
  2789. is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
  2790. */
  2791. if ('undefined' != typeof window) {
  2792. var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O[(['Active'].concat('Object').join('X'))]!=D){try{var ad=new window[(['Active'].concat('Object').join('X'))](W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y<X;Y++){U[Y]()}}function K(X){if(J){X()}else{U[U.length]=X}}function s(Y){if(typeof O.addEventListener!=D){O.addEventListener("load",Y,false)}else{if(typeof j.addEventListener!=D){j.addEventListener("load",Y,false)}else{if(typeof O.attachEvent!=D){i(O,"onload",Y)}else{if(typeof O.onload=="function"){var X=O.onload;O.onload=function(){X();Y()}}else{O.onload=Y}}}}}function h(){if(T){V()}else{H()}}function V(){var X=j.getElementsByTagName("body")[0];var aa=C(r);aa.setAttribute("type",q);var Z=X.appendChild(aa);if(Z){var Y=0;(function(){if(typeof Z.GetVariable!=D){var ab=Z.GetVariable("$version");if(ab){ab=ab.split(" ")[1].split(",");M.pv=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}else{if(Y<10){Y++;setTimeout(arguments.callee,10);return}}X.removeChild(aa);Z=null;H()})()}else{H()}}function H(){var ag=o.length;if(ag>0){for(var af=0;af<ag;af++){var Y=o[af].id;var ab=o[af].callbackFn;var aa={success:false,id:Y};if(M.pv[0]>0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad<ac;ad++){if(X[ad].getAttribute("name").toLowerCase()!="movie"){ah[X[ad].getAttribute("name")]=X[ad].getAttribute("value")}}P(ai,ah,Y,ab)}else{p(ae);if(ab){ab(aa)}}}}}else{w(Y,true);if(ab){var Z=z(Y);if(Z&&typeof Z.SetVariable!=D){aa.success=true;aa.ref=Z}ab(aa)}}}}}function z(aa){var X=null;var Y=c(aa);if(Y&&Y.nodeName=="OBJECT"){if(typeof Y.SetVariable!=D){X=Y}else{var Z=Y.getElementsByTagName(r)[0];if(Z){X=Z}}}return X}function A(){return !a&&F("6.0.65")&&(M.win||M.mac)&&!(M.wk&&M.wk<312)}function P(aa,ab,X,Z){a=true;E=Z||null;B={success:false,id:X};var ae=c(X);if(ae){if(ae.nodeName=="OBJECT"){l=g(ae);Q=null}else{l=ae;Q=X}aa.id=R;if(typeof aa.width==D||(!/%$/.test(aa.width)&&parseInt(aa.width,10)<310)){aa.width="310"}if(typeof aa.height==D||(!/%$/.test(aa.height)&&parseInt(aa.height,10)<137)){aa.height="137"}j.title=j.title.slice(0,47)+" - Flash Player Installation";var ad=M.ie&&M.win?(['Active'].concat('').join('X')):"PlugIn",ac="MMredirectURL="+O.location.toString().replace(/&/g,"%26")+"&MMplayerType="+ad+"&MMdoctitle="+j.title;if(typeof ab.flashvars!=D){ab.flashvars+="&"+ac}else{ab.flashvars=ac}if(M.ie&&M.win&&ae.readyState!=4){var Y=C("div");X+="SWFObjectNew";Y.setAttribute("id",X);ae.parentNode.insertBefore(Y,ae);ae.style.display="none";(function(){if(ae.readyState==4){ae.parentNode.removeChild(ae)}else{setTimeout(arguments.callee,10)}})()}u(aa,ab,X)}}function p(Y){if(M.ie&&M.win&&Y.readyState!=4){var X=C("div");Y.parentNode.insertBefore(X,Y);X.parentNode.replaceChild(g(Y),X);Y.style.display="none";(function(){if(Y.readyState==4){Y.parentNode.removeChild(Y)}else{setTimeout(arguments.callee,10)}})()}else{Y.parentNode.replaceChild(g(Y),Y)}}function g(ab){var aa=C("div");if(M.win&&M.ie){aa.innerHTML=ab.innerHTML}else{var Y=ab.getElementsByTagName(r)[0];if(Y){var ad=Y.childNodes;if(ad){var X=ad.length;for(var Z=0;Z<X;Z++){if(!(ad[Z].nodeType==1&&ad[Z].nodeName=="PARAM")&&!(ad[Z].nodeType==8)){aa.appendChild(ad[Z].cloneNode(true))}}}}}return aa}function u(ai,ag,Y){var X,aa=c(Y);if(M.wk&&M.wk<312){return X}if(aa){if(typeof ai.id==D){ai.id=Y}if(M.ie&&M.win){var ah="";for(var ae in ai){if(ai[ae]!=Object.prototype[ae]){if(ae.toLowerCase()=="data"){ag.movie=ai[ae]}else{if(ae.toLowerCase()=="styleclass"){ah+=' class="'+ai[ae]+'"'}else{if(ae.toLowerCase()!="classid"){ah+=" "+ae+'="'+ai[ae]+'"'}}}}}var af="";for(var ad in ag){if(ag[ad]!=Object.prototype[ad]){af+='<param name="'+ad+'" value="'+ag[ad]+'" />'}}aa.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+ah+">"+af+"</object>";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab<ac;ab++){I[ab][0].detachEvent(I[ab][1],I[ab][2])}var Z=N.length;for(var aa=0;aa<Z;aa++){y(N[aa])}for(var Y in M){M[Y]=null}M=null;for(var X in swfobject){swfobject[X]=null}swfobject=null})}}();return{registerObject:function(ab,X,aa,Z){if(M.w3&&ab&&X){var Y={};Y.id=ab;Y.swfVersion=X;Y.expressInstall=aa;Y.callbackFn=Z;o[o.length]=Y;w(ab,false)}else{if(Z){Z({success:false,id:ab})}}},getObjectById:function(X){if(M.w3){return z(X)}},embedSWF:function(ab,ah,ae,ag,Y,aa,Z,ad,af,ac){var X={success:false,id:ah};if(M.w3&&!(M.wk&&M.wk<312)&&ab&&ah&&ae&&ag&&Y){w(ah,false);K(function(){ae+="";ag+="";var aj={};if(af&&typeof af===r){for(var al in af){aj[al]=af[al]}}aj.data=ab;aj.width=ae;aj.height=ag;var am={};if(ad&&typeof ad===r){for(var ak in ad){am[ak]=ad[ak]}}if(Z&&typeof Z===r){for(var ai in Z){if(typeof am.flashvars!=D){am.flashvars+="&"+ai+"="+Z[ai]}else{am.flashvars=ai+"="+Z[ai]}}}if(F(Y)){var an=u(aj,am,ah);if(aj.id==ah){w(ah,true)}X.success=true;X.ref=an}else{if(aa&&A()){aj.data=aa;P(aj,am,ah,ac);return}else{w(ah,true)}}if(ac){ac(X)}})}else{if(ac){ac(X)}}},switchOffAutoHideShow:function(){m=false},ua:M,getFlashPlayerVersion:function(){return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z,Y,X){if(M.w3){return u(Z,Y,X)}else{return undefined}},showExpressInstall:function(Z,aa,X,Y){if(M.w3&&A()){P(Z,aa,X,Y)}},removeSWF:function(X){if(M.w3){y(X)}},createCSS:function(aa,Z,Y,X){if(M.w3){v(aa,Z,Y,X)}},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa){var Z=j.location.search||j.location.hash;if(Z){if(/\?/.test(Z)){Z=Z.split("?")[1]}if(aa==null){return L(Z)}var Y=Z.split("&");for(var X=0;X<Y.length;X++){if(Y[X].substring(0,Y[X].indexOf("="))==aa){return L(Y[X].substring((Y[X].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(a){var X=c(R);if(X&&l){X.parentNode.replaceChild(l,X);if(Q){w(Q,true);if(M.ie&&M.win){l.style.display="block"}}if(E){E(B)}}a=false}}}}();
  2793. }
  2794. // Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
  2795. // License: New BSD License
  2796. // Reference: http://dev.w3.org/html5/websockets/
  2797. // Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol
  2798. (function() {
  2799. if ('undefined' == typeof window || window.WebSocket) return;
  2800. var console = window.console;
  2801. if (!console || !console.log || !console.error) {
  2802. console = {log: function(){ }, error: function(){ }};
  2803. }
  2804. if (!swfobject.hasFlashPlayerVersion("10.0.0")) {
  2805. console.error("Flash Player >= 10.0.0 is required.");
  2806. return;
  2807. }
  2808. if (location.protocol == "file:") {
  2809. console.error(
  2810. "WARNING: web-socket-js doesn't work in file:///... URL " +
  2811. "unless you set Flash Security Settings properly. " +
  2812. "Open the page via Web server i.e. http://...");
  2813. }
  2814. /**
  2815. * This class represents a faux web socket.
  2816. * @param {string} url
  2817. * @param {array or string} protocols
  2818. * @param {string} proxyHost
  2819. * @param {int} proxyPort
  2820. * @param {string} headers
  2821. */
  2822. WebSocket = function(url, protocols, proxyHost, proxyPort, headers) {
  2823. var self = this;
  2824. self.__id = WebSocket.__nextId++;
  2825. WebSocket.__instances[self.__id] = self;
  2826. self.readyState = WebSocket.CONNECTING;
  2827. self.bufferedAmount = 0;
  2828. self.__events = {};
  2829. if (!protocols) {
  2830. protocols = [];
  2831. } else if (typeof protocols == "string") {
  2832. protocols = [protocols];
  2833. }
  2834. // Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc.
  2835. // Otherwise, when onopen fires immediately, onopen is called before it is set.
  2836. setTimeout(function() {
  2837. WebSocket.__addTask(function() {
  2838. WebSocket.__flash.create(
  2839. self.__id, url, protocols, proxyHost || null, proxyPort || 0, headers || null);
  2840. });
  2841. }, 0);
  2842. };
  2843. /**
  2844. * Send data to the web socket.
  2845. * @param {string} data The data to send to the socket.
  2846. * @return {boolean} True for success, false for failure.
  2847. */
  2848. WebSocket.prototype.send = function(data) {
  2849. if (this.readyState == WebSocket.CONNECTING) {
  2850. throw "INVALID_STATE_ERR: Web Socket connection has not been established";
  2851. }
  2852. // We use encodeURIComponent() here, because FABridge doesn't work if
  2853. // the argument includes some characters. We don't use escape() here
  2854. // because of this:
  2855. // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#escape_and_unescape_Functions
  2856. // But it looks decodeURIComponent(encodeURIComponent(s)) doesn't
  2857. // preserve all Unicode characters either e.g. "\uffff" in Firefox.
  2858. // Note by wtritch: Hopefully this will not be necessary using ExternalInterface. Will require
  2859. // additional testing.
  2860. var result = WebSocket.__flash.send(this.__id, encodeURIComponent(data));
  2861. if (result < 0) { // success
  2862. return true;
  2863. } else {
  2864. this.bufferedAmount += result;
  2865. return false;
  2866. }
  2867. };
  2868. /**
  2869. * Close this web socket gracefully.
  2870. */
  2871. WebSocket.prototype.close = function() {
  2872. if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) {
  2873. return;
  2874. }
  2875. this.readyState = WebSocket.CLOSING;
  2876. WebSocket.__flash.close(this.__id);
  2877. };
  2878. /**
  2879. * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
  2880. *
  2881. * @param {string} type
  2882. * @param {function} listener
  2883. * @param {boolean} useCapture
  2884. * @return void
  2885. */
  2886. WebSocket.prototype.addEventListener = function(type, listener, useCapture) {
  2887. if (!(type in this.__events)) {
  2888. this.__events[type] = [];
  2889. }
  2890. this.__events[type].push(listener);
  2891. };
  2892. /**
  2893. * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
  2894. *
  2895. * @param {string} type
  2896. * @param {function} listener
  2897. * @param {boolean} useCapture
  2898. * @return void
  2899. */
  2900. WebSocket.prototype.removeEventListener = function(type, listener, useCapture) {
  2901. if (!(type in this.__events)) return;
  2902. var events = this.__events[type];
  2903. for (var i = events.length - 1; i >= 0; --i) {
  2904. if (events[i] === listener) {
  2905. events.splice(i, 1);
  2906. break;
  2907. }
  2908. }
  2909. };
  2910. /**
  2911. * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
  2912. *
  2913. * @param {Event} event
  2914. * @return void
  2915. */
  2916. WebSocket.prototype.dispatchEvent = function(event) {
  2917. var events = this.__events[event.type] || [];
  2918. for (var i = 0; i < events.length; ++i) {
  2919. events[i](event);
  2920. }
  2921. var handler = this["on" + event.type];
  2922. if (handler) handler(event);
  2923. };
  2924. /**
  2925. * Handles an event from Flash.
  2926. * @param {Object} flashEvent
  2927. */
  2928. WebSocket.prototype.__handleEvent = function(flashEvent) {
  2929. if ("readyState" in flashEvent) {
  2930. this.readyState = flashEvent.readyState;
  2931. }
  2932. if ("protocol" in flashEvent) {
  2933. this.protocol = flashEvent.protocol;
  2934. }
  2935. var jsEvent;
  2936. if (flashEvent.type == "open" || flashEvent.type == "error") {
  2937. jsEvent = this.__createSimpleEvent(flashEvent.type);
  2938. } else if (flashEvent.type == "close") {
  2939. // TODO implement jsEvent.wasClean
  2940. jsEvent = this.__createSimpleEvent("close");
  2941. } else if (flashEvent.type == "message") {
  2942. var data = decodeURIComponent(flashEvent.message);
  2943. jsEvent = this.__createMessageEvent("message", data);
  2944. } else {
  2945. throw "unknown event type: " + flashEvent.type;
  2946. }
  2947. this.dispatchEvent(jsEvent);
  2948. };
  2949. WebSocket.prototype.__createSimpleEvent = function(type) {
  2950. if (document.createEvent && window.Event) {
  2951. var event = document.createEvent("Event");
  2952. event.initEvent(type, false, false);
  2953. return event;
  2954. } else {
  2955. return {type: type, bubbles: false, cancelable: false};
  2956. }
  2957. };
  2958. WebSocket.prototype.__createMessageEvent = function(type, data) {
  2959. if (document.createEvent && window.MessageEvent && !window.opera) {
  2960. var event = document.createEvent("MessageEvent");
  2961. event.initMessageEvent("message", false, false, data, null, null, window, null);
  2962. return event;
  2963. } else {
  2964. // IE and Opera, the latter one truncates the data parameter after any 0x00 bytes.
  2965. return {type: type, data: data, bubbles: false, cancelable: false};
  2966. }
  2967. };
  2968. /**
  2969. * Define the WebSocket readyState enumeration.
  2970. */
  2971. WebSocket.CONNECTING = 0;
  2972. WebSocket.OPEN = 1;
  2973. WebSocket.CLOSING = 2;
  2974. WebSocket.CLOSED = 3;
  2975. WebSocket.__flash = null;
  2976. WebSocket.__instances = {};
  2977. WebSocket.__tasks = [];
  2978. WebSocket.__nextId = 0;
  2979. /**
  2980. * Load a new flash security policy file.
  2981. * @param {string} url
  2982. */
  2983. WebSocket.loadFlashPolicyFile = function(url){
  2984. WebSocket.__addTask(function() {
  2985. WebSocket.__flash.loadManualPolicyFile(url);
  2986. });
  2987. };
  2988. /**
  2989. * Loads WebSocketMain.swf and creates WebSocketMain object in Flash.
  2990. */
  2991. WebSocket.__initialize = function() {
  2992. if (WebSocket.__flash) return;
  2993. if (WebSocket.__swfLocation) {
  2994. // For backword compatibility.
  2995. window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
  2996. }
  2997. if (!window.WEB_SOCKET_SWF_LOCATION) {
  2998. console.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf");
  2999. return;
  3000. }
  3001. var container = document.createElement("div");
  3002. container.id = "webSocketContainer";
  3003. // Hides Flash box. We cannot use display: none or visibility: hidden because it prevents
  3004. // Flash from loading at least in IE. So we move it out of the screen at (-100, -100).
  3005. // But this even doesn't work with Flash Lite (e.g. in Droid Incredible). So with Flash
  3006. // Lite, we put it at (0, 0). This shows 1x1 box visible at left-top corner but this is
  3007. // the best we can do as far as we know now.
  3008. container.style.position = "absolute";
  3009. if (WebSocket.__isFlashLite()) {
  3010. container.style.left = "0px";
  3011. container.style.top = "0px";
  3012. } else {
  3013. container.style.left = "-100px";
  3014. container.style.top = "-100px";
  3015. }
  3016. var holder = document.createElement("div");
  3017. holder.id = "webSocketFlash";
  3018. container.appendChild(holder);
  3019. document.body.appendChild(container);
  3020. // See this article for hasPriority:
  3021. // http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html
  3022. swfobject.embedSWF(
  3023. WEB_SOCKET_SWF_LOCATION,
  3024. "webSocketFlash",
  3025. "1" /* width */,
  3026. "1" /* height */,
  3027. "10.0.0" /* SWF version */,
  3028. null,
  3029. null,
  3030. {hasPriority: true, swliveconnect : true, allowScriptAccess: "always"},
  3031. null,
  3032. function(e) {
  3033. if (!e.success) {
  3034. console.error("[WebSocket] swfobject.embedSWF failed");
  3035. }
  3036. });
  3037. };
  3038. /**
  3039. * Called by Flash to notify JS that it's fully loaded and ready
  3040. * for communication.
  3041. */
  3042. WebSocket.__onFlashInitialized = function() {
  3043. // We need to set a timeout here to avoid round-trip calls
  3044. // to flash during the initialization process.
  3045. setTimeout(function() {
  3046. WebSocket.__flash = document.getElementById("webSocketFlash");
  3047. WebSocket.__flash.setCallerUrl(location.href);
  3048. WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG);
  3049. for (var i = 0; i < WebSocket.__tasks.length; ++i) {
  3050. WebSocket.__tasks[i]();
  3051. }
  3052. WebSocket.__tasks = [];
  3053. }, 0);
  3054. };
  3055. /**
  3056. * Called by Flash to notify WebSockets events are fired.
  3057. */
  3058. WebSocket.__onFlashEvent = function() {
  3059. setTimeout(function() {
  3060. try {
  3061. // Gets events using receiveEvents() instead of getting it from event object
  3062. // of Flash event. This is to make sure to keep message order.
  3063. // It seems sometimes Flash events don't arrive in the same order as they are sent.
  3064. var events = WebSocket.__flash.receiveEvents();
  3065. for (var i = 0; i < events.length; ++i) {
  3066. WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]);
  3067. }
  3068. } catch (e) {
  3069. console.error(e);
  3070. }
  3071. }, 0);
  3072. return true;
  3073. };
  3074. // Called by Flash.
  3075. WebSocket.__log = function(message) {
  3076. console.log(decodeURIComponent(message));
  3077. };
  3078. // Called by Flash.
  3079. WebSocket.__error = function(message) {
  3080. console.error(decodeURIComponent(message));
  3081. };
  3082. WebSocket.__addTask = function(task) {
  3083. if (WebSocket.__flash) {
  3084. task();
  3085. } else {
  3086. WebSocket.__tasks.push(task);
  3087. }
  3088. };
  3089. /**
  3090. * Test if the browser is running flash lite.
  3091. * @return {boolean} True if flash lite is running, false otherwise.
  3092. */
  3093. WebSocket.__isFlashLite = function() {
  3094. if (!window.navigator || !window.navigator.mimeTypes) {
  3095. return false;
  3096. }
  3097. var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"];
  3098. if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) {
  3099. return false;
  3100. }
  3101. return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false;
  3102. };
  3103. if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) {
  3104. if (window.addEventListener) {
  3105. window.addEventListener("load", function(){
  3106. WebSocket.__initialize();
  3107. }, false);
  3108. } else {
  3109. window.attachEvent("onload", function(){
  3110. WebSocket.__initialize();
  3111. });
  3112. }
  3113. }
  3114. })();
  3115. /**
  3116. * socket.io
  3117. * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
  3118. * MIT Licensed
  3119. */
  3120. (function (exports, io, global) {
  3121. /**
  3122. * Expose constructor.
  3123. *
  3124. * @api public
  3125. */
  3126. exports.XHR = XHR;
  3127. /**
  3128. * XHR constructor
  3129. *
  3130. * @costructor
  3131. * @api public
  3132. */
  3133. function XHR (socket) {
  3134. if (!socket) return;
  3135. io.Transport.apply(this, arguments);
  3136. this.sendBuffer = [];
  3137. };
  3138. /**
  3139. * Inherits from Transport.
  3140. */
  3141. io.util.inherit(XHR, io.Transport);
  3142. /**
  3143. * Establish a connection
  3144. *
  3145. * @returns {Transport}
  3146. * @api public
  3147. */
  3148. XHR.prototype.open = function () {
  3149. this.socket.setBuffer(false);
  3150. this.onOpen();
  3151. this.get();
  3152. // we need to make sure the request succeeds since we have no indication
  3153. // whether the request opened or not until it succeeded.
  3154. this.setCloseTimeout();
  3155. return this;
  3156. };
  3157. /**
  3158. * Check if we need to send data to the Socket.IO server, if we have data in our
  3159. * buffer we encode it and forward it to the `post` method.
  3160. *
  3161. * @api private
  3162. */
  3163. XHR.prototype.payload = function (payload) {
  3164. var msgs = [];
  3165. for (var i = 0, l = payload.length; i < l; i++) {
  3166. msgs.push(io.parser.encodePacket(payload[i]));
  3167. }
  3168. this.send(io.parser.encodePayload(msgs));
  3169. };
  3170. /**
  3171. * Send data to the Socket.IO server.
  3172. *
  3173. * @param data The message
  3174. * @returns {Transport}
  3175. * @api public
  3176. */
  3177. XHR.prototype.send = function (data) {
  3178. this.post(data);
  3179. return this;
  3180. };
  3181. /**
  3182. * Posts a encoded message to the Socket.IO server.
  3183. *
  3184. * @param {String} data A encoded message.
  3185. * @api private
  3186. */
  3187. function empty () { };
  3188. XHR.prototype.post = function (data) {
  3189. var self = this;
  3190. this.socket.setBuffer(true);
  3191. function stateChange () {
  3192. if (this.readyState == 4) {
  3193. this.onreadystatechange = empty;
  3194. self.posting = false;
  3195. if (this.status == 200){
  3196. self.socket.setBuffer(false);
  3197. } else {
  3198. self.onClose();
  3199. }
  3200. }
  3201. }
  3202. function onload () {
  3203. this.onload = empty;
  3204. self.socket.setBuffer(false);
  3205. };
  3206. this.sendXHR = this.request('POST');
  3207. if (global.XDomainRequest && this.sendXHR instanceof XDomainRequest) {
  3208. this.sendXHR.onload = this.sendXHR.onerror = onload;
  3209. } else {
  3210. this.sendXHR.onreadystatechange = stateChange;
  3211. }
  3212. this.sendXHR.send(data);
  3213. };
  3214. /**
  3215. * Disconnects the established `XHR` connection.
  3216. *
  3217. * @returns {Transport}
  3218. * @api public
  3219. */
  3220. XHR.prototype.close = function () {
  3221. this.onClose();
  3222. return this;
  3223. };
  3224. /**
  3225. * Generates a configured XHR request
  3226. *
  3227. * @param {String} url The url that needs to be requested.
  3228. * @param {String} method The method the request should use.
  3229. * @returns {XMLHttpRequest}
  3230. * @api private
  3231. */
  3232. XHR.prototype.request = function (method) {
  3233. var req = io.util.request(this.socket.isXDomain())
  3234. , query = io.util.query(this.socket.options.query, 't=' + +new Date);
  3235. req.open(method || 'GET', this.prepareUrl() + query, true);
  3236. if (method == 'POST') {
  3237. try {
  3238. if (req.setRequestHeader) {
  3239. req.setRequestHeader('Content-type', 'text/plain;charset=UTF-8');
  3240. } else {
  3241. // XDomainRequest
  3242. req.contentType = 'text/plain';
  3243. }
  3244. } catch (e) {}
  3245. }
  3246. return req;
  3247. };
  3248. /**
  3249. * Returns the scheme to use for the transport URLs.
  3250. *
  3251. * @api private
  3252. */
  3253. XHR.prototype.scheme = function () {
  3254. return this.socket.options.secure ? 'https' : 'http';
  3255. };
  3256. /**
  3257. * Check if the XHR transports are supported
  3258. *
  3259. * @param {Boolean} xdomain Check if we support cross domain requests.
  3260. * @returns {Boolean}
  3261. * @api public
  3262. */
  3263. XHR.check = function (socket, xdomain) {
  3264. try {
  3265. var request = io.util.request(xdomain),
  3266. usesXDomReq = (global.XDomainRequest && request instanceof XDomainRequest),
  3267. socketProtocol = (socket && socket.options && socket.options.secure ? 'https:' : 'http:'),
  3268. isXProtocol = (global.location && socketProtocol != global.location.protocol);
  3269. if (request && !(usesXDomReq && isXProtocol)) {
  3270. return true;
  3271. }
  3272. } catch(e) {}
  3273. return false;
  3274. };
  3275. /**
  3276. * Check if the XHR transport supports cross domain requests.
  3277. *
  3278. * @returns {Boolean}
  3279. * @api public
  3280. */
  3281. XHR.xdomainCheck = function (socket) {
  3282. return XHR.check(socket, true);
  3283. };
  3284. })(
  3285. 'undefined' != typeof io ? io.Transport : module.exports
  3286. , 'undefined' != typeof io ? io : module.parent.exports
  3287. , this
  3288. );
  3289. /**
  3290. * socket.io
  3291. * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
  3292. * MIT Licensed
  3293. */
  3294. (function (exports, io) {
  3295. /**
  3296. * Expose constructor.
  3297. */
  3298. exports.htmlfile = HTMLFile;
  3299. /**
  3300. * The HTMLFile transport creates a `forever iframe` based transport
  3301. * for Internet Explorer. Regular forever iframe implementations will
  3302. * continuously trigger the browsers buzy indicators. If the forever iframe
  3303. * is created inside a `htmlfile` these indicators will not be trigged.
  3304. *
  3305. * @constructor
  3306. * @extends {io.Transport.XHR}
  3307. * @api public
  3308. */
  3309. function HTMLFile (socket) {
  3310. io.Transport.XHR.apply(this, arguments);
  3311. };
  3312. /**
  3313. * Inherits from XHR transport.
  3314. */
  3315. io.util.inherit(HTMLFile, io.Transport.XHR);
  3316. /**
  3317. * Transport name
  3318. *
  3319. * @api public
  3320. */
  3321. HTMLFile.prototype.name = 'htmlfile';
  3322. /**
  3323. * Creates a new Ac...eX `htmlfile` with a forever loading iframe
  3324. * that can be used to listen to messages. Inside the generated
  3325. * `htmlfile` a reference will be made to the HTMLFile transport.
  3326. *
  3327. * @api private
  3328. */
  3329. HTMLFile.prototype.get = function () {
  3330. this.doc = new window[(['Active'].concat('Object').join('X'))]('htmlfile');
  3331. this.doc.open();
  3332. this.doc.write('<html></html>');
  3333. this.doc.close();
  3334. this.doc.parentWindow.s = this;
  3335. var iframeC = this.doc.createElement('div');
  3336. iframeC.className = 'socketio';
  3337. this.doc.body.appendChild(iframeC);
  3338. this.iframe = this.doc.createElement('iframe');
  3339. iframeC.appendChild(this.iframe);
  3340. var self = this
  3341. , query = io.util.query(this.socket.options.query, 't='+ +new Date);
  3342. this.iframe.src = this.prepareUrl() + query;
  3343. io.util.on(window, 'unload', function () {
  3344. self.destroy();
  3345. });
  3346. };
  3347. /**
  3348. * The Socket.IO server will write script tags inside the forever
  3349. * iframe, this function will be used as callback for the incoming
  3350. * information.
  3351. *
  3352. * @param {String} data The message
  3353. * @param {document} doc Reference to the context
  3354. * @api private
  3355. */
  3356. HTMLFile.prototype._ = function (data, doc) {
  3357. // unescape all forward slashes. see GH-1251
  3358. data = data.replace(/\\\//g, '/');
  3359. this.onData(data);
  3360. try {
  3361. var script = doc.getElementsByTagName('script')[0];
  3362. script.parentNode.removeChild(script);
  3363. } catch (e) { }
  3364. };
  3365. /**
  3366. * Destroy the established connection, iframe and `htmlfile`.
  3367. * And calls the `CollectGarbage` function of Internet Explorer
  3368. * to release the memory.
  3369. *
  3370. * @api private
  3371. */
  3372. HTMLFile.prototype.destroy = function () {
  3373. if (this.iframe){
  3374. try {
  3375. this.iframe.src = 'about:blank';
  3376. } catch(e){}
  3377. this.doc = null;
  3378. this.iframe.parentNode.removeChild(this.iframe);
  3379. this.iframe = null;
  3380. CollectGarbage();
  3381. }
  3382. };
  3383. /**
  3384. * Disconnects the established connection.
  3385. *
  3386. * @returns {Transport} Chaining.
  3387. * @api public
  3388. */
  3389. HTMLFile.prototype.close = function () {
  3390. this.destroy();
  3391. return io.Transport.XHR.prototype.close.call(this);
  3392. };
  3393. /**
  3394. * Checks if the browser supports this transport. The browser
  3395. * must have an `Ac...eXObject` implementation.
  3396. *
  3397. * @return {Boolean}
  3398. * @api public
  3399. */
  3400. HTMLFile.check = function (socket) {
  3401. if (typeof window != "undefined" && (['Active'].concat('Object').join('X')) in window){
  3402. try {
  3403. var a = new window[(['Active'].concat('Object').join('X'))]('htmlfile');
  3404. return a && io.Transport.XHR.check(socket);
  3405. } catch(e){}
  3406. }
  3407. return false;
  3408. };
  3409. /**
  3410. * Check if cross domain requests are supported.
  3411. *
  3412. * @returns {Boolean}
  3413. * @api public
  3414. */
  3415. HTMLFile.xdomainCheck = function () {
  3416. // we can probably do handling for sub-domains, we should
  3417. // test that it's cross domain but a subdomain here
  3418. return false;
  3419. };
  3420. /**
  3421. * Add the transport to your public io.transports array.
  3422. *
  3423. * @api private
  3424. */
  3425. io.transports.push('htmlfile');
  3426. })(
  3427. 'undefined' != typeof io ? io.Transport : module.exports
  3428. , 'undefined' != typeof io ? io : module.parent.exports
  3429. );
  3430. /**
  3431. * socket.io
  3432. * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
  3433. * MIT Licensed
  3434. */
  3435. (function (exports, io, global) {
  3436. /**
  3437. * Expose constructor.
  3438. */
  3439. exports['xhr-polling'] = XHRPolling;
  3440. /**
  3441. * The XHR-polling transport uses long polling XHR requests to create a
  3442. * "persistent" connection with the server.
  3443. *
  3444. * @constructor
  3445. * @api public
  3446. */
  3447. function XHRPolling () {
  3448. io.Transport.XHR.apply(this, arguments);
  3449. };
  3450. /**
  3451. * Inherits from XHR transport.
  3452. */
  3453. io.util.inherit(XHRPolling, io.Transport.XHR);
  3454. /**
  3455. * Merge the properties from XHR transport
  3456. */
  3457. io.util.merge(XHRPolling, io.Transport.XHR);
  3458. /**
  3459. * Transport name
  3460. *
  3461. * @api public
  3462. */
  3463. XHRPolling.prototype.name = 'xhr-polling';
  3464. /**
  3465. * Indicates whether heartbeats is enabled for this transport
  3466. *
  3467. * @api private
  3468. */
  3469. XHRPolling.prototype.heartbeats = function () {
  3470. return false;
  3471. };
  3472. /**
  3473. * Establish a connection, for iPhone and Android this will be done once the page
  3474. * is loaded.
  3475. *
  3476. * @returns {Transport} Chaining.
  3477. * @api public
  3478. */
  3479. XHRPolling.prototype.open = function () {
  3480. var self = this;
  3481. io.Transport.XHR.prototype.open.call(self);
  3482. return false;
  3483. };
  3484. /**
  3485. * Starts a XHR request to wait for incoming messages.
  3486. *
  3487. * @api private
  3488. */
  3489. function empty () {};
  3490. XHRPolling.prototype.get = function () {
  3491. if (!this.isOpen) return;
  3492. var self = this;
  3493. function stateChange () {
  3494. if (this.readyState == 4) {
  3495. this.onreadystatechange = empty;
  3496. if (this.status == 200) {
  3497. self.onData(this.responseText);
  3498. self.get();
  3499. } else {
  3500. self.onClose();
  3501. }
  3502. }
  3503. };
  3504. function onload () {
  3505. this.onload = empty;
  3506. this.onerror = empty;
  3507. self.retryCounter = 1;
  3508. self.onData(this.responseText);
  3509. self.get();
  3510. };
  3511. function onerror () {
  3512. self.retryCounter ++;
  3513. if(!self.retryCounter || self.retryCounter > 3) {
  3514. self.onClose();
  3515. } else {
  3516. self.get();
  3517. }
  3518. };
  3519. this.xhr = this.request();
  3520. if (global.XDomainRequest && this.xhr instanceof XDomainRequest) {
  3521. this.xhr.onload = onload;
  3522. this.xhr.onerror = onerror;
  3523. } else {
  3524. this.xhr.onreadystatechange = stateChange;
  3525. }
  3526. this.xhr.send(null);
  3527. };
  3528. /**
  3529. * Handle the unclean close behavior.
  3530. *
  3531. * @api private
  3532. */
  3533. XHRPolling.prototype.onClose = function () {
  3534. io.Transport.XHR.prototype.onClose.call(this);
  3535. if (this.xhr) {
  3536. this.xhr.onreadystatechange = this.xhr.onload = this.xhr.onerror = empty;
  3537. try {
  3538. this.xhr.abort();
  3539. } catch(e){}
  3540. this.xhr = null;
  3541. }
  3542. };
  3543. /**
  3544. * Webkit based browsers show a infinit spinner when you start a XHR request
  3545. * before the browsers onload event is called so we need to defer opening of
  3546. * the transport until the onload event is called. Wrapping the cb in our
  3547. * defer method solve this.
  3548. *
  3549. * @param {Socket} socket The socket instance that needs a transport
  3550. * @param {Function} fn The callback
  3551. * @api private
  3552. */
  3553. XHRPolling.prototype.ready = function (socket, fn) {
  3554. var self = this;
  3555. io.util.defer(function () {
  3556. fn.call(self);
  3557. });
  3558. };
  3559. /**
  3560. * Add the transport to your public io.transports array.
  3561. *
  3562. * @api private
  3563. */
  3564. io.transports.push('xhr-polling');
  3565. })(
  3566. 'undefined' != typeof io ? io.Transport : module.exports
  3567. , 'undefined' != typeof io ? io : module.parent.exports
  3568. , this
  3569. );
  3570. /**
  3571. * socket.io
  3572. * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
  3573. * MIT Licensed
  3574. */
  3575. (function (exports, io, global) {
  3576. /**
  3577. * There is a way to hide the loading indicator in Firefox. If you create and
  3578. * remove a iframe it will stop showing the current loading indicator.
  3579. * Unfortunately we can't feature detect that and UA sniffing is evil.
  3580. *
  3581. * @api private
  3582. */
  3583. var indicator = global.document && "MozAppearance" in
  3584. global.document.documentElement.style;
  3585. /**
  3586. * Expose constructor.
  3587. */
  3588. exports['jsonp-polling'] = JSONPPolling;
  3589. /**
  3590. * The JSONP transport creates an persistent connection by dynamically
  3591. * inserting a script tag in the page. This script tag will receive the
  3592. * information of the Socket.IO server. When new information is received
  3593. * it creates a new script tag for the new data stream.
  3594. *
  3595. * @constructor
  3596. * @extends {io.Transport.xhr-polling}
  3597. * @api public
  3598. */
  3599. function JSONPPolling (socket) {
  3600. io.Transport['xhr-polling'].apply(this, arguments);
  3601. this.index = io.j.length;
  3602. var self = this;
  3603. io.j.push(function (msg) {
  3604. self._(msg);
  3605. });
  3606. };
  3607. /**
  3608. * Inherits from XHR polling transport.
  3609. */
  3610. io.util.inherit(JSONPPolling, io.Transport['xhr-polling']);
  3611. /**
  3612. * Transport name
  3613. *
  3614. * @api public
  3615. */
  3616. JSONPPolling.prototype.name = 'jsonp-polling';
  3617. /**
  3618. * Posts a encoded message to the Socket.IO server using an iframe.
  3619. * The iframe is used because script tags can create POST based requests.
  3620. * The iframe is positioned outside of the view so the user does not
  3621. * notice it's existence.
  3622. *
  3623. * @param {String} data A encoded message.
  3624. * @api private
  3625. */
  3626. JSONPPolling.prototype.post = function (data) {
  3627. var self = this
  3628. , query = io.util.query(
  3629. this.socket.options.query
  3630. , 't='+ (+new Date) + '&i=' + this.index
  3631. );
  3632. if (!this.form) {
  3633. var form = document.createElement('form')
  3634. , area = document.createElement('textarea')
  3635. , id = this.iframeId = 'socketio_iframe_' + this.index
  3636. , iframe;
  3637. form.className = 'socketio';
  3638. form.style.position = 'absolute';
  3639. form.style.top = '0px';
  3640. form.style.left = '0px';
  3641. form.style.display = 'none';
  3642. form.target = id;
  3643. form.method = 'POST';
  3644. form.setAttribute('accept-charset', 'utf-8');
  3645. area.name = 'd';
  3646. form.appendChild(area);
  3647. document.body.appendChild(form);
  3648. this.form = form;
  3649. this.area = area;
  3650. }
  3651. this.form.action = this.prepareUrl() + query;
  3652. function complete () {
  3653. initIframe();
  3654. self.socket.setBuffer(false);
  3655. };
  3656. function initIframe () {
  3657. if (self.iframe) {
  3658. self.form.removeChild(self.iframe);
  3659. }
  3660. try {
  3661. // ie6 dynamic iframes with target="" support (thanks Chris Lambacher)
  3662. iframe = document.createElement('<iframe name="'+ self.iframeId +'">');
  3663. } catch (e) {
  3664. iframe = document.createElement('iframe');
  3665. iframe.name = self.iframeId;
  3666. }
  3667. iframe.id = self.iframeId;
  3668. self.form.appendChild(iframe);
  3669. self.iframe = iframe;
  3670. };
  3671. initIframe();
  3672. // we temporarily stringify until we figure out how to prevent
  3673. // browsers from turning `\n` into `\r\n` in form inputs
  3674. this.area.value = io.JSON.stringify(data);
  3675. try {
  3676. this.form.submit();
  3677. } catch(e) {}
  3678. if (this.iframe.attachEvent) {
  3679. iframe.onreadystatechange = function () {
  3680. if (self.iframe.readyState == 'complete') {
  3681. complete();
  3682. }
  3683. };
  3684. } else {
  3685. this.iframe.onload = complete;
  3686. }
  3687. this.socket.setBuffer(true);
  3688. };
  3689. /**
  3690. * Creates a new JSONP poll that can be used to listen
  3691. * for messages from the Socket.IO server.
  3692. *
  3693. * @api private
  3694. */
  3695. JSONPPolling.prototype.get = function () {
  3696. var self = this
  3697. , script = document.createElement('script')
  3698. , query = io.util.query(
  3699. this.socket.options.query
  3700. , 't='+ (+new Date) + '&i=' + this.index
  3701. );
  3702. if (this.script) {
  3703. this.script.parentNode.removeChild(this.script);
  3704. this.script = null;
  3705. }
  3706. script.async = true;
  3707. script.src = this.prepareUrl() + query;
  3708. script.onerror = function () {
  3709. self.onClose();
  3710. };
  3711. var insertAt = document.getElementsByTagName('script')[0];
  3712. insertAt.parentNode.insertBefore(script, insertAt);
  3713. this.script = script;
  3714. if (indicator) {
  3715. setTimeout(function () {
  3716. var iframe = document.createElement('iframe');
  3717. document.body.appendChild(iframe);
  3718. document.body.removeChild(iframe);
  3719. }, 100);
  3720. }
  3721. };
  3722. /**
  3723. * Callback function for the incoming message stream from the Socket.IO server.
  3724. *
  3725. * @param {String} data The message
  3726. * @api private
  3727. */
  3728. JSONPPolling.prototype._ = function (msg) {
  3729. this.onData(msg);
  3730. if (this.isOpen) {
  3731. this.get();
  3732. }
  3733. return this;
  3734. };
  3735. /**
  3736. * The indicator hack only works after onload
  3737. *
  3738. * @param {Socket} socket The socket instance that needs a transport
  3739. * @param {Function} fn The callback
  3740. * @api private
  3741. */
  3742. JSONPPolling.prototype.ready = function (socket, fn) {
  3743. var self = this;
  3744. if (!indicator) return fn.call(this);
  3745. io.util.load(function () {
  3746. fn.call(self);
  3747. });
  3748. };
  3749. /**
  3750. * Checks if browser supports this transport.
  3751. *
  3752. * @return {Boolean}
  3753. * @api public
  3754. */
  3755. JSONPPolling.check = function () {
  3756. return 'document' in global;
  3757. };
  3758. /**
  3759. * Check if cross domain requests are supported
  3760. *
  3761. * @returns {Boolean}
  3762. * @api public
  3763. */
  3764. JSONPPolling.xdomainCheck = function () {
  3765. return true;
  3766. };
  3767. /**
  3768. * Add the transport to your public io.transports array.
  3769. *
  3770. * @api private
  3771. */
  3772. io.transports.push('jsonp-polling');
  3773. })(
  3774. 'undefined' != typeof io ? io.Transport : module.exports
  3775. , 'undefined' != typeof io ? io : module.parent.exports
  3776. , this
  3777. );
  3778. if (typeof define === "function" && define.amd) {
  3779. define([], function () { return io; });
  3780. }
  3781. })();
  3782. },{}],9:[function(require,module,exports){
  3783. var events = require('events');
  3784. exports.isArray = isArray;
  3785. exports.isDate = function(obj){return Object.prototype.toString.call(obj) === '[object Date]'};
  3786. exports.isRegExp = function(obj){return Object.prototype.toString.call(obj) === '[object RegExp]'};
  3787. exports.print = function () {};
  3788. exports.puts = function () {};
  3789. exports.debug = function() {};
  3790. exports.inspect = function(obj, showHidden, depth, colors) {
  3791. var seen = [];
  3792. var stylize = function(str, styleType) {
  3793. // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
  3794. var styles =
  3795. { 'bold' : [1, 22],
  3796. 'italic' : [3, 23],
  3797. 'underline' : [4, 24],
  3798. 'inverse' : [7, 27],
  3799. 'white' : [37, 39],
  3800. 'grey' : [90, 39],
  3801. 'black' : [30, 39],
  3802. 'blue' : [34, 39],
  3803. 'cyan' : [36, 39],
  3804. 'green' : [32, 39],
  3805. 'magenta' : [35, 39],
  3806. 'red' : [31, 39],
  3807. 'yellow' : [33, 39] };
  3808. var style =
  3809. { 'special': 'cyan',
  3810. 'number': 'blue',
  3811. 'boolean': 'yellow',
  3812. 'undefined': 'grey',
  3813. 'null': 'bold',
  3814. 'string': 'green',
  3815. 'date': 'magenta',
  3816. // "name": intentionally not styling
  3817. 'regexp': 'red' }[styleType];
  3818. if (style) {
  3819. return '\u001b[' + styles[style][0] + 'm' + str +
  3820. '\u001b[' + styles[style][1] + 'm';
  3821. } else {
  3822. return str;
  3823. }
  3824. };
  3825. if (! colors) {
  3826. stylize = function(str, styleType) { return str; };
  3827. }
  3828. function format(value, recurseTimes) {
  3829. // Provide a hook for user-specified inspect functions.
  3830. // Check that value is an object with an inspect function on it
  3831. if (value && typeof value.inspect === 'function' &&
  3832. // Filter out the util module, it's inspect function is special
  3833. value !== exports &&
  3834. // Also filter out any prototype objects using the circular check.
  3835. !(value.constructor && value.constructor.prototype === value)) {
  3836. return value.inspect(recurseTimes);
  3837. }
  3838. // Primitive types cannot have properties
  3839. switch (typeof value) {
  3840. case 'undefined':
  3841. return stylize('undefined', 'undefined');
  3842. case 'string':
  3843. var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
  3844. .replace(/'/g, "\\'")
  3845. .replace(/\\"/g, '"') + '\'';
  3846. return stylize(simple, 'string');
  3847. case 'number':
  3848. return stylize('' + value, 'number');
  3849. case 'boolean':
  3850. return stylize('' + value, 'boolean');
  3851. }
  3852. // For some reason typeof null is "object", so special case here.
  3853. if (value === null) {
  3854. return stylize('null', 'null');
  3855. }
  3856. // Look up the keys of the object.
  3857. var visible_keys = Object_keys(value);
  3858. var keys = showHidden ? Object_getOwnPropertyNames(value) : visible_keys;
  3859. // Functions without properties can be shortcutted.
  3860. if (typeof value === 'function' && keys.length === 0) {
  3861. if (isRegExp(value)) {
  3862. return stylize('' + value, 'regexp');
  3863. } else {
  3864. var name = value.name ? ': ' + value.name : '';
  3865. return stylize('[Function' + name + ']', 'special');
  3866. }
  3867. }
  3868. // Dates without properties can be shortcutted
  3869. if (isDate(value) && keys.length === 0) {
  3870. return stylize(value.toUTCString(), 'date');
  3871. }
  3872. var base, type, braces;
  3873. // Determine the object type
  3874. if (isArray(value)) {
  3875. type = 'Array';
  3876. braces = ['[', ']'];
  3877. } else {
  3878. type = 'Object';
  3879. braces = ['{', '}'];
  3880. }
  3881. // Make functions say that they are functions
  3882. if (typeof value === 'function') {
  3883. var n = value.name ? ': ' + value.name : '';
  3884. base = (isRegExp(value)) ? ' ' + value : ' [Function' + n + ']';
  3885. } else {
  3886. base = '';
  3887. }
  3888. // Make dates with properties first say the date
  3889. if (isDate(value)) {
  3890. base = ' ' + value.toUTCString();
  3891. }
  3892. if (keys.length === 0) {
  3893. return braces[0] + base + braces[1];
  3894. }
  3895. if (recurseTimes < 0) {
  3896. if (isRegExp(value)) {
  3897. return stylize('' + value, 'regexp');
  3898. } else {
  3899. return stylize('[Object]', 'special');
  3900. }
  3901. }
  3902. seen.push(value);
  3903. var output = keys.map(function(key) {
  3904. var name, str;
  3905. if (value.__lookupGetter__) {
  3906. if (value.__lookupGetter__(key)) {
  3907. if (value.__lookupSetter__(key)) {
  3908. str = stylize('[Getter/Setter]', 'special');
  3909. } else {
  3910. str = stylize('[Getter]', 'special');
  3911. }
  3912. } else {
  3913. if (value.__lookupSetter__(key)) {
  3914. str = stylize('[Setter]', 'special');
  3915. }
  3916. }
  3917. }
  3918. if (visible_keys.indexOf(key) < 0) {
  3919. name = '[' + key + ']';
  3920. }
  3921. if (!str) {
  3922. if (seen.indexOf(value[key]) < 0) {
  3923. if (recurseTimes === null) {
  3924. str = format(value[key]);
  3925. } else {
  3926. str = format(value[key], recurseTimes - 1);
  3927. }
  3928. if (str.indexOf('\n') > -1) {
  3929. if (isArray(value)) {
  3930. str = str.split('\n').map(function(line) {
  3931. return ' ' + line;
  3932. }).join('\n').substr(2);
  3933. } else {
  3934. str = '\n' + str.split('\n').map(function(line) {
  3935. return ' ' + line;
  3936. }).join('\n');
  3937. }
  3938. }
  3939. } else {
  3940. str = stylize('[Circular]', 'special');
  3941. }
  3942. }
  3943. if (typeof name === 'undefined') {
  3944. if (type === 'Array' && key.match(/^\d+$/)) {
  3945. return str;
  3946. }
  3947. name = JSON.stringify('' + key);
  3948. if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
  3949. name = name.substr(1, name.length - 2);
  3950. name = stylize(name, 'name');
  3951. } else {
  3952. name = name.replace(/'/g, "\\'")
  3953. .replace(/\\"/g, '"')
  3954. .replace(/(^"|"$)/g, "'");
  3955. name = stylize(name, 'string');
  3956. }
  3957. }
  3958. return name + ': ' + str;
  3959. });
  3960. seen.pop();
  3961. var numLinesEst = 0;
  3962. var length = output.reduce(function(prev, cur) {
  3963. numLinesEst++;
  3964. if (cur.indexOf('\n') >= 0) numLinesEst++;
  3965. return prev + cur.length + 1;
  3966. }, 0);
  3967. if (length > 50) {
  3968. output = braces[0] +
  3969. (base === '' ? '' : base + '\n ') +
  3970. ' ' +
  3971. output.join(',\n ') +
  3972. ' ' +
  3973. braces[1];
  3974. } else {
  3975. output = braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
  3976. }
  3977. return output;
  3978. }
  3979. return format(obj, (typeof depth === 'undefined' ? 2 : depth));
  3980. };
  3981. function isArray(ar) {
  3982. return Array.isArray(ar) ||
  3983. (typeof ar === 'object' && Object.prototype.toString.call(ar) === '[object Array]');
  3984. }
  3985. function isRegExp(re) {
  3986. typeof re === 'object' && Object.prototype.toString.call(re) === '[object RegExp]';
  3987. }
  3988. function isDate(d) {
  3989. return typeof d === 'object' && Object.prototype.toString.call(d) === '[object Date]';
  3990. }
  3991. function pad(n) {
  3992. return n < 10 ? '0' + n.toString(10) : n.toString(10);
  3993. }
  3994. var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
  3995. 'Oct', 'Nov', 'Dec'];
  3996. // 26 Feb 16:19:34
  3997. function timestamp() {
  3998. var d = new Date();
  3999. var time = [pad(d.getHours()),
  4000. pad(d.getMinutes()),
  4001. pad(d.getSeconds())].join(':');
  4002. return [d.getDate(), months[d.getMonth()], time].join(' ');
  4003. }
  4004. exports.log = function (msg) {};
  4005. exports.pump = null;
  4006. var Object_keys = Object.keys || function (obj) {
  4007. var res = [];
  4008. for (var key in obj) res.push(key);
  4009. return res;
  4010. };
  4011. var Object_getOwnPropertyNames = Object.getOwnPropertyNames || function (obj) {
  4012. var res = [];
  4013. for (var key in obj) {
  4014. if (Object.hasOwnProperty.call(obj, key)) res.push(key);
  4015. }
  4016. return res;
  4017. };
  4018. var Object_create = Object.create || function (prototype, properties) {
  4019. // from es5-shim
  4020. var object;
  4021. if (prototype === null) {
  4022. object = { '__proto__' : null };
  4023. }
  4024. else {
  4025. if (typeof prototype !== 'object') {
  4026. throw new TypeError(
  4027. 'typeof prototype[' + (typeof prototype) + '] != \'object\''
  4028. );
  4029. }
  4030. var Type = function () {};
  4031. Type.prototype = prototype;
  4032. object = new Type();
  4033. object.__proto__ = prototype;
  4034. }
  4035. if (typeof properties !== 'undefined' && Object.defineProperties) {
  4036. Object.defineProperties(object, properties);
  4037. }
  4038. return object;
  4039. };
  4040. exports.inherits = function(ctor, superCtor) {
  4041. ctor.super_ = superCtor;
  4042. ctor.prototype = Object_create(superCtor.prototype, {
  4043. constructor: {
  4044. value: ctor,
  4045. enumerable: false,
  4046. writable: true,
  4047. configurable: true
  4048. }
  4049. });
  4050. };
  4051. var formatRegExp = /%[sdj%]/g;
  4052. exports.format = function(f) {
  4053. if (typeof f !== 'string') {
  4054. var objects = [];
  4055. for (var i = 0; i < arguments.length; i++) {
  4056. objects.push(exports.inspect(arguments[i]));
  4057. }
  4058. return objects.join(' ');
  4059. }
  4060. var i = 1;
  4061. var args = arguments;
  4062. var len = args.length;
  4063. var str = String(f).replace(formatRegExp, function(x) {
  4064. if (x === '%%') return '%';
  4065. if (i >= len) return x;
  4066. switch (x) {
  4067. case '%s': return String(args[i++]);
  4068. case '%d': return Number(args[i++]);
  4069. case '%j': return JSON.stringify(args[i++]);
  4070. default:
  4071. return x;
  4072. }
  4073. });
  4074. for(var x = args[i]; i < len; x = args[++i]){
  4075. if (x === null || typeof x !== 'object') {
  4076. str += ' ' + x;
  4077. } else {
  4078. str += ' ' + exports.inspect(x);
  4079. }
  4080. }
  4081. return str;
  4082. };
  4083. },{"events":10}],4:[function(require,module,exports){
  4084. var util = require('util');
  4085. var webrtc = require('webrtcsupport');
  4086. var WildEmitter = require('wildemitter');
  4087. var mockconsole = require('mockconsole');
  4088. var localMedia = require('localmedia');
  4089. var Peer = require('./peer');
  4090. function WebRTC(opts) {
  4091. var self = this;
  4092. var options = opts || {};
  4093. var config = this.config = {
  4094. debug: false,
  4095. // makes the entire PC config overridable
  4096. peerConnectionConfig: {
  4097. iceServers: [{"url": "stun:stun.l.google.com:19302"}]
  4098. },
  4099. peerConnectionConstraints: {
  4100. optional: [
  4101. {DtlsSrtpKeyAgreement: true}
  4102. ]
  4103. },
  4104. receiveMedia: {
  4105. mandatory: {
  4106. OfferToReceiveAudio: true,
  4107. OfferToReceiveVideo: true
  4108. }
  4109. },
  4110. enableDataChannels: true
  4111. };
  4112. var item;
  4113. // expose screensharing check
  4114. this.screenSharingSupport = webrtc.screenSharing;
  4115. // We also allow a 'logger' option. It can be any object that implements
  4116. // log, warn, and error methods.
  4117. // We log nothing by default, following "the rule of silence":
  4118. // http://www.linfo.org/rule_of_silence.html
  4119. this.logger = function () {
  4120. // we assume that if you're in debug mode and you didn't
  4121. // pass in a logger, you actually want to log as much as
  4122. // possible.
  4123. if (opts.debug) {
  4124. return opts.logger || console;
  4125. } else {
  4126. // or we'll use your logger which should have its own logic
  4127. // for output. Or we'll return the no-op.
  4128. return opts.logger || mockconsole;
  4129. }
  4130. }();
  4131. // set options
  4132. for (item in options) {
  4133. this.config[item] = options[item];
  4134. }
  4135. // check for support
  4136. if (!webrtc.support) {
  4137. this.logger.error('Your browser doesn\'t seem to support WebRTC');
  4138. }
  4139. // where we'll store our peer connections
  4140. this.peers = [];
  4141. // call localMedia constructor
  4142. localMedia.call(this, this.config);
  4143. this.on('speaking', function () {
  4144. if (!self.hardMuted) {
  4145. // FIXME: should use sendDirectlyToAll, but currently has different semantics wrt payload
  4146. self.peers.forEach(function (peer) {
  4147. if (peer.enableDataChannels) {
  4148. var dc = peer.getDataChannel('hark');
  4149. if (dc.readyState != 'open') return;
  4150. dc.send(JSON.stringify({type: 'speaking'}));
  4151. }
  4152. });
  4153. }
  4154. });
  4155. this.on('stoppedSpeaking', function () {
  4156. if (!self.hardMuted) {
  4157. // FIXME: should use sendDirectlyToAll, but currently has different semantics wrt payload
  4158. self.peers.forEach(function (peer) {
  4159. if (peer.enableDataChannels) {
  4160. var dc = peer.getDataChannel('hark');
  4161. if (dc.readyState != 'open') return;
  4162. dc.send(JSON.stringify({type: 'stoppedSpeaking'}));
  4163. }
  4164. });
  4165. }
  4166. });
  4167. this.on('volumeChange', function (volume, treshold) {
  4168. if (!self.hardMuted) {
  4169. // FIXME: should use sendDirectlyToAll, but currently has different semantics wrt payload
  4170. self.peers.forEach(function (peer) {
  4171. if (peer.enableDataChannels) {
  4172. var dc = peer.getDataChannel('hark');
  4173. if (dc.readyState != 'open') return;
  4174. dc.send(JSON.stringify({type: 'volume', volume: volume }));
  4175. }
  4176. });
  4177. }
  4178. });
  4179. // log events in debug mode
  4180. if (this.config.debug) {
  4181. this.on('*', function (event, val1, val2) {
  4182. var logger;
  4183. // if you didn't pass in a logger and you explicitly turning on debug
  4184. // we're just going to assume you're wanting log output with console
  4185. if (self.config.logger === mockconsole) {
  4186. logger = console;
  4187. } else {
  4188. logger = self.logger;
  4189. }
  4190. logger.log('event:', event, val1, val2);
  4191. });
  4192. }
  4193. }
  4194. util.inherits(WebRTC, localMedia);
  4195. WebRTC.prototype.createPeer = function (opts) {
  4196. var peer;
  4197. opts.parent = this;
  4198. peer = new Peer(opts);
  4199. this.peers.push(peer);
  4200. return peer;
  4201. };
  4202. // removes peers
  4203. WebRTC.prototype.removePeers = function (id, type) {
  4204. this.getPeers(id, type).forEach(function (peer) {
  4205. peer.end();
  4206. });
  4207. };
  4208. // fetches all Peer objects by session id and/or type
  4209. WebRTC.prototype.getPeers = function (sessionId, type) {
  4210. return this.peers.filter(function (peer) {
  4211. return (!sessionId || peer.id === sessionId) && (!type || peer.type === type);
  4212. });
  4213. };
  4214. // sends message to all
  4215. WebRTC.prototype.sendToAll = function (message, payload) {
  4216. this.peers.forEach(function (peer) {
  4217. peer.send(message, payload);
  4218. });
  4219. };
  4220. // sends message to all using a datachannel
  4221. // only sends to anyone who has an open datachannel
  4222. WebRTC.prototype.sendDirectlyToAll = function (channel, message, payload) {
  4223. this.peers.forEach(function (peer) {
  4224. if (peer.enableDataChannels) {
  4225. peer.sendDirectly(channel, message, payload);
  4226. }
  4227. });
  4228. };
  4229. module.exports = WebRTC;
  4230. },{"./peer":11,"localmedia":12,"mockconsole":7,"util":9,"webrtcsupport":5,"wildemitter":3}],13:[function(require,module,exports){
  4231. // shim for using process in browser
  4232. var process = module.exports = {};
  4233. process.nextTick = (function () {
  4234. var canSetImmediate = typeof window !== 'undefined'
  4235. && window.setImmediate;
  4236. var canPost = typeof window !== 'undefined'
  4237. && window.postMessage && window.addEventListener
  4238. ;
  4239. if (canSetImmediate) {
  4240. return function (f) { return window.setImmediate(f) };
  4241. }
  4242. if (canPost) {
  4243. var queue = [];
  4244. window.addEventListener('message', function (ev) {
  4245. var source = ev.source;
  4246. if ((source === window || source === null) && ev.data === 'process-tick') {
  4247. ev.stopPropagation();
  4248. if (queue.length > 0) {
  4249. var fn = queue.shift();
  4250. fn();
  4251. }
  4252. }
  4253. }, true);
  4254. return function nextTick(fn) {
  4255. queue.push(fn);
  4256. window.postMessage('process-tick', '*');
  4257. };
  4258. }
  4259. return function nextTick(fn) {
  4260. setTimeout(fn, 0);
  4261. };
  4262. })();
  4263. process.title = 'browser';
  4264. process.browser = true;
  4265. process.env = {};
  4266. process.argv = [];
  4267. process.binding = function (name) {
  4268. throw new Error('process.binding is not supported');
  4269. }
  4270. // TODO(shtylman)
  4271. process.cwd = function () { return '/' };
  4272. process.chdir = function (dir) {
  4273. throw new Error('process.chdir is not supported');
  4274. };
  4275. },{}],10:[function(require,module,exports){
  4276. var process=require("__browserify_process");if (!process.EventEmitter) process.EventEmitter = function () {};
  4277. var EventEmitter = exports.EventEmitter = process.EventEmitter;
  4278. var isArray = typeof Array.isArray === 'function'
  4279. ? Array.isArray
  4280. : function (xs) {
  4281. return Object.prototype.toString.call(xs) === '[object Array]'
  4282. }
  4283. ;
  4284. function indexOf (xs, x) {
  4285. if (xs.indexOf) return xs.indexOf(x);
  4286. for (var i = 0; i < xs.length; i++) {
  4287. if (x === xs[i]) return i;
  4288. }
  4289. return -1;
  4290. }
  4291. // By default EventEmitters will print a warning if more than
  4292. // 10 listeners are added to it. This is a useful default which
  4293. // helps finding memory leaks.
  4294. //
  4295. // Obviously not all Emitters should be limited to 10. This function allows
  4296. // that to be increased. Set to zero for unlimited.
  4297. var defaultMaxListeners = 10;
  4298. EventEmitter.prototype.setMaxListeners = function(n) {
  4299. if (!this._events) this._events = {};
  4300. this._events.maxListeners = n;
  4301. };
  4302. EventEmitter.prototype.emit = function(type) {
  4303. // If there is no 'error' event listener then throw.
  4304. if (type === 'error') {
  4305. if (!this._events || !this._events.error ||
  4306. (isArray(this._events.error) && !this._events.error.length))
  4307. {
  4308. if (arguments[1] instanceof Error) {
  4309. throw arguments[1]; // Unhandled 'error' event
  4310. } else {
  4311. throw new Error("Uncaught, unspecified 'error' event.");
  4312. }
  4313. return false;
  4314. }
  4315. }
  4316. if (!this._events) return false;
  4317. var handler = this._events[type];
  4318. if (!handler) return false;
  4319. if (typeof handler == 'function') {
  4320. switch (arguments.length) {
  4321. // fast cases
  4322. case 1:
  4323. handler.call(this);
  4324. break;
  4325. case 2:
  4326. handler.call(this, arguments[1]);
  4327. break;
  4328. case 3:
  4329. handler.call(this, arguments[1], arguments[2]);
  4330. break;
  4331. // slower
  4332. default:
  4333. var args = Array.prototype.slice.call(arguments, 1);
  4334. handler.apply(this, args);
  4335. }
  4336. return true;
  4337. } else if (isArray(handler)) {
  4338. var args = Array.prototype.slice.call(arguments, 1);
  4339. var listeners = handler.slice();
  4340. for (var i = 0, l = listeners.length; i < l; i++) {
  4341. listeners[i].apply(this, args);
  4342. }
  4343. return true;
  4344. } else {
  4345. return false;
  4346. }
  4347. };
  4348. // EventEmitter is defined in src/node_events.cc
  4349. // EventEmitter.prototype.emit() is also defined there.
  4350. EventEmitter.prototype.addListener = function(type, listener) {
  4351. if ('function' !== typeof listener) {
  4352. throw new Error('addListener only takes instances of Function');
  4353. }
  4354. if (!this._events) this._events = {};
  4355. // To avoid recursion in the case that type == "newListeners"! Before
  4356. // adding it to the listeners, first emit "newListeners".
  4357. this.emit('newListener', type, listener);
  4358. if (!this._events[type]) {
  4359. // Optimize the case of one listener. Don't need the extra array object.
  4360. this._events[type] = listener;
  4361. } else if (isArray(this._events[type])) {
  4362. // Check for listener leak
  4363. if (!this._events[type].warned) {
  4364. var m;
  4365. if (this._events.maxListeners !== undefined) {
  4366. m = this._events.maxListeners;
  4367. } else {
  4368. m = defaultMaxListeners;
  4369. }
  4370. if (m && m > 0 && this._events[type].length > m) {
  4371. this._events[type].warned = true;
  4372. console.error('(node) warning: possible EventEmitter memory ' +
  4373. 'leak detected. %d listeners added. ' +
  4374. 'Use emitter.setMaxListeners() to increase limit.',
  4375. this._events[type].length);
  4376. console.trace();
  4377. }
  4378. }
  4379. // If we've already got an array, just append.
  4380. this._events[type].push(listener);
  4381. } else {
  4382. // Adding the second element, need to change to array.
  4383. this._events[type] = [this._events[type], listener];
  4384. }
  4385. return this;
  4386. };
  4387. EventEmitter.prototype.on = EventEmitter.prototype.addListener;
  4388. EventEmitter.prototype.once = function(type, listener) {
  4389. var self = this;
  4390. self.on(type, function g() {
  4391. self.removeListener(type, g);
  4392. listener.apply(this, arguments);
  4393. });
  4394. return this;
  4395. };
  4396. EventEmitter.prototype.removeListener = function(type, listener) {
  4397. if ('function' !== typeof listener) {
  4398. throw new Error('removeListener only takes instances of Function');
  4399. }
  4400. // does not use listeners(), so no side effect of creating _events[type]
  4401. if (!this._events || !this._events[type]) return this;
  4402. var list = this._events[type];
  4403. if (isArray(list)) {
  4404. var i = indexOf(list, listener);
  4405. if (i < 0) return this;
  4406. list.splice(i, 1);
  4407. if (list.length == 0)
  4408. delete this._events[type];
  4409. } else if (this._events[type] === listener) {
  4410. delete this._events[type];
  4411. }
  4412. return this;
  4413. };
  4414. EventEmitter.prototype.removeAllListeners = function(type) {
  4415. if (arguments.length === 0) {
  4416. this._events = {};
  4417. return this;
  4418. }
  4419. // does not use listeners(), so no side effect of creating _events[type]
  4420. if (type && this._events && this._events[type]) this._events[type] = null;
  4421. return this;
  4422. };
  4423. EventEmitter.prototype.listeners = function(type) {
  4424. if (!this._events) this._events = {};
  4425. if (!this._events[type]) this._events[type] = [];
  4426. if (!isArray(this._events[type])) {
  4427. this._events[type] = [this._events[type]];
  4428. }
  4429. return this._events[type];
  4430. };
  4431. EventEmitter.listenerCount = function(emitter, type) {
  4432. var ret;
  4433. if (!emitter._events || !emitter._events[type])
  4434. ret = 0;
  4435. else if (typeof emitter._events[type] === 'function')
  4436. ret = 1;
  4437. else
  4438. ret = emitter._events[type].length;
  4439. return ret;
  4440. };
  4441. },{"__browserify_process":13}],11:[function(require,module,exports){
  4442. var util = require('util');
  4443. var webrtc = require('webrtcsupport');
  4444. var PeerConnection = require('rtcpeerconnection');
  4445. var WildEmitter = require('wildemitter');
  4446. var FileTransfer = require('filetransfer');
  4447. // the inband-v1 protocol is sending metadata inband in a serialized JSON object
  4448. // followed by the actual data. Receiver closes the datachannel upon completion
  4449. var INBAND_FILETRANSFER_V1 = 'https://simplewebrtc.com/protocol/filetransfer#inband-v1';
  4450. function Peer(options) {
  4451. var self = this;
  4452. this.id = options.id;
  4453. this.parent = options.parent;
  4454. this.type = options.type || 'video';
  4455. this.oneway = options.oneway || false;
  4456. this.sharemyscreen = options.sharemyscreen || false;
  4457. this.browserPrefix = options.prefix;
  4458. this.stream = options.stream;
  4459. this.enableDataChannels = options.enableDataChannels === undefined ? this.parent.config.enableDataChannels : options.enableDataChannels;
  4460. this.receiveMedia = options.receiveMedia || this.parent.config.receiveMedia;
  4461. this.channels = {};
  4462. this.sid = options.sid || Date.now().toString();
  4463. // Create an RTCPeerConnection via the polyfill
  4464. this.pc = new PeerConnection(this.parent.config.peerConnectionConfig, this.parent.config.peerConnectionConstraints);
  4465. this.pc.on('ice', this.onIceCandidate.bind(this));
  4466. this.pc.on('offer', function (offer) {
  4467. self.send('offer', offer);
  4468. });
  4469. this.pc.on('answer', function (offer) {
  4470. self.send('answer', offer);
  4471. });
  4472. this.pc.on('addStream', this.handleRemoteStreamAdded.bind(this));
  4473. this.pc.on('addChannel', this.handleDataChannelAdded.bind(this));
  4474. this.pc.on('removeStream', this.handleStreamRemoved.bind(this));
  4475. // Just fire negotiation needed events for now
  4476. // When browser re-negotiation handling seems to work
  4477. // we can use this as the trigger for starting the offer/answer process
  4478. // automatically. We'll just leave it be for now while this stabalizes.
  4479. this.pc.on('negotiationNeeded', this.emit.bind(this, 'negotiationNeeded'));
  4480. this.pc.on('iceConnectionStateChange', this.emit.bind(this, 'iceConnectionStateChange'));
  4481. this.pc.on('iceConnectionStateChange', function () {
  4482. switch (self.pc.iceConnectionState) {
  4483. case 'failed':
  4484. // currently, in chrome only the initiator goes to failed
  4485. // so we need to signal this to the peer
  4486. if (self.pc.pc.peerconnection.localDescription.type === 'offer') {
  4487. self.parent.emit('iceFailed', self);
  4488. self.send('connectivityError');
  4489. }
  4490. break;
  4491. }
  4492. });
  4493. this.pc.on('signalingStateChange', this.emit.bind(this, 'signalingStateChange'));
  4494. this.logger = this.parent.logger;
  4495. // handle screensharing/broadcast mode
  4496. if (options.type === 'screen') {
  4497. if (this.parent.localScreen && this.sharemyscreen) {
  4498. this.logger.log('adding local screen stream to peer connection');
  4499. this.pc.addStream(this.parent.localScreen);
  4500. this.broadcaster = options.broadcaster;
  4501. }
  4502. } else {
  4503. this.parent.localStreams.forEach(function (stream) {
  4504. self.pc.addStream(stream);
  4505. });
  4506. }
  4507. // call emitter constructor
  4508. WildEmitter.call(this);
  4509. this.on('channelOpen', function (channel) {
  4510. if (channel.protocol === INBAND_FILETRANSFER_V1) {
  4511. channel.onmessage = function (event) {
  4512. var metadata = JSON.parse(event.data);
  4513. var receiver = new FileTransfer.Receiver();
  4514. receiver.receive(metadata, channel);
  4515. self.emit('fileTransfer', metadata, receiver);
  4516. receiver.on('receivedFile', function (file, metadata) {
  4517. receiver.channel.close();
  4518. });
  4519. };
  4520. }
  4521. });
  4522. // proxy events to parent
  4523. this.on('*', function () {
  4524. self.parent.emit.apply(self.parent, arguments);
  4525. });
  4526. }
  4527. util.inherits(Peer, WildEmitter);
  4528. Peer.prototype.handleMessage = function (message) {
  4529. var self = this;
  4530. this.logger.log('getting', message.type, message);
  4531. if (message.prefix) this.browserPrefix = message.prefix;
  4532. if (message.type === 'offer') {
  4533. // workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=1064247
  4534. message.payload.sdp = message.payload.sdp.replace('a=fmtp:0 profile-level-id=0x42e00c;packetization-mode=1\r\n', '');
  4535. this.pc.handleOffer(message.payload, function (err) {
  4536. if (err) {
  4537. return;
  4538. }
  4539. // auto-accept
  4540. self.pc.answer(self.receiveMedia, function (err, sessionDescription) {
  4541. //self.send('answer', sessionDescription);
  4542. });
  4543. });
  4544. } else if (message.type === 'answer') {
  4545. this.pc.handleAnswer(message.payload);
  4546. } else if (message.type === 'candidate') {
  4547. this.pc.processIce(message.payload);
  4548. } else if (message.type === 'connectivityError') {
  4549. this.parent.emit('connectivityError', self);
  4550. } else if (message.type === 'mute') {
  4551. this.parent.emit('mute', {id: message.from, name: message.payload.name});
  4552. } else if (message.type === 'unmute') {
  4553. this.parent.emit('unmute', {id: message.from, name: message.payload.name});
  4554. }
  4555. };
  4556. // send via signalling channel
  4557. Peer.prototype.send = function (messageType, payload) {
  4558. var message = {
  4559. to: this.id,
  4560. sid: this.sid,
  4561. broadcaster: this.broadcaster,
  4562. roomType: this.type,
  4563. type: messageType,
  4564. payload: payload,
  4565. prefix: webrtc.prefix
  4566. };
  4567. this.logger.log('sending', messageType, message);
  4568. this.parent.emit('message', message);
  4569. };
  4570. // send via data channel
  4571. // returns true when message was sent and false if channel is not open
  4572. Peer.prototype.sendDirectly = function (channel, messageType, payload) {
  4573. var message = {
  4574. type: messageType,
  4575. payload: payload
  4576. };
  4577. this.logger.log('sending via datachannel', channel, messageType, message);
  4578. var dc = this.getDataChannel(channel);
  4579. if (dc.readyState != 'open') return false;
  4580. dc.send(JSON.stringify(message));
  4581. return true;
  4582. };
  4583. // Internal method registering handlers for a data channel and emitting events on the peer
  4584. Peer.prototype._observeDataChannel = function (channel) {
  4585. var self = this;
  4586. channel.onclose = this.emit.bind(this, 'channelClose', channel);
  4587. channel.onerror = this.emit.bind(this, 'channelError', channel);
  4588. channel.onmessage = function (event) {
  4589. self.emit('channelMessage', self, channel.label, JSON.parse(event.data), channel, event);
  4590. };
  4591. channel.onopen = this.emit.bind(this, 'channelOpen', channel);
  4592. };
  4593. // Fetch or create a data channel by the given name
  4594. Peer.prototype.getDataChannel = function (name, opts) {
  4595. if (!webrtc.supportDataChannel) return this.emit('error', new Error('createDataChannel not supported'));
  4596. var channel = this.channels[name];
  4597. opts || (opts = {});
  4598. if (channel) return channel;
  4599. // if we don't have one by this label, create it
  4600. channel = this.channels[name] = this.pc.createDataChannel(name, opts);
  4601. this._observeDataChannel(channel);
  4602. return channel;
  4603. };
  4604. Peer.prototype.onIceCandidate = function (candidate) {
  4605. if (this.closed) return;
  4606. if (candidate) {
  4607. this.send('candidate', candidate);
  4608. } else {
  4609. this.logger.log("End of candidates.");
  4610. }
  4611. };
  4612. Peer.prototype.start = function () {
  4613. var self = this;
  4614. // well, the webrtc api requires that we either
  4615. // a) create a datachannel a priori
  4616. // b) do a renegotiation later to add the SCTP m-line
  4617. // Let's do (a) first...
  4618. if (this.enableDataChannels) {
  4619. this.getDataChannel('simplewebrtc');
  4620. }
  4621. this.pc.offer(this.receiveMedia, function (err, sessionDescription) {
  4622. //self.send('offer', sessionDescription);
  4623. });
  4624. };
  4625. Peer.prototype.icerestart = function () {
  4626. var constraints = this.receiveMedia;
  4627. constraints.mandatory.IceRestart = true;
  4628. this.pc.offer(constraints, function (err, success) { });
  4629. };
  4630. Peer.prototype.end = function () {
  4631. if (this.closed) return;
  4632. this.pc.close();
  4633. this.handleStreamRemoved();
  4634. };
  4635. Peer.prototype.handleRemoteStreamAdded = function (event) {
  4636. var self = this;
  4637. if (this.stream) {
  4638. this.logger.warn('Already have a remote stream');
  4639. } else {
  4640. this.stream = event.stream;
  4641. // FIXME: addEventListener('ended', ...) would be nicer
  4642. // but does not work in firefox
  4643. this.stream.onended = function () {
  4644. self.end();
  4645. };
  4646. this.parent.emit('peerStreamAdded', this);
  4647. }
  4648. };
  4649. Peer.prototype.handleStreamRemoved = function () {
  4650. this.parent.peers.splice(this.parent.peers.indexOf(this), 1);
  4651. this.closed = true;
  4652. this.parent.emit('peerStreamRemoved', this);
  4653. };
  4654. Peer.prototype.handleDataChannelAdded = function (channel) {
  4655. this.channels[channel.label] = channel;
  4656. this._observeDataChannel(channel);
  4657. };
  4658. Peer.prototype.sendFile = function (file) {
  4659. var sender = new FileTransfer.Sender();
  4660. var dc = this.getDataChannel('filetransfer' + (new Date()).getTime(), {
  4661. protocol: INBAND_FILETRANSFER_V1
  4662. });
  4663. // override onopen
  4664. dc.onopen = function () {
  4665. dc.send(JSON.stringify({
  4666. size: file.size,
  4667. name: file.name
  4668. }));
  4669. sender.send(file, dc);
  4670. };
  4671. // override onclose
  4672. dc.onclose = function () {
  4673. console.log('sender received transfer');
  4674. sender.emit('complete');
  4675. };
  4676. return sender;
  4677. };
  4678. module.exports = Peer;
  4679. },{"filetransfer":15,"rtcpeerconnection":14,"util":9,"webrtcsupport":5,"wildemitter":3}],16:[function(require,module,exports){
  4680. // getUserMedia helper by @HenrikJoreteg
  4681. var func = (window.navigator.getUserMedia ||
  4682. window.navigator.webkitGetUserMedia ||
  4683. window.navigator.mozGetUserMedia ||
  4684. window.navigator.msGetUserMedia);
  4685. module.exports = function (constraints, cb) {
  4686. var options, error;
  4687. var haveOpts = arguments.length === 2;
  4688. var defaultOpts = {video: true, audio: true};
  4689. var denied = 'PermissionDeniedError';
  4690. var notSatisfied = 'ConstraintNotSatisfiedError';
  4691. // make constraints optional
  4692. if (!haveOpts) {
  4693. cb = constraints;
  4694. constraints = defaultOpts;
  4695. }
  4696. // treat lack of browser support like an error
  4697. if (!func) {
  4698. // throw proper error per spec
  4699. error = new Error('MediaStreamError');
  4700. error.name = 'NotSupportedError';
  4701. // keep all callbacks async
  4702. return window.setTimeout(function () {
  4703. cb(error);
  4704. }, 0);
  4705. }
  4706. // make requesting media from non-http sources trigger an error
  4707. // current browsers silently drop the request instead
  4708. var protocol = window.location.protocol;
  4709. if (protocol !== 'http:' && protocol !== 'https:') {
  4710. error = new Error('MediaStreamError');
  4711. error.name = 'NotSupportedError';
  4712. // keep all callbacks async
  4713. return window.setTimeout(function () {
  4714. cb(error);
  4715. }, 0);
  4716. }
  4717. // normalize error handling when no media types are requested
  4718. if (!constraints.audio && !constraints.video) {
  4719. error = new Error('MediaStreamError');
  4720. error.name = 'NoMediaRequestedError';
  4721. // keep all callbacks async
  4722. return window.setTimeout(function () {
  4723. cb(error);
  4724. }, 0);
  4725. }
  4726. if (localStorage && localStorage.useFirefoxFakeDevice === "true") {
  4727. constraints.fake = true;
  4728. }
  4729. func.call(window.navigator, constraints, function (stream) {
  4730. cb(null, stream);
  4731. }, function (err) {
  4732. var error;
  4733. // coerce into an error object since FF gives us a string
  4734. // there are only two valid names according to the spec
  4735. // we coerce all non-denied to "constraint not satisfied".
  4736. if (typeof err === 'string') {
  4737. error = new Error('MediaStreamError');
  4738. if (err === denied) {
  4739. error.name = denied;
  4740. } else {
  4741. error.name = notSatisfied;
  4742. }
  4743. } else {
  4744. // if we get an error object make sure '.name' property is set
  4745. // according to spec: http://dev.w3.org/2011/webrtc/editor/getusermedia.html#navigatorusermediaerror-and-navigatorusermediaerrorcallback
  4746. error = err;
  4747. if (!error.name) {
  4748. // this is likely chrome which
  4749. // sets a property called "ERROR_DENIED" on the error object
  4750. // if so we make sure to set a name
  4751. if (error[denied]) {
  4752. err.name = denied;
  4753. } else {
  4754. err.name = notSatisfied;
  4755. }
  4756. }
  4757. }
  4758. cb(error);
  4759. });
  4760. };
  4761. },{}],12:[function(require,module,exports){
  4762. var util = require('util');
  4763. var hark = require('hark');
  4764. var webrtc = require('webrtcsupport');
  4765. var getUserMedia = require('getusermedia');
  4766. var getScreenMedia = require('getscreenmedia');
  4767. var WildEmitter = require('wildemitter');
  4768. var GainController = require('mediastream-gain');
  4769. var mockconsole = require('mockconsole');
  4770. function LocalMedia(opts) {
  4771. WildEmitter.call(this);
  4772. var config = this.config = {
  4773. autoAdjustMic: false,
  4774. detectSpeakingEvents: true,
  4775. media: {
  4776. audio: true,
  4777. video: true
  4778. },
  4779. logger: mockconsole
  4780. };
  4781. var item;
  4782. for (item in opts) {
  4783. this.config[item] = opts[item];
  4784. }
  4785. this.logger = config.logger;
  4786. this._log = this.logger.log.bind(this.logger, 'LocalMedia:');
  4787. this._logerror = this.logger.error.bind(this.logger, 'LocalMedia:');
  4788. this.screenSharingSupport = webrtc.screenSharing;
  4789. this.localStreams = [];
  4790. this.localScreens = [];
  4791. if (!webrtc.support) {
  4792. this._logerror('Your browser does not support local media capture.');
  4793. }
  4794. }
  4795. util.inherits(LocalMedia, WildEmitter);
  4796. LocalMedia.prototype.start = function (mediaConstraints, cb) {
  4797. var self = this;
  4798. var constraints = mediaConstraints || this.config.media;
  4799. getUserMedia(constraints, function (err, stream) {
  4800. if (!err) {
  4801. if (constraints.audio && self.config.detectSpeakingEvents) {
  4802. self.setupAudioMonitor(stream, self.config.harkOptions);
  4803. }
  4804. self.localStreams.push(stream);
  4805. if (self.config.autoAdjustMic) {
  4806. self.gainController = new GainController(stream);
  4807. // start out somewhat muted if we can track audio
  4808. self.setMicIfEnabled(0.5);
  4809. }
  4810. // TODO: might need to migrate to the video tracks onended
  4811. // FIXME: firefox does not seem to trigger this...
  4812. stream.onended = function () {
  4813. /*
  4814. var idx = self.localStreams.indexOf(stream);
  4815. if (idx > -1) {
  4816. self.localScreens.splice(idx, 1);
  4817. }
  4818. self.emit('localStreamStopped', stream);
  4819. */
  4820. };
  4821. self.emit('localStream', stream);
  4822. }
  4823. if (cb) {
  4824. return cb(err, stream);
  4825. }
  4826. });
  4827. };
  4828. LocalMedia.prototype.stop = function (stream) {
  4829. var self = this;
  4830. // FIXME: duplicates cleanup code until fixed in FF
  4831. if (stream) {
  4832. stream.stop();
  4833. self.emit('localStreamStopped', stream);
  4834. var idx = self.localStreams.indexOf(stream);
  4835. if (idx > -1) {
  4836. self.localStreams = self.localStreams.splice(idx, 1);
  4837. }
  4838. } else {
  4839. if (this.audioMonitor) {
  4840. this.audioMonitor.stop();
  4841. delete this.audioMonitor;
  4842. }
  4843. this.localStreams.forEach(function (stream) {
  4844. stream.stop();
  4845. self.emit('localStreamStopped', stream);
  4846. });
  4847. this.localStreams = [];
  4848. }
  4849. };
  4850. LocalMedia.prototype.startScreenShare = function (cb) {
  4851. var self = this;
  4852. getScreenMedia(function (err, stream) {
  4853. if (!err) {
  4854. self.localScreens.push(stream);
  4855. // TODO: might need to migrate to the video tracks onended
  4856. // Firefox does not support .onended but it does not support
  4857. // screensharing either
  4858. stream.onended = function () {
  4859. var idx = self.localScreens.indexOf(stream);
  4860. if (idx > -1) {
  4861. self.localScreens.splice(idx, 1);
  4862. }
  4863. self.emit('localScreenStopped', stream);
  4864. };
  4865. self.emit('localScreen', stream);
  4866. }
  4867. // enable the callback
  4868. if (cb) {
  4869. return cb(err, stream);
  4870. }
  4871. });
  4872. };
  4873. LocalMedia.prototype.stopScreenShare = function (stream) {
  4874. if (stream) {
  4875. stream.stop();
  4876. } else {
  4877. this.localScreens.forEach(function (stream) {
  4878. stream.stop();
  4879. });
  4880. this.localScreens = [];
  4881. }
  4882. };
  4883. // Audio controls
  4884. LocalMedia.prototype.mute = function () {
  4885. this._audioEnabled(false);
  4886. this.hardMuted = true;
  4887. this.emit('audioOff');
  4888. };
  4889. LocalMedia.prototype.unmute = function () {
  4890. this._audioEnabled(true);
  4891. this.hardMuted = false;
  4892. this.emit('audioOn');
  4893. };
  4894. LocalMedia.prototype.setupAudioMonitor = function (stream, harkOptions) {
  4895. this._log('Setup audio');
  4896. var audio = this.audioMonitor = hark(stream, harkOptions);
  4897. var self = this;
  4898. var timeout;
  4899. audio.on('speaking', function () {
  4900. self.emit('speaking');
  4901. if (self.hardMuted) {
  4902. return;
  4903. }
  4904. self.setMicIfEnabled(1);
  4905. });
  4906. audio.on('stopped_speaking', function () {
  4907. if (timeout) {
  4908. clearTimeout(timeout);
  4909. }
  4910. timeout = setTimeout(function () {
  4911. self.emit('stoppedSpeaking');
  4912. if (self.hardMuted) {
  4913. return;
  4914. }
  4915. self.setMicIfEnabled(0.5);
  4916. }, 1000);
  4917. });
  4918. audio.on('volume_change', function (volume, treshold) {
  4919. self.emit('volumeChange', volume, treshold);
  4920. });
  4921. };
  4922. // We do this as a seperate method in order to
  4923. // still leave the "setMicVolume" as a working
  4924. // method.
  4925. LocalMedia.prototype.setMicIfEnabled = function (volume) {
  4926. if (!this.config.autoAdjustMic) {
  4927. return;
  4928. }
  4929. this.gainController.setGain(volume);
  4930. };
  4931. // Video controls
  4932. LocalMedia.prototype.pauseVideo = function () {
  4933. this._videoEnabled(false);
  4934. this.emit('videoOff');
  4935. };
  4936. LocalMedia.prototype.resumeVideo = function () {
  4937. this._videoEnabled(true);
  4938. this.emit('videoOn');
  4939. };
  4940. // Combined controls
  4941. LocalMedia.prototype.pause = function () {
  4942. this.mute();
  4943. this.pauseVideo();
  4944. };
  4945. LocalMedia.prototype.resume = function () {
  4946. this.unmute();
  4947. this.resumeVideo();
  4948. };
  4949. // Internal methods for enabling/disabling audio/video
  4950. LocalMedia.prototype._audioEnabled = function (bool) {
  4951. // work around for chrome 27 bug where disabling tracks
  4952. // doesn't seem to work (works in canary, remove when working)
  4953. this.setMicIfEnabled(bool ? 1 : 0);
  4954. this.localStreams.forEach(function (stream) {
  4955. stream.getAudioTracks().forEach(function (track) {
  4956. track.enabled = !!bool;
  4957. });
  4958. });
  4959. };
  4960. LocalMedia.prototype._videoEnabled = function (bool) {
  4961. this.localStreams.forEach(function (stream) {
  4962. stream.getVideoTracks().forEach(function (track) {
  4963. track.enabled = !!bool;
  4964. });
  4965. });
  4966. };
  4967. // check if all audio streams are enabled
  4968. LocalMedia.prototype.isAudioEnabled = function () {
  4969. var enabled = true;
  4970. this.localStreams.forEach(function (stream) {
  4971. stream.getAudioTracks().forEach(function (track) {
  4972. enabled = enabled && track.enabled;
  4973. });
  4974. });
  4975. return enabled;
  4976. };
  4977. // check if all video streams are enabled
  4978. LocalMedia.prototype.isVideoEnabled = function () {
  4979. var enabled = true;
  4980. this.localStreams.forEach(function (stream) {
  4981. stream.getVideoTracks().forEach(function (track) {
  4982. enabled = enabled && track.enabled;
  4983. });
  4984. });
  4985. return enabled;
  4986. };
  4987. // Backwards Compat
  4988. LocalMedia.prototype.startLocalMedia = LocalMedia.prototype.start;
  4989. LocalMedia.prototype.stopLocalMedia = LocalMedia.prototype.stop;
  4990. // fallback for old .localStream behaviour
  4991. Object.defineProperty(LocalMedia.prototype, 'localStream', {
  4992. get: function () {
  4993. return this.localStreams.length > 0 ? this.localStreams[0] : null;
  4994. }
  4995. });
  4996. // fallback for old .localScreen behaviour
  4997. Object.defineProperty(LocalMedia.prototype, 'localScreen', {
  4998. get: function () {
  4999. return this.localScreens.length > 0 ? this.localScreens[0] : null;
  5000. }
  5001. });
  5002. module.exports = LocalMedia;
  5003. },{"getscreenmedia":18,"getusermedia":16,"hark":17,"mediastream-gain":19,"mockconsole":7,"util":9,"webrtcsupport":5,"wildemitter":3}],20:[function(require,module,exports){
  5004. // Underscore.js 1.8.2
  5005. // http://underscorejs.org
  5006. // (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
  5007. // Underscore may be freely distributed under the MIT license.
  5008. (function() {
  5009. // Baseline setup
  5010. // --------------
  5011. // Establish the root object, `window` in the browser, or `exports` on the server.
  5012. var root = this;
  5013. // Save the previous value of the `_` variable.
  5014. var previousUnderscore = root._;
  5015. // Save bytes in the minified (but not gzipped) version:
  5016. var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
  5017. // Create quick reference variables for speed access to core prototypes.
  5018. var
  5019. push = ArrayProto.push,
  5020. slice = ArrayProto.slice,
  5021. toString = ObjProto.toString,
  5022. hasOwnProperty = ObjProto.hasOwnProperty;
  5023. // All **ECMAScript 5** native function implementations that we hope to use
  5024. // are declared here.
  5025. var
  5026. nativeIsArray = Array.isArray,
  5027. nativeKeys = Object.keys,
  5028. nativeBind = FuncProto.bind,
  5029. nativeCreate = Object.create;
  5030. // Naked function reference for surrogate-prototype-swapping.
  5031. var Ctor = function(){};
  5032. // Create a safe reference to the Underscore object for use below.
  5033. var _ = function(obj) {
  5034. if (obj instanceof _) return obj;
  5035. if (!(this instanceof _)) return new _(obj);
  5036. this._wrapped = obj;
  5037. };
  5038. // Export the Underscore object for **Node.js**, with
  5039. // backwards-compatibility for the old `require()` API. If we're in
  5040. // the browser, add `_` as a global object.
  5041. if (typeof exports !== 'undefined') {
  5042. if (typeof module !== 'undefined' && module.exports) {
  5043. exports = module.exports = _;
  5044. }
  5045. exports._ = _;
  5046. } else {
  5047. root._ = _;
  5048. }
  5049. // Current version.
  5050. _.VERSION = '1.8.2';
  5051. // Internal function that returns an efficient (for current engines) version
  5052. // of the passed-in callback, to be repeatedly applied in other Underscore
  5053. // functions.
  5054. var optimizeCb = function(func, context, argCount) {
  5055. if (context === void 0) return func;
  5056. switch (argCount == null ? 3 : argCount) {
  5057. case 1: return function(value) {
  5058. return func.call(context, value);
  5059. };
  5060. case 2: return function(value, other) {
  5061. return func.call(context, value, other);
  5062. };
  5063. case 3: return function(value, index, collection) {
  5064. return func.call(context, value, index, collection);
  5065. };
  5066. case 4: return function(accumulator, value, index, collection) {
  5067. return func.call(context, accumulator, value, index, collection);
  5068. };
  5069. }
  5070. return function() {
  5071. return func.apply(context, arguments);
  5072. };
  5073. };
  5074. // A mostly-internal function to generate callbacks that can be applied
  5075. // to each element in a collection, returning the desired result — either
  5076. // identity, an arbitrary callback, a property matcher, or a property accessor.
  5077. var cb = function(value, context, argCount) {
  5078. if (value == null) return _.identity;
  5079. if (_.isFunction(value)) return optimizeCb(value, context, argCount);
  5080. if (_.isObject(value)) return _.matcher(value);
  5081. return _.property(value);
  5082. };
  5083. _.iteratee = function(value, context) {
  5084. return cb(value, context, Infinity);
  5085. };
  5086. // An internal function for creating assigner functions.
  5087. var createAssigner = function(keysFunc, undefinedOnly) {
  5088. return function(obj) {
  5089. var length = arguments.length;
  5090. if (length < 2 || obj == null) return obj;
  5091. for (var index = 1; index < length; index++) {
  5092. var source = arguments[index],
  5093. keys = keysFunc(source),
  5094. l = keys.length;
  5095. for (var i = 0; i < l; i++) {
  5096. var key = keys[i];
  5097. if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key];
  5098. }
  5099. }
  5100. return obj;
  5101. };
  5102. };
  5103. // An internal function for creating a new object that inherits from another.
  5104. var baseCreate = function(prototype) {
  5105. if (!_.isObject(prototype)) return {};
  5106. if (nativeCreate) return nativeCreate(prototype);
  5107. Ctor.prototype = prototype;
  5108. var result = new Ctor;
  5109. Ctor.prototype = null;
  5110. return result;
  5111. };
  5112. // Helper for collection methods to determine whether a collection
  5113. // should be iterated as an array or as an object
  5114. // Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
  5115. var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
  5116. var isArrayLike = function(collection) {
  5117. var length = collection && collection.length;
  5118. return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
  5119. };
  5120. // Collection Functions
  5121. // --------------------
  5122. // The cornerstone, an `each` implementation, aka `forEach`.
  5123. // Handles raw objects in addition to array-likes. Treats all
  5124. // sparse array-likes as if they were dense.
  5125. _.each = _.forEach = function(obj, iteratee, context) {
  5126. iteratee = optimizeCb(iteratee, context);
  5127. var i, length;
  5128. if (isArrayLike(obj)) {
  5129. for (i = 0, length = obj.length; i < length; i++) {
  5130. iteratee(obj[i], i, obj);
  5131. }
  5132. } else {
  5133. var keys = _.keys(obj);
  5134. for (i = 0, length = keys.length; i < length; i++) {
  5135. iteratee(obj[keys[i]], keys[i], obj);
  5136. }
  5137. }
  5138. return obj;
  5139. };
  5140. // Return the results of applying the iteratee to each element.
  5141. _.map = _.collect = function(obj, iteratee, context) {
  5142. iteratee = cb(iteratee, context);
  5143. var keys = !isArrayLike(obj) && _.keys(obj),
  5144. length = (keys || obj).length,
  5145. results = Array(length);
  5146. for (var index = 0; index < length; index++) {
  5147. var currentKey = keys ? keys[index] : index;
  5148. results[index] = iteratee(obj[currentKey], currentKey, obj);
  5149. }
  5150. return results;
  5151. };
  5152. // Create a reducing function iterating left or right.
  5153. function createReduce(dir) {
  5154. // Optimized iterator function as using arguments.length
  5155. // in the main function will deoptimize the, see #1991.
  5156. function iterator(obj, iteratee, memo, keys, index, length) {
  5157. for (; index >= 0 && index < length; index += dir) {
  5158. var currentKey = keys ? keys[index] : index;
  5159. memo = iteratee(memo, obj[currentKey], currentKey, obj);
  5160. }
  5161. return memo;
  5162. }
  5163. return function(obj, iteratee, memo, context) {
  5164. iteratee = optimizeCb(iteratee, context, 4);
  5165. var keys = !isArrayLike(obj) && _.keys(obj),
  5166. length = (keys || obj).length,
  5167. index = dir > 0 ? 0 : length - 1;
  5168. // Determine the initial value if none is provided.
  5169. if (arguments.length < 3) {
  5170. memo = obj[keys ? keys[index] : index];
  5171. index += dir;
  5172. }
  5173. return iterator(obj, iteratee, memo, keys, index, length);
  5174. };
  5175. }
  5176. // **Reduce** builds up a single result from a list of values, aka `inject`,
  5177. // or `foldl`.
  5178. _.reduce = _.foldl = _.inject = createReduce(1);
  5179. // The right-associative version of reduce, also known as `foldr`.
  5180. _.reduceRight = _.foldr = createReduce(-1);
  5181. // Return the first value which passes a truth test. Aliased as `detect`.
  5182. _.find = _.detect = function(obj, predicate, context) {
  5183. var key;
  5184. if (isArrayLike(obj)) {
  5185. key = _.findIndex(obj, predicate, context);
  5186. } else {
  5187. key = _.findKey(obj, predicate, context);
  5188. }
  5189. if (key !== void 0 && key !== -1) return obj[key];
  5190. };
  5191. // Return all the elements that pass a truth test.
  5192. // Aliased as `select`.
  5193. _.filter = _.select = function(obj, predicate, context) {
  5194. var results = [];
  5195. predicate = cb(predicate, context);
  5196. _.each(obj, function(value, index, list) {
  5197. if (predicate(value, index, list)) results.push(value);
  5198. });
  5199. return results;
  5200. };
  5201. // Return all the elements for which a truth test fails.
  5202. _.reject = function(obj, predicate, context) {
  5203. return _.filter(obj, _.negate(cb(predicate)), context);
  5204. };
  5205. // Determine whether all of the elements match a truth test.
  5206. // Aliased as `all`.
  5207. _.every = _.all = function(obj, predicate, context) {
  5208. predicate = cb(predicate, context);
  5209. var keys = !isArrayLike(obj) && _.keys(obj),
  5210. length = (keys || obj).length;
  5211. for (var index = 0; index < length; index++) {
  5212. var currentKey = keys ? keys[index] : index;
  5213. if (!predicate(obj[currentKey], currentKey, obj)) return false;
  5214. }
  5215. return true;
  5216. };
  5217. // Determine if at least one element in the object matches a truth test.
  5218. // Aliased as `any`.
  5219. _.some = _.any = function(obj, predicate, context) {
  5220. predicate = cb(predicate, context);
  5221. var keys = !isArrayLike(obj) && _.keys(obj),
  5222. length = (keys || obj).length;
  5223. for (var index = 0; index < length; index++) {
  5224. var currentKey = keys ? keys[index] : index;
  5225. if (predicate(obj[currentKey], currentKey, obj)) return true;
  5226. }
  5227. return false;
  5228. };
  5229. // Determine if the array or object contains a given value (using `===`).
  5230. // Aliased as `includes` and `include`.
  5231. _.contains = _.includes = _.include = function(obj, target, fromIndex) {
  5232. if (!isArrayLike(obj)) obj = _.values(obj);
  5233. return _.indexOf(obj, target, typeof fromIndex == 'number' && fromIndex) >= 0;
  5234. };
  5235. // Invoke a method (with arguments) on every item in a collection.
  5236. _.invoke = function(obj, method) {
  5237. var args = slice.call(arguments, 2);
  5238. var isFunc = _.isFunction(method);
  5239. return _.map(obj, function(value) {
  5240. var func = isFunc ? method : value[method];
  5241. return func == null ? func : func.apply(value, args);
  5242. });
  5243. };
  5244. // Convenience version of a common use case of `map`: fetching a property.
  5245. _.pluck = function(obj, key) {
  5246. return _.map(obj, _.property(key));
  5247. };
  5248. // Convenience version of a common use case of `filter`: selecting only objects
  5249. // containing specific `key:value` pairs.
  5250. _.where = function(obj, attrs) {
  5251. return _.filter(obj, _.matcher(attrs));
  5252. };
  5253. // Convenience version of a common use case of `find`: getting the first object
  5254. // containing specific `key:value` pairs.
  5255. _.findWhere = function(obj, attrs) {
  5256. return _.find(obj, _.matcher(attrs));
  5257. };
  5258. // Return the maximum element (or element-based computation).
  5259. _.max = function(obj, iteratee, context) {
  5260. var result = -Infinity, lastComputed = -Infinity,
  5261. value, computed;
  5262. if (iteratee == null && obj != null) {
  5263. obj = isArrayLike(obj) ? obj : _.values(obj);
  5264. for (var i = 0, length = obj.length; i < length; i++) {
  5265. value = obj[i];
  5266. if (value > result) {
  5267. result = value;
  5268. }
  5269. }
  5270. } else {
  5271. iteratee = cb(iteratee, context);
  5272. _.each(obj, function(value, index, list) {
  5273. computed = iteratee(value, index, list);
  5274. if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
  5275. result = value;
  5276. lastComputed = computed;
  5277. }
  5278. });
  5279. }
  5280. return result;
  5281. };
  5282. // Return the minimum element (or element-based computation).
  5283. _.min = function(obj, iteratee, context) {
  5284. var result = Infinity, lastComputed = Infinity,
  5285. value, computed;
  5286. if (iteratee == null && obj != null) {
  5287. obj = isArrayLike(obj) ? obj : _.values(obj);
  5288. for (var i = 0, length = obj.length; i < length; i++) {
  5289. value = obj[i];
  5290. if (value < result) {
  5291. result = value;
  5292. }
  5293. }
  5294. } else {
  5295. iteratee = cb(iteratee, context);
  5296. _.each(obj, function(value, index, list) {
  5297. computed = iteratee(value, index, list);
  5298. if (computed < lastComputed || computed === Infinity && result === Infinity) {
  5299. result = value;
  5300. lastComputed = computed;
  5301. }
  5302. });
  5303. }
  5304. return result;
  5305. };
  5306. // Shuffle a collection, using the modern version of the
  5307. // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
  5308. _.shuffle = function(obj) {
  5309. var set = isArrayLike(obj) ? obj : _.values(obj);
  5310. var length = set.length;
  5311. var shuffled = Array(length);
  5312. for (var index = 0, rand; index < length; index++) {
  5313. rand = _.random(0, index);
  5314. if (rand !== index) shuffled[index] = shuffled[rand];
  5315. shuffled[rand] = set[index];
  5316. }
  5317. return shuffled;
  5318. };
  5319. // Sample **n** random values from a collection.
  5320. // If **n** is not specified, returns a single random element.
  5321. // The internal `guard` argument allows it to work with `map`.
  5322. _.sample = function(obj, n, guard) {
  5323. if (n == null || guard) {
  5324. if (!isArrayLike(obj)) obj = _.values(obj);
  5325. return obj[_.random(obj.length - 1)];
  5326. }
  5327. return _.shuffle(obj).slice(0, Math.max(0, n));
  5328. };
  5329. // Sort the object's values by a criterion produced by an iteratee.
  5330. _.sortBy = function(obj, iteratee, context) {
  5331. iteratee = cb(iteratee, context);
  5332. return _.pluck(_.map(obj, function(value, index, list) {
  5333. return {
  5334. value: value,
  5335. index: index,
  5336. criteria: iteratee(value, index, list)
  5337. };
  5338. }).sort(function(left, right) {
  5339. var a = left.criteria;
  5340. var b = right.criteria;
  5341. if (a !== b) {
  5342. if (a > b || a === void 0) return 1;
  5343. if (a < b || b === void 0) return -1;
  5344. }
  5345. return left.index - right.index;
  5346. }), 'value');
  5347. };
  5348. // An internal function used for aggregate "group by" operations.
  5349. var group = function(behavior) {
  5350. return function(obj, iteratee, context) {
  5351. var result = {};
  5352. iteratee = cb(iteratee, context);
  5353. _.each(obj, function(value, index) {
  5354. var key = iteratee(value, index, obj);
  5355. behavior(result, value, key);
  5356. });
  5357. return result;
  5358. };
  5359. };
  5360. // Groups the object's values by a criterion. Pass either a string attribute
  5361. // to group by, or a function that returns the criterion.
  5362. _.groupBy = group(function(result, value, key) {
  5363. if (_.has(result, key)) result[key].push(value); else result[key] = [value];
  5364. });
  5365. // Indexes the object's values by a criterion, similar to `groupBy`, but for
  5366. // when you know that your index values will be unique.
  5367. _.indexBy = group(function(result, value, key) {
  5368. result[key] = value;
  5369. });
  5370. // Counts instances of an object that group by a certain criterion. Pass
  5371. // either a string attribute to count by, or a function that returns the
  5372. // criterion.
  5373. _.countBy = group(function(result, value, key) {
  5374. if (_.has(result, key)) result[key]++; else result[key] = 1;
  5375. });
  5376. // Safely create a real, live array from anything iterable.
  5377. _.toArray = function(obj) {
  5378. if (!obj) return [];
  5379. if (_.isArray(obj)) return slice.call(obj);
  5380. if (isArrayLike(obj)) return _.map(obj, _.identity);
  5381. return _.values(obj);
  5382. };
  5383. // Return the number of elements in an object.
  5384. _.size = function(obj) {
  5385. if (obj == null) return 0;
  5386. return isArrayLike(obj) ? obj.length : _.keys(obj).length;
  5387. };
  5388. // Split a collection into two arrays: one whose elements all satisfy the given
  5389. // predicate, and one whose elements all do not satisfy the predicate.
  5390. _.partition = function(obj, predicate, context) {
  5391. predicate = cb(predicate, context);
  5392. var pass = [], fail = [];
  5393. _.each(obj, function(value, key, obj) {
  5394. (predicate(value, key, obj) ? pass : fail).push(value);
  5395. });
  5396. return [pass, fail];
  5397. };
  5398. // Array Functions
  5399. // ---------------
  5400. // Get the first element of an array. Passing **n** will return the first N
  5401. // values in the array. Aliased as `head` and `take`. The **guard** check
  5402. // allows it to work with `_.map`.
  5403. _.first = _.head = _.take = function(array, n, guard) {
  5404. if (array == null) return void 0;
  5405. if (n == null || guard) return array[0];
  5406. return _.initial(array, array.length - n);
  5407. };
  5408. // Returns everything but the last entry of the array. Especially useful on
  5409. // the arguments object. Passing **n** will return all the values in
  5410. // the array, excluding the last N.
  5411. _.initial = function(array, n, guard) {
  5412. return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
  5413. };
  5414. // Get the last element of an array. Passing **n** will return the last N
  5415. // values in the array.
  5416. _.last = function(array, n, guard) {
  5417. if (array == null) return void 0;
  5418. if (n == null || guard) return array[array.length - 1];
  5419. return _.rest(array, Math.max(0, array.length - n));
  5420. };
  5421. // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
  5422. // Especially useful on the arguments object. Passing an **n** will return
  5423. // the rest N values in the array.
  5424. _.rest = _.tail = _.drop = function(array, n, guard) {
  5425. return slice.call(array, n == null || guard ? 1 : n);
  5426. };
  5427. // Trim out all falsy values from an array.
  5428. _.compact = function(array) {
  5429. return _.filter(array, _.identity);
  5430. };
  5431. // Internal implementation of a recursive `flatten` function.
  5432. var flatten = function(input, shallow, strict, startIndex) {
  5433. var output = [], idx = 0;
  5434. for (var i = startIndex || 0, length = input && input.length; i < length; i++) {
  5435. var value = input[i];
  5436. if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
  5437. //flatten current level of array or arguments object
  5438. if (!shallow) value = flatten(value, shallow, strict);
  5439. var j = 0, len = value.length;
  5440. output.length += len;
  5441. while (j < len) {
  5442. output[idx++] = value[j++];
  5443. }
  5444. } else if (!strict) {
  5445. output[idx++] = value;
  5446. }
  5447. }
  5448. return output;
  5449. };
  5450. // Flatten out an array, either recursively (by default), or just one level.
  5451. _.flatten = function(array, shallow) {
  5452. return flatten(array, shallow, false);
  5453. };
  5454. // Return a version of the array that does not contain the specified value(s).
  5455. _.without = function(array) {
  5456. return _.difference(array, slice.call(arguments, 1));
  5457. };
  5458. // Produce a duplicate-free version of the array. If the array has already
  5459. // been sorted, you have the option of using a faster algorithm.
  5460. // Aliased as `unique`.
  5461. _.uniq = _.unique = function(array, isSorted, iteratee, context) {
  5462. if (array == null) return [];
  5463. if (!_.isBoolean(isSorted)) {
  5464. context = iteratee;
  5465. iteratee = isSorted;
  5466. isSorted = false;
  5467. }
  5468. if (iteratee != null) iteratee = cb(iteratee, context);
  5469. var result = [];
  5470. var seen = [];
  5471. for (var i = 0, length = array.length; i < length; i++) {
  5472. var value = array[i],
  5473. computed = iteratee ? iteratee(value, i, array) : value;
  5474. if (isSorted) {
  5475. if (!i || seen !== computed) result.push(value);
  5476. seen = computed;
  5477. } else if (iteratee) {
  5478. if (!_.contains(seen, computed)) {
  5479. seen.push(computed);
  5480. result.push(value);
  5481. }
  5482. } else if (!_.contains(result, value)) {
  5483. result.push(value);
  5484. }
  5485. }
  5486. return result;
  5487. };
  5488. // Produce an array that contains the union: each distinct element from all of
  5489. // the passed-in arrays.
  5490. _.union = function() {
  5491. return _.uniq(flatten(arguments, true, true));
  5492. };
  5493. // Produce an array that contains every item shared between all the
  5494. // passed-in arrays.
  5495. _.intersection = function(array) {
  5496. if (array == null) return [];
  5497. var result = [];
  5498. var argsLength = arguments.length;
  5499. for (var i = 0, length = array.length; i < length; i++) {
  5500. var item = array[i];
  5501. if (_.contains(result, item)) continue;
  5502. for (var j = 1; j < argsLength; j++) {
  5503. if (!_.contains(arguments[j], item)) break;
  5504. }
  5505. if (j === argsLength) result.push(item);
  5506. }
  5507. return result;
  5508. };
  5509. // Take the difference between one array and a number of other arrays.
  5510. // Only the elements present in just the first array will remain.
  5511. _.difference = function(array) {
  5512. var rest = flatten(arguments, true, true, 1);
  5513. return _.filter(array, function(value){
  5514. return !_.contains(rest, value);
  5515. });
  5516. };
  5517. // Zip together multiple lists into a single array -- elements that share
  5518. // an index go together.
  5519. _.zip = function() {
  5520. return _.unzip(arguments);
  5521. };
  5522. // Complement of _.zip. Unzip accepts an array of arrays and groups
  5523. // each array's elements on shared indices
  5524. _.unzip = function(array) {
  5525. var length = array && _.max(array, 'length').length || 0;
  5526. var result = Array(length);
  5527. for (var index = 0; index < length; index++) {
  5528. result[index] = _.pluck(array, index);
  5529. }
  5530. return result;
  5531. };
  5532. // Converts lists into objects. Pass either a single array of `[key, value]`
  5533. // pairs, or two parallel arrays of the same length -- one of keys, and one of
  5534. // the corresponding values.
  5535. _.object = function(list, values) {
  5536. var result = {};
  5537. for (var i = 0, length = list && list.length; i < length; i++) {
  5538. if (values) {
  5539. result[list[i]] = values[i];
  5540. } else {
  5541. result[list[i][0]] = list[i][1];
  5542. }
  5543. }
  5544. return result;
  5545. };
  5546. // Return the position of the first occurrence of an item in an array,
  5547. // or -1 if the item is not included in the array.
  5548. // If the array is large and already in sort order, pass `true`
  5549. // for **isSorted** to use binary search.
  5550. _.indexOf = function(array, item, isSorted) {
  5551. var i = 0, length = array && array.length;
  5552. if (typeof isSorted == 'number') {
  5553. i = isSorted < 0 ? Math.max(0, length + isSorted) : isSorted;
  5554. } else if (isSorted && length) {
  5555. i = _.sortedIndex(array, item);
  5556. return array[i] === item ? i : -1;
  5557. }
  5558. if (item !== item) {
  5559. return _.findIndex(slice.call(array, i), _.isNaN);
  5560. }
  5561. for (; i < length; i++) if (array[i] === item) return i;
  5562. return -1;
  5563. };
  5564. _.lastIndexOf = function(array, item, from) {
  5565. var idx = array ? array.length : 0;
  5566. if (typeof from == 'number') {
  5567. idx = from < 0 ? idx + from + 1 : Math.min(idx, from + 1);
  5568. }
  5569. if (item !== item) {
  5570. return _.findLastIndex(slice.call(array, 0, idx), _.isNaN);
  5571. }
  5572. while (--idx >= 0) if (array[idx] === item) return idx;
  5573. return -1;
  5574. };
  5575. // Generator function to create the findIndex and findLastIndex functions
  5576. function createIndexFinder(dir) {
  5577. return function(array, predicate, context) {
  5578. predicate = cb(predicate, context);
  5579. var length = array != null && array.length;
  5580. var index = dir > 0 ? 0 : length - 1;
  5581. for (; index >= 0 && index < length; index += dir) {
  5582. if (predicate(array[index], index, array)) return index;
  5583. }
  5584. return -1;
  5585. };
  5586. }
  5587. // Returns the first index on an array-like that passes a predicate test
  5588. _.findIndex = createIndexFinder(1);
  5589. _.findLastIndex = createIndexFinder(-1);
  5590. // Use a comparator function to figure out the smallest index at which
  5591. // an object should be inserted so as to maintain order. Uses binary search.
  5592. _.sortedIndex = function(array, obj, iteratee, context) {
  5593. iteratee = cb(iteratee, context, 1);
  5594. var value = iteratee(obj);
  5595. var low = 0, high = array.length;
  5596. while (low < high) {
  5597. var mid = Math.floor((low + high) / 2);
  5598. if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
  5599. }
  5600. return low;
  5601. };
  5602. // Generate an integer Array containing an arithmetic progression. A port of
  5603. // the native Python `range()` function. See
  5604. // [the Python documentation](http://docs.python.org/library/functions.html#range).
  5605. _.range = function(start, stop, step) {
  5606. if (arguments.length <= 1) {
  5607. stop = start || 0;
  5608. start = 0;
  5609. }
  5610. step = step || 1;
  5611. var length = Math.max(Math.ceil((stop - start) / step), 0);
  5612. var range = Array(length);
  5613. for (var idx = 0; idx < length; idx++, start += step) {
  5614. range[idx] = start;
  5615. }
  5616. return range;
  5617. };
  5618. // Function (ahem) Functions
  5619. // ------------------
  5620. // Determines whether to execute a function as a constructor
  5621. // or a normal function with the provided arguments
  5622. var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) {
  5623. if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);
  5624. var self = baseCreate(sourceFunc.prototype);
  5625. var result = sourceFunc.apply(self, args);
  5626. if (_.isObject(result)) return result;
  5627. return self;
  5628. };
  5629. // Create a function bound to a given object (assigning `this`, and arguments,
  5630. // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
  5631. // available.
  5632. _.bind = function(func, context) {
  5633. if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
  5634. if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function');
  5635. var args = slice.call(arguments, 2);
  5636. var bound = function() {
  5637. return executeBound(func, bound, context, this, args.concat(slice.call(arguments)));
  5638. };
  5639. return bound;
  5640. };
  5641. // Partially apply a function by creating a version that has had some of its
  5642. // arguments pre-filled, without changing its dynamic `this` context. _ acts
  5643. // as a placeholder, allowing any combination of arguments to be pre-filled.
  5644. _.partial = function(func) {
  5645. var boundArgs = slice.call(arguments, 1);
  5646. var bound = function() {
  5647. var position = 0, length = boundArgs.length;
  5648. var args = Array(length);
  5649. for (var i = 0; i < length; i++) {
  5650. args[i] = boundArgs[i] === _ ? arguments[position++] : boundArgs[i];
  5651. }
  5652. while (position < arguments.length) args.push(arguments[position++]);
  5653. return executeBound(func, bound, this, this, args);
  5654. };
  5655. return bound;
  5656. };
  5657. // Bind a number of an object's methods to that object. Remaining arguments
  5658. // are the method names to be bound. Useful for ensuring that all callbacks
  5659. // defined on an object belong to it.
  5660. _.bindAll = function(obj) {
  5661. var i, length = arguments.length, key;
  5662. if (length <= 1) throw new Error('bindAll must be passed function names');
  5663. for (i = 1; i < length; i++) {
  5664. key = arguments[i];
  5665. obj[key] = _.bind(obj[key], obj);
  5666. }
  5667. return obj;
  5668. };
  5669. // Memoize an expensive function by storing its results.
  5670. _.memoize = function(func, hasher) {
  5671. var memoize = function(key) {
  5672. var cache = memoize.cache;
  5673. var address = '' + (hasher ? hasher.apply(this, arguments) : key);
  5674. if (!_.has(cache, address)) cache[address] = func.apply(this, arguments);
  5675. return cache[address];
  5676. };
  5677. memoize.cache = {};
  5678. return memoize;
  5679. };
  5680. // Delays a function for the given number of milliseconds, and then calls
  5681. // it with the arguments supplied.
  5682. _.delay = function(func, wait) {
  5683. var args = slice.call(arguments, 2);
  5684. return setTimeout(function(){
  5685. return func.apply(null, args);
  5686. }, wait);
  5687. };
  5688. // Defers a function, scheduling it to run after the current call stack has
  5689. // cleared.
  5690. _.defer = _.partial(_.delay, _, 1);
  5691. // Returns a function, that, when invoked, will only be triggered at most once
  5692. // during a given window of time. Normally, the throttled function will run
  5693. // as much as it can, without ever going more than once per `wait` duration;
  5694. // but if you'd like to disable the execution on the leading edge, pass
  5695. // `{leading: false}`. To disable execution on the trailing edge, ditto.
  5696. _.throttle = function(func, wait, options) {
  5697. var context, args, result;
  5698. var timeout = null;
  5699. var previous = 0;
  5700. if (!options) options = {};
  5701. var later = function() {
  5702. previous = options.leading === false ? 0 : _.now();
  5703. timeout = null;
  5704. result = func.apply(context, args);
  5705. if (!timeout) context = args = null;
  5706. };
  5707. return function() {
  5708. var now = _.now();
  5709. if (!previous && options.leading === false) previous = now;
  5710. var remaining = wait - (now - previous);
  5711. context = this;
  5712. args = arguments;
  5713. if (remaining <= 0 || remaining > wait) {
  5714. if (timeout) {
  5715. clearTimeout(timeout);
  5716. timeout = null;
  5717. }
  5718. previous = now;
  5719. result = func.apply(context, args);
  5720. if (!timeout) context = args = null;
  5721. } else if (!timeout && options.trailing !== false) {
  5722. timeout = setTimeout(later, remaining);
  5723. }
  5724. return result;
  5725. };
  5726. };
  5727. // Returns a function, that, as long as it continues to be invoked, will not
  5728. // be triggered. The function will be called after it stops being called for
  5729. // N milliseconds. If `immediate` is passed, trigger the function on the
  5730. // leading edge, instead of the trailing.
  5731. _.debounce = function(func, wait, immediate) {
  5732. var timeout, args, context, timestamp, result;
  5733. var later = function() {
  5734. var last = _.now() - timestamp;
  5735. if (last < wait && last >= 0) {
  5736. timeout = setTimeout(later, wait - last);
  5737. } else {
  5738. timeout = null;
  5739. if (!immediate) {
  5740. result = func.apply(context, args);
  5741. if (!timeout) context = args = null;
  5742. }
  5743. }
  5744. };
  5745. return function() {
  5746. context = this;
  5747. args = arguments;
  5748. timestamp = _.now();
  5749. var callNow = immediate && !timeout;
  5750. if (!timeout) timeout = setTimeout(later, wait);
  5751. if (callNow) {
  5752. result = func.apply(context, args);
  5753. context = args = null;
  5754. }
  5755. return result;
  5756. };
  5757. };
  5758. // Returns the first function passed as an argument to the second,
  5759. // allowing you to adjust arguments, run code before and after, and
  5760. // conditionally execute the original function.
  5761. _.wrap = function(func, wrapper) {
  5762. return _.partial(wrapper, func);
  5763. };
  5764. // Returns a negated version of the passed-in predicate.
  5765. _.negate = function(predicate) {
  5766. return function() {
  5767. return !predicate.apply(this, arguments);
  5768. };
  5769. };
  5770. // Returns a function that is the composition of a list of functions, each
  5771. // consuming the return value of the function that follows.
  5772. _.compose = function() {
  5773. var args = arguments;
  5774. var start = args.length - 1;
  5775. return function() {
  5776. var i = start;
  5777. var result = args[start].apply(this, arguments);
  5778. while (i--) result = args[i].call(this, result);
  5779. return result;
  5780. };
  5781. };
  5782. // Returns a function that will only be executed on and after the Nth call.
  5783. _.after = function(times, func) {
  5784. return function() {
  5785. if (--times < 1) {
  5786. return func.apply(this, arguments);
  5787. }
  5788. };
  5789. };
  5790. // Returns a function that will only be executed up to (but not including) the Nth call.
  5791. _.before = function(times, func) {
  5792. var memo;
  5793. return function() {
  5794. if (--times > 0) {
  5795. memo = func.apply(this, arguments);
  5796. }
  5797. if (times <= 1) func = null;
  5798. return memo;
  5799. };
  5800. };
  5801. // Returns a function that will be executed at most one time, no matter how
  5802. // often you call it. Useful for lazy initialization.
  5803. _.once = _.partial(_.before, 2);
  5804. // Object Functions
  5805. // ----------------
  5806. // Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed.
  5807. var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString');
  5808. var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString',
  5809. 'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];
  5810. function collectNonEnumProps(obj, keys) {
  5811. var nonEnumIdx = nonEnumerableProps.length;
  5812. var constructor = obj.constructor;
  5813. var proto = (_.isFunction(constructor) && constructor.prototype) || ObjProto;
  5814. // Constructor is a special case.
  5815. var prop = 'constructor';
  5816. if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop);
  5817. while (nonEnumIdx--) {
  5818. prop = nonEnumerableProps[nonEnumIdx];
  5819. if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) {
  5820. keys.push(prop);
  5821. }
  5822. }
  5823. }
  5824. // Retrieve the names of an object's own properties.
  5825. // Delegates to **ECMAScript 5**'s native `Object.keys`
  5826. _.keys = function(obj) {
  5827. if (!_.isObject(obj)) return [];
  5828. if (nativeKeys) return nativeKeys(obj);
  5829. var keys = [];
  5830. for (var key in obj) if (_.has(obj, key)) keys.push(key);
  5831. // Ahem, IE < 9.
  5832. if (hasEnumBug) collectNonEnumProps(obj, keys);
  5833. return keys;
  5834. };
  5835. // Retrieve all the property names of an object.
  5836. _.allKeys = function(obj) {
  5837. if (!_.isObject(obj)) return [];
  5838. var keys = [];
  5839. for (var key in obj) keys.push(key);
  5840. // Ahem, IE < 9.
  5841. if (hasEnumBug) collectNonEnumProps(obj, keys);
  5842. return keys;
  5843. };
  5844. // Retrieve the values of an object's properties.
  5845. _.values = function(obj) {
  5846. var keys = _.keys(obj);
  5847. var length = keys.length;
  5848. var values = Array(length);
  5849. for (var i = 0; i < length; i++) {
  5850. values[i] = obj[keys[i]];
  5851. }
  5852. return values;
  5853. };
  5854. // Returns the results of applying the iteratee to each element of the object
  5855. // In contrast to _.map it returns an object
  5856. _.mapObject = function(obj, iteratee, context) {
  5857. iteratee = cb(iteratee, context);
  5858. var keys = _.keys(obj),
  5859. length = keys.length,
  5860. results = {},
  5861. currentKey;
  5862. for (var index = 0; index < length; index++) {
  5863. currentKey = keys[index];
  5864. results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
  5865. }
  5866. return results;
  5867. };
  5868. // Convert an object into a list of `[key, value]` pairs.
  5869. _.pairs = function(obj) {
  5870. var keys = _.keys(obj);
  5871. var length = keys.length;
  5872. var pairs = Array(length);
  5873. for (var i = 0; i < length; i++) {
  5874. pairs[i] = [keys[i], obj[keys[i]]];
  5875. }
  5876. return pairs;
  5877. };
  5878. // Invert the keys and values of an object. The values must be serializable.
  5879. _.invert = function(obj) {
  5880. var result = {};
  5881. var keys = _.keys(obj);
  5882. for (var i = 0, length = keys.length; i < length; i++) {
  5883. result[obj[keys[i]]] = keys[i];
  5884. }
  5885. return result;
  5886. };
  5887. // Return a sorted list of the function names available on the object.
  5888. // Aliased as `methods`
  5889. _.functions = _.methods = function(obj) {
  5890. var names = [];
  5891. for (var key in obj) {
  5892. if (_.isFunction(obj[key])) names.push(key);
  5893. }
  5894. return names.sort();
  5895. };
  5896. // Extend a given object with all the properties in passed-in object(s).
  5897. _.extend = createAssigner(_.allKeys);
  5898. // Assigns a given object with all the own properties in the passed-in object(s)
  5899. // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
  5900. _.extendOwn = _.assign = createAssigner(_.keys);
  5901. // Returns the first key on an object that passes a predicate test
  5902. _.findKey = function(obj, predicate, context) {
  5903. predicate = cb(predicate, context);
  5904. var keys = _.keys(obj), key;
  5905. for (var i = 0, length = keys.length; i < length; i++) {
  5906. key = keys[i];
  5907. if (predicate(obj[key], key, obj)) return key;
  5908. }
  5909. };
  5910. // Return a copy of the object only containing the whitelisted properties.
  5911. _.pick = function(object, oiteratee, context) {
  5912. var result = {}, obj = object, iteratee, keys;
  5913. if (obj == null) return result;
  5914. if (_.isFunction(oiteratee)) {
  5915. keys = _.allKeys(obj);
  5916. iteratee = optimizeCb(oiteratee, context);
  5917. } else {
  5918. keys = flatten(arguments, false, false, 1);
  5919. iteratee = function(value, key, obj) { return key in obj; };
  5920. obj = Object(obj);
  5921. }
  5922. for (var i = 0, length = keys.length; i < length; i++) {
  5923. var key = keys[i];
  5924. var value = obj[key];
  5925. if (iteratee(value, key, obj)) result[key] = value;
  5926. }
  5927. return result;
  5928. };
  5929. // Return a copy of the object without the blacklisted properties.
  5930. _.omit = function(obj, iteratee, context) {
  5931. if (_.isFunction(iteratee)) {
  5932. iteratee = _.negate(iteratee);
  5933. } else {
  5934. var keys = _.map(flatten(arguments, false, false, 1), String);
  5935. iteratee = function(value, key) {
  5936. return !_.contains(keys, key);
  5937. };
  5938. }
  5939. return _.pick(obj, iteratee, context);
  5940. };
  5941. // Fill in a given object with default properties.
  5942. _.defaults = createAssigner(_.allKeys, true);
  5943. // Create a (shallow-cloned) duplicate of an object.
  5944. _.clone = function(obj) {
  5945. if (!_.isObject(obj)) return obj;
  5946. return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
  5947. };
  5948. // Invokes interceptor with the obj, and then returns obj.
  5949. // The primary purpose of this method is to "tap into" a method chain, in
  5950. // order to perform operations on intermediate results within the chain.
  5951. _.tap = function(obj, interceptor) {
  5952. interceptor(obj);
  5953. return obj;
  5954. };
  5955. // Returns whether an object has a given set of `key:value` pairs.
  5956. _.isMatch = function(object, attrs) {
  5957. var keys = _.keys(attrs), length = keys.length;
  5958. if (object == null) return !length;
  5959. var obj = Object(object);
  5960. for (var i = 0; i < length; i++) {
  5961. var key = keys[i];
  5962. if (attrs[key] !== obj[key] || !(key in obj)) return false;
  5963. }
  5964. return true;
  5965. };
  5966. // Internal recursive comparison function for `isEqual`.
  5967. var eq = function(a, b, aStack, bStack) {
  5968. // Identical objects are equal. `0 === -0`, but they aren't identical.
  5969. // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
  5970. if (a === b) return a !== 0 || 1 / a === 1 / b;
  5971. // A strict comparison is necessary because `null == undefined`.
  5972. if (a == null || b == null) return a === b;
  5973. // Unwrap any wrapped objects.
  5974. if (a instanceof _) a = a._wrapped;
  5975. if (b instanceof _) b = b._wrapped;
  5976. // Compare `[[Class]]` names.
  5977. var className = toString.call(a);
  5978. if (className !== toString.call(b)) return false;
  5979. switch (className) {
  5980. // Strings, numbers, regular expressions, dates, and booleans are compared by value.
  5981. case '[object RegExp]':
  5982. // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
  5983. case '[object String]':
  5984. // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
  5985. // equivalent to `new String("5")`.
  5986. return '' + a === '' + b;
  5987. case '[object Number]':
  5988. // `NaN`s are equivalent, but non-reflexive.
  5989. // Object(NaN) is equivalent to NaN
  5990. if (+a !== +a) return +b !== +b;
  5991. // An `egal` comparison is performed for other numeric values.
  5992. return +a === 0 ? 1 / +a === 1 / b : +a === +b;
  5993. case '[object Date]':
  5994. case '[object Boolean]':
  5995. // Coerce dates and booleans to numeric primitive values. Dates are compared by their
  5996. // millisecond representations. Note that invalid dates with millisecond representations
  5997. // of `NaN` are not equivalent.
  5998. return +a === +b;
  5999. }
  6000. var areArrays = className === '[object Array]';
  6001. if (!areArrays) {
  6002. if (typeof a != 'object' || typeof b != 'object') return false;
  6003. // Objects with different constructors are not equivalent, but `Object`s or `Array`s
  6004. // from different frames are.
  6005. var aCtor = a.constructor, bCtor = b.constructor;
  6006. if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor &&
  6007. _.isFunction(bCtor) && bCtor instanceof bCtor)
  6008. && ('constructor' in a && 'constructor' in b)) {
  6009. return false;
  6010. }
  6011. }
  6012. // Assume equality for cyclic structures. The algorithm for detecting cyclic
  6013. // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
  6014. // Initializing stack of traversed objects.
  6015. // It's done here since we only need them for objects and arrays comparison.
  6016. aStack = aStack || [];
  6017. bStack = bStack || [];
  6018. var length = aStack.length;
  6019. while (length--) {
  6020. // Linear search. Performance is inversely proportional to the number of
  6021. // unique nested structures.
  6022. if (aStack[length] === a) return bStack[length] === b;
  6023. }
  6024. // Add the first object to the stack of traversed objects.
  6025. aStack.push(a);
  6026. bStack.push(b);
  6027. // Recursively compare objects and arrays.
  6028. if (areArrays) {
  6029. // Compare array lengths to determine if a deep comparison is necessary.
  6030. length = a.length;
  6031. if (length !== b.length) return false;
  6032. // Deep compare the contents, ignoring non-numeric properties.
  6033. while (length--) {
  6034. if (!eq(a[length], b[length], aStack, bStack)) return false;
  6035. }
  6036. } else {
  6037. // Deep compare objects.
  6038. var keys = _.keys(a), key;
  6039. length = keys.length;
  6040. // Ensure that both objects contain the same number of properties before comparing deep equality.
  6041. if (_.keys(b).length !== length) return false;
  6042. while (length--) {
  6043. // Deep compare each member
  6044. key = keys[length];
  6045. if (!(_.has(b, key) && eq(a[key], b[key], aStack, bStack))) return false;
  6046. }
  6047. }
  6048. // Remove the first object from the stack of traversed objects.
  6049. aStack.pop();
  6050. bStack.pop();
  6051. return true;
  6052. };
  6053. // Perform a deep comparison to check if two objects are equal.
  6054. _.isEqual = function(a, b) {
  6055. return eq(a, b);
  6056. };
  6057. // Is a given array, string, or object empty?
  6058. // An "empty" object has no enumerable own-properties.
  6059. _.isEmpty = function(obj) {
  6060. if (obj == null) return true;
  6061. if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0;
  6062. return _.keys(obj).length === 0;
  6063. };
  6064. // Is a given value a DOM element?
  6065. _.isElement = function(obj) {
  6066. return !!(obj && obj.nodeType === 1);
  6067. };
  6068. // Is a given value an array?
  6069. // Delegates to ECMA5's native Array.isArray
  6070. _.isArray = nativeIsArray || function(obj) {
  6071. return toString.call(obj) === '[object Array]';
  6072. };
  6073. // Is a given variable an object?
  6074. _.isObject = function(obj) {
  6075. var type = typeof obj;
  6076. return type === 'function' || type === 'object' && !!obj;
  6077. };
  6078. // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError.
  6079. _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) {
  6080. _['is' + name] = function(obj) {
  6081. return toString.call(obj) === '[object ' + name + ']';
  6082. };
  6083. });
  6084. // Define a fallback version of the method in browsers (ahem, IE < 9), where
  6085. // there isn't any inspectable "Arguments" type.
  6086. if (!_.isArguments(arguments)) {
  6087. _.isArguments = function(obj) {
  6088. return _.has(obj, 'callee');
  6089. };
  6090. }
  6091. // Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8,
  6092. // IE 11 (#1621), and in Safari 8 (#1929).
  6093. if (typeof /./ != 'function' && typeof Int8Array != 'object') {
  6094. _.isFunction = function(obj) {
  6095. return typeof obj == 'function' || false;
  6096. };
  6097. }
  6098. // Is a given object a finite number?
  6099. _.isFinite = function(obj) {
  6100. return isFinite(obj) && !isNaN(parseFloat(obj));
  6101. };
  6102. // Is the given value `NaN`? (NaN is the only number which does not equal itself).
  6103. _.isNaN = function(obj) {
  6104. return _.isNumber(obj) && obj !== +obj;
  6105. };
  6106. // Is a given value a boolean?
  6107. _.isBoolean = function(obj) {
  6108. return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
  6109. };
  6110. // Is a given value equal to null?
  6111. _.isNull = function(obj) {
  6112. return obj === null;
  6113. };
  6114. // Is a given variable undefined?
  6115. _.isUndefined = function(obj) {
  6116. return obj === void 0;
  6117. };
  6118. // Shortcut function for checking if an object has a given property directly
  6119. // on itself (in other words, not on a prototype).
  6120. _.has = function(obj, key) {
  6121. return obj != null && hasOwnProperty.call(obj, key);
  6122. };
  6123. // Utility Functions
  6124. // -----------------
  6125. // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
  6126. // previous owner. Returns a reference to the Underscore object.
  6127. _.noConflict = function() {
  6128. root._ = previousUnderscore;
  6129. return this;
  6130. };
  6131. // Keep the identity function around for default iteratees.
  6132. _.identity = function(value) {
  6133. return value;
  6134. };
  6135. // Predicate-generating functions. Often useful outside of Underscore.
  6136. _.constant = function(value) {
  6137. return function() {
  6138. return value;
  6139. };
  6140. };
  6141. _.noop = function(){};
  6142. _.property = function(key) {
  6143. return function(obj) {
  6144. return obj == null ? void 0 : obj[key];
  6145. };
  6146. };
  6147. // Generates a function for a given object that returns a given property.
  6148. _.propertyOf = function(obj) {
  6149. return obj == null ? function(){} : function(key) {
  6150. return obj[key];
  6151. };
  6152. };
  6153. // Returns a predicate for checking whether an object has a given set of
  6154. // `key:value` pairs.
  6155. _.matcher = _.matches = function(attrs) {
  6156. attrs = _.extendOwn({}, attrs);
  6157. return function(obj) {
  6158. return _.isMatch(obj, attrs);
  6159. };
  6160. };
  6161. // Run a function **n** times.
  6162. _.times = function(n, iteratee, context) {
  6163. var accum = Array(Math.max(0, n));
  6164. iteratee = optimizeCb(iteratee, context, 1);
  6165. for (var i = 0; i < n; i++) accum[i] = iteratee(i);
  6166. return accum;
  6167. };
  6168. // Return a random integer between min and max (inclusive).
  6169. _.random = function(min, max) {
  6170. if (max == null) {
  6171. max = min;
  6172. min = 0;
  6173. }
  6174. return min + Math.floor(Math.random() * (max - min + 1));
  6175. };
  6176. // A (possibly faster) way to get the current timestamp as an integer.
  6177. _.now = Date.now || function() {
  6178. return new Date().getTime();
  6179. };
  6180. // List of HTML entities for escaping.
  6181. var escapeMap = {
  6182. '&': '&amp;',
  6183. '<': '&lt;',
  6184. '>': '&gt;',
  6185. '"': '&quot;',
  6186. "'": '&#x27;',
  6187. '`': '&#x60;'
  6188. };
  6189. var unescapeMap = _.invert(escapeMap);
  6190. // Functions for escaping and unescaping strings to/from HTML interpolation.
  6191. var createEscaper = function(map) {
  6192. var escaper = function(match) {
  6193. return map[match];
  6194. };
  6195. // Regexes for identifying a key that needs to be escaped
  6196. var source = '(?:' + _.keys(map).join('|') + ')';
  6197. var testRegexp = RegExp(source);
  6198. var replaceRegexp = RegExp(source, 'g');
  6199. return function(string) {
  6200. string = string == null ? '' : '' + string;
  6201. return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
  6202. };
  6203. };
  6204. _.escape = createEscaper(escapeMap);
  6205. _.unescape = createEscaper(unescapeMap);
  6206. // If the value of the named `property` is a function then invoke it with the
  6207. // `object` as context; otherwise, return it.
  6208. _.result = function(object, property, fallback) {
  6209. var value = object == null ? void 0 : object[property];
  6210. if (value === void 0) {
  6211. value = fallback;
  6212. }
  6213. return _.isFunction(value) ? value.call(object) : value;
  6214. };
  6215. // Generate a unique integer id (unique within the entire client session).
  6216. // Useful for temporary DOM ids.
  6217. var idCounter = 0;
  6218. _.uniqueId = function(prefix) {
  6219. var id = ++idCounter + '';
  6220. return prefix ? prefix + id : id;
  6221. };
  6222. // By default, Underscore uses ERB-style template delimiters, change the
  6223. // following template settings to use alternative delimiters.
  6224. _.templateSettings = {
  6225. evaluate : /<%([\s\S]+?)%>/g,
  6226. interpolate : /<%=([\s\S]+?)%>/g,
  6227. escape : /<%-([\s\S]+?)%>/g
  6228. };
  6229. // When customizing `templateSettings`, if you don't want to define an
  6230. // interpolation, evaluation or escaping regex, we need one that is
  6231. // guaranteed not to match.
  6232. var noMatch = /(.)^/;
  6233. // Certain characters need to be escaped so that they can be put into a
  6234. // string literal.
  6235. var escapes = {
  6236. "'": "'",
  6237. '\\': '\\',
  6238. '\r': 'r',
  6239. '\n': 'n',
  6240. '\u2028': 'u2028',
  6241. '\u2029': 'u2029'
  6242. };
  6243. var escaper = /\\|'|\r|\n|\u2028|\u2029/g;
  6244. var escapeChar = function(match) {
  6245. return '\\' + escapes[match];
  6246. };
  6247. // JavaScript micro-templating, similar to John Resig's implementation.
  6248. // Underscore templating handles arbitrary delimiters, preserves whitespace,
  6249. // and correctly escapes quotes within interpolated code.
  6250. // NB: `oldSettings` only exists for backwards compatibility.
  6251. _.template = function(text, settings, oldSettings) {
  6252. if (!settings && oldSettings) settings = oldSettings;
  6253. settings = _.defaults({}, settings, _.templateSettings);
  6254. // Combine delimiters into one regular expression via alternation.
  6255. var matcher = RegExp([
  6256. (settings.escape || noMatch).source,
  6257. (settings.interpolate || noMatch).source,
  6258. (settings.evaluate || noMatch).source
  6259. ].join('|') + '|$', 'g');
  6260. // Compile the template source, escaping string literals appropriately.
  6261. var index = 0;
  6262. var source = "__p+='";
  6263. text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
  6264. source += text.slice(index, offset).replace(escaper, escapeChar);
  6265. index = offset + match.length;
  6266. if (escape) {
  6267. source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
  6268. } else if (interpolate) {
  6269. source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
  6270. } else if (evaluate) {
  6271. source += "';\n" + evaluate + "\n__p+='";
  6272. }
  6273. // Adobe VMs need the match returned to produce the correct offest.
  6274. return match;
  6275. });
  6276. source += "';\n";
  6277. // If a variable is not specified, place data values in local scope.
  6278. if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
  6279. source = "var __t,__p='',__j=Array.prototype.join," +
  6280. "print=function(){__p+=__j.call(arguments,'');};\n" +
  6281. source + 'return __p;\n';
  6282. try {
  6283. var render = new Function(settings.variable || 'obj', '_', source);
  6284. } catch (e) {
  6285. e.source = source;
  6286. throw e;
  6287. }
  6288. var template = function(data) {
  6289. return render.call(this, data, _);
  6290. };
  6291. // Provide the compiled source as a convenience for precompilation.
  6292. var argument = settings.variable || 'obj';
  6293. template.source = 'function(' + argument + '){\n' + source + '}';
  6294. return template;
  6295. };
  6296. // Add a "chain" function. Start chaining a wrapped Underscore object.
  6297. _.chain = function(obj) {
  6298. var instance = _(obj);
  6299. instance._chain = true;
  6300. return instance;
  6301. };
  6302. // OOP
  6303. // ---------------
  6304. // If Underscore is called as a function, it returns a wrapped object that
  6305. // can be used OO-style. This wrapper holds altered versions of all the
  6306. // underscore functions. Wrapped objects may be chained.
  6307. // Helper function to continue chaining intermediate results.
  6308. var result = function(instance, obj) {
  6309. return instance._chain ? _(obj).chain() : obj;
  6310. };
  6311. // Add your own custom functions to the Underscore object.
  6312. _.mixin = function(obj) {
  6313. _.each(_.functions(obj), function(name) {
  6314. var func = _[name] = obj[name];
  6315. _.prototype[name] = function() {
  6316. var args = [this._wrapped];
  6317. push.apply(args, arguments);
  6318. return result(this, func.apply(_, args));
  6319. };
  6320. });
  6321. };
  6322. // Add all of the Underscore functions to the wrapper object.
  6323. _.mixin(_);
  6324. // Add all mutator Array functions to the wrapper.
  6325. _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
  6326. var method = ArrayProto[name];
  6327. _.prototype[name] = function() {
  6328. var obj = this._wrapped;
  6329. method.apply(obj, arguments);
  6330. if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
  6331. return result(this, obj);
  6332. };
  6333. });
  6334. // Add all accessor Array functions to the wrapper.
  6335. _.each(['concat', 'join', 'slice'], function(name) {
  6336. var method = ArrayProto[name];
  6337. _.prototype[name] = function() {
  6338. return result(this, method.apply(this._wrapped, arguments));
  6339. };
  6340. });
  6341. // Extracts the result from a wrapped and chained object.
  6342. _.prototype.value = function() {
  6343. return this._wrapped;
  6344. };
  6345. // Provide unwrapping proxy for some methods used in engine operations
  6346. // such as arithmetic and JSON stringification.
  6347. _.prototype.valueOf = _.prototype.toJSON = _.prototype.value;
  6348. _.prototype.toString = function() {
  6349. return '' + this._wrapped;
  6350. };
  6351. // AMD registration happens at the end for compatibility with AMD loaders
  6352. // that may not enforce next-turn semantics on modules. Even though general
  6353. // practice for AMD registration is to be anonymous, underscore registers
  6354. // as a named module because, like jQuery, it is a base library that is
  6355. // popular enough to be bundled in a third party lib, but not be part of
  6356. // an AMD load request. Those cases could generate an error when an
  6357. // anonymous define() is called outside of a loader request.
  6358. if (typeof define === 'function' && define.amd) {
  6359. define('underscore', [], function() {
  6360. return _;
  6361. });
  6362. }
  6363. }.call(this));
  6364. },{}],21:[function(require,module,exports){
  6365. var toSDP = require('./lib/tosdp');
  6366. var toJSON = require('./lib/tojson');
  6367. // Converstion from JSON to SDP
  6368. exports.toIncomingSDPOffer = function (session) {
  6369. return toSDP.toSessionSDP(session, {
  6370. role: 'responder',
  6371. direction: 'incoming'
  6372. });
  6373. };
  6374. exports.toOutgoingSDPOffer = function (session) {
  6375. return toSDP.toSessionSDP(session, {
  6376. role: 'initiator',
  6377. direction: 'outgoing'
  6378. });
  6379. };
  6380. exports.toIncomingSDPAnswer = function (session) {
  6381. return toSDP.toSessionSDP(session, {
  6382. role: 'initiator',
  6383. direction: 'incoming'
  6384. });
  6385. };
  6386. exports.toOutgoingSDPAnswer = function (session) {
  6387. return toSDP.toSessionSDP(session, {
  6388. role: 'responder',
  6389. direction: 'outgoing'
  6390. });
  6391. };
  6392. exports.toIncomingMediaSDPOffer = function (media) {
  6393. return toSDP.toMediaSDP(media, {
  6394. role: 'responder',
  6395. direction: 'incoming'
  6396. });
  6397. };
  6398. exports.toOutgoingMediaSDPOffer = function (media) {
  6399. return toSDP.toMediaSDP(media, {
  6400. role: 'initiator',
  6401. direction: 'outgoing'
  6402. });
  6403. };
  6404. exports.toIncomingMediaSDPAnswer = function (media) {
  6405. return toSDP.toMediaSDP(media, {
  6406. role: 'initiator',
  6407. direction: 'incoming'
  6408. });
  6409. };
  6410. exports.toOutgoingMediaSDPAnswer = function (media) {
  6411. return toSDP.toMediaSDP(media, {
  6412. role: 'responder',
  6413. direction: 'outgoing'
  6414. });
  6415. };
  6416. exports.toCandidateSDP = toSDP.toCandidateSDP;
  6417. exports.toMediaSDP = toSDP.toMediaSDP;
  6418. exports.toSessionSDP = toSDP.toSessionSDP;
  6419. // Conversion from SDP to JSON
  6420. exports.toIncomingJSONOffer = function (sdp, creators) {
  6421. return toJSON.toSessionJSON(sdp, {
  6422. role: 'responder',
  6423. direction: 'incoming',
  6424. creators: creators
  6425. });
  6426. };
  6427. exports.toOutgoingJSONOffer = function (sdp, creators) {
  6428. return toJSON.toSessionJSON(sdp, {
  6429. role: 'initiator',
  6430. direction: 'outgoing',
  6431. creators: creators
  6432. });
  6433. };
  6434. exports.toIncomingJSONAnswer = function (sdp, creators) {
  6435. return toJSON.toSessionJSON(sdp, {
  6436. role: 'initiator',
  6437. direction: 'incoming',
  6438. creators: creators
  6439. });
  6440. };
  6441. exports.toOutgoingJSONAnswer = function (sdp, creators) {
  6442. return toJSON.toSessionJSON(sdp, {
  6443. role: 'responder',
  6444. direction: 'outgoing',
  6445. creators: creators
  6446. });
  6447. };
  6448. exports.toIncomingMediaJSONOffer = function (sdp, creator) {
  6449. return toJSON.toMediaJSON(sdp, {
  6450. role: 'responder',
  6451. direction: 'incoming',
  6452. creator: creator
  6453. });
  6454. };
  6455. exports.toOutgoingMediaJSONOffer = function (sdp, creator) {
  6456. return toJSON.toMediaJSON(sdp, {
  6457. role: 'initiator',
  6458. direction: 'outgoing',
  6459. creator: creator
  6460. });
  6461. };
  6462. exports.toIncomingMediaJSONAnswer = function (sdp, creator) {
  6463. return toJSON.toMediaJSON(sdp, {
  6464. role: 'initiator',
  6465. direction: 'incoming',
  6466. creator: creator
  6467. });
  6468. };
  6469. exports.toOutgoingMediaJSONAnswer = function (sdp, creator) {
  6470. return toJSON.toMediaJSON(sdp, {
  6471. role: 'responder',
  6472. direction: 'outgoing',
  6473. creator: creator
  6474. });
  6475. };
  6476. exports.toCandidateJSON = toJSON.toCandidateJSON;
  6477. exports.toMediaJSON = toJSON.toMediaJSON;
  6478. exports.toSessionJSON = toJSON.toSessionJSON;
  6479. },{"./lib/tojson":23,"./lib/tosdp":22}],14:[function(require,module,exports){
  6480. var _ = require('underscore');
  6481. var util = require('util');
  6482. var webrtc = require('webrtcsupport');
  6483. var SJJ = require('sdp-jingle-json');
  6484. var WildEmitter = require('wildemitter');
  6485. var peerconn = require('traceablepeerconnection');
  6486. function PeerConnection(config, constraints) {
  6487. var self = this;
  6488. var item;
  6489. WildEmitter.call(this);
  6490. config = config || {};
  6491. config.iceServers = config.iceServers || [];
  6492. // make sure this only gets enabled in Google Chrome
  6493. // EXPERIMENTAL FLAG, might get removed without notice
  6494. this.enableChromeNativeSimulcast = false;
  6495. if (constraints && constraints.optional &&
  6496. webrtc.prefix === 'webkit' &&
  6497. navigator.appVersion.match(/Chromium\//) === null) {
  6498. constraints.optional.forEach(function (constraint, idx) {
  6499. if (constraint.enableChromeNativeSimulcast) {
  6500. self.enableChromeNativeSimulcast = true;
  6501. }
  6502. });
  6503. }
  6504. // EXPERIMENTAL FLAG, might get removed without notice
  6505. this.enableMultiStreamHacks = false;
  6506. if (constraints && constraints.optional) {
  6507. constraints.optional.forEach(function (constraint, idx) {
  6508. if (constraint.enableMultiStreamHacks) {
  6509. self.enableMultiStreamHacks = true;
  6510. }
  6511. });
  6512. }
  6513. // EXPERIMENTAL FLAG, might get removed without notice
  6514. this.restrictBandwidth = 0;
  6515. if (constraints && constraints.optional) {
  6516. constraints.optional.forEach(function (constraint, idx) {
  6517. if (constraint.andyetRestrictBandwidth) {
  6518. self.restrictBandwidth = constraint.andyetRestrictBandwidth;
  6519. }
  6520. });
  6521. }
  6522. // EXPERIMENTAL FLAG, might get removed without notice
  6523. // bundle up ice candidates, only works for jingle mode
  6524. // number > 0 is the delay to wait for additional candidates
  6525. // ~20ms seems good
  6526. this.batchIceCandidates = 0;
  6527. if (constraints && constraints.optional) {
  6528. constraints.optional.forEach(function (constraint, idx) {
  6529. if (constraint.andyetBatchIce) {
  6530. self.batchIceCandidates = constraint.andyetBatchIce;
  6531. }
  6532. });
  6533. }
  6534. this.batchedIceCandidates = [];
  6535. // EXPERIMENTAL FLAG, might get removed without notice
  6536. this.assumeSetLocalSuccess = false;
  6537. if (constraints && constraints.optional) {
  6538. constraints.optional.forEach(function (constraint, idx) {
  6539. if (constraint.andyetAssumeSetLocalSuccess) {
  6540. self.assumeSetLocalSuccess = constraint.andyetAssumeSetLocalSuccess;
  6541. }
  6542. });
  6543. }
  6544. // EXPERIMENTAL FLAG, might get removed without notice
  6545. // working around https://bugzilla.mozilla.org/show_bug.cgi?id=1087551
  6546. // pass in a timeout for this
  6547. if (webrtc.prefix === 'moz') {
  6548. if (constraints && constraints.optional) {
  6549. this.wtFirefox = 0;
  6550. constraints.optional.forEach(function (constraint, idx) {
  6551. if (constraint.andyetFirefoxMakesMeSad) {
  6552. self.wtFirefox = constraint.andyetFirefoxMakesMeSad;
  6553. if (self.wtFirefox > 0) {
  6554. self.firefoxcandidatebuffer = [];
  6555. }
  6556. }
  6557. });
  6558. }
  6559. }
  6560. this.pc = new peerconn(config, constraints);
  6561. this.getLocalStreams = this.pc.getLocalStreams.bind(this.pc);
  6562. this.getRemoteStreams = this.pc.getRemoteStreams.bind(this.pc);
  6563. this.addStream = this.pc.addStream.bind(this.pc);
  6564. this.removeStream = this.pc.removeStream.bind(this.pc);
  6565. // proxy events
  6566. this.pc.on('*', function () {
  6567. self.emit.apply(self, arguments);
  6568. });
  6569. // proxy some events directly
  6570. this.pc.onremovestream = this.emit.bind(this, 'removeStream');
  6571. this.pc.onaddstream = this.emit.bind(this, 'addStream');
  6572. this.pc.onnegotiationneeded = this.emit.bind(this, 'negotiationNeeded');
  6573. this.pc.oniceconnectionstatechange = this.emit.bind(this, 'iceConnectionStateChange');
  6574. this.pc.onsignalingstatechange = this.emit.bind(this, 'signalingStateChange');
  6575. // handle ice candidate and data channel events
  6576. this.pc.onicecandidate = this._onIce.bind(this);
  6577. this.pc.ondatachannel = this._onDataChannel.bind(this);
  6578. this.localDescription = {
  6579. contents: []
  6580. };
  6581. this.remoteDescription = {
  6582. contents: []
  6583. };
  6584. this.config = {
  6585. debug: false,
  6586. ice: {},
  6587. sid: '',
  6588. isInitiator: true,
  6589. sdpSessionID: Date.now(),
  6590. useJingle: false
  6591. };
  6592. // apply our config
  6593. for (item in config) {
  6594. this.config[item] = config[item];
  6595. }
  6596. if (this.config.debug) {
  6597. this.on('*', function (eventName, event) {
  6598. var logger = config.logger || console;
  6599. logger.log('PeerConnection event:', arguments);
  6600. });
  6601. }
  6602. this.hadLocalStunCandidate = false;
  6603. this.hadRemoteStunCandidate = false;
  6604. this.hadLocalRelayCandidate = false;
  6605. this.hadRemoteRelayCandidate = false;
  6606. this.hadLocalIPv6Candidate = false;
  6607. this.hadRemoteIPv6Candidate = false;
  6608. // keeping references for all our data channels
  6609. // so they dont get garbage collected
  6610. // can be removed once the following bugs have been fixed
  6611. // https://crbug.com/405545
  6612. // https://bugzilla.mozilla.org/show_bug.cgi?id=964092
  6613. // to be filed for opera
  6614. this._remoteDataChannels = [];
  6615. this._localDataChannels = [];
  6616. }
  6617. util.inherits(PeerConnection, WildEmitter);
  6618. Object.defineProperty(PeerConnection.prototype, 'signalingState', {
  6619. get: function () {
  6620. return this.pc.signalingState;
  6621. }
  6622. });
  6623. Object.defineProperty(PeerConnection.prototype, 'iceConnectionState', {
  6624. get: function () {
  6625. return this.pc.iceConnectionState;
  6626. }
  6627. });
  6628. PeerConnection.prototype._role = function () {
  6629. return this.isInitiator ? 'initiator' : 'responder';
  6630. };
  6631. // Add a stream to the peer connection object
  6632. PeerConnection.prototype.addStream = function (stream) {
  6633. this.localStream = stream;
  6634. this.pc.addStream(stream);
  6635. };
  6636. // helper function to check if a remote candidate is a stun/relay
  6637. // candidate or an ipv6 candidate
  6638. PeerConnection.prototype._checkLocalCandidate = function (candidate) {
  6639. var cand = SJJ.toCandidateJSON(candidate);
  6640. if (cand.type == 'srflx') {
  6641. this.hadLocalStunCandidate = true;
  6642. } else if (cand.type == 'relay') {
  6643. this.hadLocalRelayCandidate = true;
  6644. }
  6645. if (cand.ip.indexOf(':') != -1) {
  6646. this.hadLocalIPv6Candidate = true;
  6647. }
  6648. };
  6649. // helper function to check if a remote candidate is a stun/relay
  6650. // candidate or an ipv6 candidate
  6651. PeerConnection.prototype._checkRemoteCandidate = function (candidate) {
  6652. var cand = SJJ.toCandidateJSON(candidate);
  6653. if (cand.type == 'srflx') {
  6654. this.hadRemoteStunCandidate = true;
  6655. } else if (cand.type == 'relay') {
  6656. this.hadRemoteRelayCandidate = true;
  6657. }
  6658. if (cand.ip.indexOf(':') != -1) {
  6659. this.hadRemoteIPv6Candidate = true;
  6660. }
  6661. };
  6662. // Init and add ice candidate object with correct constructor
  6663. PeerConnection.prototype.processIce = function (update, cb) {
  6664. cb = cb || function () {};
  6665. var self = this;
  6666. // ignore any added ice candidates to avoid errors. why does the
  6667. // spec not do this?
  6668. if (this.pc.signalingState === 'closed') return cb();
  6669. if (update.contents || (update.jingle && update.jingle.contents)) {
  6670. var contentNames = _.pluck(this.remoteDescription.contents, 'name');
  6671. var contents = update.contents || update.jingle.contents;
  6672. contents.forEach(function (content) {
  6673. var transport = content.transport || {};
  6674. var candidates = transport.candidates || [];
  6675. var mline = contentNames.indexOf(content.name);
  6676. var mid = content.name;
  6677. candidates.forEach(
  6678. function (candidate) {
  6679. var iceCandidate = SJJ.toCandidateSDP(candidate) + '\r\n';
  6680. self.pc.addIceCandidate(
  6681. new webrtc.IceCandidate({
  6682. candidate: iceCandidate,
  6683. sdpMLineIndex: mline,
  6684. sdpMid: mid
  6685. }), function () {
  6686. // well, this success callback is pretty meaningless
  6687. },
  6688. function (err) {
  6689. self.emit('error', err);
  6690. }
  6691. );
  6692. self._checkRemoteCandidate(iceCandidate);
  6693. });
  6694. });
  6695. } else {
  6696. // working around https://code.google.com/p/webrtc/issues/detail?id=3669
  6697. if (update.candidate && update.candidate.candidate.indexOf('a=') !== 0) {
  6698. update.candidate.candidate = 'a=' + update.candidate.candidate;
  6699. }
  6700. if (this.wtFirefox && this.firefoxcandidatebuffer !== null) {
  6701. // we cant add this yet due to https://bugzilla.mozilla.org/show_bug.cgi?id=1087551
  6702. if (this.pc.localDescription && this.pc.localDescription.type === 'offer') {
  6703. this.firefoxcandidatebuffer.push(update.candidate);
  6704. return cb();
  6705. }
  6706. }
  6707. self.pc.addIceCandidate(
  6708. new webrtc.IceCandidate(update.candidate),
  6709. function () { },
  6710. function (err) {
  6711. self.emit('error', err);
  6712. }
  6713. );
  6714. self._checkRemoteCandidate(update.candidate.candidate);
  6715. }
  6716. cb();
  6717. };
  6718. // Generate and emit an offer with the given constraints
  6719. PeerConnection.prototype.offer = function (constraints, cb) {
  6720. var self = this;
  6721. var hasConstraints = arguments.length === 2;
  6722. var mediaConstraints = hasConstraints ? constraints : {
  6723. mandatory: {
  6724. OfferToReceiveAudio: true,
  6725. OfferToReceiveVideo: true
  6726. }
  6727. };
  6728. cb = hasConstraints ? cb : constraints;
  6729. cb = cb || function () {};
  6730. if (this.pc.signalingState === 'closed') return cb('Already closed');
  6731. // Actually generate the offer
  6732. this.pc.createOffer(
  6733. function (offer) {
  6734. // does not work for jingle, but jingle.js doesn't need
  6735. // this hack...
  6736. var expandedOffer = {
  6737. type: 'offer',
  6738. sdp: offer.sdp
  6739. };
  6740. if (self.assumeSetLocalSuccess) {
  6741. self.emit('offer', expandedOffer);
  6742. cb(null, expandedOffer);
  6743. }
  6744. self.pc.setLocalDescription(offer,
  6745. function () {
  6746. var jingle;
  6747. if (self.config.useJingle) {
  6748. jingle = SJJ.toSessionJSON(offer.sdp, {
  6749. role: self._role(),
  6750. direction: 'outgoing'
  6751. });
  6752. jingle.sid = self.config.sid;
  6753. self.localDescription = jingle;
  6754. // Save ICE credentials
  6755. _.each(jingle.contents, function (content) {
  6756. var transport = content.transport || {};
  6757. if (transport.ufrag) {
  6758. self.config.ice[content.name] = {
  6759. ufrag: transport.ufrag,
  6760. pwd: transport.pwd
  6761. };
  6762. }
  6763. });
  6764. expandedOffer.jingle = jingle;
  6765. }
  6766. expandedOffer.sdp.split('\r\n').forEach(function (line) {
  6767. if (line.indexOf('a=candidate:') === 0) {
  6768. self._checkLocalCandidate(line);
  6769. }
  6770. });
  6771. if (!self.assumeSetLocalSuccess) {
  6772. self.emit('offer', expandedOffer);
  6773. cb(null, expandedOffer);
  6774. }
  6775. },
  6776. function (err) {
  6777. self.emit('error', err);
  6778. cb(err);
  6779. }
  6780. );
  6781. },
  6782. function (err) {
  6783. self.emit('error', err);
  6784. cb(err);
  6785. },
  6786. mediaConstraints
  6787. );
  6788. };
  6789. // Process an incoming offer so that ICE may proceed before deciding
  6790. // to answer the request.
  6791. PeerConnection.prototype.handleOffer = function (offer, cb) {
  6792. cb = cb || function () {};
  6793. var self = this;
  6794. offer.type = 'offer';
  6795. if (offer.jingle) {
  6796. if (this.enableChromeNativeSimulcast) {
  6797. offer.jingle.contents.forEach(function (content) {
  6798. if (content.name === 'video') {
  6799. content.description.googConferenceFlag = true;
  6800. }
  6801. });
  6802. }
  6803. /*
  6804. if (this.enableMultiStreamHacks) {
  6805. // add a mixed video stream as first stream
  6806. offer.jingle.contents.forEach(function (content) {
  6807. if (content.name === 'video') {
  6808. var sources = content.description.sources || [];
  6809. if (sources.length === 0 || sources[0].ssrc !== "3735928559") {
  6810. sources.unshift({
  6811. ssrc: "3735928559", // 0xdeadbeef
  6812. parameters: [
  6813. {
  6814. key: "cname",
  6815. value: "deadbeef"
  6816. },
  6817. {
  6818. key: "msid",
  6819. value: "mixyourfecintothis please"
  6820. }
  6821. ]
  6822. });
  6823. content.description.sources = sources;
  6824. }
  6825. }
  6826. });
  6827. }
  6828. */
  6829. if (self.restrictBandwidth > 0) {
  6830. if (offer.jingle.contents.length >= 2 && offer.jingle.contents[1].name === 'video') {
  6831. var content = offer.jingle.contents[1];
  6832. var hasBw = content.description && content.description.bandwidth;
  6833. if (!hasBw) {
  6834. offer.jingle.contents[1].description.bandwidth = { type:'AS', bandwidth: self.restrictBandwidth.toString() };
  6835. offer.sdp = SJJ.toSessionSDP(offer.jingle, {
  6836. sid: self.config.sdpSessionID,
  6837. role: self._role(),
  6838. direction: 'outgoing'
  6839. });
  6840. }
  6841. }
  6842. }
  6843. offer.sdp = SJJ.toSessionSDP(offer.jingle, {
  6844. sid: self.config.sdpSessionID,
  6845. role: self._role(),
  6846. direction: 'incoming'
  6847. });
  6848. self.remoteDescription = offer.jingle;
  6849. }
  6850. offer.sdp.split('\r\n').forEach(function (line) {
  6851. if (line.indexOf('a=candidate:') === 0) {
  6852. self._checkRemoteCandidate(line);
  6853. }
  6854. });
  6855. self.pc.setRemoteDescription(new webrtc.SessionDescription(offer),
  6856. function () {
  6857. cb();
  6858. },
  6859. cb
  6860. );
  6861. };
  6862. // Answer an offer with audio only
  6863. PeerConnection.prototype.answerAudioOnly = function (cb) {
  6864. var mediaConstraints = {
  6865. mandatory: {
  6866. OfferToReceiveAudio: true,
  6867. OfferToReceiveVideo: false
  6868. }
  6869. };
  6870. this._answer(mediaConstraints, cb);
  6871. };
  6872. // Answer an offer without offering to recieve
  6873. PeerConnection.prototype.answerBroadcastOnly = function (cb) {
  6874. var mediaConstraints = {
  6875. mandatory: {
  6876. OfferToReceiveAudio: false,
  6877. OfferToReceiveVideo: false
  6878. }
  6879. };
  6880. this._answer(mediaConstraints, cb);
  6881. };
  6882. // Answer an offer with given constraints default is audio/video
  6883. PeerConnection.prototype.answer = function (constraints, cb) {
  6884. var self = this;
  6885. var hasConstraints = arguments.length === 2;
  6886. var callback = hasConstraints ? cb : constraints;
  6887. var mediaConstraints = hasConstraints ? constraints : {
  6888. mandatory: {
  6889. OfferToReceiveAudio: true,
  6890. OfferToReceiveVideo: true
  6891. }
  6892. };
  6893. this._answer(mediaConstraints, callback);
  6894. };
  6895. // Process an answer
  6896. PeerConnection.prototype.handleAnswer = function (answer, cb) {
  6897. cb = cb || function () {};
  6898. var self = this;
  6899. if (answer.jingle) {
  6900. answer.sdp = SJJ.toSessionSDP(answer.jingle, {
  6901. sid: self.config.sdpSessionID,
  6902. role: self._role(),
  6903. direction: 'incoming'
  6904. });
  6905. self.remoteDescription = answer.jingle;
  6906. }
  6907. answer.sdp.split('\r\n').forEach(function (line) {
  6908. if (line.indexOf('a=candidate:') === 0) {
  6909. self._checkRemoteCandidate(line);
  6910. }
  6911. });
  6912. self.pc.setRemoteDescription(
  6913. new webrtc.SessionDescription(answer),
  6914. function () {
  6915. if (self.wtFirefox) {
  6916. window.setTimeout(function () {
  6917. self.firefoxcandidatebuffer.forEach(function (candidate) {
  6918. // add candidates later
  6919. self.pc.addIceCandidate(
  6920. new webrtc.IceCandidate(candidate),
  6921. function () { },
  6922. function (err) {
  6923. self.emit('error', err);
  6924. }
  6925. );
  6926. self._checkRemoteCandidate(candidate.candidate);
  6927. });
  6928. self.firefoxcandidatebuffer = null;
  6929. }, self.wtFirefox);
  6930. }
  6931. cb(null);
  6932. },
  6933. cb
  6934. );
  6935. };
  6936. // Close the peer connection
  6937. PeerConnection.prototype.close = function () {
  6938. this.pc.close();
  6939. this._localDataChannels = [];
  6940. this._remoteDataChannels = [];
  6941. this.emit('close');
  6942. };
  6943. // Internal code sharing for various types of answer methods
  6944. PeerConnection.prototype._answer = function (constraints, cb) {
  6945. cb = cb || function () {};
  6946. var self = this;
  6947. if (!this.pc.remoteDescription) {
  6948. // the old API is used, call handleOffer
  6949. throw new Error('remoteDescription not set');
  6950. }
  6951. if (this.pc.signalingState === 'closed') return cb('Already closed');
  6952. self.pc.createAnswer(
  6953. function (answer) {
  6954. var sim = [];
  6955. var rtx = [];
  6956. if (self.enableChromeNativeSimulcast) {
  6957. // native simulcast part 1: add another SSRC
  6958. answer.jingle = SJJ.toSessionJSON(answer.sdp, {
  6959. role: self._role(),
  6960. direction: 'outgoing'
  6961. });
  6962. if (answer.jingle.contents.length >= 2 && answer.jingle.contents[1].name === 'video') {
  6963. var hasSimgroup = false;
  6964. var groups = answer.jingle.contents[1].description.sourceGroups || [];
  6965. var hasSim = false;
  6966. groups.forEach(function (group) {
  6967. if (group.semantics == 'SIM') hasSim = true;
  6968. });
  6969. if (!hasSim &&
  6970. answer.jingle.contents[1].description.sources.length) {
  6971. var newssrc = JSON.parse(JSON.stringify(answer.jingle.contents[1].description.sources[0]));
  6972. newssrc.ssrc = '' + Math.floor(Math.random() * 0xffffffff); // FIXME: look for conflicts
  6973. answer.jingle.contents[1].description.sources.push(newssrc);
  6974. sim.push(answer.jingle.contents[1].description.sources[0].ssrc);
  6975. sim.push(newssrc.ssrc);
  6976. groups.push({
  6977. semantics: 'SIM',
  6978. sources: sim
  6979. });
  6980. // also create an RTX one for the SIM one
  6981. var rtxssrc = JSON.parse(JSON.stringify(newssrc));
  6982. rtxssrc.ssrc = '' + Math.floor(Math.random() * 0xffffffff); // FIXME: look for conflicts
  6983. answer.jingle.contents[1].description.sources.push(rtxssrc);
  6984. groups.push({
  6985. semantics: 'FID',
  6986. sources: [newssrc.ssrc, rtxssrc.ssrc]
  6987. });
  6988. answer.jingle.contents[1].description.sourceGroups = groups;
  6989. answer.sdp = SJJ.toSessionSDP(answer.jingle, {
  6990. sid: self.config.sdpSessionID,
  6991. role: self._role(),
  6992. direction: 'outgoing'
  6993. });
  6994. }
  6995. }
  6996. }
  6997. var expandedAnswer = {
  6998. type: 'answer',
  6999. sdp: answer.sdp
  7000. };
  7001. if (self.assumeSetLocalSuccess) {
  7002. // not safe to do when doing simulcast mangling
  7003. self.emit('answer', expandedAnswer);
  7004. cb(null, expandedAnswer);
  7005. }
  7006. self.pc.setLocalDescription(answer,
  7007. function () {
  7008. if (self.config.useJingle) {
  7009. var jingle = SJJ.toSessionJSON(answer.sdp, {
  7010. role: self._role(),
  7011. direction: 'outgoing'
  7012. });
  7013. jingle.sid = self.config.sid;
  7014. self.localDescription = jingle;
  7015. expandedAnswer.jingle = jingle;
  7016. }
  7017. if (self.enableChromeNativeSimulcast) {
  7018. // native simulcast part 2:
  7019. // signal multiple tracks to the receiver
  7020. // for anything in the SIM group
  7021. if (!expandedAnswer.jingle) {
  7022. expandedAnswer.jingle = SJJ.toSessionJSON(answer.sdp, {
  7023. role: self._role(),
  7024. direction: 'outgoing'
  7025. });
  7026. }
  7027. var groups = expandedAnswer.jingle.contents[1].description.sourceGroups || [];
  7028. expandedAnswer.jingle.contents[1].description.sources.forEach(function (source, idx) {
  7029. // the floor idx/2 is a hack that relies on a particular order
  7030. // of groups, alternating between sim and rtx
  7031. source.parameters = source.parameters.map(function (parameter) {
  7032. if (parameter.key === 'msid') {
  7033. parameter.value += '-' + Math.floor(idx / 2);
  7034. }
  7035. return parameter;
  7036. });
  7037. });
  7038. expandedAnswer.sdp = SJJ.toSessionSDP(expandedAnswer.jingle, {
  7039. sid: self.sdpSessionID,
  7040. role: self._role(),
  7041. direction: 'outgoing'
  7042. });
  7043. }
  7044. expandedAnswer.sdp.split('\r\n').forEach(function (line) {
  7045. if (line.indexOf('a=candidate:') === 0) {
  7046. self._checkLocalCandidate(line);
  7047. }
  7048. });
  7049. if (!self.assumeSetLocalSuccess) {
  7050. self.emit('answer', expandedAnswer);
  7051. cb(null, expandedAnswer);
  7052. }
  7053. },
  7054. function (err) {
  7055. self.emit('error', err);
  7056. cb(err);
  7057. }
  7058. );
  7059. },
  7060. function (err) {
  7061. self.emit('error', err);
  7062. cb(err);
  7063. },
  7064. constraints
  7065. );
  7066. };
  7067. // Internal method for emitting ice candidates on our peer object
  7068. PeerConnection.prototype._onIce = function (event) {
  7069. var self = this;
  7070. if (event.candidate) {
  7071. var ice = event.candidate;
  7072. var expandedCandidate = {
  7073. candidate: {
  7074. candidate: ice.candidate,
  7075. sdpMid: ice.sdpMid,
  7076. sdpMLineIndex: ice.sdpMLineIndex
  7077. }
  7078. };
  7079. this._checkLocalCandidate(ice.candidate);
  7080. var cand = SJJ.toCandidateJSON(ice.candidate);
  7081. if (self.config.useJingle) {
  7082. if (!ice.sdpMid) { // firefox doesn't set this
  7083. ice.sdpMid = self.localDescription.contents[ice.sdpMLineIndex].name;
  7084. }
  7085. if (!self.config.ice[ice.sdpMid]) {
  7086. var jingle = SJJ.toSessionJSON(self.pc.localDescription.sdp, {
  7087. role: self._role(),
  7088. direction: 'outgoing'
  7089. });
  7090. _.each(jingle.contents, function (content) {
  7091. var transport = content.transport || {};
  7092. if (transport.ufrag) {
  7093. self.config.ice[content.name] = {
  7094. ufrag: transport.ufrag,
  7095. pwd: transport.pwd
  7096. };
  7097. }
  7098. });
  7099. }
  7100. expandedCandidate.jingle = {
  7101. contents: [{
  7102. name: ice.sdpMid,
  7103. creator: self._role(),
  7104. transport: {
  7105. transType: 'iceUdp',
  7106. ufrag: self.config.ice[ice.sdpMid].ufrag,
  7107. pwd: self.config.ice[ice.sdpMid].pwd,
  7108. candidates: [
  7109. cand
  7110. ]
  7111. }
  7112. }]
  7113. };
  7114. if (self.batchIceCandidates > 0) {
  7115. if (self.batchedIceCandidates.length === 0) {
  7116. window.setTimeout(function () {
  7117. var contents = {};
  7118. self.batchedIceCandidates.forEach(function (content) {
  7119. content = content.contents[0];
  7120. if (!contents[content.name]) contents[content.name] = content;
  7121. contents[content.name].transport.candidates.push(content.transport.candidates[0]);
  7122. });
  7123. var newCand = {
  7124. jingle: {
  7125. contents: []
  7126. }
  7127. };
  7128. Object.keys(contents).forEach(function (name) {
  7129. newCand.jingle.contents.push(contents[name]);
  7130. });
  7131. self.batchedIceCandidates = [];
  7132. self.emit('ice', newCand);
  7133. }, self.batchIceCandidates);
  7134. }
  7135. self.batchedIceCandidates.push(expandedCandidate.jingle);
  7136. return;
  7137. }
  7138. }
  7139. this.emit('ice', expandedCandidate);
  7140. } else {
  7141. this.emit('endOfCandidates');
  7142. }
  7143. };
  7144. // Internal method for processing a new data channel being added by the
  7145. // other peer.
  7146. PeerConnection.prototype._onDataChannel = function (event) {
  7147. // make sure we keep a reference so this doesn't get garbage collected
  7148. var channel = event.channel;
  7149. this._remoteDataChannels.push(channel);
  7150. this.emit('addChannel', channel);
  7151. };
  7152. // Create a data channel spec reference:
  7153. // http://dev.w3.org/2011/webrtc/editor/webrtc.html#idl-def-RTCDataChannelInit
  7154. PeerConnection.prototype.createDataChannel = function (name, opts) {
  7155. var channel = this.pc.createDataChannel(name, opts);
  7156. // make sure we keep a reference so this doesn't get garbage collected
  7157. this._localDataChannels.push(channel);
  7158. return channel;
  7159. };
  7160. // a wrapper around getStats which hides the differences (where possible)
  7161. PeerConnection.prototype.getStats = function (cb) {
  7162. if (webrtc.prefix === 'moz') {
  7163. this.pc.getStats(
  7164. function (res) {
  7165. var items = [];
  7166. for (var result in res) {
  7167. if (typeof res[result] === 'object') {
  7168. items.push(res[result]);
  7169. }
  7170. }
  7171. cb(null, items);
  7172. },
  7173. cb
  7174. );
  7175. } else {
  7176. this.pc.getStats(function (res) {
  7177. var items = [];
  7178. res.result().forEach(function (result) {
  7179. var item = {};
  7180. result.names().forEach(function (name) {
  7181. item[name] = result.stat(name);
  7182. });
  7183. item.id = result.id;
  7184. item.type = result.type;
  7185. item.timestamp = result.timestamp;
  7186. items.push(item);
  7187. });
  7188. cb(null, items);
  7189. });
  7190. }
  7191. };
  7192. module.exports = PeerConnection;
  7193. },{"sdp-jingle-json":21,"traceablepeerconnection":24,"underscore":20,"util":9,"webrtcsupport":5,"wildemitter":3}],15:[function(require,module,exports){
  7194. var WildEmitter = require('wildemitter');
  7195. var util = require('util');
  7196. function Sender(opts) {
  7197. WildEmitter.call(this);
  7198. var options = opts || {};
  7199. this.config = {
  7200. chunksize: 16384,
  7201. pacing: 0
  7202. };
  7203. // set our config from options
  7204. var item;
  7205. for (item in options) {
  7206. this.config[item] = options[item];
  7207. }
  7208. this.file = null;
  7209. this.channel = null;
  7210. }
  7211. util.inherits(Sender, WildEmitter);
  7212. Sender.prototype.send = function (file, channel) {
  7213. var self = this;
  7214. this.file = file;
  7215. this.channel = channel;
  7216. var sliceFile = function(offset) {
  7217. var reader = new window.FileReader();
  7218. reader.onload = (function() {
  7219. return function(e) {
  7220. self.channel.send(e.target.result);
  7221. self.emit('progress', offset, file.size, e.target.result);
  7222. if (file.size > offset + e.target.result.byteLength) {
  7223. window.setTimeout(sliceFile, self.config.pacing, offset + self.config.chunksize);
  7224. } else {
  7225. self.emit('progress', file.size, file.size, null);
  7226. self.emit('sentFile');
  7227. }
  7228. };
  7229. })(file);
  7230. var slice = file.slice(offset, offset + self.config.chunksize);
  7231. reader.readAsArrayBuffer(slice);
  7232. };
  7233. window.setTimeout(sliceFile, 0, 0);
  7234. };
  7235. function Receiver() {
  7236. WildEmitter.call(this);
  7237. this.receiveBuffer = [];
  7238. this.received = 0;
  7239. this.metadata = {};
  7240. this.channel = null;
  7241. }
  7242. util.inherits(Receiver, WildEmitter);
  7243. Receiver.prototype.receive = function (metadata, channel) {
  7244. var self = this;
  7245. if (metadata) {
  7246. this.metadata = metadata;
  7247. }
  7248. this.channel = channel;
  7249. // chrome only supports arraybuffers and those make it easier to calc the hash
  7250. channel.binaryType = 'arraybuffer';
  7251. this.channel.onmessage = function (event) {
  7252. var len = event.data.byteLength;
  7253. self.received += len;
  7254. self.receiveBuffer.push(event.data);
  7255. self.emit('progress', self.received, self.metadata.size, event.data);
  7256. if (self.received === self.metadata.size) {
  7257. self.emit('receivedFile', new window.Blob(self.receiveBuffer), self.metadata);
  7258. self.receiveBuffer = []; // discard receivebuffer
  7259. } else if (self.received > self.metadata.size) {
  7260. // FIXME
  7261. console.error('received more than expected, discarding...');
  7262. self.receiveBuffer = []; // just discard...
  7263. }
  7264. };
  7265. };
  7266. module.exports = {};
  7267. module.exports.support = window && window.File && window.FileReader && window.Blob;
  7268. module.exports.Sender = Sender;
  7269. module.exports.Receiver = Receiver;
  7270. },{"util":9,"wildemitter":3}],18:[function(require,module,exports){
  7271. // getScreenMedia helper by @HenrikJoreteg
  7272. var getUserMedia = require('getusermedia');
  7273. // cache for constraints and callback
  7274. var cache = {};
  7275. module.exports = function (constraints, cb) {
  7276. var hasConstraints = arguments.length === 2;
  7277. var callback = hasConstraints ? cb : constraints;
  7278. var error;
  7279. if (typeof window === 'undefined' || window.location.protocol === 'http:') {
  7280. error = new Error('NavigatorUserMediaError');
  7281. error.name = 'HTTPS_REQUIRED';
  7282. return callback(error);
  7283. }
  7284. if (window.navigator.userAgent.match('Chrome')) {
  7285. var chromever = parseInt(window.navigator.userAgent.match(/Chrome\/(.*) /)[1], 10);
  7286. var maxver = 33;
  7287. var isCef = !window.chrome.webstore;
  7288. // "known" crash in chrome 34 and 35 on linux
  7289. if (window.navigator.userAgent.match('Linux')) maxver = 35;
  7290. if (isCef || (chromever >= 26 && chromever <= maxver)) {
  7291. // chrome 26 - chrome 33 way to do it -- requires bad chrome://flags
  7292. // note: this is basically in maintenance mode and will go away soon
  7293. constraints = (hasConstraints && constraints) || {
  7294. video: {
  7295. mandatory: {
  7296. googLeakyBucket: true,
  7297. maxWidth: window.screen.width,
  7298. maxHeight: window.screen.height,
  7299. maxFrameRate: 3,
  7300. chromeMediaSource: 'screen'
  7301. }
  7302. }
  7303. };
  7304. getUserMedia(constraints, callback);
  7305. } else {
  7306. // chrome 34+ way requiring an extension
  7307. var pending = window.setTimeout(function () {
  7308. error = new Error('NavigatorUserMediaError');
  7309. error.name = 'EXTENSION_UNAVAILABLE';
  7310. return callback(error);
  7311. }, 1000);
  7312. cache[pending] = [callback, hasConstraints ? constraint : null];
  7313. window.postMessage({ type: 'getScreen', id: pending }, '*');
  7314. }
  7315. } else if (window.navigator.userAgent.match('Firefox')) {
  7316. var ffver = parseInt(window.navigator.userAgent.match(/Firefox\/(.*)/)[1], 10);
  7317. if (ffver >= 33) {
  7318. constraints = (hasConstraints && constraints) || {
  7319. video: {
  7320. mozMediaSource: 'window',
  7321. mediaSource: 'window'
  7322. }
  7323. }
  7324. getUserMedia(constraints, function (err, stream) {
  7325. callback(err, stream);
  7326. // workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=1045810
  7327. if (!err) {
  7328. var lastTime = stream.currentTime;
  7329. var polly = window.setInterval(function () {
  7330. if (!stream) window.clearInterval(polly);
  7331. if (stream.currentTime == lastTime) {
  7332. window.clearInterval(polly);
  7333. if (stream.onended) {
  7334. stream.onended();
  7335. }
  7336. }
  7337. lastTime = stream.currentTime;
  7338. }, 500);
  7339. }
  7340. });
  7341. } else {
  7342. error = new Error('NavigatorUserMediaError');
  7343. error.name = 'EXTENSION_UNAVAILABLE'; // does not make much sense but...
  7344. }
  7345. }
  7346. };
  7347. window.addEventListener('message', function (event) {
  7348. if (event.origin != window.location.origin) {
  7349. return;
  7350. }
  7351. if (event.data.type == 'gotScreen' && cache[event.data.id]) {
  7352. var data = cache[event.data.id];
  7353. var constraints = data[1];
  7354. var callback = data[0];
  7355. delete cache[event.data.id];
  7356. if (event.data.sourceId === '') { // user canceled
  7357. var error = new Error('NavigatorUserMediaError');
  7358. error.name = 'PERMISSION_DENIED';
  7359. callback(error);
  7360. } else {
  7361. constraints = constraints || {audio: false, video: {
  7362. mandatory: {
  7363. chromeMediaSource: 'desktop',
  7364. maxWidth: window.screen.width,
  7365. maxHeight: window.screen.height,
  7366. maxFrameRate: 3
  7367. },
  7368. optional: [
  7369. {googLeakyBucket: true},
  7370. {googTemporalLayeredScreencast: true}
  7371. ]
  7372. }};
  7373. constraints.video.mandatory.chromeMediaSourceId = event.data.sourceId;
  7374. getUserMedia(constraints, callback);
  7375. }
  7376. } else if (event.data.type == 'getScreenPending') {
  7377. window.clearTimeout(event.data.id);
  7378. }
  7379. });
  7380. },{"getusermedia":16}],19:[function(require,module,exports){
  7381. var support = require('webrtcsupport');
  7382. function GainController(stream) {
  7383. this.support = support.webAudio && support.mediaStream;
  7384. // set our starting value
  7385. this.gain = 1;
  7386. if (this.support) {
  7387. var context = this.context = new support.AudioContext();
  7388. this.microphone = context.createMediaStreamSource(stream);
  7389. this.gainFilter = context.createGain();
  7390. this.destination = context.createMediaStreamDestination();
  7391. this.outputStream = this.destination.stream;
  7392. this.microphone.connect(this.gainFilter);
  7393. this.gainFilter.connect(this.destination);
  7394. stream.addTrack(this.outputStream.getAudioTracks()[0]);
  7395. stream.removeTrack(stream.getAudioTracks()[0]);
  7396. }
  7397. this.stream = stream;
  7398. }
  7399. // setting
  7400. GainController.prototype.setGain = function (val) {
  7401. // check for support
  7402. if (!this.support) return;
  7403. this.gainFilter.gain.value = val;
  7404. this.gain = val;
  7405. };
  7406. GainController.prototype.getGain = function () {
  7407. return this.gain;
  7408. };
  7409. GainController.prototype.off = function () {
  7410. return this.setGain(0);
  7411. };
  7412. GainController.prototype.on = function () {
  7413. this.setGain(1);
  7414. };
  7415. module.exports = GainController;
  7416. },{"webrtcsupport":5}],17:[function(require,module,exports){
  7417. var WildEmitter = require('wildemitter');
  7418. function getMaxVolume (analyser, fftBins) {
  7419. var maxVolume = -Infinity;
  7420. analyser.getFloatFrequencyData(fftBins);
  7421. for(var i=4, ii=fftBins.length; i < ii; i++) {
  7422. if (fftBins[i] > maxVolume && fftBins[i] < 0) {
  7423. maxVolume = fftBins[i];
  7424. }
  7425. };
  7426. return maxVolume;
  7427. }
  7428. var audioContextType = window.AudioContext || window.webkitAudioContext;
  7429. // use a single audio context due to hardware limits
  7430. var audioContext = null;
  7431. module.exports = function(stream, options) {
  7432. var harker = new WildEmitter();
  7433. // make it not break in non-supported browsers
  7434. if (!audioContextType) return harker;
  7435. //Config
  7436. var options = options || {},
  7437. smoothing = (options.smoothing || 0.1),
  7438. interval = (options.interval || 50),
  7439. threshold = options.threshold,
  7440. play = options.play,
  7441. history = options.history || 10,
  7442. running = true;
  7443. //Setup Audio Context
  7444. if (!audioContext) {
  7445. audioContext = new audioContextType();
  7446. }
  7447. var sourceNode, fftBins, analyser;
  7448. analyser = audioContext.createAnalyser();
  7449. analyser.fftSize = 512;
  7450. analyser.smoothingTimeConstant = smoothing;
  7451. fftBins = new Float32Array(analyser.fftSize);
  7452. if (stream.jquery) stream = stream[0];
  7453. if (stream instanceof HTMLAudioElement || stream instanceof HTMLVideoElement) {
  7454. //Audio Tag
  7455. sourceNode = audioContext.createMediaElementSource(stream);
  7456. if (typeof play === 'undefined') play = true;
  7457. threshold = threshold || -50;
  7458. } else {
  7459. //WebRTC Stream
  7460. sourceNode = audioContext.createMediaStreamSource(stream);
  7461. threshold = threshold || -50;
  7462. }
  7463. sourceNode.connect(analyser);
  7464. if (play) analyser.connect(audioContext.destination);
  7465. harker.speaking = false;
  7466. harker.setThreshold = function(t) {
  7467. threshold = t;
  7468. };
  7469. harker.setInterval = function(i) {
  7470. interval = i;
  7471. };
  7472. harker.stop = function() {
  7473. running = false;
  7474. harker.emit('volume_change', -100, threshold);
  7475. if (harker.speaking) {
  7476. harker.speaking = false;
  7477. harker.emit('stopped_speaking');
  7478. }
  7479. };
  7480. harker.speakingHistory = [];
  7481. for (var i = 0; i < history; i++) {
  7482. harker.speakingHistory.push(0);
  7483. }
  7484. // Poll the analyser node to determine if speaking
  7485. // and emit events if changed
  7486. var looper = function() {
  7487. setTimeout(function() {
  7488. //check if stop has been called
  7489. if(!running) {
  7490. return;
  7491. }
  7492. var currentVolume = getMaxVolume(analyser, fftBins);
  7493. harker.emit('volume_change', currentVolume, threshold);
  7494. var history = 0;
  7495. if (currentVolume > threshold && !harker.speaking) {
  7496. // trigger quickly, short history
  7497. for (var i = harker.speakingHistory.length - 3; i < harker.speakingHistory.length; i++) {
  7498. history += harker.speakingHistory[i];
  7499. }
  7500. if (history >= 2) {
  7501. harker.speaking = true;
  7502. harker.emit('speaking');
  7503. }
  7504. } else if (currentVolume < threshold && harker.speaking) {
  7505. for (var i = 0; i < harker.speakingHistory.length; i++) {
  7506. history += harker.speakingHistory[i];
  7507. }
  7508. if (history == 0) {
  7509. harker.speaking = false;
  7510. harker.emit('stopped_speaking');
  7511. }
  7512. }
  7513. harker.speakingHistory.shift();
  7514. harker.speakingHistory.push(0 + (currentVolume > threshold));
  7515. looper();
  7516. }, interval);
  7517. };
  7518. looper();
  7519. return harker;
  7520. }
  7521. },{"wildemitter":3}],22:[function(require,module,exports){
  7522. var SENDERS = require('./senders');
  7523. exports.toSessionSDP = function (session, opts) {
  7524. var role = opts.role || 'initiator';
  7525. var direction = opts.direction || 'outgoing';
  7526. var sid = opts.sid || session.sid || Date.now();
  7527. var time = opts.time || Date.now();
  7528. var sdp = [
  7529. 'v=0',
  7530. 'o=- ' + sid + ' ' + time + ' IN IP4 0.0.0.0',
  7531. 's=-',
  7532. 't=0 0'
  7533. ];
  7534. var groups = session.groups || [];
  7535. groups.forEach(function (group) {
  7536. sdp.push('a=group:' + group.semantics + ' ' + group.contents.join(' '));
  7537. });
  7538. var contents = session.contents || [];
  7539. contents.forEach(function (content) {
  7540. sdp.push(exports.toMediaSDP(content, opts));
  7541. });
  7542. return sdp.join('\r\n') + '\r\n';
  7543. };
  7544. exports.toMediaSDP = function (content, opts) {
  7545. var sdp = [];
  7546. var role = opts.role || 'initiator';
  7547. var direction = opts.direction || 'outgoing';
  7548. var desc = content.description;
  7549. var transport = content.transport;
  7550. var payloads = desc.payloads || [];
  7551. var fingerprints = (transport && transport.fingerprints) || [];
  7552. var mline = [];
  7553. if (desc.descType == 'datachannel') {
  7554. mline.push('application');
  7555. mline.push('1');
  7556. mline.push('DTLS/SCTP');
  7557. if (transport.sctp) {
  7558. transport.sctp.forEach(function (map) {
  7559. mline.push(map.number);
  7560. });
  7561. }
  7562. } else {
  7563. mline.push(desc.media);
  7564. mline.push('1');
  7565. if ((desc.encryption && desc.encryption.length > 0) || (fingerprints.length > 0)) {
  7566. mline.push('RTP/SAVPF');
  7567. } else {
  7568. mline.push('RTP/AVPF');
  7569. }
  7570. payloads.forEach(function (payload) {
  7571. mline.push(payload.id);
  7572. });
  7573. }
  7574. sdp.push('m=' + mline.join(' '));
  7575. sdp.push('c=IN IP4 0.0.0.0');
  7576. if (desc.bandwidth && desc.bandwidth.type && desc.bandwidth.bandwidth) {
  7577. sdp.push('b=' + desc.bandwidth.type + ':' + desc.bandwidth.bandwidth);
  7578. }
  7579. if (desc.descType == 'rtp') {
  7580. sdp.push('a=rtcp:1 IN IP4 0.0.0.0');
  7581. }
  7582. if (transport) {
  7583. if (transport.ufrag) {
  7584. sdp.push('a=ice-ufrag:' + transport.ufrag);
  7585. }
  7586. if (transport.pwd) {
  7587. sdp.push('a=ice-pwd:' + transport.pwd);
  7588. }
  7589. var pushedSetup = false;
  7590. fingerprints.forEach(function (fingerprint) {
  7591. sdp.push('a=fingerprint:' + fingerprint.hash + ' ' + fingerprint.value);
  7592. if (fingerprint.setup && !pushedSetup) {
  7593. sdp.push('a=setup:' + fingerprint.setup);
  7594. }
  7595. });
  7596. if (transport.sctp) {
  7597. transport.sctp.forEach(function (map) {
  7598. sdp.push('a=sctpmap:' + map.number + ' ' + map.protocol + ' ' + map.streams);
  7599. });
  7600. }
  7601. }
  7602. if (desc.descType == 'rtp') {
  7603. sdp.push('a=' + (SENDERS[role][direction][content.senders] || 'sendrecv'));
  7604. }
  7605. sdp.push('a=mid:' + content.name);
  7606. if (desc.mux) {
  7607. sdp.push('a=rtcp-mux');
  7608. }
  7609. var encryption = desc.encryption || [];
  7610. encryption.forEach(function (crypto) {
  7611. sdp.push('a=crypto:' + crypto.tag + ' ' + crypto.cipherSuite + ' ' + crypto.keyParams + (crypto.sessionParams ? ' ' + crypto.sessionParams : ''));
  7612. });
  7613. if (desc.googConferenceFlag) {
  7614. sdp.push('a=x-google-flag:conference');
  7615. }
  7616. payloads.forEach(function (payload) {
  7617. var rtpmap = 'a=rtpmap:' + payload.id + ' ' + payload.name + '/' + payload.clockrate;
  7618. if (payload.channels && payload.channels != '1') {
  7619. rtpmap += '/' + payload.channels;
  7620. }
  7621. sdp.push(rtpmap);
  7622. if (payload.parameters && payload.parameters.length) {
  7623. var fmtp = ['a=fmtp:' + payload.id];
  7624. var parameters = [];
  7625. payload.parameters.forEach(function (param) {
  7626. parameters.push((param.key ? param.key + '=' : '') + param.value);
  7627. });
  7628. fmtp.push(parameters.join(';'));
  7629. sdp.push(fmtp.join(' '));
  7630. }
  7631. if (payload.feedback) {
  7632. payload.feedback.forEach(function (fb) {
  7633. if (fb.type === 'trr-int') {
  7634. sdp.push('a=rtcp-fb:' + payload.id + ' trr-int ' + (fb.value ? fb.value : '0'));
  7635. } else {
  7636. sdp.push('a=rtcp-fb:' + payload.id + ' ' + fb.type + (fb.subtype ? ' ' + fb.subtype : ''));
  7637. }
  7638. });
  7639. }
  7640. });
  7641. if (desc.feedback) {
  7642. desc.feedback.forEach(function (fb) {
  7643. if (fb.type === 'trr-int') {
  7644. sdp.push('a=rtcp-fb:* trr-int ' + (fb.value ? fb.value : '0'));
  7645. } else {
  7646. sdp.push('a=rtcp-fb:* ' + fb.type + (fb.subtype ? ' ' + fb.subtype : ''));
  7647. }
  7648. });
  7649. }
  7650. var hdrExts = desc.headerExtensions || [];
  7651. hdrExts.forEach(function (hdr) {
  7652. sdp.push('a=extmap:' + hdr.id + (hdr.senders ? '/' + SENDERS[role][direction][hdr.senders] : '') + ' ' + hdr.uri);
  7653. });
  7654. var ssrcGroups = desc.sourceGroups || [];
  7655. ssrcGroups.forEach(function (ssrcGroup) {
  7656. sdp.push('a=ssrc-group:' + ssrcGroup.semantics + ' ' + ssrcGroup.sources.join(' '));
  7657. });
  7658. var ssrcs = desc.sources || [];
  7659. ssrcs.forEach(function (ssrc) {
  7660. for (var i = 0; i < ssrc.parameters.length; i++) {
  7661. var param = ssrc.parameters[i];
  7662. sdp.push('a=ssrc:' + (ssrc.ssrc || desc.ssrc) + ' ' + param.key + (param.value ? (':' + param.value) : ''));
  7663. }
  7664. });
  7665. var candidates = transport.candidates || [];
  7666. candidates.forEach(function (candidate) {
  7667. sdp.push(exports.toCandidateSDP(candidate));
  7668. });
  7669. return sdp.join('\r\n');
  7670. };
  7671. exports.toCandidateSDP = function (candidate) {
  7672. var sdp = [];
  7673. sdp.push(candidate.foundation);
  7674. sdp.push(candidate.component);
  7675. sdp.push(candidate.protocol.toUpperCase());
  7676. sdp.push(candidate.priority);
  7677. sdp.push(candidate.ip);
  7678. sdp.push(candidate.port);
  7679. var type = candidate.type;
  7680. sdp.push('typ');
  7681. sdp.push(type);
  7682. if (type === 'srflx' || type === 'prflx' || type === 'relay') {
  7683. if (candidate.relAddr && candidate.relPort) {
  7684. sdp.push('raddr');
  7685. sdp.push(candidate.relAddr);
  7686. sdp.push('rport');
  7687. sdp.push(candidate.relPort);
  7688. }
  7689. }
  7690. if (candidate.tcpType && candidate.protocol.toUpperCase() == 'TCP') {
  7691. sdp.push('tcptype');
  7692. sdp.push(candidate.tcpType);
  7693. }
  7694. sdp.push('generation');
  7695. sdp.push(candidate.generation || '0');
  7696. // FIXME: apparently this is wrong per spec
  7697. // but then, we need this when actually putting this into
  7698. // SDP so it's going to stay.
  7699. // decision needs to be revisited when browsers dont
  7700. // accept this any longer
  7701. return 'a=candidate:' + sdp.join(' ');
  7702. };
  7703. },{"./senders":25}],23:[function(require,module,exports){
  7704. var SENDERS = require('./senders');
  7705. var parsers = require('./parsers');
  7706. var idCounter = Math.random();
  7707. exports._setIdCounter = function (counter) {
  7708. idCounter = counter;
  7709. };
  7710. exports.toSessionJSON = function (sdp, opts) {
  7711. var i;
  7712. var creators = opts.creators || [];
  7713. var role = opts.role || 'initiator';
  7714. var direction = opts.direction || 'outgoing';
  7715. // Divide the SDP into session and media sections.
  7716. var media = sdp.split('\r\nm=');
  7717. for (i = 1; i < media.length; i++) {
  7718. media[i] = 'm=' + media[i];
  7719. if (i !== media.length - 1) {
  7720. media[i] += '\r\n';
  7721. }
  7722. }
  7723. var session = media.shift() + '\r\n';
  7724. var sessionLines = parsers.lines(session);
  7725. var parsed = {};
  7726. var contents = [];
  7727. for (i = 0; i < media.length; i++) {
  7728. contents.push(exports.toMediaJSON(media[i], session, {
  7729. role: role,
  7730. direction: direction,
  7731. creator: creators[i] || 'initiator'
  7732. }));
  7733. }
  7734. parsed.contents = contents;
  7735. var groupLines = parsers.findLines('a=group:', sessionLines);
  7736. if (groupLines.length) {
  7737. parsed.groups = parsers.groups(groupLines);
  7738. }
  7739. return parsed;
  7740. };
  7741. exports.toMediaJSON = function (media, session, opts) {
  7742. var creator = opts.creator || 'initiator';
  7743. var role = opts.role || 'initiator';
  7744. var direction = opts.direction || 'outgoing';
  7745. var lines = parsers.lines(media);
  7746. var sessionLines = parsers.lines(session);
  7747. var mline = parsers.mline(lines[0]);
  7748. var content = {
  7749. creator: creator,
  7750. name: mline.media,
  7751. description: {
  7752. descType: 'rtp',
  7753. media: mline.media,
  7754. payloads: [],
  7755. encryption: [],
  7756. feedback: [],
  7757. headerExtensions: []
  7758. },
  7759. transport: {
  7760. transType: 'iceUdp',
  7761. candidates: [],
  7762. fingerprints: []
  7763. }
  7764. };
  7765. if (mline.media == 'application') {
  7766. // FIXME: the description is most likely to be independent
  7767. // of the SDP and should be processed by other parts of the library
  7768. content.description = {
  7769. descType: 'datachannel'
  7770. };
  7771. content.transport.sctp = [];
  7772. }
  7773. var desc = content.description;
  7774. var trans = content.transport;
  7775. // If we have a mid, use that for the content name instead.
  7776. var mid = parsers.findLine('a=mid:', lines);
  7777. if (mid) {
  7778. content.name = mid.substr(6);
  7779. }
  7780. if (parsers.findLine('a=sendrecv', lines, sessionLines)) {
  7781. content.senders = 'both';
  7782. } else if (parsers.findLine('a=sendonly', lines, sessionLines)) {
  7783. content.senders = SENDERS[role][direction].sendonly;
  7784. } else if (parsers.findLine('a=recvonly', lines, sessionLines)) {
  7785. content.senders = SENDERS[role][direction].recvonly;
  7786. } else if (parsers.findLine('a=inactive', lines, sessionLines)) {
  7787. content.senders = 'none';
  7788. }
  7789. if (desc.descType == 'rtp') {
  7790. var bandwidth = parsers.findLine('b=', lines);
  7791. if (bandwidth) {
  7792. desc.bandwidth = parsers.bandwidth(bandwidth);
  7793. }
  7794. var ssrc = parsers.findLine('a=ssrc:', lines);
  7795. if (ssrc) {
  7796. desc.ssrc = ssrc.substr(7).split(' ')[0];
  7797. }
  7798. var rtpmapLines = parsers.findLines('a=rtpmap:', lines);
  7799. rtpmapLines.forEach(function (line) {
  7800. var payload = parsers.rtpmap(line);
  7801. payload.parameters = [];
  7802. payload.feedback = [];
  7803. var fmtpLines = parsers.findLines('a=fmtp:' + payload.id, lines);
  7804. // There should only be one fmtp line per payload
  7805. fmtpLines.forEach(function (line) {
  7806. payload.parameters = parsers.fmtp(line);
  7807. });
  7808. var fbLines = parsers.findLines('a=rtcp-fb:' + payload.id, lines);
  7809. fbLines.forEach(function (line) {
  7810. payload.feedback.push(parsers.rtcpfb(line));
  7811. });
  7812. desc.payloads.push(payload);
  7813. });
  7814. var cryptoLines = parsers.findLines('a=crypto:', lines, sessionLines);
  7815. cryptoLines.forEach(function (line) {
  7816. desc.encryption.push(parsers.crypto(line));
  7817. });
  7818. if (parsers.findLine('a=rtcp-mux', lines)) {
  7819. desc.mux = true;
  7820. }
  7821. var fbLines = parsers.findLines('a=rtcp-fb:*', lines);
  7822. fbLines.forEach(function (line) {
  7823. desc.feedback.push(parsers.rtcpfb(line));
  7824. });
  7825. var extLines = parsers.findLines('a=extmap:', lines);
  7826. extLines.forEach(function (line) {
  7827. var ext = parsers.extmap(line);
  7828. ext.senders = SENDERS[role][direction][ext.senders];
  7829. desc.headerExtensions.push(ext);
  7830. });
  7831. var ssrcGroupLines = parsers.findLines('a=ssrc-group:', lines);
  7832. desc.sourceGroups = parsers.sourceGroups(ssrcGroupLines || []);
  7833. var ssrcLines = parsers.findLines('a=ssrc:', lines);
  7834. desc.sources = parsers.sources(ssrcLines || []);
  7835. if (parsers.findLine('a=x-google-flag:conference', lines, sessionLines)) {
  7836. desc.googConferenceFlag = true;
  7837. }
  7838. }
  7839. // transport specific attributes
  7840. var fingerprintLines = parsers.findLines('a=fingerprint:', lines, sessionLines);
  7841. var setup = parsers.findLine('a=setup:', lines, sessionLines);
  7842. fingerprintLines.forEach(function (line) {
  7843. var fp = parsers.fingerprint(line);
  7844. if (setup) {
  7845. fp.setup = setup.substr(8);
  7846. }
  7847. trans.fingerprints.push(fp);
  7848. });
  7849. var ufragLine = parsers.findLine('a=ice-ufrag:', lines, sessionLines);
  7850. var pwdLine = parsers.findLine('a=ice-pwd:', lines, sessionLines);
  7851. if (ufragLine && pwdLine) {
  7852. trans.ufrag = ufragLine.substr(12);
  7853. trans.pwd = pwdLine.substr(10);
  7854. trans.candidates = [];
  7855. var candidateLines = parsers.findLines('a=candidate:', lines, sessionLines);
  7856. candidateLines.forEach(function (line) {
  7857. trans.candidates.push(exports.toCandidateJSON(line));
  7858. });
  7859. }
  7860. if (desc.descType == 'datachannel') {
  7861. var sctpmapLines = parsers.findLines('a=sctpmap:', lines);
  7862. sctpmapLines.forEach(function (line) {
  7863. var sctp = parsers.sctpmap(line);
  7864. trans.sctp.push(sctp);
  7865. });
  7866. }
  7867. return content;
  7868. };
  7869. exports.toCandidateJSON = function (line) {
  7870. var candidate = parsers.candidate(line.split('\r\n')[0]);
  7871. candidate.id = (idCounter++).toString(36).substr(0, 12);
  7872. return candidate;
  7873. };
  7874. },{"./parsers":26,"./senders":25}],26:[function(require,module,exports){
  7875. exports.lines = function (sdp) {
  7876. return sdp.split('\r\n').filter(function (line) {
  7877. return line.length > 0;
  7878. });
  7879. };
  7880. exports.findLine = function (prefix, mediaLines, sessionLines) {
  7881. var prefixLength = prefix.length;
  7882. for (var i = 0; i < mediaLines.length; i++) {
  7883. if (mediaLines[i].substr(0, prefixLength) === prefix) {
  7884. return mediaLines[i];
  7885. }
  7886. }
  7887. // Continue searching in parent session section
  7888. if (!sessionLines) {
  7889. return false;
  7890. }
  7891. for (var j = 0; j < sessionLines.length; j++) {
  7892. if (sessionLines[j].substr(0, prefixLength) === prefix) {
  7893. return sessionLines[j];
  7894. }
  7895. }
  7896. return false;
  7897. };
  7898. exports.findLines = function (prefix, mediaLines, sessionLines) {
  7899. var results = [];
  7900. var prefixLength = prefix.length;
  7901. for (var i = 0; i < mediaLines.length; i++) {
  7902. if (mediaLines[i].substr(0, prefixLength) === prefix) {
  7903. results.push(mediaLines[i]);
  7904. }
  7905. }
  7906. if (results.length || !sessionLines) {
  7907. return results;
  7908. }
  7909. for (var j = 0; j < sessionLines.length; j++) {
  7910. if (sessionLines[j].substr(0, prefixLength) === prefix) {
  7911. results.push(sessionLines[j]);
  7912. }
  7913. }
  7914. return results;
  7915. };
  7916. exports.mline = function (line) {
  7917. var parts = line.substr(2).split(' ');
  7918. var parsed = {
  7919. media: parts[0],
  7920. port: parts[1],
  7921. proto: parts[2],
  7922. formats: []
  7923. };
  7924. for (var i = 3; i < parts.length; i++) {
  7925. if (parts[i]) {
  7926. parsed.formats.push(parts[i]);
  7927. }
  7928. }
  7929. return parsed;
  7930. };
  7931. exports.rtpmap = function (line) {
  7932. var parts = line.substr(9).split(' ');
  7933. var parsed = {
  7934. id: parts.shift()
  7935. };
  7936. parts = parts[0].split('/');
  7937. parsed.name = parts[0];
  7938. parsed.clockrate = parts[1];
  7939. parsed.channels = parts.length == 3 ? parts[2] : '1';
  7940. return parsed;
  7941. };
  7942. exports.sctpmap = function (line) {
  7943. // based on -05 draft
  7944. var parts = line.substr(10).split(' ');
  7945. var parsed = {
  7946. number: parts.shift(),
  7947. protocol: parts.shift(),
  7948. streams: parts.shift()
  7949. };
  7950. return parsed;
  7951. };
  7952. exports.fmtp = function (line) {
  7953. var kv, key, value;
  7954. var parts = line.substr(line.indexOf(' ') + 1).split(';');
  7955. var parsed = [];
  7956. for (var i = 0; i < parts.length; i++) {
  7957. kv = parts[i].split('=');
  7958. key = kv[0].trim();
  7959. value = kv[1];
  7960. if (key && value) {
  7961. parsed.push({key: key, value: value});
  7962. } else if (key) {
  7963. parsed.push({key: '', value: key});
  7964. }
  7965. }
  7966. return parsed;
  7967. };
  7968. exports.crypto = function (line) {
  7969. var parts = line.substr(9).split(' ');
  7970. var parsed = {
  7971. tag: parts[0],
  7972. cipherSuite: parts[1],
  7973. keyParams: parts[2],
  7974. sessionParams: parts.slice(3).join(' ')
  7975. };
  7976. return parsed;
  7977. };
  7978. exports.fingerprint = function (line) {
  7979. var parts = line.substr(14).split(' ');
  7980. return {
  7981. hash: parts[0],
  7982. value: parts[1]
  7983. };
  7984. };
  7985. exports.extmap = function (line) {
  7986. var parts = line.substr(9).split(' ');
  7987. var parsed = {};
  7988. var idpart = parts.shift();
  7989. var sp = idpart.indexOf('/');
  7990. if (sp >= 0) {
  7991. parsed.id = idpart.substr(0, sp);
  7992. parsed.senders = idpart.substr(sp + 1);
  7993. } else {
  7994. parsed.id = idpart;
  7995. parsed.senders = 'sendrecv';
  7996. }
  7997. parsed.uri = parts.shift() || '';
  7998. return parsed;
  7999. };
  8000. exports.rtcpfb = function (line) {
  8001. var parts = line.substr(10).split(' ');
  8002. var parsed = {};
  8003. parsed.id = parts.shift();
  8004. parsed.type = parts.shift();
  8005. if (parsed.type === 'trr-int') {
  8006. parsed.value = parts.shift();
  8007. } else {
  8008. parsed.subtype = parts.shift() || '';
  8009. }
  8010. parsed.parameters = parts;
  8011. return parsed;
  8012. };
  8013. exports.candidate = function (line) {
  8014. var parts;
  8015. if (line.indexOf('a=candidate:') === 0) {
  8016. parts = line.substring(12).split(' ');
  8017. } else { // no a=candidate
  8018. parts = line.substring(10).split(' ');
  8019. }
  8020. var candidate = {
  8021. foundation: parts[0],
  8022. component: parts[1],
  8023. protocol: parts[2].toLowerCase(),
  8024. priority: parts[3],
  8025. ip: parts[4],
  8026. port: parts[5],
  8027. // skip parts[6] == 'typ'
  8028. type: parts[7],
  8029. generation: '0'
  8030. };
  8031. for (var i = 8; i < parts.length; i += 2) {
  8032. if (parts[i] === 'raddr') {
  8033. candidate.relAddr = parts[i + 1];
  8034. } else if (parts[i] === 'rport') {
  8035. candidate.relPort = parts[i + 1];
  8036. } else if (parts[i] === 'generation') {
  8037. candidate.generation = parts[i + 1];
  8038. } else if (parts[i] === 'tcptype') {
  8039. candidate.tcpType = parts[i + 1];
  8040. }
  8041. }
  8042. candidate.network = '1';
  8043. return candidate;
  8044. };
  8045. exports.sourceGroups = function (lines) {
  8046. var parsed = [];
  8047. for (var i = 0; i < lines.length; i++) {
  8048. var parts = lines[i].substr(13).split(' ');
  8049. parsed.push({
  8050. semantics: parts.shift(),
  8051. sources: parts
  8052. });
  8053. }
  8054. return parsed;
  8055. };
  8056. exports.sources = function (lines) {
  8057. // http://tools.ietf.org/html/rfc5576
  8058. var parsed = [];
  8059. var sources = {};
  8060. for (var i = 0; i < lines.length; i++) {
  8061. var parts = lines[i].substr(7).split(' ');
  8062. var ssrc = parts.shift();
  8063. if (!sources[ssrc]) {
  8064. var source = {
  8065. ssrc: ssrc,
  8066. parameters: []
  8067. };
  8068. parsed.push(source);
  8069. // Keep an index
  8070. sources[ssrc] = source;
  8071. }
  8072. parts = parts.join(' ').split(':');
  8073. var attribute = parts.shift();
  8074. var value = parts.join(':') || null;
  8075. sources[ssrc].parameters.push({
  8076. key: attribute,
  8077. value: value
  8078. });
  8079. }
  8080. return parsed;
  8081. };
  8082. exports.groups = function (lines) {
  8083. // http://tools.ietf.org/html/rfc5888
  8084. var parsed = [];
  8085. var parts;
  8086. for (var i = 0; i < lines.length; i++) {
  8087. parts = lines[i].substr(8).split(' ');
  8088. parsed.push({
  8089. semantics: parts.shift(),
  8090. contents: parts
  8091. });
  8092. }
  8093. return parsed;
  8094. };
  8095. exports.bandwidth = function (line) {
  8096. var parts = line.substr(2).split(':');
  8097. var parsed = {};
  8098. parsed.type = parts.shift();
  8099. parsed.bandwidth = parts.shift();
  8100. return parsed;
  8101. };
  8102. },{}],25:[function(require,module,exports){
  8103. module.exports = {
  8104. initiator: {
  8105. incoming: {
  8106. initiator: 'recvonly',
  8107. responder: 'sendonly',
  8108. both: 'sendrecv',
  8109. none: 'inactive',
  8110. recvonly: 'initiator',
  8111. sendonly: 'responder',
  8112. sendrecv: 'both',
  8113. inactive: 'none'
  8114. },
  8115. outgoing: {
  8116. initiator: 'sendonly',
  8117. responder: 'recvonly',
  8118. both: 'sendrecv',
  8119. none: 'inactive',
  8120. recvonly: 'responder',
  8121. sendonly: 'initiator',
  8122. sendrecv: 'both',
  8123. inactive: 'none'
  8124. }
  8125. },
  8126. responder: {
  8127. incoming: {
  8128. initiator: 'sendonly',
  8129. responder: 'recvonly',
  8130. both: 'sendrecv',
  8131. none: 'inactive',
  8132. recvonly: 'responder',
  8133. sendonly: 'initiator',
  8134. sendrecv: 'both',
  8135. inactive: 'none'
  8136. },
  8137. outgoing: {
  8138. initiator: 'recvonly',
  8139. responder: 'sendonly',
  8140. both: 'sendrecv',
  8141. none: 'inactive',
  8142. recvonly: 'initiator',
  8143. sendonly: 'responder',
  8144. sendrecv: 'both',
  8145. inactive: 'none'
  8146. }
  8147. }
  8148. };
  8149. },{}],24:[function(require,module,exports){
  8150. // based on https://github.com/ESTOS/strophe.jingle/
  8151. // adds wildemitter support
  8152. var util = require('util');
  8153. var webrtc = require('webrtcsupport');
  8154. var WildEmitter = require('wildemitter');
  8155. function dumpSDP(description) {
  8156. return {
  8157. type: description.type,
  8158. sdp: description.sdp
  8159. };
  8160. }
  8161. function dumpStream(stream) {
  8162. var info = {
  8163. label: stream.id,
  8164. };
  8165. if (stream.getAudioTracks().length) {
  8166. info.audio = stream.getAudioTracks().map(function (track) {
  8167. return track.id;
  8168. });
  8169. }
  8170. if (stream.getVideoTracks().length) {
  8171. info.video = stream.getVideoTracks().map(function (track) {
  8172. return track.id;
  8173. });
  8174. }
  8175. return info;
  8176. }
  8177. function TraceablePeerConnection(config, constraints) {
  8178. var self = this;
  8179. WildEmitter.call(this);
  8180. this.peerconnection = new webrtc.PeerConnection(config, constraints);
  8181. this.trace = function (what, info) {
  8182. self.emit('PeerConnectionTrace', {
  8183. time: new Date(),
  8184. type: what,
  8185. value: info || ""
  8186. });
  8187. };
  8188. this.onicecandidate = null;
  8189. this.peerconnection.onicecandidate = function (event) {
  8190. self.trace('onicecandidate', event.candidate);
  8191. if (self.onicecandidate !== null) {
  8192. self.onicecandidate(event);
  8193. }
  8194. };
  8195. this.onaddstream = null;
  8196. this.peerconnection.onaddstream = function (event) {
  8197. self.trace('onaddstream', dumpStream(event.stream));
  8198. if (self.onaddstream !== null) {
  8199. self.onaddstream(event);
  8200. }
  8201. };
  8202. this.onremovestream = null;
  8203. this.peerconnection.onremovestream = function (event) {
  8204. self.trace('onremovestream', dumpStream(event.stream));
  8205. if (self.onremovestream !== null) {
  8206. self.onremovestream(event);
  8207. }
  8208. };
  8209. this.onsignalingstatechange = null;
  8210. this.peerconnection.onsignalingstatechange = function (event) {
  8211. self.trace('onsignalingstatechange', self.signalingState);
  8212. if (self.onsignalingstatechange !== null) {
  8213. self.onsignalingstatechange(event);
  8214. }
  8215. };
  8216. this.oniceconnectionstatechange = null;
  8217. this.peerconnection.oniceconnectionstatechange = function (event) {
  8218. self.trace('oniceconnectionstatechange', self.iceConnectionState);
  8219. if (self.oniceconnectionstatechange !== null) {
  8220. self.oniceconnectionstatechange(event);
  8221. }
  8222. };
  8223. this.onnegotiationneeded = null;
  8224. this.peerconnection.onnegotiationneeded = function (event) {
  8225. self.trace('onnegotiationneeded');
  8226. if (self.onnegotiationneeded !== null) {
  8227. self.onnegotiationneeded(event);
  8228. }
  8229. };
  8230. self.ondatachannel = null;
  8231. this.peerconnection.ondatachannel = function (event) {
  8232. self.trace('ondatachannel', event);
  8233. if (self.ondatachannel !== null) {
  8234. self.ondatachannel(event);
  8235. }
  8236. };
  8237. this.getLocalStreams = this.peerconnection.getLocalStreams.bind(this.peerconnection);
  8238. this.getRemoteStreams = this.peerconnection.getRemoteStreams.bind(this.peerconnection);
  8239. }
  8240. util.inherits(TraceablePeerConnection, WildEmitter);
  8241. Object.defineProperty(TraceablePeerConnection.prototype, 'signalingState', {
  8242. get: function () {
  8243. return this.peerconnection.signalingState;
  8244. }
  8245. });
  8246. Object.defineProperty(TraceablePeerConnection.prototype, 'iceConnectionState', {
  8247. get: function () {
  8248. return this.peerconnection.iceConnectionState;
  8249. }
  8250. });
  8251. Object.defineProperty(TraceablePeerConnection.prototype, 'localDescription', {
  8252. get: function () {
  8253. return this.peerconnection.localDescription;
  8254. }
  8255. });
  8256. Object.defineProperty(TraceablePeerConnection.prototype, 'remoteDescription', {
  8257. get: function () {
  8258. return this.peerconnection.remoteDescription;
  8259. }
  8260. });
  8261. TraceablePeerConnection.prototype.addStream = function (stream) {
  8262. this.trace('addStream', dumpStream(stream));
  8263. this.peerconnection.addStream(stream);
  8264. };
  8265. TraceablePeerConnection.prototype.removeStream = function (stream) {
  8266. this.trace('removeStream', dumpStream(stream));
  8267. this.peerconnection.removeStream(stream);
  8268. };
  8269. TraceablePeerConnection.prototype.createDataChannel = function (label, opts) {
  8270. this.trace('createDataChannel', label, opts);
  8271. return this.peerconnection.createDataChannel(label, opts);
  8272. };
  8273. TraceablePeerConnection.prototype.setLocalDescription = function (description, successCallback, failureCallback) {
  8274. var self = this;
  8275. this.trace('setLocalDescription', dumpSDP(description));
  8276. this.peerconnection.setLocalDescription(description,
  8277. function () {
  8278. self.trace('setLocalDescriptionOnSuccess');
  8279. successCallback();
  8280. },
  8281. function (err) {
  8282. self.trace('setLocalDescriptionOnFailure', err);
  8283. failureCallback(err);
  8284. }
  8285. );
  8286. };
  8287. TraceablePeerConnection.prototype.setRemoteDescription = function (description, successCallback, failureCallback) {
  8288. var self = this;
  8289. this.trace('setRemoteDescription', dumpSDP(description));
  8290. this.peerconnection.setRemoteDescription(description,
  8291. function () {
  8292. self.trace('setRemoteDescriptionOnSuccess');
  8293. successCallback();
  8294. },
  8295. function (err) {
  8296. self.trace('setRemoteDescriptionOnFailure', err);
  8297. failureCallback(err);
  8298. }
  8299. );
  8300. };
  8301. TraceablePeerConnection.prototype.close = function () {
  8302. this.trace('stop');
  8303. if (this.statsinterval !== null) {
  8304. window.clearInterval(this.statsinterval);
  8305. this.statsinterval = null;
  8306. }
  8307. if (this.peerconnection.signalingState != 'closed') {
  8308. this.peerconnection.close();
  8309. }
  8310. };
  8311. TraceablePeerConnection.prototype.createOffer = function (successCallback, failureCallback, constraints) {
  8312. var self = this;
  8313. this.trace('createOffer', constraints);
  8314. this.peerconnection.createOffer(
  8315. function (offer) {
  8316. self.trace('createOfferOnSuccess', dumpSDP(offer));
  8317. successCallback(offer);
  8318. },
  8319. function (err) {
  8320. self.trace('createOfferOnFailure', err);
  8321. failureCallback(err);
  8322. },
  8323. constraints
  8324. );
  8325. };
  8326. TraceablePeerConnection.prototype.createAnswer = function (successCallback, failureCallback, constraints) {
  8327. var self = this;
  8328. this.trace('createAnswer', constraints);
  8329. this.peerconnection.createAnswer(
  8330. function (answer) {
  8331. self.trace('createAnswerOnSuccess', dumpSDP(answer));
  8332. successCallback(answer);
  8333. },
  8334. function (err) {
  8335. self.trace('createAnswerOnFailure', err);
  8336. failureCallback(err);
  8337. },
  8338. constraints
  8339. );
  8340. };
  8341. TraceablePeerConnection.prototype.addIceCandidate = function (candidate, successCallback, failureCallback) {
  8342. var self = this;
  8343. this.trace('addIceCandidate', candidate);
  8344. this.peerconnection.addIceCandidate(candidate,
  8345. function () {
  8346. //self.trace('addIceCandidateOnSuccess');
  8347. if (successCallback) successCallback();
  8348. },
  8349. function (err) {
  8350. self.trace('addIceCandidateOnFailure', err);
  8351. if (failureCallback) failureCallback(err);
  8352. }
  8353. );
  8354. };
  8355. TraceablePeerConnection.prototype.getStats = function (callback, errback) {
  8356. if (navigator.mozGetUserMedia) {
  8357. this.peerconnection.getStats(null, callback, errback);
  8358. } else {
  8359. this.peerconnection.getStats(callback);
  8360. }
  8361. };
  8362. module.exports = TraceablePeerConnection;
  8363. },{"util":9,"webrtcsupport":5,"wildemitter":3}]},{},[1])(1)
  8364. });
  8365. ;