123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719137201372113722137231372413725137261372713728137291373013731137321373313734137351373613737137381373913740137411374213743137441374513746137471374813749137501375113752137531375413755137561375713758137591376013761137621376313764137651376613767137681376913770137711377213773137741377513776137771377813779137801378113782137831378413785137861378713788137891379013791137921379313794137951379613797137981379913800138011380213803138041380513806138071380813809138101381113812138131381413815138161381713818138191382013821138221382313824138251382613827138281382913830138311383213833138341383513836138371383813839138401384113842138431384413845138461384713848138491385013851138521385313854138551385613857138581385913860138611386213863138641386513866138671386813869138701387113872138731387413875138761387713878138791388013881138821388313884138851388613887138881388913890138911389213893138941389513896138971389813899139001390113902139031390413905139061390713908139091391013911139121391313914139151391613917139181391913920139211392213923139241392513926139271392813929139301393113932139331393413935139361393713938139391394013941139421394313944139451394613947139481394913950139511395213953139541395513956139571395813959139601396113962139631396413965139661396713968139691397013971139721397313974139751397613977139781397913980139811398213983139841398513986139871398813989139901399113992139931399413995139961399713998139991400014001140021400314004140051400614007140081400914010140111401214013140141401514016140171401814019140201402114022140231402414025140261402714028140291403014031140321403314034140351403614037140381403914040140411404214043140441404514046140471404814049140501405114052140531405414055140561405714058140591406014061140621406314064140651406614067140681406914070140711407214073140741407514076140771407814079140801408114082140831408414085140861408714088140891409014091140921409314094140951409614097140981409914100141011410214103141041410514106141071410814109141101411114112141131411414115141161411714118141191412014121141221412314124141251412614127141281412914130141311413214133141341413514136141371413814139141401414114142141431414414145141461414714148141491415014151141521415314154141551415614157141581415914160141611416214163141641416514166141671416814169141701417114172141731417414175141761417714178141791418014181141821418314184141851418614187141881418914190141911419214193141941419514196141971419814199142001420114202142031420414205142061420714208142091421014211142121421314214142151421614217142181421914220142211422214223142241422514226142271422814229142301423114232142331423414235142361423714238142391424014241142421424314244142451424614247142481424914250142511425214253142541425514256142571425814259142601426114262142631426414265142661426714268142691427014271142721427314274142751427614277142781427914280142811428214283142841428514286142871428814289142901429114292142931429414295142961429714298142991430014301143021430314304143051430614307143081430914310143111431214313143141431514316143171431814319143201432114322143231432414325143261432714328143291433014331143321433314334143351433614337143381433914340143411434214343143441434514346143471434814349143501435114352143531435414355143561435714358143591436014361143621436314364143651436614367143681436914370143711437214373143741437514376143771437814379143801438114382143831438414385143861438714388143891439014391143921439314394143951439614397143981439914400144011440214403144041440514406144071440814409144101441114412144131441414415144161441714418144191442014421144221442314424144251442614427144281442914430144311443214433144341443514436144371443814439144401444114442144431444414445144461444714448144491445014451144521445314454144551445614457144581445914460144611446214463144641446514466144671446814469144701447114472144731447414475144761447714478144791448014481144821448314484144851448614487144881448914490144911449214493144941449514496144971449814499145001450114502145031450414505145061450714508145091451014511145121451314514145151451614517145181451914520145211452214523145241452514526145271452814529145301453114532145331453414535145361453714538145391454014541145421454314544145451454614547145481454914550145511455214553145541455514556145571455814559145601456114562145631456414565145661456714568145691457014571145721457314574145751457614577145781457914580145811458214583145841458514586145871458814589145901459114592145931459414595145961459714598145991460014601146021460314604146051460614607146081460914610146111461214613146141461514616146171461814619146201462114622146231462414625146261462714628146291463014631146321463314634146351463614637146381463914640146411464214643146441464514646146471464814649146501465114652146531465414655146561465714658146591466014661146621466314664146651466614667146681466914670146711467214673146741467514676146771467814679146801468114682146831468414685146861468714688146891469014691146921469314694146951469614697146981469914700147011470214703147041470514706147071470814709147101471114712147131471414715147161471714718147191472014721147221472314724147251472614727147281472914730147311473214733147341473514736147371473814739147401474114742147431474414745147461474714748147491475014751147521475314754147551475614757147581475914760147611476214763147641476514766147671476814769147701477114772147731477414775147761477714778147791478014781147821478314784147851478614787147881478914790147911479214793147941479514796147971479814799148001480114802148031480414805148061480714808148091481014811148121481314814148151481614817148181481914820148211482214823148241482514826148271482814829148301483114832148331483414835148361483714838148391484014841148421484314844148451484614847148481484914850148511485214853148541485514856148571485814859148601486114862148631486414865148661486714868148691487014871148721487314874148751487614877148781487914880148811488214883148841488514886148871488814889148901489114892148931489414895148961489714898148991490014901149021490314904149051490614907149081490914910149111491214913149141491514916149171491814919149201492114922149231492414925149261492714928149291493014931149321493314934149351493614937149381493914940149411494214943149441494514946149471494814949149501495114952149531495414955149561495714958149591496014961149621496314964149651496614967149681496914970149711497214973149741497514976149771497814979149801498114982149831498414985149861498714988149891499014991149921499314994149951499614997149981499915000150011500215003150041500515006150071500815009150101501115012150131501415015150161501715018150191502015021150221502315024150251502615027150281502915030150311503215033150341503515036150371503815039150401504115042150431504415045150461504715048150491505015051150521505315054150551505615057150581505915060150611506215063150641506515066150671506815069150701507115072150731507415075150761507715078150791508015081150821508315084150851508615087150881508915090150911509215093150941509515096150971509815099151001510115102151031510415105151061510715108151091511015111151121511315114151151511615117151181511915120151211512215123151241512515126151271512815129151301513115132151331513415135151361513715138151391514015141151421514315144151451514615147151481514915150151511515215153151541515515156151571515815159151601516115162151631516415165151661516715168151691517015171151721517315174151751517615177151781517915180151811518215183151841518515186151871518815189151901519115192151931519415195151961519715198151991520015201152021520315204152051520615207152081520915210152111521215213152141521515216152171521815219152201522115222152231522415225152261522715228152291523015231152321523315234152351523615237152381523915240152411524215243152441524515246152471524815249152501525115252152531525415255152561525715258152591526015261152621526315264152651526615267152681526915270152711527215273152741527515276152771527815279152801528115282152831528415285152861528715288152891529015291152921529315294152951529615297152981529915300153011530215303153041530515306153071530815309153101531115312153131531415315153161531715318153191532015321153221532315324153251532615327153281532915330153311533215333153341533515336153371533815339153401534115342153431534415345153461534715348153491535015351153521535315354153551535615357153581535915360153611536215363153641536515366153671536815369153701537115372153731537415375153761537715378153791538015381153821538315384153851538615387153881538915390153911539215393153941539515396153971539815399154001540115402154031540415405154061540715408154091541015411154121541315414154151541615417154181541915420154211542215423154241542515426154271542815429154301543115432154331543415435154361543715438154391544015441154421544315444154451544615447154481544915450154511545215453154541545515456154571545815459154601546115462154631546415465154661546715468154691547015471154721547315474154751547615477154781547915480154811548215483154841548515486154871548815489154901549115492154931549415495154961549715498154991550015501155021550315504155051550615507155081550915510155111551215513155141551515516155171551815519155201552115522155231552415525155261552715528155291553015531155321553315534155351553615537155381553915540155411554215543155441554515546155471554815549155501555115552155531555415555155561555715558155591556015561155621556315564155651556615567155681556915570155711557215573155741557515576155771557815579155801558115582155831558415585155861558715588155891559015591155921559315594155951559615597155981559915600156011560215603156041560515606156071560815609156101561115612156131561415615156161561715618156191562015621156221562315624156251562615627156281562915630156311563215633156341563515636156371563815639156401564115642156431564415645156461564715648156491565015651156521565315654156551565615657156581565915660156611566215663156641566515666156671566815669156701567115672156731567415675156761567715678156791568015681156821568315684156851568615687156881568915690156911569215693156941569515696156971569815699157001570115702157031570415705157061570715708157091571015711157121571315714157151571615717157181571915720157211572215723157241572515726157271572815729157301573115732157331573415735157361573715738157391574015741157421574315744157451574615747157481574915750157511575215753157541575515756157571575815759157601576115762157631576415765157661576715768157691577015771157721577315774157751577615777157781577915780157811578215783157841578515786157871578815789157901579115792157931579415795157961579715798157991580015801158021580315804158051580615807158081580915810158111581215813158141581515816158171581815819158201582115822158231582415825158261582715828158291583015831158321583315834158351583615837158381583915840158411584215843158441584515846158471584815849158501585115852158531585415855158561585715858158591586015861158621586315864158651586615867158681586915870158711587215873158741587515876158771587815879158801588115882158831588415885158861588715888158891589015891158921589315894158951589615897158981589915900159011590215903159041590515906159071590815909159101591115912159131591415915159161591715918159191592015921159221592315924159251592615927159281592915930159311593215933159341593515936159371593815939159401594115942159431594415945159461594715948159491595015951159521595315954159551595615957159581595915960159611596215963159641596515966159671596815969159701597115972159731597415975159761597715978159791598015981159821598315984159851598615987159881598915990159911599215993159941599515996159971599815999160001600116002160031600416005160061600716008160091601016011160121601316014160151601616017160181601916020160211602216023160241602516026160271602816029160301603116032160331603416035160361603716038160391604016041160421604316044160451604616047160481604916050160511605216053160541605516056160571605816059160601606116062160631606416065160661606716068160691607016071160721607316074160751607616077160781607916080160811608216083160841608516086160871608816089160901609116092160931609416095160961609716098160991610016101161021610316104161051610616107161081610916110161111611216113161141611516116161171611816119161201612116122161231612416125161261612716128161291613016131161321613316134161351613616137161381613916140161411614216143161441614516146161471614816149161501615116152161531615416155161561615716158161591616016161161621616316164161651616616167161681616916170161711617216173161741617516176161771617816179161801618116182161831618416185161861618716188161891619016191161921619316194161951619616197161981619916200162011620216203162041620516206162071620816209162101621116212162131621416215162161621716218162191622016221162221622316224162251622616227162281622916230162311623216233162341623516236162371623816239162401624116242162431624416245162461624716248162491625016251162521625316254162551625616257162581625916260162611626216263162641626516266162671626816269162701627116272162731627416275162761627716278162791628016281162821628316284162851628616287162881628916290162911629216293162941629516296162971629816299163001630116302163031630416305163061630716308163091631016311163121631316314163151631616317163181631916320163211632216323163241632516326163271632816329163301633116332163331633416335163361633716338163391634016341163421634316344163451634616347163481634916350163511635216353163541635516356163571635816359163601636116362163631636416365163661636716368163691637016371163721637316374163751637616377163781637916380163811638216383163841638516386163871638816389163901639116392163931639416395163961639716398163991640016401164021640316404164051640616407164081640916410164111641216413164141641516416164171641816419164201642116422164231642416425164261642716428164291643016431164321643316434164351643616437164381643916440164411644216443164441644516446164471644816449164501645116452164531645416455164561645716458164591646016461164621646316464164651646616467164681646916470164711647216473164741647516476164771647816479164801648116482164831648416485164861648716488164891649016491164921649316494164951649616497164981649916500165011650216503165041650516506165071650816509165101651116512165131651416515165161651716518165191652016521165221652316524165251652616527165281652916530165311653216533165341653516536165371653816539165401654116542165431654416545165461654716548165491655016551165521655316554165551655616557165581655916560165611656216563165641656516566165671656816569165701657116572165731657416575165761657716578165791658016581165821658316584165851658616587165881658916590165911659216593165941659516596165971659816599166001660116602166031660416605166061660716608166091661016611166121661316614166151661616617166181661916620166211662216623166241662516626166271662816629166301663116632166331663416635166361663716638166391664016641166421664316644166451664616647166481664916650166511665216653166541665516656166571665816659166601666116662166631666416665166661666716668166691667016671166721667316674166751667616677166781667916680166811668216683166841668516686166871668816689166901669116692166931669416695166961669716698166991670016701167021670316704167051670616707167081670916710167111671216713167141671516716167171671816719167201672116722167231672416725167261672716728167291673016731167321673316734167351673616737167381673916740167411674216743167441674516746167471674816749167501675116752167531675416755167561675716758167591676016761167621676316764167651676616767167681676916770167711677216773167741677516776167771677816779167801678116782167831678416785167861678716788167891679016791167921679316794167951679616797167981679916800168011680216803168041680516806168071680816809168101681116812168131681416815168161681716818168191682016821168221682316824168251682616827168281682916830168311683216833168341683516836168371683816839168401684116842168431684416845168461684716848168491685016851168521685316854168551685616857168581685916860168611686216863168641686516866168671686816869168701687116872168731687416875168761687716878168791688016881168821688316884168851688616887168881688916890168911689216893168941689516896168971689816899169001690116902169031690416905169061690716908169091691016911169121691316914169151691616917169181691916920169211692216923169241692516926169271692816929169301693116932169331693416935169361693716938169391694016941169421694316944169451694616947169481694916950169511695216953169541695516956169571695816959169601696116962169631696416965169661696716968169691697016971169721697316974169751697616977169781697916980169811698216983169841698516986169871698816989169901699116992169931699416995169961699716998169991700017001170021700317004170051700617007170081700917010170111701217013170141701517016170171701817019170201702117022170231702417025170261702717028170291703017031170321703317034170351703617037170381703917040170411704217043170441704517046170471704817049170501705117052170531705417055170561705717058170591706017061170621706317064170651706617067170681706917070170711707217073170741707517076170771707817079170801708117082170831708417085170861708717088170891709017091170921709317094170951709617097170981709917100171011710217103171041710517106171071710817109171101711117112171131711417115171161711717118171191712017121171221712317124171251712617127171281712917130171311713217133171341713517136171371713817139171401714117142171431714417145171461714717148171491715017151171521715317154171551715617157171581715917160171611716217163171641716517166171671716817169171701717117172171731717417175171761717717178171791718017181171821718317184171851718617187171881718917190171911719217193171941719517196171971719817199172001720117202172031720417205172061720717208172091721017211172121721317214172151721617217172181721917220172211722217223172241722517226172271722817229172301723117232172331723417235172361723717238172391724017241172421724317244172451724617247172481724917250172511725217253172541725517256172571725817259172601726117262172631726417265172661726717268172691727017271172721727317274172751727617277172781727917280172811728217283172841728517286172871728817289172901729117292172931729417295172961729717298172991730017301173021730317304173051730617307173081730917310173111731217313173141731517316173171731817319173201732117322173231732417325173261732717328173291733017331173321733317334173351733617337173381733917340173411734217343173441734517346173471734817349173501735117352173531735417355173561735717358173591736017361173621736317364173651736617367173681736917370173711737217373173741737517376173771737817379173801738117382173831738417385173861738717388173891739017391173921739317394173951739617397173981739917400174011740217403174041740517406174071740817409174101741117412174131741417415174161741717418174191742017421174221742317424174251742617427174281742917430174311743217433174341743517436174371743817439174401744117442174431744417445174461744717448174491745017451174521745317454174551745617457174581745917460174611746217463174641746517466174671746817469174701747117472174731747417475174761747717478174791748017481174821748317484174851748617487174881748917490174911749217493174941749517496174971749817499175001750117502175031750417505175061750717508175091751017511175121751317514175151751617517175181751917520175211752217523175241752517526175271752817529175301753117532175331753417535175361753717538175391754017541175421754317544175451754617547175481754917550175511755217553175541755517556175571755817559175601756117562175631756417565175661756717568175691757017571175721757317574175751757617577175781757917580175811758217583175841758517586175871758817589175901759117592175931759417595175961759717598175991760017601176021760317604176051760617607176081760917610176111761217613176141761517616176171761817619176201762117622176231762417625176261762717628176291763017631176321763317634176351763617637176381763917640176411764217643176441764517646176471764817649176501765117652176531765417655176561765717658176591766017661176621766317664176651766617667176681766917670176711767217673176741767517676176771767817679176801768117682176831768417685176861768717688176891769017691176921769317694176951769617697176981769917700177011770217703177041770517706177071770817709177101771117712177131771417715177161771717718177191772017721177221772317724177251772617727177281772917730177311773217733177341773517736177371773817739177401774117742177431774417745177461774717748177491775017751177521775317754177551775617757177581775917760177611776217763177641776517766177671776817769177701777117772177731777417775177761777717778177791778017781177821778317784177851778617787177881778917790177911779217793177941779517796177971779817799178001780117802178031780417805178061780717808178091781017811178121781317814178151781617817178181781917820178211782217823178241782517826178271782817829178301783117832178331783417835178361783717838178391784017841178421784317844178451784617847178481784917850178511785217853178541785517856178571785817859178601786117862178631786417865178661786717868178691787017871178721787317874178751787617877178781787917880178811788217883178841788517886178871788817889178901789117892178931789417895178961789717898178991790017901179021790317904179051790617907179081790917910179111791217913179141791517916179171791817919179201792117922179231792417925179261792717928179291793017931179321793317934179351793617937179381793917940179411794217943179441794517946179471794817949179501795117952179531795417955179561795717958179591796017961179621796317964179651796617967179681796917970179711797217973179741797517976179771797817979179801798117982179831798417985179861798717988179891799017991179921799317994179951799617997179981799918000180011800218003180041800518006180071800818009180101801118012180131801418015180161801718018180191802018021180221802318024180251802618027180281802918030180311803218033180341803518036180371803818039180401804118042180431804418045180461804718048180491805018051180521805318054180551805618057180581805918060180611806218063180641806518066180671806818069180701807118072180731807418075180761807718078180791808018081180821808318084180851808618087180881808918090180911809218093180941809518096180971809818099181001810118102181031810418105181061810718108181091811018111181121811318114181151811618117181181811918120181211812218123181241812518126181271812818129181301813118132181331813418135181361813718138181391814018141181421814318144181451814618147181481814918150181511815218153181541815518156181571815818159181601816118162181631816418165181661816718168181691817018171181721817318174181751817618177181781817918180181811818218183181841818518186181871818818189181901819118192181931819418195181961819718198181991820018201182021820318204182051820618207182081820918210182111821218213182141821518216182171821818219182201822118222182231822418225182261822718228182291823018231182321823318234182351823618237182381823918240182411824218243182441824518246182471824818249182501825118252182531825418255182561825718258182591826018261182621826318264182651826618267182681826918270182711827218273182741827518276182771827818279182801828118282182831828418285182861828718288182891829018291182921829318294182951829618297182981829918300183011830218303183041830518306183071830818309183101831118312183131831418315183161831718318183191832018321183221832318324183251832618327183281832918330183311833218333183341833518336183371833818339183401834118342183431834418345183461834718348 |
- (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){
- function corslite(url, callback, cors) {
- var sent = false;
- if (typeof window.XMLHttpRequest === 'undefined') {
- return callback(Error('Browser not supported'));
- }
- if (typeof cors === 'undefined') {
- var m = url.match(/^\s*https?:\/\/[^\/]*/);
- cors = m && (m[0] !== location.protocol + '//' + location.domain +
- (location.port ? ':' + location.port : ''));
- }
- var x = new window.XMLHttpRequest();
- function isSuccessful(status) {
- return status >= 200 && status < 300 || status === 304;
- }
- if (cors && !('withCredentials' in x)) {
- // IE8-9
- x = new window.XDomainRequest();
- // Ensure callback is never called synchronously, i.e., before
- // x.send() returns (this has been observed in the wild).
- // See https://github.com/mapbox/mapbox.js/issues/472
- var original = callback;
- callback = function() {
- if (sent) {
- original.apply(this, arguments);
- } else {
- var that = this, args = arguments;
- setTimeout(function() {
- original.apply(that, args);
- }, 0);
- }
- }
- }
- function loaded() {
- if (
- // XDomainRequest
- x.status === undefined ||
- // modern browsers
- isSuccessful(x.status)) callback.call(x, null, x);
- else callback.call(x, x, null);
- }
- // Both `onreadystatechange` and `onload` can fire. `onreadystatechange`
- // has [been supported for longer](http://stackoverflow.com/a/9181508/229001).
- if ('onload' in x) {
- x.onload = loaded;
- } else {
- x.onreadystatechange = function readystate() {
- if (x.readyState === 4) {
- loaded();
- }
- };
- }
- // Call the callback with the XMLHttpRequest object as an error and prevent
- // it from ever being called again by reassigning it to `noop`
- x.onerror = function error(evt) {
- // XDomainRequest provides no evt parameter
- callback.call(this, evt || true, null);
- callback = function() { };
- };
- // IE9 must have onprogress be set to a unique function.
- x.onprogress = function() { };
- x.ontimeout = function(evt) {
- callback.call(this, evt, null);
- callback = function() { };
- };
- x.onabort = function(evt) {
- callback.call(this, evt, null);
- callback = function() { };
- };
- // GET is the only supported HTTP Verb by XDomainRequest and is the
- // only one supported here.
- x.open('GET', url, true);
- // Send the request. Sending data is not supported.
- x.send(null);
- sent = true;
- return x;
- }
- if (typeof module !== 'undefined') module.exports = corslite;
- },{}],2:[function(require,module,exports){
- module.exports = Array.isArray || function (arr) {
- return Object.prototype.toString.call(arr) == '[object Array]';
- };
- },{}],3:[function(require,module,exports){
- /*
- Leaflet 1.0.2, a JS library for interactive maps. http://leafletjs.com
- (c) 2010-2016 Vladimir Agafonkin, (c) 2010-2011 CloudMade
- */
- (function (window, document, undefined) {
- var L = {
- version: "1.0.2"
- };
- function expose() {
- var oldL = window.L;
- L.noConflict = function () {
- window.L = oldL;
- return this;
- };
- window.L = L;
- }
- // define Leaflet for Node module pattern loaders, including Browserify
- if (typeof module === 'object' && typeof module.exports === 'object') {
- module.exports = L;
- // define Leaflet as an AMD module
- } else if (typeof define === 'function' && define.amd) {
- define(L);
- }
- // define Leaflet as a global L variable, saving the original L to restore later if needed
- if (typeof window !== 'undefined') {
- expose();
- }
- /*
- * @namespace Util
- *
- * Various utility functions, used by Leaflet internally.
- */
- L.Util = {
- // @function extend(dest: Object, src?: Object): Object
- // Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut.
- extend: function (dest) {
- var i, j, len, src;
- for (j = 1, len = arguments.length; j < len; j++) {
- src = arguments[j];
- for (i in src) {
- dest[i] = src[i];
- }
- }
- return dest;
- },
- // @function create(proto: Object, properties?: Object): Object
- // Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create)
- create: Object.create || (function () {
- function F() {}
- return function (proto) {
- F.prototype = proto;
- return new F();
- };
- })(),
- // @function bind(fn: Function, …): Function
- // Returns a new function bound to the arguments passed, like [Function.prototype.bind](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Function/bind).
- // Has a `L.bind()` shortcut.
- bind: function (fn, obj) {
- var slice = Array.prototype.slice;
- if (fn.bind) {
- return fn.bind.apply(fn, slice.call(arguments, 1));
- }
- var args = slice.call(arguments, 2);
- return function () {
- return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments);
- };
- },
- // @function stamp(obj: Object): Number
- // Returns the unique ID of an object, assiging it one if it doesn't have it.
- stamp: function (obj) {
- /*eslint-disable */
- obj._leaflet_id = obj._leaflet_id || ++L.Util.lastId;
- return obj._leaflet_id;
- /*eslint-enable */
- },
- // @property lastId: Number
- // Last unique ID used by [`stamp()`](#util-stamp)
- lastId: 0,
- // @function throttle(fn: Function, time: Number, context: Object): Function
- // Returns a function which executes function `fn` with the given scope `context`
- // (so that the `this` keyword refers to `context` inside `fn`'s code). The function
- // `fn` will be called no more than one time per given amount of `time`. The arguments
- // received by the bound function will be any arguments passed when binding the
- // function, followed by any arguments passed when invoking the bound function.
- // Has an `L.bind` shortcut.
- throttle: function (fn, time, context) {
- var lock, args, wrapperFn, later;
- later = function () {
- // reset lock and call if queued
- lock = false;
- if (args) {
- wrapperFn.apply(context, args);
- args = false;
- }
- };
- wrapperFn = function () {
- if (lock) {
- // called too soon, queue to call later
- args = arguments;
- } else {
- // call and lock until later
- fn.apply(context, arguments);
- setTimeout(later, time);
- lock = true;
- }
- };
- return wrapperFn;
- },
- // @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number
- // Returns the number `num` modulo `range` in such a way so it lies within
- // `range[0]` and `range[1]`. The returned value will be always smaller than
- // `range[1]` unless `includeMax` is set to `true`.
- wrapNum: function (x, range, includeMax) {
- var max = range[1],
- min = range[0],
- d = max - min;
- return x === max && includeMax ? x : ((x - min) % d + d) % d + min;
- },
- // @function falseFn(): Function
- // Returns a function which always returns `false`.
- falseFn: function () { return false; },
- // @function formatNum(num: Number, digits?: Number): Number
- // Returns the number `num` rounded to `digits` decimals, or to 5 decimals by default.
- formatNum: function (num, digits) {
- var pow = Math.pow(10, digits || 5);
- return Math.round(num * pow) / pow;
- },
- // @function trim(str: String): String
- // Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)
- trim: function (str) {
- return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
- },
- // @function splitWords(str: String): String[]
- // Trims and splits the string on whitespace and returns the array of parts.
- splitWords: function (str) {
- return L.Util.trim(str).split(/\s+/);
- },
- // @function setOptions(obj: Object, options: Object): Object
- // Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut.
- setOptions: function (obj, options) {
- if (!obj.hasOwnProperty('options')) {
- obj.options = obj.options ? L.Util.create(obj.options) : {};
- }
- for (var i in options) {
- obj.options[i] = options[i];
- }
- return obj.options;
- },
- // @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String
- // Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}`
- // translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will
- // be appended at the end. If `uppercase` is `true`, the parameter names will
- // be uppercased (e.g. `'?A=foo&B=bar'`)
- getParamString: function (obj, existingUrl, uppercase) {
- var params = [];
- for (var i in obj) {
- params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
- }
- return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
- },
- // @function template(str: String, data: Object): String
- // Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'`
- // and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string
- // `('Hello foo, bar')`. You can also specify functions instead of strings for
- // data values — they will be evaluated passing `data` as an argument.
- template: function (str, data) {
- return str.replace(L.Util.templateRe, function (str, key) {
- var value = data[key];
- if (value === undefined) {
- throw new Error('No value provided for variable ' + str);
- } else if (typeof value === 'function') {
- value = value(data);
- }
- return value;
- });
- },
- templateRe: /\{ *([\w_\-]+) *\}/g,
- // @function isArray(obj): Boolean
- // Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray)
- isArray: Array.isArray || function (obj) {
- return (Object.prototype.toString.call(obj) === '[object Array]');
- },
- // @function indexOf(array: Array, el: Object): Number
- // Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
- indexOf: function (array, el) {
- for (var i = 0; i < array.length; i++) {
- if (array[i] === el) { return i; }
- }
- return -1;
- },
- // @property emptyImageUrl: String
- // Data URI string containing a base64-encoded empty GIF image.
- // Used as a hack to free memory from unused images on WebKit-powered
- // mobile devices (by setting image `src` to this string).
- emptyImageUrl: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='
- };
- (function () {
- // inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/
- function getPrefixed(name) {
- return window['webkit' + name] || window['moz' + name] || window['ms' + name];
- }
- var lastTime = 0;
- // fallback for IE 7-8
- function timeoutDefer(fn) {
- var time = +new Date(),
- timeToCall = Math.max(0, 16 - (time - lastTime));
- lastTime = time + timeToCall;
- return window.setTimeout(fn, timeToCall);
- }
- var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer,
- cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') ||
- getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); };
- // @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number
- // Schedules `fn` to be executed when the browser repaints. `fn` is bound to
- // `context` if given. When `immediate` is set, `fn` is called immediately if
- // the browser doesn't have native support for
- // [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame),
- // otherwise it's delayed. Returns a request ID that can be used to cancel the request.
- L.Util.requestAnimFrame = function (fn, context, immediate) {
- if (immediate && requestFn === timeoutDefer) {
- fn.call(context);
- } else {
- return requestFn.call(window, L.bind(fn, context));
- }
- };
- // @function cancelAnimFrame(id: Number): undefined
- // Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame).
- L.Util.cancelAnimFrame = function (id) {
- if (id) {
- cancelFn.call(window, id);
- }
- };
- })();
- // shortcuts for most used utility functions
- L.extend = L.Util.extend;
- L.bind = L.Util.bind;
- L.stamp = L.Util.stamp;
- L.setOptions = L.Util.setOptions;
- // @class Class
- // @aka L.Class
- // @section
- // @uninheritable
- // Thanks to John Resig and Dean Edwards for inspiration!
- L.Class = function () {};
- L.Class.extend = function (props) {
- // @function extend(props: Object): Function
- // [Extends the current class](#class-inheritance) given the properties to be included.
- // Returns a Javascript function that is a class constructor (to be called with `new`).
- var NewClass = function () {
- // call the constructor
- if (this.initialize) {
- this.initialize.apply(this, arguments);
- }
- // call all constructor hooks
- this.callInitHooks();
- };
- var parentProto = NewClass.__super__ = this.prototype;
- var proto = L.Util.create(parentProto);
- proto.constructor = NewClass;
- NewClass.prototype = proto;
- // inherit parent's statics
- for (var i in this) {
- if (this.hasOwnProperty(i) && i !== 'prototype') {
- NewClass[i] = this[i];
- }
- }
- // mix static properties into the class
- if (props.statics) {
- L.extend(NewClass, props.statics);
- delete props.statics;
- }
- // mix includes into the prototype
- if (props.includes) {
- L.Util.extend.apply(null, [proto].concat(props.includes));
- delete props.includes;
- }
- // merge options
- if (proto.options) {
- props.options = L.Util.extend(L.Util.create(proto.options), props.options);
- }
- // mix given properties into the prototype
- L.extend(proto, props);
- proto._initHooks = [];
- // add method for calling all hooks
- proto.callInitHooks = function () {
- if (this._initHooksCalled) { return; }
- if (parentProto.callInitHooks) {
- parentProto.callInitHooks.call(this);
- }
- this._initHooksCalled = true;
- for (var i = 0, len = proto._initHooks.length; i < len; i++) {
- proto._initHooks[i].call(this);
- }
- };
- return NewClass;
- };
- // @function include(properties: Object): this
- // [Includes a mixin](#class-includes) into the current class.
- L.Class.include = function (props) {
- L.extend(this.prototype, props);
- return this;
- };
- // @function mergeOptions(options: Object): this
- // [Merges `options`](#class-options) into the defaults of the class.
- L.Class.mergeOptions = function (options) {
- L.extend(this.prototype.options, options);
- return this;
- };
- // @function addInitHook(fn: Function): this
- // Adds a [constructor hook](#class-constructor-hooks) to the class.
- L.Class.addInitHook = function (fn) { // (Function) || (String, args...)
- var args = Array.prototype.slice.call(arguments, 1);
- var init = typeof fn === 'function' ? fn : function () {
- this[fn].apply(this, args);
- };
- this.prototype._initHooks = this.prototype._initHooks || [];
- this.prototype._initHooks.push(init);
- return this;
- };
- /*
- * @class Evented
- * @aka L.Evented
- * @inherits Class
- *
- * A set of methods shared between event-powered classes (like `Map` and `Marker`). Generally, events allow you to execute some function when something happens with an object (e.g. the user clicks on the map, causing the map to fire `'click'` event).
- *
- * @example
- *
- * ```js
- * map.on('click', function(e) {
- * alert(e.latlng);
- * } );
- * ```
- *
- * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function:
- *
- * ```js
- * function onClick(e) { ... }
- *
- * map.on('click', onClick);
- * map.off('click', onClick);
- * ```
- */
- L.Evented = L.Class.extend({
- /* @method on(type: String, fn: Function, context?: Object): this
- * Adds a listener function (`fn`) to a particular event type of the object. You can optionally specify the context of the listener (object the this keyword will point to). You can also pass several space-separated types (e.g. `'click dblclick'`).
- *
- * @alternative
- * @method on(eventMap: Object): this
- * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
- */
- on: function (types, fn, context) {
- // types can be a map of types/handlers
- if (typeof types === 'object') {
- for (var type in types) {
- // we don't process space-separated events here for performance;
- // it's a hot path since Layer uses the on(obj) syntax
- this._on(type, types[type], fn);
- }
- } else {
- // types can be a string of space-separated words
- types = L.Util.splitWords(types);
- for (var i = 0, len = types.length; i < len; i++) {
- this._on(types[i], fn, context);
- }
- }
- return this;
- },
- /* @method off(type: String, fn?: Function, context?: Object): this
- * Removes a previously added listener function. If no function is specified, it will remove all the listeners of that particular event from the object. Note that if you passed a custom context to `on`, you must pass the same context to `off` in order to remove the listener.
- *
- * @alternative
- * @method off(eventMap: Object): this
- * Removes a set of type/listener pairs.
- *
- * @alternative
- * @method off: this
- * Removes all listeners to all events on the object.
- */
- off: function (types, fn, context) {
- if (!types) {
- // clear all listeners if called without arguments
- delete this._events;
- } else if (typeof types === 'object') {
- for (var type in types) {
- this._off(type, types[type], fn);
- }
- } else {
- types = L.Util.splitWords(types);
- for (var i = 0, len = types.length; i < len; i++) {
- this._off(types[i], fn, context);
- }
- }
- return this;
- },
- // attach listener (without syntactic sugar now)
- _on: function (type, fn, context) {
- this._events = this._events || {};
- /* get/init listeners for type */
- var typeListeners = this._events[type];
- if (!typeListeners) {
- typeListeners = [];
- this._events[type] = typeListeners;
- }
- if (context === this) {
- // Less memory footprint.
- context = undefined;
- }
- var newListener = {fn: fn, ctx: context},
- listeners = typeListeners;
- // check if fn already there
- for (var i = 0, len = listeners.length; i < len; i++) {
- if (listeners[i].fn === fn && listeners[i].ctx === context) {
- return;
- }
- }
- listeners.push(newListener);
- typeListeners.count++;
- },
- _off: function (type, fn, context) {
- var listeners,
- i,
- len;
- if (!this._events) { return; }
- listeners = this._events[type];
- if (!listeners) {
- return;
- }
- if (!fn) {
- // Set all removed listeners to noop so they are not called if remove happens in fire
- for (i = 0, len = listeners.length; i < len; i++) {
- listeners[i].fn = L.Util.falseFn;
- }
- // clear all listeners for a type if function isn't specified
- delete this._events[type];
- return;
- }
- if (context === this) {
- context = undefined;
- }
- if (listeners) {
- // find fn and remove it
- for (i = 0, len = listeners.length; i < len; i++) {
- var l = listeners[i];
- if (l.ctx !== context) { continue; }
- if (l.fn === fn) {
- // set the removed listener to noop so that's not called if remove happens in fire
- l.fn = L.Util.falseFn;
- if (this._firingCount) {
- /* copy array in case events are being fired */
- this._events[type] = listeners = listeners.slice();
- }
- listeners.splice(i, 1);
- return;
- }
- }
- }
- },
- // @method fire(type: String, data?: Object, propagate?: Boolean): this
- // Fires an event of the specified type. You can optionally provide an data
- // object — the first argument of the listener function will contain its
- // properties. The event can optionally be propagated to event parents.
- fire: function (type, data, propagate) {
- if (!this.listens(type, propagate)) { return this; }
- var event = L.Util.extend({}, data, {type: type, target: this});
- if (this._events) {
- var listeners = this._events[type];
- if (listeners) {
- this._firingCount = (this._firingCount + 1) || 1;
- for (var i = 0, len = listeners.length; i < len; i++) {
- var l = listeners[i];
- l.fn.call(l.ctx || this, event);
- }
- this._firingCount--;
- }
- }
- if (propagate) {
- // propagate the event to parents (set with addEventParent)
- this._propagateEvent(event);
- }
- return this;
- },
- // @method listens(type: String): Boolean
- // Returns `true` if a particular event type has any listeners attached to it.
- listens: function (type, propagate) {
- var listeners = this._events && this._events[type];
- if (listeners && listeners.length) { return true; }
- if (propagate) {
- // also check parents for listeners if event propagates
- for (var id in this._eventParents) {
- if (this._eventParents[id].listens(type, propagate)) { return true; }
- }
- }
- return false;
- },
- // @method once(…): this
- // Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed.
- once: function (types, fn, context) {
- if (typeof types === 'object') {
- for (var type in types) {
- this.once(type, types[type], fn);
- }
- return this;
- }
- var handler = L.bind(function () {
- this
- .off(types, fn, context)
- .off(types, handler, context);
- }, this);
- // add a listener that's executed once and removed after that
- return this
- .on(types, fn, context)
- .on(types, handler, context);
- },
- // @method addEventParent(obj: Evented): this
- // Adds an event parent - an `Evented` that will receive propagated events
- addEventParent: function (obj) {
- this._eventParents = this._eventParents || {};
- this._eventParents[L.stamp(obj)] = obj;
- return this;
- },
- // @method removeEventParent(obj: Evented): this
- // Removes an event parent, so it will stop receiving propagated events
- removeEventParent: function (obj) {
- if (this._eventParents) {
- delete this._eventParents[L.stamp(obj)];
- }
- return this;
- },
- _propagateEvent: function (e) {
- for (var id in this._eventParents) {
- this._eventParents[id].fire(e.type, L.extend({layer: e.target}, e), true);
- }
- }
- });
- var proto = L.Evented.prototype;
- // aliases; we should ditch those eventually
- // @method addEventListener(…): this
- // Alias to [`on(…)`](#evented-on)
- proto.addEventListener = proto.on;
- // @method removeEventListener(…): this
- // Alias to [`off(…)`](#evented-off)
- // @method clearAllEventListeners(…): this
- // Alias to [`off()`](#evented-off)
- proto.removeEventListener = proto.clearAllEventListeners = proto.off;
- // @method addOneTimeEventListener(…): this
- // Alias to [`once(…)`](#evented-once)
- proto.addOneTimeEventListener = proto.once;
- // @method fireEvent(…): this
- // Alias to [`fire(…)`](#evented-fire)
- proto.fireEvent = proto.fire;
- // @method hasEventListeners(…): Boolean
- // Alias to [`listens(…)`](#evented-listens)
- proto.hasEventListeners = proto.listens;
- L.Mixin = {Events: proto};
- /*
- * @namespace Browser
- * @aka L.Browser
- *
- * A namespace with static properties for browser/feature detection used by Leaflet internally.
- *
- * @example
- *
- * ```js
- * if (L.Browser.ielt9) {
- * alert('Upgrade your browser, dude!');
- * }
- * ```
- */
- (function () {
- var ua = navigator.userAgent.toLowerCase(),
- doc = document.documentElement,
- ie = 'ActiveXObject' in window,
- webkit = ua.indexOf('webkit') !== -1,
- phantomjs = ua.indexOf('phantom') !== -1,
- android23 = ua.search('android [23]') !== -1,
- chrome = ua.indexOf('chrome') !== -1,
- gecko = ua.indexOf('gecko') !== -1 && !webkit && !window.opera && !ie,
- win = navigator.platform.indexOf('Win') === 0,
- mobile = typeof orientation !== 'undefined' || ua.indexOf('mobile') !== -1,
- msPointer = !window.PointerEvent && window.MSPointerEvent,
- pointer = window.PointerEvent || msPointer,
- ie3d = ie && ('transition' in doc.style),
- webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23,
- gecko3d = 'MozPerspective' in doc.style,
- opera12 = 'OTransition' in doc.style;
- var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window ||
- (window.DocumentTouch && document instanceof window.DocumentTouch));
- L.Browser = {
- // @property ie: Boolean
- // `true` for all Internet Explorer versions (not Edge).
- ie: ie,
- // @property ielt9: Boolean
- // `true` for Internet Explorer versions less than 9.
- ielt9: ie && !document.addEventListener,
- // @property edge: Boolean
- // `true` for the Edge web browser.
- edge: 'msLaunchUri' in navigator && !('documentMode' in document),
- // @property webkit: Boolean
- // `true` for webkit-based browsers like Chrome and Safari (including mobile versions).
- webkit: webkit,
- // @property gecko: Boolean
- // `true` for gecko-based browsers like Firefox.
- gecko: gecko,
- // @property android: Boolean
- // `true` for any browser running on an Android platform.
- android: ua.indexOf('android') !== -1,
- // @property android23: Boolean
- // `true` for browsers running on Android 2 or Android 3.
- android23: android23,
- // @property chrome: Boolean
- // `true` for the Chrome browser.
- chrome: chrome,
- // @property safari: Boolean
- // `true` for the Safari browser.
- safari: !chrome && ua.indexOf('safari') !== -1,
- // @property win: Boolean
- // `true` when the browser is running in a Windows platform
- win: win,
- // @property ie3d: Boolean
- // `true` for all Internet Explorer versions supporting CSS transforms.
- ie3d: ie3d,
- // @property webkit3d: Boolean
- // `true` for webkit-based browsers supporting CSS transforms.
- webkit3d: webkit3d,
- // @property gecko3d: Boolean
- // `true` for gecko-based browsers supporting CSS transforms.
- gecko3d: gecko3d,
- // @property opera12: Boolean
- // `true` for the Opera browser supporting CSS transforms (version 12 or later).
- opera12: opera12,
- // @property any3d: Boolean
- // `true` for all browsers supporting CSS transforms.
- any3d: !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantomjs,
- // @property mobile: Boolean
- // `true` for all browsers running in a mobile device.
- mobile: mobile,
- // @property mobileWebkit: Boolean
- // `true` for all webkit-based browsers in a mobile device.
- mobileWebkit: mobile && webkit,
- // @property mobileWebkit3d: Boolean
- // `true` for all webkit-based browsers in a mobile device supporting CSS transforms.
- mobileWebkit3d: mobile && webkit3d,
- // @property mobileOpera: Boolean
- // `true` for the Opera browser in a mobile device.
- mobileOpera: mobile && window.opera,
- // @property mobileGecko: Boolean
- // `true` for gecko-based browsers running in a mobile device.
- mobileGecko: mobile && gecko,
- // @property touch: Boolean
- // `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events).
- touch: !!touch,
- // @property msPointer: Boolean
- // `true` for browsers implementing the Microsoft touch events model (notably IE10).
- msPointer: !!msPointer,
- // @property pointer: Boolean
- // `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx).
- pointer: !!pointer,
- // @property retina: Boolean
- // `true` for browsers on a high-resolution "retina" screen.
- retina: (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1
- };
- }());
- /*
- * @class Point
- * @aka L.Point
- *
- * Represents a point with `x` and `y` coordinates in pixels.
- *
- * @example
- *
- * ```js
- * var point = L.point(200, 300);
- * ```
- *
- * All Leaflet methods and options that accept `Point` objects also accept them in a simple Array form (unless noted otherwise), so these lines are equivalent:
- *
- * ```js
- * map.panBy([200, 300]);
- * map.panBy(L.point(200, 300));
- * ```
- */
- L.Point = function (x, y, round) {
- // @property x: Number; The `x` coordinate of the point
- this.x = (round ? Math.round(x) : x);
- // @property y: Number; The `y` coordinate of the point
- this.y = (round ? Math.round(y) : y);
- };
- L.Point.prototype = {
- // @method clone(): Point
- // Returns a copy of the current point.
- clone: function () {
- return new L.Point(this.x, this.y);
- },
- // @method add(otherPoint: Point): Point
- // Returns the result of addition of the current and the given points.
- add: function (point) {
- // non-destructive, returns a new point
- return this.clone()._add(L.point(point));
- },
- _add: function (point) {
- // destructive, used directly for performance in situations where it's safe to modify existing point
- this.x += point.x;
- this.y += point.y;
- return this;
- },
- // @method subtract(otherPoint: Point): Point
- // Returns the result of subtraction of the given point from the current.
- subtract: function (point) {
- return this.clone()._subtract(L.point(point));
- },
- _subtract: function (point) {
- this.x -= point.x;
- this.y -= point.y;
- return this;
- },
- // @method divideBy(num: Number): Point
- // Returns the result of division of the current point by the given number.
- divideBy: function (num) {
- return this.clone()._divideBy(num);
- },
- _divideBy: function (num) {
- this.x /= num;
- this.y /= num;
- return this;
- },
- // @method multiplyBy(num: Number): Point
- // Returns the result of multiplication of the current point by the given number.
- multiplyBy: function (num) {
- return this.clone()._multiplyBy(num);
- },
- _multiplyBy: function (num) {
- this.x *= num;
- this.y *= num;
- return this;
- },
- // @method scaleBy(scale: Point): Point
- // Multiply each coordinate of the current point by each coordinate of
- // `scale`. In linear algebra terms, multiply the point by the
- // [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation)
- // defined by `scale`.
- scaleBy: function (point) {
- return new L.Point(this.x * point.x, this.y * point.y);
- },
- // @method unscaleBy(scale: Point): Point
- // Inverse of `scaleBy`. Divide each coordinate of the current point by
- // each coordinate of `scale`.
- unscaleBy: function (point) {
- return new L.Point(this.x / point.x, this.y / point.y);
- },
- // @method round(): Point
- // Returns a copy of the current point with rounded coordinates.
- round: function () {
- return this.clone()._round();
- },
- _round: function () {
- this.x = Math.round(this.x);
- this.y = Math.round(this.y);
- return this;
- },
- // @method floor(): Point
- // Returns a copy of the current point with floored coordinates (rounded down).
- floor: function () {
- return this.clone()._floor();
- },
- _floor: function () {
- this.x = Math.floor(this.x);
- this.y = Math.floor(this.y);
- return this;
- },
- // @method ceil(): Point
- // Returns a copy of the current point with ceiled coordinates (rounded up).
- ceil: function () {
- return this.clone()._ceil();
- },
- _ceil: function () {
- this.x = Math.ceil(this.x);
- this.y = Math.ceil(this.y);
- return this;
- },
- // @method distanceTo(otherPoint: Point): Number
- // Returns the cartesian distance between the current and the given points.
- distanceTo: function (point) {
- point = L.point(point);
- var x = point.x - this.x,
- y = point.y - this.y;
- return Math.sqrt(x * x + y * y);
- },
- // @method equals(otherPoint: Point): Boolean
- // Returns `true` if the given point has the same coordinates.
- equals: function (point) {
- point = L.point(point);
- return point.x === this.x &&
- point.y === this.y;
- },
- // @method contains(otherPoint: Point): Boolean
- // Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values).
- contains: function (point) {
- point = L.point(point);
- return Math.abs(point.x) <= Math.abs(this.x) &&
- Math.abs(point.y) <= Math.abs(this.y);
- },
- // @method toString(): String
- // Returns a string representation of the point for debugging purposes.
- toString: function () {
- return 'Point(' +
- L.Util.formatNum(this.x) + ', ' +
- L.Util.formatNum(this.y) + ')';
- }
- };
- // @factory L.point(x: Number, y: Number, round?: Boolean)
- // Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values.
- // @alternative
- // @factory L.point(coords: Number[])
- // Expects an array of the form `[x, y]` instead.
- // @alternative
- // @factory L.point(coords: Object)
- // Expects a plain object of the form `{x: Number, y: Number}` instead.
- L.point = function (x, y, round) {
- if (x instanceof L.Point) {
- return x;
- }
- if (L.Util.isArray(x)) {
- return new L.Point(x[0], x[1]);
- }
- if (x === undefined || x === null) {
- return x;
- }
- if (typeof x === 'object' && 'x' in x && 'y' in x) {
- return new L.Point(x.x, x.y);
- }
- return new L.Point(x, y, round);
- };
- /*
- * @class Bounds
- * @aka L.Bounds
- *
- * Represents a rectangular area in pixel coordinates.
- *
- * @example
- *
- * ```js
- * var p1 = L.point(10, 10),
- * p2 = L.point(40, 60),
- * bounds = L.bounds(p1, p2);
- * ```
- *
- * All Leaflet methods that accept `Bounds` objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this:
- *
- * ```js
- * otherBounds.intersects([[10, 10], [40, 60]]);
- * ```
- */
- L.Bounds = function (a, b) {
- if (!a) { return; }
- var points = b ? [a, b] : a;
- for (var i = 0, len = points.length; i < len; i++) {
- this.extend(points[i]);
- }
- };
- L.Bounds.prototype = {
- // @method extend(point: Point): this
- // Extends the bounds to contain the given point.
- extend: function (point) { // (Point)
- point = L.point(point);
- // @property min: Point
- // The top left corner of the rectangle.
- // @property max: Point
- // The bottom right corner of the rectangle.
- if (!this.min && !this.max) {
- this.min = point.clone();
- this.max = point.clone();
- } else {
- this.min.x = Math.min(point.x, this.min.x);
- this.max.x = Math.max(point.x, this.max.x);
- this.min.y = Math.min(point.y, this.min.y);
- this.max.y = Math.max(point.y, this.max.y);
- }
- return this;
- },
- // @method getCenter(round?: Boolean): Point
- // Returns the center point of the bounds.
- getCenter: function (round) {
- return new L.Point(
- (this.min.x + this.max.x) / 2,
- (this.min.y + this.max.y) / 2, round);
- },
- // @method getBottomLeft(): Point
- // Returns the bottom-left point of the bounds.
- getBottomLeft: function () {
- return new L.Point(this.min.x, this.max.y);
- },
- // @method getTopRight(): Point
- // Returns the top-right point of the bounds.
- getTopRight: function () { // -> Point
- return new L.Point(this.max.x, this.min.y);
- },
- // @method getSize(): Point
- // Returns the size of the given bounds
- getSize: function () {
- return this.max.subtract(this.min);
- },
- // @method contains(otherBounds: Bounds): Boolean
- // Returns `true` if the rectangle contains the given one.
- // @alternative
- // @method contains(point: Point): Boolean
- // Returns `true` if the rectangle contains the given point.
- contains: function (obj) {
- var min, max;
- if (typeof obj[0] === 'number' || obj instanceof L.Point) {
- obj = L.point(obj);
- } else {
- obj = L.bounds(obj);
- }
- if (obj instanceof L.Bounds) {
- min = obj.min;
- max = obj.max;
- } else {
- min = max = obj;
- }
- return (min.x >= this.min.x) &&
- (max.x <= this.max.x) &&
- (min.y >= this.min.y) &&
- (max.y <= this.max.y);
- },
- // @method intersects(otherBounds: Bounds): Boolean
- // Returns `true` if the rectangle intersects the given bounds. Two bounds
- // intersect if they have at least one point in common.
- intersects: function (bounds) { // (Bounds) -> Boolean
- bounds = L.bounds(bounds);
- var min = this.min,
- max = this.max,
- min2 = bounds.min,
- max2 = bounds.max,
- xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
- yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
- return xIntersects && yIntersects;
- },
- // @method overlaps(otherBounds: Bounds): Boolean
- // Returns `true` if the rectangle overlaps the given bounds. Two bounds
- // overlap if their intersection is an area.
- overlaps: function (bounds) { // (Bounds) -> Boolean
- bounds = L.bounds(bounds);
- var min = this.min,
- max = this.max,
- min2 = bounds.min,
- max2 = bounds.max,
- xOverlaps = (max2.x > min.x) && (min2.x < max.x),
- yOverlaps = (max2.y > min.y) && (min2.y < max.y);
- return xOverlaps && yOverlaps;
- },
- isValid: function () {
- return !!(this.min && this.max);
- }
- };
- // @factory L.bounds(topLeft: Point, bottomRight: Point)
- // Creates a Bounds object from two coordinates (usually top-left and bottom-right corners).
- // @alternative
- // @factory L.bounds(points: Point[])
- // Creates a Bounds object from the points it contains
- L.bounds = function (a, b) {
- if (!a || a instanceof L.Bounds) {
- return a;
- }
- return new L.Bounds(a, b);
- };
- /*
- * @class Transformation
- * @aka L.Transformation
- *
- * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d`
- * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing
- * the reverse. Used by Leaflet in its projections code.
- *
- * @example
- *
- * ```js
- * var transformation = new L.Transformation(2, 5, -1, 10),
- * p = L.point(1, 2),
- * p2 = transformation.transform(p), // L.point(7, 8)
- * p3 = transformation.untransform(p2); // L.point(1, 2)
- * ```
- */
- // factory new L.Transformation(a: Number, b: Number, c: Number, d: Number)
- // Creates a `Transformation` object with the given coefficients.
- L.Transformation = function (a, b, c, d) {
- this._a = a;
- this._b = b;
- this._c = c;
- this._d = d;
- };
- L.Transformation.prototype = {
- // @method transform(point: Point, scale?: Number): Point
- // Returns a transformed point, optionally multiplied by the given scale.
- // Only accepts actual `L.Point` instances, not arrays.
- transform: function (point, scale) { // (Point, Number) -> Point
- return this._transform(point.clone(), scale);
- },
- // destructive transform (faster)
- _transform: function (point, scale) {
- scale = scale || 1;
- point.x = scale * (this._a * point.x + this._b);
- point.y = scale * (this._c * point.y + this._d);
- return point;
- },
- // @method untransform(point: Point, scale?: Number): Point
- // Returns the reverse transformation of the given point, optionally divided
- // by the given scale. Only accepts actual `L.Point` instances, not arrays.
- untransform: function (point, scale) {
- scale = scale || 1;
- return new L.Point(
- (point.x / scale - this._b) / this._a,
- (point.y / scale - this._d) / this._c);
- }
- };
- /*
- * @namespace DomUtil
- *
- * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
- * tree, used by Leaflet internally.
- *
- * Most functions expecting or returning a `HTMLElement` also work for
- * SVG elements. The only difference is that classes refer to CSS classes
- * in HTML and SVG classes in SVG.
- */
- L.DomUtil = {
- // @function get(id: String|HTMLElement): HTMLElement
- // Returns an element given its DOM id, or returns the element itself
- // if it was passed directly.
- get: function (id) {
- return typeof id === 'string' ? document.getElementById(id) : id;
- },
- // @function getStyle(el: HTMLElement, styleAttrib: String): String
- // Returns the value for a certain style attribute on an element,
- // including computed values or values set through CSS.
- getStyle: function (el, style) {
- var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
- if ((!value || value === 'auto') && document.defaultView) {
- var css = document.defaultView.getComputedStyle(el, null);
- value = css ? css[style] : null;
- }
- return value === 'auto' ? null : value;
- },
- // @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
- // Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
- create: function (tagName, className, container) {
- var el = document.createElement(tagName);
- el.className = className || '';
- if (container) {
- container.appendChild(el);
- }
- return el;
- },
- // @function remove(el: HTMLElement)
- // Removes `el` from its parent element
- remove: function (el) {
- var parent = el.parentNode;
- if (parent) {
- parent.removeChild(el);
- }
- },
- // @function empty(el: HTMLElement)
- // Removes all of `el`'s children elements from `el`
- empty: function (el) {
- while (el.firstChild) {
- el.removeChild(el.firstChild);
- }
- },
- // @function toFront(el: HTMLElement)
- // Makes `el` the last children of its parent, so it renders in front of the other children.
- toFront: function (el) {
- el.parentNode.appendChild(el);
- },
- // @function toBack(el: HTMLElement)
- // Makes `el` the first children of its parent, so it renders back from the other children.
- toBack: function (el) {
- var parent = el.parentNode;
- parent.insertBefore(el, parent.firstChild);
- },
- // @function hasClass(el: HTMLElement, name: String): Boolean
- // Returns `true` if the element's class attribute contains `name`.
- hasClass: function (el, name) {
- if (el.classList !== undefined) {
- return el.classList.contains(name);
- }
- var className = L.DomUtil.getClass(el);
- return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
- },
- // @function addClass(el: HTMLElement, name: String)
- // Adds `name` to the element's class attribute.
- addClass: function (el, name) {
- if (el.classList !== undefined) {
- var classes = L.Util.splitWords(name);
- for (var i = 0, len = classes.length; i < len; i++) {
- el.classList.add(classes[i]);
- }
- } else if (!L.DomUtil.hasClass(el, name)) {
- var className = L.DomUtil.getClass(el);
- L.DomUtil.setClass(el, (className ? className + ' ' : '') + name);
- }
- },
- // @function removeClass(el: HTMLElement, name: String)
- // Removes `name` from the element's class attribute.
- removeClass: function (el, name) {
- if (el.classList !== undefined) {
- el.classList.remove(name);
- } else {
- L.DomUtil.setClass(el, L.Util.trim((' ' + L.DomUtil.getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
- }
- },
- // @function setClass(el: HTMLElement, name: String)
- // Sets the element's class.
- setClass: function (el, name) {
- if (el.className.baseVal === undefined) {
- el.className = name;
- } else {
- // in case of SVG element
- el.className.baseVal = name;
- }
- },
- // @function getClass(el: HTMLElement): String
- // Returns the element's class.
- getClass: function (el) {
- return el.className.baseVal === undefined ? el.className : el.className.baseVal;
- },
- // @function setOpacity(el: HTMLElement, opacity: Number)
- // Set the opacity of an element (including old IE support).
- // `opacity` must be a number from `0` to `1`.
- setOpacity: function (el, value) {
- if ('opacity' in el.style) {
- el.style.opacity = value;
- } else if ('filter' in el.style) {
- L.DomUtil._setOpacityIE(el, value);
- }
- },
- _setOpacityIE: function (el, value) {
- var filter = false,
- filterName = 'DXImageTransform.Microsoft.Alpha';
- // filters collection throws an error if we try to retrieve a filter that doesn't exist
- try {
- filter = el.filters.item(filterName);
- } catch (e) {
- // don't set opacity to 1 if we haven't already set an opacity,
- // it isn't needed and breaks transparent pngs.
- if (value === 1) { return; }
- }
- value = Math.round(value * 100);
- if (filter) {
- filter.Enabled = (value !== 100);
- filter.Opacity = value;
- } else {
- el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
- }
- },
- // @function testProp(props: String[]): String|false
- // Goes through the array of style names and returns the first name
- // that is a valid style name for an element. If no such name is found,
- // it returns false. Useful for vendor-prefixed styles like `transform`.
- testProp: function (props) {
- var style = document.documentElement.style;
- for (var i = 0; i < props.length; i++) {
- if (props[i] in style) {
- return props[i];
- }
- }
- return false;
- },
- // @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
- // Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
- // and optionally scaled by `scale`. Does not have an effect if the
- // browser doesn't support 3D CSS transforms.
- setTransform: function (el, offset, scale) {
- var pos = offset || new L.Point(0, 0);
- el.style[L.DomUtil.TRANSFORM] =
- (L.Browser.ie3d ?
- 'translate(' + pos.x + 'px,' + pos.y + 'px)' :
- 'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
- (scale ? ' scale(' + scale + ')' : '');
- },
- // @function setPosition(el: HTMLElement, position: Point)
- // Sets the position of `el` to coordinates specified by `position`,
- // using CSS translate or top/left positioning depending on the browser
- // (used by Leaflet internally to position its layers).
- setPosition: function (el, point) { // (HTMLElement, Point[, Boolean])
- /*eslint-disable */
- el._leaflet_pos = point;
- /*eslint-enable */
- if (L.Browser.any3d) {
- L.DomUtil.setTransform(el, point);
- } else {
- el.style.left = point.x + 'px';
- el.style.top = point.y + 'px';
- }
- },
- // @function getPosition(el: HTMLElement): Point
- // Returns the coordinates of an element previously positioned with setPosition.
- getPosition: function (el) {
- // this method is only used for elements previously positioned using setPosition,
- // so it's safe to cache the position for performance
- return el._leaflet_pos || new L.Point(0, 0);
- }
- };
- (function () {
- // prefix style property names
- // @property TRANSFORM: String
- // Vendor-prefixed fransform style name (e.g. `'webkitTransform'` for WebKit).
- L.DomUtil.TRANSFORM = L.DomUtil.testProp(
- ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
- // webkitTransition comes first because some browser versions that drop vendor prefix don't do
- // the same for the transitionend event, in particular the Android 4.1 stock browser
- // @property TRANSITION: String
- // Vendor-prefixed transform style name.
- var transition = L.DomUtil.TRANSITION = L.DomUtil.testProp(
- ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
- L.DomUtil.TRANSITION_END =
- transition === 'webkitTransition' || transition === 'OTransition' ? transition + 'End' : 'transitionend';
- // @function disableTextSelection()
- // Prevents the user from generating `selectstart` DOM events, usually generated
- // when the user drags the mouse through a page with text. Used internally
- // by Leaflet to override the behaviour of any click-and-drag interaction on
- // the map. Affects drag interactions on the whole document.
- // @function enableTextSelection()
- // Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
- if ('onselectstart' in document) {
- L.DomUtil.disableTextSelection = function () {
- L.DomEvent.on(window, 'selectstart', L.DomEvent.preventDefault);
- };
- L.DomUtil.enableTextSelection = function () {
- L.DomEvent.off(window, 'selectstart', L.DomEvent.preventDefault);
- };
- } else {
- var userSelectProperty = L.DomUtil.testProp(
- ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
- L.DomUtil.disableTextSelection = function () {
- if (userSelectProperty) {
- var style = document.documentElement.style;
- this._userSelect = style[userSelectProperty];
- style[userSelectProperty] = 'none';
- }
- };
- L.DomUtil.enableTextSelection = function () {
- if (userSelectProperty) {
- document.documentElement.style[userSelectProperty] = this._userSelect;
- delete this._userSelect;
- }
- };
- }
- // @function disableImageDrag()
- // As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
- // for `dragstart` DOM events, usually generated when the user drags an image.
- L.DomUtil.disableImageDrag = function () {
- L.DomEvent.on(window, 'dragstart', L.DomEvent.preventDefault);
- };
- // @function enableImageDrag()
- // Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
- L.DomUtil.enableImageDrag = function () {
- L.DomEvent.off(window, 'dragstart', L.DomEvent.preventDefault);
- };
- // @function preventOutline(el: HTMLElement)
- // Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
- // of the element `el` invisible. Used internally by Leaflet to prevent
- // focusable elements from displaying an outline when the user performs a
- // drag interaction on them.
- L.DomUtil.preventOutline = function (element) {
- while (element.tabIndex === -1) {
- element = element.parentNode;
- }
- if (!element || !element.style) { return; }
- L.DomUtil.restoreOutline();
- this._outlineElement = element;
- this._outlineStyle = element.style.outline;
- element.style.outline = 'none';
- L.DomEvent.on(window, 'keydown', L.DomUtil.restoreOutline, this);
- };
- // @function restoreOutline()
- // Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
- L.DomUtil.restoreOutline = function () {
- if (!this._outlineElement) { return; }
- this._outlineElement.style.outline = this._outlineStyle;
- delete this._outlineElement;
- delete this._outlineStyle;
- L.DomEvent.off(window, 'keydown', L.DomUtil.restoreOutline, this);
- };
- })();
- /* @class LatLng
- * @aka L.LatLng
- *
- * Represents a geographical point with a certain latitude and longitude.
- *
- * @example
- *
- * ```
- * var latlng = L.latLng(50.5, 30.5);
- * ```
- *
- * All Leaflet methods that accept LatLng objects also accept them in a simple Array form and simple object form (unless noted otherwise), so these lines are equivalent:
- *
- * ```
- * map.panTo([50, 30]);
- * map.panTo({lon: 30, lat: 50});
- * map.panTo({lat: 50, lng: 30});
- * map.panTo(L.latLng(50, 30));
- * ```
- */
- L.LatLng = function (lat, lng, alt) {
- if (isNaN(lat) || isNaN(lng)) {
- throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
- }
- // @property lat: Number
- // Latitude in degrees
- this.lat = +lat;
- // @property lng: Number
- // Longitude in degrees
- this.lng = +lng;
- // @property alt: Number
- // Altitude in meters (optional)
- if (alt !== undefined) {
- this.alt = +alt;
- }
- };
- L.LatLng.prototype = {
- // @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean
- // Returns `true` if the given `LatLng` point is at the same position (within a small margin of error). The margin of error can be overriden by setting `maxMargin` to a small number.
- equals: function (obj, maxMargin) {
- if (!obj) { return false; }
- obj = L.latLng(obj);
- var margin = Math.max(
- Math.abs(this.lat - obj.lat),
- Math.abs(this.lng - obj.lng));
- return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin);
- },
- // @method toString(): String
- // Returns a string representation of the point (for debugging purposes).
- toString: function (precision) {
- return 'LatLng(' +
- L.Util.formatNum(this.lat, precision) + ', ' +
- L.Util.formatNum(this.lng, precision) + ')';
- },
- // @method distanceTo(otherLatLng: LatLng): Number
- // Returns the distance (in meters) to the given `LatLng` calculated using the [Haversine formula](http://en.wikipedia.org/wiki/Haversine_formula).
- distanceTo: function (other) {
- return L.CRS.Earth.distance(this, L.latLng(other));
- },
- // @method wrap(): LatLng
- // Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees.
- wrap: function () {
- return L.CRS.Earth.wrapLatLng(this);
- },
- // @method toBounds(sizeInMeters: Number): LatLngBounds
- // Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters` meters apart from the `LatLng`.
- toBounds: function (sizeInMeters) {
- var latAccuracy = 180 * sizeInMeters / 40075017,
- lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat);
- return L.latLngBounds(
- [this.lat - latAccuracy, this.lng - lngAccuracy],
- [this.lat + latAccuracy, this.lng + lngAccuracy]);
- },
- clone: function () {
- return new L.LatLng(this.lat, this.lng, this.alt);
- }
- };
- // @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng
- // Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude).
- // @alternative
- // @factory L.latLng(coords: Array): LatLng
- // Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead.
- // @alternative
- // @factory L.latLng(coords: Object): LatLng
- // Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead.
- L.latLng = function (a, b, c) {
- if (a instanceof L.LatLng) {
- return a;
- }
- if (L.Util.isArray(a) && typeof a[0] !== 'object') {
- if (a.length === 3) {
- return new L.LatLng(a[0], a[1], a[2]);
- }
- if (a.length === 2) {
- return new L.LatLng(a[0], a[1]);
- }
- return null;
- }
- if (a === undefined || a === null) {
- return a;
- }
- if (typeof a === 'object' && 'lat' in a) {
- return new L.LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt);
- }
- if (b === undefined) {
- return null;
- }
- return new L.LatLng(a, b, c);
- };
- /*
- * @class LatLngBounds
- * @aka L.LatLngBounds
- *
- * Represents a rectangular geographical area on a map.
- *
- * @example
- *
- * ```js
- * var corner1 = L.latLng(40.712, -74.227),
- * corner2 = L.latLng(40.774, -74.125),
- * bounds = L.latLngBounds(corner1, corner2);
- * ```
- *
- * All Leaflet methods that accept LatLngBounds objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this:
- *
- * ```js
- * map.fitBounds([
- * [40.712, -74.227],
- * [40.774, -74.125]
- * ]);
- * ```
- *
- * Caution: if the area crosses the antimeridian (often confused with the International Date Line), you must specify corners _outside_ the [-180, 180] degrees longitude range.
- */
- L.LatLngBounds = function (corner1, corner2) { // (LatLng, LatLng) or (LatLng[])
- if (!corner1) { return; }
- var latlngs = corner2 ? [corner1, corner2] : corner1;
- for (var i = 0, len = latlngs.length; i < len; i++) {
- this.extend(latlngs[i]);
- }
- };
- L.LatLngBounds.prototype = {
- // @method extend(latlng: LatLng): this
- // Extend the bounds to contain the given point
- // @alternative
- // @method extend(otherBounds: LatLngBounds): this
- // Extend the bounds to contain the given bounds
- extend: function (obj) {
- var sw = this._southWest,
- ne = this._northEast,
- sw2, ne2;
- if (obj instanceof L.LatLng) {
- sw2 = obj;
- ne2 = obj;
- } else if (obj instanceof L.LatLngBounds) {
- sw2 = obj._southWest;
- ne2 = obj._northEast;
- if (!sw2 || !ne2) { return this; }
- } else {
- return obj ? this.extend(L.latLng(obj) || L.latLngBounds(obj)) : this;
- }
- if (!sw && !ne) {
- this._southWest = new L.LatLng(sw2.lat, sw2.lng);
- this._northEast = new L.LatLng(ne2.lat, ne2.lng);
- } else {
- sw.lat = Math.min(sw2.lat, sw.lat);
- sw.lng = Math.min(sw2.lng, sw.lng);
- ne.lat = Math.max(ne2.lat, ne.lat);
- ne.lng = Math.max(ne2.lng, ne.lng);
- }
- return this;
- },
- // @method pad(bufferRatio: Number): LatLngBounds
- // Returns bigger bounds created by extending the current bounds by a given percentage in each direction.
- pad: function (bufferRatio) {
- var sw = this._southWest,
- ne = this._northEast,
- heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
- widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
- return new L.LatLngBounds(
- new L.LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
- new L.LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
- },
- // @method getCenter(): LatLng
- // Returns the center point of the bounds.
- getCenter: function () {
- return new L.LatLng(
- (this._southWest.lat + this._northEast.lat) / 2,
- (this._southWest.lng + this._northEast.lng) / 2);
- },
- // @method getSouthWest(): LatLng
- // Returns the south-west point of the bounds.
- getSouthWest: function () {
- return this._southWest;
- },
- // @method getNorthEast(): LatLng
- // Returns the north-east point of the bounds.
- getNorthEast: function () {
- return this._northEast;
- },
- // @method getNorthWest(): LatLng
- // Returns the north-west point of the bounds.
- getNorthWest: function () {
- return new L.LatLng(this.getNorth(), this.getWest());
- },
- // @method getSouthEast(): LatLng
- // Returns the south-east point of the bounds.
- getSouthEast: function () {
- return new L.LatLng(this.getSouth(), this.getEast());
- },
- // @method getWest(): Number
- // Returns the west longitude of the bounds
- getWest: function () {
- return this._southWest.lng;
- },
- // @method getSouth(): Number
- // Returns the south latitude of the bounds
- getSouth: function () {
- return this._southWest.lat;
- },
- // @method getEast(): Number
- // Returns the east longitude of the bounds
- getEast: function () {
- return this._northEast.lng;
- },
- // @method getNorth(): Number
- // Returns the north latitude of the bounds
- getNorth: function () {
- return this._northEast.lat;
- },
- // @method contains(otherBounds: LatLngBounds): Boolean
- // Returns `true` if the rectangle contains the given one.
- // @alternative
- // @method contains (latlng: LatLng): Boolean
- // Returns `true` if the rectangle contains the given point.
- contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
- if (typeof obj[0] === 'number' || obj instanceof L.LatLng) {
- obj = L.latLng(obj);
- } else {
- obj = L.latLngBounds(obj);
- }
- var sw = this._southWest,
- ne = this._northEast,
- sw2, ne2;
- if (obj instanceof L.LatLngBounds) {
- sw2 = obj.getSouthWest();
- ne2 = obj.getNorthEast();
- } else {
- sw2 = ne2 = obj;
- }
- return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
- (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
- },
- // @method intersects(otherBounds: LatLngBounds): Boolean
- // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common.
- intersects: function (bounds) {
- bounds = L.latLngBounds(bounds);
- var sw = this._southWest,
- ne = this._northEast,
- sw2 = bounds.getSouthWest(),
- ne2 = bounds.getNorthEast(),
- latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
- lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
- return latIntersects && lngIntersects;
- },
- // @method overlaps(otherBounds: Bounds): Boolean
- // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area.
- overlaps: function (bounds) {
- bounds = L.latLngBounds(bounds);
- var sw = this._southWest,
- ne = this._northEast,
- sw2 = bounds.getSouthWest(),
- ne2 = bounds.getNorthEast(),
- latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat),
- lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng);
- return latOverlaps && lngOverlaps;
- },
- // @method toBBoxString(): String
- // Returns a string with bounding box coordinates in a 'southwest_lng,southwest_lat,northeast_lng,northeast_lat' format. Useful for sending requests to web services that return geo data.
- toBBoxString: function () {
- return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
- },
- // @method equals(otherBounds: LatLngBounds): Boolean
- // Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds.
- equals: function (bounds) {
- if (!bounds) { return false; }
- bounds = L.latLngBounds(bounds);
- return this._southWest.equals(bounds.getSouthWest()) &&
- this._northEast.equals(bounds.getNorthEast());
- },
- // @method isValid(): Boolean
- // Returns `true` if the bounds are properly initialized.
- isValid: function () {
- return !!(this._southWest && this._northEast);
- }
- };
- // TODO International date line?
- // @factory L.latLngBounds(corner1: LatLng, corner2: LatLng)
- // Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle.
- // @alternative
- // @factory L.latLngBounds(latlngs: LatLng[])
- // Creates a `LatLngBounds` object defined by the geographical points it contains. Very useful for zooming the map to fit a particular set of locations with [`fitBounds`](#map-fitbounds).
- L.latLngBounds = function (a, b) {
- if (a instanceof L.LatLngBounds) {
- return a;
- }
- return new L.LatLngBounds(a, b);
- };
- /*
- * @namespace Projection
- * @section
- * Leaflet comes with a set of already defined Projections out of the box:
- *
- * @projection L.Projection.LonLat
- *
- * Equirectangular, or Plate Carree projection — the most simple projection,
- * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
- * latitude. Also suitable for flat worlds, e.g. game maps. Used by the
- * `EPSG:3395` and `Simple` CRS.
- */
- L.Projection = {};
- L.Projection.LonLat = {
- project: function (latlng) {
- return new L.Point(latlng.lng, latlng.lat);
- },
- unproject: function (point) {
- return new L.LatLng(point.y, point.x);
- },
- bounds: L.bounds([-180, -90], [180, 90])
- };
- /*
- * @namespace Projection
- * @projection L.Projection.SphericalMercator
- *
- * Spherical Mercator projection — the most common projection for online maps,
- * used by almost all free and commercial tile providers. Assumes that Earth is
- * a sphere. Used by the `EPSG:3857` CRS.
- */
- L.Projection.SphericalMercator = {
- R: 6378137,
- MAX_LATITUDE: 85.0511287798,
- project: function (latlng) {
- var d = Math.PI / 180,
- max = this.MAX_LATITUDE,
- lat = Math.max(Math.min(max, latlng.lat), -max),
- sin = Math.sin(lat * d);
- return new L.Point(
- this.R * latlng.lng * d,
- this.R * Math.log((1 + sin) / (1 - sin)) / 2);
- },
- unproject: function (point) {
- var d = 180 / Math.PI;
- return new L.LatLng(
- (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d,
- point.x * d / this.R);
- },
- bounds: (function () {
- var d = 6378137 * Math.PI;
- return L.bounds([-d, -d], [d, d]);
- })()
- };
- /*
- * @class CRS
- * @aka L.CRS
- * Abstract class that defines coordinate reference systems for projecting
- * geographical points into pixel (screen) coordinates and back (and to
- * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See
- * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system).
- *
- * Leaflet defines the most usual CRSs by default. If you want to use a
- * CRS not defined by default, take a look at the
- * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin.
- */
- L.CRS = {
- // @method latLngToPoint(latlng: LatLng, zoom: Number): Point
- // Projects geographical coordinates into pixel coordinates for a given zoom.
- latLngToPoint: function (latlng, zoom) {
- var projectedPoint = this.projection.project(latlng),
- scale = this.scale(zoom);
- return this.transformation._transform(projectedPoint, scale);
- },
- // @method pointToLatLng(point: Point, zoom: Number): LatLng
- // The inverse of `latLngToPoint`. Projects pixel coordinates on a given
- // zoom into geographical coordinates.
- pointToLatLng: function (point, zoom) {
- var scale = this.scale(zoom),
- untransformedPoint = this.transformation.untransform(point, scale);
- return this.projection.unproject(untransformedPoint);
- },
- // @method project(latlng: LatLng): Point
- // Projects geographical coordinates into coordinates in units accepted for
- // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services).
- project: function (latlng) {
- return this.projection.project(latlng);
- },
- // @method unproject(point: Point): LatLng
- // Given a projected coordinate returns the corresponding LatLng.
- // The inverse of `project`.
- unproject: function (point) {
- return this.projection.unproject(point);
- },
- // @method scale(zoom: Number): Number
- // Returns the scale used when transforming projected coordinates into
- // pixel coordinates for a particular zoom. For example, it returns
- // `256 * 2^zoom` for Mercator-based CRS.
- scale: function (zoom) {
- return 256 * Math.pow(2, zoom);
- },
- // @method zoom(scale: Number): Number
- // Inverse of `scale()`, returns the zoom level corresponding to a scale
- // factor of `scale`.
- zoom: function (scale) {
- return Math.log(scale / 256) / Math.LN2;
- },
- // @method getProjectedBounds(zoom: Number): Bounds
- // Returns the projection's bounds scaled and transformed for the provided `zoom`.
- getProjectedBounds: function (zoom) {
- if (this.infinite) { return null; }
- var b = this.projection.bounds,
- s = this.scale(zoom),
- min = this.transformation.transform(b.min, s),
- max = this.transformation.transform(b.max, s);
- return L.bounds(min, max);
- },
- // @method distance(latlng1: LatLng, latlng2: LatLng): Number
- // Returns the distance between two geographical coordinates.
- // @property code: String
- // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`)
- //
- // @property wrapLng: Number[]
- // An array of two numbers defining whether the longitude (horizontal) coordinate
- // axis wraps around a given range and how. Defaults to `[-180, 180]` in most
- // geographical CRSs. If `undefined`, the longitude axis does not wrap around.
- //
- // @property wrapLat: Number[]
- // Like `wrapLng`, but for the latitude (vertical) axis.
- // wrapLng: [min, max],
- // wrapLat: [min, max],
- // @property infinite: Boolean
- // If true, the coordinate space will be unbounded (infinite in both axes)
- infinite: false,
- // @method wrapLatLng(latlng: LatLng): LatLng
- // Returns a `LatLng` where lat and lng has been wrapped according to the
- // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds.
- wrapLatLng: function (latlng) {
- var lng = this.wrapLng ? L.Util.wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng,
- lat = this.wrapLat ? L.Util.wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat,
- alt = latlng.alt;
- return L.latLng(lat, lng, alt);
- }
- };
- /*
- * @namespace CRS
- * @crs L.CRS.Simple
- *
- * A simple CRS that maps longitude and latitude into `x` and `y` directly.
- * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
- * axis should still be inverted (going from bottom to top). `distance()` returns
- * simple euclidean distance.
- */
- L.CRS.Simple = L.extend({}, L.CRS, {
- projection: L.Projection.LonLat,
- transformation: new L.Transformation(1, 0, -1, 0),
- scale: function (zoom) {
- return Math.pow(2, zoom);
- },
- zoom: function (scale) {
- return Math.log(scale) / Math.LN2;
- },
- distance: function (latlng1, latlng2) {
- var dx = latlng2.lng - latlng1.lng,
- dy = latlng2.lat - latlng1.lat;
- return Math.sqrt(dx * dx + dy * dy);
- },
- infinite: true
- });
- /*
- * @namespace CRS
- * @crs L.CRS.Earth
- *
- * Serves as the base for CRS that are global such that they cover the earth.
- * Can only be used as the base for other CRS and cannot be used directly,
- * since it does not have a `code`, `projection` or `transformation`. `distance()` returns
- * meters.
- */
- L.CRS.Earth = L.extend({}, L.CRS, {
- wrapLng: [-180, 180],
- // Mean Earth Radius, as recommended for use by
- // the International Union of Geodesy and Geophysics,
- // see http://rosettacode.org/wiki/Haversine_formula
- R: 6371000,
- // distance between two geographical points using spherical law of cosines approximation
- distance: function (latlng1, latlng2) {
- var rad = Math.PI / 180,
- lat1 = latlng1.lat * rad,
- lat2 = latlng2.lat * rad,
- a = Math.sin(lat1) * Math.sin(lat2) +
- Math.cos(lat1) * Math.cos(lat2) * Math.cos((latlng2.lng - latlng1.lng) * rad);
- return this.R * Math.acos(Math.min(a, 1));
- }
- });
- /*
- * @namespace CRS
- * @crs L.CRS.EPSG3857
- *
- * The most common CRS for online maps, used by almost all free and commercial
- * tile providers. Uses Spherical Mercator projection. Set in by default in
- * Map's `crs` option.
- */
- L.CRS.EPSG3857 = L.extend({}, L.CRS.Earth, {
- code: 'EPSG:3857',
- projection: L.Projection.SphericalMercator,
- transformation: (function () {
- var scale = 0.5 / (Math.PI * L.Projection.SphericalMercator.R);
- return new L.Transformation(scale, 0.5, -scale, 0.5);
- }())
- });
- L.CRS.EPSG900913 = L.extend({}, L.CRS.EPSG3857, {
- code: 'EPSG:900913'
- });
- /*
- * @namespace CRS
- * @crs L.CRS.EPSG4326
- *
- * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
- *
- * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic),
- * which is a breaking change from 0.7.x behaviour. If you are using a `TileLayer`
- * with this CRS, ensure that there are two 256x256 pixel tiles covering the
- * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90),
- * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set.
- */
- L.CRS.EPSG4326 = L.extend({}, L.CRS.Earth, {
- code: 'EPSG:4326',
- projection: L.Projection.LonLat,
- transformation: new L.Transformation(1 / 180, 1, -1 / 180, 0.5)
- });
- /*
- * @class Map
- * @aka L.Map
- * @inherits Evented
- *
- * The central class of the API — it is used to create a map on a page and manipulate it.
- *
- * @example
- *
- * ```js
- * // initialize the map on the "map" div with a given center and zoom
- * var map = L.map('map', {
- * center: [51.505, -0.09],
- * zoom: 13
- * });
- * ```
- *
- */
- L.Map = L.Evented.extend({
- options: {
- // @section Map State Options
- // @option crs: CRS = L.CRS.EPSG3857
- // The [Coordinate Reference System](#crs) to use. Don't change this if you're not
- // sure what it means.
- crs: L.CRS.EPSG3857,
- // @option center: LatLng = undefined
- // Initial geographic center of the map
- center: undefined,
- // @option zoom: Number = undefined
- // Initial map zoom level
- zoom: undefined,
- // @option minZoom: Number = undefined
- // Minimum zoom level of the map. Overrides any `minZoom` option set on map layers.
- minZoom: undefined,
- // @option maxZoom: Number = undefined
- // Maximum zoom level of the map. Overrides any `maxZoom` option set on map layers.
- maxZoom: undefined,
- // @option layers: Layer[] = []
- // Array of layers that will be added to the map initially
- layers: [],
- // @option maxBounds: LatLngBounds = null
- // When this option is set, the map restricts the view to the given
- // geographical bounds, bouncing the user back when he tries to pan
- // outside the view. To set the restriction dynamically, use
- // [`setMaxBounds`](#map-setmaxbounds) method.
- maxBounds: undefined,
- // @option renderer: Renderer = *
- // The default method for drawing vector layers on the map. `L.SVG`
- // or `L.Canvas` by default depending on browser support.
- renderer: undefined,
- // @section Animation Options
- // @option zoomAnimation: Boolean = true
- // Whether the map zoom animation is enabled. By default it's enabled
- // in all browsers that support CSS3 Transitions except Android.
- zoomAnimation: true,
- // @option zoomAnimationThreshold: Number = 4
- // Won't animate zoom if the zoom difference exceeds this value.
- zoomAnimationThreshold: 4,
- // @option fadeAnimation: Boolean = true
- // Whether the tile fade animation is enabled. By default it's enabled
- // in all browsers that support CSS3 Transitions except Android.
- fadeAnimation: true,
- // @option markerZoomAnimation: Boolean = true
- // Whether markers animate their zoom with the zoom animation, if disabled
- // they will disappear for the length of the animation. By default it's
- // enabled in all browsers that support CSS3 Transitions except Android.
- markerZoomAnimation: true,
- // @option transform3DLimit: Number = 2^23
- // Defines the maximum size of a CSS translation transform. The default
- // value should not be changed unless a web browser positions layers in
- // the wrong place after doing a large `panBy`.
- transform3DLimit: 8388608, // Precision limit of a 32-bit float
- // @section Interaction Options
- // @option zoomSnap: Number = 1
- // Forces the map's zoom level to always be a multiple of this, particularly
- // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
- // By default, the zoom level snaps to the nearest integer; lower values
- // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
- // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
- zoomSnap: 1,
- // @option zoomDelta: Number = 1
- // Controls how much the map's zoom level will change after a
- // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
- // or `-` on the keyboard, or using the [zoom controls](#control-zoom).
- // Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
- zoomDelta: 1,
- // @option trackResize: Boolean = true
- // Whether the map automatically handles browser window resize to update itself.
- trackResize: true
- },
- initialize: function (id, options) { // (HTMLElement or String, Object)
- options = L.setOptions(this, options);
- this._initContainer(id);
- this._initLayout();
- // hack for https://github.com/Leaflet/Leaflet/issues/1980
- this._onResize = L.bind(this._onResize, this);
- this._initEvents();
- if (options.maxBounds) {
- this.setMaxBounds(options.maxBounds);
- }
- if (options.zoom !== undefined) {
- this._zoom = this._limitZoom(options.zoom);
- }
- if (options.center && options.zoom !== undefined) {
- this.setView(L.latLng(options.center), options.zoom, {reset: true});
- }
- this._handlers = [];
- this._layers = {};
- this._zoomBoundLayers = {};
- this._sizeChanged = true;
- this.callInitHooks();
- // don't animate on browsers without hardware-accelerated transitions or old Android/Opera
- this._zoomAnimated = L.DomUtil.TRANSITION && L.Browser.any3d && !L.Browser.mobileOpera &&
- this.options.zoomAnimation;
- // zoom transitions run with the same duration for all layers, so if one of transitionend events
- // happens after starting zoom animation (propagating to the map pane), we know that it ended globally
- if (this._zoomAnimated) {
- this._createAnimProxy();
- L.DomEvent.on(this._proxy, L.DomUtil.TRANSITION_END, this._catchTransitionEnd, this);
- }
- this._addLayers(this.options.layers);
- },
- // @section Methods for modifying map state
- // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
- // Sets the view of the map (geographical center and zoom) with the given
- // animation options.
- setView: function (center, zoom, options) {
- zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
- center = this._limitCenter(L.latLng(center), zoom, this.options.maxBounds);
- options = options || {};
- this._stop();
- if (this._loaded && !options.reset && options !== true) {
- if (options.animate !== undefined) {
- options.zoom = L.extend({animate: options.animate}, options.zoom);
- options.pan = L.extend({animate: options.animate, duration: options.duration}, options.pan);
- }
- // try animating pan or zoom
- var moved = (this._zoom !== zoom) ?
- this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
- this._tryAnimatedPan(center, options.pan);
- if (moved) {
- // prevent resize handler call, the view will refresh after animation anyway
- clearTimeout(this._sizeTimer);
- return this;
- }
- }
- // animation didn't start, just reset the map view
- this._resetView(center, zoom);
- return this;
- },
- // @method setZoom(zoom: Number, options: Zoom/pan options): this
- // Sets the zoom of the map.
- setZoom: function (zoom, options) {
- if (!this._loaded) {
- this._zoom = zoom;
- return this;
- }
- return this.setView(this.getCenter(), zoom, {zoom: options});
- },
- // @method zoomIn(delta?: Number, options?: Zoom options): this
- // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
- zoomIn: function (delta, options) {
- delta = delta || (L.Browser.any3d ? this.options.zoomDelta : 1);
- return this.setZoom(this._zoom + delta, options);
- },
- // @method zoomOut(delta?: Number, options?: Zoom options): this
- // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
- zoomOut: function (delta, options) {
- delta = delta || (L.Browser.any3d ? this.options.zoomDelta : 1);
- return this.setZoom(this._zoom - delta, options);
- },
- // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
- // Zooms the map while keeping a specified geographical point on the map
- // stationary (e.g. used internally for scroll zoom and double-click zoom).
- // @alternative
- // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
- // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
- setZoomAround: function (latlng, zoom, options) {
- var scale = this.getZoomScale(zoom),
- viewHalf = this.getSize().divideBy(2),
- containerPoint = latlng instanceof L.Point ? latlng : this.latLngToContainerPoint(latlng),
- centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
- newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
- return this.setView(newCenter, zoom, {zoom: options});
- },
- _getBoundsCenterZoom: function (bounds, options) {
- options = options || {};
- bounds = bounds.getBounds ? bounds.getBounds() : L.latLngBounds(bounds);
- var paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]),
- paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]),
- zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
- zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
- var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
- swPoint = this.project(bounds.getSouthWest(), zoom),
- nePoint = this.project(bounds.getNorthEast(), zoom),
- center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
- return {
- center: center,
- zoom: zoom
- };
- },
- // @method fitBounds(bounds: LatLngBounds, options: fitBounds options): this
- // Sets a map view that contains the given geographical bounds with the
- // maximum zoom level possible.
- fitBounds: function (bounds, options) {
- bounds = L.latLngBounds(bounds);
- if (!bounds.isValid()) {
- throw new Error('Bounds are not valid.');
- }
- var target = this._getBoundsCenterZoom(bounds, options);
- return this.setView(target.center, target.zoom, options);
- },
- // @method fitWorld(options?: fitBounds options): this
- // Sets a map view that mostly contains the whole world with the maximum
- // zoom level possible.
- fitWorld: function (options) {
- return this.fitBounds([[-90, -180], [90, 180]], options);
- },
- // @method panTo(latlng: LatLng, options?: Pan options): this
- // Pans the map to a given center.
- panTo: function (center, options) { // (LatLng)
- return this.setView(center, this._zoom, {pan: options});
- },
- // @method panBy(offset: Point): this
- // Pans the map by a given number of pixels (animated).
- panBy: function (offset, options) {
- offset = L.point(offset).round();
- options = options || {};
- if (!offset.x && !offset.y) {
- return this.fire('moveend');
- }
- // If we pan too far, Chrome gets issues with tiles
- // and makes them disappear or appear in the wrong place (slightly offset) #2602
- if (options.animate !== true && !this.getSize().contains(offset)) {
- this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
- return this;
- }
- if (!this._panAnim) {
- this._panAnim = new L.PosAnimation();
- this._panAnim.on({
- 'step': this._onPanTransitionStep,
- 'end': this._onPanTransitionEnd
- }, this);
- }
- // don't fire movestart if animating inertia
- if (!options.noMoveStart) {
- this.fire('movestart');
- }
- // animate pan unless animate: false specified
- if (options.animate !== false) {
- L.DomUtil.addClass(this._mapPane, 'leaflet-pan-anim');
- var newPos = this._getMapPanePos().subtract(offset).round();
- this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
- } else {
- this._rawPanBy(offset);
- this.fire('move').fire('moveend');
- }
- return this;
- },
- // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
- // Sets the view of the map (geographical center and zoom) performing a smooth
- // pan-zoom animation.
- flyTo: function (targetCenter, targetZoom, options) {
- options = options || {};
- if (options.animate === false || !L.Browser.any3d) {
- return this.setView(targetCenter, targetZoom, options);
- }
- this._stop();
- var from = this.project(this.getCenter()),
- to = this.project(targetCenter),
- size = this.getSize(),
- startZoom = this._zoom;
- targetCenter = L.latLng(targetCenter);
- targetZoom = targetZoom === undefined ? startZoom : targetZoom;
- var w0 = Math.max(size.x, size.y),
- w1 = w0 * this.getZoomScale(startZoom, targetZoom),
- u1 = (to.distanceTo(from)) || 1,
- rho = 1.42,
- rho2 = rho * rho;
- function r(i) {
- var s1 = i ? -1 : 1,
- s2 = i ? w1 : w0,
- t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
- b1 = 2 * s2 * rho2 * u1,
- b = t1 / b1,
- sq = Math.sqrt(b * b + 1) - b;
- // workaround for floating point precision bug when sq = 0, log = -Infinite,
- // thus triggering an infinite loop in flyTo
- var log = sq < 0.000000001 ? -18 : Math.log(sq);
- return log;
- }
- function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
- function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
- function tanh(n) { return sinh(n) / cosh(n); }
- var r0 = r(0);
- function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
- function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
- function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
- var start = Date.now(),
- S = (r(1) - r0) / rho,
- duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
- function frame() {
- var t = (Date.now() - start) / duration,
- s = easeOut(t) * S;
- if (t <= 1) {
- this._flyToFrame = L.Util.requestAnimFrame(frame, this);
- this._move(
- this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
- this.getScaleZoom(w0 / w(s), startZoom),
- {flyTo: true});
- } else {
- this
- ._move(targetCenter, targetZoom)
- ._moveEnd(true);
- }
- }
- this._moveStart(true);
- frame.call(this);
- return this;
- },
- // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
- // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
- // but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
- flyToBounds: function (bounds, options) {
- var target = this._getBoundsCenterZoom(bounds, options);
- return this.flyTo(target.center, target.zoom, options);
- },
- // @method setMaxBounds(bounds: Bounds): this
- // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
- setMaxBounds: function (bounds) {
- bounds = L.latLngBounds(bounds);
- if (!bounds.isValid()) {
- this.options.maxBounds = null;
- return this.off('moveend', this._panInsideMaxBounds);
- } else if (this.options.maxBounds) {
- this.off('moveend', this._panInsideMaxBounds);
- }
- this.options.maxBounds = bounds;
- if (this._loaded) {
- this._panInsideMaxBounds();
- }
- return this.on('moveend', this._panInsideMaxBounds);
- },
- // @method setMinZoom(zoom: Number): this
- // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
- setMinZoom: function (zoom) {
- this.options.minZoom = zoom;
- if (this._loaded && this.getZoom() < this.options.minZoom) {
- return this.setZoom(zoom);
- }
- return this;
- },
- // @method setMaxZoom(zoom: Number): this
- // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
- setMaxZoom: function (zoom) {
- this.options.maxZoom = zoom;
- if (this._loaded && (this.getZoom() > this.options.maxZoom)) {
- return this.setZoom(zoom);
- }
- return this;
- },
- // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
- // Pans the map to the closest view that would lie inside the given bounds (if it's not already), controlling the animation using the options specific, if any.
- panInsideBounds: function (bounds, options) {
- this._enforcingBounds = true;
- var center = this.getCenter(),
- newCenter = this._limitCenter(center, this._zoom, L.latLngBounds(bounds));
- if (!center.equals(newCenter)) {
- this.panTo(newCenter, options);
- }
- this._enforcingBounds = false;
- return this;
- },
- // @method invalidateSize(options: Zoom/Pan options): this
- // Checks if the map container size changed and updates the map if so —
- // call it after you've changed the map size dynamically, also animating
- // pan by default. If `options.pan` is `false`, panning will not occur.
- // If `options.debounceMoveend` is `true`, it will delay `moveend` event so
- // that it doesn't happen often even if the method is called many
- // times in a row.
- // @alternative
- // @method invalidateSize(animate: Boolean): this
- // Checks if the map container size changed and updates the map if so —
- // call it after you've changed the map size dynamically, also animating
- // pan by default.
- invalidateSize: function (options) {
- if (!this._loaded) { return this; }
- options = L.extend({
- animate: false,
- pan: true
- }, options === true ? {animate: true} : options);
- var oldSize = this.getSize();
- this._sizeChanged = true;
- this._lastCenter = null;
- var newSize = this.getSize(),
- oldCenter = oldSize.divideBy(2).round(),
- newCenter = newSize.divideBy(2).round(),
- offset = oldCenter.subtract(newCenter);
- if (!offset.x && !offset.y) { return this; }
- if (options.animate && options.pan) {
- this.panBy(offset);
- } else {
- if (options.pan) {
- this._rawPanBy(offset);
- }
- this.fire('move');
- if (options.debounceMoveend) {
- clearTimeout(this._sizeTimer);
- this._sizeTimer = setTimeout(L.bind(this.fire, this, 'moveend'), 200);
- } else {
- this.fire('moveend');
- }
- }
- // @section Map state change events
- // @event resize: ResizeEvent
- // Fired when the map is resized.
- return this.fire('resize', {
- oldSize: oldSize,
- newSize: newSize
- });
- },
- // @section Methods for modifying map state
- // @method stop(): this
- // Stops the currently running `panTo` or `flyTo` animation, if any.
- stop: function () {
- this.setZoom(this._limitZoom(this._zoom));
- if (!this.options.zoomSnap) {
- this.fire('viewreset');
- }
- return this._stop();
- },
- // @section Geolocation methods
- // @method locate(options?: Locate options): this
- // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
- // event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
- // and optionally sets the map view to the user's location with respect to
- // detection accuracy (or to the world view if geolocation failed).
- // Note that, if your page doesn't use HTTPS, this method will fail in
- // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
- // See `Locate options` for more details.
- locate: function (options) {
- options = this._locateOptions = L.extend({
- timeout: 10000,
- watch: false
- // setView: false
- // maxZoom: <Number>
- // maximumAge: 0
- // enableHighAccuracy: false
- }, options);
- if (!('geolocation' in navigator)) {
- this._handleGeolocationError({
- code: 0,
- message: 'Geolocation not supported.'
- });
- return this;
- }
- var onResponse = L.bind(this._handleGeolocationResponse, this),
- onError = L.bind(this._handleGeolocationError, this);
- if (options.watch) {
- this._locationWatchId =
- navigator.geolocation.watchPosition(onResponse, onError, options);
- } else {
- navigator.geolocation.getCurrentPosition(onResponse, onError, options);
- }
- return this;
- },
- // @method stopLocate(): this
- // Stops watching location previously initiated by `map.locate({watch: true})`
- // and aborts resetting the map view if map.locate was called with
- // `{setView: true}`.
- stopLocate: function () {
- if (navigator.geolocation && navigator.geolocation.clearWatch) {
- navigator.geolocation.clearWatch(this._locationWatchId);
- }
- if (this._locateOptions) {
- this._locateOptions.setView = false;
- }
- return this;
- },
- _handleGeolocationError: function (error) {
- var c = error.code,
- message = error.message ||
- (c === 1 ? 'permission denied' :
- (c === 2 ? 'position unavailable' : 'timeout'));
- if (this._locateOptions.setView && !this._loaded) {
- this.fitWorld();
- }
- // @section Location events
- // @event locationerror: ErrorEvent
- // Fired when geolocation (using the [`locate`](#map-locate) method) failed.
- this.fire('locationerror', {
- code: c,
- message: 'Geolocation error: ' + message + '.'
- });
- },
- _handleGeolocationResponse: function (pos) {
- var lat = pos.coords.latitude,
- lng = pos.coords.longitude,
- latlng = new L.LatLng(lat, lng),
- bounds = latlng.toBounds(pos.coords.accuracy),
- options = this._locateOptions;
- if (options.setView) {
- var zoom = this.getBoundsZoom(bounds);
- this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
- }
- var data = {
- latlng: latlng,
- bounds: bounds,
- timestamp: pos.timestamp
- };
- for (var i in pos.coords) {
- if (typeof pos.coords[i] === 'number') {
- data[i] = pos.coords[i];
- }
- }
- // @event locationfound: LocationEvent
- // Fired when geolocation (using the [`locate`](#map-locate) method)
- // went successfully.
- this.fire('locationfound', data);
- },
- // TODO handler.addTo
- // TODO Appropiate docs section?
- // @section Other Methods
- // @method addHandler(name: String, HandlerClass: Function): this
- // Adds a new `Handler` to the map, given its name and constructor function.
- addHandler: function (name, HandlerClass) {
- if (!HandlerClass) { return this; }
- var handler = this[name] = new HandlerClass(this);
- this._handlers.push(handler);
- if (this.options[name]) {
- handler.enable();
- }
- return this;
- },
- // @method remove(): this
- // Destroys the map and clears all related event listeners.
- remove: function () {
- this._initEvents(true);
- if (this._containerId !== this._container._leaflet_id) {
- throw new Error('Map container is being reused by another instance');
- }
- try {
- // throws error in IE6-8
- delete this._container._leaflet_id;
- delete this._containerId;
- } catch (e) {
- /*eslint-disable */
- this._container._leaflet_id = undefined;
- /*eslint-enable */
- this._containerId = undefined;
- }
- L.DomUtil.remove(this._mapPane);
- if (this._clearControlPos) {
- this._clearControlPos();
- }
- this._clearHandlers();
- if (this._loaded) {
- // @section Map state change events
- // @event unload: Event
- // Fired when the map is destroyed with [remove](#map-remove) method.
- this.fire('unload');
- }
- for (var i in this._layers) {
- this._layers[i].remove();
- }
- return this;
- },
- // @section Other Methods
- // @method createPane(name: String, container?: HTMLElement): HTMLElement
- // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
- // then returns it. The pane is created as a children of `container`, or
- // as a children of the main map pane if not set.
- createPane: function (name, container) {
- var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
- pane = L.DomUtil.create('div', className, container || this._mapPane);
- if (name) {
- this._panes[name] = pane;
- }
- return pane;
- },
- // @section Methods for Getting Map State
- // @method getCenter(): LatLng
- // Returns the geographical center of the map view
- getCenter: function () {
- this._checkIfLoaded();
- if (this._lastCenter && !this._moved()) {
- return this._lastCenter;
- }
- return this.layerPointToLatLng(this._getCenterLayerPoint());
- },
- // @method getZoom(): Number
- // Returns the current zoom level of the map view
- getZoom: function () {
- return this._zoom;
- },
- // @method getBounds(): LatLngBounds
- // Returns the geographical bounds visible in the current map view
- getBounds: function () {
- var bounds = this.getPixelBounds(),
- sw = this.unproject(bounds.getBottomLeft()),
- ne = this.unproject(bounds.getTopRight());
- return new L.LatLngBounds(sw, ne);
- },
- // @method getMinZoom(): Number
- // Returns the minimum zoom level of the map (if set in the `minZoom` option of the map or of any layers), or `0` by default.
- getMinZoom: function () {
- return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
- },
- // @method getMaxZoom(): Number
- // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
- getMaxZoom: function () {
- return this.options.maxZoom === undefined ?
- (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
- this.options.maxZoom;
- },
- // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean): Number
- // Returns the maximum zoom level on which the given bounds fit to the map
- // view in its entirety. If `inside` (optional) is set to `true`, the method
- // instead returns the minimum zoom level on which the map view fits into
- // the given bounds in its entirety.
- getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
- bounds = L.latLngBounds(bounds);
- padding = L.point(padding || [0, 0]);
- var zoom = this.getZoom() || 0,
- min = this.getMinZoom(),
- max = this.getMaxZoom(),
- nw = bounds.getNorthWest(),
- se = bounds.getSouthEast(),
- size = this.getSize().subtract(padding),
- boundsSize = this.project(se, zoom).subtract(this.project(nw, zoom)),
- snap = L.Browser.any3d ? this.options.zoomSnap : 1;
- var scale = Math.min(size.x / boundsSize.x, size.y / boundsSize.y);
- zoom = this.getScaleZoom(scale, zoom);
- if (snap) {
- zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
- zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
- }
- return Math.max(min, Math.min(max, zoom));
- },
- // @method getSize(): Point
- // Returns the current size of the map container (in pixels).
- getSize: function () {
- if (!this._size || this._sizeChanged) {
- this._size = new L.Point(
- this._container.clientWidth,
- this._container.clientHeight);
- this._sizeChanged = false;
- }
- return this._size.clone();
- },
- // @method getPixelBounds(): Bounds
- // Returns the bounds of the current map view in projected pixel
- // coordinates (sometimes useful in layer and overlay implementations).
- getPixelBounds: function (center, zoom) {
- var topLeftPoint = this._getTopLeftPoint(center, zoom);
- return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
- },
- // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
- // the map pane? "left point of the map layer" can be confusing, specially
- // since there can be negative offsets.
- // @method getPixelOrigin(): Point
- // Returns the projected pixel coordinates of the top left point of
- // the map layer (useful in custom layer and overlay implementations).
- getPixelOrigin: function () {
- this._checkIfLoaded();
- return this._pixelOrigin;
- },
- // @method getPixelWorldBounds(zoom?: Number): Bounds
- // Returns the world's bounds in pixel coordinates for zoom level `zoom`.
- // If `zoom` is omitted, the map's current zoom level is used.
- getPixelWorldBounds: function (zoom) {
- return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
- },
- // @section Other Methods
- // @method getPane(pane: String|HTMLElement): HTMLElement
- // Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
- getPane: function (pane) {
- return typeof pane === 'string' ? this._panes[pane] : pane;
- },
- // @method getPanes(): Object
- // Returns a plain object containing the names of all [panes](#map-pane) as keys and
- // the panes as values.
- getPanes: function () {
- return this._panes;
- },
- // @method getContainer: HTMLElement
- // Returns the HTML element that contains the map.
- getContainer: function () {
- return this._container;
- },
- // @section Conversion Methods
- // @method getZoomScale(toZoom: Number, fromZoom: Number): Number
- // Returns the scale factor to be applied to a map transition from zoom level
- // `fromZoom` to `toZoom`. Used internally to help with zoom animations.
- getZoomScale: function (toZoom, fromZoom) {
- // TODO replace with universal implementation after refactoring projections
- var crs = this.options.crs;
- fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
- return crs.scale(toZoom) / crs.scale(fromZoom);
- },
- // @method getScaleZoom(scale: Number, fromZoom: Number): Number
- // Returns the zoom level that the map would end up at, if it is at `fromZoom`
- // level and everything is scaled by a factor of `scale`. Inverse of
- // [`getZoomScale`](#map-getZoomScale).
- getScaleZoom: function (scale, fromZoom) {
- var crs = this.options.crs;
- fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
- var zoom = crs.zoom(scale * crs.scale(fromZoom));
- return isNaN(zoom) ? Infinity : zoom;
- },
- // @method project(latlng: LatLng, zoom: Number): Point
- // Projects a geographical coordinate `LatLng` according to the projection
- // of the map's CRS, then scales it according to `zoom` and the CRS's
- // `Transformation`. The result is pixel coordinate relative to
- // the CRS origin.
- project: function (latlng, zoom) {
- zoom = zoom === undefined ? this._zoom : zoom;
- return this.options.crs.latLngToPoint(L.latLng(latlng), zoom);
- },
- // @method unproject(point: Point, zoom: Number): LatLng
- // Inverse of [`project`](#map-project).
- unproject: function (point, zoom) {
- zoom = zoom === undefined ? this._zoom : zoom;
- return this.options.crs.pointToLatLng(L.point(point), zoom);
- },
- // @method layerPointToLatLng(point: Point): LatLng
- // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
- // returns the corresponding geographical coordinate (for the current zoom level).
- layerPointToLatLng: function (point) {
- var projectedPoint = L.point(point).add(this.getPixelOrigin());
- return this.unproject(projectedPoint);
- },
- // @method latLngToLayerPoint(latlng: LatLng): Point
- // Given a geographical coordinate, returns the corresponding pixel coordinate
- // relative to the [origin pixel](#map-getpixelorigin).
- latLngToLayerPoint: function (latlng) {
- var projectedPoint = this.project(L.latLng(latlng))._round();
- return projectedPoint._subtract(this.getPixelOrigin());
- },
- // @method wrapLatLng(latlng: LatLng): LatLng
- // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
- // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
- // CRS's bounds.
- // By default this means longitude is wrapped around the dateline so its
- // value is between -180 and +180 degrees.
- wrapLatLng: function (latlng) {
- return this.options.crs.wrapLatLng(L.latLng(latlng));
- },
- // @method distance(latlng1: LatLng, latlng2: LatLng): Number
- // Returns the distance between two geographical coordinates according to
- // the map's CRS. By default this measures distance in meters.
- distance: function (latlng1, latlng2) {
- return this.options.crs.distance(L.latLng(latlng1), L.latLng(latlng2));
- },
- // @method containerPointToLayerPoint(point: Point): Point
- // Given a pixel coordinate relative to the map container, returns the corresponding
- // pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
- containerPointToLayerPoint: function (point) { // (Point)
- return L.point(point).subtract(this._getMapPanePos());
- },
- // @method layerPointToContainerPoint(point: Point): Point
- // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
- // returns the corresponding pixel coordinate relative to the map container.
- layerPointToContainerPoint: function (point) { // (Point)
- return L.point(point).add(this._getMapPanePos());
- },
- // @method containerPointToLatLng(point: Point): Point
- // Given a pixel coordinate relative to the map container, returns
- // the corresponding geographical coordinate (for the current zoom level).
- containerPointToLatLng: function (point) {
- var layerPoint = this.containerPointToLayerPoint(L.point(point));
- return this.layerPointToLatLng(layerPoint);
- },
- // @method latLngToContainerPoint(latlng: LatLng): Point
- // Given a geographical coordinate, returns the corresponding pixel coordinate
- // relative to the map container.
- latLngToContainerPoint: function (latlng) {
- return this.layerPointToContainerPoint(this.latLngToLayerPoint(L.latLng(latlng)));
- },
- // @method mouseEventToContainerPoint(ev: MouseEvent): Point
- // Given a MouseEvent object, returns the pixel coordinate relative to the
- // map container where the event took place.
- mouseEventToContainerPoint: function (e) {
- return L.DomEvent.getMousePosition(e, this._container);
- },
- // @method mouseEventToLayerPoint(ev: MouseEvent): Point
- // Given a MouseEvent object, returns the pixel coordinate relative to
- // the [origin pixel](#map-getpixelorigin) where the event took place.
- mouseEventToLayerPoint: function (e) {
- return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
- },
- // @method mouseEventToLatLng(ev: MouseEvent): LatLng
- // Given a MouseEvent object, returns geographical coordinate where the
- // event took place.
- mouseEventToLatLng: function (e) { // (MouseEvent)
- return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
- },
- // map initialization methods
- _initContainer: function (id) {
- var container = this._container = L.DomUtil.get(id);
- if (!container) {
- throw new Error('Map container not found.');
- } else if (container._leaflet_id) {
- throw new Error('Map container is already initialized.');
- }
- L.DomEvent.addListener(container, 'scroll', this._onScroll, this);
- this._containerId = L.Util.stamp(container);
- },
- _initLayout: function () {
- var container = this._container;
- this._fadeAnimated = this.options.fadeAnimation && L.Browser.any3d;
- L.DomUtil.addClass(container, 'leaflet-container' +
- (L.Browser.touch ? ' leaflet-touch' : '') +
- (L.Browser.retina ? ' leaflet-retina' : '') +
- (L.Browser.ielt9 ? ' leaflet-oldie' : '') +
- (L.Browser.safari ? ' leaflet-safari' : '') +
- (this._fadeAnimated ? ' leaflet-fade-anim' : ''));
- var position = L.DomUtil.getStyle(container, 'position');
- if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
- container.style.position = 'relative';
- }
- this._initPanes();
- if (this._initControlPos) {
- this._initControlPos();
- }
- },
- _initPanes: function () {
- var panes = this._panes = {};
- this._paneRenderers = {};
- // @section
- //
- // Panes are DOM elements used to control the ordering of layers on the map. You
- // can access panes with [`map.getPane`](#map-getpane) or
- // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
- // [`map.createPane`](#map-createpane) method.
- //
- // Every map has the following default panes that differ only in zIndex.
- //
- // @pane mapPane: HTMLElement = 'auto'
- // Pane that contains all other map panes
- this._mapPane = this.createPane('mapPane', this._container);
- L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
- // @pane tilePane: HTMLElement = 200
- // Pane for `GridLayer`s and `TileLayer`s
- this.createPane('tilePane');
- // @pane overlayPane: HTMLElement = 400
- // Pane for vector overlays (`Path`s), like `Polyline`s and `Polygon`s
- this.createPane('shadowPane');
- // @pane shadowPane: HTMLElement = 500
- // Pane for overlay shadows (e.g. `Marker` shadows)
- this.createPane('overlayPane');
- // @pane markerPane: HTMLElement = 600
- // Pane for `Icon`s of `Marker`s
- this.createPane('markerPane');
- // @pane tooltipPane: HTMLElement = 650
- // Pane for tooltip.
- this.createPane('tooltipPane');
- // @pane popupPane: HTMLElement = 700
- // Pane for `Popup`s.
- this.createPane('popupPane');
- if (!this.options.markerZoomAnimation) {
- L.DomUtil.addClass(panes.markerPane, 'leaflet-zoom-hide');
- L.DomUtil.addClass(panes.shadowPane, 'leaflet-zoom-hide');
- }
- },
- // private methods that modify map state
- // @section Map state change events
- _resetView: function (center, zoom) {
- L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
- var loading = !this._loaded;
- this._loaded = true;
- zoom = this._limitZoom(zoom);
- this.fire('viewprereset');
- var zoomChanged = this._zoom !== zoom;
- this
- ._moveStart(zoomChanged)
- ._move(center, zoom)
- ._moveEnd(zoomChanged);
- // @event viewreset: Event
- // Fired when the map needs to redraw its content (this usually happens
- // on map zoom or load). Very useful for creating custom overlays.
- this.fire('viewreset');
- // @event load: Event
- // Fired when the map is initialized (when its center and zoom are set
- // for the first time).
- if (loading) {
- this.fire('load');
- }
- },
- _moveStart: function (zoomChanged) {
- // @event zoomstart: Event
- // Fired when the map zoom is about to change (e.g. before zoom animation).
- // @event movestart: Event
- // Fired when the view of the map starts changing (e.g. user starts dragging the map).
- if (zoomChanged) {
- this.fire('zoomstart');
- }
- return this.fire('movestart');
- },
- _move: function (center, zoom, data) {
- if (zoom === undefined) {
- zoom = this._zoom;
- }
- var zoomChanged = this._zoom !== zoom;
- this._zoom = zoom;
- this._lastCenter = center;
- this._pixelOrigin = this._getNewPixelOrigin(center);
- // @event zoom: Event
- // Fired repeatedly during any change in zoom level, including zoom
- // and fly animations.
- if (zoomChanged || (data && data.pinch)) { // Always fire 'zoom' if pinching because #3530
- this.fire('zoom', data);
- }
- // @event move: Event
- // Fired repeatedly during any movement of the map, including pan and
- // fly animations.
- return this.fire('move', data);
- },
- _moveEnd: function (zoomChanged) {
- // @event zoomend: Event
- // Fired when the map has changed, after any animations.
- if (zoomChanged) {
- this.fire('zoomend');
- }
- // @event moveend: Event
- // Fired when the center of the map stops changing (e.g. user stopped
- // dragging the map).
- return this.fire('moveend');
- },
- _stop: function () {
- L.Util.cancelAnimFrame(this._flyToFrame);
- if (this._panAnim) {
- this._panAnim.stop();
- }
- return this;
- },
- _rawPanBy: function (offset) {
- L.DomUtil.setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
- },
- _getZoomSpan: function () {
- return this.getMaxZoom() - this.getMinZoom();
- },
- _panInsideMaxBounds: function () {
- if (!this._enforcingBounds) {
- this.panInsideBounds(this.options.maxBounds);
- }
- },
- _checkIfLoaded: function () {
- if (!this._loaded) {
- throw new Error('Set map center and zoom first.');
- }
- },
- // DOM event handling
- // @section Interaction events
- _initEvents: function (remove) {
- if (!L.DomEvent) { return; }
- this._targets = {};
- this._targets[L.stamp(this._container)] = this;
- var onOff = remove ? 'off' : 'on';
- // @event click: MouseEvent
- // Fired when the user clicks (or taps) the map.
- // @event dblclick: MouseEvent
- // Fired when the user double-clicks (or double-taps) the map.
- // @event mousedown: MouseEvent
- // Fired when the user pushes the mouse button on the map.
- // @event mouseup: MouseEvent
- // Fired when the user releases the mouse button on the map.
- // @event mouseover: MouseEvent
- // Fired when the mouse enters the map.
- // @event mouseout: MouseEvent
- // Fired when the mouse leaves the map.
- // @event mousemove: MouseEvent
- // Fired while the mouse moves over the map.
- // @event contextmenu: MouseEvent
- // Fired when the user pushes the right mouse button on the map, prevents
- // default browser context menu from showing if there are listeners on
- // this event. Also fired on mobile when the user holds a single touch
- // for a second (also called long press).
- // @event keypress: KeyboardEvent
- // Fired when the user presses a key from the keyboard while the map is focused.
- L.DomEvent[onOff](this._container, 'click dblclick mousedown mouseup ' +
- 'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this);
- if (this.options.trackResize) {
- L.DomEvent[onOff](window, 'resize', this._onResize, this);
- }
- if (L.Browser.any3d && this.options.transform3DLimit) {
- this[onOff]('moveend', this._onMoveEnd);
- }
- },
- _onResize: function () {
- L.Util.cancelAnimFrame(this._resizeRequest);
- this._resizeRequest = L.Util.requestAnimFrame(
- function () { this.invalidateSize({debounceMoveend: true}); }, this);
- },
- _onScroll: function () {
- this._container.scrollTop = 0;
- this._container.scrollLeft = 0;
- },
- _onMoveEnd: function () {
- var pos = this._getMapPanePos();
- if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
- // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
- // a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/
- this._resetView(this.getCenter(), this.getZoom());
- }
- },
- _findEventTargets: function (e, type) {
- var targets = [],
- target,
- isHover = type === 'mouseout' || type === 'mouseover',
- src = e.target || e.srcElement,
- dragging = false;
- while (src) {
- target = this._targets[L.stamp(src)];
- if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) {
- // Prevent firing click after you just dragged an object.
- dragging = true;
- break;
- }
- if (target && target.listens(type, true)) {
- if (isHover && !L.DomEvent._isExternalTarget(src, e)) { break; }
- targets.push(target);
- if (isHover) { break; }
- }
- if (src === this._container) { break; }
- src = src.parentNode;
- }
- if (!targets.length && !dragging && !isHover && L.DomEvent._isExternalTarget(src, e)) {
- targets = [this];
- }
- return targets;
- },
- _handleDOMEvent: function (e) {
- if (!this._loaded || L.DomEvent._skipped(e)) { return; }
- var type = e.type === 'keypress' && e.keyCode === 13 ? 'click' : e.type;
- if (type === 'mousedown') {
- // prevents outline when clicking on keyboard-focusable element
- L.DomUtil.preventOutline(e.target || e.srcElement);
- }
- this._fireDOMEvent(e, type);
- },
- _fireDOMEvent: function (e, type, targets) {
- if (e.type === 'click') {
- // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
- // @event preclick: MouseEvent
- // Fired before mouse click on the map (sometimes useful when you
- // want something to happen on click before any existing click
- // handlers start running).
- var synth = L.Util.extend({}, e);
- synth.type = 'preclick';
- this._fireDOMEvent(synth, synth.type, targets);
- }
- if (e._stopped) { return; }
- // Find the layer the event is propagating from and its parents.
- targets = (targets || []).concat(this._findEventTargets(e, type));
- if (!targets.length) { return; }
- var target = targets[0];
- if (type === 'contextmenu' && target.listens(type, true)) {
- L.DomEvent.preventDefault(e);
- }
- var data = {
- originalEvent: e
- };
- if (e.type !== 'keypress') {
- var isMarker = target instanceof L.Marker;
- data.containerPoint = isMarker ?
- this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
- data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
- data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
- }
- for (var i = 0; i < targets.length; i++) {
- targets[i].fire(type, data, true);
- if (data.originalEvent._stopped ||
- (targets[i].options.nonBubblingEvents && L.Util.indexOf(targets[i].options.nonBubblingEvents, type) !== -1)) { return; }
- }
- },
- _draggableMoved: function (obj) {
- obj = obj.dragging && obj.dragging.enabled() ? obj : this;
- return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
- },
- _clearHandlers: function () {
- for (var i = 0, len = this._handlers.length; i < len; i++) {
- this._handlers[i].disable();
- }
- },
- // @section Other Methods
- // @method whenReady(fn: Function, context?: Object): this
- // Runs the given function `fn` when the map gets initialized with
- // a view (center and zoom) and at least one layer, or immediately
- // if it's already initialized, optionally passing a function context.
- whenReady: function (callback, context) {
- if (this._loaded) {
- callback.call(context || this, {target: this});
- } else {
- this.on('load', callback, context);
- }
- return this;
- },
- // private methods for getting map state
- _getMapPanePos: function () {
- return L.DomUtil.getPosition(this._mapPane) || new L.Point(0, 0);
- },
- _moved: function () {
- var pos = this._getMapPanePos();
- return pos && !pos.equals([0, 0]);
- },
- _getTopLeftPoint: function (center, zoom) {
- var pixelOrigin = center && zoom !== undefined ?
- this._getNewPixelOrigin(center, zoom) :
- this.getPixelOrigin();
- return pixelOrigin.subtract(this._getMapPanePos());
- },
- _getNewPixelOrigin: function (center, zoom) {
- var viewHalf = this.getSize()._divideBy(2);
- return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
- },
- _latLngToNewLayerPoint: function (latlng, zoom, center) {
- var topLeft = this._getNewPixelOrigin(center, zoom);
- return this.project(latlng, zoom)._subtract(topLeft);
- },
- _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
- var topLeft = this._getNewPixelOrigin(center, zoom);
- return L.bounds([
- this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
- this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
- this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
- this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
- ]);
- },
- // layer point of the current center
- _getCenterLayerPoint: function () {
- return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
- },
- // offset of the specified place to the current center in pixels
- _getCenterOffset: function (latlng) {
- return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
- },
- // adjust center for view to get inside bounds
- _limitCenter: function (center, zoom, bounds) {
- if (!bounds) { return center; }
- var centerPoint = this.project(center, zoom),
- viewHalf = this.getSize().divideBy(2),
- viewBounds = new L.Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
- offset = this._getBoundsOffset(viewBounds, bounds, zoom);
- // If offset is less than a pixel, ignore.
- // This prevents unstable projections from getting into
- // an infinite loop of tiny offsets.
- if (offset.round().equals([0, 0])) {
- return center;
- }
- return this.unproject(centerPoint.add(offset), zoom);
- },
- // adjust offset for view to get inside bounds
- _limitOffset: function (offset, bounds) {
- if (!bounds) { return offset; }
- var viewBounds = this.getPixelBounds(),
- newBounds = new L.Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
- return offset.add(this._getBoundsOffset(newBounds, bounds));
- },
- // returns offset needed for pxBounds to get inside maxBounds at a specified zoom
- _getBoundsOffset: function (pxBounds, maxBounds, zoom) {
- var projectedMaxBounds = L.bounds(
- this.project(maxBounds.getNorthEast(), zoom),
- this.project(maxBounds.getSouthWest(), zoom)
- ),
- minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
- maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
- dx = this._rebound(minOffset.x, -maxOffset.x),
- dy = this._rebound(minOffset.y, -maxOffset.y);
- return new L.Point(dx, dy);
- },
- _rebound: function (left, right) {
- return left + right > 0 ?
- Math.round(left - right) / 2 :
- Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
- },
- _limitZoom: function (zoom) {
- var min = this.getMinZoom(),
- max = this.getMaxZoom(),
- snap = L.Browser.any3d ? this.options.zoomSnap : 1;
- if (snap) {
- zoom = Math.round(zoom / snap) * snap;
- }
- return Math.max(min, Math.min(max, zoom));
- },
- _onPanTransitionStep: function () {
- this.fire('move');
- },
- _onPanTransitionEnd: function () {
- L.DomUtil.removeClass(this._mapPane, 'leaflet-pan-anim');
- this.fire('moveend');
- },
- _tryAnimatedPan: function (center, options) {
- // difference between the new and current centers in pixels
- var offset = this._getCenterOffset(center)._floor();
- // don't animate too far unless animate: true specified in options
- if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
- this.panBy(offset, options);
- return true;
- },
- _createAnimProxy: function () {
- var proxy = this._proxy = L.DomUtil.create('div', 'leaflet-proxy leaflet-zoom-animated');
- this._panes.mapPane.appendChild(proxy);
- this.on('zoomanim', function (e) {
- var prop = L.DomUtil.TRANSFORM,
- transform = proxy.style[prop];
- L.DomUtil.setTransform(proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
- // workaround for case when transform is the same and so transitionend event is not fired
- if (transform === proxy.style[prop] && this._animatingZoom) {
- this._onZoomTransitionEnd();
- }
- }, this);
- this.on('load moveend', function () {
- var c = this.getCenter(),
- z = this.getZoom();
- L.DomUtil.setTransform(proxy, this.project(c, z), this.getZoomScale(z, 1));
- }, this);
- },
- _catchTransitionEnd: function (e) {
- if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
- this._onZoomTransitionEnd();
- }
- },
- _nothingToAnimate: function () {
- return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
- },
- _tryAnimatedZoom: function (center, zoom, options) {
- if (this._animatingZoom) { return true; }
- options = options || {};
- // don't animate if disabled, not supported or zoom difference is too large
- if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
- Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
- // offset is the pixel coords of the zoom origin relative to the current center
- var scale = this.getZoomScale(zoom),
- offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
- // don't animate if the zoom origin isn't within one screen from the current center, unless forced
- if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
- L.Util.requestAnimFrame(function () {
- this
- ._moveStart(true)
- ._animateZoom(center, zoom, true);
- }, this);
- return true;
- },
- _animateZoom: function (center, zoom, startAnim, noUpdate) {
- if (startAnim) {
- this._animatingZoom = true;
- // remember what center/zoom to set after animation
- this._animateToCenter = center;
- this._animateToZoom = zoom;
- L.DomUtil.addClass(this._mapPane, 'leaflet-zoom-anim');
- }
- // @event zoomanim: ZoomAnimEvent
- // Fired on every frame of a zoom animation
- this.fire('zoomanim', {
- center: center,
- zoom: zoom,
- noUpdate: noUpdate
- });
- // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693
- setTimeout(L.bind(this._onZoomTransitionEnd, this), 250);
- },
- _onZoomTransitionEnd: function () {
- if (!this._animatingZoom) { return; }
- L.DomUtil.removeClass(this._mapPane, 'leaflet-zoom-anim');
- this._animatingZoom = false;
- this._move(this._animateToCenter, this._animateToZoom);
- // This anim frame should prevent an obscure iOS webkit tile loading race condition.
- L.Util.requestAnimFrame(function () {
- this._moveEnd(true);
- }, this);
- }
- });
- // @section
- // @factory L.map(id: String, options?: Map options)
- // Instantiates a map object given the DOM ID of a `<div>` element
- // and optionally an object literal with `Map options`.
- //
- // @alternative
- // @factory L.map(el: HTMLElement, options?: Map options)
- // Instantiates a map object given an instance of a `<div>` HTML element
- // and optionally an object literal with `Map options`.
- L.map = function (id, options) {
- return new L.Map(id, options);
- };
- /*
- * @class Layer
- * @inherits Evented
- * @aka L.Layer
- * @aka ILayer
- *
- * A set of methods from the Layer base class that all Leaflet layers use.
- * Inherits all methods, options and events from `L.Evented`.
- *
- * @example
- *
- * ```js
- * var layer = L.Marker(latlng).addTo(map);
- * layer.addTo(map);
- * layer.remove();
- * ```
- *
- * @event add: Event
- * Fired after the layer is added to a map
- *
- * @event remove: Event
- * Fired after the layer is removed from a map
- */
- L.Layer = L.Evented.extend({
- // Classes extending `L.Layer` will inherit the following options:
- options: {
- // @option pane: String = 'overlayPane'
- // By default the layer will be added to the map's [overlay pane](#map-overlaypane). Overriding this option will cause the layer to be placed on another pane by default.
- pane: 'overlayPane',
- nonBubblingEvents: [], // Array of events that should not be bubbled to DOM parents (like the map),
- // @option attribution: String = null
- // String to be shown in the attribution control, describes the layer data, e.g. "© Mapbox".
- attribution: null,
- },
- /* @section
- * Classes extending `L.Layer` will inherit the following methods:
- *
- * @method addTo(map: Map): this
- * Adds the layer to the given map
- */
- addTo: function (map) {
- map.addLayer(this);
- return this;
- },
- // @method remove: this
- // Removes the layer from the map it is currently active on.
- remove: function () {
- return this.removeFrom(this._map || this._mapToAdd);
- },
- // @method removeFrom(map: Map): this
- // Removes the layer from the given map
- removeFrom: function (obj) {
- if (obj) {
- obj.removeLayer(this);
- }
- return this;
- },
- // @method getPane(name? : String): HTMLElement
- // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
- getPane: function (name) {
- return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
- },
- addInteractiveTarget: function (targetEl) {
- this._map._targets[L.stamp(targetEl)] = this;
- return this;
- },
- removeInteractiveTarget: function (targetEl) {
- delete this._map._targets[L.stamp(targetEl)];
- return this;
- },
- // @method getAttribution: String
- // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
- getAttribution: function () {
- return this.options.attribution;
- },
- _layerAdd: function (e) {
- var map = e.target;
- // check in case layer gets added and then removed before the map is ready
- if (!map.hasLayer(this)) { return; }
- this._map = map;
- this._zoomAnimated = map._zoomAnimated;
- if (this.getEvents) {
- var events = this.getEvents();
- map.on(events, this);
- this.once('remove', function () {
- map.off(events, this);
- }, this);
- }
- this.onAdd(map);
- if (this.getAttribution && this._map.attributionControl) {
- this._map.attributionControl.addAttribution(this.getAttribution());
- }
- this.fire('add');
- map.fire('layeradd', {layer: this});
- }
- });
- /* @section Extension methods
- * @uninheritable
- *
- * Every layer should extend from `L.Layer` and (re-)implement the following methods.
- *
- * @method onAdd(map: Map): this
- * Should contain code that creates DOM elements for the layer, adds them to `map panes` where they should belong and puts listeners on relevant map events. Called on [`map.addLayer(layer)`](#map-addlayer).
- *
- * @method onRemove(map: Map): this
- * Should contain all clean up code that removes the layer's elements from the DOM and removes listeners previously added in [`onAdd`](#layer-onadd). Called on [`map.removeLayer(layer)`](#map-removelayer).
- *
- * @method getEvents(): Object
- * This optional method should return an object like `{ viewreset: this._reset }` for [`addEventListener`](#evented-addeventlistener). The event handlers in this object will be automatically added and removed from the map with your layer.
- *
- * @method getAttribution(): String
- * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
- *
- * @method beforeAdd(map: Map): this
- * Optional method. Called on [`map.addLayer(layer)`](#map-addlayer), before the layer is added to the map, before events are initialized, without waiting until the map is in a usable state. Use for early initialization only.
- */
- /* @namespace Map
- * @section Layer events
- *
- * @event layeradd: LayerEvent
- * Fired when a new layer is added to the map.
- *
- * @event layerremove: LayerEvent
- * Fired when some layer is removed from the map
- *
- * @section Methods for Layers and Controls
- */
- L.Map.include({
- // @method addLayer(layer: Layer): this
- // Adds the given layer to the map
- addLayer: function (layer) {
- var id = L.stamp(layer);
- if (this._layers[id]) { return this; }
- this._layers[id] = layer;
- layer._mapToAdd = this;
- if (layer.beforeAdd) {
- layer.beforeAdd(this);
- }
- this.whenReady(layer._layerAdd, layer);
- return this;
- },
- // @method removeLayer(layer: Layer): this
- // Removes the given layer from the map.
- removeLayer: function (layer) {
- var id = L.stamp(layer);
- if (!this._layers[id]) { return this; }
- if (this._loaded) {
- layer.onRemove(this);
- }
- if (layer.getAttribution && this.attributionControl) {
- this.attributionControl.removeAttribution(layer.getAttribution());
- }
- delete this._layers[id];
- if (this._loaded) {
- this.fire('layerremove', {layer: layer});
- layer.fire('remove');
- }
- layer._map = layer._mapToAdd = null;
- return this;
- },
- // @method hasLayer(layer: Layer): Boolean
- // Returns `true` if the given layer is currently added to the map
- hasLayer: function (layer) {
- return !!layer && (L.stamp(layer) in this._layers);
- },
- /* @method eachLayer(fn: Function, context?: Object): this
- * Iterates over the layers of the map, optionally specifying context of the iterator function.
- * ```
- * map.eachLayer(function(layer){
- * layer.bindPopup('Hello');
- * });
- * ```
- */
- eachLayer: function (method, context) {
- for (var i in this._layers) {
- method.call(context, this._layers[i]);
- }
- return this;
- },
- _addLayers: function (layers) {
- layers = layers ? (L.Util.isArray(layers) ? layers : [layers]) : [];
- for (var i = 0, len = layers.length; i < len; i++) {
- this.addLayer(layers[i]);
- }
- },
- _addZoomLimit: function (layer) {
- if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
- this._zoomBoundLayers[L.stamp(layer)] = layer;
- this._updateZoomLevels();
- }
- },
- _removeZoomLimit: function (layer) {
- var id = L.stamp(layer);
- if (this._zoomBoundLayers[id]) {
- delete this._zoomBoundLayers[id];
- this._updateZoomLevels();
- }
- },
- _updateZoomLevels: function () {
- var minZoom = Infinity,
- maxZoom = -Infinity,
- oldZoomSpan = this._getZoomSpan();
- for (var i in this._zoomBoundLayers) {
- var options = this._zoomBoundLayers[i].options;
- minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
- maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
- }
- this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
- this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
- // @section Map state change events
- // @event zoomlevelschange: Event
- // Fired when the number of zoomlevels on the map is changed due
- // to adding or removing a layer.
- if (oldZoomSpan !== this._getZoomSpan()) {
- this.fire('zoomlevelschange');
- }
- if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
- this.setZoom(this._layersMaxZoom);
- }
- if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
- this.setZoom(this._layersMinZoom);
- }
- }
- });
- /*
- * @namespace DomEvent
- * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
- */
- // Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
- var eventsKey = '_leaflet_events';
- L.DomEvent = {
- // @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
- // Adds a listener function (`fn`) to a particular DOM event type of the
- // element `el`. You can optionally specify the context of the listener
- // (object the `this` keyword will point to). You can also pass several
- // space-separated types (e.g. `'click dblclick'`).
- // @alternative
- // @function on(el: HTMLElement, eventMap: Object, context?: Object): this
- // Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
- on: function (obj, types, fn, context) {
- if (typeof types === 'object') {
- for (var type in types) {
- this._on(obj, type, types[type], fn);
- }
- } else {
- types = L.Util.splitWords(types);
- for (var i = 0, len = types.length; i < len; i++) {
- this._on(obj, types[i], fn, context);
- }
- }
- return this;
- },
- // @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
- // Removes a previously added listener function. If no function is specified,
- // it will remove all the listeners of that particular DOM event from the element.
- // Note that if you passed a custom context to on, you must pass the same
- // context to `off` in order to remove the listener.
- // @alternative
- // @function off(el: HTMLElement, eventMap: Object, context?: Object): this
- // Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
- off: function (obj, types, fn, context) {
- if (typeof types === 'object') {
- for (var type in types) {
- this._off(obj, type, types[type], fn);
- }
- } else {
- types = L.Util.splitWords(types);
- for (var i = 0, len = types.length; i < len; i++) {
- this._off(obj, types[i], fn, context);
- }
- }
- return this;
- },
- _on: function (obj, type, fn, context) {
- var id = type + L.stamp(fn) + (context ? '_' + L.stamp(context) : '');
- if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
- var handler = function (e) {
- return fn.call(context || obj, e || window.event);
- };
- var originalHandler = handler;
- if (L.Browser.pointer && type.indexOf('touch') === 0) {
- this.addPointerListener(obj, type, handler, id);
- } else if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener) {
- this.addDoubleTapListener(obj, handler, id);
- } else if ('addEventListener' in obj) {
- if (type === 'mousewheel') {
- obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
- } else if ((type === 'mouseenter') || (type === 'mouseleave')) {
- handler = function (e) {
- e = e || window.event;
- if (L.DomEvent._isExternalTarget(obj, e)) {
- originalHandler(e);
- }
- };
- obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false);
- } else {
- if (type === 'click' && L.Browser.android) {
- handler = function (e) {
- return L.DomEvent._filterClick(e, originalHandler);
- };
- }
- obj.addEventListener(type, handler, false);
- }
- } else if ('attachEvent' in obj) {
- obj.attachEvent('on' + type, handler);
- }
- obj[eventsKey] = obj[eventsKey] || {};
- obj[eventsKey][id] = handler;
- return this;
- },
- _off: function (obj, type, fn, context) {
- var id = type + L.stamp(fn) + (context ? '_' + L.stamp(context) : ''),
- handler = obj[eventsKey] && obj[eventsKey][id];
- if (!handler) { return this; }
- if (L.Browser.pointer && type.indexOf('touch') === 0) {
- this.removePointerListener(obj, type, id);
- } else if (L.Browser.touch && (type === 'dblclick') && this.removeDoubleTapListener) {
- this.removeDoubleTapListener(obj, id);
- } else if ('removeEventListener' in obj) {
- if (type === 'mousewheel') {
- obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
- } else {
- obj.removeEventListener(
- type === 'mouseenter' ? 'mouseover' :
- type === 'mouseleave' ? 'mouseout' : type, handler, false);
- }
- } else if ('detachEvent' in obj) {
- obj.detachEvent('on' + type, handler);
- }
- obj[eventsKey][id] = null;
- return this;
- },
- // @function stopPropagation(ev: DOMEvent): this
- // Stop the given event from propagation to parent elements. Used inside the listener functions:
- // ```js
- // L.DomEvent.on(div, 'click', function (ev) {
- // L.DomEvent.stopPropagation(ev);
- // });
- // ```
- stopPropagation: function (e) {
- if (e.stopPropagation) {
- e.stopPropagation();
- } else if (e.originalEvent) { // In case of Leaflet event.
- e.originalEvent._stopped = true;
- } else {
- e.cancelBubble = true;
- }
- L.DomEvent._skipped(e);
- return this;
- },
- // @function disableScrollPropagation(el: HTMLElement): this
- // Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants).
- disableScrollPropagation: function (el) {
- return L.DomEvent.on(el, 'mousewheel', L.DomEvent.stopPropagation);
- },
- // @function disableClickPropagation(el: HTMLElement): this
- // Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`,
- // `'mousedown'` and `'touchstart'` events (plus browser variants).
- disableClickPropagation: function (el) {
- var stop = L.DomEvent.stopPropagation;
- L.DomEvent.on(el, L.Draggable.START.join(' '), stop);
- return L.DomEvent.on(el, {
- click: L.DomEvent._fakeStop,
- dblclick: stop
- });
- },
- // @function preventDefault(ev: DOMEvent): this
- // Prevents the default action of the DOM Event `ev` from happening (such as
- // following a link in the href of the a element, or doing a POST request
- // with page reload when a `<form>` is submitted).
- // Use it inside listener functions.
- preventDefault: function (e) {
- if (e.preventDefault) {
- e.preventDefault();
- } else {
- e.returnValue = false;
- }
- return this;
- },
- // @function stop(ev): this
- // Does `stopPropagation` and `preventDefault` at the same time.
- stop: function (e) {
- return L.DomEvent
- .preventDefault(e)
- .stopPropagation(e);
- },
- // @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
- // Gets normalized mouse position from a DOM event relative to the
- // `container` or to the whole page if not specified.
- getMousePosition: function (e, container) {
- if (!container) {
- return new L.Point(e.clientX, e.clientY);
- }
- var rect = container.getBoundingClientRect();
- return new L.Point(
- e.clientX - rect.left - container.clientLeft,
- e.clientY - rect.top - container.clientTop);
- },
- // Chrome on Win scrolls double the pixels as in other platforms (see #4538),
- // and Firefox scrolls device pixels, not CSS pixels
- _wheelPxFactor: (L.Browser.win && L.Browser.chrome) ? 2 :
- L.Browser.gecko ? window.devicePixelRatio :
- 1,
- // @function getWheelDelta(ev: DOMEvent): Number
- // Gets normalized wheel delta from a mousewheel DOM event, in vertical
- // pixels scrolled (negative if scrolling down).
- // Events from pointing devices without precise scrolling are mapped to
- // a best guess of 60 pixels.
- getWheelDelta: function (e) {
- return (L.Browser.edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
- (e.deltaY && e.deltaMode === 0) ? -e.deltaY / L.DomEvent._wheelPxFactor : // Pixels
- (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
- (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
- (e.deltaX || e.deltaZ) ? 0 : // Skip horizontal/depth wheel events
- e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
- (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
- e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
- 0;
- },
- _skipEvents: {},
- _fakeStop: function (e) {
- // fakes stopPropagation by setting a special event flag, checked/reset with L.DomEvent._skipped(e)
- L.DomEvent._skipEvents[e.type] = true;
- },
- _skipped: function (e) {
- var skipped = this._skipEvents[e.type];
- // reset when checking, as it's only used in map container and propagates outside of the map
- this._skipEvents[e.type] = false;
- return skipped;
- },
- // check if element really left/entered the event target (for mouseenter/mouseleave)
- _isExternalTarget: function (el, e) {
- var related = e.relatedTarget;
- if (!related) { return true; }
- try {
- while (related && (related !== el)) {
- related = related.parentNode;
- }
- } catch (err) {
- return false;
- }
- return (related !== el);
- },
- // this is a horrible workaround for a bug in Android where a single touch triggers two click events
- _filterClick: function (e, handler) {
- var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)),
- elapsed = L.DomEvent._lastClick && (timeStamp - L.DomEvent._lastClick);
- // are they closer together than 500ms yet more than 100ms?
- // Android typically triggers them ~300ms apart while multiple listeners
- // on the same event should be triggered far faster;
- // or check if click is simulated on the element, and if it is, reject any non-simulated events
- if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {
- L.DomEvent.stop(e);
- return;
- }
- L.DomEvent._lastClick = timeStamp;
- handler(e);
- }
- };
- // @function addListener(…): this
- // Alias to [`L.DomEvent.on`](#domevent-on)
- L.DomEvent.addListener = L.DomEvent.on;
- // @function removeListener(…): this
- // Alias to [`L.DomEvent.off`](#domevent-off)
- L.DomEvent.removeListener = L.DomEvent.off;
- /*
- * @class PosAnimation
- * @aka L.PosAnimation
- * @inherits Evented
- * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
- *
- * @example
- * ```js
- * var fx = new L.PosAnimation();
- * fx.run(el, [300, 500], 0.5);
- * ```
- *
- * @constructor L.PosAnimation()
- * Creates a `PosAnimation` object.
- *
- */
- L.PosAnimation = L.Evented.extend({
- // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number)
- // Run an animation of a given element to a new position, optionally setting
- // duration in seconds (`0.25` by default) and easing linearity factor (3rd
- // argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1),
- // `0.5` by default).
- run: function (el, newPos, duration, easeLinearity) {
- this.stop();
- this._el = el;
- this._inProgress = true;
- this._duration = duration || 0.25;
- this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
- this._startPos = L.DomUtil.getPosition(el);
- this._offset = newPos.subtract(this._startPos);
- this._startTime = +new Date();
- // @event start: Event
- // Fired when the animation starts
- this.fire('start');
- this._animate();
- },
- // @method stop()
- // Stops the animation (if currently running).
- stop: function () {
- if (!this._inProgress) { return; }
- this._step(true);
- this._complete();
- },
- _animate: function () {
- // animation loop
- this._animId = L.Util.requestAnimFrame(this._animate, this);
- this._step();
- },
- _step: function (round) {
- var elapsed = (+new Date()) - this._startTime,
- duration = this._duration * 1000;
- if (elapsed < duration) {
- this._runFrame(this._easeOut(elapsed / duration), round);
- } else {
- this._runFrame(1);
- this._complete();
- }
- },
- _runFrame: function (progress, round) {
- var pos = this._startPos.add(this._offset.multiplyBy(progress));
- if (round) {
- pos._round();
- }
- L.DomUtil.setPosition(this._el, pos);
- // @event step: Event
- // Fired continuously during the animation.
- this.fire('step');
- },
- _complete: function () {
- L.Util.cancelAnimFrame(this._animId);
- this._inProgress = false;
- // @event end: Event
- // Fired when the animation ends.
- this.fire('end');
- },
- _easeOut: function (t) {
- return 1 - Math.pow(1 - t, this._easeOutPower);
- }
- });
- /*
- * @namespace Projection
- * @projection L.Projection.Mercator
- *
- * Elliptical Mercator projection — more complex than Spherical Mercator. Takes into account that Earth is a geoid, not a perfect sphere. Used by the EPSG:3395 CRS.
- */
- L.Projection.Mercator = {
- R: 6378137,
- R_MINOR: 6356752.314245179,
- bounds: L.bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]),
- project: function (latlng) {
- var d = Math.PI / 180,
- r = this.R,
- y = latlng.lat * d,
- tmp = this.R_MINOR / r,
- e = Math.sqrt(1 - tmp * tmp),
- con = e * Math.sin(y);
- var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
- y = -r * Math.log(Math.max(ts, 1E-10));
- return new L.Point(latlng.lng * d * r, y);
- },
- unproject: function (point) {
- var d = 180 / Math.PI,
- r = this.R,
- tmp = this.R_MINOR / r,
- e = Math.sqrt(1 - tmp * tmp),
- ts = Math.exp(-point.y / r),
- phi = Math.PI / 2 - 2 * Math.atan(ts);
- for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
- con = e * Math.sin(phi);
- con = Math.pow((1 - con) / (1 + con), e / 2);
- dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
- phi += dphi;
- }
- return new L.LatLng(phi * d, point.x * d / r);
- }
- };
- /*
- * @namespace CRS
- * @crs L.CRS.EPSG3395
- *
- * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection.
- */
- L.CRS.EPSG3395 = L.extend({}, L.CRS.Earth, {
- code: 'EPSG:3395',
- projection: L.Projection.Mercator,
- transformation: (function () {
- var scale = 0.5 / (Math.PI * L.Projection.Mercator.R);
- return new L.Transformation(scale, 0.5, -scale, 0.5);
- }())
- });
- /*
- * @class GridLayer
- * @inherits Layer
- * @aka L.GridLayer
- *
- * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`.
- * GridLayer can be extended to create a tiled grid of HTML elements like `<canvas>`, `<img>` or `<div>`. GridLayer will handle creating and animating these DOM elements for you.
- *
- *
- * @section Synchronous usage
- * @example
- *
- * To create a custom layer, extend GridLayer and implement the `createTile()` method, which will be passed a `Point` object with the `x`, `y`, and `z` (zoom level) coordinates to draw your tile.
- *
- * ```js
- * var CanvasLayer = L.GridLayer.extend({
- * createTile: function(coords){
- * // create a <canvas> element for drawing
- * var tile = L.DomUtil.create('canvas', 'leaflet-tile');
- *
- * // setup tile width and height according to the options
- * var size = this.getTileSize();
- * tile.width = size.x;
- * tile.height = size.y;
- *
- * // get a canvas context and draw something on it using coords.x, coords.y and coords.z
- * var ctx = tile.getContext('2d');
- *
- * // return the tile so it can be rendered on screen
- * return tile;
- * }
- * });
- * ```
- *
- * @section Asynchronous usage
- * @example
- *
- * Tile creation can also be asynchronous, this is useful when using a third-party drawing library. Once the tile is finished drawing it can be passed to the `done()` callback.
- *
- * ```js
- * var CanvasLayer = L.GridLayer.extend({
- * createTile: function(coords, done){
- * var error;
- *
- * // create a <canvas> element for drawing
- * var tile = L.DomUtil.create('canvas', 'leaflet-tile');
- *
- * // setup tile width and height according to the options
- * var size = this.getTileSize();
- * tile.width = size.x;
- * tile.height = size.y;
- *
- * // draw something asynchronously and pass the tile to the done() callback
- * setTimeout(function() {
- * done(error, tile);
- * }, 1000);
- *
- * return tile;
- * }
- * });
- * ```
- *
- * @section
- */
- L.GridLayer = L.Layer.extend({
- // @section
- // @aka GridLayer options
- options: {
- // @option tileSize: Number|Point = 256
- // Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise.
- tileSize: 256,
- // @option opacity: Number = 1.0
- // Opacity of the tiles. Can be used in the `createTile()` function.
- opacity: 1,
- // @option updateWhenIdle: Boolean = depends
- // If `false`, new tiles are loaded during panning, otherwise only after it (for better performance). `true` by default on mobile browsers, otherwise `false`.
- updateWhenIdle: L.Browser.mobile,
- // @option updateWhenZooming: Boolean = true
- // By default, a smooth zoom animation (during a [touch zoom](#map-touchzoom) or a [`flyTo()`](#map-flyto)) will update grid layers every integer zoom level. Setting this option to `false` will update the grid layer only when the smooth animation ends.
- updateWhenZooming: true,
- // @option updateInterval: Number = 200
- // Tiles will not update more than once every `updateInterval` milliseconds when panning.
- updateInterval: 200,
- // @option zIndex: Number = 1
- // The explicit zIndex of the tile layer.
- zIndex: 1,
- // @option bounds: LatLngBounds = undefined
- // If set, tiles will only be loaded inside the set `LatLngBounds`.
- bounds: null,
- // @option minZoom: Number = 0
- // The minimum zoom level that tiles will be loaded at. By default the entire map.
- minZoom: 0,
- // @option maxZoom: Number = undefined
- // The maximum zoom level that tiles will be loaded at.
- maxZoom: undefined,
- // @option noWrap: Boolean = false
- // Whether the layer is wrapped around the antimeridian. If `true`, the
- // GridLayer will only be displayed once at low zoom levels. Has no
- // effect when the [map CRS](#map-crs) doesn't wrap around.
- noWrap: false,
- // @option pane: String = 'tilePane'
- // `Map pane` where the grid layer will be added.
- pane: 'tilePane',
- // @option className: String = ''
- // A custom class name to assign to the tile layer. Empty by default.
- className: '',
- // @option keepBuffer: Number = 2
- // When panning the map, keep this many rows and columns of tiles before unloading them.
- keepBuffer: 2
- },
- initialize: function (options) {
- L.setOptions(this, options);
- },
- onAdd: function () {
- this._initContainer();
- this._levels = {};
- this._tiles = {};
- this._resetView();
- this._update();
- },
- beforeAdd: function (map) {
- map._addZoomLimit(this);
- },
- onRemove: function (map) {
- this._removeAllTiles();
- L.DomUtil.remove(this._container);
- map._removeZoomLimit(this);
- this._container = null;
- this._tileZoom = null;
- },
- // @method bringToFront: this
- // Brings the tile layer to the top of all tile layers.
- bringToFront: function () {
- if (this._map) {
- L.DomUtil.toFront(this._container);
- this._setAutoZIndex(Math.max);
- }
- return this;
- },
- // @method bringToBack: this
- // Brings the tile layer to the bottom of all tile layers.
- bringToBack: function () {
- if (this._map) {
- L.DomUtil.toBack(this._container);
- this._setAutoZIndex(Math.min);
- }
- return this;
- },
- // @method getContainer: HTMLElement
- // Returns the HTML element that contains the tiles for this layer.
- getContainer: function () {
- return this._container;
- },
- // @method setOpacity(opacity: Number): this
- // Changes the [opacity](#gridlayer-opacity) of the grid layer.
- setOpacity: function (opacity) {
- this.options.opacity = opacity;
- this._updateOpacity();
- return this;
- },
- // @method setZIndex(zIndex: Number): this
- // Changes the [zIndex](#gridlayer-zindex) of the grid layer.
- setZIndex: function (zIndex) {
- this.options.zIndex = zIndex;
- this._updateZIndex();
- return this;
- },
- // @method isLoading: Boolean
- // Returns `true` if any tile in the grid layer has not finished loading.
- isLoading: function () {
- return this._loading;
- },
- // @method redraw: this
- // Causes the layer to clear all the tiles and request them again.
- redraw: function () {
- if (this._map) {
- this._removeAllTiles();
- this._update();
- }
- return this;
- },
- getEvents: function () {
- var events = {
- viewprereset: this._invalidateAll,
- viewreset: this._resetView,
- zoom: this._resetView,
- moveend: this._onMoveEnd
- };
- if (!this.options.updateWhenIdle) {
- // update tiles on move, but not more often than once per given interval
- if (!this._onMove) {
- this._onMove = L.Util.throttle(this._onMoveEnd, this.options.updateInterval, this);
- }
- events.move = this._onMove;
- }
- if (this._zoomAnimated) {
- events.zoomanim = this._animateZoom;
- }
- return events;
- },
- // @section Extension methods
- // Layers extending `GridLayer` shall reimplement the following method.
- // @method createTile(coords: Object, done?: Function): HTMLElement
- // Called only internally, must be overriden by classes extending `GridLayer`.
- // Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback
- // is specified, it must be called when the tile has finished loading and drawing.
- createTile: function () {
- return document.createElement('div');
- },
- // @section
- // @method getTileSize: Point
- // Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method.
- getTileSize: function () {
- var s = this.options.tileSize;
- return s instanceof L.Point ? s : new L.Point(s, s);
- },
- _updateZIndex: function () {
- if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) {
- this._container.style.zIndex = this.options.zIndex;
- }
- },
- _setAutoZIndex: function (compare) {
- // go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back)
- var layers = this.getPane().children,
- edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min
- for (var i = 0, len = layers.length, zIndex; i < len; i++) {
- zIndex = layers[i].style.zIndex;
- if (layers[i] !== this._container && zIndex) {
- edgeZIndex = compare(edgeZIndex, +zIndex);
- }
- }
- if (isFinite(edgeZIndex)) {
- this.options.zIndex = edgeZIndex + compare(-1, 1);
- this._updateZIndex();
- }
- },
- _updateOpacity: function () {
- if (!this._map) { return; }
- // IE doesn't inherit filter opacity properly, so we're forced to set it on tiles
- if (L.Browser.ielt9) { return; }
- L.DomUtil.setOpacity(this._container, this.options.opacity);
- var now = +new Date(),
- nextFrame = false,
- willPrune = false;
- for (var key in this._tiles) {
- var tile = this._tiles[key];
- if (!tile.current || !tile.loaded) { continue; }
- var fade = Math.min(1, (now - tile.loaded) / 200);
- L.DomUtil.setOpacity(tile.el, fade);
- if (fade < 1) {
- nextFrame = true;
- } else {
- if (tile.active) { willPrune = true; }
- tile.active = true;
- }
- }
- if (willPrune && !this._noPrune) { this._pruneTiles(); }
- if (nextFrame) {
- L.Util.cancelAnimFrame(this._fadeFrame);
- this._fadeFrame = L.Util.requestAnimFrame(this._updateOpacity, this);
- }
- },
- _initContainer: function () {
- if (this._container) { return; }
- this._container = L.DomUtil.create('div', 'leaflet-layer ' + (this.options.className || ''));
- this._updateZIndex();
- if (this.options.opacity < 1) {
- this._updateOpacity();
- }
- this.getPane().appendChild(this._container);
- },
- _updateLevels: function () {
- var zoom = this._tileZoom,
- maxZoom = this.options.maxZoom;
- if (zoom === undefined) { return undefined; }
- for (var z in this._levels) {
- if (this._levels[z].el.children.length || z === zoom) {
- this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z);
- } else {
- L.DomUtil.remove(this._levels[z].el);
- this._removeTilesAtZoom(z);
- delete this._levels[z];
- }
- }
- var level = this._levels[zoom],
- map = this._map;
- if (!level) {
- level = this._levels[zoom] = {};
- level.el = L.DomUtil.create('div', 'leaflet-tile-container leaflet-zoom-animated', this._container);
- level.el.style.zIndex = maxZoom;
- level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round();
- level.zoom = zoom;
- this._setZoomTransform(level, map.getCenter(), map.getZoom());
- // force the browser to consider the newly added element for transition
- L.Util.falseFn(level.el.offsetWidth);
- }
- this._level = level;
- return level;
- },
- _pruneTiles: function () {
- if (!this._map) {
- return;
- }
- var key, tile;
- var zoom = this._map.getZoom();
- if (zoom > this.options.maxZoom ||
- zoom < this.options.minZoom) {
- this._removeAllTiles();
- return;
- }
- for (key in this._tiles) {
- tile = this._tiles[key];
- tile.retain = tile.current;
- }
- for (key in this._tiles) {
- tile = this._tiles[key];
- if (tile.current && !tile.active) {
- var coords = tile.coords;
- if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
- this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
- }
- }
- }
- for (key in this._tiles) {
- if (!this._tiles[key].retain) {
- this._removeTile(key);
- }
- }
- },
- _removeTilesAtZoom: function (zoom) {
- for (var key in this._tiles) {
- if (this._tiles[key].coords.z !== zoom) {
- continue;
- }
- this._removeTile(key);
- }
- },
- _removeAllTiles: function () {
- for (var key in this._tiles) {
- this._removeTile(key);
- }
- },
- _invalidateAll: function () {
- for (var z in this._levels) {
- L.DomUtil.remove(this._levels[z].el);
- delete this._levels[z];
- }
- this._removeAllTiles();
- this._tileZoom = null;
- },
- _retainParent: function (x, y, z, minZoom) {
- var x2 = Math.floor(x / 2),
- y2 = Math.floor(y / 2),
- z2 = z - 1,
- coords2 = new L.Point(+x2, +y2);
- coords2.z = +z2;
- var key = this._tileCoordsToKey(coords2),
- tile = this._tiles[key];
- if (tile && tile.active) {
- tile.retain = true;
- return true;
- } else if (tile && tile.loaded) {
- tile.retain = true;
- }
- if (z2 > minZoom) {
- return this._retainParent(x2, y2, z2, minZoom);
- }
- return false;
- },
- _retainChildren: function (x, y, z, maxZoom) {
- for (var i = 2 * x; i < 2 * x + 2; i++) {
- for (var j = 2 * y; j < 2 * y + 2; j++) {
- var coords = new L.Point(i, j);
- coords.z = z + 1;
- var key = this._tileCoordsToKey(coords),
- tile = this._tiles[key];
- if (tile && tile.active) {
- tile.retain = true;
- continue;
- } else if (tile && tile.loaded) {
- tile.retain = true;
- }
- if (z + 1 < maxZoom) {
- this._retainChildren(i, j, z + 1, maxZoom);
- }
- }
- }
- },
- _resetView: function (e) {
- var animating = e && (e.pinch || e.flyTo);
- this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating);
- },
- _animateZoom: function (e) {
- this._setView(e.center, e.zoom, true, e.noUpdate);
- },
- _setView: function (center, zoom, noPrune, noUpdate) {
- var tileZoom = Math.round(zoom);
- if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) ||
- (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) {
- tileZoom = undefined;
- }
- var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
- if (!noUpdate || tileZoomChanged) {
- this._tileZoom = tileZoom;
- if (this._abortLoading) {
- this._abortLoading();
- }
- this._updateLevels();
- this._resetGrid();
- if (tileZoom !== undefined) {
- this._update(center);
- }
- if (!noPrune) {
- this._pruneTiles();
- }
- // Flag to prevent _updateOpacity from pruning tiles during
- // a zoom anim or a pinch gesture
- this._noPrune = !!noPrune;
- }
- this._setZoomTransforms(center, zoom);
- },
- _setZoomTransforms: function (center, zoom) {
- for (var i in this._levels) {
- this._setZoomTransform(this._levels[i], center, zoom);
- }
- },
- _setZoomTransform: function (level, center, zoom) {
- var scale = this._map.getZoomScale(zoom, level.zoom),
- translate = level.origin.multiplyBy(scale)
- .subtract(this._map._getNewPixelOrigin(center, zoom)).round();
- if (L.Browser.any3d) {
- L.DomUtil.setTransform(level.el, translate, scale);
- } else {
- L.DomUtil.setPosition(level.el, translate);
- }
- },
- _resetGrid: function () {
- var map = this._map,
- crs = map.options.crs,
- tileSize = this._tileSize = this.getTileSize(),
- tileZoom = this._tileZoom;
- var bounds = this._map.getPixelWorldBounds(this._tileZoom);
- if (bounds) {
- this._globalTileRange = this._pxBoundsToTileRange(bounds);
- }
- this._wrapX = crs.wrapLng && !this.options.noWrap && [
- Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x),
- Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y)
- ];
- this._wrapY = crs.wrapLat && !this.options.noWrap && [
- Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x),
- Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y)
- ];
- },
- _onMoveEnd: function () {
- if (!this._map || this._map._animatingZoom) { return; }
- this._update();
- },
- _getTiledPixelBounds: function (center) {
- var map = this._map,
- mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
- scale = map.getZoomScale(mapZoom, this._tileZoom),
- pixelCenter = map.project(center, this._tileZoom).floor(),
- halfSize = map.getSize().divideBy(scale * 2);
- return new L.Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
- },
- // Private method to load tiles in the grid's active zoom level according to map bounds
- _update: function (center) {
- var map = this._map;
- if (!map) { return; }
- var zoom = map.getZoom();
- if (center === undefined) { center = map.getCenter(); }
- if (this._tileZoom === undefined) { return; } // if out of minzoom/maxzoom
- var pixelBounds = this._getTiledPixelBounds(center),
- tileRange = this._pxBoundsToTileRange(pixelBounds),
- tileCenter = tileRange.getCenter(),
- queue = [],
- margin = this.options.keepBuffer,
- noPruneRange = new L.Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
- tileRange.getTopRight().add([margin, -margin]));
- for (var key in this._tiles) {
- var c = this._tiles[key].coords;
- if (c.z !== this._tileZoom || !noPruneRange.contains(L.point(c.x, c.y))) {
- this._tiles[key].current = false;
- }
- }
- // _update just loads more tiles. If the tile zoom level differs too much
- // from the map's, let _setView reset levels and prune old tiles.
- if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; }
- // create a queue of coordinates to load tiles from
- for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
- for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
- var coords = new L.Point(i, j);
- coords.z = this._tileZoom;
- if (!this._isValidTile(coords)) { continue; }
- var tile = this._tiles[this._tileCoordsToKey(coords)];
- if (tile) {
- tile.current = true;
- } else {
- queue.push(coords);
- }
- }
- }
- // sort tile queue to load tiles in order of their distance to center
- queue.sort(function (a, b) {
- return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
- });
- if (queue.length !== 0) {
- // if it's the first batch of tiles to load
- if (!this._loading) {
- this._loading = true;
- // @event loading: Event
- // Fired when the grid layer starts loading tiles.
- this.fire('loading');
- }
- // create DOM fragment to append tiles in one batch
- var fragment = document.createDocumentFragment();
- for (i = 0; i < queue.length; i++) {
- this._addTile(queue[i], fragment);
- }
- this._level.el.appendChild(fragment);
- }
- },
- _isValidTile: function (coords) {
- var crs = this._map.options.crs;
- if (!crs.infinite) {
- // don't load tile if it's out of bounds and not wrapped
- var bounds = this._globalTileRange;
- if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
- (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
- }
- if (!this.options.bounds) { return true; }
- // don't load tile if it doesn't intersect the bounds in options
- var tileBounds = this._tileCoordsToBounds(coords);
- return L.latLngBounds(this.options.bounds).overlaps(tileBounds);
- },
- _keyToBounds: function (key) {
- return this._tileCoordsToBounds(this._keyToTileCoords(key));
- },
- // converts tile coordinates to its geographical bounds
- _tileCoordsToBounds: function (coords) {
- var map = this._map,
- tileSize = this.getTileSize(),
- nwPoint = coords.scaleBy(tileSize),
- sePoint = nwPoint.add(tileSize),
- nw = map.unproject(nwPoint, coords.z),
- se = map.unproject(sePoint, coords.z);
- if (!this.options.noWrap) {
- nw = map.wrapLatLng(nw);
- se = map.wrapLatLng(se);
- }
- return new L.LatLngBounds(nw, se);
- },
- // converts tile coordinates to key for the tile cache
- _tileCoordsToKey: function (coords) {
- return coords.x + ':' + coords.y + ':' + coords.z;
- },
- // converts tile cache key to coordinates
- _keyToTileCoords: function (key) {
- var k = key.split(':'),
- coords = new L.Point(+k[0], +k[1]);
- coords.z = +k[2];
- return coords;
- },
- _removeTile: function (key) {
- var tile = this._tiles[key];
- if (!tile) { return; }
- L.DomUtil.remove(tile.el);
- delete this._tiles[key];
- // @event tileunload: TileEvent
- // Fired when a tile is removed (e.g. when a tile goes off the screen).
- this.fire('tileunload', {
- tile: tile.el,
- coords: this._keyToTileCoords(key)
- });
- },
- _initTile: function (tile) {
- L.DomUtil.addClass(tile, 'leaflet-tile');
- var tileSize = this.getTileSize();
- tile.style.width = tileSize.x + 'px';
- tile.style.height = tileSize.y + 'px';
- tile.onselectstart = L.Util.falseFn;
- tile.onmousemove = L.Util.falseFn;
- // update opacity on tiles in IE7-8 because of filter inheritance problems
- if (L.Browser.ielt9 && this.options.opacity < 1) {
- L.DomUtil.setOpacity(tile, this.options.opacity);
- }
- // without this hack, tiles disappear after zoom on Chrome for Android
- // https://github.com/Leaflet/Leaflet/issues/2078
- if (L.Browser.android && !L.Browser.android23) {
- tile.style.WebkitBackfaceVisibility = 'hidden';
- }
- },
- _addTile: function (coords, container) {
- var tilePos = this._getTilePos(coords),
- key = this._tileCoordsToKey(coords);
- var tile = this.createTile(this._wrapCoords(coords), L.bind(this._tileReady, this, coords));
- this._initTile(tile);
- // if createTile is defined with a second argument ("done" callback),
- // we know that tile is async and will be ready later; otherwise
- if (this.createTile.length < 2) {
- // mark tile as ready, but delay one frame for opacity animation to happen
- L.Util.requestAnimFrame(L.bind(this._tileReady, this, coords, null, tile));
- }
- L.DomUtil.setPosition(tile, tilePos);
- // save tile in cache
- this._tiles[key] = {
- el: tile,
- coords: coords,
- current: true
- };
- container.appendChild(tile);
- // @event tileloadstart: TileEvent
- // Fired when a tile is requested and starts loading.
- this.fire('tileloadstart', {
- tile: tile,
- coords: coords
- });
- },
- _tileReady: function (coords, err, tile) {
- if (!this._map) { return; }
- if (err) {
- // @event tileerror: TileErrorEvent
- // Fired when there is an error loading a tile.
- this.fire('tileerror', {
- error: err,
- tile: tile,
- coords: coords
- });
- }
- var key = this._tileCoordsToKey(coords);
- tile = this._tiles[key];
- if (!tile) { return; }
- tile.loaded = +new Date();
- if (this._map._fadeAnimated) {
- L.DomUtil.setOpacity(tile.el, 0);
- L.Util.cancelAnimFrame(this._fadeFrame);
- this._fadeFrame = L.Util.requestAnimFrame(this._updateOpacity, this);
- } else {
- tile.active = true;
- this._pruneTiles();
- }
- if (!err) {
- L.DomUtil.addClass(tile.el, 'leaflet-tile-loaded');
- // @event tileload: TileEvent
- // Fired when a tile loads.
- this.fire('tileload', {
- tile: tile.el,
- coords: coords
- });
- }
- if (this._noTilesToLoad()) {
- this._loading = false;
- // @event load: Event
- // Fired when the grid layer loaded all visible tiles.
- this.fire('load');
- if (L.Browser.ielt9 || !this._map._fadeAnimated) {
- L.Util.requestAnimFrame(this._pruneTiles, this);
- } else {
- // Wait a bit more than 0.2 secs (the duration of the tile fade-in)
- // to trigger a pruning.
- setTimeout(L.bind(this._pruneTiles, this), 250);
- }
- }
- },
- _getTilePos: function (coords) {
- return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
- },
- _wrapCoords: function (coords) {
- var newCoords = new L.Point(
- this._wrapX ? L.Util.wrapNum(coords.x, this._wrapX) : coords.x,
- this._wrapY ? L.Util.wrapNum(coords.y, this._wrapY) : coords.y);
- newCoords.z = coords.z;
- return newCoords;
- },
- _pxBoundsToTileRange: function (bounds) {
- var tileSize = this.getTileSize();
- return new L.Bounds(
- bounds.min.unscaleBy(tileSize).floor(),
- bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]));
- },
- _noTilesToLoad: function () {
- for (var key in this._tiles) {
- if (!this._tiles[key].loaded) { return false; }
- }
- return true;
- }
- });
- // @factory L.gridLayer(options?: GridLayer options)
- // Creates a new instance of GridLayer with the supplied options.
- L.gridLayer = function (options) {
- return new L.GridLayer(options);
- };
- /*
- * @class TileLayer
- * @inherits GridLayer
- * @aka L.TileLayer
- * Used to load and display tile layers on the map. Extends `GridLayer`.
- *
- * @example
- *
- * ```js
- * L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar'}).addTo(map);
- * ```
- *
- * @section URL template
- * @example
- *
- * A string of the following form:
- *
- * ```
- * 'http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png'
- * ```
- *
- * `{s}` means one of the available subdomains (used sequentially to help with browser parallel requests per domain limitation; subdomain values are specified in options; `a`, `b` or `c` by default, can be omitted), `{z}` — zoom level, `{x}` and `{y}` — tile coordinates. `{r}` can be used to add @2x to the URL to load retina tiles.
- *
- * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:
- *
- * ```
- * L.tileLayer('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
- * ```
- */
- L.TileLayer = L.GridLayer.extend({
- // @section
- // @aka TileLayer options
- options: {
- // @option minZoom: Number = 0
- // Minimum zoom number.
- minZoom: 0,
- // @option maxZoom: Number = 18
- // Maximum zoom number.
- maxZoom: 18,
- // @option maxNativeZoom: Number = null
- // Maximum zoom number the tile source has available. If it is specified,
- // the tiles on all zoom levels higher than `maxNativeZoom` will be loaded
- // from `maxNativeZoom` level and auto-scaled.
- maxNativeZoom: null,
- // @option minNativeZoom: Number = null
- // Minimum zoom number the tile source has available. If it is specified,
- // the tiles on all zoom levels lower than `minNativeZoom` will be loaded
- // from `minNativeZoom` level and auto-scaled.
- minNativeZoom: null,
- // @option subdomains: String|String[] = 'abc'
- // Subdomains of the tile service. Can be passed in the form of one string (where each letter is a subdomain name) or an array of strings.
- subdomains: 'abc',
- // @option errorTileUrl: String = ''
- // URL to the tile image to show in place of the tile that failed to load.
- errorTileUrl: '',
- // @option zoomOffset: Number = 0
- // The zoom number used in tile URLs will be offset with this value.
- zoomOffset: 0,
- // @option tms: Boolean = false
- // If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
- tms: false,
- // @option zoomReverse: Boolean = false
- // If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)
- zoomReverse: false,
- // @option detectRetina: Boolean = false
- // If `true` and user is on a retina display, it will request four tiles of half the specified size and a bigger zoom level in place of one to utilize the high resolution.
- detectRetina: false,
- // @option crossOrigin: Boolean = false
- // If true, all tiles will have their crossOrigin attribute set to ''. This is needed if you want to access tile pixel data.
- crossOrigin: false
- },
- initialize: function (url, options) {
- this._url = url;
- options = L.setOptions(this, options);
- // detecting retina displays, adjusting tileSize and zoom levels
- if (options.detectRetina && L.Browser.retina && options.maxZoom > 0) {
- options.tileSize = Math.floor(options.tileSize / 2);
- if (!options.zoomReverse) {
- options.zoomOffset++;
- options.maxZoom--;
- } else {
- options.zoomOffset--;
- options.minZoom++;
- }
- options.minZoom = Math.max(0, options.minZoom);
- }
- if (typeof options.subdomains === 'string') {
- options.subdomains = options.subdomains.split('');
- }
- // for https://github.com/Leaflet/Leaflet/issues/137
- if (!L.Browser.android) {
- this.on('tileunload', this._onTileRemove);
- }
- },
- // @method setUrl(url: String, noRedraw?: Boolean): this
- // Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
- setUrl: function (url, noRedraw) {
- this._url = url;
- if (!noRedraw) {
- this.redraw();
- }
- return this;
- },
- // @method createTile(coords: Object, done?: Function): HTMLElement
- // Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
- // to return an `<img>` HTML element with the appropiate image URL given `coords`. The `done`
- // callback is called when the tile has been loaded.
- createTile: function (coords, done) {
- var tile = document.createElement('img');
- L.DomEvent.on(tile, 'load', L.bind(this._tileOnLoad, this, done, tile));
- L.DomEvent.on(tile, 'error', L.bind(this._tileOnError, this, done, tile));
- if (this.options.crossOrigin) {
- tile.crossOrigin = '';
- }
- /*
- Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons
- http://www.w3.org/TR/WCAG20-TECHS/H67
- */
- tile.alt = '';
- /*
- Set role="presentation" to force screen readers to ignore this
- https://www.w3.org/TR/wai-aria/roles#textalternativecomputation
- */
- tile.setAttribute('role', 'presentation');
- tile.src = this.getTileUrl(coords);
- return tile;
- },
- // @section Extension methods
- // @uninheritable
- // Layers extending `TileLayer` might reimplement the following method.
- // @method getTileUrl(coords: Object): String
- // Called only internally, returns the URL for a tile given its coordinates.
- // Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.
- getTileUrl: function (coords) {
- var data = {
- r: L.Browser.retina ? '@2x' : '',
- s: this._getSubdomain(coords),
- x: coords.x,
- y: coords.y,
- z: this._getZoomForUrl()
- };
- if (this._map && !this._map.options.crs.infinite) {
- var invertedY = this._globalTileRange.max.y - coords.y;
- if (this.options.tms) {
- data['y'] = invertedY;
- }
- data['-y'] = invertedY;
- }
- return L.Util.template(this._url, L.extend(data, this.options));
- },
- _tileOnLoad: function (done, tile) {
- // For https://github.com/Leaflet/Leaflet/issues/3332
- if (L.Browser.ielt9) {
- setTimeout(L.bind(done, this, null, tile), 0);
- } else {
- done(null, tile);
- }
- },
- _tileOnError: function (done, tile, e) {
- var errorUrl = this.options.errorTileUrl;
- if (errorUrl) {
- tile.src = errorUrl;
- }
- done(e, tile);
- },
- getTileSize: function () {
- var map = this._map,
- tileSize = L.GridLayer.prototype.getTileSize.call(this),
- zoom = this._tileZoom + this.options.zoomOffset,
- minNativeZoom = this.options.minNativeZoom,
- maxNativeZoom = this.options.maxNativeZoom;
- // decrease tile size when scaling below minNativeZoom
- if (minNativeZoom !== null && zoom < minNativeZoom) {
- return tileSize.divideBy(map.getZoomScale(minNativeZoom, zoom)).round();
- }
- // increase tile size when scaling above maxNativeZoom
- if (maxNativeZoom !== null && zoom > maxNativeZoom) {
- return tileSize.divideBy(map.getZoomScale(maxNativeZoom, zoom)).round();
- }
- return tileSize;
- },
- _onTileRemove: function (e) {
- e.tile.onload = null;
- },
- _getZoomForUrl: function () {
- var zoom = this._tileZoom,
- maxZoom = this.options.maxZoom,
- zoomReverse = this.options.zoomReverse,
- zoomOffset = this.options.zoomOffset,
- minNativeZoom = this.options.minNativeZoom,
- maxNativeZoom = this.options.maxNativeZoom;
- if (zoomReverse) {
- zoom = maxZoom - zoom;
- }
- zoom += zoomOffset;
- if (minNativeZoom !== null && zoom < minNativeZoom) {
- return minNativeZoom;
- }
- if (maxNativeZoom !== null && zoom > maxNativeZoom) {
- return maxNativeZoom;
- }
- return zoom;
- },
- _getSubdomain: function (tilePoint) {
- var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
- return this.options.subdomains[index];
- },
- // stops loading all tiles in the background layer
- _abortLoading: function () {
- var i, tile;
- for (i in this._tiles) {
- if (this._tiles[i].coords.z !== this._tileZoom) {
- tile = this._tiles[i].el;
- tile.onload = L.Util.falseFn;
- tile.onerror = L.Util.falseFn;
- if (!tile.complete) {
- tile.src = L.Util.emptyImageUrl;
- L.DomUtil.remove(tile);
- }
- }
- }
- }
- });
- // @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
- // Instantiates a tile layer object given a `URL template` and optionally an options object.
- L.tileLayer = function (url, options) {
- return new L.TileLayer(url, options);
- };
- /*
- * @class TileLayer.WMS
- * @inherits TileLayer
- * @aka L.TileLayer.WMS
- * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
- *
- * @example
- *
- * ```js
- * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", {
- * layers: 'nexrad-n0r-900913',
- * format: 'image/png',
- * transparent: true,
- * attribution: "Weather data © 2012 IEM Nexrad"
- * });
- * ```
- */
- L.TileLayer.WMS = L.TileLayer.extend({
- // @section
- // @aka TileLayer.WMS options
- // If any custom options not documented here are used, they will be sent to the
- // WMS server as extra parameters in each request URL. This can be useful for
- // [non-standard vendor WMS parameters](http://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
- defaultWmsParams: {
- service: 'WMS',
- request: 'GetMap',
- // @option layers: String = ''
- // **(required)** Comma-separated list of WMS layers to show.
- layers: '',
- // @option styles: String = ''
- // Comma-separated list of WMS styles.
- styles: '',
- // @option format: String = 'image/jpeg'
- // WMS image format (use `'image/png'` for layers with transparency).
- format: 'image/jpeg',
- // @option transparent: Boolean = false
- // If `true`, the WMS service will return images with transparency.
- transparent: false,
- // @option version: String = '1.1.1'
- // Version of the WMS service to use
- version: '1.1.1'
- },
- options: {
- // @option crs: CRS = null
- // Coordinate Reference System to use for the WMS requests, defaults to
- // map CRS. Don't change this if you're not sure what it means.
- crs: null,
- // @option uppercase: Boolean = false
- // If `true`, WMS request parameter keys will be uppercase.
- uppercase: false
- },
- initialize: function (url, options) {
- this._url = url;
- var wmsParams = L.extend({}, this.defaultWmsParams);
- // all keys that are not TileLayer options go to WMS params
- for (var i in options) {
- if (!(i in this.options)) {
- wmsParams[i] = options[i];
- }
- }
- options = L.setOptions(this, options);
- wmsParams.width = wmsParams.height = options.tileSize * (options.detectRetina && L.Browser.retina ? 2 : 1);
- this.wmsParams = wmsParams;
- },
- onAdd: function (map) {
- this._crs = this.options.crs || map.options.crs;
- this._wmsVersion = parseFloat(this.wmsParams.version);
- var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
- this.wmsParams[projectionKey] = this._crs.code;
- L.TileLayer.prototype.onAdd.call(this, map);
- },
- getTileUrl: function (coords) {
- var tileBounds = this._tileCoordsToBounds(coords),
- nw = this._crs.project(tileBounds.getNorthWest()),
- se = this._crs.project(tileBounds.getSouthEast()),
- bbox = (this._wmsVersion >= 1.3 && this._crs === L.CRS.EPSG4326 ?
- [se.y, nw.x, nw.y, se.x] :
- [nw.x, se.y, se.x, nw.y]).join(','),
- url = L.TileLayer.prototype.getTileUrl.call(this, coords);
- return url +
- L.Util.getParamString(this.wmsParams, url, this.options.uppercase) +
- (this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox;
- },
- // @method setParams(params: Object, noRedraw?: Boolean): this
- // Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true).
- setParams: function (params, noRedraw) {
- L.extend(this.wmsParams, params);
- if (!noRedraw) {
- this.redraw();
- }
- return this;
- }
- });
- // @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options)
- // Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object.
- L.tileLayer.wms = function (url, options) {
- return new L.TileLayer.WMS(url, options);
- };
- /*
- * @class ImageOverlay
- * @aka L.ImageOverlay
- * @inherits Interactive layer
- *
- * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
- *
- * @example
- *
- * ```js
- * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
- * imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
- * L.imageOverlay(imageUrl, imageBounds).addTo(map);
- * ```
- */
- L.ImageOverlay = L.Layer.extend({
- // @section
- // @aka ImageOverlay options
- options: {
- // @option opacity: Number = 1.0
- // The opacity of the image overlay.
- opacity: 1,
- // @option alt: String = ''
- // Text for the `alt` attribute of the image (useful for accessibility).
- alt: '',
- // @option interactive: Boolean = false
- // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
- interactive: false,
- // @option crossOrigin: Boolean = false
- // If true, the image will have its crossOrigin attribute set to ''. This is needed if you want to access image pixel data.
- crossOrigin: false
- },
- initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
- this._url = url;
- this._bounds = L.latLngBounds(bounds);
- L.setOptions(this, options);
- },
- onAdd: function () {
- if (!this._image) {
- this._initImage();
- if (this.options.opacity < 1) {
- this._updateOpacity();
- }
- }
- if (this.options.interactive) {
- L.DomUtil.addClass(this._image, 'leaflet-interactive');
- this.addInteractiveTarget(this._image);
- }
- this.getPane().appendChild(this._image);
- this._reset();
- },
- onRemove: function () {
- L.DomUtil.remove(this._image);
- if (this.options.interactive) {
- this.removeInteractiveTarget(this._image);
- }
- },
- // @method setOpacity(opacity: Number): this
- // Sets the opacity of the overlay.
- setOpacity: function (opacity) {
- this.options.opacity = opacity;
- if (this._image) {
- this._updateOpacity();
- }
- return this;
- },
- setStyle: function (styleOpts) {
- if (styleOpts.opacity) {
- this.setOpacity(styleOpts.opacity);
- }
- return this;
- },
- // @method bringToFront(): this
- // Brings the layer to the top of all overlays.
- bringToFront: function () {
- if (this._map) {
- L.DomUtil.toFront(this._image);
- }
- return this;
- },
- // @method bringToBack(): this
- // Brings the layer to the bottom of all overlays.
- bringToBack: function () {
- if (this._map) {
- L.DomUtil.toBack(this._image);
- }
- return this;
- },
- // @method setUrl(url: String): this
- // Changes the URL of the image.
- setUrl: function (url) {
- this._url = url;
- if (this._image) {
- this._image.src = url;
- }
- return this;
- },
- setBounds: function (bounds) {
- this._bounds = bounds;
- if (this._map) {
- this._reset();
- }
- return this;
- },
- getEvents: function () {
- var events = {
- zoom: this._reset,
- viewreset: this._reset
- };
- if (this._zoomAnimated) {
- events.zoomanim = this._animateZoom;
- }
- return events;
- },
- getBounds: function () {
- return this._bounds;
- },
- getElement: function () {
- return this._image;
- },
- _initImage: function () {
- var img = this._image = L.DomUtil.create('img',
- 'leaflet-image-layer ' + (this._zoomAnimated ? 'leaflet-zoom-animated' : ''));
- img.onselectstart = L.Util.falseFn;
- img.onmousemove = L.Util.falseFn;
- img.onload = L.bind(this.fire, this, 'load');
- if (this.options.crossOrigin) {
- img.crossOrigin = '';
- }
- img.src = this._url;
- img.alt = this.options.alt;
- },
- _animateZoom: function (e) {
- var scale = this._map.getZoomScale(e.zoom),
- offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min;
- L.DomUtil.setTransform(this._image, offset, scale);
- },
- _reset: function () {
- var image = this._image,
- bounds = new L.Bounds(
- this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
- this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
- size = bounds.getSize();
- L.DomUtil.setPosition(image, bounds.min);
- image.style.width = size.x + 'px';
- image.style.height = size.y + 'px';
- },
- _updateOpacity: function () {
- L.DomUtil.setOpacity(this._image, this.options.opacity);
- }
- });
- // @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
- // Instantiates an image overlay object given the URL of the image and the
- // geographical bounds it is tied to.
- L.imageOverlay = function (url, bounds, options) {
- return new L.ImageOverlay(url, bounds, options);
- };
- /*
- * @class Icon
- * @aka L.Icon
- * @inherits Layer
- *
- * Represents an icon to provide when creating a marker.
- *
- * @example
- *
- * ```js
- * var myIcon = L.icon({
- * iconUrl: 'my-icon.png',
- * iconRetinaUrl: 'my-icon@2x.png',
- * iconSize: [38, 95],
- * iconAnchor: [22, 94],
- * popupAnchor: [-3, -76],
- * shadowUrl: 'my-icon-shadow.png',
- * shadowRetinaUrl: 'my-icon-shadow@2x.png',
- * shadowSize: [68, 95],
- * shadowAnchor: [22, 94]
- * });
- *
- * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
- * ```
- *
- * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default.
- *
- */
- L.Icon = L.Class.extend({
- /* @section
- * @aka Icon options
- *
- * @option iconUrl: String = null
- * **(required)** The URL to the icon image (absolute or relative to your script path).
- *
- * @option iconRetinaUrl: String = null
- * The URL to a retina sized version of the icon image (absolute or relative to your
- * script path). Used for Retina screen devices.
- *
- * @option iconSize: Point = null
- * Size of the icon image in pixels.
- *
- * @option iconAnchor: Point = null
- * The coordinates of the "tip" of the icon (relative to its top left corner). The icon
- * will be aligned so that this point is at the marker's geographical location. Centered
- * by default if size is specified, also can be set in CSS with negative margins.
- *
- * @option popupAnchor: Point = null
- * The coordinates of the point from which popups will "open", relative to the icon anchor.
- *
- * @option shadowUrl: String = null
- * The URL to the icon shadow image. If not specified, no shadow image will be created.
- *
- * @option shadowRetinaUrl: String = null
- *
- * @option shadowSize: Point = null
- * Size of the shadow image in pixels.
- *
- * @option shadowAnchor: Point = null
- * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same
- * as iconAnchor if not specified).
- *
- * @option className: String = ''
- * A custom class name to assign to both icon and shadow images. Empty by default.
- */
- initialize: function (options) {
- L.setOptions(this, options);
- },
- // @method createIcon(oldIcon?: HTMLElement): HTMLElement
- // Called internally when the icon has to be shown, returns a `<img>` HTML element
- // styled according to the options.
- createIcon: function (oldIcon) {
- return this._createIcon('icon', oldIcon);
- },
- // @method createShadow(oldIcon?: HTMLElement): HTMLElement
- // As `createIcon`, but for the shadow beneath it.
- createShadow: function (oldIcon) {
- return this._createIcon('shadow', oldIcon);
- },
- _createIcon: function (name, oldIcon) {
- var src = this._getIconUrl(name);
- if (!src) {
- if (name === 'icon') {
- throw new Error('iconUrl not set in Icon options (see the docs).');
- }
- return null;
- }
- var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null);
- this._setIconStyles(img, name);
- return img;
- },
- _setIconStyles: function (img, name) {
- var options = this.options;
- var sizeOption = options[name + 'Size'];
- if (typeof sizeOption === 'number') {
- sizeOption = [sizeOption, sizeOption];
- }
- var size = L.point(sizeOption),
- anchor = L.point(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
- size && size.divideBy(2, true));
- img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
- if (anchor) {
- img.style.marginLeft = (-anchor.x) + 'px';
- img.style.marginTop = (-anchor.y) + 'px';
- }
- if (size) {
- img.style.width = size.x + 'px';
- img.style.height = size.y + 'px';
- }
- },
- _createImg: function (src, el) {
- el = el || document.createElement('img');
- el.src = src;
- return el;
- },
- _getIconUrl: function (name) {
- return L.Browser.retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
- }
- });
- // @factory L.icon(options: Icon options)
- // Creates an icon instance with the given options.
- L.icon = function (options) {
- return new L.Icon(options);
- };
- /*
- * @miniclass Icon.Default (Icon)
- * @aka L.Icon.Default
- * @section
- *
- * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when
- * no icon is specified. Points to the blue marker image distributed with Leaflet
- * releases.
- *
- * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options`
- * (which is a set of `Icon options`).
- *
- * If you want to _completely_ replace the default icon, override the
- * `L.Marker.prototype.options.icon` with your own icon instead.
- */
- L.Icon.Default = L.Icon.extend({
- options: {
- iconUrl: 'marker-icon.png',
- iconRetinaUrl: 'marker-icon-2x.png',
- shadowUrl: 'marker-shadow.png',
- iconSize: [25, 41],
- iconAnchor: [12, 41],
- popupAnchor: [1, -34],
- tooltipAnchor: [16, -28],
- shadowSize: [41, 41]
- },
- _getIconUrl: function (name) {
- if (!L.Icon.Default.imagePath) { // Deprecated, backwards-compatibility only
- L.Icon.Default.imagePath = this._detectIconPath();
- }
- // @option imagePath: String
- // `L.Icon.Default` will try to auto-detect the absolute location of the
- // blue icon images. If you are placing these images in a non-standard
- // way, set this option to point to the right absolute path.
- return (this.options.imagePath || L.Icon.Default.imagePath) + L.Icon.prototype._getIconUrl.call(this, name);
- },
- _detectIconPath: function () {
- var el = L.DomUtil.create('div', 'leaflet-default-icon-path', document.body);
- var path = L.DomUtil.getStyle(el, 'background-image') ||
- L.DomUtil.getStyle(el, 'backgroundImage'); // IE8
- document.body.removeChild(el);
- return path.indexOf('url') === 0 ?
- path.replace(/^url\([\"\']?/, '').replace(/marker-icon\.png[\"\']?\)$/, '') : '';
- }
- });
- /*
- * @class Marker
- * @inherits Interactive layer
- * @aka L.Marker
- * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
- *
- * @example
- *
- * ```js
- * L.marker([50.5, 30.5]).addTo(map);
- * ```
- */
- L.Marker = L.Layer.extend({
- // @section
- // @aka Marker options
- options: {
- // @option icon: Icon = *
- // Icon class to use for rendering the marker. See [Icon documentation](#L.Icon) for details on how to customize the marker icon. If not specified, a new `L.Icon.Default` is used.
- icon: new L.Icon.Default(),
- // Option inherited from "Interactive layer" abstract class
- interactive: true,
- // @option draggable: Boolean = false
- // Whether the marker is draggable with mouse/touch or not.
- draggable: false,
- // @option keyboard: Boolean = true
- // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
- keyboard: true,
- // @option title: String = ''
- // Text for the browser tooltip that appear on marker hover (no tooltip by default).
- title: '',
- // @option alt: String = ''
- // Text for the `alt` attribute of the icon image (useful for accessibility).
- alt: '',
- // @option zIndexOffset: Number = 0
- // By default, marker images zIndex is set automatically based on its latitude. Use this option if you want to put the marker on top of all others (or below), specifying a high value like `1000` (or high negative value, respectively).
- zIndexOffset: 0,
- // @option opacity: Number = 1.0
- // The opacity of the marker.
- opacity: 1,
- // @option riseOnHover: Boolean = false
- // If `true`, the marker will get on top of others when you hover the mouse over it.
- riseOnHover: false,
- // @option riseOffset: Number = 250
- // The z-index offset used for the `riseOnHover` feature.
- riseOffset: 250,
- // @option pane: String = 'markerPane'
- // `Map pane` where the markers icon will be added.
- pane: 'markerPane',
- // FIXME: shadowPane is no longer a valid option
- nonBubblingEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu']
- },
- /* @section
- *
- * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
- */
- initialize: function (latlng, options) {
- L.setOptions(this, options);
- this._latlng = L.latLng(latlng);
- },
- onAdd: function (map) {
- this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
- if (this._zoomAnimated) {
- map.on('zoomanim', this._animateZoom, this);
- }
- this._initIcon();
- this.update();
- },
- onRemove: function (map) {
- if (this.dragging && this.dragging.enabled()) {
- this.options.draggable = true;
- this.dragging.removeHooks();
- }
- if (this._zoomAnimated) {
- map.off('zoomanim', this._animateZoom, this);
- }
- this._removeIcon();
- this._removeShadow();
- },
- getEvents: function () {
- return {
- zoom: this.update,
- viewreset: this.update
- };
- },
- // @method getLatLng: LatLng
- // Returns the current geographical position of the marker.
- getLatLng: function () {
- return this._latlng;
- },
- // @method setLatLng(latlng: LatLng): this
- // Changes the marker position to the given point.
- setLatLng: function (latlng) {
- var oldLatLng = this._latlng;
- this._latlng = L.latLng(latlng);
- this.update();
- // @event move: Event
- // Fired when the marker is moved via [`setLatLng`](#marker-setlatlng) or by [dragging](#marker-dragging). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`.
- return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
- },
- // @method setZIndexOffset(offset: Number): this
- // Changes the [zIndex offset](#marker-zindexoffset) of the marker.
- setZIndexOffset: function (offset) {
- this.options.zIndexOffset = offset;
- return this.update();
- },
- // @method setIcon(icon: Icon): this
- // Changes the marker icon.
- setIcon: function (icon) {
- this.options.icon = icon;
- if (this._map) {
- this._initIcon();
- this.update();
- }
- if (this._popup) {
- this.bindPopup(this._popup, this._popup.options);
- }
- return this;
- },
- getElement: function () {
- return this._icon;
- },
- update: function () {
- if (this._icon) {
- var pos = this._map.latLngToLayerPoint(this._latlng).round();
- this._setPos(pos);
- }
- return this;
- },
- _initIcon: function () {
- var options = this.options,
- classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
- var icon = options.icon.createIcon(this._icon),
- addIcon = false;
- // if we're not reusing the icon, remove the old one and init new one
- if (icon !== this._icon) {
- if (this._icon) {
- this._removeIcon();
- }
- addIcon = true;
- if (options.title) {
- icon.title = options.title;
- }
- if (options.alt) {
- icon.alt = options.alt;
- }
- }
- L.DomUtil.addClass(icon, classToAdd);
- if (options.keyboard) {
- icon.tabIndex = '0';
- }
- this._icon = icon;
- if (options.riseOnHover) {
- this.on({
- mouseover: this._bringToFront,
- mouseout: this._resetZIndex
- });
- }
- var newShadow = options.icon.createShadow(this._shadow),
- addShadow = false;
- if (newShadow !== this._shadow) {
- this._removeShadow();
- addShadow = true;
- }
- if (newShadow) {
- L.DomUtil.addClass(newShadow, classToAdd);
- }
- this._shadow = newShadow;
- if (options.opacity < 1) {
- this._updateOpacity();
- }
- if (addIcon) {
- this.getPane().appendChild(this._icon);
- }
- this._initInteraction();
- if (newShadow && addShadow) {
- this.getPane('shadowPane').appendChild(this._shadow);
- }
- },
- _removeIcon: function () {
- if (this.options.riseOnHover) {
- this.off({
- mouseover: this._bringToFront,
- mouseout: this._resetZIndex
- });
- }
- L.DomUtil.remove(this._icon);
- this.removeInteractiveTarget(this._icon);
- this._icon = null;
- },
- _removeShadow: function () {
- if (this._shadow) {
- L.DomUtil.remove(this._shadow);
- }
- this._shadow = null;
- },
- _setPos: function (pos) {
- L.DomUtil.setPosition(this._icon, pos);
- if (this._shadow) {
- L.DomUtil.setPosition(this._shadow, pos);
- }
- this._zIndex = pos.y + this.options.zIndexOffset;
- this._resetZIndex();
- },
- _updateZIndex: function (offset) {
- this._icon.style.zIndex = this._zIndex + offset;
- },
- _animateZoom: function (opt) {
- var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
- this._setPos(pos);
- },
- _initInteraction: function () {
- if (!this.options.interactive) { return; }
- L.DomUtil.addClass(this._icon, 'leaflet-interactive');
- this.addInteractiveTarget(this._icon);
- if (L.Handler.MarkerDrag) {
- var draggable = this.options.draggable;
- if (this.dragging) {
- draggable = this.dragging.enabled();
- this.dragging.disable();
- }
- this.dragging = new L.Handler.MarkerDrag(this);
- if (draggable) {
- this.dragging.enable();
- }
- }
- },
- // @method setOpacity(opacity: Number): this
- // Changes the opacity of the marker.
- setOpacity: function (opacity) {
- this.options.opacity = opacity;
- if (this._map) {
- this._updateOpacity();
- }
- return this;
- },
- _updateOpacity: function () {
- var opacity = this.options.opacity;
- L.DomUtil.setOpacity(this._icon, opacity);
- if (this._shadow) {
- L.DomUtil.setOpacity(this._shadow, opacity);
- }
- },
- _bringToFront: function () {
- this._updateZIndex(this.options.riseOffset);
- },
- _resetZIndex: function () {
- this._updateZIndex(0);
- },
- _getPopupAnchor: function () {
- return this.options.icon.options.popupAnchor || [0, 0];
- },
- _getTooltipAnchor: function () {
- return this.options.icon.options.tooltipAnchor || [0, 0];
- }
- });
- // factory L.marker(latlng: LatLng, options? : Marker options)
- // @factory L.marker(latlng: LatLng, options? : Marker options)
- // Instantiates a Marker object given a geographical point and optionally an options object.
- L.marker = function (latlng, options) {
- return new L.Marker(latlng, options);
- };
- /*
- * @class DivIcon
- * @aka L.DivIcon
- * @inherits Icon
- *
- * Represents a lightweight icon for markers that uses a simple `<div>`
- * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options.
- *
- * @example
- * ```js
- * var myIcon = L.divIcon({className: 'my-div-icon'});
- * // you can set .my-div-icon styles in CSS
- *
- * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
- * ```
- *
- * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow.
- */
- L.DivIcon = L.Icon.extend({
- options: {
- // @section
- // @aka DivIcon options
- iconSize: [12, 12], // also can be set through CSS
- // iconAnchor: (Point),
- // popupAnchor: (Point),
- // @option html: String = ''
- // Custom HTML code to put inside the div element, empty by default.
- html: false,
- // @option bgPos: Point = [0, 0]
- // Optional relative position of the background, in pixels
- bgPos: null,
- className: 'leaflet-div-icon'
- },
- createIcon: function (oldIcon) {
- var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
- options = this.options;
- div.innerHTML = options.html !== false ? options.html : '';
- if (options.bgPos) {
- var bgPos = L.point(options.bgPos);
- div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px';
- }
- this._setIconStyles(div, 'icon');
- return div;
- },
- createShadow: function () {
- return null;
- }
- });
- // @factory L.divIcon(options: DivIcon options)
- // Creates a `DivIcon` instance with the given options.
- L.divIcon = function (options) {
- return new L.DivIcon(options);
- };
- /*
- * @class DivOverlay
- * @inherits Layer
- * @aka L.DivOverlay
- * Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins.
- */
- // @namespace DivOverlay
- L.DivOverlay = L.Layer.extend({
- // @section
- // @aka DivOverlay options
- options: {
- // @option offset: Point = Point(0, 7)
- // The offset of the popup position. Useful to control the anchor
- // of the popup when opening it on some overlays.
- offset: [0, 7],
- // @option className: String = ''
- // A custom CSS class name to assign to the popup.
- className: '',
- // @option pane: String = 'popupPane'
- // `Map pane` where the popup will be added.
- pane: 'popupPane'
- },
- initialize: function (options, source) {
- L.setOptions(this, options);
- this._source = source;
- },
- onAdd: function (map) {
- this._zoomAnimated = map._zoomAnimated;
- if (!this._container) {
- this._initLayout();
- }
- if (map._fadeAnimated) {
- L.DomUtil.setOpacity(this._container, 0);
- }
- clearTimeout(this._removeTimeout);
- this.getPane().appendChild(this._container);
- this.update();
- if (map._fadeAnimated) {
- L.DomUtil.setOpacity(this._container, 1);
- }
- this.bringToFront();
- },
- onRemove: function (map) {
- if (map._fadeAnimated) {
- L.DomUtil.setOpacity(this._container, 0);
- this._removeTimeout = setTimeout(L.bind(L.DomUtil.remove, L.DomUtil, this._container), 200);
- } else {
- L.DomUtil.remove(this._container);
- }
- },
- // @namespace Popup
- // @method getLatLng: LatLng
- // Returns the geographical point of popup.
- getLatLng: function () {
- return this._latlng;
- },
- // @method setLatLng(latlng: LatLng): this
- // Sets the geographical point where the popup will open.
- setLatLng: function (latlng) {
- this._latlng = L.latLng(latlng);
- if (this._map) {
- this._updatePosition();
- this._adjustPan();
- }
- return this;
- },
- // @method getContent: String|HTMLElement
- // Returns the content of the popup.
- getContent: function () {
- return this._content;
- },
- // @method setContent(htmlContent: String|HTMLElement|Function): this
- // Sets the HTML content of the popup. If a function is passed the source layer will be passed to the function. The function should return a `String` or `HTMLElement` to be used in the popup.
- setContent: function (content) {
- this._content = content;
- this.update();
- return this;
- },
- // @method getElement: String|HTMLElement
- // Alias for [getContent()](#popup-getcontent)
- getElement: function () {
- return this._container;
- },
- // @method update: null
- // Updates the popup content, layout and position. Useful for updating the popup after something inside changed, e.g. image loaded.
- update: function () {
- if (!this._map) { return; }
- this._container.style.visibility = 'hidden';
- this._updateContent();
- this._updateLayout();
- this._updatePosition();
- this._container.style.visibility = '';
- this._adjustPan();
- },
- getEvents: function () {
- var events = {
- zoom: this._updatePosition,
- viewreset: this._updatePosition
- };
- if (this._zoomAnimated) {
- events.zoomanim = this._animateZoom;
- }
- return events;
- },
- // @method isOpen: Boolean
- // Returns `true` when the popup is visible on the map.
- isOpen: function () {
- return !!this._map && this._map.hasLayer(this);
- },
- // @method bringToFront: this
- // Brings this popup in front of other popups (in the same map pane).
- bringToFront: function () {
- if (this._map) {
- L.DomUtil.toFront(this._container);
- }
- return this;
- },
- // @method bringToBack: this
- // Brings this popup to the back of other popups (in the same map pane).
- bringToBack: function () {
- if (this._map) {
- L.DomUtil.toBack(this._container);
- }
- return this;
- },
- _updateContent: function () {
- if (!this._content) { return; }
- var node = this._contentNode;
- var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
- if (typeof content === 'string') {
- node.innerHTML = content;
- } else {
- while (node.hasChildNodes()) {
- node.removeChild(node.firstChild);
- }
- node.appendChild(content);
- }
- this.fire('contentupdate');
- },
- _updatePosition: function () {
- if (!this._map) { return; }
- var pos = this._map.latLngToLayerPoint(this._latlng),
- offset = L.point(this.options.offset),
- anchor = this._getAnchor();
- if (this._zoomAnimated) {
- L.DomUtil.setPosition(this._container, pos.add(anchor));
- } else {
- offset = offset.add(pos).add(anchor);
- }
- var bottom = this._containerBottom = -offset.y,
- left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
- // bottom position the popup in case the height of the popup changes (images loading etc)
- this._container.style.bottom = bottom + 'px';
- this._container.style.left = left + 'px';
- },
- _getAnchor: function () {
- return [0, 0];
- }
- });
- /*
- * @class Popup
- * @inherits DivOverlay
- * @aka L.Popup
- * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
- * open popups while making sure that only one popup is open at one time
- * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
- *
- * @example
- *
- * If you want to just bind a popup to marker click and then open it, it's really easy:
- *
- * ```js
- * marker.bindPopup(popupContent).openPopup();
- * ```
- * Path overlays like polylines also have a `bindPopup` method.
- * Here's a more complicated way to open a popup on a map:
- *
- * ```js
- * var popup = L.popup()
- * .setLatLng(latlng)
- * .setContent('<p>Hello world!<br />This is a nice popup.</p>')
- * .openOn(map);
- * ```
- */
- // @namespace Popup
- L.Popup = L.DivOverlay.extend({
- // @section
- // @aka Popup options
- options: {
- // @option maxWidth: Number = 300
- // Max width of the popup, in pixels.
- maxWidth: 300,
- // @option minWidth: Number = 50
- // Min width of the popup, in pixels.
- minWidth: 50,
- // @option maxHeight: Number = null
- // If set, creates a scrollable container of the given height
- // inside a popup if its content exceeds it.
- maxHeight: null,
- // @option autoPan: Boolean = true
- // Set it to `false` if you don't want the map to do panning animation
- // to fit the opened popup.
- autoPan: true,
- // @option autoPanPaddingTopLeft: Point = null
- // The margin between the popup and the top left corner of the map
- // view after autopanning was performed.
- autoPanPaddingTopLeft: null,
- // @option autoPanPaddingBottomRight: Point = null
- // The margin between the popup and the bottom right corner of the map
- // view after autopanning was performed.
- autoPanPaddingBottomRight: null,
- // @option autoPanPadding: Point = Point(5, 5)
- // Equivalent of setting both top left and bottom right autopan padding to the same value.
- autoPanPadding: [5, 5],
- // @option keepInView: Boolean = false
- // Set it to `true` if you want to prevent users from panning the popup
- // off of the screen while it is open.
- keepInView: false,
- // @option closeButton: Boolean = true
- // Controls the presence of a close button in the popup.
- closeButton: true,
- // @option autoClose: Boolean = true
- // Set it to `false` if you want to override the default behavior of
- // the popup closing when user clicks the map (set globally by
- // the Map's [closePopupOnClick](#map-closepopuponclick) option).
- autoClose: true,
- // @option className: String = ''
- // A custom CSS class name to assign to the popup.
- className: ''
- },
- // @namespace Popup
- // @method openOn(map: Map): this
- // Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`.
- openOn: function (map) {
- map.openPopup(this);
- return this;
- },
- onAdd: function (map) {
- L.DivOverlay.prototype.onAdd.call(this, map);
- // @namespace Map
- // @section Popup events
- // @event popupopen: PopupEvent
- // Fired when a popup is opened in the map
- map.fire('popupopen', {popup: this});
- if (this._source) {
- // @namespace Layer
- // @section Popup events
- // @event popupopen: PopupEvent
- // Fired when a popup bound to this layer is opened
- this._source.fire('popupopen', {popup: this}, true);
- // For non-path layers, we toggle the popup when clicking
- // again the layer, so prevent the map to reopen it.
- if (!(this._source instanceof L.Path)) {
- this._source.on('preclick', L.DomEvent.stopPropagation);
- }
- }
- },
- onRemove: function (map) {
- L.DivOverlay.prototype.onRemove.call(this, map);
- // @namespace Map
- // @section Popup events
- // @event popupclose: PopupEvent
- // Fired when a popup in the map is closed
- map.fire('popupclose', {popup: this});
- if (this._source) {
- // @namespace Layer
- // @section Popup events
- // @event popupclose: PopupEvent
- // Fired when a popup bound to this layer is closed
- this._source.fire('popupclose', {popup: this}, true);
- if (!(this._source instanceof L.Path)) {
- this._source.off('preclick', L.DomEvent.stopPropagation);
- }
- }
- },
- getEvents: function () {
- var events = L.DivOverlay.prototype.getEvents.call(this);
- if ('closeOnClick' in this.options ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
- events.preclick = this._close;
- }
- if (this.options.keepInView) {
- events.moveend = this._adjustPan;
- }
- return events;
- },
- _close: function () {
- if (this._map) {
- this._map.closePopup(this);
- }
- },
- _initLayout: function () {
- var prefix = 'leaflet-popup',
- container = this._container = L.DomUtil.create('div',
- prefix + ' ' + (this.options.className || '') +
- ' leaflet-zoom-animated');
- if (this.options.closeButton) {
- var closeButton = this._closeButton = L.DomUtil.create('a', prefix + '-close-button', container);
- closeButton.href = '#close';
- closeButton.innerHTML = '×';
- L.DomEvent.on(closeButton, 'click', this._onCloseButtonClick, this);
- }
- var wrapper = this._wrapper = L.DomUtil.create('div', prefix + '-content-wrapper', container);
- this._contentNode = L.DomUtil.create('div', prefix + '-content', wrapper);
- L.DomEvent
- .disableClickPropagation(wrapper)
- .disableScrollPropagation(this._contentNode)
- .on(wrapper, 'contextmenu', L.DomEvent.stopPropagation);
- this._tipContainer = L.DomUtil.create('div', prefix + '-tip-container', container);
- this._tip = L.DomUtil.create('div', prefix + '-tip', this._tipContainer);
- },
- _updateLayout: function () {
- var container = this._contentNode,
- style = container.style;
- style.width = '';
- style.whiteSpace = 'nowrap';
- var width = container.offsetWidth;
- width = Math.min(width, this.options.maxWidth);
- width = Math.max(width, this.options.minWidth);
- style.width = (width + 1) + 'px';
- style.whiteSpace = '';
- style.height = '';
- var height = container.offsetHeight,
- maxHeight = this.options.maxHeight,
- scrolledClass = 'leaflet-popup-scrolled';
- if (maxHeight && height > maxHeight) {
- style.height = maxHeight + 'px';
- L.DomUtil.addClass(container, scrolledClass);
- } else {
- L.DomUtil.removeClass(container, scrolledClass);
- }
- this._containerWidth = this._container.offsetWidth;
- },
- _animateZoom: function (e) {
- var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
- anchor = this._getAnchor();
- L.DomUtil.setPosition(this._container, pos.add(anchor));
- },
- _adjustPan: function () {
- if (!this.options.autoPan || (this._map._panAnim && this._map._panAnim._inProgress)) { return; }
- var map = this._map,
- marginBottom = parseInt(L.DomUtil.getStyle(this._container, 'marginBottom'), 10) || 0,
- containerHeight = this._container.offsetHeight + marginBottom,
- containerWidth = this._containerWidth,
- layerPos = new L.Point(this._containerLeft, -containerHeight - this._containerBottom);
- layerPos._add(L.DomUtil.getPosition(this._container));
- var containerPos = map.layerPointToContainerPoint(layerPos),
- padding = L.point(this.options.autoPanPadding),
- paddingTL = L.point(this.options.autoPanPaddingTopLeft || padding),
- paddingBR = L.point(this.options.autoPanPaddingBottomRight || padding),
- size = map.getSize(),
- dx = 0,
- dy = 0;
- if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
- dx = containerPos.x + containerWidth - size.x + paddingBR.x;
- }
- if (containerPos.x - dx - paddingTL.x < 0) { // left
- dx = containerPos.x - paddingTL.x;
- }
- if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
- dy = containerPos.y + containerHeight - size.y + paddingBR.y;
- }
- if (containerPos.y - dy - paddingTL.y < 0) { // top
- dy = containerPos.y - paddingTL.y;
- }
- // @namespace Map
- // @section Popup events
- // @event autopanstart: Event
- // Fired when the map starts autopanning when opening a popup.
- if (dx || dy) {
- map
- .fire('autopanstart')
- .panBy([dx, dy]);
- }
- },
- _onCloseButtonClick: function (e) {
- this._close();
- L.DomEvent.stop(e);
- },
- _getAnchor: function () {
- // Where should we anchor the popup on the source layer?
- return L.point(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
- }
- });
- // @namespace Popup
- // @factory L.popup(options?: Popup options, source?: Layer)
- // Instantiates a `Popup` object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the popup with a reference to the Layer to which it refers.
- L.popup = function (options, source) {
- return new L.Popup(options, source);
- };
- /* @namespace Map
- * @section Interaction Options
- * @option closePopupOnClick: Boolean = true
- * Set it to `false` if you don't want popups to close when user clicks the map.
- */
- L.Map.mergeOptions({
- closePopupOnClick: true
- });
- // @namespace Map
- // @section Methods for Layers and Controls
- L.Map.include({
- // @method openPopup(popup: Popup): this
- // Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability).
- // @alternative
- // @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this
- // Creates a popup with the specified content and options and opens it in the given point on a map.
- openPopup: function (popup, latlng, options) {
- if (!(popup instanceof L.Popup)) {
- popup = new L.Popup(options).setContent(popup);
- }
- if (latlng) {
- popup.setLatLng(latlng);
- }
- if (this.hasLayer(popup)) {
- return this;
- }
- if (this._popup && this._popup.options.autoClose) {
- this.closePopup();
- }
- this._popup = popup;
- return this.addLayer(popup);
- },
- // @method closePopup(popup?: Popup): this
- // Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one).
- closePopup: function (popup) {
- if (!popup || popup === this._popup) {
- popup = this._popup;
- this._popup = null;
- }
- if (popup) {
- this.removeLayer(popup);
- }
- return this;
- }
- });
- /*
- * @namespace Layer
- * @section Popup methods example
- *
- * All layers share a set of methods convenient for binding popups to it.
- *
- * ```js
- * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map);
- * layer.openPopup();
- * layer.closePopup();
- * ```
- *
- * Popups will also be automatically opened when the layer is clicked on and closed when the layer is removed from the map or another popup is opened.
- */
- // @section Popup methods
- L.Layer.include({
- // @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this
- // Binds a popup to the layer with the passed `content` and sets up the
- // neccessary event listeners. If a `Function` is passed it will receive
- // the layer as the first argument and should return a `String` or `HTMLElement`.
- bindPopup: function (content, options) {
- if (content instanceof L.Popup) {
- L.setOptions(content, options);
- this._popup = content;
- content._source = this;
- } else {
- if (!this._popup || options) {
- this._popup = new L.Popup(options, this);
- }
- this._popup.setContent(content);
- }
- if (!this._popupHandlersAdded) {
- this.on({
- click: this._openPopup,
- remove: this.closePopup,
- move: this._movePopup
- });
- this._popupHandlersAdded = true;
- }
- return this;
- },
- // @method unbindPopup(): this
- // Removes the popup previously bound with `bindPopup`.
- unbindPopup: function () {
- if (this._popup) {
- this.off({
- click: this._openPopup,
- remove: this.closePopup,
- move: this._movePopup
- });
- this._popupHandlersAdded = false;
- this._popup = null;
- }
- return this;
- },
- // @method openPopup(latlng?: LatLng): this
- // Opens the bound popup at the specificed `latlng` or at the default popup anchor if no `latlng` is passed.
- openPopup: function (layer, latlng) {
- if (!(layer instanceof L.Layer)) {
- latlng = layer;
- layer = this;
- }
- if (layer instanceof L.FeatureGroup) {
- for (var id in this._layers) {
- layer = this._layers[id];
- break;
- }
- }
- if (!latlng) {
- latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
- }
- if (this._popup && this._map) {
- // set popup source to this layer
- this._popup._source = layer;
- // update the popup (content, layout, ect...)
- this._popup.update();
- // open the popup on the map
- this._map.openPopup(this._popup, latlng);
- }
- return this;
- },
- // @method closePopup(): this
- // Closes the popup bound to this layer if it is open.
- closePopup: function () {
- if (this._popup) {
- this._popup._close();
- }
- return this;
- },
- // @method togglePopup(): this
- // Opens or closes the popup bound to this layer depending on its current state.
- togglePopup: function (target) {
- if (this._popup) {
- if (this._popup._map) {
- this.closePopup();
- } else {
- this.openPopup(target);
- }
- }
- return this;
- },
- // @method isPopupOpen(): boolean
- // Returns `true` if the popup bound to this layer is currently open.
- isPopupOpen: function () {
- return this._popup.isOpen();
- },
- // @method setPopupContent(content: String|HTMLElement|Popup): this
- // Sets the content of the popup bound to this layer.
- setPopupContent: function (content) {
- if (this._popup) {
- this._popup.setContent(content);
- }
- return this;
- },
- // @method getPopup(): Popup
- // Returns the popup bound to this layer.
- getPopup: function () {
- return this._popup;
- },
- _openPopup: function (e) {
- var layer = e.layer || e.target;
- if (!this._popup) {
- return;
- }
- if (!this._map) {
- return;
- }
- // prevent map click
- L.DomEvent.stop(e);
- // if this inherits from Path its a vector and we can just
- // open the popup at the new location
- if (layer instanceof L.Path) {
- this.openPopup(e.layer || e.target, e.latlng);
- return;
- }
- // otherwise treat it like a marker and figure out
- // if we should toggle it open/closed
- if (this._map.hasLayer(this._popup) && this._popup._source === layer) {
- this.closePopup();
- } else {
- this.openPopup(layer, e.latlng);
- }
- },
- _movePopup: function (e) {
- this._popup.setLatLng(e.latlng);
- }
- });
- /*
- * @class Tooltip
- * @inherits DivOverlay
- * @aka L.Tooltip
- * Used to display small texts on top of map layers.
- *
- * @example
- *
- * ```js
- * marker.bindTooltip("my tooltip text").openTooltip();
- * ```
- * Note about tooltip offset. Leaflet takes two options in consideration
- * for computing tooltip offseting:
- * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip.
- * Add a positive x offset to move the tooltip to the right, and a positive y offset to
- * move it to the bottom. Negatives will move to the left and top.
- * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You
- * should adapt this value if you use a custom icon.
- */
- // @namespace Tooltip
- L.Tooltip = L.DivOverlay.extend({
- // @section
- // @aka Tooltip options
- options: {
- // @option pane: String = 'tooltipPane'
- // `Map pane` where the tooltip will be added.
- pane: 'tooltipPane',
- // @option offset: Point = Point(0, 0)
- // Optional offset of the tooltip position.
- offset: [0, 0],
- // @option direction: String = 'auto'
- // Direction where to open the tooltip. Possible values are: `right`, `left`,
- // `top`, `bottom`, `center`, `auto`.
- // `auto` will dynamicaly switch between `right` and `left` according to the tooltip
- // position on the map.
- direction: 'auto',
- // @option permanent: Boolean = false
- // Whether to open the tooltip permanently or only on mouseover.
- permanent: false,
- // @option sticky: Boolean = false
- // If true, the tooltip will follow the mouse instead of being fixed at the feature center.
- sticky: false,
- // @option interactive: Boolean = false
- // If true, the tooltip will listen to the feature events.
- interactive: false,
- // @option opacity: Number = 0.9
- // Tooltip container opacity.
- opacity: 0.9
- },
- onAdd: function (map) {
- L.DivOverlay.prototype.onAdd.call(this, map);
- this.setOpacity(this.options.opacity);
- // @namespace Map
- // @section Tooltip events
- // @event tooltipopen: TooltipEvent
- // Fired when a tooltip is opened in the map.
- map.fire('tooltipopen', {tooltip: this});
- if (this._source) {
- // @namespace Layer
- // @section Tooltip events
- // @event tooltipopen: TooltipEvent
- // Fired when a tooltip bound to this layer is opened.
- this._source.fire('tooltipopen', {tooltip: this}, true);
- }
- },
- onRemove: function (map) {
- L.DivOverlay.prototype.onRemove.call(this, map);
- // @namespace Map
- // @section Tooltip events
- // @event tooltipclose: TooltipEvent
- // Fired when a tooltip in the map is closed.
- map.fire('tooltipclose', {tooltip: this});
- if (this._source) {
- // @namespace Layer
- // @section Tooltip events
- // @event tooltipclose: TooltipEvent
- // Fired when a tooltip bound to this layer is closed.
- this._source.fire('tooltipclose', {tooltip: this}, true);
- }
- },
- getEvents: function () {
- var events = L.DivOverlay.prototype.getEvents.call(this);
- if (L.Browser.touch && !this.options.permanent) {
- events.preclick = this._close;
- }
- return events;
- },
- _close: function () {
- if (this._map) {
- this._map.closeTooltip(this);
- }
- },
- _initLayout: function () {
- var prefix = 'leaflet-tooltip',
- className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
- this._contentNode = this._container = L.DomUtil.create('div', className);
- },
- _updateLayout: function () {},
- _adjustPan: function () {},
- _setPosition: function (pos) {
- var map = this._map,
- container = this._container,
- centerPoint = map.latLngToContainerPoint(map.getCenter()),
- tooltipPoint = map.layerPointToContainerPoint(pos),
- direction = this.options.direction,
- tooltipWidth = container.offsetWidth,
- tooltipHeight = container.offsetHeight,
- offset = L.point(this.options.offset),
- anchor = this._getAnchor();
- if (direction === 'top') {
- pos = pos.add(L.point(-tooltipWidth / 2 + offset.x, -tooltipHeight + offset.y + anchor.y, true));
- } else if (direction === 'bottom') {
- pos = pos.subtract(L.point(tooltipWidth / 2 - offset.x, -offset.y, true));
- } else if (direction === 'center') {
- pos = pos.subtract(L.point(tooltipWidth / 2 + offset.x, tooltipHeight / 2 - anchor.y + offset.y, true));
- } else if (direction === 'right' || direction === 'auto' && tooltipPoint.x < centerPoint.x) {
- direction = 'right';
- pos = pos.add(L.point(offset.x + anchor.x, anchor.y - tooltipHeight / 2 + offset.y, true));
- } else {
- direction = 'left';
- pos = pos.subtract(L.point(tooltipWidth + anchor.x - offset.x, tooltipHeight / 2 - anchor.y - offset.y, true));
- }
- L.DomUtil.removeClass(container, 'leaflet-tooltip-right');
- L.DomUtil.removeClass(container, 'leaflet-tooltip-left');
- L.DomUtil.removeClass(container, 'leaflet-tooltip-top');
- L.DomUtil.removeClass(container, 'leaflet-tooltip-bottom');
- L.DomUtil.addClass(container, 'leaflet-tooltip-' + direction);
- L.DomUtil.setPosition(container, pos);
- },
- _updatePosition: function () {
- var pos = this._map.latLngToLayerPoint(this._latlng);
- this._setPosition(pos);
- },
- setOpacity: function (opacity) {
- this.options.opacity = opacity;
- if (this._container) {
- L.DomUtil.setOpacity(this._container, opacity);
- }
- },
- _animateZoom: function (e) {
- var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
- this._setPosition(pos);
- },
- _getAnchor: function () {
- // Where should we anchor the tooltip on the source layer?
- return L.point(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
- }
- });
- // @namespace Tooltip
- // @factory L.tooltip(options?: Tooltip options, source?: Layer)
- // Instantiates a Tooltip object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the tooltip with a reference to the Layer to which it refers.
- L.tooltip = function (options, source) {
- return new L.Tooltip(options, source);
- };
- // @namespace Map
- // @section Methods for Layers and Controls
- L.Map.include({
- // @method openTooltip(tooltip: Tooltip): this
- // Opens the specified tooltip.
- // @alternative
- // @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this
- // Creates a tooltip with the specified content and options and open it.
- openTooltip: function (tooltip, latlng, options) {
- if (!(tooltip instanceof L.Tooltip)) {
- tooltip = new L.Tooltip(options).setContent(tooltip);
- }
- if (latlng) {
- tooltip.setLatLng(latlng);
- }
- if (this.hasLayer(tooltip)) {
- return this;
- }
- return this.addLayer(tooltip);
- },
- // @method closeTooltip(tooltip?: Tooltip): this
- // Closes the tooltip given as parameter.
- closeTooltip: function (tooltip) {
- if (tooltip) {
- this.removeLayer(tooltip);
- }
- return this;
- }
- });
- /*
- * @namespace Layer
- * @section Tooltip methods example
- *
- * All layers share a set of methods convenient for binding tooltips to it.
- *
- * ```js
- * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map);
- * layer.openTooltip();
- * layer.closeTooltip();
- * ```
- */
- // @section Tooltip methods
- L.Layer.include({
- // @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this
- // Binds a tooltip to the layer with the passed `content` and sets up the
- // neccessary event listeners. If a `Function` is passed it will receive
- // the layer as the first argument and should return a `String` or `HTMLElement`.
- bindTooltip: function (content, options) {
- if (content instanceof L.Tooltip) {
- L.setOptions(content, options);
- this._tooltip = content;
- content._source = this;
- } else {
- if (!this._tooltip || options) {
- this._tooltip = L.tooltip(options, this);
- }
- this._tooltip.setContent(content);
- }
- this._initTooltipInteractions();
- if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) {
- this.openTooltip();
- }
- return this;
- },
- // @method unbindTooltip(): this
- // Removes the tooltip previously bound with `bindTooltip`.
- unbindTooltip: function () {
- if (this._tooltip) {
- this._initTooltipInteractions(true);
- this.closeTooltip();
- this._tooltip = null;
- }
- return this;
- },
- _initTooltipInteractions: function (remove) {
- if (!remove && this._tooltipHandlersAdded) { return; }
- var onOff = remove ? 'off' : 'on',
- events = {
- remove: this.closeTooltip,
- move: this._moveTooltip
- };
- if (!this._tooltip.options.permanent) {
- events.mouseover = this._openTooltip;
- events.mouseout = this.closeTooltip;
- if (this._tooltip.options.sticky) {
- events.mousemove = this._moveTooltip;
- }
- if (L.Browser.touch) {
- events.click = this._openTooltip;
- }
- } else {
- events.add = this._openTooltip;
- }
- this[onOff](events);
- this._tooltipHandlersAdded = !remove;
- },
- // @method openTooltip(latlng?: LatLng): this
- // Opens the bound tooltip at the specificed `latlng` or at the default tooltip anchor if no `latlng` is passed.
- openTooltip: function (layer, latlng) {
- if (!(layer instanceof L.Layer)) {
- latlng = layer;
- layer = this;
- }
- if (layer instanceof L.FeatureGroup) {
- for (var id in this._layers) {
- layer = this._layers[id];
- break;
- }
- }
- if (!latlng) {
- latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
- }
- if (this._tooltip && this._map) {
- // set tooltip source to this layer
- this._tooltip._source = layer;
- // update the tooltip (content, layout, ect...)
- this._tooltip.update();
- // open the tooltip on the map
- this._map.openTooltip(this._tooltip, latlng);
- // Tooltip container may not be defined if not permanent and never
- // opened.
- if (this._tooltip.options.interactive && this._tooltip._container) {
- L.DomUtil.addClass(this._tooltip._container, 'leaflet-clickable');
- this.addInteractiveTarget(this._tooltip._container);
- }
- }
- return this;
- },
- // @method closeTooltip(): this
- // Closes the tooltip bound to this layer if it is open.
- closeTooltip: function () {
- if (this._tooltip) {
- this._tooltip._close();
- if (this._tooltip.options.interactive && this._tooltip._container) {
- L.DomUtil.removeClass(this._tooltip._container, 'leaflet-clickable');
- this.removeInteractiveTarget(this._tooltip._container);
- }
- }
- return this;
- },
- // @method toggleTooltip(): this
- // Opens or closes the tooltip bound to this layer depending on its current state.
- toggleTooltip: function (target) {
- if (this._tooltip) {
- if (this._tooltip._map) {
- this.closeTooltip();
- } else {
- this.openTooltip(target);
- }
- }
- return this;
- },
- // @method isTooltipOpen(): boolean
- // Returns `true` if the tooltip bound to this layer is currently open.
- isTooltipOpen: function () {
- return this._tooltip.isOpen();
- },
- // @method setTooltipContent(content: String|HTMLElement|Tooltip): this
- // Sets the content of the tooltip bound to this layer.
- setTooltipContent: function (content) {
- if (this._tooltip) {
- this._tooltip.setContent(content);
- }
- return this;
- },
- // @method getTooltip(): Tooltip
- // Returns the tooltip bound to this layer.
- getTooltip: function () {
- return this._tooltip;
- },
- _openTooltip: function (e) {
- var layer = e.layer || e.target;
- if (!this._tooltip || !this._map) {
- return;
- }
- this.openTooltip(layer, this._tooltip.options.sticky ? e.latlng : undefined);
- },
- _moveTooltip: function (e) {
- var latlng = e.latlng, containerPoint, layerPoint;
- if (this._tooltip.options.sticky && e.originalEvent) {
- containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent);
- layerPoint = this._map.containerPointToLayerPoint(containerPoint);
- latlng = this._map.layerPointToLatLng(layerPoint);
- }
- this._tooltip.setLatLng(latlng);
- }
- });
- /*
- * @class LayerGroup
- * @aka L.LayerGroup
- * @inherits Layer
- *
- * Used to group several layers and handle them as one. If you add it to the map,
- * any layers added or removed from the group will be added/removed on the map as
- * well. Extends `Layer`.
- *
- * @example
- *
- * ```js
- * L.layerGroup([marker1, marker2])
- * .addLayer(polyline)
- * .addTo(map);
- * ```
- */
- L.LayerGroup = L.Layer.extend({
- initialize: function (layers) {
- this._layers = {};
- var i, len;
- if (layers) {
- for (i = 0, len = layers.length; i < len; i++) {
- this.addLayer(layers[i]);
- }
- }
- },
- // @method addLayer(layer: Layer): this
- // Adds the given layer to the group.
- addLayer: function (layer) {
- var id = this.getLayerId(layer);
- this._layers[id] = layer;
- if (this._map) {
- this._map.addLayer(layer);
- }
- return this;
- },
- // @method removeLayer(layer: Layer): this
- // Removes the given layer from the group.
- // @alternative
- // @method removeLayer(id: Number): this
- // Removes the layer with the given internal ID from the group.
- removeLayer: function (layer) {
- var id = layer in this._layers ? layer : this.getLayerId(layer);
- if (this._map && this._layers[id]) {
- this._map.removeLayer(this._layers[id]);
- }
- delete this._layers[id];
- return this;
- },
- // @method hasLayer(layer: Layer): Boolean
- // Returns `true` if the given layer is currently added to the group.
- hasLayer: function (layer) {
- return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers);
- },
- // @method clearLayers(): this
- // Removes all the layers from the group.
- clearLayers: function () {
- for (var i in this._layers) {
- this.removeLayer(this._layers[i]);
- }
- return this;
- },
- // @method invoke(methodName: String, …): this
- // Calls `methodName` on every layer contained in this group, passing any
- // additional parameters. Has no effect if the layers contained do not
- // implement `methodName`.
- invoke: function (methodName) {
- var args = Array.prototype.slice.call(arguments, 1),
- i, layer;
- for (i in this._layers) {
- layer = this._layers[i];
- if (layer[methodName]) {
- layer[methodName].apply(layer, args);
- }
- }
- return this;
- },
- onAdd: function (map) {
- for (var i in this._layers) {
- map.addLayer(this._layers[i]);
- }
- },
- onRemove: function (map) {
- for (var i in this._layers) {
- map.removeLayer(this._layers[i]);
- }
- },
- // @method eachLayer(fn: Function, context?: Object): this
- // Iterates over the layers of the group, optionally specifying context of the iterator function.
- // ```js
- // group.eachLayer(function (layer) {
- // layer.bindPopup('Hello');
- // });
- // ```
- eachLayer: function (method, context) {
- for (var i in this._layers) {
- method.call(context, this._layers[i]);
- }
- return this;
- },
- // @method getLayer(id: Number): Layer
- // Returns the layer with the given internal ID.
- getLayer: function (id) {
- return this._layers[id];
- },
- // @method getLayers(): Layer[]
- // Returns an array of all the layers added to the group.
- getLayers: function () {
- var layers = [];
- for (var i in this._layers) {
- layers.push(this._layers[i]);
- }
- return layers;
- },
- // @method setZIndex(zIndex: Number): this
- // Calls `setZIndex` on every layer contained in this group, passing the z-index.
- setZIndex: function (zIndex) {
- return this.invoke('setZIndex', zIndex);
- },
- // @method getLayerId(layer: Layer): Number
- // Returns the internal ID for a layer
- getLayerId: function (layer) {
- return L.stamp(layer);
- }
- });
- // @factory L.layerGroup(layers: Layer[])
- // Create a layer group, optionally given an initial set of layers.
- L.layerGroup = function (layers) {
- return new L.LayerGroup(layers);
- };
- /*
- * @class FeatureGroup
- * @aka L.FeatureGroup
- * @inherits LayerGroup
- *
- * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers:
- * * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip))
- * * Events are propagated to the `FeatureGroup`, so if the group has an event
- * handler, it will handle events from any of the layers. This includes mouse events
- * and custom events.
- * * Has `layeradd` and `layerremove` events
- *
- * @example
- *
- * ```js
- * L.featureGroup([marker1, marker2, polyline])
- * .bindPopup('Hello world!')
- * .on('click', function() { alert('Clicked on a member of the group!'); })
- * .addTo(map);
- * ```
- */
- L.FeatureGroup = L.LayerGroup.extend({
- addLayer: function (layer) {
- if (this.hasLayer(layer)) {
- return this;
- }
- layer.addEventParent(this);
- L.LayerGroup.prototype.addLayer.call(this, layer);
- // @event layeradd: LayerEvent
- // Fired when a layer is added to this `FeatureGroup`
- return this.fire('layeradd', {layer: layer});
- },
- removeLayer: function (layer) {
- if (!this.hasLayer(layer)) {
- return this;
- }
- if (layer in this._layers) {
- layer = this._layers[layer];
- }
- layer.removeEventParent(this);
- L.LayerGroup.prototype.removeLayer.call(this, layer);
- // @event layerremove: LayerEvent
- // Fired when a layer is removed from this `FeatureGroup`
- return this.fire('layerremove', {layer: layer});
- },
- // @method setStyle(style: Path options): this
- // Sets the given path options to each layer of the group that has a `setStyle` method.
- setStyle: function (style) {
- return this.invoke('setStyle', style);
- },
- // @method bringToFront(): this
- // Brings the layer group to the top of all other layers
- bringToFront: function () {
- return this.invoke('bringToFront');
- },
- // @method bringToBack(): this
- // Brings the layer group to the top of all other layers
- bringToBack: function () {
- return this.invoke('bringToBack');
- },
- // @method getBounds(): LatLngBounds
- // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children).
- getBounds: function () {
- var bounds = new L.LatLngBounds();
- for (var id in this._layers) {
- var layer = this._layers[id];
- bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
- }
- return bounds;
- }
- });
- // @factory L.featureGroup(layers: Layer[])
- // Create a feature group, optionally given an initial set of layers.
- L.featureGroup = function (layers) {
- return new L.FeatureGroup(layers);
- };
- /*
- * @class Renderer
- * @inherits Layer
- * @aka L.Renderer
- *
- * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the
- * DOM container of the renderer, its bounds, and its zoom animation.
- *
- * A `Renderer` works as an implicit layer group for all `Path`s - the renderer
- * itself can be added or removed to the map. All paths use a renderer, which can
- * be implicit (the map will decide the type of renderer and use it automatically)
- * or explicit (using the [`renderer`](#path-renderer) option of the path).
- *
- * Do not use this class directly, use `SVG` and `Canvas` instead.
- *
- * @event update: Event
- * Fired when the renderer updates its bounds, center and zoom, for example when
- * its map has moved
- */
- L.Renderer = L.Layer.extend({
- // @section
- // @aka Renderer options
- options: {
- // @option padding: Number = 0.1
- // How much to extend the clip area around the map view (relative to its size)
- // e.g. 0.1 would be 10% of map view in each direction
- padding: 0.1
- },
- initialize: function (options) {
- L.setOptions(this, options);
- L.stamp(this);
- this._layers = this._layers || {};
- },
- onAdd: function () {
- if (!this._container) {
- this._initContainer(); // defined by renderer implementations
- if (this._zoomAnimated) {
- L.DomUtil.addClass(this._container, 'leaflet-zoom-animated');
- }
- }
- this.getPane().appendChild(this._container);
- this._update();
- this.on('update', this._updatePaths, this);
- },
- onRemove: function () {
- L.DomUtil.remove(this._container);
- this.off('update', this._updatePaths, this);
- },
- getEvents: function () {
- var events = {
- viewreset: this._reset,
- zoom: this._onZoom,
- moveend: this._update,
- zoomend: this._onZoomEnd
- };
- if (this._zoomAnimated) {
- events.zoomanim = this._onAnimZoom;
- }
- return events;
- },
- _onAnimZoom: function (ev) {
- this._updateTransform(ev.center, ev.zoom);
- },
- _onZoom: function () {
- this._updateTransform(this._map.getCenter(), this._map.getZoom());
- },
- _updateTransform: function (center, zoom) {
- var scale = this._map.getZoomScale(zoom, this._zoom),
- position = L.DomUtil.getPosition(this._container),
- viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
- currentCenterPoint = this._map.project(this._center, zoom),
- destCenterPoint = this._map.project(center, zoom),
- centerOffset = destCenterPoint.subtract(currentCenterPoint),
- topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset);
- if (L.Browser.any3d) {
- L.DomUtil.setTransform(this._container, topLeftOffset, scale);
- } else {
- L.DomUtil.setPosition(this._container, topLeftOffset);
- }
- },
- _reset: function () {
- this._update();
- this._updateTransform(this._center, this._zoom);
- for (var id in this._layers) {
- this._layers[id]._reset();
- }
- },
- _onZoomEnd: function () {
- for (var id in this._layers) {
- this._layers[id]._project();
- }
- },
- _updatePaths: function () {
- for (var id in this._layers) {
- this._layers[id]._update();
- }
- },
- _update: function () {
- // Update pixel bounds of renderer container (for positioning/sizing/clipping later)
- // Subclasses are responsible of firing the 'update' event.
- var p = this.options.padding,
- size = this._map.getSize(),
- min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
- this._bounds = new L.Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
- this._center = this._map.getCenter();
- this._zoom = this._map.getZoom();
- }
- });
- L.Map.include({
- // @namespace Map; @method getRenderer(layer: Path): Renderer
- // Returns the instance of `Renderer` that should be used to render the given
- // `Path`. It will ensure that the `renderer` options of the map and paths
- // are respected, and that the renderers do exist on the map.
- getRenderer: function (layer) {
- // @namespace Path; @option renderer: Renderer
- // Use this specific instance of `Renderer` for this path. Takes
- // precedence over the map's [default renderer](#map-renderer).
- var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
- if (!renderer) {
- // @namespace Map; @option preferCanvas: Boolean = false
- // Whether `Path`s should be rendered on a `Canvas` renderer.
- // By default, all `Path`s are rendered in a `SVG` renderer.
- renderer = this._renderer = (this.options.preferCanvas && L.canvas()) || L.svg();
- }
- if (!this.hasLayer(renderer)) {
- this.addLayer(renderer);
- }
- return renderer;
- },
- _getPaneRenderer: function (name) {
- if (name === 'overlayPane' || name === undefined) {
- return false;
- }
- var renderer = this._paneRenderers[name];
- if (renderer === undefined) {
- renderer = (L.SVG && L.svg({pane: name})) || (L.Canvas && L.canvas({pane: name}));
- this._paneRenderers[name] = renderer;
- }
- return renderer;
- }
- });
- /*
- * @class Path
- * @aka L.Path
- * @inherits Interactive layer
- *
- * An abstract class that contains options and constants shared between vector
- * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`.
- */
- L.Path = L.Layer.extend({
- // @section
- // @aka Path options
- options: {
- // @option stroke: Boolean = true
- // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles.
- stroke: true,
- // @option color: String = '#3388ff'
- // Stroke color
- color: '#3388ff',
- // @option weight: Number = 3
- // Stroke width in pixels
- weight: 3,
- // @option opacity: Number = 1.0
- // Stroke opacity
- opacity: 1,
- // @option lineCap: String= 'round'
- // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke.
- lineCap: 'round',
- // @option lineJoin: String = 'round'
- // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke.
- lineJoin: 'round',
- // @option dashArray: String = null
- // A string that defines the stroke [dash pattern](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dasharray). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility).
- dashArray: null,
- // @option dashOffset: String = null
- // A string that defines the [distance into the dash pattern to start the dash](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dashoffset). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility).
- dashOffset: null,
- // @option fill: Boolean = depends
- // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles.
- fill: false,
- // @option fillColor: String = *
- // Fill color. Defaults to the value of the [`color`](#path-color) option
- fillColor: null,
- // @option fillOpacity: Number = 0.2
- // Fill opacity.
- fillOpacity: 0.2,
- // @option fillRule: String = 'evenodd'
- // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined.
- fillRule: 'evenodd',
- // className: '',
- // Option inherited from "Interactive layer" abstract class
- interactive: true
- },
- beforeAdd: function (map) {
- // Renderer is set here because we need to call renderer.getEvents
- // before this.getEvents.
- this._renderer = map.getRenderer(this);
- },
- onAdd: function () {
- this._renderer._initPath(this);
- this._reset();
- this._renderer._addPath(this);
- },
- onRemove: function () {
- this._renderer._removePath(this);
- },
- // @method redraw(): this
- // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses.
- redraw: function () {
- if (this._map) {
- this._renderer._updatePath(this);
- }
- return this;
- },
- // @method setStyle(style: Path options): this
- // Changes the appearance of a Path based on the options in the `Path options` object.
- setStyle: function (style) {
- L.setOptions(this, style);
- if (this._renderer) {
- this._renderer._updateStyle(this);
- }
- return this;
- },
- // @method bringToFront(): this
- // Brings the layer to the top of all path layers.
- bringToFront: function () {
- if (this._renderer) {
- this._renderer._bringToFront(this);
- }
- return this;
- },
- // @method bringToBack(): this
- // Brings the layer to the bottom of all path layers.
- bringToBack: function () {
- if (this._renderer) {
- this._renderer._bringToBack(this);
- }
- return this;
- },
- getElement: function () {
- return this._path;
- },
- _reset: function () {
- // defined in children classes
- this._project();
- this._update();
- },
- _clickTolerance: function () {
- // used when doing hit detection for Canvas layers
- return (this.options.stroke ? this.options.weight / 2 : 0) + (L.Browser.touch ? 10 : 0);
- }
- });
- /*
- * @namespace LineUtil
- *
- * Various utility functions for polyine points processing, used by Leaflet internally to make polylines lightning-fast.
- */
- L.LineUtil = {
- // Simplify polyline with vertex reduction and Douglas-Peucker simplification.
- // Improves rendering performance dramatically by lessening the number of points to draw.
- // @function simplify(points: Point[], tolerance: Number): Point[]
- // Dramatically reduces the number of points in a polyline while retaining
- // its shape and returns a new array of simplified points, using the
- // [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm).
- // Used for a huge performance boost when processing/displaying Leaflet polylines for
- // each zoom level and also reducing visual noise. tolerance affects the amount of
- // simplification (lesser value means higher quality but slower and with more points).
- // Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/).
- simplify: function (points, tolerance) {
- if (!tolerance || !points.length) {
- return points.slice();
- }
- var sqTolerance = tolerance * tolerance;
- // stage 1: vertex reduction
- points = this._reducePoints(points, sqTolerance);
- // stage 2: Douglas-Peucker simplification
- points = this._simplifyDP(points, sqTolerance);
- return points;
- },
- // @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number
- // Returns the distance between point `p` and segment `p1` to `p2`.
- pointToSegmentDistance: function (p, p1, p2) {
- return Math.sqrt(this._sqClosestPointOnSegment(p, p1, p2, true));
- },
- // @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number
- // Returns the closest point from a point `p` on a segment `p1` to `p2`.
- closestPointOnSegment: function (p, p1, p2) {
- return this._sqClosestPointOnSegment(p, p1, p2);
- },
- // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
- _simplifyDP: function (points, sqTolerance) {
- var len = points.length,
- ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
- markers = new ArrayConstructor(len);
- markers[0] = markers[len - 1] = 1;
- this._simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
- var i,
- newPoints = [];
- for (i = 0; i < len; i++) {
- if (markers[i]) {
- newPoints.push(points[i]);
- }
- }
- return newPoints;
- },
- _simplifyDPStep: function (points, markers, sqTolerance, first, last) {
- var maxSqDist = 0,
- index, i, sqDist;
- for (i = first + 1; i <= last - 1; i++) {
- sqDist = this._sqClosestPointOnSegment(points[i], points[first], points[last], true);
- if (sqDist > maxSqDist) {
- index = i;
- maxSqDist = sqDist;
- }
- }
- if (maxSqDist > sqTolerance) {
- markers[index] = 1;
- this._simplifyDPStep(points, markers, sqTolerance, first, index);
- this._simplifyDPStep(points, markers, sqTolerance, index, last);
- }
- },
- // reduce points that are too close to each other to a single point
- _reducePoints: function (points, sqTolerance) {
- var reducedPoints = [points[0]];
- for (var i = 1, prev = 0, len = points.length; i < len; i++) {
- if (this._sqDist(points[i], points[prev]) > sqTolerance) {
- reducedPoints.push(points[i]);
- prev = i;
- }
- }
- if (prev < len - 1) {
- reducedPoints.push(points[len - 1]);
- }
- return reducedPoints;
- },
- // @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean
- // Clips the segment a to b by rectangular bounds with the
- // [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
- // (modifying the segment points directly!). Used by Leaflet to only show polyline
- // points that are on the screen or near, increasing performance.
- clipSegment: function (a, b, bounds, useLastCode, round) {
- var codeA = useLastCode ? this._lastCode : this._getBitCode(a, bounds),
- codeB = this._getBitCode(b, bounds),
- codeOut, p, newCode;
- // save 2nd code to avoid calculating it on the next segment
- this._lastCode = codeB;
- while (true) {
- // if a,b is inside the clip window (trivial accept)
- if (!(codeA | codeB)) {
- return [a, b];
- }
- // if a,b is outside the clip window (trivial reject)
- if (codeA & codeB) {
- return false;
- }
- // other cases
- codeOut = codeA || codeB;
- p = this._getEdgeIntersection(a, b, codeOut, bounds, round);
- newCode = this._getBitCode(p, bounds);
- if (codeOut === codeA) {
- a = p;
- codeA = newCode;
- } else {
- b = p;
- codeB = newCode;
- }
- }
- },
- _getEdgeIntersection: function (a, b, code, bounds, round) {
- var dx = b.x - a.x,
- dy = b.y - a.y,
- min = bounds.min,
- max = bounds.max,
- x, y;
- if (code & 8) { // top
- x = a.x + dx * (max.y - a.y) / dy;
- y = max.y;
- } else if (code & 4) { // bottom
- x = a.x + dx * (min.y - a.y) / dy;
- y = min.y;
- } else if (code & 2) { // right
- x = max.x;
- y = a.y + dy * (max.x - a.x) / dx;
- } else if (code & 1) { // left
- x = min.x;
- y = a.y + dy * (min.x - a.x) / dx;
- }
- return new L.Point(x, y, round);
- },
- _getBitCode: function (p, bounds) {
- var code = 0;
- if (p.x < bounds.min.x) { // left
- code |= 1;
- } else if (p.x > bounds.max.x) { // right
- code |= 2;
- }
- if (p.y < bounds.min.y) { // bottom
- code |= 4;
- } else if (p.y > bounds.max.y) { // top
- code |= 8;
- }
- return code;
- },
- // square distance (to avoid unnecessary Math.sqrt calls)
- _sqDist: function (p1, p2) {
- var dx = p2.x - p1.x,
- dy = p2.y - p1.y;
- return dx * dx + dy * dy;
- },
- // return closest point on segment or distance to that point
- _sqClosestPointOnSegment: function (p, p1, p2, sqDist) {
- var x = p1.x,
- y = p1.y,
- dx = p2.x - x,
- dy = p2.y - y,
- dot = dx * dx + dy * dy,
- t;
- if (dot > 0) {
- t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
- if (t > 1) {
- x = p2.x;
- y = p2.y;
- } else if (t > 0) {
- x += dx * t;
- y += dy * t;
- }
- }
- dx = p.x - x;
- dy = p.y - y;
- return sqDist ? dx * dx + dy * dy : new L.Point(x, y);
- }
- };
- /*
- * @class Polyline
- * @aka L.Polyline
- * @inherits Path
- *
- * A class for drawing polyline overlays on a map. Extends `Path`.
- *
- * @example
- *
- * ```js
- * // create a red polyline from an array of LatLng points
- * var latlngs = [
- * [-122.68, 45.51],
- * [-122.43, 37.77],
- * [-118.2, 34.04]
- * ];
- *
- * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
- *
- * // zoom the map to the polyline
- * map.fitBounds(polyline.getBounds());
- * ```
- *
- * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape:
- *
- * ```js
- * // create a red polyline from an array of arrays of LatLng points
- * var latlngs = [
- * [[-122.68, 45.51],
- * [-122.43, 37.77],
- * [-118.2, 34.04]],
- * [[-73.91, 40.78],
- * [-87.62, 41.83],
- * [-96.72, 32.76]]
- * ];
- * ```
- */
- L.Polyline = L.Path.extend({
- // @section
- // @aka Polyline options
- options: {
- // @option smoothFactor: Number = 1.0
- // How much to simplify the polyline on each zoom level. More means
- // better performance and smoother look, and less means more accurate representation.
- smoothFactor: 1.0,
- // @option noClip: Boolean = false
- // Disable polyline clipping.
- noClip: false
- },
- initialize: function (latlngs, options) {
- L.setOptions(this, options);
- this._setLatLngs(latlngs);
- },
- // @method getLatLngs(): LatLng[]
- // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline.
- getLatLngs: function () {
- return this._latlngs;
- },
- // @method setLatLngs(latlngs: LatLng[]): this
- // Replaces all the points in the polyline with the given array of geographical points.
- setLatLngs: function (latlngs) {
- this._setLatLngs(latlngs);
- return this.redraw();
- },
- // @method isEmpty(): Boolean
- // Returns `true` if the Polyline has no LatLngs.
- isEmpty: function () {
- return !this._latlngs.length;
- },
- closestLayerPoint: function (p) {
- var minDistance = Infinity,
- minPoint = null,
- closest = L.LineUtil._sqClosestPointOnSegment,
- p1, p2;
- for (var j = 0, jLen = this._parts.length; j < jLen; j++) {
- var points = this._parts[j];
- for (var i = 1, len = points.length; i < len; i++) {
- p1 = points[i - 1];
- p2 = points[i];
- var sqDist = closest(p, p1, p2, true);
- if (sqDist < minDistance) {
- minDistance = sqDist;
- minPoint = closest(p, p1, p2);
- }
- }
- }
- if (minPoint) {
- minPoint.distance = Math.sqrt(minDistance);
- }
- return minPoint;
- },
- // @method getCenter(): LatLng
- // Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline.
- getCenter: function () {
- // throws error when not yet added to map as this center calculation requires projected coordinates
- if (!this._map) {
- throw new Error('Must add layer to map before using getCenter()');
- }
- var i, halfDist, segDist, dist, p1, p2, ratio,
- points = this._rings[0],
- len = points.length;
- if (!len) { return null; }
- // polyline centroid algorithm; only uses the first ring if there are multiple
- for (i = 0, halfDist = 0; i < len - 1; i++) {
- halfDist += points[i].distanceTo(points[i + 1]) / 2;
- }
- // The line is so small in the current view that all points are on the same pixel.
- if (halfDist === 0) {
- return this._map.layerPointToLatLng(points[0]);
- }
- for (i = 0, dist = 0; i < len - 1; i++) {
- p1 = points[i];
- p2 = points[i + 1];
- segDist = p1.distanceTo(p2);
- dist += segDist;
- if (dist > halfDist) {
- ratio = (dist - halfDist) / segDist;
- return this._map.layerPointToLatLng([
- p2.x - ratio * (p2.x - p1.x),
- p2.y - ratio * (p2.y - p1.y)
- ]);
- }
- }
- },
- // @method getBounds(): LatLngBounds
- // Returns the `LatLngBounds` of the path.
- getBounds: function () {
- return this._bounds;
- },
- // @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this
- // Adds a given point to the polyline. By default, adds to the first ring of
- // the polyline in case of a multi-polyline, but can be overridden by passing
- // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)).
- addLatLng: function (latlng, latlngs) {
- latlngs = latlngs || this._defaultShape();
- latlng = L.latLng(latlng);
- latlngs.push(latlng);
- this._bounds.extend(latlng);
- return this.redraw();
- },
- _setLatLngs: function (latlngs) {
- this._bounds = new L.LatLngBounds();
- this._latlngs = this._convertLatLngs(latlngs);
- },
- _defaultShape: function () {
- return L.Polyline._flat(this._latlngs) ? this._latlngs : this._latlngs[0];
- },
- // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way
- _convertLatLngs: function (latlngs) {
- var result = [],
- flat = L.Polyline._flat(latlngs);
- for (var i = 0, len = latlngs.length; i < len; i++) {
- if (flat) {
- result[i] = L.latLng(latlngs[i]);
- this._bounds.extend(result[i]);
- } else {
- result[i] = this._convertLatLngs(latlngs[i]);
- }
- }
- return result;
- },
- _project: function () {
- var pxBounds = new L.Bounds();
- this._rings = [];
- this._projectLatlngs(this._latlngs, this._rings, pxBounds);
- var w = this._clickTolerance(),
- p = new L.Point(w, w);
- if (this._bounds.isValid() && pxBounds.isValid()) {
- pxBounds.min._subtract(p);
- pxBounds.max._add(p);
- this._pxBounds = pxBounds;
- }
- },
- // recursively turns latlngs into a set of rings with projected coordinates
- _projectLatlngs: function (latlngs, result, projectedBounds) {
- var flat = latlngs[0] instanceof L.LatLng,
- len = latlngs.length,
- i, ring;
- if (flat) {
- ring = [];
- for (i = 0; i < len; i++) {
- ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
- projectedBounds.extend(ring[i]);
- }
- result.push(ring);
- } else {
- for (i = 0; i < len; i++) {
- this._projectLatlngs(latlngs[i], result, projectedBounds);
- }
- }
- },
- // clip polyline by renderer bounds so that we have less to render for performance
- _clipPoints: function () {
- var bounds = this._renderer._bounds;
- this._parts = [];
- if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
- return;
- }
- if (this.options.noClip) {
- this._parts = this._rings;
- return;
- }
- var parts = this._parts,
- i, j, k, len, len2, segment, points;
- for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
- points = this._rings[i];
- for (j = 0, len2 = points.length; j < len2 - 1; j++) {
- segment = L.LineUtil.clipSegment(points[j], points[j + 1], bounds, j, true);
- if (!segment) { continue; }
- parts[k] = parts[k] || [];
- parts[k].push(segment[0]);
- // if segment goes out of screen, or it's the last one, it's the end of the line part
- if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) {
- parts[k].push(segment[1]);
- k++;
- }
- }
- }
- },
- // simplify each clipped part of the polyline for performance
- _simplifyPoints: function () {
- var parts = this._parts,
- tolerance = this.options.smoothFactor;
- for (var i = 0, len = parts.length; i < len; i++) {
- parts[i] = L.LineUtil.simplify(parts[i], tolerance);
- }
- },
- _update: function () {
- if (!this._map) { return; }
- this._clipPoints();
- this._simplifyPoints();
- this._updatePath();
- },
- _updatePath: function () {
- this._renderer._updatePoly(this);
- }
- });
- // @factory L.polyline(latlngs: LatLng[], options?: Polyline options)
- // Instantiates a polyline object given an array of geographical points and
- // optionally an options object. You can create a `Polyline` object with
- // multiple separate lines (`MultiPolyline`) by passing an array of arrays
- // of geographic points.
- L.polyline = function (latlngs, options) {
- return new L.Polyline(latlngs, options);
- };
- L.Polyline._flat = function (latlngs) {
- // true if it's a flat array of latlngs; false if nested
- return !L.Util.isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
- };
- /*
- * @namespace PolyUtil
- * Various utility functions for polygon geometries.
- */
- L.PolyUtil = {};
- /* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[]
- * Clips the polygon geometry defined by the given `points` by the given bounds (using the [Sutherland-Hodgeman algorithm](https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm)).
- * Used by Leaflet to only show polygon points that are on the screen or near, increasing
- * performance. Note that polygon points needs different algorithm for clipping
- * than polyline, so there's a seperate method for it.
- */
- L.PolyUtil.clipPolygon = function (points, bounds, round) {
- var clippedPoints,
- edges = [1, 4, 2, 8],
- i, j, k,
- a, b,
- len, edge, p,
- lu = L.LineUtil;
- for (i = 0, len = points.length; i < len; i++) {
- points[i]._code = lu._getBitCode(points[i], bounds);
- }
- // for each edge (left, bottom, right, top)
- for (k = 0; k < 4; k++) {
- edge = edges[k];
- clippedPoints = [];
- for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
- a = points[i];
- b = points[j];
- // if a is inside the clip window
- if (!(a._code & edge)) {
- // if b is outside the clip window (a->b goes out of screen)
- if (b._code & edge) {
- p = lu._getEdgeIntersection(b, a, edge, bounds, round);
- p._code = lu._getBitCode(p, bounds);
- clippedPoints.push(p);
- }
- clippedPoints.push(a);
- // else if b is inside the clip window (a->b enters the screen)
- } else if (!(b._code & edge)) {
- p = lu._getEdgeIntersection(b, a, edge, bounds, round);
- p._code = lu._getBitCode(p, bounds);
- clippedPoints.push(p);
- }
- }
- points = clippedPoints;
- }
- return points;
- };
- /*
- * @class Polygon
- * @aka L.Polygon
- * @inherits Polyline
- *
- * A class for drawing polygon overlays on a map. Extends `Polyline`.
- *
- * Note that points you pass when creating a polygon shouldn't have an additional last point equal to the first one — it's better to filter out such points.
- *
- *
- * @example
- *
- * ```js
- * // create a red polygon from an array of LatLng points
- * var latlngs = [[-111.03, 41],[-111.04, 45],[-104.05, 45],[-104.05, 41]];
- *
- * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map);
- *
- * // zoom the map to the polygon
- * map.fitBounds(polygon.getBounds());
- * ```
- *
- * You can also pass an array of arrays of latlngs, with the first array representing the outer shape and the other arrays representing holes in the outer shape:
- *
- * ```js
- * var latlngs = [
- * [[-111.03, 41],[-111.04, 45],[-104.05, 45],[-104.05, 41]], // outer ring
- * [[-108.58,37.29],[-108.58,40.71],[-102.50,40.71],[-102.50,37.29]] // hole
- * ];
- * ```
- *
- * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape.
- *
- * ```js
- * var latlngs = [
- * [ // first polygon
- * [[-111.03, 41],[-111.04, 45],[-104.05, 45],[-104.05, 41]], // outer ring
- * [[-108.58,37.29],[-108.58,40.71],[-102.50,40.71],[-102.50,37.29]] // hole
- * ],
- * [ // second polygon
- * [[-109.05, 37],[-109.03, 41],[-102.05, 41],[-102.04, 37],[-109.05, 38]]
- * ]
- * ];
- * ```
- */
- L.Polygon = L.Polyline.extend({
- options: {
- fill: true
- },
- isEmpty: function () {
- return !this._latlngs.length || !this._latlngs[0].length;
- },
- getCenter: function () {
- // throws error when not yet added to map as this center calculation requires projected coordinates
- if (!this._map) {
- throw new Error('Must add layer to map before using getCenter()');
- }
- var i, j, p1, p2, f, area, x, y, center,
- points = this._rings[0],
- len = points.length;
- if (!len) { return null; }
- // polygon centroid algorithm; only uses the first ring if there are multiple
- area = x = y = 0;
- for (i = 0, j = len - 1; i < len; j = i++) {
- p1 = points[i];
- p2 = points[j];
- f = p1.y * p2.x - p2.y * p1.x;
- x += (p1.x + p2.x) * f;
- y += (p1.y + p2.y) * f;
- area += f * 3;
- }
- if (area === 0) {
- // Polygon is so small that all points are on same pixel.
- center = points[0];
- } else {
- center = [x / area, y / area];
- }
- return this._map.layerPointToLatLng(center);
- },
- _convertLatLngs: function (latlngs) {
- var result = L.Polyline.prototype._convertLatLngs.call(this, latlngs),
- len = result.length;
- // remove last point if it equals first one
- if (len >= 2 && result[0] instanceof L.LatLng && result[0].equals(result[len - 1])) {
- result.pop();
- }
- return result;
- },
- _setLatLngs: function (latlngs) {
- L.Polyline.prototype._setLatLngs.call(this, latlngs);
- if (L.Polyline._flat(this._latlngs)) {
- this._latlngs = [this._latlngs];
- }
- },
- _defaultShape: function () {
- return L.Polyline._flat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0];
- },
- _clipPoints: function () {
- // polygons need a different clipping algorithm so we redefine that
- var bounds = this._renderer._bounds,
- w = this.options.weight,
- p = new L.Point(w, w);
- // increase clip padding by stroke width to avoid stroke on clip edges
- bounds = new L.Bounds(bounds.min.subtract(p), bounds.max.add(p));
- this._parts = [];
- if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
- return;
- }
- if (this.options.noClip) {
- this._parts = this._rings;
- return;
- }
- for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
- clipped = L.PolyUtil.clipPolygon(this._rings[i], bounds, true);
- if (clipped.length) {
- this._parts.push(clipped);
- }
- }
- },
- _updatePath: function () {
- this._renderer._updatePoly(this, true);
- }
- });
- // @factory L.polygon(latlngs: LatLng[], options?: Polyline options)
- L.polygon = function (latlngs, options) {
- return new L.Polygon(latlngs, options);
- };
- /*
- * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
- */
- /*
- * @class Rectangle
- * @aka L.Retangle
- * @inherits Polygon
- *
- * A class for drawing rectangle overlays on a map. Extends `Polygon`.
- *
- * @example
- *
- * ```js
- * // define rectangle geographical bounds
- * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]];
- *
- * // create an orange rectangle
- * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map);
- *
- * // zoom the map to the rectangle bounds
- * map.fitBounds(bounds);
- * ```
- *
- */
- L.Rectangle = L.Polygon.extend({
- initialize: function (latLngBounds, options) {
- L.Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
- },
- // @method setBounds(latLngBounds: LatLngBounds): this
- // Redraws the rectangle with the passed bounds.
- setBounds: function (latLngBounds) {
- return this.setLatLngs(this._boundsToLatLngs(latLngBounds));
- },
- _boundsToLatLngs: function (latLngBounds) {
- latLngBounds = L.latLngBounds(latLngBounds);
- return [
- latLngBounds.getSouthWest(),
- latLngBounds.getNorthWest(),
- latLngBounds.getNorthEast(),
- latLngBounds.getSouthEast()
- ];
- }
- });
- // @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options)
- L.rectangle = function (latLngBounds, options) {
- return new L.Rectangle(latLngBounds, options);
- };
- /*
- * @class CircleMarker
- * @aka L.CircleMarker
- * @inherits Path
- *
- * A circle of a fixed size with radius specified in pixels. Extends `Path`.
- */
- L.CircleMarker = L.Path.extend({
- // @section
- // @aka CircleMarker options
- options: {
- fill: true,
- // @option radius: Number = 10
- // Radius of the circle marker, in pixels
- radius: 10
- },
- initialize: function (latlng, options) {
- L.setOptions(this, options);
- this._latlng = L.latLng(latlng);
- this._radius = this.options.radius;
- },
- // @method setLatLng(latLng: LatLng): this
- // Sets the position of a circle marker to a new location.
- setLatLng: function (latlng) {
- this._latlng = L.latLng(latlng);
- this.redraw();
- return this.fire('move', {latlng: this._latlng});
- },
- // @method getLatLng(): LatLng
- // Returns the current geographical position of the circle marker
- getLatLng: function () {
- return this._latlng;
- },
- // @method setRadius(radius: Number): this
- // Sets the radius of a circle marker. Units are in pixels.
- setRadius: function (radius) {
- this.options.radius = this._radius = radius;
- return this.redraw();
- },
- // @method getRadius(): Number
- // Returns the current radius of the circle
- getRadius: function () {
- return this._radius;
- },
- setStyle : function (options) {
- var radius = options && options.radius || this._radius;
- L.Path.prototype.setStyle.call(this, options);
- this.setRadius(radius);
- return this;
- },
- _project: function () {
- this._point = this._map.latLngToLayerPoint(this._latlng);
- this._updateBounds();
- },
- _updateBounds: function () {
- var r = this._radius,
- r2 = this._radiusY || r,
- w = this._clickTolerance(),
- p = [r + w, r2 + w];
- this._pxBounds = new L.Bounds(this._point.subtract(p), this._point.add(p));
- },
- _update: function () {
- if (this._map) {
- this._updatePath();
- }
- },
- _updatePath: function () {
- this._renderer._updateCircle(this);
- },
- _empty: function () {
- return this._radius && !this._renderer._bounds.intersects(this._pxBounds);
- }
- });
- // @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options)
- // Instantiates a circle marker object given a geographical point, and an optional options object.
- L.circleMarker = function (latlng, options) {
- return new L.CircleMarker(latlng, options);
- };
- /*
- * @class Circle
- * @aka L.Circle
- * @inherits CircleMarker
- *
- * A class for drawing circle overlays on a map. Extends `CircleMarker`.
- *
- * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion).
- *
- * @example
- *
- * ```js
- * L.circle([50.5, 30.5], {radius: 200}).addTo(map);
- * ```
- */
- L.Circle = L.CircleMarker.extend({
- initialize: function (latlng, options, legacyOptions) {
- if (typeof options === 'number') {
- // Backwards compatibility with 0.7.x factory (latlng, radius, options?)
- options = L.extend({}, legacyOptions, {radius: options});
- }
- L.setOptions(this, options);
- this._latlng = L.latLng(latlng);
- if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); }
- // @section
- // @aka Circle options
- // @option radius: Number; Radius of the circle, in meters.
- this._mRadius = this.options.radius;
- },
- // @method setRadius(radius: Number): this
- // Sets the radius of a circle. Units are in meters.
- setRadius: function (radius) {
- this._mRadius = radius;
- return this.redraw();
- },
- // @method getRadius(): Number
- // Returns the current radius of a circle. Units are in meters.
- getRadius: function () {
- return this._mRadius;
- },
- // @method getBounds(): LatLngBounds
- // Returns the `LatLngBounds` of the path.
- getBounds: function () {
- var half = [this._radius, this._radiusY || this._radius];
- return new L.LatLngBounds(
- this._map.layerPointToLatLng(this._point.subtract(half)),
- this._map.layerPointToLatLng(this._point.add(half)));
- },
- setStyle: L.Path.prototype.setStyle,
- _project: function () {
- var lng = this._latlng.lng,
- lat = this._latlng.lat,
- map = this._map,
- crs = map.options.crs;
- if (crs.distance === L.CRS.Earth.distance) {
- var d = Math.PI / 180,
- latR = (this._mRadius / L.CRS.Earth.R) / d,
- top = map.project([lat + latR, lng]),
- bottom = map.project([lat - latR, lng]),
- p = top.add(bottom).divideBy(2),
- lat2 = map.unproject(p).lat,
- lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) /
- (Math.cos(lat * d) * Math.cos(lat2 * d))) / d;
- if (isNaN(lngR) || lngR === 0) {
- lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425
- }
- this._point = p.subtract(map.getPixelOrigin());
- this._radius = isNaN(lngR) ? 0 : Math.max(Math.round(p.x - map.project([lat2, lng - lngR]).x), 1);
- this._radiusY = Math.max(Math.round(p.y - top.y), 1);
- } else {
- var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0]));
- this._point = map.latLngToLayerPoint(this._latlng);
- this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x;
- }
- this._updateBounds();
- }
- });
- // @factory L.circle(latlng: LatLng, options?: Circle options)
- // Instantiates a circle object given a geographical point, and an options object
- // which contains the circle radius.
- // @alternative
- // @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options)
- // Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
- // Do not use in new applications or plugins.
- L.circle = function (latlng, options, legacyOptions) {
- return new L.Circle(latlng, options, legacyOptions);
- };
- /*
- * @class SVG
- * @inherits Renderer
- * @aka L.SVG
- *
- * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG).
- * Inherits `Renderer`.
- *
- * Due to [technical limitations](http://caniuse.com/#search=svg), SVG is not
- * available in all web browsers, notably Android 2.x and 3.x.
- *
- * Although SVG is not available on IE7 and IE8, these browsers support
- * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language)
- * (a now deprecated technology), and the SVG renderer will fall back to VML in
- * this case.
- *
- * @example
- *
- * Use SVG by default for all paths in the map:
- *
- * ```js
- * var map = L.map('map', {
- * renderer: L.svg()
- * });
- * ```
- *
- * Use a SVG renderer with extra padding for specific vector geometries:
- *
- * ```js
- * var map = L.map('map');
- * var myRenderer = L.svg({ padding: 0.5 });
- * var line = L.polyline( coordinates, { renderer: myRenderer } );
- * var circle = L.circle( center, { renderer: myRenderer } );
- * ```
- */
- L.SVG = L.Renderer.extend({
- getEvents: function () {
- var events = L.Renderer.prototype.getEvents.call(this);
- events.zoomstart = this._onZoomStart;
- return events;
- },
- _initContainer: function () {
- this._container = L.SVG.create('svg');
- // makes it possible to click through svg root; we'll reset it back in individual paths
- this._container.setAttribute('pointer-events', 'none');
- this._rootGroup = L.SVG.create('g');
- this._container.appendChild(this._rootGroup);
- },
- _onZoomStart: function () {
- // Drag-then-pinch interactions might mess up the center and zoom.
- // In this case, the easiest way to prevent this is re-do the renderer
- // bounds and padding when the zooming starts.
- this._update();
- },
- _update: function () {
- if (this._map._animatingZoom && this._bounds) { return; }
- L.Renderer.prototype._update.call(this);
- var b = this._bounds,
- size = b.getSize(),
- container = this._container;
- // set size of svg-container if changed
- if (!this._svgSize || !this._svgSize.equals(size)) {
- this._svgSize = size;
- container.setAttribute('width', size.x);
- container.setAttribute('height', size.y);
- }
- // movement: update container viewBox so that we don't have to change coordinates of individual layers
- L.DomUtil.setPosition(container, b.min);
- container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' '));
- this.fire('update');
- },
- // methods below are called by vector layers implementations
- _initPath: function (layer) {
- var path = layer._path = L.SVG.create('path');
- // @namespace Path
- // @option className: String = null
- // Custom class name set on an element. Only for SVG renderer.
- if (layer.options.className) {
- L.DomUtil.addClass(path, layer.options.className);
- }
- if (layer.options.interactive) {
- L.DomUtil.addClass(path, 'leaflet-interactive');
- }
- this._updateStyle(layer);
- this._layers[L.stamp(layer)] = layer;
- },
- _addPath: function (layer) {
- this._rootGroup.appendChild(layer._path);
- layer.addInteractiveTarget(layer._path);
- },
- _removePath: function (layer) {
- L.DomUtil.remove(layer._path);
- layer.removeInteractiveTarget(layer._path);
- delete this._layers[L.stamp(layer)];
- },
- _updatePath: function (layer) {
- layer._project();
- layer._update();
- },
- _updateStyle: function (layer) {
- var path = layer._path,
- options = layer.options;
- if (!path) { return; }
- if (options.stroke) {
- path.setAttribute('stroke', options.color);
- path.setAttribute('stroke-opacity', options.opacity);
- path.setAttribute('stroke-width', options.weight);
- path.setAttribute('stroke-linecap', options.lineCap);
- path.setAttribute('stroke-linejoin', options.lineJoin);
- if (options.dashArray) {
- path.setAttribute('stroke-dasharray', options.dashArray);
- } else {
- path.removeAttribute('stroke-dasharray');
- }
- if (options.dashOffset) {
- path.setAttribute('stroke-dashoffset', options.dashOffset);
- } else {
- path.removeAttribute('stroke-dashoffset');
- }
- } else {
- path.setAttribute('stroke', 'none');
- }
- if (options.fill) {
- path.setAttribute('fill', options.fillColor || options.color);
- path.setAttribute('fill-opacity', options.fillOpacity);
- path.setAttribute('fill-rule', options.fillRule || 'evenodd');
- } else {
- path.setAttribute('fill', 'none');
- }
- },
- _updatePoly: function (layer, closed) {
- this._setPath(layer, L.SVG.pointsToPath(layer._parts, closed));
- },
- _updateCircle: function (layer) {
- var p = layer._point,
- r = layer._radius,
- r2 = layer._radiusY || r,
- arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
- // drawing a circle with two half-arcs
- var d = layer._empty() ? 'M0 0' :
- 'M' + (p.x - r) + ',' + p.y +
- arc + (r * 2) + ',0 ' +
- arc + (-r * 2) + ',0 ';
- this._setPath(layer, d);
- },
- _setPath: function (layer, path) {
- layer._path.setAttribute('d', path);
- },
- // SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
- _bringToFront: function (layer) {
- L.DomUtil.toFront(layer._path);
- },
- _bringToBack: function (layer) {
- L.DomUtil.toBack(layer._path);
- }
- });
- // @namespace SVG; @section
- // There are several static functions which can be called without instantiating L.SVG:
- L.extend(L.SVG, {
- // @function create(name: String): SVGElement
- // Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement),
- // corresponding to the class name passed. For example, using 'line' will return
- // an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement).
- create: function (name) {
- return document.createElementNS('http://www.w3.org/2000/svg', name);
- },
- // @function pointsToPath(rings: Point[], closed: Boolean): String
- // Generates a SVG path string for multiple rings, with each ring turning
- // into "M..L..L.." instructions
- pointsToPath: function (rings, closed) {
- var str = '',
- i, j, len, len2, points, p;
- for (i = 0, len = rings.length; i < len; i++) {
- points = rings[i];
- for (j = 0, len2 = points.length; j < len2; j++) {
- p = points[j];
- str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
- }
- // closes the ring for polygons; "x" is VML syntax
- str += closed ? (L.Browser.svg ? 'z' : 'x') : '';
- }
- // SVG complains about empty path strings
- return str || 'M0 0';
- }
- });
- // @namespace Browser; @property svg: Boolean
- // `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG).
- L.Browser.svg = !!(document.createElementNS && L.SVG.create('svg').createSVGRect);
- // @namespace SVG
- // @factory L.svg(options?: Renderer options)
- // Creates a SVG renderer with the given options.
- L.svg = function (options) {
- return L.Browser.svg || L.Browser.vml ? new L.SVG(options) : null;
- };
- /*
- * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
- */
- /*
- * @class SVG
- *
- * Although SVG is not available on IE7 and IE8, these browsers support [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language), and the SVG renderer will fall back to VML in this case.
- *
- * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility
- * with old versions of Internet Explorer.
- */
- // @namespace Browser; @property vml: Boolean
- // `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language).
- L.Browser.vml = !L.Browser.svg && (function () {
- try {
- var div = document.createElement('div');
- div.innerHTML = '<v:shape adj="1"/>';
- var shape = div.firstChild;
- shape.style.behavior = 'url(#default#VML)';
- return shape && (typeof shape.adj === 'object');
- } catch (e) {
- return false;
- }
- }());
- // redefine some SVG methods to handle VML syntax which is similar but with some differences
- L.SVG.include(!L.Browser.vml ? {} : {
- _initContainer: function () {
- this._container = L.DomUtil.create('div', 'leaflet-vml-container');
- },
- _update: function () {
- if (this._map._animatingZoom) { return; }
- L.Renderer.prototype._update.call(this);
- this.fire('update');
- },
- _initPath: function (layer) {
- var container = layer._container = L.SVG.create('shape');
- L.DomUtil.addClass(container, 'leaflet-vml-shape ' + (this.options.className || ''));
- container.coordsize = '1 1';
- layer._path = L.SVG.create('path');
- container.appendChild(layer._path);
- this._updateStyle(layer);
- },
- _addPath: function (layer) {
- var container = layer._container;
- this._container.appendChild(container);
- if (layer.options.interactive) {
- layer.addInteractiveTarget(container);
- }
- },
- _removePath: function (layer) {
- var container = layer._container;
- L.DomUtil.remove(container);
- layer.removeInteractiveTarget(container);
- },
- _updateStyle: function (layer) {
- var stroke = layer._stroke,
- fill = layer._fill,
- options = layer.options,
- container = layer._container;
- container.stroked = !!options.stroke;
- container.filled = !!options.fill;
- if (options.stroke) {
- if (!stroke) {
- stroke = layer._stroke = L.SVG.create('stroke');
- }
- container.appendChild(stroke);
- stroke.weight = options.weight + 'px';
- stroke.color = options.color;
- stroke.opacity = options.opacity;
- if (options.dashArray) {
- stroke.dashStyle = L.Util.isArray(options.dashArray) ?
- options.dashArray.join(' ') :
- options.dashArray.replace(/( *, *)/g, ' ');
- } else {
- stroke.dashStyle = '';
- }
- stroke.endcap = options.lineCap.replace('butt', 'flat');
- stroke.joinstyle = options.lineJoin;
- } else if (stroke) {
- container.removeChild(stroke);
- layer._stroke = null;
- }
- if (options.fill) {
- if (!fill) {
- fill = layer._fill = L.SVG.create('fill');
- }
- container.appendChild(fill);
- fill.color = options.fillColor || options.color;
- fill.opacity = options.fillOpacity;
- } else if (fill) {
- container.removeChild(fill);
- layer._fill = null;
- }
- },
- _updateCircle: function (layer) {
- var p = layer._point.round(),
- r = Math.round(layer._radius),
- r2 = Math.round(layer._radiusY || r);
- this._setPath(layer, layer._empty() ? 'M0 0' :
- 'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360));
- },
- _setPath: function (layer, path) {
- layer._path.v = path;
- },
- _bringToFront: function (layer) {
- L.DomUtil.toFront(layer._container);
- },
- _bringToBack: function (layer) {
- L.DomUtil.toBack(layer._container);
- }
- });
- if (L.Browser.vml) {
- L.SVG.create = (function () {
- try {
- document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
- return function (name) {
- return document.createElement('<lvml:' + name + ' class="lvml">');
- };
- } catch (e) {
- return function (name) {
- return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
- };
- }
- })();
- }
- /*
- * @class Canvas
- * @inherits Renderer
- * @aka L.Canvas
- *
- * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
- * Inherits `Renderer`.
- *
- * Due to [technical limitations](http://caniuse.com/#search=canvas), Canvas is not
- * available in all web browsers, notably IE8, and overlapping geometries might
- * not display properly in some edge cases.
- *
- * @example
- *
- * Use Canvas by default for all paths in the map:
- *
- * ```js
- * var map = L.map('map', {
- * renderer: L.canvas()
- * });
- * ```
- *
- * Use a Canvas renderer with extra padding for specific vector geometries:
- *
- * ```js
- * var map = L.map('map');
- * var myRenderer = L.canvas({ padding: 0.5 });
- * var line = L.polyline( coordinates, { renderer: myRenderer } );
- * var circle = L.circle( center, { renderer: myRenderer } );
- * ```
- */
- L.Canvas = L.Renderer.extend({
- onAdd: function () {
- L.Renderer.prototype.onAdd.call(this);
- // Redraw vectors since canvas is cleared upon removal,
- // in case of removing the renderer itself from the map.
- this._draw();
- },
- _initContainer: function () {
- var container = this._container = document.createElement('canvas');
- L.DomEvent
- .on(container, 'mousemove', L.Util.throttle(this._onMouseMove, 32, this), this)
- .on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this)
- .on(container, 'mouseout', this._handleMouseOut, this);
- this._ctx = container.getContext('2d');
- },
- _updatePaths: function () {
- var layer;
- this._redrawBounds = null;
- for (var id in this._layers) {
- layer = this._layers[id];
- layer._update();
- }
- this._redraw();
- },
- _update: function () {
- if (this._map._animatingZoom && this._bounds) { return; }
- this._drawnLayers = {};
- L.Renderer.prototype._update.call(this);
- var b = this._bounds,
- container = this._container,
- size = b.getSize(),
- m = L.Browser.retina ? 2 : 1;
- L.DomUtil.setPosition(container, b.min);
- // set canvas size (also clearing it); use double size on retina
- container.width = m * size.x;
- container.height = m * size.y;
- container.style.width = size.x + 'px';
- container.style.height = size.y + 'px';
- if (L.Browser.retina) {
- this._ctx.scale(2, 2);
- }
- // translate so we use the same path coordinates after canvas element moves
- this._ctx.translate(-b.min.x, -b.min.y);
- // Tell paths to redraw themselves
- this.fire('update');
- },
- _initPath: function (layer) {
- this._updateDashArray(layer);
- this._layers[L.stamp(layer)] = layer;
- var order = layer._order = {
- layer: layer,
- prev: this._drawLast,
- next: null
- };
- if (this._drawLast) { this._drawLast.next = order; }
- this._drawLast = order;
- this._drawFirst = this._drawFirst || this._drawLast;
- },
- _addPath: function (layer) {
- this._requestRedraw(layer);
- },
- _removePath: function (layer) {
- var order = layer._order;
- var next = order.next;
- var prev = order.prev;
- if (next) {
- next.prev = prev;
- } else {
- this._drawLast = prev;
- }
- if (prev) {
- prev.next = next;
- } else {
- this._drawFirst = next;
- }
- delete layer._order;
- delete this._layers[L.stamp(layer)];
- this._requestRedraw(layer);
- },
- _updatePath: function (layer) {
- // Redraw the union of the layer's old pixel
- // bounds and the new pixel bounds.
- this._extendRedrawBounds(layer);
- layer._project();
- layer._update();
- // The redraw will extend the redraw bounds
- // with the new pixel bounds.
- this._requestRedraw(layer);
- },
- _updateStyle: function (layer) {
- this._updateDashArray(layer);
- this._requestRedraw(layer);
- },
- _updateDashArray: function (layer) {
- if (layer.options.dashArray) {
- var parts = layer.options.dashArray.split(','),
- dashArray = [],
- i;
- for (i = 0; i < parts.length; i++) {
- dashArray.push(Number(parts[i]));
- }
- layer.options._dashArray = dashArray;
- }
- },
- _requestRedraw: function (layer) {
- if (!this._map) { return; }
- this._extendRedrawBounds(layer);
- this._redrawRequest = this._redrawRequest || L.Util.requestAnimFrame(this._redraw, this);
- },
- _extendRedrawBounds: function (layer) {
- var padding = (layer.options.weight || 0) + 1;
- this._redrawBounds = this._redrawBounds || new L.Bounds();
- this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding]));
- this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding]));
- },
- _redraw: function () {
- this._redrawRequest = null;
- this._clear(); // clear layers in redraw bounds
- this._draw(); // draw layers
- this._redrawBounds = null;
- },
- _clear: function () {
- var bounds = this._redrawBounds;
- if (bounds) {
- var size = bounds.getSize();
- this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y);
- } else {
- this._ctx.clearRect(0, 0, this._container.width, this._container.height);
- }
- },
- _draw: function () {
- var layer, bounds = this._redrawBounds;
- this._ctx.save();
- if (bounds) {
- var size = bounds.getSize();
- this._ctx.beginPath();
- this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y);
- this._ctx.clip();
- }
- this._drawing = true;
- for (var order = this._drawFirst; order; order = order.next) {
- layer = order.layer;
- if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) {
- layer._updatePath();
- }
- }
- this._drawing = false;
- this._ctx.restore(); // Restore state before clipping.
- },
- _updatePoly: function (layer, closed) {
- if (!this._drawing) { return; }
- var i, j, len2, p,
- parts = layer._parts,
- len = parts.length,
- ctx = this._ctx;
- if (!len) { return; }
- this._drawnLayers[layer._leaflet_id] = layer;
- ctx.beginPath();
- if (ctx.setLineDash) {
- ctx.setLineDash(layer.options && layer.options._dashArray || []);
- }
- for (i = 0; i < len; i++) {
- for (j = 0, len2 = parts[i].length; j < len2; j++) {
- p = parts[i][j];
- ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
- }
- if (closed) {
- ctx.closePath();
- }
- }
- this._fillStroke(ctx, layer);
- // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
- },
- _updateCircle: function (layer) {
- if (!this._drawing || layer._empty()) { return; }
- var p = layer._point,
- ctx = this._ctx,
- r = layer._radius,
- s = (layer._radiusY || r) / r;
- this._drawnLayers[layer._leaflet_id] = layer;
- if (s !== 1) {
- ctx.save();
- ctx.scale(1, s);
- }
- ctx.beginPath();
- ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);
- if (s !== 1) {
- ctx.restore();
- }
- this._fillStroke(ctx, layer);
- },
- _fillStroke: function (ctx, layer) {
- var options = layer.options;
- if (options.fill) {
- ctx.globalAlpha = options.fillOpacity;
- ctx.fillStyle = options.fillColor || options.color;
- ctx.fill(options.fillRule || 'evenodd');
- }
- if (options.stroke && options.weight !== 0) {
- ctx.globalAlpha = options.opacity;
- ctx.lineWidth = options.weight;
- ctx.strokeStyle = options.color;
- ctx.lineCap = options.lineCap;
- ctx.lineJoin = options.lineJoin;
- ctx.stroke();
- }
- },
- // Canvas obviously doesn't have mouse events for individual drawn objects,
- // so we emulate that by calculating what's under the mouse on mousemove/click manually
- _onClick: function (e) {
- var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer;
- for (var order = this._drawFirst; order; order = order.next) {
- layer = order.layer;
- if (layer.options.interactive && layer._containsPoint(point) && !this._map._draggableMoved(layer)) {
- clickedLayer = layer;
- }
- }
- if (clickedLayer) {
- L.DomEvent._fakeStop(e);
- this._fireEvent([clickedLayer], e);
- }
- },
- _onMouseMove: function (e) {
- if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; }
- var point = this._map.mouseEventToLayerPoint(e);
- this._handleMouseHover(e, point);
- },
- _handleMouseOut: function (e) {
- var layer = this._hoveredLayer;
- if (layer) {
- // if we're leaving the layer, fire mouseout
- L.DomUtil.removeClass(this._container, 'leaflet-interactive');
- this._fireEvent([layer], e, 'mouseout');
- this._hoveredLayer = null;
- }
- },
- _handleMouseHover: function (e, point) {
- var layer, candidateHoveredLayer;
- for (var order = this._drawFirst; order; order = order.next) {
- layer = order.layer;
- if (layer.options.interactive && layer._containsPoint(point)) {
- candidateHoveredLayer = layer;
- }
- }
- if (candidateHoveredLayer !== this._hoveredLayer) {
- this._handleMouseOut(e);
- if (candidateHoveredLayer) {
- L.DomUtil.addClass(this._container, 'leaflet-interactive'); // change cursor
- this._fireEvent([candidateHoveredLayer], e, 'mouseover');
- this._hoveredLayer = candidateHoveredLayer;
- }
- }
- if (this._hoveredLayer) {
- this._fireEvent([this._hoveredLayer], e);
- }
- },
- _fireEvent: function (layers, e, type) {
- this._map._fireDOMEvent(e, type || e.type, layers);
- },
- _bringToFront: function (layer) {
- var order = layer._order;
- var next = order.next;
- var prev = order.prev;
- if (next) {
- next.prev = prev;
- } else {
- // Already last
- return;
- }
- if (prev) {
- prev.next = next;
- } else if (next) {
- // Update first entry unless this is the
- // signle entry
- this._drawFirst = next;
- }
- order.prev = this._drawLast;
- this._drawLast.next = order;
- order.next = null;
- this._drawLast = order;
- this._requestRedraw(layer);
- },
- _bringToBack: function (layer) {
- var order = layer._order;
- var next = order.next;
- var prev = order.prev;
- if (prev) {
- prev.next = next;
- } else {
- // Already first
- return;
- }
- if (next) {
- next.prev = prev;
- } else if (prev) {
- // Update last entry unless this is the
- // signle entry
- this._drawLast = prev;
- }
- order.prev = null;
- order.next = this._drawFirst;
- this._drawFirst.prev = order;
- this._drawFirst = order;
- this._requestRedraw(layer);
- }
- });
- // @namespace Browser; @property canvas: Boolean
- // `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
- L.Browser.canvas = (function () {
- return !!document.createElement('canvas').getContext;
- }());
- // @namespace Canvas
- // @factory L.canvas(options?: Renderer options)
- // Creates a Canvas renderer with the given options.
- L.canvas = function (options) {
- return L.Browser.canvas ? new L.Canvas(options) : null;
- };
- L.Polyline.prototype._containsPoint = function (p, closed) {
- var i, j, k, len, len2, part,
- w = this._clickTolerance();
- if (!this._pxBounds.contains(p)) { return false; }
- // hit detection for polylines
- for (i = 0, len = this._parts.length; i < len; i++) {
- part = this._parts[i];
- for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
- if (!closed && (j === 0)) { continue; }
- if (L.LineUtil.pointToSegmentDistance(p, part[k], part[j]) <= w) {
- return true;
- }
- }
- }
- return false;
- };
- L.Polygon.prototype._containsPoint = function (p) {
- var inside = false,
- part, p1, p2, i, j, k, len, len2;
- if (!this._pxBounds.contains(p)) { return false; }
- // ray casting algorithm for detecting if point is in polygon
- for (i = 0, len = this._parts.length; i < len; i++) {
- part = this._parts[i];
- for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
- p1 = part[j];
- p2 = part[k];
- if (((p1.y > p.y) !== (p2.y > p.y)) && (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) {
- inside = !inside;
- }
- }
- }
- // also check if it's on polygon stroke
- return inside || L.Polyline.prototype._containsPoint.call(this, p, true);
- };
- L.CircleMarker.prototype._containsPoint = function (p) {
- return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
- };
- /*
- * @class GeoJSON
- * @aka L.GeoJSON
- * @inherits FeatureGroup
- *
- * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse
- * GeoJSON data and display it on the map. Extends `FeatureGroup`.
- *
- * @example
- *
- * ```js
- * L.geoJSON(data, {
- * style: function (feature) {
- * return {color: feature.properties.color};
- * }
- * }).bindPopup(function (layer) {
- * return layer.feature.properties.description;
- * }).addTo(map);
- * ```
- */
- L.GeoJSON = L.FeatureGroup.extend({
- /* @section
- * @aka GeoJSON options
- *
- * @option pointToLayer: Function = *
- * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally
- * called when data is added, passing the GeoJSON point feature and its `LatLng`.
- * The default is to spawn a default `Marker`:
- * ```js
- * function(geoJsonPoint, latlng) {
- * return L.marker(latlng);
- * }
- * ```
- *
- * @option style: Function = *
- * A `Function` defining the `Path options` for styling GeoJSON lines and polygons,
- * called internally when data is added.
- * The default value is to not override any defaults:
- * ```js
- * function (geoJsonFeature) {
- * return {}
- * }
- * ```
- *
- * @option onEachFeature: Function = *
- * A `Function` that will be called once for each created `Feature`, after it has
- * been created and styled. Useful for attaching events and popups to features.
- * The default is to do nothing with the newly created layers:
- * ```js
- * function (feature, layer) {}
- * ```
- *
- * @option filter: Function = *
- * A `Function` that will be used to decide whether to include a feature or not.
- * The default is to include all features:
- * ```js
- * function (geoJsonFeature) {
- * return true;
- * }
- * ```
- * Note: dynamically changing the `filter` option will have effect only on newly
- * added data. It will _not_ re-evaluate already included features.
- *
- * @option coordsToLatLng: Function = *
- * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s.
- * The default is the `coordsToLatLng` static method.
- */
- initialize: function (geojson, options) {
- L.setOptions(this, options);
- this._layers = {};
- if (geojson) {
- this.addData(geojson);
- }
- },
- // @method addData( <GeoJSON> data ): this
- // Adds a GeoJSON object to the layer.
- addData: function (geojson) {
- var features = L.Util.isArray(geojson) ? geojson : geojson.features,
- i, len, feature;
- if (features) {
- for (i = 0, len = features.length; i < len; i++) {
- // only add this if geometry or geometries are set and not null
- feature = features[i];
- if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
- this.addData(feature);
- }
- }
- return this;
- }
- var options = this.options;
- if (options.filter && !options.filter(geojson)) { return this; }
- var layer = L.GeoJSON.geometryToLayer(geojson, options);
- if (!layer) {
- return this;
- }
- layer.feature = L.GeoJSON.asFeature(geojson);
- layer.defaultOptions = layer.options;
- this.resetStyle(layer);
- if (options.onEachFeature) {
- options.onEachFeature(geojson, layer);
- }
- return this.addLayer(layer);
- },
- // @method resetStyle( <Path> layer ): this
- // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
- resetStyle: function (layer) {
- // reset any custom styles
- layer.options = L.Util.extend({}, layer.defaultOptions);
- this._setLayerStyle(layer, this.options.style);
- return this;
- },
- // @method setStyle( <Function> style ): this
- // Changes styles of GeoJSON vector layers with the given style function.
- setStyle: function (style) {
- return this.eachLayer(function (layer) {
- this._setLayerStyle(layer, style);
- }, this);
- },
- _setLayerStyle: function (layer, style) {
- if (typeof style === 'function') {
- style = style(layer.feature);
- }
- if (layer.setStyle) {
- layer.setStyle(style);
- }
- }
- });
- // @section
- // There are several static functions which can be called without instantiating L.GeoJSON:
- L.extend(L.GeoJSON, {
- // @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer
- // Creates a `Layer` from a given GeoJSON feature. Can use a custom
- // [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng)
- // functions if provided as options.
- geometryToLayer: function (geojson, options) {
- var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
- coords = geometry ? geometry.coordinates : null,
- layers = [],
- pointToLayer = options && options.pointToLayer,
- coordsToLatLng = options && options.coordsToLatLng || this.coordsToLatLng,
- latlng, latlngs, i, len;
- if (!coords && !geometry) {
- return null;
- }
- switch (geometry.type) {
- case 'Point':
- latlng = coordsToLatLng(coords);
- return pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng);
- case 'MultiPoint':
- for (i = 0, len = coords.length; i < len; i++) {
- latlng = coordsToLatLng(coords[i]);
- layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng));
- }
- return new L.FeatureGroup(layers);
- case 'LineString':
- case 'MultiLineString':
- latlngs = this.coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, coordsToLatLng);
- return new L.Polyline(latlngs, options);
- case 'Polygon':
- case 'MultiPolygon':
- latlngs = this.coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, coordsToLatLng);
- return new L.Polygon(latlngs, options);
- case 'GeometryCollection':
- for (i = 0, len = geometry.geometries.length; i < len; i++) {
- var layer = this.geometryToLayer({
- geometry: geometry.geometries[i],
- type: 'Feature',
- properties: geojson.properties
- }, options);
- if (layer) {
- layers.push(layer);
- }
- }
- return new L.FeatureGroup(layers);
- default:
- throw new Error('Invalid GeoJSON object.');
- }
- },
- // @function coordsToLatLng(coords: Array): LatLng
- // Creates a `LatLng` object from an array of 2 numbers (longitude, latitude)
- // or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points.
- coordsToLatLng: function (coords) {
- return new L.LatLng(coords[1], coords[0], coords[2]);
- },
- // @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array
- // Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array.
- // `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default).
- // Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function.
- coordsToLatLngs: function (coords, levelsDeep, coordsToLatLng) {
- var latlngs = [];
- for (var i = 0, len = coords.length, latlng; i < len; i++) {
- latlng = levelsDeep ?
- this.coordsToLatLngs(coords[i], levelsDeep - 1, coordsToLatLng) :
- (coordsToLatLng || this.coordsToLatLng)(coords[i]);
- latlngs.push(latlng);
- }
- return latlngs;
- },
- // @function latLngToCoords(latlng: LatLng): Array
- // Reverse of [`coordsToLatLng`](#geojson-coordstolatlng)
- latLngToCoords: function (latlng) {
- return latlng.alt !== undefined ?
- [latlng.lng, latlng.lat, latlng.alt] :
- [latlng.lng, latlng.lat];
- },
- // @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean): Array
- // Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs)
- // `closed` determines whether the first point should be appended to the end of the array to close the feature, only used when `levelsDeep` is 0. False by default.
- latLngsToCoords: function (latlngs, levelsDeep, closed) {
- var coords = [];
- for (var i = 0, len = latlngs.length; i < len; i++) {
- coords.push(levelsDeep ?
- L.GeoJSON.latLngsToCoords(latlngs[i], levelsDeep - 1, closed) :
- L.GeoJSON.latLngToCoords(latlngs[i]));
- }
- if (!levelsDeep && closed) {
- coords.push(coords[0]);
- }
- return coords;
- },
- getFeature: function (layer, newGeometry) {
- return layer.feature ?
- L.extend({}, layer.feature, {geometry: newGeometry}) :
- L.GeoJSON.asFeature(newGeometry);
- },
- // @function asFeature(geojson: Object): Object
- // Normalize GeoJSON geometries/features into GeoJSON features.
- asFeature: function (geojson) {
- if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') {
- return geojson;
- }
- return {
- type: 'Feature',
- properties: {},
- geometry: geojson
- };
- }
- });
- var PointToGeoJSON = {
- toGeoJSON: function () {
- return L.GeoJSON.getFeature(this, {
- type: 'Point',
- coordinates: L.GeoJSON.latLngToCoords(this.getLatLng())
- });
- }
- };
- // @namespace Marker
- // @method toGeoJSON(): Object
- // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature).
- L.Marker.include(PointToGeoJSON);
- // @namespace CircleMarker
- // @method toGeoJSON(): Object
- // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature).
- L.Circle.include(PointToGeoJSON);
- L.CircleMarker.include(PointToGeoJSON);
- // @namespace Polyline
- // @method toGeoJSON(): Object
- // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
- L.Polyline.prototype.toGeoJSON = function () {
- var multi = !L.Polyline._flat(this._latlngs);
- var coords = L.GeoJSON.latLngsToCoords(this._latlngs, multi ? 1 : 0);
- return L.GeoJSON.getFeature(this, {
- type: (multi ? 'Multi' : '') + 'LineString',
- coordinates: coords
- });
- };
- // @namespace Polygon
- // @method toGeoJSON(): Object
- // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
- L.Polygon.prototype.toGeoJSON = function () {
- var holes = !L.Polyline._flat(this._latlngs),
- multi = holes && !L.Polyline._flat(this._latlngs[0]);
- var coords = L.GeoJSON.latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true);
- if (!holes) {
- coords = [coords];
- }
- return L.GeoJSON.getFeature(this, {
- type: (multi ? 'Multi' : '') + 'Polygon',
- coordinates: coords
- });
- };
- // @namespace LayerGroup
- L.LayerGroup.include({
- toMultiPoint: function () {
- var coords = [];
- this.eachLayer(function (layer) {
- coords.push(layer.toGeoJSON().geometry.coordinates);
- });
- return L.GeoJSON.getFeature(this, {
- type: 'MultiPoint',
- coordinates: coords
- });
- },
- // @method toGeoJSON(): Object
- // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `GeometryCollection`).
- toGeoJSON: function () {
- var type = this.feature && this.feature.geometry && this.feature.geometry.type;
- if (type === 'MultiPoint') {
- return this.toMultiPoint();
- }
- var isGeometryCollection = type === 'GeometryCollection',
- jsons = [];
- this.eachLayer(function (layer) {
- if (layer.toGeoJSON) {
- var json = layer.toGeoJSON();
- jsons.push(isGeometryCollection ? json.geometry : L.GeoJSON.asFeature(json));
- }
- });
- if (isGeometryCollection) {
- return L.GeoJSON.getFeature(this, {
- geometries: jsons,
- type: 'GeometryCollection'
- });
- }
- return {
- type: 'FeatureCollection',
- features: jsons
- };
- }
- });
- // @namespace GeoJSON
- // @factory L.geoJSON(geojson?: Object, options?: GeoJSON options)
- // Creates a GeoJSON layer. Optionally accepts an object in
- // [GeoJSON format](http://geojson.org/geojson-spec.html) to display on the map
- // (you can alternatively add it later with `addData` method) and an `options` object.
- L.geoJSON = function (geojson, options) {
- return new L.GeoJSON(geojson, options);
- };
- // Backward compatibility.
- L.geoJson = L.geoJSON;
- /*
- * @class Draggable
- * @aka L.Draggable
- * @inherits Evented
- *
- * A class for making DOM elements draggable (including touch support).
- * Used internally for map and marker dragging. Only works for elements
- * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
- *
- * @example
- * ```js
- * var draggable = new L.Draggable(elementToDrag);
- * draggable.enable();
- * ```
- */
- L.Draggable = L.Evented.extend({
- options: {
- // @option clickTolerance: Number = 3
- // The max number of pixels a user can shift the mouse pointer during a click
- // for it to be considered a valid click (as opposed to a mouse drag).
- clickTolerance: 3
- },
- statics: {
- START: L.Browser.touch ? ['touchstart', 'mousedown'] : ['mousedown'],
- END: {
- mousedown: 'mouseup',
- touchstart: 'touchend',
- pointerdown: 'touchend',
- MSPointerDown: 'touchend'
- },
- MOVE: {
- mousedown: 'mousemove',
- touchstart: 'touchmove',
- pointerdown: 'touchmove',
- MSPointerDown: 'touchmove'
- }
- },
- // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline: Boolean)
- // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default).
- initialize: function (element, dragStartTarget, preventOutline) {
- this._element = element;
- this._dragStartTarget = dragStartTarget || element;
- this._preventOutline = preventOutline;
- },
- // @method enable()
- // Enables the dragging ability
- enable: function () {
- if (this._enabled) { return; }
- L.DomEvent.on(this._dragStartTarget, L.Draggable.START.join(' '), this._onDown, this);
- this._enabled = true;
- },
- // @method disable()
- // Disables the dragging ability
- disable: function () {
- if (!this._enabled) { return; }
- // If we're currently dragging this draggable,
- // disabling it counts as first ending the drag.
- if (L.Draggable._dragging === this) {
- this.finishDrag();
- }
- L.DomEvent.off(this._dragStartTarget, L.Draggable.START.join(' '), this._onDown, this);
- this._enabled = false;
- this._moved = false;
- },
- _onDown: function (e) {
- // Ignore simulated events, since we handle both touch and
- // mouse explicitly; otherwise we risk getting duplicates of
- // touch events, see #4315.
- // Also ignore the event if disabled; this happens in IE11
- // under some circumstances, see #3666.
- if (e._simulated || !this._enabled) { return; }
- this._moved = false;
- if (L.DomUtil.hasClass(this._element, 'leaflet-zoom-anim')) { return; }
- if (L.Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
- L.Draggable._dragging = this; // Prevent dragging multiple objects at once.
- if (this._preventOutline) {
- L.DomUtil.preventOutline(this._element);
- }
- L.DomUtil.disableImageDrag();
- L.DomUtil.disableTextSelection();
- if (this._moving) { return; }
- // @event down: Event
- // Fired when a drag is about to start.
- this.fire('down');
- var first = e.touches ? e.touches[0] : e;
- this._startPoint = new L.Point(first.clientX, first.clientY);
- L.DomEvent
- .on(document, L.Draggable.MOVE[e.type], this._onMove, this)
- .on(document, L.Draggable.END[e.type], this._onUp, this);
- },
- _onMove: function (e) {
- // Ignore simulated events, since we handle both touch and
- // mouse explicitly; otherwise we risk getting duplicates of
- // touch events, see #4315.
- // Also ignore the event if disabled; this happens in IE11
- // under some circumstances, see #3666.
- if (e._simulated || !this._enabled) { return; }
- if (e.touches && e.touches.length > 1) {
- this._moved = true;
- return;
- }
- var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
- newPoint = new L.Point(first.clientX, first.clientY),
- offset = newPoint.subtract(this._startPoint);
- if (!offset.x && !offset.y) { return; }
- if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; }
- L.DomEvent.preventDefault(e);
- if (!this._moved) {
- // @event dragstart: Event
- // Fired when a drag starts
- this.fire('dragstart');
- this._moved = true;
- this._startPos = L.DomUtil.getPosition(this._element).subtract(offset);
- L.DomUtil.addClass(document.body, 'leaflet-dragging');
- this._lastTarget = e.target || e.srcElement;
- // IE and Edge do not give the <use> element, so fetch it
- // if necessary
- if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) {
- this._lastTarget = this._lastTarget.correspondingUseElement;
- }
- L.DomUtil.addClass(this._lastTarget, 'leaflet-drag-target');
- }
- this._newPos = this._startPos.add(offset);
- this._moving = true;
- L.Util.cancelAnimFrame(this._animRequest);
- this._lastEvent = e;
- this._animRequest = L.Util.requestAnimFrame(this._updatePosition, this, true);
- },
- _updatePosition: function () {
- var e = {originalEvent: this._lastEvent};
- // @event predrag: Event
- // Fired continuously during dragging *before* each corresponding
- // update of the element's position.
- this.fire('predrag', e);
- L.DomUtil.setPosition(this._element, this._newPos);
- // @event drag: Event
- // Fired continuously during dragging.
- this.fire('drag', e);
- },
- _onUp: function (e) {
- // Ignore simulated events, since we handle both touch and
- // mouse explicitly; otherwise we risk getting duplicates of
- // touch events, see #4315.
- // Also ignore the event if disabled; this happens in IE11
- // under some circumstances, see #3666.
- if (e._simulated || !this._enabled) { return; }
- this.finishDrag();
- },
- finishDrag: function () {
- L.DomUtil.removeClass(document.body, 'leaflet-dragging');
- if (this._lastTarget) {
- L.DomUtil.removeClass(this._lastTarget, 'leaflet-drag-target');
- this._lastTarget = null;
- }
- for (var i in L.Draggable.MOVE) {
- L.DomEvent
- .off(document, L.Draggable.MOVE[i], this._onMove, this)
- .off(document, L.Draggable.END[i], this._onUp, this);
- }
- L.DomUtil.enableImageDrag();
- L.DomUtil.enableTextSelection();
- if (this._moved && this._moving) {
- // ensure drag is not fired after dragend
- L.Util.cancelAnimFrame(this._animRequest);
- // @event dragend: DragEndEvent
- // Fired when the drag ends.
- this.fire('dragend', {
- distance: this._newPos.distanceTo(this._startPos)
- });
- }
- this._moving = false;
- L.Draggable._dragging = false;
- }
- });
- /*
- L.Handler is a base class for handler classes that are used internally to inject
- interaction features like dragging to classes like Map and Marker.
- */
- // @class Handler
- // @aka L.Handler
- // Abstract class for map interaction handlers
- L.Handler = L.Class.extend({
- initialize: function (map) {
- this._map = map;
- },
- // @method enable(): this
- // Enables the handler
- enable: function () {
- if (this._enabled) { return this; }
- this._enabled = true;
- this.addHooks();
- return this;
- },
- // @method disable(): this
- // Disables the handler
- disable: function () {
- if (!this._enabled) { return this; }
- this._enabled = false;
- this.removeHooks();
- return this;
- },
- // @method enabled(): Boolean
- // Returns `true` if the handler is enabled
- enabled: function () {
- return !!this._enabled;
- }
- // @section Extension methods
- // Classes inheriting from `Handler` must implement the two following methods:
- // @method addHooks()
- // Called when the handler is enabled, should add event hooks.
- // @method removeHooks()
- // Called when the handler is disabled, should remove the event hooks added previously.
- });
- /*
- * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
- */
- // @namespace Map
- // @section Interaction Options
- L.Map.mergeOptions({
- // @option dragging: Boolean = true
- // Whether the map be draggable with mouse/touch or not.
- dragging: true,
- // @section Panning Inertia Options
- // @option inertia: Boolean = *
- // If enabled, panning of the map will have an inertia effect where
- // the map builds momentum while dragging and continues moving in
- // the same direction for some time. Feels especially nice on touch
- // devices. Enabled by default unless running on old Android devices.
- inertia: !L.Browser.android23,
- // @option inertiaDeceleration: Number = 3000
- // The rate with which the inertial movement slows down, in pixels/second².
- inertiaDeceleration: 3400, // px/s^2
- // @option inertiaMaxSpeed: Number = Infinity
- // Max speed of the inertial movement, in pixels/second.
- inertiaMaxSpeed: Infinity, // px/s
- // @option easeLinearity: Number = 0.2
- easeLinearity: 0.2,
- // TODO refactor, move to CRS
- // @option worldCopyJump: Boolean = false
- // With this option enabled, the map tracks when you pan to another "copy"
- // of the world and seamlessly jumps to the original one so that all overlays
- // like markers and vector layers are still visible.
- worldCopyJump: false,
- // @option maxBoundsViscosity: Number = 0.0
- // If `maxBounds` is set, this option will control how solid the bounds
- // are when dragging the map around. The default value of `0.0` allows the
- // user to drag outside the bounds at normal speed, higher values will
- // slow down map dragging outside bounds, and `1.0` makes the bounds fully
- // solid, preventing the user from dragging outside the bounds.
- maxBoundsViscosity: 0.0
- });
- L.Map.Drag = L.Handler.extend({
- addHooks: function () {
- if (!this._draggable) {
- var map = this._map;
- this._draggable = new L.Draggable(map._mapPane, map._container);
- this._draggable.on({
- down: this._onDown,
- dragstart: this._onDragStart,
- drag: this._onDrag,
- dragend: this._onDragEnd
- }, this);
- this._draggable.on('predrag', this._onPreDragLimit, this);
- if (map.options.worldCopyJump) {
- this._draggable.on('predrag', this._onPreDragWrap, this);
- map.on('zoomend', this._onZoomEnd, this);
- map.whenReady(this._onZoomEnd, this);
- }
- }
- L.DomUtil.addClass(this._map._container, 'leaflet-grab leaflet-touch-drag');
- this._draggable.enable();
- this._positions = [];
- this._times = [];
- },
- removeHooks: function () {
- L.DomUtil.removeClass(this._map._container, 'leaflet-grab');
- L.DomUtil.removeClass(this._map._container, 'leaflet-touch-drag');
- this._draggable.disable();
- },
- moved: function () {
- return this._draggable && this._draggable._moved;
- },
- moving: function () {
- return this._draggable && this._draggable._moving;
- },
- _onDown: function () {
- this._map._stop();
- },
- _onDragStart: function () {
- var map = this._map;
- if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
- var bounds = L.latLngBounds(this._map.options.maxBounds);
- this._offsetLimit = L.bounds(
- this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1),
- this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1)
- .add(this._map.getSize()));
- this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity));
- } else {
- this._offsetLimit = null;
- }
- map
- .fire('movestart')
- .fire('dragstart');
- if (map.options.inertia) {
- this._positions = [];
- this._times = [];
- }
- },
- _onDrag: function (e) {
- if (this._map.options.inertia) {
- var time = this._lastTime = +new Date(),
- pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
- this._positions.push(pos);
- this._times.push(time);
- if (time - this._times[0] > 50) {
- this._positions.shift();
- this._times.shift();
- }
- }
- this._map
- .fire('move', e)
- .fire('drag', e);
- },
- _onZoomEnd: function () {
- var pxCenter = this._map.getSize().divideBy(2),
- pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
- this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
- this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
- },
- _viscousLimit: function (value, threshold) {
- return value - (value - threshold) * this._viscosity;
- },
- _onPreDragLimit: function () {
- if (!this._viscosity || !this._offsetLimit) { return; }
- var offset = this._draggable._newPos.subtract(this._draggable._startPos);
- var limit = this._offsetLimit;
- if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); }
- if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); }
- if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); }
- if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); }
- this._draggable._newPos = this._draggable._startPos.add(offset);
- },
- _onPreDragWrap: function () {
- // TODO refactor to be able to adjust map pane position after zoom
- var worldWidth = this._worldWidth,
- halfWidth = Math.round(worldWidth / 2),
- dx = this._initialWorldOffset,
- x = this._draggable._newPos.x,
- newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
- newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
- newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
- this._draggable._absPos = this._draggable._newPos.clone();
- this._draggable._newPos.x = newX;
- },
- _onDragEnd: function (e) {
- var map = this._map,
- options = map.options,
- noInertia = !options.inertia || this._times.length < 2;
- map.fire('dragend', e);
- if (noInertia) {
- map.fire('moveend');
- } else {
- var direction = this._lastPos.subtract(this._positions[0]),
- duration = (this._lastTime - this._times[0]) / 1000,
- ease = options.easeLinearity,
- speedVector = direction.multiplyBy(ease / duration),
- speed = speedVector.distanceTo([0, 0]),
- limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
- limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
- decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
- offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
- if (!offset.x && !offset.y) {
- map.fire('moveend');
- } else {
- offset = map._limitOffset(offset, map.options.maxBounds);
- L.Util.requestAnimFrame(function () {
- map.panBy(offset, {
- duration: decelerationDuration,
- easeLinearity: ease,
- noMoveStart: true,
- animate: true
- });
- });
- }
- }
- }
- });
- // @section Handlers
- // @property dragging: Handler
- // Map dragging handler (by both mouse and touch).
- L.Map.addInitHook('addHandler', 'dragging', L.Map.Drag);
- /*
- * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
- */
- // @namespace Map
- // @section Interaction Options
- L.Map.mergeOptions({
- // @option doubleClickZoom: Boolean|String = true
- // Whether the map can be zoomed in by double clicking on it and
- // zoomed out by double clicking while holding shift. If passed
- // `'center'`, double-click zoom will zoom to the center of the
- // view regardless of where the mouse was.
- doubleClickZoom: true
- });
- L.Map.DoubleClickZoom = L.Handler.extend({
- addHooks: function () {
- this._map.on('dblclick', this._onDoubleClick, this);
- },
- removeHooks: function () {
- this._map.off('dblclick', this._onDoubleClick, this);
- },
- _onDoubleClick: function (e) {
- var map = this._map,
- oldZoom = map.getZoom(),
- delta = map.options.zoomDelta,
- zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta;
- if (map.options.doubleClickZoom === 'center') {
- map.setZoom(zoom);
- } else {
- map.setZoomAround(e.containerPoint, zoom);
- }
- }
- });
- // @section Handlers
- //
- // Map properties include interaction handlers that allow you to control
- // interaction behavior in runtime, enabling or disabling certain features such
- // as dragging or touch zoom (see `Handler` methods). For example:
- //
- // ```js
- // map.doubleClickZoom.disable();
- // ```
- //
- // @property doubleClickZoom: Handler
- // Double click zoom handler.
- L.Map.addInitHook('addHandler', 'doubleClickZoom', L.Map.DoubleClickZoom);
- /*
- * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
- */
- // @namespace Map
- // @section Interaction Options
- L.Map.mergeOptions({
- // @section Mousewheel options
- // @option scrollWheelZoom: Boolean|String = true
- // Whether the map can be zoomed by using the mouse wheel. If passed `'center'`,
- // it will zoom to the center of the view regardless of where the mouse was.
- scrollWheelZoom: true,
- // @option wheelDebounceTime: Number = 40
- // Limits the rate at which a wheel can fire (in milliseconds). By default
- // user can't zoom via wheel more often than once per 40 ms.
- wheelDebounceTime: 40,
- // @option wheelPxPerZoomLevel: Number = 60
- // How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta))
- // mean a change of one full zoom level. Smaller values will make wheel-zooming
- // faster (and vice versa).
- wheelPxPerZoomLevel: 60
- });
- L.Map.ScrollWheelZoom = L.Handler.extend({
- addHooks: function () {
- L.DomEvent.on(this._map._container, 'mousewheel', this._onWheelScroll, this);
- this._delta = 0;
- },
- removeHooks: function () {
- L.DomEvent.off(this._map._container, 'mousewheel', this._onWheelScroll, this);
- },
- _onWheelScroll: function (e) {
- var delta = L.DomEvent.getWheelDelta(e);
- var debounce = this._map.options.wheelDebounceTime;
- this._delta += delta;
- this._lastMousePos = this._map.mouseEventToContainerPoint(e);
- if (!this._startTime) {
- this._startTime = +new Date();
- }
- var left = Math.max(debounce - (+new Date() - this._startTime), 0);
- clearTimeout(this._timer);
- this._timer = setTimeout(L.bind(this._performZoom, this), left);
- L.DomEvent.stop(e);
- },
- _performZoom: function () {
- var map = this._map,
- zoom = map.getZoom(),
- snap = this._map.options.zoomSnap || 0;
- map._stop(); // stop panning and fly animations if any
- // map the delta with a sigmoid function to -4..4 range leaning on -1..1
- var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4),
- d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
- d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
- delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom;
- this._delta = 0;
- this._startTime = null;
- if (!delta) { return; }
- if (map.options.scrollWheelZoom === 'center') {
- map.setZoom(zoom + delta);
- } else {
- map.setZoomAround(this._lastMousePos, zoom + delta);
- }
- }
- });
- // @section Handlers
- // @property scrollWheelZoom: Handler
- // Scroll wheel zoom handler.
- L.Map.addInitHook('addHandler', 'scrollWheelZoom', L.Map.ScrollWheelZoom);
- /*
- * Extends the event handling code with double tap support for mobile browsers.
- */
- L.extend(L.DomEvent, {
- _touchstart: L.Browser.msPointer ? 'MSPointerDown' : L.Browser.pointer ? 'pointerdown' : 'touchstart',
- _touchend: L.Browser.msPointer ? 'MSPointerUp' : L.Browser.pointer ? 'pointerup' : 'touchend',
- // inspired by Zepto touch code by Thomas Fuchs
- addDoubleTapListener: function (obj, handler, id) {
- var last, touch,
- doubleTap = false,
- delay = 250;
- function onTouchStart(e) {
- var count;
- if (L.Browser.pointer) {
- count = L.DomEvent._pointersCount;
- } else {
- count = e.touches.length;
- }
- if (count > 1) { return; }
- var now = Date.now(),
- delta = now - (last || now);
- touch = e.touches ? e.touches[0] : e;
- doubleTap = (delta > 0 && delta <= delay);
- last = now;
- }
- function onTouchEnd() {
- if (doubleTap && !touch.cancelBubble) {
- if (L.Browser.pointer) {
- // work around .type being readonly with MSPointer* events
- var newTouch = {},
- prop, i;
- for (i in touch) {
- prop = touch[i];
- newTouch[i] = prop && prop.bind ? prop.bind(touch) : prop;
- }
- touch = newTouch;
- }
- touch.type = 'dblclick';
- handler(touch);
- last = null;
- }
- }
- var pre = '_leaflet_',
- touchstart = this._touchstart,
- touchend = this._touchend;
- obj[pre + touchstart + id] = onTouchStart;
- obj[pre + touchend + id] = onTouchEnd;
- obj[pre + 'dblclick' + id] = handler;
- obj.addEventListener(touchstart, onTouchStart, false);
- obj.addEventListener(touchend, onTouchEnd, false);
- // On some platforms (notably, chrome on win10 + touchscreen + mouse),
- // the browser doesn't fire touchend/pointerup events but does fire
- // native dblclicks. See #4127.
- if (!L.Browser.edge) {
- obj.addEventListener('dblclick', handler, false);
- }
- return this;
- },
- removeDoubleTapListener: function (obj, id) {
- var pre = '_leaflet_',
- touchstart = obj[pre + this._touchstart + id],
- touchend = obj[pre + this._touchend + id],
- dblclick = obj[pre + 'dblclick' + id];
- obj.removeEventListener(this._touchstart, touchstart, false);
- obj.removeEventListener(this._touchend, touchend, false);
- if (!L.Browser.edge) {
- obj.removeEventListener('dblclick', dblclick, false);
- }
- return this;
- }
- });
- /*
- * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
- */
- L.extend(L.DomEvent, {
- POINTER_DOWN: L.Browser.msPointer ? 'MSPointerDown' : 'pointerdown',
- POINTER_MOVE: L.Browser.msPointer ? 'MSPointerMove' : 'pointermove',
- POINTER_UP: L.Browser.msPointer ? 'MSPointerUp' : 'pointerup',
- POINTER_CANCEL: L.Browser.msPointer ? 'MSPointerCancel' : 'pointercancel',
- TAG_WHITE_LIST: ['INPUT', 'SELECT', 'OPTION'],
- _pointers: {},
- _pointersCount: 0,
- // Provides a touch events wrapper for (ms)pointer events.
- // ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
- addPointerListener: function (obj, type, handler, id) {
- if (type === 'touchstart') {
- this._addPointerStart(obj, handler, id);
- } else if (type === 'touchmove') {
- this._addPointerMove(obj, handler, id);
- } else if (type === 'touchend') {
- this._addPointerEnd(obj, handler, id);
- }
- return this;
- },
- removePointerListener: function (obj, type, id) {
- var handler = obj['_leaflet_' + type + id];
- if (type === 'touchstart') {
- obj.removeEventListener(this.POINTER_DOWN, handler, false);
- } else if (type === 'touchmove') {
- obj.removeEventListener(this.POINTER_MOVE, handler, false);
- } else if (type === 'touchend') {
- obj.removeEventListener(this.POINTER_UP, handler, false);
- obj.removeEventListener(this.POINTER_CANCEL, handler, false);
- }
- return this;
- },
- _addPointerStart: function (obj, handler, id) {
- var onDown = L.bind(function (e) {
- if (e.pointerType !== 'mouse' && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) {
- // In IE11, some touch events needs to fire for form controls, or
- // the controls will stop working. We keep a whitelist of tag names that
- // need these events. For other target tags, we prevent default on the event.
- if (this.TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) {
- L.DomEvent.preventDefault(e);
- } else {
- return;
- }
- }
- this._handlePointer(e, handler);
- }, this);
- obj['_leaflet_touchstart' + id] = onDown;
- obj.addEventListener(this.POINTER_DOWN, onDown, false);
- // need to keep track of what pointers and how many are active to provide e.touches emulation
- if (!this._pointerDocListener) {
- var pointerUp = L.bind(this._globalPointerUp, this);
- // we listen documentElement as any drags that end by moving the touch off the screen get fired there
- document.documentElement.addEventListener(this.POINTER_DOWN, L.bind(this._globalPointerDown, this), true);
- document.documentElement.addEventListener(this.POINTER_MOVE, L.bind(this._globalPointerMove, this), true);
- document.documentElement.addEventListener(this.POINTER_UP, pointerUp, true);
- document.documentElement.addEventListener(this.POINTER_CANCEL, pointerUp, true);
- this._pointerDocListener = true;
- }
- },
- _globalPointerDown: function (e) {
- this._pointers[e.pointerId] = e;
- this._pointersCount++;
- },
- _globalPointerMove: function (e) {
- if (this._pointers[e.pointerId]) {
- this._pointers[e.pointerId] = e;
- }
- },
- _globalPointerUp: function (e) {
- delete this._pointers[e.pointerId];
- this._pointersCount--;
- },
- _handlePointer: function (e, handler) {
- e.touches = [];
- for (var i in this._pointers) {
- e.touches.push(this._pointers[i]);
- }
- e.changedTouches = [e];
- handler(e);
- },
- _addPointerMove: function (obj, handler, id) {
- var onMove = L.bind(function (e) {
- // don't fire touch moves when mouse isn't down
- if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; }
- this._handlePointer(e, handler);
- }, this);
- obj['_leaflet_touchmove' + id] = onMove;
- obj.addEventListener(this.POINTER_MOVE, onMove, false);
- },
- _addPointerEnd: function (obj, handler, id) {
- var onUp = L.bind(function (e) {
- this._handlePointer(e, handler);
- }, this);
- obj['_leaflet_touchend' + id] = onUp;
- obj.addEventListener(this.POINTER_UP, onUp, false);
- obj.addEventListener(this.POINTER_CANCEL, onUp, false);
- }
- });
- /*
- * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
- */
- // @namespace Map
- // @section Interaction Options
- L.Map.mergeOptions({
- // @section Touch interaction options
- // @option touchZoom: Boolean|String = *
- // Whether the map can be zoomed by touch-dragging with two fingers. If
- // passed `'center'`, it will zoom to the center of the view regardless of
- // where the touch events (fingers) were. Enabled for touch-capable web
- // browsers except for old Androids.
- touchZoom: L.Browser.touch && !L.Browser.android23,
- // @option bounceAtZoomLimits: Boolean = true
- // Set it to false if you don't want the map to zoom beyond min/max zoom
- // and then bounce back when pinch-zooming.
- bounceAtZoomLimits: true
- });
- L.Map.TouchZoom = L.Handler.extend({
- addHooks: function () {
- L.DomUtil.addClass(this._map._container, 'leaflet-touch-zoom');
- L.DomEvent.on(this._map._container, 'touchstart', this._onTouchStart, this);
- },
- removeHooks: function () {
- L.DomUtil.removeClass(this._map._container, 'leaflet-touch-zoom');
- L.DomEvent.off(this._map._container, 'touchstart', this._onTouchStart, this);
- },
- _onTouchStart: function (e) {
- var map = this._map;
- if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
- var p1 = map.mouseEventToContainerPoint(e.touches[0]),
- p2 = map.mouseEventToContainerPoint(e.touches[1]);
- this._centerPoint = map.getSize()._divideBy(2);
- this._startLatLng = map.containerPointToLatLng(this._centerPoint);
- if (map.options.touchZoom !== 'center') {
- this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
- }
- this._startDist = p1.distanceTo(p2);
- this._startZoom = map.getZoom();
- this._moved = false;
- this._zooming = true;
- map._stop();
- L.DomEvent
- .on(document, 'touchmove', this._onTouchMove, this)
- .on(document, 'touchend', this._onTouchEnd, this);
- L.DomEvent.preventDefault(e);
- },
- _onTouchMove: function (e) {
- if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
- var map = this._map,
- p1 = map.mouseEventToContainerPoint(e.touches[0]),
- p2 = map.mouseEventToContainerPoint(e.touches[1]),
- scale = p1.distanceTo(p2) / this._startDist;
- this._zoom = map.getScaleZoom(scale, this._startZoom);
- if (!map.options.bounceAtZoomLimits && (
- (this._zoom < map.getMinZoom() && scale < 1) ||
- (this._zoom > map.getMaxZoom() && scale > 1))) {
- this._zoom = map._limitZoom(this._zoom);
- }
- if (map.options.touchZoom === 'center') {
- this._center = this._startLatLng;
- if (scale === 1) { return; }
- } else {
- // Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
- var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
- if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
- this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom);
- }
- if (!this._moved) {
- map._moveStart(true);
- this._moved = true;
- }
- L.Util.cancelAnimFrame(this._animRequest);
- var moveFn = L.bind(map._move, map, this._center, this._zoom, {pinch: true, round: false});
- this._animRequest = L.Util.requestAnimFrame(moveFn, this, true);
- L.DomEvent.preventDefault(e);
- },
- _onTouchEnd: function () {
- if (!this._moved || !this._zooming) {
- this._zooming = false;
- return;
- }
- this._zooming = false;
- L.Util.cancelAnimFrame(this._animRequest);
- L.DomEvent
- .off(document, 'touchmove', this._onTouchMove)
- .off(document, 'touchend', this._onTouchEnd);
- // Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
- if (this._map.options.zoomAnimation) {
- this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
- } else {
- this._map._resetView(this._center, this._map._limitZoom(this._zoom));
- }
- }
- });
- // @section Handlers
- // @property touchZoom: Handler
- // Touch zoom handler.
- L.Map.addInitHook('addHandler', 'touchZoom', L.Map.TouchZoom);
- /*
- * L.Map.Tap is used to enable mobile hacks like quick taps and long hold.
- */
- // @namespace Map
- // @section Interaction Options
- L.Map.mergeOptions({
- // @section Touch interaction options
- // @option tap: Boolean = true
- // Enables mobile hacks for supporting instant taps (fixing 200ms click
- // delay on iOS/Android) and touch holds (fired as `contextmenu` events).
- tap: true,
- // @option tapTolerance: Number = 15
- // The max number of pixels a user can shift his finger during touch
- // for it to be considered a valid tap.
- tapTolerance: 15
- });
- L.Map.Tap = L.Handler.extend({
- addHooks: function () {
- L.DomEvent.on(this._map._container, 'touchstart', this._onDown, this);
- },
- removeHooks: function () {
- L.DomEvent.off(this._map._container, 'touchstart', this._onDown, this);
- },
- _onDown: function (e) {
- if (!e.touches) { return; }
- L.DomEvent.preventDefault(e);
- this._fireClick = true;
- // don't simulate click or track longpress if more than 1 touch
- if (e.touches.length > 1) {
- this._fireClick = false;
- clearTimeout(this._holdTimeout);
- return;
- }
- var first = e.touches[0],
- el = first.target;
- this._startPos = this._newPos = new L.Point(first.clientX, first.clientY);
- // if touching a link, highlight it
- if (el.tagName && el.tagName.toLowerCase() === 'a') {
- L.DomUtil.addClass(el, 'leaflet-active');
- }
- // simulate long hold but setting a timeout
- this._holdTimeout = setTimeout(L.bind(function () {
- if (this._isTapValid()) {
- this._fireClick = false;
- this._onUp();
- this._simulateEvent('contextmenu', first);
- }
- }, this), 1000);
- this._simulateEvent('mousedown', first);
- L.DomEvent.on(document, {
- touchmove: this._onMove,
- touchend: this._onUp
- }, this);
- },
- _onUp: function (e) {
- clearTimeout(this._holdTimeout);
- L.DomEvent.off(document, {
- touchmove: this._onMove,
- touchend: this._onUp
- }, this);
- if (this._fireClick && e && e.changedTouches) {
- var first = e.changedTouches[0],
- el = first.target;
- if (el && el.tagName && el.tagName.toLowerCase() === 'a') {
- L.DomUtil.removeClass(el, 'leaflet-active');
- }
- this._simulateEvent('mouseup', first);
- // simulate click if the touch didn't move too much
- if (this._isTapValid()) {
- this._simulateEvent('click', first);
- }
- }
- },
- _isTapValid: function () {
- return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
- },
- _onMove: function (e) {
- var first = e.touches[0];
- this._newPos = new L.Point(first.clientX, first.clientY);
- this._simulateEvent('mousemove', first);
- },
- _simulateEvent: function (type, e) {
- var simulatedEvent = document.createEvent('MouseEvents');
- simulatedEvent._simulated = true;
- e.target._simulatedClick = true;
- simulatedEvent.initMouseEvent(
- type, true, true, window, 1,
- e.screenX, e.screenY,
- e.clientX, e.clientY,
- false, false, false, false, 0, null);
- e.target.dispatchEvent(simulatedEvent);
- }
- });
- // @section Handlers
- // @property tap: Handler
- // Mobile touch hacks (quick tap and touch hold) handler.
- if (L.Browser.touch && !L.Browser.pointer) {
- L.Map.addInitHook('addHandler', 'tap', L.Map.Tap);
- }
- /*
- * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map
- * (zoom to a selected bounding box), enabled by default.
- */
- // @namespace Map
- // @section Interaction Options
- L.Map.mergeOptions({
- // @option boxZoom: Boolean = true
- // Whether the map can be zoomed to a rectangular area specified by
- // dragging the mouse while pressing the shift key.
- boxZoom: true
- });
- L.Map.BoxZoom = L.Handler.extend({
- initialize: function (map) {
- this._map = map;
- this._container = map._container;
- this._pane = map._panes.overlayPane;
- },
- addHooks: function () {
- L.DomEvent.on(this._container, 'mousedown', this._onMouseDown, this);
- },
- removeHooks: function () {
- L.DomEvent.off(this._container, 'mousedown', this._onMouseDown, this);
- },
- moved: function () {
- return this._moved;
- },
- _resetState: function () {
- this._moved = false;
- },
- _onMouseDown: function (e) {
- if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
- this._resetState();
- L.DomUtil.disableTextSelection();
- L.DomUtil.disableImageDrag();
- this._startPoint = this._map.mouseEventToContainerPoint(e);
- L.DomEvent.on(document, {
- contextmenu: L.DomEvent.stop,
- mousemove: this._onMouseMove,
- mouseup: this._onMouseUp,
- keydown: this._onKeyDown
- }, this);
- },
- _onMouseMove: function (e) {
- if (!this._moved) {
- this._moved = true;
- this._box = L.DomUtil.create('div', 'leaflet-zoom-box', this._container);
- L.DomUtil.addClass(this._container, 'leaflet-crosshair');
- this._map.fire('boxzoomstart');
- }
- this._point = this._map.mouseEventToContainerPoint(e);
- var bounds = new L.Bounds(this._point, this._startPoint),
- size = bounds.getSize();
- L.DomUtil.setPosition(this._box, bounds.min);
- this._box.style.width = size.x + 'px';
- this._box.style.height = size.y + 'px';
- },
- _finish: function () {
- if (this._moved) {
- L.DomUtil.remove(this._box);
- L.DomUtil.removeClass(this._container, 'leaflet-crosshair');
- }
- L.DomUtil.enableTextSelection();
- L.DomUtil.enableImageDrag();
- L.DomEvent.off(document, {
- contextmenu: L.DomEvent.stop,
- mousemove: this._onMouseMove,
- mouseup: this._onMouseUp,
- keydown: this._onKeyDown
- }, this);
- },
- _onMouseUp: function (e) {
- if ((e.which !== 1) && (e.button !== 1)) { return; }
- this._finish();
- if (!this._moved) { return; }
- // Postpone to next JS tick so internal click event handling
- // still see it as "moved".
- setTimeout(L.bind(this._resetState, this), 0);
- var bounds = new L.LatLngBounds(
- this._map.containerPointToLatLng(this._startPoint),
- this._map.containerPointToLatLng(this._point));
- this._map
- .fitBounds(bounds)
- .fire('boxzoomend', {boxZoomBounds: bounds});
- },
- _onKeyDown: function (e) {
- if (e.keyCode === 27) {
- this._finish();
- }
- }
- });
- // @section Handlers
- // @property boxZoom: Handler
- // Box (shift-drag with mouse) zoom handler.
- L.Map.addInitHook('addHandler', 'boxZoom', L.Map.BoxZoom);
- /*
- * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
- */
- // @namespace Map
- // @section Keyboard Navigation Options
- L.Map.mergeOptions({
- // @option keyboard: Boolean = true
- // Makes the map focusable and allows users to navigate the map with keyboard
- // arrows and `+`/`-` keys.
- keyboard: true,
- // @option keyboardPanDelta: Number = 80
- // Amount of pixels to pan when pressing an arrow key.
- keyboardPanDelta: 80
- });
- L.Map.Keyboard = L.Handler.extend({
- keyCodes: {
- left: [37],
- right: [39],
- down: [40],
- up: [38],
- zoomIn: [187, 107, 61, 171],
- zoomOut: [189, 109, 54, 173]
- },
- initialize: function (map) {
- this._map = map;
- this._setPanDelta(map.options.keyboardPanDelta);
- this._setZoomDelta(map.options.zoomDelta);
- },
- addHooks: function () {
- var container = this._map._container;
- // make the container focusable by tabbing
- if (container.tabIndex <= 0) {
- container.tabIndex = '0';
- }
- L.DomEvent.on(container, {
- focus: this._onFocus,
- blur: this._onBlur,
- mousedown: this._onMouseDown
- }, this);
- this._map.on({
- focus: this._addHooks,
- blur: this._removeHooks
- }, this);
- },
- removeHooks: function () {
- this._removeHooks();
- L.DomEvent.off(this._map._container, {
- focus: this._onFocus,
- blur: this._onBlur,
- mousedown: this._onMouseDown
- }, this);
- this._map.off({
- focus: this._addHooks,
- blur: this._removeHooks
- }, this);
- },
- _onMouseDown: function () {
- if (this._focused) { return; }
- var body = document.body,
- docEl = document.documentElement,
- top = body.scrollTop || docEl.scrollTop,
- left = body.scrollLeft || docEl.scrollLeft;
- this._map._container.focus();
- window.scrollTo(left, top);
- },
- _onFocus: function () {
- this._focused = true;
- this._map.fire('focus');
- },
- _onBlur: function () {
- this._focused = false;
- this._map.fire('blur');
- },
- _setPanDelta: function (panDelta) {
- var keys = this._panKeys = {},
- codes = this.keyCodes,
- i, len;
- for (i = 0, len = codes.left.length; i < len; i++) {
- keys[codes.left[i]] = [-1 * panDelta, 0];
- }
- for (i = 0, len = codes.right.length; i < len; i++) {
- keys[codes.right[i]] = [panDelta, 0];
- }
- for (i = 0, len = codes.down.length; i < len; i++) {
- keys[codes.down[i]] = [0, panDelta];
- }
- for (i = 0, len = codes.up.length; i < len; i++) {
- keys[codes.up[i]] = [0, -1 * panDelta];
- }
- },
- _setZoomDelta: function (zoomDelta) {
- var keys = this._zoomKeys = {},
- codes = this.keyCodes,
- i, len;
- for (i = 0, len = codes.zoomIn.length; i < len; i++) {
- keys[codes.zoomIn[i]] = zoomDelta;
- }
- for (i = 0, len = codes.zoomOut.length; i < len; i++) {
- keys[codes.zoomOut[i]] = -zoomDelta;
- }
- },
- _addHooks: function () {
- L.DomEvent.on(document, 'keydown', this._onKeyDown, this);
- },
- _removeHooks: function () {
- L.DomEvent.off(document, 'keydown', this._onKeyDown, this);
- },
- _onKeyDown: function (e) {
- if (e.altKey || e.ctrlKey || e.metaKey) { return; }
- var key = e.keyCode,
- map = this._map,
- offset;
- if (key in this._panKeys) {
- if (map._panAnim && map._panAnim._inProgress) { return; }
- offset = this._panKeys[key];
- if (e.shiftKey) {
- offset = L.point(offset).multiplyBy(3);
- }
- map.panBy(offset);
- if (map.options.maxBounds) {
- map.panInsideBounds(map.options.maxBounds);
- }
- } else if (key in this._zoomKeys) {
- map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
- } else if (key === 27) {
- map.closePopup();
- } else {
- return;
- }
- L.DomEvent.stop(e);
- }
- });
- // @section Handlers
- // @section Handlers
- // @property keyboard: Handler
- // Keyboard navigation handler.
- L.Map.addInitHook('addHandler', 'keyboard', L.Map.Keyboard);
- /*
- * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
- */
- /* @namespace Marker
- * @section Interaction handlers
- *
- * Interaction handlers are properties of a marker instance that allow you to control interaction behavior in runtime, enabling or disabling certain features such as dragging (see `Handler` methods). Example:
- *
- * ```js
- * marker.dragging.disable();
- * ```
- *
- * @property dragging: Handler
- * Marker dragging handler (by both mouse and touch).
- */
- L.Handler.MarkerDrag = L.Handler.extend({
- initialize: function (marker) {
- this._marker = marker;
- },
- addHooks: function () {
- var icon = this._marker._icon;
- if (!this._draggable) {
- this._draggable = new L.Draggable(icon, icon, true);
- }
- this._draggable.on({
- dragstart: this._onDragStart,
- drag: this._onDrag,
- dragend: this._onDragEnd
- }, this).enable();
- L.DomUtil.addClass(icon, 'leaflet-marker-draggable');
- },
- removeHooks: function () {
- this._draggable.off({
- dragstart: this._onDragStart,
- drag: this._onDrag,
- dragend: this._onDragEnd
- }, this).disable();
- if (this._marker._icon) {
- L.DomUtil.removeClass(this._marker._icon, 'leaflet-marker-draggable');
- }
- },
- moved: function () {
- return this._draggable && this._draggable._moved;
- },
- _onDragStart: function () {
- // @section Dragging events
- // @event dragstart: Event
- // Fired when the user starts dragging the marker.
- // @event movestart: Event
- // Fired when the marker starts moving (because of dragging).
- this._oldLatLng = this._marker.getLatLng();
- this._marker
- .closePopup()
- .fire('movestart')
- .fire('dragstart');
- },
- _onDrag: function (e) {
- var marker = this._marker,
- shadow = marker._shadow,
- iconPos = L.DomUtil.getPosition(marker._icon),
- latlng = marker._map.layerPointToLatLng(iconPos);
- // update shadow position
- if (shadow) {
- L.DomUtil.setPosition(shadow, iconPos);
- }
- marker._latlng = latlng;
- e.latlng = latlng;
- e.oldLatLng = this._oldLatLng;
- // @event drag: Event
- // Fired repeatedly while the user drags the marker.
- marker
- .fire('move', e)
- .fire('drag', e);
- },
- _onDragEnd: function (e) {
- // @event dragend: DragEndEvent
- // Fired when the user stops dragging the marker.
- // @event moveend: Event
- // Fired when the marker stops moving (because of dragging).
- delete this._oldLatLng;
- this._marker
- .fire('moveend')
- .fire('dragend', e);
- }
- });
- /*
- * @class Control
- * @aka L.Control
- * @inherits Class
- *
- * L.Control is a base class for implementing map controls. Handles positioning.
- * All other controls extend from this class.
- */
- L.Control = L.Class.extend({
- // @section
- // @aka Control options
- options: {
- // @option position: String = 'topright'
- // The position of the control (one of the map corners). Possible values are `'topleft'`,
- // `'topright'`, `'bottomleft'` or `'bottomright'`
- position: 'topright'
- },
- initialize: function (options) {
- L.setOptions(this, options);
- },
- /* @section
- * Classes extending L.Control will inherit the following methods:
- *
- * @method getPosition: string
- * Returns the position of the control.
- */
- getPosition: function () {
- return this.options.position;
- },
- // @method setPosition(position: string): this
- // Sets the position of the control.
- setPosition: function (position) {
- var map = this._map;
- if (map) {
- map.removeControl(this);
- }
- this.options.position = position;
- if (map) {
- map.addControl(this);
- }
- return this;
- },
- // @method getContainer: HTMLElement
- // Returns the HTMLElement that contains the control.
- getContainer: function () {
- return this._container;
- },
- // @method addTo(map: Map): this
- // Adds the control to the given map.
- addTo: function (map) {
- this.remove();
- this._map = map;
- var container = this._container = this.onAdd(map),
- pos = this.getPosition(),
- corner = map._controlCorners[pos];
- L.DomUtil.addClass(container, 'leaflet-control');
- if (pos.indexOf('bottom') !== -1) {
- corner.insertBefore(container, corner.firstChild);
- } else {
- corner.appendChild(container);
- }
- return this;
- },
- // @method remove: this
- // Removes the control from the map it is currently active on.
- remove: function () {
- if (!this._map) {
- return this;
- }
- L.DomUtil.remove(this._container);
- if (this.onRemove) {
- this.onRemove(this._map);
- }
- this._map = null;
- return this;
- },
- _refocusOnMap: function (e) {
- // if map exists and event is not a keyboard event
- if (this._map && e && e.screenX > 0 && e.screenY > 0) {
- this._map.getContainer().focus();
- }
- }
- });
- L.control = function (options) {
- return new L.Control(options);
- };
- /* @section Extension methods
- * @uninheritable
- *
- * Every control should extend from `L.Control` and (re-)implement the following methods.
- *
- * @method onAdd(map: Map): HTMLElement
- * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
- *
- * @method onRemove(map: Map)
- * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
- */
- /* @namespace Map
- * @section Methods for Layers and Controls
- */
- L.Map.include({
- // @method addControl(control: Control): this
- // Adds the given control to the map
- addControl: function (control) {
- control.addTo(this);
- return this;
- },
- // @method removeControl(control: Control): this
- // Removes the given control from the map
- removeControl: function (control) {
- control.remove();
- return this;
- },
- _initControlPos: function () {
- var corners = this._controlCorners = {},
- l = 'leaflet-',
- container = this._controlContainer =
- L.DomUtil.create('div', l + 'control-container', this._container);
- function createCorner(vSide, hSide) {
- var className = l + vSide + ' ' + l + hSide;
- corners[vSide + hSide] = L.DomUtil.create('div', className, container);
- }
- createCorner('top', 'left');
- createCorner('top', 'right');
- createCorner('bottom', 'left');
- createCorner('bottom', 'right');
- },
- _clearControlPos: function () {
- L.DomUtil.remove(this._controlContainer);
- }
- });
- /*
- * @class Control.Zoom
- * @aka L.Control.Zoom
- * @inherits Control
- *
- * A basic zoom control with two buttons (zoom in and zoom out). It is put on the map by default unless you set its [`zoomControl` option](#map-zoomcontrol) to `false`. Extends `Control`.
- */
- L.Control.Zoom = L.Control.extend({
- // @section
- // @aka Control.Zoom options
- options: {
- position: 'topleft',
- // @option zoomInText: String = '+'
- // The text set on the 'zoom in' button.
- zoomInText: '+',
- // @option zoomInTitle: String = 'Zoom in'
- // The title set on the 'zoom in' button.
- zoomInTitle: 'Zoom in',
- // @option zoomOutText: String = '-'
- // The text set on the 'zoom out' button.
- zoomOutText: '-',
- // @option zoomOutTitle: String = 'Zoom out'
- // The title set on the 'zoom out' button.
- zoomOutTitle: 'Zoom out'
- },
- onAdd: function (map) {
- var zoomName = 'leaflet-control-zoom',
- container = L.DomUtil.create('div', zoomName + ' leaflet-bar'),
- options = this.options;
- this._zoomInButton = this._createButton(options.zoomInText, options.zoomInTitle,
- zoomName + '-in', container, this._zoomIn);
- this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle,
- zoomName + '-out', container, this._zoomOut);
- this._updateDisabled();
- map.on('zoomend zoomlevelschange', this._updateDisabled, this);
- return container;
- },
- onRemove: function (map) {
- map.off('zoomend zoomlevelschange', this._updateDisabled, this);
- },
- disable: function () {
- this._disabled = true;
- this._updateDisabled();
- return this;
- },
- enable: function () {
- this._disabled = false;
- this._updateDisabled();
- return this;
- },
- _zoomIn: function (e) {
- if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) {
- this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
- }
- },
- _zoomOut: function (e) {
- if (!this._disabled && this._map._zoom > this._map.getMinZoom()) {
- this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
- }
- },
- _createButton: function (html, title, className, container, fn) {
- var link = L.DomUtil.create('a', className, container);
- link.innerHTML = html;
- link.href = '#';
- link.title = title;
- /*
- * Will force screen readers like VoiceOver to read this as "Zoom in - button"
- */
- link.setAttribute('role', 'button');
- link.setAttribute('aria-label', title);
- L.DomEvent
- .on(link, 'mousedown dblclick', L.DomEvent.stopPropagation)
- .on(link, 'click', L.DomEvent.stop)
- .on(link, 'click', fn, this)
- .on(link, 'click', this._refocusOnMap, this);
- return link;
- },
- _updateDisabled: function () {
- var map = this._map,
- className = 'leaflet-disabled';
- L.DomUtil.removeClass(this._zoomInButton, className);
- L.DomUtil.removeClass(this._zoomOutButton, className);
- if (this._disabled || map._zoom === map.getMinZoom()) {
- L.DomUtil.addClass(this._zoomOutButton, className);
- }
- if (this._disabled || map._zoom === map.getMaxZoom()) {
- L.DomUtil.addClass(this._zoomInButton, className);
- }
- }
- });
- // @namespace Map
- // @section Control options
- // @option zoomControl: Boolean = true
- // Whether a [zoom control](#control-zoom) is added to the map by default.
- L.Map.mergeOptions({
- zoomControl: true
- });
- L.Map.addInitHook(function () {
- if (this.options.zoomControl) {
- this.zoomControl = new L.Control.Zoom();
- this.addControl(this.zoomControl);
- }
- });
- // @namespace Control.Zoom
- // @factory L.control.zoom(options: Control.Zoom options)
- // Creates a zoom control
- L.control.zoom = function (options) {
- return new L.Control.Zoom(options);
- };
- /*
- * @class Control.Attribution
- * @aka L.Control.Attribution
- * @inherits Control
- *
- * The attribution control allows you to display attribution data in a small text box on a map. It is put on the map by default unless you set its [`attributionControl` option](#map-attributioncontrol) to `false`, and it fetches attribution texts from layers with the [`getAttribution` method](#layer-getattribution) automatically. Extends Control.
- */
- L.Control.Attribution = L.Control.extend({
- // @section
- // @aka Control.Attribution options
- options: {
- position: 'bottomright',
- // @option prefix: String = 'Leaflet'
- // The HTML text shown before the attributions. Pass `false` to disable.
- prefix: '<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'
- },
- initialize: function (options) {
- L.setOptions(this, options);
- this._attributions = {};
- },
- onAdd: function (map) {
- map.attributionControl = this;
- this._container = L.DomUtil.create('div', 'leaflet-control-attribution');
- if (L.DomEvent) {
- L.DomEvent.disableClickPropagation(this._container);
- }
- // TODO ugly, refactor
- for (var i in map._layers) {
- if (map._layers[i].getAttribution) {
- this.addAttribution(map._layers[i].getAttribution());
- }
- }
- this._update();
- return this._container;
- },
- // @method setPrefix(prefix: String): this
- // Sets the text before the attributions.
- setPrefix: function (prefix) {
- this.options.prefix = prefix;
- this._update();
- return this;
- },
- // @method addAttribution(text: String): this
- // Adds an attribution text (e.g. `'Vector data © Mapbox'`).
- addAttribution: function (text) {
- if (!text) { return this; }
- if (!this._attributions[text]) {
- this._attributions[text] = 0;
- }
- this._attributions[text]++;
- this._update();
- return this;
- },
- // @method removeAttribution(text: String): this
- // Removes an attribution text.
- removeAttribution: function (text) {
- if (!text) { return this; }
- if (this._attributions[text]) {
- this._attributions[text]--;
- this._update();
- }
- return this;
- },
- _update: function () {
- if (!this._map) { return; }
- var attribs = [];
- for (var i in this._attributions) {
- if (this._attributions[i]) {
- attribs.push(i);
- }
- }
- var prefixAndAttribs = [];
- if (this.options.prefix) {
- prefixAndAttribs.push(this.options.prefix);
- }
- if (attribs.length) {
- prefixAndAttribs.push(attribs.join(', '));
- }
- this._container.innerHTML = prefixAndAttribs.join(' | ');
- }
- });
- // @namespace Map
- // @section Control options
- // @option attributionControl: Boolean = true
- // Whether a [attribution control](#control-attribution) is added to the map by default.
- L.Map.mergeOptions({
- attributionControl: true
- });
- L.Map.addInitHook(function () {
- if (this.options.attributionControl) {
- new L.Control.Attribution().addTo(this);
- }
- });
- // @namespace Control.Attribution
- // @factory L.control.attribution(options: Control.Attribution options)
- // Creates an attribution control.
- L.control.attribution = function (options) {
- return new L.Control.Attribution(options);
- };
- /*
- * @class Control.Scale
- * @aka L.Control.Scale
- * @inherits Control
- *
- * A simple scale control that shows the scale of the current center of screen in metric (m/km) and imperial (mi/ft) systems. Extends `Control`.
- *
- * @example
- *
- * ```js
- * L.control.scale().addTo(map);
- * ```
- */
- L.Control.Scale = L.Control.extend({
- // @section
- // @aka Control.Scale options
- options: {
- position: 'bottomleft',
- // @option maxWidth: Number = 100
- // Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500).
- maxWidth: 100,
- // @option metric: Boolean = True
- // Whether to show the metric scale line (m/km).
- metric: true,
- // @option imperial: Boolean = True
- // Whether to show the imperial scale line (mi/ft).
- imperial: true
- // @option updateWhenIdle: Boolean = false
- // If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)).
- },
- onAdd: function (map) {
- var className = 'leaflet-control-scale',
- container = L.DomUtil.create('div', className),
- options = this.options;
- this._addScales(options, className + '-line', container);
- map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
- map.whenReady(this._update, this);
- return container;
- },
- onRemove: function (map) {
- map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
- },
- _addScales: function (options, className, container) {
- if (options.metric) {
- this._mScale = L.DomUtil.create('div', className, container);
- }
- if (options.imperial) {
- this._iScale = L.DomUtil.create('div', className, container);
- }
- },
- _update: function () {
- var map = this._map,
- y = map.getSize().y / 2;
- var maxMeters = map.distance(
- map.containerPointToLatLng([0, y]),
- map.containerPointToLatLng([this.options.maxWidth, y]));
- this._updateScales(maxMeters);
- },
- _updateScales: function (maxMeters) {
- if (this.options.metric && maxMeters) {
- this._updateMetric(maxMeters);
- }
- if (this.options.imperial && maxMeters) {
- this._updateImperial(maxMeters);
- }
- },
- _updateMetric: function (maxMeters) {
- var meters = this._getRoundNum(maxMeters),
- label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
- this._updateScale(this._mScale, label, meters / maxMeters);
- },
- _updateImperial: function (maxMeters) {
- var maxFeet = maxMeters * 3.2808399,
- maxMiles, miles, feet;
- if (maxFeet > 5280) {
- maxMiles = maxFeet / 5280;
- miles = this._getRoundNum(maxMiles);
- this._updateScale(this._iScale, miles + ' mi', miles / maxMiles);
- } else {
- feet = this._getRoundNum(maxFeet);
- this._updateScale(this._iScale, feet + ' ft', feet / maxFeet);
- }
- },
- _updateScale: function (scale, text, ratio) {
- scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px';
- scale.innerHTML = text;
- },
- _getRoundNum: function (num) {
- var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
- d = num / pow10;
- d = d >= 10 ? 10 :
- d >= 5 ? 5 :
- d >= 3 ? 3 :
- d >= 2 ? 2 : 1;
- return pow10 * d;
- }
- });
- // @factory L.control.scale(options?: Control.Scale options)
- // Creates an scale control with the given options.
- L.control.scale = function (options) {
- return new L.Control.Scale(options);
- };
- /*
- * @class Control.Layers
- * @aka L.Control.Layers
- * @inherits Control
- *
- * The layers control gives users the ability to switch between different base layers and switch overlays on/off (check out the [detailed example](http://leafletjs.com/examples/layers-control.html)). Extends `Control`.
- *
- * @example
- *
- * ```js
- * var baseLayers = {
- * "Mapbox": mapbox,
- * "OpenStreetMap": osm
- * };
- *
- * var overlays = {
- * "Marker": marker,
- * "Roads": roadsLayer
- * };
- *
- * L.control.layers(baseLayers, overlays).addTo(map);
- * ```
- *
- * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:
- *
- * ```js
- * {
- * "<someName1>": layer1,
- * "<someName2>": layer2
- * }
- * ```
- *
- * The layer names can contain HTML, which allows you to add additional styling to the items:
- *
- * ```js
- * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer}
- * ```
- */
- L.Control.Layers = L.Control.extend({
- // @section
- // @aka Control.Layers options
- options: {
- // @option collapsed: Boolean = true
- // If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch.
- collapsed: true,
- position: 'topright',
- // @option autoZIndex: Boolean = true
- // If `true`, the control will assign zIndexes in increasing order to all of its layers so that the order is preserved when switching them on/off.
- autoZIndex: true,
- // @option hideSingleBase: Boolean = false
- // If `true`, the base layers in the control will be hidden when there is only one.
- hideSingleBase: false,
- // @option sortLayers: Boolean = false
- // Whether to sort the layers. When `false`, layers will keep the order
- // in which they were added to the control.
- sortLayers: false,
- // @option sortFunction: Function = *
- // A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
- // that will be used for sorting the layers, when `sortLayers` is `true`.
- // The function receives both the `L.Layer` instances and their names, as in
- // `sortFunction(layerA, layerB, nameA, nameB)`.
- // By default, it sorts layers alphabetically by their name.
- sortFunction: function (layerA, layerB, nameA, nameB) {
- return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0);
- }
- },
- initialize: function (baseLayers, overlays, options) {
- L.setOptions(this, options);
- this._layers = [];
- this._lastZIndex = 0;
- this._handlingClick = false;
- for (var i in baseLayers) {
- this._addLayer(baseLayers[i], i);
- }
- for (i in overlays) {
- this._addLayer(overlays[i], i, true);
- }
- },
- onAdd: function (map) {
- this._initLayout();
- this._update();
- this._map = map;
- map.on('zoomend', this._checkDisabledLayers, this);
- return this._container;
- },
- onRemove: function () {
- this._map.off('zoomend', this._checkDisabledLayers, this);
- for (var i = 0; i < this._layers.length; i++) {
- this._layers[i].layer.off('add remove', this._onLayerChange, this);
- }
- },
- // @method addBaseLayer(layer: Layer, name: String): this
- // Adds a base layer (radio button entry) with the given name to the control.
- addBaseLayer: function (layer, name) {
- this._addLayer(layer, name);
- return (this._map) ? this._update() : this;
- },
- // @method addOverlay(layer: Layer, name: String): this
- // Adds an overlay (checkbox entry) with the given name to the control.
- addOverlay: function (layer, name) {
- this._addLayer(layer, name, true);
- return (this._map) ? this._update() : this;
- },
- // @method removeLayer(layer: Layer): this
- // Remove the given layer from the control.
- removeLayer: function (layer) {
- layer.off('add remove', this._onLayerChange, this);
- var obj = this._getLayer(L.stamp(layer));
- if (obj) {
- this._layers.splice(this._layers.indexOf(obj), 1);
- }
- return (this._map) ? this._update() : this;
- },
- // @method expand(): this
- // Expand the control container if collapsed.
- expand: function () {
- L.DomUtil.addClass(this._container, 'leaflet-control-layers-expanded');
- this._form.style.height = null;
- var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
- if (acceptableHeight < this._form.clientHeight) {
- L.DomUtil.addClass(this._form, 'leaflet-control-layers-scrollbar');
- this._form.style.height = acceptableHeight + 'px';
- } else {
- L.DomUtil.removeClass(this._form, 'leaflet-control-layers-scrollbar');
- }
- this._checkDisabledLayers();
- return this;
- },
- // @method collapse(): this
- // Collapse the control container if expanded.
- collapse: function () {
- L.DomUtil.removeClass(this._container, 'leaflet-control-layers-expanded');
- return this;
- },
- _initLayout: function () {
- var className = 'leaflet-control-layers',
- container = this._container = L.DomUtil.create('div', className);
- // makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
- container.setAttribute('aria-haspopup', true);
- L.DomEvent.disableClickPropagation(container);
- if (!L.Browser.touch) {
- L.DomEvent.disableScrollPropagation(container);
- }
- var form = this._form = L.DomUtil.create('form', className + '-list');
- if (!L.Browser.android) {
- L.DomEvent.on(container, {
- mouseenter: this.expand,
- mouseleave: this.collapse
- }, this);
- }
- var link = this._layersLink = L.DomUtil.create('a', className + '-toggle', container);
- link.href = '#';
- link.title = 'Layers';
- if (L.Browser.touch) {
- L.DomEvent
- .on(link, 'click', L.DomEvent.stop)
- .on(link, 'click', this.expand, this);
- } else {
- L.DomEvent.on(link, 'focus', this.expand, this);
- }
- // work around for Firefox Android issue https://github.com/Leaflet/Leaflet/issues/2033
- L.DomEvent.on(form, 'click', function () {
- setTimeout(L.bind(this._onInputClick, this), 0);
- }, this);
- this._map.on('click', this.collapse, this);
- // TODO keyboard accessibility
- if (!this.options.collapsed) {
- this.expand();
- }
- this._baseLayersList = L.DomUtil.create('div', className + '-base', form);
- this._separator = L.DomUtil.create('div', className + '-separator', form);
- this._overlaysList = L.DomUtil.create('div', className + '-overlays', form);
- container.appendChild(form);
- },
- _getLayer: function (id) {
- for (var i = 0; i < this._layers.length; i++) {
- if (this._layers[i] && L.stamp(this._layers[i].layer) === id) {
- return this._layers[i];
- }
- }
- },
- _addLayer: function (layer, name, overlay) {
- layer.on('add remove', this._onLayerChange, this);
- this._layers.push({
- layer: layer,
- name: name,
- overlay: overlay
- });
- if (this.options.sortLayers) {
- this._layers.sort(L.bind(function (a, b) {
- return this.options.sortFunction(a.layer, b.layer, a.name, b.name);
- }, this));
- }
- if (this.options.autoZIndex && layer.setZIndex) {
- this._lastZIndex++;
- layer.setZIndex(this._lastZIndex);
- }
- },
- _update: function () {
- if (!this._container) { return this; }
- L.DomUtil.empty(this._baseLayersList);
- L.DomUtil.empty(this._overlaysList);
- var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;
- for (i = 0; i < this._layers.length; i++) {
- obj = this._layers[i];
- this._addItem(obj);
- overlaysPresent = overlaysPresent || obj.overlay;
- baseLayersPresent = baseLayersPresent || !obj.overlay;
- baseLayersCount += !obj.overlay ? 1 : 0;
- }
- // Hide base layers section if there's only one layer.
- if (this.options.hideSingleBase) {
- baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
- this._baseLayersList.style.display = baseLayersPresent ? '' : 'none';
- }
- this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
- return this;
- },
- _onLayerChange: function (e) {
- if (!this._handlingClick) {
- this._update();
- }
- var obj = this._getLayer(L.stamp(e.target));
- // @namespace Map
- // @section Layer events
- // @event baselayerchange: LayersControlEvent
- // Fired when the base layer is changed through the [layer control](#control-layers).
- // @event overlayadd: LayersControlEvent
- // Fired when an overlay is selected through the [layer control](#control-layers).
- // @event overlayremove: LayersControlEvent
- // Fired when an overlay is deselected through the [layer control](#control-layers).
- // @namespace Control.Layers
- var type = obj.overlay ?
- (e.type === 'add' ? 'overlayadd' : 'overlayremove') :
- (e.type === 'add' ? 'baselayerchange' : null);
- if (type) {
- this._map.fire(type, obj);
- }
- },
- // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
- _createRadioElement: function (name, checked) {
- var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' +
- name + '"' + (checked ? ' checked="checked"' : '') + '/>';
- var radioFragment = document.createElement('div');
- radioFragment.innerHTML = radioHtml;
- return radioFragment.firstChild;
- },
- _addItem: function (obj) {
- var label = document.createElement('label'),
- checked = this._map.hasLayer(obj.layer),
- input;
- if (obj.overlay) {
- input = document.createElement('input');
- input.type = 'checkbox';
- input.className = 'leaflet-control-layers-selector';
- input.defaultChecked = checked;
- } else {
- input = this._createRadioElement('leaflet-base-layers', checked);
- }
- input.layerId = L.stamp(obj.layer);
- L.DomEvent.on(input, 'click', this._onInputClick, this);
- var name = document.createElement('span');
- name.innerHTML = ' ' + obj.name;
- // Helps from preventing layer control flicker when checkboxes are disabled
- // https://github.com/Leaflet/Leaflet/issues/2771
- var holder = document.createElement('div');
- label.appendChild(holder);
- holder.appendChild(input);
- holder.appendChild(name);
- var container = obj.overlay ? this._overlaysList : this._baseLayersList;
- container.appendChild(label);
- this._checkDisabledLayers();
- return label;
- },
- _onInputClick: function () {
- var inputs = this._form.getElementsByTagName('input'),
- input, layer, hasLayer;
- var addedLayers = [],
- removedLayers = [];
- this._handlingClick = true;
- for (var i = inputs.length - 1; i >= 0; i--) {
- input = inputs[i];
- layer = this._getLayer(input.layerId).layer;
- hasLayer = this._map.hasLayer(layer);
- if (input.checked && !hasLayer) {
- addedLayers.push(layer);
- } else if (!input.checked && hasLayer) {
- removedLayers.push(layer);
- }
- }
- // Bugfix issue 2318: Should remove all old layers before readding new ones
- for (i = 0; i < removedLayers.length; i++) {
- this._map.removeLayer(removedLayers[i]);
- }
- for (i = 0; i < addedLayers.length; i++) {
- this._map.addLayer(addedLayers[i]);
- }
- this._handlingClick = false;
- this._refocusOnMap();
- },
- _checkDisabledLayers: function () {
- var inputs = this._form.getElementsByTagName('input'),
- input,
- layer,
- zoom = this._map.getZoom();
- for (var i = inputs.length - 1; i >= 0; i--) {
- input = inputs[i];
- layer = this._getLayer(input.layerId).layer;
- input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||
- (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);
- }
- },
- _expand: function () {
- // Backward compatibility, remove me in 1.1.
- return this.expand();
- },
- _collapse: function () {
- // Backward compatibility, remove me in 1.1.
- return this.collapse();
- }
- });
- // @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)
- // Creates an attribution control with the given layers. Base layers will be switched with radio buttons, while overlays will be switched with checkboxes. Note that all base layers should be passed in the base layers object, but only one should be added to the map during map instantiation.
- L.control.layers = function (baseLayers, overlays, options) {
- return new L.Control.Layers(baseLayers, overlays, options);
- };
- }(window, document));
- },{}],4:[function(require,module,exports){
- /*!
- * mustache.js - Logic-less {{mustache}} templates with JavaScript
- * http://github.com/janl/mustache.js
- */
- /*global define: false Mustache: true*/
- (function defineMustache (global, factory) {
- if (typeof exports === 'object' && exports && typeof exports.nodeName !== 'string') {
- factory(exports); // CommonJS
- } else if (typeof define === 'function' && define.amd) {
- define(['exports'], factory); // AMD
- } else {
- global.Mustache = {};
- factory(global.Mustache); // script, wsh, asp
- }
- }(this, function mustacheFactory (mustache) {
- var objectToString = Object.prototype.toString;
- var isArray = Array.isArray || function isArrayPolyfill (object) {
- return objectToString.call(object) === '[object Array]';
- };
- function isFunction (object) {
- return typeof object === 'function';
- }
- /**
- * More correct typeof string handling array
- * which normally returns typeof 'object'
- */
- function typeStr (obj) {
- return isArray(obj) ? 'array' : typeof obj;
- }
- function escapeRegExp (string) {
- return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&');
- }
- /**
- * Null safe way of checking whether or not an object,
- * including its prototype, has a given property
- */
- function hasProperty (obj, propName) {
- return obj != null && typeof obj === 'object' && (propName in obj);
- }
- // Workaround for https://issues.apache.org/jira/browse/COUCHDB-577
- // See https://github.com/janl/mustache.js/issues/189
- var regExpTest = RegExp.prototype.test;
- function testRegExp (re, string) {
- return regExpTest.call(re, string);
- }
- var nonSpaceRe = /\S/;
- function isWhitespace (string) {
- return !testRegExp(nonSpaceRe, string);
- }
- var entityMap = {
- '&': '&',
- '<': '<',
- '>': '>',
- '"': '"',
- "'": ''',
- '/': '/',
- '`': '`',
- '=': '='
- };
- function escapeHtml (string) {
- return String(string).replace(/[&<>"'`=\/]/g, function fromEntityMap (s) {
- return entityMap[s];
- });
- }
- var whiteRe = /\s*/;
- var spaceRe = /\s+/;
- var equalsRe = /\s*=/;
- var curlyRe = /\s*\}/;
- var tagRe = /#|\^|\/|>|\{|&|=|!/;
- /**
- * Breaks up the given `template` string into a tree of tokens. If the `tags`
- * argument is given here it must be an array with two string values: the
- * opening and closing tags used in the template (e.g. [ "<%", "%>" ]). Of
- * course, the default is to use mustaches (i.e. mustache.tags).
- *
- * A token is an array with at least 4 elements. The first element is the
- * mustache symbol that was used inside the tag, e.g. "#" or "&". If the tag
- * did not contain a symbol (i.e. {{myValue}}) this element is "name". For
- * all text that appears outside a symbol this element is "text".
- *
- * The second element of a token is its "value". For mustache tags this is
- * whatever else was inside the tag besides the opening symbol. For text tokens
- * this is the text itself.
- *
- * The third and fourth elements of the token are the start and end indices,
- * respectively, of the token in the original template.
- *
- * Tokens that are the root node of a subtree contain two more elements: 1) an
- * array of tokens in the subtree and 2) the index in the original template at
- * which the closing tag for that section begins.
- */
- function parseTemplate (template, tags) {
- if (!template)
- return [];
- var sections = []; // Stack to hold section tokens
- var tokens = []; // Buffer to hold the tokens
- var spaces = []; // Indices of whitespace tokens on the current line
- var hasTag = false; // Is there a {{tag}} on the current line?
- var nonSpace = false; // Is there a non-space char on the current line?
- // Strips all whitespace tokens array for the current line
- // if there was a {{#tag}} on it and otherwise only space.
- function stripSpace () {
- if (hasTag && !nonSpace) {
- while (spaces.length)
- delete tokens[spaces.pop()];
- } else {
- spaces = [];
- }
- hasTag = false;
- nonSpace = false;
- }
- var openingTagRe, closingTagRe, closingCurlyRe;
- function compileTags (tagsToCompile) {
- if (typeof tagsToCompile === 'string')
- tagsToCompile = tagsToCompile.split(spaceRe, 2);
- if (!isArray(tagsToCompile) || tagsToCompile.length !== 2)
- throw new Error('Invalid tags: ' + tagsToCompile);
- openingTagRe = new RegExp(escapeRegExp(tagsToCompile[0]) + '\\s*');
- closingTagRe = new RegExp('\\s*' + escapeRegExp(tagsToCompile[1]));
- closingCurlyRe = new RegExp('\\s*' + escapeRegExp('}' + tagsToCompile[1]));
- }
- compileTags(tags || mustache.tags);
- var scanner = new Scanner(template);
- var start, type, value, chr, token, openSection;
- while (!scanner.eos()) {
- start = scanner.pos;
- // Match any text between tags.
- value = scanner.scanUntil(openingTagRe);
- if (value) {
- for (var i = 0, valueLength = value.length; i < valueLength; ++i) {
- chr = value.charAt(i);
- if (isWhitespace(chr)) {
- spaces.push(tokens.length);
- } else {
- nonSpace = true;
- }
- tokens.push([ 'text', chr, start, start + 1 ]);
- start += 1;
- // Check for whitespace on the current line.
- if (chr === '\n')
- stripSpace();
- }
- }
- // Match the opening tag.
- if (!scanner.scan(openingTagRe))
- break;
- hasTag = true;
- // Get the tag type.
- type = scanner.scan(tagRe) || 'name';
- scanner.scan(whiteRe);
- // Get the tag value.
- if (type === '=') {
- value = scanner.scanUntil(equalsRe);
- scanner.scan(equalsRe);
- scanner.scanUntil(closingTagRe);
- } else if (type === '{') {
- value = scanner.scanUntil(closingCurlyRe);
- scanner.scan(curlyRe);
- scanner.scanUntil(closingTagRe);
- type = '&';
- } else {
- value = scanner.scanUntil(closingTagRe);
- }
- // Match the closing tag.
- if (!scanner.scan(closingTagRe))
- throw new Error('Unclosed tag at ' + scanner.pos);
- token = [ type, value, start, scanner.pos ];
- tokens.push(token);
- if (type === '#' || type === '^') {
- sections.push(token);
- } else if (type === '/') {
- // Check section nesting.
- openSection = sections.pop();
- if (!openSection)
- throw new Error('Unopened section "' + value + '" at ' + start);
- if (openSection[1] !== value)
- throw new Error('Unclosed section "' + openSection[1] + '" at ' + start);
- } else if (type === 'name' || type === '{' || type === '&') {
- nonSpace = true;
- } else if (type === '=') {
- // Set the tags for the next time around.
- compileTags(value);
- }
- }
- // Make sure there are no open sections when we're done.
- openSection = sections.pop();
- if (openSection)
- throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos);
- return nestTokens(squashTokens(tokens));
- }
- /**
- * Combines the values of consecutive text tokens in the given `tokens` array
- * to a single token.
- */
- function squashTokens (tokens) {
- var squashedTokens = [];
- var token, lastToken;
- for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
- token = tokens[i];
- if (token) {
- if (token[0] === 'text' && lastToken && lastToken[0] === 'text') {
- lastToken[1] += token[1];
- lastToken[3] = token[3];
- } else {
- squashedTokens.push(token);
- lastToken = token;
- }
- }
- }
- return squashedTokens;
- }
- /**
- * Forms the given array of `tokens` into a nested tree structure where
- * tokens that represent a section have two additional items: 1) an array of
- * all tokens that appear in that section and 2) the index in the original
- * template that represents the end of that section.
- */
- function nestTokens (tokens) {
- var nestedTokens = [];
- var collector = nestedTokens;
- var sections = [];
- var token, section;
- for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
- token = tokens[i];
- switch (token[0]) {
- case '#':
- case '^':
- collector.push(token);
- sections.push(token);
- collector = token[4] = [];
- break;
- case '/':
- section = sections.pop();
- section[5] = token[2];
- collector = sections.length > 0 ? sections[sections.length - 1][4] : nestedTokens;
- break;
- default:
- collector.push(token);
- }
- }
- return nestedTokens;
- }
- /**
- * A simple string scanner that is used by the template parser to find
- * tokens in template strings.
- */
- function Scanner (string) {
- this.string = string;
- this.tail = string;
- this.pos = 0;
- }
- /**
- * Returns `true` if the tail is empty (end of string).
- */
- Scanner.prototype.eos = function eos () {
- return this.tail === '';
- };
- /**
- * Tries to match the given regular expression at the current position.
- * Returns the matched text if it can match, the empty string otherwise.
- */
- Scanner.prototype.scan = function scan (re) {
- var match = this.tail.match(re);
- if (!match || match.index !== 0)
- return '';
- var string = match[0];
- this.tail = this.tail.substring(string.length);
- this.pos += string.length;
- return string;
- };
- /**
- * Skips all text until the given regular expression can be matched. Returns
- * the skipped string, which is the entire tail if no match can be made.
- */
- Scanner.prototype.scanUntil = function scanUntil (re) {
- var index = this.tail.search(re), match;
- switch (index) {
- case -1:
- match = this.tail;
- this.tail = '';
- break;
- case 0:
- match = '';
- break;
- default:
- match = this.tail.substring(0, index);
- this.tail = this.tail.substring(index);
- }
- this.pos += match.length;
- return match;
- };
- /**
- * Represents a rendering context by wrapping a view object and
- * maintaining a reference to the parent context.
- */
- function Context (view, parentContext) {
- this.view = view;
- this.cache = { '.': this.view };
- this.parent = parentContext;
- }
- /**
- * Creates a new context using the given view with this context
- * as the parent.
- */
- Context.prototype.push = function push (view) {
- return new Context(view, this);
- };
- /**
- * Returns the value of the given name in this context, traversing
- * up the context hierarchy if the value is absent in this context's view.
- */
- Context.prototype.lookup = function lookup (name) {
- var cache = this.cache;
- var value;
- if (cache.hasOwnProperty(name)) {
- value = cache[name];
- } else {
- var context = this, names, index, lookupHit = false;
- while (context) {
- if (name.indexOf('.') > 0) {
- value = context.view;
- names = name.split('.');
- index = 0;
- /**
- * Using the dot notion path in `name`, we descend through the
- * nested objects.
- *
- * To be certain that the lookup has been successful, we have to
- * check if the last object in the path actually has the property
- * we are looking for. We store the result in `lookupHit`.
- *
- * This is specially necessary for when the value has been set to
- * `undefined` and we want to avoid looking up parent contexts.
- **/
- while (value != null && index < names.length) {
- if (index === names.length - 1)
- lookupHit = hasProperty(value, names[index]);
- value = value[names[index++]];
- }
- } else {
- value = context.view[name];
- lookupHit = hasProperty(context.view, name);
- }
- if (lookupHit)
- break;
- context = context.parent;
- }
- cache[name] = value;
- }
- if (isFunction(value))
- value = value.call(this.view);
- return value;
- };
- /**
- * A Writer knows how to take a stream of tokens and render them to a
- * string, given a context. It also maintains a cache of templates to
- * avoid the need to parse the same template twice.
- */
- function Writer () {
- this.cache = {};
- }
- /**
- * Clears all cached templates in this writer.
- */
- Writer.prototype.clearCache = function clearCache () {
- this.cache = {};
- };
- /**
- * Parses and caches the given `template` and returns the array of tokens
- * that is generated from the parse.
- */
- Writer.prototype.parse = function parse (template, tags) {
- var cache = this.cache;
- var tokens = cache[template];
- if (tokens == null)
- tokens = cache[template] = parseTemplate(template, tags);
- return tokens;
- };
- /**
- * High-level method that is used to render the given `template` with
- * the given `view`.
- *
- * The optional `partials` argument may be an object that contains the
- * names and templates of partials that are used in the template. It may
- * also be a function that is used to load partial templates on the fly
- * that takes a single argument: the name of the partial.
- */
- Writer.prototype.render = function render (template, view, partials) {
- var tokens = this.parse(template);
- var context = (view instanceof Context) ? view : new Context(view);
- return this.renderTokens(tokens, context, partials, template);
- };
- /**
- * Low-level method that renders the given array of `tokens` using
- * the given `context` and `partials`.
- *
- * Note: The `originalTemplate` is only ever used to extract the portion
- * of the original template that was contained in a higher-order section.
- * If the template doesn't use higher-order sections, this argument may
- * be omitted.
- */
- Writer.prototype.renderTokens = function renderTokens (tokens, context, partials, originalTemplate) {
- var buffer = '';
- var token, symbol, value;
- for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
- value = undefined;
- token = tokens[i];
- symbol = token[0];
- if (symbol === '#') value = this.renderSection(token, context, partials, originalTemplate);
- else if (symbol === '^') value = this.renderInverted(token, context, partials, originalTemplate);
- else if (symbol === '>') value = this.renderPartial(token, context, partials, originalTemplate);
- else if (symbol === '&') value = this.unescapedValue(token, context);
- else if (symbol === 'name') value = this.escapedValue(token, context);
- else if (symbol === 'text') value = this.rawValue(token);
- if (value !== undefined)
- buffer += value;
- }
- return buffer;
- };
- Writer.prototype.renderSection = function renderSection (token, context, partials, originalTemplate) {
- var self = this;
- var buffer = '';
- var value = context.lookup(token[1]);
- // This function is used to render an arbitrary template
- // in the current context by higher-order sections.
- function subRender (template) {
- return self.render(template, context, partials);
- }
- if (!value) return;
- if (isArray(value)) {
- for (var j = 0, valueLength = value.length; j < valueLength; ++j) {
- buffer += this.renderTokens(token[4], context.push(value[j]), partials, originalTemplate);
- }
- } else if (typeof value === 'object' || typeof value === 'string' || typeof value === 'number') {
- buffer += this.renderTokens(token[4], context.push(value), partials, originalTemplate);
- } else if (isFunction(value)) {
- if (typeof originalTemplate !== 'string')
- throw new Error('Cannot use higher-order sections without the original template');
- // Extract the portion of the original template that the section contains.
- value = value.call(context.view, originalTemplate.slice(token[3], token[5]), subRender);
- if (value != null)
- buffer += value;
- } else {
- buffer += this.renderTokens(token[4], context, partials, originalTemplate);
- }
- return buffer;
- };
- Writer.prototype.renderInverted = function renderInverted (token, context, partials, originalTemplate) {
- var value = context.lookup(token[1]);
- // Use JavaScript's definition of falsy. Include empty arrays.
- // See https://github.com/janl/mustache.js/issues/186
- if (!value || (isArray(value) && value.length === 0))
- return this.renderTokens(token[4], context, partials, originalTemplate);
- };
- Writer.prototype.renderPartial = function renderPartial (token, context, partials) {
- if (!partials) return;
- var value = isFunction(partials) ? partials(token[1]) : partials[token[1]];
- if (value != null)
- return this.renderTokens(this.parse(value), context, partials, value);
- };
- Writer.prototype.unescapedValue = function unescapedValue (token, context) {
- var value = context.lookup(token[1]);
- if (value != null)
- return value;
- };
- Writer.prototype.escapedValue = function escapedValue (token, context) {
- var value = context.lookup(token[1]);
- if (value != null)
- return mustache.escape(value);
- };
- Writer.prototype.rawValue = function rawValue (token) {
- return token[1];
- };
- mustache.name = 'mustache.js';
- mustache.version = '2.2.1';
- mustache.tags = [ '{{', '}}' ];
- // All high-level mustache.* functions use this writer.
- var defaultWriter = new Writer();
- /**
- * Clears all cached templates in the default writer.
- */
- mustache.clearCache = function clearCache () {
- return defaultWriter.clearCache();
- };
- /**
- * Parses and caches the given template in the default writer and returns the
- * array of tokens it contains. Doing this ahead of time avoids the need to
- * parse templates on the fly as they are rendered.
- */
- mustache.parse = function parse (template, tags) {
- return defaultWriter.parse(template, tags);
- };
- /**
- * Renders the `template` with the given `view` and `partials` using the
- * default writer.
- */
- mustache.render = function render (template, view, partials) {
- if (typeof template !== 'string') {
- throw new TypeError('Invalid template! Template should be a "string" ' +
- 'but "' + typeStr(template) + '" was given as the first ' +
- 'argument for mustache#render(template, view, partials)');
- }
- return defaultWriter.render(template, view, partials);
- };
- // This is here for backwards compatibility with 0.4.x.,
- /*eslint-disable */ // eslint wants camel cased function name
- mustache.to_html = function to_html (template, view, partials, send) {
- /*eslint-enable*/
- var result = mustache.render(template, view, partials);
- if (isFunction(send)) {
- send(result);
- } else {
- return result;
- }
- };
- // Export the escaping function so that the user may override it.
- // See https://github.com/janl/mustache.js/issues/244
- mustache.escape = escapeHtml;
- // Export these mainly for testing, but also for advanced usage.
- mustache.Scanner = Scanner;
- mustache.Context = Context;
- mustache.Writer = Writer;
- }));
- },{}],5:[function(require,module,exports){
- var html_sanitize = require('./sanitizer-bundle.js');
- module.exports = function(_) {
- if (!_) return '';
- return html_sanitize(_, cleanUrl, cleanId);
- };
- // https://bugzilla.mozilla.org/show_bug.cgi?id=255107
- function cleanUrl(url) {
- 'use strict';
- if (/^https?/.test(url.getScheme())) return url.toString();
- if (/^mailto?/.test(url.getScheme())) return url.toString();
- if ('data' == url.getScheme() && /^image/.test(url.getPath())) {
- return url.toString();
- }
- }
- function cleanId(id) { return id; }
- },{"./sanitizer-bundle.js":6}],6:[function(require,module,exports){
- // Copyright (C) 2010 Google Inc.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- /**
- * @fileoverview
- * Implements RFC 3986 for parsing/formatting URIs.
- *
- * @author mikesamuel@gmail.com
- * \@provides URI
- * \@overrides window
- */
- var URI = (function () {
- /**
- * creates a uri from the string form. The parser is relaxed, so special
- * characters that aren't escaped but don't cause ambiguities will not cause
- * parse failures.
- *
- * @return {URI|null}
- */
- function parse(uriStr) {
- var m = ('' + uriStr).match(URI_RE_);
- if (!m) { return null; }
- return new URI(
- nullIfAbsent(m[1]),
- nullIfAbsent(m[2]),
- nullIfAbsent(m[3]),
- nullIfAbsent(m[4]),
- nullIfAbsent(m[5]),
- nullIfAbsent(m[6]),
- nullIfAbsent(m[7]));
- }
- /**
- * creates a uri from the given parts.
- *
- * @param scheme {string} an unencoded scheme such as "http" or null
- * @param credentials {string} unencoded user credentials or null
- * @param domain {string} an unencoded domain name or null
- * @param port {number} a port number in [1, 32768].
- * -1 indicates no port, as does null.
- * @param path {string} an unencoded path
- * @param query {Array.<string>|string|null} a list of unencoded cgi
- * parameters where even values are keys and odds the corresponding values
- * or an unencoded query.
- * @param fragment {string} an unencoded fragment without the "#" or null.
- * @return {URI}
- */
- function create(scheme, credentials, domain, port, path, query, fragment) {
- var uri = new URI(
- encodeIfExists2(scheme, URI_DISALLOWED_IN_SCHEME_OR_CREDENTIALS_),
- encodeIfExists2(
- credentials, URI_DISALLOWED_IN_SCHEME_OR_CREDENTIALS_),
- encodeIfExists(domain),
- port > 0 ? port.toString() : null,
- encodeIfExists2(path, URI_DISALLOWED_IN_PATH_),
- null,
- encodeIfExists(fragment));
- if (query) {
- if ('string' === typeof query) {
- uri.setRawQuery(query.replace(/[^?&=0-9A-Za-z_\-~.%]/g, encodeOne));
- } else {
- uri.setAllParameters(query);
- }
- }
- return uri;
- }
- function encodeIfExists(unescapedPart) {
- if ('string' == typeof unescapedPart) {
- return encodeURIComponent(unescapedPart);
- }
- return null;
- };
- /**
- * if unescapedPart is non null, then escapes any characters in it that aren't
- * valid characters in a url and also escapes any special characters that
- * appear in extra.
- *
- * @param unescapedPart {string}
- * @param extra {RegExp} a character set of characters in [\01-\177].
- * @return {string|null} null iff unescapedPart == null.
- */
- function encodeIfExists2(unescapedPart, extra) {
- if ('string' == typeof unescapedPart) {
- return encodeURI(unescapedPart).replace(extra, encodeOne);
- }
- return null;
- };
- /** converts a character in [\01-\177] to its url encoded equivalent. */
- function encodeOne(ch) {
- var n = ch.charCodeAt(0);
- return '%' + '0123456789ABCDEF'.charAt((n >> 4) & 0xf) +
- '0123456789ABCDEF'.charAt(n & 0xf);
- }
- /**
- * {@updoc
- * $ normPath('foo/./bar')
- * # 'foo/bar'
- * $ normPath('./foo')
- * # 'foo'
- * $ normPath('foo/.')
- * # 'foo'
- * $ normPath('foo//bar')
- * # 'foo/bar'
- * }
- */
- function normPath(path) {
- return path.replace(/(^|\/)\.(?:\/|$)/g, '$1').replace(/\/{2,}/g, '/');
- }
- var PARENT_DIRECTORY_HANDLER = new RegExp(
- ''
- // A path break
- + '(/|^)'
- // followed by a non .. path element
- // (cannot be . because normPath is used prior to this RegExp)
- + '(?:[^./][^/]*|\\.{2,}(?:[^./][^/]*)|\\.{3,}[^/]*)'
- // followed by .. followed by a path break.
- + '/\\.\\.(?:/|$)');
- var PARENT_DIRECTORY_HANDLER_RE = new RegExp(PARENT_DIRECTORY_HANDLER);
- var EXTRA_PARENT_PATHS_RE = /^(?:\.\.\/)*(?:\.\.$)?/;
- /**
- * Normalizes its input path and collapses all . and .. sequences except for
- * .. sequences that would take it above the root of the current parent
- * directory.
- * {@updoc
- * $ collapse_dots('foo/../bar')
- * # 'bar'
- * $ collapse_dots('foo/./bar')
- * # 'foo/bar'
- * $ collapse_dots('foo/../bar/./../../baz')
- * # 'baz'
- * $ collapse_dots('../foo')
- * # '../foo'
- * $ collapse_dots('../foo').replace(EXTRA_PARENT_PATHS_RE, '')
- * # 'foo'
- * }
- */
- function collapse_dots(path) {
- if (path === null) { return null; }
- var p = normPath(path);
- // Only /../ left to flatten
- var r = PARENT_DIRECTORY_HANDLER_RE;
- // We replace with $1 which matches a / before the .. because this
- // guarantees that:
- // (1) we have at most 1 / between the adjacent place,
- // (2) always have a slash if there is a preceding path section, and
- // (3) we never turn a relative path into an absolute path.
- for (var q; (q = p.replace(r, '$1')) != p; p = q) {};
- return p;
- }
- /**
- * resolves a relative url string to a base uri.
- * @return {URI}
- */
- function resolve(baseUri, relativeUri) {
- // there are several kinds of relative urls:
- // 1. //foo - replaces everything from the domain on. foo is a domain name
- // 2. foo - replaces the last part of the path, the whole query and fragment
- // 3. /foo - replaces the the path, the query and fragment
- // 4. ?foo - replace the query and fragment
- // 5. #foo - replace the fragment only
- var absoluteUri = baseUri.clone();
- // we satisfy these conditions by looking for the first part of relativeUri
- // that is not blank and applying defaults to the rest
- var overridden = relativeUri.hasScheme();
- if (overridden) {
- absoluteUri.setRawScheme(relativeUri.getRawScheme());
- } else {
- overridden = relativeUri.hasCredentials();
- }
- if (overridden) {
- absoluteUri.setRawCredentials(relativeUri.getRawCredentials());
- } else {
- overridden = relativeUri.hasDomain();
- }
- if (overridden) {
- absoluteUri.setRawDomain(relativeUri.getRawDomain());
- } else {
- overridden = relativeUri.hasPort();
- }
- var rawPath = relativeUri.getRawPath();
- var simplifiedPath = collapse_dots(rawPath);
- if (overridden) {
- absoluteUri.setPort(relativeUri.getPort());
- simplifiedPath = simplifiedPath
- && simplifiedPath.replace(EXTRA_PARENT_PATHS_RE, '');
- } else {
- overridden = !!rawPath;
- if (overridden) {
- // resolve path properly
- if (simplifiedPath.charCodeAt(0) !== 0x2f /* / */) { // path is relative
- var absRawPath = collapse_dots(absoluteUri.getRawPath() || '')
- .replace(EXTRA_PARENT_PATHS_RE, '');
- var slash = absRawPath.lastIndexOf('/') + 1;
- simplifiedPath = collapse_dots(
- (slash ? absRawPath.substring(0, slash) : '')
- + collapse_dots(rawPath))
- .replace(EXTRA_PARENT_PATHS_RE, '');
- }
- } else {
- simplifiedPath = simplifiedPath
- && simplifiedPath.replace(EXTRA_PARENT_PATHS_RE, '');
- if (simplifiedPath !== rawPath) {
- absoluteUri.setRawPath(simplifiedPath);
- }
- }
- }
- if (overridden) {
- absoluteUri.setRawPath(simplifiedPath);
- } else {
- overridden = relativeUri.hasQuery();
- }
- if (overridden) {
- absoluteUri.setRawQuery(relativeUri.getRawQuery());
- } else {
- overridden = relativeUri.hasFragment();
- }
- if (overridden) {
- absoluteUri.setRawFragment(relativeUri.getRawFragment());
- }
- return absoluteUri;
- }
- /**
- * a mutable URI.
- *
- * This class contains setters and getters for the parts of the URI.
- * The <tt>getXYZ</tt>/<tt>setXYZ</tt> methods return the decoded part -- so
- * <code>uri.parse('/foo%20bar').getPath()</code> will return the decoded path,
- * <tt>/foo bar</tt>.
- *
- * <p>The raw versions of fields are available too.
- * <code>uri.parse('/foo%20bar').getRawPath()</code> will return the raw path,
- * <tt>/foo%20bar</tt>. Use the raw setters with care, since
- * <code>URI::toString</code> is not guaranteed to return a valid url if a
- * raw setter was used.
- *
- * <p>All setters return <tt>this</tt> and so may be chained, a la
- * <code>uri.parse('/foo').setFragment('part').toString()</code>.
- *
- * <p>You should not use this constructor directly -- please prefer the factory
- * functions {@link uri.parse}, {@link uri.create}, {@link uri.resolve}
- * instead.</p>
- *
- * <p>The parameters are all raw (assumed to be properly escaped) parts, and
- * any (but not all) may be null. Undefined is not allowed.</p>
- *
- * @constructor
- */
- function URI(
- rawScheme,
- rawCredentials, rawDomain, port,
- rawPath, rawQuery, rawFragment) {
- this.scheme_ = rawScheme;
- this.credentials_ = rawCredentials;
- this.domain_ = rawDomain;
- this.port_ = port;
- this.path_ = rawPath;
- this.query_ = rawQuery;
- this.fragment_ = rawFragment;
- /**
- * @type {Array|null}
- */
- this.paramCache_ = null;
- }
- /** returns the string form of the url. */
- URI.prototype.toString = function () {
- var out = [];
- if (null !== this.scheme_) { out.push(this.scheme_, ':'); }
- if (null !== this.domain_) {
- out.push('//');
- if (null !== this.credentials_) { out.push(this.credentials_, '@'); }
- out.push(this.domain_);
- if (null !== this.port_) { out.push(':', this.port_.toString()); }
- }
- if (null !== this.path_) { out.push(this.path_); }
- if (null !== this.query_) { out.push('?', this.query_); }
- if (null !== this.fragment_) { out.push('#', this.fragment_); }
- return out.join('');
- };
- URI.prototype.clone = function () {
- return new URI(this.scheme_, this.credentials_, this.domain_, this.port_,
- this.path_, this.query_, this.fragment_);
- };
- URI.prototype.getScheme = function () {
- // HTML5 spec does not require the scheme to be lowercased but
- // all common browsers except Safari lowercase the scheme.
- return this.scheme_ && decodeURIComponent(this.scheme_).toLowerCase();
- };
- URI.prototype.getRawScheme = function () {
- return this.scheme_;
- };
- URI.prototype.setScheme = function (newScheme) {
- this.scheme_ = encodeIfExists2(
- newScheme, URI_DISALLOWED_IN_SCHEME_OR_CREDENTIALS_);
- return this;
- };
- URI.prototype.setRawScheme = function (newScheme) {
- this.scheme_ = newScheme ? newScheme : null;
- return this;
- };
- URI.prototype.hasScheme = function () {
- return null !== this.scheme_;
- };
- URI.prototype.getCredentials = function () {
- return this.credentials_ && decodeURIComponent(this.credentials_);
- };
- URI.prototype.getRawCredentials = function () {
- return this.credentials_;
- };
- URI.prototype.setCredentials = function (newCredentials) {
- this.credentials_ = encodeIfExists2(
- newCredentials, URI_DISALLOWED_IN_SCHEME_OR_CREDENTIALS_);
- return this;
- };
- URI.prototype.setRawCredentials = function (newCredentials) {
- this.credentials_ = newCredentials ? newCredentials : null;
- return this;
- };
- URI.prototype.hasCredentials = function () {
- return null !== this.credentials_;
- };
- URI.prototype.getDomain = function () {
- return this.domain_ && decodeURIComponent(this.domain_);
- };
- URI.prototype.getRawDomain = function () {
- return this.domain_;
- };
- URI.prototype.setDomain = function (newDomain) {
- return this.setRawDomain(newDomain && encodeURIComponent(newDomain));
- };
- URI.prototype.setRawDomain = function (newDomain) {
- this.domain_ = newDomain ? newDomain : null;
- // Maintain the invariant that paths must start with a slash when the URI
- // is not path-relative.
- return this.setRawPath(this.path_);
- };
- URI.prototype.hasDomain = function () {
- return null !== this.domain_;
- };
- URI.prototype.getPort = function () {
- return this.port_ && decodeURIComponent(this.port_);
- };
- URI.prototype.setPort = function (newPort) {
- if (newPort) {
- newPort = Number(newPort);
- if (newPort !== (newPort & 0xffff)) {
- throw new Error('Bad port number ' + newPort);
- }
- this.port_ = '' + newPort;
- } else {
- this.port_ = null;
- }
- return this;
- };
- URI.prototype.hasPort = function () {
- return null !== this.port_;
- };
- URI.prototype.getPath = function () {
- return this.path_ && decodeURIComponent(this.path_);
- };
- URI.prototype.getRawPath = function () {
- return this.path_;
- };
- URI.prototype.setPath = function (newPath) {
- return this.setRawPath(encodeIfExists2(newPath, URI_DISALLOWED_IN_PATH_));
- };
- URI.prototype.setRawPath = function (newPath) {
- if (newPath) {
- newPath = String(newPath);
- this.path_ =
- // Paths must start with '/' unless this is a path-relative URL.
- (!this.domain_ || /^\//.test(newPath)) ? newPath : '/' + newPath;
- } else {
- this.path_ = null;
- }
- return this;
- };
- URI.prototype.hasPath = function () {
- return null !== this.path_;
- };
- URI.prototype.getQuery = function () {
- // From http://www.w3.org/Addressing/URL/4_URI_Recommentations.html
- // Within the query string, the plus sign is reserved as shorthand notation
- // for a space.
- return this.query_ && decodeURIComponent(this.query_).replace(/\+/g, ' ');
- };
- URI.prototype.getRawQuery = function () {
- return this.query_;
- };
- URI.prototype.setQuery = function (newQuery) {
- this.paramCache_ = null;
- this.query_ = encodeIfExists(newQuery);
- return this;
- };
- URI.prototype.setRawQuery = function (newQuery) {
- this.paramCache_ = null;
- this.query_ = newQuery ? newQuery : null;
- return this;
- };
- URI.prototype.hasQuery = function () {
- return null !== this.query_;
- };
- /**
- * sets the query given a list of strings of the form
- * [ key0, value0, key1, value1, ... ].
- *
- * <p><code>uri.setAllParameters(['a', 'b', 'c', 'd']).getQuery()</code>
- * will yield <code>'a=b&c=d'</code>.
- */
- URI.prototype.setAllParameters = function (params) {
- if (typeof params === 'object') {
- if (!(params instanceof Array)
- && (params instanceof Object
- || Object.prototype.toString.call(params) !== '[object Array]')) {
- var newParams = [];
- var i = -1;
- for (var k in params) {
- var v = params[k];
- if ('string' === typeof v) {
- newParams[++i] = k;
- newParams[++i] = v;
- }
- }
- params = newParams;
- }
- }
- this.paramCache_ = null;
- var queryBuf = [];
- var separator = '';
- for (var j = 0; j < params.length;) {
- var k = params[j++];
- var v = params[j++];
- queryBuf.push(separator, encodeURIComponent(k.toString()));
- separator = '&';
- if (v) {
- queryBuf.push('=', encodeURIComponent(v.toString()));
- }
- }
- this.query_ = queryBuf.join('');
- return this;
- };
- URI.prototype.checkParameterCache_ = function () {
- if (!this.paramCache_) {
- var q = this.query_;
- if (!q) {
- this.paramCache_ = [];
- } else {
- var cgiParams = q.split(/[&\?]/);
- var out = [];
- var k = -1;
- for (var i = 0; i < cgiParams.length; ++i) {
- var m = cgiParams[i].match(/^([^=]*)(?:=(.*))?$/);
- // From http://www.w3.org/Addressing/URL/4_URI_Recommentations.html
- // Within the query string, the plus sign is reserved as shorthand
- // notation for a space.
- out[++k] = decodeURIComponent(m[1]).replace(/\+/g, ' ');
- out[++k] = decodeURIComponent(m[2] || '').replace(/\+/g, ' ');
- }
- this.paramCache_ = out;
- }
- }
- };
- /**
- * sets the values of the named cgi parameters.
- *
- * <p>So, <code>uri.parse('foo?a=b&c=d&e=f').setParameterValues('c', ['new'])
- * </code> yields <tt>foo?a=b&c=new&e=f</tt>.</p>
- *
- * @param key {string}
- * @param values {Array.<string>} the new values. If values is a single string
- * then it will be treated as the sole value.
- */
- URI.prototype.setParameterValues = function (key, values) {
- // be nice and avoid subtle bugs where [] operator on string performs charAt
- // on some browsers and crashes on IE
- if (typeof values === 'string') {
- values = [ values ];
- }
- this.checkParameterCache_();
- var newValueIndex = 0;
- var pc = this.paramCache_;
- var params = [];
- for (var i = 0, k = 0; i < pc.length; i += 2) {
- if (key === pc[i]) {
- if (newValueIndex < values.length) {
- params.push(key, values[newValueIndex++]);
- }
- } else {
- params.push(pc[i], pc[i + 1]);
- }
- }
- while (newValueIndex < values.length) {
- params.push(key, values[newValueIndex++]);
- }
- this.setAllParameters(params);
- return this;
- };
- URI.prototype.removeParameter = function (key) {
- return this.setParameterValues(key, []);
- };
- /**
- * returns the parameters specified in the query part of the uri as a list of
- * keys and values like [ key0, value0, key1, value1, ... ].
- *
- * @return {Array.<string>}
- */
- URI.prototype.getAllParameters = function () {
- this.checkParameterCache_();
- return this.paramCache_.slice(0, this.paramCache_.length);
- };
- /**
- * returns the value<b>s</b> for a given cgi parameter as a list of decoded
- * query parameter values.
- * @return {Array.<string>}
- */
- URI.prototype.getParameterValues = function (paramNameUnescaped) {
- this.checkParameterCache_();
- var values = [];
- for (var i = 0; i < this.paramCache_.length; i += 2) {
- if (paramNameUnescaped === this.paramCache_[i]) {
- values.push(this.paramCache_[i + 1]);
- }
- }
- return values;
- };
- /**
- * returns a map of cgi parameter names to (non-empty) lists of values.
- * @return {Object.<string,Array.<string>>}
- */
- URI.prototype.getParameterMap = function (paramNameUnescaped) {
- this.checkParameterCache_();
- var paramMap = {};
- for (var i = 0; i < this.paramCache_.length; i += 2) {
- var key = this.paramCache_[i++],
- value = this.paramCache_[i++];
- if (!(key in paramMap)) {
- paramMap[key] = [value];
- } else {
- paramMap[key].push(value);
- }
- }
- return paramMap;
- };
- /**
- * returns the first value for a given cgi parameter or null if the given
- * parameter name does not appear in the query string.
- * If the given parameter name does appear, but has no '<tt>=</tt>' following
- * it, then the empty string will be returned.
- * @return {string|null}
- */
- URI.prototype.getParameterValue = function (paramNameUnescaped) {
- this.checkParameterCache_();
- for (var i = 0; i < this.paramCache_.length; i += 2) {
- if (paramNameUnescaped === this.paramCache_[i]) {
- return this.paramCache_[i + 1];
- }
- }
- return null;
- };
- URI.prototype.getFragment = function () {
- return this.fragment_ && decodeURIComponent(this.fragment_);
- };
- URI.prototype.getRawFragment = function () {
- return this.fragment_;
- };
- URI.prototype.setFragment = function (newFragment) {
- this.fragment_ = newFragment ? encodeURIComponent(newFragment) : null;
- return this;
- };
- URI.prototype.setRawFragment = function (newFragment) {
- this.fragment_ = newFragment ? newFragment : null;
- return this;
- };
- URI.prototype.hasFragment = function () {
- return null !== this.fragment_;
- };
- function nullIfAbsent(matchPart) {
- return ('string' == typeof matchPart) && (matchPart.length > 0)
- ? matchPart
- : null;
- }
- /**
- * a regular expression for breaking a URI into its component parts.
- *
- * <p>http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#RFC2234 says
- * As the "first-match-wins" algorithm is identical to the "greedy"
- * disambiguation method used by POSIX regular expressions, it is natural and
- * commonplace to use a regular expression for parsing the potential five
- * components of a URI reference.
- *
- * <p>The following line is the regular expression for breaking-down a
- * well-formed URI reference into its components.
- *
- * <pre>
- * ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
- * 12 3 4 5 6 7 8 9
- * </pre>
- *
- * <p>The numbers in the second line above are only to assist readability; they
- * indicate the reference points for each subexpression (i.e., each paired
- * parenthesis). We refer to the value matched for subexpression <n> as $<n>.
- * For example, matching the above expression to
- * <pre>
- * http://www.ics.uci.edu/pub/ietf/uri/#Related
- * </pre>
- * results in the following subexpression matches:
- * <pre>
- * $1 = http:
- * $2 = http
- * $3 = //www.ics.uci.edu
- * $4 = www.ics.uci.edu
- * $5 = /pub/ietf/uri/
- * $6 = <undefined>
- * $7 = <undefined>
- * $8 = #Related
- * $9 = Related
- * </pre>
- * where <undefined> indicates that the component is not present, as is the
- * case for the query component in the above example. Therefore, we can
- * determine the value of the five components as
- * <pre>
- * scheme = $2
- * authority = $4
- * path = $5
- * query = $7
- * fragment = $9
- * </pre>
- *
- * <p>msamuel: I have modified the regular expression slightly to expose the
- * credentials, domain, and port separately from the authority.
- * The modified version yields
- * <pre>
- * $1 = http scheme
- * $2 = <undefined> credentials -\
- * $3 = www.ics.uci.edu domain | authority
- * $4 = <undefined> port -/
- * $5 = /pub/ietf/uri/ path
- * $6 = <undefined> query without ?
- * $7 = Related fragment without #
- * </pre>
- */
- var URI_RE_ = new RegExp(
- "^" +
- "(?:" +
- "([^:/?#]+)" + // scheme
- ":)?" +
- "(?://" +
- "(?:([^/?#]*)@)?" + // credentials
- "([^/?#:@]*)" + // domain
- "(?::([0-9]+))?" + // port
- ")?" +
- "([^?#]+)?" + // path
- "(?:\\?([^#]*))?" + // query
- "(?:#(.*))?" + // fragment
- "$"
- );
- var URI_DISALLOWED_IN_SCHEME_OR_CREDENTIALS_ = /[#\/\?@]/g;
- var URI_DISALLOWED_IN_PATH_ = /[\#\?]/g;
- URI.parse = parse;
- URI.create = create;
- URI.resolve = resolve;
- URI.collapse_dots = collapse_dots; // Visible for testing.
- // lightweight string-based api for loadModuleMaker
- URI.utils = {
- mimeTypeOf: function (uri) {
- var uriObj = parse(uri);
- if (/\.html$/.test(uriObj.getPath())) {
- return 'text/html';
- } else {
- return 'application/javascript';
- }
- },
- resolve: function (base, uri) {
- if (base) {
- return resolve(parse(base), parse(uri)).toString();
- } else {
- return '' + uri;
- }
- }
- };
- return URI;
- })();
- // Copyright Google Inc.
- // Licensed under the Apache Licence Version 2.0
- // Autogenerated at Mon Feb 25 13:05:42 EST 2013
- // @overrides window
- // @provides html4
- var html4 = {};
- html4.atype = {
- 'NONE': 0,
- 'URI': 1,
- 'URI_FRAGMENT': 11,
- 'SCRIPT': 2,
- 'STYLE': 3,
- 'HTML': 12,
- 'ID': 4,
- 'IDREF': 5,
- 'IDREFS': 6,
- 'GLOBAL_NAME': 7,
- 'LOCAL_NAME': 8,
- 'CLASSES': 9,
- 'FRAME_TARGET': 10,
- 'MEDIA_QUERY': 13
- };
- html4[ 'atype' ] = html4.atype;
- html4.ATTRIBS = {
- '*::class': 9,
- '*::dir': 0,
- '*::draggable': 0,
- '*::hidden': 0,
- '*::id': 4,
- '*::inert': 0,
- '*::itemprop': 0,
- '*::itemref': 6,
- '*::itemscope': 0,
- '*::lang': 0,
- '*::onblur': 2,
- '*::onchange': 2,
- '*::onclick': 2,
- '*::ondblclick': 2,
- '*::onfocus': 2,
- '*::onkeydown': 2,
- '*::onkeypress': 2,
- '*::onkeyup': 2,
- '*::onload': 2,
- '*::onmousedown': 2,
- '*::onmousemove': 2,
- '*::onmouseout': 2,
- '*::onmouseover': 2,
- '*::onmouseup': 2,
- '*::onreset': 2,
- '*::onscroll': 2,
- '*::onselect': 2,
- '*::onsubmit': 2,
- '*::onunload': 2,
- '*::spellcheck': 0,
- '*::style': 3,
- '*::title': 0,
- '*::translate': 0,
- 'a::accesskey': 0,
- 'a::coords': 0,
- 'a::href': 1,
- 'a::hreflang': 0,
- 'a::name': 7,
- 'a::onblur': 2,
- 'a::onfocus': 2,
- 'a::shape': 0,
- 'a::tabindex': 0,
- 'a::target': 10,
- 'a::type': 0,
- 'area::accesskey': 0,
- 'area::alt': 0,
- 'area::coords': 0,
- 'area::href': 1,
- 'area::nohref': 0,
- 'area::onblur': 2,
- 'area::onfocus': 2,
- 'area::shape': 0,
- 'area::tabindex': 0,
- 'area::target': 10,
- 'audio::controls': 0,
- 'audio::loop': 0,
- 'audio::mediagroup': 5,
- 'audio::muted': 0,
- 'audio::preload': 0,
- 'bdo::dir': 0,
- 'blockquote::cite': 1,
- 'br::clear': 0,
- 'button::accesskey': 0,
- 'button::disabled': 0,
- 'button::name': 8,
- 'button::onblur': 2,
- 'button::onfocus': 2,
- 'button::tabindex': 0,
- 'button::type': 0,
- 'button::value': 0,
- 'canvas::height': 0,
- 'canvas::width': 0,
- 'caption::align': 0,
- 'col::align': 0,
- 'col::char': 0,
- 'col::charoff': 0,
- 'col::span': 0,
- 'col::valign': 0,
- 'col::width': 0,
- 'colgroup::align': 0,
- 'colgroup::char': 0,
- 'colgroup::charoff': 0,
- 'colgroup::span': 0,
- 'colgroup::valign': 0,
- 'colgroup::width': 0,
- 'command::checked': 0,
- 'command::command': 5,
- 'command::disabled': 0,
- 'command::icon': 1,
- 'command::label': 0,
- 'command::radiogroup': 0,
- 'command::type': 0,
- 'data::value': 0,
- 'del::cite': 1,
- 'del::datetime': 0,
- 'details::open': 0,
- 'dir::compact': 0,
- 'div::align': 0,
- 'dl::compact': 0,
- 'fieldset::disabled': 0,
- 'font::color': 0,
- 'font::face': 0,
- 'font::size': 0,
- 'form::accept': 0,
- 'form::action': 1,
- 'form::autocomplete': 0,
- 'form::enctype': 0,
- 'form::method': 0,
- 'form::name': 7,
- 'form::novalidate': 0,
- 'form::onreset': 2,
- 'form::onsubmit': 2,
- 'form::target': 10,
- 'h1::align': 0,
- 'h2::align': 0,
- 'h3::align': 0,
- 'h4::align': 0,
- 'h5::align': 0,
- 'h6::align': 0,
- 'hr::align': 0,
- 'hr::noshade': 0,
- 'hr::size': 0,
- 'hr::width': 0,
- 'iframe::align': 0,
- 'iframe::frameborder': 0,
- 'iframe::height': 0,
- 'iframe::marginheight': 0,
- 'iframe::marginwidth': 0,
- 'iframe::width': 0,
- 'img::align': 0,
- 'img::alt': 0,
- 'img::border': 0,
- 'img::height': 0,
- 'img::hspace': 0,
- 'img::ismap': 0,
- 'img::name': 7,
- 'img::src': 1,
- 'img::usemap': 11,
- 'img::vspace': 0,
- 'img::width': 0,
- 'input::accept': 0,
- 'input::accesskey': 0,
- 'input::align': 0,
- 'input::alt': 0,
- 'input::autocomplete': 0,
- 'input::checked': 0,
- 'input::disabled': 0,
- 'input::inputmode': 0,
- 'input::ismap': 0,
- 'input::list': 5,
- 'input::max': 0,
- 'input::maxlength': 0,
- 'input::min': 0,
- 'input::multiple': 0,
- 'input::name': 8,
- 'input::onblur': 2,
- 'input::onchange': 2,
- 'input::onfocus': 2,
- 'input::onselect': 2,
- 'input::placeholder': 0,
- 'input::readonly': 0,
- 'input::required': 0,
- 'input::size': 0,
- 'input::src': 1,
- 'input::step': 0,
- 'input::tabindex': 0,
- 'input::type': 0,
- 'input::usemap': 11,
- 'input::value': 0,
- 'ins::cite': 1,
- 'ins::datetime': 0,
- 'label::accesskey': 0,
- 'label::for': 5,
- 'label::onblur': 2,
- 'label::onfocus': 2,
- 'legend::accesskey': 0,
- 'legend::align': 0,
- 'li::type': 0,
- 'li::value': 0,
- 'map::name': 7,
- 'menu::compact': 0,
- 'menu::label': 0,
- 'menu::type': 0,
- 'meter::high': 0,
- 'meter::low': 0,
- 'meter::max': 0,
- 'meter::min': 0,
- 'meter::value': 0,
- 'ol::compact': 0,
- 'ol::reversed': 0,
- 'ol::start': 0,
- 'ol::type': 0,
- 'optgroup::disabled': 0,
- 'optgroup::label': 0,
- 'option::disabled': 0,
- 'option::label': 0,
- 'option::selected': 0,
- 'option::value': 0,
- 'output::for': 6,
- 'output::name': 8,
- 'p::align': 0,
- 'pre::width': 0,
- 'progress::max': 0,
- 'progress::min': 0,
- 'progress::value': 0,
- 'q::cite': 1,
- 'select::autocomplete': 0,
- 'select::disabled': 0,
- 'select::multiple': 0,
- 'select::name': 8,
- 'select::onblur': 2,
- 'select::onchange': 2,
- 'select::onfocus': 2,
- 'select::required': 0,
- 'select::size': 0,
- 'select::tabindex': 0,
- 'source::type': 0,
- 'table::align': 0,
- 'table::bgcolor': 0,
- 'table::border': 0,
- 'table::cellpadding': 0,
- 'table::cellspacing': 0,
- 'table::frame': 0,
- 'table::rules': 0,
- 'table::summary': 0,
- 'table::width': 0,
- 'tbody::align': 0,
- 'tbody::char': 0,
- 'tbody::charoff': 0,
- 'tbody::valign': 0,
- 'td::abbr': 0,
- 'td::align': 0,
- 'td::axis': 0,
- 'td::bgcolor': 0,
- 'td::char': 0,
- 'td::charoff': 0,
- 'td::colspan': 0,
- 'td::headers': 6,
- 'td::height': 0,
- 'td::nowrap': 0,
- 'td::rowspan': 0,
- 'td::scope': 0,
- 'td::valign': 0,
- 'td::width': 0,
- 'textarea::accesskey': 0,
- 'textarea::autocomplete': 0,
- 'textarea::cols': 0,
- 'textarea::disabled': 0,
- 'textarea::inputmode': 0,
- 'textarea::name': 8,
- 'textarea::onblur': 2,
- 'textarea::onchange': 2,
- 'textarea::onfocus': 2,
- 'textarea::onselect': 2,
- 'textarea::placeholder': 0,
- 'textarea::readonly': 0,
- 'textarea::required': 0,
- 'textarea::rows': 0,
- 'textarea::tabindex': 0,
- 'textarea::wrap': 0,
- 'tfoot::align': 0,
- 'tfoot::char': 0,
- 'tfoot::charoff': 0,
- 'tfoot::valign': 0,
- 'th::abbr': 0,
- 'th::align': 0,
- 'th::axis': 0,
- 'th::bgcolor': 0,
- 'th::char': 0,
- 'th::charoff': 0,
- 'th::colspan': 0,
- 'th::headers': 6,
- 'th::height': 0,
- 'th::nowrap': 0,
- 'th::rowspan': 0,
- 'th::scope': 0,
- 'th::valign': 0,
- 'th::width': 0,
- 'thead::align': 0,
- 'thead::char': 0,
- 'thead::charoff': 0,
- 'thead::valign': 0,
- 'tr::align': 0,
- 'tr::bgcolor': 0,
- 'tr::char': 0,
- 'tr::charoff': 0,
- 'tr::valign': 0,
- 'track::default': 0,
- 'track::kind': 0,
- 'track::label': 0,
- 'track::srclang': 0,
- 'ul::compact': 0,
- 'ul::type': 0,
- 'video::controls': 0,
- 'video::height': 0,
- 'video::loop': 0,
- 'video::mediagroup': 5,
- 'video::muted': 0,
- 'video::poster': 1,
- 'video::preload': 0,
- 'video::width': 0
- };
- html4[ 'ATTRIBS' ] = html4.ATTRIBS;
- html4.eflags = {
- 'OPTIONAL_ENDTAG': 1,
- 'EMPTY': 2,
- 'CDATA': 4,
- 'RCDATA': 8,
- 'UNSAFE': 16,
- 'FOLDABLE': 32,
- 'SCRIPT': 64,
- 'STYLE': 128,
- 'VIRTUALIZED': 256
- };
- html4[ 'eflags' ] = html4.eflags;
- // these are bitmasks of the eflags above.
- html4.ELEMENTS = {
- 'a': 0,
- 'abbr': 0,
- 'acronym': 0,
- 'address': 0,
- 'applet': 272,
- 'area': 2,
- 'article': 0,
- 'aside': 0,
- 'audio': 0,
- 'b': 0,
- 'base': 274,
- 'basefont': 274,
- 'bdi': 0,
- 'bdo': 0,
- 'big': 0,
- 'blockquote': 0,
- 'body': 305,
- 'br': 2,
- 'button': 0,
- 'canvas': 0,
- 'caption': 0,
- 'center': 0,
- 'cite': 0,
- 'code': 0,
- 'col': 2,
- 'colgroup': 1,
- 'command': 2,
- 'data': 0,
- 'datalist': 0,
- 'dd': 1,
- 'del': 0,
- 'details': 0,
- 'dfn': 0,
- 'dialog': 272,
- 'dir': 0,
- 'div': 0,
- 'dl': 0,
- 'dt': 1,
- 'em': 0,
- 'fieldset': 0,
- 'figcaption': 0,
- 'figure': 0,
- 'font': 0,
- 'footer': 0,
- 'form': 0,
- 'frame': 274,
- 'frameset': 272,
- 'h1': 0,
- 'h2': 0,
- 'h3': 0,
- 'h4': 0,
- 'h5': 0,
- 'h6': 0,
- 'head': 305,
- 'header': 0,
- 'hgroup': 0,
- 'hr': 2,
- 'html': 305,
- 'i': 0,
- 'iframe': 16,
- 'img': 2,
- 'input': 2,
- 'ins': 0,
- 'isindex': 274,
- 'kbd': 0,
- 'keygen': 274,
- 'label': 0,
- 'legend': 0,
- 'li': 1,
- 'link': 274,
- 'map': 0,
- 'mark': 0,
- 'menu': 0,
- 'meta': 274,
- 'meter': 0,
- 'nav': 0,
- 'nobr': 0,
- 'noembed': 276,
- 'noframes': 276,
- 'noscript': 276,
- 'object': 272,
- 'ol': 0,
- 'optgroup': 0,
- 'option': 1,
- 'output': 0,
- 'p': 1,
- 'param': 274,
- 'pre': 0,
- 'progress': 0,
- 'q': 0,
- 's': 0,
- 'samp': 0,
- 'script': 84,
- 'section': 0,
- 'select': 0,
- 'small': 0,
- 'source': 2,
- 'span': 0,
- 'strike': 0,
- 'strong': 0,
- 'style': 148,
- 'sub': 0,
- 'summary': 0,
- 'sup': 0,
- 'table': 0,
- 'tbody': 1,
- 'td': 1,
- 'textarea': 8,
- 'tfoot': 1,
- 'th': 1,
- 'thead': 1,
- 'time': 0,
- 'title': 280,
- 'tr': 1,
- 'track': 2,
- 'tt': 0,
- 'u': 0,
- 'ul': 0,
- 'var': 0,
- 'video': 0,
- 'wbr': 2
- };
- html4[ 'ELEMENTS' ] = html4.ELEMENTS;
- html4.ELEMENT_DOM_INTERFACES = {
- 'a': 'HTMLAnchorElement',
- 'abbr': 'HTMLElement',
- 'acronym': 'HTMLElement',
- 'address': 'HTMLElement',
- 'applet': 'HTMLAppletElement',
- 'area': 'HTMLAreaElement',
- 'article': 'HTMLElement',
- 'aside': 'HTMLElement',
- 'audio': 'HTMLAudioElement',
- 'b': 'HTMLElement',
- 'base': 'HTMLBaseElement',
- 'basefont': 'HTMLBaseFontElement',
- 'bdi': 'HTMLElement',
- 'bdo': 'HTMLElement',
- 'big': 'HTMLElement',
- 'blockquote': 'HTMLQuoteElement',
- 'body': 'HTMLBodyElement',
- 'br': 'HTMLBRElement',
- 'button': 'HTMLButtonElement',
- 'canvas': 'HTMLCanvasElement',
- 'caption': 'HTMLTableCaptionElement',
- 'center': 'HTMLElement',
- 'cite': 'HTMLElement',
- 'code': 'HTMLElement',
- 'col': 'HTMLTableColElement',
- 'colgroup': 'HTMLTableColElement',
- 'command': 'HTMLCommandElement',
- 'data': 'HTMLElement',
- 'datalist': 'HTMLDataListElement',
- 'dd': 'HTMLElement',
- 'del': 'HTMLModElement',
- 'details': 'HTMLDetailsElement',
- 'dfn': 'HTMLElement',
- 'dialog': 'HTMLDialogElement',
- 'dir': 'HTMLDirectoryElement',
- 'div': 'HTMLDivElement',
- 'dl': 'HTMLDListElement',
- 'dt': 'HTMLElement',
- 'em': 'HTMLElement',
- 'fieldset': 'HTMLFieldSetElement',
- 'figcaption': 'HTMLElement',
- 'figure': 'HTMLElement',
- 'font': 'HTMLFontElement',
- 'footer': 'HTMLElement',
- 'form': 'HTMLFormElement',
- 'frame': 'HTMLFrameElement',
- 'frameset': 'HTMLFrameSetElement',
- 'h1': 'HTMLHeadingElement',
- 'h2': 'HTMLHeadingElement',
- 'h3': 'HTMLHeadingElement',
- 'h4': 'HTMLHeadingElement',
- 'h5': 'HTMLHeadingElement',
- 'h6': 'HTMLHeadingElement',
- 'head': 'HTMLHeadElement',
- 'header': 'HTMLElement',
- 'hgroup': 'HTMLElement',
- 'hr': 'HTMLHRElement',
- 'html': 'HTMLHtmlElement',
- 'i': 'HTMLElement',
- 'iframe': 'HTMLIFrameElement',
- 'img': 'HTMLImageElement',
- 'input': 'HTMLInputElement',
- 'ins': 'HTMLModElement',
- 'isindex': 'HTMLUnknownElement',
- 'kbd': 'HTMLElement',
- 'keygen': 'HTMLKeygenElement',
- 'label': 'HTMLLabelElement',
- 'legend': 'HTMLLegendElement',
- 'li': 'HTMLLIElement',
- 'link': 'HTMLLinkElement',
- 'map': 'HTMLMapElement',
- 'mark': 'HTMLElement',
- 'menu': 'HTMLMenuElement',
- 'meta': 'HTMLMetaElement',
- 'meter': 'HTMLMeterElement',
- 'nav': 'HTMLElement',
- 'nobr': 'HTMLElement',
- 'noembed': 'HTMLElement',
- 'noframes': 'HTMLElement',
- 'noscript': 'HTMLElement',
- 'object': 'HTMLObjectElement',
- 'ol': 'HTMLOListElement',
- 'optgroup': 'HTMLOptGroupElement',
- 'option': 'HTMLOptionElement',
- 'output': 'HTMLOutputElement',
- 'p': 'HTMLParagraphElement',
- 'param': 'HTMLParamElement',
- 'pre': 'HTMLPreElement',
- 'progress': 'HTMLProgressElement',
- 'q': 'HTMLQuoteElement',
- 's': 'HTMLElement',
- 'samp': 'HTMLElement',
- 'script': 'HTMLScriptElement',
- 'section': 'HTMLElement',
- 'select': 'HTMLSelectElement',
- 'small': 'HTMLElement',
- 'source': 'HTMLSourceElement',
- 'span': 'HTMLSpanElement',
- 'strike': 'HTMLElement',
- 'strong': 'HTMLElement',
- 'style': 'HTMLStyleElement',
- 'sub': 'HTMLElement',
- 'summary': 'HTMLElement',
- 'sup': 'HTMLElement',
- 'table': 'HTMLTableElement',
- 'tbody': 'HTMLTableSectionElement',
- 'td': 'HTMLTableDataCellElement',
- 'textarea': 'HTMLTextAreaElement',
- 'tfoot': 'HTMLTableSectionElement',
- 'th': 'HTMLTableHeaderCellElement',
- 'thead': 'HTMLTableSectionElement',
- 'time': 'HTMLTimeElement',
- 'title': 'HTMLTitleElement',
- 'tr': 'HTMLTableRowElement',
- 'track': 'HTMLTrackElement',
- 'tt': 'HTMLElement',
- 'u': 'HTMLElement',
- 'ul': 'HTMLUListElement',
- 'var': 'HTMLElement',
- 'video': 'HTMLVideoElement',
- 'wbr': 'HTMLElement'
- };
- html4[ 'ELEMENT_DOM_INTERFACES' ] = html4.ELEMENT_DOM_INTERFACES;
- html4.ueffects = {
- 'NOT_LOADED': 0,
- 'SAME_DOCUMENT': 1,
- 'NEW_DOCUMENT': 2
- };
- html4[ 'ueffects' ] = html4.ueffects;
- html4.URIEFFECTS = {
- 'a::href': 2,
- 'area::href': 2,
- 'blockquote::cite': 0,
- 'command::icon': 1,
- 'del::cite': 0,
- 'form::action': 2,
- 'img::src': 1,
- 'input::src': 1,
- 'ins::cite': 0,
- 'q::cite': 0,
- 'video::poster': 1
- };
- html4[ 'URIEFFECTS' ] = html4.URIEFFECTS;
- html4.ltypes = {
- 'UNSANDBOXED': 2,
- 'SANDBOXED': 1,
- 'DATA': 0
- };
- html4[ 'ltypes' ] = html4.ltypes;
- html4.LOADERTYPES = {
- 'a::href': 2,
- 'area::href': 2,
- 'blockquote::cite': 2,
- 'command::icon': 1,
- 'del::cite': 2,
- 'form::action': 2,
- 'img::src': 1,
- 'input::src': 1,
- 'ins::cite': 2,
- 'q::cite': 2,
- 'video::poster': 1
- };
- html4[ 'LOADERTYPES' ] = html4.LOADERTYPES;
- // Copyright (C) 2006 Google Inc.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- /**
- * @fileoverview
- * An HTML sanitizer that can satisfy a variety of security policies.
- *
- * <p>
- * The HTML sanitizer is built around a SAX parser and HTML element and
- * attributes schemas.
- *
- * If the cssparser is loaded, inline styles are sanitized using the
- * css property and value schemas. Else they are remove during
- * sanitization.
- *
- * If it exists, uses parseCssDeclarations, sanitizeCssProperty, cssSchema
- *
- * @author mikesamuel@gmail.com
- * @author jasvir@gmail.com
- * \@requires html4, URI
- * \@overrides window
- * \@provides html, html_sanitize
- */
- // The Turkish i seems to be a non-issue, but abort in case it is.
- if ('I'.toLowerCase() !== 'i') { throw 'I/i problem'; }
- /**
- * \@namespace
- */
- var html = (function(html4) {
- // For closure compiler
- var parseCssDeclarations, sanitizeCssProperty, cssSchema;
- if ('undefined' !== typeof window) {
- parseCssDeclarations = window['parseCssDeclarations'];
- sanitizeCssProperty = window['sanitizeCssProperty'];
- cssSchema = window['cssSchema'];
- }
- // The keys of this object must be 'quoted' or JSCompiler will mangle them!
- // This is a partial list -- lookupEntity() uses the host browser's parser
- // (when available) to implement full entity lookup.
- // Note that entities are in general case-sensitive; the uppercase ones are
- // explicitly defined by HTML5 (presumably as compatibility).
- var ENTITIES = {
- 'lt': '<',
- 'LT': '<',
- 'gt': '>',
- 'GT': '>',
- 'amp': '&',
- 'AMP': '&',
- 'quot': '"',
- 'apos': '\'',
- 'nbsp': '\u00A0'
- };
- // Patterns for types of entity/character reference names.
- var decimalEscapeRe = /^#(\d+)$/;
- var hexEscapeRe = /^#x([0-9A-Fa-f]+)$/;
- // contains every entity per http://www.w3.org/TR/2011/WD-html5-20110113/named-character-references.html
- var safeEntityNameRe = /^[A-Za-z][A-za-z0-9]+$/;
- // Used as a hook to invoke the browser's entity parsing. <textarea> is used
- // because its content is parsed for entities but not tags.
- // TODO(kpreid): This retrieval is a kludge and leads to silent loss of
- // functionality if the document isn't available.
- var entityLookupElement =
- ('undefined' !== typeof window && window['document'])
- ? window['document'].createElement('textarea') : null;
- /**
- * Decodes an HTML entity.
- *
- * {\@updoc
- * $ lookupEntity('lt')
- * # '<'
- * $ lookupEntity('GT')
- * # '>'
- * $ lookupEntity('amp')
- * # '&'
- * $ lookupEntity('nbsp')
- * # '\xA0'
- * $ lookupEntity('apos')
- * # "'"
- * $ lookupEntity('quot')
- * # '"'
- * $ lookupEntity('#xa')
- * # '\n'
- * $ lookupEntity('#10')
- * # '\n'
- * $ lookupEntity('#x0a')
- * # '\n'
- * $ lookupEntity('#010')
- * # '\n'
- * $ lookupEntity('#x00A')
- * # '\n'
- * $ lookupEntity('Pi') // Known failure
- * # '\u03A0'
- * $ lookupEntity('pi') // Known failure
- * # '\u03C0'
- * }
- *
- * @param {string} name the content between the '&' and the ';'.
- * @return {string} a single unicode code-point as a string.
- */
- function lookupEntity(name) {
- // TODO: entity lookup as specified by HTML5 actually depends on the
- // presence of the ";".
- if (ENTITIES.hasOwnProperty(name)) { return ENTITIES[name]; }
- var m = name.match(decimalEscapeRe);
- if (m) {
- return String.fromCharCode(parseInt(m[1], 10));
- } else if (!!(m = name.match(hexEscapeRe))) {
- return String.fromCharCode(parseInt(m[1], 16));
- } else if (entityLookupElement && safeEntityNameRe.test(name)) {
- entityLookupElement.innerHTML = '&' + name + ';';
- var text = entityLookupElement.textContent;
- ENTITIES[name] = text;
- return text;
- } else {
- return '&' + name + ';';
- }
- }
- function decodeOneEntity(_, name) {
- return lookupEntity(name);
- }
- var nulRe = /\0/g;
- function stripNULs(s) {
- return s.replace(nulRe, '');
- }
- var ENTITY_RE_1 = /&(#[0-9]+|#[xX][0-9A-Fa-f]+|\w+);/g;
- var ENTITY_RE_2 = /^(#[0-9]+|#[xX][0-9A-Fa-f]+|\w+);/;
- /**
- * The plain text of a chunk of HTML CDATA which possibly containing.
- *
- * {\@updoc
- * $ unescapeEntities('')
- * # ''
- * $ unescapeEntities('hello World!')
- * # 'hello World!'
- * $ unescapeEntities('1 < 2 && 4 > 3 ')
- * # '1 < 2 && 4 > 3\n'
- * $ unescapeEntities('<< <- unfinished entity>')
- * # '<< <- unfinished entity>'
- * $ unescapeEntities('/foo?bar=baz©=true') // & often unescaped in URLS
- * # '/foo?bar=baz©=true'
- * $ unescapeEntities('pi=ππ, Pi=Π\u03A0') // FIXME: known failure
- * # 'pi=\u03C0\u03c0, Pi=\u03A0\u03A0'
- * }
- *
- * @param {string} s a chunk of HTML CDATA. It must not start or end inside
- * an HTML entity.
- */
- function unescapeEntities(s) {
- return s.replace(ENTITY_RE_1, decodeOneEntity);
- }
- var ampRe = /&/g;
- var looseAmpRe = /&([^a-z#]|#(?:[^0-9x]|x(?:[^0-9a-f]|$)|$)|$)/gi;
- var ltRe = /[<]/g;
- var gtRe = />/g;
- var quotRe = /\"/g;
- /**
- * Escapes HTML special characters in attribute values.
- *
- * {\@updoc
- * $ escapeAttrib('')
- * # ''
- * $ escapeAttrib('"<<&==&>>"') // Do not just escape the first occurrence.
- * # '"<<&==&>>"'
- * $ escapeAttrib('Hello <World>!')
- * # 'Hello <World>!'
- * }
- */
- function escapeAttrib(s) {
- return ('' + s).replace(ampRe, '&').replace(ltRe, '<')
- .replace(gtRe, '>').replace(quotRe, '"');
- }
- /**
- * Escape entities in RCDATA that can be escaped without changing the meaning.
- * {\@updoc
- * $ normalizeRCData('1 < 2 && 3 > 4 && 5 < 7&8')
- * # '1 < 2 && 3 > 4 && 5 < 7&8'
- * }
- */
- function normalizeRCData(rcdata) {
- return rcdata
- .replace(looseAmpRe, '&$1')
- .replace(ltRe, '<')
- .replace(gtRe, '>');
- }
- // TODO(felix8a): validate sanitizer regexs against the HTML5 grammar at
- // http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html
- // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html
- // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html
- // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html
- // We initially split input so that potentially meaningful characters
- // like '<' and '>' are separate tokens, using a fast dumb process that
- // ignores quoting. Then we walk that token stream, and when we see a
- // '<' that's the start of a tag, we use ATTR_RE to extract tag
- // attributes from the next token. That token will never have a '>'
- // character. However, it might have an unbalanced quote character, and
- // when we see that, we combine additional tokens to balance the quote.
- var ATTR_RE = new RegExp(
- '^\\s*' +
- '([-.:\\w]+)' + // 1 = Attribute name
- '(?:' + (
- '\\s*(=)\\s*' + // 2 = Is there a value?
- '(' + ( // 3 = Attribute value
- // TODO(felix8a): maybe use backref to match quotes
- '(\")[^\"]*(\"|$)' + // 4, 5 = Double-quoted string
- '|' +
- '(\')[^\']*(\'|$)' + // 6, 7 = Single-quoted string
- '|' +
- // Positive lookahead to prevent interpretation of
- // <foo a= b=c> as <foo a='b=c'>
- // TODO(felix8a): might be able to drop this case
- '(?=[a-z][-\\w]*\\s*=)' +
- '|' +
- // Unquoted value that isn't an attribute name
- // (since we didn't match the positive lookahead above)
- '[^\"\'\\s]*' ) +
- ')' ) +
- ')?',
- 'i');
- // false on IE<=8, true on most other browsers
- var splitWillCapture = ('a,b'.split(/(,)/).length === 3);
- // bitmask for tags with special parsing, like <script> and <textarea>
- var EFLAGS_TEXT = html4.eflags['CDATA'] | html4.eflags['RCDATA'];
- /**
- * Given a SAX-like event handler, produce a function that feeds those
- * events and a parameter to the event handler.
- *
- * The event handler has the form:{@code
- * {
- * // Name is an upper-case HTML tag name. Attribs is an array of
- * // alternating upper-case attribute names, and attribute values. The
- * // attribs array is reused by the parser. Param is the value passed to
- * // the saxParser.
- * startTag: function (name, attribs, param) { ... },
- * endTag: function (name, param) { ... },
- * pcdata: function (text, param) { ... },
- * rcdata: function (text, param) { ... },
- * cdata: function (text, param) { ... },
- * startDoc: function (param) { ... },
- * endDoc: function (param) { ... }
- * }}
- *
- * @param {Object} handler a record containing event handlers.
- * @return {function(string, Object)} A function that takes a chunk of HTML
- * and a parameter. The parameter is passed on to the handler methods.
- */
- function makeSaxParser(handler) {
- // Accept quoted or unquoted keys (Closure compat)
- var hcopy = {
- cdata: handler.cdata || handler['cdata'],
- comment: handler.comment || handler['comment'],
- endDoc: handler.endDoc || handler['endDoc'],
- endTag: handler.endTag || handler['endTag'],
- pcdata: handler.pcdata || handler['pcdata'],
- rcdata: handler.rcdata || handler['rcdata'],
- startDoc: handler.startDoc || handler['startDoc'],
- startTag: handler.startTag || handler['startTag']
- };
- return function(htmlText, param) {
- return parse(htmlText, hcopy, param);
- };
- }
- // Parsing strategy is to split input into parts that might be lexically
- // meaningful (every ">" becomes a separate part), and then recombine
- // parts if we discover they're in a different context.
- // TODO(felix8a): Significant performance regressions from -legacy,
- // tested on
- // Chrome 18.0
- // Firefox 11.0
- // IE 6, 7, 8, 9
- // Opera 11.61
- // Safari 5.1.3
- // Many of these are unusual patterns that are linearly slower and still
- // pretty fast (eg 1ms to 5ms), so not necessarily worth fixing.
- // TODO(felix8a): "<script> && && && ... <\/script>" is slower on all
- // browsers. The hotspot is htmlSplit.
- // TODO(felix8a): "<p title='>>>>...'><\/p>" is slower on all browsers.
- // This is partly htmlSplit, but the hotspot is parseTagAndAttrs.
- // TODO(felix8a): "<a><\/a><a><\/a>..." is slower on IE9.
- // "<a>1<\/a><a>1<\/a>..." is faster, "<a><\/a>2<a><\/a>2..." is faster.
- // TODO(felix8a): "<p<p<p..." is slower on IE[6-8]
- var continuationMarker = {};
- function parse(htmlText, handler, param) {
- var m, p, tagName;
- var parts = htmlSplit(htmlText);
- var state = {
- noMoreGT: false,
- noMoreEndComments: false
- };
- parseCPS(handler, parts, 0, state, param);
- }
- function continuationMaker(h, parts, initial, state, param) {
- return function () {
- parseCPS(h, parts, initial, state, param);
- };
- }
- function parseCPS(h, parts, initial, state, param) {
- try {
- if (h.startDoc && initial == 0) { h.startDoc(param); }
- var m, p, tagName;
- for (var pos = initial, end = parts.length; pos < end;) {
- var current = parts[pos++];
- var next = parts[pos];
- switch (current) {
- case '&':
- if (ENTITY_RE_2.test(next)) {
- if (h.pcdata) {
- h.pcdata('&' + next, param, continuationMarker,
- continuationMaker(h, parts, pos, state, param));
- }
- pos++;
- } else {
- if (h.pcdata) { h.pcdata("&", param, continuationMarker,
- continuationMaker(h, parts, pos, state, param));
- }
- }
- break;
- case '<\/':
- if (m = /^([-\w:]+)[^\'\"]*/.exec(next)) {
- if (m[0].length === next.length && parts[pos + 1] === '>') {
- // fast case, no attribute parsing needed
- pos += 2;
- tagName = m[1].toLowerCase();
- if (h.endTag) {
- h.endTag(tagName, param, continuationMarker,
- continuationMaker(h, parts, pos, state, param));
- }
- } else {
- // slow case, need to parse attributes
- // TODO(felix8a): do we really care about misparsing this?
- pos = parseEndTag(
- parts, pos, h, param, continuationMarker, state);
- }
- } else {
- if (h.pcdata) {
- h.pcdata('</', param, continuationMarker,
- continuationMaker(h, parts, pos, state, param));
- }
- }
- break;
- case '<':
- if (m = /^([-\w:]+)\s*\/?/.exec(next)) {
- if (m[0].length === next.length && parts[pos + 1] === '>') {
- // fast case, no attribute parsing needed
- pos += 2;
- tagName = m[1].toLowerCase();
- if (h.startTag) {
- h.startTag(tagName, [], param, continuationMarker,
- continuationMaker(h, parts, pos, state, param));
- }
- // tags like <script> and <textarea> have special parsing
- var eflags = html4.ELEMENTS[tagName];
- if (eflags & EFLAGS_TEXT) {
- var tag = { name: tagName, next: pos, eflags: eflags };
- pos = parseText(
- parts, tag, h, param, continuationMarker, state);
- }
- } else {
- // slow case, need to parse attributes
- pos = parseStartTag(
- parts, pos, h, param, continuationMarker, state);
- }
- } else {
- if (h.pcdata) {
- h.pcdata('<', param, continuationMarker,
- continuationMaker(h, parts, pos, state, param));
- }
- }
- break;
- case '<\!--':
- // The pathological case is n copies of '<\!--' without '-->', and
- // repeated failure to find '-->' is quadratic. We avoid that by
- // remembering when search for '-->' fails.
- if (!state.noMoreEndComments) {
- // A comment <\!--x--> is split into three tokens:
- // '<\!--', 'x--', '>'
- // We want to find the next '>' token that has a preceding '--'.
- // pos is at the 'x--'.
- for (p = pos + 1; p < end; p++) {
- if (parts[p] === '>' && /--$/.test(parts[p - 1])) { break; }
- }
- if (p < end) {
- if (h.comment) {
- var comment = parts.slice(pos, p).join('');
- h.comment(
- comment.substr(0, comment.length - 2), param,
- continuationMarker,
- continuationMaker(h, parts, p + 1, state, param));
- }
- pos = p + 1;
- } else {
- state.noMoreEndComments = true;
- }
- }
- if (state.noMoreEndComments) {
- if (h.pcdata) {
- h.pcdata('<!--', param, continuationMarker,
- continuationMaker(h, parts, pos, state, param));
- }
- }
- break;
- case '<\!':
- if (!/^\w/.test(next)) {
- if (h.pcdata) {
- h.pcdata('<!', param, continuationMarker,
- continuationMaker(h, parts, pos, state, param));
- }
- } else {
- // similar to noMoreEndComment logic
- if (!state.noMoreGT) {
- for (p = pos + 1; p < end; p++) {
- if (parts[p] === '>') { break; }
- }
- if (p < end) {
- pos = p + 1;
- } else {
- state.noMoreGT = true;
- }
- }
- if (state.noMoreGT) {
- if (h.pcdata) {
- h.pcdata('<!', param, continuationMarker,
- continuationMaker(h, parts, pos, state, param));
- }
- }
- }
- break;
- case '<?':
- // similar to noMoreEndComment logic
- if (!state.noMoreGT) {
- for (p = pos + 1; p < end; p++) {
- if (parts[p] === '>') { break; }
- }
- if (p < end) {
- pos = p + 1;
- } else {
- state.noMoreGT = true;
- }
- }
- if (state.noMoreGT) {
- if (h.pcdata) {
- h.pcdata('<?', param, continuationMarker,
- continuationMaker(h, parts, pos, state, param));
- }
- }
- break;
- case '>':
- if (h.pcdata) {
- h.pcdata(">", param, continuationMarker,
- continuationMaker(h, parts, pos, state, param));
- }
- break;
- case '':
- break;
- default:
- if (h.pcdata) {
- h.pcdata(current, param, continuationMarker,
- continuationMaker(h, parts, pos, state, param));
- }
- break;
- }
- }
- if (h.endDoc) { h.endDoc(param); }
- } catch (e) {
- if (e !== continuationMarker) { throw e; }
- }
- }
- // Split str into parts for the html parser.
- function htmlSplit(str) {
- // can't hoist this out of the function because of the re.exec loop.
- var re = /(<\/|<\!--|<[!?]|[&<>])/g;
- str += '';
- if (splitWillCapture) {
- return str.split(re);
- } else {
- var parts = [];
- var lastPos = 0;
- var m;
- while ((m = re.exec(str)) !== null) {
- parts.push(str.substring(lastPos, m.index));
- parts.push(m[0]);
- lastPos = m.index + m[0].length;
- }
- parts.push(str.substring(lastPos));
- return parts;
- }
- }
- function parseEndTag(parts, pos, h, param, continuationMarker, state) {
- var tag = parseTagAndAttrs(parts, pos);
- // drop unclosed tags
- if (!tag) { return parts.length; }
- if (h.endTag) {
- h.endTag(tag.name, param, continuationMarker,
- continuationMaker(h, parts, pos, state, param));
- }
- return tag.next;
- }
- function parseStartTag(parts, pos, h, param, continuationMarker, state) {
- var tag = parseTagAndAttrs(parts, pos);
- // drop unclosed tags
- if (!tag) { return parts.length; }
- if (h.startTag) {
- h.startTag(tag.name, tag.attrs, param, continuationMarker,
- continuationMaker(h, parts, tag.next, state, param));
- }
- // tags like <script> and <textarea> have special parsing
- if (tag.eflags & EFLAGS_TEXT) {
- return parseText(parts, tag, h, param, continuationMarker, state);
- } else {
- return tag.next;
- }
- }
- var endTagRe = {};
- // Tags like <script> and <textarea> are flagged as CDATA or RCDATA,
- // which means everything is text until we see the correct closing tag.
- function parseText(parts, tag, h, param, continuationMarker, state) {
- var end = parts.length;
- if (!endTagRe.hasOwnProperty(tag.name)) {
- endTagRe[tag.name] = new RegExp('^' + tag.name + '(?:[\\s\\/]|$)', 'i');
- }
- var re = endTagRe[tag.name];
- var first = tag.next;
- var p = tag.next + 1;
- for (; p < end; p++) {
- if (parts[p - 1] === '<\/' && re.test(parts[p])) { break; }
- }
- if (p < end) { p -= 1; }
- var buf = parts.slice(first, p).join('');
- if (tag.eflags & html4.eflags['CDATA']) {
- if (h.cdata) {
- h.cdata(buf, param, continuationMarker,
- continuationMaker(h, parts, p, state, param));
- }
- } else if (tag.eflags & html4.eflags['RCDATA']) {
- if (h.rcdata) {
- h.rcdata(normalizeRCData(buf), param, continuationMarker,
- continuationMaker(h, parts, p, state, param));
- }
- } else {
- throw new Error('bug');
- }
- return p;
- }
- // at this point, parts[pos-1] is either "<" or "<\/".
- function parseTagAndAttrs(parts, pos) {
- var m = /^([-\w:]+)/.exec(parts[pos]);
- var tag = {};
- tag.name = m[1].toLowerCase();
- tag.eflags = html4.ELEMENTS[tag.name];
- var buf = parts[pos].substr(m[0].length);
- // Find the next '>'. We optimistically assume this '>' is not in a
- // quoted context, and further down we fix things up if it turns out to
- // be quoted.
- var p = pos + 1;
- var end = parts.length;
- for (; p < end; p++) {
- if (parts[p] === '>') { break; }
- buf += parts[p];
- }
- if (end <= p) { return void 0; }
- var attrs = [];
- while (buf !== '') {
- m = ATTR_RE.exec(buf);
- if (!m) {
- // No attribute found: skip garbage
- buf = buf.replace(/^[\s\S][^a-z\s]*/, '');
- } else if ((m[4] && !m[5]) || (m[6] && !m[7])) {
- // Unterminated quote: slurp to the next unquoted '>'
- var quote = m[4] || m[6];
- var sawQuote = false;
- var abuf = [buf, parts[p++]];
- for (; p < end; p++) {
- if (sawQuote) {
- if (parts[p] === '>') { break; }
- } else if (0 <= parts[p].indexOf(quote)) {
- sawQuote = true;
- }
- abuf.push(parts[p]);
- }
- // Slurp failed: lose the garbage
- if (end <= p) { break; }
- // Otherwise retry attribute parsing
- buf = abuf.join('');
- continue;
- } else {
- // We have an attribute
- var aName = m[1].toLowerCase();
- var aValue = m[2] ? decodeValue(m[3]) : '';
- attrs.push(aName, aValue);
- buf = buf.substr(m[0].length);
- }
- }
- tag.attrs = attrs;
- tag.next = p + 1;
- return tag;
- }
- function decodeValue(v) {
- var q = v.charCodeAt(0);
- if (q === 0x22 || q === 0x27) { // " or '
- v = v.substr(1, v.length - 2);
- }
- return unescapeEntities(stripNULs(v));
- }
- /**
- * Returns a function that strips unsafe tags and attributes from html.
- * @param {function(string, Array.<string>): ?Array.<string>} tagPolicy
- * A function that takes (tagName, attribs[]), where tagName is a key in
- * html4.ELEMENTS and attribs is an array of alternating attribute names
- * and values. It should return a record (as follows), or null to delete
- * the element. It's okay for tagPolicy to modify the attribs array,
- * but the same array is reused, so it should not be held between calls.
- * Record keys:
- * attribs: (required) Sanitized attributes array.
- * tagName: Replacement tag name.
- * @return {function(string, Array)} A function that sanitizes a string of
- * HTML and appends result strings to the second argument, an array.
- */
- function makeHtmlSanitizer(tagPolicy) {
- var stack;
- var ignoring;
- var emit = function (text, out) {
- if (!ignoring) { out.push(text); }
- };
- return makeSaxParser({
- 'startDoc': function(_) {
- stack = [];
- ignoring = false;
- },
- 'startTag': function(tagNameOrig, attribs, out) {
- if (ignoring) { return; }
- if (!html4.ELEMENTS.hasOwnProperty(tagNameOrig)) { return; }
- var eflagsOrig = html4.ELEMENTS[tagNameOrig];
- if (eflagsOrig & html4.eflags['FOLDABLE']) {
- return;
- }
- var decision = tagPolicy(tagNameOrig, attribs);
- if (!decision) {
- ignoring = !(eflagsOrig & html4.eflags['EMPTY']);
- return;
- } else if (typeof decision !== 'object') {
- throw new Error('tagPolicy did not return object (old API?)');
- }
- if ('attribs' in decision) {
- attribs = decision['attribs'];
- } else {
- throw new Error('tagPolicy gave no attribs');
- }
- var eflagsRep;
- var tagNameRep;
- if ('tagName' in decision) {
- tagNameRep = decision['tagName'];
- eflagsRep = html4.ELEMENTS[tagNameRep];
- } else {
- tagNameRep = tagNameOrig;
- eflagsRep = eflagsOrig;
- }
- // TODO(mikesamuel): relying on tagPolicy not to insert unsafe
- // attribute names.
- // If this is an optional-end-tag element and either this element or its
- // previous like sibling was rewritten, then insert a close tag to
- // preserve structure.
- if (eflagsOrig & html4.eflags['OPTIONAL_ENDTAG']) {
- var onStack = stack[stack.length - 1];
- if (onStack && onStack.orig === tagNameOrig &&
- (onStack.rep !== tagNameRep || tagNameOrig !== tagNameRep)) {
- out.push('<\/', onStack.rep, '>');
- }
- }
- if (!(eflagsOrig & html4.eflags['EMPTY'])) {
- stack.push({orig: tagNameOrig, rep: tagNameRep});
- }
- out.push('<', tagNameRep);
- for (var i = 0, n = attribs.length; i < n; i += 2) {
- var attribName = attribs[i],
- value = attribs[i + 1];
- if (value !== null && value !== void 0) {
- out.push(' ', attribName, '="', escapeAttrib(value), '"');
- }
- }
- out.push('>');
- if ((eflagsOrig & html4.eflags['EMPTY'])
- && !(eflagsRep & html4.eflags['EMPTY'])) {
- // replacement is non-empty, synthesize end tag
- out.push('<\/', tagNameRep, '>');
- }
- },
- 'endTag': function(tagName, out) {
- if (ignoring) {
- ignoring = false;
- return;
- }
- if (!html4.ELEMENTS.hasOwnProperty(tagName)) { return; }
- var eflags = html4.ELEMENTS[tagName];
- if (!(eflags & (html4.eflags['EMPTY'] | html4.eflags['FOLDABLE']))) {
- var index;
- if (eflags & html4.eflags['OPTIONAL_ENDTAG']) {
- for (index = stack.length; --index >= 0;) {
- var stackElOrigTag = stack[index].orig;
- if (stackElOrigTag === tagName) { break; }
- if (!(html4.ELEMENTS[stackElOrigTag] &
- html4.eflags['OPTIONAL_ENDTAG'])) {
- // Don't pop non optional end tags looking for a match.
- return;
- }
- }
- } else {
- for (index = stack.length; --index >= 0;) {
- if (stack[index].orig === tagName) { break; }
- }
- }
- if (index < 0) { return; } // Not opened.
- for (var i = stack.length; --i > index;) {
- var stackElRepTag = stack[i].rep;
- if (!(html4.ELEMENTS[stackElRepTag] &
- html4.eflags['OPTIONAL_ENDTAG'])) {
- out.push('<\/', stackElRepTag, '>');
- }
- }
- if (index < stack.length) {
- tagName = stack[index].rep;
- }
- stack.length = index;
- out.push('<\/', tagName, '>');
- }
- },
- 'pcdata': emit,
- 'rcdata': emit,
- 'cdata': emit,
- 'endDoc': function(out) {
- for (; stack.length; stack.length--) {
- out.push('<\/', stack[stack.length - 1].rep, '>');
- }
- }
- });
- }
- var ALLOWED_URI_SCHEMES = /^(?:https?|mailto|data)$/i;
- function safeUri(uri, effect, ltype, hints, naiveUriRewriter) {
- if (!naiveUriRewriter) { return null; }
- try {
- var parsed = URI.parse('' + uri);
- if (parsed) {
- if (!parsed.hasScheme() ||
- ALLOWED_URI_SCHEMES.test(parsed.getScheme())) {
- var safe = naiveUriRewriter(parsed, effect, ltype, hints);
- return safe ? safe.toString() : null;
- }
- }
- } catch (e) {
- return null;
- }
- return null;
- }
- function log(logger, tagName, attribName, oldValue, newValue) {
- if (!attribName) {
- logger(tagName + " removed", {
- change: "removed",
- tagName: tagName
- });
- }
- if (oldValue !== newValue) {
- var changed = "changed";
- if (oldValue && !newValue) {
- changed = "removed";
- } else if (!oldValue && newValue) {
- changed = "added";
- }
- logger(tagName + "." + attribName + " " + changed, {
- change: changed,
- tagName: tagName,
- attribName: attribName,
- oldValue: oldValue,
- newValue: newValue
- });
- }
- }
- function lookupAttribute(map, tagName, attribName) {
- var attribKey;
- attribKey = tagName + '::' + attribName;
- if (map.hasOwnProperty(attribKey)) {
- return map[attribKey];
- }
- attribKey = '*::' + attribName;
- if (map.hasOwnProperty(attribKey)) {
- return map[attribKey];
- }
- return void 0;
- }
- function getAttributeType(tagName, attribName) {
- return lookupAttribute(html4.ATTRIBS, tagName, attribName);
- }
- function getLoaderType(tagName, attribName) {
- return lookupAttribute(html4.LOADERTYPES, tagName, attribName);
- }
- function getUriEffect(tagName, attribName) {
- return lookupAttribute(html4.URIEFFECTS, tagName, attribName);
- }
- /**
- * Sanitizes attributes on an HTML tag.
- * @param {string} tagName An HTML tag name in lowercase.
- * @param {Array.<?string>} attribs An array of alternating names and values.
- * @param {?function(?string): ?string} opt_naiveUriRewriter A transform to
- * apply to URI attributes; it can return a new string value, or null to
- * delete the attribute. If unspecified, URI attributes are deleted.
- * @param {function(?string): ?string} opt_nmTokenPolicy A transform to apply
- * to attributes containing HTML names, element IDs, and space-separated
- * lists of classes; it can return a new string value, or null to delete
- * the attribute. If unspecified, these attributes are kept unchanged.
- * @return {Array.<?string>} The sanitized attributes as a list of alternating
- * names and values, where a null value means to omit the attribute.
- */
- function sanitizeAttribs(tagName, attribs,
- opt_naiveUriRewriter, opt_nmTokenPolicy, opt_logger) {
- // TODO(felix8a): it's obnoxious that domado duplicates much of this
- // TODO(felix8a): maybe consistently enforce constraints like target=
- for (var i = 0; i < attribs.length; i += 2) {
- var attribName = attribs[i];
- var value = attribs[i + 1];
- var oldValue = value;
- var atype = null, attribKey;
- if ((attribKey = tagName + '::' + attribName,
- html4.ATTRIBS.hasOwnProperty(attribKey)) ||
- (attribKey = '*::' + attribName,
- html4.ATTRIBS.hasOwnProperty(attribKey))) {
- atype = html4.ATTRIBS[attribKey];
- }
- if (atype !== null) {
- switch (atype) {
- case html4.atype['NONE']: break;
- case html4.atype['SCRIPT']:
- value = null;
- if (opt_logger) {
- log(opt_logger, tagName, attribName, oldValue, value);
- }
- break;
- case html4.atype['STYLE']:
- if ('undefined' === typeof parseCssDeclarations) {
- value = null;
- if (opt_logger) {
- log(opt_logger, tagName, attribName, oldValue, value);
- }
- break;
- }
- var sanitizedDeclarations = [];
- parseCssDeclarations(
- value,
- {
- declaration: function (property, tokens) {
- var normProp = property.toLowerCase();
- var schema = cssSchema[normProp];
- if (!schema) {
- return;
- }
- sanitizeCssProperty(
- normProp, schema, tokens,
- opt_naiveUriRewriter
- ? function (url) {
- return safeUri(
- url, html4.ueffects.SAME_DOCUMENT,
- html4.ltypes.SANDBOXED,
- {
- "TYPE": "CSS",
- "CSS_PROP": normProp
- }, opt_naiveUriRewriter);
- }
- : null);
- sanitizedDeclarations.push(property + ': ' + tokens.join(' '));
- }
- });
- value = sanitizedDeclarations.length > 0 ?
- sanitizedDeclarations.join(' ; ') : null;
- if (opt_logger) {
- log(opt_logger, tagName, attribName, oldValue, value);
- }
- break;
- case html4.atype['ID']:
- case html4.atype['IDREF']:
- case html4.atype['IDREFS']:
- case html4.atype['GLOBAL_NAME']:
- case html4.atype['LOCAL_NAME']:
- case html4.atype['CLASSES']:
- value = opt_nmTokenPolicy ? opt_nmTokenPolicy(value) : value;
- if (opt_logger) {
- log(opt_logger, tagName, attribName, oldValue, value);
- }
- break;
- case html4.atype['URI']:
- value = safeUri(value,
- getUriEffect(tagName, attribName),
- getLoaderType(tagName, attribName),
- {
- "TYPE": "MARKUP",
- "XML_ATTR": attribName,
- "XML_TAG": tagName
- }, opt_naiveUriRewriter);
- if (opt_logger) {
- log(opt_logger, tagName, attribName, oldValue, value);
- }
- break;
- case html4.atype['URI_FRAGMENT']:
- if (value && '#' === value.charAt(0)) {
- value = value.substring(1); // remove the leading '#'
- value = opt_nmTokenPolicy ? opt_nmTokenPolicy(value) : value;
- if (value !== null && value !== void 0) {
- value = '#' + value; // restore the leading '#'
- }
- } else {
- value = null;
- }
- if (opt_logger) {
- log(opt_logger, tagName, attribName, oldValue, value);
- }
- break;
- default:
- value = null;
- if (opt_logger) {
- log(opt_logger, tagName, attribName, oldValue, value);
- }
- break;
- }
- } else {
- value = null;
- if (opt_logger) {
- log(opt_logger, tagName, attribName, oldValue, value);
- }
- }
- attribs[i + 1] = value;
- }
- return attribs;
- }
- /**
- * Creates a tag policy that omits all tags marked UNSAFE in html4-defs.js
- * and applies the default attribute sanitizer with the supplied policy for
- * URI attributes and NMTOKEN attributes.
- * @param {?function(?string): ?string} opt_naiveUriRewriter A transform to
- * apply to URI attributes. If not given, URI attributes are deleted.
- * @param {function(?string): ?string} opt_nmTokenPolicy A transform to apply
- * to attributes containing HTML names, element IDs, and space-separated
- * lists of classes. If not given, such attributes are left unchanged.
- * @return {function(string, Array.<?string>)} A tagPolicy suitable for
- * passing to html.sanitize.
- */
- function makeTagPolicy(
- opt_naiveUriRewriter, opt_nmTokenPolicy, opt_logger) {
- return function(tagName, attribs) {
- if (!(html4.ELEMENTS[tagName] & html4.eflags['UNSAFE'])) {
- return {
- 'attribs': sanitizeAttribs(tagName, attribs,
- opt_naiveUriRewriter, opt_nmTokenPolicy, opt_logger)
- };
- } else {
- if (opt_logger) {
- log(opt_logger, tagName, undefined, undefined, undefined);
- }
- }
- };
- }
- /**
- * Sanitizes HTML tags and attributes according to a given policy.
- * @param {string} inputHtml The HTML to sanitize.
- * @param {function(string, Array.<?string>)} tagPolicy A function that
- * decides which tags to accept and sanitizes their attributes (see
- * makeHtmlSanitizer above for details).
- * @return {string} The sanitized HTML.
- */
- function sanitizeWithPolicy(inputHtml, tagPolicy) {
- var outputArray = [];
- makeHtmlSanitizer(tagPolicy)(inputHtml, outputArray);
- return outputArray.join('');
- }
- /**
- * Strips unsafe tags and attributes from HTML.
- * @param {string} inputHtml The HTML to sanitize.
- * @param {?function(?string): ?string} opt_naiveUriRewriter A transform to
- * apply to URI attributes. If not given, URI attributes are deleted.
- * @param {function(?string): ?string} opt_nmTokenPolicy A transform to apply
- * to attributes containing HTML names, element IDs, and space-separated
- * lists of classes. If not given, such attributes are left unchanged.
- */
- function sanitize(inputHtml,
- opt_naiveUriRewriter, opt_nmTokenPolicy, opt_logger) {
- var tagPolicy = makeTagPolicy(
- opt_naiveUriRewriter, opt_nmTokenPolicy, opt_logger);
- return sanitizeWithPolicy(inputHtml, tagPolicy);
- }
- // Export both quoted and unquoted names for Closure linkage.
- var html = {};
- html.escapeAttrib = html['escapeAttrib'] = escapeAttrib;
- html.makeHtmlSanitizer = html['makeHtmlSanitizer'] = makeHtmlSanitizer;
- html.makeSaxParser = html['makeSaxParser'] = makeSaxParser;
- html.makeTagPolicy = html['makeTagPolicy'] = makeTagPolicy;
- html.normalizeRCData = html['normalizeRCData'] = normalizeRCData;
- html.sanitize = html['sanitize'] = sanitize;
- html.sanitizeAttribs = html['sanitizeAttribs'] = sanitizeAttribs;
- html.sanitizeWithPolicy = html['sanitizeWithPolicy'] = sanitizeWithPolicy;
- html.unescapeEntities = html['unescapeEntities'] = unescapeEntities;
- return html;
- })(html4);
- var html_sanitize = html['sanitize'];
- // Loosen restrictions of Caja's
- // html-sanitizer to allow for styling
- html4.ATTRIBS['*::style'] = 0;
- html4.ELEMENTS['style'] = 0;
- html4.ATTRIBS['a::target'] = 0;
- html4.ELEMENTS['video'] = 0;
- html4.ATTRIBS['video::src'] = 0;
- html4.ATTRIBS['video::poster'] = 0;
- html4.ATTRIBS['video::controls'] = 0;
- html4.ELEMENTS['audio'] = 0;
- html4.ATTRIBS['audio::src'] = 0;
- html4.ATTRIBS['video::autoplay'] = 0;
- html4.ATTRIBS['video::controls'] = 0;
- if (typeof module !== 'undefined') {
- module.exports = html_sanitize;
- }
- },{}],7:[function(require,module,exports){
- module.exports={
- "author": "Mapbox",
- "name": "mapbox.js",
- "description": "mapbox javascript api",
- "version": "3.1.1",
- "homepage": "http://mapbox.com/",
- "repository": {
- "type": "git",
- "url": "git://github.com/mapbox/mapbox.js.git"
- },
- "main": "src/index.js",
- "dependencies": {
- "corslite": "0.0.6",
- "isarray": "0.0.1",
- "leaflet": "1.0.2",
- "mustache": "2.2.1",
- "sanitize-caja": "0.1.4"
- },
- "scripts": {
- "test": "eslint --no-eslintrc -c .eslintrc src && phantomjs node_modules/mocha-phantomjs-core/mocha-phantomjs-core.js test/index.html"
- },
- "license": "BSD-3-Clause",
- "devDependencies": {
- "browserify": "^13.0.0",
- "clean-css": "~2.0.7",
- "cz-conventional-changelog": "1.2.0",
- "eslint": "^0.23.0",
- "expect.js": "0.3.1",
- "happen": "0.1.3",
- "leaflet-fullscreen": "0.0.4",
- "leaflet-hash": "0.2.1",
- "marked": "~0.3.0",
- "minifyify": "^6.1.0",
- "minimist": "0.0.5",
- "mocha": "2.4.5",
- "mocha-phantomjs-core": "2.0.1",
- "phantomjs-prebuilt": "2.1.12",
- "sinon": "1.10.2"
- },
- "optionalDependencies": {},
- "engines": {
- "node": "*"
- },
- "config": {
- "commitizen": {
- "path": "./node_modules/cz-conventional-changelog"
- }
- }
- }
- },{}],8:[function(require,module,exports){
- 'use strict';
- module.exports = {
- HTTP_URL: 'http://a.tiles.mapbox.com/v4',
- HTTPS_URL: 'https://a.tiles.mapbox.com/v4',
- FORCE_HTTPS: false,
- REQUIRE_ACCESS_TOKEN: true
- };
- },{}],9:[function(require,module,exports){
- 'use strict';
- var util = require('./util'),
- format_url = require('./format_url'),
- request = require('./request'),
- marker = require('./marker'),
- simplestyle = require('./simplestyle');
- // # featureLayer
- //
- // A layer of features, loaded from Mapbox or else. Adds the ability
- // to reset features, filter them, and load them from a GeoJSON URL.
- var FeatureLayer = L.FeatureGroup.extend({
- options: {
- filter: function() { return true; },
- sanitizer: require('sanitize-caja'),
- style: simplestyle.style,
- popupOptions: { closeButton: false }
- },
- initialize: function(_, options) {
- L.setOptions(this, options);
- this._layers = {};
- if (typeof _ === 'string') {
- util.idUrl(_, this);
- // javascript object of TileJSON data
- } else if (_ && typeof _ === 'object') {
- this.setGeoJSON(_);
- }
- },
- setGeoJSON: function(_) {
- this._geojson = _;
- this.clearLayers();
- this._initialize(_);
- return this;
- },
- getGeoJSON: function() {
- return this._geojson;
- },
- loadURL: function(url) {
- if (this._request && 'abort' in this._request) this._request.abort();
- this._request = request(url, L.bind(function(err, json) {
- this._request = null;
- if (err && err.type !== 'abort') {
- util.log('could not load features at ' + url);
- this.fire('error', {error: err});
- } else if (json) {
- this.setGeoJSON(json);
- this.fire('ready');
- }
- }, this));
- return this;
- },
- loadID: function(id) {
- return this.loadURL(format_url('/v4/' + id + '/features.json', this.options.accessToken));
- },
- setFilter: function(_) {
- this.options.filter = _;
- if (this._geojson) {
- this.clearLayers();
- this._initialize(this._geojson);
- }
- return this;
- },
- getFilter: function() {
- return this.options.filter;
- },
- _initialize: function(json) {
- var features = L.Util.isArray(json) ? json : json.features,
- i, len;
- if (features) {
- for (i = 0, len = features.length; i < len; i++) {
- // Only add this if geometry or geometries are set and not null
- if (features[i].geometries || features[i].geometry || features[i].features) {
- this._initialize(features[i]);
- }
- }
- } else if (this.options.filter(json)) {
- var opts = {accessToken: this.options.accessToken},
- pointToLayer = this.options.pointToLayer || function(feature, latlon) {
- return marker.style(feature, latlon, opts);
- },
- layer = L.GeoJSON.geometryToLayer(json, {
- pointToLayer: pointToLayer
- }),
- popupHtml = marker.createPopup(json, this.options.sanitizer),
- style = this.options.style,
- defaultStyle = style === simplestyle.style;
- if (style && 'setStyle' in layer &&
- // if the style method is the simplestyle default, then
- // never style L.Circle or L.CircleMarker because
- // simplestyle has no rules over them, only over geometry
- // primitives directly from GeoJSON
- (!(defaultStyle && (layer instanceof L.Circle ||
- layer instanceof L.CircleMarker)))) {
- if (typeof style === 'function') {
- style = style(json);
- }
- layer.setStyle(style);
- }
- layer.feature = json;
- if (popupHtml) {
- layer.bindPopup(popupHtml, this.options.popupOptions);
- }
- this.addLayer(layer);
- }
- }
- });
- module.exports.FeatureLayer = FeatureLayer;
- module.exports.featureLayer = function(_, options) {
- return new FeatureLayer(_, options);
- };
- },{"./format_url":11,"./marker":24,"./request":25,"./simplestyle":27,"./util":30,"sanitize-caja":5}],10:[function(require,module,exports){
- 'use strict';
- var Feedback = L.Class.extend({
- includes: L.Mixin.Events,
- data: {},
- record: function(data) {
- L.extend(this.data, data);
- this.fire('change');
- }
- });
- module.exports = new Feedback();
- },{}],11:[function(require,module,exports){
- 'use strict';
- var config = require('./config'),
- version = require('../package.json').version;
- module.exports = function(path, accessToken) {
- accessToken = accessToken || L.mapbox.accessToken;
- if (!accessToken && config.REQUIRE_ACCESS_TOKEN) {
- throw new Error('An API access token is required to use Mapbox.js. ' +
- 'See https://www.mapbox.com/mapbox.js/api/v' + version + '/api-access-tokens/');
- }
- var url = (document.location.protocol === 'https:' || config.FORCE_HTTPS) ? config.HTTPS_URL : config.HTTP_URL;
- url = url.replace(/\/v4$/, '');
- url += path;
- if (config.REQUIRE_ACCESS_TOKEN) {
- if (accessToken[0] === 's') {
- throw new Error('Use a public access token (pk.*) with Mapbox.js, not a secret access token (sk.*). ' +
- 'See https://www.mapbox.com/mapbox.js/api/v' + version + '/api-access-tokens/');
- }
- url += url.indexOf('?') !== -1 ? '&access_token=' : '?access_token=';
- url += accessToken;
- }
- return url;
- };
- module.exports.tileJSON = function(urlOrMapID, accessToken) {
- if (urlOrMapID.indexOf('mapbox://styles') === 0) {
- throw new Error('Styles created with Mapbox Studio need to be used with ' +
- 'L.mapbox.styleLayer, not L.mapbox.tileLayer');
- }
- if (urlOrMapID.indexOf('/') !== -1)
- return urlOrMapID;
- var url = module.exports('/v4/' + urlOrMapID + '.json', accessToken);
- // TileJSON requests need a secure flag appended to their URLs so
- // that the server knows to send SSL-ified resource references.
- if (url.indexOf('https') === 0)
- url += '&secure';
- return url;
- };
- module.exports.style = function(styleURL, accessToken) {
- if (styleURL.indexOf('mapbox://styles/') === -1) throw new Error('Incorrectly formatted Mapbox style at ' + styleURL);
- var ownerIDStyle = styleURL.split('mapbox://styles/')[1];
- var url = module.exports('/styles/v1/' + ownerIDStyle, accessToken)
- .replace('http://', 'https://');
- return url;
- };
- },{"../package.json":7,"./config":8}],12:[function(require,module,exports){
- 'use strict';
- var isArray = require('isarray'),
- util = require('./util'),
- format_url = require('./format_url'),
- feedback = require('./feedback'),
- request = require('./request');
- // Low-level geocoding interface - wraps specific API calls and their
- // return values.
- module.exports = function(url, options) {
- if (!options) options = {};
- var geocoder = {};
- util.strict(url, 'string');
- if (url.indexOf('/') === -1) {
- url = format_url('/geocoding/v5/' + url + '/{query}.json', options.accessToken, 5);
- }
- function roundTo(latLng, precision) {
- var mult = Math.pow(10, precision);
- latLng.lat = Math.round(latLng.lat * mult) / mult;
- latLng.lng = Math.round(latLng.lng * mult) / mult;
- return latLng;
- }
- geocoder.getURL = function() {
- return url;
- };
- geocoder.queryURL = function(_) {
- var isObject = !(isArray(_) || typeof _ === 'string'),
- query = isObject ? _.query : _;
- if (isArray(query)) {
- var parts = [];
- for (var i = 0; i < query.length; i++) {
- parts[i] = encodeURIComponent(query[i]);
- }
- query = parts.join(';');
- } else {
- query = encodeURIComponent(query);
- }
- feedback.record({ geocoding: query });
- var url = L.Util.template(geocoder.getURL(), {query: query});
- if (isObject) {
- if (_.types) {
- if (isArray(_.types)) {
- url += '&types=' + _.types.join();
- } else {
- url += '&types=' + _.types;
- }
- }
- if (_.country) {
- if (isArray(_.country)) {
- url += '&country=' + _.country.join();
- } else {
- url += '&country=' + _.country;
- }
- }
- if (_.bbox) {
- if (isArray(_.bbox)) {
- url += '&bbox=' + _.bbox.join();
- } else {
- url += '&bbox=' + _.bbox;
- }
- }
- if (_.proximity) {
- var proximity = roundTo(L.latLng(_.proximity), 3);
- url += '&proximity=' + proximity.lng + ',' + proximity.lat;
- }
- if (typeof _.autocomplete === 'boolean') {
- url += '&autocomplete=' + _.autocomplete;
- }
- }
- return url;
- };
- geocoder.query = function(_, callback) {
- util.strict(callback, 'function');
- request(geocoder.queryURL(_), function(err, json) {
- if (json && (json.length || json.features)) {
- var res = {
- results: json
- };
- if (json.features && json.features.length) {
- res.latlng = [
- json.features[0].center[1],
- json.features[0].center[0]];
- if (json.features[0].bbox) {
- res.bounds = json.features[0].bbox;
- res.lbounds = util.lbounds(res.bounds);
- }
- }
- callback(null, res);
- } else callback(err || true);
- });
- return geocoder;
- };
- // a reverse geocode:
- //
- // geocoder.reverseQuery([80, 20])
- geocoder.reverseQuery = function(_, callback) {
- var q = '';
- // sort through different ways people represent lat and lon pairs
- function normalize(x) {
- var latLng;
- if (x.lat !== undefined && x.lng !== undefined) {
- latLng = L.latLng(x.lat, x.lng);
- } else if (x.lat !== undefined && x.lon !== undefined) {
- latLng = L.latLng(x.lat, x.lon);
- } else {
- latLng = L.latLng(x[1], x[0]);
- }
- latLng = roundTo(latLng, 5);
- return latLng.lng + ',' + latLng.lat;
- }
- if (_.length && _[0].length) {
- for (var i = 0, pts = []; i < _.length; i++) {
- pts.push(normalize(_[i]));
- }
- q = pts.join(';');
- } else {
- q = normalize(_);
- }
- request(geocoder.queryURL(q), function(err, json) {
- callback(err, json);
- });
- return geocoder;
- };
- return geocoder;
- };
- },{"./feedback":10,"./format_url":11,"./request":25,"./util":30,"isarray":2}],13:[function(require,module,exports){
- 'use strict';
- var geocoder = require('./geocoder'),
- util = require('./util');
- var GeocoderControl = L.Control.extend({
- includes: L.Mixin.Events,
- options: {
- proximity: true,
- position: 'topleft',
- pointZoom: 16,
- keepOpen: false,
- autocomplete: false,
- queryOptions: {}
- },
- initialize: function(_, options) {
- L.Util.setOptions(this, options);
- this.setURL(_);
- this._updateSubmit = L.bind(this._updateSubmit, this);
- this._updateAutocomplete = L.bind(this._updateAutocomplete, this);
- this._chooseResult = L.bind(this._chooseResult, this);
- },
- setURL: function(_) {
- this.geocoder = geocoder(_, {
- accessToken: this.options.accessToken
- });
- return this;
- },
- getURL: function() {
- return this.geocoder.getURL();
- },
- setID: function(_) {
- return this.setURL(_);
- },
- setTileJSON: function(_) {
- return this.setURL(_.geocoder);
- },
- _toggle: function(e) {
- if (e) L.DomEvent.stop(e);
- if (L.DomUtil.hasClass(this._container, 'active')) {
- L.DomUtil.removeClass(this._container, 'active');
- this._results.innerHTML = '';
- this._input.blur();
- } else {
- L.DomUtil.addClass(this._container, 'active');
- this._input.focus();
- this._input.select();
- }
- },
- _closeIfOpen: function() {
- if (L.DomUtil.hasClass(this._container, 'active') &&
- !this.options.keepOpen) {
- L.DomUtil.removeClass(this._container, 'active');
- this._results.innerHTML = '';
- this._input.blur();
- }
- },
- onAdd: function(map) {
- var container = L.DomUtil.create('div', 'leaflet-control-mapbox-geocoder leaflet-bar leaflet-control'),
- link = L.DomUtil.create('a', 'leaflet-control-mapbox-geocoder-toggle mapbox-icon mapbox-icon-geocoder', container),
- results = L.DomUtil.create('div', 'leaflet-control-mapbox-geocoder-results', container),
- wrap = L.DomUtil.create('div', 'leaflet-control-mapbox-geocoder-wrap', container),
- form = L.DomUtil.create('form', 'leaflet-control-mapbox-geocoder-form', wrap),
- input = L.DomUtil.create('input', '', form);
- link.href = '#';
- link.innerHTML = ' ';
- input.type = 'text';
- input.setAttribute('placeholder', 'Search');
- L.DomEvent.addListener(form, 'submit', this._geocode, this);
- L.DomEvent.addListener(input, 'keyup', this._autocomplete, this);
- L.DomEvent.disableClickPropagation(container);
- this._map = map;
- this._results = results;
- this._input = input;
- this._form = form;
- if (this.options.keepOpen) {
- L.DomUtil.addClass(container, 'active');
- } else {
- this._map.on('click', this._closeIfOpen, this);
- L.DomEvent.addListener(link, 'click', this._toggle, this);
- }
- return container;
- },
- _updateSubmit: function(err, resp) {
- L.DomUtil.removeClass(this._container, 'searching');
- this._results.innerHTML = '';
- if (err || !resp) {
- this.fire('error', {error: err});
- } else {
- var features = [];
- if (resp.results && resp.results.features) {
- features = resp.results.features;
- }
- if (features.length === 1) {
- this.fire('autoselect', { feature: features[0] });
- this.fire('found', {results: resp.results});
- this._chooseResult(features[0]);
- this._closeIfOpen();
- } else if (features.length > 1) {
- this.fire('found', {results: resp.results});
- this._displayResults(features);
- } else {
- this.fire('notfound');
- this._displayResults(features);
- }
- }
- },
- _updateAutocomplete: function(err, resp) {
- this._results.innerHTML = '';
- if (err || !resp) {
- this.fire('error', {error: err});
- } else {
- var features = [];
- if (resp.results && resp.results.features) {
- features = resp.results.features;
- }
- if (features.length) {
- this.fire('found', {results: resp.results});
- } else {
- this.fire('notfound');
- }
- this._displayResults(features);
- }
- },
- _displayResults: function(features) {
- for (var i = 0, l = Math.min(features.length, 5); i < l; i++) {
- var feature = features[i];
- var name = feature.place_name;
- if (!name.length) continue;
- var r = L.DomUtil.create('a', '', this._results);
- var text = ('innerText' in r) ? 'innerText' : 'textContent';
- r[text] = name;
- r.setAttribute('title', name);
- r.href = '#';
- (L.bind(function(feature) {
- L.DomEvent.addListener(r, 'click', function(e) {
- this._chooseResult(feature);
- L.DomEvent.stop(e);
- this.fire('select', { feature: feature });
- }, this);
- }, this))(feature);
- }
- if (features.length > 5) {
- var outof = L.DomUtil.create('span', '', this._results);
- outof.innerHTML = 'Top 5 of ' + features.length + ' results';
- }
- },
- _chooseResult: function(result) {
- if (result.bbox) {
- this._map.fitBounds(util.lbounds(result.bbox));
- } else if (result.center) {
- this._map.setView([result.center[1], result.center[0]], (this._map.getZoom() === undefined) ?
- this.options.pointZoom :
- Math.max(this._map.getZoom(), this.options.pointZoom));
- }
- },
- _geocode: function(e) {
- L.DomEvent.preventDefault(e);
- if (this._input.value === '') return this._updateSubmit();
- L.DomUtil.addClass(this._container, 'searching');
- this.geocoder.query(L.Util.extend({
- query: this._input.value,
- proximity: this.options.proximity ? this._map.getCenter() : false
- }, this.options.queryOptions), this._updateSubmit);
- },
- _autocomplete: function() {
- if (!this.options.autocomplete) return;
- if (this._input.value === '') return this._updateAutocomplete();
- this.geocoder.query(L.Util.extend({
- query: this._input.value,
- proximity: this.options.proximity ? this._map.getCenter() : false
- }, this.options.queryOptions), this._updateAutocomplete);
- }
- });
- module.exports.GeocoderControl = GeocoderControl;
- module.exports.geocoderControl = function(_, options) {
- return new GeocoderControl(_, options);
- };
- },{"./geocoder":12,"./util":30}],14:[function(require,module,exports){
- 'use strict';
- function utfDecode(c) {
- if (c >= 93) c--;
- if (c >= 35) c--;
- return c - 32;
- }
- module.exports = function(data) {
- return function(x, y) {
- if (!data) return;
- var idx = utfDecode(data.grid[y].charCodeAt(x)),
- key = data.keys[idx];
- return data.data[key];
- };
- };
- },{}],15:[function(require,module,exports){
- 'use strict';
- var util = require('./util'),
- Mustache = require('mustache');
- var GridControl = L.Control.extend({
- options: {
- pinnable: true,
- follow: false,
- sanitizer: require('sanitize-caja'),
- touchTeaser: true,
- location: true
- },
- _currentContent: '',
- // pinned means that this control is on a feature and the user has likely
- // clicked. pinned will not become false unless the user clicks off
- // of the feature onto another or clicks x
- _pinned: false,
- initialize: function(_, options) {
- L.Util.setOptions(this, options);
- util.strict_instance(_, L.Class, 'L.mapbox.gridLayer');
- this._layer = _;
- },
- setTemplate: function(template) {
- util.strict(template, 'string');
- this.options.template = template;
- return this;
- },
- _template: function(format, data) {
- if (!data) return;
- var template = this.options.template || this._layer.getTileJSON().template;
- if (template) {
- var d = {};
- d['__' + format + '__'] = true;
- return this.options.sanitizer(
- Mustache.to_html(template, L.extend(d, data)));
- }
- },
- // change the content of the tooltip HTML if it has changed, otherwise
- // noop
- _show: function(content, o) {
- if (content === this._currentContent) return;
- this._currentContent = content;
- if (this.options.follow) {
- this._popup.setContent(content)
- .setLatLng(o.latLng);
- if (this._map._popup !== this._popup) this._popup.openOn(this._map);
- } else {
- this._container.style.display = 'block';
- this._contentWrapper.innerHTML = content;
- }
- },
- hide: function() {
- this._pinned = false;
- this._currentContent = '';
- this._map.closePopup();
- this._container.style.display = 'none';
- this._contentWrapper.innerHTML = '';
- L.DomUtil.removeClass(this._container, 'closable');
- return this;
- },
- _mouseover: function(o) {
- if (o.data) {
- L.DomUtil.addClass(this._map._container, 'map-clickable');
- } else {
- L.DomUtil.removeClass(this._map._container, 'map-clickable');
- }
- if (this._pinned) return;
- var content = this._template('teaser', o.data);
- if (content) {
- this._show(content, o);
- } else {
- this.hide();
- }
- },
- _mousemove: function(o) {
- if (this._pinned) return;
- if (!this.options.follow) return;
- this._popup.setLatLng(o.latLng);
- },
- _navigateTo: function(url) {
- window.top.location.href = url;
- },
- _click: function(o) {
- var location_formatted = this._template('location', o.data);
- if (this.options.location && location_formatted &&
- location_formatted.search(/^https?:/) === 0) {
- return this._navigateTo(this._template('location', o.data));
- }
- if (!this.options.pinnable) return;
- var content = this._template('full', o.data);
- if (!content && this.options.touchTeaser && L.Browser.touch) {
- content = this._template('teaser', o.data);
- }
- if (content) {
- L.DomUtil.addClass(this._container, 'closable');
- this._pinned = true;
- this._show(content, o);
- } else if (this._pinned) {
- L.DomUtil.removeClass(this._container, 'closable');
- this._pinned = false;
- this.hide();
- }
- },
- _onPopupClose: function() {
- this._currentContent = null;
- this._pinned = false;
- },
- _createClosebutton: function(container, fn) {
- var link = L.DomUtil.create('a', 'close', container);
- link.innerHTML = 'close';
- link.href = '#';
- link.title = 'close';
- L.DomEvent
- .on(link, 'click', L.DomEvent.stopPropagation)
- .on(link, 'mousedown', L.DomEvent.stopPropagation)
- .on(link, 'dblclick', L.DomEvent.stopPropagation)
- .on(link, 'click', L.DomEvent.preventDefault)
- .on(link, 'click', fn, this);
- return link;
- },
- onAdd: function(map) {
- this._map = map;
- var className = 'leaflet-control-grid map-tooltip',
- container = L.DomUtil.create('div', className),
- contentWrapper = L.DomUtil.create('div', 'map-tooltip-content');
- // hide the container element initially
- container.style.display = 'none';
- this._createClosebutton(container, this.hide);
- container.appendChild(contentWrapper);
- this._contentWrapper = contentWrapper;
- this._popup = new L.Popup({ autoPan: false, closeOnClick: false });
- map.on('popupclose', this._onPopupClose, this);
- L.DomEvent
- .disableClickPropagation(container)
- // allow people to scroll tooltips with mousewheel
- .addListener(container, 'mousewheel', L.DomEvent.stopPropagation);
- this._layer
- .on('mouseover', this._mouseover, this)
- .on('mousemove', this._mousemove, this)
- .on('click', this._click, this);
- return container;
- },
- onRemove: function (map) {
- map.off('popupclose', this._onPopupClose, this);
- this._layer
- .off('mouseover', this._mouseover, this)
- .off('mousemove', this._mousemove, this)
- .off('click', this._click, this);
- }
- });
- module.exports.GridControl = GridControl;
- module.exports.gridControl = function(_, options) {
- return new GridControl(_, options);
- };
- },{"./util":30,"mustache":4,"sanitize-caja":5}],16:[function(require,module,exports){
- 'use strict';
- var util = require('./util'),
- request = require('./request'),
- grid = require('./grid');
- // forked from danzel/L.UTFGrid
- var GridLayer = L.Layer.extend({
- includes: [require('./load_tilejson')],
- options: {
- template: function() { return ''; }
- },
- _mouseOn: null,
- _tilejson: {},
- _cache: {},
- initialize: function(_, options) {
- L.Util.setOptions(this, options);
- this._loadTileJSON(_);
- },
- _setTileJSON: function(json) {
- util.strict(json, 'object');
- L.extend(this.options, {
- grids: json.grids,
- minZoom: json.minzoom,
- maxZoom: json.maxzoom,
- bounds: json.bounds && util.lbounds(json.bounds)
- });
- this._tilejson = json;
- this._cache = {};
- this._update();
- return this;
- },
- getTileJSON: function() {
- return this._tilejson;
- },
- active: function() {
- return !!(this._map && this.options.grids && this.options.grids.length);
- },
- onAdd: function(map) {
- this._map = map;
- this._update();
- this._map
- .on('click', this._click, this)
- .on('mousemove', this._move, this)
- .on('moveend', this._update, this);
- },
- onRemove: function() {
- this._map
- .off('click', this._click, this)
- .off('mousemove', this._move, this)
- .off('moveend', this._update, this);
- },
- getData: function(latlng, callback) {
- if (!this.active()) return;
- var map = this._map,
- point = map.project(latlng.wrap()),
- tileSize = 256,
- resolution = 4,
- x = Math.floor(point.x / tileSize),
- y = Math.floor(point.y / tileSize),
- max = map.options.crs.scale(map.getZoom()) / tileSize;
- x = (x + max) % max;
- y = (y + max) % max;
- this._getTile(map.getZoom(), x, y, function(grid) {
- var gridX = Math.floor((point.x - (x * tileSize)) / resolution),
- gridY = Math.floor((point.y - (y * tileSize)) / resolution);
- callback(grid(gridX, gridY));
- });
- return this;
- },
- _click: function(e) {
- this.getData(e.latlng, L.bind(function(data) {
- this.fire('click', {
- latLng: e.latlng,
- data: data
- });
- }, this));
- },
- _move: function(e) {
- this.getData(e.latlng, L.bind(function(data) {
- if (data !== this._mouseOn) {
- if (this._mouseOn) {
- this.fire('mouseout', {
- latLng: e.latlng,
- data: this._mouseOn
- });
- }
- this.fire('mouseover', {
- latLng: e.latlng,
- data: data
- });
- this._mouseOn = data;
- } else {
- this.fire('mousemove', {
- latLng: e.latlng,
- data: data
- });
- }
- }, this));
- },
- _getTileURL: function(tilePoint) {
- var urls = this.options.grids,
- index = (tilePoint.x + tilePoint.y) % urls.length,
- url = urls[index];
- return L.Util.template(url, tilePoint);
- },
- // Load up all required json grid files
- _update: function() {
- if (!this.active()) return;
- var bounds = this._map.getPixelBounds(),
- z = this._map.getZoom(),
- tileSize = 256;
- if (z > this.options.maxZoom || z < this.options.minZoom) return;
- var tileBounds = L.bounds(
- bounds.min.divideBy(tileSize)._floor(),
- bounds.max.divideBy(tileSize)._floor()),
- max = this._map.options.crs.scale(z) / tileSize;
- for (var x = tileBounds.min.x; x <= tileBounds.max.x; x++) {
- for (var y = tileBounds.min.y; y <= tileBounds.max.y; y++) {
- // x wrapped
- this._getTile(z, ((x % max) + max) % max, ((y % max) + max) % max);
- }
- }
- },
- _getTile: function(z, x, y, callback) {
- var key = z + '_' + x + '_' + y,
- tilePoint = L.point(x, y);
- tilePoint.z = z;
- if (!this._tileShouldBeLoaded(tilePoint)) {
- return;
- }
- if (key in this._cache) {
- if (!callback) return;
- if (typeof this._cache[key] === 'function') {
- callback(this._cache[key]); // Already loaded
- } else {
- this._cache[key].push(callback); // Pending
- }
- return;
- }
- this._cache[key] = [];
- if (callback) {
- this._cache[key].push(callback);
- }
- request(this._getTileURL(tilePoint), L.bind(function(err, json) {
- var callbacks = this._cache[key];
- this._cache[key] = grid(json);
- for (var i = 0; i < callbacks.length; ++i) {
- callbacks[i](this._cache[key]);
- }
- }, this));
- },
- _tileShouldBeLoaded: function(tilePoint) {
- if (tilePoint.z > this.options.maxZoom || tilePoint.z < this.options.minZoom) {
- return false;
- }
- if (this.options.bounds) {
- var tileSize = 256,
- nwPoint = tilePoint.multiplyBy(tileSize),
- sePoint = nwPoint.add(new L.Point(tileSize, tileSize)),
- nw = this._map.unproject(nwPoint),
- se = this._map.unproject(sePoint),
- bounds = new L.LatLngBounds([nw, se]);
- if (!this.options.bounds.intersects(bounds)) {
- return false;
- }
- }
- return true;
- }
- });
- module.exports.GridLayer = GridLayer;
- module.exports.gridLayer = function(_, options) {
- return new GridLayer(_, options);
- };
- },{"./grid":14,"./load_tilejson":20,"./request":25,"./util":30}],17:[function(require,module,exports){
- 'use strict';
- var leaflet = require('./leaflet');
- require('./mapbox');
- module.exports = leaflet;
- },{"./leaflet":18,"./mapbox":22}],18:[function(require,module,exports){
- module.exports = window.L = require('leaflet/dist/leaflet-src');
- },{"leaflet/dist/leaflet-src":3}],19:[function(require,module,exports){
- 'use strict';
- var LegendControl = L.Control.extend({
- options: {
- position: 'bottomright',
- sanitizer: require('sanitize-caja')
- },
- initialize: function(options) {
- L.setOptions(this, options);
- this._legends = {};
- },
- onAdd: function() {
- this._container = L.DomUtil.create('div', 'map-legends wax-legends');
- L.DomEvent.disableClickPropagation(this._container);
- this._update();
- return this._container;
- },
- addLegend: function(text) {
- if (!text) { return this; }
- if (!this._legends[text]) {
- this._legends[text] = 0;
- }
- this._legends[text]++;
- return this._update();
- },
- removeLegend: function(text) {
- if (!text) { return this; }
- if (this._legends[text]) this._legends[text]--;
- return this._update();
- },
- _update: function() {
- if (!this._map) { return this; }
- this._container.innerHTML = '';
- var hide = 'none';
- for (var i in this._legends) {
- if (this._legends.hasOwnProperty(i) && this._legends[i]) {
- var div = L.DomUtil.create('div', 'map-legend wax-legend', this._container);
- div.innerHTML = this.options.sanitizer(i);
- hide = 'block';
- }
- }
- // hide the control entirely unless there is at least one legend;
- // otherwise there will be a small grey blemish on the map.
- this._container.style.display = hide;
- return this;
- }
- });
- module.exports.LegendControl = LegendControl;
- module.exports.legendControl = function(options) {
- return new LegendControl(options);
- };
- },{"sanitize-caja":5}],20:[function(require,module,exports){
- 'use strict';
- var request = require('./request'),
- format_url = require('./format_url'),
- util = require('./util');
- module.exports = {
- _loadTileJSON: function(_) {
- if (typeof _ === 'string') {
- _ = format_url.tileJSON(_, this.options && this.options.accessToken);
- request(_, L.bind(function(err, json) {
- if (err) {
- util.log('could not load TileJSON at ' + _);
- this.fire('error', {error: err});
- } else if (json) {
- this._setTileJSON(json);
- this.fire('ready');
- }
- }, this));
- } else if (_ && typeof _ === 'object') {
- this._setTileJSON(_);
- }
- }
- };
- },{"./format_url":11,"./request":25,"./util":30}],21:[function(require,module,exports){
- 'use strict';
- var tileLayer = require('./tile_layer').tileLayer,
- featureLayer = require('./feature_layer').featureLayer,
- gridLayer = require('./grid_layer').gridLayer,
- gridControl = require('./grid_control').gridControl,
- shareControl = require('./share_control').shareControl,
- legendControl = require('./legend_control').legendControl,
- mapboxLogoControl = require('./mapbox_logo').mapboxLogoControl,
- feedback = require('./feedback');
- function withAccessToken(options, accessToken) {
- if (!accessToken || options.accessToken)
- return options;
- return L.extend({accessToken: accessToken}, options);
- }
- var LMap = L.Map.extend({
- includes: [require('./load_tilejson')],
- options: {
- tileLayer: {},
- featureLayer: {},
- gridLayer: {},
- legendControl: {},
- gridControl: {},
- shareControl: false,
- sanitizer: require('sanitize-caja')
- },
- _tilejson: {},
- initialize: function(element, _, options) {
- L.Map.prototype.initialize.call(this, element,
- L.extend({}, L.Map.prototype.options, options));
- // Disable the default 'Leaflet' text
- if (this.attributionControl) {
- this.attributionControl.setPrefix('');
- var compact = this.options.attributionControl.compact;
- // Set a compact display if map container width is < 640 or
- // compact is set to `true` in attributionControl options.
- if (compact || (compact !== false && this._container.offsetWidth <= 640)) {
- L.DomUtil.addClass(this.attributionControl._container, 'leaflet-compact-attribution');
- }
- if (compact === undefined) {
- this.on('resize', function() {
- if (this._container.offsetWidth > 640) {
- L.DomUtil.removeClass(this.attributionControl._container, 'leaflet-compact-attribution');
- } else {
- L.DomUtil.addClass(this.attributionControl._container, 'leaflet-compact-attribution');
- }
- });
- }
- }
- if (this.options.tileLayer) {
- this.tileLayer = tileLayer(undefined,
- withAccessToken(this.options.tileLayer, this.options.accessToken));
- this.addLayer(this.tileLayer);
- }
- if (this.options.featureLayer) {
- this.featureLayer = featureLayer(undefined,
- withAccessToken(this.options.featureLayer, this.options.accessToken));
- this.addLayer(this.featureLayer);
- }
- if (this.options.gridLayer) {
- this.gridLayer = gridLayer(undefined,
- withAccessToken(this.options.gridLayer, this.options.accessToken));
- this.addLayer(this.gridLayer);
- }
- if (this.options.gridLayer && this.options.gridControl) {
- this.gridControl = gridControl(this.gridLayer, this.options.gridControl);
- this.addControl(this.gridControl);
- }
- if (this.options.legendControl) {
- this.legendControl = legendControl(this.options.legendControl);
- this.addControl(this.legendControl);
- }
- if (this.options.shareControl) {
- this.shareControl = shareControl(undefined,
- withAccessToken(this.options.shareControl, this.options.accessToken));
- this.addControl(this.shareControl);
- }
- this._mapboxLogoControl = mapboxLogoControl(this.options.mapboxLogoControl);
- this.addControl(this._mapboxLogoControl);
- this._loadTileJSON(_);
- this.on('layeradd', this._onLayerAdd, this)
- .on('layerremove', this._onLayerRemove, this)
- .on('moveend', this._updateMapFeedbackLink, this);
- this.whenReady(function () {
- feedback.on('change', this._updateMapFeedbackLink, this);
- });
- this.on('unload', function () {
- feedback.off('change', this._updateMapFeedbackLink, this);
- });
- },
- // use a javascript object of tilejson data to configure this layer
- _setTileJSON: function(_) {
- this._tilejson = _;
- this._initialize(_);
- return this;
- },
- getTileJSON: function() {
- return this._tilejson;
- },
- _initialize: function(json) {
- if (this.tileLayer) {
- this.tileLayer._setTileJSON(json);
- this._updateLayer(this.tileLayer);
- }
- if (this.featureLayer && !this.featureLayer.getGeoJSON() && json.data && json.data[0]) {
- this.featureLayer.loadURL(json.data[0]);
- }
- if (this.gridLayer) {
- this.gridLayer._setTileJSON(json);
- this._updateLayer(this.gridLayer);
- }
- if (this.legendControl && json.legend) {
- this.legendControl.addLegend(json.legend);
- }
- if (this.shareControl) {
- this.shareControl._setTileJSON(json);
- }
- this._mapboxLogoControl._setTileJSON(json);
- if (!this._loaded && json.center) {
- var zoom = this.getZoom() !== undefined ? this.getZoom() : json.center[2],
- center = L.latLng(json.center[1], json.center[0]);
- this.setView(center, zoom);
- }
- },
- _updateMapFeedbackLink: function() {
- if (!this._controlContainer.getElementsByClassName) return;
- var link = this._controlContainer.getElementsByClassName('mapbox-improve-map');
- if (link.length && this._loaded) {
- var center = this.getCenter().wrap();
- var tilejson = this._tilejson || {};
- var id = tilejson.id || '';
- var hash = '#' + id + '/' +
- center.lng.toFixed(3) + '/' +
- center.lat.toFixed(3) + '/' +
- this.getZoom();
- for (var key in feedback.data) {
- hash += '/' + key + '=' + feedback.data[key];
- }
- for (var i = 0; i < link.length; i++) {
- link[i].hash = hash;
- }
- }
- },
- _onLayerAdd: function(e) {
- if ('on' in e.layer) {
- e.layer.on('ready', this._onLayerReady, this);
- }
- window.setTimeout(L.bind(this._updateMapFeedbackLink, this), 0); // Update after attribution control resets the HTML.
- },
- _onLayerRemove: function(e) {
- if ('on' in e.layer) {
- e.layer.off('ready', this._onLayerReady, this);
- }
- window.setTimeout(L.bind(this._updateMapFeedbackLink, this), 0); // Update after attribution control resets the HTML.
- },
- _onLayerReady: function(e) {
- this._updateLayer(e.target);
- },
- _updateLayer: function(layer) {
- if (!layer.options) return;
- if (this.attributionControl && this._loaded && layer.getAttribution) {
- this.attributionControl.addAttribution(layer.getAttribution());
- }
- if (!(L.stamp(layer) in this._zoomBoundLayers) &&
- (layer.options.maxZoom || layer.options.minZoom)) {
- this._zoomBoundLayers[L.stamp(layer)] = layer;
- }
- this._updateMapFeedbackLink();
- this._updateZoomLevels();
- }
- });
- module.exports.Map = LMap;
- module.exports.map = function(element, _, options) {
- return new LMap(element, _, options);
- };
- },{"./feature_layer":9,"./feedback":10,"./grid_control":15,"./grid_layer":16,"./legend_control":19,"./load_tilejson":20,"./mapbox_logo":23,"./share_control":26,"./tile_layer":29,"sanitize-caja":5}],22:[function(require,module,exports){
- 'use strict';
- var geocoderControl = require('./geocoder_control'),
- gridControl = require('./grid_control'),
- featureLayer = require('./feature_layer'),
- legendControl = require('./legend_control'),
- shareControl = require('./share_control'),
- tileLayer = require('./tile_layer'),
- map = require('./map'),
- gridLayer = require('./grid_layer'),
- styleLayer = require('./style_layer');
- L.mapbox = module.exports = {
- VERSION: require('../package.json').version,
- geocoder: require('./geocoder'),
- marker: require('./marker'),
- simplestyle: require('./simplestyle'),
- tileLayer: tileLayer.tileLayer,
- TileLayer: tileLayer.TileLayer,
- styleLayer: styleLayer.styleLayer,
- StyleLayer: styleLayer.StyleLayer,
- shareControl: shareControl.shareControl,
- ShareControl: shareControl.ShareControl,
- legendControl: legendControl.legendControl,
- LegendControl: legendControl.LegendControl,
- geocoderControl: geocoderControl.geocoderControl,
- GeocoderControl: geocoderControl.GeocoderControl,
- gridControl: gridControl.gridControl,
- GridControl: gridControl.GridControl,
- gridLayer: gridLayer.gridLayer,
- GridLayer: gridLayer.GridLayer,
- featureLayer: featureLayer.featureLayer,
- FeatureLayer: featureLayer.FeatureLayer,
- map: map.map,
- Map: map.Map,
- config: require('./config'),
- sanitize: require('sanitize-caja'),
- template: require('mustache').to_html,
- feedback: require('./feedback')
- };
- // Hardcode image path, because Leaflet's autodetection
- // fails, because mapbox.js is not named leaflet.js
- window.L.Icon.Default.imagePath =
- // Detect bad-news protocols like file:// and hardcode
- // to https if they're detected.
- ((document.location.protocol === 'https:' ||
- document.location.protocol === 'http:') ? '' : 'https:') +
- '//api.tiles.mapbox.com/mapbox.js/' + 'v' +
- require('../package.json').version + '/images/';
- },{"../package.json":7,"./config":8,"./feature_layer":9,"./feedback":10,"./geocoder":12,"./geocoder_control":13,"./grid_control":15,"./grid_layer":16,"./legend_control":19,"./map":21,"./marker":24,"./share_control":26,"./simplestyle":27,"./style_layer":28,"./tile_layer":29,"mustache":4,"sanitize-caja":5}],23:[function(require,module,exports){
- 'use strict';
- var MapboxLogoControl = L.Control.extend({
- options: {
- position: 'bottomleft'
- },
- initialize: function(options) {
- L.setOptions(this, options);
- },
- onAdd: function() {
- this._container = L.DomUtil.create('div', 'mapbox-logo');
- return this._container;
- },
- _setTileJSON: function(json) {
- // Check if account referenced by the accessToken
- // is asscociated with the Mapbox Logo
- // as determined by mapbox-maps.
- if (json.mapbox_logo) {
- L.DomUtil.addClass(this._container, 'mapbox-logo-true');
- }
- }
- });
- module.exports.MapboxLogoControl = MapboxLogoControl;
- module.exports.mapboxLogoControl = function(options) {
- return new MapboxLogoControl(options);
- };
- },{}],24:[function(require,module,exports){
- 'use strict';
- var format_url = require('./format_url'),
- util = require('./util'),
- sanitize = require('sanitize-caja');
- // mapbox-related markers functionality
- // provide an icon from mapbox's simple-style spec and hosted markers
- // service
- function icon(fp, options) {
- fp = fp || {};
- var sizes = {
- small: [20, 50],
- medium: [30, 70],
- large: [35, 90]
- },
- size = fp['marker-size'] || 'medium',
- symbol = ('marker-symbol' in fp && fp['marker-symbol'] !== '') ? '-' + fp['marker-symbol'] : '',
- color = (fp['marker-color'] || '7e7e7e').replace('#', '');
- return L.icon({
- iconUrl: format_url('/v4/marker/' +
- 'pin-' + size.charAt(0) + symbol + '+' + color +
- // detect and use retina markers, which are x2 resolution
- (L.Browser.retina ? '@2x' : '') + '.png', options && options.accessToken),
- iconSize: sizes[size],
- iconAnchor: [sizes[size][0] / 2, sizes[size][1] / 2],
- popupAnchor: [0, -sizes[size][1] / 2]
- });
- }
- // a factory that provides markers for Leaflet from Mapbox's
- // [simple-style specification](https://github.com/mapbox/simplestyle-spec)
- // and [Markers API](http://mapbox.com/developers/api/#markers).
- function style(f, latlon, options) {
- return L.marker(latlon, {
- icon: icon(f.properties, options),
- title: util.strip_tags(
- sanitize((f.properties && f.properties.title) || ''))
- });
- }
- // Sanitize and format properties of a GeoJSON Feature object in order
- // to form the HTML string used as the argument for `L.createPopup`
- function createPopup(f, sanitizer) {
- if (!f || !f.properties) return '';
- var popup = '';
- if (f.properties.title) {
- popup += '<div class="marker-title">' + f.properties.title + '</div>';
- }
- if (f.properties.description) {
- popup += '<div class="marker-description">' + f.properties.description + '</div>';
- }
- return (sanitizer || sanitize)(popup);
- }
- module.exports = {
- icon: icon,
- style: style,
- createPopup: createPopup
- };
- },{"./format_url":11,"./util":30,"sanitize-caja":5}],25:[function(require,module,exports){
- 'use strict';
- var corslite = require('corslite'),
- strict = require('./util').strict,
- config = require('./config');
- var protocol = /^(https?:)?(?=\/\/(.|api)\.tiles\.mapbox\.com\/)/;
- module.exports = function(url, callback) {
- strict(url, 'string');
- strict(callback, 'function');
- url = url.replace(protocol, function(match, protocol) {
- if (!('withCredentials' in new window.XMLHttpRequest())) {
- // XDomainRequest in use; doesn't support cross-protocol requests
- return document.location.protocol;
- } else if (protocol === 'https:' || document.location.protocol === 'https:' || config.FORCE_HTTPS) {
- return 'https:';
- } else {
- return 'http:';
- }
- });
- function onload(err, resp) {
- if (!err && resp) {
- resp = JSON.parse(resp.responseText);
- }
- callback(err, resp);
- }
- return corslite(url, onload);
- };
- },{"./config":8,"./util":30,"corslite":1}],26:[function(require,module,exports){
- 'use strict';
- var format_url = require('./format_url');
- var ShareControl = L.Control.extend({
- includes: [require('./load_tilejson')],
- options: {
- position: 'topleft',
- url: ''
- },
- initialize: function(_, options) {
- L.setOptions(this, options);
- this._loadTileJSON(_);
- },
- _setTileJSON: function(json) {
- this._tilejson = json;
- },
- onAdd: function(map) {
- this._map = map;
- var container = L.DomUtil.create('div', 'leaflet-control-mapbox-share leaflet-bar');
- var link = L.DomUtil.create('a', 'mapbox-share mapbox-icon mapbox-icon-share', container);
- link.href = '#';
- this._modal = L.DomUtil.create('div', 'mapbox-modal', this._map._container);
- this._mask = L.DomUtil.create('div', 'mapbox-modal-mask', this._modal);
- this._content = L.DomUtil.create('div', 'mapbox-modal-content', this._modal);
- L.DomEvent.addListener(link, 'click', this._shareClick, this);
- L.DomEvent.disableClickPropagation(container);
- this._map.on('mousedown', this._clickOut, this);
- return container;
- },
- _clickOut: function(e) {
- if (this._sharing) {
- L.DomEvent.preventDefault(e);
- L.DomUtil.removeClass(this._modal, 'active');
- this._content.innerHTML = '';
- this._sharing = null;
- return;
- }
- },
- _shareClick: function(e) {
- L.DomEvent.stop(e);
- if (this._sharing) return this._clickOut(e);
- var tilejson = this._tilejson || this._map._tilejson || {},
- url = encodeURIComponent(this.options.url || tilejson.webpage || window.location),
- name = encodeURIComponent(tilejson.name),
- image = format_url('/v4/' + tilejson.id + '/' + this._map.getCenter().lng + ',' + this._map.getCenter().lat + ',' + this._map.getZoom() + '/600x600.png', this.options.accessToken),
- embed = format_url('/v4/' + tilejson.id + '.html', this.options.accessToken),
- twitterURL = '//twitter.com/intent/tweet?status=' + name + ' ' + url,
- facebookURL = '//www.facebook.com/sharer.php?u=' + url + '&t=' + name,
- pinterestURL = '//www.pinterest.com/pin/create/button/?url=' + url + '&media=' + image + '&description=' + name,
- embedValue = '<iframe width="100%" height="500px" frameBorder="0" src="' + embed + '"></iframe>',
- embedLabel = 'Copy and paste this <strong>HTML code</strong> into documents to embed this map on web pages.';
- function createShareButton(buttonClass, href, socialMediaName) {
- var elem = document.createElement('a');
- elem.setAttribute('class', buttonClass);
- elem.setAttribute('href', href);
- elem.setAttribute('target', '_blank');
- socialMediaName = document.createTextNode(socialMediaName);
- elem.appendChild(socialMediaName);
- return elem;
- }
- L.DomUtil.addClass(this._modal, 'active');
- this._sharing = L.DomUtil.create('div', 'mapbox-modal-body', this._content);
- var twitterButton = createShareButton('mapbox-button mapbox-button-icon mapbox-icon-twitter', twitterURL, 'Twitter');
- var facebookButton = createShareButton('mapbox-button mapbox-button-icon mapbox-icon-facebook', facebookURL, 'Facebook');
- var pinterestButton = createShareButton('mapbox-button mapbox-button-icon mapbox-icon-pinterest', pinterestURL, 'Pinterest');
- var shareHeader = document.createElement('h3');
- var shareText = document.createTextNode('Share this map');
- shareHeader.appendChild(shareText);
- var shareButtons = document.createElement('div');
- shareButtons.setAttribute('class', 'mapbox-share-buttons');
- shareButtons.appendChild(facebookButton);
- shareButtons.appendChild(twitterButton);
- shareButtons.appendChild(pinterestButton);
- this._sharing.appendChild(shareHeader);
- this._sharing.appendChild(shareButtons);
- var input = L.DomUtil.create('input', 'mapbox-embed', this._sharing);
- input.type = 'text';
- input.value = embedValue;
- var label = L.DomUtil.create('label', 'mapbox-embed-description', this._sharing);
- label.innerHTML = embedLabel;
- var close = L.DomUtil.create('a', 'leaflet-popup-close-button', this._sharing);
- close.href = '#';
- L.DomEvent.disableClickPropagation(this._sharing);
- L.DomEvent.addListener(close, 'click', this._clickOut, this);
- L.DomEvent.addListener(input, 'click', function(e) {
- e.target.focus();
- e.target.select();
- });
- }
- });
- module.exports.ShareControl = ShareControl;
- module.exports.shareControl = function(_, options) {
- return new ShareControl(_, options);
- };
- },{"./format_url":11,"./load_tilejson":20}],27:[function(require,module,exports){
- 'use strict';
- // an implementation of the simplestyle spec for polygon and linestring features
- // https://github.com/mapbox/simplestyle-spec
- var defaults = {
- stroke: '#555555',
- 'stroke-width': 2,
- 'stroke-opacity': 1,
- fill: '#555555',
- 'fill-opacity': 0.5
- };
- var mapping = [
- ['stroke', 'color'],
- ['stroke-width', 'weight'],
- ['stroke-opacity', 'opacity'],
- ['fill', 'fillColor'],
- ['fill-opacity', 'fillOpacity']
- ];
- function fallback(a, b) {
- var c = {};
- for (var k in b) {
- if (a[k] === undefined) c[k] = b[k];
- else c[k] = a[k];
- }
- return c;
- }
- function remap(a) {
- var d = {};
- for (var i = 0; i < mapping.length; i++) {
- d[mapping[i][1]] = a[mapping[i][0]];
- }
- return d;
- }
- function style(feature) {
- return remap(fallback(feature.properties || {}, defaults));
- }
- module.exports = {
- style: style,
- defaults: defaults
- };
- },{}],28:[function(require,module,exports){
- 'use strict';
- var util = require('./util');
- var format_url = require('./format_url');
- var request = require('./request');
- var StyleLayer = L.TileLayer.extend({
- options: {
- sanitizer: require('sanitize-caja')
- },
- initialize: function(_, options) {
- L.TileLayer.prototype.initialize.call(this, undefined, L.extend({}, options, {
- tileSize: 512,
- zoomOffset: -1,
- minNativeZoom: 0,
- tms: false
- }));
- this._url = this._formatTileURL(_);
- this._getAttribution(_);
- },
- _getAttribution: function(_) {
- var styleURL = format_url.style(_, this.options && this.options.accessToken);
- request(styleURL, L.bind(function(err, style) {
- if (err) {
- util.log('could not load Mapbox style at ' + styleURL);
- this.fire('error', {error: err});
- }
- var sources = [];
- for (var id in style.sources) {
- var source = style.sources[id].url.split('mapbox://')[1];
- sources.push(source);
- }
- request(format_url.tileJSON(sources.join(), this.options.accessToken), L.bind(function(err, json) {
- if (err) {
- util.log('could not load TileJSON at ' + _);
- this.fire('error', {error: err});
- } else if (json) {
- util.strict(json, 'object');
- this.options.attribution = this.options.sanitizer(json.attribution);
- this._tilejson = json;
- this.fire('ready');
- }
- }, this));
- }, this));
- },
- // disable the setUrl function, which is not available on mapbox tilelayers
- setUrl: null,
- _formatTileURL: function(style) {
- if (typeof style === 'string') {
- if (style.indexOf('mapbox://styles/') === -1) {
- util.log('Incorrectly formatted Mapbox style at ' + style);
- this.fire('error');
- }
- var ownerIDStyle = style.split('mapbox://styles/')[1];
- return format_url('/styles/v1/' + ownerIDStyle + '/tiles/{z}/{x}/{y}{r}', this.options.accessToken);
- } else if (typeof style === 'object') {
- return format_url('/styles/v1/' + style.owner + '/' + style.id + '/tiles/{z}/{x}/{y}{r}', this.options.accessToken);
- }
- }
- });
- module.exports.StyleLayer = StyleLayer;
- module.exports.styleLayer = function(_, options) {
- return new StyleLayer(_, options);
- };
- },{"./format_url":11,"./request":25,"./util":30,"sanitize-caja":5}],29:[function(require,module,exports){
- 'use strict';
- var util = require('./util');
- var formatPattern = /\.((?:png|jpg)\d*)(?=$|\?)/;
- var TileLayer = L.TileLayer.extend({
- includes: [require('./load_tilejson')],
- options: {
- sanitizer: require('sanitize-caja')
- },
- // http://mapbox.com/developers/api/#image_quality
- formats: [
- 'png', 'jpg',
- // PNG
- 'png32', 'png64', 'png128', 'png256',
- // JPG
- 'jpg70', 'jpg80', 'jpg90'],
- scalePrefix: '@2x.',
- initialize: function(_, options) {
- L.TileLayer.prototype.initialize.call(this, undefined, options);
- this._tilejson = {};
- if (options && options.format) {
- util.strict_oneof(options.format, this.formats);
- }
- this._loadTileJSON(_);
- },
- setFormat: function(_) {
- util.strict(_, 'string');
- this.options.format = _;
- this.redraw();
- return this;
- },
- // disable the setUrl function, which is not available on mapbox tilelayers
- setUrl: null,
- _setTileJSON: function(json) {
- util.strict(json, 'object');
- if (!this.options.format) {
- var match = json.tiles[0].match(formatPattern);
- if (match) {
- this.options.format = match[1];
- }
- }
- L.extend(this.options, {
- tiles: json.tiles,
- attribution: this.options.sanitizer(json.attribution),
- minZoom: json.minzoom || 0,
- maxZoom: json.maxzoom || 18,
- tms: json.scheme === 'tms',
- bounds: json.bounds && util.lbounds(json.bounds)
- });
- this._tilejson = json;
- this.redraw();
- return this;
- },
- getTileJSON: function() {
- return this._tilejson;
- },
- // this is an exception to mapbox.js naming rules because it's called
- // by `L.map`
- getTileUrl: function(tilePoint) {
- var tiles = this.options.tiles,
- index = Math.floor(Math.abs(tilePoint.x + tilePoint.y) % tiles.length),
- url = tiles[index];
- var templated = L.Util.template(url, tilePoint);
- if (!templated || !this.options.format) {
- return templated;
- } else {
- return templated.replace(formatPattern,
- (L.Browser.retina ? this.scalePrefix : '.') + this.options.format);
- }
- },
- // TileJSON.TileLayers are added to the map immediately, so that they get
- // the desired z-index, but do not update until the TileJSON has been loaded.
- _update: function() {
- if (this.options.tiles) {
- L.TileLayer.prototype._update.call(this);
- }
- }
- });
- module.exports.TileLayer = TileLayer;
- module.exports.tileLayer = function(_, options) {
- return new TileLayer(_, options);
- };
- },{"./load_tilejson":20,"./util":30,"sanitize-caja":5}],30:[function(require,module,exports){
- 'use strict';
- function contains(item, list) {
- if (!list || !list.length) return false;
- for (var i = 0; i < list.length; i++) {
- if (list[i] === item) return true;
- }
- return false;
- }
- module.exports = {
- idUrl: function(_, t) {
- if (_.indexOf('/') === -1) t.loadID(_);
- else t.loadURL(_);
- },
- log: function(_) {
- if (typeof console === 'object' &&
- typeof console.error === 'function') {
- console.error(_);
- }
- },
- strict: function(_, type) {
- if (typeof _ !== type) {
- throw new Error('Invalid argument: ' + type + ' expected');
- }
- },
- strict_instance: function(_, klass, name) {
- if (!(_ instanceof klass)) {
- throw new Error('Invalid argument: ' + name + ' expected');
- }
- },
- strict_oneof: function(_, values) {
- if (!contains(_, values)) {
- throw new Error('Invalid argument: ' + _ + ' given, valid values are ' +
- values.join(', '));
- }
- },
- strip_tags: function(_) {
- return _.replace(/<[^<]+>/g, '');
- },
- lbounds: function(_) {
- // leaflet-compatible bounds, since leaflet does not do geojson
- return new L.LatLngBounds([[_[1], _[0]], [_[3], _[2]]]);
- }
- };
- },{}]},{},[17])
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJub2RlX21vZHVsZXMvY29yc2xpdGUvY29yc2xpdGUuanMiLCJub2RlX21vZHVsZXMvaXNhcnJheS9pbmRleC5qcyIsIm5vZGVfbW9kdWxlcy9sZWFmbGV0L2Rpc3QvbGVhZmxldC1zcmMuanMiLCJub2RlX21vZHVsZXMvbXVzdGFjaGUvbXVzdGFjaGUuanMiLCJub2RlX21vZHVsZXMvc2FuaXRpemUtY2FqYS9pbmRleC5qcyIsIm5vZGVfbW9kdWxlcy9zYW5pdGl6ZS1jYWphL3Nhbml0aXplci1idW5kbGUuanMiLCJwYWNrYWdlLmpzb24iLCJzcmMvY29uZmlnLmpzIiwic3JjL2ZlYXR1cmVfbGF5ZXIuanMiLCJzcmMvZmVlZGJhY2suanMiLCJzcmMvZm9ybWF0X3VybC5qcyIsInNyYy9nZW9jb2Rlci5qcyIsInNyYy9nZW9jb2Rlcl9jb250cm9sLmpzIiwic3JjL2dyaWQuanMiLCJzcmMvZ3JpZF9jb250cm9sLmpzIiwic3JjL2dyaWRfbGF5ZXIuanMiLCJzcmMvaW5kZXguanMiLCJzcmMvbGVhZmxldC5qcyIsInNyYy9sZWdlbmRfY29udHJvbC5qcyIsInNyYy9sb2FkX3RpbGVqc29uLmpzIiwic3JjL21hcC5qcyIsInNyYy9tYXBib3guanMiLCJzcmMvbWFwYm94X2xvZ28uanMiLCJzcmMvbWFya2VyLmpzIiwic3JjL3JlcXVlc3QuanMiLCJzcmMvc2hhcmVfY29udHJvbC5qcyIsInNyYy9zaW1wbGVzdHlsZS5qcyIsInNyYy9zdHlsZV9sYXllci5qcyIsInNyYy90aWxlX2xheWVyLmpzIiwic3JjL3V0aWwuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM3RkE7QUFDQTtBQUNBO0FBQ0E7O0FDSEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2ozWkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3JuQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbEJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMvNEVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDakRBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNSQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNqSUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDWkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNURBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3RKQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1TUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNoQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdE1BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMxTkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNQQTtBQUNBOztBQ0RBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbkVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3hCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDMU5BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ25EQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDaENBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNqRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2hDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pIQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM3Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN6RUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdEdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsImZ1bmN0aW9uIGNvcnNsaXRlKHVybCwgY2FsbGJhY2ssIGNvcnMpIHtcbiAgICB2YXIgc2VudCA9IGZhbHNlO1xuXG4gICAgaWYgKHR5cGVvZiB3aW5kb3cuWE1MSHR0cFJlcXVlc3QgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgIHJldHVybiBjYWxsYmFjayhFcnJvcignQnJvd3NlciBub3Qgc3VwcG9ydGVkJykpO1xuICAgIH1cblxuICAgIGlmICh0eXBlb2YgY29ycyA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgdmFyIG0gPSB1cmwubWF0Y2goL15cXHMqaHR0cHM/OlxcL1xcL1teXFwvXSovKTtcbiAgICAgICAgY29ycyA9IG0gJiYgKG1bMF0gIT09IGxvY2F0aW9uLnByb3RvY29sICsgJy8vJyArIGxvY2F0aW9uLmRvbWFpbiArXG4gICAgICAgICAgICAgICAgKGxvY2F0aW9uLnBvcnQgPyAnOicgKyBsb2NhdGlvbi5wb3J0IDogJycpKTtcbiAgICB9XG5cbiAgICB2YXIgeCA9IG5ldyB3aW5kb3cuWE1MSHR0cFJlcXVlc3QoKTtcblxuICAgIGZ1bmN0aW9uIGlzU3VjY2Vzc2Z1bChzdGF0dXMpIHtcbiAgICAgICAgcmV0dXJuIHN0YXR1cyA+PSAyMDAgJiYgc3RhdHVzIDwgMzAwIHx8IHN0YXR1cyA9PT0gMzA0O1xuICAgIH1cblxuICAgIGlmIChjb3JzICYmICEoJ3dpdGhDcmVkZW50aWFscycgaW4geCkpIHtcbiAgICAgICAgLy8gSUU4LTlcbiAgICAgICAgeCA9IG5ldyB3aW5kb3cuWERvbWFpblJlcXVlc3QoKTtcblxuICAgICAgICAvLyBFbnN1cmUgY2FsbGJhY2sgaXMgbmV2ZXIgY2FsbGVkIHN5bmNocm9ub3VzbHksIGkuZS4sIGJlZm9yZVxuICAgICAgICAvLyB4LnNlbmQoKSByZXR1cm5zICh0aGlzIGhhcyBiZWVuIG9ic2VydmVkIGluIHRoZSB3aWxkKS5cbiAgICAgICAgLy8gU2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9tYXBib3gvbWFwYm94LmpzL2lzc3Vlcy80NzJcbiAgICAgICAgdmFyIG9yaWdpbmFsID0gY2FsbGJhY2s7XG4gICAgICAgIGNhbGxiYWNrID0gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICBpZiAoc2VudCkge1xuICAgICAgICAgICAgICAgIG9yaWdpbmFsLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHZhciB0aGF0ID0gdGhpcywgYXJncyA9IGFyZ3VtZW50cztcbiAgICAgICAgICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICAgICAgICBvcmlnaW5hbC5hcHBseSh0aGF0LCBhcmdzKTtcbiAgICAgICAgICAgICAgICB9LCAwKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIGZ1bmN0aW9uIGxvYWRlZCgpIHtcbiAgICAgICAgaWYgKFxuICAgICAgICAgICAgLy8gWERvbWFpblJlcXVlc3RcbiAgICAgICAgICAgIHguc3RhdHVzID09PSB1bmRlZmluZWQgfHxcbiAgICAgICAgICAgIC8vIG1vZGVybiBicm93c2Vyc1xuICAgICAgICAgICAgaXNTdWNjZXNzZnVsKHguc3RhdHVzKSkgY2FsbGJhY2suY2FsbCh4LCBudWxsLCB4KTtcbiAgICAgICAgZWxzZSBjYWxsYmFjay5jYWxsKHgsIHgsIG51bGwpO1xuICAgIH1cblxuICAgIC8vIEJvdGggYG9ucmVhZHlzdGF0ZWNoYW5nZWAgYW5kIGBvbmxvYWRgIGNhbiBmaXJlLiBgb25yZWFkeXN0YXRlY2hhbmdlYFxuICAgIC8vIGhhcyBbYmVlbiBzdXBwb3J0ZWQgZm9yIGxvbmdlcl0oaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL2EvOTE4MTUwOC8yMjkwMDEpLlxuICAgIGlmICgnb25sb2FkJyBpbiB4KSB7XG4gICAgICAgIHgub25sb2FkID0gbG9hZGVkO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIHgub25yZWFkeXN0YXRlY2hhbmdlID0gZnVuY3Rpb24gcmVhZHlzdGF0ZSgpIHtcbiAgICAgICAgICAgIGlmICh4LnJlYWR5U3RhdGUgPT09IDQpIHtcbiAgICAgICAgICAgICAgICBsb2FkZWQoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICB9XG5cbiAgICAvLyBDYWxsIHRoZSBjYWxsYmFjayB3aXRoIHRoZSBYTUxIdHRwUmVxdWVzdCBvYmplY3QgYXMgYW4gZXJyb3IgYW5kIHByZXZlbnRcbiAgICAvLyBpdCBmcm9tIGV2ZXIgYmVpbmcgY2FsbGVkIGFnYWluIGJ5IHJlYXNzaWduaW5nIGl0IHRvIGBub29wYFxuICAgIHgub25lcnJvciA9IGZ1bmN0aW9uIGVycm9yKGV2dCkge1xuICAgICAgICAvLyBYRG9tYWluUmVxdWVzdCBwcm92aWRlcyBubyBldnQgcGFyYW1ldGVyXG4gICAgICAgIGNhbGxiYWNrLmNhbGwodGhpcywgZXZ0IHx8IHRydWUsIG51bGwpO1xuICAgICAgICBjYWxsYmFjayA9IGZ1bmN0aW9uKCkgeyB9O1xuICAgIH07XG5cbiAgICAvLyBJRTkgbXVzdCBoYXZlIG9ucHJvZ3Jlc3MgYmUgc2V0IHRvIGEgdW5pcXVlIGZ1bmN0aW9uLlxuICAgIHgub25wcm9ncmVzcyA9IGZ1bmN0aW9uKCkgeyB9O1xuXG4gICAgeC5vbnRpbWVvdXQgPSBmdW5jdGlvbihldnQpIHtcbiAgICAgICAgY2FsbGJhY2suY2FsbCh0aGlzLCBldnQsIG51bGwpO1xuICAgICAgICBjYWxsYmFjayA9IGZ1bmN0aW9uKCkgeyB9O1xuICAgIH07XG5cbiAgICB4Lm9uYWJvcnQgPSBmdW5jdGlvbihldnQpIHtcbiAgICAgICAgY2FsbGJhY2suY2FsbCh0aGlzLCBldnQsIG51bGwpO1xuICAgICAgICBjYWxsYmFjayA9IGZ1bmN0aW9uKCkgeyB9O1xuICAgIH07XG5cbiAgICAvLyBHRVQgaXMgdGhlIG9ubHkgc3VwcG9ydGVkIEhUVFAgVmVyYiBieSBYRG9tYWluUmVxdWVzdCBhbmQgaXMgdGhlXG4gICAgLy8gb25seSBvbmUgc3VwcG9ydGVkIGhlcmUuXG4gICAgeC5vcGVuKCdHRVQnLCB1cmwsIHRydWUpO1xuXG4gICAgLy8gU2VuZCB0aGUgcmVxdWVzdC4gU2VuZGluZyBkYXRhIGlzIG5vdCBzdXBwb3J0ZWQuXG4gICAgeC5zZW5kKG51bGwpO1xuICAgIHNlbnQgPSB0cnVlO1xuXG4gICAgcmV0dXJuIHg7XG59XG5cbmlmICh0eXBlb2YgbW9kdWxlICE9PSAndW5kZWZpbmVkJykgbW9kdWxlLmV4cG9ydHMgPSBjb3JzbGl0ZTtcbiIsIm1vZHVsZS5leHBvcnRzID0gQXJyYXkuaXNBcnJheSB8fCBmdW5jdGlvbiAoYXJyKSB7XG4gIHJldHVybiBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwoYXJyKSA9PSAnW29iamVjdCBBcnJheV0nO1xufTtcbiIsIi8qXG4gTGVhZmxldCAxLjAuMiwgYSBKUyBsaWJyYXJ5IGZvciBpbnRlcmFjdGl2ZSBtYXBzLiBodHRwOi8vbGVhZmxldGpzLmNvbVxuIChjKSAyMDEwLTIwMTYgVmxhZGltaXIgQWdhZm9ua2luLCAoYykgMjAxMC0yMDExIENsb3VkTWFkZVxuKi9cbihmdW5jdGlvbiAod2luZG93LCBkb2N1bWVudCwgdW5kZWZpbmVkKSB7XHJcbnZhciBMID0ge1xyXG5cdHZlcnNpb246IFwiMS4wLjJcIlxyXG59O1xyXG5cclxuZnVuY3Rpb24gZXhwb3NlKCkge1xyXG5cdHZhciBvbGRMID0gd2luZG93Lkw7XHJcblxyXG5cdEwubm9Db25mbGljdCA9IGZ1bmN0aW9uICgpIHtcclxuXHRcdHdpbmRvdy5MID0gb2xkTDtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH07XHJcblxyXG5cdHdpbmRvdy5MID0gTDtcclxufVxyXG5cclxuLy8gZGVmaW5lIExlYWZsZXQgZm9yIE5vZGUgbW9kdWxlIHBhdHRlcm4gbG9hZGVycywgaW5jbHVkaW5nIEJyb3dzZXJpZnlcclxuaWYgKHR5cGVvZiBtb2R1bGUgPT09ICdvYmplY3QnICYmIHR5cGVvZiBtb2R1bGUuZXhwb3J0cyA9PT0gJ29iamVjdCcpIHtcclxuXHRtb2R1bGUuZXhwb3J0cyA9IEw7XHJcblxyXG4vLyBkZWZpbmUgTGVhZmxldCBhcyBhbiBBTUQgbW9kdWxlXHJcbn0gZWxzZSBpZiAodHlwZW9mIGRlZmluZSA9PT0gJ2Z1bmN0aW9uJyAmJiBkZWZpbmUuYW1kKSB7XHJcblx0ZGVmaW5lKEwpO1xyXG59XHJcblxyXG4vLyBkZWZpbmUgTGVhZmxldCBhcyBhIGdsb2JhbCBMIHZhcmlhYmxlLCBzYXZpbmcgdGhlIG9yaWdpbmFsIEwgdG8gcmVzdG9yZSBsYXRlciBpZiBuZWVkZWRcclxuaWYgKHR5cGVvZiB3aW5kb3cgIT09ICd1bmRlZmluZWQnKSB7XHJcblx0ZXhwb3NlKCk7XHJcbn1cclxuXG5cblxuLypcclxuICogQG5hbWVzcGFjZSBVdGlsXHJcbiAqXHJcbiAqIFZhcmlvdXMgdXRpbGl0eSBmdW5jdGlvbnMsIHVzZWQgYnkgTGVhZmxldCBpbnRlcm5hbGx5LlxyXG4gKi9cclxuXHJcbkwuVXRpbCA9IHtcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGV4dGVuZChkZXN0OiBPYmplY3QsIHNyYz86IE9iamVjdCk6IE9iamVjdFxyXG5cdC8vIE1lcmdlcyB0aGUgcHJvcGVydGllcyBvZiB0aGUgYHNyY2Agb2JqZWN0IChvciBtdWx0aXBsZSBvYmplY3RzKSBpbnRvIGBkZXN0YCBvYmplY3QgYW5kIHJldHVybnMgdGhlIGxhdHRlci4gSGFzIGFuIGBMLmV4dGVuZGAgc2hvcnRjdXQuXHJcblx0ZXh0ZW5kOiBmdW5jdGlvbiAoZGVzdCkge1xyXG5cdFx0dmFyIGksIGosIGxlbiwgc3JjO1xyXG5cclxuXHRcdGZvciAoaiA9IDEsIGxlbiA9IGFyZ3VtZW50cy5sZW5ndGg7IGogPCBsZW47IGorKykge1xyXG5cdFx0XHRzcmMgPSBhcmd1bWVudHNbal07XHJcblx0XHRcdGZvciAoaSBpbiBzcmMpIHtcclxuXHRcdFx0XHRkZXN0W2ldID0gc3JjW2ldO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gZGVzdDtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gY3JlYXRlKHByb3RvOiBPYmplY3QsIHByb3BlcnRpZXM/OiBPYmplY3QpOiBPYmplY3RcclxuXHQvLyBDb21wYXRpYmlsaXR5IHBvbHlmaWxsIGZvciBbT2JqZWN0LmNyZWF0ZV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvT2JqZWN0L2NyZWF0ZSlcclxuXHRjcmVhdGU6IE9iamVjdC5jcmVhdGUgfHwgKGZ1bmN0aW9uICgpIHtcclxuXHRcdGZ1bmN0aW9uIEYoKSB7fVxyXG5cdFx0cmV0dXJuIGZ1bmN0aW9uIChwcm90bykge1xyXG5cdFx0XHRGLnByb3RvdHlwZSA9IHByb3RvO1xyXG5cdFx0XHRyZXR1cm4gbmV3IEYoKTtcclxuXHRcdH07XHJcblx0fSkoKSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGJpbmQoZm46IEZ1bmN0aW9uLCDigKYpOiBGdW5jdGlvblxyXG5cdC8vIFJldHVybnMgYSBuZXcgZnVuY3Rpb24gYm91bmQgdG8gdGhlIGFyZ3VtZW50cyBwYXNzZWQsIGxpa2UgW0Z1bmN0aW9uLnByb3RvdHlwZS5iaW5kXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9GdW5jdGlvbi9iaW5kKS5cclxuXHQvLyBIYXMgYSBgTC5iaW5kKClgIHNob3J0Y3V0LlxyXG5cdGJpbmQ6IGZ1bmN0aW9uIChmbiwgb2JqKSB7XHJcblx0XHR2YXIgc2xpY2UgPSBBcnJheS5wcm90b3R5cGUuc2xpY2U7XHJcblxyXG5cdFx0aWYgKGZuLmJpbmQpIHtcclxuXHRcdFx0cmV0dXJuIGZuLmJpbmQuYXBwbHkoZm4sIHNsaWNlLmNhbGwoYXJndW1lbnRzLCAxKSk7XHJcblx0XHR9XHJcblxyXG5cdFx0dmFyIGFyZ3MgPSBzbGljZS5jYWxsKGFyZ3VtZW50cywgMik7XHJcblxyXG5cdFx0cmV0dXJuIGZ1bmN0aW9uICgpIHtcclxuXHRcdFx0cmV0dXJuIGZuLmFwcGx5KG9iaiwgYXJncy5sZW5ndGggPyBhcmdzLmNvbmNhdChzbGljZS5jYWxsKGFyZ3VtZW50cykpIDogYXJndW1lbnRzKTtcclxuXHRcdH07XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHN0YW1wKG9iajogT2JqZWN0KTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgdW5pcXVlIElEIG9mIGFuIG9iamVjdCwgYXNzaWdpbmcgaXQgb25lIGlmIGl0IGRvZXNuJ3QgaGF2ZSBpdC5cclxuXHRzdGFtcDogZnVuY3Rpb24gKG9iaikge1xyXG5cdFx0Lyplc2xpbnQtZGlzYWJsZSAqL1xyXG5cdFx0b2JqLl9sZWFmbGV0X2lkID0gb2JqLl9sZWFmbGV0X2lkIHx8ICsrTC5VdGlsLmxhc3RJZDtcclxuXHRcdHJldHVybiBvYmouX2xlYWZsZXRfaWQ7XHJcblx0XHQvKmVzbGludC1lbmFibGUgKi9cclxuXHR9LFxyXG5cclxuXHQvLyBAcHJvcGVydHkgbGFzdElkOiBOdW1iZXJcclxuXHQvLyBMYXN0IHVuaXF1ZSBJRCB1c2VkIGJ5IFtgc3RhbXAoKWBdKCN1dGlsLXN0YW1wKVxyXG5cdGxhc3RJZDogMCxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHRocm90dGxlKGZuOiBGdW5jdGlvbiwgdGltZTogTnVtYmVyLCBjb250ZXh0OiBPYmplY3QpOiBGdW5jdGlvblxyXG5cdC8vIFJldHVybnMgYSBmdW5jdGlvbiB3aGljaCBleGVjdXRlcyBmdW5jdGlvbiBgZm5gIHdpdGggdGhlIGdpdmVuIHNjb3BlIGBjb250ZXh0YFxyXG5cdC8vIChzbyB0aGF0IHRoZSBgdGhpc2Aga2V5d29yZCByZWZlcnMgdG8gYGNvbnRleHRgIGluc2lkZSBgZm5gJ3MgY29kZSkuIFRoZSBmdW5jdGlvblxyXG5cdC8vIGBmbmAgd2lsbCBiZSBjYWxsZWQgbm8gbW9yZSB0aGFuIG9uZSB0aW1lIHBlciBnaXZlbiBhbW91bnQgb2YgYHRpbWVgLiBUaGUgYXJndW1lbnRzXHJcblx0Ly8gcmVjZWl2ZWQgYnkgdGhlIGJvdW5kIGZ1bmN0aW9uIHdpbGwgYmUgYW55IGFyZ3VtZW50cyBwYXNzZWQgd2hlbiBiaW5kaW5nIHRoZVxyXG5cdC8vIGZ1bmN0aW9uLCBmb2xsb3dlZCBieSBhbnkgYXJndW1lbnRzIHBhc3NlZCB3aGVuIGludm9raW5nIHRoZSBib3VuZCBmdW5jdGlvbi5cclxuXHQvLyBIYXMgYW4gYEwuYmluZGAgc2hvcnRjdXQuXHJcblx0dGhyb3R0bGU6IGZ1bmN0aW9uIChmbiwgdGltZSwgY29udGV4dCkge1xyXG5cdFx0dmFyIGxvY2ssIGFyZ3MsIHdyYXBwZXJGbiwgbGF0ZXI7XHJcblxyXG5cdFx0bGF0ZXIgPSBmdW5jdGlvbiAoKSB7XHJcblx0XHRcdC8vIHJlc2V0IGxvY2sgYW5kIGNhbGwgaWYgcXVldWVkXHJcblx0XHRcdGxvY2sgPSBmYWxzZTtcclxuXHRcdFx0aWYgKGFyZ3MpIHtcclxuXHRcdFx0XHR3cmFwcGVyRm4uYXBwbHkoY29udGV4dCwgYXJncyk7XHJcblx0XHRcdFx0YXJncyA9IGZhbHNlO1xyXG5cdFx0XHR9XHJcblx0XHR9O1xyXG5cclxuXHRcdHdyYXBwZXJGbiA9IGZ1bmN0aW9uICgpIHtcclxuXHRcdFx0aWYgKGxvY2spIHtcclxuXHRcdFx0XHQvLyBjYWxsZWQgdG9vIHNvb24sIHF1ZXVlIHRvIGNhbGwgbGF0ZXJcclxuXHRcdFx0XHRhcmdzID0gYXJndW1lbnRzO1xyXG5cclxuXHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHQvLyBjYWxsIGFuZCBsb2NrIHVudGlsIGxhdGVyXHJcblx0XHRcdFx0Zm4uYXBwbHkoY29udGV4dCwgYXJndW1lbnRzKTtcclxuXHRcdFx0XHRzZXRUaW1lb3V0KGxhdGVyLCB0aW1lKTtcclxuXHRcdFx0XHRsb2NrID0gdHJ1ZTtcclxuXHRcdFx0fVxyXG5cdFx0fTtcclxuXHJcblx0XHRyZXR1cm4gd3JhcHBlckZuO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiB3cmFwTnVtKG51bTogTnVtYmVyLCByYW5nZTogTnVtYmVyW10sIGluY2x1ZGVNYXg/OiBCb29sZWFuKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgbnVtYmVyIGBudW1gIG1vZHVsbyBgcmFuZ2VgIGluIHN1Y2ggYSB3YXkgc28gaXQgbGllcyB3aXRoaW5cclxuXHQvLyBgcmFuZ2VbMF1gIGFuZCBgcmFuZ2VbMV1gLiBUaGUgcmV0dXJuZWQgdmFsdWUgd2lsbCBiZSBhbHdheXMgc21hbGxlciB0aGFuXHJcblx0Ly8gYHJhbmdlWzFdYCB1bmxlc3MgYGluY2x1ZGVNYXhgIGlzIHNldCB0byBgdHJ1ZWAuXHJcblx0d3JhcE51bTogZnVuY3Rpb24gKHgsIHJhbmdlLCBpbmNsdWRlTWF4KSB7XHJcblx0XHR2YXIgbWF4ID0gcmFuZ2VbMV0sXHJcblx0XHQgICAgbWluID0gcmFuZ2VbMF0sXHJcblx0XHQgICAgZCA9IG1heCAtIG1pbjtcclxuXHRcdHJldHVybiB4ID09PSBtYXggJiYgaW5jbHVkZU1heCA/IHggOiAoKHggLSBtaW4pICUgZCArIGQpICUgZCArIG1pbjtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gZmFsc2VGbigpOiBGdW5jdGlvblxyXG5cdC8vIFJldHVybnMgYSBmdW5jdGlvbiB3aGljaCBhbHdheXMgcmV0dXJucyBgZmFsc2VgLlxyXG5cdGZhbHNlRm46IGZ1bmN0aW9uICgpIHsgcmV0dXJuIGZhbHNlOyB9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gZm9ybWF0TnVtKG51bTogTnVtYmVyLCBkaWdpdHM/OiBOdW1iZXIpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBudW1iZXIgYG51bWAgcm91bmRlZCB0byBgZGlnaXRzYCBkZWNpbWFscywgb3IgdG8gNSBkZWNpbWFscyBieSBkZWZhdWx0LlxyXG5cdGZvcm1hdE51bTogZnVuY3Rpb24gKG51bSwgZGlnaXRzKSB7XHJcblx0XHR2YXIgcG93ID0gTWF0aC5wb3coMTAsIGRpZ2l0cyB8fCA1KTtcclxuXHRcdHJldHVybiBNYXRoLnJvdW5kKG51bSAqIHBvdykgLyBwb3c7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHRyaW0oc3RyOiBTdHJpbmcpOiBTdHJpbmdcclxuXHQvLyBDb21wYXRpYmlsaXR5IHBvbHlmaWxsIGZvciBbU3RyaW5nLnByb3RvdHlwZS50cmltXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9TdHJpbmcvVHJpbSlcclxuXHR0cmltOiBmdW5jdGlvbiAoc3RyKSB7XHJcblx0XHRyZXR1cm4gc3RyLnRyaW0gPyBzdHIudHJpbSgpIDogc3RyLnJlcGxhY2UoL15cXHMrfFxccyskL2csICcnKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gc3BsaXRXb3JkcyhzdHI6IFN0cmluZyk6IFN0cmluZ1tdXHJcblx0Ly8gVHJpbXMgYW5kIHNwbGl0cyB0aGUgc3RyaW5nIG9uIHdoaXRlc3BhY2UgYW5kIHJldHVybnMgdGhlIGFycmF5IG9mIHBhcnRzLlxyXG5cdHNwbGl0V29yZHM6IGZ1bmN0aW9uIChzdHIpIHtcclxuXHRcdHJldHVybiBMLlV0aWwudHJpbShzdHIpLnNwbGl0KC9cXHMrLyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHNldE9wdGlvbnMob2JqOiBPYmplY3QsIG9wdGlvbnM6IE9iamVjdCk6IE9iamVjdFxyXG5cdC8vIE1lcmdlcyB0aGUgZ2l2ZW4gcHJvcGVydGllcyB0byB0aGUgYG9wdGlvbnNgIG9mIHRoZSBgb2JqYCBvYmplY3QsIHJldHVybmluZyB0aGUgcmVzdWx0aW5nIG9wdGlvbnMuIFNlZSBgQ2xhc3Mgb3B0aW9uc2AuIEhhcyBhbiBgTC5zZXRPcHRpb25zYCBzaG9ydGN1dC5cclxuXHRzZXRPcHRpb25zOiBmdW5jdGlvbiAob2JqLCBvcHRpb25zKSB7XHJcblx0XHRpZiAoIW9iai5oYXNPd25Qcm9wZXJ0eSgnb3B0aW9ucycpKSB7XHJcblx0XHRcdG9iai5vcHRpb25zID0gb2JqLm9wdGlvbnMgPyBMLlV0aWwuY3JlYXRlKG9iai5vcHRpb25zKSA6IHt9O1xyXG5cdFx0fVxyXG5cdFx0Zm9yICh2YXIgaSBpbiBvcHRpb25zKSB7XHJcblx0XHRcdG9iai5vcHRpb25zW2ldID0gb3B0aW9uc1tpXTtcclxuXHRcdH1cclxuXHRcdHJldHVybiBvYmoub3B0aW9ucztcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gZ2V0UGFyYW1TdHJpbmcob2JqOiBPYmplY3QsIGV4aXN0aW5nVXJsPzogU3RyaW5nLCB1cHBlcmNhc2U/OiBCb29sZWFuKTogU3RyaW5nXHJcblx0Ly8gQ29udmVydHMgYW4gb2JqZWN0IGludG8gYSBwYXJhbWV0ZXIgVVJMIHN0cmluZywgZS5nLiBge2E6IFwiZm9vXCIsIGI6IFwiYmFyXCJ9YFxyXG5cdC8vIHRyYW5zbGF0ZXMgdG8gYCc/YT1mb28mYj1iYXInYC4gSWYgYGV4aXN0aW5nVXJsYCBpcyBzZXQsIHRoZSBwYXJhbWV0ZXJzIHdpbGxcclxuXHQvLyBiZSBhcHBlbmRlZCBhdCB0aGUgZW5kLiBJZiBgdXBwZXJjYXNlYCBpcyBgdHJ1ZWAsIHRoZSBwYXJhbWV0ZXIgbmFtZXMgd2lsbFxyXG5cdC8vIGJlIHVwcGVyY2FzZWQgKGUuZy4gYCc/QT1mb28mQj1iYXInYClcclxuXHRnZXRQYXJhbVN0cmluZzogZnVuY3Rpb24gKG9iaiwgZXhpc3RpbmdVcmwsIHVwcGVyY2FzZSkge1xyXG5cdFx0dmFyIHBhcmFtcyA9IFtdO1xyXG5cdFx0Zm9yICh2YXIgaSBpbiBvYmopIHtcclxuXHRcdFx0cGFyYW1zLnB1c2goZW5jb2RlVVJJQ29tcG9uZW50KHVwcGVyY2FzZSA/IGkudG9VcHBlckNhc2UoKSA6IGkpICsgJz0nICsgZW5jb2RlVVJJQ29tcG9uZW50KG9ialtpXSkpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuICgoIWV4aXN0aW5nVXJsIHx8IGV4aXN0aW5nVXJsLmluZGV4T2YoJz8nKSA9PT0gLTEpID8gJz8nIDogJyYnKSArIHBhcmFtcy5qb2luKCcmJyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHRlbXBsYXRlKHN0cjogU3RyaW5nLCBkYXRhOiBPYmplY3QpOiBTdHJpbmdcclxuXHQvLyBTaW1wbGUgdGVtcGxhdGluZyBmYWNpbGl0eSwgYWNjZXB0cyBhIHRlbXBsYXRlIHN0cmluZyBvZiB0aGUgZm9ybSBgJ0hlbGxvIHthfSwge2J9J2BcclxuXHQvLyBhbmQgYSBkYXRhIG9iamVjdCBsaWtlIGB7YTogJ2ZvbycsIGI6ICdiYXInfWAsIHJldHVybnMgZXZhbHVhdGVkIHN0cmluZ1xyXG5cdC8vIGAoJ0hlbGxvIGZvbywgYmFyJylgLiBZb3UgY2FuIGFsc28gc3BlY2lmeSBmdW5jdGlvbnMgaW5zdGVhZCBvZiBzdHJpbmdzIGZvclxyXG5cdC8vIGRhdGEgdmFsdWVzIOKAlCB0aGV5IHdpbGwgYmUgZXZhbHVhdGVkIHBhc3NpbmcgYGRhdGFgIGFzIGFuIGFyZ3VtZW50LlxyXG5cdHRlbXBsYXRlOiBmdW5jdGlvbiAoc3RyLCBkYXRhKSB7XHJcblx0XHRyZXR1cm4gc3RyLnJlcGxhY2UoTC5VdGlsLnRlbXBsYXRlUmUsIGZ1bmN0aW9uIChzdHIsIGtleSkge1xyXG5cdFx0XHR2YXIgdmFsdWUgPSBkYXRhW2tleV07XHJcblxyXG5cdFx0XHRpZiAodmFsdWUgPT09IHVuZGVmaW5lZCkge1xyXG5cdFx0XHRcdHRocm93IG5ldyBFcnJvcignTm8gdmFsdWUgcHJvdmlkZWQgZm9yIHZhcmlhYmxlICcgKyBzdHIpO1xyXG5cclxuXHRcdFx0fSBlbHNlIGlmICh0eXBlb2YgdmFsdWUgPT09ICdmdW5jdGlvbicpIHtcclxuXHRcdFx0XHR2YWx1ZSA9IHZhbHVlKGRhdGEpO1xyXG5cdFx0XHR9XHJcblx0XHRcdHJldHVybiB2YWx1ZTtcclxuXHRcdH0pO1xyXG5cdH0sXHJcblxyXG5cdHRlbXBsYXRlUmU6IC9cXHsgKihbXFx3X1xcLV0rKSAqXFx9L2csXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBpc0FycmF5KG9iaik6IEJvb2xlYW5cclxuXHQvLyBDb21wYXRpYmlsaXR5IHBvbHlmaWxsIGZvciBbQXJyYXkuaXNBcnJheV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvQXJyYXkvaXNBcnJheSlcclxuXHRpc0FycmF5OiBBcnJheS5pc0FycmF5IHx8IGZ1bmN0aW9uIChvYmopIHtcclxuXHRcdHJldHVybiAoT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKG9iaikgPT09ICdbb2JqZWN0IEFycmF5XScpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBpbmRleE9mKGFycmF5OiBBcnJheSwgZWw6IE9iamVjdCk6IE51bWJlclxyXG5cdC8vIENvbXBhdGliaWxpdHkgcG9seWZpbGwgZm9yIFtBcnJheS5wcm90b3R5cGUuaW5kZXhPZl0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvQXJyYXkvaW5kZXhPZilcclxuXHRpbmRleE9mOiBmdW5jdGlvbiAoYXJyYXksIGVsKSB7XHJcblx0XHRmb3IgKHZhciBpID0gMDsgaSA8IGFycmF5Lmxlbmd0aDsgaSsrKSB7XHJcblx0XHRcdGlmIChhcnJheVtpXSA9PT0gZWwpIHsgcmV0dXJuIGk7IH1cclxuXHRcdH1cclxuXHRcdHJldHVybiAtMTtcclxuXHR9LFxyXG5cclxuXHQvLyBAcHJvcGVydHkgZW1wdHlJbWFnZVVybDogU3RyaW5nXHJcblx0Ly8gRGF0YSBVUkkgc3RyaW5nIGNvbnRhaW5pbmcgYSBiYXNlNjQtZW5jb2RlZCBlbXB0eSBHSUYgaW1hZ2UuXHJcblx0Ly8gVXNlZCBhcyBhIGhhY2sgdG8gZnJlZSBtZW1vcnkgZnJvbSB1bnVzZWQgaW1hZ2VzIG9uIFdlYktpdC1wb3dlcmVkXHJcblx0Ly8gbW9iaWxlIGRldmljZXMgKGJ5IHNldHRpbmcgaW1hZ2UgYHNyY2AgdG8gdGhpcyBzdHJpbmcpLlxyXG5cdGVtcHR5SW1hZ2VVcmw6ICdkYXRhOmltYWdlL2dpZjtiYXNlNjQsUjBsR09EbGhBUUFCQUFEL0FDd0FBQUFBQVFBQkFBQUNBRHM9J1xyXG59O1xyXG5cclxuKGZ1bmN0aW9uICgpIHtcclxuXHQvLyBpbnNwaXJlZCBieSBodHRwOi8vcGF1bGlyaXNoLmNvbS8yMDExL3JlcXVlc3RhbmltYXRpb25mcmFtZS1mb3Itc21hcnQtYW5pbWF0aW5nL1xyXG5cclxuXHRmdW5jdGlvbiBnZXRQcmVmaXhlZChuYW1lKSB7XHJcblx0XHRyZXR1cm4gd2luZG93Wyd3ZWJraXQnICsgbmFtZV0gfHwgd2luZG93Wydtb3onICsgbmFtZV0gfHwgd2luZG93WydtcycgKyBuYW1lXTtcclxuXHR9XHJcblxyXG5cdHZhciBsYXN0VGltZSA9IDA7XHJcblxyXG5cdC8vIGZhbGxiYWNrIGZvciBJRSA3LThcclxuXHRmdW5jdGlvbiB0aW1lb3V0RGVmZXIoZm4pIHtcclxuXHRcdHZhciB0aW1lID0gK25ldyBEYXRlKCksXHJcblx0XHQgICAgdGltZVRvQ2FsbCA9IE1hdGgubWF4KDAsIDE2IC0gKHRpbWUgLSBsYXN0VGltZSkpO1xyXG5cclxuXHRcdGxhc3RUaW1lID0gdGltZSArIHRpbWVUb0NhbGw7XHJcblx0XHRyZXR1cm4gd2luZG93LnNldFRpbWVvdXQoZm4sIHRpbWVUb0NhbGwpO1xyXG5cdH1cclxuXHJcblx0dmFyIHJlcXVlc3RGbiA9IHdpbmRvdy5yZXF1ZXN0QW5pbWF0aW9uRnJhbWUgfHwgZ2V0UHJlZml4ZWQoJ1JlcXVlc3RBbmltYXRpb25GcmFtZScpIHx8IHRpbWVvdXREZWZlcixcclxuXHQgICAgY2FuY2VsRm4gPSB3aW5kb3cuY2FuY2VsQW5pbWF0aW9uRnJhbWUgfHwgZ2V0UHJlZml4ZWQoJ0NhbmNlbEFuaW1hdGlvbkZyYW1lJykgfHxcclxuXHQgICAgICAgICAgICAgICBnZXRQcmVmaXhlZCgnQ2FuY2VsUmVxdWVzdEFuaW1hdGlvbkZyYW1lJykgfHwgZnVuY3Rpb24gKGlkKSB7IHdpbmRvdy5jbGVhclRpbWVvdXQoaWQpOyB9O1xyXG5cclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHJlcXVlc3RBbmltRnJhbWUoZm46IEZ1bmN0aW9uLCBjb250ZXh0PzogT2JqZWN0LCBpbW1lZGlhdGU/OiBCb29sZWFuKTogTnVtYmVyXHJcblx0Ly8gU2NoZWR1bGVzIGBmbmAgdG8gYmUgZXhlY3V0ZWQgd2hlbiB0aGUgYnJvd3NlciByZXBhaW50cy4gYGZuYCBpcyBib3VuZCB0b1xyXG5cdC8vIGBjb250ZXh0YCBpZiBnaXZlbi4gV2hlbiBgaW1tZWRpYXRlYCBpcyBzZXQsIGBmbmAgaXMgY2FsbGVkIGltbWVkaWF0ZWx5IGlmXHJcblx0Ly8gdGhlIGJyb3dzZXIgZG9lc24ndCBoYXZlIG5hdGl2ZSBzdXBwb3J0IGZvclxyXG5cdC8vIFtgd2luZG93LnJlcXVlc3RBbmltYXRpb25GcmFtZWBdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0FQSS93aW5kb3cvcmVxdWVzdEFuaW1hdGlvbkZyYW1lKSxcclxuXHQvLyBvdGhlcndpc2UgaXQncyBkZWxheWVkLiBSZXR1cm5zIGEgcmVxdWVzdCBJRCB0aGF0IGNhbiBiZSB1c2VkIHRvIGNhbmNlbCB0aGUgcmVxdWVzdC5cclxuXHRMLlV0aWwucmVxdWVzdEFuaW1GcmFtZSA9IGZ1bmN0aW9uIChmbiwgY29udGV4dCwgaW1tZWRpYXRlKSB7XHJcblx0XHRpZiAoaW1tZWRpYXRlICYmIHJlcXVlc3RGbiA9PT0gdGltZW91dERlZmVyKSB7XHJcblx0XHRcdGZuLmNhbGwoY29udGV4dCk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRyZXR1cm4gcmVxdWVzdEZuLmNhbGwod2luZG93LCBMLmJpbmQoZm4sIGNvbnRleHQpKTtcclxuXHRcdH1cclxuXHR9O1xyXG5cclxuXHQvLyBAZnVuY3Rpb24gY2FuY2VsQW5pbUZyYW1lKGlkOiBOdW1iZXIpOiB1bmRlZmluZWRcclxuXHQvLyBDYW5jZWxzIGEgcHJldmlvdXMgYHJlcXVlc3RBbmltRnJhbWVgLiBTZWUgYWxzbyBbd2luZG93LmNhbmNlbEFuaW1hdGlvbkZyYW1lXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9BUEkvd2luZG93L2NhbmNlbEFuaW1hdGlvbkZyYW1lKS5cclxuXHRMLlV0aWwuY2FuY2VsQW5pbUZyYW1lID0gZnVuY3Rpb24gKGlkKSB7XHJcblx0XHRpZiAoaWQpIHtcclxuXHRcdFx0Y2FuY2VsRm4uY2FsbCh3aW5kb3csIGlkKTtcclxuXHRcdH1cclxuXHR9O1xyXG59KSgpO1xyXG5cclxuLy8gc2hvcnRjdXRzIGZvciBtb3N0IHVzZWQgdXRpbGl0eSBmdW5jdGlvbnNcclxuTC5leHRlbmQgPSBMLlV0aWwuZXh0ZW5kO1xyXG5MLmJpbmQgPSBMLlV0aWwuYmluZDtcclxuTC5zdGFtcCA9IEwuVXRpbC5zdGFtcDtcclxuTC5zZXRPcHRpb25zID0gTC5VdGlsLnNldE9wdGlvbnM7XHJcblxuXG5cblxyXG4vLyBAY2xhc3MgQ2xhc3NcclxuLy8gQGFrYSBMLkNsYXNzXHJcblxyXG4vLyBAc2VjdGlvblxyXG4vLyBAdW5pbmhlcml0YWJsZVxyXG5cclxuLy8gVGhhbmtzIHRvIEpvaG4gUmVzaWcgYW5kIERlYW4gRWR3YXJkcyBmb3IgaW5zcGlyYXRpb24hXHJcblxyXG5MLkNsYXNzID0gZnVuY3Rpb24gKCkge307XHJcblxyXG5MLkNsYXNzLmV4dGVuZCA9IGZ1bmN0aW9uIChwcm9wcykge1xyXG5cclxuXHQvLyBAZnVuY3Rpb24gZXh0ZW5kKHByb3BzOiBPYmplY3QpOiBGdW5jdGlvblxyXG5cdC8vIFtFeHRlbmRzIHRoZSBjdXJyZW50IGNsYXNzXSgjY2xhc3MtaW5oZXJpdGFuY2UpIGdpdmVuIHRoZSBwcm9wZXJ0aWVzIHRvIGJlIGluY2x1ZGVkLlxyXG5cdC8vIFJldHVybnMgYSBKYXZhc2NyaXB0IGZ1bmN0aW9uIHRoYXQgaXMgYSBjbGFzcyBjb25zdHJ1Y3RvciAodG8gYmUgY2FsbGVkIHdpdGggYG5ld2ApLlxyXG5cdHZhciBOZXdDbGFzcyA9IGZ1bmN0aW9uICgpIHtcclxuXHJcblx0XHQvLyBjYWxsIHRoZSBjb25zdHJ1Y3RvclxyXG5cdFx0aWYgKHRoaXMuaW5pdGlhbGl6ZSkge1xyXG5cdFx0XHR0aGlzLmluaXRpYWxpemUuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBjYWxsIGFsbCBjb25zdHJ1Y3RvciBob29rc1xyXG5cdFx0dGhpcy5jYWxsSW5pdEhvb2tzKCk7XHJcblx0fTtcclxuXHJcblx0dmFyIHBhcmVudFByb3RvID0gTmV3Q2xhc3MuX19zdXBlcl9fID0gdGhpcy5wcm90b3R5cGU7XHJcblxyXG5cdHZhciBwcm90byA9IEwuVXRpbC5jcmVhdGUocGFyZW50UHJvdG8pO1xyXG5cdHByb3RvLmNvbnN0cnVjdG9yID0gTmV3Q2xhc3M7XHJcblxyXG5cdE5ld0NsYXNzLnByb3RvdHlwZSA9IHByb3RvO1xyXG5cclxuXHQvLyBpbmhlcml0IHBhcmVudCdzIHN0YXRpY3NcclxuXHRmb3IgKHZhciBpIGluIHRoaXMpIHtcclxuXHRcdGlmICh0aGlzLmhhc093blByb3BlcnR5KGkpICYmIGkgIT09ICdwcm90b3R5cGUnKSB7XHJcblx0XHRcdE5ld0NsYXNzW2ldID0gdGhpc1tpXTtcclxuXHRcdH1cclxuXHR9XHJcblxyXG5cdC8vIG1peCBzdGF0aWMgcHJvcGVydGllcyBpbnRvIHRoZSBjbGFzc1xyXG5cdGlmIChwcm9wcy5zdGF0aWNzKSB7XHJcblx0XHRMLmV4dGVuZChOZXdDbGFzcywgcHJvcHMuc3RhdGljcyk7XHJcblx0XHRkZWxldGUgcHJvcHMuc3RhdGljcztcclxuXHR9XHJcblxyXG5cdC8vIG1peCBpbmNsdWRlcyBpbnRvIHRoZSBwcm90b3R5cGVcclxuXHRpZiAocHJvcHMuaW5jbHVkZXMpIHtcclxuXHRcdEwuVXRpbC5leHRlbmQuYXBwbHkobnVsbCwgW3Byb3RvXS5jb25jYXQocHJvcHMuaW5jbHVkZXMpKTtcclxuXHRcdGRlbGV0ZSBwcm9wcy5pbmNsdWRlcztcclxuXHR9XHJcblxyXG5cdC8vIG1lcmdlIG9wdGlvbnNcclxuXHRpZiAocHJvdG8ub3B0aW9ucykge1xyXG5cdFx0cHJvcHMub3B0aW9ucyA9IEwuVXRpbC5leHRlbmQoTC5VdGlsLmNyZWF0ZShwcm90by5vcHRpb25zKSwgcHJvcHMub3B0aW9ucyk7XHJcblx0fVxyXG5cclxuXHQvLyBtaXggZ2l2ZW4gcHJvcGVydGllcyBpbnRvIHRoZSBwcm90b3R5cGVcclxuXHRMLmV4dGVuZChwcm90bywgcHJvcHMpO1xyXG5cclxuXHRwcm90by5faW5pdEhvb2tzID0gW107XHJcblxyXG5cdC8vIGFkZCBtZXRob2QgZm9yIGNhbGxpbmcgYWxsIGhvb2tzXHJcblx0cHJvdG8uY2FsbEluaXRIb29rcyA9IGZ1bmN0aW9uICgpIHtcclxuXHJcblx0XHRpZiAodGhpcy5faW5pdEhvb2tzQ2FsbGVkKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdGlmIChwYXJlbnRQcm90by5jYWxsSW5pdEhvb2tzKSB7XHJcblx0XHRcdHBhcmVudFByb3RvLmNhbGxJbml0SG9va3MuY2FsbCh0aGlzKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9pbml0SG9va3NDYWxsZWQgPSB0cnVlO1xyXG5cclxuXHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSBwcm90by5faW5pdEhvb2tzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdHByb3RvLl9pbml0SG9va3NbaV0uY2FsbCh0aGlzKTtcclxuXHRcdH1cclxuXHR9O1xyXG5cclxuXHRyZXR1cm4gTmV3Q2xhc3M7XHJcbn07XHJcblxyXG5cclxuLy8gQGZ1bmN0aW9uIGluY2x1ZGUocHJvcGVydGllczogT2JqZWN0KTogdGhpc1xyXG4vLyBbSW5jbHVkZXMgYSBtaXhpbl0oI2NsYXNzLWluY2x1ZGVzKSBpbnRvIHRoZSBjdXJyZW50IGNsYXNzLlxyXG5MLkNsYXNzLmluY2x1ZGUgPSBmdW5jdGlvbiAocHJvcHMpIHtcclxuXHRMLmV4dGVuZCh0aGlzLnByb3RvdHlwZSwgcHJvcHMpO1xyXG5cdHJldHVybiB0aGlzO1xyXG59O1xyXG5cclxuLy8gQGZ1bmN0aW9uIG1lcmdlT3B0aW9ucyhvcHRpb25zOiBPYmplY3QpOiB0aGlzXHJcbi8vIFtNZXJnZXMgYG9wdGlvbnNgXSgjY2xhc3Mtb3B0aW9ucykgaW50byB0aGUgZGVmYXVsdHMgb2YgdGhlIGNsYXNzLlxyXG5MLkNsYXNzLm1lcmdlT3B0aW9ucyA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XHJcblx0TC5leHRlbmQodGhpcy5wcm90b3R5cGUub3B0aW9ucywgb3B0aW9ucyk7XHJcblx0cmV0dXJuIHRoaXM7XHJcbn07XHJcblxyXG4vLyBAZnVuY3Rpb24gYWRkSW5pdEhvb2soZm46IEZ1bmN0aW9uKTogdGhpc1xyXG4vLyBBZGRzIGEgW2NvbnN0cnVjdG9yIGhvb2tdKCNjbGFzcy1jb25zdHJ1Y3Rvci1ob29rcykgdG8gdGhlIGNsYXNzLlxyXG5MLkNsYXNzLmFkZEluaXRIb29rID0gZnVuY3Rpb24gKGZuKSB7IC8vIChGdW5jdGlvbikgfHwgKFN0cmluZywgYXJncy4uLilcclxuXHR2YXIgYXJncyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cywgMSk7XHJcblxyXG5cdHZhciBpbml0ID0gdHlwZW9mIGZuID09PSAnZnVuY3Rpb24nID8gZm4gOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzW2ZuXS5hcHBseSh0aGlzLCBhcmdzKTtcclxuXHR9O1xyXG5cclxuXHR0aGlzLnByb3RvdHlwZS5faW5pdEhvb2tzID0gdGhpcy5wcm90b3R5cGUuX2luaXRIb29rcyB8fCBbXTtcclxuXHR0aGlzLnByb3RvdHlwZS5faW5pdEhvb2tzLnB1c2goaW5pdCk7XHJcblx0cmV0dXJuIHRoaXM7XHJcbn07XHJcblxuXG5cbi8qXHJcbiAqIEBjbGFzcyBFdmVudGVkXHJcbiAqIEBha2EgTC5FdmVudGVkXHJcbiAqIEBpbmhlcml0cyBDbGFzc1xyXG4gKlxyXG4gKiBBIHNldCBvZiBtZXRob2RzIHNoYXJlZCBiZXR3ZWVuIGV2ZW50LXBvd2VyZWQgY2xhc3NlcyAobGlrZSBgTWFwYCBhbmQgYE1hcmtlcmApLiBHZW5lcmFsbHksIGV2ZW50cyBhbGxvdyB5b3UgdG8gZXhlY3V0ZSBzb21lIGZ1bmN0aW9uIHdoZW4gc29tZXRoaW5nIGhhcHBlbnMgd2l0aCBhbiBvYmplY3QgKGUuZy4gdGhlIHVzZXIgY2xpY2tzIG9uIHRoZSBtYXAsIGNhdXNpbmcgdGhlIG1hcCB0byBmaXJlIGAnY2xpY2snYCBldmVudCkuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIG1hcC5vbignY2xpY2snLCBmdW5jdGlvbihlKSB7XHJcbiAqIFx0YWxlcnQoZS5sYXRsbmcpO1xyXG4gKiB9ICk7XHJcbiAqIGBgYFxyXG4gKlxyXG4gKiBMZWFmbGV0IGRlYWxzIHdpdGggZXZlbnQgbGlzdGVuZXJzIGJ5IHJlZmVyZW5jZSwgc28gaWYgeW91IHdhbnQgdG8gYWRkIGEgbGlzdGVuZXIgYW5kIHRoZW4gcmVtb3ZlIGl0LCBkZWZpbmUgaXQgYXMgYSBmdW5jdGlvbjpcclxuICpcclxuICogYGBganNcclxuICogZnVuY3Rpb24gb25DbGljayhlKSB7IC4uLiB9XHJcbiAqXHJcbiAqIG1hcC5vbignY2xpY2snLCBvbkNsaWNrKTtcclxuICogbWFwLm9mZignY2xpY2snLCBvbkNsaWNrKTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxuXHJcbkwuRXZlbnRlZCA9IEwuQ2xhc3MuZXh0ZW5kKHtcclxuXHJcblx0LyogQG1ldGhvZCBvbih0eXBlOiBTdHJpbmcsIGZuOiBGdW5jdGlvbiwgY29udGV4dD86IE9iamVjdCk6IHRoaXNcclxuXHQgKiBBZGRzIGEgbGlzdGVuZXIgZnVuY3Rpb24gKGBmbmApIHRvIGEgcGFydGljdWxhciBldmVudCB0eXBlIG9mIHRoZSBvYmplY3QuIFlvdSBjYW4gb3B0aW9uYWxseSBzcGVjaWZ5IHRoZSBjb250ZXh0IG9mIHRoZSBsaXN0ZW5lciAob2JqZWN0IHRoZSB0aGlzIGtleXdvcmQgd2lsbCBwb2ludCB0bykuIFlvdSBjYW4gYWxzbyBwYXNzIHNldmVyYWwgc3BhY2Utc2VwYXJhdGVkIHR5cGVzIChlLmcuIGAnY2xpY2sgZGJsY2xpY2snYCkuXHJcblx0ICpcclxuXHQgKiBAYWx0ZXJuYXRpdmVcclxuXHQgKiBAbWV0aG9kIG9uKGV2ZW50TWFwOiBPYmplY3QpOiB0aGlzXHJcblx0ICogQWRkcyBhIHNldCBvZiB0eXBlL2xpc3RlbmVyIHBhaXJzLCBlLmcuIGB7Y2xpY2s6IG9uQ2xpY2ssIG1vdXNlbW92ZTogb25Nb3VzZU1vdmV9YFxyXG5cdCAqL1xyXG5cdG9uOiBmdW5jdGlvbiAodHlwZXMsIGZuLCBjb250ZXh0KSB7XHJcblxyXG5cdFx0Ly8gdHlwZXMgY2FuIGJlIGEgbWFwIG9mIHR5cGVzL2hhbmRsZXJzXHJcblx0XHRpZiAodHlwZW9mIHR5cGVzID09PSAnb2JqZWN0Jykge1xyXG5cdFx0XHRmb3IgKHZhciB0eXBlIGluIHR5cGVzKSB7XHJcblx0XHRcdFx0Ly8gd2UgZG9uJ3QgcHJvY2VzcyBzcGFjZS1zZXBhcmF0ZWQgZXZlbnRzIGhlcmUgZm9yIHBlcmZvcm1hbmNlO1xyXG5cdFx0XHRcdC8vIGl0J3MgYSBob3QgcGF0aCBzaW5jZSBMYXllciB1c2VzIHRoZSBvbihvYmopIHN5bnRheFxyXG5cdFx0XHRcdHRoaXMuX29uKHR5cGUsIHR5cGVzW3R5cGVdLCBmbik7XHJcblx0XHRcdH1cclxuXHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHQvLyB0eXBlcyBjYW4gYmUgYSBzdHJpbmcgb2Ygc3BhY2Utc2VwYXJhdGVkIHdvcmRzXHJcblx0XHRcdHR5cGVzID0gTC5VdGlsLnNwbGl0V29yZHModHlwZXMpO1xyXG5cclxuXHRcdFx0Zm9yICh2YXIgaSA9IDAsIGxlbiA9IHR5cGVzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdFx0dGhpcy5fb24odHlwZXNbaV0sIGZuLCBjb250ZXh0KTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8qIEBtZXRob2Qgb2ZmKHR5cGU6IFN0cmluZywgZm4/OiBGdW5jdGlvbiwgY29udGV4dD86IE9iamVjdCk6IHRoaXNcclxuXHQgKiBSZW1vdmVzIGEgcHJldmlvdXNseSBhZGRlZCBsaXN0ZW5lciBmdW5jdGlvbi4gSWYgbm8gZnVuY3Rpb24gaXMgc3BlY2lmaWVkLCBpdCB3aWxsIHJlbW92ZSBhbGwgdGhlIGxpc3RlbmVycyBvZiB0aGF0IHBhcnRpY3VsYXIgZXZlbnQgZnJvbSB0aGUgb2JqZWN0LiBOb3RlIHRoYXQgaWYgeW91IHBhc3NlZCBhIGN1c3RvbSBjb250ZXh0IHRvIGBvbmAsIHlvdSBtdXN0IHBhc3MgdGhlIHNhbWUgY29udGV4dCB0byBgb2ZmYCBpbiBvcmRlciB0byByZW1vdmUgdGhlIGxpc3RlbmVyLlxyXG5cdCAqXHJcblx0ICogQGFsdGVybmF0aXZlXHJcblx0ICogQG1ldGhvZCBvZmYoZXZlbnRNYXA6IE9iamVjdCk6IHRoaXNcclxuXHQgKiBSZW1vdmVzIGEgc2V0IG9mIHR5cGUvbGlzdGVuZXIgcGFpcnMuXHJcblx0ICpcclxuXHQgKiBAYWx0ZXJuYXRpdmVcclxuXHQgKiBAbWV0aG9kIG9mZjogdGhpc1xyXG5cdCAqIFJlbW92ZXMgYWxsIGxpc3RlbmVycyB0byBhbGwgZXZlbnRzIG9uIHRoZSBvYmplY3QuXHJcblx0ICovXHJcblx0b2ZmOiBmdW5jdGlvbiAodHlwZXMsIGZuLCBjb250ZXh0KSB7XHJcblxyXG5cdFx0aWYgKCF0eXBlcykge1xyXG5cdFx0XHQvLyBjbGVhciBhbGwgbGlzdGVuZXJzIGlmIGNhbGxlZCB3aXRob3V0IGFyZ3VtZW50c1xyXG5cdFx0XHRkZWxldGUgdGhpcy5fZXZlbnRzO1xyXG5cclxuXHRcdH0gZWxzZSBpZiAodHlwZW9mIHR5cGVzID09PSAnb2JqZWN0Jykge1xyXG5cdFx0XHRmb3IgKHZhciB0eXBlIGluIHR5cGVzKSB7XHJcblx0XHRcdFx0dGhpcy5fb2ZmKHR5cGUsIHR5cGVzW3R5cGVdLCBmbik7XHJcblx0XHRcdH1cclxuXHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHR0eXBlcyA9IEwuVXRpbC5zcGxpdFdvcmRzKHR5cGVzKTtcclxuXHJcblx0XHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSB0eXBlcy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRcdHRoaXMuX29mZih0eXBlc1tpXSwgZm4sIGNvbnRleHQpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gYXR0YWNoIGxpc3RlbmVyICh3aXRob3V0IHN5bnRhY3RpYyBzdWdhciBub3cpXHJcblx0X29uOiBmdW5jdGlvbiAodHlwZSwgZm4sIGNvbnRleHQpIHtcclxuXHRcdHRoaXMuX2V2ZW50cyA9IHRoaXMuX2V2ZW50cyB8fCB7fTtcclxuXHJcblx0XHQvKiBnZXQvaW5pdCBsaXN0ZW5lcnMgZm9yIHR5cGUgKi9cclxuXHRcdHZhciB0eXBlTGlzdGVuZXJzID0gdGhpcy5fZXZlbnRzW3R5cGVdO1xyXG5cdFx0aWYgKCF0eXBlTGlzdGVuZXJzKSB7XHJcblx0XHRcdHR5cGVMaXN0ZW5lcnMgPSBbXTtcclxuXHRcdFx0dGhpcy5fZXZlbnRzW3R5cGVdID0gdHlwZUxpc3RlbmVycztcclxuXHRcdH1cclxuXHJcblx0XHRpZiAoY29udGV4dCA9PT0gdGhpcykge1xyXG5cdFx0XHQvLyBMZXNzIG1lbW9yeSBmb290cHJpbnQuXHJcblx0XHRcdGNvbnRleHQgPSB1bmRlZmluZWQ7XHJcblx0XHR9XHJcblx0XHR2YXIgbmV3TGlzdGVuZXIgPSB7Zm46IGZuLCBjdHg6IGNvbnRleHR9LFxyXG5cdFx0ICAgIGxpc3RlbmVycyA9IHR5cGVMaXN0ZW5lcnM7XHJcblxyXG5cdFx0Ly8gY2hlY2sgaWYgZm4gYWxyZWFkeSB0aGVyZVxyXG5cdFx0Zm9yICh2YXIgaSA9IDAsIGxlbiA9IGxpc3RlbmVycy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRpZiAobGlzdGVuZXJzW2ldLmZuID09PSBmbiAmJiBsaXN0ZW5lcnNbaV0uY3R4ID09PSBjb250ZXh0KSB7XHJcblx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0bGlzdGVuZXJzLnB1c2gobmV3TGlzdGVuZXIpO1xyXG5cdFx0dHlwZUxpc3RlbmVycy5jb3VudCsrO1xyXG5cdH0sXHJcblxyXG5cdF9vZmY6IGZ1bmN0aW9uICh0eXBlLCBmbiwgY29udGV4dCkge1xyXG5cdFx0dmFyIGxpc3RlbmVycyxcclxuXHRcdCAgICBpLFxyXG5cdFx0ICAgIGxlbjtcclxuXHJcblx0XHRpZiAoIXRoaXMuX2V2ZW50cykgeyByZXR1cm47IH1cclxuXHJcblx0XHRsaXN0ZW5lcnMgPSB0aGlzLl9ldmVudHNbdHlwZV07XHJcblxyXG5cdFx0aWYgKCFsaXN0ZW5lcnMpIHtcclxuXHRcdFx0cmV0dXJuO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICghZm4pIHtcclxuXHRcdFx0Ly8gU2V0IGFsbCByZW1vdmVkIGxpc3RlbmVycyB0byBub29wIHNvIHRoZXkgYXJlIG5vdCBjYWxsZWQgaWYgcmVtb3ZlIGhhcHBlbnMgaW4gZmlyZVxyXG5cdFx0XHRmb3IgKGkgPSAwLCBsZW4gPSBsaXN0ZW5lcnMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0XHRsaXN0ZW5lcnNbaV0uZm4gPSBMLlV0aWwuZmFsc2VGbjtcclxuXHRcdFx0fVxyXG5cdFx0XHQvLyBjbGVhciBhbGwgbGlzdGVuZXJzIGZvciBhIHR5cGUgaWYgZnVuY3Rpb24gaXNuJ3Qgc3BlY2lmaWVkXHJcblx0XHRcdGRlbGV0ZSB0aGlzLl9ldmVudHNbdHlwZV07XHJcblx0XHRcdHJldHVybjtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAoY29udGV4dCA9PT0gdGhpcykge1xyXG5cdFx0XHRjb250ZXh0ID0gdW5kZWZpbmVkO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmIChsaXN0ZW5lcnMpIHtcclxuXHJcblx0XHRcdC8vIGZpbmQgZm4gYW5kIHJlbW92ZSBpdFxyXG5cdFx0XHRmb3IgKGkgPSAwLCBsZW4gPSBsaXN0ZW5lcnMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0XHR2YXIgbCA9IGxpc3RlbmVyc1tpXTtcclxuXHRcdFx0XHRpZiAobC5jdHggIT09IGNvbnRleHQpIHsgY29udGludWU7IH1cclxuXHRcdFx0XHRpZiAobC5mbiA9PT0gZm4pIHtcclxuXHJcblx0XHRcdFx0XHQvLyBzZXQgdGhlIHJlbW92ZWQgbGlzdGVuZXIgdG8gbm9vcCBzbyB0aGF0J3Mgbm90IGNhbGxlZCBpZiByZW1vdmUgaGFwcGVucyBpbiBmaXJlXHJcblx0XHRcdFx0XHRsLmZuID0gTC5VdGlsLmZhbHNlRm47XHJcblxyXG5cdFx0XHRcdFx0aWYgKHRoaXMuX2ZpcmluZ0NvdW50KSB7XHJcblx0XHRcdFx0XHRcdC8qIGNvcHkgYXJyYXkgaW4gY2FzZSBldmVudHMgYXJlIGJlaW5nIGZpcmVkICovXHJcblx0XHRcdFx0XHRcdHRoaXMuX2V2ZW50c1t0eXBlXSA9IGxpc3RlbmVycyA9IGxpc3RlbmVycy5zbGljZSgpO1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0bGlzdGVuZXJzLnNwbGljZShpLCAxKTtcclxuXHJcblx0XHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBmaXJlKHR5cGU6IFN0cmluZywgZGF0YT86IE9iamVjdCwgcHJvcGFnYXRlPzogQm9vbGVhbik6IHRoaXNcclxuXHQvLyBGaXJlcyBhbiBldmVudCBvZiB0aGUgc3BlY2lmaWVkIHR5cGUuIFlvdSBjYW4gb3B0aW9uYWxseSBwcm92aWRlIGFuIGRhdGFcclxuXHQvLyBvYmplY3Qg4oCUIHRoZSBmaXJzdCBhcmd1bWVudCBvZiB0aGUgbGlzdGVuZXIgZnVuY3Rpb24gd2lsbCBjb250YWluIGl0c1xyXG5cdC8vIHByb3BlcnRpZXMuIFRoZSBldmVudCBjYW4gb3B0aW9uYWxseSBiZSBwcm9wYWdhdGVkIHRvIGV2ZW50IHBhcmVudHMuXHJcblx0ZmlyZTogZnVuY3Rpb24gKHR5cGUsIGRhdGEsIHByb3BhZ2F0ZSkge1xyXG5cdFx0aWYgKCF0aGlzLmxpc3RlbnModHlwZSwgcHJvcGFnYXRlKSkgeyByZXR1cm4gdGhpczsgfVxyXG5cclxuXHRcdHZhciBldmVudCA9IEwuVXRpbC5leHRlbmQoe30sIGRhdGEsIHt0eXBlOiB0eXBlLCB0YXJnZXQ6IHRoaXN9KTtcclxuXHJcblx0XHRpZiAodGhpcy5fZXZlbnRzKSB7XHJcblx0XHRcdHZhciBsaXN0ZW5lcnMgPSB0aGlzLl9ldmVudHNbdHlwZV07XHJcblxyXG5cdFx0XHRpZiAobGlzdGVuZXJzKSB7XHJcblx0XHRcdFx0dGhpcy5fZmlyaW5nQ291bnQgPSAodGhpcy5fZmlyaW5nQ291bnQgKyAxKSB8fCAxO1xyXG5cdFx0XHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSBsaXN0ZW5lcnMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0XHRcdHZhciBsID0gbGlzdGVuZXJzW2ldO1xyXG5cdFx0XHRcdFx0bC5mbi5jYWxsKGwuY3R4IHx8IHRoaXMsIGV2ZW50KTtcclxuXHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdHRoaXMuX2ZpcmluZ0NvdW50LS07XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHRpZiAocHJvcGFnYXRlKSB7XHJcblx0XHRcdC8vIHByb3BhZ2F0ZSB0aGUgZXZlbnQgdG8gcGFyZW50cyAoc2V0IHdpdGggYWRkRXZlbnRQYXJlbnQpXHJcblx0XHRcdHRoaXMuX3Byb3BhZ2F0ZUV2ZW50KGV2ZW50KTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGxpc3RlbnModHlwZTogU3RyaW5nKTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIGEgcGFydGljdWxhciBldmVudCB0eXBlIGhhcyBhbnkgbGlzdGVuZXJzIGF0dGFjaGVkIHRvIGl0LlxyXG5cdGxpc3RlbnM6IGZ1bmN0aW9uICh0eXBlLCBwcm9wYWdhdGUpIHtcclxuXHRcdHZhciBsaXN0ZW5lcnMgPSB0aGlzLl9ldmVudHMgJiYgdGhpcy5fZXZlbnRzW3R5cGVdO1xyXG5cdFx0aWYgKGxpc3RlbmVycyAmJiBsaXN0ZW5lcnMubGVuZ3RoKSB7IHJldHVybiB0cnVlOyB9XHJcblxyXG5cdFx0aWYgKHByb3BhZ2F0ZSkge1xyXG5cdFx0XHQvLyBhbHNvIGNoZWNrIHBhcmVudHMgZm9yIGxpc3RlbmVycyBpZiBldmVudCBwcm9wYWdhdGVzXHJcblx0XHRcdGZvciAodmFyIGlkIGluIHRoaXMuX2V2ZW50UGFyZW50cykge1xyXG5cdFx0XHRcdGlmICh0aGlzLl9ldmVudFBhcmVudHNbaWRdLmxpc3RlbnModHlwZSwgcHJvcGFnYXRlKSkgeyByZXR1cm4gdHJ1ZTsgfVxyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gZmFsc2U7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBvbmNlKOKApik6IHRoaXNcclxuXHQvLyBCZWhhdmVzIGFzIFtgb24o4oCmKWBdKCNldmVudGVkLW9uKSwgZXhjZXB0IHRoZSBsaXN0ZW5lciB3aWxsIG9ubHkgZ2V0IGZpcmVkIG9uY2UgYW5kIHRoZW4gcmVtb3ZlZC5cclxuXHRvbmNlOiBmdW5jdGlvbiAodHlwZXMsIGZuLCBjb250ZXh0KSB7XHJcblxyXG5cdFx0aWYgKHR5cGVvZiB0eXBlcyA9PT0gJ29iamVjdCcpIHtcclxuXHRcdFx0Zm9yICh2YXIgdHlwZSBpbiB0eXBlcykge1xyXG5cdFx0XHRcdHRoaXMub25jZSh0eXBlLCB0eXBlc1t0eXBlXSwgZm4pO1xyXG5cdFx0XHR9XHJcblx0XHRcdHJldHVybiB0aGlzO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBoYW5kbGVyID0gTC5iaW5kKGZ1bmN0aW9uICgpIHtcclxuXHRcdFx0dGhpc1xyXG5cdFx0XHQgICAgLm9mZih0eXBlcywgZm4sIGNvbnRleHQpXHJcblx0XHRcdCAgICAub2ZmKHR5cGVzLCBoYW5kbGVyLCBjb250ZXh0KTtcclxuXHRcdH0sIHRoaXMpO1xyXG5cclxuXHRcdC8vIGFkZCBhIGxpc3RlbmVyIHRoYXQncyBleGVjdXRlZCBvbmNlIGFuZCByZW1vdmVkIGFmdGVyIHRoYXRcclxuXHRcdHJldHVybiB0aGlzXHJcblx0XHQgICAgLm9uKHR5cGVzLCBmbiwgY29udGV4dClcclxuXHRcdCAgICAub24odHlwZXMsIGhhbmRsZXIsIGNvbnRleHQpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgYWRkRXZlbnRQYXJlbnQob2JqOiBFdmVudGVkKTogdGhpc1xyXG5cdC8vIEFkZHMgYW4gZXZlbnQgcGFyZW50IC0gYW4gYEV2ZW50ZWRgIHRoYXQgd2lsbCByZWNlaXZlIHByb3BhZ2F0ZWQgZXZlbnRzXHJcblx0YWRkRXZlbnRQYXJlbnQ6IGZ1bmN0aW9uIChvYmopIHtcclxuXHRcdHRoaXMuX2V2ZW50UGFyZW50cyA9IHRoaXMuX2V2ZW50UGFyZW50cyB8fCB7fTtcclxuXHRcdHRoaXMuX2V2ZW50UGFyZW50c1tMLnN0YW1wKG9iaildID0gb2JqO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCByZW1vdmVFdmVudFBhcmVudChvYmo6IEV2ZW50ZWQpOiB0aGlzXHJcblx0Ly8gUmVtb3ZlcyBhbiBldmVudCBwYXJlbnQsIHNvIGl0IHdpbGwgc3RvcCByZWNlaXZpbmcgcHJvcGFnYXRlZCBldmVudHNcclxuXHRyZW1vdmVFdmVudFBhcmVudDogZnVuY3Rpb24gKG9iaikge1xyXG5cdFx0aWYgKHRoaXMuX2V2ZW50UGFyZW50cykge1xyXG5cdFx0XHRkZWxldGUgdGhpcy5fZXZlbnRQYXJlbnRzW0wuc3RhbXAob2JqKV07XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfcHJvcGFnYXRlRXZlbnQ6IGZ1bmN0aW9uIChlKSB7XHJcblx0XHRmb3IgKHZhciBpZCBpbiB0aGlzLl9ldmVudFBhcmVudHMpIHtcclxuXHRcdFx0dGhpcy5fZXZlbnRQYXJlbnRzW2lkXS5maXJlKGUudHlwZSwgTC5leHRlbmQoe2xheWVyOiBlLnRhcmdldH0sIGUpLCB0cnVlKTtcclxuXHRcdH1cclxuXHR9XHJcbn0pO1xyXG5cclxudmFyIHByb3RvID0gTC5FdmVudGVkLnByb3RvdHlwZTtcclxuXHJcbi8vIGFsaWFzZXM7IHdlIHNob3VsZCBkaXRjaCB0aG9zZSBldmVudHVhbGx5XHJcblxyXG4vLyBAbWV0aG9kIGFkZEV2ZW50TGlzdGVuZXIo4oCmKTogdGhpc1xyXG4vLyBBbGlhcyB0byBbYG9uKOKApilgXSgjZXZlbnRlZC1vbilcclxucHJvdG8uYWRkRXZlbnRMaXN0ZW5lciA9IHByb3RvLm9uO1xyXG5cclxuLy8gQG1ldGhvZCByZW1vdmVFdmVudExpc3RlbmVyKOKApik6IHRoaXNcclxuLy8gQWxpYXMgdG8gW2BvZmYo4oCmKWBdKCNldmVudGVkLW9mZilcclxuXHJcbi8vIEBtZXRob2QgY2xlYXJBbGxFdmVudExpc3RlbmVycyjigKYpOiB0aGlzXHJcbi8vIEFsaWFzIHRvIFtgb2ZmKClgXSgjZXZlbnRlZC1vZmYpXHJcbnByb3RvLnJlbW92ZUV2ZW50TGlzdGVuZXIgPSBwcm90by5jbGVhckFsbEV2ZW50TGlzdGVuZXJzID0gcHJvdG8ub2ZmO1xyXG5cclxuLy8gQG1ldGhvZCBhZGRPbmVUaW1lRXZlbnRMaXN0ZW5lcijigKYpOiB0aGlzXHJcbi8vIEFsaWFzIHRvIFtgb25jZSjigKYpYF0oI2V2ZW50ZWQtb25jZSlcclxucHJvdG8uYWRkT25lVGltZUV2ZW50TGlzdGVuZXIgPSBwcm90by5vbmNlO1xyXG5cclxuLy8gQG1ldGhvZCBmaXJlRXZlbnQo4oCmKTogdGhpc1xyXG4vLyBBbGlhcyB0byBbYGZpcmUo4oCmKWBdKCNldmVudGVkLWZpcmUpXHJcbnByb3RvLmZpcmVFdmVudCA9IHByb3RvLmZpcmU7XHJcblxyXG4vLyBAbWV0aG9kIGhhc0V2ZW50TGlzdGVuZXJzKOKApik6IEJvb2xlYW5cclxuLy8gQWxpYXMgdG8gW2BsaXN0ZW5zKOKApilgXSgjZXZlbnRlZC1saXN0ZW5zKVxyXG5wcm90by5oYXNFdmVudExpc3RlbmVycyA9IHByb3RvLmxpc3RlbnM7XHJcblxyXG5MLk1peGluID0ge0V2ZW50czogcHJvdG99O1xyXG5cblxuXG4vKlxyXG4gKiBAbmFtZXNwYWNlIEJyb3dzZXJcclxuICogQGFrYSBMLkJyb3dzZXJcclxuICpcclxuICogQSBuYW1lc3BhY2Ugd2l0aCBzdGF0aWMgcHJvcGVydGllcyBmb3IgYnJvd3Nlci9mZWF0dXJlIGRldGVjdGlvbiB1c2VkIGJ5IExlYWZsZXQgaW50ZXJuYWxseS5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYGBganNcclxuICogaWYgKEwuQnJvd3Nlci5pZWx0OSkge1xyXG4gKiAgIGFsZXJ0KCdVcGdyYWRlIHlvdXIgYnJvd3NlciwgZHVkZSEnKTtcclxuICogfVxyXG4gKiBgYGBcclxuICovXHJcblxyXG4oZnVuY3Rpb24gKCkge1xyXG5cclxuXHR2YXIgdWEgPSBuYXZpZ2F0b3IudXNlckFnZW50LnRvTG93ZXJDYXNlKCksXHJcblx0ICAgIGRvYyA9IGRvY3VtZW50LmRvY3VtZW50RWxlbWVudCxcclxuXHJcblx0ICAgIGllID0gJ0FjdGl2ZVhPYmplY3QnIGluIHdpbmRvdyxcclxuXHJcblx0ICAgIHdlYmtpdCAgICA9IHVhLmluZGV4T2YoJ3dlYmtpdCcpICE9PSAtMSxcclxuXHQgICAgcGhhbnRvbWpzID0gdWEuaW5kZXhPZigncGhhbnRvbScpICE9PSAtMSxcclxuXHQgICAgYW5kcm9pZDIzID0gdWEuc2VhcmNoKCdhbmRyb2lkIFsyM10nKSAhPT0gLTEsXHJcblx0ICAgIGNocm9tZSAgICA9IHVhLmluZGV4T2YoJ2Nocm9tZScpICE9PSAtMSxcclxuXHQgICAgZ2Vja28gICAgID0gdWEuaW5kZXhPZignZ2Vja28nKSAhPT0gLTEgICYmICF3ZWJraXQgJiYgIXdpbmRvdy5vcGVyYSAmJiAhaWUsXHJcblxyXG5cdCAgICB3aW4gPSBuYXZpZ2F0b3IucGxhdGZvcm0uaW5kZXhPZignV2luJykgPT09IDAsXHJcblxyXG5cdCAgICBtb2JpbGUgPSB0eXBlb2Ygb3JpZW50YXRpb24gIT09ICd1bmRlZmluZWQnIHx8IHVhLmluZGV4T2YoJ21vYmlsZScpICE9PSAtMSxcclxuXHQgICAgbXNQb2ludGVyID0gIXdpbmRvdy5Qb2ludGVyRXZlbnQgJiYgd2luZG93Lk1TUG9pbnRlckV2ZW50LFxyXG5cdCAgICBwb2ludGVyID0gd2luZG93LlBvaW50ZXJFdmVudCB8fCBtc1BvaW50ZXIsXHJcblxyXG5cdCAgICBpZTNkID0gaWUgJiYgKCd0cmFuc2l0aW9uJyBpbiBkb2Muc3R5bGUpLFxyXG5cdCAgICB3ZWJraXQzZCA9ICgnV2ViS2l0Q1NTTWF0cml4JyBpbiB3aW5kb3cpICYmICgnbTExJyBpbiBuZXcgd2luZG93LldlYktpdENTU01hdHJpeCgpKSAmJiAhYW5kcm9pZDIzLFxyXG5cdCAgICBnZWNrbzNkID0gJ01velBlcnNwZWN0aXZlJyBpbiBkb2Muc3R5bGUsXHJcblx0ICAgIG9wZXJhMTIgPSAnT1RyYW5zaXRpb24nIGluIGRvYy5zdHlsZTtcclxuXHJcblxyXG5cdHZhciB0b3VjaCA9ICF3aW5kb3cuTF9OT19UT1VDSCAmJiAocG9pbnRlciB8fCAnb250b3VjaHN0YXJ0JyBpbiB3aW5kb3cgfHxcclxuXHRcdFx0KHdpbmRvdy5Eb2N1bWVudFRvdWNoICYmIGRvY3VtZW50IGluc3RhbmNlb2Ygd2luZG93LkRvY3VtZW50VG91Y2gpKTtcclxuXHJcblx0TC5Ccm93c2VyID0ge1xyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBpZTogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIGZvciBhbGwgSW50ZXJuZXQgRXhwbG9yZXIgdmVyc2lvbnMgKG5vdCBFZGdlKS5cclxuXHRcdGllOiBpZSxcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgaWVsdDk6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgSW50ZXJuZXQgRXhwbG9yZXIgdmVyc2lvbnMgbGVzcyB0aGFuIDkuXHJcblx0XHRpZWx0OTogaWUgJiYgIWRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIsXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IGVkZ2U6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgdGhlIEVkZ2Ugd2ViIGJyb3dzZXIuXHJcblx0XHRlZGdlOiAnbXNMYXVuY2hVcmknIGluIG5hdmlnYXRvciAmJiAhKCdkb2N1bWVudE1vZGUnIGluIGRvY3VtZW50KSxcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgd2Via2l0OiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIHdlYmtpdC1iYXNlZCBicm93c2VycyBsaWtlIENocm9tZSBhbmQgU2FmYXJpIChpbmNsdWRpbmcgbW9iaWxlIHZlcnNpb25zKS5cclxuXHRcdHdlYmtpdDogd2Via2l0LFxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBnZWNrbzogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIGZvciBnZWNrby1iYXNlZCBicm93c2VycyBsaWtlIEZpcmVmb3guXHJcblx0XHRnZWNrbzogZ2Vja28sXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IGFuZHJvaWQ6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgYW55IGJyb3dzZXIgcnVubmluZyBvbiBhbiBBbmRyb2lkIHBsYXRmb3JtLlxyXG5cdFx0YW5kcm9pZDogdWEuaW5kZXhPZignYW5kcm9pZCcpICE9PSAtMSxcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgYW5kcm9pZDIzOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIGJyb3dzZXJzIHJ1bm5pbmcgb24gQW5kcm9pZCAyIG9yIEFuZHJvaWQgMy5cclxuXHRcdGFuZHJvaWQyMzogYW5kcm9pZDIzLFxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBjaHJvbWU6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgdGhlIENocm9tZSBicm93c2VyLlxyXG5cdFx0Y2hyb21lOiBjaHJvbWUsXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IHNhZmFyaTogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIGZvciB0aGUgU2FmYXJpIGJyb3dzZXIuXHJcblx0XHRzYWZhcmk6ICFjaHJvbWUgJiYgdWEuaW5kZXhPZignc2FmYXJpJykgIT09IC0xLFxyXG5cclxuXHJcblx0XHQvLyBAcHJvcGVydHkgd2luOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgd2hlbiB0aGUgYnJvd3NlciBpcyBydW5uaW5nIGluIGEgV2luZG93cyBwbGF0Zm9ybVxyXG5cdFx0d2luOiB3aW4sXHJcblxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBpZTNkOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIGFsbCBJbnRlcm5ldCBFeHBsb3JlciB2ZXJzaW9ucyBzdXBwb3J0aW5nIENTUyB0cmFuc2Zvcm1zLlxyXG5cdFx0aWUzZDogaWUzZCxcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgd2Via2l0M2Q6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3Igd2Via2l0LWJhc2VkIGJyb3dzZXJzIHN1cHBvcnRpbmcgQ1NTIHRyYW5zZm9ybXMuXHJcblx0XHR3ZWJraXQzZDogd2Via2l0M2QsXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IGdlY2tvM2Q6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgZ2Vja28tYmFzZWQgYnJvd3NlcnMgc3VwcG9ydGluZyBDU1MgdHJhbnNmb3Jtcy5cclxuXHRcdGdlY2tvM2Q6IGdlY2tvM2QsXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IG9wZXJhMTI6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgdGhlIE9wZXJhIGJyb3dzZXIgc3VwcG9ydGluZyBDU1MgdHJhbnNmb3JtcyAodmVyc2lvbiAxMiBvciBsYXRlcikuXHJcblx0XHRvcGVyYTEyOiBvcGVyYTEyLFxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBhbnkzZDogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIGZvciBhbGwgYnJvd3NlcnMgc3VwcG9ydGluZyBDU1MgdHJhbnNmb3Jtcy5cclxuXHRcdGFueTNkOiAhd2luZG93LkxfRElTQUJMRV8zRCAmJiAoaWUzZCB8fCB3ZWJraXQzZCB8fCBnZWNrbzNkKSAmJiAhb3BlcmExMiAmJiAhcGhhbnRvbWpzLFxyXG5cclxuXHJcblx0XHQvLyBAcHJvcGVydHkgbW9iaWxlOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIGFsbCBicm93c2VycyBydW5uaW5nIGluIGEgbW9iaWxlIGRldmljZS5cclxuXHRcdG1vYmlsZTogbW9iaWxlLFxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBtb2JpbGVXZWJraXQ6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgYWxsIHdlYmtpdC1iYXNlZCBicm93c2VycyBpbiBhIG1vYmlsZSBkZXZpY2UuXHJcblx0XHRtb2JpbGVXZWJraXQ6IG1vYmlsZSAmJiB3ZWJraXQsXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IG1vYmlsZVdlYmtpdDNkOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIGFsbCB3ZWJraXQtYmFzZWQgYnJvd3NlcnMgaW4gYSBtb2JpbGUgZGV2aWNlIHN1cHBvcnRpbmcgQ1NTIHRyYW5zZm9ybXMuXHJcblx0XHRtb2JpbGVXZWJraXQzZDogbW9iaWxlICYmIHdlYmtpdDNkLFxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBtb2JpbGVPcGVyYTogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIGZvciB0aGUgT3BlcmEgYnJvd3NlciBpbiBhIG1vYmlsZSBkZXZpY2UuXHJcblx0XHRtb2JpbGVPcGVyYTogbW9iaWxlICYmIHdpbmRvdy5vcGVyYSxcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgbW9iaWxlR2Vja286IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgZ2Vja28tYmFzZWQgYnJvd3NlcnMgcnVubmluZyBpbiBhIG1vYmlsZSBkZXZpY2UuXHJcblx0XHRtb2JpbGVHZWNrbzogbW9iaWxlICYmIGdlY2tvLFxyXG5cclxuXHJcblx0XHQvLyBAcHJvcGVydHkgdG91Y2g6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgYWxsIGJyb3dzZXJzIHN1cHBvcnRpbmcgW3RvdWNoIGV2ZW50c10oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvQVBJL1RvdWNoX2V2ZW50cykuXHJcblx0XHR0b3VjaDogISF0b3VjaCxcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgbXNQb2ludGVyOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIGJyb3dzZXJzIGltcGxlbWVudGluZyB0aGUgTWljcm9zb2Z0IHRvdWNoIGV2ZW50cyBtb2RlbCAobm90YWJseSBJRTEwKS5cclxuXHRcdG1zUG9pbnRlcjogISFtc1BvaW50ZXIsXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IHBvaW50ZXI6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgYWxsIGJyb3dzZXJzIHN1cHBvcnRpbmcgW3BvaW50ZXIgZXZlbnRzXShodHRwczovL21zZG4ubWljcm9zb2Z0LmNvbS9lbi11cy9saWJyYXJ5L2RuNDMzMjQ0JTI4dj12cy44NSUyOS5hc3B4KS5cclxuXHRcdHBvaW50ZXI6ICEhcG9pbnRlcixcclxuXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IHJldGluYTogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIGZvciBicm93c2VycyBvbiBhIGhpZ2gtcmVzb2x1dGlvbiBcInJldGluYVwiIHNjcmVlbi5cclxuXHRcdHJldGluYTogKHdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvIHx8ICh3aW5kb3cuc2NyZWVuLmRldmljZVhEUEkgLyB3aW5kb3cuc2NyZWVuLmxvZ2ljYWxYRFBJKSkgPiAxXHJcblx0fTtcclxuXHJcbn0oKSk7XHJcblxuXG5cbi8qXHJcbiAqIEBjbGFzcyBQb2ludFxyXG4gKiBAYWthIEwuUG9pbnRcclxuICpcclxuICogUmVwcmVzZW50cyBhIHBvaW50IHdpdGggYHhgIGFuZCBgeWAgY29vcmRpbmF0ZXMgaW4gcGl4ZWxzLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiB2YXIgcG9pbnQgPSBMLnBvaW50KDIwMCwgMzAwKTtcclxuICogYGBgXHJcbiAqXHJcbiAqIEFsbCBMZWFmbGV0IG1ldGhvZHMgYW5kIG9wdGlvbnMgdGhhdCBhY2NlcHQgYFBvaW50YCBvYmplY3RzIGFsc28gYWNjZXB0IHRoZW0gaW4gYSBzaW1wbGUgQXJyYXkgZm9ybSAodW5sZXNzIG5vdGVkIG90aGVyd2lzZSksIHNvIHRoZXNlIGxpbmVzIGFyZSBlcXVpdmFsZW50OlxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBtYXAucGFuQnkoWzIwMCwgMzAwXSk7XHJcbiAqIG1hcC5wYW5CeShMLnBvaW50KDIwMCwgMzAwKSk7XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcbkwuUG9pbnQgPSBmdW5jdGlvbiAoeCwgeSwgcm91bmQpIHtcclxuXHQvLyBAcHJvcGVydHkgeDogTnVtYmVyOyBUaGUgYHhgIGNvb3JkaW5hdGUgb2YgdGhlIHBvaW50XHJcblx0dGhpcy54ID0gKHJvdW5kID8gTWF0aC5yb3VuZCh4KSA6IHgpO1xyXG5cdC8vIEBwcm9wZXJ0eSB5OiBOdW1iZXI7IFRoZSBgeWAgY29vcmRpbmF0ZSBvZiB0aGUgcG9pbnRcclxuXHR0aGlzLnkgPSAocm91bmQgPyBNYXRoLnJvdW5kKHkpIDogeSk7XHJcbn07XHJcblxyXG5MLlBvaW50LnByb3RvdHlwZSA9IHtcclxuXHJcblx0Ly8gQG1ldGhvZCBjbG9uZSgpOiBQb2ludFxyXG5cdC8vIFJldHVybnMgYSBjb3B5IG9mIHRoZSBjdXJyZW50IHBvaW50LlxyXG5cdGNsb25lOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQodGhpcy54LCB0aGlzLnkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgYWRkKG90aGVyUG9pbnQ6IFBvaW50KTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIHRoZSByZXN1bHQgb2YgYWRkaXRpb24gb2YgdGhlIGN1cnJlbnQgYW5kIHRoZSBnaXZlbiBwb2ludHMuXHJcblx0YWRkOiBmdW5jdGlvbiAocG9pbnQpIHtcclxuXHRcdC8vIG5vbi1kZXN0cnVjdGl2ZSwgcmV0dXJucyBhIG5ldyBwb2ludFxyXG5cdFx0cmV0dXJuIHRoaXMuY2xvbmUoKS5fYWRkKEwucG9pbnQocG9pbnQpKTtcclxuXHR9LFxyXG5cclxuXHRfYWRkOiBmdW5jdGlvbiAocG9pbnQpIHtcclxuXHRcdC8vIGRlc3RydWN0aXZlLCB1c2VkIGRpcmVjdGx5IGZvciBwZXJmb3JtYW5jZSBpbiBzaXR1YXRpb25zIHdoZXJlIGl0J3Mgc2FmZSB0byBtb2RpZnkgZXhpc3RpbmcgcG9pbnRcclxuXHRcdHRoaXMueCArPSBwb2ludC54O1xyXG5cdFx0dGhpcy55ICs9IHBvaW50Lnk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHN1YnRyYWN0KG90aGVyUG9pbnQ6IFBvaW50KTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIHRoZSByZXN1bHQgb2Ygc3VidHJhY3Rpb24gb2YgdGhlIGdpdmVuIHBvaW50IGZyb20gdGhlIGN1cnJlbnQuXHJcblx0c3VidHJhY3Q6IGZ1bmN0aW9uIChwb2ludCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuY2xvbmUoKS5fc3VidHJhY3QoTC5wb2ludChwb2ludCkpO1xyXG5cdH0sXHJcblxyXG5cdF9zdWJ0cmFjdDogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHR0aGlzLnggLT0gcG9pbnQueDtcclxuXHRcdHRoaXMueSAtPSBwb2ludC55O1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBkaXZpZGVCeShudW06IE51bWJlcik6IFBvaW50XHJcblx0Ly8gUmV0dXJucyB0aGUgcmVzdWx0IG9mIGRpdmlzaW9uIG9mIHRoZSBjdXJyZW50IHBvaW50IGJ5IHRoZSBnaXZlbiBudW1iZXIuXHJcblx0ZGl2aWRlQnk6IGZ1bmN0aW9uIChudW0pIHtcclxuXHRcdHJldHVybiB0aGlzLmNsb25lKCkuX2RpdmlkZUJ5KG51bSk7XHJcblx0fSxcclxuXHJcblx0X2RpdmlkZUJ5OiBmdW5jdGlvbiAobnVtKSB7XHJcblx0XHR0aGlzLnggLz0gbnVtO1xyXG5cdFx0dGhpcy55IC89IG51bTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgbXVsdGlwbHlCeShudW06IE51bWJlcik6IFBvaW50XHJcblx0Ly8gUmV0dXJucyB0aGUgcmVzdWx0IG9mIG11bHRpcGxpY2F0aW9uIG9mIHRoZSBjdXJyZW50IHBvaW50IGJ5IHRoZSBnaXZlbiBudW1iZXIuXHJcblx0bXVsdGlwbHlCeTogZnVuY3Rpb24gKG51bSkge1xyXG5cdFx0cmV0dXJuIHRoaXMuY2xvbmUoKS5fbXVsdGlwbHlCeShudW0pO1xyXG5cdH0sXHJcblxyXG5cdF9tdWx0aXBseUJ5OiBmdW5jdGlvbiAobnVtKSB7XHJcblx0XHR0aGlzLnggKj0gbnVtO1xyXG5cdFx0dGhpcy55ICo9IG51bTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2NhbGVCeShzY2FsZTogUG9pbnQpOiBQb2ludFxyXG5cdC8vIE11bHRpcGx5IGVhY2ggY29vcmRpbmF0ZSBvZiB0aGUgY3VycmVudCBwb2ludCBieSBlYWNoIGNvb3JkaW5hdGUgb2ZcclxuXHQvLyBgc2NhbGVgLiBJbiBsaW5lYXIgYWxnZWJyYSB0ZXJtcywgbXVsdGlwbHkgdGhlIHBvaW50IGJ5IHRoZVxyXG5cdC8vIFtzY2FsaW5nIG1hdHJpeF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvU2NhbGluZ18lMjhnZW9tZXRyeSUyOSNNYXRyaXhfcmVwcmVzZW50YXRpb24pXHJcblx0Ly8gZGVmaW5lZCBieSBgc2NhbGVgLlxyXG5cdHNjYWxlQnk6IGZ1bmN0aW9uIChwb2ludCkge1xyXG5cdFx0cmV0dXJuIG5ldyBMLlBvaW50KHRoaXMueCAqIHBvaW50LngsIHRoaXMueSAqIHBvaW50LnkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgdW5zY2FsZUJ5KHNjYWxlOiBQb2ludCk6IFBvaW50XHJcblx0Ly8gSW52ZXJzZSBvZiBgc2NhbGVCeWAuIERpdmlkZSBlYWNoIGNvb3JkaW5hdGUgb2YgdGhlIGN1cnJlbnQgcG9pbnQgYnlcclxuXHQvLyBlYWNoIGNvb3JkaW5hdGUgb2YgYHNjYWxlYC5cclxuXHR1bnNjYWxlQnk6IGZ1bmN0aW9uIChwb2ludCkge1xyXG5cdFx0cmV0dXJuIG5ldyBMLlBvaW50KHRoaXMueCAvIHBvaW50LngsIHRoaXMueSAvIHBvaW50LnkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgcm91bmQoKTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIGEgY29weSBvZiB0aGUgY3VycmVudCBwb2ludCB3aXRoIHJvdW5kZWQgY29vcmRpbmF0ZXMuXHJcblx0cm91bmQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLmNsb25lKCkuX3JvdW5kKCk7XHJcblx0fSxcclxuXHJcblx0X3JvdW5kOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLnggPSBNYXRoLnJvdW5kKHRoaXMueCk7XHJcblx0XHR0aGlzLnkgPSBNYXRoLnJvdW5kKHRoaXMueSk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGZsb29yKCk6IFBvaW50XHJcblx0Ly8gUmV0dXJucyBhIGNvcHkgb2YgdGhlIGN1cnJlbnQgcG9pbnQgd2l0aCBmbG9vcmVkIGNvb3JkaW5hdGVzIChyb3VuZGVkIGRvd24pLlxyXG5cdGZsb29yOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5jbG9uZSgpLl9mbG9vcigpO1xyXG5cdH0sXHJcblxyXG5cdF9mbG9vcjogZnVuY3Rpb24gKCkge1xyXG5cdFx0dGhpcy54ID0gTWF0aC5mbG9vcih0aGlzLngpO1xyXG5cdFx0dGhpcy55ID0gTWF0aC5mbG9vcih0aGlzLnkpO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBjZWlsKCk6IFBvaW50XHJcblx0Ly8gUmV0dXJucyBhIGNvcHkgb2YgdGhlIGN1cnJlbnQgcG9pbnQgd2l0aCBjZWlsZWQgY29vcmRpbmF0ZXMgKHJvdW5kZWQgdXApLlxyXG5cdGNlaWw6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLmNsb25lKCkuX2NlaWwoKTtcclxuXHR9LFxyXG5cclxuXHRfY2VpbDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dGhpcy54ID0gTWF0aC5jZWlsKHRoaXMueCk7XHJcblx0XHR0aGlzLnkgPSBNYXRoLmNlaWwodGhpcy55KTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZGlzdGFuY2VUbyhvdGhlclBvaW50OiBQb2ludCk6IE51bWJlclxyXG5cdC8vIFJldHVybnMgdGhlIGNhcnRlc2lhbiBkaXN0YW5jZSBiZXR3ZWVuIHRoZSBjdXJyZW50IGFuZCB0aGUgZ2l2ZW4gcG9pbnRzLlxyXG5cdGRpc3RhbmNlVG86IGZ1bmN0aW9uIChwb2ludCkge1xyXG5cdFx0cG9pbnQgPSBMLnBvaW50KHBvaW50KTtcclxuXHJcblx0XHR2YXIgeCA9IHBvaW50LnggLSB0aGlzLngsXHJcblx0XHQgICAgeSA9IHBvaW50LnkgLSB0aGlzLnk7XHJcblxyXG5cdFx0cmV0dXJuIE1hdGguc3FydCh4ICogeCArIHkgKiB5KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGVxdWFscyhvdGhlclBvaW50OiBQb2ludCk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgZ2l2ZW4gcG9pbnQgaGFzIHRoZSBzYW1lIGNvb3JkaW5hdGVzLlxyXG5cdGVxdWFsczogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHRwb2ludCA9IEwucG9pbnQocG9pbnQpO1xyXG5cclxuXHRcdHJldHVybiBwb2ludC54ID09PSB0aGlzLnggJiZcclxuXHRcdCAgICAgICBwb2ludC55ID09PSB0aGlzLnk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBjb250YWlucyhvdGhlclBvaW50OiBQb2ludCk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiBib3RoIGNvb3JkaW5hdGVzIG9mIHRoZSBnaXZlbiBwb2ludCBhcmUgbGVzcyB0aGFuIHRoZSBjb3JyZXNwb25kaW5nIGN1cnJlbnQgcG9pbnQgY29vcmRpbmF0ZXMgKGluIGFic29sdXRlIHZhbHVlcykuXHJcblx0Y29udGFpbnM6IGZ1bmN0aW9uIChwb2ludCkge1xyXG5cdFx0cG9pbnQgPSBMLnBvaW50KHBvaW50KTtcclxuXHJcblx0XHRyZXR1cm4gTWF0aC5hYnMocG9pbnQueCkgPD0gTWF0aC5hYnModGhpcy54KSAmJlxyXG5cdFx0ICAgICAgIE1hdGguYWJzKHBvaW50LnkpIDw9IE1hdGguYWJzKHRoaXMueSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB0b1N0cmluZygpOiBTdHJpbmdcclxuXHQvLyBSZXR1cm5zIGEgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBwb2ludCBmb3IgZGVidWdnaW5nIHB1cnBvc2VzLlxyXG5cdHRvU3RyaW5nOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gJ1BvaW50KCcgK1xyXG5cdFx0ICAgICAgICBMLlV0aWwuZm9ybWF0TnVtKHRoaXMueCkgKyAnLCAnICtcclxuXHRcdCAgICAgICAgTC5VdGlsLmZvcm1hdE51bSh0aGlzLnkpICsgJyknO1xyXG5cdH1cclxufTtcclxuXHJcbi8vIEBmYWN0b3J5IEwucG9pbnQoeDogTnVtYmVyLCB5OiBOdW1iZXIsIHJvdW5kPzogQm9vbGVhbilcclxuLy8gQ3JlYXRlcyBhIFBvaW50IG9iamVjdCB3aXRoIHRoZSBnaXZlbiBgeGAgYW5kIGB5YCBjb29yZGluYXRlcy4gSWYgb3B0aW9uYWwgYHJvdW5kYCBpcyBzZXQgdG8gdHJ1ZSwgcm91bmRzIHRoZSBgeGAgYW5kIGB5YCB2YWx1ZXMuXHJcblxyXG4vLyBAYWx0ZXJuYXRpdmVcclxuLy8gQGZhY3RvcnkgTC5wb2ludChjb29yZHM6IE51bWJlcltdKVxyXG4vLyBFeHBlY3RzIGFuIGFycmF5IG9mIHRoZSBmb3JtIGBbeCwgeV1gIGluc3RlYWQuXHJcblxyXG4vLyBAYWx0ZXJuYXRpdmVcclxuLy8gQGZhY3RvcnkgTC5wb2ludChjb29yZHM6IE9iamVjdClcclxuLy8gRXhwZWN0cyBhIHBsYWluIG9iamVjdCBvZiB0aGUgZm9ybSBge3g6IE51bWJlciwgeTogTnVtYmVyfWAgaW5zdGVhZC5cclxuTC5wb2ludCA9IGZ1bmN0aW9uICh4LCB5LCByb3VuZCkge1xyXG5cdGlmICh4IGluc3RhbmNlb2YgTC5Qb2ludCkge1xyXG5cdFx0cmV0dXJuIHg7XHJcblx0fVxyXG5cdGlmIChMLlV0aWwuaXNBcnJheSh4KSkge1xyXG5cdFx0cmV0dXJuIG5ldyBMLlBvaW50KHhbMF0sIHhbMV0pO1xyXG5cdH1cclxuXHRpZiAoeCA9PT0gdW5kZWZpbmVkIHx8IHggPT09IG51bGwpIHtcclxuXHRcdHJldHVybiB4O1xyXG5cdH1cclxuXHRpZiAodHlwZW9mIHggPT09ICdvYmplY3QnICYmICd4JyBpbiB4ICYmICd5JyBpbiB4KSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQoeC54LCB4LnkpO1xyXG5cdH1cclxuXHRyZXR1cm4gbmV3IEwuUG9pbnQoeCwgeSwgcm91bmQpO1xyXG59O1xyXG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgQm91bmRzXHJcbiAqIEBha2EgTC5Cb3VuZHNcclxuICpcclxuICogUmVwcmVzZW50cyBhIHJlY3Rhbmd1bGFyIGFyZWEgaW4gcGl4ZWwgY29vcmRpbmF0ZXMuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIHZhciBwMSA9IEwucG9pbnQoMTAsIDEwKSxcclxuICogcDIgPSBMLnBvaW50KDQwLCA2MCksXHJcbiAqIGJvdW5kcyA9IEwuYm91bmRzKHAxLCBwMik7XHJcbiAqIGBgYFxyXG4gKlxyXG4gKiBBbGwgTGVhZmxldCBtZXRob2RzIHRoYXQgYWNjZXB0IGBCb3VuZHNgIG9iamVjdHMgYWxzbyBhY2NlcHQgdGhlbSBpbiBhIHNpbXBsZSBBcnJheSBmb3JtICh1bmxlc3Mgbm90ZWQgb3RoZXJ3aXNlKSwgc28gdGhlIGJvdW5kcyBleGFtcGxlIGFib3ZlIGNhbiBiZSBwYXNzZWQgbGlrZSB0aGlzOlxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBvdGhlckJvdW5kcy5pbnRlcnNlY3RzKFtbMTAsIDEwXSwgWzQwLCA2MF1dKTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxuTC5Cb3VuZHMgPSBmdW5jdGlvbiAoYSwgYikge1xyXG5cdGlmICghYSkgeyByZXR1cm47IH1cclxuXHJcblx0dmFyIHBvaW50cyA9IGIgPyBbYSwgYl0gOiBhO1xyXG5cclxuXHRmb3IgKHZhciBpID0gMCwgbGVuID0gcG9pbnRzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHR0aGlzLmV4dGVuZChwb2ludHNbaV0pO1xyXG5cdH1cclxufTtcclxuXHJcbkwuQm91bmRzLnByb3RvdHlwZSA9IHtcclxuXHQvLyBAbWV0aG9kIGV4dGVuZChwb2ludDogUG9pbnQpOiB0aGlzXHJcblx0Ly8gRXh0ZW5kcyB0aGUgYm91bmRzIHRvIGNvbnRhaW4gdGhlIGdpdmVuIHBvaW50LlxyXG5cdGV4dGVuZDogZnVuY3Rpb24gKHBvaW50KSB7IC8vIChQb2ludClcclxuXHRcdHBvaW50ID0gTC5wb2ludChwb2ludCk7XHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IG1pbjogUG9pbnRcclxuXHRcdC8vIFRoZSB0b3AgbGVmdCBjb3JuZXIgb2YgdGhlIHJlY3RhbmdsZS5cclxuXHRcdC8vIEBwcm9wZXJ0eSBtYXg6IFBvaW50XHJcblx0XHQvLyBUaGUgYm90dG9tIHJpZ2h0IGNvcm5lciBvZiB0aGUgcmVjdGFuZ2xlLlxyXG5cdFx0aWYgKCF0aGlzLm1pbiAmJiAhdGhpcy5tYXgpIHtcclxuXHRcdFx0dGhpcy5taW4gPSBwb2ludC5jbG9uZSgpO1xyXG5cdFx0XHR0aGlzLm1heCA9IHBvaW50LmNsb25lKCk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHR0aGlzLm1pbi54ID0gTWF0aC5taW4ocG9pbnQueCwgdGhpcy5taW4ueCk7XHJcblx0XHRcdHRoaXMubWF4LnggPSBNYXRoLm1heChwb2ludC54LCB0aGlzLm1heC54KTtcclxuXHRcdFx0dGhpcy5taW4ueSA9IE1hdGgubWluKHBvaW50LnksIHRoaXMubWluLnkpO1xyXG5cdFx0XHR0aGlzLm1heC55ID0gTWF0aC5tYXgocG9pbnQueSwgdGhpcy5tYXgueSk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldENlbnRlcihyb3VuZD86IEJvb2xlYW4pOiBQb2ludFxyXG5cdC8vIFJldHVybnMgdGhlIGNlbnRlciBwb2ludCBvZiB0aGUgYm91bmRzLlxyXG5cdGdldENlbnRlcjogZnVuY3Rpb24gKHJvdW5kKSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQoXHJcblx0XHQgICAgICAgICh0aGlzLm1pbi54ICsgdGhpcy5tYXgueCkgLyAyLFxyXG5cdFx0ICAgICAgICAodGhpcy5taW4ueSArIHRoaXMubWF4LnkpIC8gMiwgcm91bmQpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Qm90dG9tTGVmdCgpOiBQb2ludFxyXG5cdC8vIFJldHVybnMgdGhlIGJvdHRvbS1sZWZ0IHBvaW50IG9mIHRoZSBib3VuZHMuXHJcblx0Z2V0Qm90dG9tTGVmdDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIG5ldyBMLlBvaW50KHRoaXMubWluLngsIHRoaXMubWF4LnkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0VG9wUmlnaHQoKTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIHRoZSB0b3AtcmlnaHQgcG9pbnQgb2YgdGhlIGJvdW5kcy5cclxuXHRnZXRUb3BSaWdodDogZnVuY3Rpb24gKCkgeyAvLyAtPiBQb2ludFxyXG5cdFx0cmV0dXJuIG5ldyBMLlBvaW50KHRoaXMubWF4LngsIHRoaXMubWluLnkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0U2l6ZSgpOiBQb2ludFxyXG5cdC8vIFJldHVybnMgdGhlIHNpemUgb2YgdGhlIGdpdmVuIGJvdW5kc1xyXG5cdGdldFNpemU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLm1heC5zdWJ0cmFjdCh0aGlzLm1pbik7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBjb250YWlucyhvdGhlckJvdW5kczogQm91bmRzKTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSByZWN0YW5nbGUgY29udGFpbnMgdGhlIGdpdmVuIG9uZS5cclxuXHQvLyBAYWx0ZXJuYXRpdmVcclxuXHQvLyBAbWV0aG9kIGNvbnRhaW5zKHBvaW50OiBQb2ludCk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgcmVjdGFuZ2xlIGNvbnRhaW5zIHRoZSBnaXZlbiBwb2ludC5cclxuXHRjb250YWluczogZnVuY3Rpb24gKG9iaikge1xyXG5cdFx0dmFyIG1pbiwgbWF4O1xyXG5cclxuXHRcdGlmICh0eXBlb2Ygb2JqWzBdID09PSAnbnVtYmVyJyB8fCBvYmogaW5zdGFuY2VvZiBMLlBvaW50KSB7XHJcblx0XHRcdG9iaiA9IEwucG9pbnQob2JqKTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdG9iaiA9IEwuYm91bmRzKG9iaik7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKG9iaiBpbnN0YW5jZW9mIEwuQm91bmRzKSB7XHJcblx0XHRcdG1pbiA9IG9iai5taW47XHJcblx0XHRcdG1heCA9IG9iai5tYXg7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRtaW4gPSBtYXggPSBvYmo7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIChtaW4ueCA+PSB0aGlzLm1pbi54KSAmJlxyXG5cdFx0ICAgICAgIChtYXgueCA8PSB0aGlzLm1heC54KSAmJlxyXG5cdFx0ICAgICAgIChtaW4ueSA+PSB0aGlzLm1pbi55KSAmJlxyXG5cdFx0ICAgICAgIChtYXgueSA8PSB0aGlzLm1heC55KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGludGVyc2VjdHMob3RoZXJCb3VuZHM6IEJvdW5kcyk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgcmVjdGFuZ2xlIGludGVyc2VjdHMgdGhlIGdpdmVuIGJvdW5kcy4gVHdvIGJvdW5kc1xyXG5cdC8vIGludGVyc2VjdCBpZiB0aGV5IGhhdmUgYXQgbGVhc3Qgb25lIHBvaW50IGluIGNvbW1vbi5cclxuXHRpbnRlcnNlY3RzOiBmdW5jdGlvbiAoYm91bmRzKSB7IC8vIChCb3VuZHMpIC0+IEJvb2xlYW5cclxuXHRcdGJvdW5kcyA9IEwuYm91bmRzKGJvdW5kcyk7XHJcblxyXG5cdFx0dmFyIG1pbiA9IHRoaXMubWluLFxyXG5cdFx0ICAgIG1heCA9IHRoaXMubWF4LFxyXG5cdFx0ICAgIG1pbjIgPSBib3VuZHMubWluLFxyXG5cdFx0ICAgIG1heDIgPSBib3VuZHMubWF4LFxyXG5cdFx0ICAgIHhJbnRlcnNlY3RzID0gKG1heDIueCA+PSBtaW4ueCkgJiYgKG1pbjIueCA8PSBtYXgueCksXHJcblx0XHQgICAgeUludGVyc2VjdHMgPSAobWF4Mi55ID49IG1pbi55KSAmJiAobWluMi55IDw9IG1heC55KTtcclxuXHJcblx0XHRyZXR1cm4geEludGVyc2VjdHMgJiYgeUludGVyc2VjdHM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBvdmVybGFwcyhvdGhlckJvdW5kczogQm91bmRzKTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSByZWN0YW5nbGUgb3ZlcmxhcHMgdGhlIGdpdmVuIGJvdW5kcy4gVHdvIGJvdW5kc1xyXG5cdC8vIG92ZXJsYXAgaWYgdGhlaXIgaW50ZXJzZWN0aW9uIGlzIGFuIGFyZWEuXHJcblx0b3ZlcmxhcHM6IGZ1bmN0aW9uIChib3VuZHMpIHsgLy8gKEJvdW5kcykgLT4gQm9vbGVhblxyXG5cdFx0Ym91bmRzID0gTC5ib3VuZHMoYm91bmRzKTtcclxuXHJcblx0XHR2YXIgbWluID0gdGhpcy5taW4sXHJcblx0XHQgICAgbWF4ID0gdGhpcy5tYXgsXHJcblx0XHQgICAgbWluMiA9IGJvdW5kcy5taW4sXHJcblx0XHQgICAgbWF4MiA9IGJvdW5kcy5tYXgsXHJcblx0XHQgICAgeE92ZXJsYXBzID0gKG1heDIueCA+IG1pbi54KSAmJiAobWluMi54IDwgbWF4LngpLFxyXG5cdFx0ICAgIHlPdmVybGFwcyA9IChtYXgyLnkgPiBtaW4ueSkgJiYgKG1pbjIueSA8IG1heC55KTtcclxuXHJcblx0XHRyZXR1cm4geE92ZXJsYXBzICYmIHlPdmVybGFwcztcclxuXHR9LFxyXG5cclxuXHRpc1ZhbGlkOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gISEodGhpcy5taW4gJiYgdGhpcy5tYXgpO1xyXG5cdH1cclxufTtcclxuXHJcblxyXG4vLyBAZmFjdG9yeSBMLmJvdW5kcyh0b3BMZWZ0OiBQb2ludCwgYm90dG9tUmlnaHQ6IFBvaW50KVxyXG4vLyBDcmVhdGVzIGEgQm91bmRzIG9iamVjdCBmcm9tIHR3byBjb29yZGluYXRlcyAodXN1YWxseSB0b3AtbGVmdCBhbmQgYm90dG9tLXJpZ2h0IGNvcm5lcnMpLlxyXG4vLyBAYWx0ZXJuYXRpdmVcclxuLy8gQGZhY3RvcnkgTC5ib3VuZHMocG9pbnRzOiBQb2ludFtdKVxyXG4vLyBDcmVhdGVzIGEgQm91bmRzIG9iamVjdCBmcm9tIHRoZSBwb2ludHMgaXQgY29udGFpbnNcclxuTC5ib3VuZHMgPSBmdW5jdGlvbiAoYSwgYikge1xyXG5cdGlmICghYSB8fCBhIGluc3RhbmNlb2YgTC5Cb3VuZHMpIHtcclxuXHRcdHJldHVybiBhO1xyXG5cdH1cclxuXHRyZXR1cm4gbmV3IEwuQm91bmRzKGEsIGIpO1xyXG59O1xyXG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgVHJhbnNmb3JtYXRpb25cclxuICogQGFrYSBMLlRyYW5zZm9ybWF0aW9uXHJcbiAqXHJcbiAqIFJlcHJlc2VudHMgYW4gYWZmaW5lIHRyYW5zZm9ybWF0aW9uOiBhIHNldCBvZiBjb2VmZmljaWVudHMgYGFgLCBgYmAsIGBjYCwgYGRgXHJcbiAqIGZvciB0cmFuc2Zvcm1pbmcgYSBwb2ludCBvZiBhIGZvcm0gYCh4LCB5KWAgaW50byBgKGEqeCArIGIsIGMqeSArIGQpYCBhbmQgZG9pbmdcclxuICogdGhlIHJldmVyc2UuIFVzZWQgYnkgTGVhZmxldCBpbiBpdHMgcHJvamVjdGlvbnMgY29kZS5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYGBganNcclxuICogdmFyIHRyYW5zZm9ybWF0aW9uID0gbmV3IEwuVHJhbnNmb3JtYXRpb24oMiwgNSwgLTEsIDEwKSxcclxuICogXHRwID0gTC5wb2ludCgxLCAyKSxcclxuICogXHRwMiA9IHRyYW5zZm9ybWF0aW9uLnRyYW5zZm9ybShwKSwgLy8gIEwucG9pbnQoNywgOClcclxuICogXHRwMyA9IHRyYW5zZm9ybWF0aW9uLnVudHJhbnNmb3JtKHAyKTsgLy8gIEwucG9pbnQoMSwgMilcclxuICogYGBgXHJcbiAqL1xyXG5cclxuXHJcbi8vIGZhY3RvcnkgbmV3IEwuVHJhbnNmb3JtYXRpb24oYTogTnVtYmVyLCBiOiBOdW1iZXIsIGM6IE51bWJlciwgZDogTnVtYmVyKVxyXG4vLyBDcmVhdGVzIGEgYFRyYW5zZm9ybWF0aW9uYCBvYmplY3Qgd2l0aCB0aGUgZ2l2ZW4gY29lZmZpY2llbnRzLlxyXG5MLlRyYW5zZm9ybWF0aW9uID0gZnVuY3Rpb24gKGEsIGIsIGMsIGQpIHtcclxuXHR0aGlzLl9hID0gYTtcclxuXHR0aGlzLl9iID0gYjtcclxuXHR0aGlzLl9jID0gYztcclxuXHR0aGlzLl9kID0gZDtcclxufTtcclxuXHJcbkwuVHJhbnNmb3JtYXRpb24ucHJvdG90eXBlID0ge1xyXG5cdC8vIEBtZXRob2QgdHJhbnNmb3JtKHBvaW50OiBQb2ludCwgc2NhbGU/OiBOdW1iZXIpOiBQb2ludFxyXG5cdC8vIFJldHVybnMgYSB0cmFuc2Zvcm1lZCBwb2ludCwgb3B0aW9uYWxseSBtdWx0aXBsaWVkIGJ5IHRoZSBnaXZlbiBzY2FsZS5cclxuXHQvLyBPbmx5IGFjY2VwdHMgYWN0dWFsIGBMLlBvaW50YCBpbnN0YW5jZXMsIG5vdCBhcnJheXMuXHJcblx0dHJhbnNmb3JtOiBmdW5jdGlvbiAocG9pbnQsIHNjYWxlKSB7IC8vIChQb2ludCwgTnVtYmVyKSAtPiBQb2ludFxyXG5cdFx0cmV0dXJuIHRoaXMuX3RyYW5zZm9ybShwb2ludC5jbG9uZSgpLCBzY2FsZSk7XHJcblx0fSxcclxuXHJcblx0Ly8gZGVzdHJ1Y3RpdmUgdHJhbnNmb3JtIChmYXN0ZXIpXHJcblx0X3RyYW5zZm9ybTogZnVuY3Rpb24gKHBvaW50LCBzY2FsZSkge1xyXG5cdFx0c2NhbGUgPSBzY2FsZSB8fCAxO1xyXG5cdFx0cG9pbnQueCA9IHNjYWxlICogKHRoaXMuX2EgKiBwb2ludC54ICsgdGhpcy5fYik7XHJcblx0XHRwb2ludC55ID0gc2NhbGUgKiAodGhpcy5fYyAqIHBvaW50LnkgKyB0aGlzLl9kKTtcclxuXHRcdHJldHVybiBwb2ludDtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHVudHJhbnNmb3JtKHBvaW50OiBQb2ludCwgc2NhbGU/OiBOdW1iZXIpOiBQb2ludFxyXG5cdC8vIFJldHVybnMgdGhlIHJldmVyc2UgdHJhbnNmb3JtYXRpb24gb2YgdGhlIGdpdmVuIHBvaW50LCBvcHRpb25hbGx5IGRpdmlkZWRcclxuXHQvLyBieSB0aGUgZ2l2ZW4gc2NhbGUuIE9ubHkgYWNjZXB0cyBhY3R1YWwgYEwuUG9pbnRgIGluc3RhbmNlcywgbm90IGFycmF5cy5cclxuXHR1bnRyYW5zZm9ybTogZnVuY3Rpb24gKHBvaW50LCBzY2FsZSkge1xyXG5cdFx0c2NhbGUgPSBzY2FsZSB8fCAxO1xyXG5cdFx0cmV0dXJuIG5ldyBMLlBvaW50KFxyXG5cdFx0ICAgICAgICAocG9pbnQueCAvIHNjYWxlIC0gdGhpcy5fYikgLyB0aGlzLl9hLFxyXG5cdFx0ICAgICAgICAocG9pbnQueSAvIHNjYWxlIC0gdGhpcy5fZCkgLyB0aGlzLl9jKTtcclxuXHR9XHJcbn07XHJcblxuXG5cbi8qXHJcbiAqIEBuYW1lc3BhY2UgRG9tVXRpbFxyXG4gKlxyXG4gKiBVdGlsaXR5IGZ1bmN0aW9ucyB0byB3b3JrIHdpdGggdGhlIFtET01dKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0FQSS9Eb2N1bWVudF9PYmplY3RfTW9kZWwpXHJcbiAqIHRyZWUsIHVzZWQgYnkgTGVhZmxldCBpbnRlcm5hbGx5LlxyXG4gKlxyXG4gKiBNb3N0IGZ1bmN0aW9ucyBleHBlY3Rpbmcgb3IgcmV0dXJuaW5nIGEgYEhUTUxFbGVtZW50YCBhbHNvIHdvcmsgZm9yXHJcbiAqIFNWRyBlbGVtZW50cy4gVGhlIG9ubHkgZGlmZmVyZW5jZSBpcyB0aGF0IGNsYXNzZXMgcmVmZXIgdG8gQ1NTIGNsYXNzZXNcclxuICogaW4gSFRNTCBhbmQgU1ZHIGNsYXNzZXMgaW4gU1ZHLlxyXG4gKi9cclxuXHJcbkwuRG9tVXRpbCA9IHtcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGdldChpZDogU3RyaW5nfEhUTUxFbGVtZW50KTogSFRNTEVsZW1lbnRcclxuXHQvLyBSZXR1cm5zIGFuIGVsZW1lbnQgZ2l2ZW4gaXRzIERPTSBpZCwgb3IgcmV0dXJucyB0aGUgZWxlbWVudCBpdHNlbGZcclxuXHQvLyBpZiBpdCB3YXMgcGFzc2VkIGRpcmVjdGx5LlxyXG5cdGdldDogZnVuY3Rpb24gKGlkKSB7XHJcblx0XHRyZXR1cm4gdHlwZW9mIGlkID09PSAnc3RyaW5nJyA/IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGlkKSA6IGlkO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBnZXRTdHlsZShlbDogSFRNTEVsZW1lbnQsIHN0eWxlQXR0cmliOiBTdHJpbmcpOiBTdHJpbmdcclxuXHQvLyBSZXR1cm5zIHRoZSB2YWx1ZSBmb3IgYSBjZXJ0YWluIHN0eWxlIGF0dHJpYnV0ZSBvbiBhbiBlbGVtZW50LFxyXG5cdC8vIGluY2x1ZGluZyBjb21wdXRlZCB2YWx1ZXMgb3IgdmFsdWVzIHNldCB0aHJvdWdoIENTUy5cclxuXHRnZXRTdHlsZTogZnVuY3Rpb24gKGVsLCBzdHlsZSkge1xyXG5cclxuXHRcdHZhciB2YWx1ZSA9IGVsLnN0eWxlW3N0eWxlXSB8fCAoZWwuY3VycmVudFN0eWxlICYmIGVsLmN1cnJlbnRTdHlsZVtzdHlsZV0pO1xyXG5cclxuXHRcdGlmICgoIXZhbHVlIHx8IHZhbHVlID09PSAnYXV0bycpICYmIGRvY3VtZW50LmRlZmF1bHRWaWV3KSB7XHJcblx0XHRcdHZhciBjc3MgPSBkb2N1bWVudC5kZWZhdWx0Vmlldy5nZXRDb21wdXRlZFN0eWxlKGVsLCBudWxsKTtcclxuXHRcdFx0dmFsdWUgPSBjc3MgPyBjc3Nbc3R5bGVdIDogbnVsbDtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdmFsdWUgPT09ICdhdXRvJyA/IG51bGwgOiB2YWx1ZTtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gY3JlYXRlKHRhZ05hbWU6IFN0cmluZywgY2xhc3NOYW1lPzogU3RyaW5nLCBjb250YWluZXI/OiBIVE1MRWxlbWVudCk6IEhUTUxFbGVtZW50XHJcblx0Ly8gQ3JlYXRlcyBhbiBIVE1MIGVsZW1lbnQgd2l0aCBgdGFnTmFtZWAsIHNldHMgaXRzIGNsYXNzIHRvIGBjbGFzc05hbWVgLCBhbmQgb3B0aW9uYWxseSBhcHBlbmRzIGl0IHRvIGBjb250YWluZXJgIGVsZW1lbnQuXHJcblx0Y3JlYXRlOiBmdW5jdGlvbiAodGFnTmFtZSwgY2xhc3NOYW1lLCBjb250YWluZXIpIHtcclxuXHJcblx0XHR2YXIgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KHRhZ05hbWUpO1xyXG5cdFx0ZWwuY2xhc3NOYW1lID0gY2xhc3NOYW1lIHx8ICcnO1xyXG5cclxuXHRcdGlmIChjb250YWluZXIpIHtcclxuXHRcdFx0Y29udGFpbmVyLmFwcGVuZENoaWxkKGVsKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gZWw7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHJlbW92ZShlbDogSFRNTEVsZW1lbnQpXHJcblx0Ly8gUmVtb3ZlcyBgZWxgIGZyb20gaXRzIHBhcmVudCBlbGVtZW50XHJcblx0cmVtb3ZlOiBmdW5jdGlvbiAoZWwpIHtcclxuXHRcdHZhciBwYXJlbnQgPSBlbC5wYXJlbnROb2RlO1xyXG5cdFx0aWYgKHBhcmVudCkge1xyXG5cdFx0XHRwYXJlbnQucmVtb3ZlQ2hpbGQoZWwpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBlbXB0eShlbDogSFRNTEVsZW1lbnQpXHJcblx0Ly8gUmVtb3ZlcyBhbGwgb2YgYGVsYCdzIGNoaWxkcmVuIGVsZW1lbnRzIGZyb20gYGVsYFxyXG5cdGVtcHR5OiBmdW5jdGlvbiAoZWwpIHtcclxuXHRcdHdoaWxlIChlbC5maXJzdENoaWxkKSB7XHJcblx0XHRcdGVsLnJlbW92ZUNoaWxkKGVsLmZpcnN0Q2hpbGQpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiB0b0Zyb250KGVsOiBIVE1MRWxlbWVudClcclxuXHQvLyBNYWtlcyBgZWxgIHRoZSBsYXN0IGNoaWxkcmVuIG9mIGl0cyBwYXJlbnQsIHNvIGl0IHJlbmRlcnMgaW4gZnJvbnQgb2YgdGhlIG90aGVyIGNoaWxkcmVuLlxyXG5cdHRvRnJvbnQ6IGZ1bmN0aW9uIChlbCkge1xyXG5cdFx0ZWwucGFyZW50Tm9kZS5hcHBlbmRDaGlsZChlbCk7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHRvQmFjayhlbDogSFRNTEVsZW1lbnQpXHJcblx0Ly8gTWFrZXMgYGVsYCB0aGUgZmlyc3QgY2hpbGRyZW4gb2YgaXRzIHBhcmVudCwgc28gaXQgcmVuZGVycyBiYWNrIGZyb20gdGhlIG90aGVyIGNoaWxkcmVuLlxyXG5cdHRvQmFjazogZnVuY3Rpb24gKGVsKSB7XHJcblx0XHR2YXIgcGFyZW50ID0gZWwucGFyZW50Tm9kZTtcclxuXHRcdHBhcmVudC5pbnNlcnRCZWZvcmUoZWwsIHBhcmVudC5maXJzdENoaWxkKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gaGFzQ2xhc3MoZWw6IEhUTUxFbGVtZW50LCBuYW1lOiBTdHJpbmcpOiBCb29sZWFuXHJcblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIGVsZW1lbnQncyBjbGFzcyBhdHRyaWJ1dGUgY29udGFpbnMgYG5hbWVgLlxyXG5cdGhhc0NsYXNzOiBmdW5jdGlvbiAoZWwsIG5hbWUpIHtcclxuXHRcdGlmIChlbC5jbGFzc0xpc3QgIT09IHVuZGVmaW5lZCkge1xyXG5cdFx0XHRyZXR1cm4gZWwuY2xhc3NMaXN0LmNvbnRhaW5zKG5hbWUpO1xyXG5cdFx0fVxyXG5cdFx0dmFyIGNsYXNzTmFtZSA9IEwuRG9tVXRpbC5nZXRDbGFzcyhlbCk7XHJcblx0XHRyZXR1cm4gY2xhc3NOYW1lLmxlbmd0aCA+IDAgJiYgbmV3IFJlZ0V4cCgnKF58XFxcXHMpJyArIG5hbWUgKyAnKFxcXFxzfCQpJykudGVzdChjbGFzc05hbWUpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBhZGRDbGFzcyhlbDogSFRNTEVsZW1lbnQsIG5hbWU6IFN0cmluZylcclxuXHQvLyBBZGRzIGBuYW1lYCB0byB0aGUgZWxlbWVudCdzIGNsYXNzIGF0dHJpYnV0ZS5cclxuXHRhZGRDbGFzczogZnVuY3Rpb24gKGVsLCBuYW1lKSB7XHJcblx0XHRpZiAoZWwuY2xhc3NMaXN0ICE9PSB1bmRlZmluZWQpIHtcclxuXHRcdFx0dmFyIGNsYXNzZXMgPSBMLlV0aWwuc3BsaXRXb3JkcyhuYW1lKTtcclxuXHRcdFx0Zm9yICh2YXIgaSA9IDAsIGxlbiA9IGNsYXNzZXMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0XHRlbC5jbGFzc0xpc3QuYWRkKGNsYXNzZXNbaV0pO1xyXG5cdFx0XHR9XHJcblx0XHR9IGVsc2UgaWYgKCFMLkRvbVV0aWwuaGFzQ2xhc3MoZWwsIG5hbWUpKSB7XHJcblx0XHRcdHZhciBjbGFzc05hbWUgPSBMLkRvbVV0aWwuZ2V0Q2xhc3MoZWwpO1xyXG5cdFx0XHRMLkRvbVV0aWwuc2V0Q2xhc3MoZWwsIChjbGFzc05hbWUgPyBjbGFzc05hbWUgKyAnICcgOiAnJykgKyBuYW1lKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gcmVtb3ZlQ2xhc3MoZWw6IEhUTUxFbGVtZW50LCBuYW1lOiBTdHJpbmcpXHJcblx0Ly8gUmVtb3ZlcyBgbmFtZWAgZnJvbSB0aGUgZWxlbWVudCdzIGNsYXNzIGF0dHJpYnV0ZS5cclxuXHRyZW1vdmVDbGFzczogZnVuY3Rpb24gKGVsLCBuYW1lKSB7XHJcblx0XHRpZiAoZWwuY2xhc3NMaXN0ICE9PSB1bmRlZmluZWQpIHtcclxuXHRcdFx0ZWwuY2xhc3NMaXN0LnJlbW92ZShuYW1lKTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdEwuRG9tVXRpbC5zZXRDbGFzcyhlbCwgTC5VdGlsLnRyaW0oKCcgJyArIEwuRG9tVXRpbC5nZXRDbGFzcyhlbCkgKyAnICcpLnJlcGxhY2UoJyAnICsgbmFtZSArICcgJywgJyAnKSkpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBzZXRDbGFzcyhlbDogSFRNTEVsZW1lbnQsIG5hbWU6IFN0cmluZylcclxuXHQvLyBTZXRzIHRoZSBlbGVtZW50J3MgY2xhc3MuXHJcblx0c2V0Q2xhc3M6IGZ1bmN0aW9uIChlbCwgbmFtZSkge1xyXG5cdFx0aWYgKGVsLmNsYXNzTmFtZS5iYXNlVmFsID09PSB1bmRlZmluZWQpIHtcclxuXHRcdFx0ZWwuY2xhc3NOYW1lID0gbmFtZTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdC8vIGluIGNhc2Ugb2YgU1ZHIGVsZW1lbnRcclxuXHRcdFx0ZWwuY2xhc3NOYW1lLmJhc2VWYWwgPSBuYW1lO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBnZXRDbGFzcyhlbDogSFRNTEVsZW1lbnQpOiBTdHJpbmdcclxuXHQvLyBSZXR1cm5zIHRoZSBlbGVtZW50J3MgY2xhc3MuXHJcblx0Z2V0Q2xhc3M6IGZ1bmN0aW9uIChlbCkge1xyXG5cdFx0cmV0dXJuIGVsLmNsYXNzTmFtZS5iYXNlVmFsID09PSB1bmRlZmluZWQgPyBlbC5jbGFzc05hbWUgOiBlbC5jbGFzc05hbWUuYmFzZVZhbDtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gc2V0T3BhY2l0eShlbDogSFRNTEVsZW1lbnQsIG9wYWNpdHk6IE51bWJlcilcclxuXHQvLyBTZXQgdGhlIG9wYWNpdHkgb2YgYW4gZWxlbWVudCAoaW5jbHVkaW5nIG9sZCBJRSBzdXBwb3J0KS5cclxuXHQvLyBgb3BhY2l0eWAgbXVzdCBiZSBhIG51bWJlciBmcm9tIGAwYCB0byBgMWAuXHJcblx0c2V0T3BhY2l0eTogZnVuY3Rpb24gKGVsLCB2YWx1ZSkge1xyXG5cclxuXHRcdGlmICgnb3BhY2l0eScgaW4gZWwuc3R5bGUpIHtcclxuXHRcdFx0ZWwuc3R5bGUub3BhY2l0eSA9IHZhbHVlO1xyXG5cclxuXHRcdH0gZWxzZSBpZiAoJ2ZpbHRlcicgaW4gZWwuc3R5bGUpIHtcclxuXHRcdFx0TC5Eb21VdGlsLl9zZXRPcGFjaXR5SUUoZWwsIHZhbHVlKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfc2V0T3BhY2l0eUlFOiBmdW5jdGlvbiAoZWwsIHZhbHVlKSB7XHJcblx0XHR2YXIgZmlsdGVyID0gZmFsc2UsXHJcblx0XHQgICAgZmlsdGVyTmFtZSA9ICdEWEltYWdlVHJhbnNmb3JtLk1pY3Jvc29mdC5BbHBoYSc7XHJcblxyXG5cdFx0Ly8gZmlsdGVycyBjb2xsZWN0aW9uIHRocm93cyBhbiBlcnJvciBpZiB3ZSB0cnkgdG8gcmV0cmlldmUgYSBmaWx0ZXIgdGhhdCBkb2Vzbid0IGV4aXN0XHJcblx0XHR0cnkge1xyXG5cdFx0XHRmaWx0ZXIgPSBlbC5maWx0ZXJzLml0ZW0oZmlsdGVyTmFtZSk7XHJcblx0XHR9IGNhdGNoIChlKSB7XHJcblx0XHRcdC8vIGRvbid0IHNldCBvcGFjaXR5IHRvIDEgaWYgd2UgaGF2ZW4ndCBhbHJlYWR5IHNldCBhbiBvcGFjaXR5LFxyXG5cdFx0XHQvLyBpdCBpc24ndCBuZWVkZWQgYW5kIGJyZWFrcyB0cmFuc3BhcmVudCBwbmdzLlxyXG5cdFx0XHRpZiAodmFsdWUgPT09IDEpIHsgcmV0dXJuOyB9XHJcblx0XHR9XHJcblxyXG5cdFx0dmFsdWUgPSBNYXRoLnJvdW5kKHZhbHVlICogMTAwKTtcclxuXHJcblx0XHRpZiAoZmlsdGVyKSB7XHJcblx0XHRcdGZpbHRlci5FbmFibGVkID0gKHZhbHVlICE9PSAxMDApO1xyXG5cdFx0XHRmaWx0ZXIuT3BhY2l0eSA9IHZhbHVlO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0ZWwuc3R5bGUuZmlsdGVyICs9ICcgcHJvZ2lkOicgKyBmaWx0ZXJOYW1lICsgJyhvcGFjaXR5PScgKyB2YWx1ZSArICcpJztcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gdGVzdFByb3AocHJvcHM6IFN0cmluZ1tdKTogU3RyaW5nfGZhbHNlXHJcblx0Ly8gR29lcyB0aHJvdWdoIHRoZSBhcnJheSBvZiBzdHlsZSBuYW1lcyBhbmQgcmV0dXJucyB0aGUgZmlyc3QgbmFtZVxyXG5cdC8vIHRoYXQgaXMgYSB2YWxpZCBzdHlsZSBuYW1lIGZvciBhbiBlbGVtZW50LiBJZiBubyBzdWNoIG5hbWUgaXMgZm91bmQsXHJcblx0Ly8gaXQgcmV0dXJucyBmYWxzZS4gVXNlZnVsIGZvciB2ZW5kb3ItcHJlZml4ZWQgc3R5bGVzIGxpa2UgYHRyYW5zZm9ybWAuXHJcblx0dGVzdFByb3A6IGZ1bmN0aW9uIChwcm9wcykge1xyXG5cclxuXHRcdHZhciBzdHlsZSA9IGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5zdHlsZTtcclxuXHJcblx0XHRmb3IgKHZhciBpID0gMDsgaSA8IHByb3BzLmxlbmd0aDsgaSsrKSB7XHJcblx0XHRcdGlmIChwcm9wc1tpXSBpbiBzdHlsZSkge1xyXG5cdFx0XHRcdHJldHVybiBwcm9wc1tpXTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIGZhbHNlO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBzZXRUcmFuc2Zvcm0oZWw6IEhUTUxFbGVtZW50LCBvZmZzZXQ6IFBvaW50LCBzY2FsZT86IE51bWJlcilcclxuXHQvLyBSZXNldHMgdGhlIDNEIENTUyB0cmFuc2Zvcm0gb2YgYGVsYCBzbyBpdCBpcyB0cmFuc2xhdGVkIGJ5IGBvZmZzZXRgIHBpeGVsc1xyXG5cdC8vIGFuZCBvcHRpb25hbGx5IHNjYWxlZCBieSBgc2NhbGVgLiBEb2VzIG5vdCBoYXZlIGFuIGVmZmVjdCBpZiB0aGVcclxuXHQvLyBicm93c2VyIGRvZXNuJ3Qgc3VwcG9ydCAzRCBDU1MgdHJhbnNmb3Jtcy5cclxuXHRzZXRUcmFuc2Zvcm06IGZ1bmN0aW9uIChlbCwgb2Zmc2V0LCBzY2FsZSkge1xyXG5cdFx0dmFyIHBvcyA9IG9mZnNldCB8fCBuZXcgTC5Qb2ludCgwLCAwKTtcclxuXHJcblx0XHRlbC5zdHlsZVtMLkRvbVV0aWwuVFJBTlNGT1JNXSA9XHJcblx0XHRcdChMLkJyb3dzZXIuaWUzZCA/XHJcblx0XHRcdFx0J3RyYW5zbGF0ZSgnICsgcG9zLnggKyAncHgsJyArIHBvcy55ICsgJ3B4KScgOlxyXG5cdFx0XHRcdCd0cmFuc2xhdGUzZCgnICsgcG9zLnggKyAncHgsJyArIHBvcy55ICsgJ3B4LDApJykgK1xyXG5cdFx0XHQoc2NhbGUgPyAnIHNjYWxlKCcgKyBzY2FsZSArICcpJyA6ICcnKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gc2V0UG9zaXRpb24oZWw6IEhUTUxFbGVtZW50LCBwb3NpdGlvbjogUG9pbnQpXHJcblx0Ly8gU2V0cyB0aGUgcG9zaXRpb24gb2YgYGVsYCB0byBjb29yZGluYXRlcyBzcGVjaWZpZWQgYnkgYHBvc2l0aW9uYCxcclxuXHQvLyB1c2luZyBDU1MgdHJhbnNsYXRlIG9yIHRvcC9sZWZ0IHBvc2l0aW9uaW5nIGRlcGVuZGluZyBvbiB0aGUgYnJvd3NlclxyXG5cdC8vICh1c2VkIGJ5IExlYWZsZXQgaW50ZXJuYWxseSB0byBwb3NpdGlvbiBpdHMgbGF5ZXJzKS5cclxuXHRzZXRQb3NpdGlvbjogZnVuY3Rpb24gKGVsLCBwb2ludCkgeyAvLyAoSFRNTEVsZW1lbnQsIFBvaW50WywgQm9vbGVhbl0pXHJcblxyXG5cdFx0Lyplc2xpbnQtZGlzYWJsZSAqL1xyXG5cdFx0ZWwuX2xlYWZsZXRfcG9zID0gcG9pbnQ7XHJcblx0XHQvKmVzbGludC1lbmFibGUgKi9cclxuXHJcblx0XHRpZiAoTC5Ccm93c2VyLmFueTNkKSB7XHJcblx0XHRcdEwuRG9tVXRpbC5zZXRUcmFuc2Zvcm0oZWwsIHBvaW50KTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdGVsLnN0eWxlLmxlZnQgPSBwb2ludC54ICsgJ3B4JztcclxuXHRcdFx0ZWwuc3R5bGUudG9wID0gcG9pbnQueSArICdweCc7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGdldFBvc2l0aW9uKGVsOiBIVE1MRWxlbWVudCk6IFBvaW50XHJcblx0Ly8gUmV0dXJucyB0aGUgY29vcmRpbmF0ZXMgb2YgYW4gZWxlbWVudCBwcmV2aW91c2x5IHBvc2l0aW9uZWQgd2l0aCBzZXRQb3NpdGlvbi5cclxuXHRnZXRQb3NpdGlvbjogZnVuY3Rpb24gKGVsKSB7XHJcblx0XHQvLyB0aGlzIG1ldGhvZCBpcyBvbmx5IHVzZWQgZm9yIGVsZW1lbnRzIHByZXZpb3VzbHkgcG9zaXRpb25lZCB1c2luZyBzZXRQb3NpdGlvbixcclxuXHRcdC8vIHNvIGl0J3Mgc2FmZSB0byBjYWNoZSB0aGUgcG9zaXRpb24gZm9yIHBlcmZvcm1hbmNlXHJcblxyXG5cdFx0cmV0dXJuIGVsLl9sZWFmbGV0X3BvcyB8fCBuZXcgTC5Qb2ludCgwLCAwKTtcclxuXHR9XHJcbn07XHJcblxyXG5cclxuKGZ1bmN0aW9uICgpIHtcclxuXHQvLyBwcmVmaXggc3R5bGUgcHJvcGVydHkgbmFtZXNcclxuXHJcblx0Ly8gQHByb3BlcnR5IFRSQU5TRk9STTogU3RyaW5nXHJcblx0Ly8gVmVuZG9yLXByZWZpeGVkIGZyYW5zZm9ybSBzdHlsZSBuYW1lIChlLmcuIGAnd2Via2l0VHJhbnNmb3JtJ2AgZm9yIFdlYktpdCkuXHJcblx0TC5Eb21VdGlsLlRSQU5TRk9STSA9IEwuRG9tVXRpbC50ZXN0UHJvcChcclxuXHRcdFx0Wyd0cmFuc2Zvcm0nLCAnV2Via2l0VHJhbnNmb3JtJywgJ09UcmFuc2Zvcm0nLCAnTW96VHJhbnNmb3JtJywgJ21zVHJhbnNmb3JtJ10pO1xyXG5cclxuXHJcblx0Ly8gd2Via2l0VHJhbnNpdGlvbiBjb21lcyBmaXJzdCBiZWNhdXNlIHNvbWUgYnJvd3NlciB2ZXJzaW9ucyB0aGF0IGRyb3AgdmVuZG9yIHByZWZpeCBkb24ndCBkb1xyXG5cdC8vIHRoZSBzYW1lIGZvciB0aGUgdHJhbnNpdGlvbmVuZCBldmVudCwgaW4gcGFydGljdWxhciB0aGUgQW5kcm9pZCA0LjEgc3RvY2sgYnJvd3NlclxyXG5cclxuXHQvLyBAcHJvcGVydHkgVFJBTlNJVElPTjogU3RyaW5nXHJcblx0Ly8gVmVuZG9yLXByZWZpeGVkIHRyYW5zZm9ybSBzdHlsZSBuYW1lLlxyXG5cdHZhciB0cmFuc2l0aW9uID0gTC5Eb21VdGlsLlRSQU5TSVRJT04gPSBMLkRvbVV0aWwudGVzdFByb3AoXHJcblx0XHRcdFsnd2Via2l0VHJhbnNpdGlvbicsICd0cmFuc2l0aW9uJywgJ09UcmFuc2l0aW9uJywgJ01velRyYW5zaXRpb24nLCAnbXNUcmFuc2l0aW9uJ10pO1xyXG5cclxuXHRMLkRvbVV0aWwuVFJBTlNJVElPTl9FTkQgPVxyXG5cdFx0XHR0cmFuc2l0aW9uID09PSAnd2Via2l0VHJhbnNpdGlvbicgfHwgdHJhbnNpdGlvbiA9PT0gJ09UcmFuc2l0aW9uJyA/IHRyYW5zaXRpb24gKyAnRW5kJyA6ICd0cmFuc2l0aW9uZW5kJztcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGRpc2FibGVUZXh0U2VsZWN0aW9uKClcclxuXHQvLyBQcmV2ZW50cyB0aGUgdXNlciBmcm9tIGdlbmVyYXRpbmcgYHNlbGVjdHN0YXJ0YCBET00gZXZlbnRzLCB1c3VhbGx5IGdlbmVyYXRlZFxyXG5cdC8vIHdoZW4gdGhlIHVzZXIgZHJhZ3MgdGhlIG1vdXNlIHRocm91Z2ggYSBwYWdlIHdpdGggdGV4dC4gVXNlZCBpbnRlcm5hbGx5XHJcblx0Ly8gYnkgTGVhZmxldCB0byBvdmVycmlkZSB0aGUgYmVoYXZpb3VyIG9mIGFueSBjbGljay1hbmQtZHJhZyBpbnRlcmFjdGlvbiBvblxyXG5cdC8vIHRoZSBtYXAuIEFmZmVjdHMgZHJhZyBpbnRlcmFjdGlvbnMgb24gdGhlIHdob2xlIGRvY3VtZW50LlxyXG5cclxuXHQvLyBAZnVuY3Rpb24gZW5hYmxlVGV4dFNlbGVjdGlvbigpXHJcblx0Ly8gQ2FuY2VscyB0aGUgZWZmZWN0cyBvZiBhIHByZXZpb3VzIFtgTC5Eb21VdGlsLmRpc2FibGVUZXh0U2VsZWN0aW9uYF0oI2RvbXV0aWwtZGlzYWJsZXRleHRzZWxlY3Rpb24pLlxyXG5cdGlmICgnb25zZWxlY3RzdGFydCcgaW4gZG9jdW1lbnQpIHtcclxuXHRcdEwuRG9tVXRpbC5kaXNhYmxlVGV4dFNlbGVjdGlvbiA9IGZ1bmN0aW9uICgpIHtcclxuXHRcdFx0TC5Eb21FdmVudC5vbih3aW5kb3csICdzZWxlY3RzdGFydCcsIEwuRG9tRXZlbnQucHJldmVudERlZmF1bHQpO1xyXG5cdFx0fTtcclxuXHRcdEwuRG9tVXRpbC5lbmFibGVUZXh0U2VsZWN0aW9uID0gZnVuY3Rpb24gKCkge1xyXG5cdFx0XHRMLkRvbUV2ZW50Lm9mZih3aW5kb3csICdzZWxlY3RzdGFydCcsIEwuRG9tRXZlbnQucHJldmVudERlZmF1bHQpO1xyXG5cdFx0fTtcclxuXHJcblx0fSBlbHNlIHtcclxuXHRcdHZhciB1c2VyU2VsZWN0UHJvcGVydHkgPSBMLkRvbVV0aWwudGVzdFByb3AoXHJcblx0XHRcdFsndXNlclNlbGVjdCcsICdXZWJraXRVc2VyU2VsZWN0JywgJ09Vc2VyU2VsZWN0JywgJ01velVzZXJTZWxlY3QnLCAnbXNVc2VyU2VsZWN0J10pO1xyXG5cclxuXHRcdEwuRG9tVXRpbC5kaXNhYmxlVGV4dFNlbGVjdGlvbiA9IGZ1bmN0aW9uICgpIHtcclxuXHRcdFx0aWYgKHVzZXJTZWxlY3RQcm9wZXJ0eSkge1xyXG5cdFx0XHRcdHZhciBzdHlsZSA9IGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5zdHlsZTtcclxuXHRcdFx0XHR0aGlzLl91c2VyU2VsZWN0ID0gc3R5bGVbdXNlclNlbGVjdFByb3BlcnR5XTtcclxuXHRcdFx0XHRzdHlsZVt1c2VyU2VsZWN0UHJvcGVydHldID0gJ25vbmUnO1xyXG5cdFx0XHR9XHJcblx0XHR9O1xyXG5cdFx0TC5Eb21VdGlsLmVuYWJsZVRleHRTZWxlY3Rpb24gPSBmdW5jdGlvbiAoKSB7XHJcblx0XHRcdGlmICh1c2VyU2VsZWN0UHJvcGVydHkpIHtcclxuXHRcdFx0XHRkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuc3R5bGVbdXNlclNlbGVjdFByb3BlcnR5XSA9IHRoaXMuX3VzZXJTZWxlY3Q7XHJcblx0XHRcdFx0ZGVsZXRlIHRoaXMuX3VzZXJTZWxlY3Q7XHJcblx0XHRcdH1cclxuXHRcdH07XHJcblx0fVxyXG5cclxuXHQvLyBAZnVuY3Rpb24gZGlzYWJsZUltYWdlRHJhZygpXHJcblx0Ly8gQXMgW2BMLkRvbVV0aWwuZGlzYWJsZVRleHRTZWxlY3Rpb25gXSgjZG9tdXRpbC1kaXNhYmxldGV4dHNlbGVjdGlvbiksIGJ1dFxyXG5cdC8vIGZvciBgZHJhZ3N0YXJ0YCBET00gZXZlbnRzLCB1c3VhbGx5IGdlbmVyYXRlZCB3aGVuIHRoZSB1c2VyIGRyYWdzIGFuIGltYWdlLlxyXG5cdEwuRG9tVXRpbC5kaXNhYmxlSW1hZ2VEcmFnID0gZnVuY3Rpb24gKCkge1xyXG5cdFx0TC5Eb21FdmVudC5vbih3aW5kb3csICdkcmFnc3RhcnQnLCBMLkRvbUV2ZW50LnByZXZlbnREZWZhdWx0KTtcclxuXHR9O1xyXG5cclxuXHQvLyBAZnVuY3Rpb24gZW5hYmxlSW1hZ2VEcmFnKClcclxuXHQvLyBDYW5jZWxzIHRoZSBlZmZlY3RzIG9mIGEgcHJldmlvdXMgW2BMLkRvbVV0aWwuZGlzYWJsZUltYWdlRHJhZ2BdKCNkb211dGlsLWRpc2FibGV0ZXh0c2VsZWN0aW9uKS5cclxuXHRMLkRvbVV0aWwuZW5hYmxlSW1hZ2VEcmFnID0gZnVuY3Rpb24gKCkge1xyXG5cdFx0TC5Eb21FdmVudC5vZmYod2luZG93LCAnZHJhZ3N0YXJ0JywgTC5Eb21FdmVudC5wcmV2ZW50RGVmYXVsdCk7XHJcblx0fTtcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHByZXZlbnRPdXRsaW5lKGVsOiBIVE1MRWxlbWVudClcclxuXHQvLyBNYWtlcyB0aGUgW291dGxpbmVdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0NTUy9vdXRsaW5lKVxyXG5cdC8vIG9mIHRoZSBlbGVtZW50IGBlbGAgaW52aXNpYmxlLiBVc2VkIGludGVybmFsbHkgYnkgTGVhZmxldCB0byBwcmV2ZW50XHJcblx0Ly8gZm9jdXNhYmxlIGVsZW1lbnRzIGZyb20gZGlzcGxheWluZyBhbiBvdXRsaW5lIHdoZW4gdGhlIHVzZXIgcGVyZm9ybXMgYVxyXG5cdC8vIGRyYWcgaW50ZXJhY3Rpb24gb24gdGhlbS5cclxuXHRMLkRvbVV0aWwucHJldmVudE91dGxpbmUgPSBmdW5jdGlvbiAoZWxlbWVudCkge1xyXG5cdFx0d2hpbGUgKGVsZW1lbnQudGFiSW5kZXggPT09IC0xKSB7XHJcblx0XHRcdGVsZW1lbnQgPSBlbGVtZW50LnBhcmVudE5vZGU7XHJcblx0XHR9XHJcblx0XHRpZiAoIWVsZW1lbnQgfHwgIWVsZW1lbnQuc3R5bGUpIHsgcmV0dXJuOyB9XHJcblx0XHRMLkRvbVV0aWwucmVzdG9yZU91dGxpbmUoKTtcclxuXHRcdHRoaXMuX291dGxpbmVFbGVtZW50ID0gZWxlbWVudDtcclxuXHRcdHRoaXMuX291dGxpbmVTdHlsZSA9IGVsZW1lbnQuc3R5bGUub3V0bGluZTtcclxuXHRcdGVsZW1lbnQuc3R5bGUub3V0bGluZSA9ICdub25lJztcclxuXHRcdEwuRG9tRXZlbnQub24od2luZG93LCAna2V5ZG93bicsIEwuRG9tVXRpbC5yZXN0b3JlT3V0bGluZSwgdGhpcyk7XHJcblx0fTtcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHJlc3RvcmVPdXRsaW5lKClcclxuXHQvLyBDYW5jZWxzIHRoZSBlZmZlY3RzIG9mIGEgcHJldmlvdXMgW2BMLkRvbVV0aWwucHJldmVudE91dGxpbmVgXSgpLlxyXG5cdEwuRG9tVXRpbC5yZXN0b3JlT3V0bGluZSA9IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICghdGhpcy5fb3V0bGluZUVsZW1lbnQpIHsgcmV0dXJuOyB9XHJcblx0XHR0aGlzLl9vdXRsaW5lRWxlbWVudC5zdHlsZS5vdXRsaW5lID0gdGhpcy5fb3V0bGluZVN0eWxlO1xyXG5cdFx0ZGVsZXRlIHRoaXMuX291dGxpbmVFbGVtZW50O1xyXG5cdFx0ZGVsZXRlIHRoaXMuX291dGxpbmVTdHlsZTtcclxuXHRcdEwuRG9tRXZlbnQub2ZmKHdpbmRvdywgJ2tleWRvd24nLCBMLkRvbVV0aWwucmVzdG9yZU91dGxpbmUsIHRoaXMpO1xyXG5cdH07XHJcbn0pKCk7XHJcblxuXG5cbi8qIEBjbGFzcyBMYXRMbmdcclxuICogQGFrYSBMLkxhdExuZ1xyXG4gKlxyXG4gKiBSZXByZXNlbnRzIGEgZ2VvZ3JhcGhpY2FsIHBvaW50IHdpdGggYSBjZXJ0YWluIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYFxyXG4gKiB2YXIgbGF0bG5nID0gTC5sYXRMbmcoNTAuNSwgMzAuNSk7XHJcbiAqIGBgYFxyXG4gKlxyXG4gKiBBbGwgTGVhZmxldCBtZXRob2RzIHRoYXQgYWNjZXB0IExhdExuZyBvYmplY3RzIGFsc28gYWNjZXB0IHRoZW0gaW4gYSBzaW1wbGUgQXJyYXkgZm9ybSBhbmQgc2ltcGxlIG9iamVjdCBmb3JtICh1bmxlc3Mgbm90ZWQgb3RoZXJ3aXNlKSwgc28gdGhlc2UgbGluZXMgYXJlIGVxdWl2YWxlbnQ6XHJcbiAqXHJcbiAqIGBgYFxyXG4gKiBtYXAucGFuVG8oWzUwLCAzMF0pO1xyXG4gKiBtYXAucGFuVG8oe2xvbjogMzAsIGxhdDogNTB9KTtcclxuICogbWFwLnBhblRvKHtsYXQ6IDUwLCBsbmc6IDMwfSk7XHJcbiAqIG1hcC5wYW5UbyhMLmxhdExuZyg1MCwgMzApKTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxuTC5MYXRMbmcgPSBmdW5jdGlvbiAobGF0LCBsbmcsIGFsdCkge1xyXG5cdGlmIChpc05hTihsYXQpIHx8IGlzTmFOKGxuZykpIHtcclxuXHRcdHRocm93IG5ldyBFcnJvcignSW52YWxpZCBMYXRMbmcgb2JqZWN0OiAoJyArIGxhdCArICcsICcgKyBsbmcgKyAnKScpO1xyXG5cdH1cclxuXHJcblx0Ly8gQHByb3BlcnR5IGxhdDogTnVtYmVyXHJcblx0Ly8gTGF0aXR1ZGUgaW4gZGVncmVlc1xyXG5cdHRoaXMubGF0ID0gK2xhdDtcclxuXHJcblx0Ly8gQHByb3BlcnR5IGxuZzogTnVtYmVyXHJcblx0Ly8gTG9uZ2l0dWRlIGluIGRlZ3JlZXNcclxuXHR0aGlzLmxuZyA9ICtsbmc7XHJcblxyXG5cdC8vIEBwcm9wZXJ0eSBhbHQ6IE51bWJlclxyXG5cdC8vIEFsdGl0dWRlIGluIG1ldGVycyAob3B0aW9uYWwpXHJcblx0aWYgKGFsdCAhPT0gdW5kZWZpbmVkKSB7XHJcblx0XHR0aGlzLmFsdCA9ICthbHQ7XHJcblx0fVxyXG59O1xyXG5cclxuTC5MYXRMbmcucHJvdG90eXBlID0ge1xyXG5cdC8vIEBtZXRob2QgZXF1YWxzKG90aGVyTGF0TG5nOiBMYXRMbmcsIG1heE1hcmdpbj86IE51bWJlcik6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgZ2l2ZW4gYExhdExuZ2AgcG9pbnQgaXMgYXQgdGhlIHNhbWUgcG9zaXRpb24gKHdpdGhpbiBhIHNtYWxsIG1hcmdpbiBvZiBlcnJvcikuIFRoZSBtYXJnaW4gb2YgZXJyb3IgY2FuIGJlIG92ZXJyaWRlbiBieSBzZXR0aW5nIGBtYXhNYXJnaW5gIHRvIGEgc21hbGwgbnVtYmVyLlxyXG5cdGVxdWFsczogZnVuY3Rpb24gKG9iaiwgbWF4TWFyZ2luKSB7XHJcblx0XHRpZiAoIW9iaikgeyByZXR1cm4gZmFsc2U7IH1cclxuXHJcblx0XHRvYmogPSBMLmxhdExuZyhvYmopO1xyXG5cclxuXHRcdHZhciBtYXJnaW4gPSBNYXRoLm1heChcclxuXHRcdCAgICAgICAgTWF0aC5hYnModGhpcy5sYXQgLSBvYmoubGF0KSxcclxuXHRcdCAgICAgICAgTWF0aC5hYnModGhpcy5sbmcgLSBvYmoubG5nKSk7XHJcblxyXG5cdFx0cmV0dXJuIG1hcmdpbiA8PSAobWF4TWFyZ2luID09PSB1bmRlZmluZWQgPyAxLjBFLTkgOiBtYXhNYXJnaW4pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgdG9TdHJpbmcoKTogU3RyaW5nXHJcblx0Ly8gUmV0dXJucyBhIHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGUgcG9pbnQgKGZvciBkZWJ1Z2dpbmcgcHVycG9zZXMpLlxyXG5cdHRvU3RyaW5nOiBmdW5jdGlvbiAocHJlY2lzaW9uKSB7XHJcblx0XHRyZXR1cm4gJ0xhdExuZygnICtcclxuXHRcdCAgICAgICAgTC5VdGlsLmZvcm1hdE51bSh0aGlzLmxhdCwgcHJlY2lzaW9uKSArICcsICcgK1xyXG5cdFx0ICAgICAgICBMLlV0aWwuZm9ybWF0TnVtKHRoaXMubG5nLCBwcmVjaXNpb24pICsgJyknO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZGlzdGFuY2VUbyhvdGhlckxhdExuZzogTGF0TG5nKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgZGlzdGFuY2UgKGluIG1ldGVycykgdG8gdGhlIGdpdmVuIGBMYXRMbmdgIGNhbGN1bGF0ZWQgdXNpbmcgdGhlIFtIYXZlcnNpbmUgZm9ybXVsYV0oaHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9IYXZlcnNpbmVfZm9ybXVsYSkuXHJcblx0ZGlzdGFuY2VUbzogZnVuY3Rpb24gKG90aGVyKSB7XHJcblx0XHRyZXR1cm4gTC5DUlMuRWFydGguZGlzdGFuY2UodGhpcywgTC5sYXRMbmcob3RoZXIpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHdyYXAoKTogTGF0TG5nXHJcblx0Ly8gUmV0dXJucyBhIG5ldyBgTGF0TG5nYCBvYmplY3Qgd2l0aCB0aGUgbG9uZ2l0dWRlIHdyYXBwZWQgc28gaXQncyBhbHdheXMgYmV0d2VlbiAtMTgwIGFuZCArMTgwIGRlZ3JlZXMuXHJcblx0d3JhcDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIEwuQ1JTLkVhcnRoLndyYXBMYXRMbmcodGhpcyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB0b0JvdW5kcyhzaXplSW5NZXRlcnM6IE51bWJlcik6IExhdExuZ0JvdW5kc1xyXG5cdC8vIFJldHVybnMgYSBuZXcgYExhdExuZ0JvdW5kc2Agb2JqZWN0IGluIHdoaWNoIGVhY2ggYm91bmRhcnkgaXMgYHNpemVJbk1ldGVyc2AgbWV0ZXJzIGFwYXJ0IGZyb20gdGhlIGBMYXRMbmdgLlxyXG5cdHRvQm91bmRzOiBmdW5jdGlvbiAoc2l6ZUluTWV0ZXJzKSB7XHJcblx0XHR2YXIgbGF0QWNjdXJhY3kgPSAxODAgKiBzaXplSW5NZXRlcnMgLyA0MDA3NTAxNyxcclxuXHRcdCAgICBsbmdBY2N1cmFjeSA9IGxhdEFjY3VyYWN5IC8gTWF0aC5jb3MoKE1hdGguUEkgLyAxODApICogdGhpcy5sYXQpO1xyXG5cclxuXHRcdHJldHVybiBMLmxhdExuZ0JvdW5kcyhcclxuXHRcdCAgICAgICAgW3RoaXMubGF0IC0gbGF0QWNjdXJhY3ksIHRoaXMubG5nIC0gbG5nQWNjdXJhY3ldLFxyXG5cdFx0ICAgICAgICBbdGhpcy5sYXQgKyBsYXRBY2N1cmFjeSwgdGhpcy5sbmcgKyBsbmdBY2N1cmFjeV0pO1xyXG5cdH0sXHJcblxyXG5cdGNsb25lOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuTGF0TG5nKHRoaXMubGF0LCB0aGlzLmxuZywgdGhpcy5hbHQpO1xyXG5cdH1cclxufTtcclxuXHJcblxyXG5cclxuLy8gQGZhY3RvcnkgTC5sYXRMbmcobGF0aXR1ZGU6IE51bWJlciwgbG9uZ2l0dWRlOiBOdW1iZXIsIGFsdGl0dWRlPzogTnVtYmVyKTogTGF0TG5nXHJcbi8vIENyZWF0ZXMgYW4gb2JqZWN0IHJlcHJlc2VudGluZyBhIGdlb2dyYXBoaWNhbCBwb2ludCB3aXRoIHRoZSBnaXZlbiBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlIChhbmQgb3B0aW9uYWxseSBhbHRpdHVkZSkuXHJcblxyXG4vLyBAYWx0ZXJuYXRpdmVcclxuLy8gQGZhY3RvcnkgTC5sYXRMbmcoY29vcmRzOiBBcnJheSk6IExhdExuZ1xyXG4vLyBFeHBlY3RzIGFuIGFycmF5IG9mIHRoZSBmb3JtIGBbTnVtYmVyLCBOdW1iZXJdYCBvciBgW051bWJlciwgTnVtYmVyLCBOdW1iZXJdYCBpbnN0ZWFkLlxyXG5cclxuLy8gQGFsdGVybmF0aXZlXHJcbi8vIEBmYWN0b3J5IEwubGF0TG5nKGNvb3JkczogT2JqZWN0KTogTGF0TG5nXHJcbi8vIEV4cGVjdHMgYW4gcGxhaW4gb2JqZWN0IG9mIHRoZSBmb3JtIGB7bGF0OiBOdW1iZXIsIGxuZzogTnVtYmVyfWAgb3IgYHtsYXQ6IE51bWJlciwgbG5nOiBOdW1iZXIsIGFsdDogTnVtYmVyfWAgaW5zdGVhZC5cclxuXHJcbkwubGF0TG5nID0gZnVuY3Rpb24gKGEsIGIsIGMpIHtcclxuXHRpZiAoYSBpbnN0YW5jZW9mIEwuTGF0TG5nKSB7XHJcblx0XHRyZXR1cm4gYTtcclxuXHR9XHJcblx0aWYgKEwuVXRpbC5pc0FycmF5KGEpICYmIHR5cGVvZiBhWzBdICE9PSAnb2JqZWN0Jykge1xyXG5cdFx0aWYgKGEubGVuZ3RoID09PSAzKSB7XHJcblx0XHRcdHJldHVybiBuZXcgTC5MYXRMbmcoYVswXSwgYVsxXSwgYVsyXSk7XHJcblx0XHR9XHJcblx0XHRpZiAoYS5sZW5ndGggPT09IDIpIHtcclxuXHRcdFx0cmV0dXJuIG5ldyBMLkxhdExuZyhhWzBdLCBhWzFdKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiBudWxsO1xyXG5cdH1cclxuXHRpZiAoYSA9PT0gdW5kZWZpbmVkIHx8IGEgPT09IG51bGwpIHtcclxuXHRcdHJldHVybiBhO1xyXG5cdH1cclxuXHRpZiAodHlwZW9mIGEgPT09ICdvYmplY3QnICYmICdsYXQnIGluIGEpIHtcclxuXHRcdHJldHVybiBuZXcgTC5MYXRMbmcoYS5sYXQsICdsbmcnIGluIGEgPyBhLmxuZyA6IGEubG9uLCBhLmFsdCk7XHJcblx0fVxyXG5cdGlmIChiID09PSB1bmRlZmluZWQpIHtcclxuXHRcdHJldHVybiBudWxsO1xyXG5cdH1cclxuXHRyZXR1cm4gbmV3IEwuTGF0TG5nKGEsIGIsIGMpO1xyXG59O1xyXG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgTGF0TG5nQm91bmRzXHJcbiAqIEBha2EgTC5MYXRMbmdCb3VuZHNcclxuICpcclxuICogUmVwcmVzZW50cyBhIHJlY3Rhbmd1bGFyIGdlb2dyYXBoaWNhbCBhcmVhIG9uIGEgbWFwLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiB2YXIgY29ybmVyMSA9IEwubGF0TG5nKDQwLjcxMiwgLTc0LjIyNyksXHJcbiAqIGNvcm5lcjIgPSBMLmxhdExuZyg0MC43NzQsIC03NC4xMjUpLFxyXG4gKiBib3VuZHMgPSBMLmxhdExuZ0JvdW5kcyhjb3JuZXIxLCBjb3JuZXIyKTtcclxuICogYGBgXHJcbiAqXHJcbiAqIEFsbCBMZWFmbGV0IG1ldGhvZHMgdGhhdCBhY2NlcHQgTGF0TG5nQm91bmRzIG9iamVjdHMgYWxzbyBhY2NlcHQgdGhlbSBpbiBhIHNpbXBsZSBBcnJheSBmb3JtICh1bmxlc3Mgbm90ZWQgb3RoZXJ3aXNlKSwgc28gdGhlIGJvdW5kcyBleGFtcGxlIGFib3ZlIGNhbiBiZSBwYXNzZWQgbGlrZSB0aGlzOlxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBtYXAuZml0Qm91bmRzKFtcclxuICogXHRbNDAuNzEyLCAtNzQuMjI3XSxcclxuICogXHRbNDAuNzc0LCAtNzQuMTI1XVxyXG4gKiBdKTtcclxuICogYGBgXHJcbiAqXHJcbiAqIENhdXRpb246IGlmIHRoZSBhcmVhIGNyb3NzZXMgdGhlIGFudGltZXJpZGlhbiAob2Z0ZW4gY29uZnVzZWQgd2l0aCB0aGUgSW50ZXJuYXRpb25hbCBEYXRlIExpbmUpLCB5b3UgbXVzdCBzcGVjaWZ5IGNvcm5lcnMgX291dHNpZGVfIHRoZSBbLTE4MCwgMTgwXSBkZWdyZWVzIGxvbmdpdHVkZSByYW5nZS5cclxuICovXHJcblxyXG5MLkxhdExuZ0JvdW5kcyA9IGZ1bmN0aW9uIChjb3JuZXIxLCBjb3JuZXIyKSB7IC8vIChMYXRMbmcsIExhdExuZykgb3IgKExhdExuZ1tdKVxyXG5cdGlmICghY29ybmVyMSkgeyByZXR1cm47IH1cclxuXHJcblx0dmFyIGxhdGxuZ3MgPSBjb3JuZXIyID8gW2Nvcm5lcjEsIGNvcm5lcjJdIDogY29ybmVyMTtcclxuXHJcblx0Zm9yICh2YXIgaSA9IDAsIGxlbiA9IGxhdGxuZ3MubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdHRoaXMuZXh0ZW5kKGxhdGxuZ3NbaV0pO1xyXG5cdH1cclxufTtcclxuXHJcbkwuTGF0TG5nQm91bmRzLnByb3RvdHlwZSA9IHtcclxuXHJcblx0Ly8gQG1ldGhvZCBleHRlbmQobGF0bG5nOiBMYXRMbmcpOiB0aGlzXHJcblx0Ly8gRXh0ZW5kIHRoZSBib3VuZHMgdG8gY29udGFpbiB0aGUgZ2l2ZW4gcG9pbnRcclxuXHJcblx0Ly8gQGFsdGVybmF0aXZlXHJcblx0Ly8gQG1ldGhvZCBleHRlbmQob3RoZXJCb3VuZHM6IExhdExuZ0JvdW5kcyk6IHRoaXNcclxuXHQvLyBFeHRlbmQgdGhlIGJvdW5kcyB0byBjb250YWluIHRoZSBnaXZlbiBib3VuZHNcclxuXHRleHRlbmQ6IGZ1bmN0aW9uIChvYmopIHtcclxuXHRcdHZhciBzdyA9IHRoaXMuX3NvdXRoV2VzdCxcclxuXHRcdCAgICBuZSA9IHRoaXMuX25vcnRoRWFzdCxcclxuXHRcdCAgICBzdzIsIG5lMjtcclxuXHJcblx0XHRpZiAob2JqIGluc3RhbmNlb2YgTC5MYXRMbmcpIHtcclxuXHRcdFx0c3cyID0gb2JqO1xyXG5cdFx0XHRuZTIgPSBvYmo7XHJcblxyXG5cdFx0fSBlbHNlIGlmIChvYmogaW5zdGFuY2VvZiBMLkxhdExuZ0JvdW5kcykge1xyXG5cdFx0XHRzdzIgPSBvYmouX3NvdXRoV2VzdDtcclxuXHRcdFx0bmUyID0gb2JqLl9ub3J0aEVhc3Q7XHJcblxyXG5cdFx0XHRpZiAoIXN3MiB8fCAhbmUyKSB7IHJldHVybiB0aGlzOyB9XHJcblxyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0cmV0dXJuIG9iaiA/IHRoaXMuZXh0ZW5kKEwubGF0TG5nKG9iaikgfHwgTC5sYXRMbmdCb3VuZHMob2JqKSkgOiB0aGlzO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICghc3cgJiYgIW5lKSB7XHJcblx0XHRcdHRoaXMuX3NvdXRoV2VzdCA9IG5ldyBMLkxhdExuZyhzdzIubGF0LCBzdzIubG5nKTtcclxuXHRcdFx0dGhpcy5fbm9ydGhFYXN0ID0gbmV3IEwuTGF0TG5nKG5lMi5sYXQsIG5lMi5sbmcpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0c3cubGF0ID0gTWF0aC5taW4oc3cyLmxhdCwgc3cubGF0KTtcclxuXHRcdFx0c3cubG5nID0gTWF0aC5taW4oc3cyLmxuZywgc3cubG5nKTtcclxuXHRcdFx0bmUubGF0ID0gTWF0aC5tYXgobmUyLmxhdCwgbmUubGF0KTtcclxuXHRcdFx0bmUubG5nID0gTWF0aC5tYXgobmUyLmxuZywgbmUubG5nKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHBhZChidWZmZXJSYXRpbzogTnVtYmVyKTogTGF0TG5nQm91bmRzXHJcblx0Ly8gUmV0dXJucyBiaWdnZXIgYm91bmRzIGNyZWF0ZWQgYnkgZXh0ZW5kaW5nIHRoZSBjdXJyZW50IGJvdW5kcyBieSBhIGdpdmVuIHBlcmNlbnRhZ2UgaW4gZWFjaCBkaXJlY3Rpb24uXHJcblx0cGFkOiBmdW5jdGlvbiAoYnVmZmVyUmF0aW8pIHtcclxuXHRcdHZhciBzdyA9IHRoaXMuX3NvdXRoV2VzdCxcclxuXHRcdCAgICBuZSA9IHRoaXMuX25vcnRoRWFzdCxcclxuXHRcdCAgICBoZWlnaHRCdWZmZXIgPSBNYXRoLmFicyhzdy5sYXQgLSBuZS5sYXQpICogYnVmZmVyUmF0aW8sXHJcblx0XHQgICAgd2lkdGhCdWZmZXIgPSBNYXRoLmFicyhzdy5sbmcgLSBuZS5sbmcpICogYnVmZmVyUmF0aW87XHJcblxyXG5cdFx0cmV0dXJuIG5ldyBMLkxhdExuZ0JvdW5kcyhcclxuXHRcdCAgICAgICAgbmV3IEwuTGF0TG5nKHN3LmxhdCAtIGhlaWdodEJ1ZmZlciwgc3cubG5nIC0gd2lkdGhCdWZmZXIpLFxyXG5cdFx0ICAgICAgICBuZXcgTC5MYXRMbmcobmUubGF0ICsgaGVpZ2h0QnVmZmVyLCBuZS5sbmcgKyB3aWR0aEJ1ZmZlcikpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Q2VudGVyKCk6IExhdExuZ1xyXG5cdC8vIFJldHVybnMgdGhlIGNlbnRlciBwb2ludCBvZiB0aGUgYm91bmRzLlxyXG5cdGdldENlbnRlcjogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIG5ldyBMLkxhdExuZyhcclxuXHRcdCAgICAgICAgKHRoaXMuX3NvdXRoV2VzdC5sYXQgKyB0aGlzLl9ub3J0aEVhc3QubGF0KSAvIDIsXHJcblx0XHQgICAgICAgICh0aGlzLl9zb3V0aFdlc3QubG5nICsgdGhpcy5fbm9ydGhFYXN0LmxuZykgLyAyKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldFNvdXRoV2VzdCgpOiBMYXRMbmdcclxuXHQvLyBSZXR1cm5zIHRoZSBzb3V0aC13ZXN0IHBvaW50IG9mIHRoZSBib3VuZHMuXHJcblx0Z2V0U291dGhXZXN0OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fc291dGhXZXN0O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Tm9ydGhFYXN0KCk6IExhdExuZ1xyXG5cdC8vIFJldHVybnMgdGhlIG5vcnRoLWVhc3QgcG9pbnQgb2YgdGhlIGJvdW5kcy5cclxuXHRnZXROb3J0aEVhc3Q6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9ub3J0aEVhc3Q7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXROb3J0aFdlc3QoKTogTGF0TG5nXHJcblx0Ly8gUmV0dXJucyB0aGUgbm9ydGgtd2VzdCBwb2ludCBvZiB0aGUgYm91bmRzLlxyXG5cdGdldE5vcnRoV2VzdDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIG5ldyBMLkxhdExuZyh0aGlzLmdldE5vcnRoKCksIHRoaXMuZ2V0V2VzdCgpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldFNvdXRoRWFzdCgpOiBMYXRMbmdcclxuXHQvLyBSZXR1cm5zIHRoZSBzb3V0aC1lYXN0IHBvaW50IG9mIHRoZSBib3VuZHMuXHJcblx0Z2V0U291dGhFYXN0OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuTGF0TG5nKHRoaXMuZ2V0U291dGgoKSwgdGhpcy5nZXRFYXN0KCkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0V2VzdCgpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSB3ZXN0IGxvbmdpdHVkZSBvZiB0aGUgYm91bmRzXHJcblx0Z2V0V2VzdDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX3NvdXRoV2VzdC5sbmc7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRTb3V0aCgpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBzb3V0aCBsYXRpdHVkZSBvZiB0aGUgYm91bmRzXHJcblx0Z2V0U291dGg6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9zb3V0aFdlc3QubGF0O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0RWFzdCgpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBlYXN0IGxvbmdpdHVkZSBvZiB0aGUgYm91bmRzXHJcblx0Z2V0RWFzdDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX25vcnRoRWFzdC5sbmc7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXROb3J0aCgpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBub3J0aCBsYXRpdHVkZSBvZiB0aGUgYm91bmRzXHJcblx0Z2V0Tm9ydGg6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9ub3J0aEVhc3QubGF0O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgY29udGFpbnMob3RoZXJCb3VuZHM6IExhdExuZ0JvdW5kcyk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgcmVjdGFuZ2xlIGNvbnRhaW5zIHRoZSBnaXZlbiBvbmUuXHJcblxyXG5cdC8vIEBhbHRlcm5hdGl2ZVxyXG5cdC8vIEBtZXRob2QgY29udGFpbnMgKGxhdGxuZzogTGF0TG5nKTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSByZWN0YW5nbGUgY29udGFpbnMgdGhlIGdpdmVuIHBvaW50LlxyXG5cdGNvbnRhaW5zOiBmdW5jdGlvbiAob2JqKSB7IC8vIChMYXRMbmdCb3VuZHMpIG9yIChMYXRMbmcpIC0+IEJvb2xlYW5cclxuXHRcdGlmICh0eXBlb2Ygb2JqWzBdID09PSAnbnVtYmVyJyB8fCBvYmogaW5zdGFuY2VvZiBMLkxhdExuZykge1xyXG5cdFx0XHRvYmogPSBMLmxhdExuZyhvYmopO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0b2JqID0gTC5sYXRMbmdCb3VuZHMob2JqKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgc3cgPSB0aGlzLl9zb3V0aFdlc3QsXHJcblx0XHQgICAgbmUgPSB0aGlzLl9ub3J0aEVhc3QsXHJcblx0XHQgICAgc3cyLCBuZTI7XHJcblxyXG5cdFx0aWYgKG9iaiBpbnN0YW5jZW9mIEwuTGF0TG5nQm91bmRzKSB7XHJcblx0XHRcdHN3MiA9IG9iai5nZXRTb3V0aFdlc3QoKTtcclxuXHRcdFx0bmUyID0gb2JqLmdldE5vcnRoRWFzdCgpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0c3cyID0gbmUyID0gb2JqO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiAoc3cyLmxhdCA+PSBzdy5sYXQpICYmIChuZTIubGF0IDw9IG5lLmxhdCkgJiZcclxuXHRcdCAgICAgICAoc3cyLmxuZyA+PSBzdy5sbmcpICYmIChuZTIubG5nIDw9IG5lLmxuZyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBpbnRlcnNlY3RzKG90aGVyQm91bmRzOiBMYXRMbmdCb3VuZHMpOiBCb29sZWFuXHJcblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIHJlY3RhbmdsZSBpbnRlcnNlY3RzIHRoZSBnaXZlbiBib3VuZHMuIFR3byBib3VuZHMgaW50ZXJzZWN0IGlmIHRoZXkgaGF2ZSBhdCBsZWFzdCBvbmUgcG9pbnQgaW4gY29tbW9uLlxyXG5cdGludGVyc2VjdHM6IGZ1bmN0aW9uIChib3VuZHMpIHtcclxuXHRcdGJvdW5kcyA9IEwubGF0TG5nQm91bmRzKGJvdW5kcyk7XHJcblxyXG5cdFx0dmFyIHN3ID0gdGhpcy5fc291dGhXZXN0LFxyXG5cdFx0ICAgIG5lID0gdGhpcy5fbm9ydGhFYXN0LFxyXG5cdFx0ICAgIHN3MiA9IGJvdW5kcy5nZXRTb3V0aFdlc3QoKSxcclxuXHRcdCAgICBuZTIgPSBib3VuZHMuZ2V0Tm9ydGhFYXN0KCksXHJcblxyXG5cdFx0ICAgIGxhdEludGVyc2VjdHMgPSAobmUyLmxhdCA+PSBzdy5sYXQpICYmIChzdzIubGF0IDw9IG5lLmxhdCksXHJcblx0XHQgICAgbG5nSW50ZXJzZWN0cyA9IChuZTIubG5nID49IHN3LmxuZykgJiYgKHN3Mi5sbmcgPD0gbmUubG5nKTtcclxuXHJcblx0XHRyZXR1cm4gbGF0SW50ZXJzZWN0cyAmJiBsbmdJbnRlcnNlY3RzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgb3ZlcmxhcHMob3RoZXJCb3VuZHM6IEJvdW5kcyk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgcmVjdGFuZ2xlIG92ZXJsYXBzIHRoZSBnaXZlbiBib3VuZHMuIFR3byBib3VuZHMgb3ZlcmxhcCBpZiB0aGVpciBpbnRlcnNlY3Rpb24gaXMgYW4gYXJlYS5cclxuXHRvdmVybGFwczogZnVuY3Rpb24gKGJvdW5kcykge1xyXG5cdFx0Ym91bmRzID0gTC5sYXRMbmdCb3VuZHMoYm91bmRzKTtcclxuXHJcblx0XHR2YXIgc3cgPSB0aGlzLl9zb3V0aFdlc3QsXHJcblx0XHQgICAgbmUgPSB0aGlzLl9ub3J0aEVhc3QsXHJcblx0XHQgICAgc3cyID0gYm91bmRzLmdldFNvdXRoV2VzdCgpLFxyXG5cdFx0ICAgIG5lMiA9IGJvdW5kcy5nZXROb3J0aEVhc3QoKSxcclxuXHJcblx0XHQgICAgbGF0T3ZlcmxhcHMgPSAobmUyLmxhdCA+IHN3LmxhdCkgJiYgKHN3Mi5sYXQgPCBuZS5sYXQpLFxyXG5cdFx0ICAgIGxuZ092ZXJsYXBzID0gKG5lMi5sbmcgPiBzdy5sbmcpICYmIChzdzIubG5nIDwgbmUubG5nKTtcclxuXHJcblx0XHRyZXR1cm4gbGF0T3ZlcmxhcHMgJiYgbG5nT3ZlcmxhcHM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB0b0JCb3hTdHJpbmcoKTogU3RyaW5nXHJcblx0Ly8gUmV0dXJucyBhIHN0cmluZyB3aXRoIGJvdW5kaW5nIGJveCBjb29yZGluYXRlcyBpbiBhICdzb3V0aHdlc3RfbG5nLHNvdXRod2VzdF9sYXQsbm9ydGhlYXN0X2xuZyxub3J0aGVhc3RfbGF0JyBmb3JtYXQuIFVzZWZ1bCBmb3Igc2VuZGluZyByZXF1ZXN0cyB0byB3ZWIgc2VydmljZXMgdGhhdCByZXR1cm4gZ2VvIGRhdGEuXHJcblx0dG9CQm94U3RyaW5nOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gW3RoaXMuZ2V0V2VzdCgpLCB0aGlzLmdldFNvdXRoKCksIHRoaXMuZ2V0RWFzdCgpLCB0aGlzLmdldE5vcnRoKCldLmpvaW4oJywnKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGVxdWFscyhvdGhlckJvdW5kczogTGF0TG5nQm91bmRzKTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSByZWN0YW5nbGUgaXMgZXF1aXZhbGVudCAod2l0aGluIGEgc21hbGwgbWFyZ2luIG9mIGVycm9yKSB0byB0aGUgZ2l2ZW4gYm91bmRzLlxyXG5cdGVxdWFsczogZnVuY3Rpb24gKGJvdW5kcykge1xyXG5cdFx0aWYgKCFib3VuZHMpIHsgcmV0dXJuIGZhbHNlOyB9XHJcblxyXG5cdFx0Ym91bmRzID0gTC5sYXRMbmdCb3VuZHMoYm91bmRzKTtcclxuXHJcblx0XHRyZXR1cm4gdGhpcy5fc291dGhXZXN0LmVxdWFscyhib3VuZHMuZ2V0U291dGhXZXN0KCkpICYmXHJcblx0XHQgICAgICAgdGhpcy5fbm9ydGhFYXN0LmVxdWFscyhib3VuZHMuZ2V0Tm9ydGhFYXN0KCkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgaXNWYWxpZCgpOiBCb29sZWFuXHJcblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIGJvdW5kcyBhcmUgcHJvcGVybHkgaW5pdGlhbGl6ZWQuXHJcblx0aXNWYWxpZDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuICEhKHRoaXMuX3NvdXRoV2VzdCAmJiB0aGlzLl9ub3J0aEVhc3QpO1xyXG5cdH1cclxufTtcclxuXHJcbi8vIFRPRE8gSW50ZXJuYXRpb25hbCBkYXRlIGxpbmU/XHJcblxyXG4vLyBAZmFjdG9yeSBMLmxhdExuZ0JvdW5kcyhjb3JuZXIxOiBMYXRMbmcsIGNvcm5lcjI6IExhdExuZylcclxuLy8gQ3JlYXRlcyBhIGBMYXRMbmdCb3VuZHNgIG9iamVjdCBieSBkZWZpbmluZyB0d28gZGlhZ29uYWxseSBvcHBvc2l0ZSBjb3JuZXJzIG9mIHRoZSByZWN0YW5nbGUuXHJcblxyXG4vLyBAYWx0ZXJuYXRpdmVcclxuLy8gQGZhY3RvcnkgTC5sYXRMbmdCb3VuZHMobGF0bG5nczogTGF0TG5nW10pXHJcbi8vIENyZWF0ZXMgYSBgTGF0TG5nQm91bmRzYCBvYmplY3QgZGVmaW5lZCBieSB0aGUgZ2VvZ3JhcGhpY2FsIHBvaW50cyBpdCBjb250YWlucy4gVmVyeSB1c2VmdWwgZm9yIHpvb21pbmcgdGhlIG1hcCB0byBmaXQgYSBwYXJ0aWN1bGFyIHNldCBvZiBsb2NhdGlvbnMgd2l0aCBbYGZpdEJvdW5kc2BdKCNtYXAtZml0Ym91bmRzKS5cclxuTC5sYXRMbmdCb3VuZHMgPSBmdW5jdGlvbiAoYSwgYikge1xyXG5cdGlmIChhIGluc3RhbmNlb2YgTC5MYXRMbmdCb3VuZHMpIHtcclxuXHRcdHJldHVybiBhO1xyXG5cdH1cclxuXHRyZXR1cm4gbmV3IEwuTGF0TG5nQm91bmRzKGEsIGIpO1xyXG59O1xyXG5cblxuXG4vKlxyXG4gKiBAbmFtZXNwYWNlIFByb2plY3Rpb25cclxuICogQHNlY3Rpb25cclxuICogTGVhZmxldCBjb21lcyB3aXRoIGEgc2V0IG9mIGFscmVhZHkgZGVmaW5lZCBQcm9qZWN0aW9ucyBvdXQgb2YgdGhlIGJveDpcclxuICpcclxuICogQHByb2plY3Rpb24gTC5Qcm9qZWN0aW9uLkxvbkxhdFxyXG4gKlxyXG4gKiBFcXVpcmVjdGFuZ3VsYXIsIG9yIFBsYXRlIENhcnJlZSBwcm9qZWN0aW9uIOKAlCB0aGUgbW9zdCBzaW1wbGUgcHJvamVjdGlvbixcclxuICogbW9zdGx5IHVzZWQgYnkgR0lTIGVudGh1c2lhc3RzLiBEaXJlY3RseSBtYXBzIGB4YCBhcyBsb25naXR1ZGUsIGFuZCBgeWAgYXNcclxuICogbGF0aXR1ZGUuIEFsc28gc3VpdGFibGUgZm9yIGZsYXQgd29ybGRzLCBlLmcuIGdhbWUgbWFwcy4gVXNlZCBieSB0aGVcclxuICogYEVQU0c6MzM5NWAgYW5kIGBTaW1wbGVgIENSUy5cclxuICovXHJcblxyXG5MLlByb2plY3Rpb24gPSB7fTtcclxuXHJcbkwuUHJvamVjdGlvbi5Mb25MYXQgPSB7XHJcblx0cHJvamVjdDogZnVuY3Rpb24gKGxhdGxuZykge1xyXG5cdFx0cmV0dXJuIG5ldyBMLlBvaW50KGxhdGxuZy5sbmcsIGxhdGxuZy5sYXQpO1xyXG5cdH0sXHJcblxyXG5cdHVucHJvamVjdDogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuTGF0TG5nKHBvaW50LnksIHBvaW50LngpO1xyXG5cdH0sXHJcblxyXG5cdGJvdW5kczogTC5ib3VuZHMoWy0xODAsIC05MF0sIFsxODAsIDkwXSlcclxufTtcclxuXG5cblxuLypcclxuICogQG5hbWVzcGFjZSBQcm9qZWN0aW9uXHJcbiAqIEBwcm9qZWN0aW9uIEwuUHJvamVjdGlvbi5TcGhlcmljYWxNZXJjYXRvclxyXG4gKlxyXG4gKiBTcGhlcmljYWwgTWVyY2F0b3IgcHJvamVjdGlvbiDigJQgdGhlIG1vc3QgY29tbW9uIHByb2plY3Rpb24gZm9yIG9ubGluZSBtYXBzLFxyXG4gKiB1c2VkIGJ5IGFsbW9zdCBhbGwgZnJlZSBhbmQgY29tbWVyY2lhbCB0aWxlIHByb3ZpZGVycy4gQXNzdW1lcyB0aGF0IEVhcnRoIGlzXHJcbiAqIGEgc3BoZXJlLiBVc2VkIGJ5IHRoZSBgRVBTRzozODU3YCBDUlMuXHJcbiAqL1xyXG5cclxuTC5Qcm9qZWN0aW9uLlNwaGVyaWNhbE1lcmNhdG9yID0ge1xyXG5cclxuXHRSOiA2Mzc4MTM3LFxyXG5cdE1BWF9MQVRJVFVERTogODUuMDUxMTI4Nzc5OCxcclxuXHJcblx0cHJvamVjdDogZnVuY3Rpb24gKGxhdGxuZykge1xyXG5cdFx0dmFyIGQgPSBNYXRoLlBJIC8gMTgwLFxyXG5cdFx0ICAgIG1heCA9IHRoaXMuTUFYX0xBVElUVURFLFxyXG5cdFx0ICAgIGxhdCA9IE1hdGgubWF4KE1hdGgubWluKG1heCwgbGF0bG5nLmxhdCksIC1tYXgpLFxyXG5cdFx0ICAgIHNpbiA9IE1hdGguc2luKGxhdCAqIGQpO1xyXG5cclxuXHRcdHJldHVybiBuZXcgTC5Qb2ludChcclxuXHRcdFx0XHR0aGlzLlIgKiBsYXRsbmcubG5nICogZCxcclxuXHRcdFx0XHR0aGlzLlIgKiBNYXRoLmxvZygoMSArIHNpbikgLyAoMSAtIHNpbikpIC8gMik7XHJcblx0fSxcclxuXHJcblx0dW5wcm9qZWN0OiBmdW5jdGlvbiAocG9pbnQpIHtcclxuXHRcdHZhciBkID0gMTgwIC8gTWF0aC5QSTtcclxuXHJcblx0XHRyZXR1cm4gbmV3IEwuTGF0TG5nKFxyXG5cdFx0XHQoMiAqIE1hdGguYXRhbihNYXRoLmV4cChwb2ludC55IC8gdGhpcy5SKSkgLSAoTWF0aC5QSSAvIDIpKSAqIGQsXHJcblx0XHRcdHBvaW50LnggKiBkIC8gdGhpcy5SKTtcclxuXHR9LFxyXG5cclxuXHRib3VuZHM6IChmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgZCA9IDYzNzgxMzcgKiBNYXRoLlBJO1xyXG5cdFx0cmV0dXJuIEwuYm91bmRzKFstZCwgLWRdLCBbZCwgZF0pO1xyXG5cdH0pKClcclxufTtcclxuXG5cblxuLypcclxuICogQGNsYXNzIENSU1xyXG4gKiBAYWthIEwuQ1JTXHJcbiAqIEFic3RyYWN0IGNsYXNzIHRoYXQgZGVmaW5lcyBjb29yZGluYXRlIHJlZmVyZW5jZSBzeXN0ZW1zIGZvciBwcm9qZWN0aW5nXHJcbiAqIGdlb2dyYXBoaWNhbCBwb2ludHMgaW50byBwaXhlbCAoc2NyZWVuKSBjb29yZGluYXRlcyBhbmQgYmFjayAoYW5kIHRvXHJcbiAqIGNvb3JkaW5hdGVzIGluIG90aGVyIHVuaXRzIGZvciBbV01TXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9XZWJfTWFwX1NlcnZpY2UpIHNlcnZpY2VzKS4gU2VlXHJcbiAqIFtzcGF0aWFsIHJlZmVyZW5jZSBzeXN0ZW1dKGh0dHA6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ29vcmRpbmF0ZV9yZWZlcmVuY2Vfc3lzdGVtKS5cclxuICpcclxuICogTGVhZmxldCBkZWZpbmVzIHRoZSBtb3N0IHVzdWFsIENSU3MgYnkgZGVmYXVsdC4gSWYgeW91IHdhbnQgdG8gdXNlIGFcclxuICogQ1JTIG5vdCBkZWZpbmVkIGJ5IGRlZmF1bHQsIHRha2UgYSBsb29rIGF0IHRoZVxyXG4gKiBbUHJvajRMZWFmbGV0XShodHRwczovL2dpdGh1Yi5jb20va2FydGVuYS9Qcm9qNExlYWZsZXQpIHBsdWdpbi5cclxuICovXHJcblxyXG5MLkNSUyA9IHtcclxuXHQvLyBAbWV0aG9kIGxhdExuZ1RvUG9pbnQobGF0bG5nOiBMYXRMbmcsIHpvb206IE51bWJlcik6IFBvaW50XHJcblx0Ly8gUHJvamVjdHMgZ2VvZ3JhcGhpY2FsIGNvb3JkaW5hdGVzIGludG8gcGl4ZWwgY29vcmRpbmF0ZXMgZm9yIGEgZ2l2ZW4gem9vbS5cclxuXHRsYXRMbmdUb1BvaW50OiBmdW5jdGlvbiAobGF0bG5nLCB6b29tKSB7XHJcblx0XHR2YXIgcHJvamVjdGVkUG9pbnQgPSB0aGlzLnByb2plY3Rpb24ucHJvamVjdChsYXRsbmcpLFxyXG5cdFx0ICAgIHNjYWxlID0gdGhpcy5zY2FsZSh6b29tKTtcclxuXHJcblx0XHRyZXR1cm4gdGhpcy50cmFuc2Zvcm1hdGlvbi5fdHJhbnNmb3JtKHByb2plY3RlZFBvaW50LCBzY2FsZSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBwb2ludFRvTGF0TG5nKHBvaW50OiBQb2ludCwgem9vbTogTnVtYmVyKTogTGF0TG5nXHJcblx0Ly8gVGhlIGludmVyc2Ugb2YgYGxhdExuZ1RvUG9pbnRgLiBQcm9qZWN0cyBwaXhlbCBjb29yZGluYXRlcyBvbiBhIGdpdmVuXHJcblx0Ly8gem9vbSBpbnRvIGdlb2dyYXBoaWNhbCBjb29yZGluYXRlcy5cclxuXHRwb2ludFRvTGF0TG5nOiBmdW5jdGlvbiAocG9pbnQsIHpvb20pIHtcclxuXHRcdHZhciBzY2FsZSA9IHRoaXMuc2NhbGUoem9vbSksXHJcblx0XHQgICAgdW50cmFuc2Zvcm1lZFBvaW50ID0gdGhpcy50cmFuc2Zvcm1hdGlvbi51bnRyYW5zZm9ybShwb2ludCwgc2NhbGUpO1xyXG5cclxuXHRcdHJldHVybiB0aGlzLnByb2plY3Rpb24udW5wcm9qZWN0KHVudHJhbnNmb3JtZWRQb2ludCk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBwcm9qZWN0KGxhdGxuZzogTGF0TG5nKTogUG9pbnRcclxuXHQvLyBQcm9qZWN0cyBnZW9ncmFwaGljYWwgY29vcmRpbmF0ZXMgaW50byBjb29yZGluYXRlcyBpbiB1bml0cyBhY2NlcHRlZCBmb3JcclxuXHQvLyB0aGlzIENSUyAoZS5nLiBtZXRlcnMgZm9yIEVQU0c6Mzg1NywgZm9yIHBhc3NpbmcgaXQgdG8gV01TIHNlcnZpY2VzKS5cclxuXHRwcm9qZWN0OiBmdW5jdGlvbiAobGF0bG5nKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5wcm9qZWN0aW9uLnByb2plY3QobGF0bG5nKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHVucHJvamVjdChwb2ludDogUG9pbnQpOiBMYXRMbmdcclxuXHQvLyBHaXZlbiBhIHByb2plY3RlZCBjb29yZGluYXRlIHJldHVybnMgdGhlIGNvcnJlc3BvbmRpbmcgTGF0TG5nLlxyXG5cdC8vIFRoZSBpbnZlcnNlIG9mIGBwcm9qZWN0YC5cclxuXHR1bnByb2plY3Q6IGZ1bmN0aW9uIChwb2ludCkge1xyXG5cdFx0cmV0dXJuIHRoaXMucHJvamVjdGlvbi51bnByb2plY3QocG9pbnQpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2NhbGUoem9vbTogTnVtYmVyKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgc2NhbGUgdXNlZCB3aGVuIHRyYW5zZm9ybWluZyBwcm9qZWN0ZWQgY29vcmRpbmF0ZXMgaW50b1xyXG5cdC8vIHBpeGVsIGNvb3JkaW5hdGVzIGZvciBhIHBhcnRpY3VsYXIgem9vbS4gRm9yIGV4YW1wbGUsIGl0IHJldHVybnNcclxuXHQvLyBgMjU2ICogMl56b29tYCBmb3IgTWVyY2F0b3ItYmFzZWQgQ1JTLlxyXG5cdHNjYWxlOiBmdW5jdGlvbiAoem9vbSkge1xyXG5cdFx0cmV0dXJuIDI1NiAqIE1hdGgucG93KDIsIHpvb20pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgem9vbShzY2FsZTogTnVtYmVyKTogTnVtYmVyXHJcblx0Ly8gSW52ZXJzZSBvZiBgc2NhbGUoKWAsIHJldHVybnMgdGhlIHpvb20gbGV2ZWwgY29ycmVzcG9uZGluZyB0byBhIHNjYWxlXHJcblx0Ly8gZmFjdG9yIG9mIGBzY2FsZWAuXHJcblx0em9vbTogZnVuY3Rpb24gKHNjYWxlKSB7XHJcblx0XHRyZXR1cm4gTWF0aC5sb2coc2NhbGUgLyAyNTYpIC8gTWF0aC5MTjI7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRQcm9qZWN0ZWRCb3VuZHMoem9vbTogTnVtYmVyKTogQm91bmRzXHJcblx0Ly8gUmV0dXJucyB0aGUgcHJvamVjdGlvbidzIGJvdW5kcyBzY2FsZWQgYW5kIHRyYW5zZm9ybWVkIGZvciB0aGUgcHJvdmlkZWQgYHpvb21gLlxyXG5cdGdldFByb2plY3RlZEJvdW5kczogZnVuY3Rpb24gKHpvb20pIHtcclxuXHRcdGlmICh0aGlzLmluZmluaXRlKSB7IHJldHVybiBudWxsOyB9XHJcblxyXG5cdFx0dmFyIGIgPSB0aGlzLnByb2plY3Rpb24uYm91bmRzLFxyXG5cdFx0ICAgIHMgPSB0aGlzLnNjYWxlKHpvb20pLFxyXG5cdFx0ICAgIG1pbiA9IHRoaXMudHJhbnNmb3JtYXRpb24udHJhbnNmb3JtKGIubWluLCBzKSxcclxuXHRcdCAgICBtYXggPSB0aGlzLnRyYW5zZm9ybWF0aW9uLnRyYW5zZm9ybShiLm1heCwgcyk7XHJcblxyXG5cdFx0cmV0dXJuIEwuYm91bmRzKG1pbiwgbWF4KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGRpc3RhbmNlKGxhdGxuZzE6IExhdExuZywgbGF0bG5nMjogTGF0TG5nKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgZGlzdGFuY2UgYmV0d2VlbiB0d28gZ2VvZ3JhcGhpY2FsIGNvb3JkaW5hdGVzLlxyXG5cclxuXHQvLyBAcHJvcGVydHkgY29kZTogU3RyaW5nXHJcblx0Ly8gU3RhbmRhcmQgY29kZSBuYW1lIG9mIHRoZSBDUlMgcGFzc2VkIGludG8gV01TIHNlcnZpY2VzIChlLmcuIGAnRVBTRzozODU3J2ApXHJcblx0Ly9cclxuXHQvLyBAcHJvcGVydHkgd3JhcExuZzogTnVtYmVyW11cclxuXHQvLyBBbiBhcnJheSBvZiB0d28gbnVtYmVycyBkZWZpbmluZyB3aGV0aGVyIHRoZSBsb25naXR1ZGUgKGhvcml6b250YWwpIGNvb3JkaW5hdGVcclxuXHQvLyBheGlzIHdyYXBzIGFyb3VuZCBhIGdpdmVuIHJhbmdlIGFuZCBob3cuIERlZmF1bHRzIHRvIGBbLTE4MCwgMTgwXWAgaW4gbW9zdFxyXG5cdC8vIGdlb2dyYXBoaWNhbCBDUlNzLiBJZiBgdW5kZWZpbmVkYCwgdGhlIGxvbmdpdHVkZSBheGlzIGRvZXMgbm90IHdyYXAgYXJvdW5kLlxyXG5cdC8vXHJcblx0Ly8gQHByb3BlcnR5IHdyYXBMYXQ6IE51bWJlcltdXHJcblx0Ly8gTGlrZSBgd3JhcExuZ2AsIGJ1dCBmb3IgdGhlIGxhdGl0dWRlICh2ZXJ0aWNhbCkgYXhpcy5cclxuXHJcblx0Ly8gd3JhcExuZzogW21pbiwgbWF4XSxcclxuXHQvLyB3cmFwTGF0OiBbbWluLCBtYXhdLFxyXG5cclxuXHQvLyBAcHJvcGVydHkgaW5maW5pdGU6IEJvb2xlYW5cclxuXHQvLyBJZiB0cnVlLCB0aGUgY29vcmRpbmF0ZSBzcGFjZSB3aWxsIGJlIHVuYm91bmRlZCAoaW5maW5pdGUgaW4gYm90aCBheGVzKVxyXG5cdGluZmluaXRlOiBmYWxzZSxcclxuXHJcblx0Ly8gQG1ldGhvZCB3cmFwTGF0TG5nKGxhdGxuZzogTGF0TG5nKTogTGF0TG5nXHJcblx0Ly8gUmV0dXJucyBhIGBMYXRMbmdgIHdoZXJlIGxhdCBhbmQgbG5nIGhhcyBiZWVuIHdyYXBwZWQgYWNjb3JkaW5nIHRvIHRoZVxyXG5cdC8vIENSUydzIGB3cmFwTGF0YCBhbmQgYHdyYXBMbmdgIHByb3BlcnRpZXMsIGlmIHRoZXkgYXJlIG91dHNpZGUgdGhlIENSUydzIGJvdW5kcy5cclxuXHR3cmFwTGF0TG5nOiBmdW5jdGlvbiAobGF0bG5nKSB7XHJcblx0XHR2YXIgbG5nID0gdGhpcy53cmFwTG5nID8gTC5VdGlsLndyYXBOdW0obGF0bG5nLmxuZywgdGhpcy53cmFwTG5nLCB0cnVlKSA6IGxhdGxuZy5sbmcsXHJcblx0XHQgICAgbGF0ID0gdGhpcy53cmFwTGF0ID8gTC5VdGlsLndyYXBOdW0obGF0bG5nLmxhdCwgdGhpcy53cmFwTGF0LCB0cnVlKSA6IGxhdGxuZy5sYXQsXHJcblx0XHQgICAgYWx0ID0gbGF0bG5nLmFsdDtcclxuXHJcblx0XHRyZXR1cm4gTC5sYXRMbmcobGF0LCBsbmcsIGFsdCk7XHJcblx0fVxyXG59O1xyXG5cblxuXG4vKlxuICogQG5hbWVzcGFjZSBDUlNcbiAqIEBjcnMgTC5DUlMuU2ltcGxlXG4gKlxuICogQSBzaW1wbGUgQ1JTIHRoYXQgbWFwcyBsb25naXR1ZGUgYW5kIGxhdGl0dWRlIGludG8gYHhgIGFuZCBgeWAgZGlyZWN0bHkuXG4gKiBNYXkgYmUgdXNlZCBmb3IgbWFwcyBvZiBmbGF0IHN1cmZhY2VzIChlLmcuIGdhbWUgbWFwcykuIE5vdGUgdGhhdCB0aGUgYHlgXG4gKiBheGlzIHNob3VsZCBzdGlsbCBiZSBpbnZlcnRlZCAoZ29pbmcgZnJvbSBib3R0b20gdG8gdG9wKS4gYGRpc3RhbmNlKClgIHJldHVybnNcbiAqIHNpbXBsZSBldWNsaWRlYW4gZGlzdGFuY2UuXG4gKi9cblxuTC5DUlMuU2ltcGxlID0gTC5leHRlbmQoe30sIEwuQ1JTLCB7XG5cdHByb2plY3Rpb246IEwuUHJvamVjdGlvbi5Mb25MYXQsXG5cdHRyYW5zZm9ybWF0aW9uOiBuZXcgTC5UcmFuc2Zvcm1hdGlvbigxLCAwLCAtMSwgMCksXG5cblx0c2NhbGU6IGZ1bmN0aW9uICh6b29tKSB7XG5cdFx0cmV0dXJuIE1hdGgucG93KDIsIHpvb20pO1xuXHR9LFxuXG5cdHpvb206IGZ1bmN0aW9uIChzY2FsZSkge1xuXHRcdHJldHVybiBNYXRoLmxvZyhzY2FsZSkgLyBNYXRoLkxOMjtcblx0fSxcblxuXHRkaXN0YW5jZTogZnVuY3Rpb24gKGxhdGxuZzEsIGxhdGxuZzIpIHtcblx0XHR2YXIgZHggPSBsYXRsbmcyLmxuZyAtIGxhdGxuZzEubG5nLFxuXHRcdCAgICBkeSA9IGxhdGxuZzIubGF0IC0gbGF0bG5nMS5sYXQ7XG5cblx0XHRyZXR1cm4gTWF0aC5zcXJ0KGR4ICogZHggKyBkeSAqIGR5KTtcblx0fSxcblxuXHRpbmZpbml0ZTogdHJ1ZVxufSk7XG5cblxuXG4vKlxuICogQG5hbWVzcGFjZSBDUlNcbiAqIEBjcnMgTC5DUlMuRWFydGhcbiAqXG4gKiBTZXJ2ZXMgYXMgdGhlIGJhc2UgZm9yIENSUyB0aGF0IGFyZSBnbG9iYWwgc3VjaCB0aGF0IHRoZXkgY292ZXIgdGhlIGVhcnRoLlxuICogQ2FuIG9ubHkgYmUgdXNlZCBhcyB0aGUgYmFzZSBmb3Igb3RoZXIgQ1JTIGFuZCBjYW5ub3QgYmUgdXNlZCBkaXJlY3RseSxcbiAqIHNpbmNlIGl0IGRvZXMgbm90IGhhdmUgYSBgY29kZWAsIGBwcm9qZWN0aW9uYCBvciBgdHJhbnNmb3JtYXRpb25gLiBgZGlzdGFuY2UoKWAgcmV0dXJuc1xuICogbWV0ZXJzLlxuICovXG5cbkwuQ1JTLkVhcnRoID0gTC5leHRlbmQoe30sIEwuQ1JTLCB7XG5cdHdyYXBMbmc6IFstMTgwLCAxODBdLFxuXG5cdC8vIE1lYW4gRWFydGggUmFkaXVzLCBhcyByZWNvbW1lbmRlZCBmb3IgdXNlIGJ5XG5cdC8vIHRoZSBJbnRlcm5hdGlvbmFsIFVuaW9uIG9mIEdlb2Rlc3kgYW5kIEdlb3BoeXNpY3MsXG5cdC8vIHNlZSBodHRwOi8vcm9zZXR0YWNvZGUub3JnL3dpa2kvSGF2ZXJzaW5lX2Zvcm11bGFcblx0UjogNjM3MTAwMCxcblxuXHQvLyBkaXN0YW5jZSBiZXR3ZWVuIHR3byBnZW9ncmFwaGljYWwgcG9pbnRzIHVzaW5nIHNwaGVyaWNhbCBsYXcgb2YgY29zaW5lcyBhcHByb3hpbWF0aW9uXG5cdGRpc3RhbmNlOiBmdW5jdGlvbiAobGF0bG5nMSwgbGF0bG5nMikge1xuXHRcdHZhciByYWQgPSBNYXRoLlBJIC8gMTgwLFxuXHRcdCAgICBsYXQxID0gbGF0bG5nMS5sYXQgKiByYWQsXG5cdFx0ICAgIGxhdDIgPSBsYXRsbmcyLmxhdCAqIHJhZCxcblx0XHQgICAgYSA9IE1hdGguc2luKGxhdDEpICogTWF0aC5zaW4obGF0MikgK1xuXHRcdCAgICAgICAgTWF0aC5jb3MobGF0MSkgKiBNYXRoLmNvcyhsYXQyKSAqIE1hdGguY29zKChsYXRsbmcyLmxuZyAtIGxhdGxuZzEubG5nKSAqIHJhZCk7XG5cblx0XHRyZXR1cm4gdGhpcy5SICogTWF0aC5hY29zKE1hdGgubWluKGEsIDEpKTtcblx0fVxufSk7XG5cblxuXG4vKlxyXG4gKiBAbmFtZXNwYWNlIENSU1xyXG4gKiBAY3JzIEwuQ1JTLkVQU0czODU3XHJcbiAqXHJcbiAqIFRoZSBtb3N0IGNvbW1vbiBDUlMgZm9yIG9ubGluZSBtYXBzLCB1c2VkIGJ5IGFsbW9zdCBhbGwgZnJlZSBhbmQgY29tbWVyY2lhbFxyXG4gKiB0aWxlIHByb3ZpZGVycy4gVXNlcyBTcGhlcmljYWwgTWVyY2F0b3IgcHJvamVjdGlvbi4gU2V0IGluIGJ5IGRlZmF1bHQgaW5cclxuICogTWFwJ3MgYGNyc2Agb3B0aW9uLlxyXG4gKi9cclxuXHJcbkwuQ1JTLkVQU0czODU3ID0gTC5leHRlbmQoe30sIEwuQ1JTLkVhcnRoLCB7XHJcblx0Y29kZTogJ0VQU0c6Mzg1NycsXHJcblx0cHJvamVjdGlvbjogTC5Qcm9qZWN0aW9uLlNwaGVyaWNhbE1lcmNhdG9yLFxyXG5cclxuXHR0cmFuc2Zvcm1hdGlvbjogKGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBzY2FsZSA9IDAuNSAvIChNYXRoLlBJICogTC5Qcm9qZWN0aW9uLlNwaGVyaWNhbE1lcmNhdG9yLlIpO1xyXG5cdFx0cmV0dXJuIG5ldyBMLlRyYW5zZm9ybWF0aW9uKHNjYWxlLCAwLjUsIC1zY2FsZSwgMC41KTtcclxuXHR9KCkpXHJcbn0pO1xyXG5cclxuTC5DUlMuRVBTRzkwMDkxMyA9IEwuZXh0ZW5kKHt9LCBMLkNSUy5FUFNHMzg1Nywge1xyXG5cdGNvZGU6ICdFUFNHOjkwMDkxMydcclxufSk7XHJcblxuXG5cbi8qXHJcbiAqIEBuYW1lc3BhY2UgQ1JTXHJcbiAqIEBjcnMgTC5DUlMuRVBTRzQzMjZcclxuICpcclxuICogQSBjb21tb24gQ1JTIGFtb25nIEdJUyBlbnRodXNpYXN0cy4gVXNlcyBzaW1wbGUgRXF1aXJlY3Rhbmd1bGFyIHByb2plY3Rpb24uXHJcbiAqXHJcbiAqIExlYWZsZXQgMS4wLnggY29tcGxpZXMgd2l0aCB0aGUgW1RNUyBjb29yZGluYXRlIHNjaGVtZSBmb3IgRVBTRzo0MzI2XShodHRwczovL3dpa2kub3NnZW8ub3JnL3dpa2kvVGlsZV9NYXBfU2VydmljZV9TcGVjaWZpY2F0aW9uI2dsb2JhbC1nZW9kZXRpYyksXHJcbiAqIHdoaWNoIGlzIGEgYnJlYWtpbmcgY2hhbmdlIGZyb20gMC43LnggYmVoYXZpb3VyLiAgSWYgeW91IGFyZSB1c2luZyBhIGBUaWxlTGF5ZXJgXHJcbiAqIHdpdGggdGhpcyBDUlMsIGVuc3VyZSB0aGF0IHRoZXJlIGFyZSB0d28gMjU2eDI1NiBwaXhlbCB0aWxlcyBjb3ZlcmluZyB0aGVcclxuICogd2hvbGUgZWFydGggYXQgem9vbSBsZXZlbCB6ZXJvLCBhbmQgdGhhdCB0aGUgdGlsZSBjb29yZGluYXRlIG9yaWdpbiBpcyAoLTE4MCwrOTApLFxyXG4gKiBvciAoLTE4MCwtOTApIGZvciBgVGlsZUxheWVyYHMgd2l0aCBbdGhlIGB0bXNgIG9wdGlvbl0oI3RpbGVsYXllci10bXMpIHNldC5cclxuICovXHJcblxyXG5MLkNSUy5FUFNHNDMyNiA9IEwuZXh0ZW5kKHt9LCBMLkNSUy5FYXJ0aCwge1xyXG5cdGNvZGU6ICdFUFNHOjQzMjYnLFxyXG5cdHByb2plY3Rpb246IEwuUHJvamVjdGlvbi5Mb25MYXQsXHJcblx0dHJhbnNmb3JtYXRpb246IG5ldyBMLlRyYW5zZm9ybWF0aW9uKDEgLyAxODAsIDEsIC0xIC8gMTgwLCAwLjUpXHJcbn0pO1xyXG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgTWFwXHJcbiAqIEBha2EgTC5NYXBcclxuICogQGluaGVyaXRzIEV2ZW50ZWRcclxuICpcclxuICogVGhlIGNlbnRyYWwgY2xhc3Mgb2YgdGhlIEFQSSDigJQgaXQgaXMgdXNlZCB0byBjcmVhdGUgYSBtYXAgb24gYSBwYWdlIGFuZCBtYW5pcHVsYXRlIGl0LlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiAvLyBpbml0aWFsaXplIHRoZSBtYXAgb24gdGhlIFwibWFwXCIgZGl2IHdpdGggYSBnaXZlbiBjZW50ZXIgYW5kIHpvb21cclxuICogdmFyIG1hcCA9IEwubWFwKCdtYXAnLCB7XHJcbiAqIFx0Y2VudGVyOiBbNTEuNTA1LCAtMC4wOV0sXHJcbiAqIFx0em9vbTogMTNcclxuICogfSk7XHJcbiAqIGBgYFxyXG4gKlxyXG4gKi9cclxuXHJcbkwuTWFwID0gTC5FdmVudGVkLmV4dGVuZCh7XHJcblxyXG5cdG9wdGlvbnM6IHtcclxuXHRcdC8vIEBzZWN0aW9uIE1hcCBTdGF0ZSBPcHRpb25zXHJcblx0XHQvLyBAb3B0aW9uIGNyczogQ1JTID0gTC5DUlMuRVBTRzM4NTdcclxuXHRcdC8vIFRoZSBbQ29vcmRpbmF0ZSBSZWZlcmVuY2UgU3lzdGVtXSgjY3JzKSB0byB1c2UuIERvbid0IGNoYW5nZSB0aGlzIGlmIHlvdSdyZSBub3RcclxuXHRcdC8vIHN1cmUgd2hhdCBpdCBtZWFucy5cclxuXHRcdGNyczogTC5DUlMuRVBTRzM4NTcsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBjZW50ZXI6IExhdExuZyA9IHVuZGVmaW5lZFxyXG5cdFx0Ly8gSW5pdGlhbCBnZW9ncmFwaGljIGNlbnRlciBvZiB0aGUgbWFwXHJcblx0XHRjZW50ZXI6IHVuZGVmaW5lZCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHpvb206IE51bWJlciA9IHVuZGVmaW5lZFxyXG5cdFx0Ly8gSW5pdGlhbCBtYXAgem9vbSBsZXZlbFxyXG5cdFx0em9vbTogdW5kZWZpbmVkLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gbWluWm9vbTogTnVtYmVyID0gdW5kZWZpbmVkXHJcblx0XHQvLyBNaW5pbXVtIHpvb20gbGV2ZWwgb2YgdGhlIG1hcC4gT3ZlcnJpZGVzIGFueSBgbWluWm9vbWAgb3B0aW9uIHNldCBvbiBtYXAgbGF5ZXJzLlxyXG5cdFx0bWluWm9vbTogdW5kZWZpbmVkLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gbWF4Wm9vbTogTnVtYmVyID0gdW5kZWZpbmVkXHJcblx0XHQvLyBNYXhpbXVtIHpvb20gbGV2ZWwgb2YgdGhlIG1hcC4gT3ZlcnJpZGVzIGFueSBgbWF4Wm9vbWAgb3B0aW9uIHNldCBvbiBtYXAgbGF5ZXJzLlxyXG5cdFx0bWF4Wm9vbTogdW5kZWZpbmVkLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gbGF5ZXJzOiBMYXllcltdID0gW11cclxuXHRcdC8vIEFycmF5IG9mIGxheWVycyB0aGF0IHdpbGwgYmUgYWRkZWQgdG8gdGhlIG1hcCBpbml0aWFsbHlcclxuXHRcdGxheWVyczogW10sXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBtYXhCb3VuZHM6IExhdExuZ0JvdW5kcyA9IG51bGxcclxuXHRcdC8vIFdoZW4gdGhpcyBvcHRpb24gaXMgc2V0LCB0aGUgbWFwIHJlc3RyaWN0cyB0aGUgdmlldyB0byB0aGUgZ2l2ZW5cclxuXHRcdC8vIGdlb2dyYXBoaWNhbCBib3VuZHMsIGJvdW5jaW5nIHRoZSB1c2VyIGJhY2sgd2hlbiBoZSB0cmllcyB0byBwYW5cclxuXHRcdC8vIG91dHNpZGUgdGhlIHZpZXcuIFRvIHNldCB0aGUgcmVzdHJpY3Rpb24gZHluYW1pY2FsbHksIHVzZVxyXG5cdFx0Ly8gW2BzZXRNYXhCb3VuZHNgXSgjbWFwLXNldG1heGJvdW5kcykgbWV0aG9kLlxyXG5cdFx0bWF4Qm91bmRzOiB1bmRlZmluZWQsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiByZW5kZXJlcjogUmVuZGVyZXIgPSAqXHJcblx0XHQvLyBUaGUgZGVmYXVsdCBtZXRob2QgZm9yIGRyYXdpbmcgdmVjdG9yIGxheWVycyBvbiB0aGUgbWFwLiBgTC5TVkdgXHJcblx0XHQvLyBvciBgTC5DYW52YXNgIGJ5IGRlZmF1bHQgZGVwZW5kaW5nIG9uIGJyb3dzZXIgc3VwcG9ydC5cclxuXHRcdHJlbmRlcmVyOiB1bmRlZmluZWQsXHJcblxyXG5cclxuXHRcdC8vIEBzZWN0aW9uIEFuaW1hdGlvbiBPcHRpb25zXHJcblx0XHQvLyBAb3B0aW9uIHpvb21BbmltYXRpb246IEJvb2xlYW4gPSB0cnVlXHJcblx0XHQvLyBXaGV0aGVyIHRoZSBtYXAgem9vbSBhbmltYXRpb24gaXMgZW5hYmxlZC4gQnkgZGVmYXVsdCBpdCdzIGVuYWJsZWRcclxuXHRcdC8vIGluIGFsbCBicm93c2VycyB0aGF0IHN1cHBvcnQgQ1NTMyBUcmFuc2l0aW9ucyBleGNlcHQgQW5kcm9pZC5cclxuXHRcdHpvb21BbmltYXRpb246IHRydWUsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB6b29tQW5pbWF0aW9uVGhyZXNob2xkOiBOdW1iZXIgPSA0XHJcblx0XHQvLyBXb24ndCBhbmltYXRlIHpvb20gaWYgdGhlIHpvb20gZGlmZmVyZW5jZSBleGNlZWRzIHRoaXMgdmFsdWUuXHJcblx0XHR6b29tQW5pbWF0aW9uVGhyZXNob2xkOiA0LFxyXG5cclxuXHRcdC8vIEBvcHRpb24gZmFkZUFuaW1hdGlvbjogQm9vbGVhbiA9IHRydWVcclxuXHRcdC8vIFdoZXRoZXIgdGhlIHRpbGUgZmFkZSBhbmltYXRpb24gaXMgZW5hYmxlZC4gQnkgZGVmYXVsdCBpdCdzIGVuYWJsZWRcclxuXHRcdC8vIGluIGFsbCBicm93c2VycyB0aGF0IHN1cHBvcnQgQ1NTMyBUcmFuc2l0aW9ucyBleGNlcHQgQW5kcm9pZC5cclxuXHRcdGZhZGVBbmltYXRpb246IHRydWUsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBtYXJrZXJab29tQW5pbWF0aW9uOiBCb29sZWFuID0gdHJ1ZVxyXG5cdFx0Ly8gV2hldGhlciBtYXJrZXJzIGFuaW1hdGUgdGhlaXIgem9vbSB3aXRoIHRoZSB6b29tIGFuaW1hdGlvbiwgaWYgZGlzYWJsZWRcclxuXHRcdC8vIHRoZXkgd2lsbCBkaXNhcHBlYXIgZm9yIHRoZSBsZW5ndGggb2YgdGhlIGFuaW1hdGlvbi4gQnkgZGVmYXVsdCBpdCdzXHJcblx0XHQvLyBlbmFibGVkIGluIGFsbCBicm93c2VycyB0aGF0IHN1cHBvcnQgQ1NTMyBUcmFuc2l0aW9ucyBleGNlcHQgQW5kcm9pZC5cclxuXHRcdG1hcmtlclpvb21BbmltYXRpb246IHRydWUsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB0cmFuc2Zvcm0zRExpbWl0OiBOdW1iZXIgPSAyXjIzXHJcblx0XHQvLyBEZWZpbmVzIHRoZSBtYXhpbXVtIHNpemUgb2YgYSBDU1MgdHJhbnNsYXRpb24gdHJhbnNmb3JtLiBUaGUgZGVmYXVsdFxyXG5cdFx0Ly8gdmFsdWUgc2hvdWxkIG5vdCBiZSBjaGFuZ2VkIHVubGVzcyBhIHdlYiBicm93c2VyIHBvc2l0aW9ucyBsYXllcnMgaW5cclxuXHRcdC8vIHRoZSB3cm9uZyBwbGFjZSBhZnRlciBkb2luZyBhIGxhcmdlIGBwYW5CeWAuXHJcblx0XHR0cmFuc2Zvcm0zRExpbWl0OiA4Mzg4NjA4LCAvLyBQcmVjaXNpb24gbGltaXQgb2YgYSAzMi1iaXQgZmxvYXRcclxuXHJcblx0XHQvLyBAc2VjdGlvbiBJbnRlcmFjdGlvbiBPcHRpb25zXHJcblx0XHQvLyBAb3B0aW9uIHpvb21TbmFwOiBOdW1iZXIgPSAxXHJcblx0XHQvLyBGb3JjZXMgdGhlIG1hcCdzIHpvb20gbGV2ZWwgdG8gYWx3YXlzIGJlIGEgbXVsdGlwbGUgb2YgdGhpcywgcGFydGljdWxhcmx5XHJcblx0XHQvLyByaWdodCBhZnRlciBhIFtgZml0Qm91bmRzKClgXSgjbWFwLWZpdGJvdW5kcykgb3IgYSBwaW5jaC16b29tLlxyXG5cdFx0Ly8gQnkgZGVmYXVsdCwgdGhlIHpvb20gbGV2ZWwgc25hcHMgdG8gdGhlIG5lYXJlc3QgaW50ZWdlcjsgbG93ZXIgdmFsdWVzXHJcblx0XHQvLyAoZS5nLiBgMC41YCBvciBgMC4xYCkgYWxsb3cgZm9yIGdyZWF0ZXIgZ3JhbnVsYXJpdHkuIEEgdmFsdWUgb2YgYDBgXHJcblx0XHQvLyBtZWFucyB0aGUgem9vbSBsZXZlbCB3aWxsIG5vdCBiZSBzbmFwcGVkIGFmdGVyIGBmaXRCb3VuZHNgIG9yIGEgcGluY2gtem9vbS5cclxuXHRcdHpvb21TbmFwOiAxLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gem9vbURlbHRhOiBOdW1iZXIgPSAxXHJcblx0XHQvLyBDb250cm9scyBob3cgbXVjaCB0aGUgbWFwJ3Mgem9vbSBsZXZlbCB3aWxsIGNoYW5nZSBhZnRlciBhXHJcblx0XHQvLyBbYHpvb21JbigpYF0oI21hcC16b29taW4pLCBbYHpvb21PdXQoKWBdKCNtYXAtem9vbW91dCksIHByZXNzaW5nIGArYFxyXG5cdFx0Ly8gb3IgYC1gIG9uIHRoZSBrZXlib2FyZCwgb3IgdXNpbmcgdGhlIFt6b29tIGNvbnRyb2xzXSgjY29udHJvbC16b29tKS5cclxuXHRcdC8vIFZhbHVlcyBzbWFsbGVyIHRoYW4gYDFgIChlLmcuIGAwLjVgKSBhbGxvdyBmb3IgZ3JlYXRlciBncmFudWxhcml0eS5cclxuXHRcdHpvb21EZWx0YTogMSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHRyYWNrUmVzaXplOiBCb29sZWFuID0gdHJ1ZVxyXG5cdFx0Ly8gV2hldGhlciB0aGUgbWFwIGF1dG9tYXRpY2FsbHkgaGFuZGxlcyBicm93c2VyIHdpbmRvdyByZXNpemUgdG8gdXBkYXRlIGl0c2VsZi5cclxuXHRcdHRyYWNrUmVzaXplOiB0cnVlXHJcblx0fSxcclxuXHJcblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKGlkLCBvcHRpb25zKSB7IC8vIChIVE1MRWxlbWVudCBvciBTdHJpbmcsIE9iamVjdClcclxuXHRcdG9wdGlvbnMgPSBMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XHJcblxyXG5cdFx0dGhpcy5faW5pdENvbnRhaW5lcihpZCk7XHJcblx0XHR0aGlzLl9pbml0TGF5b3V0KCk7XHJcblxyXG5cdFx0Ly8gaGFjayBmb3IgaHR0cHM6Ly9naXRodWIuY29tL0xlYWZsZXQvTGVhZmxldC9pc3N1ZXMvMTk4MFxyXG5cdFx0dGhpcy5fb25SZXNpemUgPSBMLmJpbmQodGhpcy5fb25SZXNpemUsIHRoaXMpO1xyXG5cclxuXHRcdHRoaXMuX2luaXRFdmVudHMoKTtcclxuXHJcblx0XHRpZiAob3B0aW9ucy5tYXhCb3VuZHMpIHtcclxuXHRcdFx0dGhpcy5zZXRNYXhCb3VuZHMob3B0aW9ucy5tYXhCb3VuZHMpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmIChvcHRpb25zLnpvb20gIT09IHVuZGVmaW5lZCkge1xyXG5cdFx0XHR0aGlzLl96b29tID0gdGhpcy5fbGltaXRab29tKG9wdGlvbnMuem9vbSk7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKG9wdGlvbnMuY2VudGVyICYmIG9wdGlvbnMuem9vbSAhPT0gdW5kZWZpbmVkKSB7XHJcblx0XHRcdHRoaXMuc2V0VmlldyhMLmxhdExuZyhvcHRpb25zLmNlbnRlciksIG9wdGlvbnMuem9vbSwge3Jlc2V0OiB0cnVlfSk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5faGFuZGxlcnMgPSBbXTtcclxuXHRcdHRoaXMuX2xheWVycyA9IHt9O1xyXG5cdFx0dGhpcy5fem9vbUJvdW5kTGF5ZXJzID0ge307XHJcblx0XHR0aGlzLl9zaXplQ2hhbmdlZCA9IHRydWU7XHJcblxyXG5cdFx0dGhpcy5jYWxsSW5pdEhvb2tzKCk7XHJcblxyXG5cdFx0Ly8gZG9uJ3QgYW5pbWF0ZSBvbiBicm93c2VycyB3aXRob3V0IGhhcmR3YXJlLWFjY2VsZXJhdGVkIHRyYW5zaXRpb25zIG9yIG9sZCBBbmRyb2lkL09wZXJhXHJcblx0XHR0aGlzLl96b29tQW5pbWF0ZWQgPSBMLkRvbVV0aWwuVFJBTlNJVElPTiAmJiBMLkJyb3dzZXIuYW55M2QgJiYgIUwuQnJvd3Nlci5tb2JpbGVPcGVyYSAmJlxyXG5cdFx0XHRcdHRoaXMub3B0aW9ucy56b29tQW5pbWF0aW9uO1xyXG5cclxuXHRcdC8vIHpvb20gdHJhbnNpdGlvbnMgcnVuIHdpdGggdGhlIHNhbWUgZHVyYXRpb24gZm9yIGFsbCBsYXllcnMsIHNvIGlmIG9uZSBvZiB0cmFuc2l0aW9uZW5kIGV2ZW50c1xyXG5cdFx0Ly8gaGFwcGVucyBhZnRlciBzdGFydGluZyB6b29tIGFuaW1hdGlvbiAocHJvcGFnYXRpbmcgdG8gdGhlIG1hcCBwYW5lKSwgd2Uga25vdyB0aGF0IGl0IGVuZGVkIGdsb2JhbGx5XHJcblx0XHRpZiAodGhpcy5fem9vbUFuaW1hdGVkKSB7XHJcblx0XHRcdHRoaXMuX2NyZWF0ZUFuaW1Qcm94eSgpO1xyXG5cdFx0XHRMLkRvbUV2ZW50Lm9uKHRoaXMuX3Byb3h5LCBMLkRvbVV0aWwuVFJBTlNJVElPTl9FTkQsIHRoaXMuX2NhdGNoVHJhbnNpdGlvbkVuZCwgdGhpcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fYWRkTGF5ZXJzKHRoaXMub3B0aW9ucy5sYXllcnMpO1xyXG5cdH0sXHJcblxyXG5cclxuXHQvLyBAc2VjdGlvbiBNZXRob2RzIGZvciBtb2RpZnlpbmcgbWFwIHN0YXRlXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0VmlldyhjZW50ZXI6IExhdExuZywgem9vbTogTnVtYmVyLCBvcHRpb25zPzogWm9vbS9wYW4gb3B0aW9ucyk6IHRoaXNcclxuXHQvLyBTZXRzIHRoZSB2aWV3IG9mIHRoZSBtYXAgKGdlb2dyYXBoaWNhbCBjZW50ZXIgYW5kIHpvb20pIHdpdGggdGhlIGdpdmVuXHJcblx0Ly8gYW5pbWF0aW9uIG9wdGlvbnMuXHJcblx0c2V0VmlldzogZnVuY3Rpb24gKGNlbnRlciwgem9vbSwgb3B0aW9ucykge1xyXG5cclxuXHRcdHpvb20gPSB6b29tID09PSB1bmRlZmluZWQgPyB0aGlzLl96b29tIDogdGhpcy5fbGltaXRab29tKHpvb20pO1xyXG5cdFx0Y2VudGVyID0gdGhpcy5fbGltaXRDZW50ZXIoTC5sYXRMbmcoY2VudGVyKSwgem9vbSwgdGhpcy5vcHRpb25zLm1heEJvdW5kcyk7XHJcblx0XHRvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcclxuXHJcblx0XHR0aGlzLl9zdG9wKCk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2xvYWRlZCAmJiAhb3B0aW9ucy5yZXNldCAmJiBvcHRpb25zICE9PSB0cnVlKSB7XHJcblxyXG5cdFx0XHRpZiAob3B0aW9ucy5hbmltYXRlICE9PSB1bmRlZmluZWQpIHtcclxuXHRcdFx0XHRvcHRpb25zLnpvb20gPSBMLmV4dGVuZCh7YW5pbWF0ZTogb3B0aW9ucy5hbmltYXRlfSwgb3B0aW9ucy56b29tKTtcclxuXHRcdFx0XHRvcHRpb25zLnBhbiA9IEwuZXh0ZW5kKHthbmltYXRlOiBvcHRpb25zLmFuaW1hdGUsIGR1cmF0aW9uOiBvcHRpb25zLmR1cmF0aW9ufSwgb3B0aW9ucy5wYW4pO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHQvLyB0cnkgYW5pbWF0aW5nIHBhbiBvciB6b29tXHJcblx0XHRcdHZhciBtb3ZlZCA9ICh0aGlzLl96b29tICE9PSB6b29tKSA/XHJcblx0XHRcdFx0dGhpcy5fdHJ5QW5pbWF0ZWRab29tICYmIHRoaXMuX3RyeUFuaW1hdGVkWm9vbShjZW50ZXIsIHpvb20sIG9wdGlvbnMuem9vbSkgOlxyXG5cdFx0XHRcdHRoaXMuX3RyeUFuaW1hdGVkUGFuKGNlbnRlciwgb3B0aW9ucy5wYW4pO1xyXG5cclxuXHRcdFx0aWYgKG1vdmVkKSB7XHJcblx0XHRcdFx0Ly8gcHJldmVudCByZXNpemUgaGFuZGxlciBjYWxsLCB0aGUgdmlldyB3aWxsIHJlZnJlc2ggYWZ0ZXIgYW5pbWF0aW9uIGFueXdheVxyXG5cdFx0XHRcdGNsZWFyVGltZW91dCh0aGlzLl9zaXplVGltZXIpO1xyXG5cdFx0XHRcdHJldHVybiB0aGlzO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gYW5pbWF0aW9uIGRpZG4ndCBzdGFydCwganVzdCByZXNldCB0aGUgbWFwIHZpZXdcclxuXHRcdHRoaXMuX3Jlc2V0VmlldyhjZW50ZXIsIHpvb20pO1xyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0Wm9vbSh6b29tOiBOdW1iZXIsIG9wdGlvbnM6IFpvb20vcGFuIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gU2V0cyB0aGUgem9vbSBvZiB0aGUgbWFwLlxyXG5cdHNldFpvb206IGZ1bmN0aW9uICh6b29tLCBvcHRpb25zKSB7XHJcblx0XHRpZiAoIXRoaXMuX2xvYWRlZCkge1xyXG5cdFx0XHR0aGlzLl96b29tID0gem9vbTtcclxuXHRcdFx0cmV0dXJuIHRoaXM7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcy5zZXRWaWV3KHRoaXMuZ2V0Q2VudGVyKCksIHpvb20sIHt6b29tOiBvcHRpb25zfSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB6b29tSW4oZGVsdGE/OiBOdW1iZXIsIG9wdGlvbnM/OiBab29tIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gSW5jcmVhc2VzIHRoZSB6b29tIG9mIHRoZSBtYXAgYnkgYGRlbHRhYCAoW2B6b29tRGVsdGFgXSgjbWFwLXpvb21kZWx0YSkgYnkgZGVmYXVsdCkuXHJcblx0em9vbUluOiBmdW5jdGlvbiAoZGVsdGEsIG9wdGlvbnMpIHtcclxuXHRcdGRlbHRhID0gZGVsdGEgfHwgKEwuQnJvd3Nlci5hbnkzZCA/IHRoaXMub3B0aW9ucy56b29tRGVsdGEgOiAxKTtcclxuXHRcdHJldHVybiB0aGlzLnNldFpvb20odGhpcy5fem9vbSArIGRlbHRhLCBvcHRpb25zKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHpvb21PdXQoZGVsdGE/OiBOdW1iZXIsIG9wdGlvbnM/OiBab29tIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gRGVjcmVhc2VzIHRoZSB6b29tIG9mIHRoZSBtYXAgYnkgYGRlbHRhYCAoW2B6b29tRGVsdGFgXSgjbWFwLXpvb21kZWx0YSkgYnkgZGVmYXVsdCkuXHJcblx0em9vbU91dDogZnVuY3Rpb24gKGRlbHRhLCBvcHRpb25zKSB7XHJcblx0XHRkZWx0YSA9IGRlbHRhIHx8IChMLkJyb3dzZXIuYW55M2QgPyB0aGlzLm9wdGlvbnMuem9vbURlbHRhIDogMSk7XHJcblx0XHRyZXR1cm4gdGhpcy5zZXRab29tKHRoaXMuX3pvb20gLSBkZWx0YSwgb3B0aW9ucyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzZXRab29tQXJvdW5kKGxhdGxuZzogTGF0TG5nLCB6b29tOiBOdW1iZXIsIG9wdGlvbnM6IFpvb20gb3B0aW9ucyk6IHRoaXNcclxuXHQvLyBab29tcyB0aGUgbWFwIHdoaWxlIGtlZXBpbmcgYSBzcGVjaWZpZWQgZ2VvZ3JhcGhpY2FsIHBvaW50IG9uIHRoZSBtYXBcclxuXHQvLyBzdGF0aW9uYXJ5IChlLmcuIHVzZWQgaW50ZXJuYWxseSBmb3Igc2Nyb2xsIHpvb20gYW5kIGRvdWJsZS1jbGljayB6b29tKS5cclxuXHQvLyBAYWx0ZXJuYXRpdmVcclxuXHQvLyBAbWV0aG9kIHNldFpvb21Bcm91bmQob2Zmc2V0OiBQb2ludCwgem9vbTogTnVtYmVyLCBvcHRpb25zOiBab29tIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gWm9vbXMgdGhlIG1hcCB3aGlsZSBrZWVwaW5nIGEgc3BlY2lmaWVkIHBpeGVsIG9uIHRoZSBtYXAgKHJlbGF0aXZlIHRvIHRoZSB0b3AtbGVmdCBjb3JuZXIpIHN0YXRpb25hcnkuXHJcblx0c2V0Wm9vbUFyb3VuZDogZnVuY3Rpb24gKGxhdGxuZywgem9vbSwgb3B0aW9ucykge1xyXG5cdFx0dmFyIHNjYWxlID0gdGhpcy5nZXRab29tU2NhbGUoem9vbSksXHJcblx0XHQgICAgdmlld0hhbGYgPSB0aGlzLmdldFNpemUoKS5kaXZpZGVCeSgyKSxcclxuXHRcdCAgICBjb250YWluZXJQb2ludCA9IGxhdGxuZyBpbnN0YW5jZW9mIEwuUG9pbnQgPyBsYXRsbmcgOiB0aGlzLmxhdExuZ1RvQ29udGFpbmVyUG9pbnQobGF0bG5nKSxcclxuXHJcblx0XHQgICAgY2VudGVyT2Zmc2V0ID0gY29udGFpbmVyUG9pbnQuc3VidHJhY3Qodmlld0hhbGYpLm11bHRpcGx5QnkoMSAtIDEgLyBzY2FsZSksXHJcblx0XHQgICAgbmV3Q2VudGVyID0gdGhpcy5jb250YWluZXJQb2ludFRvTGF0TG5nKHZpZXdIYWxmLmFkZChjZW50ZXJPZmZzZXQpKTtcclxuXHJcblx0XHRyZXR1cm4gdGhpcy5zZXRWaWV3KG5ld0NlbnRlciwgem9vbSwge3pvb206IG9wdGlvbnN9KTtcclxuXHR9LFxyXG5cclxuXHRfZ2V0Qm91bmRzQ2VudGVyWm9vbTogZnVuY3Rpb24gKGJvdW5kcywgb3B0aW9ucykge1xyXG5cclxuXHRcdG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xyXG5cdFx0Ym91bmRzID0gYm91bmRzLmdldEJvdW5kcyA/IGJvdW5kcy5nZXRCb3VuZHMoKSA6IEwubGF0TG5nQm91bmRzKGJvdW5kcyk7XHJcblxyXG5cdFx0dmFyIHBhZGRpbmdUTCA9IEwucG9pbnQob3B0aW9ucy5wYWRkaW5nVG9wTGVmdCB8fCBvcHRpb25zLnBhZGRpbmcgfHwgWzAsIDBdKSxcclxuXHRcdCAgICBwYWRkaW5nQlIgPSBMLnBvaW50KG9wdGlvbnMucGFkZGluZ0JvdHRvbVJpZ2h0IHx8IG9wdGlvbnMucGFkZGluZyB8fCBbMCwgMF0pLFxyXG5cclxuXHRcdCAgICB6b29tID0gdGhpcy5nZXRCb3VuZHNab29tKGJvdW5kcywgZmFsc2UsIHBhZGRpbmdUTC5hZGQocGFkZGluZ0JSKSk7XHJcblxyXG5cdFx0em9vbSA9ICh0eXBlb2Ygb3B0aW9ucy5tYXhab29tID09PSAnbnVtYmVyJykgPyBNYXRoLm1pbihvcHRpb25zLm1heFpvb20sIHpvb20pIDogem9vbTtcclxuXHJcblx0XHR2YXIgcGFkZGluZ09mZnNldCA9IHBhZGRpbmdCUi5zdWJ0cmFjdChwYWRkaW5nVEwpLmRpdmlkZUJ5KDIpLFxyXG5cclxuXHRcdCAgICBzd1BvaW50ID0gdGhpcy5wcm9qZWN0KGJvdW5kcy5nZXRTb3V0aFdlc3QoKSwgem9vbSksXHJcblx0XHQgICAgbmVQb2ludCA9IHRoaXMucHJvamVjdChib3VuZHMuZ2V0Tm9ydGhFYXN0KCksIHpvb20pLFxyXG5cdFx0ICAgIGNlbnRlciA9IHRoaXMudW5wcm9qZWN0KHN3UG9pbnQuYWRkKG5lUG9pbnQpLmRpdmlkZUJ5KDIpLmFkZChwYWRkaW5nT2Zmc2V0KSwgem9vbSk7XHJcblxyXG5cdFx0cmV0dXJuIHtcclxuXHRcdFx0Y2VudGVyOiBjZW50ZXIsXHJcblx0XHRcdHpvb206IHpvb21cclxuXHRcdH07XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBmaXRCb3VuZHMoYm91bmRzOiBMYXRMbmdCb3VuZHMsIG9wdGlvbnM6IGZpdEJvdW5kcyBvcHRpb25zKTogdGhpc1xyXG5cdC8vIFNldHMgYSBtYXAgdmlldyB0aGF0IGNvbnRhaW5zIHRoZSBnaXZlbiBnZW9ncmFwaGljYWwgYm91bmRzIHdpdGggdGhlXHJcblx0Ly8gbWF4aW11bSB6b29tIGxldmVsIHBvc3NpYmxlLlxyXG5cdGZpdEJvdW5kczogZnVuY3Rpb24gKGJvdW5kcywgb3B0aW9ucykge1xyXG5cclxuXHRcdGJvdW5kcyA9IEwubGF0TG5nQm91bmRzKGJvdW5kcyk7XHJcblxyXG5cdFx0aWYgKCFib3VuZHMuaXNWYWxpZCgpKSB7XHJcblx0XHRcdHRocm93IG5ldyBFcnJvcignQm91bmRzIGFyZSBub3QgdmFsaWQuJyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dmFyIHRhcmdldCA9IHRoaXMuX2dldEJvdW5kc0NlbnRlclpvb20oYm91bmRzLCBvcHRpb25zKTtcclxuXHRcdHJldHVybiB0aGlzLnNldFZpZXcodGFyZ2V0LmNlbnRlciwgdGFyZ2V0Lnpvb20sIG9wdGlvbnMpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZml0V29ybGQob3B0aW9ucz86IGZpdEJvdW5kcyBvcHRpb25zKTogdGhpc1xyXG5cdC8vIFNldHMgYSBtYXAgdmlldyB0aGF0IG1vc3RseSBjb250YWlucyB0aGUgd2hvbGUgd29ybGQgd2l0aCB0aGUgbWF4aW11bVxyXG5cdC8vIHpvb20gbGV2ZWwgcG9zc2libGUuXHJcblx0Zml0V29ybGQ6IGZ1bmN0aW9uIChvcHRpb25zKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5maXRCb3VuZHMoW1stOTAsIC0xODBdLCBbOTAsIDE4MF1dLCBvcHRpb25zKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHBhblRvKGxhdGxuZzogTGF0TG5nLCBvcHRpb25zPzogUGFuIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gUGFucyB0aGUgbWFwIHRvIGEgZ2l2ZW4gY2VudGVyLlxyXG5cdHBhblRvOiBmdW5jdGlvbiAoY2VudGVyLCBvcHRpb25zKSB7IC8vIChMYXRMbmcpXHJcblx0XHRyZXR1cm4gdGhpcy5zZXRWaWV3KGNlbnRlciwgdGhpcy5fem9vbSwge3Bhbjogb3B0aW9uc30pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgcGFuQnkob2Zmc2V0OiBQb2ludCk6IHRoaXNcclxuXHQvLyBQYW5zIHRoZSBtYXAgYnkgYSBnaXZlbiBudW1iZXIgb2YgcGl4ZWxzIChhbmltYXRlZCkuXHJcblx0cGFuQnk6IGZ1bmN0aW9uIChvZmZzZXQsIG9wdGlvbnMpIHtcclxuXHRcdG9mZnNldCA9IEwucG9pbnQob2Zmc2V0KS5yb3VuZCgpO1xyXG5cdFx0b3B0aW9ucyA9IG9wdGlvbnMgfHwge307XHJcblxyXG5cdFx0aWYgKCFvZmZzZXQueCAmJiAhb2Zmc2V0LnkpIHtcclxuXHRcdFx0cmV0dXJuIHRoaXMuZmlyZSgnbW92ZWVuZCcpO1xyXG5cdFx0fVxyXG5cdFx0Ly8gSWYgd2UgcGFuIHRvbyBmYXIsIENocm9tZSBnZXRzIGlzc3VlcyB3aXRoIHRpbGVzXHJcblx0XHQvLyBhbmQgbWFrZXMgdGhlbSBkaXNhcHBlYXIgb3IgYXBwZWFyIGluIHRoZSB3cm9uZyBwbGFjZSAoc2xpZ2h0bHkgb2Zmc2V0KSAjMjYwMlxyXG5cdFx0aWYgKG9wdGlvbnMuYW5pbWF0ZSAhPT0gdHJ1ZSAmJiAhdGhpcy5nZXRTaXplKCkuY29udGFpbnMob2Zmc2V0KSkge1xyXG5cdFx0XHR0aGlzLl9yZXNldFZpZXcodGhpcy51bnByb2plY3QodGhpcy5wcm9qZWN0KHRoaXMuZ2V0Q2VudGVyKCkpLmFkZChvZmZzZXQpKSwgdGhpcy5nZXRab29tKCkpO1xyXG5cdFx0XHRyZXR1cm4gdGhpcztcclxuXHRcdH1cclxuXHJcblx0XHRpZiAoIXRoaXMuX3BhbkFuaW0pIHtcclxuXHRcdFx0dGhpcy5fcGFuQW5pbSA9IG5ldyBMLlBvc0FuaW1hdGlvbigpO1xyXG5cclxuXHRcdFx0dGhpcy5fcGFuQW5pbS5vbih7XHJcblx0XHRcdFx0J3N0ZXAnOiB0aGlzLl9vblBhblRyYW5zaXRpb25TdGVwLFxyXG5cdFx0XHRcdCdlbmQnOiB0aGlzLl9vblBhblRyYW5zaXRpb25FbmRcclxuXHRcdFx0fSwgdGhpcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gZG9uJ3QgZmlyZSBtb3Zlc3RhcnQgaWYgYW5pbWF0aW5nIGluZXJ0aWFcclxuXHRcdGlmICghb3B0aW9ucy5ub01vdmVTdGFydCkge1xyXG5cdFx0XHR0aGlzLmZpcmUoJ21vdmVzdGFydCcpO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8vIGFuaW1hdGUgcGFuIHVubGVzcyBhbmltYXRlOiBmYWxzZSBzcGVjaWZpZWRcclxuXHRcdGlmIChvcHRpb25zLmFuaW1hdGUgIT09IGZhbHNlKSB7XHJcblx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9tYXBQYW5lLCAnbGVhZmxldC1wYW4tYW5pbScpO1xyXG5cclxuXHRcdFx0dmFyIG5ld1BvcyA9IHRoaXMuX2dldE1hcFBhbmVQb3MoKS5zdWJ0cmFjdChvZmZzZXQpLnJvdW5kKCk7XHJcblx0XHRcdHRoaXMuX3BhbkFuaW0ucnVuKHRoaXMuX21hcFBhbmUsIG5ld1Bvcywgb3B0aW9ucy5kdXJhdGlvbiB8fCAwLjI1LCBvcHRpb25zLmVhc2VMaW5lYXJpdHkpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0dGhpcy5fcmF3UGFuQnkob2Zmc2V0KTtcclxuXHRcdFx0dGhpcy5maXJlKCdtb3ZlJykuZmlyZSgnbW92ZWVuZCcpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZmx5VG8obGF0bG5nOiBMYXRMbmcsIHpvb20/OiBOdW1iZXIsIG9wdGlvbnM/OiBab29tL3BhbiBvcHRpb25zKTogdGhpc1xyXG5cdC8vIFNldHMgdGhlIHZpZXcgb2YgdGhlIG1hcCAoZ2VvZ3JhcGhpY2FsIGNlbnRlciBhbmQgem9vbSkgcGVyZm9ybWluZyBhIHNtb290aFxyXG5cdC8vIHBhbi16b29tIGFuaW1hdGlvbi5cclxuXHRmbHlUbzogZnVuY3Rpb24gKHRhcmdldENlbnRlciwgdGFyZ2V0Wm9vbSwgb3B0aW9ucykge1xyXG5cclxuXHRcdG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xyXG5cdFx0aWYgKG9wdGlvbnMuYW5pbWF0ZSA9PT0gZmFsc2UgfHwgIUwuQnJvd3Nlci5hbnkzZCkge1xyXG5cdFx0XHRyZXR1cm4gdGhpcy5zZXRWaWV3KHRhcmdldENlbnRlciwgdGFyZ2V0Wm9vbSwgb3B0aW9ucyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fc3RvcCgpO1xyXG5cclxuXHRcdHZhciBmcm9tID0gdGhpcy5wcm9qZWN0KHRoaXMuZ2V0Q2VudGVyKCkpLFxyXG5cdFx0ICAgIHRvID0gdGhpcy5wcm9qZWN0KHRhcmdldENlbnRlciksXHJcblx0XHQgICAgc2l6ZSA9IHRoaXMuZ2V0U2l6ZSgpLFxyXG5cdFx0ICAgIHN0YXJ0Wm9vbSA9IHRoaXMuX3pvb207XHJcblxyXG5cdFx0dGFyZ2V0Q2VudGVyID0gTC5sYXRMbmcodGFyZ2V0Q2VudGVyKTtcclxuXHRcdHRhcmdldFpvb20gPSB0YXJnZXRab29tID09PSB1bmRlZmluZWQgPyBzdGFydFpvb20gOiB0YXJnZXRab29tO1xyXG5cclxuXHRcdHZhciB3MCA9IE1hdGgubWF4KHNpemUueCwgc2l6ZS55KSxcclxuXHRcdCAgICB3MSA9IHcwICogdGhpcy5nZXRab29tU2NhbGUoc3RhcnRab29tLCB0YXJnZXRab29tKSxcclxuXHRcdCAgICB1MSA9ICh0by5kaXN0YW5jZVRvKGZyb20pKSB8fCAxLFxyXG5cdFx0ICAgIHJobyA9IDEuNDIsXHJcblx0XHQgICAgcmhvMiA9IHJobyAqIHJobztcclxuXHJcblx0XHRmdW5jdGlvbiByKGkpIHtcclxuXHRcdFx0dmFyIHMxID0gaSA/IC0xIDogMSxcclxuXHRcdFx0ICAgIHMyID0gaSA/IHcxIDogdzAsXHJcblx0XHRcdCAgICB0MSA9IHcxICogdzEgLSB3MCAqIHcwICsgczEgKiByaG8yICogcmhvMiAqIHUxICogdTEsXHJcblx0XHRcdCAgICBiMSA9IDIgKiBzMiAqIHJobzIgKiB1MSxcclxuXHRcdFx0ICAgIGIgPSB0MSAvIGIxLFxyXG5cdFx0XHQgICAgc3EgPSBNYXRoLnNxcnQoYiAqIGIgKyAxKSAtIGI7XHJcblxyXG5cdFx0XHQgICAgLy8gd29ya2Fyb3VuZCBmb3IgZmxvYXRpbmcgcG9pbnQgcHJlY2lzaW9uIGJ1ZyB3aGVuIHNxID0gMCwgbG9nID0gLUluZmluaXRlLFxyXG5cdFx0XHQgICAgLy8gdGh1cyB0cmlnZ2VyaW5nIGFuIGluZmluaXRlIGxvb3AgaW4gZmx5VG9cclxuXHRcdFx0ICAgIHZhciBsb2cgPSBzcSA8IDAuMDAwMDAwMDAxID8gLTE4IDogTWF0aC5sb2coc3EpO1xyXG5cclxuXHRcdFx0cmV0dXJuIGxvZztcclxuXHRcdH1cclxuXHJcblx0XHRmdW5jdGlvbiBzaW5oKG4pIHsgcmV0dXJuIChNYXRoLmV4cChuKSAtIE1hdGguZXhwKC1uKSkgLyAyOyB9XHJcblx0XHRmdW5jdGlvbiBjb3NoKG4pIHsgcmV0dXJuIChNYXRoLmV4cChuKSArIE1hdGguZXhwKC1uKSkgLyAyOyB9XHJcblx0XHRmdW5jdGlvbiB0YW5oKG4pIHsgcmV0dXJuIHNpbmgobikgLyBjb3NoKG4pOyB9XHJcblxyXG5cdFx0dmFyIHIwID0gcigwKTtcclxuXHJcblx0XHRmdW5jdGlvbiB3KHMpIHsgcmV0dXJuIHcwICogKGNvc2gocjApIC8gY29zaChyMCArIHJobyAqIHMpKTsgfVxyXG5cdFx0ZnVuY3Rpb24gdShzKSB7IHJldHVybiB3MCAqIChjb3NoKHIwKSAqIHRhbmgocjAgKyByaG8gKiBzKSAtIHNpbmgocjApKSAvIHJobzI7IH1cclxuXHJcblx0XHRmdW5jdGlvbiBlYXNlT3V0KHQpIHsgcmV0dXJuIDEgLSBNYXRoLnBvdygxIC0gdCwgMS41KTsgfVxyXG5cclxuXHRcdHZhciBzdGFydCA9IERhdGUubm93KCksXHJcblx0XHQgICAgUyA9IChyKDEpIC0gcjApIC8gcmhvLFxyXG5cdFx0ICAgIGR1cmF0aW9uID0gb3B0aW9ucy5kdXJhdGlvbiA/IDEwMDAgKiBvcHRpb25zLmR1cmF0aW9uIDogMTAwMCAqIFMgKiAwLjg7XHJcblxyXG5cdFx0ZnVuY3Rpb24gZnJhbWUoKSB7XHJcblx0XHRcdHZhciB0ID0gKERhdGUubm93KCkgLSBzdGFydCkgLyBkdXJhdGlvbixcclxuXHRcdFx0ICAgIHMgPSBlYXNlT3V0KHQpICogUztcclxuXHJcblx0XHRcdGlmICh0IDw9IDEpIHtcclxuXHRcdFx0XHR0aGlzLl9mbHlUb0ZyYW1lID0gTC5VdGlsLnJlcXVlc3RBbmltRnJhbWUoZnJhbWUsIHRoaXMpO1xyXG5cclxuXHRcdFx0XHR0aGlzLl9tb3ZlKFxyXG5cdFx0XHRcdFx0dGhpcy51bnByb2plY3QoZnJvbS5hZGQodG8uc3VidHJhY3QoZnJvbSkubXVsdGlwbHlCeSh1KHMpIC8gdTEpKSwgc3RhcnRab29tKSxcclxuXHRcdFx0XHRcdHRoaXMuZ2V0U2NhbGVab29tKHcwIC8gdyhzKSwgc3RhcnRab29tKSxcclxuXHRcdFx0XHRcdHtmbHlUbzogdHJ1ZX0pO1xyXG5cclxuXHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHR0aGlzXHJcblx0XHRcdFx0XHQuX21vdmUodGFyZ2V0Q2VudGVyLCB0YXJnZXRab29tKVxyXG5cdFx0XHRcdFx0Ll9tb3ZlRW5kKHRydWUpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fbW92ZVN0YXJ0KHRydWUpO1xyXG5cclxuXHRcdGZyYW1lLmNhbGwodGhpcyk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGZseVRvQm91bmRzKGJvdW5kczogTGF0TG5nQm91bmRzLCBvcHRpb25zPzogZml0Qm91bmRzIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gU2V0cyB0aGUgdmlldyBvZiB0aGUgbWFwIHdpdGggYSBzbW9vdGggYW5pbWF0aW9uIGxpa2UgW2BmbHlUb2BdKCNtYXAtZmx5dG8pLFxyXG5cdC8vIGJ1dCB0YWtlcyBhIGJvdW5kcyBwYXJhbWV0ZXIgbGlrZSBbYGZpdEJvdW5kc2BdKCNtYXAtZml0Ym91bmRzKS5cclxuXHRmbHlUb0JvdW5kczogZnVuY3Rpb24gKGJvdW5kcywgb3B0aW9ucykge1xyXG5cdFx0dmFyIHRhcmdldCA9IHRoaXMuX2dldEJvdW5kc0NlbnRlclpvb20oYm91bmRzLCBvcHRpb25zKTtcclxuXHRcdHJldHVybiB0aGlzLmZseVRvKHRhcmdldC5jZW50ZXIsIHRhcmdldC56b29tLCBvcHRpb25zKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldE1heEJvdW5kcyhib3VuZHM6IEJvdW5kcyk6IHRoaXNcclxuXHQvLyBSZXN0cmljdHMgdGhlIG1hcCB2aWV3IHRvIHRoZSBnaXZlbiBib3VuZHMgKHNlZSB0aGUgW21heEJvdW5kc10oI21hcC1tYXhib3VuZHMpIG9wdGlvbikuXHJcblx0c2V0TWF4Qm91bmRzOiBmdW5jdGlvbiAoYm91bmRzKSB7XHJcblx0XHRib3VuZHMgPSBMLmxhdExuZ0JvdW5kcyhib3VuZHMpO1xyXG5cclxuXHRcdGlmICghYm91bmRzLmlzVmFsaWQoKSkge1xyXG5cdFx0XHR0aGlzLm9wdGlvbnMubWF4Qm91bmRzID0gbnVsbDtcclxuXHRcdFx0cmV0dXJuIHRoaXMub2ZmKCdtb3ZlZW5kJywgdGhpcy5fcGFuSW5zaWRlTWF4Qm91bmRzKTtcclxuXHRcdH0gZWxzZSBpZiAodGhpcy5vcHRpb25zLm1heEJvdW5kcykge1xyXG5cdFx0XHR0aGlzLm9mZignbW92ZWVuZCcsIHRoaXMuX3Bhbkluc2lkZU1heEJvdW5kcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5vcHRpb25zLm1heEJvdW5kcyA9IGJvdW5kcztcclxuXHJcblx0XHRpZiAodGhpcy5fbG9hZGVkKSB7XHJcblx0XHRcdHRoaXMuX3Bhbkluc2lkZU1heEJvdW5kcygpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzLm9uKCdtb3ZlZW5kJywgdGhpcy5fcGFuSW5zaWRlTWF4Qm91bmRzKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldE1pblpvb20oem9vbTogTnVtYmVyKTogdGhpc1xyXG5cdC8vIFNldHMgdGhlIGxvd2VyIGxpbWl0IGZvciB0aGUgYXZhaWxhYmxlIHpvb20gbGV2ZWxzIChzZWUgdGhlIFttaW5ab29tXSgjbWFwLW1pbnpvb20pIG9wdGlvbikuXHJcblx0c2V0TWluWm9vbTogZnVuY3Rpb24gKHpvb20pIHtcclxuXHRcdHRoaXMub3B0aW9ucy5taW5ab29tID0gem9vbTtcclxuXHJcblx0XHRpZiAodGhpcy5fbG9hZGVkICYmIHRoaXMuZ2V0Wm9vbSgpIDwgdGhpcy5vcHRpb25zLm1pblpvb20pIHtcclxuXHRcdFx0cmV0dXJuIHRoaXMuc2V0Wm9vbSh6b29tKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldE1heFpvb20oem9vbTogTnVtYmVyKTogdGhpc1xyXG5cdC8vIFNldHMgdGhlIHVwcGVyIGxpbWl0IGZvciB0aGUgYXZhaWxhYmxlIHpvb20gbGV2ZWxzIChzZWUgdGhlIFttYXhab29tXSgjbWFwLW1heHpvb20pIG9wdGlvbikuXHJcblx0c2V0TWF4Wm9vbTogZnVuY3Rpb24gKHpvb20pIHtcclxuXHRcdHRoaXMub3B0aW9ucy5tYXhab29tID0gem9vbTtcclxuXHJcblx0XHRpZiAodGhpcy5fbG9hZGVkICYmICh0aGlzLmdldFpvb20oKSA+IHRoaXMub3B0aW9ucy5tYXhab29tKSkge1xyXG5cdFx0XHRyZXR1cm4gdGhpcy5zZXRab29tKHpvb20pO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgcGFuSW5zaWRlQm91bmRzKGJvdW5kczogTGF0TG5nQm91bmRzLCBvcHRpb25zPzogUGFuIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gUGFucyB0aGUgbWFwIHRvIHRoZSBjbG9zZXN0IHZpZXcgdGhhdCB3b3VsZCBsaWUgaW5zaWRlIHRoZSBnaXZlbiBib3VuZHMgKGlmIGl0J3Mgbm90IGFscmVhZHkpLCBjb250cm9sbGluZyB0aGUgYW5pbWF0aW9uIHVzaW5nIHRoZSBvcHRpb25zIHNwZWNpZmljLCBpZiBhbnkuXHJcblx0cGFuSW5zaWRlQm91bmRzOiBmdW5jdGlvbiAoYm91bmRzLCBvcHRpb25zKSB7XHJcblx0XHR0aGlzLl9lbmZvcmNpbmdCb3VuZHMgPSB0cnVlO1xyXG5cdFx0dmFyIGNlbnRlciA9IHRoaXMuZ2V0Q2VudGVyKCksXHJcblx0XHQgICAgbmV3Q2VudGVyID0gdGhpcy5fbGltaXRDZW50ZXIoY2VudGVyLCB0aGlzLl96b29tLCBMLmxhdExuZ0JvdW5kcyhib3VuZHMpKTtcclxuXHJcblx0XHRpZiAoIWNlbnRlci5lcXVhbHMobmV3Q2VudGVyKSkge1xyXG5cdFx0XHR0aGlzLnBhblRvKG5ld0NlbnRlciwgb3B0aW9ucyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fZW5mb3JjaW5nQm91bmRzID0gZmFsc2U7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGludmFsaWRhdGVTaXplKG9wdGlvbnM6IFpvb20vUGFuIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gQ2hlY2tzIGlmIHRoZSBtYXAgY29udGFpbmVyIHNpemUgY2hhbmdlZCBhbmQgdXBkYXRlcyB0aGUgbWFwIGlmIHNvIOKAlFxyXG5cdC8vIGNhbGwgaXQgYWZ0ZXIgeW91J3ZlIGNoYW5nZWQgdGhlIG1hcCBzaXplIGR5bmFtaWNhbGx5LCBhbHNvIGFuaW1hdGluZ1xyXG5cdC8vIHBhbiBieSBkZWZhdWx0LiBJZiBgb3B0aW9ucy5wYW5gIGlzIGBmYWxzZWAsIHBhbm5pbmcgd2lsbCBub3Qgb2NjdXIuXHJcblx0Ly8gSWYgYG9wdGlvbnMuZGVib3VuY2VNb3ZlZW5kYCBpcyBgdHJ1ZWAsIGl0IHdpbGwgZGVsYXkgYG1vdmVlbmRgIGV2ZW50IHNvXHJcblx0Ly8gdGhhdCBpdCBkb2Vzbid0IGhhcHBlbiBvZnRlbiBldmVuIGlmIHRoZSBtZXRob2QgaXMgY2FsbGVkIG1hbnlcclxuXHQvLyB0aW1lcyBpbiBhIHJvdy5cclxuXHJcblx0Ly8gQGFsdGVybmF0aXZlXHJcblx0Ly8gQG1ldGhvZCBpbnZhbGlkYXRlU2l6ZShhbmltYXRlOiBCb29sZWFuKTogdGhpc1xyXG5cdC8vIENoZWNrcyBpZiB0aGUgbWFwIGNvbnRhaW5lciBzaXplIGNoYW5nZWQgYW5kIHVwZGF0ZXMgdGhlIG1hcCBpZiBzbyDigJRcclxuXHQvLyBjYWxsIGl0IGFmdGVyIHlvdSd2ZSBjaGFuZ2VkIHRoZSBtYXAgc2l6ZSBkeW5hbWljYWxseSwgYWxzbyBhbmltYXRpbmdcclxuXHQvLyBwYW4gYnkgZGVmYXVsdC5cclxuXHRpbnZhbGlkYXRlU2l6ZTogZnVuY3Rpb24gKG9wdGlvbnMpIHtcclxuXHRcdGlmICghdGhpcy5fbG9hZGVkKSB7IHJldHVybiB0aGlzOyB9XHJcblxyXG5cdFx0b3B0aW9ucyA9IEwuZXh0ZW5kKHtcclxuXHRcdFx0YW5pbWF0ZTogZmFsc2UsXHJcblx0XHRcdHBhbjogdHJ1ZVxyXG5cdFx0fSwgb3B0aW9ucyA9PT0gdHJ1ZSA/IHthbmltYXRlOiB0cnVlfSA6IG9wdGlvbnMpO1xyXG5cclxuXHRcdHZhciBvbGRTaXplID0gdGhpcy5nZXRTaXplKCk7XHJcblx0XHR0aGlzLl9zaXplQ2hhbmdlZCA9IHRydWU7XHJcblx0XHR0aGlzLl9sYXN0Q2VudGVyID0gbnVsbDtcclxuXHJcblx0XHR2YXIgbmV3U2l6ZSA9IHRoaXMuZ2V0U2l6ZSgpLFxyXG5cdFx0ICAgIG9sZENlbnRlciA9IG9sZFNpemUuZGl2aWRlQnkoMikucm91bmQoKSxcclxuXHRcdCAgICBuZXdDZW50ZXIgPSBuZXdTaXplLmRpdmlkZUJ5KDIpLnJvdW5kKCksXHJcblx0XHQgICAgb2Zmc2V0ID0gb2xkQ2VudGVyLnN1YnRyYWN0KG5ld0NlbnRlcik7XHJcblxyXG5cdFx0aWYgKCFvZmZzZXQueCAmJiAhb2Zmc2V0LnkpIHsgcmV0dXJuIHRoaXM7IH1cclxuXHJcblx0XHRpZiAob3B0aW9ucy5hbmltYXRlICYmIG9wdGlvbnMucGFuKSB7XHJcblx0XHRcdHRoaXMucGFuQnkob2Zmc2V0KTtcclxuXHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRpZiAob3B0aW9ucy5wYW4pIHtcclxuXHRcdFx0XHR0aGlzLl9yYXdQYW5CeShvZmZzZXQpO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHR0aGlzLmZpcmUoJ21vdmUnKTtcclxuXHJcblx0XHRcdGlmIChvcHRpb25zLmRlYm91bmNlTW92ZWVuZCkge1xyXG5cdFx0XHRcdGNsZWFyVGltZW91dCh0aGlzLl9zaXplVGltZXIpO1xyXG5cdFx0XHRcdHRoaXMuX3NpemVUaW1lciA9IHNldFRpbWVvdXQoTC5iaW5kKHRoaXMuZmlyZSwgdGhpcywgJ21vdmVlbmQnKSwgMjAwKTtcclxuXHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHR0aGlzLmZpcmUoJ21vdmVlbmQnKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdC8vIEBzZWN0aW9uIE1hcCBzdGF0ZSBjaGFuZ2UgZXZlbnRzXHJcblx0XHQvLyBAZXZlbnQgcmVzaXplOiBSZXNpemVFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgbWFwIGlzIHJlc2l6ZWQuXHJcblx0XHRyZXR1cm4gdGhpcy5maXJlKCdyZXNpemUnLCB7XHJcblx0XHRcdG9sZFNpemU6IG9sZFNpemUsXHJcblx0XHRcdG5ld1NpemU6IG5ld1NpemVcclxuXHRcdH0pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBzZWN0aW9uIE1ldGhvZHMgZm9yIG1vZGlmeWluZyBtYXAgc3RhdGVcclxuXHQvLyBAbWV0aG9kIHN0b3AoKTogdGhpc1xyXG5cdC8vIFN0b3BzIHRoZSBjdXJyZW50bHkgcnVubmluZyBgcGFuVG9gIG9yIGBmbHlUb2AgYW5pbWF0aW9uLCBpZiBhbnkuXHJcblx0c3RvcDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dGhpcy5zZXRab29tKHRoaXMuX2xpbWl0Wm9vbSh0aGlzLl96b29tKSk7XHJcblx0XHRpZiAoIXRoaXMub3B0aW9ucy56b29tU25hcCkge1xyXG5cdFx0XHR0aGlzLmZpcmUoJ3ZpZXdyZXNldCcpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXMuX3N0b3AoKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAc2VjdGlvbiBHZW9sb2NhdGlvbiBtZXRob2RzXHJcblx0Ly8gQG1ldGhvZCBsb2NhdGUob3B0aW9ucz86IExvY2F0ZSBvcHRpb25zKTogdGhpc1xyXG5cdC8vIFRyaWVzIHRvIGxvY2F0ZSB0aGUgdXNlciB1c2luZyB0aGUgR2VvbG9jYXRpb24gQVBJLCBmaXJpbmcgYSBbYGxvY2F0aW9uZm91bmRgXSgjbWFwLWxvY2F0aW9uZm91bmQpXHJcblx0Ly8gZXZlbnQgd2l0aCBsb2NhdGlvbiBkYXRhIG9uIHN1Y2Nlc3Mgb3IgYSBbYGxvY2F0aW9uZXJyb3JgXSgjbWFwLWxvY2F0aW9uZXJyb3IpIGV2ZW50IG9uIGZhaWx1cmUsXHJcblx0Ly8gYW5kIG9wdGlvbmFsbHkgc2V0cyB0aGUgbWFwIHZpZXcgdG8gdGhlIHVzZXIncyBsb2NhdGlvbiB3aXRoIHJlc3BlY3QgdG9cclxuXHQvLyBkZXRlY3Rpb24gYWNjdXJhY3kgKG9yIHRvIHRoZSB3b3JsZCB2aWV3IGlmIGdlb2xvY2F0aW9uIGZhaWxlZCkuXHJcblx0Ly8gTm90ZSB0aGF0LCBpZiB5b3VyIHBhZ2UgZG9lc24ndCB1c2UgSFRUUFMsIHRoaXMgbWV0aG9kIHdpbGwgZmFpbCBpblxyXG5cdC8vIG1vZGVybiBicm93c2VycyAoW0Nocm9tZSA1MCBhbmQgbmV3ZXJdKGh0dHBzOi8vc2l0ZXMuZ29vZ2xlLmNvbS9hL2Nocm9taXVtLm9yZy9kZXYvSG9tZS9jaHJvbWl1bS1zZWN1cml0eS9kZXByZWNhdGluZy1wb3dlcmZ1bC1mZWF0dXJlcy1vbi1pbnNlY3VyZS1vcmlnaW5zKSlcclxuXHQvLyBTZWUgYExvY2F0ZSBvcHRpb25zYCBmb3IgbW9yZSBkZXRhaWxzLlxyXG5cdGxvY2F0ZTogZnVuY3Rpb24gKG9wdGlvbnMpIHtcclxuXHJcblx0XHRvcHRpb25zID0gdGhpcy5fbG9jYXRlT3B0aW9ucyA9IEwuZXh0ZW5kKHtcclxuXHRcdFx0dGltZW91dDogMTAwMDAsXHJcblx0XHRcdHdhdGNoOiBmYWxzZVxyXG5cdFx0XHQvLyBzZXRWaWV3OiBmYWxzZVxyXG5cdFx0XHQvLyBtYXhab29tOiA8TnVtYmVyPlxyXG5cdFx0XHQvLyBtYXhpbXVtQWdlOiAwXHJcblx0XHRcdC8vIGVuYWJsZUhpZ2hBY2N1cmFjeTogZmFsc2VcclxuXHRcdH0sIG9wdGlvbnMpO1xyXG5cclxuXHRcdGlmICghKCdnZW9sb2NhdGlvbicgaW4gbmF2aWdhdG9yKSkge1xyXG5cdFx0XHR0aGlzLl9oYW5kbGVHZW9sb2NhdGlvbkVycm9yKHtcclxuXHRcdFx0XHRjb2RlOiAwLFxyXG5cdFx0XHRcdG1lc3NhZ2U6ICdHZW9sb2NhdGlvbiBub3Qgc3VwcG9ydGVkLidcclxuXHRcdFx0fSk7XHJcblx0XHRcdHJldHVybiB0aGlzO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBvblJlc3BvbnNlID0gTC5iaW5kKHRoaXMuX2hhbmRsZUdlb2xvY2F0aW9uUmVzcG9uc2UsIHRoaXMpLFxyXG5cdFx0ICAgIG9uRXJyb3IgPSBMLmJpbmQodGhpcy5faGFuZGxlR2VvbG9jYXRpb25FcnJvciwgdGhpcyk7XHJcblxyXG5cdFx0aWYgKG9wdGlvbnMud2F0Y2gpIHtcclxuXHRcdFx0dGhpcy5fbG9jYXRpb25XYXRjaElkID1cclxuXHRcdFx0ICAgICAgICBuYXZpZ2F0b3IuZ2VvbG9jYXRpb24ud2F0Y2hQb3NpdGlvbihvblJlc3BvbnNlLCBvbkVycm9yLCBvcHRpb25zKTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdG5hdmlnYXRvci5nZW9sb2NhdGlvbi5nZXRDdXJyZW50UG9zaXRpb24ob25SZXNwb25zZSwgb25FcnJvciwgb3B0aW9ucyk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHN0b3BMb2NhdGUoKTogdGhpc1xyXG5cdC8vIFN0b3BzIHdhdGNoaW5nIGxvY2F0aW9uIHByZXZpb3VzbHkgaW5pdGlhdGVkIGJ5IGBtYXAubG9jYXRlKHt3YXRjaDogdHJ1ZX0pYFxyXG5cdC8vIGFuZCBhYm9ydHMgcmVzZXR0aW5nIHRoZSBtYXAgdmlldyBpZiBtYXAubG9jYXRlIHdhcyBjYWxsZWQgd2l0aFxyXG5cdC8vIGB7c2V0VmlldzogdHJ1ZX1gLlxyXG5cdHN0b3BMb2NhdGU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmIChuYXZpZ2F0b3IuZ2VvbG9jYXRpb24gJiYgbmF2aWdhdG9yLmdlb2xvY2F0aW9uLmNsZWFyV2F0Y2gpIHtcclxuXHRcdFx0bmF2aWdhdG9yLmdlb2xvY2F0aW9uLmNsZWFyV2F0Y2godGhpcy5fbG9jYXRpb25XYXRjaElkKTtcclxuXHRcdH1cclxuXHRcdGlmICh0aGlzLl9sb2NhdGVPcHRpb25zKSB7XHJcblx0XHRcdHRoaXMuX2xvY2F0ZU9wdGlvbnMuc2V0VmlldyA9IGZhbHNlO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0X2hhbmRsZUdlb2xvY2F0aW9uRXJyb3I6IGZ1bmN0aW9uIChlcnJvcikge1xyXG5cdFx0dmFyIGMgPSBlcnJvci5jb2RlLFxyXG5cdFx0ICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlIHx8XHJcblx0XHQgICAgICAgICAgICAoYyA9PT0gMSA/ICdwZXJtaXNzaW9uIGRlbmllZCcgOlxyXG5cdFx0ICAgICAgICAgICAgKGMgPT09IDIgPyAncG9zaXRpb24gdW5hdmFpbGFibGUnIDogJ3RpbWVvdXQnKSk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2xvY2F0ZU9wdGlvbnMuc2V0VmlldyAmJiAhdGhpcy5fbG9hZGVkKSB7XHJcblx0XHRcdHRoaXMuZml0V29ybGQoKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBAc2VjdGlvbiBMb2NhdGlvbiBldmVudHNcclxuXHRcdC8vIEBldmVudCBsb2NhdGlvbmVycm9yOiBFcnJvckV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIGdlb2xvY2F0aW9uICh1c2luZyB0aGUgW2Bsb2NhdGVgXSgjbWFwLWxvY2F0ZSkgbWV0aG9kKSBmYWlsZWQuXHJcblx0XHR0aGlzLmZpcmUoJ2xvY2F0aW9uZXJyb3InLCB7XHJcblx0XHRcdGNvZGU6IGMsXHJcblx0XHRcdG1lc3NhZ2U6ICdHZW9sb2NhdGlvbiBlcnJvcjogJyArIG1lc3NhZ2UgKyAnLidcclxuXHRcdH0pO1xyXG5cdH0sXHJcblxyXG5cdF9oYW5kbGVHZW9sb2NhdGlvblJlc3BvbnNlOiBmdW5jdGlvbiAocG9zKSB7XHJcblx0XHR2YXIgbGF0ID0gcG9zLmNvb3Jkcy5sYXRpdHVkZSxcclxuXHRcdCAgICBsbmcgPSBwb3MuY29vcmRzLmxvbmdpdHVkZSxcclxuXHRcdCAgICBsYXRsbmcgPSBuZXcgTC5MYXRMbmcobGF0LCBsbmcpLFxyXG5cdFx0ICAgIGJvdW5kcyA9IGxhdGxuZy50b0JvdW5kcyhwb3MuY29vcmRzLmFjY3VyYWN5KSxcclxuXHRcdCAgICBvcHRpb25zID0gdGhpcy5fbG9jYXRlT3B0aW9ucztcclxuXHJcblx0XHRpZiAob3B0aW9ucy5zZXRWaWV3KSB7XHJcblx0XHRcdHZhciB6b29tID0gdGhpcy5nZXRCb3VuZHNab29tKGJvdW5kcyk7XHJcblx0XHRcdHRoaXMuc2V0VmlldyhsYXRsbmcsIG9wdGlvbnMubWF4Wm9vbSA/IE1hdGgubWluKHpvb20sIG9wdGlvbnMubWF4Wm9vbSkgOiB6b29tKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgZGF0YSA9IHtcclxuXHRcdFx0bGF0bG5nOiBsYXRsbmcsXHJcblx0XHRcdGJvdW5kczogYm91bmRzLFxyXG5cdFx0XHR0aW1lc3RhbXA6IHBvcy50aW1lc3RhbXBcclxuXHRcdH07XHJcblxyXG5cdFx0Zm9yICh2YXIgaSBpbiBwb3MuY29vcmRzKSB7XHJcblx0XHRcdGlmICh0eXBlb2YgcG9zLmNvb3Jkc1tpXSA9PT0gJ251bWJlcicpIHtcclxuXHRcdFx0XHRkYXRhW2ldID0gcG9zLmNvb3Jkc1tpXTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdC8vIEBldmVudCBsb2NhdGlvbmZvdW5kOiBMb2NhdGlvbkV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIGdlb2xvY2F0aW9uICh1c2luZyB0aGUgW2Bsb2NhdGVgXSgjbWFwLWxvY2F0ZSkgbWV0aG9kKVxyXG5cdFx0Ly8gd2VudCBzdWNjZXNzZnVsbHkuXHJcblx0XHR0aGlzLmZpcmUoJ2xvY2F0aW9uZm91bmQnLCBkYXRhKTtcclxuXHR9LFxyXG5cclxuXHQvLyBUT0RPIGhhbmRsZXIuYWRkVG9cclxuXHQvLyBUT0RPIEFwcHJvcGlhdGUgZG9jcyBzZWN0aW9uP1xyXG5cdC8vIEBzZWN0aW9uIE90aGVyIE1ldGhvZHNcclxuXHQvLyBAbWV0aG9kIGFkZEhhbmRsZXIobmFtZTogU3RyaW5nLCBIYW5kbGVyQ2xhc3M6IEZ1bmN0aW9uKTogdGhpc1xyXG5cdC8vIEFkZHMgYSBuZXcgYEhhbmRsZXJgIHRvIHRoZSBtYXAsIGdpdmVuIGl0cyBuYW1lIGFuZCBjb25zdHJ1Y3RvciBmdW5jdGlvbi5cclxuXHRhZGRIYW5kbGVyOiBmdW5jdGlvbiAobmFtZSwgSGFuZGxlckNsYXNzKSB7XHJcblx0XHRpZiAoIUhhbmRsZXJDbGFzcykgeyByZXR1cm4gdGhpczsgfVxyXG5cclxuXHRcdHZhciBoYW5kbGVyID0gdGhpc1tuYW1lXSA9IG5ldyBIYW5kbGVyQ2xhc3ModGhpcyk7XHJcblxyXG5cdFx0dGhpcy5faGFuZGxlcnMucHVzaChoYW5kbGVyKTtcclxuXHJcblx0XHRpZiAodGhpcy5vcHRpb25zW25hbWVdKSB7XHJcblx0XHRcdGhhbmRsZXIuZW5hYmxlKCk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCByZW1vdmUoKTogdGhpc1xyXG5cdC8vIERlc3Ryb3lzIHRoZSBtYXAgYW5kIGNsZWFycyBhbGwgcmVsYXRlZCBldmVudCBsaXN0ZW5lcnMuXHJcblx0cmVtb3ZlOiBmdW5jdGlvbiAoKSB7XHJcblxyXG5cdFx0dGhpcy5faW5pdEV2ZW50cyh0cnVlKTtcclxuXHJcblx0XHRpZiAodGhpcy5fY29udGFpbmVySWQgIT09IHRoaXMuX2NvbnRhaW5lci5fbGVhZmxldF9pZCkge1xyXG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoJ01hcCBjb250YWluZXIgaXMgYmVpbmcgcmV1c2VkIGJ5IGFub3RoZXIgaW5zdGFuY2UnKTtcclxuXHRcdH1cclxuXHJcblx0XHR0cnkge1xyXG5cdFx0XHQvLyB0aHJvd3MgZXJyb3IgaW4gSUU2LThcclxuXHRcdFx0ZGVsZXRlIHRoaXMuX2NvbnRhaW5lci5fbGVhZmxldF9pZDtcclxuXHRcdFx0ZGVsZXRlIHRoaXMuX2NvbnRhaW5lcklkO1xyXG5cdFx0fSBjYXRjaCAoZSkge1xyXG5cdFx0XHQvKmVzbGludC1kaXNhYmxlICovXHJcblx0XHRcdHRoaXMuX2NvbnRhaW5lci5fbGVhZmxldF9pZCA9IHVuZGVmaW5lZDtcclxuXHRcdFx0Lyplc2xpbnQtZW5hYmxlICovXHJcblx0XHRcdHRoaXMuX2NvbnRhaW5lcklkID0gdW5kZWZpbmVkO1xyXG5cdFx0fVxyXG5cclxuXHRcdEwuRG9tVXRpbC5yZW1vdmUodGhpcy5fbWFwUGFuZSk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2NsZWFyQ29udHJvbFBvcykge1xyXG5cdFx0XHR0aGlzLl9jbGVhckNvbnRyb2xQb3MoKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9jbGVhckhhbmRsZXJzKCk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2xvYWRlZCkge1xyXG5cdFx0XHQvLyBAc2VjdGlvbiBNYXAgc3RhdGUgY2hhbmdlIGV2ZW50c1xyXG5cdFx0XHQvLyBAZXZlbnQgdW5sb2FkOiBFdmVudFxyXG5cdFx0XHQvLyBGaXJlZCB3aGVuIHRoZSBtYXAgaXMgZGVzdHJveWVkIHdpdGggW3JlbW92ZV0oI21hcC1yZW1vdmUpIG1ldGhvZC5cclxuXHRcdFx0dGhpcy5maXJlKCd1bmxvYWQnKTtcclxuXHRcdH1cclxuXHJcblx0XHRmb3IgKHZhciBpIGluIHRoaXMuX2xheWVycykge1xyXG5cdFx0XHR0aGlzLl9sYXllcnNbaV0ucmVtb3ZlKCk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQHNlY3Rpb24gT3RoZXIgTWV0aG9kc1xyXG5cdC8vIEBtZXRob2QgY3JlYXRlUGFuZShuYW1lOiBTdHJpbmcsIGNvbnRhaW5lcj86IEhUTUxFbGVtZW50KTogSFRNTEVsZW1lbnRcclxuXHQvLyBDcmVhdGVzIGEgbmV3IFttYXAgcGFuZV0oI21hcC1wYW5lKSB3aXRoIHRoZSBnaXZlbiBuYW1lIGlmIGl0IGRvZXNuJ3QgZXhpc3QgYWxyZWFkeSxcclxuXHQvLyB0aGVuIHJldHVybnMgaXQuIFRoZSBwYW5lIGlzIGNyZWF0ZWQgYXMgYSBjaGlsZHJlbiBvZiBgY29udGFpbmVyYCwgb3JcclxuXHQvLyBhcyBhIGNoaWxkcmVuIG9mIHRoZSBtYWluIG1hcCBwYW5lIGlmIG5vdCBzZXQuXHJcblx0Y3JlYXRlUGFuZTogZnVuY3Rpb24gKG5hbWUsIGNvbnRhaW5lcikge1xyXG5cdFx0dmFyIGNsYXNzTmFtZSA9ICdsZWFmbGV0LXBhbmUnICsgKG5hbWUgPyAnIGxlYWZsZXQtJyArIG5hbWUucmVwbGFjZSgnUGFuZScsICcnKSArICctcGFuZScgOiAnJyksXHJcblx0XHQgICAgcGFuZSA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsIGNsYXNzTmFtZSwgY29udGFpbmVyIHx8IHRoaXMuX21hcFBhbmUpO1xyXG5cclxuXHRcdGlmIChuYW1lKSB7XHJcblx0XHRcdHRoaXMuX3BhbmVzW25hbWVdID0gcGFuZTtcclxuXHRcdH1cclxuXHRcdHJldHVybiBwYW5lO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBzZWN0aW9uIE1ldGhvZHMgZm9yIEdldHRpbmcgTWFwIFN0YXRlXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Q2VudGVyKCk6IExhdExuZ1xyXG5cdC8vIFJldHVybnMgdGhlIGdlb2dyYXBoaWNhbCBjZW50ZXIgb2YgdGhlIG1hcCB2aWV3XHJcblx0Z2V0Q2VudGVyOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLl9jaGVja0lmTG9hZGVkKCk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2xhc3RDZW50ZXIgJiYgIXRoaXMuX21vdmVkKCkpIHtcclxuXHRcdFx0cmV0dXJuIHRoaXMuX2xhc3RDZW50ZXI7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcy5sYXllclBvaW50VG9MYXRMbmcodGhpcy5fZ2V0Q2VudGVyTGF5ZXJQb2ludCgpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldFpvb20oKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgY3VycmVudCB6b29tIGxldmVsIG9mIHRoZSBtYXAgdmlld1xyXG5cdGdldFpvb206IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl96b29tO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Qm91bmRzKCk6IExhdExuZ0JvdW5kc1xyXG5cdC8vIFJldHVybnMgdGhlIGdlb2dyYXBoaWNhbCBib3VuZHMgdmlzaWJsZSBpbiB0aGUgY3VycmVudCBtYXAgdmlld1xyXG5cdGdldEJvdW5kczogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGJvdW5kcyA9IHRoaXMuZ2V0UGl4ZWxCb3VuZHMoKSxcclxuXHRcdCAgICBzdyA9IHRoaXMudW5wcm9qZWN0KGJvdW5kcy5nZXRCb3R0b21MZWZ0KCkpLFxyXG5cdFx0ICAgIG5lID0gdGhpcy51bnByb2plY3QoYm91bmRzLmdldFRvcFJpZ2h0KCkpO1xyXG5cclxuXHRcdHJldHVybiBuZXcgTC5MYXRMbmdCb3VuZHMoc3csIG5lKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldE1pblpvb20oKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgbWluaW11bSB6b29tIGxldmVsIG9mIHRoZSBtYXAgKGlmIHNldCBpbiB0aGUgYG1pblpvb21gIG9wdGlvbiBvZiB0aGUgbWFwIG9yIG9mIGFueSBsYXllcnMpLCBvciBgMGAgYnkgZGVmYXVsdC5cclxuXHRnZXRNaW5ab29tOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5vcHRpb25zLm1pblpvb20gPT09IHVuZGVmaW5lZCA/IHRoaXMuX2xheWVyc01pblpvb20gfHwgMCA6IHRoaXMub3B0aW9ucy5taW5ab29tO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0TWF4Wm9vbSgpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBtYXhpbXVtIHpvb20gbGV2ZWwgb2YgdGhlIG1hcCAoaWYgc2V0IGluIHRoZSBgbWF4Wm9vbWAgb3B0aW9uIG9mIHRoZSBtYXAgb3Igb2YgYW55IGxheWVycykuXHJcblx0Z2V0TWF4Wm9vbTogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5tYXhab29tID09PSB1bmRlZmluZWQgP1xyXG5cdFx0XHQodGhpcy5fbGF5ZXJzTWF4Wm9vbSA9PT0gdW5kZWZpbmVkID8gSW5maW5pdHkgOiB0aGlzLl9sYXllcnNNYXhab29tKSA6XHJcblx0XHRcdHRoaXMub3B0aW9ucy5tYXhab29tO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Qm91bmRzWm9vbShib3VuZHM6IExhdExuZ0JvdW5kcywgaW5zaWRlPzogQm9vbGVhbik6IE51bWJlclxyXG5cdC8vIFJldHVybnMgdGhlIG1heGltdW0gem9vbSBsZXZlbCBvbiB3aGljaCB0aGUgZ2l2ZW4gYm91bmRzIGZpdCB0byB0aGUgbWFwXHJcblx0Ly8gdmlldyBpbiBpdHMgZW50aXJldHkuIElmIGBpbnNpZGVgIChvcHRpb25hbCkgaXMgc2V0IHRvIGB0cnVlYCwgdGhlIG1ldGhvZFxyXG5cdC8vIGluc3RlYWQgcmV0dXJucyB0aGUgbWluaW11bSB6b29tIGxldmVsIG9uIHdoaWNoIHRoZSBtYXAgdmlldyBmaXRzIGludG9cclxuXHQvLyB0aGUgZ2l2ZW4gYm91bmRzIGluIGl0cyBlbnRpcmV0eS5cclxuXHRnZXRCb3VuZHNab29tOiBmdW5jdGlvbiAoYm91bmRzLCBpbnNpZGUsIHBhZGRpbmcpIHsgLy8gKExhdExuZ0JvdW5kc1ssIEJvb2xlYW4sIFBvaW50XSkgLT4gTnVtYmVyXHJcblx0XHRib3VuZHMgPSBMLmxhdExuZ0JvdW5kcyhib3VuZHMpO1xyXG5cdFx0cGFkZGluZyA9IEwucG9pbnQocGFkZGluZyB8fCBbMCwgMF0pO1xyXG5cclxuXHRcdHZhciB6b29tID0gdGhpcy5nZXRab29tKCkgfHwgMCxcclxuXHRcdCAgICBtaW4gPSB0aGlzLmdldE1pblpvb20oKSxcclxuXHRcdCAgICBtYXggPSB0aGlzLmdldE1heFpvb20oKSxcclxuXHRcdCAgICBudyA9IGJvdW5kcy5nZXROb3J0aFdlc3QoKSxcclxuXHRcdCAgICBzZSA9IGJvdW5kcy5nZXRTb3V0aEVhc3QoKSxcclxuXHRcdCAgICBzaXplID0gdGhpcy5nZXRTaXplKCkuc3VidHJhY3QocGFkZGluZyksXHJcblx0XHQgICAgYm91bmRzU2l6ZSA9IHRoaXMucHJvamVjdChzZSwgem9vbSkuc3VidHJhY3QodGhpcy5wcm9qZWN0KG53LCB6b29tKSksXHJcblx0XHQgICAgc25hcCA9IEwuQnJvd3Nlci5hbnkzZCA/IHRoaXMub3B0aW9ucy56b29tU25hcCA6IDE7XHJcblxyXG5cdFx0dmFyIHNjYWxlID0gTWF0aC5taW4oc2l6ZS54IC8gYm91bmRzU2l6ZS54LCBzaXplLnkgLyBib3VuZHNTaXplLnkpO1xyXG5cdFx0em9vbSA9IHRoaXMuZ2V0U2NhbGVab29tKHNjYWxlLCB6b29tKTtcclxuXHJcblx0XHRpZiAoc25hcCkge1xyXG5cdFx0XHR6b29tID0gTWF0aC5yb3VuZCh6b29tIC8gKHNuYXAgLyAxMDApKSAqIChzbmFwIC8gMTAwKTsgLy8gZG9uJ3QganVtcCBpZiB3aXRoaW4gMSUgb2YgYSBzbmFwIGxldmVsXHJcblx0XHRcdHpvb20gPSBpbnNpZGUgPyBNYXRoLmNlaWwoem9vbSAvIHNuYXApICogc25hcCA6IE1hdGguZmxvb3Ioem9vbSAvIHNuYXApICogc25hcDtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gTWF0aC5tYXgobWluLCBNYXRoLm1pbihtYXgsIHpvb20pKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldFNpemUoKTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIHRoZSBjdXJyZW50IHNpemUgb2YgdGhlIG1hcCBjb250YWluZXIgKGluIHBpeGVscykuXHJcblx0Z2V0U2l6ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKCF0aGlzLl9zaXplIHx8IHRoaXMuX3NpemVDaGFuZ2VkKSB7XHJcblx0XHRcdHRoaXMuX3NpemUgPSBuZXcgTC5Qb2ludChcclxuXHRcdFx0XHR0aGlzLl9jb250YWluZXIuY2xpZW50V2lkdGgsXHJcblx0XHRcdFx0dGhpcy5fY29udGFpbmVyLmNsaWVudEhlaWdodCk7XHJcblxyXG5cdFx0XHR0aGlzLl9zaXplQ2hhbmdlZCA9IGZhbHNlO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXMuX3NpemUuY2xvbmUoKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldFBpeGVsQm91bmRzKCk6IEJvdW5kc1xyXG5cdC8vIFJldHVybnMgdGhlIGJvdW5kcyBvZiB0aGUgY3VycmVudCBtYXAgdmlldyBpbiBwcm9qZWN0ZWQgcGl4ZWxcclxuXHQvLyBjb29yZGluYXRlcyAoc29tZXRpbWVzIHVzZWZ1bCBpbiBsYXllciBhbmQgb3ZlcmxheSBpbXBsZW1lbnRhdGlvbnMpLlxyXG5cdGdldFBpeGVsQm91bmRzOiBmdW5jdGlvbiAoY2VudGVyLCB6b29tKSB7XHJcblx0XHR2YXIgdG9wTGVmdFBvaW50ID0gdGhpcy5fZ2V0VG9wTGVmdFBvaW50KGNlbnRlciwgem9vbSk7XHJcblx0XHRyZXR1cm4gbmV3IEwuQm91bmRzKHRvcExlZnRQb2ludCwgdG9wTGVmdFBvaW50LmFkZCh0aGlzLmdldFNpemUoKSkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIFRPRE86IENoZWNrIHNlbWFudGljcyAtIGlzbid0IHRoZSBwaXhlbCBvcmlnaW4gdGhlIDAsMCBjb29yZCByZWxhdGl2ZSB0b1xyXG5cdC8vIHRoZSBtYXAgcGFuZT8gXCJsZWZ0IHBvaW50IG9mIHRoZSBtYXAgbGF5ZXJcIiBjYW4gYmUgY29uZnVzaW5nLCBzcGVjaWFsbHlcclxuXHQvLyBzaW5jZSB0aGVyZSBjYW4gYmUgbmVnYXRpdmUgb2Zmc2V0cy5cclxuXHQvLyBAbWV0aG9kIGdldFBpeGVsT3JpZ2luKCk6IFBvaW50XHJcblx0Ly8gUmV0dXJucyB0aGUgcHJvamVjdGVkIHBpeGVsIGNvb3JkaW5hdGVzIG9mIHRoZSB0b3AgbGVmdCBwb2ludCBvZlxyXG5cdC8vIHRoZSBtYXAgbGF5ZXIgKHVzZWZ1bCBpbiBjdXN0b20gbGF5ZXIgYW5kIG92ZXJsYXkgaW1wbGVtZW50YXRpb25zKS5cclxuXHRnZXRQaXhlbE9yaWdpbjogZnVuY3Rpb24gKCkge1xyXG5cdFx0dGhpcy5fY2hlY2tJZkxvYWRlZCgpO1xyXG5cdFx0cmV0dXJuIHRoaXMuX3BpeGVsT3JpZ2luO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0UGl4ZWxXb3JsZEJvdW5kcyh6b29tPzogTnVtYmVyKTogQm91bmRzXHJcblx0Ly8gUmV0dXJucyB0aGUgd29ybGQncyBib3VuZHMgaW4gcGl4ZWwgY29vcmRpbmF0ZXMgZm9yIHpvb20gbGV2ZWwgYHpvb21gLlxyXG5cdC8vIElmIGB6b29tYCBpcyBvbWl0dGVkLCB0aGUgbWFwJ3MgY3VycmVudCB6b29tIGxldmVsIGlzIHVzZWQuXHJcblx0Z2V0UGl4ZWxXb3JsZEJvdW5kczogZnVuY3Rpb24gKHpvb20pIHtcclxuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMuY3JzLmdldFByb2plY3RlZEJvdW5kcyh6b29tID09PSB1bmRlZmluZWQgPyB0aGlzLmdldFpvb20oKSA6IHpvb20pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBzZWN0aW9uIE90aGVyIE1ldGhvZHNcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRQYW5lKHBhbmU6IFN0cmluZ3xIVE1MRWxlbWVudCk6IEhUTUxFbGVtZW50XHJcblx0Ly8gUmV0dXJucyBhIFttYXAgcGFuZV0oI21hcC1wYW5lKSwgZ2l2ZW4gaXRzIG5hbWUgb3IgaXRzIEhUTUwgZWxlbWVudCAoaXRzIGlkZW50aXR5KS5cclxuXHRnZXRQYW5lOiBmdW5jdGlvbiAocGFuZSkge1xyXG5cdFx0cmV0dXJuIHR5cGVvZiBwYW5lID09PSAnc3RyaW5nJyA/IHRoaXMuX3BhbmVzW3BhbmVdIDogcGFuZTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldFBhbmVzKCk6IE9iamVjdFxyXG5cdC8vIFJldHVybnMgYSBwbGFpbiBvYmplY3QgY29udGFpbmluZyB0aGUgbmFtZXMgb2YgYWxsIFtwYW5lc10oI21hcC1wYW5lKSBhcyBrZXlzIGFuZFxyXG5cdC8vIHRoZSBwYW5lcyBhcyB2YWx1ZXMuXHJcblx0Z2V0UGFuZXM6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9wYW5lcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldENvbnRhaW5lcjogSFRNTEVsZW1lbnRcclxuXHQvLyBSZXR1cm5zIHRoZSBIVE1MIGVsZW1lbnQgdGhhdCBjb250YWlucyB0aGUgbWFwLlxyXG5cdGdldENvbnRhaW5lcjogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX2NvbnRhaW5lcjtcclxuXHR9LFxyXG5cclxuXHJcblx0Ly8gQHNlY3Rpb24gQ29udmVyc2lvbiBNZXRob2RzXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Wm9vbVNjYWxlKHRvWm9vbTogTnVtYmVyLCBmcm9tWm9vbTogTnVtYmVyKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgc2NhbGUgZmFjdG9yIHRvIGJlIGFwcGxpZWQgdG8gYSBtYXAgdHJhbnNpdGlvbiBmcm9tIHpvb20gbGV2ZWxcclxuXHQvLyBgZnJvbVpvb21gIHRvIGB0b1pvb21gLiBVc2VkIGludGVybmFsbHkgdG8gaGVscCB3aXRoIHpvb20gYW5pbWF0aW9ucy5cclxuXHRnZXRab29tU2NhbGU6IGZ1bmN0aW9uICh0b1pvb20sIGZyb21ab29tKSB7XHJcblx0XHQvLyBUT0RPIHJlcGxhY2Ugd2l0aCB1bml2ZXJzYWwgaW1wbGVtZW50YXRpb24gYWZ0ZXIgcmVmYWN0b3JpbmcgcHJvamVjdGlvbnNcclxuXHRcdHZhciBjcnMgPSB0aGlzLm9wdGlvbnMuY3JzO1xyXG5cdFx0ZnJvbVpvb20gPSBmcm9tWm9vbSA9PT0gdW5kZWZpbmVkID8gdGhpcy5fem9vbSA6IGZyb21ab29tO1xyXG5cdFx0cmV0dXJuIGNycy5zY2FsZSh0b1pvb20pIC8gY3JzLnNjYWxlKGZyb21ab29tKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldFNjYWxlWm9vbShzY2FsZTogTnVtYmVyLCBmcm9tWm9vbTogTnVtYmVyKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgem9vbSBsZXZlbCB0aGF0IHRoZSBtYXAgd291bGQgZW5kIHVwIGF0LCBpZiBpdCBpcyBhdCBgZnJvbVpvb21gXHJcblx0Ly8gbGV2ZWwgYW5kIGV2ZXJ5dGhpbmcgaXMgc2NhbGVkIGJ5IGEgZmFjdG9yIG9mIGBzY2FsZWAuIEludmVyc2Ugb2ZcclxuXHQvLyBbYGdldFpvb21TY2FsZWBdKCNtYXAtZ2V0Wm9vbVNjYWxlKS5cclxuXHRnZXRTY2FsZVpvb206IGZ1bmN0aW9uIChzY2FsZSwgZnJvbVpvb20pIHtcclxuXHRcdHZhciBjcnMgPSB0aGlzLm9wdGlvbnMuY3JzO1xyXG5cdFx0ZnJvbVpvb20gPSBmcm9tWm9vbSA9PT0gdW5kZWZpbmVkID8gdGhpcy5fem9vbSA6IGZyb21ab29tO1xyXG5cdFx0dmFyIHpvb20gPSBjcnMuem9vbShzY2FsZSAqIGNycy5zY2FsZShmcm9tWm9vbSkpO1xyXG5cdFx0cmV0dXJuIGlzTmFOKHpvb20pID8gSW5maW5pdHkgOiB6b29tO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgcHJvamVjdChsYXRsbmc6IExhdExuZywgem9vbTogTnVtYmVyKTogUG9pbnRcclxuXHQvLyBQcm9qZWN0cyBhIGdlb2dyYXBoaWNhbCBjb29yZGluYXRlIGBMYXRMbmdgIGFjY29yZGluZyB0byB0aGUgcHJvamVjdGlvblxyXG5cdC8vIG9mIHRoZSBtYXAncyBDUlMsIHRoZW4gc2NhbGVzIGl0IGFjY29yZGluZyB0byBgem9vbWAgYW5kIHRoZSBDUlMnc1xyXG5cdC8vIGBUcmFuc2Zvcm1hdGlvbmAuIFRoZSByZXN1bHQgaXMgcGl4ZWwgY29vcmRpbmF0ZSByZWxhdGl2ZSB0b1xyXG5cdC8vIHRoZSBDUlMgb3JpZ2luLlxyXG5cdHByb2plY3Q6IGZ1bmN0aW9uIChsYXRsbmcsIHpvb20pIHtcclxuXHRcdHpvb20gPSB6b29tID09PSB1bmRlZmluZWQgPyB0aGlzLl96b29tIDogem9vbTtcclxuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMuY3JzLmxhdExuZ1RvUG9pbnQoTC5sYXRMbmcobGF0bG5nKSwgem9vbSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB1bnByb2plY3QocG9pbnQ6IFBvaW50LCB6b29tOiBOdW1iZXIpOiBMYXRMbmdcclxuXHQvLyBJbnZlcnNlIG9mIFtgcHJvamVjdGBdKCNtYXAtcHJvamVjdCkuXHJcblx0dW5wcm9qZWN0OiBmdW5jdGlvbiAocG9pbnQsIHpvb20pIHtcclxuXHRcdHpvb20gPSB6b29tID09PSB1bmRlZmluZWQgPyB0aGlzLl96b29tIDogem9vbTtcclxuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMuY3JzLnBvaW50VG9MYXRMbmcoTC5wb2ludChwb2ludCksIHpvb20pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgbGF5ZXJQb2ludFRvTGF0TG5nKHBvaW50OiBQb2ludCk6IExhdExuZ1xyXG5cdC8vIEdpdmVuIGEgcGl4ZWwgY29vcmRpbmF0ZSByZWxhdGl2ZSB0byB0aGUgW29yaWdpbiBwaXhlbF0oI21hcC1nZXRwaXhlbG9yaWdpbiksXHJcblx0Ly8gcmV0dXJucyB0aGUgY29ycmVzcG9uZGluZyBnZW9ncmFwaGljYWwgY29vcmRpbmF0ZSAoZm9yIHRoZSBjdXJyZW50IHpvb20gbGV2ZWwpLlxyXG5cdGxheWVyUG9pbnRUb0xhdExuZzogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHR2YXIgcHJvamVjdGVkUG9pbnQgPSBMLnBvaW50KHBvaW50KS5hZGQodGhpcy5nZXRQaXhlbE9yaWdpbigpKTtcclxuXHRcdHJldHVybiB0aGlzLnVucHJvamVjdChwcm9qZWN0ZWRQb2ludCk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBsYXRMbmdUb0xheWVyUG9pbnQobGF0bG5nOiBMYXRMbmcpOiBQb2ludFxyXG5cdC8vIEdpdmVuIGEgZ2VvZ3JhcGhpY2FsIGNvb3JkaW5hdGUsIHJldHVybnMgdGhlIGNvcnJlc3BvbmRpbmcgcGl4ZWwgY29vcmRpbmF0ZVxyXG5cdC8vIHJlbGF0aXZlIHRvIHRoZSBbb3JpZ2luIHBpeGVsXSgjbWFwLWdldHBpeGVsb3JpZ2luKS5cclxuXHRsYXRMbmdUb0xheWVyUG9pbnQ6IGZ1bmN0aW9uIChsYXRsbmcpIHtcclxuXHRcdHZhciBwcm9qZWN0ZWRQb2ludCA9IHRoaXMucHJvamVjdChMLmxhdExuZyhsYXRsbmcpKS5fcm91bmQoKTtcclxuXHRcdHJldHVybiBwcm9qZWN0ZWRQb2ludC5fc3VidHJhY3QodGhpcy5nZXRQaXhlbE9yaWdpbigpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHdyYXBMYXRMbmcobGF0bG5nOiBMYXRMbmcpOiBMYXRMbmdcclxuXHQvLyBSZXR1cm5zIGEgYExhdExuZ2Agd2hlcmUgYGxhdGAgYW5kIGBsbmdgIGhhcyBiZWVuIHdyYXBwZWQgYWNjb3JkaW5nIHRvIHRoZVxyXG5cdC8vIG1hcCdzIENSUydzIGB3cmFwTGF0YCBhbmQgYHdyYXBMbmdgIHByb3BlcnRpZXMsIGlmIHRoZXkgYXJlIG91dHNpZGUgdGhlXHJcblx0Ly8gQ1JTJ3MgYm91bmRzLlxyXG5cdC8vIEJ5IGRlZmF1bHQgdGhpcyBtZWFucyBsb25naXR1ZGUgaXMgd3JhcHBlZCBhcm91bmQgdGhlIGRhdGVsaW5lIHNvIGl0c1xyXG5cdC8vIHZhbHVlIGlzIGJldHdlZW4gLTE4MCBhbmQgKzE4MCBkZWdyZWVzLlxyXG5cdHdyYXBMYXRMbmc6IGZ1bmN0aW9uIChsYXRsbmcpIHtcclxuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMuY3JzLndyYXBMYXRMbmcoTC5sYXRMbmcobGF0bG5nKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBkaXN0YW5jZShsYXRsbmcxOiBMYXRMbmcsIGxhdGxuZzI6IExhdExuZyk6IE51bWJlclxyXG5cdC8vIFJldHVybnMgdGhlIGRpc3RhbmNlIGJldHdlZW4gdHdvIGdlb2dyYXBoaWNhbCBjb29yZGluYXRlcyBhY2NvcmRpbmcgdG9cclxuXHQvLyB0aGUgbWFwJ3MgQ1JTLiBCeSBkZWZhdWx0IHRoaXMgbWVhc3VyZXMgZGlzdGFuY2UgaW4gbWV0ZXJzLlxyXG5cdGRpc3RhbmNlOiBmdW5jdGlvbiAobGF0bG5nMSwgbGF0bG5nMikge1xyXG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5jcnMuZGlzdGFuY2UoTC5sYXRMbmcobGF0bG5nMSksIEwubGF0TG5nKGxhdGxuZzIpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGNvbnRhaW5lclBvaW50VG9MYXllclBvaW50KHBvaW50OiBQb2ludCk6IFBvaW50XHJcblx0Ly8gR2l2ZW4gYSBwaXhlbCBjb29yZGluYXRlIHJlbGF0aXZlIHRvIHRoZSBtYXAgY29udGFpbmVyLCByZXR1cm5zIHRoZSBjb3JyZXNwb25kaW5nXHJcblx0Ly8gcGl4ZWwgY29vcmRpbmF0ZSByZWxhdGl2ZSB0byB0aGUgW29yaWdpbiBwaXhlbF0oI21hcC1nZXRwaXhlbG9yaWdpbikuXHJcblx0Y29udGFpbmVyUG9pbnRUb0xheWVyUG9pbnQ6IGZ1bmN0aW9uIChwb2ludCkgeyAvLyAoUG9pbnQpXHJcblx0XHRyZXR1cm4gTC5wb2ludChwb2ludCkuc3VidHJhY3QodGhpcy5fZ2V0TWFwUGFuZVBvcygpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGxheWVyUG9pbnRUb0NvbnRhaW5lclBvaW50KHBvaW50OiBQb2ludCk6IFBvaW50XHJcblx0Ly8gR2l2ZW4gYSBwaXhlbCBjb29yZGluYXRlIHJlbGF0aXZlIHRvIHRoZSBbb3JpZ2luIHBpeGVsXSgjbWFwLWdldHBpeGVsb3JpZ2luKSxcclxuXHQvLyByZXR1cm5zIHRoZSBjb3JyZXNwb25kaW5nIHBpeGVsIGNvb3JkaW5hdGUgcmVsYXRpdmUgdG8gdGhlIG1hcCBjb250YWluZXIuXHJcblx0bGF5ZXJQb2ludFRvQ29udGFpbmVyUG9pbnQ6IGZ1bmN0aW9uIChwb2ludCkgeyAvLyAoUG9pbnQpXHJcblx0XHRyZXR1cm4gTC5wb2ludChwb2ludCkuYWRkKHRoaXMuX2dldE1hcFBhbmVQb3MoKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBjb250YWluZXJQb2ludFRvTGF0TG5nKHBvaW50OiBQb2ludCk6IFBvaW50XHJcblx0Ly8gR2l2ZW4gYSBwaXhlbCBjb29yZGluYXRlIHJlbGF0aXZlIHRvIHRoZSBtYXAgY29udGFpbmVyLCByZXR1cm5zXHJcblx0Ly8gdGhlIGNvcnJlc3BvbmRpbmcgZ2VvZ3JhcGhpY2FsIGNvb3JkaW5hdGUgKGZvciB0aGUgY3VycmVudCB6b29tIGxldmVsKS5cclxuXHRjb250YWluZXJQb2ludFRvTGF0TG5nOiBmdW5jdGlvbiAocG9pbnQpIHtcclxuXHRcdHZhciBsYXllclBvaW50ID0gdGhpcy5jb250YWluZXJQb2ludFRvTGF5ZXJQb2ludChMLnBvaW50KHBvaW50KSk7XHJcblx0XHRyZXR1cm4gdGhpcy5sYXllclBvaW50VG9MYXRMbmcobGF5ZXJQb2ludCk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBsYXRMbmdUb0NvbnRhaW5lclBvaW50KGxhdGxuZzogTGF0TG5nKTogUG9pbnRcclxuXHQvLyBHaXZlbiBhIGdlb2dyYXBoaWNhbCBjb29yZGluYXRlLCByZXR1cm5zIHRoZSBjb3JyZXNwb25kaW5nIHBpeGVsIGNvb3JkaW5hdGVcclxuXHQvLyByZWxhdGl2ZSB0byB0aGUgbWFwIGNvbnRhaW5lci5cclxuXHRsYXRMbmdUb0NvbnRhaW5lclBvaW50OiBmdW5jdGlvbiAobGF0bG5nKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5sYXllclBvaW50VG9Db250YWluZXJQb2ludCh0aGlzLmxhdExuZ1RvTGF5ZXJQb2ludChMLmxhdExuZyhsYXRsbmcpKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBtb3VzZUV2ZW50VG9Db250YWluZXJQb2ludChldjogTW91c2VFdmVudCk6IFBvaW50XHJcblx0Ly8gR2l2ZW4gYSBNb3VzZUV2ZW50IG9iamVjdCwgcmV0dXJucyB0aGUgcGl4ZWwgY29vcmRpbmF0ZSByZWxhdGl2ZSB0byB0aGVcclxuXHQvLyBtYXAgY29udGFpbmVyIHdoZXJlIHRoZSBldmVudCB0b29rIHBsYWNlLlxyXG5cdG1vdXNlRXZlbnRUb0NvbnRhaW5lclBvaW50OiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0cmV0dXJuIEwuRG9tRXZlbnQuZ2V0TW91c2VQb3NpdGlvbihlLCB0aGlzLl9jb250YWluZXIpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgbW91c2VFdmVudFRvTGF5ZXJQb2ludChldjogTW91c2VFdmVudCk6IFBvaW50XHJcblx0Ly8gR2l2ZW4gYSBNb3VzZUV2ZW50IG9iamVjdCwgcmV0dXJucyB0aGUgcGl4ZWwgY29vcmRpbmF0ZSByZWxhdGl2ZSB0b1xyXG5cdC8vIHRoZSBbb3JpZ2luIHBpeGVsXSgjbWFwLWdldHBpeGVsb3JpZ2luKSB3aGVyZSB0aGUgZXZlbnQgdG9vayBwbGFjZS5cclxuXHRtb3VzZUV2ZW50VG9MYXllclBvaW50OiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0cmV0dXJuIHRoaXMuY29udGFpbmVyUG9pbnRUb0xheWVyUG9pbnQodGhpcy5tb3VzZUV2ZW50VG9Db250YWluZXJQb2ludChlKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBtb3VzZUV2ZW50VG9MYXRMbmcoZXY6IE1vdXNlRXZlbnQpOiBMYXRMbmdcclxuXHQvLyBHaXZlbiBhIE1vdXNlRXZlbnQgb2JqZWN0LCByZXR1cm5zIGdlb2dyYXBoaWNhbCBjb29yZGluYXRlIHdoZXJlIHRoZVxyXG5cdC8vIGV2ZW50IHRvb2sgcGxhY2UuXHJcblx0bW91c2VFdmVudFRvTGF0TG5nOiBmdW5jdGlvbiAoZSkgeyAvLyAoTW91c2VFdmVudClcclxuXHRcdHJldHVybiB0aGlzLmxheWVyUG9pbnRUb0xhdExuZyh0aGlzLm1vdXNlRXZlbnRUb0xheWVyUG9pbnQoZSkpO1xyXG5cdH0sXHJcblxyXG5cclxuXHQvLyBtYXAgaW5pdGlhbGl6YXRpb24gbWV0aG9kc1xyXG5cclxuXHRfaW5pdENvbnRhaW5lcjogZnVuY3Rpb24gKGlkKSB7XHJcblx0XHR2YXIgY29udGFpbmVyID0gdGhpcy5fY29udGFpbmVyID0gTC5Eb21VdGlsLmdldChpZCk7XHJcblxyXG5cdFx0aWYgKCFjb250YWluZXIpIHtcclxuXHRcdFx0dGhyb3cgbmV3IEVycm9yKCdNYXAgY29udGFpbmVyIG5vdCBmb3VuZC4nKTtcclxuXHRcdH0gZWxzZSBpZiAoY29udGFpbmVyLl9sZWFmbGV0X2lkKSB7XHJcblx0XHRcdHRocm93IG5ldyBFcnJvcignTWFwIGNvbnRhaW5lciBpcyBhbHJlYWR5IGluaXRpYWxpemVkLicpO1xyXG5cdFx0fVxyXG5cclxuXHRcdEwuRG9tRXZlbnQuYWRkTGlzdGVuZXIoY29udGFpbmVyLCAnc2Nyb2xsJywgdGhpcy5fb25TY3JvbGwsIHRoaXMpO1xyXG5cdFx0dGhpcy5fY29udGFpbmVySWQgPSBMLlV0aWwuc3RhbXAoY29udGFpbmVyKTtcclxuXHR9LFxyXG5cclxuXHRfaW5pdExheW91dDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGNvbnRhaW5lciA9IHRoaXMuX2NvbnRhaW5lcjtcclxuXHJcblx0XHR0aGlzLl9mYWRlQW5pbWF0ZWQgPSB0aGlzLm9wdGlvbnMuZmFkZUFuaW1hdGlvbiAmJiBMLkJyb3dzZXIuYW55M2Q7XHJcblxyXG5cdFx0TC5Eb21VdGlsLmFkZENsYXNzKGNvbnRhaW5lciwgJ2xlYWZsZXQtY29udGFpbmVyJyArXHJcblx0XHRcdChMLkJyb3dzZXIudG91Y2ggPyAnIGxlYWZsZXQtdG91Y2gnIDogJycpICtcclxuXHRcdFx0KEwuQnJvd3Nlci5yZXRpbmEgPyAnIGxlYWZsZXQtcmV0aW5hJyA6ICcnKSArXHJcblx0XHRcdChMLkJyb3dzZXIuaWVsdDkgPyAnIGxlYWZsZXQtb2xkaWUnIDogJycpICtcclxuXHRcdFx0KEwuQnJvd3Nlci5zYWZhcmkgPyAnIGxlYWZsZXQtc2FmYXJpJyA6ICcnKSArXHJcblx0XHRcdCh0aGlzLl9mYWRlQW5pbWF0ZWQgPyAnIGxlYWZsZXQtZmFkZS1hbmltJyA6ICcnKSk7XHJcblxyXG5cdFx0dmFyIHBvc2l0aW9uID0gTC5Eb21VdGlsLmdldFN0eWxlKGNvbnRhaW5lciwgJ3Bvc2l0aW9uJyk7XHJcblxyXG5cdFx0aWYgKHBvc2l0aW9uICE9PSAnYWJzb2x1dGUnICYmIHBvc2l0aW9uICE9PSAncmVsYXRpdmUnICYmIHBvc2l0aW9uICE9PSAnZml4ZWQnKSB7XHJcblx0XHRcdGNvbnRhaW5lci5zdHlsZS5wb3NpdGlvbiA9ICdyZWxhdGl2ZSc7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5faW5pdFBhbmVzKCk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2luaXRDb250cm9sUG9zKSB7XHJcblx0XHRcdHRoaXMuX2luaXRDb250cm9sUG9zKCk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2luaXRQYW5lczogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIHBhbmVzID0gdGhpcy5fcGFuZXMgPSB7fTtcclxuXHRcdHRoaXMuX3BhbmVSZW5kZXJlcnMgPSB7fTtcclxuXHJcblx0XHQvLyBAc2VjdGlvblxyXG5cdFx0Ly9cclxuXHRcdC8vIFBhbmVzIGFyZSBET00gZWxlbWVudHMgdXNlZCB0byBjb250cm9sIHRoZSBvcmRlcmluZyBvZiBsYXllcnMgb24gdGhlIG1hcC4gWW91XHJcblx0XHQvLyBjYW4gYWNjZXNzIHBhbmVzIHdpdGggW2BtYXAuZ2V0UGFuZWBdKCNtYXAtZ2V0cGFuZSkgb3JcclxuXHRcdC8vIFtgbWFwLmdldFBhbmVzYF0oI21hcC1nZXRwYW5lcykgbWV0aG9kcy4gTmV3IHBhbmVzIGNhbiBiZSBjcmVhdGVkIHdpdGggdGhlXHJcblx0XHQvLyBbYG1hcC5jcmVhdGVQYW5lYF0oI21hcC1jcmVhdGVwYW5lKSBtZXRob2QuXHJcblx0XHQvL1xyXG5cdFx0Ly8gRXZlcnkgbWFwIGhhcyB0aGUgZm9sbG93aW5nIGRlZmF1bHQgcGFuZXMgdGhhdCBkaWZmZXIgb25seSBpbiB6SW5kZXguXHJcblx0XHQvL1xyXG5cdFx0Ly8gQHBhbmUgbWFwUGFuZTogSFRNTEVsZW1lbnQgPSAnYXV0bydcclxuXHRcdC8vIFBhbmUgdGhhdCBjb250YWlucyBhbGwgb3RoZXIgbWFwIHBhbmVzXHJcblxyXG5cdFx0dGhpcy5fbWFwUGFuZSA9IHRoaXMuY3JlYXRlUGFuZSgnbWFwUGFuZScsIHRoaXMuX2NvbnRhaW5lcik7XHJcblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24odGhpcy5fbWFwUGFuZSwgbmV3IEwuUG9pbnQoMCwgMCkpO1xyXG5cclxuXHRcdC8vIEBwYW5lIHRpbGVQYW5lOiBIVE1MRWxlbWVudCA9IDIwMFxyXG5cdFx0Ly8gUGFuZSBmb3IgYEdyaWRMYXllcmBzIGFuZCBgVGlsZUxheWVyYHNcclxuXHRcdHRoaXMuY3JlYXRlUGFuZSgndGlsZVBhbmUnKTtcclxuXHRcdC8vIEBwYW5lIG92ZXJsYXlQYW5lOiBIVE1MRWxlbWVudCA9IDQwMFxyXG5cdFx0Ly8gUGFuZSBmb3IgdmVjdG9yIG92ZXJsYXlzIChgUGF0aGBzKSwgbGlrZSBgUG9seWxpbmVgcyBhbmQgYFBvbHlnb25gc1xyXG5cdFx0dGhpcy5jcmVhdGVQYW5lKCdzaGFkb3dQYW5lJyk7XHJcblx0XHQvLyBAcGFuZSBzaGFkb3dQYW5lOiBIVE1MRWxlbWVudCA9IDUwMFxyXG5cdFx0Ly8gUGFuZSBmb3Igb3ZlcmxheSBzaGFkb3dzIChlLmcuIGBNYXJrZXJgIHNoYWRvd3MpXHJcblx0XHR0aGlzLmNyZWF0ZVBhbmUoJ292ZXJsYXlQYW5lJyk7XHJcblx0XHQvLyBAcGFuZSBtYXJrZXJQYW5lOiBIVE1MRWxlbWVudCA9IDYwMFxyXG5cdFx0Ly8gUGFuZSBmb3IgYEljb25gcyBvZiBgTWFya2VyYHNcclxuXHRcdHRoaXMuY3JlYXRlUGFuZSgnbWFya2VyUGFuZScpO1xyXG5cdFx0Ly8gQHBhbmUgdG9vbHRpcFBhbmU6IEhUTUxFbGVtZW50ID0gNjUwXHJcblx0XHQvLyBQYW5lIGZvciB0b29sdGlwLlxyXG5cdFx0dGhpcy5jcmVhdGVQYW5lKCd0b29sdGlwUGFuZScpO1xyXG5cdFx0Ly8gQHBhbmUgcG9wdXBQYW5lOiBIVE1MRWxlbWVudCA9IDcwMFxyXG5cdFx0Ly8gUGFuZSBmb3IgYFBvcHVwYHMuXHJcblx0XHR0aGlzLmNyZWF0ZVBhbmUoJ3BvcHVwUGFuZScpO1xyXG5cclxuXHRcdGlmICghdGhpcy5vcHRpb25zLm1hcmtlclpvb21BbmltYXRpb24pIHtcclxuXHRcdFx0TC5Eb21VdGlsLmFkZENsYXNzKHBhbmVzLm1hcmtlclBhbmUsICdsZWFmbGV0LXpvb20taGlkZScpO1xyXG5cdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3MocGFuZXMuc2hhZG93UGFuZSwgJ2xlYWZsZXQtem9vbS1oaWRlJyk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblxyXG5cdC8vIHByaXZhdGUgbWV0aG9kcyB0aGF0IG1vZGlmeSBtYXAgc3RhdGVcclxuXHJcblx0Ly8gQHNlY3Rpb24gTWFwIHN0YXRlIGNoYW5nZSBldmVudHNcclxuXHRfcmVzZXRWaWV3OiBmdW5jdGlvbiAoY2VudGVyLCB6b29tKSB7XHJcblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24odGhpcy5fbWFwUGFuZSwgbmV3IEwuUG9pbnQoMCwgMCkpO1xyXG5cclxuXHRcdHZhciBsb2FkaW5nID0gIXRoaXMuX2xvYWRlZDtcclxuXHRcdHRoaXMuX2xvYWRlZCA9IHRydWU7XHJcblx0XHR6b29tID0gdGhpcy5fbGltaXRab29tKHpvb20pO1xyXG5cclxuXHRcdHRoaXMuZmlyZSgndmlld3ByZXJlc2V0Jyk7XHJcblxyXG5cdFx0dmFyIHpvb21DaGFuZ2VkID0gdGhpcy5fem9vbSAhPT0gem9vbTtcclxuXHRcdHRoaXNcclxuXHRcdFx0Ll9tb3ZlU3RhcnQoem9vbUNoYW5nZWQpXHJcblx0XHRcdC5fbW92ZShjZW50ZXIsIHpvb20pXHJcblx0XHRcdC5fbW92ZUVuZCh6b29tQ2hhbmdlZCk7XHJcblxyXG5cdFx0Ly8gQGV2ZW50IHZpZXdyZXNldDogRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIG1hcCBuZWVkcyB0byByZWRyYXcgaXRzIGNvbnRlbnQgKHRoaXMgdXN1YWxseSBoYXBwZW5zXHJcblx0XHQvLyBvbiBtYXAgem9vbSBvciBsb2FkKS4gVmVyeSB1c2VmdWwgZm9yIGNyZWF0aW5nIGN1c3RvbSBvdmVybGF5cy5cclxuXHRcdHRoaXMuZmlyZSgndmlld3Jlc2V0Jyk7XHJcblxyXG5cdFx0Ly8gQGV2ZW50IGxvYWQ6IEV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBtYXAgaXMgaW5pdGlhbGl6ZWQgKHdoZW4gaXRzIGNlbnRlciBhbmQgem9vbSBhcmUgc2V0XHJcblx0XHQvLyBmb3IgdGhlIGZpcnN0IHRpbWUpLlxyXG5cdFx0aWYgKGxvYWRpbmcpIHtcclxuXHRcdFx0dGhpcy5maXJlKCdsb2FkJyk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X21vdmVTdGFydDogZnVuY3Rpb24gKHpvb21DaGFuZ2VkKSB7XHJcblx0XHQvLyBAZXZlbnQgem9vbXN0YXJ0OiBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgbWFwIHpvb20gaXMgYWJvdXQgdG8gY2hhbmdlIChlLmcuIGJlZm9yZSB6b29tIGFuaW1hdGlvbikuXHJcblx0XHQvLyBAZXZlbnQgbW92ZXN0YXJ0OiBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgdmlldyBvZiB0aGUgbWFwIHN0YXJ0cyBjaGFuZ2luZyAoZS5nLiB1c2VyIHN0YXJ0cyBkcmFnZ2luZyB0aGUgbWFwKS5cclxuXHRcdGlmICh6b29tQ2hhbmdlZCkge1xyXG5cdFx0XHR0aGlzLmZpcmUoJ3pvb21zdGFydCcpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXMuZmlyZSgnbW92ZXN0YXJ0Jyk7XHJcblx0fSxcclxuXHJcblx0X21vdmU6IGZ1bmN0aW9uIChjZW50ZXIsIHpvb20sIGRhdGEpIHtcclxuXHRcdGlmICh6b29tID09PSB1bmRlZmluZWQpIHtcclxuXHRcdFx0em9vbSA9IHRoaXMuX3pvb207XHJcblx0XHR9XHJcblx0XHR2YXIgem9vbUNoYW5nZWQgPSB0aGlzLl96b29tICE9PSB6b29tO1xyXG5cclxuXHRcdHRoaXMuX3pvb20gPSB6b29tO1xyXG5cdFx0dGhpcy5fbGFzdENlbnRlciA9IGNlbnRlcjtcclxuXHRcdHRoaXMuX3BpeGVsT3JpZ2luID0gdGhpcy5fZ2V0TmV3UGl4ZWxPcmlnaW4oY2VudGVyKTtcclxuXHJcblx0XHQvLyBAZXZlbnQgem9vbTogRXZlbnRcclxuXHRcdC8vIEZpcmVkIHJlcGVhdGVkbHkgZHVyaW5nIGFueSBjaGFuZ2UgaW4gem9vbSBsZXZlbCwgaW5jbHVkaW5nIHpvb21cclxuXHRcdC8vIGFuZCBmbHkgYW5pbWF0aW9ucy5cclxuXHRcdGlmICh6b29tQ2hhbmdlZCB8fCAoZGF0YSAmJiBkYXRhLnBpbmNoKSkge1x0Ly8gQWx3YXlzIGZpcmUgJ3pvb20nIGlmIHBpbmNoaW5nIGJlY2F1c2UgIzM1MzBcclxuXHRcdFx0dGhpcy5maXJlKCd6b29tJywgZGF0YSk7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gQGV2ZW50IG1vdmU6IEV2ZW50XHJcblx0XHQvLyBGaXJlZCByZXBlYXRlZGx5IGR1cmluZyBhbnkgbW92ZW1lbnQgb2YgdGhlIG1hcCwgaW5jbHVkaW5nIHBhbiBhbmRcclxuXHRcdC8vIGZseSBhbmltYXRpb25zLlxyXG5cdFx0cmV0dXJuIHRoaXMuZmlyZSgnbW92ZScsIGRhdGEpO1xyXG5cdH0sXHJcblxyXG5cdF9tb3ZlRW5kOiBmdW5jdGlvbiAoem9vbUNoYW5nZWQpIHtcclxuXHRcdC8vIEBldmVudCB6b29tZW5kOiBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgbWFwIGhhcyBjaGFuZ2VkLCBhZnRlciBhbnkgYW5pbWF0aW9ucy5cclxuXHRcdGlmICh6b29tQ2hhbmdlZCkge1xyXG5cdFx0XHR0aGlzLmZpcmUoJ3pvb21lbmQnKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBAZXZlbnQgbW92ZWVuZDogRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIGNlbnRlciBvZiB0aGUgbWFwIHN0b3BzIGNoYW5naW5nIChlLmcuIHVzZXIgc3RvcHBlZFxyXG5cdFx0Ly8gZHJhZ2dpbmcgdGhlIG1hcCkuXHJcblx0XHRyZXR1cm4gdGhpcy5maXJlKCdtb3ZlZW5kJyk7XHJcblx0fSxcclxuXHJcblx0X3N0b3A6IGZ1bmN0aW9uICgpIHtcclxuXHRcdEwuVXRpbC5jYW5jZWxBbmltRnJhbWUodGhpcy5fZmx5VG9GcmFtZSk7XHJcblx0XHRpZiAodGhpcy5fcGFuQW5pbSkge1xyXG5cdFx0XHR0aGlzLl9wYW5BbmltLnN0b3AoKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdF9yYXdQYW5CeTogZnVuY3Rpb24gKG9mZnNldCkge1xyXG5cdFx0TC5Eb21VdGlsLnNldFBvc2l0aW9uKHRoaXMuX21hcFBhbmUsIHRoaXMuX2dldE1hcFBhbmVQb3MoKS5zdWJ0cmFjdChvZmZzZXQpKTtcclxuXHR9LFxyXG5cclxuXHRfZ2V0Wm9vbVNwYW46IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLmdldE1heFpvb20oKSAtIHRoaXMuZ2V0TWluWm9vbSgpO1xyXG5cdH0sXHJcblxyXG5cdF9wYW5JbnNpZGVNYXhCb3VuZHM6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICghdGhpcy5fZW5mb3JjaW5nQm91bmRzKSB7XHJcblx0XHRcdHRoaXMucGFuSW5zaWRlQm91bmRzKHRoaXMub3B0aW9ucy5tYXhCb3VuZHMpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdF9jaGVja0lmTG9hZGVkOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAoIXRoaXMuX2xvYWRlZCkge1xyXG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoJ1NldCBtYXAgY2VudGVyIGFuZCB6b29tIGZpcnN0LicpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIERPTSBldmVudCBoYW5kbGluZ1xyXG5cclxuXHQvLyBAc2VjdGlvbiBJbnRlcmFjdGlvbiBldmVudHNcclxuXHRfaW5pdEV2ZW50czogZnVuY3Rpb24gKHJlbW92ZSkge1xyXG5cdFx0aWYgKCFMLkRvbUV2ZW50KSB7IHJldHVybjsgfVxyXG5cclxuXHRcdHRoaXMuX3RhcmdldHMgPSB7fTtcclxuXHRcdHRoaXMuX3RhcmdldHNbTC5zdGFtcCh0aGlzLl9jb250YWluZXIpXSA9IHRoaXM7XHJcblxyXG5cdFx0dmFyIG9uT2ZmID0gcmVtb3ZlID8gJ29mZicgOiAnb24nO1xyXG5cclxuXHRcdC8vIEBldmVudCBjbGljazogTW91c2VFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgdXNlciBjbGlja3MgKG9yIHRhcHMpIHRoZSBtYXAuXHJcblx0XHQvLyBAZXZlbnQgZGJsY2xpY2s6IE1vdXNlRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIHVzZXIgZG91YmxlLWNsaWNrcyAob3IgZG91YmxlLXRhcHMpIHRoZSBtYXAuXHJcblx0XHQvLyBAZXZlbnQgbW91c2Vkb3duOiBNb3VzZUV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSB1c2VyIHB1c2hlcyB0aGUgbW91c2UgYnV0dG9uIG9uIHRoZSBtYXAuXHJcblx0XHQvLyBAZXZlbnQgbW91c2V1cDogTW91c2VFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgdXNlciByZWxlYXNlcyB0aGUgbW91c2UgYnV0dG9uIG9uIHRoZSBtYXAuXHJcblx0XHQvLyBAZXZlbnQgbW91c2VvdmVyOiBNb3VzZUV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBtb3VzZSBlbnRlcnMgdGhlIG1hcC5cclxuXHRcdC8vIEBldmVudCBtb3VzZW91dDogTW91c2VFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgbW91c2UgbGVhdmVzIHRoZSBtYXAuXHJcblx0XHQvLyBAZXZlbnQgbW91c2Vtb3ZlOiBNb3VzZUV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGlsZSB0aGUgbW91c2UgbW92ZXMgb3ZlciB0aGUgbWFwLlxyXG5cdFx0Ly8gQGV2ZW50IGNvbnRleHRtZW51OiBNb3VzZUV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSB1c2VyIHB1c2hlcyB0aGUgcmlnaHQgbW91c2UgYnV0dG9uIG9uIHRoZSBtYXAsIHByZXZlbnRzXHJcblx0XHQvLyBkZWZhdWx0IGJyb3dzZXIgY29udGV4dCBtZW51IGZyb20gc2hvd2luZyBpZiB0aGVyZSBhcmUgbGlzdGVuZXJzIG9uXHJcblx0XHQvLyB0aGlzIGV2ZW50LiBBbHNvIGZpcmVkIG9uIG1vYmlsZSB3aGVuIHRoZSB1c2VyIGhvbGRzIGEgc2luZ2xlIHRvdWNoXHJcblx0XHQvLyBmb3IgYSBzZWNvbmQgKGFsc28gY2FsbGVkIGxvbmcgcHJlc3MpLlxyXG5cdFx0Ly8gQGV2ZW50IGtleXByZXNzOiBLZXlib2FyZEV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSB1c2VyIHByZXNzZXMgYSBrZXkgZnJvbSB0aGUga2V5Ym9hcmQgd2hpbGUgdGhlIG1hcCBpcyBmb2N1c2VkLlxyXG5cdFx0TC5Eb21FdmVudFtvbk9mZl0odGhpcy5fY29udGFpbmVyLCAnY2xpY2sgZGJsY2xpY2sgbW91c2Vkb3duIG1vdXNldXAgJyArXHJcblx0XHRcdCdtb3VzZW92ZXIgbW91c2VvdXQgbW91c2Vtb3ZlIGNvbnRleHRtZW51IGtleXByZXNzJywgdGhpcy5faGFuZGxlRE9NRXZlbnQsIHRoaXMpO1xyXG5cclxuXHRcdGlmICh0aGlzLm9wdGlvbnMudHJhY2tSZXNpemUpIHtcclxuXHRcdFx0TC5Eb21FdmVudFtvbk9mZl0od2luZG93LCAncmVzaXplJywgdGhpcy5fb25SZXNpemUsIHRoaXMpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmIChMLkJyb3dzZXIuYW55M2QgJiYgdGhpcy5vcHRpb25zLnRyYW5zZm9ybTNETGltaXQpIHtcclxuXHRcdFx0dGhpc1tvbk9mZl0oJ21vdmVlbmQnLCB0aGlzLl9vbk1vdmVFbmQpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdF9vblJlc2l6ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0TC5VdGlsLmNhbmNlbEFuaW1GcmFtZSh0aGlzLl9yZXNpemVSZXF1ZXN0KTtcclxuXHRcdHRoaXMuX3Jlc2l6ZVJlcXVlc3QgPSBMLlV0aWwucmVxdWVzdEFuaW1GcmFtZShcclxuXHRcdCAgICAgICAgZnVuY3Rpb24gKCkgeyB0aGlzLmludmFsaWRhdGVTaXplKHtkZWJvdW5jZU1vdmVlbmQ6IHRydWV9KTsgfSwgdGhpcyk7XHJcblx0fSxcclxuXHJcblx0X29uU2Nyb2xsOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLl9jb250YWluZXIuc2Nyb2xsVG9wICA9IDA7XHJcblx0XHR0aGlzLl9jb250YWluZXIuc2Nyb2xsTGVmdCA9IDA7XHJcblx0fSxcclxuXHJcblx0X29uTW92ZUVuZDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIHBvcyA9IHRoaXMuX2dldE1hcFBhbmVQb3MoKTtcclxuXHRcdGlmIChNYXRoLm1heChNYXRoLmFicyhwb3MueCksIE1hdGguYWJzKHBvcy55KSkgPj0gdGhpcy5vcHRpb25zLnRyYW5zZm9ybTNETGltaXQpIHtcclxuXHRcdFx0Ly8gaHR0cHM6Ly9idWd6aWxsYS5tb3ppbGxhLm9yZy9zaG93X2J1Zy5jZ2k/aWQ9MTIwMzg3MyBidXQgV2Via2l0IGFsc28gaGF2ZVxyXG5cdFx0XHQvLyBhIHBpeGVsIG9mZnNldCBvbiB2ZXJ5IGhpZ2ggdmFsdWVzLCBzZWU6IGh0dHA6Ly9qc2ZpZGRsZS5uZXQvZGc2cjVoaGIvXHJcblx0XHRcdHRoaXMuX3Jlc2V0Vmlldyh0aGlzLmdldENlbnRlcigpLCB0aGlzLmdldFpvb20oKSk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2ZpbmRFdmVudFRhcmdldHM6IGZ1bmN0aW9uIChlLCB0eXBlKSB7XHJcblx0XHR2YXIgdGFyZ2V0cyA9IFtdLFxyXG5cdFx0ICAgIHRhcmdldCxcclxuXHRcdCAgICBpc0hvdmVyID0gdHlwZSA9PT0gJ21vdXNlb3V0JyB8fCB0eXBlID09PSAnbW91c2VvdmVyJyxcclxuXHRcdCAgICBzcmMgPSBlLnRhcmdldCB8fCBlLnNyY0VsZW1lbnQsXHJcblx0XHQgICAgZHJhZ2dpbmcgPSBmYWxzZTtcclxuXHJcblx0XHR3aGlsZSAoc3JjKSB7XHJcblx0XHRcdHRhcmdldCA9IHRoaXMuX3RhcmdldHNbTC5zdGFtcChzcmMpXTtcclxuXHRcdFx0aWYgKHRhcmdldCAmJiAodHlwZSA9PT0gJ2NsaWNrJyB8fCB0eXBlID09PSAncHJlY2xpY2snKSAmJiAhZS5fc2ltdWxhdGVkICYmIHRoaXMuX2RyYWdnYWJsZU1vdmVkKHRhcmdldCkpIHtcclxuXHRcdFx0XHQvLyBQcmV2ZW50IGZpcmluZyBjbGljayBhZnRlciB5b3UganVzdCBkcmFnZ2VkIGFuIG9iamVjdC5cclxuXHRcdFx0XHRkcmFnZ2luZyA9IHRydWU7XHJcblx0XHRcdFx0YnJlYWs7XHJcblx0XHRcdH1cclxuXHRcdFx0aWYgKHRhcmdldCAmJiB0YXJnZXQubGlzdGVucyh0eXBlLCB0cnVlKSkge1xyXG5cdFx0XHRcdGlmIChpc0hvdmVyICYmICFMLkRvbUV2ZW50Ll9pc0V4dGVybmFsVGFyZ2V0KHNyYywgZSkpIHsgYnJlYWs7IH1cclxuXHRcdFx0XHR0YXJnZXRzLnB1c2godGFyZ2V0KTtcclxuXHRcdFx0XHRpZiAoaXNIb3ZlcikgeyBicmVhazsgfVxyXG5cdFx0XHR9XHJcblx0XHRcdGlmIChzcmMgPT09IHRoaXMuX2NvbnRhaW5lcikgeyBicmVhazsgfVxyXG5cdFx0XHRzcmMgPSBzcmMucGFyZW50Tm9kZTtcclxuXHRcdH1cclxuXHRcdGlmICghdGFyZ2V0cy5sZW5ndGggJiYgIWRyYWdnaW5nICYmICFpc0hvdmVyICYmIEwuRG9tRXZlbnQuX2lzRXh0ZXJuYWxUYXJnZXQoc3JjLCBlKSkge1xyXG5cdFx0XHR0YXJnZXRzID0gW3RoaXNdO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRhcmdldHM7XHJcblx0fSxcclxuXHJcblx0X2hhbmRsZURPTUV2ZW50OiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0aWYgKCF0aGlzLl9sb2FkZWQgfHwgTC5Eb21FdmVudC5fc2tpcHBlZChlKSkgeyByZXR1cm47IH1cclxuXHJcblx0XHR2YXIgdHlwZSA9IGUudHlwZSA9PT0gJ2tleXByZXNzJyAmJiBlLmtleUNvZGUgPT09IDEzID8gJ2NsaWNrJyA6IGUudHlwZTtcclxuXHJcblx0XHRpZiAodHlwZSA9PT0gJ21vdXNlZG93bicpIHtcclxuXHRcdFx0Ly8gcHJldmVudHMgb3V0bGluZSB3aGVuIGNsaWNraW5nIG9uIGtleWJvYXJkLWZvY3VzYWJsZSBlbGVtZW50XHJcblx0XHRcdEwuRG9tVXRpbC5wcmV2ZW50T3V0bGluZShlLnRhcmdldCB8fCBlLnNyY0VsZW1lbnQpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX2ZpcmVET01FdmVudChlLCB0eXBlKTtcclxuXHR9LFxyXG5cclxuXHRfZmlyZURPTUV2ZW50OiBmdW5jdGlvbiAoZSwgdHlwZSwgdGFyZ2V0cykge1xyXG5cclxuXHRcdGlmIChlLnR5cGUgPT09ICdjbGljaycpIHtcclxuXHRcdFx0Ly8gRmlyZSBhIHN5bnRoZXRpYyAncHJlY2xpY2snIGV2ZW50IHdoaWNoIHByb3BhZ2F0ZXMgdXAgKG1haW5seSBmb3IgY2xvc2luZyBwb3B1cHMpLlxyXG5cdFx0XHQvLyBAZXZlbnQgcHJlY2xpY2s6IE1vdXNlRXZlbnRcclxuXHRcdFx0Ly8gRmlyZWQgYmVmb3JlIG1vdXNlIGNsaWNrIG9uIHRoZSBtYXAgKHNvbWV0aW1lcyB1c2VmdWwgd2hlbiB5b3VcclxuXHRcdFx0Ly8gd2FudCBzb21ldGhpbmcgdG8gaGFwcGVuIG9uIGNsaWNrIGJlZm9yZSBhbnkgZXhpc3RpbmcgY2xpY2tcclxuXHRcdFx0Ly8gaGFuZGxlcnMgc3RhcnQgcnVubmluZykuXHJcblx0XHRcdHZhciBzeW50aCA9IEwuVXRpbC5leHRlbmQoe30sIGUpO1xyXG5cdFx0XHRzeW50aC50eXBlID0gJ3ByZWNsaWNrJztcclxuXHRcdFx0dGhpcy5fZmlyZURPTUV2ZW50KHN5bnRoLCBzeW50aC50eXBlLCB0YXJnZXRzKTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAoZS5fc3RvcHBlZCkgeyByZXR1cm47IH1cclxuXHJcblx0XHQvLyBGaW5kIHRoZSBsYXllciB0aGUgZXZlbnQgaXMgcHJvcGFnYXRpbmcgZnJvbSBhbmQgaXRzIHBhcmVudHMuXHJcblx0XHR0YXJnZXRzID0gKHRhcmdldHMgfHwgW10pLmNvbmNhdCh0aGlzLl9maW5kRXZlbnRUYXJnZXRzKGUsIHR5cGUpKTtcclxuXHJcblx0XHRpZiAoIXRhcmdldHMubGVuZ3RoKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdHZhciB0YXJnZXQgPSB0YXJnZXRzWzBdO1xyXG5cdFx0aWYgKHR5cGUgPT09ICdjb250ZXh0bWVudScgJiYgdGFyZ2V0Lmxpc3RlbnModHlwZSwgdHJ1ZSkpIHtcclxuXHRcdFx0TC5Eb21FdmVudC5wcmV2ZW50RGVmYXVsdChlKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgZGF0YSA9IHtcclxuXHRcdFx0b3JpZ2luYWxFdmVudDogZVxyXG5cdFx0fTtcclxuXHJcblx0XHRpZiAoZS50eXBlICE9PSAna2V5cHJlc3MnKSB7XHJcblx0XHRcdHZhciBpc01hcmtlciA9IHRhcmdldCBpbnN0YW5jZW9mIEwuTWFya2VyO1xyXG5cdFx0XHRkYXRhLmNvbnRhaW5lclBvaW50ID0gaXNNYXJrZXIgP1xyXG5cdFx0XHRcdFx0dGhpcy5sYXRMbmdUb0NvbnRhaW5lclBvaW50KHRhcmdldC5nZXRMYXRMbmcoKSkgOiB0aGlzLm1vdXNlRXZlbnRUb0NvbnRhaW5lclBvaW50KGUpO1xyXG5cdFx0XHRkYXRhLmxheWVyUG9pbnQgPSB0aGlzLmNvbnRhaW5lclBvaW50VG9MYXllclBvaW50KGRhdGEuY29udGFpbmVyUG9pbnQpO1xyXG5cdFx0XHRkYXRhLmxhdGxuZyA9IGlzTWFya2VyID8gdGFyZ2V0LmdldExhdExuZygpIDogdGhpcy5sYXllclBvaW50VG9MYXRMbmcoZGF0YS5sYXllclBvaW50KTtcclxuXHRcdH1cclxuXHJcblx0XHRmb3IgKHZhciBpID0gMDsgaSA8IHRhcmdldHMubGVuZ3RoOyBpKyspIHtcclxuXHRcdFx0dGFyZ2V0c1tpXS5maXJlKHR5cGUsIGRhdGEsIHRydWUpO1xyXG5cdFx0XHRpZiAoZGF0YS5vcmlnaW5hbEV2ZW50Ll9zdG9wcGVkIHx8XHJcblx0XHRcdFx0KHRhcmdldHNbaV0ub3B0aW9ucy5ub25CdWJibGluZ0V2ZW50cyAmJiBMLlV0aWwuaW5kZXhPZih0YXJnZXRzW2ldLm9wdGlvbnMubm9uQnViYmxpbmdFdmVudHMsIHR5cGUpICE9PSAtMSkpIHsgcmV0dXJuOyB9XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2RyYWdnYWJsZU1vdmVkOiBmdW5jdGlvbiAob2JqKSB7XHJcblx0XHRvYmogPSBvYmouZHJhZ2dpbmcgJiYgb2JqLmRyYWdnaW5nLmVuYWJsZWQoKSA/IG9iaiA6IHRoaXM7XHJcblx0XHRyZXR1cm4gKG9iai5kcmFnZ2luZyAmJiBvYmouZHJhZ2dpbmcubW92ZWQoKSkgfHwgKHRoaXMuYm94Wm9vbSAmJiB0aGlzLmJveFpvb20ubW92ZWQoKSk7XHJcblx0fSxcclxuXHJcblx0X2NsZWFySGFuZGxlcnM6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSB0aGlzLl9oYW5kbGVycy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHR0aGlzLl9oYW5kbGVyc1tpXS5kaXNhYmxlKCk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQHNlY3Rpb24gT3RoZXIgTWV0aG9kc1xyXG5cclxuXHQvLyBAbWV0aG9kIHdoZW5SZWFkeShmbjogRnVuY3Rpb24sIGNvbnRleHQ/OiBPYmplY3QpOiB0aGlzXHJcblx0Ly8gUnVucyB0aGUgZ2l2ZW4gZnVuY3Rpb24gYGZuYCB3aGVuIHRoZSBtYXAgZ2V0cyBpbml0aWFsaXplZCB3aXRoXHJcblx0Ly8gYSB2aWV3IChjZW50ZXIgYW5kIHpvb20pIGFuZCBhdCBsZWFzdCBvbmUgbGF5ZXIsIG9yIGltbWVkaWF0ZWx5XHJcblx0Ly8gaWYgaXQncyBhbHJlYWR5IGluaXRpYWxpemVkLCBvcHRpb25hbGx5IHBhc3NpbmcgYSBmdW5jdGlvbiBjb250ZXh0LlxyXG5cdHdoZW5SZWFkeTogZnVuY3Rpb24gKGNhbGxiYWNrLCBjb250ZXh0KSB7XHJcblx0XHRpZiAodGhpcy5fbG9hZGVkKSB7XHJcblx0XHRcdGNhbGxiYWNrLmNhbGwoY29udGV4dCB8fCB0aGlzLCB7dGFyZ2V0OiB0aGlzfSk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHR0aGlzLm9uKCdsb2FkJywgY2FsbGJhY2ssIGNvbnRleHQpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblxyXG5cdC8vIHByaXZhdGUgbWV0aG9kcyBmb3IgZ2V0dGluZyBtYXAgc3RhdGVcclxuXHJcblx0X2dldE1hcFBhbmVQb3M6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiBMLkRvbVV0aWwuZ2V0UG9zaXRpb24odGhpcy5fbWFwUGFuZSkgfHwgbmV3IEwuUG9pbnQoMCwgMCk7XHJcblx0fSxcclxuXHJcblx0X21vdmVkOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgcG9zID0gdGhpcy5fZ2V0TWFwUGFuZVBvcygpO1xyXG5cdFx0cmV0dXJuIHBvcyAmJiAhcG9zLmVxdWFscyhbMCwgMF0pO1xyXG5cdH0sXHJcblxyXG5cdF9nZXRUb3BMZWZ0UG9pbnQ6IGZ1bmN0aW9uIChjZW50ZXIsIHpvb20pIHtcclxuXHRcdHZhciBwaXhlbE9yaWdpbiA9IGNlbnRlciAmJiB6b29tICE9PSB1bmRlZmluZWQgP1xyXG5cdFx0XHR0aGlzLl9nZXROZXdQaXhlbE9yaWdpbihjZW50ZXIsIHpvb20pIDpcclxuXHRcdFx0dGhpcy5nZXRQaXhlbE9yaWdpbigpO1xyXG5cdFx0cmV0dXJuIHBpeGVsT3JpZ2luLnN1YnRyYWN0KHRoaXMuX2dldE1hcFBhbmVQb3MoKSk7XHJcblx0fSxcclxuXHJcblx0X2dldE5ld1BpeGVsT3JpZ2luOiBmdW5jdGlvbiAoY2VudGVyLCB6b29tKSB7XHJcblx0XHR2YXIgdmlld0hhbGYgPSB0aGlzLmdldFNpemUoKS5fZGl2aWRlQnkoMik7XHJcblx0XHRyZXR1cm4gdGhpcy5wcm9qZWN0KGNlbnRlciwgem9vbSkuX3N1YnRyYWN0KHZpZXdIYWxmKS5fYWRkKHRoaXMuX2dldE1hcFBhbmVQb3MoKSkuX3JvdW5kKCk7XHJcblx0fSxcclxuXHJcblx0X2xhdExuZ1RvTmV3TGF5ZXJQb2ludDogZnVuY3Rpb24gKGxhdGxuZywgem9vbSwgY2VudGVyKSB7XHJcblx0XHR2YXIgdG9wTGVmdCA9IHRoaXMuX2dldE5ld1BpeGVsT3JpZ2luKGNlbnRlciwgem9vbSk7XHJcblx0XHRyZXR1cm4gdGhpcy5wcm9qZWN0KGxhdGxuZywgem9vbSkuX3N1YnRyYWN0KHRvcExlZnQpO1xyXG5cdH0sXHJcblxyXG5cdF9sYXRMbmdCb3VuZHNUb05ld0xheWVyQm91bmRzOiBmdW5jdGlvbiAobGF0TG5nQm91bmRzLCB6b29tLCBjZW50ZXIpIHtcclxuXHRcdHZhciB0b3BMZWZ0ID0gdGhpcy5fZ2V0TmV3UGl4ZWxPcmlnaW4oY2VudGVyLCB6b29tKTtcclxuXHRcdHJldHVybiBMLmJvdW5kcyhbXHJcblx0XHRcdHRoaXMucHJvamVjdChsYXRMbmdCb3VuZHMuZ2V0U291dGhXZXN0KCksIHpvb20pLl9zdWJ0cmFjdCh0b3BMZWZ0KSxcclxuXHRcdFx0dGhpcy5wcm9qZWN0KGxhdExuZ0JvdW5kcy5nZXROb3J0aFdlc3QoKSwgem9vbSkuX3N1YnRyYWN0KHRvcExlZnQpLFxyXG5cdFx0XHR0aGlzLnByb2plY3QobGF0TG5nQm91bmRzLmdldFNvdXRoRWFzdCgpLCB6b29tKS5fc3VidHJhY3QodG9wTGVmdCksXHJcblx0XHRcdHRoaXMucHJvamVjdChsYXRMbmdCb3VuZHMuZ2V0Tm9ydGhFYXN0KCksIHpvb20pLl9zdWJ0cmFjdCh0b3BMZWZ0KVxyXG5cdFx0XSk7XHJcblx0fSxcclxuXHJcblx0Ly8gbGF5ZXIgcG9pbnQgb2YgdGhlIGN1cnJlbnQgY2VudGVyXHJcblx0X2dldENlbnRlckxheWVyUG9pbnQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLmNvbnRhaW5lclBvaW50VG9MYXllclBvaW50KHRoaXMuZ2V0U2l6ZSgpLl9kaXZpZGVCeSgyKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gb2Zmc2V0IG9mIHRoZSBzcGVjaWZpZWQgcGxhY2UgdG8gdGhlIGN1cnJlbnQgY2VudGVyIGluIHBpeGVsc1xyXG5cdF9nZXRDZW50ZXJPZmZzZXQ6IGZ1bmN0aW9uIChsYXRsbmcpIHtcclxuXHRcdHJldHVybiB0aGlzLmxhdExuZ1RvTGF5ZXJQb2ludChsYXRsbmcpLnN1YnRyYWN0KHRoaXMuX2dldENlbnRlckxheWVyUG9pbnQoKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gYWRqdXN0IGNlbnRlciBmb3IgdmlldyB0byBnZXQgaW5zaWRlIGJvdW5kc1xyXG5cdF9saW1pdENlbnRlcjogZnVuY3Rpb24gKGNlbnRlciwgem9vbSwgYm91bmRzKSB7XHJcblxyXG5cdFx0aWYgKCFib3VuZHMpIHsgcmV0dXJuIGNlbnRlcjsgfVxyXG5cclxuXHRcdHZhciBjZW50ZXJQb2ludCA9IHRoaXMucHJvamVjdChjZW50ZXIsIHpvb20pLFxyXG5cdFx0ICAgIHZpZXdIYWxmID0gdGhpcy5nZXRTaXplKCkuZGl2aWRlQnkoMiksXHJcblx0XHQgICAgdmlld0JvdW5kcyA9IG5ldyBMLkJvdW5kcyhjZW50ZXJQb2ludC5zdWJ0cmFjdCh2aWV3SGFsZiksIGNlbnRlclBvaW50LmFkZCh2aWV3SGFsZikpLFxyXG5cdFx0ICAgIG9mZnNldCA9IHRoaXMuX2dldEJvdW5kc09mZnNldCh2aWV3Qm91bmRzLCBib3VuZHMsIHpvb20pO1xyXG5cclxuXHRcdC8vIElmIG9mZnNldCBpcyBsZXNzIHRoYW4gYSBwaXhlbCwgaWdub3JlLlxyXG5cdFx0Ly8gVGhpcyBwcmV2ZW50cyB1bnN0YWJsZSBwcm9qZWN0aW9ucyBmcm9tIGdldHRpbmcgaW50b1xyXG5cdFx0Ly8gYW4gaW5maW5pdGUgbG9vcCBvZiB0aW55IG9mZnNldHMuXHJcblx0XHRpZiAob2Zmc2V0LnJvdW5kKCkuZXF1YWxzKFswLCAwXSkpIHtcclxuXHRcdFx0cmV0dXJuIGNlbnRlcjtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcy51bnByb2plY3QoY2VudGVyUG9pbnQuYWRkKG9mZnNldCksIHpvb20pO1xyXG5cdH0sXHJcblxyXG5cdC8vIGFkanVzdCBvZmZzZXQgZm9yIHZpZXcgdG8gZ2V0IGluc2lkZSBib3VuZHNcclxuXHRfbGltaXRPZmZzZXQ6IGZ1bmN0aW9uIChvZmZzZXQsIGJvdW5kcykge1xyXG5cdFx0aWYgKCFib3VuZHMpIHsgcmV0dXJuIG9mZnNldDsgfVxyXG5cclxuXHRcdHZhciB2aWV3Qm91bmRzID0gdGhpcy5nZXRQaXhlbEJvdW5kcygpLFxyXG5cdFx0ICAgIG5ld0JvdW5kcyA9IG5ldyBMLkJvdW5kcyh2aWV3Qm91bmRzLm1pbi5hZGQob2Zmc2V0KSwgdmlld0JvdW5kcy5tYXguYWRkKG9mZnNldCkpO1xyXG5cclxuXHRcdHJldHVybiBvZmZzZXQuYWRkKHRoaXMuX2dldEJvdW5kc09mZnNldChuZXdCb3VuZHMsIGJvdW5kcykpO1xyXG5cdH0sXHJcblxyXG5cdC8vIHJldHVybnMgb2Zmc2V0IG5lZWRlZCBmb3IgcHhCb3VuZHMgdG8gZ2V0IGluc2lkZSBtYXhCb3VuZHMgYXQgYSBzcGVjaWZpZWQgem9vbVxyXG5cdF9nZXRCb3VuZHNPZmZzZXQ6IGZ1bmN0aW9uIChweEJvdW5kcywgbWF4Qm91bmRzLCB6b29tKSB7XHJcblx0XHR2YXIgcHJvamVjdGVkTWF4Qm91bmRzID0gTC5ib3VuZHMoXHJcblx0XHQgICAgICAgIHRoaXMucHJvamVjdChtYXhCb3VuZHMuZ2V0Tm9ydGhFYXN0KCksIHpvb20pLFxyXG5cdFx0ICAgICAgICB0aGlzLnByb2plY3QobWF4Qm91bmRzLmdldFNvdXRoV2VzdCgpLCB6b29tKVxyXG5cdFx0ICAgICksXHJcblx0XHQgICAgbWluT2Zmc2V0ID0gcHJvamVjdGVkTWF4Qm91bmRzLm1pbi5zdWJ0cmFjdChweEJvdW5kcy5taW4pLFxyXG5cdFx0ICAgIG1heE9mZnNldCA9IHByb2plY3RlZE1heEJvdW5kcy5tYXguc3VidHJhY3QocHhCb3VuZHMubWF4KSxcclxuXHJcblx0XHQgICAgZHggPSB0aGlzLl9yZWJvdW5kKG1pbk9mZnNldC54LCAtbWF4T2Zmc2V0LngpLFxyXG5cdFx0ICAgIGR5ID0gdGhpcy5fcmVib3VuZChtaW5PZmZzZXQueSwgLW1heE9mZnNldC55KTtcclxuXHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQoZHgsIGR5KTtcclxuXHR9LFxyXG5cclxuXHRfcmVib3VuZDogZnVuY3Rpb24gKGxlZnQsIHJpZ2h0KSB7XHJcblx0XHRyZXR1cm4gbGVmdCArIHJpZ2h0ID4gMCA/XHJcblx0XHRcdE1hdGgucm91bmQobGVmdCAtIHJpZ2h0KSAvIDIgOlxyXG5cdFx0XHRNYXRoLm1heCgwLCBNYXRoLmNlaWwobGVmdCkpIC0gTWF0aC5tYXgoMCwgTWF0aC5mbG9vcihyaWdodCkpO1xyXG5cdH0sXHJcblxyXG5cdF9saW1pdFpvb206IGZ1bmN0aW9uICh6b29tKSB7XHJcblx0XHR2YXIgbWluID0gdGhpcy5nZXRNaW5ab29tKCksXHJcblx0XHQgICAgbWF4ID0gdGhpcy5nZXRNYXhab29tKCksXHJcblx0XHQgICAgc25hcCA9IEwuQnJvd3Nlci5hbnkzZCA/IHRoaXMub3B0aW9ucy56b29tU25hcCA6IDE7XHJcblx0XHRpZiAoc25hcCkge1xyXG5cdFx0XHR6b29tID0gTWF0aC5yb3VuZCh6b29tIC8gc25hcCkgKiBzbmFwO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIE1hdGgubWF4KG1pbiwgTWF0aC5taW4obWF4LCB6b29tKSk7XHJcblx0fSxcclxuXHJcblx0X29uUGFuVHJhbnNpdGlvblN0ZXA6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHRoaXMuZmlyZSgnbW92ZScpO1xyXG5cdH0sXHJcblxyXG5cdF9vblBhblRyYW5zaXRpb25FbmQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9tYXBQYW5lLCAnbGVhZmxldC1wYW4tYW5pbScpO1xyXG5cdFx0dGhpcy5maXJlKCdtb3ZlZW5kJyk7XHJcblx0fSxcclxuXHJcblx0X3RyeUFuaW1hdGVkUGFuOiBmdW5jdGlvbiAoY2VudGVyLCBvcHRpb25zKSB7XHJcblx0XHQvLyBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIG5ldyBhbmQgY3VycmVudCBjZW50ZXJzIGluIHBpeGVsc1xyXG5cdFx0dmFyIG9mZnNldCA9IHRoaXMuX2dldENlbnRlck9mZnNldChjZW50ZXIpLl9mbG9vcigpO1xyXG5cclxuXHRcdC8vIGRvbid0IGFuaW1hdGUgdG9vIGZhciB1bmxlc3MgYW5pbWF0ZTogdHJ1ZSBzcGVjaWZpZWQgaW4gb3B0aW9uc1xyXG5cdFx0aWYgKChvcHRpb25zICYmIG9wdGlvbnMuYW5pbWF0ZSkgIT09IHRydWUgJiYgIXRoaXMuZ2V0U2l6ZSgpLmNvbnRhaW5zKG9mZnNldCkpIHsgcmV0dXJuIGZhbHNlOyB9XHJcblxyXG5cdFx0dGhpcy5wYW5CeShvZmZzZXQsIG9wdGlvbnMpO1xyXG5cclxuXHRcdHJldHVybiB0cnVlO1xyXG5cdH0sXHJcblxyXG5cdF9jcmVhdGVBbmltUHJveHk6IGZ1bmN0aW9uICgpIHtcclxuXHJcblx0XHR2YXIgcHJveHkgPSB0aGlzLl9wcm94eSA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LXByb3h5IGxlYWZsZXQtem9vbS1hbmltYXRlZCcpO1xyXG5cdFx0dGhpcy5fcGFuZXMubWFwUGFuZS5hcHBlbmRDaGlsZChwcm94eSk7XHJcblxyXG5cdFx0dGhpcy5vbignem9vbWFuaW0nLCBmdW5jdGlvbiAoZSkge1xyXG5cdFx0XHR2YXIgcHJvcCA9IEwuRG9tVXRpbC5UUkFOU0ZPUk0sXHJcblx0XHRcdCAgICB0cmFuc2Zvcm0gPSBwcm94eS5zdHlsZVtwcm9wXTtcclxuXHJcblx0XHRcdEwuRG9tVXRpbC5zZXRUcmFuc2Zvcm0ocHJveHksIHRoaXMucHJvamVjdChlLmNlbnRlciwgZS56b29tKSwgdGhpcy5nZXRab29tU2NhbGUoZS56b29tLCAxKSk7XHJcblxyXG5cdFx0XHQvLyB3b3JrYXJvdW5kIGZvciBjYXNlIHdoZW4gdHJhbnNmb3JtIGlzIHRoZSBzYW1lIGFuZCBzbyB0cmFuc2l0aW9uZW5kIGV2ZW50IGlzIG5vdCBmaXJlZFxyXG5cdFx0XHRpZiAodHJhbnNmb3JtID09PSBwcm94eS5zdHlsZVtwcm9wXSAmJiB0aGlzLl9hbmltYXRpbmdab29tKSB7XHJcblx0XHRcdFx0dGhpcy5fb25ab29tVHJhbnNpdGlvbkVuZCgpO1xyXG5cdFx0XHR9XHJcblx0XHR9LCB0aGlzKTtcclxuXHJcblx0XHR0aGlzLm9uKCdsb2FkIG1vdmVlbmQnLCBmdW5jdGlvbiAoKSB7XHJcblx0XHRcdHZhciBjID0gdGhpcy5nZXRDZW50ZXIoKSxcclxuXHRcdFx0ICAgIHogPSB0aGlzLmdldFpvb20oKTtcclxuXHRcdFx0TC5Eb21VdGlsLnNldFRyYW5zZm9ybShwcm94eSwgdGhpcy5wcm9qZWN0KGMsIHopLCB0aGlzLmdldFpvb21TY2FsZSh6LCAxKSk7XHJcblx0XHR9LCB0aGlzKTtcclxuXHR9LFxyXG5cclxuXHRfY2F0Y2hUcmFuc2l0aW9uRW5kOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0aWYgKHRoaXMuX2FuaW1hdGluZ1pvb20gJiYgZS5wcm9wZXJ0eU5hbWUuaW5kZXhPZigndHJhbnNmb3JtJykgPj0gMCkge1xyXG5cdFx0XHR0aGlzLl9vblpvb21UcmFuc2l0aW9uRW5kKCk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X25vdGhpbmdUb0FuaW1hdGU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiAhdGhpcy5fY29udGFpbmVyLmdldEVsZW1lbnRzQnlDbGFzc05hbWUoJ2xlYWZsZXQtem9vbS1hbmltYXRlZCcpLmxlbmd0aDtcclxuXHR9LFxyXG5cclxuXHRfdHJ5QW5pbWF0ZWRab29tOiBmdW5jdGlvbiAoY2VudGVyLCB6b29tLCBvcHRpb25zKSB7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2FuaW1hdGluZ1pvb20pIHsgcmV0dXJuIHRydWU7IH1cclxuXHJcblx0XHRvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcclxuXHJcblx0XHQvLyBkb24ndCBhbmltYXRlIGlmIGRpc2FibGVkLCBub3Qgc3VwcG9ydGVkIG9yIHpvb20gZGlmZmVyZW5jZSBpcyB0b28gbGFyZ2VcclxuXHRcdGlmICghdGhpcy5fem9vbUFuaW1hdGVkIHx8IG9wdGlvbnMuYW5pbWF0ZSA9PT0gZmFsc2UgfHwgdGhpcy5fbm90aGluZ1RvQW5pbWF0ZSgpIHx8XHJcblx0XHQgICAgICAgIE1hdGguYWJzKHpvb20gLSB0aGlzLl96b29tKSA+IHRoaXMub3B0aW9ucy56b29tQW5pbWF0aW9uVGhyZXNob2xkKSB7IHJldHVybiBmYWxzZTsgfVxyXG5cclxuXHRcdC8vIG9mZnNldCBpcyB0aGUgcGl4ZWwgY29vcmRzIG9mIHRoZSB6b29tIG9yaWdpbiByZWxhdGl2ZSB0byB0aGUgY3VycmVudCBjZW50ZXJcclxuXHRcdHZhciBzY2FsZSA9IHRoaXMuZ2V0Wm9vbVNjYWxlKHpvb20pLFxyXG5cdFx0ICAgIG9mZnNldCA9IHRoaXMuX2dldENlbnRlck9mZnNldChjZW50ZXIpLl9kaXZpZGVCeSgxIC0gMSAvIHNjYWxlKTtcclxuXHJcblx0XHQvLyBkb24ndCBhbmltYXRlIGlmIHRoZSB6b29tIG9yaWdpbiBpc24ndCB3aXRoaW4gb25lIHNjcmVlbiBmcm9tIHRoZSBjdXJyZW50IGNlbnRlciwgdW5sZXNzIGZvcmNlZFxyXG5cdFx0aWYgKG9wdGlvbnMuYW5pbWF0ZSAhPT0gdHJ1ZSAmJiAhdGhpcy5nZXRTaXplKCkuY29udGFpbnMob2Zmc2V0KSkgeyByZXR1cm4gZmFsc2U7IH1cclxuXHJcblx0XHRMLlV0aWwucmVxdWVzdEFuaW1GcmFtZShmdW5jdGlvbiAoKSB7XHJcblx0XHRcdHRoaXNcclxuXHRcdFx0ICAgIC5fbW92ZVN0YXJ0KHRydWUpXHJcblx0XHRcdCAgICAuX2FuaW1hdGVab29tKGNlbnRlciwgem9vbSwgdHJ1ZSk7XHJcblx0XHR9LCB0aGlzKTtcclxuXHJcblx0XHRyZXR1cm4gdHJ1ZTtcclxuXHR9LFxyXG5cclxuXHRfYW5pbWF0ZVpvb206IGZ1bmN0aW9uIChjZW50ZXIsIHpvb20sIHN0YXJ0QW5pbSwgbm9VcGRhdGUpIHtcclxuXHRcdGlmIChzdGFydEFuaW0pIHtcclxuXHRcdFx0dGhpcy5fYW5pbWF0aW5nWm9vbSA9IHRydWU7XHJcblxyXG5cdFx0XHQvLyByZW1lbWJlciB3aGF0IGNlbnRlci96b29tIHRvIHNldCBhZnRlciBhbmltYXRpb25cclxuXHRcdFx0dGhpcy5fYW5pbWF0ZVRvQ2VudGVyID0gY2VudGVyO1xyXG5cdFx0XHR0aGlzLl9hbmltYXRlVG9ab29tID0gem9vbTtcclxuXHJcblx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9tYXBQYW5lLCAnbGVhZmxldC16b29tLWFuaW0nKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBAZXZlbnQgem9vbWFuaW06IFpvb21BbmltRXZlbnRcclxuXHRcdC8vIEZpcmVkIG9uIGV2ZXJ5IGZyYW1lIG9mIGEgem9vbSBhbmltYXRpb25cclxuXHRcdHRoaXMuZmlyZSgnem9vbWFuaW0nLCB7XHJcblx0XHRcdGNlbnRlcjogY2VudGVyLFxyXG5cdFx0XHR6b29tOiB6b29tLFxyXG5cdFx0XHRub1VwZGF0ZTogbm9VcGRhdGVcclxuXHRcdH0pO1xyXG5cclxuXHRcdC8vIFdvcmsgYXJvdW5kIHdlYmtpdCBub3QgZmlyaW5nICd0cmFuc2l0aW9uZW5kJywgc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9MZWFmbGV0L0xlYWZsZXQvaXNzdWVzLzM2ODksIDI2OTNcclxuXHRcdHNldFRpbWVvdXQoTC5iaW5kKHRoaXMuX29uWm9vbVRyYW5zaXRpb25FbmQsIHRoaXMpLCAyNTApO1xyXG5cdH0sXHJcblxyXG5cdF9vblpvb21UcmFuc2l0aW9uRW5kOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAoIXRoaXMuX2FuaW1hdGluZ1pvb20pIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0TC5Eb21VdGlsLnJlbW92ZUNsYXNzKHRoaXMuX21hcFBhbmUsICdsZWFmbGV0LXpvb20tYW5pbScpO1xyXG5cclxuXHRcdHRoaXMuX2FuaW1hdGluZ1pvb20gPSBmYWxzZTtcclxuXHJcblx0XHR0aGlzLl9tb3ZlKHRoaXMuX2FuaW1hdGVUb0NlbnRlciwgdGhpcy5fYW5pbWF0ZVRvWm9vbSk7XHJcblxyXG5cdFx0Ly8gVGhpcyBhbmltIGZyYW1lIHNob3VsZCBwcmV2ZW50IGFuIG9ic2N1cmUgaU9TIHdlYmtpdCB0aWxlIGxvYWRpbmcgcmFjZSBjb25kaXRpb24uXHJcblx0XHRMLlV0aWwucmVxdWVzdEFuaW1GcmFtZShmdW5jdGlvbiAoKSB7XHJcblx0XHRcdHRoaXMuX21vdmVFbmQodHJ1ZSk7XHJcblx0XHR9LCB0aGlzKTtcclxuXHR9XHJcbn0pO1xyXG5cclxuLy8gQHNlY3Rpb25cclxuXHJcbi8vIEBmYWN0b3J5IEwubWFwKGlkOiBTdHJpbmcsIG9wdGlvbnM/OiBNYXAgb3B0aW9ucylcclxuLy8gSW5zdGFudGlhdGVzIGEgbWFwIG9iamVjdCBnaXZlbiB0aGUgRE9NIElEIG9mIGEgYDxkaXY+YCBlbGVtZW50XHJcbi8vIGFuZCBvcHRpb25hbGx5IGFuIG9iamVjdCBsaXRlcmFsIHdpdGggYE1hcCBvcHRpb25zYC5cclxuLy9cclxuLy8gQGFsdGVybmF0aXZlXHJcbi8vIEBmYWN0b3J5IEwubWFwKGVsOiBIVE1MRWxlbWVudCwgb3B0aW9ucz86IE1hcCBvcHRpb25zKVxyXG4vLyBJbnN0YW50aWF0ZXMgYSBtYXAgb2JqZWN0IGdpdmVuIGFuIGluc3RhbmNlIG9mIGEgYDxkaXY+YCBIVE1MIGVsZW1lbnRcclxuLy8gYW5kIG9wdGlvbmFsbHkgYW4gb2JqZWN0IGxpdGVyYWwgd2l0aCBgTWFwIG9wdGlvbnNgLlxyXG5MLm1hcCA9IGZ1bmN0aW9uIChpZCwgb3B0aW9ucykge1xyXG5cdHJldHVybiBuZXcgTC5NYXAoaWQsIG9wdGlvbnMpO1xyXG59O1xyXG5cblxuXG5cbi8qXG4gKiBAY2xhc3MgTGF5ZXJcbiAqIEBpbmhlcml0cyBFdmVudGVkXG4gKiBAYWthIEwuTGF5ZXJcbiAqIEBha2EgSUxheWVyXG4gKlxuICogQSBzZXQgb2YgbWV0aG9kcyBmcm9tIHRoZSBMYXllciBiYXNlIGNsYXNzIHRoYXQgYWxsIExlYWZsZXQgbGF5ZXJzIHVzZS5cbiAqIEluaGVyaXRzIGFsbCBtZXRob2RzLCBvcHRpb25zIGFuZCBldmVudHMgZnJvbSBgTC5FdmVudGVkYC5cbiAqXG4gKiBAZXhhbXBsZVxuICpcbiAqIGBgYGpzXG4gKiB2YXIgbGF5ZXIgPSBMLk1hcmtlcihsYXRsbmcpLmFkZFRvKG1hcCk7XG4gKiBsYXllci5hZGRUbyhtYXApO1xuICogbGF5ZXIucmVtb3ZlKCk7XG4gKiBgYGBcbiAqXG4gKiBAZXZlbnQgYWRkOiBFdmVudFxuICogRmlyZWQgYWZ0ZXIgdGhlIGxheWVyIGlzIGFkZGVkIHRvIGEgbWFwXG4gKlxuICogQGV2ZW50IHJlbW92ZTogRXZlbnRcbiAqIEZpcmVkIGFmdGVyIHRoZSBsYXllciBpcyByZW1vdmVkIGZyb20gYSBtYXBcbiAqL1xuXG5cbkwuTGF5ZXIgPSBMLkV2ZW50ZWQuZXh0ZW5kKHtcblxuXHQvLyBDbGFzc2VzIGV4dGVuZGluZyBgTC5MYXllcmAgd2lsbCBpbmhlcml0IHRoZSBmb2xsb3dpbmcgb3B0aW9uczpcblx0b3B0aW9uczoge1xuXHRcdC8vIEBvcHRpb24gcGFuZTogU3RyaW5nID0gJ292ZXJsYXlQYW5lJ1xuXHRcdC8vIEJ5IGRlZmF1bHQgdGhlIGxheWVyIHdpbGwgYmUgYWRkZWQgdG8gdGhlIG1hcCdzIFtvdmVybGF5IHBhbmVdKCNtYXAtb3ZlcmxheXBhbmUpLiBPdmVycmlkaW5nIHRoaXMgb3B0aW9uIHdpbGwgY2F1c2UgdGhlIGxheWVyIHRvIGJlIHBsYWNlZCBvbiBhbm90aGVyIHBhbmUgYnkgZGVmYXVsdC5cblx0XHRwYW5lOiAnb3ZlcmxheVBhbmUnLFxuXHRcdG5vbkJ1YmJsaW5nRXZlbnRzOiBbXSwgIC8vIEFycmF5IG9mIGV2ZW50cyB0aGF0IHNob3VsZCBub3QgYmUgYnViYmxlZCB0byBET00gcGFyZW50cyAobGlrZSB0aGUgbWFwKSxcblxuXHRcdC8vIEBvcHRpb24gYXR0cmlidXRpb246IFN0cmluZyA9IG51bGxcblx0XHQvLyBTdHJpbmcgdG8gYmUgc2hvd24gaW4gdGhlIGF0dHJpYnV0aW9uIGNvbnRyb2wsIGRlc2NyaWJlcyB0aGUgbGF5ZXIgZGF0YSwgZS5nLiBcIsKpIE1hcGJveFwiLlxuXHRcdGF0dHJpYnV0aW9uOiBudWxsLFxuXHR9LFxuXG5cdC8qIEBzZWN0aW9uXG5cdCAqIENsYXNzZXMgZXh0ZW5kaW5nIGBMLkxheWVyYCB3aWxsIGluaGVyaXQgdGhlIGZvbGxvd2luZyBtZXRob2RzOlxuXHQgKlxuXHQgKiBAbWV0aG9kIGFkZFRvKG1hcDogTWFwKTogdGhpc1xuXHQgKiBBZGRzIHRoZSBsYXllciB0byB0aGUgZ2l2ZW4gbWFwXG5cdCAqL1xuXHRhZGRUbzogZnVuY3Rpb24gKG1hcCkge1xuXHRcdG1hcC5hZGRMYXllcih0aGlzKTtcblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIHJlbW92ZTogdGhpc1xuXHQvLyBSZW1vdmVzIHRoZSBsYXllciBmcm9tIHRoZSBtYXAgaXQgaXMgY3VycmVudGx5IGFjdGl2ZSBvbi5cblx0cmVtb3ZlOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMucmVtb3ZlRnJvbSh0aGlzLl9tYXAgfHwgdGhpcy5fbWFwVG9BZGQpO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgcmVtb3ZlRnJvbShtYXA6IE1hcCk6IHRoaXNcblx0Ly8gUmVtb3ZlcyB0aGUgbGF5ZXIgZnJvbSB0aGUgZ2l2ZW4gbWFwXG5cdHJlbW92ZUZyb206IGZ1bmN0aW9uIChvYmopIHtcblx0XHRpZiAob2JqKSB7XG5cdFx0XHRvYmoucmVtb3ZlTGF5ZXIodGhpcyk7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgZ2V0UGFuZShuYW1lPyA6IFN0cmluZyk6IEhUTUxFbGVtZW50XG5cdC8vIFJldHVybnMgdGhlIGBIVE1MRWxlbWVudGAgcmVwcmVzZW50aW5nIHRoZSBuYW1lZCBwYW5lIG9uIHRoZSBtYXAuIElmIGBuYW1lYCBpcyBvbWl0dGVkLCByZXR1cm5zIHRoZSBwYW5lIGZvciB0aGlzIGxheWVyLlxuXHRnZXRQYW5lOiBmdW5jdGlvbiAobmFtZSkge1xuXHRcdHJldHVybiB0aGlzLl9tYXAuZ2V0UGFuZShuYW1lID8gKHRoaXMub3B0aW9uc1tuYW1lXSB8fCBuYW1lKSA6IHRoaXMub3B0aW9ucy5wYW5lKTtcblx0fSxcblxuXHRhZGRJbnRlcmFjdGl2ZVRhcmdldDogZnVuY3Rpb24gKHRhcmdldEVsKSB7XG5cdFx0dGhpcy5fbWFwLl90YXJnZXRzW0wuc3RhbXAodGFyZ2V0RWwpXSA9IHRoaXM7XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0cmVtb3ZlSW50ZXJhY3RpdmVUYXJnZXQ6IGZ1bmN0aW9uICh0YXJnZXRFbCkge1xuXHRcdGRlbGV0ZSB0aGlzLl9tYXAuX3RhcmdldHNbTC5zdGFtcCh0YXJnZXRFbCldO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgZ2V0QXR0cmlidXRpb246IFN0cmluZ1xuXHQvLyBVc2VkIGJ5IHRoZSBgYXR0cmlidXRpb24gY29udHJvbGAsIHJldHVybnMgdGhlIFthdHRyaWJ1dGlvbiBvcHRpb25dKCNncmlkbGF5ZXItYXR0cmlidXRpb24pLlxuXHRnZXRBdHRyaWJ1dGlvbjogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMuYXR0cmlidXRpb247XG5cdH0sXG5cblx0X2xheWVyQWRkOiBmdW5jdGlvbiAoZSkge1xuXHRcdHZhciBtYXAgPSBlLnRhcmdldDtcblxuXHRcdC8vIGNoZWNrIGluIGNhc2UgbGF5ZXIgZ2V0cyBhZGRlZCBhbmQgdGhlbiByZW1vdmVkIGJlZm9yZSB0aGUgbWFwIGlzIHJlYWR5XG5cdFx0aWYgKCFtYXAuaGFzTGF5ZXIodGhpcykpIHsgcmV0dXJuOyB9XG5cblx0XHR0aGlzLl9tYXAgPSBtYXA7XG5cdFx0dGhpcy5fem9vbUFuaW1hdGVkID0gbWFwLl96b29tQW5pbWF0ZWQ7XG5cblx0XHRpZiAodGhpcy5nZXRFdmVudHMpIHtcblx0XHRcdHZhciBldmVudHMgPSB0aGlzLmdldEV2ZW50cygpO1xuXHRcdFx0bWFwLm9uKGV2ZW50cywgdGhpcyk7XG5cdFx0XHR0aGlzLm9uY2UoJ3JlbW92ZScsIGZ1bmN0aW9uICgpIHtcblx0XHRcdFx0bWFwLm9mZihldmVudHMsIHRoaXMpO1xuXHRcdFx0fSwgdGhpcyk7XG5cdFx0fVxuXG5cdFx0dGhpcy5vbkFkZChtYXApO1xuXG5cdFx0aWYgKHRoaXMuZ2V0QXR0cmlidXRpb24gJiYgdGhpcy5fbWFwLmF0dHJpYnV0aW9uQ29udHJvbCkge1xuXHRcdFx0dGhpcy5fbWFwLmF0dHJpYnV0aW9uQ29udHJvbC5hZGRBdHRyaWJ1dGlvbih0aGlzLmdldEF0dHJpYnV0aW9uKCkpO1xuXHRcdH1cblxuXHRcdHRoaXMuZmlyZSgnYWRkJyk7XG5cdFx0bWFwLmZpcmUoJ2xheWVyYWRkJywge2xheWVyOiB0aGlzfSk7XG5cdH1cbn0pO1xuXG4vKiBAc2VjdGlvbiBFeHRlbnNpb24gbWV0aG9kc1xuICogQHVuaW5oZXJpdGFibGVcbiAqXG4gKiBFdmVyeSBsYXllciBzaG91bGQgZXh0ZW5kIGZyb20gYEwuTGF5ZXJgIGFuZCAocmUtKWltcGxlbWVudCB0aGUgZm9sbG93aW5nIG1ldGhvZHMuXG4gKlxuICogQG1ldGhvZCBvbkFkZChtYXA6IE1hcCk6IHRoaXNcbiAqIFNob3VsZCBjb250YWluIGNvZGUgdGhhdCBjcmVhdGVzIERPTSBlbGVtZW50cyBmb3IgdGhlIGxheWVyLCBhZGRzIHRoZW0gdG8gYG1hcCBwYW5lc2Agd2hlcmUgdGhleSBzaG91bGQgYmVsb25nIGFuZCBwdXRzIGxpc3RlbmVycyBvbiByZWxldmFudCBtYXAgZXZlbnRzLiBDYWxsZWQgb24gW2BtYXAuYWRkTGF5ZXIobGF5ZXIpYF0oI21hcC1hZGRsYXllcikuXG4gKlxuICogQG1ldGhvZCBvblJlbW92ZShtYXA6IE1hcCk6IHRoaXNcbiAqIFNob3VsZCBjb250YWluIGFsbCBjbGVhbiB1cCBjb2RlIHRoYXQgcmVtb3ZlcyB0aGUgbGF5ZXIncyBlbGVtZW50cyBmcm9tIHRoZSBET00gYW5kIHJlbW92ZXMgbGlzdGVuZXJzIHByZXZpb3VzbHkgYWRkZWQgaW4gW2BvbkFkZGBdKCNsYXllci1vbmFkZCkuIENhbGxlZCBvbiBbYG1hcC5yZW1vdmVMYXllcihsYXllcilgXSgjbWFwLXJlbW92ZWxheWVyKS5cbiAqXG4gKiBAbWV0aG9kIGdldEV2ZW50cygpOiBPYmplY3RcbiAqIFRoaXMgb3B0aW9uYWwgbWV0aG9kIHNob3VsZCByZXR1cm4gYW4gb2JqZWN0IGxpa2UgYHsgdmlld3Jlc2V0OiB0aGlzLl9yZXNldCB9YCBmb3IgW2BhZGRFdmVudExpc3RlbmVyYF0oI2V2ZW50ZWQtYWRkZXZlbnRsaXN0ZW5lcikuIFRoZSBldmVudCBoYW5kbGVycyBpbiB0aGlzIG9iamVjdCB3aWxsIGJlIGF1dG9tYXRpY2FsbHkgYWRkZWQgYW5kIHJlbW92ZWQgZnJvbSB0aGUgbWFwIHdpdGggeW91ciBsYXllci5cbiAqXG4gKiBAbWV0aG9kIGdldEF0dHJpYnV0aW9uKCk6IFN0cmluZ1xuICogVGhpcyBvcHRpb25hbCBtZXRob2Qgc2hvdWxkIHJldHVybiBhIHN0cmluZyBjb250YWluaW5nIEhUTUwgdG8gYmUgc2hvd24gb24gdGhlIGBBdHRyaWJ1dGlvbiBjb250cm9sYCB3aGVuZXZlciB0aGUgbGF5ZXIgaXMgdmlzaWJsZS5cbiAqXG4gKiBAbWV0aG9kIGJlZm9yZUFkZChtYXA6IE1hcCk6IHRoaXNcbiAqIE9wdGlvbmFsIG1ldGhvZC4gQ2FsbGVkIG9uIFtgbWFwLmFkZExheWVyKGxheWVyKWBdKCNtYXAtYWRkbGF5ZXIpLCBiZWZvcmUgdGhlIGxheWVyIGlzIGFkZGVkIHRvIHRoZSBtYXAsIGJlZm9yZSBldmVudHMgYXJlIGluaXRpYWxpemVkLCB3aXRob3V0IHdhaXRpbmcgdW50aWwgdGhlIG1hcCBpcyBpbiBhIHVzYWJsZSBzdGF0ZS4gVXNlIGZvciBlYXJseSBpbml0aWFsaXphdGlvbiBvbmx5LlxuICovXG5cblxuLyogQG5hbWVzcGFjZSBNYXBcbiAqIEBzZWN0aW9uIExheWVyIGV2ZW50c1xuICpcbiAqIEBldmVudCBsYXllcmFkZDogTGF5ZXJFdmVudFxuICogRmlyZWQgd2hlbiBhIG5ldyBsYXllciBpcyBhZGRlZCB0byB0aGUgbWFwLlxuICpcbiAqIEBldmVudCBsYXllcnJlbW92ZTogTGF5ZXJFdmVudFxuICogRmlyZWQgd2hlbiBzb21lIGxheWVyIGlzIHJlbW92ZWQgZnJvbSB0aGUgbWFwXG4gKlxuICogQHNlY3Rpb24gTWV0aG9kcyBmb3IgTGF5ZXJzIGFuZCBDb250cm9sc1xuICovXG5MLk1hcC5pbmNsdWRlKHtcblx0Ly8gQG1ldGhvZCBhZGRMYXllcihsYXllcjogTGF5ZXIpOiB0aGlzXG5cdC8vIEFkZHMgdGhlIGdpdmVuIGxheWVyIHRvIHRoZSBtYXBcblx0YWRkTGF5ZXI6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHZhciBpZCA9IEwuc3RhbXAobGF5ZXIpO1xuXHRcdGlmICh0aGlzLl9sYXllcnNbaWRdKSB7IHJldHVybiB0aGlzOyB9XG5cdFx0dGhpcy5fbGF5ZXJzW2lkXSA9IGxheWVyO1xuXG5cdFx0bGF5ZXIuX21hcFRvQWRkID0gdGhpcztcblxuXHRcdGlmIChsYXllci5iZWZvcmVBZGQpIHtcblx0XHRcdGxheWVyLmJlZm9yZUFkZCh0aGlzKTtcblx0XHR9XG5cblx0XHR0aGlzLndoZW5SZWFkeShsYXllci5fbGF5ZXJBZGQsIGxheWVyKTtcblxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgcmVtb3ZlTGF5ZXIobGF5ZXI6IExheWVyKTogdGhpc1xuXHQvLyBSZW1vdmVzIHRoZSBnaXZlbiBsYXllciBmcm9tIHRoZSBtYXAuXG5cdHJlbW92ZUxheWVyOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR2YXIgaWQgPSBMLnN0YW1wKGxheWVyKTtcblxuXHRcdGlmICghdGhpcy5fbGF5ZXJzW2lkXSkgeyByZXR1cm4gdGhpczsgfVxuXG5cdFx0aWYgKHRoaXMuX2xvYWRlZCkge1xuXHRcdFx0bGF5ZXIub25SZW1vdmUodGhpcyk7XG5cdFx0fVxuXG5cdFx0aWYgKGxheWVyLmdldEF0dHJpYnV0aW9uICYmIHRoaXMuYXR0cmlidXRpb25Db250cm9sKSB7XG5cdFx0XHR0aGlzLmF0dHJpYnV0aW9uQ29udHJvbC5yZW1vdmVBdHRyaWJ1dGlvbihsYXllci5nZXRBdHRyaWJ1dGlvbigpKTtcblx0XHR9XG5cblx0XHRkZWxldGUgdGhpcy5fbGF5ZXJzW2lkXTtcblxuXHRcdGlmICh0aGlzLl9sb2FkZWQpIHtcblx0XHRcdHRoaXMuZmlyZSgnbGF5ZXJyZW1vdmUnLCB7bGF5ZXI6IGxheWVyfSk7XG5cdFx0XHRsYXllci5maXJlKCdyZW1vdmUnKTtcblx0XHR9XG5cblx0XHRsYXllci5fbWFwID0gbGF5ZXIuX21hcFRvQWRkID0gbnVsbDtcblxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgaGFzTGF5ZXIobGF5ZXI6IExheWVyKTogQm9vbGVhblxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgZ2l2ZW4gbGF5ZXIgaXMgY3VycmVudGx5IGFkZGVkIHRvIHRoZSBtYXBcblx0aGFzTGF5ZXI6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHJldHVybiAhIWxheWVyICYmIChMLnN0YW1wKGxheWVyKSBpbiB0aGlzLl9sYXllcnMpO1xuXHR9LFxuXG5cdC8qIEBtZXRob2QgZWFjaExheWVyKGZuOiBGdW5jdGlvbiwgY29udGV4dD86IE9iamVjdCk6IHRoaXNcblx0ICogSXRlcmF0ZXMgb3ZlciB0aGUgbGF5ZXJzIG9mIHRoZSBtYXAsIG9wdGlvbmFsbHkgc3BlY2lmeWluZyBjb250ZXh0IG9mIHRoZSBpdGVyYXRvciBmdW5jdGlvbi5cblx0ICogYGBgXG5cdCAqIG1hcC5lYWNoTGF5ZXIoZnVuY3Rpb24obGF5ZXIpe1xuXHQgKiAgICAgbGF5ZXIuYmluZFBvcHVwKCdIZWxsbycpO1xuXHQgKiB9KTtcblx0ICogYGBgXG5cdCAqL1xuXHRlYWNoTGF5ZXI6IGZ1bmN0aW9uIChtZXRob2QsIGNvbnRleHQpIHtcblx0XHRmb3IgKHZhciBpIGluIHRoaXMuX2xheWVycykge1xuXHRcdFx0bWV0aG9kLmNhbGwoY29udGV4dCwgdGhpcy5fbGF5ZXJzW2ldKTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0X2FkZExheWVyczogZnVuY3Rpb24gKGxheWVycykge1xuXHRcdGxheWVycyA9IGxheWVycyA/IChMLlV0aWwuaXNBcnJheShsYXllcnMpID8gbGF5ZXJzIDogW2xheWVyc10pIDogW107XG5cblx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gbGF5ZXJzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHR0aGlzLmFkZExheWVyKGxheWVyc1tpXSk7XG5cdFx0fVxuXHR9LFxuXG5cdF9hZGRab29tTGltaXQ6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdGlmIChpc05hTihsYXllci5vcHRpb25zLm1heFpvb20pIHx8ICFpc05hTihsYXllci5vcHRpb25zLm1pblpvb20pKSB7XG5cdFx0XHR0aGlzLl96b29tQm91bmRMYXllcnNbTC5zdGFtcChsYXllcildID0gbGF5ZXI7XG5cdFx0XHR0aGlzLl91cGRhdGVab29tTGV2ZWxzKCk7XG5cdFx0fVxuXHR9LFxuXG5cdF9yZW1vdmVab29tTGltaXQ6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHZhciBpZCA9IEwuc3RhbXAobGF5ZXIpO1xuXG5cdFx0aWYgKHRoaXMuX3pvb21Cb3VuZExheWVyc1tpZF0pIHtcblx0XHRcdGRlbGV0ZSB0aGlzLl96b29tQm91bmRMYXllcnNbaWRdO1xuXHRcdFx0dGhpcy5fdXBkYXRlWm9vbUxldmVscygpO1xuXHRcdH1cblx0fSxcblxuXHRfdXBkYXRlWm9vbUxldmVsczogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBtaW5ab29tID0gSW5maW5pdHksXG5cdFx0ICAgIG1heFpvb20gPSAtSW5maW5pdHksXG5cdFx0ICAgIG9sZFpvb21TcGFuID0gdGhpcy5fZ2V0Wm9vbVNwYW4oKTtcblxuXHRcdGZvciAodmFyIGkgaW4gdGhpcy5fem9vbUJvdW5kTGF5ZXJzKSB7XG5cdFx0XHR2YXIgb3B0aW9ucyA9IHRoaXMuX3pvb21Cb3VuZExheWVyc1tpXS5vcHRpb25zO1xuXG5cdFx0XHRtaW5ab29tID0gb3B0aW9ucy5taW5ab29tID09PSB1bmRlZmluZWQgPyBtaW5ab29tIDogTWF0aC5taW4obWluWm9vbSwgb3B0aW9ucy5taW5ab29tKTtcblx0XHRcdG1heFpvb20gPSBvcHRpb25zLm1heFpvb20gPT09IHVuZGVmaW5lZCA/IG1heFpvb20gOiBNYXRoLm1heChtYXhab29tLCBvcHRpb25zLm1heFpvb20pO1xuXHRcdH1cblxuXHRcdHRoaXMuX2xheWVyc01heFpvb20gPSBtYXhab29tID09PSAtSW5maW5pdHkgPyB1bmRlZmluZWQgOiBtYXhab29tO1xuXHRcdHRoaXMuX2xheWVyc01pblpvb20gPSBtaW5ab29tID09PSBJbmZpbml0eSA/IHVuZGVmaW5lZCA6IG1pblpvb207XG5cblx0XHQvLyBAc2VjdGlvbiBNYXAgc3RhdGUgY2hhbmdlIGV2ZW50c1xuXHRcdC8vIEBldmVudCB6b29tbGV2ZWxzY2hhbmdlOiBFdmVudFxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIG51bWJlciBvZiB6b29tbGV2ZWxzIG9uIHRoZSBtYXAgaXMgY2hhbmdlZCBkdWVcblx0XHQvLyB0byBhZGRpbmcgb3IgcmVtb3ZpbmcgYSBsYXllci5cblx0XHRpZiAob2xkWm9vbVNwYW4gIT09IHRoaXMuX2dldFpvb21TcGFuKCkpIHtcblx0XHRcdHRoaXMuZmlyZSgnem9vbWxldmVsc2NoYW5nZScpO1xuXHRcdH1cblxuXHRcdGlmICh0aGlzLm9wdGlvbnMubWF4Wm9vbSA9PT0gdW5kZWZpbmVkICYmIHRoaXMuX2xheWVyc01heFpvb20gJiYgdGhpcy5nZXRab29tKCkgPiB0aGlzLl9sYXllcnNNYXhab29tKSB7XG5cdFx0XHR0aGlzLnNldFpvb20odGhpcy5fbGF5ZXJzTWF4Wm9vbSk7XG5cdFx0fVxuXHRcdGlmICh0aGlzLm9wdGlvbnMubWluWm9vbSA9PT0gdW5kZWZpbmVkICYmIHRoaXMuX2xheWVyc01pblpvb20gJiYgdGhpcy5nZXRab29tKCkgPCB0aGlzLl9sYXllcnNNaW5ab29tKSB7XG5cdFx0XHR0aGlzLnNldFpvb20odGhpcy5fbGF5ZXJzTWluWm9vbSk7XG5cdFx0fVxuXHR9XG59KTtcblxuXG5cbi8qXHJcbiAqIEBuYW1lc3BhY2UgRG9tRXZlbnRcclxuICogVXRpbGl0eSBmdW5jdGlvbnMgdG8gd29yayB3aXRoIHRoZSBbRE9NIGV2ZW50c10oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvQVBJL0V2ZW50KSwgdXNlZCBieSBMZWFmbGV0IGludGVybmFsbHkuXHJcbiAqL1xyXG5cclxuLy8gSW5zcGlyZWQgYnkgSm9obiBSZXNpZywgRGVhbiBFZHdhcmRzIGFuZCBZVUkgYWRkRXZlbnQgaW1wbGVtZW50YXRpb25zLlxyXG5cclxuXHJcblxyXG52YXIgZXZlbnRzS2V5ID0gJ19sZWFmbGV0X2V2ZW50cyc7XHJcblxyXG5MLkRvbUV2ZW50ID0ge1xyXG5cclxuXHQvLyBAZnVuY3Rpb24gb24oZWw6IEhUTUxFbGVtZW50LCB0eXBlczogU3RyaW5nLCBmbjogRnVuY3Rpb24sIGNvbnRleHQ/OiBPYmplY3QpOiB0aGlzXHJcblx0Ly8gQWRkcyBhIGxpc3RlbmVyIGZ1bmN0aW9uIChgZm5gKSB0byBhIHBhcnRpY3VsYXIgRE9NIGV2ZW50IHR5cGUgb2YgdGhlXHJcblx0Ly8gZWxlbWVudCBgZWxgLiBZb3UgY2FuIG9wdGlvbmFsbHkgc3BlY2lmeSB0aGUgY29udGV4dCBvZiB0aGUgbGlzdGVuZXJcclxuXHQvLyAob2JqZWN0IHRoZSBgdGhpc2Aga2V5d29yZCB3aWxsIHBvaW50IHRvKS4gWW91IGNhbiBhbHNvIHBhc3Mgc2V2ZXJhbFxyXG5cdC8vIHNwYWNlLXNlcGFyYXRlZCB0eXBlcyAoZS5nLiBgJ2NsaWNrIGRibGNsaWNrJ2ApLlxyXG5cclxuXHQvLyBAYWx0ZXJuYXRpdmVcclxuXHQvLyBAZnVuY3Rpb24gb24oZWw6IEhUTUxFbGVtZW50LCBldmVudE1hcDogT2JqZWN0LCBjb250ZXh0PzogT2JqZWN0KTogdGhpc1xyXG5cdC8vIEFkZHMgYSBzZXQgb2YgdHlwZS9saXN0ZW5lciBwYWlycywgZS5nLiBge2NsaWNrOiBvbkNsaWNrLCBtb3VzZW1vdmU6IG9uTW91c2VNb3ZlfWBcclxuXHRvbjogZnVuY3Rpb24gKG9iaiwgdHlwZXMsIGZuLCBjb250ZXh0KSB7XHJcblxyXG5cdFx0aWYgKHR5cGVvZiB0eXBlcyA9PT0gJ29iamVjdCcpIHtcclxuXHRcdFx0Zm9yICh2YXIgdHlwZSBpbiB0eXBlcykge1xyXG5cdFx0XHRcdHRoaXMuX29uKG9iaiwgdHlwZSwgdHlwZXNbdHlwZV0sIGZuKTtcclxuXHRcdFx0fVxyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0dHlwZXMgPSBMLlV0aWwuc3BsaXRXb3Jkcyh0eXBlcyk7XHJcblxyXG5cdFx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gdHlwZXMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0XHR0aGlzLl9vbihvYmosIHR5cGVzW2ldLCBmbiwgY29udGV4dCk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gb2ZmKGVsOiBIVE1MRWxlbWVudCwgdHlwZXM6IFN0cmluZywgZm46IEZ1bmN0aW9uLCBjb250ZXh0PzogT2JqZWN0KTogdGhpc1xyXG5cdC8vIFJlbW92ZXMgYSBwcmV2aW91c2x5IGFkZGVkIGxpc3RlbmVyIGZ1bmN0aW9uLiBJZiBubyBmdW5jdGlvbiBpcyBzcGVjaWZpZWQsXHJcblx0Ly8gaXQgd2lsbCByZW1vdmUgYWxsIHRoZSBsaXN0ZW5lcnMgb2YgdGhhdCBwYXJ0aWN1bGFyIERPTSBldmVudCBmcm9tIHRoZSBlbGVtZW50LlxyXG5cdC8vIE5vdGUgdGhhdCBpZiB5b3UgcGFzc2VkIGEgY3VzdG9tIGNvbnRleHQgdG8gb24sIHlvdSBtdXN0IHBhc3MgdGhlIHNhbWVcclxuXHQvLyBjb250ZXh0IHRvIGBvZmZgIGluIG9yZGVyIHRvIHJlbW92ZSB0aGUgbGlzdGVuZXIuXHJcblxyXG5cdC8vIEBhbHRlcm5hdGl2ZVxyXG5cdC8vIEBmdW5jdGlvbiBvZmYoZWw6IEhUTUxFbGVtZW50LCBldmVudE1hcDogT2JqZWN0LCBjb250ZXh0PzogT2JqZWN0KTogdGhpc1xyXG5cdC8vIFJlbW92ZXMgYSBzZXQgb2YgdHlwZS9saXN0ZW5lciBwYWlycywgZS5nLiBge2NsaWNrOiBvbkNsaWNrLCBtb3VzZW1vdmU6IG9uTW91c2VNb3ZlfWBcclxuXHRvZmY6IGZ1bmN0aW9uIChvYmosIHR5cGVzLCBmbiwgY29udGV4dCkge1xyXG5cclxuXHRcdGlmICh0eXBlb2YgdHlwZXMgPT09ICdvYmplY3QnKSB7XHJcblx0XHRcdGZvciAodmFyIHR5cGUgaW4gdHlwZXMpIHtcclxuXHRcdFx0XHR0aGlzLl9vZmYob2JqLCB0eXBlLCB0eXBlc1t0eXBlXSwgZm4pO1xyXG5cdFx0XHR9XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHR0eXBlcyA9IEwuVXRpbC5zcGxpdFdvcmRzKHR5cGVzKTtcclxuXHJcblx0XHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSB0eXBlcy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRcdHRoaXMuX29mZihvYmosIHR5cGVzW2ldLCBmbiwgY29udGV4dCk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfb246IGZ1bmN0aW9uIChvYmosIHR5cGUsIGZuLCBjb250ZXh0KSB7XHJcblx0XHR2YXIgaWQgPSB0eXBlICsgTC5zdGFtcChmbikgKyAoY29udGV4dCA/ICdfJyArIEwuc3RhbXAoY29udGV4dCkgOiAnJyk7XHJcblxyXG5cdFx0aWYgKG9ialtldmVudHNLZXldICYmIG9ialtldmVudHNLZXldW2lkXSkgeyByZXR1cm4gdGhpczsgfVxyXG5cclxuXHRcdHZhciBoYW5kbGVyID0gZnVuY3Rpb24gKGUpIHtcclxuXHRcdFx0cmV0dXJuIGZuLmNhbGwoY29udGV4dCB8fCBvYmosIGUgfHwgd2luZG93LmV2ZW50KTtcclxuXHRcdH07XHJcblxyXG5cdFx0dmFyIG9yaWdpbmFsSGFuZGxlciA9IGhhbmRsZXI7XHJcblxyXG5cdFx0aWYgKEwuQnJvd3Nlci5wb2ludGVyICYmIHR5cGUuaW5kZXhPZigndG91Y2gnKSA9PT0gMCkge1xyXG5cdFx0XHR0aGlzLmFkZFBvaW50ZXJMaXN0ZW5lcihvYmosIHR5cGUsIGhhbmRsZXIsIGlkKTtcclxuXHJcblx0XHR9IGVsc2UgaWYgKEwuQnJvd3Nlci50b3VjaCAmJiAodHlwZSA9PT0gJ2RibGNsaWNrJykgJiYgdGhpcy5hZGREb3VibGVUYXBMaXN0ZW5lcikge1xyXG5cdFx0XHR0aGlzLmFkZERvdWJsZVRhcExpc3RlbmVyKG9iaiwgaGFuZGxlciwgaWQpO1xyXG5cclxuXHRcdH0gZWxzZSBpZiAoJ2FkZEV2ZW50TGlzdGVuZXInIGluIG9iaikge1xyXG5cclxuXHRcdFx0aWYgKHR5cGUgPT09ICdtb3VzZXdoZWVsJykge1xyXG5cdFx0XHRcdG9iai5hZGRFdmVudExpc3RlbmVyKCdvbndoZWVsJyBpbiBvYmogPyAnd2hlZWwnIDogJ21vdXNld2hlZWwnLCBoYW5kbGVyLCBmYWxzZSk7XHJcblxyXG5cdFx0XHR9IGVsc2UgaWYgKCh0eXBlID09PSAnbW91c2VlbnRlcicpIHx8ICh0eXBlID09PSAnbW91c2VsZWF2ZScpKSB7XHJcblx0XHRcdFx0aGFuZGxlciA9IGZ1bmN0aW9uIChlKSB7XHJcblx0XHRcdFx0XHRlID0gZSB8fCB3aW5kb3cuZXZlbnQ7XHJcblx0XHRcdFx0XHRpZiAoTC5Eb21FdmVudC5faXNFeHRlcm5hbFRhcmdldChvYmosIGUpKSB7XHJcblx0XHRcdFx0XHRcdG9yaWdpbmFsSGFuZGxlcihlKTtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHR9O1xyXG5cdFx0XHRcdG9iai5hZGRFdmVudExpc3RlbmVyKHR5cGUgPT09ICdtb3VzZWVudGVyJyA/ICdtb3VzZW92ZXInIDogJ21vdXNlb3V0JywgaGFuZGxlciwgZmFsc2UpO1xyXG5cclxuXHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHRpZiAodHlwZSA9PT0gJ2NsaWNrJyAmJiBMLkJyb3dzZXIuYW5kcm9pZCkge1xyXG5cdFx0XHRcdFx0aGFuZGxlciA9IGZ1bmN0aW9uIChlKSB7XHJcblx0XHRcdFx0XHRcdHJldHVybiBMLkRvbUV2ZW50Ll9maWx0ZXJDbGljayhlLCBvcmlnaW5hbEhhbmRsZXIpO1xyXG5cdFx0XHRcdFx0fTtcclxuXHRcdFx0XHR9XHJcblx0XHRcdFx0b2JqLmFkZEV2ZW50TGlzdGVuZXIodHlwZSwgaGFuZGxlciwgZmFsc2UpO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0fSBlbHNlIGlmICgnYXR0YWNoRXZlbnQnIGluIG9iaikge1xyXG5cdFx0XHRvYmouYXR0YWNoRXZlbnQoJ29uJyArIHR5cGUsIGhhbmRsZXIpO1xyXG5cdFx0fVxyXG5cclxuXHRcdG9ialtldmVudHNLZXldID0gb2JqW2V2ZW50c0tleV0gfHwge307XHJcblx0XHRvYmpbZXZlbnRzS2V5XVtpZF0gPSBoYW5kbGVyO1xyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdF9vZmY6IGZ1bmN0aW9uIChvYmosIHR5cGUsIGZuLCBjb250ZXh0KSB7XHJcblxyXG5cdFx0dmFyIGlkID0gdHlwZSArIEwuc3RhbXAoZm4pICsgKGNvbnRleHQgPyAnXycgKyBMLnN0YW1wKGNvbnRleHQpIDogJycpLFxyXG5cdFx0ICAgIGhhbmRsZXIgPSBvYmpbZXZlbnRzS2V5XSAmJiBvYmpbZXZlbnRzS2V5XVtpZF07XHJcblxyXG5cdFx0aWYgKCFoYW5kbGVyKSB7IHJldHVybiB0aGlzOyB9XHJcblxyXG5cdFx0aWYgKEwuQnJvd3Nlci5wb2ludGVyICYmIHR5cGUuaW5kZXhPZigndG91Y2gnKSA9PT0gMCkge1xyXG5cdFx0XHR0aGlzLnJlbW92ZVBvaW50ZXJMaXN0ZW5lcihvYmosIHR5cGUsIGlkKTtcclxuXHJcblx0XHR9IGVsc2UgaWYgKEwuQnJvd3Nlci50b3VjaCAmJiAodHlwZSA9PT0gJ2RibGNsaWNrJykgJiYgdGhpcy5yZW1vdmVEb3VibGVUYXBMaXN0ZW5lcikge1xyXG5cdFx0XHR0aGlzLnJlbW92ZURvdWJsZVRhcExpc3RlbmVyKG9iaiwgaWQpO1xyXG5cclxuXHRcdH0gZWxzZSBpZiAoJ3JlbW92ZUV2ZW50TGlzdGVuZXInIGluIG9iaikge1xyXG5cclxuXHRcdFx0aWYgKHR5cGUgPT09ICdtb3VzZXdoZWVsJykge1xyXG5cdFx0XHRcdG9iai5yZW1vdmVFdmVudExpc3RlbmVyKCdvbndoZWVsJyBpbiBvYmogPyAnd2hlZWwnIDogJ21vdXNld2hlZWwnLCBoYW5kbGVyLCBmYWxzZSk7XHJcblxyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdG9iai5yZW1vdmVFdmVudExpc3RlbmVyKFxyXG5cdFx0XHRcdFx0dHlwZSA9PT0gJ21vdXNlZW50ZXInID8gJ21vdXNlb3ZlcicgOlxyXG5cdFx0XHRcdFx0dHlwZSA9PT0gJ21vdXNlbGVhdmUnID8gJ21vdXNlb3V0JyA6IHR5cGUsIGhhbmRsZXIsIGZhbHNlKTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdH0gZWxzZSBpZiAoJ2RldGFjaEV2ZW50JyBpbiBvYmopIHtcclxuXHRcdFx0b2JqLmRldGFjaEV2ZW50KCdvbicgKyB0eXBlLCBoYW5kbGVyKTtcclxuXHRcdH1cclxuXHJcblx0XHRvYmpbZXZlbnRzS2V5XVtpZF0gPSBudWxsO1xyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBzdG9wUHJvcGFnYXRpb24oZXY6IERPTUV2ZW50KTogdGhpc1xyXG5cdC8vIFN0b3AgdGhlIGdpdmVuIGV2ZW50IGZyb20gcHJvcGFnYXRpb24gdG8gcGFyZW50IGVsZW1lbnRzLiBVc2VkIGluc2lkZSB0aGUgbGlzdGVuZXIgZnVuY3Rpb25zOlxyXG5cdC8vIGBgYGpzXHJcblx0Ly8gTC5Eb21FdmVudC5vbihkaXYsICdjbGljaycsIGZ1bmN0aW9uIChldikge1xyXG5cdC8vIFx0TC5Eb21FdmVudC5zdG9wUHJvcGFnYXRpb24oZXYpO1xyXG5cdC8vIH0pO1xyXG5cdC8vIGBgYFxyXG5cdHN0b3BQcm9wYWdhdGlvbjogZnVuY3Rpb24gKGUpIHtcclxuXHJcblx0XHRpZiAoZS5zdG9wUHJvcGFnYXRpb24pIHtcclxuXHRcdFx0ZS5zdG9wUHJvcGFnYXRpb24oKTtcclxuXHRcdH0gZWxzZSBpZiAoZS5vcmlnaW5hbEV2ZW50KSB7ICAvLyBJbiBjYXNlIG9mIExlYWZsZXQgZXZlbnQuXHJcblx0XHRcdGUub3JpZ2luYWxFdmVudC5fc3RvcHBlZCA9IHRydWU7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRlLmNhbmNlbEJ1YmJsZSA9IHRydWU7XHJcblx0XHR9XHJcblx0XHRMLkRvbUV2ZW50Ll9za2lwcGVkKGUpO1xyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBkaXNhYmxlU2Nyb2xsUHJvcGFnYXRpb24oZWw6IEhUTUxFbGVtZW50KTogdGhpc1xyXG5cdC8vIEFkZHMgYHN0b3BQcm9wYWdhdGlvbmAgdG8gdGhlIGVsZW1lbnQncyBgJ21vdXNld2hlZWwnYCBldmVudHMgKHBsdXMgYnJvd3NlciB2YXJpYW50cykuXHJcblx0ZGlzYWJsZVNjcm9sbFByb3BhZ2F0aW9uOiBmdW5jdGlvbiAoZWwpIHtcclxuXHRcdHJldHVybiBMLkRvbUV2ZW50Lm9uKGVsLCAnbW91c2V3aGVlbCcsIEwuRG9tRXZlbnQuc3RvcFByb3BhZ2F0aW9uKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gZGlzYWJsZUNsaWNrUHJvcGFnYXRpb24oZWw6IEhUTUxFbGVtZW50KTogdGhpc1xyXG5cdC8vIEFkZHMgYHN0b3BQcm9wYWdhdGlvbmAgdG8gdGhlIGVsZW1lbnQncyBgJ2NsaWNrJ2AsIGAnZG91YmxlY2xpY2snYCxcclxuXHQvLyBgJ21vdXNlZG93bidgIGFuZCBgJ3RvdWNoc3RhcnQnYCBldmVudHMgKHBsdXMgYnJvd3NlciB2YXJpYW50cykuXHJcblx0ZGlzYWJsZUNsaWNrUHJvcGFnYXRpb246IGZ1bmN0aW9uIChlbCkge1xyXG5cdFx0dmFyIHN0b3AgPSBMLkRvbUV2ZW50LnN0b3BQcm9wYWdhdGlvbjtcclxuXHJcblx0XHRMLkRvbUV2ZW50Lm9uKGVsLCBMLkRyYWdnYWJsZS5TVEFSVC5qb2luKCcgJyksIHN0b3ApO1xyXG5cclxuXHRcdHJldHVybiBMLkRvbUV2ZW50Lm9uKGVsLCB7XHJcblx0XHRcdGNsaWNrOiBMLkRvbUV2ZW50Ll9mYWtlU3RvcCxcclxuXHRcdFx0ZGJsY2xpY2s6IHN0b3BcclxuXHRcdH0pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBwcmV2ZW50RGVmYXVsdChldjogRE9NRXZlbnQpOiB0aGlzXHJcblx0Ly8gUHJldmVudHMgdGhlIGRlZmF1bHQgYWN0aW9uIG9mIHRoZSBET00gRXZlbnQgYGV2YCBmcm9tIGhhcHBlbmluZyAoc3VjaCBhc1xyXG5cdC8vIGZvbGxvd2luZyBhIGxpbmsgaW4gdGhlIGhyZWYgb2YgdGhlIGEgZWxlbWVudCwgb3IgZG9pbmcgYSBQT1NUIHJlcXVlc3RcclxuXHQvLyB3aXRoIHBhZ2UgcmVsb2FkIHdoZW4gYSBgPGZvcm0+YCBpcyBzdWJtaXR0ZWQpLlxyXG5cdC8vIFVzZSBpdCBpbnNpZGUgbGlzdGVuZXIgZnVuY3Rpb25zLlxyXG5cdHByZXZlbnREZWZhdWx0OiBmdW5jdGlvbiAoZSkge1xyXG5cclxuXHRcdGlmIChlLnByZXZlbnREZWZhdWx0KSB7XHJcblx0XHRcdGUucHJldmVudERlZmF1bHQoKTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdGUucmV0dXJuVmFsdWUgPSBmYWxzZTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBzdG9wKGV2KTogdGhpc1xyXG5cdC8vIERvZXMgYHN0b3BQcm9wYWdhdGlvbmAgYW5kIGBwcmV2ZW50RGVmYXVsdGAgYXQgdGhlIHNhbWUgdGltZS5cclxuXHRzdG9wOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0cmV0dXJuIEwuRG9tRXZlbnRcclxuXHRcdFx0LnByZXZlbnREZWZhdWx0KGUpXHJcblx0XHRcdC5zdG9wUHJvcGFnYXRpb24oZSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGdldE1vdXNlUG9zaXRpb24oZXY6IERPTUV2ZW50LCBjb250YWluZXI/OiBIVE1MRWxlbWVudCk6IFBvaW50XHJcblx0Ly8gR2V0cyBub3JtYWxpemVkIG1vdXNlIHBvc2l0aW9uIGZyb20gYSBET00gZXZlbnQgcmVsYXRpdmUgdG8gdGhlXHJcblx0Ly8gYGNvbnRhaW5lcmAgb3IgdG8gdGhlIHdob2xlIHBhZ2UgaWYgbm90IHNwZWNpZmllZC5cclxuXHRnZXRNb3VzZVBvc2l0aW9uOiBmdW5jdGlvbiAoZSwgY29udGFpbmVyKSB7XHJcblx0XHRpZiAoIWNvbnRhaW5lcikge1xyXG5cdFx0XHRyZXR1cm4gbmV3IEwuUG9pbnQoZS5jbGllbnRYLCBlLmNsaWVudFkpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciByZWN0ID0gY29udGFpbmVyLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xyXG5cclxuXHRcdHJldHVybiBuZXcgTC5Qb2ludChcclxuXHRcdFx0ZS5jbGllbnRYIC0gcmVjdC5sZWZ0IC0gY29udGFpbmVyLmNsaWVudExlZnQsXHJcblx0XHRcdGUuY2xpZW50WSAtIHJlY3QudG9wIC0gY29udGFpbmVyLmNsaWVudFRvcCk7XHJcblx0fSxcclxuXHJcblx0Ly8gQ2hyb21lIG9uIFdpbiBzY3JvbGxzIGRvdWJsZSB0aGUgcGl4ZWxzIGFzIGluIG90aGVyIHBsYXRmb3JtcyAoc2VlICM0NTM4KSxcclxuXHQvLyBhbmQgRmlyZWZveCBzY3JvbGxzIGRldmljZSBwaXhlbHMsIG5vdCBDU1MgcGl4ZWxzXHJcblx0X3doZWVsUHhGYWN0b3I6IChMLkJyb3dzZXIud2luICYmIEwuQnJvd3Nlci5jaHJvbWUpID8gMiA6XHJcblx0ICAgICAgICAgICAgICAgIEwuQnJvd3Nlci5nZWNrbyA/IHdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvIDpcclxuXHQgICAgICAgICAgICAgICAgMSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGdldFdoZWVsRGVsdGEoZXY6IERPTUV2ZW50KTogTnVtYmVyXHJcblx0Ly8gR2V0cyBub3JtYWxpemVkIHdoZWVsIGRlbHRhIGZyb20gYSBtb3VzZXdoZWVsIERPTSBldmVudCwgaW4gdmVydGljYWxcclxuXHQvLyBwaXhlbHMgc2Nyb2xsZWQgKG5lZ2F0aXZlIGlmIHNjcm9sbGluZyBkb3duKS5cclxuXHQvLyBFdmVudHMgZnJvbSBwb2ludGluZyBkZXZpY2VzIHdpdGhvdXQgcHJlY2lzZSBzY3JvbGxpbmcgYXJlIG1hcHBlZCB0b1xyXG5cdC8vIGEgYmVzdCBndWVzcyBvZiA2MCBwaXhlbHMuXHJcblx0Z2V0V2hlZWxEZWx0YTogZnVuY3Rpb24gKGUpIHtcclxuXHRcdHJldHVybiAoTC5Ccm93c2VyLmVkZ2UpID8gZS53aGVlbERlbHRhWSAvIDIgOiAvLyBEb24ndCB0cnVzdCB3aW5kb3ctZ2VvbWV0cnktYmFzZWQgZGVsdGFcclxuXHRcdCAgICAgICAoZS5kZWx0YVkgJiYgZS5kZWx0YU1vZGUgPT09IDApID8gLWUuZGVsdGFZIC8gTC5Eb21FdmVudC5fd2hlZWxQeEZhY3RvciA6IC8vIFBpeGVsc1xyXG5cdFx0ICAgICAgIChlLmRlbHRhWSAmJiBlLmRlbHRhTW9kZSA9PT0gMSkgPyAtZS5kZWx0YVkgKiAyMCA6IC8vIExpbmVzXHJcblx0XHQgICAgICAgKGUuZGVsdGFZICYmIGUuZGVsdGFNb2RlID09PSAyKSA/IC1lLmRlbHRhWSAqIDYwIDogLy8gUGFnZXNcclxuXHRcdCAgICAgICAoZS5kZWx0YVggfHwgZS5kZWx0YVopID8gMCA6XHQvLyBTa2lwIGhvcml6b250YWwvZGVwdGggd2hlZWwgZXZlbnRzXHJcblx0XHQgICAgICAgZS53aGVlbERlbHRhID8gKGUud2hlZWxEZWx0YVkgfHwgZS53aGVlbERlbHRhKSAvIDIgOiAvLyBMZWdhY3kgSUUgcGl4ZWxzXHJcblx0XHQgICAgICAgKGUuZGV0YWlsICYmIE1hdGguYWJzKGUuZGV0YWlsKSA8IDMyNzY1KSA/IC1lLmRldGFpbCAqIDIwIDogLy8gTGVnYWN5IE1veiBsaW5lc1xyXG5cdFx0ICAgICAgIGUuZGV0YWlsID8gZS5kZXRhaWwgLyAtMzI3NjUgKiA2MCA6IC8vIExlZ2FjeSBNb3ogcGFnZXNcclxuXHRcdCAgICAgICAwO1xyXG5cdH0sXHJcblxyXG5cdF9za2lwRXZlbnRzOiB7fSxcclxuXHJcblx0X2Zha2VTdG9wOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0Ly8gZmFrZXMgc3RvcFByb3BhZ2F0aW9uIGJ5IHNldHRpbmcgYSBzcGVjaWFsIGV2ZW50IGZsYWcsIGNoZWNrZWQvcmVzZXQgd2l0aCBMLkRvbUV2ZW50Ll9za2lwcGVkKGUpXHJcblx0XHRMLkRvbUV2ZW50Ll9za2lwRXZlbnRzW2UudHlwZV0gPSB0cnVlO1xyXG5cdH0sXHJcblxyXG5cdF9za2lwcGVkOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0dmFyIHNraXBwZWQgPSB0aGlzLl9za2lwRXZlbnRzW2UudHlwZV07XHJcblx0XHQvLyByZXNldCB3aGVuIGNoZWNraW5nLCBhcyBpdCdzIG9ubHkgdXNlZCBpbiBtYXAgY29udGFpbmVyIGFuZCBwcm9wYWdhdGVzIG91dHNpZGUgb2YgdGhlIG1hcFxyXG5cdFx0dGhpcy5fc2tpcEV2ZW50c1tlLnR5cGVdID0gZmFsc2U7XHJcblx0XHRyZXR1cm4gc2tpcHBlZDtcclxuXHR9LFxyXG5cclxuXHQvLyBjaGVjayBpZiBlbGVtZW50IHJlYWxseSBsZWZ0L2VudGVyZWQgdGhlIGV2ZW50IHRhcmdldCAoZm9yIG1vdXNlZW50ZXIvbW91c2VsZWF2ZSlcclxuXHRfaXNFeHRlcm5hbFRhcmdldDogZnVuY3Rpb24gKGVsLCBlKSB7XHJcblxyXG5cdFx0dmFyIHJlbGF0ZWQgPSBlLnJlbGF0ZWRUYXJnZXQ7XHJcblxyXG5cdFx0aWYgKCFyZWxhdGVkKSB7IHJldHVybiB0cnVlOyB9XHJcblxyXG5cdFx0dHJ5IHtcclxuXHRcdFx0d2hpbGUgKHJlbGF0ZWQgJiYgKHJlbGF0ZWQgIT09IGVsKSkge1xyXG5cdFx0XHRcdHJlbGF0ZWQgPSByZWxhdGVkLnBhcmVudE5vZGU7XHJcblx0XHRcdH1cclxuXHRcdH0gY2F0Y2ggKGVycikge1xyXG5cdFx0XHRyZXR1cm4gZmFsc2U7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gKHJlbGF0ZWQgIT09IGVsKTtcclxuXHR9LFxyXG5cclxuXHQvLyB0aGlzIGlzIGEgaG9ycmlibGUgd29ya2Fyb3VuZCBmb3IgYSBidWcgaW4gQW5kcm9pZCB3aGVyZSBhIHNpbmdsZSB0b3VjaCB0cmlnZ2VycyB0d28gY2xpY2sgZXZlbnRzXHJcblx0X2ZpbHRlckNsaWNrOiBmdW5jdGlvbiAoZSwgaGFuZGxlcikge1xyXG5cdFx0dmFyIHRpbWVTdGFtcCA9IChlLnRpbWVTdGFtcCB8fCAoZS5vcmlnaW5hbEV2ZW50ICYmIGUub3JpZ2luYWxFdmVudC50aW1lU3RhbXApKSxcclxuXHRcdCAgICBlbGFwc2VkID0gTC5Eb21FdmVudC5fbGFzdENsaWNrICYmICh0aW1lU3RhbXAgLSBMLkRvbUV2ZW50Ll9sYXN0Q2xpY2spO1xyXG5cclxuXHRcdC8vIGFyZSB0aGV5IGNsb3NlciB0b2dldGhlciB0aGFuIDUwMG1zIHlldCBtb3JlIHRoYW4gMTAwbXM/XHJcblx0XHQvLyBBbmRyb2lkIHR5cGljYWxseSB0cmlnZ2VycyB0aGVtIH4zMDBtcyBhcGFydCB3aGlsZSBtdWx0aXBsZSBsaXN0ZW5lcnNcclxuXHRcdC8vIG9uIHRoZSBzYW1lIGV2ZW50IHNob3VsZCBiZSB0cmlnZ2VyZWQgZmFyIGZhc3RlcjtcclxuXHRcdC8vIG9yIGNoZWNrIGlmIGNsaWNrIGlzIHNpbXVsYXRlZCBvbiB0aGUgZWxlbWVudCwgYW5kIGlmIGl0IGlzLCByZWplY3QgYW55IG5vbi1zaW11bGF0ZWQgZXZlbnRzXHJcblxyXG5cdFx0aWYgKChlbGFwc2VkICYmIGVsYXBzZWQgPiAxMDAgJiYgZWxhcHNlZCA8IDUwMCkgfHwgKGUudGFyZ2V0Ll9zaW11bGF0ZWRDbGljayAmJiAhZS5fc2ltdWxhdGVkKSkge1xyXG5cdFx0XHRMLkRvbUV2ZW50LnN0b3AoZSk7XHJcblx0XHRcdHJldHVybjtcclxuXHRcdH1cclxuXHRcdEwuRG9tRXZlbnQuX2xhc3RDbGljayA9IHRpbWVTdGFtcDtcclxuXHJcblx0XHRoYW5kbGVyKGUpO1xyXG5cdH1cclxufTtcclxuXHJcbi8vIEBmdW5jdGlvbiBhZGRMaXN0ZW5lcijigKYpOiB0aGlzXHJcbi8vIEFsaWFzIHRvIFtgTC5Eb21FdmVudC5vbmBdKCNkb21ldmVudC1vbilcclxuTC5Eb21FdmVudC5hZGRMaXN0ZW5lciA9IEwuRG9tRXZlbnQub247XHJcblxyXG4vLyBAZnVuY3Rpb24gcmVtb3ZlTGlzdGVuZXIo4oCmKTogdGhpc1xyXG4vLyBBbGlhcyB0byBbYEwuRG9tRXZlbnQub2ZmYF0oI2RvbWV2ZW50LW9mZilcclxuTC5Eb21FdmVudC5yZW1vdmVMaXN0ZW5lciA9IEwuRG9tRXZlbnQub2ZmO1xyXG5cblxuXG4vKlxuICogQGNsYXNzIFBvc0FuaW1hdGlvblxuICogQGFrYSBMLlBvc0FuaW1hdGlvblxuICogQGluaGVyaXRzIEV2ZW50ZWRcbiAqIFVzZWQgaW50ZXJuYWxseSBmb3IgcGFubmluZyBhbmltYXRpb25zLCB1dGlsaXppbmcgQ1NTMyBUcmFuc2l0aW9ucyBmb3IgbW9kZXJuIGJyb3dzZXJzIGFuZCBhIHRpbWVyIGZhbGxiYWNrIGZvciBJRTYtOS5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBganNcbiAqIHZhciBmeCA9IG5ldyBMLlBvc0FuaW1hdGlvbigpO1xuICogZngucnVuKGVsLCBbMzAwLCA1MDBdLCAwLjUpO1xuICogYGBgXG4gKlxuICogQGNvbnN0cnVjdG9yIEwuUG9zQW5pbWF0aW9uKClcbiAqIENyZWF0ZXMgYSBgUG9zQW5pbWF0aW9uYCBvYmplY3QuXG4gKlxuICovXG5cbkwuUG9zQW5pbWF0aW9uID0gTC5FdmVudGVkLmV4dGVuZCh7XG5cblx0Ly8gQG1ldGhvZCBydW4oZWw6IEhUTUxFbGVtZW50LCBuZXdQb3M6IFBvaW50LCBkdXJhdGlvbj86IE51bWJlciwgZWFzZUxpbmVhcml0eT86IE51bWJlcilcblx0Ly8gUnVuIGFuIGFuaW1hdGlvbiBvZiBhIGdpdmVuIGVsZW1lbnQgdG8gYSBuZXcgcG9zaXRpb24sIG9wdGlvbmFsbHkgc2V0dGluZ1xuXHQvLyBkdXJhdGlvbiBpbiBzZWNvbmRzIChgMC4yNWAgYnkgZGVmYXVsdCkgYW5kIGVhc2luZyBsaW5lYXJpdHkgZmFjdG9yICgzcmRcblx0Ly8gYXJndW1lbnQgb2YgdGhlIFtjdWJpYyBiZXppZXIgY3VydmVdKGh0dHA6Ly9jdWJpYy1iZXppZXIuY29tLyMwLDAsLjUsMSksXG5cdC8vIGAwLjVgIGJ5IGRlZmF1bHQpLlxuXHRydW46IGZ1bmN0aW9uIChlbCwgbmV3UG9zLCBkdXJhdGlvbiwgZWFzZUxpbmVhcml0eSkge1xuXHRcdHRoaXMuc3RvcCgpO1xuXG5cdFx0dGhpcy5fZWwgPSBlbDtcblx0XHR0aGlzLl9pblByb2dyZXNzID0gdHJ1ZTtcblx0XHR0aGlzLl9kdXJhdGlvbiA9IGR1cmF0aW9uIHx8IDAuMjU7XG5cdFx0dGhpcy5fZWFzZU91dFBvd2VyID0gMSAvIE1hdGgubWF4KGVhc2VMaW5lYXJpdHkgfHwgMC41LCAwLjIpO1xuXG5cdFx0dGhpcy5fc3RhcnRQb3MgPSBMLkRvbVV0aWwuZ2V0UG9zaXRpb24oZWwpO1xuXHRcdHRoaXMuX29mZnNldCA9IG5ld1Bvcy5zdWJ0cmFjdCh0aGlzLl9zdGFydFBvcyk7XG5cdFx0dGhpcy5fc3RhcnRUaW1lID0gK25ldyBEYXRlKCk7XG5cblx0XHQvLyBAZXZlbnQgc3RhcnQ6IEV2ZW50XG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgYW5pbWF0aW9uIHN0YXJ0c1xuXHRcdHRoaXMuZmlyZSgnc3RhcnQnKTtcblxuXHRcdHRoaXMuX2FuaW1hdGUoKTtcblx0fSxcblxuXHQvLyBAbWV0aG9kIHN0b3AoKVxuXHQvLyBTdG9wcyB0aGUgYW5pbWF0aW9uIChpZiBjdXJyZW50bHkgcnVubmluZykuXG5cdHN0b3A6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAoIXRoaXMuX2luUHJvZ3Jlc3MpIHsgcmV0dXJuOyB9XG5cblx0XHR0aGlzLl9zdGVwKHRydWUpO1xuXHRcdHRoaXMuX2NvbXBsZXRlKCk7XG5cdH0sXG5cblx0X2FuaW1hdGU6IGZ1bmN0aW9uICgpIHtcblx0XHQvLyBhbmltYXRpb24gbG9vcFxuXHRcdHRoaXMuX2FuaW1JZCA9IEwuVXRpbC5yZXF1ZXN0QW5pbUZyYW1lKHRoaXMuX2FuaW1hdGUsIHRoaXMpO1xuXHRcdHRoaXMuX3N0ZXAoKTtcblx0fSxcblxuXHRfc3RlcDogZnVuY3Rpb24gKHJvdW5kKSB7XG5cdFx0dmFyIGVsYXBzZWQgPSAoK25ldyBEYXRlKCkpIC0gdGhpcy5fc3RhcnRUaW1lLFxuXHRcdCAgICBkdXJhdGlvbiA9IHRoaXMuX2R1cmF0aW9uICogMTAwMDtcblxuXHRcdGlmIChlbGFwc2VkIDwgZHVyYXRpb24pIHtcblx0XHRcdHRoaXMuX3J1bkZyYW1lKHRoaXMuX2Vhc2VPdXQoZWxhcHNlZCAvIGR1cmF0aW9uKSwgcm91bmQpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aGlzLl9ydW5GcmFtZSgxKTtcblx0XHRcdHRoaXMuX2NvbXBsZXRlKCk7XG5cdFx0fVxuXHR9LFxuXG5cdF9ydW5GcmFtZTogZnVuY3Rpb24gKHByb2dyZXNzLCByb3VuZCkge1xuXHRcdHZhciBwb3MgPSB0aGlzLl9zdGFydFBvcy5hZGQodGhpcy5fb2Zmc2V0Lm11bHRpcGx5QnkocHJvZ3Jlc3MpKTtcblx0XHRpZiAocm91bmQpIHtcblx0XHRcdHBvcy5fcm91bmQoKTtcblx0XHR9XG5cdFx0TC5Eb21VdGlsLnNldFBvc2l0aW9uKHRoaXMuX2VsLCBwb3MpO1xuXG5cdFx0Ly8gQGV2ZW50IHN0ZXA6IEV2ZW50XG5cdFx0Ly8gRmlyZWQgY29udGludW91c2x5IGR1cmluZyB0aGUgYW5pbWF0aW9uLlxuXHRcdHRoaXMuZmlyZSgnc3RlcCcpO1xuXHR9LFxuXG5cdF9jb21wbGV0ZTogZnVuY3Rpb24gKCkge1xuXHRcdEwuVXRpbC5jYW5jZWxBbmltRnJhbWUodGhpcy5fYW5pbUlkKTtcblxuXHRcdHRoaXMuX2luUHJvZ3Jlc3MgPSBmYWxzZTtcblx0XHQvLyBAZXZlbnQgZW5kOiBFdmVudFxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIGFuaW1hdGlvbiBlbmRzLlxuXHRcdHRoaXMuZmlyZSgnZW5kJyk7XG5cdH0sXG5cblx0X2Vhc2VPdXQ6IGZ1bmN0aW9uICh0KSB7XG5cdFx0cmV0dXJuIDEgLSBNYXRoLnBvdygxIC0gdCwgdGhpcy5fZWFzZU91dFBvd2VyKTtcblx0fVxufSk7XG5cblxuXG4vKlxyXG4gKiBAbmFtZXNwYWNlIFByb2plY3Rpb25cclxuICogQHByb2plY3Rpb24gTC5Qcm9qZWN0aW9uLk1lcmNhdG9yXHJcbiAqXHJcbiAqIEVsbGlwdGljYWwgTWVyY2F0b3IgcHJvamVjdGlvbiDigJQgbW9yZSBjb21wbGV4IHRoYW4gU3BoZXJpY2FsIE1lcmNhdG9yLiBUYWtlcyBpbnRvIGFjY291bnQgdGhhdCBFYXJ0aCBpcyBhIGdlb2lkLCBub3QgYSBwZXJmZWN0IHNwaGVyZS4gVXNlZCBieSB0aGUgRVBTRzozMzk1IENSUy5cclxuICovXHJcblxyXG5MLlByb2plY3Rpb24uTWVyY2F0b3IgPSB7XHJcblx0UjogNjM3ODEzNyxcclxuXHRSX01JTk9SOiA2MzU2NzUyLjMxNDI0NTE3OSxcclxuXHJcblx0Ym91bmRzOiBMLmJvdW5kcyhbLTIwMDM3NTA4LjM0Mjc5LCAtMTU0OTY1NzAuNzM5NzJdLCBbMjAwMzc1MDguMzQyNzksIDE4NzY0NjU2LjIzMTM4XSksXHJcblxyXG5cdHByb2plY3Q6IGZ1bmN0aW9uIChsYXRsbmcpIHtcclxuXHRcdHZhciBkID0gTWF0aC5QSSAvIDE4MCxcclxuXHRcdCAgICByID0gdGhpcy5SLFxyXG5cdFx0ICAgIHkgPSBsYXRsbmcubGF0ICogZCxcclxuXHRcdCAgICB0bXAgPSB0aGlzLlJfTUlOT1IgLyByLFxyXG5cdFx0ICAgIGUgPSBNYXRoLnNxcnQoMSAtIHRtcCAqIHRtcCksXHJcblx0XHQgICAgY29uID0gZSAqIE1hdGguc2luKHkpO1xyXG5cclxuXHRcdHZhciB0cyA9IE1hdGgudGFuKE1hdGguUEkgLyA0IC0geSAvIDIpIC8gTWF0aC5wb3coKDEgLSBjb24pIC8gKDEgKyBjb24pLCBlIC8gMik7XHJcblx0XHR5ID0gLXIgKiBNYXRoLmxvZyhNYXRoLm1heCh0cywgMUUtMTApKTtcclxuXHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQobGF0bG5nLmxuZyAqIGQgKiByLCB5KTtcclxuXHR9LFxyXG5cclxuXHR1bnByb2plY3Q6IGZ1bmN0aW9uIChwb2ludCkge1xyXG5cdFx0dmFyIGQgPSAxODAgLyBNYXRoLlBJLFxyXG5cdFx0ICAgIHIgPSB0aGlzLlIsXHJcblx0XHQgICAgdG1wID0gdGhpcy5SX01JTk9SIC8gcixcclxuXHRcdCAgICBlID0gTWF0aC5zcXJ0KDEgLSB0bXAgKiB0bXApLFxyXG5cdFx0ICAgIHRzID0gTWF0aC5leHAoLXBvaW50LnkgLyByKSxcclxuXHRcdCAgICBwaGkgPSBNYXRoLlBJIC8gMiAtIDIgKiBNYXRoLmF0YW4odHMpO1xyXG5cclxuXHRcdGZvciAodmFyIGkgPSAwLCBkcGhpID0gMC4xLCBjb247IGkgPCAxNSAmJiBNYXRoLmFicyhkcGhpKSA+IDFlLTc7IGkrKykge1xyXG5cdFx0XHRjb24gPSBlICogTWF0aC5zaW4ocGhpKTtcclxuXHRcdFx0Y29uID0gTWF0aC5wb3coKDEgLSBjb24pIC8gKDEgKyBjb24pLCBlIC8gMik7XHJcblx0XHRcdGRwaGkgPSBNYXRoLlBJIC8gMiAtIDIgKiBNYXRoLmF0YW4odHMgKiBjb24pIC0gcGhpO1xyXG5cdFx0XHRwaGkgKz0gZHBoaTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gbmV3IEwuTGF0TG5nKHBoaSAqIGQsIHBvaW50LnggKiBkIC8gcik7XHJcblx0fVxyXG59O1xyXG5cblxuXG4vKlxyXG4gKiBAbmFtZXNwYWNlIENSU1xyXG4gKiBAY3JzIEwuQ1JTLkVQU0czMzk1XHJcbiAqXHJcbiAqIFJhcmVseSB1c2VkIGJ5IHNvbWUgY29tbWVyY2lhbCB0aWxlIHByb3ZpZGVycy4gVXNlcyBFbGxpcHRpY2FsIE1lcmNhdG9yIHByb2plY3Rpb24uXHJcbiAqL1xyXG5cclxuTC5DUlMuRVBTRzMzOTUgPSBMLmV4dGVuZCh7fSwgTC5DUlMuRWFydGgsIHtcclxuXHRjb2RlOiAnRVBTRzozMzk1JyxcclxuXHRwcm9qZWN0aW9uOiBMLlByb2plY3Rpb24uTWVyY2F0b3IsXHJcblxyXG5cdHRyYW5zZm9ybWF0aW9uOiAoZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIHNjYWxlID0gMC41IC8gKE1hdGguUEkgKiBMLlByb2plY3Rpb24uTWVyY2F0b3IuUik7XHJcblx0XHRyZXR1cm4gbmV3IEwuVHJhbnNmb3JtYXRpb24oc2NhbGUsIDAuNSwgLXNjYWxlLCAwLjUpO1xyXG5cdH0oKSlcclxufSk7XHJcblxuXG5cbi8qXG4gKiBAY2xhc3MgR3JpZExheWVyXG4gKiBAaW5oZXJpdHMgTGF5ZXJcbiAqIEBha2EgTC5HcmlkTGF5ZXJcbiAqXG4gKiBHZW5lcmljIGNsYXNzIGZvciBoYW5kbGluZyBhIHRpbGVkIGdyaWQgb2YgSFRNTCBlbGVtZW50cy4gVGhpcyBpcyB0aGUgYmFzZSBjbGFzcyBmb3IgYWxsIHRpbGUgbGF5ZXJzIGFuZCByZXBsYWNlcyBgVGlsZUxheWVyLkNhbnZhc2AuXG4gKiBHcmlkTGF5ZXIgY2FuIGJlIGV4dGVuZGVkIHRvIGNyZWF0ZSBhIHRpbGVkIGdyaWQgb2YgSFRNTCBlbGVtZW50cyBsaWtlIGA8Y2FudmFzPmAsIGA8aW1nPmAgb3IgYDxkaXY+YC4gR3JpZExheWVyIHdpbGwgaGFuZGxlIGNyZWF0aW5nIGFuZCBhbmltYXRpbmcgdGhlc2UgRE9NIGVsZW1lbnRzIGZvciB5b3UuXG4gKlxuICpcbiAqIEBzZWN0aW9uIFN5bmNocm9ub3VzIHVzYWdlXG4gKiBAZXhhbXBsZVxuICpcbiAqIFRvIGNyZWF0ZSBhIGN1c3RvbSBsYXllciwgZXh0ZW5kIEdyaWRMYXllciBhbmQgaW1wbGVtZW50IHRoZSBgY3JlYXRlVGlsZSgpYCBtZXRob2QsIHdoaWNoIHdpbGwgYmUgcGFzc2VkIGEgYFBvaW50YCBvYmplY3Qgd2l0aCB0aGUgYHhgLCBgeWAsIGFuZCBgemAgKHpvb20gbGV2ZWwpIGNvb3JkaW5hdGVzIHRvIGRyYXcgeW91ciB0aWxlLlxuICpcbiAqIGBgYGpzXG4gKiB2YXIgQ2FudmFzTGF5ZXIgPSBMLkdyaWRMYXllci5leHRlbmQoe1xuICogICAgIGNyZWF0ZVRpbGU6IGZ1bmN0aW9uKGNvb3Jkcyl7XG4gKiAgICAgICAgIC8vIGNyZWF0ZSBhIDxjYW52YXM+IGVsZW1lbnQgZm9yIGRyYXdpbmdcbiAqICAgICAgICAgdmFyIHRpbGUgPSBMLkRvbVV0aWwuY3JlYXRlKCdjYW52YXMnLCAnbGVhZmxldC10aWxlJyk7XG4gKlxuICogICAgICAgICAvLyBzZXR1cCB0aWxlIHdpZHRoIGFuZCBoZWlnaHQgYWNjb3JkaW5nIHRvIHRoZSBvcHRpb25zXG4gKiAgICAgICAgIHZhciBzaXplID0gdGhpcy5nZXRUaWxlU2l6ZSgpO1xuICogICAgICAgICB0aWxlLndpZHRoID0gc2l6ZS54O1xuICogICAgICAgICB0aWxlLmhlaWdodCA9IHNpemUueTtcbiAqXG4gKiAgICAgICAgIC8vIGdldCBhIGNhbnZhcyBjb250ZXh0IGFuZCBkcmF3IHNvbWV0aGluZyBvbiBpdCB1c2luZyBjb29yZHMueCwgY29vcmRzLnkgYW5kIGNvb3Jkcy56XG4gKiAgICAgICAgIHZhciBjdHggPSB0aWxlLmdldENvbnRleHQoJzJkJyk7XG4gKlxuICogICAgICAgICAvLyByZXR1cm4gdGhlIHRpbGUgc28gaXQgY2FuIGJlIHJlbmRlcmVkIG9uIHNjcmVlblxuICogICAgICAgICByZXR1cm4gdGlsZTtcbiAqICAgICB9XG4gKiB9KTtcbiAqIGBgYFxuICpcbiAqIEBzZWN0aW9uIEFzeW5jaHJvbm91cyB1c2FnZVxuICogQGV4YW1wbGVcbiAqXG4gKiBUaWxlIGNyZWF0aW9uIGNhbiBhbHNvIGJlIGFzeW5jaHJvbm91cywgdGhpcyBpcyB1c2VmdWwgd2hlbiB1c2luZyBhIHRoaXJkLXBhcnR5IGRyYXdpbmcgbGlicmFyeS4gT25jZSB0aGUgdGlsZSBpcyBmaW5pc2hlZCBkcmF3aW5nIGl0IGNhbiBiZSBwYXNzZWQgdG8gdGhlIGBkb25lKClgIGNhbGxiYWNrLlxuICpcbiAqIGBgYGpzXG4gKiB2YXIgQ2FudmFzTGF5ZXIgPSBMLkdyaWRMYXllci5leHRlbmQoe1xuICogICAgIGNyZWF0ZVRpbGU6IGZ1bmN0aW9uKGNvb3JkcywgZG9uZSl7XG4gKiAgICAgICAgIHZhciBlcnJvcjtcbiAqXG4gKiAgICAgICAgIC8vIGNyZWF0ZSBhIDxjYW52YXM+IGVsZW1lbnQgZm9yIGRyYXdpbmdcbiAqICAgICAgICAgdmFyIHRpbGUgPSBMLkRvbVV0aWwuY3JlYXRlKCdjYW52YXMnLCAnbGVhZmxldC10aWxlJyk7XG4gKlxuICogICAgICAgICAvLyBzZXR1cCB0aWxlIHdpZHRoIGFuZCBoZWlnaHQgYWNjb3JkaW5nIHRvIHRoZSBvcHRpb25zXG4gKiAgICAgICAgIHZhciBzaXplID0gdGhpcy5nZXRUaWxlU2l6ZSgpO1xuICogICAgICAgICB0aWxlLndpZHRoID0gc2l6ZS54O1xuICogICAgICAgICB0aWxlLmhlaWdodCA9IHNpemUueTtcbiAqXG4gKiAgICAgICAgIC8vIGRyYXcgc29tZXRoaW5nIGFzeW5jaHJvbm91c2x5IGFuZCBwYXNzIHRoZSB0aWxlIHRvIHRoZSBkb25lKCkgY2FsbGJhY2tcbiAqICAgICAgICAgc2V0VGltZW91dChmdW5jdGlvbigpIHtcbiAqICAgICAgICAgICAgIGRvbmUoZXJyb3IsIHRpbGUpO1xuICogICAgICAgICB9LCAxMDAwKTtcbiAqXG4gKiAgICAgICAgIHJldHVybiB0aWxlO1xuICogICAgIH1cbiAqIH0pO1xuICogYGBgXG4gKlxuICogQHNlY3Rpb25cbiAqL1xuXG5cbkwuR3JpZExheWVyID0gTC5MYXllci5leHRlbmQoe1xuXG5cdC8vIEBzZWN0aW9uXG5cdC8vIEBha2EgR3JpZExheWVyIG9wdGlvbnNcblx0b3B0aW9uczoge1xuXHRcdC8vIEBvcHRpb24gdGlsZVNpemU6IE51bWJlcnxQb2ludCA9IDI1NlxuXHRcdC8vIFdpZHRoIGFuZCBoZWlnaHQgb2YgdGlsZXMgaW4gdGhlIGdyaWQuIFVzZSBhIG51bWJlciBpZiB3aWR0aCBhbmQgaGVpZ2h0IGFyZSBlcXVhbCwgb3IgYEwucG9pbnQod2lkdGgsIGhlaWdodClgIG90aGVyd2lzZS5cblx0XHR0aWxlU2l6ZTogMjU2LFxuXG5cdFx0Ly8gQG9wdGlvbiBvcGFjaXR5OiBOdW1iZXIgPSAxLjBcblx0XHQvLyBPcGFjaXR5IG9mIHRoZSB0aWxlcy4gQ2FuIGJlIHVzZWQgaW4gdGhlIGBjcmVhdGVUaWxlKClgIGZ1bmN0aW9uLlxuXHRcdG9wYWNpdHk6IDEsXG5cblx0XHQvLyBAb3B0aW9uIHVwZGF0ZVdoZW5JZGxlOiBCb29sZWFuID0gZGVwZW5kc1xuXHRcdC8vIElmIGBmYWxzZWAsIG5ldyB0aWxlcyBhcmUgbG9hZGVkIGR1cmluZyBwYW5uaW5nLCBvdGhlcndpc2Ugb25seSBhZnRlciBpdCAoZm9yIGJldHRlciBwZXJmb3JtYW5jZSkuIGB0cnVlYCBieSBkZWZhdWx0IG9uIG1vYmlsZSBicm93c2Vycywgb3RoZXJ3aXNlIGBmYWxzZWAuXG5cdFx0dXBkYXRlV2hlbklkbGU6IEwuQnJvd3Nlci5tb2JpbGUsXG5cblx0XHQvLyBAb3B0aW9uIHVwZGF0ZVdoZW5ab29taW5nOiBCb29sZWFuID0gdHJ1ZVxuXHRcdC8vIEJ5IGRlZmF1bHQsIGEgc21vb3RoIHpvb20gYW5pbWF0aW9uIChkdXJpbmcgYSBbdG91Y2ggem9vbV0oI21hcC10b3VjaHpvb20pIG9yIGEgW2BmbHlUbygpYF0oI21hcC1mbHl0bykpIHdpbGwgdXBkYXRlIGdyaWQgbGF5ZXJzIGV2ZXJ5IGludGVnZXIgem9vbSBsZXZlbC4gU2V0dGluZyB0aGlzIG9wdGlvbiB0byBgZmFsc2VgIHdpbGwgdXBkYXRlIHRoZSBncmlkIGxheWVyIG9ubHkgd2hlbiB0aGUgc21vb3RoIGFuaW1hdGlvbiBlbmRzLlxuXHRcdHVwZGF0ZVdoZW5ab29taW5nOiB0cnVlLFxuXG5cdFx0Ly8gQG9wdGlvbiB1cGRhdGVJbnRlcnZhbDogTnVtYmVyID0gMjAwXG5cdFx0Ly8gVGlsZXMgd2lsbCBub3QgdXBkYXRlIG1vcmUgdGhhbiBvbmNlIGV2ZXJ5IGB1cGRhdGVJbnRlcnZhbGAgbWlsbGlzZWNvbmRzIHdoZW4gcGFubmluZy5cblx0XHR1cGRhdGVJbnRlcnZhbDogMjAwLFxuXG5cdFx0Ly8gQG9wdGlvbiB6SW5kZXg6IE51bWJlciA9IDFcblx0XHQvLyBUaGUgZXhwbGljaXQgekluZGV4IG9mIHRoZSB0aWxlIGxheWVyLlxuXHRcdHpJbmRleDogMSxcblxuXHRcdC8vIEBvcHRpb24gYm91bmRzOiBMYXRMbmdCb3VuZHMgPSB1bmRlZmluZWRcblx0XHQvLyBJZiBzZXQsIHRpbGVzIHdpbGwgb25seSBiZSBsb2FkZWQgaW5zaWRlIHRoZSBzZXQgYExhdExuZ0JvdW5kc2AuXG5cdFx0Ym91bmRzOiBudWxsLFxuXG5cdFx0Ly8gQG9wdGlvbiBtaW5ab29tOiBOdW1iZXIgPSAwXG5cdFx0Ly8gVGhlIG1pbmltdW0gem9vbSBsZXZlbCB0aGF0IHRpbGVzIHdpbGwgYmUgbG9hZGVkIGF0LiBCeSBkZWZhdWx0IHRoZSBlbnRpcmUgbWFwLlxuXHRcdG1pblpvb206IDAsXG5cblx0XHQvLyBAb3B0aW9uIG1heFpvb206IE51bWJlciA9IHVuZGVmaW5lZFxuXHRcdC8vIFRoZSBtYXhpbXVtIHpvb20gbGV2ZWwgdGhhdCB0aWxlcyB3aWxsIGJlIGxvYWRlZCBhdC5cblx0XHRtYXhab29tOiB1bmRlZmluZWQsXG5cblx0XHQvLyBAb3B0aW9uIG5vV3JhcDogQm9vbGVhbiA9IGZhbHNlXG5cdFx0Ly8gV2hldGhlciB0aGUgbGF5ZXIgaXMgd3JhcHBlZCBhcm91bmQgdGhlIGFudGltZXJpZGlhbi4gSWYgYHRydWVgLCB0aGVcblx0XHQvLyBHcmlkTGF5ZXIgd2lsbCBvbmx5IGJlIGRpc3BsYXllZCBvbmNlIGF0IGxvdyB6b29tIGxldmVscy4gSGFzIG5vXG5cdFx0Ly8gZWZmZWN0IHdoZW4gdGhlIFttYXAgQ1JTXSgjbWFwLWNycykgZG9lc24ndCB3cmFwIGFyb3VuZC5cblx0XHRub1dyYXA6IGZhbHNlLFxuXG5cdFx0Ly8gQG9wdGlvbiBwYW5lOiBTdHJpbmcgPSAndGlsZVBhbmUnXG5cdFx0Ly8gYE1hcCBwYW5lYCB3aGVyZSB0aGUgZ3JpZCBsYXllciB3aWxsIGJlIGFkZGVkLlxuXHRcdHBhbmU6ICd0aWxlUGFuZScsXG5cblx0XHQvLyBAb3B0aW9uIGNsYXNzTmFtZTogU3RyaW5nID0gJydcblx0XHQvLyBBIGN1c3RvbSBjbGFzcyBuYW1lIHRvIGFzc2lnbiB0byB0aGUgdGlsZSBsYXllci4gRW1wdHkgYnkgZGVmYXVsdC5cblx0XHRjbGFzc05hbWU6ICcnLFxuXG5cdFx0Ly8gQG9wdGlvbiBrZWVwQnVmZmVyOiBOdW1iZXIgPSAyXG5cdFx0Ly8gV2hlbiBwYW5uaW5nIHRoZSBtYXAsIGtlZXAgdGhpcyBtYW55IHJvd3MgYW5kIGNvbHVtbnMgb2YgdGlsZXMgYmVmb3JlIHVubG9hZGluZyB0aGVtLlxuXHRcdGtlZXBCdWZmZXI6IDJcblx0fSxcblxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAob3B0aW9ucykge1xuXHRcdEwuc2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcblx0fSxcblxuXHRvbkFkZDogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX2luaXRDb250YWluZXIoKTtcblxuXHRcdHRoaXMuX2xldmVscyA9IHt9O1xuXHRcdHRoaXMuX3RpbGVzID0ge307XG5cblx0XHR0aGlzLl9yZXNldFZpZXcoKTtcblx0XHR0aGlzLl91cGRhdGUoKTtcblx0fSxcblxuXHRiZWZvcmVBZGQ6IGZ1bmN0aW9uIChtYXApIHtcblx0XHRtYXAuX2FkZFpvb21MaW1pdCh0aGlzKTtcblx0fSxcblxuXHRvblJlbW92ZTogZnVuY3Rpb24gKG1hcCkge1xuXHRcdHRoaXMuX3JlbW92ZUFsbFRpbGVzKCk7XG5cdFx0TC5Eb21VdGlsLnJlbW92ZSh0aGlzLl9jb250YWluZXIpO1xuXHRcdG1hcC5fcmVtb3ZlWm9vbUxpbWl0KHRoaXMpO1xuXHRcdHRoaXMuX2NvbnRhaW5lciA9IG51bGw7XG5cdFx0dGhpcy5fdGlsZVpvb20gPSBudWxsO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgYnJpbmdUb0Zyb250OiB0aGlzXG5cdC8vIEJyaW5ncyB0aGUgdGlsZSBsYXllciB0byB0aGUgdG9wIG9mIGFsbCB0aWxlIGxheWVycy5cblx0YnJpbmdUb0Zyb250OiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX21hcCkge1xuXHRcdFx0TC5Eb21VdGlsLnRvRnJvbnQodGhpcy5fY29udGFpbmVyKTtcblx0XHRcdHRoaXMuX3NldEF1dG9aSW5kZXgoTWF0aC5tYXgpO1xuXHRcdH1cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIGJyaW5nVG9CYWNrOiB0aGlzXG5cdC8vIEJyaW5ncyB0aGUgdGlsZSBsYXllciB0byB0aGUgYm90dG9tIG9mIGFsbCB0aWxlIGxheWVycy5cblx0YnJpbmdUb0JhY2s6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fbWFwKSB7XG5cdFx0XHRMLkRvbVV0aWwudG9CYWNrKHRoaXMuX2NvbnRhaW5lcik7XG5cdFx0XHR0aGlzLl9zZXRBdXRvWkluZGV4KE1hdGgubWluKTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBnZXRDb250YWluZXI6IEhUTUxFbGVtZW50XG5cdC8vIFJldHVybnMgdGhlIEhUTUwgZWxlbWVudCB0aGF0IGNvbnRhaW5zIHRoZSB0aWxlcyBmb3IgdGhpcyBsYXllci5cblx0Z2V0Q29udGFpbmVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX2NvbnRhaW5lcjtcblx0fSxcblxuXHQvLyBAbWV0aG9kIHNldE9wYWNpdHkob3BhY2l0eTogTnVtYmVyKTogdGhpc1xuXHQvLyBDaGFuZ2VzIHRoZSBbb3BhY2l0eV0oI2dyaWRsYXllci1vcGFjaXR5KSBvZiB0aGUgZ3JpZCBsYXllci5cblx0c2V0T3BhY2l0eTogZnVuY3Rpb24gKG9wYWNpdHkpIHtcblx0XHR0aGlzLm9wdGlvbnMub3BhY2l0eSA9IG9wYWNpdHk7XG5cdFx0dGhpcy5fdXBkYXRlT3BhY2l0eSgpO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2Qgc2V0WkluZGV4KHpJbmRleDogTnVtYmVyKTogdGhpc1xuXHQvLyBDaGFuZ2VzIHRoZSBbekluZGV4XSgjZ3JpZGxheWVyLXppbmRleCkgb2YgdGhlIGdyaWQgbGF5ZXIuXG5cdHNldFpJbmRleDogZnVuY3Rpb24gKHpJbmRleCkge1xuXHRcdHRoaXMub3B0aW9ucy56SW5kZXggPSB6SW5kZXg7XG5cdFx0dGhpcy5fdXBkYXRlWkluZGV4KCk7XG5cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIGlzTG9hZGluZzogQm9vbGVhblxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiBhbnkgdGlsZSBpbiB0aGUgZ3JpZCBsYXllciBoYXMgbm90IGZpbmlzaGVkIGxvYWRpbmcuXG5cdGlzTG9hZGluZzogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9sb2FkaW5nO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgcmVkcmF3OiB0aGlzXG5cdC8vIENhdXNlcyB0aGUgbGF5ZXIgdG8gY2xlYXIgYWxsIHRoZSB0aWxlcyBhbmQgcmVxdWVzdCB0aGVtIGFnYWluLlxuXHRyZWRyYXc6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fbWFwKSB7XG5cdFx0XHR0aGlzLl9yZW1vdmVBbGxUaWxlcygpO1xuXHRcdFx0dGhpcy5fdXBkYXRlKCk7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdGdldEV2ZW50czogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBldmVudHMgPSB7XG5cdFx0XHR2aWV3cHJlcmVzZXQ6IHRoaXMuX2ludmFsaWRhdGVBbGwsXG5cdFx0XHR2aWV3cmVzZXQ6IHRoaXMuX3Jlc2V0Vmlldyxcblx0XHRcdHpvb206IHRoaXMuX3Jlc2V0Vmlldyxcblx0XHRcdG1vdmVlbmQ6IHRoaXMuX29uTW92ZUVuZFxuXHRcdH07XG5cblx0XHRpZiAoIXRoaXMub3B0aW9ucy51cGRhdGVXaGVuSWRsZSkge1xuXHRcdFx0Ly8gdXBkYXRlIHRpbGVzIG9uIG1vdmUsIGJ1dCBub3QgbW9yZSBvZnRlbiB0aGFuIG9uY2UgcGVyIGdpdmVuIGludGVydmFsXG5cdFx0XHRpZiAoIXRoaXMuX29uTW92ZSkge1xuXHRcdFx0XHR0aGlzLl9vbk1vdmUgPSBMLlV0aWwudGhyb3R0bGUodGhpcy5fb25Nb3ZlRW5kLCB0aGlzLm9wdGlvbnMudXBkYXRlSW50ZXJ2YWwsIHRoaXMpO1xuXHRcdFx0fVxuXG5cdFx0XHRldmVudHMubW92ZSA9IHRoaXMuX29uTW92ZTtcblx0XHR9XG5cblx0XHRpZiAodGhpcy5fem9vbUFuaW1hdGVkKSB7XG5cdFx0XHRldmVudHMuem9vbWFuaW0gPSB0aGlzLl9hbmltYXRlWm9vbTtcblx0XHR9XG5cblx0XHRyZXR1cm4gZXZlbnRzO1xuXHR9LFxuXG5cdC8vIEBzZWN0aW9uIEV4dGVuc2lvbiBtZXRob2RzXG5cdC8vIExheWVycyBleHRlbmRpbmcgYEdyaWRMYXllcmAgc2hhbGwgcmVpbXBsZW1lbnQgdGhlIGZvbGxvd2luZyBtZXRob2QuXG5cdC8vIEBtZXRob2QgY3JlYXRlVGlsZShjb29yZHM6IE9iamVjdCwgZG9uZT86IEZ1bmN0aW9uKTogSFRNTEVsZW1lbnRcblx0Ly8gQ2FsbGVkIG9ubHkgaW50ZXJuYWxseSwgbXVzdCBiZSBvdmVycmlkZW4gYnkgY2xhc3NlcyBleHRlbmRpbmcgYEdyaWRMYXllcmAuXG5cdC8vIFJldHVybnMgdGhlIGBIVE1MRWxlbWVudGAgY29ycmVzcG9uZGluZyB0byB0aGUgZ2l2ZW4gYGNvb3Jkc2AuIElmIHRoZSBgZG9uZWAgY2FsbGJhY2tcblx0Ly8gaXMgc3BlY2lmaWVkLCBpdCBtdXN0IGJlIGNhbGxlZCB3aGVuIHRoZSB0aWxlIGhhcyBmaW5pc2hlZCBsb2FkaW5nIGFuZCBkcmF3aW5nLlxuXHRjcmVhdGVUaWxlOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xuXHR9LFxuXG5cdC8vIEBzZWN0aW9uXG5cdC8vIEBtZXRob2QgZ2V0VGlsZVNpemU6IFBvaW50XG5cdC8vIE5vcm1hbGl6ZXMgdGhlIFt0aWxlU2l6ZSBvcHRpb25dKCNncmlkbGF5ZXItdGlsZXNpemUpIGludG8gYSBwb2ludC4gVXNlZCBieSB0aGUgYGNyZWF0ZVRpbGUoKWAgbWV0aG9kLlxuXHRnZXRUaWxlU2l6ZTogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBzID0gdGhpcy5vcHRpb25zLnRpbGVTaXplO1xuXHRcdHJldHVybiBzIGluc3RhbmNlb2YgTC5Qb2ludCA/IHMgOiBuZXcgTC5Qb2ludChzLCBzKTtcblx0fSxcblxuXHRfdXBkYXRlWkluZGV4OiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX2NvbnRhaW5lciAmJiB0aGlzLm9wdGlvbnMuekluZGV4ICE9PSB1bmRlZmluZWQgJiYgdGhpcy5vcHRpb25zLnpJbmRleCAhPT0gbnVsbCkge1xuXHRcdFx0dGhpcy5fY29udGFpbmVyLnN0eWxlLnpJbmRleCA9IHRoaXMub3B0aW9ucy56SW5kZXg7XG5cdFx0fVxuXHR9LFxuXG5cdF9zZXRBdXRvWkluZGV4OiBmdW5jdGlvbiAoY29tcGFyZSkge1xuXHRcdC8vIGdvIHRocm91Z2ggYWxsIG90aGVyIGxheWVycyBvZiB0aGUgc2FtZSBwYW5lLCBzZXQgekluZGV4IHRvIG1heCArIDEgKGZyb250KSBvciBtaW4gLSAxIChiYWNrKVxuXG5cdFx0dmFyIGxheWVycyA9IHRoaXMuZ2V0UGFuZSgpLmNoaWxkcmVuLFxuXHRcdCAgICBlZGdlWkluZGV4ID0gLWNvbXBhcmUoLUluZmluaXR5LCBJbmZpbml0eSk7IC8vIC1JbmZpbml0eSBmb3IgbWF4LCBJbmZpbml0eSBmb3IgbWluXG5cblx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gbGF5ZXJzLmxlbmd0aCwgekluZGV4OyBpIDwgbGVuOyBpKyspIHtcblxuXHRcdFx0ekluZGV4ID0gbGF5ZXJzW2ldLnN0eWxlLnpJbmRleDtcblxuXHRcdFx0aWYgKGxheWVyc1tpXSAhPT0gdGhpcy5fY29udGFpbmVyICYmIHpJbmRleCkge1xuXHRcdFx0XHRlZGdlWkluZGV4ID0gY29tcGFyZShlZGdlWkluZGV4LCArekluZGV4KTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiAoaXNGaW5pdGUoZWRnZVpJbmRleCkpIHtcblx0XHRcdHRoaXMub3B0aW9ucy56SW5kZXggPSBlZGdlWkluZGV4ICsgY29tcGFyZSgtMSwgMSk7XG5cdFx0XHR0aGlzLl91cGRhdGVaSW5kZXgoKTtcblx0XHR9XG5cdH0sXG5cblx0X3VwZGF0ZU9wYWNpdHk6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAoIXRoaXMuX21hcCkgeyByZXR1cm47IH1cblxuXHRcdC8vIElFIGRvZXNuJ3QgaW5oZXJpdCBmaWx0ZXIgb3BhY2l0eSBwcm9wZXJseSwgc28gd2UncmUgZm9yY2VkIHRvIHNldCBpdCBvbiB0aWxlc1xuXHRcdGlmIChMLkJyb3dzZXIuaWVsdDkpIHsgcmV0dXJuOyB9XG5cblx0XHRMLkRvbVV0aWwuc2V0T3BhY2l0eSh0aGlzLl9jb250YWluZXIsIHRoaXMub3B0aW9ucy5vcGFjaXR5KTtcblxuXHRcdHZhciBub3cgPSArbmV3IERhdGUoKSxcblx0XHQgICAgbmV4dEZyYW1lID0gZmFsc2UsXG5cdFx0ICAgIHdpbGxQcnVuZSA9IGZhbHNlO1xuXG5cdFx0Zm9yICh2YXIga2V5IGluIHRoaXMuX3RpbGVzKSB7XG5cdFx0XHR2YXIgdGlsZSA9IHRoaXMuX3RpbGVzW2tleV07XG5cdFx0XHRpZiAoIXRpbGUuY3VycmVudCB8fCAhdGlsZS5sb2FkZWQpIHsgY29udGludWU7IH1cblxuXHRcdFx0dmFyIGZhZGUgPSBNYXRoLm1pbigxLCAobm93IC0gdGlsZS5sb2FkZWQpIC8gMjAwKTtcblxuXHRcdFx0TC5Eb21VdGlsLnNldE9wYWNpdHkodGlsZS5lbCwgZmFkZSk7XG5cdFx0XHRpZiAoZmFkZSA8IDEpIHtcblx0XHRcdFx0bmV4dEZyYW1lID0gdHJ1ZTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdGlmICh0aWxlLmFjdGl2ZSkgeyB3aWxsUHJ1bmUgPSB0cnVlOyB9XG5cdFx0XHRcdHRpbGUuYWN0aXZlID0gdHJ1ZTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiAod2lsbFBydW5lICYmICF0aGlzLl9ub1BydW5lKSB7IHRoaXMuX3BydW5lVGlsZXMoKTsgfVxuXG5cdFx0aWYgKG5leHRGcmFtZSkge1xuXHRcdFx0TC5VdGlsLmNhbmNlbEFuaW1GcmFtZSh0aGlzLl9mYWRlRnJhbWUpO1xuXHRcdFx0dGhpcy5fZmFkZUZyYW1lID0gTC5VdGlsLnJlcXVlc3RBbmltRnJhbWUodGhpcy5fdXBkYXRlT3BhY2l0eSwgdGhpcyk7XG5cdFx0fVxuXHR9LFxuXG5cdF9pbml0Q29udGFpbmVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX2NvbnRhaW5lcikgeyByZXR1cm47IH1cblxuXHRcdHRoaXMuX2NvbnRhaW5lciA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LWxheWVyICcgKyAodGhpcy5vcHRpb25zLmNsYXNzTmFtZSB8fCAnJykpO1xuXHRcdHRoaXMuX3VwZGF0ZVpJbmRleCgpO1xuXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5vcGFjaXR5IDwgMSkge1xuXHRcdFx0dGhpcy5fdXBkYXRlT3BhY2l0eSgpO1xuXHRcdH1cblxuXHRcdHRoaXMuZ2V0UGFuZSgpLmFwcGVuZENoaWxkKHRoaXMuX2NvbnRhaW5lcik7XG5cdH0sXG5cblx0X3VwZGF0ZUxldmVsczogZnVuY3Rpb24gKCkge1xuXG5cdFx0dmFyIHpvb20gPSB0aGlzLl90aWxlWm9vbSxcblx0XHQgICAgbWF4Wm9vbSA9IHRoaXMub3B0aW9ucy5tYXhab29tO1xuXG5cdFx0aWYgKHpvb20gPT09IHVuZGVmaW5lZCkgeyByZXR1cm4gdW5kZWZpbmVkOyB9XG5cblx0XHRmb3IgKHZhciB6IGluIHRoaXMuX2xldmVscykge1xuXHRcdFx0aWYgKHRoaXMuX2xldmVsc1t6XS5lbC5jaGlsZHJlbi5sZW5ndGggfHwgeiA9PT0gem9vbSkge1xuXHRcdFx0XHR0aGlzLl9sZXZlbHNbel0uZWwuc3R5bGUuekluZGV4ID0gbWF4Wm9vbSAtIE1hdGguYWJzKHpvb20gLSB6KTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdEwuRG9tVXRpbC5yZW1vdmUodGhpcy5fbGV2ZWxzW3pdLmVsKTtcblx0XHRcdFx0dGhpcy5fcmVtb3ZlVGlsZXNBdFpvb20oeik7XG5cdFx0XHRcdGRlbGV0ZSB0aGlzLl9sZXZlbHNbel07XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0dmFyIGxldmVsID0gdGhpcy5fbGV2ZWxzW3pvb21dLFxuXHRcdCAgICBtYXAgPSB0aGlzLl9tYXA7XG5cblx0XHRpZiAoIWxldmVsKSB7XG5cdFx0XHRsZXZlbCA9IHRoaXMuX2xldmVsc1t6b29tXSA9IHt9O1xuXG5cdFx0XHRsZXZlbC5lbCA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LXRpbGUtY29udGFpbmVyIGxlYWZsZXQtem9vbS1hbmltYXRlZCcsIHRoaXMuX2NvbnRhaW5lcik7XG5cdFx0XHRsZXZlbC5lbC5zdHlsZS56SW5kZXggPSBtYXhab29tO1xuXG5cdFx0XHRsZXZlbC5vcmlnaW4gPSBtYXAucHJvamVjdChtYXAudW5wcm9qZWN0KG1hcC5nZXRQaXhlbE9yaWdpbigpKSwgem9vbSkucm91bmQoKTtcblx0XHRcdGxldmVsLnpvb20gPSB6b29tO1xuXG5cdFx0XHR0aGlzLl9zZXRab29tVHJhbnNmb3JtKGxldmVsLCBtYXAuZ2V0Q2VudGVyKCksIG1hcC5nZXRab29tKCkpO1xuXG5cdFx0XHQvLyBmb3JjZSB0aGUgYnJvd3NlciB0byBjb25zaWRlciB0aGUgbmV3bHkgYWRkZWQgZWxlbWVudCBmb3IgdHJhbnNpdGlvblxuXHRcdFx0TC5VdGlsLmZhbHNlRm4obGV2ZWwuZWwub2Zmc2V0V2lkdGgpO1xuXHRcdH1cblxuXHRcdHRoaXMuX2xldmVsID0gbGV2ZWw7XG5cblx0XHRyZXR1cm4gbGV2ZWw7XG5cdH0sXG5cblx0X3BydW5lVGlsZXM6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAoIXRoaXMuX21hcCkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdHZhciBrZXksIHRpbGU7XG5cblx0XHR2YXIgem9vbSA9IHRoaXMuX21hcC5nZXRab29tKCk7XG5cdFx0aWYgKHpvb20gPiB0aGlzLm9wdGlvbnMubWF4Wm9vbSB8fFxuXHRcdFx0em9vbSA8IHRoaXMub3B0aW9ucy5taW5ab29tKSB7XG5cdFx0XHR0aGlzLl9yZW1vdmVBbGxUaWxlcygpO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdGZvciAoa2V5IGluIHRoaXMuX3RpbGVzKSB7XG5cdFx0XHR0aWxlID0gdGhpcy5fdGlsZXNba2V5XTtcblx0XHRcdHRpbGUucmV0YWluID0gdGlsZS5jdXJyZW50O1xuXHRcdH1cblxuXHRcdGZvciAoa2V5IGluIHRoaXMuX3RpbGVzKSB7XG5cdFx0XHR0aWxlID0gdGhpcy5fdGlsZXNba2V5XTtcblx0XHRcdGlmICh0aWxlLmN1cnJlbnQgJiYgIXRpbGUuYWN0aXZlKSB7XG5cdFx0XHRcdHZhciBjb29yZHMgPSB0aWxlLmNvb3Jkcztcblx0XHRcdFx0aWYgKCF0aGlzLl9yZXRhaW5QYXJlbnQoY29vcmRzLngsIGNvb3Jkcy55LCBjb29yZHMueiwgY29vcmRzLnogLSA1KSkge1xuXHRcdFx0XHRcdHRoaXMuX3JldGFpbkNoaWxkcmVuKGNvb3Jkcy54LCBjb29yZHMueSwgY29vcmRzLnosIGNvb3Jkcy56ICsgMik7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cblx0XHRmb3IgKGtleSBpbiB0aGlzLl90aWxlcykge1xuXHRcdFx0aWYgKCF0aGlzLl90aWxlc1trZXldLnJldGFpbikge1xuXHRcdFx0XHR0aGlzLl9yZW1vdmVUaWxlKGtleSk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9LFxuXG5cdF9yZW1vdmVUaWxlc0F0Wm9vbTogZnVuY3Rpb24gKHpvb20pIHtcblx0XHRmb3IgKHZhciBrZXkgaW4gdGhpcy5fdGlsZXMpIHtcblx0XHRcdGlmICh0aGlzLl90aWxlc1trZXldLmNvb3Jkcy56ICE9PSB6b29tKSB7XG5cdFx0XHRcdGNvbnRpbnVlO1xuXHRcdFx0fVxuXHRcdFx0dGhpcy5fcmVtb3ZlVGlsZShrZXkpO1xuXHRcdH1cblx0fSxcblxuXHRfcmVtb3ZlQWxsVGlsZXM6IGZ1bmN0aW9uICgpIHtcblx0XHRmb3IgKHZhciBrZXkgaW4gdGhpcy5fdGlsZXMpIHtcblx0XHRcdHRoaXMuX3JlbW92ZVRpbGUoa2V5KTtcblx0XHR9XG5cdH0sXG5cblx0X2ludmFsaWRhdGVBbGw6IGZ1bmN0aW9uICgpIHtcblx0XHRmb3IgKHZhciB6IGluIHRoaXMuX2xldmVscykge1xuXHRcdFx0TC5Eb21VdGlsLnJlbW92ZSh0aGlzLl9sZXZlbHNbel0uZWwpO1xuXHRcdFx0ZGVsZXRlIHRoaXMuX2xldmVsc1t6XTtcblx0XHR9XG5cdFx0dGhpcy5fcmVtb3ZlQWxsVGlsZXMoKTtcblxuXHRcdHRoaXMuX3RpbGVab29tID0gbnVsbDtcblx0fSxcblxuXHRfcmV0YWluUGFyZW50OiBmdW5jdGlvbiAoeCwgeSwgeiwgbWluWm9vbSkge1xuXHRcdHZhciB4MiA9IE1hdGguZmxvb3IoeCAvIDIpLFxuXHRcdCAgICB5MiA9IE1hdGguZmxvb3IoeSAvIDIpLFxuXHRcdCAgICB6MiA9IHogLSAxLFxuXHRcdCAgICBjb29yZHMyID0gbmV3IEwuUG9pbnQoK3gyLCAreTIpO1xuXHRcdGNvb3JkczIueiA9ICt6MjtcblxuXHRcdHZhciBrZXkgPSB0aGlzLl90aWxlQ29vcmRzVG9LZXkoY29vcmRzMiksXG5cdFx0ICAgIHRpbGUgPSB0aGlzLl90aWxlc1trZXldO1xuXG5cdFx0aWYgKHRpbGUgJiYgdGlsZS5hY3RpdmUpIHtcblx0XHRcdHRpbGUucmV0YWluID0gdHJ1ZTtcblx0XHRcdHJldHVybiB0cnVlO1xuXG5cdFx0fSBlbHNlIGlmICh0aWxlICYmIHRpbGUubG9hZGVkKSB7XG5cdFx0XHR0aWxlLnJldGFpbiA9IHRydWU7XG5cdFx0fVxuXG5cdFx0aWYgKHoyID4gbWluWm9vbSkge1xuXHRcdFx0cmV0dXJuIHRoaXMuX3JldGFpblBhcmVudCh4MiwgeTIsIHoyLCBtaW5ab29tKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gZmFsc2U7XG5cdH0sXG5cblx0X3JldGFpbkNoaWxkcmVuOiBmdW5jdGlvbiAoeCwgeSwgeiwgbWF4Wm9vbSkge1xuXG5cdFx0Zm9yICh2YXIgaSA9IDIgKiB4OyBpIDwgMiAqIHggKyAyOyBpKyspIHtcblx0XHRcdGZvciAodmFyIGogPSAyICogeTsgaiA8IDIgKiB5ICsgMjsgaisrKSB7XG5cblx0XHRcdFx0dmFyIGNvb3JkcyA9IG5ldyBMLlBvaW50KGksIGopO1xuXHRcdFx0XHRjb29yZHMueiA9IHogKyAxO1xuXG5cdFx0XHRcdHZhciBrZXkgPSB0aGlzLl90aWxlQ29vcmRzVG9LZXkoY29vcmRzKSxcblx0XHRcdFx0ICAgIHRpbGUgPSB0aGlzLl90aWxlc1trZXldO1xuXG5cdFx0XHRcdGlmICh0aWxlICYmIHRpbGUuYWN0aXZlKSB7XG5cdFx0XHRcdFx0dGlsZS5yZXRhaW4gPSB0cnVlO1xuXHRcdFx0XHRcdGNvbnRpbnVlO1xuXG5cdFx0XHRcdH0gZWxzZSBpZiAodGlsZSAmJiB0aWxlLmxvYWRlZCkge1xuXHRcdFx0XHRcdHRpbGUucmV0YWluID0gdHJ1ZTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICh6ICsgMSA8IG1heFpvb20pIHtcblx0XHRcdFx0XHR0aGlzLl9yZXRhaW5DaGlsZHJlbihpLCBqLCB6ICsgMSwgbWF4Wm9vbSk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdH0sXG5cblx0X3Jlc2V0VmlldzogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgYW5pbWF0aW5nID0gZSAmJiAoZS5waW5jaCB8fCBlLmZseVRvKTtcblx0XHR0aGlzLl9zZXRWaWV3KHRoaXMuX21hcC5nZXRDZW50ZXIoKSwgdGhpcy5fbWFwLmdldFpvb20oKSwgYW5pbWF0aW5nLCBhbmltYXRpbmcpO1xuXHR9LFxuXG5cdF9hbmltYXRlWm9vbTogZnVuY3Rpb24gKGUpIHtcblx0XHR0aGlzLl9zZXRWaWV3KGUuY2VudGVyLCBlLnpvb20sIHRydWUsIGUubm9VcGRhdGUpO1xuXHR9LFxuXG5cdF9zZXRWaWV3OiBmdW5jdGlvbiAoY2VudGVyLCB6b29tLCBub1BydW5lLCBub1VwZGF0ZSkge1xuXHRcdHZhciB0aWxlWm9vbSA9IE1hdGgucm91bmQoem9vbSk7XG5cdFx0aWYgKCh0aGlzLm9wdGlvbnMubWF4Wm9vbSAhPT0gdW5kZWZpbmVkICYmIHRpbGVab29tID4gdGhpcy5vcHRpb25zLm1heFpvb20pIHx8XG5cdFx0ICAgICh0aGlzLm9wdGlvbnMubWluWm9vbSAhPT0gdW5kZWZpbmVkICYmIHRpbGVab29tIDwgdGhpcy5vcHRpb25zLm1pblpvb20pKSB7XG5cdFx0XHR0aWxlWm9vbSA9IHVuZGVmaW5lZDtcblx0XHR9XG5cblx0XHR2YXIgdGlsZVpvb21DaGFuZ2VkID0gdGhpcy5vcHRpb25zLnVwZGF0ZVdoZW5ab29taW5nICYmICh0aWxlWm9vbSAhPT0gdGhpcy5fdGlsZVpvb20pO1xuXG5cdFx0aWYgKCFub1VwZGF0ZSB8fCB0aWxlWm9vbUNoYW5nZWQpIHtcblxuXHRcdFx0dGhpcy5fdGlsZVpvb20gPSB0aWxlWm9vbTtcblxuXHRcdFx0aWYgKHRoaXMuX2Fib3J0TG9hZGluZykge1xuXHRcdFx0XHR0aGlzLl9hYm9ydExvYWRpbmcoKTtcblx0XHRcdH1cblxuXHRcdFx0dGhpcy5fdXBkYXRlTGV2ZWxzKCk7XG5cdFx0XHR0aGlzLl9yZXNldEdyaWQoKTtcblxuXHRcdFx0aWYgKHRpbGVab29tICE9PSB1bmRlZmluZWQpIHtcblx0XHRcdFx0dGhpcy5fdXBkYXRlKGNlbnRlcik7XG5cdFx0XHR9XG5cblx0XHRcdGlmICghbm9QcnVuZSkge1xuXHRcdFx0XHR0aGlzLl9wcnVuZVRpbGVzKCk7XG5cdFx0XHR9XG5cblx0XHRcdC8vIEZsYWcgdG8gcHJldmVudCBfdXBkYXRlT3BhY2l0eSBmcm9tIHBydW5pbmcgdGlsZXMgZHVyaW5nXG5cdFx0XHQvLyBhIHpvb20gYW5pbSBvciBhIHBpbmNoIGdlc3R1cmVcblx0XHRcdHRoaXMuX25vUHJ1bmUgPSAhIW5vUHJ1bmU7XG5cdFx0fVxuXG5cdFx0dGhpcy5fc2V0Wm9vbVRyYW5zZm9ybXMoY2VudGVyLCB6b29tKTtcblx0fSxcblxuXHRfc2V0Wm9vbVRyYW5zZm9ybXM6IGZ1bmN0aW9uIChjZW50ZXIsIHpvb20pIHtcblx0XHRmb3IgKHZhciBpIGluIHRoaXMuX2xldmVscykge1xuXHRcdFx0dGhpcy5fc2V0Wm9vbVRyYW5zZm9ybSh0aGlzLl9sZXZlbHNbaV0sIGNlbnRlciwgem9vbSk7XG5cdFx0fVxuXHR9LFxuXG5cdF9zZXRab29tVHJhbnNmb3JtOiBmdW5jdGlvbiAobGV2ZWwsIGNlbnRlciwgem9vbSkge1xuXHRcdHZhciBzY2FsZSA9IHRoaXMuX21hcC5nZXRab29tU2NhbGUoem9vbSwgbGV2ZWwuem9vbSksXG5cdFx0ICAgIHRyYW5zbGF0ZSA9IGxldmVsLm9yaWdpbi5tdWx0aXBseUJ5KHNjYWxlKVxuXHRcdCAgICAgICAgLnN1YnRyYWN0KHRoaXMuX21hcC5fZ2V0TmV3UGl4ZWxPcmlnaW4oY2VudGVyLCB6b29tKSkucm91bmQoKTtcblxuXHRcdGlmIChMLkJyb3dzZXIuYW55M2QpIHtcblx0XHRcdEwuRG9tVXRpbC5zZXRUcmFuc2Zvcm0obGV2ZWwuZWwsIHRyYW5zbGF0ZSwgc2NhbGUpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24obGV2ZWwuZWwsIHRyYW5zbGF0ZSk7XG5cdFx0fVxuXHR9LFxuXG5cdF9yZXNldEdyaWQ6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxuXHRcdCAgICBjcnMgPSBtYXAub3B0aW9ucy5jcnMsXG5cdFx0ICAgIHRpbGVTaXplID0gdGhpcy5fdGlsZVNpemUgPSB0aGlzLmdldFRpbGVTaXplKCksXG5cdFx0ICAgIHRpbGVab29tID0gdGhpcy5fdGlsZVpvb207XG5cblx0XHR2YXIgYm91bmRzID0gdGhpcy5fbWFwLmdldFBpeGVsV29ybGRCb3VuZHModGhpcy5fdGlsZVpvb20pO1xuXHRcdGlmIChib3VuZHMpIHtcblx0XHRcdHRoaXMuX2dsb2JhbFRpbGVSYW5nZSA9IHRoaXMuX3B4Qm91bmRzVG9UaWxlUmFuZ2UoYm91bmRzKTtcblx0XHR9XG5cblx0XHR0aGlzLl93cmFwWCA9IGNycy53cmFwTG5nICYmICF0aGlzLm9wdGlvbnMubm9XcmFwICYmIFtcblx0XHRcdE1hdGguZmxvb3IobWFwLnByb2plY3QoWzAsIGNycy53cmFwTG5nWzBdXSwgdGlsZVpvb20pLnggLyB0aWxlU2l6ZS54KSxcblx0XHRcdE1hdGguY2VpbChtYXAucHJvamVjdChbMCwgY3JzLndyYXBMbmdbMV1dLCB0aWxlWm9vbSkueCAvIHRpbGVTaXplLnkpXG5cdFx0XTtcblx0XHR0aGlzLl93cmFwWSA9IGNycy53cmFwTGF0ICYmICF0aGlzLm9wdGlvbnMubm9XcmFwICYmIFtcblx0XHRcdE1hdGguZmxvb3IobWFwLnByb2plY3QoW2Nycy53cmFwTGF0WzBdLCAwXSwgdGlsZVpvb20pLnkgLyB0aWxlU2l6ZS54KSxcblx0XHRcdE1hdGguY2VpbChtYXAucHJvamVjdChbY3JzLndyYXBMYXRbMV0sIDBdLCB0aWxlWm9vbSkueSAvIHRpbGVTaXplLnkpXG5cdFx0XTtcblx0fSxcblxuXHRfb25Nb3ZlRW5kOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKCF0aGlzLl9tYXAgfHwgdGhpcy5fbWFwLl9hbmltYXRpbmdab29tKSB7IHJldHVybjsgfVxuXG5cdFx0dGhpcy5fdXBkYXRlKCk7XG5cdH0sXG5cblx0X2dldFRpbGVkUGl4ZWxCb3VuZHM6IGZ1bmN0aW9uIChjZW50ZXIpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxuXHRcdCAgICBtYXBab29tID0gbWFwLl9hbmltYXRpbmdab29tID8gTWF0aC5tYXgobWFwLl9hbmltYXRlVG9ab29tLCBtYXAuZ2V0Wm9vbSgpKSA6IG1hcC5nZXRab29tKCksXG5cdFx0ICAgIHNjYWxlID0gbWFwLmdldFpvb21TY2FsZShtYXBab29tLCB0aGlzLl90aWxlWm9vbSksXG5cdFx0ICAgIHBpeGVsQ2VudGVyID0gbWFwLnByb2plY3QoY2VudGVyLCB0aGlzLl90aWxlWm9vbSkuZmxvb3IoKSxcblx0XHQgICAgaGFsZlNpemUgPSBtYXAuZ2V0U2l6ZSgpLmRpdmlkZUJ5KHNjYWxlICogMik7XG5cblx0XHRyZXR1cm4gbmV3IEwuQm91bmRzKHBpeGVsQ2VudGVyLnN1YnRyYWN0KGhhbGZTaXplKSwgcGl4ZWxDZW50ZXIuYWRkKGhhbGZTaXplKSk7XG5cdH0sXG5cblx0Ly8gUHJpdmF0ZSBtZXRob2QgdG8gbG9hZCB0aWxlcyBpbiB0aGUgZ3JpZCdzIGFjdGl2ZSB6b29tIGxldmVsIGFjY29yZGluZyB0byBtYXAgYm91bmRzXG5cdF91cGRhdGU6IGZ1bmN0aW9uIChjZW50ZXIpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwO1xuXHRcdGlmICghbWFwKSB7IHJldHVybjsgfVxuXHRcdHZhciB6b29tID0gbWFwLmdldFpvb20oKTtcblxuXHRcdGlmIChjZW50ZXIgPT09IHVuZGVmaW5lZCkgeyBjZW50ZXIgPSBtYXAuZ2V0Q2VudGVyKCk7IH1cblx0XHRpZiAodGhpcy5fdGlsZVpvb20gPT09IHVuZGVmaW5lZCkgeyByZXR1cm47IH1cdC8vIGlmIG91dCBvZiBtaW56b29tL21heHpvb21cblxuXHRcdHZhciBwaXhlbEJvdW5kcyA9IHRoaXMuX2dldFRpbGVkUGl4ZWxCb3VuZHMoY2VudGVyKSxcblx0XHQgICAgdGlsZVJhbmdlID0gdGhpcy5fcHhCb3VuZHNUb1RpbGVSYW5nZShwaXhlbEJvdW5kcyksXG5cdFx0ICAgIHRpbGVDZW50ZXIgPSB0aWxlUmFuZ2UuZ2V0Q2VudGVyKCksXG5cdFx0ICAgIHF1ZXVlID0gW10sXG5cdFx0ICAgIG1hcmdpbiA9IHRoaXMub3B0aW9ucy5rZWVwQnVmZmVyLFxuXHRcdCAgICBub1BydW5lUmFuZ2UgPSBuZXcgTC5Cb3VuZHModGlsZVJhbmdlLmdldEJvdHRvbUxlZnQoKS5zdWJ0cmFjdChbbWFyZ2luLCAtbWFyZ2luXSksXG5cdFx0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGlsZVJhbmdlLmdldFRvcFJpZ2h0KCkuYWRkKFttYXJnaW4sIC1tYXJnaW5dKSk7XG5cblx0XHRmb3IgKHZhciBrZXkgaW4gdGhpcy5fdGlsZXMpIHtcblx0XHRcdHZhciBjID0gdGhpcy5fdGlsZXNba2V5XS5jb29yZHM7XG5cdFx0XHRpZiAoYy56ICE9PSB0aGlzLl90aWxlWm9vbSB8fCAhbm9QcnVuZVJhbmdlLmNvbnRhaW5zKEwucG9pbnQoYy54LCBjLnkpKSkge1xuXHRcdFx0XHR0aGlzLl90aWxlc1trZXldLmN1cnJlbnQgPSBmYWxzZTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHQvLyBfdXBkYXRlIGp1c3QgbG9hZHMgbW9yZSB0aWxlcy4gSWYgdGhlIHRpbGUgem9vbSBsZXZlbCBkaWZmZXJzIHRvbyBtdWNoXG5cdFx0Ly8gZnJvbSB0aGUgbWFwJ3MsIGxldCBfc2V0VmlldyByZXNldCBsZXZlbHMgYW5kIHBydW5lIG9sZCB0aWxlcy5cblx0XHRpZiAoTWF0aC5hYnMoem9vbSAtIHRoaXMuX3RpbGVab29tKSA+IDEpIHsgdGhpcy5fc2V0VmlldyhjZW50ZXIsIHpvb20pOyByZXR1cm47IH1cblxuXHRcdC8vIGNyZWF0ZSBhIHF1ZXVlIG9mIGNvb3JkaW5hdGVzIHRvIGxvYWQgdGlsZXMgZnJvbVxuXHRcdGZvciAodmFyIGogPSB0aWxlUmFuZ2UubWluLnk7IGogPD0gdGlsZVJhbmdlLm1heC55OyBqKyspIHtcblx0XHRcdGZvciAodmFyIGkgPSB0aWxlUmFuZ2UubWluLng7IGkgPD0gdGlsZVJhbmdlLm1heC54OyBpKyspIHtcblx0XHRcdFx0dmFyIGNvb3JkcyA9IG5ldyBMLlBvaW50KGksIGopO1xuXHRcdFx0XHRjb29yZHMueiA9IHRoaXMuX3RpbGVab29tO1xuXG5cdFx0XHRcdGlmICghdGhpcy5faXNWYWxpZFRpbGUoY29vcmRzKSkgeyBjb250aW51ZTsgfVxuXG5cdFx0XHRcdHZhciB0aWxlID0gdGhpcy5fdGlsZXNbdGhpcy5fdGlsZUNvb3Jkc1RvS2V5KGNvb3JkcyldO1xuXHRcdFx0XHRpZiAodGlsZSkge1xuXHRcdFx0XHRcdHRpbGUuY3VycmVudCA9IHRydWU7XG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0cXVldWUucHVzaChjb29yZHMpO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0Ly8gc29ydCB0aWxlIHF1ZXVlIHRvIGxvYWQgdGlsZXMgaW4gb3JkZXIgb2YgdGhlaXIgZGlzdGFuY2UgdG8gY2VudGVyXG5cdFx0cXVldWUuc29ydChmdW5jdGlvbiAoYSwgYikge1xuXHRcdFx0cmV0dXJuIGEuZGlzdGFuY2VUbyh0aWxlQ2VudGVyKSAtIGIuZGlzdGFuY2VUbyh0aWxlQ2VudGVyKTtcblx0XHR9KTtcblxuXHRcdGlmIChxdWV1ZS5sZW5ndGggIT09IDApIHtcblx0XHRcdC8vIGlmIGl0J3MgdGhlIGZpcnN0IGJhdGNoIG9mIHRpbGVzIHRvIGxvYWRcblx0XHRcdGlmICghdGhpcy5fbG9hZGluZykge1xuXHRcdFx0XHR0aGlzLl9sb2FkaW5nID0gdHJ1ZTtcblx0XHRcdFx0Ly8gQGV2ZW50IGxvYWRpbmc6IEV2ZW50XG5cdFx0XHRcdC8vIEZpcmVkIHdoZW4gdGhlIGdyaWQgbGF5ZXIgc3RhcnRzIGxvYWRpbmcgdGlsZXMuXG5cdFx0XHRcdHRoaXMuZmlyZSgnbG9hZGluZycpO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBjcmVhdGUgRE9NIGZyYWdtZW50IHRvIGFwcGVuZCB0aWxlcyBpbiBvbmUgYmF0Y2hcblx0XHRcdHZhciBmcmFnbWVudCA9IGRvY3VtZW50LmNyZWF0ZURvY3VtZW50RnJhZ21lbnQoKTtcblxuXHRcdFx0Zm9yIChpID0gMDsgaSA8IHF1ZXVlLmxlbmd0aDsgaSsrKSB7XG5cdFx0XHRcdHRoaXMuX2FkZFRpbGUocXVldWVbaV0sIGZyYWdtZW50KTtcblx0XHRcdH1cblxuXHRcdFx0dGhpcy5fbGV2ZWwuZWwuYXBwZW5kQ2hpbGQoZnJhZ21lbnQpO1xuXHRcdH1cblx0fSxcblxuXHRfaXNWYWxpZFRpbGU6IGZ1bmN0aW9uIChjb29yZHMpIHtcblx0XHR2YXIgY3JzID0gdGhpcy5fbWFwLm9wdGlvbnMuY3JzO1xuXG5cdFx0aWYgKCFjcnMuaW5maW5pdGUpIHtcblx0XHRcdC8vIGRvbid0IGxvYWQgdGlsZSBpZiBpdCdzIG91dCBvZiBib3VuZHMgYW5kIG5vdCB3cmFwcGVkXG5cdFx0XHR2YXIgYm91bmRzID0gdGhpcy5fZ2xvYmFsVGlsZVJhbmdlO1xuXHRcdFx0aWYgKCghY3JzLndyYXBMbmcgJiYgKGNvb3Jkcy54IDwgYm91bmRzLm1pbi54IHx8IGNvb3Jkcy54ID4gYm91bmRzLm1heC54KSkgfHxcblx0XHRcdCAgICAoIWNycy53cmFwTGF0ICYmIChjb29yZHMueSA8IGJvdW5kcy5taW4ueSB8fCBjb29yZHMueSA+IGJvdW5kcy5tYXgueSkpKSB7IHJldHVybiBmYWxzZTsgfVxuXHRcdH1cblxuXHRcdGlmICghdGhpcy5vcHRpb25zLmJvdW5kcykgeyByZXR1cm4gdHJ1ZTsgfVxuXG5cdFx0Ly8gZG9uJ3QgbG9hZCB0aWxlIGlmIGl0IGRvZXNuJ3QgaW50ZXJzZWN0IHRoZSBib3VuZHMgaW4gb3B0aW9uc1xuXHRcdHZhciB0aWxlQm91bmRzID0gdGhpcy5fdGlsZUNvb3Jkc1RvQm91bmRzKGNvb3Jkcyk7XG5cdFx0cmV0dXJuIEwubGF0TG5nQm91bmRzKHRoaXMub3B0aW9ucy5ib3VuZHMpLm92ZXJsYXBzKHRpbGVCb3VuZHMpO1xuXHR9LFxuXG5cdF9rZXlUb0JvdW5kczogZnVuY3Rpb24gKGtleSkge1xuXHRcdHJldHVybiB0aGlzLl90aWxlQ29vcmRzVG9Cb3VuZHModGhpcy5fa2V5VG9UaWxlQ29vcmRzKGtleSkpO1xuXHR9LFxuXG5cdC8vIGNvbnZlcnRzIHRpbGUgY29vcmRpbmF0ZXMgdG8gaXRzIGdlb2dyYXBoaWNhbCBib3VuZHNcblx0X3RpbGVDb29yZHNUb0JvdW5kczogZnVuY3Rpb24gKGNvb3Jkcykge1xuXG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcCxcblx0XHQgICAgdGlsZVNpemUgPSB0aGlzLmdldFRpbGVTaXplKCksXG5cblx0XHQgICAgbndQb2ludCA9IGNvb3Jkcy5zY2FsZUJ5KHRpbGVTaXplKSxcblx0XHQgICAgc2VQb2ludCA9IG53UG9pbnQuYWRkKHRpbGVTaXplKSxcblxuXHRcdCAgICBudyA9IG1hcC51bnByb2plY3QobndQb2ludCwgY29vcmRzLnopLFxuXHRcdCAgICBzZSA9IG1hcC51bnByb2plY3Qoc2VQb2ludCwgY29vcmRzLnopO1xuXG5cdFx0aWYgKCF0aGlzLm9wdGlvbnMubm9XcmFwKSB7XG5cdFx0XHRudyA9IG1hcC53cmFwTGF0TG5nKG53KTtcblx0XHRcdHNlID0gbWFwLndyYXBMYXRMbmcoc2UpO1xuXHRcdH1cblxuXHRcdHJldHVybiBuZXcgTC5MYXRMbmdCb3VuZHMobncsIHNlKTtcblx0fSxcblxuXHQvLyBjb252ZXJ0cyB0aWxlIGNvb3JkaW5hdGVzIHRvIGtleSBmb3IgdGhlIHRpbGUgY2FjaGVcblx0X3RpbGVDb29yZHNUb0tleTogZnVuY3Rpb24gKGNvb3Jkcykge1xuXHRcdHJldHVybiBjb29yZHMueCArICc6JyArIGNvb3Jkcy55ICsgJzonICsgY29vcmRzLno7XG5cdH0sXG5cblx0Ly8gY29udmVydHMgdGlsZSBjYWNoZSBrZXkgdG8gY29vcmRpbmF0ZXNcblx0X2tleVRvVGlsZUNvb3JkczogZnVuY3Rpb24gKGtleSkge1xuXHRcdHZhciBrID0ga2V5LnNwbGl0KCc6JyksXG5cdFx0ICAgIGNvb3JkcyA9IG5ldyBMLlBvaW50KCtrWzBdLCAra1sxXSk7XG5cdFx0Y29vcmRzLnogPSAra1syXTtcblx0XHRyZXR1cm4gY29vcmRzO1xuXHR9LFxuXG5cdF9yZW1vdmVUaWxlOiBmdW5jdGlvbiAoa2V5KSB7XG5cdFx0dmFyIHRpbGUgPSB0aGlzLl90aWxlc1trZXldO1xuXHRcdGlmICghdGlsZSkgeyByZXR1cm47IH1cblxuXHRcdEwuRG9tVXRpbC5yZW1vdmUodGlsZS5lbCk7XG5cblx0XHRkZWxldGUgdGhpcy5fdGlsZXNba2V5XTtcblxuXHRcdC8vIEBldmVudCB0aWxldW5sb2FkOiBUaWxlRXZlbnRcblx0XHQvLyBGaXJlZCB3aGVuIGEgdGlsZSBpcyByZW1vdmVkIChlLmcuIHdoZW4gYSB0aWxlIGdvZXMgb2ZmIHRoZSBzY3JlZW4pLlxuXHRcdHRoaXMuZmlyZSgndGlsZXVubG9hZCcsIHtcblx0XHRcdHRpbGU6IHRpbGUuZWwsXG5cdFx0XHRjb29yZHM6IHRoaXMuX2tleVRvVGlsZUNvb3JkcyhrZXkpXG5cdFx0fSk7XG5cdH0sXG5cblx0X2luaXRUaWxlOiBmdW5jdGlvbiAodGlsZSkge1xuXHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aWxlLCAnbGVhZmxldC10aWxlJyk7XG5cblx0XHR2YXIgdGlsZVNpemUgPSB0aGlzLmdldFRpbGVTaXplKCk7XG5cdFx0dGlsZS5zdHlsZS53aWR0aCA9IHRpbGVTaXplLnggKyAncHgnO1xuXHRcdHRpbGUuc3R5bGUuaGVpZ2h0ID0gdGlsZVNpemUueSArICdweCc7XG5cblx0XHR0aWxlLm9uc2VsZWN0c3RhcnQgPSBMLlV0aWwuZmFsc2VGbjtcblx0XHR0aWxlLm9ubW91c2Vtb3ZlID0gTC5VdGlsLmZhbHNlRm47XG5cblx0XHQvLyB1cGRhdGUgb3BhY2l0eSBvbiB0aWxlcyBpbiBJRTctOCBiZWNhdXNlIG9mIGZpbHRlciBpbmhlcml0YW5jZSBwcm9ibGVtc1xuXHRcdGlmIChMLkJyb3dzZXIuaWVsdDkgJiYgdGhpcy5vcHRpb25zLm9wYWNpdHkgPCAxKSB7XG5cdFx0XHRMLkRvbVV0aWwuc2V0T3BhY2l0eSh0aWxlLCB0aGlzLm9wdGlvbnMub3BhY2l0eSk7XG5cdFx0fVxuXG5cdFx0Ly8gd2l0aG91dCB0aGlzIGhhY2ssIHRpbGVzIGRpc2FwcGVhciBhZnRlciB6b29tIG9uIENocm9tZSBmb3IgQW5kcm9pZFxuXHRcdC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9MZWFmbGV0L0xlYWZsZXQvaXNzdWVzLzIwNzhcblx0XHRpZiAoTC5Ccm93c2VyLmFuZHJvaWQgJiYgIUwuQnJvd3Nlci5hbmRyb2lkMjMpIHtcblx0XHRcdHRpbGUuc3R5bGUuV2Via2l0QmFja2ZhY2VWaXNpYmlsaXR5ID0gJ2hpZGRlbic7XG5cdFx0fVxuXHR9LFxuXG5cdF9hZGRUaWxlOiBmdW5jdGlvbiAoY29vcmRzLCBjb250YWluZXIpIHtcblx0XHR2YXIgdGlsZVBvcyA9IHRoaXMuX2dldFRpbGVQb3MoY29vcmRzKSxcblx0XHQgICAga2V5ID0gdGhpcy5fdGlsZUNvb3Jkc1RvS2V5KGNvb3Jkcyk7XG5cblx0XHR2YXIgdGlsZSA9IHRoaXMuY3JlYXRlVGlsZSh0aGlzLl93cmFwQ29vcmRzKGNvb3JkcyksIEwuYmluZCh0aGlzLl90aWxlUmVhZHksIHRoaXMsIGNvb3JkcykpO1xuXG5cdFx0dGhpcy5faW5pdFRpbGUodGlsZSk7XG5cblx0XHQvLyBpZiBjcmVhdGVUaWxlIGlzIGRlZmluZWQgd2l0aCBhIHNlY29uZCBhcmd1bWVudCAoXCJkb25lXCIgY2FsbGJhY2spLFxuXHRcdC8vIHdlIGtub3cgdGhhdCB0aWxlIGlzIGFzeW5jIGFuZCB3aWxsIGJlIHJlYWR5IGxhdGVyOyBvdGhlcndpc2Vcblx0XHRpZiAodGhpcy5jcmVhdGVUaWxlLmxlbmd0aCA8IDIpIHtcblx0XHRcdC8vIG1hcmsgdGlsZSBhcyByZWFkeSwgYnV0IGRlbGF5IG9uZSBmcmFtZSBmb3Igb3BhY2l0eSBhbmltYXRpb24gdG8gaGFwcGVuXG5cdFx0XHRMLlV0aWwucmVxdWVzdEFuaW1GcmFtZShMLmJpbmQodGhpcy5fdGlsZVJlYWR5LCB0aGlzLCBjb29yZHMsIG51bGwsIHRpbGUpKTtcblx0XHR9XG5cblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24odGlsZSwgdGlsZVBvcyk7XG5cblx0XHQvLyBzYXZlIHRpbGUgaW4gY2FjaGVcblx0XHR0aGlzLl90aWxlc1trZXldID0ge1xuXHRcdFx0ZWw6IHRpbGUsXG5cdFx0XHRjb29yZHM6IGNvb3Jkcyxcblx0XHRcdGN1cnJlbnQ6IHRydWVcblx0XHR9O1xuXG5cdFx0Y29udGFpbmVyLmFwcGVuZENoaWxkKHRpbGUpO1xuXHRcdC8vIEBldmVudCB0aWxlbG9hZHN0YXJ0OiBUaWxlRXZlbnRcblx0XHQvLyBGaXJlZCB3aGVuIGEgdGlsZSBpcyByZXF1ZXN0ZWQgYW5kIHN0YXJ0cyBsb2FkaW5nLlxuXHRcdHRoaXMuZmlyZSgndGlsZWxvYWRzdGFydCcsIHtcblx0XHRcdHRpbGU6IHRpbGUsXG5cdFx0XHRjb29yZHM6IGNvb3Jkc1xuXHRcdH0pO1xuXHR9LFxuXG5cdF90aWxlUmVhZHk6IGZ1bmN0aW9uIChjb29yZHMsIGVyciwgdGlsZSkge1xuXHRcdGlmICghdGhpcy5fbWFwKSB7IHJldHVybjsgfVxuXG5cdFx0aWYgKGVycikge1xuXHRcdFx0Ly8gQGV2ZW50IHRpbGVlcnJvcjogVGlsZUVycm9yRXZlbnRcblx0XHRcdC8vIEZpcmVkIHdoZW4gdGhlcmUgaXMgYW4gZXJyb3IgbG9hZGluZyBhIHRpbGUuXG5cdFx0XHR0aGlzLmZpcmUoJ3RpbGVlcnJvcicsIHtcblx0XHRcdFx0ZXJyb3I6IGVycixcblx0XHRcdFx0dGlsZTogdGlsZSxcblx0XHRcdFx0Y29vcmRzOiBjb29yZHNcblx0XHRcdH0pO1xuXHRcdH1cblxuXHRcdHZhciBrZXkgPSB0aGlzLl90aWxlQ29vcmRzVG9LZXkoY29vcmRzKTtcblxuXHRcdHRpbGUgPSB0aGlzLl90aWxlc1trZXldO1xuXHRcdGlmICghdGlsZSkgeyByZXR1cm47IH1cblxuXHRcdHRpbGUubG9hZGVkID0gK25ldyBEYXRlKCk7XG5cdFx0aWYgKHRoaXMuX21hcC5fZmFkZUFuaW1hdGVkKSB7XG5cdFx0XHRMLkRvbVV0aWwuc2V0T3BhY2l0eSh0aWxlLmVsLCAwKTtcblx0XHRcdEwuVXRpbC5jYW5jZWxBbmltRnJhbWUodGhpcy5fZmFkZUZyYW1lKTtcblx0XHRcdHRoaXMuX2ZhZGVGcmFtZSA9IEwuVXRpbC5yZXF1ZXN0QW5pbUZyYW1lKHRoaXMuX3VwZGF0ZU9wYWNpdHksIHRoaXMpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aWxlLmFjdGl2ZSA9IHRydWU7XG5cdFx0XHR0aGlzLl9wcnVuZVRpbGVzKCk7XG5cdFx0fVxuXG5cdFx0aWYgKCFlcnIpIHtcblx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aWxlLmVsLCAnbGVhZmxldC10aWxlLWxvYWRlZCcpO1xuXG5cdFx0XHQvLyBAZXZlbnQgdGlsZWxvYWQ6IFRpbGVFdmVudFxuXHRcdFx0Ly8gRmlyZWQgd2hlbiBhIHRpbGUgbG9hZHMuXG5cdFx0XHR0aGlzLmZpcmUoJ3RpbGVsb2FkJywge1xuXHRcdFx0XHR0aWxlOiB0aWxlLmVsLFxuXHRcdFx0XHRjb29yZHM6IGNvb3Jkc1xuXHRcdFx0fSk7XG5cdFx0fVxuXG5cdFx0aWYgKHRoaXMuX25vVGlsZXNUb0xvYWQoKSkge1xuXHRcdFx0dGhpcy5fbG9hZGluZyA9IGZhbHNlO1xuXHRcdFx0Ly8gQGV2ZW50IGxvYWQ6IEV2ZW50XG5cdFx0XHQvLyBGaXJlZCB3aGVuIHRoZSBncmlkIGxheWVyIGxvYWRlZCBhbGwgdmlzaWJsZSB0aWxlcy5cblx0XHRcdHRoaXMuZmlyZSgnbG9hZCcpO1xuXG5cdFx0XHRpZiAoTC5Ccm93c2VyLmllbHQ5IHx8ICF0aGlzLl9tYXAuX2ZhZGVBbmltYXRlZCkge1xuXHRcdFx0XHRMLlV0aWwucmVxdWVzdEFuaW1GcmFtZSh0aGlzLl9wcnVuZVRpbGVzLCB0aGlzKTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdC8vIFdhaXQgYSBiaXQgbW9yZSB0aGFuIDAuMiBzZWNzICh0aGUgZHVyYXRpb24gb2YgdGhlIHRpbGUgZmFkZS1pbilcblx0XHRcdFx0Ly8gdG8gdHJpZ2dlciBhIHBydW5pbmcuXG5cdFx0XHRcdHNldFRpbWVvdXQoTC5iaW5kKHRoaXMuX3BydW5lVGlsZXMsIHRoaXMpLCAyNTApO1xuXHRcdFx0fVxuXHRcdH1cblx0fSxcblxuXHRfZ2V0VGlsZVBvczogZnVuY3Rpb24gKGNvb3Jkcykge1xuXHRcdHJldHVybiBjb29yZHMuc2NhbGVCeSh0aGlzLmdldFRpbGVTaXplKCkpLnN1YnRyYWN0KHRoaXMuX2xldmVsLm9yaWdpbik7XG5cdH0sXG5cblx0X3dyYXBDb29yZHM6IGZ1bmN0aW9uIChjb29yZHMpIHtcblx0XHR2YXIgbmV3Q29vcmRzID0gbmV3IEwuUG9pbnQoXG5cdFx0XHR0aGlzLl93cmFwWCA/IEwuVXRpbC53cmFwTnVtKGNvb3Jkcy54LCB0aGlzLl93cmFwWCkgOiBjb29yZHMueCxcblx0XHRcdHRoaXMuX3dyYXBZID8gTC5VdGlsLndyYXBOdW0oY29vcmRzLnksIHRoaXMuX3dyYXBZKSA6IGNvb3Jkcy55KTtcblx0XHRuZXdDb29yZHMueiA9IGNvb3Jkcy56O1xuXHRcdHJldHVybiBuZXdDb29yZHM7XG5cdH0sXG5cblx0X3B4Qm91bmRzVG9UaWxlUmFuZ2U6IGZ1bmN0aW9uIChib3VuZHMpIHtcblx0XHR2YXIgdGlsZVNpemUgPSB0aGlzLmdldFRpbGVTaXplKCk7XG5cdFx0cmV0dXJuIG5ldyBMLkJvdW5kcyhcblx0XHRcdGJvdW5kcy5taW4udW5zY2FsZUJ5KHRpbGVTaXplKS5mbG9vcigpLFxuXHRcdFx0Ym91bmRzLm1heC51bnNjYWxlQnkodGlsZVNpemUpLmNlaWwoKS5zdWJ0cmFjdChbMSwgMV0pKTtcblx0fSxcblxuXHRfbm9UaWxlc1RvTG9hZDogZnVuY3Rpb24gKCkge1xuXHRcdGZvciAodmFyIGtleSBpbiB0aGlzLl90aWxlcykge1xuXHRcdFx0aWYgKCF0aGlzLl90aWxlc1trZXldLmxvYWRlZCkgeyByZXR1cm4gZmFsc2U7IH1cblx0XHR9XG5cdFx0cmV0dXJuIHRydWU7XG5cdH1cbn0pO1xuXG4vLyBAZmFjdG9yeSBMLmdyaWRMYXllcihvcHRpb25zPzogR3JpZExheWVyIG9wdGlvbnMpXG4vLyBDcmVhdGVzIGEgbmV3IGluc3RhbmNlIG9mIEdyaWRMYXllciB3aXRoIHRoZSBzdXBwbGllZCBvcHRpb25zLlxuTC5ncmlkTGF5ZXIgPSBmdW5jdGlvbiAob3B0aW9ucykge1xuXHRyZXR1cm4gbmV3IEwuR3JpZExheWVyKG9wdGlvbnMpO1xufTtcblxuXG5cbi8qXHJcbiAqIEBjbGFzcyBUaWxlTGF5ZXJcclxuICogQGluaGVyaXRzIEdyaWRMYXllclxyXG4gKiBAYWthIEwuVGlsZUxheWVyXHJcbiAqIFVzZWQgdG8gbG9hZCBhbmQgZGlzcGxheSB0aWxlIGxheWVycyBvbiB0aGUgbWFwLiBFeHRlbmRzIGBHcmlkTGF5ZXJgLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBMLnRpbGVMYXllcignaHR0cDovL3tzfS50aWxlLm9zbS5vcmcve3p9L3t4fS97eX0ucG5nP3tmb299Jywge2ZvbzogJ2Jhcid9KS5hZGRUbyhtYXApO1xyXG4gKiBgYGBcclxuICpcclxuICogQHNlY3Rpb24gVVJMIHRlbXBsYXRlXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIEEgc3RyaW5nIG9mIHRoZSBmb2xsb3dpbmcgZm9ybTpcclxuICpcclxuICogYGBgXHJcbiAqICdodHRwOi8ve3N9LnNvbWVkb21haW4uY29tL2JsYWJsYS97en0ve3h9L3t5fXtyfS5wbmcnXHJcbiAqIGBgYFxyXG4gKlxyXG4gKiBge3N9YCBtZWFucyBvbmUgb2YgdGhlIGF2YWlsYWJsZSBzdWJkb21haW5zICh1c2VkIHNlcXVlbnRpYWxseSB0byBoZWxwIHdpdGggYnJvd3NlciBwYXJhbGxlbCByZXF1ZXN0cyBwZXIgZG9tYWluIGxpbWl0YXRpb247IHN1YmRvbWFpbiB2YWx1ZXMgYXJlIHNwZWNpZmllZCBpbiBvcHRpb25zOyBgYWAsIGBiYCBvciBgY2AgYnkgZGVmYXVsdCwgY2FuIGJlIG9taXR0ZWQpLCBge3p9YCDigJQgem9vbSBsZXZlbCwgYHt4fWAgYW5kIGB7eX1gIOKAlCB0aWxlIGNvb3JkaW5hdGVzLiBge3J9YCBjYW4gYmUgdXNlZCB0byBhZGQgQDJ4IHRvIHRoZSBVUkwgdG8gbG9hZCByZXRpbmEgdGlsZXMuXHJcbiAqXHJcbiAqIFlvdSBjYW4gdXNlIGN1c3RvbSBrZXlzIGluIHRoZSB0ZW1wbGF0ZSwgd2hpY2ggd2lsbCBiZSBbZXZhbHVhdGVkXSgjdXRpbC10ZW1wbGF0ZSkgZnJvbSBUaWxlTGF5ZXIgb3B0aW9ucywgbGlrZSB0aGlzOlxyXG4gKlxyXG4gKiBgYGBcclxuICogTC50aWxlTGF5ZXIoJ2h0dHA6Ly97c30uc29tZWRvbWFpbi5jb20ve2Zvb30ve3p9L3t4fS97eX0ucG5nJywge2ZvbzogJ2Jhcid9KTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxuXHJcbkwuVGlsZUxheWVyID0gTC5HcmlkTGF5ZXIuZXh0ZW5kKHtcclxuXHJcblx0Ly8gQHNlY3Rpb25cclxuXHQvLyBAYWthIFRpbGVMYXllciBvcHRpb25zXHJcblx0b3B0aW9uczoge1xyXG5cdFx0Ly8gQG9wdGlvbiBtaW5ab29tOiBOdW1iZXIgPSAwXHJcblx0XHQvLyBNaW5pbXVtIHpvb20gbnVtYmVyLlxyXG5cdFx0bWluWm9vbTogMCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIG1heFpvb206IE51bWJlciA9IDE4XHJcblx0XHQvLyBNYXhpbXVtIHpvb20gbnVtYmVyLlxyXG5cdFx0bWF4Wm9vbTogMTgsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBtYXhOYXRpdmVab29tOiBOdW1iZXIgPSBudWxsXHJcblx0XHQvLyBNYXhpbXVtIHpvb20gbnVtYmVyIHRoZSB0aWxlIHNvdXJjZSBoYXMgYXZhaWxhYmxlLiBJZiBpdCBpcyBzcGVjaWZpZWQsXHJcblx0XHQvLyB0aGUgdGlsZXMgb24gYWxsIHpvb20gbGV2ZWxzIGhpZ2hlciB0aGFuIGBtYXhOYXRpdmVab29tYCB3aWxsIGJlIGxvYWRlZFxyXG5cdFx0Ly8gZnJvbSBgbWF4TmF0aXZlWm9vbWAgbGV2ZWwgYW5kIGF1dG8tc2NhbGVkLlxyXG5cdFx0bWF4TmF0aXZlWm9vbTogbnVsbCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIG1pbk5hdGl2ZVpvb206IE51bWJlciA9IG51bGxcclxuXHRcdC8vIE1pbmltdW0gem9vbSBudW1iZXIgdGhlIHRpbGUgc291cmNlIGhhcyBhdmFpbGFibGUuIElmIGl0IGlzIHNwZWNpZmllZCxcclxuXHRcdC8vIHRoZSB0aWxlcyBvbiBhbGwgem9vbSBsZXZlbHMgbG93ZXIgdGhhbiBgbWluTmF0aXZlWm9vbWAgd2lsbCBiZSBsb2FkZWRcclxuXHRcdC8vIGZyb20gYG1pbk5hdGl2ZVpvb21gIGxldmVsIGFuZCBhdXRvLXNjYWxlZC5cclxuXHRcdG1pbk5hdGl2ZVpvb206IG51bGwsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBzdWJkb21haW5zOiBTdHJpbmd8U3RyaW5nW10gPSAnYWJjJ1xyXG5cdFx0Ly8gU3ViZG9tYWlucyBvZiB0aGUgdGlsZSBzZXJ2aWNlLiBDYW4gYmUgcGFzc2VkIGluIHRoZSBmb3JtIG9mIG9uZSBzdHJpbmcgKHdoZXJlIGVhY2ggbGV0dGVyIGlzIGEgc3ViZG9tYWluIG5hbWUpIG9yIGFuIGFycmF5IG9mIHN0cmluZ3MuXHJcblx0XHRzdWJkb21haW5zOiAnYWJjJyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGVycm9yVGlsZVVybDogU3RyaW5nID0gJydcclxuXHRcdC8vIFVSTCB0byB0aGUgdGlsZSBpbWFnZSB0byBzaG93IGluIHBsYWNlIG9mIHRoZSB0aWxlIHRoYXQgZmFpbGVkIHRvIGxvYWQuXHJcblx0XHRlcnJvclRpbGVVcmw6ICcnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gem9vbU9mZnNldDogTnVtYmVyID0gMFxyXG5cdFx0Ly8gVGhlIHpvb20gbnVtYmVyIHVzZWQgaW4gdGlsZSBVUkxzIHdpbGwgYmUgb2Zmc2V0IHdpdGggdGhpcyB2YWx1ZS5cclxuXHRcdHpvb21PZmZzZXQ6IDAsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB0bXM6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gSWYgYHRydWVgLCBpbnZlcnNlcyBZIGF4aXMgbnVtYmVyaW5nIGZvciB0aWxlcyAodHVybiB0aGlzIG9uIGZvciBbVE1TXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9UaWxlX01hcF9TZXJ2aWNlKSBzZXJ2aWNlcykuXHJcblx0XHR0bXM6IGZhbHNlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gem9vbVJldmVyc2U6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gSWYgc2V0IHRvIHRydWUsIHRoZSB6b29tIG51bWJlciB1c2VkIGluIHRpbGUgVVJMcyB3aWxsIGJlIHJldmVyc2VkIChgbWF4Wm9vbSAtIHpvb21gIGluc3RlYWQgb2YgYHpvb21gKVxyXG5cdFx0em9vbVJldmVyc2U6IGZhbHNlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gZGV0ZWN0UmV0aW5hOiBCb29sZWFuID0gZmFsc2VcclxuXHRcdC8vIElmIGB0cnVlYCBhbmQgdXNlciBpcyBvbiBhIHJldGluYSBkaXNwbGF5LCBpdCB3aWxsIHJlcXVlc3QgZm91ciB0aWxlcyBvZiBoYWxmIHRoZSBzcGVjaWZpZWQgc2l6ZSBhbmQgYSBiaWdnZXIgem9vbSBsZXZlbCBpbiBwbGFjZSBvZiBvbmUgdG8gdXRpbGl6ZSB0aGUgaGlnaCByZXNvbHV0aW9uLlxyXG5cdFx0ZGV0ZWN0UmV0aW5hOiBmYWxzZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGNyb3NzT3JpZ2luOiBCb29sZWFuID0gZmFsc2VcclxuXHRcdC8vIElmIHRydWUsIGFsbCB0aWxlcyB3aWxsIGhhdmUgdGhlaXIgY3Jvc3NPcmlnaW4gYXR0cmlidXRlIHNldCB0byAnJy4gVGhpcyBpcyBuZWVkZWQgaWYgeW91IHdhbnQgdG8gYWNjZXNzIHRpbGUgcGl4ZWwgZGF0YS5cclxuXHRcdGNyb3NzT3JpZ2luOiBmYWxzZVxyXG5cdH0sXHJcblxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uICh1cmwsIG9wdGlvbnMpIHtcclxuXHJcblx0XHR0aGlzLl91cmwgPSB1cmw7XHJcblxyXG5cdFx0b3B0aW9ucyA9IEwuc2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcclxuXHJcblx0XHQvLyBkZXRlY3RpbmcgcmV0aW5hIGRpc3BsYXlzLCBhZGp1c3RpbmcgdGlsZVNpemUgYW5kIHpvb20gbGV2ZWxzXHJcblx0XHRpZiAob3B0aW9ucy5kZXRlY3RSZXRpbmEgJiYgTC5Ccm93c2VyLnJldGluYSAmJiBvcHRpb25zLm1heFpvb20gPiAwKSB7XHJcblxyXG5cdFx0XHRvcHRpb25zLnRpbGVTaXplID0gTWF0aC5mbG9vcihvcHRpb25zLnRpbGVTaXplIC8gMik7XHJcblxyXG5cdFx0XHRpZiAoIW9wdGlvbnMuem9vbVJldmVyc2UpIHtcclxuXHRcdFx0XHRvcHRpb25zLnpvb21PZmZzZXQrKztcclxuXHRcdFx0XHRvcHRpb25zLm1heFpvb20tLTtcclxuXHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHRvcHRpb25zLnpvb21PZmZzZXQtLTtcclxuXHRcdFx0XHRvcHRpb25zLm1pblpvb20rKztcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0b3B0aW9ucy5taW5ab29tID0gTWF0aC5tYXgoMCwgb3B0aW9ucy5taW5ab29tKTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAodHlwZW9mIG9wdGlvbnMuc3ViZG9tYWlucyA9PT0gJ3N0cmluZycpIHtcclxuXHRcdFx0b3B0aW9ucy5zdWJkb21haW5zID0gb3B0aW9ucy5zdWJkb21haW5zLnNwbGl0KCcnKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBmb3IgaHR0cHM6Ly9naXRodWIuY29tL0xlYWZsZXQvTGVhZmxldC9pc3N1ZXMvMTM3XHJcblx0XHRpZiAoIUwuQnJvd3Nlci5hbmRyb2lkKSB7XHJcblx0XHRcdHRoaXMub24oJ3RpbGV1bmxvYWQnLCB0aGlzLl9vblRpbGVSZW1vdmUpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0VXJsKHVybDogU3RyaW5nLCBub1JlZHJhdz86IEJvb2xlYW4pOiB0aGlzXHJcblx0Ly8gVXBkYXRlcyB0aGUgbGF5ZXIncyBVUkwgdGVtcGxhdGUgYW5kIHJlZHJhd3MgaXQgKHVubGVzcyBgbm9SZWRyYXdgIGlzIHNldCB0byBgdHJ1ZWApLlxyXG5cdHNldFVybDogZnVuY3Rpb24gKHVybCwgbm9SZWRyYXcpIHtcclxuXHRcdHRoaXMuX3VybCA9IHVybDtcclxuXHJcblx0XHRpZiAoIW5vUmVkcmF3KSB7XHJcblx0XHRcdHRoaXMucmVkcmF3KCk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGNyZWF0ZVRpbGUoY29vcmRzOiBPYmplY3QsIGRvbmU/OiBGdW5jdGlvbik6IEhUTUxFbGVtZW50XHJcblx0Ly8gQ2FsbGVkIG9ubHkgaW50ZXJuYWxseSwgb3ZlcnJpZGVzIEdyaWRMYXllcidzIFtgY3JlYXRlVGlsZSgpYF0oI2dyaWRsYXllci1jcmVhdGV0aWxlKVxyXG5cdC8vIHRvIHJldHVybiBhbiBgPGltZz5gIEhUTUwgZWxlbWVudCB3aXRoIHRoZSBhcHByb3BpYXRlIGltYWdlIFVSTCBnaXZlbiBgY29vcmRzYC4gVGhlIGBkb25lYFxyXG5cdC8vIGNhbGxiYWNrIGlzIGNhbGxlZCB3aGVuIHRoZSB0aWxlIGhhcyBiZWVuIGxvYWRlZC5cclxuXHRjcmVhdGVUaWxlOiBmdW5jdGlvbiAoY29vcmRzLCBkb25lKSB7XHJcblx0XHR2YXIgdGlsZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2ltZycpO1xyXG5cclxuXHRcdEwuRG9tRXZlbnQub24odGlsZSwgJ2xvYWQnLCBMLmJpbmQodGhpcy5fdGlsZU9uTG9hZCwgdGhpcywgZG9uZSwgdGlsZSkpO1xyXG5cdFx0TC5Eb21FdmVudC5vbih0aWxlLCAnZXJyb3InLCBMLmJpbmQodGhpcy5fdGlsZU9uRXJyb3IsIHRoaXMsIGRvbmUsIHRpbGUpKTtcclxuXHJcblx0XHRpZiAodGhpcy5vcHRpb25zLmNyb3NzT3JpZ2luKSB7XHJcblx0XHRcdHRpbGUuY3Jvc3NPcmlnaW4gPSAnJztcclxuXHRcdH1cclxuXHJcblx0XHQvKlxyXG5cdFx0IEFsdCB0YWcgaXMgc2V0IHRvIGVtcHR5IHN0cmluZyB0byBrZWVwIHNjcmVlbiByZWFkZXJzIGZyb20gcmVhZGluZyBVUkwgYW5kIGZvciBjb21wbGlhbmNlIHJlYXNvbnNcclxuXHRcdCBodHRwOi8vd3d3LnczLm9yZy9UUi9XQ0FHMjAtVEVDSFMvSDY3XHJcblx0XHQqL1xyXG5cdFx0dGlsZS5hbHQgPSAnJztcclxuXHJcblx0XHQvKlxyXG5cdFx0IFNldCByb2xlPVwicHJlc2VudGF0aW9uXCIgdG8gZm9yY2Ugc2NyZWVuIHJlYWRlcnMgdG8gaWdub3JlIHRoaXNcclxuXHRcdCBodHRwczovL3d3dy53My5vcmcvVFIvd2FpLWFyaWEvcm9sZXMjdGV4dGFsdGVybmF0aXZlY29tcHV0YXRpb25cclxuXHRcdCovXHJcblx0XHR0aWxlLnNldEF0dHJpYnV0ZSgncm9sZScsICdwcmVzZW50YXRpb24nKTtcclxuXHJcblx0XHR0aWxlLnNyYyA9IHRoaXMuZ2V0VGlsZVVybChjb29yZHMpO1xyXG5cclxuXHRcdHJldHVybiB0aWxlO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBzZWN0aW9uIEV4dGVuc2lvbiBtZXRob2RzXHJcblx0Ly8gQHVuaW5oZXJpdGFibGVcclxuXHQvLyBMYXllcnMgZXh0ZW5kaW5nIGBUaWxlTGF5ZXJgIG1pZ2h0IHJlaW1wbGVtZW50IHRoZSBmb2xsb3dpbmcgbWV0aG9kLlxyXG5cdC8vIEBtZXRob2QgZ2V0VGlsZVVybChjb29yZHM6IE9iamVjdCk6IFN0cmluZ1xyXG5cdC8vIENhbGxlZCBvbmx5IGludGVybmFsbHksIHJldHVybnMgdGhlIFVSTCBmb3IgYSB0aWxlIGdpdmVuIGl0cyBjb29yZGluYXRlcy5cclxuXHQvLyBDbGFzc2VzIGV4dGVuZGluZyBgVGlsZUxheWVyYCBjYW4gb3ZlcnJpZGUgdGhpcyBmdW5jdGlvbiB0byBwcm92aWRlIGN1c3RvbSB0aWxlIFVSTCBuYW1pbmcgc2NoZW1lcy5cclxuXHRnZXRUaWxlVXJsOiBmdW5jdGlvbiAoY29vcmRzKSB7XHJcblx0XHR2YXIgZGF0YSA9IHtcclxuXHRcdFx0cjogTC5Ccm93c2VyLnJldGluYSA/ICdAMngnIDogJycsXHJcblx0XHRcdHM6IHRoaXMuX2dldFN1YmRvbWFpbihjb29yZHMpLFxyXG5cdFx0XHR4OiBjb29yZHMueCxcclxuXHRcdFx0eTogY29vcmRzLnksXHJcblx0XHRcdHo6IHRoaXMuX2dldFpvb21Gb3JVcmwoKVxyXG5cdFx0fTtcclxuXHRcdGlmICh0aGlzLl9tYXAgJiYgIXRoaXMuX21hcC5vcHRpb25zLmNycy5pbmZpbml0ZSkge1xyXG5cdFx0XHR2YXIgaW52ZXJ0ZWRZID0gdGhpcy5fZ2xvYmFsVGlsZVJhbmdlLm1heC55IC0gY29vcmRzLnk7XHJcblx0XHRcdGlmICh0aGlzLm9wdGlvbnMudG1zKSB7XHJcblx0XHRcdFx0ZGF0YVsneSddID0gaW52ZXJ0ZWRZO1xyXG5cdFx0XHR9XHJcblx0XHRcdGRhdGFbJy15J10gPSBpbnZlcnRlZFk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIEwuVXRpbC50ZW1wbGF0ZSh0aGlzLl91cmwsIEwuZXh0ZW5kKGRhdGEsIHRoaXMub3B0aW9ucykpO1xyXG5cdH0sXHJcblxyXG5cdF90aWxlT25Mb2FkOiBmdW5jdGlvbiAoZG9uZSwgdGlsZSkge1xyXG5cdFx0Ly8gRm9yIGh0dHBzOi8vZ2l0aHViLmNvbS9MZWFmbGV0L0xlYWZsZXQvaXNzdWVzLzMzMzJcclxuXHRcdGlmIChMLkJyb3dzZXIuaWVsdDkpIHtcclxuXHRcdFx0c2V0VGltZW91dChMLmJpbmQoZG9uZSwgdGhpcywgbnVsbCwgdGlsZSksIDApO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0ZG9uZShudWxsLCB0aWxlKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfdGlsZU9uRXJyb3I6IGZ1bmN0aW9uIChkb25lLCB0aWxlLCBlKSB7XHJcblx0XHR2YXIgZXJyb3JVcmwgPSB0aGlzLm9wdGlvbnMuZXJyb3JUaWxlVXJsO1xyXG5cdFx0aWYgKGVycm9yVXJsKSB7XHJcblx0XHRcdHRpbGUuc3JjID0gZXJyb3JVcmw7XHJcblx0XHR9XHJcblx0XHRkb25lKGUsIHRpbGUpO1xyXG5cdH0sXHJcblxyXG5cdGdldFRpbGVTaXplOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxyXG5cdFx0dGlsZVNpemUgPSBMLkdyaWRMYXllci5wcm90b3R5cGUuZ2V0VGlsZVNpemUuY2FsbCh0aGlzKSxcclxuXHRcdHpvb20gPSB0aGlzLl90aWxlWm9vbSArIHRoaXMub3B0aW9ucy56b29tT2Zmc2V0LFxyXG5cdFx0bWluTmF0aXZlWm9vbSA9IHRoaXMub3B0aW9ucy5taW5OYXRpdmVab29tLFxyXG5cdFx0bWF4TmF0aXZlWm9vbSA9IHRoaXMub3B0aW9ucy5tYXhOYXRpdmVab29tO1xyXG5cclxuXHRcdC8vIGRlY3JlYXNlIHRpbGUgc2l6ZSB3aGVuIHNjYWxpbmcgYmVsb3cgbWluTmF0aXZlWm9vbVxyXG5cdFx0aWYgKG1pbk5hdGl2ZVpvb20gIT09IG51bGwgJiYgem9vbSA8IG1pbk5hdGl2ZVpvb20pIHtcclxuXHRcdFx0cmV0dXJuIHRpbGVTaXplLmRpdmlkZUJ5KG1hcC5nZXRab29tU2NhbGUobWluTmF0aXZlWm9vbSwgem9vbSkpLnJvdW5kKCk7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gaW5jcmVhc2UgdGlsZSBzaXplIHdoZW4gc2NhbGluZyBhYm92ZSBtYXhOYXRpdmVab29tXHJcblx0XHRpZiAobWF4TmF0aXZlWm9vbSAhPT0gbnVsbCAmJiB6b29tID4gbWF4TmF0aXZlWm9vbSkge1xyXG5cdFx0XHRyZXR1cm4gdGlsZVNpemUuZGl2aWRlQnkobWFwLmdldFpvb21TY2FsZShtYXhOYXRpdmVab29tLCB6b29tKSkucm91bmQoKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGlsZVNpemU7XHJcblx0fSxcclxuXHJcblx0X29uVGlsZVJlbW92ZTogZnVuY3Rpb24gKGUpIHtcclxuXHRcdGUudGlsZS5vbmxvYWQgPSBudWxsO1xyXG5cdH0sXHJcblxyXG5cdF9nZXRab29tRm9yVXJsOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgem9vbSA9IHRoaXMuX3RpbGVab29tLFxyXG5cdFx0bWF4Wm9vbSA9IHRoaXMub3B0aW9ucy5tYXhab29tLFxyXG5cdFx0em9vbVJldmVyc2UgPSB0aGlzLm9wdGlvbnMuem9vbVJldmVyc2UsXHJcblx0XHR6b29tT2Zmc2V0ID0gdGhpcy5vcHRpb25zLnpvb21PZmZzZXQsXHJcblx0XHRtaW5OYXRpdmVab29tID0gdGhpcy5vcHRpb25zLm1pbk5hdGl2ZVpvb20sXHJcblx0XHRtYXhOYXRpdmVab29tID0gdGhpcy5vcHRpb25zLm1heE5hdGl2ZVpvb207XHJcblxyXG5cdFx0aWYgKHpvb21SZXZlcnNlKSB7XHJcblx0XHRcdHpvb20gPSBtYXhab29tIC0gem9vbTtcclxuXHRcdH1cclxuXHJcblx0XHR6b29tICs9IHpvb21PZmZzZXQ7XHJcblxyXG5cdFx0aWYgKG1pbk5hdGl2ZVpvb20gIT09IG51bGwgJiYgem9vbSA8IG1pbk5hdGl2ZVpvb20pIHtcclxuXHRcdFx0cmV0dXJuIG1pbk5hdGl2ZVpvb207XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKG1heE5hdGl2ZVpvb20gIT09IG51bGwgJiYgem9vbSA+IG1heE5hdGl2ZVpvb20pIHtcclxuXHRcdFx0cmV0dXJuIG1heE5hdGl2ZVpvb207XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHpvb207XHJcblx0fSxcclxuXHJcblx0X2dldFN1YmRvbWFpbjogZnVuY3Rpb24gKHRpbGVQb2ludCkge1xyXG5cdFx0dmFyIGluZGV4ID0gTWF0aC5hYnModGlsZVBvaW50LnggKyB0aWxlUG9pbnQueSkgJSB0aGlzLm9wdGlvbnMuc3ViZG9tYWlucy5sZW5ndGg7XHJcblx0XHRyZXR1cm4gdGhpcy5vcHRpb25zLnN1YmRvbWFpbnNbaW5kZXhdO1xyXG5cdH0sXHJcblxyXG5cdC8vIHN0b3BzIGxvYWRpbmcgYWxsIHRpbGVzIGluIHRoZSBiYWNrZ3JvdW5kIGxheWVyXHJcblx0X2Fib3J0TG9hZGluZzogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGksIHRpbGU7XHJcblx0XHRmb3IgKGkgaW4gdGhpcy5fdGlsZXMpIHtcclxuXHRcdFx0aWYgKHRoaXMuX3RpbGVzW2ldLmNvb3Jkcy56ICE9PSB0aGlzLl90aWxlWm9vbSkge1xyXG5cdFx0XHRcdHRpbGUgPSB0aGlzLl90aWxlc1tpXS5lbDtcclxuXHJcblx0XHRcdFx0dGlsZS5vbmxvYWQgPSBMLlV0aWwuZmFsc2VGbjtcclxuXHRcdFx0XHR0aWxlLm9uZXJyb3IgPSBMLlV0aWwuZmFsc2VGbjtcclxuXHJcblx0XHRcdFx0aWYgKCF0aWxlLmNvbXBsZXRlKSB7XHJcblx0XHRcdFx0XHR0aWxlLnNyYyA9IEwuVXRpbC5lbXB0eUltYWdlVXJsO1xyXG5cdFx0XHRcdFx0TC5Eb21VdGlsLnJlbW92ZSh0aWxlKTtcclxuXHRcdFx0XHR9XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHR9XHJcbn0pO1xyXG5cclxuXHJcbi8vIEBmYWN0b3J5IEwudGlsZWxheWVyKHVybFRlbXBsYXRlOiBTdHJpbmcsIG9wdGlvbnM/OiBUaWxlTGF5ZXIgb3B0aW9ucylcclxuLy8gSW5zdGFudGlhdGVzIGEgdGlsZSBsYXllciBvYmplY3QgZ2l2ZW4gYSBgVVJMIHRlbXBsYXRlYCBhbmQgb3B0aW9uYWxseSBhbiBvcHRpb25zIG9iamVjdC5cclxuXHJcbkwudGlsZUxheWVyID0gZnVuY3Rpb24gKHVybCwgb3B0aW9ucykge1xyXG5cdHJldHVybiBuZXcgTC5UaWxlTGF5ZXIodXJsLCBvcHRpb25zKTtcclxufTtcclxuXG5cblxuLypcclxuICogQGNsYXNzIFRpbGVMYXllci5XTVNcclxuICogQGluaGVyaXRzIFRpbGVMYXllclxyXG4gKiBAYWthIEwuVGlsZUxheWVyLldNU1xyXG4gKiBVc2VkIHRvIGRpc3BsYXkgW1dNU10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvV2ViX01hcF9TZXJ2aWNlKSBzZXJ2aWNlcyBhcyB0aWxlIGxheWVycyBvbiB0aGUgbWFwLiBFeHRlbmRzIGBUaWxlTGF5ZXJgLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiB2YXIgbmV4cmFkID0gTC50aWxlTGF5ZXIud21zKFwiaHR0cDovL21lc29uZXQuYWdyb24uaWFzdGF0ZS5lZHUvY2dpLWJpbi93bXMvbmV4cmFkL24wci5jZ2lcIiwge1xyXG4gKiBcdGxheWVyczogJ25leHJhZC1uMHItOTAwOTEzJyxcclxuICogXHRmb3JtYXQ6ICdpbWFnZS9wbmcnLFxyXG4gKiBcdHRyYW5zcGFyZW50OiB0cnVlLFxyXG4gKiBcdGF0dHJpYnV0aW9uOiBcIldlYXRoZXIgZGF0YSDCqSAyMDEyIElFTSBOZXhyYWRcIlxyXG4gKiB9KTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxuTC5UaWxlTGF5ZXIuV01TID0gTC5UaWxlTGF5ZXIuZXh0ZW5kKHtcclxuXHJcblx0Ly8gQHNlY3Rpb25cclxuXHQvLyBAYWthIFRpbGVMYXllci5XTVMgb3B0aW9uc1xyXG5cdC8vIElmIGFueSBjdXN0b20gb3B0aW9ucyBub3QgZG9jdW1lbnRlZCBoZXJlIGFyZSB1c2VkLCB0aGV5IHdpbGwgYmUgc2VudCB0byB0aGVcclxuXHQvLyBXTVMgc2VydmVyIGFzIGV4dHJhIHBhcmFtZXRlcnMgaW4gZWFjaCByZXF1ZXN0IFVSTC4gVGhpcyBjYW4gYmUgdXNlZnVsIGZvclxyXG5cdC8vIFtub24tc3RhbmRhcmQgdmVuZG9yIFdNUyBwYXJhbWV0ZXJzXShodHRwOi8vZG9jcy5nZW9zZXJ2ZXIub3JnL3N0YWJsZS9lbi91c2VyL3NlcnZpY2VzL3dtcy92ZW5kb3IuaHRtbCkuXHJcblx0ZGVmYXVsdFdtc1BhcmFtczoge1xyXG5cdFx0c2VydmljZTogJ1dNUycsXHJcblx0XHRyZXF1ZXN0OiAnR2V0TWFwJyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGxheWVyczogU3RyaW5nID0gJydcclxuXHRcdC8vICoqKHJlcXVpcmVkKSoqIENvbW1hLXNlcGFyYXRlZCBsaXN0IG9mIFdNUyBsYXllcnMgdG8gc2hvdy5cclxuXHRcdGxheWVyczogJycsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBzdHlsZXM6IFN0cmluZyA9ICcnXHJcblx0XHQvLyBDb21tYS1zZXBhcmF0ZWQgbGlzdCBvZiBXTVMgc3R5bGVzLlxyXG5cdFx0c3R5bGVzOiAnJyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGZvcm1hdDogU3RyaW5nID0gJ2ltYWdlL2pwZWcnXHJcblx0XHQvLyBXTVMgaW1hZ2UgZm9ybWF0ICh1c2UgYCdpbWFnZS9wbmcnYCBmb3IgbGF5ZXJzIHdpdGggdHJhbnNwYXJlbmN5KS5cclxuXHRcdGZvcm1hdDogJ2ltYWdlL2pwZWcnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gdHJhbnNwYXJlbnQ6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gSWYgYHRydWVgLCB0aGUgV01TIHNlcnZpY2Ugd2lsbCByZXR1cm4gaW1hZ2VzIHdpdGggdHJhbnNwYXJlbmN5LlxyXG5cdFx0dHJhbnNwYXJlbnQ6IGZhbHNlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gdmVyc2lvbjogU3RyaW5nID0gJzEuMS4xJ1xyXG5cdFx0Ly8gVmVyc2lvbiBvZiB0aGUgV01TIHNlcnZpY2UgdG8gdXNlXHJcblx0XHR2ZXJzaW9uOiAnMS4xLjEnXHJcblx0fSxcclxuXHJcblx0b3B0aW9uczoge1xyXG5cdFx0Ly8gQG9wdGlvbiBjcnM6IENSUyA9IG51bGxcclxuXHRcdC8vIENvb3JkaW5hdGUgUmVmZXJlbmNlIFN5c3RlbSB0byB1c2UgZm9yIHRoZSBXTVMgcmVxdWVzdHMsIGRlZmF1bHRzIHRvXHJcblx0XHQvLyBtYXAgQ1JTLiBEb24ndCBjaGFuZ2UgdGhpcyBpZiB5b3UncmUgbm90IHN1cmUgd2hhdCBpdCBtZWFucy5cclxuXHRcdGNyczogbnVsbCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHVwcGVyY2FzZTogQm9vbGVhbiA9IGZhbHNlXHJcblx0XHQvLyBJZiBgdHJ1ZWAsIFdNUyByZXF1ZXN0IHBhcmFtZXRlciBrZXlzIHdpbGwgYmUgdXBwZXJjYXNlLlxyXG5cdFx0dXBwZXJjYXNlOiBmYWxzZVxyXG5cdH0sXHJcblxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uICh1cmwsIG9wdGlvbnMpIHtcclxuXHJcblx0XHR0aGlzLl91cmwgPSB1cmw7XHJcblxyXG5cdFx0dmFyIHdtc1BhcmFtcyA9IEwuZXh0ZW5kKHt9LCB0aGlzLmRlZmF1bHRXbXNQYXJhbXMpO1xyXG5cclxuXHRcdC8vIGFsbCBrZXlzIHRoYXQgYXJlIG5vdCBUaWxlTGF5ZXIgb3B0aW9ucyBnbyB0byBXTVMgcGFyYW1zXHJcblx0XHRmb3IgKHZhciBpIGluIG9wdGlvbnMpIHtcclxuXHRcdFx0aWYgKCEoaSBpbiB0aGlzLm9wdGlvbnMpKSB7XHJcblx0XHRcdFx0d21zUGFyYW1zW2ldID0gb3B0aW9uc1tpXTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdG9wdGlvbnMgPSBMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XHJcblxyXG5cdFx0d21zUGFyYW1zLndpZHRoID0gd21zUGFyYW1zLmhlaWdodCA9IG9wdGlvbnMudGlsZVNpemUgKiAob3B0aW9ucy5kZXRlY3RSZXRpbmEgJiYgTC5Ccm93c2VyLnJldGluYSA/IDIgOiAxKTtcclxuXHJcblx0XHR0aGlzLndtc1BhcmFtcyA9IHdtc1BhcmFtcztcclxuXHR9LFxyXG5cclxuXHRvbkFkZDogZnVuY3Rpb24gKG1hcCkge1xyXG5cclxuXHRcdHRoaXMuX2NycyA9IHRoaXMub3B0aW9ucy5jcnMgfHwgbWFwLm9wdGlvbnMuY3JzO1xyXG5cdFx0dGhpcy5fd21zVmVyc2lvbiA9IHBhcnNlRmxvYXQodGhpcy53bXNQYXJhbXMudmVyc2lvbik7XHJcblxyXG5cdFx0dmFyIHByb2plY3Rpb25LZXkgPSB0aGlzLl93bXNWZXJzaW9uID49IDEuMyA/ICdjcnMnIDogJ3Nycyc7XHJcblx0XHR0aGlzLndtc1BhcmFtc1twcm9qZWN0aW9uS2V5XSA9IHRoaXMuX2Nycy5jb2RlO1xyXG5cclxuXHRcdEwuVGlsZUxheWVyLnByb3RvdHlwZS5vbkFkZC5jYWxsKHRoaXMsIG1hcCk7XHJcblx0fSxcclxuXHJcblx0Z2V0VGlsZVVybDogZnVuY3Rpb24gKGNvb3Jkcykge1xyXG5cclxuXHRcdHZhciB0aWxlQm91bmRzID0gdGhpcy5fdGlsZUNvb3Jkc1RvQm91bmRzKGNvb3JkcyksXHJcblx0XHQgICAgbncgPSB0aGlzLl9jcnMucHJvamVjdCh0aWxlQm91bmRzLmdldE5vcnRoV2VzdCgpKSxcclxuXHRcdCAgICBzZSA9IHRoaXMuX2Nycy5wcm9qZWN0KHRpbGVCb3VuZHMuZ2V0U291dGhFYXN0KCkpLFxyXG5cclxuXHRcdCAgICBiYm94ID0gKHRoaXMuX3dtc1ZlcnNpb24gPj0gMS4zICYmIHRoaXMuX2NycyA9PT0gTC5DUlMuRVBTRzQzMjYgP1xyXG5cdFx0XHQgICAgW3NlLnksIG53LngsIG53LnksIHNlLnhdIDpcclxuXHRcdFx0ICAgIFtudy54LCBzZS55LCBzZS54LCBudy55XSkuam9pbignLCcpLFxyXG5cclxuXHRcdCAgICB1cmwgPSBMLlRpbGVMYXllci5wcm90b3R5cGUuZ2V0VGlsZVVybC5jYWxsKHRoaXMsIGNvb3Jkcyk7XHJcblxyXG5cdFx0cmV0dXJuIHVybCArXHJcblx0XHRcdEwuVXRpbC5nZXRQYXJhbVN0cmluZyh0aGlzLndtc1BhcmFtcywgdXJsLCB0aGlzLm9wdGlvbnMudXBwZXJjYXNlKSArXHJcblx0XHRcdCh0aGlzLm9wdGlvbnMudXBwZXJjYXNlID8gJyZCQk9YPScgOiAnJmJib3g9JykgKyBiYm94O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0UGFyYW1zKHBhcmFtczogT2JqZWN0LCBub1JlZHJhdz86IEJvb2xlYW4pOiB0aGlzXHJcblx0Ly8gTWVyZ2VzIGFuIG9iamVjdCB3aXRoIHRoZSBuZXcgcGFyYW1ldGVycyBhbmQgcmUtcmVxdWVzdHMgdGlsZXMgb24gdGhlIGN1cnJlbnQgc2NyZWVuICh1bmxlc3MgYG5vUmVkcmF3YCB3YXMgc2V0IHRvIHRydWUpLlxyXG5cdHNldFBhcmFtczogZnVuY3Rpb24gKHBhcmFtcywgbm9SZWRyYXcpIHtcclxuXHJcblx0XHRMLmV4dGVuZCh0aGlzLndtc1BhcmFtcywgcGFyYW1zKTtcclxuXHJcblx0XHRpZiAoIW5vUmVkcmF3KSB7XHJcblx0XHRcdHRoaXMucmVkcmF3KCk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fVxyXG59KTtcclxuXHJcblxyXG4vLyBAZmFjdG9yeSBMLnRpbGVMYXllci53bXMoYmFzZVVybDogU3RyaW5nLCBvcHRpb25zOiBUaWxlTGF5ZXIuV01TIG9wdGlvbnMpXHJcbi8vIEluc3RhbnRpYXRlcyBhIFdNUyB0aWxlIGxheWVyIG9iamVjdCBnaXZlbiBhIGJhc2UgVVJMIG9mIHRoZSBXTVMgc2VydmljZSBhbmQgYSBXTVMgcGFyYW1ldGVycy9vcHRpb25zIG9iamVjdC5cclxuTC50aWxlTGF5ZXIud21zID0gZnVuY3Rpb24gKHVybCwgb3B0aW9ucykge1xyXG5cdHJldHVybiBuZXcgTC5UaWxlTGF5ZXIuV01TKHVybCwgb3B0aW9ucyk7XHJcbn07XHJcblxuXG5cbi8qXHJcbiAqIEBjbGFzcyBJbWFnZU92ZXJsYXlcclxuICogQGFrYSBMLkltYWdlT3ZlcmxheVxyXG4gKiBAaW5oZXJpdHMgSW50ZXJhY3RpdmUgbGF5ZXJcclxuICpcclxuICogVXNlZCB0byBsb2FkIGFuZCBkaXNwbGF5IGEgc2luZ2xlIGltYWdlIG92ZXIgc3BlY2lmaWMgYm91bmRzIG9mIHRoZSBtYXAuIEV4dGVuZHMgYExheWVyYC5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYGBganNcclxuICogdmFyIGltYWdlVXJsID0gJ2h0dHA6Ly93d3cubGliLnV0ZXhhcy5lZHUvbWFwcy9oaXN0b3JpY2FsL25ld2Fya19ual8xOTIyLmpwZycsXHJcbiAqIFx0aW1hZ2VCb3VuZHMgPSBbWzQwLjcxMjIxNiwgLTc0LjIyNjU1XSwgWzQwLjc3Mzk0MSwgLTc0LjEyNTQ0XV07XHJcbiAqIEwuaW1hZ2VPdmVybGF5KGltYWdlVXJsLCBpbWFnZUJvdW5kcykuYWRkVG8obWFwKTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxuTC5JbWFnZU92ZXJsYXkgPSBMLkxheWVyLmV4dGVuZCh7XHJcblxyXG5cdC8vIEBzZWN0aW9uXHJcblx0Ly8gQGFrYSBJbWFnZU92ZXJsYXkgb3B0aW9uc1xyXG5cdG9wdGlvbnM6IHtcclxuXHRcdC8vIEBvcHRpb24gb3BhY2l0eTogTnVtYmVyID0gMS4wXHJcblx0XHQvLyBUaGUgb3BhY2l0eSBvZiB0aGUgaW1hZ2Ugb3ZlcmxheS5cclxuXHRcdG9wYWNpdHk6IDEsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBhbHQ6IFN0cmluZyA9ICcnXHJcblx0XHQvLyBUZXh0IGZvciB0aGUgYGFsdGAgYXR0cmlidXRlIG9mIHRoZSBpbWFnZSAodXNlZnVsIGZvciBhY2Nlc3NpYmlsaXR5KS5cclxuXHRcdGFsdDogJycsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBpbnRlcmFjdGl2ZTogQm9vbGVhbiA9IGZhbHNlXHJcblx0XHQvLyBJZiBgdHJ1ZWAsIHRoZSBpbWFnZSBvdmVybGF5IHdpbGwgZW1pdCBbbW91c2UgZXZlbnRzXSgjaW50ZXJhY3RpdmUtbGF5ZXIpIHdoZW4gY2xpY2tlZCBvciBob3ZlcmVkLlxyXG5cdFx0aW50ZXJhY3RpdmU6IGZhbHNlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gY3Jvc3NPcmlnaW46IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gSWYgdHJ1ZSwgdGhlIGltYWdlIHdpbGwgaGF2ZSBpdHMgY3Jvc3NPcmlnaW4gYXR0cmlidXRlIHNldCB0byAnJy4gVGhpcyBpcyBuZWVkZWQgaWYgeW91IHdhbnQgdG8gYWNjZXNzIGltYWdlIHBpeGVsIGRhdGEuXHJcblx0XHRjcm9zc09yaWdpbjogZmFsc2VcclxuXHR9LFxyXG5cclxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAodXJsLCBib3VuZHMsIG9wdGlvbnMpIHsgLy8gKFN0cmluZywgTGF0TG5nQm91bmRzLCBPYmplY3QpXHJcblx0XHR0aGlzLl91cmwgPSB1cmw7XHJcblx0XHR0aGlzLl9ib3VuZHMgPSBMLmxhdExuZ0JvdW5kcyhib3VuZHMpO1xyXG5cclxuXHRcdEwuc2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcclxuXHR9LFxyXG5cclxuXHRvbkFkZDogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKCF0aGlzLl9pbWFnZSkge1xyXG5cdFx0XHR0aGlzLl9pbml0SW1hZ2UoKTtcclxuXHJcblx0XHRcdGlmICh0aGlzLm9wdGlvbnMub3BhY2l0eSA8IDEpIHtcclxuXHRcdFx0XHR0aGlzLl91cGRhdGVPcGFjaXR5KCk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHRpZiAodGhpcy5vcHRpb25zLmludGVyYWN0aXZlKSB7XHJcblx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9pbWFnZSwgJ2xlYWZsZXQtaW50ZXJhY3RpdmUnKTtcclxuXHRcdFx0dGhpcy5hZGRJbnRlcmFjdGl2ZVRhcmdldCh0aGlzLl9pbWFnZSk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5nZXRQYW5lKCkuYXBwZW5kQ2hpbGQodGhpcy5faW1hZ2UpO1xyXG5cdFx0dGhpcy5fcmVzZXQoKTtcclxuXHR9LFxyXG5cclxuXHRvblJlbW92ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0TC5Eb21VdGlsLnJlbW92ZSh0aGlzLl9pbWFnZSk7XHJcblx0XHRpZiAodGhpcy5vcHRpb25zLmludGVyYWN0aXZlKSB7XHJcblx0XHRcdHRoaXMucmVtb3ZlSW50ZXJhY3RpdmVUYXJnZXQodGhpcy5faW1hZ2UpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0T3BhY2l0eShvcGFjaXR5OiBOdW1iZXIpOiB0aGlzXHJcblx0Ly8gU2V0cyB0aGUgb3BhY2l0eSBvZiB0aGUgb3ZlcmxheS5cclxuXHRzZXRPcGFjaXR5OiBmdW5jdGlvbiAob3BhY2l0eSkge1xyXG5cdFx0dGhpcy5vcHRpb25zLm9wYWNpdHkgPSBvcGFjaXR5O1xyXG5cclxuXHRcdGlmICh0aGlzLl9pbWFnZSkge1xyXG5cdFx0XHR0aGlzLl91cGRhdGVPcGFjaXR5KCk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRzZXRTdHlsZTogZnVuY3Rpb24gKHN0eWxlT3B0cykge1xyXG5cdFx0aWYgKHN0eWxlT3B0cy5vcGFjaXR5KSB7XHJcblx0XHRcdHRoaXMuc2V0T3BhY2l0eShzdHlsZU9wdHMub3BhY2l0eSk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGJyaW5nVG9Gcm9udCgpOiB0aGlzXHJcblx0Ly8gQnJpbmdzIHRoZSBsYXllciB0byB0aGUgdG9wIG9mIGFsbCBvdmVybGF5cy5cclxuXHRicmluZ1RvRnJvbnQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICh0aGlzLl9tYXApIHtcclxuXHRcdFx0TC5Eb21VdGlsLnRvRnJvbnQodGhpcy5faW1hZ2UpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBicmluZ1RvQmFjaygpOiB0aGlzXHJcblx0Ly8gQnJpbmdzIHRoZSBsYXllciB0byB0aGUgYm90dG9tIG9mIGFsbCBvdmVybGF5cy5cclxuXHRicmluZ1RvQmFjazogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKHRoaXMuX21hcCkge1xyXG5cdFx0XHRMLkRvbVV0aWwudG9CYWNrKHRoaXMuX2ltYWdlKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0VXJsKHVybDogU3RyaW5nKTogdGhpc1xyXG5cdC8vIENoYW5nZXMgdGhlIFVSTCBvZiB0aGUgaW1hZ2UuXHJcblx0c2V0VXJsOiBmdW5jdGlvbiAodXJsKSB7XHJcblx0XHR0aGlzLl91cmwgPSB1cmw7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2ltYWdlKSB7XHJcblx0XHRcdHRoaXMuX2ltYWdlLnNyYyA9IHVybDtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdHNldEJvdW5kczogZnVuY3Rpb24gKGJvdW5kcykge1xyXG5cdFx0dGhpcy5fYm91bmRzID0gYm91bmRzO1xyXG5cclxuXHRcdGlmICh0aGlzLl9tYXApIHtcclxuXHRcdFx0dGhpcy5fcmVzZXQoKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdGdldEV2ZW50czogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGV2ZW50cyA9IHtcclxuXHRcdFx0em9vbTogdGhpcy5fcmVzZXQsXHJcblx0XHRcdHZpZXdyZXNldDogdGhpcy5fcmVzZXRcclxuXHRcdH07XHJcblxyXG5cdFx0aWYgKHRoaXMuX3pvb21BbmltYXRlZCkge1xyXG5cdFx0XHRldmVudHMuem9vbWFuaW0gPSB0aGlzLl9hbmltYXRlWm9vbTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gZXZlbnRzO1xyXG5cdH0sXHJcblxyXG5cdGdldEJvdW5kczogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX2JvdW5kcztcclxuXHR9LFxyXG5cclxuXHRnZXRFbGVtZW50OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5faW1hZ2U7XHJcblx0fSxcclxuXHJcblx0X2luaXRJbWFnZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGltZyA9IHRoaXMuX2ltYWdlID0gTC5Eb21VdGlsLmNyZWF0ZSgnaW1nJyxcclxuXHRcdFx0XHQnbGVhZmxldC1pbWFnZS1sYXllciAnICsgKHRoaXMuX3pvb21BbmltYXRlZCA/ICdsZWFmbGV0LXpvb20tYW5pbWF0ZWQnIDogJycpKTtcclxuXHJcblx0XHRpbWcub25zZWxlY3RzdGFydCA9IEwuVXRpbC5mYWxzZUZuO1xyXG5cdFx0aW1nLm9ubW91c2Vtb3ZlID0gTC5VdGlsLmZhbHNlRm47XHJcblxyXG5cdFx0aW1nLm9ubG9hZCA9IEwuYmluZCh0aGlzLmZpcmUsIHRoaXMsICdsb2FkJyk7XHJcblxyXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5jcm9zc09yaWdpbikge1xyXG5cdFx0XHRpbWcuY3Jvc3NPcmlnaW4gPSAnJztcclxuXHRcdH1cclxuXHJcblx0XHRpbWcuc3JjID0gdGhpcy5fdXJsO1xyXG5cdFx0aW1nLmFsdCA9IHRoaXMub3B0aW9ucy5hbHQ7XHJcblx0fSxcclxuXHJcblx0X2FuaW1hdGVab29tOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0dmFyIHNjYWxlID0gdGhpcy5fbWFwLmdldFpvb21TY2FsZShlLnpvb20pLFxyXG5cdFx0ICAgIG9mZnNldCA9IHRoaXMuX21hcC5fbGF0TG5nQm91bmRzVG9OZXdMYXllckJvdW5kcyh0aGlzLl9ib3VuZHMsIGUuem9vbSwgZS5jZW50ZXIpLm1pbjtcclxuXHJcblx0XHRMLkRvbVV0aWwuc2V0VHJhbnNmb3JtKHRoaXMuX2ltYWdlLCBvZmZzZXQsIHNjYWxlKTtcclxuXHR9LFxyXG5cclxuXHRfcmVzZXQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBpbWFnZSA9IHRoaXMuX2ltYWdlLFxyXG5cdFx0ICAgIGJvdW5kcyA9IG5ldyBMLkJvdW5kcyhcclxuXHRcdCAgICAgICAgdGhpcy5fbWFwLmxhdExuZ1RvTGF5ZXJQb2ludCh0aGlzLl9ib3VuZHMuZ2V0Tm9ydGhXZXN0KCkpLFxyXG5cdFx0ICAgICAgICB0aGlzLl9tYXAubGF0TG5nVG9MYXllclBvaW50KHRoaXMuX2JvdW5kcy5nZXRTb3V0aEVhc3QoKSkpLFxyXG5cdFx0ICAgIHNpemUgPSBib3VuZHMuZ2V0U2l6ZSgpO1xyXG5cclxuXHRcdEwuRG9tVXRpbC5zZXRQb3NpdGlvbihpbWFnZSwgYm91bmRzLm1pbik7XHJcblxyXG5cdFx0aW1hZ2Uuc3R5bGUud2lkdGggID0gc2l6ZS54ICsgJ3B4JztcclxuXHRcdGltYWdlLnN0eWxlLmhlaWdodCA9IHNpemUueSArICdweCc7XHJcblx0fSxcclxuXHJcblx0X3VwZGF0ZU9wYWNpdHk6IGZ1bmN0aW9uICgpIHtcclxuXHRcdEwuRG9tVXRpbC5zZXRPcGFjaXR5KHRoaXMuX2ltYWdlLCB0aGlzLm9wdGlvbnMub3BhY2l0eSk7XHJcblx0fVxyXG59KTtcclxuXHJcbi8vIEBmYWN0b3J5IEwuaW1hZ2VPdmVybGF5KGltYWdlVXJsOiBTdHJpbmcsIGJvdW5kczogTGF0TG5nQm91bmRzLCBvcHRpb25zPzogSW1hZ2VPdmVybGF5IG9wdGlvbnMpXHJcbi8vIEluc3RhbnRpYXRlcyBhbiBpbWFnZSBvdmVybGF5IG9iamVjdCBnaXZlbiB0aGUgVVJMIG9mIHRoZSBpbWFnZSBhbmQgdGhlXHJcbi8vIGdlb2dyYXBoaWNhbCBib3VuZHMgaXQgaXMgdGllZCB0by5cclxuTC5pbWFnZU92ZXJsYXkgPSBmdW5jdGlvbiAodXJsLCBib3VuZHMsIG9wdGlvbnMpIHtcclxuXHRyZXR1cm4gbmV3IEwuSW1hZ2VPdmVybGF5KHVybCwgYm91bmRzLCBvcHRpb25zKTtcclxufTtcclxuXG5cblxuLypcclxuICogQGNsYXNzIEljb25cclxuICogQGFrYSBMLkljb25cclxuICogQGluaGVyaXRzIExheWVyXHJcbiAqXHJcbiAqIFJlcHJlc2VudHMgYW4gaWNvbiB0byBwcm92aWRlIHdoZW4gY3JlYXRpbmcgYSBtYXJrZXIuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIHZhciBteUljb24gPSBMLmljb24oe1xyXG4gKiAgICAgaWNvblVybDogJ215LWljb24ucG5nJyxcclxuICogICAgIGljb25SZXRpbmFVcmw6ICdteS1pY29uQDJ4LnBuZycsXHJcbiAqICAgICBpY29uU2l6ZTogWzM4LCA5NV0sXHJcbiAqICAgICBpY29uQW5jaG9yOiBbMjIsIDk0XSxcclxuICogICAgIHBvcHVwQW5jaG9yOiBbLTMsIC03Nl0sXHJcbiAqICAgICBzaGFkb3dVcmw6ICdteS1pY29uLXNoYWRvdy5wbmcnLFxyXG4gKiAgICAgc2hhZG93UmV0aW5hVXJsOiAnbXktaWNvbi1zaGFkb3dAMngucG5nJyxcclxuICogICAgIHNoYWRvd1NpemU6IFs2OCwgOTVdLFxyXG4gKiAgICAgc2hhZG93QW5jaG9yOiBbMjIsIDk0XVxyXG4gKiB9KTtcclxuICpcclxuICogTC5tYXJrZXIoWzUwLjUwNSwgMzAuNTddLCB7aWNvbjogbXlJY29ufSkuYWRkVG8obWFwKTtcclxuICogYGBgXHJcbiAqXHJcbiAqIGBMLkljb24uRGVmYXVsdGAgZXh0ZW5kcyBgTC5JY29uYCBhbmQgaXMgdGhlIGJsdWUgaWNvbiBMZWFmbGV0IHVzZXMgZm9yIG1hcmtlcnMgYnkgZGVmYXVsdC5cclxuICpcclxuICovXHJcblxyXG5MLkljb24gPSBMLkNsYXNzLmV4dGVuZCh7XHJcblxyXG5cdC8qIEBzZWN0aW9uXHJcblx0ICogQGFrYSBJY29uIG9wdGlvbnNcclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gaWNvblVybDogU3RyaW5nID0gbnVsbFxyXG5cdCAqICoqKHJlcXVpcmVkKSoqIFRoZSBVUkwgdG8gdGhlIGljb24gaW1hZ2UgKGFic29sdXRlIG9yIHJlbGF0aXZlIHRvIHlvdXIgc2NyaXB0IHBhdGgpLlxyXG5cdCAqXHJcblx0ICogQG9wdGlvbiBpY29uUmV0aW5hVXJsOiBTdHJpbmcgPSBudWxsXHJcblx0ICogVGhlIFVSTCB0byBhIHJldGluYSBzaXplZCB2ZXJzaW9uIG9mIHRoZSBpY29uIGltYWdlIChhYnNvbHV0ZSBvciByZWxhdGl2ZSB0byB5b3VyXHJcblx0ICogc2NyaXB0IHBhdGgpLiBVc2VkIGZvciBSZXRpbmEgc2NyZWVuIGRldmljZXMuXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIGljb25TaXplOiBQb2ludCA9IG51bGxcclxuXHQgKiBTaXplIG9mIHRoZSBpY29uIGltYWdlIGluIHBpeGVscy5cclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gaWNvbkFuY2hvcjogUG9pbnQgPSBudWxsXHJcblx0ICogVGhlIGNvb3JkaW5hdGVzIG9mIHRoZSBcInRpcFwiIG9mIHRoZSBpY29uIChyZWxhdGl2ZSB0byBpdHMgdG9wIGxlZnQgY29ybmVyKS4gVGhlIGljb25cclxuXHQgKiB3aWxsIGJlIGFsaWduZWQgc28gdGhhdCB0aGlzIHBvaW50IGlzIGF0IHRoZSBtYXJrZXIncyBnZW9ncmFwaGljYWwgbG9jYXRpb24uIENlbnRlcmVkXHJcblx0ICogYnkgZGVmYXVsdCBpZiBzaXplIGlzIHNwZWNpZmllZCwgYWxzbyBjYW4gYmUgc2V0IGluIENTUyB3aXRoIG5lZ2F0aXZlIG1hcmdpbnMuXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIHBvcHVwQW5jaG9yOiBQb2ludCA9IG51bGxcclxuXHQgKiBUaGUgY29vcmRpbmF0ZXMgb2YgdGhlIHBvaW50IGZyb20gd2hpY2ggcG9wdXBzIHdpbGwgXCJvcGVuXCIsIHJlbGF0aXZlIHRvIHRoZSBpY29uIGFuY2hvci5cclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gc2hhZG93VXJsOiBTdHJpbmcgPSBudWxsXHJcblx0ICogVGhlIFVSTCB0byB0aGUgaWNvbiBzaGFkb3cgaW1hZ2UuIElmIG5vdCBzcGVjaWZpZWQsIG5vIHNoYWRvdyBpbWFnZSB3aWxsIGJlIGNyZWF0ZWQuXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIHNoYWRvd1JldGluYVVybDogU3RyaW5nID0gbnVsbFxyXG5cdCAqXHJcblx0ICogQG9wdGlvbiBzaGFkb3dTaXplOiBQb2ludCA9IG51bGxcclxuXHQgKiBTaXplIG9mIHRoZSBzaGFkb3cgaW1hZ2UgaW4gcGl4ZWxzLlxyXG5cdCAqXHJcblx0ICogQG9wdGlvbiBzaGFkb3dBbmNob3I6IFBvaW50ID0gbnVsbFxyXG5cdCAqIFRoZSBjb29yZGluYXRlcyBvZiB0aGUgXCJ0aXBcIiBvZiB0aGUgc2hhZG93IChyZWxhdGl2ZSB0byBpdHMgdG9wIGxlZnQgY29ybmVyKSAodGhlIHNhbWVcclxuXHQgKiBhcyBpY29uQW5jaG9yIGlmIG5vdCBzcGVjaWZpZWQpLlxyXG5cdCAqXHJcblx0ICogQG9wdGlvbiBjbGFzc05hbWU6IFN0cmluZyA9ICcnXHJcblx0ICogQSBjdXN0b20gY2xhc3MgbmFtZSB0byBhc3NpZ24gdG8gYm90aCBpY29uIGFuZCBzaGFkb3cgaW1hZ2VzLiBFbXB0eSBieSBkZWZhdWx0LlxyXG5cdCAqL1xyXG5cclxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAob3B0aW9ucykge1xyXG5cdFx0TC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgY3JlYXRlSWNvbihvbGRJY29uPzogSFRNTEVsZW1lbnQpOiBIVE1MRWxlbWVudFxyXG5cdC8vIENhbGxlZCBpbnRlcm5hbGx5IHdoZW4gdGhlIGljb24gaGFzIHRvIGJlIHNob3duLCByZXR1cm5zIGEgYDxpbWc+YCBIVE1MIGVsZW1lbnRcclxuXHQvLyBzdHlsZWQgYWNjb3JkaW5nIHRvIHRoZSBvcHRpb25zLlxyXG5cdGNyZWF0ZUljb246IGZ1bmN0aW9uIChvbGRJY29uKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fY3JlYXRlSWNvbignaWNvbicsIG9sZEljb24pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgY3JlYXRlU2hhZG93KG9sZEljb24/OiBIVE1MRWxlbWVudCk6IEhUTUxFbGVtZW50XHJcblx0Ly8gQXMgYGNyZWF0ZUljb25gLCBidXQgZm9yIHRoZSBzaGFkb3cgYmVuZWF0aCBpdC5cclxuXHRjcmVhdGVTaGFkb3c6IGZ1bmN0aW9uIChvbGRJY29uKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fY3JlYXRlSWNvbignc2hhZG93Jywgb2xkSWNvbik7XHJcblx0fSxcclxuXHJcblx0X2NyZWF0ZUljb246IGZ1bmN0aW9uIChuYW1lLCBvbGRJY29uKSB7XHJcblx0XHR2YXIgc3JjID0gdGhpcy5fZ2V0SWNvblVybChuYW1lKTtcclxuXHJcblx0XHRpZiAoIXNyYykge1xyXG5cdFx0XHRpZiAobmFtZSA9PT0gJ2ljb24nKSB7XHJcblx0XHRcdFx0dGhyb3cgbmV3IEVycm9yKCdpY29uVXJsIG5vdCBzZXQgaW4gSWNvbiBvcHRpb25zIChzZWUgdGhlIGRvY3MpLicpO1xyXG5cdFx0XHR9XHJcblx0XHRcdHJldHVybiBudWxsO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBpbWcgPSB0aGlzLl9jcmVhdGVJbWcoc3JjLCBvbGRJY29uICYmIG9sZEljb24udGFnTmFtZSA9PT0gJ0lNRycgPyBvbGRJY29uIDogbnVsbCk7XHJcblx0XHR0aGlzLl9zZXRJY29uU3R5bGVzKGltZywgbmFtZSk7XHJcblxyXG5cdFx0cmV0dXJuIGltZztcclxuXHR9LFxyXG5cclxuXHRfc2V0SWNvblN0eWxlczogZnVuY3Rpb24gKGltZywgbmFtZSkge1xyXG5cdFx0dmFyIG9wdGlvbnMgPSB0aGlzLm9wdGlvbnM7XHJcblx0XHR2YXIgc2l6ZU9wdGlvbiA9IG9wdGlvbnNbbmFtZSArICdTaXplJ107XHJcblxyXG5cdFx0aWYgKHR5cGVvZiBzaXplT3B0aW9uID09PSAnbnVtYmVyJykge1xyXG5cdFx0XHRzaXplT3B0aW9uID0gW3NpemVPcHRpb24sIHNpemVPcHRpb25dO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBzaXplID0gTC5wb2ludChzaXplT3B0aW9uKSxcclxuXHRcdCAgICBhbmNob3IgPSBMLnBvaW50KG5hbWUgPT09ICdzaGFkb3cnICYmIG9wdGlvbnMuc2hhZG93QW5jaG9yIHx8IG9wdGlvbnMuaWNvbkFuY2hvciB8fFxyXG5cdFx0ICAgICAgICAgICAgc2l6ZSAmJiBzaXplLmRpdmlkZUJ5KDIsIHRydWUpKTtcclxuXHJcblx0XHRpbWcuY2xhc3NOYW1lID0gJ2xlYWZsZXQtbWFya2VyLScgKyBuYW1lICsgJyAnICsgKG9wdGlvbnMuY2xhc3NOYW1lIHx8ICcnKTtcclxuXHJcblx0XHRpZiAoYW5jaG9yKSB7XHJcblx0XHRcdGltZy5zdHlsZS5tYXJnaW5MZWZ0ID0gKC1hbmNob3IueCkgKyAncHgnO1xyXG5cdFx0XHRpbWcuc3R5bGUubWFyZ2luVG9wICA9ICgtYW5jaG9yLnkpICsgJ3B4JztcclxuXHRcdH1cclxuXHJcblx0XHRpZiAoc2l6ZSkge1xyXG5cdFx0XHRpbWcuc3R5bGUud2lkdGggID0gc2l6ZS54ICsgJ3B4JztcclxuXHRcdFx0aW1nLnN0eWxlLmhlaWdodCA9IHNpemUueSArICdweCc7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2NyZWF0ZUltZzogZnVuY3Rpb24gKHNyYywgZWwpIHtcclxuXHRcdGVsID0gZWwgfHwgZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaW1nJyk7XHJcblx0XHRlbC5zcmMgPSBzcmM7XHJcblx0XHRyZXR1cm4gZWw7XHJcblx0fSxcclxuXHJcblx0X2dldEljb25Vcmw6IGZ1bmN0aW9uIChuYW1lKSB7XHJcblx0XHRyZXR1cm4gTC5Ccm93c2VyLnJldGluYSAmJiB0aGlzLm9wdGlvbnNbbmFtZSArICdSZXRpbmFVcmwnXSB8fCB0aGlzLm9wdGlvbnNbbmFtZSArICdVcmwnXTtcclxuXHR9XHJcbn0pO1xyXG5cclxuXHJcbi8vIEBmYWN0b3J5IEwuaWNvbihvcHRpb25zOiBJY29uIG9wdGlvbnMpXHJcbi8vIENyZWF0ZXMgYW4gaWNvbiBpbnN0YW5jZSB3aXRoIHRoZSBnaXZlbiBvcHRpb25zLlxyXG5MLmljb24gPSBmdW5jdGlvbiAob3B0aW9ucykge1xyXG5cdHJldHVybiBuZXcgTC5JY29uKG9wdGlvbnMpO1xyXG59O1xyXG5cblxuXG4vKlxuICogQG1pbmljbGFzcyBJY29uLkRlZmF1bHQgKEljb24pXG4gKiBAYWthIEwuSWNvbi5EZWZhdWx0XG4gKiBAc2VjdGlvblxuICpcbiAqIEEgdHJpdmlhbCBzdWJjbGFzcyBvZiBgSWNvbmAsIHJlcHJlc2VudHMgdGhlIGljb24gdG8gdXNlIGluIGBNYXJrZXJgcyB3aGVuXG4gKiBubyBpY29uIGlzIHNwZWNpZmllZC4gUG9pbnRzIHRvIHRoZSBibHVlIG1hcmtlciBpbWFnZSBkaXN0cmlidXRlZCB3aXRoIExlYWZsZXRcbiAqIHJlbGVhc2VzLlxuICpcbiAqIEluIG9yZGVyIHRvIGN1c3RvbWl6ZSB0aGUgZGVmYXVsdCBpY29uLCBqdXN0IGNoYW5nZSB0aGUgcHJvcGVydGllcyBvZiBgTC5JY29uLkRlZmF1bHQucHJvdG90eXBlLm9wdGlvbnNgXG4gKiAod2hpY2ggaXMgYSBzZXQgb2YgYEljb24gb3B0aW9uc2ApLlxuICpcbiAqIElmIHlvdSB3YW50IHRvIF9jb21wbGV0ZWx5XyByZXBsYWNlIHRoZSBkZWZhdWx0IGljb24sIG92ZXJyaWRlIHRoZVxuICogYEwuTWFya2VyLnByb3RvdHlwZS5vcHRpb25zLmljb25gIHdpdGggeW91ciBvd24gaWNvbiBpbnN0ZWFkLlxuICovXG5cbkwuSWNvbi5EZWZhdWx0ID0gTC5JY29uLmV4dGVuZCh7XG5cblx0b3B0aW9uczoge1xuXHRcdGljb25Vcmw6ICAgICAgICdtYXJrZXItaWNvbi5wbmcnLFxuXHRcdGljb25SZXRpbmFVcmw6ICdtYXJrZXItaWNvbi0yeC5wbmcnLFxuXHRcdHNoYWRvd1VybDogICAgICdtYXJrZXItc2hhZG93LnBuZycsXG5cdFx0aWNvblNpemU6ICAgIFsyNSwgNDFdLFxuXHRcdGljb25BbmNob3I6ICBbMTIsIDQxXSxcblx0XHRwb3B1cEFuY2hvcjogWzEsIC0zNF0sXG5cdFx0dG9vbHRpcEFuY2hvcjogWzE2LCAtMjhdLFxuXHRcdHNoYWRvd1NpemU6ICBbNDEsIDQxXVxuXHR9LFxuXG5cdF9nZXRJY29uVXJsOiBmdW5jdGlvbiAobmFtZSkge1xuXHRcdGlmICghTC5JY29uLkRlZmF1bHQuaW1hZ2VQYXRoKSB7XHQvLyBEZXByZWNhdGVkLCBiYWNrd2FyZHMtY29tcGF0aWJpbGl0eSBvbmx5XG5cdFx0XHRMLkljb24uRGVmYXVsdC5pbWFnZVBhdGggPSB0aGlzLl9kZXRlY3RJY29uUGF0aCgpO1xuXHRcdH1cblxuXHRcdC8vIEBvcHRpb24gaW1hZ2VQYXRoOiBTdHJpbmdcblx0XHQvLyBgTC5JY29uLkRlZmF1bHRgIHdpbGwgdHJ5IHRvIGF1dG8tZGV0ZWN0IHRoZSBhYnNvbHV0ZSBsb2NhdGlvbiBvZiB0aGVcblx0XHQvLyBibHVlIGljb24gaW1hZ2VzLiBJZiB5b3UgYXJlIHBsYWNpbmcgdGhlc2UgaW1hZ2VzIGluIGEgbm9uLXN0YW5kYXJkXG5cdFx0Ly8gd2F5LCBzZXQgdGhpcyBvcHRpb24gdG8gcG9pbnQgdG8gdGhlIHJpZ2h0IGFic29sdXRlIHBhdGguXG5cdFx0cmV0dXJuICh0aGlzLm9wdGlvbnMuaW1hZ2VQYXRoIHx8IEwuSWNvbi5EZWZhdWx0LmltYWdlUGF0aCkgKyBMLkljb24ucHJvdG90eXBlLl9nZXRJY29uVXJsLmNhbGwodGhpcywgbmFtZSk7XG5cdH0sXG5cblx0X2RldGVjdEljb25QYXRoOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIGVsID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgICdsZWFmbGV0LWRlZmF1bHQtaWNvbi1wYXRoJywgZG9jdW1lbnQuYm9keSk7XG5cdFx0dmFyIHBhdGggPSBMLkRvbVV0aWwuZ2V0U3R5bGUoZWwsICdiYWNrZ3JvdW5kLWltYWdlJykgfHxcblx0XHQgICAgICAgICAgIEwuRG9tVXRpbC5nZXRTdHlsZShlbCwgJ2JhY2tncm91bmRJbWFnZScpO1x0Ly8gSUU4XG5cblx0XHRkb2N1bWVudC5ib2R5LnJlbW92ZUNoaWxkKGVsKTtcblxuXHRcdHJldHVybiBwYXRoLmluZGV4T2YoJ3VybCcpID09PSAwID9cblx0XHRcdHBhdGgucmVwbGFjZSgvXnVybFxcKFtcXFwiXFwnXT8vLCAnJykucmVwbGFjZSgvbWFya2VyLWljb25cXC5wbmdbXFxcIlxcJ10/XFwpJC8sICcnKSA6ICcnO1xuXHR9XG59KTtcblxuXG5cbi8qXHJcbiAqIEBjbGFzcyBNYXJrZXJcclxuICogQGluaGVyaXRzIEludGVyYWN0aXZlIGxheWVyXHJcbiAqIEBha2EgTC5NYXJrZXJcclxuICogTC5NYXJrZXIgaXMgdXNlZCB0byBkaXNwbGF5IGNsaWNrYWJsZS9kcmFnZ2FibGUgaWNvbnMgb24gdGhlIG1hcC4gRXh0ZW5kcyBgTGF5ZXJgLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBMLm1hcmtlcihbNTAuNSwgMzAuNV0pLmFkZFRvKG1hcCk7XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcbkwuTWFya2VyID0gTC5MYXllci5leHRlbmQoe1xyXG5cclxuXHQvLyBAc2VjdGlvblxyXG5cdC8vIEBha2EgTWFya2VyIG9wdGlvbnNcclxuXHRvcHRpb25zOiB7XHJcblx0XHQvLyBAb3B0aW9uIGljb246IEljb24gPSAqXHJcblx0XHQvLyBJY29uIGNsYXNzIHRvIHVzZSBmb3IgcmVuZGVyaW5nIHRoZSBtYXJrZXIuIFNlZSBbSWNvbiBkb2N1bWVudGF0aW9uXSgjTC5JY29uKSBmb3IgZGV0YWlscyBvbiBob3cgdG8gY3VzdG9taXplIHRoZSBtYXJrZXIgaWNvbi4gSWYgbm90IHNwZWNpZmllZCwgYSBuZXcgYEwuSWNvbi5EZWZhdWx0YCBpcyB1c2VkLlxyXG5cdFx0aWNvbjogbmV3IEwuSWNvbi5EZWZhdWx0KCksXHJcblxyXG5cdFx0Ly8gT3B0aW9uIGluaGVyaXRlZCBmcm9tIFwiSW50ZXJhY3RpdmUgbGF5ZXJcIiBhYnN0cmFjdCBjbGFzc1xyXG5cdFx0aW50ZXJhY3RpdmU6IHRydWUsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBkcmFnZ2FibGU6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gV2hldGhlciB0aGUgbWFya2VyIGlzIGRyYWdnYWJsZSB3aXRoIG1vdXNlL3RvdWNoIG9yIG5vdC5cclxuXHRcdGRyYWdnYWJsZTogZmFsc2UsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBrZXlib2FyZDogQm9vbGVhbiA9IHRydWVcclxuXHRcdC8vIFdoZXRoZXIgdGhlIG1hcmtlciBjYW4gYmUgdGFiYmVkIHRvIHdpdGggYSBrZXlib2FyZCBhbmQgY2xpY2tlZCBieSBwcmVzc2luZyBlbnRlci5cclxuXHRcdGtleWJvYXJkOiB0cnVlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gdGl0bGU6IFN0cmluZyA9ICcnXHJcblx0XHQvLyBUZXh0IGZvciB0aGUgYnJvd3NlciB0b29sdGlwIHRoYXQgYXBwZWFyIG9uIG1hcmtlciBob3ZlciAobm8gdG9vbHRpcCBieSBkZWZhdWx0KS5cclxuXHRcdHRpdGxlOiAnJyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGFsdDogU3RyaW5nID0gJydcclxuXHRcdC8vIFRleHQgZm9yIHRoZSBgYWx0YCBhdHRyaWJ1dGUgb2YgdGhlIGljb24gaW1hZ2UgKHVzZWZ1bCBmb3IgYWNjZXNzaWJpbGl0eSkuXHJcblx0XHRhbHQ6ICcnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gekluZGV4T2Zmc2V0OiBOdW1iZXIgPSAwXHJcblx0XHQvLyBCeSBkZWZhdWx0LCBtYXJrZXIgaW1hZ2VzIHpJbmRleCBpcyBzZXQgYXV0b21hdGljYWxseSBiYXNlZCBvbiBpdHMgbGF0aXR1ZGUuIFVzZSB0aGlzIG9wdGlvbiBpZiB5b3Ugd2FudCB0byBwdXQgdGhlIG1hcmtlciBvbiB0b3Agb2YgYWxsIG90aGVycyAob3IgYmVsb3cpLCBzcGVjaWZ5aW5nIGEgaGlnaCB2YWx1ZSBsaWtlIGAxMDAwYCAob3IgaGlnaCBuZWdhdGl2ZSB2YWx1ZSwgcmVzcGVjdGl2ZWx5KS5cclxuXHRcdHpJbmRleE9mZnNldDogMCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIG9wYWNpdHk6IE51bWJlciA9IDEuMFxyXG5cdFx0Ly8gVGhlIG9wYWNpdHkgb2YgdGhlIG1hcmtlci5cclxuXHRcdG9wYWNpdHk6IDEsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiByaXNlT25Ib3ZlcjogQm9vbGVhbiA9IGZhbHNlXHJcblx0XHQvLyBJZiBgdHJ1ZWAsIHRoZSBtYXJrZXIgd2lsbCBnZXQgb24gdG9wIG9mIG90aGVycyB3aGVuIHlvdSBob3ZlciB0aGUgbW91c2Ugb3ZlciBpdC5cclxuXHRcdHJpc2VPbkhvdmVyOiBmYWxzZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHJpc2VPZmZzZXQ6IE51bWJlciA9IDI1MFxyXG5cdFx0Ly8gVGhlIHotaW5kZXggb2Zmc2V0IHVzZWQgZm9yIHRoZSBgcmlzZU9uSG92ZXJgIGZlYXR1cmUuXHJcblx0XHRyaXNlT2Zmc2V0OiAyNTAsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBwYW5lOiBTdHJpbmcgPSAnbWFya2VyUGFuZSdcclxuXHRcdC8vIGBNYXAgcGFuZWAgd2hlcmUgdGhlIG1hcmtlcnMgaWNvbiB3aWxsIGJlIGFkZGVkLlxyXG5cdFx0cGFuZTogJ21hcmtlclBhbmUnLFxyXG5cclxuXHRcdC8vIEZJWE1FOiBzaGFkb3dQYW5lIGlzIG5vIGxvbmdlciBhIHZhbGlkIG9wdGlvblxyXG5cdFx0bm9uQnViYmxpbmdFdmVudHM6IFsnY2xpY2snLCAnZGJsY2xpY2snLCAnbW91c2VvdmVyJywgJ21vdXNlb3V0JywgJ2NvbnRleHRtZW51J11cclxuXHR9LFxyXG5cclxuXHQvKiBAc2VjdGlvblxyXG5cdCAqXHJcblx0ICogSW4gYWRkaXRpb24gdG8gW3NoYXJlZCBsYXllciBtZXRob2RzXSgjTGF5ZXIpIGxpa2UgYGFkZFRvKClgIGFuZCBgcmVtb3ZlKClgIGFuZCBbcG9wdXAgbWV0aG9kc10oI1BvcHVwKSBsaWtlIGJpbmRQb3B1cCgpIHlvdSBjYW4gYWxzbyB1c2UgdGhlIGZvbGxvd2luZyBtZXRob2RzOlxyXG5cdCAqL1xyXG5cclxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAobGF0bG5nLCBvcHRpb25zKSB7XHJcblx0XHRMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XHJcblx0XHR0aGlzLl9sYXRsbmcgPSBMLmxhdExuZyhsYXRsbmcpO1xyXG5cdH0sXHJcblxyXG5cdG9uQWRkOiBmdW5jdGlvbiAobWFwKSB7XHJcblx0XHR0aGlzLl96b29tQW5pbWF0ZWQgPSB0aGlzLl96b29tQW5pbWF0ZWQgJiYgbWFwLm9wdGlvbnMubWFya2VyWm9vbUFuaW1hdGlvbjtcclxuXHJcblx0XHRpZiAodGhpcy5fem9vbUFuaW1hdGVkKSB7XHJcblx0XHRcdG1hcC5vbignem9vbWFuaW0nLCB0aGlzLl9hbmltYXRlWm9vbSwgdGhpcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5faW5pdEljb24oKTtcclxuXHRcdHRoaXMudXBkYXRlKCk7XHJcblx0fSxcclxuXHJcblx0b25SZW1vdmU6IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdGlmICh0aGlzLmRyYWdnaW5nICYmIHRoaXMuZHJhZ2dpbmcuZW5hYmxlZCgpKSB7XHJcblx0XHRcdHRoaXMub3B0aW9ucy5kcmFnZ2FibGUgPSB0cnVlO1xyXG5cdFx0XHR0aGlzLmRyYWdnaW5nLnJlbW92ZUhvb2tzKCk7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKHRoaXMuX3pvb21BbmltYXRlZCkge1xyXG5cdFx0XHRtYXAub2ZmKCd6b29tYW5pbScsIHRoaXMuX2FuaW1hdGVab29tLCB0aGlzKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9yZW1vdmVJY29uKCk7XHJcblx0XHR0aGlzLl9yZW1vdmVTaGFkb3coKTtcclxuXHR9LFxyXG5cclxuXHRnZXRFdmVudHM6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB7XHJcblx0XHRcdHpvb206IHRoaXMudXBkYXRlLFxyXG5cdFx0XHR2aWV3cmVzZXQ6IHRoaXMudXBkYXRlXHJcblx0XHR9O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0TGF0TG5nOiBMYXRMbmdcclxuXHQvLyBSZXR1cm5zIHRoZSBjdXJyZW50IGdlb2dyYXBoaWNhbCBwb3NpdGlvbiBvZiB0aGUgbWFya2VyLlxyXG5cdGdldExhdExuZzogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX2xhdGxuZztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldExhdExuZyhsYXRsbmc6IExhdExuZyk6IHRoaXNcclxuXHQvLyBDaGFuZ2VzIHRoZSBtYXJrZXIgcG9zaXRpb24gdG8gdGhlIGdpdmVuIHBvaW50LlxyXG5cdHNldExhdExuZzogZnVuY3Rpb24gKGxhdGxuZykge1xyXG5cdFx0dmFyIG9sZExhdExuZyA9IHRoaXMuX2xhdGxuZztcclxuXHRcdHRoaXMuX2xhdGxuZyA9IEwubGF0TG5nKGxhdGxuZyk7XHJcblx0XHR0aGlzLnVwZGF0ZSgpO1xyXG5cclxuXHRcdC8vIEBldmVudCBtb3ZlOiBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgbWFya2VyIGlzIG1vdmVkIHZpYSBbYHNldExhdExuZ2BdKCNtYXJrZXItc2V0bGF0bG5nKSBvciBieSBbZHJhZ2dpbmddKCNtYXJrZXItZHJhZ2dpbmcpLiBPbGQgYW5kIG5ldyBjb29yZGluYXRlcyBhcmUgaW5jbHVkZWQgaW4gZXZlbnQgYXJndW1lbnRzIGFzIGBvbGRMYXRMbmdgLCBgbGF0bG5nYC5cclxuXHRcdHJldHVybiB0aGlzLmZpcmUoJ21vdmUnLCB7b2xkTGF0TG5nOiBvbGRMYXRMbmcsIGxhdGxuZzogdGhpcy5fbGF0bG5nfSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzZXRaSW5kZXhPZmZzZXQob2Zmc2V0OiBOdW1iZXIpOiB0aGlzXHJcblx0Ly8gQ2hhbmdlcyB0aGUgW3pJbmRleCBvZmZzZXRdKCNtYXJrZXItemluZGV4b2Zmc2V0KSBvZiB0aGUgbWFya2VyLlxyXG5cdHNldFpJbmRleE9mZnNldDogZnVuY3Rpb24gKG9mZnNldCkge1xyXG5cdFx0dGhpcy5vcHRpb25zLnpJbmRleE9mZnNldCA9IG9mZnNldDtcclxuXHRcdHJldHVybiB0aGlzLnVwZGF0ZSgpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0SWNvbihpY29uOiBJY29uKTogdGhpc1xyXG5cdC8vIENoYW5nZXMgdGhlIG1hcmtlciBpY29uLlxyXG5cdHNldEljb246IGZ1bmN0aW9uIChpY29uKSB7XHJcblxyXG5cdFx0dGhpcy5vcHRpb25zLmljb24gPSBpY29uO1xyXG5cclxuXHRcdGlmICh0aGlzLl9tYXApIHtcclxuXHRcdFx0dGhpcy5faW5pdEljb24oKTtcclxuXHRcdFx0dGhpcy51cGRhdGUoKTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAodGhpcy5fcG9wdXApIHtcclxuXHRcdFx0dGhpcy5iaW5kUG9wdXAodGhpcy5fcG9wdXAsIHRoaXMuX3BvcHVwLm9wdGlvbnMpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdGdldEVsZW1lbnQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9pY29uO1xyXG5cdH0sXHJcblxyXG5cdHVwZGF0ZTogZnVuY3Rpb24gKCkge1xyXG5cclxuXHRcdGlmICh0aGlzLl9pY29uKSB7XHJcblx0XHRcdHZhciBwb3MgPSB0aGlzLl9tYXAubGF0TG5nVG9MYXllclBvaW50KHRoaXMuX2xhdGxuZykucm91bmQoKTtcclxuXHRcdFx0dGhpcy5fc2V0UG9zKHBvcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0X2luaXRJY29uOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgb3B0aW9ucyA9IHRoaXMub3B0aW9ucyxcclxuXHRcdCAgICBjbGFzc1RvQWRkID0gJ2xlYWZsZXQtem9vbS0nICsgKHRoaXMuX3pvb21BbmltYXRlZCA/ICdhbmltYXRlZCcgOiAnaGlkZScpO1xyXG5cclxuXHRcdHZhciBpY29uID0gb3B0aW9ucy5pY29uLmNyZWF0ZUljb24odGhpcy5faWNvbiksXHJcblx0XHQgICAgYWRkSWNvbiA9IGZhbHNlO1xyXG5cclxuXHRcdC8vIGlmIHdlJ3JlIG5vdCByZXVzaW5nIHRoZSBpY29uLCByZW1vdmUgdGhlIG9sZCBvbmUgYW5kIGluaXQgbmV3IG9uZVxyXG5cdFx0aWYgKGljb24gIT09IHRoaXMuX2ljb24pIHtcclxuXHRcdFx0aWYgKHRoaXMuX2ljb24pIHtcclxuXHRcdFx0XHR0aGlzLl9yZW1vdmVJY29uKCk7XHJcblx0XHRcdH1cclxuXHRcdFx0YWRkSWNvbiA9IHRydWU7XHJcblxyXG5cdFx0XHRpZiAob3B0aW9ucy50aXRsZSkge1xyXG5cdFx0XHRcdGljb24udGl0bGUgPSBvcHRpb25zLnRpdGxlO1xyXG5cdFx0XHR9XHJcblx0XHRcdGlmIChvcHRpb25zLmFsdCkge1xyXG5cdFx0XHRcdGljb24uYWx0ID0gb3B0aW9ucy5hbHQ7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHRMLkRvbVV0aWwuYWRkQ2xhc3MoaWNvbiwgY2xhc3NUb0FkZCk7XHJcblxyXG5cdFx0aWYgKG9wdGlvbnMua2V5Ym9hcmQpIHtcclxuXHRcdFx0aWNvbi50YWJJbmRleCA9ICcwJztcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9pY29uID0gaWNvbjtcclxuXHJcblx0XHRpZiAob3B0aW9ucy5yaXNlT25Ib3Zlcikge1xyXG5cdFx0XHR0aGlzLm9uKHtcclxuXHRcdFx0XHRtb3VzZW92ZXI6IHRoaXMuX2JyaW5nVG9Gcm9udCxcclxuXHRcdFx0XHRtb3VzZW91dDogdGhpcy5fcmVzZXRaSW5kZXhcclxuXHRcdFx0fSk7XHJcblx0XHR9XHJcblxyXG5cdFx0dmFyIG5ld1NoYWRvdyA9IG9wdGlvbnMuaWNvbi5jcmVhdGVTaGFkb3codGhpcy5fc2hhZG93KSxcclxuXHRcdCAgICBhZGRTaGFkb3cgPSBmYWxzZTtcclxuXHJcblx0XHRpZiAobmV3U2hhZG93ICE9PSB0aGlzLl9zaGFkb3cpIHtcclxuXHRcdFx0dGhpcy5fcmVtb3ZlU2hhZG93KCk7XHJcblx0XHRcdGFkZFNoYWRvdyA9IHRydWU7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKG5ld1NoYWRvdykge1xyXG5cdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3MobmV3U2hhZG93LCBjbGFzc1RvQWRkKTtcclxuXHRcdH1cclxuXHRcdHRoaXMuX3NoYWRvdyA9IG5ld1NoYWRvdztcclxuXHJcblxyXG5cdFx0aWYgKG9wdGlvbnMub3BhY2l0eSA8IDEpIHtcclxuXHRcdFx0dGhpcy5fdXBkYXRlT3BhY2l0eSgpO1xyXG5cdFx0fVxyXG5cclxuXHJcblx0XHRpZiAoYWRkSWNvbikge1xyXG5cdFx0XHR0aGlzLmdldFBhbmUoKS5hcHBlbmRDaGlsZCh0aGlzLl9pY29uKTtcclxuXHRcdH1cclxuXHRcdHRoaXMuX2luaXRJbnRlcmFjdGlvbigpO1xyXG5cdFx0aWYgKG5ld1NoYWRvdyAmJiBhZGRTaGFkb3cpIHtcclxuXHRcdFx0dGhpcy5nZXRQYW5lKCdzaGFkb3dQYW5lJykuYXBwZW5kQ2hpbGQodGhpcy5fc2hhZG93KTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfcmVtb3ZlSWNvbjogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5yaXNlT25Ib3Zlcikge1xyXG5cdFx0XHR0aGlzLm9mZih7XHJcblx0XHRcdFx0bW91c2VvdmVyOiB0aGlzLl9icmluZ1RvRnJvbnQsXHJcblx0XHRcdFx0bW91c2VvdXQ6IHRoaXMuX3Jlc2V0WkluZGV4XHJcblx0XHRcdH0pO1xyXG5cdFx0fVxyXG5cclxuXHRcdEwuRG9tVXRpbC5yZW1vdmUodGhpcy5faWNvbik7XHJcblx0XHR0aGlzLnJlbW92ZUludGVyYWN0aXZlVGFyZ2V0KHRoaXMuX2ljb24pO1xyXG5cclxuXHRcdHRoaXMuX2ljb24gPSBudWxsO1xyXG5cdH0sXHJcblxyXG5cdF9yZW1vdmVTaGFkb3c6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICh0aGlzLl9zaGFkb3cpIHtcclxuXHRcdFx0TC5Eb21VdGlsLnJlbW92ZSh0aGlzLl9zaGFkb3cpO1xyXG5cdFx0fVxyXG5cdFx0dGhpcy5fc2hhZG93ID0gbnVsbDtcclxuXHR9LFxyXG5cclxuXHRfc2V0UG9zOiBmdW5jdGlvbiAocG9zKSB7XHJcblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24odGhpcy5faWNvbiwgcG9zKTtcclxuXHJcblx0XHRpZiAodGhpcy5fc2hhZG93KSB7XHJcblx0XHRcdEwuRG9tVXRpbC5zZXRQb3NpdGlvbih0aGlzLl9zaGFkb3csIHBvcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fekluZGV4ID0gcG9zLnkgKyB0aGlzLm9wdGlvbnMuekluZGV4T2Zmc2V0O1xyXG5cclxuXHRcdHRoaXMuX3Jlc2V0WkluZGV4KCk7XHJcblx0fSxcclxuXHJcblx0X3VwZGF0ZVpJbmRleDogZnVuY3Rpb24gKG9mZnNldCkge1xyXG5cdFx0dGhpcy5faWNvbi5zdHlsZS56SW5kZXggPSB0aGlzLl96SW5kZXggKyBvZmZzZXQ7XHJcblx0fSxcclxuXHJcblx0X2FuaW1hdGVab29tOiBmdW5jdGlvbiAob3B0KSB7XHJcblx0XHR2YXIgcG9zID0gdGhpcy5fbWFwLl9sYXRMbmdUb05ld0xheWVyUG9pbnQodGhpcy5fbGF0bG5nLCBvcHQuem9vbSwgb3B0LmNlbnRlcikucm91bmQoKTtcclxuXHJcblx0XHR0aGlzLl9zZXRQb3MocG9zKTtcclxuXHR9LFxyXG5cclxuXHRfaW5pdEludGVyYWN0aW9uOiBmdW5jdGlvbiAoKSB7XHJcblxyXG5cdFx0aWYgKCF0aGlzLm9wdGlvbnMuaW50ZXJhY3RpdmUpIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0TC5Eb21VdGlsLmFkZENsYXNzKHRoaXMuX2ljb24sICdsZWFmbGV0LWludGVyYWN0aXZlJyk7XHJcblxyXG5cdFx0dGhpcy5hZGRJbnRlcmFjdGl2ZVRhcmdldCh0aGlzLl9pY29uKTtcclxuXHJcblx0XHRpZiAoTC5IYW5kbGVyLk1hcmtlckRyYWcpIHtcclxuXHRcdFx0dmFyIGRyYWdnYWJsZSA9IHRoaXMub3B0aW9ucy5kcmFnZ2FibGU7XHJcblx0XHRcdGlmICh0aGlzLmRyYWdnaW5nKSB7XHJcblx0XHRcdFx0ZHJhZ2dhYmxlID0gdGhpcy5kcmFnZ2luZy5lbmFibGVkKCk7XHJcblx0XHRcdFx0dGhpcy5kcmFnZ2luZy5kaXNhYmxlKCk7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdHRoaXMuZHJhZ2dpbmcgPSBuZXcgTC5IYW5kbGVyLk1hcmtlckRyYWcodGhpcyk7XHJcblxyXG5cdFx0XHRpZiAoZHJhZ2dhYmxlKSB7XHJcblx0XHRcdFx0dGhpcy5kcmFnZ2luZy5lbmFibGUoKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0T3BhY2l0eShvcGFjaXR5OiBOdW1iZXIpOiB0aGlzXHJcblx0Ly8gQ2hhbmdlcyB0aGUgb3BhY2l0eSBvZiB0aGUgbWFya2VyLlxyXG5cdHNldE9wYWNpdHk6IGZ1bmN0aW9uIChvcGFjaXR5KSB7XHJcblx0XHR0aGlzLm9wdGlvbnMub3BhY2l0eSA9IG9wYWNpdHk7XHJcblx0XHRpZiAodGhpcy5fbWFwKSB7XHJcblx0XHRcdHRoaXMuX3VwZGF0ZU9wYWNpdHkoKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfdXBkYXRlT3BhY2l0eTogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIG9wYWNpdHkgPSB0aGlzLm9wdGlvbnMub3BhY2l0eTtcclxuXHJcblx0XHRMLkRvbVV0aWwuc2V0T3BhY2l0eSh0aGlzLl9pY29uLCBvcGFjaXR5KTtcclxuXHJcblx0XHRpZiAodGhpcy5fc2hhZG93KSB7XHJcblx0XHRcdEwuRG9tVXRpbC5zZXRPcGFjaXR5KHRoaXMuX3NoYWRvdywgb3BhY2l0eSk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2JyaW5nVG9Gcm9udDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dGhpcy5fdXBkYXRlWkluZGV4KHRoaXMub3B0aW9ucy5yaXNlT2Zmc2V0KTtcclxuXHR9LFxyXG5cclxuXHRfcmVzZXRaSW5kZXg6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHRoaXMuX3VwZGF0ZVpJbmRleCgwKTtcclxuXHR9LFxyXG5cclxuXHRfZ2V0UG9wdXBBbmNob3I6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMuaWNvbi5vcHRpb25zLnBvcHVwQW5jaG9yIHx8IFswLCAwXTtcclxuXHR9LFxyXG5cclxuXHRfZ2V0VG9vbHRpcEFuY2hvcjogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5pY29uLm9wdGlvbnMudG9vbHRpcEFuY2hvciB8fCBbMCwgMF07XHJcblx0fVxyXG59KTtcclxuXHJcblxyXG4vLyBmYWN0b3J5IEwubWFya2VyKGxhdGxuZzogTGF0TG5nLCBvcHRpb25zPyA6IE1hcmtlciBvcHRpb25zKVxyXG5cclxuLy8gQGZhY3RvcnkgTC5tYXJrZXIobGF0bG5nOiBMYXRMbmcsIG9wdGlvbnM/IDogTWFya2VyIG9wdGlvbnMpXHJcbi8vIEluc3RhbnRpYXRlcyBhIE1hcmtlciBvYmplY3QgZ2l2ZW4gYSBnZW9ncmFwaGljYWwgcG9pbnQgYW5kIG9wdGlvbmFsbHkgYW4gb3B0aW9ucyBvYmplY3QuXHJcbkwubWFya2VyID0gZnVuY3Rpb24gKGxhdGxuZywgb3B0aW9ucykge1xyXG5cdHJldHVybiBuZXcgTC5NYXJrZXIobGF0bG5nLCBvcHRpb25zKTtcclxufTtcclxuXG5cblxuLypcbiAqIEBjbGFzcyBEaXZJY29uXG4gKiBAYWthIEwuRGl2SWNvblxuICogQGluaGVyaXRzIEljb25cbiAqXG4gKiBSZXByZXNlbnRzIGEgbGlnaHR3ZWlnaHQgaWNvbiBmb3IgbWFya2VycyB0aGF0IHVzZXMgYSBzaW1wbGUgYDxkaXY+YFxuICogZWxlbWVudCBpbnN0ZWFkIG9mIGFuIGltYWdlLiBJbmhlcml0cyBmcm9tIGBJY29uYCBidXQgaWdub3JlcyB0aGUgYGljb25VcmxgIGFuZCBzaGFkb3cgb3B0aW9ucy5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBganNcbiAqIHZhciBteUljb24gPSBMLmRpdkljb24oe2NsYXNzTmFtZTogJ215LWRpdi1pY29uJ30pO1xuICogLy8geW91IGNhbiBzZXQgLm15LWRpdi1pY29uIHN0eWxlcyBpbiBDU1NcbiAqXG4gKiBMLm1hcmtlcihbNTAuNTA1LCAzMC41N10sIHtpY29uOiBteUljb259KS5hZGRUbyhtYXApO1xuICogYGBgXG4gKlxuICogQnkgZGVmYXVsdCwgaXQgaGFzIGEgJ2xlYWZsZXQtZGl2LWljb24nIENTUyBjbGFzcyBhbmQgaXMgc3R5bGVkIGFzIGEgbGl0dGxlIHdoaXRlIHNxdWFyZSB3aXRoIGEgc2hhZG93LlxuICovXG5cbkwuRGl2SWNvbiA9IEwuSWNvbi5leHRlbmQoe1xuXHRvcHRpb25zOiB7XG5cdFx0Ly8gQHNlY3Rpb25cblx0XHQvLyBAYWthIERpdkljb24gb3B0aW9uc1xuXHRcdGljb25TaXplOiBbMTIsIDEyXSwgLy8gYWxzbyBjYW4gYmUgc2V0IHRocm91Z2ggQ1NTXG5cblx0XHQvLyBpY29uQW5jaG9yOiAoUG9pbnQpLFxuXHRcdC8vIHBvcHVwQW5jaG9yOiAoUG9pbnQpLFxuXG5cdFx0Ly8gQG9wdGlvbiBodG1sOiBTdHJpbmcgPSAnJ1xuXHRcdC8vIEN1c3RvbSBIVE1MIGNvZGUgdG8gcHV0IGluc2lkZSB0aGUgZGl2IGVsZW1lbnQsIGVtcHR5IGJ5IGRlZmF1bHQuXG5cdFx0aHRtbDogZmFsc2UsXG5cblx0XHQvLyBAb3B0aW9uIGJnUG9zOiBQb2ludCA9IFswLCAwXVxuXHRcdC8vIE9wdGlvbmFsIHJlbGF0aXZlIHBvc2l0aW9uIG9mIHRoZSBiYWNrZ3JvdW5kLCBpbiBwaXhlbHNcblx0XHRiZ1BvczogbnVsbCxcblxuXHRcdGNsYXNzTmFtZTogJ2xlYWZsZXQtZGl2LWljb24nXG5cdH0sXG5cblx0Y3JlYXRlSWNvbjogZnVuY3Rpb24gKG9sZEljb24pIHtcblx0XHR2YXIgZGl2ID0gKG9sZEljb24gJiYgb2xkSWNvbi50YWdOYW1lID09PSAnRElWJykgPyBvbGRJY29uIDogZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2JyksXG5cdFx0ICAgIG9wdGlvbnMgPSB0aGlzLm9wdGlvbnM7XG5cblx0XHRkaXYuaW5uZXJIVE1MID0gb3B0aW9ucy5odG1sICE9PSBmYWxzZSA/IG9wdGlvbnMuaHRtbCA6ICcnO1xuXG5cdFx0aWYgKG9wdGlvbnMuYmdQb3MpIHtcblx0XHRcdHZhciBiZ1BvcyA9IEwucG9pbnQob3B0aW9ucy5iZ1Bvcyk7XG5cdFx0XHRkaXYuc3R5bGUuYmFja2dyb3VuZFBvc2l0aW9uID0gKC1iZ1Bvcy54KSArICdweCAnICsgKC1iZ1Bvcy55KSArICdweCc7XG5cdFx0fVxuXHRcdHRoaXMuX3NldEljb25TdHlsZXMoZGl2LCAnaWNvbicpO1xuXG5cdFx0cmV0dXJuIGRpdjtcblx0fSxcblxuXHRjcmVhdGVTaGFkb3c6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gbnVsbDtcblx0fVxufSk7XG5cbi8vIEBmYWN0b3J5IEwuZGl2SWNvbihvcHRpb25zOiBEaXZJY29uIG9wdGlvbnMpXG4vLyBDcmVhdGVzIGEgYERpdkljb25gIGluc3RhbmNlIHdpdGggdGhlIGdpdmVuIG9wdGlvbnMuXG5MLmRpdkljb24gPSBmdW5jdGlvbiAob3B0aW9ucykge1xuXHRyZXR1cm4gbmV3IEwuRGl2SWNvbihvcHRpb25zKTtcbn07XG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgRGl2T3ZlcmxheVxyXG4gKiBAaW5oZXJpdHMgTGF5ZXJcclxuICogQGFrYSBMLkRpdk92ZXJsYXlcclxuICogQmFzZSBtb2RlbCBmb3IgTC5Qb3B1cCBhbmQgTC5Ub29sdGlwLiBJbmhlcml0IGZyb20gaXQgZm9yIGN1c3RvbSBwb3B1cCBsaWtlIHBsdWdpbnMuXHJcbiAqL1xyXG5cclxuLy8gQG5hbWVzcGFjZSBEaXZPdmVybGF5XHJcbkwuRGl2T3ZlcmxheSA9IEwuTGF5ZXIuZXh0ZW5kKHtcclxuXHJcblx0Ly8gQHNlY3Rpb25cclxuXHQvLyBAYWthIERpdk92ZXJsYXkgb3B0aW9uc1xyXG5cdG9wdGlvbnM6IHtcclxuXHRcdC8vIEBvcHRpb24gb2Zmc2V0OiBQb2ludCA9IFBvaW50KDAsIDcpXHJcblx0XHQvLyBUaGUgb2Zmc2V0IG9mIHRoZSBwb3B1cCBwb3NpdGlvbi4gVXNlZnVsIHRvIGNvbnRyb2wgdGhlIGFuY2hvclxyXG5cdFx0Ly8gb2YgdGhlIHBvcHVwIHdoZW4gb3BlbmluZyBpdCBvbiBzb21lIG92ZXJsYXlzLlxyXG5cdFx0b2Zmc2V0OiBbMCwgN10sXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBjbGFzc05hbWU6IFN0cmluZyA9ICcnXHJcblx0XHQvLyBBIGN1c3RvbSBDU1MgY2xhc3MgbmFtZSB0byBhc3NpZ24gdG8gdGhlIHBvcHVwLlxyXG5cdFx0Y2xhc3NOYW1lOiAnJyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHBhbmU6IFN0cmluZyA9ICdwb3B1cFBhbmUnXHJcblx0XHQvLyBgTWFwIHBhbmVgIHdoZXJlIHRoZSBwb3B1cCB3aWxsIGJlIGFkZGVkLlxyXG5cdFx0cGFuZTogJ3BvcHVwUGFuZSdcclxuXHR9LFxyXG5cclxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAob3B0aW9ucywgc291cmNlKSB7XHJcblx0XHRMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XHJcblxyXG5cdFx0dGhpcy5fc291cmNlID0gc291cmNlO1xyXG5cdH0sXHJcblxyXG5cdG9uQWRkOiBmdW5jdGlvbiAobWFwKSB7XHJcblx0XHR0aGlzLl96b29tQW5pbWF0ZWQgPSBtYXAuX3pvb21BbmltYXRlZDtcclxuXHJcblx0XHRpZiAoIXRoaXMuX2NvbnRhaW5lcikge1xyXG5cdFx0XHR0aGlzLl9pbml0TGF5b3V0KCk7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKG1hcC5fZmFkZUFuaW1hdGVkKSB7XHJcblx0XHRcdEwuRG9tVXRpbC5zZXRPcGFjaXR5KHRoaXMuX2NvbnRhaW5lciwgMCk7XHJcblx0XHR9XHJcblxyXG5cdFx0Y2xlYXJUaW1lb3V0KHRoaXMuX3JlbW92ZVRpbWVvdXQpO1xyXG5cdFx0dGhpcy5nZXRQYW5lKCkuYXBwZW5kQ2hpbGQodGhpcy5fY29udGFpbmVyKTtcclxuXHRcdHRoaXMudXBkYXRlKCk7XHJcblxyXG5cdFx0aWYgKG1hcC5fZmFkZUFuaW1hdGVkKSB7XHJcblx0XHRcdEwuRG9tVXRpbC5zZXRPcGFjaXR5KHRoaXMuX2NvbnRhaW5lciwgMSk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5icmluZ1RvRnJvbnQoKTtcclxuXHR9LFxyXG5cclxuXHRvblJlbW92ZTogZnVuY3Rpb24gKG1hcCkge1xyXG5cdFx0aWYgKG1hcC5fZmFkZUFuaW1hdGVkKSB7XHJcblx0XHRcdEwuRG9tVXRpbC5zZXRPcGFjaXR5KHRoaXMuX2NvbnRhaW5lciwgMCk7XHJcblx0XHRcdHRoaXMuX3JlbW92ZVRpbWVvdXQgPSBzZXRUaW1lb3V0KEwuYmluZChMLkRvbVV0aWwucmVtb3ZlLCBMLkRvbVV0aWwsIHRoaXMuX2NvbnRhaW5lciksIDIwMCk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRMLkRvbVV0aWwucmVtb3ZlKHRoaXMuX2NvbnRhaW5lcik7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQG5hbWVzcGFjZSBQb3B1cFxyXG5cdC8vIEBtZXRob2QgZ2V0TGF0TG5nOiBMYXRMbmdcclxuXHQvLyBSZXR1cm5zIHRoZSBnZW9ncmFwaGljYWwgcG9pbnQgb2YgcG9wdXAuXHJcblx0Z2V0TGF0TG5nOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fbGF0bG5nO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0TGF0TG5nKGxhdGxuZzogTGF0TG5nKTogdGhpc1xyXG5cdC8vIFNldHMgdGhlIGdlb2dyYXBoaWNhbCBwb2ludCB3aGVyZSB0aGUgcG9wdXAgd2lsbCBvcGVuLlxyXG5cdHNldExhdExuZzogZnVuY3Rpb24gKGxhdGxuZykge1xyXG5cdFx0dGhpcy5fbGF0bG5nID0gTC5sYXRMbmcobGF0bG5nKTtcclxuXHRcdGlmICh0aGlzLl9tYXApIHtcclxuXHRcdFx0dGhpcy5fdXBkYXRlUG9zaXRpb24oKTtcclxuXHRcdFx0dGhpcy5fYWRqdXN0UGFuKCk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldENvbnRlbnQ6IFN0cmluZ3xIVE1MRWxlbWVudFxyXG5cdC8vIFJldHVybnMgdGhlIGNvbnRlbnQgb2YgdGhlIHBvcHVwLlxyXG5cdGdldENvbnRlbnQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9jb250ZW50O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0Q29udGVudChodG1sQ29udGVudDogU3RyaW5nfEhUTUxFbGVtZW50fEZ1bmN0aW9uKTogdGhpc1xyXG5cdC8vIFNldHMgdGhlIEhUTUwgY29udGVudCBvZiB0aGUgcG9wdXAuIElmIGEgZnVuY3Rpb24gaXMgcGFzc2VkIHRoZSBzb3VyY2UgbGF5ZXIgd2lsbCBiZSBwYXNzZWQgdG8gdGhlIGZ1bmN0aW9uLiBUaGUgZnVuY3Rpb24gc2hvdWxkIHJldHVybiBhIGBTdHJpbmdgIG9yIGBIVE1MRWxlbWVudGAgdG8gYmUgdXNlZCBpbiB0aGUgcG9wdXAuXHJcblx0c2V0Q29udGVudDogZnVuY3Rpb24gKGNvbnRlbnQpIHtcclxuXHRcdHRoaXMuX2NvbnRlbnQgPSBjb250ZW50O1xyXG5cdFx0dGhpcy51cGRhdGUoKTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0RWxlbWVudDogU3RyaW5nfEhUTUxFbGVtZW50XHJcblx0Ly8gQWxpYXMgZm9yIFtnZXRDb250ZW50KCldKCNwb3B1cC1nZXRjb250ZW50KVxyXG5cdGdldEVsZW1lbnQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9jb250YWluZXI7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB1cGRhdGU6IG51bGxcclxuXHQvLyBVcGRhdGVzIHRoZSBwb3B1cCBjb250ZW50LCBsYXlvdXQgYW5kIHBvc2l0aW9uLiBVc2VmdWwgZm9yIHVwZGF0aW5nIHRoZSBwb3B1cCBhZnRlciBzb21ldGhpbmcgaW5zaWRlIGNoYW5nZWQsIGUuZy4gaW1hZ2UgbG9hZGVkLlxyXG5cdHVwZGF0ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKCF0aGlzLl9tYXApIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0dGhpcy5fY29udGFpbmVyLnN0eWxlLnZpc2liaWxpdHkgPSAnaGlkZGVuJztcclxuXHJcblx0XHR0aGlzLl91cGRhdGVDb250ZW50KCk7XHJcblx0XHR0aGlzLl91cGRhdGVMYXlvdXQoKTtcclxuXHRcdHRoaXMuX3VwZGF0ZVBvc2l0aW9uKCk7XHJcblxyXG5cdFx0dGhpcy5fY29udGFpbmVyLnN0eWxlLnZpc2liaWxpdHkgPSAnJztcclxuXHJcblx0XHR0aGlzLl9hZGp1c3RQYW4oKTtcclxuXHR9LFxyXG5cclxuXHRnZXRFdmVudHM6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBldmVudHMgPSB7XHJcblx0XHRcdHpvb206IHRoaXMuX3VwZGF0ZVBvc2l0aW9uLFxyXG5cdFx0XHR2aWV3cmVzZXQ6IHRoaXMuX3VwZGF0ZVBvc2l0aW9uXHJcblx0XHR9O1xyXG5cclxuXHRcdGlmICh0aGlzLl96b29tQW5pbWF0ZWQpIHtcclxuXHRcdFx0ZXZlbnRzLnpvb21hbmltID0gdGhpcy5fYW5pbWF0ZVpvb207XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gZXZlbnRzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgaXNPcGVuOiBCb29sZWFuXHJcblx0Ly8gUmV0dXJucyBgdHJ1ZWAgd2hlbiB0aGUgcG9wdXAgaXMgdmlzaWJsZSBvbiB0aGUgbWFwLlxyXG5cdGlzT3BlbjogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuICEhdGhpcy5fbWFwICYmIHRoaXMuX21hcC5oYXNMYXllcih0aGlzKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGJyaW5nVG9Gcm9udDogdGhpc1xyXG5cdC8vIEJyaW5ncyB0aGlzIHBvcHVwIGluIGZyb250IG9mIG90aGVyIHBvcHVwcyAoaW4gdGhlIHNhbWUgbWFwIHBhbmUpLlxyXG5cdGJyaW5nVG9Gcm9udDogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKHRoaXMuX21hcCkge1xyXG5cdFx0XHRMLkRvbVV0aWwudG9Gcm9udCh0aGlzLl9jb250YWluZXIpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBicmluZ1RvQmFjazogdGhpc1xyXG5cdC8vIEJyaW5ncyB0aGlzIHBvcHVwIHRvIHRoZSBiYWNrIG9mIG90aGVyIHBvcHVwcyAoaW4gdGhlIHNhbWUgbWFwIHBhbmUpLlxyXG5cdGJyaW5nVG9CYWNrOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAodGhpcy5fbWFwKSB7XHJcblx0XHRcdEwuRG9tVXRpbC50b0JhY2sodGhpcy5fY29udGFpbmVyKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdF91cGRhdGVDb250ZW50OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAoIXRoaXMuX2NvbnRlbnQpIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0dmFyIG5vZGUgPSB0aGlzLl9jb250ZW50Tm9kZTtcclxuXHRcdHZhciBjb250ZW50ID0gKHR5cGVvZiB0aGlzLl9jb250ZW50ID09PSAnZnVuY3Rpb24nKSA/IHRoaXMuX2NvbnRlbnQodGhpcy5fc291cmNlIHx8IHRoaXMpIDogdGhpcy5fY29udGVudDtcclxuXHJcblx0XHRpZiAodHlwZW9mIGNvbnRlbnQgPT09ICdzdHJpbmcnKSB7XHJcblx0XHRcdG5vZGUuaW5uZXJIVE1MID0gY29udGVudDtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdHdoaWxlIChub2RlLmhhc0NoaWxkTm9kZXMoKSkge1xyXG5cdFx0XHRcdG5vZGUucmVtb3ZlQ2hpbGQobm9kZS5maXJzdENoaWxkKTtcclxuXHRcdFx0fVxyXG5cdFx0XHRub2RlLmFwcGVuZENoaWxkKGNvbnRlbnQpO1xyXG5cdFx0fVxyXG5cdFx0dGhpcy5maXJlKCdjb250ZW50dXBkYXRlJyk7XHJcblx0fSxcclxuXHJcblx0X3VwZGF0ZVBvc2l0aW9uOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAoIXRoaXMuX21hcCkgeyByZXR1cm47IH1cclxuXHJcblx0XHR2YXIgcG9zID0gdGhpcy5fbWFwLmxhdExuZ1RvTGF5ZXJQb2ludCh0aGlzLl9sYXRsbmcpLFxyXG5cdFx0ICAgIG9mZnNldCA9IEwucG9pbnQodGhpcy5vcHRpb25zLm9mZnNldCksXHJcblx0XHQgICAgYW5jaG9yID0gdGhpcy5fZ2V0QW5jaG9yKCk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX3pvb21BbmltYXRlZCkge1xyXG5cdFx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24odGhpcy5fY29udGFpbmVyLCBwb3MuYWRkKGFuY2hvcikpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0b2Zmc2V0ID0gb2Zmc2V0LmFkZChwb3MpLmFkZChhbmNob3IpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBib3R0b20gPSB0aGlzLl9jb250YWluZXJCb3R0b20gPSAtb2Zmc2V0LnksXHJcblx0XHQgICAgbGVmdCA9IHRoaXMuX2NvbnRhaW5lckxlZnQgPSAtTWF0aC5yb3VuZCh0aGlzLl9jb250YWluZXJXaWR0aCAvIDIpICsgb2Zmc2V0Lng7XHJcblxyXG5cdFx0Ly8gYm90dG9tIHBvc2l0aW9uIHRoZSBwb3B1cCBpbiBjYXNlIHRoZSBoZWlnaHQgb2YgdGhlIHBvcHVwIGNoYW5nZXMgKGltYWdlcyBsb2FkaW5nIGV0YylcclxuXHRcdHRoaXMuX2NvbnRhaW5lci5zdHlsZS5ib3R0b20gPSBib3R0b20gKyAncHgnO1xyXG5cdFx0dGhpcy5fY29udGFpbmVyLnN0eWxlLmxlZnQgPSBsZWZ0ICsgJ3B4JztcclxuXHR9LFxyXG5cclxuXHRfZ2V0QW5jaG9yOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gWzAsIDBdO1xyXG5cdH1cclxuXHJcbn0pO1xyXG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgUG9wdXBcclxuICogQGluaGVyaXRzIERpdk92ZXJsYXlcclxuICogQGFrYSBMLlBvcHVwXHJcbiAqIFVzZWQgdG8gb3BlbiBwb3B1cHMgaW4gY2VydGFpbiBwbGFjZXMgb2YgdGhlIG1hcC4gVXNlIFtNYXAub3BlblBvcHVwXSgjbWFwLW9wZW5wb3B1cCkgdG9cclxuICogb3BlbiBwb3B1cHMgd2hpbGUgbWFraW5nIHN1cmUgdGhhdCBvbmx5IG9uZSBwb3B1cCBpcyBvcGVuIGF0IG9uZSB0aW1lXHJcbiAqIChyZWNvbW1lbmRlZCBmb3IgdXNhYmlsaXR5KSwgb3IgdXNlIFtNYXAuYWRkTGF5ZXJdKCNtYXAtYWRkbGF5ZXIpIHRvIG9wZW4gYXMgbWFueSBhcyB5b3Ugd2FudC5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogSWYgeW91IHdhbnQgdG8ganVzdCBiaW5kIGEgcG9wdXAgdG8gbWFya2VyIGNsaWNrIGFuZCB0aGVuIG9wZW4gaXQsIGl0J3MgcmVhbGx5IGVhc3k6XHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIG1hcmtlci5iaW5kUG9wdXAocG9wdXBDb250ZW50KS5vcGVuUG9wdXAoKTtcclxuICogYGBgXHJcbiAqIFBhdGggb3ZlcmxheXMgbGlrZSBwb2x5bGluZXMgYWxzbyBoYXZlIGEgYGJpbmRQb3B1cGAgbWV0aG9kLlxyXG4gKiBIZXJlJ3MgYSBtb3JlIGNvbXBsaWNhdGVkIHdheSB0byBvcGVuIGEgcG9wdXAgb24gYSBtYXA6XHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIHZhciBwb3B1cCA9IEwucG9wdXAoKVxyXG4gKiBcdC5zZXRMYXRMbmcobGF0bG5nKVxyXG4gKiBcdC5zZXRDb250ZW50KCc8cD5IZWxsbyB3b3JsZCE8YnIgLz5UaGlzIGlzIGEgbmljZSBwb3B1cC48L3A+JylcclxuICogXHQub3Blbk9uKG1hcCk7XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcblxyXG4vLyBAbmFtZXNwYWNlIFBvcHVwXHJcbkwuUG9wdXAgPSBMLkRpdk92ZXJsYXkuZXh0ZW5kKHtcclxuXHJcblx0Ly8gQHNlY3Rpb25cclxuXHQvLyBAYWthIFBvcHVwIG9wdGlvbnNcclxuXHRvcHRpb25zOiB7XHJcblx0XHQvLyBAb3B0aW9uIG1heFdpZHRoOiBOdW1iZXIgPSAzMDBcclxuXHRcdC8vIE1heCB3aWR0aCBvZiB0aGUgcG9wdXAsIGluIHBpeGVscy5cclxuXHRcdG1heFdpZHRoOiAzMDAsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBtaW5XaWR0aDogTnVtYmVyID0gNTBcclxuXHRcdC8vIE1pbiB3aWR0aCBvZiB0aGUgcG9wdXAsIGluIHBpeGVscy5cclxuXHRcdG1pbldpZHRoOiA1MCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIG1heEhlaWdodDogTnVtYmVyID0gbnVsbFxyXG5cdFx0Ly8gSWYgc2V0LCBjcmVhdGVzIGEgc2Nyb2xsYWJsZSBjb250YWluZXIgb2YgdGhlIGdpdmVuIGhlaWdodFxyXG5cdFx0Ly8gaW5zaWRlIGEgcG9wdXAgaWYgaXRzIGNvbnRlbnQgZXhjZWVkcyBpdC5cclxuXHRcdG1heEhlaWdodDogbnVsbCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGF1dG9QYW46IEJvb2xlYW4gPSB0cnVlXHJcblx0XHQvLyBTZXQgaXQgdG8gYGZhbHNlYCBpZiB5b3UgZG9uJ3Qgd2FudCB0aGUgbWFwIHRvIGRvIHBhbm5pbmcgYW5pbWF0aW9uXHJcblx0XHQvLyB0byBmaXQgdGhlIG9wZW5lZCBwb3B1cC5cclxuXHRcdGF1dG9QYW46IHRydWUsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBhdXRvUGFuUGFkZGluZ1RvcExlZnQ6IFBvaW50ID0gbnVsbFxyXG5cdFx0Ly8gVGhlIG1hcmdpbiBiZXR3ZWVuIHRoZSBwb3B1cCBhbmQgdGhlIHRvcCBsZWZ0IGNvcm5lciBvZiB0aGUgbWFwXHJcblx0XHQvLyB2aWV3IGFmdGVyIGF1dG9wYW5uaW5nIHdhcyBwZXJmb3JtZWQuXHJcblx0XHRhdXRvUGFuUGFkZGluZ1RvcExlZnQ6IG51bGwsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBhdXRvUGFuUGFkZGluZ0JvdHRvbVJpZ2h0OiBQb2ludCA9IG51bGxcclxuXHRcdC8vIFRoZSBtYXJnaW4gYmV0d2VlbiB0aGUgcG9wdXAgYW5kIHRoZSBib3R0b20gcmlnaHQgY29ybmVyIG9mIHRoZSBtYXBcclxuXHRcdC8vIHZpZXcgYWZ0ZXIgYXV0b3Bhbm5pbmcgd2FzIHBlcmZvcm1lZC5cclxuXHRcdGF1dG9QYW5QYWRkaW5nQm90dG9tUmlnaHQ6IG51bGwsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBhdXRvUGFuUGFkZGluZzogUG9pbnQgPSBQb2ludCg1LCA1KVxyXG5cdFx0Ly8gRXF1aXZhbGVudCBvZiBzZXR0aW5nIGJvdGggdG9wIGxlZnQgYW5kIGJvdHRvbSByaWdodCBhdXRvcGFuIHBhZGRpbmcgdG8gdGhlIHNhbWUgdmFsdWUuXHJcblx0XHRhdXRvUGFuUGFkZGluZzogWzUsIDVdLFxyXG5cclxuXHRcdC8vIEBvcHRpb24ga2VlcEluVmlldzogQm9vbGVhbiA9IGZhbHNlXHJcblx0XHQvLyBTZXQgaXQgdG8gYHRydWVgIGlmIHlvdSB3YW50IHRvIHByZXZlbnQgdXNlcnMgZnJvbSBwYW5uaW5nIHRoZSBwb3B1cFxyXG5cdFx0Ly8gb2ZmIG9mIHRoZSBzY3JlZW4gd2hpbGUgaXQgaXMgb3Blbi5cclxuXHRcdGtlZXBJblZpZXc6IGZhbHNlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gY2xvc2VCdXR0b246IEJvb2xlYW4gPSB0cnVlXHJcblx0XHQvLyBDb250cm9scyB0aGUgcHJlc2VuY2Ugb2YgYSBjbG9zZSBidXR0b24gaW4gdGhlIHBvcHVwLlxyXG5cdFx0Y2xvc2VCdXR0b246IHRydWUsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBhdXRvQ2xvc2U6IEJvb2xlYW4gPSB0cnVlXHJcblx0XHQvLyBTZXQgaXQgdG8gYGZhbHNlYCBpZiB5b3Ugd2FudCB0byBvdmVycmlkZSB0aGUgZGVmYXVsdCBiZWhhdmlvciBvZlxyXG5cdFx0Ly8gdGhlIHBvcHVwIGNsb3Npbmcgd2hlbiB1c2VyIGNsaWNrcyB0aGUgbWFwIChzZXQgZ2xvYmFsbHkgYnlcclxuXHRcdC8vIHRoZSBNYXAncyBbY2xvc2VQb3B1cE9uQ2xpY2tdKCNtYXAtY2xvc2Vwb3B1cG9uY2xpY2spIG9wdGlvbikuXHJcblx0XHRhdXRvQ2xvc2U6IHRydWUsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBjbGFzc05hbWU6IFN0cmluZyA9ICcnXHJcblx0XHQvLyBBIGN1c3RvbSBDU1MgY2xhc3MgbmFtZSB0byBhc3NpZ24gdG8gdGhlIHBvcHVwLlxyXG5cdFx0Y2xhc3NOYW1lOiAnJ1xyXG5cdH0sXHJcblxyXG5cdC8vIEBuYW1lc3BhY2UgUG9wdXBcclxuXHQvLyBAbWV0aG9kIG9wZW5PbihtYXA6IE1hcCk6IHRoaXNcclxuXHQvLyBBZGRzIHRoZSBwb3B1cCB0byB0aGUgbWFwIGFuZCBjbG9zZXMgdGhlIHByZXZpb3VzIG9uZS4gVGhlIHNhbWUgYXMgYG1hcC5vcGVuUG9wdXAocG9wdXApYC5cclxuXHRvcGVuT246IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdG1hcC5vcGVuUG9wdXAodGhpcyk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRvbkFkZDogZnVuY3Rpb24gKG1hcCkge1xyXG5cdFx0TC5EaXZPdmVybGF5LnByb3RvdHlwZS5vbkFkZC5jYWxsKHRoaXMsIG1hcCk7XHJcblxyXG5cdFx0Ly8gQG5hbWVzcGFjZSBNYXBcclxuXHRcdC8vIEBzZWN0aW9uIFBvcHVwIGV2ZW50c1xyXG5cdFx0Ly8gQGV2ZW50IHBvcHVwb3BlbjogUG9wdXBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiBhIHBvcHVwIGlzIG9wZW5lZCBpbiB0aGUgbWFwXHJcblx0XHRtYXAuZmlyZSgncG9wdXBvcGVuJywge3BvcHVwOiB0aGlzfSk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX3NvdXJjZSkge1xyXG5cdFx0XHQvLyBAbmFtZXNwYWNlIExheWVyXHJcblx0XHRcdC8vIEBzZWN0aW9uIFBvcHVwIGV2ZW50c1xyXG5cdFx0XHQvLyBAZXZlbnQgcG9wdXBvcGVuOiBQb3B1cEV2ZW50XHJcblx0XHRcdC8vIEZpcmVkIHdoZW4gYSBwb3B1cCBib3VuZCB0byB0aGlzIGxheWVyIGlzIG9wZW5lZFxyXG5cdFx0XHR0aGlzLl9zb3VyY2UuZmlyZSgncG9wdXBvcGVuJywge3BvcHVwOiB0aGlzfSwgdHJ1ZSk7XHJcblx0XHRcdC8vIEZvciBub24tcGF0aCBsYXllcnMsIHdlIHRvZ2dsZSB0aGUgcG9wdXAgd2hlbiBjbGlja2luZ1xyXG5cdFx0XHQvLyBhZ2FpbiB0aGUgbGF5ZXIsIHNvIHByZXZlbnQgdGhlIG1hcCB0byByZW9wZW4gaXQuXHJcblx0XHRcdGlmICghKHRoaXMuX3NvdXJjZSBpbnN0YW5jZW9mIEwuUGF0aCkpIHtcclxuXHRcdFx0XHR0aGlzLl9zb3VyY2Uub24oJ3ByZWNsaWNrJywgTC5Eb21FdmVudC5zdG9wUHJvcGFnYXRpb24pO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0b25SZW1vdmU6IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdEwuRGl2T3ZlcmxheS5wcm90b3R5cGUub25SZW1vdmUuY2FsbCh0aGlzLCBtYXApO1xyXG5cclxuXHRcdC8vIEBuYW1lc3BhY2UgTWFwXHJcblx0XHQvLyBAc2VjdGlvbiBQb3B1cCBldmVudHNcclxuXHRcdC8vIEBldmVudCBwb3B1cGNsb3NlOiBQb3B1cEV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIGEgcG9wdXAgaW4gdGhlIG1hcCBpcyBjbG9zZWRcclxuXHRcdG1hcC5maXJlKCdwb3B1cGNsb3NlJywge3BvcHVwOiB0aGlzfSk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX3NvdXJjZSkge1xyXG5cdFx0XHQvLyBAbmFtZXNwYWNlIExheWVyXHJcblx0XHRcdC8vIEBzZWN0aW9uIFBvcHVwIGV2ZW50c1xyXG5cdFx0XHQvLyBAZXZlbnQgcG9wdXBjbG9zZTogUG9wdXBFdmVudFxyXG5cdFx0XHQvLyBGaXJlZCB3aGVuIGEgcG9wdXAgYm91bmQgdG8gdGhpcyBsYXllciBpcyBjbG9zZWRcclxuXHRcdFx0dGhpcy5fc291cmNlLmZpcmUoJ3BvcHVwY2xvc2UnLCB7cG9wdXA6IHRoaXN9LCB0cnVlKTtcclxuXHRcdFx0aWYgKCEodGhpcy5fc291cmNlIGluc3RhbmNlb2YgTC5QYXRoKSkge1xyXG5cdFx0XHRcdHRoaXMuX3NvdXJjZS5vZmYoJ3ByZWNsaWNrJywgTC5Eb21FdmVudC5zdG9wUHJvcGFnYXRpb24pO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Z2V0RXZlbnRzOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgZXZlbnRzID0gTC5EaXZPdmVybGF5LnByb3RvdHlwZS5nZXRFdmVudHMuY2FsbCh0aGlzKTtcclxuXHJcblx0XHRpZiAoJ2Nsb3NlT25DbGljaycgaW4gdGhpcy5vcHRpb25zID8gdGhpcy5vcHRpb25zLmNsb3NlT25DbGljayA6IHRoaXMuX21hcC5vcHRpb25zLmNsb3NlUG9wdXBPbkNsaWNrKSB7XHJcblx0XHRcdGV2ZW50cy5wcmVjbGljayA9IHRoaXMuX2Nsb3NlO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICh0aGlzLm9wdGlvbnMua2VlcEluVmlldykge1xyXG5cdFx0XHRldmVudHMubW92ZWVuZCA9IHRoaXMuX2FkanVzdFBhbjtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gZXZlbnRzO1xyXG5cdH0sXHJcblxyXG5cdF9jbG9zZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKHRoaXMuX21hcCkge1xyXG5cdFx0XHR0aGlzLl9tYXAuY2xvc2VQb3B1cCh0aGlzKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfaW5pdExheW91dDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIHByZWZpeCA9ICdsZWFmbGV0LXBvcHVwJyxcclxuXHRcdCAgICBjb250YWluZXIgPSB0aGlzLl9jb250YWluZXIgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLFxyXG5cdFx0XHRwcmVmaXggKyAnICcgKyAodGhpcy5vcHRpb25zLmNsYXNzTmFtZSB8fCAnJykgK1xyXG5cdFx0XHQnIGxlYWZsZXQtem9vbS1hbmltYXRlZCcpO1xyXG5cclxuXHRcdGlmICh0aGlzLm9wdGlvbnMuY2xvc2VCdXR0b24pIHtcclxuXHRcdFx0dmFyIGNsb3NlQnV0dG9uID0gdGhpcy5fY2xvc2VCdXR0b24gPSBMLkRvbVV0aWwuY3JlYXRlKCdhJywgcHJlZml4ICsgJy1jbG9zZS1idXR0b24nLCBjb250YWluZXIpO1xyXG5cdFx0XHRjbG9zZUJ1dHRvbi5ocmVmID0gJyNjbG9zZSc7XHJcblx0XHRcdGNsb3NlQnV0dG9uLmlubmVySFRNTCA9ICcmIzIxNTsnO1xyXG5cclxuXHRcdFx0TC5Eb21FdmVudC5vbihjbG9zZUJ1dHRvbiwgJ2NsaWNrJywgdGhpcy5fb25DbG9zZUJ1dHRvbkNsaWNrLCB0aGlzKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgd3JhcHBlciA9IHRoaXMuX3dyYXBwZXIgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCBwcmVmaXggKyAnLWNvbnRlbnQtd3JhcHBlcicsIGNvbnRhaW5lcik7XHJcblx0XHR0aGlzLl9jb250ZW50Tm9kZSA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsIHByZWZpeCArICctY29udGVudCcsIHdyYXBwZXIpO1xyXG5cclxuXHRcdEwuRG9tRXZlbnRcclxuXHRcdFx0LmRpc2FibGVDbGlja1Byb3BhZ2F0aW9uKHdyYXBwZXIpXHJcblx0XHRcdC5kaXNhYmxlU2Nyb2xsUHJvcGFnYXRpb24odGhpcy5fY29udGVudE5vZGUpXHJcblx0XHRcdC5vbih3cmFwcGVyLCAnY29udGV4dG1lbnUnLCBMLkRvbUV2ZW50LnN0b3BQcm9wYWdhdGlvbik7XHJcblxyXG5cdFx0dGhpcy5fdGlwQ29udGFpbmVyID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgcHJlZml4ICsgJy10aXAtY29udGFpbmVyJywgY29udGFpbmVyKTtcclxuXHRcdHRoaXMuX3RpcCA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsIHByZWZpeCArICctdGlwJywgdGhpcy5fdGlwQ29udGFpbmVyKTtcclxuXHR9LFxyXG5cclxuXHRfdXBkYXRlTGF5b3V0OiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgY29udGFpbmVyID0gdGhpcy5fY29udGVudE5vZGUsXHJcblx0XHQgICAgc3R5bGUgPSBjb250YWluZXIuc3R5bGU7XHJcblxyXG5cdFx0c3R5bGUud2lkdGggPSAnJztcclxuXHRcdHN0eWxlLndoaXRlU3BhY2UgPSAnbm93cmFwJztcclxuXHJcblx0XHR2YXIgd2lkdGggPSBjb250YWluZXIub2Zmc2V0V2lkdGg7XHJcblx0XHR3aWR0aCA9IE1hdGgubWluKHdpZHRoLCB0aGlzLm9wdGlvbnMubWF4V2lkdGgpO1xyXG5cdFx0d2lkdGggPSBNYXRoLm1heCh3aWR0aCwgdGhpcy5vcHRpb25zLm1pbldpZHRoKTtcclxuXHJcblx0XHRzdHlsZS53aWR0aCA9ICh3aWR0aCArIDEpICsgJ3B4JztcclxuXHRcdHN0eWxlLndoaXRlU3BhY2UgPSAnJztcclxuXHJcblx0XHRzdHlsZS5oZWlnaHQgPSAnJztcclxuXHJcblx0XHR2YXIgaGVpZ2h0ID0gY29udGFpbmVyLm9mZnNldEhlaWdodCxcclxuXHRcdCAgICBtYXhIZWlnaHQgPSB0aGlzLm9wdGlvbnMubWF4SGVpZ2h0LFxyXG5cdFx0ICAgIHNjcm9sbGVkQ2xhc3MgPSAnbGVhZmxldC1wb3B1cC1zY3JvbGxlZCc7XHJcblxyXG5cdFx0aWYgKG1heEhlaWdodCAmJiBoZWlnaHQgPiBtYXhIZWlnaHQpIHtcclxuXHRcdFx0c3R5bGUuaGVpZ2h0ID0gbWF4SGVpZ2h0ICsgJ3B4JztcclxuXHRcdFx0TC5Eb21VdGlsLmFkZENsYXNzKGNvbnRhaW5lciwgc2Nyb2xsZWRDbGFzcyk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRMLkRvbVV0aWwucmVtb3ZlQ2xhc3MoY29udGFpbmVyLCBzY3JvbGxlZENsYXNzKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9jb250YWluZXJXaWR0aCA9IHRoaXMuX2NvbnRhaW5lci5vZmZzZXRXaWR0aDtcclxuXHR9LFxyXG5cclxuXHRfYW5pbWF0ZVpvb206IGZ1bmN0aW9uIChlKSB7XHJcblx0XHR2YXIgcG9zID0gdGhpcy5fbWFwLl9sYXRMbmdUb05ld0xheWVyUG9pbnQodGhpcy5fbGF0bG5nLCBlLnpvb20sIGUuY2VudGVyKSxcclxuXHRcdCAgICBhbmNob3IgPSB0aGlzLl9nZXRBbmNob3IoKTtcclxuXHRcdEwuRG9tVXRpbC5zZXRQb3NpdGlvbih0aGlzLl9jb250YWluZXIsIHBvcy5hZGQoYW5jaG9yKSk7XHJcblx0fSxcclxuXHJcblx0X2FkanVzdFBhbjogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKCF0aGlzLm9wdGlvbnMuYXV0b1BhbiB8fCAodGhpcy5fbWFwLl9wYW5BbmltICYmIHRoaXMuX21hcC5fcGFuQW5pbS5faW5Qcm9ncmVzcykpIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcCxcclxuXHRcdCAgICBtYXJnaW5Cb3R0b20gPSBwYXJzZUludChMLkRvbVV0aWwuZ2V0U3R5bGUodGhpcy5fY29udGFpbmVyLCAnbWFyZ2luQm90dG9tJyksIDEwKSB8fCAwLFxyXG5cdFx0ICAgIGNvbnRhaW5lckhlaWdodCA9IHRoaXMuX2NvbnRhaW5lci5vZmZzZXRIZWlnaHQgKyBtYXJnaW5Cb3R0b20sXHJcblx0XHQgICAgY29udGFpbmVyV2lkdGggPSB0aGlzLl9jb250YWluZXJXaWR0aCxcclxuXHRcdCAgICBsYXllclBvcyA9IG5ldyBMLlBvaW50KHRoaXMuX2NvbnRhaW5lckxlZnQsIC1jb250YWluZXJIZWlnaHQgLSB0aGlzLl9jb250YWluZXJCb3R0b20pO1xyXG5cclxuXHRcdGxheWVyUG9zLl9hZGQoTC5Eb21VdGlsLmdldFBvc2l0aW9uKHRoaXMuX2NvbnRhaW5lcikpO1xyXG5cclxuXHRcdHZhciBjb250YWluZXJQb3MgPSBtYXAubGF5ZXJQb2ludFRvQ29udGFpbmVyUG9pbnQobGF5ZXJQb3MpLFxyXG5cdFx0ICAgIHBhZGRpbmcgPSBMLnBvaW50KHRoaXMub3B0aW9ucy5hdXRvUGFuUGFkZGluZyksXHJcblx0XHQgICAgcGFkZGluZ1RMID0gTC5wb2ludCh0aGlzLm9wdGlvbnMuYXV0b1BhblBhZGRpbmdUb3BMZWZ0IHx8IHBhZGRpbmcpLFxyXG5cdFx0ICAgIHBhZGRpbmdCUiA9IEwucG9pbnQodGhpcy5vcHRpb25zLmF1dG9QYW5QYWRkaW5nQm90dG9tUmlnaHQgfHwgcGFkZGluZyksXHJcblx0XHQgICAgc2l6ZSA9IG1hcC5nZXRTaXplKCksXHJcblx0XHQgICAgZHggPSAwLFxyXG5cdFx0ICAgIGR5ID0gMDtcclxuXHJcblx0XHRpZiAoY29udGFpbmVyUG9zLnggKyBjb250YWluZXJXaWR0aCArIHBhZGRpbmdCUi54ID4gc2l6ZS54KSB7IC8vIHJpZ2h0XHJcblx0XHRcdGR4ID0gY29udGFpbmVyUG9zLnggKyBjb250YWluZXJXaWR0aCAtIHNpemUueCArIHBhZGRpbmdCUi54O1xyXG5cdFx0fVxyXG5cdFx0aWYgKGNvbnRhaW5lclBvcy54IC0gZHggLSBwYWRkaW5nVEwueCA8IDApIHsgLy8gbGVmdFxyXG5cdFx0XHRkeCA9IGNvbnRhaW5lclBvcy54IC0gcGFkZGluZ1RMLng7XHJcblx0XHR9XHJcblx0XHRpZiAoY29udGFpbmVyUG9zLnkgKyBjb250YWluZXJIZWlnaHQgKyBwYWRkaW5nQlIueSA+IHNpemUueSkgeyAvLyBib3R0b21cclxuXHRcdFx0ZHkgPSBjb250YWluZXJQb3MueSArIGNvbnRhaW5lckhlaWdodCAtIHNpemUueSArIHBhZGRpbmdCUi55O1xyXG5cdFx0fVxyXG5cdFx0aWYgKGNvbnRhaW5lclBvcy55IC0gZHkgLSBwYWRkaW5nVEwueSA8IDApIHsgLy8gdG9wXHJcblx0XHRcdGR5ID0gY29udGFpbmVyUG9zLnkgLSBwYWRkaW5nVEwueTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBAbmFtZXNwYWNlIE1hcFxyXG5cdFx0Ly8gQHNlY3Rpb24gUG9wdXAgZXZlbnRzXHJcblx0XHQvLyBAZXZlbnQgYXV0b3BhbnN0YXJ0OiBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgbWFwIHN0YXJ0cyBhdXRvcGFubmluZyB3aGVuIG9wZW5pbmcgYSBwb3B1cC5cclxuXHRcdGlmIChkeCB8fCBkeSkge1xyXG5cdFx0XHRtYXBcclxuXHRcdFx0ICAgIC5maXJlKCdhdXRvcGFuc3RhcnQnKVxyXG5cdFx0XHQgICAgLnBhbkJ5KFtkeCwgZHldKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfb25DbG9zZUJ1dHRvbkNsaWNrOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0dGhpcy5fY2xvc2UoKTtcclxuXHRcdEwuRG9tRXZlbnQuc3RvcChlKTtcclxuXHR9LFxyXG5cclxuXHRfZ2V0QW5jaG9yOiBmdW5jdGlvbiAoKSB7XHJcblx0XHQvLyBXaGVyZSBzaG91bGQgd2UgYW5jaG9yIHRoZSBwb3B1cCBvbiB0aGUgc291cmNlIGxheWVyP1xyXG5cdFx0cmV0dXJuIEwucG9pbnQodGhpcy5fc291cmNlICYmIHRoaXMuX3NvdXJjZS5fZ2V0UG9wdXBBbmNob3IgPyB0aGlzLl9zb3VyY2UuX2dldFBvcHVwQW5jaG9yKCkgOiBbMCwgMF0pO1xyXG5cdH1cclxuXHJcbn0pO1xyXG5cclxuLy8gQG5hbWVzcGFjZSBQb3B1cFxyXG4vLyBAZmFjdG9yeSBMLnBvcHVwKG9wdGlvbnM/OiBQb3B1cCBvcHRpb25zLCBzb3VyY2U/OiBMYXllcilcclxuLy8gSW5zdGFudGlhdGVzIGEgYFBvcHVwYCBvYmplY3QgZ2l2ZW4gYW4gb3B0aW9uYWwgYG9wdGlvbnNgIG9iamVjdCB0aGF0IGRlc2NyaWJlcyBpdHMgYXBwZWFyYW5jZSBhbmQgbG9jYXRpb24gYW5kIGFuIG9wdGlvbmFsIGBzb3VyY2VgIG9iamVjdCB0aGF0IGlzIHVzZWQgdG8gdGFnIHRoZSBwb3B1cCB3aXRoIGEgcmVmZXJlbmNlIHRvIHRoZSBMYXllciB0byB3aGljaCBpdCByZWZlcnMuXHJcbkwucG9wdXAgPSBmdW5jdGlvbiAob3B0aW9ucywgc291cmNlKSB7XHJcblx0cmV0dXJuIG5ldyBMLlBvcHVwKG9wdGlvbnMsIHNvdXJjZSk7XHJcbn07XHJcblxyXG5cclxuLyogQG5hbWVzcGFjZSBNYXBcclxuICogQHNlY3Rpb24gSW50ZXJhY3Rpb24gT3B0aW9uc1xyXG4gKiBAb3B0aW9uIGNsb3NlUG9wdXBPbkNsaWNrOiBCb29sZWFuID0gdHJ1ZVxyXG4gKiBTZXQgaXQgdG8gYGZhbHNlYCBpZiB5b3UgZG9uJ3Qgd2FudCBwb3B1cHMgdG8gY2xvc2Ugd2hlbiB1c2VyIGNsaWNrcyB0aGUgbWFwLlxyXG4gKi9cclxuTC5NYXAubWVyZ2VPcHRpb25zKHtcclxuXHRjbG9zZVBvcHVwT25DbGljazogdHJ1ZVxyXG59KTtcclxuXHJcblxyXG4vLyBAbmFtZXNwYWNlIE1hcFxyXG4vLyBAc2VjdGlvbiBNZXRob2RzIGZvciBMYXllcnMgYW5kIENvbnRyb2xzXHJcbkwuTWFwLmluY2x1ZGUoe1xyXG5cdC8vIEBtZXRob2Qgb3BlblBvcHVwKHBvcHVwOiBQb3B1cCk6IHRoaXNcclxuXHQvLyBPcGVucyB0aGUgc3BlY2lmaWVkIHBvcHVwIHdoaWxlIGNsb3NpbmcgdGhlIHByZXZpb3VzbHkgb3BlbmVkICh0byBtYWtlIHN1cmUgb25seSBvbmUgaXMgb3BlbmVkIGF0IG9uZSB0aW1lIGZvciB1c2FiaWxpdHkpLlxyXG5cdC8vIEBhbHRlcm5hdGl2ZVxyXG5cdC8vIEBtZXRob2Qgb3BlblBvcHVwKGNvbnRlbnQ6IFN0cmluZ3xIVE1MRWxlbWVudCwgbGF0bG5nOiBMYXRMbmcsIG9wdGlvbnM/OiBQb3B1cCBvcHRpb25zKTogdGhpc1xyXG5cdC8vIENyZWF0ZXMgYSBwb3B1cCB3aXRoIHRoZSBzcGVjaWZpZWQgY29udGVudCBhbmQgb3B0aW9ucyBhbmQgb3BlbnMgaXQgaW4gdGhlIGdpdmVuIHBvaW50IG9uIGEgbWFwLlxyXG5cdG9wZW5Qb3B1cDogZnVuY3Rpb24gKHBvcHVwLCBsYXRsbmcsIG9wdGlvbnMpIHtcclxuXHRcdGlmICghKHBvcHVwIGluc3RhbmNlb2YgTC5Qb3B1cCkpIHtcclxuXHRcdFx0cG9wdXAgPSBuZXcgTC5Qb3B1cChvcHRpb25zKS5zZXRDb250ZW50KHBvcHVwKTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAobGF0bG5nKSB7XHJcblx0XHRcdHBvcHVwLnNldExhdExuZyhsYXRsbmcpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICh0aGlzLmhhc0xheWVyKHBvcHVwKSkge1xyXG5cdFx0XHRyZXR1cm4gdGhpcztcclxuXHRcdH1cclxuXHJcblx0XHRpZiAodGhpcy5fcG9wdXAgJiYgdGhpcy5fcG9wdXAub3B0aW9ucy5hdXRvQ2xvc2UpIHtcclxuXHRcdFx0dGhpcy5jbG9zZVBvcHVwKCk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fcG9wdXAgPSBwb3B1cDtcclxuXHRcdHJldHVybiB0aGlzLmFkZExheWVyKHBvcHVwKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGNsb3NlUG9wdXAocG9wdXA/OiBQb3B1cCk6IHRoaXNcclxuXHQvLyBDbG9zZXMgdGhlIHBvcHVwIHByZXZpb3VzbHkgb3BlbmVkIHdpdGggW29wZW5Qb3B1cF0oI21hcC1vcGVucG9wdXApIChvciB0aGUgZ2l2ZW4gb25lKS5cclxuXHRjbG9zZVBvcHVwOiBmdW5jdGlvbiAocG9wdXApIHtcclxuXHRcdGlmICghcG9wdXAgfHwgcG9wdXAgPT09IHRoaXMuX3BvcHVwKSB7XHJcblx0XHRcdHBvcHVwID0gdGhpcy5fcG9wdXA7XHJcblx0XHRcdHRoaXMuX3BvcHVwID0gbnVsbDtcclxuXHRcdH1cclxuXHRcdGlmIChwb3B1cCkge1xyXG5cdFx0XHR0aGlzLnJlbW92ZUxheWVyKHBvcHVwKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH1cclxufSk7XHJcblxyXG4vKlxyXG4gKiBAbmFtZXNwYWNlIExheWVyXHJcbiAqIEBzZWN0aW9uIFBvcHVwIG1ldGhvZHMgZXhhbXBsZVxyXG4gKlxyXG4gKiBBbGwgbGF5ZXJzIHNoYXJlIGEgc2V0IG9mIG1ldGhvZHMgY29udmVuaWVudCBmb3IgYmluZGluZyBwb3B1cHMgdG8gaXQuXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIHZhciBsYXllciA9IEwuUG9seWdvbihsYXRsbmdzKS5iaW5kUG9wdXAoJ0hpIFRoZXJlIScpLmFkZFRvKG1hcCk7XHJcbiAqIGxheWVyLm9wZW5Qb3B1cCgpO1xyXG4gKiBsYXllci5jbG9zZVBvcHVwKCk7XHJcbiAqIGBgYFxyXG4gKlxyXG4gKiBQb3B1cHMgd2lsbCBhbHNvIGJlIGF1dG9tYXRpY2FsbHkgb3BlbmVkIHdoZW4gdGhlIGxheWVyIGlzIGNsaWNrZWQgb24gYW5kIGNsb3NlZCB3aGVuIHRoZSBsYXllciBpcyByZW1vdmVkIGZyb20gdGhlIG1hcCBvciBhbm90aGVyIHBvcHVwIGlzIG9wZW5lZC5cclxuICovXHJcblxyXG4vLyBAc2VjdGlvbiBQb3B1cCBtZXRob2RzXHJcbkwuTGF5ZXIuaW5jbHVkZSh7XHJcblxyXG5cdC8vIEBtZXRob2QgYmluZFBvcHVwKGNvbnRlbnQ6IFN0cmluZ3xIVE1MRWxlbWVudHxGdW5jdGlvbnxQb3B1cCwgb3B0aW9ucz86IFBvcHVwIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gQmluZHMgYSBwb3B1cCB0byB0aGUgbGF5ZXIgd2l0aCB0aGUgcGFzc2VkIGBjb250ZW50YCBhbmQgc2V0cyB1cCB0aGVcclxuXHQvLyBuZWNjZXNzYXJ5IGV2ZW50IGxpc3RlbmVycy4gSWYgYSBgRnVuY3Rpb25gIGlzIHBhc3NlZCBpdCB3aWxsIHJlY2VpdmVcclxuXHQvLyB0aGUgbGF5ZXIgYXMgdGhlIGZpcnN0IGFyZ3VtZW50IGFuZCBzaG91bGQgcmV0dXJuIGEgYFN0cmluZ2Agb3IgYEhUTUxFbGVtZW50YC5cclxuXHRiaW5kUG9wdXA6IGZ1bmN0aW9uIChjb250ZW50LCBvcHRpb25zKSB7XHJcblxyXG5cdFx0aWYgKGNvbnRlbnQgaW5zdGFuY2VvZiBMLlBvcHVwKSB7XHJcblx0XHRcdEwuc2V0T3B0aW9ucyhjb250ZW50LCBvcHRpb25zKTtcclxuXHRcdFx0dGhpcy5fcG9wdXAgPSBjb250ZW50O1xyXG5cdFx0XHRjb250ZW50Ll9zb3VyY2UgPSB0aGlzO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0aWYgKCF0aGlzLl9wb3B1cCB8fCBvcHRpb25zKSB7XHJcblx0XHRcdFx0dGhpcy5fcG9wdXAgPSBuZXcgTC5Qb3B1cChvcHRpb25zLCB0aGlzKTtcclxuXHRcdFx0fVxyXG5cdFx0XHR0aGlzLl9wb3B1cC5zZXRDb250ZW50KGNvbnRlbnQpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICghdGhpcy5fcG9wdXBIYW5kbGVyc0FkZGVkKSB7XHJcblx0XHRcdHRoaXMub24oe1xyXG5cdFx0XHRcdGNsaWNrOiB0aGlzLl9vcGVuUG9wdXAsXHJcblx0XHRcdFx0cmVtb3ZlOiB0aGlzLmNsb3NlUG9wdXAsXHJcblx0XHRcdFx0bW92ZTogdGhpcy5fbW92ZVBvcHVwXHJcblx0XHRcdH0pO1xyXG5cdFx0XHR0aGlzLl9wb3B1cEhhbmRsZXJzQWRkZWQgPSB0cnVlO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgdW5iaW5kUG9wdXAoKTogdGhpc1xyXG5cdC8vIFJlbW92ZXMgdGhlIHBvcHVwIHByZXZpb3VzbHkgYm91bmQgd2l0aCBgYmluZFBvcHVwYC5cclxuXHR1bmJpbmRQb3B1cDogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKHRoaXMuX3BvcHVwKSB7XHJcblx0XHRcdHRoaXMub2ZmKHtcclxuXHRcdFx0XHRjbGljazogdGhpcy5fb3BlblBvcHVwLFxyXG5cdFx0XHRcdHJlbW92ZTogdGhpcy5jbG9zZVBvcHVwLFxyXG5cdFx0XHRcdG1vdmU6IHRoaXMuX21vdmVQb3B1cFxyXG5cdFx0XHR9KTtcclxuXHRcdFx0dGhpcy5fcG9wdXBIYW5kbGVyc0FkZGVkID0gZmFsc2U7XHJcblx0XHRcdHRoaXMuX3BvcHVwID0gbnVsbDtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgb3BlblBvcHVwKGxhdGxuZz86IExhdExuZyk6IHRoaXNcclxuXHQvLyBPcGVucyB0aGUgYm91bmQgcG9wdXAgYXQgdGhlIHNwZWNpZmljZWQgYGxhdGxuZ2Agb3IgYXQgdGhlIGRlZmF1bHQgcG9wdXAgYW5jaG9yIGlmIG5vIGBsYXRsbmdgIGlzIHBhc3NlZC5cclxuXHRvcGVuUG9wdXA6IGZ1bmN0aW9uIChsYXllciwgbGF0bG5nKSB7XHJcblx0XHRpZiAoIShsYXllciBpbnN0YW5jZW9mIEwuTGF5ZXIpKSB7XHJcblx0XHRcdGxhdGxuZyA9IGxheWVyO1xyXG5cdFx0XHRsYXllciA9IHRoaXM7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKGxheWVyIGluc3RhbmNlb2YgTC5GZWF0dXJlR3JvdXApIHtcclxuXHRcdFx0Zm9yICh2YXIgaWQgaW4gdGhpcy5fbGF5ZXJzKSB7XHJcblx0XHRcdFx0bGF5ZXIgPSB0aGlzLl9sYXllcnNbaWRdO1xyXG5cdFx0XHRcdGJyZWFrO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKCFsYXRsbmcpIHtcclxuXHRcdFx0bGF0bG5nID0gbGF5ZXIuZ2V0Q2VudGVyID8gbGF5ZXIuZ2V0Q2VudGVyKCkgOiBsYXllci5nZXRMYXRMbmcoKTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAodGhpcy5fcG9wdXAgJiYgdGhpcy5fbWFwKSB7XHJcblx0XHRcdC8vIHNldCBwb3B1cCBzb3VyY2UgdG8gdGhpcyBsYXllclxyXG5cdFx0XHR0aGlzLl9wb3B1cC5fc291cmNlID0gbGF5ZXI7XHJcblxyXG5cdFx0XHQvLyB1cGRhdGUgdGhlIHBvcHVwIChjb250ZW50LCBsYXlvdXQsIGVjdC4uLilcclxuXHRcdFx0dGhpcy5fcG9wdXAudXBkYXRlKCk7XHJcblxyXG5cdFx0XHQvLyBvcGVuIHRoZSBwb3B1cCBvbiB0aGUgbWFwXHJcblx0XHRcdHRoaXMuX21hcC5vcGVuUG9wdXAodGhpcy5fcG9wdXAsIGxhdGxuZyk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBjbG9zZVBvcHVwKCk6IHRoaXNcclxuXHQvLyBDbG9zZXMgdGhlIHBvcHVwIGJvdW5kIHRvIHRoaXMgbGF5ZXIgaWYgaXQgaXMgb3Blbi5cclxuXHRjbG9zZVBvcHVwOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAodGhpcy5fcG9wdXApIHtcclxuXHRcdFx0dGhpcy5fcG9wdXAuX2Nsb3NlKCk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHRvZ2dsZVBvcHVwKCk6IHRoaXNcclxuXHQvLyBPcGVucyBvciBjbG9zZXMgdGhlIHBvcHVwIGJvdW5kIHRvIHRoaXMgbGF5ZXIgZGVwZW5kaW5nIG9uIGl0cyBjdXJyZW50IHN0YXRlLlxyXG5cdHRvZ2dsZVBvcHVwOiBmdW5jdGlvbiAodGFyZ2V0KSB7XHJcblx0XHRpZiAodGhpcy5fcG9wdXApIHtcclxuXHRcdFx0aWYgKHRoaXMuX3BvcHVwLl9tYXApIHtcclxuXHRcdFx0XHR0aGlzLmNsb3NlUG9wdXAoKTtcclxuXHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHR0aGlzLm9wZW5Qb3B1cCh0YXJnZXQpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGlzUG9wdXBPcGVuKCk6IGJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgcG9wdXAgYm91bmQgdG8gdGhpcyBsYXllciBpcyBjdXJyZW50bHkgb3Blbi5cclxuXHRpc1BvcHVwT3BlbjogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX3BvcHVwLmlzT3BlbigpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0UG9wdXBDb250ZW50KGNvbnRlbnQ6IFN0cmluZ3xIVE1MRWxlbWVudHxQb3B1cCk6IHRoaXNcclxuXHQvLyBTZXRzIHRoZSBjb250ZW50IG9mIHRoZSBwb3B1cCBib3VuZCB0byB0aGlzIGxheWVyLlxyXG5cdHNldFBvcHVwQ29udGVudDogZnVuY3Rpb24gKGNvbnRlbnQpIHtcclxuXHRcdGlmICh0aGlzLl9wb3B1cCkge1xyXG5cdFx0XHR0aGlzLl9wb3B1cC5zZXRDb250ZW50KGNvbnRlbnQpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRQb3B1cCgpOiBQb3B1cFxyXG5cdC8vIFJldHVybnMgdGhlIHBvcHVwIGJvdW5kIHRvIHRoaXMgbGF5ZXIuXHJcblx0Z2V0UG9wdXA6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9wb3B1cDtcclxuXHR9LFxyXG5cclxuXHRfb3BlblBvcHVwOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0dmFyIGxheWVyID0gZS5sYXllciB8fCBlLnRhcmdldDtcclxuXHJcblx0XHRpZiAoIXRoaXMuX3BvcHVwKSB7XHJcblx0XHRcdHJldHVybjtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAoIXRoaXMuX21hcCkge1xyXG5cdFx0XHRyZXR1cm47XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gcHJldmVudCBtYXAgY2xpY2tcclxuXHRcdEwuRG9tRXZlbnQuc3RvcChlKTtcclxuXHJcblx0XHQvLyBpZiB0aGlzIGluaGVyaXRzIGZyb20gUGF0aCBpdHMgYSB2ZWN0b3IgYW5kIHdlIGNhbiBqdXN0XHJcblx0XHQvLyBvcGVuIHRoZSBwb3B1cCBhdCB0aGUgbmV3IGxvY2F0aW9uXHJcblx0XHRpZiAobGF5ZXIgaW5zdGFuY2VvZiBMLlBhdGgpIHtcclxuXHRcdFx0dGhpcy5vcGVuUG9wdXAoZS5sYXllciB8fCBlLnRhcmdldCwgZS5sYXRsbmcpO1xyXG5cdFx0XHRyZXR1cm47XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gb3RoZXJ3aXNlIHRyZWF0IGl0IGxpa2UgYSBtYXJrZXIgYW5kIGZpZ3VyZSBvdXRcclxuXHRcdC8vIGlmIHdlIHNob3VsZCB0b2dnbGUgaXQgb3Blbi9jbG9zZWRcclxuXHRcdGlmICh0aGlzLl9tYXAuaGFzTGF5ZXIodGhpcy5fcG9wdXApICYmIHRoaXMuX3BvcHVwLl9zb3VyY2UgPT09IGxheWVyKSB7XHJcblx0XHRcdHRoaXMuY2xvc2VQb3B1cCgpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0dGhpcy5vcGVuUG9wdXAobGF5ZXIsIGUubGF0bG5nKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfbW92ZVBvcHVwOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0dGhpcy5fcG9wdXAuc2V0TGF0TG5nKGUubGF0bG5nKTtcclxuXHR9XHJcbn0pO1xyXG5cblxuXG4vKlxuICogQGNsYXNzIFRvb2x0aXBcbiAqIEBpbmhlcml0cyBEaXZPdmVybGF5XG4gKiBAYWthIEwuVG9vbHRpcFxuICogVXNlZCB0byBkaXNwbGF5IHNtYWxsIHRleHRzIG9uIHRvcCBvZiBtYXAgbGF5ZXJzLlxuICpcbiAqIEBleGFtcGxlXG4gKlxuICogYGBganNcbiAqIG1hcmtlci5iaW5kVG9vbHRpcChcIm15IHRvb2x0aXAgdGV4dFwiKS5vcGVuVG9vbHRpcCgpO1xuICogYGBgXG4gKiBOb3RlIGFib3V0IHRvb2x0aXAgb2Zmc2V0LiBMZWFmbGV0IHRha2VzIHR3byBvcHRpb25zIGluIGNvbnNpZGVyYXRpb25cbiAqIGZvciBjb21wdXRpbmcgdG9vbHRpcCBvZmZzZXRpbmc6XG4gKiAtIHRoZSBgb2Zmc2V0YCBUb29sdGlwIG9wdGlvbjogaXQgZGVmYXVsdHMgdG8gWzAsIDBdLCBhbmQgaXQncyBzcGVjaWZpYyB0byBvbmUgdG9vbHRpcC5cbiAqICAgQWRkIGEgcG9zaXRpdmUgeCBvZmZzZXQgdG8gbW92ZSB0aGUgdG9vbHRpcCB0byB0aGUgcmlnaHQsIGFuZCBhIHBvc2l0aXZlIHkgb2Zmc2V0IHRvXG4gKiAgIG1vdmUgaXQgdG8gdGhlIGJvdHRvbS4gTmVnYXRpdmVzIHdpbGwgbW92ZSB0byB0aGUgbGVmdCBhbmQgdG9wLlxuICogLSB0aGUgYHRvb2x0aXBBbmNob3JgIEljb24gb3B0aW9uOiB0aGlzIHdpbGwgb25seSBiZSBjb25zaWRlcmVkIGZvciBNYXJrZXIuIFlvdVxuICogICBzaG91bGQgYWRhcHQgdGhpcyB2YWx1ZSBpZiB5b3UgdXNlIGEgY3VzdG9tIGljb24uXG4gKi9cblxuXG4vLyBAbmFtZXNwYWNlIFRvb2x0aXBcbkwuVG9vbHRpcCA9IEwuRGl2T3ZlcmxheS5leHRlbmQoe1xuXG5cdC8vIEBzZWN0aW9uXG5cdC8vIEBha2EgVG9vbHRpcCBvcHRpb25zXG5cdG9wdGlvbnM6IHtcblx0XHQvLyBAb3B0aW9uIHBhbmU6IFN0cmluZyA9ICd0b29sdGlwUGFuZSdcblx0XHQvLyBgTWFwIHBhbmVgIHdoZXJlIHRoZSB0b29sdGlwIHdpbGwgYmUgYWRkZWQuXG5cdFx0cGFuZTogJ3Rvb2x0aXBQYW5lJyxcblxuXHRcdC8vIEBvcHRpb24gb2Zmc2V0OiBQb2ludCA9IFBvaW50KDAsIDApXG5cdFx0Ly8gT3B0aW9uYWwgb2Zmc2V0IG9mIHRoZSB0b29sdGlwIHBvc2l0aW9uLlxuXHRcdG9mZnNldDogWzAsIDBdLFxuXG5cdFx0Ly8gQG9wdGlvbiBkaXJlY3Rpb246IFN0cmluZyA9ICdhdXRvJ1xuXHRcdC8vIERpcmVjdGlvbiB3aGVyZSB0byBvcGVuIHRoZSB0b29sdGlwLiBQb3NzaWJsZSB2YWx1ZXMgYXJlOiBgcmlnaHRgLCBgbGVmdGAsXG5cdFx0Ly8gYHRvcGAsIGBib3R0b21gLCBgY2VudGVyYCwgYGF1dG9gLlxuXHRcdC8vIGBhdXRvYCB3aWxsIGR5bmFtaWNhbHkgc3dpdGNoIGJldHdlZW4gYHJpZ2h0YCBhbmQgYGxlZnRgIGFjY29yZGluZyB0byB0aGUgdG9vbHRpcFxuXHRcdC8vIHBvc2l0aW9uIG9uIHRoZSBtYXAuXG5cdFx0ZGlyZWN0aW9uOiAnYXV0bycsXG5cblx0XHQvLyBAb3B0aW9uIHBlcm1hbmVudDogQm9vbGVhbiA9IGZhbHNlXG5cdFx0Ly8gV2hldGhlciB0byBvcGVuIHRoZSB0b29sdGlwIHBlcm1hbmVudGx5IG9yIG9ubHkgb24gbW91c2VvdmVyLlxuXHRcdHBlcm1hbmVudDogZmFsc2UsXG5cblx0XHQvLyBAb3B0aW9uIHN0aWNreTogQm9vbGVhbiA9IGZhbHNlXG5cdFx0Ly8gSWYgdHJ1ZSwgdGhlIHRvb2x0aXAgd2lsbCBmb2xsb3cgdGhlIG1vdXNlIGluc3RlYWQgb2YgYmVpbmcgZml4ZWQgYXQgdGhlIGZlYXR1cmUgY2VudGVyLlxuXHRcdHN0aWNreTogZmFsc2UsXG5cblx0XHQvLyBAb3B0aW9uIGludGVyYWN0aXZlOiBCb29sZWFuID0gZmFsc2Vcblx0XHQvLyBJZiB0cnVlLCB0aGUgdG9vbHRpcCB3aWxsIGxpc3RlbiB0byB0aGUgZmVhdHVyZSBldmVudHMuXG5cdFx0aW50ZXJhY3RpdmU6IGZhbHNlLFxuXG5cdFx0Ly8gQG9wdGlvbiBvcGFjaXR5OiBOdW1iZXIgPSAwLjlcblx0XHQvLyBUb29sdGlwIGNvbnRhaW5lciBvcGFjaXR5LlxuXHRcdG9wYWNpdHk6IDAuOVxuXHR9LFxuXG5cdG9uQWRkOiBmdW5jdGlvbiAobWFwKSB7XG5cdFx0TC5EaXZPdmVybGF5LnByb3RvdHlwZS5vbkFkZC5jYWxsKHRoaXMsIG1hcCk7XG5cdFx0dGhpcy5zZXRPcGFjaXR5KHRoaXMub3B0aW9ucy5vcGFjaXR5KTtcblxuXHRcdC8vIEBuYW1lc3BhY2UgTWFwXG5cdFx0Ly8gQHNlY3Rpb24gVG9vbHRpcCBldmVudHNcblx0XHQvLyBAZXZlbnQgdG9vbHRpcG9wZW46IFRvb2x0aXBFdmVudFxuXHRcdC8vIEZpcmVkIHdoZW4gYSB0b29sdGlwIGlzIG9wZW5lZCBpbiB0aGUgbWFwLlxuXHRcdG1hcC5maXJlKCd0b29sdGlwb3BlbicsIHt0b29sdGlwOiB0aGlzfSk7XG5cblx0XHRpZiAodGhpcy5fc291cmNlKSB7XG5cdFx0XHQvLyBAbmFtZXNwYWNlIExheWVyXG5cdFx0XHQvLyBAc2VjdGlvbiBUb29sdGlwIGV2ZW50c1xuXHRcdFx0Ly8gQGV2ZW50IHRvb2x0aXBvcGVuOiBUb29sdGlwRXZlbnRcblx0XHRcdC8vIEZpcmVkIHdoZW4gYSB0b29sdGlwIGJvdW5kIHRvIHRoaXMgbGF5ZXIgaXMgb3BlbmVkLlxuXHRcdFx0dGhpcy5fc291cmNlLmZpcmUoJ3Rvb2x0aXBvcGVuJywge3Rvb2x0aXA6IHRoaXN9LCB0cnVlKTtcblx0XHR9XG5cdH0sXG5cblx0b25SZW1vdmU6IGZ1bmN0aW9uIChtYXApIHtcblx0XHRMLkRpdk92ZXJsYXkucHJvdG90eXBlLm9uUmVtb3ZlLmNhbGwodGhpcywgbWFwKTtcblxuXHRcdC8vIEBuYW1lc3BhY2UgTWFwXG5cdFx0Ly8gQHNlY3Rpb24gVG9vbHRpcCBldmVudHNcblx0XHQvLyBAZXZlbnQgdG9vbHRpcGNsb3NlOiBUb29sdGlwRXZlbnRcblx0XHQvLyBGaXJlZCB3aGVuIGEgdG9vbHRpcCBpbiB0aGUgbWFwIGlzIGNsb3NlZC5cblx0XHRtYXAuZmlyZSgndG9vbHRpcGNsb3NlJywge3Rvb2x0aXA6IHRoaXN9KTtcblxuXHRcdGlmICh0aGlzLl9zb3VyY2UpIHtcblx0XHRcdC8vIEBuYW1lc3BhY2UgTGF5ZXJcblx0XHRcdC8vIEBzZWN0aW9uIFRvb2x0aXAgZXZlbnRzXG5cdFx0XHQvLyBAZXZlbnQgdG9vbHRpcGNsb3NlOiBUb29sdGlwRXZlbnRcblx0XHRcdC8vIEZpcmVkIHdoZW4gYSB0b29sdGlwIGJvdW5kIHRvIHRoaXMgbGF5ZXIgaXMgY2xvc2VkLlxuXHRcdFx0dGhpcy5fc291cmNlLmZpcmUoJ3Rvb2x0aXBjbG9zZScsIHt0b29sdGlwOiB0aGlzfSwgdHJ1ZSk7XG5cdFx0fVxuXHR9LFxuXG5cdGdldEV2ZW50czogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBldmVudHMgPSBMLkRpdk92ZXJsYXkucHJvdG90eXBlLmdldEV2ZW50cy5jYWxsKHRoaXMpO1xuXG5cdFx0aWYgKEwuQnJvd3Nlci50b3VjaCAmJiAhdGhpcy5vcHRpb25zLnBlcm1hbmVudCkge1xuXHRcdFx0ZXZlbnRzLnByZWNsaWNrID0gdGhpcy5fY2xvc2U7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIGV2ZW50cztcblx0fSxcblxuXHRfY2xvc2U6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fbWFwKSB7XG5cdFx0XHR0aGlzLl9tYXAuY2xvc2VUb29sdGlwKHRoaXMpO1xuXHRcdH1cblx0fSxcblxuXHRfaW5pdExheW91dDogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBwcmVmaXggPSAnbGVhZmxldC10b29sdGlwJyxcblx0XHQgICAgY2xhc3NOYW1lID0gcHJlZml4ICsgJyAnICsgKHRoaXMub3B0aW9ucy5jbGFzc05hbWUgfHwgJycpICsgJyBsZWFmbGV0LXpvb20tJyArICh0aGlzLl96b29tQW5pbWF0ZWQgPyAnYW5pbWF0ZWQnIDogJ2hpZGUnKTtcblxuXHRcdHRoaXMuX2NvbnRlbnROb2RlID0gdGhpcy5fY29udGFpbmVyID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgY2xhc3NOYW1lKTtcblx0fSxcblxuXHRfdXBkYXRlTGF5b3V0OiBmdW5jdGlvbiAoKSB7fSxcblxuXHRfYWRqdXN0UGFuOiBmdW5jdGlvbiAoKSB7fSxcblxuXHRfc2V0UG9zaXRpb246IGZ1bmN0aW9uIChwb3MpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxuXHRcdCAgICBjb250YWluZXIgPSB0aGlzLl9jb250YWluZXIsXG5cdFx0ICAgIGNlbnRlclBvaW50ID0gbWFwLmxhdExuZ1RvQ29udGFpbmVyUG9pbnQobWFwLmdldENlbnRlcigpKSxcblx0XHQgICAgdG9vbHRpcFBvaW50ID0gbWFwLmxheWVyUG9pbnRUb0NvbnRhaW5lclBvaW50KHBvcyksXG5cdFx0ICAgIGRpcmVjdGlvbiA9IHRoaXMub3B0aW9ucy5kaXJlY3Rpb24sXG5cdFx0ICAgIHRvb2x0aXBXaWR0aCA9IGNvbnRhaW5lci5vZmZzZXRXaWR0aCxcblx0XHQgICAgdG9vbHRpcEhlaWdodCA9IGNvbnRhaW5lci5vZmZzZXRIZWlnaHQsXG5cdFx0ICAgIG9mZnNldCA9IEwucG9pbnQodGhpcy5vcHRpb25zLm9mZnNldCksXG5cdFx0ICAgIGFuY2hvciA9IHRoaXMuX2dldEFuY2hvcigpO1xuXG5cdFx0aWYgKGRpcmVjdGlvbiA9PT0gJ3RvcCcpIHtcblx0XHRcdHBvcyA9IHBvcy5hZGQoTC5wb2ludCgtdG9vbHRpcFdpZHRoIC8gMiArIG9mZnNldC54LCAtdG9vbHRpcEhlaWdodCArIG9mZnNldC55ICsgYW5jaG9yLnksIHRydWUpKTtcblx0XHR9IGVsc2UgaWYgKGRpcmVjdGlvbiA9PT0gJ2JvdHRvbScpIHtcblx0XHRcdHBvcyA9IHBvcy5zdWJ0cmFjdChMLnBvaW50KHRvb2x0aXBXaWR0aCAvIDIgLSBvZmZzZXQueCwgLW9mZnNldC55LCB0cnVlKSk7XG5cdFx0fSBlbHNlIGlmIChkaXJlY3Rpb24gPT09ICdjZW50ZXInKSB7XG5cdFx0XHRwb3MgPSBwb3Muc3VidHJhY3QoTC5wb2ludCh0b29sdGlwV2lkdGggLyAyICsgb2Zmc2V0LngsIHRvb2x0aXBIZWlnaHQgLyAyIC0gYW5jaG9yLnkgKyBvZmZzZXQueSwgdHJ1ZSkpO1xuXHRcdH0gZWxzZSBpZiAoZGlyZWN0aW9uID09PSAncmlnaHQnIHx8IGRpcmVjdGlvbiA9PT0gJ2F1dG8nICYmIHRvb2x0aXBQb2ludC54IDwgY2VudGVyUG9pbnQueCkge1xuXHRcdFx0ZGlyZWN0aW9uID0gJ3JpZ2h0Jztcblx0XHRcdHBvcyA9IHBvcy5hZGQoTC5wb2ludChvZmZzZXQueCArIGFuY2hvci54LCBhbmNob3IueSAtIHRvb2x0aXBIZWlnaHQgLyAyICsgb2Zmc2V0LnksIHRydWUpKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0ZGlyZWN0aW9uID0gJ2xlZnQnO1xuXHRcdFx0cG9zID0gcG9zLnN1YnRyYWN0KEwucG9pbnQodG9vbHRpcFdpZHRoICsgYW5jaG9yLnggLSBvZmZzZXQueCwgdG9vbHRpcEhlaWdodCAvIDIgLSBhbmNob3IueSAtIG9mZnNldC55LCB0cnVlKSk7XG5cdFx0fVxuXG5cdFx0TC5Eb21VdGlsLnJlbW92ZUNsYXNzKGNvbnRhaW5lciwgJ2xlYWZsZXQtdG9vbHRpcC1yaWdodCcpO1xuXHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyhjb250YWluZXIsICdsZWFmbGV0LXRvb2x0aXAtbGVmdCcpO1xuXHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyhjb250YWluZXIsICdsZWFmbGV0LXRvb2x0aXAtdG9wJyk7XG5cdFx0TC5Eb21VdGlsLnJlbW92ZUNsYXNzKGNvbnRhaW5lciwgJ2xlYWZsZXQtdG9vbHRpcC1ib3R0b20nKTtcblx0XHRMLkRvbVV0aWwuYWRkQ2xhc3MoY29udGFpbmVyLCAnbGVhZmxldC10b29sdGlwLScgKyBkaXJlY3Rpb24pO1xuXHRcdEwuRG9tVXRpbC5zZXRQb3NpdGlvbihjb250YWluZXIsIHBvcyk7XG5cdH0sXG5cblx0X3VwZGF0ZVBvc2l0aW9uOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIHBvcyA9IHRoaXMuX21hcC5sYXRMbmdUb0xheWVyUG9pbnQodGhpcy5fbGF0bG5nKTtcblx0XHR0aGlzLl9zZXRQb3NpdGlvbihwb3MpO1xuXHR9LFxuXG5cdHNldE9wYWNpdHk6IGZ1bmN0aW9uIChvcGFjaXR5KSB7XG5cdFx0dGhpcy5vcHRpb25zLm9wYWNpdHkgPSBvcGFjaXR5O1xuXG5cdFx0aWYgKHRoaXMuX2NvbnRhaW5lcikge1xuXHRcdFx0TC5Eb21VdGlsLnNldE9wYWNpdHkodGhpcy5fY29udGFpbmVyLCBvcGFjaXR5KTtcblx0XHR9XG5cdH0sXG5cblx0X2FuaW1hdGVab29tOiBmdW5jdGlvbiAoZSkge1xuXHRcdHZhciBwb3MgPSB0aGlzLl9tYXAuX2xhdExuZ1RvTmV3TGF5ZXJQb2ludCh0aGlzLl9sYXRsbmcsIGUuem9vbSwgZS5jZW50ZXIpO1xuXHRcdHRoaXMuX3NldFBvc2l0aW9uKHBvcyk7XG5cdH0sXG5cblx0X2dldEFuY2hvcjogZnVuY3Rpb24gKCkge1xuXHRcdC8vIFdoZXJlIHNob3VsZCB3ZSBhbmNob3IgdGhlIHRvb2x0aXAgb24gdGhlIHNvdXJjZSBsYXllcj9cblx0XHRyZXR1cm4gTC5wb2ludCh0aGlzLl9zb3VyY2UgJiYgdGhpcy5fc291cmNlLl9nZXRUb29sdGlwQW5jaG9yICYmICF0aGlzLm9wdGlvbnMuc3RpY2t5ID8gdGhpcy5fc291cmNlLl9nZXRUb29sdGlwQW5jaG9yKCkgOiBbMCwgMF0pO1xuXHR9XG5cbn0pO1xuXG4vLyBAbmFtZXNwYWNlIFRvb2x0aXBcbi8vIEBmYWN0b3J5IEwudG9vbHRpcChvcHRpb25zPzogVG9vbHRpcCBvcHRpb25zLCBzb3VyY2U/OiBMYXllcilcbi8vIEluc3RhbnRpYXRlcyBhIFRvb2x0aXAgb2JqZWN0IGdpdmVuIGFuIG9wdGlvbmFsIGBvcHRpb25zYCBvYmplY3QgdGhhdCBkZXNjcmliZXMgaXRzIGFwcGVhcmFuY2UgYW5kIGxvY2F0aW9uIGFuZCBhbiBvcHRpb25hbCBgc291cmNlYCBvYmplY3QgdGhhdCBpcyB1c2VkIHRvIHRhZyB0aGUgdG9vbHRpcCB3aXRoIGEgcmVmZXJlbmNlIHRvIHRoZSBMYXllciB0byB3aGljaCBpdCByZWZlcnMuXG5MLnRvb2x0aXAgPSBmdW5jdGlvbiAob3B0aW9ucywgc291cmNlKSB7XG5cdHJldHVybiBuZXcgTC5Ub29sdGlwKG9wdGlvbnMsIHNvdXJjZSk7XG59O1xuXG4vLyBAbmFtZXNwYWNlIE1hcFxuLy8gQHNlY3Rpb24gTWV0aG9kcyBmb3IgTGF5ZXJzIGFuZCBDb250cm9sc1xuTC5NYXAuaW5jbHVkZSh7XG5cblx0Ly8gQG1ldGhvZCBvcGVuVG9vbHRpcCh0b29sdGlwOiBUb29sdGlwKTogdGhpc1xuXHQvLyBPcGVucyB0aGUgc3BlY2lmaWVkIHRvb2x0aXAuXG5cdC8vIEBhbHRlcm5hdGl2ZVxuXHQvLyBAbWV0aG9kIG9wZW5Ub29sdGlwKGNvbnRlbnQ6IFN0cmluZ3xIVE1MRWxlbWVudCwgbGF0bG5nOiBMYXRMbmcsIG9wdGlvbnM/OiBUb29sdGlwIG9wdGlvbnMpOiB0aGlzXG5cdC8vIENyZWF0ZXMgYSB0b29sdGlwIHdpdGggdGhlIHNwZWNpZmllZCBjb250ZW50IGFuZCBvcHRpb25zIGFuZCBvcGVuIGl0LlxuXHRvcGVuVG9vbHRpcDogZnVuY3Rpb24gKHRvb2x0aXAsIGxhdGxuZywgb3B0aW9ucykge1xuXHRcdGlmICghKHRvb2x0aXAgaW5zdGFuY2VvZiBMLlRvb2x0aXApKSB7XG5cdFx0XHR0b29sdGlwID0gbmV3IEwuVG9vbHRpcChvcHRpb25zKS5zZXRDb250ZW50KHRvb2x0aXApO1xuXHRcdH1cblxuXHRcdGlmIChsYXRsbmcpIHtcblx0XHRcdHRvb2x0aXAuc2V0TGF0TG5nKGxhdGxuZyk7XG5cdFx0fVxuXG5cdFx0aWYgKHRoaXMuaGFzTGF5ZXIodG9vbHRpcCkpIHtcblx0XHRcdHJldHVybiB0aGlzO1xuXHRcdH1cblxuXHRcdHJldHVybiB0aGlzLmFkZExheWVyKHRvb2x0aXApO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgY2xvc2VUb29sdGlwKHRvb2x0aXA/OiBUb29sdGlwKTogdGhpc1xuXHQvLyBDbG9zZXMgdGhlIHRvb2x0aXAgZ2l2ZW4gYXMgcGFyYW1ldGVyLlxuXHRjbG9zZVRvb2x0aXA6IGZ1bmN0aW9uICh0b29sdGlwKSB7XG5cdFx0aWYgKHRvb2x0aXApIHtcblx0XHRcdHRoaXMucmVtb3ZlTGF5ZXIodG9vbHRpcCk7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9XG5cbn0pO1xuXG4vKlxuICogQG5hbWVzcGFjZSBMYXllclxuICogQHNlY3Rpb24gVG9vbHRpcCBtZXRob2RzIGV4YW1wbGVcbiAqXG4gKiBBbGwgbGF5ZXJzIHNoYXJlIGEgc2V0IG9mIG1ldGhvZHMgY29udmVuaWVudCBmb3IgYmluZGluZyB0b29sdGlwcyB0byBpdC5cbiAqXG4gKiBgYGBqc1xuICogdmFyIGxheWVyID0gTC5Qb2x5Z29uKGxhdGxuZ3MpLmJpbmRUb29sdGlwKCdIaSBUaGVyZSEnKS5hZGRUbyhtYXApO1xuICogbGF5ZXIub3BlblRvb2x0aXAoKTtcbiAqIGxheWVyLmNsb3NlVG9vbHRpcCgpO1xuICogYGBgXG4gKi9cblxuLy8gQHNlY3Rpb24gVG9vbHRpcCBtZXRob2RzXG5MLkxheWVyLmluY2x1ZGUoe1xuXG5cdC8vIEBtZXRob2QgYmluZFRvb2x0aXAoY29udGVudDogU3RyaW5nfEhUTUxFbGVtZW50fEZ1bmN0aW9ufFRvb2x0aXAsIG9wdGlvbnM/OiBUb29sdGlwIG9wdGlvbnMpOiB0aGlzXG5cdC8vIEJpbmRzIGEgdG9vbHRpcCB0byB0aGUgbGF5ZXIgd2l0aCB0aGUgcGFzc2VkIGBjb250ZW50YCBhbmQgc2V0cyB1cCB0aGVcblx0Ly8gbmVjY2Vzc2FyeSBldmVudCBsaXN0ZW5lcnMuIElmIGEgYEZ1bmN0aW9uYCBpcyBwYXNzZWQgaXQgd2lsbCByZWNlaXZlXG5cdC8vIHRoZSBsYXllciBhcyB0aGUgZmlyc3QgYXJndW1lbnQgYW5kIHNob3VsZCByZXR1cm4gYSBgU3RyaW5nYCBvciBgSFRNTEVsZW1lbnRgLlxuXHRiaW5kVG9vbHRpcDogZnVuY3Rpb24gKGNvbnRlbnQsIG9wdGlvbnMpIHtcblxuXHRcdGlmIChjb250ZW50IGluc3RhbmNlb2YgTC5Ub29sdGlwKSB7XG5cdFx0XHRMLnNldE9wdGlvbnMoY29udGVudCwgb3B0aW9ucyk7XG5cdFx0XHR0aGlzLl90b29sdGlwID0gY29udGVudDtcblx0XHRcdGNvbnRlbnQuX3NvdXJjZSA9IHRoaXM7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGlmICghdGhpcy5fdG9vbHRpcCB8fCBvcHRpb25zKSB7XG5cdFx0XHRcdHRoaXMuX3Rvb2x0aXAgPSBMLnRvb2x0aXAob3B0aW9ucywgdGhpcyk7XG5cdFx0XHR9XG5cdFx0XHR0aGlzLl90b29sdGlwLnNldENvbnRlbnQoY29udGVudCk7XG5cblx0XHR9XG5cblx0XHR0aGlzLl9pbml0VG9vbHRpcEludGVyYWN0aW9ucygpO1xuXG5cdFx0aWYgKHRoaXMuX3Rvb2x0aXAub3B0aW9ucy5wZXJtYW5lbnQgJiYgdGhpcy5fbWFwICYmIHRoaXMuX21hcC5oYXNMYXllcih0aGlzKSkge1xuXHRcdFx0dGhpcy5vcGVuVG9vbHRpcCgpO1xuXHRcdH1cblxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgdW5iaW5kVG9vbHRpcCgpOiB0aGlzXG5cdC8vIFJlbW92ZXMgdGhlIHRvb2x0aXAgcHJldmlvdXNseSBib3VuZCB3aXRoIGBiaW5kVG9vbHRpcGAuXG5cdHVuYmluZFRvb2x0aXA6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fdG9vbHRpcCkge1xuXHRcdFx0dGhpcy5faW5pdFRvb2x0aXBJbnRlcmFjdGlvbnModHJ1ZSk7XG5cdFx0XHR0aGlzLmNsb3NlVG9vbHRpcCgpO1xuXHRcdFx0dGhpcy5fdG9vbHRpcCA9IG51bGw7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdF9pbml0VG9vbHRpcEludGVyYWN0aW9uczogZnVuY3Rpb24gKHJlbW92ZSkge1xuXHRcdGlmICghcmVtb3ZlICYmIHRoaXMuX3Rvb2x0aXBIYW5kbGVyc0FkZGVkKSB7IHJldHVybjsgfVxuXHRcdHZhciBvbk9mZiA9IHJlbW92ZSA/ICdvZmYnIDogJ29uJyxcblx0XHQgICAgZXZlbnRzID0ge1xuXHRcdFx0cmVtb3ZlOiB0aGlzLmNsb3NlVG9vbHRpcCxcblx0XHRcdG1vdmU6IHRoaXMuX21vdmVUb29sdGlwXG5cdFx0ICAgIH07XG5cdFx0aWYgKCF0aGlzLl90b29sdGlwLm9wdGlvbnMucGVybWFuZW50KSB7XG5cdFx0XHRldmVudHMubW91c2VvdmVyID0gdGhpcy5fb3BlblRvb2x0aXA7XG5cdFx0XHRldmVudHMubW91c2VvdXQgPSB0aGlzLmNsb3NlVG9vbHRpcDtcblx0XHRcdGlmICh0aGlzLl90b29sdGlwLm9wdGlvbnMuc3RpY2t5KSB7XG5cdFx0XHRcdGV2ZW50cy5tb3VzZW1vdmUgPSB0aGlzLl9tb3ZlVG9vbHRpcDtcblx0XHRcdH1cblx0XHRcdGlmIChMLkJyb3dzZXIudG91Y2gpIHtcblx0XHRcdFx0ZXZlbnRzLmNsaWNrID0gdGhpcy5fb3BlblRvb2x0aXA7XG5cdFx0XHR9XG5cdFx0fSBlbHNlIHtcblx0XHRcdGV2ZW50cy5hZGQgPSB0aGlzLl9vcGVuVG9vbHRpcDtcblx0XHR9XG5cdFx0dGhpc1tvbk9mZl0oZXZlbnRzKTtcblx0XHR0aGlzLl90b29sdGlwSGFuZGxlcnNBZGRlZCA9ICFyZW1vdmU7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBvcGVuVG9vbHRpcChsYXRsbmc/OiBMYXRMbmcpOiB0aGlzXG5cdC8vIE9wZW5zIHRoZSBib3VuZCB0b29sdGlwIGF0IHRoZSBzcGVjaWZpY2VkIGBsYXRsbmdgIG9yIGF0IHRoZSBkZWZhdWx0IHRvb2x0aXAgYW5jaG9yIGlmIG5vIGBsYXRsbmdgIGlzIHBhc3NlZC5cblx0b3BlblRvb2x0aXA6IGZ1bmN0aW9uIChsYXllciwgbGF0bG5nKSB7XG5cdFx0aWYgKCEobGF5ZXIgaW5zdGFuY2VvZiBMLkxheWVyKSkge1xuXHRcdFx0bGF0bG5nID0gbGF5ZXI7XG5cdFx0XHRsYXllciA9IHRoaXM7XG5cdFx0fVxuXG5cdFx0aWYgKGxheWVyIGluc3RhbmNlb2YgTC5GZWF0dXJlR3JvdXApIHtcblx0XHRcdGZvciAodmFyIGlkIGluIHRoaXMuX2xheWVycykge1xuXHRcdFx0XHRsYXllciA9IHRoaXMuX2xheWVyc1tpZF07XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdGlmICghbGF0bG5nKSB7XG5cdFx0XHRsYXRsbmcgPSBsYXllci5nZXRDZW50ZXIgPyBsYXllci5nZXRDZW50ZXIoKSA6IGxheWVyLmdldExhdExuZygpO1xuXHRcdH1cblxuXHRcdGlmICh0aGlzLl90b29sdGlwICYmIHRoaXMuX21hcCkge1xuXG5cdFx0XHQvLyBzZXQgdG9vbHRpcCBzb3VyY2UgdG8gdGhpcyBsYXllclxuXHRcdFx0dGhpcy5fdG9vbHRpcC5fc291cmNlID0gbGF5ZXI7XG5cblx0XHRcdC8vIHVwZGF0ZSB0aGUgdG9vbHRpcCAoY29udGVudCwgbGF5b3V0LCBlY3QuLi4pXG5cdFx0XHR0aGlzLl90b29sdGlwLnVwZGF0ZSgpO1xuXG5cdFx0XHQvLyBvcGVuIHRoZSB0b29sdGlwIG9uIHRoZSBtYXBcblx0XHRcdHRoaXMuX21hcC5vcGVuVG9vbHRpcCh0aGlzLl90b29sdGlwLCBsYXRsbmcpO1xuXG5cdFx0XHQvLyBUb29sdGlwIGNvbnRhaW5lciBtYXkgbm90IGJlIGRlZmluZWQgaWYgbm90IHBlcm1hbmVudCBhbmQgbmV2ZXJcblx0XHRcdC8vIG9wZW5lZC5cblx0XHRcdGlmICh0aGlzLl90b29sdGlwLm9wdGlvbnMuaW50ZXJhY3RpdmUgJiYgdGhpcy5fdG9vbHRpcC5fY29udGFpbmVyKSB7XG5cdFx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl90b29sdGlwLl9jb250YWluZXIsICdsZWFmbGV0LWNsaWNrYWJsZScpO1xuXHRcdFx0XHR0aGlzLmFkZEludGVyYWN0aXZlVGFyZ2V0KHRoaXMuX3Rvb2x0aXAuX2NvbnRhaW5lcik7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBjbG9zZVRvb2x0aXAoKTogdGhpc1xuXHQvLyBDbG9zZXMgdGhlIHRvb2x0aXAgYm91bmQgdG8gdGhpcyBsYXllciBpZiBpdCBpcyBvcGVuLlxuXHRjbG9zZVRvb2x0aXA6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fdG9vbHRpcCkge1xuXHRcdFx0dGhpcy5fdG9vbHRpcC5fY2xvc2UoKTtcblx0XHRcdGlmICh0aGlzLl90b29sdGlwLm9wdGlvbnMuaW50ZXJhY3RpdmUgJiYgdGhpcy5fdG9vbHRpcC5fY29udGFpbmVyKSB7XG5cdFx0XHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl90b29sdGlwLl9jb250YWluZXIsICdsZWFmbGV0LWNsaWNrYWJsZScpO1xuXHRcdFx0XHR0aGlzLnJlbW92ZUludGVyYWN0aXZlVGFyZ2V0KHRoaXMuX3Rvb2x0aXAuX2NvbnRhaW5lcik7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgdG9nZ2xlVG9vbHRpcCgpOiB0aGlzXG5cdC8vIE9wZW5zIG9yIGNsb3NlcyB0aGUgdG9vbHRpcCBib3VuZCB0byB0aGlzIGxheWVyIGRlcGVuZGluZyBvbiBpdHMgY3VycmVudCBzdGF0ZS5cblx0dG9nZ2xlVG9vbHRpcDogZnVuY3Rpb24gKHRhcmdldCkge1xuXHRcdGlmICh0aGlzLl90b29sdGlwKSB7XG5cdFx0XHRpZiAodGhpcy5fdG9vbHRpcC5fbWFwKSB7XG5cdFx0XHRcdHRoaXMuY2xvc2VUb29sdGlwKCk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHR0aGlzLm9wZW5Ub29sdGlwKHRhcmdldCk7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgaXNUb29sdGlwT3BlbigpOiBib29sZWFuXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSB0b29sdGlwIGJvdW5kIHRvIHRoaXMgbGF5ZXIgaXMgY3VycmVudGx5IG9wZW4uXG5cdGlzVG9vbHRpcE9wZW46IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5fdG9vbHRpcC5pc09wZW4oKTtcblx0fSxcblxuXHQvLyBAbWV0aG9kIHNldFRvb2x0aXBDb250ZW50KGNvbnRlbnQ6IFN0cmluZ3xIVE1MRWxlbWVudHxUb29sdGlwKTogdGhpc1xuXHQvLyBTZXRzIHRoZSBjb250ZW50IG9mIHRoZSB0b29sdGlwIGJvdW5kIHRvIHRoaXMgbGF5ZXIuXG5cdHNldFRvb2x0aXBDb250ZW50OiBmdW5jdGlvbiAoY29udGVudCkge1xuXHRcdGlmICh0aGlzLl90b29sdGlwKSB7XG5cdFx0XHR0aGlzLl90b29sdGlwLnNldENvbnRlbnQoY29udGVudCk7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgZ2V0VG9vbHRpcCgpOiBUb29sdGlwXG5cdC8vIFJldHVybnMgdGhlIHRvb2x0aXAgYm91bmQgdG8gdGhpcyBsYXllci5cblx0Z2V0VG9vbHRpcDogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl90b29sdGlwO1xuXHR9LFxuXG5cdF9vcGVuVG9vbHRpcDogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgbGF5ZXIgPSBlLmxheWVyIHx8IGUudGFyZ2V0O1xuXG5cdFx0aWYgKCF0aGlzLl90b29sdGlwIHx8ICF0aGlzLl9tYXApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0dGhpcy5vcGVuVG9vbHRpcChsYXllciwgdGhpcy5fdG9vbHRpcC5vcHRpb25zLnN0aWNreSA/IGUubGF0bG5nIDogdW5kZWZpbmVkKTtcblx0fSxcblxuXHRfbW92ZVRvb2x0aXA6IGZ1bmN0aW9uIChlKSB7XG5cdFx0dmFyIGxhdGxuZyA9IGUubGF0bG5nLCBjb250YWluZXJQb2ludCwgbGF5ZXJQb2ludDtcblx0XHRpZiAodGhpcy5fdG9vbHRpcC5vcHRpb25zLnN0aWNreSAmJiBlLm9yaWdpbmFsRXZlbnQpIHtcblx0XHRcdGNvbnRhaW5lclBvaW50ID0gdGhpcy5fbWFwLm1vdXNlRXZlbnRUb0NvbnRhaW5lclBvaW50KGUub3JpZ2luYWxFdmVudCk7XG5cdFx0XHRsYXllclBvaW50ID0gdGhpcy5fbWFwLmNvbnRhaW5lclBvaW50VG9MYXllclBvaW50KGNvbnRhaW5lclBvaW50KTtcblx0XHRcdGxhdGxuZyA9IHRoaXMuX21hcC5sYXllclBvaW50VG9MYXRMbmcobGF5ZXJQb2ludCk7XG5cdFx0fVxuXHRcdHRoaXMuX3Rvb2x0aXAuc2V0TGF0TG5nKGxhdGxuZyk7XG5cdH1cbn0pO1xuXG5cblxuLypcclxuICogQGNsYXNzIExheWVyR3JvdXBcclxuICogQGFrYSBMLkxheWVyR3JvdXBcclxuICogQGluaGVyaXRzIExheWVyXHJcbiAqXHJcbiAqIFVzZWQgdG8gZ3JvdXAgc2V2ZXJhbCBsYXllcnMgYW5kIGhhbmRsZSB0aGVtIGFzIG9uZS4gSWYgeW91IGFkZCBpdCB0byB0aGUgbWFwLFxyXG4gKiBhbnkgbGF5ZXJzIGFkZGVkIG9yIHJlbW92ZWQgZnJvbSB0aGUgZ3JvdXAgd2lsbCBiZSBhZGRlZC9yZW1vdmVkIG9uIHRoZSBtYXAgYXNcclxuICogd2VsbC4gRXh0ZW5kcyBgTGF5ZXJgLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBMLmxheWVyR3JvdXAoW21hcmtlcjEsIG1hcmtlcjJdKVxyXG4gKiBcdC5hZGRMYXllcihwb2x5bGluZSlcclxuICogXHQuYWRkVG8obWFwKTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxuTC5MYXllckdyb3VwID0gTC5MYXllci5leHRlbmQoe1xyXG5cclxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAobGF5ZXJzKSB7XHJcblx0XHR0aGlzLl9sYXllcnMgPSB7fTtcclxuXHJcblx0XHR2YXIgaSwgbGVuO1xyXG5cclxuXHRcdGlmIChsYXllcnMpIHtcclxuXHRcdFx0Zm9yIChpID0gMCwgbGVuID0gbGF5ZXJzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdFx0dGhpcy5hZGRMYXllcihsYXllcnNbaV0pO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBhZGRMYXllcihsYXllcjogTGF5ZXIpOiB0aGlzXHJcblx0Ly8gQWRkcyB0aGUgZ2l2ZW4gbGF5ZXIgdG8gdGhlIGdyb3VwLlxyXG5cdGFkZExheWVyOiBmdW5jdGlvbiAobGF5ZXIpIHtcclxuXHRcdHZhciBpZCA9IHRoaXMuZ2V0TGF5ZXJJZChsYXllcik7XHJcblxyXG5cdFx0dGhpcy5fbGF5ZXJzW2lkXSA9IGxheWVyO1xyXG5cclxuXHRcdGlmICh0aGlzLl9tYXApIHtcclxuXHRcdFx0dGhpcy5fbWFwLmFkZExheWVyKGxheWVyKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHJlbW92ZUxheWVyKGxheWVyOiBMYXllcik6IHRoaXNcclxuXHQvLyBSZW1vdmVzIHRoZSBnaXZlbiBsYXllciBmcm9tIHRoZSBncm91cC5cclxuXHQvLyBAYWx0ZXJuYXRpdmVcclxuXHQvLyBAbWV0aG9kIHJlbW92ZUxheWVyKGlkOiBOdW1iZXIpOiB0aGlzXHJcblx0Ly8gUmVtb3ZlcyB0aGUgbGF5ZXIgd2l0aCB0aGUgZ2l2ZW4gaW50ZXJuYWwgSUQgZnJvbSB0aGUgZ3JvdXAuXHJcblx0cmVtb3ZlTGF5ZXI6IGZ1bmN0aW9uIChsYXllcikge1xyXG5cdFx0dmFyIGlkID0gbGF5ZXIgaW4gdGhpcy5fbGF5ZXJzID8gbGF5ZXIgOiB0aGlzLmdldExheWVySWQobGF5ZXIpO1xyXG5cclxuXHRcdGlmICh0aGlzLl9tYXAgJiYgdGhpcy5fbGF5ZXJzW2lkXSkge1xyXG5cdFx0XHR0aGlzLl9tYXAucmVtb3ZlTGF5ZXIodGhpcy5fbGF5ZXJzW2lkXSk7XHJcblx0XHR9XHJcblxyXG5cdFx0ZGVsZXRlIHRoaXMuX2xheWVyc1tpZF07XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBoYXNMYXllcihsYXllcjogTGF5ZXIpOiBCb29sZWFuXHJcblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIGdpdmVuIGxheWVyIGlzIGN1cnJlbnRseSBhZGRlZCB0byB0aGUgZ3JvdXAuXHJcblx0aGFzTGF5ZXI6IGZ1bmN0aW9uIChsYXllcikge1xyXG5cdFx0cmV0dXJuICEhbGF5ZXIgJiYgKGxheWVyIGluIHRoaXMuX2xheWVycyB8fCB0aGlzLmdldExheWVySWQobGF5ZXIpIGluIHRoaXMuX2xheWVycyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBjbGVhckxheWVycygpOiB0aGlzXHJcblx0Ly8gUmVtb3ZlcyBhbGwgdGhlIGxheWVycyBmcm9tIHRoZSBncm91cC5cclxuXHRjbGVhckxheWVyczogZnVuY3Rpb24gKCkge1xyXG5cdFx0Zm9yICh2YXIgaSBpbiB0aGlzLl9sYXllcnMpIHtcclxuXHRcdFx0dGhpcy5yZW1vdmVMYXllcih0aGlzLl9sYXllcnNbaV0pO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBpbnZva2UobWV0aG9kTmFtZTogU3RyaW5nLCDigKYpOiB0aGlzXHJcblx0Ly8gQ2FsbHMgYG1ldGhvZE5hbWVgIG9uIGV2ZXJ5IGxheWVyIGNvbnRhaW5lZCBpbiB0aGlzIGdyb3VwLCBwYXNzaW5nIGFueVxyXG5cdC8vIGFkZGl0aW9uYWwgcGFyYW1ldGVycy4gSGFzIG5vIGVmZmVjdCBpZiB0aGUgbGF5ZXJzIGNvbnRhaW5lZCBkbyBub3RcclxuXHQvLyBpbXBsZW1lbnQgYG1ldGhvZE5hbWVgLlxyXG5cdGludm9rZTogZnVuY3Rpb24gKG1ldGhvZE5hbWUpIHtcclxuXHRcdHZhciBhcmdzID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzLCAxKSxcclxuXHRcdCAgICBpLCBsYXllcjtcclxuXHJcblx0XHRmb3IgKGkgaW4gdGhpcy5fbGF5ZXJzKSB7XHJcblx0XHRcdGxheWVyID0gdGhpcy5fbGF5ZXJzW2ldO1xyXG5cclxuXHRcdFx0aWYgKGxheWVyW21ldGhvZE5hbWVdKSB7XHJcblx0XHRcdFx0bGF5ZXJbbWV0aG9kTmFtZV0uYXBwbHkobGF5ZXIsIGFyZ3MpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0b25BZGQ6IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdGZvciAodmFyIGkgaW4gdGhpcy5fbGF5ZXJzKSB7XHJcblx0XHRcdG1hcC5hZGRMYXllcih0aGlzLl9sYXllcnNbaV0pO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdG9uUmVtb3ZlOiBmdW5jdGlvbiAobWFwKSB7XHJcblx0XHRmb3IgKHZhciBpIGluIHRoaXMuX2xheWVycykge1xyXG5cdFx0XHRtYXAucmVtb3ZlTGF5ZXIodGhpcy5fbGF5ZXJzW2ldKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGVhY2hMYXllcihmbjogRnVuY3Rpb24sIGNvbnRleHQ/OiBPYmplY3QpOiB0aGlzXHJcblx0Ly8gSXRlcmF0ZXMgb3ZlciB0aGUgbGF5ZXJzIG9mIHRoZSBncm91cCwgb3B0aW9uYWxseSBzcGVjaWZ5aW5nIGNvbnRleHQgb2YgdGhlIGl0ZXJhdG9yIGZ1bmN0aW9uLlxyXG5cdC8vIGBgYGpzXHJcblx0Ly8gZ3JvdXAuZWFjaExheWVyKGZ1bmN0aW9uIChsYXllcikge1xyXG5cdC8vIFx0bGF5ZXIuYmluZFBvcHVwKCdIZWxsbycpO1xyXG5cdC8vIH0pO1xyXG5cdC8vIGBgYFxyXG5cdGVhY2hMYXllcjogZnVuY3Rpb24gKG1ldGhvZCwgY29udGV4dCkge1xyXG5cdFx0Zm9yICh2YXIgaSBpbiB0aGlzLl9sYXllcnMpIHtcclxuXHRcdFx0bWV0aG9kLmNhbGwoY29udGV4dCwgdGhpcy5fbGF5ZXJzW2ldKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0TGF5ZXIoaWQ6IE51bWJlcik6IExheWVyXHJcblx0Ly8gUmV0dXJucyB0aGUgbGF5ZXIgd2l0aCB0aGUgZ2l2ZW4gaW50ZXJuYWwgSUQuXHJcblx0Z2V0TGF5ZXI6IGZ1bmN0aW9uIChpZCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX2xheWVyc1tpZF07XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRMYXllcnMoKTogTGF5ZXJbXVxyXG5cdC8vIFJldHVybnMgYW4gYXJyYXkgb2YgYWxsIHRoZSBsYXllcnMgYWRkZWQgdG8gdGhlIGdyb3VwLlxyXG5cdGdldExheWVyczogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGxheWVycyA9IFtdO1xyXG5cclxuXHRcdGZvciAodmFyIGkgaW4gdGhpcy5fbGF5ZXJzKSB7XHJcblx0XHRcdGxheWVycy5wdXNoKHRoaXMuX2xheWVyc1tpXSk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gbGF5ZXJzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0WkluZGV4KHpJbmRleDogTnVtYmVyKTogdGhpc1xyXG5cdC8vIENhbGxzIGBzZXRaSW5kZXhgIG9uIGV2ZXJ5IGxheWVyIGNvbnRhaW5lZCBpbiB0aGlzIGdyb3VwLCBwYXNzaW5nIHRoZSB6LWluZGV4LlxyXG5cdHNldFpJbmRleDogZnVuY3Rpb24gKHpJbmRleCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuaW52b2tlKCdzZXRaSW5kZXgnLCB6SW5kZXgpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0TGF5ZXJJZChsYXllcjogTGF5ZXIpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBpbnRlcm5hbCBJRCBmb3IgYSBsYXllclxyXG5cdGdldExheWVySWQ6IGZ1bmN0aW9uIChsYXllcikge1xyXG5cdFx0cmV0dXJuIEwuc3RhbXAobGF5ZXIpO1xyXG5cdH1cclxufSk7XHJcblxyXG5cclxuLy8gQGZhY3RvcnkgTC5sYXllckdyb3VwKGxheWVyczogTGF5ZXJbXSlcclxuLy8gQ3JlYXRlIGEgbGF5ZXIgZ3JvdXAsIG9wdGlvbmFsbHkgZ2l2ZW4gYW4gaW5pdGlhbCBzZXQgb2YgbGF5ZXJzLlxyXG5MLmxheWVyR3JvdXAgPSBmdW5jdGlvbiAobGF5ZXJzKSB7XHJcblx0cmV0dXJuIG5ldyBMLkxheWVyR3JvdXAobGF5ZXJzKTtcclxufTtcclxuXG5cblxuLypcclxuICogQGNsYXNzIEZlYXR1cmVHcm91cFxyXG4gKiBAYWthIEwuRmVhdHVyZUdyb3VwXHJcbiAqIEBpbmhlcml0cyBMYXllckdyb3VwXHJcbiAqXHJcbiAqIEV4dGVuZGVkIGBMYXllckdyb3VwYCB0aGF0IG1ha2VzIGl0IGVhc2llciB0byBkbyB0aGUgc2FtZSB0aGluZyB0byBhbGwgaXRzIG1lbWJlciBsYXllcnM6XHJcbiAqICAqIFtgYmluZFBvcHVwYF0oI2xheWVyLWJpbmRwb3B1cCkgYmluZHMgYSBwb3B1cCB0byBhbGwgb2YgdGhlIGxheWVycyBhdCBvbmNlIChsaWtld2lzZSB3aXRoIFtgYmluZFRvb2x0aXBgXSgjbGF5ZXItYmluZHRvb2x0aXApKVxyXG4gKiAgKiBFdmVudHMgYXJlIHByb3BhZ2F0ZWQgdG8gdGhlIGBGZWF0dXJlR3JvdXBgLCBzbyBpZiB0aGUgZ3JvdXAgaGFzIGFuIGV2ZW50XHJcbiAqIGhhbmRsZXIsIGl0IHdpbGwgaGFuZGxlIGV2ZW50cyBmcm9tIGFueSBvZiB0aGUgbGF5ZXJzLiBUaGlzIGluY2x1ZGVzIG1vdXNlIGV2ZW50c1xyXG4gKiBhbmQgY3VzdG9tIGV2ZW50cy5cclxuICogICogSGFzIGBsYXllcmFkZGAgYW5kIGBsYXllcnJlbW92ZWAgZXZlbnRzXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIEwuZmVhdHVyZUdyb3VwKFttYXJrZXIxLCBtYXJrZXIyLCBwb2x5bGluZV0pXHJcbiAqIFx0LmJpbmRQb3B1cCgnSGVsbG8gd29ybGQhJylcclxuICogXHQub24oJ2NsaWNrJywgZnVuY3Rpb24oKSB7IGFsZXJ0KCdDbGlja2VkIG9uIGEgbWVtYmVyIG9mIHRoZSBncm91cCEnKTsgfSlcclxuICogXHQuYWRkVG8obWFwKTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxuTC5GZWF0dXJlR3JvdXAgPSBMLkxheWVyR3JvdXAuZXh0ZW5kKHtcclxuXHJcblx0YWRkTGF5ZXI6IGZ1bmN0aW9uIChsYXllcikge1xyXG5cdFx0aWYgKHRoaXMuaGFzTGF5ZXIobGF5ZXIpKSB7XHJcblx0XHRcdHJldHVybiB0aGlzO1xyXG5cdFx0fVxyXG5cclxuXHRcdGxheWVyLmFkZEV2ZW50UGFyZW50KHRoaXMpO1xyXG5cclxuXHRcdEwuTGF5ZXJHcm91cC5wcm90b3R5cGUuYWRkTGF5ZXIuY2FsbCh0aGlzLCBsYXllcik7XHJcblxyXG5cdFx0Ly8gQGV2ZW50IGxheWVyYWRkOiBMYXllckV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIGEgbGF5ZXIgaXMgYWRkZWQgdG8gdGhpcyBgRmVhdHVyZUdyb3VwYFxyXG5cdFx0cmV0dXJuIHRoaXMuZmlyZSgnbGF5ZXJhZGQnLCB7bGF5ZXI6IGxheWVyfSk7XHJcblx0fSxcclxuXHJcblx0cmVtb3ZlTGF5ZXI6IGZ1bmN0aW9uIChsYXllcikge1xyXG5cdFx0aWYgKCF0aGlzLmhhc0xheWVyKGxheWVyKSkge1xyXG5cdFx0XHRyZXR1cm4gdGhpcztcclxuXHRcdH1cclxuXHRcdGlmIChsYXllciBpbiB0aGlzLl9sYXllcnMpIHtcclxuXHRcdFx0bGF5ZXIgPSB0aGlzLl9sYXllcnNbbGF5ZXJdO1xyXG5cdFx0fVxyXG5cclxuXHRcdGxheWVyLnJlbW92ZUV2ZW50UGFyZW50KHRoaXMpO1xyXG5cclxuXHRcdEwuTGF5ZXJHcm91cC5wcm90b3R5cGUucmVtb3ZlTGF5ZXIuY2FsbCh0aGlzLCBsYXllcik7XHJcblxyXG5cdFx0Ly8gQGV2ZW50IGxheWVycmVtb3ZlOiBMYXllckV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIGEgbGF5ZXIgaXMgcmVtb3ZlZCBmcm9tIHRoaXMgYEZlYXR1cmVHcm91cGBcclxuXHRcdHJldHVybiB0aGlzLmZpcmUoJ2xheWVycmVtb3ZlJywge2xheWVyOiBsYXllcn0pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0U3R5bGUoc3R5bGU6IFBhdGggb3B0aW9ucyk6IHRoaXNcclxuXHQvLyBTZXRzIHRoZSBnaXZlbiBwYXRoIG9wdGlvbnMgdG8gZWFjaCBsYXllciBvZiB0aGUgZ3JvdXAgdGhhdCBoYXMgYSBgc2V0U3R5bGVgIG1ldGhvZC5cclxuXHRzZXRTdHlsZTogZnVuY3Rpb24gKHN0eWxlKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5pbnZva2UoJ3NldFN0eWxlJywgc3R5bGUpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgYnJpbmdUb0Zyb250KCk6IHRoaXNcclxuXHQvLyBCcmluZ3MgdGhlIGxheWVyIGdyb3VwIHRvIHRoZSB0b3Agb2YgYWxsIG90aGVyIGxheWVyc1xyXG5cdGJyaW5nVG9Gcm9udDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuaW52b2tlKCdicmluZ1RvRnJvbnQnKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGJyaW5nVG9CYWNrKCk6IHRoaXNcclxuXHQvLyBCcmluZ3MgdGhlIGxheWVyIGdyb3VwIHRvIHRoZSB0b3Agb2YgYWxsIG90aGVyIGxheWVyc1xyXG5cdGJyaW5nVG9CYWNrOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5pbnZva2UoJ2JyaW5nVG9CYWNrJyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRCb3VuZHMoKTogTGF0TG5nQm91bmRzXHJcblx0Ly8gUmV0dXJucyB0aGUgTGF0TG5nQm91bmRzIG9mIHRoZSBGZWF0dXJlIEdyb3VwIChjcmVhdGVkIGZyb20gYm91bmRzIGFuZCBjb29yZGluYXRlcyBvZiBpdHMgY2hpbGRyZW4pLlxyXG5cdGdldEJvdW5kczogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGJvdW5kcyA9IG5ldyBMLkxhdExuZ0JvdW5kcygpO1xyXG5cclxuXHRcdGZvciAodmFyIGlkIGluIHRoaXMuX2xheWVycykge1xyXG5cdFx0XHR2YXIgbGF5ZXIgPSB0aGlzLl9sYXllcnNbaWRdO1xyXG5cdFx0XHRib3VuZHMuZXh0ZW5kKGxheWVyLmdldEJvdW5kcyA/IGxheWVyLmdldEJvdW5kcygpIDogbGF5ZXIuZ2V0TGF0TG5nKCkpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIGJvdW5kcztcclxuXHR9XHJcbn0pO1xyXG5cclxuLy8gQGZhY3RvcnkgTC5mZWF0dXJlR3JvdXAobGF5ZXJzOiBMYXllcltdKVxyXG4vLyBDcmVhdGUgYSBmZWF0dXJlIGdyb3VwLCBvcHRpb25hbGx5IGdpdmVuIGFuIGluaXRpYWwgc2V0IG9mIGxheWVycy5cclxuTC5mZWF0dXJlR3JvdXAgPSBmdW5jdGlvbiAobGF5ZXJzKSB7XHJcblx0cmV0dXJuIG5ldyBMLkZlYXR1cmVHcm91cChsYXllcnMpO1xyXG59O1xyXG5cblxuXG4vKlxuICogQGNsYXNzIFJlbmRlcmVyXG4gKiBAaW5oZXJpdHMgTGF5ZXJcbiAqIEBha2EgTC5SZW5kZXJlclxuICpcbiAqIEJhc2UgY2xhc3MgZm9yIHZlY3RvciByZW5kZXJlciBpbXBsZW1lbnRhdGlvbnMgKGBTVkdgLCBgQ2FudmFzYCkuIEhhbmRsZXMgdGhlXG4gKiBET00gY29udGFpbmVyIG9mIHRoZSByZW5kZXJlciwgaXRzIGJvdW5kcywgYW5kIGl0cyB6b29tIGFuaW1hdGlvbi5cbiAqXG4gKiBBIGBSZW5kZXJlcmAgd29ya3MgYXMgYW4gaW1wbGljaXQgbGF5ZXIgZ3JvdXAgZm9yIGFsbCBgUGF0aGBzIC0gdGhlIHJlbmRlcmVyXG4gKiBpdHNlbGYgY2FuIGJlIGFkZGVkIG9yIHJlbW92ZWQgdG8gdGhlIG1hcC4gQWxsIHBhdGhzIHVzZSBhIHJlbmRlcmVyLCB3aGljaCBjYW5cbiAqIGJlIGltcGxpY2l0ICh0aGUgbWFwIHdpbGwgZGVjaWRlIHRoZSB0eXBlIG9mIHJlbmRlcmVyIGFuZCB1c2UgaXQgYXV0b21hdGljYWxseSlcbiAqIG9yIGV4cGxpY2l0ICh1c2luZyB0aGUgW2ByZW5kZXJlcmBdKCNwYXRoLXJlbmRlcmVyKSBvcHRpb24gb2YgdGhlIHBhdGgpLlxuICpcbiAqIERvIG5vdCB1c2UgdGhpcyBjbGFzcyBkaXJlY3RseSwgdXNlIGBTVkdgIGFuZCBgQ2FudmFzYCBpbnN0ZWFkLlxuICpcbiAqIEBldmVudCB1cGRhdGU6IEV2ZW50XG4gKiBGaXJlZCB3aGVuIHRoZSByZW5kZXJlciB1cGRhdGVzIGl0cyBib3VuZHMsIGNlbnRlciBhbmQgem9vbSwgZm9yIGV4YW1wbGUgd2hlblxuICogaXRzIG1hcCBoYXMgbW92ZWRcbiAqL1xuXG5MLlJlbmRlcmVyID0gTC5MYXllci5leHRlbmQoe1xuXG5cdC8vIEBzZWN0aW9uXG5cdC8vIEBha2EgUmVuZGVyZXIgb3B0aW9uc1xuXHRvcHRpb25zOiB7XG5cdFx0Ly8gQG9wdGlvbiBwYWRkaW5nOiBOdW1iZXIgPSAwLjFcblx0XHQvLyBIb3cgbXVjaCB0byBleHRlbmQgdGhlIGNsaXAgYXJlYSBhcm91bmQgdGhlIG1hcCB2aWV3IChyZWxhdGl2ZSB0byBpdHMgc2l6ZSlcblx0XHQvLyBlLmcuIDAuMSB3b3VsZCBiZSAxMCUgb2YgbWFwIHZpZXcgaW4gZWFjaCBkaXJlY3Rpb25cblx0XHRwYWRkaW5nOiAwLjFcblx0fSxcblxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAob3B0aW9ucykge1xuXHRcdEwuc2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcblx0XHRMLnN0YW1wKHRoaXMpO1xuXHRcdHRoaXMuX2xheWVycyA9IHRoaXMuX2xheWVycyB8fCB7fTtcblx0fSxcblxuXHRvbkFkZDogZnVuY3Rpb24gKCkge1xuXHRcdGlmICghdGhpcy5fY29udGFpbmVyKSB7XG5cdFx0XHR0aGlzLl9pbml0Q29udGFpbmVyKCk7IC8vIGRlZmluZWQgYnkgcmVuZGVyZXIgaW1wbGVtZW50YXRpb25zXG5cblx0XHRcdGlmICh0aGlzLl96b29tQW5pbWF0ZWQpIHtcblx0XHRcdFx0TC5Eb21VdGlsLmFkZENsYXNzKHRoaXMuX2NvbnRhaW5lciwgJ2xlYWZsZXQtem9vbS1hbmltYXRlZCcpO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHRoaXMuZ2V0UGFuZSgpLmFwcGVuZENoaWxkKHRoaXMuX2NvbnRhaW5lcik7XG5cdFx0dGhpcy5fdXBkYXRlKCk7XG5cdFx0dGhpcy5vbigndXBkYXRlJywgdGhpcy5fdXBkYXRlUGF0aHMsIHRoaXMpO1xuXHR9LFxuXG5cdG9uUmVtb3ZlOiBmdW5jdGlvbiAoKSB7XG5cdFx0TC5Eb21VdGlsLnJlbW92ZSh0aGlzLl9jb250YWluZXIpO1xuXHRcdHRoaXMub2ZmKCd1cGRhdGUnLCB0aGlzLl91cGRhdGVQYXRocywgdGhpcyk7XG5cdH0sXG5cblx0Z2V0RXZlbnRzOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIGV2ZW50cyA9IHtcblx0XHRcdHZpZXdyZXNldDogdGhpcy5fcmVzZXQsXG5cdFx0XHR6b29tOiB0aGlzLl9vblpvb20sXG5cdFx0XHRtb3ZlZW5kOiB0aGlzLl91cGRhdGUsXG5cdFx0XHR6b29tZW5kOiB0aGlzLl9vblpvb21FbmRcblx0XHR9O1xuXHRcdGlmICh0aGlzLl96b29tQW5pbWF0ZWQpIHtcblx0XHRcdGV2ZW50cy56b29tYW5pbSA9IHRoaXMuX29uQW5pbVpvb207XG5cdFx0fVxuXHRcdHJldHVybiBldmVudHM7XG5cdH0sXG5cblx0X29uQW5pbVpvb206IGZ1bmN0aW9uIChldikge1xuXHRcdHRoaXMuX3VwZGF0ZVRyYW5zZm9ybShldi5jZW50ZXIsIGV2Lnpvb20pO1xuXHR9LFxuXG5cdF9vblpvb206IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl91cGRhdGVUcmFuc2Zvcm0odGhpcy5fbWFwLmdldENlbnRlcigpLCB0aGlzLl9tYXAuZ2V0Wm9vbSgpKTtcblx0fSxcblxuXHRfdXBkYXRlVHJhbnNmb3JtOiBmdW5jdGlvbiAoY2VudGVyLCB6b29tKSB7XG5cdFx0dmFyIHNjYWxlID0gdGhpcy5fbWFwLmdldFpvb21TY2FsZSh6b29tLCB0aGlzLl96b29tKSxcblx0XHQgICAgcG9zaXRpb24gPSBMLkRvbVV0aWwuZ2V0UG9zaXRpb24odGhpcy5fY29udGFpbmVyKSxcblx0XHQgICAgdmlld0hhbGYgPSB0aGlzLl9tYXAuZ2V0U2l6ZSgpLm11bHRpcGx5QnkoMC41ICsgdGhpcy5vcHRpb25zLnBhZGRpbmcpLFxuXHRcdCAgICBjdXJyZW50Q2VudGVyUG9pbnQgPSB0aGlzLl9tYXAucHJvamVjdCh0aGlzLl9jZW50ZXIsIHpvb20pLFxuXHRcdCAgICBkZXN0Q2VudGVyUG9pbnQgPSB0aGlzLl9tYXAucHJvamVjdChjZW50ZXIsIHpvb20pLFxuXHRcdCAgICBjZW50ZXJPZmZzZXQgPSBkZXN0Q2VudGVyUG9pbnQuc3VidHJhY3QoY3VycmVudENlbnRlclBvaW50KSxcblxuXHRcdCAgICB0b3BMZWZ0T2Zmc2V0ID0gdmlld0hhbGYubXVsdGlwbHlCeSgtc2NhbGUpLmFkZChwb3NpdGlvbikuYWRkKHZpZXdIYWxmKS5zdWJ0cmFjdChjZW50ZXJPZmZzZXQpO1xuXG5cdFx0aWYgKEwuQnJvd3Nlci5hbnkzZCkge1xuXHRcdFx0TC5Eb21VdGlsLnNldFRyYW5zZm9ybSh0aGlzLl9jb250YWluZXIsIHRvcExlZnRPZmZzZXQsIHNjYWxlKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0TC5Eb21VdGlsLnNldFBvc2l0aW9uKHRoaXMuX2NvbnRhaW5lciwgdG9wTGVmdE9mZnNldCk7XG5cdFx0fVxuXHR9LFxuXG5cdF9yZXNldDogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX3VwZGF0ZSgpO1xuXHRcdHRoaXMuX3VwZGF0ZVRyYW5zZm9ybSh0aGlzLl9jZW50ZXIsIHRoaXMuX3pvb20pO1xuXG5cdFx0Zm9yICh2YXIgaWQgaW4gdGhpcy5fbGF5ZXJzKSB7XG5cdFx0XHR0aGlzLl9sYXllcnNbaWRdLl9yZXNldCgpO1xuXHRcdH1cblx0fSxcblxuXHRfb25ab29tRW5kOiBmdW5jdGlvbiAoKSB7XG5cdFx0Zm9yICh2YXIgaWQgaW4gdGhpcy5fbGF5ZXJzKSB7XG5cdFx0XHR0aGlzLl9sYXllcnNbaWRdLl9wcm9qZWN0KCk7XG5cdFx0fVxuXHR9LFxuXG5cdF91cGRhdGVQYXRoczogZnVuY3Rpb24gKCkge1xuXHRcdGZvciAodmFyIGlkIGluIHRoaXMuX2xheWVycykge1xuXHRcdFx0dGhpcy5fbGF5ZXJzW2lkXS5fdXBkYXRlKCk7XG5cdFx0fVxuXHR9LFxuXG5cdF91cGRhdGU6IGZ1bmN0aW9uICgpIHtcblx0XHQvLyBVcGRhdGUgcGl4ZWwgYm91bmRzIG9mIHJlbmRlcmVyIGNvbnRhaW5lciAoZm9yIHBvc2l0aW9uaW5nL3NpemluZy9jbGlwcGluZyBsYXRlcilcblx0XHQvLyBTdWJjbGFzc2VzIGFyZSByZXNwb25zaWJsZSBvZiBmaXJpbmcgdGhlICd1cGRhdGUnIGV2ZW50LlxuXHRcdHZhciBwID0gdGhpcy5vcHRpb25zLnBhZGRpbmcsXG5cdFx0ICAgIHNpemUgPSB0aGlzLl9tYXAuZ2V0U2l6ZSgpLFxuXHRcdCAgICBtaW4gPSB0aGlzLl9tYXAuY29udGFpbmVyUG9pbnRUb0xheWVyUG9pbnQoc2l6ZS5tdWx0aXBseUJ5KC1wKSkucm91bmQoKTtcblxuXHRcdHRoaXMuX2JvdW5kcyA9IG5ldyBMLkJvdW5kcyhtaW4sIG1pbi5hZGQoc2l6ZS5tdWx0aXBseUJ5KDEgKyBwICogMikpLnJvdW5kKCkpO1xuXG5cdFx0dGhpcy5fY2VudGVyID0gdGhpcy5fbWFwLmdldENlbnRlcigpO1xuXHRcdHRoaXMuX3pvb20gPSB0aGlzLl9tYXAuZ2V0Wm9vbSgpO1xuXHR9XG59KTtcblxuXG5MLk1hcC5pbmNsdWRlKHtcblx0Ly8gQG5hbWVzcGFjZSBNYXA7IEBtZXRob2QgZ2V0UmVuZGVyZXIobGF5ZXI6IFBhdGgpOiBSZW5kZXJlclxuXHQvLyBSZXR1cm5zIHRoZSBpbnN0YW5jZSBvZiBgUmVuZGVyZXJgIHRoYXQgc2hvdWxkIGJlIHVzZWQgdG8gcmVuZGVyIHRoZSBnaXZlblxuXHQvLyBgUGF0aGAuIEl0IHdpbGwgZW5zdXJlIHRoYXQgdGhlIGByZW5kZXJlcmAgb3B0aW9ucyBvZiB0aGUgbWFwIGFuZCBwYXRoc1xuXHQvLyBhcmUgcmVzcGVjdGVkLCBhbmQgdGhhdCB0aGUgcmVuZGVyZXJzIGRvIGV4aXN0IG9uIHRoZSBtYXAuXG5cdGdldFJlbmRlcmVyOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHQvLyBAbmFtZXNwYWNlIFBhdGg7IEBvcHRpb24gcmVuZGVyZXI6IFJlbmRlcmVyXG5cdFx0Ly8gVXNlIHRoaXMgc3BlY2lmaWMgaW5zdGFuY2Ugb2YgYFJlbmRlcmVyYCBmb3IgdGhpcyBwYXRoLiBUYWtlc1xuXHRcdC8vIHByZWNlZGVuY2Ugb3ZlciB0aGUgbWFwJ3MgW2RlZmF1bHQgcmVuZGVyZXJdKCNtYXAtcmVuZGVyZXIpLlxuXHRcdHZhciByZW5kZXJlciA9IGxheWVyLm9wdGlvbnMucmVuZGVyZXIgfHwgdGhpcy5fZ2V0UGFuZVJlbmRlcmVyKGxheWVyLm9wdGlvbnMucGFuZSkgfHwgdGhpcy5vcHRpb25zLnJlbmRlcmVyIHx8IHRoaXMuX3JlbmRlcmVyO1xuXG5cdFx0aWYgKCFyZW5kZXJlcikge1xuXHRcdFx0Ly8gQG5hbWVzcGFjZSBNYXA7IEBvcHRpb24gcHJlZmVyQ2FudmFzOiBCb29sZWFuID0gZmFsc2Vcblx0XHRcdC8vIFdoZXRoZXIgYFBhdGhgcyBzaG91bGQgYmUgcmVuZGVyZWQgb24gYSBgQ2FudmFzYCByZW5kZXJlci5cblx0XHRcdC8vIEJ5IGRlZmF1bHQsIGFsbCBgUGF0aGBzIGFyZSByZW5kZXJlZCBpbiBhIGBTVkdgIHJlbmRlcmVyLlxuXHRcdFx0cmVuZGVyZXIgPSB0aGlzLl9yZW5kZXJlciA9ICh0aGlzLm9wdGlvbnMucHJlZmVyQ2FudmFzICYmIEwuY2FudmFzKCkpIHx8IEwuc3ZnKCk7XG5cdFx0fVxuXG5cdFx0aWYgKCF0aGlzLmhhc0xheWVyKHJlbmRlcmVyKSkge1xuXHRcdFx0dGhpcy5hZGRMYXllcihyZW5kZXJlcik7XG5cdFx0fVxuXHRcdHJldHVybiByZW5kZXJlcjtcblx0fSxcblxuXHRfZ2V0UGFuZVJlbmRlcmVyOiBmdW5jdGlvbiAobmFtZSkge1xuXHRcdGlmIChuYW1lID09PSAnb3ZlcmxheVBhbmUnIHx8IG5hbWUgPT09IHVuZGVmaW5lZCkge1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH1cblxuXHRcdHZhciByZW5kZXJlciA9IHRoaXMuX3BhbmVSZW5kZXJlcnNbbmFtZV07XG5cdFx0aWYgKHJlbmRlcmVyID09PSB1bmRlZmluZWQpIHtcblx0XHRcdHJlbmRlcmVyID0gKEwuU1ZHICYmIEwuc3ZnKHtwYW5lOiBuYW1lfSkpIHx8IChMLkNhbnZhcyAmJiBMLmNhbnZhcyh7cGFuZTogbmFtZX0pKTtcblx0XHRcdHRoaXMuX3BhbmVSZW5kZXJlcnNbbmFtZV0gPSByZW5kZXJlcjtcblx0XHR9XG5cdFx0cmV0dXJuIHJlbmRlcmVyO1xuXHR9XG59KTtcblxuXG5cbi8qXG4gKiBAY2xhc3MgUGF0aFxuICogQGFrYSBMLlBhdGhcbiAqIEBpbmhlcml0cyBJbnRlcmFjdGl2ZSBsYXllclxuICpcbiAqIEFuIGFic3RyYWN0IGNsYXNzIHRoYXQgY29udGFpbnMgb3B0aW9ucyBhbmQgY29uc3RhbnRzIHNoYXJlZCBiZXR3ZWVuIHZlY3RvclxuICogb3ZlcmxheXMgKFBvbHlnb24sIFBvbHlsaW5lLCBDaXJjbGUpLiBEbyBub3QgdXNlIGl0IGRpcmVjdGx5LiBFeHRlbmRzIGBMYXllcmAuXG4gKi9cblxuTC5QYXRoID0gTC5MYXllci5leHRlbmQoe1xuXG5cdC8vIEBzZWN0aW9uXG5cdC8vIEBha2EgUGF0aCBvcHRpb25zXG5cdG9wdGlvbnM6IHtcblx0XHQvLyBAb3B0aW9uIHN0cm9rZTogQm9vbGVhbiA9IHRydWVcblx0XHQvLyBXaGV0aGVyIHRvIGRyYXcgc3Ryb2tlIGFsb25nIHRoZSBwYXRoLiBTZXQgaXQgdG8gYGZhbHNlYCB0byBkaXNhYmxlIGJvcmRlcnMgb24gcG9seWdvbnMgb3IgY2lyY2xlcy5cblx0XHRzdHJva2U6IHRydWUsXG5cblx0XHQvLyBAb3B0aW9uIGNvbG9yOiBTdHJpbmcgPSAnIzMzODhmZidcblx0XHQvLyBTdHJva2UgY29sb3Jcblx0XHRjb2xvcjogJyMzMzg4ZmYnLFxuXG5cdFx0Ly8gQG9wdGlvbiB3ZWlnaHQ6IE51bWJlciA9IDNcblx0XHQvLyBTdHJva2Ugd2lkdGggaW4gcGl4ZWxzXG5cdFx0d2VpZ2h0OiAzLFxuXG5cdFx0Ly8gQG9wdGlvbiBvcGFjaXR5OiBOdW1iZXIgPSAxLjBcblx0XHQvLyBTdHJva2Ugb3BhY2l0eVxuXHRcdG9wYWNpdHk6IDEsXG5cblx0XHQvLyBAb3B0aW9uIGxpbmVDYXA6IFN0cmluZz0gJ3JvdW5kJ1xuXHRcdC8vIEEgc3RyaW5nIHRoYXQgZGVmaW5lcyBbc2hhcGUgdG8gYmUgdXNlZCBhdCB0aGUgZW5kXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9TVkcvQXR0cmlidXRlL3N0cm9rZS1saW5lY2FwKSBvZiB0aGUgc3Ryb2tlLlxuXHRcdGxpbmVDYXA6ICdyb3VuZCcsXG5cblx0XHQvLyBAb3B0aW9uIGxpbmVKb2luOiBTdHJpbmcgPSAncm91bmQnXG5cdFx0Ly8gQSBzdHJpbmcgdGhhdCBkZWZpbmVzIFtzaGFwZSB0byBiZSB1c2VkIGF0IHRoZSBjb3JuZXJzXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9TVkcvQXR0cmlidXRlL3N0cm9rZS1saW5lam9pbikgb2YgdGhlIHN0cm9rZS5cblx0XHRsaW5lSm9pbjogJ3JvdW5kJyxcblxuXHRcdC8vIEBvcHRpb24gZGFzaEFycmF5OiBTdHJpbmcgPSBudWxsXG5cdFx0Ly8gQSBzdHJpbmcgdGhhdCBkZWZpbmVzIHRoZSBzdHJva2UgW2Rhc2ggcGF0dGVybl0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvU1ZHL0F0dHJpYnV0ZS9zdHJva2UtZGFzaGFycmF5KS4gRG9lc24ndCB3b3JrIG9uIGBDYW52YXNgLXBvd2VyZWQgbGF5ZXJzIGluIFtzb21lIG9sZCBicm93c2Vyc10oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvQVBJL0NhbnZhc1JlbmRlcmluZ0NvbnRleHQyRC9zZXRMaW5lRGFzaCNCcm93c2VyX2NvbXBhdGliaWxpdHkpLlxuXHRcdGRhc2hBcnJheTogbnVsbCxcblxuXHRcdC8vIEBvcHRpb24gZGFzaE9mZnNldDogU3RyaW5nID0gbnVsbFxuXHRcdC8vIEEgc3RyaW5nIHRoYXQgZGVmaW5lcyB0aGUgW2Rpc3RhbmNlIGludG8gdGhlIGRhc2ggcGF0dGVybiB0byBzdGFydCB0aGUgZGFzaF0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvU1ZHL0F0dHJpYnV0ZS9zdHJva2UtZGFzaG9mZnNldCkuIERvZXNuJ3Qgd29yayBvbiBgQ2FudmFzYC1wb3dlcmVkIGxheWVycyBpbiBbc29tZSBvbGQgYnJvd3NlcnNdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0FQSS9DYW52YXNSZW5kZXJpbmdDb250ZXh0MkQvc2V0TGluZURhc2gjQnJvd3Nlcl9jb21wYXRpYmlsaXR5KS5cblx0XHRkYXNoT2Zmc2V0OiBudWxsLFxuXG5cdFx0Ly8gQG9wdGlvbiBmaWxsOiBCb29sZWFuID0gZGVwZW5kc1xuXHRcdC8vIFdoZXRoZXIgdG8gZmlsbCB0aGUgcGF0aCB3aXRoIGNvbG9yLiBTZXQgaXQgdG8gYGZhbHNlYCB0byBkaXNhYmxlIGZpbGxpbmcgb24gcG9seWdvbnMgb3IgY2lyY2xlcy5cblx0XHRmaWxsOiBmYWxzZSxcblxuXHRcdC8vIEBvcHRpb24gZmlsbENvbG9yOiBTdHJpbmcgPSAqXG5cdFx0Ly8gRmlsbCBjb2xvci4gRGVmYXVsdHMgdG8gdGhlIHZhbHVlIG9mIHRoZSBbYGNvbG9yYF0oI3BhdGgtY29sb3IpIG9wdGlvblxuXHRcdGZpbGxDb2xvcjogbnVsbCxcblxuXHRcdC8vIEBvcHRpb24gZmlsbE9wYWNpdHk6IE51bWJlciA9IDAuMlxuXHRcdC8vIEZpbGwgb3BhY2l0eS5cblx0XHRmaWxsT3BhY2l0eTogMC4yLFxuXG5cdFx0Ly8gQG9wdGlvbiBmaWxsUnVsZTogU3RyaW5nID0gJ2V2ZW5vZGQnXG5cdFx0Ly8gQSBzdHJpbmcgdGhhdCBkZWZpbmVzIFtob3cgdGhlIGluc2lkZSBvZiBhIHNoYXBlXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9TVkcvQXR0cmlidXRlL2ZpbGwtcnVsZSkgaXMgZGV0ZXJtaW5lZC5cblx0XHRmaWxsUnVsZTogJ2V2ZW5vZGQnLFxuXG5cdFx0Ly8gY2xhc3NOYW1lOiAnJyxcblxuXHRcdC8vIE9wdGlvbiBpbmhlcml0ZWQgZnJvbSBcIkludGVyYWN0aXZlIGxheWVyXCIgYWJzdHJhY3QgY2xhc3Ncblx0XHRpbnRlcmFjdGl2ZTogdHJ1ZVxuXHR9LFxuXG5cdGJlZm9yZUFkZDogZnVuY3Rpb24gKG1hcCkge1xuXHRcdC8vIFJlbmRlcmVyIGlzIHNldCBoZXJlIGJlY2F1c2Ugd2UgbmVlZCB0byBjYWxsIHJlbmRlcmVyLmdldEV2ZW50c1xuXHRcdC8vIGJlZm9yZSB0aGlzLmdldEV2ZW50cy5cblx0XHR0aGlzLl9yZW5kZXJlciA9IG1hcC5nZXRSZW5kZXJlcih0aGlzKTtcblx0fSxcblxuXHRvbkFkZDogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX3JlbmRlcmVyLl9pbml0UGF0aCh0aGlzKTtcblx0XHR0aGlzLl9yZXNldCgpO1xuXHRcdHRoaXMuX3JlbmRlcmVyLl9hZGRQYXRoKHRoaXMpO1xuXHR9LFxuXG5cdG9uUmVtb3ZlOiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fcmVuZGVyZXIuX3JlbW92ZVBhdGgodGhpcyk7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCByZWRyYXcoKTogdGhpc1xuXHQvLyBSZWRyYXdzIHRoZSBsYXllci4gU29tZXRpbWVzIHVzZWZ1bCBhZnRlciB5b3UgY2hhbmdlZCB0aGUgY29vcmRpbmF0ZXMgdGhhdCB0aGUgcGF0aCB1c2VzLlxuXHRyZWRyYXc6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fbWFwKSB7XG5cdFx0XHR0aGlzLl9yZW5kZXJlci5fdXBkYXRlUGF0aCh0aGlzKTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBzZXRTdHlsZShzdHlsZTogUGF0aCBvcHRpb25zKTogdGhpc1xuXHQvLyBDaGFuZ2VzIHRoZSBhcHBlYXJhbmNlIG9mIGEgUGF0aCBiYXNlZCBvbiB0aGUgb3B0aW9ucyBpbiB0aGUgYFBhdGggb3B0aW9uc2Agb2JqZWN0LlxuXHRzZXRTdHlsZTogZnVuY3Rpb24gKHN0eWxlKSB7XG5cdFx0TC5zZXRPcHRpb25zKHRoaXMsIHN0eWxlKTtcblx0XHRpZiAodGhpcy5fcmVuZGVyZXIpIHtcblx0XHRcdHRoaXMuX3JlbmRlcmVyLl91cGRhdGVTdHlsZSh0aGlzKTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBicmluZ1RvRnJvbnQoKTogdGhpc1xuXHQvLyBCcmluZ3MgdGhlIGxheWVyIHRvIHRoZSB0b3Agb2YgYWxsIHBhdGggbGF5ZXJzLlxuXHRicmluZ1RvRnJvbnQ6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fcmVuZGVyZXIpIHtcblx0XHRcdHRoaXMuX3JlbmRlcmVyLl9icmluZ1RvRnJvbnQodGhpcyk7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgYnJpbmdUb0JhY2soKTogdGhpc1xuXHQvLyBCcmluZ3MgdGhlIGxheWVyIHRvIHRoZSBib3R0b20gb2YgYWxsIHBhdGggbGF5ZXJzLlxuXHRicmluZ1RvQmFjazogZnVuY3Rpb24gKCkge1xuXHRcdGlmICh0aGlzLl9yZW5kZXJlcikge1xuXHRcdFx0dGhpcy5fcmVuZGVyZXIuX2JyaW5nVG9CYWNrKHRoaXMpO1xuXHRcdH1cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHRnZXRFbGVtZW50OiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX3BhdGg7XG5cdH0sXG5cblx0X3Jlc2V0OiBmdW5jdGlvbiAoKSB7XG5cdFx0Ly8gZGVmaW5lZCBpbiBjaGlsZHJlbiBjbGFzc2VzXG5cdFx0dGhpcy5fcHJvamVjdCgpO1xuXHRcdHRoaXMuX3VwZGF0ZSgpO1xuXHR9LFxuXG5cdF9jbGlja1RvbGVyYW5jZTogZnVuY3Rpb24gKCkge1xuXHRcdC8vIHVzZWQgd2hlbiBkb2luZyBoaXQgZGV0ZWN0aW9uIGZvciBDYW52YXMgbGF5ZXJzXG5cdFx0cmV0dXJuICh0aGlzLm9wdGlvbnMuc3Ryb2tlID8gdGhpcy5vcHRpb25zLndlaWdodCAvIDIgOiAwKSArIChMLkJyb3dzZXIudG91Y2ggPyAxMCA6IDApO1xuXHR9XG59KTtcblxuXG5cbi8qXHJcbiAqIEBuYW1lc3BhY2UgTGluZVV0aWxcclxuICpcclxuICogVmFyaW91cyB1dGlsaXR5IGZ1bmN0aW9ucyBmb3IgcG9seWluZSBwb2ludHMgcHJvY2Vzc2luZywgdXNlZCBieSBMZWFmbGV0IGludGVybmFsbHkgdG8gbWFrZSBwb2x5bGluZXMgbGlnaHRuaW5nLWZhc3QuXHJcbiAqL1xyXG5cclxuTC5MaW5lVXRpbCA9IHtcclxuXHJcblx0Ly8gU2ltcGxpZnkgcG9seWxpbmUgd2l0aCB2ZXJ0ZXggcmVkdWN0aW9uIGFuZCBEb3VnbGFzLVBldWNrZXIgc2ltcGxpZmljYXRpb24uXHJcblx0Ly8gSW1wcm92ZXMgcmVuZGVyaW5nIHBlcmZvcm1hbmNlIGRyYW1hdGljYWxseSBieSBsZXNzZW5pbmcgdGhlIG51bWJlciBvZiBwb2ludHMgdG8gZHJhdy5cclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHNpbXBsaWZ5KHBvaW50czogUG9pbnRbXSwgdG9sZXJhbmNlOiBOdW1iZXIpOiBQb2ludFtdXHJcblx0Ly8gRHJhbWF0aWNhbGx5IHJlZHVjZXMgdGhlIG51bWJlciBvZiBwb2ludHMgaW4gYSBwb2x5bGluZSB3aGlsZSByZXRhaW5pbmdcclxuXHQvLyBpdHMgc2hhcGUgYW5kIHJldHVybnMgYSBuZXcgYXJyYXkgb2Ygc2ltcGxpZmllZCBwb2ludHMsIHVzaW5nIHRoZVxyXG5cdC8vIFtEb3VnbGFzLVBldWNrZXIgYWxnb3JpdGhtXShodHRwOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0RvdWdsYXMtUGV1Y2tlcl9hbGdvcml0aG0pLlxyXG5cdC8vIFVzZWQgZm9yIGEgaHVnZSBwZXJmb3JtYW5jZSBib29zdCB3aGVuIHByb2Nlc3NpbmcvZGlzcGxheWluZyBMZWFmbGV0IHBvbHlsaW5lcyBmb3JcclxuXHQvLyBlYWNoIHpvb20gbGV2ZWwgYW5kIGFsc28gcmVkdWNpbmcgdmlzdWFsIG5vaXNlLiB0b2xlcmFuY2UgYWZmZWN0cyB0aGUgYW1vdW50IG9mXHJcblx0Ly8gc2ltcGxpZmljYXRpb24gKGxlc3NlciB2YWx1ZSBtZWFucyBoaWdoZXIgcXVhbGl0eSBidXQgc2xvd2VyIGFuZCB3aXRoIG1vcmUgcG9pbnRzKS5cclxuXHQvLyBBbHNvIHJlbGVhc2VkIGFzIGEgc2VwYXJhdGVkIG1pY3JvLWxpYnJhcnkgW1NpbXBsaWZ5LmpzXShodHRwOi8vbW91cm5lci5naXRodWIuY29tL3NpbXBsaWZ5LWpzLykuXHJcblx0c2ltcGxpZnk6IGZ1bmN0aW9uIChwb2ludHMsIHRvbGVyYW5jZSkge1xyXG5cdFx0aWYgKCF0b2xlcmFuY2UgfHwgIXBvaW50cy5sZW5ndGgpIHtcclxuXHRcdFx0cmV0dXJuIHBvaW50cy5zbGljZSgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBzcVRvbGVyYW5jZSA9IHRvbGVyYW5jZSAqIHRvbGVyYW5jZTtcclxuXHJcblx0XHQvLyBzdGFnZSAxOiB2ZXJ0ZXggcmVkdWN0aW9uXHJcblx0XHRwb2ludHMgPSB0aGlzLl9yZWR1Y2VQb2ludHMocG9pbnRzLCBzcVRvbGVyYW5jZSk7XHJcblxyXG5cdFx0Ly8gc3RhZ2UgMjogRG91Z2xhcy1QZXVja2VyIHNpbXBsaWZpY2F0aW9uXHJcblx0XHRwb2ludHMgPSB0aGlzLl9zaW1wbGlmeURQKHBvaW50cywgc3FUb2xlcmFuY2UpO1xyXG5cclxuXHRcdHJldHVybiBwb2ludHM7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHBvaW50VG9TZWdtZW50RGlzdGFuY2UocDogUG9pbnQsIHAxOiBQb2ludCwgcDI6IFBvaW50KTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgZGlzdGFuY2UgYmV0d2VlbiBwb2ludCBgcGAgYW5kIHNlZ21lbnQgYHAxYCB0byBgcDJgLlxyXG5cdHBvaW50VG9TZWdtZW50RGlzdGFuY2U6ICBmdW5jdGlvbiAocCwgcDEsIHAyKSB7XHJcblx0XHRyZXR1cm4gTWF0aC5zcXJ0KHRoaXMuX3NxQ2xvc2VzdFBvaW50T25TZWdtZW50KHAsIHAxLCBwMiwgdHJ1ZSkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBjbG9zZXN0UG9pbnRPblNlZ21lbnQocDogUG9pbnQsIHAxOiBQb2ludCwgcDI6IFBvaW50KTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgY2xvc2VzdCBwb2ludCBmcm9tIGEgcG9pbnQgYHBgIG9uIGEgc2VnbWVudCBgcDFgIHRvIGBwMmAuXHJcblx0Y2xvc2VzdFBvaW50T25TZWdtZW50OiBmdW5jdGlvbiAocCwgcDEsIHAyKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fc3FDbG9zZXN0UG9pbnRPblNlZ21lbnQocCwgcDEsIHAyKTtcclxuXHR9LFxyXG5cclxuXHQvLyBEb3VnbGFzLVBldWNrZXIgc2ltcGxpZmljYXRpb24sIHNlZSBodHRwOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0RvdWdsYXMtUGV1Y2tlcl9hbGdvcml0aG1cclxuXHRfc2ltcGxpZnlEUDogZnVuY3Rpb24gKHBvaW50cywgc3FUb2xlcmFuY2UpIHtcclxuXHJcblx0XHR2YXIgbGVuID0gcG9pbnRzLmxlbmd0aCxcclxuXHRcdCAgICBBcnJheUNvbnN0cnVjdG9yID0gdHlwZW9mIFVpbnQ4QXJyYXkgIT09IHVuZGVmaW5lZCArICcnID8gVWludDhBcnJheSA6IEFycmF5LFxyXG5cdFx0ICAgIG1hcmtlcnMgPSBuZXcgQXJyYXlDb25zdHJ1Y3RvcihsZW4pO1xyXG5cclxuXHRcdG1hcmtlcnNbMF0gPSBtYXJrZXJzW2xlbiAtIDFdID0gMTtcclxuXHJcblx0XHR0aGlzLl9zaW1wbGlmeURQU3RlcChwb2ludHMsIG1hcmtlcnMsIHNxVG9sZXJhbmNlLCAwLCBsZW4gLSAxKTtcclxuXHJcblx0XHR2YXIgaSxcclxuXHRcdCAgICBuZXdQb2ludHMgPSBbXTtcclxuXHJcblx0XHRmb3IgKGkgPSAwOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0aWYgKG1hcmtlcnNbaV0pIHtcclxuXHRcdFx0XHRuZXdQb2ludHMucHVzaChwb2ludHNbaV0pO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIG5ld1BvaW50cztcclxuXHR9LFxyXG5cclxuXHRfc2ltcGxpZnlEUFN0ZXA6IGZ1bmN0aW9uIChwb2ludHMsIG1hcmtlcnMsIHNxVG9sZXJhbmNlLCBmaXJzdCwgbGFzdCkge1xyXG5cclxuXHRcdHZhciBtYXhTcURpc3QgPSAwLFxyXG5cdFx0ICAgIGluZGV4LCBpLCBzcURpc3Q7XHJcblxyXG5cdFx0Zm9yIChpID0gZmlyc3QgKyAxOyBpIDw9IGxhc3QgLSAxOyBpKyspIHtcclxuXHRcdFx0c3FEaXN0ID0gdGhpcy5fc3FDbG9zZXN0UG9pbnRPblNlZ21lbnQocG9pbnRzW2ldLCBwb2ludHNbZmlyc3RdLCBwb2ludHNbbGFzdF0sIHRydWUpO1xyXG5cclxuXHRcdFx0aWYgKHNxRGlzdCA+IG1heFNxRGlzdCkge1xyXG5cdFx0XHRcdGluZGV4ID0gaTtcclxuXHRcdFx0XHRtYXhTcURpc3QgPSBzcURpc3Q7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHRpZiAobWF4U3FEaXN0ID4gc3FUb2xlcmFuY2UpIHtcclxuXHRcdFx0bWFya2Vyc1tpbmRleF0gPSAxO1xyXG5cclxuXHRcdFx0dGhpcy5fc2ltcGxpZnlEUFN0ZXAocG9pbnRzLCBtYXJrZXJzLCBzcVRvbGVyYW5jZSwgZmlyc3QsIGluZGV4KTtcclxuXHRcdFx0dGhpcy5fc2ltcGxpZnlEUFN0ZXAocG9pbnRzLCBtYXJrZXJzLCBzcVRvbGVyYW5jZSwgaW5kZXgsIGxhc3QpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIHJlZHVjZSBwb2ludHMgdGhhdCBhcmUgdG9vIGNsb3NlIHRvIGVhY2ggb3RoZXIgdG8gYSBzaW5nbGUgcG9pbnRcclxuXHRfcmVkdWNlUG9pbnRzOiBmdW5jdGlvbiAocG9pbnRzLCBzcVRvbGVyYW5jZSkge1xyXG5cdFx0dmFyIHJlZHVjZWRQb2ludHMgPSBbcG9pbnRzWzBdXTtcclxuXHJcblx0XHRmb3IgKHZhciBpID0gMSwgcHJldiA9IDAsIGxlbiA9IHBvaW50cy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRpZiAodGhpcy5fc3FEaXN0KHBvaW50c1tpXSwgcG9pbnRzW3ByZXZdKSA+IHNxVG9sZXJhbmNlKSB7XHJcblx0XHRcdFx0cmVkdWNlZFBvaW50cy5wdXNoKHBvaW50c1tpXSk7XHJcblx0XHRcdFx0cHJldiA9IGk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHRcdGlmIChwcmV2IDwgbGVuIC0gMSkge1xyXG5cdFx0XHRyZWR1Y2VkUG9pbnRzLnB1c2gocG9pbnRzW2xlbiAtIDFdKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiByZWR1Y2VkUG9pbnRzO1xyXG5cdH0sXHJcblxyXG5cclxuXHQvLyBAZnVuY3Rpb24gY2xpcFNlZ21lbnQoYTogUG9pbnQsIGI6IFBvaW50LCBib3VuZHM6IEJvdW5kcywgdXNlTGFzdENvZGU/OiBCb29sZWFuLCByb3VuZD86IEJvb2xlYW4pOiBQb2ludFtdfEJvb2xlYW5cclxuXHQvLyBDbGlwcyB0aGUgc2VnbWVudCBhIHRvIGIgYnkgcmVjdGFuZ3VsYXIgYm91bmRzIHdpdGggdGhlXHJcblx0Ly8gW0NvaGVuLVN1dGhlcmxhbmQgYWxnb3JpdGhtXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Db2hlbiVFMiU4MCU5M1N1dGhlcmxhbmRfYWxnb3JpdGhtKVxyXG5cdC8vIChtb2RpZnlpbmcgdGhlIHNlZ21lbnQgcG9pbnRzIGRpcmVjdGx5ISkuIFVzZWQgYnkgTGVhZmxldCB0byBvbmx5IHNob3cgcG9seWxpbmVcclxuXHQvLyBwb2ludHMgdGhhdCBhcmUgb24gdGhlIHNjcmVlbiBvciBuZWFyLCBpbmNyZWFzaW5nIHBlcmZvcm1hbmNlLlxyXG5cdGNsaXBTZWdtZW50OiBmdW5jdGlvbiAoYSwgYiwgYm91bmRzLCB1c2VMYXN0Q29kZSwgcm91bmQpIHtcclxuXHRcdHZhciBjb2RlQSA9IHVzZUxhc3RDb2RlID8gdGhpcy5fbGFzdENvZGUgOiB0aGlzLl9nZXRCaXRDb2RlKGEsIGJvdW5kcyksXHJcblx0XHQgICAgY29kZUIgPSB0aGlzLl9nZXRCaXRDb2RlKGIsIGJvdW5kcyksXHJcblxyXG5cdFx0ICAgIGNvZGVPdXQsIHAsIG5ld0NvZGU7XHJcblxyXG5cdFx0Ly8gc2F2ZSAybmQgY29kZSB0byBhdm9pZCBjYWxjdWxhdGluZyBpdCBvbiB0aGUgbmV4dCBzZWdtZW50XHJcblx0XHR0aGlzLl9sYXN0Q29kZSA9IGNvZGVCO1xyXG5cclxuXHRcdHdoaWxlICh0cnVlKSB7XHJcblx0XHRcdC8vIGlmIGEsYiBpcyBpbnNpZGUgdGhlIGNsaXAgd2luZG93ICh0cml2aWFsIGFjY2VwdClcclxuXHRcdFx0aWYgKCEoY29kZUEgfCBjb2RlQikpIHtcclxuXHRcdFx0XHRyZXR1cm4gW2EsIGJdO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHQvLyBpZiBhLGIgaXMgb3V0c2lkZSB0aGUgY2xpcCB3aW5kb3cgKHRyaXZpYWwgcmVqZWN0KVxyXG5cdFx0XHRpZiAoY29kZUEgJiBjb2RlQikge1xyXG5cdFx0XHRcdHJldHVybiBmYWxzZTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Ly8gb3RoZXIgY2FzZXNcclxuXHRcdFx0Y29kZU91dCA9IGNvZGVBIHx8IGNvZGVCO1xyXG5cdFx0XHRwID0gdGhpcy5fZ2V0RWRnZUludGVyc2VjdGlvbihhLCBiLCBjb2RlT3V0LCBib3VuZHMsIHJvdW5kKTtcclxuXHRcdFx0bmV3Q29kZSA9IHRoaXMuX2dldEJpdENvZGUocCwgYm91bmRzKTtcclxuXHJcblx0XHRcdGlmIChjb2RlT3V0ID09PSBjb2RlQSkge1xyXG5cdFx0XHRcdGEgPSBwO1xyXG5cdFx0XHRcdGNvZGVBID0gbmV3Q29kZTtcclxuXHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHRiID0gcDtcclxuXHRcdFx0XHRjb2RlQiA9IG5ld0NvZGU7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfZ2V0RWRnZUludGVyc2VjdGlvbjogZnVuY3Rpb24gKGEsIGIsIGNvZGUsIGJvdW5kcywgcm91bmQpIHtcclxuXHRcdHZhciBkeCA9IGIueCAtIGEueCxcclxuXHRcdCAgICBkeSA9IGIueSAtIGEueSxcclxuXHRcdCAgICBtaW4gPSBib3VuZHMubWluLFxyXG5cdFx0ICAgIG1heCA9IGJvdW5kcy5tYXgsXHJcblx0XHQgICAgeCwgeTtcclxuXHJcblx0XHRpZiAoY29kZSAmIDgpIHsgLy8gdG9wXHJcblx0XHRcdHggPSBhLnggKyBkeCAqIChtYXgueSAtIGEueSkgLyBkeTtcclxuXHRcdFx0eSA9IG1heC55O1xyXG5cclxuXHRcdH0gZWxzZSBpZiAoY29kZSAmIDQpIHsgLy8gYm90dG9tXHJcblx0XHRcdHggPSBhLnggKyBkeCAqIChtaW4ueSAtIGEueSkgLyBkeTtcclxuXHRcdFx0eSA9IG1pbi55O1xyXG5cclxuXHRcdH0gZWxzZSBpZiAoY29kZSAmIDIpIHsgLy8gcmlnaHRcclxuXHRcdFx0eCA9IG1heC54O1xyXG5cdFx0XHR5ID0gYS55ICsgZHkgKiAobWF4LnggLSBhLngpIC8gZHg7XHJcblxyXG5cdFx0fSBlbHNlIGlmIChjb2RlICYgMSkgeyAvLyBsZWZ0XHJcblx0XHRcdHggPSBtaW4ueDtcclxuXHRcdFx0eSA9IGEueSArIGR5ICogKG1pbi54IC0gYS54KSAvIGR4O1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiBuZXcgTC5Qb2ludCh4LCB5LCByb3VuZCk7XHJcblx0fSxcclxuXHJcblx0X2dldEJpdENvZGU6IGZ1bmN0aW9uIChwLCBib3VuZHMpIHtcclxuXHRcdHZhciBjb2RlID0gMDtcclxuXHJcblx0XHRpZiAocC54IDwgYm91bmRzLm1pbi54KSB7IC8vIGxlZnRcclxuXHRcdFx0Y29kZSB8PSAxO1xyXG5cdFx0fSBlbHNlIGlmIChwLnggPiBib3VuZHMubWF4LngpIHsgLy8gcmlnaHRcclxuXHRcdFx0Y29kZSB8PSAyO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmIChwLnkgPCBib3VuZHMubWluLnkpIHsgLy8gYm90dG9tXHJcblx0XHRcdGNvZGUgfD0gNDtcclxuXHRcdH0gZWxzZSBpZiAocC55ID4gYm91bmRzLm1heC55KSB7IC8vIHRvcFxyXG5cdFx0XHRjb2RlIHw9IDg7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIGNvZGU7XHJcblx0fSxcclxuXHJcblx0Ly8gc3F1YXJlIGRpc3RhbmNlICh0byBhdm9pZCB1bm5lY2Vzc2FyeSBNYXRoLnNxcnQgY2FsbHMpXHJcblx0X3NxRGlzdDogZnVuY3Rpb24gKHAxLCBwMikge1xyXG5cdFx0dmFyIGR4ID0gcDIueCAtIHAxLngsXHJcblx0XHQgICAgZHkgPSBwMi55IC0gcDEueTtcclxuXHRcdHJldHVybiBkeCAqIGR4ICsgZHkgKiBkeTtcclxuXHR9LFxyXG5cclxuXHQvLyByZXR1cm4gY2xvc2VzdCBwb2ludCBvbiBzZWdtZW50IG9yIGRpc3RhbmNlIHRvIHRoYXQgcG9pbnRcclxuXHRfc3FDbG9zZXN0UG9pbnRPblNlZ21lbnQ6IGZ1bmN0aW9uIChwLCBwMSwgcDIsIHNxRGlzdCkge1xyXG5cdFx0dmFyIHggPSBwMS54LFxyXG5cdFx0ICAgIHkgPSBwMS55LFxyXG5cdFx0ICAgIGR4ID0gcDIueCAtIHgsXHJcblx0XHQgICAgZHkgPSBwMi55IC0geSxcclxuXHRcdCAgICBkb3QgPSBkeCAqIGR4ICsgZHkgKiBkeSxcclxuXHRcdCAgICB0O1xyXG5cclxuXHRcdGlmIChkb3QgPiAwKSB7XHJcblx0XHRcdHQgPSAoKHAueCAtIHgpICogZHggKyAocC55IC0geSkgKiBkeSkgLyBkb3Q7XHJcblxyXG5cdFx0XHRpZiAodCA+IDEpIHtcclxuXHRcdFx0XHR4ID0gcDIueDtcclxuXHRcdFx0XHR5ID0gcDIueTtcclxuXHRcdFx0fSBlbHNlIGlmICh0ID4gMCkge1xyXG5cdFx0XHRcdHggKz0gZHggKiB0O1xyXG5cdFx0XHRcdHkgKz0gZHkgKiB0O1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0ZHggPSBwLnggLSB4O1xyXG5cdFx0ZHkgPSBwLnkgLSB5O1xyXG5cclxuXHRcdHJldHVybiBzcURpc3QgPyBkeCAqIGR4ICsgZHkgKiBkeSA6IG5ldyBMLlBvaW50KHgsIHkpO1xyXG5cdH1cclxufTtcclxuXG5cblxuLypcbiAqIEBjbGFzcyBQb2x5bGluZVxuICogQGFrYSBMLlBvbHlsaW5lXG4gKiBAaW5oZXJpdHMgUGF0aFxuICpcbiAqIEEgY2xhc3MgZm9yIGRyYXdpbmcgcG9seWxpbmUgb3ZlcmxheXMgb24gYSBtYXAuIEV4dGVuZHMgYFBhdGhgLlxuICpcbiAqIEBleGFtcGxlXG4gKlxuICogYGBganNcbiAqIC8vIGNyZWF0ZSBhIHJlZCBwb2x5bGluZSBmcm9tIGFuIGFycmF5IG9mIExhdExuZyBwb2ludHNcbiAqIHZhciBsYXRsbmdzID0gW1xuICogXHRbLTEyMi42OCwgNDUuNTFdLFxuICogXHRbLTEyMi40MywgMzcuNzddLFxuICogXHRbLTExOC4yLCAzNC4wNF1cbiAqIF07XG4gKlxuICogdmFyIHBvbHlsaW5lID0gTC5wb2x5bGluZShsYXRsbmdzLCB7Y29sb3I6ICdyZWQnfSkuYWRkVG8obWFwKTtcbiAqXG4gKiAvLyB6b29tIHRoZSBtYXAgdG8gdGhlIHBvbHlsaW5lXG4gKiBtYXAuZml0Qm91bmRzKHBvbHlsaW5lLmdldEJvdW5kcygpKTtcbiAqIGBgYFxuICpcbiAqIFlvdSBjYW4gYWxzbyBwYXNzIGEgbXVsdGktZGltZW5zaW9uYWwgYXJyYXkgdG8gcmVwcmVzZW50IGEgYE11bHRpUG9seWxpbmVgIHNoYXBlOlxuICpcbiAqIGBgYGpzXG4gKiAvLyBjcmVhdGUgYSByZWQgcG9seWxpbmUgZnJvbSBhbiBhcnJheSBvZiBhcnJheXMgb2YgTGF0TG5nIHBvaW50c1xuICogdmFyIGxhdGxuZ3MgPSBbXG4gKiBcdFtbLTEyMi42OCwgNDUuNTFdLFxuICogXHQgWy0xMjIuNDMsIDM3Ljc3XSxcbiAqIFx0IFstMTE4LjIsIDM0LjA0XV0sXG4gKiBcdFtbLTczLjkxLCA0MC43OF0sXG4gKiBcdCBbLTg3LjYyLCA0MS44M10sXG4gKiBcdCBbLTk2LjcyLCAzMi43Nl1dXG4gKiBdO1xuICogYGBgXG4gKi9cblxuTC5Qb2x5bGluZSA9IEwuUGF0aC5leHRlbmQoe1xuXG5cdC8vIEBzZWN0aW9uXG5cdC8vIEBha2EgUG9seWxpbmUgb3B0aW9uc1xuXHRvcHRpb25zOiB7XG5cdFx0Ly8gQG9wdGlvbiBzbW9vdGhGYWN0b3I6IE51bWJlciA9IDEuMFxuXHRcdC8vIEhvdyBtdWNoIHRvIHNpbXBsaWZ5IHRoZSBwb2x5bGluZSBvbiBlYWNoIHpvb20gbGV2ZWwuIE1vcmUgbWVhbnNcblx0XHQvLyBiZXR0ZXIgcGVyZm9ybWFuY2UgYW5kIHNtb290aGVyIGxvb2ssIGFuZCBsZXNzIG1lYW5zIG1vcmUgYWNjdXJhdGUgcmVwcmVzZW50YXRpb24uXG5cdFx0c21vb3RoRmFjdG9yOiAxLjAsXG5cblx0XHQvLyBAb3B0aW9uIG5vQ2xpcDogQm9vbGVhbiA9IGZhbHNlXG5cdFx0Ly8gRGlzYWJsZSBwb2x5bGluZSBjbGlwcGluZy5cblx0XHRub0NsaXA6IGZhbHNlXG5cdH0sXG5cblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKGxhdGxuZ3MsIG9wdGlvbnMpIHtcblx0XHRMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XG5cdFx0dGhpcy5fc2V0TGF0TG5ncyhsYXRsbmdzKTtcblx0fSxcblxuXHQvLyBAbWV0aG9kIGdldExhdExuZ3MoKTogTGF0TG5nW11cblx0Ly8gUmV0dXJucyBhbiBhcnJheSBvZiB0aGUgcG9pbnRzIGluIHRoZSBwYXRoLCBvciBuZXN0ZWQgYXJyYXlzIG9mIHBvaW50cyBpbiBjYXNlIG9mIG11bHRpLXBvbHlsaW5lLlxuXHRnZXRMYXRMbmdzOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX2xhdGxuZ3M7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBzZXRMYXRMbmdzKGxhdGxuZ3M6IExhdExuZ1tdKTogdGhpc1xuXHQvLyBSZXBsYWNlcyBhbGwgdGhlIHBvaW50cyBpbiB0aGUgcG9seWxpbmUgd2l0aCB0aGUgZ2l2ZW4gYXJyYXkgb2YgZ2VvZ3JhcGhpY2FsIHBvaW50cy5cblx0c2V0TGF0TG5nczogZnVuY3Rpb24gKGxhdGxuZ3MpIHtcblx0XHR0aGlzLl9zZXRMYXRMbmdzKGxhdGxuZ3MpO1xuXHRcdHJldHVybiB0aGlzLnJlZHJhdygpO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgaXNFbXB0eSgpOiBCb29sZWFuXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSBQb2x5bGluZSBoYXMgbm8gTGF0TG5ncy5cblx0aXNFbXB0eTogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiAhdGhpcy5fbGF0bG5ncy5sZW5ndGg7XG5cdH0sXG5cblx0Y2xvc2VzdExheWVyUG9pbnQ6IGZ1bmN0aW9uIChwKSB7XG5cdFx0dmFyIG1pbkRpc3RhbmNlID0gSW5maW5pdHksXG5cdFx0ICAgIG1pblBvaW50ID0gbnVsbCxcblx0XHQgICAgY2xvc2VzdCA9IEwuTGluZVV0aWwuX3NxQ2xvc2VzdFBvaW50T25TZWdtZW50LFxuXHRcdCAgICBwMSwgcDI7XG5cblx0XHRmb3IgKHZhciBqID0gMCwgakxlbiA9IHRoaXMuX3BhcnRzLmxlbmd0aDsgaiA8IGpMZW47IGorKykge1xuXHRcdFx0dmFyIHBvaW50cyA9IHRoaXMuX3BhcnRzW2pdO1xuXG5cdFx0XHRmb3IgKHZhciBpID0gMSwgbGVuID0gcG9pbnRzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRcdHAxID0gcG9pbnRzW2kgLSAxXTtcblx0XHRcdFx0cDIgPSBwb2ludHNbaV07XG5cblx0XHRcdFx0dmFyIHNxRGlzdCA9IGNsb3Nlc3QocCwgcDEsIHAyLCB0cnVlKTtcblxuXHRcdFx0XHRpZiAoc3FEaXN0IDwgbWluRGlzdGFuY2UpIHtcblx0XHRcdFx0XHRtaW5EaXN0YW5jZSA9IHNxRGlzdDtcblx0XHRcdFx0XHRtaW5Qb2ludCA9IGNsb3Nlc3QocCwgcDEsIHAyKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0XHRpZiAobWluUG9pbnQpIHtcblx0XHRcdG1pblBvaW50LmRpc3RhbmNlID0gTWF0aC5zcXJ0KG1pbkRpc3RhbmNlKTtcblx0XHR9XG5cdFx0cmV0dXJuIG1pblBvaW50O1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgZ2V0Q2VudGVyKCk6IExhdExuZ1xuXHQvLyBSZXR1cm5zIHRoZSBjZW50ZXIgKFtjZW50cm9pZF0oaHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9DZW50cm9pZCkpIG9mIHRoZSBwb2x5bGluZS5cblx0Z2V0Q2VudGVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0Ly8gdGhyb3dzIGVycm9yIHdoZW4gbm90IHlldCBhZGRlZCB0byBtYXAgYXMgdGhpcyBjZW50ZXIgY2FsY3VsYXRpb24gcmVxdWlyZXMgcHJvamVjdGVkIGNvb3JkaW5hdGVzXG5cdFx0aWYgKCF0aGlzLl9tYXApIHtcblx0XHRcdHRocm93IG5ldyBFcnJvcignTXVzdCBhZGQgbGF5ZXIgdG8gbWFwIGJlZm9yZSB1c2luZyBnZXRDZW50ZXIoKScpO1xuXHRcdH1cblxuXHRcdHZhciBpLCBoYWxmRGlzdCwgc2VnRGlzdCwgZGlzdCwgcDEsIHAyLCByYXRpbyxcblx0XHQgICAgcG9pbnRzID0gdGhpcy5fcmluZ3NbMF0sXG5cdFx0ICAgIGxlbiA9IHBvaW50cy5sZW5ndGg7XG5cblx0XHRpZiAoIWxlbikgeyByZXR1cm4gbnVsbDsgfVxuXG5cdFx0Ly8gcG9seWxpbmUgY2VudHJvaWQgYWxnb3JpdGhtOyBvbmx5IHVzZXMgdGhlIGZpcnN0IHJpbmcgaWYgdGhlcmUgYXJlIG11bHRpcGxlXG5cblx0XHRmb3IgKGkgPSAwLCBoYWxmRGlzdCA9IDA7IGkgPCBsZW4gLSAxOyBpKyspIHtcblx0XHRcdGhhbGZEaXN0ICs9IHBvaW50c1tpXS5kaXN0YW5jZVRvKHBvaW50c1tpICsgMV0pIC8gMjtcblx0XHR9XG5cblx0XHQvLyBUaGUgbGluZSBpcyBzbyBzbWFsbCBpbiB0aGUgY3VycmVudCB2aWV3IHRoYXQgYWxsIHBvaW50cyBhcmUgb24gdGhlIHNhbWUgcGl4ZWwuXG5cdFx0aWYgKGhhbGZEaXN0ID09PSAwKSB7XG5cdFx0XHRyZXR1cm4gdGhpcy5fbWFwLmxheWVyUG9pbnRUb0xhdExuZyhwb2ludHNbMF0pO1xuXHRcdH1cblxuXHRcdGZvciAoaSA9IDAsIGRpc3QgPSAwOyBpIDwgbGVuIC0gMTsgaSsrKSB7XG5cdFx0XHRwMSA9IHBvaW50c1tpXTtcblx0XHRcdHAyID0gcG9pbnRzW2kgKyAxXTtcblx0XHRcdHNlZ0Rpc3QgPSBwMS5kaXN0YW5jZVRvKHAyKTtcblx0XHRcdGRpc3QgKz0gc2VnRGlzdDtcblxuXHRcdFx0aWYgKGRpc3QgPiBoYWxmRGlzdCkge1xuXHRcdFx0XHRyYXRpbyA9IChkaXN0IC0gaGFsZkRpc3QpIC8gc2VnRGlzdDtcblx0XHRcdFx0cmV0dXJuIHRoaXMuX21hcC5sYXllclBvaW50VG9MYXRMbmcoW1xuXHRcdFx0XHRcdHAyLnggLSByYXRpbyAqIChwMi54IC0gcDEueCksXG5cdFx0XHRcdFx0cDIueSAtIHJhdGlvICogKHAyLnkgLSBwMS55KVxuXHRcdFx0XHRdKTtcblx0XHRcdH1cblx0XHR9XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBnZXRCb3VuZHMoKTogTGF0TG5nQm91bmRzXG5cdC8vIFJldHVybnMgdGhlIGBMYXRMbmdCb3VuZHNgIG9mIHRoZSBwYXRoLlxuXHRnZXRCb3VuZHM6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5fYm91bmRzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgYWRkTGF0TG5nKGxhdGxuZzogTGF0TG5nLCBsYXRsbmdzPyBMYXRMbmdbXSk6IHRoaXNcblx0Ly8gQWRkcyBhIGdpdmVuIHBvaW50IHRvIHRoZSBwb2x5bGluZS4gQnkgZGVmYXVsdCwgYWRkcyB0byB0aGUgZmlyc3QgcmluZyBvZlxuXHQvLyB0aGUgcG9seWxpbmUgaW4gY2FzZSBvZiBhIG11bHRpLXBvbHlsaW5lLCBidXQgY2FuIGJlIG92ZXJyaWRkZW4gYnkgcGFzc2luZ1xuXHQvLyBhIHNwZWNpZmljIHJpbmcgYXMgYSBMYXRMbmcgYXJyYXkgKHRoYXQgeW91IGNhbiBlYXJsaWVyIGFjY2VzcyB3aXRoIFtgZ2V0TGF0TG5nc2BdKCNwb2x5bGluZS1nZXRsYXRsbmdzKSkuXG5cdGFkZExhdExuZzogZnVuY3Rpb24gKGxhdGxuZywgbGF0bG5ncykge1xuXHRcdGxhdGxuZ3MgPSBsYXRsbmdzIHx8IHRoaXMuX2RlZmF1bHRTaGFwZSgpO1xuXHRcdGxhdGxuZyA9IEwubGF0TG5nKGxhdGxuZyk7XG5cdFx0bGF0bG5ncy5wdXNoKGxhdGxuZyk7XG5cdFx0dGhpcy5fYm91bmRzLmV4dGVuZChsYXRsbmcpO1xuXHRcdHJldHVybiB0aGlzLnJlZHJhdygpO1xuXHR9LFxuXG5cdF9zZXRMYXRMbmdzOiBmdW5jdGlvbiAobGF0bG5ncykge1xuXHRcdHRoaXMuX2JvdW5kcyA9IG5ldyBMLkxhdExuZ0JvdW5kcygpO1xuXHRcdHRoaXMuX2xhdGxuZ3MgPSB0aGlzLl9jb252ZXJ0TGF0TG5ncyhsYXRsbmdzKTtcblx0fSxcblxuXHRfZGVmYXVsdFNoYXBlOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIEwuUG9seWxpbmUuX2ZsYXQodGhpcy5fbGF0bG5ncykgPyB0aGlzLl9sYXRsbmdzIDogdGhpcy5fbGF0bG5nc1swXTtcblx0fSxcblxuXHQvLyByZWN1cnNpdmVseSBjb252ZXJ0IGxhdGxuZ3MgaW5wdXQgaW50byBhY3R1YWwgTGF0TG5nIGluc3RhbmNlczsgY2FsY3VsYXRlIGJvdW5kcyBhbG9uZyB0aGUgd2F5XG5cdF9jb252ZXJ0TGF0TG5nczogZnVuY3Rpb24gKGxhdGxuZ3MpIHtcblx0XHR2YXIgcmVzdWx0ID0gW10sXG5cdFx0ICAgIGZsYXQgPSBMLlBvbHlsaW5lLl9mbGF0KGxhdGxuZ3MpO1xuXG5cdFx0Zm9yICh2YXIgaSA9IDAsIGxlbiA9IGxhdGxuZ3MubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdGlmIChmbGF0KSB7XG5cdFx0XHRcdHJlc3VsdFtpXSA9IEwubGF0TG5nKGxhdGxuZ3NbaV0pO1xuXHRcdFx0XHR0aGlzLl9ib3VuZHMuZXh0ZW5kKHJlc3VsdFtpXSk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRyZXN1bHRbaV0gPSB0aGlzLl9jb252ZXJ0TGF0TG5ncyhsYXRsbmdzW2ldKTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRyZXR1cm4gcmVzdWx0O1xuXHR9LFxuXG5cdF9wcm9qZWN0OiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIHB4Qm91bmRzID0gbmV3IEwuQm91bmRzKCk7XG5cdFx0dGhpcy5fcmluZ3MgPSBbXTtcblx0XHR0aGlzLl9wcm9qZWN0TGF0bG5ncyh0aGlzLl9sYXRsbmdzLCB0aGlzLl9yaW5ncywgcHhCb3VuZHMpO1xuXG5cdFx0dmFyIHcgPSB0aGlzLl9jbGlja1RvbGVyYW5jZSgpLFxuXHRcdCAgICBwID0gbmV3IEwuUG9pbnQodywgdyk7XG5cblx0XHRpZiAodGhpcy5fYm91bmRzLmlzVmFsaWQoKSAmJiBweEJvdW5kcy5pc1ZhbGlkKCkpIHtcblx0XHRcdHB4Qm91bmRzLm1pbi5fc3VidHJhY3QocCk7XG5cdFx0XHRweEJvdW5kcy5tYXguX2FkZChwKTtcblx0XHRcdHRoaXMuX3B4Qm91bmRzID0gcHhCb3VuZHM7XG5cdFx0fVxuXHR9LFxuXG5cdC8vIHJlY3Vyc2l2ZWx5IHR1cm5zIGxhdGxuZ3MgaW50byBhIHNldCBvZiByaW5ncyB3aXRoIHByb2plY3RlZCBjb29yZGluYXRlc1xuXHRfcHJvamVjdExhdGxuZ3M6IGZ1bmN0aW9uIChsYXRsbmdzLCByZXN1bHQsIHByb2plY3RlZEJvdW5kcykge1xuXHRcdHZhciBmbGF0ID0gbGF0bG5nc1swXSBpbnN0YW5jZW9mIEwuTGF0TG5nLFxuXHRcdCAgICBsZW4gPSBsYXRsbmdzLmxlbmd0aCxcblx0XHQgICAgaSwgcmluZztcblxuXHRcdGlmIChmbGF0KSB7XG5cdFx0XHRyaW5nID0gW107XG5cdFx0XHRmb3IgKGkgPSAwOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdFx0cmluZ1tpXSA9IHRoaXMuX21hcC5sYXRMbmdUb0xheWVyUG9pbnQobGF0bG5nc1tpXSk7XG5cdFx0XHRcdHByb2plY3RlZEJvdW5kcy5leHRlbmQocmluZ1tpXSk7XG5cdFx0XHR9XG5cdFx0XHRyZXN1bHQucHVzaChyaW5nKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0Zm9yIChpID0gMDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRcdHRoaXMuX3Byb2plY3RMYXRsbmdzKGxhdGxuZ3NbaV0sIHJlc3VsdCwgcHJvamVjdGVkQm91bmRzKTtcblx0XHRcdH1cblx0XHR9XG5cdH0sXG5cblx0Ly8gY2xpcCBwb2x5bGluZSBieSByZW5kZXJlciBib3VuZHMgc28gdGhhdCB3ZSBoYXZlIGxlc3MgdG8gcmVuZGVyIGZvciBwZXJmb3JtYW5jZVxuXHRfY2xpcFBvaW50czogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBib3VuZHMgPSB0aGlzLl9yZW5kZXJlci5fYm91bmRzO1xuXG5cdFx0dGhpcy5fcGFydHMgPSBbXTtcblx0XHRpZiAoIXRoaXMuX3B4Qm91bmRzIHx8ICF0aGlzLl9weEJvdW5kcy5pbnRlcnNlY3RzKGJvdW5kcykpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRpZiAodGhpcy5vcHRpb25zLm5vQ2xpcCkge1xuXHRcdFx0dGhpcy5fcGFydHMgPSB0aGlzLl9yaW5ncztcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHR2YXIgcGFydHMgPSB0aGlzLl9wYXJ0cyxcblx0XHQgICAgaSwgaiwgaywgbGVuLCBsZW4yLCBzZWdtZW50LCBwb2ludHM7XG5cblx0XHRmb3IgKGkgPSAwLCBrID0gMCwgbGVuID0gdGhpcy5fcmluZ3MubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdHBvaW50cyA9IHRoaXMuX3JpbmdzW2ldO1xuXG5cdFx0XHRmb3IgKGogPSAwLCBsZW4yID0gcG9pbnRzLmxlbmd0aDsgaiA8IGxlbjIgLSAxOyBqKyspIHtcblx0XHRcdFx0c2VnbWVudCA9IEwuTGluZVV0aWwuY2xpcFNlZ21lbnQocG9pbnRzW2pdLCBwb2ludHNbaiArIDFdLCBib3VuZHMsIGosIHRydWUpO1xuXG5cdFx0XHRcdGlmICghc2VnbWVudCkgeyBjb250aW51ZTsgfVxuXG5cdFx0XHRcdHBhcnRzW2tdID0gcGFydHNba10gfHwgW107XG5cdFx0XHRcdHBhcnRzW2tdLnB1c2goc2VnbWVudFswXSk7XG5cblx0XHRcdFx0Ly8gaWYgc2VnbWVudCBnb2VzIG91dCBvZiBzY3JlZW4sIG9yIGl0J3MgdGhlIGxhc3Qgb25lLCBpdCdzIHRoZSBlbmQgb2YgdGhlIGxpbmUgcGFydFxuXHRcdFx0XHRpZiAoKHNlZ21lbnRbMV0gIT09IHBvaW50c1tqICsgMV0pIHx8IChqID09PSBsZW4yIC0gMikpIHtcblx0XHRcdFx0XHRwYXJ0c1trXS5wdXNoKHNlZ21lbnRbMV0pO1xuXHRcdFx0XHRcdGsrKztcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0fSxcblxuXHQvLyBzaW1wbGlmeSBlYWNoIGNsaXBwZWQgcGFydCBvZiB0aGUgcG9seWxpbmUgZm9yIHBlcmZvcm1hbmNlXG5cdF9zaW1wbGlmeVBvaW50czogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBwYXJ0cyA9IHRoaXMuX3BhcnRzLFxuXHRcdCAgICB0b2xlcmFuY2UgPSB0aGlzLm9wdGlvbnMuc21vb3RoRmFjdG9yO1xuXG5cdFx0Zm9yICh2YXIgaSA9IDAsIGxlbiA9IHBhcnRzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRwYXJ0c1tpXSA9IEwuTGluZVV0aWwuc2ltcGxpZnkocGFydHNbaV0sIHRvbGVyYW5jZSk7XG5cdFx0fVxuXHR9LFxuXG5cdF91cGRhdGU6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAoIXRoaXMuX21hcCkgeyByZXR1cm47IH1cblxuXHRcdHRoaXMuX2NsaXBQb2ludHMoKTtcblx0XHR0aGlzLl9zaW1wbGlmeVBvaW50cygpO1xuXHRcdHRoaXMuX3VwZGF0ZVBhdGgoKTtcblx0fSxcblxuXHRfdXBkYXRlUGF0aDogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX3JlbmRlcmVyLl91cGRhdGVQb2x5KHRoaXMpO1xuXHR9XG59KTtcblxuLy8gQGZhY3RvcnkgTC5wb2x5bGluZShsYXRsbmdzOiBMYXRMbmdbXSwgb3B0aW9ucz86IFBvbHlsaW5lIG9wdGlvbnMpXG4vLyBJbnN0YW50aWF0ZXMgYSBwb2x5bGluZSBvYmplY3QgZ2l2ZW4gYW4gYXJyYXkgb2YgZ2VvZ3JhcGhpY2FsIHBvaW50cyBhbmRcbi8vIG9wdGlvbmFsbHkgYW4gb3B0aW9ucyBvYmplY3QuIFlvdSBjYW4gY3JlYXRlIGEgYFBvbHlsaW5lYCBvYmplY3Qgd2l0aFxuLy8gbXVsdGlwbGUgc2VwYXJhdGUgbGluZXMgKGBNdWx0aVBvbHlsaW5lYCkgYnkgcGFzc2luZyBhbiBhcnJheSBvZiBhcnJheXNcbi8vIG9mIGdlb2dyYXBoaWMgcG9pbnRzLlxuTC5wb2x5bGluZSA9IGZ1bmN0aW9uIChsYXRsbmdzLCBvcHRpb25zKSB7XG5cdHJldHVybiBuZXcgTC5Qb2x5bGluZShsYXRsbmdzLCBvcHRpb25zKTtcbn07XG5cbkwuUG9seWxpbmUuX2ZsYXQgPSBmdW5jdGlvbiAobGF0bG5ncykge1xuXHQvLyB0cnVlIGlmIGl0J3MgYSBmbGF0IGFycmF5IG9mIGxhdGxuZ3M7IGZhbHNlIGlmIG5lc3RlZFxuXHRyZXR1cm4gIUwuVXRpbC5pc0FycmF5KGxhdGxuZ3NbMF0pIHx8ICh0eXBlb2YgbGF0bG5nc1swXVswXSAhPT0gJ29iamVjdCcgJiYgdHlwZW9mIGxhdGxuZ3NbMF1bMF0gIT09ICd1bmRlZmluZWQnKTtcbn07XG5cblxuXG4vKlxyXG4gKiBAbmFtZXNwYWNlIFBvbHlVdGlsXHJcbiAqIFZhcmlvdXMgdXRpbGl0eSBmdW5jdGlvbnMgZm9yIHBvbHlnb24gZ2VvbWV0cmllcy5cclxuICovXHJcblxyXG5MLlBvbHlVdGlsID0ge307XHJcblxyXG4vKiBAZnVuY3Rpb24gY2xpcFBvbHlnb24ocG9pbnRzOiBQb2ludFtdLCBib3VuZHM6IEJvdW5kcywgcm91bmQ/OiBCb29sZWFuKTogUG9pbnRbXVxyXG4gKiBDbGlwcyB0aGUgcG9seWdvbiBnZW9tZXRyeSBkZWZpbmVkIGJ5IHRoZSBnaXZlbiBgcG9pbnRzYCBieSB0aGUgZ2l2ZW4gYm91bmRzICh1c2luZyB0aGUgW1N1dGhlcmxhbmQtSG9kZ2VtYW4gYWxnb3JpdGhtXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9TdXRoZXJsYW5kJUUyJTgwJTkzSG9kZ21hbl9hbGdvcml0aG0pKS5cclxuICogVXNlZCBieSBMZWFmbGV0IHRvIG9ubHkgc2hvdyBwb2x5Z29uIHBvaW50cyB0aGF0IGFyZSBvbiB0aGUgc2NyZWVuIG9yIG5lYXIsIGluY3JlYXNpbmdcclxuICogcGVyZm9ybWFuY2UuIE5vdGUgdGhhdCBwb2x5Z29uIHBvaW50cyBuZWVkcyBkaWZmZXJlbnQgYWxnb3JpdGhtIGZvciBjbGlwcGluZ1xyXG4gKiB0aGFuIHBvbHlsaW5lLCBzbyB0aGVyZSdzIGEgc2VwZXJhdGUgbWV0aG9kIGZvciBpdC5cclxuICovXHJcbkwuUG9seVV0aWwuY2xpcFBvbHlnb24gPSBmdW5jdGlvbiAocG9pbnRzLCBib3VuZHMsIHJvdW5kKSB7XHJcblx0dmFyIGNsaXBwZWRQb2ludHMsXHJcblx0ICAgIGVkZ2VzID0gWzEsIDQsIDIsIDhdLFxyXG5cdCAgICBpLCBqLCBrLFxyXG5cdCAgICBhLCBiLFxyXG5cdCAgICBsZW4sIGVkZ2UsIHAsXHJcblx0ICAgIGx1ID0gTC5MaW5lVXRpbDtcclxuXHJcblx0Zm9yIChpID0gMCwgbGVuID0gcG9pbnRzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRwb2ludHNbaV0uX2NvZGUgPSBsdS5fZ2V0Qml0Q29kZShwb2ludHNbaV0sIGJvdW5kcyk7XHJcblx0fVxyXG5cclxuXHQvLyBmb3IgZWFjaCBlZGdlIChsZWZ0LCBib3R0b20sIHJpZ2h0LCB0b3ApXHJcblx0Zm9yIChrID0gMDsgayA8IDQ7IGsrKykge1xyXG5cdFx0ZWRnZSA9IGVkZ2VzW2tdO1xyXG5cdFx0Y2xpcHBlZFBvaW50cyA9IFtdO1xyXG5cclxuXHRcdGZvciAoaSA9IDAsIGxlbiA9IHBvaW50cy5sZW5ndGgsIGogPSBsZW4gLSAxOyBpIDwgbGVuOyBqID0gaSsrKSB7XHJcblx0XHRcdGEgPSBwb2ludHNbaV07XHJcblx0XHRcdGIgPSBwb2ludHNbal07XHJcblxyXG5cdFx0XHQvLyBpZiBhIGlzIGluc2lkZSB0aGUgY2xpcCB3aW5kb3dcclxuXHRcdFx0aWYgKCEoYS5fY29kZSAmIGVkZ2UpKSB7XHJcblx0XHRcdFx0Ly8gaWYgYiBpcyBvdXRzaWRlIHRoZSBjbGlwIHdpbmRvdyAoYS0+YiBnb2VzIG91dCBvZiBzY3JlZW4pXHJcblx0XHRcdFx0aWYgKGIuX2NvZGUgJiBlZGdlKSB7XHJcblx0XHRcdFx0XHRwID0gbHUuX2dldEVkZ2VJbnRlcnNlY3Rpb24oYiwgYSwgZWRnZSwgYm91bmRzLCByb3VuZCk7XHJcblx0XHRcdFx0XHRwLl9jb2RlID0gbHUuX2dldEJpdENvZGUocCwgYm91bmRzKTtcclxuXHRcdFx0XHRcdGNsaXBwZWRQb2ludHMucHVzaChwKTtcclxuXHRcdFx0XHR9XHJcblx0XHRcdFx0Y2xpcHBlZFBvaW50cy5wdXNoKGEpO1xyXG5cclxuXHRcdFx0Ly8gZWxzZSBpZiBiIGlzIGluc2lkZSB0aGUgY2xpcCB3aW5kb3cgKGEtPmIgZW50ZXJzIHRoZSBzY3JlZW4pXHJcblx0XHRcdH0gZWxzZSBpZiAoIShiLl9jb2RlICYgZWRnZSkpIHtcclxuXHRcdFx0XHRwID0gbHUuX2dldEVkZ2VJbnRlcnNlY3Rpb24oYiwgYSwgZWRnZSwgYm91bmRzLCByb3VuZCk7XHJcblx0XHRcdFx0cC5fY29kZSA9IGx1Ll9nZXRCaXRDb2RlKHAsIGJvdW5kcyk7XHJcblx0XHRcdFx0Y2xpcHBlZFBvaW50cy5wdXNoKHApO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0XHRwb2ludHMgPSBjbGlwcGVkUG9pbnRzO1xyXG5cdH1cclxuXHJcblx0cmV0dXJuIHBvaW50cztcclxufTtcclxuXG5cblxuLypcbiAqIEBjbGFzcyBQb2x5Z29uXG4gKiBAYWthIEwuUG9seWdvblxuICogQGluaGVyaXRzIFBvbHlsaW5lXG4gKlxuICogQSBjbGFzcyBmb3IgZHJhd2luZyBwb2x5Z29uIG92ZXJsYXlzIG9uIGEgbWFwLiBFeHRlbmRzIGBQb2x5bGluZWAuXG4gKlxuICogTm90ZSB0aGF0IHBvaW50cyB5b3UgcGFzcyB3aGVuIGNyZWF0aW5nIGEgcG9seWdvbiBzaG91bGRuJ3QgaGF2ZSBhbiBhZGRpdGlvbmFsIGxhc3QgcG9pbnQgZXF1YWwgdG8gdGhlIGZpcnN0IG9uZSDigJQgaXQncyBiZXR0ZXIgdG8gZmlsdGVyIG91dCBzdWNoIHBvaW50cy5cbiAqXG4gKlxuICogQGV4YW1wbGVcbiAqXG4gKiBgYGBqc1xuICogLy8gY3JlYXRlIGEgcmVkIHBvbHlnb24gZnJvbSBhbiBhcnJheSBvZiBMYXRMbmcgcG9pbnRzXG4gKiB2YXIgbGF0bG5ncyA9IFtbLTExMS4wMywgNDFdLFstMTExLjA0LCA0NV0sWy0xMDQuMDUsIDQ1XSxbLTEwNC4wNSwgNDFdXTtcbiAqXG4gKiB2YXIgcG9seWdvbiA9IEwucG9seWdvbihsYXRsbmdzLCB7Y29sb3I6ICdyZWQnfSkuYWRkVG8obWFwKTtcbiAqXG4gKiAvLyB6b29tIHRoZSBtYXAgdG8gdGhlIHBvbHlnb25cbiAqIG1hcC5maXRCb3VuZHMocG9seWdvbi5nZXRCb3VuZHMoKSk7XG4gKiBgYGBcbiAqXG4gKiBZb3UgY2FuIGFsc28gcGFzcyBhbiBhcnJheSBvZiBhcnJheXMgb2YgbGF0bG5ncywgd2l0aCB0aGUgZmlyc3QgYXJyYXkgcmVwcmVzZW50aW5nIHRoZSBvdXRlciBzaGFwZSBhbmQgdGhlIG90aGVyIGFycmF5cyByZXByZXNlbnRpbmcgaG9sZXMgaW4gdGhlIG91dGVyIHNoYXBlOlxuICpcbiAqIGBgYGpzXG4gKiB2YXIgbGF0bG5ncyA9IFtcbiAqICAgW1stMTExLjAzLCA0MV0sWy0xMTEuMDQsIDQ1XSxbLTEwNC4wNSwgNDVdLFstMTA0LjA1LCA0MV1dLCAvLyBvdXRlciByaW5nXG4gKiAgIFtbLTEwOC41OCwzNy4yOV0sWy0xMDguNTgsNDAuNzFdLFstMTAyLjUwLDQwLjcxXSxbLTEwMi41MCwzNy4yOV1dIC8vIGhvbGVcbiAqIF07XG4gKiBgYGBcbiAqXG4gKiBBZGRpdGlvbmFsbHksIHlvdSBjYW4gcGFzcyBhIG11bHRpLWRpbWVuc2lvbmFsIGFycmF5IHRvIHJlcHJlc2VudCBhIE11bHRpUG9seWdvbiBzaGFwZS5cbiAqXG4gKiBgYGBqc1xuICogdmFyIGxhdGxuZ3MgPSBbXG4gKiAgIFsgLy8gZmlyc3QgcG9seWdvblxuICogICAgIFtbLTExMS4wMywgNDFdLFstMTExLjA0LCA0NV0sWy0xMDQuMDUsIDQ1XSxbLTEwNC4wNSwgNDFdXSwgLy8gb3V0ZXIgcmluZ1xuICogICAgIFtbLTEwOC41OCwzNy4yOV0sWy0xMDguNTgsNDAuNzFdLFstMTAyLjUwLDQwLjcxXSxbLTEwMi41MCwzNy4yOV1dIC8vIGhvbGVcbiAqICAgXSxcbiAqICAgWyAvLyBzZWNvbmQgcG9seWdvblxuICogICAgIFtbLTEwOS4wNSwgMzddLFstMTA5LjAzLCA0MV0sWy0xMDIuMDUsIDQxXSxbLTEwMi4wNCwgMzddLFstMTA5LjA1LCAzOF1dXG4gKiAgIF1cbiAqIF07XG4gKiBgYGBcbiAqL1xuXG5MLlBvbHlnb24gPSBMLlBvbHlsaW5lLmV4dGVuZCh7XG5cblx0b3B0aW9uczoge1xuXHRcdGZpbGw6IHRydWVcblx0fSxcblxuXHRpc0VtcHR5OiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuICF0aGlzLl9sYXRsbmdzLmxlbmd0aCB8fCAhdGhpcy5fbGF0bG5nc1swXS5sZW5ndGg7XG5cdH0sXG5cblx0Z2V0Q2VudGVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0Ly8gdGhyb3dzIGVycm9yIHdoZW4gbm90IHlldCBhZGRlZCB0byBtYXAgYXMgdGhpcyBjZW50ZXIgY2FsY3VsYXRpb24gcmVxdWlyZXMgcHJvamVjdGVkIGNvb3JkaW5hdGVzXG5cdFx0aWYgKCF0aGlzLl9tYXApIHtcblx0XHRcdHRocm93IG5ldyBFcnJvcignTXVzdCBhZGQgbGF5ZXIgdG8gbWFwIGJlZm9yZSB1c2luZyBnZXRDZW50ZXIoKScpO1xuXHRcdH1cblxuXHRcdHZhciBpLCBqLCBwMSwgcDIsIGYsIGFyZWEsIHgsIHksIGNlbnRlcixcblx0XHQgICAgcG9pbnRzID0gdGhpcy5fcmluZ3NbMF0sXG5cdFx0ICAgIGxlbiA9IHBvaW50cy5sZW5ndGg7XG5cblx0XHRpZiAoIWxlbikgeyByZXR1cm4gbnVsbDsgfVxuXG5cdFx0Ly8gcG9seWdvbiBjZW50cm9pZCBhbGdvcml0aG07IG9ubHkgdXNlcyB0aGUgZmlyc3QgcmluZyBpZiB0aGVyZSBhcmUgbXVsdGlwbGVcblxuXHRcdGFyZWEgPSB4ID0geSA9IDA7XG5cblx0XHRmb3IgKGkgPSAwLCBqID0gbGVuIC0gMTsgaSA8IGxlbjsgaiA9IGkrKykge1xuXHRcdFx0cDEgPSBwb2ludHNbaV07XG5cdFx0XHRwMiA9IHBvaW50c1tqXTtcblxuXHRcdFx0ZiA9IHAxLnkgKiBwMi54IC0gcDIueSAqIHAxLng7XG5cdFx0XHR4ICs9IChwMS54ICsgcDIueCkgKiBmO1xuXHRcdFx0eSArPSAocDEueSArIHAyLnkpICogZjtcblx0XHRcdGFyZWEgKz0gZiAqIDM7XG5cdFx0fVxuXG5cdFx0aWYgKGFyZWEgPT09IDApIHtcblx0XHRcdC8vIFBvbHlnb24gaXMgc28gc21hbGwgdGhhdCBhbGwgcG9pbnRzIGFyZSBvbiBzYW1lIHBpeGVsLlxuXHRcdFx0Y2VudGVyID0gcG9pbnRzWzBdO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRjZW50ZXIgPSBbeCAvIGFyZWEsIHkgLyBhcmVhXTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXMuX21hcC5sYXllclBvaW50VG9MYXRMbmcoY2VudGVyKTtcblx0fSxcblxuXHRfY29udmVydExhdExuZ3M6IGZ1bmN0aW9uIChsYXRsbmdzKSB7XG5cdFx0dmFyIHJlc3VsdCA9IEwuUG9seWxpbmUucHJvdG90eXBlLl9jb252ZXJ0TGF0TG5ncy5jYWxsKHRoaXMsIGxhdGxuZ3MpLFxuXHRcdCAgICBsZW4gPSByZXN1bHQubGVuZ3RoO1xuXG5cdFx0Ly8gcmVtb3ZlIGxhc3QgcG9pbnQgaWYgaXQgZXF1YWxzIGZpcnN0IG9uZVxuXHRcdGlmIChsZW4gPj0gMiAmJiByZXN1bHRbMF0gaW5zdGFuY2VvZiBMLkxhdExuZyAmJiByZXN1bHRbMF0uZXF1YWxzKHJlc3VsdFtsZW4gLSAxXSkpIHtcblx0XHRcdHJlc3VsdC5wb3AoKTtcblx0XHR9XG5cdFx0cmV0dXJuIHJlc3VsdDtcblx0fSxcblxuXHRfc2V0TGF0TG5nczogZnVuY3Rpb24gKGxhdGxuZ3MpIHtcblx0XHRMLlBvbHlsaW5lLnByb3RvdHlwZS5fc2V0TGF0TG5ncy5jYWxsKHRoaXMsIGxhdGxuZ3MpO1xuXHRcdGlmIChMLlBvbHlsaW5lLl9mbGF0KHRoaXMuX2xhdGxuZ3MpKSB7XG5cdFx0XHR0aGlzLl9sYXRsbmdzID0gW3RoaXMuX2xhdGxuZ3NdO1xuXHRcdH1cblx0fSxcblxuXHRfZGVmYXVsdFNoYXBlOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIEwuUG9seWxpbmUuX2ZsYXQodGhpcy5fbGF0bG5nc1swXSkgPyB0aGlzLl9sYXRsbmdzWzBdIDogdGhpcy5fbGF0bG5nc1swXVswXTtcblx0fSxcblxuXHRfY2xpcFBvaW50czogZnVuY3Rpb24gKCkge1xuXHRcdC8vIHBvbHlnb25zIG5lZWQgYSBkaWZmZXJlbnQgY2xpcHBpbmcgYWxnb3JpdGhtIHNvIHdlIHJlZGVmaW5lIHRoYXRcblxuXHRcdHZhciBib3VuZHMgPSB0aGlzLl9yZW5kZXJlci5fYm91bmRzLFxuXHRcdCAgICB3ID0gdGhpcy5vcHRpb25zLndlaWdodCxcblx0XHQgICAgcCA9IG5ldyBMLlBvaW50KHcsIHcpO1xuXG5cdFx0Ly8gaW5jcmVhc2UgY2xpcCBwYWRkaW5nIGJ5IHN0cm9rZSB3aWR0aCB0byBhdm9pZCBzdHJva2Ugb24gY2xpcCBlZGdlc1xuXHRcdGJvdW5kcyA9IG5ldyBMLkJvdW5kcyhib3VuZHMubWluLnN1YnRyYWN0KHApLCBib3VuZHMubWF4LmFkZChwKSk7XG5cblx0XHR0aGlzLl9wYXJ0cyA9IFtdO1xuXHRcdGlmICghdGhpcy5fcHhCb3VuZHMgfHwgIXRoaXMuX3B4Qm91bmRzLmludGVyc2VjdHMoYm91bmRzKSkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdGlmICh0aGlzLm9wdGlvbnMubm9DbGlwKSB7XG5cdFx0XHR0aGlzLl9wYXJ0cyA9IHRoaXMuX3JpbmdzO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSB0aGlzLl9yaW5ncy5sZW5ndGgsIGNsaXBwZWQ7IGkgPCBsZW47IGkrKykge1xuXHRcdFx0Y2xpcHBlZCA9IEwuUG9seVV0aWwuY2xpcFBvbHlnb24odGhpcy5fcmluZ3NbaV0sIGJvdW5kcywgdHJ1ZSk7XG5cdFx0XHRpZiAoY2xpcHBlZC5sZW5ndGgpIHtcblx0XHRcdFx0dGhpcy5fcGFydHMucHVzaChjbGlwcGVkKTtcblx0XHRcdH1cblx0XHR9XG5cdH0sXG5cblx0X3VwZGF0ZVBhdGg6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9yZW5kZXJlci5fdXBkYXRlUG9seSh0aGlzLCB0cnVlKTtcblx0fVxufSk7XG5cblxuLy8gQGZhY3RvcnkgTC5wb2x5Z29uKGxhdGxuZ3M6IExhdExuZ1tdLCBvcHRpb25zPzogUG9seWxpbmUgb3B0aW9ucylcbkwucG9seWdvbiA9IGZ1bmN0aW9uIChsYXRsbmdzLCBvcHRpb25zKSB7XG5cdHJldHVybiBuZXcgTC5Qb2x5Z29uKGxhdGxuZ3MsIG9wdGlvbnMpO1xufTtcblxuXG5cbi8qXG4gKiBMLlJlY3RhbmdsZSBleHRlbmRzIFBvbHlnb24gYW5kIGNyZWF0ZXMgYSByZWN0YW5nbGUgd2hlbiBwYXNzZWQgYSBMYXRMbmdCb3VuZHMgb2JqZWN0LlxuICovXG5cbi8qXG4gKiBAY2xhc3MgUmVjdGFuZ2xlXG4gKiBAYWthIEwuUmV0YW5nbGVcbiAqIEBpbmhlcml0cyBQb2x5Z29uXG4gKlxuICogQSBjbGFzcyBmb3IgZHJhd2luZyByZWN0YW5nbGUgb3ZlcmxheXMgb24gYSBtYXAuIEV4dGVuZHMgYFBvbHlnb25gLlxuICpcbiAqIEBleGFtcGxlXG4gKlxuICogYGBganNcbiAqIC8vIGRlZmluZSByZWN0YW5nbGUgZ2VvZ3JhcGhpY2FsIGJvdW5kc1xuICogdmFyIGJvdW5kcyA9IFtbNTQuNTU5MzIyLCAtNS43Njc4MjJdLCBbNTYuMTIxMDYwNCwgLTMuMDIxMjQwXV07XG4gKlxuICogLy8gY3JlYXRlIGFuIG9yYW5nZSByZWN0YW5nbGVcbiAqIEwucmVjdGFuZ2xlKGJvdW5kcywge2NvbG9yOiBcIiNmZjc4MDBcIiwgd2VpZ2h0OiAxfSkuYWRkVG8obWFwKTtcbiAqXG4gKiAvLyB6b29tIHRoZSBtYXAgdG8gdGhlIHJlY3RhbmdsZSBib3VuZHNcbiAqIG1hcC5maXRCb3VuZHMoYm91bmRzKTtcbiAqIGBgYFxuICpcbiAqL1xuXG5cbkwuUmVjdGFuZ2xlID0gTC5Qb2x5Z29uLmV4dGVuZCh7XG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChsYXRMbmdCb3VuZHMsIG9wdGlvbnMpIHtcblx0XHRMLlBvbHlnb24ucHJvdG90eXBlLmluaXRpYWxpemUuY2FsbCh0aGlzLCB0aGlzLl9ib3VuZHNUb0xhdExuZ3MobGF0TG5nQm91bmRzKSwgb3B0aW9ucyk7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBzZXRCb3VuZHMobGF0TG5nQm91bmRzOiBMYXRMbmdCb3VuZHMpOiB0aGlzXG5cdC8vIFJlZHJhd3MgdGhlIHJlY3RhbmdsZSB3aXRoIHRoZSBwYXNzZWQgYm91bmRzLlxuXHRzZXRCb3VuZHM6IGZ1bmN0aW9uIChsYXRMbmdCb3VuZHMpIHtcblx0XHRyZXR1cm4gdGhpcy5zZXRMYXRMbmdzKHRoaXMuX2JvdW5kc1RvTGF0TG5ncyhsYXRMbmdCb3VuZHMpKTtcblx0fSxcblxuXHRfYm91bmRzVG9MYXRMbmdzOiBmdW5jdGlvbiAobGF0TG5nQm91bmRzKSB7XG5cdFx0bGF0TG5nQm91bmRzID0gTC5sYXRMbmdCb3VuZHMobGF0TG5nQm91bmRzKTtcblx0XHRyZXR1cm4gW1xuXHRcdFx0bGF0TG5nQm91bmRzLmdldFNvdXRoV2VzdCgpLFxuXHRcdFx0bGF0TG5nQm91bmRzLmdldE5vcnRoV2VzdCgpLFxuXHRcdFx0bGF0TG5nQm91bmRzLmdldE5vcnRoRWFzdCgpLFxuXHRcdFx0bGF0TG5nQm91bmRzLmdldFNvdXRoRWFzdCgpXG5cdFx0XTtcblx0fVxufSk7XG5cblxuLy8gQGZhY3RvcnkgTC5yZWN0YW5nbGUobGF0TG5nQm91bmRzOiBMYXRMbmdCb3VuZHMsIG9wdGlvbnM/OiBQb2x5bGluZSBvcHRpb25zKVxuTC5yZWN0YW5nbGUgPSBmdW5jdGlvbiAobGF0TG5nQm91bmRzLCBvcHRpb25zKSB7XG5cdHJldHVybiBuZXcgTC5SZWN0YW5nbGUobGF0TG5nQm91bmRzLCBvcHRpb25zKTtcbn07XG5cblxuXG4vKlxuICogQGNsYXNzIENpcmNsZU1hcmtlclxuICogQGFrYSBMLkNpcmNsZU1hcmtlclxuICogQGluaGVyaXRzIFBhdGhcbiAqXG4gKiBBIGNpcmNsZSBvZiBhIGZpeGVkIHNpemUgd2l0aCByYWRpdXMgc3BlY2lmaWVkIGluIHBpeGVscy4gRXh0ZW5kcyBgUGF0aGAuXG4gKi9cblxuTC5DaXJjbGVNYXJrZXIgPSBMLlBhdGguZXh0ZW5kKHtcblxuXHQvLyBAc2VjdGlvblxuXHQvLyBAYWthIENpcmNsZU1hcmtlciBvcHRpb25zXG5cdG9wdGlvbnM6IHtcblx0XHRmaWxsOiB0cnVlLFxuXG5cdFx0Ly8gQG9wdGlvbiByYWRpdXM6IE51bWJlciA9IDEwXG5cdFx0Ly8gUmFkaXVzIG9mIHRoZSBjaXJjbGUgbWFya2VyLCBpbiBwaXhlbHNcblx0XHRyYWRpdXM6IDEwXG5cdH0sXG5cblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKGxhdGxuZywgb3B0aW9ucykge1xuXHRcdEwuc2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcblx0XHR0aGlzLl9sYXRsbmcgPSBMLmxhdExuZyhsYXRsbmcpO1xuXHRcdHRoaXMuX3JhZGl1cyA9IHRoaXMub3B0aW9ucy5yYWRpdXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBzZXRMYXRMbmcobGF0TG5nOiBMYXRMbmcpOiB0aGlzXG5cdC8vIFNldHMgdGhlIHBvc2l0aW9uIG9mIGEgY2lyY2xlIG1hcmtlciB0byBhIG5ldyBsb2NhdGlvbi5cblx0c2V0TGF0TG5nOiBmdW5jdGlvbiAobGF0bG5nKSB7XG5cdFx0dGhpcy5fbGF0bG5nID0gTC5sYXRMbmcobGF0bG5nKTtcblx0XHR0aGlzLnJlZHJhdygpO1xuXHRcdHJldHVybiB0aGlzLmZpcmUoJ21vdmUnLCB7bGF0bG5nOiB0aGlzLl9sYXRsbmd9KTtcblx0fSxcblxuXHQvLyBAbWV0aG9kIGdldExhdExuZygpOiBMYXRMbmdcblx0Ly8gUmV0dXJucyB0aGUgY3VycmVudCBnZW9ncmFwaGljYWwgcG9zaXRpb24gb2YgdGhlIGNpcmNsZSBtYXJrZXJcblx0Z2V0TGF0TG5nOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX2xhdGxuZztcblx0fSxcblxuXHQvLyBAbWV0aG9kIHNldFJhZGl1cyhyYWRpdXM6IE51bWJlcik6IHRoaXNcblx0Ly8gU2V0cyB0aGUgcmFkaXVzIG9mIGEgY2lyY2xlIG1hcmtlci4gVW5pdHMgYXJlIGluIHBpeGVscy5cblx0c2V0UmFkaXVzOiBmdW5jdGlvbiAocmFkaXVzKSB7XG5cdFx0dGhpcy5vcHRpb25zLnJhZGl1cyA9IHRoaXMuX3JhZGl1cyA9IHJhZGl1cztcblx0XHRyZXR1cm4gdGhpcy5yZWRyYXcoKTtcblx0fSxcblxuXHQvLyBAbWV0aG9kIGdldFJhZGl1cygpOiBOdW1iZXJcblx0Ly8gUmV0dXJucyB0aGUgY3VycmVudCByYWRpdXMgb2YgdGhlIGNpcmNsZVxuXHRnZXRSYWRpdXM6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5fcmFkaXVzO1xuXHR9LFxuXG5cdHNldFN0eWxlIDogZnVuY3Rpb24gKG9wdGlvbnMpIHtcblx0XHR2YXIgcmFkaXVzID0gb3B0aW9ucyAmJiBvcHRpb25zLnJhZGl1cyB8fCB0aGlzLl9yYWRpdXM7XG5cdFx0TC5QYXRoLnByb3RvdHlwZS5zZXRTdHlsZS5jYWxsKHRoaXMsIG9wdGlvbnMpO1xuXHRcdHRoaXMuc2V0UmFkaXVzKHJhZGl1cyk7XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0X3Byb2plY3Q6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9wb2ludCA9IHRoaXMuX21hcC5sYXRMbmdUb0xheWVyUG9pbnQodGhpcy5fbGF0bG5nKTtcblx0XHR0aGlzLl91cGRhdGVCb3VuZHMoKTtcblx0fSxcblxuXHRfdXBkYXRlQm91bmRzOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIHIgPSB0aGlzLl9yYWRpdXMsXG5cdFx0ICAgIHIyID0gdGhpcy5fcmFkaXVzWSB8fCByLFxuXHRcdCAgICB3ID0gdGhpcy5fY2xpY2tUb2xlcmFuY2UoKSxcblx0XHQgICAgcCA9IFtyICsgdywgcjIgKyB3XTtcblx0XHR0aGlzLl9weEJvdW5kcyA9IG5ldyBMLkJvdW5kcyh0aGlzLl9wb2ludC5zdWJ0cmFjdChwKSwgdGhpcy5fcG9pbnQuYWRkKHApKTtcblx0fSxcblxuXHRfdXBkYXRlOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX21hcCkge1xuXHRcdFx0dGhpcy5fdXBkYXRlUGF0aCgpO1xuXHRcdH1cblx0fSxcblxuXHRfdXBkYXRlUGF0aDogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX3JlbmRlcmVyLl91cGRhdGVDaXJjbGUodGhpcyk7XG5cdH0sXG5cblx0X2VtcHR5OiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX3JhZGl1cyAmJiAhdGhpcy5fcmVuZGVyZXIuX2JvdW5kcy5pbnRlcnNlY3RzKHRoaXMuX3B4Qm91bmRzKTtcblx0fVxufSk7XG5cblxuLy8gQGZhY3RvcnkgTC5jaXJjbGVNYXJrZXIobGF0bG5nOiBMYXRMbmcsIG9wdGlvbnM/OiBDaXJjbGVNYXJrZXIgb3B0aW9ucylcbi8vIEluc3RhbnRpYXRlcyBhIGNpcmNsZSBtYXJrZXIgb2JqZWN0IGdpdmVuIGEgZ2VvZ3JhcGhpY2FsIHBvaW50LCBhbmQgYW4gb3B0aW9uYWwgb3B0aW9ucyBvYmplY3QuXG5MLmNpcmNsZU1hcmtlciA9IGZ1bmN0aW9uIChsYXRsbmcsIG9wdGlvbnMpIHtcblx0cmV0dXJuIG5ldyBMLkNpcmNsZU1hcmtlcihsYXRsbmcsIG9wdGlvbnMpO1xufTtcblxuXG5cbi8qXG4gKiBAY2xhc3MgQ2lyY2xlXG4gKiBAYWthIEwuQ2lyY2xlXG4gKiBAaW5oZXJpdHMgQ2lyY2xlTWFya2VyXG4gKlxuICogQSBjbGFzcyBmb3IgZHJhd2luZyBjaXJjbGUgb3ZlcmxheXMgb24gYSBtYXAuIEV4dGVuZHMgYENpcmNsZU1hcmtlcmAuXG4gKlxuICogSXQncyBhbiBhcHByb3hpbWF0aW9uIGFuZCBzdGFydHMgdG8gZGl2ZXJnZSBmcm9tIGEgcmVhbCBjaXJjbGUgY2xvc2VyIHRvIHBvbGVzIChkdWUgdG8gcHJvamVjdGlvbiBkaXN0b3J0aW9uKS5cbiAqXG4gKiBAZXhhbXBsZVxuICpcbiAqIGBgYGpzXG4gKiBMLmNpcmNsZShbNTAuNSwgMzAuNV0sIHtyYWRpdXM6IDIwMH0pLmFkZFRvKG1hcCk7XG4gKiBgYGBcbiAqL1xuXG5MLkNpcmNsZSA9IEwuQ2lyY2xlTWFya2VyLmV4dGVuZCh7XG5cblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKGxhdGxuZywgb3B0aW9ucywgbGVnYWN5T3B0aW9ucykge1xuXHRcdGlmICh0eXBlb2Ygb3B0aW9ucyA9PT0gJ251bWJlcicpIHtcblx0XHRcdC8vIEJhY2t3YXJkcyBjb21wYXRpYmlsaXR5IHdpdGggMC43LnggZmFjdG9yeSAobGF0bG5nLCByYWRpdXMsIG9wdGlvbnM/KVxuXHRcdFx0b3B0aW9ucyA9IEwuZXh0ZW5kKHt9LCBsZWdhY3lPcHRpb25zLCB7cmFkaXVzOiBvcHRpb25zfSk7XG5cdFx0fVxuXHRcdEwuc2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcblx0XHR0aGlzLl9sYXRsbmcgPSBMLmxhdExuZyhsYXRsbmcpO1xuXG5cdFx0aWYgKGlzTmFOKHRoaXMub3B0aW9ucy5yYWRpdXMpKSB7IHRocm93IG5ldyBFcnJvcignQ2lyY2xlIHJhZGl1cyBjYW5ub3QgYmUgTmFOJyk7IH1cblxuXHRcdC8vIEBzZWN0aW9uXG5cdFx0Ly8gQGFrYSBDaXJjbGUgb3B0aW9uc1xuXHRcdC8vIEBvcHRpb24gcmFkaXVzOiBOdW1iZXI7IFJhZGl1cyBvZiB0aGUgY2lyY2xlLCBpbiBtZXRlcnMuXG5cdFx0dGhpcy5fbVJhZGl1cyA9IHRoaXMub3B0aW9ucy5yYWRpdXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBzZXRSYWRpdXMocmFkaXVzOiBOdW1iZXIpOiB0aGlzXG5cdC8vIFNldHMgdGhlIHJhZGl1cyBvZiBhIGNpcmNsZS4gVW5pdHMgYXJlIGluIG1ldGVycy5cblx0c2V0UmFkaXVzOiBmdW5jdGlvbiAocmFkaXVzKSB7XG5cdFx0dGhpcy5fbVJhZGl1cyA9IHJhZGl1cztcblx0XHRyZXR1cm4gdGhpcy5yZWRyYXcoKTtcblx0fSxcblxuXHQvLyBAbWV0aG9kIGdldFJhZGl1cygpOiBOdW1iZXJcblx0Ly8gUmV0dXJucyB0aGUgY3VycmVudCByYWRpdXMgb2YgYSBjaXJjbGUuIFVuaXRzIGFyZSBpbiBtZXRlcnMuXG5cdGdldFJhZGl1czogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9tUmFkaXVzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgZ2V0Qm91bmRzKCk6IExhdExuZ0JvdW5kc1xuXHQvLyBSZXR1cm5zIHRoZSBgTGF0TG5nQm91bmRzYCBvZiB0aGUgcGF0aC5cblx0Z2V0Qm91bmRzOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIGhhbGYgPSBbdGhpcy5fcmFkaXVzLCB0aGlzLl9yYWRpdXNZIHx8IHRoaXMuX3JhZGl1c107XG5cblx0XHRyZXR1cm4gbmV3IEwuTGF0TG5nQm91bmRzKFxuXHRcdFx0dGhpcy5fbWFwLmxheWVyUG9pbnRUb0xhdExuZyh0aGlzLl9wb2ludC5zdWJ0cmFjdChoYWxmKSksXG5cdFx0XHR0aGlzLl9tYXAubGF5ZXJQb2ludFRvTGF0TG5nKHRoaXMuX3BvaW50LmFkZChoYWxmKSkpO1xuXHR9LFxuXG5cdHNldFN0eWxlOiBMLlBhdGgucHJvdG90eXBlLnNldFN0eWxlLFxuXG5cdF9wcm9qZWN0OiBmdW5jdGlvbiAoKSB7XG5cblx0XHR2YXIgbG5nID0gdGhpcy5fbGF0bG5nLmxuZyxcblx0XHQgICAgbGF0ID0gdGhpcy5fbGF0bG5nLmxhdCxcblx0XHQgICAgbWFwID0gdGhpcy5fbWFwLFxuXHRcdCAgICBjcnMgPSBtYXAub3B0aW9ucy5jcnM7XG5cblx0XHRpZiAoY3JzLmRpc3RhbmNlID09PSBMLkNSUy5FYXJ0aC5kaXN0YW5jZSkge1xuXHRcdFx0dmFyIGQgPSBNYXRoLlBJIC8gMTgwLFxuXHRcdFx0ICAgIGxhdFIgPSAodGhpcy5fbVJhZGl1cyAvIEwuQ1JTLkVhcnRoLlIpIC8gZCxcblx0XHRcdCAgICB0b3AgPSBtYXAucHJvamVjdChbbGF0ICsgbGF0UiwgbG5nXSksXG5cdFx0XHQgICAgYm90dG9tID0gbWFwLnByb2plY3QoW2xhdCAtIGxhdFIsIGxuZ10pLFxuXHRcdFx0ICAgIHAgPSB0b3AuYWRkKGJvdHRvbSkuZGl2aWRlQnkoMiksXG5cdFx0XHQgICAgbGF0MiA9IG1hcC51bnByb2plY3QocCkubGF0LFxuXHRcdFx0ICAgIGxuZ1IgPSBNYXRoLmFjb3MoKE1hdGguY29zKGxhdFIgKiBkKSAtIE1hdGguc2luKGxhdCAqIGQpICogTWF0aC5zaW4obGF0MiAqIGQpKSAvXG5cdFx0XHQgICAgICAgICAgICAoTWF0aC5jb3MobGF0ICogZCkgKiBNYXRoLmNvcyhsYXQyICogZCkpKSAvIGQ7XG5cblx0XHRcdGlmIChpc05hTihsbmdSKSB8fCBsbmdSID09PSAwKSB7XG5cdFx0XHRcdGxuZ1IgPSBsYXRSIC8gTWF0aC5jb3MoTWF0aC5QSSAvIDE4MCAqIGxhdCk7IC8vIEZhbGxiYWNrIGZvciBlZGdlIGNhc2UsICMyNDI1XG5cdFx0XHR9XG5cblx0XHRcdHRoaXMuX3BvaW50ID0gcC5zdWJ0cmFjdChtYXAuZ2V0UGl4ZWxPcmlnaW4oKSk7XG5cdFx0XHR0aGlzLl9yYWRpdXMgPSBpc05hTihsbmdSKSA/IDAgOiBNYXRoLm1heChNYXRoLnJvdW5kKHAueCAtIG1hcC5wcm9qZWN0KFtsYXQyLCBsbmcgLSBsbmdSXSkueCksIDEpO1xuXHRcdFx0dGhpcy5fcmFkaXVzWSA9IE1hdGgubWF4KE1hdGgucm91bmQocC55IC0gdG9wLnkpLCAxKTtcblxuXHRcdH0gZWxzZSB7XG5cdFx0XHR2YXIgbGF0bG5nMiA9IGNycy51bnByb2plY3QoY3JzLnByb2plY3QodGhpcy5fbGF0bG5nKS5zdWJ0cmFjdChbdGhpcy5fbVJhZGl1cywgMF0pKTtcblxuXHRcdFx0dGhpcy5fcG9pbnQgPSBtYXAubGF0TG5nVG9MYXllclBvaW50KHRoaXMuX2xhdGxuZyk7XG5cdFx0XHR0aGlzLl9yYWRpdXMgPSB0aGlzLl9wb2ludC54IC0gbWFwLmxhdExuZ1RvTGF5ZXJQb2ludChsYXRsbmcyKS54O1xuXHRcdH1cblxuXHRcdHRoaXMuX3VwZGF0ZUJvdW5kcygpO1xuXHR9XG59KTtcblxuLy8gQGZhY3RvcnkgTC5jaXJjbGUobGF0bG5nOiBMYXRMbmcsIG9wdGlvbnM/OiBDaXJjbGUgb3B0aW9ucylcbi8vIEluc3RhbnRpYXRlcyBhIGNpcmNsZSBvYmplY3QgZ2l2ZW4gYSBnZW9ncmFwaGljYWwgcG9pbnQsIGFuZCBhbiBvcHRpb25zIG9iamVjdFxuLy8gd2hpY2ggY29udGFpbnMgdGhlIGNpcmNsZSByYWRpdXMuXG4vLyBAYWx0ZXJuYXRpdmVcbi8vIEBmYWN0b3J5IEwuY2lyY2xlKGxhdGxuZzogTGF0TG5nLCByYWRpdXM6IE51bWJlciwgb3B0aW9ucz86IENpcmNsZSBvcHRpb25zKVxuLy8gT2Jzb2xldGUgd2F5IG9mIGluc3RhbnRpYXRpbmcgYSBjaXJjbGUsIGZvciBjb21wYXRpYmlsaXR5IHdpdGggMC43LnggY29kZS5cbi8vIERvIG5vdCB1c2UgaW4gbmV3IGFwcGxpY2F0aW9ucyBvciBwbHVnaW5zLlxuTC5jaXJjbGUgPSBmdW5jdGlvbiAobGF0bG5nLCBvcHRpb25zLCBsZWdhY3lPcHRpb25zKSB7XG5cdHJldHVybiBuZXcgTC5DaXJjbGUobGF0bG5nLCBvcHRpb25zLCBsZWdhY3lPcHRpb25zKTtcbn07XG5cblxuXG4vKlxuICogQGNsYXNzIFNWR1xuICogQGluaGVyaXRzIFJlbmRlcmVyXG4gKiBAYWthIEwuU1ZHXG4gKlxuICogQWxsb3dzIHZlY3RvciBsYXllcnMgdG8gYmUgZGlzcGxheWVkIHdpdGggW1NWR10oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvU1ZHKS5cbiAqIEluaGVyaXRzIGBSZW5kZXJlcmAuXG4gKlxuICogRHVlIHRvIFt0ZWNobmljYWwgbGltaXRhdGlvbnNdKGh0dHA6Ly9jYW5pdXNlLmNvbS8jc2VhcmNoPXN2ZyksIFNWRyBpcyBub3RcbiAqIGF2YWlsYWJsZSBpbiBhbGwgd2ViIGJyb3dzZXJzLCBub3RhYmx5IEFuZHJvaWQgMi54IGFuZCAzLnguXG4gKlxuICogQWx0aG91Z2ggU1ZHIGlzIG5vdCBhdmFpbGFibGUgb24gSUU3IGFuZCBJRTgsIHRoZXNlIGJyb3dzZXJzIHN1cHBvcnRcbiAqIFtWTUxdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1ZlY3Rvcl9NYXJrdXBfTGFuZ3VhZ2UpXG4gKiAoYSBub3cgZGVwcmVjYXRlZCB0ZWNobm9sb2d5KSwgYW5kIHRoZSBTVkcgcmVuZGVyZXIgd2lsbCBmYWxsIGJhY2sgdG8gVk1MIGluXG4gKiB0aGlzIGNhc2UuXG4gKlxuICogQGV4YW1wbGVcbiAqXG4gKiBVc2UgU1ZHIGJ5IGRlZmF1bHQgZm9yIGFsbCBwYXRocyBpbiB0aGUgbWFwOlxuICpcbiAqIGBgYGpzXG4gKiB2YXIgbWFwID0gTC5tYXAoJ21hcCcsIHtcbiAqIFx0cmVuZGVyZXI6IEwuc3ZnKClcbiAqIH0pO1xuICogYGBgXG4gKlxuICogVXNlIGEgU1ZHIHJlbmRlcmVyIHdpdGggZXh0cmEgcGFkZGluZyBmb3Igc3BlY2lmaWMgdmVjdG9yIGdlb21ldHJpZXM6XG4gKlxuICogYGBganNcbiAqIHZhciBtYXAgPSBMLm1hcCgnbWFwJyk7XG4gKiB2YXIgbXlSZW5kZXJlciA9IEwuc3ZnKHsgcGFkZGluZzogMC41IH0pO1xuICogdmFyIGxpbmUgPSBMLnBvbHlsaW5lKCBjb29yZGluYXRlcywgeyByZW5kZXJlcjogbXlSZW5kZXJlciB9ICk7XG4gKiB2YXIgY2lyY2xlID0gTC5jaXJjbGUoIGNlbnRlciwgeyByZW5kZXJlcjogbXlSZW5kZXJlciB9ICk7XG4gKiBgYGBcbiAqL1xuXG5MLlNWRyA9IEwuUmVuZGVyZXIuZXh0ZW5kKHtcblxuXHRnZXRFdmVudHM6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgZXZlbnRzID0gTC5SZW5kZXJlci5wcm90b3R5cGUuZ2V0RXZlbnRzLmNhbGwodGhpcyk7XG5cdFx0ZXZlbnRzLnpvb21zdGFydCA9IHRoaXMuX29uWm9vbVN0YXJ0O1xuXHRcdHJldHVybiBldmVudHM7XG5cdH0sXG5cblx0X2luaXRDb250YWluZXI6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9jb250YWluZXIgPSBMLlNWRy5jcmVhdGUoJ3N2ZycpO1xuXG5cdFx0Ly8gbWFrZXMgaXQgcG9zc2libGUgdG8gY2xpY2sgdGhyb3VnaCBzdmcgcm9vdDsgd2UnbGwgcmVzZXQgaXQgYmFjayBpbiBpbmRpdmlkdWFsIHBhdGhzXG5cdFx0dGhpcy5fY29udGFpbmVyLnNldEF0dHJpYnV0ZSgncG9pbnRlci1ldmVudHMnLCAnbm9uZScpO1xuXG5cdFx0dGhpcy5fcm9vdEdyb3VwID0gTC5TVkcuY3JlYXRlKCdnJyk7XG5cdFx0dGhpcy5fY29udGFpbmVyLmFwcGVuZENoaWxkKHRoaXMuX3Jvb3RHcm91cCk7XG5cdH0sXG5cblx0X29uWm9vbVN0YXJ0OiBmdW5jdGlvbiAoKSB7XG5cdFx0Ly8gRHJhZy10aGVuLXBpbmNoIGludGVyYWN0aW9ucyBtaWdodCBtZXNzIHVwIHRoZSBjZW50ZXIgYW5kIHpvb20uXG5cdFx0Ly8gSW4gdGhpcyBjYXNlLCB0aGUgZWFzaWVzdCB3YXkgdG8gcHJldmVudCB0aGlzIGlzIHJlLWRvIHRoZSByZW5kZXJlclxuXHRcdC8vICAgYm91bmRzIGFuZCBwYWRkaW5nIHdoZW4gdGhlIHpvb21pbmcgc3RhcnRzLlxuXHRcdHRoaXMuX3VwZGF0ZSgpO1xuXHR9LFxuXG5cdF91cGRhdGU6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fbWFwLl9hbmltYXRpbmdab29tICYmIHRoaXMuX2JvdW5kcykgeyByZXR1cm47IH1cblxuXHRcdEwuUmVuZGVyZXIucHJvdG90eXBlLl91cGRhdGUuY2FsbCh0aGlzKTtcblxuXHRcdHZhciBiID0gdGhpcy5fYm91bmRzLFxuXHRcdCAgICBzaXplID0gYi5nZXRTaXplKCksXG5cdFx0ICAgIGNvbnRhaW5lciA9IHRoaXMuX2NvbnRhaW5lcjtcblxuXHRcdC8vIHNldCBzaXplIG9mIHN2Zy1jb250YWluZXIgaWYgY2hhbmdlZFxuXHRcdGlmICghdGhpcy5fc3ZnU2l6ZSB8fCAhdGhpcy5fc3ZnU2l6ZS5lcXVhbHMoc2l6ZSkpIHtcblx0XHRcdHRoaXMuX3N2Z1NpemUgPSBzaXplO1xuXHRcdFx0Y29udGFpbmVyLnNldEF0dHJpYnV0ZSgnd2lkdGgnLCBzaXplLngpO1xuXHRcdFx0Y29udGFpbmVyLnNldEF0dHJpYnV0ZSgnaGVpZ2h0Jywgc2l6ZS55KTtcblx0XHR9XG5cblx0XHQvLyBtb3ZlbWVudDogdXBkYXRlIGNvbnRhaW5lciB2aWV3Qm94IHNvIHRoYXQgd2UgZG9uJ3QgaGF2ZSB0byBjaGFuZ2UgY29vcmRpbmF0ZXMgb2YgaW5kaXZpZHVhbCBsYXllcnNcblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24oY29udGFpbmVyLCBiLm1pbik7XG5cdFx0Y29udGFpbmVyLnNldEF0dHJpYnV0ZSgndmlld0JveCcsIFtiLm1pbi54LCBiLm1pbi55LCBzaXplLngsIHNpemUueV0uam9pbignICcpKTtcblxuXHRcdHRoaXMuZmlyZSgndXBkYXRlJyk7XG5cdH0sXG5cblx0Ly8gbWV0aG9kcyBiZWxvdyBhcmUgY2FsbGVkIGJ5IHZlY3RvciBsYXllcnMgaW1wbGVtZW50YXRpb25zXG5cblx0X2luaXRQYXRoOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR2YXIgcGF0aCA9IGxheWVyLl9wYXRoID0gTC5TVkcuY3JlYXRlKCdwYXRoJyk7XG5cblx0XHQvLyBAbmFtZXNwYWNlIFBhdGhcblx0XHQvLyBAb3B0aW9uIGNsYXNzTmFtZTogU3RyaW5nID0gbnVsbFxuXHRcdC8vIEN1c3RvbSBjbGFzcyBuYW1lIHNldCBvbiBhbiBlbGVtZW50LiBPbmx5IGZvciBTVkcgcmVuZGVyZXIuXG5cdFx0aWYgKGxheWVyLm9wdGlvbnMuY2xhc3NOYW1lKSB7XG5cdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3MocGF0aCwgbGF5ZXIub3B0aW9ucy5jbGFzc05hbWUpO1xuXHRcdH1cblxuXHRcdGlmIChsYXllci5vcHRpb25zLmludGVyYWN0aXZlKSB7XG5cdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3MocGF0aCwgJ2xlYWZsZXQtaW50ZXJhY3RpdmUnKTtcblx0XHR9XG5cblx0XHR0aGlzLl91cGRhdGVTdHlsZShsYXllcik7XG5cdFx0dGhpcy5fbGF5ZXJzW0wuc3RhbXAobGF5ZXIpXSA9IGxheWVyO1xuXHR9LFxuXG5cdF9hZGRQYXRoOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR0aGlzLl9yb290R3JvdXAuYXBwZW5kQ2hpbGQobGF5ZXIuX3BhdGgpO1xuXHRcdGxheWVyLmFkZEludGVyYWN0aXZlVGFyZ2V0KGxheWVyLl9wYXRoKTtcblx0fSxcblxuXHRfcmVtb3ZlUGF0aDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0TC5Eb21VdGlsLnJlbW92ZShsYXllci5fcGF0aCk7XG5cdFx0bGF5ZXIucmVtb3ZlSW50ZXJhY3RpdmVUYXJnZXQobGF5ZXIuX3BhdGgpO1xuXHRcdGRlbGV0ZSB0aGlzLl9sYXllcnNbTC5zdGFtcChsYXllcildO1xuXHR9LFxuXG5cdF91cGRhdGVQYXRoOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHRsYXllci5fcHJvamVjdCgpO1xuXHRcdGxheWVyLl91cGRhdGUoKTtcblx0fSxcblxuXHRfdXBkYXRlU3R5bGU6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHZhciBwYXRoID0gbGF5ZXIuX3BhdGgsXG5cdFx0ICAgIG9wdGlvbnMgPSBsYXllci5vcHRpb25zO1xuXG5cdFx0aWYgKCFwYXRoKSB7IHJldHVybjsgfVxuXG5cdFx0aWYgKG9wdGlvbnMuc3Ryb2tlKSB7XG5cdFx0XHRwYXRoLnNldEF0dHJpYnV0ZSgnc3Ryb2tlJywgb3B0aW9ucy5jb2xvcik7XG5cdFx0XHRwYXRoLnNldEF0dHJpYnV0ZSgnc3Ryb2tlLW9wYWNpdHknLCBvcHRpb25zLm9wYWNpdHkpO1xuXHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ3N0cm9rZS13aWR0aCcsIG9wdGlvbnMud2VpZ2h0KTtcblx0XHRcdHBhdGguc2V0QXR0cmlidXRlKCdzdHJva2UtbGluZWNhcCcsIG9wdGlvbnMubGluZUNhcCk7XG5cdFx0XHRwYXRoLnNldEF0dHJpYnV0ZSgnc3Ryb2tlLWxpbmVqb2luJywgb3B0aW9ucy5saW5lSm9pbik7XG5cblx0XHRcdGlmIChvcHRpb25zLmRhc2hBcnJheSkge1xuXHRcdFx0XHRwYXRoLnNldEF0dHJpYnV0ZSgnc3Ryb2tlLWRhc2hhcnJheScsIG9wdGlvbnMuZGFzaEFycmF5KTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHBhdGgucmVtb3ZlQXR0cmlidXRlKCdzdHJva2UtZGFzaGFycmF5Jyk7XG5cdFx0XHR9XG5cblx0XHRcdGlmIChvcHRpb25zLmRhc2hPZmZzZXQpIHtcblx0XHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ3N0cm9rZS1kYXNob2Zmc2V0Jywgb3B0aW9ucy5kYXNoT2Zmc2V0KTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHBhdGgucmVtb3ZlQXR0cmlidXRlKCdzdHJva2UtZGFzaG9mZnNldCcpO1xuXHRcdFx0fVxuXHRcdH0gZWxzZSB7XG5cdFx0XHRwYXRoLnNldEF0dHJpYnV0ZSgnc3Ryb2tlJywgJ25vbmUnKTtcblx0XHR9XG5cblx0XHRpZiAob3B0aW9ucy5maWxsKSB7XG5cdFx0XHRwYXRoLnNldEF0dHJpYnV0ZSgnZmlsbCcsIG9wdGlvbnMuZmlsbENvbG9yIHx8IG9wdGlvbnMuY29sb3IpO1xuXHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ2ZpbGwtb3BhY2l0eScsIG9wdGlvbnMuZmlsbE9wYWNpdHkpO1xuXHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ2ZpbGwtcnVsZScsIG9wdGlvbnMuZmlsbFJ1bGUgfHwgJ2V2ZW5vZGQnKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ2ZpbGwnLCAnbm9uZScpO1xuXHRcdH1cblx0fSxcblxuXHRfdXBkYXRlUG9seTogZnVuY3Rpb24gKGxheWVyLCBjbG9zZWQpIHtcblx0XHR0aGlzLl9zZXRQYXRoKGxheWVyLCBMLlNWRy5wb2ludHNUb1BhdGgobGF5ZXIuX3BhcnRzLCBjbG9zZWQpKTtcblx0fSxcblxuXHRfdXBkYXRlQ2lyY2xlOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR2YXIgcCA9IGxheWVyLl9wb2ludCxcblx0XHQgICAgciA9IGxheWVyLl9yYWRpdXMsXG5cdFx0ICAgIHIyID0gbGF5ZXIuX3JhZGl1c1kgfHwgcixcblx0XHQgICAgYXJjID0gJ2EnICsgciArICcsJyArIHIyICsgJyAwIDEsMCAnO1xuXG5cdFx0Ly8gZHJhd2luZyBhIGNpcmNsZSB3aXRoIHR3byBoYWxmLWFyY3Ncblx0XHR2YXIgZCA9IGxheWVyLl9lbXB0eSgpID8gJ00wIDAnIDpcblx0XHRcdFx0J00nICsgKHAueCAtIHIpICsgJywnICsgcC55ICtcblx0XHRcdFx0YXJjICsgKHIgKiAyKSArICcsMCAnICtcblx0XHRcdFx0YXJjICsgKC1yICogMikgKyAnLDAgJztcblxuXHRcdHRoaXMuX3NldFBhdGgobGF5ZXIsIGQpO1xuXHR9LFxuXG5cdF9zZXRQYXRoOiBmdW5jdGlvbiAobGF5ZXIsIHBhdGgpIHtcblx0XHRsYXllci5fcGF0aC5zZXRBdHRyaWJ1dGUoJ2QnLCBwYXRoKTtcblx0fSxcblxuXHQvLyBTVkcgZG9lcyBub3QgaGF2ZSB0aGUgY29uY2VwdCBvZiB6SW5kZXggc28gd2UgcmVzb3J0IHRvIGNoYW5naW5nIHRoZSBET00gb3JkZXIgb2YgZWxlbWVudHNcblx0X2JyaW5nVG9Gcm9udDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0TC5Eb21VdGlsLnRvRnJvbnQobGF5ZXIuX3BhdGgpO1xuXHR9LFxuXG5cdF9icmluZ1RvQmFjazogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0TC5Eb21VdGlsLnRvQmFjayhsYXllci5fcGF0aCk7XG5cdH1cbn0pO1xuXG5cbi8vIEBuYW1lc3BhY2UgU1ZHOyBAc2VjdGlvblxuLy8gVGhlcmUgYXJlIHNldmVyYWwgc3RhdGljIGZ1bmN0aW9ucyB3aGljaCBjYW4gYmUgY2FsbGVkIHdpdGhvdXQgaW5zdGFudGlhdGluZyBMLlNWRzpcbkwuZXh0ZW5kKEwuU1ZHLCB7XG5cdC8vIEBmdW5jdGlvbiBjcmVhdGUobmFtZTogU3RyaW5nKTogU1ZHRWxlbWVudFxuXHQvLyBSZXR1cm5zIGEgaW5zdGFuY2Ugb2YgW1NWR0VsZW1lbnRdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0FQSS9TVkdFbGVtZW50KSxcblx0Ly8gY29ycmVzcG9uZGluZyB0byB0aGUgY2xhc3MgbmFtZSBwYXNzZWQuIEZvciBleGFtcGxlLCB1c2luZyAnbGluZScgd2lsbCByZXR1cm5cblx0Ly8gYW4gaW5zdGFuY2Ugb2YgW1NWR0xpbmVFbGVtZW50XShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9BUEkvU1ZHTGluZUVsZW1lbnQpLlxuXHRjcmVhdGU6IGZ1bmN0aW9uIChuYW1lKSB7XG5cdFx0cmV0dXJuIGRvY3VtZW50LmNyZWF0ZUVsZW1lbnROUygnaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnLCBuYW1lKTtcblx0fSxcblxuXHQvLyBAZnVuY3Rpb24gcG9pbnRzVG9QYXRoKHJpbmdzOiBQb2ludFtdLCBjbG9zZWQ6IEJvb2xlYW4pOiBTdHJpbmdcblx0Ly8gR2VuZXJhdGVzIGEgU1ZHIHBhdGggc3RyaW5nIGZvciBtdWx0aXBsZSByaW5ncywgd2l0aCBlYWNoIHJpbmcgdHVybmluZ1xuXHQvLyBpbnRvIFwiTS4uTC4uTC4uXCIgaW5zdHJ1Y3Rpb25zXG5cdHBvaW50c1RvUGF0aDogZnVuY3Rpb24gKHJpbmdzLCBjbG9zZWQpIHtcblx0XHR2YXIgc3RyID0gJycsXG5cdFx0ICAgIGksIGosIGxlbiwgbGVuMiwgcG9pbnRzLCBwO1xuXG5cdFx0Zm9yIChpID0gMCwgbGVuID0gcmluZ3MubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdHBvaW50cyA9IHJpbmdzW2ldO1xuXG5cdFx0XHRmb3IgKGogPSAwLCBsZW4yID0gcG9pbnRzLmxlbmd0aDsgaiA8IGxlbjI7IGorKykge1xuXHRcdFx0XHRwID0gcG9pbnRzW2pdO1xuXHRcdFx0XHRzdHIgKz0gKGogPyAnTCcgOiAnTScpICsgcC54ICsgJyAnICsgcC55O1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBjbG9zZXMgdGhlIHJpbmcgZm9yIHBvbHlnb25zOyBcInhcIiBpcyBWTUwgc3ludGF4XG5cdFx0XHRzdHIgKz0gY2xvc2VkID8gKEwuQnJvd3Nlci5zdmcgPyAneicgOiAneCcpIDogJyc7XG5cdFx0fVxuXG5cdFx0Ly8gU1ZHIGNvbXBsYWlucyBhYm91dCBlbXB0eSBwYXRoIHN0cmluZ3Ncblx0XHRyZXR1cm4gc3RyIHx8ICdNMCAwJztcblx0fVxufSk7XG5cbi8vIEBuYW1lc3BhY2UgQnJvd3NlcjsgQHByb3BlcnR5IHN2ZzogQm9vbGVhblxuLy8gYHRydWVgIHdoZW4gdGhlIGJyb3dzZXIgc3VwcG9ydHMgW1NWR10oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvU1ZHKS5cbkwuQnJvd3Nlci5zdmcgPSAhIShkb2N1bWVudC5jcmVhdGVFbGVtZW50TlMgJiYgTC5TVkcuY3JlYXRlKCdzdmcnKS5jcmVhdGVTVkdSZWN0KTtcblxuXG4vLyBAbmFtZXNwYWNlIFNWR1xuLy8gQGZhY3RvcnkgTC5zdmcob3B0aW9ucz86IFJlbmRlcmVyIG9wdGlvbnMpXG4vLyBDcmVhdGVzIGEgU1ZHIHJlbmRlcmVyIHdpdGggdGhlIGdpdmVuIG9wdGlvbnMuXG5MLnN2ZyA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XG5cdHJldHVybiBMLkJyb3dzZXIuc3ZnIHx8IEwuQnJvd3Nlci52bWwgPyBuZXcgTC5TVkcob3B0aW9ucykgOiBudWxsO1xufTtcblxuXG5cbi8qXG4gKiBUaGFua3MgdG8gRG1pdHJ5IEJhcmFub3Zza3kgYW5kIGhpcyBSYXBoYWVsIGxpYnJhcnkgZm9yIGluc3BpcmF0aW9uIVxuICovXG5cbi8qXG4gKiBAY2xhc3MgU1ZHXG4gKlxuICogQWx0aG91Z2ggU1ZHIGlzIG5vdCBhdmFpbGFibGUgb24gSUU3IGFuZCBJRTgsIHRoZXNlIGJyb3dzZXJzIHN1cHBvcnQgW1ZNTF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvVmVjdG9yX01hcmt1cF9MYW5ndWFnZSksIGFuZCB0aGUgU1ZHIHJlbmRlcmVyIHdpbGwgZmFsbCBiYWNrIHRvIFZNTCBpbiB0aGlzIGNhc2UuXG4gKlxuICogVk1MIHdhcyBkZXByZWNhdGVkIGluIDIwMTIsIHdoaWNoIG1lYW5zIFZNTCBmdW5jdGlvbmFsaXR5IGV4aXN0cyBvbmx5IGZvciBiYWNrd2FyZHMgY29tcGF0aWJpbGl0eVxuICogd2l0aCBvbGQgdmVyc2lvbnMgb2YgSW50ZXJuZXQgRXhwbG9yZXIuXG4gKi9cblxuLy8gQG5hbWVzcGFjZSBCcm93c2VyOyBAcHJvcGVydHkgdm1sOiBCb29sZWFuXG4vLyBgdHJ1ZWAgaWYgdGhlIGJyb3dzZXIgc3VwcG9ydHMgW1ZNTF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvVmVjdG9yX01hcmt1cF9MYW5ndWFnZSkuXG5MLkJyb3dzZXIudm1sID0gIUwuQnJvd3Nlci5zdmcgJiYgKGZ1bmN0aW9uICgpIHtcblx0dHJ5IHtcblx0XHR2YXIgZGl2ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG5cdFx0ZGl2LmlubmVySFRNTCA9ICc8djpzaGFwZSBhZGo9XCIxXCIvPic7XG5cblx0XHR2YXIgc2hhcGUgPSBkaXYuZmlyc3RDaGlsZDtcblx0XHRzaGFwZS5zdHlsZS5iZWhhdmlvciA9ICd1cmwoI2RlZmF1bHQjVk1MKSc7XG5cblx0XHRyZXR1cm4gc2hhcGUgJiYgKHR5cGVvZiBzaGFwZS5hZGogPT09ICdvYmplY3QnKTtcblxuXHR9IGNhdGNoIChlKSB7XG5cdFx0cmV0dXJuIGZhbHNlO1xuXHR9XG59KCkpO1xuXG4vLyByZWRlZmluZSBzb21lIFNWRyBtZXRob2RzIHRvIGhhbmRsZSBWTUwgc3ludGF4IHdoaWNoIGlzIHNpbWlsYXIgYnV0IHdpdGggc29tZSBkaWZmZXJlbmNlc1xuTC5TVkcuaW5jbHVkZSghTC5Ccm93c2VyLnZtbCA/IHt9IDoge1xuXG5cdF9pbml0Q29udGFpbmVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fY29udGFpbmVyID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgJ2xlYWZsZXQtdm1sLWNvbnRhaW5lcicpO1xuXHR9LFxuXG5cdF91cGRhdGU6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fbWFwLl9hbmltYXRpbmdab29tKSB7IHJldHVybjsgfVxuXHRcdEwuUmVuZGVyZXIucHJvdG90eXBlLl91cGRhdGUuY2FsbCh0aGlzKTtcblx0XHR0aGlzLmZpcmUoJ3VwZGF0ZScpO1xuXHR9LFxuXG5cdF9pbml0UGF0aDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIGNvbnRhaW5lciA9IGxheWVyLl9jb250YWluZXIgPSBMLlNWRy5jcmVhdGUoJ3NoYXBlJyk7XG5cblx0XHRMLkRvbVV0aWwuYWRkQ2xhc3MoY29udGFpbmVyLCAnbGVhZmxldC12bWwtc2hhcGUgJyArICh0aGlzLm9wdGlvbnMuY2xhc3NOYW1lIHx8ICcnKSk7XG5cblx0XHRjb250YWluZXIuY29vcmRzaXplID0gJzEgMSc7XG5cblx0XHRsYXllci5fcGF0aCA9IEwuU1ZHLmNyZWF0ZSgncGF0aCcpO1xuXHRcdGNvbnRhaW5lci5hcHBlbmRDaGlsZChsYXllci5fcGF0aCk7XG5cblx0XHR0aGlzLl91cGRhdGVTdHlsZShsYXllcik7XG5cdH0sXG5cblx0X2FkZFBhdGg6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHZhciBjb250YWluZXIgPSBsYXllci5fY29udGFpbmVyO1xuXHRcdHRoaXMuX2NvbnRhaW5lci5hcHBlbmRDaGlsZChjb250YWluZXIpO1xuXG5cdFx0aWYgKGxheWVyLm9wdGlvbnMuaW50ZXJhY3RpdmUpIHtcblx0XHRcdGxheWVyLmFkZEludGVyYWN0aXZlVGFyZ2V0KGNvbnRhaW5lcik7XG5cdFx0fVxuXHR9LFxuXG5cdF9yZW1vdmVQYXRoOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR2YXIgY29udGFpbmVyID0gbGF5ZXIuX2NvbnRhaW5lcjtcblx0XHRMLkRvbVV0aWwucmVtb3ZlKGNvbnRhaW5lcik7XG5cdFx0bGF5ZXIucmVtb3ZlSW50ZXJhY3RpdmVUYXJnZXQoY29udGFpbmVyKTtcblx0fSxcblxuXHRfdXBkYXRlU3R5bGU6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHZhciBzdHJva2UgPSBsYXllci5fc3Ryb2tlLFxuXHRcdCAgICBmaWxsID0gbGF5ZXIuX2ZpbGwsXG5cdFx0ICAgIG9wdGlvbnMgPSBsYXllci5vcHRpb25zLFxuXHRcdCAgICBjb250YWluZXIgPSBsYXllci5fY29udGFpbmVyO1xuXG5cdFx0Y29udGFpbmVyLnN0cm9rZWQgPSAhIW9wdGlvbnMuc3Ryb2tlO1xuXHRcdGNvbnRhaW5lci5maWxsZWQgPSAhIW9wdGlvbnMuZmlsbDtcblxuXHRcdGlmIChvcHRpb25zLnN0cm9rZSkge1xuXHRcdFx0aWYgKCFzdHJva2UpIHtcblx0XHRcdFx0c3Ryb2tlID0gbGF5ZXIuX3N0cm9rZSA9IEwuU1ZHLmNyZWF0ZSgnc3Ryb2tlJyk7XG5cdFx0XHR9XG5cdFx0XHRjb250YWluZXIuYXBwZW5kQ2hpbGQoc3Ryb2tlKTtcblx0XHRcdHN0cm9rZS53ZWlnaHQgPSBvcHRpb25zLndlaWdodCArICdweCc7XG5cdFx0XHRzdHJva2UuY29sb3IgPSBvcHRpb25zLmNvbG9yO1xuXHRcdFx0c3Ryb2tlLm9wYWNpdHkgPSBvcHRpb25zLm9wYWNpdHk7XG5cblx0XHRcdGlmIChvcHRpb25zLmRhc2hBcnJheSkge1xuXHRcdFx0XHRzdHJva2UuZGFzaFN0eWxlID0gTC5VdGlsLmlzQXJyYXkob3B0aW9ucy5kYXNoQXJyYXkpID9cblx0XHRcdFx0ICAgIG9wdGlvbnMuZGFzaEFycmF5LmpvaW4oJyAnKSA6XG5cdFx0XHRcdCAgICBvcHRpb25zLmRhc2hBcnJheS5yZXBsYWNlKC8oICosICopL2csICcgJyk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRzdHJva2UuZGFzaFN0eWxlID0gJyc7XG5cdFx0XHR9XG5cdFx0XHRzdHJva2UuZW5kY2FwID0gb3B0aW9ucy5saW5lQ2FwLnJlcGxhY2UoJ2J1dHQnLCAnZmxhdCcpO1xuXHRcdFx0c3Ryb2tlLmpvaW5zdHlsZSA9IG9wdGlvbnMubGluZUpvaW47XG5cblx0XHR9IGVsc2UgaWYgKHN0cm9rZSkge1xuXHRcdFx0Y29udGFpbmVyLnJlbW92ZUNoaWxkKHN0cm9rZSk7XG5cdFx0XHRsYXllci5fc3Ryb2tlID0gbnVsbDtcblx0XHR9XG5cblx0XHRpZiAob3B0aW9ucy5maWxsKSB7XG5cdFx0XHRpZiAoIWZpbGwpIHtcblx0XHRcdFx0ZmlsbCA9IGxheWVyLl9maWxsID0gTC5TVkcuY3JlYXRlKCdmaWxsJyk7XG5cdFx0XHR9XG5cdFx0XHRjb250YWluZXIuYXBwZW5kQ2hpbGQoZmlsbCk7XG5cdFx0XHRmaWxsLmNvbG9yID0gb3B0aW9ucy5maWxsQ29sb3IgfHwgb3B0aW9ucy5jb2xvcjtcblx0XHRcdGZpbGwub3BhY2l0eSA9IG9wdGlvbnMuZmlsbE9wYWNpdHk7XG5cblx0XHR9IGVsc2UgaWYgKGZpbGwpIHtcblx0XHRcdGNvbnRhaW5lci5yZW1vdmVDaGlsZChmaWxsKTtcblx0XHRcdGxheWVyLl9maWxsID0gbnVsbDtcblx0XHR9XG5cdH0sXG5cblx0X3VwZGF0ZUNpcmNsZTogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIHAgPSBsYXllci5fcG9pbnQucm91bmQoKSxcblx0XHQgICAgciA9IE1hdGgucm91bmQobGF5ZXIuX3JhZGl1cyksXG5cdFx0ICAgIHIyID0gTWF0aC5yb3VuZChsYXllci5fcmFkaXVzWSB8fCByKTtcblxuXHRcdHRoaXMuX3NldFBhdGgobGF5ZXIsIGxheWVyLl9lbXB0eSgpID8gJ00wIDAnIDpcblx0XHRcdFx0J0FMICcgKyBwLnggKyAnLCcgKyBwLnkgKyAnICcgKyByICsgJywnICsgcjIgKyAnIDAsJyArICg2NTUzNSAqIDM2MCkpO1xuXHR9LFxuXG5cdF9zZXRQYXRoOiBmdW5jdGlvbiAobGF5ZXIsIHBhdGgpIHtcblx0XHRsYXllci5fcGF0aC52ID0gcGF0aDtcblx0fSxcblxuXHRfYnJpbmdUb0Zyb250OiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHRMLkRvbVV0aWwudG9Gcm9udChsYXllci5fY29udGFpbmVyKTtcblx0fSxcblxuXHRfYnJpbmdUb0JhY2s6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdEwuRG9tVXRpbC50b0JhY2sobGF5ZXIuX2NvbnRhaW5lcik7XG5cdH1cbn0pO1xuXG5pZiAoTC5Ccm93c2VyLnZtbCkge1xuXHRMLlNWRy5jcmVhdGUgPSAoZnVuY3Rpb24gKCkge1xuXHRcdHRyeSB7XG5cdFx0XHRkb2N1bWVudC5uYW1lc3BhY2VzLmFkZCgnbHZtbCcsICd1cm46c2NoZW1hcy1taWNyb3NvZnQtY29tOnZtbCcpO1xuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIChuYW1lKSB7XG5cdFx0XHRcdHJldHVybiBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCc8bHZtbDonICsgbmFtZSArICcgY2xhc3M9XCJsdm1sXCI+Jyk7XG5cdFx0XHR9O1xuXHRcdH0gY2F0Y2ggKGUpIHtcblx0XHRcdHJldHVybiBmdW5jdGlvbiAobmFtZSkge1xuXHRcdFx0XHRyZXR1cm4gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnPCcgKyBuYW1lICsgJyB4bWxucz1cInVybjpzY2hlbWFzLW1pY3Jvc29mdC5jb206dm1sXCIgY2xhc3M9XCJsdm1sXCI+Jyk7XG5cdFx0XHR9O1xuXHRcdH1cblx0fSkoKTtcbn1cblxuXG5cbi8qXG4gKiBAY2xhc3MgQ2FudmFzXG4gKiBAaW5oZXJpdHMgUmVuZGVyZXJcbiAqIEBha2EgTC5DYW52YXNcbiAqXG4gKiBBbGxvd3MgdmVjdG9yIGxheWVycyB0byBiZSBkaXNwbGF5ZWQgd2l0aCBbYDxjYW52YXM+YF0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvQVBJL0NhbnZhc19BUEkpLlxuICogSW5oZXJpdHMgYFJlbmRlcmVyYC5cbiAqXG4gKiBEdWUgdG8gW3RlY2huaWNhbCBsaW1pdGF0aW9uc10oaHR0cDovL2Nhbml1c2UuY29tLyNzZWFyY2g9Y2FudmFzKSwgQ2FudmFzIGlzIG5vdFxuICogYXZhaWxhYmxlIGluIGFsbCB3ZWIgYnJvd3NlcnMsIG5vdGFibHkgSUU4LCBhbmQgb3ZlcmxhcHBpbmcgZ2VvbWV0cmllcyBtaWdodFxuICogbm90IGRpc3BsYXkgcHJvcGVybHkgaW4gc29tZSBlZGdlIGNhc2VzLlxuICpcbiAqIEBleGFtcGxlXG4gKlxuICogVXNlIENhbnZhcyBieSBkZWZhdWx0IGZvciBhbGwgcGF0aHMgaW4gdGhlIG1hcDpcbiAqXG4gKiBgYGBqc1xuICogdmFyIG1hcCA9IEwubWFwKCdtYXAnLCB7XG4gKiBcdHJlbmRlcmVyOiBMLmNhbnZhcygpXG4gKiB9KTtcbiAqIGBgYFxuICpcbiAqIFVzZSBhIENhbnZhcyByZW5kZXJlciB3aXRoIGV4dHJhIHBhZGRpbmcgZm9yIHNwZWNpZmljIHZlY3RvciBnZW9tZXRyaWVzOlxuICpcbiAqIGBgYGpzXG4gKiB2YXIgbWFwID0gTC5tYXAoJ21hcCcpO1xuICogdmFyIG15UmVuZGVyZXIgPSBMLmNhbnZhcyh7IHBhZGRpbmc6IDAuNSB9KTtcbiAqIHZhciBsaW5lID0gTC5wb2x5bGluZSggY29vcmRpbmF0ZXMsIHsgcmVuZGVyZXI6IG15UmVuZGVyZXIgfSApO1xuICogdmFyIGNpcmNsZSA9IEwuY2lyY2xlKCBjZW50ZXIsIHsgcmVuZGVyZXI6IG15UmVuZGVyZXIgfSApO1xuICogYGBgXG4gKi9cblxuTC5DYW52YXMgPSBMLlJlbmRlcmVyLmV4dGVuZCh7XG5cblx0b25BZGQ6IGZ1bmN0aW9uICgpIHtcblx0XHRMLlJlbmRlcmVyLnByb3RvdHlwZS5vbkFkZC5jYWxsKHRoaXMpO1xuXG5cdFx0Ly8gUmVkcmF3IHZlY3RvcnMgc2luY2UgY2FudmFzIGlzIGNsZWFyZWQgdXBvbiByZW1vdmFsLFxuXHRcdC8vIGluIGNhc2Ugb2YgcmVtb3ZpbmcgdGhlIHJlbmRlcmVyIGl0c2VsZiBmcm9tIHRoZSBtYXAuXG5cdFx0dGhpcy5fZHJhdygpO1xuXHR9LFxuXG5cdF9pbml0Q29udGFpbmVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIGNvbnRhaW5lciA9IHRoaXMuX2NvbnRhaW5lciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpO1xuXG5cdFx0TC5Eb21FdmVudFxuXHRcdFx0Lm9uKGNvbnRhaW5lciwgJ21vdXNlbW92ZScsIEwuVXRpbC50aHJvdHRsZSh0aGlzLl9vbk1vdXNlTW92ZSwgMzIsIHRoaXMpLCB0aGlzKVxuXHRcdFx0Lm9uKGNvbnRhaW5lciwgJ2NsaWNrIGRibGNsaWNrIG1vdXNlZG93biBtb3VzZXVwIGNvbnRleHRtZW51JywgdGhpcy5fb25DbGljaywgdGhpcylcblx0XHRcdC5vbihjb250YWluZXIsICdtb3VzZW91dCcsIHRoaXMuX2hhbmRsZU1vdXNlT3V0LCB0aGlzKTtcblxuXHRcdHRoaXMuX2N0eCA9IGNvbnRhaW5lci5nZXRDb250ZXh0KCcyZCcpO1xuXHR9LFxuXG5cdF91cGRhdGVQYXRoczogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBsYXllcjtcblx0XHR0aGlzLl9yZWRyYXdCb3VuZHMgPSBudWxsO1xuXHRcdGZvciAodmFyIGlkIGluIHRoaXMuX2xheWVycykge1xuXHRcdFx0bGF5ZXIgPSB0aGlzLl9sYXllcnNbaWRdO1xuXHRcdFx0bGF5ZXIuX3VwZGF0ZSgpO1xuXHRcdH1cblx0XHR0aGlzLl9yZWRyYXcoKTtcblx0fSxcblxuXHRfdXBkYXRlOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX21hcC5fYW5pbWF0aW5nWm9vbSAmJiB0aGlzLl9ib3VuZHMpIHsgcmV0dXJuOyB9XG5cblx0XHR0aGlzLl9kcmF3bkxheWVycyA9IHt9O1xuXG5cdFx0TC5SZW5kZXJlci5wcm90b3R5cGUuX3VwZGF0ZS5jYWxsKHRoaXMpO1xuXG5cdFx0dmFyIGIgPSB0aGlzLl9ib3VuZHMsXG5cdFx0ICAgIGNvbnRhaW5lciA9IHRoaXMuX2NvbnRhaW5lcixcblx0XHQgICAgc2l6ZSA9IGIuZ2V0U2l6ZSgpLFxuXHRcdCAgICBtID0gTC5Ccm93c2VyLnJldGluYSA/IDIgOiAxO1xuXG5cdFx0TC5Eb21VdGlsLnNldFBvc2l0aW9uKGNvbnRhaW5lciwgYi5taW4pO1xuXG5cdFx0Ly8gc2V0IGNhbnZhcyBzaXplIChhbHNvIGNsZWFyaW5nIGl0KTsgdXNlIGRvdWJsZSBzaXplIG9uIHJldGluYVxuXHRcdGNvbnRhaW5lci53aWR0aCA9IG0gKiBzaXplLng7XG5cdFx0Y29udGFpbmVyLmhlaWdodCA9IG0gKiBzaXplLnk7XG5cdFx0Y29udGFpbmVyLnN0eWxlLndpZHRoID0gc2l6ZS54ICsgJ3B4Jztcblx0XHRjb250YWluZXIuc3R5bGUuaGVpZ2h0ID0gc2l6ZS55ICsgJ3B4JztcblxuXHRcdGlmIChMLkJyb3dzZXIucmV0aW5hKSB7XG5cdFx0XHR0aGlzLl9jdHguc2NhbGUoMiwgMik7XG5cdFx0fVxuXG5cdFx0Ly8gdHJhbnNsYXRlIHNvIHdlIHVzZSB0aGUgc2FtZSBwYXRoIGNvb3JkaW5hdGVzIGFmdGVyIGNhbnZhcyBlbGVtZW50IG1vdmVzXG5cdFx0dGhpcy5fY3R4LnRyYW5zbGF0ZSgtYi5taW4ueCwgLWIubWluLnkpO1xuXG5cdFx0Ly8gVGVsbCBwYXRocyB0byByZWRyYXcgdGhlbXNlbHZlc1xuXHRcdHRoaXMuZmlyZSgndXBkYXRlJyk7XG5cdH0sXG5cblx0X2luaXRQYXRoOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR0aGlzLl91cGRhdGVEYXNoQXJyYXkobGF5ZXIpO1xuXHRcdHRoaXMuX2xheWVyc1tMLnN0YW1wKGxheWVyKV0gPSBsYXllcjtcblxuXHRcdHZhciBvcmRlciA9IGxheWVyLl9vcmRlciA9IHtcblx0XHRcdGxheWVyOiBsYXllcixcblx0XHRcdHByZXY6IHRoaXMuX2RyYXdMYXN0LFxuXHRcdFx0bmV4dDogbnVsbFxuXHRcdH07XG5cdFx0aWYgKHRoaXMuX2RyYXdMYXN0KSB7IHRoaXMuX2RyYXdMYXN0Lm5leHQgPSBvcmRlcjsgfVxuXHRcdHRoaXMuX2RyYXdMYXN0ID0gb3JkZXI7XG5cdFx0dGhpcy5fZHJhd0ZpcnN0ID0gdGhpcy5fZHJhd0ZpcnN0IHx8IHRoaXMuX2RyYXdMYXN0O1xuXHR9LFxuXG5cdF9hZGRQYXRoOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR0aGlzLl9yZXF1ZXN0UmVkcmF3KGxheWVyKTtcblx0fSxcblxuXHRfcmVtb3ZlUGF0aDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIG9yZGVyID0gbGF5ZXIuX29yZGVyO1xuXHRcdHZhciBuZXh0ID0gb3JkZXIubmV4dDtcblx0XHR2YXIgcHJldiA9IG9yZGVyLnByZXY7XG5cblx0XHRpZiAobmV4dCkge1xuXHRcdFx0bmV4dC5wcmV2ID0gcHJldjtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy5fZHJhd0xhc3QgPSBwcmV2O1xuXHRcdH1cblx0XHRpZiAocHJldikge1xuXHRcdFx0cHJldi5uZXh0ID0gbmV4dDtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy5fZHJhd0ZpcnN0ID0gbmV4dDtcblx0XHR9XG5cblx0XHRkZWxldGUgbGF5ZXIuX29yZGVyO1xuXG5cdFx0ZGVsZXRlIHRoaXMuX2xheWVyc1tMLnN0YW1wKGxheWVyKV07XG5cblx0XHR0aGlzLl9yZXF1ZXN0UmVkcmF3KGxheWVyKTtcblx0fSxcblxuXHRfdXBkYXRlUGF0aDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0Ly8gUmVkcmF3IHRoZSB1bmlvbiBvZiB0aGUgbGF5ZXIncyBvbGQgcGl4ZWxcblx0XHQvLyBib3VuZHMgYW5kIHRoZSBuZXcgcGl4ZWwgYm91bmRzLlxuXHRcdHRoaXMuX2V4dGVuZFJlZHJhd0JvdW5kcyhsYXllcik7XG5cdFx0bGF5ZXIuX3Byb2plY3QoKTtcblx0XHRsYXllci5fdXBkYXRlKCk7XG5cdFx0Ly8gVGhlIHJlZHJhdyB3aWxsIGV4dGVuZCB0aGUgcmVkcmF3IGJvdW5kc1xuXHRcdC8vIHdpdGggdGhlIG5ldyBwaXhlbCBib3VuZHMuXG5cdFx0dGhpcy5fcmVxdWVzdFJlZHJhdyhsYXllcik7XG5cdH0sXG5cblx0X3VwZGF0ZVN0eWxlOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR0aGlzLl91cGRhdGVEYXNoQXJyYXkobGF5ZXIpO1xuXHRcdHRoaXMuX3JlcXVlc3RSZWRyYXcobGF5ZXIpO1xuXHR9LFxuXG5cdF91cGRhdGVEYXNoQXJyYXk6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdGlmIChsYXllci5vcHRpb25zLmRhc2hBcnJheSkge1xuXHRcdFx0dmFyIHBhcnRzID0gbGF5ZXIub3B0aW9ucy5kYXNoQXJyYXkuc3BsaXQoJywnKSxcblx0XHRcdCAgICBkYXNoQXJyYXkgPSBbXSxcblx0XHRcdCAgICBpO1xuXHRcdFx0Zm9yIChpID0gMDsgaSA8IHBhcnRzLmxlbmd0aDsgaSsrKSB7XG5cdFx0XHRcdGRhc2hBcnJheS5wdXNoKE51bWJlcihwYXJ0c1tpXSkpO1xuXHRcdFx0fVxuXHRcdFx0bGF5ZXIub3B0aW9ucy5fZGFzaEFycmF5ID0gZGFzaEFycmF5O1xuXHRcdH1cblx0fSxcblxuXHRfcmVxdWVzdFJlZHJhdzogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0aWYgKCF0aGlzLl9tYXApIHsgcmV0dXJuOyB9XG5cblx0XHR0aGlzLl9leHRlbmRSZWRyYXdCb3VuZHMobGF5ZXIpO1xuXHRcdHRoaXMuX3JlZHJhd1JlcXVlc3QgPSB0aGlzLl9yZWRyYXdSZXF1ZXN0IHx8IEwuVXRpbC5yZXF1ZXN0QW5pbUZyYW1lKHRoaXMuX3JlZHJhdywgdGhpcyk7XG5cdH0sXG5cblx0X2V4dGVuZFJlZHJhd0JvdW5kczogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIHBhZGRpbmcgPSAobGF5ZXIub3B0aW9ucy53ZWlnaHQgfHwgMCkgKyAxO1xuXHRcdHRoaXMuX3JlZHJhd0JvdW5kcyA9IHRoaXMuX3JlZHJhd0JvdW5kcyB8fCBuZXcgTC5Cb3VuZHMoKTtcblx0XHR0aGlzLl9yZWRyYXdCb3VuZHMuZXh0ZW5kKGxheWVyLl9weEJvdW5kcy5taW4uc3VidHJhY3QoW3BhZGRpbmcsIHBhZGRpbmddKSk7XG5cdFx0dGhpcy5fcmVkcmF3Qm91bmRzLmV4dGVuZChsYXllci5fcHhCb3VuZHMubWF4LmFkZChbcGFkZGluZywgcGFkZGluZ10pKTtcblx0fSxcblxuXHRfcmVkcmF3OiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fcmVkcmF3UmVxdWVzdCA9IG51bGw7XG5cblx0XHR0aGlzLl9jbGVhcigpOyAvLyBjbGVhciBsYXllcnMgaW4gcmVkcmF3IGJvdW5kc1xuXHRcdHRoaXMuX2RyYXcoKTsgLy8gZHJhdyBsYXllcnNcblxuXHRcdHRoaXMuX3JlZHJhd0JvdW5kcyA9IG51bGw7XG5cdH0sXG5cblx0X2NsZWFyOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIGJvdW5kcyA9IHRoaXMuX3JlZHJhd0JvdW5kcztcblx0XHRpZiAoYm91bmRzKSB7XG5cdFx0XHR2YXIgc2l6ZSA9IGJvdW5kcy5nZXRTaXplKCk7XG5cdFx0XHR0aGlzLl9jdHguY2xlYXJSZWN0KGJvdW5kcy5taW4ueCwgYm91bmRzLm1pbi55LCBzaXplLngsIHNpemUueSk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMuX2N0eC5jbGVhclJlY3QoMCwgMCwgdGhpcy5fY29udGFpbmVyLndpZHRoLCB0aGlzLl9jb250YWluZXIuaGVpZ2h0KTtcblx0XHR9XG5cdH0sXG5cblx0X2RyYXc6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgbGF5ZXIsIGJvdW5kcyA9IHRoaXMuX3JlZHJhd0JvdW5kcztcblx0XHR0aGlzLl9jdHguc2F2ZSgpO1xuXHRcdGlmIChib3VuZHMpIHtcblx0XHRcdHZhciBzaXplID0gYm91bmRzLmdldFNpemUoKTtcblx0XHRcdHRoaXMuX2N0eC5iZWdpblBhdGgoKTtcblx0XHRcdHRoaXMuX2N0eC5yZWN0KGJvdW5kcy5taW4ueCwgYm91bmRzLm1pbi55LCBzaXplLngsIHNpemUueSk7XG5cdFx0XHR0aGlzLl9jdHguY2xpcCgpO1xuXHRcdH1cblxuXHRcdHRoaXMuX2RyYXdpbmcgPSB0cnVlO1xuXG5cdFx0Zm9yICh2YXIgb3JkZXIgPSB0aGlzLl9kcmF3Rmlyc3Q7IG9yZGVyOyBvcmRlciA9IG9yZGVyLm5leHQpIHtcblx0XHRcdGxheWVyID0gb3JkZXIubGF5ZXI7XG5cdFx0XHRpZiAoIWJvdW5kcyB8fCAobGF5ZXIuX3B4Qm91bmRzICYmIGxheWVyLl9weEJvdW5kcy5pbnRlcnNlY3RzKGJvdW5kcykpKSB7XG5cdFx0XHRcdGxheWVyLl91cGRhdGVQYXRoKCk7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0dGhpcy5fZHJhd2luZyA9IGZhbHNlO1xuXG5cdFx0dGhpcy5fY3R4LnJlc3RvcmUoKTsgIC8vIFJlc3RvcmUgc3RhdGUgYmVmb3JlIGNsaXBwaW5nLlxuXHR9LFxuXG5cdF91cGRhdGVQb2x5OiBmdW5jdGlvbiAobGF5ZXIsIGNsb3NlZCkge1xuXHRcdGlmICghdGhpcy5fZHJhd2luZykgeyByZXR1cm47IH1cblxuXHRcdHZhciBpLCBqLCBsZW4yLCBwLFxuXHRcdCAgICBwYXJ0cyA9IGxheWVyLl9wYXJ0cyxcblx0XHQgICAgbGVuID0gcGFydHMubGVuZ3RoLFxuXHRcdCAgICBjdHggPSB0aGlzLl9jdHg7XG5cblx0XHRpZiAoIWxlbikgeyByZXR1cm47IH1cblxuXHRcdHRoaXMuX2RyYXduTGF5ZXJzW2xheWVyLl9sZWFmbGV0X2lkXSA9IGxheWVyO1xuXG5cdFx0Y3R4LmJlZ2luUGF0aCgpO1xuXG5cdFx0aWYgKGN0eC5zZXRMaW5lRGFzaCkge1xuXHRcdFx0Y3R4LnNldExpbmVEYXNoKGxheWVyLm9wdGlvbnMgJiYgbGF5ZXIub3B0aW9ucy5fZGFzaEFycmF5IHx8IFtdKTtcblx0XHR9XG5cblx0XHRmb3IgKGkgPSAwOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdGZvciAoaiA9IDAsIGxlbjIgPSBwYXJ0c1tpXS5sZW5ndGg7IGogPCBsZW4yOyBqKyspIHtcblx0XHRcdFx0cCA9IHBhcnRzW2ldW2pdO1xuXHRcdFx0XHRjdHhbaiA/ICdsaW5lVG8nIDogJ21vdmVUbyddKHAueCwgcC55KTtcblx0XHRcdH1cblx0XHRcdGlmIChjbG9zZWQpIHtcblx0XHRcdFx0Y3R4LmNsb3NlUGF0aCgpO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHRoaXMuX2ZpbGxTdHJva2UoY3R4LCBsYXllcik7XG5cblx0XHQvLyBUT0RPIG9wdGltaXphdGlvbjogMSBmaWxsL3N0cm9rZSBmb3IgYWxsIGZlYXR1cmVzIHdpdGggZXF1YWwgc3R5bGUgaW5zdGVhZCBvZiAxIGZvciBlYWNoIGZlYXR1cmVcblx0fSxcblxuXHRfdXBkYXRlQ2lyY2xlOiBmdW5jdGlvbiAobGF5ZXIpIHtcblxuXHRcdGlmICghdGhpcy5fZHJhd2luZyB8fCBsYXllci5fZW1wdHkoKSkgeyByZXR1cm47IH1cblxuXHRcdHZhciBwID0gbGF5ZXIuX3BvaW50LFxuXHRcdCAgICBjdHggPSB0aGlzLl9jdHgsXG5cdFx0ICAgIHIgPSBsYXllci5fcmFkaXVzLFxuXHRcdCAgICBzID0gKGxheWVyLl9yYWRpdXNZIHx8IHIpIC8gcjtcblxuXHRcdHRoaXMuX2RyYXduTGF5ZXJzW2xheWVyLl9sZWFmbGV0X2lkXSA9IGxheWVyO1xuXG5cdFx0aWYgKHMgIT09IDEpIHtcblx0XHRcdGN0eC5zYXZlKCk7XG5cdFx0XHRjdHguc2NhbGUoMSwgcyk7XG5cdFx0fVxuXG5cdFx0Y3R4LmJlZ2luUGF0aCgpO1xuXHRcdGN0eC5hcmMocC54LCBwLnkgLyBzLCByLCAwLCBNYXRoLlBJICogMiwgZmFsc2UpO1xuXG5cdFx0aWYgKHMgIT09IDEpIHtcblx0XHRcdGN0eC5yZXN0b3JlKCk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fZmlsbFN0cm9rZShjdHgsIGxheWVyKTtcblx0fSxcblxuXHRfZmlsbFN0cm9rZTogZnVuY3Rpb24gKGN0eCwgbGF5ZXIpIHtcblx0XHR2YXIgb3B0aW9ucyA9IGxheWVyLm9wdGlvbnM7XG5cblx0XHRpZiAob3B0aW9ucy5maWxsKSB7XG5cdFx0XHRjdHguZ2xvYmFsQWxwaGEgPSBvcHRpb25zLmZpbGxPcGFjaXR5O1xuXHRcdFx0Y3R4LmZpbGxTdHlsZSA9IG9wdGlvbnMuZmlsbENvbG9yIHx8IG9wdGlvbnMuY29sb3I7XG5cdFx0XHRjdHguZmlsbChvcHRpb25zLmZpbGxSdWxlIHx8ICdldmVub2RkJyk7XG5cdFx0fVxuXG5cdFx0aWYgKG9wdGlvbnMuc3Ryb2tlICYmIG9wdGlvbnMud2VpZ2h0ICE9PSAwKSB7XG5cdFx0XHRjdHguZ2xvYmFsQWxwaGEgPSBvcHRpb25zLm9wYWNpdHk7XG5cdFx0XHRjdHgubGluZVdpZHRoID0gb3B0aW9ucy53ZWlnaHQ7XG5cdFx0XHRjdHguc3Ryb2tlU3R5bGUgPSBvcHRpb25zLmNvbG9yO1xuXHRcdFx0Y3R4LmxpbmVDYXAgPSBvcHRpb25zLmxpbmVDYXA7XG5cdFx0XHRjdHgubGluZUpvaW4gPSBvcHRpb25zLmxpbmVKb2luO1xuXHRcdFx0Y3R4LnN0cm9rZSgpO1xuXHRcdH1cblx0fSxcblxuXHQvLyBDYW52YXMgb2J2aW91c2x5IGRvZXNuJ3QgaGF2ZSBtb3VzZSBldmVudHMgZm9yIGluZGl2aWR1YWwgZHJhd24gb2JqZWN0cyxcblx0Ly8gc28gd2UgZW11bGF0ZSB0aGF0IGJ5IGNhbGN1bGF0aW5nIHdoYXQncyB1bmRlciB0aGUgbW91c2Ugb24gbW91c2Vtb3ZlL2NsaWNrIG1hbnVhbGx5XG5cblx0X29uQ2xpY2s6IGZ1bmN0aW9uIChlKSB7XG5cdFx0dmFyIHBvaW50ID0gdGhpcy5fbWFwLm1vdXNlRXZlbnRUb0xheWVyUG9pbnQoZSksIGxheWVyLCBjbGlja2VkTGF5ZXI7XG5cblx0XHRmb3IgKHZhciBvcmRlciA9IHRoaXMuX2RyYXdGaXJzdDsgb3JkZXI7IG9yZGVyID0gb3JkZXIubmV4dCkge1xuXHRcdFx0bGF5ZXIgPSBvcmRlci5sYXllcjtcblx0XHRcdGlmIChsYXllci5vcHRpb25zLmludGVyYWN0aXZlICYmIGxheWVyLl9jb250YWluc1BvaW50KHBvaW50KSAmJiAhdGhpcy5fbWFwLl9kcmFnZ2FibGVNb3ZlZChsYXllcikpIHtcblx0XHRcdFx0Y2xpY2tlZExheWVyID0gbGF5ZXI7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdGlmIChjbGlja2VkTGF5ZXIpICB7XG5cdFx0XHRMLkRvbUV2ZW50Ll9mYWtlU3RvcChlKTtcblx0XHRcdHRoaXMuX2ZpcmVFdmVudChbY2xpY2tlZExheWVyXSwgZSk7XG5cdFx0fVxuXHR9LFxuXG5cdF9vbk1vdXNlTW92ZTogZnVuY3Rpb24gKGUpIHtcblx0XHRpZiAoIXRoaXMuX21hcCB8fCB0aGlzLl9tYXAuZHJhZ2dpbmcubW92aW5nKCkgfHwgdGhpcy5fbWFwLl9hbmltYXRpbmdab29tKSB7IHJldHVybjsgfVxuXG5cdFx0dmFyIHBvaW50ID0gdGhpcy5fbWFwLm1vdXNlRXZlbnRUb0xheWVyUG9pbnQoZSk7XG5cdFx0dGhpcy5faGFuZGxlTW91c2VIb3ZlcihlLCBwb2ludCk7XG5cdH0sXG5cblxuXHRfaGFuZGxlTW91c2VPdXQ6IGZ1bmN0aW9uIChlKSB7XG5cdFx0dmFyIGxheWVyID0gdGhpcy5faG92ZXJlZExheWVyO1xuXHRcdGlmIChsYXllcikge1xuXHRcdFx0Ly8gaWYgd2UncmUgbGVhdmluZyB0aGUgbGF5ZXIsIGZpcmUgbW91c2VvdXRcblx0XHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9jb250YWluZXIsICdsZWFmbGV0LWludGVyYWN0aXZlJyk7XG5cdFx0XHR0aGlzLl9maXJlRXZlbnQoW2xheWVyXSwgZSwgJ21vdXNlb3V0Jyk7XG5cdFx0XHR0aGlzLl9ob3ZlcmVkTGF5ZXIgPSBudWxsO1xuXHRcdH1cblx0fSxcblxuXHRfaGFuZGxlTW91c2VIb3ZlcjogZnVuY3Rpb24gKGUsIHBvaW50KSB7XG5cdFx0dmFyIGxheWVyLCBjYW5kaWRhdGVIb3ZlcmVkTGF5ZXI7XG5cblx0XHRmb3IgKHZhciBvcmRlciA9IHRoaXMuX2RyYXdGaXJzdDsgb3JkZXI7IG9yZGVyID0gb3JkZXIubmV4dCkge1xuXHRcdFx0bGF5ZXIgPSBvcmRlci5sYXllcjtcblx0XHRcdGlmIChsYXllci5vcHRpb25zLmludGVyYWN0aXZlICYmIGxheWVyLl9jb250YWluc1BvaW50KHBvaW50KSkge1xuXHRcdFx0XHRjYW5kaWRhdGVIb3ZlcmVkTGF5ZXIgPSBsYXllcjtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiAoY2FuZGlkYXRlSG92ZXJlZExheWVyICE9PSB0aGlzLl9ob3ZlcmVkTGF5ZXIpIHtcblx0XHRcdHRoaXMuX2hhbmRsZU1vdXNlT3V0KGUpO1xuXG5cdFx0XHRpZiAoY2FuZGlkYXRlSG92ZXJlZExheWVyKSB7XG5cdFx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9jb250YWluZXIsICdsZWFmbGV0LWludGVyYWN0aXZlJyk7IC8vIGNoYW5nZSBjdXJzb3Jcblx0XHRcdFx0dGhpcy5fZmlyZUV2ZW50KFtjYW5kaWRhdGVIb3ZlcmVkTGF5ZXJdLCBlLCAnbW91c2VvdmVyJyk7XG5cdFx0XHRcdHRoaXMuX2hvdmVyZWRMYXllciA9IGNhbmRpZGF0ZUhvdmVyZWRMYXllcjtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiAodGhpcy5faG92ZXJlZExheWVyKSB7XG5cdFx0XHR0aGlzLl9maXJlRXZlbnQoW3RoaXMuX2hvdmVyZWRMYXllcl0sIGUpO1xuXHRcdH1cblx0fSxcblxuXHRfZmlyZUV2ZW50OiBmdW5jdGlvbiAobGF5ZXJzLCBlLCB0eXBlKSB7XG5cdFx0dGhpcy5fbWFwLl9maXJlRE9NRXZlbnQoZSwgdHlwZSB8fCBlLnR5cGUsIGxheWVycyk7XG5cdH0sXG5cblx0X2JyaW5nVG9Gcm9udDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIG9yZGVyID0gbGF5ZXIuX29yZGVyO1xuXHRcdHZhciBuZXh0ID0gb3JkZXIubmV4dDtcblx0XHR2YXIgcHJldiA9IG9yZGVyLnByZXY7XG5cblx0XHRpZiAobmV4dCkge1xuXHRcdFx0bmV4dC5wcmV2ID0gcHJldjtcblx0XHR9IGVsc2Uge1xuXHRcdFx0Ly8gQWxyZWFkeSBsYXN0XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdGlmIChwcmV2KSB7XG5cdFx0XHRwcmV2Lm5leHQgPSBuZXh0O1xuXHRcdH0gZWxzZSBpZiAobmV4dCkge1xuXHRcdFx0Ly8gVXBkYXRlIGZpcnN0IGVudHJ5IHVubGVzcyB0aGlzIGlzIHRoZVxuXHRcdFx0Ly8gc2lnbmxlIGVudHJ5XG5cdFx0XHR0aGlzLl9kcmF3Rmlyc3QgPSBuZXh0O1xuXHRcdH1cblxuXHRcdG9yZGVyLnByZXYgPSB0aGlzLl9kcmF3TGFzdDtcblx0XHR0aGlzLl9kcmF3TGFzdC5uZXh0ID0gb3JkZXI7XG5cblx0XHRvcmRlci5uZXh0ID0gbnVsbDtcblx0XHR0aGlzLl9kcmF3TGFzdCA9IG9yZGVyO1xuXG5cdFx0dGhpcy5fcmVxdWVzdFJlZHJhdyhsYXllcik7XG5cdH0sXG5cblx0X2JyaW5nVG9CYWNrOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR2YXIgb3JkZXIgPSBsYXllci5fb3JkZXI7XG5cdFx0dmFyIG5leHQgPSBvcmRlci5uZXh0O1xuXHRcdHZhciBwcmV2ID0gb3JkZXIucHJldjtcblxuXHRcdGlmIChwcmV2KSB7XG5cdFx0XHRwcmV2Lm5leHQgPSBuZXh0O1xuXHRcdH0gZWxzZSB7XG5cdFx0XHQvLyBBbHJlYWR5IGZpcnN0XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdGlmIChuZXh0KSB7XG5cdFx0XHRuZXh0LnByZXYgPSBwcmV2O1xuXHRcdH0gZWxzZSBpZiAocHJldikge1xuXHRcdFx0Ly8gVXBkYXRlIGxhc3QgZW50cnkgdW5sZXNzIHRoaXMgaXMgdGhlXG5cdFx0XHQvLyBzaWdubGUgZW50cnlcblx0XHRcdHRoaXMuX2RyYXdMYXN0ID0gcHJldjtcblx0XHR9XG5cblx0XHRvcmRlci5wcmV2ID0gbnVsbDtcblxuXHRcdG9yZGVyLm5leHQgPSB0aGlzLl9kcmF3Rmlyc3Q7XG5cdFx0dGhpcy5fZHJhd0ZpcnN0LnByZXYgPSBvcmRlcjtcblx0XHR0aGlzLl9kcmF3Rmlyc3QgPSBvcmRlcjtcblxuXHRcdHRoaXMuX3JlcXVlc3RSZWRyYXcobGF5ZXIpO1xuXHR9XG59KTtcblxuLy8gQG5hbWVzcGFjZSBCcm93c2VyOyBAcHJvcGVydHkgY2FudmFzOiBCb29sZWFuXG4vLyBgdHJ1ZWAgd2hlbiB0aGUgYnJvd3NlciBzdXBwb3J0cyBbYDxjYW52YXM+YF0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvQVBJL0NhbnZhc19BUEkpLlxuTC5Ccm93c2VyLmNhbnZhcyA9IChmdW5jdGlvbiAoKSB7XG5cdHJldHVybiAhIWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpLmdldENvbnRleHQ7XG59KCkpO1xuXG4vLyBAbmFtZXNwYWNlIENhbnZhc1xuLy8gQGZhY3RvcnkgTC5jYW52YXMob3B0aW9ucz86IFJlbmRlcmVyIG9wdGlvbnMpXG4vLyBDcmVhdGVzIGEgQ2FudmFzIHJlbmRlcmVyIHdpdGggdGhlIGdpdmVuIG9wdGlvbnMuXG5MLmNhbnZhcyA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XG5cdHJldHVybiBMLkJyb3dzZXIuY2FudmFzID8gbmV3IEwuQ2FudmFzKG9wdGlvbnMpIDogbnVsbDtcbn07XG5cbkwuUG9seWxpbmUucHJvdG90eXBlLl9jb250YWluc1BvaW50ID0gZnVuY3Rpb24gKHAsIGNsb3NlZCkge1xuXHR2YXIgaSwgaiwgaywgbGVuLCBsZW4yLCBwYXJ0LFxuXHQgICAgdyA9IHRoaXMuX2NsaWNrVG9sZXJhbmNlKCk7XG5cblx0aWYgKCF0aGlzLl9weEJvdW5kcy5jb250YWlucyhwKSkgeyByZXR1cm4gZmFsc2U7IH1cblxuXHQvLyBoaXQgZGV0ZWN0aW9uIGZvciBwb2x5bGluZXNcblx0Zm9yIChpID0gMCwgbGVuID0gdGhpcy5fcGFydHMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcblx0XHRwYXJ0ID0gdGhpcy5fcGFydHNbaV07XG5cblx0XHRmb3IgKGogPSAwLCBsZW4yID0gcGFydC5sZW5ndGgsIGsgPSBsZW4yIC0gMTsgaiA8IGxlbjI7IGsgPSBqKyspIHtcblx0XHRcdGlmICghY2xvc2VkICYmIChqID09PSAwKSkgeyBjb250aW51ZTsgfVxuXG5cdFx0XHRpZiAoTC5MaW5lVXRpbC5wb2ludFRvU2VnbWVudERpc3RhbmNlKHAsIHBhcnRba10sIHBhcnRbal0pIDw9IHcpIHtcblx0XHRcdFx0cmV0dXJuIHRydWU7XG5cdFx0XHR9XG5cdFx0fVxuXHR9XG5cdHJldHVybiBmYWxzZTtcbn07XG5cbkwuUG9seWdvbi5wcm90b3R5cGUuX2NvbnRhaW5zUG9pbnQgPSBmdW5jdGlvbiAocCkge1xuXHR2YXIgaW5zaWRlID0gZmFsc2UsXG5cdCAgICBwYXJ0LCBwMSwgcDIsIGksIGosIGssIGxlbiwgbGVuMjtcblxuXHRpZiAoIXRoaXMuX3B4Qm91bmRzLmNvbnRhaW5zKHApKSB7IHJldHVybiBmYWxzZTsgfVxuXG5cdC8vIHJheSBjYXN0aW5nIGFsZ29yaXRobSBmb3IgZGV0ZWN0aW5nIGlmIHBvaW50IGlzIGluIHBvbHlnb25cblx0Zm9yIChpID0gMCwgbGVuID0gdGhpcy5fcGFydHMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcblx0XHRwYXJ0ID0gdGhpcy5fcGFydHNbaV07XG5cblx0XHRmb3IgKGogPSAwLCBsZW4yID0gcGFydC5sZW5ndGgsIGsgPSBsZW4yIC0gMTsgaiA8IGxlbjI7IGsgPSBqKyspIHtcblx0XHRcdHAxID0gcGFydFtqXTtcblx0XHRcdHAyID0gcGFydFtrXTtcblxuXHRcdFx0aWYgKCgocDEueSA+IHAueSkgIT09IChwMi55ID4gcC55KSkgJiYgKHAueCA8IChwMi54IC0gcDEueCkgKiAocC55IC0gcDEueSkgLyAocDIueSAtIHAxLnkpICsgcDEueCkpIHtcblx0XHRcdFx0aW5zaWRlID0gIWluc2lkZTtcblx0XHRcdH1cblx0XHR9XG5cdH1cblxuXHQvLyBhbHNvIGNoZWNrIGlmIGl0J3Mgb24gcG9seWdvbiBzdHJva2Vcblx0cmV0dXJuIGluc2lkZSB8fCBMLlBvbHlsaW5lLnByb3RvdHlwZS5fY29udGFpbnNQb2ludC5jYWxsKHRoaXMsIHAsIHRydWUpO1xufTtcblxuTC5DaXJjbGVNYXJrZXIucHJvdG90eXBlLl9jb250YWluc1BvaW50ID0gZnVuY3Rpb24gKHApIHtcblx0cmV0dXJuIHAuZGlzdGFuY2VUbyh0aGlzLl9wb2ludCkgPD0gdGhpcy5fcmFkaXVzICsgdGhpcy5fY2xpY2tUb2xlcmFuY2UoKTtcbn07XG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgR2VvSlNPTlxyXG4gKiBAYWthIEwuR2VvSlNPTlxyXG4gKiBAaW5oZXJpdHMgRmVhdHVyZUdyb3VwXHJcbiAqXHJcbiAqIFJlcHJlc2VudHMgYSBHZW9KU09OIG9iamVjdCBvciBhbiBhcnJheSBvZiBHZW9KU09OIG9iamVjdHMuIEFsbG93cyB5b3UgdG8gcGFyc2VcclxuICogR2VvSlNPTiBkYXRhIGFuZCBkaXNwbGF5IGl0IG9uIHRoZSBtYXAuIEV4dGVuZHMgYEZlYXR1cmVHcm91cGAuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIEwuZ2VvSlNPTihkYXRhLCB7XHJcbiAqIFx0c3R5bGU6IGZ1bmN0aW9uIChmZWF0dXJlKSB7XHJcbiAqIFx0XHRyZXR1cm4ge2NvbG9yOiBmZWF0dXJlLnByb3BlcnRpZXMuY29sb3J9O1xyXG4gKiBcdH1cclxuICogfSkuYmluZFBvcHVwKGZ1bmN0aW9uIChsYXllcikge1xyXG4gKiBcdHJldHVybiBsYXllci5mZWF0dXJlLnByb3BlcnRpZXMuZGVzY3JpcHRpb247XHJcbiAqIH0pLmFkZFRvKG1hcCk7XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcbkwuR2VvSlNPTiA9IEwuRmVhdHVyZUdyb3VwLmV4dGVuZCh7XHJcblxyXG5cdC8qIEBzZWN0aW9uXHJcblx0ICogQGFrYSBHZW9KU09OIG9wdGlvbnNcclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gcG9pbnRUb0xheWVyOiBGdW5jdGlvbiA9ICpcclxuXHQgKiBBIGBGdW5jdGlvbmAgZGVmaW5pbmcgaG93IEdlb0pTT04gcG9pbnRzIHNwYXduIExlYWZsZXQgbGF5ZXJzLiBJdCBpcyBpbnRlcm5hbGx5XHJcblx0ICogY2FsbGVkIHdoZW4gZGF0YSBpcyBhZGRlZCwgcGFzc2luZyB0aGUgR2VvSlNPTiBwb2ludCBmZWF0dXJlIGFuZCBpdHMgYExhdExuZ2AuXHJcblx0ICogVGhlIGRlZmF1bHQgaXMgdG8gc3Bhd24gYSBkZWZhdWx0IGBNYXJrZXJgOlxyXG5cdCAqIGBgYGpzXHJcblx0ICogZnVuY3Rpb24oZ2VvSnNvblBvaW50LCBsYXRsbmcpIHtcclxuXHQgKiBcdHJldHVybiBMLm1hcmtlcihsYXRsbmcpO1xyXG5cdCAqIH1cclxuXHQgKiBgYGBcclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gc3R5bGU6IEZ1bmN0aW9uID0gKlxyXG5cdCAqIEEgYEZ1bmN0aW9uYCBkZWZpbmluZyB0aGUgYFBhdGggb3B0aW9uc2AgZm9yIHN0eWxpbmcgR2VvSlNPTiBsaW5lcyBhbmQgcG9seWdvbnMsXHJcblx0ICogY2FsbGVkIGludGVybmFsbHkgd2hlbiBkYXRhIGlzIGFkZGVkLlxyXG5cdCAqIFRoZSBkZWZhdWx0IHZhbHVlIGlzIHRvIG5vdCBvdmVycmlkZSBhbnkgZGVmYXVsdHM6XHJcblx0ICogYGBganNcclxuXHQgKiBmdW5jdGlvbiAoZ2VvSnNvbkZlYXR1cmUpIHtcclxuXHQgKiBcdHJldHVybiB7fVxyXG5cdCAqIH1cclxuXHQgKiBgYGBcclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gb25FYWNoRmVhdHVyZTogRnVuY3Rpb24gPSAqXHJcblx0ICogQSBgRnVuY3Rpb25gIHRoYXQgd2lsbCBiZSBjYWxsZWQgb25jZSBmb3IgZWFjaCBjcmVhdGVkIGBGZWF0dXJlYCwgYWZ0ZXIgaXQgaGFzXHJcblx0ICogYmVlbiBjcmVhdGVkIGFuZCBzdHlsZWQuIFVzZWZ1bCBmb3IgYXR0YWNoaW5nIGV2ZW50cyBhbmQgcG9wdXBzIHRvIGZlYXR1cmVzLlxyXG5cdCAqIFRoZSBkZWZhdWx0IGlzIHRvIGRvIG5vdGhpbmcgd2l0aCB0aGUgbmV3bHkgY3JlYXRlZCBsYXllcnM6XHJcblx0ICogYGBganNcclxuXHQgKiBmdW5jdGlvbiAoZmVhdHVyZSwgbGF5ZXIpIHt9XHJcblx0ICogYGBgXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIGZpbHRlcjogRnVuY3Rpb24gPSAqXHJcblx0ICogQSBgRnVuY3Rpb25gIHRoYXQgd2lsbCBiZSB1c2VkIHRvIGRlY2lkZSB3aGV0aGVyIHRvIGluY2x1ZGUgYSBmZWF0dXJlIG9yIG5vdC5cclxuXHQgKiBUaGUgZGVmYXVsdCBpcyB0byBpbmNsdWRlIGFsbCBmZWF0dXJlczpcclxuXHQgKiBgYGBqc1xyXG5cdCAqIGZ1bmN0aW9uIChnZW9Kc29uRmVhdHVyZSkge1xyXG5cdCAqIFx0cmV0dXJuIHRydWU7XHJcblx0ICogfVxyXG5cdCAqIGBgYFxyXG5cdCAqIE5vdGU6IGR5bmFtaWNhbGx5IGNoYW5naW5nIHRoZSBgZmlsdGVyYCBvcHRpb24gd2lsbCBoYXZlIGVmZmVjdCBvbmx5IG9uIG5ld2x5XHJcblx0ICogYWRkZWQgZGF0YS4gSXQgd2lsbCBfbm90XyByZS1ldmFsdWF0ZSBhbHJlYWR5IGluY2x1ZGVkIGZlYXR1cmVzLlxyXG5cdCAqXHJcblx0ICogQG9wdGlvbiBjb29yZHNUb0xhdExuZzogRnVuY3Rpb24gPSAqXHJcblx0ICogQSBgRnVuY3Rpb25gIHRoYXQgd2lsbCBiZSB1c2VkIGZvciBjb252ZXJ0aW5nIEdlb0pTT04gY29vcmRpbmF0ZXMgdG8gYExhdExuZ2BzLlxyXG5cdCAqIFRoZSBkZWZhdWx0IGlzIHRoZSBgY29vcmRzVG9MYXRMbmdgIHN0YXRpYyBtZXRob2QuXHJcblx0ICovXHJcblxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChnZW9qc29uLCBvcHRpb25zKSB7XHJcblx0XHRMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XHJcblxyXG5cdFx0dGhpcy5fbGF5ZXJzID0ge307XHJcblxyXG5cdFx0aWYgKGdlb2pzb24pIHtcclxuXHRcdFx0dGhpcy5hZGREYXRhKGdlb2pzb24pO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgYWRkRGF0YSggPEdlb0pTT04+IGRhdGEgKTogdGhpc1xyXG5cdC8vIEFkZHMgYSBHZW9KU09OIG9iamVjdCB0byB0aGUgbGF5ZXIuXHJcblx0YWRkRGF0YTogZnVuY3Rpb24gKGdlb2pzb24pIHtcclxuXHRcdHZhciBmZWF0dXJlcyA9IEwuVXRpbC5pc0FycmF5KGdlb2pzb24pID8gZ2VvanNvbiA6IGdlb2pzb24uZmVhdHVyZXMsXHJcblx0XHQgICAgaSwgbGVuLCBmZWF0dXJlO1xyXG5cclxuXHRcdGlmIChmZWF0dXJlcykge1xyXG5cdFx0XHRmb3IgKGkgPSAwLCBsZW4gPSBmZWF0dXJlcy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRcdC8vIG9ubHkgYWRkIHRoaXMgaWYgZ2VvbWV0cnkgb3IgZ2VvbWV0cmllcyBhcmUgc2V0IGFuZCBub3QgbnVsbFxyXG5cdFx0XHRcdGZlYXR1cmUgPSBmZWF0dXJlc1tpXTtcclxuXHRcdFx0XHRpZiAoZmVhdHVyZS5nZW9tZXRyaWVzIHx8IGZlYXR1cmUuZ2VvbWV0cnkgfHwgZmVhdHVyZS5mZWF0dXJlcyB8fCBmZWF0dXJlLmNvb3JkaW5hdGVzKSB7XHJcblx0XHRcdFx0XHR0aGlzLmFkZERhdGEoZmVhdHVyZSk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHRcdHJldHVybiB0aGlzO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBvcHRpb25zID0gdGhpcy5vcHRpb25zO1xyXG5cclxuXHRcdGlmIChvcHRpb25zLmZpbHRlciAmJiAhb3B0aW9ucy5maWx0ZXIoZ2VvanNvbikpIHsgcmV0dXJuIHRoaXM7IH1cclxuXHJcblx0XHR2YXIgbGF5ZXIgPSBMLkdlb0pTT04uZ2VvbWV0cnlUb0xheWVyKGdlb2pzb24sIG9wdGlvbnMpO1xyXG5cdFx0aWYgKCFsYXllcikge1xyXG5cdFx0XHRyZXR1cm4gdGhpcztcclxuXHRcdH1cclxuXHRcdGxheWVyLmZlYXR1cmUgPSBMLkdlb0pTT04uYXNGZWF0dXJlKGdlb2pzb24pO1xyXG5cclxuXHRcdGxheWVyLmRlZmF1bHRPcHRpb25zID0gbGF5ZXIub3B0aW9ucztcclxuXHRcdHRoaXMucmVzZXRTdHlsZShsYXllcik7XHJcblxyXG5cdFx0aWYgKG9wdGlvbnMub25FYWNoRmVhdHVyZSkge1xyXG5cdFx0XHRvcHRpb25zLm9uRWFjaEZlYXR1cmUoZ2VvanNvbiwgbGF5ZXIpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzLmFkZExheWVyKGxheWVyKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHJlc2V0U3R5bGUoIDxQYXRoPiBsYXllciApOiB0aGlzXHJcblx0Ly8gUmVzZXRzIHRoZSBnaXZlbiB2ZWN0b3IgbGF5ZXIncyBzdHlsZSB0byB0aGUgb3JpZ2luYWwgR2VvSlNPTiBzdHlsZSwgdXNlZnVsIGZvciByZXNldHRpbmcgc3R5bGUgYWZ0ZXIgaG92ZXIgZXZlbnRzLlxyXG5cdHJlc2V0U3R5bGU6IGZ1bmN0aW9uIChsYXllcikge1xyXG5cdFx0Ly8gcmVzZXQgYW55IGN1c3RvbSBzdHlsZXNcclxuXHRcdGxheWVyLm9wdGlvbnMgPSBMLlV0aWwuZXh0ZW5kKHt9LCBsYXllci5kZWZhdWx0T3B0aW9ucyk7XHJcblx0XHR0aGlzLl9zZXRMYXllclN0eWxlKGxheWVyLCB0aGlzLm9wdGlvbnMuc3R5bGUpO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzZXRTdHlsZSggPEZ1bmN0aW9uPiBzdHlsZSApOiB0aGlzXHJcblx0Ly8gQ2hhbmdlcyBzdHlsZXMgb2YgR2VvSlNPTiB2ZWN0b3IgbGF5ZXJzIHdpdGggdGhlIGdpdmVuIHN0eWxlIGZ1bmN0aW9uLlxyXG5cdHNldFN0eWxlOiBmdW5jdGlvbiAoc3R5bGUpIHtcclxuXHRcdHJldHVybiB0aGlzLmVhY2hMYXllcihmdW5jdGlvbiAobGF5ZXIpIHtcclxuXHRcdFx0dGhpcy5fc2V0TGF5ZXJTdHlsZShsYXllciwgc3R5bGUpO1xyXG5cdFx0fSwgdGhpcyk7XHJcblx0fSxcclxuXHJcblx0X3NldExheWVyU3R5bGU6IGZ1bmN0aW9uIChsYXllciwgc3R5bGUpIHtcclxuXHRcdGlmICh0eXBlb2Ygc3R5bGUgPT09ICdmdW5jdGlvbicpIHtcclxuXHRcdFx0c3R5bGUgPSBzdHlsZShsYXllci5mZWF0dXJlKTtcclxuXHRcdH1cclxuXHRcdGlmIChsYXllci5zZXRTdHlsZSkge1xyXG5cdFx0XHRsYXllci5zZXRTdHlsZShzdHlsZSk7XHJcblx0XHR9XHJcblx0fVxyXG59KTtcclxuXHJcbi8vIEBzZWN0aW9uXHJcbi8vIFRoZXJlIGFyZSBzZXZlcmFsIHN0YXRpYyBmdW5jdGlvbnMgd2hpY2ggY2FuIGJlIGNhbGxlZCB3aXRob3V0IGluc3RhbnRpYXRpbmcgTC5HZW9KU09OOlxyXG5MLmV4dGVuZChMLkdlb0pTT04sIHtcclxuXHQvLyBAZnVuY3Rpb24gZ2VvbWV0cnlUb0xheWVyKGZlYXR1cmVEYXRhOiBPYmplY3QsIG9wdGlvbnM/OiBHZW9KU09OIG9wdGlvbnMpOiBMYXllclxyXG5cdC8vIENyZWF0ZXMgYSBgTGF5ZXJgIGZyb20gYSBnaXZlbiBHZW9KU09OIGZlYXR1cmUuIENhbiB1c2UgYSBjdXN0b21cclxuXHQvLyBbYHBvaW50VG9MYXllcmBdKCNnZW9qc29uLXBvaW50dG9sYXllcikgYW5kL29yIFtgY29vcmRzVG9MYXRMbmdgXSgjZ2VvanNvbi1jb29yZHN0b2xhdGxuZylcclxuXHQvLyBmdW5jdGlvbnMgaWYgcHJvdmlkZWQgYXMgb3B0aW9ucy5cclxuXHRnZW9tZXRyeVRvTGF5ZXI6IGZ1bmN0aW9uIChnZW9qc29uLCBvcHRpb25zKSB7XHJcblxyXG5cdFx0dmFyIGdlb21ldHJ5ID0gZ2VvanNvbi50eXBlID09PSAnRmVhdHVyZScgPyBnZW9qc29uLmdlb21ldHJ5IDogZ2VvanNvbixcclxuXHRcdCAgICBjb29yZHMgPSBnZW9tZXRyeSA/IGdlb21ldHJ5LmNvb3JkaW5hdGVzIDogbnVsbCxcclxuXHRcdCAgICBsYXllcnMgPSBbXSxcclxuXHRcdCAgICBwb2ludFRvTGF5ZXIgPSBvcHRpb25zICYmIG9wdGlvbnMucG9pbnRUb0xheWVyLFxyXG5cdFx0ICAgIGNvb3Jkc1RvTGF0TG5nID0gb3B0aW9ucyAmJiBvcHRpb25zLmNvb3Jkc1RvTGF0TG5nIHx8IHRoaXMuY29vcmRzVG9MYXRMbmcsXHJcblx0XHQgICAgbGF0bG5nLCBsYXRsbmdzLCBpLCBsZW47XHJcblxyXG5cdFx0aWYgKCFjb29yZHMgJiYgIWdlb21ldHJ5KSB7XHJcblx0XHRcdHJldHVybiBudWxsO1xyXG5cdFx0fVxyXG5cclxuXHRcdHN3aXRjaCAoZ2VvbWV0cnkudHlwZSkge1xyXG5cdFx0Y2FzZSAnUG9pbnQnOlxyXG5cdFx0XHRsYXRsbmcgPSBjb29yZHNUb0xhdExuZyhjb29yZHMpO1xyXG5cdFx0XHRyZXR1cm4gcG9pbnRUb0xheWVyID8gcG9pbnRUb0xheWVyKGdlb2pzb24sIGxhdGxuZykgOiBuZXcgTC5NYXJrZXIobGF0bG5nKTtcclxuXHJcblx0XHRjYXNlICdNdWx0aVBvaW50JzpcclxuXHRcdFx0Zm9yIChpID0gMCwgbGVuID0gY29vcmRzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdFx0bGF0bG5nID0gY29vcmRzVG9MYXRMbmcoY29vcmRzW2ldKTtcclxuXHRcdFx0XHRsYXllcnMucHVzaChwb2ludFRvTGF5ZXIgPyBwb2ludFRvTGF5ZXIoZ2VvanNvbiwgbGF0bG5nKSA6IG5ldyBMLk1hcmtlcihsYXRsbmcpKTtcclxuXHRcdFx0fVxyXG5cdFx0XHRyZXR1cm4gbmV3IEwuRmVhdHVyZUdyb3VwKGxheWVycyk7XHJcblxyXG5cdFx0Y2FzZSAnTGluZVN0cmluZyc6XHJcblx0XHRjYXNlICdNdWx0aUxpbmVTdHJpbmcnOlxyXG5cdFx0XHRsYXRsbmdzID0gdGhpcy5jb29yZHNUb0xhdExuZ3MoY29vcmRzLCBnZW9tZXRyeS50eXBlID09PSAnTGluZVN0cmluZycgPyAwIDogMSwgY29vcmRzVG9MYXRMbmcpO1xyXG5cdFx0XHRyZXR1cm4gbmV3IEwuUG9seWxpbmUobGF0bG5ncywgb3B0aW9ucyk7XHJcblxyXG5cdFx0Y2FzZSAnUG9seWdvbic6XHJcblx0XHRjYXNlICdNdWx0aVBvbHlnb24nOlxyXG5cdFx0XHRsYXRsbmdzID0gdGhpcy5jb29yZHNUb0xhdExuZ3MoY29vcmRzLCBnZW9tZXRyeS50eXBlID09PSAnUG9seWdvbicgPyAxIDogMiwgY29vcmRzVG9MYXRMbmcpO1xyXG5cdFx0XHRyZXR1cm4gbmV3IEwuUG9seWdvbihsYXRsbmdzLCBvcHRpb25zKTtcclxuXHJcblx0XHRjYXNlICdHZW9tZXRyeUNvbGxlY3Rpb24nOlxyXG5cdFx0XHRmb3IgKGkgPSAwLCBsZW4gPSBnZW9tZXRyeS5nZW9tZXRyaWVzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdFx0dmFyIGxheWVyID0gdGhpcy5nZW9tZXRyeVRvTGF5ZXIoe1xyXG5cdFx0XHRcdFx0Z2VvbWV0cnk6IGdlb21ldHJ5Lmdlb21ldHJpZXNbaV0sXHJcblx0XHRcdFx0XHR0eXBlOiAnRmVhdHVyZScsXHJcblx0XHRcdFx0XHRwcm9wZXJ0aWVzOiBnZW9qc29uLnByb3BlcnRpZXNcclxuXHRcdFx0XHR9LCBvcHRpb25zKTtcclxuXHJcblx0XHRcdFx0aWYgKGxheWVyKSB7XHJcblx0XHRcdFx0XHRsYXllcnMucHVzaChsYXllcik7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHRcdHJldHVybiBuZXcgTC5GZWF0dXJlR3JvdXAobGF5ZXJzKTtcclxuXHJcblx0XHRkZWZhdWx0OlxyXG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgR2VvSlNPTiBvYmplY3QuJyk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGNvb3Jkc1RvTGF0TG5nKGNvb3JkczogQXJyYXkpOiBMYXRMbmdcclxuXHQvLyBDcmVhdGVzIGEgYExhdExuZ2Agb2JqZWN0IGZyb20gYW4gYXJyYXkgb2YgMiBudW1iZXJzIChsb25naXR1ZGUsIGxhdGl0dWRlKVxyXG5cdC8vIG9yIDMgbnVtYmVycyAobG9uZ2l0dWRlLCBsYXRpdHVkZSwgYWx0aXR1ZGUpIHVzZWQgaW4gR2VvSlNPTiBmb3IgcG9pbnRzLlxyXG5cdGNvb3Jkc1RvTGF0TG5nOiBmdW5jdGlvbiAoY29vcmRzKSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuTGF0TG5nKGNvb3Jkc1sxXSwgY29vcmRzWzBdLCBjb29yZHNbMl0pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBjb29yZHNUb0xhdExuZ3MoY29vcmRzOiBBcnJheSwgbGV2ZWxzRGVlcD86IE51bWJlciwgY29vcmRzVG9MYXRMbmc/OiBGdW5jdGlvbik6IEFycmF5XHJcblx0Ly8gQ3JlYXRlcyBhIG11bHRpZGltZW5zaW9uYWwgYXJyYXkgb2YgYExhdExuZ2BzIGZyb20gYSBHZW9KU09OIGNvb3JkaW5hdGVzIGFycmF5LlxyXG5cdC8vIGBsZXZlbHNEZWVwYCBzcGVjaWZpZXMgdGhlIG5lc3RpbmcgbGV2ZWwgKDAgaXMgZm9yIGFuIGFycmF5IG9mIHBvaW50cywgMSBmb3IgYW4gYXJyYXkgb2YgYXJyYXlzIG9mIHBvaW50cywgZXRjLiwgMCBieSBkZWZhdWx0KS5cclxuXHQvLyBDYW4gdXNlIGEgY3VzdG9tIFtgY29vcmRzVG9MYXRMbmdgXSgjZ2VvanNvbi1jb29yZHN0b2xhdGxuZykgZnVuY3Rpb24uXHJcblx0Y29vcmRzVG9MYXRMbmdzOiBmdW5jdGlvbiAoY29vcmRzLCBsZXZlbHNEZWVwLCBjb29yZHNUb0xhdExuZykge1xyXG5cdFx0dmFyIGxhdGxuZ3MgPSBbXTtcclxuXHJcblx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gY29vcmRzLmxlbmd0aCwgbGF0bG5nOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0bGF0bG5nID0gbGV2ZWxzRGVlcCA/XHJcblx0XHRcdCAgICAgICAgdGhpcy5jb29yZHNUb0xhdExuZ3MoY29vcmRzW2ldLCBsZXZlbHNEZWVwIC0gMSwgY29vcmRzVG9MYXRMbmcpIDpcclxuXHRcdFx0ICAgICAgICAoY29vcmRzVG9MYXRMbmcgfHwgdGhpcy5jb29yZHNUb0xhdExuZykoY29vcmRzW2ldKTtcclxuXHJcblx0XHRcdGxhdGxuZ3MucHVzaChsYXRsbmcpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiBsYXRsbmdzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBsYXRMbmdUb0Nvb3JkcyhsYXRsbmc6IExhdExuZyk6IEFycmF5XHJcblx0Ly8gUmV2ZXJzZSBvZiBbYGNvb3Jkc1RvTGF0TG5nYF0oI2dlb2pzb24tY29vcmRzdG9sYXRsbmcpXHJcblx0bGF0TG5nVG9Db29yZHM6IGZ1bmN0aW9uIChsYXRsbmcpIHtcclxuXHRcdHJldHVybiBsYXRsbmcuYWx0ICE9PSB1bmRlZmluZWQgP1xyXG5cdFx0XHRcdFtsYXRsbmcubG5nLCBsYXRsbmcubGF0LCBsYXRsbmcuYWx0XSA6XHJcblx0XHRcdFx0W2xhdGxuZy5sbmcsIGxhdGxuZy5sYXRdO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBsYXRMbmdzVG9Db29yZHMobGF0bG5nczogQXJyYXksIGxldmVsc0RlZXA/OiBOdW1iZXIsIGNsb3NlZD86IEJvb2xlYW4pOiBBcnJheVxyXG5cdC8vIFJldmVyc2Ugb2YgW2Bjb29yZHNUb0xhdExuZ3NgXSgjZ2VvanNvbi1jb29yZHN0b2xhdGxuZ3MpXHJcblx0Ly8gYGNsb3NlZGAgZGV0ZXJtaW5lcyB3aGV0aGVyIHRoZSBmaXJzdCBwb2ludCBzaG91bGQgYmUgYXBwZW5kZWQgdG8gdGhlIGVuZCBvZiB0aGUgYXJyYXkgdG8gY2xvc2UgdGhlIGZlYXR1cmUsIG9ubHkgdXNlZCB3aGVuIGBsZXZlbHNEZWVwYCBpcyAwLiBGYWxzZSBieSBkZWZhdWx0LlxyXG5cdGxhdExuZ3NUb0Nvb3JkczogZnVuY3Rpb24gKGxhdGxuZ3MsIGxldmVsc0RlZXAsIGNsb3NlZCkge1xyXG5cdFx0dmFyIGNvb3JkcyA9IFtdO1xyXG5cclxuXHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSBsYXRsbmdzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdGNvb3Jkcy5wdXNoKGxldmVsc0RlZXAgP1xyXG5cdFx0XHRcdEwuR2VvSlNPTi5sYXRMbmdzVG9Db29yZHMobGF0bG5nc1tpXSwgbGV2ZWxzRGVlcCAtIDEsIGNsb3NlZCkgOlxyXG5cdFx0XHRcdEwuR2VvSlNPTi5sYXRMbmdUb0Nvb3JkcyhsYXRsbmdzW2ldKSk7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKCFsZXZlbHNEZWVwICYmIGNsb3NlZCkge1xyXG5cdFx0XHRjb29yZHMucHVzaChjb29yZHNbMF0pO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiBjb29yZHM7XHJcblx0fSxcclxuXHJcblx0Z2V0RmVhdHVyZTogZnVuY3Rpb24gKGxheWVyLCBuZXdHZW9tZXRyeSkge1xyXG5cdFx0cmV0dXJuIGxheWVyLmZlYXR1cmUgP1xyXG5cdFx0XHRcdEwuZXh0ZW5kKHt9LCBsYXllci5mZWF0dXJlLCB7Z2VvbWV0cnk6IG5ld0dlb21ldHJ5fSkgOlxyXG5cdFx0XHRcdEwuR2VvSlNPTi5hc0ZlYXR1cmUobmV3R2VvbWV0cnkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBhc0ZlYXR1cmUoZ2VvanNvbjogT2JqZWN0KTogT2JqZWN0XHJcblx0Ly8gTm9ybWFsaXplIEdlb0pTT04gZ2VvbWV0cmllcy9mZWF0dXJlcyBpbnRvIEdlb0pTT04gZmVhdHVyZXMuXHJcblx0YXNGZWF0dXJlOiBmdW5jdGlvbiAoZ2VvanNvbikge1xyXG5cdFx0aWYgKGdlb2pzb24udHlwZSA9PT0gJ0ZlYXR1cmUnIHx8IGdlb2pzb24udHlwZSA9PT0gJ0ZlYXR1cmVDb2xsZWN0aW9uJykge1xyXG5cdFx0XHRyZXR1cm4gZ2VvanNvbjtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4ge1xyXG5cdFx0XHR0eXBlOiAnRmVhdHVyZScsXHJcblx0XHRcdHByb3BlcnRpZXM6IHt9LFxyXG5cdFx0XHRnZW9tZXRyeTogZ2VvanNvblxyXG5cdFx0fTtcclxuXHR9XHJcbn0pO1xyXG5cclxudmFyIFBvaW50VG9HZW9KU09OID0ge1xyXG5cdHRvR2VvSlNPTjogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIEwuR2VvSlNPTi5nZXRGZWF0dXJlKHRoaXMsIHtcclxuXHRcdFx0dHlwZTogJ1BvaW50JyxcclxuXHRcdFx0Y29vcmRpbmF0ZXM6IEwuR2VvSlNPTi5sYXRMbmdUb0Nvb3Jkcyh0aGlzLmdldExhdExuZygpKVxyXG5cdFx0fSk7XHJcblx0fVxyXG59O1xyXG5cclxuLy8gQG5hbWVzcGFjZSBNYXJrZXJcclxuLy8gQG1ldGhvZCB0b0dlb0pTT04oKTogT2JqZWN0XHJcbi8vIFJldHVybnMgYSBbYEdlb0pTT05gXShodHRwOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0dlb0pTT04pIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBtYXJrZXIgKGFzIGEgR2VvSlNPTiBgUG9pbnRgIEZlYXR1cmUpLlxyXG5MLk1hcmtlci5pbmNsdWRlKFBvaW50VG9HZW9KU09OKTtcclxuXHJcbi8vIEBuYW1lc3BhY2UgQ2lyY2xlTWFya2VyXHJcbi8vIEBtZXRob2QgdG9HZW9KU09OKCk6IE9iamVjdFxyXG4vLyBSZXR1cm5zIGEgW2BHZW9KU09OYF0oaHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9HZW9KU09OKSByZXByZXNlbnRhdGlvbiBvZiB0aGUgY2lyY2xlIG1hcmtlciAoYXMgYSBHZW9KU09OIGBQb2ludGAgRmVhdHVyZSkuXHJcbkwuQ2lyY2xlLmluY2x1ZGUoUG9pbnRUb0dlb0pTT04pO1xyXG5MLkNpcmNsZU1hcmtlci5pbmNsdWRlKFBvaW50VG9HZW9KU09OKTtcclxuXHJcblxyXG4vLyBAbmFtZXNwYWNlIFBvbHlsaW5lXHJcbi8vIEBtZXRob2QgdG9HZW9KU09OKCk6IE9iamVjdFxyXG4vLyBSZXR1cm5zIGEgW2BHZW9KU09OYF0oaHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9HZW9KU09OKSByZXByZXNlbnRhdGlvbiBvZiB0aGUgcG9seWxpbmUgKGFzIGEgR2VvSlNPTiBgTGluZVN0cmluZ2Agb3IgYE11bHRpTGluZVN0cmluZ2AgRmVhdHVyZSkuXHJcbkwuUG9seWxpbmUucHJvdG90eXBlLnRvR2VvSlNPTiA9IGZ1bmN0aW9uICgpIHtcclxuXHR2YXIgbXVsdGkgPSAhTC5Qb2x5bGluZS5fZmxhdCh0aGlzLl9sYXRsbmdzKTtcclxuXHJcblx0dmFyIGNvb3JkcyA9IEwuR2VvSlNPTi5sYXRMbmdzVG9Db29yZHModGhpcy5fbGF0bG5ncywgbXVsdGkgPyAxIDogMCk7XHJcblxyXG5cdHJldHVybiBMLkdlb0pTT04uZ2V0RmVhdHVyZSh0aGlzLCB7XHJcblx0XHR0eXBlOiAobXVsdGkgPyAnTXVsdGknIDogJycpICsgJ0xpbmVTdHJpbmcnLFxyXG5cdFx0Y29vcmRpbmF0ZXM6IGNvb3Jkc1xyXG5cdH0pO1xyXG59O1xyXG5cclxuLy8gQG5hbWVzcGFjZSBQb2x5Z29uXHJcbi8vIEBtZXRob2QgdG9HZW9KU09OKCk6IE9iamVjdFxyXG4vLyBSZXR1cm5zIGEgW2BHZW9KU09OYF0oaHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9HZW9KU09OKSByZXByZXNlbnRhdGlvbiBvZiB0aGUgcG9seWdvbiAoYXMgYSBHZW9KU09OIGBQb2x5Z29uYCBvciBgTXVsdGlQb2x5Z29uYCBGZWF0dXJlKS5cclxuTC5Qb2x5Z29uLnByb3RvdHlwZS50b0dlb0pTT04gPSBmdW5jdGlvbiAoKSB7XHJcblx0dmFyIGhvbGVzID0gIUwuUG9seWxpbmUuX2ZsYXQodGhpcy5fbGF0bG5ncyksXHJcblx0ICAgIG11bHRpID0gaG9sZXMgJiYgIUwuUG9seWxpbmUuX2ZsYXQodGhpcy5fbGF0bG5nc1swXSk7XHJcblxyXG5cdHZhciBjb29yZHMgPSBMLkdlb0pTT04ubGF0TG5nc1RvQ29vcmRzKHRoaXMuX2xhdGxuZ3MsIG11bHRpID8gMiA6IGhvbGVzID8gMSA6IDAsIHRydWUpO1xyXG5cclxuXHRpZiAoIWhvbGVzKSB7XHJcblx0XHRjb29yZHMgPSBbY29vcmRzXTtcclxuXHR9XHJcblxyXG5cdHJldHVybiBMLkdlb0pTT04uZ2V0RmVhdHVyZSh0aGlzLCB7XHJcblx0XHR0eXBlOiAobXVsdGkgPyAnTXVsdGknIDogJycpICsgJ1BvbHlnb24nLFxyXG5cdFx0Y29vcmRpbmF0ZXM6IGNvb3Jkc1xyXG5cdH0pO1xyXG59O1xyXG5cclxuXHJcbi8vIEBuYW1lc3BhY2UgTGF5ZXJHcm91cFxyXG5MLkxheWVyR3JvdXAuaW5jbHVkZSh7XHJcblx0dG9NdWx0aVBvaW50OiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgY29vcmRzID0gW107XHJcblxyXG5cdFx0dGhpcy5lYWNoTGF5ZXIoZnVuY3Rpb24gKGxheWVyKSB7XHJcblx0XHRcdGNvb3Jkcy5wdXNoKGxheWVyLnRvR2VvSlNPTigpLmdlb21ldHJ5LmNvb3JkaW5hdGVzKTtcclxuXHRcdH0pO1xyXG5cclxuXHRcdHJldHVybiBMLkdlb0pTT04uZ2V0RmVhdHVyZSh0aGlzLCB7XHJcblx0XHRcdHR5cGU6ICdNdWx0aVBvaW50JyxcclxuXHRcdFx0Y29vcmRpbmF0ZXM6IGNvb3Jkc1xyXG5cdFx0fSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB0b0dlb0pTT04oKTogT2JqZWN0XHJcblx0Ly8gUmV0dXJucyBhIFtgR2VvSlNPTmBdKGh0dHA6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvR2VvSlNPTikgcmVwcmVzZW50YXRpb24gb2YgdGhlIGxheWVyIGdyb3VwIChhcyBhIEdlb0pTT04gYEdlb21ldHJ5Q29sbGVjdGlvbmApLlxyXG5cdHRvR2VvSlNPTjogZnVuY3Rpb24gKCkge1xyXG5cclxuXHRcdHZhciB0eXBlID0gdGhpcy5mZWF0dXJlICYmIHRoaXMuZmVhdHVyZS5nZW9tZXRyeSAmJiB0aGlzLmZlYXR1cmUuZ2VvbWV0cnkudHlwZTtcclxuXHJcblx0XHRpZiAodHlwZSA9PT0gJ011bHRpUG9pbnQnKSB7XHJcblx0XHRcdHJldHVybiB0aGlzLnRvTXVsdGlQb2ludCgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBpc0dlb21ldHJ5Q29sbGVjdGlvbiA9IHR5cGUgPT09ICdHZW9tZXRyeUNvbGxlY3Rpb24nLFxyXG5cdFx0ICAgIGpzb25zID0gW107XHJcblxyXG5cdFx0dGhpcy5lYWNoTGF5ZXIoZnVuY3Rpb24gKGxheWVyKSB7XHJcblx0XHRcdGlmIChsYXllci50b0dlb0pTT04pIHtcclxuXHRcdFx0XHR2YXIganNvbiA9IGxheWVyLnRvR2VvSlNPTigpO1xyXG5cdFx0XHRcdGpzb25zLnB1c2goaXNHZW9tZXRyeUNvbGxlY3Rpb24gPyBqc29uLmdlb21ldHJ5IDogTC5HZW9KU09OLmFzRmVhdHVyZShqc29uKSk7XHJcblx0XHRcdH1cclxuXHRcdH0pO1xyXG5cclxuXHRcdGlmIChpc0dlb21ldHJ5Q29sbGVjdGlvbikge1xyXG5cdFx0XHRyZXR1cm4gTC5HZW9KU09OLmdldEZlYXR1cmUodGhpcywge1xyXG5cdFx0XHRcdGdlb21ldHJpZXM6IGpzb25zLFxyXG5cdFx0XHRcdHR5cGU6ICdHZW9tZXRyeUNvbGxlY3Rpb24nXHJcblx0XHRcdH0pO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB7XHJcblx0XHRcdHR5cGU6ICdGZWF0dXJlQ29sbGVjdGlvbicsXHJcblx0XHRcdGZlYXR1cmVzOiBqc29uc1xyXG5cdFx0fTtcclxuXHR9XHJcbn0pO1xyXG5cclxuLy8gQG5hbWVzcGFjZSBHZW9KU09OXHJcbi8vIEBmYWN0b3J5IEwuZ2VvSlNPTihnZW9qc29uPzogT2JqZWN0LCBvcHRpb25zPzogR2VvSlNPTiBvcHRpb25zKVxyXG4vLyBDcmVhdGVzIGEgR2VvSlNPTiBsYXllci4gT3B0aW9uYWxseSBhY2NlcHRzIGFuIG9iamVjdCBpblxyXG4vLyBbR2VvSlNPTiBmb3JtYXRdKGh0dHA6Ly9nZW9qc29uLm9yZy9nZW9qc29uLXNwZWMuaHRtbCkgdG8gZGlzcGxheSBvbiB0aGUgbWFwXHJcbi8vICh5b3UgY2FuIGFsdGVybmF0aXZlbHkgYWRkIGl0IGxhdGVyIHdpdGggYGFkZERhdGFgIG1ldGhvZCkgYW5kIGFuIGBvcHRpb25zYCBvYmplY3QuXHJcbkwuZ2VvSlNPTiA9IGZ1bmN0aW9uIChnZW9qc29uLCBvcHRpb25zKSB7XHJcblx0cmV0dXJuIG5ldyBMLkdlb0pTT04oZ2VvanNvbiwgb3B0aW9ucyk7XHJcbn07XHJcbi8vIEJhY2t3YXJkIGNvbXBhdGliaWxpdHkuXHJcbkwuZ2VvSnNvbiA9IEwuZ2VvSlNPTjtcclxuXG5cblxuLypcclxuICogQGNsYXNzIERyYWdnYWJsZVxyXG4gKiBAYWthIEwuRHJhZ2dhYmxlXHJcbiAqIEBpbmhlcml0cyBFdmVudGVkXHJcbiAqXHJcbiAqIEEgY2xhc3MgZm9yIG1ha2luZyBET00gZWxlbWVudHMgZHJhZ2dhYmxlIChpbmNsdWRpbmcgdG91Y2ggc3VwcG9ydCkuXHJcbiAqIFVzZWQgaW50ZXJuYWxseSBmb3IgbWFwIGFuZCBtYXJrZXIgZHJhZ2dpbmcuIE9ubHkgd29ya3MgZm9yIGVsZW1lbnRzXHJcbiAqIHRoYXQgd2VyZSBwb3NpdGlvbmVkIHdpdGggW2BMLkRvbVV0aWwuc2V0UG9zaXRpb25gXSgjZG9tdXRpbC1zZXRwb3NpdGlvbikuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqIGBgYGpzXHJcbiAqIHZhciBkcmFnZ2FibGUgPSBuZXcgTC5EcmFnZ2FibGUoZWxlbWVudFRvRHJhZyk7XHJcbiAqIGRyYWdnYWJsZS5lbmFibGUoKTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxuTC5EcmFnZ2FibGUgPSBMLkV2ZW50ZWQuZXh0ZW5kKHtcclxuXHJcblx0b3B0aW9uczoge1xyXG5cdFx0Ly8gQG9wdGlvbiBjbGlja1RvbGVyYW5jZTogTnVtYmVyID0gM1xyXG5cdFx0Ly8gVGhlIG1heCBudW1iZXIgb2YgcGl4ZWxzIGEgdXNlciBjYW4gc2hpZnQgdGhlIG1vdXNlIHBvaW50ZXIgZHVyaW5nIGEgY2xpY2tcclxuXHRcdC8vIGZvciBpdCB0byBiZSBjb25zaWRlcmVkIGEgdmFsaWQgY2xpY2sgKGFzIG9wcG9zZWQgdG8gYSBtb3VzZSBkcmFnKS5cclxuXHRcdGNsaWNrVG9sZXJhbmNlOiAzXHJcblx0fSxcclxuXHJcblx0c3RhdGljczoge1xyXG5cdFx0U1RBUlQ6IEwuQnJvd3Nlci50b3VjaCA/IFsndG91Y2hzdGFydCcsICdtb3VzZWRvd24nXSA6IFsnbW91c2Vkb3duJ10sXHJcblx0XHRFTkQ6IHtcclxuXHRcdFx0bW91c2Vkb3duOiAnbW91c2V1cCcsXHJcblx0XHRcdHRvdWNoc3RhcnQ6ICd0b3VjaGVuZCcsXHJcblx0XHRcdHBvaW50ZXJkb3duOiAndG91Y2hlbmQnLFxyXG5cdFx0XHRNU1BvaW50ZXJEb3duOiAndG91Y2hlbmQnXHJcblx0XHR9LFxyXG5cdFx0TU9WRToge1xyXG5cdFx0XHRtb3VzZWRvd246ICdtb3VzZW1vdmUnLFxyXG5cdFx0XHR0b3VjaHN0YXJ0OiAndG91Y2htb3ZlJyxcclxuXHRcdFx0cG9pbnRlcmRvd246ICd0b3VjaG1vdmUnLFxyXG5cdFx0XHRNU1BvaW50ZXJEb3duOiAndG91Y2htb3ZlJ1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBjb25zdHJ1Y3RvciBMLkRyYWdnYWJsZShlbDogSFRNTEVsZW1lbnQsIGRyYWdIYW5kbGU/OiBIVE1MRWxlbWVudCwgcHJldmVudE91dGxpbmU6IEJvb2xlYW4pXHJcblx0Ly8gQ3JlYXRlcyBhIGBEcmFnZ2FibGVgIG9iamVjdCBmb3IgbW92aW5nIGBlbGAgd2hlbiB5b3Ugc3RhcnQgZHJhZ2dpbmcgdGhlIGBkcmFnSGFuZGxlYCBlbGVtZW50IChlcXVhbHMgYGVsYCBpdHNlbGYgYnkgZGVmYXVsdCkuXHJcblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKGVsZW1lbnQsIGRyYWdTdGFydFRhcmdldCwgcHJldmVudE91dGxpbmUpIHtcclxuXHRcdHRoaXMuX2VsZW1lbnQgPSBlbGVtZW50O1xyXG5cdFx0dGhpcy5fZHJhZ1N0YXJ0VGFyZ2V0ID0gZHJhZ1N0YXJ0VGFyZ2V0IHx8IGVsZW1lbnQ7XHJcblx0XHR0aGlzLl9wcmV2ZW50T3V0bGluZSA9IHByZXZlbnRPdXRsaW5lO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZW5hYmxlKClcclxuXHQvLyBFbmFibGVzIHRoZSBkcmFnZ2luZyBhYmlsaXR5XHJcblx0ZW5hYmxlOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAodGhpcy5fZW5hYmxlZCkgeyByZXR1cm47IH1cclxuXHJcblx0XHRMLkRvbUV2ZW50Lm9uKHRoaXMuX2RyYWdTdGFydFRhcmdldCwgTC5EcmFnZ2FibGUuU1RBUlQuam9pbignICcpLCB0aGlzLl9vbkRvd24sIHRoaXMpO1xyXG5cclxuXHRcdHRoaXMuX2VuYWJsZWQgPSB0cnVlO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZGlzYWJsZSgpXHJcblx0Ly8gRGlzYWJsZXMgdGhlIGRyYWdnaW5nIGFiaWxpdHlcclxuXHRkaXNhYmxlOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAoIXRoaXMuX2VuYWJsZWQpIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0Ly8gSWYgd2UncmUgY3VycmVudGx5IGRyYWdnaW5nIHRoaXMgZHJhZ2dhYmxlLFxyXG5cdFx0Ly8gZGlzYWJsaW5nIGl0IGNvdW50cyBhcyBmaXJzdCBlbmRpbmcgdGhlIGRyYWcuXHJcblx0XHRpZiAoTC5EcmFnZ2FibGUuX2RyYWdnaW5nID09PSB0aGlzKSB7XHJcblx0XHRcdHRoaXMuZmluaXNoRHJhZygpO1xyXG5cdFx0fVxyXG5cclxuXHRcdEwuRG9tRXZlbnQub2ZmKHRoaXMuX2RyYWdTdGFydFRhcmdldCwgTC5EcmFnZ2FibGUuU1RBUlQuam9pbignICcpLCB0aGlzLl9vbkRvd24sIHRoaXMpO1xyXG5cclxuXHRcdHRoaXMuX2VuYWJsZWQgPSBmYWxzZTtcclxuXHRcdHRoaXMuX21vdmVkID0gZmFsc2U7XHJcblx0fSxcclxuXHJcblx0X29uRG93bjogZnVuY3Rpb24gKGUpIHtcclxuXHRcdC8vIElnbm9yZSBzaW11bGF0ZWQgZXZlbnRzLCBzaW5jZSB3ZSBoYW5kbGUgYm90aCB0b3VjaCBhbmRcclxuXHRcdC8vIG1vdXNlIGV4cGxpY2l0bHk7IG90aGVyd2lzZSB3ZSByaXNrIGdldHRpbmcgZHVwbGljYXRlcyBvZlxyXG5cdFx0Ly8gdG91Y2ggZXZlbnRzLCBzZWUgIzQzMTUuXHJcblx0XHQvLyBBbHNvIGlnbm9yZSB0aGUgZXZlbnQgaWYgZGlzYWJsZWQ7IHRoaXMgaGFwcGVucyBpbiBJRTExXHJcblx0XHQvLyB1bmRlciBzb21lIGNpcmN1bXN0YW5jZXMsIHNlZSAjMzY2Ni5cclxuXHRcdGlmIChlLl9zaW11bGF0ZWQgfHwgIXRoaXMuX2VuYWJsZWQpIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0dGhpcy5fbW92ZWQgPSBmYWxzZTtcclxuXHJcblx0XHRpZiAoTC5Eb21VdGlsLmhhc0NsYXNzKHRoaXMuX2VsZW1lbnQsICdsZWFmbGV0LXpvb20tYW5pbScpKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdGlmIChMLkRyYWdnYWJsZS5fZHJhZ2dpbmcgfHwgZS5zaGlmdEtleSB8fCAoKGUud2hpY2ggIT09IDEpICYmIChlLmJ1dHRvbiAhPT0gMSkgJiYgIWUudG91Y2hlcykpIHsgcmV0dXJuOyB9XHJcblx0XHRMLkRyYWdnYWJsZS5fZHJhZ2dpbmcgPSB0aGlzOyAgLy8gUHJldmVudCBkcmFnZ2luZyBtdWx0aXBsZSBvYmplY3RzIGF0IG9uY2UuXHJcblxyXG5cdFx0aWYgKHRoaXMuX3ByZXZlbnRPdXRsaW5lKSB7XHJcblx0XHRcdEwuRG9tVXRpbC5wcmV2ZW50T3V0bGluZSh0aGlzLl9lbGVtZW50KTtcclxuXHRcdH1cclxuXHJcblx0XHRMLkRvbVV0aWwuZGlzYWJsZUltYWdlRHJhZygpO1xyXG5cdFx0TC5Eb21VdGlsLmRpc2FibGVUZXh0U2VsZWN0aW9uKCk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX21vdmluZykgeyByZXR1cm47IH1cclxuXHJcblx0XHQvLyBAZXZlbnQgZG93bjogRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gYSBkcmFnIGlzIGFib3V0IHRvIHN0YXJ0LlxyXG5cdFx0dGhpcy5maXJlKCdkb3duJyk7XHJcblxyXG5cdFx0dmFyIGZpcnN0ID0gZS50b3VjaGVzID8gZS50b3VjaGVzWzBdIDogZTtcclxuXHJcblx0XHR0aGlzLl9zdGFydFBvaW50ID0gbmV3IEwuUG9pbnQoZmlyc3QuY2xpZW50WCwgZmlyc3QuY2xpZW50WSk7XHJcblxyXG5cdFx0TC5Eb21FdmVudFxyXG5cdFx0XHQub24oZG9jdW1lbnQsIEwuRHJhZ2dhYmxlLk1PVkVbZS50eXBlXSwgdGhpcy5fb25Nb3ZlLCB0aGlzKVxyXG5cdFx0XHQub24oZG9jdW1lbnQsIEwuRHJhZ2dhYmxlLkVORFtlLnR5cGVdLCB0aGlzLl9vblVwLCB0aGlzKTtcclxuXHR9LFxyXG5cclxuXHRfb25Nb3ZlOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0Ly8gSWdub3JlIHNpbXVsYXRlZCBldmVudHMsIHNpbmNlIHdlIGhhbmRsZSBib3RoIHRvdWNoIGFuZFxyXG5cdFx0Ly8gbW91c2UgZXhwbGljaXRseTsgb3RoZXJ3aXNlIHdlIHJpc2sgZ2V0dGluZyBkdXBsaWNhdGVzIG9mXHJcblx0XHQvLyB0b3VjaCBldmVudHMsIHNlZSAjNDMxNS5cclxuXHRcdC8vIEFsc28gaWdub3JlIHRoZSBldmVudCBpZiBkaXNhYmxlZDsgdGhpcyBoYXBwZW5zIGluIElFMTFcclxuXHRcdC8vIHVuZGVyIHNvbWUgY2lyY3Vtc3RhbmNlcywgc2VlICMzNjY2LlxyXG5cdFx0aWYgKGUuX3NpbXVsYXRlZCB8fCAhdGhpcy5fZW5hYmxlZCkgeyByZXR1cm47IH1cclxuXHJcblx0XHRpZiAoZS50b3VjaGVzICYmIGUudG91Y2hlcy5sZW5ndGggPiAxKSB7XHJcblx0XHRcdHRoaXMuX21vdmVkID0gdHJ1ZTtcclxuXHRcdFx0cmV0dXJuO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBmaXJzdCA9IChlLnRvdWNoZXMgJiYgZS50b3VjaGVzLmxlbmd0aCA9PT0gMSA/IGUudG91Y2hlc1swXSA6IGUpLFxyXG5cdFx0ICAgIG5ld1BvaW50ID0gbmV3IEwuUG9pbnQoZmlyc3QuY2xpZW50WCwgZmlyc3QuY2xpZW50WSksXHJcblx0XHQgICAgb2Zmc2V0ID0gbmV3UG9pbnQuc3VidHJhY3QodGhpcy5fc3RhcnRQb2ludCk7XHJcblxyXG5cdFx0aWYgKCFvZmZzZXQueCAmJiAhb2Zmc2V0LnkpIHsgcmV0dXJuOyB9XHJcblx0XHRpZiAoTWF0aC5hYnMob2Zmc2V0LngpICsgTWF0aC5hYnMob2Zmc2V0LnkpIDwgdGhpcy5vcHRpb25zLmNsaWNrVG9sZXJhbmNlKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdEwuRG9tRXZlbnQucHJldmVudERlZmF1bHQoZSk7XHJcblxyXG5cdFx0aWYgKCF0aGlzLl9tb3ZlZCkge1xyXG5cdFx0XHQvLyBAZXZlbnQgZHJhZ3N0YXJ0OiBFdmVudFxyXG5cdFx0XHQvLyBGaXJlZCB3aGVuIGEgZHJhZyBzdGFydHNcclxuXHRcdFx0dGhpcy5maXJlKCdkcmFnc3RhcnQnKTtcclxuXHJcblx0XHRcdHRoaXMuX21vdmVkID0gdHJ1ZTtcclxuXHRcdFx0dGhpcy5fc3RhcnRQb3MgPSBMLkRvbVV0aWwuZ2V0UG9zaXRpb24odGhpcy5fZWxlbWVudCkuc3VidHJhY3Qob2Zmc2V0KTtcclxuXHJcblx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyhkb2N1bWVudC5ib2R5LCAnbGVhZmxldC1kcmFnZ2luZycpO1xyXG5cclxuXHRcdFx0dGhpcy5fbGFzdFRhcmdldCA9IGUudGFyZ2V0IHx8IGUuc3JjRWxlbWVudDtcclxuXHRcdFx0Ly8gSUUgYW5kIEVkZ2UgZG8gbm90IGdpdmUgdGhlIDx1c2U+IGVsZW1lbnQsIHNvIGZldGNoIGl0XHJcblx0XHRcdC8vIGlmIG5lY2Vzc2FyeVxyXG5cdFx0XHRpZiAoKHdpbmRvdy5TVkdFbGVtZW50SW5zdGFuY2UpICYmICh0aGlzLl9sYXN0VGFyZ2V0IGluc3RhbmNlb2YgU1ZHRWxlbWVudEluc3RhbmNlKSkge1xyXG5cdFx0XHRcdHRoaXMuX2xhc3RUYXJnZXQgPSB0aGlzLl9sYXN0VGFyZ2V0LmNvcnJlc3BvbmRpbmdVc2VFbGVtZW50O1xyXG5cdFx0XHR9XHJcblx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9sYXN0VGFyZ2V0LCAnbGVhZmxldC1kcmFnLXRhcmdldCcpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX25ld1BvcyA9IHRoaXMuX3N0YXJ0UG9zLmFkZChvZmZzZXQpO1xyXG5cdFx0dGhpcy5fbW92aW5nID0gdHJ1ZTtcclxuXHJcblx0XHRMLlV0aWwuY2FuY2VsQW5pbUZyYW1lKHRoaXMuX2FuaW1SZXF1ZXN0KTtcclxuXHRcdHRoaXMuX2xhc3RFdmVudCA9IGU7XHJcblx0XHR0aGlzLl9hbmltUmVxdWVzdCA9IEwuVXRpbC5yZXF1ZXN0QW5pbUZyYW1lKHRoaXMuX3VwZGF0ZVBvc2l0aW9uLCB0aGlzLCB0cnVlKTtcclxuXHR9LFxyXG5cclxuXHRfdXBkYXRlUG9zaXRpb246IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBlID0ge29yaWdpbmFsRXZlbnQ6IHRoaXMuX2xhc3RFdmVudH07XHJcblxyXG5cdFx0Ly8gQGV2ZW50IHByZWRyYWc6IEV2ZW50XHJcblx0XHQvLyBGaXJlZCBjb250aW51b3VzbHkgZHVyaW5nIGRyYWdnaW5nICpiZWZvcmUqIGVhY2ggY29ycmVzcG9uZGluZ1xyXG5cdFx0Ly8gdXBkYXRlIG9mIHRoZSBlbGVtZW50J3MgcG9zaXRpb24uXHJcblx0XHR0aGlzLmZpcmUoJ3ByZWRyYWcnLCBlKTtcclxuXHRcdEwuRG9tVXRpbC5zZXRQb3NpdGlvbih0aGlzLl9lbGVtZW50LCB0aGlzLl9uZXdQb3MpO1xyXG5cclxuXHRcdC8vIEBldmVudCBkcmFnOiBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgY29udGludW91c2x5IGR1cmluZyBkcmFnZ2luZy5cclxuXHRcdHRoaXMuZmlyZSgnZHJhZycsIGUpO1xyXG5cdH0sXHJcblxyXG5cdF9vblVwOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0Ly8gSWdub3JlIHNpbXVsYXRlZCBldmVudHMsIHNpbmNlIHdlIGhhbmRsZSBib3RoIHRvdWNoIGFuZFxyXG5cdFx0Ly8gbW91c2UgZXhwbGljaXRseTsgb3RoZXJ3aXNlIHdlIHJpc2sgZ2V0dGluZyBkdXBsaWNhdGVzIG9mXHJcblx0XHQvLyB0b3VjaCBldmVudHMsIHNlZSAjNDMxNS5cclxuXHRcdC8vIEFsc28gaWdub3JlIHRoZSBldmVudCBpZiBkaXNhYmxlZDsgdGhpcyBoYXBwZW5zIGluIElFMTFcclxuXHRcdC8vIHVuZGVyIHNvbWUgY2lyY3Vtc3RhbmNlcywgc2VlICMzNjY2LlxyXG5cdFx0aWYgKGUuX3NpbXVsYXRlZCB8fCAhdGhpcy5fZW5hYmxlZCkgeyByZXR1cm47IH1cclxuXHRcdHRoaXMuZmluaXNoRHJhZygpO1xyXG5cdH0sXHJcblxyXG5cdGZpbmlzaERyYWc6IGZ1bmN0aW9uICgpIHtcclxuXHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyhkb2N1bWVudC5ib2R5LCAnbGVhZmxldC1kcmFnZ2luZycpO1xyXG5cclxuXHRcdGlmICh0aGlzLl9sYXN0VGFyZ2V0KSB7XHJcblx0XHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9sYXN0VGFyZ2V0LCAnbGVhZmxldC1kcmFnLXRhcmdldCcpO1xyXG5cdFx0XHR0aGlzLl9sYXN0VGFyZ2V0ID0gbnVsbDtcclxuXHRcdH1cclxuXHJcblx0XHRmb3IgKHZhciBpIGluIEwuRHJhZ2dhYmxlLk1PVkUpIHtcclxuXHRcdFx0TC5Eb21FdmVudFxyXG5cdFx0XHRcdC5vZmYoZG9jdW1lbnQsIEwuRHJhZ2dhYmxlLk1PVkVbaV0sIHRoaXMuX29uTW92ZSwgdGhpcylcclxuXHRcdFx0XHQub2ZmKGRvY3VtZW50LCBMLkRyYWdnYWJsZS5FTkRbaV0sIHRoaXMuX29uVXAsIHRoaXMpO1xyXG5cdFx0fVxyXG5cclxuXHRcdEwuRG9tVXRpbC5lbmFibGVJbWFnZURyYWcoKTtcclxuXHRcdEwuRG9tVXRpbC5lbmFibGVUZXh0U2VsZWN0aW9uKCk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX21vdmVkICYmIHRoaXMuX21vdmluZykge1xyXG5cdFx0XHQvLyBlbnN1cmUgZHJhZyBpcyBub3QgZmlyZWQgYWZ0ZXIgZHJhZ2VuZFxyXG5cdFx0XHRMLlV0aWwuY2FuY2VsQW5pbUZyYW1lKHRoaXMuX2FuaW1SZXF1ZXN0KTtcclxuXHJcblx0XHRcdC8vIEBldmVudCBkcmFnZW5kOiBEcmFnRW5kRXZlbnRcclxuXHRcdFx0Ly8gRmlyZWQgd2hlbiB0aGUgZHJhZyBlbmRzLlxyXG5cdFx0XHR0aGlzLmZpcmUoJ2RyYWdlbmQnLCB7XHJcblx0XHRcdFx0ZGlzdGFuY2U6IHRoaXMuX25ld1Bvcy5kaXN0YW5jZVRvKHRoaXMuX3N0YXJ0UG9zKVxyXG5cdFx0XHR9KTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9tb3ZpbmcgPSBmYWxzZTtcclxuXHRcdEwuRHJhZ2dhYmxlLl9kcmFnZ2luZyA9IGZhbHNlO1xyXG5cdH1cclxuXHJcbn0pO1xyXG5cblxuXG4vKlxuXHRMLkhhbmRsZXIgaXMgYSBiYXNlIGNsYXNzIGZvciBoYW5kbGVyIGNsYXNzZXMgdGhhdCBhcmUgdXNlZCBpbnRlcm5hbGx5IHRvIGluamVjdFxuXHRpbnRlcmFjdGlvbiBmZWF0dXJlcyBsaWtlIGRyYWdnaW5nIHRvIGNsYXNzZXMgbGlrZSBNYXAgYW5kIE1hcmtlci5cbiovXG5cbi8vIEBjbGFzcyBIYW5kbGVyXG4vLyBAYWthIEwuSGFuZGxlclxuLy8gQWJzdHJhY3QgY2xhc3MgZm9yIG1hcCBpbnRlcmFjdGlvbiBoYW5kbGVyc1xuXG5MLkhhbmRsZXIgPSBMLkNsYXNzLmV4dGVuZCh7XG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChtYXApIHtcblx0XHR0aGlzLl9tYXAgPSBtYXA7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBlbmFibGUoKTogdGhpc1xuXHQvLyBFbmFibGVzIHRoZSBoYW5kbGVyXG5cdGVuYWJsZTogZnVuY3Rpb24gKCkge1xuXHRcdGlmICh0aGlzLl9lbmFibGVkKSB7IHJldHVybiB0aGlzOyB9XG5cblx0XHR0aGlzLl9lbmFibGVkID0gdHJ1ZTtcblx0XHR0aGlzLmFkZEhvb2tzKCk7XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBkaXNhYmxlKCk6IHRoaXNcblx0Ly8gRGlzYWJsZXMgdGhlIGhhbmRsZXJcblx0ZGlzYWJsZTogZnVuY3Rpb24gKCkge1xuXHRcdGlmICghdGhpcy5fZW5hYmxlZCkgeyByZXR1cm4gdGhpczsgfVxuXG5cdFx0dGhpcy5fZW5hYmxlZCA9IGZhbHNlO1xuXHRcdHRoaXMucmVtb3ZlSG9va3MoKTtcblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIGVuYWJsZWQoKTogQm9vbGVhblxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgaGFuZGxlciBpcyBlbmFibGVkXG5cdGVuYWJsZWQ6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gISF0aGlzLl9lbmFibGVkO1xuXHR9XG5cblx0Ly8gQHNlY3Rpb24gRXh0ZW5zaW9uIG1ldGhvZHNcblx0Ly8gQ2xhc3NlcyBpbmhlcml0aW5nIGZyb20gYEhhbmRsZXJgIG11c3QgaW1wbGVtZW50IHRoZSB0d28gZm9sbG93aW5nIG1ldGhvZHM6XG5cdC8vIEBtZXRob2QgYWRkSG9va3MoKVxuXHQvLyBDYWxsZWQgd2hlbiB0aGUgaGFuZGxlciBpcyBlbmFibGVkLCBzaG91bGQgYWRkIGV2ZW50IGhvb2tzLlxuXHQvLyBAbWV0aG9kIHJlbW92ZUhvb2tzKClcblx0Ly8gQ2FsbGVkIHdoZW4gdGhlIGhhbmRsZXIgaXMgZGlzYWJsZWQsIHNob3VsZCByZW1vdmUgdGhlIGV2ZW50IGhvb2tzIGFkZGVkIHByZXZpb3VzbHkuXG59KTtcblxuXG5cbi8qXG4gKiBMLkhhbmRsZXIuTWFwRHJhZyBpcyB1c2VkIHRvIG1ha2UgdGhlIG1hcCBkcmFnZ2FibGUgKHdpdGggcGFubmluZyBpbmVydGlhKSwgZW5hYmxlZCBieSBkZWZhdWx0LlxuICovXG5cbi8vIEBuYW1lc3BhY2UgTWFwXG4vLyBAc2VjdGlvbiBJbnRlcmFjdGlvbiBPcHRpb25zXG5MLk1hcC5tZXJnZU9wdGlvbnMoe1xuXHQvLyBAb3B0aW9uIGRyYWdnaW5nOiBCb29sZWFuID0gdHJ1ZVxuXHQvLyBXaGV0aGVyIHRoZSBtYXAgYmUgZHJhZ2dhYmxlIHdpdGggbW91c2UvdG91Y2ggb3Igbm90LlxuXHRkcmFnZ2luZzogdHJ1ZSxcblxuXHQvLyBAc2VjdGlvbiBQYW5uaW5nIEluZXJ0aWEgT3B0aW9uc1xuXHQvLyBAb3B0aW9uIGluZXJ0aWE6IEJvb2xlYW4gPSAqXG5cdC8vIElmIGVuYWJsZWQsIHBhbm5pbmcgb2YgdGhlIG1hcCB3aWxsIGhhdmUgYW4gaW5lcnRpYSBlZmZlY3Qgd2hlcmVcblx0Ly8gdGhlIG1hcCBidWlsZHMgbW9tZW50dW0gd2hpbGUgZHJhZ2dpbmcgYW5kIGNvbnRpbnVlcyBtb3ZpbmcgaW5cblx0Ly8gdGhlIHNhbWUgZGlyZWN0aW9uIGZvciBzb21lIHRpbWUuIEZlZWxzIGVzcGVjaWFsbHkgbmljZSBvbiB0b3VjaFxuXHQvLyBkZXZpY2VzLiBFbmFibGVkIGJ5IGRlZmF1bHQgdW5sZXNzIHJ1bm5pbmcgb24gb2xkIEFuZHJvaWQgZGV2aWNlcy5cblx0aW5lcnRpYTogIUwuQnJvd3Nlci5hbmRyb2lkMjMsXG5cblx0Ly8gQG9wdGlvbiBpbmVydGlhRGVjZWxlcmF0aW9uOiBOdW1iZXIgPSAzMDAwXG5cdC8vIFRoZSByYXRlIHdpdGggd2hpY2ggdGhlIGluZXJ0aWFsIG1vdmVtZW50IHNsb3dzIGRvd24sIGluIHBpeGVscy9zZWNvbmTCsi5cblx0aW5lcnRpYURlY2VsZXJhdGlvbjogMzQwMCwgLy8gcHgvc14yXG5cblx0Ly8gQG9wdGlvbiBpbmVydGlhTWF4U3BlZWQ6IE51bWJlciA9IEluZmluaXR5XG5cdC8vIE1heCBzcGVlZCBvZiB0aGUgaW5lcnRpYWwgbW92ZW1lbnQsIGluIHBpeGVscy9zZWNvbmQuXG5cdGluZXJ0aWFNYXhTcGVlZDogSW5maW5pdHksIC8vIHB4L3NcblxuXHQvLyBAb3B0aW9uIGVhc2VMaW5lYXJpdHk6IE51bWJlciA9IDAuMlxuXHRlYXNlTGluZWFyaXR5OiAwLjIsXG5cblx0Ly8gVE9ETyByZWZhY3RvciwgbW92ZSB0byBDUlNcblx0Ly8gQG9wdGlvbiB3b3JsZENvcHlKdW1wOiBCb29sZWFuID0gZmFsc2Vcblx0Ly8gV2l0aCB0aGlzIG9wdGlvbiBlbmFibGVkLCB0aGUgbWFwIHRyYWNrcyB3aGVuIHlvdSBwYW4gdG8gYW5vdGhlciBcImNvcHlcIlxuXHQvLyBvZiB0aGUgd29ybGQgYW5kIHNlYW1sZXNzbHkganVtcHMgdG8gdGhlIG9yaWdpbmFsIG9uZSBzbyB0aGF0IGFsbCBvdmVybGF5c1xuXHQvLyBsaWtlIG1hcmtlcnMgYW5kIHZlY3RvciBsYXllcnMgYXJlIHN0aWxsIHZpc2libGUuXG5cdHdvcmxkQ29weUp1bXA6IGZhbHNlLFxuXG5cdC8vIEBvcHRpb24gbWF4Qm91bmRzVmlzY29zaXR5OiBOdW1iZXIgPSAwLjBcblx0Ly8gSWYgYG1heEJvdW5kc2AgaXMgc2V0LCB0aGlzIG9wdGlvbiB3aWxsIGNvbnRyb2wgaG93IHNvbGlkIHRoZSBib3VuZHNcblx0Ly8gYXJlIHdoZW4gZHJhZ2dpbmcgdGhlIG1hcCBhcm91bmQuIFRoZSBkZWZhdWx0IHZhbHVlIG9mIGAwLjBgIGFsbG93cyB0aGVcblx0Ly8gdXNlciB0byBkcmFnIG91dHNpZGUgdGhlIGJvdW5kcyBhdCBub3JtYWwgc3BlZWQsIGhpZ2hlciB2YWx1ZXMgd2lsbFxuXHQvLyBzbG93IGRvd24gbWFwIGRyYWdnaW5nIG91dHNpZGUgYm91bmRzLCBhbmQgYDEuMGAgbWFrZXMgdGhlIGJvdW5kcyBmdWxseVxuXHQvLyBzb2xpZCwgcHJldmVudGluZyB0aGUgdXNlciBmcm9tIGRyYWdnaW5nIG91dHNpZGUgdGhlIGJvdW5kcy5cblx0bWF4Qm91bmRzVmlzY29zaXR5OiAwLjBcbn0pO1xuXG5MLk1hcC5EcmFnID0gTC5IYW5kbGVyLmV4dGVuZCh7XG5cdGFkZEhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKCF0aGlzLl9kcmFnZ2FibGUpIHtcblx0XHRcdHZhciBtYXAgPSB0aGlzLl9tYXA7XG5cblx0XHRcdHRoaXMuX2RyYWdnYWJsZSA9IG5ldyBMLkRyYWdnYWJsZShtYXAuX21hcFBhbmUsIG1hcC5fY29udGFpbmVyKTtcblxuXHRcdFx0dGhpcy5fZHJhZ2dhYmxlLm9uKHtcblx0XHRcdFx0ZG93bjogdGhpcy5fb25Eb3duLFxuXHRcdFx0XHRkcmFnc3RhcnQ6IHRoaXMuX29uRHJhZ1N0YXJ0LFxuXHRcdFx0XHRkcmFnOiB0aGlzLl9vbkRyYWcsXG5cdFx0XHRcdGRyYWdlbmQ6IHRoaXMuX29uRHJhZ0VuZFxuXHRcdFx0fSwgdGhpcyk7XG5cblx0XHRcdHRoaXMuX2RyYWdnYWJsZS5vbigncHJlZHJhZycsIHRoaXMuX29uUHJlRHJhZ0xpbWl0LCB0aGlzKTtcblx0XHRcdGlmIChtYXAub3B0aW9ucy53b3JsZENvcHlKdW1wKSB7XG5cdFx0XHRcdHRoaXMuX2RyYWdnYWJsZS5vbigncHJlZHJhZycsIHRoaXMuX29uUHJlRHJhZ1dyYXAsIHRoaXMpO1xuXHRcdFx0XHRtYXAub24oJ3pvb21lbmQnLCB0aGlzLl9vblpvb21FbmQsIHRoaXMpO1xuXG5cdFx0XHRcdG1hcC53aGVuUmVhZHkodGhpcy5fb25ab29tRW5kLCB0aGlzKTtcblx0XHRcdH1cblx0XHR9XG5cdFx0TC5Eb21VdGlsLmFkZENsYXNzKHRoaXMuX21hcC5fY29udGFpbmVyLCAnbGVhZmxldC1ncmFiIGxlYWZsZXQtdG91Y2gtZHJhZycpO1xuXHRcdHRoaXMuX2RyYWdnYWJsZS5lbmFibGUoKTtcblx0XHR0aGlzLl9wb3NpdGlvbnMgPSBbXTtcblx0XHR0aGlzLl90aW1lcyA9IFtdO1xuXHR9LFxuXG5cdHJlbW92ZUhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0TC5Eb21VdGlsLnJlbW92ZUNsYXNzKHRoaXMuX21hcC5fY29udGFpbmVyLCAnbGVhZmxldC1ncmFiJyk7XG5cdFx0TC5Eb21VdGlsLnJlbW92ZUNsYXNzKHRoaXMuX21hcC5fY29udGFpbmVyLCAnbGVhZmxldC10b3VjaC1kcmFnJyk7XG5cdFx0dGhpcy5fZHJhZ2dhYmxlLmRpc2FibGUoKTtcblx0fSxcblxuXHRtb3ZlZDogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9kcmFnZ2FibGUgJiYgdGhpcy5fZHJhZ2dhYmxlLl9tb3ZlZDtcblx0fSxcblxuXHRtb3Zpbmc6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5fZHJhZ2dhYmxlICYmIHRoaXMuX2RyYWdnYWJsZS5fbW92aW5nO1xuXHR9LFxuXG5cdF9vbkRvd246IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9tYXAuX3N0b3AoKTtcblx0fSxcblxuXHRfb25EcmFnU3RhcnQ6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwO1xuXG5cdFx0aWYgKHRoaXMuX21hcC5vcHRpb25zLm1heEJvdW5kcyAmJiB0aGlzLl9tYXAub3B0aW9ucy5tYXhCb3VuZHNWaXNjb3NpdHkpIHtcblx0XHRcdHZhciBib3VuZHMgPSBMLmxhdExuZ0JvdW5kcyh0aGlzLl9tYXAub3B0aW9ucy5tYXhCb3VuZHMpO1xuXG5cdFx0XHR0aGlzLl9vZmZzZXRMaW1pdCA9IEwuYm91bmRzKFxuXHRcdFx0XHR0aGlzLl9tYXAubGF0TG5nVG9Db250YWluZXJQb2ludChib3VuZHMuZ2V0Tm9ydGhXZXN0KCkpLm11bHRpcGx5QnkoLTEpLFxuXHRcdFx0XHR0aGlzLl9tYXAubGF0TG5nVG9Db250YWluZXJQb2ludChib3VuZHMuZ2V0U291dGhFYXN0KCkpLm11bHRpcGx5QnkoLTEpXG5cdFx0XHRcdFx0LmFkZCh0aGlzLl9tYXAuZ2V0U2l6ZSgpKSk7XG5cblx0XHRcdHRoaXMuX3Zpc2Nvc2l0eSA9IE1hdGgubWluKDEuMCwgTWF0aC5tYXgoMC4wLCB0aGlzLl9tYXAub3B0aW9ucy5tYXhCb3VuZHNWaXNjb3NpdHkpKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy5fb2Zmc2V0TGltaXQgPSBudWxsO1xuXHRcdH1cblxuXHRcdG1hcFxuXHRcdCAgICAuZmlyZSgnbW92ZXN0YXJ0Jylcblx0XHQgICAgLmZpcmUoJ2RyYWdzdGFydCcpO1xuXG5cdFx0aWYgKG1hcC5vcHRpb25zLmluZXJ0aWEpIHtcblx0XHRcdHRoaXMuX3Bvc2l0aW9ucyA9IFtdO1xuXHRcdFx0dGhpcy5fdGltZXMgPSBbXTtcblx0XHR9XG5cdH0sXG5cblx0X29uRHJhZzogZnVuY3Rpb24gKGUpIHtcblx0XHRpZiAodGhpcy5fbWFwLm9wdGlvbnMuaW5lcnRpYSkge1xuXHRcdFx0dmFyIHRpbWUgPSB0aGlzLl9sYXN0VGltZSA9ICtuZXcgRGF0ZSgpLFxuXHRcdFx0ICAgIHBvcyA9IHRoaXMuX2xhc3RQb3MgPSB0aGlzLl9kcmFnZ2FibGUuX2Fic1BvcyB8fCB0aGlzLl9kcmFnZ2FibGUuX25ld1BvcztcblxuXHRcdFx0dGhpcy5fcG9zaXRpb25zLnB1c2gocG9zKTtcblx0XHRcdHRoaXMuX3RpbWVzLnB1c2godGltZSk7XG5cblx0XHRcdGlmICh0aW1lIC0gdGhpcy5fdGltZXNbMF0gPiA1MCkge1xuXHRcdFx0XHR0aGlzLl9wb3NpdGlvbnMuc2hpZnQoKTtcblx0XHRcdFx0dGhpcy5fdGltZXMuc2hpZnQoKTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHR0aGlzLl9tYXBcblx0XHQgICAgLmZpcmUoJ21vdmUnLCBlKVxuXHRcdCAgICAuZmlyZSgnZHJhZycsIGUpO1xuXHR9LFxuXG5cdF9vblpvb21FbmQ6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgcHhDZW50ZXIgPSB0aGlzLl9tYXAuZ2V0U2l6ZSgpLmRpdmlkZUJ5KDIpLFxuXHRcdCAgICBweFdvcmxkQ2VudGVyID0gdGhpcy5fbWFwLmxhdExuZ1RvTGF5ZXJQb2ludChbMCwgMF0pO1xuXG5cdFx0dGhpcy5faW5pdGlhbFdvcmxkT2Zmc2V0ID0gcHhXb3JsZENlbnRlci5zdWJ0cmFjdChweENlbnRlcikueDtcblx0XHR0aGlzLl93b3JsZFdpZHRoID0gdGhpcy5fbWFwLmdldFBpeGVsV29ybGRCb3VuZHMoKS5nZXRTaXplKCkueDtcblx0fSxcblxuXHRfdmlzY291c0xpbWl0OiBmdW5jdGlvbiAodmFsdWUsIHRocmVzaG9sZCkge1xuXHRcdHJldHVybiB2YWx1ZSAtICh2YWx1ZSAtIHRocmVzaG9sZCkgKiB0aGlzLl92aXNjb3NpdHk7XG5cdH0sXG5cblx0X29uUHJlRHJhZ0xpbWl0OiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKCF0aGlzLl92aXNjb3NpdHkgfHwgIXRoaXMuX29mZnNldExpbWl0KSB7IHJldHVybjsgfVxuXG5cdFx0dmFyIG9mZnNldCA9IHRoaXMuX2RyYWdnYWJsZS5fbmV3UG9zLnN1YnRyYWN0KHRoaXMuX2RyYWdnYWJsZS5fc3RhcnRQb3MpO1xuXG5cdFx0dmFyIGxpbWl0ID0gdGhpcy5fb2Zmc2V0TGltaXQ7XG5cdFx0aWYgKG9mZnNldC54IDwgbGltaXQubWluLngpIHsgb2Zmc2V0LnggPSB0aGlzLl92aXNjb3VzTGltaXQob2Zmc2V0LngsIGxpbWl0Lm1pbi54KTsgfVxuXHRcdGlmIChvZmZzZXQueSA8IGxpbWl0Lm1pbi55KSB7IG9mZnNldC55ID0gdGhpcy5fdmlzY291c0xpbWl0KG9mZnNldC55LCBsaW1pdC5taW4ueSk7IH1cblx0XHRpZiAob2Zmc2V0LnggPiBsaW1pdC5tYXgueCkgeyBvZmZzZXQueCA9IHRoaXMuX3Zpc2NvdXNMaW1pdChvZmZzZXQueCwgbGltaXQubWF4LngpOyB9XG5cdFx0aWYgKG9mZnNldC55ID4gbGltaXQubWF4LnkpIHsgb2Zmc2V0LnkgPSB0aGlzLl92aXNjb3VzTGltaXQob2Zmc2V0LnksIGxpbWl0Lm1heC55KTsgfVxuXG5cdFx0dGhpcy5fZHJhZ2dhYmxlLl9uZXdQb3MgPSB0aGlzLl9kcmFnZ2FibGUuX3N0YXJ0UG9zLmFkZChvZmZzZXQpO1xuXHR9LFxuXG5cdF9vblByZURyYWdXcmFwOiBmdW5jdGlvbiAoKSB7XG5cdFx0Ly8gVE9ETyByZWZhY3RvciB0byBiZSBhYmxlIHRvIGFkanVzdCBtYXAgcGFuZSBwb3NpdGlvbiBhZnRlciB6b29tXG5cdFx0dmFyIHdvcmxkV2lkdGggPSB0aGlzLl93b3JsZFdpZHRoLFxuXHRcdCAgICBoYWxmV2lkdGggPSBNYXRoLnJvdW5kKHdvcmxkV2lkdGggLyAyKSxcblx0XHQgICAgZHggPSB0aGlzLl9pbml0aWFsV29ybGRPZmZzZXQsXG5cdFx0ICAgIHggPSB0aGlzLl9kcmFnZ2FibGUuX25ld1Bvcy54LFxuXHRcdCAgICBuZXdYMSA9ICh4IC0gaGFsZldpZHRoICsgZHgpICUgd29ybGRXaWR0aCArIGhhbGZXaWR0aCAtIGR4LFxuXHRcdCAgICBuZXdYMiA9ICh4ICsgaGFsZldpZHRoICsgZHgpICUgd29ybGRXaWR0aCAtIGhhbGZXaWR0aCAtIGR4LFxuXHRcdCAgICBuZXdYID0gTWF0aC5hYnMobmV3WDEgKyBkeCkgPCBNYXRoLmFicyhuZXdYMiArIGR4KSA/IG5ld1gxIDogbmV3WDI7XG5cblx0XHR0aGlzLl9kcmFnZ2FibGUuX2Fic1BvcyA9IHRoaXMuX2RyYWdnYWJsZS5fbmV3UG9zLmNsb25lKCk7XG5cdFx0dGhpcy5fZHJhZ2dhYmxlLl9uZXdQb3MueCA9IG5ld1g7XG5cdH0sXG5cblx0X29uRHJhZ0VuZDogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxuXHRcdCAgICBvcHRpb25zID0gbWFwLm9wdGlvbnMsXG5cblx0XHQgICAgbm9JbmVydGlhID0gIW9wdGlvbnMuaW5lcnRpYSB8fCB0aGlzLl90aW1lcy5sZW5ndGggPCAyO1xuXG5cdFx0bWFwLmZpcmUoJ2RyYWdlbmQnLCBlKTtcblxuXHRcdGlmIChub0luZXJ0aWEpIHtcblx0XHRcdG1hcC5maXJlKCdtb3ZlZW5kJyk7XG5cblx0XHR9IGVsc2Uge1xuXG5cdFx0XHR2YXIgZGlyZWN0aW9uID0gdGhpcy5fbGFzdFBvcy5zdWJ0cmFjdCh0aGlzLl9wb3NpdGlvbnNbMF0pLFxuXHRcdFx0ICAgIGR1cmF0aW9uID0gKHRoaXMuX2xhc3RUaW1lIC0gdGhpcy5fdGltZXNbMF0pIC8gMTAwMCxcblx0XHRcdCAgICBlYXNlID0gb3B0aW9ucy5lYXNlTGluZWFyaXR5LFxuXG5cdFx0XHQgICAgc3BlZWRWZWN0b3IgPSBkaXJlY3Rpb24ubXVsdGlwbHlCeShlYXNlIC8gZHVyYXRpb24pLFxuXHRcdFx0ICAgIHNwZWVkID0gc3BlZWRWZWN0b3IuZGlzdGFuY2VUbyhbMCwgMF0pLFxuXG5cdFx0XHQgICAgbGltaXRlZFNwZWVkID0gTWF0aC5taW4ob3B0aW9ucy5pbmVydGlhTWF4U3BlZWQsIHNwZWVkKSxcblx0XHRcdCAgICBsaW1pdGVkU3BlZWRWZWN0b3IgPSBzcGVlZFZlY3Rvci5tdWx0aXBseUJ5KGxpbWl0ZWRTcGVlZCAvIHNwZWVkKSxcblxuXHRcdFx0ICAgIGRlY2VsZXJhdGlvbkR1cmF0aW9uID0gbGltaXRlZFNwZWVkIC8gKG9wdGlvbnMuaW5lcnRpYURlY2VsZXJhdGlvbiAqIGVhc2UpLFxuXHRcdFx0ICAgIG9mZnNldCA9IGxpbWl0ZWRTcGVlZFZlY3Rvci5tdWx0aXBseUJ5KC1kZWNlbGVyYXRpb25EdXJhdGlvbiAvIDIpLnJvdW5kKCk7XG5cblx0XHRcdGlmICghb2Zmc2V0LnggJiYgIW9mZnNldC55KSB7XG5cdFx0XHRcdG1hcC5maXJlKCdtb3ZlZW5kJyk7XG5cblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdG9mZnNldCA9IG1hcC5fbGltaXRPZmZzZXQob2Zmc2V0LCBtYXAub3B0aW9ucy5tYXhCb3VuZHMpO1xuXG5cdFx0XHRcdEwuVXRpbC5yZXF1ZXN0QW5pbUZyYW1lKGZ1bmN0aW9uICgpIHtcblx0XHRcdFx0XHRtYXAucGFuQnkob2Zmc2V0LCB7XG5cdFx0XHRcdFx0XHRkdXJhdGlvbjogZGVjZWxlcmF0aW9uRHVyYXRpb24sXG5cdFx0XHRcdFx0XHRlYXNlTGluZWFyaXR5OiBlYXNlLFxuXHRcdFx0XHRcdFx0bm9Nb3ZlU3RhcnQ6IHRydWUsXG5cdFx0XHRcdFx0XHRhbmltYXRlOiB0cnVlXG5cdFx0XHRcdFx0fSk7XG5cdFx0XHRcdH0pO1xuXHRcdFx0fVxuXHRcdH1cblx0fVxufSk7XG5cbi8vIEBzZWN0aW9uIEhhbmRsZXJzXG4vLyBAcHJvcGVydHkgZHJhZ2dpbmc6IEhhbmRsZXJcbi8vIE1hcCBkcmFnZ2luZyBoYW5kbGVyIChieSBib3RoIG1vdXNlIGFuZCB0b3VjaCkuXG5MLk1hcC5hZGRJbml0SG9vaygnYWRkSGFuZGxlcicsICdkcmFnZ2luZycsIEwuTWFwLkRyYWcpO1xuXG5cblxuLypcbiAqIEwuSGFuZGxlci5Eb3VibGVDbGlja1pvb20gaXMgdXNlZCB0byBoYW5kbGUgZG91YmxlLWNsaWNrIHpvb20gb24gdGhlIG1hcCwgZW5hYmxlZCBieSBkZWZhdWx0LlxuICovXG5cbi8vIEBuYW1lc3BhY2UgTWFwXG4vLyBAc2VjdGlvbiBJbnRlcmFjdGlvbiBPcHRpb25zXG5cbkwuTWFwLm1lcmdlT3B0aW9ucyh7XG5cdC8vIEBvcHRpb24gZG91YmxlQ2xpY2tab29tOiBCb29sZWFufFN0cmluZyA9IHRydWVcblx0Ly8gV2hldGhlciB0aGUgbWFwIGNhbiBiZSB6b29tZWQgaW4gYnkgZG91YmxlIGNsaWNraW5nIG9uIGl0IGFuZFxuXHQvLyB6b29tZWQgb3V0IGJ5IGRvdWJsZSBjbGlja2luZyB3aGlsZSBob2xkaW5nIHNoaWZ0LiBJZiBwYXNzZWRcblx0Ly8gYCdjZW50ZXInYCwgZG91YmxlLWNsaWNrIHpvb20gd2lsbCB6b29tIHRvIHRoZSBjZW50ZXIgb2YgdGhlXG5cdC8vICB2aWV3IHJlZ2FyZGxlc3Mgb2Ygd2hlcmUgdGhlIG1vdXNlIHdhcy5cblx0ZG91YmxlQ2xpY2tab29tOiB0cnVlXG59KTtcblxuTC5NYXAuRG91YmxlQ2xpY2tab29tID0gTC5IYW5kbGVyLmV4dGVuZCh7XG5cdGFkZEhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fbWFwLm9uKCdkYmxjbGljaycsIHRoaXMuX29uRG91YmxlQ2xpY2ssIHRoaXMpO1xuXHR9LFxuXG5cdHJlbW92ZUhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fbWFwLm9mZignZGJsY2xpY2snLCB0aGlzLl9vbkRvdWJsZUNsaWNrLCB0aGlzKTtcblx0fSxcblxuXHRfb25Eb3VibGVDbGljazogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxuXHRcdCAgICBvbGRab29tID0gbWFwLmdldFpvb20oKSxcblx0XHQgICAgZGVsdGEgPSBtYXAub3B0aW9ucy56b29tRGVsdGEsXG5cdFx0ICAgIHpvb20gPSBlLm9yaWdpbmFsRXZlbnQuc2hpZnRLZXkgPyBvbGRab29tIC0gZGVsdGEgOiBvbGRab29tICsgZGVsdGE7XG5cblx0XHRpZiAobWFwLm9wdGlvbnMuZG91YmxlQ2xpY2tab29tID09PSAnY2VudGVyJykge1xuXHRcdFx0bWFwLnNldFpvb20oem9vbSk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdG1hcC5zZXRab29tQXJvdW5kKGUuY29udGFpbmVyUG9pbnQsIHpvb20pO1xuXHRcdH1cblx0fVxufSk7XG5cbi8vIEBzZWN0aW9uIEhhbmRsZXJzXG4vL1xuLy8gTWFwIHByb3BlcnRpZXMgaW5jbHVkZSBpbnRlcmFjdGlvbiBoYW5kbGVycyB0aGF0IGFsbG93IHlvdSB0byBjb250cm9sXG4vLyBpbnRlcmFjdGlvbiBiZWhhdmlvciBpbiBydW50aW1lLCBlbmFibGluZyBvciBkaXNhYmxpbmcgY2VydGFpbiBmZWF0dXJlcyBzdWNoXG4vLyBhcyBkcmFnZ2luZyBvciB0b3VjaCB6b29tIChzZWUgYEhhbmRsZXJgIG1ldGhvZHMpLiBGb3IgZXhhbXBsZTpcbi8vXG4vLyBgYGBqc1xuLy8gbWFwLmRvdWJsZUNsaWNrWm9vbS5kaXNhYmxlKCk7XG4vLyBgYGBcbi8vXG4vLyBAcHJvcGVydHkgZG91YmxlQ2xpY2tab29tOiBIYW5kbGVyXG4vLyBEb3VibGUgY2xpY2sgem9vbSBoYW5kbGVyLlxuTC5NYXAuYWRkSW5pdEhvb2soJ2FkZEhhbmRsZXInLCAnZG91YmxlQ2xpY2tab29tJywgTC5NYXAuRG91YmxlQ2xpY2tab29tKTtcblxuXG5cbi8qXG4gKiBMLkhhbmRsZXIuU2Nyb2xsV2hlZWxab29tIGlzIHVzZWQgYnkgTC5NYXAgdG8gZW5hYmxlIG1vdXNlIHNjcm9sbCB3aGVlbCB6b29tIG9uIHRoZSBtYXAuXG4gKi9cblxuLy8gQG5hbWVzcGFjZSBNYXBcbi8vIEBzZWN0aW9uIEludGVyYWN0aW9uIE9wdGlvbnNcbkwuTWFwLm1lcmdlT3B0aW9ucyh7XG5cdC8vIEBzZWN0aW9uIE1vdXNld2hlZWwgb3B0aW9uc1xuXHQvLyBAb3B0aW9uIHNjcm9sbFdoZWVsWm9vbTogQm9vbGVhbnxTdHJpbmcgPSB0cnVlXG5cdC8vIFdoZXRoZXIgdGhlIG1hcCBjYW4gYmUgem9vbWVkIGJ5IHVzaW5nIHRoZSBtb3VzZSB3aGVlbC4gSWYgcGFzc2VkIGAnY2VudGVyJ2AsXG5cdC8vIGl0IHdpbGwgem9vbSB0byB0aGUgY2VudGVyIG9mIHRoZSB2aWV3IHJlZ2FyZGxlc3Mgb2Ygd2hlcmUgdGhlIG1vdXNlIHdhcy5cblx0c2Nyb2xsV2hlZWxab29tOiB0cnVlLFxuXG5cdC8vIEBvcHRpb24gd2hlZWxEZWJvdW5jZVRpbWU6IE51bWJlciA9IDQwXG5cdC8vIExpbWl0cyB0aGUgcmF0ZSBhdCB3aGljaCBhIHdoZWVsIGNhbiBmaXJlIChpbiBtaWxsaXNlY29uZHMpLiBCeSBkZWZhdWx0XG5cdC8vIHVzZXIgY2FuJ3Qgem9vbSB2aWEgd2hlZWwgbW9yZSBvZnRlbiB0aGFuIG9uY2UgcGVyIDQwIG1zLlxuXHR3aGVlbERlYm91bmNlVGltZTogNDAsXG5cblx0Ly8gQG9wdGlvbiB3aGVlbFB4UGVyWm9vbUxldmVsOiBOdW1iZXIgPSA2MFxuXHQvLyBIb3cgbWFueSBzY3JvbGwgcGl4ZWxzIChhcyByZXBvcnRlZCBieSBbTC5Eb21FdmVudC5nZXRXaGVlbERlbHRhXSgjZG9tZXZlbnQtZ2V0d2hlZWxkZWx0YSkpXG5cdC8vIG1lYW4gYSBjaGFuZ2Ugb2Ygb25lIGZ1bGwgem9vbSBsZXZlbC4gU21hbGxlciB2YWx1ZXMgd2lsbCBtYWtlIHdoZWVsLXpvb21pbmdcblx0Ly8gZmFzdGVyIChhbmQgdmljZSB2ZXJzYSkuXG5cdHdoZWVsUHhQZXJab29tTGV2ZWw6IDYwXG59KTtcblxuTC5NYXAuU2Nyb2xsV2hlZWxab29tID0gTC5IYW5kbGVyLmV4dGVuZCh7XG5cdGFkZEhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0TC5Eb21FdmVudC5vbih0aGlzLl9tYXAuX2NvbnRhaW5lciwgJ21vdXNld2hlZWwnLCB0aGlzLl9vbldoZWVsU2Nyb2xsLCB0aGlzKTtcblxuXHRcdHRoaXMuX2RlbHRhID0gMDtcblx0fSxcblxuXHRyZW1vdmVIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdEwuRG9tRXZlbnQub2ZmKHRoaXMuX21hcC5fY29udGFpbmVyLCAnbW91c2V3aGVlbCcsIHRoaXMuX29uV2hlZWxTY3JvbGwsIHRoaXMpO1xuXHR9LFxuXG5cdF9vbldoZWVsU2Nyb2xsOiBmdW5jdGlvbiAoZSkge1xuXHRcdHZhciBkZWx0YSA9IEwuRG9tRXZlbnQuZ2V0V2hlZWxEZWx0YShlKTtcblxuXHRcdHZhciBkZWJvdW5jZSA9IHRoaXMuX21hcC5vcHRpb25zLndoZWVsRGVib3VuY2VUaW1lO1xuXG5cdFx0dGhpcy5fZGVsdGEgKz0gZGVsdGE7XG5cdFx0dGhpcy5fbGFzdE1vdXNlUG9zID0gdGhpcy5fbWFwLm1vdXNlRXZlbnRUb0NvbnRhaW5lclBvaW50KGUpO1xuXG5cdFx0aWYgKCF0aGlzLl9zdGFydFRpbWUpIHtcblx0XHRcdHRoaXMuX3N0YXJ0VGltZSA9ICtuZXcgRGF0ZSgpO1xuXHRcdH1cblxuXHRcdHZhciBsZWZ0ID0gTWF0aC5tYXgoZGVib3VuY2UgLSAoK25ldyBEYXRlKCkgLSB0aGlzLl9zdGFydFRpbWUpLCAwKTtcblxuXHRcdGNsZWFyVGltZW91dCh0aGlzLl90aW1lcik7XG5cdFx0dGhpcy5fdGltZXIgPSBzZXRUaW1lb3V0KEwuYmluZCh0aGlzLl9wZXJmb3JtWm9vbSwgdGhpcyksIGxlZnQpO1xuXG5cdFx0TC5Eb21FdmVudC5zdG9wKGUpO1xuXHR9LFxuXG5cdF9wZXJmb3JtWm9vbTogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBtYXAgPSB0aGlzLl9tYXAsXG5cdFx0ICAgIHpvb20gPSBtYXAuZ2V0Wm9vbSgpLFxuXHRcdCAgICBzbmFwID0gdGhpcy5fbWFwLm9wdGlvbnMuem9vbVNuYXAgfHwgMDtcblxuXHRcdG1hcC5fc3RvcCgpOyAvLyBzdG9wIHBhbm5pbmcgYW5kIGZseSBhbmltYXRpb25zIGlmIGFueVxuXG5cdFx0Ly8gbWFwIHRoZSBkZWx0YSB3aXRoIGEgc2lnbW9pZCBmdW5jdGlvbiB0byAtNC4uNCByYW5nZSBsZWFuaW5nIG9uIC0xLi4xXG5cdFx0dmFyIGQyID0gdGhpcy5fZGVsdGEgLyAodGhpcy5fbWFwLm9wdGlvbnMud2hlZWxQeFBlclpvb21MZXZlbCAqIDQpLFxuXHRcdCAgICBkMyA9IDQgKiBNYXRoLmxvZygyIC8gKDEgKyBNYXRoLmV4cCgtTWF0aC5hYnMoZDIpKSkpIC8gTWF0aC5MTjIsXG5cdFx0ICAgIGQ0ID0gc25hcCA/IE1hdGguY2VpbChkMyAvIHNuYXApICogc25hcCA6IGQzLFxuXHRcdCAgICBkZWx0YSA9IG1hcC5fbGltaXRab29tKHpvb20gKyAodGhpcy5fZGVsdGEgPiAwID8gZDQgOiAtZDQpKSAtIHpvb207XG5cblx0XHR0aGlzLl9kZWx0YSA9IDA7XG5cdFx0dGhpcy5fc3RhcnRUaW1lID0gbnVsbDtcblxuXHRcdGlmICghZGVsdGEpIHsgcmV0dXJuOyB9XG5cblx0XHRpZiAobWFwLm9wdGlvbnMuc2Nyb2xsV2hlZWxab29tID09PSAnY2VudGVyJykge1xuXHRcdFx0bWFwLnNldFpvb20oem9vbSArIGRlbHRhKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0bWFwLnNldFpvb21Bcm91bmQodGhpcy5fbGFzdE1vdXNlUG9zLCB6b29tICsgZGVsdGEpO1xuXHRcdH1cblx0fVxufSk7XG5cbi8vIEBzZWN0aW9uIEhhbmRsZXJzXG4vLyBAcHJvcGVydHkgc2Nyb2xsV2hlZWxab29tOiBIYW5kbGVyXG4vLyBTY3JvbGwgd2hlZWwgem9vbSBoYW5kbGVyLlxuTC5NYXAuYWRkSW5pdEhvb2soJ2FkZEhhbmRsZXInLCAnc2Nyb2xsV2hlZWxab29tJywgTC5NYXAuU2Nyb2xsV2hlZWxab29tKTtcblxuXG5cbi8qXHJcbiAqIEV4dGVuZHMgdGhlIGV2ZW50IGhhbmRsaW5nIGNvZGUgd2l0aCBkb3VibGUgdGFwIHN1cHBvcnQgZm9yIG1vYmlsZSBicm93c2Vycy5cclxuICovXHJcblxyXG5MLmV4dGVuZChMLkRvbUV2ZW50LCB7XHJcblxyXG5cdF90b3VjaHN0YXJ0OiBMLkJyb3dzZXIubXNQb2ludGVyID8gJ01TUG9pbnRlckRvd24nIDogTC5Ccm93c2VyLnBvaW50ZXIgPyAncG9pbnRlcmRvd24nIDogJ3RvdWNoc3RhcnQnLFxyXG5cdF90b3VjaGVuZDogTC5Ccm93c2VyLm1zUG9pbnRlciA/ICdNU1BvaW50ZXJVcCcgOiBMLkJyb3dzZXIucG9pbnRlciA/ICdwb2ludGVydXAnIDogJ3RvdWNoZW5kJyxcclxuXHJcblx0Ly8gaW5zcGlyZWQgYnkgWmVwdG8gdG91Y2ggY29kZSBieSBUaG9tYXMgRnVjaHNcclxuXHRhZGREb3VibGVUYXBMaXN0ZW5lcjogZnVuY3Rpb24gKG9iaiwgaGFuZGxlciwgaWQpIHtcclxuXHRcdHZhciBsYXN0LCB0b3VjaCxcclxuXHRcdCAgICBkb3VibGVUYXAgPSBmYWxzZSxcclxuXHRcdCAgICBkZWxheSA9IDI1MDtcclxuXHJcblx0XHRmdW5jdGlvbiBvblRvdWNoU3RhcnQoZSkge1xyXG5cdFx0XHR2YXIgY291bnQ7XHJcblxyXG5cdFx0XHRpZiAoTC5Ccm93c2VyLnBvaW50ZXIpIHtcclxuXHRcdFx0XHRjb3VudCA9IEwuRG9tRXZlbnQuX3BvaW50ZXJzQ291bnQ7XHJcblx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0Y291bnQgPSBlLnRvdWNoZXMubGVuZ3RoO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHRpZiAoY291bnQgPiAxKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdFx0dmFyIG5vdyA9IERhdGUubm93KCksXHJcblx0XHRcdCAgICBkZWx0YSA9IG5vdyAtIChsYXN0IHx8IG5vdyk7XHJcblxyXG5cdFx0XHR0b3VjaCA9IGUudG91Y2hlcyA/IGUudG91Y2hlc1swXSA6IGU7XHJcblx0XHRcdGRvdWJsZVRhcCA9IChkZWx0YSA+IDAgJiYgZGVsdGEgPD0gZGVsYXkpO1xyXG5cdFx0XHRsYXN0ID0gbm93O1xyXG5cdFx0fVxyXG5cclxuXHRcdGZ1bmN0aW9uIG9uVG91Y2hFbmQoKSB7XHJcblx0XHRcdGlmIChkb3VibGVUYXAgJiYgIXRvdWNoLmNhbmNlbEJ1YmJsZSkge1xyXG5cdFx0XHRcdGlmIChMLkJyb3dzZXIucG9pbnRlcikge1xyXG5cdFx0XHRcdFx0Ly8gd29yayBhcm91bmQgLnR5cGUgYmVpbmcgcmVhZG9ubHkgd2l0aCBNU1BvaW50ZXIqIGV2ZW50c1xyXG5cdFx0XHRcdFx0dmFyIG5ld1RvdWNoID0ge30sXHJcblx0XHRcdFx0XHQgICAgcHJvcCwgaTtcclxuXHJcblx0XHRcdFx0XHRmb3IgKGkgaW4gdG91Y2gpIHtcclxuXHRcdFx0XHRcdFx0cHJvcCA9IHRvdWNoW2ldO1xyXG5cdFx0XHRcdFx0XHRuZXdUb3VjaFtpXSA9IHByb3AgJiYgcHJvcC5iaW5kID8gcHJvcC5iaW5kKHRvdWNoKSA6IHByb3A7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHR0b3VjaCA9IG5ld1RvdWNoO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHR0b3VjaC50eXBlID0gJ2RibGNsaWNrJztcclxuXHRcdFx0XHRoYW5kbGVyKHRvdWNoKTtcclxuXHRcdFx0XHRsYXN0ID0gbnVsbDtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBwcmUgPSAnX2xlYWZsZXRfJyxcclxuXHRcdCAgICB0b3VjaHN0YXJ0ID0gdGhpcy5fdG91Y2hzdGFydCxcclxuXHRcdCAgICB0b3VjaGVuZCA9IHRoaXMuX3RvdWNoZW5kO1xyXG5cclxuXHRcdG9ialtwcmUgKyB0b3VjaHN0YXJ0ICsgaWRdID0gb25Ub3VjaFN0YXJ0O1xyXG5cdFx0b2JqW3ByZSArIHRvdWNoZW5kICsgaWRdID0gb25Ub3VjaEVuZDtcclxuXHRcdG9ialtwcmUgKyAnZGJsY2xpY2snICsgaWRdID0gaGFuZGxlcjtcclxuXHJcblx0XHRvYmouYWRkRXZlbnRMaXN0ZW5lcih0b3VjaHN0YXJ0LCBvblRvdWNoU3RhcnQsIGZhbHNlKTtcclxuXHRcdG9iai5hZGRFdmVudExpc3RlbmVyKHRvdWNoZW5kLCBvblRvdWNoRW5kLCBmYWxzZSk7XHJcblxyXG5cdFx0Ly8gT24gc29tZSBwbGF0Zm9ybXMgKG5vdGFibHksIGNocm9tZSBvbiB3aW4xMCArIHRvdWNoc2NyZWVuICsgbW91c2UpLFxyXG5cdFx0Ly8gdGhlIGJyb3dzZXIgZG9lc24ndCBmaXJlIHRvdWNoZW5kL3BvaW50ZXJ1cCBldmVudHMgYnV0IGRvZXMgZmlyZVxyXG5cdFx0Ly8gbmF0aXZlIGRibGNsaWNrcy4gU2VlICM0MTI3LlxyXG5cdFx0aWYgKCFMLkJyb3dzZXIuZWRnZSkge1xyXG5cdFx0XHRvYmouYWRkRXZlbnRMaXN0ZW5lcignZGJsY2xpY2snLCBoYW5kbGVyLCBmYWxzZSk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0cmVtb3ZlRG91YmxlVGFwTGlzdGVuZXI6IGZ1bmN0aW9uIChvYmosIGlkKSB7XHJcblx0XHR2YXIgcHJlID0gJ19sZWFmbGV0XycsXHJcblx0XHQgICAgdG91Y2hzdGFydCA9IG9ialtwcmUgKyB0aGlzLl90b3VjaHN0YXJ0ICsgaWRdLFxyXG5cdFx0ICAgIHRvdWNoZW5kID0gb2JqW3ByZSArIHRoaXMuX3RvdWNoZW5kICsgaWRdLFxyXG5cdFx0ICAgIGRibGNsaWNrID0gb2JqW3ByZSArICdkYmxjbGljaycgKyBpZF07XHJcblxyXG5cdFx0b2JqLnJlbW92ZUV2ZW50TGlzdGVuZXIodGhpcy5fdG91Y2hzdGFydCwgdG91Y2hzdGFydCwgZmFsc2UpO1xyXG5cdFx0b2JqLnJlbW92ZUV2ZW50TGlzdGVuZXIodGhpcy5fdG91Y2hlbmQsIHRvdWNoZW5kLCBmYWxzZSk7XHJcblx0XHRpZiAoIUwuQnJvd3Nlci5lZGdlKSB7XHJcblx0XHRcdG9iai5yZW1vdmVFdmVudExpc3RlbmVyKCdkYmxjbGljaycsIGRibGNsaWNrLCBmYWxzZSk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fVxyXG59KTtcclxuXG5cblxuLypcbiAqIEV4dGVuZHMgTC5Eb21FdmVudCB0byBwcm92aWRlIHRvdWNoIHN1cHBvcnQgZm9yIEludGVybmV0IEV4cGxvcmVyIGFuZCBXaW5kb3dzLWJhc2VkIGRldmljZXMuXG4gKi9cblxuTC5leHRlbmQoTC5Eb21FdmVudCwge1xuXG5cdFBPSU5URVJfRE9XTjogICBMLkJyb3dzZXIubXNQb2ludGVyID8gJ01TUG9pbnRlckRvd24nICAgOiAncG9pbnRlcmRvd24nLFxuXHRQT0lOVEVSX01PVkU6ICAgTC5Ccm93c2VyLm1zUG9pbnRlciA/ICdNU1BvaW50ZXJNb3ZlJyAgIDogJ3BvaW50ZXJtb3ZlJyxcblx0UE9JTlRFUl9VUDogICAgIEwuQnJvd3Nlci5tc1BvaW50ZXIgPyAnTVNQb2ludGVyVXAnICAgICA6ICdwb2ludGVydXAnLFxuXHRQT0lOVEVSX0NBTkNFTDogTC5Ccm93c2VyLm1zUG9pbnRlciA/ICdNU1BvaW50ZXJDYW5jZWwnIDogJ3BvaW50ZXJjYW5jZWwnLFxuXHRUQUdfV0hJVEVfTElTVDogWydJTlBVVCcsICdTRUxFQ1QnLCAnT1BUSU9OJ10sXG5cblx0X3BvaW50ZXJzOiB7fSxcblx0X3BvaW50ZXJzQ291bnQ6IDAsXG5cblx0Ly8gUHJvdmlkZXMgYSB0b3VjaCBldmVudHMgd3JhcHBlciBmb3IgKG1zKXBvaW50ZXIgZXZlbnRzLlxuXHQvLyByZWYgaHR0cDovL3d3dy53My5vcmcvVFIvcG9pbnRlcmV2ZW50cy8gaHR0cHM6Ly93d3cudzMub3JnL0J1Z3MvUHVibGljL3Nob3dfYnVnLmNnaT9pZD0yMjg5MFxuXG5cdGFkZFBvaW50ZXJMaXN0ZW5lcjogZnVuY3Rpb24gKG9iaiwgdHlwZSwgaGFuZGxlciwgaWQpIHtcblxuXHRcdGlmICh0eXBlID09PSAndG91Y2hzdGFydCcpIHtcblx0XHRcdHRoaXMuX2FkZFBvaW50ZXJTdGFydChvYmosIGhhbmRsZXIsIGlkKTtcblxuXHRcdH0gZWxzZSBpZiAodHlwZSA9PT0gJ3RvdWNobW92ZScpIHtcblx0XHRcdHRoaXMuX2FkZFBvaW50ZXJNb3ZlKG9iaiwgaGFuZGxlciwgaWQpO1xuXG5cdFx0fSBlbHNlIGlmICh0eXBlID09PSAndG91Y2hlbmQnKSB7XG5cdFx0XHR0aGlzLl9hZGRQb2ludGVyRW5kKG9iaiwgaGFuZGxlciwgaWQpO1xuXHRcdH1cblxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdHJlbW92ZVBvaW50ZXJMaXN0ZW5lcjogZnVuY3Rpb24gKG9iaiwgdHlwZSwgaWQpIHtcblx0XHR2YXIgaGFuZGxlciA9IG9ialsnX2xlYWZsZXRfJyArIHR5cGUgKyBpZF07XG5cblx0XHRpZiAodHlwZSA9PT0gJ3RvdWNoc3RhcnQnKSB7XG5cdFx0XHRvYmoucmVtb3ZlRXZlbnRMaXN0ZW5lcih0aGlzLlBPSU5URVJfRE9XTiwgaGFuZGxlciwgZmFsc2UpO1xuXG5cdFx0fSBlbHNlIGlmICh0eXBlID09PSAndG91Y2htb3ZlJykge1xuXHRcdFx0b2JqLnJlbW92ZUV2ZW50TGlzdGVuZXIodGhpcy5QT0lOVEVSX01PVkUsIGhhbmRsZXIsIGZhbHNlKTtcblxuXHRcdH0gZWxzZSBpZiAodHlwZSA9PT0gJ3RvdWNoZW5kJykge1xuXHRcdFx0b2JqLnJlbW92ZUV2ZW50TGlzdGVuZXIodGhpcy5QT0lOVEVSX1VQLCBoYW5kbGVyLCBmYWxzZSk7XG5cdFx0XHRvYmoucmVtb3ZlRXZlbnRMaXN0ZW5lcih0aGlzLlBPSU5URVJfQ0FOQ0VMLCBoYW5kbGVyLCBmYWxzZSk7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0X2FkZFBvaW50ZXJTdGFydDogZnVuY3Rpb24gKG9iaiwgaGFuZGxlciwgaWQpIHtcblx0XHR2YXIgb25Eb3duID0gTC5iaW5kKGZ1bmN0aW9uIChlKSB7XG5cdFx0XHRpZiAoZS5wb2ludGVyVHlwZSAhPT0gJ21vdXNlJyAmJiBlLnBvaW50ZXJUeXBlICE9PSBlLk1TUE9JTlRFUl9UWVBFX01PVVNFKSB7XG5cdFx0XHRcdC8vIEluIElFMTEsIHNvbWUgdG91Y2ggZXZlbnRzIG5lZWRzIHRvIGZpcmUgZm9yIGZvcm0gY29udHJvbHMsIG9yXG5cdFx0XHRcdC8vIHRoZSBjb250cm9scyB3aWxsIHN0b3Agd29ya2luZy4gV2Uga2VlcCBhIHdoaXRlbGlzdCBvZiB0YWcgbmFtZXMgdGhhdFxuXHRcdFx0XHQvLyBuZWVkIHRoZXNlIGV2ZW50cy4gRm9yIG90aGVyIHRhcmdldCB0YWdzLCB3ZSBwcmV2ZW50IGRlZmF1bHQgb24gdGhlIGV2ZW50LlxuXHRcdFx0XHRpZiAodGhpcy5UQUdfV0hJVEVfTElTVC5pbmRleE9mKGUudGFyZ2V0LnRhZ05hbWUpIDwgMCkge1xuXHRcdFx0XHRcdEwuRG9tRXZlbnQucHJldmVudERlZmF1bHQoZSk7XG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cblx0XHRcdHRoaXMuX2hhbmRsZVBvaW50ZXIoZSwgaGFuZGxlcik7XG5cdFx0fSwgdGhpcyk7XG5cblx0XHRvYmpbJ19sZWFmbGV0X3RvdWNoc3RhcnQnICsgaWRdID0gb25Eb3duO1xuXHRcdG9iai5hZGRFdmVudExpc3RlbmVyKHRoaXMuUE9JTlRFUl9ET1dOLCBvbkRvd24sIGZhbHNlKTtcblxuXHRcdC8vIG5lZWQgdG8ga2VlcCB0cmFjayBvZiB3aGF0IHBvaW50ZXJzIGFuZCBob3cgbWFueSBhcmUgYWN0aXZlIHRvIHByb3ZpZGUgZS50b3VjaGVzIGVtdWxhdGlvblxuXHRcdGlmICghdGhpcy5fcG9pbnRlckRvY0xpc3RlbmVyKSB7XG5cdFx0XHR2YXIgcG9pbnRlclVwID0gTC5iaW5kKHRoaXMuX2dsb2JhbFBvaW50ZXJVcCwgdGhpcyk7XG5cblx0XHRcdC8vIHdlIGxpc3RlbiBkb2N1bWVudEVsZW1lbnQgYXMgYW55IGRyYWdzIHRoYXQgZW5kIGJ5IG1vdmluZyB0aGUgdG91Y2ggb2ZmIHRoZSBzY3JlZW4gZ2V0IGZpcmVkIHRoZXJlXG5cdFx0XHRkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcih0aGlzLlBPSU5URVJfRE9XTiwgTC5iaW5kKHRoaXMuX2dsb2JhbFBvaW50ZXJEb3duLCB0aGlzKSwgdHJ1ZSk7XG5cdFx0XHRkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcih0aGlzLlBPSU5URVJfTU9WRSwgTC5iaW5kKHRoaXMuX2dsb2JhbFBvaW50ZXJNb3ZlLCB0aGlzKSwgdHJ1ZSk7XG5cdFx0XHRkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcih0aGlzLlBPSU5URVJfVVAsIHBvaW50ZXJVcCwgdHJ1ZSk7XG5cdFx0XHRkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcih0aGlzLlBPSU5URVJfQ0FOQ0VMLCBwb2ludGVyVXAsIHRydWUpO1xuXG5cdFx0XHR0aGlzLl9wb2ludGVyRG9jTGlzdGVuZXIgPSB0cnVlO1xuXHRcdH1cblx0fSxcblxuXHRfZ2xvYmFsUG9pbnRlckRvd246IGZ1bmN0aW9uIChlKSB7XG5cdFx0dGhpcy5fcG9pbnRlcnNbZS5wb2ludGVySWRdID0gZTtcblx0XHR0aGlzLl9wb2ludGVyc0NvdW50Kys7XG5cdH0sXG5cblx0X2dsb2JhbFBvaW50ZXJNb3ZlOiBmdW5jdGlvbiAoZSkge1xuXHRcdGlmICh0aGlzLl9wb2ludGVyc1tlLnBvaW50ZXJJZF0pIHtcblx0XHRcdHRoaXMuX3BvaW50ZXJzW2UucG9pbnRlcklkXSA9IGU7XG5cdFx0fVxuXHR9LFxuXG5cdF9nbG9iYWxQb2ludGVyVXA6IGZ1bmN0aW9uIChlKSB7XG5cdFx0ZGVsZXRlIHRoaXMuX3BvaW50ZXJzW2UucG9pbnRlcklkXTtcblx0XHR0aGlzLl9wb2ludGVyc0NvdW50LS07XG5cdH0sXG5cblx0X2hhbmRsZVBvaW50ZXI6IGZ1bmN0aW9uIChlLCBoYW5kbGVyKSB7XG5cdFx0ZS50b3VjaGVzID0gW107XG5cdFx0Zm9yICh2YXIgaSBpbiB0aGlzLl9wb2ludGVycykge1xuXHRcdFx0ZS50b3VjaGVzLnB1c2godGhpcy5fcG9pbnRlcnNbaV0pO1xuXHRcdH1cblx0XHRlLmNoYW5nZWRUb3VjaGVzID0gW2VdO1xuXG5cdFx0aGFuZGxlcihlKTtcblx0fSxcblxuXHRfYWRkUG9pbnRlck1vdmU6IGZ1bmN0aW9uIChvYmosIGhhbmRsZXIsIGlkKSB7XG5cdFx0dmFyIG9uTW92ZSA9IEwuYmluZChmdW5jdGlvbiAoZSkge1xuXHRcdFx0Ly8gZG9uJ3QgZmlyZSB0b3VjaCBtb3ZlcyB3aGVuIG1vdXNlIGlzbid0IGRvd25cblx0XHRcdGlmICgoZS5wb2ludGVyVHlwZSA9PT0gZS5NU1BPSU5URVJfVFlQRV9NT1VTRSB8fCBlLnBvaW50ZXJUeXBlID09PSAnbW91c2UnKSAmJiBlLmJ1dHRvbnMgPT09IDApIHsgcmV0dXJuOyB9XG5cblx0XHRcdHRoaXMuX2hhbmRsZVBvaW50ZXIoZSwgaGFuZGxlcik7XG5cdFx0fSwgdGhpcyk7XG5cblx0XHRvYmpbJ19sZWFmbGV0X3RvdWNobW92ZScgKyBpZF0gPSBvbk1vdmU7XG5cdFx0b2JqLmFkZEV2ZW50TGlzdGVuZXIodGhpcy5QT0lOVEVSX01PVkUsIG9uTW92ZSwgZmFsc2UpO1xuXHR9LFxuXG5cdF9hZGRQb2ludGVyRW5kOiBmdW5jdGlvbiAob2JqLCBoYW5kbGVyLCBpZCkge1xuXHRcdHZhciBvblVwID0gTC5iaW5kKGZ1bmN0aW9uIChlKSB7XG5cdFx0XHR0aGlzLl9oYW5kbGVQb2ludGVyKGUsIGhhbmRsZXIpO1xuXHRcdH0sIHRoaXMpO1xuXG5cdFx0b2JqWydfbGVhZmxldF90b3VjaGVuZCcgKyBpZF0gPSBvblVwO1xuXHRcdG9iai5hZGRFdmVudExpc3RlbmVyKHRoaXMuUE9JTlRFUl9VUCwgb25VcCwgZmFsc2UpO1xuXHRcdG9iai5hZGRFdmVudExpc3RlbmVyKHRoaXMuUE9JTlRFUl9DQU5DRUwsIG9uVXAsIGZhbHNlKTtcblx0fVxufSk7XG5cblxuXG4vKlxuICogTC5IYW5kbGVyLlRvdWNoWm9vbSBpcyB1c2VkIGJ5IEwuTWFwIHRvIGFkZCBwaW5jaCB6b29tIG9uIHN1cHBvcnRlZCBtb2JpbGUgYnJvd3NlcnMuXG4gKi9cblxuLy8gQG5hbWVzcGFjZSBNYXBcbi8vIEBzZWN0aW9uIEludGVyYWN0aW9uIE9wdGlvbnNcbkwuTWFwLm1lcmdlT3B0aW9ucyh7XG5cdC8vIEBzZWN0aW9uIFRvdWNoIGludGVyYWN0aW9uIG9wdGlvbnNcblx0Ly8gQG9wdGlvbiB0b3VjaFpvb206IEJvb2xlYW58U3RyaW5nID0gKlxuXHQvLyBXaGV0aGVyIHRoZSBtYXAgY2FuIGJlIHpvb21lZCBieSB0b3VjaC1kcmFnZ2luZyB3aXRoIHR3byBmaW5nZXJzLiBJZlxuXHQvLyBwYXNzZWQgYCdjZW50ZXInYCwgaXQgd2lsbCB6b29tIHRvIHRoZSBjZW50ZXIgb2YgdGhlIHZpZXcgcmVnYXJkbGVzcyBvZlxuXHQvLyB3aGVyZSB0aGUgdG91Y2ggZXZlbnRzIChmaW5nZXJzKSB3ZXJlLiBFbmFibGVkIGZvciB0b3VjaC1jYXBhYmxlIHdlYlxuXHQvLyBicm93c2VycyBleGNlcHQgZm9yIG9sZCBBbmRyb2lkcy5cblx0dG91Y2hab29tOiBMLkJyb3dzZXIudG91Y2ggJiYgIUwuQnJvd3Nlci5hbmRyb2lkMjMsXG5cblx0Ly8gQG9wdGlvbiBib3VuY2VBdFpvb21MaW1pdHM6IEJvb2xlYW4gPSB0cnVlXG5cdC8vIFNldCBpdCB0byBmYWxzZSBpZiB5b3UgZG9uJ3Qgd2FudCB0aGUgbWFwIHRvIHpvb20gYmV5b25kIG1pbi9tYXggem9vbVxuXHQvLyBhbmQgdGhlbiBib3VuY2UgYmFjayB3aGVuIHBpbmNoLXpvb21pbmcuXG5cdGJvdW5jZUF0Wm9vbUxpbWl0czogdHJ1ZVxufSk7XG5cbkwuTWFwLlRvdWNoWm9vbSA9IEwuSGFuZGxlci5leHRlbmQoe1xuXHRhZGRIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9tYXAuX2NvbnRhaW5lciwgJ2xlYWZsZXQtdG91Y2gtem9vbScpO1xuXHRcdEwuRG9tRXZlbnQub24odGhpcy5fbWFwLl9jb250YWluZXIsICd0b3VjaHN0YXJ0JywgdGhpcy5fb25Ub3VjaFN0YXJ0LCB0aGlzKTtcblx0fSxcblxuXHRyZW1vdmVIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9tYXAuX2NvbnRhaW5lciwgJ2xlYWZsZXQtdG91Y2gtem9vbScpO1xuXHRcdEwuRG9tRXZlbnQub2ZmKHRoaXMuX21hcC5fY29udGFpbmVyLCAndG91Y2hzdGFydCcsIHRoaXMuX29uVG91Y2hTdGFydCwgdGhpcyk7XG5cdH0sXG5cblx0X29uVG91Y2hTdGFydDogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwO1xuXHRcdGlmICghZS50b3VjaGVzIHx8IGUudG91Y2hlcy5sZW5ndGggIT09IDIgfHwgbWFwLl9hbmltYXRpbmdab29tIHx8IHRoaXMuX3pvb21pbmcpIHsgcmV0dXJuOyB9XG5cblx0XHR2YXIgcDEgPSBtYXAubW91c2VFdmVudFRvQ29udGFpbmVyUG9pbnQoZS50b3VjaGVzWzBdKSxcblx0XHQgICAgcDIgPSBtYXAubW91c2VFdmVudFRvQ29udGFpbmVyUG9pbnQoZS50b3VjaGVzWzFdKTtcblxuXHRcdHRoaXMuX2NlbnRlclBvaW50ID0gbWFwLmdldFNpemUoKS5fZGl2aWRlQnkoMik7XG5cdFx0dGhpcy5fc3RhcnRMYXRMbmcgPSBtYXAuY29udGFpbmVyUG9pbnRUb0xhdExuZyh0aGlzLl9jZW50ZXJQb2ludCk7XG5cdFx0aWYgKG1hcC5vcHRpb25zLnRvdWNoWm9vbSAhPT0gJ2NlbnRlcicpIHtcblx0XHRcdHRoaXMuX3BpbmNoU3RhcnRMYXRMbmcgPSBtYXAuY29udGFpbmVyUG9pbnRUb0xhdExuZyhwMS5hZGQocDIpLl9kaXZpZGVCeSgyKSk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fc3RhcnREaXN0ID0gcDEuZGlzdGFuY2VUbyhwMik7XG5cdFx0dGhpcy5fc3RhcnRab29tID0gbWFwLmdldFpvb20oKTtcblxuXHRcdHRoaXMuX21vdmVkID0gZmFsc2U7XG5cdFx0dGhpcy5fem9vbWluZyA9IHRydWU7XG5cblx0XHRtYXAuX3N0b3AoKTtcblxuXHRcdEwuRG9tRXZlbnRcblx0XHQgICAgLm9uKGRvY3VtZW50LCAndG91Y2htb3ZlJywgdGhpcy5fb25Ub3VjaE1vdmUsIHRoaXMpXG5cdFx0ICAgIC5vbihkb2N1bWVudCwgJ3RvdWNoZW5kJywgdGhpcy5fb25Ub3VjaEVuZCwgdGhpcyk7XG5cblx0XHRMLkRvbUV2ZW50LnByZXZlbnREZWZhdWx0KGUpO1xuXHR9LFxuXG5cdF9vblRvdWNoTW92ZTogZnVuY3Rpb24gKGUpIHtcblx0XHRpZiAoIWUudG91Y2hlcyB8fCBlLnRvdWNoZXMubGVuZ3RoICE9PSAyIHx8ICF0aGlzLl96b29taW5nKSB7IHJldHVybjsgfVxuXG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcCxcblx0XHQgICAgcDEgPSBtYXAubW91c2VFdmVudFRvQ29udGFpbmVyUG9pbnQoZS50b3VjaGVzWzBdKSxcblx0XHQgICAgcDIgPSBtYXAubW91c2VFdmVudFRvQ29udGFpbmVyUG9pbnQoZS50b3VjaGVzWzFdKSxcblx0XHQgICAgc2NhbGUgPSBwMS5kaXN0YW5jZVRvKHAyKSAvIHRoaXMuX3N0YXJ0RGlzdDtcblxuXG5cdFx0dGhpcy5fem9vbSA9IG1hcC5nZXRTY2FsZVpvb20oc2NhbGUsIHRoaXMuX3N0YXJ0Wm9vbSk7XG5cblx0XHRpZiAoIW1hcC5vcHRpb25zLmJvdW5jZUF0Wm9vbUxpbWl0cyAmJiAoXG5cdFx0XHQodGhpcy5fem9vbSA8IG1hcC5nZXRNaW5ab29tKCkgJiYgc2NhbGUgPCAxKSB8fFxuXHRcdFx0KHRoaXMuX3pvb20gPiBtYXAuZ2V0TWF4Wm9vbSgpICYmIHNjYWxlID4gMSkpKSB7XG5cdFx0XHR0aGlzLl96b29tID0gbWFwLl9saW1pdFpvb20odGhpcy5fem9vbSk7XG5cdFx0fVxuXG5cdFx0aWYgKG1hcC5vcHRpb25zLnRvdWNoWm9vbSA9PT0gJ2NlbnRlcicpIHtcblx0XHRcdHRoaXMuX2NlbnRlciA9IHRoaXMuX3N0YXJ0TGF0TG5nO1xuXHRcdFx0aWYgKHNjYWxlID09PSAxKSB7IHJldHVybjsgfVxuXHRcdH0gZWxzZSB7XG5cdFx0XHQvLyBHZXQgZGVsdGEgZnJvbSBwaW5jaCB0byBjZW50ZXIsIHNvIGNlbnRlckxhdExuZyBpcyBkZWx0YSBhcHBsaWVkIHRvIGluaXRpYWwgcGluY2hMYXRMbmdcblx0XHRcdHZhciBkZWx0YSA9IHAxLl9hZGQocDIpLl9kaXZpZGVCeSgyKS5fc3VidHJhY3QodGhpcy5fY2VudGVyUG9pbnQpO1xuXHRcdFx0aWYgKHNjYWxlID09PSAxICYmIGRlbHRhLnggPT09IDAgJiYgZGVsdGEueSA9PT0gMCkgeyByZXR1cm47IH1cblx0XHRcdHRoaXMuX2NlbnRlciA9IG1hcC51bnByb2plY3QobWFwLnByb2plY3QodGhpcy5fcGluY2hTdGFydExhdExuZywgdGhpcy5fem9vbSkuc3VidHJhY3QoZGVsdGEpLCB0aGlzLl96b29tKTtcblx0XHR9XG5cblx0XHRpZiAoIXRoaXMuX21vdmVkKSB7XG5cdFx0XHRtYXAuX21vdmVTdGFydCh0cnVlKTtcblx0XHRcdHRoaXMuX21vdmVkID0gdHJ1ZTtcblx0XHR9XG5cblx0XHRMLlV0aWwuY2FuY2VsQW5pbUZyYW1lKHRoaXMuX2FuaW1SZXF1ZXN0KTtcblxuXHRcdHZhciBtb3ZlRm4gPSBMLmJpbmQobWFwLl9tb3ZlLCBtYXAsIHRoaXMuX2NlbnRlciwgdGhpcy5fem9vbSwge3BpbmNoOiB0cnVlLCByb3VuZDogZmFsc2V9KTtcblx0XHR0aGlzLl9hbmltUmVxdWVzdCA9IEwuVXRpbC5yZXF1ZXN0QW5pbUZyYW1lKG1vdmVGbiwgdGhpcywgdHJ1ZSk7XG5cblx0XHRMLkRvbUV2ZW50LnByZXZlbnREZWZhdWx0KGUpO1xuXHR9LFxuXG5cdF9vblRvdWNoRW5kOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKCF0aGlzLl9tb3ZlZCB8fCAhdGhpcy5fem9vbWluZykge1xuXHRcdFx0dGhpcy5fem9vbWluZyA9IGZhbHNlO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdHRoaXMuX3pvb21pbmcgPSBmYWxzZTtcblx0XHRMLlV0aWwuY2FuY2VsQW5pbUZyYW1lKHRoaXMuX2FuaW1SZXF1ZXN0KTtcblxuXHRcdEwuRG9tRXZlbnRcblx0XHQgICAgLm9mZihkb2N1bWVudCwgJ3RvdWNobW92ZScsIHRoaXMuX29uVG91Y2hNb3ZlKVxuXHRcdCAgICAub2ZmKGRvY3VtZW50LCAndG91Y2hlbmQnLCB0aGlzLl9vblRvdWNoRW5kKTtcblxuXHRcdC8vIFBpbmNoIHVwZGF0ZXMgR3JpZExheWVycycgbGV2ZWxzIG9ubHkgd2hlbiB6b29tU25hcCBpcyBvZmYsIHNvIHpvb21TbmFwIGJlY29tZXMgbm9VcGRhdGUuXG5cdFx0aWYgKHRoaXMuX21hcC5vcHRpb25zLnpvb21BbmltYXRpb24pIHtcblx0XHRcdHRoaXMuX21hcC5fYW5pbWF0ZVpvb20odGhpcy5fY2VudGVyLCB0aGlzLl9tYXAuX2xpbWl0Wm9vbSh0aGlzLl96b29tKSwgdHJ1ZSwgdGhpcy5fbWFwLm9wdGlvbnMuem9vbVNuYXApO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aGlzLl9tYXAuX3Jlc2V0Vmlldyh0aGlzLl9jZW50ZXIsIHRoaXMuX21hcC5fbGltaXRab29tKHRoaXMuX3pvb20pKTtcblx0XHR9XG5cdH1cbn0pO1xuXG4vLyBAc2VjdGlvbiBIYW5kbGVyc1xuLy8gQHByb3BlcnR5IHRvdWNoWm9vbTogSGFuZGxlclxuLy8gVG91Y2ggem9vbSBoYW5kbGVyLlxuTC5NYXAuYWRkSW5pdEhvb2soJ2FkZEhhbmRsZXInLCAndG91Y2hab29tJywgTC5NYXAuVG91Y2hab29tKTtcblxuXG5cbi8qXG4gKiBMLk1hcC5UYXAgaXMgdXNlZCB0byBlbmFibGUgbW9iaWxlIGhhY2tzIGxpa2UgcXVpY2sgdGFwcyBhbmQgbG9uZyBob2xkLlxuICovXG5cbi8vIEBuYW1lc3BhY2UgTWFwXG4vLyBAc2VjdGlvbiBJbnRlcmFjdGlvbiBPcHRpb25zXG5MLk1hcC5tZXJnZU9wdGlvbnMoe1xuXHQvLyBAc2VjdGlvbiBUb3VjaCBpbnRlcmFjdGlvbiBvcHRpb25zXG5cdC8vIEBvcHRpb24gdGFwOiBCb29sZWFuID0gdHJ1ZVxuXHQvLyBFbmFibGVzIG1vYmlsZSBoYWNrcyBmb3Igc3VwcG9ydGluZyBpbnN0YW50IHRhcHMgKGZpeGluZyAyMDBtcyBjbGlja1xuXHQvLyBkZWxheSBvbiBpT1MvQW5kcm9pZCkgYW5kIHRvdWNoIGhvbGRzIChmaXJlZCBhcyBgY29udGV4dG1lbnVgIGV2ZW50cykuXG5cdHRhcDogdHJ1ZSxcblxuXHQvLyBAb3B0aW9uIHRhcFRvbGVyYW5jZTogTnVtYmVyID0gMTVcblx0Ly8gVGhlIG1heCBudW1iZXIgb2YgcGl4ZWxzIGEgdXNlciBjYW4gc2hpZnQgaGlzIGZpbmdlciBkdXJpbmcgdG91Y2hcblx0Ly8gZm9yIGl0IHRvIGJlIGNvbnNpZGVyZWQgYSB2YWxpZCB0YXAuXG5cdHRhcFRvbGVyYW5jZTogMTVcbn0pO1xuXG5MLk1hcC5UYXAgPSBMLkhhbmRsZXIuZXh0ZW5kKHtcblx0YWRkSG9va3M6IGZ1bmN0aW9uICgpIHtcblx0XHRMLkRvbUV2ZW50Lm9uKHRoaXMuX21hcC5fY29udGFpbmVyLCAndG91Y2hzdGFydCcsIHRoaXMuX29uRG93biwgdGhpcyk7XG5cdH0sXG5cblx0cmVtb3ZlSG9va3M6IGZ1bmN0aW9uICgpIHtcblx0XHRMLkRvbUV2ZW50Lm9mZih0aGlzLl9tYXAuX2NvbnRhaW5lciwgJ3RvdWNoc3RhcnQnLCB0aGlzLl9vbkRvd24sIHRoaXMpO1xuXHR9LFxuXG5cdF9vbkRvd246IGZ1bmN0aW9uIChlKSB7XG5cdFx0aWYgKCFlLnRvdWNoZXMpIHsgcmV0dXJuOyB9XG5cblx0XHRMLkRvbUV2ZW50LnByZXZlbnREZWZhdWx0KGUpO1xuXG5cdFx0dGhpcy5fZmlyZUNsaWNrID0gdHJ1ZTtcblxuXHRcdC8vIGRvbid0IHNpbXVsYXRlIGNsaWNrIG9yIHRyYWNrIGxvbmdwcmVzcyBpZiBtb3JlIHRoYW4gMSB0b3VjaFxuXHRcdGlmIChlLnRvdWNoZXMubGVuZ3RoID4gMSkge1xuXHRcdFx0dGhpcy5fZmlyZUNsaWNrID0gZmFsc2U7XG5cdFx0XHRjbGVhclRpbWVvdXQodGhpcy5faG9sZFRpbWVvdXQpO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdHZhciBmaXJzdCA9IGUudG91Y2hlc1swXSxcblx0XHQgICAgZWwgPSBmaXJzdC50YXJnZXQ7XG5cblx0XHR0aGlzLl9zdGFydFBvcyA9IHRoaXMuX25ld1BvcyA9IG5ldyBMLlBvaW50KGZpcnN0LmNsaWVudFgsIGZpcnN0LmNsaWVudFkpO1xuXG5cdFx0Ly8gaWYgdG91Y2hpbmcgYSBsaW5rLCBoaWdobGlnaHQgaXRcblx0XHRpZiAoZWwudGFnTmFtZSAmJiBlbC50YWdOYW1lLnRvTG93ZXJDYXNlKCkgPT09ICdhJykge1xuXHRcdFx0TC5Eb21VdGlsLmFkZENsYXNzKGVsLCAnbGVhZmxldC1hY3RpdmUnKTtcblx0XHR9XG5cblx0XHQvLyBzaW11bGF0ZSBsb25nIGhvbGQgYnV0IHNldHRpbmcgYSB0aW1lb3V0XG5cdFx0dGhpcy5faG9sZFRpbWVvdXQgPSBzZXRUaW1lb3V0KEwuYmluZChmdW5jdGlvbiAoKSB7XG5cdFx0XHRpZiAodGhpcy5faXNUYXBWYWxpZCgpKSB7XG5cdFx0XHRcdHRoaXMuX2ZpcmVDbGljayA9IGZhbHNlO1xuXHRcdFx0XHR0aGlzLl9vblVwKCk7XG5cdFx0XHRcdHRoaXMuX3NpbXVsYXRlRXZlbnQoJ2NvbnRleHRtZW51JywgZmlyc3QpO1xuXHRcdFx0fVxuXHRcdH0sIHRoaXMpLCAxMDAwKTtcblxuXHRcdHRoaXMuX3NpbXVsYXRlRXZlbnQoJ21vdXNlZG93bicsIGZpcnN0KTtcblxuXHRcdEwuRG9tRXZlbnQub24oZG9jdW1lbnQsIHtcblx0XHRcdHRvdWNobW92ZTogdGhpcy5fb25Nb3ZlLFxuXHRcdFx0dG91Y2hlbmQ6IHRoaXMuX29uVXBcblx0XHR9LCB0aGlzKTtcblx0fSxcblxuXHRfb25VcDogZnVuY3Rpb24gKGUpIHtcblx0XHRjbGVhclRpbWVvdXQodGhpcy5faG9sZFRpbWVvdXQpO1xuXG5cdFx0TC5Eb21FdmVudC5vZmYoZG9jdW1lbnQsIHtcblx0XHRcdHRvdWNobW92ZTogdGhpcy5fb25Nb3ZlLFxuXHRcdFx0dG91Y2hlbmQ6IHRoaXMuX29uVXBcblx0XHR9LCB0aGlzKTtcblxuXHRcdGlmICh0aGlzLl9maXJlQ2xpY2sgJiYgZSAmJiBlLmNoYW5nZWRUb3VjaGVzKSB7XG5cblx0XHRcdHZhciBmaXJzdCA9IGUuY2hhbmdlZFRvdWNoZXNbMF0sXG5cdFx0XHQgICAgZWwgPSBmaXJzdC50YXJnZXQ7XG5cblx0XHRcdGlmIChlbCAmJiBlbC50YWdOYW1lICYmIGVsLnRhZ05hbWUudG9Mb3dlckNhc2UoKSA9PT0gJ2EnKSB7XG5cdFx0XHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyhlbCwgJ2xlYWZsZXQtYWN0aXZlJyk7XG5cdFx0XHR9XG5cblx0XHRcdHRoaXMuX3NpbXVsYXRlRXZlbnQoJ21vdXNldXAnLCBmaXJzdCk7XG5cblx0XHRcdC8vIHNpbXVsYXRlIGNsaWNrIGlmIHRoZSB0b3VjaCBkaWRuJ3QgbW92ZSB0b28gbXVjaFxuXHRcdFx0aWYgKHRoaXMuX2lzVGFwVmFsaWQoKSkge1xuXHRcdFx0XHR0aGlzLl9zaW11bGF0ZUV2ZW50KCdjbGljaycsIGZpcnN0KTtcblx0XHRcdH1cblx0XHR9XG5cdH0sXG5cblx0X2lzVGFwVmFsaWQ6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5fbmV3UG9zLmRpc3RhbmNlVG8odGhpcy5fc3RhcnRQb3MpIDw9IHRoaXMuX21hcC5vcHRpb25zLnRhcFRvbGVyYW5jZTtcblx0fSxcblxuXHRfb25Nb3ZlOiBmdW5jdGlvbiAoZSkge1xuXHRcdHZhciBmaXJzdCA9IGUudG91Y2hlc1swXTtcblx0XHR0aGlzLl9uZXdQb3MgPSBuZXcgTC5Qb2ludChmaXJzdC5jbGllbnRYLCBmaXJzdC5jbGllbnRZKTtcblx0XHR0aGlzLl9zaW11bGF0ZUV2ZW50KCdtb3VzZW1vdmUnLCBmaXJzdCk7XG5cdH0sXG5cblx0X3NpbXVsYXRlRXZlbnQ6IGZ1bmN0aW9uICh0eXBlLCBlKSB7XG5cdFx0dmFyIHNpbXVsYXRlZEV2ZW50ID0gZG9jdW1lbnQuY3JlYXRlRXZlbnQoJ01vdXNlRXZlbnRzJyk7XG5cblx0XHRzaW11bGF0ZWRFdmVudC5fc2ltdWxhdGVkID0gdHJ1ZTtcblx0XHRlLnRhcmdldC5fc2ltdWxhdGVkQ2xpY2sgPSB0cnVlO1xuXG5cdFx0c2ltdWxhdGVkRXZlbnQuaW5pdE1vdXNlRXZlbnQoXG5cdFx0ICAgICAgICB0eXBlLCB0cnVlLCB0cnVlLCB3aW5kb3csIDEsXG5cdFx0ICAgICAgICBlLnNjcmVlblgsIGUuc2NyZWVuWSxcblx0XHQgICAgICAgIGUuY2xpZW50WCwgZS5jbGllbnRZLFxuXHRcdCAgICAgICAgZmFsc2UsIGZhbHNlLCBmYWxzZSwgZmFsc2UsIDAsIG51bGwpO1xuXG5cdFx0ZS50YXJnZXQuZGlzcGF0Y2hFdmVudChzaW11bGF0ZWRFdmVudCk7XG5cdH1cbn0pO1xuXG4vLyBAc2VjdGlvbiBIYW5kbGVyc1xuLy8gQHByb3BlcnR5IHRhcDogSGFuZGxlclxuLy8gTW9iaWxlIHRvdWNoIGhhY2tzIChxdWljayB0YXAgYW5kIHRvdWNoIGhvbGQpIGhhbmRsZXIuXG5pZiAoTC5Ccm93c2VyLnRvdWNoICYmICFMLkJyb3dzZXIucG9pbnRlcikge1xuXHRMLk1hcC5hZGRJbml0SG9vaygnYWRkSGFuZGxlcicsICd0YXAnLCBMLk1hcC5UYXApO1xufVxuXG5cblxuLypcbiAqIEwuSGFuZGxlci5Cb3hab29tIGlzIHVzZWQgdG8gYWRkIHNoaWZ0LWRyYWcgem9vbSBpbnRlcmFjdGlvbiB0byB0aGUgbWFwXG4gKiAoem9vbSB0byBhIHNlbGVjdGVkIGJvdW5kaW5nIGJveCksIGVuYWJsZWQgYnkgZGVmYXVsdC5cbiAqL1xuXG4vLyBAbmFtZXNwYWNlIE1hcFxuLy8gQHNlY3Rpb24gSW50ZXJhY3Rpb24gT3B0aW9uc1xuTC5NYXAubWVyZ2VPcHRpb25zKHtcblx0Ly8gQG9wdGlvbiBib3hab29tOiBCb29sZWFuID0gdHJ1ZVxuXHQvLyBXaGV0aGVyIHRoZSBtYXAgY2FuIGJlIHpvb21lZCB0byBhIHJlY3Rhbmd1bGFyIGFyZWEgc3BlY2lmaWVkIGJ5XG5cdC8vIGRyYWdnaW5nIHRoZSBtb3VzZSB3aGlsZSBwcmVzc2luZyB0aGUgc2hpZnQga2V5LlxuXHRib3hab29tOiB0cnVlXG59KTtcblxuTC5NYXAuQm94Wm9vbSA9IEwuSGFuZGxlci5leHRlbmQoe1xuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAobWFwKSB7XG5cdFx0dGhpcy5fbWFwID0gbWFwO1xuXHRcdHRoaXMuX2NvbnRhaW5lciA9IG1hcC5fY29udGFpbmVyO1xuXHRcdHRoaXMuX3BhbmUgPSBtYXAuX3BhbmVzLm92ZXJsYXlQYW5lO1xuXHR9LFxuXG5cdGFkZEhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0TC5Eb21FdmVudC5vbih0aGlzLl9jb250YWluZXIsICdtb3VzZWRvd24nLCB0aGlzLl9vbk1vdXNlRG93biwgdGhpcyk7XG5cdH0sXG5cblx0cmVtb3ZlSG9va3M6IGZ1bmN0aW9uICgpIHtcblx0XHRMLkRvbUV2ZW50Lm9mZih0aGlzLl9jb250YWluZXIsICdtb3VzZWRvd24nLCB0aGlzLl9vbk1vdXNlRG93biwgdGhpcyk7XG5cdH0sXG5cblx0bW92ZWQ6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5fbW92ZWQ7XG5cdH0sXG5cblx0X3Jlc2V0U3RhdGU6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9tb3ZlZCA9IGZhbHNlO1xuXHR9LFxuXG5cdF9vbk1vdXNlRG93bjogZnVuY3Rpb24gKGUpIHtcblx0XHRpZiAoIWUuc2hpZnRLZXkgfHwgKChlLndoaWNoICE9PSAxKSAmJiAoZS5idXR0b24gIT09IDEpKSkgeyByZXR1cm4gZmFsc2U7IH1cblxuXHRcdHRoaXMuX3Jlc2V0U3RhdGUoKTtcblxuXHRcdEwuRG9tVXRpbC5kaXNhYmxlVGV4dFNlbGVjdGlvbigpO1xuXHRcdEwuRG9tVXRpbC5kaXNhYmxlSW1hZ2VEcmFnKCk7XG5cblx0XHR0aGlzLl9zdGFydFBvaW50ID0gdGhpcy5fbWFwLm1vdXNlRXZlbnRUb0NvbnRhaW5lclBvaW50KGUpO1xuXG5cdFx0TC5Eb21FdmVudC5vbihkb2N1bWVudCwge1xuXHRcdFx0Y29udGV4dG1lbnU6IEwuRG9tRXZlbnQuc3RvcCxcblx0XHRcdG1vdXNlbW92ZTogdGhpcy5fb25Nb3VzZU1vdmUsXG5cdFx0XHRtb3VzZXVwOiB0aGlzLl9vbk1vdXNlVXAsXG5cdFx0XHRrZXlkb3duOiB0aGlzLl9vbktleURvd25cblx0XHR9LCB0aGlzKTtcblx0fSxcblxuXHRfb25Nb3VzZU1vdmU6IGZ1bmN0aW9uIChlKSB7XG5cdFx0aWYgKCF0aGlzLl9tb3ZlZCkge1xuXHRcdFx0dGhpcy5fbW92ZWQgPSB0cnVlO1xuXG5cdFx0XHR0aGlzLl9ib3ggPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCAnbGVhZmxldC16b29tLWJveCcsIHRoaXMuX2NvbnRhaW5lcik7XG5cdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3ModGhpcy5fY29udGFpbmVyLCAnbGVhZmxldC1jcm9zc2hhaXInKTtcblxuXHRcdFx0dGhpcy5fbWFwLmZpcmUoJ2JveHpvb21zdGFydCcpO1xuXHRcdH1cblxuXHRcdHRoaXMuX3BvaW50ID0gdGhpcy5fbWFwLm1vdXNlRXZlbnRUb0NvbnRhaW5lclBvaW50KGUpO1xuXG5cdFx0dmFyIGJvdW5kcyA9IG5ldyBMLkJvdW5kcyh0aGlzLl9wb2ludCwgdGhpcy5fc3RhcnRQb2ludCksXG5cdFx0ICAgIHNpemUgPSBib3VuZHMuZ2V0U2l6ZSgpO1xuXG5cdFx0TC5Eb21VdGlsLnNldFBvc2l0aW9uKHRoaXMuX2JveCwgYm91bmRzLm1pbik7XG5cblx0XHR0aGlzLl9ib3guc3R5bGUud2lkdGggID0gc2l6ZS54ICsgJ3B4Jztcblx0XHR0aGlzLl9ib3guc3R5bGUuaGVpZ2h0ID0gc2l6ZS55ICsgJ3B4Jztcblx0fSxcblxuXHRfZmluaXNoOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX21vdmVkKSB7XG5cdFx0XHRMLkRvbVV0aWwucmVtb3ZlKHRoaXMuX2JveCk7XG5cdFx0XHRMLkRvbVV0aWwucmVtb3ZlQ2xhc3ModGhpcy5fY29udGFpbmVyLCAnbGVhZmxldC1jcm9zc2hhaXInKTtcblx0XHR9XG5cblx0XHRMLkRvbVV0aWwuZW5hYmxlVGV4dFNlbGVjdGlvbigpO1xuXHRcdEwuRG9tVXRpbC5lbmFibGVJbWFnZURyYWcoKTtcblxuXHRcdEwuRG9tRXZlbnQub2ZmKGRvY3VtZW50LCB7XG5cdFx0XHRjb250ZXh0bWVudTogTC5Eb21FdmVudC5zdG9wLFxuXHRcdFx0bW91c2Vtb3ZlOiB0aGlzLl9vbk1vdXNlTW92ZSxcblx0XHRcdG1vdXNldXA6IHRoaXMuX29uTW91c2VVcCxcblx0XHRcdGtleWRvd246IHRoaXMuX29uS2V5RG93blxuXHRcdH0sIHRoaXMpO1xuXHR9LFxuXG5cdF9vbk1vdXNlVXA6IGZ1bmN0aW9uIChlKSB7XG5cdFx0aWYgKChlLndoaWNoICE9PSAxKSAmJiAoZS5idXR0b24gIT09IDEpKSB7IHJldHVybjsgfVxuXG5cdFx0dGhpcy5fZmluaXNoKCk7XG5cblx0XHRpZiAoIXRoaXMuX21vdmVkKSB7IHJldHVybjsgfVxuXHRcdC8vIFBvc3Rwb25lIHRvIG5leHQgSlMgdGljayBzbyBpbnRlcm5hbCBjbGljayBldmVudCBoYW5kbGluZ1xuXHRcdC8vIHN0aWxsIHNlZSBpdCBhcyBcIm1vdmVkXCIuXG5cdFx0c2V0VGltZW91dChMLmJpbmQodGhpcy5fcmVzZXRTdGF0ZSwgdGhpcyksIDApO1xuXG5cdFx0dmFyIGJvdW5kcyA9IG5ldyBMLkxhdExuZ0JvdW5kcyhcblx0XHQgICAgICAgIHRoaXMuX21hcC5jb250YWluZXJQb2ludFRvTGF0TG5nKHRoaXMuX3N0YXJ0UG9pbnQpLFxuXHRcdCAgICAgICAgdGhpcy5fbWFwLmNvbnRhaW5lclBvaW50VG9MYXRMbmcodGhpcy5fcG9pbnQpKTtcblxuXHRcdHRoaXMuX21hcFxuXHRcdFx0LmZpdEJvdW5kcyhib3VuZHMpXG5cdFx0XHQuZmlyZSgnYm94em9vbWVuZCcsIHtib3hab29tQm91bmRzOiBib3VuZHN9KTtcblx0fSxcblxuXHRfb25LZXlEb3duOiBmdW5jdGlvbiAoZSkge1xuXHRcdGlmIChlLmtleUNvZGUgPT09IDI3KSB7XG5cdFx0XHR0aGlzLl9maW5pc2goKTtcblx0XHR9XG5cdH1cbn0pO1xuXG4vLyBAc2VjdGlvbiBIYW5kbGVyc1xuLy8gQHByb3BlcnR5IGJveFpvb206IEhhbmRsZXJcbi8vIEJveCAoc2hpZnQtZHJhZyB3aXRoIG1vdXNlKSB6b29tIGhhbmRsZXIuXG5MLk1hcC5hZGRJbml0SG9vaygnYWRkSGFuZGxlcicsICdib3hab29tJywgTC5NYXAuQm94Wm9vbSk7XG5cblxuXG4vKlxuICogTC5NYXAuS2V5Ym9hcmQgaXMgaGFuZGxpbmcga2V5Ym9hcmQgaW50ZXJhY3Rpb24gd2l0aCB0aGUgbWFwLCBlbmFibGVkIGJ5IGRlZmF1bHQuXG4gKi9cblxuLy8gQG5hbWVzcGFjZSBNYXBcbi8vIEBzZWN0aW9uIEtleWJvYXJkIE5hdmlnYXRpb24gT3B0aW9uc1xuTC5NYXAubWVyZ2VPcHRpb25zKHtcblx0Ly8gQG9wdGlvbiBrZXlib2FyZDogQm9vbGVhbiA9IHRydWVcblx0Ly8gTWFrZXMgdGhlIG1hcCBmb2N1c2FibGUgYW5kIGFsbG93cyB1c2VycyB0byBuYXZpZ2F0ZSB0aGUgbWFwIHdpdGgga2V5Ym9hcmRcblx0Ly8gYXJyb3dzIGFuZCBgK2AvYC1gIGtleXMuXG5cdGtleWJvYXJkOiB0cnVlLFxuXG5cdC8vIEBvcHRpb24ga2V5Ym9hcmRQYW5EZWx0YTogTnVtYmVyID0gODBcblx0Ly8gQW1vdW50IG9mIHBpeGVscyB0byBwYW4gd2hlbiBwcmVzc2luZyBhbiBhcnJvdyBrZXkuXG5cdGtleWJvYXJkUGFuRGVsdGE6IDgwXG59KTtcblxuTC5NYXAuS2V5Ym9hcmQgPSBMLkhhbmRsZXIuZXh0ZW5kKHtcblxuXHRrZXlDb2Rlczoge1xuXHRcdGxlZnQ6ICAgIFszN10sXG5cdFx0cmlnaHQ6ICAgWzM5XSxcblx0XHRkb3duOiAgICBbNDBdLFxuXHRcdHVwOiAgICAgIFszOF0sXG5cdFx0em9vbUluOiAgWzE4NywgMTA3LCA2MSwgMTcxXSxcblx0XHR6b29tT3V0OiBbMTg5LCAxMDksIDU0LCAxNzNdXG5cdH0sXG5cblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKG1hcCkge1xuXHRcdHRoaXMuX21hcCA9IG1hcDtcblxuXHRcdHRoaXMuX3NldFBhbkRlbHRhKG1hcC5vcHRpb25zLmtleWJvYXJkUGFuRGVsdGEpO1xuXHRcdHRoaXMuX3NldFpvb21EZWx0YShtYXAub3B0aW9ucy56b29tRGVsdGEpO1xuXHR9LFxuXG5cdGFkZEhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIGNvbnRhaW5lciA9IHRoaXMuX21hcC5fY29udGFpbmVyO1xuXG5cdFx0Ly8gbWFrZSB0aGUgY29udGFpbmVyIGZvY3VzYWJsZSBieSB0YWJiaW5nXG5cdFx0aWYgKGNvbnRhaW5lci50YWJJbmRleCA8PSAwKSB7XG5cdFx0XHRjb250YWluZXIudGFiSW5kZXggPSAnMCc7XG5cdFx0fVxuXG5cdFx0TC5Eb21FdmVudC5vbihjb250YWluZXIsIHtcblx0XHRcdGZvY3VzOiB0aGlzLl9vbkZvY3VzLFxuXHRcdFx0Ymx1cjogdGhpcy5fb25CbHVyLFxuXHRcdFx0bW91c2Vkb3duOiB0aGlzLl9vbk1vdXNlRG93blxuXHRcdH0sIHRoaXMpO1xuXG5cdFx0dGhpcy5fbWFwLm9uKHtcblx0XHRcdGZvY3VzOiB0aGlzLl9hZGRIb29rcyxcblx0XHRcdGJsdXI6IHRoaXMuX3JlbW92ZUhvb2tzXG5cdFx0fSwgdGhpcyk7XG5cdH0sXG5cblx0cmVtb3ZlSG9va3M6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9yZW1vdmVIb29rcygpO1xuXG5cdFx0TC5Eb21FdmVudC5vZmYodGhpcy5fbWFwLl9jb250YWluZXIsIHtcblx0XHRcdGZvY3VzOiB0aGlzLl9vbkZvY3VzLFxuXHRcdFx0Ymx1cjogdGhpcy5fb25CbHVyLFxuXHRcdFx0bW91c2Vkb3duOiB0aGlzLl9vbk1vdXNlRG93blxuXHRcdH0sIHRoaXMpO1xuXG5cdFx0dGhpcy5fbWFwLm9mZih7XG5cdFx0XHRmb2N1czogdGhpcy5fYWRkSG9va3MsXG5cdFx0XHRibHVyOiB0aGlzLl9yZW1vdmVIb29rc1xuXHRcdH0sIHRoaXMpO1xuXHR9LFxuXG5cdF9vbk1vdXNlRG93bjogZnVuY3Rpb24gKCkge1xuXHRcdGlmICh0aGlzLl9mb2N1c2VkKSB7IHJldHVybjsgfVxuXG5cdFx0dmFyIGJvZHkgPSBkb2N1bWVudC5ib2R5LFxuXHRcdCAgICBkb2NFbCA9IGRvY3VtZW50LmRvY3VtZW50RWxlbWVudCxcblx0XHQgICAgdG9wID0gYm9keS5zY3JvbGxUb3AgfHwgZG9jRWwuc2Nyb2xsVG9wLFxuXHRcdCAgICBsZWZ0ID0gYm9keS5zY3JvbGxMZWZ0IHx8IGRvY0VsLnNjcm9sbExlZnQ7XG5cblx0XHR0aGlzLl9tYXAuX2NvbnRhaW5lci5mb2N1cygpO1xuXG5cdFx0d2luZG93LnNjcm9sbFRvKGxlZnQsIHRvcCk7XG5cdH0sXG5cblx0X29uRm9jdXM6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9mb2N1c2VkID0gdHJ1ZTtcblx0XHR0aGlzLl9tYXAuZmlyZSgnZm9jdXMnKTtcblx0fSxcblxuXHRfb25CbHVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fZm9jdXNlZCA9IGZhbHNlO1xuXHRcdHRoaXMuX21hcC5maXJlKCdibHVyJyk7XG5cdH0sXG5cblx0X3NldFBhbkRlbHRhOiBmdW5jdGlvbiAocGFuRGVsdGEpIHtcblx0XHR2YXIga2V5cyA9IHRoaXMuX3BhbktleXMgPSB7fSxcblx0XHQgICAgY29kZXMgPSB0aGlzLmtleUNvZGVzLFxuXHRcdCAgICBpLCBsZW47XG5cblx0XHRmb3IgKGkgPSAwLCBsZW4gPSBjb2Rlcy5sZWZ0Lmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRrZXlzW2NvZGVzLmxlZnRbaV1dID0gWy0xICogcGFuRGVsdGEsIDBdO1xuXHRcdH1cblx0XHRmb3IgKGkgPSAwLCBsZW4gPSBjb2Rlcy5yaWdodC5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xuXHRcdFx0a2V5c1tjb2Rlcy5yaWdodFtpXV0gPSBbcGFuRGVsdGEsIDBdO1xuXHRcdH1cblx0XHRmb3IgKGkgPSAwLCBsZW4gPSBjb2Rlcy5kb3duLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRrZXlzW2NvZGVzLmRvd25baV1dID0gWzAsIHBhbkRlbHRhXTtcblx0XHR9XG5cdFx0Zm9yIChpID0gMCwgbGVuID0gY29kZXMudXAubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdGtleXNbY29kZXMudXBbaV1dID0gWzAsIC0xICogcGFuRGVsdGFdO1xuXHRcdH1cblx0fSxcblxuXHRfc2V0Wm9vbURlbHRhOiBmdW5jdGlvbiAoem9vbURlbHRhKSB7XG5cdFx0dmFyIGtleXMgPSB0aGlzLl96b29tS2V5cyA9IHt9LFxuXHRcdCAgICBjb2RlcyA9IHRoaXMua2V5Q29kZXMsXG5cdFx0ICAgIGksIGxlbjtcblxuXHRcdGZvciAoaSA9IDAsIGxlbiA9IGNvZGVzLnpvb21Jbi5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xuXHRcdFx0a2V5c1tjb2Rlcy56b29tSW5baV1dID0gem9vbURlbHRhO1xuXHRcdH1cblx0XHRmb3IgKGkgPSAwLCBsZW4gPSBjb2Rlcy56b29tT3V0Lmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRrZXlzW2NvZGVzLnpvb21PdXRbaV1dID0gLXpvb21EZWx0YTtcblx0XHR9XG5cdH0sXG5cblx0X2FkZEhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0TC5Eb21FdmVudC5vbihkb2N1bWVudCwgJ2tleWRvd24nLCB0aGlzLl9vbktleURvd24sIHRoaXMpO1xuXHR9LFxuXG5cdF9yZW1vdmVIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdEwuRG9tRXZlbnQub2ZmKGRvY3VtZW50LCAna2V5ZG93bicsIHRoaXMuX29uS2V5RG93biwgdGhpcyk7XG5cdH0sXG5cblx0X29uS2V5RG93bjogZnVuY3Rpb24gKGUpIHtcblx0XHRpZiAoZS5hbHRLZXkgfHwgZS5jdHJsS2V5IHx8IGUubWV0YUtleSkgeyByZXR1cm47IH1cblxuXHRcdHZhciBrZXkgPSBlLmtleUNvZGUsXG5cdFx0ICAgIG1hcCA9IHRoaXMuX21hcCxcblx0XHQgICAgb2Zmc2V0O1xuXG5cdFx0aWYgKGtleSBpbiB0aGlzLl9wYW5LZXlzKSB7XG5cblx0XHRcdGlmIChtYXAuX3BhbkFuaW0gJiYgbWFwLl9wYW5BbmltLl9pblByb2dyZXNzKSB7IHJldHVybjsgfVxuXG5cdFx0XHRvZmZzZXQgPSB0aGlzLl9wYW5LZXlzW2tleV07XG5cdFx0XHRpZiAoZS5zaGlmdEtleSkge1xuXHRcdFx0XHRvZmZzZXQgPSBMLnBvaW50KG9mZnNldCkubXVsdGlwbHlCeSgzKTtcblx0XHRcdH1cblxuXHRcdFx0bWFwLnBhbkJ5KG9mZnNldCk7XG5cblx0XHRcdGlmIChtYXAub3B0aW9ucy5tYXhCb3VuZHMpIHtcblx0XHRcdFx0bWFwLnBhbkluc2lkZUJvdW5kcyhtYXAub3B0aW9ucy5tYXhCb3VuZHMpO1xuXHRcdFx0fVxuXG5cdFx0fSBlbHNlIGlmIChrZXkgaW4gdGhpcy5fem9vbUtleXMpIHtcblx0XHRcdG1hcC5zZXRab29tKG1hcC5nZXRab29tKCkgKyAoZS5zaGlmdEtleSA/IDMgOiAxKSAqIHRoaXMuX3pvb21LZXlzW2tleV0pO1xuXG5cdFx0fSBlbHNlIGlmIChrZXkgPT09IDI3KSB7XG5cdFx0XHRtYXAuY2xvc2VQb3B1cCgpO1xuXG5cdFx0fSBlbHNlIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRMLkRvbUV2ZW50LnN0b3AoZSk7XG5cdH1cbn0pO1xuXG4vLyBAc2VjdGlvbiBIYW5kbGVyc1xuLy8gQHNlY3Rpb24gSGFuZGxlcnNcbi8vIEBwcm9wZXJ0eSBrZXlib2FyZDogSGFuZGxlclxuLy8gS2V5Ym9hcmQgbmF2aWdhdGlvbiBoYW5kbGVyLlxuTC5NYXAuYWRkSW5pdEhvb2soJ2FkZEhhbmRsZXInLCAna2V5Ym9hcmQnLCBMLk1hcC5LZXlib2FyZCk7XG5cblxuXG4vKlxuICogTC5IYW5kbGVyLk1hcmtlckRyYWcgaXMgdXNlZCBpbnRlcm5hbGx5IGJ5IEwuTWFya2VyIHRvIG1ha2UgdGhlIG1hcmtlcnMgZHJhZ2dhYmxlLlxuICovXG5cblxuLyogQG5hbWVzcGFjZSBNYXJrZXJcbiAqIEBzZWN0aW9uIEludGVyYWN0aW9uIGhhbmRsZXJzXG4gKlxuICogSW50ZXJhY3Rpb24gaGFuZGxlcnMgYXJlIHByb3BlcnRpZXMgb2YgYSBtYXJrZXIgaW5zdGFuY2UgdGhhdCBhbGxvdyB5b3UgdG8gY29udHJvbCBpbnRlcmFjdGlvbiBiZWhhdmlvciBpbiBydW50aW1lLCBlbmFibGluZyBvciBkaXNhYmxpbmcgY2VydGFpbiBmZWF0dXJlcyBzdWNoIGFzIGRyYWdnaW5nIChzZWUgYEhhbmRsZXJgIG1ldGhvZHMpLiBFeGFtcGxlOlxuICpcbiAqIGBgYGpzXG4gKiBtYXJrZXIuZHJhZ2dpbmcuZGlzYWJsZSgpO1xuICogYGBgXG4gKlxuICogQHByb3BlcnR5IGRyYWdnaW5nOiBIYW5kbGVyXG4gKiBNYXJrZXIgZHJhZ2dpbmcgaGFuZGxlciAoYnkgYm90aCBtb3VzZSBhbmQgdG91Y2gpLlxuICovXG5cbkwuSGFuZGxlci5NYXJrZXJEcmFnID0gTC5IYW5kbGVyLmV4dGVuZCh7XG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChtYXJrZXIpIHtcblx0XHR0aGlzLl9tYXJrZXIgPSBtYXJrZXI7XG5cdH0sXG5cblx0YWRkSG9va3M6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgaWNvbiA9IHRoaXMuX21hcmtlci5faWNvbjtcblxuXHRcdGlmICghdGhpcy5fZHJhZ2dhYmxlKSB7XG5cdFx0XHR0aGlzLl9kcmFnZ2FibGUgPSBuZXcgTC5EcmFnZ2FibGUoaWNvbiwgaWNvbiwgdHJ1ZSk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fZHJhZ2dhYmxlLm9uKHtcblx0XHRcdGRyYWdzdGFydDogdGhpcy5fb25EcmFnU3RhcnQsXG5cdFx0XHRkcmFnOiB0aGlzLl9vbkRyYWcsXG5cdFx0XHRkcmFnZW5kOiB0aGlzLl9vbkRyYWdFbmRcblx0XHR9LCB0aGlzKS5lbmFibGUoKTtcblxuXHRcdEwuRG9tVXRpbC5hZGRDbGFzcyhpY29uLCAnbGVhZmxldC1tYXJrZXItZHJhZ2dhYmxlJyk7XG5cdH0sXG5cblx0cmVtb3ZlSG9va3M6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9kcmFnZ2FibGUub2ZmKHtcblx0XHRcdGRyYWdzdGFydDogdGhpcy5fb25EcmFnU3RhcnQsXG5cdFx0XHRkcmFnOiB0aGlzLl9vbkRyYWcsXG5cdFx0XHRkcmFnZW5kOiB0aGlzLl9vbkRyYWdFbmRcblx0XHR9LCB0aGlzKS5kaXNhYmxlKCk7XG5cblx0XHRpZiAodGhpcy5fbWFya2VyLl9pY29uKSB7XG5cdFx0XHRMLkRvbVV0aWwucmVtb3ZlQ2xhc3ModGhpcy5fbWFya2VyLl9pY29uLCAnbGVhZmxldC1tYXJrZXItZHJhZ2dhYmxlJyk7XG5cdFx0fVxuXHR9LFxuXG5cdG1vdmVkOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX2RyYWdnYWJsZSAmJiB0aGlzLl9kcmFnZ2FibGUuX21vdmVkO1xuXHR9LFxuXG5cdF9vbkRyYWdTdGFydDogZnVuY3Rpb24gKCkge1xuXHRcdC8vIEBzZWN0aW9uIERyYWdnaW5nIGV2ZW50c1xuXHRcdC8vIEBldmVudCBkcmFnc3RhcnQ6IEV2ZW50XG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgdXNlciBzdGFydHMgZHJhZ2dpbmcgdGhlIG1hcmtlci5cblxuXHRcdC8vIEBldmVudCBtb3Zlc3RhcnQ6IEV2ZW50XG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgbWFya2VyIHN0YXJ0cyBtb3ZpbmcgKGJlY2F1c2Ugb2YgZHJhZ2dpbmcpLlxuXG5cdFx0dGhpcy5fb2xkTGF0TG5nID0gdGhpcy5fbWFya2VyLmdldExhdExuZygpO1xuXHRcdHRoaXMuX21hcmtlclxuXHRcdCAgICAuY2xvc2VQb3B1cCgpXG5cdFx0ICAgIC5maXJlKCdtb3Zlc3RhcnQnKVxuXHRcdCAgICAuZmlyZSgnZHJhZ3N0YXJ0Jyk7XG5cdH0sXG5cblx0X29uRHJhZzogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgbWFya2VyID0gdGhpcy5fbWFya2VyLFxuXHRcdCAgICBzaGFkb3cgPSBtYXJrZXIuX3NoYWRvdyxcblx0XHQgICAgaWNvblBvcyA9IEwuRG9tVXRpbC5nZXRQb3NpdGlvbihtYXJrZXIuX2ljb24pLFxuXHRcdCAgICBsYXRsbmcgPSBtYXJrZXIuX21hcC5sYXllclBvaW50VG9MYXRMbmcoaWNvblBvcyk7XG5cblx0XHQvLyB1cGRhdGUgc2hhZG93IHBvc2l0aW9uXG5cdFx0aWYgKHNoYWRvdykge1xuXHRcdFx0TC5Eb21VdGlsLnNldFBvc2l0aW9uKHNoYWRvdywgaWNvblBvcyk7XG5cdFx0fVxuXG5cdFx0bWFya2VyLl9sYXRsbmcgPSBsYXRsbmc7XG5cdFx0ZS5sYXRsbmcgPSBsYXRsbmc7XG5cdFx0ZS5vbGRMYXRMbmcgPSB0aGlzLl9vbGRMYXRMbmc7XG5cblx0XHQvLyBAZXZlbnQgZHJhZzogRXZlbnRcblx0XHQvLyBGaXJlZCByZXBlYXRlZGx5IHdoaWxlIHRoZSB1c2VyIGRyYWdzIHRoZSBtYXJrZXIuXG5cdFx0bWFya2VyXG5cdFx0ICAgIC5maXJlKCdtb3ZlJywgZSlcblx0XHQgICAgLmZpcmUoJ2RyYWcnLCBlKTtcblx0fSxcblxuXHRfb25EcmFnRW5kOiBmdW5jdGlvbiAoZSkge1xuXHRcdC8vIEBldmVudCBkcmFnZW5kOiBEcmFnRW5kRXZlbnRcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSB1c2VyIHN0b3BzIGRyYWdnaW5nIHRoZSBtYXJrZXIuXG5cblx0XHQvLyBAZXZlbnQgbW92ZWVuZDogRXZlbnRcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBtYXJrZXIgc3RvcHMgbW92aW5nIChiZWNhdXNlIG9mIGRyYWdnaW5nKS5cblx0XHRkZWxldGUgdGhpcy5fb2xkTGF0TG5nO1xuXHRcdHRoaXMuX21hcmtlclxuXHRcdCAgICAuZmlyZSgnbW92ZWVuZCcpXG5cdFx0ICAgIC5maXJlKCdkcmFnZW5kJywgZSk7XG5cdH1cbn0pO1xuXG5cblxuLypcclxuICogQGNsYXNzIENvbnRyb2xcclxuICogQGFrYSBMLkNvbnRyb2xcclxuICogQGluaGVyaXRzIENsYXNzXHJcbiAqXHJcbiAqIEwuQ29udHJvbCBpcyBhIGJhc2UgY2xhc3MgZm9yIGltcGxlbWVudGluZyBtYXAgY29udHJvbHMuIEhhbmRsZXMgcG9zaXRpb25pbmcuXHJcbiAqIEFsbCBvdGhlciBjb250cm9scyBleHRlbmQgZnJvbSB0aGlzIGNsYXNzLlxyXG4gKi9cclxuXHJcbkwuQ29udHJvbCA9IEwuQ2xhc3MuZXh0ZW5kKHtcclxuXHQvLyBAc2VjdGlvblxyXG5cdC8vIEBha2EgQ29udHJvbCBvcHRpb25zXHJcblx0b3B0aW9uczoge1xyXG5cdFx0Ly8gQG9wdGlvbiBwb3NpdGlvbjogU3RyaW5nID0gJ3RvcHJpZ2h0J1xyXG5cdFx0Ly8gVGhlIHBvc2l0aW9uIG9mIHRoZSBjb250cm9sIChvbmUgb2YgdGhlIG1hcCBjb3JuZXJzKS4gUG9zc2libGUgdmFsdWVzIGFyZSBgJ3RvcGxlZnQnYCxcclxuXHRcdC8vIGAndG9wcmlnaHQnYCwgYCdib3R0b21sZWZ0J2Agb3IgYCdib3R0b21yaWdodCdgXHJcblx0XHRwb3NpdGlvbjogJ3RvcHJpZ2h0J1xyXG5cdH0sXHJcblxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChvcHRpb25zKSB7XHJcblx0XHRMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XHJcblx0fSxcclxuXHJcblx0LyogQHNlY3Rpb25cclxuXHQgKiBDbGFzc2VzIGV4dGVuZGluZyBMLkNvbnRyb2wgd2lsbCBpbmhlcml0IHRoZSBmb2xsb3dpbmcgbWV0aG9kczpcclxuXHQgKlxyXG5cdCAqIEBtZXRob2QgZ2V0UG9zaXRpb246IHN0cmluZ1xyXG5cdCAqIFJldHVybnMgdGhlIHBvc2l0aW9uIG9mIHRoZSBjb250cm9sLlxyXG5cdCAqL1xyXG5cdGdldFBvc2l0aW9uOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5vcHRpb25zLnBvc2l0aW9uO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0UG9zaXRpb24ocG9zaXRpb246IHN0cmluZyk6IHRoaXNcclxuXHQvLyBTZXRzIHRoZSBwb3NpdGlvbiBvZiB0aGUgY29udHJvbC5cclxuXHRzZXRQb3NpdGlvbjogZnVuY3Rpb24gKHBvc2l0aW9uKSB7XHJcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwO1xyXG5cclxuXHRcdGlmIChtYXApIHtcclxuXHRcdFx0bWFwLnJlbW92ZUNvbnRyb2wodGhpcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5vcHRpb25zLnBvc2l0aW9uID0gcG9zaXRpb247XHJcblxyXG5cdFx0aWYgKG1hcCkge1xyXG5cdFx0XHRtYXAuYWRkQ29udHJvbCh0aGlzKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldENvbnRhaW5lcjogSFRNTEVsZW1lbnRcclxuXHQvLyBSZXR1cm5zIHRoZSBIVE1MRWxlbWVudCB0aGF0IGNvbnRhaW5zIHRoZSBjb250cm9sLlxyXG5cdGdldENvbnRhaW5lcjogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX2NvbnRhaW5lcjtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGFkZFRvKG1hcDogTWFwKTogdGhpc1xyXG5cdC8vIEFkZHMgdGhlIGNvbnRyb2wgdG8gdGhlIGdpdmVuIG1hcC5cclxuXHRhZGRUbzogZnVuY3Rpb24gKG1hcCkge1xyXG5cdFx0dGhpcy5yZW1vdmUoKTtcclxuXHRcdHRoaXMuX21hcCA9IG1hcDtcclxuXHJcblx0XHR2YXIgY29udGFpbmVyID0gdGhpcy5fY29udGFpbmVyID0gdGhpcy5vbkFkZChtYXApLFxyXG5cdFx0ICAgIHBvcyA9IHRoaXMuZ2V0UG9zaXRpb24oKSxcclxuXHRcdCAgICBjb3JuZXIgPSBtYXAuX2NvbnRyb2xDb3JuZXJzW3Bvc107XHJcblxyXG5cdFx0TC5Eb21VdGlsLmFkZENsYXNzKGNvbnRhaW5lciwgJ2xlYWZsZXQtY29udHJvbCcpO1xyXG5cclxuXHRcdGlmIChwb3MuaW5kZXhPZignYm90dG9tJykgIT09IC0xKSB7XHJcblx0XHRcdGNvcm5lci5pbnNlcnRCZWZvcmUoY29udGFpbmVyLCBjb3JuZXIuZmlyc3RDaGlsZCk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRjb3JuZXIuYXBwZW5kQ2hpbGQoY29udGFpbmVyKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHJlbW92ZTogdGhpc1xyXG5cdC8vIFJlbW92ZXMgdGhlIGNvbnRyb2wgZnJvbSB0aGUgbWFwIGl0IGlzIGN1cnJlbnRseSBhY3RpdmUgb24uXHJcblx0cmVtb3ZlOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAoIXRoaXMuX21hcCkge1xyXG5cdFx0XHRyZXR1cm4gdGhpcztcclxuXHRcdH1cclxuXHJcblx0XHRMLkRvbVV0aWwucmVtb3ZlKHRoaXMuX2NvbnRhaW5lcik7XHJcblxyXG5cdFx0aWYgKHRoaXMub25SZW1vdmUpIHtcclxuXHRcdFx0dGhpcy5vblJlbW92ZSh0aGlzLl9tYXApO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX21hcCA9IG51bGw7XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0X3JlZm9jdXNPbk1hcDogZnVuY3Rpb24gKGUpIHtcclxuXHRcdC8vIGlmIG1hcCBleGlzdHMgYW5kIGV2ZW50IGlzIG5vdCBhIGtleWJvYXJkIGV2ZW50XHJcblx0XHRpZiAodGhpcy5fbWFwICYmIGUgJiYgZS5zY3JlZW5YID4gMCAmJiBlLnNjcmVlblkgPiAwKSB7XHJcblx0XHRcdHRoaXMuX21hcC5nZXRDb250YWluZXIoKS5mb2N1cygpO1xyXG5cdFx0fVxyXG5cdH1cclxufSk7XHJcblxyXG5MLmNvbnRyb2wgPSBmdW5jdGlvbiAob3B0aW9ucykge1xyXG5cdHJldHVybiBuZXcgTC5Db250cm9sKG9wdGlvbnMpO1xyXG59O1xyXG5cclxuLyogQHNlY3Rpb24gRXh0ZW5zaW9uIG1ldGhvZHNcclxuICogQHVuaW5oZXJpdGFibGVcclxuICpcclxuICogRXZlcnkgY29udHJvbCBzaG91bGQgZXh0ZW5kIGZyb20gYEwuQ29udHJvbGAgYW5kIChyZS0paW1wbGVtZW50IHRoZSBmb2xsb3dpbmcgbWV0aG9kcy5cclxuICpcclxuICogQG1ldGhvZCBvbkFkZChtYXA6IE1hcCk6IEhUTUxFbGVtZW50XHJcbiAqIFNob3VsZCByZXR1cm4gdGhlIGNvbnRhaW5lciBET00gZWxlbWVudCBmb3IgdGhlIGNvbnRyb2wgYW5kIGFkZCBsaXN0ZW5lcnMgb24gcmVsZXZhbnQgbWFwIGV2ZW50cy4gQ2FsbGVkIG9uIFtgY29udHJvbC5hZGRUbyhtYXApYF0oI2NvbnRyb2wtYWRkVG8pLlxyXG4gKlxyXG4gKiBAbWV0aG9kIG9uUmVtb3ZlKG1hcDogTWFwKVxyXG4gKiBPcHRpb25hbCBtZXRob2QuIFNob3VsZCBjb250YWluIGFsbCBjbGVhbiB1cCBjb2RlIHRoYXQgcmVtb3ZlcyB0aGUgbGlzdGVuZXJzIHByZXZpb3VzbHkgYWRkZWQgaW4gW2BvbkFkZGBdKCNjb250cm9sLW9uYWRkKS4gQ2FsbGVkIG9uIFtgY29udHJvbC5yZW1vdmUoKWBdKCNjb250cm9sLXJlbW92ZSkuXHJcbiAqL1xyXG5cclxuLyogQG5hbWVzcGFjZSBNYXBcclxuICogQHNlY3Rpb24gTWV0aG9kcyBmb3IgTGF5ZXJzIGFuZCBDb250cm9sc1xyXG4gKi9cclxuTC5NYXAuaW5jbHVkZSh7XHJcblx0Ly8gQG1ldGhvZCBhZGRDb250cm9sKGNvbnRyb2w6IENvbnRyb2wpOiB0aGlzXHJcblx0Ly8gQWRkcyB0aGUgZ2l2ZW4gY29udHJvbCB0byB0aGUgbWFwXHJcblx0YWRkQ29udHJvbDogZnVuY3Rpb24gKGNvbnRyb2wpIHtcclxuXHRcdGNvbnRyb2wuYWRkVG8odGhpcyk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHJlbW92ZUNvbnRyb2woY29udHJvbDogQ29udHJvbCk6IHRoaXNcclxuXHQvLyBSZW1vdmVzIHRoZSBnaXZlbiBjb250cm9sIGZyb20gdGhlIG1hcFxyXG5cdHJlbW92ZUNvbnRyb2w6IGZ1bmN0aW9uIChjb250cm9sKSB7XHJcblx0XHRjb250cm9sLnJlbW92ZSgpO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0X2luaXRDb250cm9sUG9zOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgY29ybmVycyA9IHRoaXMuX2NvbnRyb2xDb3JuZXJzID0ge30sXHJcblx0XHQgICAgbCA9ICdsZWFmbGV0LScsXHJcblx0XHQgICAgY29udGFpbmVyID0gdGhpcy5fY29udHJvbENvbnRhaW5lciA9XHJcblx0XHQgICAgICAgICAgICBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCBsICsgJ2NvbnRyb2wtY29udGFpbmVyJywgdGhpcy5fY29udGFpbmVyKTtcclxuXHJcblx0XHRmdW5jdGlvbiBjcmVhdGVDb3JuZXIodlNpZGUsIGhTaWRlKSB7XHJcblx0XHRcdHZhciBjbGFzc05hbWUgPSBsICsgdlNpZGUgKyAnICcgKyBsICsgaFNpZGU7XHJcblxyXG5cdFx0XHRjb3JuZXJzW3ZTaWRlICsgaFNpZGVdID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgY2xhc3NOYW1lLCBjb250YWluZXIpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGNyZWF0ZUNvcm5lcigndG9wJywgJ2xlZnQnKTtcclxuXHRcdGNyZWF0ZUNvcm5lcigndG9wJywgJ3JpZ2h0Jyk7XHJcblx0XHRjcmVhdGVDb3JuZXIoJ2JvdHRvbScsICdsZWZ0Jyk7XHJcblx0XHRjcmVhdGVDb3JuZXIoJ2JvdHRvbScsICdyaWdodCcpO1xyXG5cdH0sXHJcblxyXG5cdF9jbGVhckNvbnRyb2xQb3M6IGZ1bmN0aW9uICgpIHtcclxuXHRcdEwuRG9tVXRpbC5yZW1vdmUodGhpcy5fY29udHJvbENvbnRhaW5lcik7XHJcblx0fVxyXG59KTtcclxuXG5cblxuLypcclxuICogQGNsYXNzIENvbnRyb2wuWm9vbVxyXG4gKiBAYWthIEwuQ29udHJvbC5ab29tXHJcbiAqIEBpbmhlcml0cyBDb250cm9sXHJcbiAqXHJcbiAqIEEgYmFzaWMgem9vbSBjb250cm9sIHdpdGggdHdvIGJ1dHRvbnMgKHpvb20gaW4gYW5kIHpvb20gb3V0KS4gSXQgaXMgcHV0IG9uIHRoZSBtYXAgYnkgZGVmYXVsdCB1bmxlc3MgeW91IHNldCBpdHMgW2B6b29tQ29udHJvbGAgb3B0aW9uXSgjbWFwLXpvb21jb250cm9sKSB0byBgZmFsc2VgLiBFeHRlbmRzIGBDb250cm9sYC5cclxuICovXHJcblxyXG5MLkNvbnRyb2wuWm9vbSA9IEwuQ29udHJvbC5leHRlbmQoe1xyXG5cdC8vIEBzZWN0aW9uXHJcblx0Ly8gQGFrYSBDb250cm9sLlpvb20gb3B0aW9uc1xyXG5cdG9wdGlvbnM6IHtcclxuXHRcdHBvc2l0aW9uOiAndG9wbGVmdCcsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB6b29tSW5UZXh0OiBTdHJpbmcgPSAnKydcclxuXHRcdC8vIFRoZSB0ZXh0IHNldCBvbiB0aGUgJ3pvb20gaW4nIGJ1dHRvbi5cclxuXHRcdHpvb21JblRleHQ6ICcrJyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHpvb21JblRpdGxlOiBTdHJpbmcgPSAnWm9vbSBpbidcclxuXHRcdC8vIFRoZSB0aXRsZSBzZXQgb24gdGhlICd6b29tIGluJyBidXR0b24uXHJcblx0XHR6b29tSW5UaXRsZTogJ1pvb20gaW4nLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gem9vbU91dFRleHQ6IFN0cmluZyA9ICctJ1xyXG5cdFx0Ly8gVGhlIHRleHQgc2V0IG9uIHRoZSAnem9vbSBvdXQnIGJ1dHRvbi5cclxuXHRcdHpvb21PdXRUZXh0OiAnLScsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB6b29tT3V0VGl0bGU6IFN0cmluZyA9ICdab29tIG91dCdcclxuXHRcdC8vIFRoZSB0aXRsZSBzZXQgb24gdGhlICd6b29tIG91dCcgYnV0dG9uLlxyXG5cdFx0em9vbU91dFRpdGxlOiAnWm9vbSBvdXQnXHJcblx0fSxcclxuXHJcblx0b25BZGQ6IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdHZhciB6b29tTmFtZSA9ICdsZWFmbGV0LWNvbnRyb2wtem9vbScsXHJcblx0XHQgICAgY29udGFpbmVyID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2Jywgem9vbU5hbWUgKyAnIGxlYWZsZXQtYmFyJyksXHJcblx0XHQgICAgb3B0aW9ucyA9IHRoaXMub3B0aW9ucztcclxuXHJcblx0XHR0aGlzLl96b29tSW5CdXR0b24gID0gdGhpcy5fY3JlYXRlQnV0dG9uKG9wdGlvbnMuem9vbUluVGV4dCwgb3B0aW9ucy56b29tSW5UaXRsZSxcclxuXHRcdCAgICAgICAgem9vbU5hbWUgKyAnLWluJywgIGNvbnRhaW5lciwgdGhpcy5fem9vbUluKTtcclxuXHRcdHRoaXMuX3pvb21PdXRCdXR0b24gPSB0aGlzLl9jcmVhdGVCdXR0b24ob3B0aW9ucy56b29tT3V0VGV4dCwgb3B0aW9ucy56b29tT3V0VGl0bGUsXHJcblx0XHQgICAgICAgIHpvb21OYW1lICsgJy1vdXQnLCBjb250YWluZXIsIHRoaXMuX3pvb21PdXQpO1xyXG5cclxuXHRcdHRoaXMuX3VwZGF0ZURpc2FibGVkKCk7XHJcblx0XHRtYXAub24oJ3pvb21lbmQgem9vbWxldmVsc2NoYW5nZScsIHRoaXMuX3VwZGF0ZURpc2FibGVkLCB0aGlzKTtcclxuXHJcblx0XHRyZXR1cm4gY29udGFpbmVyO1xyXG5cdH0sXHJcblxyXG5cdG9uUmVtb3ZlOiBmdW5jdGlvbiAobWFwKSB7XHJcblx0XHRtYXAub2ZmKCd6b29tZW5kIHpvb21sZXZlbHNjaGFuZ2UnLCB0aGlzLl91cGRhdGVEaXNhYmxlZCwgdGhpcyk7XHJcblx0fSxcclxuXHJcblx0ZGlzYWJsZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0dGhpcy5fZGlzYWJsZWQgPSB0cnVlO1xyXG5cdFx0dGhpcy5fdXBkYXRlRGlzYWJsZWQoKTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdGVuYWJsZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0dGhpcy5fZGlzYWJsZWQgPSBmYWxzZTtcclxuXHRcdHRoaXMuX3VwZGF0ZURpc2FibGVkKCk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfem9vbUluOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0aWYgKCF0aGlzLl9kaXNhYmxlZCAmJiB0aGlzLl9tYXAuX3pvb20gPCB0aGlzLl9tYXAuZ2V0TWF4Wm9vbSgpKSB7XHJcblx0XHRcdHRoaXMuX21hcC56b29tSW4odGhpcy5fbWFwLm9wdGlvbnMuem9vbURlbHRhICogKGUuc2hpZnRLZXkgPyAzIDogMSkpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdF96b29tT3V0OiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0aWYgKCF0aGlzLl9kaXNhYmxlZCAmJiB0aGlzLl9tYXAuX3pvb20gPiB0aGlzLl9tYXAuZ2V0TWluWm9vbSgpKSB7XHJcblx0XHRcdHRoaXMuX21hcC56b29tT3V0KHRoaXMuX21hcC5vcHRpb25zLnpvb21EZWx0YSAqIChlLnNoaWZ0S2V5ID8gMyA6IDEpKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfY3JlYXRlQnV0dG9uOiBmdW5jdGlvbiAoaHRtbCwgdGl0bGUsIGNsYXNzTmFtZSwgY29udGFpbmVyLCBmbikge1xyXG5cdFx0dmFyIGxpbmsgPSBMLkRvbVV0aWwuY3JlYXRlKCdhJywgY2xhc3NOYW1lLCBjb250YWluZXIpO1xyXG5cdFx0bGluay5pbm5lckhUTUwgPSBodG1sO1xyXG5cdFx0bGluay5ocmVmID0gJyMnO1xyXG5cdFx0bGluay50aXRsZSA9IHRpdGxlO1xyXG5cclxuXHRcdC8qXHJcblx0XHQgKiBXaWxsIGZvcmNlIHNjcmVlbiByZWFkZXJzIGxpa2UgVm9pY2VPdmVyIHRvIHJlYWQgdGhpcyBhcyBcIlpvb20gaW4gLSBidXR0b25cIlxyXG5cdFx0ICovXHJcblx0XHRsaW5rLnNldEF0dHJpYnV0ZSgncm9sZScsICdidXR0b24nKTtcclxuXHRcdGxpbmsuc2V0QXR0cmlidXRlKCdhcmlhLWxhYmVsJywgdGl0bGUpO1xyXG5cclxuXHRcdEwuRG9tRXZlbnRcclxuXHRcdCAgICAub24obGluaywgJ21vdXNlZG93biBkYmxjbGljaycsIEwuRG9tRXZlbnQuc3RvcFByb3BhZ2F0aW9uKVxyXG5cdFx0ICAgIC5vbihsaW5rLCAnY2xpY2snLCBMLkRvbUV2ZW50LnN0b3ApXHJcblx0XHQgICAgLm9uKGxpbmssICdjbGljaycsIGZuLCB0aGlzKVxyXG5cdFx0ICAgIC5vbihsaW5rLCAnY2xpY2snLCB0aGlzLl9yZWZvY3VzT25NYXAsIHRoaXMpO1xyXG5cclxuXHRcdHJldHVybiBsaW5rO1xyXG5cdH0sXHJcblxyXG5cdF91cGRhdGVEaXNhYmxlZDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcCxcclxuXHRcdCAgICBjbGFzc05hbWUgPSAnbGVhZmxldC1kaXNhYmxlZCc7XHJcblxyXG5cdFx0TC5Eb21VdGlsLnJlbW92ZUNsYXNzKHRoaXMuX3pvb21JbkJ1dHRvbiwgY2xhc3NOYW1lKTtcclxuXHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl96b29tT3V0QnV0dG9uLCBjbGFzc05hbWUpO1xyXG5cclxuXHRcdGlmICh0aGlzLl9kaXNhYmxlZCB8fCBtYXAuX3pvb20gPT09IG1hcC5nZXRNaW5ab29tKCkpIHtcclxuXHRcdFx0TC5Eb21VdGlsLmFkZENsYXNzKHRoaXMuX3pvb21PdXRCdXR0b24sIGNsYXNzTmFtZSk7XHJcblx0XHR9XHJcblx0XHRpZiAodGhpcy5fZGlzYWJsZWQgfHwgbWFwLl96b29tID09PSBtYXAuZ2V0TWF4Wm9vbSgpKSB7XHJcblx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl96b29tSW5CdXR0b24sIGNsYXNzTmFtZSk7XHJcblx0XHR9XHJcblx0fVxyXG59KTtcclxuXHJcbi8vIEBuYW1lc3BhY2UgTWFwXHJcbi8vIEBzZWN0aW9uIENvbnRyb2wgb3B0aW9uc1xyXG4vLyBAb3B0aW9uIHpvb21Db250cm9sOiBCb29sZWFuID0gdHJ1ZVxyXG4vLyBXaGV0aGVyIGEgW3pvb20gY29udHJvbF0oI2NvbnRyb2wtem9vbSkgaXMgYWRkZWQgdG8gdGhlIG1hcCBieSBkZWZhdWx0LlxyXG5MLk1hcC5tZXJnZU9wdGlvbnMoe1xyXG5cdHpvb21Db250cm9sOiB0cnVlXHJcbn0pO1xyXG5cclxuTC5NYXAuYWRkSW5pdEhvb2soZnVuY3Rpb24gKCkge1xyXG5cdGlmICh0aGlzLm9wdGlvbnMuem9vbUNvbnRyb2wpIHtcclxuXHRcdHRoaXMuem9vbUNvbnRyb2wgPSBuZXcgTC5Db250cm9sLlpvb20oKTtcclxuXHRcdHRoaXMuYWRkQ29udHJvbCh0aGlzLnpvb21Db250cm9sKTtcclxuXHR9XHJcbn0pO1xyXG5cclxuLy8gQG5hbWVzcGFjZSBDb250cm9sLlpvb21cclxuLy8gQGZhY3RvcnkgTC5jb250cm9sLnpvb20ob3B0aW9uczogQ29udHJvbC5ab29tIG9wdGlvbnMpXHJcbi8vIENyZWF0ZXMgYSB6b29tIGNvbnRyb2xcclxuTC5jb250cm9sLnpvb20gPSBmdW5jdGlvbiAob3B0aW9ucykge1xyXG5cdHJldHVybiBuZXcgTC5Db250cm9sLlpvb20ob3B0aW9ucyk7XHJcbn07XHJcblxuXG5cbi8qXHJcbiAqIEBjbGFzcyBDb250cm9sLkF0dHJpYnV0aW9uXHJcbiAqIEBha2EgTC5Db250cm9sLkF0dHJpYnV0aW9uXHJcbiAqIEBpbmhlcml0cyBDb250cm9sXHJcbiAqXHJcbiAqIFRoZSBhdHRyaWJ1dGlvbiBjb250cm9sIGFsbG93cyB5b3UgdG8gZGlzcGxheSBhdHRyaWJ1dGlvbiBkYXRhIGluIGEgc21hbGwgdGV4dCBib3ggb24gYSBtYXAuIEl0IGlzIHB1dCBvbiB0aGUgbWFwIGJ5IGRlZmF1bHQgdW5sZXNzIHlvdSBzZXQgaXRzIFtgYXR0cmlidXRpb25Db250cm9sYCBvcHRpb25dKCNtYXAtYXR0cmlidXRpb25jb250cm9sKSB0byBgZmFsc2VgLCBhbmQgaXQgZmV0Y2hlcyBhdHRyaWJ1dGlvbiB0ZXh0cyBmcm9tIGxheWVycyB3aXRoIHRoZSBbYGdldEF0dHJpYnV0aW9uYCBtZXRob2RdKCNsYXllci1nZXRhdHRyaWJ1dGlvbikgYXV0b21hdGljYWxseS4gRXh0ZW5kcyBDb250cm9sLlxyXG4gKi9cclxuXHJcbkwuQ29udHJvbC5BdHRyaWJ1dGlvbiA9IEwuQ29udHJvbC5leHRlbmQoe1xyXG5cdC8vIEBzZWN0aW9uXHJcblx0Ly8gQGFrYSBDb250cm9sLkF0dHJpYnV0aW9uIG9wdGlvbnNcclxuXHRvcHRpb25zOiB7XHJcblx0XHRwb3NpdGlvbjogJ2JvdHRvbXJpZ2h0JyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHByZWZpeDogU3RyaW5nID0gJ0xlYWZsZXQnXHJcblx0XHQvLyBUaGUgSFRNTCB0ZXh0IHNob3duIGJlZm9yZSB0aGUgYXR0cmlidXRpb25zLiBQYXNzIGBmYWxzZWAgdG8gZGlzYWJsZS5cclxuXHRcdHByZWZpeDogJzxhIGhyZWY9XCJodHRwOi8vbGVhZmxldGpzLmNvbVwiIHRpdGxlPVwiQSBKUyBsaWJyYXJ5IGZvciBpbnRlcmFjdGl2ZSBtYXBzXCI+TGVhZmxldDwvYT4nXHJcblx0fSxcclxuXHJcblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKG9wdGlvbnMpIHtcclxuXHRcdEwuc2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcclxuXHJcblx0XHR0aGlzLl9hdHRyaWJ1dGlvbnMgPSB7fTtcclxuXHR9LFxyXG5cclxuXHRvbkFkZDogZnVuY3Rpb24gKG1hcCkge1xyXG5cdFx0bWFwLmF0dHJpYnV0aW9uQ29udHJvbCA9IHRoaXM7XHJcblx0XHR0aGlzLl9jb250YWluZXIgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCAnbGVhZmxldC1jb250cm9sLWF0dHJpYnV0aW9uJyk7XHJcblx0XHRpZiAoTC5Eb21FdmVudCkge1xyXG5cdFx0XHRMLkRvbUV2ZW50LmRpc2FibGVDbGlja1Byb3BhZ2F0aW9uKHRoaXMuX2NvbnRhaW5lcik7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gVE9ETyB1Z2x5LCByZWZhY3RvclxyXG5cdFx0Zm9yICh2YXIgaSBpbiBtYXAuX2xheWVycykge1xyXG5cdFx0XHRpZiAobWFwLl9sYXllcnNbaV0uZ2V0QXR0cmlidXRpb24pIHtcclxuXHRcdFx0XHR0aGlzLmFkZEF0dHJpYnV0aW9uKG1hcC5fbGF5ZXJzW2ldLmdldEF0dHJpYnV0aW9uKCkpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fdXBkYXRlKCk7XHJcblxyXG5cdFx0cmV0dXJuIHRoaXMuX2NvbnRhaW5lcjtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldFByZWZpeChwcmVmaXg6IFN0cmluZyk6IHRoaXNcclxuXHQvLyBTZXRzIHRoZSB0ZXh0IGJlZm9yZSB0aGUgYXR0cmlidXRpb25zLlxyXG5cdHNldFByZWZpeDogZnVuY3Rpb24gKHByZWZpeCkge1xyXG5cdFx0dGhpcy5vcHRpb25zLnByZWZpeCA9IHByZWZpeDtcclxuXHRcdHRoaXMuX3VwZGF0ZSgpO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBhZGRBdHRyaWJ1dGlvbih0ZXh0OiBTdHJpbmcpOiB0aGlzXHJcblx0Ly8gQWRkcyBhbiBhdHRyaWJ1dGlvbiB0ZXh0IChlLmcuIGAnVmVjdG9yIGRhdGEgJmNvcHk7IE1hcGJveCdgKS5cclxuXHRhZGRBdHRyaWJ1dGlvbjogZnVuY3Rpb24gKHRleHQpIHtcclxuXHRcdGlmICghdGV4dCkgeyByZXR1cm4gdGhpczsgfVxyXG5cclxuXHRcdGlmICghdGhpcy5fYXR0cmlidXRpb25zW3RleHRdKSB7XHJcblx0XHRcdHRoaXMuX2F0dHJpYnV0aW9uc1t0ZXh0XSA9IDA7XHJcblx0XHR9XHJcblx0XHR0aGlzLl9hdHRyaWJ1dGlvbnNbdGV4dF0rKztcclxuXHJcblx0XHR0aGlzLl91cGRhdGUoKTtcclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHJlbW92ZUF0dHJpYnV0aW9uKHRleHQ6IFN0cmluZyk6IHRoaXNcclxuXHQvLyBSZW1vdmVzIGFuIGF0dHJpYnV0aW9uIHRleHQuXHJcblx0cmVtb3ZlQXR0cmlidXRpb246IGZ1bmN0aW9uICh0ZXh0KSB7XHJcblx0XHRpZiAoIXRleHQpIHsgcmV0dXJuIHRoaXM7IH1cclxuXHJcblx0XHRpZiAodGhpcy5fYXR0cmlidXRpb25zW3RleHRdKSB7XHJcblx0XHRcdHRoaXMuX2F0dHJpYnV0aW9uc1t0ZXh0XS0tO1xyXG5cdFx0XHR0aGlzLl91cGRhdGUoKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfdXBkYXRlOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAoIXRoaXMuX21hcCkgeyByZXR1cm47IH1cclxuXHJcblx0XHR2YXIgYXR0cmlicyA9IFtdO1xyXG5cclxuXHRcdGZvciAodmFyIGkgaW4gdGhpcy5fYXR0cmlidXRpb25zKSB7XHJcblx0XHRcdGlmICh0aGlzLl9hdHRyaWJ1dGlvbnNbaV0pIHtcclxuXHRcdFx0XHRhdHRyaWJzLnB1c2goaSk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHR2YXIgcHJlZml4QW5kQXR0cmlicyA9IFtdO1xyXG5cclxuXHRcdGlmICh0aGlzLm9wdGlvbnMucHJlZml4KSB7XHJcblx0XHRcdHByZWZpeEFuZEF0dHJpYnMucHVzaCh0aGlzLm9wdGlvbnMucHJlZml4KTtcclxuXHRcdH1cclxuXHRcdGlmIChhdHRyaWJzLmxlbmd0aCkge1xyXG5cdFx0XHRwcmVmaXhBbmRBdHRyaWJzLnB1c2goYXR0cmlicy5qb2luKCcsICcpKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9jb250YWluZXIuaW5uZXJIVE1MID0gcHJlZml4QW5kQXR0cmlicy5qb2luKCcgfCAnKTtcclxuXHR9XHJcbn0pO1xyXG5cclxuLy8gQG5hbWVzcGFjZSBNYXBcclxuLy8gQHNlY3Rpb24gQ29udHJvbCBvcHRpb25zXHJcbi8vIEBvcHRpb24gYXR0cmlidXRpb25Db250cm9sOiBCb29sZWFuID0gdHJ1ZVxyXG4vLyBXaGV0aGVyIGEgW2F0dHJpYnV0aW9uIGNvbnRyb2xdKCNjb250cm9sLWF0dHJpYnV0aW9uKSBpcyBhZGRlZCB0byB0aGUgbWFwIGJ5IGRlZmF1bHQuXHJcbkwuTWFwLm1lcmdlT3B0aW9ucyh7XHJcblx0YXR0cmlidXRpb25Db250cm9sOiB0cnVlXHJcbn0pO1xyXG5cclxuTC5NYXAuYWRkSW5pdEhvb2soZnVuY3Rpb24gKCkge1xyXG5cdGlmICh0aGlzLm9wdGlvbnMuYXR0cmlidXRpb25Db250cm9sKSB7XHJcblx0XHRuZXcgTC5Db250cm9sLkF0dHJpYnV0aW9uKCkuYWRkVG8odGhpcyk7XHJcblx0fVxyXG59KTtcclxuXHJcbi8vIEBuYW1lc3BhY2UgQ29udHJvbC5BdHRyaWJ1dGlvblxyXG4vLyBAZmFjdG9yeSBMLmNvbnRyb2wuYXR0cmlidXRpb24ob3B0aW9uczogQ29udHJvbC5BdHRyaWJ1dGlvbiBvcHRpb25zKVxyXG4vLyBDcmVhdGVzIGFuIGF0dHJpYnV0aW9uIGNvbnRyb2wuXHJcbkwuY29udHJvbC5hdHRyaWJ1dGlvbiA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XHJcblx0cmV0dXJuIG5ldyBMLkNvbnRyb2wuQXR0cmlidXRpb24ob3B0aW9ucyk7XHJcbn07XHJcblxuXG5cbi8qXG4gKiBAY2xhc3MgQ29udHJvbC5TY2FsZVxuICogQGFrYSBMLkNvbnRyb2wuU2NhbGVcbiAqIEBpbmhlcml0cyBDb250cm9sXG4gKlxuICogQSBzaW1wbGUgc2NhbGUgY29udHJvbCB0aGF0IHNob3dzIHRoZSBzY2FsZSBvZiB0aGUgY3VycmVudCBjZW50ZXIgb2Ygc2NyZWVuIGluIG1ldHJpYyAobS9rbSkgYW5kIGltcGVyaWFsIChtaS9mdCkgc3lzdGVtcy4gRXh0ZW5kcyBgQ29udHJvbGAuXG4gKlxuICogQGV4YW1wbGVcbiAqXG4gKiBgYGBqc1xuICogTC5jb250cm9sLnNjYWxlKCkuYWRkVG8obWFwKTtcbiAqIGBgYFxuICovXG5cbkwuQ29udHJvbC5TY2FsZSA9IEwuQ29udHJvbC5leHRlbmQoe1xuXHQvLyBAc2VjdGlvblxuXHQvLyBAYWthIENvbnRyb2wuU2NhbGUgb3B0aW9uc1xuXHRvcHRpb25zOiB7XG5cdFx0cG9zaXRpb246ICdib3R0b21sZWZ0JyxcblxuXHRcdC8vIEBvcHRpb24gbWF4V2lkdGg6IE51bWJlciA9IDEwMFxuXHRcdC8vIE1heGltdW0gd2lkdGggb2YgdGhlIGNvbnRyb2wgaW4gcGl4ZWxzLiBUaGUgd2lkdGggaXMgc2V0IGR5bmFtaWNhbGx5IHRvIHNob3cgcm91bmQgdmFsdWVzIChlLmcuIDEwMCwgMjAwLCA1MDApLlxuXHRcdG1heFdpZHRoOiAxMDAsXG5cblx0XHQvLyBAb3B0aW9uIG1ldHJpYzogQm9vbGVhbiA9IFRydWVcblx0XHQvLyBXaGV0aGVyIHRvIHNob3cgdGhlIG1ldHJpYyBzY2FsZSBsaW5lIChtL2ttKS5cblx0XHRtZXRyaWM6IHRydWUsXG5cblx0XHQvLyBAb3B0aW9uIGltcGVyaWFsOiBCb29sZWFuID0gVHJ1ZVxuXHRcdC8vIFdoZXRoZXIgdG8gc2hvdyB0aGUgaW1wZXJpYWwgc2NhbGUgbGluZSAobWkvZnQpLlxuXHRcdGltcGVyaWFsOiB0cnVlXG5cblx0XHQvLyBAb3B0aW9uIHVwZGF0ZVdoZW5JZGxlOiBCb29sZWFuID0gZmFsc2Vcblx0XHQvLyBJZiBgdHJ1ZWAsIHRoZSBjb250cm9sIGlzIHVwZGF0ZWQgb24gW2Btb3ZlZW5kYF0oI21hcC1tb3ZlZW5kKSwgb3RoZXJ3aXNlIGl0J3MgYWx3YXlzIHVwLXRvLWRhdGUgKHVwZGF0ZWQgb24gW2Btb3ZlYF0oI21hcC1tb3ZlKSkuXG5cdH0sXG5cblx0b25BZGQ6IGZ1bmN0aW9uIChtYXApIHtcblx0XHR2YXIgY2xhc3NOYW1lID0gJ2xlYWZsZXQtY29udHJvbC1zY2FsZScsXG5cdFx0ICAgIGNvbnRhaW5lciA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsIGNsYXNzTmFtZSksXG5cdFx0ICAgIG9wdGlvbnMgPSB0aGlzLm9wdGlvbnM7XG5cblx0XHR0aGlzLl9hZGRTY2FsZXMob3B0aW9ucywgY2xhc3NOYW1lICsgJy1saW5lJywgY29udGFpbmVyKTtcblxuXHRcdG1hcC5vbihvcHRpb25zLnVwZGF0ZVdoZW5JZGxlID8gJ21vdmVlbmQnIDogJ21vdmUnLCB0aGlzLl91cGRhdGUsIHRoaXMpO1xuXHRcdG1hcC53aGVuUmVhZHkodGhpcy5fdXBkYXRlLCB0aGlzKTtcblxuXHRcdHJldHVybiBjb250YWluZXI7XG5cdH0sXG5cblx0b25SZW1vdmU6IGZ1bmN0aW9uIChtYXApIHtcblx0XHRtYXAub2ZmKHRoaXMub3B0aW9ucy51cGRhdGVXaGVuSWRsZSA/ICdtb3ZlZW5kJyA6ICdtb3ZlJywgdGhpcy5fdXBkYXRlLCB0aGlzKTtcblx0fSxcblxuXHRfYWRkU2NhbGVzOiBmdW5jdGlvbiAob3B0aW9ucywgY2xhc3NOYW1lLCBjb250YWluZXIpIHtcblx0XHRpZiAob3B0aW9ucy5tZXRyaWMpIHtcblx0XHRcdHRoaXMuX21TY2FsZSA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsIGNsYXNzTmFtZSwgY29udGFpbmVyKTtcblx0XHR9XG5cdFx0aWYgKG9wdGlvbnMuaW1wZXJpYWwpIHtcblx0XHRcdHRoaXMuX2lTY2FsZSA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsIGNsYXNzTmFtZSwgY29udGFpbmVyKTtcblx0XHR9XG5cdH0sXG5cblx0X3VwZGF0ZTogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBtYXAgPSB0aGlzLl9tYXAsXG5cdFx0ICAgIHkgPSBtYXAuZ2V0U2l6ZSgpLnkgLyAyO1xuXG5cdFx0dmFyIG1heE1ldGVycyA9IG1hcC5kaXN0YW5jZShcblx0XHRcdFx0bWFwLmNvbnRhaW5lclBvaW50VG9MYXRMbmcoWzAsIHldKSxcblx0XHRcdFx0bWFwLmNvbnRhaW5lclBvaW50VG9MYXRMbmcoW3RoaXMub3B0aW9ucy5tYXhXaWR0aCwgeV0pKTtcblxuXHRcdHRoaXMuX3VwZGF0ZVNjYWxlcyhtYXhNZXRlcnMpO1xuXHR9LFxuXG5cdF91cGRhdGVTY2FsZXM6IGZ1bmN0aW9uIChtYXhNZXRlcnMpIHtcblx0XHRpZiAodGhpcy5vcHRpb25zLm1ldHJpYyAmJiBtYXhNZXRlcnMpIHtcblx0XHRcdHRoaXMuX3VwZGF0ZU1ldHJpYyhtYXhNZXRlcnMpO1xuXHRcdH1cblx0XHRpZiAodGhpcy5vcHRpb25zLmltcGVyaWFsICYmIG1heE1ldGVycykge1xuXHRcdFx0dGhpcy5fdXBkYXRlSW1wZXJpYWwobWF4TWV0ZXJzKTtcblx0XHR9XG5cdH0sXG5cblx0X3VwZGF0ZU1ldHJpYzogZnVuY3Rpb24gKG1heE1ldGVycykge1xuXHRcdHZhciBtZXRlcnMgPSB0aGlzLl9nZXRSb3VuZE51bShtYXhNZXRlcnMpLFxuXHRcdCAgICBsYWJlbCA9IG1ldGVycyA8IDEwMDAgPyBtZXRlcnMgKyAnIG0nIDogKG1ldGVycyAvIDEwMDApICsgJyBrbSc7XG5cblx0XHR0aGlzLl91cGRhdGVTY2FsZSh0aGlzLl9tU2NhbGUsIGxhYmVsLCBtZXRlcnMgLyBtYXhNZXRlcnMpO1xuXHR9LFxuXG5cdF91cGRhdGVJbXBlcmlhbDogZnVuY3Rpb24gKG1heE1ldGVycykge1xuXHRcdHZhciBtYXhGZWV0ID0gbWF4TWV0ZXJzICogMy4yODA4Mzk5LFxuXHRcdCAgICBtYXhNaWxlcywgbWlsZXMsIGZlZXQ7XG5cblx0XHRpZiAobWF4RmVldCA+IDUyODApIHtcblx0XHRcdG1heE1pbGVzID0gbWF4RmVldCAvIDUyODA7XG5cdFx0XHRtaWxlcyA9IHRoaXMuX2dldFJvdW5kTnVtKG1heE1pbGVzKTtcblx0XHRcdHRoaXMuX3VwZGF0ZVNjYWxlKHRoaXMuX2lTY2FsZSwgbWlsZXMgKyAnIG1pJywgbWlsZXMgLyBtYXhNaWxlcyk7XG5cblx0XHR9IGVsc2Uge1xuXHRcdFx0ZmVldCA9IHRoaXMuX2dldFJvdW5kTnVtKG1heEZlZXQpO1xuXHRcdFx0dGhpcy5fdXBkYXRlU2NhbGUodGhpcy5faVNjYWxlLCBmZWV0ICsgJyBmdCcsIGZlZXQgLyBtYXhGZWV0KTtcblx0XHR9XG5cdH0sXG5cblx0X3VwZGF0ZVNjYWxlOiBmdW5jdGlvbiAoc2NhbGUsIHRleHQsIHJhdGlvKSB7XG5cdFx0c2NhbGUuc3R5bGUud2lkdGggPSBNYXRoLnJvdW5kKHRoaXMub3B0aW9ucy5tYXhXaWR0aCAqIHJhdGlvKSArICdweCc7XG5cdFx0c2NhbGUuaW5uZXJIVE1MID0gdGV4dDtcblx0fSxcblxuXHRfZ2V0Um91bmROdW06IGZ1bmN0aW9uIChudW0pIHtcblx0XHR2YXIgcG93MTAgPSBNYXRoLnBvdygxMCwgKE1hdGguZmxvb3IobnVtKSArICcnKS5sZW5ndGggLSAxKSxcblx0XHQgICAgZCA9IG51bSAvIHBvdzEwO1xuXG5cdFx0ZCA9IGQgPj0gMTAgPyAxMCA6XG5cdFx0ICAgIGQgPj0gNSA/IDUgOlxuXHRcdCAgICBkID49IDMgPyAzIDpcblx0XHQgICAgZCA+PSAyID8gMiA6IDE7XG5cblx0XHRyZXR1cm4gcG93MTAgKiBkO1xuXHR9XG59KTtcblxuXG4vLyBAZmFjdG9yeSBMLmNvbnRyb2wuc2NhbGUob3B0aW9ucz86IENvbnRyb2wuU2NhbGUgb3B0aW9ucylcbi8vIENyZWF0ZXMgYW4gc2NhbGUgY29udHJvbCB3aXRoIHRoZSBnaXZlbiBvcHRpb25zLlxuTC5jb250cm9sLnNjYWxlID0gZnVuY3Rpb24gKG9wdGlvbnMpIHtcblx0cmV0dXJuIG5ldyBMLkNvbnRyb2wuU2NhbGUob3B0aW9ucyk7XG59O1xuXG5cblxuLypcclxuICogQGNsYXNzIENvbnRyb2wuTGF5ZXJzXHJcbiAqIEBha2EgTC5Db250cm9sLkxheWVyc1xyXG4gKiBAaW5oZXJpdHMgQ29udHJvbFxyXG4gKlxyXG4gKiBUaGUgbGF5ZXJzIGNvbnRyb2wgZ2l2ZXMgdXNlcnMgdGhlIGFiaWxpdHkgdG8gc3dpdGNoIGJldHdlZW4gZGlmZmVyZW50IGJhc2UgbGF5ZXJzIGFuZCBzd2l0Y2ggb3ZlcmxheXMgb24vb2ZmIChjaGVjayBvdXQgdGhlIFtkZXRhaWxlZCBleGFtcGxlXShodHRwOi8vbGVhZmxldGpzLmNvbS9leGFtcGxlcy9sYXllcnMtY29udHJvbC5odG1sKSkuIEV4dGVuZHMgYENvbnRyb2xgLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiB2YXIgYmFzZUxheWVycyA9IHtcclxuICogXHRcIk1hcGJveFwiOiBtYXBib3gsXHJcbiAqIFx0XCJPcGVuU3RyZWV0TWFwXCI6IG9zbVxyXG4gKiB9O1xyXG4gKlxyXG4gKiB2YXIgb3ZlcmxheXMgPSB7XHJcbiAqIFx0XCJNYXJrZXJcIjogbWFya2VyLFxyXG4gKiBcdFwiUm9hZHNcIjogcm9hZHNMYXllclxyXG4gKiB9O1xyXG4gKlxyXG4gKiBMLmNvbnRyb2wubGF5ZXJzKGJhc2VMYXllcnMsIG92ZXJsYXlzKS5hZGRUbyhtYXApO1xyXG4gKiBgYGBcclxuICpcclxuICogVGhlIGBiYXNlTGF5ZXJzYCBhbmQgYG92ZXJsYXlzYCBwYXJhbWV0ZXJzIGFyZSBvYmplY3QgbGl0ZXJhbHMgd2l0aCBsYXllciBuYW1lcyBhcyBrZXlzIGFuZCBgTGF5ZXJgIG9iamVjdHMgYXMgdmFsdWVzOlxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiB7XHJcbiAqICAgICBcIjxzb21lTmFtZTE+XCI6IGxheWVyMSxcclxuICogICAgIFwiPHNvbWVOYW1lMj5cIjogbGF5ZXIyXHJcbiAqIH1cclxuICogYGBgXHJcbiAqXHJcbiAqIFRoZSBsYXllciBuYW1lcyBjYW4gY29udGFpbiBIVE1MLCB3aGljaCBhbGxvd3MgeW91IHRvIGFkZCBhZGRpdGlvbmFsIHN0eWxpbmcgdG8gdGhlIGl0ZW1zOlxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiB7XCI8aW1nIHNyYz0nbXktbGF5ZXItaWNvbicgLz4gPHNwYW4gY2xhc3M9J215LWxheWVyLWl0ZW0nPk15IExheWVyPC9zcGFuPlwiOiBteUxheWVyfVxyXG4gKiBgYGBcclxuICovXHJcblxyXG5cclxuTC5Db250cm9sLkxheWVycyA9IEwuQ29udHJvbC5leHRlbmQoe1xyXG5cdC8vIEBzZWN0aW9uXHJcblx0Ly8gQGFrYSBDb250cm9sLkxheWVycyBvcHRpb25zXHJcblx0b3B0aW9uczoge1xyXG5cdFx0Ly8gQG9wdGlvbiBjb2xsYXBzZWQ6IEJvb2xlYW4gPSB0cnVlXHJcblx0XHQvLyBJZiBgdHJ1ZWAsIHRoZSBjb250cm9sIHdpbGwgYmUgY29sbGFwc2VkIGludG8gYW4gaWNvbiBhbmQgZXhwYW5kZWQgb24gbW91c2UgaG92ZXIgb3IgdG91Y2guXHJcblx0XHRjb2xsYXBzZWQ6IHRydWUsXHJcblx0XHRwb3NpdGlvbjogJ3RvcHJpZ2h0JyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGF1dG9aSW5kZXg6IEJvb2xlYW4gPSB0cnVlXHJcblx0XHQvLyBJZiBgdHJ1ZWAsIHRoZSBjb250cm9sIHdpbGwgYXNzaWduIHpJbmRleGVzIGluIGluY3JlYXNpbmcgb3JkZXIgdG8gYWxsIG9mIGl0cyBsYXllcnMgc28gdGhhdCB0aGUgb3JkZXIgaXMgcHJlc2VydmVkIHdoZW4gc3dpdGNoaW5nIHRoZW0gb24vb2ZmLlxyXG5cdFx0YXV0b1pJbmRleDogdHJ1ZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGhpZGVTaW5nbGVCYXNlOiBCb29sZWFuID0gZmFsc2VcclxuXHRcdC8vIElmIGB0cnVlYCwgdGhlIGJhc2UgbGF5ZXJzIGluIHRoZSBjb250cm9sIHdpbGwgYmUgaGlkZGVuIHdoZW4gdGhlcmUgaXMgb25seSBvbmUuXHJcblx0XHRoaWRlU2luZ2xlQmFzZTogZmFsc2UsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBzb3J0TGF5ZXJzOiBCb29sZWFuID0gZmFsc2VcclxuXHRcdC8vIFdoZXRoZXIgdG8gc29ydCB0aGUgbGF5ZXJzLiBXaGVuIGBmYWxzZWAsIGxheWVycyB3aWxsIGtlZXAgdGhlIG9yZGVyXHJcblx0XHQvLyBpbiB3aGljaCB0aGV5IHdlcmUgYWRkZWQgdG8gdGhlIGNvbnRyb2wuXHJcblx0XHRzb3J0TGF5ZXJzOiBmYWxzZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHNvcnRGdW5jdGlvbjogRnVuY3Rpb24gPSAqXHJcblx0XHQvLyBBIFtjb21wYXJlIGZ1bmN0aW9uXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9BcnJheS9zb3J0KVxyXG5cdFx0Ly8gdGhhdCB3aWxsIGJlIHVzZWQgZm9yIHNvcnRpbmcgdGhlIGxheWVycywgd2hlbiBgc29ydExheWVyc2AgaXMgYHRydWVgLlxyXG5cdFx0Ly8gVGhlIGZ1bmN0aW9uIHJlY2VpdmVzIGJvdGggdGhlIGBMLkxheWVyYCBpbnN0YW5jZXMgYW5kIHRoZWlyIG5hbWVzLCBhcyBpblxyXG5cdFx0Ly8gYHNvcnRGdW5jdGlvbihsYXllckEsIGxheWVyQiwgbmFtZUEsIG5hbWVCKWAuXHJcblx0XHQvLyBCeSBkZWZhdWx0LCBpdCBzb3J0cyBsYXllcnMgYWxwaGFiZXRpY2FsbHkgYnkgdGhlaXIgbmFtZS5cclxuXHRcdHNvcnRGdW5jdGlvbjogZnVuY3Rpb24gKGxheWVyQSwgbGF5ZXJCLCBuYW1lQSwgbmFtZUIpIHtcclxuXHRcdFx0cmV0dXJuIG5hbWVBIDwgbmFtZUIgPyAtMSA6IChuYW1lQiA8IG5hbWVBID8gMSA6IDApO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChiYXNlTGF5ZXJzLCBvdmVybGF5cywgb3B0aW9ucykge1xyXG5cdFx0TC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xyXG5cclxuXHRcdHRoaXMuX2xheWVycyA9IFtdO1xyXG5cdFx0dGhpcy5fbGFzdFpJbmRleCA9IDA7XHJcblx0XHR0aGlzLl9oYW5kbGluZ0NsaWNrID0gZmFsc2U7XHJcblxyXG5cdFx0Zm9yICh2YXIgaSBpbiBiYXNlTGF5ZXJzKSB7XHJcblx0XHRcdHRoaXMuX2FkZExheWVyKGJhc2VMYXllcnNbaV0sIGkpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGZvciAoaSBpbiBvdmVybGF5cykge1xyXG5cdFx0XHR0aGlzLl9hZGRMYXllcihvdmVybGF5c1tpXSwgaSwgdHJ1ZSk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0b25BZGQ6IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdHRoaXMuX2luaXRMYXlvdXQoKTtcclxuXHRcdHRoaXMuX3VwZGF0ZSgpO1xyXG5cclxuXHRcdHRoaXMuX21hcCA9IG1hcDtcclxuXHRcdG1hcC5vbignem9vbWVuZCcsIHRoaXMuX2NoZWNrRGlzYWJsZWRMYXllcnMsIHRoaXMpO1xyXG5cclxuXHRcdHJldHVybiB0aGlzLl9jb250YWluZXI7XHJcblx0fSxcclxuXHJcblx0b25SZW1vdmU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHRoaXMuX21hcC5vZmYoJ3pvb21lbmQnLCB0aGlzLl9jaGVja0Rpc2FibGVkTGF5ZXJzLCB0aGlzKTtcclxuXHJcblx0XHRmb3IgKHZhciBpID0gMDsgaSA8IHRoaXMuX2xheWVycy5sZW5ndGg7IGkrKykge1xyXG5cdFx0XHR0aGlzLl9sYXllcnNbaV0ubGF5ZXIub2ZmKCdhZGQgcmVtb3ZlJywgdGhpcy5fb25MYXllckNoYW5nZSwgdGhpcyk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBhZGRCYXNlTGF5ZXIobGF5ZXI6IExheWVyLCBuYW1lOiBTdHJpbmcpOiB0aGlzXHJcblx0Ly8gQWRkcyBhIGJhc2UgbGF5ZXIgKHJhZGlvIGJ1dHRvbiBlbnRyeSkgd2l0aCB0aGUgZ2l2ZW4gbmFtZSB0byB0aGUgY29udHJvbC5cclxuXHRhZGRCYXNlTGF5ZXI6IGZ1bmN0aW9uIChsYXllciwgbmFtZSkge1xyXG5cdFx0dGhpcy5fYWRkTGF5ZXIobGF5ZXIsIG5hbWUpO1xyXG5cdFx0cmV0dXJuICh0aGlzLl9tYXApID8gdGhpcy5fdXBkYXRlKCkgOiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgYWRkT3ZlcmxheShsYXllcjogTGF5ZXIsIG5hbWU6IFN0cmluZyk6IHRoaXNcclxuXHQvLyBBZGRzIGFuIG92ZXJsYXkgKGNoZWNrYm94IGVudHJ5KSB3aXRoIHRoZSBnaXZlbiBuYW1lIHRvIHRoZSBjb250cm9sLlxyXG5cdGFkZE92ZXJsYXk6IGZ1bmN0aW9uIChsYXllciwgbmFtZSkge1xyXG5cdFx0dGhpcy5fYWRkTGF5ZXIobGF5ZXIsIG5hbWUsIHRydWUpO1xyXG5cdFx0cmV0dXJuICh0aGlzLl9tYXApID8gdGhpcy5fdXBkYXRlKCkgOiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgcmVtb3ZlTGF5ZXIobGF5ZXI6IExheWVyKTogdGhpc1xyXG5cdC8vIFJlbW92ZSB0aGUgZ2l2ZW4gbGF5ZXIgZnJvbSB0aGUgY29udHJvbC5cclxuXHRyZW1vdmVMYXllcjogZnVuY3Rpb24gKGxheWVyKSB7XHJcblx0XHRsYXllci5vZmYoJ2FkZCByZW1vdmUnLCB0aGlzLl9vbkxheWVyQ2hhbmdlLCB0aGlzKTtcclxuXHJcblx0XHR2YXIgb2JqID0gdGhpcy5fZ2V0TGF5ZXIoTC5zdGFtcChsYXllcikpO1xyXG5cdFx0aWYgKG9iaikge1xyXG5cdFx0XHR0aGlzLl9sYXllcnMuc3BsaWNlKHRoaXMuX2xheWVycy5pbmRleE9mKG9iaiksIDEpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuICh0aGlzLl9tYXApID8gdGhpcy5fdXBkYXRlKCkgOiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZXhwYW5kKCk6IHRoaXNcclxuXHQvLyBFeHBhbmQgdGhlIGNvbnRyb2wgY29udGFpbmVyIGlmIGNvbGxhcHNlZC5cclxuXHRleHBhbmQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9jb250YWluZXIsICdsZWFmbGV0LWNvbnRyb2wtbGF5ZXJzLWV4cGFuZGVkJyk7XHJcblx0XHR0aGlzLl9mb3JtLnN0eWxlLmhlaWdodCA9IG51bGw7XHJcblx0XHR2YXIgYWNjZXB0YWJsZUhlaWdodCA9IHRoaXMuX21hcC5nZXRTaXplKCkueSAtICh0aGlzLl9jb250YWluZXIub2Zmc2V0VG9wICsgNTApO1xyXG5cdFx0aWYgKGFjY2VwdGFibGVIZWlnaHQgPCB0aGlzLl9mb3JtLmNsaWVudEhlaWdodCkge1xyXG5cdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3ModGhpcy5fZm9ybSwgJ2xlYWZsZXQtY29udHJvbC1sYXllcnMtc2Nyb2xsYmFyJyk7XHJcblx0XHRcdHRoaXMuX2Zvcm0uc3R5bGUuaGVpZ2h0ID0gYWNjZXB0YWJsZUhlaWdodCArICdweCc7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRMLkRvbVV0aWwucmVtb3ZlQ2xhc3ModGhpcy5fZm9ybSwgJ2xlYWZsZXQtY29udHJvbC1sYXllcnMtc2Nyb2xsYmFyJyk7XHJcblx0XHR9XHJcblx0XHR0aGlzLl9jaGVja0Rpc2FibGVkTGF5ZXJzKCk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGNvbGxhcHNlKCk6IHRoaXNcclxuXHQvLyBDb2xsYXBzZSB0aGUgY29udHJvbCBjb250YWluZXIgaWYgZXhwYW5kZWQuXHJcblx0Y29sbGFwc2U6IGZ1bmN0aW9uICgpIHtcclxuXHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9jb250YWluZXIsICdsZWFmbGV0LWNvbnRyb2wtbGF5ZXJzLWV4cGFuZGVkJyk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfaW5pdExheW91dDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGNsYXNzTmFtZSA9ICdsZWFmbGV0LWNvbnRyb2wtbGF5ZXJzJyxcclxuXHRcdCAgICBjb250YWluZXIgPSB0aGlzLl9jb250YWluZXIgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCBjbGFzc05hbWUpO1xyXG5cclxuXHRcdC8vIG1ha2VzIHRoaXMgd29yayBvbiBJRSB0b3VjaCBkZXZpY2VzIGJ5IHN0b3BwaW5nIGl0IGZyb20gZmlyaW5nIGEgbW91c2VvdXQgZXZlbnQgd2hlbiB0aGUgdG91Y2ggaXMgcmVsZWFzZWRcclxuXHRcdGNvbnRhaW5lci5zZXRBdHRyaWJ1dGUoJ2FyaWEtaGFzcG9wdXAnLCB0cnVlKTtcclxuXHJcblx0XHRMLkRvbUV2ZW50LmRpc2FibGVDbGlja1Byb3BhZ2F0aW9uKGNvbnRhaW5lcik7XHJcblx0XHRpZiAoIUwuQnJvd3Nlci50b3VjaCkge1xyXG5cdFx0XHRMLkRvbUV2ZW50LmRpc2FibGVTY3JvbGxQcm9wYWdhdGlvbihjb250YWluZXIpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBmb3JtID0gdGhpcy5fZm9ybSA9IEwuRG9tVXRpbC5jcmVhdGUoJ2Zvcm0nLCBjbGFzc05hbWUgKyAnLWxpc3QnKTtcclxuXHJcblx0XHRpZiAoIUwuQnJvd3Nlci5hbmRyb2lkKSB7XHJcblx0XHRcdEwuRG9tRXZlbnQub24oY29udGFpbmVyLCB7XHJcblx0XHRcdFx0bW91c2VlbnRlcjogdGhpcy5leHBhbmQsXHJcblx0XHRcdFx0bW91c2VsZWF2ZTogdGhpcy5jb2xsYXBzZVxyXG5cdFx0XHR9LCB0aGlzKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgbGluayA9IHRoaXMuX2xheWVyc0xpbmsgPSBMLkRvbVV0aWwuY3JlYXRlKCdhJywgY2xhc3NOYW1lICsgJy10b2dnbGUnLCBjb250YWluZXIpO1xyXG5cdFx0bGluay5ocmVmID0gJyMnO1xyXG5cdFx0bGluay50aXRsZSA9ICdMYXllcnMnO1xyXG5cclxuXHRcdGlmIChMLkJyb3dzZXIudG91Y2gpIHtcclxuXHRcdFx0TC5Eb21FdmVudFxyXG5cdFx0XHQgICAgLm9uKGxpbmssICdjbGljaycsIEwuRG9tRXZlbnQuc3RvcClcclxuXHRcdFx0ICAgIC5vbihsaW5rLCAnY2xpY2snLCB0aGlzLmV4cGFuZCwgdGhpcyk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRMLkRvbUV2ZW50Lm9uKGxpbmssICdmb2N1cycsIHRoaXMuZXhwYW5kLCB0aGlzKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyB3b3JrIGFyb3VuZCBmb3IgRmlyZWZveCBBbmRyb2lkIGlzc3VlIGh0dHBzOi8vZ2l0aHViLmNvbS9MZWFmbGV0L0xlYWZsZXQvaXNzdWVzLzIwMzNcclxuXHRcdEwuRG9tRXZlbnQub24oZm9ybSwgJ2NsaWNrJywgZnVuY3Rpb24gKCkge1xyXG5cdFx0XHRzZXRUaW1lb3V0KEwuYmluZCh0aGlzLl9vbklucHV0Q2xpY2ssIHRoaXMpLCAwKTtcclxuXHRcdH0sIHRoaXMpO1xyXG5cclxuXHRcdHRoaXMuX21hcC5vbignY2xpY2snLCB0aGlzLmNvbGxhcHNlLCB0aGlzKTtcclxuXHRcdC8vIFRPRE8ga2V5Ym9hcmQgYWNjZXNzaWJpbGl0eVxyXG5cclxuXHRcdGlmICghdGhpcy5vcHRpb25zLmNvbGxhcHNlZCkge1xyXG5cdFx0XHR0aGlzLmV4cGFuZCgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX2Jhc2VMYXllcnNMaXN0ID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgY2xhc3NOYW1lICsgJy1iYXNlJywgZm9ybSk7XHJcblx0XHR0aGlzLl9zZXBhcmF0b3IgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCBjbGFzc05hbWUgKyAnLXNlcGFyYXRvcicsIGZvcm0pO1xyXG5cdFx0dGhpcy5fb3ZlcmxheXNMaXN0ID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgY2xhc3NOYW1lICsgJy1vdmVybGF5cycsIGZvcm0pO1xyXG5cclxuXHRcdGNvbnRhaW5lci5hcHBlbmRDaGlsZChmb3JtKTtcclxuXHR9LFxyXG5cclxuXHRfZ2V0TGF5ZXI6IGZ1bmN0aW9uIChpZCkge1xyXG5cdFx0Zm9yICh2YXIgaSA9IDA7IGkgPCB0aGlzLl9sYXllcnMubGVuZ3RoOyBpKyspIHtcclxuXHJcblx0XHRcdGlmICh0aGlzLl9sYXllcnNbaV0gJiYgTC5zdGFtcCh0aGlzLl9sYXllcnNbaV0ubGF5ZXIpID09PSBpZCkge1xyXG5cdFx0XHRcdHJldHVybiB0aGlzLl9sYXllcnNbaV07XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfYWRkTGF5ZXI6IGZ1bmN0aW9uIChsYXllciwgbmFtZSwgb3ZlcmxheSkge1xyXG5cdFx0bGF5ZXIub24oJ2FkZCByZW1vdmUnLCB0aGlzLl9vbkxheWVyQ2hhbmdlLCB0aGlzKTtcclxuXHJcblx0XHR0aGlzLl9sYXllcnMucHVzaCh7XHJcblx0XHRcdGxheWVyOiBsYXllcixcclxuXHRcdFx0bmFtZTogbmFtZSxcclxuXHRcdFx0b3ZlcmxheTogb3ZlcmxheVxyXG5cdFx0fSk7XHJcblxyXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5zb3J0TGF5ZXJzKSB7XHJcblx0XHRcdHRoaXMuX2xheWVycy5zb3J0KEwuYmluZChmdW5jdGlvbiAoYSwgYikge1xyXG5cdFx0XHRcdHJldHVybiB0aGlzLm9wdGlvbnMuc29ydEZ1bmN0aW9uKGEubGF5ZXIsIGIubGF5ZXIsIGEubmFtZSwgYi5uYW1lKTtcclxuXHRcdFx0fSwgdGhpcykpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICh0aGlzLm9wdGlvbnMuYXV0b1pJbmRleCAmJiBsYXllci5zZXRaSW5kZXgpIHtcclxuXHRcdFx0dGhpcy5fbGFzdFpJbmRleCsrO1xyXG5cdFx0XHRsYXllci5zZXRaSW5kZXgodGhpcy5fbGFzdFpJbmRleCk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X3VwZGF0ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKCF0aGlzLl9jb250YWluZXIpIHsgcmV0dXJuIHRoaXM7IH1cclxuXHJcblx0XHRMLkRvbVV0aWwuZW1wdHkodGhpcy5fYmFzZUxheWVyc0xpc3QpO1xyXG5cdFx0TC5Eb21VdGlsLmVtcHR5KHRoaXMuX292ZXJsYXlzTGlzdCk7XHJcblxyXG5cdFx0dmFyIGJhc2VMYXllcnNQcmVzZW50LCBvdmVybGF5c1ByZXNlbnQsIGksIG9iaiwgYmFzZUxheWVyc0NvdW50ID0gMDtcclxuXHJcblx0XHRmb3IgKGkgPSAwOyBpIDwgdGhpcy5fbGF5ZXJzLmxlbmd0aDsgaSsrKSB7XHJcblx0XHRcdG9iaiA9IHRoaXMuX2xheWVyc1tpXTtcclxuXHRcdFx0dGhpcy5fYWRkSXRlbShvYmopO1xyXG5cdFx0XHRvdmVybGF5c1ByZXNlbnQgPSBvdmVybGF5c1ByZXNlbnQgfHwgb2JqLm92ZXJsYXk7XHJcblx0XHRcdGJhc2VMYXllcnNQcmVzZW50ID0gYmFzZUxheWVyc1ByZXNlbnQgfHwgIW9iai5vdmVybGF5O1xyXG5cdFx0XHRiYXNlTGF5ZXJzQ291bnQgKz0gIW9iai5vdmVybGF5ID8gMSA6IDA7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gSGlkZSBiYXNlIGxheWVycyBzZWN0aW9uIGlmIHRoZXJlJ3Mgb25seSBvbmUgbGF5ZXIuXHJcblx0XHRpZiAodGhpcy5vcHRpb25zLmhpZGVTaW5nbGVCYXNlKSB7XHJcblx0XHRcdGJhc2VMYXllcnNQcmVzZW50ID0gYmFzZUxheWVyc1ByZXNlbnQgJiYgYmFzZUxheWVyc0NvdW50ID4gMTtcclxuXHRcdFx0dGhpcy5fYmFzZUxheWVyc0xpc3Quc3R5bGUuZGlzcGxheSA9IGJhc2VMYXllcnNQcmVzZW50ID8gJycgOiAnbm9uZSc7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fc2VwYXJhdG9yLnN0eWxlLmRpc3BsYXkgPSBvdmVybGF5c1ByZXNlbnQgJiYgYmFzZUxheWVyc1ByZXNlbnQgPyAnJyA6ICdub25lJztcclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfb25MYXllckNoYW5nZTogZnVuY3Rpb24gKGUpIHtcclxuXHRcdGlmICghdGhpcy5faGFuZGxpbmdDbGljaykge1xyXG5cdFx0XHR0aGlzLl91cGRhdGUoKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgb2JqID0gdGhpcy5fZ2V0TGF5ZXIoTC5zdGFtcChlLnRhcmdldCkpO1xyXG5cclxuXHRcdC8vIEBuYW1lc3BhY2UgTWFwXHJcblx0XHQvLyBAc2VjdGlvbiBMYXllciBldmVudHNcclxuXHRcdC8vIEBldmVudCBiYXNlbGF5ZXJjaGFuZ2U6IExheWVyc0NvbnRyb2xFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgYmFzZSBsYXllciBpcyBjaGFuZ2VkIHRocm91Z2ggdGhlIFtsYXllciBjb250cm9sXSgjY29udHJvbC1sYXllcnMpLlxyXG5cdFx0Ly8gQGV2ZW50IG92ZXJsYXlhZGQ6IExheWVyc0NvbnRyb2xFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiBhbiBvdmVybGF5IGlzIHNlbGVjdGVkIHRocm91Z2ggdGhlIFtsYXllciBjb250cm9sXSgjY29udHJvbC1sYXllcnMpLlxyXG5cdFx0Ly8gQGV2ZW50IG92ZXJsYXlyZW1vdmU6IExheWVyc0NvbnRyb2xFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiBhbiBvdmVybGF5IGlzIGRlc2VsZWN0ZWQgdGhyb3VnaCB0aGUgW2xheWVyIGNvbnRyb2xdKCNjb250cm9sLWxheWVycykuXHJcblx0XHQvLyBAbmFtZXNwYWNlIENvbnRyb2wuTGF5ZXJzXHJcblx0XHR2YXIgdHlwZSA9IG9iai5vdmVybGF5ID9cclxuXHRcdFx0KGUudHlwZSA9PT0gJ2FkZCcgPyAnb3ZlcmxheWFkZCcgOiAnb3ZlcmxheXJlbW92ZScpIDpcclxuXHRcdFx0KGUudHlwZSA9PT0gJ2FkZCcgPyAnYmFzZWxheWVyY2hhbmdlJyA6IG51bGwpO1xyXG5cclxuXHRcdGlmICh0eXBlKSB7XHJcblx0XHRcdHRoaXMuX21hcC5maXJlKHR5cGUsIG9iaik7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gSUU3IGJ1Z3Mgb3V0IGlmIHlvdSBjcmVhdGUgYSByYWRpbyBkeW5hbWljYWxseSwgc28geW91IGhhdmUgdG8gZG8gaXQgdGhpcyBoYWNreSB3YXkgKHNlZSBodHRwOi8vYml0Lmx5L1BxWUxCZSlcclxuXHRfY3JlYXRlUmFkaW9FbGVtZW50OiBmdW5jdGlvbiAobmFtZSwgY2hlY2tlZCkge1xyXG5cclxuXHRcdHZhciByYWRpb0h0bWwgPSAnPGlucHV0IHR5cGU9XCJyYWRpb1wiIGNsYXNzPVwibGVhZmxldC1jb250cm9sLWxheWVycy1zZWxlY3RvclwiIG5hbWU9XCInICtcclxuXHRcdFx0XHRuYW1lICsgJ1wiJyArIChjaGVja2VkID8gJyBjaGVja2VkPVwiY2hlY2tlZFwiJyA6ICcnKSArICcvPic7XHJcblxyXG5cdFx0dmFyIHJhZGlvRnJhZ21lbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcclxuXHRcdHJhZGlvRnJhZ21lbnQuaW5uZXJIVE1MID0gcmFkaW9IdG1sO1xyXG5cclxuXHRcdHJldHVybiByYWRpb0ZyYWdtZW50LmZpcnN0Q2hpbGQ7XHJcblx0fSxcclxuXHJcblx0X2FkZEl0ZW06IGZ1bmN0aW9uIChvYmopIHtcclxuXHRcdHZhciBsYWJlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2xhYmVsJyksXHJcblx0XHQgICAgY2hlY2tlZCA9IHRoaXMuX21hcC5oYXNMYXllcihvYmoubGF5ZXIpLFxyXG5cdFx0ICAgIGlucHV0O1xyXG5cclxuXHRcdGlmIChvYmoub3ZlcmxheSkge1xyXG5cdFx0XHRpbnB1dCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2lucHV0Jyk7XHJcblx0XHRcdGlucHV0LnR5cGUgPSAnY2hlY2tib3gnO1xyXG5cdFx0XHRpbnB1dC5jbGFzc05hbWUgPSAnbGVhZmxldC1jb250cm9sLWxheWVycy1zZWxlY3Rvcic7XHJcblx0XHRcdGlucHV0LmRlZmF1bHRDaGVja2VkID0gY2hlY2tlZDtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdGlucHV0ID0gdGhpcy5fY3JlYXRlUmFkaW9FbGVtZW50KCdsZWFmbGV0LWJhc2UtbGF5ZXJzJywgY2hlY2tlZCk7XHJcblx0XHR9XHJcblxyXG5cdFx0aW5wdXQubGF5ZXJJZCA9IEwuc3RhbXAob2JqLmxheWVyKTtcclxuXHJcblx0XHRMLkRvbUV2ZW50Lm9uKGlucHV0LCAnY2xpY2snLCB0aGlzLl9vbklucHV0Q2xpY2ssIHRoaXMpO1xyXG5cclxuXHRcdHZhciBuYW1lID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3BhbicpO1xyXG5cdFx0bmFtZS5pbm5lckhUTUwgPSAnICcgKyBvYmoubmFtZTtcclxuXHJcblx0XHQvLyBIZWxwcyBmcm9tIHByZXZlbnRpbmcgbGF5ZXIgY29udHJvbCBmbGlja2VyIHdoZW4gY2hlY2tib3hlcyBhcmUgZGlzYWJsZWRcclxuXHRcdC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9MZWFmbGV0L0xlYWZsZXQvaXNzdWVzLzI3NzFcclxuXHRcdHZhciBob2xkZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcclxuXHJcblx0XHRsYWJlbC5hcHBlbmRDaGlsZChob2xkZXIpO1xyXG5cdFx0aG9sZGVyLmFwcGVuZENoaWxkKGlucHV0KTtcclxuXHRcdGhvbGRlci5hcHBlbmRDaGlsZChuYW1lKTtcclxuXHJcblx0XHR2YXIgY29udGFpbmVyID0gb2JqLm92ZXJsYXkgPyB0aGlzLl9vdmVybGF5c0xpc3QgOiB0aGlzLl9iYXNlTGF5ZXJzTGlzdDtcclxuXHRcdGNvbnRhaW5lci5hcHBlbmRDaGlsZChsYWJlbCk7XHJcblxyXG5cdFx0dGhpcy5fY2hlY2tEaXNhYmxlZExheWVycygpO1xyXG5cdFx0cmV0dXJuIGxhYmVsO1xyXG5cdH0sXHJcblxyXG5cdF9vbklucHV0Q2xpY2s6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBpbnB1dHMgPSB0aGlzLl9mb3JtLmdldEVsZW1lbnRzQnlUYWdOYW1lKCdpbnB1dCcpLFxyXG5cdFx0ICAgIGlucHV0LCBsYXllciwgaGFzTGF5ZXI7XHJcblx0XHR2YXIgYWRkZWRMYXllcnMgPSBbXSxcclxuXHRcdCAgICByZW1vdmVkTGF5ZXJzID0gW107XHJcblxyXG5cdFx0dGhpcy5faGFuZGxpbmdDbGljayA9IHRydWU7XHJcblxyXG5cdFx0Zm9yICh2YXIgaSA9IGlucHV0cy5sZW5ndGggLSAxOyBpID49IDA7IGktLSkge1xyXG5cdFx0XHRpbnB1dCA9IGlucHV0c1tpXTtcclxuXHRcdFx0bGF5ZXIgPSB0aGlzLl9nZXRMYXllcihpbnB1dC5sYXllcklkKS5sYXllcjtcclxuXHRcdFx0aGFzTGF5ZXIgPSB0aGlzLl9tYXAuaGFzTGF5ZXIobGF5ZXIpO1xyXG5cclxuXHRcdFx0aWYgKGlucHV0LmNoZWNrZWQgJiYgIWhhc0xheWVyKSB7XHJcblx0XHRcdFx0YWRkZWRMYXllcnMucHVzaChsYXllcik7XHJcblxyXG5cdFx0XHR9IGVsc2UgaWYgKCFpbnB1dC5jaGVja2VkICYmIGhhc0xheWVyKSB7XHJcblx0XHRcdFx0cmVtb3ZlZExheWVycy5wdXNoKGxheWVyKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdC8vIEJ1Z2ZpeCBpc3N1ZSAyMzE4OiBTaG91bGQgcmVtb3ZlIGFsbCBvbGQgbGF5ZXJzIGJlZm9yZSByZWFkZGluZyBuZXcgb25lc1xyXG5cdFx0Zm9yIChpID0gMDsgaSA8IHJlbW92ZWRMYXllcnMubGVuZ3RoOyBpKyspIHtcclxuXHRcdFx0dGhpcy5fbWFwLnJlbW92ZUxheWVyKHJlbW92ZWRMYXllcnNbaV0pO1xyXG5cdFx0fVxyXG5cdFx0Zm9yIChpID0gMDsgaSA8IGFkZGVkTGF5ZXJzLmxlbmd0aDsgaSsrKSB7XHJcblx0XHRcdHRoaXMuX21hcC5hZGRMYXllcihhZGRlZExheWVyc1tpXSk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5faGFuZGxpbmdDbGljayA9IGZhbHNlO1xyXG5cclxuXHRcdHRoaXMuX3JlZm9jdXNPbk1hcCgpO1xyXG5cdH0sXHJcblxyXG5cdF9jaGVja0Rpc2FibGVkTGF5ZXJzOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgaW5wdXRzID0gdGhpcy5fZm9ybS5nZXRFbGVtZW50c0J5VGFnTmFtZSgnaW5wdXQnKSxcclxuXHRcdCAgICBpbnB1dCxcclxuXHRcdCAgICBsYXllcixcclxuXHRcdCAgICB6b29tID0gdGhpcy5fbWFwLmdldFpvb20oKTtcclxuXHJcblx0XHRmb3IgKHZhciBpID0gaW5wdXRzLmxlbmd0aCAtIDE7IGkgPj0gMDsgaS0tKSB7XHJcblx0XHRcdGlucHV0ID0gaW5wdXRzW2ldO1xyXG5cdFx0XHRsYXllciA9IHRoaXMuX2dldExheWVyKGlucHV0LmxheWVySWQpLmxheWVyO1xyXG5cdFx0XHRpbnB1dC5kaXNhYmxlZCA9IChsYXllci5vcHRpb25zLm1pblpvb20gIT09IHVuZGVmaW5lZCAmJiB6b29tIDwgbGF5ZXIub3B0aW9ucy5taW5ab29tKSB8fFxyXG5cdFx0XHQgICAgICAgICAgICAgICAgIChsYXllci5vcHRpb25zLm1heFpvb20gIT09IHVuZGVmaW5lZCAmJiB6b29tID4gbGF5ZXIub3B0aW9ucy5tYXhab29tKTtcclxuXHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2V4cGFuZDogZnVuY3Rpb24gKCkge1xyXG5cdFx0Ly8gQmFja3dhcmQgY29tcGF0aWJpbGl0eSwgcmVtb3ZlIG1lIGluIDEuMS5cclxuXHRcdHJldHVybiB0aGlzLmV4cGFuZCgpO1xyXG5cdH0sXHJcblxyXG5cdF9jb2xsYXBzZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0Ly8gQmFja3dhcmQgY29tcGF0aWJpbGl0eSwgcmVtb3ZlIG1lIGluIDEuMS5cclxuXHRcdHJldHVybiB0aGlzLmNvbGxhcHNlKCk7XHJcblx0fVxyXG5cclxufSk7XHJcblxyXG5cclxuLy8gQGZhY3RvcnkgTC5jb250cm9sLmxheWVycyhiYXNlbGF5ZXJzPzogT2JqZWN0LCBvdmVybGF5cz86IE9iamVjdCwgb3B0aW9ucz86IENvbnRyb2wuTGF5ZXJzIG9wdGlvbnMpXHJcbi8vIENyZWF0ZXMgYW4gYXR0cmlidXRpb24gY29udHJvbCB3aXRoIHRoZSBnaXZlbiBsYXllcnMuIEJhc2UgbGF5ZXJzIHdpbGwgYmUgc3dpdGNoZWQgd2l0aCByYWRpbyBidXR0b25zLCB3aGlsZSBvdmVybGF5cyB3aWxsIGJlIHN3aXRjaGVkIHdpdGggY2hlY2tib3hlcy4gTm90ZSB0aGF0IGFsbCBiYXNlIGxheWVycyBzaG91bGQgYmUgcGFzc2VkIGluIHRoZSBiYXNlIGxheWVycyBvYmplY3QsIGJ1dCBvbmx5IG9uZSBzaG91bGQgYmUgYWRkZWQgdG8gdGhlIG1hcCBkdXJpbmcgbWFwIGluc3RhbnRpYXRpb24uXHJcbkwuY29udHJvbC5sYXllcnMgPSBmdW5jdGlvbiAoYmFzZUxheWVycywgb3ZlcmxheXMsIG9wdGlvbnMpIHtcclxuXHRyZXR1cm4gbmV3IEwuQ29udHJvbC5MYXllcnMoYmFzZUxheWVycywgb3ZlcmxheXMsIG9wdGlvbnMpO1xyXG59O1xyXG5cblxuXG59KHdpbmRvdywgZG9jdW1lbnQpKTtcbi8vIyBzb3VyY2VNYXBwaW5nVVJMPWxlYWZsZXQtc3JjLm1hcCIsIi8qIVxuICogbXVzdGFjaGUuanMgLSBMb2dpYy1sZXNzIHt7bXVzdGFjaGV9fSB0ZW1wbGF0ZXMgd2l0aCBKYXZhU2NyaXB0XG4gKiBodHRwOi8vZ2l0aHViLmNvbS9qYW5sL211c3RhY2hlLmpzXG4gKi9cblxuLypnbG9iYWwgZGVmaW5lOiBmYWxzZSBNdXN0YWNoZTogdHJ1ZSovXG5cbihmdW5jdGlvbiBkZWZpbmVNdXN0YWNoZSAoZ2xvYmFsLCBmYWN0b3J5KSB7XG4gIGlmICh0eXBlb2YgZXhwb3J0cyA9PT0gJ29iamVjdCcgJiYgZXhwb3J0cyAmJiB0eXBlb2YgZXhwb3J0cy5ub2RlTmFtZSAhPT0gJ3N0cmluZycpIHtcbiAgICBmYWN0b3J5KGV4cG9ydHMpOyAvLyBDb21tb25KU1xuICB9IGVsc2UgaWYgKHR5cGVvZiBkZWZpbmUgPT09ICdmdW5jdGlvbicgJiYgZGVmaW5lLmFtZCkge1xuICAgIGRlZmluZShbJ2V4cG9ydHMnXSwgZmFjdG9yeSk7IC8vIEFNRFxuICB9IGVsc2Uge1xuICAgIGdsb2JhbC5NdXN0YWNoZSA9IHt9O1xuICAgIGZhY3RvcnkoZ2xvYmFsLk11c3RhY2hlKTsgLy8gc2NyaXB0LCB3c2gsIGFzcFxuICB9XG59KHRoaXMsIGZ1bmN0aW9uIG11c3RhY2hlRmFjdG9yeSAobXVzdGFjaGUpIHtcblxuICB2YXIgb2JqZWN0VG9TdHJpbmcgPSBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nO1xuICB2YXIgaXNBcnJheSA9IEFycmF5LmlzQXJyYXkgfHwgZnVuY3Rpb24gaXNBcnJheVBvbHlmaWxsIChvYmplY3QpIHtcbiAgICByZXR1cm4gb2JqZWN0VG9TdHJpbmcuY2FsbChvYmplY3QpID09PSAnW29iamVjdCBBcnJheV0nO1xuICB9O1xuXG4gIGZ1bmN0aW9uIGlzRnVuY3Rpb24gKG9iamVjdCkge1xuICAgIHJldHVybiB0eXBlb2Ygb2JqZWN0ID09PSAnZnVuY3Rpb24nO1xuICB9XG5cbiAgLyoqXG4gICAqIE1vcmUgY29ycmVjdCB0eXBlb2Ygc3RyaW5nIGhhbmRsaW5nIGFycmF5XG4gICAqIHdoaWNoIG5vcm1hbGx5IHJldHVybnMgdHlwZW9mICdvYmplY3QnXG4gICAqL1xuICBmdW5jdGlvbiB0eXBlU3RyIChvYmopIHtcbiAgICByZXR1cm4gaXNBcnJheShvYmopID8gJ2FycmF5JyA6IHR5cGVvZiBvYmo7XG4gIH1cblxuICBmdW5jdGlvbiBlc2NhcGVSZWdFeHAgKHN0cmluZykge1xuICAgIHJldHVybiBzdHJpbmcucmVwbGFjZSgvW1xcLVxcW1xcXXt9KCkqKz8uLFxcXFxcXF4kfCNcXHNdL2csICdcXFxcJCYnKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBOdWxsIHNhZmUgd2F5IG9mIGNoZWNraW5nIHdoZXRoZXIgb3Igbm90IGFuIG9iamVjdCxcbiAgICogaW5jbHVkaW5nIGl0cyBwcm90b3R5cGUsIGhhcyBhIGdpdmVuIHByb3BlcnR5XG4gICAqL1xuICBmdW5jdGlvbiBoYXNQcm9wZXJ0eSAob2JqLCBwcm9wTmFtZSkge1xuICAgIHJldHVybiBvYmogIT0gbnVsbCAmJiB0eXBlb2Ygb2JqID09PSAnb2JqZWN0JyAmJiAocHJvcE5hbWUgaW4gb2JqKTtcbiAgfVxuXG4gIC8vIFdvcmthcm91bmQgZm9yIGh0dHBzOi8vaXNzdWVzLmFwYWNoZS5vcmcvamlyYS9icm93c2UvQ09VQ0hEQi01NzdcbiAgLy8gU2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9qYW5sL211c3RhY2hlLmpzL2lzc3Vlcy8xODlcbiAgdmFyIHJlZ0V4cFRlc3QgPSBSZWdFeHAucHJvdG90eXBlLnRlc3Q7XG4gIGZ1bmN0aW9uIHRlc3RSZWdFeHAgKHJlLCBzdHJpbmcpIHtcbiAgICByZXR1cm4gcmVnRXhwVGVzdC5jYWxsKHJlLCBzdHJpbmcpO1xuICB9XG5cbiAgdmFyIG5vblNwYWNlUmUgPSAvXFxTLztcbiAgZnVuY3Rpb24gaXNXaGl0ZXNwYWNlIChzdHJpbmcpIHtcbiAgICByZXR1cm4gIXRlc3RSZWdFeHAobm9uU3BhY2VSZSwgc3RyaW5nKTtcbiAgfVxuXG4gIHZhciBlbnRpdHlNYXAgPSB7XG4gICAgJyYnOiAnJmFtcDsnLFxuICAgICc8JzogJyZsdDsnLFxuICAgICc+JzogJyZndDsnLFxuICAgICdcIic6ICcmcXVvdDsnLFxuICAgIFwiJ1wiOiAnJiMzOTsnLFxuICAgICcvJzogJyYjeDJGOycsXG4gICAgJ2AnOiAnJiN4NjA7JyxcbiAgICAnPSc6ICcmI3gzRDsnXG4gIH07XG5cbiAgZnVuY3Rpb24gZXNjYXBlSHRtbCAoc3RyaW5nKSB7XG4gICAgcmV0dXJuIFN0cmluZyhzdHJpbmcpLnJlcGxhY2UoL1smPD5cIidgPVxcL10vZywgZnVuY3Rpb24gZnJvbUVudGl0eU1hcCAocykge1xuICAgICAgcmV0dXJuIGVudGl0eU1hcFtzXTtcbiAgICB9KTtcbiAgfVxuXG4gIHZhciB3aGl0ZVJlID0gL1xccyovO1xuICB2YXIgc3BhY2VSZSA9IC9cXHMrLztcbiAgdmFyIGVxdWFsc1JlID0gL1xccyo9LztcbiAgdmFyIGN1cmx5UmUgPSAvXFxzKlxcfS87XG4gIHZhciB0YWdSZSA9IC8jfFxcXnxcXC98PnxcXHt8Jnw9fCEvO1xuXG4gIC8qKlxuICAgKiBCcmVha3MgdXAgdGhlIGdpdmVuIGB0ZW1wbGF0ZWAgc3RyaW5nIGludG8gYSB0cmVlIG9mIHRva2Vucy4gSWYgdGhlIGB0YWdzYFxuICAgKiBhcmd1bWVudCBpcyBnaXZlbiBoZXJlIGl0IG11c3QgYmUgYW4gYXJyYXkgd2l0aCB0d28gc3RyaW5nIHZhbHVlczogdGhlXG4gICAqIG9wZW5pbmcgYW5kIGNsb3NpbmcgdGFncyB1c2VkIGluIHRoZSB0ZW1wbGF0ZSAoZS5nLiBbIFwiPCVcIiwgXCIlPlwiIF0pLiBPZlxuICAgKiBjb3Vyc2UsIHRoZSBkZWZhdWx0IGlzIHRvIHVzZSBtdXN0YWNoZXMgKGkuZS4gbXVzdGFjaGUudGFncykuXG4gICAqXG4gICAqIEEgdG9rZW4gaXMgYW4gYXJyYXkgd2l0aCBhdCBsZWFzdCA0IGVsZW1lbnRzLiBUaGUgZmlyc3QgZWxlbWVudCBpcyB0aGVcbiAgICogbXVzdGFjaGUgc3ltYm9sIHRoYXQgd2FzIHVzZWQgaW5zaWRlIHRoZSB0YWcsIGUuZy4gXCIjXCIgb3IgXCImXCIuIElmIHRoZSB0YWdcbiAgICogZGlkIG5vdCBjb250YWluIGEgc3ltYm9sIChpLmUuIHt7bXlWYWx1ZX19KSB0aGlzIGVsZW1lbnQgaXMgXCJuYW1lXCIuIEZvclxuICAgKiBhbGwgdGV4dCB0aGF0IGFwcGVhcnMgb3V0c2lkZSBhIHN5bWJvbCB0aGlzIGVsZW1lbnQgaXMgXCJ0ZXh0XCIuXG4gICAqXG4gICAqIFRoZSBzZWNvbmQgZWxlbWVudCBvZiBhIHRva2VuIGlzIGl0cyBcInZhbHVlXCIuIEZvciBtdXN0YWNoZSB0YWdzIHRoaXMgaXNcbiAgICogd2hhdGV2ZXIgZWxzZSB3YXMgaW5zaWRlIHRoZSB0YWcgYmVzaWRlcyB0aGUgb3BlbmluZyBzeW1ib2wuIEZvciB0ZXh0IHRva2Vuc1xuICAgKiB0aGlzIGlzIHRoZSB0ZXh0IGl0c2VsZi5cbiAgICpcbiAgICogVGhlIHRoaXJkIGFuZCBmb3VydGggZWxlbWVudHMgb2YgdGhlIHRva2VuIGFyZSB0aGUgc3RhcnQgYW5kIGVuZCBpbmRpY2VzLFxuICAgKiByZXNwZWN0aXZlbHksIG9mIHRoZSB0b2tlbiBpbiB0aGUgb3JpZ2luYWwgdGVtcGxhdGUuXG4gICAqXG4gICAqIFRva2VucyB0aGF0IGFyZSB0aGUgcm9vdCBub2RlIG9mIGEgc3VidHJlZSBjb250YWluIHR3byBtb3JlIGVsZW1lbnRzOiAxKSBhblxuICAgKiBhcnJheSBvZiB0b2tlbnMgaW4gdGhlIHN1YnRyZWUgYW5kIDIpIHRoZSBpbmRleCBpbiB0aGUgb3JpZ2luYWwgdGVtcGxhdGUgYXRcbiAgICogd2hpY2ggdGhlIGNsb3NpbmcgdGFnIGZvciB0aGF0IHNlY3Rpb24gYmVnaW5zLlxuICAgKi9cbiAgZnVuY3Rpb24gcGFyc2VUZW1wbGF0ZSAodGVtcGxhdGUsIHRhZ3MpIHtcbiAgICBpZiAoIXRlbXBsYXRlKVxuICAgICAgcmV0dXJuIFtdO1xuXG4gICAgdmFyIHNlY3Rpb25zID0gW107ICAgICAvLyBTdGFjayB0byBob2xkIHNlY3Rpb24gdG9rZW5zXG4gICAgdmFyIHRva2VucyA9IFtdOyAgICAgICAvLyBCdWZmZXIgdG8gaG9sZCB0aGUgdG9rZW5zXG4gICAgdmFyIHNwYWNlcyA9IFtdOyAgICAgICAvLyBJbmRpY2VzIG9mIHdoaXRlc3BhY2UgdG9rZW5zIG9uIHRoZSBjdXJyZW50IGxpbmVcbiAgICB2YXIgaGFzVGFnID0gZmFsc2U7ICAgIC8vIElzIHRoZXJlIGEge3t0YWd9fSBvbiB0aGUgY3VycmVudCBsaW5lP1xuICAgIHZhciBub25TcGFjZSA9IGZhbHNlOyAgLy8gSXMgdGhlcmUgYSBub24tc3BhY2UgY2hhciBvbiB0aGUgY3VycmVudCBsaW5lP1xuXG4gICAgLy8gU3RyaXBzIGFsbCB3aGl0ZXNwYWNlIHRva2VucyBhcnJheSBmb3IgdGhlIGN1cnJlbnQgbGluZVxuICAgIC8vIGlmIHRoZXJlIHdhcyBhIHt7I3RhZ319IG9uIGl0IGFuZCBvdGhlcndpc2Ugb25seSBzcGFjZS5cbiAgICBmdW5jdGlvbiBzdHJpcFNwYWNlICgpIHtcbiAgICAgIGlmIChoYXNUYWcgJiYgIW5vblNwYWNlKSB7XG4gICAgICAgIHdoaWxlIChzcGFjZXMubGVuZ3RoKVxuICAgICAgICAgIGRlbGV0ZSB0b2tlbnNbc3BhY2VzLnBvcCgpXTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHNwYWNlcyA9IFtdO1xuICAgICAgfVxuXG4gICAgICBoYXNUYWcgPSBmYWxzZTtcbiAgICAgIG5vblNwYWNlID0gZmFsc2U7XG4gICAgfVxuXG4gICAgdmFyIG9wZW5pbmdUYWdSZSwgY2xvc2luZ1RhZ1JlLCBjbG9zaW5nQ3VybHlSZTtcbiAgICBmdW5jdGlvbiBjb21waWxlVGFncyAodGFnc1RvQ29tcGlsZSkge1xuICAgICAgaWYgKHR5cGVvZiB0YWdzVG9Db21waWxlID09PSAnc3RyaW5nJylcbiAgICAgICAgdGFnc1RvQ29tcGlsZSA9IHRhZ3NUb0NvbXBpbGUuc3BsaXQoc3BhY2VSZSwgMik7XG5cbiAgICAgIGlmICghaXNBcnJheSh0YWdzVG9Db21waWxlKSB8fCB0YWdzVG9Db21waWxlLmxlbmd0aCAhPT0gMilcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIHRhZ3M6ICcgKyB0YWdzVG9Db21waWxlKTtcblxuICAgICAgb3BlbmluZ1RhZ1JlID0gbmV3IFJlZ0V4cChlc2NhcGVSZWdFeHAodGFnc1RvQ29tcGlsZVswXSkgKyAnXFxcXHMqJyk7XG4gICAgICBjbG9zaW5nVGFnUmUgPSBuZXcgUmVnRXhwKCdcXFxccyonICsgZXNjYXBlUmVnRXhwKHRhZ3NUb0NvbXBpbGVbMV0pKTtcbiAgICAgIGNsb3NpbmdDdXJseVJlID0gbmV3IFJlZ0V4cCgnXFxcXHMqJyArIGVzY2FwZVJlZ0V4cCgnfScgKyB0YWdzVG9Db21waWxlWzFdKSk7XG4gICAgfVxuXG4gICAgY29tcGlsZVRhZ3ModGFncyB8fCBtdXN0YWNoZS50YWdzKTtcblxuICAgIHZhciBzY2FubmVyID0gbmV3IFNjYW5uZXIodGVtcGxhdGUpO1xuXG4gICAgdmFyIHN0YXJ0LCB0eXBlLCB2YWx1ZSwgY2hyLCB0b2tlbiwgb3BlblNlY3Rpb247XG4gICAgd2hpbGUgKCFzY2FubmVyLmVvcygpKSB7XG4gICAgICBzdGFydCA9IHNjYW5uZXIucG9zO1xuXG4gICAgICAvLyBNYXRjaCBhbnkgdGV4dCBiZXR3ZWVuIHRhZ3MuXG4gICAgICB2YWx1ZSA9IHNjYW5uZXIuc2NhblVudGlsKG9wZW5pbmdUYWdSZSk7XG5cbiAgICAgIGlmICh2YWx1ZSkge1xuICAgICAgICBmb3IgKHZhciBpID0gMCwgdmFsdWVMZW5ndGggPSB2YWx1ZS5sZW5ndGg7IGkgPCB2YWx1ZUxlbmd0aDsgKytpKSB7XG4gICAgICAgICAgY2hyID0gdmFsdWUuY2hhckF0KGkpO1xuXG4gICAgICAgICAgaWYgKGlzV2hpdGVzcGFjZShjaHIpKSB7XG4gICAgICAgICAgICBzcGFjZXMucHVzaCh0b2tlbnMubGVuZ3RoKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgbm9uU3BhY2UgPSB0cnVlO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHRva2Vucy5wdXNoKFsgJ3RleHQnLCBjaHIsIHN0YXJ0LCBzdGFydCArIDEgXSk7XG4gICAgICAgICAgc3RhcnQgKz0gMTtcblxuICAgICAgICAgIC8vIENoZWNrIGZvciB3aGl0ZXNwYWNlIG9uIHRoZSBjdXJyZW50IGxpbmUuXG4gICAgICAgICAgaWYgKGNociA9PT0gJ1xcbicpXG4gICAgICAgICAgICBzdHJpcFNwYWNlKCk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gTWF0Y2ggdGhlIG9wZW5pbmcgdGFnLlxuICAgICAgaWYgKCFzY2FubmVyLnNjYW4ob3BlbmluZ1RhZ1JlKSlcbiAgICAgICAgYnJlYWs7XG5cbiAgICAgIGhhc1RhZyA9IHRydWU7XG5cbiAgICAgIC8vIEdldCB0aGUgdGFnIHR5cGUuXG4gICAgICB0eXBlID0gc2Nhbm5lci5zY2FuKHRhZ1JlKSB8fCAnbmFtZSc7XG4gICAgICBzY2FubmVyLnNjYW4od2hpdGVSZSk7XG5cbiAgICAgIC8vIEdldCB0aGUgdGFnIHZhbHVlLlxuICAgICAgaWYgKHR5cGUgPT09ICc9Jykge1xuICAgICAgICB2YWx1ZSA9IHNjYW5uZXIuc2NhblVudGlsKGVxdWFsc1JlKTtcbiAgICAgICAgc2Nhbm5lci5zY2FuKGVxdWFsc1JlKTtcbiAgICAgICAgc2Nhbm5lci5zY2FuVW50aWwoY2xvc2luZ1RhZ1JlKTtcbiAgICAgIH0gZWxzZSBpZiAodHlwZSA9PT0gJ3snKSB7XG4gICAgICAgIHZhbHVlID0gc2Nhbm5lci5zY2FuVW50aWwoY2xvc2luZ0N1cmx5UmUpO1xuICAgICAgICBzY2FubmVyLnNjYW4oY3VybHlSZSk7XG4gICAgICAgIHNjYW5uZXIuc2NhblVudGlsKGNsb3NpbmdUYWdSZSk7XG4gICAgICAgIHR5cGUgPSAnJic7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB2YWx1ZSA9IHNjYW5uZXIuc2NhblVudGlsKGNsb3NpbmdUYWdSZSk7XG4gICAgICB9XG5cbiAgICAgIC8vIE1hdGNoIHRoZSBjbG9zaW5nIHRhZy5cbiAgICAgIGlmICghc2Nhbm5lci5zY2FuKGNsb3NpbmdUYWdSZSkpXG4gICAgICAgIHRocm93IG5ldyBFcnJvcignVW5jbG9zZWQgdGFnIGF0ICcgKyBzY2FubmVyLnBvcyk7XG5cbiAgICAgIHRva2VuID0gWyB0eXBlLCB2YWx1ZSwgc3RhcnQsIHNjYW5uZXIucG9zIF07XG4gICAgICB0b2tlbnMucHVzaCh0b2tlbik7XG5cbiAgICAgIGlmICh0eXBlID09PSAnIycgfHwgdHlwZSA9PT0gJ14nKSB7XG4gICAgICAgIHNlY3Rpb25zLnB1c2godG9rZW4pO1xuICAgICAgfSBlbHNlIGlmICh0eXBlID09PSAnLycpIHtcbiAgICAgICAgLy8gQ2hlY2sgc2VjdGlvbiBuZXN0aW5nLlxuICAgICAgICBvcGVuU2VjdGlvbiA9IHNlY3Rpb25zLnBvcCgpO1xuXG4gICAgICAgIGlmICghb3BlblNlY3Rpb24pXG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdVbm9wZW5lZCBzZWN0aW9uIFwiJyArIHZhbHVlICsgJ1wiIGF0ICcgKyBzdGFydCk7XG5cbiAgICAgICAgaWYgKG9wZW5TZWN0aW9uWzFdICE9PSB2YWx1ZSlcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1VuY2xvc2VkIHNlY3Rpb24gXCInICsgb3BlblNlY3Rpb25bMV0gKyAnXCIgYXQgJyArIHN0YXJ0KTtcbiAgICAgIH0gZWxzZSBpZiAodHlwZSA9PT0gJ25hbWUnIHx8IHR5cGUgPT09ICd7JyB8fCB0eXBlID09PSAnJicpIHtcbiAgICAgICAgbm9uU3BhY2UgPSB0cnVlO1xuICAgICAgfSBlbHNlIGlmICh0eXBlID09PSAnPScpIHtcbiAgICAgICAgLy8gU2V0IHRoZSB0YWdzIGZvciB0aGUgbmV4dCB0aW1lIGFyb3VuZC5cbiAgICAgICAgY29tcGlsZVRhZ3ModmFsdWUpO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIE1ha2Ugc3VyZSB0aGVyZSBhcmUgbm8gb3BlbiBzZWN0aW9ucyB3aGVuIHdlJ3JlIGRvbmUuXG4gICAgb3BlblNlY3Rpb24gPSBzZWN0aW9ucy5wb3AoKTtcblxuICAgIGlmIChvcGVuU2VjdGlvbilcbiAgICAgIHRocm93IG5ldyBFcnJvcignVW5jbG9zZWQgc2VjdGlvbiBcIicgKyBvcGVuU2VjdGlvblsxXSArICdcIiBhdCAnICsgc2Nhbm5lci5wb3MpO1xuXG4gICAgcmV0dXJuIG5lc3RUb2tlbnMoc3F1YXNoVG9rZW5zKHRva2VucykpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbWJpbmVzIHRoZSB2YWx1ZXMgb2YgY29uc2VjdXRpdmUgdGV4dCB0b2tlbnMgaW4gdGhlIGdpdmVuIGB0b2tlbnNgIGFycmF5XG4gICAqIHRvIGEgc2luZ2xlIHRva2VuLlxuICAgKi9cbiAgZnVuY3Rpb24gc3F1YXNoVG9rZW5zICh0b2tlbnMpIHtcbiAgICB2YXIgc3F1YXNoZWRUb2tlbnMgPSBbXTtcblxuICAgIHZhciB0b2tlbiwgbGFzdFRva2VuO1xuICAgIGZvciAodmFyIGkgPSAwLCBudW1Ub2tlbnMgPSB0b2tlbnMubGVuZ3RoOyBpIDwgbnVtVG9rZW5zOyArK2kpIHtcbiAgICAgIHRva2VuID0gdG9rZW5zW2ldO1xuXG4gICAgICBpZiAodG9rZW4pIHtcbiAgICAgICAgaWYgKHRva2VuWzBdID09PSAndGV4dCcgJiYgbGFzdFRva2VuICYmIGxhc3RUb2tlblswXSA9PT0gJ3RleHQnKSB7XG4gICAgICAgICAgbGFzdFRva2VuWzFdICs9IHRva2VuWzFdO1xuICAgICAgICAgIGxhc3RUb2tlblszXSA9IHRva2VuWzNdO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHNxdWFzaGVkVG9rZW5zLnB1c2godG9rZW4pO1xuICAgICAgICAgIGxhc3RUb2tlbiA9IHRva2VuO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHNxdWFzaGVkVG9rZW5zO1xuICB9XG5cbiAgLyoqXG4gICAqIEZvcm1zIHRoZSBnaXZlbiBhcnJheSBvZiBgdG9rZW5zYCBpbnRvIGEgbmVzdGVkIHRyZWUgc3RydWN0dXJlIHdoZXJlXG4gICAqIHRva2VucyB0aGF0IHJlcHJlc2VudCBhIHNlY3Rpb24gaGF2ZSB0d28gYWRkaXRpb25hbCBpdGVtczogMSkgYW4gYXJyYXkgb2ZcbiAgICogYWxsIHRva2VucyB0aGF0IGFwcGVhciBpbiB0aGF0IHNlY3Rpb24gYW5kIDIpIHRoZSBpbmRleCBpbiB0aGUgb3JpZ2luYWxcbiAgICogdGVtcGxhdGUgdGhhdCByZXByZXNlbnRzIHRoZSBlbmQgb2YgdGhhdCBzZWN0aW9uLlxuICAgKi9cbiAgZnVuY3Rpb24gbmVzdFRva2VucyAodG9rZW5zKSB7XG4gICAgdmFyIG5lc3RlZFRva2VucyA9IFtdO1xuICAgIHZhciBjb2xsZWN0b3IgPSBuZXN0ZWRUb2tlbnM7XG4gICAgdmFyIHNlY3Rpb25zID0gW107XG5cbiAgICB2YXIgdG9rZW4sIHNlY3Rpb247XG4gICAgZm9yICh2YXIgaSA9IDAsIG51bVRva2VucyA9IHRva2Vucy5sZW5ndGg7IGkgPCBudW1Ub2tlbnM7ICsraSkge1xuICAgICAgdG9rZW4gPSB0b2tlbnNbaV07XG5cbiAgICAgIHN3aXRjaCAodG9rZW5bMF0pIHtcbiAgICAgICAgY2FzZSAnIyc6XG4gICAgICAgIGNhc2UgJ14nOlxuICAgICAgICAgIGNvbGxlY3Rvci5wdXNoKHRva2VuKTtcbiAgICAgICAgICBzZWN0aW9ucy5wdXNoKHRva2VuKTtcbiAgICAgICAgICBjb2xsZWN0b3IgPSB0b2tlbls0XSA9IFtdO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICcvJzpcbiAgICAgICAgICBzZWN0aW9uID0gc2VjdGlvbnMucG9wKCk7XG4gICAgICAgICAgc2VjdGlvbls1XSA9IHRva2VuWzJdO1xuICAgICAgICAgIGNvbGxlY3RvciA9IHNlY3Rpb25zLmxlbmd0aCA+IDAgPyBzZWN0aW9uc1tzZWN0aW9ucy5sZW5ndGggLSAxXVs0XSA6IG5lc3RlZFRva2VucztcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICBjb2xsZWN0b3IucHVzaCh0b2tlbik7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIG5lc3RlZFRva2VucztcbiAgfVxuXG4gIC8qKlxuICAgKiBBIHNpbXBsZSBzdHJpbmcgc2Nhbm5lciB0aGF0IGlzIHVzZWQgYnkgdGhlIHRlbXBsYXRlIHBhcnNlciB0byBmaW5kXG4gICAqIHRva2VucyBpbiB0ZW1wbGF0ZSBzdHJpbmdzLlxuICAgKi9cbiAgZnVuY3Rpb24gU2Nhbm5lciAoc3RyaW5nKSB7XG4gICAgdGhpcy5zdHJpbmcgPSBzdHJpbmc7XG4gICAgdGhpcy50YWlsID0gc3RyaW5nO1xuICAgIHRoaXMucG9zID0gMDtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgdGFpbCBpcyBlbXB0eSAoZW5kIG9mIHN0cmluZykuXG4gICAqL1xuICBTY2FubmVyLnByb3RvdHlwZS5lb3MgPSBmdW5jdGlvbiBlb3MgKCkge1xuICAgIHJldHVybiB0aGlzLnRhaWwgPT09ICcnO1xuICB9O1xuXG4gIC8qKlxuICAgKiBUcmllcyB0byBtYXRjaCB0aGUgZ2l2ZW4gcmVndWxhciBleHByZXNzaW9uIGF0IHRoZSBjdXJyZW50IHBvc2l0aW9uLlxuICAgKiBSZXR1cm5zIHRoZSBtYXRjaGVkIHRleHQgaWYgaXQgY2FuIG1hdGNoLCB0aGUgZW1wdHkgc3RyaW5nIG90aGVyd2lzZS5cbiAgICovXG4gIFNjYW5uZXIucHJvdG90eXBlLnNjYW4gPSBmdW5jdGlvbiBzY2FuIChyZSkge1xuICAgIHZhciBtYXRjaCA9IHRoaXMudGFpbC5tYXRjaChyZSk7XG5cbiAgICBpZiAoIW1hdGNoIHx8IG1hdGNoLmluZGV4ICE9PSAwKVxuICAgICAgcmV0dXJuICcnO1xuXG4gICAgdmFyIHN0cmluZyA9IG1hdGNoWzBdO1xuXG4gICAgdGhpcy50YWlsID0gdGhpcy50YWlsLnN1YnN0cmluZyhzdHJpbmcubGVuZ3RoKTtcbiAgICB0aGlzLnBvcyArPSBzdHJpbmcubGVuZ3RoO1xuXG4gICAgcmV0dXJuIHN0cmluZztcbiAgfTtcblxuICAvKipcbiAgICogU2tpcHMgYWxsIHRleHQgdW50aWwgdGhlIGdpdmVuIHJlZ3VsYXIgZXhwcmVzc2lvbiBjYW4gYmUgbWF0Y2hlZC4gUmV0dXJuc1xuICAgKiB0aGUgc2tpcHBlZCBzdHJpbmcsIHdoaWNoIGlzIHRoZSBlbnRpcmUgdGFpbCBpZiBubyBtYXRjaCBjYW4gYmUgbWFkZS5cbiAgICovXG4gIFNjYW5uZXIucHJvdG90eXBlLnNjYW5VbnRpbCA9IGZ1bmN0aW9uIHNjYW5VbnRpbCAocmUpIHtcbiAgICB2YXIgaW5kZXggPSB0aGlzLnRhaWwuc2VhcmNoKHJlKSwgbWF0Y2g7XG5cbiAgICBzd2l0Y2ggKGluZGV4KSB7XG4gICAgICBjYXNlIC0xOlxuICAgICAgICBtYXRjaCA9IHRoaXMudGFpbDtcbiAgICAgICAgdGhpcy50YWlsID0gJyc7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAwOlxuICAgICAgICBtYXRjaCA9ICcnO1xuICAgICAgICBicmVhaztcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIG1hdGNoID0gdGhpcy50YWlsLnN1YnN0cmluZygwLCBpbmRleCk7XG4gICAgICAgIHRoaXMudGFpbCA9IHRoaXMudGFpbC5zdWJzdHJpbmcoaW5kZXgpO1xuICAgIH1cblxuICAgIHRoaXMucG9zICs9IG1hdGNoLmxlbmd0aDtcblxuICAgIHJldHVybiBtYXRjaDtcbiAgfTtcblxuICAvKipcbiAgICogUmVwcmVzZW50cyBhIHJlbmRlcmluZyBjb250ZXh0IGJ5IHdyYXBwaW5nIGEgdmlldyBvYmplY3QgYW5kXG4gICAqIG1haW50YWluaW5nIGEgcmVmZXJlbmNlIHRvIHRoZSBwYXJlbnQgY29udGV4dC5cbiAgICovXG4gIGZ1bmN0aW9uIENvbnRleHQgKHZpZXcsIHBhcmVudENvbnRleHQpIHtcbiAgICB0aGlzLnZpZXcgPSB2aWV3O1xuICAgIHRoaXMuY2FjaGUgPSB7ICcuJzogdGhpcy52aWV3IH07XG4gICAgdGhpcy5wYXJlbnQgPSBwYXJlbnRDb250ZXh0O1xuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYSBuZXcgY29udGV4dCB1c2luZyB0aGUgZ2l2ZW4gdmlldyB3aXRoIHRoaXMgY29udGV4dFxuICAgKiBhcyB0aGUgcGFyZW50LlxuICAgKi9cbiAgQ29udGV4dC5wcm90b3R5cGUucHVzaCA9IGZ1bmN0aW9uIHB1c2ggKHZpZXcpIHtcbiAgICByZXR1cm4gbmV3IENvbnRleHQodmlldywgdGhpcyk7XG4gIH07XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdGhlIHZhbHVlIG9mIHRoZSBnaXZlbiBuYW1lIGluIHRoaXMgY29udGV4dCwgdHJhdmVyc2luZ1xuICAgKiB1cCB0aGUgY29udGV4dCBoaWVyYXJjaHkgaWYgdGhlIHZhbHVlIGlzIGFic2VudCBpbiB0aGlzIGNvbnRleHQncyB2aWV3LlxuICAgKi9cbiAgQ29udGV4dC5wcm90b3R5cGUubG9va3VwID0gZnVuY3Rpb24gbG9va3VwIChuYW1lKSB7XG4gICAgdmFyIGNhY2hlID0gdGhpcy5jYWNoZTtcblxuICAgIHZhciB2YWx1ZTtcbiAgICBpZiAoY2FjaGUuaGFzT3duUHJvcGVydHkobmFtZSkpIHtcbiAgICAgIHZhbHVlID0gY2FjaGVbbmFtZV07XG4gICAgfSBlbHNlIHtcbiAgICAgIHZhciBjb250ZXh0ID0gdGhpcywgbmFtZXMsIGluZGV4LCBsb29rdXBIaXQgPSBmYWxzZTtcblxuICAgICAgd2hpbGUgKGNvbnRleHQpIHtcbiAgICAgICAgaWYgKG5hbWUuaW5kZXhPZignLicpID4gMCkge1xuICAgICAgICAgIHZhbHVlID0gY29udGV4dC52aWV3O1xuICAgICAgICAgIG5hbWVzID0gbmFtZS5zcGxpdCgnLicpO1xuICAgICAgICAgIGluZGV4ID0gMDtcblxuICAgICAgICAgIC8qKlxuICAgICAgICAgICAqIFVzaW5nIHRoZSBkb3Qgbm90aW9uIHBhdGggaW4gYG5hbWVgLCB3ZSBkZXNjZW5kIHRocm91Z2ggdGhlXG4gICAgICAgICAgICogbmVzdGVkIG9iamVjdHMuXG4gICAgICAgICAgICpcbiAgICAgICAgICAgKiBUbyBiZSBjZXJ0YWluIHRoYXQgdGhlIGxvb2t1cCBoYXMgYmVlbiBzdWNjZXNzZnVsLCB3ZSBoYXZlIHRvXG4gICAgICAgICAgICogY2hlY2sgaWYgdGhlIGxhc3Qgb2JqZWN0IGluIHRoZSBwYXRoIGFjdHVhbGx5IGhhcyB0aGUgcHJvcGVydHlcbiAgICAgICAgICAgKiB3ZSBhcmUgbG9va2luZyBmb3IuIFdlIHN0b3JlIHRoZSByZXN1bHQgaW4gYGxvb2t1cEhpdGAuXG4gICAgICAgICAgICpcbiAgICAgICAgICAgKiBUaGlzIGlzIHNwZWNpYWxseSBuZWNlc3NhcnkgZm9yIHdoZW4gdGhlIHZhbHVlIGhhcyBiZWVuIHNldCB0b1xuICAgICAgICAgICAqIGB1bmRlZmluZWRgIGFuZCB3ZSB3YW50IHRvIGF2b2lkIGxvb2tpbmcgdXAgcGFyZW50IGNvbnRleHRzLlxuICAgICAgICAgICAqKi9cbiAgICAgICAgICB3aGlsZSAodmFsdWUgIT0gbnVsbCAmJiBpbmRleCA8IG5hbWVzLmxlbmd0aCkge1xuICAgICAgICAgICAgaWYgKGluZGV4ID09PSBuYW1lcy5sZW5ndGggLSAxKVxuICAgICAgICAgICAgICBsb29rdXBIaXQgPSBoYXNQcm9wZXJ0eSh2YWx1ZSwgbmFtZXNbaW5kZXhdKTtcblxuICAgICAgICAgICAgdmFsdWUgPSB2YWx1ZVtuYW1lc1tpbmRleCsrXV07XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHZhbHVlID0gY29udGV4dC52aWV3W25hbWVdO1xuICAgICAgICAgIGxvb2t1cEhpdCA9IGhhc1Byb3BlcnR5KGNvbnRleHQudmlldywgbmFtZSk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAobG9va3VwSGl0KVxuICAgICAgICAgIGJyZWFrO1xuXG4gICAgICAgIGNvbnRleHQgPSBjb250ZXh0LnBhcmVudDtcbiAgICAgIH1cblxuICAgICAgY2FjaGVbbmFtZV0gPSB2YWx1ZTtcbiAgICB9XG5cbiAgICBpZiAoaXNGdW5jdGlvbih2YWx1ZSkpXG4gICAgICB2YWx1ZSA9IHZhbHVlLmNhbGwodGhpcy52aWV3KTtcblxuICAgIHJldHVybiB2YWx1ZTtcbiAgfTtcblxuICAvKipcbiAgICogQSBXcml0ZXIga25vd3MgaG93IHRvIHRha2UgYSBzdHJlYW0gb2YgdG9rZW5zIGFuZCByZW5kZXIgdGhlbSB0byBhXG4gICAqIHN0cmluZywgZ2l2ZW4gYSBjb250ZXh0LiBJdCBhbHNvIG1haW50YWlucyBhIGNhY2hlIG9mIHRlbXBsYXRlcyB0b1xuICAgKiBhdm9pZCB0aGUgbmVlZCB0byBwYXJzZSB0aGUgc2FtZSB0ZW1wbGF0ZSB0d2ljZS5cbiAgICovXG4gIGZ1bmN0aW9uIFdyaXRlciAoKSB7XG4gICAgdGhpcy5jYWNoZSA9IHt9O1xuICB9XG5cbiAgLyoqXG4gICAqIENsZWFycyBhbGwgY2FjaGVkIHRlbXBsYXRlcyBpbiB0aGlzIHdyaXRlci5cbiAgICovXG4gIFdyaXRlci5wcm90b3R5cGUuY2xlYXJDYWNoZSA9IGZ1bmN0aW9uIGNsZWFyQ2FjaGUgKCkge1xuICAgIHRoaXMuY2FjaGUgPSB7fTtcbiAgfTtcblxuICAvKipcbiAgICogUGFyc2VzIGFuZCBjYWNoZXMgdGhlIGdpdmVuIGB0ZW1wbGF0ZWAgYW5kIHJldHVybnMgdGhlIGFycmF5IG9mIHRva2Vuc1xuICAgKiB0aGF0IGlzIGdlbmVyYXRlZCBmcm9tIHRoZSBwYXJzZS5cbiAgICovXG4gIFdyaXRlci5wcm90b3R5cGUucGFyc2UgPSBmdW5jdGlvbiBwYXJzZSAodGVtcGxhdGUsIHRhZ3MpIHtcbiAgICB2YXIgY2FjaGUgPSB0aGlzLmNhY2hlO1xuICAgIHZhciB0b2tlbnMgPSBjYWNoZVt0ZW1wbGF0ZV07XG5cbiAgICBpZiAodG9rZW5zID09IG51bGwpXG4gICAgICB0b2tlbnMgPSBjYWNoZVt0ZW1wbGF0ZV0gPSBwYXJzZVRlbXBsYXRlKHRlbXBsYXRlLCB0YWdzKTtcblxuICAgIHJldHVybiB0b2tlbnM7XG4gIH07XG5cbiAgLyoqXG4gICAqIEhpZ2gtbGV2ZWwgbWV0aG9kIHRoYXQgaXMgdXNlZCB0byByZW5kZXIgdGhlIGdpdmVuIGB0ZW1wbGF0ZWAgd2l0aFxuICAgKiB0aGUgZ2l2ZW4gYHZpZXdgLlxuICAgKlxuICAgKiBUaGUgb3B0aW9uYWwgYHBhcnRpYWxzYCBhcmd1bWVudCBtYXkgYmUgYW4gb2JqZWN0IHRoYXQgY29udGFpbnMgdGhlXG4gICAqIG5hbWVzIGFuZCB0ZW1wbGF0ZXMgb2YgcGFydGlhbHMgdGhhdCBhcmUgdXNlZCBpbiB0aGUgdGVtcGxhdGUuIEl0IG1heVxuICAgKiBhbHNvIGJlIGEgZnVuY3Rpb24gdGhhdCBpcyB1c2VkIHRvIGxvYWQgcGFydGlhbCB0ZW1wbGF0ZXMgb24gdGhlIGZseVxuICAgKiB0aGF0IHRha2VzIGEgc2luZ2xlIGFyZ3VtZW50OiB0aGUgbmFtZSBvZiB0aGUgcGFydGlhbC5cbiAgICovXG4gIFdyaXRlci5wcm90b3R5cGUucmVuZGVyID0gZnVuY3Rpb24gcmVuZGVyICh0ZW1wbGF0ZSwgdmlldywgcGFydGlhbHMpIHtcbiAgICB2YXIgdG9rZW5zID0gdGhpcy5wYXJzZSh0ZW1wbGF0ZSk7XG4gICAgdmFyIGNvbnRleHQgPSAodmlldyBpbnN0YW5jZW9mIENvbnRleHQpID8gdmlldyA6IG5ldyBDb250ZXh0KHZpZXcpO1xuICAgIHJldHVybiB0aGlzLnJlbmRlclRva2Vucyh0b2tlbnMsIGNvbnRleHQsIHBhcnRpYWxzLCB0ZW1wbGF0ZSk7XG4gIH07XG5cbiAgLyoqXG4gICAqIExvdy1sZXZlbCBtZXRob2QgdGhhdCByZW5kZXJzIHRoZSBnaXZlbiBhcnJheSBvZiBgdG9rZW5zYCB1c2luZ1xuICAgKiB0aGUgZ2l2ZW4gYGNvbnRleHRgIGFuZCBgcGFydGlhbHNgLlxuICAgKlxuICAgKiBOb3RlOiBUaGUgYG9yaWdpbmFsVGVtcGxhdGVgIGlzIG9ubHkgZXZlciB1c2VkIHRvIGV4dHJhY3QgdGhlIHBvcnRpb25cbiAgICogb2YgdGhlIG9yaWdpbmFsIHRlbXBsYXRlIHRoYXQgd2FzIGNvbnRhaW5lZCBpbiBhIGhpZ2hlci1vcmRlciBzZWN0aW9uLlxuICAgKiBJZiB0aGUgdGVtcGxhdGUgZG9lc24ndCB1c2UgaGlnaGVyLW9yZGVyIHNlY3Rpb25zLCB0aGlzIGFyZ3VtZW50IG1heVxuICAgKiBiZSBvbWl0dGVkLlxuICAgKi9cbiAgV3JpdGVyLnByb3RvdHlwZS5yZW5kZXJUb2tlbnMgPSBmdW5jdGlvbiByZW5kZXJUb2tlbnMgKHRva2VucywgY29udGV4dCwgcGFydGlhbHMsIG9yaWdpbmFsVGVtcGxhdGUpIHtcbiAgICB2YXIgYnVmZmVyID0gJyc7XG5cbiAgICB2YXIgdG9rZW4sIHN5bWJvbCwgdmFsdWU7XG4gICAgZm9yICh2YXIgaSA9IDAsIG51bVRva2VucyA9IHRva2Vucy5sZW5ndGg7IGkgPCBudW1Ub2tlbnM7ICsraSkge1xuICAgICAgdmFsdWUgPSB1bmRlZmluZWQ7XG4gICAgICB0b2tlbiA9IHRva2Vuc1tpXTtcbiAgICAgIHN5bWJvbCA9IHRva2VuWzBdO1xuXG4gICAgICBpZiAoc3ltYm9sID09PSAnIycpIHZhbHVlID0gdGhpcy5yZW5kZXJTZWN0aW9uKHRva2VuLCBjb250ZXh0LCBwYXJ0aWFscywgb3JpZ2luYWxUZW1wbGF0ZSk7XG4gICAgICBlbHNlIGlmIChzeW1ib2wgPT09ICdeJykgdmFsdWUgPSB0aGlzLnJlbmRlckludmVydGVkKHRva2VuLCBjb250ZXh0LCBwYXJ0aWFscywgb3JpZ2luYWxUZW1wbGF0ZSk7XG4gICAgICBlbHNlIGlmIChzeW1ib2wgPT09ICc+JykgdmFsdWUgPSB0aGlzLnJlbmRlclBhcnRpYWwodG9rZW4sIGNvbnRleHQsIHBhcnRpYWxzLCBvcmlnaW5hbFRlbXBsYXRlKTtcbiAgICAgIGVsc2UgaWYgKHN5bWJvbCA9PT0gJyYnKSB2YWx1ZSA9IHRoaXMudW5lc2NhcGVkVmFsdWUodG9rZW4sIGNvbnRleHQpO1xuICAgICAgZWxzZSBpZiAoc3ltYm9sID09PSAnbmFtZScpIHZhbHVlID0gdGhpcy5lc2NhcGVkVmFsdWUodG9rZW4sIGNvbnRleHQpO1xuICAgICAgZWxzZSBpZiAoc3ltYm9sID09PSAndGV4dCcpIHZhbHVlID0gdGhpcy5yYXdWYWx1ZSh0b2tlbik7XG5cbiAgICAgIGlmICh2YWx1ZSAhPT0gdW5kZWZpbmVkKVxuICAgICAgICBidWZmZXIgKz0gdmFsdWU7XG4gICAgfVxuXG4gICAgcmV0dXJuIGJ1ZmZlcjtcbiAgfTtcblxuICBXcml0ZXIucHJvdG90eXBlLnJlbmRlclNlY3Rpb24gPSBmdW5jdGlvbiByZW5kZXJTZWN0aW9uICh0b2tlbiwgY29udGV4dCwgcGFydGlhbHMsIG9yaWdpbmFsVGVtcGxhdGUpIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdmFyIGJ1ZmZlciA9ICcnO1xuICAgIHZhciB2YWx1ZSA9IGNvbnRleHQubG9va3VwKHRva2VuWzFdKTtcblxuICAgIC8vIFRoaXMgZnVuY3Rpb24gaXMgdXNlZCB0byByZW5kZXIgYW4gYXJiaXRyYXJ5IHRlbXBsYXRlXG4gICAgLy8gaW4gdGhlIGN1cnJlbnQgY29udGV4dCBieSBoaWdoZXItb3JkZXIgc2VjdGlvbnMuXG4gICAgZnVuY3Rpb24gc3ViUmVuZGVyICh0ZW1wbGF0ZSkge1xuICAgICAgcmV0dXJuIHNlbGYucmVuZGVyKHRlbXBsYXRlLCBjb250ZXh0LCBwYXJ0aWFscyk7XG4gICAgfVxuXG4gICAgaWYgKCF2YWx1ZSkgcmV0dXJuO1xuXG4gICAgaWYgKGlzQXJyYXkodmFsdWUpKSB7XG4gICAgICBmb3IgKHZhciBqID0gMCwgdmFsdWVMZW5ndGggPSB2YWx1ZS5sZW5ndGg7IGogPCB2YWx1ZUxlbmd0aDsgKytqKSB7XG4gICAgICAgIGJ1ZmZlciArPSB0aGlzLnJlbmRlclRva2Vucyh0b2tlbls0XSwgY29udGV4dC5wdXNoKHZhbHVlW2pdKSwgcGFydGlhbHMsIG9yaWdpbmFsVGVtcGxhdGUpO1xuICAgICAgfVxuICAgIH0gZWxzZSBpZiAodHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyB8fCB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnIHx8IHR5cGVvZiB2YWx1ZSA9PT0gJ251bWJlcicpIHtcbiAgICAgIGJ1ZmZlciArPSB0aGlzLnJlbmRlclRva2Vucyh0b2tlbls0XSwgY29udGV4dC5wdXNoKHZhbHVlKSwgcGFydGlhbHMsIG9yaWdpbmFsVGVtcGxhdGUpO1xuICAgIH0gZWxzZSBpZiAoaXNGdW5jdGlvbih2YWx1ZSkpIHtcbiAgICAgIGlmICh0eXBlb2Ygb3JpZ2luYWxUZW1wbGF0ZSAhPT0gJ3N0cmluZycpXG4gICAgICAgIHRocm93IG5ldyBFcnJvcignQ2Fubm90IHVzZSBoaWdoZXItb3JkZXIgc2VjdGlvbnMgd2l0aG91dCB0aGUgb3JpZ2luYWwgdGVtcGxhdGUnKTtcblxuICAgICAgLy8gRXh0cmFjdCB0aGUgcG9ydGlvbiBvZiB0aGUgb3JpZ2luYWwgdGVtcGxhdGUgdGhhdCB0aGUgc2VjdGlvbiBjb250YWlucy5cbiAgICAgIHZhbHVlID0gdmFsdWUuY2FsbChjb250ZXh0LnZpZXcsIG9yaWdpbmFsVGVtcGxhdGUuc2xpY2UodG9rZW5bM10sIHRva2VuWzVdKSwgc3ViUmVuZGVyKTtcblxuICAgICAgaWYgKHZhbHVlICE9IG51bGwpXG4gICAgICAgIGJ1ZmZlciArPSB2YWx1ZTtcbiAgICB9IGVsc2Uge1xuICAgICAgYnVmZmVyICs9IHRoaXMucmVuZGVyVG9rZW5zKHRva2VuWzRdLCBjb250ZXh0LCBwYXJ0aWFscywgb3JpZ2luYWxUZW1wbGF0ZSk7XG4gICAgfVxuICAgIHJldHVybiBidWZmZXI7XG4gIH07XG5cbiAgV3JpdGVyLnByb3RvdHlwZS5yZW5kZXJJbnZlcnRlZCA9IGZ1bmN0aW9uIHJlbmRlckludmVydGVkICh0b2tlbiwgY29udGV4dCwgcGFydGlhbHMsIG9yaWdpbmFsVGVtcGxhdGUpIHtcbiAgICB2YXIgdmFsdWUgPSBjb250ZXh0Lmxvb2t1cCh0b2tlblsxXSk7XG5cbiAgICAvLyBVc2UgSmF2YVNjcmlwdCdzIGRlZmluaXRpb24gb2YgZmFsc3kuIEluY2x1ZGUgZW1wdHkgYXJyYXlzLlxuICAgIC8vIFNlZSBodHRwczovL2dpdGh1Yi5jb20vamFubC9tdXN0YWNoZS5qcy9pc3N1ZXMvMTg2XG4gICAgaWYgKCF2YWx1ZSB8fCAoaXNBcnJheSh2YWx1ZSkgJiYgdmFsdWUubGVuZ3RoID09PSAwKSlcbiAgICAgIHJldHVybiB0aGlzLnJlbmRlclRva2Vucyh0b2tlbls0XSwgY29udGV4dCwgcGFydGlhbHMsIG9yaWdpbmFsVGVtcGxhdGUpO1xuICB9O1xuXG4gIFdyaXRlci5wcm90b3R5cGUucmVuZGVyUGFydGlhbCA9IGZ1bmN0aW9uIHJlbmRlclBhcnRpYWwgKHRva2VuLCBjb250ZXh0LCBwYXJ0aWFscykge1xuICAgIGlmICghcGFydGlhbHMpIHJldHVybjtcblxuICAgIHZhciB2YWx1ZSA9IGlzRnVuY3Rpb24ocGFydGlhbHMpID8gcGFydGlhbHModG9rZW5bMV0pIDogcGFydGlhbHNbdG9rZW5bMV1dO1xuICAgIGlmICh2YWx1ZSAhPSBudWxsKVxuICAgICAgcmV0dXJuIHRoaXMucmVuZGVyVG9rZW5zKHRoaXMucGFyc2UodmFsdWUpLCBjb250ZXh0LCBwYXJ0aWFscywgdmFsdWUpO1xuICB9O1xuXG4gIFdyaXRlci5wcm90b3R5cGUudW5lc2NhcGVkVmFsdWUgPSBmdW5jdGlvbiB1bmVzY2FwZWRWYWx1ZSAodG9rZW4sIGNvbnRleHQpIHtcbiAgICB2YXIgdmFsdWUgPSBjb250ZXh0Lmxvb2t1cCh0b2tlblsxXSk7XG4gICAgaWYgKHZhbHVlICE9IG51bGwpXG4gICAgICByZXR1cm4gdmFsdWU7XG4gIH07XG5cbiAgV3JpdGVyLnByb3RvdHlwZS5lc2NhcGVkVmFsdWUgPSBmdW5jdGlvbiBlc2NhcGVkVmFsdWUgKHRva2VuLCBjb250ZXh0KSB7XG4gICAgdmFyIHZhbHVlID0gY29udGV4dC5sb29rdXAodG9rZW5bMV0pO1xuICAgIGlmICh2YWx1ZSAhPSBudWxsKVxuICAgICAgcmV0dXJuIG11c3RhY2hlLmVzY2FwZSh2YWx1ZSk7XG4gIH07XG5cbiAgV3JpdGVyLnByb3RvdHlwZS5yYXdWYWx1ZSA9IGZ1bmN0aW9uIHJhd1ZhbHVlICh0b2tlbikge1xuICAgIHJldHVybiB0b2tlblsxXTtcbiAgfTtcblxuICBtdXN0YWNoZS5uYW1lID0gJ211c3RhY2hlLmpzJztcbiAgbXVzdGFjaGUudmVyc2lvbiA9ICcyLjIuMSc7XG4gIG11c3RhY2hlLnRhZ3MgPSBbICd7eycsICd9fScgXTtcblxuICAvLyBBbGwgaGlnaC1sZXZlbCBtdXN0YWNoZS4qIGZ1bmN0aW9ucyB1c2UgdGhpcyB3cml0ZXIuXG4gIHZhciBkZWZhdWx0V3JpdGVyID0gbmV3IFdyaXRlcigpO1xuXG4gIC8qKlxuICAgKiBDbGVhcnMgYWxsIGNhY2hlZCB0ZW1wbGF0ZXMgaW4gdGhlIGRlZmF1bHQgd3JpdGVyLlxuICAgKi9cbiAgbXVzdGFjaGUuY2xlYXJDYWNoZSA9IGZ1bmN0aW9uIGNsZWFyQ2FjaGUgKCkge1xuICAgIHJldHVybiBkZWZhdWx0V3JpdGVyLmNsZWFyQ2FjaGUoKTtcbiAgfTtcblxuICAvKipcbiAgICogUGFyc2VzIGFuZCBjYWNoZXMgdGhlIGdpdmVuIHRlbXBsYXRlIGluIHRoZSBkZWZhdWx0IHdyaXRlciBhbmQgcmV0dXJucyB0aGVcbiAgICogYXJyYXkgb2YgdG9rZW5zIGl0IGNvbnRhaW5zLiBEb2luZyB0aGlzIGFoZWFkIG9mIHRpbWUgYXZvaWRzIHRoZSBuZWVkIHRvXG4gICAqIHBhcnNlIHRlbXBsYXRlcyBvbiB0aGUgZmx5IGFzIHRoZXkgYXJlIHJlbmRlcmVkLlxuICAgKi9cbiAgbXVzdGFjaGUucGFyc2UgPSBmdW5jdGlvbiBwYXJzZSAodGVtcGxhdGUsIHRhZ3MpIHtcbiAgICByZXR1cm4gZGVmYXVsdFdyaXRlci5wYXJzZSh0ZW1wbGF0ZSwgdGFncyk7XG4gIH07XG5cbiAgLyoqXG4gICAqIFJlbmRlcnMgdGhlIGB0ZW1wbGF0ZWAgd2l0aCB0aGUgZ2l2ZW4gYHZpZXdgIGFuZCBgcGFydGlhbHNgIHVzaW5nIHRoZVxuICAgKiBkZWZhdWx0IHdyaXRlci5cbiAgICovXG4gIG11c3RhY2hlLnJlbmRlciA9IGZ1bmN0aW9uIHJlbmRlciAodGVtcGxhdGUsIHZpZXcsIHBhcnRpYWxzKSB7XG4gICAgaWYgKHR5cGVvZiB0ZW1wbGF0ZSAhPT0gJ3N0cmluZycpIHtcbiAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ0ludmFsaWQgdGVtcGxhdGUhIFRlbXBsYXRlIHNob3VsZCBiZSBhIFwic3RyaW5nXCIgJyArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICdidXQgXCInICsgdHlwZVN0cih0ZW1wbGF0ZSkgKyAnXCIgd2FzIGdpdmVuIGFzIHRoZSBmaXJzdCAnICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgJ2FyZ3VtZW50IGZvciBtdXN0YWNoZSNyZW5kZXIodGVtcGxhdGUsIHZpZXcsIHBhcnRpYWxzKScpO1xuICAgIH1cblxuICAgIHJldHVybiBkZWZhdWx0V3JpdGVyLnJlbmRlcih0ZW1wbGF0ZSwgdmlldywgcGFydGlhbHMpO1xuICB9O1xuXG4gIC8vIFRoaXMgaXMgaGVyZSBmb3IgYmFja3dhcmRzIGNvbXBhdGliaWxpdHkgd2l0aCAwLjQueC4sXG4gIC8qZXNsaW50LWRpc2FibGUgKi8gLy8gZXNsaW50IHdhbnRzIGNhbWVsIGNhc2VkIGZ1bmN0aW9uIG5hbWVcbiAgbXVzdGFjaGUudG9faHRtbCA9IGZ1bmN0aW9uIHRvX2h0bWwgKHRlbXBsYXRlLCB2aWV3LCBwYXJ0aWFscywgc2VuZCkge1xuICAgIC8qZXNsaW50LWVuYWJsZSovXG5cbiAgICB2YXIgcmVzdWx0ID0gbXVzdGFjaGUucmVuZGVyKHRlbXBsYXRlLCB2aWV3LCBwYXJ0aWFscyk7XG5cbiAgICBpZiAoaXNGdW5jdGlvbihzZW5kKSkge1xuICAgICAgc2VuZChyZXN1bHQpO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cbiAgfTtcblxuICAvLyBFeHBvcnQgdGhlIGVzY2FwaW5nIGZ1bmN0aW9uIHNvIHRoYXQgdGhlIHVzZXIgbWF5IG92ZXJyaWRlIGl0LlxuICAvLyBTZWUgaHR0cHM6Ly9naXRodWIuY29tL2phbmwvbXVzdGFjaGUuanMvaXNzdWVzLzI0NFxuICBtdXN0YWNoZS5lc2NhcGUgPSBlc2NhcGVIdG1sO1xuXG4gIC8vIEV4cG9ydCB0aGVzZSBtYWlubHkgZm9yIHRlc3RpbmcsIGJ1dCBhbHNvIGZvciBhZHZhbmNlZCB1c2FnZS5cbiAgbXVzdGFjaGUuU2Nhbm5lciA9IFNjYW5uZXI7XG4gIG11c3RhY2hlLkNvbnRleHQgPSBDb250ZXh0O1xuICBtdXN0YWNoZS5Xcml0ZXIgPSBXcml0ZXI7XG5cbn0pKTtcbiIsInZhciBodG1sX3Nhbml0aXplID0gcmVxdWlyZSgnLi9zYW5pdGl6ZXItYnVuZGxlLmpzJyk7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24oXykge1xuICAgIGlmICghXykgcmV0dXJuICcnO1xuICAgIHJldHVybiBodG1sX3Nhbml0aXplKF8sIGNsZWFuVXJsLCBjbGVhbklkKTtcbn07XG5cbi8vIGh0dHBzOi8vYnVnemlsbGEubW96aWxsYS5vcmcvc2hvd19idWcuY2dpP2lkPTI1NTEwN1xuZnVuY3Rpb24gY2xlYW5VcmwodXJsKSB7XG4gICAgJ3VzZSBzdHJpY3QnO1xuICAgIGlmICgvXmh0dHBzPy8udGVzdCh1cmwuZ2V0U2NoZW1lKCkpKSByZXR1cm4gdXJsLnRvU3RyaW5nKCk7XG4gICAgaWYgKC9ebWFpbHRvPy8udGVzdCh1cmwuZ2V0U2NoZW1lKCkpKSByZXR1cm4gdXJsLnRvU3RyaW5nKCk7XG4gICAgaWYgKCdkYXRhJyA9PSB1cmwuZ2V0U2NoZW1lKCkgJiYgL15pbWFnZS8udGVzdCh1cmwuZ2V0UGF0aCgpKSkge1xuICAgICAgICByZXR1cm4gdXJsLnRvU3RyaW5nKCk7XG4gICAgfVxufVxuXG5mdW5jdGlvbiBjbGVhbklkKGlkKSB7IHJldHVybiBpZDsgfVxuIiwiXG4vLyBDb3B5cmlnaHQgKEMpIDIwMTAgR29vZ2xlIEluYy5cbi8vXG4vLyBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xuLy8geW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuLy8gWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4vL1xuLy8gICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbi8vXG4vLyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4vLyBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4vLyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbi8vIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbi8vIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuXG4vKipcbiAqIEBmaWxlb3ZlcnZpZXdcbiAqIEltcGxlbWVudHMgUkZDIDM5ODYgZm9yIHBhcnNpbmcvZm9ybWF0dGluZyBVUklzLlxuICpcbiAqIEBhdXRob3IgbWlrZXNhbXVlbEBnbWFpbC5jb21cbiAqIFxcQHByb3ZpZGVzIFVSSVxuICogXFxAb3ZlcnJpZGVzIHdpbmRvd1xuICovXG5cbnZhciBVUkkgPSAoZnVuY3Rpb24gKCkge1xuXG4vKipcbiAqIGNyZWF0ZXMgYSB1cmkgZnJvbSB0aGUgc3RyaW5nIGZvcm0uICBUaGUgcGFyc2VyIGlzIHJlbGF4ZWQsIHNvIHNwZWNpYWxcbiAqIGNoYXJhY3RlcnMgdGhhdCBhcmVuJ3QgZXNjYXBlZCBidXQgZG9uJ3QgY2F1c2UgYW1iaWd1aXRpZXMgd2lsbCBub3QgY2F1c2VcbiAqIHBhcnNlIGZhaWx1cmVzLlxuICpcbiAqIEByZXR1cm4ge1VSSXxudWxsfVxuICovXG5mdW5jdGlvbiBwYXJzZSh1cmlTdHIpIHtcbiAgdmFyIG0gPSAoJycgKyB1cmlTdHIpLm1hdGNoKFVSSV9SRV8pO1xuICBpZiAoIW0pIHsgcmV0dXJuIG51bGw7IH1cbiAgcmV0dXJuIG5ldyBVUkkoXG4gICAgICBudWxsSWZBYnNlbnQobVsxXSksXG4gICAgICBudWxsSWZBYnNlbnQobVsyXSksXG4gICAgICBudWxsSWZBYnNlbnQobVszXSksXG4gICAgICBudWxsSWZBYnNlbnQobVs0XSksXG4gICAgICBudWxsSWZBYnNlbnQobVs1XSksXG4gICAgICBudWxsSWZBYnNlbnQobVs2XSksXG4gICAgICBudWxsSWZBYnNlbnQobVs3XSkpO1xufVxuXG5cbi8qKlxuICogY3JlYXRlcyBhIHVyaSBmcm9tIHRoZSBnaXZlbiBwYXJ0cy5cbiAqXG4gKiBAcGFyYW0gc2NoZW1lIHtzdHJpbmd9IGFuIHVuZW5jb2RlZCBzY2hlbWUgc3VjaCBhcyBcImh0dHBcIiBvciBudWxsXG4gKiBAcGFyYW0gY3JlZGVudGlhbHMge3N0cmluZ30gdW5lbmNvZGVkIHVzZXIgY3JlZGVudGlhbHMgb3IgbnVsbFxuICogQHBhcmFtIGRvbWFpbiB7c3RyaW5nfSBhbiB1bmVuY29kZWQgZG9tYWluIG5hbWUgb3IgbnVsbFxuICogQHBhcmFtIHBvcnQge251bWJlcn0gYSBwb3J0IG51bWJlciBpbiBbMSwgMzI3NjhdLlxuICogICAgLTEgaW5kaWNhdGVzIG5vIHBvcnQsIGFzIGRvZXMgbnVsbC5cbiAqIEBwYXJhbSBwYXRoIHtzdHJpbmd9IGFuIHVuZW5jb2RlZCBwYXRoXG4gKiBAcGFyYW0gcXVlcnkge0FycmF5LjxzdHJpbmc+fHN0cmluZ3xudWxsfSBhIGxpc3Qgb2YgdW5lbmNvZGVkIGNnaVxuICogICBwYXJhbWV0ZXJzIHdoZXJlIGV2ZW4gdmFsdWVzIGFyZSBrZXlzIGFuZCBvZGRzIHRoZSBjb3JyZXNwb25kaW5nIHZhbHVlc1xuICogICBvciBhbiB1bmVuY29kZWQgcXVlcnkuXG4gKiBAcGFyYW0gZnJhZ21lbnQge3N0cmluZ30gYW4gdW5lbmNvZGVkIGZyYWdtZW50IHdpdGhvdXQgdGhlIFwiI1wiIG9yIG51bGwuXG4gKiBAcmV0dXJuIHtVUkl9XG4gKi9cbmZ1bmN0aW9uIGNyZWF0ZShzY2hlbWUsIGNyZWRlbnRpYWxzLCBkb21haW4sIHBvcnQsIHBhdGgsIHF1ZXJ5LCBmcmFnbWVudCkge1xuICB2YXIgdXJpID0gbmV3IFVSSShcbiAgICAgIGVuY29kZUlmRXhpc3RzMihzY2hlbWUsIFVSSV9ESVNBTExPV0VEX0lOX1NDSEVNRV9PUl9DUkVERU5USUFMU18pLFxuICAgICAgZW5jb2RlSWZFeGlzdHMyKFxuICAgICAgICAgIGNyZWRlbnRpYWxzLCBVUklfRElTQUxMT1dFRF9JTl9TQ0hFTUVfT1JfQ1JFREVOVElBTFNfKSxcbiAgICAgIGVuY29kZUlmRXhpc3RzKGRvbWFpbiksXG4gICAgICBwb3J0ID4gMCA/IHBvcnQudG9TdHJpbmcoKSA6IG51bGwsXG4gICAgICBlbmNvZGVJZkV4aXN0czIocGF0aCwgVVJJX0RJU0FMTE9XRURfSU5fUEFUSF8pLFxuICAgICAgbnVsbCxcbiAgICAgIGVuY29kZUlmRXhpc3RzKGZyYWdtZW50KSk7XG4gIGlmIChxdWVyeSkge1xuICAgIGlmICgnc3RyaW5nJyA9PT0gdHlwZW9mIHF1ZXJ5KSB7XG4gICAgICB1cmkuc2V0UmF3UXVlcnkocXVlcnkucmVwbGFjZSgvW14/Jj0wLTlBLVphLXpfXFwtfi4lXS9nLCBlbmNvZGVPbmUpKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdXJpLnNldEFsbFBhcmFtZXRlcnMocXVlcnkpO1xuICAgIH1cbiAgfVxuICByZXR1cm4gdXJpO1xufVxuZnVuY3Rpb24gZW5jb2RlSWZFeGlzdHModW5lc2NhcGVkUGFydCkge1xuICBpZiAoJ3N0cmluZycgPT0gdHlwZW9mIHVuZXNjYXBlZFBhcnQpIHtcbiAgICByZXR1cm4gZW5jb2RlVVJJQ29tcG9uZW50KHVuZXNjYXBlZFBhcnQpO1xuICB9XG4gIHJldHVybiBudWxsO1xufTtcbi8qKlxuICogaWYgdW5lc2NhcGVkUGFydCBpcyBub24gbnVsbCwgdGhlbiBlc2NhcGVzIGFueSBjaGFyYWN0ZXJzIGluIGl0IHRoYXQgYXJlbid0XG4gKiB2YWxpZCBjaGFyYWN0ZXJzIGluIGEgdXJsIGFuZCBhbHNvIGVzY2FwZXMgYW55IHNwZWNpYWwgY2hhcmFjdGVycyB0aGF0XG4gKiBhcHBlYXIgaW4gZXh0cmEuXG4gKlxuICogQHBhcmFtIHVuZXNjYXBlZFBhcnQge3N0cmluZ31cbiAqIEBwYXJhbSBleHRyYSB7UmVnRXhwfSBhIGNoYXJhY3RlciBzZXQgb2YgY2hhcmFjdGVycyBpbiBbXFwwMS1cXDE3N10uXG4gKiBAcmV0dXJuIHtzdHJpbmd8bnVsbH0gbnVsbCBpZmYgdW5lc2NhcGVkUGFydCA9PSBudWxsLlxuICovXG5mdW5jdGlvbiBlbmNvZGVJZkV4aXN0czIodW5lc2NhcGVkUGFydCwgZXh0cmEpIHtcbiAgaWYgKCdzdHJpbmcnID09IHR5cGVvZiB1bmVzY2FwZWRQYXJ0KSB7XG4gICAgcmV0dXJuIGVuY29kZVVSSSh1bmVzY2FwZWRQYXJ0KS5yZXBsYWNlKGV4dHJhLCBlbmNvZGVPbmUpO1xuICB9XG4gIHJldHVybiBudWxsO1xufTtcbi8qKiBjb252ZXJ0cyBhIGNoYXJhY3RlciBpbiBbXFwwMS1cXDE3N10gdG8gaXRzIHVybCBlbmNvZGVkIGVxdWl2YWxlbnQuICovXG5mdW5jdGlvbiBlbmNvZGVPbmUoY2gpIHtcbiAgdmFyIG4gPSBjaC5jaGFyQ29kZUF0KDApO1xuICByZXR1cm4gJyUnICsgJzAxMjM0NTY3ODlBQkNERUYnLmNoYXJBdCgobiA+PiA0KSAmIDB4ZikgK1xuICAgICAgJzAxMjM0NTY3ODlBQkNERUYnLmNoYXJBdChuICYgMHhmKTtcbn1cblxuLyoqXG4gKiB7QHVwZG9jXG4gKiAgJCBub3JtUGF0aCgnZm9vLy4vYmFyJylcbiAqICAjICdmb28vYmFyJ1xuICogICQgbm9ybVBhdGgoJy4vZm9vJylcbiAqICAjICdmb28nXG4gKiAgJCBub3JtUGF0aCgnZm9vLy4nKVxuICogICMgJ2ZvbydcbiAqICAkIG5vcm1QYXRoKCdmb28vL2JhcicpXG4gKiAgIyAnZm9vL2JhcidcbiAqIH1cbiAqL1xuZnVuY3Rpb24gbm9ybVBhdGgocGF0aCkge1xuICByZXR1cm4gcGF0aC5yZXBsYWNlKC8oXnxcXC8pXFwuKD86XFwvfCQpL2csICckMScpLnJlcGxhY2UoL1xcL3syLH0vZywgJy8nKTtcbn1cblxudmFyIFBBUkVOVF9ESVJFQ1RPUllfSEFORExFUiA9IG5ldyBSZWdFeHAoXG4gICAgJydcbiAgICAvLyBBIHBhdGggYnJlYWtcbiAgICArICcoL3xeKSdcbiAgICAvLyBmb2xsb3dlZCBieSBhIG5vbiAuLiBwYXRoIGVsZW1lbnRcbiAgICAvLyAoY2Fubm90IGJlIC4gYmVjYXVzZSBub3JtUGF0aCBpcyB1c2VkIHByaW9yIHRvIHRoaXMgUmVnRXhwKVxuICAgICsgJyg/OlteLi9dW14vXSp8XFxcXC57Mix9KD86W14uL11bXi9dKil8XFxcXC57Myx9W14vXSopJ1xuICAgIC8vIGZvbGxvd2VkIGJ5IC4uIGZvbGxvd2VkIGJ5IGEgcGF0aCBicmVhay5cbiAgICArICcvXFxcXC5cXFxcLig/Oi98JCknKTtcblxudmFyIFBBUkVOVF9ESVJFQ1RPUllfSEFORExFUl9SRSA9IG5ldyBSZWdFeHAoUEFSRU5UX0RJUkVDVE9SWV9IQU5ETEVSKTtcblxudmFyIEVYVFJBX1BBUkVOVF9QQVRIU19SRSA9IC9eKD86XFwuXFwuXFwvKSooPzpcXC5cXC4kKT8vO1xuXG4vKipcbiAqIE5vcm1hbGl6ZXMgaXRzIGlucHV0IHBhdGggYW5kIGNvbGxhcHNlcyBhbGwgLiBhbmQgLi4gc2VxdWVuY2VzIGV4Y2VwdCBmb3JcbiAqIC4uIHNlcXVlbmNlcyB0aGF0IHdvdWxkIHRha2UgaXQgYWJvdmUgdGhlIHJvb3Qgb2YgdGhlIGN1cnJlbnQgcGFyZW50XG4gKiBkaXJlY3RvcnkuXG4gKiB7QHVwZG9jXG4gKiAgJCBjb2xsYXBzZV9kb3RzKCdmb28vLi4vYmFyJylcbiAqICAjICdiYXInXG4gKiAgJCBjb2xsYXBzZV9kb3RzKCdmb28vLi9iYXInKVxuICogICMgJ2Zvby9iYXInXG4gKiAgJCBjb2xsYXBzZV9kb3RzKCdmb28vLi4vYmFyLy4vLi4vLi4vYmF6JylcbiAqICAjICdiYXonXG4gKiAgJCBjb2xsYXBzZV9kb3RzKCcuLi9mb28nKVxuICogICMgJy4uL2ZvbydcbiAqICAkIGNvbGxhcHNlX2RvdHMoJy4uL2ZvbycpLnJlcGxhY2UoRVhUUkFfUEFSRU5UX1BBVEhTX1JFLCAnJylcbiAqICAjICdmb28nXG4gKiB9XG4gKi9cbmZ1bmN0aW9uIGNvbGxhcHNlX2RvdHMocGF0aCkge1xuICBpZiAocGF0aCA9PT0gbnVsbCkgeyByZXR1cm4gbnVsbDsgfVxuICB2YXIgcCA9IG5vcm1QYXRoKHBhdGgpO1xuICAvLyBPbmx5IC8uLi8gbGVmdCB0byBmbGF0dGVuXG4gIHZhciByID0gUEFSRU5UX0RJUkVDVE9SWV9IQU5ETEVSX1JFO1xuICAvLyBXZSByZXBsYWNlIHdpdGggJDEgd2hpY2ggbWF0Y2hlcyBhIC8gYmVmb3JlIHRoZSAuLiBiZWNhdXNlIHRoaXNcbiAgLy8gZ3VhcmFudGVlcyB0aGF0OlxuICAvLyAoMSkgd2UgaGF2ZSBhdCBtb3N0IDEgLyBiZXR3ZWVuIHRoZSBhZGphY2VudCBwbGFjZSxcbiAgLy8gKDIpIGFsd2F5cyBoYXZlIGEgc2xhc2ggaWYgdGhlcmUgaXMgYSBwcmVjZWRpbmcgcGF0aCBzZWN0aW9uLCBhbmRcbiAgLy8gKDMpIHdlIG5ldmVyIHR1cm4gYSByZWxhdGl2ZSBwYXRoIGludG8gYW4gYWJzb2x1dGUgcGF0aC5cbiAgZm9yICh2YXIgcTsgKHEgPSBwLnJlcGxhY2UociwgJyQxJykpICE9IHA7IHAgPSBxKSB7fTtcbiAgcmV0dXJuIHA7XG59XG5cbi8qKlxuICogcmVzb2x2ZXMgYSByZWxhdGl2ZSB1cmwgc3RyaW5nIHRvIGEgYmFzZSB1cmkuXG4gKiBAcmV0dXJuIHtVUkl9XG4gKi9cbmZ1bmN0aW9uIHJlc29sdmUoYmFzZVVyaSwgcmVsYXRpdmVVcmkpIHtcbiAgLy8gdGhlcmUgYXJlIHNldmVyYWwga2luZHMgb2YgcmVsYXRpdmUgdXJsczpcbiAgLy8gMS4gLy9mb28gLSByZXBsYWNlcyBldmVyeXRoaW5nIGZyb20gdGhlIGRvbWFpbiBvbi4gIGZvbyBpcyBhIGRvbWFpbiBuYW1lXG4gIC8vIDIuIGZvbyAtIHJlcGxhY2VzIHRoZSBsYXN0IHBhcnQgb2YgdGhlIHBhdGgsIHRoZSB3aG9sZSBxdWVyeSBhbmQgZnJhZ21lbnRcbiAgLy8gMy4gL2ZvbyAtIHJlcGxhY2VzIHRoZSB0aGUgcGF0aCwgdGhlIHF1ZXJ5IGFuZCBmcmFnbWVudFxuICAvLyA0LiA/Zm9vIC0gcmVwbGFjZSB0aGUgcXVlcnkgYW5kIGZyYWdtZW50XG4gIC8vIDUuICNmb28gLSByZXBsYWNlIHRoZSBmcmFnbWVudCBvbmx5XG5cbiAgdmFyIGFic29sdXRlVXJpID0gYmFzZVVyaS5jbG9uZSgpO1xuICAvLyB3ZSBzYXRpc2Z5IHRoZXNlIGNvbmRpdGlvbnMgYnkgbG9va2luZyBmb3IgdGhlIGZpcnN0IHBhcnQgb2YgcmVsYXRpdmVVcmlcbiAgLy8gdGhhdCBpcyBub3QgYmxhbmsgYW5kIGFwcGx5aW5nIGRlZmF1bHRzIHRvIHRoZSByZXN0XG5cbiAgdmFyIG92ZXJyaWRkZW4gPSByZWxhdGl2ZVVyaS5oYXNTY2hlbWUoKTtcblxuICBpZiAob3ZlcnJpZGRlbikge1xuICAgIGFic29sdXRlVXJpLnNldFJhd1NjaGVtZShyZWxhdGl2ZVVyaS5nZXRSYXdTY2hlbWUoKSk7XG4gIH0gZWxzZSB7XG4gICAgb3ZlcnJpZGRlbiA9IHJlbGF0aXZlVXJpLmhhc0NyZWRlbnRpYWxzKCk7XG4gIH1cblxuICBpZiAob3ZlcnJpZGRlbikge1xuICAgIGFic29sdXRlVXJpLnNldFJhd0NyZWRlbnRpYWxzKHJlbGF0aXZlVXJpLmdldFJhd0NyZWRlbnRpYWxzKCkpO1xuICB9IGVsc2Uge1xuICAgIG92ZXJyaWRkZW4gPSByZWxhdGl2ZVVyaS5oYXNEb21haW4oKTtcbiAgfVxuXG4gIGlmIChvdmVycmlkZGVuKSB7XG4gICAgYWJzb2x1dGVVcmkuc2V0UmF3RG9tYWluKHJlbGF0aXZlVXJpLmdldFJhd0RvbWFpbigpKTtcbiAgfSBlbHNlIHtcbiAgICBvdmVycmlkZGVuID0gcmVsYXRpdmVVcmkuaGFzUG9ydCgpO1xuICB9XG5cbiAgdmFyIHJhd1BhdGggPSByZWxhdGl2ZVVyaS5nZXRSYXdQYXRoKCk7XG4gIHZhciBzaW1wbGlmaWVkUGF0aCA9IGNvbGxhcHNlX2RvdHMocmF3UGF0aCk7XG4gIGlmIChvdmVycmlkZGVuKSB7XG4gICAgYWJzb2x1dGVVcmkuc2V0UG9ydChyZWxhdGl2ZVVyaS5nZXRQb3J0KCkpO1xuICAgIHNpbXBsaWZpZWRQYXRoID0gc2ltcGxpZmllZFBhdGhcbiAgICAgICAgJiYgc2ltcGxpZmllZFBhdGgucmVwbGFjZShFWFRSQV9QQVJFTlRfUEFUSFNfUkUsICcnKTtcbiAgfSBlbHNlIHtcbiAgICBvdmVycmlkZGVuID0gISFyYXdQYXRoO1xuICAgIGlmIChvdmVycmlkZGVuKSB7XG4gICAgICAvLyByZXNvbHZlIHBhdGggcHJvcGVybHlcbiAgICAgIGlmIChzaW1wbGlmaWVkUGF0aC5jaGFyQ29kZUF0KDApICE9PSAweDJmIC8qIC8gKi8pIHsgIC8vIHBhdGggaXMgcmVsYXRpdmVcbiAgICAgICAgdmFyIGFic1Jhd1BhdGggPSBjb2xsYXBzZV9kb3RzKGFic29sdXRlVXJpLmdldFJhd1BhdGgoKSB8fCAnJylcbiAgICAgICAgICAgIC5yZXBsYWNlKEVYVFJBX1BBUkVOVF9QQVRIU19SRSwgJycpO1xuICAgICAgICB2YXIgc2xhc2ggPSBhYnNSYXdQYXRoLmxhc3RJbmRleE9mKCcvJykgKyAxO1xuICAgICAgICBzaW1wbGlmaWVkUGF0aCA9IGNvbGxhcHNlX2RvdHMoXG4gICAgICAgICAgICAoc2xhc2ggPyBhYnNSYXdQYXRoLnN1YnN0cmluZygwLCBzbGFzaCkgOiAnJylcbiAgICAgICAgICAgICsgY29sbGFwc2VfZG90cyhyYXdQYXRoKSlcbiAgICAgICAgICAgIC5yZXBsYWNlKEVYVFJBX1BBUkVOVF9QQVRIU19SRSwgJycpO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBzaW1wbGlmaWVkUGF0aCA9IHNpbXBsaWZpZWRQYXRoXG4gICAgICAgICAgJiYgc2ltcGxpZmllZFBhdGgucmVwbGFjZShFWFRSQV9QQVJFTlRfUEFUSFNfUkUsICcnKTtcbiAgICAgIGlmIChzaW1wbGlmaWVkUGF0aCAhPT0gcmF3UGF0aCkge1xuICAgICAgICBhYnNvbHV0ZVVyaS5zZXRSYXdQYXRoKHNpbXBsaWZpZWRQYXRoKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBpZiAob3ZlcnJpZGRlbikge1xuICAgIGFic29sdXRlVXJpLnNldFJhd1BhdGgoc2ltcGxpZmllZFBhdGgpO1xuICB9IGVsc2Uge1xuICAgIG92ZXJyaWRkZW4gPSByZWxhdGl2ZVVyaS5oYXNRdWVyeSgpO1xuICB9XG5cbiAgaWYgKG92ZXJyaWRkZW4pIHtcbiAgICBhYnNvbHV0ZVVyaS5zZXRSYXdRdWVyeShyZWxhdGl2ZVVyaS5nZXRSYXdRdWVyeSgpKTtcbiAgfSBlbHNlIHtcbiAgICBvdmVycmlkZGVuID0gcmVsYXRpdmVVcmkuaGFzRnJhZ21lbnQoKTtcbiAgfVxuXG4gIGlmIChvdmVycmlkZGVuKSB7XG4gICAgYWJzb2x1dGVVcmkuc2V0UmF3RnJhZ21lbnQocmVsYXRpdmVVcmkuZ2V0UmF3RnJhZ21lbnQoKSk7XG4gIH1cblxuICByZXR1cm4gYWJzb2x1dGVVcmk7XG59XG5cbi8qKlxuICogYSBtdXRhYmxlIFVSSS5cbiAqXG4gKiBUaGlzIGNsYXNzIGNvbnRhaW5zIHNldHRlcnMgYW5kIGdldHRlcnMgZm9yIHRoZSBwYXJ0cyBvZiB0aGUgVVJJLlxuICogVGhlIDx0dD5nZXRYWVo8L3R0Pi88dHQ+c2V0WFlaPC90dD4gbWV0aG9kcyByZXR1cm4gdGhlIGRlY29kZWQgcGFydCAtLSBzb1xuICogPGNvZGU+dXJpLnBhcnNlKCcvZm9vJTIwYmFyJykuZ2V0UGF0aCgpPC9jb2RlPiB3aWxsIHJldHVybiB0aGUgZGVjb2RlZCBwYXRoLFxuICogPHR0Pi9mb28gYmFyPC90dD4uXG4gKlxuICogPHA+VGhlIHJhdyB2ZXJzaW9ucyBvZiBmaWVsZHMgYXJlIGF2YWlsYWJsZSB0b28uXG4gKiA8Y29kZT51cmkucGFyc2UoJy9mb28lMjBiYXInKS5nZXRSYXdQYXRoKCk8L2NvZGU+IHdpbGwgcmV0dXJuIHRoZSByYXcgcGF0aCxcbiAqIDx0dD4vZm9vJTIwYmFyPC90dD4uICBVc2UgdGhlIHJhdyBzZXR0ZXJzIHdpdGggY2FyZSwgc2luY2VcbiAqIDxjb2RlPlVSSTo6dG9TdHJpbmc8L2NvZGU+IGlzIG5vdCBndWFyYW50ZWVkIHRvIHJldHVybiBhIHZhbGlkIHVybCBpZiBhXG4gKiByYXcgc2V0dGVyIHdhcyB1c2VkLlxuICpcbiAqIDxwPkFsbCBzZXR0ZXJzIHJldHVybiA8dHQ+dGhpczwvdHQ+IGFuZCBzbyBtYXkgYmUgY2hhaW5lZCwgYSBsYVxuICogPGNvZGU+dXJpLnBhcnNlKCcvZm9vJykuc2V0RnJhZ21lbnQoJ3BhcnQnKS50b1N0cmluZygpPC9jb2RlPi5cbiAqXG4gKiA8cD5Zb3Ugc2hvdWxkIG5vdCB1c2UgdGhpcyBjb25zdHJ1Y3RvciBkaXJlY3RseSAtLSBwbGVhc2UgcHJlZmVyIHRoZSBmYWN0b3J5XG4gKiBmdW5jdGlvbnMge0BsaW5rIHVyaS5wYXJzZX0sIHtAbGluayB1cmkuY3JlYXRlfSwge0BsaW5rIHVyaS5yZXNvbHZlfVxuICogaW5zdGVhZC48L3A+XG4gKlxuICogPHA+VGhlIHBhcmFtZXRlcnMgYXJlIGFsbCByYXcgKGFzc3VtZWQgdG8gYmUgcHJvcGVybHkgZXNjYXBlZCkgcGFydHMsIGFuZFxuICogYW55IChidXQgbm90IGFsbCkgbWF5IGJlIG51bGwuICBVbmRlZmluZWQgaXMgbm90IGFsbG93ZWQuPC9wPlxuICpcbiAqIEBjb25zdHJ1Y3RvclxuICovXG5mdW5jdGlvbiBVUkkoXG4gICAgcmF3U2NoZW1lLFxuICAgIHJhd0NyZWRlbnRpYWxzLCByYXdEb21haW4sIHBvcnQsXG4gICAgcmF3UGF0aCwgcmF3UXVlcnksIHJhd0ZyYWdtZW50KSB7XG4gIHRoaXMuc2NoZW1lXyA9IHJhd1NjaGVtZTtcbiAgdGhpcy5jcmVkZW50aWFsc18gPSByYXdDcmVkZW50aWFscztcbiAgdGhpcy5kb21haW5fID0gcmF3RG9tYWluO1xuICB0aGlzLnBvcnRfID0gcG9ydDtcbiAgdGhpcy5wYXRoXyA9IHJhd1BhdGg7XG4gIHRoaXMucXVlcnlfID0gcmF3UXVlcnk7XG4gIHRoaXMuZnJhZ21lbnRfID0gcmF3RnJhZ21lbnQ7XG4gIC8qKlxuICAgKiBAdHlwZSB7QXJyYXl8bnVsbH1cbiAgICovXG4gIHRoaXMucGFyYW1DYWNoZV8gPSBudWxsO1xufVxuXG4vKiogcmV0dXJucyB0aGUgc3RyaW5nIGZvcm0gb2YgdGhlIHVybC4gKi9cblVSSS5wcm90b3R5cGUudG9TdHJpbmcgPSBmdW5jdGlvbiAoKSB7XG4gIHZhciBvdXQgPSBbXTtcbiAgaWYgKG51bGwgIT09IHRoaXMuc2NoZW1lXykgeyBvdXQucHVzaCh0aGlzLnNjaGVtZV8sICc6Jyk7IH1cbiAgaWYgKG51bGwgIT09IHRoaXMuZG9tYWluXykge1xuICAgIG91dC5wdXNoKCcvLycpO1xuICAgIGlmIChudWxsICE9PSB0aGlzLmNyZWRlbnRpYWxzXykgeyBvdXQucHVzaCh0aGlzLmNyZWRlbnRpYWxzXywgJ0AnKTsgfVxuICAgIG91dC5wdXNoKHRoaXMuZG9tYWluXyk7XG4gICAgaWYgKG51bGwgIT09IHRoaXMucG9ydF8pIHsgb3V0LnB1c2goJzonLCB0aGlzLnBvcnRfLnRvU3RyaW5nKCkpOyB9XG4gIH1cbiAgaWYgKG51bGwgIT09IHRoaXMucGF0aF8pIHsgb3V0LnB1c2godGhpcy5wYXRoXyk7IH1cbiAgaWYgKG51bGwgIT09IHRoaXMucXVlcnlfKSB7IG91dC5wdXNoKCc/JywgdGhpcy5xdWVyeV8pOyB9XG4gIGlmIChudWxsICE9PSB0aGlzLmZyYWdtZW50XykgeyBvdXQucHVzaCgnIycsIHRoaXMuZnJhZ21lbnRfKTsgfVxuICByZXR1cm4gb3V0LmpvaW4oJycpO1xufTtcblxuVVJJLnByb3RvdHlwZS5jbG9uZSA9IGZ1bmN0aW9uICgpIHtcbiAgcmV0dXJuIG5ldyBVUkkodGhpcy5zY2hlbWVfLCB0aGlzLmNyZWRlbnRpYWxzXywgdGhpcy5kb21haW5fLCB0aGlzLnBvcnRfLFxuICAgICAgICAgICAgICAgICB0aGlzLnBhdGhfLCB0aGlzLnF1ZXJ5XywgdGhpcy5mcmFnbWVudF8pO1xufTtcblxuVVJJLnByb3RvdHlwZS5nZXRTY2hlbWUgPSBmdW5jdGlvbiAoKSB7XG4gIC8vIEhUTUw1IHNwZWMgZG9lcyBub3QgcmVxdWlyZSB0aGUgc2NoZW1lIHRvIGJlIGxvd2VyY2FzZWQgYnV0XG4gIC8vIGFsbCBjb21tb24gYnJvd3NlcnMgZXhjZXB0IFNhZmFyaSBsb3dlcmNhc2UgdGhlIHNjaGVtZS5cbiAgcmV0dXJuIHRoaXMuc2NoZW1lXyAmJiBkZWNvZGVVUklDb21wb25lbnQodGhpcy5zY2hlbWVfKS50b0xvd2VyQ2FzZSgpO1xufTtcblVSSS5wcm90b3R5cGUuZ2V0UmF3U2NoZW1lID0gZnVuY3Rpb24gKCkge1xuICByZXR1cm4gdGhpcy5zY2hlbWVfO1xufTtcblVSSS5wcm90b3R5cGUuc2V0U2NoZW1lID0gZnVuY3Rpb24gKG5ld1NjaGVtZSkge1xuICB0aGlzLnNjaGVtZV8gPSBlbmNvZGVJZkV4aXN0czIoXG4gICAgICBuZXdTY2hlbWUsIFVSSV9ESVNBTExPV0VEX0lOX1NDSEVNRV9PUl9DUkVERU5USUFMU18pO1xuICByZXR1cm4gdGhpcztcbn07XG5VUkkucHJvdG90eXBlLnNldFJhd1NjaGVtZSA9IGZ1bmN0aW9uIChuZXdTY2hlbWUpIHtcbiAgdGhpcy5zY2hlbWVfID0gbmV3U2NoZW1lID8gbmV3U2NoZW1lIDogbnVsbDtcbiAgcmV0dXJuIHRoaXM7XG59O1xuVVJJLnByb3RvdHlwZS5oYXNTY2hlbWUgPSBmdW5jdGlvbiAoKSB7XG4gIHJldHVybiBudWxsICE9PSB0aGlzLnNjaGVtZV87XG59O1xuXG5cblVSSS5wcm90b3R5cGUuZ2V0Q3JlZGVudGlhbHMgPSBmdW5jdGlvbiAoKSB7XG4gIHJldHVybiB0aGlzLmNyZWRlbnRpYWxzXyAmJiBkZWNvZGVVUklDb21wb25lbnQodGhpcy5jcmVkZW50aWFsc18pO1xufTtcblVSSS5wcm90b3R5cGUuZ2V0UmF3Q3JlZGVudGlhbHMgPSBmdW5jdGlvbiAoKSB7XG4gIHJldHVybiB0aGlzLmNyZWRlbnRpYWxzXztcbn07XG5VUkkucHJvdG90eXBlLnNldENyZWRlbnRpYWxzID0gZnVuY3Rpb24gKG5ld0NyZWRlbnRpYWxzKSB7XG4gIHRoaXMuY3JlZGVudGlhbHNfID0gZW5jb2RlSWZFeGlzdHMyKFxuICAgICAgbmV3Q3JlZGVudGlhbHMsIFVSSV9ESVNBTExPV0VEX0lOX1NDSEVNRV9PUl9DUkVERU5USUFMU18pO1xuXG4gIHJldHVybiB0aGlzO1xufTtcblVSSS5wcm90b3R5cGUuc2V0UmF3Q3JlZGVudGlhbHMgPSBmdW5jdGlvbiAobmV3Q3JlZGVudGlhbHMpIHtcbiAgdGhpcy5jcmVkZW50aWFsc18gPSBuZXdDcmVkZW50aWFscyA/IG5ld0NyZWRlbnRpYWxzIDogbnVsbDtcbiAgcmV0dXJuIHRoaXM7XG59O1xuVVJJLnByb3RvdHlwZS5oYXNDcmVkZW50aWFscyA9IGZ1bmN0aW9uICgpIHtcbiAgcmV0dXJuIG51bGwgIT09IHRoaXMuY3JlZGVudGlhbHNfO1xufTtcblxuXG5VUkkucHJvdG90eXBlLmdldERvbWFpbiA9IGZ1bmN0aW9uICgpIHtcbiAgcmV0dXJuIHRoaXMuZG9tYWluXyAmJiBkZWNvZGVVUklDb21wb25lbnQodGhpcy5kb21haW5fKTtcbn07XG5VUkkucHJvdG90eXBlLmdldFJhd0RvbWFpbiA9IGZ1bmN0aW9uICgpIHtcbiAgcmV0dXJuIHRoaXMuZG9tYWluXztcbn07XG5VUkkucHJvdG90eXBlLnNldERvbWFpbiA9IGZ1bmN0aW9uIChuZXdEb21haW4pIHtcbiAgcmV0dXJuIHRoaXMuc2V0UmF3RG9tYWluKG5ld0RvbWFpbiAmJiBlbmNvZGVVUklDb21wb25lbnQobmV3RG9tYWluKSk7XG59O1xuVVJJLnByb3RvdHlwZS5zZXRSYXdEb21haW4gPSBmdW5jdGlvbiAobmV3RG9tYWluKSB7XG4gIHRoaXMuZG9tYWluXyA9IG5ld0RvbWFpbiA/IG5ld0RvbWFpbiA6IG51bGw7XG4gIC8vIE1haW50YWluIHRoZSBpbnZhcmlhbnQgdGhhdCBwYXRocyBtdXN0IHN0YXJ0IHdpdGggYSBzbGFzaCB3aGVuIHRoZSBVUklcbiAgLy8gaXMgbm90IHBhdGgtcmVsYXRpdmUuXG4gIHJldHVybiB0aGlzLnNldFJhd1BhdGgodGhpcy5wYXRoXyk7XG59O1xuVVJJLnByb3RvdHlwZS5oYXNEb21haW4gPSBmdW5jdGlvbiAoKSB7XG4gIHJldHVybiBudWxsICE9PSB0aGlzLmRvbWFpbl87XG59O1xuXG5cblVSSS5wcm90b3R5cGUuZ2V0UG9ydCA9IGZ1bmN0aW9uICgpIHtcbiAgcmV0dXJuIHRoaXMucG9ydF8gJiYgZGVjb2RlVVJJQ29tcG9uZW50KHRoaXMucG9ydF8pO1xufTtcblVSSS5wcm90b3R5cGUuc2V0UG9ydCA9IGZ1bmN0aW9uIChuZXdQb3J0KSB7XG4gIGlmIChuZXdQb3J0KSB7XG4gICAgbmV3UG9ydCA9IE51bWJlcihuZXdQb3J0KTtcbiAgICBpZiAobmV3UG9ydCAhPT0gKG5ld1BvcnQgJiAweGZmZmYpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0JhZCBwb3J0IG51bWJlciAnICsgbmV3UG9ydCk7XG4gICAgfVxuICAgIHRoaXMucG9ydF8gPSAnJyArIG5ld1BvcnQ7XG4gIH0gZWxzZSB7XG4gICAgdGhpcy5wb3J0XyA9IG51bGw7XG4gIH1cbiAgcmV0dXJuIHRoaXM7XG59O1xuVVJJLnByb3RvdHlwZS5oYXNQb3J0ID0gZnVuY3Rpb24gKCkge1xuICByZXR1cm4gbnVsbCAhPT0gdGhpcy5wb3J0Xztcbn07XG5cblxuVVJJLnByb3RvdHlwZS5nZXRQYXRoID0gZnVuY3Rpb24gKCkge1xuICByZXR1cm4gdGhpcy5wYXRoXyAmJiBkZWNvZGVVUklDb21wb25lbnQodGhpcy5wYXRoXyk7XG59O1xuVVJJLnByb3RvdHlwZS5nZXRSYXdQYXRoID0gZnVuY3Rpb24gKCkge1xuICByZXR1cm4gdGhpcy5wYXRoXztcbn07XG5VUkkucHJvdG90eXBlLnNldFBhdGggPSBmdW5jdGlvbiAobmV3UGF0aCkge1xuICByZXR1cm4gdGhpcy5zZXRSYXdQYXRoKGVuY29kZUlmRXhpc3RzMihuZXdQYXRoLCBVUklfRElTQUxMT1dFRF9JTl9QQVRIXykpO1xufTtcblVSSS5wcm90b3R5cGUuc2V0UmF3UGF0aCA9IGZ1bmN0aW9uIChuZXdQYXRoKSB7XG4gIGlmIChuZXdQYXRoKSB7XG4gICAgbmV3UGF0aCA9IFN0cmluZyhuZXdQYXRoKTtcbiAgICB0aGlzLnBhdGhfID0gXG4gICAgICAvLyBQYXRocyBtdXN0IHN0YXJ0IHdpdGggJy8nIHVubGVzcyB0aGlzIGlzIGEgcGF0aC1yZWxhdGl2ZSBVUkwuXG4gICAgICAoIXRoaXMuZG9tYWluXyB8fCAvXlxcLy8udGVzdChuZXdQYXRoKSkgPyBuZXdQYXRoIDogJy8nICsgbmV3UGF0aDtcbiAgfSBlbHNlIHtcbiAgICB0aGlzLnBhdGhfID0gbnVsbDtcbiAgfVxuICByZXR1cm4gdGhpcztcbn07XG5VUkkucHJvdG90eXBlLmhhc1BhdGggPSBmdW5jdGlvbiAoKSB7XG4gIHJldHVybiBudWxsICE9PSB0aGlzLnBhdGhfO1xufTtcblxuXG5VUkkucHJvdG90eXBlLmdldFF1ZXJ5ID0gZnVuY3Rpb24gKCkge1xuICAvLyBGcm9tIGh0dHA6Ly93d3cudzMub3JnL0FkZHJlc3NpbmcvVVJMLzRfVVJJX1JlY29tbWVudGF0aW9ucy5odG1sXG4gIC8vIFdpdGhpbiB0aGUgcXVlcnkgc3RyaW5nLCB0aGUgcGx1cyBzaWduIGlzIHJlc2VydmVkIGFzIHNob3J0aGFuZCBub3RhdGlvblxuICAvLyBmb3IgYSBzcGFjZS5cbiAgcmV0dXJuIHRoaXMucXVlcnlfICYmIGRlY29kZVVSSUNvbXBvbmVudCh0aGlzLnF1ZXJ5XykucmVwbGFjZSgvXFwrL2csICcgJyk7XG59O1xuVVJJLnByb3RvdHlwZS5nZXRSYXdRdWVyeSA9IGZ1bmN0aW9uICgpIHtcbiAgcmV0dXJuIHRoaXMucXVlcnlfO1xufTtcblVSSS5wcm90b3R5cGUuc2V0UXVlcnkgPSBmdW5jdGlvbiAobmV3UXVlcnkpIHtcbiAgdGhpcy5wYXJhbUNhY2hlXyA9IG51bGw7XG4gIHRoaXMucXVlcnlfID0gZW5jb2RlSWZFeGlzdHMobmV3UXVlcnkpO1xuICByZXR1cm4gdGhpcztcbn07XG5VUkkucHJvdG90eXBlLnNldFJhd1F1ZXJ5ID0gZnVuY3Rpb24gKG5ld1F1ZXJ5KSB7XG4gIHRoaXMucGFyYW1DYWNoZV8gPSBudWxsO1xuICB0aGlzLnF1ZXJ5XyA9IG5ld1F1ZXJ5ID8gbmV3UXVlcnkgOiBudWxsO1xuICByZXR1cm4gdGhpcztcbn07XG5VUkkucHJvdG90eXBlLmhhc1F1ZXJ5ID0gZnVuY3Rpb24gKCkge1xuICByZXR1cm4gbnVsbCAhPT0gdGhpcy5xdWVyeV87XG59O1xuXG4vKipcbiAqIHNldHMgdGhlIHF1ZXJ5IGdpdmVuIGEgbGlzdCBvZiBzdHJpbmdzIG9mIHRoZSBmb3JtXG4gKiBbIGtleTAsIHZhbHVlMCwga2V5MSwgdmFsdWUxLCAuLi4gXS5cbiAqXG4gKiA8cD48Y29kZT51cmkuc2V0QWxsUGFyYW1ldGVycyhbJ2EnLCAnYicsICdjJywgJ2QnXSkuZ2V0UXVlcnkoKTwvY29kZT5cbiAqIHdpbGwgeWllbGQgPGNvZGU+J2E9YiZjPWQnPC9jb2RlPi5cbiAqL1xuVVJJLnByb3RvdHlwZS5zZXRBbGxQYXJhbWV0ZXJzID0gZnVuY3Rpb24gKHBhcmFtcykge1xuICBpZiAodHlwZW9mIHBhcmFtcyA9PT0gJ29iamVjdCcpIHtcbiAgICBpZiAoIShwYXJhbXMgaW5zdGFuY2VvZiBBcnJheSlcbiAgICAgICAgJiYgKHBhcmFtcyBpbnN0YW5jZW9mIE9iamVjdFxuICAgICAgICAgICAgfHwgT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKHBhcmFtcykgIT09ICdbb2JqZWN0IEFycmF5XScpKSB7XG4gICAgICB2YXIgbmV3UGFyYW1zID0gW107XG4gICAgICB2YXIgaSA9IC0xO1xuICAgICAgZm9yICh2YXIgayBpbiBwYXJhbXMpIHtcbiAgICAgICAgdmFyIHYgPSBwYXJhbXNba107XG4gICAgICAgIGlmICgnc3RyaW5nJyA9PT0gdHlwZW9mIHYpIHtcbiAgICAgICAgICBuZXdQYXJhbXNbKytpXSA9IGs7XG4gICAgICAgICAgbmV3UGFyYW1zWysraV0gPSB2O1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBwYXJhbXMgPSBuZXdQYXJhbXM7XG4gICAgfVxuICB9XG4gIHRoaXMucGFyYW1DYWNoZV8gPSBudWxsO1xuICB2YXIgcXVlcnlCdWYgPSBbXTtcbiAgdmFyIHNlcGFyYXRvciA9ICcnO1xuICBmb3IgKHZhciBqID0gMDsgaiA8IHBhcmFtcy5sZW5ndGg7KSB7XG4gICAgdmFyIGsgPSBwYXJhbXNbaisrXTtcbiAgICB2YXIgdiA9IHBhcmFtc1tqKytdO1xuICAgIHF1ZXJ5QnVmLnB1c2goc2VwYXJhdG9yLCBlbmNvZGVVUklDb21wb25lbnQoay50b1N0cmluZygpKSk7XG4gICAgc2VwYXJhdG9yID0gJyYnO1xuICAgIGlmICh2KSB7XG4gICAgICBxdWVyeUJ1Zi5wdXNoKCc9JywgZW5jb2RlVVJJQ29tcG9uZW50KHYudG9TdHJpbmcoKSkpO1xuICAgIH1cbiAgfVxuICB0aGlzLnF1ZXJ5XyA9IHF1ZXJ5QnVmLmpvaW4oJycpO1xuICByZXR1cm4gdGhpcztcbn07XG5VUkkucHJvdG90eXBlLmNoZWNrUGFyYW1ldGVyQ2FjaGVfID0gZnVuY3Rpb24gKCkge1xuICBpZiAoIXRoaXMucGFyYW1DYWNoZV8pIHtcbiAgICB2YXIgcSA9IHRoaXMucXVlcnlfO1xuICAgIGlmICghcSkge1xuICAgICAgdGhpcy5wYXJhbUNhY2hlXyA9IFtdO1xuICAgIH0gZWxzZSB7XG4gICAgICB2YXIgY2dpUGFyYW1zID0gcS5zcGxpdCgvWyZcXD9dLyk7XG4gICAgICB2YXIgb3V0ID0gW107XG4gICAgICB2YXIgayA9IC0xO1xuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBjZ2lQYXJhbXMubGVuZ3RoOyArK2kpIHtcbiAgICAgICAgdmFyIG0gPSBjZ2lQYXJhbXNbaV0ubWF0Y2goL14oW149XSopKD86PSguKikpPyQvKTtcbiAgICAgICAgLy8gRnJvbSBodHRwOi8vd3d3LnczLm9yZy9BZGRyZXNzaW5nL1VSTC80X1VSSV9SZWNvbW1lbnRhdGlvbnMuaHRtbFxuICAgICAgICAvLyBXaXRoaW4gdGhlIHF1ZXJ5IHN0cmluZywgdGhlIHBsdXMgc2lnbiBpcyByZXNlcnZlZCBhcyBzaG9ydGhhbmRcbiAgICAgICAgLy8gbm90YXRpb24gZm9yIGEgc3BhY2UuXG4gICAgICAgIG91dFsrK2tdID0gZGVjb2RlVVJJQ29tcG9uZW50KG1bMV0pLnJlcGxhY2UoL1xcKy9nLCAnICcpO1xuICAgICAgICBvdXRbKytrXSA9IGRlY29kZVVSSUNvbXBvbmVudChtWzJdIHx8ICcnKS5yZXBsYWNlKC9cXCsvZywgJyAnKTtcbiAgICAgIH1cbiAgICAgIHRoaXMucGFyYW1DYWNoZV8gPSBvdXQ7XG4gICAgfVxuICB9XG59O1xuLyoqXG4gKiBzZXRzIHRoZSB2YWx1ZXMgb2YgdGhlIG5hbWVkIGNnaSBwYXJhbWV0ZXJzLlxuICpcbiAqIDxwPlNvLCA8Y29kZT51cmkucGFyc2UoJ2Zvbz9hPWImYz1kJmU9ZicpLnNldFBhcmFtZXRlclZhbHVlcygnYycsIFsnbmV3J10pXG4gKiA8L2NvZGU+IHlpZWxkcyA8dHQ+Zm9vP2E9YiZjPW5ldyZlPWY8L3R0Pi48L3A+XG4gKlxuICogQHBhcmFtIGtleSB7c3RyaW5nfVxuICogQHBhcmFtIHZhbHVlcyB7QXJyYXkuPHN0cmluZz59IHRoZSBuZXcgdmFsdWVzLiAgSWYgdmFsdWVzIGlzIGEgc2luZ2xlIHN0cmluZ1xuICogICB0aGVuIGl0IHdpbGwgYmUgdHJlYXRlZCBhcyB0aGUgc29sZSB2YWx1ZS5cbiAqL1xuVVJJLnByb3RvdHlwZS5zZXRQYXJhbWV0ZXJWYWx1ZXMgPSBmdW5jdGlvbiAoa2V5LCB2YWx1ZXMpIHtcbiAgLy8gYmUgbmljZSBhbmQgYXZvaWQgc3VidGxlIGJ1Z3Mgd2hlcmUgW10gb3BlcmF0b3Igb24gc3RyaW5nIHBlcmZvcm1zIGNoYXJBdFxuICAvLyBvbiBzb21lIGJyb3dzZXJzIGFuZCBjcmFzaGVzIG9uIElFXG4gIGlmICh0eXBlb2YgdmFsdWVzID09PSAnc3RyaW5nJykge1xuICAgIHZhbHVlcyA9IFsgdmFsdWVzIF07XG4gIH1cblxuICB0aGlzLmNoZWNrUGFyYW1ldGVyQ2FjaGVfKCk7XG4gIHZhciBuZXdWYWx1ZUluZGV4ID0gMDtcbiAgdmFyIHBjID0gdGhpcy5wYXJhbUNhY2hlXztcbiAgdmFyIHBhcmFtcyA9IFtdO1xuICBmb3IgKHZhciBpID0gMCwgayA9IDA7IGkgPCBwYy5sZW5ndGg7IGkgKz0gMikge1xuICAgIGlmIChrZXkgPT09IHBjW2ldKSB7XG4gICAgICBpZiAobmV3VmFsdWVJbmRleCA8IHZhbHVlcy5sZW5ndGgpIHtcbiAgICAgICAgcGFyYW1zLnB1c2goa2V5LCB2YWx1ZXNbbmV3VmFsdWVJbmRleCsrXSk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHBhcmFtcy5wdXNoKHBjW2ldLCBwY1tpICsgMV0pO1xuICAgIH1cbiAgfVxuICB3aGlsZSAobmV3VmFsdWVJbmRleCA8IHZhbHVlcy5sZW5ndGgpIHtcbiAgICBwYXJhbXMucHVzaChrZXksIHZhbHVlc1tuZXdWYWx1ZUluZGV4KytdKTtcbiAgfVxuICB0aGlzLnNldEFsbFBhcmFtZXRlcnMocGFyYW1zKTtcbiAgcmV0dXJuIHRoaXM7XG59O1xuVVJJLnByb3RvdHlwZS5yZW1vdmVQYXJhbWV0ZXIgPSBmdW5jdGlvbiAoa2V5KSB7XG4gIHJldHVybiB0aGlzLnNldFBhcmFtZXRlclZhbHVlcyhrZXksIFtdKTtcbn07XG4vKipcbiAqIHJldHVybnMgdGhlIHBhcmFtZXRlcnMgc3BlY2lmaWVkIGluIHRoZSBxdWVyeSBwYXJ0IG9mIHRoZSB1cmkgYXMgYSBsaXN0IG9mXG4gKiBrZXlzIGFuZCB2YWx1ZXMgbGlrZSBbIGtleTAsIHZhbHVlMCwga2V5MSwgdmFsdWUxLCAuLi4gXS5cbiAqXG4gKiBAcmV0dXJuIHtBcnJheS48c3RyaW5nPn1cbiAqL1xuVVJJLnByb3RvdHlwZS5nZXRBbGxQYXJhbWV0ZXJzID0gZnVuY3Rpb24gKCkge1xuICB0aGlzLmNoZWNrUGFyYW1ldGVyQ2FjaGVfKCk7XG4gIHJldHVybiB0aGlzLnBhcmFtQ2FjaGVfLnNsaWNlKDAsIHRoaXMucGFyYW1DYWNoZV8ubGVuZ3RoKTtcbn07XG4vKipcbiAqIHJldHVybnMgdGhlIHZhbHVlPGI+czwvYj4gZm9yIGEgZ2l2ZW4gY2dpIHBhcmFtZXRlciBhcyBhIGxpc3Qgb2YgZGVjb2RlZFxuICogcXVlcnkgcGFyYW1ldGVyIHZhbHVlcy5cbiAqIEByZXR1cm4ge0FycmF5LjxzdHJpbmc+fVxuICovXG5VUkkucHJvdG90eXBlLmdldFBhcmFtZXRlclZhbHVlcyA9IGZ1bmN0aW9uIChwYXJhbU5hbWVVbmVzY2FwZWQpIHtcbiAgdGhpcy5jaGVja1BhcmFtZXRlckNhY2hlXygpO1xuICB2YXIgdmFsdWVzID0gW107XG4gIGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy5wYXJhbUNhY2hlXy5sZW5ndGg7IGkgKz0gMikge1xuICAgIGlmIChwYXJhbU5hbWVVbmVzY2FwZWQgPT09IHRoaXMucGFyYW1DYWNoZV9baV0pIHtcbiAgICAgIHZhbHVlcy5wdXNoKHRoaXMucGFyYW1DYWNoZV9baSArIDFdKTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIHZhbHVlcztcbn07XG4vKipcbiAqIHJldHVybnMgYSBtYXAgb2YgY2dpIHBhcmFtZXRlciBuYW1lcyB0byAobm9uLWVtcHR5KSBsaXN0cyBvZiB2YWx1ZXMuXG4gKiBAcmV0dXJuIHtPYmplY3QuPHN0cmluZyxBcnJheS48c3RyaW5nPj59XG4gKi9cblVSSS5wcm90b3R5cGUuZ2V0UGFyYW1ldGVyTWFwID0gZnVuY3Rpb24gKHBhcmFtTmFtZVVuZXNjYXBlZCkge1xuICB0aGlzLmNoZWNrUGFyYW1ldGVyQ2FjaGVfKCk7XG4gIHZhciBwYXJhbU1hcCA9IHt9O1xuICBmb3IgKHZhciBpID0gMDsgaSA8IHRoaXMucGFyYW1DYWNoZV8ubGVuZ3RoOyBpICs9IDIpIHtcbiAgICB2YXIga2V5ID0gdGhpcy5wYXJhbUNhY2hlX1tpKytdLFxuICAgICAgdmFsdWUgPSB0aGlzLnBhcmFtQ2FjaGVfW2krK107XG4gICAgaWYgKCEoa2V5IGluIHBhcmFtTWFwKSkge1xuICAgICAgcGFyYW1NYXBba2V5XSA9IFt2YWx1ZV07XG4gICAgfSBlbHNlIHtcbiAgICAgIHBhcmFtTWFwW2tleV0ucHVzaCh2YWx1ZSk7XG4gICAgfVxuICB9XG4gIHJldHVybiBwYXJhbU1hcDtcbn07XG4vKipcbiAqIHJldHVybnMgdGhlIGZpcnN0IHZhbHVlIGZvciBhIGdpdmVuIGNnaSBwYXJhbWV0ZXIgb3IgbnVsbCBpZiB0aGUgZ2l2ZW5cbiAqIHBhcmFtZXRlciBuYW1lIGRvZXMgbm90IGFwcGVhciBpbiB0aGUgcXVlcnkgc3RyaW5nLlxuICogSWYgdGhlIGdpdmVuIHBhcmFtZXRlciBuYW1lIGRvZXMgYXBwZWFyLCBidXQgaGFzIG5vICc8dHQ+PTwvdHQ+JyBmb2xsb3dpbmdcbiAqIGl0LCB0aGVuIHRoZSBlbXB0eSBzdHJpbmcgd2lsbCBiZSByZXR1cm5lZC5cbiAqIEByZXR1cm4ge3N0cmluZ3xudWxsfVxuICovXG5VUkkucHJvdG90eXBlLmdldFBhcmFtZXRlclZhbHVlID0gZnVuY3Rpb24gKHBhcmFtTmFtZVVuZXNjYXBlZCkge1xuICB0aGlzLmNoZWNrUGFyYW1ldGVyQ2FjaGVfKCk7XG4gIGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy5wYXJhbUNhY2hlXy5sZW5ndGg7IGkgKz0gMikge1xuICAgIGlmIChwYXJhbU5hbWVVbmVzY2FwZWQgPT09IHRoaXMucGFyYW1DYWNoZV9baV0pIHtcbiAgICAgIHJldHVybiB0aGlzLnBhcmFtQ2FjaGVfW2kgKyAxXTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIG51bGw7XG59O1xuXG5VUkkucHJvdG90eXBlLmdldEZyYWdtZW50ID0gZnVuY3Rpb24gKCkge1xuICByZXR1cm4gdGhpcy5mcmFnbWVudF8gJiYgZGVjb2RlVVJJQ29tcG9uZW50KHRoaXMuZnJhZ21lbnRfKTtcbn07XG5VUkkucHJvdG90eXBlLmdldFJhd0ZyYWdtZW50ID0gZnVuY3Rpb24gKCkge1xuICByZXR1cm4gdGhpcy5mcmFnbWVudF87XG59O1xuVVJJLnByb3RvdHlwZS5zZXRGcmFnbWVudCA9IGZ1bmN0aW9uIChuZXdGcmFnbWVudCkge1xuICB0aGlzLmZyYWdtZW50XyA9IG5ld0ZyYWdtZW50ID8gZW5jb2RlVVJJQ29tcG9uZW50KG5ld0ZyYWdtZW50KSA6IG51bGw7XG4gIHJldHVybiB0aGlzO1xufTtcblVSSS5wcm90b3R5cGUuc2V0UmF3RnJhZ21lbnQgPSBmdW5jdGlvbiAobmV3RnJhZ21lbnQpIHtcbiAgdGhpcy5mcmFnbWVudF8gPSBuZXdGcmFnbWVudCA/IG5ld0ZyYWdtZW50IDogbnVsbDtcbiAgcmV0dXJuIHRoaXM7XG59O1xuVVJJLnByb3RvdHlwZS5oYXNGcmFnbWVudCA9IGZ1bmN0aW9uICgpIHtcbiAgcmV0dXJuIG51bGwgIT09IHRoaXMuZnJhZ21lbnRfO1xufTtcblxuZnVuY3Rpb24gbnVsbElmQWJzZW50KG1hdGNoUGFydCkge1xuICByZXR1cm4gKCdzdHJpbmcnID09IHR5cGVvZiBtYXRjaFBhcnQpICYmIChtYXRjaFBhcnQubGVuZ3RoID4gMClcbiAgICAgICAgID8gbWF0Y2hQYXJ0XG4gICAgICAgICA6IG51bGw7XG59XG5cblxuXG5cbi8qKlxuICogYSByZWd1bGFyIGV4cHJlc3Npb24gZm9yIGJyZWFraW5nIGEgVVJJIGludG8gaXRzIGNvbXBvbmVudCBwYXJ0cy5cbiAqXG4gKiA8cD5odHRwOi8vd3d3LmdiaXYuY29tL3Byb3RvY29scy91cmkvcmZjL3JmYzM5ODYuaHRtbCNSRkMyMjM0IHNheXNcbiAqIEFzIHRoZSBcImZpcnN0LW1hdGNoLXdpbnNcIiBhbGdvcml0aG0gaXMgaWRlbnRpY2FsIHRvIHRoZSBcImdyZWVkeVwiXG4gKiBkaXNhbWJpZ3VhdGlvbiBtZXRob2QgdXNlZCBieSBQT1NJWCByZWd1bGFyIGV4cHJlc3Npb25zLCBpdCBpcyBuYXR1cmFsIGFuZFxuICogY29tbW9ucGxhY2UgdG8gdXNlIGEgcmVndWxhciBleHByZXNzaW9uIGZvciBwYXJzaW5nIHRoZSBwb3RlbnRpYWwgZml2ZVxuICogY29tcG9uZW50cyBvZiBhIFVSSSByZWZlcmVuY2UuXG4gKlxuICogPHA+VGhlIGZvbGxvd2luZyBsaW5lIGlzIHRoZSByZWd1bGFyIGV4cHJlc3Npb24gZm9yIGJyZWFraW5nLWRvd24gYVxuICogd2VsbC1mb3JtZWQgVVJJIHJlZmVyZW5jZSBpbnRvIGl0cyBjb21wb25lbnRzLlxuICpcbiAqIDxwcmU+XG4gKiBeKChbXjovPyNdKyk6KT8oLy8oW14vPyNdKikpPyhbXj8jXSopKFxcPyhbXiNdKikpPygjKC4qKSk/XG4gKiAgMTIgICAgICAgICAgICAzICA0ICAgICAgICAgIDUgICAgICAgNiAgNyAgICAgICAgOCA5XG4gKiA8L3ByZT5cbiAqXG4gKiA8cD5UaGUgbnVtYmVycyBpbiB0aGUgc2Vjb25kIGxpbmUgYWJvdmUgYXJlIG9ubHkgdG8gYXNzaXN0IHJlYWRhYmlsaXR5OyB0aGV5XG4gKiBpbmRpY2F0ZSB0aGUgcmVmZXJlbmNlIHBvaW50cyBmb3IgZWFjaCBzdWJleHByZXNzaW9uIChpLmUuLCBlYWNoIHBhaXJlZFxuICogcGFyZW50aGVzaXMpLiBXZSByZWZlciB0byB0aGUgdmFsdWUgbWF0Y2hlZCBmb3Igc3ViZXhwcmVzc2lvbiA8bj4gYXMgJDxuPi5cbiAqIEZvciBleGFtcGxlLCBtYXRjaGluZyB0aGUgYWJvdmUgZXhwcmVzc2lvbiB0b1xuICogPHByZT5cbiAqICAgICBodHRwOi8vd3d3Lmljcy51Y2kuZWR1L3B1Yi9pZXRmL3VyaS8jUmVsYXRlZFxuICogPC9wcmU+XG4gKiByZXN1bHRzIGluIHRoZSBmb2xsb3dpbmcgc3ViZXhwcmVzc2lvbiBtYXRjaGVzOlxuICogPHByZT5cbiAqICAgICQxID0gaHR0cDpcbiAqICAgICQyID0gaHR0cFxuICogICAgJDMgPSAvL3d3dy5pY3MudWNpLmVkdVxuICogICAgJDQgPSB3d3cuaWNzLnVjaS5lZHVcbiAqICAgICQ1ID0gL3B1Yi9pZXRmL3VyaS9cbiAqICAgICQ2ID0gPHVuZGVmaW5lZD5cbiAqICAgICQ3ID0gPHVuZGVmaW5lZD5cbiAqICAgICQ4ID0gI1JlbGF0ZWRcbiAqICAgICQ5ID0gUmVsYXRlZFxuICogPC9wcmU+XG4gKiB3aGVyZSA8dW5kZWZpbmVkPiBpbmRpY2F0ZXMgdGhhdCB0aGUgY29tcG9uZW50IGlzIG5vdCBwcmVzZW50LCBhcyBpcyB0aGVcbiAqIGNhc2UgZm9yIHRoZSBxdWVyeSBjb21wb25lbnQgaW4gdGhlIGFib3ZlIGV4YW1wbGUuIFRoZXJlZm9yZSwgd2UgY2FuXG4gKiBkZXRlcm1pbmUgdGhlIHZhbHVlIG9mIHRoZSBmaXZlIGNvbXBvbmVudHMgYXNcbiAqIDxwcmU+XG4gKiAgICBzY2hlbWUgICAgPSAkMlxuICogICAgYXV0aG9yaXR5ID0gJDRcbiAqICAgIHBhdGggICAgICA9ICQ1XG4gKiAgICBxdWVyeSAgICAgPSAkN1xuICogICAgZnJhZ21lbnQgID0gJDlcbiAqIDwvcHJlPlxuICpcbiAqIDxwPm1zYW11ZWw6IEkgaGF2ZSBtb2RpZmllZCB0aGUgcmVndWxhciBleHByZXNzaW9uIHNsaWdodGx5IHRvIGV4cG9zZSB0aGVcbiAqIGNyZWRlbnRpYWxzLCBkb21haW4sIGFuZCBwb3J0IHNlcGFyYXRlbHkgZnJvbSB0aGUgYXV0aG9yaXR5LlxuICogVGhlIG1vZGlmaWVkIHZlcnNpb24geWllbGRzXG4gKiA8cHJlPlxuICogICAgJDEgPSBodHRwICAgICAgICAgICAgICBzY2hlbWVcbiAqICAgICQyID0gPHVuZGVmaW5lZD4gICAgICAgY3JlZGVudGlhbHMgLVxcXG4gKiAgICAkMyA9IHd3dy5pY3MudWNpLmVkdSAgIGRvbWFpbiAgICAgICB8IGF1dGhvcml0eVxuICogICAgJDQgPSA8dW5kZWZpbmVkPiAgICAgICBwb3J0ICAgICAgICAtL1xuICogICAgJDUgPSAvcHViL2lldGYvdXJpLyAgICBwYXRoXG4gKiAgICAkNiA9IDx1bmRlZmluZWQ+ICAgICAgIHF1ZXJ5IHdpdGhvdXQgP1xuICogICAgJDcgPSBSZWxhdGVkICAgICAgICAgICBmcmFnbWVudCB3aXRob3V0ICNcbiAqIDwvcHJlPlxuICovXG52YXIgVVJJX1JFXyA9IG5ldyBSZWdFeHAoXG4gICAgICBcIl5cIiArXG4gICAgICBcIig/OlwiICtcbiAgICAgICAgXCIoW146Lz8jXSspXCIgKyAgICAgICAgIC8vIHNjaGVtZVxuICAgICAgXCI6KT9cIiArXG4gICAgICBcIig/Oi8vXCIgK1xuICAgICAgICBcIig/OihbXi8/I10qKUApP1wiICsgICAgLy8gY3JlZGVudGlhbHNcbiAgICAgICAgXCIoW14vPyM6QF0qKVwiICsgICAgICAgIC8vIGRvbWFpblxuICAgICAgICBcIig/OjooWzAtOV0rKSk/XCIgKyAgICAgLy8gcG9ydFxuICAgICAgXCIpP1wiICtcbiAgICAgIFwiKFtePyNdKyk/XCIgKyAgICAgICAgICAgIC8vIHBhdGhcbiAgICAgIFwiKD86XFxcXD8oW14jXSopKT9cIiArICAgICAgLy8gcXVlcnlcbiAgICAgIFwiKD86IyguKikpP1wiICsgICAgICAgICAgIC8vIGZyYWdtZW50XG4gICAgICBcIiRcIlxuICAgICAgKTtcblxudmFyIFVSSV9ESVNBTExPV0VEX0lOX1NDSEVNRV9PUl9DUkVERU5USUFMU18gPSAvWyNcXC9cXD9AXS9nO1xudmFyIFVSSV9ESVNBTExPV0VEX0lOX1BBVEhfID0gL1tcXCNcXD9dL2c7XG5cblVSSS5wYXJzZSA9IHBhcnNlO1xuVVJJLmNyZWF0ZSA9IGNyZWF0ZTtcblVSSS5yZXNvbHZlID0gcmVzb2x2ZTtcblVSSS5jb2xsYXBzZV9kb3RzID0gY29sbGFwc2VfZG90czsgIC8vIFZpc2libGUgZm9yIHRlc3RpbmcuXG5cbi8vIGxpZ2h0d2VpZ2h0IHN0cmluZy1iYXNlZCBhcGkgZm9yIGxvYWRNb2R1bGVNYWtlclxuVVJJLnV0aWxzID0ge1xuICBtaW1lVHlwZU9mOiBmdW5jdGlvbiAodXJpKSB7XG4gICAgdmFyIHVyaU9iaiA9IHBhcnNlKHVyaSk7XG4gICAgaWYgKC9cXC5odG1sJC8udGVzdCh1cmlPYmouZ2V0UGF0aCgpKSkge1xuICAgICAgcmV0dXJuICd0ZXh0L2h0bWwnO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gJ2FwcGxpY2F0aW9uL2phdmFzY3JpcHQnO1xuICAgIH1cbiAgfSxcbiAgcmVzb2x2ZTogZnVuY3Rpb24gKGJhc2UsIHVyaSkge1xuICAgIGlmIChiYXNlKSB7XG4gICAgICByZXR1cm4gcmVzb2x2ZShwYXJzZShiYXNlKSwgcGFyc2UodXJpKSkudG9TdHJpbmcoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuICcnICsgdXJpO1xuICAgIH1cbiAgfVxufTtcblxuXG5yZXR1cm4gVVJJO1xufSkoKTtcblxuLy8gQ29weXJpZ2h0IEdvb2dsZSBJbmMuXG4vLyBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2VuY2UgVmVyc2lvbiAyLjBcbi8vIEF1dG9nZW5lcmF0ZWQgYXQgTW9uIEZlYiAyNSAxMzowNTo0MiBFU1QgMjAxM1xuLy8gQG92ZXJyaWRlcyB3aW5kb3dcbi8vIEBwcm92aWRlcyBodG1sNFxudmFyIGh0bWw0ID0ge307XG5odG1sNC5hdHlwZSA9IHtcbiAgJ05PTkUnOiAwLFxuICAnVVJJJzogMSxcbiAgJ1VSSV9GUkFHTUVOVCc6IDExLFxuICAnU0NSSVBUJzogMixcbiAgJ1NUWUxFJzogMyxcbiAgJ0hUTUwnOiAxMixcbiAgJ0lEJzogNCxcbiAgJ0lEUkVGJzogNSxcbiAgJ0lEUkVGUyc6IDYsXG4gICdHTE9CQUxfTkFNRSc6IDcsXG4gICdMT0NBTF9OQU1FJzogOCxcbiAgJ0NMQVNTRVMnOiA5LFxuICAnRlJBTUVfVEFSR0VUJzogMTAsXG4gICdNRURJQV9RVUVSWSc6IDEzXG59O1xuaHRtbDRbICdhdHlwZScgXSA9IGh0bWw0LmF0eXBlO1xuaHRtbDQuQVRUUklCUyA9IHtcbiAgJyo6OmNsYXNzJzogOSxcbiAgJyo6OmRpcic6IDAsXG4gICcqOjpkcmFnZ2FibGUnOiAwLFxuICAnKjo6aGlkZGVuJzogMCxcbiAgJyo6OmlkJzogNCxcbiAgJyo6OmluZXJ0JzogMCxcbiAgJyo6Oml0ZW1wcm9wJzogMCxcbiAgJyo6Oml0ZW1yZWYnOiA2LFxuICAnKjo6aXRlbXNjb3BlJzogMCxcbiAgJyo6OmxhbmcnOiAwLFxuICAnKjo6b25ibHVyJzogMixcbiAgJyo6Om9uY2hhbmdlJzogMixcbiAgJyo6Om9uY2xpY2snOiAyLFxuICAnKjo6b25kYmxjbGljayc6IDIsXG4gICcqOjpvbmZvY3VzJzogMixcbiAgJyo6Om9ua2V5ZG93bic6IDIsXG4gICcqOjpvbmtleXByZXNzJzogMixcbiAgJyo6Om9ua2V5dXAnOiAyLFxuICAnKjo6b25sb2FkJzogMixcbiAgJyo6Om9ubW91c2Vkb3duJzogMixcbiAgJyo6Om9ubW91c2Vtb3ZlJzogMixcbiAgJyo6Om9ubW91c2VvdXQnOiAyLFxuICAnKjo6b25tb3VzZW92ZXInOiAyLFxuICAnKjo6b25tb3VzZXVwJzogMixcbiAgJyo6Om9ucmVzZXQnOiAyLFxuICAnKjo6b25zY3JvbGwnOiAyLFxuICAnKjo6b25zZWxlY3QnOiAyLFxuICAnKjo6b25zdWJtaXQnOiAyLFxuICAnKjo6b251bmxvYWQnOiAyLFxuICAnKjo6c3BlbGxjaGVjayc6IDAsXG4gICcqOjpzdHlsZSc6IDMsXG4gICcqOjp0aXRsZSc6IDAsXG4gICcqOjp0cmFuc2xhdGUnOiAwLFxuICAnYTo6YWNjZXNza2V5JzogMCxcbiAgJ2E6OmNvb3Jkcyc6IDAsXG4gICdhOjpocmVmJzogMSxcbiAgJ2E6OmhyZWZsYW5nJzogMCxcbiAgJ2E6Om5hbWUnOiA3LFxuICAnYTo6b25ibHVyJzogMixcbiAgJ2E6Om9uZm9jdXMnOiAyLFxuICAnYTo6c2hhcGUnOiAwLFxuICAnYTo6dGFiaW5kZXgnOiAwLFxuICAnYTo6dGFyZ2V0JzogMTAsXG4gICdhOjp0eXBlJzogMCxcbiAgJ2FyZWE6OmFjY2Vzc2tleSc6IDAsXG4gICdhcmVhOjphbHQnOiAwLFxuICAnYXJlYTo6Y29vcmRzJzogMCxcbiAgJ2FyZWE6OmhyZWYnOiAxLFxuICAnYXJlYTo6bm9ocmVmJzogMCxcbiAgJ2FyZWE6Om9uYmx1cic6IDIsXG4gICdhcmVhOjpvbmZvY3VzJzogMixcbiAgJ2FyZWE6OnNoYXBlJzogMCxcbiAgJ2FyZWE6OnRhYmluZGV4JzogMCxcbiAgJ2FyZWE6OnRhcmdldCc6IDEwLFxuICAnYXVkaW86OmNvbnRyb2xzJzogMCxcbiAgJ2F1ZGlvOjpsb29wJzogMCxcbiAgJ2F1ZGlvOjptZWRpYWdyb3VwJzogNSxcbiAgJ2F1ZGlvOjptdXRlZCc6IDAsXG4gICdhdWRpbzo6cHJlbG9hZCc6IDAsXG4gICdiZG86OmRpcic6IDAsXG4gICdibG9ja3F1b3RlOjpjaXRlJzogMSxcbiAgJ2JyOjpjbGVhcic6IDAsXG4gICdidXR0b246OmFjY2Vzc2tleSc6IDAsXG4gICdidXR0b246OmRpc2FibGVkJzogMCxcbiAgJ2J1dHRvbjo6bmFtZSc6IDgsXG4gICdidXR0b246Om9uYmx1cic6IDIsXG4gICdidXR0b246Om9uZm9jdXMnOiAyLFxuICAnYnV0dG9uOjp0YWJpbmRleCc6IDAsXG4gICdidXR0b246OnR5cGUnOiAwLFxuICAnYnV0dG9uOjp2YWx1ZSc6IDAsXG4gICdjYW52YXM6OmhlaWdodCc6IDAsXG4gICdjYW52YXM6OndpZHRoJzogMCxcbiAgJ2NhcHRpb246OmFsaWduJzogMCxcbiAgJ2NvbDo6YWxpZ24nOiAwLFxuICAnY29sOjpjaGFyJzogMCxcbiAgJ2NvbDo6Y2hhcm9mZic6IDAsXG4gICdjb2w6OnNwYW4nOiAwLFxuICAnY29sOjp2YWxpZ24nOiAwLFxuICAnY29sOjp3aWR0aCc6IDAsXG4gICdjb2xncm91cDo6YWxpZ24nOiAwLFxuICAnY29sZ3JvdXA6OmNoYXInOiAwLFxuICAnY29sZ3JvdXA6OmNoYXJvZmYnOiAwLFxuICAnY29sZ3JvdXA6OnNwYW4nOiAwLFxuICAnY29sZ3JvdXA6OnZhbGlnbic6IDAsXG4gICdjb2xncm91cDo6d2lkdGgnOiAwLFxuICAnY29tbWFuZDo6Y2hlY2tlZCc6IDAsXG4gICdjb21tYW5kOjpjb21tYW5kJzogNSxcbiAgJ2NvbW1hbmQ6OmRpc2FibGVkJzogMCxcbiAgJ2NvbW1hbmQ6Omljb24nOiAxLFxuICAnY29tbWFuZDo6bGFiZWwnOiAwLFxuICAnY29tbWFuZDo6cmFkaW9ncm91cCc6IDAsXG4gICdjb21tYW5kOjp0eXBlJzogMCxcbiAgJ2RhdGE6OnZhbHVlJzogMCxcbiAgJ2RlbDo6Y2l0ZSc6IDEsXG4gICdkZWw6OmRhdGV0aW1lJzogMCxcbiAgJ2RldGFpbHM6Om9wZW4nOiAwLFxuICAnZGlyOjpjb21wYWN0JzogMCxcbiAgJ2Rpdjo6YWxpZ24nOiAwLFxuICAnZGw6OmNvbXBhY3QnOiAwLFxuICAnZmllbGRzZXQ6OmRpc2FibGVkJzogMCxcbiAgJ2ZvbnQ6OmNvbG9yJzogMCxcbiAgJ2ZvbnQ6OmZhY2UnOiAwLFxuICAnZm9udDo6c2l6ZSc6IDAsXG4gICdmb3JtOjphY2NlcHQnOiAwLFxuICAnZm9ybTo6YWN0aW9uJzogMSxcbiAgJ2Zvcm06OmF1dG9jb21wbGV0ZSc6IDAsXG4gICdmb3JtOjplbmN0eXBlJzogMCxcbiAgJ2Zvcm06Om1ldGhvZCc6IDAsXG4gICdmb3JtOjpuYW1lJzogNyxcbiAgJ2Zvcm06Om5vdmFsaWRhdGUnOiAwLFxuICAnZm9ybTo6b25yZXNldCc6IDIsXG4gICdmb3JtOjpvbnN1Ym1pdCc6IDIsXG4gICdmb3JtOjp0YXJnZXQnOiAxMCxcbiAgJ2gxOjphbGlnbic6IDAsXG4gICdoMjo6YWxpZ24nOiAwLFxuICAnaDM6OmFsaWduJzogMCxcbiAgJ2g0OjphbGlnbic6IDAsXG4gICdoNTo6YWxpZ24nOiAwLFxuICAnaDY6OmFsaWduJzogMCxcbiAgJ2hyOjphbGlnbic6IDAsXG4gICdocjo6bm9zaGFkZSc6IDAsXG4gICdocjo6c2l6ZSc6IDAsXG4gICdocjo6d2lkdGgnOiAwLFxuICAnaWZyYW1lOjphbGlnbic6IDAsXG4gICdpZnJhbWU6OmZyYW1lYm9yZGVyJzogMCxcbiAgJ2lmcmFtZTo6aGVpZ2h0JzogMCxcbiAgJ2lmcmFtZTo6bWFyZ2luaGVpZ2h0JzogMCxcbiAgJ2lmcmFtZTo6bWFyZ2lud2lkdGgnOiAwLFxuICAnaWZyYW1lOjp3aWR0aCc6IDAsXG4gICdpbWc6OmFsaWduJzogMCxcbiAgJ2ltZzo6YWx0JzogMCxcbiAgJ2ltZzo6Ym9yZGVyJzogMCxcbiAgJ2ltZzo6aGVpZ2h0JzogMCxcbiAgJ2ltZzo6aHNwYWNlJzogMCxcbiAgJ2ltZzo6aXNtYXAnOiAwLFxuICAnaW1nOjpuYW1lJzogNyxcbiAgJ2ltZzo6c3JjJzogMSxcbiAgJ2ltZzo6dXNlbWFwJzogMTEsXG4gICdpbWc6OnZzcGFjZSc6IDAsXG4gICdpbWc6OndpZHRoJzogMCxcbiAgJ2lucHV0OjphY2NlcHQnOiAwLFxuICAnaW5wdXQ6OmFjY2Vzc2tleSc6IDAsXG4gICdpbnB1dDo6YWxpZ24nOiAwLFxuICAnaW5wdXQ6OmFsdCc6IDAsXG4gICdpbnB1dDo6YXV0b2NvbXBsZXRlJzogMCxcbiAgJ2lucHV0OjpjaGVja2VkJzogMCxcbiAgJ2lucHV0OjpkaXNhYmxlZCc6IDAsXG4gICdpbnB1dDo6aW5wdXRtb2RlJzogMCxcbiAgJ2lucHV0Ojppc21hcCc6IDAsXG4gICdpbnB1dDo6bGlzdCc6IDUsXG4gICdpbnB1dDo6bWF4JzogMCxcbiAgJ2lucHV0OjptYXhsZW5ndGgnOiAwLFxuICAnaW5wdXQ6Om1pbic6IDAsXG4gICdpbnB1dDo6bXVsdGlwbGUnOiAwLFxuICAnaW5wdXQ6Om5hbWUnOiA4LFxuICAnaW5wdXQ6Om9uYmx1cic6IDIsXG4gICdpbnB1dDo6b25jaGFuZ2UnOiAyLFxuICAnaW5wdXQ6Om9uZm9jdXMnOiAyLFxuICAnaW5wdXQ6Om9uc2VsZWN0JzogMixcbiAgJ2lucHV0OjpwbGFjZWhvbGRlcic6IDAsXG4gICdpbnB1dDo6cmVhZG9ubHknOiAwLFxuICAnaW5wdXQ6OnJlcXVpcmVkJzogMCxcbiAgJ2lucHV0OjpzaXplJzogMCxcbiAgJ2lucHV0OjpzcmMnOiAxLFxuICAnaW5wdXQ6OnN0ZXAnOiAwLFxuICAnaW5wdXQ6OnRhYmluZGV4JzogMCxcbiAgJ2lucHV0Ojp0eXBlJzogMCxcbiAgJ2lucHV0Ojp1c2VtYXAnOiAxMSxcbiAgJ2lucHV0Ojp2YWx1ZSc6IDAsXG4gICdpbnM6OmNpdGUnOiAxLFxuICAnaW5zOjpkYXRldGltZSc6IDAsXG4gICdsYWJlbDo6YWNjZXNza2V5JzogMCxcbiAgJ2xhYmVsOjpmb3InOiA1LFxuICAnbGFiZWw6Om9uYmx1cic6IDIsXG4gICdsYWJlbDo6b25mb2N1cyc6IDIsXG4gICdsZWdlbmQ6OmFjY2Vzc2tleSc6IDAsXG4gICdsZWdlbmQ6OmFsaWduJzogMCxcbiAgJ2xpOjp0eXBlJzogMCxcbiAgJ2xpOjp2YWx1ZSc6IDAsXG4gICdtYXA6Om5hbWUnOiA3LFxuICAnbWVudTo6Y29tcGFjdCc6IDAsXG4gICdtZW51OjpsYWJlbCc6IDAsXG4gICdtZW51Ojp0eXBlJzogMCxcbiAgJ21ldGVyOjpoaWdoJzogMCxcbiAgJ21ldGVyOjpsb3cnOiAwLFxuICAnbWV0ZXI6Om1heCc6IDAsXG4gICdtZXRlcjo6bWluJzogMCxcbiAgJ21ldGVyOjp2YWx1ZSc6IDAsXG4gICdvbDo6Y29tcGFjdCc6IDAsXG4gICdvbDo6cmV2ZXJzZWQnOiAwLFxuICAnb2w6OnN0YXJ0JzogMCxcbiAgJ29sOjp0eXBlJzogMCxcbiAgJ29wdGdyb3VwOjpkaXNhYmxlZCc6IDAsXG4gICdvcHRncm91cDo6bGFiZWwnOiAwLFxuICAnb3B0aW9uOjpkaXNhYmxlZCc6IDAsXG4gICdvcHRpb246OmxhYmVsJzogMCxcbiAgJ29wdGlvbjo6c2VsZWN0ZWQnOiAwLFxuICAnb3B0aW9uOjp2YWx1ZSc6IDAsXG4gICdvdXRwdXQ6OmZvcic6IDYsXG4gICdvdXRwdXQ6Om5hbWUnOiA4LFxuICAncDo6YWxpZ24nOiAwLFxuICAncHJlOjp3aWR0aCc6IDAsXG4gICdwcm9ncmVzczo6bWF4JzogMCxcbiAgJ3Byb2dyZXNzOjptaW4nOiAwLFxuICAncHJvZ3Jlc3M6OnZhbHVlJzogMCxcbiAgJ3E6OmNpdGUnOiAxLFxuICAnc2VsZWN0OjphdXRvY29tcGxldGUnOiAwLFxuICAnc2VsZWN0OjpkaXNhYmxlZCc6IDAsXG4gICdzZWxlY3Q6Om11bHRpcGxlJzogMCxcbiAgJ3NlbGVjdDo6bmFtZSc6IDgsXG4gICdzZWxlY3Q6Om9uYmx1cic6IDIsXG4gICdzZWxlY3Q6Om9uY2hhbmdlJzogMixcbiAgJ3NlbGVjdDo6b25mb2N1cyc6IDIsXG4gICdzZWxlY3Q6OnJlcXVpcmVkJzogMCxcbiAgJ3NlbGVjdDo6c2l6ZSc6IDAsXG4gICdzZWxlY3Q6OnRhYmluZGV4JzogMCxcbiAgJ3NvdXJjZTo6dHlwZSc6IDAsXG4gICd0YWJsZTo6YWxpZ24nOiAwLFxuICAndGFibGU6OmJnY29sb3InOiAwLFxuICAndGFibGU6OmJvcmRlcic6IDAsXG4gICd0YWJsZTo6Y2VsbHBhZGRpbmcnOiAwLFxuICAndGFibGU6OmNlbGxzcGFjaW5nJzogMCxcbiAgJ3RhYmxlOjpmcmFtZSc6IDAsXG4gICd0YWJsZTo6cnVsZXMnOiAwLFxuICAndGFibGU6OnN1bW1hcnknOiAwLFxuICAndGFibGU6OndpZHRoJzogMCxcbiAgJ3Rib2R5OjphbGlnbic6IDAsXG4gICd0Ym9keTo6Y2hhcic6IDAsXG4gICd0Ym9keTo6Y2hhcm9mZic6IDAsXG4gICd0Ym9keTo6dmFsaWduJzogMCxcbiAgJ3RkOjphYmJyJzogMCxcbiAgJ3RkOjphbGlnbic6IDAsXG4gICd0ZDo6YXhpcyc6IDAsXG4gICd0ZDo6Ymdjb2xvcic6IDAsXG4gICd0ZDo6Y2hhcic6IDAsXG4gICd0ZDo6Y2hhcm9mZic6IDAsXG4gICd0ZDo6Y29sc3Bhbic6IDAsXG4gICd0ZDo6aGVhZGVycyc6IDYsXG4gICd0ZDo6aGVpZ2h0JzogMCxcbiAgJ3RkOjpub3dyYXAnOiAwLFxuICAndGQ6OnJvd3NwYW4nOiAwLFxuICAndGQ6OnNjb3BlJzogMCxcbiAgJ3RkOjp2YWxpZ24nOiAwLFxuICAndGQ6OndpZHRoJzogMCxcbiAgJ3RleHRhcmVhOjphY2Nlc3NrZXknOiAwLFxuICAndGV4dGFyZWE6OmF1dG9jb21wbGV0ZSc6IDAsXG4gICd0ZXh0YXJlYTo6Y29scyc6IDAsXG4gICd0ZXh0YXJlYTo6ZGlzYWJsZWQnOiAwLFxuICAndGV4dGFyZWE6OmlucHV0bW9kZSc6IDAsXG4gICd0ZXh0YXJlYTo6bmFtZSc6IDgsXG4gICd0ZXh0YXJlYTo6b25ibHVyJzogMixcbiAgJ3RleHRhcmVhOjpvbmNoYW5nZSc6IDIsXG4gICd0ZXh0YXJlYTo6b25mb2N1cyc6IDIsXG4gICd0ZXh0YXJlYTo6b25zZWxlY3QnOiAyLFxuICAndGV4dGFyZWE6OnBsYWNlaG9sZGVyJzogMCxcbiAgJ3RleHRhcmVhOjpyZWFkb25seSc6IDAsXG4gICd0ZXh0YXJlYTo6cmVxdWlyZWQnOiAwLFxuICAndGV4dGFyZWE6OnJvd3MnOiAwLFxuICAndGV4dGFyZWE6OnRhYmluZGV4JzogMCxcbiAgJ3RleHRhcmVhOjp3cmFwJzogMCxcbiAgJ3Rmb290OjphbGlnbic6IDAsXG4gICd0Zm9vdDo6Y2hhcic6IDAsXG4gICd0Zm9vdDo6Y2hhcm9mZic6IDAsXG4gICd0Zm9vdDo6dmFsaWduJzogMCxcbiAgJ3RoOjphYmJyJzogMCxcbiAgJ3RoOjphbGlnbic6IDAsXG4gICd0aDo6YXhpcyc6IDAsXG4gICd0aDo6Ymdjb2xvcic6IDAsXG4gICd0aDo6Y2hhcic6IDAsXG4gICd0aDo6Y2hhcm9mZic6IDAsXG4gICd0aDo6Y29sc3Bhbic6IDAsXG4gICd0aDo6aGVhZGVycyc6IDYsXG4gICd0aDo6aGVpZ2h0JzogMCxcbiAgJ3RoOjpub3dyYXAnOiAwLFxuICAndGg6OnJvd3NwYW4nOiAwLFxuICAndGg6OnNjb3BlJzogMCxcbiAgJ3RoOjp2YWxpZ24nOiAwLFxuICAndGg6OndpZHRoJzogMCxcbiAgJ3RoZWFkOjphbGlnbic6IDAsXG4gICd0aGVhZDo6Y2hhcic6IDAsXG4gICd0aGVhZDo6Y2hhcm9mZic6IDAsXG4gICd0aGVhZDo6dmFsaWduJzogMCxcbiAgJ3RyOjphbGlnbic6IDAsXG4gICd0cjo6Ymdjb2xvcic6IDAsXG4gICd0cjo6Y2hhcic6IDAsXG4gICd0cjo6Y2hhcm9mZic6IDAsXG4gICd0cjo6dmFsaWduJzogMCxcbiAgJ3RyYWNrOjpkZWZhdWx0JzogMCxcbiAgJ3RyYWNrOjpraW5kJzogMCxcbiAgJ3RyYWNrOjpsYWJlbCc6IDAsXG4gICd0cmFjazo6c3JjbGFuZyc6IDAsXG4gICd1bDo6Y29tcGFjdCc6IDAsXG4gICd1bDo6dHlwZSc6IDAsXG4gICd2aWRlbzo6Y29udHJvbHMnOiAwLFxuICAndmlkZW86OmhlaWdodCc6IDAsXG4gICd2aWRlbzo6bG9vcCc6IDAsXG4gICd2aWRlbzo6bWVkaWFncm91cCc6IDUsXG4gICd2aWRlbzo6bXV0ZWQnOiAwLFxuICAndmlkZW86OnBvc3Rlcic6IDEsXG4gICd2aWRlbzo6cHJlbG9hZCc6IDAsXG4gICd2aWRlbzo6d2lkdGgnOiAwXG59O1xuaHRtbDRbICdBVFRSSUJTJyBdID0gaHRtbDQuQVRUUklCUztcbmh0bWw0LmVmbGFncyA9IHtcbiAgJ09QVElPTkFMX0VORFRBRyc6IDEsXG4gICdFTVBUWSc6IDIsXG4gICdDREFUQSc6IDQsXG4gICdSQ0RBVEEnOiA4LFxuICAnVU5TQUZFJzogMTYsXG4gICdGT0xEQUJMRSc6IDMyLFxuICAnU0NSSVBUJzogNjQsXG4gICdTVFlMRSc6IDEyOCxcbiAgJ1ZJUlRVQUxJWkVEJzogMjU2XG59O1xuaHRtbDRbICdlZmxhZ3MnIF0gPSBodG1sNC5lZmxhZ3M7XG4vLyB0aGVzZSBhcmUgYml0bWFza3Mgb2YgdGhlIGVmbGFncyBhYm92ZS5cbmh0bWw0LkVMRU1FTlRTID0ge1xuICAnYSc6IDAsXG4gICdhYmJyJzogMCxcbiAgJ2Fjcm9ueW0nOiAwLFxuICAnYWRkcmVzcyc6IDAsXG4gICdhcHBsZXQnOiAyNzIsXG4gICdhcmVhJzogMixcbiAgJ2FydGljbGUnOiAwLFxuICAnYXNpZGUnOiAwLFxuICAnYXVkaW8nOiAwLFxuICAnYic6IDAsXG4gICdiYXNlJzogMjc0LFxuICAnYmFzZWZvbnQnOiAyNzQsXG4gICdiZGknOiAwLFxuICAnYmRvJzogMCxcbiAgJ2JpZyc6IDAsXG4gICdibG9ja3F1b3RlJzogMCxcbiAgJ2JvZHknOiAzMDUsXG4gICdicic6IDIsXG4gICdidXR0b24nOiAwLFxuICAnY2FudmFzJzogMCxcbiAgJ2NhcHRpb24nOiAwLFxuICAnY2VudGVyJzogMCxcbiAgJ2NpdGUnOiAwLFxuICAnY29kZSc6IDAsXG4gICdjb2wnOiAyLFxuICAnY29sZ3JvdXAnOiAxLFxuICAnY29tbWFuZCc6IDIsXG4gICdkYXRhJzogMCxcbiAgJ2RhdGFsaXN0JzogMCxcbiAgJ2RkJzogMSxcbiAgJ2RlbCc6IDAsXG4gICdkZXRhaWxzJzogMCxcbiAgJ2Rmbic6IDAsXG4gICdkaWFsb2cnOiAyNzIsXG4gICdkaXInOiAwLFxuICAnZGl2JzogMCxcbiAgJ2RsJzogMCxcbiAgJ2R0JzogMSxcbiAgJ2VtJzogMCxcbiAgJ2ZpZWxkc2V0JzogMCxcbiAgJ2ZpZ2NhcHRpb24nOiAwLFxuICAnZmlndXJlJzogMCxcbiAgJ2ZvbnQnOiAwLFxuICAnZm9vdGVyJzogMCxcbiAgJ2Zvcm0nOiAwLFxuICAnZnJhbWUnOiAyNzQsXG4gICdmcmFtZXNldCc6IDI3MixcbiAgJ2gxJzogMCxcbiAgJ2gyJzogMCxcbiAgJ2gzJzogMCxcbiAgJ2g0JzogMCxcbiAgJ2g1JzogMCxcbiAgJ2g2JzogMCxcbiAgJ2hlYWQnOiAzMDUsXG4gICdoZWFkZXInOiAwLFxuICAnaGdyb3VwJzogMCxcbiAgJ2hyJzogMixcbiAgJ2h0bWwnOiAzMDUsXG4gICdpJzogMCxcbiAgJ2lmcmFtZSc6IDE2LFxuICAnaW1nJzogMixcbiAgJ2lucHV0JzogMixcbiAgJ2lucyc6IDAsXG4gICdpc2luZGV4JzogMjc0LFxuICAna2JkJzogMCxcbiAgJ2tleWdlbic6IDI3NCxcbiAgJ2xhYmVsJzogMCxcbiAgJ2xlZ2VuZCc6IDAsXG4gICdsaSc6IDEsXG4gICdsaW5rJzogMjc0LFxuICAnbWFwJzogMCxcbiAgJ21hcmsnOiAwLFxuICAnbWVudSc6IDAsXG4gICdtZXRhJzogMjc0LFxuICAnbWV0ZXInOiAwLFxuICAnbmF2JzogMCxcbiAgJ25vYnInOiAwLFxuICAnbm9lbWJlZCc6IDI3NixcbiAgJ25vZnJhbWVzJzogMjc2LFxuICAnbm9zY3JpcHQnOiAyNzYsXG4gICdvYmplY3QnOiAyNzIsXG4gICdvbCc6IDAsXG4gICdvcHRncm91cCc6IDAsXG4gICdvcHRpb24nOiAxLFxuICAnb3V0cHV0JzogMCxcbiAgJ3AnOiAxLFxuICAncGFyYW0nOiAyNzQsXG4gICdwcmUnOiAwLFxuICAncHJvZ3Jlc3MnOiAwLFxuICAncSc6IDAsXG4gICdzJzogMCxcbiAgJ3NhbXAnOiAwLFxuICAnc2NyaXB0JzogODQsXG4gICdzZWN0aW9uJzogMCxcbiAgJ3NlbGVjdCc6IDAsXG4gICdzbWFsbCc6IDAsXG4gICdzb3VyY2UnOiAyLFxuICAnc3Bhbic6IDAsXG4gICdzdHJpa2UnOiAwLFxuICAnc3Ryb25nJzogMCxcbiAgJ3N0eWxlJzogMTQ4LFxuICAnc3ViJzogMCxcbiAgJ3N1bW1hcnknOiAwLFxuICAnc3VwJzogMCxcbiAgJ3RhYmxlJzogMCxcbiAgJ3Rib2R5JzogMSxcbiAgJ3RkJzogMSxcbiAgJ3RleHRhcmVhJzogOCxcbiAgJ3Rmb290JzogMSxcbiAgJ3RoJzogMSxcbiAgJ3RoZWFkJzogMSxcbiAgJ3RpbWUnOiAwLFxuICAndGl0bGUnOiAyODAsXG4gICd0cic6IDEsXG4gICd0cmFjayc6IDIsXG4gICd0dCc6IDAsXG4gICd1JzogMCxcbiAgJ3VsJzogMCxcbiAgJ3Zhcic6IDAsXG4gICd2aWRlbyc6IDAsXG4gICd3YnInOiAyXG59O1xuaHRtbDRbICdFTEVNRU5UUycgXSA9IGh0bWw0LkVMRU1FTlRTO1xuaHRtbDQuRUxFTUVOVF9ET01fSU5URVJGQUNFUyA9IHtcbiAgJ2EnOiAnSFRNTEFuY2hvckVsZW1lbnQnLFxuICAnYWJicic6ICdIVE1MRWxlbWVudCcsXG4gICdhY3JvbnltJzogJ0hUTUxFbGVtZW50JyxcbiAgJ2FkZHJlc3MnOiAnSFRNTEVsZW1lbnQnLFxuICAnYXBwbGV0JzogJ0hUTUxBcHBsZXRFbGVtZW50JyxcbiAgJ2FyZWEnOiAnSFRNTEFyZWFFbGVtZW50JyxcbiAgJ2FydGljbGUnOiAnSFRNTEVsZW1lbnQnLFxuICAnYXNpZGUnOiAnSFRNTEVsZW1lbnQnLFxuICAnYXVkaW8nOiAnSFRNTEF1ZGlvRWxlbWVudCcsXG4gICdiJzogJ0hUTUxFbGVtZW50JyxcbiAgJ2Jhc2UnOiAnSFRNTEJhc2VFbGVtZW50JyxcbiAgJ2Jhc2Vmb250JzogJ0hUTUxCYXNlRm9udEVsZW1lbnQnLFxuICAnYmRpJzogJ0hUTUxFbGVtZW50JyxcbiAgJ2Jkbyc6ICdIVE1MRWxlbWVudCcsXG4gICdiaWcnOiAnSFRNTEVsZW1lbnQnLFxuICAnYmxvY2txdW90ZSc6ICdIVE1MUXVvdGVFbGVtZW50JyxcbiAgJ2JvZHknOiAnSFRNTEJvZHlFbGVtZW50JyxcbiAgJ2JyJzogJ0hUTUxCUkVsZW1lbnQnLFxuICAnYnV0dG9uJzogJ0hUTUxCdXR0b25FbGVtZW50JyxcbiAgJ2NhbnZhcyc6ICdIVE1MQ2FudmFzRWxlbWVudCcsXG4gICdjYXB0aW9uJzogJ0hUTUxUYWJsZUNhcHRpb25FbGVtZW50JyxcbiAgJ2NlbnRlcic6ICdIVE1MRWxlbWVudCcsXG4gICdjaXRlJzogJ0hUTUxFbGVtZW50JyxcbiAgJ2NvZGUnOiAnSFRNTEVsZW1lbnQnLFxuICAnY29sJzogJ0hUTUxUYWJsZUNvbEVsZW1lbnQnLFxuICAnY29sZ3JvdXAnOiAnSFRNTFRhYmxlQ29sRWxlbWVudCcsXG4gICdjb21tYW5kJzogJ0hUTUxDb21tYW5kRWxlbWVudCcsXG4gICdkYXRhJzogJ0hUTUxFbGVtZW50JyxcbiAgJ2RhdGFsaXN0JzogJ0hUTUxEYXRhTGlzdEVsZW1lbnQnLFxuICAnZGQnOiAnSFRNTEVsZW1lbnQnLFxuICAnZGVsJzogJ0hUTUxNb2RFbGVtZW50JyxcbiAgJ2RldGFpbHMnOiAnSFRNTERldGFpbHNFbGVtZW50JyxcbiAgJ2Rmbic6ICdIVE1MRWxlbWVudCcsXG4gICdkaWFsb2cnOiAnSFRNTERpYWxvZ0VsZW1lbnQnLFxuICAnZGlyJzogJ0hUTUxEaXJlY3RvcnlFbGVtZW50JyxcbiAgJ2Rpdic6ICdIVE1MRGl2RWxlbWVudCcsXG4gICdkbCc6ICdIVE1MRExpc3RFbGVtZW50JyxcbiAgJ2R0JzogJ0hUTUxFbGVtZW50JyxcbiAgJ2VtJzogJ0hUTUxFbGVtZW50JyxcbiAgJ2ZpZWxkc2V0JzogJ0hUTUxGaWVsZFNldEVsZW1lbnQnLFxuICAnZmlnY2FwdGlvbic6ICdIVE1MRWxlbWVudCcsXG4gICdmaWd1cmUnOiAnSFRNTEVsZW1lbnQnLFxuICAnZm9udCc6ICdIVE1MRm9udEVsZW1lbnQnLFxuICAnZm9vdGVyJzogJ0hUTUxFbGVtZW50JyxcbiAgJ2Zvcm0nOiAnSFRNTEZvcm1FbGVtZW50JyxcbiAgJ2ZyYW1lJzogJ0hUTUxGcmFtZUVsZW1lbnQnLFxuICAnZnJhbWVzZXQnOiAnSFRNTEZyYW1lU2V0RWxlbWVudCcsXG4gICdoMSc6ICdIVE1MSGVhZGluZ0VsZW1lbnQnLFxuICAnaDInOiAnSFRNTEhlYWRpbmdFbGVtZW50JyxcbiAgJ2gzJzogJ0hUTUxIZWFkaW5nRWxlbWVudCcsXG4gICdoNCc6ICdIVE1MSGVhZGluZ0VsZW1lbnQnLFxuICAnaDUnOiAnSFRNTEhlYWRpbmdFbGVtZW50JyxcbiAgJ2g2JzogJ0hUTUxIZWFkaW5nRWxlbWVudCcsXG4gICdoZWFkJzogJ0hUTUxIZWFkRWxlbWVudCcsXG4gICdoZWFkZXInOiAnSFRNTEVsZW1lbnQnLFxuICAnaGdyb3VwJzogJ0hUTUxFbGVtZW50JyxcbiAgJ2hyJzogJ0hUTUxIUkVsZW1lbnQnLFxuICAnaHRtbCc6ICdIVE1MSHRtbEVsZW1lbnQnLFxuICAnaSc6ICdIVE1MRWxlbWVudCcsXG4gICdpZnJhbWUnOiAnSFRNTElGcmFtZUVsZW1lbnQnLFxuICAnaW1nJzogJ0hUTUxJbWFnZUVsZW1lbnQnLFxuICAnaW5wdXQnOiAnSFRNTElucHV0RWxlbWVudCcsXG4gICdpbnMnOiAnSFRNTE1vZEVsZW1lbnQnLFxuICAnaXNpbmRleCc6ICdIVE1MVW5rbm93bkVsZW1lbnQnLFxuICAna2JkJzogJ0hUTUxFbGVtZW50JyxcbiAgJ2tleWdlbic6ICdIVE1MS2V5Z2VuRWxlbWVudCcsXG4gICdsYWJlbCc6ICdIVE1MTGFiZWxFbGVtZW50JyxcbiAgJ2xlZ2VuZCc6ICdIVE1MTGVnZW5kRWxlbWVudCcsXG4gICdsaSc6ICdIVE1MTElFbGVtZW50JyxcbiAgJ2xpbmsnOiAnSFRNTExpbmtFbGVtZW50JyxcbiAgJ21hcCc6ICdIVE1MTWFwRWxlbWVudCcsXG4gICdtYXJrJzogJ0hUTUxFbGVtZW50JyxcbiAgJ21lbnUnOiAnSFRNTE1lbnVFbGVtZW50JyxcbiAgJ21ldGEnOiAnSFRNTE1ldGFFbGVtZW50JyxcbiAgJ21ldGVyJzogJ0hUTUxNZXRlckVsZW1lbnQnLFxuICAnbmF2JzogJ0hUTUxFbGVtZW50JyxcbiAgJ25vYnInOiAnSFRNTEVsZW1lbnQnLFxuICAnbm9lbWJlZCc6ICdIVE1MRWxlbWVudCcsXG4gICdub2ZyYW1lcyc6ICdIVE1MRWxlbWVudCcsXG4gICdub3NjcmlwdCc6ICdIVE1MRWxlbWVudCcsXG4gICdvYmplY3QnOiAnSFRNTE9iamVjdEVsZW1lbnQnLFxuICAnb2wnOiAnSFRNTE9MaXN0RWxlbWVudCcsXG4gICdvcHRncm91cCc6ICdIVE1MT3B0R3JvdXBFbGVtZW50JyxcbiAgJ29wdGlvbic6ICdIVE1MT3B0aW9uRWxlbWVudCcsXG4gICdvdXRwdXQnOiAnSFRNTE91dHB1dEVsZW1lbnQnLFxuICAncCc6ICdIVE1MUGFyYWdyYXBoRWxlbWVudCcsXG4gICdwYXJhbSc6ICdIVE1MUGFyYW1FbGVtZW50JyxcbiAgJ3ByZSc6ICdIVE1MUHJlRWxlbWVudCcsXG4gICdwcm9ncmVzcyc6ICdIVE1MUHJvZ3Jlc3NFbGVtZW50JyxcbiAgJ3EnOiAnSFRNTFF1b3RlRWxlbWVudCcsXG4gICdzJzogJ0hUTUxFbGVtZW50JyxcbiAgJ3NhbXAnOiAnSFRNTEVsZW1lbnQnLFxuICAnc2NyaXB0JzogJ0hUTUxTY3JpcHRFbGVtZW50JyxcbiAgJ3NlY3Rpb24nOiAnSFRNTEVsZW1lbnQnLFxuICAnc2VsZWN0JzogJ0hUTUxTZWxlY3RFbGVtZW50JyxcbiAgJ3NtYWxsJzogJ0hUTUxFbGVtZW50JyxcbiAgJ3NvdXJjZSc6ICdIVE1MU291cmNlRWxlbWVudCcsXG4gICdzcGFuJzogJ0hUTUxTcGFuRWxlbWVudCcsXG4gICdzdHJpa2UnOiAnSFRNTEVsZW1lbnQnLFxuICAnc3Ryb25nJzogJ0hUTUxFbGVtZW50JyxcbiAgJ3N0eWxlJzogJ0hUTUxTdHlsZUVsZW1lbnQnLFxuICAnc3ViJzogJ0hUTUxFbGVtZW50JyxcbiAgJ3N1bW1hcnknOiAnSFRNTEVsZW1lbnQnLFxuICAnc3VwJzogJ0hUTUxFbGVtZW50JyxcbiAgJ3RhYmxlJzogJ0hUTUxUYWJsZUVsZW1lbnQnLFxuICAndGJvZHknOiAnSFRNTFRhYmxlU2VjdGlvbkVsZW1lbnQnLFxuICAndGQnOiAnSFRNTFRhYmxlRGF0YUNlbGxFbGVtZW50JyxcbiAgJ3RleHRhcmVhJzogJ0hUTUxUZXh0QXJlYUVsZW1lbnQnLFxuICAndGZvb3QnOiAnSFRNTFRhYmxlU2VjdGlvbkVsZW1lbnQnLFxuICAndGgnOiAnSFRNTFRhYmxlSGVhZGVyQ2VsbEVsZW1lbnQnLFxuICAndGhlYWQnOiAnSFRNTFRhYmxlU2VjdGlvbkVsZW1lbnQnLFxuICAndGltZSc6ICdIVE1MVGltZUVsZW1lbnQnLFxuICAndGl0bGUnOiAnSFRNTFRpdGxlRWxlbWVudCcsXG4gICd0cic6ICdIVE1MVGFibGVSb3dFbGVtZW50JyxcbiAgJ3RyYWNrJzogJ0hUTUxUcmFja0VsZW1lbnQnLFxuICAndHQnOiAnSFRNTEVsZW1lbnQnLFxuICAndSc6ICdIVE1MRWxlbWVudCcsXG4gICd1bCc6ICdIVE1MVUxpc3RFbGVtZW50JyxcbiAgJ3Zhcic6ICdIVE1MRWxlbWVudCcsXG4gICd2aWRlbyc6ICdIVE1MVmlkZW9FbGVtZW50JyxcbiAgJ3dicic6ICdIVE1MRWxlbWVudCdcbn07XG5odG1sNFsgJ0VMRU1FTlRfRE9NX0lOVEVSRkFDRVMnIF0gPSBodG1sNC5FTEVNRU5UX0RPTV9JTlRFUkZBQ0VTO1xuaHRtbDQudWVmZmVjdHMgPSB7XG4gICdOT1RfTE9BREVEJzogMCxcbiAgJ1NBTUVfRE9DVU1FTlQnOiAxLFxuICAnTkVXX0RPQ1VNRU5UJzogMlxufTtcbmh0bWw0WyAndWVmZmVjdHMnIF0gPSBodG1sNC51ZWZmZWN0cztcbmh0bWw0LlVSSUVGRkVDVFMgPSB7XG4gICdhOjpocmVmJzogMixcbiAgJ2FyZWE6OmhyZWYnOiAyLFxuICAnYmxvY2txdW90ZTo6Y2l0ZSc6IDAsXG4gICdjb21tYW5kOjppY29uJzogMSxcbiAgJ2RlbDo6Y2l0ZSc6IDAsXG4gICdmb3JtOjphY3Rpb24nOiAyLFxuICAnaW1nOjpzcmMnOiAxLFxuICAnaW5wdXQ6OnNyYyc6IDEsXG4gICdpbnM6OmNpdGUnOiAwLFxuICAncTo6Y2l0ZSc6IDAsXG4gICd2aWRlbzo6cG9zdGVyJzogMVxufTtcbmh0bWw0WyAnVVJJRUZGRUNUUycgXSA9IGh0bWw0LlVSSUVGRkVDVFM7XG5odG1sNC5sdHlwZXMgPSB7XG4gICdVTlNBTkRCT1hFRCc6IDIsXG4gICdTQU5EQk9YRUQnOiAxLFxuICAnREFUQSc6IDBcbn07XG5odG1sNFsgJ2x0eXBlcycgXSA9IGh0bWw0Lmx0eXBlcztcbmh0bWw0LkxPQURFUlRZUEVTID0ge1xuICAnYTo6aHJlZic6IDIsXG4gICdhcmVhOjpocmVmJzogMixcbiAgJ2Jsb2NrcXVvdGU6OmNpdGUnOiAyLFxuICAnY29tbWFuZDo6aWNvbic6IDEsXG4gICdkZWw6OmNpdGUnOiAyLFxuICAnZm9ybTo6YWN0aW9uJzogMixcbiAgJ2ltZzo6c3JjJzogMSxcbiAgJ2lucHV0OjpzcmMnOiAxLFxuICAnaW5zOjpjaXRlJzogMixcbiAgJ3E6OmNpdGUnOiAyLFxuICAndmlkZW86OnBvc3Rlcic6IDFcbn07XG5odG1sNFsgJ0xPQURFUlRZUEVTJyBdID0gaHRtbDQuTE9BREVSVFlQRVM7XG5cbi8vIENvcHlyaWdodCAoQykgMjAwNiBHb29nbGUgSW5jLlxuLy9cbi8vIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4vLyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4vLyBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbi8vXG4vLyAgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuLy9cbi8vIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbi8vIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbi8vIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuLy8gU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuLy8gbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG5cbi8qKlxuICogQGZpbGVvdmVydmlld1xuICogQW4gSFRNTCBzYW5pdGl6ZXIgdGhhdCBjYW4gc2F0aXNmeSBhIHZhcmlldHkgb2Ygc2VjdXJpdHkgcG9saWNpZXMuXG4gKlxuICogPHA+XG4gKiBUaGUgSFRNTCBzYW5pdGl6ZXIgaXMgYnVpbHQgYXJvdW5kIGEgU0FYIHBhcnNlciBhbmQgSFRNTCBlbGVtZW50IGFuZFxuICogYXR0cmlidXRlcyBzY2hlbWFzLlxuICpcbiAqIElmIHRoZSBjc3NwYXJzZXIgaXMgbG9hZGVkLCBpbmxpbmUgc3R5bGVzIGFyZSBzYW5pdGl6ZWQgdXNpbmcgdGhlXG4gKiBjc3MgcHJvcGVydHkgYW5kIHZhbHVlIHNjaGVtYXMuICBFbHNlIHRoZXkgYXJlIHJlbW92ZSBkdXJpbmdcbiAqIHNhbml0aXphdGlvbi5cbiAqXG4gKiBJZiBpdCBleGlzdHMsIHVzZXMgcGFyc2VDc3NEZWNsYXJhdGlvbnMsIHNhbml0aXplQ3NzUHJvcGVydHksICBjc3NTY2hlbWFcbiAqXG4gKiBAYXV0aG9yIG1pa2VzYW11ZWxAZ21haWwuY29tXG4gKiBAYXV0aG9yIGphc3ZpckBnbWFpbC5jb21cbiAqIFxcQHJlcXVpcmVzIGh0bWw0LCBVUklcbiAqIFxcQG92ZXJyaWRlcyB3aW5kb3dcbiAqIFxcQHByb3ZpZGVzIGh0bWwsIGh0bWxfc2FuaXRpemVcbiAqL1xuXG4vLyBUaGUgVHVya2lzaCBpIHNlZW1zIHRvIGJlIGEgbm9uLWlzc3VlLCBidXQgYWJvcnQgaW4gY2FzZSBpdCBpcy5cbmlmICgnSScudG9Mb3dlckNhc2UoKSAhPT0gJ2knKSB7IHRocm93ICdJL2kgcHJvYmxlbSc7IH1cblxuLyoqXG4gKiBcXEBuYW1lc3BhY2VcbiAqL1xudmFyIGh0bWwgPSAoZnVuY3Rpb24oaHRtbDQpIHtcblxuICAvLyBGb3IgY2xvc3VyZSBjb21waWxlclxuICB2YXIgcGFyc2VDc3NEZWNsYXJhdGlvbnMsIHNhbml0aXplQ3NzUHJvcGVydHksIGNzc1NjaGVtYTtcbiAgaWYgKCd1bmRlZmluZWQnICE9PSB0eXBlb2Ygd2luZG93KSB7XG4gICAgcGFyc2VDc3NEZWNsYXJhdGlvbnMgPSB3aW5kb3dbJ3BhcnNlQ3NzRGVjbGFyYXRpb25zJ107XG4gICAgc2FuaXRpemVDc3NQcm9wZXJ0eSA9IHdpbmRvd1snc2FuaXRpemVDc3NQcm9wZXJ0eSddO1xuICAgIGNzc1NjaGVtYSA9IHdpbmRvd1snY3NzU2NoZW1hJ107XG4gIH1cblxuICAvLyBUaGUga2V5cyBvZiB0aGlzIG9iamVjdCBtdXN0IGJlICdxdW90ZWQnIG9yIEpTQ29tcGlsZXIgd2lsbCBtYW5nbGUgdGhlbSFcbiAgLy8gVGhpcyBpcyBhIHBhcnRpYWwgbGlzdCAtLSBsb29rdXBFbnRpdHkoKSB1c2VzIHRoZSBob3N0IGJyb3dzZXIncyBwYXJzZXJcbiAgLy8gKHdoZW4gYXZhaWxhYmxlKSB0byBpbXBsZW1lbnQgZnVsbCBlbnRpdHkgbG9va3VwLlxuICAvLyBOb3RlIHRoYXQgZW50aXRpZXMgYXJlIGluIGdlbmVyYWwgY2FzZS1zZW5zaXRpdmU7IHRoZSB1cHBlcmNhc2Ugb25lcyBhcmVcbiAgLy8gZXhwbGljaXRseSBkZWZpbmVkIGJ5IEhUTUw1IChwcmVzdW1hYmx5IGFzIGNvbXBhdGliaWxpdHkpLlxuICB2YXIgRU5USVRJRVMgPSB7XG4gICAgJ2x0JzogJzwnLFxuICAgICdMVCc6ICc8JyxcbiAgICAnZ3QnOiAnPicsXG4gICAgJ0dUJzogJz4nLFxuICAgICdhbXAnOiAnJicsXG4gICAgJ0FNUCc6ICcmJyxcbiAgICAncXVvdCc6ICdcIicsXG4gICAgJ2Fwb3MnOiAnXFwnJyxcbiAgICAnbmJzcCc6ICdcXHUwMEEwJ1xuICB9O1xuXG4gIC8vIFBhdHRlcm5zIGZvciB0eXBlcyBvZiBlbnRpdHkvY2hhcmFjdGVyIHJlZmVyZW5jZSBuYW1lcy5cbiAgdmFyIGRlY2ltYWxFc2NhcGVSZSA9IC9eIyhcXGQrKSQvO1xuICB2YXIgaGV4RXNjYXBlUmUgPSAvXiN4KFswLTlBLUZhLWZdKykkLztcbiAgLy8gY29udGFpbnMgZXZlcnkgZW50aXR5IHBlciBodHRwOi8vd3d3LnczLm9yZy9UUi8yMDExL1dELWh0bWw1LTIwMTEwMTEzL25hbWVkLWNoYXJhY3Rlci1yZWZlcmVuY2VzLmh0bWxcbiAgdmFyIHNhZmVFbnRpdHlOYW1lUmUgPSAvXltBLVphLXpdW0EtemEtejAtOV0rJC87XG4gIC8vIFVzZWQgYXMgYSBob29rIHRvIGludm9rZSB0aGUgYnJvd3NlcidzIGVudGl0eSBwYXJzaW5nLiA8dGV4dGFyZWE+IGlzIHVzZWRcbiAgLy8gYmVjYXVzZSBpdHMgY29udGVudCBpcyBwYXJzZWQgZm9yIGVudGl0aWVzIGJ1dCBub3QgdGFncy5cbiAgLy8gVE9ETyhrcHJlaWQpOiBUaGlzIHJldHJpZXZhbCBpcyBhIGtsdWRnZSBhbmQgbGVhZHMgdG8gc2lsZW50IGxvc3Mgb2ZcbiAgLy8gZnVuY3Rpb25hbGl0eSBpZiB0aGUgZG9jdW1lbnQgaXNuJ3QgYXZhaWxhYmxlLlxuICB2YXIgZW50aXR5TG9va3VwRWxlbWVudCA9XG4gICAgICAoJ3VuZGVmaW5lZCcgIT09IHR5cGVvZiB3aW5kb3cgJiYgd2luZG93Wydkb2N1bWVudCddKVxuICAgICAgICAgID8gd2luZG93Wydkb2N1bWVudCddLmNyZWF0ZUVsZW1lbnQoJ3RleHRhcmVhJykgOiBudWxsO1xuICAvKipcbiAgICogRGVjb2RlcyBhbiBIVE1MIGVudGl0eS5cbiAgICpcbiAgICoge1xcQHVwZG9jXG4gICAqICQgbG9va3VwRW50aXR5KCdsdCcpXG4gICAqICMgJzwnXG4gICAqICQgbG9va3VwRW50aXR5KCdHVCcpXG4gICAqICMgJz4nXG4gICAqICQgbG9va3VwRW50aXR5KCdhbXAnKVxuICAgKiAjICcmJ1xuICAgKiAkIGxvb2t1cEVudGl0eSgnbmJzcCcpXG4gICAqICMgJ1xceEEwJ1xuICAgKiAkIGxvb2t1cEVudGl0eSgnYXBvcycpXG4gICAqICMgXCInXCJcbiAgICogJCBsb29rdXBFbnRpdHkoJ3F1b3QnKVxuICAgKiAjICdcIidcbiAgICogJCBsb29rdXBFbnRpdHkoJyN4YScpXG4gICAqICMgJ1xcbidcbiAgICogJCBsb29rdXBFbnRpdHkoJyMxMCcpXG4gICAqICMgJ1xcbidcbiAgICogJCBsb29rdXBFbnRpdHkoJyN4MGEnKVxuICAgKiAjICdcXG4nXG4gICAqICQgbG9va3VwRW50aXR5KCcjMDEwJylcbiAgICogIyAnXFxuJ1xuICAgKiAkIGxvb2t1cEVudGl0eSgnI3gwMEEnKVxuICAgKiAjICdcXG4nXG4gICAqICQgbG9va3VwRW50aXR5KCdQaScpICAgICAgLy8gS25vd24gZmFpbHVyZVxuICAgKiAjICdcXHUwM0EwJ1xuICAgKiAkIGxvb2t1cEVudGl0eSgncGknKSAgICAgIC8vIEtub3duIGZhaWx1cmVcbiAgICogIyAnXFx1MDNDMCdcbiAgICogfVxuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gbmFtZSB0aGUgY29udGVudCBiZXR3ZWVuIHRoZSAnJicgYW5kIHRoZSAnOycuXG4gICAqIEByZXR1cm4ge3N0cmluZ30gYSBzaW5nbGUgdW5pY29kZSBjb2RlLXBvaW50IGFzIGEgc3RyaW5nLlxuICAgKi9cbiAgZnVuY3Rpb24gbG9va3VwRW50aXR5KG5hbWUpIHtcbiAgICAvLyBUT0RPOiBlbnRpdHkgbG9va3VwIGFzIHNwZWNpZmllZCBieSBIVE1MNSBhY3R1YWxseSBkZXBlbmRzIG9uIHRoZVxuICAgIC8vIHByZXNlbmNlIG9mIHRoZSBcIjtcIi5cbiAgICBpZiAoRU5USVRJRVMuaGFzT3duUHJvcGVydHkobmFtZSkpIHsgcmV0dXJuIEVOVElUSUVTW25hbWVdOyB9XG4gICAgdmFyIG0gPSBuYW1lLm1hdGNoKGRlY2ltYWxFc2NhcGVSZSk7XG4gICAgaWYgKG0pIHtcbiAgICAgIHJldHVybiBTdHJpbmcuZnJvbUNoYXJDb2RlKHBhcnNlSW50KG1bMV0sIDEwKSk7XG4gICAgfSBlbHNlIGlmICghIShtID0gbmFtZS5tYXRjaChoZXhFc2NhcGVSZSkpKSB7XG4gICAgICByZXR1cm4gU3RyaW5nLmZyb21DaGFyQ29kZShwYXJzZUludChtWzFdLCAxNikpO1xuICAgIH0gZWxzZSBpZiAoZW50aXR5TG9va3VwRWxlbWVudCAmJiBzYWZlRW50aXR5TmFtZVJlLnRlc3QobmFtZSkpIHtcbiAgICAgIGVudGl0eUxvb2t1cEVsZW1lbnQuaW5uZXJIVE1MID0gJyYnICsgbmFtZSArICc7JztcbiAgICAgIHZhciB0ZXh0ID0gZW50aXR5TG9va3VwRWxlbWVudC50ZXh0Q29udGVudDtcbiAgICAgIEVOVElUSUVTW25hbWVdID0gdGV4dDtcbiAgICAgIHJldHVybiB0ZXh0O1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gJyYnICsgbmFtZSArICc7JztcbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBkZWNvZGVPbmVFbnRpdHkoXywgbmFtZSkge1xuICAgIHJldHVybiBsb29rdXBFbnRpdHkobmFtZSk7XG4gIH1cblxuICB2YXIgbnVsUmUgPSAvXFwwL2c7XG4gIGZ1bmN0aW9uIHN0cmlwTlVMcyhzKSB7XG4gICAgcmV0dXJuIHMucmVwbGFjZShudWxSZSwgJycpO1xuICB9XG5cbiAgdmFyIEVOVElUWV9SRV8xID0gLyYoI1swLTldK3wjW3hYXVswLTlBLUZhLWZdK3xcXHcrKTsvZztcbiAgdmFyIEVOVElUWV9SRV8yID0gL14oI1swLTldK3wjW3hYXVswLTlBLUZhLWZdK3xcXHcrKTsvO1xuICAvKipcbiAgICogVGhlIHBsYWluIHRleHQgb2YgYSBjaHVuayBvZiBIVE1MIENEQVRBIHdoaWNoIHBvc3NpYmx5IGNvbnRhaW5pbmcuXG4gICAqXG4gICAqIHtcXEB1cGRvY1xuICAgKiAkIHVuZXNjYXBlRW50aXRpZXMoJycpXG4gICAqICMgJydcbiAgICogJCB1bmVzY2FwZUVudGl0aWVzKCdoZWxsbyBXb3JsZCEnKVxuICAgKiAjICdoZWxsbyBXb3JsZCEnXG4gICAqICQgdW5lc2NhcGVFbnRpdGllcygnMSAmbHQ7IDIgJmFtcDsmQU1QOyA0ICZndDsgMyYjMTA7JylcbiAgICogIyAnMSA8IDIgJiYgNCA+IDNcXG4nXG4gICAqICQgdW5lc2NhcGVFbnRpdGllcygnJmx0OyZsdCA8LSB1bmZpbmlzaGVkIGVudGl0eSZndDsnKVxuICAgKiAjICc8Jmx0IDwtIHVuZmluaXNoZWQgZW50aXR5PidcbiAgICogJCB1bmVzY2FwZUVudGl0aWVzKCcvZm9vP2Jhcj1iYXomY29weT10cnVlJykgIC8vICYgb2Z0ZW4gdW5lc2NhcGVkIGluIFVSTFNcbiAgICogIyAnL2Zvbz9iYXI9YmF6JmNvcHk9dHJ1ZSdcbiAgICogJCB1bmVzY2FwZUVudGl0aWVzKCdwaT0mcGk7JiN4M2MwOywgUGk9JlBpO1xcdTAzQTAnKSAvLyBGSVhNRToga25vd24gZmFpbHVyZVxuICAgKiAjICdwaT1cXHUwM0MwXFx1MDNjMCwgUGk9XFx1MDNBMFxcdTAzQTAnXG4gICAqIH1cbiAgICpcbiAgICogQHBhcmFtIHtzdHJpbmd9IHMgYSBjaHVuayBvZiBIVE1MIENEQVRBLiAgSXQgbXVzdCBub3Qgc3RhcnQgb3IgZW5kIGluc2lkZVxuICAgKiAgICAgYW4gSFRNTCBlbnRpdHkuXG4gICAqL1xuICBmdW5jdGlvbiB1bmVzY2FwZUVudGl0aWVzKHMpIHtcbiAgICByZXR1cm4gcy5yZXBsYWNlKEVOVElUWV9SRV8xLCBkZWNvZGVPbmVFbnRpdHkpO1xuICB9XG5cbiAgdmFyIGFtcFJlID0gLyYvZztcbiAgdmFyIGxvb3NlQW1wUmUgPSAvJihbXmEteiNdfCMoPzpbXjAtOXhdfHgoPzpbXjAtOWEtZl18JCl8JCl8JCkvZ2k7XG4gIHZhciBsdFJlID0gL1s8XS9nO1xuICB2YXIgZ3RSZSA9IC8+L2c7XG4gIHZhciBxdW90UmUgPSAvXFxcIi9nO1xuXG4gIC8qKlxuICAgKiBFc2NhcGVzIEhUTUwgc3BlY2lhbCBjaGFyYWN0ZXJzIGluIGF0dHJpYnV0ZSB2YWx1ZXMuXG4gICAqXG4gICAqIHtcXEB1cGRvY1xuICAgKiAkIGVzY2FwZUF0dHJpYignJylcbiAgICogIyAnJ1xuICAgKiAkIGVzY2FwZUF0dHJpYignXCI8PCY9PSY+PlwiJykgIC8vIERvIG5vdCBqdXN0IGVzY2FwZSB0aGUgZmlyc3Qgb2NjdXJyZW5jZS5cbiAgICogIyAnJiMzNDsmbHQ7Jmx0OyZhbXA7JiM2MTsmIzYxOyZhbXA7Jmd0OyZndDsmIzM0OydcbiAgICogJCBlc2NhcGVBdHRyaWIoJ0hlbGxvIDxXb3JsZD4hJylcbiAgICogIyAnSGVsbG8gJmx0O1dvcmxkJmd0OyEnXG4gICAqIH1cbiAgICovXG4gIGZ1bmN0aW9uIGVzY2FwZUF0dHJpYihzKSB7XG4gICAgcmV0dXJuICgnJyArIHMpLnJlcGxhY2UoYW1wUmUsICcmYW1wOycpLnJlcGxhY2UobHRSZSwgJyZsdDsnKVxuICAgICAgICAucmVwbGFjZShndFJlLCAnJmd0OycpLnJlcGxhY2UocXVvdFJlLCAnJiMzNDsnKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFc2NhcGUgZW50aXRpZXMgaW4gUkNEQVRBIHRoYXQgY2FuIGJlIGVzY2FwZWQgd2l0aG91dCBjaGFuZ2luZyB0aGUgbWVhbmluZy5cbiAgICoge1xcQHVwZG9jXG4gICAqICQgbm9ybWFsaXplUkNEYXRhKCcxIDwgMiAmJmFtcDsgMyA+IDQgJmFtcDsmIDUgJmx0OyA3JjgnKVxuICAgKiAjICcxICZsdDsgMiAmYW1wOyZhbXA7IDMgJmd0OyA0ICZhbXA7JmFtcDsgNSAmbHQ7IDcmYW1wOzgnXG4gICAqIH1cbiAgICovXG4gIGZ1bmN0aW9uIG5vcm1hbGl6ZVJDRGF0YShyY2RhdGEpIHtcbiAgICByZXR1cm4gcmNkYXRhXG4gICAgICAgIC5yZXBsYWNlKGxvb3NlQW1wUmUsICcmYW1wOyQxJylcbiAgICAgICAgLnJlcGxhY2UobHRSZSwgJyZsdDsnKVxuICAgICAgICAucmVwbGFjZShndFJlLCAnJmd0OycpO1xuICB9XG5cbiAgLy8gVE9ETyhmZWxpeDhhKTogdmFsaWRhdGUgc2FuaXRpemVyIHJlZ2V4cyBhZ2FpbnN0IHRoZSBIVE1MNSBncmFtbWFyIGF0XG4gIC8vIGh0dHA6Ly93d3cud2hhdHdnLm9yZy9zcGVjcy93ZWItYXBwcy9jdXJyZW50LXdvcmsvbXVsdGlwYWdlL3N5bnRheC5odG1sXG4gIC8vIGh0dHA6Ly93d3cud2hhdHdnLm9yZy9zcGVjcy93ZWItYXBwcy9jdXJyZW50LXdvcmsvbXVsdGlwYWdlL3BhcnNpbmcuaHRtbFxuICAvLyBodHRwOi8vd3d3LndoYXR3Zy5vcmcvc3BlY3Mvd2ViLWFwcHMvY3VycmVudC13b3JrL211bHRpcGFnZS90b2tlbml6YXRpb24uaHRtbFxuICAvLyBodHRwOi8vd3d3LndoYXR3Zy5vcmcvc3BlY3Mvd2ViLWFwcHMvY3VycmVudC13b3JrL211bHRpcGFnZS90cmVlLWNvbnN0cnVjdGlvbi5odG1sXG5cbiAgLy8gV2UgaW5pdGlhbGx5IHNwbGl0IGlucHV0IHNvIHRoYXQgcG90ZW50aWFsbHkgbWVhbmluZ2Z1bCBjaGFyYWN0ZXJzXG4gIC8vIGxpa2UgJzwnIGFuZCAnPicgYXJlIHNlcGFyYXRlIHRva2VucywgdXNpbmcgYSBmYXN0IGR1bWIgcHJvY2VzcyB0aGF0XG4gIC8vIGlnbm9yZXMgcXVvdGluZy4gIFRoZW4gd2Ugd2FsayB0aGF0IHRva2VuIHN0cmVhbSwgYW5kIHdoZW4gd2Ugc2VlIGFcbiAgLy8gJzwnIHRoYXQncyB0aGUgc3RhcnQgb2YgYSB0YWcsIHdlIHVzZSBBVFRSX1JFIHRvIGV4dHJhY3QgdGFnXG4gIC8vIGF0dHJpYnV0ZXMgZnJvbSB0aGUgbmV4dCB0b2tlbi4gIFRoYXQgdG9rZW4gd2lsbCBuZXZlciBoYXZlIGEgJz4nXG4gIC8vIGNoYXJhY3Rlci4gIEhvd2V2ZXIsIGl0IG1pZ2h0IGhhdmUgYW4gdW5iYWxhbmNlZCBxdW90ZSBjaGFyYWN0ZXIsIGFuZFxuICAvLyB3aGVuIHdlIHNlZSB0aGF0LCB3ZSBjb21iaW5lIGFkZGl0aW9uYWwgdG9rZW5zIHRvIGJhbGFuY2UgdGhlIHF1b3RlLlxuXG4gIHZhciBBVFRSX1JFID0gbmV3IFJlZ0V4cChcbiAgICAnXlxcXFxzKicgK1xuICAgICcoWy0uOlxcXFx3XSspJyArICAgICAgICAgICAgIC8vIDEgPSBBdHRyaWJ1dGUgbmFtZVxuICAgICcoPzonICsgKFxuICAgICAgJ1xcXFxzKig9KVxcXFxzKicgKyAgICAgICAgICAgLy8gMiA9IElzIHRoZXJlIGEgdmFsdWU/XG4gICAgICAnKCcgKyAoICAgICAgICAgICAgICAgICAgIC8vIDMgPSBBdHRyaWJ1dGUgdmFsdWVcbiAgICAgICAgLy8gVE9ETyhmZWxpeDhhKTogbWF5YmUgdXNlIGJhY2tyZWYgdG8gbWF0Y2ggcXVvdGVzXG4gICAgICAgICcoXFxcIilbXlxcXCJdKihcXFwifCQpJyArICAgIC8vIDQsIDUgPSBEb3VibGUtcXVvdGVkIHN0cmluZ1xuICAgICAgICAnfCcgK1xuICAgICAgICAnKFxcJylbXlxcJ10qKFxcJ3wkKScgKyAgICAvLyA2LCA3ID0gU2luZ2xlLXF1b3RlZCBzdHJpbmdcbiAgICAgICAgJ3wnICtcbiAgICAgICAgLy8gUG9zaXRpdmUgbG9va2FoZWFkIHRvIHByZXZlbnQgaW50ZXJwcmV0YXRpb24gb2ZcbiAgICAgICAgLy8gPGZvbyBhPSBiPWM+IGFzIDxmb28gYT0nYj1jJz5cbiAgICAgICAgLy8gVE9ETyhmZWxpeDhhKTogbWlnaHQgYmUgYWJsZSB0byBkcm9wIHRoaXMgY2FzZVxuICAgICAgICAnKD89W2Etel1bLVxcXFx3XSpcXFxccyo9KScgK1xuICAgICAgICAnfCcgK1xuICAgICAgICAvLyBVbnF1b3RlZCB2YWx1ZSB0aGF0IGlzbid0IGFuIGF0dHJpYnV0ZSBuYW1lXG4gICAgICAgIC8vIChzaW5jZSB3ZSBkaWRuJ3QgbWF0Y2ggdGhlIHBvc2l0aXZlIGxvb2thaGVhZCBhYm92ZSlcbiAgICAgICAgJ1teXFxcIlxcJ1xcXFxzXSonICkgK1xuICAgICAgJyknICkgK1xuICAgICcpPycsXG4gICAgJ2knKTtcblxuICAvLyBmYWxzZSBvbiBJRTw9OCwgdHJ1ZSBvbiBtb3N0IG90aGVyIGJyb3dzZXJzXG4gIHZhciBzcGxpdFdpbGxDYXB0dXJlID0gKCdhLGInLnNwbGl0KC8oLCkvKS5sZW5ndGggPT09IDMpO1xuXG4gIC8vIGJpdG1hc2sgZm9yIHRhZ3Mgd2l0aCBzcGVjaWFsIHBhcnNpbmcsIGxpa2UgPHNjcmlwdD4gYW5kIDx0ZXh0YXJlYT5cbiAgdmFyIEVGTEFHU19URVhUID0gaHRtbDQuZWZsYWdzWydDREFUQSddIHwgaHRtbDQuZWZsYWdzWydSQ0RBVEEnXTtcblxuICAvKipcbiAgICogR2l2ZW4gYSBTQVgtbGlrZSBldmVudCBoYW5kbGVyLCBwcm9kdWNlIGEgZnVuY3Rpb24gdGhhdCBmZWVkcyB0aG9zZVxuICAgKiBldmVudHMgYW5kIGEgcGFyYW1ldGVyIHRvIHRoZSBldmVudCBoYW5kbGVyLlxuICAgKlxuICAgKiBUaGUgZXZlbnQgaGFuZGxlciBoYXMgdGhlIGZvcm06e0Bjb2RlXG4gICAqIHtcbiAgICogICAvLyBOYW1lIGlzIGFuIHVwcGVyLWNhc2UgSFRNTCB0YWcgbmFtZS4gIEF0dHJpYnMgaXMgYW4gYXJyYXkgb2ZcbiAgICogICAvLyBhbHRlcm5hdGluZyB1cHBlci1jYXNlIGF0dHJpYnV0ZSBuYW1lcywgYW5kIGF0dHJpYnV0ZSB2YWx1ZXMuICBUaGVcbiAgICogICAvLyBhdHRyaWJzIGFycmF5IGlzIHJldXNlZCBieSB0aGUgcGFyc2VyLiAgUGFyYW0gaXMgdGhlIHZhbHVlIHBhc3NlZCB0b1xuICAgKiAgIC8vIHRoZSBzYXhQYXJzZXIuXG4gICAqICAgc3RhcnRUYWc6IGZ1bmN0aW9uIChuYW1lLCBhdHRyaWJzLCBwYXJhbSkgeyAuLi4gfSxcbiAgICogICBlbmRUYWc6ICAgZnVuY3Rpb24gKG5hbWUsIHBhcmFtKSB7IC4uLiB9LFxuICAgKiAgIHBjZGF0YTogICBmdW5jdGlvbiAodGV4dCwgcGFyYW0pIHsgLi4uIH0sXG4gICAqICAgcmNkYXRhOiAgIGZ1bmN0aW9uICh0ZXh0LCBwYXJhbSkgeyAuLi4gfSxcbiAgICogICBjZGF0YTogICAgZnVuY3Rpb24gKHRleHQsIHBhcmFtKSB7IC4uLiB9LFxuICAgKiAgIHN0YXJ0RG9jOiBmdW5jdGlvbiAocGFyYW0pIHsgLi4uIH0sXG4gICAqICAgZW5kRG9jOiAgIGZ1bmN0aW9uIChwYXJhbSkgeyAuLi4gfVxuICAgKiB9fVxuICAgKlxuICAgKiBAcGFyYW0ge09iamVjdH0gaGFuZGxlciBhIHJlY29yZCBjb250YWluaW5nIGV2ZW50IGhhbmRsZXJzLlxuICAgKiBAcmV0dXJuIHtmdW5jdGlvbihzdHJpbmcsIE9iamVjdCl9IEEgZnVuY3Rpb24gdGhhdCB0YWtlcyBhIGNodW5rIG9mIEhUTUxcbiAgICogICAgIGFuZCBhIHBhcmFtZXRlci4gIFRoZSBwYXJhbWV0ZXIgaXMgcGFzc2VkIG9uIHRvIHRoZSBoYW5kbGVyIG1ldGhvZHMuXG4gICAqL1xuICBmdW5jdGlvbiBtYWtlU2F4UGFyc2VyKGhhbmRsZXIpIHtcbiAgICAvLyBBY2NlcHQgcXVvdGVkIG9yIHVucXVvdGVkIGtleXMgKENsb3N1cmUgY29tcGF0KVxuICAgIHZhciBoY29weSA9IHtcbiAgICAgIGNkYXRhOiBoYW5kbGVyLmNkYXRhIHx8IGhhbmRsZXJbJ2NkYXRhJ10sXG4gICAgICBjb21tZW50OiBoYW5kbGVyLmNvbW1lbnQgfHwgaGFuZGxlclsnY29tbWVudCddLFxuICAgICAgZW5kRG9jOiBoYW5kbGVyLmVuZERvYyB8fCBoYW5kbGVyWydlbmREb2MnXSxcbiAgICAgIGVuZFRhZzogaGFuZGxlci5lbmRUYWcgfHwgaGFuZGxlclsnZW5kVGFnJ10sXG4gICAgICBwY2RhdGE6IGhhbmRsZXIucGNkYXRhIHx8IGhhbmRsZXJbJ3BjZGF0YSddLFxuICAgICAgcmNkYXRhOiBoYW5kbGVyLnJjZGF0YSB8fCBoYW5kbGVyWydyY2RhdGEnXSxcbiAgICAgIHN0YXJ0RG9jOiBoYW5kbGVyLnN0YXJ0RG9jIHx8IGhhbmRsZXJbJ3N0YXJ0RG9jJ10sXG4gICAgICBzdGFydFRhZzogaGFuZGxlci5zdGFydFRhZyB8fCBoYW5kbGVyWydzdGFydFRhZyddXG4gICAgfTtcbiAgICByZXR1cm4gZnVuY3Rpb24oaHRtbFRleHQsIHBhcmFtKSB7XG4gICAgICByZXR1cm4gcGFyc2UoaHRtbFRleHQsIGhjb3B5LCBwYXJhbSk7XG4gICAgfTtcbiAgfVxuXG4gIC8vIFBhcnNpbmcgc3RyYXRlZ3kgaXMgdG8gc3BsaXQgaW5wdXQgaW50byBwYXJ0cyB0aGF0IG1pZ2h0IGJlIGxleGljYWxseVxuICAvLyBtZWFuaW5nZnVsIChldmVyeSBcIj5cIiBiZWNvbWVzIGEgc2VwYXJhdGUgcGFydCksIGFuZCB0aGVuIHJlY29tYmluZVxuICAvLyBwYXJ0cyBpZiB3ZSBkaXNjb3ZlciB0aGV5J3JlIGluIGEgZGlmZmVyZW50IGNvbnRleHQuXG5cbiAgLy8gVE9ETyhmZWxpeDhhKTogU2lnbmlmaWNhbnQgcGVyZm9ybWFuY2UgcmVncmVzc2lvbnMgZnJvbSAtbGVnYWN5LFxuICAvLyB0ZXN0ZWQgb25cbiAgLy8gICAgQ2hyb21lIDE4LjBcbiAgLy8gICAgRmlyZWZveCAxMS4wXG4gIC8vICAgIElFIDYsIDcsIDgsIDlcbiAgLy8gICAgT3BlcmEgMTEuNjFcbiAgLy8gICAgU2FmYXJpIDUuMS4zXG4gIC8vIE1hbnkgb2YgdGhlc2UgYXJlIHVudXN1YWwgcGF0dGVybnMgdGhhdCBhcmUgbGluZWFybHkgc2xvd2VyIGFuZCBzdGlsbFxuICAvLyBwcmV0dHkgZmFzdCAoZWcgMW1zIHRvIDVtcyksIHNvIG5vdCBuZWNlc3NhcmlseSB3b3J0aCBmaXhpbmcuXG5cbiAgLy8gVE9ETyhmZWxpeDhhKTogXCI8c2NyaXB0PiAmJiAmJiAmJiAuLi4gPFxcL3NjcmlwdD5cIiBpcyBzbG93ZXIgb24gYWxsXG4gIC8vIGJyb3dzZXJzLiAgVGhlIGhvdHNwb3QgaXMgaHRtbFNwbGl0LlxuXG4gIC8vIFRPRE8oZmVsaXg4YSk6IFwiPHAgdGl0bGU9Jz4+Pj4uLi4nPjxcXC9wPlwiIGlzIHNsb3dlciBvbiBhbGwgYnJvd3NlcnMuXG4gIC8vIFRoaXMgaXMgcGFydGx5IGh0bWxTcGxpdCwgYnV0IHRoZSBob3RzcG90IGlzIHBhcnNlVGFnQW5kQXR0cnMuXG5cbiAgLy8gVE9ETyhmZWxpeDhhKTogXCI8YT48XFwvYT48YT48XFwvYT4uLi5cIiBpcyBzbG93ZXIgb24gSUU5LlxuICAvLyBcIjxhPjE8XFwvYT48YT4xPFxcL2E+Li4uXCIgaXMgZmFzdGVyLCBcIjxhPjxcXC9hPjI8YT48XFwvYT4yLi4uXCIgaXMgZmFzdGVyLlxuXG4gIC8vIFRPRE8oZmVsaXg4YSk6IFwiPHA8cDxwLi4uXCIgaXMgc2xvd2VyIG9uIElFWzYtOF1cblxuICB2YXIgY29udGludWF0aW9uTWFya2VyID0ge307XG4gIGZ1bmN0aW9uIHBhcnNlKGh0bWxUZXh0LCBoYW5kbGVyLCBwYXJhbSkge1xuICAgIHZhciBtLCBwLCB0YWdOYW1lO1xuICAgIHZhciBwYXJ0cyA9IGh0bWxTcGxpdChodG1sVGV4dCk7XG4gICAgdmFyIHN0YXRlID0ge1xuICAgICAgbm9Nb3JlR1Q6IGZhbHNlLFxuICAgICAgbm9Nb3JlRW5kQ29tbWVudHM6IGZhbHNlXG4gICAgfTtcbiAgICBwYXJzZUNQUyhoYW5kbGVyLCBwYXJ0cywgMCwgc3RhdGUsIHBhcmFtKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIGNvbnRpbnVhdGlvbk1ha2VyKGgsIHBhcnRzLCBpbml0aWFsLCBzdGF0ZSwgcGFyYW0pIHtcbiAgICByZXR1cm4gZnVuY3Rpb24gKCkge1xuICAgICAgcGFyc2VDUFMoaCwgcGFydHMsIGluaXRpYWwsIHN0YXRlLCBwYXJhbSk7XG4gICAgfTtcbiAgfVxuXG4gIGZ1bmN0aW9uIHBhcnNlQ1BTKGgsIHBhcnRzLCBpbml0aWFsLCBzdGF0ZSwgcGFyYW0pIHtcbiAgICB0cnkge1xuICAgICAgaWYgKGguc3RhcnREb2MgJiYgaW5pdGlhbCA9PSAwKSB7IGguc3RhcnREb2MocGFyYW0pOyB9XG4gICAgICB2YXIgbSwgcCwgdGFnTmFtZTtcbiAgICAgIGZvciAodmFyIHBvcyA9IGluaXRpYWwsIGVuZCA9IHBhcnRzLmxlbmd0aDsgcG9zIDwgZW5kOykge1xuICAgICAgICB2YXIgY3VycmVudCA9IHBhcnRzW3BvcysrXTtcbiAgICAgICAgdmFyIG5leHQgPSBwYXJ0c1twb3NdO1xuICAgICAgICBzd2l0Y2ggKGN1cnJlbnQpIHtcbiAgICAgICAgY2FzZSAnJic6XG4gICAgICAgICAgaWYgKEVOVElUWV9SRV8yLnRlc3QobmV4dCkpIHtcbiAgICAgICAgICAgIGlmIChoLnBjZGF0YSkge1xuICAgICAgICAgICAgICBoLnBjZGF0YSgnJicgKyBuZXh0LCBwYXJhbSwgY29udGludWF0aW9uTWFya2VyLFxuICAgICAgICAgICAgICAgIGNvbnRpbnVhdGlvbk1ha2VyKGgsIHBhcnRzLCBwb3MsIHN0YXRlLCBwYXJhbSkpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcG9zKys7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGlmIChoLnBjZGF0YSkgeyBoLnBjZGF0YShcIiZhbXA7XCIsIHBhcmFtLCBjb250aW51YXRpb25NYXJrZXIsXG4gICAgICAgICAgICAgICAgY29udGludWF0aW9uTWFrZXIoaCwgcGFydHMsIHBvcywgc3RhdGUsIHBhcmFtKSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICc8XFwvJzpcbiAgICAgICAgICBpZiAobSA9IC9eKFstXFx3Ol0rKVteXFwnXFxcIl0qLy5leGVjKG5leHQpKSB7XG4gICAgICAgICAgICBpZiAobVswXS5sZW5ndGggPT09IG5leHQubGVuZ3RoICYmIHBhcnRzW3BvcyArIDFdID09PSAnPicpIHtcbiAgICAgICAgICAgICAgLy8gZmFzdCBjYXNlLCBubyBhdHRyaWJ1dGUgcGFyc2luZyBuZWVkZWRcbiAgICAgICAgICAgICAgcG9zICs9IDI7XG4gICAgICAgICAgICAgIHRhZ05hbWUgPSBtWzFdLnRvTG93ZXJDYXNlKCk7XG4gICAgICAgICAgICAgIGlmIChoLmVuZFRhZykge1xuICAgICAgICAgICAgICAgIGguZW5kVGFnKHRhZ05hbWUsIHBhcmFtLCBjb250aW51YXRpb25NYXJrZXIsXG4gICAgICAgICAgICAgICAgICBjb250aW51YXRpb25NYWtlcihoLCBwYXJ0cywgcG9zLCBzdGF0ZSwgcGFyYW0pKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgLy8gc2xvdyBjYXNlLCBuZWVkIHRvIHBhcnNlIGF0dHJpYnV0ZXNcbiAgICAgICAgICAgICAgLy8gVE9ETyhmZWxpeDhhKTogZG8gd2UgcmVhbGx5IGNhcmUgYWJvdXQgbWlzcGFyc2luZyB0aGlzP1xuICAgICAgICAgICAgICBwb3MgPSBwYXJzZUVuZFRhZyhcbiAgICAgICAgICAgICAgICBwYXJ0cywgcG9zLCBoLCBwYXJhbSwgY29udGludWF0aW9uTWFya2VyLCBzdGF0ZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGlmIChoLnBjZGF0YSkge1xuICAgICAgICAgICAgICBoLnBjZGF0YSgnJmx0Oy8nLCBwYXJhbSwgY29udGludWF0aW9uTWFya2VyLFxuICAgICAgICAgICAgICAgIGNvbnRpbnVhdGlvbk1ha2VyKGgsIHBhcnRzLCBwb3MsIHN0YXRlLCBwYXJhbSkpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnPCc6XG4gICAgICAgICAgaWYgKG0gPSAvXihbLVxcdzpdKylcXHMqXFwvPy8uZXhlYyhuZXh0KSkge1xuICAgICAgICAgICAgaWYgKG1bMF0ubGVuZ3RoID09PSBuZXh0Lmxlbmd0aCAmJiBwYXJ0c1twb3MgKyAxXSA9PT0gJz4nKSB7XG4gICAgICAgICAgICAgIC8vIGZhc3QgY2FzZSwgbm8gYXR0cmlidXRlIHBhcnNpbmcgbmVlZGVkXG4gICAgICAgICAgICAgIHBvcyArPSAyO1xuICAgICAgICAgICAgICB0YWdOYW1lID0gbVsxXS50b0xvd2VyQ2FzZSgpO1xuICAgICAgICAgICAgICBpZiAoaC5zdGFydFRhZykge1xuICAgICAgICAgICAgICAgIGguc3RhcnRUYWcodGFnTmFtZSwgW10sIHBhcmFtLCBjb250aW51YXRpb25NYXJrZXIsXG4gICAgICAgICAgICAgICAgICBjb250aW51YXRpb25NYWtlcihoLCBwYXJ0cywgcG9zLCBzdGF0ZSwgcGFyYW0pKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAvLyB0YWdzIGxpa2UgPHNjcmlwdD4gYW5kIDx0ZXh0YXJlYT4gaGF2ZSBzcGVjaWFsIHBhcnNpbmdcbiAgICAgICAgICAgICAgdmFyIGVmbGFncyA9IGh0bWw0LkVMRU1FTlRTW3RhZ05hbWVdO1xuICAgICAgICAgICAgICBpZiAoZWZsYWdzICYgRUZMQUdTX1RFWFQpIHtcbiAgICAgICAgICAgICAgICB2YXIgdGFnID0geyBuYW1lOiB0YWdOYW1lLCBuZXh0OiBwb3MsIGVmbGFnczogZWZsYWdzIH07XG4gICAgICAgICAgICAgICAgcG9zID0gcGFyc2VUZXh0KFxuICAgICAgICAgICAgICAgICAgcGFydHMsIHRhZywgaCwgcGFyYW0sIGNvbnRpbnVhdGlvbk1hcmtlciwgc3RhdGUpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAvLyBzbG93IGNhc2UsIG5lZWQgdG8gcGFyc2UgYXR0cmlidXRlc1xuICAgICAgICAgICAgICBwb3MgPSBwYXJzZVN0YXJ0VGFnKFxuICAgICAgICAgICAgICAgIHBhcnRzLCBwb3MsIGgsIHBhcmFtLCBjb250aW51YXRpb25NYXJrZXIsIHN0YXRlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaWYgKGgucGNkYXRhKSB7XG4gICAgICAgICAgICAgIGgucGNkYXRhKCcmbHQ7JywgcGFyYW0sIGNvbnRpbnVhdGlvbk1hcmtlcixcbiAgICAgICAgICAgICAgICBjb250aW51YXRpb25NYWtlcihoLCBwYXJ0cywgcG9zLCBzdGF0ZSwgcGFyYW0pKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJzxcXCEtLSc6XG4gICAgICAgICAgLy8gVGhlIHBhdGhvbG9naWNhbCBjYXNlIGlzIG4gY29waWVzIG9mICc8XFwhLS0nIHdpdGhvdXQgJy0tPicsIGFuZFxuICAgICAgICAgIC8vIHJlcGVhdGVkIGZhaWx1cmUgdG8gZmluZCAnLS0+JyBpcyBxdWFkcmF0aWMuICBXZSBhdm9pZCB0aGF0IGJ5XG4gICAgICAgICAgLy8gcmVtZW1iZXJpbmcgd2hlbiBzZWFyY2ggZm9yICctLT4nIGZhaWxzLlxuICAgICAgICAgIGlmICghc3RhdGUubm9Nb3JlRW5kQ29tbWVudHMpIHtcbiAgICAgICAgICAgIC8vIEEgY29tbWVudCA8XFwhLS14LS0+IGlzIHNwbGl0IGludG8gdGhyZWUgdG9rZW5zOlxuICAgICAgICAgICAgLy8gICAnPFxcIS0tJywgJ3gtLScsICc+J1xuICAgICAgICAgICAgLy8gV2Ugd2FudCB0byBmaW5kIHRoZSBuZXh0ICc+JyB0b2tlbiB0aGF0IGhhcyBhIHByZWNlZGluZyAnLS0nLlxuICAgICAgICAgICAgLy8gcG9zIGlzIGF0IHRoZSAneC0tJy5cbiAgICAgICAgICAgIGZvciAocCA9IHBvcyArIDE7IHAgPCBlbmQ7IHArKykge1xuICAgICAgICAgICAgICBpZiAocGFydHNbcF0gPT09ICc+JyAmJiAvLS0kLy50ZXN0KHBhcnRzW3AgLSAxXSkpIHsgYnJlYWs7IH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChwIDwgZW5kKSB7XG4gICAgICAgICAgICAgIGlmIChoLmNvbW1lbnQpIHtcbiAgICAgICAgICAgICAgICB2YXIgY29tbWVudCA9IHBhcnRzLnNsaWNlKHBvcywgcCkuam9pbignJyk7XG4gICAgICAgICAgICAgICAgaC5jb21tZW50KFxuICAgICAgICAgICAgICAgICAgY29tbWVudC5zdWJzdHIoMCwgY29tbWVudC5sZW5ndGggLSAyKSwgcGFyYW0sXG4gICAgICAgICAgICAgICAgICBjb250aW51YXRpb25NYXJrZXIsXG4gICAgICAgICAgICAgICAgICBjb250aW51YXRpb25NYWtlcihoLCBwYXJ0cywgcCArIDEsIHN0YXRlLCBwYXJhbSkpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIHBvcyA9IHAgKyAxO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgc3RhdGUubm9Nb3JlRW5kQ29tbWVudHMgPSB0cnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoc3RhdGUubm9Nb3JlRW5kQ29tbWVudHMpIHtcbiAgICAgICAgICAgIGlmIChoLnBjZGF0YSkge1xuICAgICAgICAgICAgICBoLnBjZGF0YSgnJmx0OyEtLScsIHBhcmFtLCBjb250aW51YXRpb25NYXJrZXIsXG4gICAgICAgICAgICAgICAgY29udGludWF0aW9uTWFrZXIoaCwgcGFydHMsIHBvcywgc3RhdGUsIHBhcmFtKSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICc8XFwhJzpcbiAgICAgICAgICBpZiAoIS9eXFx3Ly50ZXN0KG5leHQpKSB7XG4gICAgICAgICAgICBpZiAoaC5wY2RhdGEpIHtcbiAgICAgICAgICAgICAgaC5wY2RhdGEoJyZsdDshJywgcGFyYW0sIGNvbnRpbnVhdGlvbk1hcmtlcixcbiAgICAgICAgICAgICAgICBjb250aW51YXRpb25NYWtlcihoLCBwYXJ0cywgcG9zLCBzdGF0ZSwgcGFyYW0pKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gc2ltaWxhciB0byBub01vcmVFbmRDb21tZW50IGxvZ2ljXG4gICAgICAgICAgICBpZiAoIXN0YXRlLm5vTW9yZUdUKSB7XG4gICAgICAgICAgICAgIGZvciAocCA9IHBvcyArIDE7IHAgPCBlbmQ7IHArKykge1xuICAgICAgICAgICAgICAgIGlmIChwYXJ0c1twXSA9PT0gJz4nKSB7IGJyZWFrOyB9XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgaWYgKHAgPCBlbmQpIHtcbiAgICAgICAgICAgICAgICBwb3MgPSBwICsgMTtcbiAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBzdGF0ZS5ub01vcmVHVCA9IHRydWU7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChzdGF0ZS5ub01vcmVHVCkge1xuICAgICAgICAgICAgICBpZiAoaC5wY2RhdGEpIHtcbiAgICAgICAgICAgICAgICBoLnBjZGF0YSgnJmx0OyEnLCBwYXJhbSwgY29udGludWF0aW9uTWFya2VyLFxuICAgICAgICAgICAgICAgICAgY29udGludWF0aW9uTWFrZXIoaCwgcGFydHMsIHBvcywgc3RhdGUsIHBhcmFtKSk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJzw/JzpcbiAgICAgICAgICAvLyBzaW1pbGFyIHRvIG5vTW9yZUVuZENvbW1lbnQgbG9naWNcbiAgICAgICAgICBpZiAoIXN0YXRlLm5vTW9yZUdUKSB7XG4gICAgICAgICAgICBmb3IgKHAgPSBwb3MgKyAxOyBwIDwgZW5kOyBwKyspIHtcbiAgICAgICAgICAgICAgaWYgKHBhcnRzW3BdID09PSAnPicpIHsgYnJlYWs7IH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChwIDwgZW5kKSB7XG4gICAgICAgICAgICAgIHBvcyA9IHAgKyAxO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgc3RhdGUubm9Nb3JlR1QgPSB0cnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoc3RhdGUubm9Nb3JlR1QpIHtcbiAgICAgICAgICAgIGlmIChoLnBjZGF0YSkge1xuICAgICAgICAgICAgICBoLnBjZGF0YSgnJmx0Oz8nLCBwYXJhbSwgY29udGludWF0aW9uTWFya2VyLFxuICAgICAgICAgICAgICAgIGNvbnRpbnVhdGlvbk1ha2VyKGgsIHBhcnRzLCBwb3MsIHN0YXRlLCBwYXJhbSkpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnPic6XG4gICAgICAgICAgaWYgKGgucGNkYXRhKSB7XG4gICAgICAgICAgICBoLnBjZGF0YShcIiZndDtcIiwgcGFyYW0sIGNvbnRpbnVhdGlvbk1hcmtlcixcbiAgICAgICAgICAgICAgY29udGludWF0aW9uTWFrZXIoaCwgcGFydHMsIHBvcywgc3RhdGUsIHBhcmFtKSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICcnOlxuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgIGlmIChoLnBjZGF0YSkge1xuICAgICAgICAgICAgaC5wY2RhdGEoY3VycmVudCwgcGFyYW0sIGNvbnRpbnVhdGlvbk1hcmtlcixcbiAgICAgICAgICAgICAgY29udGludWF0aW9uTWFrZXIoaCwgcGFydHMsIHBvcywgc3RhdGUsIHBhcmFtKSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAoaC5lbmREb2MpIHsgaC5lbmREb2MocGFyYW0pOyB9XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgaWYgKGUgIT09IGNvbnRpbnVhdGlvbk1hcmtlcikgeyB0aHJvdyBlOyB9XG4gICAgfVxuICB9XG5cbiAgLy8gU3BsaXQgc3RyIGludG8gcGFydHMgZm9yIHRoZSBodG1sIHBhcnNlci5cbiAgZnVuY3Rpb24gaHRtbFNwbGl0KHN0cikge1xuICAgIC8vIGNhbid0IGhvaXN0IHRoaXMgb3V0IG9mIHRoZSBmdW5jdGlvbiBiZWNhdXNlIG9mIHRoZSByZS5leGVjIGxvb3AuXG4gICAgdmFyIHJlID0gLyg8XFwvfDxcXCEtLXw8WyE/XXxbJjw+XSkvZztcbiAgICBzdHIgKz0gJyc7XG4gICAgaWYgKHNwbGl0V2lsbENhcHR1cmUpIHtcbiAgICAgIHJldHVybiBzdHIuc3BsaXQocmUpO1xuICAgIH0gZWxzZSB7XG4gICAgICB2YXIgcGFydHMgPSBbXTtcbiAgICAgIHZhciBsYXN0UG9zID0gMDtcbiAgICAgIHZhciBtO1xuICAgICAgd2hpbGUgKChtID0gcmUuZXhlYyhzdHIpKSAhPT0gbnVsbCkge1xuICAgICAgICBwYXJ0cy5wdXNoKHN0ci5zdWJzdHJpbmcobGFzdFBvcywgbS5pbmRleCkpO1xuICAgICAgICBwYXJ0cy5wdXNoKG1bMF0pO1xuICAgICAgICBsYXN0UG9zID0gbS5pbmRleCArIG1bMF0ubGVuZ3RoO1xuICAgICAgfVxuICAgICAgcGFydHMucHVzaChzdHIuc3Vic3RyaW5nKGxhc3RQb3MpKTtcbiAgICAgIHJldHVybiBwYXJ0cztcbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBwYXJzZUVuZFRhZyhwYXJ0cywgcG9zLCBoLCBwYXJhbSwgY29udGludWF0aW9uTWFya2VyLCBzdGF0ZSkge1xuICAgIHZhciB0YWcgPSBwYXJzZVRhZ0FuZEF0dHJzKHBhcnRzLCBwb3MpO1xuICAgIC8vIGRyb3AgdW5jbG9zZWQgdGFnc1xuICAgIGlmICghdGFnKSB7IHJldHVybiBwYXJ0cy5sZW5ndGg7IH1cbiAgICBpZiAoaC5lbmRUYWcpIHtcbiAgICAgIGguZW5kVGFnKHRhZy5uYW1lLCBwYXJhbSwgY29udGludWF0aW9uTWFya2VyLFxuICAgICAgICBjb250aW51YXRpb25NYWtlcihoLCBwYXJ0cywgcG9zLCBzdGF0ZSwgcGFyYW0pKTtcbiAgICB9XG4gICAgcmV0dXJuIHRhZy5uZXh0O1xuICB9XG5cbiAgZnVuY3Rpb24gcGFyc2VTdGFydFRhZyhwYXJ0cywgcG9zLCBoLCBwYXJhbSwgY29udGludWF0aW9uTWFya2VyLCBzdGF0ZSkge1xuICAgIHZhciB0YWcgPSBwYXJzZVRhZ0FuZEF0dHJzKHBhcnRzLCBwb3MpO1xuICAgIC8vIGRyb3AgdW5jbG9zZWQgdGFnc1xuICAgIGlmICghdGFnKSB7IHJldHVybiBwYXJ0cy5sZW5ndGg7IH1cbiAgICBpZiAoaC5zdGFydFRhZykge1xuICAgICAgaC5zdGFydFRhZyh0YWcubmFtZSwgdGFnLmF0dHJzLCBwYXJhbSwgY29udGludWF0aW9uTWFya2VyLFxuICAgICAgICBjb250aW51YXRpb25NYWtlcihoLCBwYXJ0cywgdGFnLm5leHQsIHN0YXRlLCBwYXJhbSkpO1xuICAgIH1cbiAgICAvLyB0YWdzIGxpa2UgPHNjcmlwdD4gYW5kIDx0ZXh0YXJlYT4gaGF2ZSBzcGVjaWFsIHBhcnNpbmdcbiAgICBpZiAodGFnLmVmbGFncyAmIEVGTEFHU19URVhUKSB7XG4gICAgICByZXR1cm4gcGFyc2VUZXh0KHBhcnRzLCB0YWcsIGgsIHBhcmFtLCBjb250aW51YXRpb25NYXJrZXIsIHN0YXRlKTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIHRhZy5uZXh0O1xuICAgIH1cbiAgfVxuXG4gIHZhciBlbmRUYWdSZSA9IHt9O1xuXG4gIC8vIFRhZ3MgbGlrZSA8c2NyaXB0PiBhbmQgPHRleHRhcmVhPiBhcmUgZmxhZ2dlZCBhcyBDREFUQSBvciBSQ0RBVEEsXG4gIC8vIHdoaWNoIG1lYW5zIGV2ZXJ5dGhpbmcgaXMgdGV4dCB1bnRpbCB3ZSBzZWUgdGhlIGNvcnJlY3QgY2xvc2luZyB0YWcuXG4gIGZ1bmN0aW9uIHBhcnNlVGV4dChwYXJ0cywgdGFnLCBoLCBwYXJhbSwgY29udGludWF0aW9uTWFya2VyLCBzdGF0ZSkge1xuICAgIHZhciBlbmQgPSBwYXJ0cy5sZW5ndGg7XG4gICAgaWYgKCFlbmRUYWdSZS5oYXNPd25Qcm9wZXJ0eSh0YWcubmFtZSkpIHtcbiAgICAgIGVuZFRhZ1JlW3RhZy5uYW1lXSA9IG5ldyBSZWdFeHAoJ14nICsgdGFnLm5hbWUgKyAnKD86W1xcXFxzXFxcXC9dfCQpJywgJ2knKTtcbiAgICB9XG4gICAgdmFyIHJlID0gZW5kVGFnUmVbdGFnLm5hbWVdO1xuICAgIHZhciBmaXJzdCA9IHRhZy5uZXh0O1xuICAgIHZhciBwID0gdGFnLm5leHQgKyAxO1xuICAgIGZvciAoOyBwIDwgZW5kOyBwKyspIHtcbiAgICAgIGlmIChwYXJ0c1twIC0gMV0gPT09ICc8XFwvJyAmJiByZS50ZXN0KHBhcnRzW3BdKSkgeyBicmVhazsgfVxuICAgIH1cbiAgICBpZiAocCA8IGVuZCkgeyBwIC09IDE7IH1cbiAgICB2YXIgYnVmID0gcGFydHMuc2xpY2UoZmlyc3QsIHApLmpvaW4oJycpO1xuICAgIGlmICh0YWcuZWZsYWdzICYgaHRtbDQuZWZsYWdzWydDREFUQSddKSB7XG4gICAgICBpZiAoaC5jZGF0YSkge1xuICAgICAgICBoLmNkYXRhKGJ1ZiwgcGFyYW0sIGNvbnRpbnVhdGlvbk1hcmtlcixcbiAgICAgICAgICBjb250aW51YXRpb25NYWtlcihoLCBwYXJ0cywgcCwgc3RhdGUsIHBhcmFtKSk7XG4gICAgICB9XG4gICAgfSBlbHNlIGlmICh0YWcuZWZsYWdzICYgaHRtbDQuZWZsYWdzWydSQ0RBVEEnXSkge1xuICAgICAgaWYgKGgucmNkYXRhKSB7XG4gICAgICAgIGgucmNkYXRhKG5vcm1hbGl6ZVJDRGF0YShidWYpLCBwYXJhbSwgY29udGludWF0aW9uTWFya2VyLFxuICAgICAgICAgIGNvbnRpbnVhdGlvbk1ha2VyKGgsIHBhcnRzLCBwLCBzdGF0ZSwgcGFyYW0pKTtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdidWcnKTtcbiAgICB9XG4gICAgcmV0dXJuIHA7XG4gIH1cblxuICAvLyBhdCB0aGlzIHBvaW50LCBwYXJ0c1twb3MtMV0gaXMgZWl0aGVyIFwiPFwiIG9yIFwiPFxcL1wiLlxuICBmdW5jdGlvbiBwYXJzZVRhZ0FuZEF0dHJzKHBhcnRzLCBwb3MpIHtcbiAgICB2YXIgbSA9IC9eKFstXFx3Ol0rKS8uZXhlYyhwYXJ0c1twb3NdKTtcbiAgICB2YXIgdGFnID0ge307XG4gICAgdGFnLm5hbWUgPSBtWzFdLnRvTG93ZXJDYXNlKCk7XG4gICAgdGFnLmVmbGFncyA9IGh0bWw0LkVMRU1FTlRTW3RhZy5uYW1lXTtcbiAgICB2YXIgYnVmID0gcGFydHNbcG9zXS5zdWJzdHIobVswXS5sZW5ndGgpO1xuICAgIC8vIEZpbmQgdGhlIG5leHQgJz4nLiAgV2Ugb3B0aW1pc3RpY2FsbHkgYXNzdW1lIHRoaXMgJz4nIGlzIG5vdCBpbiBhXG4gICAgLy8gcXVvdGVkIGNvbnRleHQsIGFuZCBmdXJ0aGVyIGRvd24gd2UgZml4IHRoaW5ncyB1cCBpZiBpdCB0dXJucyBvdXQgdG9cbiAgICAvLyBiZSBxdW90ZWQuXG4gICAgdmFyIHAgPSBwb3MgKyAxO1xuICAgIHZhciBlbmQgPSBwYXJ0cy5sZW5ndGg7XG4gICAgZm9yICg7IHAgPCBlbmQ7IHArKykge1xuICAgICAgaWYgKHBhcnRzW3BdID09PSAnPicpIHsgYnJlYWs7IH1cbiAgICAgIGJ1ZiArPSBwYXJ0c1twXTtcbiAgICB9XG4gICAgaWYgKGVuZCA8PSBwKSB7IHJldHVybiB2b2lkIDA7IH1cbiAgICB2YXIgYXR0cnMgPSBbXTtcbiAgICB3aGlsZSAoYnVmICE9PSAnJykge1xuICAgICAgbSA9IEFUVFJfUkUuZXhlYyhidWYpO1xuICAgICAgaWYgKCFtKSB7XG4gICAgICAgIC8vIE5vIGF0dHJpYnV0ZSBmb3VuZDogc2tpcCBnYXJiYWdlXG4gICAgICAgIGJ1ZiA9IGJ1Zi5yZXBsYWNlKC9eW1xcc1xcU11bXmEtelxcc10qLywgJycpO1xuXG4gICAgICB9IGVsc2UgaWYgKChtWzRdICYmICFtWzVdKSB8fCAobVs2XSAmJiAhbVs3XSkpIHtcbiAgICAgICAgLy8gVW50ZXJtaW5hdGVkIHF1b3RlOiBzbHVycCB0byB0aGUgbmV4dCB1bnF1b3RlZCAnPidcbiAgICAgICAgdmFyIHF1b3RlID0gbVs0XSB8fCBtWzZdO1xuICAgICAgICB2YXIgc2F3UXVvdGUgPSBmYWxzZTtcbiAgICAgICAgdmFyIGFidWYgPSBbYnVmLCBwYXJ0c1twKytdXTtcbiAgICAgICAgZm9yICg7IHAgPCBlbmQ7IHArKykge1xuICAgICAgICAgIGlmIChzYXdRdW90ZSkge1xuICAgICAgICAgICAgaWYgKHBhcnRzW3BdID09PSAnPicpIHsgYnJlYWs7IH1cbiAgICAgICAgICB9IGVsc2UgaWYgKDAgPD0gcGFydHNbcF0uaW5kZXhPZihxdW90ZSkpIHtcbiAgICAgICAgICAgIHNhd1F1b3RlID0gdHJ1ZTtcbiAgICAgICAgICB9XG4gICAgICAgICAgYWJ1Zi5wdXNoKHBhcnRzW3BdKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBTbHVycCBmYWlsZWQ6IGxvc2UgdGhlIGdhcmJhZ2VcbiAgICAgICAgaWYgKGVuZCA8PSBwKSB7IGJyZWFrOyB9XG4gICAgICAgIC8vIE90aGVyd2lzZSByZXRyeSBhdHRyaWJ1dGUgcGFyc2luZ1xuICAgICAgICBidWYgPSBhYnVmLmpvaW4oJycpO1xuICAgICAgICBjb250aW51ZTtcblxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gV2UgaGF2ZSBhbiBhdHRyaWJ1dGVcbiAgICAgICAgdmFyIGFOYW1lID0gbVsxXS50b0xvd2VyQ2FzZSgpO1xuICAgICAgICB2YXIgYVZhbHVlID0gbVsyXSA/IGRlY29kZVZhbHVlKG1bM10pIDogJyc7XG4gICAgICAgIGF0dHJzLnB1c2goYU5hbWUsIGFWYWx1ZSk7XG4gICAgICAgIGJ1ZiA9IGJ1Zi5zdWJzdHIobVswXS5sZW5ndGgpO1xuICAgICAgfVxuICAgIH1cbiAgICB0YWcuYXR0cnMgPSBhdHRycztcbiAgICB0YWcubmV4dCA9IHAgKyAxO1xuICAgIHJldHVybiB0YWc7XG4gIH1cblxuICBmdW5jdGlvbiBkZWNvZGVWYWx1ZSh2KSB7XG4gICAgdmFyIHEgPSB2LmNoYXJDb2RlQXQoMCk7XG4gICAgaWYgKHEgPT09IDB4MjIgfHwgcSA9PT0gMHgyNykgeyAvLyBcIiBvciAnXG4gICAgICB2ID0gdi5zdWJzdHIoMSwgdi5sZW5ndGggLSAyKTtcbiAgICB9XG4gICAgcmV0dXJuIHVuZXNjYXBlRW50aXRpZXMoc3RyaXBOVUxzKHYpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGEgZnVuY3Rpb24gdGhhdCBzdHJpcHMgdW5zYWZlIHRhZ3MgYW5kIGF0dHJpYnV0ZXMgZnJvbSBodG1sLlxuICAgKiBAcGFyYW0ge2Z1bmN0aW9uKHN0cmluZywgQXJyYXkuPHN0cmluZz4pOiA/QXJyYXkuPHN0cmluZz59IHRhZ1BvbGljeVxuICAgKiAgICAgQSBmdW5jdGlvbiB0aGF0IHRha2VzICh0YWdOYW1lLCBhdHRyaWJzW10pLCB3aGVyZSB0YWdOYW1lIGlzIGEga2V5IGluXG4gICAqICAgICBodG1sNC5FTEVNRU5UUyBhbmQgYXR0cmlicyBpcyBhbiBhcnJheSBvZiBhbHRlcm5hdGluZyBhdHRyaWJ1dGUgbmFtZXNcbiAgICogICAgIGFuZCB2YWx1ZXMuICBJdCBzaG91bGQgcmV0dXJuIGEgcmVjb3JkIChhcyBmb2xsb3dzKSwgb3IgbnVsbCB0byBkZWxldGVcbiAgICogICAgIHRoZSBlbGVtZW50LiAgSXQncyBva2F5IGZvciB0YWdQb2xpY3kgdG8gbW9kaWZ5IHRoZSBhdHRyaWJzIGFycmF5LFxuICAgKiAgICAgYnV0IHRoZSBzYW1lIGFycmF5IGlzIHJldXNlZCwgc28gaXQgc2hvdWxkIG5vdCBiZSBoZWxkIGJldHdlZW4gY2FsbHMuXG4gICAqICAgICBSZWNvcmQga2V5czpcbiAgICogICAgICAgIGF0dHJpYnM6IChyZXF1aXJlZCkgU2FuaXRpemVkIGF0dHJpYnV0ZXMgYXJyYXkuXG4gICAqICAgICAgICB0YWdOYW1lOiBSZXBsYWNlbWVudCB0YWcgbmFtZS5cbiAgICogQHJldHVybiB7ZnVuY3Rpb24oc3RyaW5nLCBBcnJheSl9IEEgZnVuY3Rpb24gdGhhdCBzYW5pdGl6ZXMgYSBzdHJpbmcgb2ZcbiAgICogICAgIEhUTUwgYW5kIGFwcGVuZHMgcmVzdWx0IHN0cmluZ3MgdG8gdGhlIHNlY29uZCBhcmd1bWVudCwgYW4gYXJyYXkuXG4gICAqL1xuICBmdW5jdGlvbiBtYWtlSHRtbFNhbml0aXplcih0YWdQb2xpY3kpIHtcbiAgICB2YXIgc3RhY2s7XG4gICAgdmFyIGlnbm9yaW5nO1xuICAgIHZhciBlbWl0ID0gZnVuY3Rpb24gKHRleHQsIG91dCkge1xuICAgICAgaWYgKCFpZ25vcmluZykgeyBvdXQucHVzaCh0ZXh0KTsgfVxuICAgIH07XG4gICAgcmV0dXJuIG1ha2VTYXhQYXJzZXIoe1xuICAgICAgJ3N0YXJ0RG9jJzogZnVuY3Rpb24oXykge1xuICAgICAgICBzdGFjayA9IFtdO1xuICAgICAgICBpZ25vcmluZyA9IGZhbHNlO1xuICAgICAgfSxcbiAgICAgICdzdGFydFRhZyc6IGZ1bmN0aW9uKHRhZ05hbWVPcmlnLCBhdHRyaWJzLCBvdXQpIHtcbiAgICAgICAgaWYgKGlnbm9yaW5nKSB7IHJldHVybjsgfVxuICAgICAgICBpZiAoIWh0bWw0LkVMRU1FTlRTLmhhc093blByb3BlcnR5KHRhZ05hbWVPcmlnKSkgeyByZXR1cm47IH1cbiAgICAgICAgdmFyIGVmbGFnc09yaWcgPSBodG1sNC5FTEVNRU5UU1t0YWdOYW1lT3JpZ107XG4gICAgICAgIGlmIChlZmxhZ3NPcmlnICYgaHRtbDQuZWZsYWdzWydGT0xEQUJMRSddKSB7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIGRlY2lzaW9uID0gdGFnUG9saWN5KHRhZ05hbWVPcmlnLCBhdHRyaWJzKTtcbiAgICAgICAgaWYgKCFkZWNpc2lvbikge1xuICAgICAgICAgIGlnbm9yaW5nID0gIShlZmxhZ3NPcmlnICYgaHRtbDQuZWZsYWdzWydFTVBUWSddKTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH0gZWxzZSBpZiAodHlwZW9mIGRlY2lzaW9uICE9PSAnb2JqZWN0Jykge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcigndGFnUG9saWN5IGRpZCBub3QgcmV0dXJuIG9iamVjdCAob2xkIEFQST8pJyk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCdhdHRyaWJzJyBpbiBkZWNpc2lvbikge1xuICAgICAgICAgIGF0dHJpYnMgPSBkZWNpc2lvblsnYXR0cmlicyddO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcigndGFnUG9saWN5IGdhdmUgbm8gYXR0cmlicycpO1xuICAgICAgICB9XG4gICAgICAgIHZhciBlZmxhZ3NSZXA7XG4gICAgICAgIHZhciB0YWdOYW1lUmVwO1xuICAgICAgICBpZiAoJ3RhZ05hbWUnIGluIGRlY2lzaW9uKSB7XG4gICAgICAgICAgdGFnTmFtZVJlcCA9IGRlY2lzaW9uWyd0YWdOYW1lJ107XG4gICAgICAgICAgZWZsYWdzUmVwID0gaHRtbDQuRUxFTUVOVFNbdGFnTmFtZVJlcF07XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdGFnTmFtZVJlcCA9IHRhZ05hbWVPcmlnO1xuICAgICAgICAgIGVmbGFnc1JlcCA9IGVmbGFnc09yaWc7XG4gICAgICAgIH1cbiAgICAgICAgLy8gVE9ETyhtaWtlc2FtdWVsKTogcmVseWluZyBvbiB0YWdQb2xpY3kgbm90IHRvIGluc2VydCB1bnNhZmVcbiAgICAgICAgLy8gYXR0cmlidXRlIG5hbWVzLlxuXG4gICAgICAgIC8vIElmIHRoaXMgaXMgYW4gb3B0aW9uYWwtZW5kLXRhZyBlbGVtZW50IGFuZCBlaXRoZXIgdGhpcyBlbGVtZW50IG9yIGl0c1xuICAgICAgICAvLyBwcmV2aW91cyBsaWtlIHNpYmxpbmcgd2FzIHJld3JpdHRlbiwgdGhlbiBpbnNlcnQgYSBjbG9zZSB0YWcgdG9cbiAgICAgICAgLy8gcHJlc2VydmUgc3RydWN0dXJlLlxuICAgICAgICBpZiAoZWZsYWdzT3JpZyAmIGh0bWw0LmVmbGFnc1snT1BUSU9OQUxfRU5EVEFHJ10pIHtcbiAgICAgICAgICB2YXIgb25TdGFjayA9IHN0YWNrW3N0YWNrLmxlbmd0aCAtIDFdO1xuICAgICAgICAgIGlmIChvblN0YWNrICYmIG9uU3RhY2sub3JpZyA9PT0gdGFnTmFtZU9yaWcgJiZcbiAgICAgICAgICAgICAgKG9uU3RhY2sucmVwICE9PSB0YWdOYW1lUmVwIHx8IHRhZ05hbWVPcmlnICE9PSB0YWdOYW1lUmVwKSkge1xuICAgICAgICAgICAgICAgIG91dC5wdXNoKCc8XFwvJywgb25TdGFjay5yZXAsICc+Jyk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCEoZWZsYWdzT3JpZyAmIGh0bWw0LmVmbGFnc1snRU1QVFknXSkpIHtcbiAgICAgICAgICBzdGFjay5wdXNoKHtvcmlnOiB0YWdOYW1lT3JpZywgcmVwOiB0YWdOYW1lUmVwfSk7XG4gICAgICAgIH1cblxuICAgICAgICBvdXQucHVzaCgnPCcsIHRhZ05hbWVSZXApO1xuICAgICAgICBmb3IgKHZhciBpID0gMCwgbiA9IGF0dHJpYnMubGVuZ3RoOyBpIDwgbjsgaSArPSAyKSB7XG4gICAgICAgICAgdmFyIGF0dHJpYk5hbWUgPSBhdHRyaWJzW2ldLFxuICAgICAgICAgICAgICB2YWx1ZSA9IGF0dHJpYnNbaSArIDFdO1xuICAgICAgICAgIGlmICh2YWx1ZSAhPT0gbnVsbCAmJiB2YWx1ZSAhPT0gdm9pZCAwKSB7XG4gICAgICAgICAgICBvdXQucHVzaCgnICcsIGF0dHJpYk5hbWUsICc9XCInLCBlc2NhcGVBdHRyaWIodmFsdWUpLCAnXCInKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgb3V0LnB1c2goJz4nKTtcblxuICAgICAgICBpZiAoKGVmbGFnc09yaWcgJiBodG1sNC5lZmxhZ3NbJ0VNUFRZJ10pXG4gICAgICAgICAgICAmJiAhKGVmbGFnc1JlcCAmIGh0bWw0LmVmbGFnc1snRU1QVFknXSkpIHtcbiAgICAgICAgICAvLyByZXBsYWNlbWVudCBpcyBub24tZW1wdHksIHN5bnRoZXNpemUgZW5kIHRhZ1xuICAgICAgICAgIG91dC5wdXNoKCc8XFwvJywgdGFnTmFtZVJlcCwgJz4nKTtcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgICdlbmRUYWcnOiBmdW5jdGlvbih0YWdOYW1lLCBvdXQpIHtcbiAgICAgICAgaWYgKGlnbm9yaW5nKSB7XG4gICAgICAgICAgaWdub3JpbmcgPSBmYWxzZTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCFodG1sNC5FTEVNRU5UUy5oYXNPd25Qcm9wZXJ0eSh0YWdOYW1lKSkgeyByZXR1cm47IH1cbiAgICAgICAgdmFyIGVmbGFncyA9IGh0bWw0LkVMRU1FTlRTW3RhZ05hbWVdO1xuICAgICAgICBpZiAoIShlZmxhZ3MgJiAoaHRtbDQuZWZsYWdzWydFTVBUWSddIHwgaHRtbDQuZWZsYWdzWydGT0xEQUJMRSddKSkpIHtcbiAgICAgICAgICB2YXIgaW5kZXg7XG4gICAgICAgICAgaWYgKGVmbGFncyAmIGh0bWw0LmVmbGFnc1snT1BUSU9OQUxfRU5EVEFHJ10pIHtcbiAgICAgICAgICAgIGZvciAoaW5kZXggPSBzdGFjay5sZW5ndGg7IC0taW5kZXggPj0gMDspIHtcbiAgICAgICAgICAgICAgdmFyIHN0YWNrRWxPcmlnVGFnID0gc3RhY2tbaW5kZXhdLm9yaWc7XG4gICAgICAgICAgICAgIGlmIChzdGFja0VsT3JpZ1RhZyA9PT0gdGFnTmFtZSkgeyBicmVhazsgfVxuICAgICAgICAgICAgICBpZiAoIShodG1sNC5FTEVNRU5UU1tzdGFja0VsT3JpZ1RhZ10gJlxuICAgICAgICAgICAgICAgICAgICBodG1sNC5lZmxhZ3NbJ09QVElPTkFMX0VORFRBRyddKSkge1xuICAgICAgICAgICAgICAgIC8vIERvbid0IHBvcCBub24gb3B0aW9uYWwgZW5kIHRhZ3MgbG9va2luZyBmb3IgYSBtYXRjaC5cbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgZm9yIChpbmRleCA9IHN0YWNrLmxlbmd0aDsgLS1pbmRleCA+PSAwOykge1xuICAgICAgICAgICAgICBpZiAoc3RhY2tbaW5kZXhdLm9yaWcgPT09IHRhZ05hbWUpIHsgYnJlYWs7IH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGluZGV4IDwgMCkgeyByZXR1cm47IH0gIC8vIE5vdCBvcGVuZWQuXG4gICAgICAgICAgZm9yICh2YXIgaSA9IHN0YWNrLmxlbmd0aDsgLS1pID4gaW5kZXg7KSB7XG4gICAgICAgICAgICB2YXIgc3RhY2tFbFJlcFRhZyA9IHN0YWNrW2ldLnJlcDtcbiAgICAgICAgICAgIGlmICghKGh0bWw0LkVMRU1FTlRTW3N0YWNrRWxSZXBUYWddICZcbiAgICAgICAgICAgICAgICAgIGh0bWw0LmVmbGFnc1snT1BUSU9OQUxfRU5EVEFHJ10pKSB7XG4gICAgICAgICAgICAgIG91dC5wdXNoKCc8XFwvJywgc3RhY2tFbFJlcFRhZywgJz4nKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGluZGV4IDwgc3RhY2subGVuZ3RoKSB7XG4gICAgICAgICAgICB0YWdOYW1lID0gc3RhY2tbaW5kZXhdLnJlcDtcbiAgICAgICAgICB9XG4gICAgICAgICAgc3RhY2subGVuZ3RoID0gaW5kZXg7XG4gICAgICAgICAgb3V0LnB1c2goJzxcXC8nLCB0YWdOYW1lLCAnPicpO1xuICAgICAgICB9XG4gICAgICB9LFxuICAgICAgJ3BjZGF0YSc6IGVtaXQsXG4gICAgICAncmNkYXRhJzogZW1pdCxcbiAgICAgICdjZGF0YSc6IGVtaXQsXG4gICAgICAnZW5kRG9jJzogZnVuY3Rpb24ob3V0KSB7XG4gICAgICAgIGZvciAoOyBzdGFjay5sZW5ndGg7IHN0YWNrLmxlbmd0aC0tKSB7XG4gICAgICAgICAgb3V0LnB1c2goJzxcXC8nLCBzdGFja1tzdGFjay5sZW5ndGggLSAxXS5yZXAsICc+Jyk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIHZhciBBTExPV0VEX1VSSV9TQ0hFTUVTID0gL14oPzpodHRwcz98bWFpbHRvfGRhdGEpJC9pO1xuXG4gIGZ1bmN0aW9uIHNhZmVVcmkodXJpLCBlZmZlY3QsIGx0eXBlLCBoaW50cywgbmFpdmVVcmlSZXdyaXRlcikge1xuICAgIGlmICghbmFpdmVVcmlSZXdyaXRlcikgeyByZXR1cm4gbnVsbDsgfVxuICAgIHRyeSB7XG4gICAgICB2YXIgcGFyc2VkID0gVVJJLnBhcnNlKCcnICsgdXJpKTtcbiAgICAgIGlmIChwYXJzZWQpIHtcbiAgICAgICAgaWYgKCFwYXJzZWQuaGFzU2NoZW1lKCkgfHxcbiAgICAgICAgICAgIEFMTE9XRURfVVJJX1NDSEVNRVMudGVzdChwYXJzZWQuZ2V0U2NoZW1lKCkpKSB7XG4gICAgICAgICAgdmFyIHNhZmUgPSBuYWl2ZVVyaVJld3JpdGVyKHBhcnNlZCwgZWZmZWN0LCBsdHlwZSwgaGludHMpO1xuICAgICAgICAgIHJldHVybiBzYWZlID8gc2FmZS50b1N0cmluZygpIDogbnVsbDtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGxvZyhsb2dnZXIsIHRhZ05hbWUsIGF0dHJpYk5hbWUsIG9sZFZhbHVlLCBuZXdWYWx1ZSkge1xuICAgIGlmICghYXR0cmliTmFtZSkge1xuICAgICAgbG9nZ2VyKHRhZ05hbWUgKyBcIiByZW1vdmVkXCIsIHtcbiAgICAgICAgY2hhbmdlOiBcInJlbW92ZWRcIixcbiAgICAgICAgdGFnTmFtZTogdGFnTmFtZVxuICAgICAgfSk7XG4gICAgfVxuICAgIGlmIChvbGRWYWx1ZSAhPT0gbmV3VmFsdWUpIHtcbiAgICAgIHZhciBjaGFuZ2VkID0gXCJjaGFuZ2VkXCI7XG4gICAgICBpZiAob2xkVmFsdWUgJiYgIW5ld1ZhbHVlKSB7XG4gICAgICAgIGNoYW5nZWQgPSBcInJlbW92ZWRcIjtcbiAgICAgIH0gZWxzZSBpZiAoIW9sZFZhbHVlICYmIG5ld1ZhbHVlKSAge1xuICAgICAgICBjaGFuZ2VkID0gXCJhZGRlZFwiO1xuICAgICAgfVxuICAgICAgbG9nZ2VyKHRhZ05hbWUgKyBcIi5cIiArIGF0dHJpYk5hbWUgKyBcIiBcIiArIGNoYW5nZWQsIHtcbiAgICAgICAgY2hhbmdlOiBjaGFuZ2VkLFxuICAgICAgICB0YWdOYW1lOiB0YWdOYW1lLFxuICAgICAgICBhdHRyaWJOYW1lOiBhdHRyaWJOYW1lLFxuICAgICAgICBvbGRWYWx1ZTogb2xkVmFsdWUsXG4gICAgICAgIG5ld1ZhbHVlOiBuZXdWYWx1ZVxuICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgZnVuY3Rpb24gbG9va3VwQXR0cmlidXRlKG1hcCwgdGFnTmFtZSwgYXR0cmliTmFtZSkge1xuICAgIHZhciBhdHRyaWJLZXk7XG4gICAgYXR0cmliS2V5ID0gdGFnTmFtZSArICc6OicgKyBhdHRyaWJOYW1lO1xuICAgIGlmIChtYXAuaGFzT3duUHJvcGVydHkoYXR0cmliS2V5KSkge1xuICAgICAgcmV0dXJuIG1hcFthdHRyaWJLZXldO1xuICAgIH1cbiAgICBhdHRyaWJLZXkgPSAnKjo6JyArIGF0dHJpYk5hbWU7XG4gICAgaWYgKG1hcC5oYXNPd25Qcm9wZXJ0eShhdHRyaWJLZXkpKSB7XG4gICAgICByZXR1cm4gbWFwW2F0dHJpYktleV07XG4gICAgfVxuICAgIHJldHVybiB2b2lkIDA7XG4gIH1cbiAgZnVuY3Rpb24gZ2V0QXR0cmlidXRlVHlwZSh0YWdOYW1lLCBhdHRyaWJOYW1lKSB7XG4gICAgcmV0dXJuIGxvb2t1cEF0dHJpYnV0ZShodG1sNC5BVFRSSUJTLCB0YWdOYW1lLCBhdHRyaWJOYW1lKTtcbiAgfVxuICBmdW5jdGlvbiBnZXRMb2FkZXJUeXBlKHRhZ05hbWUsIGF0dHJpYk5hbWUpIHtcbiAgICByZXR1cm4gbG9va3VwQXR0cmlidXRlKGh0bWw0LkxPQURFUlRZUEVTLCB0YWdOYW1lLCBhdHRyaWJOYW1lKTtcbiAgfVxuICBmdW5jdGlvbiBnZXRVcmlFZmZlY3QodGFnTmFtZSwgYXR0cmliTmFtZSkge1xuICAgIHJldHVybiBsb29rdXBBdHRyaWJ1dGUoaHRtbDQuVVJJRUZGRUNUUywgdGFnTmFtZSwgYXR0cmliTmFtZSk7XG4gIH1cblxuICAvKipcbiAgICogU2FuaXRpemVzIGF0dHJpYnV0ZXMgb24gYW4gSFRNTCB0YWcuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSB0YWdOYW1lIEFuIEhUTUwgdGFnIG5hbWUgaW4gbG93ZXJjYXNlLlxuICAgKiBAcGFyYW0ge0FycmF5Ljw/c3RyaW5nPn0gYXR0cmlicyBBbiBhcnJheSBvZiBhbHRlcm5hdGluZyBuYW1lcyBhbmQgdmFsdWVzLlxuICAgKiBAcGFyYW0gez9mdW5jdGlvbig/c3RyaW5nKTogP3N0cmluZ30gb3B0X25haXZlVXJpUmV3cml0ZXIgQSB0cmFuc2Zvcm0gdG9cbiAgICogICAgIGFwcGx5IHRvIFVSSSBhdHRyaWJ1dGVzOyBpdCBjYW4gcmV0dXJuIGEgbmV3IHN0cmluZyB2YWx1ZSwgb3IgbnVsbCB0b1xuICAgKiAgICAgZGVsZXRlIHRoZSBhdHRyaWJ1dGUuICBJZiB1bnNwZWNpZmllZCwgVVJJIGF0dHJpYnV0ZXMgYXJlIGRlbGV0ZWQuXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb24oP3N0cmluZyk6ID9zdHJpbmd9IG9wdF9ubVRva2VuUG9saWN5IEEgdHJhbnNmb3JtIHRvIGFwcGx5XG4gICAqICAgICB0byBhdHRyaWJ1dGVzIGNvbnRhaW5pbmcgSFRNTCBuYW1lcywgZWxlbWVudCBJRHMsIGFuZCBzcGFjZS1zZXBhcmF0ZWRcbiAgICogICAgIGxpc3RzIG9mIGNsYXNzZXM7IGl0IGNhbiByZXR1cm4gYSBuZXcgc3RyaW5nIHZhbHVlLCBvciBudWxsIHRvIGRlbGV0ZVxuICAgKiAgICAgdGhlIGF0dHJpYnV0ZS4gIElmIHVuc3BlY2lmaWVkLCB0aGVzZSBhdHRyaWJ1dGVzIGFyZSBrZXB0IHVuY2hhbmdlZC5cbiAgICogQHJldHVybiB7QXJyYXkuPD9zdHJpbmc+fSBUaGUgc2FuaXRpemVkIGF0dHJpYnV0ZXMgYXMgYSBsaXN0IG9mIGFsdGVybmF0aW5nXG4gICAqICAgICBuYW1lcyBhbmQgdmFsdWVzLCB3aGVyZSBhIG51bGwgdmFsdWUgbWVhbnMgdG8gb21pdCB0aGUgYXR0cmlidXRlLlxuICAgKi9cbiAgZnVuY3Rpb24gc2FuaXRpemVBdHRyaWJzKHRhZ05hbWUsIGF0dHJpYnMsXG4gICAgb3B0X25haXZlVXJpUmV3cml0ZXIsIG9wdF9ubVRva2VuUG9saWN5LCBvcHRfbG9nZ2VyKSB7XG4gICAgLy8gVE9ETyhmZWxpeDhhKTogaXQncyBvYm5veGlvdXMgdGhhdCBkb21hZG8gZHVwbGljYXRlcyBtdWNoIG9mIHRoaXNcbiAgICAvLyBUT0RPKGZlbGl4OGEpOiBtYXliZSBjb25zaXN0ZW50bHkgZW5mb3JjZSBjb25zdHJhaW50cyBsaWtlIHRhcmdldD1cbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGF0dHJpYnMubGVuZ3RoOyBpICs9IDIpIHtcbiAgICAgIHZhciBhdHRyaWJOYW1lID0gYXR0cmlic1tpXTtcbiAgICAgIHZhciB2YWx1ZSA9IGF0dHJpYnNbaSArIDFdO1xuICAgICAgdmFyIG9sZFZhbHVlID0gdmFsdWU7XG4gICAgICB2YXIgYXR5cGUgPSBudWxsLCBhdHRyaWJLZXk7XG4gICAgICBpZiAoKGF0dHJpYktleSA9IHRhZ05hbWUgKyAnOjonICsgYXR0cmliTmFtZSxcbiAgICAgICAgICAgaHRtbDQuQVRUUklCUy5oYXNPd25Qcm9wZXJ0eShhdHRyaWJLZXkpKSB8fFxuICAgICAgICAgIChhdHRyaWJLZXkgPSAnKjo6JyArIGF0dHJpYk5hbWUsXG4gICAgICAgICAgIGh0bWw0LkFUVFJJQlMuaGFzT3duUHJvcGVydHkoYXR0cmliS2V5KSkpIHtcbiAgICAgICAgYXR5cGUgPSBodG1sNC5BVFRSSUJTW2F0dHJpYktleV07XG4gICAgICB9XG4gICAgICBpZiAoYXR5cGUgIT09IG51bGwpIHtcbiAgICAgICAgc3dpdGNoIChhdHlwZSkge1xuICAgICAgICAgIGNhc2UgaHRtbDQuYXR5cGVbJ05PTkUnXTogYnJlYWs7XG4gICAgICAgICAgY2FzZSBodG1sNC5hdHlwZVsnU0NSSVBUJ106XG4gICAgICAgICAgICB2YWx1ZSA9IG51bGw7XG4gICAgICAgICAgICBpZiAob3B0X2xvZ2dlcikge1xuICAgICAgICAgICAgICBsb2cob3B0X2xvZ2dlciwgdGFnTmFtZSwgYXR0cmliTmFtZSwgb2xkVmFsdWUsIHZhbHVlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgaHRtbDQuYXR5cGVbJ1NUWUxFJ106XG4gICAgICAgICAgICBpZiAoJ3VuZGVmaW5lZCcgPT09IHR5cGVvZiBwYXJzZUNzc0RlY2xhcmF0aW9ucykge1xuICAgICAgICAgICAgICB2YWx1ZSA9IG51bGw7XG4gICAgICAgICAgICAgIGlmIChvcHRfbG9nZ2VyKSB7XG4gICAgICAgICAgICAgICAgbG9nKG9wdF9sb2dnZXIsIHRhZ05hbWUsIGF0dHJpYk5hbWUsIG9sZFZhbHVlLCB2YWx1ZSk7XG5cdCAgICAgIH1cbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB2YXIgc2FuaXRpemVkRGVjbGFyYXRpb25zID0gW107XG4gICAgICAgICAgICBwYXJzZUNzc0RlY2xhcmF0aW9ucyhcbiAgICAgICAgICAgICAgICB2YWx1ZSxcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICBkZWNsYXJhdGlvbjogZnVuY3Rpb24gKHByb3BlcnR5LCB0b2tlbnMpIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIG5vcm1Qcm9wID0gcHJvcGVydHkudG9Mb3dlckNhc2UoKTtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHNjaGVtYSA9IGNzc1NjaGVtYVtub3JtUHJvcF07XG4gICAgICAgICAgICAgICAgICAgIGlmICghc2NoZW1hKSB7XG4gICAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIHNhbml0aXplQ3NzUHJvcGVydHkoXG4gICAgICAgICAgICAgICAgICAgICAgICBub3JtUHJvcCwgc2NoZW1hLCB0b2tlbnMsXG4gICAgICAgICAgICAgICAgICAgICAgICBvcHRfbmFpdmVVcmlSZXdyaXRlclxuICAgICAgICAgICAgICAgICAgICAgICAgPyBmdW5jdGlvbiAodXJsKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHNhZmVVcmkoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVybCwgaHRtbDQudWVmZmVjdHMuU0FNRV9ET0NVTUVOVCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaHRtbDQubHR5cGVzLlNBTkRCT1hFRCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwiVFlQRVwiOiBcIkNTU1wiLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwiQ1NTX1BST1BcIjogbm9ybVByb3BcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgb3B0X25haXZlVXJpUmV3cml0ZXIpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICA6IG51bGwpO1xuICAgICAgICAgICAgICAgICAgICBzYW5pdGl6ZWREZWNsYXJhdGlvbnMucHVzaChwcm9wZXJ0eSArICc6ICcgKyB0b2tlbnMuam9pbignICcpKTtcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHZhbHVlID0gc2FuaXRpemVkRGVjbGFyYXRpb25zLmxlbmd0aCA+IDAgP1xuICAgICAgICAgICAgICBzYW5pdGl6ZWREZWNsYXJhdGlvbnMuam9pbignIDsgJykgOiBudWxsO1xuICAgICAgICAgICAgaWYgKG9wdF9sb2dnZXIpIHtcbiAgICAgICAgICAgICAgbG9nKG9wdF9sb2dnZXIsIHRhZ05hbWUsIGF0dHJpYk5hbWUsIG9sZFZhbHVlLCB2YWx1ZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBjYXNlIGh0bWw0LmF0eXBlWydJRCddOlxuICAgICAgICAgIGNhc2UgaHRtbDQuYXR5cGVbJ0lEUkVGJ106XG4gICAgICAgICAgY2FzZSBodG1sNC5hdHlwZVsnSURSRUZTJ106XG4gICAgICAgICAgY2FzZSBodG1sNC5hdHlwZVsnR0xPQkFMX05BTUUnXTpcbiAgICAgICAgICBjYXNlIGh0bWw0LmF0eXBlWydMT0NBTF9OQU1FJ106XG4gICAgICAgICAgY2FzZSBodG1sNC5hdHlwZVsnQ0xBU1NFUyddOlxuICAgICAgICAgICAgdmFsdWUgPSBvcHRfbm1Ub2tlblBvbGljeSA/IG9wdF9ubVRva2VuUG9saWN5KHZhbHVlKSA6IHZhbHVlO1xuICAgICAgICAgICAgaWYgKG9wdF9sb2dnZXIpIHtcbiAgICAgICAgICAgICAgbG9nKG9wdF9sb2dnZXIsIHRhZ05hbWUsIGF0dHJpYk5hbWUsIG9sZFZhbHVlLCB2YWx1ZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBjYXNlIGh0bWw0LmF0eXBlWydVUkknXTpcbiAgICAgICAgICAgIHZhbHVlID0gc2FmZVVyaSh2YWx1ZSxcbiAgICAgICAgICAgICAgZ2V0VXJpRWZmZWN0KHRhZ05hbWUsIGF0dHJpYk5hbWUpLFxuICAgICAgICAgICAgICBnZXRMb2FkZXJUeXBlKHRhZ05hbWUsIGF0dHJpYk5hbWUpLFxuICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgXCJUWVBFXCI6IFwiTUFSS1VQXCIsXG4gICAgICAgICAgICAgICAgXCJYTUxfQVRUUlwiOiBhdHRyaWJOYW1lLFxuICAgICAgICAgICAgICAgIFwiWE1MX1RBR1wiOiB0YWdOYW1lXG4gICAgICAgICAgICAgIH0sIG9wdF9uYWl2ZVVyaVJld3JpdGVyKTtcbiAgICAgICAgICAgICAgaWYgKG9wdF9sb2dnZXIpIHtcbiAgICAgICAgICAgICAgbG9nKG9wdF9sb2dnZXIsIHRhZ05hbWUsIGF0dHJpYk5hbWUsIG9sZFZhbHVlLCB2YWx1ZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBjYXNlIGh0bWw0LmF0eXBlWydVUklfRlJBR01FTlQnXTpcbiAgICAgICAgICAgIGlmICh2YWx1ZSAmJiAnIycgPT09IHZhbHVlLmNoYXJBdCgwKSkge1xuICAgICAgICAgICAgICB2YWx1ZSA9IHZhbHVlLnN1YnN0cmluZygxKTsgIC8vIHJlbW92ZSB0aGUgbGVhZGluZyAnIydcbiAgICAgICAgICAgICAgdmFsdWUgPSBvcHRfbm1Ub2tlblBvbGljeSA/IG9wdF9ubVRva2VuUG9saWN5KHZhbHVlKSA6IHZhbHVlO1xuICAgICAgICAgICAgICBpZiAodmFsdWUgIT09IG51bGwgJiYgdmFsdWUgIT09IHZvaWQgMCkge1xuICAgICAgICAgICAgICAgIHZhbHVlID0gJyMnICsgdmFsdWU7ICAvLyByZXN0b3JlIHRoZSBsZWFkaW5nICcjJ1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICB2YWx1ZSA9IG51bGw7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAob3B0X2xvZ2dlcikge1xuICAgICAgICAgICAgICBsb2cob3B0X2xvZ2dlciwgdGFnTmFtZSwgYXR0cmliTmFtZSwgb2xkVmFsdWUsIHZhbHVlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICB2YWx1ZSA9IG51bGw7XG4gICAgICAgICAgICBpZiAob3B0X2xvZ2dlcikge1xuICAgICAgICAgICAgICBsb2cob3B0X2xvZ2dlciwgdGFnTmFtZSwgYXR0cmliTmFtZSwgb2xkVmFsdWUsIHZhbHVlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB2YWx1ZSA9IG51bGw7XG4gICAgICAgIGlmIChvcHRfbG9nZ2VyKSB7XG4gICAgICAgICAgbG9nKG9wdF9sb2dnZXIsIHRhZ05hbWUsIGF0dHJpYk5hbWUsIG9sZFZhbHVlLCB2YWx1ZSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGF0dHJpYnNbaSArIDFdID0gdmFsdWU7XG4gICAgfVxuICAgIHJldHVybiBhdHRyaWJzO1xuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYSB0YWcgcG9saWN5IHRoYXQgb21pdHMgYWxsIHRhZ3MgbWFya2VkIFVOU0FGRSBpbiBodG1sNC1kZWZzLmpzXG4gICAqIGFuZCBhcHBsaWVzIHRoZSBkZWZhdWx0IGF0dHJpYnV0ZSBzYW5pdGl6ZXIgd2l0aCB0aGUgc3VwcGxpZWQgcG9saWN5IGZvclxuICAgKiBVUkkgYXR0cmlidXRlcyBhbmQgTk1UT0tFTiBhdHRyaWJ1dGVzLlxuICAgKiBAcGFyYW0gez9mdW5jdGlvbig/c3RyaW5nKTogP3N0cmluZ30gb3B0X25haXZlVXJpUmV3cml0ZXIgQSB0cmFuc2Zvcm0gdG9cbiAgICogICAgIGFwcGx5IHRvIFVSSSBhdHRyaWJ1dGVzLiAgSWYgbm90IGdpdmVuLCBVUkkgYXR0cmlidXRlcyBhcmUgZGVsZXRlZC5cbiAgICogQHBhcmFtIHtmdW5jdGlvbig/c3RyaW5nKTogP3N0cmluZ30gb3B0X25tVG9rZW5Qb2xpY3kgQSB0cmFuc2Zvcm0gdG8gYXBwbHlcbiAgICogICAgIHRvIGF0dHJpYnV0ZXMgY29udGFpbmluZyBIVE1MIG5hbWVzLCBlbGVtZW50IElEcywgYW5kIHNwYWNlLXNlcGFyYXRlZFxuICAgKiAgICAgbGlzdHMgb2YgY2xhc3Nlcy4gIElmIG5vdCBnaXZlbiwgc3VjaCBhdHRyaWJ1dGVzIGFyZSBsZWZ0IHVuY2hhbmdlZC5cbiAgICogQHJldHVybiB7ZnVuY3Rpb24oc3RyaW5nLCBBcnJheS48P3N0cmluZz4pfSBBIHRhZ1BvbGljeSBzdWl0YWJsZSBmb3JcbiAgICogICAgIHBhc3NpbmcgdG8gaHRtbC5zYW5pdGl6ZS5cbiAgICovXG4gIGZ1bmN0aW9uIG1ha2VUYWdQb2xpY3koXG4gICAgb3B0X25haXZlVXJpUmV3cml0ZXIsIG9wdF9ubVRva2VuUG9saWN5LCBvcHRfbG9nZ2VyKSB7XG4gICAgcmV0dXJuIGZ1bmN0aW9uKHRhZ05hbWUsIGF0dHJpYnMpIHtcbiAgICAgIGlmICghKGh0bWw0LkVMRU1FTlRTW3RhZ05hbWVdICYgaHRtbDQuZWZsYWdzWydVTlNBRkUnXSkpIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAnYXR0cmlicyc6IHNhbml0aXplQXR0cmlicyh0YWdOYW1lLCBhdHRyaWJzLFxuICAgICAgICAgICAgb3B0X25haXZlVXJpUmV3cml0ZXIsIG9wdF9ubVRva2VuUG9saWN5LCBvcHRfbG9nZ2VyKVxuICAgICAgICB9O1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaWYgKG9wdF9sb2dnZXIpIHtcbiAgICAgICAgICBsb2cob3B0X2xvZ2dlciwgdGFnTmFtZSwgdW5kZWZpbmVkLCB1bmRlZmluZWQsIHVuZGVmaW5lZCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIFNhbml0aXplcyBIVE1MIHRhZ3MgYW5kIGF0dHJpYnV0ZXMgYWNjb3JkaW5nIHRvIGEgZ2l2ZW4gcG9saWN5LlxuICAgKiBAcGFyYW0ge3N0cmluZ30gaW5wdXRIdG1sIFRoZSBIVE1MIHRvIHNhbml0aXplLlxuICAgKiBAcGFyYW0ge2Z1bmN0aW9uKHN0cmluZywgQXJyYXkuPD9zdHJpbmc+KX0gdGFnUG9saWN5IEEgZnVuY3Rpb24gdGhhdFxuICAgKiAgICAgZGVjaWRlcyB3aGljaCB0YWdzIHRvIGFjY2VwdCBhbmQgc2FuaXRpemVzIHRoZWlyIGF0dHJpYnV0ZXMgKHNlZVxuICAgKiAgICAgbWFrZUh0bWxTYW5pdGl6ZXIgYWJvdmUgZm9yIGRldGFpbHMpLlxuICAgKiBAcmV0dXJuIHtzdHJpbmd9IFRoZSBzYW5pdGl6ZWQgSFRNTC5cbiAgICovXG4gIGZ1bmN0aW9uIHNhbml0aXplV2l0aFBvbGljeShpbnB1dEh0bWwsIHRhZ1BvbGljeSkge1xuICAgIHZhciBvdXRwdXRBcnJheSA9IFtdO1xuICAgIG1ha2VIdG1sU2FuaXRpemVyKHRhZ1BvbGljeSkoaW5wdXRIdG1sLCBvdXRwdXRBcnJheSk7XG4gICAgcmV0dXJuIG91dHB1dEFycmF5LmpvaW4oJycpO1xuICB9XG5cbiAgLyoqXG4gICAqIFN0cmlwcyB1bnNhZmUgdGFncyBhbmQgYXR0cmlidXRlcyBmcm9tIEhUTUwuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dEh0bWwgVGhlIEhUTUwgdG8gc2FuaXRpemUuXG4gICAqIEBwYXJhbSB7P2Z1bmN0aW9uKD9zdHJpbmcpOiA/c3RyaW5nfSBvcHRfbmFpdmVVcmlSZXdyaXRlciBBIHRyYW5zZm9ybSB0b1xuICAgKiAgICAgYXBwbHkgdG8gVVJJIGF0dHJpYnV0ZXMuICBJZiBub3QgZ2l2ZW4sIFVSSSBhdHRyaWJ1dGVzIGFyZSBkZWxldGVkLlxuICAgKiBAcGFyYW0ge2Z1bmN0aW9uKD9zdHJpbmcpOiA/c3RyaW5nfSBvcHRfbm1Ub2tlblBvbGljeSBBIHRyYW5zZm9ybSB0byBhcHBseVxuICAgKiAgICAgdG8gYXR0cmlidXRlcyBjb250YWluaW5nIEhUTUwgbmFtZXMsIGVsZW1lbnQgSURzLCBhbmQgc3BhY2Utc2VwYXJhdGVkXG4gICAqICAgICBsaXN0cyBvZiBjbGFzc2VzLiAgSWYgbm90IGdpdmVuLCBzdWNoIGF0dHJpYnV0ZXMgYXJlIGxlZnQgdW5jaGFuZ2VkLlxuICAgKi9cbiAgZnVuY3Rpb24gc2FuaXRpemUoaW5wdXRIdG1sLFxuICAgIG9wdF9uYWl2ZVVyaVJld3JpdGVyLCBvcHRfbm1Ub2tlblBvbGljeSwgb3B0X2xvZ2dlcikge1xuICAgIHZhciB0YWdQb2xpY3kgPSBtYWtlVGFnUG9saWN5KFxuICAgICAgb3B0X25haXZlVXJpUmV3cml0ZXIsIG9wdF9ubVRva2VuUG9saWN5LCBvcHRfbG9nZ2VyKTtcbiAgICByZXR1cm4gc2FuaXRpemVXaXRoUG9saWN5KGlucHV0SHRtbCwgdGFnUG9saWN5KTtcbiAgfVxuXG4gIC8vIEV4cG9ydCBib3RoIHF1b3RlZCBhbmQgdW5xdW90ZWQgbmFtZXMgZm9yIENsb3N1cmUgbGlua2FnZS5cbiAgdmFyIGh0bWwgPSB7fTtcbiAgaHRtbC5lc2NhcGVBdHRyaWIgPSBodG1sWydlc2NhcGVBdHRyaWInXSA9IGVzY2FwZUF0dHJpYjtcbiAgaHRtbC5tYWtlSHRtbFNhbml0aXplciA9IGh0bWxbJ21ha2VIdG1sU2FuaXRpemVyJ10gPSBtYWtlSHRtbFNhbml0aXplcjtcbiAgaHRtbC5tYWtlU2F4UGFyc2VyID0gaHRtbFsnbWFrZVNheFBhcnNlciddID0gbWFrZVNheFBhcnNlcjtcbiAgaHRtbC5tYWtlVGFnUG9saWN5ID0gaHRtbFsnbWFrZVRhZ1BvbGljeSddID0gbWFrZVRhZ1BvbGljeTtcbiAgaHRtbC5ub3JtYWxpemVSQ0RhdGEgPSBodG1sWydub3JtYWxpemVSQ0RhdGEnXSA9IG5vcm1hbGl6ZVJDRGF0YTtcbiAgaHRtbC5zYW5pdGl6ZSA9IGh0bWxbJ3Nhbml0aXplJ10gPSBzYW5pdGl6ZTtcbiAgaHRtbC5zYW5pdGl6ZUF0dHJpYnMgPSBodG1sWydzYW5pdGl6ZUF0dHJpYnMnXSA9IHNhbml0aXplQXR0cmlicztcbiAgaHRtbC5zYW5pdGl6ZVdpdGhQb2xpY3kgPSBodG1sWydzYW5pdGl6ZVdpdGhQb2xpY3knXSA9IHNhbml0aXplV2l0aFBvbGljeTtcbiAgaHRtbC51bmVzY2FwZUVudGl0aWVzID0gaHRtbFsndW5lc2NhcGVFbnRpdGllcyddID0gdW5lc2NhcGVFbnRpdGllcztcbiAgcmV0dXJuIGh0bWw7XG59KShodG1sNCk7XG5cbnZhciBodG1sX3Nhbml0aXplID0gaHRtbFsnc2FuaXRpemUnXTtcblxuLy8gTG9vc2VuIHJlc3RyaWN0aW9ucyBvZiBDYWphJ3Ncbi8vIGh0bWwtc2FuaXRpemVyIHRvIGFsbG93IGZvciBzdHlsaW5nXG5odG1sNC5BVFRSSUJTWycqOjpzdHlsZSddID0gMDtcbmh0bWw0LkVMRU1FTlRTWydzdHlsZSddID0gMDtcbmh0bWw0LkFUVFJJQlNbJ2E6OnRhcmdldCddID0gMDtcbmh0bWw0LkVMRU1FTlRTWyd2aWRlbyddID0gMDtcbmh0bWw0LkFUVFJJQlNbJ3ZpZGVvOjpzcmMnXSA9IDA7XG5odG1sNC5BVFRSSUJTWyd2aWRlbzo6cG9zdGVyJ10gPSAwO1xuaHRtbDQuQVRUUklCU1sndmlkZW86OmNvbnRyb2xzJ10gPSAwO1xuaHRtbDQuRUxFTUVOVFNbJ2F1ZGlvJ10gPSAwO1xuaHRtbDQuQVRUUklCU1snYXVkaW86OnNyYyddID0gMDtcbmh0bWw0LkFUVFJJQlNbJ3ZpZGVvOjphdXRvcGxheSddID0gMDtcbmh0bWw0LkFUVFJJQlNbJ3ZpZGVvOjpjb250cm9scyddID0gMDtcblxuaWYgKHR5cGVvZiBtb2R1bGUgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgbW9kdWxlLmV4cG9ydHMgPSBodG1sX3Nhbml0aXplO1xufVxuIiwibW9kdWxlLmV4cG9ydHM9e1xuICBcImF1dGhvclwiOiBcIk1hcGJveFwiLFxuICBcIm5hbWVcIjogXCJtYXBib3guanNcIixcbiAgXCJkZXNjcmlwdGlvblwiOiBcIm1hcGJveCBqYXZhc2NyaXB0IGFwaVwiLFxuICBcInZlcnNpb25cIjogXCIzLjEuMVwiLFxuICBcImhvbWVwYWdlXCI6IFwiaHR0cDovL21hcGJveC5jb20vXCIsXG4gIFwicmVwb3NpdG9yeVwiOiB7XG4gICAgXCJ0eXBlXCI6IFwiZ2l0XCIsXG4gICAgXCJ1cmxcIjogXCJnaXQ6Ly9naXRodWIuY29tL21hcGJveC9tYXBib3guanMuZ2l0XCJcbiAgfSxcbiAgXCJtYWluXCI6IFwic3JjL2luZGV4LmpzXCIsXG4gIFwiZGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcImNvcnNsaXRlXCI6IFwiMC4wLjZcIixcbiAgICBcImlzYXJyYXlcIjogXCIwLjAuMVwiLFxuICAgIFwibGVhZmxldFwiOiBcIjEuMC4yXCIsXG4gICAgXCJtdXN0YWNoZVwiOiBcIjIuMi4xXCIsXG4gICAgXCJzYW5pdGl6ZS1jYWphXCI6IFwiMC4xLjRcIlxuICB9LFxuICBcInNjcmlwdHNcIjoge1xuICAgIFwidGVzdFwiOiBcImVzbGludCAtLW5vLWVzbGludHJjIC1jIC5lc2xpbnRyYyBzcmMgJiYgcGhhbnRvbWpzIG5vZGVfbW9kdWxlcy9tb2NoYS1waGFudG9tanMtY29yZS9tb2NoYS1waGFudG9tanMtY29yZS5qcyB0ZXN0L2luZGV4Lmh0bWxcIlxuICB9LFxuICBcImxpY2Vuc2VcIjogXCJCU0QtMy1DbGF1c2VcIixcbiAgXCJkZXZEZXBlbmRlbmNpZXNcIjoge1xuICAgIFwiYnJvd3NlcmlmeVwiOiBcIl4xMy4wLjBcIixcbiAgICBcImNsZWFuLWNzc1wiOiBcIn4yLjAuN1wiLFxuICAgIFwiY3otY29udmVudGlvbmFsLWNoYW5nZWxvZ1wiOiBcIjEuMi4wXCIsXG4gICAgXCJlc2xpbnRcIjogXCJeMC4yMy4wXCIsXG4gICAgXCJleHBlY3QuanNcIjogXCIwLjMuMVwiLFxuICAgIFwiaGFwcGVuXCI6IFwiMC4xLjNcIixcbiAgICBcImxlYWZsZXQtZnVsbHNjcmVlblwiOiBcIjAuMC40XCIsXG4gICAgXCJsZWFmbGV0LWhhc2hcIjogXCIwLjIuMVwiLFxuICAgIFwibWFya2VkXCI6IFwifjAuMy4wXCIsXG4gICAgXCJtaW5pZnlpZnlcIjogXCJeNi4xLjBcIixcbiAgICBcIm1pbmltaXN0XCI6IFwiMC4wLjVcIixcbiAgICBcIm1vY2hhXCI6IFwiMi40LjVcIixcbiAgICBcIm1vY2hhLXBoYW50b21qcy1jb3JlXCI6IFwiMi4wLjFcIixcbiAgICBcInBoYW50b21qcy1wcmVidWlsdFwiOiBcIjIuMS4xMlwiLFxuICAgIFwic2lub25cIjogXCIxLjEwLjJcIlxuICB9LFxuICBcIm9wdGlvbmFsRGVwZW5kZW5jaWVzXCI6IHt9LFxuICBcImVuZ2luZXNcIjoge1xuICAgIFwibm9kZVwiOiBcIipcIlxuICB9LFxuICBcImNvbmZpZ1wiOiB7XG4gICAgXCJjb21taXRpemVuXCI6IHtcbiAgICAgIFwicGF0aFwiOiBcIi4vbm9kZV9tb2R1bGVzL2N6LWNvbnZlbnRpb25hbC1jaGFuZ2Vsb2dcIlxuICAgIH1cbiAgfVxufVxuIiwiJ3VzZSBzdHJpY3QnO1xuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgICBIVFRQX1VSTDogJ2h0dHA6Ly9hLnRpbGVzLm1hcGJveC5jb20vdjQnLFxuICAgIEhUVFBTX1VSTDogJ2h0dHBzOi8vYS50aWxlcy5tYXBib3guY29tL3Y0JyxcbiAgICBGT1JDRV9IVFRQUzogZmFsc2UsXG4gICAgUkVRVUlSRV9BQ0NFU1NfVE9LRU46IHRydWVcbn07XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciB1dGlsID0gcmVxdWlyZSgnLi91dGlsJyksXG4gICAgZm9ybWF0X3VybCA9IHJlcXVpcmUoJy4vZm9ybWF0X3VybCcpLFxuICAgIHJlcXVlc3QgPSByZXF1aXJlKCcuL3JlcXVlc3QnKSxcbiAgICBtYXJrZXIgPSByZXF1aXJlKCcuL21hcmtlcicpLFxuICAgIHNpbXBsZXN0eWxlID0gcmVxdWlyZSgnLi9zaW1wbGVzdHlsZScpO1xuXG4vLyAjIGZlYXR1cmVMYXllclxuLy9cbi8vIEEgbGF5ZXIgb2YgZmVhdHVyZXMsIGxvYWRlZCBmcm9tIE1hcGJveCBvciBlbHNlLiBBZGRzIHRoZSBhYmlsaXR5XG4vLyB0byByZXNldCBmZWF0dXJlcywgZmlsdGVyIHRoZW0sIGFuZCBsb2FkIHRoZW0gZnJvbSBhIEdlb0pTT04gVVJMLlxudmFyIEZlYXR1cmVMYXllciA9IEwuRmVhdHVyZUdyb3VwLmV4dGVuZCh7XG4gICAgb3B0aW9uczoge1xuICAgICAgICBmaWx0ZXI6IGZ1bmN0aW9uKCkgeyByZXR1cm4gdHJ1ZTsgfSxcbiAgICAgICAgc2FuaXRpemVyOiByZXF1aXJlKCdzYW5pdGl6ZS1jYWphJyksXG4gICAgICAgIHN0eWxlOiBzaW1wbGVzdHlsZS5zdHlsZSxcbiAgICAgICAgcG9wdXBPcHRpb25zOiB7IGNsb3NlQnV0dG9uOiBmYWxzZSB9XG4gICAgfSxcblxuICAgIGluaXRpYWxpemU6IGZ1bmN0aW9uKF8sIG9wdGlvbnMpIHtcbiAgICAgICAgTC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xuXG4gICAgICAgIHRoaXMuX2xheWVycyA9IHt9O1xuXG4gICAgICAgIGlmICh0eXBlb2YgXyA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgIHV0aWwuaWRVcmwoXywgdGhpcyk7XG4gICAgICAgIC8vIGphdmFzY3JpcHQgb2JqZWN0IG9mIFRpbGVKU09OIGRhdGFcbiAgICAgICAgfSBlbHNlIGlmIChfICYmIHR5cGVvZiBfID09PSAnb2JqZWN0Jykge1xuICAgICAgICAgICAgdGhpcy5zZXRHZW9KU09OKF8pO1xuICAgICAgICB9XG4gICAgfSxcblxuICAgIHNldEdlb0pTT046IGZ1bmN0aW9uKF8pIHtcbiAgICAgICAgdGhpcy5fZ2VvanNvbiA9IF87XG4gICAgICAgIHRoaXMuY2xlYXJMYXllcnMoKTtcbiAgICAgICAgdGhpcy5faW5pdGlhbGl6ZShfKTtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcblxuICAgIGdldEdlb0pTT046IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5fZ2VvanNvbjtcbiAgICB9LFxuXG4gICAgbG9hZFVSTDogZnVuY3Rpb24odXJsKSB7XG4gICAgICAgIGlmICh0aGlzLl9yZXF1ZXN0ICYmICdhYm9ydCcgaW4gdGhpcy5fcmVxdWVzdCkgdGhpcy5fcmVxdWVzdC5hYm9ydCgpO1xuICAgICAgICB0aGlzLl9yZXF1ZXN0ID0gcmVxdWVzdCh1cmwsIEwuYmluZChmdW5jdGlvbihlcnIsIGpzb24pIHtcbiAgICAgICAgICAgIHRoaXMuX3JlcXVlc3QgPSBudWxsO1xuICAgICAgICAgICAgaWYgKGVyciAmJiBlcnIudHlwZSAhPT0gJ2Fib3J0Jykge1xuICAgICAgICAgICAgICAgIHV0aWwubG9nKCdjb3VsZCBub3QgbG9hZCBmZWF0dXJlcyBhdCAnICsgdXJsKTtcbiAgICAgICAgICAgICAgICB0aGlzLmZpcmUoJ2Vycm9yJywge2Vycm9yOiBlcnJ9KTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoanNvbikge1xuICAgICAgICAgICAgICAgIHRoaXMuc2V0R2VvSlNPTihqc29uKTtcbiAgICAgICAgICAgICAgICB0aGlzLmZpcmUoJ3JlYWR5Jyk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sIHRoaXMpKTtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcblxuICAgIGxvYWRJRDogZnVuY3Rpb24oaWQpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubG9hZFVSTChmb3JtYXRfdXJsKCcvdjQvJyArIGlkICsgJy9mZWF0dXJlcy5qc29uJywgdGhpcy5vcHRpb25zLmFjY2Vzc1Rva2VuKSk7XG4gICAgfSxcblxuICAgIHNldEZpbHRlcjogZnVuY3Rpb24oXykge1xuICAgICAgICB0aGlzLm9wdGlvbnMuZmlsdGVyID0gXztcbiAgICAgICAgaWYgKHRoaXMuX2dlb2pzb24pIHtcbiAgICAgICAgICAgIHRoaXMuY2xlYXJMYXllcnMoKTtcbiAgICAgICAgICAgIHRoaXMuX2luaXRpYWxpemUodGhpcy5fZ2VvanNvbik7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcblxuICAgIGdldEZpbHRlcjogZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiB0aGlzLm9wdGlvbnMuZmlsdGVyO1xuICAgIH0sXG5cbiAgICBfaW5pdGlhbGl6ZTogZnVuY3Rpb24oanNvbikge1xuICAgICAgICB2YXIgZmVhdHVyZXMgPSBMLlV0aWwuaXNBcnJheShqc29uKSA/IGpzb24gOiBqc29uLmZlYXR1cmVzLFxuICAgICAgICAgICAgaSwgbGVuO1xuXG4gICAgICAgIGlmIChmZWF0dXJlcykge1xuICAgICAgICAgICAgZm9yIChpID0gMCwgbGVuID0gZmVhdHVyZXMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcbiAgICAgICAgICAgICAgICAvLyBPbmx5IGFkZCB0aGlzIGlmIGdlb21ldHJ5IG9yIGdlb21ldHJpZXMgYXJlIHNldCBhbmQgbm90IG51bGxcbiAgICAgICAgICAgICAgICBpZiAoZmVhdHVyZXNbaV0uZ2VvbWV0cmllcyB8fCBmZWF0dXJlc1tpXS5nZW9tZXRyeSB8fCBmZWF0dXJlc1tpXS5mZWF0dXJlcykge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLl9pbml0aWFsaXplKGZlYXR1cmVzW2ldKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAodGhpcy5vcHRpb25zLmZpbHRlcihqc29uKSkge1xuXG4gICAgICAgICAgICB2YXIgb3B0cyA9IHthY2Nlc3NUb2tlbjogdGhpcy5vcHRpb25zLmFjY2Vzc1Rva2VufSxcbiAgICAgICAgICAgICAgICBwb2ludFRvTGF5ZXIgPSB0aGlzLm9wdGlvbnMucG9pbnRUb0xheWVyIHx8IGZ1bmN0aW9uKGZlYXR1cmUsIGxhdGxvbikge1xuICAgICAgICAgICAgICAgICAgcmV0dXJuIG1hcmtlci5zdHlsZShmZWF0dXJlLCBsYXRsb24sIG9wdHMpO1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgbGF5ZXIgPSBMLkdlb0pTT04uZ2VvbWV0cnlUb0xheWVyKGpzb24sIHtcbiAgICAgICAgICAgICAgICAgICAgcG9pbnRUb0xheWVyOiBwb2ludFRvTGF5ZXJcbiAgICAgICAgICAgICAgICB9KSxcbiAgICAgICAgICAgICAgICBwb3B1cEh0bWwgPSBtYXJrZXIuY3JlYXRlUG9wdXAoanNvbiwgdGhpcy5vcHRpb25zLnNhbml0aXplciksXG4gICAgICAgICAgICAgICAgc3R5bGUgPSB0aGlzLm9wdGlvbnMuc3R5bGUsXG4gICAgICAgICAgICAgICAgZGVmYXVsdFN0eWxlID0gc3R5bGUgPT09IHNpbXBsZXN0eWxlLnN0eWxlO1xuXG4gICAgICAgICAgICBpZiAoc3R5bGUgJiYgJ3NldFN0eWxlJyBpbiBsYXllciAmJlxuICAgICAgICAgICAgICAgIC8vIGlmIHRoZSBzdHlsZSBtZXRob2QgaXMgdGhlIHNpbXBsZXN0eWxlIGRlZmF1bHQsIHRoZW5cbiAgICAgICAgICAgICAgICAvLyBuZXZlciBzdHlsZSBMLkNpcmNsZSBvciBMLkNpcmNsZU1hcmtlciBiZWNhdXNlXG4gICAgICAgICAgICAgICAgLy8gc2ltcGxlc3R5bGUgaGFzIG5vIHJ1bGVzIG92ZXIgdGhlbSwgb25seSBvdmVyIGdlb21ldHJ5XG4gICAgICAgICAgICAgICAgLy8gcHJpbWl0aXZlcyBkaXJlY3RseSBmcm9tIEdlb0pTT05cbiAgICAgICAgICAgICAgICAoIShkZWZhdWx0U3R5bGUgJiYgKGxheWVyIGluc3RhbmNlb2YgTC5DaXJjbGUgfHxcbiAgICAgICAgICAgICAgICAgIGxheWVyIGluc3RhbmNlb2YgTC5DaXJjbGVNYXJrZXIpKSkpIHtcbiAgICAgICAgICAgICAgICBpZiAodHlwZW9mIHN0eWxlID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICAgICAgICAgIHN0eWxlID0gc3R5bGUoanNvbik7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGxheWVyLnNldFN0eWxlKHN0eWxlKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgbGF5ZXIuZmVhdHVyZSA9IGpzb247XG5cbiAgICAgICAgICAgIGlmIChwb3B1cEh0bWwpIHtcbiAgICAgICAgICAgICAgICBsYXllci5iaW5kUG9wdXAocG9wdXBIdG1sLCB0aGlzLm9wdGlvbnMucG9wdXBPcHRpb25zKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdGhpcy5hZGRMYXllcihsYXllcik7XG4gICAgICAgIH1cbiAgICB9XG59KTtcblxubW9kdWxlLmV4cG9ydHMuRmVhdHVyZUxheWVyID0gRmVhdHVyZUxheWVyO1xuXG5tb2R1bGUuZXhwb3J0cy5mZWF0dXJlTGF5ZXIgPSBmdW5jdGlvbihfLCBvcHRpb25zKSB7XG4gICAgcmV0dXJuIG5ldyBGZWF0dXJlTGF5ZXIoXywgb3B0aW9ucyk7XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgRmVlZGJhY2sgPSBMLkNsYXNzLmV4dGVuZCh7XG4gICAgaW5jbHVkZXM6IEwuTWl4aW4uRXZlbnRzLFxuICAgIGRhdGE6IHt9LFxuICAgIHJlY29yZDogZnVuY3Rpb24oZGF0YSkge1xuICAgICAgICBMLmV4dGVuZCh0aGlzLmRhdGEsIGRhdGEpO1xuICAgICAgICB0aGlzLmZpcmUoJ2NoYW5nZScpO1xuICAgIH1cbn0pO1xuXG5tb2R1bGUuZXhwb3J0cyA9IG5ldyBGZWVkYmFjaygpO1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgY29uZmlnID0gcmVxdWlyZSgnLi9jb25maWcnKSxcbiAgICB2ZXJzaW9uID0gcmVxdWlyZSgnLi4vcGFja2FnZS5qc29uJykudmVyc2lvbjtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbihwYXRoLCBhY2Nlc3NUb2tlbikge1xuICAgIGFjY2Vzc1Rva2VuID0gYWNjZXNzVG9rZW4gfHwgTC5tYXBib3guYWNjZXNzVG9rZW47XG5cbiAgICBpZiAoIWFjY2Vzc1Rva2VuICYmIGNvbmZpZy5SRVFVSVJFX0FDQ0VTU19UT0tFTikge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0FuIEFQSSBhY2Nlc3MgdG9rZW4gaXMgcmVxdWlyZWQgdG8gdXNlIE1hcGJveC5qcy4gJyArXG4gICAgICAgICAgICAnU2VlIGh0dHBzOi8vd3d3Lm1hcGJveC5jb20vbWFwYm94LmpzL2FwaS92JyArIHZlcnNpb24gKyAnL2FwaS1hY2Nlc3MtdG9rZW5zLycpO1xuICAgIH1cblxuICAgIHZhciB1cmwgPSAoZG9jdW1lbnQubG9jYXRpb24ucHJvdG9jb2wgPT09ICdodHRwczonIHx8IGNvbmZpZy5GT1JDRV9IVFRQUykgPyBjb25maWcuSFRUUFNfVVJMIDogY29uZmlnLkhUVFBfVVJMO1xuICAgIHVybCA9IHVybC5yZXBsYWNlKC9cXC92NCQvLCAnJyk7XG4gICAgdXJsICs9IHBhdGg7XG5cbiAgICBpZiAoY29uZmlnLlJFUVVJUkVfQUNDRVNTX1RPS0VOKSB7XG4gICAgICAgIGlmIChhY2Nlc3NUb2tlblswXSA9PT0gJ3MnKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1VzZSBhIHB1YmxpYyBhY2Nlc3MgdG9rZW4gKHBrLiopIHdpdGggTWFwYm94LmpzLCBub3QgYSBzZWNyZXQgYWNjZXNzIHRva2VuIChzay4qKS4gJyArXG4gICAgICAgICAgICAgICAgJ1NlZSBodHRwczovL3d3dy5tYXBib3guY29tL21hcGJveC5qcy9hcGkvdicgKyB2ZXJzaW9uICsgJy9hcGktYWNjZXNzLXRva2Vucy8nKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHVybCArPSB1cmwuaW5kZXhPZignPycpICE9PSAtMSA/ICcmYWNjZXNzX3Rva2VuPScgOiAnP2FjY2Vzc190b2tlbj0nO1xuICAgICAgICB1cmwgKz0gYWNjZXNzVG9rZW47XG4gICAgfVxuXG4gICAgcmV0dXJuIHVybDtcbn07XG5cbm1vZHVsZS5leHBvcnRzLnRpbGVKU09OID0gZnVuY3Rpb24odXJsT3JNYXBJRCwgYWNjZXNzVG9rZW4pIHtcblxuICAgIGlmICh1cmxPck1hcElELmluZGV4T2YoJ21hcGJveDovL3N0eWxlcycpID09PSAwKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignU3R5bGVzIGNyZWF0ZWQgd2l0aCBNYXBib3ggU3R1ZGlvIG5lZWQgdG8gYmUgdXNlZCB3aXRoICcgK1xuICAgICAgICAgICAgJ0wubWFwYm94LnN0eWxlTGF5ZXIsIG5vdCBMLm1hcGJveC50aWxlTGF5ZXInKTtcbiAgICB9XG5cbiAgICBpZiAodXJsT3JNYXBJRC5pbmRleE9mKCcvJykgIT09IC0xKVxuICAgICAgICByZXR1cm4gdXJsT3JNYXBJRDtcblxuICAgIHZhciB1cmwgPSBtb2R1bGUuZXhwb3J0cygnL3Y0LycgKyB1cmxPck1hcElEICsgJy5qc29uJywgYWNjZXNzVG9rZW4pO1xuXG4gICAgLy8gVGlsZUpTT04gcmVxdWVzdHMgbmVlZCBhIHNlY3VyZSBmbGFnIGFwcGVuZGVkIHRvIHRoZWlyIFVSTHMgc29cbiAgICAvLyB0aGF0IHRoZSBzZXJ2ZXIga25vd3MgdG8gc2VuZCBTU0wtaWZpZWQgcmVzb3VyY2UgcmVmZXJlbmNlcy5cbiAgICBpZiAodXJsLmluZGV4T2YoJ2h0dHBzJykgPT09IDApXG4gICAgICAgIHVybCArPSAnJnNlY3VyZSc7XG5cbiAgICByZXR1cm4gdXJsO1xufTtcblxuXG5tb2R1bGUuZXhwb3J0cy5zdHlsZSA9IGZ1bmN0aW9uKHN0eWxlVVJMLCBhY2Nlc3NUb2tlbikge1xuICAgIGlmIChzdHlsZVVSTC5pbmRleE9mKCdtYXBib3g6Ly9zdHlsZXMvJykgPT09IC0xKSB0aHJvdyBuZXcgRXJyb3IoJ0luY29ycmVjdGx5IGZvcm1hdHRlZCBNYXBib3ggc3R5bGUgYXQgJyArIHN0eWxlVVJMKTtcblxuICAgIHZhciBvd25lcklEU3R5bGUgPSBzdHlsZVVSTC5zcGxpdCgnbWFwYm94Oi8vc3R5bGVzLycpWzFdO1xuICAgIHZhciB1cmwgPSBtb2R1bGUuZXhwb3J0cygnL3N0eWxlcy92MS8nICsgb3duZXJJRFN0eWxlLCBhY2Nlc3NUb2tlbilcbiAgICAgICAgLnJlcGxhY2UoJ2h0dHA6Ly8nLCAnaHR0cHM6Ly8nKTtcblxuICAgIHJldHVybiB1cmw7XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgaXNBcnJheSA9IHJlcXVpcmUoJ2lzYXJyYXknKSxcbiAgICB1dGlsID0gcmVxdWlyZSgnLi91dGlsJyksXG4gICAgZm9ybWF0X3VybCA9IHJlcXVpcmUoJy4vZm9ybWF0X3VybCcpLFxuICAgIGZlZWRiYWNrID0gcmVxdWlyZSgnLi9mZWVkYmFjaycpLFxuICAgIHJlcXVlc3QgPSByZXF1aXJlKCcuL3JlcXVlc3QnKTtcblxuLy8gTG93LWxldmVsIGdlb2NvZGluZyBpbnRlcmZhY2UgLSB3cmFwcyBzcGVjaWZpYyBBUEkgY2FsbHMgYW5kIHRoZWlyXG4vLyByZXR1cm4gdmFsdWVzLlxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbih1cmwsIG9wdGlvbnMpIHtcbiAgICBpZiAoIW9wdGlvbnMpIG9wdGlvbnMgPSB7fTtcbiAgICB2YXIgZ2VvY29kZXIgPSB7fTtcblxuICAgIHV0aWwuc3RyaWN0KHVybCwgJ3N0cmluZycpO1xuXG4gICAgaWYgKHVybC5pbmRleE9mKCcvJykgPT09IC0xKSB7XG4gICAgICAgIHVybCA9IGZvcm1hdF91cmwoJy9nZW9jb2RpbmcvdjUvJyArIHVybCArICcve3F1ZXJ5fS5qc29uJywgb3B0aW9ucy5hY2Nlc3NUb2tlbiwgNSk7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gcm91bmRUbyhsYXRMbmcsIHByZWNpc2lvbikge1xuICAgICAgICB2YXIgbXVsdCA9IE1hdGgucG93KDEwLCBwcmVjaXNpb24pO1xuICAgICAgICBsYXRMbmcubGF0ID0gTWF0aC5yb3VuZChsYXRMbmcubGF0ICogbXVsdCkgLyBtdWx0O1xuICAgICAgICBsYXRMbmcubG5nID0gTWF0aC5yb3VuZChsYXRMbmcubG5nICogbXVsdCkgLyBtdWx0O1xuICAgICAgICByZXR1cm4gbGF0TG5nO1xuICAgIH1cblxuICAgIGdlb2NvZGVyLmdldFVSTCA9IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gdXJsO1xuICAgIH07XG5cbiAgICBnZW9jb2Rlci5xdWVyeVVSTCA9IGZ1bmN0aW9uKF8pIHtcbiAgICAgICAgdmFyIGlzT2JqZWN0ID0gIShpc0FycmF5KF8pIHx8IHR5cGVvZiBfID09PSAnc3RyaW5nJyksXG4gICAgICAgICAgICBxdWVyeSA9IGlzT2JqZWN0ID8gXy5xdWVyeSA6IF87XG5cbiAgICAgICAgaWYgKGlzQXJyYXkocXVlcnkpKSB7XG4gICAgICAgICAgICB2YXIgcGFydHMgPSBbXTtcbiAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgcXVlcnkubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICBwYXJ0c1tpXSA9IGVuY29kZVVSSUNvbXBvbmVudChxdWVyeVtpXSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBxdWVyeSA9IHBhcnRzLmpvaW4oJzsnKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHF1ZXJ5ID0gZW5jb2RlVVJJQ29tcG9uZW50KHF1ZXJ5KTtcbiAgICAgICAgfVxuXG4gICAgICAgIGZlZWRiYWNrLnJlY29yZCh7IGdlb2NvZGluZzogcXVlcnkgfSk7XG5cbiAgICAgICAgdmFyIHVybCA9IEwuVXRpbC50ZW1wbGF0ZShnZW9jb2Rlci5nZXRVUkwoKSwge3F1ZXJ5OiBxdWVyeX0pO1xuXG4gICAgICAgIGlmIChpc09iamVjdCkge1xuICAgICAgICAgICAgaWYgKF8udHlwZXMpIHtcbiAgICAgICAgICAgICAgICBpZiAoaXNBcnJheShfLnR5cGVzKSkge1xuICAgICAgICAgICAgICAgICAgICB1cmwgKz0gJyZ0eXBlcz0nICsgXy50eXBlcy5qb2luKCk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgdXJsICs9ICcmdHlwZXM9JyArIF8udHlwZXM7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoXy5jb3VudHJ5KSB7XG4gICAgICAgICAgICAgICAgaWYgKGlzQXJyYXkoXy5jb3VudHJ5KSkge1xuICAgICAgICAgICAgICAgICAgICB1cmwgKz0gJyZjb3VudHJ5PScgKyBfLmNvdW50cnkuam9pbigpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHVybCArPSAnJmNvdW50cnk9JyArIF8uY291bnRyeTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChfLmJib3gpIHtcbiAgICAgICAgICAgICAgICBpZiAoaXNBcnJheShfLmJib3gpKSB7XG4gICAgICAgICAgICAgICAgICAgIHVybCArPSAnJmJib3g9JyArIF8uYmJveC5qb2luKCk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgdXJsICs9ICcmYmJveD0nICsgXy5iYm94O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKF8ucHJveGltaXR5KSB7XG4gICAgICAgICAgICAgICAgdmFyIHByb3hpbWl0eSA9IHJvdW5kVG8oTC5sYXRMbmcoXy5wcm94aW1pdHkpLCAzKTtcbiAgICAgICAgICAgICAgICB1cmwgKz0gJyZwcm94aW1pdHk9JyArIHByb3hpbWl0eS5sbmcgKyAnLCcgKyBwcm94aW1pdHkubGF0O1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAodHlwZW9mIF8uYXV0b2NvbXBsZXRlID09PSAnYm9vbGVhbicpIHtcbiAgICAgICAgICAgICAgICB1cmwgKz0gJyZhdXRvY29tcGxldGU9JyArIF8uYXV0b2NvbXBsZXRlO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHVybDtcbiAgICB9O1xuXG4gICAgZ2VvY29kZXIucXVlcnkgPSBmdW5jdGlvbihfLCBjYWxsYmFjaykge1xuICAgICAgICB1dGlsLnN0cmljdChjYWxsYmFjaywgJ2Z1bmN0aW9uJyk7XG5cbiAgICAgICAgcmVxdWVzdChnZW9jb2Rlci5xdWVyeVVSTChfKSwgZnVuY3Rpb24oZXJyLCBqc29uKSB7XG4gICAgICAgICAgICBpZiAoanNvbiAmJiAoanNvbi5sZW5ndGggfHwganNvbi5mZWF0dXJlcykpIHtcbiAgICAgICAgICAgICAgICB2YXIgcmVzID0ge1xuICAgICAgICAgICAgICAgICAgICByZXN1bHRzOiBqc29uXG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICBpZiAoanNvbi5mZWF0dXJlcyAmJiBqc29uLmZlYXR1cmVzLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgICAgICByZXMubGF0bG5nID0gW1xuICAgICAgICAgICAgICAgICAgICAgICAganNvbi5mZWF0dXJlc1swXS5jZW50ZXJbMV0sXG4gICAgICAgICAgICAgICAgICAgICAgICBqc29uLmZlYXR1cmVzWzBdLmNlbnRlclswXV07XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKGpzb24uZmVhdHVyZXNbMF0uYmJveCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmVzLmJvdW5kcyA9IGpzb24uZmVhdHVyZXNbMF0uYmJveDtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlcy5sYm91bmRzID0gdXRpbC5sYm91bmRzKHJlcy5ib3VuZHMpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGNhbGxiYWNrKG51bGwsIHJlcyk7XG4gICAgICAgICAgICB9IGVsc2UgY2FsbGJhY2soZXJyIHx8IHRydWUpO1xuICAgICAgICB9KTtcblxuICAgICAgICByZXR1cm4gZ2VvY29kZXI7XG4gICAgfTtcblxuICAgIC8vIGEgcmV2ZXJzZSBnZW9jb2RlOlxuICAgIC8vXG4gICAgLy8gIGdlb2NvZGVyLnJldmVyc2VRdWVyeShbODAsIDIwXSlcbiAgICBnZW9jb2Rlci5yZXZlcnNlUXVlcnkgPSBmdW5jdGlvbihfLCBjYWxsYmFjaykge1xuICAgICAgICB2YXIgcSA9ICcnO1xuXG4gICAgICAgIC8vIHNvcnQgdGhyb3VnaCBkaWZmZXJlbnQgd2F5cyBwZW9wbGUgcmVwcmVzZW50IGxhdCBhbmQgbG9uIHBhaXJzXG4gICAgICAgIGZ1bmN0aW9uIG5vcm1hbGl6ZSh4KSB7XG4gICAgICAgICAgICB2YXIgbGF0TG5nO1xuICAgICAgICAgICAgaWYgKHgubGF0ICE9PSB1bmRlZmluZWQgJiYgeC5sbmcgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIGxhdExuZyA9IEwubGF0TG5nKHgubGF0LCB4LmxuZyk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHgubGF0ICE9PSB1bmRlZmluZWQgJiYgeC5sb24gIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIGxhdExuZyA9IEwubGF0TG5nKHgubGF0LCB4Lmxvbik7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGxhdExuZyA9IEwubGF0TG5nKHhbMV0sIHhbMF0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgbGF0TG5nID0gcm91bmRUbyhsYXRMbmcsIDUpO1xuICAgICAgICAgICAgcmV0dXJuIGxhdExuZy5sbmcgKyAnLCcgKyBsYXRMbmcubGF0O1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKF8ubGVuZ3RoICYmIF9bMF0ubGVuZ3RoKSB7XG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMCwgcHRzID0gW107IGkgPCBfLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgcHRzLnB1c2gobm9ybWFsaXplKF9baV0pKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHEgPSBwdHMuam9pbignOycpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcSA9IG5vcm1hbGl6ZShfKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJlcXVlc3QoZ2VvY29kZXIucXVlcnlVUkwocSksIGZ1bmN0aW9uKGVyciwganNvbikge1xuICAgICAgICAgICAgY2FsbGJhY2soZXJyLCBqc29uKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgcmV0dXJuIGdlb2NvZGVyO1xuICAgIH07XG5cbiAgICByZXR1cm4gZ2VvY29kZXI7XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgZ2VvY29kZXIgPSByZXF1aXJlKCcuL2dlb2NvZGVyJyksXG4gICAgdXRpbCA9IHJlcXVpcmUoJy4vdXRpbCcpO1xuXG52YXIgR2VvY29kZXJDb250cm9sID0gTC5Db250cm9sLmV4dGVuZCh7XG4gICAgaW5jbHVkZXM6IEwuTWl4aW4uRXZlbnRzLFxuXG4gICAgb3B0aW9uczoge1xuICAgICAgICBwcm94aW1pdHk6IHRydWUsXG4gICAgICAgIHBvc2l0aW9uOiAndG9wbGVmdCcsXG4gICAgICAgIHBvaW50Wm9vbTogMTYsXG4gICAgICAgIGtlZXBPcGVuOiBmYWxzZSxcbiAgICAgICAgYXV0b2NvbXBsZXRlOiBmYWxzZSxcbiAgICAgICAgcXVlcnlPcHRpb25zOiB7fVxuICAgIH0sXG5cbiAgICBpbml0aWFsaXplOiBmdW5jdGlvbihfLCBvcHRpb25zKSB7XG4gICAgICAgIEwuVXRpbC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xuICAgICAgICB0aGlzLnNldFVSTChfKTtcbiAgICAgICAgdGhpcy5fdXBkYXRlU3VibWl0ID0gTC5iaW5kKHRoaXMuX3VwZGF0ZVN1Ym1pdCwgdGhpcyk7XG4gICAgICAgIHRoaXMuX3VwZGF0ZUF1dG9jb21wbGV0ZSA9IEwuYmluZCh0aGlzLl91cGRhdGVBdXRvY29tcGxldGUsIHRoaXMpO1xuICAgICAgICB0aGlzLl9jaG9vc2VSZXN1bHQgPSBMLmJpbmQodGhpcy5fY2hvb3NlUmVzdWx0LCB0aGlzKTtcbiAgICB9LFxuXG4gICAgc2V0VVJMOiBmdW5jdGlvbihfKSB7XG4gICAgICAgIHRoaXMuZ2VvY29kZXIgPSBnZW9jb2RlcihfLCB7XG4gICAgICAgICAgICBhY2Nlc3NUb2tlbjogdGhpcy5vcHRpb25zLmFjY2Vzc1Rva2VuXG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuXG4gICAgZ2V0VVJMOiBmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZ2VvY29kZXIuZ2V0VVJMKCk7XG4gICAgfSxcblxuICAgIHNldElEOiBmdW5jdGlvbihfKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnNldFVSTChfKTtcbiAgICB9LFxuXG4gICAgc2V0VGlsZUpTT046IGZ1bmN0aW9uKF8pIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc2V0VVJMKF8uZ2VvY29kZXIpO1xuICAgIH0sXG5cbiAgICBfdG9nZ2xlOiBmdW5jdGlvbihlKSB7XG4gICAgICAgIGlmIChlKSBMLkRvbUV2ZW50LnN0b3AoZSk7XG4gICAgICAgIGlmIChMLkRvbVV0aWwuaGFzQ2xhc3ModGhpcy5fY29udGFpbmVyLCAnYWN0aXZlJykpIHtcbiAgICAgICAgICAgIEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9jb250YWluZXIsICdhY3RpdmUnKTtcbiAgICAgICAgICAgIHRoaXMuX3Jlc3VsdHMuaW5uZXJIVE1MID0gJyc7XG4gICAgICAgICAgICB0aGlzLl9pbnB1dC5ibHVyKCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBMLkRvbVV0aWwuYWRkQ2xhc3ModGhpcy5fY29udGFpbmVyLCAnYWN0aXZlJyk7XG4gICAgICAgICAgICB0aGlzLl9pbnB1dC5mb2N1cygpO1xuICAgICAgICAgICAgdGhpcy5faW5wdXQuc2VsZWN0KCk7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgX2Nsb3NlSWZPcGVuOiBmdW5jdGlvbigpIHtcbiAgICAgICAgaWYgKEwuRG9tVXRpbC5oYXNDbGFzcyh0aGlzLl9jb250YWluZXIsICdhY3RpdmUnKSAmJlxuICAgICAgICAgICAgIXRoaXMub3B0aW9ucy5rZWVwT3Blbikge1xuICAgICAgICAgICAgTC5Eb21VdGlsLnJlbW92ZUNsYXNzKHRoaXMuX2NvbnRhaW5lciwgJ2FjdGl2ZScpO1xuICAgICAgICAgICAgdGhpcy5fcmVzdWx0cy5pbm5lckhUTUwgPSAnJztcbiAgICAgICAgICAgIHRoaXMuX2lucHV0LmJsdXIoKTtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICBvbkFkZDogZnVuY3Rpb24obWFwKSB7XG5cbiAgICAgICAgdmFyIGNvbnRhaW5lciA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LWNvbnRyb2wtbWFwYm94LWdlb2NvZGVyIGxlYWZsZXQtYmFyIGxlYWZsZXQtY29udHJvbCcpLFxuICAgICAgICAgICAgbGluayA9IEwuRG9tVXRpbC5jcmVhdGUoJ2EnLCAnbGVhZmxldC1jb250cm9sLW1hcGJveC1nZW9jb2Rlci10b2dnbGUgbWFwYm94LWljb24gbWFwYm94LWljb24tZ2VvY29kZXInLCBjb250YWluZXIpLFxuICAgICAgICAgICAgcmVzdWx0cyA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LWNvbnRyb2wtbWFwYm94LWdlb2NvZGVyLXJlc3VsdHMnLCBjb250YWluZXIpLFxuICAgICAgICAgICAgd3JhcCA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LWNvbnRyb2wtbWFwYm94LWdlb2NvZGVyLXdyYXAnLCBjb250YWluZXIpLFxuICAgICAgICAgICAgZm9ybSA9IEwuRG9tVXRpbC5jcmVhdGUoJ2Zvcm0nLCAnbGVhZmxldC1jb250cm9sLW1hcGJveC1nZW9jb2Rlci1mb3JtJywgd3JhcCksXG4gICAgICAgICAgICBpbnB1dCA9IEwuRG9tVXRpbC5jcmVhdGUoJ2lucHV0JywgJycsIGZvcm0pO1xuXG4gICAgICAgIGxpbmsuaHJlZiA9ICcjJztcbiAgICAgICAgbGluay5pbm5lckhUTUwgPSAnJm5ic3A7JztcblxuICAgICAgICBpbnB1dC50eXBlID0gJ3RleHQnO1xuICAgICAgICBpbnB1dC5zZXRBdHRyaWJ1dGUoJ3BsYWNlaG9sZGVyJywgJ1NlYXJjaCcpO1xuXG4gICAgICAgIEwuRG9tRXZlbnQuYWRkTGlzdGVuZXIoZm9ybSwgJ3N1Ym1pdCcsIHRoaXMuX2dlb2NvZGUsIHRoaXMpO1xuICAgICAgICBMLkRvbUV2ZW50LmFkZExpc3RlbmVyKGlucHV0LCAna2V5dXAnLCB0aGlzLl9hdXRvY29tcGxldGUsIHRoaXMpO1xuICAgICAgICBMLkRvbUV2ZW50LmRpc2FibGVDbGlja1Byb3BhZ2F0aW9uKGNvbnRhaW5lcik7XG5cbiAgICAgICAgdGhpcy5fbWFwID0gbWFwO1xuICAgICAgICB0aGlzLl9yZXN1bHRzID0gcmVzdWx0cztcbiAgICAgICAgdGhpcy5faW5wdXQgPSBpbnB1dDtcbiAgICAgICAgdGhpcy5fZm9ybSA9IGZvcm07XG5cbiAgICAgICAgaWYgKHRoaXMub3B0aW9ucy5rZWVwT3Blbikge1xuICAgICAgICAgICAgTC5Eb21VdGlsLmFkZENsYXNzKGNvbnRhaW5lciwgJ2FjdGl2ZScpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5fbWFwLm9uKCdjbGljaycsIHRoaXMuX2Nsb3NlSWZPcGVuLCB0aGlzKTtcbiAgICAgICAgICAgIEwuRG9tRXZlbnQuYWRkTGlzdGVuZXIobGluaywgJ2NsaWNrJywgdGhpcy5fdG9nZ2xlLCB0aGlzKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBjb250YWluZXI7XG4gICAgfSxcblxuICAgIF91cGRhdGVTdWJtaXQ6IGZ1bmN0aW9uKGVyciwgcmVzcCkge1xuICAgICAgICBMLkRvbVV0aWwucmVtb3ZlQ2xhc3ModGhpcy5fY29udGFpbmVyLCAnc2VhcmNoaW5nJyk7XG4gICAgICAgIHRoaXMuX3Jlc3VsdHMuaW5uZXJIVE1MID0gJyc7XG4gICAgICAgIGlmIChlcnIgfHwgIXJlc3ApIHtcbiAgICAgICAgICAgIHRoaXMuZmlyZSgnZXJyb3InLCB7ZXJyb3I6IGVycn0pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdmFyIGZlYXR1cmVzID0gW107XG4gICAgICAgICAgICBpZiAocmVzcC5yZXN1bHRzICYmIHJlc3AucmVzdWx0cy5mZWF0dXJlcykge1xuICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gcmVzcC5yZXN1bHRzLmZlYXR1cmVzO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKGZlYXR1cmVzLmxlbmd0aCA9PT0gMSkge1xuICAgICAgICAgICAgICAgIHRoaXMuZmlyZSgnYXV0b3NlbGVjdCcsIHsgZmVhdHVyZTogZmVhdHVyZXNbMF0gfSk7XG4gICAgICAgICAgICAgICAgdGhpcy5maXJlKCdmb3VuZCcsIHtyZXN1bHRzOiByZXNwLnJlc3VsdHN9KTtcbiAgICAgICAgICAgICAgICB0aGlzLl9jaG9vc2VSZXN1bHQoZmVhdHVyZXNbMF0pO1xuICAgICAgICAgICAgICAgIHRoaXMuX2Nsb3NlSWZPcGVuKCk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKGZlYXR1cmVzLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmZpcmUoJ2ZvdW5kJywge3Jlc3VsdHM6IHJlc3AucmVzdWx0c30pO1xuICAgICAgICAgICAgICAgIHRoaXMuX2Rpc3BsYXlSZXN1bHRzKGZlYXR1cmVzKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdGhpcy5maXJlKCdub3Rmb3VuZCcpO1xuICAgICAgICAgICAgICAgIHRoaXMuX2Rpc3BsYXlSZXN1bHRzKGZlYXR1cmVzKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICBfdXBkYXRlQXV0b2NvbXBsZXRlOiBmdW5jdGlvbihlcnIsIHJlc3ApIHtcbiAgICAgICAgdGhpcy5fcmVzdWx0cy5pbm5lckhUTUwgPSAnJztcbiAgICAgICAgaWYgKGVyciB8fCAhcmVzcCkge1xuICAgICAgICAgICAgdGhpcy5maXJlKCdlcnJvcicsIHtlcnJvcjogZXJyfSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB2YXIgZmVhdHVyZXMgPSBbXTtcbiAgICAgICAgICAgIGlmIChyZXNwLnJlc3VsdHMgJiYgcmVzcC5yZXN1bHRzLmZlYXR1cmVzKSB7XG4gICAgICAgICAgICAgICAgZmVhdHVyZXMgPSByZXNwLnJlc3VsdHMuZmVhdHVyZXM7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoZmVhdHVyZXMubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5maXJlKCdmb3VuZCcsIHtyZXN1bHRzOiByZXNwLnJlc3VsdHN9KTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdGhpcy5maXJlKCdub3Rmb3VuZCcpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5fZGlzcGxheVJlc3VsdHMoZmVhdHVyZXMpO1xuICAgICAgICB9XG4gICAgfSxcblxuICAgIF9kaXNwbGF5UmVzdWx0czogZnVuY3Rpb24oZmVhdHVyZXMpIHtcbiAgICAgICAgZm9yICh2YXIgaSA9IDAsIGwgPSBNYXRoLm1pbihmZWF0dXJlcy5sZW5ndGgsIDUpOyBpIDwgbDsgaSsrKSB7XG4gICAgICAgICAgICB2YXIgZmVhdHVyZSA9IGZlYXR1cmVzW2ldO1xuICAgICAgICAgICAgdmFyIG5hbWUgPSBmZWF0dXJlLnBsYWNlX25hbWU7XG4gICAgICAgICAgICBpZiAoIW5hbWUubGVuZ3RoKSBjb250aW51ZTtcblxuICAgICAgICAgICAgdmFyIHIgPSBMLkRvbVV0aWwuY3JlYXRlKCdhJywgJycsIHRoaXMuX3Jlc3VsdHMpO1xuICAgICAgICAgICAgdmFyIHRleHQgPSAoJ2lubmVyVGV4dCcgaW4gcikgPyAnaW5uZXJUZXh0JyA6ICd0ZXh0Q29udGVudCc7XG4gICAgICAgICAgICByW3RleHRdID0gbmFtZTtcbiAgICAgICAgICAgIHIuc2V0QXR0cmlidXRlKCd0aXRsZScsIG5hbWUpO1xuICAgICAgICAgICAgci5ocmVmID0gJyMnO1xuXG4gICAgICAgICAgICAoTC5iaW5kKGZ1bmN0aW9uKGZlYXR1cmUpIHtcbiAgICAgICAgICAgICAgICBMLkRvbUV2ZW50LmFkZExpc3RlbmVyKHIsICdjbGljaycsIGZ1bmN0aW9uKGUpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5fY2hvb3NlUmVzdWx0KGZlYXR1cmUpO1xuICAgICAgICAgICAgICAgICAgICBMLkRvbUV2ZW50LnN0b3AoZSk7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZmlyZSgnc2VsZWN0JywgeyBmZWF0dXJlOiBmZWF0dXJlIH0pO1xuICAgICAgICAgICAgICAgIH0sIHRoaXMpO1xuICAgICAgICAgICAgfSwgdGhpcykpKGZlYXR1cmUpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChmZWF0dXJlcy5sZW5ndGggPiA1KSB7XG4gICAgICAgICAgICB2YXIgb3V0b2YgPSBMLkRvbVV0aWwuY3JlYXRlKCdzcGFuJywgJycsIHRoaXMuX3Jlc3VsdHMpO1xuICAgICAgICAgICAgb3V0b2YuaW5uZXJIVE1MID0gJ1RvcCA1IG9mICcgKyBmZWF0dXJlcy5sZW5ndGggKyAnICByZXN1bHRzJztcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICBfY2hvb3NlUmVzdWx0OiBmdW5jdGlvbihyZXN1bHQpIHtcbiAgICAgICAgaWYgKHJlc3VsdC5iYm94KSB7XG4gICAgICAgICAgICB0aGlzLl9tYXAuZml0Qm91bmRzKHV0aWwubGJvdW5kcyhyZXN1bHQuYmJveCkpO1xuICAgICAgICB9IGVsc2UgaWYgKHJlc3VsdC5jZW50ZXIpIHtcbiAgICAgICAgICAgIHRoaXMuX21hcC5zZXRWaWV3KFtyZXN1bHQuY2VudGVyWzFdLCByZXN1bHQuY2VudGVyWzBdXSwgKHRoaXMuX21hcC5nZXRab29tKCkgPT09IHVuZGVmaW5lZCkgP1xuICAgICAgICAgICAgICAgIHRoaXMub3B0aW9ucy5wb2ludFpvb20gOlxuICAgICAgICAgICAgICAgIE1hdGgubWF4KHRoaXMuX21hcC5nZXRab29tKCksIHRoaXMub3B0aW9ucy5wb2ludFpvb20pKTtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICBfZ2VvY29kZTogZnVuY3Rpb24oZSkge1xuICAgICAgICBMLkRvbUV2ZW50LnByZXZlbnREZWZhdWx0KGUpO1xuICAgICAgICBpZiAodGhpcy5faW5wdXQudmFsdWUgPT09ICcnKSByZXR1cm4gdGhpcy5fdXBkYXRlU3VibWl0KCk7XG4gICAgICAgIEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9jb250YWluZXIsICdzZWFyY2hpbmcnKTtcbiAgICAgICAgdGhpcy5nZW9jb2Rlci5xdWVyeShMLlV0aWwuZXh0ZW5kKHtcbiAgICAgICAgICAgIHF1ZXJ5OiB0aGlzLl9pbnB1dC52YWx1ZSxcbiAgICAgICAgICAgIHByb3hpbWl0eTogdGhpcy5vcHRpb25zLnByb3hpbWl0eSA/IHRoaXMuX21hcC5nZXRDZW50ZXIoKSA6IGZhbHNlXG4gICAgICAgIH0sIHRoaXMub3B0aW9ucy5xdWVyeU9wdGlvbnMpLCB0aGlzLl91cGRhdGVTdWJtaXQpO1xuICAgIH0sXG5cbiAgICBfYXV0b2NvbXBsZXRlOiBmdW5jdGlvbigpIHtcbiAgICAgICAgaWYgKCF0aGlzLm9wdGlvbnMuYXV0b2NvbXBsZXRlKSByZXR1cm47XG4gICAgICAgIGlmICh0aGlzLl9pbnB1dC52YWx1ZSA9PT0gJycpIHJldHVybiB0aGlzLl91cGRhdGVBdXRvY29tcGxldGUoKTtcbiAgICAgICAgdGhpcy5nZW9jb2Rlci5xdWVyeShMLlV0aWwuZXh0ZW5kKHtcbiAgICAgICAgICAgIHF1ZXJ5OiB0aGlzLl9pbnB1dC52YWx1ZSxcbiAgICAgICAgICAgIHByb3hpbWl0eTogdGhpcy5vcHRpb25zLnByb3hpbWl0eSA/IHRoaXMuX21hcC5nZXRDZW50ZXIoKSA6IGZhbHNlXG4gICAgICAgIH0sIHRoaXMub3B0aW9ucy5xdWVyeU9wdGlvbnMpLCB0aGlzLl91cGRhdGVBdXRvY29tcGxldGUpO1xuICAgIH1cbn0pO1xuXG5tb2R1bGUuZXhwb3J0cy5HZW9jb2RlckNvbnRyb2wgPSBHZW9jb2RlckNvbnRyb2w7XG5cbm1vZHVsZS5leHBvcnRzLmdlb2NvZGVyQ29udHJvbCA9IGZ1bmN0aW9uKF8sIG9wdGlvbnMpIHtcbiAgICByZXR1cm4gbmV3IEdlb2NvZGVyQ29udHJvbChfLCBvcHRpb25zKTtcbn07XG4iLCIndXNlIHN0cmljdCc7XG5cbmZ1bmN0aW9uIHV0ZkRlY29kZShjKSB7XG4gICAgaWYgKGMgPj0gOTMpIGMtLTtcbiAgICBpZiAoYyA+PSAzNSkgYy0tO1xuICAgIHJldHVybiBjIC0gMzI7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24oZGF0YSkge1xuICAgIHJldHVybiBmdW5jdGlvbih4LCB5KSB7XG4gICAgICAgIGlmICghZGF0YSkgcmV0dXJuO1xuICAgICAgICB2YXIgaWR4ID0gdXRmRGVjb2RlKGRhdGEuZ3JpZFt5XS5jaGFyQ29kZUF0KHgpKSxcbiAgICAgICAgICAgIGtleSA9IGRhdGEua2V5c1tpZHhdO1xuICAgICAgICByZXR1cm4gZGF0YS5kYXRhW2tleV07XG4gICAgfTtcbn07XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciB1dGlsID0gcmVxdWlyZSgnLi91dGlsJyksXG4gICAgTXVzdGFjaGUgPSByZXF1aXJlKCdtdXN0YWNoZScpO1xuXG52YXIgR3JpZENvbnRyb2wgPSBMLkNvbnRyb2wuZXh0ZW5kKHtcblxuICAgIG9wdGlvbnM6IHtcbiAgICAgICAgcGlubmFibGU6IHRydWUsXG4gICAgICAgIGZvbGxvdzogZmFsc2UsXG4gICAgICAgIHNhbml0aXplcjogcmVxdWlyZSgnc2FuaXRpemUtY2FqYScpLFxuICAgICAgICB0b3VjaFRlYXNlcjogdHJ1ZSxcbiAgICAgICAgbG9jYXRpb246IHRydWVcbiAgICB9LFxuXG4gICAgX2N1cnJlbnRDb250ZW50OiAnJyxcblxuICAgIC8vIHBpbm5lZCBtZWFucyB0aGF0IHRoaXMgY29udHJvbCBpcyBvbiBhIGZlYXR1cmUgYW5kIHRoZSB1c2VyIGhhcyBsaWtlbHlcbiAgICAvLyBjbGlja2VkLiBwaW5uZWQgd2lsbCBub3QgYmVjb21lIGZhbHNlIHVubGVzcyB0aGUgdXNlciBjbGlja3Mgb2ZmXG4gICAgLy8gb2YgdGhlIGZlYXR1cmUgb250byBhbm90aGVyIG9yIGNsaWNrcyB4XG4gICAgX3Bpbm5lZDogZmFsc2UsXG5cbiAgICBpbml0aWFsaXplOiBmdW5jdGlvbihfLCBvcHRpb25zKSB7XG4gICAgICAgIEwuVXRpbC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xuICAgICAgICB1dGlsLnN0cmljdF9pbnN0YW5jZShfLCBMLkNsYXNzLCAnTC5tYXBib3guZ3JpZExheWVyJyk7XG4gICAgICAgIHRoaXMuX2xheWVyID0gXztcbiAgICB9LFxuXG4gICAgc2V0VGVtcGxhdGU6IGZ1bmN0aW9uKHRlbXBsYXRlKSB7XG4gICAgICAgIHV0aWwuc3RyaWN0KHRlbXBsYXRlLCAnc3RyaW5nJyk7XG4gICAgICAgIHRoaXMub3B0aW9ucy50ZW1wbGF0ZSA9IHRlbXBsYXRlO1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuXG4gICAgX3RlbXBsYXRlOiBmdW5jdGlvbihmb3JtYXQsIGRhdGEpIHtcbiAgICAgICAgaWYgKCFkYXRhKSByZXR1cm47XG4gICAgICAgIHZhciB0ZW1wbGF0ZSA9IHRoaXMub3B0aW9ucy50ZW1wbGF0ZSB8fCB0aGlzLl9sYXllci5nZXRUaWxlSlNPTigpLnRlbXBsYXRlO1xuICAgICAgICBpZiAodGVtcGxhdGUpIHtcbiAgICAgICAgICAgIHZhciBkID0ge307XG4gICAgICAgICAgICBkWydfXycgKyBmb3JtYXQgKyAnX18nXSA9IHRydWU7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5vcHRpb25zLnNhbml0aXplcihcbiAgICAgICAgICAgICAgICBNdXN0YWNoZS50b19odG1sKHRlbXBsYXRlLCBMLmV4dGVuZChkLCBkYXRhKSkpO1xuICAgICAgICB9XG4gICAgfSxcblxuICAgIC8vIGNoYW5nZSB0aGUgY29udGVudCBvZiB0aGUgdG9vbHRpcCBIVE1MIGlmIGl0IGhhcyBjaGFuZ2VkLCBvdGhlcndpc2VcbiAgICAvLyBub29wXG4gICAgX3Nob3c6IGZ1bmN0aW9uKGNvbnRlbnQsIG8pIHtcbiAgICAgICAgaWYgKGNvbnRlbnQgPT09IHRoaXMuX2N1cnJlbnRDb250ZW50KSByZXR1cm47XG5cbiAgICAgICAgdGhpcy5fY3VycmVudENvbnRlbnQgPSBjb250ZW50O1xuXG4gICAgICAgIGlmICh0aGlzLm9wdGlvbnMuZm9sbG93KSB7XG4gICAgICAgICAgICB0aGlzLl9wb3B1cC5zZXRDb250ZW50KGNvbnRlbnQpXG4gICAgICAgICAgICAgICAgLnNldExhdExuZyhvLmxhdExuZyk7XG4gICAgICAgICAgICBpZiAodGhpcy5fbWFwLl9wb3B1cCAhPT0gdGhpcy5fcG9wdXApIHRoaXMuX3BvcHVwLm9wZW5Pbih0aGlzLl9tYXApO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5fY29udGFpbmVyLnN0eWxlLmRpc3BsYXkgPSAnYmxvY2snO1xuICAgICAgICAgICAgdGhpcy5fY29udGVudFdyYXBwZXIuaW5uZXJIVE1MID0gY29udGVudDtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICBoaWRlOiBmdW5jdGlvbigpIHtcbiAgICAgICAgdGhpcy5fcGlubmVkID0gZmFsc2U7XG4gICAgICAgIHRoaXMuX2N1cnJlbnRDb250ZW50ID0gJyc7XG5cbiAgICAgICAgdGhpcy5fbWFwLmNsb3NlUG9wdXAoKTtcbiAgICAgICAgdGhpcy5fY29udGFpbmVyLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7XG4gICAgICAgIHRoaXMuX2NvbnRlbnRXcmFwcGVyLmlubmVySFRNTCA9ICcnO1xuXG4gICAgICAgIEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9jb250YWluZXIsICdjbG9zYWJsZScpO1xuXG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG5cbiAgICBfbW91c2VvdmVyOiBmdW5jdGlvbihvKSB7XG4gICAgICAgIGlmIChvLmRhdGEpIHtcbiAgICAgICAgICAgIEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9tYXAuX2NvbnRhaW5lciwgJ21hcC1jbGlja2FibGUnKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9tYXAuX2NvbnRhaW5lciwgJ21hcC1jbGlja2FibGUnKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICh0aGlzLl9waW5uZWQpIHJldHVybjtcblxuICAgICAgICB2YXIgY29udGVudCA9IHRoaXMuX3RlbXBsYXRlKCd0ZWFzZXInLCBvLmRhdGEpO1xuICAgICAgICBpZiAoY29udGVudCkge1xuICAgICAgICAgICAgdGhpcy5fc2hvdyhjb250ZW50LCBvKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuaGlkZSgpO1xuICAgICAgICB9XG4gICAgfSxcblxuICAgIF9tb3VzZW1vdmU6IGZ1bmN0aW9uKG8pIHtcbiAgICAgICAgaWYgKHRoaXMuX3Bpbm5lZCkgcmV0dXJuO1xuICAgICAgICBpZiAoIXRoaXMub3B0aW9ucy5mb2xsb3cpIHJldHVybjtcblxuICAgICAgICB0aGlzLl9wb3B1cC5zZXRMYXRMbmcoby5sYXRMbmcpO1xuICAgIH0sXG5cbiAgICBfbmF2aWdhdGVUbzogZnVuY3Rpb24odXJsKSB7XG4gICAgICAgIHdpbmRvdy50b3AubG9jYXRpb24uaHJlZiA9IHVybDtcbiAgICB9LFxuXG4gICAgX2NsaWNrOiBmdW5jdGlvbihvKSB7XG5cbiAgICAgICAgdmFyIGxvY2F0aW9uX2Zvcm1hdHRlZCA9IHRoaXMuX3RlbXBsYXRlKCdsb2NhdGlvbicsIG8uZGF0YSk7XG4gICAgICAgIGlmICh0aGlzLm9wdGlvbnMubG9jYXRpb24gJiYgbG9jYXRpb25fZm9ybWF0dGVkICYmXG4gICAgICAgICAgICBsb2NhdGlvbl9mb3JtYXR0ZWQuc2VhcmNoKC9eaHR0cHM/Oi8pID09PSAwKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5fbmF2aWdhdGVUbyh0aGlzLl90ZW1wbGF0ZSgnbG9jYXRpb24nLCBvLmRhdGEpKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICghdGhpcy5vcHRpb25zLnBpbm5hYmxlKSByZXR1cm47XG5cbiAgICAgICAgdmFyIGNvbnRlbnQgPSB0aGlzLl90ZW1wbGF0ZSgnZnVsbCcsIG8uZGF0YSk7XG5cbiAgICAgICAgaWYgKCFjb250ZW50ICYmIHRoaXMub3B0aW9ucy50b3VjaFRlYXNlciAmJiBMLkJyb3dzZXIudG91Y2gpIHtcbiAgICAgICAgICAgIGNvbnRlbnQgPSB0aGlzLl90ZW1wbGF0ZSgndGVhc2VyJywgby5kYXRhKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChjb250ZW50KSB7XG4gICAgICAgICAgICBMLkRvbVV0aWwuYWRkQ2xhc3ModGhpcy5fY29udGFpbmVyLCAnY2xvc2FibGUnKTtcbiAgICAgICAgICAgIHRoaXMuX3Bpbm5lZCA9IHRydWU7XG4gICAgICAgICAgICB0aGlzLl9zaG93KGNvbnRlbnQsIG8pO1xuICAgICAgICB9IGVsc2UgaWYgKHRoaXMuX3Bpbm5lZCkge1xuICAgICAgICAgICAgTC5Eb21VdGlsLnJlbW92ZUNsYXNzKHRoaXMuX2NvbnRhaW5lciwgJ2Nsb3NhYmxlJyk7XG4gICAgICAgICAgICB0aGlzLl9waW5uZWQgPSBmYWxzZTtcbiAgICAgICAgICAgIHRoaXMuaGlkZSgpO1xuICAgICAgICB9XG4gICAgfSxcblxuICAgIF9vblBvcHVwQ2xvc2U6IGZ1bmN0aW9uKCkge1xuICAgICAgICB0aGlzLl9jdXJyZW50Q29udGVudCA9IG51bGw7XG4gICAgICAgIHRoaXMuX3Bpbm5lZCA9IGZhbHNlO1xuICAgIH0sXG5cbiAgICBfY3JlYXRlQ2xvc2VidXR0b246IGZ1bmN0aW9uKGNvbnRhaW5lciwgZm4pIHtcbiAgICAgICAgdmFyIGxpbmsgPSBMLkRvbVV0aWwuY3JlYXRlKCdhJywgJ2Nsb3NlJywgY29udGFpbmVyKTtcblxuICAgICAgICBsaW5rLmlubmVySFRNTCA9ICdjbG9zZSc7XG4gICAgICAgIGxpbmsuaHJlZiA9ICcjJztcbiAgICAgICAgbGluay50aXRsZSA9ICdjbG9zZSc7XG5cbiAgICAgICAgTC5Eb21FdmVudFxuICAgICAgICAgICAgLm9uKGxpbmssICdjbGljaycsIEwuRG9tRXZlbnQuc3RvcFByb3BhZ2F0aW9uKVxuICAgICAgICAgICAgLm9uKGxpbmssICdtb3VzZWRvd24nLCBMLkRvbUV2ZW50LnN0b3BQcm9wYWdhdGlvbilcbiAgICAgICAgICAgIC5vbihsaW5rLCAnZGJsY2xpY2snLCBMLkRvbUV2ZW50LnN0b3BQcm9wYWdhdGlvbilcbiAgICAgICAgICAgIC5vbihsaW5rLCAnY2xpY2snLCBMLkRvbUV2ZW50LnByZXZlbnREZWZhdWx0KVxuICAgICAgICAgICAgLm9uKGxpbmssICdjbGljaycsIGZuLCB0aGlzKTtcblxuICAgICAgICByZXR1cm4gbGluaztcbiAgICB9LFxuXG4gICAgb25BZGQ6IGZ1bmN0aW9uKG1hcCkge1xuICAgICAgICB0aGlzLl9tYXAgPSBtYXA7XG5cbiAgICAgICAgdmFyIGNsYXNzTmFtZSA9ICdsZWFmbGV0LWNvbnRyb2wtZ3JpZCBtYXAtdG9vbHRpcCcsXG4gICAgICAgICAgICBjb250YWluZXIgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCBjbGFzc05hbWUpLFxuICAgICAgICAgICAgY29udGVudFdyYXBwZXIgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCAnbWFwLXRvb2x0aXAtY29udGVudCcpO1xuXG4gICAgICAgIC8vIGhpZGUgdGhlIGNvbnRhaW5lciBlbGVtZW50IGluaXRpYWxseVxuICAgICAgICBjb250YWluZXIuc3R5bGUuZGlzcGxheSA9ICdub25lJztcbiAgICAgICAgdGhpcy5fY3JlYXRlQ2xvc2VidXR0b24oY29udGFpbmVyLCB0aGlzLmhpZGUpO1xuICAgICAgICBjb250YWluZXIuYXBwZW5kQ2hpbGQoY29udGVudFdyYXBwZXIpO1xuXG4gICAgICAgIHRoaXMuX2NvbnRlbnRXcmFwcGVyID0gY29udGVudFdyYXBwZXI7XG4gICAgICAgIHRoaXMuX3BvcHVwID0gbmV3IEwuUG9wdXAoeyBhdXRvUGFuOiBmYWxzZSwgY2xvc2VPbkNsaWNrOiBmYWxzZSB9KTtcblxuICAgICAgICBtYXAub24oJ3BvcHVwY2xvc2UnLCB0aGlzLl9vblBvcHVwQ2xvc2UsIHRoaXMpO1xuXG4gICAgICAgIEwuRG9tRXZlbnRcbiAgICAgICAgICAgIC5kaXNhYmxlQ2xpY2tQcm9wYWdhdGlvbihjb250YWluZXIpXG4gICAgICAgICAgICAvLyBhbGxvdyBwZW9wbGUgdG8gc2Nyb2xsIHRvb2x0aXBzIHdpdGggbW91c2V3aGVlbFxuICAgICAgICAgICAgLmFkZExpc3RlbmVyKGNvbnRhaW5lciwgJ21vdXNld2hlZWwnLCBMLkRvbUV2ZW50LnN0b3BQcm9wYWdhdGlvbik7XG5cbiAgICAgICAgdGhpcy5fbGF5ZXJcbiAgICAgICAgICAgIC5vbignbW91c2VvdmVyJywgdGhpcy5fbW91c2VvdmVyLCB0aGlzKVxuICAgICAgICAgICAgLm9uKCdtb3VzZW1vdmUnLCB0aGlzLl9tb3VzZW1vdmUsIHRoaXMpXG4gICAgICAgICAgICAub24oJ2NsaWNrJywgdGhpcy5fY2xpY2ssIHRoaXMpO1xuXG4gICAgICAgIHJldHVybiBjb250YWluZXI7XG4gICAgfSxcblxuICAgIG9uUmVtb3ZlOiBmdW5jdGlvbiAobWFwKSB7XG5cbiAgICAgICAgbWFwLm9mZigncG9wdXBjbG9zZScsIHRoaXMuX29uUG9wdXBDbG9zZSwgdGhpcyk7XG5cbiAgICAgICAgdGhpcy5fbGF5ZXJcbiAgICAgICAgICAgIC5vZmYoJ21vdXNlb3ZlcicsIHRoaXMuX21vdXNlb3ZlciwgdGhpcylcbiAgICAgICAgICAgIC5vZmYoJ21vdXNlbW92ZScsIHRoaXMuX21vdXNlbW92ZSwgdGhpcylcbiAgICAgICAgICAgIC5vZmYoJ2NsaWNrJywgdGhpcy5fY2xpY2ssIHRoaXMpO1xuICAgIH1cbn0pO1xuXG5tb2R1bGUuZXhwb3J0cy5HcmlkQ29udHJvbCA9IEdyaWRDb250cm9sO1xuXG5tb2R1bGUuZXhwb3J0cy5ncmlkQ29udHJvbCA9IGZ1bmN0aW9uKF8sIG9wdGlvbnMpIHtcbiAgICByZXR1cm4gbmV3IEdyaWRDb250cm9sKF8sIG9wdGlvbnMpO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIHV0aWwgPSByZXF1aXJlKCcuL3V0aWwnKSxcbiAgICByZXF1ZXN0ID0gcmVxdWlyZSgnLi9yZXF1ZXN0JyksXG4gICAgZ3JpZCA9IHJlcXVpcmUoJy4vZ3JpZCcpO1xuXG4vLyBmb3JrZWQgZnJvbSBkYW56ZWwvTC5VVEZHcmlkXG52YXIgR3JpZExheWVyID0gTC5MYXllci5leHRlbmQoe1xuICAgIGluY2x1ZGVzOiBbcmVxdWlyZSgnLi9sb2FkX3RpbGVqc29uJyldLFxuXG4gICAgb3B0aW9uczoge1xuICAgICAgICB0ZW1wbGF0ZTogZnVuY3Rpb24oKSB7IHJldHVybiAnJzsgfVxuICAgIH0sXG5cbiAgICBfbW91c2VPbjogbnVsbCxcbiAgICBfdGlsZWpzb246IHt9LFxuICAgIF9jYWNoZToge30sXG5cbiAgICBpbml0aWFsaXplOiBmdW5jdGlvbihfLCBvcHRpb25zKSB7XG4gICAgICAgIEwuVXRpbC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xuICAgICAgICB0aGlzLl9sb2FkVGlsZUpTT04oXyk7XG4gICAgfSxcblxuICAgIF9zZXRUaWxlSlNPTjogZnVuY3Rpb24oanNvbikge1xuICAgICAgICB1dGlsLnN0cmljdChqc29uLCAnb2JqZWN0Jyk7XG5cbiAgICAgICAgTC5leHRlbmQodGhpcy5vcHRpb25zLCB7XG4gICAgICAgICAgICBncmlkczoganNvbi5ncmlkcyxcbiAgICAgICAgICAgIG1pblpvb206IGpzb24ubWluem9vbSxcbiAgICAgICAgICAgIG1heFpvb206IGpzb24ubWF4em9vbSxcbiAgICAgICAgICAgIGJvdW5kczoganNvbi5ib3VuZHMgJiYgdXRpbC5sYm91bmRzKGpzb24uYm91bmRzKVxuICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLl90aWxlanNvbiA9IGpzb247XG4gICAgICAgIHRoaXMuX2NhY2hlID0ge307XG4gICAgICAgIHRoaXMuX3VwZGF0ZSgpO1xuXG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG5cbiAgICBnZXRUaWxlSlNPTjogZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl90aWxlanNvbjtcbiAgICB9LFxuXG4gICAgYWN0aXZlOiBmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuICEhKHRoaXMuX21hcCAmJiB0aGlzLm9wdGlvbnMuZ3JpZHMgJiYgdGhpcy5vcHRpb25zLmdyaWRzLmxlbmd0aCk7XG4gICAgfSxcblxuICAgIG9uQWRkOiBmdW5jdGlvbihtYXApIHtcbiAgICAgICAgdGhpcy5fbWFwID0gbWFwO1xuICAgICAgICB0aGlzLl91cGRhdGUoKTtcblxuICAgICAgICB0aGlzLl9tYXBcbiAgICAgICAgICAgIC5vbignY2xpY2snLCB0aGlzLl9jbGljaywgdGhpcylcbiAgICAgICAgICAgIC5vbignbW91c2Vtb3ZlJywgdGhpcy5fbW92ZSwgdGhpcylcbiAgICAgICAgICAgIC5vbignbW92ZWVuZCcsIHRoaXMuX3VwZGF0ZSwgdGhpcyk7XG4gICAgfSxcblxuICAgIG9uUmVtb3ZlOiBmdW5jdGlvbigpIHtcbiAgICAgICAgdGhpcy5fbWFwXG4gICAgICAgICAgICAub2ZmKCdjbGljaycsIHRoaXMuX2NsaWNrLCB0aGlzKVxuICAgICAgICAgICAgLm9mZignbW91c2Vtb3ZlJywgdGhpcy5fbW92ZSwgdGhpcylcbiAgICAgICAgICAgIC5vZmYoJ21vdmVlbmQnLCB0aGlzLl91cGRhdGUsIHRoaXMpO1xuICAgIH0sXG5cbiAgICBnZXREYXRhOiBmdW5jdGlvbihsYXRsbmcsIGNhbGxiYWNrKSB7XG4gICAgICAgIGlmICghdGhpcy5hY3RpdmUoKSkgcmV0dXJuO1xuXG4gICAgICAgIHZhciBtYXAgPSB0aGlzLl9tYXAsXG4gICAgICAgICAgICBwb2ludCA9IG1hcC5wcm9qZWN0KGxhdGxuZy53cmFwKCkpLFxuICAgICAgICAgICAgdGlsZVNpemUgPSAyNTYsXG4gICAgICAgICAgICByZXNvbHV0aW9uID0gNCxcbiAgICAgICAgICAgIHggPSBNYXRoLmZsb29yKHBvaW50LnggLyB0aWxlU2l6ZSksXG4gICAgICAgICAgICB5ID0gTWF0aC5mbG9vcihwb2ludC55IC8gdGlsZVNpemUpLFxuICAgICAgICAgICAgbWF4ID0gbWFwLm9wdGlvbnMuY3JzLnNjYWxlKG1hcC5nZXRab29tKCkpIC8gdGlsZVNpemU7XG5cbiAgICAgICAgeCA9ICh4ICsgbWF4KSAlIG1heDtcbiAgICAgICAgeSA9ICh5ICsgbWF4KSAlIG1heDtcblxuICAgICAgICB0aGlzLl9nZXRUaWxlKG1hcC5nZXRab29tKCksIHgsIHksIGZ1bmN0aW9uKGdyaWQpIHtcbiAgICAgICAgICAgIHZhciBncmlkWCA9IE1hdGguZmxvb3IoKHBvaW50LnggLSAoeCAqIHRpbGVTaXplKSkgLyByZXNvbHV0aW9uKSxcbiAgICAgICAgICAgICAgICBncmlkWSA9IE1hdGguZmxvb3IoKHBvaW50LnkgLSAoeSAqIHRpbGVTaXplKSkgLyByZXNvbHV0aW9uKTtcblxuICAgICAgICAgICAgY2FsbGJhY2soZ3JpZChncmlkWCwgZ3JpZFkpKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcblxuICAgIF9jbGljazogZnVuY3Rpb24oZSkge1xuICAgICAgICB0aGlzLmdldERhdGEoZS5sYXRsbmcsIEwuYmluZChmdW5jdGlvbihkYXRhKSB7XG4gICAgICAgICAgICB0aGlzLmZpcmUoJ2NsaWNrJywge1xuICAgICAgICAgICAgICAgIGxhdExuZzogZS5sYXRsbmcsXG4gICAgICAgICAgICAgICAgZGF0YTogZGF0YVxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0sIHRoaXMpKTtcbiAgICB9LFxuXG4gICAgX21vdmU6IGZ1bmN0aW9uKGUpIHtcbiAgICAgICAgdGhpcy5nZXREYXRhKGUubGF0bG5nLCBMLmJpbmQoZnVuY3Rpb24oZGF0YSkge1xuICAgICAgICAgICAgaWYgKGRhdGEgIT09IHRoaXMuX21vdXNlT24pIHtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5fbW91c2VPbikge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmZpcmUoJ21vdXNlb3V0Jywge1xuICAgICAgICAgICAgICAgICAgICAgICAgbGF0TG5nOiBlLmxhdGxuZyxcbiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE6IHRoaXMuX21vdXNlT25cbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgdGhpcy5maXJlKCdtb3VzZW92ZXInLCB7XG4gICAgICAgICAgICAgICAgICAgIGxhdExuZzogZS5sYXRsbmcsXG4gICAgICAgICAgICAgICAgICAgIGRhdGE6IGRhdGFcbiAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICAgIHRoaXMuX21vdXNlT24gPSBkYXRhO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB0aGlzLmZpcmUoJ21vdXNlbW92ZScsIHtcbiAgICAgICAgICAgICAgICAgICAgbGF0TG5nOiBlLmxhdGxuZyxcbiAgICAgICAgICAgICAgICAgICAgZGF0YTogZGF0YVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9LCB0aGlzKSk7XG4gICAgfSxcblxuICAgIF9nZXRUaWxlVVJMOiBmdW5jdGlvbih0aWxlUG9pbnQpIHtcbiAgICAgICAgdmFyIHVybHMgPSB0aGlzLm9wdGlvbnMuZ3JpZHMsXG4gICAgICAgICAgICBpbmRleCA9ICh0aWxlUG9pbnQueCArIHRpbGVQb2ludC55KSAlIHVybHMubGVuZ3RoLFxuICAgICAgICAgICAgdXJsID0gdXJsc1tpbmRleF07XG5cbiAgICAgICAgcmV0dXJuIEwuVXRpbC50ZW1wbGF0ZSh1cmwsIHRpbGVQb2ludCk7XG4gICAgfSxcblxuICAgIC8vIExvYWQgdXAgYWxsIHJlcXVpcmVkIGpzb24gZ3JpZCBmaWxlc1xuICAgIF91cGRhdGU6IGZ1bmN0aW9uKCkge1xuICAgICAgICBpZiAoIXRoaXMuYWN0aXZlKCkpIHJldHVybjtcblxuICAgICAgICB2YXIgYm91bmRzID0gdGhpcy5fbWFwLmdldFBpeGVsQm91bmRzKCksXG4gICAgICAgICAgICB6ID0gdGhpcy5fbWFwLmdldFpvb20oKSxcbiAgICAgICAgICAgIHRpbGVTaXplID0gMjU2O1xuXG4gICAgICAgIGlmICh6ID4gdGhpcy5vcHRpb25zLm1heFpvb20gfHwgeiA8IHRoaXMub3B0aW9ucy5taW5ab29tKSByZXR1cm47XG5cbiAgICAgICAgdmFyIHRpbGVCb3VuZHMgPSBMLmJvdW5kcyhcbiAgICAgICAgICAgICAgICBib3VuZHMubWluLmRpdmlkZUJ5KHRpbGVTaXplKS5fZmxvb3IoKSxcbiAgICAgICAgICAgICAgICBib3VuZHMubWF4LmRpdmlkZUJ5KHRpbGVTaXplKS5fZmxvb3IoKSksXG4gICAgICAgICAgICBtYXggPSB0aGlzLl9tYXAub3B0aW9ucy5jcnMuc2NhbGUoeikgLyB0aWxlU2l6ZTtcblxuICAgICAgICBmb3IgKHZhciB4ID0gdGlsZUJvdW5kcy5taW4ueDsgeCA8PSB0aWxlQm91bmRzLm1heC54OyB4KyspIHtcbiAgICAgICAgICAgIGZvciAodmFyIHkgPSB0aWxlQm91bmRzLm1pbi55OyB5IDw9IHRpbGVCb3VuZHMubWF4Lnk7IHkrKykge1xuICAgICAgICAgICAgICAgIC8vIHggd3JhcHBlZFxuICAgICAgICAgICAgICAgIHRoaXMuX2dldFRpbGUoeiwgKCh4ICUgbWF4KSArIG1heCkgJSBtYXgsICgoeSAlIG1heCkgKyBtYXgpICUgbWF4KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICBfZ2V0VGlsZTogZnVuY3Rpb24oeiwgeCwgeSwgY2FsbGJhY2spIHtcbiAgICAgICAgdmFyIGtleSA9IHogKyAnXycgKyB4ICsgJ18nICsgeSxcbiAgICAgICAgICAgIHRpbGVQb2ludCA9IEwucG9pbnQoeCwgeSk7XG5cbiAgICAgICAgdGlsZVBvaW50LnogPSB6O1xuXG4gICAgICAgIGlmICghdGhpcy5fdGlsZVNob3VsZEJlTG9hZGVkKHRpbGVQb2ludCkpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChrZXkgaW4gdGhpcy5fY2FjaGUpIHtcbiAgICAgICAgICAgIGlmICghY2FsbGJhY2spIHJldHVybjtcblxuICAgICAgICAgICAgaWYgKHR5cGVvZiB0aGlzLl9jYWNoZVtrZXldID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICAgICAgY2FsbGJhY2sodGhpcy5fY2FjaGVba2V5XSk7IC8vIEFscmVhZHkgbG9hZGVkXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHRoaXMuX2NhY2hlW2tleV0ucHVzaChjYWxsYmFjayk7IC8vIFBlbmRpbmdcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5fY2FjaGVba2V5XSA9IFtdO1xuXG4gICAgICAgIGlmIChjYWxsYmFjaykge1xuICAgICAgICAgICAgdGhpcy5fY2FjaGVba2V5XS5wdXNoKGNhbGxiYWNrKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJlcXVlc3QodGhpcy5fZ2V0VGlsZVVSTCh0aWxlUG9pbnQpLCBMLmJpbmQoZnVuY3Rpb24oZXJyLCBqc29uKSB7XG4gICAgICAgICAgICB2YXIgY2FsbGJhY2tzID0gdGhpcy5fY2FjaGVba2V5XTtcbiAgICAgICAgICAgIHRoaXMuX2NhY2hlW2tleV0gPSBncmlkKGpzb24pO1xuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBjYWxsYmFja3MubGVuZ3RoOyArK2kpIHtcbiAgICAgICAgICAgICAgICBjYWxsYmFja3NbaV0odGhpcy5fY2FjaGVba2V5XSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sIHRoaXMpKTtcbiAgICB9LFxuXG4gICAgX3RpbGVTaG91bGRCZUxvYWRlZDogZnVuY3Rpb24odGlsZVBvaW50KSB7XG4gICAgICAgIGlmICh0aWxlUG9pbnQueiA+IHRoaXMub3B0aW9ucy5tYXhab29tIHx8IHRpbGVQb2ludC56IDwgdGhpcy5vcHRpb25zLm1pblpvb20pIHtcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICh0aGlzLm9wdGlvbnMuYm91bmRzKSB7XG4gICAgICAgICAgICB2YXIgdGlsZVNpemUgPSAyNTYsXG4gICAgICAgICAgICAgICAgbndQb2ludCA9IHRpbGVQb2ludC5tdWx0aXBseUJ5KHRpbGVTaXplKSxcbiAgICAgICAgICAgICAgICBzZVBvaW50ID0gbndQb2ludC5hZGQobmV3IEwuUG9pbnQodGlsZVNpemUsIHRpbGVTaXplKSksXG4gICAgICAgICAgICAgICAgbncgPSB0aGlzLl9tYXAudW5wcm9qZWN0KG53UG9pbnQpLFxuICAgICAgICAgICAgICAgIHNlID0gdGhpcy5fbWFwLnVucHJvamVjdChzZVBvaW50KSxcbiAgICAgICAgICAgICAgICBib3VuZHMgPSBuZXcgTC5MYXRMbmdCb3VuZHMoW253LCBzZV0pO1xuXG4gICAgICAgICAgICBpZiAoIXRoaXMub3B0aW9ucy5ib3VuZHMuaW50ZXJzZWN0cyhib3VuZHMpKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxufSk7XG5cbm1vZHVsZS5leHBvcnRzLkdyaWRMYXllciA9IEdyaWRMYXllcjtcblxubW9kdWxlLmV4cG9ydHMuZ3JpZExheWVyID0gZnVuY3Rpb24oXywgb3B0aW9ucykge1xuICAgIHJldHVybiBuZXcgR3JpZExheWVyKF8sIG9wdGlvbnMpO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIGxlYWZsZXQgPSByZXF1aXJlKCcuL2xlYWZsZXQnKTtcblxucmVxdWlyZSgnLi9tYXBib3gnKTtcblxubW9kdWxlLmV4cG9ydHMgPSBsZWFmbGV0O1xuIiwibW9kdWxlLmV4cG9ydHMgPSB3aW5kb3cuTCA9IHJlcXVpcmUoJ2xlYWZsZXQvZGlzdC9sZWFmbGV0LXNyYycpO1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgTGVnZW5kQ29udHJvbCA9IEwuQ29udHJvbC5leHRlbmQoe1xuXG4gICAgb3B0aW9uczoge1xuICAgICAgICBwb3NpdGlvbjogJ2JvdHRvbXJpZ2h0JyxcbiAgICAgICAgc2FuaXRpemVyOiByZXF1aXJlKCdzYW5pdGl6ZS1jYWphJylcbiAgICB9LFxuXG4gICAgaW5pdGlhbGl6ZTogZnVuY3Rpb24ob3B0aW9ucykge1xuICAgICAgICBMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XG4gICAgICAgIHRoaXMuX2xlZ2VuZHMgPSB7fTtcbiAgICB9LFxuXG4gICAgb25BZGQ6IGZ1bmN0aW9uKCkge1xuICAgICAgICB0aGlzLl9jb250YWluZXIgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCAnbWFwLWxlZ2VuZHMgd2F4LWxlZ2VuZHMnKTtcbiAgICAgICAgTC5Eb21FdmVudC5kaXNhYmxlQ2xpY2tQcm9wYWdhdGlvbih0aGlzLl9jb250YWluZXIpO1xuXG4gICAgICAgIHRoaXMuX3VwZGF0ZSgpO1xuXG4gICAgICAgIHJldHVybiB0aGlzLl9jb250YWluZXI7XG4gICAgfSxcblxuICAgIGFkZExlZ2VuZDogZnVuY3Rpb24odGV4dCkge1xuICAgICAgICBpZiAoIXRleHQpIHsgcmV0dXJuIHRoaXM7IH1cblxuICAgICAgICBpZiAoIXRoaXMuX2xlZ2VuZHNbdGV4dF0pIHtcbiAgICAgICAgICAgIHRoaXMuX2xlZ2VuZHNbdGV4dF0gPSAwO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5fbGVnZW5kc1t0ZXh0XSsrO1xuICAgICAgICByZXR1cm4gdGhpcy5fdXBkYXRlKCk7XG4gICAgfSxcblxuICAgIHJlbW92ZUxlZ2VuZDogZnVuY3Rpb24odGV4dCkge1xuICAgICAgICBpZiAoIXRleHQpIHsgcmV0dXJuIHRoaXM7IH1cbiAgICAgICAgaWYgKHRoaXMuX2xlZ2VuZHNbdGV4dF0pIHRoaXMuX2xlZ2VuZHNbdGV4dF0tLTtcbiAgICAgICAgcmV0dXJuIHRoaXMuX3VwZGF0ZSgpO1xuICAgIH0sXG5cbiAgICBfdXBkYXRlOiBmdW5jdGlvbigpIHtcbiAgICAgICAgaWYgKCF0aGlzLl9tYXApIHsgcmV0dXJuIHRoaXM7IH1cblxuICAgICAgICB0aGlzLl9jb250YWluZXIuaW5uZXJIVE1MID0gJyc7XG4gICAgICAgIHZhciBoaWRlID0gJ25vbmUnO1xuXG4gICAgICAgIGZvciAodmFyIGkgaW4gdGhpcy5fbGVnZW5kcykge1xuICAgICAgICAgICAgaWYgKHRoaXMuX2xlZ2VuZHMuaGFzT3duUHJvcGVydHkoaSkgJiYgdGhpcy5fbGVnZW5kc1tpXSkge1xuICAgICAgICAgICAgICAgIHZhciBkaXYgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCAnbWFwLWxlZ2VuZCB3YXgtbGVnZW5kJywgdGhpcy5fY29udGFpbmVyKTtcbiAgICAgICAgICAgICAgICBkaXYuaW5uZXJIVE1MID0gdGhpcy5vcHRpb25zLnNhbml0aXplcihpKTtcbiAgICAgICAgICAgICAgICBoaWRlID0gJ2Jsb2NrJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIGhpZGUgdGhlIGNvbnRyb2wgZW50aXJlbHkgdW5sZXNzIHRoZXJlIGlzIGF0IGxlYXN0IG9uZSBsZWdlbmQ7XG4gICAgICAgIC8vIG90aGVyd2lzZSB0aGVyZSB3aWxsIGJlIGEgc21hbGwgZ3JleSBibGVtaXNoIG9uIHRoZSBtYXAuXG4gICAgICAgIHRoaXMuX2NvbnRhaW5lci5zdHlsZS5kaXNwbGF5ID0gaGlkZTtcblxuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9XG59KTtcblxubW9kdWxlLmV4cG9ydHMuTGVnZW5kQ29udHJvbCA9IExlZ2VuZENvbnRyb2w7XG5cbm1vZHVsZS5leHBvcnRzLmxlZ2VuZENvbnRyb2wgPSBmdW5jdGlvbihvcHRpb25zKSB7XG4gICAgcmV0dXJuIG5ldyBMZWdlbmRDb250cm9sKG9wdGlvbnMpO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIHJlcXVlc3QgPSByZXF1aXJlKCcuL3JlcXVlc3QnKSxcbiAgICBmb3JtYXRfdXJsID0gcmVxdWlyZSgnLi9mb3JtYXRfdXJsJyksXG4gICAgdXRpbCA9IHJlcXVpcmUoJy4vdXRpbCcpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgICBfbG9hZFRpbGVKU09OOiBmdW5jdGlvbihfKSB7XG4gICAgICAgIGlmICh0eXBlb2YgXyA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgIF8gPSBmb3JtYXRfdXJsLnRpbGVKU09OKF8sIHRoaXMub3B0aW9ucyAmJiB0aGlzLm9wdGlvbnMuYWNjZXNzVG9rZW4pO1xuICAgICAgICAgICAgcmVxdWVzdChfLCBMLmJpbmQoZnVuY3Rpb24oZXJyLCBqc29uKSB7XG4gICAgICAgICAgICAgICAgaWYgKGVycikge1xuICAgICAgICAgICAgICAgICAgICB1dGlsLmxvZygnY291bGQgbm90IGxvYWQgVGlsZUpTT04gYXQgJyArIF8pO1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmZpcmUoJ2Vycm9yJywge2Vycm9yOiBlcnJ9KTtcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKGpzb24pIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5fc2V0VGlsZUpTT04oanNvbik7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZmlyZSgncmVhZHknKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LCB0aGlzKSk7XG4gICAgICAgIH0gZWxzZSBpZiAoXyAmJiB0eXBlb2YgXyA9PT0gJ29iamVjdCcpIHtcbiAgICAgICAgICAgIHRoaXMuX3NldFRpbGVKU09OKF8pO1xuICAgICAgICB9XG4gICAgfVxufTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIHRpbGVMYXllciA9IHJlcXVpcmUoJy4vdGlsZV9sYXllcicpLnRpbGVMYXllcixcbiAgICBmZWF0dXJlTGF5ZXIgPSByZXF1aXJlKCcuL2ZlYXR1cmVfbGF5ZXInKS5mZWF0dXJlTGF5ZXIsXG4gICAgZ3JpZExheWVyID0gcmVxdWlyZSgnLi9ncmlkX2xheWVyJykuZ3JpZExheWVyLFxuICAgIGdyaWRDb250cm9sID0gcmVxdWlyZSgnLi9ncmlkX2NvbnRyb2wnKS5ncmlkQ29udHJvbCxcbiAgICBzaGFyZUNvbnRyb2wgPSByZXF1aXJlKCcuL3NoYXJlX2NvbnRyb2wnKS5zaGFyZUNvbnRyb2wsXG4gICAgbGVnZW5kQ29udHJvbCA9IHJlcXVpcmUoJy4vbGVnZW5kX2NvbnRyb2wnKS5sZWdlbmRDb250cm9sLFxuICAgIG1hcGJveExvZ29Db250cm9sID0gcmVxdWlyZSgnLi9tYXBib3hfbG9nbycpLm1hcGJveExvZ29Db250cm9sLFxuICAgIGZlZWRiYWNrID0gcmVxdWlyZSgnLi9mZWVkYmFjaycpO1xuXG5mdW5jdGlvbiB3aXRoQWNjZXNzVG9rZW4ob3B0aW9ucywgYWNjZXNzVG9rZW4pIHtcbiAgICBpZiAoIWFjY2Vzc1Rva2VuIHx8IG9wdGlvbnMuYWNjZXNzVG9rZW4pXG4gICAgICAgIHJldHVybiBvcHRpb25zO1xuICAgIHJldHVybiBMLmV4dGVuZCh7YWNjZXNzVG9rZW46IGFjY2Vzc1Rva2VufSwgb3B0aW9ucyk7XG59XG5cbnZhciBMTWFwID0gTC5NYXAuZXh0ZW5kKHtcbiAgICBpbmNsdWRlczogW3JlcXVpcmUoJy4vbG9hZF90aWxlanNvbicpXSxcblxuICAgIG9wdGlvbnM6IHtcbiAgICAgICAgdGlsZUxheWVyOiB7fSxcbiAgICAgICAgZmVhdHVyZUxheWVyOiB7fSxcbiAgICAgICAgZ3JpZExheWVyOiB7fSxcbiAgICAgICAgbGVnZW5kQ29udHJvbDoge30sXG4gICAgICAgIGdyaWRDb250cm9sOiB7fSxcbiAgICAgICAgc2hhcmVDb250cm9sOiBmYWxzZSxcbiAgICAgICAgc2FuaXRpemVyOiByZXF1aXJlKCdzYW5pdGl6ZS1jYWphJylcbiAgICB9LFxuXG4gICAgX3RpbGVqc29uOiB7fSxcblxuICAgIGluaXRpYWxpemU6IGZ1bmN0aW9uKGVsZW1lbnQsIF8sIG9wdGlvbnMpIHtcblxuICAgICAgICBMLk1hcC5wcm90b3R5cGUuaW5pdGlhbGl6ZS5jYWxsKHRoaXMsIGVsZW1lbnQsXG4gICAgICAgICAgICBMLmV4dGVuZCh7fSwgTC5NYXAucHJvdG90eXBlLm9wdGlvbnMsIG9wdGlvbnMpKTtcblxuICAgICAgICAvLyBEaXNhYmxlIHRoZSBkZWZhdWx0ICdMZWFmbGV0JyB0ZXh0XG4gICAgICAgIGlmICh0aGlzLmF0dHJpYnV0aW9uQ29udHJvbCkge1xuICAgICAgICAgICAgdGhpcy5hdHRyaWJ1dGlvbkNvbnRyb2wuc2V0UHJlZml4KCcnKTtcblxuICAgICAgICAgICAgdmFyIGNvbXBhY3QgPSB0aGlzLm9wdGlvbnMuYXR0cmlidXRpb25Db250cm9sLmNvbXBhY3Q7XG4gICAgICAgICAgICAvLyBTZXQgYSBjb21wYWN0IGRpc3BsYXkgaWYgbWFwIGNvbnRhaW5lciB3aWR0aCBpcyA8IDY0MCBvclxuICAgICAgICAgICAgLy8gY29tcGFjdCBpcyBzZXQgdG8gYHRydWVgIGluIGF0dHJpYnV0aW9uQ29udHJvbCBvcHRpb25zLlxuICAgICAgICAgICAgaWYgKGNvbXBhY3QgfHwgKGNvbXBhY3QgIT09IGZhbHNlICYmIHRoaXMuX2NvbnRhaW5lci5vZmZzZXRXaWR0aCA8PSA2NDApKSB7XG4gICAgICAgICAgICAgICAgTC5Eb21VdGlsLmFkZENsYXNzKHRoaXMuYXR0cmlidXRpb25Db250cm9sLl9jb250YWluZXIsICdsZWFmbGV0LWNvbXBhY3QtYXR0cmlidXRpb24nKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKGNvbXBhY3QgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIHRoaXMub24oJ3Jlc2l6ZScsIGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5fY29udGFpbmVyLm9mZnNldFdpZHRoID4gNjQwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBMLkRvbVV0aWwucmVtb3ZlQ2xhc3ModGhpcy5hdHRyaWJ1dGlvbkNvbnRyb2wuX2NvbnRhaW5lciwgJ2xlYWZsZXQtY29tcGFjdC1hdHRyaWJ1dGlvbicpO1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgTC5Eb21VdGlsLmFkZENsYXNzKHRoaXMuYXR0cmlidXRpb25Db250cm9sLl9jb250YWluZXIsICdsZWFmbGV0LWNvbXBhY3QtYXR0cmlidXRpb24nKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMub3B0aW9ucy50aWxlTGF5ZXIpIHtcbiAgICAgICAgICAgIHRoaXMudGlsZUxheWVyID0gdGlsZUxheWVyKHVuZGVmaW5lZCxcbiAgICAgICAgICAgICAgICB3aXRoQWNjZXNzVG9rZW4odGhpcy5vcHRpb25zLnRpbGVMYXllciwgdGhpcy5vcHRpb25zLmFjY2Vzc1Rva2VuKSk7XG4gICAgICAgICAgICB0aGlzLmFkZExheWVyKHRoaXMudGlsZUxheWVyKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICh0aGlzLm9wdGlvbnMuZmVhdHVyZUxheWVyKSB7XG4gICAgICAgICAgICB0aGlzLmZlYXR1cmVMYXllciA9IGZlYXR1cmVMYXllcih1bmRlZmluZWQsXG4gICAgICAgICAgICAgICAgd2l0aEFjY2Vzc1Rva2VuKHRoaXMub3B0aW9ucy5mZWF0dXJlTGF5ZXIsIHRoaXMub3B0aW9ucy5hY2Nlc3NUb2tlbikpO1xuICAgICAgICAgICAgdGhpcy5hZGRMYXllcih0aGlzLmZlYXR1cmVMYXllcik7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy5vcHRpb25zLmdyaWRMYXllcikge1xuICAgICAgICAgICAgdGhpcy5ncmlkTGF5ZXIgPSBncmlkTGF5ZXIodW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgIHdpdGhBY2Nlc3NUb2tlbih0aGlzLm9wdGlvbnMuZ3JpZExheWVyLCB0aGlzLm9wdGlvbnMuYWNjZXNzVG9rZW4pKTtcbiAgICAgICAgICAgIHRoaXMuYWRkTGF5ZXIodGhpcy5ncmlkTGF5ZXIpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMub3B0aW9ucy5ncmlkTGF5ZXIgJiYgdGhpcy5vcHRpb25zLmdyaWRDb250cm9sKSB7XG4gICAgICAgICAgICB0aGlzLmdyaWRDb250cm9sID0gZ3JpZENvbnRyb2wodGhpcy5ncmlkTGF5ZXIsIHRoaXMub3B0aW9ucy5ncmlkQ29udHJvbCk7XG4gICAgICAgICAgICB0aGlzLmFkZENvbnRyb2wodGhpcy5ncmlkQ29udHJvbCk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy5vcHRpb25zLmxlZ2VuZENvbnRyb2wpIHtcbiAgICAgICAgICAgIHRoaXMubGVnZW5kQ29udHJvbCA9IGxlZ2VuZENvbnRyb2wodGhpcy5vcHRpb25zLmxlZ2VuZENvbnRyb2wpO1xuICAgICAgICAgICAgdGhpcy5hZGRDb250cm9sKHRoaXMubGVnZW5kQ29udHJvbCk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy5vcHRpb25zLnNoYXJlQ29udHJvbCkge1xuICAgICAgICAgICAgdGhpcy5zaGFyZUNvbnRyb2wgPSBzaGFyZUNvbnRyb2wodW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgIHdpdGhBY2Nlc3NUb2tlbih0aGlzLm9wdGlvbnMuc2hhcmVDb250cm9sLCB0aGlzLm9wdGlvbnMuYWNjZXNzVG9rZW4pKTtcbiAgICAgICAgICAgIHRoaXMuYWRkQ29udHJvbCh0aGlzLnNoYXJlQ29udHJvbCk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLl9tYXBib3hMb2dvQ29udHJvbCA9IG1hcGJveExvZ29Db250cm9sKHRoaXMub3B0aW9ucy5tYXBib3hMb2dvQ29udHJvbCk7XG4gICAgICAgIHRoaXMuYWRkQ29udHJvbCh0aGlzLl9tYXBib3hMb2dvQ29udHJvbCk7XG5cbiAgICAgICAgdGhpcy5fbG9hZFRpbGVKU09OKF8pO1xuXG4gICAgICAgIHRoaXMub24oJ2xheWVyYWRkJywgdGhpcy5fb25MYXllckFkZCwgdGhpcylcbiAgICAgICAgICAgIC5vbignbGF5ZXJyZW1vdmUnLCB0aGlzLl9vbkxheWVyUmVtb3ZlLCB0aGlzKVxuICAgICAgICAgICAgLm9uKCdtb3ZlZW5kJywgdGhpcy5fdXBkYXRlTWFwRmVlZGJhY2tMaW5rLCB0aGlzKTtcblxuICAgICAgICB0aGlzLndoZW5SZWFkeShmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBmZWVkYmFjay5vbignY2hhbmdlJywgdGhpcy5fdXBkYXRlTWFwRmVlZGJhY2tMaW5rLCB0aGlzKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgdGhpcy5vbigndW5sb2FkJywgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgZmVlZGJhY2sub2ZmKCdjaGFuZ2UnLCB0aGlzLl91cGRhdGVNYXBGZWVkYmFja0xpbmssIHRoaXMpO1xuICAgICAgICB9KTtcbiAgICB9LFxuXG4gICAgLy8gdXNlIGEgamF2YXNjcmlwdCBvYmplY3Qgb2YgdGlsZWpzb24gZGF0YSB0byBjb25maWd1cmUgdGhpcyBsYXllclxuICAgIF9zZXRUaWxlSlNPTjogZnVuY3Rpb24oXykge1xuICAgICAgICB0aGlzLl90aWxlanNvbiA9IF87XG4gICAgICAgIHRoaXMuX2luaXRpYWxpemUoXyk7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG5cbiAgICBnZXRUaWxlSlNPTjogZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl90aWxlanNvbjtcbiAgICB9LFxuXG4gICAgX2luaXRpYWxpemU6IGZ1bmN0aW9uKGpzb24pIHtcbiAgICAgICAgaWYgKHRoaXMudGlsZUxheWVyKSB7XG4gICAgICAgICAgICB0aGlzLnRpbGVMYXllci5fc2V0VGlsZUpTT04oanNvbik7XG4gICAgICAgICAgICB0aGlzLl91cGRhdGVMYXllcih0aGlzLnRpbGVMYXllcik7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy5mZWF0dXJlTGF5ZXIgJiYgIXRoaXMuZmVhdHVyZUxheWVyLmdldEdlb0pTT04oKSAmJiBqc29uLmRhdGEgJiYganNvbi5kYXRhWzBdKSB7XG4gICAgICAgICAgICB0aGlzLmZlYXR1cmVMYXllci5sb2FkVVJMKGpzb24uZGF0YVswXSk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy5ncmlkTGF5ZXIpIHtcbiAgICAgICAgICAgIHRoaXMuZ3JpZExheWVyLl9zZXRUaWxlSlNPTihqc29uKTtcbiAgICAgICAgICAgIHRoaXMuX3VwZGF0ZUxheWVyKHRoaXMuZ3JpZExheWVyKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICh0aGlzLmxlZ2VuZENvbnRyb2wgJiYganNvbi5sZWdlbmQpIHtcbiAgICAgICAgICAgIHRoaXMubGVnZW5kQ29udHJvbC5hZGRMZWdlbmQoanNvbi5sZWdlbmQpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMuc2hhcmVDb250cm9sKSB7XG4gICAgICAgICAgICB0aGlzLnNoYXJlQ29udHJvbC5fc2V0VGlsZUpTT04oanNvbik7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLl9tYXBib3hMb2dvQ29udHJvbC5fc2V0VGlsZUpTT04oanNvbik7XG5cbiAgICAgICAgaWYgKCF0aGlzLl9sb2FkZWQgJiYganNvbi5jZW50ZXIpIHtcbiAgICAgICAgICAgIHZhciB6b29tID0gdGhpcy5nZXRab29tKCkgIT09IHVuZGVmaW5lZCA/IHRoaXMuZ2V0Wm9vbSgpIDoganNvbi5jZW50ZXJbMl0sXG4gICAgICAgICAgICAgICAgY2VudGVyID0gTC5sYXRMbmcoanNvbi5jZW50ZXJbMV0sIGpzb24uY2VudGVyWzBdKTtcblxuICAgICAgICAgICAgdGhpcy5zZXRWaWV3KGNlbnRlciwgem9vbSk7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgX3VwZGF0ZU1hcEZlZWRiYWNrTGluazogZnVuY3Rpb24oKSB7XG4gICAgICAgIGlmICghdGhpcy5fY29udHJvbENvbnRhaW5lci5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lKSByZXR1cm47XG4gICAgICAgIHZhciBsaW5rID0gdGhpcy5fY29udHJvbENvbnRhaW5lci5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lKCdtYXBib3gtaW1wcm92ZS1tYXAnKTtcbiAgICAgICAgaWYgKGxpbmsubGVuZ3RoICYmIHRoaXMuX2xvYWRlZCkge1xuICAgICAgICAgICAgdmFyIGNlbnRlciA9IHRoaXMuZ2V0Q2VudGVyKCkud3JhcCgpO1xuICAgICAgICAgICAgdmFyIHRpbGVqc29uID0gdGhpcy5fdGlsZWpzb24gfHwge307XG4gICAgICAgICAgICB2YXIgaWQgPSB0aWxlanNvbi5pZCB8fCAnJztcblxuICAgICAgICAgICAgdmFyIGhhc2ggPSAnIycgKyBpZCArICcvJyArXG4gICAgICAgICAgICAgICAgY2VudGVyLmxuZy50b0ZpeGVkKDMpICsgJy8nICtcbiAgICAgICAgICAgICAgICBjZW50ZXIubGF0LnRvRml4ZWQoMykgKyAnLycgK1xuICAgICAgICAgICAgICAgIHRoaXMuZ2V0Wm9vbSgpO1xuXG4gICAgICAgICAgICBmb3IgKHZhciBrZXkgaW4gZmVlZGJhY2suZGF0YSkge1xuICAgICAgICAgICAgICAgIGhhc2ggKz0gJy8nICsga2V5ICsgJz0nICsgZmVlZGJhY2suZGF0YVtrZXldO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxpbmsubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICBsaW5rW2ldLmhhc2ggPSBoYXNoO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfSxcblxuICAgIF9vbkxheWVyQWRkOiBmdW5jdGlvbihlKSB7XG4gICAgICAgIGlmICgnb24nIGluIGUubGF5ZXIpIHtcbiAgICAgICAgICAgIGUubGF5ZXIub24oJ3JlYWR5JywgdGhpcy5fb25MYXllclJlYWR5LCB0aGlzKTtcbiAgICAgICAgfVxuICAgICAgICB3aW5kb3cuc2V0VGltZW91dChMLmJpbmQodGhpcy5fdXBkYXRlTWFwRmVlZGJhY2tMaW5rLCB0aGlzKSwgMCk7IC8vIFVwZGF0ZSBhZnRlciBhdHRyaWJ1dGlvbiBjb250cm9sIHJlc2V0cyB0aGUgSFRNTC5cbiAgICB9LFxuXG4gICAgX29uTGF5ZXJSZW1vdmU6IGZ1bmN0aW9uKGUpIHtcbiAgICAgICAgaWYgKCdvbicgaW4gZS5sYXllcikge1xuICAgICAgICAgICAgZS5sYXllci5vZmYoJ3JlYWR5JywgdGhpcy5fb25MYXllclJlYWR5LCB0aGlzKTtcbiAgICAgICAgfVxuICAgICAgICB3aW5kb3cuc2V0VGltZW91dChMLmJpbmQodGhpcy5fdXBkYXRlTWFwRmVlZGJhY2tMaW5rLCB0aGlzKSwgMCk7IC8vIFVwZGF0ZSBhZnRlciBhdHRyaWJ1dGlvbiBjb250cm9sIHJlc2V0cyB0aGUgSFRNTC5cbiAgICB9LFxuXG4gICAgX29uTGF5ZXJSZWFkeTogZnVuY3Rpb24oZSkge1xuICAgICAgICB0aGlzLl91cGRhdGVMYXllcihlLnRhcmdldCk7XG4gICAgfSxcblxuICAgIF91cGRhdGVMYXllcjogZnVuY3Rpb24obGF5ZXIpIHtcbiAgICAgICAgaWYgKCFsYXllci5vcHRpb25zKSByZXR1cm47XG5cbiAgICAgICAgaWYgKHRoaXMuYXR0cmlidXRpb25Db250cm9sICYmIHRoaXMuX2xvYWRlZCAmJiBsYXllci5nZXRBdHRyaWJ1dGlvbikge1xuICAgICAgICAgICAgdGhpcy5hdHRyaWJ1dGlvbkNvbnRyb2wuYWRkQXR0cmlidXRpb24obGF5ZXIuZ2V0QXR0cmlidXRpb24oKSk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoIShMLnN0YW1wKGxheWVyKSBpbiB0aGlzLl96b29tQm91bmRMYXllcnMpICYmXG4gICAgICAgICAgICAgICAgKGxheWVyLm9wdGlvbnMubWF4Wm9vbSB8fCBsYXllci5vcHRpb25zLm1pblpvb20pKSB7XG4gICAgICAgICAgICB0aGlzLl96b29tQm91bmRMYXllcnNbTC5zdGFtcChsYXllcildID0gbGF5ZXI7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLl91cGRhdGVNYXBGZWVkYmFja0xpbmsoKTtcbiAgICAgICAgdGhpcy5fdXBkYXRlWm9vbUxldmVscygpO1xuICAgIH1cbn0pO1xuXG5tb2R1bGUuZXhwb3J0cy5NYXAgPSBMTWFwO1xuXG5tb2R1bGUuZXhwb3J0cy5tYXAgPSBmdW5jdGlvbihlbGVtZW50LCBfLCBvcHRpb25zKSB7XG4gICAgcmV0dXJuIG5ldyBMTWFwKGVsZW1lbnQsIF8sIG9wdGlvbnMpO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIGdlb2NvZGVyQ29udHJvbCA9IHJlcXVpcmUoJy4vZ2VvY29kZXJfY29udHJvbCcpLFxuICAgIGdyaWRDb250cm9sID0gcmVxdWlyZSgnLi9ncmlkX2NvbnRyb2wnKSxcbiAgICBmZWF0dXJlTGF5ZXIgPSByZXF1aXJlKCcuL2ZlYXR1cmVfbGF5ZXInKSxcbiAgICBsZWdlbmRDb250cm9sID0gcmVxdWlyZSgnLi9sZWdlbmRfY29udHJvbCcpLFxuICAgIHNoYXJlQ29udHJvbCA9IHJlcXVpcmUoJy4vc2hhcmVfY29udHJvbCcpLFxuICAgIHRpbGVMYXllciA9IHJlcXVpcmUoJy4vdGlsZV9sYXllcicpLFxuICAgIG1hcCA9IHJlcXVpcmUoJy4vbWFwJyksXG4gICAgZ3JpZExheWVyID0gcmVxdWlyZSgnLi9ncmlkX2xheWVyJyksXG4gICAgc3R5bGVMYXllciA9IHJlcXVpcmUoJy4vc3R5bGVfbGF5ZXInKTtcblxuTC5tYXBib3ggPSBtb2R1bGUuZXhwb3J0cyA9IHtcbiAgICBWRVJTSU9OOiByZXF1aXJlKCcuLi9wYWNrYWdlLmpzb24nKS52ZXJzaW9uLFxuICAgIGdlb2NvZGVyOiByZXF1aXJlKCcuL2dlb2NvZGVyJyksXG4gICAgbWFya2VyOiByZXF1aXJlKCcuL21hcmtlcicpLFxuICAgIHNpbXBsZXN0eWxlOiByZXF1aXJlKCcuL3NpbXBsZXN0eWxlJyksXG4gICAgdGlsZUxheWVyOiB0aWxlTGF5ZXIudGlsZUxheWVyLFxuICAgIFRpbGVMYXllcjogdGlsZUxheWVyLlRpbGVMYXllcixcbiAgICBzdHlsZUxheWVyOiBzdHlsZUxheWVyLnN0eWxlTGF5ZXIsXG4gICAgU3R5bGVMYXllcjogc3R5bGVMYXllci5TdHlsZUxheWVyLFxuICAgIHNoYXJlQ29udHJvbDogc2hhcmVDb250cm9sLnNoYXJlQ29udHJvbCxcbiAgICBTaGFyZUNvbnRyb2w6IHNoYXJlQ29udHJvbC5TaGFyZUNvbnRyb2wsXG4gICAgbGVnZW5kQ29udHJvbDogbGVnZW5kQ29udHJvbC5sZWdlbmRDb250cm9sLFxuICAgIExlZ2VuZENvbnRyb2w6IGxlZ2VuZENvbnRyb2wuTGVnZW5kQ29udHJvbCxcbiAgICBnZW9jb2RlckNvbnRyb2w6IGdlb2NvZGVyQ29udHJvbC5nZW9jb2RlckNvbnRyb2wsXG4gICAgR2VvY29kZXJDb250cm9sOiBnZW9jb2RlckNvbnRyb2wuR2VvY29kZXJDb250cm9sLFxuICAgIGdyaWRDb250cm9sOiBncmlkQ29udHJvbC5ncmlkQ29udHJvbCxcbiAgICBHcmlkQ29udHJvbDogZ3JpZENvbnRyb2wuR3JpZENvbnRyb2wsXG4gICAgZ3JpZExheWVyOiBncmlkTGF5ZXIuZ3JpZExheWVyLFxuICAgIEdyaWRMYXllcjogZ3JpZExheWVyLkdyaWRMYXllcixcbiAgICBmZWF0dXJlTGF5ZXI6IGZlYXR1cmVMYXllci5mZWF0dXJlTGF5ZXIsXG4gICAgRmVhdHVyZUxheWVyOiBmZWF0dXJlTGF5ZXIuRmVhdHVyZUxheWVyLFxuICAgIG1hcDogbWFwLm1hcCxcbiAgICBNYXA6IG1hcC5NYXAsXG4gICAgY29uZmlnOiByZXF1aXJlKCcuL2NvbmZpZycpLFxuICAgIHNhbml0aXplOiByZXF1aXJlKCdzYW5pdGl6ZS1jYWphJyksXG4gICAgdGVtcGxhdGU6IHJlcXVpcmUoJ211c3RhY2hlJykudG9faHRtbCxcbiAgICBmZWVkYmFjazogcmVxdWlyZSgnLi9mZWVkYmFjaycpXG59O1xuXG5cbi8vIEhhcmRjb2RlIGltYWdlIHBhdGgsIGJlY2F1c2UgTGVhZmxldCdzIGF1dG9kZXRlY3Rpb25cbi8vIGZhaWxzLCBiZWNhdXNlIG1hcGJveC5qcyBpcyBub3QgbmFtZWQgbGVhZmxldC5qc1xud2luZG93LkwuSWNvbi5EZWZhdWx0LmltYWdlUGF0aCA9XG4gICAgLy8gRGV0ZWN0IGJhZC1uZXdzIHByb3RvY29scyBsaWtlIGZpbGU6Ly8gYW5kIGhhcmRjb2RlXG4gICAgLy8gdG8gaHR0cHMgaWYgdGhleSdyZSBkZXRlY3RlZC5cbiAgICAoKGRvY3VtZW50LmxvY2F0aW9uLnByb3RvY29sID09PSAnaHR0cHM6JyB8fFxuICAgIGRvY3VtZW50LmxvY2F0aW9uLnByb3RvY29sID09PSAnaHR0cDonKSA/ICcnIDogJ2h0dHBzOicpICtcbiAgICAnLy9hcGkudGlsZXMubWFwYm94LmNvbS9tYXBib3guanMvJyArICd2JyArXG4gICAgcmVxdWlyZSgnLi4vcGFja2FnZS5qc29uJykudmVyc2lvbiArICcvaW1hZ2VzLyc7XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciBNYXBib3hMb2dvQ29udHJvbCA9IEwuQ29udHJvbC5leHRlbmQoe1xuXG4gICAgb3B0aW9uczoge1xuICAgICAgICBwb3NpdGlvbjogJ2JvdHRvbWxlZnQnXG4gICAgfSxcblxuICAgIGluaXRpYWxpemU6IGZ1bmN0aW9uKG9wdGlvbnMpIHtcbiAgICAgICAgTC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xuICAgIH0sXG5cbiAgICBvbkFkZDogZnVuY3Rpb24oKSB7XG4gICAgICAgIHRoaXMuX2NvbnRhaW5lciA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdtYXBib3gtbG9nbycpO1xuICAgICAgICByZXR1cm4gdGhpcy5fY29udGFpbmVyO1xuICAgIH0sXG5cbiAgICBfc2V0VGlsZUpTT046IGZ1bmN0aW9uKGpzb24pIHtcbiAgICAgICAgLy8gQ2hlY2sgaWYgYWNjb3VudCByZWZlcmVuY2VkIGJ5IHRoZSBhY2Nlc3NUb2tlblxuICAgICAgICAvLyBpcyBhc3Njb2NpYXRlZCB3aXRoIHRoZSBNYXBib3ggTG9nb1xuICAgICAgICAvLyBhcyBkZXRlcm1pbmVkIGJ5IG1hcGJveC1tYXBzLlxuICAgICAgICBpZiAoanNvbi5tYXBib3hfbG9nbykge1xuICAgICAgICAgICAgTC5Eb21VdGlsLmFkZENsYXNzKHRoaXMuX2NvbnRhaW5lciwgJ21hcGJveC1sb2dvLXRydWUnKTtcbiAgICAgICAgfVxuICAgIH1cbn0pO1xuXG5tb2R1bGUuZXhwb3J0cy5NYXBib3hMb2dvQ29udHJvbCA9IE1hcGJveExvZ29Db250cm9sO1xuXG5tb2R1bGUuZXhwb3J0cy5tYXBib3hMb2dvQ29udHJvbCA9IGZ1bmN0aW9uKG9wdGlvbnMpIHtcbiAgICByZXR1cm4gbmV3IE1hcGJveExvZ29Db250cm9sKG9wdGlvbnMpO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIGZvcm1hdF91cmwgPSByZXF1aXJlKCcuL2Zvcm1hdF91cmwnKSxcbiAgICB1dGlsID0gcmVxdWlyZSgnLi91dGlsJyksXG4gICAgc2FuaXRpemUgPSByZXF1aXJlKCdzYW5pdGl6ZS1jYWphJyk7XG5cbi8vIG1hcGJveC1yZWxhdGVkIG1hcmtlcnMgZnVuY3Rpb25hbGl0eVxuLy8gcHJvdmlkZSBhbiBpY29uIGZyb20gbWFwYm94J3Mgc2ltcGxlLXN0eWxlIHNwZWMgYW5kIGhvc3RlZCBtYXJrZXJzXG4vLyBzZXJ2aWNlXG5mdW5jdGlvbiBpY29uKGZwLCBvcHRpb25zKSB7XG4gICAgZnAgPSBmcCB8fCB7fTtcblxuICAgIHZhciBzaXplcyA9IHtcbiAgICAgICAgICAgIHNtYWxsOiBbMjAsIDUwXSxcbiAgICAgICAgICAgIG1lZGl1bTogWzMwLCA3MF0sXG4gICAgICAgICAgICBsYXJnZTogWzM1LCA5MF1cbiAgICAgICAgfSxcbiAgICAgICAgc2l6ZSA9IGZwWydtYXJrZXItc2l6ZSddIHx8ICdtZWRpdW0nLFxuICAgICAgICBzeW1ib2wgPSAoJ21hcmtlci1zeW1ib2wnIGluIGZwICYmIGZwWydtYXJrZXItc3ltYm9sJ10gIT09ICcnKSA/ICctJyArIGZwWydtYXJrZXItc3ltYm9sJ10gOiAnJyxcbiAgICAgICAgY29sb3IgPSAoZnBbJ21hcmtlci1jb2xvciddIHx8ICc3ZTdlN2UnKS5yZXBsYWNlKCcjJywgJycpO1xuXG4gICAgcmV0dXJuIEwuaWNvbih7XG4gICAgICAgIGljb25Vcmw6IGZvcm1hdF91cmwoJy92NC9tYXJrZXIvJyArXG4gICAgICAgICAgICAncGluLScgKyBzaXplLmNoYXJBdCgwKSArIHN5bWJvbCArICcrJyArIGNvbG9yICtcbiAgICAgICAgICAgIC8vIGRldGVjdCBhbmQgdXNlIHJldGluYSBtYXJrZXJzLCB3aGljaCBhcmUgeDIgcmVzb2x1dGlvblxuICAgICAgICAgICAgKEwuQnJvd3Nlci5yZXRpbmEgPyAnQDJ4JyA6ICcnKSArICcucG5nJywgb3B0aW9ucyAmJiBvcHRpb25zLmFjY2Vzc1Rva2VuKSxcbiAgICAgICAgaWNvblNpemU6IHNpemVzW3NpemVdLFxuICAgICAgICBpY29uQW5jaG9yOiBbc2l6ZXNbc2l6ZV1bMF0gLyAyLCBzaXplc1tzaXplXVsxXSAvIDJdLFxuICAgICAgICBwb3B1cEFuY2hvcjogWzAsIC1zaXplc1tzaXplXVsxXSAvIDJdXG4gICAgfSk7XG59XG5cbi8vIGEgZmFjdG9yeSB0aGF0IHByb3ZpZGVzIG1hcmtlcnMgZm9yIExlYWZsZXQgZnJvbSBNYXBib3gnc1xuLy8gW3NpbXBsZS1zdHlsZSBzcGVjaWZpY2F0aW9uXShodHRwczovL2dpdGh1Yi5jb20vbWFwYm94L3NpbXBsZXN0eWxlLXNwZWMpXG4vLyBhbmQgW01hcmtlcnMgQVBJXShodHRwOi8vbWFwYm94LmNvbS9kZXZlbG9wZXJzL2FwaS8jbWFya2VycykuXG5mdW5jdGlvbiBzdHlsZShmLCBsYXRsb24sIG9wdGlvbnMpIHtcbiAgICByZXR1cm4gTC5tYXJrZXIobGF0bG9uLCB7XG4gICAgICAgIGljb246IGljb24oZi5wcm9wZXJ0aWVzLCBvcHRpb25zKSxcbiAgICAgICAgdGl0bGU6IHV0aWwuc3RyaXBfdGFncyhcbiAgICAgICAgICAgIHNhbml0aXplKChmLnByb3BlcnRpZXMgJiYgZi5wcm9wZXJ0aWVzLnRpdGxlKSB8fCAnJykpXG4gICAgfSk7XG59XG5cbi8vIFNhbml0aXplIGFuZCBmb3JtYXQgcHJvcGVydGllcyBvZiBhIEdlb0pTT04gRmVhdHVyZSBvYmplY3QgaW4gb3JkZXJcbi8vIHRvIGZvcm0gdGhlIEhUTUwgc3RyaW5nIHVzZWQgYXMgdGhlIGFyZ3VtZW50IGZvciBgTC5jcmVhdGVQb3B1cGBcbmZ1bmN0aW9uIGNyZWF0ZVBvcHVwKGYsIHNhbml0aXplcikge1xuICAgIGlmICghZiB8fCAhZi5wcm9wZXJ0aWVzKSByZXR1cm4gJyc7XG4gICAgdmFyIHBvcHVwID0gJyc7XG5cbiAgICBpZiAoZi5wcm9wZXJ0aWVzLnRpdGxlKSB7XG4gICAgICAgIHBvcHVwICs9ICc8ZGl2IGNsYXNzPVwibWFya2VyLXRpdGxlXCI+JyArIGYucHJvcGVydGllcy50aXRsZSArICc8L2Rpdj4nO1xuICAgIH1cblxuICAgIGlmIChmLnByb3BlcnRpZXMuZGVzY3JpcHRpb24pIHtcbiAgICAgICAgcG9wdXAgKz0gJzxkaXYgY2xhc3M9XCJtYXJrZXItZGVzY3JpcHRpb25cIj4nICsgZi5wcm9wZXJ0aWVzLmRlc2NyaXB0aW9uICsgJzwvZGl2Pic7XG4gICAgfVxuXG4gICAgcmV0dXJuIChzYW5pdGl6ZXIgfHwgc2FuaXRpemUpKHBvcHVwKTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgaWNvbjogaWNvbixcbiAgICBzdHlsZTogc3R5bGUsXG4gICAgY3JlYXRlUG9wdXA6IGNyZWF0ZVBvcHVwXG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgY29yc2xpdGUgPSByZXF1aXJlKCdjb3JzbGl0ZScpLFxuICAgIHN0cmljdCA9IHJlcXVpcmUoJy4vdXRpbCcpLnN0cmljdCxcbiAgICBjb25maWcgPSByZXF1aXJlKCcuL2NvbmZpZycpO1xuXG52YXIgcHJvdG9jb2wgPSAvXihodHRwcz86KT8oPz1cXC9cXC8oLnxhcGkpXFwudGlsZXNcXC5tYXBib3hcXC5jb21cXC8pLztcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbih1cmwsIGNhbGxiYWNrKSB7XG4gICAgc3RyaWN0KHVybCwgJ3N0cmluZycpO1xuICAgIHN0cmljdChjYWxsYmFjaywgJ2Z1bmN0aW9uJyk7XG5cbiAgICB1cmwgPSB1cmwucmVwbGFjZShwcm90b2NvbCwgZnVuY3Rpb24obWF0Y2gsIHByb3RvY29sKSB7XG4gICAgICAgIGlmICghKCd3aXRoQ3JlZGVudGlhbHMnIGluIG5ldyB3aW5kb3cuWE1MSHR0cFJlcXVlc3QoKSkpIHtcbiAgICAgICAgICAgIC8vIFhEb21haW5SZXF1ZXN0IGluIHVzZTsgZG9lc24ndCBzdXBwb3J0IGNyb3NzLXByb3RvY29sIHJlcXVlc3RzXG4gICAgICAgICAgICByZXR1cm4gZG9jdW1lbnQubG9jYXRpb24ucHJvdG9jb2w7XG4gICAgICAgIH0gZWxzZSBpZiAocHJvdG9jb2wgPT09ICdodHRwczonIHx8IGRvY3VtZW50LmxvY2F0aW9uLnByb3RvY29sID09PSAnaHR0cHM6JyB8fCBjb25maWcuRk9SQ0VfSFRUUFMpIHtcbiAgICAgICAgICAgIHJldHVybiAnaHR0cHM6JztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJldHVybiAnaHR0cDonO1xuICAgICAgICB9XG4gICAgfSk7XG5cbiAgICBmdW5jdGlvbiBvbmxvYWQoZXJyLCByZXNwKSB7XG4gICAgICAgIGlmICghZXJyICYmIHJlc3ApIHtcbiAgICAgICAgICAgIHJlc3AgPSBKU09OLnBhcnNlKHJlc3AucmVzcG9uc2VUZXh0KTtcbiAgICAgICAgfVxuICAgICAgICBjYWxsYmFjayhlcnIsIHJlc3ApO1xuICAgIH1cblxuICAgIHJldHVybiBjb3JzbGl0ZSh1cmwsIG9ubG9hZCk7XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgZm9ybWF0X3VybCA9IHJlcXVpcmUoJy4vZm9ybWF0X3VybCcpO1xuXG52YXIgU2hhcmVDb250cm9sID0gTC5Db250cm9sLmV4dGVuZCh7XG4gICAgaW5jbHVkZXM6IFtyZXF1aXJlKCcuL2xvYWRfdGlsZWpzb24nKV0sXG5cbiAgICBvcHRpb25zOiB7XG4gICAgICAgIHBvc2l0aW9uOiAndG9wbGVmdCcsXG4gICAgICAgIHVybDogJydcbiAgICB9LFxuXG4gICAgaW5pdGlhbGl6ZTogZnVuY3Rpb24oXywgb3B0aW9ucykge1xuICAgICAgICBMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XG4gICAgICAgIHRoaXMuX2xvYWRUaWxlSlNPTihfKTtcbiAgICB9LFxuXG4gICAgX3NldFRpbGVKU09OOiBmdW5jdGlvbihqc29uKSB7XG4gICAgICAgIHRoaXMuX3RpbGVqc29uID0ganNvbjtcbiAgICB9LFxuXG4gICAgb25BZGQ6IGZ1bmN0aW9uKG1hcCkge1xuICAgICAgICB0aGlzLl9tYXAgPSBtYXA7XG5cbiAgICAgICAgdmFyIGNvbnRhaW5lciA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LWNvbnRyb2wtbWFwYm94LXNoYXJlIGxlYWZsZXQtYmFyJyk7XG4gICAgICAgIHZhciBsaW5rID0gTC5Eb21VdGlsLmNyZWF0ZSgnYScsICdtYXBib3gtc2hhcmUgbWFwYm94LWljb24gbWFwYm94LWljb24tc2hhcmUnLCBjb250YWluZXIpO1xuICAgICAgICBsaW5rLmhyZWYgPSAnIyc7XG5cbiAgICAgICAgdGhpcy5fbW9kYWwgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCAnbWFwYm94LW1vZGFsJywgdGhpcy5fbWFwLl9jb250YWluZXIpO1xuICAgICAgICB0aGlzLl9tYXNrID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgJ21hcGJveC1tb2RhbC1tYXNrJywgdGhpcy5fbW9kYWwpO1xuICAgICAgICB0aGlzLl9jb250ZW50ID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgJ21hcGJveC1tb2RhbC1jb250ZW50JywgdGhpcy5fbW9kYWwpO1xuXG4gICAgICAgIEwuRG9tRXZlbnQuYWRkTGlzdGVuZXIobGluaywgJ2NsaWNrJywgdGhpcy5fc2hhcmVDbGljaywgdGhpcyk7XG4gICAgICAgIEwuRG9tRXZlbnQuZGlzYWJsZUNsaWNrUHJvcGFnYXRpb24oY29udGFpbmVyKTtcblxuICAgICAgICB0aGlzLl9tYXAub24oJ21vdXNlZG93bicsIHRoaXMuX2NsaWNrT3V0LCB0aGlzKTtcblxuICAgICAgICByZXR1cm4gY29udGFpbmVyO1xuICAgIH0sXG5cbiAgICBfY2xpY2tPdXQ6IGZ1bmN0aW9uKGUpIHtcbiAgICAgICAgaWYgKHRoaXMuX3NoYXJpbmcpIHtcbiAgICAgICAgICAgIEwuRG9tRXZlbnQucHJldmVudERlZmF1bHQoZSk7XG4gICAgICAgICAgICBMLkRvbVV0aWwucmVtb3ZlQ2xhc3ModGhpcy5fbW9kYWwsICdhY3RpdmUnKTtcbiAgICAgICAgICAgIHRoaXMuX2NvbnRlbnQuaW5uZXJIVE1MID0gJyc7XG4gICAgICAgICAgICB0aGlzLl9zaGFyaW5nID0gbnVsbDtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICBfc2hhcmVDbGljazogZnVuY3Rpb24oZSkge1xuICAgICAgICBMLkRvbUV2ZW50LnN0b3AoZSk7XG4gICAgICAgIGlmICh0aGlzLl9zaGFyaW5nKSByZXR1cm4gdGhpcy5fY2xpY2tPdXQoZSk7XG5cbiAgICAgICAgdmFyIHRpbGVqc29uID0gdGhpcy5fdGlsZWpzb24gfHwgdGhpcy5fbWFwLl90aWxlanNvbiB8fCB7fSxcbiAgICAgICAgICAgIHVybCA9IGVuY29kZVVSSUNvbXBvbmVudCh0aGlzLm9wdGlvbnMudXJsIHx8IHRpbGVqc29uLndlYnBhZ2UgfHwgd2luZG93LmxvY2F0aW9uKSxcbiAgICAgICAgICAgIG5hbWUgPSBlbmNvZGVVUklDb21wb25lbnQodGlsZWpzb24ubmFtZSksXG4gICAgICAgICAgICBpbWFnZSA9IGZvcm1hdF91cmwoJy92NC8nICsgdGlsZWpzb24uaWQgKyAnLycgKyB0aGlzLl9tYXAuZ2V0Q2VudGVyKCkubG5nICsgJywnICsgdGhpcy5fbWFwLmdldENlbnRlcigpLmxhdCArICcsJyArIHRoaXMuX21hcC5nZXRab29tKCkgKyAnLzYwMHg2MDAucG5nJywgdGhpcy5vcHRpb25zLmFjY2Vzc1Rva2VuKSxcbiAgICAgICAgICAgIGVtYmVkID0gZm9ybWF0X3VybCgnL3Y0LycgKyB0aWxlanNvbi5pZCArICcuaHRtbCcsIHRoaXMub3B0aW9ucy5hY2Nlc3NUb2tlbiksXG4gICAgICAgICAgICB0d2l0dGVyVVJMID0gJy8vdHdpdHRlci5jb20vaW50ZW50L3R3ZWV0P3N0YXR1cz0nICsgbmFtZSArICcgJyArIHVybCxcbiAgICAgICAgICAgIGZhY2Vib29rVVJMID0gJy8vd3d3LmZhY2Vib29rLmNvbS9zaGFyZXIucGhwP3U9JyArIHVybCArICcmdD0nICsgbmFtZSxcbiAgICAgICAgICAgIHBpbnRlcmVzdFVSTCA9ICcvL3d3dy5waW50ZXJlc3QuY29tL3Bpbi9jcmVhdGUvYnV0dG9uLz91cmw9JyArIHVybCArICcmbWVkaWE9JyArIGltYWdlICsgJyZkZXNjcmlwdGlvbj0nICsgbmFtZSxcbiAgICAgICAgICAgIGVtYmVkVmFsdWUgPSAnPGlmcmFtZSB3aWR0aD1cIjEwMCVcIiBoZWlnaHQ9XCI1MDBweFwiIGZyYW1lQm9yZGVyPVwiMFwiIHNyYz1cIicgKyBlbWJlZCArICdcIj48L2lmcmFtZT4nLFxuICAgICAgICAgICAgZW1iZWRMYWJlbCA9ICdDb3B5IGFuZCBwYXN0ZSB0aGlzIDxzdHJvbmc+SFRNTCBjb2RlPC9zdHJvbmc+IGludG8gZG9jdW1lbnRzIHRvIGVtYmVkIHRoaXMgbWFwIG9uIHdlYiBwYWdlcy4nO1xuXG4gICAgICAgIGZ1bmN0aW9uIGNyZWF0ZVNoYXJlQnV0dG9uKGJ1dHRvbkNsYXNzLCBocmVmLCBzb2NpYWxNZWRpYU5hbWUpIHtcbiAgICAgICAgICAgIHZhciBlbGVtID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnYScpO1xuICAgICAgICAgICAgZWxlbS5zZXRBdHRyaWJ1dGUoJ2NsYXNzJywgYnV0dG9uQ2xhc3MpO1xuICAgICAgICAgICAgZWxlbS5zZXRBdHRyaWJ1dGUoJ2hyZWYnLCBocmVmKTtcbiAgICAgICAgICAgIGVsZW0uc2V0QXR0cmlidXRlKCd0YXJnZXQnLCAnX2JsYW5rJyk7XG4gICAgICAgICAgICBzb2NpYWxNZWRpYU5hbWUgPSBkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZShzb2NpYWxNZWRpYU5hbWUpO1xuICAgICAgICAgICAgZWxlbS5hcHBlbmRDaGlsZChzb2NpYWxNZWRpYU5hbWUpO1xuXG4gICAgICAgICAgICByZXR1cm4gZWxlbTtcbiAgICAgICAgfVxuXG4gICAgICAgIEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9tb2RhbCwgJ2FjdGl2ZScpO1xuXG4gICAgICAgIHRoaXMuX3NoYXJpbmcgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCAnbWFwYm94LW1vZGFsLWJvZHknLCB0aGlzLl9jb250ZW50KTtcblxuICAgICAgICB2YXIgdHdpdHRlckJ1dHRvbiA9IGNyZWF0ZVNoYXJlQnV0dG9uKCdtYXBib3gtYnV0dG9uIG1hcGJveC1idXR0b24taWNvbiBtYXBib3gtaWNvbi10d2l0dGVyJywgdHdpdHRlclVSTCwgJ1R3aXR0ZXInKTtcbiAgICAgICAgdmFyIGZhY2Vib29rQnV0dG9uID0gY3JlYXRlU2hhcmVCdXR0b24oJ21hcGJveC1idXR0b24gbWFwYm94LWJ1dHRvbi1pY29uIG1hcGJveC1pY29uLWZhY2Vib29rJywgZmFjZWJvb2tVUkwsICdGYWNlYm9vaycpO1xuICAgICAgICB2YXIgcGludGVyZXN0QnV0dG9uID0gY3JlYXRlU2hhcmVCdXR0b24oJ21hcGJveC1idXR0b24gbWFwYm94LWJ1dHRvbi1pY29uIG1hcGJveC1pY29uLXBpbnRlcmVzdCcsIHBpbnRlcmVzdFVSTCwgJ1BpbnRlcmVzdCcpO1xuXG4gICAgICAgIHZhciBzaGFyZUhlYWRlciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2gzJyk7XG4gICAgICAgIHZhciBzaGFyZVRleHQgPSBkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZSgnU2hhcmUgdGhpcyBtYXAnKTtcbiAgICAgICAgc2hhcmVIZWFkZXIuYXBwZW5kQ2hpbGQoc2hhcmVUZXh0KTtcblxuICAgICAgICB2YXIgc2hhcmVCdXR0b25zID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgICAgIHNoYXJlQnV0dG9ucy5zZXRBdHRyaWJ1dGUoJ2NsYXNzJywgJ21hcGJveC1zaGFyZS1idXR0b25zJyk7XG4gICAgICAgIHNoYXJlQnV0dG9ucy5hcHBlbmRDaGlsZChmYWNlYm9va0J1dHRvbik7XG4gICAgICAgIHNoYXJlQnV0dG9ucy5hcHBlbmRDaGlsZCh0d2l0dGVyQnV0dG9uKTtcbiAgICAgICAgc2hhcmVCdXR0b25zLmFwcGVuZENoaWxkKHBpbnRlcmVzdEJ1dHRvbik7XG5cbiAgICAgICAgdGhpcy5fc2hhcmluZy5hcHBlbmRDaGlsZChzaGFyZUhlYWRlcik7XG4gICAgICAgIHRoaXMuX3NoYXJpbmcuYXBwZW5kQ2hpbGQoc2hhcmVCdXR0b25zKTtcblxuICAgICAgICB2YXIgaW5wdXQgPSBMLkRvbVV0aWwuY3JlYXRlKCdpbnB1dCcsICdtYXBib3gtZW1iZWQnLCB0aGlzLl9zaGFyaW5nKTtcbiAgICAgICAgaW5wdXQudHlwZSA9ICd0ZXh0JztcbiAgICAgICAgaW5wdXQudmFsdWUgPSBlbWJlZFZhbHVlO1xuXG4gICAgICAgIHZhciBsYWJlbCA9IEwuRG9tVXRpbC5jcmVhdGUoJ2xhYmVsJywgJ21hcGJveC1lbWJlZC1kZXNjcmlwdGlvbicsIHRoaXMuX3NoYXJpbmcpO1xuICAgICAgICBsYWJlbC5pbm5lckhUTUwgPSBlbWJlZExhYmVsO1xuXG4gICAgICAgIHZhciBjbG9zZSA9IEwuRG9tVXRpbC5jcmVhdGUoJ2EnLCAnbGVhZmxldC1wb3B1cC1jbG9zZS1idXR0b24nLCB0aGlzLl9zaGFyaW5nKTtcbiAgICAgICAgY2xvc2UuaHJlZiA9ICcjJztcblxuICAgICAgICBMLkRvbUV2ZW50LmRpc2FibGVDbGlja1Byb3BhZ2F0aW9uKHRoaXMuX3NoYXJpbmcpO1xuICAgICAgICBMLkRvbUV2ZW50LmFkZExpc3RlbmVyKGNsb3NlLCAnY2xpY2snLCB0aGlzLl9jbGlja091dCwgdGhpcyk7XG4gICAgICAgIEwuRG9tRXZlbnQuYWRkTGlzdGVuZXIoaW5wdXQsICdjbGljaycsIGZ1bmN0aW9uKGUpIHtcbiAgICAgICAgICAgIGUudGFyZ2V0LmZvY3VzKCk7XG4gICAgICAgICAgICBlLnRhcmdldC5zZWxlY3QoKTtcbiAgICAgICAgfSk7XG4gICAgfVxufSk7XG5cbm1vZHVsZS5leHBvcnRzLlNoYXJlQ29udHJvbCA9IFNoYXJlQ29udHJvbDtcblxubW9kdWxlLmV4cG9ydHMuc2hhcmVDb250cm9sID0gZnVuY3Rpb24oXywgb3B0aW9ucykge1xuICAgIHJldHVybiBuZXcgU2hhcmVDb250cm9sKF8sIG9wdGlvbnMpO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxuLy8gYW4gaW1wbGVtZW50YXRpb24gb2YgdGhlIHNpbXBsZXN0eWxlIHNwZWMgZm9yIHBvbHlnb24gYW5kIGxpbmVzdHJpbmcgZmVhdHVyZXNcbi8vIGh0dHBzOi8vZ2l0aHViLmNvbS9tYXBib3gvc2ltcGxlc3R5bGUtc3BlY1xudmFyIGRlZmF1bHRzID0ge1xuICAgIHN0cm9rZTogJyM1NTU1NTUnLFxuICAgICdzdHJva2Utd2lkdGgnOiAyLFxuICAgICdzdHJva2Utb3BhY2l0eSc6IDEsXG4gICAgZmlsbDogJyM1NTU1NTUnLFxuICAgICdmaWxsLW9wYWNpdHknOiAwLjVcbn07XG5cbnZhciBtYXBwaW5nID0gW1xuICAgIFsnc3Ryb2tlJywgJ2NvbG9yJ10sXG4gICAgWydzdHJva2Utd2lkdGgnLCAnd2VpZ2h0J10sXG4gICAgWydzdHJva2Utb3BhY2l0eScsICdvcGFjaXR5J10sXG4gICAgWydmaWxsJywgJ2ZpbGxDb2xvciddLFxuICAgIFsnZmlsbC1vcGFjaXR5JywgJ2ZpbGxPcGFjaXR5J11cbl07XG5cbmZ1bmN0aW9uIGZhbGxiYWNrKGEsIGIpIHtcbiAgICB2YXIgYyA9IHt9O1xuICAgIGZvciAodmFyIGsgaW4gYikge1xuICAgICAgICBpZiAoYVtrXSA9PT0gdW5kZWZpbmVkKSBjW2tdID0gYltrXTtcbiAgICAgICAgZWxzZSBjW2tdID0gYVtrXTtcbiAgICB9XG4gICAgcmV0dXJuIGM7XG59XG5cbmZ1bmN0aW9uIHJlbWFwKGEpIHtcbiAgICB2YXIgZCA9IHt9O1xuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbWFwcGluZy5sZW5ndGg7IGkrKykge1xuICAgICAgICBkW21hcHBpbmdbaV1bMV1dID0gYVttYXBwaW5nW2ldWzBdXTtcbiAgICB9XG4gICAgcmV0dXJuIGQ7XG59XG5cbmZ1bmN0aW9uIHN0eWxlKGZlYXR1cmUpIHtcbiAgICByZXR1cm4gcmVtYXAoZmFsbGJhY2soZmVhdHVyZS5wcm9wZXJ0aWVzIHx8IHt9LCBkZWZhdWx0cykpO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgICBzdHlsZTogc3R5bGUsXG4gICAgZGVmYXVsdHM6IGRlZmF1bHRzXG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgdXRpbCA9IHJlcXVpcmUoJy4vdXRpbCcpO1xudmFyIGZvcm1hdF91cmwgPSByZXF1aXJlKCcuL2Zvcm1hdF91cmwnKTtcbnZhciByZXF1ZXN0ID0gcmVxdWlyZSgnLi9yZXF1ZXN0Jyk7XG5cbnZhciBTdHlsZUxheWVyID0gTC5UaWxlTGF5ZXIuZXh0ZW5kKHtcblxuICAgIG9wdGlvbnM6IHtcbiAgICAgICAgc2FuaXRpemVyOiByZXF1aXJlKCdzYW5pdGl6ZS1jYWphJylcbiAgICB9LFxuXG4gICAgaW5pdGlhbGl6ZTogZnVuY3Rpb24oXywgb3B0aW9ucykge1xuICAgICAgICBMLlRpbGVMYXllci5wcm90b3R5cGUuaW5pdGlhbGl6ZS5jYWxsKHRoaXMsIHVuZGVmaW5lZCwgTC5leHRlbmQoe30sIG9wdGlvbnMsIHtcbiAgICAgICAgICAgIHRpbGVTaXplOiA1MTIsXG4gICAgICAgICAgICB6b29tT2Zmc2V0OiAtMSxcbiAgICAgICAgICAgIG1pbk5hdGl2ZVpvb206IDAsXG4gICAgICAgICAgICB0bXM6IGZhbHNlXG4gICAgICAgIH0pKTtcbiAgICAgICAgdGhpcy5fdXJsID0gdGhpcy5fZm9ybWF0VGlsZVVSTChfKTtcbiAgICAgICAgdGhpcy5fZ2V0QXR0cmlidXRpb24oXyk7XG4gICAgfSxcblxuICAgIF9nZXRBdHRyaWJ1dGlvbjogZnVuY3Rpb24oXykge1xuICAgICAgICB2YXIgc3R5bGVVUkwgPSBmb3JtYXRfdXJsLnN0eWxlKF8sIHRoaXMub3B0aW9ucyAmJiB0aGlzLm9wdGlvbnMuYWNjZXNzVG9rZW4pO1xuICAgICAgICByZXF1ZXN0KHN0eWxlVVJMLCBMLmJpbmQoZnVuY3Rpb24oZXJyLCBzdHlsZSkge1xuICAgICAgICAgICAgaWYgKGVycikge1xuICAgICAgICAgICAgICAgIHV0aWwubG9nKCdjb3VsZCBub3QgbG9hZCBNYXBib3ggc3R5bGUgYXQgJyArIHN0eWxlVVJMKTtcbiAgICAgICAgICAgICAgICB0aGlzLmZpcmUoJ2Vycm9yJywge2Vycm9yOiBlcnJ9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHZhciBzb3VyY2VzID0gW107XG4gICAgICAgICAgICBmb3IgKHZhciBpZCBpbiBzdHlsZS5zb3VyY2VzKSB7XG4gICAgICAgICAgICAgICAgdmFyIHNvdXJjZSA9IHN0eWxlLnNvdXJjZXNbaWRdLnVybC5zcGxpdCgnbWFwYm94Oi8vJylbMV07XG4gICAgICAgICAgICAgICAgc291cmNlcy5wdXNoKHNvdXJjZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXF1ZXN0KGZvcm1hdF91cmwudGlsZUpTT04oc291cmNlcy5qb2luKCksIHRoaXMub3B0aW9ucy5hY2Nlc3NUb2tlbiksIEwuYmluZChmdW5jdGlvbihlcnIsIGpzb24pIHtcbiAgICAgICAgICAgICAgICBpZiAoZXJyKSB7XG4gICAgICAgICAgICAgICAgICAgIHV0aWwubG9nKCdjb3VsZCBub3QgbG9hZCBUaWxlSlNPTiBhdCAnICsgXyk7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZmlyZSgnZXJyb3InLCB7ZXJyb3I6IGVycn0pO1xuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoanNvbikge1xuICAgICAgICAgICAgICAgICAgICB1dGlsLnN0cmljdChqc29uLCAnb2JqZWN0Jyk7XG5cbiAgICAgICAgICAgICAgICAgICAgdGhpcy5vcHRpb25zLmF0dHJpYnV0aW9uID0gdGhpcy5vcHRpb25zLnNhbml0aXplcihqc29uLmF0dHJpYnV0aW9uKTtcblxuICAgICAgICAgICAgICAgICAgICB0aGlzLl90aWxlanNvbiA9IGpzb247XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZmlyZSgncmVhZHknKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LCB0aGlzKSk7XG4gICAgICAgIH0sIHRoaXMpKTtcbiAgICB9LFxuXG4gICAgLy8gZGlzYWJsZSB0aGUgc2V0VXJsIGZ1bmN0aW9uLCB3aGljaCBpcyBub3QgYXZhaWxhYmxlIG9uIG1hcGJveCB0aWxlbGF5ZXJzXG4gICAgc2V0VXJsOiBudWxsLFxuXG4gICAgX2Zvcm1hdFRpbGVVUkw6IGZ1bmN0aW9uKHN0eWxlKSB7XG4gICAgICAgIGlmICh0eXBlb2Ygc3R5bGUgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICBpZiAoc3R5bGUuaW5kZXhPZignbWFwYm94Oi8vc3R5bGVzLycpID09PSAtMSkge1xuICAgICAgICAgICAgICAgIHV0aWwubG9nKCdJbmNvcnJlY3RseSBmb3JtYXR0ZWQgTWFwYm94IHN0eWxlIGF0ICcgKyBzdHlsZSk7XG4gICAgICAgICAgICAgICAgdGhpcy5maXJlKCdlcnJvcicpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFyIG93bmVySURTdHlsZSA9IHN0eWxlLnNwbGl0KCdtYXBib3g6Ly9zdHlsZXMvJylbMV07XG4gICAgICAgICAgICByZXR1cm4gZm9ybWF0X3VybCgnL3N0eWxlcy92MS8nICsgb3duZXJJRFN0eWxlICsgJy90aWxlcy97en0ve3h9L3t5fXtyfScsIHRoaXMub3B0aW9ucy5hY2Nlc3NUb2tlbik7XG4gICAgICAgIH0gZWxzZSBpZiAodHlwZW9mIHN0eWxlID09PSAnb2JqZWN0Jykge1xuICAgICAgICAgICAgcmV0dXJuIGZvcm1hdF91cmwoJy9zdHlsZXMvdjEvJyArIHN0eWxlLm93bmVyICsgJy8nICsgc3R5bGUuaWQgKyAnL3RpbGVzL3t6fS97eH0ve3l9e3J9JywgdGhpcy5vcHRpb25zLmFjY2Vzc1Rva2VuKTtcbiAgICAgICAgfVxuICAgIH1cbn0pO1xuXG5tb2R1bGUuZXhwb3J0cy5TdHlsZUxheWVyID0gU3R5bGVMYXllcjtcblxubW9kdWxlLmV4cG9ydHMuc3R5bGVMYXllciA9IGZ1bmN0aW9uKF8sIG9wdGlvbnMpIHtcbiAgICByZXR1cm4gbmV3IFN0eWxlTGF5ZXIoXywgb3B0aW9ucyk7XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgdXRpbCA9IHJlcXVpcmUoJy4vdXRpbCcpO1xudmFyIGZvcm1hdFBhdHRlcm4gPSAvXFwuKCg/OnBuZ3xqcGcpXFxkKikoPz0kfFxcPykvO1xuXG52YXIgVGlsZUxheWVyID0gTC5UaWxlTGF5ZXIuZXh0ZW5kKHtcbiAgICBpbmNsdWRlczogW3JlcXVpcmUoJy4vbG9hZF90aWxlanNvbicpXSxcblxuICAgIG9wdGlvbnM6IHtcbiAgICAgICAgc2FuaXRpemVyOiByZXF1aXJlKCdzYW5pdGl6ZS1jYWphJylcbiAgICB9LFxuXG4gICAgLy8gaHR0cDovL21hcGJveC5jb20vZGV2ZWxvcGVycy9hcGkvI2ltYWdlX3F1YWxpdHlcbiAgICBmb3JtYXRzOiBbXG4gICAgICAgICdwbmcnLCAnanBnJyxcbiAgICAgICAgLy8gUE5HXG4gICAgICAgICdwbmczMicsICdwbmc2NCcsICdwbmcxMjgnLCAncG5nMjU2JyxcbiAgICAgICAgLy8gSlBHXG4gICAgICAgICdqcGc3MCcsICdqcGc4MCcsICdqcGc5MCddLFxuXG4gICAgc2NhbGVQcmVmaXg6ICdAMnguJyxcblxuICAgIGluaXRpYWxpemU6IGZ1bmN0aW9uKF8sIG9wdGlvbnMpIHtcbiAgICAgICAgTC5UaWxlTGF5ZXIucHJvdG90eXBlLmluaXRpYWxpemUuY2FsbCh0aGlzLCB1bmRlZmluZWQsIG9wdGlvbnMpO1xuXG4gICAgICAgIHRoaXMuX3RpbGVqc29uID0ge307XG5cbiAgICAgICAgaWYgKG9wdGlvbnMgJiYgb3B0aW9ucy5mb3JtYXQpIHtcbiAgICAgICAgICAgIHV0aWwuc3RyaWN0X29uZW9mKG9wdGlvbnMuZm9ybWF0LCB0aGlzLmZvcm1hdHMpO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5fbG9hZFRpbGVKU09OKF8pO1xuICAgIH0sXG5cbiAgICBzZXRGb3JtYXQ6IGZ1bmN0aW9uKF8pIHtcbiAgICAgICAgdXRpbC5zdHJpY3QoXywgJ3N0cmluZycpO1xuICAgICAgICB0aGlzLm9wdGlvbnMuZm9ybWF0ID0gXztcbiAgICAgICAgdGhpcy5yZWRyYXcoKTtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcblxuICAgIC8vIGRpc2FibGUgdGhlIHNldFVybCBmdW5jdGlvbiwgd2hpY2ggaXMgbm90IGF2YWlsYWJsZSBvbiBtYXBib3ggdGlsZWxheWVyc1xuICAgIHNldFVybDogbnVsbCxcblxuICAgIF9zZXRUaWxlSlNPTjogZnVuY3Rpb24oanNvbikge1xuICAgICAgICB1dGlsLnN0cmljdChqc29uLCAnb2JqZWN0Jyk7XG5cbiAgICAgICAgaWYgKCF0aGlzLm9wdGlvbnMuZm9ybWF0KSB7XG4gICAgICAgICAgdmFyIG1hdGNoID0ganNvbi50aWxlc1swXS5tYXRjaChmb3JtYXRQYXR0ZXJuKTtcbiAgICAgICAgICBpZiAobWF0Y2gpIHtcbiAgICAgICAgICAgICAgdGhpcy5vcHRpb25zLmZvcm1hdCA9IG1hdGNoWzFdO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIEwuZXh0ZW5kKHRoaXMub3B0aW9ucywge1xuICAgICAgICAgICAgdGlsZXM6IGpzb24udGlsZXMsXG4gICAgICAgICAgICBhdHRyaWJ1dGlvbjogdGhpcy5vcHRpb25zLnNhbml0aXplcihqc29uLmF0dHJpYnV0aW9uKSxcbiAgICAgICAgICAgIG1pblpvb206IGpzb24ubWluem9vbSB8fCAwLFxuICAgICAgICAgICAgbWF4Wm9vbToganNvbi5tYXh6b29tIHx8IDE4LFxuICAgICAgICAgICAgdG1zOiBqc29uLnNjaGVtZSA9PT0gJ3RtcycsXG4gICAgICAgICAgICBib3VuZHM6IGpzb24uYm91bmRzICYmIHV0aWwubGJvdW5kcyhqc29uLmJvdW5kcylcbiAgICAgICAgfSk7XG5cbiAgICAgICAgdGhpcy5fdGlsZWpzb24gPSBqc29uO1xuICAgICAgICB0aGlzLnJlZHJhdygpO1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuXG4gICAgZ2V0VGlsZUpTT046IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5fdGlsZWpzb247XG4gICAgfSxcblxuICAgIC8vIHRoaXMgaXMgYW4gZXhjZXB0aW9uIHRvIG1hcGJveC5qcyBuYW1pbmcgcnVsZXMgYmVjYXVzZSBpdCdzIGNhbGxlZFxuICAgIC8vIGJ5IGBMLm1hcGBcbiAgICBnZXRUaWxlVXJsOiBmdW5jdGlvbih0aWxlUG9pbnQpIHtcbiAgICAgICAgdmFyIHRpbGVzID0gdGhpcy5vcHRpb25zLnRpbGVzLFxuICAgICAgICAgICAgaW5kZXggPSBNYXRoLmZsb29yKE1hdGguYWJzKHRpbGVQb2ludC54ICsgdGlsZVBvaW50LnkpICUgdGlsZXMubGVuZ3RoKSxcbiAgICAgICAgICAgIHVybCA9IHRpbGVzW2luZGV4XTtcblxuICAgICAgICB2YXIgdGVtcGxhdGVkID0gTC5VdGlsLnRlbXBsYXRlKHVybCwgdGlsZVBvaW50KTtcbiAgICAgICAgaWYgKCF0ZW1wbGF0ZWQgfHwgIXRoaXMub3B0aW9ucy5mb3JtYXQpIHtcbiAgICAgICAgICAgIHJldHVybiB0ZW1wbGF0ZWQ7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4gdGVtcGxhdGVkLnJlcGxhY2UoZm9ybWF0UGF0dGVybixcbiAgICAgICAgICAgICAgICAoTC5Ccm93c2VyLnJldGluYSA/IHRoaXMuc2NhbGVQcmVmaXggOiAnLicpICsgdGhpcy5vcHRpb25zLmZvcm1hdCk7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgLy8gVGlsZUpTT04uVGlsZUxheWVycyBhcmUgYWRkZWQgdG8gdGhlIG1hcCBpbW1lZGlhdGVseSwgc28gdGhhdCB0aGV5IGdldFxuICAgIC8vIHRoZSBkZXNpcmVkIHotaW5kZXgsIGJ1dCBkbyBub3QgdXBkYXRlIHVudGlsIHRoZSBUaWxlSlNPTiBoYXMgYmVlbiBsb2FkZWQuXG4gICAgX3VwZGF0ZTogZnVuY3Rpb24oKSB7XG4gICAgICAgIGlmICh0aGlzLm9wdGlvbnMudGlsZXMpIHtcbiAgICAgICAgICAgIEwuVGlsZUxheWVyLnByb3RvdHlwZS5fdXBkYXRlLmNhbGwodGhpcyk7XG4gICAgICAgIH1cbiAgICB9XG59KTtcblxubW9kdWxlLmV4cG9ydHMuVGlsZUxheWVyID0gVGlsZUxheWVyO1xuXG5tb2R1bGUuZXhwb3J0cy50aWxlTGF5ZXIgPSBmdW5jdGlvbihfLCBvcHRpb25zKSB7XG4gICAgcmV0dXJuIG5ldyBUaWxlTGF5ZXIoXywgb3B0aW9ucyk7XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG5mdW5jdGlvbiBjb250YWlucyhpdGVtLCBsaXN0KSB7XG4gICAgaWYgKCFsaXN0IHx8ICFsaXN0Lmxlbmd0aCkgcmV0dXJuIGZhbHNlO1xuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbGlzdC5sZW5ndGg7IGkrKykge1xuICAgICAgICBpZiAobGlzdFtpXSA9PT0gaXRlbSkgcmV0dXJuIHRydWU7XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgaWRVcmw6IGZ1bmN0aW9uKF8sIHQpIHtcbiAgICAgICAgaWYgKF8uaW5kZXhPZignLycpID09PSAtMSkgdC5sb2FkSUQoXyk7XG4gICAgICAgIGVsc2UgdC5sb2FkVVJMKF8pO1xuICAgIH0sXG4gICAgbG9nOiBmdW5jdGlvbihfKSB7XG4gICAgICAgIGlmICh0eXBlb2YgY29uc29sZSA9PT0gJ29iamVjdCcgJiZcbiAgICAgICAgICAgIHR5cGVvZiBjb25zb2xlLmVycm9yID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKF8pO1xuICAgICAgICB9XG4gICAgfSxcbiAgICBzdHJpY3Q6IGZ1bmN0aW9uKF8sIHR5cGUpIHtcbiAgICAgICAgaWYgKHR5cGVvZiBfICE9PSB0eXBlKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgYXJndW1lbnQ6ICcgKyB0eXBlICsgJyBleHBlY3RlZCcpO1xuICAgICAgICB9XG4gICAgfSxcbiAgICBzdHJpY3RfaW5zdGFuY2U6IGZ1bmN0aW9uKF8sIGtsYXNzLCBuYW1lKSB7XG4gICAgICAgIGlmICghKF8gaW5zdGFuY2VvZiBrbGFzcykpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCBhcmd1bWVudDogJyArIG5hbWUgKyAnIGV4cGVjdGVkJyk7XG4gICAgICAgIH1cbiAgICB9LFxuICAgIHN0cmljdF9vbmVvZjogZnVuY3Rpb24oXywgdmFsdWVzKSB7XG4gICAgICAgIGlmICghY29udGFpbnMoXywgdmFsdWVzKSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIGFyZ3VtZW50OiAnICsgXyArICcgZ2l2ZW4sIHZhbGlkIHZhbHVlcyBhcmUgJyArXG4gICAgICAgICAgICAgICAgdmFsdWVzLmpvaW4oJywgJykpO1xuICAgICAgICB9XG4gICAgfSxcbiAgICBzdHJpcF90YWdzOiBmdW5jdGlvbihfKSB7XG4gICAgICAgIHJldHVybiBfLnJlcGxhY2UoLzxbXjxdKz4vZywgJycpO1xuICAgIH0sXG4gICAgbGJvdW5kczogZnVuY3Rpb24oXykge1xuICAgICAgICAvLyBsZWFmbGV0LWNvbXBhdGlibGUgYm91bmRzLCBzaW5jZSBsZWFmbGV0IGRvZXMgbm90IGRvIGdlb2pzb25cbiAgICAgICAgcmV0dXJuIG5ldyBMLkxhdExuZ0JvdW5kcyhbW19bMV0sIF9bMF1dLCBbX1szXSwgX1syXV1dKTtcbiAgICB9XG59O1xuIl19
|