您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

xmpp.bundle.js 476KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082
  1. !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.xmpp=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
  2. /* jshint -W117 */
  3. var TraceablePeerConnection = require("./TraceablePeerConnection");
  4. var SDPDiffer = require("./SDPDiffer");
  5. var SDPUtil = require("./SDPUtil");
  6. var SDP = require("./SDP");
  7. // Jingle stuff
  8. function JingleSession(me, sid, connection, service) {
  9. this.me = me;
  10. this.sid = sid;
  11. this.connection = connection;
  12. this.initiator = null;
  13. this.responder = null;
  14. this.isInitiator = null;
  15. this.peerjid = null;
  16. this.state = null;
  17. this.localSDP = null;
  18. this.remoteSDP = null;
  19. this.relayedStreams = [];
  20. this.startTime = null;
  21. this.stopTime = null;
  22. this.media_constraints = null;
  23. this.pc_constraints = null;
  24. this.ice_config = {};
  25. this.drip_container = [];
  26. this.service = service;
  27. this.usetrickle = true;
  28. this.usepranswer = false; // early transport warmup -- mind you, this might fail. depends on webrtc issue 1718
  29. this.usedrip = false; // dripping is sending trickle candidates not one-by-one
  30. this.hadstuncandidate = false;
  31. this.hadturncandidate = false;
  32. this.lasticecandidate = false;
  33. this.statsinterval = null;
  34. this.reason = null;
  35. this.addssrc = [];
  36. this.removessrc = [];
  37. this.pendingop = null;
  38. this.switchstreams = false;
  39. this.wait = true;
  40. this.localStreamsSSRC = null;
  41. /**
  42. * The indicator which determines whether the (local) video has been muted
  43. * in response to a user command in contrast to an automatic decision made
  44. * by the application logic.
  45. */
  46. this.videoMuteByUser = false;
  47. }
  48. JingleSession.prototype.initiate = function (peerjid, isInitiator) {
  49. var self = this;
  50. if (this.state !== null) {
  51. console.error('attempt to initiate on session ' + this.sid +
  52. 'in state ' + this.state);
  53. return;
  54. }
  55. this.isInitiator = isInitiator;
  56. this.state = 'pending';
  57. this.initiator = isInitiator ? this.me : peerjid;
  58. this.responder = !isInitiator ? this.me : peerjid;
  59. this.peerjid = peerjid;
  60. this.hadstuncandidate = false;
  61. this.hadturncandidate = false;
  62. this.lasticecandidate = false;
  63. this.peerconnection
  64. = new TraceablePeerConnection(
  65. this.connection.jingle.ice_config,
  66. this.connection.jingle.pc_constraints );
  67. this.peerconnection.onicecandidate = function (event) {
  68. self.sendIceCandidate(event.candidate);
  69. };
  70. this.peerconnection.onaddstream = function (event) {
  71. console.log("REMOTE STREAM ADDED: " + event.stream + " - " + event.stream.id);
  72. self.remoteStreamAdded(event);
  73. };
  74. this.peerconnection.onremovestream = function (event) {
  75. // Remove the stream from remoteStreams
  76. // FIXME: remotestreamremoved.jingle not defined anywhere(unused)
  77. $(document).trigger('remotestreamremoved.jingle', [event, self.sid]);
  78. };
  79. this.peerconnection.onsignalingstatechange = function (event) {
  80. if (!(self && self.peerconnection)) return;
  81. };
  82. this.peerconnection.oniceconnectionstatechange = function (event) {
  83. if (!(self && self.peerconnection)) return;
  84. switch (self.peerconnection.iceConnectionState) {
  85. case 'connected':
  86. this.startTime = new Date();
  87. break;
  88. case 'disconnected':
  89. this.stopTime = new Date();
  90. break;
  91. }
  92. onIceConnectionStateChange(self.sid, self);
  93. };
  94. // add any local and relayed stream
  95. RTC.localStreams.forEach(function(stream) {
  96. self.peerconnection.addStream(stream.getOriginalStream());
  97. });
  98. this.relayedStreams.forEach(function(stream) {
  99. self.peerconnection.addStream(stream);
  100. });
  101. };
  102. function onIceConnectionStateChange(sid, session) {
  103. switch (session.peerconnection.iceConnectionState) {
  104. case 'checking':
  105. session.timeChecking = (new Date()).getTime();
  106. session.firstconnect = true;
  107. break;
  108. case 'completed': // on caller side
  109. case 'connected':
  110. if (session.firstconnect) {
  111. session.firstconnect = false;
  112. var metadata = {};
  113. metadata.setupTime
  114. = (new Date()).getTime() - session.timeChecking;
  115. session.peerconnection.getStats(function (res) {
  116. if(res && res.result) {
  117. res.result().forEach(function (report) {
  118. if (report.type == 'googCandidatePair' &&
  119. report.stat('googActiveConnection') == 'true') {
  120. metadata.localCandidateType
  121. = report.stat('googLocalCandidateType');
  122. metadata.remoteCandidateType
  123. = report.stat('googRemoteCandidateType');
  124. // log pair as well so we can get nice pie
  125. // charts
  126. metadata.candidatePair
  127. = report.stat('googLocalCandidateType') +
  128. ';' +
  129. report.stat('googRemoteCandidateType');
  130. if (report.stat('googRemoteAddress').indexOf('[') === 0)
  131. {
  132. metadata.ipv6 = true;
  133. }
  134. }
  135. });
  136. }
  137. });
  138. }
  139. break;
  140. }
  141. }
  142. JingleSession.prototype.accept = function () {
  143. var self = this;
  144. this.state = 'active';
  145. var pranswer = this.peerconnection.localDescription;
  146. if (!pranswer || pranswer.type != 'pranswer') {
  147. return;
  148. }
  149. console.log('going from pranswer to answer');
  150. if (this.usetrickle) {
  151. // remove candidates already sent from session-accept
  152. var lines = SDPUtil.find_lines(pranswer.sdp, 'a=candidate:');
  153. for (var i = 0; i < lines.length; i++) {
  154. pranswer.sdp = pranswer.sdp.replace(lines[i] + '\r\n', '');
  155. }
  156. }
  157. while (SDPUtil.find_line(pranswer.sdp, 'a=inactive')) {
  158. // FIXME: change any inactive to sendrecv or whatever they were originally
  159. pranswer.sdp = pranswer.sdp.replace('a=inactive', 'a=sendrecv');
  160. }
  161. pranswer = simulcast.reverseTransformLocalDescription(pranswer);
  162. var prsdp = new SDP(pranswer.sdp);
  163. var accept = $iq({to: this.peerjid,
  164. type: 'set'})
  165. .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
  166. action: 'session-accept',
  167. initiator: this.initiator,
  168. responder: this.responder,
  169. sid: this.sid });
  170. prsdp.toJingle(accept, this.initiator == this.me ? 'initiator' : 'responder', this.localStreamsSSRC);
  171. var sdp = this.peerconnection.localDescription.sdp;
  172. while (SDPUtil.find_line(sdp, 'a=inactive')) {
  173. // FIXME: change any inactive to sendrecv or whatever they were originally
  174. sdp = sdp.replace('a=inactive', 'a=sendrecv');
  175. }
  176. var self = this;
  177. this.peerconnection.setLocalDescription(new RTCSessionDescription({type: 'answer', sdp: sdp}),
  178. function () {
  179. //console.log('setLocalDescription success');
  180. self.setLocalDescription();
  181. self.connection.sendIQ(accept,
  182. function () {
  183. var ack = {};
  184. ack.source = 'answer';
  185. $(document).trigger('ack.jingle', [self.sid, ack]);
  186. },
  187. function (stanza) {
  188. var error = ($(stanza).find('error').length) ? {
  189. code: $(stanza).find('error').attr('code'),
  190. reason: $(stanza).find('error :first')[0].tagName
  191. }:{};
  192. error.source = 'answer';
  193. JingleSession.onJingleError(self.sid, error);
  194. },
  195. 10000);
  196. },
  197. function (e) {
  198. console.error('setLocalDescription failed', e);
  199. }
  200. );
  201. };
  202. JingleSession.prototype.terminate = function (reason) {
  203. this.state = 'ended';
  204. this.reason = reason;
  205. this.peerconnection.close();
  206. if (this.statsinterval !== null) {
  207. window.clearInterval(this.statsinterval);
  208. this.statsinterval = null;
  209. }
  210. };
  211. JingleSession.prototype.active = function () {
  212. return this.state == 'active';
  213. };
  214. JingleSession.prototype.sendIceCandidate = function (candidate) {
  215. var self = this;
  216. if (candidate && !this.lasticecandidate) {
  217. var ice = SDPUtil.iceparams(this.localSDP.media[candidate.sdpMLineIndex], this.localSDP.session);
  218. var jcand = SDPUtil.candidateToJingle(candidate.candidate);
  219. if (!(ice && jcand)) {
  220. console.error('failed to get ice && jcand');
  221. return;
  222. }
  223. ice.xmlns = 'urn:xmpp:jingle:transports:ice-udp:1';
  224. if (jcand.type === 'srflx') {
  225. this.hadstuncandidate = true;
  226. } else if (jcand.type === 'relay') {
  227. this.hadturncandidate = true;
  228. }
  229. if (this.usetrickle) {
  230. if (this.usedrip) {
  231. if (this.drip_container.length === 0) {
  232. // start 20ms callout
  233. window.setTimeout(function () {
  234. if (self.drip_container.length === 0) return;
  235. self.sendIceCandidates(self.drip_container);
  236. self.drip_container = [];
  237. }, 20);
  238. }
  239. this.drip_container.push(candidate);
  240. return;
  241. } else {
  242. self.sendIceCandidate([candidate]);
  243. }
  244. }
  245. } else {
  246. //console.log('sendIceCandidate: last candidate.');
  247. if (!this.usetrickle) {
  248. //console.log('should send full offer now...');
  249. var init = $iq({to: this.peerjid,
  250. type: 'set'})
  251. .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
  252. action: this.peerconnection.localDescription.type == 'offer' ? 'session-initiate' : 'session-accept',
  253. initiator: this.initiator,
  254. sid: this.sid});
  255. this.localSDP = new SDP(this.peerconnection.localDescription.sdp);
  256. var self = this;
  257. var sendJingle = function (ssrc) {
  258. if(!ssrc)
  259. ssrc = {};
  260. self.localSDP.toJingle(init, self.initiator == self.me ? 'initiator' : 'responder', ssrc);
  261. self.connection.sendIQ(init,
  262. function () {
  263. //console.log('session initiate ack');
  264. var ack = {};
  265. ack.source = 'offer';
  266. $(document).trigger('ack.jingle', [self.sid, ack]);
  267. },
  268. function (stanza) {
  269. self.state = 'error';
  270. self.peerconnection.close();
  271. var error = ($(stanza).find('error').length) ? {
  272. code: $(stanza).find('error').attr('code'),
  273. reason: $(stanza).find('error :first')[0].tagName,
  274. }:{};
  275. error.source = 'offer';
  276. JingleSession.onJingleError(self.sid, error);
  277. },
  278. 10000);
  279. }
  280. sendJingle();
  281. }
  282. this.lasticecandidate = true;
  283. console.log('Have we encountered any srflx candidates? ' + this.hadstuncandidate);
  284. console.log('Have we encountered any relay candidates? ' + this.hadturncandidate);
  285. if (!(this.hadstuncandidate || this.hadturncandidate) && this.peerconnection.signalingState != 'closed') {
  286. $(document).trigger('nostuncandidates.jingle', [this.sid]);
  287. }
  288. }
  289. };
  290. JingleSession.prototype.sendIceCandidates = function (candidates) {
  291. console.log('sendIceCandidates', candidates);
  292. var cand = $iq({to: this.peerjid, type: 'set'})
  293. .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
  294. action: 'transport-info',
  295. initiator: this.initiator,
  296. sid: this.sid});
  297. for (var mid = 0; mid < this.localSDP.media.length; mid++) {
  298. var cands = candidates.filter(function (el) { return el.sdpMLineIndex == mid; });
  299. var mline = SDPUtil.parse_mline(this.localSDP.media[mid].split('\r\n')[0]);
  300. if (cands.length > 0) {
  301. var ice = SDPUtil.iceparams(this.localSDP.media[mid], this.localSDP.session);
  302. ice.xmlns = 'urn:xmpp:jingle:transports:ice-udp:1';
  303. cand.c('content', {creator: this.initiator == this.me ? 'initiator' : 'responder',
  304. name: (cands[0].sdpMid? cands[0].sdpMid : mline.media)
  305. }).c('transport', ice);
  306. for (var i = 0; i < cands.length; i++) {
  307. cand.c('candidate', SDPUtil.candidateToJingle(cands[i].candidate)).up();
  308. }
  309. // add fingerprint
  310. if (SDPUtil.find_line(this.localSDP.media[mid], 'a=fingerprint:', this.localSDP.session)) {
  311. var tmp = SDPUtil.parse_fingerprint(SDPUtil.find_line(this.localSDP.media[mid], 'a=fingerprint:', this.localSDP.session));
  312. tmp.required = true;
  313. cand.c(
  314. 'fingerprint',
  315. {xmlns: 'urn:xmpp:jingle:apps:dtls:0'})
  316. .t(tmp.fingerprint);
  317. delete tmp.fingerprint;
  318. cand.attrs(tmp);
  319. cand.up();
  320. }
  321. cand.up(); // transport
  322. cand.up(); // content
  323. }
  324. }
  325. // might merge last-candidate notification into this, but it is called alot later. See webrtc issue #2340
  326. //console.log('was this the last candidate', this.lasticecandidate);
  327. this.connection.sendIQ(cand,
  328. function () {
  329. var ack = {};
  330. ack.source = 'transportinfo';
  331. $(document).trigger('ack.jingle', [this.sid, ack]);
  332. },
  333. function (stanza) {
  334. var error = ($(stanza).find('error').length) ? {
  335. code: $(stanza).find('error').attr('code'),
  336. reason: $(stanza).find('error :first')[0].tagName,
  337. }:{};
  338. error.source = 'transportinfo';
  339. JingleSession.onJingleError(this.sid, error);
  340. },
  341. 10000);
  342. };
  343. JingleSession.prototype.sendOffer = function () {
  344. //console.log('sendOffer...');
  345. var self = this;
  346. this.peerconnection.createOffer(function (sdp) {
  347. self.createdOffer(sdp);
  348. },
  349. function (e) {
  350. console.error('createOffer failed', e);
  351. },
  352. this.media_constraints
  353. );
  354. };
  355. JingleSession.prototype.createdOffer = function (sdp) {
  356. //console.log('createdOffer', sdp);
  357. var self = this;
  358. this.localSDP = new SDP(sdp.sdp);
  359. //this.localSDP.mangle();
  360. var sendJingle = function () {
  361. var init = $iq({to: this.peerjid,
  362. type: 'set'})
  363. .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
  364. action: 'session-initiate',
  365. initiator: this.initiator,
  366. sid: this.sid});
  367. self.localSDP.toJingle(init, this.initiator == this.me ? 'initiator' : 'responder', this.localStreamsSSRC);
  368. self.connection.sendIQ(init,
  369. function () {
  370. var ack = {};
  371. ack.source = 'offer';
  372. $(document).trigger('ack.jingle', [self.sid, ack]);
  373. },
  374. function (stanza) {
  375. self.state = 'error';
  376. self.peerconnection.close();
  377. var error = ($(stanza).find('error').length) ? {
  378. code: $(stanza).find('error').attr('code'),
  379. reason: $(stanza).find('error :first')[0].tagName,
  380. }:{};
  381. error.source = 'offer';
  382. JingleSession.onJingleError(self.sid, error);
  383. },
  384. 10000);
  385. }
  386. sdp.sdp = this.localSDP.raw;
  387. this.peerconnection.setLocalDescription(sdp,
  388. function () {
  389. if(self.usetrickle)
  390. {
  391. sendJingle();
  392. }
  393. self.setLocalDescription();
  394. //console.log('setLocalDescription success');
  395. },
  396. function (e) {
  397. console.error('setLocalDescription failed', e);
  398. }
  399. );
  400. var cands = SDPUtil.find_lines(this.localSDP.raw, 'a=candidate:');
  401. for (var i = 0; i < cands.length; i++) {
  402. var cand = SDPUtil.parse_icecandidate(cands[i]);
  403. if (cand.type == 'srflx') {
  404. this.hadstuncandidate = true;
  405. } else if (cand.type == 'relay') {
  406. this.hadturncandidate = true;
  407. }
  408. }
  409. };
  410. JingleSession.prototype.setRemoteDescription = function (elem, desctype) {
  411. //console.log('setting remote description... ', desctype);
  412. this.remoteSDP = new SDP('');
  413. this.remoteSDP.fromJingle(elem);
  414. if (this.peerconnection.remoteDescription !== null) {
  415. console.log('setRemoteDescription when remote description is not null, should be pranswer', this.peerconnection.remoteDescription);
  416. if (this.peerconnection.remoteDescription.type == 'pranswer') {
  417. var pranswer = new SDP(this.peerconnection.remoteDescription.sdp);
  418. for (var i = 0; i < pranswer.media.length; i++) {
  419. // make sure we have ice ufrag and pwd
  420. if (!SDPUtil.find_line(this.remoteSDP.media[i], 'a=ice-ufrag:', this.remoteSDP.session)) {
  421. if (SDPUtil.find_line(pranswer.media[i], 'a=ice-ufrag:', pranswer.session)) {
  422. this.remoteSDP.media[i] += SDPUtil.find_line(pranswer.media[i], 'a=ice-ufrag:', pranswer.session) + '\r\n';
  423. } else {
  424. console.warn('no ice ufrag?');
  425. }
  426. if (SDPUtil.find_line(pranswer.media[i], 'a=ice-pwd:', pranswer.session)) {
  427. this.remoteSDP.media[i] += SDPUtil.find_line(pranswer.media[i], 'a=ice-pwd:', pranswer.session) + '\r\n';
  428. } else {
  429. console.warn('no ice pwd?');
  430. }
  431. }
  432. // copy over candidates
  433. var lines = SDPUtil.find_lines(pranswer.media[i], 'a=candidate:');
  434. for (var j = 0; j < lines.length; j++) {
  435. this.remoteSDP.media[i] += lines[j] + '\r\n';
  436. }
  437. }
  438. this.remoteSDP.raw = this.remoteSDP.session + this.remoteSDP.media.join('');
  439. }
  440. }
  441. var remotedesc = new RTCSessionDescription({type: desctype, sdp: this.remoteSDP.raw});
  442. this.peerconnection.setRemoteDescription(remotedesc,
  443. function () {
  444. //console.log('setRemoteDescription success');
  445. },
  446. function (e) {
  447. console.error('setRemoteDescription error', e);
  448. JingleSession.onJingleFatalError(self, e);
  449. }
  450. );
  451. };
  452. JingleSession.prototype.addIceCandidate = function (elem) {
  453. var self = this;
  454. if (this.peerconnection.signalingState == 'closed') {
  455. return;
  456. }
  457. if (!this.peerconnection.remoteDescription && this.peerconnection.signalingState == 'have-local-offer') {
  458. console.log('trickle ice candidate arriving before session accept...');
  459. // create a PRANSWER for setRemoteDescription
  460. if (!this.remoteSDP) {
  461. var cobbled = 'v=0\r\n' +
  462. 'o=- ' + '1923518516' + ' 2 IN IP4 0.0.0.0\r\n' +// FIXME
  463. 's=-\r\n' +
  464. 't=0 0\r\n';
  465. // first, take some things from the local description
  466. for (var i = 0; i < this.localSDP.media.length; i++) {
  467. cobbled += SDPUtil.find_line(this.localSDP.media[i], 'm=') + '\r\n';
  468. cobbled += SDPUtil.find_lines(this.localSDP.media[i], 'a=rtpmap:').join('\r\n') + '\r\n';
  469. if (SDPUtil.find_line(this.localSDP.media[i], 'a=mid:')) {
  470. cobbled += SDPUtil.find_line(this.localSDP.media[i], 'a=mid:') + '\r\n';
  471. }
  472. cobbled += 'a=inactive\r\n';
  473. }
  474. this.remoteSDP = new SDP(cobbled);
  475. }
  476. // then add things like ice and dtls from remote candidate
  477. elem.each(function () {
  478. for (var i = 0; i < self.remoteSDP.media.length; i++) {
  479. if (SDPUtil.find_line(self.remoteSDP.media[i], 'a=mid:' + $(this).attr('name')) ||
  480. self.remoteSDP.media[i].indexOf('m=' + $(this).attr('name')) === 0) {
  481. if (!SDPUtil.find_line(self.remoteSDP.media[i], 'a=ice-ufrag:')) {
  482. var tmp = $(this).find('transport');
  483. self.remoteSDP.media[i] += 'a=ice-ufrag:' + tmp.attr('ufrag') + '\r\n';
  484. self.remoteSDP.media[i] += 'a=ice-pwd:' + tmp.attr('pwd') + '\r\n';
  485. tmp = $(this).find('transport>fingerprint');
  486. if (tmp.length) {
  487. self.remoteSDP.media[i] += 'a=fingerprint:' + tmp.attr('hash') + ' ' + tmp.text() + '\r\n';
  488. } else {
  489. console.log('no dtls fingerprint (webrtc issue #1718?)');
  490. self.remoteSDP.media[i] += 'a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:BAADBAADBAADBAADBAADBAADBAADBAADBAADBAAD\r\n';
  491. }
  492. break;
  493. }
  494. }
  495. }
  496. });
  497. this.remoteSDP.raw = this.remoteSDP.session + this.remoteSDP.media.join('');
  498. // we need a complete SDP with ice-ufrag/ice-pwd in all parts
  499. // this makes the assumption that the PRANSWER is constructed such that the ice-ufrag is in all mediaparts
  500. // but it could be in the session part as well. since the code above constructs this sdp this can't happen however
  501. var iscomplete = this.remoteSDP.media.filter(function (mediapart) {
  502. return SDPUtil.find_line(mediapart, 'a=ice-ufrag:');
  503. }).length == this.remoteSDP.media.length;
  504. if (iscomplete) {
  505. console.log('setting pranswer');
  506. try {
  507. this.peerconnection.setRemoteDescription(new RTCSessionDescription({type: 'pranswer', sdp: this.remoteSDP.raw }),
  508. function() {
  509. },
  510. function(e) {
  511. console.log('setRemoteDescription pranswer failed', e.toString());
  512. });
  513. } catch (e) {
  514. console.error('setting pranswer failed', e);
  515. }
  516. } else {
  517. //console.log('not yet setting pranswer');
  518. }
  519. }
  520. // operate on each content element
  521. elem.each(function () {
  522. // would love to deactivate this, but firefox still requires it
  523. var idx = -1;
  524. var i;
  525. for (i = 0; i < self.remoteSDP.media.length; i++) {
  526. if (SDPUtil.find_line(self.remoteSDP.media[i], 'a=mid:' + $(this).attr('name')) ||
  527. self.remoteSDP.media[i].indexOf('m=' + $(this).attr('name')) === 0) {
  528. idx = i;
  529. break;
  530. }
  531. }
  532. if (idx == -1) { // fall back to localdescription
  533. for (i = 0; i < self.localSDP.media.length; i++) {
  534. if (SDPUtil.find_line(self.localSDP.media[i], 'a=mid:' + $(this).attr('name')) ||
  535. self.localSDP.media[i].indexOf('m=' + $(this).attr('name')) === 0) {
  536. idx = i;
  537. break;
  538. }
  539. }
  540. }
  541. var name = $(this).attr('name');
  542. // TODO: check ice-pwd and ice-ufrag?
  543. $(this).find('transport>candidate').each(function () {
  544. var line, candidate;
  545. line = SDPUtil.candidateFromJingle(this);
  546. candidate = new RTCIceCandidate({sdpMLineIndex: idx,
  547. sdpMid: name,
  548. candidate: line});
  549. try {
  550. self.peerconnection.addIceCandidate(candidate);
  551. } catch (e) {
  552. console.error('addIceCandidate failed', e.toString(), line);
  553. }
  554. });
  555. });
  556. };
  557. JingleSession.prototype.sendAnswer = function (provisional) {
  558. //console.log('createAnswer', provisional);
  559. var self = this;
  560. this.peerconnection.createAnswer(
  561. function (sdp) {
  562. self.createdAnswer(sdp, provisional);
  563. },
  564. function (e) {
  565. console.error('createAnswer failed', e);
  566. },
  567. this.media_constraints
  568. );
  569. };
  570. JingleSession.prototype.createdAnswer = function (sdp, provisional) {
  571. //console.log('createAnswer callback');
  572. var self = this;
  573. this.localSDP = new SDP(sdp.sdp);
  574. //this.localSDP.mangle();
  575. this.usepranswer = provisional === true;
  576. if (this.usetrickle) {
  577. if (this.usepranswer) {
  578. sdp.type = 'pranswer';
  579. for (var i = 0; i < this.localSDP.media.length; i++) {
  580. this.localSDP.media[i] = this.localSDP.media[i].replace('a=sendrecv\r\n', 'a=inactive\r\n');
  581. }
  582. this.localSDP.raw = this.localSDP.session + '\r\n' + this.localSDP.media.join('');
  583. }
  584. }
  585. var self = this;
  586. var sendJingle = function (ssrcs) {
  587. var accept = $iq({to: self.peerjid,
  588. type: 'set'})
  589. .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
  590. action: 'session-accept',
  591. initiator: self.initiator,
  592. responder: self.responder,
  593. sid: self.sid });
  594. var publicLocalDesc = simulcast.reverseTransformLocalDescription(sdp);
  595. var publicLocalSDP = new SDP(publicLocalDesc.sdp);
  596. publicLocalSDP.toJingle(accept, self.initiator == self.me ? 'initiator' : 'responder', ssrcs);
  597. self.connection.sendIQ(accept,
  598. function () {
  599. var ack = {};
  600. ack.source = 'answer';
  601. $(document).trigger('ack.jingle', [self.sid, ack]);
  602. },
  603. function (stanza) {
  604. var error = ($(stanza).find('error').length) ? {
  605. code: $(stanza).find('error').attr('code'),
  606. reason: $(stanza).find('error :first')[0].tagName,
  607. }:{};
  608. error.source = 'answer';
  609. JingleSession.onJingleError(self.sid, error);
  610. },
  611. 10000);
  612. }
  613. sdp.sdp = this.localSDP.raw;
  614. this.peerconnection.setLocalDescription(sdp,
  615. function () {
  616. //console.log('setLocalDescription success');
  617. if (self.usetrickle && !self.usepranswer) {
  618. sendJingle();
  619. }
  620. self.setLocalDescription();
  621. },
  622. function (e) {
  623. console.error('setLocalDescription failed', e);
  624. }
  625. );
  626. var cands = SDPUtil.find_lines(this.localSDP.raw, 'a=candidate:');
  627. for (var j = 0; j < cands.length; j++) {
  628. var cand = SDPUtil.parse_icecandidate(cands[j]);
  629. if (cand.type == 'srflx') {
  630. this.hadstuncandidate = true;
  631. } else if (cand.type == 'relay') {
  632. this.hadturncandidate = true;
  633. }
  634. }
  635. };
  636. JingleSession.prototype.sendTerminate = function (reason, text) {
  637. var self = this,
  638. term = $iq({to: this.peerjid,
  639. type: 'set'})
  640. .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
  641. action: 'session-terminate',
  642. initiator: this.initiator,
  643. sid: this.sid})
  644. .c('reason')
  645. .c(reason || 'success');
  646. if (text) {
  647. term.up().c('text').t(text);
  648. }
  649. this.connection.sendIQ(term,
  650. function () {
  651. self.peerconnection.close();
  652. self.peerconnection = null;
  653. self.terminate();
  654. var ack = {};
  655. ack.source = 'terminate';
  656. $(document).trigger('ack.jingle', [self.sid, ack]);
  657. },
  658. function (stanza) {
  659. var error = ($(stanza).find('error').length) ? {
  660. code: $(stanza).find('error').attr('code'),
  661. reason: $(stanza).find('error :first')[0].tagName,
  662. }:{};
  663. $(document).trigger('ack.jingle', [self.sid, error]);
  664. },
  665. 10000);
  666. if (this.statsinterval !== null) {
  667. window.clearInterval(this.statsinterval);
  668. this.statsinterval = null;
  669. }
  670. };
  671. JingleSession.prototype.addSource = function (elem, fromJid) {
  672. var self = this;
  673. // FIXME: dirty waiting
  674. if (!this.peerconnection.localDescription)
  675. {
  676. console.warn("addSource - localDescription not ready yet")
  677. setTimeout(function()
  678. {
  679. self.addSource(elem, fromJid);
  680. },
  681. 200
  682. );
  683. return;
  684. }
  685. console.log('addssrc', new Date().getTime());
  686. console.log('ice', this.peerconnection.iceConnectionState);
  687. var sdp = new SDP(this.peerconnection.remoteDescription.sdp);
  688. var mySdp = new SDP(this.peerconnection.localDescription.sdp);
  689. $(elem).each(function (idx, content) {
  690. var name = $(content).attr('name');
  691. var lines = '';
  692. tmp = $(content).find('ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function() {
  693. var semantics = this.getAttribute('semantics');
  694. var ssrcs = $(this).find('>source').map(function () {
  695. return this.getAttribute('ssrc');
  696. }).get();
  697. if (ssrcs.length != 0) {
  698. lines += 'a=ssrc-group:' + semantics + ' ' + ssrcs.join(' ') + '\r\n';
  699. }
  700. });
  701. tmp = $(content).find('source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]'); // can handle both >source and >description>source
  702. tmp.each(function () {
  703. var ssrc = $(this).attr('ssrc');
  704. if(mySdp.containsSSRC(ssrc)){
  705. /**
  706. * This happens when multiple participants change their streams at the same time and
  707. * ColibriFocus.modifySources have to wait for stable state. In the meantime multiple
  708. * addssrc are scheduled for update IQ. See
  709. */
  710. console.warn("Got add stream request for my own ssrc: "+ssrc);
  711. return;
  712. }
  713. $(this).find('>parameter').each(function () {
  714. lines += 'a=ssrc:' + ssrc + ' ' + $(this).attr('name');
  715. if ($(this).attr('value') && $(this).attr('value').length)
  716. lines += ':' + $(this).attr('value');
  717. lines += '\r\n';
  718. });
  719. });
  720. sdp.media.forEach(function(media, idx) {
  721. if (!SDPUtil.find_line(media, 'a=mid:' + name))
  722. return;
  723. sdp.media[idx] += lines;
  724. if (!self.addssrc[idx]) self.addssrc[idx] = '';
  725. self.addssrc[idx] += lines;
  726. });
  727. sdp.raw = sdp.session + sdp.media.join('');
  728. });
  729. this.modifySources();
  730. };
  731. JingleSession.prototype.removeSource = function (elem, fromJid) {
  732. var self = this;
  733. // FIXME: dirty waiting
  734. if (!this.peerconnection.localDescription)
  735. {
  736. console.warn("removeSource - localDescription not ready yet")
  737. setTimeout(function()
  738. {
  739. self.removeSource(elem, fromJid);
  740. },
  741. 200
  742. );
  743. return;
  744. }
  745. console.log('removessrc', new Date().getTime());
  746. console.log('ice', this.peerconnection.iceConnectionState);
  747. var sdp = new SDP(this.peerconnection.remoteDescription.sdp);
  748. var mySdp = new SDP(this.peerconnection.localDescription.sdp);
  749. $(elem).each(function (idx, content) {
  750. var name = $(content).attr('name');
  751. var lines = '';
  752. tmp = $(content).find('ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function() {
  753. var semantics = this.getAttribute('semantics');
  754. var ssrcs = $(this).find('>source').map(function () {
  755. return this.getAttribute('ssrc');
  756. }).get();
  757. if (ssrcs.length != 0) {
  758. lines += 'a=ssrc-group:' + semantics + ' ' + ssrcs.join(' ') + '\r\n';
  759. }
  760. });
  761. tmp = $(content).find('source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]'); // can handle both >source and >description>source
  762. tmp.each(function () {
  763. var ssrc = $(this).attr('ssrc');
  764. // This should never happen, but can be useful for bug detection
  765. if(mySdp.containsSSRC(ssrc)){
  766. console.error("Got remove stream request for my own ssrc: "+ssrc);
  767. return;
  768. }
  769. $(this).find('>parameter').each(function () {
  770. lines += 'a=ssrc:' + ssrc + ' ' + $(this).attr('name');
  771. if ($(this).attr('value') && $(this).attr('value').length)
  772. lines += ':' + $(this).attr('value');
  773. lines += '\r\n';
  774. });
  775. });
  776. sdp.media.forEach(function(media, idx) {
  777. if (!SDPUtil.find_line(media, 'a=mid:' + name))
  778. return;
  779. sdp.media[idx] += lines;
  780. if (!self.removessrc[idx]) self.removessrc[idx] = '';
  781. self.removessrc[idx] += lines;
  782. });
  783. sdp.raw = sdp.session + sdp.media.join('');
  784. });
  785. this.modifySources();
  786. };
  787. JingleSession.prototype.modifySources = function (successCallback) {
  788. var self = this;
  789. if (this.peerconnection.signalingState == 'closed') return;
  790. if (!(this.addssrc.length || this.removessrc.length || this.pendingop !== null || this.switchstreams)){
  791. // There is nothing to do since scheduled job might have been executed by another succeeding call
  792. this.setLocalDescription();
  793. if(successCallback){
  794. successCallback();
  795. }
  796. return;
  797. }
  798. // FIXME: this is a big hack
  799. // https://code.google.com/p/webrtc/issues/detail?id=2688
  800. // ^ has been fixed.
  801. if (!(this.peerconnection.signalingState == 'stable' && this.peerconnection.iceConnectionState == 'connected')) {
  802. console.warn('modifySources not yet', this.peerconnection.signalingState, this.peerconnection.iceConnectionState);
  803. this.wait = true;
  804. window.setTimeout(function() { self.modifySources(successCallback); }, 250);
  805. return;
  806. }
  807. if (this.wait) {
  808. window.setTimeout(function() { self.modifySources(successCallback); }, 2500);
  809. this.wait = false;
  810. return;
  811. }
  812. // Reset switch streams flag
  813. this.switchstreams = false;
  814. var sdp = new SDP(this.peerconnection.remoteDescription.sdp);
  815. // add sources
  816. this.addssrc.forEach(function(lines, idx) {
  817. sdp.media[idx] += lines;
  818. });
  819. this.addssrc = [];
  820. // remove sources
  821. this.removessrc.forEach(function(lines, idx) {
  822. lines = lines.split('\r\n');
  823. lines.pop(); // remove empty last element;
  824. lines.forEach(function(line) {
  825. sdp.media[idx] = sdp.media[idx].replace(line + '\r\n', '');
  826. });
  827. });
  828. this.removessrc = [];
  829. // FIXME:
  830. // this was a hack for the situation when only one peer exists
  831. // in the conference.
  832. // check if still required and remove
  833. if (sdp.media[0])
  834. sdp.media[0] = sdp.media[0].replace('a=recvonly', 'a=sendrecv');
  835. if (sdp.media[1])
  836. sdp.media[1] = sdp.media[1].replace('a=recvonly', 'a=sendrecv');
  837. sdp.raw = sdp.session + sdp.media.join('');
  838. this.peerconnection.setRemoteDescription(new RTCSessionDescription({type: 'offer', sdp: sdp.raw}),
  839. function() {
  840. if(self.signalingState == 'closed') {
  841. console.error("createAnswer attempt on closed state");
  842. return;
  843. }
  844. self.peerconnection.createAnswer(
  845. function(modifiedAnswer) {
  846. // change video direction, see https://github.com/jitsi/jitmeet/issues/41
  847. if (self.pendingop !== null) {
  848. var sdp = new SDP(modifiedAnswer.sdp);
  849. if (sdp.media.length > 1) {
  850. switch(self.pendingop) {
  851. case 'mute':
  852. sdp.media[1] = sdp.media[1].replace('a=sendrecv', 'a=recvonly');
  853. break;
  854. case 'unmute':
  855. sdp.media[1] = sdp.media[1].replace('a=recvonly', 'a=sendrecv');
  856. break;
  857. }
  858. sdp.raw = sdp.session + sdp.media.join('');
  859. modifiedAnswer.sdp = sdp.raw;
  860. }
  861. self.pendingop = null;
  862. }
  863. // FIXME: pushing down an answer while ice connection state
  864. // is still checking is bad...
  865. //console.log(self.peerconnection.iceConnectionState);
  866. // trying to work around another chrome bug
  867. //modifiedAnswer.sdp = modifiedAnswer.sdp.replace(/a=setup:active/g, 'a=setup:actpass');
  868. self.peerconnection.setLocalDescription(modifiedAnswer,
  869. function() {
  870. //console.log('modified setLocalDescription ok');
  871. self.setLocalDescription();
  872. if(successCallback){
  873. successCallback();
  874. }
  875. },
  876. function(error) {
  877. console.error('modified setLocalDescription failed', error);
  878. }
  879. );
  880. },
  881. function(error) {
  882. console.error('modified answer failed', error);
  883. }
  884. );
  885. },
  886. function(error) {
  887. console.error('modify failed', error);
  888. }
  889. );
  890. };
  891. /**
  892. * Switches video streams.
  893. * @param new_stream new stream that will be used as video of this session.
  894. * @param oldStream old video stream of this session.
  895. * @param success_callback callback executed after successful stream switch.
  896. */
  897. JingleSession.prototype.switchStreams = function (new_stream, oldStream, success_callback) {
  898. var self = this;
  899. // Remember SDP to figure out added/removed SSRCs
  900. var oldSdp = null;
  901. if(self.peerconnection) {
  902. if(self.peerconnection.localDescription) {
  903. oldSdp = new SDP(self.peerconnection.localDescription.sdp);
  904. }
  905. self.peerconnection.removeStream(oldStream, true);
  906. self.peerconnection.addStream(new_stream);
  907. }
  908. RTC.switchVideoStreams(new_stream, oldStream);
  909. // Conference is not active
  910. if(!oldSdp || !self.peerconnection) {
  911. success_callback();
  912. return;
  913. }
  914. self.switchstreams = true;
  915. self.modifySources(function() {
  916. console.log('modify sources done');
  917. success_callback();
  918. var newSdp = new SDP(self.peerconnection.localDescription.sdp);
  919. console.log("SDPs", oldSdp, newSdp);
  920. self.notifyMySSRCUpdate(oldSdp, newSdp);
  921. });
  922. };
  923. /**
  924. * Figures out added/removed ssrcs and send update IQs.
  925. * @param old_sdp SDP object for old description.
  926. * @param new_sdp SDP object for new description.
  927. */
  928. JingleSession.prototype.notifyMySSRCUpdate = function (old_sdp, new_sdp) {
  929. if (!(this.peerconnection.signalingState == 'stable' &&
  930. this.peerconnection.iceConnectionState == 'connected')){
  931. console.log("Too early to send updates");
  932. return;
  933. }
  934. // send source-remove IQ.
  935. sdpDiffer = new SDPDiffer(new_sdp, old_sdp);
  936. var remove = $iq({to: this.peerjid, type: 'set'})
  937. .c('jingle', {
  938. xmlns: 'urn:xmpp:jingle:1',
  939. action: 'source-remove',
  940. initiator: this.initiator,
  941. sid: this.sid
  942. }
  943. );
  944. var removed = sdpDiffer.toJingle(remove);
  945. if (removed) {
  946. this.connection.sendIQ(remove,
  947. function (res) {
  948. console.info('got remove result', res);
  949. },
  950. function (err) {
  951. console.error('got remove error', err);
  952. }
  953. );
  954. } else {
  955. console.log('removal not necessary');
  956. }
  957. // send source-add IQ.
  958. var sdpDiffer = new SDPDiffer(old_sdp, new_sdp);
  959. var add = $iq({to: this.peerjid, type: 'set'})
  960. .c('jingle', {
  961. xmlns: 'urn:xmpp:jingle:1',
  962. action: 'source-add',
  963. initiator: this.initiator,
  964. sid: this.sid
  965. }
  966. );
  967. var added = sdpDiffer.toJingle(add);
  968. if (added) {
  969. this.connection.sendIQ(add,
  970. function (res) {
  971. console.info('got add result', res);
  972. },
  973. function (err) {
  974. console.error('got add error', err);
  975. }
  976. );
  977. } else {
  978. console.log('addition not necessary');
  979. }
  980. };
  981. /**
  982. * Determines whether the (local) video is mute i.e. all video tracks are
  983. * disabled.
  984. *
  985. * @return <tt>true</tt> if the (local) video is mute i.e. all video tracks are
  986. * disabled; otherwise, <tt>false</tt>
  987. */
  988. JingleSession.prototype.isVideoMute = function () {
  989. var tracks = RTC.localVideo.getVideoTracks();
  990. var mute = true;
  991. for (var i = 0; i < tracks.length; ++i) {
  992. if (tracks[i].enabled) {
  993. mute = false;
  994. break;
  995. }
  996. }
  997. return mute;
  998. };
  999. /**
  1000. * Mutes/unmutes the (local) video i.e. enables/disables all video tracks.
  1001. *
  1002. * @param mute <tt>true</tt> to mute the (local) video i.e. to disable all video
  1003. * tracks; otherwise, <tt>false</tt>
  1004. * @param callback a function to be invoked with <tt>mute</tt> after all video
  1005. * tracks have been enabled/disabled. The function may, optionally, return
  1006. * another function which is to be invoked after the whole mute/unmute operation
  1007. * has completed successfully.
  1008. * @param options an object which specifies optional arguments such as the
  1009. * <tt>boolean</tt> key <tt>byUser</tt> with default value <tt>true</tt> which
  1010. * specifies whether the method was initiated in response to a user command (in
  1011. * contrast to an automatic decision made by the application logic)
  1012. */
  1013. JingleSession.prototype.setVideoMute = function (mute, callback, options) {
  1014. var byUser;
  1015. if (options) {
  1016. byUser = options.byUser;
  1017. if (typeof byUser === 'undefined') {
  1018. byUser = true;
  1019. }
  1020. } else {
  1021. byUser = true;
  1022. }
  1023. // The user's command to mute the (local) video takes precedence over any
  1024. // automatic decision made by the application logic.
  1025. if (byUser) {
  1026. this.videoMuteByUser = mute;
  1027. } else if (this.videoMuteByUser) {
  1028. return;
  1029. }
  1030. var self = this;
  1031. var localCallback = function (mute) {
  1032. self.connection.emuc.addVideoInfoToPresence(mute);
  1033. self.connection.emuc.sendPresence();
  1034. return callback(mute)
  1035. };
  1036. if (mute == RTC.localVideo.isMuted())
  1037. {
  1038. // Even if no change occurs, the specified callback is to be executed.
  1039. // The specified callback may, optionally, return a successCallback
  1040. // which is to be executed as well.
  1041. var successCallback = localCallback(mute);
  1042. if (successCallback) {
  1043. successCallback();
  1044. }
  1045. } else {
  1046. RTC.localVideo.setMute(!mute);
  1047. this.hardMuteVideo(mute);
  1048. this.modifySources(localCallback(mute));
  1049. }
  1050. };
  1051. // SDP-based mute by going recvonly/sendrecv
  1052. // FIXME: should probably black out the screen as well
  1053. JingleSession.prototype.toggleVideoMute = function (callback) {
  1054. this.service.setVideoMute(RTC.localVideo.isMuted(), callback);
  1055. };
  1056. JingleSession.prototype.hardMuteVideo = function (muted) {
  1057. this.pendingop = muted ? 'mute' : 'unmute';
  1058. };
  1059. JingleSession.prototype.sendMute = function (muted, content) {
  1060. var info = $iq({to: this.peerjid,
  1061. type: 'set'})
  1062. .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
  1063. action: 'session-info',
  1064. initiator: this.initiator,
  1065. sid: this.sid });
  1066. info.c(muted ? 'mute' : 'unmute', {xmlns: 'urn:xmpp:jingle:apps:rtp:info:1'});
  1067. info.attrs({'creator': this.me == this.initiator ? 'creator' : 'responder'});
  1068. if (content) {
  1069. info.attrs({'name': content});
  1070. }
  1071. this.connection.send(info);
  1072. };
  1073. JingleSession.prototype.sendRinging = function () {
  1074. var info = $iq({to: this.peerjid,
  1075. type: 'set'})
  1076. .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
  1077. action: 'session-info',
  1078. initiator: this.initiator,
  1079. sid: this.sid });
  1080. info.c('ringing', {xmlns: 'urn:xmpp:jingle:apps:rtp:info:1'});
  1081. this.connection.send(info);
  1082. };
  1083. JingleSession.prototype.getStats = function (interval) {
  1084. var self = this;
  1085. var recv = {audio: 0, video: 0};
  1086. var lost = {audio: 0, video: 0};
  1087. var lastrecv = {audio: 0, video: 0};
  1088. var lastlost = {audio: 0, video: 0};
  1089. var loss = {audio: 0, video: 0};
  1090. var delta = {audio: 0, video: 0};
  1091. this.statsinterval = window.setInterval(function () {
  1092. if (self && self.peerconnection && self.peerconnection.getStats) {
  1093. self.peerconnection.getStats(function (stats) {
  1094. var results = stats.result();
  1095. // TODO: there are so much statistics you can get from this..
  1096. for (var i = 0; i < results.length; ++i) {
  1097. if (results[i].type == 'ssrc') {
  1098. var packetsrecv = results[i].stat('packetsReceived');
  1099. var packetslost = results[i].stat('packetsLost');
  1100. if (packetsrecv && packetslost) {
  1101. packetsrecv = parseInt(packetsrecv, 10);
  1102. packetslost = parseInt(packetslost, 10);
  1103. if (results[i].stat('googFrameRateReceived')) {
  1104. lastlost.video = lost.video;
  1105. lastrecv.video = recv.video;
  1106. recv.video = packetsrecv;
  1107. lost.video = packetslost;
  1108. } else {
  1109. lastlost.audio = lost.audio;
  1110. lastrecv.audio = recv.audio;
  1111. recv.audio = packetsrecv;
  1112. lost.audio = packetslost;
  1113. }
  1114. }
  1115. }
  1116. }
  1117. delta.audio = recv.audio - lastrecv.audio;
  1118. delta.video = recv.video - lastrecv.video;
  1119. loss.audio = (delta.audio > 0) ? Math.ceil(100 * (lost.audio - lastlost.audio) / delta.audio) : 0;
  1120. loss.video = (delta.video > 0) ? Math.ceil(100 * (lost.video - lastlost.video) / delta.video) : 0;
  1121. $(document).trigger('packetloss.jingle', [self.sid, loss]);
  1122. });
  1123. }
  1124. }, interval || 3000);
  1125. return this.statsinterval;
  1126. };
  1127. JingleSession.onJingleError = function (session, error)
  1128. {
  1129. console.error("Jingle error", error);
  1130. }
  1131. JingleSession.onJingleFatalError = function (session, error)
  1132. {
  1133. this.service.sessionTerminated = true;
  1134. connection.emuc.doLeave();
  1135. UI.messageHandler.showError( "Sorry",
  1136. "Internal application error[setRemoteDescription]");
  1137. }
  1138. JingleSession.prototype.setLocalDescription = function () {
  1139. // put our ssrcs into presence so other clients can identify our stream
  1140. var newssrcs = [];
  1141. var media = simulcast.parseMedia(this.peerconnection.localDescription);
  1142. media.forEach(function (media) {
  1143. if(Object.keys(media.sources).length > 0) {
  1144. // TODO(gp) maybe exclude FID streams?
  1145. Object.keys(media.sources).forEach(function (ssrc) {
  1146. newssrcs.push({
  1147. 'ssrc': ssrc,
  1148. 'type': media.type,
  1149. 'direction': media.direction
  1150. });
  1151. });
  1152. }
  1153. else if(this.localStreamsSSRC && this.localStreamsSSRC[media.type])
  1154. {
  1155. newssrcs.push({
  1156. 'ssrc': this.localStreamsSSRC[media.type],
  1157. 'type': media.type,
  1158. 'direction': media.direction
  1159. });
  1160. }
  1161. });
  1162. console.log('new ssrcs', newssrcs);
  1163. // Have to clear presence map to get rid of removed streams
  1164. this.connection.emuc.clearPresenceMedia();
  1165. if (newssrcs.length > 0) {
  1166. for (var i = 1; i <= newssrcs.length; i ++) {
  1167. // Change video type to screen
  1168. if (newssrcs[i-1].type === 'video' && desktopsharing.isUsingScreenStream()) {
  1169. newssrcs[i-1].type = 'screen';
  1170. }
  1171. this.connection.emuc.addMediaToPresence(i,
  1172. newssrcs[i-1].type, newssrcs[i-1].ssrc, newssrcs[i-1].direction);
  1173. }
  1174. this.connection.emuc.sendPresence();
  1175. }
  1176. }
  1177. // an attempt to work around https://github.com/jitsi/jitmeet/issues/32
  1178. function sendKeyframe(pc) {
  1179. console.log('sendkeyframe', pc.iceConnectionState);
  1180. if (pc.iceConnectionState !== 'connected') return; // safe...
  1181. pc.setRemoteDescription(
  1182. pc.remoteDescription,
  1183. function () {
  1184. pc.createAnswer(
  1185. function (modifiedAnswer) {
  1186. pc.setLocalDescription(
  1187. modifiedAnswer,
  1188. function () {
  1189. // noop
  1190. },
  1191. function (error) {
  1192. console.log('triggerKeyframe setLocalDescription failed', error);
  1193. UI.messageHandler.showError();
  1194. }
  1195. );
  1196. },
  1197. function (error) {
  1198. console.log('triggerKeyframe createAnswer failed', error);
  1199. UI.messageHandler.showError();
  1200. }
  1201. );
  1202. },
  1203. function (error) {
  1204. console.log('triggerKeyframe setRemoteDescription failed', error);
  1205. UI.messageHandler.showError();
  1206. }
  1207. );
  1208. }
  1209. JingleSession.prototype.remoteStreamAdded = function (data) {
  1210. var self = this;
  1211. var thessrc;
  1212. // look up an associated JID for a stream id
  1213. if (data.stream.id && data.stream.id.indexOf('mixedmslabel') === -1) {
  1214. // look only at a=ssrc: and _not_ at a=ssrc-group: lines
  1215. var ssrclines
  1216. = SDPUtil.find_lines(this.peerconnection.remoteDescription.sdp, 'a=ssrc:');
  1217. ssrclines = ssrclines.filter(function (line) {
  1218. // NOTE(gp) previously we filtered on the mslabel, but that property
  1219. // is not always present.
  1220. // return line.indexOf('mslabel:' + data.stream.label) !== -1;
  1221. return ((line.indexOf('msid:' + data.stream.id) !== -1));
  1222. });
  1223. if (ssrclines.length) {
  1224. thessrc = ssrclines[0].substring(7).split(' ')[0];
  1225. // We signal our streams (through Jingle to the focus) before we set
  1226. // our presence (through which peers associate remote streams to
  1227. // jids). So, it might arrive that a remote stream is added but
  1228. // ssrc2jid is not yet updated and thus data.peerjid cannot be
  1229. // successfully set. Here we wait for up to a second for the
  1230. // presence to arrive.
  1231. if (!ssrc2jid[thessrc]) {
  1232. // TODO(gp) limit wait duration to 1 sec.
  1233. setTimeout(function(d) {
  1234. return function() {
  1235. self.remoteStreamAdded(d);
  1236. }
  1237. }(data), 250);
  1238. return;
  1239. }
  1240. // ok to overwrite the one from focus? might save work in colibri.js
  1241. console.log('associated jid', ssrc2jid[thessrc], data.peerjid);
  1242. if (ssrc2jid[thessrc]) {
  1243. data.peerjid = ssrc2jid[thessrc];
  1244. }
  1245. }
  1246. }
  1247. //TODO: this code should be removed when firefox implement multistream support
  1248. if(RTC.getBrowserType() == RTCBrowserType.RTC_BROWSER_FIREFOX)
  1249. {
  1250. if((notReceivedSSRCs.length == 0) ||
  1251. !ssrc2jid[notReceivedSSRCs[notReceivedSSRCs.length - 1]])
  1252. {
  1253. // TODO(gp) limit wait duration to 1 sec.
  1254. setTimeout(function(d) {
  1255. return function() {
  1256. self.remoteStreamAdded(d);
  1257. }
  1258. }(data), 250);
  1259. return;
  1260. }
  1261. thessrc = notReceivedSSRCs.pop();
  1262. if (ssrc2jid[thessrc]) {
  1263. data.peerjid = ssrc2jid[thessrc];
  1264. }
  1265. }
  1266. RTC.createRemoteStream(data, this.sid, thessrc);
  1267. var isVideo = data.stream.getVideoTracks().length > 0;
  1268. // an attempt to work around https://github.com/jitsi/jitmeet/issues/32
  1269. if (isVideo &&
  1270. data.peerjid && this.peerjid === data.peerjid &&
  1271. data.stream.getVideoTracks().length === 0 &&
  1272. RTC.localVideo.getTracks().length > 0) {
  1273. window.setTimeout(function () {
  1274. sendKeyframe(self.peerconnection);
  1275. }, 3000);
  1276. }
  1277. }
  1278. module.exports = JingleSession;
  1279. },{"./SDP":2,"./SDPDiffer":3,"./SDPUtil":4,"./TraceablePeerConnection":5}],2:[function(require,module,exports){
  1280. /* jshint -W117 */
  1281. var SDPUtil = require("./SDPUtil");
  1282. // SDP STUFF
  1283. function SDP(sdp) {
  1284. this.media = sdp.split('\r\nm=');
  1285. for (var i = 1; i < this.media.length; i++) {
  1286. this.media[i] = 'm=' + this.media[i];
  1287. if (i != this.media.length - 1) {
  1288. this.media[i] += '\r\n';
  1289. }
  1290. }
  1291. this.session = this.media.shift() + '\r\n';
  1292. this.raw = this.session + this.media.join('');
  1293. }
  1294. /**
  1295. * Returns map of MediaChannel mapped per channel idx.
  1296. */
  1297. SDP.prototype.getMediaSsrcMap = function() {
  1298. var self = this;
  1299. var media_ssrcs = {};
  1300. var tmp;
  1301. for (var mediaindex = 0; mediaindex < self.media.length; mediaindex++) {
  1302. tmp = SDPUtil.find_lines(self.media[mediaindex], 'a=ssrc:');
  1303. var mid = SDPUtil.parse_mid(SDPUtil.find_line(self.media[mediaindex], 'a=mid:'));
  1304. var media = {
  1305. mediaindex: mediaindex,
  1306. mid: mid,
  1307. ssrcs: {},
  1308. ssrcGroups: []
  1309. };
  1310. media_ssrcs[mediaindex] = media;
  1311. tmp.forEach(function (line) {
  1312. var linessrc = line.substring(7).split(' ')[0];
  1313. // allocate new ChannelSsrc
  1314. if(!media.ssrcs[linessrc]) {
  1315. media.ssrcs[linessrc] = {
  1316. ssrc: linessrc,
  1317. lines: []
  1318. };
  1319. }
  1320. media.ssrcs[linessrc].lines.push(line);
  1321. });
  1322. tmp = SDPUtil.find_lines(self.media[mediaindex], 'a=ssrc-group:');
  1323. tmp.forEach(function(line){
  1324. var semantics = line.substr(0, idx).substr(13);
  1325. var ssrcs = line.substr(14 + semantics.length).split(' ');
  1326. if (ssrcs.length != 0) {
  1327. media.ssrcGroups.push({
  1328. semantics: semantics,
  1329. ssrcs: ssrcs
  1330. });
  1331. }
  1332. });
  1333. }
  1334. return media_ssrcs;
  1335. };
  1336. /**
  1337. * Returns <tt>true</tt> if this SDP contains given SSRC.
  1338. * @param ssrc the ssrc to check.
  1339. * @returns {boolean} <tt>true</tt> if this SDP contains given SSRC.
  1340. */
  1341. SDP.prototype.containsSSRC = function(ssrc) {
  1342. var medias = this.getMediaSsrcMap();
  1343. var contains = false;
  1344. Object.keys(medias).forEach(function(mediaindex){
  1345. var media = medias[mediaindex];
  1346. //console.log("Check", channel, ssrc);
  1347. if(Object.keys(media.ssrcs).indexOf(ssrc) != -1){
  1348. contains = true;
  1349. }
  1350. });
  1351. return contains;
  1352. };
  1353. // remove iSAC and CN from SDP
  1354. SDP.prototype.mangle = function () {
  1355. var i, j, mline, lines, rtpmap, newdesc;
  1356. for (i = 0; i < this.media.length; i++) {
  1357. lines = this.media[i].split('\r\n');
  1358. lines.pop(); // remove empty last element
  1359. mline = SDPUtil.parse_mline(lines.shift());
  1360. if (mline.media != 'audio')
  1361. continue;
  1362. newdesc = '';
  1363. mline.fmt.length = 0;
  1364. for (j = 0; j < lines.length; j++) {
  1365. if (lines[j].substr(0, 9) == 'a=rtpmap:') {
  1366. rtpmap = SDPUtil.parse_rtpmap(lines[j]);
  1367. if (rtpmap.name == 'CN' || rtpmap.name == 'ISAC')
  1368. continue;
  1369. mline.fmt.push(rtpmap.id);
  1370. newdesc += lines[j] + '\r\n';
  1371. } else {
  1372. newdesc += lines[j] + '\r\n';
  1373. }
  1374. }
  1375. this.media[i] = SDPUtil.build_mline(mline) + '\r\n';
  1376. this.media[i] += newdesc;
  1377. }
  1378. this.raw = this.session + this.media.join('');
  1379. };
  1380. // remove lines matching prefix from session section
  1381. SDP.prototype.removeSessionLines = function(prefix) {
  1382. var self = this;
  1383. var lines = SDPUtil.find_lines(this.session, prefix);
  1384. lines.forEach(function(line) {
  1385. self.session = self.session.replace(line + '\r\n', '');
  1386. });
  1387. this.raw = this.session + this.media.join('');
  1388. return lines;
  1389. }
  1390. // remove lines matching prefix from a media section specified by mediaindex
  1391. // TODO: non-numeric mediaindex could match mid
  1392. SDP.prototype.removeMediaLines = function(mediaindex, prefix) {
  1393. var self = this;
  1394. var lines = SDPUtil.find_lines(this.media[mediaindex], prefix);
  1395. lines.forEach(function(line) {
  1396. self.media[mediaindex] = self.media[mediaindex].replace(line + '\r\n', '');
  1397. });
  1398. this.raw = this.session + this.media.join('');
  1399. return lines;
  1400. }
  1401. // add content's to a jingle element
  1402. SDP.prototype.toJingle = function (elem, thecreator, ssrcs) {
  1403. // console.log("SSRC" + ssrcs["audio"] + " - " + ssrcs["video"]);
  1404. var i, j, k, mline, ssrc, rtpmap, tmp, line, lines;
  1405. var self = this;
  1406. // new bundle plan
  1407. if (SDPUtil.find_line(this.session, 'a=group:')) {
  1408. lines = SDPUtil.find_lines(this.session, 'a=group:');
  1409. for (i = 0; i < lines.length; i++) {
  1410. tmp = lines[i].split(' ');
  1411. var semantics = tmp.shift().substr(8);
  1412. elem.c('group', {xmlns: 'urn:xmpp:jingle:apps:grouping:0', semantics:semantics});
  1413. for (j = 0; j < tmp.length; j++) {
  1414. elem.c('content', {name: tmp[j]}).up();
  1415. }
  1416. elem.up();
  1417. }
  1418. }
  1419. for (i = 0; i < this.media.length; i++) {
  1420. mline = SDPUtil.parse_mline(this.media[i].split('\r\n')[0]);
  1421. if (!(mline.media === 'audio' ||
  1422. mline.media === 'video' ||
  1423. mline.media === 'application'))
  1424. {
  1425. continue;
  1426. }
  1427. if (SDPUtil.find_line(this.media[i], 'a=ssrc:')) {
  1428. ssrc = SDPUtil.find_line(this.media[i], 'a=ssrc:').substring(7).split(' ')[0]; // take the first
  1429. } else {
  1430. if(ssrcs && ssrcs[mline.media])
  1431. {
  1432. ssrc = ssrcs[mline.media];
  1433. }
  1434. else
  1435. ssrc = false;
  1436. }
  1437. elem.c('content', {creator: thecreator, name: mline.media});
  1438. if (SDPUtil.find_line(this.media[i], 'a=mid:')) {
  1439. // prefer identifier from a=mid if present
  1440. var mid = SDPUtil.parse_mid(SDPUtil.find_line(this.media[i], 'a=mid:'));
  1441. elem.attrs({ name: mid });
  1442. }
  1443. if (SDPUtil.find_line(this.media[i], 'a=rtpmap:').length)
  1444. {
  1445. elem.c('description',
  1446. {xmlns: 'urn:xmpp:jingle:apps:rtp:1',
  1447. media: mline.media });
  1448. if (ssrc) {
  1449. elem.attrs({ssrc: ssrc});
  1450. }
  1451. for (j = 0; j < mline.fmt.length; j++) {
  1452. rtpmap = SDPUtil.find_line(this.media[i], 'a=rtpmap:' + mline.fmt[j]);
  1453. elem.c('payload-type', SDPUtil.parse_rtpmap(rtpmap));
  1454. // put any 'a=fmtp:' + mline.fmt[j] lines into <param name=foo value=bar/>
  1455. if (SDPUtil.find_line(this.media[i], 'a=fmtp:' + mline.fmt[j])) {
  1456. tmp = SDPUtil.parse_fmtp(SDPUtil.find_line(this.media[i], 'a=fmtp:' + mline.fmt[j]));
  1457. for (k = 0; k < tmp.length; k++) {
  1458. elem.c('parameter', tmp[k]).up();
  1459. }
  1460. }
  1461. this.RtcpFbToJingle(i, elem, mline.fmt[j]); // XEP-0293 -- map a=rtcp-fb
  1462. elem.up();
  1463. }
  1464. if (SDPUtil.find_line(this.media[i], 'a=crypto:', this.session)) {
  1465. elem.c('encryption', {required: 1});
  1466. var crypto = SDPUtil.find_lines(this.media[i], 'a=crypto:', this.session);
  1467. crypto.forEach(function(line) {
  1468. elem.c('crypto', SDPUtil.parse_crypto(line)).up();
  1469. });
  1470. elem.up(); // end of encryption
  1471. }
  1472. if (ssrc) {
  1473. // new style mapping
  1474. elem.c('source', { ssrc: ssrc, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
  1475. // FIXME: group by ssrc and support multiple different ssrcs
  1476. var ssrclines = SDPUtil.find_lines(this.media[i], 'a=ssrc:');
  1477. if(ssrclines.length > 0) {
  1478. ssrclines.forEach(function (line) {
  1479. idx = line.indexOf(' ');
  1480. var linessrc = line.substr(0, idx).substr(7);
  1481. if (linessrc != ssrc) {
  1482. elem.up();
  1483. ssrc = linessrc;
  1484. elem.c('source', { ssrc: ssrc, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
  1485. }
  1486. var kv = line.substr(idx + 1);
  1487. elem.c('parameter');
  1488. if (kv.indexOf(':') == -1) {
  1489. elem.attrs({ name: kv });
  1490. } else {
  1491. elem.attrs({ name: kv.split(':', 2)[0] });
  1492. elem.attrs({ value: kv.split(':', 2)[1] });
  1493. }
  1494. elem.up();
  1495. });
  1496. elem.up();
  1497. }
  1498. else
  1499. {
  1500. elem.up();
  1501. elem.c('source', { ssrc: ssrc, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
  1502. elem.c('parameter');
  1503. elem.attrs({name: "cname", value:Math.random().toString(36).substring(7)});
  1504. elem.up();
  1505. var msid = null;
  1506. if(mline.media == "audio")
  1507. {
  1508. msid = RTC.localAudio.getId();
  1509. }
  1510. else
  1511. {
  1512. msid = RTC.localVideo.getId();
  1513. }
  1514. if(msid != null)
  1515. {
  1516. msid = msid.replace(/[\{,\}]/g,"");
  1517. elem.c('parameter');
  1518. elem.attrs({name: "msid", value:msid});
  1519. elem.up();
  1520. elem.c('parameter');
  1521. elem.attrs({name: "mslabel", value:msid});
  1522. elem.up();
  1523. elem.c('parameter');
  1524. elem.attrs({name: "label", value:msid});
  1525. elem.up();
  1526. elem.up();
  1527. }
  1528. }
  1529. // XEP-0339 handle ssrc-group attributes
  1530. var ssrc_group_lines = SDPUtil.find_lines(this.media[i], 'a=ssrc-group:');
  1531. ssrc_group_lines.forEach(function(line) {
  1532. idx = line.indexOf(' ');
  1533. var semantics = line.substr(0, idx).substr(13);
  1534. var ssrcs = line.substr(14 + semantics.length).split(' ');
  1535. if (ssrcs.length != 0) {
  1536. elem.c('ssrc-group', { semantics: semantics, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
  1537. ssrcs.forEach(function(ssrc) {
  1538. elem.c('source', { ssrc: ssrc })
  1539. .up();
  1540. });
  1541. elem.up();
  1542. }
  1543. });
  1544. }
  1545. if (SDPUtil.find_line(this.media[i], 'a=rtcp-mux')) {
  1546. elem.c('rtcp-mux').up();
  1547. }
  1548. // XEP-0293 -- map a=rtcp-fb:*
  1549. this.RtcpFbToJingle(i, elem, '*');
  1550. // XEP-0294
  1551. if (SDPUtil.find_line(this.media[i], 'a=extmap:')) {
  1552. lines = SDPUtil.find_lines(this.media[i], 'a=extmap:');
  1553. for (j = 0; j < lines.length; j++) {
  1554. tmp = SDPUtil.parse_extmap(lines[j]);
  1555. elem.c('rtp-hdrext', { xmlns: 'urn:xmpp:jingle:apps:rtp:rtp-hdrext:0',
  1556. uri: tmp.uri,
  1557. id: tmp.value });
  1558. if (tmp.hasOwnProperty('direction')) {
  1559. switch (tmp.direction) {
  1560. case 'sendonly':
  1561. elem.attrs({senders: 'responder'});
  1562. break;
  1563. case 'recvonly':
  1564. elem.attrs({senders: 'initiator'});
  1565. break;
  1566. case 'sendrecv':
  1567. elem.attrs({senders: 'both'});
  1568. break;
  1569. case 'inactive':
  1570. elem.attrs({senders: 'none'});
  1571. break;
  1572. }
  1573. }
  1574. // TODO: handle params
  1575. elem.up();
  1576. }
  1577. }
  1578. elem.up(); // end of description
  1579. }
  1580. // map ice-ufrag/pwd, dtls fingerprint, candidates
  1581. this.TransportToJingle(i, elem);
  1582. if (SDPUtil.find_line(this.media[i], 'a=sendrecv', this.session)) {
  1583. elem.attrs({senders: 'both'});
  1584. } else if (SDPUtil.find_line(this.media[i], 'a=sendonly', this.session)) {
  1585. elem.attrs({senders: 'initiator'});
  1586. } else if (SDPUtil.find_line(this.media[i], 'a=recvonly', this.session)) {
  1587. elem.attrs({senders: 'responder'});
  1588. } else if (SDPUtil.find_line(this.media[i], 'a=inactive', this.session)) {
  1589. elem.attrs({senders: 'none'});
  1590. }
  1591. if (mline.port == '0') {
  1592. // estos hack to reject an m-line
  1593. elem.attrs({senders: 'rejected'});
  1594. }
  1595. elem.up(); // end of content
  1596. }
  1597. elem.up();
  1598. return elem;
  1599. };
  1600. SDP.prototype.TransportToJingle = function (mediaindex, elem) {
  1601. var i = mediaindex;
  1602. var tmp;
  1603. var self = this;
  1604. elem.c('transport');
  1605. // XEP-0343 DTLS/SCTP
  1606. if (SDPUtil.find_line(this.media[mediaindex], 'a=sctpmap:').length)
  1607. {
  1608. var sctpmap = SDPUtil.find_line(
  1609. this.media[i], 'a=sctpmap:', self.session);
  1610. if (sctpmap)
  1611. {
  1612. var sctpAttrs = SDPUtil.parse_sctpmap(sctpmap);
  1613. elem.c('sctpmap',
  1614. {
  1615. xmlns: 'urn:xmpp:jingle:transports:dtls-sctp:1',
  1616. number: sctpAttrs[0], /* SCTP port */
  1617. protocol: sctpAttrs[1], /* protocol */
  1618. });
  1619. // Optional stream count attribute
  1620. if (sctpAttrs.length > 2)
  1621. elem.attrs({ streams: sctpAttrs[2]});
  1622. elem.up();
  1623. }
  1624. }
  1625. // XEP-0320
  1626. var fingerprints = SDPUtil.find_lines(this.media[mediaindex], 'a=fingerprint:', this.session);
  1627. fingerprints.forEach(function(line) {
  1628. tmp = SDPUtil.parse_fingerprint(line);
  1629. tmp.xmlns = 'urn:xmpp:jingle:apps:dtls:0';
  1630. elem.c('fingerprint').t(tmp.fingerprint);
  1631. delete tmp.fingerprint;
  1632. line = SDPUtil.find_line(self.media[mediaindex], 'a=setup:', self.session);
  1633. if (line) {
  1634. tmp.setup = line.substr(8);
  1635. }
  1636. elem.attrs(tmp);
  1637. elem.up(); // end of fingerprint
  1638. });
  1639. tmp = SDPUtil.iceparams(this.media[mediaindex], this.session);
  1640. if (tmp) {
  1641. tmp.xmlns = 'urn:xmpp:jingle:transports:ice-udp:1';
  1642. elem.attrs(tmp);
  1643. // XEP-0176
  1644. if (SDPUtil.find_line(this.media[mediaindex], 'a=candidate:', this.session)) { // add any a=candidate lines
  1645. var lines = SDPUtil.find_lines(this.media[mediaindex], 'a=candidate:', this.session);
  1646. lines.forEach(function (line) {
  1647. elem.c('candidate', SDPUtil.candidateToJingle(line)).up();
  1648. });
  1649. }
  1650. }
  1651. elem.up(); // end of transport
  1652. }
  1653. SDP.prototype.RtcpFbToJingle = function (mediaindex, elem, payloadtype) { // XEP-0293
  1654. var lines = SDPUtil.find_lines(this.media[mediaindex], 'a=rtcp-fb:' + payloadtype);
  1655. lines.forEach(function (line) {
  1656. var tmp = SDPUtil.parse_rtcpfb(line);
  1657. if (tmp.type == 'trr-int') {
  1658. elem.c('rtcp-fb-trr-int', {xmlns: 'urn:xmpp:jingle:apps:rtp:rtcp-fb:0', value: tmp.params[0]});
  1659. elem.up();
  1660. } else {
  1661. elem.c('rtcp-fb', {xmlns: 'urn:xmpp:jingle:apps:rtp:rtcp-fb:0', type: tmp.type});
  1662. if (tmp.params.length > 0) {
  1663. elem.attrs({'subtype': tmp.params[0]});
  1664. }
  1665. elem.up();
  1666. }
  1667. });
  1668. };
  1669. SDP.prototype.RtcpFbFromJingle = function (elem, payloadtype) { // XEP-0293
  1670. var media = '';
  1671. var tmp = elem.find('>rtcp-fb-trr-int[xmlns="urn:xmpp:jingle:apps:rtp:rtcp-fb:0"]');
  1672. if (tmp.length) {
  1673. media += 'a=rtcp-fb:' + '*' + ' ' + 'trr-int' + ' ';
  1674. if (tmp.attr('value')) {
  1675. media += tmp.attr('value');
  1676. } else {
  1677. media += '0';
  1678. }
  1679. media += '\r\n';
  1680. }
  1681. tmp = elem.find('>rtcp-fb[xmlns="urn:xmpp:jingle:apps:rtp:rtcp-fb:0"]');
  1682. tmp.each(function () {
  1683. media += 'a=rtcp-fb:' + payloadtype + ' ' + $(this).attr('type');
  1684. if ($(this).attr('subtype')) {
  1685. media += ' ' + $(this).attr('subtype');
  1686. }
  1687. media += '\r\n';
  1688. });
  1689. return media;
  1690. };
  1691. // construct an SDP from a jingle stanza
  1692. SDP.prototype.fromJingle = function (jingle) {
  1693. var self = this;
  1694. this.raw = 'v=0\r\n' +
  1695. 'o=- ' + '1923518516' + ' 2 IN IP4 0.0.0.0\r\n' +// FIXME
  1696. 's=-\r\n' +
  1697. 't=0 0\r\n';
  1698. // http://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-04#section-8
  1699. if ($(jingle).find('>group[xmlns="urn:xmpp:jingle:apps:grouping:0"]').length) {
  1700. $(jingle).find('>group[xmlns="urn:xmpp:jingle:apps:grouping:0"]').each(function (idx, group) {
  1701. var contents = $(group).find('>content').map(function (idx, content) {
  1702. return content.getAttribute('name');
  1703. }).get();
  1704. if (contents.length > 0) {
  1705. self.raw += 'a=group:' + (group.getAttribute('semantics') || group.getAttribute('type')) + ' ' + contents.join(' ') + '\r\n';
  1706. }
  1707. });
  1708. }
  1709. this.session = this.raw;
  1710. jingle.find('>content').each(function () {
  1711. var m = self.jingle2media($(this));
  1712. self.media.push(m);
  1713. });
  1714. // reconstruct msid-semantic -- apparently not necessary
  1715. /*
  1716. var msid = SDPUtil.parse_ssrc(this.raw);
  1717. if (msid.hasOwnProperty('mslabel')) {
  1718. this.session += "a=msid-semantic: WMS " + msid.mslabel + "\r\n";
  1719. }
  1720. */
  1721. this.raw = this.session + this.media.join('');
  1722. };
  1723. // translate a jingle content element into an an SDP media part
  1724. SDP.prototype.jingle2media = function (content) {
  1725. var media = '',
  1726. desc = content.find('description'),
  1727. ssrc = desc.attr('ssrc'),
  1728. self = this,
  1729. tmp;
  1730. var sctp = content.find(
  1731. '>transport>sctpmap[xmlns="urn:xmpp:jingle:transports:dtls-sctp:1"]');
  1732. tmp = { media: desc.attr('media') };
  1733. tmp.port = '1';
  1734. if (content.attr('senders') == 'rejected') {
  1735. // estos hack to reject an m-line.
  1736. tmp.port = '0';
  1737. }
  1738. if (content.find('>transport>fingerprint').length || desc.find('encryption').length) {
  1739. if (sctp.length)
  1740. tmp.proto = 'DTLS/SCTP';
  1741. else
  1742. tmp.proto = 'RTP/SAVPF';
  1743. } else {
  1744. tmp.proto = 'RTP/AVPF';
  1745. }
  1746. if (!sctp.length)
  1747. {
  1748. tmp.fmt = desc.find('payload-type').map(
  1749. function () { return this.getAttribute('id'); }).get();
  1750. media += SDPUtil.build_mline(tmp) + '\r\n';
  1751. }
  1752. else
  1753. {
  1754. media += 'm=application 1 DTLS/SCTP ' + sctp.attr('number') + '\r\n';
  1755. media += 'a=sctpmap:' + sctp.attr('number') +
  1756. ' ' + sctp.attr('protocol');
  1757. var streamCount = sctp.attr('streams');
  1758. if (streamCount)
  1759. media += ' ' + streamCount + '\r\n';
  1760. else
  1761. media += '\r\n';
  1762. }
  1763. media += 'c=IN IP4 0.0.0.0\r\n';
  1764. if (!sctp.length)
  1765. media += 'a=rtcp:1 IN IP4 0.0.0.0\r\n';
  1766. tmp = content.find('>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]');
  1767. if (tmp.length) {
  1768. if (tmp.attr('ufrag')) {
  1769. media += SDPUtil.build_iceufrag(tmp.attr('ufrag')) + '\r\n';
  1770. }
  1771. if (tmp.attr('pwd')) {
  1772. media += SDPUtil.build_icepwd(tmp.attr('pwd')) + '\r\n';
  1773. }
  1774. tmp.find('>fingerprint').each(function () {
  1775. // FIXME: check namespace at some point
  1776. media += 'a=fingerprint:' + this.getAttribute('hash');
  1777. media += ' ' + $(this).text();
  1778. media += '\r\n';
  1779. if (this.getAttribute('setup')) {
  1780. media += 'a=setup:' + this.getAttribute('setup') + '\r\n';
  1781. }
  1782. });
  1783. }
  1784. switch (content.attr('senders')) {
  1785. case 'initiator':
  1786. media += 'a=sendonly\r\n';
  1787. break;
  1788. case 'responder':
  1789. media += 'a=recvonly\r\n';
  1790. break;
  1791. case 'none':
  1792. media += 'a=inactive\r\n';
  1793. break;
  1794. case 'both':
  1795. media += 'a=sendrecv\r\n';
  1796. break;
  1797. }
  1798. media += 'a=mid:' + content.attr('name') + '\r\n';
  1799. // <description><rtcp-mux/></description>
  1800. // see http://code.google.com/p/libjingle/issues/detail?id=309 -- no spec though
  1801. // and http://mail.jabber.org/pipermail/jingle/2011-December/001761.html
  1802. if (desc.find('rtcp-mux').length) {
  1803. media += 'a=rtcp-mux\r\n';
  1804. }
  1805. if (desc.find('encryption').length) {
  1806. desc.find('encryption>crypto').each(function () {
  1807. media += 'a=crypto:' + this.getAttribute('tag');
  1808. media += ' ' + this.getAttribute('crypto-suite');
  1809. media += ' ' + this.getAttribute('key-params');
  1810. if (this.getAttribute('session-params')) {
  1811. media += ' ' + this.getAttribute('session-params');
  1812. }
  1813. media += '\r\n';
  1814. });
  1815. }
  1816. desc.find('payload-type').each(function () {
  1817. media += SDPUtil.build_rtpmap(this) + '\r\n';
  1818. if ($(this).find('>parameter').length) {
  1819. media += 'a=fmtp:' + this.getAttribute('id') + ' ';
  1820. media += $(this).find('parameter').map(function () { return (this.getAttribute('name') ? (this.getAttribute('name') + '=') : '') + this.getAttribute('value'); }).get().join('; ');
  1821. media += '\r\n';
  1822. }
  1823. // xep-0293
  1824. media += self.RtcpFbFromJingle($(this), this.getAttribute('id'));
  1825. });
  1826. // xep-0293
  1827. media += self.RtcpFbFromJingle(desc, '*');
  1828. // xep-0294
  1829. tmp = desc.find('>rtp-hdrext[xmlns="urn:xmpp:jingle:apps:rtp:rtp-hdrext:0"]');
  1830. tmp.each(function () {
  1831. media += 'a=extmap:' + this.getAttribute('id') + ' ' + this.getAttribute('uri') + '\r\n';
  1832. });
  1833. content.find('>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]>candidate').each(function () {
  1834. media += SDPUtil.candidateFromJingle(this);
  1835. });
  1836. // XEP-0339 handle ssrc-group attributes
  1837. tmp = content.find('description>ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function() {
  1838. var semantics = this.getAttribute('semantics');
  1839. var ssrcs = $(this).find('>source').map(function() {
  1840. return this.getAttribute('ssrc');
  1841. }).get();
  1842. if (ssrcs.length != 0) {
  1843. media += 'a=ssrc-group:' + semantics + ' ' + ssrcs.join(' ') + '\r\n';
  1844. }
  1845. });
  1846. tmp = content.find('description>source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]');
  1847. tmp.each(function () {
  1848. var ssrc = this.getAttribute('ssrc');
  1849. $(this).find('>parameter').each(function () {
  1850. media += 'a=ssrc:' + ssrc + ' ' + this.getAttribute('name');
  1851. if (this.getAttribute('value') && this.getAttribute('value').length)
  1852. media += ':' + this.getAttribute('value');
  1853. media += '\r\n';
  1854. });
  1855. });
  1856. return media;
  1857. };
  1858. module.exports = SDP;
  1859. },{"./SDPUtil":4}],3:[function(require,module,exports){
  1860. function SDPDiffer(mySDP, otherSDP) {
  1861. this.mySDP = mySDP;
  1862. this.otherSDP = otherSDP;
  1863. }
  1864. /**
  1865. * Returns map of MediaChannel that contains only media not contained in <tt>otherSdp</tt>. Mapped by channel idx.
  1866. * @param otherSdp the other SDP to check ssrc with.
  1867. */
  1868. SDPDiffer.prototype.getNewMedia = function() {
  1869. // this could be useful in Array.prototype.
  1870. function arrayEquals(array) {
  1871. // if the other array is a falsy value, return
  1872. if (!array)
  1873. return false;
  1874. // compare lengths - can save a lot of time
  1875. if (this.length != array.length)
  1876. return false;
  1877. for (var i = 0, l=this.length; i < l; i++) {
  1878. // Check if we have nested arrays
  1879. if (this[i] instanceof Array && array[i] instanceof Array) {
  1880. // recurse into the nested arrays
  1881. if (!this[i].equals(array[i]))
  1882. return false;
  1883. }
  1884. else if (this[i] != array[i]) {
  1885. // Warning - two different object instances will never be equal: {x:20} != {x:20}
  1886. return false;
  1887. }
  1888. }
  1889. return true;
  1890. }
  1891. var myMedias = this.mySDP.getMediaSsrcMap();
  1892. var othersMedias = this.otherSDP.getMediaSsrcMap();
  1893. var newMedia = {};
  1894. Object.keys(othersMedias).forEach(function(othersMediaIdx) {
  1895. var myMedia = myMedias[othersMediaIdx];
  1896. var othersMedia = othersMedias[othersMediaIdx];
  1897. if(!myMedia && othersMedia) {
  1898. // Add whole channel
  1899. newMedia[othersMediaIdx] = othersMedia;
  1900. return;
  1901. }
  1902. // Look for new ssrcs accross the channel
  1903. Object.keys(othersMedia.ssrcs).forEach(function(ssrc) {
  1904. if(Object.keys(myMedia.ssrcs).indexOf(ssrc) === -1) {
  1905. // Allocate channel if we've found ssrc that doesn't exist in our channel
  1906. if(!newMedia[othersMediaIdx]){
  1907. newMedia[othersMediaIdx] = {
  1908. mediaindex: othersMedia.mediaindex,
  1909. mid: othersMedia.mid,
  1910. ssrcs: {},
  1911. ssrcGroups: []
  1912. };
  1913. }
  1914. newMedia[othersMediaIdx].ssrcs[ssrc] = othersMedia.ssrcs[ssrc];
  1915. }
  1916. });
  1917. // Look for new ssrc groups across the channels
  1918. othersMedia.ssrcGroups.forEach(function(otherSsrcGroup){
  1919. // try to match the other ssrc-group with an ssrc-group of ours
  1920. var matched = false;
  1921. for (var i = 0; i < myMedia.ssrcGroups.length; i++) {
  1922. var mySsrcGroup = myMedia.ssrcGroups[i];
  1923. if (otherSsrcGroup.semantics == mySsrcGroup.semantics
  1924. && arrayEquals.apply(otherSsrcGroup.ssrcs, [mySsrcGroup.ssrcs])) {
  1925. matched = true;
  1926. break;
  1927. }
  1928. }
  1929. if (!matched) {
  1930. // Allocate channel if we've found an ssrc-group that doesn't
  1931. // exist in our channel
  1932. if(!newMedia[othersMediaIdx]){
  1933. newMedia[othersMediaIdx] = {
  1934. mediaindex: othersMedia.mediaindex,
  1935. mid: othersMedia.mid,
  1936. ssrcs: {},
  1937. ssrcGroups: []
  1938. };
  1939. }
  1940. newMedia[othersMediaIdx].ssrcGroups.push(otherSsrcGroup);
  1941. }
  1942. });
  1943. });
  1944. return newMedia;
  1945. };
  1946. /**
  1947. * Sends SSRC update IQ.
  1948. * @param sdpMediaSsrcs SSRCs map obtained from SDP.getNewMedia. Cntains SSRCs to add/remove.
  1949. * @param sid session identifier that will be put into the IQ.
  1950. * @param initiator initiator identifier.
  1951. * @param toJid destination Jid
  1952. * @param isAdd indicates if this is remove or add operation.
  1953. */
  1954. SDPDiffer.prototype.toJingle = function(modify) {
  1955. var sdpMediaSsrcs = this.getNewMedia();
  1956. var self = this;
  1957. // FIXME: only announce video ssrcs since we mix audio and dont need
  1958. // the audio ssrcs therefore
  1959. var modified = false;
  1960. Object.keys(sdpMediaSsrcs).forEach(function(mediaindex){
  1961. modified = true;
  1962. var media = sdpMediaSsrcs[mediaindex];
  1963. modify.c('content', {name: media.mid});
  1964. modify.c('description', {xmlns:'urn:xmpp:jingle:apps:rtp:1', media: media.mid});
  1965. // FIXME: not completly sure this operates on blocks and / or handles different ssrcs correctly
  1966. // generate sources from lines
  1967. Object.keys(media.ssrcs).forEach(function(ssrcNum) {
  1968. var mediaSsrc = media.ssrcs[ssrcNum];
  1969. modify.c('source', { xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
  1970. modify.attrs({ssrc: mediaSsrc.ssrc});
  1971. // iterate over ssrc lines
  1972. mediaSsrc.lines.forEach(function (line) {
  1973. var idx = line.indexOf(' ');
  1974. var kv = line.substr(idx + 1);
  1975. modify.c('parameter');
  1976. if (kv.indexOf(':') == -1) {
  1977. modify.attrs({ name: kv });
  1978. } else {
  1979. modify.attrs({ name: kv.split(':', 2)[0] });
  1980. modify.attrs({ value: kv.split(':', 2)[1] });
  1981. }
  1982. modify.up(); // end of parameter
  1983. });
  1984. modify.up(); // end of source
  1985. });
  1986. // generate source groups from lines
  1987. media.ssrcGroups.forEach(function(ssrcGroup) {
  1988. if (ssrcGroup.ssrcs.length != 0) {
  1989. modify.c('ssrc-group', {
  1990. semantics: ssrcGroup.semantics,
  1991. xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0'
  1992. });
  1993. ssrcGroup.ssrcs.forEach(function (ssrc) {
  1994. modify.c('source', { ssrc: ssrc })
  1995. .up(); // end of source
  1996. });
  1997. modify.up(); // end of ssrc-group
  1998. }
  1999. });
  2000. modify.up(); // end of description
  2001. modify.up(); // end of content
  2002. });
  2003. return modified;
  2004. };
  2005. module.exports = SDPDiffer;
  2006. },{}],4:[function(require,module,exports){
  2007. SDPUtil = {
  2008. iceparams: function (mediadesc, sessiondesc) {
  2009. var data = null;
  2010. if (SDPUtil.find_line(mediadesc, 'a=ice-ufrag:', sessiondesc) &&
  2011. SDPUtil.find_line(mediadesc, 'a=ice-pwd:', sessiondesc)) {
  2012. data = {
  2013. ufrag: SDPUtil.parse_iceufrag(SDPUtil.find_line(mediadesc, 'a=ice-ufrag:', sessiondesc)),
  2014. pwd: SDPUtil.parse_icepwd(SDPUtil.find_line(mediadesc, 'a=ice-pwd:', sessiondesc))
  2015. };
  2016. }
  2017. return data;
  2018. },
  2019. parse_iceufrag: function (line) {
  2020. return line.substring(12);
  2021. },
  2022. build_iceufrag: function (frag) {
  2023. return 'a=ice-ufrag:' + frag;
  2024. },
  2025. parse_icepwd: function (line) {
  2026. return line.substring(10);
  2027. },
  2028. build_icepwd: function (pwd) {
  2029. return 'a=ice-pwd:' + pwd;
  2030. },
  2031. parse_mid: function (line) {
  2032. return line.substring(6);
  2033. },
  2034. parse_mline: function (line) {
  2035. var parts = line.substring(2).split(' '),
  2036. data = {};
  2037. data.media = parts.shift();
  2038. data.port = parts.shift();
  2039. data.proto = parts.shift();
  2040. if (parts[parts.length - 1] === '') { // trailing whitespace
  2041. parts.pop();
  2042. }
  2043. data.fmt = parts;
  2044. return data;
  2045. },
  2046. build_mline: function (mline) {
  2047. return 'm=' + mline.media + ' ' + mline.port + ' ' + mline.proto + ' ' + mline.fmt.join(' ');
  2048. },
  2049. parse_rtpmap: function (line) {
  2050. var parts = line.substring(9).split(' '),
  2051. data = {};
  2052. data.id = parts.shift();
  2053. parts = parts[0].split('/');
  2054. data.name = parts.shift();
  2055. data.clockrate = parts.shift();
  2056. data.channels = parts.length ? parts.shift() : '1';
  2057. return data;
  2058. },
  2059. /**
  2060. * Parses SDP line "a=sctpmap:..." and extracts SCTP port from it.
  2061. * @param line eg. "a=sctpmap:5000 webrtc-datachannel"
  2062. * @returns [SCTP port number, protocol, streams]
  2063. */
  2064. parse_sctpmap: function (line)
  2065. {
  2066. var parts = line.substring(10).split(' ');
  2067. var sctpPort = parts[0];
  2068. var protocol = parts[1];
  2069. // Stream count is optional
  2070. var streamCount = parts.length > 2 ? parts[2] : null;
  2071. return [sctpPort, protocol, streamCount];// SCTP port
  2072. },
  2073. build_rtpmap: function (el) {
  2074. var line = 'a=rtpmap:' + el.getAttribute('id') + ' ' + el.getAttribute('name') + '/' + el.getAttribute('clockrate');
  2075. if (el.getAttribute('channels') && el.getAttribute('channels') != '1') {
  2076. line += '/' + el.getAttribute('channels');
  2077. }
  2078. return line;
  2079. },
  2080. parse_crypto: function (line) {
  2081. var parts = line.substring(9).split(' '),
  2082. data = {};
  2083. data.tag = parts.shift();
  2084. data['crypto-suite'] = parts.shift();
  2085. data['key-params'] = parts.shift();
  2086. if (parts.length) {
  2087. data['session-params'] = parts.join(' ');
  2088. }
  2089. return data;
  2090. },
  2091. parse_fingerprint: function (line) { // RFC 4572
  2092. var parts = line.substring(14).split(' '),
  2093. data = {};
  2094. data.hash = parts.shift();
  2095. data.fingerprint = parts.shift();
  2096. // TODO assert that fingerprint satisfies 2UHEX *(":" 2UHEX) ?
  2097. return data;
  2098. },
  2099. parse_fmtp: function (line) {
  2100. var parts = line.split(' '),
  2101. i, key, value,
  2102. data = [];
  2103. parts.shift();
  2104. parts = parts.join(' ').split(';');
  2105. for (i = 0; i < parts.length; i++) {
  2106. key = parts[i].split('=')[0];
  2107. while (key.length && key[0] == ' ') {
  2108. key = key.substring(1);
  2109. }
  2110. value = parts[i].split('=')[1];
  2111. if (key && value) {
  2112. data.push({name: key, value: value});
  2113. } else if (key) {
  2114. // rfc 4733 (DTMF) style stuff
  2115. data.push({name: '', value: key});
  2116. }
  2117. }
  2118. return data;
  2119. },
  2120. parse_icecandidate: function (line) {
  2121. var candidate = {},
  2122. elems = line.split(' ');
  2123. candidate.foundation = elems[0].substring(12);
  2124. candidate.component = elems[1];
  2125. candidate.protocol = elems[2].toLowerCase();
  2126. candidate.priority = elems[3];
  2127. candidate.ip = elems[4];
  2128. candidate.port = elems[5];
  2129. // elems[6] => "typ"
  2130. candidate.type = elems[7];
  2131. candidate.generation = 0; // default value, may be overwritten below
  2132. for (var i = 8; i < elems.length; i += 2) {
  2133. switch (elems[i]) {
  2134. case 'raddr':
  2135. candidate['rel-addr'] = elems[i + 1];
  2136. break;
  2137. case 'rport':
  2138. candidate['rel-port'] = elems[i + 1];
  2139. break;
  2140. case 'generation':
  2141. candidate.generation = elems[i + 1];
  2142. break;
  2143. case 'tcptype':
  2144. candidate.tcptype = elems[i + 1];
  2145. break;
  2146. default: // TODO
  2147. console.log('parse_icecandidate not translating "' + elems[i] + '" = "' + elems[i + 1] + '"');
  2148. }
  2149. }
  2150. candidate.network = '1';
  2151. candidate.id = Math.random().toString(36).substr(2, 10); // not applicable to SDP -- FIXME: should be unique, not just random
  2152. return candidate;
  2153. },
  2154. build_icecandidate: function (cand) {
  2155. var line = ['a=candidate:' + cand.foundation, cand.component, cand.protocol, cand.priority, cand.ip, cand.port, 'typ', cand.type].join(' ');
  2156. line += ' ';
  2157. switch (cand.type) {
  2158. case 'srflx':
  2159. case 'prflx':
  2160. case 'relay':
  2161. if (cand.hasOwnAttribute('rel-addr') && cand.hasOwnAttribute('rel-port')) {
  2162. line += 'raddr';
  2163. line += ' ';
  2164. line += cand['rel-addr'];
  2165. line += ' ';
  2166. line += 'rport';
  2167. line += ' ';
  2168. line += cand['rel-port'];
  2169. line += ' ';
  2170. }
  2171. break;
  2172. }
  2173. if (cand.hasOwnAttribute('tcptype')) {
  2174. line += 'tcptype';
  2175. line += ' ';
  2176. line += cand.tcptype;
  2177. line += ' ';
  2178. }
  2179. line += 'generation';
  2180. line += ' ';
  2181. line += cand.hasOwnAttribute('generation') ? cand.generation : '0';
  2182. return line;
  2183. },
  2184. parse_ssrc: function (desc) {
  2185. // proprietary mapping of a=ssrc lines
  2186. // TODO: see "Jingle RTP Source Description" by Juberti and P. Thatcher on google docs
  2187. // and parse according to that
  2188. var lines = desc.split('\r\n'),
  2189. data = {};
  2190. for (var i = 0; i < lines.length; i++) {
  2191. if (lines[i].substring(0, 7) == 'a=ssrc:') {
  2192. var idx = lines[i].indexOf(' ');
  2193. data[lines[i].substr(idx + 1).split(':', 2)[0]] = lines[i].substr(idx + 1).split(':', 2)[1];
  2194. }
  2195. }
  2196. return data;
  2197. },
  2198. parse_rtcpfb: function (line) {
  2199. var parts = line.substr(10).split(' ');
  2200. var data = {};
  2201. data.pt = parts.shift();
  2202. data.type = parts.shift();
  2203. data.params = parts;
  2204. return data;
  2205. },
  2206. parse_extmap: function (line) {
  2207. var parts = line.substr(9).split(' ');
  2208. var data = {};
  2209. data.value = parts.shift();
  2210. if (data.value.indexOf('/') != -1) {
  2211. data.direction = data.value.substr(data.value.indexOf('/') + 1);
  2212. data.value = data.value.substr(0, data.value.indexOf('/'));
  2213. } else {
  2214. data.direction = 'both';
  2215. }
  2216. data.uri = parts.shift();
  2217. data.params = parts;
  2218. return data;
  2219. },
  2220. find_line: function (haystack, needle, sessionpart) {
  2221. var lines = haystack.split('\r\n');
  2222. for (var i = 0; i < lines.length; i++) {
  2223. if (lines[i].substring(0, needle.length) == needle) {
  2224. return lines[i];
  2225. }
  2226. }
  2227. if (!sessionpart) {
  2228. return false;
  2229. }
  2230. // search session part
  2231. lines = sessionpart.split('\r\n');
  2232. for (var j = 0; j < lines.length; j++) {
  2233. if (lines[j].substring(0, needle.length) == needle) {
  2234. return lines[j];
  2235. }
  2236. }
  2237. return false;
  2238. },
  2239. find_lines: function (haystack, needle, sessionpart) {
  2240. var lines = haystack.split('\r\n'),
  2241. needles = [];
  2242. for (var i = 0; i < lines.length; i++) {
  2243. if (lines[i].substring(0, needle.length) == needle)
  2244. needles.push(lines[i]);
  2245. }
  2246. if (needles.length || !sessionpart) {
  2247. return needles;
  2248. }
  2249. // search session part
  2250. lines = sessionpart.split('\r\n');
  2251. for (var j = 0; j < lines.length; j++) {
  2252. if (lines[j].substring(0, needle.length) == needle) {
  2253. needles.push(lines[j]);
  2254. }
  2255. }
  2256. return needles;
  2257. },
  2258. candidateToJingle: function (line) {
  2259. // a=candidate:2979166662 1 udp 2113937151 192.168.2.100 57698 typ host generation 0
  2260. // <candidate component=... foundation=... generation=... id=... ip=... network=... port=... priority=... protocol=... type=.../>
  2261. if (line.indexOf('candidate:') === 0) {
  2262. line = 'a=' + line;
  2263. } else if (line.substring(0, 12) != 'a=candidate:') {
  2264. console.log('parseCandidate called with a line that is not a candidate line');
  2265. console.log(line);
  2266. return null;
  2267. }
  2268. if (line.substring(line.length - 2) == '\r\n') // chomp it
  2269. line = line.substring(0, line.length - 2);
  2270. var candidate = {},
  2271. elems = line.split(' '),
  2272. i;
  2273. if (elems[6] != 'typ') {
  2274. console.log('did not find typ in the right place');
  2275. console.log(line);
  2276. return null;
  2277. }
  2278. candidate.foundation = elems[0].substring(12);
  2279. candidate.component = elems[1];
  2280. candidate.protocol = elems[2].toLowerCase();
  2281. candidate.priority = elems[3];
  2282. candidate.ip = elems[4];
  2283. candidate.port = elems[5];
  2284. // elems[6] => "typ"
  2285. candidate.type = elems[7];
  2286. candidate.generation = '0'; // default, may be overwritten below
  2287. for (i = 8; i < elems.length; i += 2) {
  2288. switch (elems[i]) {
  2289. case 'raddr':
  2290. candidate['rel-addr'] = elems[i + 1];
  2291. break;
  2292. case 'rport':
  2293. candidate['rel-port'] = elems[i + 1];
  2294. break;
  2295. case 'generation':
  2296. candidate.generation = elems[i + 1];
  2297. break;
  2298. case 'tcptype':
  2299. candidate.tcptype = elems[i + 1];
  2300. break;
  2301. default: // TODO
  2302. console.log('not translating "' + elems[i] + '" = "' + elems[i + 1] + '"');
  2303. }
  2304. }
  2305. candidate.network = '1';
  2306. candidate.id = Math.random().toString(36).substr(2, 10); // not applicable to SDP -- FIXME: should be unique, not just random
  2307. return candidate;
  2308. },
  2309. candidateFromJingle: function (cand) {
  2310. var line = 'a=candidate:';
  2311. line += cand.getAttribute('foundation');
  2312. line += ' ';
  2313. line += cand.getAttribute('component');
  2314. line += ' ';
  2315. line += cand.getAttribute('protocol'); //.toUpperCase(); // chrome M23 doesn't like this
  2316. line += ' ';
  2317. line += cand.getAttribute('priority');
  2318. line += ' ';
  2319. line += cand.getAttribute('ip');
  2320. line += ' ';
  2321. line += cand.getAttribute('port');
  2322. line += ' ';
  2323. line += 'typ';
  2324. line += ' ' + cand.getAttribute('type');
  2325. line += ' ';
  2326. switch (cand.getAttribute('type')) {
  2327. case 'srflx':
  2328. case 'prflx':
  2329. case 'relay':
  2330. if (cand.getAttribute('rel-addr') && cand.getAttribute('rel-port')) {
  2331. line += 'raddr';
  2332. line += ' ';
  2333. line += cand.getAttribute('rel-addr');
  2334. line += ' ';
  2335. line += 'rport';
  2336. line += ' ';
  2337. line += cand.getAttribute('rel-port');
  2338. line += ' ';
  2339. }
  2340. break;
  2341. }
  2342. if (cand.getAttribute('protocol').toLowerCase() == 'tcp') {
  2343. line += 'tcptype';
  2344. line += ' ';
  2345. line += cand.getAttribute('tcptype');
  2346. line += ' ';
  2347. }
  2348. line += 'generation';
  2349. line += ' ';
  2350. line += cand.getAttribute('generation') || '0';
  2351. return line + '\r\n';
  2352. }
  2353. };
  2354. module.exports = SDPUtil;
  2355. },{}],5:[function(require,module,exports){
  2356. function TraceablePeerConnection(ice_config, constraints) {
  2357. var self = this;
  2358. var RTCPeerconnection = navigator.mozGetUserMedia ? mozRTCPeerConnection : webkitRTCPeerConnection;
  2359. this.peerconnection = new RTCPeerconnection(ice_config, constraints);
  2360. this.updateLog = [];
  2361. this.stats = {};
  2362. this.statsinterval = null;
  2363. this.maxstats = 0; // limit to 300 values, i.e. 5 minutes; set to 0 to disable
  2364. // override as desired
  2365. this.trace = function (what, info) {
  2366. //console.warn('WTRACE', what, info);
  2367. self.updateLog.push({
  2368. time: new Date(),
  2369. type: what,
  2370. value: info || ""
  2371. });
  2372. };
  2373. this.onicecandidate = null;
  2374. this.peerconnection.onicecandidate = function (event) {
  2375. self.trace('onicecandidate', JSON.stringify(event.candidate, null, ' '));
  2376. if (self.onicecandidate !== null) {
  2377. self.onicecandidate(event);
  2378. }
  2379. };
  2380. this.onaddstream = null;
  2381. this.peerconnection.onaddstream = function (event) {
  2382. self.trace('onaddstream', event.stream.id);
  2383. if (self.onaddstream !== null) {
  2384. self.onaddstream(event);
  2385. }
  2386. };
  2387. this.onremovestream = null;
  2388. this.peerconnection.onremovestream = function (event) {
  2389. self.trace('onremovestream', event.stream.id);
  2390. if (self.onremovestream !== null) {
  2391. self.onremovestream(event);
  2392. }
  2393. };
  2394. this.onsignalingstatechange = null;
  2395. this.peerconnection.onsignalingstatechange = function (event) {
  2396. self.trace('onsignalingstatechange', self.signalingState);
  2397. if (self.onsignalingstatechange !== null) {
  2398. self.onsignalingstatechange(event);
  2399. }
  2400. };
  2401. this.oniceconnectionstatechange = null;
  2402. this.peerconnection.oniceconnectionstatechange = function (event) {
  2403. self.trace('oniceconnectionstatechange', self.iceConnectionState);
  2404. if (self.oniceconnectionstatechange !== null) {
  2405. self.oniceconnectionstatechange(event);
  2406. }
  2407. };
  2408. this.onnegotiationneeded = null;
  2409. this.peerconnection.onnegotiationneeded = function (event) {
  2410. self.trace('onnegotiationneeded');
  2411. if (self.onnegotiationneeded !== null) {
  2412. self.onnegotiationneeded(event);
  2413. }
  2414. };
  2415. self.ondatachannel = null;
  2416. this.peerconnection.ondatachannel = function (event) {
  2417. self.trace('ondatachannel', event);
  2418. if (self.ondatachannel !== null) {
  2419. self.ondatachannel(event);
  2420. }
  2421. };
  2422. if (!navigator.mozGetUserMedia && this.maxstats) {
  2423. this.statsinterval = window.setInterval(function() {
  2424. self.peerconnection.getStats(function(stats) {
  2425. var results = stats.result();
  2426. for (var i = 0; i < results.length; ++i) {
  2427. //console.log(results[i].type, results[i].id, results[i].names())
  2428. var now = new Date();
  2429. results[i].names().forEach(function (name) {
  2430. var id = results[i].id + '-' + name;
  2431. if (!self.stats[id]) {
  2432. self.stats[id] = {
  2433. startTime: now,
  2434. endTime: now,
  2435. values: [],
  2436. times: []
  2437. };
  2438. }
  2439. self.stats[id].values.push(results[i].stat(name));
  2440. self.stats[id].times.push(now.getTime());
  2441. if (self.stats[id].values.length > self.maxstats) {
  2442. self.stats[id].values.shift();
  2443. self.stats[id].times.shift();
  2444. }
  2445. self.stats[id].endTime = now;
  2446. });
  2447. }
  2448. });
  2449. }, 1000);
  2450. }
  2451. };
  2452. dumpSDP = function(description) {
  2453. return 'type: ' + description.type + '\r\n' + description.sdp;
  2454. }
  2455. if (TraceablePeerConnection.prototype.__defineGetter__ !== undefined) {
  2456. TraceablePeerConnection.prototype.__defineGetter__('signalingState', function() { return this.peerconnection.signalingState; });
  2457. TraceablePeerConnection.prototype.__defineGetter__('iceConnectionState', function() { return this.peerconnection.iceConnectionState; });
  2458. TraceablePeerConnection.prototype.__defineGetter__('localDescription', function() {
  2459. var publicLocalDescription = simulcast.reverseTransformLocalDescription(this.peerconnection.localDescription);
  2460. return publicLocalDescription;
  2461. });
  2462. TraceablePeerConnection.prototype.__defineGetter__('remoteDescription', function() {
  2463. var publicRemoteDescription = simulcast.reverseTransformRemoteDescription(this.peerconnection.remoteDescription);
  2464. return publicRemoteDescription;
  2465. });
  2466. }
  2467. TraceablePeerConnection.prototype.addStream = function (stream) {
  2468. this.trace('addStream', stream.id);
  2469. simulcast.resetSender();
  2470. try
  2471. {
  2472. this.peerconnection.addStream(stream);
  2473. }
  2474. catch (e)
  2475. {
  2476. console.error(e);
  2477. return;
  2478. }
  2479. };
  2480. TraceablePeerConnection.prototype.removeStream = function (stream, stopStreams) {
  2481. this.trace('removeStream', stream.id);
  2482. simulcast.resetSender();
  2483. if(stopStreams) {
  2484. stream.getAudioTracks().forEach(function (track) {
  2485. track.stop();
  2486. });
  2487. stream.getVideoTracks().forEach(function (track) {
  2488. track.stop();
  2489. });
  2490. }
  2491. this.peerconnection.removeStream(stream);
  2492. };
  2493. TraceablePeerConnection.prototype.createDataChannel = function (label, opts) {
  2494. this.trace('createDataChannel', label, opts);
  2495. return this.peerconnection.createDataChannel(label, opts);
  2496. };
  2497. TraceablePeerConnection.prototype.setLocalDescription = function (description, successCallback, failureCallback) {
  2498. var self = this;
  2499. description = simulcast.transformLocalDescription(description);
  2500. this.trace('setLocalDescription', dumpSDP(description));
  2501. this.peerconnection.setLocalDescription(description,
  2502. function () {
  2503. self.trace('setLocalDescriptionOnSuccess');
  2504. successCallback();
  2505. },
  2506. function (err) {
  2507. self.trace('setLocalDescriptionOnFailure', err);
  2508. failureCallback(err);
  2509. }
  2510. );
  2511. /*
  2512. if (this.statsinterval === null && this.maxstats > 0) {
  2513. // start gathering stats
  2514. }
  2515. */
  2516. };
  2517. TraceablePeerConnection.prototype.setRemoteDescription = function (description, successCallback, failureCallback) {
  2518. var self = this;
  2519. description = simulcast.transformRemoteDescription(description);
  2520. this.trace('setRemoteDescription', dumpSDP(description));
  2521. this.peerconnection.setRemoteDescription(description,
  2522. function () {
  2523. self.trace('setRemoteDescriptionOnSuccess');
  2524. successCallback();
  2525. },
  2526. function (err) {
  2527. self.trace('setRemoteDescriptionOnFailure', err);
  2528. failureCallback(err);
  2529. }
  2530. );
  2531. /*
  2532. if (this.statsinterval === null && this.maxstats > 0) {
  2533. // start gathering stats
  2534. }
  2535. */
  2536. };
  2537. TraceablePeerConnection.prototype.close = function () {
  2538. this.trace('stop');
  2539. if (this.statsinterval !== null) {
  2540. window.clearInterval(this.statsinterval);
  2541. this.statsinterval = null;
  2542. }
  2543. this.peerconnection.close();
  2544. };
  2545. TraceablePeerConnection.prototype.createOffer = function (successCallback, failureCallback, constraints) {
  2546. var self = this;
  2547. this.trace('createOffer', JSON.stringify(constraints, null, ' '));
  2548. this.peerconnection.createOffer(
  2549. function (offer) {
  2550. self.trace('createOfferOnSuccess', dumpSDP(offer));
  2551. successCallback(offer);
  2552. },
  2553. function(err) {
  2554. self.trace('createOfferOnFailure', err);
  2555. failureCallback(err);
  2556. },
  2557. constraints
  2558. );
  2559. };
  2560. TraceablePeerConnection.prototype.createAnswer = function (successCallback, failureCallback, constraints) {
  2561. var self = this;
  2562. this.trace('createAnswer', JSON.stringify(constraints, null, ' '));
  2563. this.peerconnection.createAnswer(
  2564. function (answer) {
  2565. answer = simulcast.transformAnswer(answer);
  2566. self.trace('createAnswerOnSuccess', dumpSDP(answer));
  2567. successCallback(answer);
  2568. },
  2569. function(err) {
  2570. self.trace('createAnswerOnFailure', err);
  2571. failureCallback(err);
  2572. },
  2573. constraints
  2574. );
  2575. };
  2576. TraceablePeerConnection.prototype.addIceCandidate = function (candidate, successCallback, failureCallback) {
  2577. var self = this;
  2578. this.trace('addIceCandidate', JSON.stringify(candidate, null, ' '));
  2579. this.peerconnection.addIceCandidate(candidate);
  2580. /* maybe later
  2581. this.peerconnection.addIceCandidate(candidate,
  2582. function () {
  2583. self.trace('addIceCandidateOnSuccess');
  2584. successCallback();
  2585. },
  2586. function (err) {
  2587. self.trace('addIceCandidateOnFailure', err);
  2588. failureCallback(err);
  2589. }
  2590. );
  2591. */
  2592. };
  2593. TraceablePeerConnection.prototype.getStats = function(callback, errback) {
  2594. if (navigator.mozGetUserMedia) {
  2595. // ignore for now...
  2596. if(!errback)
  2597. errback = function () {
  2598. }
  2599. this.peerconnection.getStats(null,callback,errback);
  2600. } else {
  2601. this.peerconnection.getStats(callback);
  2602. }
  2603. };
  2604. module.exports = TraceablePeerConnection;
  2605. },{}],6:[function(require,module,exports){
  2606. /* global $, $iq, config, connection, UI, messageHandler,
  2607. roomName, sessionTerminated, Strophe, Util */
  2608. /**
  2609. * Contains logic responsible for enabling/disabling functionality available
  2610. * only to moderator users.
  2611. */
  2612. var connection = null;
  2613. var focusUserJid;
  2614. var getNextTimeout = Util.createExpBackoffTimer(1000);
  2615. var getNextErrorTimeout = Util.createExpBackoffTimer(1000);
  2616. // External authentication stuff
  2617. var externalAuthEnabled = false;
  2618. // Sip gateway can be enabled by configuring Jigasi host in config.js or
  2619. // it will be enabled automatically if focus detects the component through
  2620. // service discovery.
  2621. var sipGatewayEnabled = config.hosts.call_control !== undefined;
  2622. var Moderator = {
  2623. isModerator: function () {
  2624. return connection && connection.emuc.isModerator();
  2625. },
  2626. isPeerModerator: function (peerJid) {
  2627. return connection &&
  2628. connection.emuc.getMemberRole(peerJid) === 'moderator';
  2629. },
  2630. isExternalAuthEnabled: function () {
  2631. return externalAuthEnabled;
  2632. },
  2633. isSipGatewayEnabled: function () {
  2634. return sipGatewayEnabled;
  2635. },
  2636. setConnection: function (con) {
  2637. connection = con;
  2638. },
  2639. init: function (xmpp) {
  2640. this.xmppService = xmpp;
  2641. this.onLocalRoleChange = function (from, member, pres) {
  2642. UI.onModeratorStatusChanged(Moderator.isModerator());
  2643. };
  2644. },
  2645. onMucLeft: function (jid) {
  2646. console.info("Someone left is it focus ? " + jid);
  2647. var resource = Strophe.getResourceFromJid(jid);
  2648. if (resource === 'focus' && !this.xmppService.sessionTerminated) {
  2649. console.info(
  2650. "Focus has left the room - leaving conference");
  2651. //hangUp();
  2652. // We'd rather reload to have everything re-initialized
  2653. // FIXME: show some message before reload
  2654. location.reload();
  2655. }
  2656. },
  2657. setFocusUserJid: function (focusJid) {
  2658. if (!focusUserJid) {
  2659. focusUserJid = focusJid;
  2660. console.info("Focus jid set to: " + focusUserJid);
  2661. }
  2662. },
  2663. getFocusUserJid: function () {
  2664. return focusUserJid;
  2665. },
  2666. getFocusComponent: function () {
  2667. // Get focus component address
  2668. var focusComponent = config.hosts.focus;
  2669. // If not specified use default: 'focus.domain'
  2670. if (!focusComponent) {
  2671. focusComponent = 'focus.' + config.hosts.domain;
  2672. }
  2673. return focusComponent;
  2674. },
  2675. createConferenceIq: function (roomName) {
  2676. // Generate create conference IQ
  2677. var elem = $iq({to: Moderator.getFocusComponent(), type: 'set'});
  2678. elem.c('conference', {
  2679. xmlns: 'http://jitsi.org/protocol/focus',
  2680. room: roomName
  2681. });
  2682. if (config.hosts.bridge !== undefined) {
  2683. elem.c(
  2684. 'property',
  2685. { name: 'bridge', value: config.hosts.bridge})
  2686. .up();
  2687. }
  2688. // Tell the focus we have Jigasi configured
  2689. if (config.hosts.call_control !== undefined) {
  2690. elem.c(
  2691. 'property',
  2692. { name: 'call_control', value: config.hosts.call_control})
  2693. .up();
  2694. }
  2695. if (config.channelLastN !== undefined) {
  2696. elem.c(
  2697. 'property',
  2698. { name: 'channelLastN', value: config.channelLastN})
  2699. .up();
  2700. }
  2701. if (config.adaptiveLastN !== undefined) {
  2702. elem.c(
  2703. 'property',
  2704. { name: 'adaptiveLastN', value: config.adaptiveLastN})
  2705. .up();
  2706. }
  2707. if (config.adaptiveSimulcast !== undefined) {
  2708. elem.c(
  2709. 'property',
  2710. { name: 'adaptiveSimulcast', value: config.adaptiveSimulcast})
  2711. .up();
  2712. }
  2713. if (config.openSctp !== undefined) {
  2714. elem.c(
  2715. 'property',
  2716. { name: 'openSctp', value: config.openSctp})
  2717. .up();
  2718. }
  2719. if (config.enableFirefoxSupport !== undefined) {
  2720. elem.c(
  2721. 'property',
  2722. { name: 'enableFirefoxHacks',
  2723. value: config.enableFirefoxSupport})
  2724. .up();
  2725. }
  2726. elem.up();
  2727. return elem;
  2728. },
  2729. parseConfigOptions: function (resultIq) {
  2730. Moderator.setFocusUserJid(
  2731. $(resultIq).find('conference').attr('focusjid'));
  2732. var extAuthParam
  2733. = $(resultIq).find('>conference>property[name=\'externalAuth\']');
  2734. if (extAuthParam.length) {
  2735. externalAuthEnabled = extAuthParam.attr('value') === 'true';
  2736. }
  2737. console.info("External authentication enabled: " + externalAuthEnabled);
  2738. // Check if focus has auto-detected Jigasi component(this will be also
  2739. // included if we have passed our host from the config)
  2740. if ($(resultIq).find(
  2741. '>conference>property[name=\'sipGatewayEnabled\']').length) {
  2742. sipGatewayEnabled = true;
  2743. }
  2744. console.info("Sip gateway enabled: " + sipGatewayEnabled);
  2745. },
  2746. // FIXME: we need to show the fact that we're waiting for the focus
  2747. // to the user(or that focus is not available)
  2748. allocateConferenceFocus: function (roomName, callback) {
  2749. // Try to use focus user JID from the config
  2750. Moderator.setFocusUserJid(config.focusUserJid);
  2751. // Send create conference IQ
  2752. var iq = Moderator.createConferenceIq(roomName);
  2753. connection.sendIQ(
  2754. iq,
  2755. function (result) {
  2756. if ('true' === $(result).find('conference').attr('ready')) {
  2757. // Reset both timers
  2758. getNextTimeout(true);
  2759. getNextErrorTimeout(true);
  2760. // Setup config options
  2761. Moderator.parseConfigOptions(result);
  2762. // Exec callback
  2763. callback();
  2764. } else {
  2765. var waitMs = getNextTimeout();
  2766. console.info("Waiting for the focus... " + waitMs);
  2767. // Reset error timeout
  2768. getNextErrorTimeout(true);
  2769. window.setTimeout(
  2770. function () {
  2771. Moderator.allocateConferenceFocus(
  2772. roomName, callback);
  2773. }, waitMs);
  2774. }
  2775. },
  2776. function (error) {
  2777. // Not authorized to create new room
  2778. if ($(error).find('>error>not-authorized').length) {
  2779. console.warn("Unauthorized to start the conference");
  2780. UI.onAuthenticationRequired(function () {
  2781. Moderator.allocateConferenceFocus(roomName, callback);
  2782. });
  2783. return;
  2784. }
  2785. var waitMs = getNextErrorTimeout();
  2786. console.error("Focus error, retry after " + waitMs, error);
  2787. // Show message
  2788. UI.messageHandler.notify(
  2789. 'Conference focus', 'disconnected',
  2790. Moderator.getFocusComponent() +
  2791. ' not available - retry in ' +
  2792. (waitMs / 1000) + ' sec');
  2793. // Reset response timeout
  2794. getNextTimeout(true);
  2795. window.setTimeout(
  2796. function () {
  2797. Moderator.allocateConferenceFocus(roomName, callback);
  2798. }, waitMs);
  2799. }
  2800. );
  2801. },
  2802. getAuthUrl: function (roomName, urlCallback) {
  2803. var iq = $iq({to: Moderator.getFocusComponent(), type: 'get'});
  2804. iq.c('auth-url', {
  2805. xmlns: 'http://jitsi.org/protocol/focus',
  2806. room: roomName
  2807. });
  2808. connection.sendIQ(
  2809. iq,
  2810. function (result) {
  2811. var url = $(result).find('auth-url').attr('url');
  2812. if (url) {
  2813. console.info("Got auth url: " + url);
  2814. urlCallback(url);
  2815. } else {
  2816. console.error(
  2817. "Failed to get auth url fro mthe focus", result);
  2818. }
  2819. },
  2820. function (error) {
  2821. console.error("Get auth url error", error);
  2822. }
  2823. );
  2824. }
  2825. };
  2826. module.exports = Moderator;
  2827. },{}],7:[function(require,module,exports){
  2828. /* global $, $iq, config, connection, focusMucJid, messageHandler, Moderator,
  2829. Toolbar, Util */
  2830. var Moderator = require("./moderator");
  2831. var recordingToken = null;
  2832. var recordingEnabled;
  2833. /**
  2834. * Whether to use a jirecon component for recording, or use the videobridge
  2835. * through COLIBRI.
  2836. */
  2837. var useJirecon = (typeof config.hosts.jirecon != "undefined");
  2838. /**
  2839. * The ID of the jirecon recording session. Jirecon generates it when we
  2840. * initially start recording, and it needs to be used in subsequent requests
  2841. * to jirecon.
  2842. */
  2843. var jireconRid = null;
  2844. function setRecordingToken(token) {
  2845. recordingToken = token;
  2846. }
  2847. function setRecording(state, token, callback) {
  2848. if (useJirecon){
  2849. this.setRecordingJirecon(state, token, callback);
  2850. } else {
  2851. this.setRecordingColibri(state, token, callback);
  2852. }
  2853. }
  2854. function setRecordingJirecon(state, token, callback) {
  2855. if (state == recordingEnabled){
  2856. return;
  2857. }
  2858. var iq = $iq({to: config.hosts.jirecon, type: 'set'})
  2859. .c('recording', {xmlns: 'http://jitsi.org/protocol/jirecon',
  2860. action: state ? 'start' : 'stop',
  2861. mucjid: connection.emuc.roomjid});
  2862. if (!state){
  2863. iq.attrs({rid: jireconRid});
  2864. }
  2865. console.log('Start recording');
  2866. connection.sendIQ(
  2867. iq,
  2868. function (result) {
  2869. // TODO wait for an IQ with the real status, since this is
  2870. // provisional?
  2871. jireconRid = $(result).find('recording').attr('rid');
  2872. console.log('Recording ' + (state ? 'started' : 'stopped') +
  2873. '(jirecon)' + result);
  2874. recordingEnabled = state;
  2875. if (!state){
  2876. jireconRid = null;
  2877. }
  2878. callback(state);
  2879. },
  2880. function (error) {
  2881. console.log('Failed to start recording, error: ', error);
  2882. callback(recordingEnabled);
  2883. });
  2884. }
  2885. // Sends a COLIBRI message which enables or disables (according to 'state')
  2886. // the recording on the bridge. Waits for the result IQ and calls 'callback'
  2887. // with the new recording state, according to the IQ.
  2888. function setRecordingColibri(state, token, callback) {
  2889. var elem = $iq({to: focusMucJid, type: 'set'});
  2890. elem.c('conference', {
  2891. xmlns: 'http://jitsi.org/protocol/colibri'
  2892. });
  2893. elem.c('recording', {state: state, token: token});
  2894. connection.sendIQ(elem,
  2895. function (result) {
  2896. console.log('Set recording "', state, '". Result:', result);
  2897. var recordingElem = $(result).find('>conference>recording');
  2898. var newState = ('true' === recordingElem.attr('state'));
  2899. recordingEnabled = newState;
  2900. callback(newState);
  2901. },
  2902. function (error) {
  2903. console.warn(error);
  2904. callback(recordingEnabled);
  2905. }
  2906. );
  2907. }
  2908. var Recording = {
  2909. toggleRecording: function (tokenEmptyCallback,
  2910. startingCallback, startedCallback) {
  2911. if (!Moderator.isModerator()) {
  2912. console.log(
  2913. 'non-focus, or conference not yet organized:' +
  2914. ' not enabling recording');
  2915. return;
  2916. }
  2917. // Jirecon does not (currently) support a token.
  2918. if (!recordingToken && !useJirecon) {
  2919. tokenEmptyCallback(function (value) {
  2920. setRecordingToken(value);
  2921. this.toggleRecording();
  2922. });
  2923. return;
  2924. }
  2925. var oldState = recordingEnabled;
  2926. startingCallback(!oldState);
  2927. setRecording(!oldState,
  2928. recordingToken,
  2929. function (state) {
  2930. console.log("New recording state: ", state);
  2931. if (state === oldState) {
  2932. // FIXME: new focus:
  2933. // this will not work when moderator changes
  2934. // during active session. Then it will assume that
  2935. // recording status has changed to true, but it might have
  2936. // been already true(and we only received actual status from
  2937. // the focus).
  2938. //
  2939. // SO we start with status null, so that it is initialized
  2940. // here and will fail only after second click, so if invalid
  2941. // token was used we have to press the button twice before
  2942. // current status will be fetched and token will be reset.
  2943. //
  2944. // Reliable way would be to return authentication error.
  2945. // Or status update when moderator connects.
  2946. // Or we have to stop recording session when current
  2947. // moderator leaves the room.
  2948. // Failed to change, reset the token because it might
  2949. // have been wrong
  2950. setRecordingToken(null);
  2951. }
  2952. startedCallback(state);
  2953. }
  2954. );
  2955. }
  2956. }
  2957. module.exports = Recording;
  2958. },{"./moderator":6}],8:[function(require,module,exports){
  2959. /* jshint -W117 */
  2960. /* a simple MUC connection plugin
  2961. * can only handle a single MUC room
  2962. */
  2963. var bridgeIsDown = false;
  2964. var Moderator = require("./moderator");
  2965. module.exports = function(XMPP, eventEmitter) {
  2966. Strophe.addConnectionPlugin('emuc', {
  2967. connection: null,
  2968. roomjid: null,
  2969. myroomjid: null,
  2970. members: {},
  2971. list_members: [], // so we can elect a new focus
  2972. presMap: {},
  2973. preziMap: {},
  2974. joined: false,
  2975. isOwner: false,
  2976. role: null,
  2977. init: function (conn) {
  2978. this.connection = conn;
  2979. },
  2980. initPresenceMap: function (myroomjid) {
  2981. this.presMap['to'] = myroomjid;
  2982. this.presMap['xns'] = 'http://jabber.org/protocol/muc';
  2983. },
  2984. doJoin: function (jid, password) {
  2985. this.myroomjid = jid;
  2986. console.info("Joined MUC as " + this.myroomjid);
  2987. this.initPresenceMap(this.myroomjid);
  2988. if (!this.roomjid) {
  2989. this.roomjid = Strophe.getBareJidFromJid(jid);
  2990. // add handlers (just once)
  2991. this.connection.addHandler(this.onPresence.bind(this), null, 'presence', null, null, this.roomjid, {matchBare: true});
  2992. this.connection.addHandler(this.onPresenceUnavailable.bind(this), null, 'presence', 'unavailable', null, this.roomjid, {matchBare: true});
  2993. this.connection.addHandler(this.onPresenceError.bind(this), null, 'presence', 'error', null, this.roomjid, {matchBare: true});
  2994. this.connection.addHandler(this.onMessage.bind(this), null, 'message', null, null, this.roomjid, {matchBare: true});
  2995. }
  2996. if (password !== undefined) {
  2997. this.presMap['password'] = password;
  2998. }
  2999. this.sendPresence();
  3000. },
  3001. doLeave: function () {
  3002. console.log("do leave", this.myroomjid);
  3003. var pres = $pres({to: this.myroomjid, type: 'unavailable' });
  3004. this.presMap.length = 0;
  3005. this.connection.send(pres);
  3006. },
  3007. createNonAnonymousRoom: function () {
  3008. // http://xmpp.org/extensions/xep-0045.html#createroom-reserved
  3009. var getForm = $iq({type: 'get', to: this.roomjid})
  3010. .c('query', {xmlns: 'http://jabber.org/protocol/muc#owner'})
  3011. .c('x', {xmlns: 'jabber:x:data', type: 'submit'});
  3012. this.connection.sendIQ(getForm, function (form) {
  3013. if (!$(form).find(
  3014. '>query>x[xmlns="jabber:x:data"]' +
  3015. '>field[var="muc#roomconfig_whois"]').length) {
  3016. console.error('non-anonymous rooms not supported');
  3017. return;
  3018. }
  3019. var formSubmit = $iq({to: this.roomjid, type: 'set'})
  3020. .c('query', {xmlns: 'http://jabber.org/protocol/muc#owner'});
  3021. formSubmit.c('x', {xmlns: 'jabber:x:data', type: 'submit'});
  3022. formSubmit.c('field', {'var': 'FORM_TYPE'})
  3023. .c('value')
  3024. .t('http://jabber.org/protocol/muc#roomconfig').up().up();
  3025. formSubmit.c('field', {'var': 'muc#roomconfig_whois'})
  3026. .c('value').t('anyone').up().up();
  3027. this.connection.sendIQ(formSubmit);
  3028. }, function (error) {
  3029. console.error("Error getting room configuration form");
  3030. });
  3031. },
  3032. onPresence: function (pres) {
  3033. var from = pres.getAttribute('from');
  3034. // What is this for? A workaround for something?
  3035. if (pres.getAttribute('type')) {
  3036. return true;
  3037. }
  3038. // Parse etherpad tag.
  3039. var etherpad = $(pres).find('>etherpad');
  3040. if (etherpad.length) {
  3041. if (config.etherpad_base && !Moderator.isModerator()) {
  3042. UI.initEtherpad(etherpad.text());
  3043. }
  3044. }
  3045. // Parse prezi tag.
  3046. var presentation = $(pres).find('>prezi');
  3047. if (presentation.length) {
  3048. var url = presentation.attr('url');
  3049. var current = presentation.find('>current').text();
  3050. console.log('presentation info received from', from, url);
  3051. if (this.preziMap[from] == null) {
  3052. this.preziMap[from] = url;
  3053. $(document).trigger('presentationadded.muc', [from, url, current]);
  3054. }
  3055. else {
  3056. $(document).trigger('gotoslide.muc', [from, url, current]);
  3057. }
  3058. }
  3059. else if (this.preziMap[from] != null) {
  3060. var url = this.preziMap[from];
  3061. delete this.preziMap[from];
  3062. $(document).trigger('presentationremoved.muc', [from, url]);
  3063. }
  3064. // Parse audio info tag.
  3065. var audioMuted = $(pres).find('>audiomuted');
  3066. if (audioMuted.length) {
  3067. $(document).trigger('audiomuted.muc', [from, audioMuted.text()]);
  3068. }
  3069. // Parse video info tag.
  3070. var videoMuted = $(pres).find('>videomuted');
  3071. if (videoMuted.length) {
  3072. $(document).trigger('videomuted.muc', [from, videoMuted.text()]);
  3073. }
  3074. var stats = $(pres).find('>stats');
  3075. if (stats.length) {
  3076. var statsObj = {};
  3077. Strophe.forEachChild(stats[0], "stat", function (el) {
  3078. statsObj[el.getAttribute("name")] = el.getAttribute("value");
  3079. });
  3080. connectionquality.updateRemoteStats(from, statsObj);
  3081. }
  3082. // Parse status.
  3083. if ($(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="201"]').length) {
  3084. this.isOwner = true;
  3085. this.createNonAnonymousRoom();
  3086. }
  3087. // Parse roles.
  3088. var member = {};
  3089. member.show = $(pres).find('>show').text();
  3090. member.status = $(pres).find('>status').text();
  3091. var tmp = $(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>item');
  3092. member.affiliation = tmp.attr('affiliation');
  3093. member.role = tmp.attr('role');
  3094. // Focus recognition
  3095. member.jid = tmp.attr('jid');
  3096. member.isFocus = false;
  3097. if (member.jid
  3098. && member.jid.indexOf(Moderator.getFocusUserJid() + "/") == 0) {
  3099. member.isFocus = true;
  3100. }
  3101. var nicktag = $(pres).find('>nick[xmlns="http://jabber.org/protocol/nick"]');
  3102. member.displayName = (nicktag.length > 0 ? nicktag.html() : null);
  3103. if (from == this.myroomjid) {
  3104. if (member.affiliation == 'owner') this.isOwner = true;
  3105. if (this.role !== member.role) {
  3106. this.role = member.role;
  3107. if (Moderator.onLocalRoleChange)
  3108. Moderator.onLocalRoleChange(from, member, pres);
  3109. UI.onLocalRoleChange(from, member, pres);
  3110. }
  3111. if (!this.joined) {
  3112. this.joined = true;
  3113. eventEmitter.emit(XMPPEvents.MUC_JOINED, from, member);
  3114. this.list_members.push(from);
  3115. }
  3116. } else if (this.members[from] === undefined) {
  3117. // new participant
  3118. this.members[from] = member;
  3119. this.list_members.push(from);
  3120. console.log('entered', from, member);
  3121. if (member.isFocus) {
  3122. focusMucJid = from;
  3123. console.info("Ignore focus: " + from + ", real JID: " + member.jid);
  3124. }
  3125. else {
  3126. var id = $(pres).find('>userID').text();
  3127. var email = $(pres).find('>email');
  3128. if (email.length > 0) {
  3129. id = email.text();
  3130. }
  3131. UI.onMucEntered(from, id, member.displayName);
  3132. API.triggerEvent("participantJoined", {jid: from});
  3133. }
  3134. } else {
  3135. // Presence update for existing participant
  3136. // Watch role change:
  3137. if (this.members[from].role != member.role) {
  3138. this.members[from].role = member.role;
  3139. UI.onMucRoleChanged(member.role, member.displayName);
  3140. }
  3141. }
  3142. // Always trigger presence to update bindings
  3143. $(document).trigger('presence.muc', [from, member, pres]);
  3144. this.parsePresence(from, member, pres);
  3145. // Trigger status message update
  3146. if (member.status) {
  3147. UI.onMucPresenceStatus(from, member);
  3148. }
  3149. return true;
  3150. },
  3151. onPresenceUnavailable: function (pres) {
  3152. var from = pres.getAttribute('from');
  3153. // Status code 110 indicates that this notification is "self-presence".
  3154. if (!$(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="110"]').length) {
  3155. delete this.members[from];
  3156. this.list_members.splice(this.list_members.indexOf(from), 1);
  3157. this.onParticipantLeft(from);
  3158. }
  3159. // If the status code is 110 this means we're leaving and we would like
  3160. // to remove everyone else from our view, so we trigger the event.
  3161. else if (this.list_members.length > 1) {
  3162. for (var i = 0; i < this.list_members.length; i++) {
  3163. var member = this.list_members[i];
  3164. delete this.members[i];
  3165. this.list_members.splice(i, 1);
  3166. this.onParticipantLeft(member);
  3167. }
  3168. }
  3169. if ($(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="307"]').length) {
  3170. $(document).trigger('kicked.muc', [from]);
  3171. if (this.myroomjid === from) {
  3172. XMPP.disposeConference(false);
  3173. eventEmitter.emit(XMPPEvents.KICKED);
  3174. }
  3175. }
  3176. return true;
  3177. },
  3178. onPresenceError: function (pres) {
  3179. var from = pres.getAttribute('from');
  3180. if ($(pres).find('>error[type="auth"]>not-authorized[xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"]').length) {
  3181. console.log('on password required', from);
  3182. var self = this;
  3183. UI.onPasswordReqiured(function (value) {
  3184. self.doJoin(from, value);
  3185. });
  3186. } else if ($(pres).find(
  3187. '>error[type="cancel"]>not-allowed[xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"]').length) {
  3188. var toDomain = Strophe.getDomainFromJid(pres.getAttribute('to'));
  3189. if (toDomain === config.hosts.anonymousdomain) {
  3190. // we are connected with anonymous domain and only non anonymous users can create rooms
  3191. // we must authorize the user
  3192. XMPP.promptLogin();
  3193. } else {
  3194. console.warn('onPresError ', pres);
  3195. UI.messageHandler.openReportDialog(null,
  3196. 'Oops! Something went wrong and we couldn`t connect to the conference.',
  3197. pres);
  3198. }
  3199. } else {
  3200. console.warn('onPresError ', pres);
  3201. UI.messageHandler.openReportDialog(null,
  3202. 'Oops! Something went wrong and we couldn`t connect to the conference.',
  3203. pres);
  3204. }
  3205. return true;
  3206. },
  3207. sendMessage: function (body, nickname) {
  3208. var msg = $msg({to: this.roomjid, type: 'groupchat'});
  3209. msg.c('body', body).up();
  3210. if (nickname) {
  3211. msg.c('nick', {xmlns: 'http://jabber.org/protocol/nick'}).t(nickname).up().up();
  3212. }
  3213. this.connection.send(msg);
  3214. API.triggerEvent("outgoingMessage", {"message": body});
  3215. },
  3216. setSubject: function (subject) {
  3217. var msg = $msg({to: this.roomjid, type: 'groupchat'});
  3218. msg.c('subject', subject);
  3219. this.connection.send(msg);
  3220. console.log("topic changed to " + subject);
  3221. },
  3222. onMessage: function (msg) {
  3223. // FIXME: this is a hack. but jingle on muc makes nickchanges hard
  3224. var from = msg.getAttribute('from');
  3225. var nick = $(msg).find('>nick[xmlns="http://jabber.org/protocol/nick"]').text() || Strophe.getResourceFromJid(from);
  3226. var txt = $(msg).find('>body').text();
  3227. var type = msg.getAttribute("type");
  3228. if (type == "error") {
  3229. UI.chatAddError($(msg).find('>text').text(), txt);
  3230. return true;
  3231. }
  3232. var subject = $(msg).find('>subject');
  3233. if (subject.length) {
  3234. var subjectText = subject.text();
  3235. if (subjectText || subjectText == "") {
  3236. UI.chatSetSubject(subjectText);
  3237. console.log("Subject is changed to " + subjectText);
  3238. }
  3239. }
  3240. if (txt) {
  3241. console.log('chat', nick, txt);
  3242. UI.updateChatConversation(from, nick, txt);
  3243. if (from != this.myroomjid)
  3244. API.triggerEvent("incomingMessage",
  3245. {"from": from, "nick": nick, "message": txt});
  3246. }
  3247. return true;
  3248. },
  3249. lockRoom: function (key, onSuccess, onError, onNotSupported) {
  3250. //http://xmpp.org/extensions/xep-0045.html#roomconfig
  3251. var ob = this;
  3252. this.connection.sendIQ($iq({to: this.roomjid, type: 'get'}).c('query', {xmlns: 'http://jabber.org/protocol/muc#owner'}),
  3253. function (res) {
  3254. if ($(res).find('>query>x[xmlns="jabber:x:data"]>field[var="muc#roomconfig_roomsecret"]').length) {
  3255. var formsubmit = $iq({to: ob.roomjid, type: 'set'}).c('query', {xmlns: 'http://jabber.org/protocol/muc#owner'});
  3256. formsubmit.c('x', {xmlns: 'jabber:x:data', type: 'submit'});
  3257. formsubmit.c('field', {'var': 'FORM_TYPE'}).c('value').t('http://jabber.org/protocol/muc#roomconfig').up().up();
  3258. formsubmit.c('field', {'var': 'muc#roomconfig_roomsecret'}).c('value').t(key).up().up();
  3259. // Fixes a bug in prosody 0.9.+ https://code.google.com/p/lxmppd/issues/detail?id=373
  3260. formsubmit.c('field', {'var': 'muc#roomconfig_whois'}).c('value').t('anyone').up().up();
  3261. // FIXME: is muc#roomconfig_passwordprotectedroom required?
  3262. this.connection.sendIQ(formsubmit,
  3263. onSuccess,
  3264. onError);
  3265. } else {
  3266. onNotSupported();
  3267. }
  3268. }, onError);
  3269. },
  3270. kick: function (jid) {
  3271. var kickIQ = $iq({to: this.roomjid, type: 'set'})
  3272. .c('query', {xmlns: 'http://jabber.org/protocol/muc#admin'})
  3273. .c('item', {nick: Strophe.getResourceFromJid(jid), role: 'none'})
  3274. .c('reason').t('You have been kicked.').up().up().up();
  3275. this.connection.sendIQ(
  3276. kickIQ,
  3277. function (result) {
  3278. console.log('Kick participant with jid: ', jid, result);
  3279. },
  3280. function (error) {
  3281. console.log('Kick participant error: ', error);
  3282. });
  3283. },
  3284. sendPresence: function () {
  3285. var pres = $pres({to: this.presMap['to'] });
  3286. pres.c('x', {xmlns: this.presMap['xns']});
  3287. if (this.presMap['password']) {
  3288. pres.c('password').t(this.presMap['password']).up();
  3289. }
  3290. pres.up();
  3291. // Send XEP-0115 'c' stanza that contains our capabilities info
  3292. if (this.connection.caps) {
  3293. this.connection.caps.node = config.clientNode;
  3294. pres.c('c', this.connection.caps.generateCapsAttrs()).up();
  3295. }
  3296. pres.c('user-agent', {xmlns: 'http://jitsi.org/jitmeet/user-agent'})
  3297. .t(navigator.userAgent).up();
  3298. if (this.presMap['bridgeIsDown']) {
  3299. pres.c('bridgeIsDown').up();
  3300. }
  3301. if (this.presMap['email']) {
  3302. pres.c('email').t(this.presMap['email']).up();
  3303. }
  3304. if (this.presMap['userId']) {
  3305. pres.c('userId').t(this.presMap['userId']).up();
  3306. }
  3307. if (this.presMap['displayName']) {
  3308. // XEP-0172
  3309. pres.c('nick', {xmlns: 'http://jabber.org/protocol/nick'})
  3310. .t(this.presMap['displayName']).up();
  3311. }
  3312. if (this.presMap['audions']) {
  3313. pres.c('audiomuted', {xmlns: this.presMap['audions']})
  3314. .t(this.presMap['audiomuted']).up();
  3315. }
  3316. if (this.presMap['videons']) {
  3317. pres.c('videomuted', {xmlns: this.presMap['videons']})
  3318. .t(this.presMap['videomuted']).up();
  3319. }
  3320. if (this.presMap['statsns']) {
  3321. var stats = pres.c('stats', {xmlns: this.presMap['statsns']});
  3322. for (var stat in this.presMap["stats"])
  3323. if (this.presMap["stats"][stat] != null)
  3324. stats.c("stat", {name: stat, value: this.presMap["stats"][stat]}).up();
  3325. pres.up();
  3326. }
  3327. if (this.presMap['prezins']) {
  3328. pres.c('prezi',
  3329. {xmlns: this.presMap['prezins'],
  3330. 'url': this.presMap['preziurl']})
  3331. .c('current').t(this.presMap['prezicurrent']).up().up();
  3332. }
  3333. if (this.presMap['etherpadns']) {
  3334. pres.c('etherpad', {xmlns: this.presMap['etherpadns']})
  3335. .t(this.presMap['etherpadname']).up();
  3336. }
  3337. if (this.presMap['medians']) {
  3338. pres.c('media', {xmlns: this.presMap['medians']});
  3339. var sourceNumber = 0;
  3340. Object.keys(this.presMap).forEach(function (key) {
  3341. if (key.indexOf('source') >= 0) {
  3342. sourceNumber++;
  3343. }
  3344. });
  3345. if (sourceNumber > 0)
  3346. for (var i = 1; i <= sourceNumber / 3; i++) {
  3347. pres.c('source',
  3348. {type: this.presMap['source' + i + '_type'],
  3349. ssrc: this.presMap['source' + i + '_ssrc'],
  3350. direction: this.presMap['source' + i + '_direction']
  3351. || 'sendrecv' }
  3352. ).up();
  3353. }
  3354. }
  3355. pres.up();
  3356. // console.debug(pres.toString());
  3357. this.connection.send(pres);
  3358. },
  3359. addDisplayNameToPresence: function (displayName) {
  3360. this.presMap['displayName'] = displayName;
  3361. },
  3362. addMediaToPresence: function (sourceNumber, mtype, ssrcs, direction) {
  3363. if (!this.presMap['medians'])
  3364. this.presMap['medians'] = 'http://estos.de/ns/mjs';
  3365. this.presMap['source' + sourceNumber + '_type'] = mtype;
  3366. this.presMap['source' + sourceNumber + '_ssrc'] = ssrcs;
  3367. this.presMap['source' + sourceNumber + '_direction'] = direction;
  3368. },
  3369. clearPresenceMedia: function () {
  3370. var self = this;
  3371. Object.keys(this.presMap).forEach(function (key) {
  3372. if (key.indexOf('source') != -1) {
  3373. delete self.presMap[key];
  3374. }
  3375. });
  3376. },
  3377. addPreziToPresence: function (url, currentSlide) {
  3378. this.presMap['prezins'] = 'http://jitsi.org/jitmeet/prezi';
  3379. this.presMap['preziurl'] = url;
  3380. this.presMap['prezicurrent'] = currentSlide;
  3381. },
  3382. removePreziFromPresence: function () {
  3383. delete this.presMap['prezins'];
  3384. delete this.presMap['preziurl'];
  3385. delete this.presMap['prezicurrent'];
  3386. },
  3387. addCurrentSlideToPresence: function (currentSlide) {
  3388. this.presMap['prezicurrent'] = currentSlide;
  3389. },
  3390. getPrezi: function (roomjid) {
  3391. return this.preziMap[roomjid];
  3392. },
  3393. addEtherpadToPresence: function (etherpadName) {
  3394. this.presMap['etherpadns'] = 'http://jitsi.org/jitmeet/etherpad';
  3395. this.presMap['etherpadname'] = etherpadName;
  3396. },
  3397. addAudioInfoToPresence: function (isMuted) {
  3398. this.presMap['audions'] = 'http://jitsi.org/jitmeet/audio';
  3399. this.presMap['audiomuted'] = isMuted.toString();
  3400. },
  3401. addVideoInfoToPresence: function (isMuted) {
  3402. this.presMap['videons'] = 'http://jitsi.org/jitmeet/video';
  3403. this.presMap['videomuted'] = isMuted.toString();
  3404. },
  3405. addConnectionInfoToPresence: function (stats) {
  3406. this.presMap['statsns'] = 'http://jitsi.org/jitmeet/stats';
  3407. this.presMap['stats'] = stats;
  3408. },
  3409. findJidFromResource: function (resourceJid) {
  3410. if (resourceJid &&
  3411. resourceJid === Strophe.getResourceFromJid(this.myroomjid)) {
  3412. return this.myroomjid;
  3413. }
  3414. var peerJid = null;
  3415. Object.keys(this.members).some(function (jid) {
  3416. peerJid = jid;
  3417. return Strophe.getResourceFromJid(jid) === resourceJid;
  3418. });
  3419. return peerJid;
  3420. },
  3421. addBridgeIsDownToPresence: function () {
  3422. this.presMap['bridgeIsDown'] = true;
  3423. },
  3424. addEmailToPresence: function (email) {
  3425. this.presMap['email'] = email;
  3426. },
  3427. addUserIdToPresence: function (userId) {
  3428. this.presMap['userId'] = userId;
  3429. },
  3430. isModerator: function () {
  3431. return this.role === 'moderator';
  3432. },
  3433. getMemberRole: function (peerJid) {
  3434. if (this.members[peerJid]) {
  3435. return this.members[peerJid].role;
  3436. }
  3437. return null;
  3438. },
  3439. onParticipantLeft: function (jid) {
  3440. UI.onMucLeft(jid);
  3441. API.triggerEvent("participantLeft", {jid: jid});
  3442. delete jid2Ssrc[jid];
  3443. this.connection.jingle.terminateByJid(jid);
  3444. if (this.getPrezi(jid)) {
  3445. $(document).trigger('presentationremoved.muc',
  3446. [jid, this.getPrezi(jid)]);
  3447. }
  3448. Moderator.onMucLeft(jid);
  3449. },
  3450. parsePresence: function (from, memeber, pres) {
  3451. if($(pres).find(">bridgeIsDown").length > 0 && !bridgeIsDown) {
  3452. bridgeIsDown = true;
  3453. eventEmitter.emit(XMPPEvents.BRIDGE_DOWN);
  3454. }
  3455. if(memeber.isFocus)
  3456. return;
  3457. // Remove old ssrcs coming from the jid
  3458. Object.keys(ssrc2jid).forEach(function (ssrc) {
  3459. if (ssrc2jid[ssrc] == jid) {
  3460. delete ssrc2jid[ssrc];
  3461. delete ssrc2videoType[ssrc];
  3462. }
  3463. });
  3464. var changedStreams = [];
  3465. $(pres).find('>media[xmlns="http://estos.de/ns/mjs"]>source').each(function (idx, ssrc) {
  3466. //console.log(jid, 'assoc ssrc', ssrc.getAttribute('type'), ssrc.getAttribute('ssrc'));
  3467. var ssrcV = ssrc.getAttribute('ssrc');
  3468. ssrc2jid[ssrcV] = from;
  3469. notReceivedSSRCs.push(ssrcV);
  3470. var type = ssrc.getAttribute('type');
  3471. ssrc2videoType[ssrcV] = type;
  3472. var direction = ssrc.getAttribute('direction');
  3473. changedStreams.push({type: type, direction: direction});
  3474. });
  3475. eventEmitter.emit(XMPPEvents.CHANGED_STREAMS, from, changedStreams);
  3476. var displayName = !config.displayJids
  3477. ? memeber.displayName : Strophe.getResourceFromJid(from);
  3478. if (displayName && displayName.length > 0)
  3479. {
  3480. // $(document).trigger('displaynamechanged',
  3481. // [jid, displayName]);
  3482. eventEmitter.emit(XMPPEvents.DISPLAY_NAME_CHANGED, from, displayName);
  3483. }
  3484. var id = $(pres).find('>userID').text();
  3485. var email = $(pres).find('>email');
  3486. if(email.length > 0) {
  3487. id = email.text();
  3488. }
  3489. eventEmitter.emit(XMPPEvents.USER_ID_CHANGED, from, id);
  3490. }
  3491. });
  3492. };
  3493. },{"./moderator":6}],9:[function(require,module,exports){
  3494. /* jshint -W117 */
  3495. var JingleSession = require("./JingleSession");
  3496. function CallIncomingJingle(sid, connection) {
  3497. var sess = connection.jingle.sessions[sid];
  3498. // TODO: do we check activecall == null?
  3499. activecall = sess;
  3500. statistics.onConferenceCreated(sess);
  3501. RTC.onConferenceCreated(sess);
  3502. // TODO: check affiliation and/or role
  3503. console.log('emuc data for', sess.peerjid, connection.emuc.members[sess.peerjid]);
  3504. sess.usedrip = true; // not-so-naive trickle ice
  3505. sess.sendAnswer();
  3506. sess.accept();
  3507. };
  3508. module.exports = function(XMPP)
  3509. {
  3510. Strophe.addConnectionPlugin('jingle', {
  3511. connection: null,
  3512. sessions: {},
  3513. jid2session: {},
  3514. ice_config: {iceServers: []},
  3515. pc_constraints: {},
  3516. media_constraints: {
  3517. mandatory: {
  3518. 'OfferToReceiveAudio': true,
  3519. 'OfferToReceiveVideo': true
  3520. }
  3521. // MozDontOfferDataChannel: true when this is firefox
  3522. },
  3523. init: function (conn) {
  3524. this.connection = conn;
  3525. if (this.connection.disco) {
  3526. // http://xmpp.org/extensions/xep-0167.html#support
  3527. // http://xmpp.org/extensions/xep-0176.html#support
  3528. this.connection.disco.addFeature('urn:xmpp:jingle:1');
  3529. this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:1');
  3530. this.connection.disco.addFeature('urn:xmpp:jingle:transports:ice-udp:1');
  3531. this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:audio');
  3532. this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:video');
  3533. // this is dealt with by SDP O/A so we don't need to annouce this
  3534. //this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:rtcp-fb:0'); // XEP-0293
  3535. //this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:rtp-hdrext:0'); // XEP-0294
  3536. if (config.useRtcpMux) {
  3537. this.connection.disco.addFeature('urn:ietf:rfc:5761'); // rtcp-mux
  3538. }
  3539. if (config.useBundle) {
  3540. this.connection.disco.addFeature('urn:ietf:rfc:5888'); // a=group, e.g. bundle
  3541. }
  3542. //this.connection.disco.addFeature('urn:ietf:rfc:5576'); // a=ssrc
  3543. }
  3544. this.connection.addHandler(this.onJingle.bind(this), 'urn:xmpp:jingle:1', 'iq', 'set', null, null);
  3545. },
  3546. onJingle: function (iq) {
  3547. var sid = $(iq).find('jingle').attr('sid');
  3548. var action = $(iq).find('jingle').attr('action');
  3549. var fromJid = iq.getAttribute('from');
  3550. // send ack first
  3551. var ack = $iq({type: 'result',
  3552. to: fromJid,
  3553. id: iq.getAttribute('id')
  3554. });
  3555. console.log('on jingle ' + action + ' from ' + fromJid, iq);
  3556. var sess = this.sessions[sid];
  3557. if ('session-initiate' != action) {
  3558. if (sess === null) {
  3559. ack.type = 'error';
  3560. ack.c('error', {type: 'cancel'})
  3561. .c('item-not-found', {xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'}).up()
  3562. .c('unknown-session', {xmlns: 'urn:xmpp:jingle:errors:1'});
  3563. this.connection.send(ack);
  3564. return true;
  3565. }
  3566. // compare from to sess.peerjid (bare jid comparison for later compat with message-mode)
  3567. // local jid is not checked
  3568. if (Strophe.getBareJidFromJid(fromJid) != Strophe.getBareJidFromJid(sess.peerjid)) {
  3569. console.warn('jid mismatch for session id', sid, fromJid, sess.peerjid);
  3570. ack.type = 'error';
  3571. ack.c('error', {type: 'cancel'})
  3572. .c('item-not-found', {xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'}).up()
  3573. .c('unknown-session', {xmlns: 'urn:xmpp:jingle:errors:1'});
  3574. this.connection.send(ack);
  3575. return true;
  3576. }
  3577. } else if (sess !== undefined) {
  3578. // existing session with same session id
  3579. // this might be out-of-order if the sess.peerjid is the same as from
  3580. ack.type = 'error';
  3581. ack.c('error', {type: 'cancel'})
  3582. .c('service-unavailable', {xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'}).up();
  3583. console.warn('duplicate session id', sid);
  3584. this.connection.send(ack);
  3585. return true;
  3586. }
  3587. // FIXME: check for a defined action
  3588. this.connection.send(ack);
  3589. // see http://xmpp.org/extensions/xep-0166.html#concepts-session
  3590. switch (action) {
  3591. case 'session-initiate':
  3592. sess = new JingleSession(
  3593. $(iq).attr('to'), $(iq).find('jingle').attr('sid'),
  3594. this.connection, XMPP);
  3595. // configure session
  3596. sess.media_constraints = this.media_constraints;
  3597. sess.pc_constraints = this.pc_constraints;
  3598. sess.ice_config = this.ice_config;
  3599. sess.initiate(fromJid, false);
  3600. // FIXME: setRemoteDescription should only be done when this call is to be accepted
  3601. sess.setRemoteDescription($(iq).find('>jingle'), 'offer');
  3602. this.sessions[sess.sid] = sess;
  3603. this.jid2session[sess.peerjid] = sess;
  3604. // the callback should either
  3605. // .sendAnswer and .accept
  3606. // or .sendTerminate -- not necessarily synchronus
  3607. CallIncomingJingle(sess.sid, this.connection);
  3608. break;
  3609. case 'session-accept':
  3610. sess.setRemoteDescription($(iq).find('>jingle'), 'answer');
  3611. sess.accept();
  3612. $(document).trigger('callaccepted.jingle', [sess.sid]);
  3613. break;
  3614. case 'session-terminate':
  3615. // If this is not the focus sending the terminate, we have
  3616. // nothing more to do here.
  3617. if (Object.keys(this.sessions).length < 1
  3618. || !(this.sessions[Object.keys(this.sessions)[0]]
  3619. instanceof JingleSession))
  3620. {
  3621. break;
  3622. }
  3623. console.log('terminating...', sess.sid);
  3624. sess.terminate();
  3625. this.terminate(sess.sid);
  3626. if ($(iq).find('>jingle>reason').length) {
  3627. $(document).trigger('callterminated.jingle', [
  3628. sess.sid,
  3629. sess.peerjid,
  3630. $(iq).find('>jingle>reason>:first')[0].tagName,
  3631. $(iq).find('>jingle>reason>text').text()
  3632. ]);
  3633. } else {
  3634. $(document).trigger('callterminated.jingle',
  3635. [sess.sid, sess.peerjid]);
  3636. }
  3637. break;
  3638. case 'transport-info':
  3639. sess.addIceCandidate($(iq).find('>jingle>content'));
  3640. break;
  3641. case 'session-info':
  3642. var affected;
  3643. if ($(iq).find('>jingle>ringing[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').length) {
  3644. $(document).trigger('ringing.jingle', [sess.sid]);
  3645. } else if ($(iq).find('>jingle>mute[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').length) {
  3646. affected = $(iq).find('>jingle>mute[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').attr('name');
  3647. $(document).trigger('mute.jingle', [sess.sid, affected]);
  3648. } else if ($(iq).find('>jingle>unmute[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').length) {
  3649. affected = $(iq).find('>jingle>unmute[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').attr('name');
  3650. $(document).trigger('unmute.jingle', [sess.sid, affected]);
  3651. }
  3652. break;
  3653. case 'addsource': // FIXME: proprietary, un-jingleish
  3654. case 'source-add': // FIXME: proprietary
  3655. sess.addSource($(iq).find('>jingle>content'), fromJid);
  3656. break;
  3657. case 'removesource': // FIXME: proprietary, un-jingleish
  3658. case 'source-remove': // FIXME: proprietary
  3659. sess.removeSource($(iq).find('>jingle>content'), fromJid);
  3660. break;
  3661. default:
  3662. console.warn('jingle action not implemented', action);
  3663. break;
  3664. }
  3665. return true;
  3666. },
  3667. initiate: function (peerjid, myjid) { // initiate a new jinglesession to peerjid
  3668. var sess = new JingleSession(myjid || this.connection.jid,
  3669. Math.random().toString(36).substr(2, 12), // random string
  3670. this.connection, XMPP);
  3671. // configure session
  3672. sess.media_constraints = this.media_constraints;
  3673. sess.pc_constraints = this.pc_constraints;
  3674. sess.ice_config = this.ice_config;
  3675. sess.initiate(peerjid, true);
  3676. this.sessions[sess.sid] = sess;
  3677. this.jid2session[sess.peerjid] = sess;
  3678. sess.sendOffer();
  3679. return sess;
  3680. },
  3681. terminate: function (sid, reason, text) { // terminate by sessionid (or all sessions)
  3682. if (sid === null || sid === undefined) {
  3683. for (sid in this.sessions) {
  3684. if (this.sessions[sid].state != 'ended') {
  3685. this.sessions[sid].sendTerminate(reason || (!this.sessions[sid].active()) ? 'cancel' : null, text);
  3686. this.sessions[sid].terminate();
  3687. }
  3688. delete this.jid2session[this.sessions[sid].peerjid];
  3689. delete this.sessions[sid];
  3690. }
  3691. } else if (this.sessions.hasOwnProperty(sid)) {
  3692. if (this.sessions[sid].state != 'ended') {
  3693. this.sessions[sid].sendTerminate(reason || (!this.sessions[sid].active()) ? 'cancel' : null, text);
  3694. this.sessions[sid].terminate();
  3695. }
  3696. delete this.jid2session[this.sessions[sid].peerjid];
  3697. delete this.sessions[sid];
  3698. }
  3699. },
  3700. // Used to terminate a session when an unavailable presence is received.
  3701. terminateByJid: function (jid) {
  3702. if (this.jid2session.hasOwnProperty(jid)) {
  3703. var sess = this.jid2session[jid];
  3704. if (sess) {
  3705. sess.terminate();
  3706. console.log('peer went away silently', jid);
  3707. delete this.sessions[sess.sid];
  3708. delete this.jid2session[jid];
  3709. $(document).trigger('callterminated.jingle',
  3710. [sess.sid, jid], 'gone');
  3711. }
  3712. }
  3713. },
  3714. terminateRemoteByJid: function (jid, reason) {
  3715. if (this.jid2session.hasOwnProperty(jid)) {
  3716. var sess = this.jid2session[jid];
  3717. if (sess) {
  3718. sess.sendTerminate(reason || (!sess.active()) ? 'kick' : null);
  3719. sess.terminate();
  3720. console.log('terminate peer with jid', sess.sid, jid);
  3721. delete this.sessions[sess.sid];
  3722. delete this.jid2session[jid];
  3723. $(document).trigger('callterminated.jingle',
  3724. [sess.sid, jid, 'kicked']);
  3725. }
  3726. }
  3727. },
  3728. getStunAndTurnCredentials: function () {
  3729. // get stun and turn configuration from server via xep-0215
  3730. // uses time-limited credentials as described in
  3731. // http://tools.ietf.org/html/draft-uberti-behave-turn-rest-00
  3732. //
  3733. // see https://code.google.com/p/prosody-modules/source/browse/mod_turncredentials/mod_turncredentials.lua
  3734. // for a prosody module which implements this
  3735. //
  3736. // currently, this doesn't work with updateIce and therefore credentials with a long
  3737. // validity have to be fetched before creating the peerconnection
  3738. // TODO: implement refresh via updateIce as described in
  3739. // https://code.google.com/p/webrtc/issues/detail?id=1650
  3740. var self = this;
  3741. this.connection.sendIQ(
  3742. $iq({type: 'get', to: this.connection.domain})
  3743. .c('services', {xmlns: 'urn:xmpp:extdisco:1'}).c('service', {host: 'turn.' + this.connection.domain}),
  3744. function (res) {
  3745. var iceservers = [];
  3746. $(res).find('>services>service').each(function (idx, el) {
  3747. el = $(el);
  3748. var dict = {};
  3749. var type = el.attr('type');
  3750. switch (type) {
  3751. case 'stun':
  3752. dict.url = 'stun:' + el.attr('host');
  3753. if (el.attr('port')) {
  3754. dict.url += ':' + el.attr('port');
  3755. }
  3756. iceservers.push(dict);
  3757. break;
  3758. case 'turn':
  3759. case 'turns':
  3760. dict.url = type + ':';
  3761. if (el.attr('username')) { // https://code.google.com/p/webrtc/issues/detail?id=1508
  3762. if (navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./) && parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2], 10) < 28) {
  3763. dict.url += el.attr('username') + '@';
  3764. } else {
  3765. dict.username = el.attr('username'); // only works in M28
  3766. }
  3767. }
  3768. dict.url += el.attr('host');
  3769. if (el.attr('port') && el.attr('port') != '3478') {
  3770. dict.url += ':' + el.attr('port');
  3771. }
  3772. if (el.attr('transport') && el.attr('transport') != 'udp') {
  3773. dict.url += '?transport=' + el.attr('transport');
  3774. }
  3775. if (el.attr('password')) {
  3776. dict.credential = el.attr('password');
  3777. }
  3778. iceservers.push(dict);
  3779. break;
  3780. }
  3781. });
  3782. self.ice_config.iceServers = iceservers;
  3783. },
  3784. function (err) {
  3785. console.warn('getting turn credentials failed', err);
  3786. console.warn('is mod_turncredentials or similar installed?');
  3787. }
  3788. );
  3789. // implement push?
  3790. },
  3791. /**
  3792. * Populates the log data
  3793. */
  3794. populateData: function () {
  3795. var data = {};
  3796. Object.keys(this.sessions).forEach(function (sid) {
  3797. var session = this.sessions[sid];
  3798. if (session.peerconnection && session.peerconnection.updateLog) {
  3799. // FIXME: should probably be a .dump call
  3800. data["jingle_" + session.sid] = {
  3801. updateLog: session.peerconnection.updateLog,
  3802. stats: session.peerconnection.stats,
  3803. url: window.location.href
  3804. };
  3805. }
  3806. });
  3807. return data;
  3808. }
  3809. });
  3810. };
  3811. },{"./JingleSession":1}],10:[function(require,module,exports){
  3812. /* global Strophe */
  3813. module.exports = function () {
  3814. Strophe.addConnectionPlugin('logger', {
  3815. // logs raw stanzas and makes them available for download as JSON
  3816. connection: null,
  3817. log: [],
  3818. init: function (conn) {
  3819. this.connection = conn;
  3820. this.connection.rawInput = this.log_incoming.bind(this);
  3821. this.connection.rawOutput = this.log_outgoing.bind(this);
  3822. },
  3823. log_incoming: function (stanza) {
  3824. this.log.push([new Date().getTime(), 'incoming', stanza]);
  3825. },
  3826. log_outgoing: function (stanza) {
  3827. this.log.push([new Date().getTime(), 'outgoing', stanza]);
  3828. }
  3829. });
  3830. };
  3831. },{}],11:[function(require,module,exports){
  3832. /* global $, $iq, config, connection, focusMucJid, forceMuted,
  3833. setAudioMuted, Strophe */
  3834. /**
  3835. * Moderate connection plugin.
  3836. */
  3837. module.exports = function (XMPP) {
  3838. Strophe.addConnectionPlugin('moderate', {
  3839. connection: null,
  3840. init: function (conn) {
  3841. this.connection = conn;
  3842. this.connection.addHandler(this.onMute.bind(this),
  3843. 'http://jitsi.org/jitmeet/audio',
  3844. 'iq',
  3845. 'set',
  3846. null,
  3847. null);
  3848. },
  3849. setMute: function (jid, mute) {
  3850. console.info("set mute", mute);
  3851. var iqToFocus = $iq({to: focusMucJid, type: 'set'})
  3852. .c('mute', {
  3853. xmlns: 'http://jitsi.org/jitmeet/audio',
  3854. jid: jid
  3855. })
  3856. .t(mute.toString())
  3857. .up();
  3858. this.connection.sendIQ(
  3859. iqToFocus,
  3860. function (result) {
  3861. console.log('set mute', result);
  3862. },
  3863. function (error) {
  3864. console.log('set mute error', error);
  3865. });
  3866. },
  3867. onMute: function (iq) {
  3868. var from = iq.getAttribute('from');
  3869. if (from !== focusMucJid) {
  3870. console.warn("Ignored mute from non focus peer");
  3871. return false;
  3872. }
  3873. var mute = $(iq).find('mute');
  3874. if (mute.length) {
  3875. var doMuteAudio = mute.text() === "true";
  3876. UI.setAudioMuted(doMuteAudio);
  3877. XMPP.forceMuted = doMuteAudio;
  3878. }
  3879. return true;
  3880. },
  3881. eject: function (jid) {
  3882. // We're not the focus, so can't terminate
  3883. //connection.jingle.terminateRemoteByJid(jid, 'kick');
  3884. this.connection.emuc.kick(jid);
  3885. }
  3886. });
  3887. }
  3888. },{}],12:[function(require,module,exports){
  3889. /* jshint -W117 */
  3890. module.exports = function() {
  3891. Strophe.addConnectionPlugin('rayo',
  3892. {
  3893. RAYO_XMLNS: 'urn:xmpp:rayo:1',
  3894. connection: null,
  3895. init: function (conn) {
  3896. this.connection = conn;
  3897. if (this.connection.disco) {
  3898. this.connection.disco.addFeature('urn:xmpp:rayo:client:1');
  3899. }
  3900. this.connection.addHandler(
  3901. this.onRayo.bind(this), this.RAYO_XMLNS, 'iq', 'set', null, null);
  3902. },
  3903. onRayo: function (iq) {
  3904. console.info("Rayo IQ", iq);
  3905. },
  3906. dial: function (to, from, roomName, roomPass) {
  3907. var self = this;
  3908. var req = $iq(
  3909. {
  3910. type: 'set',
  3911. to: focusMucJid
  3912. }
  3913. );
  3914. req.c('dial',
  3915. {
  3916. xmlns: this.RAYO_XMLNS,
  3917. to: to,
  3918. from: from
  3919. });
  3920. req.c('header',
  3921. {
  3922. name: 'JvbRoomName',
  3923. value: roomName
  3924. }).up();
  3925. if (roomPass !== null && roomPass.length) {
  3926. req.c('header',
  3927. {
  3928. name: 'JvbRoomPassword',
  3929. value: roomPass
  3930. }).up();
  3931. }
  3932. this.connection.sendIQ(
  3933. req,
  3934. function (result) {
  3935. console.info('Dial result ', result);
  3936. var resource = $(result).find('ref').attr('uri');
  3937. this.call_resource = resource.substr('xmpp:'.length);
  3938. console.info(
  3939. "Received call resource: " + this.call_resource);
  3940. },
  3941. function (error) {
  3942. console.info('Dial error ', error);
  3943. }
  3944. );
  3945. },
  3946. hang_up: function () {
  3947. if (!this.call_resource) {
  3948. console.warn("No call in progress");
  3949. return;
  3950. }
  3951. var self = this;
  3952. var req = $iq(
  3953. {
  3954. type: 'set',
  3955. to: this.call_resource
  3956. }
  3957. );
  3958. req.c('hangup',
  3959. {
  3960. xmlns: this.RAYO_XMLNS
  3961. });
  3962. this.connection.sendIQ(
  3963. req,
  3964. function (result) {
  3965. console.info('Hangup result ', result);
  3966. self.call_resource = null;
  3967. },
  3968. function (error) {
  3969. console.info('Hangup error ', error);
  3970. self.call_resource = null;
  3971. }
  3972. );
  3973. }
  3974. }
  3975. );
  3976. };
  3977. },{}],13:[function(require,module,exports){
  3978. /**
  3979. * Strophe logger implementation. Logs from level WARN and above.
  3980. */
  3981. module.exports = function () {
  3982. Strophe.log = function (level, msg) {
  3983. switch (level) {
  3984. case Strophe.LogLevel.WARN:
  3985. console.warn("Strophe: " + msg);
  3986. break;
  3987. case Strophe.LogLevel.ERROR:
  3988. case Strophe.LogLevel.FATAL:
  3989. console.error("Strophe: " + msg);
  3990. break;
  3991. }
  3992. };
  3993. Strophe.getStatusString = function (status) {
  3994. switch (status) {
  3995. case Strophe.Status.ERROR:
  3996. return "ERROR";
  3997. case Strophe.Status.CONNECTING:
  3998. return "CONNECTING";
  3999. case Strophe.Status.CONNFAIL:
  4000. return "CONNFAIL";
  4001. case Strophe.Status.AUTHENTICATING:
  4002. return "AUTHENTICATING";
  4003. case Strophe.Status.AUTHFAIL:
  4004. return "AUTHFAIL";
  4005. case Strophe.Status.CONNECTED:
  4006. return "CONNECTED";
  4007. case Strophe.Status.DISCONNECTED:
  4008. return "DISCONNECTED";
  4009. case Strophe.Status.DISCONNECTING:
  4010. return "DISCONNECTING";
  4011. case Strophe.Status.ATTACHED:
  4012. return "ATTACHED";
  4013. default:
  4014. return "unknown";
  4015. }
  4016. };
  4017. };
  4018. },{}],14:[function(require,module,exports){
  4019. var Moderator = require("./moderator");
  4020. var EventEmitter = require("events");
  4021. var Recording = require("./recording");
  4022. var SDP = require("./SDP");
  4023. var eventEmitter = new EventEmitter();
  4024. var connection = null;
  4025. var authenticatedUser = false;
  4026. var activecall = null;
  4027. function connect(jid, password, uiCredentials) {
  4028. var bosh
  4029. = uiCredentials.bosh || config.bosh || '/http-bind';
  4030. connection = new Strophe.Connection(bosh);
  4031. Moderator.setConnection(connection);
  4032. var settings = UI.getSettings();
  4033. var email = settings.email;
  4034. var displayName = settings.displayName;
  4035. if(email) {
  4036. connection.emuc.addEmailToPresence(email);
  4037. } else {
  4038. connection.emuc.addUserIdToPresence(settings.uid);
  4039. }
  4040. if(displayName) {
  4041. connection.emuc.addDisplayNameToPresence(displayName);
  4042. }
  4043. if (connection.disco) {
  4044. // for chrome, add multistream cap
  4045. }
  4046. connection.jingle.pc_constraints = RTC.getPCConstraints();
  4047. if (config.useIPv6) {
  4048. // https://code.google.com/p/webrtc/issues/detail?id=2828
  4049. if (!connection.jingle.pc_constraints.optional)
  4050. connection.jingle.pc_constraints.optional = [];
  4051. connection.jingle.pc_constraints.optional.push({googIPv6: true});
  4052. }
  4053. if(!password)
  4054. password = uiCredentials.password;
  4055. var anonymousConnectionFailed = false;
  4056. connection.connect(jid, password, function (status, msg) {
  4057. console.log('Strophe status changed to',
  4058. Strophe.getStatusString(status));
  4059. if (status === Strophe.Status.CONNECTED) {
  4060. if (config.useStunTurn) {
  4061. connection.jingle.getStunAndTurnCredentials();
  4062. }
  4063. UI.disableConnect();
  4064. console.info("My Jabber ID: " + connection.jid);
  4065. if(password)
  4066. authenticatedUser = true;
  4067. maybeDoJoin();
  4068. } else if (status === Strophe.Status.CONNFAIL) {
  4069. if(msg === 'x-strophe-bad-non-anon-jid') {
  4070. anonymousConnectionFailed = true;
  4071. }
  4072. } else if (status === Strophe.Status.DISCONNECTED) {
  4073. if(anonymousConnectionFailed) {
  4074. // prompt user for username and password
  4075. XMPP.promptLogin();
  4076. }
  4077. } else if (status === Strophe.Status.AUTHFAIL) {
  4078. // wrong password or username, prompt user
  4079. XMPP.promptLogin();
  4080. }
  4081. });
  4082. }
  4083. function maybeDoJoin() {
  4084. if (connection && connection.connected &&
  4085. Strophe.getResourceFromJid(connection.jid)
  4086. && (RTC.localAudio || RTC.localVideo)) {
  4087. // .connected is true while connecting?
  4088. doJoin();
  4089. }
  4090. }
  4091. function doJoin() {
  4092. var roomName = UI.generateRoomName();
  4093. Moderator.allocateConferenceFocus(
  4094. roomName, UI.checkForNicknameAndJoin);
  4095. }
  4096. function initStrophePlugins()
  4097. {
  4098. require("./strophe.emuc")(XMPP, eventEmitter);
  4099. require("./strophe.jingle")();
  4100. require("./strophe.moderate")(XMPP);
  4101. require("./strophe.util")();
  4102. require("./strophe.rayo")();
  4103. require("./strophe.logger")();
  4104. }
  4105. function registerListeners() {
  4106. RTC.addStreamListener(maybeDoJoin,
  4107. StreamEventTypes.EVENT_TYPE_LOCAL_CREATED);
  4108. }
  4109. function setupEvents() {
  4110. $(window).bind('beforeunload', function () {
  4111. if (connection && connection.connected) {
  4112. // ensure signout
  4113. $.ajax({
  4114. type: 'POST',
  4115. url: config.bosh,
  4116. async: false,
  4117. cache: false,
  4118. contentType: 'application/xml',
  4119. data: "<body rid='" + (connection.rid || connection._proto.rid)
  4120. + "' xmlns='http://jabber.org/protocol/httpbind' sid='"
  4121. + (connection.sid || connection._proto.sid)
  4122. + "' type='terminate'>" +
  4123. "<presence xmlns='jabber:client' type='unavailable'/>" +
  4124. "</body>",
  4125. success: function (data) {
  4126. console.log('signed out');
  4127. console.log(data);
  4128. },
  4129. error: function (XMLHttpRequest, textStatus, errorThrown) {
  4130. console.log('signout error',
  4131. textStatus + ' (' + errorThrown + ')');
  4132. }
  4133. });
  4134. }
  4135. XMPP.disposeConference(true);
  4136. });
  4137. }
  4138. var XMPP = {
  4139. sessionTerminated: false,
  4140. /**
  4141. * Remembers if we were muted by the focus.
  4142. * @type {boolean}
  4143. */
  4144. forceMuted: false,
  4145. start: function (uiCredentials) {
  4146. setupEvents();
  4147. initStrophePlugins();
  4148. registerListeners();
  4149. Moderator.init();
  4150. var jid = uiCredentials.jid ||
  4151. config.hosts.anonymousdomain ||
  4152. config.hosts.domain ||
  4153. window.location.hostname;
  4154. connect(jid, null, uiCredentials);
  4155. },
  4156. promptLogin: function () {
  4157. UI.showLoginPopup(connect);
  4158. },
  4159. joinRooom: function(roomName, useNicks, nick)
  4160. {
  4161. var roomjid;
  4162. roomjid = roomName;
  4163. if (useNicks) {
  4164. if (nick) {
  4165. roomjid += '/' + nick;
  4166. } else {
  4167. roomjid += '/' + Strophe.getNodeFromJid(connection.jid);
  4168. }
  4169. } else {
  4170. var tmpJid = Strophe.getNodeFromJid(connection.jid);
  4171. if(!authenticatedUser)
  4172. tmpJid = tmpJid.substr(0, 8);
  4173. roomjid += '/' + tmpJid;
  4174. }
  4175. connection.emuc.doJoin(roomjid);
  4176. },
  4177. myJid: function () {
  4178. if(!connection)
  4179. return null;
  4180. return connection.emuc.myroomjid;
  4181. },
  4182. myResource: function () {
  4183. if(!connection || ! connection.emuc.myroomjid)
  4184. return null;
  4185. return Strophe.getResourceFromJid(connection.emuc.myroomjid);
  4186. },
  4187. disposeConference: function (onUnload) {
  4188. eventEmitter.emit(XMPPEvents.DISPOSE_CONFERENCE, onUnload);
  4189. var handler = activecall;
  4190. if (handler && handler.peerconnection) {
  4191. // FIXME: probably removing streams is not required and close() should
  4192. // be enough
  4193. if (RTC.localAudio) {
  4194. handler.peerconnection.removeStream(RTC.localAudio.getOriginalStream(), onUnload);
  4195. }
  4196. if (RTC.localVideo) {
  4197. handler.peerconnection.removeStream(RTC.localVideo.getOriginalStream(), onUnload);
  4198. }
  4199. handler.peerconnection.close();
  4200. }
  4201. activecall = null;
  4202. if(!onUnload)
  4203. {
  4204. this.sessionTerminated = true;
  4205. connection.emuc.doLeave();
  4206. }
  4207. },
  4208. addListener: function(type, listener)
  4209. {
  4210. eventEmitter.on(type, listener);
  4211. },
  4212. removeListener: function (type, listener) {
  4213. eventEmitter.removeListener(type, listener);
  4214. },
  4215. allocateConferenceFocus: function(roomName, callback) {
  4216. Moderator.allocateConferenceFocus(roomName, callback);
  4217. },
  4218. isModerator: function () {
  4219. return Moderator.isModerator();
  4220. },
  4221. isSipGatewayEnabled: function () {
  4222. return Moderator.isSipGatewayEnabled();
  4223. },
  4224. isExternalAuthEnabled: function () {
  4225. return Moderator.isExternalAuthEnabled();
  4226. },
  4227. switchStreams: function (stream, oldStream, callback) {
  4228. if (activecall) {
  4229. // FIXME: will block switchInProgress on true value in case of exception
  4230. activecall.switchStreams(stream, oldStream, callback);
  4231. } else {
  4232. // We are done immediately
  4233. console.error("No conference handler");
  4234. UI.messageHandler.showError('Error',
  4235. 'Unable to switch video stream.');
  4236. callback();
  4237. }
  4238. },
  4239. setVideoMute: function (mute, callback, options) {
  4240. if(activecall && connection && RTC.localVideo)
  4241. {
  4242. activecall.setVideoMute(mute, callback, options);
  4243. }
  4244. },
  4245. setAudioMute: function (mute, callback) {
  4246. if (!(connection && RTC.localAudio)) {
  4247. return false;
  4248. }
  4249. if (this.forceMuted && !mute) {
  4250. console.info("Asking focus for unmute");
  4251. connection.moderate.setMute(connection.emuc.myroomjid, mute);
  4252. // FIXME: wait for result before resetting muted status
  4253. this.forceMuted = false;
  4254. }
  4255. if (mute == RTC.localAudio.isMuted()) {
  4256. // Nothing to do
  4257. return true;
  4258. }
  4259. // It is not clear what is the right way to handle multiple tracks.
  4260. // So at least make sure that they are all muted or all unmuted and
  4261. // that we send presence just once.
  4262. RTC.localAudio.mute();
  4263. // isMuted is the opposite of audioEnabled
  4264. connection.emuc.addAudioInfoToPresence(mute);
  4265. connection.emuc.sendPresence();
  4266. callback();
  4267. return true;
  4268. },
  4269. // Really mute video, i.e. dont even send black frames
  4270. muteVideo: function (pc, unmute) {
  4271. // FIXME: this probably needs another of those lovely state safeguards...
  4272. // which checks for iceconn == connected and sigstate == stable
  4273. pc.setRemoteDescription(pc.remoteDescription,
  4274. function () {
  4275. pc.createAnswer(
  4276. function (answer) {
  4277. var sdp = new SDP(answer.sdp);
  4278. if (sdp.media.length > 1) {
  4279. if (unmute)
  4280. sdp.media[1] = sdp.media[1].replace('a=recvonly', 'a=sendrecv');
  4281. else
  4282. sdp.media[1] = sdp.media[1].replace('a=sendrecv', 'a=recvonly');
  4283. sdp.raw = sdp.session + sdp.media.join('');
  4284. answer.sdp = sdp.raw;
  4285. }
  4286. pc.setLocalDescription(answer,
  4287. function () {
  4288. console.log('mute SLD ok');
  4289. },
  4290. function (error) {
  4291. console.log('mute SLD error');
  4292. UI.messageHandler.showError('Error',
  4293. 'Oops! Something went wrong and we failed to ' +
  4294. 'mute! (SLD Failure)');
  4295. }
  4296. );
  4297. },
  4298. function (error) {
  4299. console.log(error);
  4300. UI.messageHandler.showError();
  4301. }
  4302. );
  4303. },
  4304. function (error) {
  4305. console.log('muteVideo SRD error');
  4306. UI.messageHandler.showError('Error',
  4307. 'Oops! Something went wrong and we failed to stop video!' +
  4308. '(SRD Failure)');
  4309. }
  4310. );
  4311. },
  4312. toggleRecording: function (tokenEmptyCallback,
  4313. startingCallback, startedCallback) {
  4314. Recording.toggleRecording(tokenEmptyCallback,
  4315. startingCallback, startedCallback);
  4316. },
  4317. addToPresence: function (name, value, dontSend) {
  4318. switch (name)
  4319. {
  4320. case "displayName":
  4321. connection.emuc.addDisplayNameToPresence(value);
  4322. break;
  4323. case "etherpad":
  4324. connection.emuc.addEtherpadToPresence(value);
  4325. break;
  4326. case "prezi":
  4327. connection.emuc.addPreziToPresence(value, 0);
  4328. break;
  4329. case "preziSlide":
  4330. connection.emuc.addCurrentSlideToPresence(value);
  4331. break;
  4332. case "connectionQuality":
  4333. connection.emuc.addConnectionInfoToPresence(value);
  4334. break;
  4335. case "email":
  4336. connection.emuc.addEmailToPresence(value);
  4337. default :
  4338. console.log("Unknown tag for presence.");
  4339. return;
  4340. }
  4341. if(!dontSend)
  4342. connection.emuc.sendPresence();
  4343. },
  4344. sendLogs: function (content) {
  4345. // XEP-0337-ish
  4346. var message = $msg({to: focusMucJid, type: 'normal'});
  4347. message.c('log', { xmlns: 'urn:xmpp:eventlog',
  4348. id: 'PeerConnectionStats'});
  4349. message.c('message').t(content).up();
  4350. if (deflate) {
  4351. message.c('tag', {name: "deflated", value: "true"}).up();
  4352. }
  4353. message.up();
  4354. connection.send(message);
  4355. },
  4356. populateData: function () {
  4357. var data = {};
  4358. if (connection.jingle) {
  4359. data = connection.jingle.populateData();
  4360. }
  4361. return data;
  4362. },
  4363. getLogger: function () {
  4364. if(connection.logger)
  4365. return connection.logger.log;
  4366. return null;
  4367. },
  4368. getPrezi: function () {
  4369. return connection.emuc.getPrezi(this.myJid());
  4370. },
  4371. removePreziFromPresence: function () {
  4372. connection.emuc.removePreziFromPresence();
  4373. connection.emuc.sendPresence();
  4374. },
  4375. sendChatMessage: function (message, nickname) {
  4376. connection.emuc.sendMessage(message, nickname);
  4377. },
  4378. setSubject: function (topic) {
  4379. connection.emuc.setSubject(topic);
  4380. },
  4381. lockRoom: function (key, onSuccess, onError, onNotSupported) {
  4382. connection.emuc.lockRoom(key, onSuccess, onError, onNotSupported);
  4383. },
  4384. dial: function (to, from, roomName,roomPass) {
  4385. connection.rayo.dial(to, from, roomName,roomPass);
  4386. },
  4387. setMute: function (jid, mute) {
  4388. connection.moderate.setMute(jid, mute);
  4389. },
  4390. eject: function (jid) {
  4391. connection.moderate.eject(jid);
  4392. },
  4393. findJidFromResource: function (resource) {
  4394. connection.emuc.findJidFromResource(resource);
  4395. },
  4396. getMembers: function () {
  4397. return connection.emuc.members;
  4398. }
  4399. };
  4400. module.exports = XMPP;
  4401. },{"./SDP":2,"./moderator":6,"./recording":7,"./strophe.emuc":8,"./strophe.jingle":9,"./strophe.logger":10,"./strophe.moderate":11,"./strophe.rayo":12,"./strophe.util":13,"events":15}],15:[function(require,module,exports){
  4402. // Copyright Joyent, Inc. and other Node contributors.
  4403. //
  4404. // Permission is hereby granted, free of charge, to any person obtaining a
  4405. // copy of this software and associated documentation files (the
  4406. // "Software"), to deal in the Software without restriction, including
  4407. // without limitation the rights to use, copy, modify, merge, publish,
  4408. // distribute, sublicense, and/or sell copies of the Software, and to permit
  4409. // persons to whom the Software is furnished to do so, subject to the
  4410. // following conditions:
  4411. //
  4412. // The above copyright notice and this permission notice shall be included
  4413. // in all copies or substantial portions of the Software.
  4414. //
  4415. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  4416. // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  4417. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
  4418. // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
  4419. // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  4420. // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  4421. // USE OR OTHER DEALINGS IN THE SOFTWARE.
  4422. function EventEmitter() {
  4423. this._events = this._events || {};
  4424. this._maxListeners = this._maxListeners || undefined;
  4425. }
  4426. module.exports = EventEmitter;
  4427. // Backwards-compat with node 0.10.x
  4428. EventEmitter.EventEmitter = EventEmitter;
  4429. EventEmitter.prototype._events = undefined;
  4430. EventEmitter.prototype._maxListeners = undefined;
  4431. // By default EventEmitters will print a warning if more than 10 listeners are
  4432. // added to it. This is a useful default which helps finding memory leaks.
  4433. EventEmitter.defaultMaxListeners = 10;
  4434. // Obviously not all Emitters should be limited to 10. This function allows
  4435. // that to be increased. Set to zero for unlimited.
  4436. EventEmitter.prototype.setMaxListeners = function(n) {
  4437. if (!isNumber(n) || n < 0 || isNaN(n))
  4438. throw TypeError('n must be a positive number');
  4439. this._maxListeners = n;
  4440. return this;
  4441. };
  4442. EventEmitter.prototype.emit = function(type) {
  4443. var er, handler, len, args, i, listeners;
  4444. if (!this._events)
  4445. this._events = {};
  4446. // If there is no 'error' event listener then throw.
  4447. if (type === 'error') {
  4448. if (!this._events.error ||
  4449. (isObject(this._events.error) && !this._events.error.length)) {
  4450. er = arguments[1];
  4451. if (er instanceof Error) {
  4452. throw er; // Unhandled 'error' event
  4453. } else {
  4454. throw TypeError('Uncaught, unspecified "error" event.');
  4455. }
  4456. return false;
  4457. }
  4458. }
  4459. handler = this._events[type];
  4460. if (isUndefined(handler))
  4461. return false;
  4462. if (isFunction(handler)) {
  4463. switch (arguments.length) {
  4464. // fast cases
  4465. case 1:
  4466. handler.call(this);
  4467. break;
  4468. case 2:
  4469. handler.call(this, arguments[1]);
  4470. break;
  4471. case 3:
  4472. handler.call(this, arguments[1], arguments[2]);
  4473. break;
  4474. // slower
  4475. default:
  4476. len = arguments.length;
  4477. args = new Array(len - 1);
  4478. for (i = 1; i < len; i++)
  4479. args[i - 1] = arguments[i];
  4480. handler.apply(this, args);
  4481. }
  4482. } else if (isObject(handler)) {
  4483. len = arguments.length;
  4484. args = new Array(len - 1);
  4485. for (i = 1; i < len; i++)
  4486. args[i - 1] = arguments[i];
  4487. listeners = handler.slice();
  4488. len = listeners.length;
  4489. for (i = 0; i < len; i++)
  4490. listeners[i].apply(this, args);
  4491. }
  4492. return true;
  4493. };
  4494. EventEmitter.prototype.addListener = function(type, listener) {
  4495. var m;
  4496. if (!isFunction(listener))
  4497. throw TypeError('listener must be a function');
  4498. if (!this._events)
  4499. this._events = {};
  4500. // To avoid recursion in the case that type === "newListener"! Before
  4501. // adding it to the listeners, first emit "newListener".
  4502. if (this._events.newListener)
  4503. this.emit('newListener', type,
  4504. isFunction(listener.listener) ?
  4505. listener.listener : listener);
  4506. if (!this._events[type])
  4507. // Optimize the case of one listener. Don't need the extra array object.
  4508. this._events[type] = listener;
  4509. else if (isObject(this._events[type]))
  4510. // If we've already got an array, just append.
  4511. this._events[type].push(listener);
  4512. else
  4513. // Adding the second element, need to change to array.
  4514. this._events[type] = [this._events[type], listener];
  4515. // Check for listener leak
  4516. if (isObject(this._events[type]) && !this._events[type].warned) {
  4517. var m;
  4518. if (!isUndefined(this._maxListeners)) {
  4519. m = this._maxListeners;
  4520. } else {
  4521. m = EventEmitter.defaultMaxListeners;
  4522. }
  4523. if (m && m > 0 && this._events[type].length > m) {
  4524. this._events[type].warned = true;
  4525. console.error('(node) warning: possible EventEmitter memory ' +
  4526. 'leak detected. %d listeners added. ' +
  4527. 'Use emitter.setMaxListeners() to increase limit.',
  4528. this._events[type].length);
  4529. if (typeof console.trace === 'function') {
  4530. // not supported in IE 10
  4531. console.trace();
  4532. }
  4533. }
  4534. }
  4535. return this;
  4536. };
  4537. EventEmitter.prototype.on = EventEmitter.prototype.addListener;
  4538. EventEmitter.prototype.once = function(type, listener) {
  4539. if (!isFunction(listener))
  4540. throw TypeError('listener must be a function');
  4541. var fired = false;
  4542. function g() {
  4543. this.removeListener(type, g);
  4544. if (!fired) {
  4545. fired = true;
  4546. listener.apply(this, arguments);
  4547. }
  4548. }
  4549. g.listener = listener;
  4550. this.on(type, g);
  4551. return this;
  4552. };
  4553. // emits a 'removeListener' event iff the listener was removed
  4554. EventEmitter.prototype.removeListener = function(type, listener) {
  4555. var list, position, length, i;
  4556. if (!isFunction(listener))
  4557. throw TypeError('listener must be a function');
  4558. if (!this._events || !this._events[type])
  4559. return this;
  4560. list = this._events[type];
  4561. length = list.length;
  4562. position = -1;
  4563. if (list === listener ||
  4564. (isFunction(list.listener) && list.listener === listener)) {
  4565. delete this._events[type];
  4566. if (this._events.removeListener)
  4567. this.emit('removeListener', type, listener);
  4568. } else if (isObject(list)) {
  4569. for (i = length; i-- > 0;) {
  4570. if (list[i] === listener ||
  4571. (list[i].listener && list[i].listener === listener)) {
  4572. position = i;
  4573. break;
  4574. }
  4575. }
  4576. if (position < 0)
  4577. return this;
  4578. if (list.length === 1) {
  4579. list.length = 0;
  4580. delete this._events[type];
  4581. } else {
  4582. list.splice(position, 1);
  4583. }
  4584. if (this._events.removeListener)
  4585. this.emit('removeListener', type, listener);
  4586. }
  4587. return this;
  4588. };
  4589. EventEmitter.prototype.removeAllListeners = function(type) {
  4590. var key, listeners;
  4591. if (!this._events)
  4592. return this;
  4593. // not listening for removeListener, no need to emit
  4594. if (!this._events.removeListener) {
  4595. if (arguments.length === 0)
  4596. this._events = {};
  4597. else if (this._events[type])
  4598. delete this._events[type];
  4599. return this;
  4600. }
  4601. // emit removeListener for all listeners on all events
  4602. if (arguments.length === 0) {
  4603. for (key in this._events) {
  4604. if (key === 'removeListener') continue;
  4605. this.removeAllListeners(key);
  4606. }
  4607. this.removeAllListeners('removeListener');
  4608. this._events = {};
  4609. return this;
  4610. }
  4611. listeners = this._events[type];
  4612. if (isFunction(listeners)) {
  4613. this.removeListener(type, listeners);
  4614. } else {
  4615. // LIFO order
  4616. while (listeners.length)
  4617. this.removeListener(type, listeners[listeners.length - 1]);
  4618. }
  4619. delete this._events[type];
  4620. return this;
  4621. };
  4622. EventEmitter.prototype.listeners = function(type) {
  4623. var ret;
  4624. if (!this._events || !this._events[type])
  4625. ret = [];
  4626. else if (isFunction(this._events[type]))
  4627. ret = [this._events[type]];
  4628. else
  4629. ret = this._events[type].slice();
  4630. return ret;
  4631. };
  4632. EventEmitter.listenerCount = function(emitter, type) {
  4633. var ret;
  4634. if (!emitter._events || !emitter._events[type])
  4635. ret = 0;
  4636. else if (isFunction(emitter._events[type]))
  4637. ret = 1;
  4638. else
  4639. ret = emitter._events[type].length;
  4640. return ret;
  4641. };
  4642. function isFunction(arg) {
  4643. return typeof arg === 'function';
  4644. }
  4645. function isNumber(arg) {
  4646. return typeof arg === 'number';
  4647. }
  4648. function isObject(arg) {
  4649. return typeof arg === 'object' && arg !== null;
  4650. }
  4651. function isUndefined(arg) {
  4652. return arg === void 0;
  4653. }
  4654. },{}]},{},[14])(14)
  4655. });
  4656. //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi91c3IvbG9jYWwvbGliL25vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL3htcHAvSmluZ2xlU2Vzc2lvbi5qcyIsIi9Vc2Vycy9ocmlzdG8vRG9jdW1lbnRzL3dvcmtzcGFjZS9qaXRzaS1tZWV0L21vZHVsZXMveG1wcC9TRFAuanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL3htcHAvU0RQRGlmZmVyLmpzIiwiL1VzZXJzL2hyaXN0by9Eb2N1bWVudHMvd29ya3NwYWNlL2ppdHNpLW1lZXQvbW9kdWxlcy94bXBwL1NEUFV0aWwuanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL3htcHAvVHJhY2VhYmxlUGVlckNvbm5lY3Rpb24uanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL3htcHAvbW9kZXJhdG9yLmpzIiwiL1VzZXJzL2hyaXN0by9Eb2N1bWVudHMvd29ya3NwYWNlL2ppdHNpLW1lZXQvbW9kdWxlcy94bXBwL3JlY29yZGluZy5qcyIsIi9Vc2Vycy9ocmlzdG8vRG9jdW1lbnRzL3dvcmtzcGFjZS9qaXRzaS1tZWV0L21vZHVsZXMveG1wcC9zdHJvcGhlLmVtdWMuanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL3htcHAvc3Ryb3BoZS5qaW5nbGUuanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL3htcHAvc3Ryb3BoZS5sb2dnZXIuanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL3htcHAvc3Ryb3BoZS5tb2RlcmF0ZS5qcyIsIi9Vc2Vycy9ocmlzdG8vRG9jdW1lbnRzL3dvcmtzcGFjZS9qaXRzaS1tZWV0L21vZHVsZXMveG1wcC9zdHJvcGhlLnJheW8uanMiLCIvVXNlcnMvaHJpc3RvL0RvY3VtZW50cy93b3Jrc3BhY2Uvaml0c2ktbWVldC9tb2R1bGVzL3htcHAvc3Ryb3BoZS51dGlsLmpzIiwiL1VzZXJzL2hyaXN0by9Eb2N1bWVudHMvd29ya3NwYWNlL2ppdHNpLW1lZXQvbW9kdWxlcy94bXBwL3htcHAuanMiLCIvdXNyL2xvY2FsL2xpYi9ub2RlX21vZHVsZXMvYnJvd3NlcmlmeS9ub2RlX21vZHVsZXMvZXZlbnRzL2V2ZW50cy5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQ0FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1MkNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1bUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNwS0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNVZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMxUUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNwUEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN2SkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMvbEJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDOVVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbkJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDL0ZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMzWkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwiLyoganNoaW50IC1XMTE3ICovXG52YXIgVHJhY2VhYmxlUGVlckNvbm5lY3Rpb24gPSByZXF1aXJlKFwiLi9UcmFjZWFibGVQZWVyQ29ubmVjdGlvblwiKTtcbnZhciBTRFBEaWZmZXIgPSByZXF1aXJlKFwiLi9TRFBEaWZmZXJcIik7XG52YXIgU0RQVXRpbCA9IHJlcXVpcmUoXCIuL1NEUFV0aWxcIik7XG52YXIgU0RQID0gcmVxdWlyZShcIi4vU0RQXCIpO1xuXG4vLyBKaW5nbGUgc3R1ZmZcbmZ1bmN0aW9uIEppbmdsZVNlc3Npb24obWUsIHNpZCwgY29ubmVjdGlvbiwgc2VydmljZSkge1xuICAgIHRoaXMubWUgPSBtZTtcbiAgICB0aGlzLnNpZCA9IHNpZDtcbiAgICB0aGlzLmNvbm5lY3Rpb24gPSBjb25uZWN0aW9uO1xuICAgIHRoaXMuaW5pdGlhdG9yID0gbnVsbDtcbiAgICB0aGlzLnJlc3BvbmRlciA9IG51bGw7XG4gICAgdGhpcy5pc0luaXRpYXRvciA9IG51bGw7XG4gICAgdGhpcy5wZWVyamlkID0gbnVsbDtcbiAgICB0aGlzLnN0YXRlID0gbnVsbDtcbiAgICB0aGlzLmxvY2FsU0RQID0gbnVsbDtcbiAgICB0aGlzLnJlbW90ZVNEUCA9IG51bGw7XG4gICAgdGhpcy5yZWxheWVkU3RyZWFtcyA9IFtdO1xuICAgIHRoaXMuc3RhcnRUaW1lID0gbnVsbDtcbiAgICB0aGlzLnN0b3BUaW1lID0gbnVsbDtcbiAgICB0aGlzLm1lZGlhX2NvbnN0cmFpbnRzID0gbnVsbDtcbiAgICB0aGlzLnBjX2NvbnN0cmFpbnRzID0gbnVsbDtcbiAgICB0aGlzLmljZV9jb25maWcgPSB7fTtcbiAgICB0aGlzLmRyaXBfY29udGFpbmVyID0gW107XG4gICAgdGhpcy5zZXJ2aWNlID0gc2VydmljZTtcblxuICAgIHRoaXMudXNldHJpY2tsZSA9IHRydWU7XG4gICAgdGhpcy51c2VwcmFuc3dlciA9IGZhbHNlOyAvLyBlYXJseSB0cmFuc3BvcnQgd2FybXVwIC0tIG1pbmQgeW91LCB0aGlzIG1pZ2h0IGZhaWwuIGRlcGVuZHMgb24gd2VicnRjIGlzc3VlIDE3MThcbiAgICB0aGlzLnVzZWRyaXAgPSBmYWxzZTsgLy8gZHJpcHBpbmcgaXMgc2VuZGluZyB0cmlja2xlIGNhbmRpZGF0ZXMgbm90IG9uZS1ieS1vbmVcblxuICAgIHRoaXMuaGFkc3R1bmNhbmRpZGF0ZSA9IGZhbHNlO1xuICAgIHRoaXMuaGFkdHVybmNhbmRpZGF0ZSA9IGZhbHNlO1xuICAgIHRoaXMubGFzdGljZWNhbmRpZGF0ZSA9IGZhbHNlO1xuXG4gICAgdGhpcy5zdGF0c2ludGVydmFsID0gbnVsbDtcblxuICAgIHRoaXMucmVhc29uID0gbnVsbDtcblxuICAgIHRoaXMuYWRkc3NyYyA9IFtdO1xuICAgIHRoaXMucmVtb3Zlc3NyYyA9IFtdO1xuICAgIHRoaXMucGVuZGluZ29wID0gbnVsbDtcbiAgICB0aGlzLnN3aXRjaHN0cmVhbXMgPSBmYWxzZTtcblxuICAgIHRoaXMud2FpdCA9IHRydWU7XG4gICAgdGhpcy5sb2NhbFN0cmVhbXNTU1JDID0gbnVsbDtcblxuICAgIC8qKlxuICAgICAqIFRoZSBpbmRpY2F0b3Igd2hpY2ggZGV0ZXJtaW5lcyB3aGV0aGVyIHRoZSAobG9jYWwpIHZpZGVvIGhhcyBiZWVuIG11dGVkXG4gICAgICogaW4gcmVzcG9uc2UgdG8gYSB1c2VyIGNvbW1hbmQgaW4gY29udHJhc3QgdG8gYW4gYXV0b21hdGljIGRlY2lzaW9uIG1hZGVcbiAgICAgKiBieSB0aGUgYXBwbGljYXRpb24gbG9naWMuXG4gICAgICovXG4gICAgdGhpcy52aWRlb011dGVCeVVzZXIgPSBmYWxzZTtcbn1cblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuaW5pdGlhdGUgPSBmdW5jdGlvbiAocGVlcmppZCwgaXNJbml0aWF0b3IpIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgaWYgKHRoaXMuc3RhdGUgIT09IG51bGwpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcignYXR0ZW1wdCB0byBpbml0aWF0ZSBvbiBzZXNzaW9uICcgKyB0aGlzLnNpZCArXG4gICAgICAgICAgICAnaW4gc3RhdGUgJyArIHRoaXMuc3RhdGUpO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuaXNJbml0aWF0b3IgPSBpc0luaXRpYXRvcjtcbiAgICB0aGlzLnN0YXRlID0gJ3BlbmRpbmcnO1xuICAgIHRoaXMuaW5pdGlhdG9yID0gaXNJbml0aWF0b3IgPyB0aGlzLm1lIDogcGVlcmppZDtcbiAgICB0aGlzLnJlc3BvbmRlciA9ICFpc0luaXRpYXRvciA/IHRoaXMubWUgOiBwZWVyamlkO1xuICAgIHRoaXMucGVlcmppZCA9IHBlZXJqaWQ7XG4gICAgdGhpcy5oYWRzdHVuY2FuZGlkYXRlID0gZmFsc2U7XG4gICAgdGhpcy5oYWR0dXJuY2FuZGlkYXRlID0gZmFsc2U7XG4gICAgdGhpcy5sYXN0aWNlY2FuZGlkYXRlID0gZmFsc2U7XG5cbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uXG4gICAgICAgID0gbmV3IFRyYWNlYWJsZVBlZXJDb25uZWN0aW9uKFxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmppbmdsZS5pY2VfY29uZmlnLFxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmppbmdsZS5wY19jb25zdHJhaW50cyApO1xuXG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5vbmljZWNhbmRpZGF0ZSA9IGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgICBzZWxmLnNlbmRJY2VDYW5kaWRhdGUoZXZlbnQuY2FuZGlkYXRlKTtcbiAgICB9O1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24ub25hZGRzdHJlYW0gPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgY29uc29sZS5sb2coXCJSRU1PVEUgU1RSRUFNIEFEREVEOiBcIiArIGV2ZW50LnN0cmVhbSArIFwiIC0gXCIgKyBldmVudC5zdHJlYW0uaWQpO1xuICAgICAgICBzZWxmLnJlbW90ZVN0cmVhbUFkZGVkKGV2ZW50KTtcbiAgICB9O1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24ub25yZW1vdmVzdHJlYW0gPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgLy8gUmVtb3ZlIHRoZSBzdHJlYW0gZnJvbSByZW1vdGVTdHJlYW1zXG4gICAgICAgIC8vIEZJWE1FOiByZW1vdGVzdHJlYW1yZW1vdmVkLmppbmdsZSBub3QgZGVmaW5lZCBhbnl3aGVyZSh1bnVzZWQpXG4gICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ3JlbW90ZXN0cmVhbXJlbW92ZWQuamluZ2xlJywgW2V2ZW50LCBzZWxmLnNpZF0pO1xuICAgIH07XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5vbnNpZ25hbGluZ3N0YXRlY2hhbmdlID0gZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgIGlmICghKHNlbGYgJiYgc2VsZi5wZWVyY29ubmVjdGlvbikpIHJldHVybjtcbiAgICB9O1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24ub25pY2Vjb25uZWN0aW9uc3RhdGVjaGFuZ2UgPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgaWYgKCEoc2VsZiAmJiBzZWxmLnBlZXJjb25uZWN0aW9uKSkgcmV0dXJuO1xuICAgICAgICBzd2l0Y2ggKHNlbGYucGVlcmNvbm5lY3Rpb24uaWNlQ29ubmVjdGlvblN0YXRlKSB7XG4gICAgICAgICAgICBjYXNlICdjb25uZWN0ZWQnOlxuICAgICAgICAgICAgICAgIHRoaXMuc3RhcnRUaW1lID0gbmV3IERhdGUoKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgJ2Rpc2Nvbm5lY3RlZCc6XG4gICAgICAgICAgICAgICAgdGhpcy5zdG9wVGltZSA9IG5ldyBEYXRlKCk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgICAgb25JY2VDb25uZWN0aW9uU3RhdGVDaGFuZ2Uoc2VsZi5zaWQsIHNlbGYpO1xuICAgIH07XG4gICAgLy8gYWRkIGFueSBsb2NhbCBhbmQgcmVsYXllZCBzdHJlYW1cbiAgICBSVEMubG9jYWxTdHJlYW1zLmZvckVhY2goZnVuY3Rpb24oc3RyZWFtKSB7XG4gICAgICAgIHNlbGYucGVlcmNvbm5lY3Rpb24uYWRkU3RyZWFtKHN0cmVhbS5nZXRPcmlnaW5hbFN0cmVhbSgpKTtcbiAgICB9KTtcbiAgICB0aGlzLnJlbGF5ZWRTdHJlYW1zLmZvckVhY2goZnVuY3Rpb24oc3RyZWFtKSB7XG4gICAgICAgIHNlbGYucGVlcmNvbm5lY3Rpb24uYWRkU3RyZWFtKHN0cmVhbSk7XG4gICAgfSk7XG59O1xuXG5mdW5jdGlvbiBvbkljZUNvbm5lY3Rpb25TdGF0ZUNoYW5nZShzaWQsIHNlc3Npb24pIHtcbiAgICBzd2l0Y2ggKHNlc3Npb24ucGVlcmNvbm5lY3Rpb24uaWNlQ29ubmVjdGlvblN0YXRlKSB7XG4gICAgICAgIGNhc2UgJ2NoZWNraW5nJzpcbiAgICAgICAgICAgIHNlc3Npb24udGltZUNoZWNraW5nID0gKG5ldyBEYXRlKCkpLmdldFRpbWUoKTtcbiAgICAgICAgICAgIHNlc3Npb24uZmlyc3Rjb25uZWN0ID0gdHJ1ZTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdjb21wbGV0ZWQnOiAvLyBvbiBjYWxsZXIgc2lkZVxuICAgICAgICBjYXNlICdjb25uZWN0ZWQnOlxuICAgICAgICAgICAgaWYgKHNlc3Npb24uZmlyc3Rjb25uZWN0KSB7XG4gICAgICAgICAgICAgICAgc2Vzc2lvbi5maXJzdGNvbm5lY3QgPSBmYWxzZTtcbiAgICAgICAgICAgICAgICB2YXIgbWV0YWRhdGEgPSB7fTtcbiAgICAgICAgICAgICAgICBtZXRhZGF0YS5zZXR1cFRpbWVcbiAgICAgICAgICAgICAgICAgICAgPSAobmV3IERhdGUoKSkuZ2V0VGltZSgpIC0gc2Vzc2lvbi50aW1lQ2hlY2tpbmc7XG4gICAgICAgICAgICAgICAgc2Vzc2lvbi5wZWVyY29ubmVjdGlvbi5nZXRTdGF0cyhmdW5jdGlvbiAocmVzKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmKHJlcyAmJiByZXMucmVzdWx0KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXMucmVzdWx0KCkuZm9yRWFjaChmdW5jdGlvbiAocmVwb3J0KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHJlcG9ydC50eXBlID09ICdnb29nQ2FuZGlkYXRlUGFpcicgJiZcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwb3J0LnN0YXQoJ2dvb2dBY3RpdmVDb25uZWN0aW9uJykgPT0gJ3RydWUnKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGFkYXRhLmxvY2FsQ2FuZGlkYXRlVHlwZVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPSByZXBvcnQuc3RhdCgnZ29vZ0xvY2FsQ2FuZGlkYXRlVHlwZScpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRhZGF0YS5yZW1vdGVDYW5kaWRhdGVUeXBlXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA9IHJlcG9ydC5zdGF0KCdnb29nUmVtb3RlQ2FuZGlkYXRlVHlwZScpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIGxvZyBwYWlyIGFzIHdlbGwgc28gd2UgY2FuIGdldCBuaWNlIHBpZVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBjaGFydHNcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0YWRhdGEuY2FuZGlkYXRlUGFpclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPSByZXBvcnQuc3RhdCgnZ29vZ0xvY2FsQ2FuZGlkYXRlVHlwZScpICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnOycgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcG9ydC5zdGF0KCdnb29nUmVtb3RlQ2FuZGlkYXRlVHlwZScpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChyZXBvcnQuc3RhdCgnZ29vZ1JlbW90ZUFkZHJlc3MnKS5pbmRleE9mKCdbJykgPT09IDApXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGFkYXRhLmlwdjYgPSB0cnVlO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGJyZWFrO1xuICAgIH1cbn1cblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuYWNjZXB0ID0gZnVuY3Rpb24gKCkge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB0aGlzLnN0YXRlID0gJ2FjdGl2ZSc7XG5cbiAgICB2YXIgcHJhbnN3ZXIgPSB0aGlzLnBlZXJjb25uZWN0aW9uLmxvY2FsRGVzY3JpcHRpb247XG4gICAgaWYgKCFwcmFuc3dlciB8fCBwcmFuc3dlci50eXBlICE9ICdwcmFuc3dlcicpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zb2xlLmxvZygnZ29pbmcgZnJvbSBwcmFuc3dlciB0byBhbnN3ZXInKTtcbiAgICBpZiAodGhpcy51c2V0cmlja2xlKSB7XG4gICAgICAgIC8vIHJlbW92ZSBjYW5kaWRhdGVzIGFscmVhZHkgc2VudCBmcm9tIHNlc3Npb24tYWNjZXB0XG4gICAgICAgIHZhciBsaW5lcyA9IFNEUFV0aWwuZmluZF9saW5lcyhwcmFuc3dlci5zZHAsICdhPWNhbmRpZGF0ZTonKTtcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBsaW5lcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgcHJhbnN3ZXIuc2RwID0gcHJhbnN3ZXIuc2RwLnJlcGxhY2UobGluZXNbaV0gKyAnXFxyXFxuJywgJycpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHdoaWxlIChTRFBVdGlsLmZpbmRfbGluZShwcmFuc3dlci5zZHAsICdhPWluYWN0aXZlJykpIHtcbiAgICAgICAgLy8gRklYTUU6IGNoYW5nZSBhbnkgaW5hY3RpdmUgdG8gc2VuZHJlY3Ygb3Igd2hhdGV2ZXIgdGhleSB3ZXJlIG9yaWdpbmFsbHlcbiAgICAgICAgcHJhbnN3ZXIuc2RwID0gcHJhbnN3ZXIuc2RwLnJlcGxhY2UoJ2E9aW5hY3RpdmUnLCAnYT1zZW5kcmVjdicpO1xuICAgIH1cbiAgICBwcmFuc3dlciA9IHNpbXVsY2FzdC5yZXZlcnNlVHJhbnNmb3JtTG9jYWxEZXNjcmlwdGlvbihwcmFuc3dlcik7XG4gICAgdmFyIHByc2RwID0gbmV3IFNEUChwcmFuc3dlci5zZHApO1xuICAgIHZhciBhY2NlcHQgPSAkaXEoe3RvOiB0aGlzLnBlZXJqaWQsXG4gICAgICAgIHR5cGU6ICdzZXQnfSlcbiAgICAgICAgLmMoJ2ppbmdsZScsIHt4bWxuczogJ3Vybjp4bXBwOmppbmdsZToxJyxcbiAgICAgICAgICAgIGFjdGlvbjogJ3Nlc3Npb24tYWNjZXB0JyxcbiAgICAgICAgICAgIGluaXRpYXRvcjogdGhpcy5pbml0aWF0b3IsXG4gICAgICAgICAgICByZXNwb25kZXI6IHRoaXMucmVzcG9uZGVyLFxuICAgICAgICAgICAgc2lkOiB0aGlzLnNpZCB9KTtcbiAgICBwcnNkcC50b0ppbmdsZShhY2NlcHQsIHRoaXMuaW5pdGlhdG9yID09IHRoaXMubWUgPyAnaW5pdGlhdG9yJyA6ICdyZXNwb25kZXInLCB0aGlzLmxvY2FsU3RyZWFtc1NTUkMpO1xuICAgIHZhciBzZHAgPSB0aGlzLnBlZXJjb25uZWN0aW9uLmxvY2FsRGVzY3JpcHRpb24uc2RwO1xuICAgIHdoaWxlIChTRFBVdGlsLmZpbmRfbGluZShzZHAsICdhPWluYWN0aXZlJykpIHtcbiAgICAgICAgLy8gRklYTUU6IGNoYW5nZSBhbnkgaW5hY3RpdmUgdG8gc2VuZHJlY3Ygb3Igd2hhdGV2ZXIgdGhleSB3ZXJlIG9yaWdpbmFsbHlcbiAgICAgICAgc2RwID0gc2RwLnJlcGxhY2UoJ2E9aW5hY3RpdmUnLCAnYT1zZW5kcmVjdicpO1xuICAgIH1cbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5zZXRMb2NhbERlc2NyaXB0aW9uKG5ldyBSVENTZXNzaW9uRGVzY3JpcHRpb24oe3R5cGU6ICdhbnN3ZXInLCBzZHA6IHNkcH0pLFxuICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAvL2NvbnNvbGUubG9nKCdzZXRMb2NhbERlc2NyaXB0aW9uIHN1Y2Nlc3MnKTtcbiAgICAgICAgICAgIHNlbGYuc2V0TG9jYWxEZXNjcmlwdGlvbigpO1xuXG4gICAgICAgICAgICBzZWxmLmNvbm5lY3Rpb24uc2VuZElRKGFjY2VwdCxcbiAgICAgICAgICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBhY2sgPSB7fTtcbiAgICAgICAgICAgICAgICAgICAgYWNrLnNvdXJjZSA9ICdhbnN3ZXInO1xuICAgICAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdhY2suamluZ2xlJywgW3NlbGYuc2lkLCBhY2tdKTtcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChzdGFuemEpIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIGVycm9yID0gKCQoc3RhbnphKS5maW5kKCdlcnJvcicpLmxlbmd0aCkgPyB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb2RlOiAkKHN0YW56YSkuZmluZCgnZXJyb3InKS5hdHRyKCdjb2RlJyksXG4gICAgICAgICAgICAgICAgICAgICAgICByZWFzb246ICQoc3RhbnphKS5maW5kKCdlcnJvciA6Zmlyc3QnKVswXS50YWdOYW1lXG4gICAgICAgICAgICAgICAgICAgIH06e307XG4gICAgICAgICAgICAgICAgICAgIGVycm9yLnNvdXJjZSA9ICdhbnN3ZXInO1xuICAgICAgICAgICAgICAgICAgICBKaW5nbGVTZXNzaW9uLm9uSmluZ2xlRXJyb3Ioc2VsZi5zaWQsIGVycm9yKTtcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIDEwMDAwKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ3NldExvY2FsRGVzY3JpcHRpb24gZmFpbGVkJywgZSk7XG4gICAgICAgIH1cbiAgICApO1xufTtcblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUudGVybWluYXRlID0gZnVuY3Rpb24gKHJlYXNvbikge1xuICAgIHRoaXMuc3RhdGUgPSAnZW5kZWQnO1xuICAgIHRoaXMucmVhc29uID0gcmVhc29uO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uY2xvc2UoKTtcbiAgICBpZiAodGhpcy5zdGF0c2ludGVydmFsICE9PSBudWxsKSB7XG4gICAgICAgIHdpbmRvdy5jbGVhckludGVydmFsKHRoaXMuc3RhdHNpbnRlcnZhbCk7XG4gICAgICAgIHRoaXMuc3RhdHNpbnRlcnZhbCA9IG51bGw7XG4gICAgfVxufTtcblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuYWN0aXZlID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiB0aGlzLnN0YXRlID09ICdhY3RpdmUnO1xufTtcblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuc2VuZEljZUNhbmRpZGF0ZSA9IGZ1bmN0aW9uIChjYW5kaWRhdGUpIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgaWYgKGNhbmRpZGF0ZSAmJiAhdGhpcy5sYXN0aWNlY2FuZGlkYXRlKSB7XG4gICAgICAgIHZhciBpY2UgPSBTRFBVdGlsLmljZXBhcmFtcyh0aGlzLmxvY2FsU0RQLm1lZGlhW2NhbmRpZGF0ZS5zZHBNTGluZUluZGV4XSwgdGhpcy5sb2NhbFNEUC5zZXNzaW9uKTtcbiAgICAgICAgdmFyIGpjYW5kID0gU0RQVXRpbC5jYW5kaWRhdGVUb0ppbmdsZShjYW5kaWRhdGUuY2FuZGlkYXRlKTtcbiAgICAgICAgaWYgKCEoaWNlICYmIGpjYW5kKSkge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcignZmFpbGVkIHRvIGdldCBpY2UgJiYgamNhbmQnKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBpY2UueG1sbnMgPSAndXJuOnhtcHA6amluZ2xlOnRyYW5zcG9ydHM6aWNlLXVkcDoxJztcblxuICAgICAgICBpZiAoamNhbmQudHlwZSA9PT0gJ3NyZmx4Jykge1xuICAgICAgICAgICAgdGhpcy5oYWRzdHVuY2FuZGlkYXRlID0gdHJ1ZTtcbiAgICAgICAgfSBlbHNlIGlmIChqY2FuZC50eXBlID09PSAncmVsYXknKSB7XG4gICAgICAgICAgICB0aGlzLmhhZHR1cm5jYW5kaWRhdGUgPSB0cnVlO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMudXNldHJpY2tsZSkge1xuICAgICAgICAgICAgaWYgKHRoaXMudXNlZHJpcCkge1xuICAgICAgICAgICAgICAgIGlmICh0aGlzLmRyaXBfY29udGFpbmVyLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgICAgICAgICAgICAvLyBzdGFydCAyMG1zIGNhbGxvdXRcbiAgICAgICAgICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHNlbGYuZHJpcF9jb250YWluZXIubGVuZ3RoID09PSAwKSByZXR1cm47XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLnNlbmRJY2VDYW5kaWRhdGVzKHNlbGYuZHJpcF9jb250YWluZXIpO1xuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5kcmlwX2NvbnRhaW5lciA9IFtdO1xuICAgICAgICAgICAgICAgICAgICB9LCAyMCk7XG5cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgdGhpcy5kcmlwX2NvbnRhaW5lci5wdXNoKGNhbmRpZGF0ZSk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBzZWxmLnNlbmRJY2VDYW5kaWRhdGUoW2NhbmRpZGF0ZV0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgICAgLy9jb25zb2xlLmxvZygnc2VuZEljZUNhbmRpZGF0ZTogbGFzdCBjYW5kaWRhdGUuJyk7XG4gICAgICAgIGlmICghdGhpcy51c2V0cmlja2xlKSB7XG4gICAgICAgICAgICAvL2NvbnNvbGUubG9nKCdzaG91bGQgc2VuZCBmdWxsIG9mZmVyIG5vdy4uLicpO1xuICAgICAgICAgICAgdmFyIGluaXQgPSAkaXEoe3RvOiB0aGlzLnBlZXJqaWQsXG4gICAgICAgICAgICAgICAgdHlwZTogJ3NldCd9KVxuICAgICAgICAgICAgICAgIC5jKCdqaW5nbGUnLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6MScsXG4gICAgICAgICAgICAgICAgICAgIGFjdGlvbjogdGhpcy5wZWVyY29ubmVjdGlvbi5sb2NhbERlc2NyaXB0aW9uLnR5cGUgPT0gJ29mZmVyJyA/ICdzZXNzaW9uLWluaXRpYXRlJyA6ICdzZXNzaW9uLWFjY2VwdCcsXG4gICAgICAgICAgICAgICAgICAgIGluaXRpYXRvcjogdGhpcy5pbml0aWF0b3IsXG4gICAgICAgICAgICAgICAgICAgIHNpZDogdGhpcy5zaWR9KTtcbiAgICAgICAgICAgIHRoaXMubG9jYWxTRFAgPSBuZXcgU0RQKHRoaXMucGVlcmNvbm5lY3Rpb24ubG9jYWxEZXNjcmlwdGlvbi5zZHApO1xuICAgICAgICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgICAgICAgdmFyIHNlbmRKaW5nbGUgPSBmdW5jdGlvbiAoc3NyYykge1xuICAgICAgICAgICAgICAgIGlmKCFzc3JjKVxuICAgICAgICAgICAgICAgICAgICBzc3JjID0ge307XG4gICAgICAgICAgICAgICAgc2VsZi5sb2NhbFNEUC50b0ppbmdsZShpbml0LCBzZWxmLmluaXRpYXRvciA9PSBzZWxmLm1lID8gJ2luaXRpYXRvcicgOiAncmVzcG9uZGVyJywgc3NyYyk7XG4gICAgICAgICAgICAgICAgc2VsZi5jb25uZWN0aW9uLnNlbmRJUShpbml0LFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAvL2NvbnNvbGUubG9nKCdzZXNzaW9uIGluaXRpYXRlIGFjaycpO1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGFjayA9IHt9O1xuICAgICAgICAgICAgICAgICAgICAgICAgYWNrLnNvdXJjZSA9ICdvZmZlcic7XG4gICAgICAgICAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdhY2suamluZ2xlJywgW3NlbGYuc2lkLCBhY2tdKTtcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKHN0YW56YSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5zdGF0ZSA9ICdlcnJvcic7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLnBlZXJjb25uZWN0aW9uLmNsb3NlKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgZXJyb3IgPSAoJChzdGFuemEpLmZpbmQoJ2Vycm9yJykubGVuZ3RoKSA/IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2RlOiAkKHN0YW56YSkuZmluZCgnZXJyb3InKS5hdHRyKCdjb2RlJyksXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVhc29uOiAkKHN0YW56YSkuZmluZCgnZXJyb3IgOmZpcnN0JylbMF0udGFnTmFtZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIH06e307XG4gICAgICAgICAgICAgICAgICAgICAgICBlcnJvci5zb3VyY2UgPSAnb2ZmZXInO1xuICAgICAgICAgICAgICAgICAgICAgICAgSmluZ2xlU2Vzc2lvbi5vbkppbmdsZUVycm9yKHNlbGYuc2lkLCBlcnJvcik7XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIDEwMDAwKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHNlbmRKaW5nbGUoKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmxhc3RpY2VjYW5kaWRhdGUgPSB0cnVlO1xuICAgICAgICBjb25zb2xlLmxvZygnSGF2ZSB3ZSBlbmNvdW50ZXJlZCBhbnkgc3JmbHggY2FuZGlkYXRlcz8gJyArIHRoaXMuaGFkc3R1bmNhbmRpZGF0ZSk7XG4gICAgICAgIGNvbnNvbGUubG9nKCdIYXZlIHdlIGVuY291bnRlcmVkIGFueSByZWxheSBjYW5kaWRhdGVzPyAnICsgdGhpcy5oYWR0dXJuY2FuZGlkYXRlKTtcblxuICAgICAgICBpZiAoISh0aGlzLmhhZHN0dW5jYW5kaWRhdGUgfHwgdGhpcy5oYWR0dXJuY2FuZGlkYXRlKSAmJiB0aGlzLnBlZXJjb25uZWN0aW9uLnNpZ25hbGluZ1N0YXRlICE9ICdjbG9zZWQnKSB7XG4gICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdub3N0dW5jYW5kaWRhdGVzLmppbmdsZScsIFt0aGlzLnNpZF0pO1xuICAgICAgICB9XG4gICAgfVxufTtcblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuc2VuZEljZUNhbmRpZGF0ZXMgPSBmdW5jdGlvbiAoY2FuZGlkYXRlcykge1xuICAgIGNvbnNvbGUubG9nKCdzZW5kSWNlQ2FuZGlkYXRlcycsIGNhbmRpZGF0ZXMpO1xuICAgIHZhciBjYW5kID0gJGlxKHt0bzogdGhpcy5wZWVyamlkLCB0eXBlOiAnc2V0J30pXG4gICAgICAgIC5jKCdqaW5nbGUnLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6MScsXG4gICAgICAgICAgICBhY3Rpb246ICd0cmFuc3BvcnQtaW5mbycsXG4gICAgICAgICAgICBpbml0aWF0b3I6IHRoaXMuaW5pdGlhdG9yLFxuICAgICAgICAgICAgc2lkOiB0aGlzLnNpZH0pO1xuICAgIGZvciAodmFyIG1pZCA9IDA7IG1pZCA8IHRoaXMubG9jYWxTRFAubWVkaWEubGVuZ3RoOyBtaWQrKykge1xuICAgICAgICB2YXIgY2FuZHMgPSBjYW5kaWRhdGVzLmZpbHRlcihmdW5jdGlvbiAoZWwpIHsgcmV0dXJuIGVsLnNkcE1MaW5lSW5kZXggPT0gbWlkOyB9KTtcbiAgICAgICAgdmFyIG1saW5lID0gU0RQVXRpbC5wYXJzZV9tbGluZSh0aGlzLmxvY2FsU0RQLm1lZGlhW21pZF0uc3BsaXQoJ1xcclxcbicpWzBdKTtcbiAgICAgICAgaWYgKGNhbmRzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIHZhciBpY2UgPSBTRFBVdGlsLmljZXBhcmFtcyh0aGlzLmxvY2FsU0RQLm1lZGlhW21pZF0sIHRoaXMubG9jYWxTRFAuc2Vzc2lvbik7XG4gICAgICAgICAgICBpY2UueG1sbnMgPSAndXJuOnhtcHA6amluZ2xlOnRyYW5zcG9ydHM6aWNlLXVkcDoxJztcbiAgICAgICAgICAgIGNhbmQuYygnY29udGVudCcsIHtjcmVhdG9yOiB0aGlzLmluaXRpYXRvciA9PSB0aGlzLm1lID8gJ2luaXRpYXRvcicgOiAncmVzcG9uZGVyJyxcbiAgICAgICAgICAgICAgICBuYW1lOiAoY2FuZHNbMF0uc2RwTWlkPyBjYW5kc1swXS5zZHBNaWQgOiBtbGluZS5tZWRpYSlcbiAgICAgICAgICAgIH0pLmMoJ3RyYW5zcG9ydCcsIGljZSk7XG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGNhbmRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgY2FuZC5jKCdjYW5kaWRhdGUnLCBTRFBVdGlsLmNhbmRpZGF0ZVRvSmluZ2xlKGNhbmRzW2ldLmNhbmRpZGF0ZSkpLnVwKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBhZGQgZmluZ2VycHJpbnRcbiAgICAgICAgICAgIGlmIChTRFBVdGlsLmZpbmRfbGluZSh0aGlzLmxvY2FsU0RQLm1lZGlhW21pZF0sICdhPWZpbmdlcnByaW50OicsIHRoaXMubG9jYWxTRFAuc2Vzc2lvbikpIHtcbiAgICAgICAgICAgICAgICB2YXIgdG1wID0gU0RQVXRpbC5wYXJzZV9maW5nZXJwcmludChTRFBVdGlsLmZpbmRfbGluZSh0aGlzLmxvY2FsU0RQLm1lZGlhW21pZF0sICdhPWZpbmdlcnByaW50OicsIHRoaXMubG9jYWxTRFAuc2Vzc2lvbikpO1xuICAgICAgICAgICAgICAgIHRtcC5yZXF1aXJlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgY2FuZC5jKFxuICAgICAgICAgICAgICAgICAgICAnZmluZ2VycHJpbnQnLFxuICAgICAgICAgICAgICAgICAgICB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6YXBwczpkdGxzOjAnfSlcbiAgICAgICAgICAgICAgICAgICAgLnQodG1wLmZpbmdlcnByaW50KTtcbiAgICAgICAgICAgICAgICBkZWxldGUgdG1wLmZpbmdlcnByaW50O1xuICAgICAgICAgICAgICAgIGNhbmQuYXR0cnModG1wKTtcbiAgICAgICAgICAgICAgICBjYW5kLnVwKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjYW5kLnVwKCk7IC8vIHRyYW5zcG9ydFxuICAgICAgICAgICAgY2FuZC51cCgpOyAvLyBjb250ZW50XG4gICAgICAgIH1cbiAgICB9XG4gICAgLy8gbWlnaHQgbWVyZ2UgbGFzdC1jYW5kaWRhdGUgbm90aWZpY2F0aW9uIGludG8gdGhpcywgYnV0IGl0IGlzIGNhbGxlZCBhbG90IGxhdGVyLiBTZWUgd2VicnRjIGlzc3VlICMyMzQwXG4gICAgLy9jb25zb2xlLmxvZygnd2FzIHRoaXMgdGhlIGxhc3QgY2FuZGlkYXRlJywgdGhpcy5sYXN0aWNlY2FuZGlkYXRlKTtcbiAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZElRKGNhbmQsXG4gICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHZhciBhY2sgPSB7fTtcbiAgICAgICAgICAgIGFjay5zb3VyY2UgPSAndHJhbnNwb3J0aW5mbyc7XG4gICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdhY2suamluZ2xlJywgW3RoaXMuc2lkLCBhY2tdKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKHN0YW56YSkge1xuICAgICAgICAgICAgdmFyIGVycm9yID0gKCQoc3RhbnphKS5maW5kKCdlcnJvcicpLmxlbmd0aCkgPyB7XG4gICAgICAgICAgICAgICAgY29kZTogJChzdGFuemEpLmZpbmQoJ2Vycm9yJykuYXR0cignY29kZScpLFxuICAgICAgICAgICAgICAgIHJlYXNvbjogJChzdGFuemEpLmZpbmQoJ2Vycm9yIDpmaXJzdCcpWzBdLnRhZ05hbWUsXG4gICAgICAgICAgICB9Ont9O1xuICAgICAgICAgICAgZXJyb3Iuc291cmNlID0gJ3RyYW5zcG9ydGluZm8nO1xuICAgICAgICAgICAgSmluZ2xlU2Vzc2lvbi5vbkppbmdsZUVycm9yKHRoaXMuc2lkLCBlcnJvcik7XG4gICAgICAgIH0sXG4gICAgICAgIDEwMDAwKTtcbn07XG5cblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuc2VuZE9mZmVyID0gZnVuY3Rpb24gKCkge1xuICAgIC8vY29uc29sZS5sb2coJ3NlbmRPZmZlci4uLicpO1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLmNyZWF0ZU9mZmVyKGZ1bmN0aW9uIChzZHApIHtcbiAgICAgICAgICAgIHNlbGYuY3JlYXRlZE9mZmVyKHNkcCk7XG4gICAgICAgIH0sXG4gICAgICAgIGZ1bmN0aW9uIChlKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKCdjcmVhdGVPZmZlciBmYWlsZWQnLCBlKTtcbiAgICAgICAgfSxcbiAgICAgICAgdGhpcy5tZWRpYV9jb25zdHJhaW50c1xuICAgICk7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5jcmVhdGVkT2ZmZXIgPSBmdW5jdGlvbiAoc2RwKSB7XG4gICAgLy9jb25zb2xlLmxvZygnY3JlYXRlZE9mZmVyJywgc2RwKTtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdGhpcy5sb2NhbFNEUCA9IG5ldyBTRFAoc2RwLnNkcCk7XG4gICAgLy90aGlzLmxvY2FsU0RQLm1hbmdsZSgpO1xuICAgIHZhciBzZW5kSmluZ2xlID0gZnVuY3Rpb24gKCkge1xuICAgICAgICB2YXIgaW5pdCA9ICRpcSh7dG86IHRoaXMucGVlcmppZCxcbiAgICAgICAgICAgIHR5cGU6ICdzZXQnfSlcbiAgICAgICAgICAgIC5jKCdqaW5nbGUnLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6MScsXG4gICAgICAgICAgICAgICAgYWN0aW9uOiAnc2Vzc2lvbi1pbml0aWF0ZScsXG4gICAgICAgICAgICAgICAgaW5pdGlhdG9yOiB0aGlzLmluaXRpYXRvcixcbiAgICAgICAgICAgICAgICBzaWQ6IHRoaXMuc2lkfSk7XG4gICAgICAgIHNlbGYubG9jYWxTRFAudG9KaW5nbGUoaW5pdCwgdGhpcy5pbml0aWF0b3IgPT0gdGhpcy5tZSA/ICdpbml0aWF0b3InIDogJ3Jlc3BvbmRlcicsIHRoaXMubG9jYWxTdHJlYW1zU1NSQyk7XG4gICAgICAgIHNlbGYuY29ubmVjdGlvbi5zZW5kSVEoaW5pdCxcbiAgICAgICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICB2YXIgYWNrID0ge307XG4gICAgICAgICAgICAgICAgYWNrLnNvdXJjZSA9ICdvZmZlcic7XG4gICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcignYWNrLmppbmdsZScsIFtzZWxmLnNpZCwgYWNrXSk7XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZnVuY3Rpb24gKHN0YW56YSkge1xuICAgICAgICAgICAgICAgIHNlbGYuc3RhdGUgPSAnZXJyb3InO1xuICAgICAgICAgICAgICAgIHNlbGYucGVlcmNvbm5lY3Rpb24uY2xvc2UoKTtcbiAgICAgICAgICAgICAgICB2YXIgZXJyb3IgPSAoJChzdGFuemEpLmZpbmQoJ2Vycm9yJykubGVuZ3RoKSA/IHtcbiAgICAgICAgICAgICAgICAgICAgY29kZTogJChzdGFuemEpLmZpbmQoJ2Vycm9yJykuYXR0cignY29kZScpLFxuICAgICAgICAgICAgICAgICAgICByZWFzb246ICQoc3RhbnphKS5maW5kKCdlcnJvciA6Zmlyc3QnKVswXS50YWdOYW1lLFxuICAgICAgICAgICAgICAgIH06e307XG4gICAgICAgICAgICAgICAgZXJyb3Iuc291cmNlID0gJ29mZmVyJztcbiAgICAgICAgICAgICAgICBKaW5nbGVTZXNzaW9uLm9uSmluZ2xlRXJyb3Ioc2VsZi5zaWQsIGVycm9yKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAxMDAwMCk7XG4gICAgfVxuICAgIHNkcC5zZHAgPSB0aGlzLmxvY2FsU0RQLnJhdztcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLnNldExvY2FsRGVzY3JpcHRpb24oc2RwLFxuICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBpZihzZWxmLnVzZXRyaWNrbGUpXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgc2VuZEppbmdsZSgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgc2VsZi5zZXRMb2NhbERlc2NyaXB0aW9uKCk7XG4gICAgICAgICAgICAvL2NvbnNvbGUubG9nKCdzZXRMb2NhbERlc2NyaXB0aW9uIHN1Y2Nlc3MnKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ3NldExvY2FsRGVzY3JpcHRpb24gZmFpbGVkJywgZSk7XG4gICAgICAgIH1cbiAgICApO1xuICAgIHZhciBjYW5kcyA9IFNEUFV0aWwuZmluZF9saW5lcyh0aGlzLmxvY2FsU0RQLnJhdywgJ2E9Y2FuZGlkYXRlOicpO1xuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgY2FuZHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgdmFyIGNhbmQgPSBTRFBVdGlsLnBhcnNlX2ljZWNhbmRpZGF0ZShjYW5kc1tpXSk7XG4gICAgICAgIGlmIChjYW5kLnR5cGUgPT0gJ3NyZmx4Jykge1xuICAgICAgICAgICAgdGhpcy5oYWRzdHVuY2FuZGlkYXRlID0gdHJ1ZTtcbiAgICAgICAgfSBlbHNlIGlmIChjYW5kLnR5cGUgPT0gJ3JlbGF5Jykge1xuICAgICAgICAgICAgdGhpcy5oYWR0dXJuY2FuZGlkYXRlID0gdHJ1ZTtcbiAgICAgICAgfVxuICAgIH1cbn07XG5cbkppbmdsZVNlc3Npb24ucHJvdG90eXBlLnNldFJlbW90ZURlc2NyaXB0aW9uID0gZnVuY3Rpb24gKGVsZW0sIGRlc2N0eXBlKSB7XG4gICAgLy9jb25zb2xlLmxvZygnc2V0dGluZyByZW1vdGUgZGVzY3JpcHRpb24uLi4gJywgZGVzY3R5cGUpO1xuICAgIHRoaXMucmVtb3RlU0RQID0gbmV3IFNEUCgnJyk7XG4gICAgdGhpcy5yZW1vdGVTRFAuZnJvbUppbmdsZShlbGVtKTtcbiAgICBpZiAodGhpcy5wZWVyY29ubmVjdGlvbi5yZW1vdGVEZXNjcmlwdGlvbiAhPT0gbnVsbCkge1xuICAgICAgICBjb25zb2xlLmxvZygnc2V0UmVtb3RlRGVzY3JpcHRpb24gd2hlbiByZW1vdGUgZGVzY3JpcHRpb24gaXMgbm90IG51bGwsIHNob3VsZCBiZSBwcmFuc3dlcicsIHRoaXMucGVlcmNvbm5lY3Rpb24ucmVtb3RlRGVzY3JpcHRpb24pO1xuICAgICAgICBpZiAodGhpcy5wZWVyY29ubmVjdGlvbi5yZW1vdGVEZXNjcmlwdGlvbi50eXBlID09ICdwcmFuc3dlcicpIHtcbiAgICAgICAgICAgIHZhciBwcmFuc3dlciA9IG5ldyBTRFAodGhpcy5wZWVyY29ubmVjdGlvbi5yZW1vdGVEZXNjcmlwdGlvbi5zZHApO1xuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBwcmFuc3dlci5tZWRpYS5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIC8vIG1ha2Ugc3VyZSB3ZSBoYXZlIGljZSB1ZnJhZyBhbmQgcHdkXG4gICAgICAgICAgICAgICAgaWYgKCFTRFBVdGlsLmZpbmRfbGluZSh0aGlzLnJlbW90ZVNEUC5tZWRpYVtpXSwgJ2E9aWNlLXVmcmFnOicsIHRoaXMucmVtb3RlU0RQLnNlc3Npb24pKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChTRFBVdGlsLmZpbmRfbGluZShwcmFuc3dlci5tZWRpYVtpXSwgJ2E9aWNlLXVmcmFnOicsIHByYW5zd2VyLnNlc3Npb24pKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnJlbW90ZVNEUC5tZWRpYVtpXSArPSBTRFBVdGlsLmZpbmRfbGluZShwcmFuc3dlci5tZWRpYVtpXSwgJ2E9aWNlLXVmcmFnOicsIHByYW5zd2VyLnNlc3Npb24pICsgJ1xcclxcbic7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oJ25vIGljZSB1ZnJhZz8nKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBpZiAoU0RQVXRpbC5maW5kX2xpbmUocHJhbnN3ZXIubWVkaWFbaV0sICdhPWljZS1wd2Q6JywgcHJhbnN3ZXIuc2Vzc2lvbikpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMucmVtb3RlU0RQLm1lZGlhW2ldICs9IFNEUFV0aWwuZmluZF9saW5lKHByYW5zd2VyLm1lZGlhW2ldLCAnYT1pY2UtcHdkOicsIHByYW5zd2VyLnNlc3Npb24pICsgJ1xcclxcbic7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oJ25vIGljZSBwd2Q/Jyk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgLy8gY29weSBvdmVyIGNhbmRpZGF0ZXNcbiAgICAgICAgICAgICAgICB2YXIgbGluZXMgPSBTRFBVdGlsLmZpbmRfbGluZXMocHJhbnN3ZXIubWVkaWFbaV0sICdhPWNhbmRpZGF0ZTonKTtcbiAgICAgICAgICAgICAgICBmb3IgKHZhciBqID0gMDsgaiA8IGxpbmVzLmxlbmd0aDsgaisrKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMucmVtb3RlU0RQLm1lZGlhW2ldICs9IGxpbmVzW2pdICsgJ1xcclxcbic7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5yZW1vdGVTRFAucmF3ID0gdGhpcy5yZW1vdGVTRFAuc2Vzc2lvbiArIHRoaXMucmVtb3RlU0RQLm1lZGlhLmpvaW4oJycpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHZhciByZW1vdGVkZXNjID0gbmV3IFJUQ1Nlc3Npb25EZXNjcmlwdGlvbih7dHlwZTogZGVzY3R5cGUsIHNkcDogdGhpcy5yZW1vdGVTRFAucmF3fSk7XG5cbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLnNldFJlbW90ZURlc2NyaXB0aW9uKHJlbW90ZWRlc2MsXG4gICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIC8vY29uc29sZS5sb2coJ3NldFJlbW90ZURlc2NyaXB0aW9uIHN1Y2Nlc3MnKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ3NldFJlbW90ZURlc2NyaXB0aW9uIGVycm9yJywgZSk7XG4gICAgICAgICAgICBKaW5nbGVTZXNzaW9uLm9uSmluZ2xlRmF0YWxFcnJvcihzZWxmLCBlKTtcbiAgICAgICAgfVxuICAgICk7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5hZGRJY2VDYW5kaWRhdGUgPSBmdW5jdGlvbiAoZWxlbSkge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICBpZiAodGhpcy5wZWVyY29ubmVjdGlvbi5zaWduYWxpbmdTdGF0ZSA9PSAnY2xvc2VkJykge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmICghdGhpcy5wZWVyY29ubmVjdGlvbi5yZW1vdGVEZXNjcmlwdGlvbiAmJiB0aGlzLnBlZXJjb25uZWN0aW9uLnNpZ25hbGluZ1N0YXRlID09ICdoYXZlLWxvY2FsLW9mZmVyJykge1xuICAgICAgICBjb25zb2xlLmxvZygndHJpY2tsZSBpY2UgY2FuZGlkYXRlIGFycml2aW5nIGJlZm9yZSBzZXNzaW9uIGFjY2VwdC4uLicpO1xuICAgICAgICAvLyBjcmVhdGUgYSBQUkFOU1dFUiBmb3Igc2V0UmVtb3RlRGVzY3JpcHRpb25cbiAgICAgICAgaWYgKCF0aGlzLnJlbW90ZVNEUCkge1xuICAgICAgICAgICAgdmFyIGNvYmJsZWQgPSAndj0wXFxyXFxuJyArXG4gICAgICAgICAgICAgICAgJ289LSAnICsgJzE5MjM1MTg1MTYnICsgJyAyIElOIElQNCAwLjAuMC4wXFxyXFxuJyArLy8gRklYTUVcbiAgICAgICAgICAgICAgICAncz0tXFxyXFxuJyArXG4gICAgICAgICAgICAgICAgJ3Q9MCAwXFxyXFxuJztcbiAgICAgICAgICAgIC8vIGZpcnN0LCB0YWtlIHNvbWUgdGhpbmdzIGZyb20gdGhlIGxvY2FsIGRlc2NyaXB0aW9uXG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHRoaXMubG9jYWxTRFAubWVkaWEubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICBjb2JibGVkICs9IFNEUFV0aWwuZmluZF9saW5lKHRoaXMubG9jYWxTRFAubWVkaWFbaV0sICdtPScpICsgJ1xcclxcbic7XG4gICAgICAgICAgICAgICAgY29iYmxlZCArPSBTRFBVdGlsLmZpbmRfbGluZXModGhpcy5sb2NhbFNEUC5tZWRpYVtpXSwgJ2E9cnRwbWFwOicpLmpvaW4oJ1xcclxcbicpICsgJ1xcclxcbic7XG4gICAgICAgICAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubG9jYWxTRFAubWVkaWFbaV0sICdhPW1pZDonKSkge1xuICAgICAgICAgICAgICAgICAgICBjb2JibGVkICs9IFNEUFV0aWwuZmluZF9saW5lKHRoaXMubG9jYWxTRFAubWVkaWFbaV0sICdhPW1pZDonKSArICdcXHJcXG4nO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBjb2JibGVkICs9ICdhPWluYWN0aXZlXFxyXFxuJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMucmVtb3RlU0RQID0gbmV3IFNEUChjb2JibGVkKTtcbiAgICAgICAgfVxuICAgICAgICAvLyB0aGVuIGFkZCB0aGluZ3MgbGlrZSBpY2UgYW5kIGR0bHMgZnJvbSByZW1vdGUgY2FuZGlkYXRlXG4gICAgICAgIGVsZW0uZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHNlbGYucmVtb3RlU0RQLm1lZGlhLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHNlbGYucmVtb3RlU0RQLm1lZGlhW2ldLCAnYT1taWQ6JyArICQodGhpcykuYXR0cignbmFtZScpKSB8fFxuICAgICAgICAgICAgICAgICAgICBzZWxmLnJlbW90ZVNEUC5tZWRpYVtpXS5pbmRleE9mKCdtPScgKyAkKHRoaXMpLmF0dHIoJ25hbWUnKSkgPT09IDApIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCFTRFBVdGlsLmZpbmRfbGluZShzZWxmLnJlbW90ZVNEUC5tZWRpYVtpXSwgJ2E9aWNlLXVmcmFnOicpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgdG1wID0gJCh0aGlzKS5maW5kKCd0cmFuc3BvcnQnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYucmVtb3RlU0RQLm1lZGlhW2ldICs9ICdhPWljZS11ZnJhZzonICsgdG1wLmF0dHIoJ3VmcmFnJykgKyAnXFxyXFxuJztcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYucmVtb3RlU0RQLm1lZGlhW2ldICs9ICdhPWljZS1wd2Q6JyArIHRtcC5hdHRyKCdwd2QnKSArICdcXHJcXG4nO1xuICAgICAgICAgICAgICAgICAgICAgICAgdG1wID0gJCh0aGlzKS5maW5kKCd0cmFuc3BvcnQ+ZmluZ2VycHJpbnQnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICh0bXAubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5yZW1vdGVTRFAubWVkaWFbaV0gKz0gJ2E9ZmluZ2VycHJpbnQ6JyArIHRtcC5hdHRyKCdoYXNoJykgKyAnICcgKyB0bXAudGV4dCgpICsgJ1xcclxcbic7XG4gICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdubyBkdGxzIGZpbmdlcnByaW50ICh3ZWJydGMgaXNzdWUgIzE3MTg/KScpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYucmVtb3RlU0RQLm1lZGlhW2ldICs9ICdhPWNyeXB0bzoxIEFFU19DTV8xMjhfSE1BQ19TSEExXzgwIGlubGluZTpCQUFEQkFBREJBQURCQUFEQkFBREJBQURCQUFEQkFBREJBQURCQUFEXFxyXFxuJztcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgICAgdGhpcy5yZW1vdGVTRFAucmF3ID0gdGhpcy5yZW1vdGVTRFAuc2Vzc2lvbiArIHRoaXMucmVtb3RlU0RQLm1lZGlhLmpvaW4oJycpO1xuXG4gICAgICAgIC8vIHdlIG5lZWQgYSBjb21wbGV0ZSBTRFAgd2l0aCBpY2UtdWZyYWcvaWNlLXB3ZCBpbiBhbGwgcGFydHNcbiAgICAgICAgLy8gdGhpcyBtYWtlcyB0aGUgYXNzdW1wdGlvbiB0aGF0IHRoZSBQUkFOU1dFUiBpcyBjb25zdHJ1Y3RlZCBzdWNoIHRoYXQgdGhlIGljZS11ZnJhZyBpcyBpbiBhbGwgbWVkaWFwYXJ0c1xuICAgICAgICAvLyBidXQgaXQgY291bGQgYmUgaW4gdGhlIHNlc3Npb24gcGFydCBhcyB3ZWxsLiBzaW5jZSB0aGUgY29kZSBhYm92ZSBjb25zdHJ1Y3RzIHRoaXMgc2RwIHRoaXMgY2FuJ3QgaGFwcGVuIGhvd2V2ZXJcbiAgICAgICAgdmFyIGlzY29tcGxldGUgPSB0aGlzLnJlbW90ZVNEUC5tZWRpYS5maWx0ZXIoZnVuY3Rpb24gKG1lZGlhcGFydCkge1xuICAgICAgICAgICAgcmV0dXJuIFNEUFV0aWwuZmluZF9saW5lKG1lZGlhcGFydCwgJ2E9aWNlLXVmcmFnOicpO1xuICAgICAgICB9KS5sZW5ndGggPT0gdGhpcy5yZW1vdGVTRFAubWVkaWEubGVuZ3RoO1xuXG4gICAgICAgIGlmIChpc2NvbXBsZXRlKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygnc2V0dGluZyBwcmFuc3dlcicpO1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICB0aGlzLnBlZXJjb25uZWN0aW9uLnNldFJlbW90ZURlc2NyaXB0aW9uKG5ldyBSVENTZXNzaW9uRGVzY3JpcHRpb24oe3R5cGU6ICdwcmFuc3dlcicsIHNkcDogdGhpcy5yZW1vdGVTRFAucmF3IH0pLFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3NldFJlbW90ZURlc2NyaXB0aW9uIHByYW5zd2VyIGZhaWxlZCcsIGUudG9TdHJpbmcoKSk7XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ3NldHRpbmcgcHJhbnN3ZXIgZmFpbGVkJywgZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvL2NvbnNvbGUubG9nKCdub3QgeWV0IHNldHRpbmcgcHJhbnN3ZXInKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICAvLyBvcGVyYXRlIG9uIGVhY2ggY29udGVudCBlbGVtZW50XG4gICAgZWxlbS5lYWNoKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgLy8gd291bGQgbG92ZSB0byBkZWFjdGl2YXRlIHRoaXMsIGJ1dCBmaXJlZm94IHN0aWxsIHJlcXVpcmVzIGl0XG4gICAgICAgIHZhciBpZHggPSAtMTtcbiAgICAgICAgdmFyIGk7XG4gICAgICAgIGZvciAoaSA9IDA7IGkgPCBzZWxmLnJlbW90ZVNEUC5tZWRpYS5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHNlbGYucmVtb3RlU0RQLm1lZGlhW2ldLCAnYT1taWQ6JyArICQodGhpcykuYXR0cignbmFtZScpKSB8fFxuICAgICAgICAgICAgICAgIHNlbGYucmVtb3RlU0RQLm1lZGlhW2ldLmluZGV4T2YoJ209JyArICQodGhpcykuYXR0cignbmFtZScpKSA9PT0gMCkge1xuICAgICAgICAgICAgICAgIGlkeCA9IGk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGlkeCA9PSAtMSkgeyAvLyBmYWxsIGJhY2sgdG8gbG9jYWxkZXNjcmlwdGlvblxuICAgICAgICAgICAgZm9yIChpID0gMDsgaSA8IHNlbGYubG9jYWxTRFAubWVkaWEubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICBpZiAoU0RQVXRpbC5maW5kX2xpbmUoc2VsZi5sb2NhbFNEUC5tZWRpYVtpXSwgJ2E9bWlkOicgKyAkKHRoaXMpLmF0dHIoJ25hbWUnKSkgfHxcbiAgICAgICAgICAgICAgICAgICAgc2VsZi5sb2NhbFNEUC5tZWRpYVtpXS5pbmRleE9mKCdtPScgKyAkKHRoaXMpLmF0dHIoJ25hbWUnKSkgPT09IDApIHtcbiAgICAgICAgICAgICAgICAgICAgaWR4ID0gaTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHZhciBuYW1lID0gJCh0aGlzKS5hdHRyKCduYW1lJyk7XG4gICAgICAgIC8vIFRPRE86IGNoZWNrIGljZS1wd2QgYW5kIGljZS11ZnJhZz9cbiAgICAgICAgJCh0aGlzKS5maW5kKCd0cmFuc3BvcnQ+Y2FuZGlkYXRlJykuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgbGluZSwgY2FuZGlkYXRlO1xuICAgICAgICAgICAgbGluZSA9IFNEUFV0aWwuY2FuZGlkYXRlRnJvbUppbmdsZSh0aGlzKTtcbiAgICAgICAgICAgIGNhbmRpZGF0ZSA9IG5ldyBSVENJY2VDYW5kaWRhdGUoe3NkcE1MaW5lSW5kZXg6IGlkeCxcbiAgICAgICAgICAgICAgICBzZHBNaWQ6IG5hbWUsXG4gICAgICAgICAgICAgICAgY2FuZGlkYXRlOiBsaW5lfSk7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIHNlbGYucGVlcmNvbm5lY3Rpb24uYWRkSWNlQ2FuZGlkYXRlKGNhbmRpZGF0ZSk7XG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcignYWRkSWNlQ2FuZGlkYXRlIGZhaWxlZCcsIGUudG9TdHJpbmcoKSwgbGluZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH0pO1xufTtcblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuc2VuZEFuc3dlciA9IGZ1bmN0aW9uIChwcm92aXNpb25hbCkge1xuICAgIC8vY29uc29sZS5sb2coJ2NyZWF0ZUFuc3dlcicsIHByb3Zpc2lvbmFsKTtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5jcmVhdGVBbnN3ZXIoXG4gICAgICAgIGZ1bmN0aW9uIChzZHApIHtcbiAgICAgICAgICAgIHNlbGYuY3JlYXRlZEFuc3dlcihzZHAsIHByb3Zpc2lvbmFsKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ2NyZWF0ZUFuc3dlciBmYWlsZWQnLCBlKTtcbiAgICAgICAgfSxcbiAgICAgICAgdGhpcy5tZWRpYV9jb25zdHJhaW50c1xuICAgICk7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5jcmVhdGVkQW5zd2VyID0gZnVuY3Rpb24gKHNkcCwgcHJvdmlzaW9uYWwpIHtcbiAgICAvL2NvbnNvbGUubG9nKCdjcmVhdGVBbnN3ZXIgY2FsbGJhY2snKTtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdGhpcy5sb2NhbFNEUCA9IG5ldyBTRFAoc2RwLnNkcCk7XG4gICAgLy90aGlzLmxvY2FsU0RQLm1hbmdsZSgpO1xuICAgIHRoaXMudXNlcHJhbnN3ZXIgPSBwcm92aXNpb25hbCA9PT0gdHJ1ZTtcbiAgICBpZiAodGhpcy51c2V0cmlja2xlKSB7XG4gICAgICAgIGlmICh0aGlzLnVzZXByYW5zd2VyKSB7XG4gICAgICAgICAgICBzZHAudHlwZSA9ICdwcmFuc3dlcic7XG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHRoaXMubG9jYWxTRFAubWVkaWEubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICB0aGlzLmxvY2FsU0RQLm1lZGlhW2ldID0gdGhpcy5sb2NhbFNEUC5tZWRpYVtpXS5yZXBsYWNlKCdhPXNlbmRyZWN2XFxyXFxuJywgJ2E9aW5hY3RpdmVcXHJcXG4nKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMubG9jYWxTRFAucmF3ID0gdGhpcy5sb2NhbFNEUC5zZXNzaW9uICsgJ1xcclxcbicgKyB0aGlzLmxvY2FsU0RQLm1lZGlhLmpvaW4oJycpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB2YXIgc2VuZEppbmdsZSA9IGZ1bmN0aW9uIChzc3Jjcykge1xuXG4gICAgICAgICAgICAgICAgdmFyIGFjY2VwdCA9ICRpcSh7dG86IHNlbGYucGVlcmppZCxcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3NldCd9KVxuICAgICAgICAgICAgICAgICAgICAuYygnamluZ2xlJywge3htbG5zOiAndXJuOnhtcHA6amluZ2xlOjEnLFxuICAgICAgICAgICAgICAgICAgICAgICAgYWN0aW9uOiAnc2Vzc2lvbi1hY2NlcHQnLFxuICAgICAgICAgICAgICAgICAgICAgICAgaW5pdGlhdG9yOiBzZWxmLmluaXRpYXRvcixcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlc3BvbmRlcjogc2VsZi5yZXNwb25kZXIsXG4gICAgICAgICAgICAgICAgICAgICAgICBzaWQ6IHNlbGYuc2lkIH0pO1xuICAgICAgICAgICAgICAgIHZhciBwdWJsaWNMb2NhbERlc2MgPSBzaW11bGNhc3QucmV2ZXJzZVRyYW5zZm9ybUxvY2FsRGVzY3JpcHRpb24oc2RwKTtcbiAgICAgICAgICAgICAgICB2YXIgcHVibGljTG9jYWxTRFAgPSBuZXcgU0RQKHB1YmxpY0xvY2FsRGVzYy5zZHApO1xuICAgICAgICAgICAgICAgIHB1YmxpY0xvY2FsU0RQLnRvSmluZ2xlKGFjY2VwdCwgc2VsZi5pbml0aWF0b3IgPT0gc2VsZi5tZSA/ICdpbml0aWF0b3InIDogJ3Jlc3BvbmRlcicsIHNzcmNzKTtcbiAgICAgICAgICAgICAgICBzZWxmLmNvbm5lY3Rpb24uc2VuZElRKGFjY2VwdCxcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGFjayA9IHt9O1xuICAgICAgICAgICAgICAgICAgICAgICAgYWNrLnNvdXJjZSA9ICdhbnN3ZXInO1xuICAgICAgICAgICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcignYWNrLmppbmdsZScsIFtzZWxmLnNpZCwgYWNrXSk7XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChzdGFuemEpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBlcnJvciA9ICgkKHN0YW56YSkuZmluZCgnZXJyb3InKS5sZW5ndGgpID8ge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvZGU6ICQoc3RhbnphKS5maW5kKCdlcnJvcicpLmF0dHIoJ2NvZGUnKSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWFzb246ICQoc3RhbnphKS5maW5kKCdlcnJvciA6Zmlyc3QnKVswXS50YWdOYW1lLFxuICAgICAgICAgICAgICAgICAgICAgICAgfTp7fTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGVycm9yLnNvdXJjZSA9ICdhbnN3ZXInO1xuICAgICAgICAgICAgICAgICAgICAgICAgSmluZ2xlU2Vzc2lvbi5vbkppbmdsZUVycm9yKHNlbGYuc2lkLCBlcnJvcik7XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIDEwMDAwKTtcbiAgICB9XG4gICAgc2RwLnNkcCA9IHRoaXMubG9jYWxTRFAucmF3O1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uc2V0TG9jYWxEZXNjcmlwdGlvbihzZHAsXG4gICAgICAgIGZ1bmN0aW9uICgpIHtcblxuICAgICAgICAgICAgLy9jb25zb2xlLmxvZygnc2V0TG9jYWxEZXNjcmlwdGlvbiBzdWNjZXNzJyk7XG4gICAgICAgICAgICBpZiAoc2VsZi51c2V0cmlja2xlICYmICFzZWxmLnVzZXByYW5zd2VyKSB7XG4gICAgICAgICAgICAgICAgc2VuZEppbmdsZSgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgc2VsZi5zZXRMb2NhbERlc2NyaXB0aW9uKCk7XG4gICAgICAgIH0sXG4gICAgICAgIGZ1bmN0aW9uIChlKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKCdzZXRMb2NhbERlc2NyaXB0aW9uIGZhaWxlZCcsIGUpO1xuICAgICAgICB9XG4gICAgKTtcbiAgICB2YXIgY2FuZHMgPSBTRFBVdGlsLmZpbmRfbGluZXModGhpcy5sb2NhbFNEUC5yYXcsICdhPWNhbmRpZGF0ZTonKTtcbiAgICBmb3IgKHZhciBqID0gMDsgaiA8IGNhbmRzLmxlbmd0aDsgaisrKSB7XG4gICAgICAgIHZhciBjYW5kID0gU0RQVXRpbC5wYXJzZV9pY2VjYW5kaWRhdGUoY2FuZHNbal0pO1xuICAgICAgICBpZiAoY2FuZC50eXBlID09ICdzcmZseCcpIHtcbiAgICAgICAgICAgIHRoaXMuaGFkc3R1bmNhbmRpZGF0ZSA9IHRydWU7XG4gICAgICAgIH0gZWxzZSBpZiAoY2FuZC50eXBlID09ICdyZWxheScpIHtcbiAgICAgICAgICAgIHRoaXMuaGFkdHVybmNhbmRpZGF0ZSA9IHRydWU7XG4gICAgICAgIH1cbiAgICB9XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5zZW5kVGVybWluYXRlID0gZnVuY3Rpb24gKHJlYXNvbiwgdGV4dCkge1xuICAgIHZhciBzZWxmID0gdGhpcyxcbiAgICAgICAgdGVybSA9ICRpcSh7dG86IHRoaXMucGVlcmppZCxcbiAgICAgICAgICAgIHR5cGU6ICdzZXQnfSlcbiAgICAgICAgICAgIC5jKCdqaW5nbGUnLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6MScsXG4gICAgICAgICAgICAgICAgYWN0aW9uOiAnc2Vzc2lvbi10ZXJtaW5hdGUnLFxuICAgICAgICAgICAgICAgIGluaXRpYXRvcjogdGhpcy5pbml0aWF0b3IsXG4gICAgICAgICAgICAgICAgc2lkOiB0aGlzLnNpZH0pXG4gICAgICAgICAgICAuYygncmVhc29uJylcbiAgICAgICAgICAgIC5jKHJlYXNvbiB8fCAnc3VjY2VzcycpO1xuXG4gICAgaWYgKHRleHQpIHtcbiAgICAgICAgdGVybS51cCgpLmMoJ3RleHQnKS50KHRleHQpO1xuICAgIH1cblxuICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kSVEodGVybSxcbiAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgc2VsZi5wZWVyY29ubmVjdGlvbi5jbG9zZSgpO1xuICAgICAgICAgICAgc2VsZi5wZWVyY29ubmVjdGlvbiA9IG51bGw7XG4gICAgICAgICAgICBzZWxmLnRlcm1pbmF0ZSgpO1xuICAgICAgICAgICAgdmFyIGFjayA9IHt9O1xuICAgICAgICAgICAgYWNrLnNvdXJjZSA9ICd0ZXJtaW5hdGUnO1xuICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcignYWNrLmppbmdsZScsIFtzZWxmLnNpZCwgYWNrXSk7XG4gICAgICAgIH0sXG4gICAgICAgIGZ1bmN0aW9uIChzdGFuemEpIHtcbiAgICAgICAgICAgIHZhciBlcnJvciA9ICgkKHN0YW56YSkuZmluZCgnZXJyb3InKS5sZW5ndGgpID8ge1xuICAgICAgICAgICAgICAgIGNvZGU6ICQoc3RhbnphKS5maW5kKCdlcnJvcicpLmF0dHIoJ2NvZGUnKSxcbiAgICAgICAgICAgICAgICByZWFzb246ICQoc3RhbnphKS5maW5kKCdlcnJvciA6Zmlyc3QnKVswXS50YWdOYW1lLFxuICAgICAgICAgICAgfTp7fTtcbiAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ2Fjay5qaW5nbGUnLCBbc2VsZi5zaWQsIGVycm9yXSk7XG4gICAgICAgIH0sXG4gICAgICAgIDEwMDAwKTtcbiAgICBpZiAodGhpcy5zdGF0c2ludGVydmFsICE9PSBudWxsKSB7XG4gICAgICAgIHdpbmRvdy5jbGVhckludGVydmFsKHRoaXMuc3RhdHNpbnRlcnZhbCk7XG4gICAgICAgIHRoaXMuc3RhdHNpbnRlcnZhbCA9IG51bGw7XG4gICAgfVxufTtcblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuYWRkU291cmNlID0gZnVuY3Rpb24gKGVsZW0sIGZyb21KaWQpIHtcblxuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAvLyBGSVhNRTogZGlydHkgd2FpdGluZ1xuICAgIGlmICghdGhpcy5wZWVyY29ubmVjdGlvbi5sb2NhbERlc2NyaXB0aW9uKVxuICAgIHtcbiAgICAgICAgY29uc29sZS53YXJuKFwiYWRkU291cmNlIC0gbG9jYWxEZXNjcmlwdGlvbiBub3QgcmVhZHkgeWV0XCIpXG4gICAgICAgIHNldFRpbWVvdXQoZnVuY3Rpb24oKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHNlbGYuYWRkU291cmNlKGVsZW0sIGZyb21KaWQpO1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIDIwMFxuICAgICAgICApO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc29sZS5sb2coJ2FkZHNzcmMnLCBuZXcgRGF0ZSgpLmdldFRpbWUoKSk7XG4gICAgY29uc29sZS5sb2coJ2ljZScsIHRoaXMucGVlcmNvbm5lY3Rpb24uaWNlQ29ubmVjdGlvblN0YXRlKTtcbiAgICB2YXIgc2RwID0gbmV3IFNEUCh0aGlzLnBlZXJjb25uZWN0aW9uLnJlbW90ZURlc2NyaXB0aW9uLnNkcCk7XG4gICAgdmFyIG15U2RwID0gbmV3IFNEUCh0aGlzLnBlZXJjb25uZWN0aW9uLmxvY2FsRGVzY3JpcHRpb24uc2RwKTtcblxuICAgICQoZWxlbSkuZWFjaChmdW5jdGlvbiAoaWR4LCBjb250ZW50KSB7XG4gICAgICAgIHZhciBuYW1lID0gJChjb250ZW50KS5hdHRyKCduYW1lJyk7XG4gICAgICAgIHZhciBsaW5lcyA9ICcnO1xuICAgICAgICB0bXAgPSAkKGNvbnRlbnQpLmZpbmQoJ3NzcmMtZ3JvdXBbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6c3NtYTowXCJdJykuZWFjaChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHZhciBzZW1hbnRpY3MgPSB0aGlzLmdldEF0dHJpYnV0ZSgnc2VtYW50aWNzJyk7XG4gICAgICAgICAgICB2YXIgc3NyY3MgPSAkKHRoaXMpLmZpbmQoJz5zb3VyY2UnKS5tYXAoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLmdldEF0dHJpYnV0ZSgnc3NyYycpO1xuICAgICAgICAgICAgfSkuZ2V0KCk7XG5cbiAgICAgICAgICAgIGlmIChzc3Jjcy5sZW5ndGggIT0gMCkge1xuICAgICAgICAgICAgICAgIGxpbmVzICs9ICdhPXNzcmMtZ3JvdXA6JyArIHNlbWFudGljcyArICcgJyArIHNzcmNzLmpvaW4oJyAnKSArICdcXHJcXG4nO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgICAgdG1wID0gJChjb250ZW50KS5maW5kKCdzb3VyY2VbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6c3NtYTowXCJdJyk7IC8vIGNhbiBoYW5kbGUgYm90aCA+c291cmNlIGFuZCA+ZGVzY3JpcHRpb24+c291cmNlXG4gICAgICAgIHRtcC5lYWNoKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHZhciBzc3JjID0gJCh0aGlzKS5hdHRyKCdzc3JjJyk7XG4gICAgICAgICAgICBpZihteVNkcC5jb250YWluc1NTUkMoc3NyYykpe1xuICAgICAgICAgICAgICAgIC8qKlxuICAgICAgICAgICAgICAgICAqIFRoaXMgaGFwcGVucyB3aGVuIG11bHRpcGxlIHBhcnRpY2lwYW50cyBjaGFuZ2UgdGhlaXIgc3RyZWFtcyBhdCB0aGUgc2FtZSB0aW1lIGFuZFxuICAgICAgICAgICAgICAgICAqIENvbGlicmlGb2N1cy5tb2RpZnlTb3VyY2VzIGhhdmUgdG8gd2FpdCBmb3Igc3RhYmxlIHN0YXRlLiBJbiB0aGUgbWVhbnRpbWUgbXVsdGlwbGVcbiAgICAgICAgICAgICAgICAgKiBhZGRzc3JjIGFyZSBzY2hlZHVsZWQgZm9yIHVwZGF0ZSBJUS4gU2VlXG4gICAgICAgICAgICAgICAgICovXG4gICAgICAgICAgICAgICAgY29uc29sZS53YXJuKFwiR290IGFkZCBzdHJlYW0gcmVxdWVzdCBmb3IgbXkgb3duIHNzcmM6IFwiK3NzcmMpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgICQodGhpcykuZmluZCgnPnBhcmFtZXRlcicpLmVhY2goZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIGxpbmVzICs9ICdhPXNzcmM6JyArIHNzcmMgKyAnICcgKyAkKHRoaXMpLmF0dHIoJ25hbWUnKTtcbiAgICAgICAgICAgICAgICBpZiAoJCh0aGlzKS5hdHRyKCd2YWx1ZScpICYmICQodGhpcykuYXR0cigndmFsdWUnKS5sZW5ndGgpXG4gICAgICAgICAgICAgICAgICAgIGxpbmVzICs9ICc6JyArICQodGhpcykuYXR0cigndmFsdWUnKTtcbiAgICAgICAgICAgICAgICBsaW5lcyArPSAnXFxyXFxuJztcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICAgICAgc2RwLm1lZGlhLmZvckVhY2goZnVuY3Rpb24obWVkaWEsIGlkeCkge1xuICAgICAgICAgICAgaWYgKCFTRFBVdGlsLmZpbmRfbGluZShtZWRpYSwgJ2E9bWlkOicgKyBuYW1lKSlcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICBzZHAubWVkaWFbaWR4XSArPSBsaW5lcztcbiAgICAgICAgICAgIGlmICghc2VsZi5hZGRzc3JjW2lkeF0pIHNlbGYuYWRkc3NyY1tpZHhdID0gJyc7XG4gICAgICAgICAgICBzZWxmLmFkZHNzcmNbaWR4XSArPSBsaW5lcztcbiAgICAgICAgfSk7XG4gICAgICAgIHNkcC5yYXcgPSBzZHAuc2Vzc2lvbiArIHNkcC5tZWRpYS5qb2luKCcnKTtcbiAgICB9KTtcbiAgICB0aGlzLm1vZGlmeVNvdXJjZXMoKTtcbn07XG5cbkppbmdsZVNlc3Npb24ucHJvdG90eXBlLnJlbW92ZVNvdXJjZSA9IGZ1bmN0aW9uIChlbGVtLCBmcm9tSmlkKSB7XG5cbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgLy8gRklYTUU6IGRpcnR5IHdhaXRpbmdcbiAgICBpZiAoIXRoaXMucGVlcmNvbm5lY3Rpb24ubG9jYWxEZXNjcmlwdGlvbilcbiAgICB7XG4gICAgICAgIGNvbnNvbGUud2FybihcInJlbW92ZVNvdXJjZSAtIGxvY2FsRGVzY3JpcHRpb24gbm90IHJlYWR5IHlldFwiKVxuICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKClcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBzZWxmLnJlbW92ZVNvdXJjZShlbGVtLCBmcm9tSmlkKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAyMDBcbiAgICAgICAgKTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnNvbGUubG9nKCdyZW1vdmVzc3JjJywgbmV3IERhdGUoKS5nZXRUaW1lKCkpO1xuICAgIGNvbnNvbGUubG9nKCdpY2UnLCB0aGlzLnBlZXJjb25uZWN0aW9uLmljZUNvbm5lY3Rpb25TdGF0ZSk7XG4gICAgdmFyIHNkcCA9IG5ldyBTRFAodGhpcy5wZWVyY29ubmVjdGlvbi5yZW1vdGVEZXNjcmlwdGlvbi5zZHApO1xuICAgIHZhciBteVNkcCA9IG5ldyBTRFAodGhpcy5wZWVyY29ubmVjdGlvbi5sb2NhbERlc2NyaXB0aW9uLnNkcCk7XG5cbiAgICAkKGVsZW0pLmVhY2goZnVuY3Rpb24gKGlkeCwgY29udGVudCkge1xuICAgICAgICB2YXIgbmFtZSA9ICQoY29udGVudCkuYXR0cignbmFtZScpO1xuICAgICAgICB2YXIgbGluZXMgPSAnJztcbiAgICAgICAgdG1wID0gJChjb250ZW50KS5maW5kKCdzc3JjLWdyb3VwW3htbG5zPVwidXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnNzbWE6MFwiXScpLmVhY2goZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICB2YXIgc2VtYW50aWNzID0gdGhpcy5nZXRBdHRyaWJ1dGUoJ3NlbWFudGljcycpO1xuICAgICAgICAgICAgdmFyIHNzcmNzID0gJCh0aGlzKS5maW5kKCc+c291cmNlJykubWFwKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5nZXRBdHRyaWJ1dGUoJ3NzcmMnKTtcbiAgICAgICAgICAgIH0pLmdldCgpO1xuXG4gICAgICAgICAgICBpZiAoc3NyY3MubGVuZ3RoICE9IDApIHtcbiAgICAgICAgICAgICAgICBsaW5lcyArPSAnYT1zc3JjLWdyb3VwOicgKyBzZW1hbnRpY3MgKyAnICcgKyBzc3Jjcy5qb2luKCcgJykgKyAnXFxyXFxuJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICAgIHRtcCA9ICQoY29udGVudCkuZmluZCgnc291cmNlW3htbG5zPVwidXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnNzbWE6MFwiXScpOyAvLyBjYW4gaGFuZGxlIGJvdGggPnNvdXJjZSBhbmQgPmRlc2NyaXB0aW9uPnNvdXJjZVxuICAgICAgICB0bXAuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgc3NyYyA9ICQodGhpcykuYXR0cignc3NyYycpO1xuICAgICAgICAgICAgLy8gVGhpcyBzaG91bGQgbmV2ZXIgaGFwcGVuLCBidXQgY2FuIGJlIHVzZWZ1bCBmb3IgYnVnIGRldGVjdGlvblxuICAgICAgICAgICAgaWYobXlTZHAuY29udGFpbnNTU1JDKHNzcmMpKXtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiR290IHJlbW92ZSBzdHJlYW0gcmVxdWVzdCBmb3IgbXkgb3duIHNzcmM6IFwiK3NzcmMpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgICQodGhpcykuZmluZCgnPnBhcmFtZXRlcicpLmVhY2goZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIGxpbmVzICs9ICdhPXNzcmM6JyArIHNzcmMgKyAnICcgKyAkKHRoaXMpLmF0dHIoJ25hbWUnKTtcbiAgICAgICAgICAgICAgICBpZiAoJCh0aGlzKS5hdHRyKCd2YWx1ZScpICYmICQodGhpcykuYXR0cigndmFsdWUnKS5sZW5ndGgpXG4gICAgICAgICAgICAgICAgICAgIGxpbmVzICs9ICc6JyArICQodGhpcykuYXR0cigndmFsdWUnKTtcbiAgICAgICAgICAgICAgICBsaW5lcyArPSAnXFxyXFxuJztcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICAgICAgc2RwLm1lZGlhLmZvckVhY2goZnVuY3Rpb24obWVkaWEsIGlkeCkge1xuICAgICAgICAgICAgaWYgKCFTRFBVdGlsLmZpbmRfbGluZShtZWRpYSwgJ2E9bWlkOicgKyBuYW1lKSlcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICBzZHAubWVkaWFbaWR4XSArPSBsaW5lcztcbiAgICAgICAgICAgIGlmICghc2VsZi5yZW1vdmVzc3JjW2lkeF0pIHNlbGYucmVtb3Zlc3NyY1tpZHhdID0gJyc7XG4gICAgICAgICAgICBzZWxmLnJlbW92ZXNzcmNbaWR4XSArPSBsaW5lcztcbiAgICAgICAgfSk7XG4gICAgICAgIHNkcC5yYXcgPSBzZHAuc2Vzc2lvbiArIHNkcC5tZWRpYS5qb2luKCcnKTtcbiAgICB9KTtcbiAgICB0aGlzLm1vZGlmeVNvdXJjZXMoKTtcbn07XG5cbkppbmdsZVNlc3Npb24ucHJvdG90eXBlLm1vZGlmeVNvdXJjZXMgPSBmdW5jdGlvbiAoc3VjY2Vzc0NhbGxiYWNrKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIGlmICh0aGlzLnBlZXJjb25uZWN0aW9uLnNpZ25hbGluZ1N0YXRlID09ICdjbG9zZWQnKSByZXR1cm47XG4gICAgaWYgKCEodGhpcy5hZGRzc3JjLmxlbmd0aCB8fCB0aGlzLnJlbW92ZXNzcmMubGVuZ3RoIHx8IHRoaXMucGVuZGluZ29wICE9PSBudWxsIHx8IHRoaXMuc3dpdGNoc3RyZWFtcykpe1xuICAgICAgICAvLyBUaGVyZSBpcyBub3RoaW5nIHRvIGRvIHNpbmNlIHNjaGVkdWxlZCBqb2IgbWlnaHQgaGF2ZSBiZWVuIGV4ZWN1dGVkIGJ5IGFub3RoZXIgc3VjY2VlZGluZyBjYWxsXG4gICAgICAgIHRoaXMuc2V0TG9jYWxEZXNjcmlwdGlvbigpO1xuICAgICAgICBpZihzdWNjZXNzQ2FsbGJhY2spe1xuICAgICAgICAgICAgc3VjY2Vzc0NhbGxiYWNrKCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIEZJWE1FOiB0aGlzIGlzIGEgYmlnIGhhY2tcbiAgICAvLyBodHRwczovL2NvZGUuZ29vZ2xlLmNvbS9wL3dlYnJ0Yy9pc3N1ZXMvZGV0YWlsP2lkPTI2ODhcbiAgICAvLyBeIGhhcyBiZWVuIGZpeGVkLlxuICAgIGlmICghKHRoaXMucGVlcmNvbm5lY3Rpb24uc2lnbmFsaW5nU3RhdGUgPT0gJ3N0YWJsZScgJiYgdGhpcy5wZWVyY29ubmVjdGlvbi5pY2VDb25uZWN0aW9uU3RhdGUgPT0gJ2Nvbm5lY3RlZCcpKSB7XG4gICAgICAgIGNvbnNvbGUud2FybignbW9kaWZ5U291cmNlcyBub3QgeWV0JywgdGhpcy5wZWVyY29ubmVjdGlvbi5zaWduYWxpbmdTdGF0ZSwgdGhpcy5wZWVyY29ubmVjdGlvbi5pY2VDb25uZWN0aW9uU3RhdGUpO1xuICAgICAgICB0aGlzLndhaXQgPSB0cnVlO1xuICAgICAgICB3aW5kb3cuc2V0VGltZW91dChmdW5jdGlvbigpIHsgc2VsZi5tb2RpZnlTb3VyY2VzKHN1Y2Nlc3NDYWxsYmFjayk7IH0sIDI1MCk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKHRoaXMud2FpdCkge1xuICAgICAgICB3aW5kb3cuc2V0VGltZW91dChmdW5jdGlvbigpIHsgc2VsZi5tb2RpZnlTb3VyY2VzKHN1Y2Nlc3NDYWxsYmFjayk7IH0sIDI1MDApO1xuICAgICAgICB0aGlzLndhaXQgPSBmYWxzZTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIFJlc2V0IHN3aXRjaCBzdHJlYW1zIGZsYWdcbiAgICB0aGlzLnN3aXRjaHN0cmVhbXMgPSBmYWxzZTtcblxuICAgIHZhciBzZHAgPSBuZXcgU0RQKHRoaXMucGVlcmNvbm5lY3Rpb24ucmVtb3RlRGVzY3JpcHRpb24uc2RwKTtcblxuICAgIC8vIGFkZCBzb3VyY2VzXG4gICAgdGhpcy5hZGRzc3JjLmZvckVhY2goZnVuY3Rpb24obGluZXMsIGlkeCkge1xuICAgICAgICBzZHAubWVkaWFbaWR4XSArPSBsaW5lcztcbiAgICB9KTtcbiAgICB0aGlzLmFkZHNzcmMgPSBbXTtcblxuICAgIC8vIHJlbW92ZSBzb3VyY2VzXG4gICAgdGhpcy5yZW1vdmVzc3JjLmZvckVhY2goZnVuY3Rpb24obGluZXMsIGlkeCkge1xuICAgICAgICBsaW5lcyA9IGxpbmVzLnNwbGl0KCdcXHJcXG4nKTtcbiAgICAgICAgbGluZXMucG9wKCk7IC8vIHJlbW92ZSBlbXB0eSBsYXN0IGVsZW1lbnQ7XG4gICAgICAgIGxpbmVzLmZvckVhY2goZnVuY3Rpb24obGluZSkge1xuICAgICAgICAgICAgc2RwLm1lZGlhW2lkeF0gPSBzZHAubWVkaWFbaWR4XS5yZXBsYWNlKGxpbmUgKyAnXFxyXFxuJywgJycpO1xuICAgICAgICB9KTtcbiAgICB9KTtcbiAgICB0aGlzLnJlbW92ZXNzcmMgPSBbXTtcblxuICAgIC8vIEZJWE1FOlxuICAgIC8vIHRoaXMgd2FzIGEgaGFjayBmb3IgdGhlIHNpdHVhdGlvbiB3aGVuIG9ubHkgb25lIHBlZXIgZXhpc3RzXG4gICAgLy8gaW4gdGhlIGNvbmZlcmVuY2UuXG4gICAgLy8gY2hlY2sgaWYgc3RpbGwgcmVxdWlyZWQgYW5kIHJlbW92ZVxuICAgIGlmIChzZHAubWVkaWFbMF0pXG4gICAgICAgIHNkcC5tZWRpYVswXSA9IHNkcC5tZWRpYVswXS5yZXBsYWNlKCdhPXJlY3Zvbmx5JywgJ2E9c2VuZHJlY3YnKTtcbiAgICBpZiAoc2RwLm1lZGlhWzFdKVxuICAgICAgICBzZHAubWVkaWFbMV0gPSBzZHAubWVkaWFbMV0ucmVwbGFjZSgnYT1yZWN2b25seScsICdhPXNlbmRyZWN2Jyk7XG5cbiAgICBzZHAucmF3ID0gc2RwLnNlc3Npb24gKyBzZHAubWVkaWEuam9pbignJyk7XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5zZXRSZW1vdGVEZXNjcmlwdGlvbihuZXcgUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKHt0eXBlOiAnb2ZmZXInLCBzZHA6IHNkcC5yYXd9KSxcbiAgICAgICAgZnVuY3Rpb24oKSB7XG5cbiAgICAgICAgICAgIGlmKHNlbGYuc2lnbmFsaW5nU3RhdGUgPT0gJ2Nsb3NlZCcpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiY3JlYXRlQW5zd2VyIGF0dGVtcHQgb24gY2xvc2VkIHN0YXRlXCIpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgc2VsZi5wZWVyY29ubmVjdGlvbi5jcmVhdGVBbnN3ZXIoXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24obW9kaWZpZWRBbnN3ZXIpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gY2hhbmdlIHZpZGVvIGRpcmVjdGlvbiwgc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9qaXRzaS9qaXRtZWV0L2lzc3Vlcy80MVxuICAgICAgICAgICAgICAgICAgICBpZiAoc2VsZi5wZW5kaW5nb3AgIT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBzZHAgPSBuZXcgU0RQKG1vZGlmaWVkQW5zd2VyLnNkcCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoc2RwLm1lZGlhLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzd2l0Y2goc2VsZi5wZW5kaW5nb3ApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAnbXV0ZSc6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZHAubWVkaWFbMV0gPSBzZHAubWVkaWFbMV0ucmVwbGFjZSgnYT1zZW5kcmVjdicsICdhPXJlY3Zvbmx5Jyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAndW5tdXRlJzpcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNkcC5tZWRpYVsxXSA9IHNkcC5tZWRpYVsxXS5yZXBsYWNlKCdhPXJlY3Zvbmx5JywgJ2E9c2VuZHJlY3YnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZHAucmF3ID0gc2RwLnNlc3Npb24gKyBzZHAubWVkaWEuam9pbignJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kaWZpZWRBbnN3ZXIuc2RwID0gc2RwLnJhdztcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYucGVuZGluZ29wID0gbnVsbDtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIC8vIEZJWE1FOiBwdXNoaW5nIGRvd24gYW4gYW5zd2VyIHdoaWxlIGljZSBjb25uZWN0aW9uIHN0YXRlXG4gICAgICAgICAgICAgICAgICAgIC8vIGlzIHN0aWxsIGNoZWNraW5nIGlzIGJhZC4uLlxuICAgICAgICAgICAgICAgICAgICAvL2NvbnNvbGUubG9nKHNlbGYucGVlcmNvbm5lY3Rpb24uaWNlQ29ubmVjdGlvblN0YXRlKTtcblxuICAgICAgICAgICAgICAgICAgICAvLyB0cnlpbmcgdG8gd29yayBhcm91bmQgYW5vdGhlciBjaHJvbWUgYnVnXG4gICAgICAgICAgICAgICAgICAgIC8vbW9kaWZpZWRBbnN3ZXIuc2RwID0gbW9kaWZpZWRBbnN3ZXIuc2RwLnJlcGxhY2UoL2E9c2V0dXA6YWN0aXZlL2csICdhPXNldHVwOmFjdHBhc3MnKTtcbiAgICAgICAgICAgICAgICAgICAgc2VsZi5wZWVyY29ubmVjdGlvbi5zZXRMb2NhbERlc2NyaXB0aW9uKG1vZGlmaWVkQW5zd2VyLFxuICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9jb25zb2xlLmxvZygnbW9kaWZpZWQgc2V0TG9jYWxEZXNjcmlwdGlvbiBvaycpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuc2V0TG9jYWxEZXNjcmlwdGlvbigpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmKHN1Y2Nlc3NDYWxsYmFjayl7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3NDYWxsYmFjaygpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbihlcnJvcikge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ21vZGlmaWVkIHNldExvY2FsRGVzY3JpcHRpb24gZmFpbGVkJywgZXJyb3IpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24oZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcignbW9kaWZpZWQgYW5zd2VyIGZhaWxlZCcsIGVycm9yKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICApO1xuICAgICAgICB9LFxuICAgICAgICBmdW5jdGlvbihlcnJvcikge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcignbW9kaWZ5IGZhaWxlZCcsIGVycm9yKTtcbiAgICAgICAgfVxuICAgICk7XG59O1xuXG4vKipcbiAqIFN3aXRjaGVzIHZpZGVvIHN0cmVhbXMuXG4gKiBAcGFyYW0gbmV3X3N0cmVhbSBuZXcgc3RyZWFtIHRoYXQgd2lsbCBiZSB1c2VkIGFzIHZpZGVvIG9mIHRoaXMgc2Vzc2lvbi5cbiAqIEBwYXJhbSBvbGRTdHJlYW0gb2xkIHZpZGVvIHN0cmVhbSBvZiB0aGlzIHNlc3Npb24uXG4gKiBAcGFyYW0gc3VjY2Vzc19jYWxsYmFjayBjYWxsYmFjayBleGVjdXRlZCBhZnRlciBzdWNjZXNzZnVsIHN0cmVhbSBzd2l0Y2guXG4gKi9cbkppbmdsZVNlc3Npb24ucHJvdG90eXBlLnN3aXRjaFN0cmVhbXMgPSBmdW5jdGlvbiAobmV3X3N0cmVhbSwgb2xkU3RyZWFtLCBzdWNjZXNzX2NhbGxiYWNrKSB7XG5cbiAgICB2YXIgc2VsZiA9IHRoaXM7XG5cbiAgICAvLyBSZW1lbWJlciBTRFAgdG8gZmlndXJlIG91dCBhZGRlZC9yZW1vdmVkIFNTUkNzXG4gICAgdmFyIG9sZFNkcCA9IG51bGw7XG4gICAgaWYoc2VsZi5wZWVyY29ubmVjdGlvbikge1xuICAgICAgICBpZihzZWxmLnBlZXJjb25uZWN0aW9uLmxvY2FsRGVzY3JpcHRpb24pIHtcbiAgICAgICAgICAgIG9sZFNkcCA9IG5ldyBTRFAoc2VsZi5wZWVyY29ubmVjdGlvbi5sb2NhbERlc2NyaXB0aW9uLnNkcCk7XG4gICAgICAgIH1cbiAgICAgICAgc2VsZi5wZWVyY29ubmVjdGlvbi5yZW1vdmVTdHJlYW0ob2xkU3RyZWFtLCB0cnVlKTtcbiAgICAgICAgc2VsZi5wZWVyY29ubmVjdGlvbi5hZGRTdHJlYW0obmV3X3N0cmVhbSk7XG4gICAgfVxuXG4gICAgUlRDLnN3aXRjaFZpZGVvU3RyZWFtcyhuZXdfc3RyZWFtLCBvbGRTdHJlYW0pO1xuXG4gICAgLy8gQ29uZmVyZW5jZSBpcyBub3QgYWN0aXZlXG4gICAgaWYoIW9sZFNkcCB8fCAhc2VsZi5wZWVyY29ubmVjdGlvbikge1xuICAgICAgICBzdWNjZXNzX2NhbGxiYWNrKCk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBzZWxmLnN3aXRjaHN0cmVhbXMgPSB0cnVlO1xuICAgIHNlbGYubW9kaWZ5U291cmNlcyhmdW5jdGlvbigpIHtcbiAgICAgICAgY29uc29sZS5sb2coJ21vZGlmeSBzb3VyY2VzIGRvbmUnKTtcblxuICAgICAgICBzdWNjZXNzX2NhbGxiYWNrKCk7XG5cbiAgICAgICAgdmFyIG5ld1NkcCA9IG5ldyBTRFAoc2VsZi5wZWVyY29ubmVjdGlvbi5sb2NhbERlc2NyaXB0aW9uLnNkcCk7XG4gICAgICAgIGNvbnNvbGUubG9nKFwiU0RQc1wiLCBvbGRTZHAsIG5ld1NkcCk7XG4gICAgICAgIHNlbGYubm90aWZ5TXlTU1JDVXBkYXRlKG9sZFNkcCwgbmV3U2RwKTtcbiAgICB9KTtcbn07XG5cbi8qKlxuICogRmlndXJlcyBvdXQgYWRkZWQvcmVtb3ZlZCBzc3JjcyBhbmQgc2VuZCB1cGRhdGUgSVFzLlxuICogQHBhcmFtIG9sZF9zZHAgU0RQIG9iamVjdCBmb3Igb2xkIGRlc2NyaXB0aW9uLlxuICogQHBhcmFtIG5ld19zZHAgU0RQIG9iamVjdCBmb3IgbmV3IGRlc2NyaXB0aW9uLlxuICovXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5ub3RpZnlNeVNTUkNVcGRhdGUgPSBmdW5jdGlvbiAob2xkX3NkcCwgbmV3X3NkcCkge1xuXG4gICAgaWYgKCEodGhpcy5wZWVyY29ubmVjdGlvbi5zaWduYWxpbmdTdGF0ZSA9PSAnc3RhYmxlJyAmJlxuICAgICAgICB0aGlzLnBlZXJjb25uZWN0aW9uLmljZUNvbm5lY3Rpb25TdGF0ZSA9PSAnY29ubmVjdGVkJykpe1xuICAgICAgICBjb25zb2xlLmxvZyhcIlRvbyBlYXJseSB0byBzZW5kIHVwZGF0ZXNcIik7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBzZW5kIHNvdXJjZS1yZW1vdmUgSVEuXG4gICAgc2RwRGlmZmVyID0gbmV3IFNEUERpZmZlcihuZXdfc2RwLCBvbGRfc2RwKTtcbiAgICB2YXIgcmVtb3ZlID0gJGlxKHt0bzogdGhpcy5wZWVyamlkLCB0eXBlOiAnc2V0J30pXG4gICAgICAgIC5jKCdqaW5nbGUnLCB7XG4gICAgICAgICAgICB4bWxuczogJ3Vybjp4bXBwOmppbmdsZToxJyxcbiAgICAgICAgICAgIGFjdGlvbjogJ3NvdXJjZS1yZW1vdmUnLFxuICAgICAgICAgICAgaW5pdGlhdG9yOiB0aGlzLmluaXRpYXRvcixcbiAgICAgICAgICAgIHNpZDogdGhpcy5zaWRcbiAgICAgICAgfVxuICAgICk7XG4gICAgdmFyIHJlbW92ZWQgPSBzZHBEaWZmZXIudG9KaW5nbGUocmVtb3ZlKTtcbiAgICBpZiAocmVtb3ZlZCkge1xuICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZElRKHJlbW92ZSxcbiAgICAgICAgICAgIGZ1bmN0aW9uIChyZXMpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8oJ2dvdCByZW1vdmUgcmVzdWx0JywgcmVzKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBmdW5jdGlvbiAoZXJyKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcignZ290IHJlbW92ZSBlcnJvcicsIGVycik7XG4gICAgICAgICAgICB9XG4gICAgICAgICk7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgY29uc29sZS5sb2coJ3JlbW92YWwgbm90IG5lY2Vzc2FyeScpO1xuICAgIH1cblxuICAgIC8vIHNlbmQgc291cmNlLWFkZCBJUS5cbiAgICB2YXIgc2RwRGlmZmVyID0gbmV3IFNEUERpZmZlcihvbGRfc2RwLCBuZXdfc2RwKTtcbiAgICB2YXIgYWRkID0gJGlxKHt0bzogdGhpcy5wZWVyamlkLCB0eXBlOiAnc2V0J30pXG4gICAgICAgIC5jKCdqaW5nbGUnLCB7XG4gICAgICAgICAgICB4bWxuczogJ3Vybjp4bXBwOmppbmdsZToxJyxcbiAgICAgICAgICAgIGFjdGlvbjogJ3NvdXJjZS1hZGQnLFxuICAgICAgICAgICAgaW5pdGlhdG9yOiB0aGlzLmluaXRpYXRvcixcbiAgICAgICAgICAgIHNpZDogdGhpcy5zaWRcbiAgICAgICAgfVxuICAgICk7XG4gICAgdmFyIGFkZGVkID0gc2RwRGlmZmVyLnRvSmluZ2xlKGFkZCk7XG4gICAgaWYgKGFkZGVkKSB7XG4gICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kSVEoYWRkLFxuICAgICAgICAgICAgZnVuY3Rpb24gKHJlcykge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuaW5mbygnZ290IGFkZCByZXN1bHQnLCByZXMpO1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGZ1bmN0aW9uIChlcnIpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKCdnb3QgYWRkIGVycm9yJywgZXJyKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgKTtcbiAgICB9IGVsc2Uge1xuICAgICAgICBjb25zb2xlLmxvZygnYWRkaXRpb24gbm90IG5lY2Vzc2FyeScpO1xuICAgIH1cbn07XG5cbi8qKlxuICogRGV0ZXJtaW5lcyB3aGV0aGVyIHRoZSAobG9jYWwpIHZpZGVvIGlzIG11dGUgaS5lLiBhbGwgdmlkZW8gdHJhY2tzIGFyZVxuICogZGlzYWJsZWQuXG4gKlxuICogQHJldHVybiA8dHQ+dHJ1ZTwvdHQ+IGlmIHRoZSAobG9jYWwpIHZpZGVvIGlzIG11dGUgaS5lLiBhbGwgdmlkZW8gdHJhY2tzIGFyZVxuICogZGlzYWJsZWQ7IG90aGVyd2lzZSwgPHR0PmZhbHNlPC90dD5cbiAqL1xuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuaXNWaWRlb011dGUgPSBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIHRyYWNrcyA9IFJUQy5sb2NhbFZpZGVvLmdldFZpZGVvVHJhY2tzKCk7XG4gICAgdmFyIG11dGUgPSB0cnVlO1xuXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCB0cmFja3MubGVuZ3RoOyArK2kpIHtcbiAgICAgICAgaWYgKHRyYWNrc1tpXS5lbmFibGVkKSB7XG4gICAgICAgICAgICBtdXRlID0gZmFsc2U7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gbXV0ZTtcbn07XG5cbi8qKlxuICogTXV0ZXMvdW5tdXRlcyB0aGUgKGxvY2FsKSB2aWRlbyBpLmUuIGVuYWJsZXMvZGlzYWJsZXMgYWxsIHZpZGVvIHRyYWNrcy5cbiAqXG4gKiBAcGFyYW0gbXV0ZSA8dHQ+dHJ1ZTwvdHQ+IHRvIG11dGUgdGhlIChsb2NhbCkgdmlkZW8gaS5lLiB0byBkaXNhYmxlIGFsbCB2aWRlb1xuICogdHJhY2tzOyBvdGhlcndpc2UsIDx0dD5mYWxzZTwvdHQ+XG4gKiBAcGFyYW0gY2FsbGJhY2sgYSBmdW5jdGlvbiB0byBiZSBpbnZva2VkIHdpdGggPHR0Pm11dGU8L3R0PiBhZnRlciBhbGwgdmlkZW9cbiAqIHRyYWNrcyBoYXZlIGJlZW4gZW5hYmxlZC9kaXNhYmxlZC4gVGhlIGZ1bmN0aW9uIG1heSwgb3B0aW9uYWxseSwgcmV0dXJuXG4gKiBhbm90aGVyIGZ1bmN0aW9uIHdoaWNoIGlzIHRvIGJlIGludm9rZWQgYWZ0ZXIgdGhlIHdob2xlIG11dGUvdW5tdXRlIG9wZXJhdGlvblxuICogaGFzIGNvbXBsZXRlZCBzdWNjZXNzZnVsbHkuXG4gKiBAcGFyYW0gb3B0aW9ucyBhbiBvYmplY3Qgd2hpY2ggc3BlY2lmaWVzIG9wdGlvbmFsIGFyZ3VtZW50cyBzdWNoIGFzIHRoZVxuICogPHR0PmJvb2xlYW48L3R0PiBrZXkgPHR0PmJ5VXNlcjwvdHQ+IHdpdGggZGVmYXVsdCB2YWx1ZSA8dHQ+dHJ1ZTwvdHQ+IHdoaWNoXG4gKiBzcGVjaWZpZXMgd2hldGhlciB0aGUgbWV0aG9kIHdhcyBpbml0aWF0ZWQgaW4gcmVzcG9uc2UgdG8gYSB1c2VyIGNvbW1hbmQgKGluXG4gKiBjb250cmFzdCB0byBhbiBhdXRvbWF0aWMgZGVjaXNpb24gbWFkZSBieSB0aGUgYXBwbGljYXRpb24gbG9naWMpXG4gKi9cbkppbmdsZVNlc3Npb24ucHJvdG90eXBlLnNldFZpZGVvTXV0ZSA9IGZ1bmN0aW9uIChtdXRlLCBjYWxsYmFjaywgb3B0aW9ucykge1xuICAgIHZhciBieVVzZXI7XG5cbiAgICBpZiAob3B0aW9ucykge1xuICAgICAgICBieVVzZXIgPSBvcHRpb25zLmJ5VXNlcjtcbiAgICAgICAgaWYgKHR5cGVvZiBieVVzZXIgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgICAgICBieVVzZXIgPSB0cnVlO1xuICAgICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgICAgYnlVc2VyID0gdHJ1ZTtcbiAgICB9XG4gICAgLy8gVGhlIHVzZXIncyBjb21tYW5kIHRvIG11dGUgdGhlIChsb2NhbCkgdmlkZW8gdGFrZXMgcHJlY2VkZW5jZSBvdmVyIGFueVxuICAgIC8vIGF1dG9tYXRpYyBkZWNpc2lvbiBtYWRlIGJ5IHRoZSBhcHBsaWNhdGlvbiBsb2dpYy5cbiAgICBpZiAoYnlVc2VyKSB7XG4gICAgICAgIHRoaXMudmlkZW9NdXRlQnlVc2VyID0gbXV0ZTtcbiAgICB9IGVsc2UgaWYgKHRoaXMudmlkZW9NdXRlQnlVc2VyKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdmFyIGxvY2FsQ2FsbGJhY2sgPSBmdW5jdGlvbiAobXV0ZSkge1xuICAgICAgICBzZWxmLmNvbm5lY3Rpb24uZW11Yy5hZGRWaWRlb0luZm9Ub1ByZXNlbmNlKG11dGUpO1xuICAgICAgICBzZWxmLmNvbm5lY3Rpb24uZW11Yy5zZW5kUHJlc2VuY2UoKTtcbiAgICAgICAgcmV0dXJuIGNhbGxiYWNrKG11dGUpXG4gICAgfTtcblxuICAgIGlmIChtdXRlID09IFJUQy5sb2NhbFZpZGVvLmlzTXV0ZWQoKSlcbiAgICB7XG4gICAgICAgIC8vIEV2ZW4gaWYgbm8gY2hhbmdlIG9jY3VycywgdGhlIHNwZWNpZmllZCBjYWxsYmFjayBpcyB0byBiZSBleGVjdXRlZC5cbiAgICAgICAgLy8gVGhlIHNwZWNpZmllZCBjYWxsYmFjayBtYXksIG9wdGlvbmFsbHksIHJldHVybiBhIHN1Y2Nlc3NDYWxsYmFja1xuICAgICAgICAvLyB3aGljaCBpcyB0byBiZSBleGVjdXRlZCBhcyB3ZWxsLlxuICAgICAgICB2YXIgc3VjY2Vzc0NhbGxiYWNrID0gbG9jYWxDYWxsYmFjayhtdXRlKTtcblxuICAgICAgICBpZiAoc3VjY2Vzc0NhbGxiYWNrKSB7XG4gICAgICAgICAgICBzdWNjZXNzQ2FsbGJhY2soKTtcbiAgICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICAgIFJUQy5sb2NhbFZpZGVvLnNldE11dGUoIW11dGUpO1xuXG4gICAgICAgIHRoaXMuaGFyZE11dGVWaWRlbyhtdXRlKTtcblxuICAgICAgICB0aGlzLm1vZGlmeVNvdXJjZXMobG9jYWxDYWxsYmFjayhtdXRlKSk7XG4gICAgfVxufTtcblxuLy8gU0RQLWJhc2VkIG11dGUgYnkgZ29pbmcgcmVjdm9ubHkvc2VuZHJlY3Zcbi8vIEZJWE1FOiBzaG91bGQgcHJvYmFibHkgYmxhY2sgb3V0IHRoZSBzY3JlZW4gYXMgd2VsbFxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUudG9nZ2xlVmlkZW9NdXRlID0gZnVuY3Rpb24gKGNhbGxiYWNrKSB7XG4gICAgdGhpcy5zZXJ2aWNlLnNldFZpZGVvTXV0ZShSVEMubG9jYWxWaWRlby5pc011dGVkKCksIGNhbGxiYWNrKTtcbn07XG5cbkppbmdsZVNlc3Npb24ucHJvdG90eXBlLmhhcmRNdXRlVmlkZW8gPSBmdW5jdGlvbiAobXV0ZWQpIHtcbiAgICB0aGlzLnBlbmRpbmdvcCA9IG11dGVkID8gJ211dGUnIDogJ3VubXV0ZSc7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5zZW5kTXV0ZSA9IGZ1bmN0aW9uIChtdXRlZCwgY29udGVudCkge1xuICAgIHZhciBpbmZvID0gJGlxKHt0bzogdGhpcy5wZWVyamlkLFxuICAgICAgICB0eXBlOiAnc2V0J30pXG4gICAgICAgIC5jKCdqaW5nbGUnLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6MScsXG4gICAgICAgICAgICBhY3Rpb246ICdzZXNzaW9uLWluZm8nLFxuICAgICAgICAgICAgaW5pdGlhdG9yOiB0aGlzLmluaXRpYXRvcixcbiAgICAgICAgICAgIHNpZDogdGhpcy5zaWQgfSk7XG4gICAgaW5mby5jKG11dGVkID8gJ211dGUnIDogJ3VubXV0ZScsIHt4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDppbmZvOjEnfSk7XG4gICAgaW5mby5hdHRycyh7J2NyZWF0b3InOiB0aGlzLm1lID09IHRoaXMuaW5pdGlhdG9yID8gJ2NyZWF0b3InIDogJ3Jlc3BvbmRlcid9KTtcbiAgICBpZiAoY29udGVudCkge1xuICAgICAgICBpbmZvLmF0dHJzKHsnbmFtZSc6IGNvbnRlbnR9KTtcbiAgICB9XG4gICAgdGhpcy5jb25uZWN0aW9uLnNlbmQoaW5mbyk7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5zZW5kUmluZ2luZyA9IGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgaW5mbyA9ICRpcSh7dG86IHRoaXMucGVlcmppZCxcbiAgICAgICAgdHlwZTogJ3NldCd9KVxuICAgICAgICAuYygnamluZ2xlJywge3htbG5zOiAndXJuOnhtcHA6amluZ2xlOjEnLFxuICAgICAgICAgICAgYWN0aW9uOiAnc2Vzc2lvbi1pbmZvJyxcbiAgICAgICAgICAgIGluaXRpYXRvcjogdGhpcy5pbml0aWF0b3IsXG4gICAgICAgICAgICBzaWQ6IHRoaXMuc2lkIH0pO1xuICAgIGluZm8uYygncmluZ2luZycsIHt4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDppbmZvOjEnfSk7XG4gICAgdGhpcy5jb25uZWN0aW9uLnNlbmQoaW5mbyk7XG59O1xuXG5KaW5nbGVTZXNzaW9uLnByb3RvdHlwZS5nZXRTdGF0cyA9IGZ1bmN0aW9uIChpbnRlcnZhbCkge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB2YXIgcmVjdiA9IHthdWRpbzogMCwgdmlkZW86IDB9O1xuICAgIHZhciBsb3N0ID0ge2F1ZGlvOiAwLCB2aWRlbzogMH07XG4gICAgdmFyIGxhc3RyZWN2ID0ge2F1ZGlvOiAwLCB2aWRlbzogMH07XG4gICAgdmFyIGxhc3Rsb3N0ID0ge2F1ZGlvOiAwLCB2aWRlbzogMH07XG4gICAgdmFyIGxvc3MgPSB7YXVkaW86IDAsIHZpZGVvOiAwfTtcbiAgICB2YXIgZGVsdGEgPSB7YXVkaW86IDAsIHZpZGVvOiAwfTtcbiAgICB0aGlzLnN0YXRzaW50ZXJ2YWwgPSB3aW5kb3cuc2V0SW50ZXJ2YWwoZnVuY3Rpb24gKCkge1xuICAgICAgICBpZiAoc2VsZiAmJiBzZWxmLnBlZXJjb25uZWN0aW9uICYmIHNlbGYucGVlcmNvbm5lY3Rpb24uZ2V0U3RhdHMpIHtcbiAgICAgICAgICAgIHNlbGYucGVlcmNvbm5lY3Rpb24uZ2V0U3RhdHMoZnVuY3Rpb24gKHN0YXRzKSB7XG4gICAgICAgICAgICAgICAgdmFyIHJlc3VsdHMgPSBzdGF0cy5yZXN1bHQoKTtcbiAgICAgICAgICAgICAgICAvLyBUT0RPOiB0aGVyZSBhcmUgc28gbXVjaCBzdGF0aXN0aWNzIHlvdSBjYW4gZ2V0IGZyb20gdGhpcy4uXG4gICAgICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCByZXN1bHRzLmxlbmd0aDsgKytpKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChyZXN1bHRzW2ldLnR5cGUgPT0gJ3NzcmMnKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgcGFja2V0c3JlY3YgPSByZXN1bHRzW2ldLnN0YXQoJ3BhY2tldHNSZWNlaXZlZCcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIHBhY2tldHNsb3N0ID0gcmVzdWx0c1tpXS5zdGF0KCdwYWNrZXRzTG9zdCcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHBhY2tldHNyZWN2ICYmIHBhY2tldHNsb3N0KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFja2V0c3JlY3YgPSBwYXJzZUludChwYWNrZXRzcmVjdiwgMTApO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhY2tldHNsb3N0ID0gcGFyc2VJbnQocGFja2V0c2xvc3QsIDEwKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChyZXN1bHRzW2ldLnN0YXQoJ2dvb2dGcmFtZVJhdGVSZWNlaXZlZCcpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhc3Rsb3N0LnZpZGVvID0gbG9zdC52aWRlbztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFzdHJlY3YudmlkZW8gPSByZWN2LnZpZGVvO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWN2LnZpZGVvID0gcGFja2V0c3JlY3Y7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvc3QudmlkZW8gPSBwYWNrZXRzbG9zdDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXN0bG9zdC5hdWRpbyA9IGxvc3QuYXVkaW87XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhc3RyZWN2LmF1ZGlvID0gcmVjdi5hdWRpbztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVjdi5hdWRpbyA9IHBhY2tldHNyZWN2O1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb3N0LmF1ZGlvID0gcGFja2V0c2xvc3Q7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGRlbHRhLmF1ZGlvID0gcmVjdi5hdWRpbyAtIGxhc3RyZWN2LmF1ZGlvO1xuICAgICAgICAgICAgICAgIGRlbHRhLnZpZGVvID0gcmVjdi52aWRlbyAtIGxhc3RyZWN2LnZpZGVvO1xuICAgICAgICAgICAgICAgIGxvc3MuYXVkaW8gPSAoZGVsdGEuYXVkaW8gPiAwKSA/IE1hdGguY2VpbCgxMDAgKiAobG9zdC5hdWRpbyAtIGxhc3Rsb3N0LmF1ZGlvKSAvIGRlbHRhLmF1ZGlvKSA6IDA7XG4gICAgICAgICAgICAgICAgbG9zcy52aWRlbyA9IChkZWx0YS52aWRlbyA+IDApID8gTWF0aC5jZWlsKDEwMCAqIChsb3N0LnZpZGVvIC0gbGFzdGxvc3QudmlkZW8pIC8gZGVsdGEudmlkZW8pIDogMDtcbiAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdwYWNrZXRsb3NzLmppbmdsZScsIFtzZWxmLnNpZCwgbG9zc10pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICB9LCBpbnRlcnZhbCB8fCAzMDAwKTtcbiAgICByZXR1cm4gdGhpcy5zdGF0c2ludGVydmFsO1xufTtcblxuSmluZ2xlU2Vzc2lvbi5vbkppbmdsZUVycm9yID0gZnVuY3Rpb24gKHNlc3Npb24sIGVycm9yKVxue1xuICAgIGNvbnNvbGUuZXJyb3IoXCJKaW5nbGUgZXJyb3JcIiwgZXJyb3IpO1xufVxuXG5KaW5nbGVTZXNzaW9uLm9uSmluZ2xlRmF0YWxFcnJvciA9IGZ1bmN0aW9uIChzZXNzaW9uLCBlcnJvcilcbntcbiAgICB0aGlzLnNlcnZpY2Uuc2Vzc2lvblRlcm1pbmF0ZWQgPSB0cnVlO1xuICAgIGNvbm5lY3Rpb24uZW11Yy5kb0xlYXZlKCk7XG4gICAgVUkubWVzc2FnZUhhbmRsZXIuc2hvd0Vycm9yKCAgXCJTb3JyeVwiLFxuICAgICAgICBcIkludGVybmFsIGFwcGxpY2F0aW9uIGVycm9yW3NldFJlbW90ZURlc2NyaXB0aW9uXVwiKTtcbn1cblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUuc2V0TG9jYWxEZXNjcmlwdGlvbiA9IGZ1bmN0aW9uICgpIHtcbiAgICAvLyBwdXQgb3VyIHNzcmNzIGludG8gcHJlc2VuY2Ugc28gb3RoZXIgY2xpZW50cyBjYW4gaWRlbnRpZnkgb3VyIHN0cmVhbVxuICAgIHZhciBuZXdzc3JjcyA9IFtdO1xuICAgIHZhciBtZWRpYSA9IHNpbXVsY2FzdC5wYXJzZU1lZGlhKHRoaXMucGVlcmNvbm5lY3Rpb24ubG9jYWxEZXNjcmlwdGlvbik7XG4gICAgbWVkaWEuZm9yRWFjaChmdW5jdGlvbiAobWVkaWEpIHtcblxuICAgICAgICBpZihPYmplY3Qua2V5cyhtZWRpYS5zb3VyY2VzKS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAvLyBUT0RPKGdwKSBtYXliZSBleGNsdWRlIEZJRCBzdHJlYW1zP1xuICAgICAgICAgICAgT2JqZWN0LmtleXMobWVkaWEuc291cmNlcykuZm9yRWFjaChmdW5jdGlvbiAoc3NyYykge1xuICAgICAgICAgICAgICAgIG5ld3NzcmNzLnB1c2goe1xuICAgICAgICAgICAgICAgICAgICAnc3NyYyc6IHNzcmMsXG4gICAgICAgICAgICAgICAgICAgICd0eXBlJzogbWVkaWEudHlwZSxcbiAgICAgICAgICAgICAgICAgICAgJ2RpcmVjdGlvbic6IG1lZGlhLmRpcmVjdGlvblxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSBpZih0aGlzLmxvY2FsU3RyZWFtc1NTUkMgJiYgdGhpcy5sb2NhbFN0cmVhbXNTU1JDW21lZGlhLnR5cGVdKVxuICAgICAgICB7XG4gICAgICAgICAgICBuZXdzc3Jjcy5wdXNoKHtcbiAgICAgICAgICAgICAgICAnc3NyYyc6IHRoaXMubG9jYWxTdHJlYW1zU1NSQ1ttZWRpYS50eXBlXSxcbiAgICAgICAgICAgICAgICAndHlwZSc6IG1lZGlhLnR5cGUsXG4gICAgICAgICAgICAgICAgJ2RpcmVjdGlvbic6IG1lZGlhLmRpcmVjdGlvblxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cblxuICAgIH0pO1xuXG4gICAgY29uc29sZS5sb2coJ25ldyBzc3JjcycsIG5ld3NzcmNzKTtcblxuICAgIC8vIEhhdmUgdG8gY2xlYXIgcHJlc2VuY2UgbWFwIHRvIGdldCByaWQgb2YgcmVtb3ZlZCBzdHJlYW1zXG4gICAgdGhpcy5jb25uZWN0aW9uLmVtdWMuY2xlYXJQcmVzZW5jZU1lZGlhKCk7XG5cbiAgICBpZiAobmV3c3NyY3MubGVuZ3RoID4gMCkge1xuICAgICAgICBmb3IgKHZhciBpID0gMTsgaSA8PSBuZXdzc3Jjcy5sZW5ndGg7IGkgKyspIHtcbiAgICAgICAgICAgIC8vIENoYW5nZSB2aWRlbyB0eXBlIHRvIHNjcmVlblxuICAgICAgICAgICAgaWYgKG5ld3NzcmNzW2ktMV0udHlwZSA9PT0gJ3ZpZGVvJyAmJiBkZXNrdG9wc2hhcmluZy5pc1VzaW5nU2NyZWVuU3RyZWFtKCkpIHtcbiAgICAgICAgICAgICAgICBuZXdzc3Jjc1tpLTFdLnR5cGUgPSAnc2NyZWVuJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5lbXVjLmFkZE1lZGlhVG9QcmVzZW5jZShpLFxuICAgICAgICAgICAgICAgIG5ld3NzcmNzW2ktMV0udHlwZSwgbmV3c3NyY3NbaS0xXS5zc3JjLCBuZXdzc3Jjc1tpLTFdLmRpcmVjdGlvbik7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmNvbm5lY3Rpb24uZW11Yy5zZW5kUHJlc2VuY2UoKTtcbiAgICB9XG59XG5cbi8vIGFuIGF0dGVtcHQgdG8gd29yayBhcm91bmQgaHR0cHM6Ly9naXRodWIuY29tL2ppdHNpL2ppdG1lZXQvaXNzdWVzLzMyXG5mdW5jdGlvbiBzZW5kS2V5ZnJhbWUocGMpIHtcbiAgICBjb25zb2xlLmxvZygnc2VuZGtleWZyYW1lJywgcGMuaWNlQ29ubmVjdGlvblN0YXRlKTtcbiAgICBpZiAocGMuaWNlQ29ubmVjdGlvblN0YXRlICE9PSAnY29ubmVjdGVkJykgcmV0dXJuOyAvLyBzYWZlLi4uXG4gICAgcGMuc2V0UmVtb3RlRGVzY3JpcHRpb24oXG4gICAgICAgIHBjLnJlbW90ZURlc2NyaXB0aW9uLFxuICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBwYy5jcmVhdGVBbnN3ZXIoXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKG1vZGlmaWVkQW5zd2VyKSB7XG4gICAgICAgICAgICAgICAgICAgIHBjLnNldExvY2FsRGVzY3JpcHRpb24oXG4gICAgICAgICAgICAgICAgICAgICAgICBtb2RpZmllZEFuc3dlcixcbiAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBub29wXG4gICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3RyaWdnZXJLZXlmcmFtZSBzZXRMb2NhbERlc2NyaXB0aW9uIGZhaWxlZCcsIGVycm9yKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBVSS5tZXNzYWdlSGFuZGxlci5zaG93RXJyb3IoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygndHJpZ2dlcktleWZyYW1lIGNyZWF0ZUFuc3dlciBmYWlsZWQnLCBlcnJvcik7XG4gICAgICAgICAgICAgICAgICAgIFVJLm1lc3NhZ2VIYW5kbGVyLnNob3dFcnJvcigpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICk7XG4gICAgICAgIH0sXG4gICAgICAgIGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ3RyaWdnZXJLZXlmcmFtZSBzZXRSZW1vdGVEZXNjcmlwdGlvbiBmYWlsZWQnLCBlcnJvcik7XG4gICAgICAgICAgICBVSS5tZXNzYWdlSGFuZGxlci5zaG93RXJyb3IoKTtcbiAgICAgICAgfVxuICAgICk7XG59XG5cblxuSmluZ2xlU2Vzc2lvbi5wcm90b3R5cGUucmVtb3RlU3RyZWFtQWRkZWQgPSBmdW5jdGlvbiAoZGF0YSkge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB2YXIgdGhlc3NyYztcblxuICAgIC8vIGxvb2sgdXAgYW4gYXNzb2NpYXRlZCBKSUQgZm9yIGEgc3RyZWFtIGlkXG4gICAgaWYgKGRhdGEuc3RyZWFtLmlkICYmIGRhdGEuc3RyZWFtLmlkLmluZGV4T2YoJ21peGVkbXNsYWJlbCcpID09PSAtMSkge1xuICAgICAgICAvLyBsb29rIG9ubHkgYXQgYT1zc3JjOiBhbmQgX25vdF8gYXQgYT1zc3JjLWdyb3VwOiBsaW5lc1xuXG4gICAgICAgIHZhciBzc3JjbGluZXNcbiAgICAgICAgICAgID0gU0RQVXRpbC5maW5kX2xpbmVzKHRoaXMucGVlcmNvbm5lY3Rpb24ucmVtb3RlRGVzY3JpcHRpb24uc2RwLCAnYT1zc3JjOicpO1xuICAgICAgICBzc3JjbGluZXMgPSBzc3JjbGluZXMuZmlsdGVyKGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgICAgICAvLyBOT1RFKGdwKSBwcmV2aW91c2x5IHdlIGZpbHRlcmVkIG9uIHRoZSBtc2xhYmVsLCBidXQgdGhhdCBwcm9wZXJ0eVxuICAgICAgICAgICAgLy8gaXMgbm90IGFsd2F5cyBwcmVzZW50LlxuICAgICAgICAgICAgLy8gcmV0dXJuIGxpbmUuaW5kZXhPZignbXNsYWJlbDonICsgZGF0YS5zdHJlYW0ubGFiZWwpICE9PSAtMTtcblxuICAgICAgICAgICAgcmV0dXJuICgobGluZS5pbmRleE9mKCdtc2lkOicgKyBkYXRhLnN0cmVhbS5pZCkgIT09IC0xKSk7XG4gICAgICAgIH0pO1xuICAgICAgICBpZiAoc3NyY2xpbmVzLmxlbmd0aCkge1xuICAgICAgICAgICAgdGhlc3NyYyA9IHNzcmNsaW5lc1swXS5zdWJzdHJpbmcoNykuc3BsaXQoJyAnKVswXTtcblxuICAgICAgICAgICAgLy8gV2Ugc2lnbmFsIG91ciBzdHJlYW1zICh0aHJvdWdoIEppbmdsZSB0byB0aGUgZm9jdXMpIGJlZm9yZSB3ZSBzZXRcbiAgICAgICAgICAgIC8vIG91ciBwcmVzZW5jZSAodGhyb3VnaCB3aGljaCBwZWVycyBhc3NvY2lhdGUgcmVtb3RlIHN0cmVhbXMgdG9cbiAgICAgICAgICAgIC8vIGppZHMpLiBTbywgaXQgbWlnaHQgYXJyaXZlIHRoYXQgYSByZW1vdGUgc3RyZWFtIGlzIGFkZGVkIGJ1dFxuICAgICAgICAgICAgLy8gc3NyYzJqaWQgaXMgbm90IHlldCB1cGRhdGVkIGFuZCB0aHVzIGRhdGEucGVlcmppZCBjYW5ub3QgYmVcbiAgICAgICAgICAgIC8vIHN1Y2Nlc3NmdWxseSBzZXQuIEhlcmUgd2Ugd2FpdCBmb3IgdXAgdG8gYSBzZWNvbmQgZm9yIHRoZVxuICAgICAgICAgICAgLy8gcHJlc2VuY2UgdG8gYXJyaXZlLlxuXG4gICAgICAgICAgICBpZiAoIXNzcmMyamlkW3RoZXNzcmNdKSB7XG4gICAgICAgICAgICAgICAgLy8gVE9ETyhncCkgbGltaXQgd2FpdCBkdXJhdGlvbiB0byAxIHNlYy5cbiAgICAgICAgICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKGQpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5yZW1vdGVTdHJlYW1BZGRlZChkKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0oZGF0YSksIDI1MCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBvayB0byBvdmVyd3JpdGUgdGhlIG9uZSBmcm9tIGZvY3VzPyBtaWdodCBzYXZlIHdvcmsgaW4gY29saWJyaS5qc1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ2Fzc29jaWF0ZWQgamlkJywgc3NyYzJqaWRbdGhlc3NyY10sIGRhdGEucGVlcmppZCk7XG4gICAgICAgICAgICBpZiAoc3NyYzJqaWRbdGhlc3NyY10pIHtcbiAgICAgICAgICAgICAgICBkYXRhLnBlZXJqaWQgPSBzc3JjMmppZFt0aGVzc3JjXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8vVE9ETzogdGhpcyBjb2RlIHNob3VsZCBiZSByZW1vdmVkIHdoZW4gZmlyZWZveCBpbXBsZW1lbnQgbXVsdGlzdHJlYW0gc3VwcG9ydFxuICAgIGlmKFJUQy5nZXRCcm93c2VyVHlwZSgpID09IFJUQ0Jyb3dzZXJUeXBlLlJUQ19CUk9XU0VSX0ZJUkVGT1gpXG4gICAge1xuICAgICAgICBpZigobm90UmVjZWl2ZWRTU1JDcy5sZW5ndGggPT0gMCkgfHxcbiAgICAgICAgICAgICFzc3JjMmppZFtub3RSZWNlaXZlZFNTUkNzW25vdFJlY2VpdmVkU1NSQ3MubGVuZ3RoIC0gMV1dKVxuICAgICAgICB7XG4gICAgICAgICAgICAvLyBUT0RPKGdwKSBsaW1pdCB3YWl0IGR1cmF0aW9uIHRvIDEgc2VjLlxuICAgICAgICAgICAgc2V0VGltZW91dChmdW5jdGlvbihkKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICAgICAgICBzZWxmLnJlbW90ZVN0cmVhbUFkZGVkKGQpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0oZGF0YSksIDI1MCk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICB0aGVzc3JjID0gbm90UmVjZWl2ZWRTU1JDcy5wb3AoKTtcbiAgICAgICAgaWYgKHNzcmMyamlkW3RoZXNzcmNdKSB7XG4gICAgICAgICAgICBkYXRhLnBlZXJqaWQgPSBzc3JjMmppZFt0aGVzc3JjXTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIFJUQy5jcmVhdGVSZW1vdGVTdHJlYW0oZGF0YSwgdGhpcy5zaWQsIHRoZXNzcmMpO1xuXG4gICAgdmFyIGlzVmlkZW8gPSBkYXRhLnN0cmVhbS5nZXRWaWRlb1RyYWNrcygpLmxlbmd0aCA+IDA7XG4gICAgLy8gYW4gYXR0ZW1wdCB0byB3b3JrIGFyb3VuZCBodHRwczovL2dpdGh1Yi5jb20vaml0c2kvaml0bWVldC9pc3N1ZXMvMzJcbiAgICBpZiAoaXNWaWRlbyAmJlxuICAgICAgICBkYXRhLnBlZXJqaWQgJiYgdGhpcy5wZWVyamlkID09PSBkYXRhLnBlZXJqaWQgJiZcbiAgICAgICAgZGF0YS5zdHJlYW0uZ2V0VmlkZW9UcmFja3MoKS5sZW5ndGggPT09IDAgJiZcbiAgICAgICAgUlRDLmxvY2FsVmlkZW8uZ2V0VHJhY2tzKCkubGVuZ3RoID4gMCkge1xuICAgICAgICB3aW5kb3cuc2V0VGltZW91dChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBzZW5kS2V5ZnJhbWUoc2VsZi5wZWVyY29ubmVjdGlvbik7XG4gICAgICAgIH0sIDMwMDApO1xuICAgIH1cbn1cblxubW9kdWxlLmV4cG9ydHMgPSBKaW5nbGVTZXNzaW9uOyIsIi8qIGpzaGludCAtVzExNyAqL1xudmFyIFNEUFV0aWwgPSByZXF1aXJlKFwiLi9TRFBVdGlsXCIpO1xuXG4vLyBTRFAgU1RVRkZcbmZ1bmN0aW9uIFNEUChzZHApIHtcbiAgICB0aGlzLm1lZGlhID0gc2RwLnNwbGl0KCdcXHJcXG5tPScpO1xuICAgIGZvciAodmFyIGkgPSAxOyBpIDwgdGhpcy5tZWRpYS5sZW5ndGg7IGkrKykge1xuICAgICAgICB0aGlzLm1lZGlhW2ldID0gJ209JyArIHRoaXMubWVkaWFbaV07XG4gICAgICAgIGlmIChpICE9IHRoaXMubWVkaWEubGVuZ3RoIC0gMSkge1xuICAgICAgICAgICAgdGhpcy5tZWRpYVtpXSArPSAnXFxyXFxuJztcbiAgICAgICAgfVxuICAgIH1cbiAgICB0aGlzLnNlc3Npb24gPSB0aGlzLm1lZGlhLnNoaWZ0KCkgKyAnXFxyXFxuJztcbiAgICB0aGlzLnJhdyA9IHRoaXMuc2Vzc2lvbiArIHRoaXMubWVkaWEuam9pbignJyk7XG59XG4vKipcbiAqIFJldHVybnMgbWFwIG9mIE1lZGlhQ2hhbm5lbCBtYXBwZWQgcGVyIGNoYW5uZWwgaWR4LlxuICovXG5TRFAucHJvdG90eXBlLmdldE1lZGlhU3NyY01hcCA9IGZ1bmN0aW9uKCkge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB2YXIgbWVkaWFfc3NyY3MgPSB7fTtcbiAgICB2YXIgdG1wO1xuICAgIGZvciAodmFyIG1lZGlhaW5kZXggPSAwOyBtZWRpYWluZGV4IDwgc2VsZi5tZWRpYS5sZW5ndGg7IG1lZGlhaW5kZXgrKykge1xuICAgICAgICB0bXAgPSBTRFBVdGlsLmZpbmRfbGluZXMoc2VsZi5tZWRpYVttZWRpYWluZGV4XSwgJ2E9c3NyYzonKTtcbiAgICAgICAgdmFyIG1pZCA9IFNEUFV0aWwucGFyc2VfbWlkKFNEUFV0aWwuZmluZF9saW5lKHNlbGYubWVkaWFbbWVkaWFpbmRleF0sICdhPW1pZDonKSk7XG4gICAgICAgIHZhciBtZWRpYSA9IHtcbiAgICAgICAgICAgIG1lZGlhaW5kZXg6IG1lZGlhaW5kZXgsXG4gICAgICAgICAgICBtaWQ6IG1pZCxcbiAgICAgICAgICAgIHNzcmNzOiB7fSxcbiAgICAgICAgICAgIHNzcmNHcm91cHM6IFtdXG4gICAgICAgIH07XG4gICAgICAgIG1lZGlhX3NzcmNzW21lZGlhaW5kZXhdID0gbWVkaWE7XG4gICAgICAgIHRtcC5mb3JFYWNoKGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgICAgICB2YXIgbGluZXNzcmMgPSBsaW5lLnN1YnN0cmluZyg3KS5zcGxpdCgnICcpWzBdO1xuICAgICAgICAgICAgLy8gYWxsb2NhdGUgbmV3IENoYW5uZWxTc3JjXG4gICAgICAgICAgICBpZighbWVkaWEuc3NyY3NbbGluZXNzcmNdKSB7XG4gICAgICAgICAgICAgICAgbWVkaWEuc3NyY3NbbGluZXNzcmNdID0ge1xuICAgICAgICAgICAgICAgICAgICBzc3JjOiBsaW5lc3NyYyxcbiAgICAgICAgICAgICAgICAgICAgbGluZXM6IFtdXG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIG1lZGlhLnNzcmNzW2xpbmVzc3JjXS5saW5lcy5wdXNoKGxpbmUpO1xuICAgICAgICB9KTtcbiAgICAgICAgdG1wID0gU0RQVXRpbC5maW5kX2xpbmVzKHNlbGYubWVkaWFbbWVkaWFpbmRleF0sICdhPXNzcmMtZ3JvdXA6Jyk7XG4gICAgICAgIHRtcC5mb3JFYWNoKGZ1bmN0aW9uKGxpbmUpe1xuICAgICAgICAgICAgdmFyIHNlbWFudGljcyA9IGxpbmUuc3Vic3RyKDAsIGlkeCkuc3Vic3RyKDEzKTtcbiAgICAgICAgICAgIHZhciBzc3JjcyA9IGxpbmUuc3Vic3RyKDE0ICsgc2VtYW50aWNzLmxlbmd0aCkuc3BsaXQoJyAnKTtcbiAgICAgICAgICAgIGlmIChzc3Jjcy5sZW5ndGggIT0gMCkge1xuICAgICAgICAgICAgICAgIG1lZGlhLnNzcmNHcm91cHMucHVzaCh7XG4gICAgICAgICAgICAgICAgICAgIHNlbWFudGljczogc2VtYW50aWNzLFxuICAgICAgICAgICAgICAgICAgICBzc3Jjczogc3NyY3NcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuICAgIHJldHVybiBtZWRpYV9zc3Jjcztcbn07XG4vKipcbiAqIFJldHVybnMgPHR0PnRydWU8L3R0PiBpZiB0aGlzIFNEUCBjb250YWlucyBnaXZlbiBTU1JDLlxuICogQHBhcmFtIHNzcmMgdGhlIHNzcmMgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gPHR0PnRydWU8L3R0PiBpZiB0aGlzIFNEUCBjb250YWlucyBnaXZlbiBTU1JDLlxuICovXG5TRFAucHJvdG90eXBlLmNvbnRhaW5zU1NSQyA9IGZ1bmN0aW9uKHNzcmMpIHtcbiAgICB2YXIgbWVkaWFzID0gdGhpcy5nZXRNZWRpYVNzcmNNYXAoKTtcbiAgICB2YXIgY29udGFpbnMgPSBmYWxzZTtcbiAgICBPYmplY3Qua2V5cyhtZWRpYXMpLmZvckVhY2goZnVuY3Rpb24obWVkaWFpbmRleCl7XG4gICAgICAgIHZhciBtZWRpYSA9IG1lZGlhc1ttZWRpYWluZGV4XTtcbiAgICAgICAgLy9jb25zb2xlLmxvZyhcIkNoZWNrXCIsIGNoYW5uZWwsIHNzcmMpO1xuICAgICAgICBpZihPYmplY3Qua2V5cyhtZWRpYS5zc3JjcykuaW5kZXhPZihzc3JjKSAhPSAtMSl7XG4gICAgICAgICAgICBjb250YWlucyA9IHRydWU7XG4gICAgICAgIH1cbiAgICB9KTtcbiAgICByZXR1cm4gY29udGFpbnM7XG59O1xuXG5cbi8vIHJlbW92ZSBpU0FDIGFuZCBDTiBmcm9tIFNEUFxuU0RQLnByb3RvdHlwZS5tYW5nbGUgPSBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIGksIGosIG1saW5lLCBsaW5lcywgcnRwbWFwLCBuZXdkZXNjO1xuICAgIGZvciAoaSA9IDA7IGkgPCB0aGlzLm1lZGlhLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGxpbmVzID0gdGhpcy5tZWRpYVtpXS5zcGxpdCgnXFxyXFxuJyk7XG4gICAgICAgIGxpbmVzLnBvcCgpOyAvLyByZW1vdmUgZW1wdHkgbGFzdCBlbGVtZW50XG4gICAgICAgIG1saW5lID0gU0RQVXRpbC5wYXJzZV9tbGluZShsaW5lcy5zaGlmdCgpKTtcbiAgICAgICAgaWYgKG1saW5lLm1lZGlhICE9ICdhdWRpbycpXG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgbmV3ZGVzYyA9ICcnO1xuICAgICAgICBtbGluZS5mbXQubGVuZ3RoID0gMDtcbiAgICAgICAgZm9yIChqID0gMDsgaiA8IGxpbmVzLmxlbmd0aDsgaisrKSB7XG4gICAgICAgICAgICBpZiAobGluZXNbal0uc3Vic3RyKDAsIDkpID09ICdhPXJ0cG1hcDonKSB7XG4gICAgICAgICAgICAgICAgcnRwbWFwID0gU0RQVXRpbC5wYXJzZV9ydHBtYXAobGluZXNbal0pO1xuICAgICAgICAgICAgICAgIGlmIChydHBtYXAubmFtZSA9PSAnQ04nIHx8IHJ0cG1hcC5uYW1lID09ICdJU0FDJylcbiAgICAgICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICAgICAgbWxpbmUuZm10LnB1c2gocnRwbWFwLmlkKTtcbiAgICAgICAgICAgICAgICBuZXdkZXNjICs9IGxpbmVzW2pdICsgJ1xcclxcbic7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIG5ld2Rlc2MgKz0gbGluZXNbal0gKyAnXFxyXFxuJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICB0aGlzLm1lZGlhW2ldID0gU0RQVXRpbC5idWlsZF9tbGluZShtbGluZSkgKyAnXFxyXFxuJztcbiAgICAgICAgdGhpcy5tZWRpYVtpXSArPSBuZXdkZXNjO1xuICAgIH1cbiAgICB0aGlzLnJhdyA9IHRoaXMuc2Vzc2lvbiArIHRoaXMubWVkaWEuam9pbignJyk7XG59O1xuXG4vLyByZW1vdmUgbGluZXMgbWF0Y2hpbmcgcHJlZml4IGZyb20gc2Vzc2lvbiBzZWN0aW9uXG5TRFAucHJvdG90eXBlLnJlbW92ZVNlc3Npb25MaW5lcyA9IGZ1bmN0aW9uKHByZWZpeCkge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB2YXIgbGluZXMgPSBTRFBVdGlsLmZpbmRfbGluZXModGhpcy5zZXNzaW9uLCBwcmVmaXgpO1xuICAgIGxpbmVzLmZvckVhY2goZnVuY3Rpb24obGluZSkge1xuICAgICAgICBzZWxmLnNlc3Npb24gPSBzZWxmLnNlc3Npb24ucmVwbGFjZShsaW5lICsgJ1xcclxcbicsICcnKTtcbiAgICB9KTtcbiAgICB0aGlzLnJhdyA9IHRoaXMuc2Vzc2lvbiArIHRoaXMubWVkaWEuam9pbignJyk7XG4gICAgcmV0dXJuIGxpbmVzO1xufVxuLy8gcmVtb3ZlIGxpbmVzIG1hdGNoaW5nIHByZWZpeCBmcm9tIGEgbWVkaWEgc2VjdGlvbiBzcGVjaWZpZWQgYnkgbWVkaWFpbmRleFxuLy8gVE9ETzogbm9uLW51bWVyaWMgbWVkaWFpbmRleCBjb3VsZCBtYXRjaCBtaWRcblNEUC5wcm90b3R5cGUucmVtb3ZlTWVkaWFMaW5lcyA9IGZ1bmN0aW9uKG1lZGlhaW5kZXgsIHByZWZpeCkge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB2YXIgbGluZXMgPSBTRFBVdGlsLmZpbmRfbGluZXModGhpcy5tZWRpYVttZWRpYWluZGV4XSwgcHJlZml4KTtcbiAgICBsaW5lcy5mb3JFYWNoKGZ1bmN0aW9uKGxpbmUpIHtcbiAgICAgICAgc2VsZi5tZWRpYVttZWRpYWluZGV4XSA9IHNlbGYubWVkaWFbbWVkaWFpbmRleF0ucmVwbGFjZShsaW5lICsgJ1xcclxcbicsICcnKTtcbiAgICB9KTtcbiAgICB0aGlzLnJhdyA9IHRoaXMuc2Vzc2lvbiArIHRoaXMubWVkaWEuam9pbignJyk7XG4gICAgcmV0dXJuIGxpbmVzO1xufVxuXG4vLyBhZGQgY29udGVudCdzIHRvIGEgamluZ2xlIGVsZW1lbnRcblNEUC5wcm90b3R5cGUudG9KaW5nbGUgPSBmdW5jdGlvbiAoZWxlbSwgdGhlY3JlYXRvciwgc3NyY3MpIHtcbi8vICAgIGNvbnNvbGUubG9nKFwiU1NSQ1wiICsgc3NyY3NbXCJhdWRpb1wiXSArIFwiIC0gXCIgKyBzc3Jjc1tcInZpZGVvXCJdKTtcbiAgICB2YXIgaSwgaiwgaywgbWxpbmUsIHNzcmMsIHJ0cG1hcCwgdG1wLCBsaW5lLCBsaW5lcztcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgLy8gbmV3IGJ1bmRsZSBwbGFuXG4gICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMuc2Vzc2lvbiwgJ2E9Z3JvdXA6JykpIHtcbiAgICAgICAgbGluZXMgPSBTRFBVdGlsLmZpbmRfbGluZXModGhpcy5zZXNzaW9uLCAnYT1ncm91cDonKTtcbiAgICAgICAgZm9yIChpID0gMDsgaSA8IGxpbmVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICB0bXAgPSBsaW5lc1tpXS5zcGxpdCgnICcpO1xuICAgICAgICAgICAgdmFyIHNlbWFudGljcyA9IHRtcC5zaGlmdCgpLnN1YnN0cig4KTtcbiAgICAgICAgICAgIGVsZW0uYygnZ3JvdXAnLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6YXBwczpncm91cGluZzowJywgc2VtYW50aWNzOnNlbWFudGljc30pO1xuICAgICAgICAgICAgZm9yIChqID0gMDsgaiA8IHRtcC5sZW5ndGg7IGorKykge1xuICAgICAgICAgICAgICAgIGVsZW0uYygnY29udGVudCcsIHtuYW1lOiB0bXBbal19KS51cCgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICB9XG4gICAgfVxuICAgIGZvciAoaSA9IDA7IGkgPCB0aGlzLm1lZGlhLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIG1saW5lID0gU0RQVXRpbC5wYXJzZV9tbGluZSh0aGlzLm1lZGlhW2ldLnNwbGl0KCdcXHJcXG4nKVswXSk7XG4gICAgICAgIGlmICghKG1saW5lLm1lZGlhID09PSAnYXVkaW8nIHx8XG4gICAgICAgICAgICAgIG1saW5lLm1lZGlhID09PSAndmlkZW8nIHx8XG4gICAgICAgICAgICAgIG1saW5lLm1lZGlhID09PSAnYXBwbGljYXRpb24nKSlcbiAgICAgICAge1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbaV0sICdhPXNzcmM6JykpIHtcbiAgICAgICAgICAgIHNzcmMgPSBTRFBVdGlsLmZpbmRfbGluZSh0aGlzLm1lZGlhW2ldLCAnYT1zc3JjOicpLnN1YnN0cmluZyg3KS5zcGxpdCgnICcpWzBdOyAvLyB0YWtlIHRoZSBmaXJzdFxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaWYoc3NyY3MgJiYgc3NyY3NbbWxpbmUubWVkaWFdKVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHNzcmMgPSBzc3Jjc1ttbGluZS5tZWRpYV07XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAgc3NyYyA9IGZhbHNlO1xuICAgICAgICB9XG5cbiAgICAgICAgZWxlbS5jKCdjb250ZW50Jywge2NyZWF0b3I6IHRoZWNyZWF0b3IsIG5hbWU6IG1saW5lLm1lZGlhfSk7XG4gICAgICAgIGlmIChTRFBVdGlsLmZpbmRfbGluZSh0aGlzLm1lZGlhW2ldLCAnYT1taWQ6JykpIHtcbiAgICAgICAgICAgIC8vIHByZWZlciBpZGVudGlmaWVyIGZyb20gYT1taWQgaWYgcHJlc2VudFxuICAgICAgICAgICAgdmFyIG1pZCA9IFNEUFV0aWwucGFyc2VfbWlkKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbaV0sICdhPW1pZDonKSk7XG4gICAgICAgICAgICBlbGVtLmF0dHJzKHsgbmFtZTogbWlkIH0pO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbaV0sICdhPXJ0cG1hcDonKS5sZW5ndGgpXG4gICAgICAgIHtcbiAgICAgICAgICAgIGVsZW0uYygnZGVzY3JpcHRpb24nLFxuICAgICAgICAgICAgICAgIHt4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDoxJyxcbiAgICAgICAgICAgICAgICAgICAgbWVkaWE6IG1saW5lLm1lZGlhIH0pO1xuICAgICAgICAgICAgaWYgKHNzcmMpIHtcbiAgICAgICAgICAgICAgICBlbGVtLmF0dHJzKHtzc3JjOiBzc3JjfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBmb3IgKGogPSAwOyBqIDwgbWxpbmUuZm10Lmxlbmd0aDsgaisrKSB7XG4gICAgICAgICAgICAgICAgcnRwbWFwID0gU0RQVXRpbC5maW5kX2xpbmUodGhpcy5tZWRpYVtpXSwgJ2E9cnRwbWFwOicgKyBtbGluZS5mbXRbal0pO1xuICAgICAgICAgICAgICAgIGVsZW0uYygncGF5bG9hZC10eXBlJywgU0RQVXRpbC5wYXJzZV9ydHBtYXAocnRwbWFwKSk7XG4gICAgICAgICAgICAgICAgLy8gcHV0IGFueSAnYT1mbXRwOicgKyBtbGluZS5mbXRbal0gbGluZXMgaW50byA8cGFyYW0gbmFtZT1mb28gdmFsdWU9YmFyLz5cbiAgICAgICAgICAgICAgICBpZiAoU0RQVXRpbC5maW5kX2xpbmUodGhpcy5tZWRpYVtpXSwgJ2E9Zm10cDonICsgbWxpbmUuZm10W2pdKSkge1xuICAgICAgICAgICAgICAgICAgICB0bXAgPSBTRFBVdGlsLnBhcnNlX2ZtdHAoU0RQVXRpbC5maW5kX2xpbmUodGhpcy5tZWRpYVtpXSwgJ2E9Zm10cDonICsgbWxpbmUuZm10W2pdKSk7XG4gICAgICAgICAgICAgICAgICAgIGZvciAoayA9IDA7IGsgPCB0bXAubGVuZ3RoOyBrKyspIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYygncGFyYW1ldGVyJywgdG1wW2tdKS51cCgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHRoaXMuUnRjcEZiVG9KaW5nbGUoaSwgZWxlbSwgbWxpbmUuZm10W2pdKTsgLy8gWEVQLTAyOTMgLS0gbWFwIGE9cnRjcC1mYlxuXG4gICAgICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbaV0sICdhPWNyeXB0bzonLCB0aGlzLnNlc3Npb24pKSB7XG4gICAgICAgICAgICAgICAgZWxlbS5jKCdlbmNyeXB0aW9uJywge3JlcXVpcmVkOiAxfSk7XG4gICAgICAgICAgICAgICAgdmFyIGNyeXB0byA9IFNEUFV0aWwuZmluZF9saW5lcyh0aGlzLm1lZGlhW2ldLCAnYT1jcnlwdG86JywgdGhpcy5zZXNzaW9uKTtcbiAgICAgICAgICAgICAgICBjcnlwdG8uZm9yRWFjaChmdW5jdGlvbihsaW5lKSB7XG4gICAgICAgICAgICAgICAgICAgIGVsZW0uYygnY3J5cHRvJywgU0RQVXRpbC5wYXJzZV9jcnlwdG8obGluZSkpLnVwKCk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgZWxlbS51cCgpOyAvLyBlbmQgb2YgZW5jcnlwdGlvblxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoc3NyYykge1xuICAgICAgICAgICAgICAgIC8vIG5ldyBzdHlsZSBtYXBwaW5nXG4gICAgICAgICAgICAgICAgZWxlbS5jKCdzb3VyY2UnLCB7IHNzcmM6IHNzcmMsIHhtbG5zOiAndXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnNzbWE6MCcgfSk7XG4gICAgICAgICAgICAgICAgLy8gRklYTUU6IGdyb3VwIGJ5IHNzcmMgYW5kIHN1cHBvcnQgbXVsdGlwbGUgZGlmZmVyZW50IHNzcmNzXG4gICAgICAgICAgICAgICAgdmFyIHNzcmNsaW5lcyA9IFNEUFV0aWwuZmluZF9saW5lcyh0aGlzLm1lZGlhW2ldLCAnYT1zc3JjOicpO1xuICAgICAgICAgICAgICAgIGlmKHNzcmNsaW5lcy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgIHNzcmNsaW5lcy5mb3JFYWNoKGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZHggPSBsaW5lLmluZGV4T2YoJyAnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBsaW5lc3NyYyA9IGxpbmUuc3Vic3RyKDAsIGlkeCkuc3Vic3RyKDcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGxpbmVzc3JjICE9IHNzcmMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbGVtLnVwKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc3NyYyA9IGxpbmVzc3JjO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYygnc291cmNlJywgeyBzc3JjOiBzc3JjLCB4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpzc21hOjAnIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGt2ID0gbGluZS5zdWJzdHIoaWR4ICsgMSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBlbGVtLmMoJ3BhcmFtZXRlcicpO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGt2LmluZGV4T2YoJzonKSA9PSAtMSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYXR0cnMoeyBuYW1lOiBrdiB9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS5hdHRycyh7IG5hbWU6IGt2LnNwbGl0KCc6JywgMilbMF0gfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS5hdHRycyh7IHZhbHVlOiBrdi5zcGxpdCgnOicsIDIpWzFdIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBlbGVtLnVwKCk7XG4gICAgICAgICAgICAgICAgICAgIGVsZW0uYygnc291cmNlJywgeyBzc3JjOiBzc3JjLCB4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpzc21hOjAnIH0pO1xuICAgICAgICAgICAgICAgICAgICBlbGVtLmMoJ3BhcmFtZXRlcicpO1xuICAgICAgICAgICAgICAgICAgICBlbGVtLmF0dHJzKHtuYW1lOiBcImNuYW1lXCIsIHZhbHVlOk1hdGgucmFuZG9tKCkudG9TdHJpbmcoMzYpLnN1YnN0cmluZyg3KX0pO1xuICAgICAgICAgICAgICAgICAgICBlbGVtLnVwKCk7XG4gICAgICAgICAgICAgICAgICAgIHZhciBtc2lkID0gbnVsbDtcbiAgICAgICAgICAgICAgICAgICAgaWYobWxpbmUubWVkaWEgPT0gXCJhdWRpb1wiKVxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICBtc2lkID0gUlRDLmxvY2FsQXVkaW8uZ2V0SWQoKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG1zaWQgPSBSVEMubG9jYWxWaWRlby5nZXRJZCgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGlmKG1zaWQgIT0gbnVsbClcbiAgICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAgICAgbXNpZCA9IG1zaWQucmVwbGFjZSgvW1xceyxcXH1dL2csXCJcIik7XG4gICAgICAgICAgICAgICAgICAgICAgICBlbGVtLmMoJ3BhcmFtZXRlcicpO1xuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS5hdHRycyh7bmFtZTogXCJtc2lkXCIsIHZhbHVlOm1zaWR9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0udXAoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYygncGFyYW1ldGVyJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICBlbGVtLmF0dHJzKHtuYW1lOiBcIm1zbGFiZWxcIiwgdmFsdWU6bXNpZH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS5jKCdwYXJhbWV0ZXInKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYXR0cnMoe25hbWU6IFwibGFiZWxcIiwgdmFsdWU6bXNpZH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICAgICAgICAgICAgICB9XG5cblxuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIC8vIFhFUC0wMzM5IGhhbmRsZSBzc3JjLWdyb3VwIGF0dHJpYnV0ZXNcbiAgICAgICAgICAgICAgICB2YXIgc3NyY19ncm91cF9saW5lcyA9IFNEUFV0aWwuZmluZF9saW5lcyh0aGlzLm1lZGlhW2ldLCAnYT1zc3JjLWdyb3VwOicpO1xuICAgICAgICAgICAgICAgIHNzcmNfZ3JvdXBfbGluZXMuZm9yRWFjaChmdW5jdGlvbihsaW5lKSB7XG4gICAgICAgICAgICAgICAgICAgIGlkeCA9IGxpbmUuaW5kZXhPZignICcpO1xuICAgICAgICAgICAgICAgICAgICB2YXIgc2VtYW50aWNzID0gbGluZS5zdWJzdHIoMCwgaWR4KS5zdWJzdHIoMTMpO1xuICAgICAgICAgICAgICAgICAgICB2YXIgc3NyY3MgPSBsaW5lLnN1YnN0cigxNCArIHNlbWFudGljcy5sZW5ndGgpLnNwbGl0KCcgJyk7XG4gICAgICAgICAgICAgICAgICAgIGlmIChzc3Jjcy5sZW5ndGggIT0gMCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS5jKCdzc3JjLWdyb3VwJywgeyBzZW1hbnRpY3M6IHNlbWFudGljcywgeG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6c3NtYTowJyB9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNzcmNzLmZvckVhY2goZnVuY3Rpb24oc3NyYykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYygnc291cmNlJywgeyBzc3JjOiBzc3JjIH0pXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC51cCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBlbGVtLnVwKCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbaV0sICdhPXJ0Y3AtbXV4JykpIHtcbiAgICAgICAgICAgICAgICBlbGVtLmMoJ3J0Y3AtbXV4JykudXAoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gWEVQLTAyOTMgLS0gbWFwIGE9cnRjcC1mYjoqXG4gICAgICAgICAgICB0aGlzLlJ0Y3BGYlRvSmluZ2xlKGksIGVsZW0sICcqJyk7XG5cbiAgICAgICAgICAgIC8vIFhFUC0wMjk0XG4gICAgICAgICAgICBpZiAoU0RQVXRpbC5maW5kX2xpbmUodGhpcy5tZWRpYVtpXSwgJ2E9ZXh0bWFwOicpKSB7XG4gICAgICAgICAgICAgICAgbGluZXMgPSBTRFBVdGlsLmZpbmRfbGluZXModGhpcy5tZWRpYVtpXSwgJ2E9ZXh0bWFwOicpO1xuICAgICAgICAgICAgICAgIGZvciAoaiA9IDA7IGogPCBsaW5lcy5sZW5ndGg7IGorKykge1xuICAgICAgICAgICAgICAgICAgICB0bXAgPSBTRFBVdGlsLnBhcnNlX2V4dG1hcChsaW5lc1tqXSk7XG4gICAgICAgICAgICAgICAgICAgIGVsZW0uYygncnRwLWhkcmV4dCcsIHsgeG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6cnRwLWhkcmV4dDowJyxcbiAgICAgICAgICAgICAgICAgICAgICAgIHVyaTogdG1wLnVyaSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGlkOiB0bXAudmFsdWUgfSk7XG4gICAgICAgICAgICAgICAgICAgIGlmICh0bXAuaGFzT3duUHJvcGVydHkoJ2RpcmVjdGlvbicpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBzd2l0Y2ggKHRtcC5kaXJlY3Rpb24pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlICdzZW5kb25seSc6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uYXR0cnMoe3NlbmRlcnM6ICdyZXNwb25kZXInfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgJ3JlY3Zvbmx5JzpcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWxlbS5hdHRycyh7c2VuZGVyczogJ2luaXRpYXRvcid9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAnc2VuZHJlY3YnOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbGVtLmF0dHJzKHtzZW5kZXJzOiAnYm90aCd9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAnaW5hY3RpdmUnOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbGVtLmF0dHJzKHtzZW5kZXJzOiAnbm9uZSd9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgLy8gVE9ETzogaGFuZGxlIHBhcmFtc1xuICAgICAgICAgICAgICAgICAgICBlbGVtLnVwKCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxlbS51cCgpOyAvLyBlbmQgb2YgZGVzY3JpcHRpb25cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIG1hcCBpY2UtdWZyYWcvcHdkLCBkdGxzIGZpbmdlcnByaW50LCBjYW5kaWRhdGVzXG4gICAgICAgIHRoaXMuVHJhbnNwb3J0VG9KaW5nbGUoaSwgZWxlbSk7XG5cbiAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbaV0sICdhPXNlbmRyZWN2JywgdGhpcy5zZXNzaW9uKSkge1xuICAgICAgICAgICAgZWxlbS5hdHRycyh7c2VuZGVyczogJ2JvdGgnfSk7XG4gICAgICAgIH0gZWxzZSBpZiAoU0RQVXRpbC5maW5kX2xpbmUodGhpcy5tZWRpYVtpXSwgJ2E9c2VuZG9ubHknLCB0aGlzLnNlc3Npb24pKSB7XG4gICAgICAgICAgICBlbGVtLmF0dHJzKHtzZW5kZXJzOiAnaW5pdGlhdG9yJ30pO1xuICAgICAgICB9IGVsc2UgaWYgKFNEUFV0aWwuZmluZF9saW5lKHRoaXMubWVkaWFbaV0sICdhPXJlY3Zvbmx5JywgdGhpcy5zZXNzaW9uKSkge1xuICAgICAgICAgICAgZWxlbS5hdHRycyh7c2VuZGVyczogJ3Jlc3BvbmRlcid9KTtcbiAgICAgICAgfSBlbHNlIGlmIChTRFBVdGlsLmZpbmRfbGluZSh0aGlzLm1lZGlhW2ldLCAnYT1pbmFjdGl2ZScsIHRoaXMuc2Vzc2lvbikpIHtcbiAgICAgICAgICAgIGVsZW0uYXR0cnMoe3NlbmRlcnM6ICdub25lJ30pO1xuICAgICAgICB9XG4gICAgICAgIGlmIChtbGluZS5wb3J0ID09ICcwJykge1xuICAgICAgICAgICAgLy8gZXN0b3MgaGFjayB0byByZWplY3QgYW4gbS1saW5lXG4gICAgICAgICAgICBlbGVtLmF0dHJzKHtzZW5kZXJzOiAncmVqZWN0ZWQnfSk7XG4gICAgICAgIH1cbiAgICAgICAgZWxlbS51cCgpOyAvLyBlbmQgb2YgY29udGVudFxuICAgIH1cbiAgICBlbGVtLnVwKCk7XG4gICAgcmV0dXJuIGVsZW07XG59O1xuXG5TRFAucHJvdG90eXBlLlRyYW5zcG9ydFRvSmluZ2xlID0gZnVuY3Rpb24gKG1lZGlhaW5kZXgsIGVsZW0pIHtcbiAgICB2YXIgaSA9IG1lZGlhaW5kZXg7XG4gICAgdmFyIHRtcDtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgZWxlbS5jKCd0cmFuc3BvcnQnKTtcblxuICAgIC8vIFhFUC0wMzQzIERUTFMvU0NUUFxuICAgIGlmIChTRFBVdGlsLmZpbmRfbGluZSh0aGlzLm1lZGlhW21lZGlhaW5kZXhdLCAnYT1zY3RwbWFwOicpLmxlbmd0aClcbiAgICB7XG4gICAgICAgIHZhciBzY3RwbWFwID0gU0RQVXRpbC5maW5kX2xpbmUoXG4gICAgICAgICAgICB0aGlzLm1lZGlhW2ldLCAnYT1zY3RwbWFwOicsIHNlbGYuc2Vzc2lvbik7XG4gICAgICAgIGlmIChzY3RwbWFwKVxuICAgICAgICB7XG4gICAgICAgICAgICB2YXIgc2N0cEF0dHJzID0gU0RQVXRpbC5wYXJzZV9zY3RwbWFwKHNjdHBtYXApO1xuICAgICAgICAgICAgZWxlbS5jKCdzY3RwbWFwJyxcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIHhtbG5zOiAndXJuOnhtcHA6amluZ2xlOnRyYW5zcG9ydHM6ZHRscy1zY3RwOjEnLFxuICAgICAgICAgICAgICAgICAgICBudW1iZXI6IHNjdHBBdHRyc1swXSwgLyogU0NUUCBwb3J0ICovXG4gICAgICAgICAgICAgICAgICAgIHByb3RvY29sOiBzY3RwQXR0cnNbMV0sIC8qIHByb3RvY29sICovXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAvLyBPcHRpb25hbCBzdHJlYW0gY291bnQgYXR0cmlidXRlXG4gICAgICAgICAgICBpZiAoc2N0cEF0dHJzLmxlbmd0aCA+IDIpXG4gICAgICAgICAgICAgICAgZWxlbS5hdHRycyh7IHN0cmVhbXM6IHNjdHBBdHRyc1syXX0pO1xuICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICB9XG4gICAgfVxuICAgIC8vIFhFUC0wMzIwXG4gICAgdmFyIGZpbmdlcnByaW50cyA9IFNEUFV0aWwuZmluZF9saW5lcyh0aGlzLm1lZGlhW21lZGlhaW5kZXhdLCAnYT1maW5nZXJwcmludDonLCB0aGlzLnNlc3Npb24pO1xuICAgIGZpbmdlcnByaW50cy5mb3JFYWNoKGZ1bmN0aW9uKGxpbmUpIHtcbiAgICAgICAgdG1wID0gU0RQVXRpbC5wYXJzZV9maW5nZXJwcmludChsaW5lKTtcbiAgICAgICAgdG1wLnhtbG5zID0gJ3Vybjp4bXBwOmppbmdsZTphcHBzOmR0bHM6MCc7XG4gICAgICAgIGVsZW0uYygnZmluZ2VycHJpbnQnKS50KHRtcC5maW5nZXJwcmludCk7XG4gICAgICAgIGRlbGV0ZSB0bXAuZmluZ2VycHJpbnQ7XG4gICAgICAgIGxpbmUgPSBTRFBVdGlsLmZpbmRfbGluZShzZWxmLm1lZGlhW21lZGlhaW5kZXhdLCAnYT1zZXR1cDonLCBzZWxmLnNlc3Npb24pO1xuICAgICAgICBpZiAobGluZSkge1xuICAgICAgICAgICAgdG1wLnNldHVwID0gbGluZS5zdWJzdHIoOCk7XG4gICAgICAgIH1cbiAgICAgICAgZWxlbS5hdHRycyh0bXApO1xuICAgICAgICBlbGVtLnVwKCk7IC8vIGVuZCBvZiBmaW5nZXJwcmludFxuICAgIH0pO1xuICAgIHRtcCA9IFNEUFV0aWwuaWNlcGFyYW1zKHRoaXMubWVkaWFbbWVkaWFpbmRleF0sIHRoaXMuc2Vzc2lvbik7XG4gICAgaWYgKHRtcCkge1xuICAgICAgICB0bXAueG1sbnMgPSAndXJuOnhtcHA6amluZ2xlOnRyYW5zcG9ydHM6aWNlLXVkcDoxJztcbiAgICAgICAgZWxlbS5hdHRycyh0bXApO1xuICAgICAgICAvLyBYRVAtMDE3NlxuICAgICAgICBpZiAoU0RQVXRpbC5maW5kX2xpbmUodGhpcy5tZWRpYVttZWRpYWluZGV4XSwgJ2E9Y2FuZGlkYXRlOicsIHRoaXMuc2Vzc2lvbikpIHsgLy8gYWRkIGFueSBhPWNhbmRpZGF0ZSBsaW5lc1xuICAgICAgICAgICAgdmFyIGxpbmVzID0gU0RQVXRpbC5maW5kX2xpbmVzKHRoaXMubWVkaWFbbWVkaWFpbmRleF0sICdhPWNhbmRpZGF0ZTonLCB0aGlzLnNlc3Npb24pO1xuICAgICAgICAgICAgbGluZXMuZm9yRWFjaChmdW5jdGlvbiAobGluZSkge1xuICAgICAgICAgICAgICAgIGVsZW0uYygnY2FuZGlkYXRlJywgU0RQVXRpbC5jYW5kaWRhdGVUb0ppbmdsZShsaW5lKSkudXAoKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgfVxuICAgIGVsZW0udXAoKTsgLy8gZW5kIG9mIHRyYW5zcG9ydFxufVxuXG5TRFAucHJvdG90eXBlLlJ0Y3BGYlRvSmluZ2xlID0gZnVuY3Rpb24gKG1lZGlhaW5kZXgsIGVsZW0sIHBheWxvYWR0eXBlKSB7IC8vIFhFUC0wMjkzXG4gICAgdmFyIGxpbmVzID0gU0RQVXRpbC5maW5kX2xpbmVzKHRoaXMubWVkaWFbbWVkaWFpbmRleF0sICdhPXJ0Y3AtZmI6JyArIHBheWxvYWR0eXBlKTtcbiAgICBsaW5lcy5mb3JFYWNoKGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgIHZhciB0bXAgPSBTRFBVdGlsLnBhcnNlX3J0Y3BmYihsaW5lKTtcbiAgICAgICAgaWYgKHRtcC50eXBlID09ICd0cnItaW50Jykge1xuICAgICAgICAgICAgZWxlbS5jKCdydGNwLWZiLXRyci1pbnQnLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6cnRjcC1mYjowJywgdmFsdWU6IHRtcC5wYXJhbXNbMF19KTtcbiAgICAgICAgICAgIGVsZW0udXAoKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGVsZW0uYygncnRjcC1mYicsIHt4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpydGNwLWZiOjAnLCB0eXBlOiB0bXAudHlwZX0pO1xuICAgICAgICAgICAgaWYgKHRtcC5wYXJhbXMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgIGVsZW0uYXR0cnMoeydzdWJ0eXBlJzogdG1wLnBhcmFtc1swXX0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICB9XG4gICAgfSk7XG59O1xuXG5TRFAucHJvdG90eXBlLlJ0Y3BGYkZyb21KaW5nbGUgPSBmdW5jdGlvbiAoZWxlbSwgcGF5bG9hZHR5cGUpIHsgLy8gWEVQLTAyOTNcbiAgICB2YXIgbWVkaWEgPSAnJztcbiAgICB2YXIgdG1wID0gZWxlbS5maW5kKCc+cnRjcC1mYi10cnItaW50W3htbG5zPVwidXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnJ0Y3AtZmI6MFwiXScpO1xuICAgIGlmICh0bXAubGVuZ3RoKSB7XG4gICAgICAgIG1lZGlhICs9ICdhPXJ0Y3AtZmI6JyArICcqJyArICcgJyArICd0cnItaW50JyArICcgJztcbiAgICAgICAgaWYgKHRtcC5hdHRyKCd2YWx1ZScpKSB7XG4gICAgICAgICAgICBtZWRpYSArPSB0bXAuYXR0cigndmFsdWUnKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIG1lZGlhICs9ICcwJztcbiAgICAgICAgfVxuICAgICAgICBtZWRpYSArPSAnXFxyXFxuJztcbiAgICB9XG4gICAgdG1wID0gZWxlbS5maW5kKCc+cnRjcC1mYlt4bWxucz1cInVybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpydGNwLWZiOjBcIl0nKTtcbiAgICB0bXAuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgIG1lZGlhICs9ICdhPXJ0Y3AtZmI6JyArIHBheWxvYWR0eXBlICsgJyAnICsgJCh0aGlzKS5hdHRyKCd0eXBlJyk7XG4gICAgICAgIGlmICgkKHRoaXMpLmF0dHIoJ3N1YnR5cGUnKSkge1xuICAgICAgICAgICAgbWVkaWEgKz0gJyAnICsgJCh0aGlzKS5hdHRyKCdzdWJ0eXBlJyk7XG4gICAgICAgIH1cbiAgICAgICAgbWVkaWEgKz0gJ1xcclxcbic7XG4gICAgfSk7XG4gICAgcmV0dXJuIG1lZGlhO1xufTtcblxuLy8gY29uc3RydWN0IGFuIFNEUCBmcm9tIGEgamluZ2xlIHN0YW56YVxuU0RQLnByb3RvdHlwZS5mcm9tSmluZ2xlID0gZnVuY3Rpb24gKGppbmdsZSkge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB0aGlzLnJhdyA9ICd2PTBcXHJcXG4nICtcbiAgICAgICAgJ289LSAnICsgJzE5MjM1MTg1MTYnICsgJyAyIElOIElQNCAwLjAuMC4wXFxyXFxuJyArLy8gRklYTUVcbiAgICAgICAgJ3M9LVxcclxcbicgK1xuICAgICAgICAndD0wIDBcXHJcXG4nO1xuICAgIC8vIGh0dHA6Ly90b29scy5pZXRmLm9yZy9odG1sL2RyYWZ0LWlldGYtbW11c2ljLXNkcC1idW5kbGUtbmVnb3RpYXRpb24tMDQjc2VjdGlvbi04XG4gICAgaWYgKCQoamluZ2xlKS5maW5kKCc+Z3JvdXBbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6YXBwczpncm91cGluZzowXCJdJykubGVuZ3RoKSB7XG4gICAgICAgICQoamluZ2xlKS5maW5kKCc+Z3JvdXBbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6YXBwczpncm91cGluZzowXCJdJykuZWFjaChmdW5jdGlvbiAoaWR4LCBncm91cCkge1xuICAgICAgICAgICAgdmFyIGNvbnRlbnRzID0gJChncm91cCkuZmluZCgnPmNvbnRlbnQnKS5tYXAoZnVuY3Rpb24gKGlkeCwgY29udGVudCkge1xuICAgICAgICAgICAgICAgIHJldHVybiBjb250ZW50LmdldEF0dHJpYnV0ZSgnbmFtZScpO1xuICAgICAgICAgICAgfSkuZ2V0KCk7XG4gICAgICAgICAgICBpZiAoY29udGVudHMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgIHNlbGYucmF3ICs9ICdhPWdyb3VwOicgKyAoZ3JvdXAuZ2V0QXR0cmlidXRlKCdzZW1hbnRpY3MnKSB8fCBncm91cC5nZXRBdHRyaWJ1dGUoJ3R5cGUnKSkgKyAnICcgKyBjb250ZW50cy5qb2luKCcgJykgKyAnXFxyXFxuJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgdGhpcy5zZXNzaW9uID0gdGhpcy5yYXc7XG4gICAgamluZ2xlLmZpbmQoJz5jb250ZW50JykuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBtID0gc2VsZi5qaW5nbGUybWVkaWEoJCh0aGlzKSk7XG4gICAgICAgIHNlbGYubWVkaWEucHVzaChtKTtcbiAgICB9KTtcblxuICAgIC8vIHJlY29uc3RydWN0IG1zaWQtc2VtYW50aWMgLS0gYXBwYXJlbnRseSBub3QgbmVjZXNzYXJ5XG4gICAgLypcbiAgICAgdmFyIG1zaWQgPSBTRFBVdGlsLnBhcnNlX3NzcmModGhpcy5yYXcpO1xuICAgICBpZiAobXNpZC5oYXNPd25Qcm9wZXJ0eSgnbXNsYWJlbCcpKSB7XG4gICAgIHRoaXMuc2Vzc2lvbiArPSBcImE9bXNpZC1zZW1hbnRpYzogV01TIFwiICsgbXNpZC5tc2xhYmVsICsgXCJcXHJcXG5cIjtcbiAgICAgfVxuICAgICAqL1xuXG4gICAgdGhpcy5yYXcgPSB0aGlzLnNlc3Npb24gKyB0aGlzLm1lZGlhLmpvaW4oJycpO1xufTtcblxuLy8gdHJhbnNsYXRlIGEgamluZ2xlIGNvbnRlbnQgZWxlbWVudCBpbnRvIGFuIGFuIFNEUCBtZWRpYSBwYXJ0XG5TRFAucHJvdG90eXBlLmppbmdsZTJtZWRpYSA9IGZ1bmN0aW9uIChjb250ZW50KSB7XG4gICAgdmFyIG1lZGlhID0gJycsXG4gICAgICAgIGRlc2MgPSBjb250ZW50LmZpbmQoJ2Rlc2NyaXB0aW9uJyksXG4gICAgICAgIHNzcmMgPSBkZXNjLmF0dHIoJ3NzcmMnKSxcbiAgICAgICAgc2VsZiA9IHRoaXMsXG4gICAgICAgIHRtcDtcbiAgICB2YXIgc2N0cCA9IGNvbnRlbnQuZmluZChcbiAgICAgICAgJz50cmFuc3BvcnQ+c2N0cG1hcFt4bWxucz1cInVybjp4bXBwOmppbmdsZTp0cmFuc3BvcnRzOmR0bHMtc2N0cDoxXCJdJyk7XG5cbiAgICB0bXAgPSB7IG1lZGlhOiBkZXNjLmF0dHIoJ21lZGlhJykgfTtcbiAgICB0bXAucG9ydCA9ICcxJztcbiAgICBpZiAoY29udGVudC5hdHRyKCdzZW5kZXJzJykgPT0gJ3JlamVjdGVkJykge1xuICAgICAgICAvLyBlc3RvcyBoYWNrIHRvIHJlamVjdCBhbiBtLWxpbmUuXG4gICAgICAgIHRtcC5wb3J0ID0gJzAnO1xuICAgIH1cbiAgICBpZiAoY29udGVudC5maW5kKCc+dHJhbnNwb3J0PmZpbmdlcnByaW50JykubGVuZ3RoIHx8IGRlc2MuZmluZCgnZW5jcnlwdGlvbicpLmxlbmd0aCkge1xuICAgICAgICBpZiAoc2N0cC5sZW5ndGgpXG4gICAgICAgICAgICB0bXAucHJvdG8gPSAnRFRMUy9TQ1RQJztcbiAgICAgICAgZWxzZVxuICAgICAgICAgICAgdG1wLnByb3RvID0gJ1JUUC9TQVZQRic7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgdG1wLnByb3RvID0gJ1JUUC9BVlBGJztcbiAgICB9XG4gICAgaWYgKCFzY3RwLmxlbmd0aClcbiAgICB7XG4gICAgICAgIHRtcC5mbXQgPSBkZXNjLmZpbmQoJ3BheWxvYWQtdHlwZScpLm1hcChcbiAgICAgICAgICAgIGZ1bmN0aW9uICgpIHsgcmV0dXJuIHRoaXMuZ2V0QXR0cmlidXRlKCdpZCcpOyB9KS5nZXQoKTtcbiAgICAgICAgbWVkaWEgKz0gU0RQVXRpbC5idWlsZF9tbGluZSh0bXApICsgJ1xcclxcbic7XG4gICAgfVxuICAgIGVsc2VcbiAgICB7XG4gICAgICAgIG1lZGlhICs9ICdtPWFwcGxpY2F0aW9uIDEgRFRMUy9TQ1RQICcgKyBzY3RwLmF0dHIoJ251bWJlcicpICsgJ1xcclxcbic7XG4gICAgICAgIG1lZGlhICs9ICdhPXNjdHBtYXA6JyArIHNjdHAuYXR0cignbnVtYmVyJykgK1xuICAgICAgICAgICAgJyAnICsgc2N0cC5hdHRyKCdwcm90b2NvbCcpO1xuXG4gICAgICAgIHZhciBzdHJlYW1Db3VudCA9IHNjdHAuYXR0cignc3RyZWFtcycpO1xuICAgICAgICBpZiAoc3RyZWFtQ291bnQpXG4gICAgICAgICAgICBtZWRpYSArPSAnICcgKyBzdHJlYW1Db3VudCArICdcXHJcXG4nO1xuICAgICAgICBlbHNlXG4gICAgICAgICAgICBtZWRpYSArPSAnXFxyXFxuJztcbiAgICB9XG5cbiAgICBtZWRpYSArPSAnYz1JTiBJUDQgMC4wLjAuMFxcclxcbic7XG4gICAgaWYgKCFzY3RwLmxlbmd0aClcbiAgICAgICAgbWVkaWEgKz0gJ2E9cnRjcDoxIElOIElQNCAwLjAuMC4wXFxyXFxuJztcbiAgICB0bXAgPSBjb250ZW50LmZpbmQoJz50cmFuc3BvcnRbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6dHJhbnNwb3J0czppY2UtdWRwOjFcIl0nKTtcbiAgICBpZiAodG1wLmxlbmd0aCkge1xuICAgICAgICBpZiAodG1wLmF0dHIoJ3VmcmFnJykpIHtcbiAgICAgICAgICAgIG1lZGlhICs9IFNEUFV0aWwuYnVpbGRfaWNldWZyYWcodG1wLmF0dHIoJ3VmcmFnJykpICsgJ1xcclxcbic7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRtcC5hdHRyKCdwd2QnKSkge1xuICAgICAgICAgICAgbWVkaWEgKz0gU0RQVXRpbC5idWlsZF9pY2Vwd2QodG1wLmF0dHIoJ3B3ZCcpKSArICdcXHJcXG4nO1xuICAgICAgICB9XG4gICAgICAgIHRtcC5maW5kKCc+ZmluZ2VycHJpbnQnKS5lYWNoKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIC8vIEZJWE1FOiBjaGVjayBuYW1lc3BhY2UgYXQgc29tZSBwb2ludFxuICAgICAgICAgICAgbWVkaWEgKz0gJ2E9ZmluZ2VycHJpbnQ6JyArIHRoaXMuZ2V0QXR0cmlidXRlKCdoYXNoJyk7XG4gICAgICAgICAgICBtZWRpYSArPSAnICcgKyAkKHRoaXMpLnRleHQoKTtcbiAgICAgICAgICAgIG1lZGlhICs9ICdcXHJcXG4nO1xuICAgICAgICAgICAgaWYgKHRoaXMuZ2V0QXR0cmlidXRlKCdzZXR1cCcpKSB7XG4gICAgICAgICAgICAgICAgbWVkaWEgKz0gJ2E9c2V0dXA6JyArIHRoaXMuZ2V0QXR0cmlidXRlKCdzZXR1cCcpICsgJ1xcclxcbic7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICBzd2l0Y2ggKGNvbnRlbnQuYXR0cignc2VuZGVycycpKSB7XG4gICAgICAgIGNhc2UgJ2luaXRpYXRvcic6XG4gICAgICAgICAgICBtZWRpYSArPSAnYT1zZW5kb25seVxcclxcbic7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAncmVzcG9uZGVyJzpcbiAgICAgICAgICAgIG1lZGlhICs9ICdhPXJlY3Zvbmx5XFxyXFxuJztcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdub25lJzpcbiAgICAgICAgICAgIG1lZGlhICs9ICdhPWluYWN0aXZlXFxyXFxuJztcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdib3RoJzpcbiAgICAgICAgICAgIG1lZGlhICs9ICdhPXNlbmRyZWN2XFxyXFxuJztcbiAgICAgICAgICAgIGJyZWFrO1xuICAgIH1cbiAgICBtZWRpYSArPSAnYT1taWQ6JyArIGNvbnRlbnQuYXR0cignbmFtZScpICsgJ1xcclxcbic7XG5cbiAgICAvLyA8ZGVzY3JpcHRpb24+PHJ0Y3AtbXV4Lz48L2Rlc2NyaXB0aW9uPlxuICAgIC8vIHNlZSBodHRwOi8vY29kZS5nb29nbGUuY29tL3AvbGliamluZ2xlL2lzc3Vlcy9kZXRhaWw/aWQ9MzA5IC0tIG5vIHNwZWMgdGhvdWdoXG4gICAgLy8gYW5kIGh0dHA6Ly9tYWlsLmphYmJlci5vcmcvcGlwZXJtYWlsL2ppbmdsZS8yMDExLURlY2VtYmVyLzAwMTc2MS5odG1sXG4gICAgaWYgKGRlc2MuZmluZCgncnRjcC1tdXgnKS5sZW5ndGgpIHtcbiAgICAgICAgbWVkaWEgKz0gJ2E9cnRjcC1tdXhcXHJcXG4nO1xuICAgIH1cblxuICAgIGlmIChkZXNjLmZpbmQoJ2VuY3J5cHRpb24nKS5sZW5ndGgpIHtcbiAgICAgICAgZGVzYy5maW5kKCdlbmNyeXB0aW9uPmNyeXB0bycpLmVhY2goZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgbWVkaWEgKz0gJ2E9Y3J5cHRvOicgKyB0aGlzLmdldEF0dHJpYnV0ZSgndGFnJyk7XG4gICAgICAgICAgICBtZWRpYSArPSAnICcgKyB0aGlzLmdldEF0dHJpYnV0ZSgnY3J5cHRvLXN1aXRlJyk7XG4gICAgICAgICAgICBtZWRpYSArPSAnICcgKyB0aGlzLmdldEF0dHJpYnV0ZSgna2V5LXBhcmFtcycpO1xuICAgICAgICAgICAgaWYgKHRoaXMuZ2V0QXR0cmlidXRlKCdzZXNzaW9uLXBhcmFtcycpKSB7XG4gICAgICAgICAgICAgICAgbWVkaWEgKz0gJyAnICsgdGhpcy5nZXRBdHRyaWJ1dGUoJ3Nlc3Npb24tcGFyYW1zJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBtZWRpYSArPSAnXFxyXFxuJztcbiAgICAgICAgfSk7XG4gICAgfVxuICAgIGRlc2MuZmluZCgncGF5bG9hZC10eXBlJykuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgIG1lZGlhICs9IFNEUFV0aWwuYnVpbGRfcnRwbWFwKHRoaXMpICsgJ1xcclxcbic7XG4gICAgICAgIGlmICgkKHRoaXMpLmZpbmQoJz5wYXJhbWV0ZXInKS5sZW5ndGgpIHtcbiAgICAgICAgICAgIG1lZGlhICs9ICdhPWZtdHA6JyArIHRoaXMuZ2V0QXR0cmlidXRlKCdpZCcpICsgJyAnO1xuICAgICAgICAgICAgbWVkaWEgKz0gJCh0aGlzKS5maW5kKCdwYXJhbWV0ZXInKS5tYXAoZnVuY3Rpb24gKCkgeyByZXR1cm4gKHRoaXMuZ2V0QXR0cmlidXRlKCduYW1lJykgPyAodGhpcy5nZXRBdHRyaWJ1dGUoJ25hbWUnKSArICc9JykgOiAnJykgKyB0aGlzLmdldEF0dHJpYnV0ZSgndmFsdWUnKTsgfSkuZ2V0KCkuam9pbignOyAnKTtcbiAgICAgICAgICAgIG1lZGlhICs9ICdcXHJcXG4nO1xuICAgICAgICB9XG4gICAgICAgIC8vIHhlcC0wMjkzXG4gICAgICAgIG1lZGlhICs9IHNlbGYuUnRjcEZiRnJvbUppbmdsZSgkKHRoaXMpLCB0aGlzLmdldEF0dHJpYnV0ZSgnaWQnKSk7XG4gICAgfSk7XG5cbiAgICAvLyB4ZXAtMDI5M1xuICAgIG1lZGlhICs9IHNlbGYuUnRjcEZiRnJvbUppbmdsZShkZXNjLCAnKicpO1xuXG4gICAgLy8geGVwLTAyOTRcbiAgICB0bXAgPSBkZXNjLmZpbmQoJz5ydHAtaGRyZXh0W3htbG5zPVwidXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnJ0cC1oZHJleHQ6MFwiXScpO1xuICAgIHRtcC5lYWNoKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgbWVkaWEgKz0gJ2E9ZXh0bWFwOicgKyB0aGlzLmdldEF0dHJpYnV0ZSgnaWQnKSArICcgJyArIHRoaXMuZ2V0QXR0cmlidXRlKCd1cmknKSArICdcXHJcXG4nO1xuICAgIH0pO1xuXG4gICAgY29udGVudC5maW5kKCc+dHJhbnNwb3J0W3htbG5zPVwidXJuOnhtcHA6amluZ2xlOnRyYW5zcG9ydHM6aWNlLXVkcDoxXCJdPmNhbmRpZGF0ZScpLmVhY2goZnVuY3Rpb24gKCkge1xuICAgICAgICBtZWRpYSArPSBTRFBVdGlsLmNhbmRpZGF0ZUZyb21KaW5nbGUodGhpcyk7XG4gICAgfSk7XG5cbiAgICAvLyBYRVAtMDMzOSBoYW5kbGUgc3NyYy1ncm91cCBhdHRyaWJ1dGVzXG4gICAgdG1wID0gY29udGVudC5maW5kKCdkZXNjcmlwdGlvbj5zc3JjLWdyb3VwW3htbG5zPVwidXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnNzbWE6MFwiXScpLmVhY2goZnVuY3Rpb24oKSB7XG4gICAgICAgIHZhciBzZW1hbnRpY3MgPSB0aGlzLmdldEF0dHJpYnV0ZSgnc2VtYW50aWNzJyk7XG4gICAgICAgIHZhciBzc3JjcyA9ICQodGhpcykuZmluZCgnPnNvdXJjZScpLm1hcChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmdldEF0dHJpYnV0ZSgnc3NyYycpO1xuICAgICAgICB9KS5nZXQoKTtcblxuICAgICAgICBpZiAoc3NyY3MubGVuZ3RoICE9IDApIHtcbiAgICAgICAgICAgIG1lZGlhICs9ICdhPXNzcmMtZ3JvdXA6JyArIHNlbWFudGljcyArICcgJyArIHNzcmNzLmpvaW4oJyAnKSArICdcXHJcXG4nO1xuICAgICAgICB9XG4gICAgfSk7XG5cbiAgICB0bXAgPSBjb250ZW50LmZpbmQoJ2Rlc2NyaXB0aW9uPnNvdXJjZVt4bWxucz1cInVybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpzc21hOjBcIl0nKTtcbiAgICB0bXAuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBzc3JjID0gdGhpcy5nZXRBdHRyaWJ1dGUoJ3NzcmMnKTtcbiAgICAgICAgJCh0aGlzKS5maW5kKCc+cGFyYW1ldGVyJykuZWFjaChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBtZWRpYSArPSAnYT1zc3JjOicgKyBzc3JjICsgJyAnICsgdGhpcy5nZXRBdHRyaWJ1dGUoJ25hbWUnKTtcbiAgICAgICAgICAgIGlmICh0aGlzLmdldEF0dHJpYnV0ZSgndmFsdWUnKSAmJiB0aGlzLmdldEF0dHJpYnV0ZSgndmFsdWUnKS5sZW5ndGgpXG4gICAgICAgICAgICAgICAgbWVkaWEgKz0gJzonICsgdGhpcy5nZXRBdHRyaWJ1dGUoJ3ZhbHVlJyk7XG4gICAgICAgICAgICBtZWRpYSArPSAnXFxyXFxuJztcbiAgICAgICAgfSk7XG4gICAgfSk7XG5cbiAgICByZXR1cm4gbWVkaWE7XG59O1xuXG5cbm1vZHVsZS5leHBvcnRzID0gU0RQO1xuXG4iLCJmdW5jdGlvbiBTRFBEaWZmZXIobXlTRFAsIG90aGVyU0RQKSB7XG4gICAgdGhpcy5teVNEUCA9IG15U0RQO1xuICAgIHRoaXMub3RoZXJTRFAgPSBvdGhlclNEUDtcbn1cblxuLyoqXG4gKiBSZXR1cm5zIG1hcCBvZiBNZWRpYUNoYW5uZWwgdGhhdCBjb250YWlucyBvbmx5IG1lZGlhIG5vdCBjb250YWluZWQgaW4gPHR0Pm90aGVyU2RwPC90dD4uIE1hcHBlZCBieSBjaGFubmVsIGlkeC5cbiAqIEBwYXJhbSBvdGhlclNkcCB0aGUgb3RoZXIgU0RQIHRvIGNoZWNrIHNzcmMgd2l0aC5cbiAqL1xuU0RQRGlmZmVyLnByb3RvdHlwZS5nZXROZXdNZWRpYSA9IGZ1bmN0aW9uKCkge1xuXG4gICAgLy8gdGhpcyBjb3VsZCBiZSB1c2VmdWwgaW4gQXJyYXkucHJvdG90eXBlLlxuICAgIGZ1bmN0aW9uIGFycmF5RXF1YWxzKGFycmF5KSB7XG4gICAgICAgIC8vIGlmIHRoZSBvdGhlciBhcnJheSBpcyBhIGZhbHN5IHZhbHVlLCByZXR1cm5cbiAgICAgICAgaWYgKCFhcnJheSlcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcblxuICAgICAgICAvLyBjb21wYXJlIGxlbmd0aHMgLSBjYW4gc2F2ZSBhIGxvdCBvZiB0aW1lXG4gICAgICAgIGlmICh0aGlzLmxlbmd0aCAhPSBhcnJheS5sZW5ndGgpXG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDAsIGw9dGhpcy5sZW5ndGg7IGkgPCBsOyBpKyspIHtcbiAgICAgICAgICAgIC8vIENoZWNrIGlmIHdlIGhhdmUgbmVzdGVkIGFycmF5c1xuICAgICAgICAgICAgaWYgKHRoaXNbaV0gaW5zdGFuY2VvZiBBcnJheSAmJiBhcnJheVtpXSBpbnN0YW5jZW9mIEFycmF5KSB7XG4gICAgICAgICAgICAgICAgLy8gcmVjdXJzZSBpbnRvIHRoZSBuZXN0ZWQgYXJyYXlzXG4gICAgICAgICAgICAgICAgaWYgKCF0aGlzW2ldLmVxdWFscyhhcnJheVtpXSkpXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2UgaWYgKHRoaXNbaV0gIT0gYXJyYXlbaV0pIHtcbiAgICAgICAgICAgICAgICAvLyBXYXJuaW5nIC0gdHdvIGRpZmZlcmVudCBvYmplY3QgaW5zdGFuY2VzIHdpbGwgbmV2ZXIgYmUgZXF1YWw6IHt4OjIwfSAhPSB7eDoyMH1cbiAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgdmFyIG15TWVkaWFzID0gdGhpcy5teVNEUC5nZXRNZWRpYVNzcmNNYXAoKTtcbiAgICB2YXIgb3RoZXJzTWVkaWFzID0gdGhpcy5vdGhlclNEUC5nZXRNZWRpYVNzcmNNYXAoKTtcbiAgICB2YXIgbmV3TWVkaWEgPSB7fTtcbiAgICBPYmplY3Qua2V5cyhvdGhlcnNNZWRpYXMpLmZvckVhY2goZnVuY3Rpb24ob3RoZXJzTWVkaWFJZHgpIHtcbiAgICAgICAgdmFyIG15TWVkaWEgPSBteU1lZGlhc1tvdGhlcnNNZWRpYUlkeF07XG4gICAgICAgIHZhciBvdGhlcnNNZWRpYSA9IG90aGVyc01lZGlhc1tvdGhlcnNNZWRpYUlkeF07XG4gICAgICAgIGlmKCFteU1lZGlhICYmIG90aGVyc01lZGlhKSB7XG4gICAgICAgICAgICAvLyBBZGQgd2hvbGUgY2hhbm5lbFxuICAgICAgICAgICAgbmV3TWVkaWFbb3RoZXJzTWVkaWFJZHhdID0gb3RoZXJzTWVkaWE7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgLy8gTG9vayBmb3IgbmV3IHNzcmNzIGFjY3Jvc3MgdGhlIGNoYW5uZWxcbiAgICAgICAgT2JqZWN0LmtleXMob3RoZXJzTWVkaWEuc3NyY3MpLmZvckVhY2goZnVuY3Rpb24oc3NyYykge1xuICAgICAgICAgICAgaWYoT2JqZWN0LmtleXMobXlNZWRpYS5zc3JjcykuaW5kZXhPZihzc3JjKSA9PT0gLTEpIHtcbiAgICAgICAgICAgICAgICAvLyBBbGxvY2F0ZSBjaGFubmVsIGlmIHdlJ3ZlIGZvdW5kIHNzcmMgdGhhdCBkb2Vzbid0IGV4aXN0IGluIG91ciBjaGFubmVsXG4gICAgICAgICAgICAgICAgaWYoIW5ld01lZGlhW290aGVyc01lZGlhSWR4XSl7XG4gICAgICAgICAgICAgICAgICAgIG5ld01lZGlhW290aGVyc01lZGlhSWR4XSA9IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG1lZGlhaW5kZXg6IG90aGVyc01lZGlhLm1lZGlhaW5kZXgsXG4gICAgICAgICAgICAgICAgICAgICAgICBtaWQ6IG90aGVyc01lZGlhLm1pZCxcbiAgICAgICAgICAgICAgICAgICAgICAgIHNzcmNzOiB7fSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHNzcmNHcm91cHM6IFtdXG4gICAgICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIG5ld01lZGlhW290aGVyc01lZGlhSWR4XS5zc3Jjc1tzc3JjXSA9IG90aGVyc01lZGlhLnNzcmNzW3NzcmNdO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICAvLyBMb29rIGZvciBuZXcgc3NyYyBncm91cHMgYWNyb3NzIHRoZSBjaGFubmVsc1xuICAgICAgICBvdGhlcnNNZWRpYS5zc3JjR3JvdXBzLmZvckVhY2goZnVuY3Rpb24ob3RoZXJTc3JjR3JvdXApe1xuXG4gICAgICAgICAgICAvLyB0cnkgdG8gbWF0Y2ggdGhlIG90aGVyIHNzcmMtZ3JvdXAgd2l0aCBhbiBzc3JjLWdyb3VwIG9mIG91cnNcbiAgICAgICAgICAgIHZhciBtYXRjaGVkID0gZmFsc2U7XG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IG15TWVkaWEuc3NyY0dyb3Vwcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIHZhciBteVNzcmNHcm91cCA9IG15TWVkaWEuc3NyY0dyb3Vwc1tpXTtcbiAgICAgICAgICAgICAgICBpZiAob3RoZXJTc3JjR3JvdXAuc2VtYW50aWNzID09IG15U3NyY0dyb3VwLnNlbWFudGljc1xuICAgICAgICAgICAgICAgICAgICAmJiBhcnJheUVxdWFscy5hcHBseShvdGhlclNzcmNHcm91cC5zc3JjcywgW215U3NyY0dyb3VwLnNzcmNzXSkpIHtcblxuICAgICAgICAgICAgICAgICAgICBtYXRjaGVkID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoIW1hdGNoZWQpIHtcbiAgICAgICAgICAgICAgICAvLyBBbGxvY2F0ZSBjaGFubmVsIGlmIHdlJ3ZlIGZvdW5kIGFuIHNzcmMtZ3JvdXAgdGhhdCBkb2Vzbid0XG4gICAgICAgICAgICAgICAgLy8gZXhpc3QgaW4gb3VyIGNoYW5uZWxcblxuICAgICAgICAgICAgICAgIGlmKCFuZXdNZWRpYVtvdGhlcnNNZWRpYUlkeF0pe1xuICAgICAgICAgICAgICAgICAgICBuZXdNZWRpYVtvdGhlcnNNZWRpYUlkeF0gPSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBtZWRpYWluZGV4OiBvdGhlcnNNZWRpYS5tZWRpYWluZGV4LFxuICAgICAgICAgICAgICAgICAgICAgICAgbWlkOiBvdGhlcnNNZWRpYS5taWQsXG4gICAgICAgICAgICAgICAgICAgICAgICBzc3Jjczoge30sXG4gICAgICAgICAgICAgICAgICAgICAgICBzc3JjR3JvdXBzOiBbXVxuICAgICAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBuZXdNZWRpYVtvdGhlcnNNZWRpYUlkeF0uc3NyY0dyb3Vwcy5wdXNoKG90aGVyU3NyY0dyb3VwKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfSk7XG4gICAgcmV0dXJuIG5ld01lZGlhO1xufTtcblxuLyoqXG4gKiBTZW5kcyBTU1JDIHVwZGF0ZSBJUS5cbiAqIEBwYXJhbSBzZHBNZWRpYVNzcmNzIFNTUkNzIG1hcCBvYnRhaW5lZCBmcm9tIFNEUC5nZXROZXdNZWRpYS4gQ250YWlucyBTU1JDcyB0byBhZGQvcmVtb3ZlLlxuICogQHBhcmFtIHNpZCBzZXNzaW9uIGlkZW50aWZpZXIgdGhhdCB3aWxsIGJlIHB1dCBpbnRvIHRoZSBJUS5cbiAqIEBwYXJhbSBpbml0aWF0b3IgaW5pdGlhdG9yIGlkZW50aWZpZXIuXG4gKiBAcGFyYW0gdG9KaWQgZGVzdGluYXRpb24gSmlkXG4gKiBAcGFyYW0gaXNBZGQgaW5kaWNhdGVzIGlmIHRoaXMgaXMgcmVtb3ZlIG9yIGFkZCBvcGVyYXRpb24uXG4gKi9cblNEUERpZmZlci5wcm90b3R5cGUudG9KaW5nbGUgPSBmdW5jdGlvbihtb2RpZnkpIHtcbiAgICB2YXIgc2RwTWVkaWFTc3JjcyA9IHRoaXMuZ2V0TmV3TWVkaWEoKTtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG5cbiAgICAvLyBGSVhNRTogb25seSBhbm5vdW5jZSB2aWRlbyBzc3JjcyBzaW5jZSB3ZSBtaXggYXVkaW8gYW5kIGRvbnQgbmVlZFxuICAgIC8vICAgICAgdGhlIGF1ZGlvIHNzcmNzIHRoZXJlZm9yZVxuICAgIHZhciBtb2RpZmllZCA9IGZhbHNlO1xuICAgIE9iamVjdC5rZXlzKHNkcE1lZGlhU3NyY3MpLmZvckVhY2goZnVuY3Rpb24obWVkaWFpbmRleCl7XG4gICAgICAgIG1vZGlmaWVkID0gdHJ1ZTtcbiAgICAgICAgdmFyIG1lZGlhID0gc2RwTWVkaWFTc3Jjc1ttZWRpYWluZGV4XTtcbiAgICAgICAgbW9kaWZ5LmMoJ2NvbnRlbnQnLCB7bmFtZTogbWVkaWEubWlkfSk7XG5cbiAgICAgICAgbW9kaWZ5LmMoJ2Rlc2NyaXB0aW9uJywge3htbG5zOid1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6MScsIG1lZGlhOiBtZWRpYS5taWR9KTtcbiAgICAgICAgLy8gRklYTUU6IG5vdCBjb21wbGV0bHkgc3VyZSB0aGlzIG9wZXJhdGVzIG9uIGJsb2NrcyBhbmQgLyBvciBoYW5kbGVzIGRpZmZlcmVudCBzc3JjcyBjb3JyZWN0bHlcbiAgICAgICAgLy8gZ2VuZXJhdGUgc291cmNlcyBmcm9tIGxpbmVzXG4gICAgICAgIE9iamVjdC5rZXlzKG1lZGlhLnNzcmNzKS5mb3JFYWNoKGZ1bmN0aW9uKHNzcmNOdW0pIHtcbiAgICAgICAgICAgIHZhciBtZWRpYVNzcmMgPSBtZWRpYS5zc3Jjc1tzc3JjTnVtXTtcbiAgICAgICAgICAgIG1vZGlmeS5jKCdzb3VyY2UnLCB7IHhtbG5zOiAndXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOnNzbWE6MCcgfSk7XG4gICAgICAgICAgICBtb2RpZnkuYXR0cnMoe3NzcmM6IG1lZGlhU3NyYy5zc3JjfSk7XG4gICAgICAgICAgICAvLyBpdGVyYXRlIG92ZXIgc3NyYyBsaW5lc1xuICAgICAgICAgICAgbWVkaWFTc3JjLmxpbmVzLmZvckVhY2goZnVuY3Rpb24gKGxpbmUpIHtcbiAgICAgICAgICAgICAgICB2YXIgaWR4ID0gbGluZS5pbmRleE9mKCcgJyk7XG4gICAgICAgICAgICAgICAgdmFyIGt2ID0gbGluZS5zdWJzdHIoaWR4ICsgMSk7XG4gICAgICAgICAgICAgICAgbW9kaWZ5LmMoJ3BhcmFtZXRlcicpO1xuICAgICAgICAgICAgICAgIGlmIChrdi5pbmRleE9mKCc6JykgPT0gLTEpIHtcbiAgICAgICAgICAgICAgICAgICAgbW9kaWZ5LmF0dHJzKHsgbmFtZToga3YgfSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgbW9kaWZ5LmF0dHJzKHsgbmFtZToga3Yuc3BsaXQoJzonLCAyKVswXSB9KTtcbiAgICAgICAgICAgICAgICAgICAgbW9kaWZ5LmF0dHJzKHsgdmFsdWU6IGt2LnNwbGl0KCc6JywgMilbMV0gfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIG1vZGlmeS51cCgpOyAvLyBlbmQgb2YgcGFyYW1ldGVyXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIG1vZGlmeS51cCgpOyAvLyBlbmQgb2Ygc291cmNlXG4gICAgICAgIH0pO1xuXG4gICAgICAgIC8vIGdlbmVyYXRlIHNvdXJjZSBncm91cHMgZnJvbSBsaW5lc1xuICAgICAgICBtZWRpYS5zc3JjR3JvdXBzLmZvckVhY2goZnVuY3Rpb24oc3NyY0dyb3VwKSB7XG4gICAgICAgICAgICBpZiAoc3NyY0dyb3VwLnNzcmNzLmxlbmd0aCAhPSAwKSB7XG5cbiAgICAgICAgICAgICAgICBtb2RpZnkuYygnc3NyYy1ncm91cCcsIHtcbiAgICAgICAgICAgICAgICAgICAgc2VtYW50aWNzOiBzc3JjR3JvdXAuc2VtYW50aWNzLFxuICAgICAgICAgICAgICAgICAgICB4bWxuczogJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpzc21hOjAnXG4gICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgICBzc3JjR3JvdXAuc3NyY3MuZm9yRWFjaChmdW5jdGlvbiAoc3NyYykge1xuICAgICAgICAgICAgICAgICAgICBtb2RpZnkuYygnc291cmNlJywgeyBzc3JjOiBzc3JjIH0pXG4gICAgICAgICAgICAgICAgICAgICAgICAudXAoKTsgLy8gZW5kIG9mIHNvdXJjZVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIG1vZGlmeS51cCgpOyAvLyBlbmQgb2Ygc3NyYy1ncm91cFxuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICBtb2RpZnkudXAoKTsgLy8gZW5kIG9mIGRlc2NyaXB0aW9uXG4gICAgICAgIG1vZGlmeS51cCgpOyAvLyBlbmQgb2YgY29udGVudFxuICAgIH0pO1xuXG4gICAgcmV0dXJuIG1vZGlmaWVkO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSBTRFBEaWZmZXI7IiwiU0RQVXRpbCA9IHtcbiAgICBpY2VwYXJhbXM6IGZ1bmN0aW9uIChtZWRpYWRlc2MsIHNlc3Npb25kZXNjKSB7XG4gICAgICAgIHZhciBkYXRhID0gbnVsbDtcbiAgICAgICAgaWYgKFNEUFV0aWwuZmluZF9saW5lKG1lZGlhZGVzYywgJ2E9aWNlLXVmcmFnOicsIHNlc3Npb25kZXNjKSAmJlxuICAgICAgICAgICAgU0RQVXRpbC5maW5kX2xpbmUobWVkaWFkZXNjLCAnYT1pY2UtcHdkOicsIHNlc3Npb25kZXNjKSkge1xuICAgICAgICAgICAgZGF0YSA9IHtcbiAgICAgICAgICAgICAgICB1ZnJhZzogU0RQVXRpbC5wYXJzZV9pY2V1ZnJhZyhTRFBVdGlsLmZpbmRfbGluZShtZWRpYWRlc2MsICdhPWljZS11ZnJhZzonLCBzZXNzaW9uZGVzYykpLFxuICAgICAgICAgICAgICAgIHB3ZDogU0RQVXRpbC5wYXJzZV9pY2Vwd2QoU0RQVXRpbC5maW5kX2xpbmUobWVkaWFkZXNjLCAnYT1pY2UtcHdkOicsIHNlc3Npb25kZXNjKSlcbiAgICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGRhdGE7XG4gICAgfSxcbiAgICBwYXJzZV9pY2V1ZnJhZzogZnVuY3Rpb24gKGxpbmUpIHtcbiAgICAgICAgcmV0dXJuIGxpbmUuc3Vic3RyaW5nKDEyKTtcbiAgICB9LFxuICAgIGJ1aWxkX2ljZXVmcmFnOiBmdW5jdGlvbiAoZnJhZykge1xuICAgICAgICByZXR1cm4gJ2E9aWNlLXVmcmFnOicgKyBmcmFnO1xuICAgIH0sXG4gICAgcGFyc2VfaWNlcHdkOiBmdW5jdGlvbiAobGluZSkge1xuICAgICAgICByZXR1cm4gbGluZS5zdWJzdHJpbmcoMTApO1xuICAgIH0sXG4gICAgYnVpbGRfaWNlcHdkOiBmdW5jdGlvbiAocHdkKSB7XG4gICAgICAgIHJldHVybiAnYT1pY2UtcHdkOicgKyBwd2Q7XG4gICAgfSxcbiAgICBwYXJzZV9taWQ6IGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgIHJldHVybiBsaW5lLnN1YnN0cmluZyg2KTtcbiAgICB9LFxuICAgIHBhcnNlX21saW5lOiBmdW5jdGlvbiAobGluZSkge1xuICAgICAgICB2YXIgcGFydHMgPSBsaW5lLnN1YnN0cmluZygyKS5zcGxpdCgnICcpLFxuICAgICAgICAgICAgZGF0YSA9IHt9O1xuICAgICAgICBkYXRhLm1lZGlhID0gcGFydHMuc2hpZnQoKTtcbiAgICAgICAgZGF0YS5wb3J0ID0gcGFydHMuc2hpZnQoKTtcbiAgICAgICAgZGF0YS5wcm90byA9IHBhcnRzLnNoaWZ0KCk7XG4gICAgICAgIGlmIChwYXJ0c1twYXJ0cy5sZW5ndGggLSAxXSA9PT0gJycpIHsgLy8gdHJhaWxpbmcgd2hpdGVzcGFjZVxuICAgICAgICAgICAgcGFydHMucG9wKCk7XG4gICAgICAgIH1cbiAgICAgICAgZGF0YS5mbXQgPSBwYXJ0cztcbiAgICAgICAgcmV0dXJuIGRhdGE7XG4gICAgfSxcbiAgICBidWlsZF9tbGluZTogZnVuY3Rpb24gKG1saW5lKSB7XG4gICAgICAgIHJldHVybiAnbT0nICsgbWxpbmUubWVkaWEgKyAnICcgKyBtbGluZS5wb3J0ICsgJyAnICsgbWxpbmUucHJvdG8gKyAnICcgKyBtbGluZS5mbXQuam9pbignICcpO1xuICAgIH0sXG4gICAgcGFyc2VfcnRwbWFwOiBmdW5jdGlvbiAobGluZSkge1xuICAgICAgICB2YXIgcGFydHMgPSBsaW5lLnN1YnN0cmluZyg5KS5zcGxpdCgnICcpLFxuICAgICAgICAgICAgZGF0YSA9IHt9O1xuICAgICAgICBkYXRhLmlkID0gcGFydHMuc2hpZnQoKTtcbiAgICAgICAgcGFydHMgPSBwYXJ0c1swXS5zcGxpdCgnLycpO1xuICAgICAgICBkYXRhLm5hbWUgPSBwYXJ0cy5zaGlmdCgpO1xuICAgICAgICBkYXRhLmNsb2NrcmF0ZSA9IHBhcnRzLnNoaWZ0KCk7XG4gICAgICAgIGRhdGEuY2hhbm5lbHMgPSBwYXJ0cy5sZW5ndGggPyBwYXJ0cy5zaGlmdCgpIDogJzEnO1xuICAgICAgICByZXR1cm4gZGF0YTtcbiAgICB9LFxuICAgIC8qKlxuICAgICAqIFBhcnNlcyBTRFAgbGluZSBcImE9c2N0cG1hcDouLi5cIiBhbmQgZXh0cmFjdHMgU0NUUCBwb3J0IGZyb20gaXQuXG4gICAgICogQHBhcmFtIGxpbmUgZWcuIFwiYT1zY3RwbWFwOjUwMDAgd2VicnRjLWRhdGFjaGFubmVsXCJcbiAgICAgKiBAcmV0dXJucyBbU0NUUCBwb3J0IG51bWJlciwgcHJvdG9jb2wsIHN0cmVhbXNdXG4gICAgICovXG4gICAgcGFyc2Vfc2N0cG1hcDogZnVuY3Rpb24gKGxpbmUpXG4gICAge1xuICAgICAgICB2YXIgcGFydHMgPSBsaW5lLnN1YnN0cmluZygxMCkuc3BsaXQoJyAnKTtcbiAgICAgICAgdmFyIHNjdHBQb3J0ID0gcGFydHNbMF07XG4gICAgICAgIHZhciBwcm90b2NvbCA9IHBhcnRzWzFdO1xuICAgICAgICAvLyBTdHJlYW0gY291bnQgaXMgb3B0aW9uYWxcbiAgICAgICAgdmFyIHN0cmVhbUNvdW50ID0gcGFydHMubGVuZ3RoID4gMiA/IHBhcnRzWzJdIDogbnVsbDtcbiAgICAgICAgcmV0dXJuIFtzY3RwUG9ydCwgcHJvdG9jb2wsIHN0cmVhbUNvdW50XTsvLyBTQ1RQIHBvcnRcbiAgICB9LFxuICAgIGJ1aWxkX3J0cG1hcDogZnVuY3Rpb24gKGVsKSB7XG4gICAgICAgIHZhciBsaW5lID0gJ2E9cnRwbWFwOicgKyBlbC5nZXRBdHRyaWJ1dGUoJ2lkJykgKyAnICcgKyBlbC5nZXRBdHRyaWJ1dGUoJ25hbWUnKSArICcvJyArIGVsLmdldEF0dHJpYnV0ZSgnY2xvY2tyYXRlJyk7XG4gICAgICAgIGlmIChlbC5nZXRBdHRyaWJ1dGUoJ2NoYW5uZWxzJykgJiYgZWwuZ2V0QXR0cmlidXRlKCdjaGFubmVscycpICE9ICcxJykge1xuICAgICAgICAgICAgbGluZSArPSAnLycgKyBlbC5nZXRBdHRyaWJ1dGUoJ2NoYW5uZWxzJyk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGxpbmU7XG4gICAgfSxcbiAgICBwYXJzZV9jcnlwdG86IGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgIHZhciBwYXJ0cyA9IGxpbmUuc3Vic3RyaW5nKDkpLnNwbGl0KCcgJyksXG4gICAgICAgICAgICBkYXRhID0ge307XG4gICAgICAgIGRhdGEudGFnID0gcGFydHMuc2hpZnQoKTtcbiAgICAgICAgZGF0YVsnY3J5cHRvLXN1aXRlJ10gPSBwYXJ0cy5zaGlmdCgpO1xuICAgICAgICBkYXRhWydrZXktcGFyYW1zJ10gPSBwYXJ0cy5zaGlmdCgpO1xuICAgICAgICBpZiAocGFydHMubGVuZ3RoKSB7XG4gICAgICAgICAgICBkYXRhWydzZXNzaW9uLXBhcmFtcyddID0gcGFydHMuam9pbignICcpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBkYXRhO1xuICAgIH0sXG4gICAgcGFyc2VfZmluZ2VycHJpbnQ6IGZ1bmN0aW9uIChsaW5lKSB7IC8vIFJGQyA0NTcyXG4gICAgICAgIHZhciBwYXJ0cyA9IGxpbmUuc3Vic3RyaW5nKDE0KS5zcGxpdCgnICcpLFxuICAgICAgICAgICAgZGF0YSA9IHt9O1xuICAgICAgICBkYXRhLmhhc2ggPSBwYXJ0cy5zaGlmdCgpO1xuICAgICAgICBkYXRhLmZpbmdlcnByaW50ID0gcGFydHMuc2hpZnQoKTtcbiAgICAgICAgLy8gVE9ETyBhc3NlcnQgdGhhdCBmaW5nZXJwcmludCBzYXRpc2ZpZXMgMlVIRVggKihcIjpcIiAyVUhFWCkgP1xuICAgICAgICByZXR1cm4gZGF0YTtcbiAgICB9LFxuICAgIHBhcnNlX2ZtdHA6IGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICAgIHZhciBwYXJ0cyA9IGxpbmUuc3BsaXQoJyAnKSxcbiAgICAgICAgICAgIGksIGtleSwgdmFsdWUsXG4gICAgICAgICAgICBkYXRhID0gW107XG4gICAgICAgIHBhcnRzLnNoaWZ0KCk7XG4gICAgICAgIHBhcnRzID0gcGFydHMuam9pbignICcpLnNwbGl0KCc7Jyk7XG4gICAgICAgIGZvciAoaSA9IDA7IGkgPCBwYXJ0cy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAga2V5ID0gcGFydHNbaV0uc3BsaXQoJz0nKVswXTtcbiAgICAgICAgICAgIHdoaWxlIChrZXkubGVuZ3RoICYmIGtleVswXSA9PSAnICcpIHtcbiAgICAgICAgICAgICAgICBrZXkgPSBrZXkuc3Vic3RyaW5nKDEpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFsdWUgPSBwYXJ0c1tpXS5zcGxpdCgnPScpWzFdO1xuICAgICAgICAgICAgaWYgKGtleSAmJiB2YWx1ZSkge1xuICAgICAgICAgICAgICAgIGRhdGEucHVzaCh7bmFtZToga2V5LCB2YWx1ZTogdmFsdWV9KTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoa2V5KSB7XG4gICAgICAgICAgICAgICAgLy8gcmZjIDQ3MzMgKERUTUYpIHN0eWxlIHN0dWZmXG4gICAgICAgICAgICAgICAgZGF0YS5wdXNoKHtuYW1lOiAnJywgdmFsdWU6IGtleX0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBkYXRhO1xuICAgIH0sXG4gICAgcGFyc2VfaWNlY2FuZGlkYXRlOiBmdW5jdGlvbiAobGluZSkge1xuICAgICAgICB2YXIgY2FuZGlkYXRlID0ge30sXG4gICAgICAgICAgICBlbGVtcyA9IGxpbmUuc3BsaXQoJyAnKTtcbiAgICAgICAgY2FuZGlkYXRlLmZvdW5kYXRpb24gPSBlbGVtc1swXS5zdWJzdHJpbmcoMTIpO1xuICAgICAgICBjYW5kaWRhdGUuY29tcG9uZW50ID0gZWxlbXNbMV07XG4gICAgICAgIGNhbmRpZGF0ZS5wcm90b2NvbCA9IGVsZW1zWzJdLnRvTG93ZXJDYXNlKCk7XG4gICAgICAgIGNhbmRpZGF0ZS5wcmlvcml0eSA9IGVsZW1zWzNdO1xuICAgICAgICBjYW5kaWRhdGUuaXAgPSBlbGVtc1s0XTtcbiAgICAgICAgY2FuZGlkYXRlLnBvcnQgPSBlbGVtc1s1XTtcbiAgICAgICAgLy8gZWxlbXNbNl0gPT4gXCJ0eXBcIlxuICAgICAgICBjYW5kaWRhdGUudHlwZSA9IGVsZW1zWzddO1xuICAgICAgICBjYW5kaWRhdGUuZ2VuZXJhdGlvbiA9IDA7IC8vIGRlZmF1bHQgdmFsdWUsIG1heSBiZSBvdmVyd3JpdHRlbiBiZWxvd1xuICAgICAgICBmb3IgKHZhciBpID0gODsgaSA8IGVsZW1zLmxlbmd0aDsgaSArPSAyKSB7XG4gICAgICAgICAgICBzd2l0Y2ggKGVsZW1zW2ldKSB7XG4gICAgICAgICAgICAgICAgY2FzZSAncmFkZHInOlxuICAgICAgICAgICAgICAgICAgICBjYW5kaWRhdGVbJ3JlbC1hZGRyJ10gPSBlbGVtc1tpICsgMV07XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGNhc2UgJ3Jwb3J0JzpcbiAgICAgICAgICAgICAgICAgICAgY2FuZGlkYXRlWydyZWwtcG9ydCddID0gZWxlbXNbaSArIDFdO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlICdnZW5lcmF0aW9uJzpcbiAgICAgICAgICAgICAgICAgICAgY2FuZGlkYXRlLmdlbmVyYXRpb24gPSBlbGVtc1tpICsgMV07XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGNhc2UgJ3RjcHR5cGUnOlxuICAgICAgICAgICAgICAgICAgICBjYW5kaWRhdGUudGNwdHlwZSA9IGVsZW1zW2kgKyAxXTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgZGVmYXVsdDogLy8gVE9ET1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygncGFyc2VfaWNlY2FuZGlkYXRlIG5vdCB0cmFuc2xhdGluZyBcIicgKyBlbGVtc1tpXSArICdcIiA9IFwiJyArIGVsZW1zW2kgKyAxXSArICdcIicpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGNhbmRpZGF0ZS5uZXR3b3JrID0gJzEnO1xuICAgICAgICBjYW5kaWRhdGUuaWQgPSBNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zdWJzdHIoMiwgMTApOyAvLyBub3QgYXBwbGljYWJsZSB0byBTRFAgLS0gRklYTUU6IHNob3VsZCBiZSB1bmlxdWUsIG5vdCBqdXN0IHJhbmRvbVxuICAgICAgICByZXR1cm4gY2FuZGlkYXRlO1xuICAgIH0sXG4gICAgYnVpbGRfaWNlY2FuZGlkYXRlOiBmdW5jdGlvbiAoY2FuZCkge1xuICAgICAgICB2YXIgbGluZSA9IFsnYT1jYW5kaWRhdGU6JyArIGNhbmQuZm91bmRhdGlvbiwgY2FuZC5jb21wb25lbnQsIGNhbmQucHJvdG9jb2wsIGNhbmQucHJpb3JpdHksIGNhbmQuaXAsIGNhbmQucG9ydCwgJ3R5cCcsIGNhbmQudHlwZV0uam9pbignICcpO1xuICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgc3dpdGNoIChjYW5kLnR5cGUpIHtcbiAgICAgICAgICAgIGNhc2UgJ3NyZmx4JzpcbiAgICAgICAgICAgIGNhc2UgJ3ByZmx4JzpcbiAgICAgICAgICAgIGNhc2UgJ3JlbGF5JzpcbiAgICAgICAgICAgICAgICBpZiAoY2FuZC5oYXNPd25BdHRyaWJ1dGUoJ3JlbC1hZGRyJykgJiYgY2FuZC5oYXNPd25BdHRyaWJ1dGUoJ3JlbC1wb3J0JykpIHtcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSAncmFkZHInO1xuICAgICAgICAgICAgICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSBjYW5kWydyZWwtYWRkciddO1xuICAgICAgICAgICAgICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSAncnBvcnQnO1xuICAgICAgICAgICAgICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSBjYW5kWydyZWwtcG9ydCddO1xuICAgICAgICAgICAgICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGNhbmQuaGFzT3duQXR0cmlidXRlKCd0Y3B0eXBlJykpIHtcbiAgICAgICAgICAgIGxpbmUgKz0gJ3RjcHR5cGUnO1xuICAgICAgICAgICAgbGluZSArPSAnICc7XG4gICAgICAgICAgICBsaW5lICs9IGNhbmQudGNwdHlwZTtcbiAgICAgICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICB9XG4gICAgICAgIGxpbmUgKz0gJ2dlbmVyYXRpb24nO1xuICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgbGluZSArPSBjYW5kLmhhc093bkF0dHJpYnV0ZSgnZ2VuZXJhdGlvbicpID8gY2FuZC5nZW5lcmF0aW9uIDogJzAnO1xuICAgICAgICByZXR1cm4gbGluZTtcbiAgICB9LFxuICAgIHBhcnNlX3NzcmM6IGZ1bmN0aW9uIChkZXNjKSB7XG4gICAgICAgIC8vIHByb3ByaWV0YXJ5IG1hcHBpbmcgb2YgYT1zc3JjIGxpbmVzXG4gICAgICAgIC8vIFRPRE86IHNlZSBcIkppbmdsZSBSVFAgU291cmNlIERlc2NyaXB0aW9uXCIgYnkgSnViZXJ0aSBhbmQgUC4gVGhhdGNoZXIgb24gZ29vZ2xlIGRvY3NcbiAgICAgICAgLy8gYW5kIHBhcnNlIGFjY29yZGluZyB0byB0aGF0XG4gICAgICAgIHZhciBsaW5lcyA9IGRlc2Muc3BsaXQoJ1xcclxcbicpLFxuICAgICAgICAgICAgZGF0YSA9IHt9O1xuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxpbmVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBpZiAobGluZXNbaV0uc3Vic3RyaW5nKDAsIDcpID09ICdhPXNzcmM6Jykge1xuICAgICAgICAgICAgICAgIHZhciBpZHggPSBsaW5lc1tpXS5pbmRleE9mKCcgJyk7XG4gICAgICAgICAgICAgICAgZGF0YVtsaW5lc1tpXS5zdWJzdHIoaWR4ICsgMSkuc3BsaXQoJzonLCAyKVswXV0gPSBsaW5lc1tpXS5zdWJzdHIoaWR4ICsgMSkuc3BsaXQoJzonLCAyKVsxXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZGF0YTtcbiAgICB9LFxuICAgIHBhcnNlX3J0Y3BmYjogZnVuY3Rpb24gKGxpbmUpIHtcbiAgICAgICAgdmFyIHBhcnRzID0gbGluZS5zdWJzdHIoMTApLnNwbGl0KCcgJyk7XG4gICAgICAgIHZhciBkYXRhID0ge307XG4gICAgICAgIGRhdGEucHQgPSBwYXJ0cy5zaGlmdCgpO1xuICAgICAgICBkYXRhLnR5cGUgPSBwYXJ0cy5zaGlmdCgpO1xuICAgICAgICBkYXRhLnBhcmFtcyA9IHBhcnRzO1xuICAgICAgICByZXR1cm4gZGF0YTtcbiAgICB9LFxuICAgIHBhcnNlX2V4dG1hcDogZnVuY3Rpb24gKGxpbmUpIHtcbiAgICAgICAgdmFyIHBhcnRzID0gbGluZS5zdWJzdHIoOSkuc3BsaXQoJyAnKTtcbiAgICAgICAgdmFyIGRhdGEgPSB7fTtcbiAgICAgICAgZGF0YS52YWx1ZSA9IHBhcnRzLnNoaWZ0KCk7XG4gICAgICAgIGlmIChkYXRhLnZhbHVlLmluZGV4T2YoJy8nKSAhPSAtMSkge1xuICAgICAgICAgICAgZGF0YS5kaXJlY3Rpb24gPSBkYXRhLnZhbHVlLnN1YnN0cihkYXRhLnZhbHVlLmluZGV4T2YoJy8nKSArIDEpO1xuICAgICAgICAgICAgZGF0YS52YWx1ZSA9IGRhdGEudmFsdWUuc3Vic3RyKDAsIGRhdGEudmFsdWUuaW5kZXhPZignLycpKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGRhdGEuZGlyZWN0aW9uID0gJ2JvdGgnO1xuICAgICAgICB9XG4gICAgICAgIGRhdGEudXJpID0gcGFydHMuc2hpZnQoKTtcbiAgICAgICAgZGF0YS5wYXJhbXMgPSBwYXJ0cztcbiAgICAgICAgcmV0dXJuIGRhdGE7XG4gICAgfSxcbiAgICBmaW5kX2xpbmU6IGZ1bmN0aW9uIChoYXlzdGFjaywgbmVlZGxlLCBzZXNzaW9ucGFydCkge1xuICAgICAgICB2YXIgbGluZXMgPSBoYXlzdGFjay5zcGxpdCgnXFxyXFxuJyk7XG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbGluZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGlmIChsaW5lc1tpXS5zdWJzdHJpbmcoMCwgbmVlZGxlLmxlbmd0aCkgPT0gbmVlZGxlKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGxpbmVzW2ldO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGlmICghc2Vzc2lvbnBhcnQpIHtcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICAvLyBzZWFyY2ggc2Vzc2lvbiBwYXJ0XG4gICAgICAgIGxpbmVzID0gc2Vzc2lvbnBhcnQuc3BsaXQoJ1xcclxcbicpO1xuICAgICAgICBmb3IgKHZhciBqID0gMDsgaiA8IGxpbmVzLmxlbmd0aDsgaisrKSB7XG4gICAgICAgICAgICBpZiAobGluZXNbal0uc3Vic3RyaW5nKDAsIG5lZWRsZS5sZW5ndGgpID09IG5lZWRsZSkge1xuICAgICAgICAgICAgICAgIHJldHVybiBsaW5lc1tqXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfSxcbiAgICBmaW5kX2xpbmVzOiBmdW5jdGlvbiAoaGF5c3RhY2ssIG5lZWRsZSwgc2Vzc2lvbnBhcnQpIHtcbiAgICAgICAgdmFyIGxpbmVzID0gaGF5c3RhY2suc3BsaXQoJ1xcclxcbicpLFxuICAgICAgICAgICAgbmVlZGxlcyA9IFtdO1xuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxpbmVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBpZiAobGluZXNbaV0uc3Vic3RyaW5nKDAsIG5lZWRsZS5sZW5ndGgpID09IG5lZWRsZSlcbiAgICAgICAgICAgICAgICBuZWVkbGVzLnB1c2gobGluZXNbaV0pO1xuICAgICAgICB9XG4gICAgICAgIGlmIChuZWVkbGVzLmxlbmd0aCB8fCAhc2Vzc2lvbnBhcnQpIHtcbiAgICAgICAgICAgIHJldHVybiBuZWVkbGVzO1xuICAgICAgICB9XG4gICAgICAgIC8vIHNlYXJjaCBzZXNzaW9uIHBhcnRcbiAgICAgICAgbGluZXMgPSBzZXNzaW9ucGFydC5zcGxpdCgnXFxyXFxuJyk7XG4gICAgICAgIGZvciAodmFyIGogPSAwOyBqIDwgbGluZXMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgIGlmIChsaW5lc1tqXS5zdWJzdHJpbmcoMCwgbmVlZGxlLmxlbmd0aCkgPT0gbmVlZGxlKSB7XG4gICAgICAgICAgICAgICAgbmVlZGxlcy5wdXNoKGxpbmVzW2pdKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbmVlZGxlcztcbiAgICB9LFxuICAgIGNhbmRpZGF0ZVRvSmluZ2xlOiBmdW5jdGlvbiAobGluZSkge1xuICAgICAgICAvLyBhPWNhbmRpZGF0ZToyOTc5MTY2NjYyIDEgdWRwIDIxMTM5MzcxNTEgMTkyLjE2OC4yLjEwMCA1NzY5OCB0eXAgaG9zdCBnZW5lcmF0aW9uIDBcbiAgICAgICAgLy8gICAgICA8Y2FuZGlkYXRlIGNvbXBvbmVudD0uLi4gZm91bmRhdGlvbj0uLi4gZ2VuZXJhdGlvbj0uLi4gaWQ9Li4uIGlwPS4uLiBuZXR3b3JrPS4uLiBwb3J0PS4uLiBwcmlvcml0eT0uLi4gcHJvdG9jb2w9Li4uIHR5cGU9Li4uLz5cbiAgICAgICAgaWYgKGxpbmUuaW5kZXhPZignY2FuZGlkYXRlOicpID09PSAwKSB7XG4gICAgICAgICAgICBsaW5lID0gJ2E9JyArIGxpbmU7XG4gICAgICAgIH0gZWxzZSBpZiAobGluZS5zdWJzdHJpbmcoMCwgMTIpICE9ICdhPWNhbmRpZGF0ZTonKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygncGFyc2VDYW5kaWRhdGUgY2FsbGVkIHdpdGggYSBsaW5lIHRoYXQgaXMgbm90IGEgY2FuZGlkYXRlIGxpbmUnKTtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKGxpbmUpO1xuICAgICAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGxpbmUuc3Vic3RyaW5nKGxpbmUubGVuZ3RoIC0gMikgPT0gJ1xcclxcbicpIC8vIGNob21wIGl0XG4gICAgICAgICAgICBsaW5lID0gbGluZS5zdWJzdHJpbmcoMCwgbGluZS5sZW5ndGggLSAyKTtcbiAgICAgICAgdmFyIGNhbmRpZGF0ZSA9IHt9LFxuICAgICAgICAgICAgZWxlbXMgPSBsaW5lLnNwbGl0KCcgJyksXG4gICAgICAgICAgICBpO1xuICAgICAgICBpZiAoZWxlbXNbNl0gIT0gJ3R5cCcpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdkaWQgbm90IGZpbmQgdHlwIGluIHRoZSByaWdodCBwbGFjZScpO1xuICAgICAgICAgICAgY29uc29sZS5sb2cobGluZSk7XG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgfVxuICAgICAgICBjYW5kaWRhdGUuZm91bmRhdGlvbiA9IGVsZW1zWzBdLnN1YnN0cmluZygxMik7XG4gICAgICAgIGNhbmRpZGF0ZS5jb21wb25lbnQgPSBlbGVtc1sxXTtcbiAgICAgICAgY2FuZGlkYXRlLnByb3RvY29sID0gZWxlbXNbMl0udG9Mb3dlckNhc2UoKTtcbiAgICAgICAgY2FuZGlkYXRlLnByaW9yaXR5ID0gZWxlbXNbM107XG4gICAgICAgIGNhbmRpZGF0ZS5pcCA9IGVsZW1zWzRdO1xuICAgICAgICBjYW5kaWRhdGUucG9ydCA9IGVsZW1zWzVdO1xuICAgICAgICAvLyBlbGVtc1s2XSA9PiBcInR5cFwiXG4gICAgICAgIGNhbmRpZGF0ZS50eXBlID0gZWxlbXNbN107XG5cbiAgICAgICAgY2FuZGlkYXRlLmdlbmVyYXRpb24gPSAnMCc7IC8vIGRlZmF1bHQsIG1heSBiZSBvdmVyd3JpdHRlbiBiZWxvd1xuICAgICAgICBmb3IgKGkgPSA4OyBpIDwgZWxlbXMubGVuZ3RoOyBpICs9IDIpIHtcbiAgICAgICAgICAgIHN3aXRjaCAoZWxlbXNbaV0pIHtcbiAgICAgICAgICAgICAgICBjYXNlICdyYWRkcic6XG4gICAgICAgICAgICAgICAgICAgIGNhbmRpZGF0ZVsncmVsLWFkZHInXSA9IGVsZW1zW2kgKyAxXTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAncnBvcnQnOlxuICAgICAgICAgICAgICAgICAgICBjYW5kaWRhdGVbJ3JlbC1wb3J0J10gPSBlbGVtc1tpICsgMV07XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGNhc2UgJ2dlbmVyYXRpb24nOlxuICAgICAgICAgICAgICAgICAgICBjYW5kaWRhdGUuZ2VuZXJhdGlvbiA9IGVsZW1zW2kgKyAxXTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAndGNwdHlwZSc6XG4gICAgICAgICAgICAgICAgICAgIGNhbmRpZGF0ZS50Y3B0eXBlID0gZWxlbXNbaSArIDFdO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBkZWZhdWx0OiAvLyBUT0RPXG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdub3QgdHJhbnNsYXRpbmcgXCInICsgZWxlbXNbaV0gKyAnXCIgPSBcIicgKyBlbGVtc1tpICsgMV0gKyAnXCInKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBjYW5kaWRhdGUubmV0d29yayA9ICcxJztcbiAgICAgICAgY2FuZGlkYXRlLmlkID0gTWF0aC5yYW5kb20oKS50b1N0cmluZygzNikuc3Vic3RyKDIsIDEwKTsgLy8gbm90IGFwcGxpY2FibGUgdG8gU0RQIC0tIEZJWE1FOiBzaG91bGQgYmUgdW5pcXVlLCBub3QganVzdCByYW5kb21cbiAgICAgICAgcmV0dXJuIGNhbmRpZGF0ZTtcbiAgICB9LFxuICAgIGNhbmRpZGF0ZUZyb21KaW5nbGU6IGZ1bmN0aW9uIChjYW5kKSB7XG4gICAgICAgIHZhciBsaW5lID0gJ2E9Y2FuZGlkYXRlOic7XG4gICAgICAgIGxpbmUgKz0gY2FuZC5nZXRBdHRyaWJ1dGUoJ2ZvdW5kYXRpb24nKTtcbiAgICAgICAgbGluZSArPSAnICc7XG4gICAgICAgIGxpbmUgKz0gY2FuZC5nZXRBdHRyaWJ1dGUoJ2NvbXBvbmVudCcpO1xuICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgbGluZSArPSBjYW5kLmdldEF0dHJpYnV0ZSgncHJvdG9jb2wnKTsgLy8udG9VcHBlckNhc2UoKTsgLy8gY2hyb21lIE0yMyBkb2Vzbid0IGxpa2UgdGhpc1xuICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgbGluZSArPSBjYW5kLmdldEF0dHJpYnV0ZSgncHJpb3JpdHknKTtcbiAgICAgICAgbGluZSArPSAnICc7XG4gICAgICAgIGxpbmUgKz0gY2FuZC5nZXRBdHRyaWJ1dGUoJ2lwJyk7XG4gICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICBsaW5lICs9IGNhbmQuZ2V0QXR0cmlidXRlKCdwb3J0Jyk7XG4gICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICBsaW5lICs9ICd0eXAnO1xuICAgICAgICBsaW5lICs9ICcgJyArIGNhbmQuZ2V0QXR0cmlidXRlKCd0eXBlJyk7XG4gICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICBzd2l0Y2ggKGNhbmQuZ2V0QXR0cmlidXRlKCd0eXBlJykpIHtcbiAgICAgICAgICAgIGNhc2UgJ3NyZmx4JzpcbiAgICAgICAgICAgIGNhc2UgJ3ByZmx4JzpcbiAgICAgICAgICAgIGNhc2UgJ3JlbGF5JzpcbiAgICAgICAgICAgICAgICBpZiAoY2FuZC5nZXRBdHRyaWJ1dGUoJ3JlbC1hZGRyJykgJiYgY2FuZC5nZXRBdHRyaWJ1dGUoJ3JlbC1wb3J0JykpIHtcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSAncmFkZHInO1xuICAgICAgICAgICAgICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSBjYW5kLmdldEF0dHJpYnV0ZSgncmVsLWFkZHInKTtcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSAnICc7XG4gICAgICAgICAgICAgICAgICAgIGxpbmUgKz0gJ3Jwb3J0JztcbiAgICAgICAgICAgICAgICAgICAgbGluZSArPSAnICc7XG4gICAgICAgICAgICAgICAgICAgIGxpbmUgKz0gY2FuZC5nZXRBdHRyaWJ1dGUoJ3JlbC1wb3J0Jyk7XG4gICAgICAgICAgICAgICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgICBpZiAoY2FuZC5nZXRBdHRyaWJ1dGUoJ3Byb3RvY29sJykudG9Mb3dlckNhc2UoKSA9PSAndGNwJykge1xuICAgICAgICAgICAgbGluZSArPSAndGNwdHlwZSc7XG4gICAgICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgICAgIGxpbmUgKz0gY2FuZC5nZXRBdHRyaWJ1dGUoJ3RjcHR5cGUnKTtcbiAgICAgICAgICAgIGxpbmUgKz0gJyAnO1xuICAgICAgICB9XG4gICAgICAgIGxpbmUgKz0gJ2dlbmVyYXRpb24nO1xuICAgICAgICBsaW5lICs9ICcgJztcbiAgICAgICAgbGluZSArPSBjYW5kLmdldEF0dHJpYnV0ZSgnZ2VuZXJhdGlvbicpIHx8ICcwJztcbiAgICAgICAgcmV0dXJuIGxpbmUgKyAnXFxyXFxuJztcbiAgICB9XG59O1xubW9kdWxlLmV4cG9ydHMgPSBTRFBVdGlsOyIsImZ1bmN0aW9uIFRyYWNlYWJsZVBlZXJDb25uZWN0aW9uKGljZV9jb25maWcsIGNvbnN0cmFpbnRzKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIHZhciBSVENQZWVyY29ubmVjdGlvbiA9IG5hdmlnYXRvci5tb3pHZXRVc2VyTWVkaWEgPyBtb3pSVENQZWVyQ29ubmVjdGlvbiA6IHdlYmtpdFJUQ1BlZXJDb25uZWN0aW9uO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24gPSBuZXcgUlRDUGVlcmNvbm5lY3Rpb24oaWNlX2NvbmZpZywgY29uc3RyYWludHMpO1xuICAgIHRoaXMudXBkYXRlTG9nID0gW107XG4gICAgdGhpcy5zdGF0cyA9IHt9O1xuICAgIHRoaXMuc3RhdHNpbnRlcnZhbCA9IG51bGw7XG4gICAgdGhpcy5tYXhzdGF0cyA9IDA7IC8vIGxpbWl0IHRvIDMwMCB2YWx1ZXMsIGkuZS4gNSBtaW51dGVzOyBzZXQgdG8gMCB0byBkaXNhYmxlXG5cbiAgICAvLyBvdmVycmlkZSBhcyBkZXNpcmVkXG4gICAgdGhpcy50cmFjZSA9IGZ1bmN0aW9uICh3aGF0LCBpbmZvKSB7XG4gICAgICAgIC8vY29uc29sZS53YXJuKCdXVFJBQ0UnLCB3aGF0LCBpbmZvKTtcbiAgICAgICAgc2VsZi51cGRhdGVMb2cucHVzaCh7XG4gICAgICAgICAgICB0aW1lOiBuZXcgRGF0ZSgpLFxuICAgICAgICAgICAgdHlwZTogd2hhdCxcbiAgICAgICAgICAgIHZhbHVlOiBpbmZvIHx8IFwiXCJcbiAgICAgICAgfSk7XG4gICAgfTtcbiAgICB0aGlzLm9uaWNlY2FuZGlkYXRlID0gbnVsbDtcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLm9uaWNlY2FuZGlkYXRlID0gZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgIHNlbGYudHJhY2UoJ29uaWNlY2FuZGlkYXRlJywgSlNPTi5zdHJpbmdpZnkoZXZlbnQuY2FuZGlkYXRlLCBudWxsLCAnICcpKTtcbiAgICAgICAgaWYgKHNlbGYub25pY2VjYW5kaWRhdGUgIT09IG51bGwpIHtcbiAgICAgICAgICAgIHNlbGYub25pY2VjYW5kaWRhdGUoZXZlbnQpO1xuICAgICAgICB9XG4gICAgfTtcbiAgICB0aGlzLm9uYWRkc3RyZWFtID0gbnVsbDtcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLm9uYWRkc3RyZWFtID0gZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgIHNlbGYudHJhY2UoJ29uYWRkc3RyZWFtJywgZXZlbnQuc3RyZWFtLmlkKTtcbiAgICAgICAgaWYgKHNlbGYub25hZGRzdHJlYW0gIT09IG51bGwpIHtcbiAgICAgICAgICAgIHNlbGYub25hZGRzdHJlYW0oZXZlbnQpO1xuICAgICAgICB9XG4gICAgfTtcbiAgICB0aGlzLm9ucmVtb3Zlc3RyZWFtID0gbnVsbDtcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLm9ucmVtb3Zlc3RyZWFtID0gZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgIHNlbGYudHJhY2UoJ29ucmVtb3Zlc3RyZWFtJywgZXZlbnQuc3RyZWFtLmlkKTtcbiAgICAgICAgaWYgKHNlbGYub25yZW1vdmVzdHJlYW0gIT09IG51bGwpIHtcbiAgICAgICAgICAgIHNlbGYub25yZW1vdmVzdHJlYW0oZXZlbnQpO1xuICAgICAgICB9XG4gICAgfTtcbiAgICB0aGlzLm9uc2lnbmFsaW5nc3RhdGVjaGFuZ2UgPSBudWxsO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24ub25zaWduYWxpbmdzdGF0ZWNoYW5nZSA9IGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgICBzZWxmLnRyYWNlKCdvbnNpZ25hbGluZ3N0YXRlY2hhbmdlJywgc2VsZi5zaWduYWxpbmdTdGF0ZSk7XG4gICAgICAgIGlmIChzZWxmLm9uc2lnbmFsaW5nc3RhdGVjaGFuZ2UgIT09IG51bGwpIHtcbiAgICAgICAgICAgIHNlbGYub25zaWduYWxpbmdzdGF0ZWNoYW5nZShldmVudCk7XG4gICAgICAgIH1cbiAgICB9O1xuICAgIHRoaXMub25pY2Vjb25uZWN0aW9uc3RhdGVjaGFuZ2UgPSBudWxsO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24ub25pY2Vjb25uZWN0aW9uc3RhdGVjaGFuZ2UgPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgc2VsZi50cmFjZSgnb25pY2Vjb25uZWN0aW9uc3RhdGVjaGFuZ2UnLCBzZWxmLmljZUNvbm5lY3Rpb25TdGF0ZSk7XG4gICAgICAgIGlmIChzZWxmLm9uaWNlY29ubmVjdGlvbnN0YXRlY2hhbmdlICE9PSBudWxsKSB7XG4gICAgICAgICAgICBzZWxmLm9uaWNlY29ubmVjdGlvbnN0YXRlY2hhbmdlKGV2ZW50KTtcbiAgICAgICAgfVxuICAgIH07XG4gICAgdGhpcy5vbm5lZ290aWF0aW9ubmVlZGVkID0gbnVsbDtcbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLm9ubmVnb3RpYXRpb25uZWVkZWQgPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgc2VsZi50cmFjZSgnb25uZWdvdGlhdGlvbm5lZWRlZCcpO1xuICAgICAgICBpZiAoc2VsZi5vbm5lZ290aWF0aW9ubmVlZGVkICE9PSBudWxsKSB7XG4gICAgICAgICAgICBzZWxmLm9ubmVnb3RpYXRpb25uZWVkZWQoZXZlbnQpO1xuICAgICAgICB9XG4gICAgfTtcbiAgICBzZWxmLm9uZGF0YWNoYW5uZWwgPSBudWxsO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24ub25kYXRhY2hhbm5lbCA9IGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgICBzZWxmLnRyYWNlKCdvbmRhdGFjaGFubmVsJywgZXZlbnQpO1xuICAgICAgICBpZiAoc2VsZi5vbmRhdGFjaGFubmVsICE9PSBudWxsKSB7XG4gICAgICAgICAgICBzZWxmLm9uZGF0YWNoYW5uZWwoZXZlbnQpO1xuICAgICAgICB9XG4gICAgfTtcbiAgICBpZiAoIW5hdmlnYXRvci5tb3pHZXRVc2VyTWVkaWEgJiYgdGhpcy5tYXhzdGF0cykge1xuICAgICAgICB0aGlzLnN0YXRzaW50ZXJ2YWwgPSB3aW5kb3cuc2V0SW50ZXJ2YWwoZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICBzZWxmLnBlZXJjb25uZWN0aW9uLmdldFN0YXRzKGZ1bmN0aW9uKHN0YXRzKSB7XG4gICAgICAgICAgICAgICAgdmFyIHJlc3VsdHMgPSBzdGF0cy5yZXN1bHQoKTtcbiAgICAgICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHJlc3VsdHMubGVuZ3RoOyArK2kpIHtcbiAgICAgICAgICAgICAgICAgICAgLy9jb25zb2xlLmxvZyhyZXN1bHRzW2ldLnR5cGUsIHJlc3VsdHNbaV0uaWQsIHJlc3VsdHNbaV0ubmFtZXMoKSlcbiAgICAgICAgICAgICAgICAgICAgdmFyIG5vdyA9IG5ldyBEYXRlKCk7XG4gICAgICAgICAgICAgICAgICAgIHJlc3VsdHNbaV0ubmFtZXMoKS5mb3JFYWNoKGZ1bmN0aW9uIChuYW1lKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgaWQgPSByZXN1bHRzW2ldLmlkICsgJy0nICsgbmFtZTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICghc2VsZi5zdGF0c1tpZF0pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxmLnN0YXRzW2lkXSA9IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhcnRUaW1lOiBub3csXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuZFRpbWU6IG5vdyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzOiBbXSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGltZXM6IFtdXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuc3RhdHNbaWRdLnZhbHVlcy5wdXNoKHJlc3VsdHNbaV0uc3RhdChuYW1lKSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLnN0YXRzW2lkXS50aW1lcy5wdXNoKG5vdy5nZXRUaW1lKCkpO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHNlbGYuc3RhdHNbaWRdLnZhbHVlcy5sZW5ndGggPiBzZWxmLm1heHN0YXRzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5zdGF0c1tpZF0udmFsdWVzLnNoaWZ0KCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5zdGF0c1tpZF0udGltZXMuc2hpZnQoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuc3RhdHNbaWRdLmVuZFRpbWUgPSBub3c7XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgIH0sIDEwMDApO1xuICAgIH1cbn07XG5cbmR1bXBTRFAgPSBmdW5jdGlvbihkZXNjcmlwdGlvbikge1xuICAgIHJldHVybiAndHlwZTogJyArIGRlc2NyaXB0aW9uLnR5cGUgKyAnXFxyXFxuJyArIGRlc2NyaXB0aW9uLnNkcDtcbn1cblxuaWYgKFRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fX2RlZmluZUdldHRlcl9fICE9PSB1bmRlZmluZWQpIHtcbiAgICBUcmFjZWFibGVQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX19kZWZpbmVHZXR0ZXJfXygnc2lnbmFsaW5nU3RhdGUnLCBmdW5jdGlvbigpIHsgcmV0dXJuIHRoaXMucGVlcmNvbm5lY3Rpb24uc2lnbmFsaW5nU3RhdGU7IH0pO1xuICAgIFRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5fX2RlZmluZUdldHRlcl9fKCdpY2VDb25uZWN0aW9uU3RhdGUnLCBmdW5jdGlvbigpIHsgcmV0dXJuIHRoaXMucGVlcmNvbm5lY3Rpb24uaWNlQ29ubmVjdGlvblN0YXRlOyB9KTtcbiAgICBUcmFjZWFibGVQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX19kZWZpbmVHZXR0ZXJfXygnbG9jYWxEZXNjcmlwdGlvbicsIGZ1bmN0aW9uKCkge1xuICAgICAgICB2YXIgcHVibGljTG9jYWxEZXNjcmlwdGlvbiA9IHNpbXVsY2FzdC5yZXZlcnNlVHJhbnNmb3JtTG9jYWxEZXNjcmlwdGlvbih0aGlzLnBlZXJjb25uZWN0aW9uLmxvY2FsRGVzY3JpcHRpb24pO1xuICAgICAgICByZXR1cm4gcHVibGljTG9jYWxEZXNjcmlwdGlvbjtcbiAgICB9KTtcbiAgICBUcmFjZWFibGVQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuX19kZWZpbmVHZXR0ZXJfXygncmVtb3RlRGVzY3JpcHRpb24nLCBmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIHB1YmxpY1JlbW90ZURlc2NyaXB0aW9uID0gc2ltdWxjYXN0LnJldmVyc2VUcmFuc2Zvcm1SZW1vdGVEZXNjcmlwdGlvbih0aGlzLnBlZXJjb25uZWN0aW9uLnJlbW90ZURlc2NyaXB0aW9uKTtcbiAgICAgICAgcmV0dXJuIHB1YmxpY1JlbW90ZURlc2NyaXB0aW9uO1xuICAgIH0pO1xufVxuXG5UcmFjZWFibGVQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuYWRkU3RyZWFtID0gZnVuY3Rpb24gKHN0cmVhbSkge1xuICAgIHRoaXMudHJhY2UoJ2FkZFN0cmVhbScsIHN0cmVhbS5pZCk7XG4gICAgc2ltdWxjYXN0LnJlc2V0U2VuZGVyKCk7XG4gICAgdHJ5XG4gICAge1xuICAgICAgICB0aGlzLnBlZXJjb25uZWN0aW9uLmFkZFN0cmVhbShzdHJlYW0pO1xuICAgIH1cbiAgICBjYXRjaCAoZSlcbiAgICB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoZSk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG59O1xuXG5UcmFjZWFibGVQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUucmVtb3ZlU3RyZWFtID0gZnVuY3Rpb24gKHN0cmVhbSwgc3RvcFN0cmVhbXMpIHtcbiAgICB0aGlzLnRyYWNlKCdyZW1vdmVTdHJlYW0nLCBzdHJlYW0uaWQpO1xuICAgIHNpbXVsY2FzdC5yZXNldFNlbmRlcigpO1xuICAgIGlmKHN0b3BTdHJlYW1zKSB7XG4gICAgICAgIHN0cmVhbS5nZXRBdWRpb1RyYWNrcygpLmZvckVhY2goZnVuY3Rpb24gKHRyYWNrKSB7XG4gICAgICAgICAgICB0cmFjay5zdG9wKCk7XG4gICAgICAgIH0pO1xuICAgICAgICBzdHJlYW0uZ2V0VmlkZW9UcmFja3MoKS5mb3JFYWNoKGZ1bmN0aW9uICh0cmFjaykge1xuICAgICAgICAgICAgdHJhY2suc3RvcCgpO1xuICAgICAgICB9KTtcbiAgICB9XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5yZW1vdmVTdHJlYW0oc3RyZWFtKTtcbn07XG5cblRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5jcmVhdGVEYXRhQ2hhbm5lbCA9IGZ1bmN0aW9uIChsYWJlbCwgb3B0cykge1xuICAgIHRoaXMudHJhY2UoJ2NyZWF0ZURhdGFDaGFubmVsJywgbGFiZWwsIG9wdHMpO1xuICAgIHJldHVybiB0aGlzLnBlZXJjb25uZWN0aW9uLmNyZWF0ZURhdGFDaGFubmVsKGxhYmVsLCBvcHRzKTtcbn07XG5cblRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5zZXRMb2NhbERlc2NyaXB0aW9uID0gZnVuY3Rpb24gKGRlc2NyaXB0aW9uLCBzdWNjZXNzQ2FsbGJhY2ssIGZhaWx1cmVDYWxsYmFjaykge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICBkZXNjcmlwdGlvbiA9IHNpbXVsY2FzdC50cmFuc2Zvcm1Mb2NhbERlc2NyaXB0aW9uKGRlc2NyaXB0aW9uKTtcbiAgICB0aGlzLnRyYWNlKCdzZXRMb2NhbERlc2NyaXB0aW9uJywgZHVtcFNEUChkZXNjcmlwdGlvbikpO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uc2V0TG9jYWxEZXNjcmlwdGlvbihkZXNjcmlwdGlvbixcbiAgICAgICAgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgc2VsZi50cmFjZSgnc2V0TG9jYWxEZXNjcmlwdGlvbk9uU3VjY2VzcycpO1xuICAgICAgICAgICAgc3VjY2Vzc0NhbGxiYWNrKCk7XG4gICAgICAgIH0sXG4gICAgICAgIGZ1bmN0aW9uIChlcnIpIHtcbiAgICAgICAgICAgIHNlbGYudHJhY2UoJ3NldExvY2FsRGVzY3JpcHRpb25PbkZhaWx1cmUnLCBlcnIpO1xuICAgICAgICAgICAgZmFpbHVyZUNhbGxiYWNrKGVycik7XG4gICAgICAgIH1cbiAgICApO1xuICAgIC8qXG4gICAgIGlmICh0aGlzLnN0YXRzaW50ZXJ2YWwgPT09IG51bGwgJiYgdGhpcy5tYXhzdGF0cyA+IDApIHtcbiAgICAgLy8gc3RhcnQgZ2F0aGVyaW5nIHN0YXRzXG4gICAgIH1cbiAgICAgKi9cbn07XG5cblRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5zZXRSZW1vdGVEZXNjcmlwdGlvbiA9IGZ1bmN0aW9uIChkZXNjcmlwdGlvbiwgc3VjY2Vzc0NhbGxiYWNrLCBmYWlsdXJlQ2FsbGJhY2spIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgZGVzY3JpcHRpb24gPSBzaW11bGNhc3QudHJhbnNmb3JtUmVtb3RlRGVzY3JpcHRpb24oZGVzY3JpcHRpb24pO1xuICAgIHRoaXMudHJhY2UoJ3NldFJlbW90ZURlc2NyaXB0aW9uJywgZHVtcFNEUChkZXNjcmlwdGlvbikpO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uc2V0UmVtb3RlRGVzY3JpcHRpb24oZGVzY3JpcHRpb24sXG4gICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHNlbGYudHJhY2UoJ3NldFJlbW90ZURlc2NyaXB0aW9uT25TdWNjZXNzJyk7XG4gICAgICAgICAgICBzdWNjZXNzQ2FsbGJhY2soKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGVycikge1xuICAgICAgICAgICAgc2VsZi50cmFjZSgnc2V0UmVtb3RlRGVzY3JpcHRpb25PbkZhaWx1cmUnLCBlcnIpO1xuICAgICAgICAgICAgZmFpbHVyZUNhbGxiYWNrKGVycik7XG4gICAgICAgIH1cbiAgICApO1xuICAgIC8qXG4gICAgIGlmICh0aGlzLnN0YXRzaW50ZXJ2YWwgPT09IG51bGwgJiYgdGhpcy5tYXhzdGF0cyA+IDApIHtcbiAgICAgLy8gc3RhcnQgZ2F0aGVyaW5nIHN0YXRzXG4gICAgIH1cbiAgICAgKi9cbn07XG5cblRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5jbG9zZSA9IGZ1bmN0aW9uICgpIHtcbiAgICB0aGlzLnRyYWNlKCdzdG9wJyk7XG4gICAgaWYgKHRoaXMuc3RhdHNpbnRlcnZhbCAhPT0gbnVsbCkge1xuICAgICAgICB3aW5kb3cuY2xlYXJJbnRlcnZhbCh0aGlzLnN0YXRzaW50ZXJ2YWwpO1xuICAgICAgICB0aGlzLnN0YXRzaW50ZXJ2YWwgPSBudWxsO1xuICAgIH1cbiAgICB0aGlzLnBlZXJjb25uZWN0aW9uLmNsb3NlKCk7XG59O1xuXG5UcmFjZWFibGVQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuY3JlYXRlT2ZmZXIgPSBmdW5jdGlvbiAoc3VjY2Vzc0NhbGxiYWNrLCBmYWlsdXJlQ2FsbGJhY2ssIGNvbnN0cmFpbnRzKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgIHRoaXMudHJhY2UoJ2NyZWF0ZU9mZmVyJywgSlNPTi5zdHJpbmdpZnkoY29uc3RyYWludHMsIG51bGwsICcgJykpO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uY3JlYXRlT2ZmZXIoXG4gICAgICAgIGZ1bmN0aW9uIChvZmZlcikge1xuICAgICAgICAgICAgc2VsZi50cmFjZSgnY3JlYXRlT2ZmZXJPblN1Y2Nlc3MnLCBkdW1wU0RQKG9mZmVyKSk7XG4gICAgICAgICAgICBzdWNjZXNzQ2FsbGJhY2sob2ZmZXIpO1xuICAgICAgICB9LFxuICAgICAgICBmdW5jdGlvbihlcnIpIHtcbiAgICAgICAgICAgIHNlbGYudHJhY2UoJ2NyZWF0ZU9mZmVyT25GYWlsdXJlJywgZXJyKTtcbiAgICAgICAgICAgIGZhaWx1cmVDYWxsYmFjayhlcnIpO1xuICAgICAgICB9LFxuICAgICAgICBjb25zdHJhaW50c1xuICAgICk7XG59O1xuXG5UcmFjZWFibGVQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuY3JlYXRlQW5zd2VyID0gZnVuY3Rpb24gKHN1Y2Nlc3NDYWxsYmFjaywgZmFpbHVyZUNhbGxiYWNrLCBjb25zdHJhaW50cykge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB0aGlzLnRyYWNlKCdjcmVhdGVBbnN3ZXInLCBKU09OLnN0cmluZ2lmeShjb25zdHJhaW50cywgbnVsbCwgJyAnKSk7XG4gICAgdGhpcy5wZWVyY29ubmVjdGlvbi5jcmVhdGVBbnN3ZXIoXG4gICAgICAgIGZ1bmN0aW9uIChhbnN3ZXIpIHtcbiAgICAgICAgICAgIGFuc3dlciA9IHNpbXVsY2FzdC50cmFuc2Zvcm1BbnN3ZXIoYW5zd2VyKTtcbiAgICAgICAgICAgIHNlbGYudHJhY2UoJ2NyZWF0ZUFuc3dlck9uU3VjY2VzcycsIGR1bXBTRFAoYW5zd2VyKSk7XG4gICAgICAgICAgICBzdWNjZXNzQ2FsbGJhY2soYW5zd2VyKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24oZXJyKSB7XG4gICAgICAgICAgICBzZWxmLnRyYWNlKCdjcmVhdGVBbnN3ZXJPbkZhaWx1cmUnLCBlcnIpO1xuICAgICAgICAgICAgZmFpbHVyZUNhbGxiYWNrKGVycik7XG4gICAgICAgIH0sXG4gICAgICAgIGNvbnN0cmFpbnRzXG4gICAgKTtcbn07XG5cblRyYWNlYWJsZVBlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5hZGRJY2VDYW5kaWRhdGUgPSBmdW5jdGlvbiAoY2FuZGlkYXRlLCBzdWNjZXNzQ2FsbGJhY2ssIGZhaWx1cmVDYWxsYmFjaykge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICB0aGlzLnRyYWNlKCdhZGRJY2VDYW5kaWRhdGUnLCBKU09OLnN0cmluZ2lmeShjYW5kaWRhdGUsIG51bGwsICcgJykpO1xuICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uYWRkSWNlQ2FuZGlkYXRlKGNhbmRpZGF0ZSk7XG4gICAgLyogbWF5YmUgbGF0ZXJcbiAgICAgdGhpcy5wZWVyY29ubmVjdGlvbi5hZGRJY2VDYW5kaWRhdGUoY2FuZGlkYXRlLFxuICAgICBmdW5jdGlvbiAoKSB7XG4gICAgIHNlbGYudHJhY2UoJ2FkZEljZUNhbmRpZGF0ZU9uU3VjY2VzcycpO1xuICAgICBzdWNjZXNzQ2FsbGJhY2soKTtcbiAgICAgfSxcbiAgICAgZnVuY3Rpb24gKGVycikge1xuICAgICBzZWxmLnRyYWNlKCdhZGRJY2VDYW5kaWRhdGVPbkZhaWx1cmUnLCBlcnIpO1xuICAgICBmYWlsdXJlQ2FsbGJhY2soZXJyKTtcbiAgICAgfVxuICAgICApO1xuICAgICAqL1xufTtcblxuVHJhY2VhYmxlUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmdldFN0YXRzID0gZnVuY3Rpb24oY2FsbGJhY2ssIGVycmJhY2spIHtcbiAgICBpZiAobmF2aWdhdG9yLm1vekdldFVzZXJNZWRpYSkge1xuICAgICAgICAvLyBpZ25vcmUgZm9yIG5vdy4uLlxuICAgICAgICBpZighZXJyYmFjaylcbiAgICAgICAgICAgIGVycmJhY2sgPSBmdW5jdGlvbiAoKSB7XG5cbiAgICAgICAgICAgIH1cbiAgICAgICAgdGhpcy5wZWVyY29ubmVjdGlvbi5nZXRTdGF0cyhudWxsLGNhbGxiYWNrLGVycmJhY2spO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMucGVlcmNvbm5lY3Rpb24uZ2V0U3RhdHMoY2FsbGJhY2spO1xuICAgIH1cbn07XG5cbm1vZHVsZS5leHBvcnRzID0gVHJhY2VhYmxlUGVlckNvbm5lY3Rpb247XG5cbiIsIi8qIGdsb2JhbCAkLCAkaXEsIGNvbmZpZywgY29ubmVjdGlvbiwgVUksIG1lc3NhZ2VIYW5kbGVyLFxuIHJvb21OYW1lLCBzZXNzaW9uVGVybWluYXRlZCwgU3Ryb3BoZSwgVXRpbCAqL1xuLyoqXG4gKiBDb250YWlucyBsb2dpYyByZXNwb25zaWJsZSBmb3IgZW5hYmxpbmcvZGlzYWJsaW5nIGZ1bmN0aW9uYWxpdHkgYXZhaWxhYmxlXG4gKiBvbmx5IHRvIG1vZGVyYXRvciB1c2Vycy5cbiAqL1xudmFyIGNvbm5lY3Rpb24gPSBudWxsO1xudmFyIGZvY3VzVXNlckppZDtcbnZhciBnZXROZXh0VGltZW91dCA9IFV0aWwuY3JlYXRlRXhwQmFja29mZlRpbWVyKDEwMDApO1xudmFyIGdldE5leHRFcnJvclRpbWVvdXQgPSBVdGlsLmNyZWF0ZUV4cEJhY2tvZmZUaW1lcigxMDAwKTtcbi8vIEV4dGVybmFsIGF1dGhlbnRpY2F0aW9uIHN0dWZmXG52YXIgZXh0ZXJuYWxBdXRoRW5hYmxlZCA9IGZhbHNlO1xuLy8gU2lwIGdhdGV3YXkgY2FuIGJlIGVuYWJsZWQgYnkgY29uZmlndXJpbmcgSmlnYXNpIGhvc3QgaW4gY29uZmlnLmpzIG9yXG4vLyBpdCB3aWxsIGJlIGVuYWJsZWQgYXV0b21hdGljYWxseSBpZiBmb2N1cyBkZXRlY3RzIHRoZSBjb21wb25lbnQgdGhyb3VnaFxuLy8gc2VydmljZSBkaXNjb3ZlcnkuXG52YXIgc2lwR2F0ZXdheUVuYWJsZWQgPSBjb25maWcuaG9zdHMuY2FsbF9jb250cm9sICE9PSB1bmRlZmluZWQ7XG5cbnZhciBNb2RlcmF0b3IgPSB7XG4gICAgaXNNb2RlcmF0b3I6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIGNvbm5lY3Rpb24gJiYgY29ubmVjdGlvbi5lbXVjLmlzTW9kZXJhdG9yKCk7XG4gICAgfSxcblxuICAgIGlzUGVlck1vZGVyYXRvcjogZnVuY3Rpb24gKHBlZXJKaWQpIHtcbiAgICAgICAgcmV0dXJuIGNvbm5lY3Rpb24gJiZcbiAgICAgICAgICAgIGNvbm5lY3Rpb24uZW11Yy5nZXRNZW1iZXJSb2xlKHBlZXJKaWQpID09PSAnbW9kZXJhdG9yJztcbiAgICB9LFxuXG4gICAgaXNFeHRlcm5hbEF1dGhFbmFibGVkOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBleHRlcm5hbEF1dGhFbmFibGVkO1xuICAgIH0sXG5cbiAgICBpc1NpcEdhdGV3YXlFbmFibGVkOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBzaXBHYXRld2F5RW5hYmxlZDtcbiAgICB9LFxuXG4gICAgc2V0Q29ubmVjdGlvbjogZnVuY3Rpb24gKGNvbikge1xuICAgICAgICBjb25uZWN0aW9uID0gY29uO1xuICAgIH0sXG5cbiAgICBpbml0OiBmdW5jdGlvbiAoeG1wcCkge1xuICAgICAgICB0aGlzLnhtcHBTZXJ2aWNlID0geG1wcDtcbiAgICAgICAgdGhpcy5vbkxvY2FsUm9sZUNoYW5nZSA9IGZ1bmN0aW9uIChmcm9tLCBtZW1iZXIsIHByZXMpIHtcbiAgICAgICAgICAgIFVJLm9uTW9kZXJhdG9yU3RhdHVzQ2hhbmdlZChNb2RlcmF0b3IuaXNNb2RlcmF0b3IoKSk7XG4gICAgICAgIH07XG4gICAgfSxcblxuICAgIG9uTXVjTGVmdDogZnVuY3Rpb24gKGppZCkge1xuICAgICAgICBjb25zb2xlLmluZm8oXCJTb21lb25lIGxlZnQgaXMgaXQgZm9jdXMgPyBcIiArIGppZCk7XG4gICAgICAgIHZhciByZXNvdXJjZSA9IFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGppZCk7XG4gICAgICAgIGlmIChyZXNvdXJjZSA9PT0gJ2ZvY3VzJyAmJiAhdGhpcy54bXBwU2VydmljZS5zZXNzaW9uVGVybWluYXRlZCkge1xuICAgICAgICAgICAgY29uc29sZS5pbmZvKFxuICAgICAgICAgICAgICAgIFwiRm9jdXMgaGFzIGxlZnQgdGhlIHJvb20gLSBsZWF2aW5nIGNvbmZlcmVuY2VcIik7XG4gICAgICAgICAgICAvL2hhbmdVcCgpO1xuICAgICAgICAgICAgLy8gV2UnZCByYXRoZXIgcmVsb2FkIHRvIGhhdmUgZXZlcnl0aGluZyByZS1pbml0aWFsaXplZFxuICAgICAgICAgICAgLy8gRklYTUU6IHNob3cgc29tZSBtZXNzYWdlIGJlZm9yZSByZWxvYWRcbiAgICAgICAgICAgIGxvY2F0aW9uLnJlbG9hZCgpO1xuICAgICAgICB9XG4gICAgfSxcbiAgICBcbiAgICBzZXRGb2N1c1VzZXJKaWQ6IGZ1bmN0aW9uIChmb2N1c0ppZCkge1xuICAgICAgICBpZiAoIWZvY3VzVXNlckppZCkge1xuICAgICAgICAgICAgZm9jdXNVc2VySmlkID0gZm9jdXNKaWQ7XG4gICAgICAgICAgICBjb25zb2xlLmluZm8oXCJGb2N1cyBqaWQgc2V0IHRvOiBcIiArIGZvY3VzVXNlckppZCk7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgZ2V0Rm9jdXNVc2VySmlkOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBmb2N1c1VzZXJKaWQ7XG4gICAgfSxcblxuICAgIGdldEZvY3VzQ29tcG9uZW50OiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIC8vIEdldCBmb2N1cyBjb21wb25lbnQgYWRkcmVzc1xuICAgICAgICB2YXIgZm9jdXNDb21wb25lbnQgPSBjb25maWcuaG9zdHMuZm9jdXM7XG4gICAgICAgIC8vIElmIG5vdCBzcGVjaWZpZWQgdXNlIGRlZmF1bHQ6ICdmb2N1cy5kb21haW4nXG4gICAgICAgIGlmICghZm9jdXNDb21wb25lbnQpIHtcbiAgICAgICAgICAgIGZvY3VzQ29tcG9uZW50ID0gJ2ZvY3VzLicgKyBjb25maWcuaG9zdHMuZG9tYWluO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBmb2N1c0NvbXBvbmVudDtcbiAgICB9LFxuXG4gICAgY3JlYXRlQ29uZmVyZW5jZUlxOiBmdW5jdGlvbiAocm9vbU5hbWUpIHtcbiAgICAgICAgLy8gR2VuZXJhdGUgY3JlYXRlIGNvbmZlcmVuY2UgSVFcbiAgICAgICAgdmFyIGVsZW0gPSAkaXEoe3RvOiBNb2RlcmF0b3IuZ2V0Rm9jdXNDb21wb25lbnQoKSwgdHlwZTogJ3NldCd9KTtcbiAgICAgICAgZWxlbS5jKCdjb25mZXJlbmNlJywge1xuICAgICAgICAgICAgeG1sbnM6ICdodHRwOi8vaml0c2kub3JnL3Byb3RvY29sL2ZvY3VzJyxcbiAgICAgICAgICAgIHJvb206IHJvb21OYW1lXG4gICAgICAgIH0pO1xuICAgICAgICBpZiAoY29uZmlnLmhvc3RzLmJyaWRnZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBlbGVtLmMoXG4gICAgICAgICAgICAgICAgJ3Byb3BlcnR5JyxcbiAgICAgICAgICAgICAgICB7IG5hbWU6ICdicmlkZ2UnLCB2YWx1ZTogY29uZmlnLmhvc3RzLmJyaWRnZX0pXG4gICAgICAgICAgICAgICAgLnVwKCk7XG4gICAgICAgIH1cbiAgICAgICAgLy8gVGVsbCB0aGUgZm9jdXMgd2UgaGF2ZSBKaWdhc2kgY29uZmlndXJlZFxuICAgICAgICBpZiAoY29uZmlnLmhvc3RzLmNhbGxfY29udHJvbCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBlbGVtLmMoXG4gICAgICAgICAgICAgICAgJ3Byb3BlcnR5JyxcbiAgICAgICAgICAgICAgICB7IG5hbWU6ICdjYWxsX2NvbnRyb2wnLCB2YWx1ZTogY29uZmlnLmhvc3RzLmNhbGxfY29udHJvbH0pXG4gICAgICAgICAgICAgICAgLnVwKCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGNvbmZpZy5jaGFubmVsTGFzdE4gIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgZWxlbS5jKFxuICAgICAgICAgICAgICAgICdwcm9wZXJ0eScsXG4gICAgICAgICAgICAgICAgeyBuYW1lOiAnY2hhbm5lbExhc3ROJywgdmFsdWU6IGNvbmZpZy5jaGFubmVsTGFzdE59KVxuICAgICAgICAgICAgICAgIC51cCgpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChjb25maWcuYWRhcHRpdmVMYXN0TiAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBlbGVtLmMoXG4gICAgICAgICAgICAgICAgJ3Byb3BlcnR5JyxcbiAgICAgICAgICAgICAgICB7IG5hbWU6ICdhZGFwdGl2ZUxhc3ROJywgdmFsdWU6IGNvbmZpZy5hZGFwdGl2ZUxhc3ROfSlcbiAgICAgICAgICAgICAgICAudXAoKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoY29uZmlnLmFkYXB0aXZlU2ltdWxjYXN0ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIGVsZW0uYyhcbiAgICAgICAgICAgICAgICAncHJvcGVydHknLFxuICAgICAgICAgICAgICAgIHsgbmFtZTogJ2FkYXB0aXZlU2ltdWxjYXN0JywgdmFsdWU6IGNvbmZpZy5hZGFwdGl2ZVNpbXVsY2FzdH0pXG4gICAgICAgICAgICAgICAgLnVwKCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGNvbmZpZy5vcGVuU2N0cCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBlbGVtLmMoXG4gICAgICAgICAgICAgICAgJ3Byb3BlcnR5JyxcbiAgICAgICAgICAgICAgICB7IG5hbWU6ICdvcGVuU2N0cCcsIHZhbHVlOiBjb25maWcub3BlblNjdHB9KVxuICAgICAgICAgICAgICAgIC51cCgpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChjb25maWcuZW5hYmxlRmlyZWZveFN1cHBvcnQgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgZWxlbS5jKFxuICAgICAgICAgICAgICAgICdwcm9wZXJ0eScsXG4gICAgICAgICAgICAgICAgeyBuYW1lOiAnZW5hYmxlRmlyZWZveEhhY2tzJyxcbiAgICAgICAgICAgICAgICAgICAgdmFsdWU6IGNvbmZpZy5lbmFibGVGaXJlZm94U3VwcG9ydH0pXG4gICAgICAgICAgICAgICAgLnVwKCk7XG4gICAgICAgIH1cbiAgICAgICAgZWxlbS51cCgpO1xuICAgICAgICByZXR1cm4gZWxlbTtcbiAgICB9LFxuXG4gICAgcGFyc2VDb25maWdPcHRpb25zOiBmdW5jdGlvbiAocmVzdWx0SXEpIHtcbiAgICBcbiAgICAgICAgTW9kZXJhdG9yLnNldEZvY3VzVXNlckppZChcbiAgICAgICAgICAgICQocmVzdWx0SXEpLmZpbmQoJ2NvbmZlcmVuY2UnKS5hdHRyKCdmb2N1c2ppZCcpKTtcbiAgICBcbiAgICAgICAgdmFyIGV4dEF1dGhQYXJhbVxuICAgICAgICAgICAgPSAkKHJlc3VsdElxKS5maW5kKCc+Y29uZmVyZW5jZT5wcm9wZXJ0eVtuYW1lPVxcJ2V4dGVybmFsQXV0aFxcJ10nKTtcbiAgICAgICAgaWYgKGV4dEF1dGhQYXJhbS5sZW5ndGgpIHtcbiAgICAgICAgICAgIGV4dGVybmFsQXV0aEVuYWJsZWQgPSBleHRBdXRoUGFyYW0uYXR0cigndmFsdWUnKSA9PT0gJ3RydWUnO1xuICAgICAgICB9XG4gICAgXG4gICAgICAgIGNvbnNvbGUuaW5mbyhcIkV4dGVybmFsIGF1dGhlbnRpY2F0aW9uIGVuYWJsZWQ6IFwiICsgZXh0ZXJuYWxBdXRoRW5hYmxlZCk7XG4gICAgXG4gICAgICAgIC8vIENoZWNrIGlmIGZvY3VzIGhhcyBhdXRvLWRldGVjdGVkIEppZ2FzaSBjb21wb25lbnQodGhpcyB3aWxsIGJlIGFsc29cbiAgICAgICAgLy8gaW5jbHVkZWQgaWYgd2UgaGF2ZSBwYXNzZWQgb3VyIGhvc3QgZnJvbSB0aGUgY29uZmlnKVxuICAgICAgICBpZiAoJChyZXN1bHRJcSkuZmluZChcbiAgICAgICAgICAgICc+Y29uZmVyZW5jZT5wcm9wZXJ0eVtuYW1lPVxcJ3NpcEdhdGV3YXlFbmFibGVkXFwnXScpLmxlbmd0aCkge1xuICAgICAgICAgICAgc2lwR2F0ZXdheUVuYWJsZWQgPSB0cnVlO1xuICAgICAgICB9XG4gICAgXG4gICAgICAgIGNvbnNvbGUuaW5mbyhcIlNpcCBnYXRld2F5IGVuYWJsZWQ6IFwiICsgc2lwR2F0ZXdheUVuYWJsZWQpO1xuICAgIH0sXG5cbiAgICAvLyBGSVhNRTogd2UgbmVlZCB0byBzaG93IHRoZSBmYWN0IHRoYXQgd2UncmUgd2FpdGluZyBmb3IgdGhlIGZvY3VzXG4gICAgLy8gdG8gdGhlIHVzZXIob3IgdGhhdCBmb2N1cyBpcyBub3QgYXZhaWxhYmxlKVxuICAgIGFsbG9jYXRlQ29uZmVyZW5jZUZvY3VzOiBmdW5jdGlvbiAocm9vbU5hbWUsIGNhbGxiYWNrKSB7XG4gICAgICAgIC8vIFRyeSB0byB1c2UgZm9jdXMgdXNlciBKSUQgZnJvbSB0aGUgY29uZmlnXG4gICAgICAgIE1vZGVyYXRvci5zZXRGb2N1c1VzZXJKaWQoY29uZmlnLmZvY3VzVXNlckppZCk7XG4gICAgICAgIC8vIFNlbmQgY3JlYXRlIGNvbmZlcmVuY2UgSVFcbiAgICAgICAgdmFyIGlxID0gTW9kZXJhdG9yLmNyZWF0ZUNvbmZlcmVuY2VJcShyb29tTmFtZSk7XG4gICAgICAgIGNvbm5lY3Rpb24uc2VuZElRKFxuICAgICAgICAgICAgaXEsXG4gICAgICAgICAgICBmdW5jdGlvbiAocmVzdWx0KSB7XG4gICAgICAgICAgICAgICAgaWYgKCd0cnVlJyA9PT0gJChyZXN1bHQpLmZpbmQoJ2NvbmZlcmVuY2UnKS5hdHRyKCdyZWFkeScpKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIFJlc2V0IGJvdGggdGltZXJzXG4gICAgICAgICAgICAgICAgICAgIGdldE5leHRUaW1lb3V0KHRydWUpO1xuICAgICAgICAgICAgICAgICAgICBnZXROZXh0RXJyb3JUaW1lb3V0KHRydWUpO1xuICAgICAgICAgICAgICAgICAgICAvLyBTZXR1cCBjb25maWcgb3B0aW9uc1xuICAgICAgICAgICAgICAgICAgICBNb2RlcmF0b3IucGFyc2VDb25maWdPcHRpb25zKHJlc3VsdCk7XG4gICAgICAgICAgICAgICAgICAgIC8vIEV4ZWMgY2FsbGJhY2tcbiAgICAgICAgICAgICAgICAgICAgY2FsbGJhY2soKTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICB2YXIgd2FpdE1zID0gZ2V0TmV4dFRpbWVvdXQoKTtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKFwiV2FpdGluZyBmb3IgdGhlIGZvY3VzLi4uIFwiICsgd2FpdE1zKTtcbiAgICAgICAgICAgICAgICAgICAgLy8gUmVzZXQgZXJyb3IgdGltZW91dFxuICAgICAgICAgICAgICAgICAgICBnZXROZXh0RXJyb3JUaW1lb3V0KHRydWUpO1xuICAgICAgICAgICAgICAgICAgICB3aW5kb3cuc2V0VGltZW91dChcbiAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBNb2RlcmF0b3IuYWxsb2NhdGVDb25mZXJlbmNlRm9jdXMoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvb21OYW1lLCBjYWxsYmFjayk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9LCB3YWl0TXMpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBmdW5jdGlvbiAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAvLyBOb3QgYXV0aG9yaXplZCB0byBjcmVhdGUgbmV3IHJvb21cbiAgICAgICAgICAgICAgICBpZiAoJChlcnJvcikuZmluZCgnPmVycm9yPm5vdC1hdXRob3JpemVkJykubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybihcIlVuYXV0aG9yaXplZCB0byBzdGFydCB0aGUgY29uZmVyZW5jZVwiKTtcbiAgICAgICAgICAgICAgICAgICAgVUkub25BdXRoZW50aWNhdGlvblJlcXVpcmVkKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIE1vZGVyYXRvci5hbGxvY2F0ZUNvbmZlcmVuY2VGb2N1cyhyb29tTmFtZSwgY2FsbGJhY2spO1xuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB2YXIgd2FpdE1zID0gZ2V0TmV4dEVycm9yVGltZW91dCgpO1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJGb2N1cyBlcnJvciwgcmV0cnkgYWZ0ZXIgXCIgKyB3YWl0TXMsIGVycm9yKTtcbiAgICAgICAgICAgICAgICAvLyBTaG93IG1lc3NhZ2VcbiAgICAgICAgICAgICAgICBVSS5tZXNzYWdlSGFuZGxlci5ub3RpZnkoXG4gICAgICAgICAgICAgICAgICAgICdDb25mZXJlbmNlIGZvY3VzJywgJ2Rpc2Nvbm5lY3RlZCcsXG4gICAgICAgICAgICAgICAgICAgICAgICBNb2RlcmF0b3IuZ2V0Rm9jdXNDb21wb25lbnQoKSArXG4gICAgICAgICAgICAgICAgICAgICAgICAnIG5vdCBhdmFpbGFibGUgLSByZXRyeSBpbiAnICtcbiAgICAgICAgICAgICAgICAgICAgICAgICh3YWl0TXMgLyAxMDAwKSArICcgc2VjJyk7XG4gICAgICAgICAgICAgICAgLy8gUmVzZXQgcmVzcG9uc2UgdGltZW91dFxuICAgICAgICAgICAgICAgIGdldE5leHRUaW1lb3V0KHRydWUpO1xuICAgICAgICAgICAgICAgIHdpbmRvdy5zZXRUaW1lb3V0KFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBNb2RlcmF0b3IuYWxsb2NhdGVDb25mZXJlbmNlRm9jdXMocm9vbU5hbWUsIGNhbGxiYWNrKTtcbiAgICAgICAgICAgICAgICAgICAgfSwgd2FpdE1zKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgKTtcbiAgICB9LFxuXG4gICAgZ2V0QXV0aFVybDogZnVuY3Rpb24gKHJvb21OYW1lLCB1cmxDYWxsYmFjaykge1xuICAgICAgICB2YXIgaXEgPSAkaXEoe3RvOiBNb2RlcmF0b3IuZ2V0Rm9jdXNDb21wb25lbnQoKSwgdHlwZTogJ2dldCd9KTtcbiAgICAgICAgaXEuYygnYXV0aC11cmwnLCB7XG4gICAgICAgICAgICB4bWxuczogJ2h0dHA6Ly9qaXRzaS5vcmcvcHJvdG9jb2wvZm9jdXMnLFxuICAgICAgICAgICAgcm9vbTogcm9vbU5hbWVcbiAgICAgICAgfSk7XG4gICAgICAgIGNvbm5lY3Rpb24uc2VuZElRKFxuICAgICAgICAgICAgaXEsXG4gICAgICAgICAgICBmdW5jdGlvbiAocmVzdWx0KSB7XG4gICAgICAgICAgICAgICAgdmFyIHVybCA9ICQocmVzdWx0KS5maW5kKCdhdXRoLXVybCcpLmF0dHIoJ3VybCcpO1xuICAgICAgICAgICAgICAgIGlmICh1cmwpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKFwiR290IGF1dGggdXJsOiBcIiArIHVybCk7XG4gICAgICAgICAgICAgICAgICAgIHVybENhbGxiYWNrKHVybCk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiRmFpbGVkIHRvIGdldCBhdXRoIHVybCBmcm8gbXRoZSBmb2N1c1wiLCByZXN1bHQpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBmdW5jdGlvbiAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiR2V0IGF1dGggdXJsIGVycm9yXCIsIGVycm9yKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgKTtcbiAgICB9XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IE1vZGVyYXRvcjtcblxuXG5cbiIsIi8qIGdsb2JhbCAkLCAkaXEsIGNvbmZpZywgY29ubmVjdGlvbiwgZm9jdXNNdWNKaWQsIG1lc3NhZ2VIYW5kbGVyLCBNb2RlcmF0b3IsXG4gICBUb29sYmFyLCBVdGlsICovXG52YXIgTW9kZXJhdG9yID0gcmVxdWlyZShcIi4vbW9kZXJhdG9yXCIpO1xuXG5cbnZhciByZWNvcmRpbmdUb2tlbiA9IG51bGw7XG52YXIgcmVjb3JkaW5nRW5hYmxlZDtcblxuLyoqXG4gKiBXaGV0aGVyIHRvIHVzZSBhIGppcmVjb24gY29tcG9uZW50IGZvciByZWNvcmRpbmcsIG9yIHVzZSB0aGUgdmlkZW9icmlkZ2VcbiAqIHRocm91Z2ggQ09MSUJSSS5cbiAqL1xudmFyIHVzZUppcmVjb24gPSAodHlwZW9mIGNvbmZpZy5ob3N0cy5qaXJlY29uICE9IFwidW5kZWZpbmVkXCIpO1xuXG4vKipcbiAqIFRoZSBJRCBvZiB0aGUgamlyZWNvbiByZWNvcmRpbmcgc2Vzc2lvbi4gSmlyZWNvbiBnZW5lcmF0ZXMgaXQgd2hlbiB3ZVxuICogaW5pdGlhbGx5IHN0YXJ0IHJlY29yZGluZywgYW5kIGl0IG5lZWRzIHRvIGJlIHVzZWQgaW4gc3Vic2VxdWVudCByZXF1ZXN0c1xuICogdG8gamlyZWNvbi5cbiAqL1xudmFyIGppcmVjb25SaWQgPSBudWxsO1xuXG5mdW5jdGlvbiBzZXRSZWNvcmRpbmdUb2tlbih0b2tlbikge1xuICAgIHJlY29yZGluZ1Rva2VuID0gdG9rZW47XG59XG5cbmZ1bmN0aW9uIHNldFJlY29yZGluZyhzdGF0ZSwgdG9rZW4sIGNhbGxiYWNrKSB7XG4gICAgaWYgKHVzZUppcmVjb24pe1xuICAgICAgICB0aGlzLnNldFJlY29yZGluZ0ppcmVjb24oc3RhdGUsIHRva2VuLCBjYWxsYmFjayk7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5zZXRSZWNvcmRpbmdDb2xpYnJpKHN0YXRlLCB0b2tlbiwgY2FsbGJhY2spO1xuICAgIH1cbn1cblxuZnVuY3Rpb24gc2V0UmVjb3JkaW5nSmlyZWNvbihzdGF0ZSwgdG9rZW4sIGNhbGxiYWNrKSB7XG4gICAgaWYgKHN0YXRlID09IHJlY29yZGluZ0VuYWJsZWQpe1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdmFyIGlxID0gJGlxKHt0bzogY29uZmlnLmhvc3RzLmppcmVjb24sIHR5cGU6ICdzZXQnfSlcbiAgICAgICAgLmMoJ3JlY29yZGluZycsIHt4bWxuczogJ2h0dHA6Ly9qaXRzaS5vcmcvcHJvdG9jb2wvamlyZWNvbicsXG4gICAgICAgICAgICBhY3Rpb246IHN0YXRlID8gJ3N0YXJ0JyA6ICdzdG9wJyxcbiAgICAgICAgICAgIG11Y2ppZDogY29ubmVjdGlvbi5lbXVjLnJvb21qaWR9KTtcbiAgICBpZiAoIXN0YXRlKXtcbiAgICAgICAgaXEuYXR0cnMoe3JpZDogamlyZWNvblJpZH0pO1xuICAgIH1cblxuICAgIGNvbnNvbGUubG9nKCdTdGFydCByZWNvcmRpbmcnKTtcblxuICAgIGNvbm5lY3Rpb24uc2VuZElRKFxuICAgICAgICBpcSxcbiAgICAgICAgZnVuY3Rpb24gKHJlc3VsdCkge1xuICAgICAgICAgICAgLy8gVE9ETyB3YWl0IGZvciBhbiBJUSB3aXRoIHRoZSByZWFsIHN0YXR1cywgc2luY2UgdGhpcyBpc1xuICAgICAgICAgICAgLy8gcHJvdmlzaW9uYWw/XG4gICAgICAgICAgICBqaXJlY29uUmlkID0gJChyZXN1bHQpLmZpbmQoJ3JlY29yZGluZycpLmF0dHIoJ3JpZCcpO1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ1JlY29yZGluZyAnICsgKHN0YXRlID8gJ3N0YXJ0ZWQnIDogJ3N0b3BwZWQnKSArXG4gICAgICAgICAgICAgICAgJyhqaXJlY29uKScgKyByZXN1bHQpO1xuICAgICAgICAgICAgcmVjb3JkaW5nRW5hYmxlZCA9IHN0YXRlO1xuICAgICAgICAgICAgaWYgKCFzdGF0ZSl7XG4gICAgICAgICAgICAgICAgamlyZWNvblJpZCA9IG51bGw7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNhbGxiYWNrKHN0YXRlKTtcbiAgICAgICAgfSxcbiAgICAgICAgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygnRmFpbGVkIHRvIHN0YXJ0IHJlY29yZGluZywgZXJyb3I6ICcsIGVycm9yKTtcbiAgICAgICAgICAgIGNhbGxiYWNrKHJlY29yZGluZ0VuYWJsZWQpO1xuICAgICAgICB9KTtcbn1cblxuLy8gU2VuZHMgYSBDT0xJQlJJIG1lc3NhZ2Ugd2hpY2ggZW5hYmxlcyBvciBkaXNhYmxlcyAoYWNjb3JkaW5nIHRvICdzdGF0ZScpXG4vLyB0aGUgcmVjb3JkaW5nIG9uIHRoZSBicmlkZ2UuIFdhaXRzIGZvciB0aGUgcmVzdWx0IElRIGFuZCBjYWxscyAnY2FsbGJhY2snXG4vLyB3aXRoIHRoZSBuZXcgcmVjb3JkaW5nIHN0YXRlLCBhY2NvcmRpbmcgdG8gdGhlIElRLlxuZnVuY3Rpb24gc2V0UmVjb3JkaW5nQ29saWJyaShzdGF0ZSwgdG9rZW4sIGNhbGxiYWNrKSB7XG4gICAgdmFyIGVsZW0gPSAkaXEoe3RvOiBmb2N1c011Y0ppZCwgdHlwZTogJ3NldCd9KTtcbiAgICBlbGVtLmMoJ2NvbmZlcmVuY2UnLCB7XG4gICAgICAgIHhtbG5zOiAnaHR0cDovL2ppdHNpLm9yZy9wcm90b2NvbC9jb2xpYnJpJ1xuICAgIH0pO1xuICAgIGVsZW0uYygncmVjb3JkaW5nJywge3N0YXRlOiBzdGF0ZSwgdG9rZW46IHRva2VufSk7XG5cbiAgICBjb25uZWN0aW9uLnNlbmRJUShlbGVtLFxuICAgICAgICBmdW5jdGlvbiAocmVzdWx0KSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygnU2V0IHJlY29yZGluZyBcIicsIHN0YXRlLCAnXCIuIFJlc3VsdDonLCByZXN1bHQpO1xuICAgICAgICAgICAgdmFyIHJlY29yZGluZ0VsZW0gPSAkKHJlc3VsdCkuZmluZCgnPmNvbmZlcmVuY2U+cmVjb3JkaW5nJyk7XG4gICAgICAgICAgICB2YXIgbmV3U3RhdGUgPSAoJ3RydWUnID09PSByZWNvcmRpbmdFbGVtLmF0dHIoJ3N0YXRlJykpO1xuXG4gICAgICAgICAgICByZWNvcmRpbmdFbmFibGVkID0gbmV3U3RhdGU7XG4gICAgICAgICAgICBjYWxsYmFjayhuZXdTdGF0ZSk7XG4gICAgICAgIH0sXG4gICAgICAgIGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgY29uc29sZS53YXJuKGVycm9yKTtcbiAgICAgICAgICAgIGNhbGxiYWNrKHJlY29yZGluZ0VuYWJsZWQpO1xuICAgICAgICB9XG4gICAgKTtcbn1cblxudmFyIFJlY29yZGluZyA9IHtcbiAgICB0b2dnbGVSZWNvcmRpbmc6IGZ1bmN0aW9uICh0b2tlbkVtcHR5Q2FsbGJhY2ssXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhcnRpbmdDYWxsYmFjaywgc3RhcnRlZENhbGxiYWNrKSB7XG4gICAgICAgIGlmICghTW9kZXJhdG9yLmlzTW9kZXJhdG9yKCkpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgICAgICAgICAnbm9uLWZvY3VzLCBvciBjb25mZXJlbmNlIG5vdCB5ZXQgb3JnYW5pemVkOicgK1xuICAgICAgICAgICAgICAgICAgICAnIG5vdCBlbmFibGluZyByZWNvcmRpbmcnKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEppcmVjb24gZG9lcyBub3QgKGN1cnJlbnRseSkgc3VwcG9ydCBhIHRva2VuLlxuICAgICAgICBpZiAoIXJlY29yZGluZ1Rva2VuICYmICF1c2VKaXJlY29uKSB7XG4gICAgICAgICAgICB0b2tlbkVtcHR5Q2FsbGJhY2soZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgICAgICAgICAgICAgc2V0UmVjb3JkaW5nVG9rZW4odmFsdWUpO1xuICAgICAgICAgICAgICAgIHRoaXMudG9nZ2xlUmVjb3JkaW5nKCk7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIG9sZFN0YXRlID0gcmVjb3JkaW5nRW5hYmxlZDtcbiAgICAgICAgc3RhcnRpbmdDYWxsYmFjayghb2xkU3RhdGUpO1xuICAgICAgICBzZXRSZWNvcmRpbmcoIW9sZFN0YXRlLFxuICAgICAgICAgICAgcmVjb3JkaW5nVG9rZW4sXG4gICAgICAgICAgICBmdW5jdGlvbiAoc3RhdGUpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcIk5ldyByZWNvcmRpbmcgc3RhdGU6IFwiLCBzdGF0ZSk7XG4gICAgICAgICAgICAgICAgaWYgKHN0YXRlID09PSBvbGRTdGF0ZSkge1xuICAgICAgICAgICAgICAgICAgICAvLyBGSVhNRTogbmV3IGZvY3VzOlxuICAgICAgICAgICAgICAgICAgICAvLyB0aGlzIHdpbGwgbm90IHdvcmsgd2hlbiBtb2RlcmF0b3IgY2hhbmdlc1xuICAgICAgICAgICAgICAgICAgICAvLyBkdXJpbmcgYWN0aXZlIHNlc3Npb24uIFRoZW4gaXQgd2lsbCBhc3N1bWUgdGhhdFxuICAgICAgICAgICAgICAgICAgICAvLyByZWNvcmRpbmcgc3RhdHVzIGhhcyBjaGFuZ2VkIHRvIHRydWUsIGJ1dCBpdCBtaWdodCBoYXZlXG4gICAgICAgICAgICAgICAgICAgIC8vIGJlZW4gYWxyZWFkeSB0cnVlKGFuZCB3ZSBvbmx5IHJlY2VpdmVkIGFjdHVhbCBzdGF0dXMgZnJvbVxuICAgICAgICAgICAgICAgICAgICAvLyB0aGUgZm9jdXMpLlxuICAgICAgICAgICAgICAgICAgICAvL1xuICAgICAgICAgICAgICAgICAgICAvLyBTTyB3ZSBzdGFydCB3aXRoIHN0YXR1cyBudWxsLCBzbyB0aGF0IGl0IGlzIGluaXRpYWxpemVkXG4gICAgICAgICAgICAgICAgICAgIC8vIGhlcmUgYW5kIHdpbGwgZmFpbCBvbmx5IGFmdGVyIHNlY29uZCBjbGljaywgc28gaWYgaW52YWxpZFxuICAgICAgICAgICAgICAgICAgICAvLyB0b2tlbiB3YXMgdXNlZCB3ZSBoYXZlIHRvIHByZXNzIHRoZSBidXR0b24gdHdpY2UgYmVmb3JlXG4gICAgICAgICAgICAgICAgICAgIC8vIGN1cnJlbnQgc3RhdHVzIHdpbGwgYmUgZmV0Y2hlZCBhbmQgdG9rZW4gd2lsbCBiZSByZXNldC5cbiAgICAgICAgICAgICAgICAgICAgLy9cbiAgICAgICAgICAgICAgICAgICAgLy8gUmVsaWFibGUgd2F5IHdvdWxkIGJlIHRvIHJldHVybiBhdXRoZW50aWNhdGlvbiBlcnJvci5cbiAgICAgICAgICAgICAgICAgICAgLy8gT3Igc3RhdHVzIHVwZGF0ZSB3aGVuIG1vZGVyYXRvciBjb25uZWN0cy5cbiAgICAgICAgICAgICAgICAgICAgLy8gT3Igd2UgaGF2ZSB0byBzdG9wIHJlY29yZGluZyBzZXNzaW9uIHdoZW4gY3VycmVudFxuICAgICAgICAgICAgICAgICAgICAvLyBtb2RlcmF0b3IgbGVhdmVzIHRoZSByb29tLlxuXG4gICAgICAgICAgICAgICAgICAgIC8vIEZhaWxlZCB0byBjaGFuZ2UsIHJlc2V0IHRoZSB0b2tlbiBiZWNhdXNlIGl0IG1pZ2h0XG4gICAgICAgICAgICAgICAgICAgIC8vIGhhdmUgYmVlbiB3cm9uZ1xuICAgICAgICAgICAgICAgICAgICBzZXRSZWNvcmRpbmdUb2tlbihudWxsKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgc3RhcnRlZENhbGxiYWNrKHN0YXRlKTtcblxuICAgICAgICAgICAgfVxuICAgICAgICApO1xuICAgIH1cblxufVxuXG5tb2R1bGUuZXhwb3J0cyA9IFJlY29yZGluZzsiLCIvKiBqc2hpbnQgLVcxMTcgKi9cbi8qIGEgc2ltcGxlIE1VQyBjb25uZWN0aW9uIHBsdWdpblxuICogY2FuIG9ubHkgaGFuZGxlIGEgc2luZ2xlIE1VQyByb29tXG4gKi9cblxudmFyIGJyaWRnZUlzRG93biA9IGZhbHNlO1xuXG52YXIgTW9kZXJhdG9yID0gcmVxdWlyZShcIi4vbW9kZXJhdG9yXCIpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uKFhNUFAsIGV2ZW50RW1pdHRlcikge1xuICAgIFN0cm9waGUuYWRkQ29ubmVjdGlvblBsdWdpbignZW11YycsIHtcbiAgICAgICAgY29ubmVjdGlvbjogbnVsbCxcbiAgICAgICAgcm9vbWppZDogbnVsbCxcbiAgICAgICAgbXlyb29tamlkOiBudWxsLFxuICAgICAgICBtZW1iZXJzOiB7fSxcbiAgICAgICAgbGlzdF9tZW1iZXJzOiBbXSwgLy8gc28gd2UgY2FuIGVsZWN0IGEgbmV3IGZvY3VzXG4gICAgICAgIHByZXNNYXA6IHt9LFxuICAgICAgICBwcmV6aU1hcDoge30sXG4gICAgICAgIGpvaW5lZDogZmFsc2UsXG4gICAgICAgIGlzT3duZXI6IGZhbHNlLFxuICAgICAgICByb2xlOiBudWxsLFxuICAgICAgICBpbml0OiBmdW5jdGlvbiAoY29ubikge1xuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uID0gY29ubjtcbiAgICAgICAgfSxcbiAgICAgICAgaW5pdFByZXNlbmNlTWFwOiBmdW5jdGlvbiAobXlyb29tamlkKSB7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ3RvJ10gPSBteXJvb21qaWQ7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ3hucyddID0gJ2h0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL211Yyc7XG4gICAgICAgIH0sXG4gICAgICAgIGRvSm9pbjogZnVuY3Rpb24gKGppZCwgcGFzc3dvcmQpIHtcbiAgICAgICAgICAgIHRoaXMubXlyb29tamlkID0gamlkO1xuXG4gICAgICAgICAgICBjb25zb2xlLmluZm8oXCJKb2luZWQgTVVDIGFzIFwiICsgdGhpcy5teXJvb21qaWQpO1xuXG4gICAgICAgICAgICB0aGlzLmluaXRQcmVzZW5jZU1hcCh0aGlzLm15cm9vbWppZCk7XG5cbiAgICAgICAgICAgIGlmICghdGhpcy5yb29tamlkKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5yb29tamlkID0gU3Ryb3BoZS5nZXRCYXJlSmlkRnJvbUppZChqaWQpO1xuICAgICAgICAgICAgICAgIC8vIGFkZCBoYW5kbGVycyAoanVzdCBvbmNlKVxuICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5hZGRIYW5kbGVyKHRoaXMub25QcmVzZW5jZS5iaW5kKHRoaXMpLCBudWxsLCAncHJlc2VuY2UnLCBudWxsLCBudWxsLCB0aGlzLnJvb21qaWQsIHttYXRjaEJhcmU6IHRydWV9KTtcbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uYWRkSGFuZGxlcih0aGlzLm9uUHJlc2VuY2VVbmF2YWlsYWJsZS5iaW5kKHRoaXMpLCBudWxsLCAncHJlc2VuY2UnLCAndW5hdmFpbGFibGUnLCBudWxsLCB0aGlzLnJvb21qaWQsIHttYXRjaEJhcmU6IHRydWV9KTtcbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uYWRkSGFuZGxlcih0aGlzLm9uUHJlc2VuY2VFcnJvci5iaW5kKHRoaXMpLCBudWxsLCAncHJlc2VuY2UnLCAnZXJyb3InLCBudWxsLCB0aGlzLnJvb21qaWQsIHttYXRjaEJhcmU6IHRydWV9KTtcbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uYWRkSGFuZGxlcih0aGlzLm9uTWVzc2FnZS5iaW5kKHRoaXMpLCBudWxsLCAnbWVzc2FnZScsIG51bGwsIG51bGwsIHRoaXMucm9vbWppZCwge21hdGNoQmFyZTogdHJ1ZX0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKHBhc3N3b3JkICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnByZXNNYXBbJ3Bhc3N3b3JkJ10gPSBwYXNzd29yZDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMuc2VuZFByZXNlbmNlKCk7XG4gICAgICAgIH0sXG4gICAgICAgIGRvTGVhdmU6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKFwiZG8gbGVhdmVcIiwgdGhpcy5teXJvb21qaWQpO1xuICAgICAgICAgICAgdmFyIHByZXMgPSAkcHJlcyh7dG86IHRoaXMubXlyb29tamlkLCB0eXBlOiAndW5hdmFpbGFibGUnIH0pO1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwLmxlbmd0aCA9IDA7XG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZChwcmVzKTtcbiAgICAgICAgfSxcbiAgICAgICAgY3JlYXRlTm9uQW5vbnltb3VzUm9vbTogZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgLy8gaHR0cDovL3htcHAub3JnL2V4dGVuc2lvbnMveGVwLTAwNDUuaHRtbCNjcmVhdGVyb29tLXJlc2VydmVkXG5cbiAgICAgICAgICAgIHZhciBnZXRGb3JtID0gJGlxKHt0eXBlOiAnZ2V0JywgdG86IHRoaXMucm9vbWppZH0pXG4gICAgICAgICAgICAgICAgLmMoJ3F1ZXJ5Jywge3htbG5zOiAnaHR0cDovL2phYmJlci5vcmcvcHJvdG9jb2wvbXVjI293bmVyJ30pXG4gICAgICAgICAgICAgICAgLmMoJ3gnLCB7eG1sbnM6ICdqYWJiZXI6eDpkYXRhJywgdHlwZTogJ3N1Ym1pdCd9KTtcblxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRJUShnZXRGb3JtLCBmdW5jdGlvbiAoZm9ybSkge1xuXG4gICAgICAgICAgICAgICAgaWYgKCEkKGZvcm0pLmZpbmQoXG4gICAgICAgICAgICAgICAgICAgICAgICAnPnF1ZXJ5PnhbeG1sbnM9XCJqYWJiZXI6eDpkYXRhXCJdJyArXG4gICAgICAgICAgICAgICAgICAgICAgICAnPmZpZWxkW3Zhcj1cIm11YyNyb29tY29uZmlnX3dob2lzXCJdJykubGVuZ3RoKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcignbm9uLWFub255bW91cyByb29tcyBub3Qgc3VwcG9ydGVkJyk7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICB2YXIgZm9ybVN1Ym1pdCA9ICRpcSh7dG86IHRoaXMucm9vbWppZCwgdHlwZTogJ3NldCd9KVxuICAgICAgICAgICAgICAgICAgICAuYygncXVlcnknLCB7eG1sbnM6ICdodHRwOi8vamFiYmVyLm9yZy9wcm90b2NvbC9tdWMjb3duZXInfSk7XG5cbiAgICAgICAgICAgICAgICBmb3JtU3VibWl0LmMoJ3gnLCB7eG1sbnM6ICdqYWJiZXI6eDpkYXRhJywgdHlwZTogJ3N1Ym1pdCd9KTtcblxuICAgICAgICAgICAgICAgIGZvcm1TdWJtaXQuYygnZmllbGQnLCB7J3Zhcic6ICdGT1JNX1RZUEUnfSlcbiAgICAgICAgICAgICAgICAgICAgLmMoJ3ZhbHVlJylcbiAgICAgICAgICAgICAgICAgICAgLnQoJ2h0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL211YyNyb29tY29uZmlnJykudXAoKS51cCgpO1xuXG4gICAgICAgICAgICAgICAgZm9ybVN1Ym1pdC5jKCdmaWVsZCcsIHsndmFyJzogJ211YyNyb29tY29uZmlnX3dob2lzJ30pXG4gICAgICAgICAgICAgICAgICAgIC5jKCd2YWx1ZScpLnQoJ2FueW9uZScpLnVwKCkudXAoKTtcblxuICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kSVEoZm9ybVN1Ym1pdCk7XG5cbiAgICAgICAgICAgIH0sIGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJFcnJvciBnZXR0aW5nIHJvb20gY29uZmlndXJhdGlvbiBmb3JtXCIpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0sXG4gICAgICAgIG9uUHJlc2VuY2U6IGZ1bmN0aW9uIChwcmVzKSB7XG4gICAgICAgICAgICB2YXIgZnJvbSA9IHByZXMuZ2V0QXR0cmlidXRlKCdmcm9tJyk7XG5cbiAgICAgICAgICAgIC8vIFdoYXQgaXMgdGhpcyBmb3I/IEEgd29ya2Fyb3VuZCBmb3Igc29tZXRoaW5nP1xuICAgICAgICAgICAgaWYgKHByZXMuZ2V0QXR0cmlidXRlKCd0eXBlJykpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gUGFyc2UgZXRoZXJwYWQgdGFnLlxuICAgICAgICAgICAgdmFyIGV0aGVycGFkID0gJChwcmVzKS5maW5kKCc+ZXRoZXJwYWQnKTtcbiAgICAgICAgICAgIGlmIChldGhlcnBhZC5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICBpZiAoY29uZmlnLmV0aGVycGFkX2Jhc2UgJiYgIU1vZGVyYXRvci5pc01vZGVyYXRvcigpKSB7XG4gICAgICAgICAgICAgICAgICAgIFVJLmluaXRFdGhlcnBhZChldGhlcnBhZC50ZXh0KCkpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gUGFyc2UgcHJlemkgdGFnLlxuICAgICAgICAgICAgdmFyIHByZXNlbnRhdGlvbiA9ICQocHJlcykuZmluZCgnPnByZXppJyk7XG4gICAgICAgICAgICBpZiAocHJlc2VudGF0aW9uLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIHZhciB1cmwgPSBwcmVzZW50YXRpb24uYXR0cigndXJsJyk7XG4gICAgICAgICAgICAgICAgdmFyIGN1cnJlbnQgPSBwcmVzZW50YXRpb24uZmluZCgnPmN1cnJlbnQnKS50ZXh0KCk7XG5cbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygncHJlc2VudGF0aW9uIGluZm8gcmVjZWl2ZWQgZnJvbScsIGZyb20sIHVybCk7XG5cbiAgICAgICAgICAgICAgICBpZiAodGhpcy5wcmV6aU1hcFtmcm9tXSA9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMucHJlemlNYXBbZnJvbV0gPSB1cmw7XG5cbiAgICAgICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcigncHJlc2VudGF0aW9uYWRkZWQubXVjJywgW2Zyb20sIHVybCwgY3VycmVudF0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcignZ290b3NsaWRlLm11YycsIFtmcm9tLCB1cmwsIGN1cnJlbnRdKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlIGlmICh0aGlzLnByZXppTWFwW2Zyb21dICE9IG51bGwpIHtcbiAgICAgICAgICAgICAgICB2YXIgdXJsID0gdGhpcy5wcmV6aU1hcFtmcm9tXTtcbiAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5wcmV6aU1hcFtmcm9tXTtcbiAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdwcmVzZW50YXRpb25yZW1vdmVkLm11YycsIFtmcm9tLCB1cmxdKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gUGFyc2UgYXVkaW8gaW5mbyB0YWcuXG4gICAgICAgICAgICB2YXIgYXVkaW9NdXRlZCA9ICQocHJlcykuZmluZCgnPmF1ZGlvbXV0ZWQnKTtcbiAgICAgICAgICAgIGlmIChhdWRpb011dGVkLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ2F1ZGlvbXV0ZWQubXVjJywgW2Zyb20sIGF1ZGlvTXV0ZWQudGV4dCgpXSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIFBhcnNlIHZpZGVvIGluZm8gdGFnLlxuICAgICAgICAgICAgdmFyIHZpZGVvTXV0ZWQgPSAkKHByZXMpLmZpbmQoJz52aWRlb211dGVkJyk7XG4gICAgICAgICAgICBpZiAodmlkZW9NdXRlZC5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCd2aWRlb211dGVkLm11YycsIFtmcm9tLCB2aWRlb011dGVkLnRleHQoKV0pO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgc3RhdHMgPSAkKHByZXMpLmZpbmQoJz5zdGF0cycpO1xuICAgICAgICAgICAgaWYgKHN0YXRzLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIHZhciBzdGF0c09iaiA9IHt9O1xuICAgICAgICAgICAgICAgIFN0cm9waGUuZm9yRWFjaENoaWxkKHN0YXRzWzBdLCBcInN0YXRcIiwgZnVuY3Rpb24gKGVsKSB7XG4gICAgICAgICAgICAgICAgICAgIHN0YXRzT2JqW2VsLmdldEF0dHJpYnV0ZShcIm5hbWVcIildID0gZWwuZ2V0QXR0cmlidXRlKFwidmFsdWVcIik7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgY29ubmVjdGlvbnF1YWxpdHkudXBkYXRlUmVtb3RlU3RhdHMoZnJvbSwgc3RhdHNPYmopO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBQYXJzZSBzdGF0dXMuXG4gICAgICAgICAgICBpZiAoJChwcmVzKS5maW5kKCc+eFt4bWxucz1cImh0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL211YyN1c2VyXCJdPnN0YXR1c1tjb2RlPVwiMjAxXCJdJykubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5pc093bmVyID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICB0aGlzLmNyZWF0ZU5vbkFub255bW91c1Jvb20oKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gUGFyc2Ugcm9sZXMuXG4gICAgICAgICAgICB2YXIgbWVtYmVyID0ge307XG4gICAgICAgICAgICBtZW1iZXIuc2hvdyA9ICQocHJlcykuZmluZCgnPnNob3cnKS50ZXh0KCk7XG4gICAgICAgICAgICBtZW1iZXIuc3RhdHVzID0gJChwcmVzKS5maW5kKCc+c3RhdHVzJykudGV4dCgpO1xuICAgICAgICAgICAgdmFyIHRtcCA9ICQocHJlcykuZmluZCgnPnhbeG1sbnM9XCJodHRwOi8vamFiYmVyLm9yZy9wcm90b2NvbC9tdWMjdXNlclwiXT5pdGVtJyk7XG4gICAgICAgICAgICBtZW1iZXIuYWZmaWxpYXRpb24gPSB0bXAuYXR0cignYWZmaWxpYXRpb24nKTtcbiAgICAgICAgICAgIG1lbWJlci5yb2xlID0gdG1wLmF0dHIoJ3JvbGUnKTtcblxuICAgICAgICAgICAgLy8gRm9jdXMgcmVjb2duaXRpb25cbiAgICAgICAgICAgIG1lbWJlci5qaWQgPSB0bXAuYXR0cignamlkJyk7XG4gICAgICAgICAgICBtZW1iZXIuaXNGb2N1cyA9IGZhbHNlO1xuICAgICAgICAgICAgaWYgKG1lbWJlci5qaWRcbiAgICAgICAgICAgICAgICAmJiBtZW1iZXIuamlkLmluZGV4T2YoTW9kZXJhdG9yLmdldEZvY3VzVXNlckppZCgpICsgXCIvXCIpID09IDApIHtcbiAgICAgICAgICAgICAgICBtZW1iZXIuaXNGb2N1cyA9IHRydWU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHZhciBuaWNrdGFnID0gJChwcmVzKS5maW5kKCc+bmlja1t4bWxucz1cImh0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL25pY2tcIl0nKTtcbiAgICAgICAgICAgIG1lbWJlci5kaXNwbGF5TmFtZSA9IChuaWNrdGFnLmxlbmd0aCA+IDAgPyBuaWNrdGFnLmh0bWwoKSA6IG51bGwpO1xuXG4gICAgICAgICAgICBpZiAoZnJvbSA9PSB0aGlzLm15cm9vbWppZCkge1xuICAgICAgICAgICAgICAgIGlmIChtZW1iZXIuYWZmaWxpYXRpb24gPT0gJ293bmVyJykgdGhpcy5pc093bmVyID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5yb2xlICE9PSBtZW1iZXIucm9sZSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnJvbGUgPSBtZW1iZXIucm9sZTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKE1vZGVyYXRvci5vbkxvY2FsUm9sZUNoYW5nZSlcbiAgICAgICAgICAgICAgICAgICAgICAgIE1vZGVyYXRvci5vbkxvY2FsUm9sZUNoYW5nZShmcm9tLCBtZW1iZXIsIHByZXMpO1xuICAgICAgICAgICAgICAgICAgICBVSS5vbkxvY2FsUm9sZUNoYW5nZShmcm9tLCBtZW1iZXIsIHByZXMpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBpZiAoIXRoaXMuam9pbmVkKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuam9pbmVkID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICAgICAgZXZlbnRFbWl0dGVyLmVtaXQoWE1QUEV2ZW50cy5NVUNfSk9JTkVELCBmcm9tLCBtZW1iZXIpO1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmxpc3RfbWVtYmVycy5wdXNoKGZyb20pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSBpZiAodGhpcy5tZW1iZXJzW2Zyb21dID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICAvLyBuZXcgcGFydGljaXBhbnRcbiAgICAgICAgICAgICAgICB0aGlzLm1lbWJlcnNbZnJvbV0gPSBtZW1iZXI7XG4gICAgICAgICAgICAgICAgdGhpcy5saXN0X21lbWJlcnMucHVzaChmcm9tKTtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnZW50ZXJlZCcsIGZyb20sIG1lbWJlcik7XG4gICAgICAgICAgICAgICAgaWYgKG1lbWJlci5pc0ZvY3VzKSB7XG4gICAgICAgICAgICAgICAgICAgIGZvY3VzTXVjSmlkID0gZnJvbTtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKFwiSWdub3JlIGZvY3VzOiBcIiArIGZyb20gKyBcIiwgcmVhbCBKSUQ6IFwiICsgbWVtYmVyLmppZCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICB2YXIgaWQgPSAkKHByZXMpLmZpbmQoJz51c2VySUQnKS50ZXh0KCk7XG4gICAgICAgICAgICAgICAgICAgIHZhciBlbWFpbCA9ICQocHJlcykuZmluZCgnPmVtYWlsJyk7XG4gICAgICAgICAgICAgICAgICAgIGlmIChlbWFpbC5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZCA9IGVtYWlsLnRleHQoKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBVSS5vbk11Y0VudGVyZWQoZnJvbSwgaWQsIG1lbWJlci5kaXNwbGF5TmFtZSk7XG4gICAgICAgICAgICAgICAgICAgIEFQSS50cmlnZ2VyRXZlbnQoXCJwYXJ0aWNpcGFudEpvaW5lZFwiLCB7amlkOiBmcm9tfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAvLyBQcmVzZW5jZSB1cGRhdGUgZm9yIGV4aXN0aW5nIHBhcnRpY2lwYW50XG4gICAgICAgICAgICAgICAgLy8gV2F0Y2ggcm9sZSBjaGFuZ2U6XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMubWVtYmVyc1tmcm9tXS5yb2xlICE9IG1lbWJlci5yb2xlKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMubWVtYmVyc1tmcm9tXS5yb2xlID0gbWVtYmVyLnJvbGU7XG4gICAgICAgICAgICAgICAgICAgIFVJLm9uTXVjUm9sZUNoYW5nZWQobWVtYmVyLnJvbGUsIG1lbWJlci5kaXNwbGF5TmFtZSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBBbHdheXMgdHJpZ2dlciBwcmVzZW5jZSB0byB1cGRhdGUgYmluZGluZ3NcbiAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ3ByZXNlbmNlLm11YycsIFtmcm9tLCBtZW1iZXIsIHByZXNdKTtcbiAgICAgICAgICAgIHRoaXMucGFyc2VQcmVzZW5jZShmcm9tLCBtZW1iZXIsIHByZXMpO1xuXG4gICAgICAgICAgICAvLyBUcmlnZ2VyIHN0YXR1cyBtZXNzYWdlIHVwZGF0ZVxuICAgICAgICAgICAgaWYgKG1lbWJlci5zdGF0dXMpIHtcbiAgICAgICAgICAgICAgICBVSS5vbk11Y1ByZXNlbmNlU3RhdHVzKGZyb20sIG1lbWJlcik7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9LFxuICAgICAgICBvblByZXNlbmNlVW5hdmFpbGFibGU6IGZ1bmN0aW9uIChwcmVzKSB7XG4gICAgICAgICAgICB2YXIgZnJvbSA9IHByZXMuZ2V0QXR0cmlidXRlKCdmcm9tJyk7XG4gICAgICAgICAgICAvLyBTdGF0dXMgY29kZSAxMTAgaW5kaWNhdGVzIHRoYXQgdGhpcyBub3RpZmljYXRpb24gaXMgXCJzZWxmLXByZXNlbmNlXCIuXG4gICAgICAgICAgICBpZiAoISQocHJlcykuZmluZCgnPnhbeG1sbnM9XCJodHRwOi8vamFiYmVyLm9yZy9wcm90b2NvbC9tdWMjdXNlclwiXT5zdGF0dXNbY29kZT1cIjExMFwiXScpLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIGRlbGV0ZSB0aGlzLm1lbWJlcnNbZnJvbV07XG4gICAgICAgICAgICAgICAgdGhpcy5saXN0X21lbWJlcnMuc3BsaWNlKHRoaXMubGlzdF9tZW1iZXJzLmluZGV4T2YoZnJvbSksIDEpO1xuICAgICAgICAgICAgICAgIHRoaXMub25QYXJ0aWNpcGFudExlZnQoZnJvbSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBJZiB0aGUgc3RhdHVzIGNvZGUgaXMgMTEwIHRoaXMgbWVhbnMgd2UncmUgbGVhdmluZyBhbmQgd2Ugd291bGQgbGlrZVxuICAgICAgICAgICAgLy8gdG8gcmVtb3ZlIGV2ZXJ5b25lIGVsc2UgZnJvbSBvdXIgdmlldywgc28gd2UgdHJpZ2dlciB0aGUgZXZlbnQuXG4gICAgICAgICAgICBlbHNlIGlmICh0aGlzLmxpc3RfbWVtYmVycy5sZW5ndGggPiAxKSB7XG4gICAgICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCB0aGlzLmxpc3RfbWVtYmVycy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgICAgICB2YXIgbWVtYmVyID0gdGhpcy5saXN0X21lbWJlcnNbaV07XG4gICAgICAgICAgICAgICAgICAgIGRlbGV0ZSB0aGlzLm1lbWJlcnNbaV07XG4gICAgICAgICAgICAgICAgICAgIHRoaXMubGlzdF9tZW1iZXJzLnNwbGljZShpLCAxKTtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5vblBhcnRpY2lwYW50TGVmdChtZW1iZXIpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICgkKHByZXMpLmZpbmQoJz54W3htbG5zPVwiaHR0cDovL2phYmJlci5vcmcvcHJvdG9jb2wvbXVjI3VzZXJcIl0+c3RhdHVzW2NvZGU9XCIzMDdcIl0nKS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdraWNrZWQubXVjJywgW2Zyb21dKTtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5teXJvb21qaWQgPT09IGZyb20pIHtcbiAgICAgICAgICAgICAgICAgICAgWE1QUC5kaXNwb3NlQ29uZmVyZW5jZShmYWxzZSk7XG4gICAgICAgICAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFhNUFBFdmVudHMuS0lDS0VEKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfSxcbiAgICAgICAgb25QcmVzZW5jZUVycm9yOiBmdW5jdGlvbiAocHJlcykge1xuICAgICAgICAgICAgdmFyIGZyb20gPSBwcmVzLmdldEF0dHJpYnV0ZSgnZnJvbScpO1xuICAgICAgICAgICAgaWYgKCQocHJlcykuZmluZCgnPmVycm9yW3R5cGU9XCJhdXRoXCJdPm5vdC1hdXRob3JpemVkW3htbG5zPVwidXJuOmlldGY6cGFyYW1zOnhtbDpuczp4bXBwLXN0YW56YXNcIl0nKS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnb24gcGFzc3dvcmQgcmVxdWlyZWQnLCBmcm9tKTtcbiAgICAgICAgICAgICAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgICAgICAgICAgICAgVUkub25QYXNzd29yZFJlcWl1cmVkKGZ1bmN0aW9uICh2YWx1ZSkge1xuICAgICAgICAgICAgICAgICAgICBzZWxmLmRvSm9pbihmcm9tLCB2YWx1ZSk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKCQocHJlcykuZmluZChcbiAgICAgICAgICAgICAgICAnPmVycm9yW3R5cGU9XCJjYW5jZWxcIl0+bm90LWFsbG93ZWRbeG1sbnM9XCJ1cm46aWV0ZjpwYXJhbXM6eG1sOm5zOnhtcHAtc3Rhbnphc1wiXScpLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIHZhciB0b0RvbWFpbiA9IFN0cm9waGUuZ2V0RG9tYWluRnJvbUppZChwcmVzLmdldEF0dHJpYnV0ZSgndG8nKSk7XG4gICAgICAgICAgICAgICAgaWYgKHRvRG9tYWluID09PSBjb25maWcuaG9zdHMuYW5vbnltb3VzZG9tYWluKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIHdlIGFyZSBjb25uZWN0ZWQgd2l0aCBhbm9ueW1vdXMgZG9tYWluIGFuZCBvbmx5IG5vbiBhbm9ueW1vdXMgdXNlcnMgY2FuIGNyZWF0ZSByb29tc1xuICAgICAgICAgICAgICAgICAgICAvLyB3ZSBtdXN0IGF1dGhvcml6ZSB0aGUgdXNlclxuICAgICAgICAgICAgICAgICAgICBYTVBQLnByb21wdExvZ2luKCk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKCdvblByZXNFcnJvciAnLCBwcmVzKTtcbiAgICAgICAgICAgICAgICAgICAgVUkubWVzc2FnZUhhbmRsZXIub3BlblJlcG9ydERpYWxvZyhudWxsLFxuICAgICAgICAgICAgICAgICAgICAgICAgJ09vcHMhIFNvbWV0aGluZyB3ZW50IHdyb25nIGFuZCB3ZSBjb3VsZG5gdCBjb25uZWN0IHRvIHRoZSBjb25mZXJlbmNlLicsXG4gICAgICAgICAgICAgICAgICAgICAgICBwcmVzKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUud2Fybignb25QcmVzRXJyb3IgJywgcHJlcyk7XG4gICAgICAgICAgICAgICAgVUkubWVzc2FnZUhhbmRsZXIub3BlblJlcG9ydERpYWxvZyhudWxsLFxuICAgICAgICAgICAgICAgICAgICAnT29wcyEgU29tZXRoaW5nIHdlbnQgd3JvbmcgYW5kIHdlIGNvdWxkbmB0IGNvbm5lY3QgdG8gdGhlIGNvbmZlcmVuY2UuJyxcbiAgICAgICAgICAgICAgICAgICAgcHJlcyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfSxcbiAgICAgICAgc2VuZE1lc3NhZ2U6IGZ1bmN0aW9uIChib2R5LCBuaWNrbmFtZSkge1xuICAgICAgICAgICAgdmFyIG1zZyA9ICRtc2coe3RvOiB0aGlzLnJvb21qaWQsIHR5cGU6ICdncm91cGNoYXQnfSk7XG4gICAgICAgICAgICBtc2cuYygnYm9keScsIGJvZHkpLnVwKCk7XG4gICAgICAgICAgICBpZiAobmlja25hbWUpIHtcbiAgICAgICAgICAgICAgICBtc2cuYygnbmljaycsIHt4bWxuczogJ2h0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL25pY2snfSkudChuaWNrbmFtZSkudXAoKS51cCgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmQobXNnKTtcbiAgICAgICAgICAgIEFQSS50cmlnZ2VyRXZlbnQoXCJvdXRnb2luZ01lc3NhZ2VcIiwge1wibWVzc2FnZVwiOiBib2R5fSk7XG4gICAgICAgIH0sXG4gICAgICAgIHNldFN1YmplY3Q6IGZ1bmN0aW9uIChzdWJqZWN0KSB7XG4gICAgICAgICAgICB2YXIgbXNnID0gJG1zZyh7dG86IHRoaXMucm9vbWppZCwgdHlwZTogJ2dyb3VwY2hhdCd9KTtcbiAgICAgICAgICAgIG1zZy5jKCdzdWJqZWN0Jywgc3ViamVjdCk7XG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZChtc2cpO1xuICAgICAgICAgICAgY29uc29sZS5sb2coXCJ0b3BpYyBjaGFuZ2VkIHRvIFwiICsgc3ViamVjdCk7XG4gICAgICAgIH0sXG4gICAgICAgIG9uTWVzc2FnZTogZnVuY3Rpb24gKG1zZykge1xuICAgICAgICAgICAgLy8gRklYTUU6IHRoaXMgaXMgYSBoYWNrLiBidXQgamluZ2xlIG9uIG11YyBtYWtlcyBuaWNrY2hhbmdlcyBoYXJkXG4gICAgICAgICAgICB2YXIgZnJvbSA9IG1zZy5nZXRBdHRyaWJ1dGUoJ2Zyb20nKTtcbiAgICAgICAgICAgIHZhciBuaWNrID0gJChtc2cpLmZpbmQoJz5uaWNrW3htbG5zPVwiaHR0cDovL2phYmJlci5vcmcvcHJvdG9jb2wvbmlja1wiXScpLnRleHQoKSB8fCBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChmcm9tKTtcblxuICAgICAgICAgICAgdmFyIHR4dCA9ICQobXNnKS5maW5kKCc+Ym9keScpLnRleHQoKTtcbiAgICAgICAgICAgIHZhciB0eXBlID0gbXNnLmdldEF0dHJpYnV0ZShcInR5cGVcIik7XG4gICAgICAgICAgICBpZiAodHlwZSA9PSBcImVycm9yXCIpIHtcbiAgICAgICAgICAgICAgICBVSS5jaGF0QWRkRXJyb3IoJChtc2cpLmZpbmQoJz50ZXh0JykudGV4dCgpLCB0eHQpO1xuICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgc3ViamVjdCA9ICQobXNnKS5maW5kKCc+c3ViamVjdCcpO1xuICAgICAgICAgICAgaWYgKHN1YmplY3QubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgdmFyIHN1YmplY3RUZXh0ID0gc3ViamVjdC50ZXh0KCk7XG4gICAgICAgICAgICAgICAgaWYgKHN1YmplY3RUZXh0IHx8IHN1YmplY3RUZXh0ID09IFwiXCIpIHtcbiAgICAgICAgICAgICAgICAgICAgVUkuY2hhdFNldFN1YmplY3Qoc3ViamVjdFRleHQpO1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcIlN1YmplY3QgaXMgY2hhbmdlZCB0byBcIiArIHN1YmplY3RUZXh0KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cblxuICAgICAgICAgICAgaWYgKHR4dCkge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdjaGF0JywgbmljaywgdHh0KTtcbiAgICAgICAgICAgICAgICBVSS51cGRhdGVDaGF0Q29udmVyc2F0aW9uKGZyb20sIG5pY2ssIHR4dCk7XG4gICAgICAgICAgICAgICAgaWYgKGZyb20gIT0gdGhpcy5teXJvb21qaWQpXG4gICAgICAgICAgICAgICAgICAgIEFQSS50cmlnZ2VyRXZlbnQoXCJpbmNvbWluZ01lc3NhZ2VcIixcbiAgICAgICAgICAgICAgICAgICAgICAgIHtcImZyb21cIjogZnJvbSwgXCJuaWNrXCI6IG5pY2ssIFwibWVzc2FnZVwiOiB0eHR9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9LFxuICAgICAgICBsb2NrUm9vbTogZnVuY3Rpb24gKGtleSwgb25TdWNjZXNzLCBvbkVycm9yLCBvbk5vdFN1cHBvcnRlZCkge1xuICAgICAgICAgICAgLy9odHRwOi8veG1wcC5vcmcvZXh0ZW5zaW9ucy94ZXAtMDA0NS5odG1sI3Jvb21jb25maWdcbiAgICAgICAgICAgIHZhciBvYiA9IHRoaXM7XG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZElRKCRpcSh7dG86IHRoaXMucm9vbWppZCwgdHlwZTogJ2dldCd9KS5jKCdxdWVyeScsIHt4bWxuczogJ2h0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL211YyNvd25lcid9KSxcbiAgICAgICAgICAgICAgICBmdW5jdGlvbiAocmVzKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmICgkKHJlcykuZmluZCgnPnF1ZXJ5PnhbeG1sbnM9XCJqYWJiZXI6eDpkYXRhXCJdPmZpZWxkW3Zhcj1cIm11YyNyb29tY29uZmlnX3Jvb21zZWNyZXRcIl0nKS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBmb3Jtc3VibWl0ID0gJGlxKHt0bzogb2Iucm9vbWppZCwgdHlwZTogJ3NldCd9KS5jKCdxdWVyeScsIHt4bWxuczogJ2h0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL211YyNvd25lcid9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1zdWJtaXQuYygneCcsIHt4bWxuczogJ2phYmJlcjp4OmRhdGEnLCB0eXBlOiAnc3VibWl0J30pO1xuICAgICAgICAgICAgICAgICAgICAgICAgZm9ybXN1Ym1pdC5jKCdmaWVsZCcsIHsndmFyJzogJ0ZPUk1fVFlQRSd9KS5jKCd2YWx1ZScpLnQoJ2h0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL211YyNyb29tY29uZmlnJykudXAoKS51cCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgZm9ybXN1Ym1pdC5jKCdmaWVsZCcsIHsndmFyJzogJ211YyNyb29tY29uZmlnX3Jvb21zZWNyZXQnfSkuYygndmFsdWUnKS50KGtleSkudXAoKS51cCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gRml4ZXMgYSBidWcgaW4gcHJvc29keSAwLjkuKyBodHRwczovL2NvZGUuZ29vZ2xlLmNvbS9wL2x4bXBwZC9pc3N1ZXMvZGV0YWlsP2lkPTM3M1xuICAgICAgICAgICAgICAgICAgICAgICAgZm9ybXN1Ym1pdC5jKCdmaWVsZCcsIHsndmFyJzogJ211YyNyb29tY29uZmlnX3dob2lzJ30pLmMoJ3ZhbHVlJykudCgnYW55b25lJykudXAoKS51cCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gRklYTUU6IGlzIG11YyNyb29tY29uZmlnX3Bhc3N3b3JkcHJvdGVjdGVkcm9vbSByZXF1aXJlZD9cbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kSVEoZm9ybXN1Ym1pdCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBvblN1Y2Nlc3MsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgb25FcnJvcik7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBvbk5vdFN1cHBvcnRlZCgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSwgb25FcnJvcik7XG4gICAgICAgIH0sXG4gICAgICAgIGtpY2s6IGZ1bmN0aW9uIChqaWQpIHtcbiAgICAgICAgICAgIHZhciBraWNrSVEgPSAkaXEoe3RvOiB0aGlzLnJvb21qaWQsIHR5cGU6ICdzZXQnfSlcbiAgICAgICAgICAgICAgICAuYygncXVlcnknLCB7eG1sbnM6ICdodHRwOi8vamFiYmVyLm9yZy9wcm90b2NvbC9tdWMjYWRtaW4nfSlcbiAgICAgICAgICAgICAgICAuYygnaXRlbScsIHtuaWNrOiBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpLCByb2xlOiAnbm9uZSd9KVxuICAgICAgICAgICAgICAgIC5jKCdyZWFzb24nKS50KCdZb3UgaGF2ZSBiZWVuIGtpY2tlZC4nKS51cCgpLnVwKCkudXAoKTtcblxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRJUShcbiAgICAgICAgICAgICAgICBraWNrSVEsXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKHJlc3VsdCkge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnS2ljayBwYXJ0aWNpcGFudCB3aXRoIGppZDogJywgamlkLCByZXN1bHQpO1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdLaWNrIHBhcnRpY2lwYW50IGVycm9yOiAnLCBlcnJvcik7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgIH0sXG4gICAgICAgIHNlbmRQcmVzZW5jZTogZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdmFyIHByZXMgPSAkcHJlcyh7dG86IHRoaXMucHJlc01hcFsndG8nXSB9KTtcbiAgICAgICAgICAgIHByZXMuYygneCcsIHt4bWxuczogdGhpcy5wcmVzTWFwWyd4bnMnXX0pO1xuXG4gICAgICAgICAgICBpZiAodGhpcy5wcmVzTWFwWydwYXNzd29yZCddKSB7XG4gICAgICAgICAgICAgICAgcHJlcy5jKCdwYXNzd29yZCcpLnQodGhpcy5wcmVzTWFwWydwYXNzd29yZCddKS51cCgpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBwcmVzLnVwKCk7XG5cbiAgICAgICAgICAgIC8vIFNlbmQgWEVQLTAxMTUgJ2MnIHN0YW56YSB0aGF0IGNvbnRhaW5zIG91ciBjYXBhYmlsaXRpZXMgaW5mb1xuICAgICAgICAgICAgaWYgKHRoaXMuY29ubmVjdGlvbi5jYXBzKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmNhcHMubm9kZSA9IGNvbmZpZy5jbGllbnROb2RlO1xuICAgICAgICAgICAgICAgIHByZXMuYygnYycsIHRoaXMuY29ubmVjdGlvbi5jYXBzLmdlbmVyYXRlQ2Fwc0F0dHJzKCkpLnVwKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHByZXMuYygndXNlci1hZ2VudCcsIHt4bWxuczogJ2h0dHA6Ly9qaXRzaS5vcmcvaml0bWVldC91c2VyLWFnZW50J30pXG4gICAgICAgICAgICAgICAgLnQobmF2aWdhdG9yLnVzZXJBZ2VudCkudXAoKTtcblxuICAgICAgICAgICAgaWYgKHRoaXMucHJlc01hcFsnYnJpZGdlSXNEb3duJ10pIHtcbiAgICAgICAgICAgICAgICBwcmVzLmMoJ2JyaWRnZUlzRG93bicpLnVwKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICh0aGlzLnByZXNNYXBbJ2VtYWlsJ10pIHtcbiAgICAgICAgICAgICAgICBwcmVzLmMoJ2VtYWlsJykudCh0aGlzLnByZXNNYXBbJ2VtYWlsJ10pLnVwKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICh0aGlzLnByZXNNYXBbJ3VzZXJJZCddKSB7XG4gICAgICAgICAgICAgICAgcHJlcy5jKCd1c2VySWQnKS50KHRoaXMucHJlc01hcFsndXNlcklkJ10pLnVwKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICh0aGlzLnByZXNNYXBbJ2Rpc3BsYXlOYW1lJ10pIHtcbiAgICAgICAgICAgICAgICAvLyBYRVAtMDE3MlxuICAgICAgICAgICAgICAgIHByZXMuYygnbmljaycsIHt4bWxuczogJ2h0dHA6Ly9qYWJiZXIub3JnL3Byb3RvY29sL25pY2snfSlcbiAgICAgICAgICAgICAgICAgICAgLnQodGhpcy5wcmVzTWFwWydkaXNwbGF5TmFtZSddKS51cCgpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAodGhpcy5wcmVzTWFwWydhdWRpb25zJ10pIHtcbiAgICAgICAgICAgICAgICBwcmVzLmMoJ2F1ZGlvbXV0ZWQnLCB7eG1sbnM6IHRoaXMucHJlc01hcFsnYXVkaW9ucyddfSlcbiAgICAgICAgICAgICAgICAgICAgLnQodGhpcy5wcmVzTWFwWydhdWRpb211dGVkJ10pLnVwKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICh0aGlzLnByZXNNYXBbJ3ZpZGVvbnMnXSkge1xuICAgICAgICAgICAgICAgIHByZXMuYygndmlkZW9tdXRlZCcsIHt4bWxuczogdGhpcy5wcmVzTWFwWyd2aWRlb25zJ119KVxuICAgICAgICAgICAgICAgICAgICAudCh0aGlzLnByZXNNYXBbJ3ZpZGVvbXV0ZWQnXSkudXAoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHRoaXMucHJlc01hcFsnc3RhdHNucyddKSB7XG4gICAgICAgICAgICAgICAgdmFyIHN0YXRzID0gcHJlcy5jKCdzdGF0cycsIHt4bWxuczogdGhpcy5wcmVzTWFwWydzdGF0c25zJ119KTtcbiAgICAgICAgICAgICAgICBmb3IgKHZhciBzdGF0IGluIHRoaXMucHJlc01hcFtcInN0YXRzXCJdKVxuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5wcmVzTWFwW1wic3RhdHNcIl1bc3RhdF0gIT0gbnVsbClcbiAgICAgICAgICAgICAgICAgICAgICAgIHN0YXRzLmMoXCJzdGF0XCIsIHtuYW1lOiBzdGF0LCB2YWx1ZTogdGhpcy5wcmVzTWFwW1wic3RhdHNcIl1bc3RhdF19KS51cCgpO1xuICAgICAgICAgICAgICAgIHByZXMudXAoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHRoaXMucHJlc01hcFsncHJlemlucyddKSB7XG4gICAgICAgICAgICAgICAgcHJlcy5jKCdwcmV6aScsXG4gICAgICAgICAgICAgICAgICAgIHt4bWxuczogdGhpcy5wcmVzTWFwWydwcmV6aW5zJ10sXG4gICAgICAgICAgICAgICAgICAgICAgICAndXJsJzogdGhpcy5wcmVzTWFwWydwcmV6aXVybCddfSlcbiAgICAgICAgICAgICAgICAgICAgLmMoJ2N1cnJlbnQnKS50KHRoaXMucHJlc01hcFsncHJlemljdXJyZW50J10pLnVwKCkudXAoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHRoaXMucHJlc01hcFsnZXRoZXJwYWRucyddKSB7XG4gICAgICAgICAgICAgICAgcHJlcy5jKCdldGhlcnBhZCcsIHt4bWxuczogdGhpcy5wcmVzTWFwWydldGhlcnBhZG5zJ119KVxuICAgICAgICAgICAgICAgICAgICAudCh0aGlzLnByZXNNYXBbJ2V0aGVycGFkbmFtZSddKS51cCgpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAodGhpcy5wcmVzTWFwWydtZWRpYW5zJ10pIHtcbiAgICAgICAgICAgICAgICBwcmVzLmMoJ21lZGlhJywge3htbG5zOiB0aGlzLnByZXNNYXBbJ21lZGlhbnMnXX0pO1xuICAgICAgICAgICAgICAgIHZhciBzb3VyY2VOdW1iZXIgPSAwO1xuICAgICAgICAgICAgICAgIE9iamVjdC5rZXlzKHRoaXMucHJlc01hcCkuZm9yRWFjaChmdW5jdGlvbiAoa2V5KSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChrZXkuaW5kZXhPZignc291cmNlJykgPj0gMCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgc291cmNlTnVtYmVyKys7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICBpZiAoc291cmNlTnVtYmVyID4gMClcbiAgICAgICAgICAgICAgICAgICAgZm9yICh2YXIgaSA9IDE7IGkgPD0gc291cmNlTnVtYmVyIC8gMzsgaSsrKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBwcmVzLmMoJ3NvdXJjZScsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAge3R5cGU6IHRoaXMucHJlc01hcFsnc291cmNlJyArIGkgKyAnX3R5cGUnXSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3NyYzogdGhpcy5wcmVzTWFwWydzb3VyY2UnICsgaSArICdfc3NyYyddLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXJlY3Rpb246IHRoaXMucHJlc01hcFsnc291cmNlJyArIGkgKyAnX2RpcmVjdGlvbiddXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8fCAnc2VuZHJlY3YnIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICkudXAoKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBwcmVzLnVwKCk7XG4vLyAgICAgICAgY29uc29sZS5kZWJ1ZyhwcmVzLnRvU3RyaW5nKCkpO1xuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmQocHJlcyk7XG4gICAgICAgIH0sXG4gICAgICAgIGFkZERpc3BsYXlOYW1lVG9QcmVzZW5jZTogZnVuY3Rpb24gKGRpc3BsYXlOYW1lKSB7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ2Rpc3BsYXlOYW1lJ10gPSBkaXNwbGF5TmFtZTtcbiAgICAgICAgfSxcbiAgICAgICAgYWRkTWVkaWFUb1ByZXNlbmNlOiBmdW5jdGlvbiAoc291cmNlTnVtYmVyLCBtdHlwZSwgc3NyY3MsIGRpcmVjdGlvbikge1xuICAgICAgICAgICAgaWYgKCF0aGlzLnByZXNNYXBbJ21lZGlhbnMnXSlcbiAgICAgICAgICAgICAgICB0aGlzLnByZXNNYXBbJ21lZGlhbnMnXSA9ICdodHRwOi8vZXN0b3MuZGUvbnMvbWpzJztcblxuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydzb3VyY2UnICsgc291cmNlTnVtYmVyICsgJ190eXBlJ10gPSBtdHlwZTtcbiAgICAgICAgICAgIHRoaXMucHJlc01hcFsnc291cmNlJyArIHNvdXJjZU51bWJlciArICdfc3NyYyddID0gc3NyY3M7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ3NvdXJjZScgKyBzb3VyY2VOdW1iZXIgKyAnX2RpcmVjdGlvbiddID0gZGlyZWN0aW9uO1xuICAgICAgICB9LFxuICAgICAgICBjbGVhclByZXNlbmNlTWVkaWE6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgICAgICAgIE9iamVjdC5rZXlzKHRoaXMucHJlc01hcCkuZm9yRWFjaChmdW5jdGlvbiAoa2V5KSB7XG4gICAgICAgICAgICAgICAgaWYgKGtleS5pbmRleE9mKCdzb3VyY2UnKSAhPSAtMSkge1xuICAgICAgICAgICAgICAgICAgICBkZWxldGUgc2VsZi5wcmVzTWFwW2tleV07XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0sXG4gICAgICAgIGFkZFByZXppVG9QcmVzZW5jZTogZnVuY3Rpb24gKHVybCwgY3VycmVudFNsaWRlKSB7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ3ByZXppbnMnXSA9ICdodHRwOi8vaml0c2kub3JnL2ppdG1lZXQvcHJlemknO1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydwcmV6aXVybCddID0gdXJsO1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydwcmV6aWN1cnJlbnQnXSA9IGN1cnJlbnRTbGlkZTtcbiAgICAgICAgfSxcbiAgICAgICAgcmVtb3ZlUHJlemlGcm9tUHJlc2VuY2U6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIGRlbGV0ZSB0aGlzLnByZXNNYXBbJ3ByZXppbnMnXTtcbiAgICAgICAgICAgIGRlbGV0ZSB0aGlzLnByZXNNYXBbJ3ByZXppdXJsJ107XG4gICAgICAgICAgICBkZWxldGUgdGhpcy5wcmVzTWFwWydwcmV6aWN1cnJlbnQnXTtcbiAgICAgICAgfSxcbiAgICAgICAgYWRkQ3VycmVudFNsaWRlVG9QcmVzZW5jZTogZnVuY3Rpb24gKGN1cnJlbnRTbGlkZSkge1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydwcmV6aWN1cnJlbnQnXSA9IGN1cnJlbnRTbGlkZTtcbiAgICAgICAgfSxcbiAgICAgICAgZ2V0UHJlemk6IGZ1bmN0aW9uIChyb29tamlkKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5wcmV6aU1hcFtyb29tamlkXTtcbiAgICAgICAgfSxcbiAgICAgICAgYWRkRXRoZXJwYWRUb1ByZXNlbmNlOiBmdW5jdGlvbiAoZXRoZXJwYWROYW1lKSB7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ2V0aGVycGFkbnMnXSA9ICdodHRwOi8vaml0c2kub3JnL2ppdG1lZXQvZXRoZXJwYWQnO1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydldGhlcnBhZG5hbWUnXSA9IGV0aGVycGFkTmFtZTtcbiAgICAgICAgfSxcbiAgICAgICAgYWRkQXVkaW9JbmZvVG9QcmVzZW5jZTogZnVuY3Rpb24gKGlzTXV0ZWQpIHtcbiAgICAgICAgICAgIHRoaXMucHJlc01hcFsnYXVkaW9ucyddID0gJ2h0dHA6Ly9qaXRzaS5vcmcvaml0bWVldC9hdWRpbyc7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ2F1ZGlvbXV0ZWQnXSA9IGlzTXV0ZWQudG9TdHJpbmcoKTtcbiAgICAgICAgfSxcbiAgICAgICAgYWRkVmlkZW9JbmZvVG9QcmVzZW5jZTogZnVuY3Rpb24gKGlzTXV0ZWQpIHtcbiAgICAgICAgICAgIHRoaXMucHJlc01hcFsndmlkZW9ucyddID0gJ2h0dHA6Ly9qaXRzaS5vcmcvaml0bWVldC92aWRlbyc7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ3ZpZGVvbXV0ZWQnXSA9IGlzTXV0ZWQudG9TdHJpbmcoKTtcbiAgICAgICAgfSxcbiAgICAgICAgYWRkQ29ubmVjdGlvbkluZm9Ub1ByZXNlbmNlOiBmdW5jdGlvbiAoc3RhdHMpIHtcbiAgICAgICAgICAgIHRoaXMucHJlc01hcFsnc3RhdHNucyddID0gJ2h0dHA6Ly9qaXRzaS5vcmcvaml0bWVldC9zdGF0cyc7XG4gICAgICAgICAgICB0aGlzLnByZXNNYXBbJ3N0YXRzJ10gPSBzdGF0cztcbiAgICAgICAgfSxcbiAgICAgICAgZmluZEppZEZyb21SZXNvdXJjZTogZnVuY3Rpb24gKHJlc291cmNlSmlkKSB7XG4gICAgICAgICAgICBpZiAocmVzb3VyY2VKaWQgJiZcbiAgICAgICAgICAgICAgICByZXNvdXJjZUppZCA9PT0gU3Ryb3BoZS5nZXRSZXNvdXJjZUZyb21KaWQodGhpcy5teXJvb21qaWQpKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMubXlyb29tamlkO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFyIHBlZXJKaWQgPSBudWxsO1xuICAgICAgICAgICAgT2JqZWN0LmtleXModGhpcy5tZW1iZXJzKS5zb21lKGZ1bmN0aW9uIChqaWQpIHtcbiAgICAgICAgICAgICAgICBwZWVySmlkID0gamlkO1xuICAgICAgICAgICAgICAgIHJldHVybiBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChqaWQpID09PSByZXNvdXJjZUppZDtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgcmV0dXJuIHBlZXJKaWQ7XG4gICAgICAgIH0sXG4gICAgICAgIGFkZEJyaWRnZUlzRG93blRvUHJlc2VuY2U6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHRoaXMucHJlc01hcFsnYnJpZGdlSXNEb3duJ10gPSB0cnVlO1xuICAgICAgICB9LFxuICAgICAgICBhZGRFbWFpbFRvUHJlc2VuY2U6IGZ1bmN0aW9uIChlbWFpbCkge1xuICAgICAgICAgICAgdGhpcy5wcmVzTWFwWydlbWFpbCddID0gZW1haWw7XG4gICAgICAgIH0sXG4gICAgICAgIGFkZFVzZXJJZFRvUHJlc2VuY2U6IGZ1bmN0aW9uICh1c2VySWQpIHtcbiAgICAgICAgICAgIHRoaXMucHJlc01hcFsndXNlcklkJ10gPSB1c2VySWQ7XG4gICAgICAgIH0sXG4gICAgICAgIGlzTW9kZXJhdG9yOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5yb2xlID09PSAnbW9kZXJhdG9yJztcbiAgICAgICAgfSxcbiAgICAgICAgZ2V0TWVtYmVyUm9sZTogZnVuY3Rpb24gKHBlZXJKaWQpIHtcbiAgICAgICAgICAgIGlmICh0aGlzLm1lbWJlcnNbcGVlckppZF0pIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5tZW1iZXJzW3BlZXJKaWRdLnJvbGU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgfSxcbiAgICAgICAgb25QYXJ0aWNpcGFudExlZnQ6IGZ1bmN0aW9uIChqaWQpIHtcbiAgICAgICAgICAgIFVJLm9uTXVjTGVmdChqaWQpO1xuXG4gICAgICAgICAgICBBUEkudHJpZ2dlckV2ZW50KFwicGFydGljaXBhbnRMZWZ0XCIsIHtqaWQ6IGppZH0pO1xuXG4gICAgICAgICAgICBkZWxldGUgamlkMlNzcmNbamlkXTtcblxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmppbmdsZS50ZXJtaW5hdGVCeUppZChqaWQpO1xuXG4gICAgICAgICAgICBpZiAodGhpcy5nZXRQcmV6aShqaWQpKSB7XG4gICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcigncHJlc2VudGF0aW9ucmVtb3ZlZC5tdWMnLFxuICAgICAgICAgICAgICAgICAgICBbamlkLCB0aGlzLmdldFByZXppKGppZCldKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgTW9kZXJhdG9yLm9uTXVjTGVmdChqaWQpO1xuICAgICAgICB9LFxuICAgICAgICBwYXJzZVByZXNlbmNlOiBmdW5jdGlvbiAoZnJvbSwgbWVtZWJlciwgcHJlcykge1xuICAgICAgICAgICAgaWYoJChwcmVzKS5maW5kKFwiPmJyaWRnZUlzRG93blwiKS5sZW5ndGggPiAwICYmICFicmlkZ2VJc0Rvd24pIHtcbiAgICAgICAgICAgICAgICBicmlkZ2VJc0Rvd24gPSB0cnVlO1xuICAgICAgICAgICAgICAgIGV2ZW50RW1pdHRlci5lbWl0KFhNUFBFdmVudHMuQlJJREdFX0RPV04pO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZihtZW1lYmVyLmlzRm9jdXMpXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuXG4gICAgICAgICAgICAvLyBSZW1vdmUgb2xkIHNzcmNzIGNvbWluZyBmcm9tIHRoZSBqaWRcbiAgICAgICAgICAgIE9iamVjdC5rZXlzKHNzcmMyamlkKS5mb3JFYWNoKGZ1bmN0aW9uIChzc3JjKSB7XG4gICAgICAgICAgICAgICAgaWYgKHNzcmMyamlkW3NzcmNdID09IGppZCkge1xuICAgICAgICAgICAgICAgICAgICBkZWxldGUgc3NyYzJqaWRbc3NyY107XG4gICAgICAgICAgICAgICAgICAgIGRlbGV0ZSBzc3JjMnZpZGVvVHlwZVtzc3JjXTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgdmFyIGNoYW5nZWRTdHJlYW1zID0gW107XG4gICAgICAgICAgICAkKHByZXMpLmZpbmQoJz5tZWRpYVt4bWxucz1cImh0dHA6Ly9lc3Rvcy5kZS9ucy9tanNcIl0+c291cmNlJykuZWFjaChmdW5jdGlvbiAoaWR4LCBzc3JjKSB7XG4gICAgICAgICAgICAgICAgLy9jb25zb2xlLmxvZyhqaWQsICdhc3NvYyBzc3JjJywgc3NyYy5nZXRBdHRyaWJ1dGUoJ3R5cGUnKSwgc3NyYy5nZXRBdHRyaWJ1dGUoJ3NzcmMnKSk7XG4gICAgICAgICAgICAgICAgdmFyIHNzcmNWID0gc3NyYy5nZXRBdHRyaWJ1dGUoJ3NzcmMnKTtcbiAgICAgICAgICAgICAgICBzc3JjMmppZFtzc3JjVl0gPSBmcm9tO1xuICAgICAgICAgICAgICAgIG5vdFJlY2VpdmVkU1NSQ3MucHVzaChzc3JjVik7XG5cbiAgICAgICAgICAgICAgICB2YXIgdHlwZSA9IHNzcmMuZ2V0QXR0cmlidXRlKCd0eXBlJyk7XG4gICAgICAgICAgICAgICAgc3NyYzJ2aWRlb1R5cGVbc3NyY1ZdID0gdHlwZTtcblxuICAgICAgICAgICAgICAgIHZhciBkaXJlY3Rpb24gPSBzc3JjLmdldEF0dHJpYnV0ZSgnZGlyZWN0aW9uJyk7XG5cbiAgICAgICAgICAgICAgICBjaGFuZ2VkU3RyZWFtcy5wdXNoKHt0eXBlOiB0eXBlLCBkaXJlY3Rpb246IGRpcmVjdGlvbn0pO1xuXG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgZXZlbnRFbWl0dGVyLmVtaXQoWE1QUEV2ZW50cy5DSEFOR0VEX1NUUkVBTVMsIGZyb20sIGNoYW5nZWRTdHJlYW1zKTtcblxuICAgICAgICAgICAgdmFyIGRpc3BsYXlOYW1lID0gIWNvbmZpZy5kaXNwbGF5Smlkc1xuICAgICAgICAgICAgICAgID8gbWVtZWJlci5kaXNwbGF5TmFtZSA6IFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGZyb20pO1xuXG4gICAgICAgICAgICBpZiAoZGlzcGxheU5hbWUgJiYgZGlzcGxheU5hbWUubGVuZ3RoID4gMClcbiAgICAgICAgICAgIHtcbi8vICAgICAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ2Rpc3BsYXluYW1lY2hhbmdlZCcsXG4vLyAgICAgICAgICAgICAgICAgICAgW2ppZCwgZGlzcGxheU5hbWVdKTtcbiAgICAgICAgICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChYTVBQRXZlbnRzLkRJU1BMQVlfTkFNRV9DSEFOR0VELCBmcm9tLCBkaXNwbGF5TmFtZSk7XG4gICAgICAgICAgICB9XG5cblxuICAgICAgICAgICAgdmFyIGlkID0gJChwcmVzKS5maW5kKCc+dXNlcklEJykudGV4dCgpO1xuICAgICAgICAgICAgdmFyIGVtYWlsID0gJChwcmVzKS5maW5kKCc+ZW1haWwnKTtcbiAgICAgICAgICAgIGlmKGVtYWlsLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgICBpZCA9IGVtYWlsLnRleHQoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgZXZlbnRFbWl0dGVyLmVtaXQoWE1QUEV2ZW50cy5VU0VSX0lEX0NIQU5HRUQsIGZyb20sIGlkKTtcbiAgICAgICAgfVxuICAgIH0pO1xufTtcblxuIiwiLyoganNoaW50IC1XMTE3ICovXG5cbnZhciBKaW5nbGVTZXNzaW9uID0gcmVxdWlyZShcIi4vSmluZ2xlU2Vzc2lvblwiKTtcblxuZnVuY3Rpb24gQ2FsbEluY29taW5nSmluZ2xlKHNpZCwgY29ubmVjdGlvbikge1xuICAgIHZhciBzZXNzID0gY29ubmVjdGlvbi5qaW5nbGUuc2Vzc2lvbnNbc2lkXTtcblxuICAgIC8vIFRPRE86IGRvIHdlIGNoZWNrIGFjdGl2ZWNhbGwgPT0gbnVsbD9cbiAgICBhY3RpdmVjYWxsID0gc2VzcztcblxuICAgIHN0YXRpc3RpY3Mub25Db25mZXJlbmNlQ3JlYXRlZChzZXNzKTtcbiAgICBSVEMub25Db25mZXJlbmNlQ3JlYXRlZChzZXNzKTtcblxuICAgIC8vIFRPRE86IGNoZWNrIGFmZmlsaWF0aW9uIGFuZC9vciByb2xlXG4gICAgY29uc29sZS5sb2coJ2VtdWMgZGF0YSBmb3InLCBzZXNzLnBlZXJqaWQsIGNvbm5lY3Rpb24uZW11Yy5tZW1iZXJzW3Nlc3MucGVlcmppZF0pO1xuICAgIHNlc3MudXNlZHJpcCA9IHRydWU7IC8vIG5vdC1zby1uYWl2ZSB0cmlja2xlIGljZVxuICAgIHNlc3Muc2VuZEFuc3dlcigpO1xuICAgIHNlc3MuYWNjZXB0KCk7XG5cbn07XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24oWE1QUClcbntcbiAgICBTdHJvcGhlLmFkZENvbm5lY3Rpb25QbHVnaW4oJ2ppbmdsZScsIHtcbiAgICAgICAgY29ubmVjdGlvbjogbnVsbCxcbiAgICAgICAgc2Vzc2lvbnM6IHt9LFxuICAgICAgICBqaWQyc2Vzc2lvbjoge30sXG4gICAgICAgIGljZV9jb25maWc6IHtpY2VTZXJ2ZXJzOiBbXX0sXG4gICAgICAgIHBjX2NvbnN0cmFpbnRzOiB7fSxcbiAgICAgICAgbWVkaWFfY29uc3RyYWludHM6IHtcbiAgICAgICAgICAgIG1hbmRhdG9yeToge1xuICAgICAgICAgICAgICAgICdPZmZlclRvUmVjZWl2ZUF1ZGlvJzogdHJ1ZSxcbiAgICAgICAgICAgICAgICAnT2ZmZXJUb1JlY2VpdmVWaWRlbyc6IHRydWVcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIE1vekRvbnRPZmZlckRhdGFDaGFubmVsOiB0cnVlIHdoZW4gdGhpcyBpcyBmaXJlZm94XG4gICAgICAgIH0sXG4gICAgICAgIGluaXQ6IGZ1bmN0aW9uIChjb25uKSB7XG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24gPSBjb25uO1xuICAgICAgICAgICAgaWYgKHRoaXMuY29ubmVjdGlvbi5kaXNjbykge1xuICAgICAgICAgICAgICAgIC8vIGh0dHA6Ly94bXBwLm9yZy9leHRlbnNpb25zL3hlcC0wMTY3Lmh0bWwjc3VwcG9ydFxuICAgICAgICAgICAgICAgIC8vIGh0dHA6Ly94bXBwLm9yZy9leHRlbnNpb25zL3hlcC0wMTc2Lmh0bWwjc3VwcG9ydFxuICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5kaXNjby5hZGRGZWF0dXJlKCd1cm46eG1wcDpqaW5nbGU6MScpO1xuICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5kaXNjby5hZGRGZWF0dXJlKCd1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6MScpO1xuICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5kaXNjby5hZGRGZWF0dXJlKCd1cm46eG1wcDpqaW5nbGU6dHJhbnNwb3J0czppY2UtdWRwOjEnKTtcbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uZGlzY28uYWRkRmVhdHVyZSgndXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOmF1ZGlvJyk7XG4gICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmRpc2NvLmFkZEZlYXR1cmUoJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDp2aWRlbycpO1xuXG5cbiAgICAgICAgICAgICAgICAvLyB0aGlzIGlzIGRlYWx0IHdpdGggYnkgU0RQIE8vQSBzbyB3ZSBkb24ndCBuZWVkIHRvIGFubm91Y2UgdGhpc1xuICAgICAgICAgICAgICAgIC8vdGhpcy5jb25uZWN0aW9uLmRpc2NvLmFkZEZlYXR1cmUoJ3Vybjp4bXBwOmppbmdsZTphcHBzOnJ0cDpydGNwLWZiOjAnKTsgLy8gWEVQLTAyOTNcbiAgICAgICAgICAgICAgICAvL3RoaXMuY29ubmVjdGlvbi5kaXNjby5hZGRGZWF0dXJlKCd1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6cnRwLWhkcmV4dDowJyk7IC8vIFhFUC0wMjk0XG4gICAgICAgICAgICAgICAgaWYgKGNvbmZpZy51c2VSdGNwTXV4KSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5kaXNjby5hZGRGZWF0dXJlKCd1cm46aWV0ZjpyZmM6NTc2MScpOyAvLyBydGNwLW11eFxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBpZiAoY29uZmlnLnVzZUJ1bmRsZSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uZGlzY28uYWRkRmVhdHVyZSgndXJuOmlldGY6cmZjOjU4ODgnKTsgLy8gYT1ncm91cCwgZS5nLiBidW5kbGVcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgLy90aGlzLmNvbm5lY3Rpb24uZGlzY28uYWRkRmVhdHVyZSgndXJuOmlldGY6cmZjOjU1NzYnKTsgLy8gYT1zc3JjXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uYWRkSGFuZGxlcih0aGlzLm9uSmluZ2xlLmJpbmQodGhpcyksICd1cm46eG1wcDpqaW5nbGU6MScsICdpcScsICdzZXQnLCBudWxsLCBudWxsKTtcbiAgICAgICAgfSxcbiAgICAgICAgb25KaW5nbGU6IGZ1bmN0aW9uIChpcSkge1xuICAgICAgICAgICAgdmFyIHNpZCA9ICQoaXEpLmZpbmQoJ2ppbmdsZScpLmF0dHIoJ3NpZCcpO1xuICAgICAgICAgICAgdmFyIGFjdGlvbiA9ICQoaXEpLmZpbmQoJ2ppbmdsZScpLmF0dHIoJ2FjdGlvbicpO1xuICAgICAgICAgICAgdmFyIGZyb21KaWQgPSBpcS5nZXRBdHRyaWJ1dGUoJ2Zyb20nKTtcbiAgICAgICAgICAgIC8vIHNlbmQgYWNrIGZpcnN0XG4gICAgICAgICAgICB2YXIgYWNrID0gJGlxKHt0eXBlOiAncmVzdWx0JyxcbiAgICAgICAgICAgICAgICB0bzogZnJvbUppZCxcbiAgICAgICAgICAgICAgICBpZDogaXEuZ2V0QXR0cmlidXRlKCdpZCcpXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdvbiBqaW5nbGUgJyArIGFjdGlvbiArICcgZnJvbSAnICsgZnJvbUppZCwgaXEpO1xuICAgICAgICAgICAgdmFyIHNlc3MgPSB0aGlzLnNlc3Npb25zW3NpZF07XG4gICAgICAgICAgICBpZiAoJ3Nlc3Npb24taW5pdGlhdGUnICE9IGFjdGlvbikge1xuICAgICAgICAgICAgICAgIGlmIChzZXNzID09PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgIGFjay50eXBlID0gJ2Vycm9yJztcbiAgICAgICAgICAgICAgICAgICAgYWNrLmMoJ2Vycm9yJywge3R5cGU6ICdjYW5jZWwnfSlcbiAgICAgICAgICAgICAgICAgICAgICAgIC5jKCdpdGVtLW5vdC1mb3VuZCcsIHt4bWxuczogJ3VybjppZXRmOnBhcmFtczp4bWw6bnM6eG1wcC1zdGFuemFzJ30pLnVwKClcbiAgICAgICAgICAgICAgICAgICAgICAgIC5jKCd1bmtub3duLXNlc3Npb24nLCB7eG1sbnM6ICd1cm46eG1wcDpqaW5nbGU6ZXJyb3JzOjEnfSk7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kKGFjayk7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAvLyBjb21wYXJlIGZyb20gdG8gc2Vzcy5wZWVyamlkIChiYXJlIGppZCBjb21wYXJpc29uIGZvciBsYXRlciBjb21wYXQgd2l0aCBtZXNzYWdlLW1vZGUpXG4gICAgICAgICAgICAgICAgLy8gbG9jYWwgamlkIGlzIG5vdCBjaGVja2VkXG4gICAgICAgICAgICAgICAgaWYgKFN0cm9waGUuZ2V0QmFyZUppZEZyb21KaWQoZnJvbUppZCkgIT0gU3Ryb3BoZS5nZXRCYXJlSmlkRnJvbUppZChzZXNzLnBlZXJqaWQpKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybignamlkIG1pc21hdGNoIGZvciBzZXNzaW9uIGlkJywgc2lkLCBmcm9tSmlkLCBzZXNzLnBlZXJqaWQpO1xuICAgICAgICAgICAgICAgICAgICBhY2sudHlwZSA9ICdlcnJvcic7XG4gICAgICAgICAgICAgICAgICAgIGFjay5jKCdlcnJvcicsIHt0eXBlOiAnY2FuY2VsJ30pXG4gICAgICAgICAgICAgICAgICAgICAgICAuYygnaXRlbS1ub3QtZm91bmQnLCB7eG1sbnM6ICd1cm46aWV0ZjpwYXJhbXM6eG1sOm5zOnhtcHAtc3Rhbnphcyd9KS51cCgpXG4gICAgICAgICAgICAgICAgICAgICAgICAuYygndW5rbm93bi1zZXNzaW9uJywge3htbG5zOiAndXJuOnhtcHA6amluZ2xlOmVycm9yczoxJ30pO1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZChhY2spO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHNlc3MgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIC8vIGV4aXN0aW5nIHNlc3Npb24gd2l0aCBzYW1lIHNlc3Npb24gaWRcbiAgICAgICAgICAgICAgICAvLyB0aGlzIG1pZ2h0IGJlIG91dC1vZi1vcmRlciBpZiB0aGUgc2Vzcy5wZWVyamlkIGlzIHRoZSBzYW1lIGFzIGZyb21cbiAgICAgICAgICAgICAgICBhY2sudHlwZSA9ICdlcnJvcic7XG4gICAgICAgICAgICAgICAgYWNrLmMoJ2Vycm9yJywge3R5cGU6ICdjYW5jZWwnfSlcbiAgICAgICAgICAgICAgICAgICAgLmMoJ3NlcnZpY2UtdW5hdmFpbGFibGUnLCB7eG1sbnM6ICd1cm46aWV0ZjpwYXJhbXM6eG1sOm5zOnhtcHAtc3Rhbnphcyd9KS51cCgpO1xuICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybignZHVwbGljYXRlIHNlc3Npb24gaWQnLCBzaWQpO1xuICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kKGFjayk7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBGSVhNRTogY2hlY2sgZm9yIGEgZGVmaW5lZCBhY3Rpb25cbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kKGFjayk7XG4gICAgICAgICAgICAvLyBzZWUgaHR0cDovL3htcHAub3JnL2V4dGVuc2lvbnMveGVwLTAxNjYuaHRtbCNjb25jZXB0cy1zZXNzaW9uXG4gICAgICAgICAgICBzd2l0Y2ggKGFjdGlvbikge1xuICAgICAgICAgICAgICAgIGNhc2UgJ3Nlc3Npb24taW5pdGlhdGUnOlxuICAgICAgICAgICAgICAgICAgICBzZXNzID0gbmV3IEppbmdsZVNlc3Npb24oXG4gICAgICAgICAgICAgICAgICAgICAgICAkKGlxKS5hdHRyKCd0bycpLCAkKGlxKS5maW5kKCdqaW5nbGUnKS5hdHRyKCdzaWQnKSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbiwgWE1QUCk7XG4gICAgICAgICAgICAgICAgICAgIC8vIGNvbmZpZ3VyZSBzZXNzaW9uXG5cbiAgICAgICAgICAgICAgICAgICAgc2Vzcy5tZWRpYV9jb25zdHJhaW50cyA9IHRoaXMubWVkaWFfY29uc3RyYWludHM7XG4gICAgICAgICAgICAgICAgICAgIHNlc3MucGNfY29uc3RyYWludHMgPSB0aGlzLnBjX2NvbnN0cmFpbnRzO1xuICAgICAgICAgICAgICAgICAgICBzZXNzLmljZV9jb25maWcgPSB0aGlzLmljZV9jb25maWc7XG5cbiAgICAgICAgICAgICAgICAgICAgc2Vzcy5pbml0aWF0ZShmcm9tSmlkLCBmYWxzZSk7XG4gICAgICAgICAgICAgICAgICAgIC8vIEZJWE1FOiBzZXRSZW1vdGVEZXNjcmlwdGlvbiBzaG91bGQgb25seSBiZSBkb25lIHdoZW4gdGhpcyBjYWxsIGlzIHRvIGJlIGFjY2VwdGVkXG4gICAgICAgICAgICAgICAgICAgIHNlc3Muc2V0UmVtb3RlRGVzY3JpcHRpb24oJChpcSkuZmluZCgnPmppbmdsZScpLCAnb2ZmZXInKTtcblxuICAgICAgICAgICAgICAgICAgICB0aGlzLnNlc3Npb25zW3Nlc3Muc2lkXSA9IHNlc3M7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuamlkMnNlc3Npb25bc2Vzcy5wZWVyamlkXSA9IHNlc3M7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gdGhlIGNhbGxiYWNrIHNob3VsZCBlaXRoZXJcbiAgICAgICAgICAgICAgICAgICAgLy8gLnNlbmRBbnN3ZXIgYW5kIC5hY2NlcHRcbiAgICAgICAgICAgICAgICAgICAgLy8gb3IgLnNlbmRUZXJtaW5hdGUgLS0gbm90IG5lY2Vzc2FyaWx5IHN5bmNocm9udXNcbiAgICAgICAgICAgICAgICAgICAgQ2FsbEluY29taW5nSmluZ2xlKHNlc3Muc2lkLCB0aGlzLmNvbm5lY3Rpb24pO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlICdzZXNzaW9uLWFjY2VwdCc6XG4gICAgICAgICAgICAgICAgICAgIHNlc3Muc2V0UmVtb3RlRGVzY3JpcHRpb24oJChpcSkuZmluZCgnPmppbmdsZScpLCAnYW5zd2VyJyk7XG4gICAgICAgICAgICAgICAgICAgIHNlc3MuYWNjZXB0KCk7XG4gICAgICAgICAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ2NhbGxhY2NlcHRlZC5qaW5nbGUnLCBbc2Vzcy5zaWRdKTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAnc2Vzc2lvbi10ZXJtaW5hdGUnOlxuICAgICAgICAgICAgICAgICAgICAvLyBJZiB0aGlzIGlzIG5vdCB0aGUgZm9jdXMgc2VuZGluZyB0aGUgdGVybWluYXRlLCB3ZSBoYXZlXG4gICAgICAgICAgICAgICAgICAgIC8vIG5vdGhpbmcgbW9yZSB0byBkbyBoZXJlLlxuICAgICAgICAgICAgICAgICAgICBpZiAoT2JqZWN0LmtleXModGhpcy5zZXNzaW9ucykubGVuZ3RoIDwgMVxuICAgICAgICAgICAgICAgICAgICAgICAgfHwgISh0aGlzLnNlc3Npb25zW09iamVjdC5rZXlzKHRoaXMuc2Vzc2lvbnMpWzBdXVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluc3RhbmNlb2YgSmluZ2xlU2Vzc2lvbikpXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCd0ZXJtaW5hdGluZy4uLicsIHNlc3Muc2lkKTtcbiAgICAgICAgICAgICAgICAgICAgc2Vzcy50ZXJtaW5hdGUoKTtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy50ZXJtaW5hdGUoc2Vzcy5zaWQpO1xuICAgICAgICAgICAgICAgICAgICBpZiAoJChpcSkuZmluZCgnPmppbmdsZT5yZWFzb24nKS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ2NhbGx0ZXJtaW5hdGVkLmppbmdsZScsIFtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXNzLnNpZCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXNzLnBlZXJqaWQsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJChpcSkuZmluZCgnPmppbmdsZT5yZWFzb24+OmZpcnN0JylbMF0udGFnTmFtZSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAkKGlxKS5maW5kKCc+amluZ2xlPnJlYXNvbj50ZXh0JykudGV4dCgpXG4gICAgICAgICAgICAgICAgICAgICAgICBdKTtcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICQoZG9jdW1lbnQpLnRyaWdnZXIoJ2NhbGx0ZXJtaW5hdGVkLmppbmdsZScsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgW3Nlc3Muc2lkLCBzZXNzLnBlZXJqaWRdKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlICd0cmFuc3BvcnQtaW5mbyc6XG4gICAgICAgICAgICAgICAgICAgIHNlc3MuYWRkSWNlQ2FuZGlkYXRlKCQoaXEpLmZpbmQoJz5qaW5nbGU+Y29udGVudCcpKTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAnc2Vzc2lvbi1pbmZvJzpcbiAgICAgICAgICAgICAgICAgICAgdmFyIGFmZmVjdGVkO1xuICAgICAgICAgICAgICAgICAgICBpZiAoJChpcSkuZmluZCgnPmppbmdsZT5yaW5naW5nW3htbG5zPVwidXJuOnhtcHA6amluZ2xlOmFwcHM6cnRwOmluZm86MVwiXScpLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcigncmluZ2luZy5qaW5nbGUnLCBbc2Vzcy5zaWRdKTtcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmICgkKGlxKS5maW5kKCc+amluZ2xlPm11dGVbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6aW5mbzoxXCJdJykubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBhZmZlY3RlZCA9ICQoaXEpLmZpbmQoJz5qaW5nbGU+bXV0ZVt4bWxucz1cInVybjp4bXBwOmppbmdsZTphcHBzOnJ0cDppbmZvOjFcIl0nKS5hdHRyKCduYW1lJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdtdXRlLmppbmdsZScsIFtzZXNzLnNpZCwgYWZmZWN0ZWRdKTtcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmICgkKGlxKS5maW5kKCc+amluZ2xlPnVubXV0ZVt4bWxucz1cInVybjp4bXBwOmppbmdsZTphcHBzOnJ0cDppbmZvOjFcIl0nKS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGFmZmVjdGVkID0gJChpcSkuZmluZCgnPmppbmdsZT51bm11dGVbeG1sbnM9XCJ1cm46eG1wcDpqaW5nbGU6YXBwczpydHA6aW5mbzoxXCJdJykuYXR0cignbmFtZScpO1xuICAgICAgICAgICAgICAgICAgICAgICAgJChkb2N1bWVudCkudHJpZ2dlcigndW5tdXRlLmppbmdsZScsIFtzZXNzLnNpZCwgYWZmZWN0ZWRdKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlICdhZGRzb3VyY2UnOiAvLyBGSVhNRTogcHJvcHJpZXRhcnksIHVuLWppbmdsZWlzaFxuICAgICAgICAgICAgICAgIGNhc2UgJ3NvdXJjZS1hZGQnOiAvLyBGSVhNRTogcHJvcHJpZXRhcnlcbiAgICAgICAgICAgICAgICAgICAgc2Vzcy5hZGRTb3VyY2UoJChpcSkuZmluZCgnPmppbmdsZT5jb250ZW50JyksIGZyb21KaWQpO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlICdyZW1vdmVzb3VyY2UnOiAvLyBGSVhNRTogcHJvcHJpZXRhcnksIHVuLWppbmdsZWlzaFxuICAgICAgICAgICAgICAgIGNhc2UgJ3NvdXJjZS1yZW1vdmUnOiAvLyBGSVhNRTogcHJvcHJpZXRhcnlcbiAgICAgICAgICAgICAgICAgICAgc2Vzcy5yZW1vdmVTb3VyY2UoJChpcSkuZmluZCgnPmppbmdsZT5jb250ZW50JyksIGZyb21KaWQpO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oJ2ppbmdsZSBhY3Rpb24gbm90IGltcGxlbWVudGVkJywgYWN0aW9uKTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfSxcbiAgICAgICAgaW5pdGlhdGU6IGZ1bmN0aW9uIChwZWVyamlkLCBteWppZCkgeyAvLyBpbml0aWF0ZSBhIG5ldyBqaW5nbGVzZXNzaW9uIHRvIHBlZXJqaWRcbiAgICAgICAgICAgIHZhciBzZXNzID0gbmV3IEppbmdsZVNlc3Npb24obXlqaWQgfHwgdGhpcy5jb25uZWN0aW9uLmppZCxcbiAgICAgICAgICAgICAgICBNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zdWJzdHIoMiwgMTIpLCAvLyByYW5kb20gc3RyaW5nXG4gICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLCBYTVBQKTtcbiAgICAgICAgICAgIC8vIGNvbmZpZ3VyZSBzZXNzaW9uXG5cbiAgICAgICAgICAgIHNlc3MubWVkaWFfY29uc3RyYWludHMgPSB0aGlzLm1lZGlhX2NvbnN0cmFpbnRzO1xuICAgICAgICAgICAgc2Vzcy5wY19jb25zdHJhaW50cyA9IHRoaXMucGNfY29uc3RyYWludHM7XG4gICAgICAgICAgICBzZXNzLmljZV9jb25maWcgPSB0aGlzLmljZV9jb25maWc7XG5cbiAgICAgICAgICAgIHNlc3MuaW5pdGlhdGUocGVlcmppZCwgdHJ1ZSk7XG4gICAgICAgICAgICB0aGlzLnNlc3Npb25zW3Nlc3Muc2lkXSA9IHNlc3M7XG4gICAgICAgICAgICB0aGlzLmppZDJzZXNzaW9uW3Nlc3MucGVlcmppZF0gPSBzZXNzO1xuICAgICAgICAgICAgc2Vzcy5zZW5kT2ZmZXIoKTtcbiAgICAgICAgICAgIHJldHVybiBzZXNzO1xuICAgICAgICB9LFxuICAgICAgICB0ZXJtaW5hdGU6IGZ1bmN0aW9uIChzaWQsIHJlYXNvbiwgdGV4dCkgeyAvLyB0ZXJtaW5hdGUgYnkgc2Vzc2lvbmlkIChvciBhbGwgc2Vzc2lvbnMpXG4gICAgICAgICAgICBpZiAoc2lkID09PSBudWxsIHx8IHNpZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICAgICAgZm9yIChzaWQgaW4gdGhpcy5zZXNzaW9ucykge1xuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5zZXNzaW9uc1tzaWRdLnN0YXRlICE9ICdlbmRlZCcpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuc2Vzc2lvbnNbc2lkXS5zZW5kVGVybWluYXRlKHJlYXNvbiB8fCAoIXRoaXMuc2Vzc2lvbnNbc2lkXS5hY3RpdmUoKSkgPyAnY2FuY2VsJyA6IG51bGwsIHRleHQpO1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5zZXNzaW9uc1tzaWRdLnRlcm1pbmF0ZSgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGRlbGV0ZSB0aGlzLmppZDJzZXNzaW9uW3RoaXMuc2Vzc2lvbnNbc2lkXS5wZWVyamlkXTtcbiAgICAgICAgICAgICAgICAgICAgZGVsZXRlIHRoaXMuc2Vzc2lvbnNbc2lkXTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHRoaXMuc2Vzc2lvbnMuaGFzT3duUHJvcGVydHkoc2lkKSkge1xuICAgICAgICAgICAgICAgIGlmICh0aGlzLnNlc3Npb25zW3NpZF0uc3RhdGUgIT0gJ2VuZGVkJykge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnNlc3Npb25zW3NpZF0uc2VuZFRlcm1pbmF0ZShyZWFzb24gfHwgKCF0aGlzLnNlc3Npb25zW3NpZF0uYWN0aXZlKCkpID8gJ2NhbmNlbCcgOiBudWxsLCB0ZXh0KTtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5zZXNzaW9uc1tzaWRdLnRlcm1pbmF0ZSgpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5qaWQyc2Vzc2lvblt0aGlzLnNlc3Npb25zW3NpZF0ucGVlcmppZF07XG4gICAgICAgICAgICAgICAgZGVsZXRlIHRoaXMuc2Vzc2lvbnNbc2lkXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICAgICAgLy8gVXNlZCB0byB0ZXJtaW5hdGUgYSBzZXNzaW9uIHdoZW4gYW4gdW5hdmFpbGFibGUgcHJlc2VuY2UgaXMgcmVjZWl2ZWQuXG4gICAgICAgIHRlcm1pbmF0ZUJ5SmlkOiBmdW5jdGlvbiAoamlkKSB7XG4gICAgICAgICAgICBpZiAodGhpcy5qaWQyc2Vzc2lvbi5oYXNPd25Qcm9wZXJ0eShqaWQpKSB7XG4gICAgICAgICAgICAgICAgdmFyIHNlc3MgPSB0aGlzLmppZDJzZXNzaW9uW2ppZF07XG4gICAgICAgICAgICAgICAgaWYgKHNlc3MpIHtcbiAgICAgICAgICAgICAgICAgICAgc2Vzcy50ZXJtaW5hdGUoKTtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3BlZXIgd2VudCBhd2F5IHNpbGVudGx5JywgamlkKTtcbiAgICAgICAgICAgICAgICAgICAgZGVsZXRlIHRoaXMuc2Vzc2lvbnNbc2Vzcy5zaWRdO1xuICAgICAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5qaWQyc2Vzc2lvbltqaWRdO1xuICAgICAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdjYWxsdGVybWluYXRlZC5qaW5nbGUnLFxuICAgICAgICAgICAgICAgICAgICAgICAgW3Nlc3Muc2lkLCBqaWRdLCAnZ29uZScpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICAgICAgdGVybWluYXRlUmVtb3RlQnlKaWQ6IGZ1bmN0aW9uIChqaWQsIHJlYXNvbikge1xuICAgICAgICAgICAgaWYgKHRoaXMuamlkMnNlc3Npb24uaGFzT3duUHJvcGVydHkoamlkKSkge1xuICAgICAgICAgICAgICAgIHZhciBzZXNzID0gdGhpcy5qaWQyc2Vzc2lvbltqaWRdO1xuICAgICAgICAgICAgICAgIGlmIChzZXNzKSB7XG4gICAgICAgICAgICAgICAgICAgIHNlc3Muc2VuZFRlcm1pbmF0ZShyZWFzb24gfHwgKCFzZXNzLmFjdGl2ZSgpKSA/ICdraWNrJyA6IG51bGwpO1xuICAgICAgICAgICAgICAgICAgICBzZXNzLnRlcm1pbmF0ZSgpO1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygndGVybWluYXRlIHBlZXIgd2l0aCBqaWQnLCBzZXNzLnNpZCwgamlkKTtcbiAgICAgICAgICAgICAgICAgICAgZGVsZXRlIHRoaXMuc2Vzc2lvbnNbc2Vzcy5zaWRdO1xuICAgICAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5qaWQyc2Vzc2lvbltqaWRdO1xuICAgICAgICAgICAgICAgICAgICAkKGRvY3VtZW50KS50cmlnZ2VyKCdjYWxsdGVybWluYXRlZC5qaW5nbGUnLFxuICAgICAgICAgICAgICAgICAgICAgICAgW3Nlc3Muc2lkLCBqaWQsICdraWNrZWQnXSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9LFxuICAgICAgICBnZXRTdHVuQW5kVHVybkNyZWRlbnRpYWxzOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAvLyBnZXQgc3R1biBhbmQgdHVybiBjb25maWd1cmF0aW9uIGZyb20gc2VydmVyIHZpYSB4ZXAtMDIxNVxuICAgICAgICAgICAgLy8gdXNlcyB0aW1lLWxpbWl0ZWQgY3JlZGVudGlhbHMgYXMgZGVzY3JpYmVkIGluXG4gICAgICAgICAgICAvLyBodHRwOi8vdG9vbHMuaWV0Zi5vcmcvaHRtbC9kcmFmdC11YmVydGktYmVoYXZlLXR1cm4tcmVzdC0wMFxuICAgICAgICAgICAgLy9cbiAgICAgICAgICAgIC8vIHNlZSBodHRwczovL2NvZGUuZ29vZ2xlLmNvbS9wL3Byb3NvZHktbW9kdWxlcy9zb3VyY2UvYnJvd3NlL21vZF90dXJuY3JlZGVudGlhbHMvbW9kX3R1cm5jcmVkZW50aWFscy5sdWFcbiAgICAgICAgICAgIC8vIGZvciBhIHByb3NvZHkgbW9kdWxlIHdoaWNoIGltcGxlbWVudHMgdGhpc1xuICAgICAgICAgICAgLy9cbiAgICAgICAgICAgIC8vIGN1cnJlbnRseSwgdGhpcyBkb2Vzbid0IHdvcmsgd2l0aCB1cGRhdGVJY2UgYW5kIHRoZXJlZm9yZSBjcmVkZW50aWFscyB3aXRoIGEgbG9uZ1xuICAgICAgICAgICAgLy8gdmFsaWRpdHkgaGF2ZSB0byBiZSBmZXRjaGVkIGJlZm9yZSBjcmVhdGluZyB0aGUgcGVlcmNvbm5lY3Rpb25cbiAgICAgICAgICAgIC8vIFRPRE86IGltcGxlbWVudCByZWZyZXNoIHZpYSB1cGRhdGVJY2UgYXMgZGVzY3JpYmVkIGluXG4gICAgICAgICAgICAvLyAgICAgIGh0dHBzOi8vY29kZS5nb29nbGUuY29tL3Avd2VicnRjL2lzc3Vlcy9kZXRhaWw/aWQ9MTY1MFxuICAgICAgICAgICAgdmFyIHNlbGYgPSB0aGlzO1xuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRJUShcbiAgICAgICAgICAgICAgICAkaXEoe3R5cGU6ICdnZXQnLCB0bzogdGhpcy5jb25uZWN0aW9uLmRvbWFpbn0pXG4gICAgICAgICAgICAgICAgICAgIC5jKCdzZXJ2aWNlcycsIHt4bWxuczogJ3Vybjp4bXBwOmV4dGRpc2NvOjEnfSkuYygnc2VydmljZScsIHtob3N0OiAndHVybi4nICsgdGhpcy5jb25uZWN0aW9uLmRvbWFpbn0pLFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChyZXMpIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIGljZXNlcnZlcnMgPSBbXTtcbiAgICAgICAgICAgICAgICAgICAgJChyZXMpLmZpbmQoJz5zZXJ2aWNlcz5zZXJ2aWNlJykuZWFjaChmdW5jdGlvbiAoaWR4LCBlbCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgZWwgPSAkKGVsKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBkaWN0ID0ge307XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgdHlwZSA9IGVsLmF0dHIoJ3R5cGUnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHN3aXRjaCAodHlwZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgJ3N0dW4nOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaWN0LnVybCA9ICdzdHVuOicgKyBlbC5hdHRyKCdob3N0Jyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChlbC5hdHRyKCdwb3J0JykpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpY3QudXJsICs9ICc6JyArIGVsLmF0dHIoJ3BvcnQnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpY2VzZXJ2ZXJzLnB1c2goZGljdCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgJ3R1cm4nOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgJ3R1cm5zJzpcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGljdC51cmwgPSB0eXBlICsgJzonO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoZWwuYXR0cigndXNlcm5hbWUnKSkgeyAvLyBodHRwczovL2NvZGUuZ29vZ2xlLmNvbS9wL3dlYnJ0Yy9pc3N1ZXMvZGV0YWlsP2lkPTE1MDhcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChuYXZpZ2F0b3IudXNlckFnZW50Lm1hdGNoKC9DaHJvbShlfGl1bSlcXC8oWzAtOV0rKVxcLi8pICYmIHBhcnNlSW50KG5hdmlnYXRvci51c2VyQWdlbnQubWF0Y2goL0Nocm9tKGV8aXVtKVxcLyhbMC05XSspXFwuLylbMl0sIDEwKSA8IDI4KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGljdC51cmwgKz0gZWwuYXR0cigndXNlcm5hbWUnKSArICdAJztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGljdC51c2VybmFtZSA9IGVsLmF0dHIoJ3VzZXJuYW1lJyk7IC8vIG9ubHkgd29ya3MgaW4gTTI4XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGljdC51cmwgKz0gZWwuYXR0cignaG9zdCcpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoZWwuYXR0cigncG9ydCcpICYmIGVsLmF0dHIoJ3BvcnQnKSAhPSAnMzQ3OCcpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpY3QudXJsICs9ICc6JyArIGVsLmF0dHIoJ3BvcnQnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoZWwuYXR0cigndHJhbnNwb3J0JykgJiYgZWwuYXR0cigndHJhbnNwb3J0JykgIT0gJ3VkcCcpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpY3QudXJsICs9ICc/dHJhbnNwb3J0PScgKyBlbC5hdHRyKCd0cmFuc3BvcnQnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoZWwuYXR0cigncGFzc3dvcmQnKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGljdC5jcmVkZW50aWFsID0gZWwuYXR0cigncGFzc3dvcmQnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpY2VzZXJ2ZXJzLnB1c2goZGljdCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgc2VsZi5pY2VfY29uZmlnLmljZVNlcnZlcnMgPSBpY2VzZXJ2ZXJzO1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKGVycikge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oJ2dldHRpbmcgdHVybiBjcmVkZW50aWFscyBmYWlsZWQnLCBlcnIpO1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oJ2lzIG1vZF90dXJuY3JlZGVudGlhbHMgb3Igc2ltaWxhciBpbnN0YWxsZWQ/Jyk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIC8vIGltcGxlbWVudCBwdXNoP1xuICAgICAgICB9LFxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBQb3B1bGF0ZXMgdGhlIGxvZyBkYXRhXG4gICAgICAgICAqL1xuICAgICAgICBwb3B1bGF0ZURhdGE6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHZhciBkYXRhID0ge307XG4gICAgICAgICAgICBPYmplY3Qua2V5cyh0aGlzLnNlc3Npb25zKS5mb3JFYWNoKGZ1bmN0aW9uIChzaWQpIHtcbiAgICAgICAgICAgICAgICB2YXIgc2Vzc2lvbiA9IHRoaXMuc2Vzc2lvbnNbc2lkXTtcbiAgICAgICAgICAgICAgICBpZiAoc2Vzc2lvbi5wZWVyY29ubmVjdGlvbiAmJiBzZXNzaW9uLnBlZXJjb25uZWN0aW9uLnVwZGF0ZUxvZykge1xuICAgICAgICAgICAgICAgICAgICAvLyBGSVhNRTogc2hvdWxkIHByb2JhYmx5IGJlIGEgLmR1bXAgY2FsbFxuICAgICAgICAgICAgICAgICAgICBkYXRhW1wiamluZ2xlX1wiICsgc2Vzc2lvbi5zaWRdID0ge1xuICAgICAgICAgICAgICAgICAgICAgICAgdXBkYXRlTG9nOiBzZXNzaW9uLnBlZXJjb25uZWN0aW9uLnVwZGF0ZUxvZyxcbiAgICAgICAgICAgICAgICAgICAgICAgIHN0YXRzOiBzZXNzaW9uLnBlZXJjb25uZWN0aW9uLnN0YXRzLFxuICAgICAgICAgICAgICAgICAgICAgICAgdXJsOiB3aW5kb3cubG9jYXRpb24uaHJlZlxuICAgICAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgcmV0dXJuIGRhdGE7XG4gICAgICAgIH1cbiAgICB9KTtcbn07XG5cbiIsIi8qIGdsb2JhbCBTdHJvcGhlICovXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uICgpIHtcblxuICAgIFN0cm9waGUuYWRkQ29ubmVjdGlvblBsdWdpbignbG9nZ2VyJywge1xuICAgICAgICAvLyBsb2dzIHJhdyBzdGFuemFzIGFuZCBtYWtlcyB0aGVtIGF2YWlsYWJsZSBmb3IgZG93bmxvYWQgYXMgSlNPTlxuICAgICAgICBjb25uZWN0aW9uOiBudWxsLFxuICAgICAgICBsb2c6IFtdLFxuICAgICAgICBpbml0OiBmdW5jdGlvbiAoY29ubikge1xuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uID0gY29ubjtcbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5yYXdJbnB1dCA9IHRoaXMubG9nX2luY29taW5nLmJpbmQodGhpcyk7XG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24ucmF3T3V0cHV0ID0gdGhpcy5sb2dfb3V0Z29pbmcuYmluZCh0aGlzKTtcbiAgICAgICAgfSxcbiAgICAgICAgbG9nX2luY29taW5nOiBmdW5jdGlvbiAoc3RhbnphKSB7XG4gICAgICAgICAgICB0aGlzLmxvZy5wdXNoKFtuZXcgRGF0ZSgpLmdldFRpbWUoKSwgJ2luY29taW5nJywgc3RhbnphXSk7XG4gICAgICAgIH0sXG4gICAgICAgIGxvZ19vdXRnb2luZzogZnVuY3Rpb24gKHN0YW56YSkge1xuICAgICAgICAgICAgdGhpcy5sb2cucHVzaChbbmV3IERhdGUoKS5nZXRUaW1lKCksICdvdXRnb2luZycsIHN0YW56YV0pO1xuICAgICAgICB9XG4gICAgfSk7XG59OyIsIi8qIGdsb2JhbCAkLCAkaXEsIGNvbmZpZywgY29ubmVjdGlvbiwgZm9jdXNNdWNKaWQsIGZvcmNlTXV0ZWQsXG4gICBzZXRBdWRpb011dGVkLCBTdHJvcGhlICovXG4vKipcbiAqIE1vZGVyYXRlIGNvbm5lY3Rpb24gcGx1Z2luLlxuICovXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChYTVBQKSB7XG4gICAgU3Ryb3BoZS5hZGRDb25uZWN0aW9uUGx1Z2luKCdtb2RlcmF0ZScsIHtcbiAgICAgICAgY29ubmVjdGlvbjogbnVsbCxcbiAgICAgICAgaW5pdDogZnVuY3Rpb24gKGNvbm4pIHtcbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbiA9IGNvbm47XG5cbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5hZGRIYW5kbGVyKHRoaXMub25NdXRlLmJpbmQodGhpcyksXG4gICAgICAgICAgICAgICAgJ2h0dHA6Ly9qaXRzaS5vcmcvaml0bWVldC9hdWRpbycsXG4gICAgICAgICAgICAgICAgJ2lxJyxcbiAgICAgICAgICAgICAgICAnc2V0JyxcbiAgICAgICAgICAgICAgICBudWxsLFxuICAgICAgICAgICAgICAgIG51bGwpO1xuICAgICAgICB9LFxuICAgICAgICBzZXRNdXRlOiBmdW5jdGlvbiAoamlkLCBtdXRlKSB7XG4gICAgICAgICAgICBjb25zb2xlLmluZm8oXCJzZXQgbXV0ZVwiLCBtdXRlKTtcbiAgICAgICAgICAgIHZhciBpcVRvRm9jdXMgPSAkaXEoe3RvOiBmb2N1c011Y0ppZCwgdHlwZTogJ3NldCd9KVxuICAgICAgICAgICAgICAgIC5jKCdtdXRlJywge1xuICAgICAgICAgICAgICAgICAgICB4bWxuczogJ2h0dHA6Ly9qaXRzaS5vcmcvaml0bWVldC9hdWRpbycsXG4gICAgICAgICAgICAgICAgICAgIGppZDogamlkXG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAudChtdXRlLnRvU3RyaW5nKCkpXG4gICAgICAgICAgICAgICAgLnVwKCk7XG5cbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kSVEoXG4gICAgICAgICAgICAgICAgaXFUb0ZvY3VzLFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChyZXN1bHQpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3NldCBtdXRlJywgcmVzdWx0KTtcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnc2V0IG11dGUgZXJyb3InLCBlcnJvcik7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgIH0sXG4gICAgICAgIG9uTXV0ZTogZnVuY3Rpb24gKGlxKSB7XG4gICAgICAgICAgICB2YXIgZnJvbSA9IGlxLmdldEF0dHJpYnV0ZSgnZnJvbScpO1xuICAgICAgICAgICAgaWYgKGZyb20gIT09IGZvY3VzTXVjSmlkKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS53YXJuKFwiSWdub3JlZCBtdXRlIGZyb20gbm9uIGZvY3VzIHBlZXJcIik7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFyIG11dGUgPSAkKGlxKS5maW5kKCdtdXRlJyk7XG4gICAgICAgICAgICBpZiAobXV0ZS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICB2YXIgZG9NdXRlQXVkaW8gPSBtdXRlLnRleHQoKSA9PT0gXCJ0cnVlXCI7XG4gICAgICAgICAgICAgICAgVUkuc2V0QXVkaW9NdXRlZChkb011dGVBdWRpbyk7XG4gICAgICAgICAgICAgICAgWE1QUC5mb3JjZU11dGVkID0gZG9NdXRlQXVkaW87XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfSxcbiAgICAgICAgZWplY3Q6IGZ1bmN0aW9uIChqaWQpIHtcbiAgICAgICAgICAgIC8vIFdlJ3JlIG5vdCB0aGUgZm9jdXMsIHNvIGNhbid0IHRlcm1pbmF0ZVxuICAgICAgICAgICAgLy9jb25uZWN0aW9uLmppbmdsZS50ZXJtaW5hdGVSZW1vdGVCeUppZChqaWQsICdraWNrJyk7XG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uZW11Yy5raWNrKGppZCk7XG4gICAgICAgIH1cbiAgICB9KTtcbn0iLCIvKiBqc2hpbnQgLVcxMTcgKi9cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24oKSB7XG4gICAgU3Ryb3BoZS5hZGRDb25uZWN0aW9uUGx1Z2luKCdyYXlvJyxcbiAgICAgICAge1xuICAgICAgICAgICAgUkFZT19YTUxOUzogJ3Vybjp4bXBwOnJheW86MScsXG4gICAgICAgICAgICBjb25uZWN0aW9uOiBudWxsLFxuICAgICAgICAgICAgaW5pdDogZnVuY3Rpb24gKGNvbm4pIHtcbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24gPSBjb25uO1xuICAgICAgICAgICAgICAgIGlmICh0aGlzLmNvbm5lY3Rpb24uZGlzY28pIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmRpc2NvLmFkZEZlYXR1cmUoJ3Vybjp4bXBwOnJheW86Y2xpZW50OjEnKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uYWRkSGFuZGxlcihcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5vblJheW8uYmluZCh0aGlzKSwgdGhpcy5SQVlPX1hNTE5TLCAnaXEnLCAnc2V0JywgbnVsbCwgbnVsbCk7XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgb25SYXlvOiBmdW5jdGlvbiAoaXEpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8oXCJSYXlvIElRXCIsIGlxKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBkaWFsOiBmdW5jdGlvbiAodG8sIGZyb20sIHJvb21OYW1lLCByb29tUGFzcykge1xuICAgICAgICAgICAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgICAgICAgICAgICB2YXIgcmVxID0gJGlxKFxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc2V0JyxcbiAgICAgICAgICAgICAgICAgICAgICAgIHRvOiBmb2N1c011Y0ppZFxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICByZXEuYygnZGlhbCcsXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHhtbG5zOiB0aGlzLlJBWU9fWE1MTlMsXG4gICAgICAgICAgICAgICAgICAgICAgICB0bzogdG8sXG4gICAgICAgICAgICAgICAgICAgICAgICBmcm9tOiBmcm9tXG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIHJlcS5jKCdoZWFkZXInLFxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICBuYW1lOiAnSnZiUm9vbU5hbWUnLFxuICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWU6IHJvb21OYW1lXG4gICAgICAgICAgICAgICAgICAgIH0pLnVwKCk7XG5cbiAgICAgICAgICAgICAgICBpZiAocm9vbVBhc3MgIT09IG51bGwgJiYgcm9vbVBhc3MubGVuZ3RoKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgcmVxLmMoJ2hlYWRlcicsXG4gICAgICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZTogJ0p2YlJvb21QYXNzd29yZCcsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWU6IHJvb21QYXNzXG4gICAgICAgICAgICAgICAgICAgICAgICB9KS51cCgpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kSVEoXG4gICAgICAgICAgICAgICAgICAgIHJlcSxcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKHJlc3VsdCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKCdEaWFsIHJlc3VsdCAnLCByZXN1bHQpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgcmVzb3VyY2UgPSAkKHJlc3VsdCkuZmluZCgncmVmJykuYXR0cigndXJpJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmNhbGxfcmVzb3VyY2UgPSByZXNvdXJjZS5zdWJzdHIoJ3htcHA6Jy5sZW5ndGgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcIlJlY2VpdmVkIGNhbGwgcmVzb3VyY2U6IFwiICsgdGhpcy5jYWxsX3Jlc291cmNlKTtcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8oJ0RpYWwgZXJyb3IgJywgZXJyb3IpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBoYW5nX3VwOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgaWYgKCF0aGlzLmNhbGxfcmVzb3VyY2UpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKFwiTm8gY2FsbCBpbiBwcm9ncmVzc1wiKTtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgICAgICAgICAgICB2YXIgcmVxID0gJGlxKFxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc2V0JyxcbiAgICAgICAgICAgICAgICAgICAgICAgIHRvOiB0aGlzLmNhbGxfcmVzb3VyY2VcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgcmVxLmMoJ2hhbmd1cCcsXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHhtbG5zOiB0aGlzLlJBWU9fWE1MTlNcbiAgICAgICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZElRKFxuICAgICAgICAgICAgICAgICAgICByZXEsXG4gICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChyZXN1bHQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuaW5mbygnSGFuZ3VwIHJlc3VsdCAnLCByZXN1bHQpO1xuICAgICAgICAgICAgICAgICAgICAgICAgc2VsZi5jYWxsX3Jlc291cmNlID0gbnVsbDtcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8oJ0hhbmd1cCBlcnJvciAnLCBlcnJvcik7XG4gICAgICAgICAgICAgICAgICAgICAgICBzZWxmLmNhbGxfcmVzb3VyY2UgPSBudWxsO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICk7XG59O1xuIiwiLyoqXG4gKiBTdHJvcGhlIGxvZ2dlciBpbXBsZW1lbnRhdGlvbi4gTG9ncyBmcm9tIGxldmVsIFdBUk4gYW5kIGFib3ZlLlxuICovXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uICgpIHtcblxuICAgIFN0cm9waGUubG9nID0gZnVuY3Rpb24gKGxldmVsLCBtc2cpIHtcbiAgICAgICAgc3dpdGNoIChsZXZlbCkge1xuICAgICAgICAgICAgY2FzZSBTdHJvcGhlLkxvZ0xldmVsLldBUk46XG4gICAgICAgICAgICAgICAgY29uc29sZS53YXJuKFwiU3Ryb3BoZTogXCIgKyBtc2cpO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSBTdHJvcGhlLkxvZ0xldmVsLkVSUk9SOlxuICAgICAgICAgICAgY2FzZSBTdHJvcGhlLkxvZ0xldmVsLkZBVEFMOlxuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJTdHJvcGhlOiBcIiArIG1zZyk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgU3Ryb3BoZS5nZXRTdGF0dXNTdHJpbmcgPSBmdW5jdGlvbiAoc3RhdHVzKSB7XG4gICAgICAgIHN3aXRjaCAoc3RhdHVzKSB7XG4gICAgICAgICAgICBjYXNlIFN0cm9waGUuU3RhdHVzLkVSUk9SOlxuICAgICAgICAgICAgICAgIHJldHVybiBcIkVSUk9SXCI7XG4gICAgICAgICAgICBjYXNlIFN0cm9waGUuU3RhdHVzLkNPTk5FQ1RJTkc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIFwiQ09OTkVDVElOR1wiO1xuICAgICAgICAgICAgY2FzZSBTdHJvcGhlLlN0YXR1cy5DT05ORkFJTDpcbiAgICAgICAgICAgICAgICByZXR1cm4gXCJDT05ORkFJTFwiO1xuICAgICAgICAgICAgY2FzZSBTdHJvcGhlLlN0YXR1cy5BVVRIRU5USUNBVElORzpcbiAgICAgICAgICAgICAgICByZXR1cm4gXCJBVVRIRU5USUNBVElOR1wiO1xuICAgICAgICAgICAgY2FzZSBTdHJvcGhlLlN0YXR1cy5BVVRIRkFJTDpcbiAgICAgICAgICAgICAgICByZXR1cm4gXCJBVVRIRkFJTFwiO1xuICAgICAgICAgICAgY2FzZSBTdHJvcGhlLlN0YXR1cy5DT05ORUNURUQ6XG4gICAgICAgICAgICAgICAgcmV0dXJuIFwiQ09OTkVDVEVEXCI7XG4gICAgICAgICAgICBjYXNlIFN0cm9waGUuU3RhdHVzLkRJU0NPTk5FQ1RFRDpcbiAgICAgICAgICAgICAgICByZXR1cm4gXCJESVNDT05ORUNURURcIjtcbiAgICAgICAgICAgIGNhc2UgU3Ryb3BoZS5TdGF0dXMuRElTQ09OTkVDVElORzpcbiAgICAgICAgICAgICAgICByZXR1cm4gXCJESVNDT05ORUNUSU5HXCI7XG4gICAgICAgICAgICBjYXNlIFN0cm9waGUuU3RhdHVzLkFUVEFDSEVEOlxuICAgICAgICAgICAgICAgIHJldHVybiBcIkFUVEFDSEVEXCI7XG4gICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICAgIHJldHVybiBcInVua25vd25cIjtcbiAgICAgICAgfVxuICAgIH07XG59O1xuIiwidmFyIE1vZGVyYXRvciA9IHJlcXVpcmUoXCIuL21vZGVyYXRvclwiKTtcbnZhciBFdmVudEVtaXR0ZXIgPSByZXF1aXJlKFwiZXZlbnRzXCIpO1xudmFyIFJlY29yZGluZyA9IHJlcXVpcmUoXCIuL3JlY29yZGluZ1wiKTtcbnZhciBTRFAgPSByZXF1aXJlKFwiLi9TRFBcIik7XG5cbnZhciBldmVudEVtaXR0ZXIgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG52YXIgY29ubmVjdGlvbiA9IG51bGw7XG52YXIgYXV0aGVudGljYXRlZFVzZXIgPSBmYWxzZTtcbnZhciBhY3RpdmVjYWxsID0gbnVsbDtcblxuZnVuY3Rpb24gY29ubmVjdChqaWQsIHBhc3N3b3JkLCB1aUNyZWRlbnRpYWxzKSB7XG4gICAgdmFyIGJvc2hcbiAgICAgICAgPSB1aUNyZWRlbnRpYWxzLmJvc2ggfHwgY29uZmlnLmJvc2ggfHwgJy9odHRwLWJpbmQnO1xuICAgIGNvbm5lY3Rpb24gPSBuZXcgU3Ryb3BoZS5Db25uZWN0aW9uKGJvc2gpO1xuICAgIE1vZGVyYXRvci5zZXRDb25uZWN0aW9uKGNvbm5lY3Rpb24pO1xuXG4gICAgdmFyIHNldHRpbmdzID0gVUkuZ2V0U2V0dGluZ3MoKTtcbiAgICB2YXIgZW1haWwgPSBzZXR0aW5ncy5lbWFpbDtcbiAgICB2YXIgZGlzcGxheU5hbWUgPSBzZXR0aW5ncy5kaXNwbGF5TmFtZTtcbiAgICBpZihlbWFpbCkge1xuICAgICAgICBjb25uZWN0aW9uLmVtdWMuYWRkRW1haWxUb1ByZXNlbmNlKGVtYWlsKTtcbiAgICB9IGVsc2Uge1xuICAgICAgICBjb25uZWN0aW9uLmVtdWMuYWRkVXNlcklkVG9QcmVzZW5jZShzZXR0aW5ncy51aWQpO1xuICAgIH1cbiAgICBpZihkaXNwbGF5TmFtZSkge1xuICAgICAgICBjb25uZWN0aW9uLmVtdWMuYWRkRGlzcGxheU5hbWVUb1ByZXNlbmNlKGRpc3BsYXlOYW1lKTtcbiAgICB9XG5cbiAgICBpZiAoY29ubmVjdGlvbi5kaXNjbykge1xuICAgICAgICAvLyBmb3IgY2hyb21lLCBhZGQgbXVsdGlzdHJlYW0gY2FwXG4gICAgfVxuICAgIGNvbm5lY3Rpb24uamluZ2xlLnBjX2NvbnN0cmFpbnRzID0gUlRDLmdldFBDQ29uc3RyYWludHMoKTtcbiAgICBpZiAoY29uZmlnLnVzZUlQdjYpIHtcbiAgICAgICAgLy8gaHR0cHM6Ly9jb2RlLmdvb2dsZS5jb20vcC93ZWJydGMvaXNzdWVzL2RldGFpbD9pZD0yODI4XG4gICAgICAgIGlmICghY29ubmVjdGlvbi5qaW5nbGUucGNfY29uc3RyYWludHMub3B0aW9uYWwpXG4gICAgICAgICAgICBjb25uZWN0aW9uLmppbmdsZS5wY19jb25zdHJhaW50cy5vcHRpb25hbCA9IFtdO1xuICAgICAgICBjb25uZWN0aW9uLmppbmdsZS5wY19jb25zdHJhaW50cy5vcHRpb25hbC5wdXNoKHtnb29nSVB2NjogdHJ1ZX0pO1xuICAgIH1cblxuICAgIGlmKCFwYXNzd29yZClcbiAgICAgICAgcGFzc3dvcmQgPSB1aUNyZWRlbnRpYWxzLnBhc3N3b3JkO1xuXG4gICAgdmFyIGFub255bW91c0Nvbm5lY3Rpb25GYWlsZWQgPSBmYWxzZTtcbiAgICBjb25uZWN0aW9uLmNvbm5lY3QoamlkLCBwYXNzd29yZCwgZnVuY3Rpb24gKHN0YXR1cywgbXNnKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKCdTdHJvcGhlIHN0YXR1cyBjaGFuZ2VkIHRvJyxcbiAgICAgICAgICAgIFN0cm9waGUuZ2V0U3RhdHVzU3RyaW5nKHN0YXR1cykpO1xuICAgICAgICBpZiAoc3RhdHVzID09PSBTdHJvcGhlLlN0YXR1cy5DT05ORUNURUQpIHtcbiAgICAgICAgICAgIGlmIChjb25maWcudXNlU3R1blR1cm4pIHtcbiAgICAgICAgICAgICAgICBjb25uZWN0aW9uLmppbmdsZS5nZXRTdHVuQW5kVHVybkNyZWRlbnRpYWxzKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBVSS5kaXNhYmxlQ29ubmVjdCgpO1xuXG4gICAgICAgICAgICBjb25zb2xlLmluZm8oXCJNeSBKYWJiZXIgSUQ6IFwiICsgY29ubmVjdGlvbi5qaWQpO1xuXG4gICAgICAgICAgICBpZihwYXNzd29yZClcbiAgICAgICAgICAgICAgICBhdXRoZW50aWNhdGVkVXNlciA9IHRydWU7XG4gICAgICAgICAgICBtYXliZURvSm9pbigpO1xuICAgICAgICB9IGVsc2UgaWYgKHN0YXR1cyA9PT0gU3Ryb3BoZS5TdGF0dXMuQ09OTkZBSUwpIHtcbiAgICAgICAgICAgIGlmKG1zZyA9PT0gJ3gtc3Ryb3BoZS1iYWQtbm9uLWFub24tamlkJykge1xuICAgICAgICAgICAgICAgIGFub255bW91c0Nvbm5lY3Rpb25GYWlsZWQgPSB0cnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKHN0YXR1cyA9PT0gU3Ryb3BoZS5TdGF0dXMuRElTQ09OTkVDVEVEKSB7XG4gICAgICAgICAgICBpZihhbm9ueW1vdXNDb25uZWN0aW9uRmFpbGVkKSB7XG4gICAgICAgICAgICAgICAgLy8gcHJvbXB0IHVzZXIgZm9yIHVzZXJuYW1lIGFuZCBwYXNzd29yZFxuICAgICAgICAgICAgICAgIFhNUFAucHJvbXB0TG9naW4oKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmIChzdGF0dXMgPT09IFN0cm9waGUuU3RhdHVzLkFVVEhGQUlMKSB7XG4gICAgICAgICAgICAvLyB3cm9uZyBwYXNzd29yZCBvciB1c2VybmFtZSwgcHJvbXB0IHVzZXJcbiAgICAgICAgICAgIFhNUFAucHJvbXB0TG9naW4oKTtcblxuICAgICAgICB9XG4gICAgfSk7XG59XG5cblxuXG5mdW5jdGlvbiBtYXliZURvSm9pbigpIHtcbiAgICBpZiAoY29ubmVjdGlvbiAmJiBjb25uZWN0aW9uLmNvbm5lY3RlZCAmJlxuICAgICAgICBTdHJvcGhlLmdldFJlc291cmNlRnJvbUppZChjb25uZWN0aW9uLmppZClcbiAgICAgICAgJiYgKFJUQy5sb2NhbEF1ZGlvIHx8IFJUQy5sb2NhbFZpZGVvKSkge1xuICAgICAgICAvLyAuY29ubmVjdGVkIGlzIHRydWUgd2hpbGUgY29ubmVjdGluZz9cbiAgICAgICAgZG9Kb2luKCk7XG4gICAgfVxufVxuXG5mdW5jdGlvbiBkb0pvaW4oKSB7XG4gICAgdmFyIHJvb21OYW1lID0gVUkuZ2VuZXJhdGVSb29tTmFtZSgpO1xuXG4gICAgTW9kZXJhdG9yLmFsbG9jYXRlQ29uZmVyZW5jZUZvY3VzKFxuICAgICAgICByb29tTmFtZSwgVUkuY2hlY2tGb3JOaWNrbmFtZUFuZEpvaW4pO1xufVxuXG5mdW5jdGlvbiBpbml0U3Ryb3BoZVBsdWdpbnMoKVxue1xuICAgIHJlcXVpcmUoXCIuL3N0cm9waGUuZW11Y1wiKShYTVBQLCBldmVudEVtaXR0ZXIpO1xuICAgIHJlcXVpcmUoXCIuL3N0cm9waGUuamluZ2xlXCIpKCk7XG4gICAgcmVxdWlyZShcIi4vc3Ryb3BoZS5tb2RlcmF0ZVwiKShYTVBQKTtcbiAgICByZXF1aXJlKFwiLi9zdHJvcGhlLnV0aWxcIikoKTtcbiAgICByZXF1aXJlKFwiLi9zdHJvcGhlLnJheW9cIikoKTtcbiAgICByZXF1aXJlKFwiLi9zdHJvcGhlLmxvZ2dlclwiKSgpO1xufVxuXG5mdW5jdGlvbiByZWdpc3Rlckxpc3RlbmVycygpIHtcbiAgICBSVEMuYWRkU3RyZWFtTGlzdGVuZXIobWF5YmVEb0pvaW4sXG4gICAgICAgIFN0cmVhbUV2ZW50VHlwZXMuRVZFTlRfVFlQRV9MT0NBTF9DUkVBVEVEKTtcbn1cblxuZnVuY3Rpb24gc2V0dXBFdmVudHMoKSB7XG4gICAgJCh3aW5kb3cpLmJpbmQoJ2JlZm9yZXVubG9hZCcsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgaWYgKGNvbm5lY3Rpb24gJiYgY29ubmVjdGlvbi5jb25uZWN0ZWQpIHtcbiAgICAgICAgICAgIC8vIGVuc3VyZSBzaWdub3V0XG4gICAgICAgICAgICAkLmFqYXgoe1xuICAgICAgICAgICAgICAgIHR5cGU6ICdQT1NUJyxcbiAgICAgICAgICAgICAgICB1cmw6IGNvbmZpZy5ib3NoLFxuICAgICAgICAgICAgICAgIGFzeW5jOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBjYWNoZTogZmFsc2UsXG4gICAgICAgICAgICAgICAgY29udGVudFR5cGU6ICdhcHBsaWNhdGlvbi94bWwnLFxuICAgICAgICAgICAgICAgIGRhdGE6IFwiPGJvZHkgcmlkPSdcIiArIChjb25uZWN0aW9uLnJpZCB8fCBjb25uZWN0aW9uLl9wcm90by5yaWQpXG4gICAgICAgICAgICAgICAgICAgICsgXCInIHhtbG5zPSdodHRwOi8vamFiYmVyLm9yZy9wcm90b2NvbC9odHRwYmluZCcgc2lkPSdcIlxuICAgICAgICAgICAgICAgICAgICArIChjb25uZWN0aW9uLnNpZCB8fCBjb25uZWN0aW9uLl9wcm90by5zaWQpXG4gICAgICAgICAgICAgICAgICAgICsgXCInIHR5cGU9J3Rlcm1pbmF0ZSc+XCIgK1xuICAgICAgICAgICAgICAgICAgICBcIjxwcmVzZW5jZSB4bWxucz0namFiYmVyOmNsaWVudCcgdHlwZT0ndW5hdmFpbGFibGUnLz5cIiArXG4gICAgICAgICAgICAgICAgICAgIFwiPC9ib2R5PlwiLFxuICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IGZ1bmN0aW9uIChkYXRhKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdzaWduZWQgb3V0Jyk7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGRhdGEpO1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgZXJyb3I6IGZ1bmN0aW9uIChYTUxIdHRwUmVxdWVzdCwgdGV4dFN0YXR1cywgZXJyb3JUaHJvd24pIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3NpZ25vdXQgZXJyb3InLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRleHRTdGF0dXMgKyAnICgnICsgZXJyb3JUaHJvd24gKyAnKScpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICAgIFhNUFAuZGlzcG9zZUNvbmZlcmVuY2UodHJ1ZSk7XG4gICAgfSk7XG59XG5cbnZhciBYTVBQID0ge1xuICAgIHNlc3Npb25UZXJtaW5hdGVkOiBmYWxzZSxcbiAgICAvKipcbiAgICAgKiBSZW1lbWJlcnMgaWYgd2Ugd2VyZSBtdXRlZCBieSB0aGUgZm9jdXMuXG4gICAgICogQHR5cGUge2Jvb2xlYW59XG4gICAgICovXG4gICAgZm9yY2VNdXRlZDogZmFsc2UsXG4gICAgc3RhcnQ6IGZ1bmN0aW9uICh1aUNyZWRlbnRpYWxzKSB7XG4gICAgICAgIHNldHVwRXZlbnRzKCk7XG4gICAgICAgIGluaXRTdHJvcGhlUGx1Z2lucygpO1xuICAgICAgICByZWdpc3Rlckxpc3RlbmVycygpO1xuICAgICAgICBNb2RlcmF0b3IuaW5pdCgpO1xuICAgICAgICB2YXIgamlkID0gdWlDcmVkZW50aWFscy5qaWQgfHxcbiAgICAgICAgICAgIGNvbmZpZy5ob3N0cy5hbm9ueW1vdXNkb21haW4gfHxcbiAgICAgICAgICAgIGNvbmZpZy5ob3N0cy5kb21haW4gfHxcbiAgICAgICAgICAgIHdpbmRvdy5sb2NhdGlvbi5ob3N0bmFtZTtcbiAgICAgICAgY29ubmVjdChqaWQsIG51bGwsIHVpQ3JlZGVudGlhbHMpO1xuICAgIH0sXG4gICAgcHJvbXB0TG9naW46IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgVUkuc2hvd0xvZ2luUG9wdXAoY29ubmVjdCk7XG4gICAgfSxcbiAgICBqb2luUm9vb206IGZ1bmN0aW9uKHJvb21OYW1lLCB1c2VOaWNrcywgbmljaylcbiAgICB7XG4gICAgICAgIHZhciByb29tamlkO1xuICAgICAgICByb29tamlkID0gcm9vbU5hbWU7XG5cbiAgICAgICAgaWYgKHVzZU5pY2tzKSB7XG4gICAgICAgICAgICBpZiAobmljaykge1xuICAgICAgICAgICAgICAgIHJvb21qaWQgKz0gJy8nICsgbmljaztcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgcm9vbWppZCArPSAnLycgKyBTdHJvcGhlLmdldE5vZGVGcm9tSmlkKGNvbm5lY3Rpb24uamlkKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcblxuICAgICAgICAgICAgdmFyIHRtcEppZCA9IFN0cm9waGUuZ2V0Tm9kZUZyb21KaWQoY29ubmVjdGlvbi5qaWQpO1xuXG4gICAgICAgICAgICBpZighYXV0aGVudGljYXRlZFVzZXIpXG4gICAgICAgICAgICAgICAgdG1wSmlkID0gdG1wSmlkLnN1YnN0cigwLCA4KTtcblxuICAgICAgICAgICAgcm9vbWppZCArPSAnLycgKyB0bXBKaWQ7XG4gICAgICAgIH1cbiAgICAgICAgY29ubmVjdGlvbi5lbXVjLmRvSm9pbihyb29tamlkKTtcbiAgICB9LFxuICAgIG15SmlkOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGlmKCFjb25uZWN0aW9uKVxuICAgICAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICAgIHJldHVybiBjb25uZWN0aW9uLmVtdWMubXlyb29tamlkO1xuICAgIH0sXG4gICAgbXlSZXNvdXJjZTogZnVuY3Rpb24gKCkge1xuICAgICAgICBpZighY29ubmVjdGlvbiB8fCAhIGNvbm5lY3Rpb24uZW11Yy5teXJvb21qaWQpXG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgcmV0dXJuIFN0cm9waGUuZ2V0UmVzb3VyY2VGcm9tSmlkKGNvbm5lY3Rpb24uZW11Yy5teXJvb21qaWQpO1xuICAgIH0sXG4gICAgZGlzcG9zZUNvbmZlcmVuY2U6IGZ1bmN0aW9uIChvblVubG9hZCkge1xuICAgICAgICBldmVudEVtaXR0ZXIuZW1pdChYTVBQRXZlbnRzLkRJU1BPU0VfQ09ORkVSRU5DRSwgb25VbmxvYWQpO1xuICAgICAgICB2YXIgaGFuZGxlciA9IGFjdGl2ZWNhbGw7XG4gICAgICAgIGlmIChoYW5kbGVyICYmIGhhbmRsZXIucGVlcmNvbm5lY3Rpb24pIHtcbiAgICAgICAgICAgIC8vIEZJWE1FOiBwcm9iYWJseSByZW1vdmluZyBzdHJlYW1zIGlzIG5vdCByZXF1aXJlZCBhbmQgY2xvc2UoKSBzaG91bGRcbiAgICAgICAgICAgIC8vIGJlIGVub3VnaFxuICAgICAgICAgICAgaWYgKFJUQy5sb2NhbEF1ZGlvKSB7XG4gICAgICAgICAgICAgICAgaGFuZGxlci5wZWVyY29ubmVjdGlvbi5yZW1vdmVTdHJlYW0oUlRDLmxvY2FsQXVkaW8uZ2V0T3JpZ2luYWxTdHJlYW0oKSwgb25VbmxvYWQpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKFJUQy5sb2NhbFZpZGVvKSB7XG4gICAgICAgICAgICAgICAgaGFuZGxlci5wZWVyY29ubmVjdGlvbi5yZW1vdmVTdHJlYW0oUlRDLmxvY2FsVmlkZW8uZ2V0T3JpZ2luYWxTdHJlYW0oKSwgb25VbmxvYWQpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaGFuZGxlci5wZWVyY29ubmVjdGlvbi5jbG9zZSgpO1xuICAgICAgICB9XG4gICAgICAgIGFjdGl2ZWNhbGwgPSBudWxsO1xuICAgICAgICBpZighb25VbmxvYWQpXG4gICAgICAgIHtcbiAgICAgICAgICAgIHRoaXMuc2Vzc2lvblRlcm1pbmF0ZWQgPSB0cnVlO1xuICAgICAgICAgICAgY29ubmVjdGlvbi5lbXVjLmRvTGVhdmUoKTtcbiAgICAgICAgfVxuICAgIH0sXG4gICAgYWRkTGlzdGVuZXI6IGZ1bmN0aW9uKHR5cGUsIGxpc3RlbmVyKVxuICAgIHtcbiAgICAgICAgZXZlbnRFbWl0dGVyLm9uKHR5cGUsIGxpc3RlbmVyKTtcbiAgICB9LFxuICAgIHJlbW92ZUxpc3RlbmVyOiBmdW5jdGlvbiAodHlwZSwgbGlzdGVuZXIpIHtcbiAgICAgICAgZXZlbnRFbWl0dGVyLnJlbW92ZUxpc3RlbmVyKHR5cGUsIGxpc3RlbmVyKTtcbiAgICB9LFxuICAgIGFsbG9jYXRlQ29uZmVyZW5jZUZvY3VzOiBmdW5jdGlvbihyb29tTmFtZSwgY2FsbGJhY2spIHtcbiAgICAgICAgTW9kZXJhdG9yLmFsbG9jYXRlQ29uZmVyZW5jZUZvY3VzKHJvb21OYW1lLCBjYWxsYmFjayk7XG4gICAgfSxcbiAgICBpc01vZGVyYXRvcjogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gTW9kZXJhdG9yLmlzTW9kZXJhdG9yKCk7XG4gICAgfSxcbiAgICBpc1NpcEdhdGV3YXlFbmFibGVkOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBNb2RlcmF0b3IuaXNTaXBHYXRld2F5RW5hYmxlZCgpO1xuICAgIH0sXG4gICAgaXNFeHRlcm5hbEF1dGhFbmFibGVkOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBNb2RlcmF0b3IuaXNFeHRlcm5hbEF1dGhFbmFibGVkKCk7XG4gICAgfSxcbiAgICBzd2l0Y2hTdHJlYW1zOiBmdW5jdGlvbiAoc3RyZWFtLCBvbGRTdHJlYW0sIGNhbGxiYWNrKSB7XG4gICAgICAgIGlmIChhY3RpdmVjYWxsKSB7XG4gICAgICAgICAgICAvLyBGSVhNRTogd2lsbCBibG9jayBzd2l0Y2hJblByb2dyZXNzIG9uIHRydWUgdmFsdWUgaW4gY2FzZSBvZiBleGNlcHRpb25cbiAgICAgICAgICAgIGFjdGl2ZWNhbGwuc3dpdGNoU3RyZWFtcyhzdHJlYW0sIG9sZFN0cmVhbSwgY2FsbGJhY2spO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gV2UgYXJlIGRvbmUgaW1tZWRpYXRlbHlcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJObyBjb25mZXJlbmNlIGhhbmRsZXJcIik7XG4gICAgICAgICAgICBVSS5tZXNzYWdlSGFuZGxlci5zaG93RXJyb3IoJ0Vycm9yJyxcbiAgICAgICAgICAgICAgICAnVW5hYmxlIHRvIHN3aXRjaCB2aWRlbyBzdHJlYW0uJyk7XG4gICAgICAgICAgICBjYWxsYmFjaygpO1xuICAgICAgICB9XG4gICAgfSxcbiAgICBzZXRWaWRlb011dGU6IGZ1bmN0aW9uIChtdXRlLCBjYWxsYmFjaywgb3B0aW9ucykge1xuICAgICAgIGlmKGFjdGl2ZWNhbGwgJiYgY29ubmVjdGlvbiAmJiBSVEMubG9jYWxWaWRlbylcbiAgICAgICB7XG4gICAgICAgICAgIGFjdGl2ZWNhbGwuc2V0VmlkZW9NdXRlKG11dGUsIGNhbGxiYWNrLCBvcHRpb25zKTtcbiAgICAgICB9XG4gICAgfSxcbiAgICBzZXRBdWRpb011dGU6IGZ1bmN0aW9uIChtdXRlLCBjYWxsYmFjaykge1xuICAgICAgICBpZiAoIShjb25uZWN0aW9uICYmIFJUQy5sb2NhbEF1ZGlvKSkge1xuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG5cblxuICAgICAgICBpZiAodGhpcy5mb3JjZU11dGVkICYmICFtdXRlKSB7XG4gICAgICAgICAgICBjb25zb2xlLmluZm8oXCJBc2tpbmcgZm9jdXMgZm9yIHVubXV0ZVwiKTtcbiAgICAgICAgICAgIGNvbm5lY3Rpb24ubW9kZXJhdGUuc2V0TXV0ZShjb25uZWN0aW9uLmVtdWMubXlyb29tamlkLCBtdXRlKTtcbiAgICAgICAgICAgIC8vIEZJWE1FOiB3YWl0IGZvciByZXN1bHQgYmVmb3JlIHJlc2V0dGluZyBtdXRlZCBzdGF0dXNcbiAgICAgICAgICAgIHRoaXMuZm9yY2VNdXRlZCA9IGZhbHNlO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKG11dGUgPT0gUlRDLmxvY2FsQXVkaW8uaXNNdXRlZCgpKSB7XG4gICAgICAgICAgICAvLyBOb3RoaW5nIHRvIGRvXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEl0IGlzIG5vdCBjbGVhciB3aGF0IGlzIHRoZSByaWdodCB3YXkgdG8gaGFuZGxlIG11bHRpcGxlIHRyYWNrcy5cbiAgICAgICAgLy8gU28gYXQgbGVhc3QgbWFrZSBzdXJlIHRoYXQgdGhleSBhcmUgYWxsIG11dGVkIG9yIGFsbCB1bm11dGVkIGFuZFxuICAgICAgICAvLyB0aGF0IHdlIHNlbmQgcHJlc2VuY2UganVzdCBvbmNlLlxuICAgICAgICBSVEMubG9jYWxBdWRpby5tdXRlKCk7XG4gICAgICAgIC8vIGlzTXV0ZWQgaXMgdGhlIG9wcG9zaXRlIG9mIGF1ZGlvRW5hYmxlZFxuICAgICAgICBjb25uZWN0aW9uLmVtdWMuYWRkQXVkaW9JbmZvVG9QcmVzZW5jZShtdXRlKTtcbiAgICAgICAgY29ubmVjdGlvbi5lbXVjLnNlbmRQcmVzZW5jZSgpO1xuICAgICAgICBjYWxsYmFjaygpO1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9LFxuICAgIC8vIFJlYWxseSBtdXRlIHZpZGVvLCBpLmUuIGRvbnQgZXZlbiBzZW5kIGJsYWNrIGZyYW1lc1xuICAgIG11dGVWaWRlbzogZnVuY3Rpb24gKHBjLCB1bm11dGUpIHtcbiAgICAgICAgLy8gRklYTUU6IHRoaXMgcHJvYmFibHkgbmVlZHMgYW5vdGhlciBvZiB0aG9zZSBsb3ZlbHkgc3RhdGUgc2FmZWd1YXJkcy4uLlxuICAgICAgICAvLyB3aGljaCBjaGVja3MgZm9yIGljZWNvbm4gPT0gY29ubmVjdGVkIGFuZCBzaWdzdGF0ZSA9PSBzdGFibGVcbiAgICAgICAgcGMuc2V0UmVtb3RlRGVzY3JpcHRpb24ocGMucmVtb3RlRGVzY3JpcHRpb24sXG4gICAgICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgcGMuY3JlYXRlQW5zd2VyKFxuICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoYW5zd2VyKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgc2RwID0gbmV3IFNEUChhbnN3ZXIuc2RwKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChzZHAubWVkaWEubGVuZ3RoID4gMSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICh1bm11dGUpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNkcC5tZWRpYVsxXSA9IHNkcC5tZWRpYVsxXS5yZXBsYWNlKCdhPXJlY3Zvbmx5JywgJ2E9c2VuZHJlY3YnKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNkcC5tZWRpYVsxXSA9IHNkcC5tZWRpYVsxXS5yZXBsYWNlKCdhPXNlbmRyZWN2JywgJ2E9cmVjdm9ubHknKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZHAucmF3ID0gc2RwLnNlc3Npb24gKyBzZHAubWVkaWEuam9pbignJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5zd2VyLnNkcCA9IHNkcC5yYXc7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICBwYy5zZXRMb2NhbERlc2NyaXB0aW9uKGFuc3dlcixcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdtdXRlIFNMRCBvaycpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdtdXRlIFNMRCBlcnJvcicpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBVSS5tZXNzYWdlSGFuZGxlci5zaG93RXJyb3IoJ0Vycm9yJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnT29wcyEgU29tZXRoaW5nIHdlbnQgd3JvbmcgYW5kIHdlIGZhaWxlZCB0byAnICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnbXV0ZSEgKFNMRCBGYWlsdXJlKScpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coZXJyb3IpO1xuICAgICAgICAgICAgICAgICAgICAgICAgVUkubWVzc2FnZUhhbmRsZXIuc2hvd0Vycm9yKCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdtdXRlVmlkZW8gU1JEIGVycm9yJyk7XG4gICAgICAgICAgICAgICAgVUkubWVzc2FnZUhhbmRsZXIuc2hvd0Vycm9yKCdFcnJvcicsXG4gICAgICAgICAgICAgICAgICAgICAgICAnT29wcyEgU29tZXRoaW5nIHdlbnQgd3JvbmcgYW5kIHdlIGZhaWxlZCB0byBzdG9wIHZpZGVvIScgK1xuICAgICAgICAgICAgICAgICAgICAgICAgJyhTUkQgRmFpbHVyZSknKTtcblxuICAgICAgICAgICAgfVxuICAgICAgICApO1xuICAgIH0sXG4gICAgdG9nZ2xlUmVjb3JkaW5nOiBmdW5jdGlvbiAodG9rZW5FbXB0eUNhbGxiYWNrLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0aW5nQ2FsbGJhY2ssIHN0YXJ0ZWRDYWxsYmFjaykge1xuICAgICAgICBSZWNvcmRpbmcudG9nZ2xlUmVjb3JkaW5nKHRva2VuRW1wdHlDYWxsYmFjayxcbiAgICAgICAgICAgIHN0YXJ0aW5nQ2FsbGJhY2ssIHN0YXJ0ZWRDYWxsYmFjayk7XG4gICAgfSxcbiAgICBhZGRUb1ByZXNlbmNlOiBmdW5jdGlvbiAobmFtZSwgdmFsdWUsIGRvbnRTZW5kKSB7XG4gICAgICAgIHN3aXRjaCAobmFtZSlcbiAgICAgICAge1xuICAgICAgICAgICAgY2FzZSBcImRpc3BsYXlOYW1lXCI6XG4gICAgICAgICAgICAgICAgY29ubmVjdGlvbi5lbXVjLmFkZERpc3BsYXlOYW1lVG9QcmVzZW5jZSh2YWx1ZSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlIFwiZXRoZXJwYWRcIjpcbiAgICAgICAgICAgICAgICBjb25uZWN0aW9uLmVtdWMuYWRkRXRoZXJwYWRUb1ByZXNlbmNlKHZhbHVlKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgXCJwcmV6aVwiOlxuICAgICAgICAgICAgICAgIGNvbm5lY3Rpb24uZW11Yy5hZGRQcmV6aVRvUHJlc2VuY2UodmFsdWUsIDApO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSBcInByZXppU2xpZGVcIjpcbiAgICAgICAgICAgICAgICBjb25uZWN0aW9uLmVtdWMuYWRkQ3VycmVudFNsaWRlVG9QcmVzZW5jZSh2YWx1ZSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlIFwiY29ubmVjdGlvblF1YWxpdHlcIjpcbiAgICAgICAgICAgICAgICBjb25uZWN0aW9uLmVtdWMuYWRkQ29ubmVjdGlvbkluZm9Ub1ByZXNlbmNlKHZhbHVlKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgXCJlbWFpbFwiOlxuICAgICAgICAgICAgICAgIGNvbm5lY3Rpb24uZW11Yy5hZGRFbWFpbFRvUHJlc2VuY2UodmFsdWUpO1xuICAgICAgICAgICAgZGVmYXVsdCA6XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coXCJVbmtub3duIHRhZyBmb3IgcHJlc2VuY2UuXCIpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBpZighZG9udFNlbmQpXG4gICAgICAgICAgICBjb25uZWN0aW9uLmVtdWMuc2VuZFByZXNlbmNlKCk7XG4gICAgfSxcbiAgICBzZW5kTG9nczogZnVuY3Rpb24gKGNvbnRlbnQpIHtcbiAgICAgICAgLy8gWEVQLTAzMzctaXNoXG4gICAgICAgIHZhciBtZXNzYWdlID0gJG1zZyh7dG86IGZvY3VzTXVjSmlkLCB0eXBlOiAnbm9ybWFsJ30pO1xuICAgICAgICBtZXNzYWdlLmMoJ2xvZycsIHsgeG1sbnM6ICd1cm46eG1wcDpldmVudGxvZycsXG4gICAgICAgICAgICBpZDogJ1BlZXJDb25uZWN0aW9uU3RhdHMnfSk7XG4gICAgICAgIG1lc3NhZ2UuYygnbWVzc2FnZScpLnQoY29udGVudCkudXAoKTtcbiAgICAgICAgaWYgKGRlZmxhdGUpIHtcbiAgICAgICAgICAgIG1lc3NhZ2UuYygndGFnJywge25hbWU6IFwiZGVmbGF0ZWRcIiwgdmFsdWU6IFwidHJ1ZVwifSkudXAoKTtcbiAgICAgICAgfVxuICAgICAgICBtZXNzYWdlLnVwKCk7XG5cbiAgICAgICAgY29ubmVjdGlvbi5zZW5kKG1lc3NhZ2UpO1xuICAgIH0sXG4gICAgcG9wdWxhdGVEYXRhOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBkYXRhID0ge307XG4gICAgICAgIGlmIChjb25uZWN0aW9uLmppbmdsZSkge1xuICAgICAgICAgICAgZGF0YSA9IGNvbm5lY3Rpb24uamluZ2xlLnBvcHVsYXRlRGF0YSgpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBkYXRhO1xuICAgIH0sXG4gICAgZ2V0TG9nZ2VyOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGlmKGNvbm5lY3Rpb24ubG9nZ2VyKVxuICAgICAgICAgICAgcmV0dXJuIGNvbm5lY3Rpb24ubG9nZ2VyLmxvZztcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgfSxcbiAgICBnZXRQcmV6aTogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gY29ubmVjdGlvbi5lbXVjLmdldFByZXppKHRoaXMubXlKaWQoKSk7XG4gICAgfSxcbiAgICByZW1vdmVQcmV6aUZyb21QcmVzZW5jZTogZnVuY3Rpb24gKCkge1xuICAgICAgICBjb25uZWN0aW9uLmVtdWMucmVtb3ZlUHJlemlGcm9tUHJlc2VuY2UoKTtcbiAgICAgICAgY29ubmVjdGlvbi5lbXVjLnNlbmRQcmVzZW5jZSgpO1xuICAgIH0sXG4gICAgc2VuZENoYXRNZXNzYWdlOiBmdW5jdGlvbiAobWVzc2FnZSwgbmlja25hbWUpIHtcbiAgICAgICAgY29ubmVjdGlvbi5lbXVjLnNlbmRNZXNzYWdlKG1lc3NhZ2UsIG5pY2tuYW1lKTtcbiAgICB9LFxuICAgIHNldFN1YmplY3Q6IGZ1bmN0aW9uICh0b3BpYykge1xuICAgICAgICBjb25uZWN0aW9uLmVtdWMuc2V0U3ViamVjdCh0b3BpYyk7XG4gICAgfSxcbiAgICBsb2NrUm9vbTogZnVuY3Rpb24gKGtleSwgb25TdWNjZXNzLCBvbkVycm9yLCBvbk5vdFN1cHBvcnRlZCkge1xuICAgICAgICBjb25uZWN0aW9uLmVtdWMubG9ja1Jvb20oa2V5LCBvblN1Y2Nlc3MsIG9uRXJyb3IsIG9uTm90U3VwcG9ydGVkKTtcbiAgICB9LFxuICAgIGRpYWw6IGZ1bmN0aW9uICh0bywgZnJvbSwgcm9vbU5hbWUscm9vbVBhc3MpIHtcbiAgICAgICAgY29ubmVjdGlvbi5yYXlvLmRpYWwodG8sIGZyb20sIHJvb21OYW1lLHJvb21QYXNzKTtcbiAgICB9LFxuICAgIHNldE11dGU6IGZ1bmN0aW9uIChqaWQsIG11dGUpIHtcbiAgICAgICAgY29ubmVjdGlvbi5tb2RlcmF0ZS5zZXRNdXRlKGppZCwgbXV0ZSk7XG4gICAgfSxcbiAgICBlamVjdDogZnVuY3Rpb24gKGppZCkge1xuICAgICAgICBjb25uZWN0aW9uLm1vZGVyYXRlLmVqZWN0KGppZCk7XG4gICAgfSxcbiAgICBmaW5kSmlkRnJvbVJlc291cmNlOiBmdW5jdGlvbiAocmVzb3VyY2UpIHtcbiAgICAgICAgY29ubmVjdGlvbi5lbXVjLmZpbmRKaWRGcm9tUmVzb3VyY2UocmVzb3VyY2UpO1xuICAgIH0sXG4gICAgZ2V0TWVtYmVyczogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gY29ubmVjdGlvbi5lbXVjLm1lbWJlcnM7XG4gICAgfVxuXG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFhNUFA7IiwiLy8gQ29weXJpZ2h0IEpveWVudCwgSW5jLiBhbmQgb3RoZXIgTm9kZSBjb250cmlidXRvcnMuXG4vL1xuLy8gUGVybWlzc2lvbiBpcyBoZXJlYnkgZ3JhbnRlZCwgZnJlZSBvZiBjaGFyZ2UsIHRvIGFueSBwZXJzb24gb2J0YWluaW5nIGFcbi8vIGNvcHkgb2YgdGhpcyBzb2Z0d2FyZSBhbmQgYXNzb2NpYXRlZCBkb2N1bWVudGF0aW9uIGZpbGVzICh0aGVcbi8vIFwiU29mdHdhcmVcIiksIHRvIGRlYWwgaW4gdGhlIFNvZnR3YXJlIHdpdGhvdXQgcmVzdHJpY3Rpb24sIGluY2x1ZGluZ1xuLy8gd2l0aG91dCBsaW1pdGF0aW9uIHRoZSByaWdodHMgdG8gdXNlLCBjb3B5LCBtb2RpZnksIG1lcmdlLCBwdWJsaXNoLFxuLy8gZGlzdHJpYnV0ZSwgc3VibGljZW5zZSwgYW5kL29yIHNlbGwgY29waWVzIG9mIHRoZSBTb2Z0d2FyZSwgYW5kIHRvIHBlcm1pdFxuLy8gcGVyc29ucyB0byB3aG9tIHRoZSBTb2Z0d2FyZSBpcyBmdXJuaXNoZWQgdG8gZG8gc28sIHN1YmplY3QgdG8gdGhlXG4vLyBmb2xsb3dpbmcgY29uZGl0aW9uczpcbi8vXG4vLyBUaGUgYWJvdmUgY29weXJpZ2h0IG5vdGljZSBhbmQgdGhpcyBwZXJtaXNzaW9uIG5vdGljZSBzaGFsbCBiZSBpbmNsdWRlZFxuLy8gaW4gYWxsIGNvcGllcyBvciBzdWJzdGFudGlhbCBwb3J0aW9ucyBvZiB0aGUgU29mdHdhcmUuXG4vL1xuLy8gVEhFIFNPRlRXQVJFIElTIFBST1ZJREVEIFwiQVMgSVNcIiwgV0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORCwgRVhQUkVTU1xuLy8gT1IgSU1QTElFRCwgSU5DTFVESU5HIEJVVCBOT1QgTElNSVRFRCBUTyBUSEUgV0FSUkFOVElFUyBPRlxuLy8gTUVSQ0hBTlRBQklMSVRZLCBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBTkQgTk9OSU5GUklOR0VNRU5ULiBJTlxuLy8gTk8gRVZFTlQgU0hBTEwgVEhFIEFVVEhPUlMgT1IgQ09QWVJJR0hUIEhPTERFUlMgQkUgTElBQkxFIEZPUiBBTlkgQ0xBSU0sXG4vLyBEQU1BR0VTIE9SIE9USEVSIExJQUJJTElUWSwgV0hFVEhFUiBJTiBBTiBBQ1RJT04gT0YgQ09OVFJBQ1QsIFRPUlQgT1Jcbi8vIE9USEVSV0lTRSwgQVJJU0lORyBGUk9NLCBPVVQgT0YgT1IgSU4gQ09OTkVDVElPTiBXSVRIIFRIRSBTT0ZUV0FSRSBPUiBUSEVcbi8vIFVTRSBPUiBPVEhFUiBERUFMSU5HUyBJTiBUSEUgU09GVFdBUkUuXG5cbmZ1bmN0aW9uIEV2ZW50RW1pdHRlcigpIHtcbiAgdGhpcy5fZXZlbnRzID0gdGhpcy5fZXZlbnRzIHx8IHt9O1xuICB0aGlzLl9tYXhMaXN0ZW5lcnMgPSB0aGlzLl9tYXhMaXN0ZW5lcnMgfHwgdW5kZWZpbmVkO1xufVxubW9kdWxlLmV4cG9ydHMgPSBFdmVudEVtaXR0ZXI7XG5cbi8vIEJhY2t3YXJkcy1jb21wYXQgd2l0aCBub2RlIDAuMTAueFxuRXZlbnRFbWl0dGVyLkV2ZW50RW1pdHRlciA9IEV2ZW50RW1pdHRlcjtcblxuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5fZXZlbnRzID0gdW5kZWZpbmVkO1xuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5fbWF4TGlzdGVuZXJzID0gdW5kZWZpbmVkO1xuXG4vLyBCeSBkZWZhdWx0IEV2ZW50RW1pdHRlcnMgd2lsbCBwcmludCBhIHdhcm5pbmcgaWYgbW9yZSB0aGFuIDEwIGxpc3RlbmVycyBhcmVcbi8vIGFkZGVkIHRvIGl0LiBUaGlzIGlzIGEgdXNlZnVsIGRlZmF1bHQgd2hpY2ggaGVscHMgZmluZGluZyBtZW1vcnkgbGVha3MuXG5FdmVudEVtaXR0ZXIuZGVmYXVsdE1heExpc3RlbmVycyA9IDEwO1xuXG4vLyBPYnZpb3VzbHkgbm90IGFsbCBFbWl0dGVycyBzaG91bGQgYmUgbGltaXRlZCB0byAxMC4gVGhpcyBmdW5jdGlvbiBhbGxvd3Ncbi8vIHRoYXQgdG8gYmUgaW5jcmVhc2VkLiBTZXQgdG8gemVybyBmb3IgdW5saW1pdGVkLlxuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5zZXRNYXhMaXN0ZW5lcnMgPSBmdW5jdGlvbihuKSB7XG4gIGlmICghaXNOdW1iZXIobikgfHwgbiA8IDAgfHwgaXNOYU4obikpXG4gICAgdGhyb3cgVHlwZUVycm9yKCduIG11c3QgYmUgYSBwb3NpdGl2ZSBudW1iZXInKTtcbiAgdGhpcy5fbWF4TGlzdGVuZXJzID0gbjtcbiAgcmV0dXJuIHRoaXM7XG59O1xuXG5FdmVudEVtaXR0ZXIucHJvdG90eXBlLmVtaXQgPSBmdW5jdGlvbih0eXBlKSB7XG4gIHZhciBlciwgaGFuZGxlciwgbGVuLCBhcmdzLCBpLCBsaXN0ZW5lcnM7XG5cbiAgaWYgKCF0aGlzLl9ldmVudHMpXG4gICAgdGhpcy5fZXZlbnRzID0ge307XG5cbiAgLy8gSWYgdGhlcmUgaXMgbm8gJ2Vycm9yJyBldmVudCBsaXN0ZW5lciB0aGVuIHRocm93LlxuICBpZiAodHlwZSA9PT0gJ2Vycm9yJykge1xuICAgIGlmICghdGhpcy5fZXZlbnRzLmVycm9yIHx8XG4gICAgICAgIChpc09iamVjdCh0aGlzLl9ldmVudHMuZXJyb3IpICYmICF0aGlzLl9ldmVudHMuZXJyb3IubGVuZ3RoKSkge1xuICAgICAgZXIgPSBhcmd1bWVudHNbMV07XG4gICAgICBpZiAoZXIgaW5zdGFuY2VvZiBFcnJvcikge1xuICAgICAgICB0aHJvdyBlcjsgLy8gVW5oYW5kbGVkICdlcnJvcicgZXZlbnRcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRocm93IFR5cGVFcnJvcignVW5jYXVnaHQsIHVuc3BlY2lmaWVkIFwiZXJyb3JcIiBldmVudC4nKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cblxuICBoYW5kbGVyID0gdGhpcy5fZXZlbnRzW3R5cGVdO1xuXG4gIGlmIChpc1VuZGVmaW5lZChoYW5kbGVyKSlcbiAgICByZXR1cm4gZmFsc2U7XG5cbiAgaWYgKGlzRnVuY3Rpb24oaGFuZGxlcikpIHtcbiAgICBzd2l0Y2ggKGFyZ3VtZW50cy5sZW5ndGgpIHtcbiAgICAgIC8vIGZhc3QgY2FzZXNcbiAgICAgIGNhc2UgMTpcbiAgICAgICAgaGFuZGxlci5jYWxsKHRoaXMpO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgMjpcbiAgICAgICAgaGFuZGxlci5jYWxsKHRoaXMsIGFyZ3VtZW50c1sxXSk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAzOlxuICAgICAgICBoYW5kbGVyLmNhbGwodGhpcywgYXJndW1lbnRzWzFdLCBhcmd1bWVudHNbMl0pO1xuICAgICAgICBicmVhaztcbiAgICAgIC8vIHNsb3dlclxuICAgICAgZGVmYXVsdDpcbiAgICAgICAgbGVuID0gYXJndW1lbnRzLmxlbmd0aDtcbiAgICAgICAgYXJncyA9IG5ldyBBcnJheShsZW4gLSAxKTtcbiAgICAgICAgZm9yIChpID0gMTsgaSA8IGxlbjsgaSsrKVxuICAgICAgICAgIGFyZ3NbaSAtIDFdID0gYXJndW1lbnRzW2ldO1xuICAgICAgICBoYW5kbGVyLmFwcGx5KHRoaXMsIGFyZ3MpO1xuICAgIH1cbiAgfSBlbHNlIGlmIChpc09iamVjdChoYW5kbGVyKSkge1xuICAgIGxlbiA9IGFyZ3VtZW50cy5sZW5ndGg7XG4gICAgYXJncyA9IG5ldyBBcnJheShsZW4gLSAxKTtcbiAgICBmb3IgKGkgPSAxOyBpIDwgbGVuOyBpKyspXG4gICAgICBhcmdzW2kgLSAxXSA9IGFyZ3VtZW50c1tpXTtcblxuICAgIGxpc3RlbmVycyA9IGhhbmRsZXIuc2xpY2UoKTtcbiAgICBsZW4gPSBsaXN0ZW5lcnMubGVuZ3RoO1xuICAgIGZvciAoaSA9IDA7IGkgPCBsZW47IGkrKylcbiAgICAgIGxpc3RlbmVyc1tpXS5hcHBseSh0aGlzLCBhcmdzKTtcbiAgfVxuXG4gIHJldHVybiB0cnVlO1xufTtcblxuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5hZGRMaXN0ZW5lciA9IGZ1bmN0aW9uKHR5cGUsIGxpc3RlbmVyKSB7XG4gIHZhciBtO1xuXG4gIGlmICghaXNGdW5jdGlvbihsaXN0ZW5lcikpXG4gICAgdGhyb3cgVHlwZUVycm9yKCdsaXN0ZW5lciBtdXN0IGJlIGEgZnVuY3Rpb24nKTtcblxuICBpZiAoIXRoaXMuX2V2ZW50cylcbiAgICB0aGlzLl9ldmVudHMgPSB7fTtcblxuICAvLyBUbyBhdm9pZCByZWN1cnNpb24gaW4gdGhlIGNhc2UgdGhhdCB0eXBlID09PSBcIm5ld0xpc3RlbmVyXCIhIEJlZm9yZVxuICAvLyBhZGRpbmcgaXQgdG8gdGhlIGxpc3RlbmVycywgZmlyc3QgZW1pdCBcIm5ld0xpc3RlbmVyXCIuXG4gIGlmICh0aGlzLl9ldmVudHMubmV3TGlzdGVuZXIpXG4gICAgdGhpcy5lbWl0KCduZXdMaXN0ZW5lcicsIHR5cGUsXG4gICAgICAgICAgICAgIGlzRnVuY3Rpb24obGlzdGVuZXIubGlzdGVuZXIpID9cbiAgICAgICAgICAgICAgbGlzdGVuZXIubGlzdGVuZXIgOiBsaXN0ZW5lcik7XG5cbiAgaWYgKCF0aGlzLl9ldmVudHNbdHlwZV0pXG4gICAgLy8gT3B0aW1pemUgdGhlIGNhc2Ugb2Ygb25lIGxpc3RlbmVyLiBEb24ndCBuZWVkIHRoZSBleHRyYSBhcnJheSBvYmplY3QuXG4gICAgdGhpcy5fZXZlbnRzW3R5cGVdID0gbGlzdGVuZXI7XG4gIGVsc2UgaWYgKGlzT2JqZWN0KHRoaXMuX2V2ZW50c1t0eXBlXSkpXG4gICAgLy8gSWYgd2UndmUgYWxyZWFkeSBnb3QgYW4gYXJyYXksIGp1c3QgYXBwZW5kLlxuICAgIHRoaXMuX2V2ZW50c1t0eXBlXS5wdXNoKGxpc3RlbmVyKTtcbiAgZWxzZVxuICAgIC8vIEFkZGluZyB0aGUgc2Vjb25kIGVsZW1lbnQsIG5lZWQgdG8gY2hhbmdlIHRvIGFycmF5LlxuICAgIHRoaXMuX2V2ZW50c1t0eXBlXSA9IFt0aGlzLl9ldmVudHNbdHlwZV0sIGxpc3RlbmVyXTtcblxuICAvLyBDaGVjayBmb3IgbGlzdGVuZXIgbGVha1xuICBpZiAoaXNPYmplY3QodGhpcy5fZXZlbnRzW3R5cGVdKSAmJiAhdGhpcy5fZXZlbnRzW3R5cGVdLndhcm5lZCkge1xuICAgIHZhciBtO1xuICAgIGlmICghaXNVbmRlZmluZWQodGhpcy5fbWF4TGlzdGVuZXJzKSkge1xuICAgICAgbSA9IHRoaXMuX21heExpc3RlbmVycztcbiAgICB9IGVsc2Uge1xuICAgICAgbSA9IEV2ZW50RW1pdHRlci5kZWZhdWx0TWF4TGlzdGVuZXJzO1xuICAgIH1cblxuICAgIGlmIChtICYmIG0gPiAwICYmIHRoaXMuX2V2ZW50c1t0eXBlXS5sZW5ndGggPiBtKSB7XG4gICAgICB0aGlzLl9ldmVudHNbdHlwZV0ud2FybmVkID0gdHJ1ZTtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJyhub2RlKSB3YXJuaW5nOiBwb3NzaWJsZSBFdmVudEVtaXR0ZXIgbWVtb3J5ICcgK1xuICAgICAgICAgICAgICAgICAgICAnbGVhayBkZXRlY3RlZC4gJWQgbGlzdGVuZXJzIGFkZGVkLiAnICtcbiAgICAgICAgICAgICAgICAgICAgJ1VzZSBlbWl0dGVyLnNldE1heExpc3RlbmVycygpIHRvIGluY3JlYXNlIGxpbWl0LicsXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX2V2ZW50c1t0eXBlXS5sZW5ndGgpO1xuICAgICAgaWYgKHR5cGVvZiBjb25zb2xlLnRyYWNlID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIC8vIG5vdCBzdXBwb3J0ZWQgaW4gSUUgMTBcbiAgICAgICAgY29uc29sZS50cmFjZSgpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiB0aGlzO1xufTtcblxuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5vbiA9IEV2ZW50RW1pdHRlci5wcm90b3R5cGUuYWRkTGlzdGVuZXI7XG5cbkV2ZW50RW1pdHRlci5wcm90b3R5cGUub25jZSA9IGZ1bmN0aW9uKHR5cGUsIGxpc3RlbmVyKSB7XG4gIGlmICghaXNGdW5jdGlvbihsaXN0ZW5lcikpXG4gICAgdGhyb3cgVHlwZUVycm9yKCdsaXN0ZW5lciBtdXN0IGJlIGEgZnVuY3Rpb24nKTtcblxuICB2YXIgZmlyZWQgPSBmYWxzZTtcblxuICBmdW5jdGlvbiBnKCkge1xuICAgIHRoaXMucmVtb3ZlTGlzdGVuZXIodHlwZSwgZyk7XG5cbiAgICBpZiAoIWZpcmVkKSB7XG4gICAgICBmaXJlZCA9IHRydWU7XG4gICAgICBsaXN0ZW5lci5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgIH1cbiAgfVxuXG4gIGcubGlzdGVuZXIgPSBsaXN0ZW5lcjtcbiAgdGhpcy5vbih0eXBlLCBnKTtcblxuICByZXR1cm4gdGhpcztcbn07XG5cbi8vIGVtaXRzIGEgJ3JlbW92ZUxpc3RlbmVyJyBldmVudCBpZmYgdGhlIGxpc3RlbmVyIHdhcyByZW1vdmVkXG5FdmVudEVtaXR0ZXIucHJvdG90eXBlLnJlbW92ZUxpc3RlbmVyID0gZnVuY3Rpb24odHlwZSwgbGlzdGVuZXIpIHtcbiAgdmFyIGxpc3QsIHBvc2l0aW9uLCBsZW5ndGgsIGk7XG5cbiAgaWYgKCFpc0Z1bmN0aW9uKGxpc3RlbmVyKSlcbiAgICB0aHJvdyBUeXBlRXJyb3IoJ2xpc3RlbmVyIG11c3QgYmUgYSBmdW5jdGlvbicpO1xuXG4gIGlmICghdGhpcy5fZXZlbnRzIHx8ICF0aGlzLl9ldmVudHNbdHlwZV0pXG4gICAgcmV0dXJuIHRoaXM7XG5cbiAgbGlzdCA9IHRoaXMuX2V2ZW50c1t0eXBlXTtcbiAgbGVuZ3RoID0gbGlzdC5sZW5ndGg7XG4gIHBvc2l0aW9uID0gLTE7XG5cbiAgaWYgKGxpc3QgPT09IGxpc3RlbmVyIHx8XG4gICAgICAoaXNGdW5jdGlvbihsaXN0Lmxpc3RlbmVyKSAmJiBsaXN0Lmxpc3RlbmVyID09PSBsaXN0ZW5lcikpIHtcbiAgICBkZWxldGUgdGhpcy5fZXZlbnRzW3R5cGVdO1xuICAgIGlmICh0aGlzLl9ldmVudHMucmVtb3ZlTGlzdGVuZXIpXG4gICAgICB0aGlzLmVtaXQoJ3JlbW92ZUxpc3RlbmVyJywgdHlwZSwgbGlzdGVuZXIpO1xuXG4gIH0gZWxzZSBpZiAoaXNPYmplY3QobGlzdCkpIHtcbiAgICBmb3IgKGkgPSBsZW5ndGg7IGktLSA+IDA7KSB7XG4gICAgICBpZiAobGlzdFtpXSA9PT0gbGlzdGVuZXIgfHxcbiAgICAgICAgICAobGlzdFtpXS5saXN0ZW5lciAmJiBsaXN0W2ldLmxpc3RlbmVyID09PSBsaXN0ZW5lcikpIHtcbiAgICAgICAgcG9zaXRpb24gPSBpO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAocG9zaXRpb24gPCAwKVxuICAgICAgcmV0dXJuIHRoaXM7XG5cbiAgICBpZiAobGlzdC5sZW5ndGggPT09IDEpIHtcbiAgICAgIGxpc3QubGVuZ3RoID0gMDtcbiAgICAgIGRlbGV0ZSB0aGlzLl9ldmVudHNbdHlwZV07XG4gICAgfSBlbHNlIHtcbiAgICAgIGxpc3Quc3BsaWNlKHBvc2l0aW9uLCAxKTtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5fZXZlbnRzLnJlbW92ZUxpc3RlbmVyKVxuICAgICAgdGhpcy5lbWl0KCdyZW1vdmVMaXN0ZW5lcicsIHR5cGUsIGxpc3RlbmVyKTtcbiAgfVxuXG4gIHJldHVybiB0aGlzO1xufTtcblxuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5yZW1vdmVBbGxMaXN0ZW5lcnMgPSBmdW5jdGlvbih0eXBlKSB7XG4gIHZhciBrZXksIGxpc3RlbmVycztcblxuICBpZiAoIXRoaXMuX2V2ZW50cylcbiAgICByZXR1cm4gdGhpcztcblxuICAvLyBub3QgbGlzdGVuaW5nIGZvciByZW1vdmVMaXN0ZW5lciwgbm8gbmVlZCB0byBlbWl0XG4gIGlmICghdGhpcy5fZXZlbnRzLnJlbW92ZUxpc3RlbmVyKSB7XG4gICAgaWYgKGFyZ3VtZW50cy5sZW5ndGggPT09IDApXG4gICAgICB0aGlzLl9ldmVudHMgPSB7fTtcbiAgICBlbHNlIGlmICh0aGlzLl9ldmVudHNbdHlwZV0pXG4gICAgICBkZWxldGUgdGhpcy5fZXZlbnRzW3R5cGVdO1xuICAgIHJldHVybiB0aGlzO1xuICB9XG5cbiAgLy8gZW1pdCByZW1vdmVMaXN0ZW5lciBmb3IgYWxsIGxpc3RlbmVycyBvbiBhbGwgZXZlbnRzXG4gIGlmIChhcmd1bWVudHMubGVuZ3RoID09PSAwKSB7XG4gICAgZm9yIChrZXkgaW4gdGhpcy5fZXZlbnRzKSB7XG4gICAgICBpZiAoa2V5ID09PSAncmVtb3ZlTGlzdGVuZXInKSBjb250aW51ZTtcbiAgICAgIHRoaXMucmVtb3ZlQWxsTGlzdGVuZXJzKGtleSk7XG4gICAgfVxuICAgIHRoaXMucmVtb3ZlQWxsTGlzdGVuZXJzKCdyZW1vdmVMaXN0ZW5lcicpO1xuICAgIHRoaXMuX2V2ZW50cyA9IHt9O1xuICAgIHJldHVybiB0aGlzO1xuICB9XG5cbiAgbGlzdGVuZXJzID0gdGhpcy5fZXZlbnRzW3R5cGVdO1xuXG4gIGlmIChpc0Z1bmN0aW9uKGxpc3RlbmVycykpIHtcbiAgICB0aGlzLnJlbW92ZUxpc3RlbmVyKHR5cGUsIGxpc3RlbmVycyk7XG4gIH0gZWxzZSB7XG4gICAgLy8gTElGTyBvcmRlclxuICAgIHdoaWxlIChsaXN0ZW5lcnMubGVuZ3RoKVxuICAgICAgdGhpcy5yZW1vdmVMaXN0ZW5lcih0eXBlLCBsaXN0ZW5lcnNbbGlzdGVuZXJzLmxlbmd0aCAtIDFdKTtcbiAgfVxuICBkZWxldGUgdGhpcy5fZXZlbnRzW3R5cGVdO1xuXG4gIHJldHVybiB0aGlzO1xufTtcblxuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5saXN0ZW5lcnMgPSBmdW5jdGlvbih0eXBlKSB7XG4gIHZhciByZXQ7XG4gIGlmICghdGhpcy5fZXZlbnRzIHx8ICF0aGlzLl9ldmVudHNbdHlwZV0pXG4gICAgcmV0ID0gW107XG4gIGVsc2UgaWYgKGlzRnVuY3Rpb24odGhpcy5fZXZlbnRzW3R5cGVdKSlcbiAgICByZXQgPSBbdGhpcy5fZXZlbnRzW3R5cGVdXTtcbiAgZWxzZVxuICAgIHJldCA9IHRoaXMuX2V2ZW50c1t0eXBlXS5zbGljZSgpO1xuICByZXR1cm4gcmV0O1xufTtcblxuRXZlbnRFbWl0dGVyLmxpc3RlbmVyQ291bnQgPSBmdW5jdGlvbihlbWl0dGVyLCB0eXBlKSB7XG4gIHZhciByZXQ7XG4gIGlmICghZW1pdHRlci5fZXZlbnRzIHx8ICFlbWl0dGVyLl9ldmVudHNbdHlwZV0pXG4gICAgcmV0ID0gMDtcbiAgZWxzZSBpZiAoaXNGdW5jdGlvbihlbWl0dGVyLl9ldmVudHNbdHlwZV0pKVxuICAgIHJldCA9IDE7XG4gIGVsc2VcbiAgICByZXQgPSBlbWl0dGVyLl9ldmVudHNbdHlwZV0ubGVuZ3RoO1xuICByZXR1cm4gcmV0O1xufTtcblxuZnVuY3Rpb24gaXNGdW5jdGlvbihhcmcpIHtcbiAgcmV0dXJuIHR5cGVvZiBhcmcgPT09ICdmdW5jdGlvbic7XG59XG5cbmZ1bmN0aW9uIGlzTnVtYmVyKGFyZykge1xuICByZXR1cm4gdHlwZW9mIGFyZyA9PT0gJ251bWJlcic7XG59XG5cbmZ1bmN0aW9uIGlzT2JqZWN0KGFyZykge1xuICByZXR1cm4gdHlwZW9mIGFyZyA9PT0gJ29iamVjdCcgJiYgYXJnICE9PSBudWxsO1xufVxuXG5mdW5jdGlvbiBpc1VuZGVmaW5lZChhcmcpIHtcbiAgcmV0dXJuIGFyZyA9PT0gdm9pZCAwO1xufVxuIl19