{"version":3,"sources":["jsdelivr-header.js","/npm/speedtest-net-cors@1.7.10/index.js"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA,ACsBA,aAEA,IAAI,SAAW,QAAQ,UAAU,YAC7B,IAAM,QAAQ,OACd,aAAe,QAAQ,UAAU,aACjC,eAAiB,QAAQ,oBACzB,gBAAkB,QAAQ,qBAC1B,GAAK,QAAQ,MAEb,kCAAoC,MACpC,gCAAkC,MAClC,aAAe,KACf,aAAe,+BAA+B,cAC9C,cAAgB,+BAA+B,eAC/C,aAAe,KACnB,SAAS,+BAA+B,GAEpC,IAAK,IAAI,KADT,EAAO,EAAK,cACE,QAAQ,IAClB,GAAI,QAAQ,IAAI,eAAe,IAAM,IAAS,EAAE,cAC5C,OAAO,QAAQ,IAAI,GAG3B,OAAO,IACX,CAEA,SAAS,SAAS,GACd,IAAI,GAAQ,EAEZ,GAAI,aAAc,CACd,IAAI,EAAS,GAAG,oBAEhB,OAAO,KAAK,GAAQ,MAAK,SAAU,GAW/B,OAVA,EAAO,GAAQ,SAAQ,SAAU,GAC7B,IACuB,IAAnB,EAAM,UACN,eAAiB,EAAM,QAIvB,OAFA,EAAQ,aAAe,aACvB,GAAQ,CAGhB,IACO,CACX,GACJ,CACJ,CAOA,SAAS,MAAM,GACX,IAAI,EAAQ,KACR,GAAQ,EACa,WAArB,EAAQ,WACR,GAAQ,GAIR,aACA,EAAQ,aAGJ,aACA,EAAQ,aACD,gBACP,EAAQ,eAIX,IAKD,EAAQ,MADR,EACgB,IAAI,gBAAgB,GAEpB,IAAI,eAAe,GAE3C,CAEA,SAAS,KAAK,GAIV,MAHwB,mBAAb,IACP,EAAW,WAAa,GAErB,WACC,IACA,EAAS,MAAM,KAAM,WACrB,EAAW,KAEnB,CACJ,CAEA,SAAS,SAAS,EAAQ,GACtB,IAOI,EAPA,EAAO,EAAO,IACd,EAAO,EAAO,IACd,EAAO,EAAY,IACnB,EAAO,EAAY,IAEnB,EAAO,EAAQ,EAAO,GACtB,EAAO,EAAQ,EAAO,GAY1B,OARA,EACI,KAAK,IAAI,EAAO,GAAK,KAAK,IAAI,EAAO,GACrC,KAAK,IAAI,EAAQ,IACb,KAAK,IAAI,EAAQ,IACjB,KAAK,IAAI,EAAO,GAChB,KAAK,IAAI,EAAO,GAXX,MAYT,EAAI,KAAK,MAAM,KAAK,KAAK,GAAI,KAAK,KAAK,EAAI,KAI/C,SAAS,EAAQ,GACb,OAAQ,EAAI,IAAO,KAAK,EAC5B,CACJ,CAEA,SAAS,QAAQ,EAAQ,EAAS,GAC9B,IAAI,EAAM,EAEL,IACD,EAAW,EACX,GAAU,GAGd,EAAW,KAAK,GAIO,iBAFvB,EAAU,KAEuB,EAAU,IAAI,MAAM,IAErD,EAA4B,WAArB,EAAQ,SAAwB,QAAQ,SAAW,QAAQ,QAClE,MAAM,GACN,SAAS,UACF,EAAQ,SAEf,EAAQ,QAAU,EAAQ,SAAW,CAAC,EACtC,EAAQ,QAAQ,cACZ,EAAQ,QAAQ,eAChB,mFACI,KAAK,MAAsB,IAAhB,KAAK,SAAiB,MACjC,IACA,KAAK,MAAsB,IAAhB,KAAK,SAAiB,KACjC,iBAER,EAAQ,QAAU,OAAO,OAAO,EAAQ,QAAS,CAC7C,8BAA+B,IAC/B,+BACI,yCACJ,+BACI,uFACJ,yBAA0B,OAC1B,eAAgB,eAGpB,EAAQ,QAAU,IAElB,EAAK,IAAI,GAAS,SAAU,GACxB,GAAuB,MAAnB,EAAI,WACJ,OAAO,QAAQ,EAAI,QAAQ,SAAU,EAAS,GAElD,IAAI,EAAO,GACP,EAAQ,EACP,GAAS,EAAI,YAAY,QAC9B,EAAI,GAAG,QAAS,GAChB,EAAI,GAAG,QAAQ,SAAU,GACrB,GAAS,EAAQ,OACZ,IAAS,GAAQ,EAC1B,IACA,EAAI,GAAG,OAAO,WACN,IAAS,EAAO,GACpB,EAAS,KAAM,EAAM,EAAI,WAC7B,GACJ,IAAG,GAAG,QAAS,EACnB,CAEA,SAAS,SAAS,EAAQ,EAAM,GACvB,IACD,EAAW,EACX,EAAO,IAGX,EAAW,KAAK,GAEhB,IACI,EACA,EAFA,EAAU,EAIS,iBAAZ,IAAsB,EAAU,IAAI,MAAM,IAErD,EAAQ,QAAU,EAAQ,SAAW,CAAC,EACtC,EAAQ,QAAQ,cACZ,EAAQ,QAAQ,eAChB,oFACJ,EAAQ,QAAQ,gBAAkB,oCAClC,EAAQ,QAAQ,kBAAoB,EAAK,OACzC,EAAQ,QAAU,OAAO,OAAO,EAAQ,QAAS,CAC7C,8BAA+B,IAC/B,+BACI,yCACJ,+BACI,uFACJ,yBAA0B,OAC1B,eAAgB,eAEpB,EAAQ,OAAS,OAEjB,EAAO,QAA6B,WAArB,EAAQ,SAAwB,QAAU,QACzD,MAAM,GACN,SAAS,UACF,EAAQ,SAEf,EAAM,EAAK,QAAQ,GAAS,SAAU,GAClC,IAAI,EAAO,GACX,EAAI,YAAY,QAChB,EAAI,GAAG,QAAS,GAChB,EAAI,GAAG,QAAQ,SAAU,GACrB,GAAQ,CACZ,IACA,EAAI,GAAG,OAAO,WACV,EAAS,KAAM,EAAM,EAAI,WAC7B,GACJ,IAEA,EAAI,GAAG,QAAS,GAEhB,EAAI,IAAI,EACZ,CAEA,SAAS,cAAc,EAAQ,EAAM,GACjC,EAAW,KAAK,GAIhB,IAQI,EACA,EACA,EACA,EAXA,EAAU,EACV,EAAU,CACN,aACI,oFACJ,iBANR,EAA0B,GAAlB,GAAQ,SAQZ,EAAS,EACT,GAAQ,EAUZ,IAAK,IAJkB,iBAAZ,IAAsB,EAAU,IAAI,MAAM,IAErD,EAAQ,QAAU,EAAQ,SAAW,CAAC,EAEnB,EACf,EAAQ,QAAQ,GACZ,EAAQ,QAAQ,IAAe,EAAQ,GAuC/C,SAAS,IACL,EAAG,CACC,IAAK,EACD,OAEJ,IAAI,EAAO,EACN,IACD,GAAQ,EACR,EAAO,YAAc,GAEzB,EAAO,EAAK,OAAO,EAAG,GACtB,GAAU,EAAK,MACnB,OAAS,EAAQ,MAAM,GAC3B,CAjDA,EAAQ,OAAS,OAEjB,EAAQ,QAAU,OAAO,OAAO,EAAQ,QAAS,CAC7C,8BAA+B,IAC/B,+BACI,yCACJ,+BACI,uFACJ,yBAA0B,OAC1B,eAAgB,eAGpB,EAAY,WAER,IADA,IAAI,EAAI,uCACD,EAAE,OAAS,OAAW,GAAK,EAClC,OAAO,EAAE,OAAO,EAAG,MACtB,CAJW,GAMZ,EAA4B,WAArB,EAAQ,SAAwB,QAAQ,SAAW,QAAQ,QAClE,MAAM,GACN,SAAS,UACF,EAAQ,UAEf,EAAU,EAAK,QAAQ,GAAS,SAAU,GACtC,EAAS,GAAG,QAAS,GACrB,EAAS,GAAG,QAAQ,SAAU,GAE9B,IACA,EAAS,GAAG,OAAO,WAEf,EAAS,KAAM,EAAO,EAC1B,GACJ,KAEQ,GAAG,QAAS,GAiBpB,EAAQ,GAAG,QAAS,GAEpB,GACJ,CAEA,SAAS,OAAO,EAAQ,GACpB,EAAW,KAAK,GAEhB,QAAQ,GAAQ,SAAU,EAAK,GAC3B,GAAI,EAAK,OAAO,EAAS,GACzB,SAAS,GAAM,SAAU,EAAK,GAC1B,GAAI,EAAK,OAAO,EAAS,GACzB,EAAS,KAAM,EACnB,GACJ,GACJ,CAEA,SAAS,WAAW,EAAQ,GACxB,EAAW,KAAK,GAEhB,IACI,EAAO,EACP,EAAW,MACf,SAAS,IACL,IACI,EADA,EAAQ,QAAQ,SAGpB,YAAW,WACP,IAAK,EAED,OADA,GAAW,EACJ,EAAS,IAAI,MAAM,gBAElC,GAAG,KAEH,QAAQ,IAAI,QAAQ,EAAO,IAAK,gBAAgB,SAAU,EAAK,GAC3D,IAAI,EAAJ,CACA,GAAW,EACX,IAAI,EAAO,QAAQ,OAAO,GAO1B,GANA,EAAO,EAAK,GAAe,KAAV,EAAK,GACjB,GAA6B,cAAtB,EAAK,OAAO,EAAG,KACvB,EAAM,IAAI,MAAM,yBAChB,IAAK,EAAO,MACZ,EAAO,IAAU,EAAW,GAtB5B,MAuBJ,EAEI,OAAI,GAAY,KAAa,EAAS,IAAI,MAAM,gBACzC,EAAS,KAAiB,IAAX,GAEtB,GAbgB,CAexB,GACJ,CAEA,EACJ,CAEA,SAAS,YAAY,EAAS,EAAO,GACjC,IAGI,EAHA,EAAS,GACT,EAAO,KAAK,IAAI,EAAO,EAAQ,QAC/B,EAAO,EAGX,IAAK,EAAc,EAAG,EAAc,EAAM,KACtC,SAAW,GACP,EAAO,KAAK,GACZ,EAAO,SAAW,KAClB,WAAW,GAAQ,SAAU,EAAK,GAC1B,EAAW,IAAM,EAAO,KAAO,IAE/B,EAAW,KAGX,EAAO,SADP,EACkB,KAEA,IAEtB,IACa,IACT,EAAO,MAAK,SAAU,EAAG,GACrB,OAAO,EAAE,SAAW,EAAE,QAC1B,IACA,EAAS,KAAM,GAEvB,GACH,CArBD,CAqBG,EAAQ,IAEF,IAAT,GACA,aAAa,EAAU,KAAM,GAErC,CAEA,SAAS,cAAc,EAAM,EAAS,GAClC,IAMI,EACA,EANA,EAAU,EACV,EAAU,EACV,EAAO,EACP,EAAO,EAAK,OACZ,EAAa,EAIjB,EAAW,KAAK,GAEhB,GAAW,GAAW,KAAS,IAG3B,EADA,KAAK,KACE,KAAK,KAAK,KAAK,MAEf,WAAa,EAOxB,SAAS,IACL,GAAI,GAAW,EAAM,OACrB,GAAI,GAzBS,EAyBc,OAC3B,IAEA,IACI,EAAM,EADK,GAEf,IAEA,QAAQ,GAAK,GAAM,SAAU,EAAK,GAE9B,GAAI,EAAK,OAAO,EAAS,GACzB,IACI,EAEA,EACA,EAJA,EAAO,QAAQ,OAAO,GAM1B,EAAO,EAAK,GAAe,KAAV,EAAK,GAEtB,IAEA,IAEA,GADA,GAFA,GAAc,GAEO,GACJ,kCAAqC,MAEtD,EAAW,EAAO,EAAW,IAIzB,EAAO,IACP,EAAO,GAEP,GAAQ,IACR,EACI,mBACA,KAAK,MAC4C,GAA7C,KAAK,IAAI,KAAK,IAAI,EATrB,GASuC,MACpC,IAER,EAAK,wBAAyB,IAE9B,GAAQ,EACR,EAAS,KAAM,GAEf,GAER,IAEA,GACJ,CAtDA,GAEA,EAAY,QAAQ,QAqDxB,CAEA,SAAS,YAAY,EAAK,EAAO,EAAS,GACtC,IAMI,EACA,EANA,EAAU,EACV,EAAU,EACV,EAAO,EACP,EAAO,EAAM,OACb,EAAa,EAIjB,EAAW,KAAK,GAChB,GAAW,GAAW,KAAS,IAG3B,EADA,KAAK,KACE,KAAK,KAAK,KAAK,MAEf,WAAa,EAOxB,SAAS,IACL,GAAI,GAAW,EAAM,OACrB,GAAI,GAxBS,EAwBc,OAC3B,IACA,IACI,EAAO,EADI,GAEf,IAGA,cAAc,EAAK,GAAM,SAAU,EAAK,GAEpC,KAAI,GAAQ,GAAZ,CACI,IACA,EAAQ,GAEZ,IACI,EACA,EACA,EACA,EAJA,EAAO,QAAQ,OAAO,GAM1B,EAAO,EAAK,GAAe,KAAV,EAAK,GAEtB,IAEA,IAEA,GADA,GAFA,GAAc,GAEO,GACJ,gCAAmC,MAEpD,EAAW,EAAO,EAAW,IAC7B,EAAU,EAAO,EAAQ,IAGrB,EAAO,IACP,EAAO,GAEP,GAAQ,IACR,EACI,iBACA,KAAK,MAC4C,GAA7C,KAAK,IAAI,KAAK,IAAI,EAAS,GAAS,MACpC,IAER,EAAK,sBAAuB,IAE5B,GAAQ,EACR,EAAS,KAAM,GAEf,GArCoB,CAuC5B,IAEA,GACJ,CAzDA,GAEA,EAAY,QAAQ,QAwDxB,CAEA,SAAS,UAAU,GACf,IAeI,EAfA,EAAO,IAAI,aACX,EAAY,CAAC,EACb,EAAc,CACV,sDACA,uDACA,wDACA,mEACA,yDACA,wEACA,iDACA,6DACA,kDACA,qEAEJ,EAAY,EAYhB,SAAS,EAAS,GACd,IAAI,EAAI,IAAI,MAAM,GAElB,OADA,EAAE,QAAU,EAAQ,SAAW,CAAC,EACzB,CACX,CAqCA,SAAS,EAAW,GAChB,GAAI,GAAa,EAAY,OACzB,OAAO,EAAK,KACR,QACA,GACI,IAAI,MACA,2GAIZ,EAAY,EACZ,EAAY,EAAY,QAExB,EAAa,EAAY,GACzB,KAEJ,OAAO,EAAS,GAAa,EACjC,CAIA,SAAS,EAAW,EAAK,GACrB,GAAI,EAAK,OAAO,EAAW,GAC3B,IAAK,IAAY,EAAQ,SACrB,OAAO,EACH,IAAI,MAAM,+CAGlB,IACI,EADA,EAAS,EAAQ,SAAS,QAAQ,GAAG,OAIzC,IADA,EAAU,GACL,EAAc,EAAG,EAAc,EAAO,OAAQ,IAAe,CAC9D,GACI,EAAQ,UACR,EAAO,GAAa,EAAE,KAAO,EAAQ,SACvC,CACE,EAAU,CAAC,EAAO,GAAa,GAC/B,KACJ,CACA,EAAQ,KAAK,EAAO,GAAa,EACrC,CAEA,EAAU,QAAU,EAEpB,EAAK,KAAK,UAAW,GACrB,GACJ,CAEA,SAAS,IACL,GAAK,EAAU,QAAW,EAAU,QAApC,CAGA,IACI,EACA,EACA,EAHA,EAAU,EAAU,QAKxB,IAAK,EAAc,EAAG,EAAc,EAAQ,OAAQ,IAChD,EAAS,EAAQ,GACjB,EAAO,SAAS,EAAU,OAAO,OAAQ,GAEzC,EAAO,KAAO,EACd,EAAO,OAAgB,QAAP,EAGpB,EAAQ,MAAK,SAAU,EAAG,GACtB,OAAO,EAAE,KAAO,EAAE,IACtB,IAEA,YAAY,EAAS,EAAQ,WAAW,SAAU,EAAK,GACnD,OAAI,EAAY,EAAK,KAAK,QAAS,GAC9B,GAAgB,EAAY,QAMjC,EAAU,YAAc,EACxB,EAAU,WAAa,EAAU,YAAY,GAC7C,EAAK,KAAK,cAAe,QAEzB,KATW,EAAK,KACR,QACA,IAAI,MAAM,uCAQtB,GAjCmD,CAkCvD,CAEA,SAAS,EAAc,GAEnB,IADA,EAAK,GAAM,IACD,EAAU,YAAY,QAAU,GAAM,EAAQ,WACpD,OAyCR,WACI,IAWI,EACA,EACA,EAbA,EAAQ,GACR,EAAY,CACR,KAAK,MAAM,MACX,KAAK,MAAM,KACX,KAAK,MAAM,KACX,KAAK,MAAM,KACX,KAAK,MAAM,KACX,KAAK,MAAM,KACX,KAAK,MAAM,MACX,KAAK,MAAM,OAMnB,IAAK,EAAI,EAAG,EAAI,EAAU,OAAQ,IAE9B,IADA,EAAW,EAAU,GAChB,EAAI,EAAG,EAAI,GAAI,IAChB,EAAM,KAAK,GAGnB,EAAK,KAAK,aAAc,EAAU,YAClC,YAAY,KACR,EACA,EAAU,WAAW,IACrB,EACA,EAAQ,SACR,SAAU,EAAK,GACX,GAAI,EAAK,OAAO,EAAK,KAAK,QAAS,GAuBnC,SAAS,EAAI,GACT,EAAU,OAAO,OAAO,GAAQ,WAC5B,EAAU,OAAO,OAAO,GAEhC,CA1BA,EAAU,YAAc,EACxB,EAAU,qBACL,EAAQ,gCAAmC,MAChD,EAAK,KAAK,iBAAkB,KAC5B,EAAK,KAAK,cAAe,EAAU,sBAwBnC,EAAI,OACJ,EAAI,OACJ,EAAI,aACJ,EAAI,UACJ,EAAI,YACJ,EAAI,mBAEG,EAAU,OAAO,OAAO,SAG/B,EAAU,OAAO,OAAO,UAAY,IACpC,EAAU,OAAO,OAAO,UAAY,IAEpC,IAAI,EAAO,EAAU,WACjB,EAAO,CACH,OAAQ,CAEJ,SACI,KAAK,MACkC,IAAnC,EAAU,wBACV,IACR,OACI,KAAK,MACgC,IAAjC,EAAU,sBACV,IACR,iBAAkB,KAAK,MACnB,EAAU,eAEd,eAAgB,KAAK,MAAM,EAAU,cAEzC,OAAQ,EAAU,OAAO,OACzB,OAAQ,CACJ,KAAM,IAAI,MAAM,EAAK,KAAK,KAC1B,IAAK,OAAO,EAAK,KACjB,IAAK,OAAO,EAAK,KACjB,SAAU,EAAK,KACf,QAAS,EAAK,QACd,GAAI,EAAK,GACT,QAAS,EAAK,QACd,SAAU,KAAK,MAAkB,IAAZ,EAAK,MAAc,IACxC,WAAY,KAAK,MAAoB,IAAd,EAAK,QAAgB,IAC5C,KAAM,KAAK,MAAsB,GAAhB,EAAK,UAAiB,GACvC,GAAI,EAAK,KAGrB,EAAK,KAAK,OAAQ,GAM9B,WACI,IAgCI,EACA,EAjCA,EAAO,EAAU,WACjB,EAAM,SAAU,GACZ,OAAO,QAAQ,UACV,WAAW,OACX,OAAO,GACP,OAAO,MAChB,EACA,EAAW,KAAK,MAAyC,IAAnC,EAAU,wBAChC,EAAW,KAAK,MAAuC,IAAjC,EAAU,sBAChC,EAAO,KAAK,MAAM,EAAK,UACvB,EAAM,CACF,WACA,EACA,OACA,EACA,SACA,EACA,QACA,GACA,YACA,aACA,sBACA,EAAK,GACL,WACA,EACA,WACA,EAAK,GACL,OACA,EAAI,EAAO,IAAM,EAAW,IAAM,EAAW,cAEjD,EAAY,uCACZ,EAAO,GAIX,IAAK,EAAI,EAAG,EAAI,EAAI,OAAQ,GAAK,EAC7B,EAAK,KAAK,EAAI,GAAK,IAAM,mBAAmB,EAAI,EAAI,MAGxD,EAAO,EAAS,IAEX,QAAQ,QAAU,6CAEvB,SAAS,EAAM,EAAK,KAAK,MAAM,SAAU,EAAK,EAAM,GAChD,GAAI,EAAK,OAAO,EAAK,KAAK,QAAS,GACnC,IACI,EADA,EAAQ,OAAO,GAAM,MAAM,wBAEhB,MAAX,GAAkB,GAAS,EAAM,KAEjC,EACI,mCAAqC,EAAM,GAAK,QAGxD,EAAU,UAAY,EAEtB,EAAK,KAAK,SAAU,GACpB,EAAK,KAAK,OAAQ,EACtB,GACJ,CAhEY,EACJ,GAER,CApJe,GACX,IAII,EACA,EACA,EANA,EAAS,EAAU,YAAY,GAC/B,EAAS,EAAO,IAChB,EAAQ,CAAC,IAAK,IAAK,IAAK,IAAM,KAAM,IAAM,KAAM,IAAM,KAAM,KAC5D,EAAO,GAKX,IAAK,EAAI,EAAG,EAAI,EAAM,OAAQ,IAE1B,IADA,EAAO,EAAM,GACR,EAAI,EAAG,EAAI,EAAG,IACf,EAAK,KACD,IAAI,QAAQ,EAAQ,SAAW,EAAO,IAAM,EAAO,SAK/D,EAAK,KAAK,aAAc,GAExB,cAAc,KAAK,EAAM,EAAM,EAAQ,SAAS,SAAU,EAAK,GAC3D,GAAI,EAAK,OAAO,EAAK,KAAK,QAAS,GACnC,IAAI,EAAS,EAAQ,kCAAqC,MAC1D,EAAK,KAAK,mBAAoB,KAC9B,EAAK,KAAK,gBAAiB,GAEvB,EAAU,cACN,EAAQ,EAAU,gBAClB,EAAU,cAAgB,EAC1B,EAAU,uBAAyB,EACnC,EAAU,WAAa,IAG3B,EAAU,cAAgB,EAC1B,EAAU,uBAAyB,GAGvC,EAAc,EAAK,EACvB,GACJ,CA4KA,OA9VA,EAAU,GAAW,CAAC,GAEd,QAAU,EAAQ,SAAW,IACrC,EAAQ,UAAY,EAAQ,YAAc,EAAQ,SAAW,EAAI,GACjE,EAAQ,WAAa,EAAQ,YAAc,EAC3C,EAAQ,MAAQ,EAAQ,OAAS,KACjC,aAAe,EAAQ,MACvB,aAAe,EAAQ,UAAY,KAUnC,OACI,EAAS,mDAIb,SAAmB,EAAK,GACpB,GAAI,EAAK,OAAO,EAAK,KAAK,QAAS,GAGnC,SAAS,EAAI,GACT,QAAS,EAAO,IAAS,IAAI,IAAM,CAAC,GAAG,GAAK,CAAC,CACjD,CAJA,EAAU,GAAU,EAAO,UAAa,CAAC,EAMzC,IAAI,EAAS,EAAI,UACb,EAAQ,EAAI,SACZ,EAAW,EAAI,YACf,EAAS,EAAI,UACjB,EAAU,OAAS,CACf,OAAQ,EACR,MAAO,EACP,SAAU,EACV,OAAQ,GAGZ,EAAK,KAAK,SAAU,EAAU,QAC9B,GACJ,IAEI,EAAQ,aACR,EAAa,EAAQ,WACrB,GAAa,GAsBjB,IAyRO,CACX,CAIA,SAAS,gBAAgB,EAAS,GAC9B,QAAQ,YAAY,KAAK,SAGzB,IAGI,EAHA,EAAQ,QAAQ,SAChB,EAAO,UAAU,GACjB,EAAM,WAAa,EAEnB,EAAa,EACb,EAAQ,EACR,EAAM,QAAQ,QAEd,EAAM,EAAM,cAAgB,EAAM,MAAM,KAAO,IAC/C,EAAQ,EAAM,cAAgB,EAAM,QAAQ,KAAO,IA8CvD,SAAS,EAAK,EAAM,EAAK,GACrB,EAAa,GAAO,EACpB,EAAQ,GAAO,EAEf,IAAI,EAAW,KAAK,MAAO,EAAa,IApDjC,IAqDH,EAAS,GAEb,GAAU,EACV,GAAU,IAAI,OAAO,GAAK,EAAK,QAG/B,GAAU,EAAM,OAAO,GACvB,GAAU,EAAI,OA5DP,GA4DqB,GAI5B,GAAU,KADV,EAAM,EAAa,KAInB,GAAU,IAAI,OAAO,EAAI,EAAI,QAAU,EAEvC,EAAI,EACR,CA4CA,OA/GA,EAAW,KAAK,GAEZ,EAAQ,MAEJ,EADuB,mBAAhB,EAAQ,IACT,EAAQ,IAER,QAAQ,IAAI,KAAK,UAI/B,EAAK,GAAG,SAAS,SAAU,GACvB,EAAS,EACb,IAEA,EAAK,GAAG,cAAc,SAAU,GAC5B,EACI,mBACI,EAAO,QACP,OACA,EAAO,KACP,KACA,EAAO,QACP,MACiB,QAAhB,EAAO,QAAmB,QAAQ,GACnC,OACA,EAAO,SAAS,QAAQ,GACxB,MAEZ,IAEA,EAAK,GAAG,UAAU,SAAU,GACxB,IAAI,EAAS,EAAO,OACpB,EACI,gBACI,EAAO,GACP,OACA,EAAO,IACP,mBACC,EAAO,SAAW,KAAM,QAAQ,GACjC,uBACC,EAAO,SAAW,KAAM,QAAQ,GACjC,OAEZ,IA0BA,EAAK,GAAG,oBAAoB,SAAU,GAClC,EAAK,WAAY,EAAK,KAC1B,IAEA,EAAK,GAAG,kBAAkB,SAAU,GAChC,EAAK,SAAU,EAAK,KACxB,IAEA,EAAK,GAAG,iBAAiB,SAAU,GAC/B,EAAI,mBAAoB,EAAM,QAAQ,GAAK,QAG3C,EAAM,QAAQ,OAClB,IAEA,EAAK,GAAG,eAAe,SAAU,GAC7B,EAAI,iBAAkB,EAAM,QAAQ,GAAK,QAGzC,EAAM,QAAQ,OAClB,IAEA,EAAK,GAAG,yBAAyB,SAAU,GACvC,EAAK,WAAY,KAAM,EAAM,QAAQ,GAAK,OAC9C,IAEA,EAAK,GAAG,uBAAuB,SAAU,GACrC,EAAK,SAAU,KAAM,EAAM,QAAQ,GAAK,OAC5C,IAEA,EAAK,GAAG,QAAQ,SAAU,GACtB,EAAY,CAChB,IAEA,EAAK,GAAG,UAAU,SAAU,GACxB,EAAI,gBAAkB,EAC1B,IAEA,EAAK,GAAG,QAAQ,WACZ,EAAS,KAAM,EACnB,IAEO,CACX,CAhIA,OAAO,QAAU,UAkIjB,UAAU,OAAS","file":"/npm/speedtest-net-cors@1.7.10/index.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/speedtest-net-cors@1.7.10/index.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","/*\n\nSpeedtest.net client.\n\nThe MIT License (MIT)\n\nCopyright (c) 2014 Han de Boer\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n*/\n\n'use strict';\n\nvar parseXML = require('xml2js').parseString,\n    url = require('url'),\n    EventEmitter = require('events').EventEmitter,\n    HttpProxyAgent = require('http-proxy-agent'),\n    HttpsProxyAgent = require('https-proxy-agent'),\n    os = require('os'),\n    // These numbers were obtained by measuring and averaging both using this module and the official speedtest.net\n    speedTestDownloadCorrectionFactor = 1.135,\n    speedTestUploadCorrectionFactor = 1.139,\n    proxyOptions = null,\n    proxyHttpEnv = findPropertiesInEnvInsensitive('HTTP_PROXY'),\n    proxyHttpsEnv = findPropertiesInEnvInsensitive('HTTPS_PROXY'),\n    localAddress = null;\nfunction findPropertiesInEnvInsensitive(prop) {\n    prop = prop.toLowerCase();\n    for (var p in process.env) {\n        if (process.env.hasOwnProperty(p) && prop === p.toLowerCase()) {\n            return process.env[p];\n        }\n    }\n    return null;\n}\n\nfunction sourceIP(options) {\n    var found = false;\n\n    if (localAddress) {\n        var ifaces = os.networkInterfaces();\n\n        Object.keys(ifaces).some(function (ifname) {\n            ifaces[ifname].forEach(function (iface) {\n                if (\n                    iface.internal === false &&\n                    localAddress === iface.address\n                ) {\n                    options.localAddress = localAddress;\n                    found = true;\n                    return found;\n                }\n            });\n            return found;\n        });\n    }\n}\n\n//set the proxy agent for each http request\n// priority :\n// 1 - proxyOptions\n// 2 - proxyHttpEnv (HTTP_PROXY)\n// 3 - proxyHttpsEnv (HTTPS_PROXY)\nfunction proxy(options) {\n    var proxy = null,\n        isSSL = false;\n    if (options.protocol === 'https:') {\n        isSSL = true;\n    }\n\n    // Test the proxy parameter first for priority\n    if (proxyOptions) {\n        proxy = proxyOptions;\n    } else {\n        // Test proxy by env\n        if (proxyHttpEnv) {\n            proxy = proxyHttpEnv;\n        } else if (proxyHttpsEnv) {\n            proxy = proxyHttpsEnv;\n        }\n    }\n\n    if (!proxy) {\n        return;\n    }\n\n    if (isSSL) {\n        options.agent = new HttpsProxyAgent(proxy);\n    } else {\n        options.agent = new HttpProxyAgent(proxy);\n    }\n}\n\nfunction once(callback) {\n    if (typeof callback !== 'function') {\n        callback = function () {};\n    }\n    return function () {\n        if (callback) {\n            callback.apply(this, arguments);\n            callback = null;\n        }\n    };\n}\n\nfunction distance(origin, destination) {\n    var lat1 = origin.lat,\n        lon1 = origin.lon,\n        lat2 = destination.lat,\n        lon2 = destination.lon,\n        radius = 6371, //km\n        dlat = deg2rad(lat2 - lat1),\n        dlon = deg2rad(lon2 - lon1),\n        a,\n        c;\n\n    a =\n        Math.sin(dlat / 2) * Math.sin(dlat / 2) +\n        Math.cos(deg2rad(lat1)) *\n            Math.cos(deg2rad(lat2)) *\n            Math.sin(dlon / 2) *\n            Math.sin(dlon / 2);\n    c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n\n    return radius * c;\n\n    function deg2rad(d) {\n        return (d / 180) * Math.PI;\n    }\n}\n\nfunction getHttp(theUrl, discard, callback) {\n    var http, options;\n\n    if (!callback) {\n        callback = discard;\n        discard = false;\n    }\n\n    callback = once(callback);\n\n    options = theUrl;\n\n    if (typeof options === 'string') options = url.parse(options);\n\n    http = options.protocol === 'https:' ? require('https') : require('http');\n    proxy(options);\n    sourceIP(options);\n    delete options.protocol;\n\n    options.headers = options.headers || {};\n    options.headers['user-agent'] =\n        options.headers['user-agent'] ||\n        'Mozilla/5.0 (Windows NT 6.3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.' +\n            Math.trunc(Math.random() * 400 + 2704) +\n            '.' +\n            Math.trunc(Math.random() * 400 + 103) +\n            ' Safari/537.36';\n\n    options.headers = Object.assign(options.headers, {\n        'Access-Control-Allow-Origin': '*',\n        'Access-Control-Allow-Methods':\n            'GET, POST, PUT, DELETE, PATCH, OPTIONS',\n        'Access-Control-Allow-Headers':\n            'x-requested-with, Content-Type, origin, authorization, accept, client-security-token',\n        'Access-Control-Max-Age': '1000',\n        ReferrerPolicy: 'unsafe-url',\n    });\n\n    options.timeout = 500;\n\n    http.get(options, function (res) {\n        if (res.statusCode === 302) {\n            return getHttp(res.headers.location, discard, callback);\n        }\n        var data = '',\n            count = 0;\n        if (!discard) res.setEncoding('utf8');\n        res.on('error', callback);\n        res.on('data', function (newData) {\n            count += newData.length;\n            if (!discard) data += newData;\n        });\n        res.on('end', function () {\n            if (discard) data = count;\n            callback(null, data, res.statusCode);\n        });\n    }).on('error', callback);\n}\n\nfunction postHttp(theUrl, data, callback) {\n    if (!callback) {\n        callback = data;\n        data = '';\n    }\n\n    callback = once(callback);\n\n    var options = theUrl,\n        http,\n        req;\n\n    if (typeof options === 'string') options = url.parse(options);\n\n    options.headers = options.headers || {};\n    options.headers['user-agent'] =\n        options.headers['user-agent'] ||\n        'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:24.0) Gecko/20100101 Firefox/24.0';\n    options.headers['content-type'] = 'application/x-www-form-urlencoded';\n    options.headers['content-length'] = data.length;\n    options.headers = Object.assign(options.headers, {\n        'Access-Control-Allow-Origin': '*',\n        'Access-Control-Allow-Methods':\n            'GET, POST, PUT, DELETE, PATCH, OPTIONS',\n        'Access-Control-Allow-Headers':\n            'x-requested-with, Content-Type, origin, authorization, accept, client-security-token',\n        'Access-Control-Max-Age': '1000',\n        ReferrerPolicy: 'unsafe-url',\n    });\n    options.method = 'POST';\n\n    http = require(options.protocol === 'https:' ? 'https' : 'http');\n    proxy(options);\n    sourceIP(options);\n    delete options.protocol;\n\n    req = http.request(options, function (res) {\n        var data = '';\n        res.setEncoding('utf8');\n        res.on('error', callback);\n        res.on('data', function (newData) {\n            data += newData;\n        });\n        res.on('end', function () {\n            callback(null, data, res.statusCode);\n        });\n    });\n\n    req.on('error', callback);\n\n    req.end(data);\n}\n\nfunction randomPutHttp(theUrl, size, callback) {\n    callback = once(callback);\n\n    size = (size || 131072) | 0; //eslint-disable-line no-bitwise\n\n    var options = theUrl,\n        headers = {\n            'user-agent':\n                'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:24.0) Gecko/20100101 Firefox/24.0',\n            'content-length': size,\n        },\n        toSend = size,\n        sent1 = false,\n        dataBlock,\n        http,\n        headerName,\n        request;\n\n    if (typeof options === 'string') options = url.parse(theUrl);\n\n    options.headers = options.headers || {};\n\n    for (headerName in headers) {\n        options.headers[headerName] =\n            options.headers[headerName] || headers[headerName];\n    }\n\n    options.method = 'POST';\n\n    options.headers = Object.assign(options.headers, {\n        'Access-Control-Allow-Origin': '*',\n        'Access-Control-Allow-Methods':\n            'GET, POST, PUT, DELETE, PATCH, OPTIONS',\n        'Access-Control-Allow-Headers':\n            'x-requested-with, Content-Type, origin, authorization, accept, client-security-token',\n        'Access-Control-Max-Age': '1000',\n        ReferrerPolicy: 'unsafe-url',\n    });\n\n    dataBlock = (function () {\n        var d = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';\n        while (d.length < 1024 * 16) d += d;\n        return d.substr(0, 1024 * 16);\n    })();\n\n    http = options.protocol === 'https:' ? require('https') : require('http');\n    proxy(options);\n    sourceIP(options);\n    delete options.protocol;\n\n    request = http.request(options, function (response) {\n        response.on('error', callback);\n        response.on('data', function (newData) {\n            //discard\n        });\n        response.on('end', function () {\n            // Some cases (like HTTP 413) will interrupt the upload, but still return a response\n            callback(null, size - toSend);\n        });\n    });\n\n    request.on('error', callback);\n\n    function write() {\n        do {\n            if (!toSend) {\n                return; //we're done sending...\n            }\n            var data = dataBlock;\n            if (!sent1) {\n                sent1 = true;\n                data = 'content1=' + data;\n            }\n            data = data.substr(0, toSend);\n            toSend -= data.length;\n        } while (request.write(data));\n    }\n\n    request.on('drain', write);\n\n    write();\n}\n\nfunction getXML(xmlurl, callback) {\n    callback = once(callback);\n\n    getHttp(xmlurl, function (err, data) {\n        if (err) return callback(err);\n        parseXML(data, function (err, xml) {\n            if (err) return callback(err);\n            callback(null, xml);\n        });\n    });\n}\n\nfunction pingServer(server, callback) {\n    callback = once(callback);\n\n    var total = 3,\n        done = 0,\n        bestTime = 3600;\n    function nextPing() {\n        var start = process.hrtime(),\n            complete;\n\n        setTimeout(function () {\n            if (!complete) {\n                complete = true;\n                return callback(new Error('Ping timeout'));\n            }\n        }, 5000);\n\n        getHttp(url.resolve(server.url, 'latency.txt'), function (err, data) {\n            if (complete) return; // already hit timeout\n            complete = true;\n            var diff = process.hrtime(start);\n            diff = diff[0] + diff[1] * 1e-9; //seconds\n            if (!err && data.substr(0, 9) !== 'test=test')\n                err = new Error('Unknown latency file');\n            if (err) diff = 3600; //an hour...\n            if (diff < bestTime) bestTime = diff;\n            done++;\n            if (done === total) {\n                if (bestTime >= 3600) return callback(new Error('Ping failed'));\n                return callback(null, bestTime * 1000); //ms\n            } else {\n                nextPing();\n            }\n        });\n    }\n\n    nextPing();\n}\n\nfunction pingServers(servers, count, callback) {\n    var result = [],\n        todo = Math.min(count, servers.length),\n        done = 0,\n        serverIndex;\n\n    for (serverIndex = 0; serverIndex < todo; serverIndex++) {\n        (function (server) {\n            result.push(server);\n            server.bestPing = 3600;\n            pingServer(server, function (err, bestTime) {\n                if (bestTime < 10 && server.dist < 2) {\n                    //too close! Same datacenter? upload speeds of several GB/s ?? Bad measurment...\n                    bestTime = 100;\n                }\n                if (err) {\n                    server.bestPing = 3600;\n                } else {\n                    server.bestPing = bestTime;\n                }\n                done++;\n                if (done === todo) {\n                    result.sort(function (a, b) {\n                        return a.bestPing - b.bestPing;\n                    });\n                    callback(null, result);\n                }\n            });\n        })(servers[serverIndex]);\n    }\n    if (todo === 0) {\n        setImmediate(callback, null, []);\n    }\n}\n\nfunction downloadSpeed(urls, maxTime, callback) {\n    var concurrent = 2,\n        running = 0,\n        started = 0,\n        done = 0,\n        todo = urls.length,\n        totalBytes = 0,\n        emit,\n        timeStart;\n\n    callback = once(callback);\n\n    maxTime = (maxTime || 10000) / 1000;\n\n    if (this.emit) {\n        emit = this.emit.bind(this);\n    } else {\n        emit = function () {};\n    }\n\n    next();\n\n    timeStart = process.hrtime();\n\n    function next() {\n        if (started >= todo) return; //all are started\n        if (running >= concurrent) return;\n        running++;\n\n        var starting = started,\n            url = urls[starting];\n        started++;\n\n        getHttp(url, true, function (err, count) {\n            //discard all data and return byte count\n            if (err) return callback(err);\n            var diff = process.hrtime(timeStart),\n                timePct,\n                amtPct,\n                speed,\n                fixed;\n\n            diff = diff[0] + diff[1] * 1e-9; //seconds\n\n            running--;\n            totalBytes += count;\n            done++;\n            speed = totalBytes / diff;\n            fixed = (speed * speedTestDownloadCorrectionFactor) / 125000;\n\n            timePct = (diff / maxTime) * 100;\n            // amtPct=done/todo*100;\n            amtPct = 0; //time-only\n\n            if (diff > maxTime) {\n                done = todo;\n            }\n            if (done <= todo) {\n                emit(\n                    'downloadprogress',\n                    Math.round(\n                        Math.min(Math.max(timePct, amtPct), 100.0) * 10\n                    ) / 10\n                );\n                emit('downloadspeedprogress', fixed);\n            }\n            if (done >= todo) {\n                callback(null, speed); //bytes/sec\n            } else {\n                next();\n            }\n        });\n\n        next(); //Try another\n    }\n}\n\nfunction uploadSpeed(url, sizes, maxTime, callback) {\n    var concurrent = 2,\n        running = 0,\n        started = 0,\n        done = 0,\n        todo = sizes.length,\n        totalBytes = 0,\n        emit,\n        timeStart;\n\n    callback = once(callback);\n    maxTime = (maxTime || 10000) / 1000;\n\n    if (this.emit) {\n        emit = this.emit.bind(this);\n    } else {\n        emit = function () {};\n    }\n\n    next();\n\n    timeStart = process.hrtime();\n\n    function next() {\n        if (started >= todo) return; //all are started\n        if (running >= concurrent) return;\n        running++;\n        var starting = started,\n            size = sizes[starting];\n        started++;\n        //started=(started+1) % todo; //Keep staing more until the time is up...\n\n        randomPutHttp(url, size, function (err, count) {\n            //discard all data and return byte count\n            if (done >= todo) return;\n            if (err) {\n                count = 0;\n            }\n            var diff = process.hrtime(timeStart),\n                timePct,\n                amtPct,\n                speed,\n                fixed;\n\n            diff = diff[0] + diff[1] * 1e-9; //seconds\n\n            running--;\n            totalBytes += count;\n            done++;\n            speed = totalBytes / diff;\n            fixed = (speed * speedTestUploadCorrectionFactor) / 125000;\n\n            timePct = (diff / maxTime) * 100;\n            amtPct = (done / todo) * 100;\n            //amtPct=0; //time-only\n\n            if (diff > maxTime) {\n                done = todo;\n            }\n            if (done <= todo) {\n                emit(\n                    'uploadprogress',\n                    Math.round(\n                        Math.min(Math.max(timePct, amtPct), 100.0) * 10\n                    ) / 10\n                );\n                emit('uploadspeedprogress', fixed);\n            }\n            if (done >= todo) {\n                callback(null, speed); //bytes/sec\n            } else {\n                next();\n            }\n        });\n\n        next(); //Try another\n    }\n}\n\nfunction speedTest(options) {\n    var self = new EventEmitter(),\n        speedInfo = {},\n        serversUrls = [\n            'http://c.speedtest.net/speedtest-servers-static.php',\n            'https://c.speedtest.net/speedtest-servers-static.php',\n            'http://www.speedtest.net/speedtest-servers-static.php',\n            'http://www.speedtest.net/speedtest-servers-static.php?really=yes',\n            'https://www.speedtest.net/speedtest-servers-static.php',\n            'https://www.speedtest.net/speedtest-servers-static.php?really=totally',\n            'http://www.speedtest.net/speedtest-servers.php',\n            'http://www.speedtest.net/speedtest-servers.php?really=sure',\n            'https://www.speedtest.net/speedtest-servers.php',\n            'https://www.speedtest.net/speedtest-servers.php?really=absolutely',\n        ],\n        curServer = 0,\n        serversUrl;\n\n    options = options || {};\n\n    options.maxTime = options.maxTime || 10000;\n    options.pingCount = options.pingCount || (options.serverId ? 1 : 5);\n    options.maxServers = options.maxServers || 1;\n    options.proxy = options.proxy || null;\n    proxyOptions = options.proxy;\n    localAddress = options.sourceIp || null;\n\n    function httpOpts(theUrl) {\n        var o = url.parse(theUrl);\n        o.headers = options.headers || {};\n        return o;\n    }\n\n    //Fetch config\n\n    getXML(\n        httpOpts('https://www.speedtest.net/speedtest-config.php'),\n        gotConfig\n    );\n\n    function gotConfig(err, config) {\n        if (err) return self.emit('error', err);\n        config = (config && config.settings) || {};\n\n        function get(name) {\n            return ((config[name] || [])[0] || {}).$ || {};\n        }\n\n        var client = get('client'),\n            times = get('times'),\n            download = get('download'),\n            upload = get('upload');\n        speedInfo.config = {\n            client: client,\n            times: times,\n            download: download,\n            upload: upload,\n        };\n\n        self.emit('config', speedInfo.config);\n        gotData();\n    }\n\n    if (options.serversUrl) {\n        serversUrl = options.serversUrl;\n        curServer = -1;\n    }\n\n    function nextServer(err) {\n        if (curServer >= serversUrls.length) {\n            return self.emit(\n                'error',\n                err ||\n                    new Error(\n                        'There was a problem getting the list of servers from SpeedTest.net. Consider using a custom serversUrl'\n                    )\n            );\n        }\n        if (curServer < 0) {\n            curServer = serversUrls.length;\n        } else {\n            serversUrl = serversUrls[curServer];\n            curServer++;\n        }\n        getXML(httpOpts(serversUrl), gotServers);\n    }\n\n    nextServer();\n\n    function gotServers(err, servers) {\n        if (err) return nextServer(err);\n        if (!servers || !servers.settings) {\n            return nextServer(\n                new Error('No server found, verify your proxy/network')\n            );\n        }\n        var server = servers.settings.servers[0].server,\n            serverIndex;\n\n        servers = [];\n        for (serverIndex = 0; serverIndex < server.length; serverIndex++) {\n            if (\n                options.serverId &&\n                server[serverIndex].$.id === options.serverId\n            ) {\n                servers = [server[serverIndex].$];\n                break;\n            }\n            servers.push(server[serverIndex].$);\n        }\n\n        speedInfo.servers = servers;\n\n        self.emit('servers', servers);\n        gotData();\n    }\n\n    function gotData() {\n        if (!speedInfo.config || !speedInfo.servers) return; //not ready yet\n\n        //order servers by how close they are:\n        var servers = speedInfo.servers,\n            serverIndex,\n            server,\n            dist;\n\n        for (serverIndex = 0; serverIndex < servers.length; serverIndex++) {\n            server = servers[serverIndex];\n            dist = distance(speedInfo.config.client, server);\n\n            server.dist = dist;\n            server.distMi = dist * 0.621371;\n        }\n\n        servers.sort(function (a, b) {\n            return a.dist - b.dist;\n        });\n\n        pingServers(servers, options.pingCount, function (err, bestServers) {\n            if (err) return self.emit('error', err);\n            if (!bestServers || !bestServers.length)\n                return self.emit(\n                    'error',\n                    new Error('Could not find a server to test on.')\n                );\n\n            speedInfo.bestServers = bestServers;\n            speedInfo.bestServer = speedInfo.bestServers[0];\n            self.emit('bestservers', bestServers);\n\n            startDownload();\n        });\n    }\n\n    function startDownload(ix) {\n        ix = ix || 0;\n        if (ix >= speedInfo.bestServers.length || ix >= options.maxServers)\n            return startUpload();\n        var server = speedInfo.bestServers[ix],\n            svrurl = server.url,\n            sizes = [350, 500, 750, 1000, 1500, 2000, 2500, 3000, 3500, 4000],\n            urls = [],\n            n,\n            i,\n            size;\n\n        for (n = 0; n < sizes.length; n++) {\n            size = sizes[n];\n            for (i = 0; i < 4; i++) {\n                urls.push(\n                    url.resolve(svrurl, 'random' + size + 'x' + size + '.jpg')\n                );\n            }\n        }\n\n        self.emit('testserver', server);\n\n        downloadSpeed.call(self, urls, options.maxTime, function (err, speed) {\n            if (err) return self.emit('error', err);\n            var fixed = (speed * speedTestDownloadCorrectionFactor) / 125000;\n            self.emit('downloadprogress', 100);\n            self.emit('downloadspeed', fixed);\n\n            if (speedInfo.downloadSpeed) {\n                if (speed > speedInfo.downloadSpeed) {\n                    speedInfo.downloadSpeed = speed;\n                    speedInfo.speedTestDownloadSpeed = fixed;\n                    speedInfo.bestServer = server;\n                }\n            } else {\n                speedInfo.downloadSpeed = speed;\n                speedInfo.speedTestDownloadSpeed = fixed;\n            }\n\n            startDownload(ix + 1);\n        });\n    }\n\n    function startUpload() {\n        var sizes = [],\n            sizesizes = [\n                Math.round(0.25 * 1000 * 1000),\n                Math.round(0.5 * 1000 * 1000),\n                Math.round(1 * 1000 * 1000),\n                Math.round(2 * 1000 * 1000),\n                Math.round(4 * 1000 * 1000),\n                Math.round(8 * 1000 * 1000),\n                Math.round(16 * 1000 * 1000),\n                Math.round(32 * 1000 * 1000),\n            ],\n            sizesize,\n            n,\n            i;\n\n        for (n = 0; n < sizesizes.length; n++) {\n            sizesize = sizesizes[n];\n            for (i = 0; i < 25; i++) {\n                sizes.push(sizesize);\n            }\n        }\n        self.emit('testserver', speedInfo.bestServer);\n        uploadSpeed.call(\n            self,\n            speedInfo.bestServer.url,\n            sizes,\n            options.maxTime,\n            function (err, speed) {\n                if (err) return self.emit('error', err);\n                speedInfo.uploadSpeed = speed;\n                speedInfo.speedTestUploadSpeed =\n                    (speed * speedTestUploadCorrectionFactor) / 125000;\n                self.emit('uploadprogress', 100);\n                self.emit('uploadspeed', speedInfo.speedTestUploadSpeed);\n\n                //emit results as nice, clean, object\n\n                /*\n      { url: 'http://208.54.87.70/speedtest/upload.jsp',\n        lat: '40.9419',\n        lon: '-74.2506',\n        name: 'Wayne, NJ',\n        country: 'United States',\n        cc: 'US',\n        sponsor: 'T-Mobile',\n        id: '1861',\n        host: '208.54.87.70:8080',\n        dist: 114.3911751633326,\n        bestPing: 37.36689500000001 }\n      */\n\n                function num(name) {\n                    speedInfo.config.client[name] = parseFloat(\n                        speedInfo.config.client[name]\n                    );\n                }\n\n                num('lat');\n                num('lon');\n                num('isprating');\n                num('rating');\n                num('ispdlavg');\n                num('ispulavg');\n\n                delete speedInfo.config.client.loggedin; //We're never logged in, so this is useless.\n\n                //Convert to megabits/s\n                speedInfo.config.client.ispdlavg /= 1000;\n                speedInfo.config.client.ispulavg /= 1000;\n\n                var best = speedInfo.bestServer,\n                    data = {\n                        speeds: {\n                            //Rounding, because these numbers look way more accurate than they are...\n                            download:\n                                Math.round(\n                                    speedInfo.speedTestDownloadSpeed * 1000\n                                ) / 1000,\n                            upload:\n                                Math.round(\n                                    speedInfo.speedTestUploadSpeed * 1000\n                                ) / 1000,\n                            originalDownload: Math.round(\n                                speedInfo.downloadSpeed\n                            ),\n                            originalUpload: Math.round(speedInfo.uploadSpeed),\n                        },\n                        client: speedInfo.config.client,\n                        server: {\n                            host: url.parse(best.url).host,\n                            lat: Number(best.lat),\n                            lon: Number(best.lon),\n                            location: best.name,\n                            country: best.country,\n                            cc: best.cc,\n                            sponsor: best.sponsor,\n                            distance: Math.round(best.dist * 100) / 100,\n                            distanceMi: Math.round(best.distMi * 100) / 100,\n                            ping: Math.round(best.bestPing * 10) / 10,\n                            id: best.id,\n                        },\n                    };\n                self.emit('data', data);\n                postResults();\n            }\n        );\n    }\n\n    function postResults() {\n        var best = speedInfo.bestServer,\n            md5 = function (v) {\n                return require('crypto')\n                    .createHash('md5')\n                    .update(v)\n                    .digest('hex');\n            },\n            dlspeedk = Math.round(speedInfo.speedTestDownloadSpeed * 1000),\n            ulspeedk = Math.round(speedInfo.speedTestUploadSpeed * 1000),\n            ping = Math.round(best.bestPing),\n            res = [\n                'download',\n                dlspeedk,\n                'ping',\n                ping,\n                'upload',\n                ulspeedk,\n                'promo',\n                '',\n                'startmode',\n                'pingselect', //or flyok, recommendedselect\n                'recommendedserverid',\n                best.id,\n                'accuracy',\n                1,\n                'serverid',\n                best.id,\n                'hash',\n                md5(ping + '-' + ulspeedk + '-' + dlspeedk + '-297aae72'),\n            ],\n            reportUrl = 'http://www.speedtest.net/api/api.php',\n            prms = [],\n            opts,\n            n;\n\n        for (n = 0; n < res.length; n += 2) {\n            prms.push(res[n] + '=' + encodeURIComponent(res[n + 1]));\n        }\n\n        opts = httpOpts(reportUrl);\n\n        opts.headers.referer = 'http://c.speedtest.net/flash/speedtest.swf';\n\n        postHttp(opts, prms.join('&'), function (err, data, status) {\n            if (err) return self.emit('error', err);\n            var match = String(data).match(/^resultid=(\\d+)(&|$)/),\n                resultUrl;\n            if (status === 200 && match && match[1]) {\n                //I get '0', don't know why. No one knows why.\n                resultUrl =\n                    'http://www.speedtest.net/result/' + match[1] + '.png';\n            }\n\n            speedInfo.resultUrl = resultUrl;\n\n            self.emit('result', resultUrl);\n            self.emit('done', speedInfo);\n        });\n    }\n\n    return self;\n}\n\nmodule.exports = speedTest;\n\nfunction visualSpeedTest(options, callback) {\n    require('draftlog').into(console);\n\n    // We only need chalk and DraftLog here. Lazy load it.\n    var chalk = require('chalk'),\n        test = speedTest(options),\n        log = function () {},\n        finalData,\n        percentage = 0,\n        speed = 0,\n        bar = console.draft(),\n        size = 50,\n        red = chalk.supportsColor ? chalk.bgRed(' ') : '─',\n        green = chalk.supportsColor ? chalk.bgGreen(' ') : '▇';\n    callback = once(callback);\n\n    if (options.log) {\n        if (typeof options.log === 'function') {\n            log = options.log;\n        } else {\n            log = console.log.bind(console);\n        }\n    }\n\n    test.on('error', function (err) {\n        callback(err);\n    });\n\n    test.on('testserver', function (server) {\n        log(\n            'Using server by ' +\n                server.sponsor +\n                ' in ' +\n                server.name +\n                ', ' +\n                server.country +\n                ' (' +\n                (server.distMi * 0.621371).toFixed(0) +\n                'mi, ' +\n                server.bestPing.toFixed(0) +\n                'ms)'\n        );\n    });\n\n    test.on('config', function (config) {\n        var client = config.client;\n        log(\n            'Testing from ' +\n                client.ip +\n                ' at ' +\n                client.isp +\n                ', expected dl: ' +\n                (client.ispdlavg / 8000).toFixed(2) +\n                'MB/s, expected ul: ' +\n                (client.ispulavg / 8000).toFixed(2) +\n                'MB/s'\n        );\n    });\n\n    function prog(what, pct, spd) {\n        percentage = pct || percentage;\n        speed = spd || speed;\n\n        var complete = Math.round((percentage / 100) * size),\n            barStr = '';\n        // What + padding\n        barStr += what;\n        barStr += ' '.repeat(12 - what.length);\n\n        // Bar\n        barStr += green.repeat(complete);\n        barStr += red.repeat(size - complete);\n\n        // Percent\n        pct = percentage + '%';\n        barStr += ' ' + pct;\n\n        // Speed\n        barStr += ' '.repeat(8 - pct.length) + speed;\n\n        bar(barStr);\n    }\n\n    test.on('downloadprogress', function (pct) {\n        prog('download', pct, null);\n    });\n\n    test.on('uploadprogress', function (pct) {\n        prog('upload', pct, null);\n    });\n\n    test.on('downloadspeed', function (speed) {\n        log('Download speed: ', speed.toFixed(2) + 'Mbps');\n\n        // Create a new line after each new download\n        bar = console.draft();\n    });\n\n    test.on('uploadspeed', function (speed) {\n        log('Upload speed: ', speed.toFixed(2) + 'Mbps');\n\n        // Create a new line after each new upload\n        bar = console.draft();\n    });\n\n    test.on('downloadspeedprogress', function (speed) {\n        prog('download', null, speed.toFixed(2) + 'Mbps');\n    });\n\n    test.on('uploadspeedprogress', function (speed) {\n        prog('upload', null, speed.toFixed(2) + 'Mbps');\n    });\n\n    test.on('data', function (data) {\n        finalData = data;\n    });\n\n    test.on('result', function (url) {\n        log('Results url: ' + url);\n    });\n\n    test.on('done', function () {\n        callback(null, finalData);\n    });\n\n    return test;\n}\n\nspeedTest.visual = visualSpeedTest;\n"]}