libreavous-audio-reader/js/wavesurfer.min.js.map

1 line
220 KiB
Plaintext

{"version":3,"file":"wavesurfer.min.js","mappings":";;;;;CAAA,SAA2CA,EAAMC,GAC1B,iBAAZC,SAA0C,iBAAXC,OACxCA,OAAOD,QAAUD,IACQ,mBAAXG,QAAyBA,OAAOC,IAC9CD,OAAO,aAAc,GAAIH,GACC,iBAAZC,QACdA,QAAoB,WAAID,IAExBD,EAAiB,WAAIC,IARvB,CASGK,MAAM,WACT,O,gGCNA,gBACA,W,6NASqBC,EAAAA,WACjB,c,4FAAc,SAMVD,KAAKE,KAAO,KAMZF,KAAKG,QAAU,KAMfH,KAAKI,SAAW,KAMhBJ,KAAKK,YAAc,KAMnBL,KAAKM,MAAQ,EAMbN,KAAKO,IAAM,EAMXP,KAAKQ,IAAK,kBAC2B,IAA1BR,KAAKS,YAAYC,KAClBV,KAAKS,YAAYC,KAAKC,cAAgB,IACtC,gBAOVX,KAAKY,wBAA0B,G,+CAQnC,SAASC,GACLb,KAAKE,KAAOW,EACZb,KAAKG,QAAUH,KAAKE,KAAKY,WAAW,KAAMd,KAAKY,2B,0BASnD,SAAaC,GACTb,KAAKI,SAAWS,EAChBb,KAAKK,YAAcL,KAAKI,SAASU,WAC7B,KACAd,KAAKY,2B,8BAYb,SAAiBG,EAAcC,EAAYC,EAAOC,GAG9ClB,KAAKM,MAAQN,KAAKE,KAAKiB,WAAaH,GAAc,EAClDhB,KAAKO,IAAMP,KAAKM,MAAQS,EAAeC,EAGvChB,KAAKE,KAAKe,MAAQA,EAClBjB,KAAKE,KAAKgB,OAASA,EACnB,IAAIE,EAAc,CAAEH,MAAOF,EAAe,OAC1C,aAAMf,KAAKE,KAAMkB,GAEbpB,KAAKqB,oBAELrB,KAAKI,SAASa,MAAQA,EACtBjB,KAAKI,SAASc,OAASA,GACvB,aAAMlB,KAAKI,SAAUgB,M,uBAO7B,WAEIpB,KAAKG,QAAQmB,UACT,EACA,EACAtB,KAAKG,QAAQoB,OAAON,MACpBjB,KAAKG,QAAQoB,OAAOL,QAIpBlB,KAAKqB,mBACLrB,KAAKK,YAAYiB,UACb,EACA,EACAtB,KAAKK,YAAYkB,OAAON,MACxBjB,KAAKK,YAAYkB,OAAOL,U,2BAWpC,SAAcM,EAAWC,GACrBzB,KAAKG,QAAQuB,UAAYF,EAErBxB,KAAKqB,oBACLrB,KAAKK,YAAYqB,UAAYD,K,mCASrC,SAAsBE,GACdA,IAEA3B,KAAKG,QAAQyB,aAAa,EAAG,EAAG,EAAG,EAAG,EAAG,GAErC5B,KAAKqB,mBACLrB,KAAKK,YAAYuB,aAAa,EAAG,EAAG,EAAG,EAAG,EAAG,M,uBAczD,SAAUC,EAAGC,EAAGb,EAAOC,EAAQa,GAC3B/B,KAAKgC,kBAAkBhC,KAAKG,QAAS0B,EAAGC,EAAGb,EAAOC,EAAQa,GAEtD/B,KAAKqB,mBACLrB,KAAKgC,kBACDhC,KAAKK,YACLwB,EACAC,EACAb,EACAC,EACAa,K,+BAeZ,SAAkBE,EAAKJ,EAAGC,EAAGb,EAAOC,EAAQa,GACnCE,IAIDF,EACA/B,KAAKkC,gBAAgBD,EAAKJ,EAAGC,EAAGb,EAAOC,EAAQa,GAE/CE,EAAIE,SAASN,EAAGC,EAAGb,EAAOC,M,6BAiBlC,SAAgBe,EAAKJ,EAAGC,EAAGb,EAAOC,EAAQa,GACvB,IAAXb,IAKAA,EAAS,IAETY,GADAZ,IAAW,GAGfe,EAAIG,YACJH,EAAII,OAAOR,EAAIE,EAAQD,GACvBG,EAAIK,OAAOT,EAAIZ,EAAQc,EAAQD,GAC/BG,EAAIM,iBAAiBV,EAAIZ,EAAOa,EAAGD,EAAIZ,EAAOa,EAAIC,GAClDE,EAAIK,OAAOT,EAAIZ,EAAOa,EAAIZ,EAASa,GACnCE,EAAIM,iBACAV,EAAIZ,EACJa,EAAIZ,EACJW,EAAIZ,EAAQc,EACZD,EAAIZ,GAERe,EAAIK,OAAOT,EAAIE,EAAQD,EAAIZ,GAC3Be,EAAIM,iBAAiBV,EAAGC,EAAIZ,EAAQW,EAAGC,EAAIZ,EAASa,GACpDE,EAAIK,OAAOT,EAAGC,EAAIC,GAClBE,EAAIM,iBAAiBV,EAAGC,EAAGD,EAAIE,EAAQD,GACvCG,EAAIO,YACJP,EAAIQ,U,uBAeR,SAAUC,EAAOC,EAAQC,EAAOC,EAASvC,EAAOC,GAC5CP,KAAK8C,kBACD9C,KAAKG,QACLuC,EACAC,EACAC,EACAC,EACAvC,EACAC,GAGAP,KAAKqB,mBACLrB,KAAK8C,kBACD9C,KAAKK,YACLqC,EACAC,EACAC,EACAC,EACAvC,EACAC,K,+BAkBZ,SAAkB0B,EAAKS,EAAOC,EAAQC,EAAOC,EAASvC,EAAOC,GACzD,GAAK0B,EAAL,CAIA,IAuBIc,EAAGC,EAAMC,EAvBPC,EAASR,EAAMQ,OAAS,EACxBC,EAAQC,KAAKC,MAAMH,EAASlD,KAAKM,OAMjCgD,EAAcH,EACdI,EAHOH,KAAKC,MAAMH,EAASlD,KAAKO,KAAO,EAIvCiD,EAAQxD,KAAKE,KAAKe,OAASsC,EAAYD,EAAc,GAGrDG,EAAab,EAAQC,EACrBa,EAAaf,EAASC,EAW5B,IATAX,EAAIG,YACJH,EAAII,QAAQiB,EAAcH,GAASK,EAAOC,GAE1CxB,EAAIK,QACCgB,EAAcH,GAASK,EACxBC,EAAaL,KAAKC,OAAOX,EAAM,EAAIY,IAAgB,GAAKI,IAIvDX,EAAIO,EAAaP,EAAIQ,EAAWR,IACjCC,EAAON,EAAM,EAAIK,IAAM,EACvBE,EAAIG,KAAKC,MAAML,EAAOU,GACtBzB,EAAIK,QAAQS,EAAII,GAASK,EAAQxD,KAAK2D,UAAWF,EAAaR,GAMlE,IADA,IAAIW,EAAIL,EAAY,EACZK,GAAKN,EAAaM,IACtBZ,EAAON,EAAM,EAAIkB,EAAI,IAAM,EAC3BX,EAAIG,KAAKC,MAAML,EAAOU,GACtBzB,EAAIK,QAAQsB,EAAIT,GAASK,EAAQxD,KAAK2D,UAAWF,EAAaR,GAGlEhB,EAAIK,QACCgB,EAAcH,GAASK,EACxBC,EACIL,KAAKC,OAAOX,EAAM,EAAIY,EAAc,IAAM,GAAKI,IAGvDzB,EAAIO,YACJP,EAAIQ,U,qBAMR,WACIzC,KAAKG,QAAU,KACfH,KAAKE,KAAO,KAEZF,KAAKK,YAAc,KACnBL,KAAKI,SAAW,O,sBAgBpB,SAASyD,EAAQC,EAASC,GAAM,WAC5B,MAAa,SAATA,EACO,IAAIC,SAAQ,SAAAC,GACf,EAAK/D,KAAKgE,OAAOD,EAASJ,EAAQC,MAEtB,YAATC,EACA/D,KAAKE,KAAKiE,UAAUN,EAAQC,QADhC,O,6BA7XM7D,G,mWCdrB,M,ybAAA,S,4mCAOqBmE,EAAAA,SAAAA,I,yPAKjB,WAAYC,EAAWC,GAAQ,a,4FAAA,UAC3B,gBAEKD,UAAYE,EAAKC,gBAAgBH,EAAWC,EAAO3C,UAIxD,EAAK2C,OAASA,EAKd,EAAKrD,MAAQ,EAKb,EAAKC,OAASoD,EAAOpD,OAAS,EAAKoD,OAAOG,WAE1C,EAAKC,QAAU,EAKf,EAAKC,QAAU,KAxBY,E,kCAkC/B,SAAMC,EAAIC,GACN,OAAON,EAAKO,MAAMF,EAAIC,K,2BAO1B,WACI7E,KAAK2E,QAAUJ,EAAKC,gBAChBxE,KAAKqE,UAAUU,YAAYC,SAASC,cAAc,SAClDjF,KAAKsE,OAAO3C,UAGhB3B,KAAK8E,MAAM9E,KAAK2E,QAAS,CACrBO,QAAS,QACTC,SAAU,WACVC,WAAY,OACZC,iBAAkB,OAClBnE,OAAQlB,KAAKsE,OAAOpD,OAAS,QAG7BlB,KAAKsE,OAAOgB,YAActF,KAAKsE,OAAOiB,eACtCvF,KAAK8E,MAAM9E,KAAK2E,QAAS,CACrB1D,MAAO,OACPuE,UAAWxF,KAAKsE,OAAOmB,cAAgB,SAAW,OAClDC,UAAW,WAInB1F,KAAK2F,uB,yBAUT,SAAYC,EAAGC,IACVA,GAAaD,EAAEE,iBAEhB,IAUI1F,EAVE2F,EAAUxB,EAAKC,gBACjBoB,EAAEI,cAAgBJ,EAAEI,cAAc,GAAKJ,EACvC5F,KAAKsE,OAAO3C,UACdoE,QACIE,EAAOjG,KAAK2E,QAAQuB,wBAEpBC,EAAenG,KAAKiB,MACpBmF,EAAcpG,KAAKqG,WACnBC,EAAiBtG,KAAKuG,kBAAkBN,EAAMF,GAWpD,OAPI3F,GADCJ,KAAKsE,OAAOgB,YAAca,EAAeC,EAC/BE,GACNtG,KAAKsE,OAAOG,WAAa0B,IAAiB,GAEnCG,EAAiBtG,KAAK2E,QAAQ6B,YACtCxG,KAAK2E,QAAQ8B,aAAe,EAG7BlC,EAAKmC,MAAMtG,EAAU,EAAG,K,+BAGnC,SAAkBuG,EAAaZ,GAC3B,OAAI/F,KAAKsE,OAAOsC,IACLD,EAAYE,MAAQd,EAEpBA,EAAUY,EAAYG,O,gCAIrC,WAAqB,WACjB9G,KAAK2E,QAAQoC,iBAAiB,SAAS,SAAAnB,GACnC,IAAMoB,EAAgBzC,EAAKC,gBAAgBoB,EAAG,EAAKtB,OAAO3C,UACpDsF,EAAkB,EAAKtC,QAAQuC,aAC/B,EAAKvC,QAAQwC,aAEnB,GAAwB,IAApBF,EAAuB,CAEvB,IAAMhB,EAAO,EAAKtB,QAAQuB,wBAC1B,GAAIc,EAAcI,SAAWnB,EAAKoB,OAASJ,EAEvC,OAIJ,EAAK3C,OAAOgD,UACZ,EAAKC,UAAU,QAAS3B,EAAG,EAAK4B,YAAY5B,OAIpD5F,KAAK2E,QAAQoC,iBAAiB,YAAY,SAAAnB,GAClC,EAAKtB,OAAOgD,UACZ,EAAKC,UAAU,WAAY3B,EAAG,EAAK4B,YAAY5B,OAIvD5F,KAAK2E,QAAQoC,iBAAiB,UAAU,SAAAnB,GAAC,OACrC,EAAK2B,UAAU,SAAU3B,Q,uBAejC,SAAUlD,EAAOQ,EAAQ5C,EAAOC,GACvBP,KAAKyH,SAASvE,IACflD,KAAK0H,YAGT1H,KAAKsE,OAAOqD,SACN3H,KAAK4H,SAASlF,EAAO,EAAGpC,EAAOC,GAC/BP,KAAK6H,SAASnF,EAAO,EAAGpC,EAAOC,K,yBAMzC,WACyB,OAAjBP,KAAK2E,UACL3E,KAAK2E,QAAQ6B,WAAa,K,sBASlC,SAASsB,GACL,IAAM3C,EAAWnF,KAAK2E,QAAQ8B,YAAcqB,EAC5C9H,KAAK+H,mBAAmB5C,GAAU,K,gCAUtC,SAAmBA,EAAU6C,GACzB,IAAMxB,EAAaxG,KAAK2E,QAAQ6B,WAC1ByB,KAAUjI,KAAK2E,QAAQuD,YAAc,GACrCC,EAAYnI,KAAK2E,QAAQ8B,YAAczG,KAAK2E,QAAQuD,YACtDE,EAASjD,EAAW8C,EACpBI,EAASD,EAAS5B,EAEtB,GAAiB,GAAb2B,EAAJ,CAMA,IAAKH,IAAcC,GAAQI,GAAUA,EAASJ,EAAM,CAEhD,IAAIK,EAAOtI,KAAKsE,OAAOiE,eAGvBD,GAAQL,EACRK,GAAQH,EAGRC,EAAS5B,GADT6B,EAASjF,KAAKoF,KAAKF,EAAMlF,KAAKqF,IAAIH,EAAMD,MAK5CD,EAAShF,KAAKoF,IAAI,EAAGpF,KAAKqF,IAAIN,EAAWC,MAE3B5B,IACVxG,KAAK2E,QAAQ6B,WAAa4B,M,wBASlC,WACI,IAAIvG,EAAI,EACR,GAAI7B,KAAK2E,QAAS,CACd,IAAMF,EAAazE,KAAKsE,OAAOG,WAQ/B,GAPA5C,EAAIuB,KAAKC,MAAMrD,KAAK2E,QAAQ6B,WAAa/B,GAOrCzE,KAAKsE,OAAOiB,aAAc,CAC1B,IAAM4C,KACFnI,KAAK2E,QAAQ8B,YAAchC,EAC3BzE,KAAKqG,YAETxE,EAAIuB,KAAKqF,IAAIN,EAAW/E,KAAKoF,IAAI,EAAG3G,KAG5C,OAAOA,I,sBAQX,WACI,OAAOuB,KAAKC,MAAMrD,KAAKqE,UAAU6D,YAAclI,KAAKsE,OAAOG,c,sBAS/D,SAASxD,GACL,GAAIjB,KAAKiB,OAASA,EACd,OAAO,EAKX,GAFAjB,KAAKiB,MAAQA,EAETjB,KAAKsE,OAAOgB,YAActF,KAAKsE,OAAOiB,aACtCvF,KAAK8E,MAAM9E,KAAK2E,QAAS,CACrB1D,MAAO,SAER,CACH,IAAMyH,KAAc1I,KAAKiB,MAAQjB,KAAKsE,OAAOG,YAAc,KAC3DzE,KAAK8E,MAAM9E,KAAK2E,QAAS,CACrB1D,MAAOyH,IAKf,OADA1I,KAAK2I,cACE,I,uBASX,SAAUzH,GACN,OAAIA,GAAUlB,KAAKkB,SAGnBlB,KAAKkB,OAASA,EAEdlB,KAAK8E,MAAM9E,KAAK2E,QAAS,CACrBzD,UAAWlB,KAAKkB,OAASlB,KAAKsE,OAAOG,YAAc,OAGvDzE,KAAK2I,cACE,K,sBAQX,SAASvI,GACL,IAAMwI,EAAa,EAAI5I,KAAKsE,OAAOG,WAC7BoE,EAAMzF,KAAKC,MAAMjD,EAAWJ,KAAKiB,OAAS2H,EAEhD,GAAIC,EAAM7I,KAAK0E,SAAWmE,EAAM7I,KAAK0E,SAAWkE,EAAY,CAGxD,GAFA5I,KAAK0E,QAAUmE,EAEX7I,KAAKsE,OAAOiB,cAAgBvF,KAAKsE,OAAOwE,WAAY,CACpD,IAAMC,KAAY/I,KAAK2E,QAAQ8B,YAAcrG,GAC7CJ,KAAK+H,mBACDgB,EACA/I,KAAKsE,OAAO0E,uBAIpBhJ,KAAKiJ,eAAeJ,M,qBAO5B,WACI7I,KAAKkJ,QACDlJ,KAAK2E,UACD3E,KAAK2E,QAAQwE,YAAcnJ,KAAKqE,UAAU+E,YAC1CpJ,KAAKqE,UAAUgF,YAAYrJ,KAAK2E,QAAQyE,YAE5CpJ,KAAK2E,QAAU,Q,0BAWvB,c,wBAOA,c,sBAeA,SAASjC,EAAO4G,EAAchJ,EAAOC,M,sBAerC,SAASmC,EAAO4G,EAAchJ,EAAOC,M,uBAOrC,c,4BAQA,SAAe4E,S,6BAnZEf,CAAeG,EAAKgF,U,mWCPzC,gBACA,E,ybAAA,SACA,Y,+pCASqBC,EAAAA,SAAAA,I,yPAKjB,WAAYnF,EAAWC,GAAQ,a,4FAAA,UAC3B,cAAMD,EAAWC,IAKZmF,eAAiBnF,EAAOmF,eAK7B,EAAKC,sBAAwBtG,KAAKC,MAC9BiB,EAAOmF,eAAiBnF,EAAOG,YASnC,EAAKpD,kBAAoBiD,EAAO9C,WAAa8C,EAAO7C,cAKpD,EAAKkC,UAAY,GAAMW,EAAOG,WAO9B,EAAKkF,SAAW,GAKhB,EAAKC,aAAe,KAOpB,EAAKC,WAAa5J,EAAAA,QAOlB,EAAKW,wBAA0B0D,EAAOwF,yBAQtC,EAAKC,QAAU,EAAI3G,KAAK4G,KAAK1F,EAAOG,WAAa,GAOjD,EAAKwF,UAAY3F,EAAO2F,WAAa,EAOrC,EAAKtI,SAAW2C,EAAO3C,SA1EI,E,iCAgF/B,WACI3B,KAAKkK,gBACLlK,KAAKmK,mB,4BAOT,WACInK,KAAK4J,aAAerF,EAAKC,gBACrBxE,KAAK2E,QAAQI,YAAYC,SAASC,cAAc,SAChDjF,KAAKsE,OAAO3C,UAEhB3B,KAAK8E,MAAM9E,KAAK4J,aAAc,CAC1BzE,SAAU,WACViF,OAAQ,EACRtD,KAAM,EACNuD,IAAK,EACLhD,OAAQ,EACRiD,SAAU,SACVrJ,MAAO,IACPiE,QAAS,OACTqF,UAAW,aACXC,iBAAkB,QAClBC,cAAe,SAGnBzK,KAAK0K,YACL1K,KAAK2K,iB,0BAMT,WACI3K,KAAK8E,MAAM9E,KAAK4J,aAAc,CAC1BgB,iBAAkB5K,KAAKsE,OAAOuG,YAAc,KAC5CC,iBAAkB9K,KAAKsE,OAAOyG,gB,wBAOtC,WAOI,IAPS,WACH/J,EAAaoC,KAAKC,MAAMrD,KAAKiB,MAAQjB,KAAKsE,OAAOG,YACjDuG,EAAmB5H,KAAK4G,KAC1BhJ,GAAchB,KAAK0J,sBAAwB1J,KAAK+J,UAI7C/J,KAAK2J,SAASzG,OAAS8H,GAC1BhL,KAAK0K,YAIT,KAAO1K,KAAK2J,SAASzG,OAAS8H,GAC1BhL,KAAKiL,eAGT,IAAIC,EAAclL,KAAKyJ,eAAiBzJ,KAAK+J,QACvCoB,EAAanL,KAAK2J,SAASzG,OAAS,EAC1ClD,KAAK2J,SAASyB,SAAQ,SAACC,EAAOtI,GACtBA,GAAKoI,IACLD,EAAc,EAAKjK,MAAQ,EAAKwI,eAAiB0B,GAErD,EAAKG,iBAAiBD,EAAOH,EAAa,EAAKhK,QAE/CmK,EAAM3D,iB,uBAQd,WACI,IAAM2D,EAAQ,IAAIrL,KAAK6J,WACvBwB,EAAMzK,wBAA0BZ,KAAKY,wBACrCyK,EAAMhK,kBAAoBrB,KAAKqB,kBAC/BgK,EAAM1H,UAAY3D,KAAK2D,UACvB,IAAM4H,EAAavL,KAAK0J,sBAAwB1J,KAAK2J,SAASzG,OAG1DhD,EAAOqE,EAAKC,gBACZxE,KAAK2E,QAAQI,YAAYC,SAASC,cAAc,WAChDjF,KAAKsE,OAAO3C,UAchB,GAZA3B,KAAK8E,MAAM5E,EAAM,CACbiF,SAAU,WACViF,OAAQ,EACRtD,KAAMyE,EAAa,KACnBlB,IAAK,EACLhD,OAAQ,EACRnG,OAAQ,OACRuJ,cAAe,SAEnBY,EAAMG,SAAStL,GAGXF,KAAKqB,kBAAmB,CACxB,IAAIjB,EAAWmE,EAAKC,gBAChBxE,KAAK4J,aAAa7E,YAAYC,SAASC,cAAc,WACrDjF,KAAKsE,OAAO3C,UAEhB3B,KAAK8E,MAAM1E,EAAU,CACjB+E,SAAU,WACV2B,KAAMyE,EAAa,KACnBlB,IAAK,EACLhD,OAAQ,EACRnG,OAAQ,SAEZmK,EAAMI,aAAarL,GAGvBJ,KAAK2J,SAAS+B,KAAKL,K,0BAOvB,WACI,IAAIM,EAAY3L,KAAK2J,SAAS3J,KAAK2J,SAASzG,OAAS,GAGrDyI,EAAUzL,KAAK0L,cAAcvC,YAAYsC,EAAUzL,KAAKkJ,YAGpDpJ,KAAKqB,mBACLsK,EAAUvL,SAASwL,cAAcvC,YAAYsC,EAAUvL,SAASgJ,YAIhEuC,IACAA,EAAUE,UACVF,EAAY,MAGhB3L,KAAK2J,SAASmC,Q,8BAUlB,SAAiBT,EAAOpK,EAAOC,GAC3B,IAAMH,EAAeqC,KAAKC,MAAMpC,EAAQjB,KAAKsE,OAAOG,YAC9CzD,EAAaoC,KAAKC,MAAMrD,KAAKiB,MAAQjB,KAAKsE,OAAOG,YAGvD4G,EAAMC,iBAAiBvK,EAAcC,EAAYC,EAAOC,GAGxDlB,KAAK8E,MAAM9E,KAAK4J,aAAc,CAAE1E,QAAS,Y,uBAM7C,WAAY,WACRX,EAAKwH,OAAM,WACP,EAAKpC,SAASyB,SAAQ,SAAAC,GAAK,OAAIA,EAAM3D,iBADzCnD,K,sBAkBJ,SAAS7B,EAAO4G,EAAchJ,EAAOC,GAAK,WACtC,OAAOP,KAAKgM,YACRtJ,EACA4G,EACAhJ,EACAC,GACA,YAA6E,IAA1EoC,EAA0E,EAA1EA,OAAQsJ,EAAkE,EAAlEA,WAAoBpJ,GAA8C,EAAtD3B,OAAsD,EAA9C2B,SAASD,EAAqC,EAArCA,MAAOF,EAA8B,EAA9BA,MAAqBwJ,EAAS,EAAvB5C,aAGlD,QAAc6C,IAAV7L,EAqBJ,IAjBA,IAAM8L,EAAiBH,EAAa,EAAI,EAClC/I,EAASR,EAAMQ,OAASkJ,EACxBC,EAAM,EAAK/H,OAAOqD,SAAW,EAAKrD,OAAOG,WAQzC6H,EAAOD,GANc,OAAvB,EAAK/H,OAAOiI,OACNnJ,KAAKoF,IAAI,EAAKlE,OAAOG,cAAe4H,EAAM,IAC1CjJ,KAAKoF,IACH,EAAKlE,OAAOG,WACZ,EAAKH,OAAOiI,OAAS,EAAKjI,OAAOG,aAIvCjB,EAAQN,EAAS,EAAKjC,MAEtBuL,EAAOjM,EACTwC,EAFUzC,EAINyC,EAAIyJ,EAAMzJ,GAAKuJ,EAAM,CACzB,IAAMtJ,EACFN,EAAMU,KAAKqJ,MAAM1J,EAAIS,EAAQ4I,KAAoB,EACjDnJ,EAAIG,KAAKC,MAAOL,EAAOL,EAAUC,GAI5B,GAALK,GAAU,EAAKqB,OAAOoI,eACtBzJ,EAAI,EAAKqB,OAAOoI,cAGpB,EAAKvK,SACDY,EAAI,EAAKY,UACTf,EAAQK,EAAIJ,EACZwJ,EAAM,EAAK1I,UACP,EAAJV,EACA,EAAKgH,UACLiC,S,sBAoBpB,SAASxJ,EAAO4G,EAAchJ,EAAOC,GAAK,WACtC,OAAOP,KAAKgM,YACRtJ,EACA4G,EACAhJ,EACAC,GACA,YAAyE,IAAtEoC,EAAsE,EAAtEA,OAAQsJ,EAA8D,EAA9DA,WAAoBpJ,GAA0C,EAAlD3B,OAAkD,EAA1C2B,SAASD,EAAiC,EAAjCA,MAAOF,EAA0B,EAA1BA,MAAO4G,EAAmB,EAAnBA,aAClD,IAAK2C,EAAY,CAIb,IAHA,IAAMU,EAAiB,GACjBC,EAAMlK,EAAMQ,OACdH,EAAI,EACAA,EAAI6J,EAAK7J,IACb4J,EAAe,EAAI5J,GAAKL,EAAMK,GAC9B4J,EAAe,EAAI5J,EAAI,IAAML,EAAMK,GAEvCL,EAAQiK,OAKER,IAAV7L,GACA,EAAKuM,SAASnK,EAAOC,EAAQC,EAAOC,EAASvC,EAAOC,EAAK+I,GAI7D,EAAKnH,SACD,EACAS,EAAQC,EAAU,EAAKc,UACvB,EAAK1C,MACL,EAAK0C,UACL,EAAKsG,UACLX,Q,sBAmBhB,SAAS5G,EAAOC,EAAQC,EAAOC,EAASvC,EAAOC,EAAK+I,GAAc,WAC9D,EAAqCtJ,KAAKsE,OAAOwI,qBAAqBC,cAAczD,IAAiB,GAA7F9H,EAAR,EAAQA,UAAWC,EAAnB,EAAmBA,cACnBzB,KAAK2J,SAASyB,SAAQ,SAACC,EAAOtI,GAC1B,EAAKiK,cAAc3B,EAAO7J,EAAWC,GACrC,EAAKwL,sBAAsB5B,EAAO,EAAK/G,OAAO3C,UAC9C0J,EAAM6B,UAAUxK,EAAOC,EAAQC,EAAOC,EAASvC,EAAOC,Q,sBAc9D,SAASsB,EAAGC,EAAGb,EAAOC,EAAQa,EAAQuH,GAOlC,IANA,IAAM6D,EAAc/J,KAAKqJ,MAAM5K,EAAI7B,KAAKyJ,gBAClC2D,EAAYhK,KAAKqF,IACnBrF,KAAK4G,MAAMnI,EAAIZ,GAASjB,KAAKyJ,gBAAkB,EAC/CzJ,KAAK2J,SAASzG,QAEdH,EAAIoK,EACApK,EAAIqK,EAAWrK,IAAK,CACxB,IAAMsI,EAAQrL,KAAK2J,SAAS5G,GACtBwI,EAAaxI,EAAI/C,KAAKyJ,eAEtB4D,EAAe,CACjBC,GAAIlK,KAAKoF,IAAI3G,EAAGkB,EAAI/C,KAAKyJ,gBACzB8D,GAAIzL,EACJ0L,GAAIpK,KAAKqF,IACL5G,EAAIZ,EACJ8B,EAAI/C,KAAKyJ,eAAiB4B,EAAMnL,KAAKe,OAEzCwM,GAAI3L,EAAIZ,GAGZ,GAAImM,EAAaC,GAAKD,EAAaG,GAAI,CACnC,MAAqCxN,KAAKsE,OAAOwI,qBAAqBC,cAAczD,IAAiB,GAA7F9H,EAAR,EAAQA,UAAWC,EAAnB,EAAmBA,cACnBzB,KAAKgN,cAAc3B,EAAO7J,EAAWC,GACrCzB,KAAKiN,sBAAsB5B,EAAOrL,KAAKsE,OAAO3C,UAE9C0J,EAAMqC,UACFL,EAAaC,GAAK/B,EAClB8B,EAAaE,GACbF,EAAaG,GAAKH,EAAaC,GAC/BD,EAAaI,GAAKJ,EAAaE,GAC/BxL,O,yBAYhB,SAAYuH,GACR,OAAOtJ,KAAKsE,OAAOqJ,eAAiB3N,KAAKsE,OAAOwI,qBAAqBc,eAAeC,SAASvE,K,yBAoBjG,SAAY5G,EAAO4G,EAAchJ,EAAOC,EAAKuN,EAAIC,EAAWC,GAAe,WACvE,OAAOzJ,EAAKwH,OAAM,WAEd,GAAIrJ,EAAM,aAAcuL,MAAO,CAC3B,IAAMC,EAAWxL,EAEjB,GAAI,EAAK4B,OAAOqJ,cAAe,CAC3B,IASIQ,EATEC,EAAmBF,EAASG,QAAO,SAACC,EAAGvL,GAAJ,OAAW,EAAKwL,YAAYxL,MAgBrE,OAfK,EAAKuB,OAAOwI,qBAAqB0B,SAClC,EAAKC,UACDrL,KAAKoF,IAAI4F,EAAiBlL,OAAQ,GAC9B,EAAKoB,OAAOpD,OACZ,EAAKoD,OAAOG,YAKpB,EAAKH,OAAOwI,sBAAwB,EAAKxI,OAAOwI,qBAAqB4B,wBAErEP,EAAgB5J,EAAKiE,IAAI0F,EAASS,KAAK,SAAAC,GAAY,OAAIrK,EAAKsK,OAAOD,QAIhEV,EAAS9C,SAAQ,SAACwD,EAAc7L,GAAf,OACpB,EAAKiJ,YAAY4C,EAAc7L,EAAGzC,EAAOC,EAAKuN,EAAIM,EAAiBU,QAAQF,GAAeT,MAGlGzL,EAAQwL,EAAS,GAIrB,IAAI,EAAKK,YAAYjF,GAArB,CAOA,IAAI3G,EAAS,EAAI,EAAK2B,OAAOyK,UACzB,EAAKzK,OAAO0K,YACZrM,OAA2BwJ,IAAlB6B,EAA8BzJ,EAAKsK,OAAOnM,GAASsL,GAKhE,IAAM/B,EAAa,GAAGgD,KAAKC,KAAKxM,GAAO,SAAAyM,GAAG,OAAIA,EAAM,KAC9CjO,EAAS,EAAKoD,OAAOpD,OAAS,EAAKoD,OAAOG,WAC1C7B,EAAQ1B,EAAS,EAEnB2B,EAAU3B,EAAS6M,GAAa,EAOpC,OAJI,EAAKzJ,OAAOwI,sBAAwB,EAAKxI,OAAOwI,qBAAqB0B,UACrE3L,EAAU,GAGPiL,EAAG,CACNnL,OAAQA,EACRsJ,WAAYA,EACZ/K,OAAQA,EACR2B,QAASA,EACTD,MAAOA,EACPF,MAAOA,EACP4G,aAAcA,OA9Df/E,K,2BA0EX,SAAc8G,GAAqF,IAA9E7J,EAA8E,uDAAlExB,KAAKsE,OAAO9C,UAAWC,EAA2C,uDAA3BzB,KAAKsE,OAAO7C,cAChF4J,EAAM2B,cAAcxL,EAAWC,K,mCASnC,SAAsB4J,GAAyB,IAAlB1J,EAAkB,wDAC3C0J,EAAM4B,sBAAsBtL,K,sBAiBhC,SAASkC,EAAQC,EAASC,GACtB,GAAa,SAATA,EACA,OAAOC,QAAQoL,IACXpP,KAAK2J,SAASgF,KAAI,SAAAtD,GACd,OAAOA,EAAMgE,SAASxL,EAAQC,EAASC,OAG5C,GAAa,YAATA,EAAoB,CAC3B,IAAIuL,EAAStP,KAAK2J,SAASgF,KAAI,SAAAtD,GAAK,OAChCA,EAAMgE,SAASxL,EAAQC,EAASC,MAEpC,OAAOuL,EAAOpM,OAAS,EAAIoM,EAASA,EAAO,M,4BASnD,SAAenK,GACXnF,KAAK8E,MAAM9E,KAAK4J,aAAc,CAAE3I,MAAOkE,EAAW,Y,6BAhlBrCqE,CAAoBpF,EAAAA,S,iSCXzC,I,w1CAWqBmL,EAAAA,SAAAA,I,yPAMjB,WAAYjL,GAAQ,a,4FAAA,UAChB,cAAMA,IAEDA,OAASA,EAEd,EAAKkL,mBAAqB,KALV,E,iCAWpB,WACIxP,KAAKyP,gBAAgBzP,KAAKsE,OAAOoL,WACjC1P,KAAK2P,cACL3P,KAAK4P,mBACL5P,KAAK6P,mBACL7P,KAAK8P,uB,mBAWT,SAAMC,EAAOrN,EAAOsN,GAChB,yCAAYD,EAAOrN,EAAOsN,GAC1BhQ,KAAKiQ,yBAAyBF,K,sCASlC,SAAyBG,GACrBlQ,KAAKwP,mBAAqBxP,KAAKmQ,GAAGF,yBAC9BC,GAEJlQ,KAAKwP,mBAAmBY,QAAQpQ,KAAKqQ,Y,kBAGzC,SAAK/P,EAAOC,GAER,OADAP,KAAKsQ,qBACL,wCAAkBhQ,EAAOC,K,qBAO7B,WACI,4CAEAP,KAAKuQ,uB,6BA/DQhB,G,EAXrB,S,4BAWkDiB,S,mWCXlD,I,EAAA,G,EAAA,S,2BACA,E,ybAAA,S,m5CAKqBA,EAAAA,SAAAA,I,yPAMjB,WAAYlM,GAAQ,a,4FAAA,UAChB,cAAMA,IAEDA,OAASA,EAOd,EAAKyL,MAAQ,CACTU,YAAa,EACbC,SAAU,EACVC,QAAQ,EACRC,aAAc,EACdC,KALS,aAMTC,MANS,aAOTC,OAAQ,GAIZ,EAAKC,UAAY1M,EAAO0M,UAAUrQ,cAElC,EAAKsQ,gBAAkB3M,EAAO2M,gBAE9B,EAAKvO,MAAQ,KAEb,EAAKkO,aAAe,EAEpB,EAAKG,OAAS,EAEd,EAAKG,SAAU,EAEf,EAAKC,OAAS,KAEd,EAAKC,UAAY,KAEjB,EAAKC,eAAiB,GArCN,E,iCA2CpB,WACIrR,KAAKyP,gBAAgBzP,KAAKsE,OAAOoL,WACjC1P,KAAK2P,gB,kCAMT,WAAuB,WACnB3P,KAAKqR,eAAeC,MAAQ,WACxB,EAAK/J,UAAU,QAAS,gCAE5BvH,KAAKqR,eAAeE,QAAU,WAC1B,EAAKhK,UAAU,YAEnBvH,KAAKqR,eAAeG,MAAQ,WACxB,EAAKjK,UAAU,WAInBvH,KAAKqR,eAAeR,KAAO,WACvB,EAAKtJ,UAAU,SAEnBvH,KAAKqR,eAAeP,MAAQ,WACxB,EAAKvJ,UAAU,UAEnBvH,KAAKqR,eAAeI,OAAS,SAAAC,GACzB,EAAKnK,UAAU,SAEnBvH,KAAKqR,eAAeM,aAAe,SAAAD,GAC/B,EAAKR,QAAU,EAAKnB,MAAM6B,MACtB,EAAKV,QACL,EAAKH,OAAS,EAEd,EAAKA,OAAS,EAAKhB,MAAMgB,OAE7B,EAAKxJ,UAAU,WAInBsK,OAAOC,KAAK9R,KAAKqR,gBAAgBjG,SAAQ,SAAA5K,GACrC,EAAKuP,MAAMgC,oBAAoBvR,EAAI,EAAK6Q,eAAe7Q,IACvD,EAAKuP,MAAMhJ,iBAAiBvG,EAAI,EAAK6Q,eAAe7Q,S,yBAO5D,WAAc,WAWVR,KAAKgS,GAAG,QAVe,SAAjBC,IACE,EAAKC,aAGT,EAAK3K,UAAU,eAAgB,EAAK4K,kBAGpC5N,EAAKwH,MAAMkG,EAAX1N,OAOJvE,KAAKgS,GAAG,SAAS,WACb,EAAKzK,UAAU,eAAgB,EAAK4K,uB,kBAe5C,SAAKC,EAAK/N,EAAW3B,EAAOsN,GACxB,IAAMD,EAAQ/K,SAASC,cAAcjF,KAAKgR,WAC1CjB,EAAMsC,SAAWrS,KAAKsE,OAAOgO,cAC7BvC,EAAMwC,SAAWvS,KAAKsE,OAAOiO,WAAY,EACzCxC,EAAMC,QAAqB,MAAXA,EAAkB,OAASA,EAC3CD,EAAMyC,IAAMJ,EACZrC,EAAMjL,MAAM7D,MAAQ,OAEpB,IAAMwR,EAAYpO,EAAUqO,cAAc1S,KAAKgR,WAC3CyB,GACApO,EAAUgF,YAAYoJ,GAE1BpO,EAAUU,YAAYgL,GAEtB/P,KAAK2S,MAAM5C,EAAOrN,EAAOsN,K,qBAS7B,SAAQ4C,EAAKlQ,GACTkQ,EAAIP,SAAWrS,KAAKsE,OAAOgO,cAC3BM,EAAIL,SAAWvS,KAAKsE,OAAOiO,WAAY,EAEvCvS,KAAK2S,MAAMC,EAAKlQ,EAAOkQ,EAAI5C,W,mBAc/B,SAAMD,EAAOrN,EAAOsN,GAEhB,KACMD,aAAiB8C,wBACe,IAA3B9C,EAAMhJ,iBAEb,MAAM,IAAI+L,MAAM,gDAQK,mBAAd/C,EAAMgD,MAAwBrQ,GAAoB,QAAXsN,GAI9CD,EAAMgD,OAGV/S,KAAK+P,MAAQA,EACb/P,KAAKgT,uBACLhT,KAAK0C,MAAQA,EACb1C,KAAKoR,UAAY,KACjBpR,KAAKmR,OAAS,KACdnR,KAAKkR,QAAUnB,EAAM6B,MACrB5R,KAAKyP,gBAAgBzP,KAAK4Q,cAC1B5Q,KAAKiT,UAAUjT,KAAK+Q,U,sBAQxB,WACI,OAAQ/Q,KAAK+P,OAAS/P,KAAK+P,MAAMY,S,yBAQrC,WACI,GAAI3Q,KAAKkT,iBACL,OAAOlT,KAAKkT,iBAEhB,IAAIxC,GAAY1Q,KAAKmR,QAAUnR,KAAK+P,OAAOW,SAK3C,OAJIA,GAAYyC,EAAAA,IAEZzC,EAAW1Q,KAAK+P,MAAMqD,SAAS7S,IAAI,IAEhCmQ,I,4BASX,WACI,OAAO1Q,KAAK+P,OAAS/P,KAAK+P,MAAMU,c,+BAQpC,WACI,OAAOzQ,KAAKmS,iBAAmBnS,KAAKqT,eAAiB,I,6BAQzD,WACI,OAAOrT,KAAK4Q,cAAgB5Q,KAAK+P,MAAMa,e,6BAQ3C,SAAgB0C,GACZtT,KAAK4Q,aAAe0C,GAAS,EAC7BtT,KAAK+P,MAAMa,aAAe5Q,KAAK4Q,e,oBAQnC,SAAOtQ,GACU,MAATA,IACAN,KAAK+P,MAAMU,YAAcnQ,GAE7BN,KAAKuT,iB,kBAYT,SAAKjT,EAAOC,GACRP,KAAKwT,OAAOlT,GACZ,IAAMmT,EAAUzT,KAAK+P,MAAMc,OAG3B,OAFAtQ,GAAOP,KAAK0T,WAAWnT,GAEhBkT,I,mBASX,WACI,IAAIA,EAOJ,OALIzT,KAAK+P,QACL0D,EAAUzT,KAAK+P,MAAMe,SAEzB9Q,KAAKuT,eAEEE,I,wBAQX,SAAWlT,GAAK,WACZP,KAAKuT,eAELvT,KAAK2T,WAAa,SAAAC,GACVA,GAAQrT,IACR,EAAKuQ,QACL,EAAK0C,OAAOjT,KAGpBP,KAAKgS,GAAG,eAAgBhS,KAAK2T,c,0BAIjC,WACQ3T,KAAK2T,aACL3T,KAAK6T,GAAG,eAAgB7T,KAAK2T,YAC7B3T,KAAK2T,WAAa,Q,sBAc1B,SAASzQ,EAAQC,EAAOqJ,GACpB,OAAIxM,KAAKmR,OACL,4CAAsBjO,EAAQC,EAAOqJ,GAElCxM,KAAK0C,OAAS,K,uBAUzB,SAAUoR,GACN,OAAIA,EACK9T,KAAK+P,MAAMgE,UAKT/T,KAAK+P,MAAMgE,UAAUD,GAJjB9P,QAAQgQ,OACX,IAAIlB,MAAM,+CAMf9O,QAAQgQ,OAAO,IAAIlB,MAAM,qBAAuBgB,M,uBAQ3D,WACI,OAAO9T,KAAK+Q,S,uBAQhB,SAAUuC,GACNtT,KAAK+Q,OAASuC,EAEVtT,KAAK+P,MAAMgB,SAAW/Q,KAAK+Q,SAC3B/Q,KAAK+P,MAAMgB,OAAS/Q,KAAK+Q,U,qBAUjC,SAAQa,GAGJ5R,KAAKkR,QAAUlR,KAAK+P,MAAM6B,MAAQA,I,qBAOtC,WAAU,WACN5R,KAAK8Q,QACL9Q,KAAKkJ,QACLlJ,KAAKiU,WAAY,EAGjBpC,OAAOC,KAAK9R,KAAKqR,gBAAgBjG,SAAQ,SAAA5K,GACjC,EAAKuP,OACL,EAAKA,MAAMgC,oBAAoBvR,EAAI,EAAK6Q,eAAe7Q,OAK3DR,KAAKsE,OAAO4P,6BACZlU,KAAK+P,OACL/P,KAAK+P,MAAM5G,YAEXnJ,KAAK+P,MAAM5G,WAAWE,YAAYrJ,KAAK+P,OAG3C/P,KAAK+P,MAAQ,U,6BA/aAS,CAAqB2D,EAAAA,S,sSCDrBC,EAAAA,WAIjB,c,4FAAc,SACVpU,KAAKqU,iB,qDAMT,WAOIrU,KAAKsU,gBAAkB,GAMvBtU,KAAKuU,iBAAmB,I,iCAW5B,SAAoBrR,EAAQ5C,EAAOC,GAC3B2C,GAAUlD,KAAKuU,kBACfvU,KAAKqU,iBACLrU,KAAKuU,gBAAkBrR,GAO3B,IAHA,IAAIsR,EAAiB,GACjBzR,EAAI,EAGJA,EAAI/C,KAAKsU,gBAAgBpR,QACzBlD,KAAKsU,gBAAgBvR,GAAKzC,GAE1ByC,IASJ,IAHIA,EAAI,GAAK,GACTyR,EAAe9I,KAAKpL,GAGpByC,EAAI/C,KAAKsU,gBAAgBpR,QACzBlD,KAAKsU,gBAAgBvR,IAAMxC,GAE3BiU,EAAe9I,KAAK1L,KAAKsU,gBAAgBvR,IACzCA,IAGAA,EAAI,GAAK,GACTyR,EAAe9I,KAAKnL,GAIxBiU,EAAiBA,EAAenG,QAAO,SAACoG,EAAM5L,EAAK6L,GAC/C,OAAW,GAAP7L,EACO4L,GAAQC,EAAI7L,EAAM,GAClBA,GAAO6L,EAAIxR,OAAS,EACpBuR,GAAQC,EAAI7L,EAAM,GAEtB4L,GAAQC,EAAI7L,EAAM,IAAM4L,GAAQC,EAAI7L,EAAM,MAMrD7I,KAAKsU,gBAAkBtU,KAAKsU,gBAAgBK,OAAOH,GACnDxU,KAAKsU,gBAAkBtU,KAAKsU,gBACvBM,MAAK,SAACC,EAAGC,GAAJ,OAAUD,EAAIC,KACnBzG,QAAO,SAACoG,EAAM5L,EAAK6L,GAChB,OAAW,GAAP7L,EACO4L,GAAQC,EAAI7L,EAAM,GAClBA,GAAO6L,EAAIxR,OAAS,EACpBuR,GAAQC,EAAI7L,EAAM,GAEtB4L,GAAQC,EAAI7L,EAAM,IAAM4L,GAAQC,EAAI7L,EAAM,MAKzD,IAAMkM,EAAqB,GAC3B,IAAKhS,EAAI,EAAGA,EAAIyR,EAAetR,OAAQH,GAAK,EACxCgS,EAAmBrJ,KAAK,CAAC8I,EAAezR,GAAIyR,EAAezR,EAAI,KAGnE,OAAOgS,I,4BAQX,WACI,IACIhS,EADEiS,EAAsB,GAE5B,IAAKjS,EAAI,EAAGA,EAAI/C,KAAKsU,gBAAgBpR,OAAQH,GAAK,EAC9CiS,EAAoBtJ,KAAK,CACrB1L,KAAKsU,gBAAgBvR,GACrB/C,KAAKsU,gBAAgBvR,EAAI,KAGjC,OAAOiS,O,6BAvHMZ,G,uHCMN,SAAgBa,GAC3B,IAAMzM,GAAM,aAAQyM,GACdxM,GAAM,aAAQwM,GACpB,OAAQxM,EAAMD,GAAOC,EAAMD,GAd/B,gBACA,Y,4JCOe,SAAe2G,EAAK1G,EAAKD,GACpC,OAAOpF,KAAKqF,IAAIrF,KAAKoF,IAAIC,EAAK0G,GAAM3G,I,2GCwGzB,SAAmB0M,GAC9B,IAAKA,EACD,MAAM,IAAIpC,MAAM,yBACb,IAAKoC,EAAQ9C,IAChB,MAAM,IAAIU,MAAM,qBAEpB,IAAMqC,EAAW,IAAI5L,EAAAA,QACf6L,EAAe,IAAIC,QACnBC,EAAe,IAAIC,QAAQL,EAAQ9C,KAGzC+C,EAASK,WAAa,IAAIC,gBAGtBP,GAAWA,EAAQQ,gBAEnBR,EAAQQ,eAAetK,SAAQ,SAAAuK,GAC3BP,EAAaQ,OAAOD,EAAOE,IAAKF,EAAOrC,UAK/C,IAAMwC,EAAeZ,EAAQY,cAAgB,OACvCC,EAAe,CACjBC,OAAQd,EAAQc,QAAU,MAC1BC,QAASb,EACTc,KAAMhB,EAAQgB,MAAQ,OACtBC,YAAajB,EAAQiB,aAAe,cACpCC,MAAOlB,EAAQkB,OAAS,UACxBC,SAAUnB,EAAQmB,UAAY,SAC9BC,SAAUpB,EAAQoB,UAAY,SAC9BC,OAAQpB,EAASK,WAAWe,QA4EhC,OAzEAC,MAAMlB,EAAcS,GACfU,MAAK,SAAAC,GAEFvB,EAASuB,SAAWA,EAEpB,IAAIC,GAAoB,EACnBD,EAASE,OAGVD,GAAoB,GAIxB,IAAME,EAAgBH,EAAST,QAAQa,IAAI,kBAQ3C,OAPsB,OAAlBD,IAIAF,GAAoB,GAGnBA,GAMLxB,EAAS4B,WAAa,SAAAnR,GAClBuP,EAAS5N,UAAU,WAAY3B,IAG5B,IAAIoR,SACP,IAAIC,eACA,IAAIC,EAAgB/B,EAAU0B,EAAeH,IAEjDX,IAZOW,KAedD,MAAK,SAAAC,GACF,IAAIS,EACJ,GAAIT,EAASU,GACT,OAAQtB,GACJ,IAAK,cACD,OAAOY,EAASW,cAEpB,IAAK,OACD,OAAOX,EAASY,OAEpB,IAAK,OACD,OAAOZ,EAASa,OAEpB,IAAK,OACD,OAAOb,EAASc,OAEpB,QACIL,EAAS,yBAA2BrB,EAOhD,MAHKqB,IACDA,EAAS,sBAAwBT,EAASe,QAExC,IAAI3E,MAAMqE,MAEnBV,MAAK,SAAAC,GACFvB,EAAS5N,UAAU,UAAWmP,MAEjCgB,OAAM,SAAApG,GACH6D,EAAS5N,UAAU,QAAS+J,MAIpC6D,EAASG,aAAeA,EACjBH,GAxNX,I,EAAA,G,EAAA,S,qMAEM+B,EAAAA,WAQF,WAAY/B,EAAU0B,EAAeH,I,4FAAU,SAC3C1W,KAAKmV,SAAWA,EAChBnV,KAAKmV,SAASwC,QAAUjB,EAASE,KAAKgB,YAEtC5X,KAAK6X,MAAQC,SAASjB,EAAe,IACrC7W,KAAK+X,OAAS,E,4CAUlB,SAAMvC,GAAY,YACD,SAAPwC,IAGF,EAAK7C,SAASwC,QACTK,OACAvB,MAAK,YAAqB,IAAlBwB,EAAkB,EAAlBA,KAAM3E,EAAY,EAAZA,MAIX,GAAI2E,EAWA,OATmB,IAAf,EAAKJ,OACL,EAAK1C,SAAS4B,WAAW7H,KAAK,EAAKiG,SAAU,CACzC4C,OAAQ,EAAKA,OACbF,MAAO,EAAKA,MACZK,kBAAkB,SAI1B1C,EAAW2C,QAIf,EAAKJ,QAAUzE,EAAM8E,WACrB,EAAKjD,SAAS4B,WAAW7H,KAAK,EAAKiG,SAAU,CACzC4C,OAAQ,EAAKA,OACbF,MAAO,EAAKA,MACZK,mBAAmC,IAAf,EAAKL,SAG7BrC,EAAW6C,QAAQ/E,GACnB0E,OAEHN,OAAM,SAAApG,GACHkE,EAAWlE,MAAMA,MAI7B0G,Q,6BA9DFd,G,2GCIS,SAAeoB,GAC1B,OAAO,sCAAIC,EAAJ,yBAAIA,EAAJ,uBAAa,cAAkB,kBAAMD,EAAI,WAAJ,EAAQC,QAXxD,I,EAAA,G,EAAA,S,mICWe,SAAeC,GAI1B,YAHerM,IAAXqM,IACAA,EAAS,eAGTA,EACApV,KAAKqV,SACAC,SAAS,IACTC,UAAU,I,mrCCnBvB,eACA,YACA,YACA,YACA,YACA,YACA,YACA,YACA,YACA,YACA,YACA,YACA,Y,yICLe,SAAa1D,GACxB,IAAI2D,GAAU,IAMd,OALA/G,OAAOC,KAAKmD,GAAQ7J,SAAQ,SAAArI,GACpBkS,EAAOlS,GAAK6V,IACZA,EAAU3D,EAAOlS,OAGlB6V,G,yGCPI,SAAa3D,GACxB,IAAI4D,EAAWC,OAAO3F,EAAAA,GAMtB,OALAtB,OAAOC,KAAKmD,GAAQ7J,SAAQ,SAAArI,GACpBkS,EAAOlS,GAAK8V,IACZA,EAAW5D,EAAOlS,OAGnB8V,G,0RCJUtP,EAAAA,WAIjB,c,4FAAc,SAMVvJ,KAAK+Y,wBAA0B,GAC/B/Y,KAAKgZ,SAAW,K,yCASpB,SAAGtH,EAAO5D,GAAI,WACL9N,KAAKgZ,WACNhZ,KAAKgZ,SAAW,IAGpB,IAAIA,EAAWhZ,KAAKgZ,SAAStH,GAO7B,OANKsH,IACDA,EAAWhZ,KAAKgZ,SAAStH,GAAS,IAEtCsH,EAAStN,KAAKoC,GAGP,CACHpN,KAAMgR,EACNuH,SAAUnL,EACV+F,GAAI,SAACjO,EAAGkI,GAAJ,OAAW,EAAK+F,GAAGjO,EAAGkI,O,gBAWlC,SAAG4D,EAAO5D,GACN,GAAK9N,KAAKgZ,SAAV,CAIA,IACIjW,EADEiW,EAAWhZ,KAAKgZ,SAAStH,GAE/B,GAAIsH,EACA,GAAIlL,EACA,IAAK/K,EAAIiW,EAAS9V,OAAS,EAAGH,GAAK,EAAGA,IAC9BiW,EAASjW,IAAM+K,GACfkL,EAASE,OAAOnW,EAAG,QAI3BiW,EAAS9V,OAAS,K,mBAQ9B,WACIlD,KAAKgZ,SAAW,O,kBAWpB,SAAKtH,EAAOyH,GAAS,WASjB,OAAOnZ,KAAKgS,GAAGN,GARJ,SAAL5D,IAAkB,2BAATyK,EAAS,yBAATA,EAAS,gBAEpBY,EAAQC,MAAM,EAAMb,GAEpBc,YAAW,WACP,EAAKxF,GAAGnC,EAAO5D,KAChB,Q,uCAeX,SAA0BwL,GACtBtZ,KAAK+Y,wBAA0BO,I,sCAQnC,SAAyB5H,GACrB,OAAO1R,KAAK+Y,yBAA2B/Y,KAAK+Y,wBAAwBlL,SAAS6D,K,uBASjF,SAAUA,GAAgB,2BAAN6G,EAAM,iCAANA,EAAM,kBACtB,GAAKvY,KAAKgZ,WAAYhZ,KAAKuZ,yBAAyB7H,GAApD,CAIA,IAAMsH,EAAWhZ,KAAKgZ,SAAStH,GAC/BsH,GACIA,EAAS5N,SAAQ,SAAA0C,GACbA,EAAE,WAAF,EAAMyK,Y,6BAlIDhP,G,qHCoDN,SAAS/E,EAAgB4D,EAAQzG,GAC5C,OAAIyG,EAAOoR,GACApR,EAEA,IAAIqR,MACPrR,EAAQ,CACJ0O,IAAK,SAAS4C,EAAKC,EAAMC,GACrB,GAAID,IAASH,EACT,OAAO,EACJ,GAAa,eAATG,EACP,OAAOD,EACJ,GAAa,UAATC,EACP,OAAOnV,EAAgBkV,EAAI5U,MAAOnD,GAC/B,GAAa,WAATgY,EACP,OAAOnV,EAAgBkV,EAAInY,OAAQI,GAChC,GAAa,0BAATgY,EACP,OAAO,WACH,OAAOnV,EAAgBkV,EAAIxT,sBAAJ,MAAAwT,EAAG,WAAiC/X,IAE5D,GAAa,eAATgY,EACP,OAAO,WACH,OAAOnV,EAAgBkV,EAAI5Y,WAAJ,MAAA4Y,EAAG,WAAsB/X,IAGpD,IAAI2R,EAAQoG,EAAIG,EAAQF,EAAMhY,IAC9B,MAAuB,mBAAT2R,EAAsBA,EAAMwG,KAAKJ,GAAOpG,GAG9DyG,IAAK,SAASL,EAAKC,EAAMrG,GAErB,OADAoG,EAAIG,EAAQF,EAAMhY,IAAa2R,GACxB,MA5F3B,IAAM0G,EAAkB,CACpB/Y,MAAO,SACPC,OAAQ,QAERsE,UAAW,YACXE,UAAW,YAEXwC,YAAa,eACbf,aAAc,cAEdpB,QAAS,UACTqB,QAAS,UAETX,YAAa,eACbD,WAAY,YAEZrF,WAAY,YACZ8Y,UAAW,aACX/S,aAAc,cACdgT,YAAa,eAEbpT,KAAM,MACND,MAAO,SACPwD,IAAK,OACLhD,OAAQ,QAERmD,iBAAkB,oBAClBI,iBAAkB,oBAClBE,iBAAkB,qBAUtB,SAAS+O,EAAQF,EAAMhY,GACnB,OAAIkQ,OAAOsI,UAAUC,eAAelL,KAAK8K,EAAiBL,IAC/ChY,EAAWqY,EAAgBL,GAE3BA,EAIf,IAAMH,EAAUa,OAAO,W,8CCxCvB,SAASC,EAAoB5I,GACzBA,EAAM6I,kBACNvV,SAAS4R,KAAK7E,oBAAoB,QAASuI,GAAqB,G,2DAQrD,SAAsBrF,GACjCjQ,SAAS4R,KAAK7P,iBAAiB,QAASuT,GAAqB,I,uHCT7DE,OAAOC,uBACPD,OAAOE,6BACPF,OAAOG,0BACPH,OAAOI,wBACPJ,OAAOK,yBACN,SAAC5B,EAAUpY,GAAX,OAAuBwY,WAAWJ,EAAU,IAAO,MACtDa,KAAKU,Q,qHCNQ,SAAe5V,EAAIC,GAM9B,OALAgN,OAAOC,KAAKjN,GAAQuG,SAAQ,SAAAuO,GACpB/U,EAAGE,MAAM6U,KAAU9U,EAAO8U,KAC1B/U,EAAGE,MAAM6U,GAAQ9U,EAAO8U,OAGzB/U,G,uVCdX,M,ybAAA,SACA,YACA,YACA,YACA,YACA,Y,yzCA2OqBkW,EAAAA,SAAAA,I,mPAuHjB,WAAYxW,GAAQ,MAkBhB,GAlBgB,WAChB,gBAtHJyW,cAAgB,CACZC,aAAc,KACdC,qBAAsB,KACtBvL,UAAW,EACX5G,YAAY,EACZP,eAAgB,EAChBS,uBAAuB,EACvBkS,QAAS,WACTC,gBAAiB,KACjBpM,UAAW,EACX9E,UAAW,EACXsC,OAAQ,KACRG,aAAc,KACdrI,UAAW,KACX0G,YAAa,OACbF,YAAa,EACbuQ,eAAe,EACftR,yBAA0B,CAItBuR,gBAAgB,GAEpB3K,SAAU,KACVpL,YAAY,EACZgW,aAAa,EACbpa,OAAQ,IACRuE,eAAe,EACf6B,UAAU,EACViU,eAAe,EACf9R,eAAgB,IAChB+R,eAAgB,KAChBlJ,eAAe,EACftB,UAAW,QACXyK,YAAa,GACbzM,WAAW,EACX0M,eAAe,EACfjX,WACI+V,OAAOmB,kBAAoBC,OAAOC,WAAaD,OAAOE,YAC1DC,QAAS,GACTta,cAAe,OACfyS,6BAA6B,EAC7B8H,SAAUxS,EAAAA,QACVyS,YAAY,EACZrV,KAAK,EACLrB,cAAc,EACd2W,WAAY,EACZvO,eAAe,EACfb,qBAAsB,CAClB0B,SAAS,EACTzB,cAAe,GACfa,eAAgB,GAChBc,uBAAuB,GAE3B/M,UAAU,EACVH,UAAW,OACX2a,IAAK,IA6DW,EAzDpBC,SAAW,CACP5L,aAAAA,EAAAA,QACA2D,SAAAA,EAAAA,QACA5E,qBAAAA,EAAAA,SAsDgB,EApBpBhL,KAAOA,EA0BH,EAAKD,OAASuN,OAAOwK,OAAO,GAAI,EAAKtB,cAAezW,GACpD,EAAKA,OAAOwI,qBAAuB+E,OAAOwK,OACtC,GACA,EAAKtB,cAAcjO,qBACnBxI,EAAOwI,sBAGX,EAAKzI,UACD,iBAAmBC,EAAOD,UACpBW,SAAS0N,cAAc,EAAKpO,OAAOD,WACnC,EAAKC,OAAOD,WAEjB,EAAKA,UACN,MAAM,IAAIyO,MAAM,+BAgBpB,GAbkC,MAA9B,EAAKxO,OAAOkX,eAEZ,EAAKA,eAAiB,EAAKnX,UACiB,iBAA9B,EAAKC,OAAOkX,eAE1B,EAAKA,eAAiBxW,SAAS0N,cAC3B,EAAKpO,OAAOkX,gBAIhB,EAAKA,eAAiB,EAAKlX,OAAOkX,gBAGjC,EAAKA,eACN,MAAM,IAAI1I,MAAM,qCAGpB,GAAI,EAAKxO,OAAOmF,gBAAkB,EAC9B,MAAM,IAAIqJ,MAAM,yCACb,GAAI,EAAKxO,OAAOmF,eAAiB,GAAK,EACzC,MAAM,IAAIqJ,MAAM,yCAkDpB,IA/CwB,IAApB,EAAKxO,OAAOsC,OACiB,IAAzB,EAAKtC,OAAO3C,SACZ4C,EAAKO,MAAM,EAAKT,UAAW,CAAEiY,UAAW,oBAExC/X,EAAKO,MAAM,EAAKT,UAAW,CAAEiY,UAAW,qBAI5C,EAAKhY,OAAO6W,iBACZ,EAAKoB,mBAAmB,EAAKjY,OAAO6W,iBAQxC,EAAKqB,YAAc,EAMnB,EAAKtL,SAAU,EAOf,EAAKuL,UAAY,GAMjB,EAAKC,eAAiB,KAEtB,EAAKC,YAAc,KAEnB,EAAKC,OAAS,KAEd,EAAK1B,QAAU,KAEf,EAAK2B,UAAY,KAGmB,mBAAzB,EAAKvY,OAAO0X,SACnB,MAAM,IAAIlJ,MAAM,iCAKpB,EAAK1O,OAAS,EAAKE,OAAO0X,SAKC,gBAAvB,EAAK1X,OAAO4W,UACZ,EAAK5W,OAAO4W,QAAU,gBAIE,YAAvB,EAAK5W,OAAO4W,SACe,yBAAxB,EAAK5W,OAAO4W,SACf/G,EAAAA,QAASgG,UAAU2C,iBAAiB5N,KAAK,QAE1C,EAAK5K,OAAO4W,QAAU,gBAE1B,EAAK6B,QAAU,EAAKX,SAAS,EAAK9X,OAAO4W,SAKzC,EAAK8B,sBAAwB,GAE7B,EAAKC,aAAc,EAQnB,EAAKC,SAAU,EAKf,IAAIC,EAAY,EAgBhB,OAfA,EAAKC,UAAY7Y,EAAK8Y,UAClB,WAEQF,GAAa,EAAKP,OAAOjY,QAAQuD,aAChC,EAAK5D,OAAOiB,eAEb4X,EAAY,EAAKP,OAAOjY,QAAQuD,YAChC,EAAK0U,OAAOrV,UAAU,aAGI,iBAA3B,EAAKjD,OAAO2X,WACb,EAAK3X,OAAO2X,WACZ,KAGV,U,8BAWJ,WAKI,OAJAjc,KAAKsd,gBAAgBtd,KAAKsE,OAAOyX,SACjC/b,KAAKud,eACLvd,KAAKwd,gBACLxd,KAAKyd,kBACEzd,O,6BAWX,SAAgB+b,GAAS,WAarB,OAXAA,EAAQ3Q,SAAQ,SAAAsS,GAAM,OAAI,EAAKC,UAAUD,MAGzC3B,EAAQ3Q,SAAQ,SAAAsS,GAGPA,EAAOE,WACR,EAAKC,WAAWH,EAAOhd,SAG/BV,KAAKuH,UAAU,qBAAsBwU,GAC9B/b,O,8BASX,WACI,OAAOA,KAAKgd,wB,uBAWhB,SAAUU,GAAQ,WACd,IAAKA,EAAOhd,KACR,MAAM,IAAIoS,MAAM,gCAEpB,IAAK4K,EAAOvI,SACR,MAAM,IAAIrC,MAAJ,iBACQ4K,EAAOhd,KADf,yCAMNgd,EAAOI,aACPjM,OAAOC,KAAK4L,EAAOI,aAAa1S,SAAQ,SAAA2S,GAKpC,EAAKA,GAAoBL,EAAOI,YAAYC,MAIpD,IAAMC,EAAWN,EAAOvI,SAiBxB,OAd8BtD,OAAOoM,oBACjC1Z,EAAKgF,SAAS4Q,WAEI/O,SAAQ,SAAAyK,GAC1BmI,EAAS7D,UAAUtE,GAAOtR,EAAKgF,SAAS4Q,UAAUtE,MAQtD7V,KAAK0d,EAAOhd,MAAQ,IAAIsd,EAASN,EAAOpZ,QAAU,GAAItE,MACtDA,KAAKuH,UAAU,eAAgBmW,EAAOhd,MAC/BV,O,wBAWX,SAAWU,GACP,IAAKV,KAAKU,GACN,MAAM,IAAIoS,MAAJ,iBAAoBpS,EAApB,6BASV,OAPIV,KAAKgd,sBAAsBtc,IAE3BV,KAAKke,cAAcxd,GAEvBV,KAAKU,GAAMyd,OACXne,KAAKgd,sBAAsBtc,IAAQ,EACnCV,KAAKuH,UAAU,qBAAsB7G,GAC9BV,O,2BAWX,SAAcU,GACV,IAAKV,KAAKU,GACN,MAAM,IAAIoS,MAAJ,iBACQpS,EADR,qDAIV,IAAKV,KAAKgd,sBAAsBtc,GAC5B,MAAM,IAAIoS,MAAJ,iBACQpS,EADR,4CAIV,GAAkC,mBAAvBV,KAAKU,GAAMmL,QAClB,MAAM,IAAIiH,MAAJ,iBAAoBpS,EAApB,uCAMV,OAHAV,KAAKU,GAAMmL,iBACJ7L,KAAKgd,sBAAsBtc,GAClCV,KAAKuH,UAAU,mBAAoB7G,GAC5BV,O,+BASX,WAAoB,WAChB6R,OAAOC,KAAK9R,KAAKgd,uBAAuB5R,SAAQ,SAAA1K,GAAI,OAChD,EAAKwd,cAAcxd,Q,0BAU3B,WAAe,WACXV,KAAK4c,OAAS,IAAI5c,KAAKoE,OAAOpE,KAAKqE,UAAWrE,KAAKsE,QACnDtE,KAAK4c,OAAOuB,OACZne,KAAKuH,UAAU,iBAAkBvH,KAAK4c,SAEP,IAA3B5c,KAAKsE,OAAO2X,aACZzB,OAAOzT,iBAAiB,SAAU/G,KAAKod,WAAW,GAClD5C,OAAOzT,iBAAiB,oBAAqB/G,KAAKod,WAAW,IAGjEpd,KAAK4c,OAAO5K,GAAG,UAAU,WACrB,EAAKoM,aACL,EAAKxB,OAAOxc,SAAS,EAAK8a,QAAQmD,wBAItCre,KAAK4c,OAAO5K,GAAG,SAAS,SAACpM,EAAGxF,GACxBiZ,YAAW,kBAAM,EAAK7F,OAAOpT,KAAW,MAI5CJ,KAAK4c,OAAO5K,GAAG,UAAU,SAAApM,GACjB,EAAKtB,OAAOoX,eACZ,EAAK0C,aAET,EAAK7W,UAAU,SAAU3B,Q,2BAUjC,WAAgB,WACR5F,KAAKkb,SACLlb,KAAKkb,QAAQrP,UAGjB7L,KAAKkb,QAAU,IAAIlb,KAAK+c,QAAQ/c,KAAKsE,QACrCtE,KAAKkb,QAAQiD,OACbne,KAAKuH,UAAU,kBAAmBvH,KAAKkb,SAEvClb,KAAKkb,QAAQlJ,GAAG,UAAU,WACtB,EAAK4K,OAAOxc,SAAS,EAAK8a,QAAQmD,qBAClC,EAAK9W,UAAU,aAEnBvH,KAAKkb,QAAQlJ,GAAG,QAAQ,kBAAM,EAAKzK,UAAU,WAC7CvH,KAAKkb,QAAQlJ,GAAG,SAAS,kBAAM,EAAKzK,UAAU,YAE9CvH,KAAKkb,QAAQlJ,GAAG,gBAAgB,SAAA4B,GAC5B,EAAKgJ,OAAOxc,SAAS,EAAK8a,QAAQmD,qBAClC,EAAK9W,UAAU,eAAgBqM,MAKP,iBAAxB5T,KAAKsE,OAAO4W,SACY,yBAAxBlb,KAAKsE,OAAO4W,UAEZlb,KAAKkb,QAAQlJ,GAAG,QAAQ,WACpB,EAAK4K,OAAOxc,SAAS,EAAK8a,QAAQmD,wBAGtCre,KAAKkb,QAAQlJ,GAAG,UAAU,WACtB,IAAIsM,EAAY,EAAKC,YACrB,EAAKhX,UAAU,SAAU+W,GAErB,EAAKpD,QAAQhK,UAAY,EAAKA,UAC9B,EAAKA,QAAU,EAAKgK,QAAQhK,QAC5B,EAAK3J,UAAU,OAAQ,EAAK2J,gB,6BAW5C,WACQlR,KAAKsE,OAAOoX,gBACZ1b,KAAK6c,UAAY,IAAIzI,EAAAA,W,yBAU7B,WACI,OAAOpU,KAAKkb,QAAQ7H,gB,4BASxB,WACI,OAAOrT,KAAKkb,QAAQ/I,mB,4BASxB,SAAeqM,GACPA,GAAWxe,KAAKqT,cAChBrT,KAAKwT,OAAO,GAEZxT,KAAKwT,OAAOgL,EAAUxe,KAAKqT,iB,kBAgBnC,SAAK/S,EAAOC,GAAK,WAEb,OADAP,KAAKuH,UAAU,eAAe,kBAAM,EAAKsJ,KAAKvQ,EAAOC,MAC9CP,KAAKkb,QAAQrK,KAAKvQ,EAAOC,K,wBASpC,SAAW4E,GACPnF,KAAKkb,QAAQxH,WAAWvO,K,mBAS5B,WACI,IAAKnF,KAAKkb,QAAQhJ,WACd,OAAOlS,KAAKkb,QAAQpK,U,uBAU5B,WACI,OAAO9Q,KAAKkb,QAAQhJ,WAAalS,KAAK6Q,OAAS7Q,KAAK8Q,U,uBASxD,WACI,OAAQ9Q,KAAKkb,QAAQhJ,a,0BAUzB,SAAasM,GACTxe,KAAKye,MAAMD,IAAYxe,KAAKsE,OAAO4X,c,yBAUvC,SAAYsC,GACRxe,KAAKye,KAAKD,GAAWxe,KAAKsE,OAAO4X,c,kBAYrC,SAAK7T,GACD,IAAMqI,EAAW1Q,KAAKqT,eAAiB,EACnClO,EAAWnF,KAAKmS,kBAAoB,EACxChN,EAAW/B,KAAKoF,IAAI,EAAGpF,KAAKqF,IAAIiI,EAAUvL,GAAYkD,GAAU,KAChErI,KAAK0e,cAAcvZ,EAAWuL,K,2BAWlC,SAActQ,GACVJ,KAAKwT,OAAOpT,GACZJ,KAAK4c,OAAO+B,SAASve,K,oBAazB,SAAOA,GAAU,WAEb,GACwB,iBAAbA,IACNwe,SAASxe,IACVA,EAAW,GACXA,EAAW,EAEX,MAAM,IAAI0S,MACN,gFAGR9S,KAAKuH,UAAU,eAAe,kBAAM,EAAKiM,OAAOpT,MAEhD,IAAMye,EAA4C,aAAxB7e,KAAKsE,OAAO4W,QAChCvK,EAAS3Q,KAAKkb,QAAQhJ,WAExB2M,IAAsBlO,GACtB3Q,KAAKkb,QAAQpK,QAIjB,IAAMgO,EAAkB9e,KAAKsE,OAAOiB,aACpCvF,KAAKsE,OAAOiB,cAAe,EAC3BvF,KAAKkb,QAAQ1H,OAAOpT,EAAWJ,KAAKqT,eACpCrT,KAAK4c,OAAOxc,SAASA,GAEjBye,IAAsBlO,GACtB3Q,KAAKkb,QAAQrK,OAGjB7Q,KAAKsE,OAAOiB,aAAeuZ,EAC3B9e,KAAKuH,UAAU,OAAQnH,K,kBAQ3B,WACIJ,KAAK8Q,QACL9Q,KAAKwT,OAAO,GACZxT,KAAK4c,OAAOxc,SAAS,K,uBAWzB,SAAU0T,GACN,OAAO9T,KAAKkb,QAAQnH,UAAUD,K,uBAUlC,SAAUwK,GACNte,KAAKkb,QAAQjI,UAAUqL,GACvBte,KAAKuH,UAAU,SAAU+W,K,uBAS7B,WACI,OAAOte,KAAKkb,QAAQqD,c,6BAUxB,SAAgBjW,GACZtI,KAAKkb,QAAQzL,gBAAgBnH,K,6BAQjC,WACI,OAAOtI,KAAKkb,QAAQ6D,oB,wBAWxB,WACI/e,KAAKgf,SAAShf,KAAKkR,W,qBAcvB,SAAQ+N,GAEAA,IAASjf,KAAKkR,SAKdlR,KAAKkb,QAAQ8D,SAGbhf,KAAKkb,QAAQ8D,QAAQC,GACrBjf,KAAKkR,QAAU+N,GAEXA,GAGAjf,KAAKwc,YAAcxc,KAAKkb,QAAQqD,YAChCve,KAAKkb,QAAQjI,UAAU,GACvBjT,KAAKkR,SAAU,EACflR,KAAKuH,UAAU,SAAU,KAIzBvH,KAAKkb,QAAQjI,UAAUjT,KAAKwc,aAC5Bxc,KAAKkR,SAAU,EACflR,KAAKuH,UAAU,SAAUvH,KAAKwc,cAGtCxc,KAAKuH,UAAU,OAAQvH,KAAKkR,UAzBxBlR,KAAKuH,UAAU,OAAQvH,KAAKkR,W,qBAkCpC,WACI,OAAOlR,KAAKkR,U,wBAUhB,WACI,OAAOlR,KAAKkb,QAAQgE,SAAW,K,0BAQnC,WACIlf,KAAKsE,OAAOiB,cAAgBvF,KAAKsE,OAAOiB,aACxCvF,KAAKoe,e,+BAQT,WACIpe,KAAKsE,OAAOgD,UAAYtH,KAAKsE,OAAOgD,W,0BAQxC,WACI,OAAOtH,KAAKsE,OAAO9C,Y,0BASvB,SAAa2d,GACTnf,KAAKsE,OAAO9C,UAAY2d,EACxBnf,KAAKoe,e,8BAQT,WACI,OAAOpe,KAAKsE,OAAO7C,gB,8BASvB,SAAiB0d,GACbnf,KAAKsE,OAAO7C,cAAgB0d,EAC5Bnf,KAAKoe,e,gCAQT,WACI,OAAOpe,KAAKsE,OAAO6W,kB,gCASvB,SAAmBgE,GACfnf,KAAKsE,OAAO6W,gBAAkBgE,EAC9B5a,EAAKO,MAAM9E,KAAKqE,UAAW,CAAE+a,WAAYpf,KAAKsE,OAAO6W,oB,4BASzD,WACI,OAAOnb,KAAKsE,OAAOyG,c,4BAUvB,SAAeoU,GACXnf,KAAKsE,OAAOyG,YAAcoU,EAC1Bnf,KAAK4c,OAAOjS,iB,uBAQhB,WACI,OAAO3K,KAAKsE,OAAOpD,S,uBASvB,SAAUA,GACNlB,KAAKsE,OAAOpD,OAASA,EACrBlB,KAAK4c,OAAOnO,UAAUvN,EAASlB,KAAKsE,OAAOG,YAC3CzE,KAAKoe,e,iCAgBT,SAAoBiB,GAChBrf,KAAKsE,OAAOwI,qBAAqBc,eAAiByR,EAClDrf,KAAKoe,e,wBAST,WACI,IAoBI1b,EApBEyD,EAAe/C,KAAKC,MACtBrD,KAAKqT,cACDrT,KAAKsE,OAAOmX,YACZzb,KAAKsE,OAAOG,YAEd2B,EAAcpG,KAAK4c,OAAOvW,WAC5BpF,EAAQkF,EAER7F,EAAQ,EACRC,EAAM6C,KAAKoF,IAAIlI,EAAQ8F,EAAanF,GAYxC,GATIjB,KAAKsE,OAAOgB,cACVtF,KAAKsE,OAAOiB,cAAgBY,EAAeC,KAG7C9F,EAAQ,EACRC,EAFAU,EAAQmF,GAMRpG,KAAKsE,OAAOoX,cAAe,CAC3B,IAKI3Y,EALEuc,EAAYtf,KAAK6c,UAAU0C,oBAC7Bte,EACAX,EACAC,GAGJ,IAAKwC,EAAI,EAAGA,EAAIuc,EAAUpc,OAAQH,IAC9BL,EAAQ1C,KAAKkb,QAAQsE,SACjBve,EACAqe,EAAUvc,GAAG,GACbuc,EAAUvc,GAAG,IAEjB/C,KAAK4c,OAAO6C,UACR/c,EACAzB,EACAqe,EAAUvc,GAAG,GACbuc,EAAUvc,GAAG,SAIrBL,EAAQ1C,KAAKkb,QAAQsE,SAASve,EAAOX,EAAOC,GAC5CP,KAAK4c,OAAO6C,UAAU/c,EAAOzB,EAAOX,EAAOC,GAE/CP,KAAKuH,UAAU,SAAU7E,EAAOzB,K,kBAapC,SAAKye,GACIA,GAID1f,KAAKsE,OAAOmX,YAAciE,EAC1B1f,KAAKsE,OAAOiB,cAAe,IAJ3BvF,KAAKsE,OAAOmX,YAAczb,KAAK+a,cAAcU,YAC7Czb,KAAKsE,OAAOiB,cAAe,GAM/BvF,KAAKoe,aACLpe,KAAK4c,OAAOxc,SAASJ,KAAKkb,QAAQmD,qBAElCre,KAAK4c,OAAO+B,SAAS3e,KAAKmS,iBAAmBnS,KAAKqT,eAClDrT,KAAKuH,UAAU,OAAQmY,K,6BAS3B,SAAgB/C,GAAa,WACzB3c,KAAK2f,kBAAkBhD,GAAa,SAAAiD,GAC3B,EAAK3C,aACN,EAAK4C,kBAAkBD,Q,+BAYnC,SAAkBzO,GACdnR,KAAKkb,QAAQnI,KAAK5B,GAClBnR,KAAKoe,aACLpe,KAAKkd,SAAU,EACfld,KAAKuH,UAAU,W,sBASnB,SAASgQ,GAAM,WAELuI,EAAS,IAAIC,WACnBD,EAAO/Y,iBAAiB,YAAY,SAAAnB,GAAC,OAAI,EAAKmR,WAAWnR,MACzDka,EAAO/Y,iBAAiB,QAAQ,SAAAnB,GAAC,OAC7B,EAAKoa,gBAAgBpa,EAAEwC,OAAO6X,WAElCH,EAAO/Y,iBAAiB,SAAS,kBAC7B,EAAKQ,UAAU,QAAS,yBAE5BuY,EAAOI,kBAAkB3I,GACzBvX,KAAKmgB,U,kBA8BT,SAAK/N,EAAK1P,EAAOsN,EAASU,GACtB,IAAK0B,EACD,MAAM,IAAIU,MAAM,iCAGpB,GADA9S,KAAKmgB,QACDnQ,EAAS,CAGT,IAAMoQ,EAAuB,CACzB,+CACuD,IAAnD,CAAC,OAAQ,WAAY,QAAQtR,QAAQkB,GACzC,0BAA2BtN,EAC3B,mEAGW,IAFP,CAAC,eAAgB,wBAAwBoM,QACrC9O,KAAKsE,OAAO4W,SAEpB,4BAA4C,iBAAR9I,GAElCiO,EAAgBxO,OAAOC,KAAKsO,GAAsB/R,QACpD,SAAAiS,GAAM,OAAIF,EAAqBE,MAE/BD,EAAcnd,SAEdqd,QAAQC,KACJ,sEACIH,EAAcI,KAAK,WAG3BzQ,EAAU,MAUlB,OAJ4B,aAAxBhQ,KAAKsE,OAAO4W,SAA0B9I,aAAeS,mBACrDT,EAAMA,EAAII,KAGNxS,KAAKsE,OAAO4W,SAChB,IAAK,WACD,OAAOlb,KAAK0gB,WAAWtO,EAAK1P,EAAOgO,GACvC,IAAK,eACL,IAAK,uBACD,OAAO1Q,KAAK2gB,iBAAiBvO,EAAK1P,EAAOsN,EAASU,M,wBAc9D,SAAW0B,EAAK1P,EAAOgO,GAAU,WACvBqC,EAAO,SAAA6N,GAIT,OAHIA,GACA,EAAKnE,UAAU/Q,KAAK,EAAKmV,KAAK,QAASD,IAEpC,EAAKE,eAAe1O,GAAK,SAAAwN,GAAI,OAAI,EAAKI,gBAAgBJ,OAGjE,IAAIld,EAMA,OAAOqQ,IALP/S,KAAKkb,QAAQ6F,SAASre,EAAOgO,GAC7B1Q,KAAKoe,aACLpe,KAAKuH,UAAU,kBACfvH,KAAKyc,UAAU/Q,KAAK1L,KAAK6gB,KAAK,cAAe9N,M,8BAmBrD,SAAiBiO,EAAUte,EAAOsN,EAASU,GAAU,WAC7C0B,EAAM4O,EAEV,GAAwB,iBAAbA,EACPhhB,KAAKkb,QAAQnI,KAAKX,EAAKpS,KAAKwb,eAAgB9Y,EAAOsN,OAChD,CACH,IAAM4C,EAAMoO,EACZhhB,KAAKkb,QAAQ+F,QAAQrO,EAAKlQ,GAI1B0P,EAAMQ,EAAIJ,IAGdxS,KAAKyc,UAAU/Q,KACX1L,KAAKkb,QAAQ2F,KAAK,WAAW,WAEpB,EAAK3F,QAAQjH,YACd,EAAKmK,aACL,EAAKlB,SAAU,EACf,EAAK3V,UAAU,aAGvBvH,KAAKkb,QAAQ2F,KAAK,SAAS,SAAAK,GAAG,OAAI,EAAK3Z,UAAU,QAAS2Z,OAI1Dxe,IACA1C,KAAKkb,QAAQ6F,SAASre,EAAOgO,GAC7B1Q,KAAKoe,aACLpe,KAAKuH,UAAU,mBAOb7E,IAAS1C,KAAKsE,OAAOgX,cACvBtb,KAAKkb,QAAQ4B,oBAEb9c,KAAK8gB,eAAe1O,GAAK,SAAAuK,GACrB,EAAKgD,kBAAkBhD,GAAa,SAAAxL,GAChC,EAAK+J,QAAQ/J,OAASA,EACtB,EAAK+J,QAAQ6F,SAAS,MACtB,EAAK3C,aACL,EAAK7W,UAAU,0B,+BAa/B,SAAkBoV,EAAa1D,GAAU,WACrCjZ,KAAK2c,YAAcA,EACnB3c,KAAKkb,QAAQyE,kBACThD,GACA,SAAAiD,GAGS,EAAK3C,aAAe,EAAKN,aAAeA,IACzC1D,EAAS2G,GACT,EAAKjD,YAAc,SAG3B,kBAAM,EAAKpV,UAAU,QAAS,mC,4BAYtC,SAAe6K,EAAK6G,GAAU,WACtB/D,EAAUrD,OAAOwK,OACjB,CACIjK,IAAKA,EACL0D,aAAc,eAElB9V,KAAKsE,OAAO6X,KAEVgF,EAAU5c,EAAK6c,UAAUlM,GAkB/B,OAhBAlV,KAAK0c,eAAiByE,EAEtBnhB,KAAKyc,UAAU/Q,KACXyV,EAAQnP,GAAG,YAAY,SAAApM,GACnB,EAAKmR,WAAWnR,MAEpBub,EAAQnP,GAAG,WAAW,SAAA4N,GAClB3G,EAAS2G,GACT,EAAKlD,eAAiB,QAE1ByE,EAAQnP,GAAG,SAAS,SAAApM,GAChB,EAAK2B,UAAU,QAAS3B,GACxB,EAAK8W,eAAiB,SAIvByE,I,wBAUX,SAAWvb,GACP,IAAIyb,EAEAA,EADAzb,EAAEsS,iBACgBtS,EAAEmS,OAASnS,EAAEiS,MAIbjS,EAAEmS,QAAUnS,EAAEmS,OAAS,KAE7C/X,KAAKuH,UAAU,UAAWnE,KAAKC,MAAwB,IAAlBge,GAAwBzb,EAAEwC,U,uBAenE,SAAUlF,EAAQoe,EAAUC,EAAUjhB,EAAOC,GACzC2C,EAASA,GAAU,KACnB5C,EAAQA,GAAS,EACjBghB,EAAWA,GAAY,IACvBC,EAAWA,IAAY,EACvB,IAAM7e,EAAQ1C,KAAKkb,QAAQsE,SAAStc,EAAQ5C,EAAOC,GAC7CmU,EAAM,GAAG/F,IAAIO,KACfxM,GACA,SAAAyM,GAAG,OAAI/L,KAAKC,MAAM8L,EAAMmS,GAAYA,KAGxC,OAAO,IAAItd,SAAQ,SAACC,EAAS+P,GACzB,IAAKuN,EAAS,CACV,IAAMC,EAAW,IAAIC,KACjB,CAACC,KAAKC,UAAUjN,IAChB,CAAC3Q,KAAM,mCAEL6d,EAASC,IAAIC,gBAAgBN,GACnChH,OAAOuH,KAAKH,GACZC,IAAIG,gBAAgBJ,GAExB3d,EAAQyQ,Q,yBAsBhB,SAAY7Q,EAAQC,EAASC,GAWzB,OAVKF,IACDA,EAAS,aAERC,IACDA,EAAU,GAETC,IACDA,EAAO,WAGJ/D,KAAK4c,OAAOvN,SAASxL,EAAQC,EAASC,K,wBAMjD,WACQ/D,KAAK0c,gBAAkB1c,KAAK0c,eAAelH,aAIvCxV,KAAK0c,eAAe/E,SAEpB3X,KAAK0c,eAAe/E,QAAQsK,SAASvK,OAAM,SAAAwJ,OAG/ClhB,KAAK0c,eAAelH,WAAW0M,QAC/BliB,KAAK0c,eAAiB,Q,4BAO9B,WACI1c,KAAKyc,UAAUrR,SAAQ,SAAAxF,GAAC,OAAIA,EAAEiO,U,mBAMlC,WACS7T,KAAKkb,QAAQhJ,aACdlS,KAAKmiB,OACLniB,KAAKkb,QAAQkH,oBAEjBpiB,KAAKkd,SAAU,EACfld,KAAKqiB,aACLriB,KAAKsiB,iBAGLtiB,KAAK4c,OAAOxc,SAAS,GACrBJ,KAAK4c,OAAOnV,SAAS,GACrBzH,KAAK4c,OAAO6C,UAAU,CAAEvc,OAAQlD,KAAK4c,OAAOvW,YAAc,K,qBAQ9D,WACIrG,KAAKuiB,oBACLviB,KAAKuH,UAAU,WACfvH,KAAKqiB,aACLriB,KAAKsiB,iBACLtiB,KAAKkJ,SAC0B,IAA3BlJ,KAAKsE,OAAO2X,aACZzB,OAAOzI,oBAAoB,SAAU/R,KAAKod,WAAW,GACrD5C,OAAOzI,oBACH,oBACA/R,KAAKod,WACL,IAGJpd,KAAKkb,UACLlb,KAAKkb,QAAQrP,UAEb7L,KAAKkb,QAAU,MAEflb,KAAK4c,QACL5c,KAAK4c,OAAO/Q,UAEhB7L,KAAKid,aAAc,EACnBjd,KAAKkd,SAAU,EACfld,KAAK2c,YAAc,Q,qBA75CvB,SAAcrY,GAEV,OADmB,IAAIwW,EAAWxW,GAChB6Z,W,EA7ELrD,CAAmBvW,EAAKgF,U,YAAxBuR,EAwFV0H,QAAUC,QAxFA3H,EA6GVvW,KAAOA,E,uVC7VlB,M,ybAAA,S,guCAGA,IAAMme,EAAU,UACVC,EAAS,SACTC,EAAW,WAOIzO,EAAAA,SAAAA,I,yPAyFjB,WAAY7P,GAAQ,iB,4FAAA,UAChB,gBAtFJ0W,aAAe,KAqFK,EAnFpB6H,oBAAsB,KAmFF,EAjFpBC,gBAiFoB,OAhFfJ,EAAU,CACPvE,KADO,WAEHne,KAAK+iB,qBAET1E,kBAJO,WAKH,IAAM3N,EAAW1Q,KAAKqT,cACtB,OAAOrT,KAAKmS,iBAAmBzB,GAAY,GAE/CyB,eARO,WASH,OAAOnS,KAAKgjB,cAAgBhjB,KAAKijB,mBAuEzB,IApEfN,EAAS,CACNxE,KADM,WAEFne,KAAKkjB,wBAET7E,kBAJM,WAKF,IAAM3N,EAAW1Q,KAAKqT,cACtB,OAAOrT,KAAKmS,iBAAmBzB,GAAY,GAE/CyB,eARM,WASF,OAAOnS,KAAKgjB,iBA2DJ,IAxDfJ,EAAW,CACRzE,KADQ,WAEJne,KAAKkjB,uBACLljB,KAAKuH,UAAU,WAEnB8W,kBALQ,WAMJ,OAAO,GAEXlM,eARQ,WASJ,OAAOnS,KAAKqT,iBA+CJ,GAGhB,EAAK/O,OAASA,EAEd,EAAK6L,GACD7L,EAAO0W,eACN,EAAK8B,mBAAqB,EAAKqG,kBAAoB,IAExD,EAAKC,SAAW,EAAKjT,GAAGM,YAExB,EAAKuS,cAAgB,EAErB,EAAKK,eAAiB,KAEtB,EAAKC,QAAL,OACKZ,EAAU7Q,OAAO0R,OAAO,EAAKT,eAAL,UAD7B,IAEKH,EAAS9Q,OAAO0R,OAAO,EAAKT,eAAL,SAF5B,IAGKF,EAAW/Q,OAAO0R,OAAO,EAAKT,eAAL,WAH9B,GAMA,EAAK3R,OAAS,KAEd,EAAK+N,QAAU,GAEf,EAAKsE,SAAW,KAEhB,EAAKC,YAAc,KAEnB,EAAKC,UAAY,KAEjB,EAAKhhB,MAAQ,KAEb,EAAKkO,aAAe,EAEpB,EAAKP,SAAW,KAEhB,EAAKsT,WAAa,KAElB,EAAKC,OAAS,KAEd,EAAKC,WAAa,GAElB,EAAKC,MAAQ,KAEb,EAAK5Q,iBAAmB5O,EAAOoM,SAI/B,EAAKuD,WAAY,EAjDD,E,6CArCpB,WACI,SAAUuG,OAAOuJ,eAAgBvJ,OAAOwJ,sB,6BAQ5C,WAKI,OAJKxJ,OAAOyJ,yBACRzJ,OAAOyJ,uBAAyB,IAAKzJ,OAAOuJ,cACxCvJ,OAAOwJ,qBAERxJ,OAAOyJ,yB,oCAUlB,SAAuBC,GAKnB,OAJK1J,OAAO2J,gCACR3J,OAAO2J,8BAAgC,IAAK3J,OAAO4J,qBAC/C5J,OAAO6J,2BAA2B,EAAG,EAAGH,IAEzC1J,OAAO2J,gC,kBA+DlB,WACInkB,KAAK4P,mBACL5P,KAAK6P,mBACL7P,KAAK8P,qBAEL9P,KAAKskB,SAAS3B,GACd3iB,KAAKyP,gBAAgBzP,KAAKsE,OAAOoL,WACjC1P,KAAKukB,UAAU,K,+BAInB,WACQvkB,KAAKkf,UACLlf,KAAKkf,QAAQ9T,SAAQ,SAAAiD,GACjBA,GAAUA,EAAOmW,gBAErBxkB,KAAKkf,QAAU,KAEflf,KAAKqQ,SAASD,QAAQpQ,KAAKwjB,a,sBASnC,SAASM,GACD9jB,KAAK8jB,QAAU9jB,KAAKsjB,OAAOQ,KAC3B9jB,KAAK8jB,MAAQ9jB,KAAKsjB,OAAOQ,GACzB9jB,KAAK8jB,MAAM3F,KAAKjP,KAAKlP,S,uBAS7B,WAAsB,2BAATkf,EAAS,yBAATA,EAAS,gBAClBlf,KAAKykB,WAAWvF,K,wBAWpB,SAAWA,GAEPlf,KAAK0kB,oBAGDxF,GAAWA,EAAQhc,SACnBlD,KAAKkf,QAAUA,EAGflf,KAAKqQ,SAASmU,aAGdtF,EACKyF,QAAO,SAACC,EAAMC,GAEX,OADAD,EAAKxU,QAAQyU,GACNA,IACR7kB,KAAKqQ,UACPD,QAAQpQ,KAAKwjB,a,8BAI1B,WACQxjB,KAAKsE,OAAO2W,qBACZjb,KAAK2jB,WAAa3jB,KAAKsE,OAAO2W,qBAE1Bjb,KAAKmQ,GAAG2U,sBACR9kB,KAAK2jB,WAAa3jB,KAAKmQ,GAAG2U,sBACtB3Q,EAAS4Q,kBAGb/kB,KAAK2jB,WAAa3jB,KAAKmQ,GAAG6U,qBACtB7Q,EAAS4Q,kBAIrB/kB,KAAK2jB,WAAWvT,QAAQpQ,KAAKmQ,GAAG8U,e,+BAIpC,WAAoB,WAChBjlB,KAAK2jB,WAAWuB,eAAiB,WAC7B,IAAMtR,EAAO,EAAKzB,iBAEdyB,GAAQ,EAAKP,eACb,EAAKiR,SAAS1B,GACd,EAAKrb,UAAU,UACRqM,GAAQ,EAAKyP,eACpB,EAAKvS,QACE,EAAKgT,QAAU,EAAKR,OAAL,SACtB,EAAK/b,UAAU,eAAgBqM,M,kCAM3C,WACI5T,KAAK2jB,WAAWuB,eAAiB,O,gCAGrC,WACIllB,KAAKqQ,SAAWrQ,KAAKmQ,GAAGgV,iBACxBnlB,KAAKqQ,SAASD,QAAQpQ,KAAKwjB,Y,8BAO/B,WAEQxjB,KAAKmQ,GAAGiV,WACRplB,KAAKwjB,SAAWxjB,KAAKmQ,GAAGiV,aAExBplB,KAAKwjB,SAAWxjB,KAAKmQ,GAAGkV,iBAG5BrlB,KAAKwjB,SAASpT,QAAQpQ,KAAKmQ,GAAG8U,e,uBAUlC,SAAUnR,GACN,GAAIA,EAAU,CAMV,IAAIwR,EAAQ,IAAI9K,OAAO+K,MACvB,IAAKD,EAAMvR,UACP,OAAO/P,QAAQgQ,OACX,IAAIlB,MAAM,+CAGlBwS,EAAM/S,UAAW,EACjB,IAAMiT,EAAOxlB,KAAKmQ,GAAGsV,+BAKrB,OAJAzlB,KAAKwjB,SAASgB,aACdxkB,KAAKwjB,SAASpT,QAAQoV,GACtBF,EAAMI,UAAYF,EAAKG,OAEhBL,EAAMvR,UAAUD,GAEvB,OAAO9P,QAAQgQ,OAAO,IAAIlB,MAAM,qBAAuBgB,M,uBAS/D,SAAUR,GACNtT,KAAKwjB,SAASoC,KAAKC,eAAevS,EAAOtT,KAAKmQ,GAAGM,e,uBAQrD,WACI,OAAOzQ,KAAKwjB,SAASoC,KAAKtS,Q,+BAW9B,SAAkBqJ,EAAa1D,EAAU6M,GAChC9lB,KAAK0jB,YACN1jB,KAAK0jB,UAAY1jB,KAAK+lB,uBAClB/lB,KAAKmQ,IAAMnQ,KAAKmQ,GAAG+T,WAAalkB,KAAKmQ,GAAG+T,WAAa,QAGzD,uBAAwB1J,OAGxBxa,KAAK0jB,UAAUsC,gBACXrJ,GACA,SAAAiD,GAAI,OAAI3G,EAAS2G,KACjBkG,GAGJ9lB,KAAK0jB,UAAUsC,gBAAgBrJ,GAAalG,MACxC,SAACmJ,GAAD,OAAU3G,EAAS2G,MACrBlI,OACE,SAACwJ,GAAD,OAAS4E,EAAQ5E,Q,sBAW7B,SAASxe,EAAOgO,GACI,MAAZA,IACA1Q,KAAKkT,iBAAmBxC,GAE5B1Q,KAAK0C,MAAQA,I,uBAQjB,SAAUQ,GAEN,IAAIlD,KAAKyjB,aAAevgB,GAAU,EAAIlD,KAAKyjB,YAAYvgB,OAAS,EAAI,EAApE,CAIAlD,KAAK6jB,WAAa,GAClB7jB,KAAKyjB,YAAc,GAGnB,IACInV,EADEJ,EAAWlO,KAAKmR,OAASnR,KAAKmR,OAAO8U,iBAAmB,EAE9D,IAAK3X,EAAI,EAAGA,EAAIJ,EAAUI,IACtBtO,KAAK6jB,WAAWvV,GAAK,GACrBtO,KAAK6jB,WAAWvV,GAAG,GAAKpL,EAAS,IAAM,EACvClD,KAAK6jB,WAAWvV,GAAG,GAAKpL,EAAS,GAAK,GAAK,EAE/ClD,KAAKyjB,YAAY,GAAKvgB,EAAS,IAAM,EACrClD,KAAKyjB,YAAY,GAAKvgB,EAAS,GAAK,GAAK,K,sBAY7C,SAASA,EAAQC,EAAOqJ,GACpB,GAAIxM,KAAK0C,MACL,OAAO1C,KAAK0C,MAEhB,IAAK1C,KAAKmR,OACN,MAAO,GAQX,GALAhO,EAAQA,GAAS,EACjBqJ,EAAOA,GAAQtJ,EAAS,EAExBlD,KAAKukB,UAAUrhB,IAEVlD,KAAKmR,OACN,OAAOnR,KAAKsE,OAAOqJ,cACb3N,KAAK6jB,WACL7jB,KAAKyjB,YAUf,IAAKzjB,KAAKmR,OAAOjO,OAAQ,CACrB,IAAMgjB,EAAYlmB,KAAKmmB,aAAa,EAAG,KAAMnmB,KAAKkkB,YAClDlkB,KAAKmR,OAAS+U,EAAU/U,OAG5B,IAGI7C,EAHE8X,EAAapmB,KAAKmR,OAAOjO,OAASA,EAClCmjB,KAAgBD,EAAa,KAAO,EACpClY,EAAWlO,KAAKmR,OAAO8U,iBAG7B,IAAK3X,EAAI,EAAGA,EAAIJ,EAAUI,IAAK,CAC3B,IAAM5L,EAAQ1C,KAAK6jB,WAAWvV,GACxBgY,EAAOtmB,KAAKmR,OAAOoV,eAAejY,GACpCvL,OAAC,EAEL,IAAKA,EAAII,EAAOJ,GAAKyJ,EAAMzJ,IAAK,CAC5B,IAAMzC,KAAWyC,EAAIqjB,GACf7lB,KAASD,EAAQ8lB,GAOnB3d,EAAM6d,EAAKhmB,GACXkI,EAAMC,EACN7E,OAAC,EAEL,IAAKA,EAAItD,EAAOsD,EAAIrD,EAAKqD,GAAKyiB,EAAY,CACtC,IAAM/S,EAAQgT,EAAK1iB,GAEf0P,EAAQ9K,IACRA,EAAM8K,GAGNA,EAAQ7K,IACRA,EAAM6K,GAId5Q,EAAM,EAAIK,GAAKyF,EACf9F,EAAM,EAAIK,EAAI,GAAK0F,GAEV,GAAL6F,GAAU9F,EAAMxI,KAAKyjB,YAAY,EAAI1gB,MACrC/C,KAAKyjB,YAAY,EAAI1gB,GAAKyF,IAGrB,GAAL8F,GAAU7F,EAAMzI,KAAKyjB,YAAY,EAAI1gB,EAAI,MACzC/C,KAAKyjB,YAAY,EAAI1gB,EAAI,GAAK0F,IAK1C,OAAOzI,KAAKsE,OAAOqJ,cAAgB3N,KAAK6jB,WAAa7jB,KAAKyjB,c,+BAQ9D,WACI,OAAOzjB,KAAK8jB,MAAMzF,kBAAkBnP,KAAKlP,Q,8BAI7C,WACQA,KAAK4jB,QACL5jB,KAAK4jB,OAAOY,e,6BAMpB,WACIxkB,KAAK0kB,oBACL1kB,KAAKoiB,mBACLpiB,KAAKwjB,SAASgB,aACdxkB,KAAK2jB,WAAWa,aAChBxkB,KAAKqQ,SAASmU,aAGVxkB,KAAKsE,OAAOkiB,oBAGiB,mBAAlBxmB,KAAKmQ,GAAGgI,OACE,UAAjBnY,KAAKmQ,GAAG2T,OAER9jB,KAAKmQ,GAAGgI,QAGZnY,KAAKmQ,GAAK,KAGLnQ,KAAKsE,OAAO0W,aAGbhb,KAAKsE,OAAO0W,aAAe,KAF3BR,OAAOyJ,uBAAyB,KAKpCzJ,OAAO2J,8BAAgC,Q,qBAM/C,WACSnkB,KAAKkS,YACNlS,KAAK8Q,QAET9Q,KAAKkJ,QACLlJ,KAAKmR,OAAS,KACdnR,KAAKiU,WAAY,EAEjBjU,KAAKuQ,oB,kBAQT,SAAKY,GACDnR,KAAKgjB,cAAgB,EACrBhjB,KAAKojB,SAAWpjB,KAAKmQ,GAAGM,YACxBzQ,KAAKmR,OAASA,EACdnR,KAAKymB,iB,0BAIT,WACIzmB,KAAKoiB,mBACLpiB,KAAK4jB,OAAS5jB,KAAKmQ,GAAGuW,qBAGtB1mB,KAAK4jB,OAAOtjB,MAAQN,KAAK4jB,OAAOtjB,OAASN,KAAK4jB,OAAO+C,YACrD3mB,KAAK4jB,OAAOzB,KAAOniB,KAAK4jB,OAAOzB,MAAQniB,KAAK4jB,OAAOgD,QAEnD5mB,KAAKyP,gBAAgBzP,KAAK4Q,cAC1B5Q,KAAK4jB,OAAOzS,OAASnR,KAAKmR,OAC1BnR,KAAK4jB,OAAOxT,QAAQpQ,KAAKqQ,Y,gCAQ7B,WACyB,aAAjBrQ,KAAKmQ,GAAG2T,OACR9jB,KAAKmQ,GAAG0W,QAAU7mB,KAAKmQ,GAAG0W,W,sBASlC,WACI,OAAO7mB,KAAK8jB,QAAU9jB,KAAKsjB,OAAL,U,yBAQ1B,WACI,OAAItjB,KAAKkT,iBACElT,KAAKkT,iBAEXlT,KAAKmR,OAGHnR,KAAKmR,OAAOT,SAFR,I,oBAaf,SAAOpQ,EAAOC,GACV,GAAKP,KAAKmR,OAuBV,OAnBAnR,KAAKqjB,eAAiB,KAET,MAAT/iB,IACAA,EAAQN,KAAKmS,mBACAnS,KAAKqT,gBACd/S,EAAQ,GAGL,MAAPC,IACAA,EAAMP,KAAKqT,eAGfrT,KAAKgjB,cAAgB1iB,EACrBN,KAAKojB,SAAWpjB,KAAKmQ,GAAGM,YAEpBzQ,KAAK8jB,QAAU9jB,KAAKsjB,OAAL,UACftjB,KAAKskB,SAAS3B,GAGX,CACHriB,MAAOA,EACPC,IAAKA,K,2BASb,WACI,OAAQP,KAAKmQ,GAAGM,YAAczQ,KAAKojB,UAAYpjB,KAAK4Q,e,kBAUxD,SAAKtQ,EAAOC,GACR,GAAKP,KAAKmR,OAAV,CAKAnR,KAAKymB,eAEL,IAAMK,EAAe9mB,KAAKwT,OAAOlT,EAAOC,GAExCD,EAAQwmB,EAAaxmB,MACrBC,EAAMumB,EAAavmB,IAEnBP,KAAKqjB,eAAiB9iB,EAEtBP,KAAK4jB,OAAOtjB,MAAM,EAAGA,GAErBN,KAAKsQ,qBAELtQ,KAAKskB,SAAS5B,GAEd1iB,KAAKuH,UAAU,W,mBAMnB,WACIvH,KAAKqjB,eAAiB,KAEtBrjB,KAAKgjB,eAAiBhjB,KAAKijB,gBAC3BjjB,KAAK4jB,QAAU5jB,KAAK4jB,OAAOzB,KAAK,GAEhCniB,KAAKskB,SAAS3B,GAEd3iB,KAAKuH,UAAU,W,4BASnB,WACI,OAAOvH,KAAK8jB,MAAM3R,eAAejD,KAAKlP,Q,6BAQ1C,WACI,OAAOA,KAAK4Q,e,6BAQhB,SAAgB0C,GACZtT,KAAK4Q,aAAe0C,GAAS,EAC7BtT,KAAK4jB,QAAU5jB,KAAK4jB,OAAOhT,aAAaiV,eACpC7lB,KAAK4Q,aACL5Q,KAAKmQ,GAAGM,e,wBAUhB,SAAWlQ,GACPP,KAAKqjB,eAAiB9iB,O,6BAluBT4T,CAAiB5P,EAAKgF,U,YAAtB4K,EAEV4Q,iBAAmB,I,6BCA9B,SAAS1H,EAAS/E,EAAMyO,EAAM/e,GAC5B,IAAIgf,EAASzO,EAAM0O,EAASC,EAAWjH,EAGvC,SAASkH,IACP,IAAI3a,EAAO4a,KAAKC,MAAQH,EAEpB1a,EAAOua,GAAQva,GAAQ,EACzBwa,EAAU3N,WAAW8N,EAAOJ,EAAOva,IAEnCwa,EAAU,KACLhf,IACHiY,EAAS3H,EAAKc,MAAM6N,EAAS1O,GAC7B0O,EAAU1O,EAAO,OAXnB,MAAQwO,IAAMA,EAAO,KAgBzB,IAAIO,EAAY,WACdL,EAAUjnB,KACVuY,EAAOgP,UACPL,EAAYE,KAAKC,MACjB,IAAIG,EAAUxf,IAAcgf,EAO5B,OANKA,IAASA,EAAU3N,WAAW8N,EAAOJ,IACtCS,IACFvH,EAAS3H,EAAKc,MAAM6N,EAAS1O,GAC7B0O,EAAU1O,EAAO,MAGZ0H,GAoBT,OAjBAqH,EAAUG,MAAQ,WACZT,IACFU,aAAaV,GACbA,EAAU,OAIdM,EAAUK,MAAQ,WACZX,IACF/G,EAAS3H,EAAKc,MAAM6N,EAAS1O,GAC7B0O,EAAU1O,EAAO,KAEjBmP,aAAaV,GACbA,EAAU,OAIPM,EAITjK,EAASA,SAAWA,EAEpBxd,EAAOD,QAAUyd,ICpEbuK,EAA2B,GAG/B,SAASC,EAAoBC,GAE5B,IAAIC,EAAeH,EAAyBE,GAC5C,QAAqB3b,IAAjB4b,EACH,OAAOA,EAAanoB,QAGrB,IAAIC,EAAS+nB,EAAyBE,GAAY,CAGjDloB,QAAS,IAOV,OAHAooB,EAAoBF,GAAUjoB,EAAQA,EAAOD,QAASioB,GAG/ChoB,EAAOD,QClBWioB,CAAoB,KxBO9C,I,EuBTID","sources":["webpack://WaveSurfer/webpack/universalModuleDefinition","webpack://WaveSurfer/./src/drawer.canvasentry.js","webpack://WaveSurfer/./src/drawer.js","webpack://WaveSurfer/./src/drawer.multicanvas.js","webpack://WaveSurfer/./src/mediaelement-webaudio.js","webpack://WaveSurfer/./src/mediaelement.js","webpack://WaveSurfer/./src/peakcache.js","webpack://WaveSurfer/./src/util/absMax.js","webpack://WaveSurfer/./src/util/clamp.js","webpack://WaveSurfer/./src/util/fetch.js","webpack://WaveSurfer/./src/util/frame.js","webpack://WaveSurfer/./src/util/get-id.js","webpack://WaveSurfer/./src/util/index.js","webpack://WaveSurfer/./src/util/max.js","webpack://WaveSurfer/./src/util/min.js","webpack://WaveSurfer/./src/util/observer.js","webpack://WaveSurfer/./src/util/orientation.js","webpack://WaveSurfer/./src/util/prevent-click.js","webpack://WaveSurfer/./src/util/request-animation-frame.js","webpack://WaveSurfer/./src/util/style.js","webpack://WaveSurfer/./src/wavesurfer.js","webpack://WaveSurfer/./src/webaudio.js","webpack://WaveSurfer/./node_modules/debounce/index.js","webpack://WaveSurfer/webpack/bootstrap","webpack://WaveSurfer/webpack/startup"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine(\"WaveSurfer\", [], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"WaveSurfer\"] = factory();\n\telse\n\t\troot[\"WaveSurfer\"] = factory();\n})(this, function() {\nreturn ","/**\n * @since 3.0.0\n */\n\nimport style from './util/style';\nimport getId from './util/get-id';\n\n/**\n * The `CanvasEntry` class represents an element consisting of a wave `canvas`\n * and an (optional) progress wave `canvas`.\n *\n * The `MultiCanvas` renderer uses one or more `CanvasEntry` instances to\n * render a waveform, depending on the zoom level.\n */\nexport default class CanvasEntry {\n constructor() {\n /**\n * The wave node\n *\n * @type {HTMLCanvasElement}\n */\n this.wave = null;\n /**\n * The wave canvas rendering context\n *\n * @type {CanvasRenderingContext2D}\n */\n this.waveCtx = null;\n /**\n * The (optional) progress wave node\n *\n * @type {HTMLCanvasElement}\n */\n this.progress = null;\n /**\n * The (optional) progress wave canvas rendering context\n *\n * @type {CanvasRenderingContext2D}\n */\n this.progressCtx = null;\n /**\n * Start of the area the canvas should render, between 0 and 1\n *\n * @type {number}\n */\n this.start = 0;\n /**\n * End of the area the canvas should render, between 0 and 1\n *\n * @type {number}\n */\n this.end = 1;\n /**\n * Unique identifier for this entry\n *\n * @type {string}\n */\n this.id = getId(\n typeof this.constructor.name !== 'undefined'\n ? this.constructor.name.toLowerCase() + '_'\n : 'canvasentry_'\n );\n /**\n * Canvas 2d context attributes\n *\n * @type {object}\n */\n this.canvasContextAttributes = {};\n }\n\n /**\n * Store the wave canvas element and create the 2D rendering context\n *\n * @param {HTMLCanvasElement} element The wave `canvas` element.\n */\n initWave(element) {\n this.wave = element;\n this.waveCtx = this.wave.getContext('2d', this.canvasContextAttributes);\n }\n\n /**\n * Store the progress wave canvas element and create the 2D rendering\n * context\n *\n * @param {HTMLCanvasElement} element The progress wave `canvas` element.\n */\n initProgress(element) {\n this.progress = element;\n this.progressCtx = this.progress.getContext(\n '2d',\n this.canvasContextAttributes\n );\n }\n\n /**\n * Update the dimensions\n *\n * @param {number} elementWidth Width of the entry\n * @param {number} totalWidth Total width of the multi canvas renderer\n * @param {number} width The new width of the element\n * @param {number} height The new height of the element\n */\n updateDimensions(elementWidth, totalWidth, width, height) {\n // where the canvas starts and ends in the waveform, represented as a\n // decimal between 0 and 1\n this.start = this.wave.offsetLeft / totalWidth || 0;\n this.end = this.start + elementWidth / totalWidth;\n\n // set wave canvas dimensions\n this.wave.width = width;\n this.wave.height = height;\n let elementSize = { width: elementWidth + 'px' };\n style(this.wave, elementSize);\n\n if (this.hasProgressCanvas) {\n // set progress canvas dimensions\n this.progress.width = width;\n this.progress.height = height;\n style(this.progress, elementSize);\n }\n }\n\n /**\n * Clear the wave and progress rendering contexts\n */\n clearWave() {\n // wave\n this.waveCtx.clearRect(\n 0,\n 0,\n this.waveCtx.canvas.width,\n this.waveCtx.canvas.height\n );\n\n // progress\n if (this.hasProgressCanvas) {\n this.progressCtx.clearRect(\n 0,\n 0,\n this.progressCtx.canvas.width,\n this.progressCtx.canvas.height\n );\n }\n }\n\n /**\n * Set the fill styles for wave and progress\n *\n * @param {string} waveColor Fill color for the wave canvas\n * @param {?string} progressColor Fill color for the progress canvas\n */\n setFillStyles(waveColor, progressColor) {\n this.waveCtx.fillStyle = waveColor;\n\n if (this.hasProgressCanvas) {\n this.progressCtx.fillStyle = progressColor;\n }\n }\n\n /**\n * Set the canvas transforms for wave and progress\n *\n * @param {boolean} vertical Whether to render vertically\n */\n applyCanvasTransforms(vertical) {\n if (vertical) {\n // Reflect the waveform across the line y = -x\n this.waveCtx.setTransform(0, 1, 1, 0, 0, 0);\n\n if (this.hasProgressCanvas) {\n this.progressCtx.setTransform(0, 1, 1, 0, 0, 0);\n }\n }\n }\n\n /**\n * Draw a rectangle for wave and progress\n *\n * @param {number} x X start position\n * @param {number} y Y start position\n * @param {number} width Width of the rectangle\n * @param {number} height Height of the rectangle\n * @param {number} radius Radius of the rectangle\n */\n fillRects(x, y, width, height, radius) {\n this.fillRectToContext(this.waveCtx, x, y, width, height, radius);\n\n if (this.hasProgressCanvas) {\n this.fillRectToContext(\n this.progressCtx,\n x,\n y,\n width,\n height,\n radius\n );\n }\n }\n\n /**\n * Draw the actual rectangle on a `canvas` element\n *\n * @param {CanvasRenderingContext2D} ctx Rendering context of target canvas\n * @param {number} x X start position\n * @param {number} y Y start position\n * @param {number} width Width of the rectangle\n * @param {number} height Height of the rectangle\n * @param {number} radius Radius of the rectangle\n */\n fillRectToContext(ctx, x, y, width, height, radius) {\n if (!ctx) {\n return;\n }\n\n if (radius) {\n this.drawRoundedRect(ctx, x, y, width, height, radius);\n } else {\n ctx.fillRect(x, y, width, height);\n }\n }\n\n /**\n * Draw a rounded rectangle on Canvas\n *\n * @param {CanvasRenderingContext2D} ctx Canvas context\n * @param {number} x X-position of the rectangle\n * @param {number} y Y-position of the rectangle\n * @param {number} width Width of the rectangle\n * @param {number} height Height of the rectangle\n * @param {number} radius Radius of the rectangle\n *\n * @return {void}\n * @example drawRoundedRect(ctx, 50, 50, 5, 10, 3)\n */\n drawRoundedRect(ctx, x, y, width, height, radius) {\n if (height === 0) {\n return;\n }\n // peaks are float values from -1 to 1. Use absolute height values in\n // order to correctly calculate rounded rectangle coordinates\n if (height < 0) {\n height *= -1;\n y -= height;\n }\n ctx.beginPath();\n ctx.moveTo(x + radius, y);\n ctx.lineTo(x + width - radius, y);\n ctx.quadraticCurveTo(x + width, y, x + width, y + radius);\n ctx.lineTo(x + width, y + height - radius);\n ctx.quadraticCurveTo(\n x + width,\n y + height,\n x + width - radius,\n y + height\n );\n ctx.lineTo(x + radius, y + height);\n ctx.quadraticCurveTo(x, y + height, x, y + height - radius);\n ctx.lineTo(x, y + radius);\n ctx.quadraticCurveTo(x, y, x + radius, y);\n ctx.closePath();\n ctx.fill();\n }\n\n /**\n * Render the actual wave and progress lines\n *\n * @param {number[]} peaks Array with peaks data\n * @param {number} absmax Maximum peak value (absolute)\n * @param {number} halfH Half the height of the waveform\n * @param {number} offsetY Offset to the top\n * @param {number} start The x-offset of the beginning of the area that\n * should be rendered\n * @param {number} end The x-offset of the end of the area that\n * should be rendered\n */\n drawLines(peaks, absmax, halfH, offsetY, start, end) {\n this.drawLineToContext(\n this.waveCtx,\n peaks,\n absmax,\n halfH,\n offsetY,\n start,\n end\n );\n\n if (this.hasProgressCanvas) {\n this.drawLineToContext(\n this.progressCtx,\n peaks,\n absmax,\n halfH,\n offsetY,\n start,\n end\n );\n }\n }\n\n /**\n * Render the actual waveform line on a `canvas` element\n *\n * @param {CanvasRenderingContext2D} ctx Rendering context of target canvas\n * @param {number[]} peaks Array with peaks data\n * @param {number} absmax Maximum peak value (absolute)\n * @param {number} halfH Half the height of the waveform\n * @param {number} offsetY Offset to the top\n * @param {number} start The x-offset of the beginning of the area that\n * should be rendered\n * @param {number} end The x-offset of the end of the area that\n * should be rendered\n */\n drawLineToContext(ctx, peaks, absmax, halfH, offsetY, start, end) {\n if (!ctx) {\n return;\n }\n\n const length = peaks.length / 2;\n const first = Math.round(length * this.start);\n\n // use one more peak value to make sure we join peaks at ends -- unless,\n // of course, this is the last canvas\n const last = Math.round(length * this.end) + 1;\n\n const canvasStart = first;\n const canvasEnd = last;\n const scale = this.wave.width / (canvasEnd - canvasStart - 1);\n\n // optimization\n const halfOffset = halfH + offsetY;\n const absmaxHalf = absmax / halfH;\n\n ctx.beginPath();\n ctx.moveTo((canvasStart - first) * scale, halfOffset);\n\n ctx.lineTo(\n (canvasStart - first) * scale,\n halfOffset - Math.round((peaks[2 * canvasStart] || 0) / absmaxHalf)\n );\n\n let i, peak, h;\n for (i = canvasStart; i < canvasEnd; i++) {\n peak = peaks[2 * i] || 0;\n h = Math.round(peak / absmaxHalf);\n ctx.lineTo((i - first) * scale + this.halfPixel, halfOffset - h);\n }\n\n // draw the bottom edge going backwards, to make a single\n // closed hull to fill\n let j = canvasEnd - 1;\n for (j; j >= canvasStart; j--) {\n peak = peaks[2 * j + 1] || 0;\n h = Math.round(peak / absmaxHalf);\n ctx.lineTo((j - first) * scale + this.halfPixel, halfOffset - h);\n }\n\n ctx.lineTo(\n (canvasStart - first) * scale,\n halfOffset -\n Math.round((peaks[2 * canvasStart + 1] || 0) / absmaxHalf)\n );\n\n ctx.closePath();\n ctx.fill();\n }\n\n /**\n * Destroys this entry\n */\n destroy() {\n this.waveCtx = null;\n this.wave = null;\n\n this.progressCtx = null;\n this.progress = null;\n }\n\n /**\n * Return image data of the wave `canvas` element\n *\n * When using a `type` of `'blob'`, this will return a `Promise` that\n * resolves with a `Blob` instance.\n *\n * @param {string} format='image/png' An optional value of a format type.\n * @param {number} quality=0.92 An optional value between 0 and 1.\n * @param {string} type='dataURL' Either 'dataURL' or 'blob'.\n * @return {string|Promise} When using the default `'dataURL'` `type` this\n * returns a data URL. When using the `'blob'` `type` this returns a\n * `Promise` that resolves with a `Blob` instance.\n */\n getImage(format, quality, type) {\n if (type === 'blob') {\n return new Promise(resolve => {\n this.wave.toBlob(resolve, format, quality);\n });\n } else if (type === 'dataURL') {\n return this.wave.toDataURL(format, quality);\n }\n }\n}\n","import * as util from './util';\n\n/**\n * Parent class for renderers\n *\n * @extends {Observer}\n */\nexport default class Drawer extends util.Observer {\n /**\n * @param {HTMLElement} container The container node of the wavesurfer instance\n * @param {WavesurferParams} params The wavesurfer initialisation options\n */\n constructor(container, params) {\n super();\n\n this.container = util.withOrientation(container, params.vertical);\n /**\n * @type {WavesurferParams}\n */\n this.params = params;\n /**\n * The width of the renderer\n * @type {number}\n */\n this.width = 0;\n /**\n * The height of the renderer\n * @type {number}\n */\n this.height = params.height * this.params.pixelRatio;\n\n this.lastPos = 0;\n /**\n * The `<wave>` element which is added to the container\n * @type {HTMLElement}\n */\n this.wrapper = null;\n }\n\n /**\n * Alias of `util.style`\n *\n * @param {HTMLElement} el The element that the styles will be applied to\n * @param {Object} styles The map of propName: attribute, both are used as-is\n * @return {HTMLElement} el\n */\n style(el, styles) {\n return util.style(el, styles);\n }\n\n /**\n * Create the wrapper `<wave>` element, style it and set up the events for\n * interaction\n */\n createWrapper() {\n this.wrapper = util.withOrientation(\n this.container.appendChild(document.createElement('wave')),\n this.params.vertical\n );\n\n this.style(this.wrapper, {\n display: 'block',\n position: 'relative',\n userSelect: 'none',\n webkitUserSelect: 'none',\n height: this.params.height + 'px'\n });\n\n if (this.params.fillParent || this.params.scrollParent) {\n this.style(this.wrapper, {\n width: '100%',\n overflowX: this.params.hideScrollbar ? 'hidden' : 'auto',\n overflowY: 'hidden'\n });\n }\n\n this.setupWrapperEvents();\n }\n\n /**\n * Handle click event\n *\n * @param {Event} e Click event\n * @param {?boolean} noPrevent Set to true to not call `e.preventDefault()`\n * @return {number} Playback position from 0 to 1\n */\n handleEvent(e, noPrevent) {\n !noPrevent && e.preventDefault();\n\n const clientX = util.withOrientation(\n e.targetTouches ? e.targetTouches[0] : e,\n this.params.vertical\n ).clientX;\n const bbox = this.wrapper.getBoundingClientRect();\n\n const nominalWidth = this.width;\n const parentWidth = this.getWidth();\n const progressPixels = this.getProgressPixels(bbox, clientX);\n\n let progress;\n if (!this.params.fillParent && nominalWidth < parentWidth) {\n progress = progressPixels *\n (this.params.pixelRatio / nominalWidth) || 0;\n } else {\n progress = (progressPixels + this.wrapper.scrollLeft) /\n this.wrapper.scrollWidth || 0;\n }\n\n return util.clamp(progress, 0, 1);\n }\n\n getProgressPixels(wrapperBbox, clientX) {\n if (this.params.rtl) {\n return wrapperBbox.right - clientX;\n } else {\n return clientX - wrapperBbox.left;\n }\n }\n\n setupWrapperEvents() {\n this.wrapper.addEventListener('click', e => {\n const orientedEvent = util.withOrientation(e, this.params.vertical);\n const scrollbarHeight = this.wrapper.offsetHeight -\n this.wrapper.clientHeight;\n\n if (scrollbarHeight !== 0) {\n // scrollbar is visible. Check if click was on it\n const bbox = this.wrapper.getBoundingClientRect();\n if (orientedEvent.clientY >= bbox.bottom - scrollbarHeight) {\n // ignore mousedown as it was on the scrollbar\n return;\n }\n }\n\n if (this.params.interact) {\n this.fireEvent('click', e, this.handleEvent(e));\n }\n });\n\n this.wrapper.addEventListener('dblclick', e => {\n if (this.params.interact) {\n this.fireEvent('dblclick', e, this.handleEvent(e));\n }\n });\n\n this.wrapper.addEventListener('scroll', e =>\n this.fireEvent('scroll', e)\n );\n }\n\n /**\n * Draw peaks on the canvas\n *\n * @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays\n * for split channel rendering\n * @param {number} length The width of the area that should be drawn\n * @param {number} start The x-offset of the beginning of the area that\n * should be rendered\n * @param {number} end The x-offset of the end of the area that should be\n * rendered\n */\n drawPeaks(peaks, length, start, end) {\n if (!this.setWidth(length)) {\n this.clearWave();\n }\n\n this.params.barWidth\n ? this.drawBars(peaks, 0, start, end)\n : this.drawWave(peaks, 0, start, end);\n }\n\n /**\n * Scroll to the beginning\n */\n resetScroll() {\n if (this.wrapper !== null) {\n this.wrapper.scrollLeft = 0;\n }\n }\n\n /**\n * Recenter the view-port at a certain percent of the waveform\n *\n * @param {number} percent Value from 0 to 1 on the waveform\n */\n recenter(percent) {\n const position = this.wrapper.scrollWidth * percent;\n this.recenterOnPosition(position, true);\n }\n\n /**\n * Recenter the view-port on a position, either scroll there immediately or\n * in steps of 5 pixels\n *\n * @param {number} position X-offset in pixels\n * @param {boolean} immediate Set to true to immediately scroll somewhere\n */\n recenterOnPosition(position, immediate) {\n const scrollLeft = this.wrapper.scrollLeft;\n const half = ~~(this.wrapper.clientWidth / 2);\n const maxScroll = this.wrapper.scrollWidth - this.wrapper.clientWidth;\n let target = position - half;\n let offset = target - scrollLeft;\n\n if (maxScroll == 0) {\n // no need to continue if scrollbar is not there\n return;\n }\n\n // if the cursor is currently visible...\n if (!immediate && -half <= offset && offset < half) {\n // set rate at which waveform is centered\n let rate = this.params.autoCenterRate;\n\n // make rate depend on width of view and length of waveform\n rate /= half;\n rate *= maxScroll;\n\n offset = Math.max(-rate, Math.min(rate, offset));\n target = scrollLeft + offset;\n }\n\n // limit target to valid range (0 to maxScroll)\n target = Math.max(0, Math.min(maxScroll, target));\n // no use attempting to scroll if we're not moving\n if (target != scrollLeft) {\n this.wrapper.scrollLeft = target;\n }\n }\n\n /**\n * Get the current scroll position in pixels\n *\n * @return {number} Horizontal scroll position in pixels\n */\n getScrollX() {\n let x = 0;\n if (this.wrapper) {\n const pixelRatio = this.params.pixelRatio;\n x = Math.round(this.wrapper.scrollLeft * pixelRatio);\n\n // In cases of elastic scroll (safari with mouse wheel) you can\n // scroll beyond the limits of the container\n // Calculate and floor the scrollable extent to make sure an out\n // of bounds value is not returned\n // Ticket #1312\n if (this.params.scrollParent) {\n const maxScroll = ~~(\n this.wrapper.scrollWidth * pixelRatio -\n this.getWidth()\n );\n x = Math.min(maxScroll, Math.max(0, x));\n }\n }\n return x;\n }\n\n /**\n * Get the width of the container\n *\n * @return {number} The width of the container\n */\n getWidth() {\n return Math.round(this.container.clientWidth * this.params.pixelRatio);\n }\n\n /**\n * Set the width of the container\n *\n * @param {number} width The new width of the container\n * @return {boolean} Whether the width of the container was updated or not\n */\n setWidth(width) {\n if (this.width == width) {\n return false;\n }\n\n this.width = width;\n\n if (this.params.fillParent || this.params.scrollParent) {\n this.style(this.wrapper, {\n width: ''\n });\n } else {\n const newWidth = ~~(this.width / this.params.pixelRatio) + 'px';\n this.style(this.wrapper, {\n width: newWidth\n });\n }\n\n this.updateSize();\n return true;\n }\n\n /**\n * Set the height of the container\n *\n * @param {number} height The new height of the container.\n * @return {boolean} Whether the height of the container was updated or not\n */\n setHeight(height) {\n if (height == this.height) {\n return false;\n }\n this.height = height;\n\n this.style(this.wrapper, {\n height: ~~(this.height / this.params.pixelRatio) + 'px'\n });\n\n this.updateSize();\n return true;\n }\n\n /**\n * Called by wavesurfer when progress should be rendered\n *\n * @param {number} progress From 0 to 1\n */\n progress(progress) {\n const minPxDelta = 1 / this.params.pixelRatio;\n const pos = Math.round(progress * this.width) * minPxDelta;\n\n if (pos < this.lastPos || pos - this.lastPos >= minPxDelta) {\n this.lastPos = pos;\n\n if (this.params.scrollParent && this.params.autoCenter) {\n const newPos = ~~(this.wrapper.scrollWidth * progress);\n this.recenterOnPosition(\n newPos,\n this.params.autoCenterImmediately\n );\n }\n\n this.updateProgress(pos);\n }\n }\n\n /**\n * This is called when wavesurfer is destroyed\n */\n destroy() {\n this.unAll();\n if (this.wrapper) {\n if (this.wrapper.parentNode == this.container.domElement) {\n this.container.removeChild(this.wrapper.domElement);\n }\n this.wrapper = null;\n }\n }\n\n /* Renderer-specific methods */\n\n /**\n * Called after cursor related params have changed.\n *\n * @abstract\n */\n updateCursor() {}\n\n /**\n * Called when the size of the container changes so the renderer can adjust\n *\n * @abstract\n */\n updateSize() {}\n\n /**\n * Draw a waveform with bars\n *\n * @abstract\n * @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays for split channel\n * rendering\n * @param {number} channelIndex The index of the current channel. Normally\n * should be 0\n * @param {number} start The x-offset of the beginning of the area that\n * should be rendered\n * @param {number} end The x-offset of the end of the area that should be\n * rendered\n */\n drawBars(peaks, channelIndex, start, end) {}\n\n /**\n * Draw a waveform\n *\n * @abstract\n * @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays for split channel\n * rendering\n * @param {number} channelIndex The index of the current channel. Normally\n * should be 0\n * @param {number} start The x-offset of the beginning of the area that\n * should be rendered\n * @param {number} end The x-offset of the end of the area that should be\n * rendered\n */\n drawWave(peaks, channelIndex, start, end) {}\n\n /**\n * Clear the waveform\n *\n * @abstract\n */\n clearWave() {}\n\n /**\n * Render the new progress\n *\n * @abstract\n * @param {number} position X-Offset of progress position in pixels\n */\n updateProgress(position) {}\n}\n","import Drawer from './drawer';\nimport * as util from './util';\nimport CanvasEntry from './drawer.canvasentry';\n\n/**\n * MultiCanvas renderer for wavesurfer. Is currently the default and sole\n * builtin renderer.\n *\n * A `MultiCanvas` consists of one or more `CanvasEntry` instances, depending\n * on the zoom level.\n */\nexport default class MultiCanvas extends Drawer {\n /**\n * @param {HTMLElement} container The container node of the wavesurfer instance\n * @param {WavesurferParams} params The wavesurfer initialisation options\n */\n constructor(container, params) {\n super(container, params);\n\n /**\n * @type {number}\n */\n this.maxCanvasWidth = params.maxCanvasWidth;\n\n /**\n * @type {number}\n */\n this.maxCanvasElementWidth = Math.round(\n params.maxCanvasWidth / params.pixelRatio\n );\n\n /**\n * Whether or not the progress wave is rendered. If the `waveColor`\n * and `progressColor` are the same color it is not.\n *\n * @type {boolean}\n */\n this.hasProgressCanvas = params.waveColor != params.progressColor;\n\n /**\n * @type {number}\n */\n this.halfPixel = 0.5 / params.pixelRatio;\n\n /**\n * List of `CanvasEntry` instances.\n *\n * @type {Array}\n */\n this.canvases = [];\n\n /**\n * @type {HTMLElement}\n */\n this.progressWave = null;\n\n /**\n * Class used to generate entries.\n *\n * @type {function}\n */\n this.EntryClass = CanvasEntry;\n\n /**\n * Canvas 2d context attributes.\n *\n * @type {object}\n */\n this.canvasContextAttributes = params.drawingContextAttributes;\n\n /**\n * Overlap added between entries to prevent vertical white stripes\n * between `canvas` elements.\n *\n * @type {number}\n */\n this.overlap = 2 * Math.ceil(params.pixelRatio / 2);\n\n /**\n * The radius of the wave bars. Makes bars rounded\n *\n * @type {number}\n */\n this.barRadius = params.barRadius || 0;\n\n /**\n * Whether to render the waveform vertically. Defaults to false.\n *\n * @type {boolean}\n */\n this.vertical = params.vertical;\n }\n\n /**\n * Initialize the drawer\n */\n init() {\n this.createWrapper();\n this.createElements();\n }\n\n /**\n * Create the canvas elements and style them\n *\n */\n createElements() {\n this.progressWave = util.withOrientation(\n this.wrapper.appendChild(document.createElement('wave')),\n this.params.vertical\n );\n this.style(this.progressWave, {\n position: 'absolute',\n zIndex: 3,\n left: 0,\n top: 0,\n bottom: 0,\n overflow: 'hidden',\n width: '0',\n display: 'none',\n boxSizing: 'border-box',\n borderRightStyle: 'solid',\n pointerEvents: 'none'\n });\n\n this.addCanvas();\n this.updateCursor();\n }\n\n /**\n * Update cursor style\n */\n updateCursor() {\n this.style(this.progressWave, {\n borderRightWidth: this.params.cursorWidth + 'px',\n borderRightColor: this.params.cursorColor\n });\n }\n\n /**\n * Adjust to the updated size by adding or removing canvases\n */\n updateSize() {\n const totalWidth = Math.round(this.width / this.params.pixelRatio);\n const requiredCanvases = Math.ceil(\n totalWidth / (this.maxCanvasElementWidth + this.overlap)\n );\n\n // add required canvases\n while (this.canvases.length < requiredCanvases) {\n this.addCanvas();\n }\n\n // remove older existing canvases, if any\n while (this.canvases.length > requiredCanvases) {\n this.removeCanvas();\n }\n\n let canvasWidth = this.maxCanvasWidth + this.overlap;\n const lastCanvas = this.canvases.length - 1;\n this.canvases.forEach((entry, i) => {\n if (i == lastCanvas) {\n canvasWidth = this.width - this.maxCanvasWidth * lastCanvas;\n }\n this.updateDimensions(entry, canvasWidth, this.height);\n\n entry.clearWave();\n });\n }\n\n /**\n * Add a canvas to the canvas list\n *\n */\n addCanvas() {\n const entry = new this.EntryClass();\n entry.canvasContextAttributes = this.canvasContextAttributes;\n entry.hasProgressCanvas = this.hasProgressCanvas;\n entry.halfPixel = this.halfPixel;\n const leftOffset = this.maxCanvasElementWidth * this.canvases.length;\n\n // wave\n let wave = util.withOrientation(\n this.wrapper.appendChild(document.createElement('canvas')),\n this.params.vertical\n );\n this.style(wave, {\n position: 'absolute',\n zIndex: 2,\n left: leftOffset + 'px',\n top: 0,\n bottom: 0,\n height: '100%',\n pointerEvents: 'none'\n });\n entry.initWave(wave);\n\n // progress\n if (this.hasProgressCanvas) {\n let progress = util.withOrientation(\n this.progressWave.appendChild(document.createElement('canvas')),\n this.params.vertical\n );\n this.style(progress, {\n position: 'absolute',\n left: leftOffset + 'px',\n top: 0,\n bottom: 0,\n height: '100%'\n });\n entry.initProgress(progress);\n }\n\n this.canvases.push(entry);\n }\n\n /**\n * Pop single canvas from the list\n *\n */\n removeCanvas() {\n let lastEntry = this.canvases[this.canvases.length - 1];\n\n // wave\n lastEntry.wave.parentElement.removeChild(lastEntry.wave.domElement);\n\n // progress\n if (this.hasProgressCanvas) {\n lastEntry.progress.parentElement.removeChild(lastEntry.progress.domElement);\n }\n\n // cleanup\n if (lastEntry) {\n lastEntry.destroy();\n lastEntry = null;\n }\n\n this.canvases.pop();\n }\n\n /**\n * Update the dimensions of a canvas element\n *\n * @param {CanvasEntry} entry Target entry\n * @param {number} width The new width of the element\n * @param {number} height The new height of the element\n */\n updateDimensions(entry, width, height) {\n const elementWidth = Math.round(width / this.params.pixelRatio);\n const totalWidth = Math.round(this.width / this.params.pixelRatio);\n\n // update canvas dimensions\n entry.updateDimensions(elementWidth, totalWidth, width, height);\n\n // style element\n this.style(this.progressWave, { display: 'block' });\n }\n\n /**\n * Clear the whole multi-canvas\n */\n clearWave() {\n util.frame(() => {\n this.canvases.forEach(entry => entry.clearWave());\n })();\n }\n\n /**\n * Draw a waveform with bars\n *\n * @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays\n * for split channel rendering\n * @param {number} channelIndex The index of the current channel. Normally\n * should be 0. Must be an integer.\n * @param {number} start The x-offset of the beginning of the area that\n * should be rendered\n * @param {number} end The x-offset of the end of the area that should be\n * rendered\n * @returns {void}\n */\n drawBars(peaks, channelIndex, start, end) {\n return this.prepareDraw(\n peaks,\n channelIndex,\n start,\n end,\n ({ absmax, hasMinVals, height, offsetY, halfH, peaks, channelIndex: ch }) => {\n // if drawBars was called within ws.empty we don't pass a start and\n // don't want anything to happen\n if (start === undefined) {\n return;\n }\n // Skip every other value if there are negatives.\n const peakIndexScale = hasMinVals ? 2 : 1;\n const length = peaks.length / peakIndexScale;\n const bar = this.params.barWidth * this.params.pixelRatio;\n const gap =\n this.params.barGap === null\n ? Math.max(this.params.pixelRatio, ~~(bar / 2))\n : Math.max(\n this.params.pixelRatio,\n this.params.barGap * this.params.pixelRatio\n );\n const step = bar + gap;\n\n const scale = length / this.width;\n const first = start;\n const last = end;\n let i = first;\n\n for (i; i < last; i += step) {\n const peak =\n peaks[Math.floor(i * scale * peakIndexScale)] || 0;\n let h = Math.round((peak / absmax) * halfH);\n\n /* in case of silences, allow the user to specify that we\n * always draw *something* (normally a 1px high bar) */\n if (h == 0 && this.params.barMinHeight) {\n h = this.params.barMinHeight;\n }\n\n this.fillRect(\n i + this.halfPixel,\n halfH - h + offsetY,\n bar + this.halfPixel,\n h * 2,\n this.barRadius,\n ch\n );\n }\n }\n );\n }\n\n /**\n * Draw a waveform\n *\n * @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays\n * for split channel rendering\n * @param {number} channelIndex The index of the current channel. Normally\n * should be 0\n * @param {number?} start The x-offset of the beginning of the area that\n * should be rendered (If this isn't set only a flat line is rendered)\n * @param {number?} end The x-offset of the end of the area that should be\n * rendered\n * @returns {void}\n */\n drawWave(peaks, channelIndex, start, end) {\n return this.prepareDraw(\n peaks,\n channelIndex,\n start,\n end,\n ({ absmax, hasMinVals, height, offsetY, halfH, peaks, channelIndex }) => {\n if (!hasMinVals) {\n const reflectedPeaks = [];\n const len = peaks.length;\n let i = 0;\n for (i; i < len; i++) {\n reflectedPeaks[2 * i] = peaks[i];\n reflectedPeaks[2 * i + 1] = -peaks[i];\n }\n peaks = reflectedPeaks;\n }\n\n // if drawWave was called within ws.empty we don't pass a start and\n // end and simply want a flat line\n if (start !== undefined) {\n this.drawLine(peaks, absmax, halfH, offsetY, start, end, channelIndex);\n }\n\n // always draw a median line\n this.fillRect(\n 0,\n halfH + offsetY - this.halfPixel,\n this.width,\n this.halfPixel,\n this.barRadius,\n channelIndex\n );\n }\n );\n }\n\n /**\n * Tell the canvas entries to render their portion of the waveform\n *\n * @param {number[]} peaks Peaks data\n * @param {number} absmax Maximum peak value (absolute)\n * @param {number} halfH Half the height of the waveform\n * @param {number} offsetY Offset to the top\n * @param {number} start The x-offset of the beginning of the area that\n * should be rendered\n * @param {number} end The x-offset of the end of the area that\n * should be rendered\n * @param {channelIndex} channelIndex The channel index of the line drawn\n */\n drawLine(peaks, absmax, halfH, offsetY, start, end, channelIndex) {\n const { waveColor, progressColor } = this.params.splitChannelsOptions.channelColors[channelIndex] || {};\n this.canvases.forEach((entry, i) => {\n this.setFillStyles(entry, waveColor, progressColor);\n this.applyCanvasTransforms(entry, this.params.vertical);\n entry.drawLines(peaks, absmax, halfH, offsetY, start, end);\n });\n }\n\n /**\n * Draw a rectangle on the multi-canvas\n *\n * @param {number} x X-position of the rectangle\n * @param {number} y Y-position of the rectangle\n * @param {number} width Width of the rectangle\n * @param {number} height Height of the rectangle\n * @param {number} radius Radius of the rectangle\n * @param {channelIndex} channelIndex The channel index of the bar drawn\n */\n fillRect(x, y, width, height, radius, channelIndex) {\n const startCanvas = Math.floor(x / this.maxCanvasWidth);\n const endCanvas = Math.min(\n Math.ceil((x + width) / this.maxCanvasWidth) + 1,\n this.canvases.length\n );\n let i = startCanvas;\n for (i; i < endCanvas; i++) {\n const entry = this.canvases[i];\n const leftOffset = i * this.maxCanvasWidth;\n\n const intersection = {\n x1: Math.max(x, i * this.maxCanvasWidth),\n y1: y,\n x2: Math.min(\n x + width,\n i * this.maxCanvasWidth + entry.wave.width\n ),\n y2: y + height\n };\n\n if (intersection.x1 < intersection.x2) {\n const { waveColor, progressColor } = this.params.splitChannelsOptions.channelColors[channelIndex] || {};\n this.setFillStyles(entry, waveColor, progressColor);\n this.applyCanvasTransforms(entry, this.params.vertical);\n\n entry.fillRects(\n intersection.x1 - leftOffset,\n intersection.y1,\n intersection.x2 - intersection.x1,\n intersection.y2 - intersection.y1,\n radius\n );\n }\n }\n }\n\n /**\n * Returns whether to hide the channel from being drawn based on params.\n *\n * @param {number} channelIndex The index of the current channel.\n * @returns {bool} True to hide the channel, false to draw.\n */\n hideChannel(channelIndex) {\n return this.params.splitChannels && this.params.splitChannelsOptions.filterChannels.includes(channelIndex);\n }\n\n /**\n * Performs preparation tasks and calculations which are shared by `drawBars`\n * and `drawWave`\n *\n * @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays for\n * split channel rendering\n * @param {number} channelIndex The index of the current channel. Normally\n * should be 0\n * @param {number?} start The x-offset of the beginning of the area that\n * should be rendered. If this isn't set only a flat line is rendered\n * @param {number?} end The x-offset of the end of the area that should be\n * rendered\n * @param {function} fn The render function to call, e.g. `drawWave`\n * @param {number} drawIndex The index of the current channel after filtering.\n * @param {number?} normalizedMax Maximum modulation value across channels for use with relativeNormalization. Ignored when undefined\n * @returns {void}\n */\n prepareDraw(peaks, channelIndex, start, end, fn, drawIndex, normalizedMax) {\n return util.frame(() => {\n // Split channels and call this function with the channelIndex set\n if (peaks[0] instanceof Array) {\n const channels = peaks;\n\n if (this.params.splitChannels) {\n const filteredChannels = channels.filter((c, i) => !this.hideChannel(i));\n if (!this.params.splitChannelsOptions.overlay) {\n this.setHeight(\n Math.max(filteredChannels.length, 1) *\n this.params.height *\n this.params.pixelRatio\n );\n }\n\n let overallAbsMax;\n if (this.params.splitChannelsOptions && this.params.splitChannelsOptions.relativeNormalization) {\n // calculate maximum peak across channels to use for normalization\n overallAbsMax = util.max(channels.map((channelPeaks => util.absMax(channelPeaks))));\n }\n\n\n return channels.forEach((channelPeaks, i) =>\n this.prepareDraw(channelPeaks, i, start, end, fn, filteredChannels.indexOf(channelPeaks), overallAbsMax)\n );\n }\n peaks = channels[0];\n }\n\n // Return and do not draw channel peaks if hidden.\n if (this.hideChannel(channelIndex)) {\n return;\n }\n\n // calculate maximum modulation value, either from the barHeight\n // parameter or if normalize=true from the largest value in the peak\n // set\n let absmax = 1 / this.params.barHeight;\n if (this.params.normalize) {\n absmax = normalizedMax === undefined ? util.absMax(peaks) : normalizedMax;\n }\n\n // Bar wave draws the bottom only as a reflection of the top,\n // so we don't need negative values\n const hasMinVals = [].some.call(peaks, val => val < 0);\n const height = this.params.height * this.params.pixelRatio;\n const halfH = height / 2;\n\n let offsetY = height * drawIndex || 0;\n\n // Override offsetY if overlay is true\n if (this.params.splitChannelsOptions && this.params.splitChannelsOptions.overlay) {\n offsetY = 0;\n }\n\n return fn({\n absmax: absmax,\n hasMinVals: hasMinVals,\n height: height,\n offsetY: offsetY,\n halfH: halfH,\n peaks: peaks,\n channelIndex: channelIndex\n });\n })();\n }\n\n /**\n * Set the fill styles for a certain entry (wave and progress)\n *\n * @param {CanvasEntry} entry Target entry\n * @param {string} waveColor Wave color to draw this entry\n * @param {string} progressColor Progress color to draw this entry\n */\n setFillStyles(entry, waveColor = this.params.waveColor, progressColor = this.params.progressColor) {\n entry.setFillStyles(waveColor, progressColor);\n }\n\n /**\n * Set the canvas transforms for a certain entry (wave and progress)\n *\n * @param {CanvasEntry} entry Target entry\n * @param {boolean} vertical Whether to render the waveform vertically\n */\n applyCanvasTransforms(entry, vertical = false) {\n entry.applyCanvasTransforms(vertical);\n }\n\n /**\n * Return image data of the multi-canvas\n *\n * When using a `type` of `'blob'`, this will return a `Promise`.\n *\n * @param {string} format='image/png' An optional value of a format type.\n * @param {number} quality=0.92 An optional value between 0 and 1.\n * @param {string} type='dataURL' Either 'dataURL' or 'blob'.\n * @return {string|string[]|Promise} When using the default `'dataURL'`\n * `type` this returns a single data URL or an array of data URLs,\n * one for each canvas. When using the `'blob'` `type` this returns a\n * `Promise` that resolves with an array of `Blob` instances, one for each\n * canvas.\n */\n getImage(format, quality, type) {\n if (type === 'blob') {\n return Promise.all(\n this.canvases.map(entry => {\n return entry.getImage(format, quality, type);\n })\n );\n } else if (type === 'dataURL') {\n let images = this.canvases.map(entry =>\n entry.getImage(format, quality, type)\n );\n return images.length > 1 ? images : images[0];\n }\n }\n\n /**\n * Render the new progress\n *\n * @param {number} position X-offset of progress position in pixels\n */\n updateProgress(position) {\n this.style(this.progressWave, { width: position + 'px' });\n }\n}\n","import MediaElement from './mediaelement';\n\n/**\n * MediaElementWebAudio backend: load audio via an HTML5 audio tag, but playback with the WebAudio API.\n * The advantage here is that the html5 <audio> tag can perform range requests on the server and not\n * buffer the entire file in one request, and you still get the filtering and scripting functionality\n * of the webaudio API.\n * Note that in order to use range requests and prevent buffering, you must provide peak data.\n *\n * @since 3.2.0\n */\nexport default class MediaElementWebAudio extends MediaElement {\n /**\n * Construct the backend\n *\n * @param {WavesurferParams} params Wavesurfer parameters\n */\n constructor(params) {\n super(params);\n /** @private */\n this.params = params;\n /** @private */\n this.sourceMediaElement = null;\n }\n\n /**\n * Initialise the backend, called in `wavesurfer.createBackend()`\n */\n init() {\n this.setPlaybackRate(this.params.audioRate);\n this.createTimer();\n this.createVolumeNode();\n this.createScriptNode();\n this.createAnalyserNode();\n }\n /**\n * Private method called by both `load` (from url)\n * and `loadElt` (existing media element) methods.\n *\n * @param {HTMLMediaElement} media HTML5 Audio or Video element\n * @param {number[]|Number.<Array[]>} peaks Array of peak data\n * @param {string} preload HTML 5 preload attribute value\n * @private\n */\n _load(media, peaks, preload) {\n super._load(media, peaks, preload);\n this.createMediaElementSource(media);\n }\n\n /**\n * Create MediaElementSource node\n *\n * @since 3.2.0\n * @param {HTMLMediaElement} mediaElement HTML5 Audio to load\n */\n createMediaElementSource(mediaElement) {\n this.sourceMediaElement = this.ac.createMediaElementSource(\n mediaElement\n );\n this.sourceMediaElement.connect(this.analyser);\n }\n\n play(start, end) {\n this.resumeAudioContext();\n return super.play(start, end);\n }\n\n /**\n * This is called when wavesurfer is destroyed\n *\n */\n destroy() {\n super.destroy();\n\n this.destroyWebAudio();\n }\n}\n","import WebAudio from './webaudio';\nimport * as util from './util';\n\n/**\n * MediaElement backend\n */\nexport default class MediaElement extends WebAudio {\n /**\n * Construct the backend\n *\n * @param {WavesurferParams} params Wavesurfer parameters\n */\n constructor(params) {\n super(params);\n /** @private */\n this.params = params;\n\n /**\n * Initially a dummy media element to catch errors. Once `_load` is\n * called, this will contain the actual `HTMLMediaElement`.\n * @private\n */\n this.media = {\n currentTime: 0,\n duration: 0,\n paused: true,\n playbackRate: 1,\n play() {},\n pause() {},\n volume: 0\n };\n\n /** @private */\n this.mediaType = params.mediaType.toLowerCase();\n /** @private */\n this.elementPosition = params.elementPosition;\n /** @private */\n this.peaks = null;\n /** @private */\n this.playbackRate = 1;\n /** @private */\n this.volume = 1;\n /** @private */\n this.isMuted = false;\n /** @private */\n this.buffer = null;\n /** @private */\n this.onPlayEnd = null;\n /** @private */\n this.mediaListeners = {};\n }\n\n /**\n * Initialise the backend, called in `wavesurfer.createBackend()`\n */\n init() {\n this.setPlaybackRate(this.params.audioRate);\n this.createTimer();\n }\n\n /**\n * Attach event listeners to media element.\n */\n _setupMediaListeners() {\n this.mediaListeners.error = () => {\n this.fireEvent('error', 'Error loading media element');\n };\n this.mediaListeners.canplay = () => {\n this.fireEvent('canplay');\n };\n this.mediaListeners.ended = () => {\n this.fireEvent('finish');\n };\n // listen to and relay play, pause and seeked events to enable\n // playback control from the external media element\n this.mediaListeners.play = () => {\n this.fireEvent('play');\n };\n this.mediaListeners.pause = () => {\n this.fireEvent('pause');\n };\n this.mediaListeners.seeked = event => {\n this.fireEvent('seek');\n };\n this.mediaListeners.volumechange = event => {\n this.isMuted = this.media.muted;\n if (this.isMuted) {\n this.volume = 0;\n } else {\n this.volume = this.media.volume;\n }\n this.fireEvent('volume');\n };\n\n // reset event listeners\n Object.keys(this.mediaListeners).forEach(id => {\n this.media.removeEventListener(id, this.mediaListeners[id]);\n this.media.addEventListener(id, this.mediaListeners[id]);\n });\n }\n\n /**\n * Create a timer to provide a more precise `audioprocess` event.\n */\n createTimer() {\n const onAudioProcess = () => {\n if (this.isPaused()) {\n return;\n }\n this.fireEvent('audioprocess', this.getCurrentTime());\n\n // Call again in the next frame\n util.frame(onAudioProcess)();\n };\n\n this.on('play', onAudioProcess);\n\n // Update the progress one more time to prevent it from being stuck in\n // case of lower framerates\n this.on('pause', () => {\n this.fireEvent('audioprocess', this.getCurrentTime());\n });\n }\n\n /**\n * Create media element with url as its source,\n * and append to container element.\n *\n * @param {string} url Path to media file\n * @param {HTMLElement} container HTML element\n * @param {number[]|Number.<Array[]>} peaks Array of peak data\n * @param {string} preload HTML 5 preload attribute value\n * @throws Will throw an error if the `url` argument is not a valid media\n * element.\n */\n load(url, container, peaks, preload) {\n const media = document.createElement(this.mediaType);\n media.controls = this.params.mediaControls;\n media.autoplay = this.params.autoplay || false;\n media.preload = preload == null ? 'auto' : preload;\n media.src = url;\n media.style.width = '100%';\n\n const prevMedia = container.querySelector(this.mediaType);\n if (prevMedia) {\n container.removeChild(prevMedia);\n }\n container.appendChild(media);\n\n this._load(media, peaks, preload);\n }\n\n /**\n * Load existing media element.\n *\n * @param {HTMLMediaElement} elt HTML5 Audio or Video element\n * @param {number[]|Number.<Array[]>} peaks Array of peak data\n */\n loadElt(elt, peaks) {\n elt.controls = this.params.mediaControls;\n elt.autoplay = this.params.autoplay || false;\n\n this._load(elt, peaks, elt.preload);\n }\n\n /**\n * Method called by both `load` (from url)\n * and `loadElt` (existing media element) methods.\n *\n * @param {HTMLMediaElement} media HTML5 Audio or Video element\n * @param {number[]|Number.<Array[]>} peaks Array of peak data\n * @param {string} preload HTML 5 preload attribute value\n * @throws Will throw an error if the `media` argument is not a valid media\n * element.\n * @private\n */\n _load(media, peaks, preload) {\n // verify media element is valid\n if (\n !(media instanceof HTMLMediaElement) ||\n typeof media.addEventListener === 'undefined'\n ) {\n throw new Error('media parameter is not a valid media element');\n }\n\n // load must be called manually on iOS, otherwise peaks won't draw\n // until a user interaction triggers load --> 'ready' event\n //\n // note that we avoid calling media.load here when given peaks and preload == 'none'\n // as this almost always triggers some browser fetch of the media.\n if (typeof media.load == 'function' && !(peaks && preload == 'none')) {\n // Resets the media element and restarts the media resource. Any\n // pending events are discarded. How much media data is fetched is\n // still affected by the preload attribute.\n media.load();\n }\n\n this.media = media;\n this._setupMediaListeners();\n this.peaks = peaks;\n this.onPlayEnd = null;\n this.buffer = null;\n this.isMuted = media.muted;\n this.setPlaybackRate(this.playbackRate);\n this.setVolume(this.volume);\n }\n\n /**\n * Used by `wavesurfer.isPlaying()` and `wavesurfer.playPause()`\n *\n * @return {boolean} Media paused or not\n */\n isPaused() {\n return !this.media || this.media.paused;\n }\n\n /**\n * Used by `wavesurfer.getDuration()`\n *\n * @return {number} Duration\n */\n getDuration() {\n if (this.explicitDuration) {\n return this.explicitDuration;\n }\n let duration = (this.buffer || this.media).duration;\n if (duration >= Infinity) {\n // streaming audio\n duration = this.media.seekable.end(0);\n }\n return duration;\n }\n\n /**\n * Returns the current time in seconds relative to the audio-clip's\n * duration.\n *\n * @return {number} Current time\n */\n getCurrentTime() {\n return this.media && this.media.currentTime;\n }\n\n /**\n * Get the position from 0 to 1\n *\n * @return {number} Current position\n */\n getPlayedPercents() {\n return this.getCurrentTime() / this.getDuration() || 0;\n }\n\n /**\n * Get the audio source playback rate.\n *\n * @return {number} Playback rate\n */\n getPlaybackRate() {\n return this.playbackRate || this.media.playbackRate;\n }\n\n /**\n * Set the audio source playback rate.\n *\n * @param {number} value Playback rate\n */\n setPlaybackRate(value) {\n this.playbackRate = value || 1;\n this.media.playbackRate = this.playbackRate;\n }\n\n /**\n * Used by `wavesurfer.seekTo()`\n *\n * @param {number} start Position to start at in seconds\n */\n seekTo(start) {\n if (start != null) {\n this.media.currentTime = start;\n }\n this.clearPlayEnd();\n }\n\n /**\n * Plays the loaded audio region.\n *\n * @param {number} start Start offset in seconds, relative to the beginning\n * of a clip.\n * @param {number} end When to stop, relative to the beginning of a clip.\n * @emits MediaElement#play\n * @return {Promise} Result\n */\n play(start, end) {\n this.seekTo(start);\n const promise = this.media.play();\n end && this.setPlayEnd(end);\n\n return promise;\n }\n\n /**\n * Pauses the loaded audio.\n *\n * @emits MediaElement#pause\n * @return {Promise} Result\n */\n pause() {\n let promise;\n\n if (this.media) {\n promise = this.media.pause();\n }\n this.clearPlayEnd();\n\n return promise;\n }\n\n /**\n * Set the play end\n *\n * @param {number} end Where to end\n */\n setPlayEnd(end) {\n this.clearPlayEnd();\n\n this._onPlayEnd = time => {\n if (time >= end) {\n this.pause();\n this.seekTo(end);\n }\n };\n this.on('audioprocess', this._onPlayEnd);\n }\n\n /** @private */\n clearPlayEnd() {\n if (this._onPlayEnd) {\n this.un('audioprocess', this._onPlayEnd);\n this._onPlayEnd = null;\n }\n }\n\n /**\n * Compute the max and min value of the waveform when broken into\n * <length> subranges.\n *\n * @param {number} length How many subranges to break the waveform into.\n * @param {number} first First sample in the required range.\n * @param {number} last Last sample in the required range.\n * @return {number[]|Number.<Array[]>} Array of 2*<length> peaks or array of\n * arrays of peaks consisting of (max, min) values for each subrange.\n */\n getPeaks(length, first, last) {\n if (this.buffer) {\n return super.getPeaks(length, first, last);\n }\n return this.peaks || [];\n }\n\n /**\n * Set the sink id for the media player\n *\n * @param {string} deviceId String value representing audio device id.\n * @returns {Promise} A Promise that resolves to `undefined` when there\n * are no errors.\n */\n setSinkId(deviceId) {\n if (deviceId) {\n if (!this.media.setSinkId) {\n return Promise.reject(\n new Error('setSinkId is not supported in your browser')\n );\n }\n return this.media.setSinkId(deviceId);\n }\n\n return Promise.reject(new Error('Invalid deviceId: ' + deviceId));\n }\n\n /**\n * Get the current volume\n *\n * @return {number} value A floating point value between 0 and 1.\n */\n getVolume() {\n return this.volume;\n }\n\n /**\n * Set the audio volume\n *\n * @param {number} value A floating point value between 0 and 1.\n */\n setVolume(value) {\n this.volume = value;\n // no need to change when it's already at that volume\n if (this.media.volume !== this.volume) {\n this.media.volume = this.volume;\n }\n }\n\n /**\n * Enable or disable muted audio\n *\n * @since 4.0.0\n * @param {boolean} muted Specify `true` to mute audio.\n */\n setMute(muted) {\n // This causes a volume change to be emitted too through the\n // volumechange event listener.\n this.isMuted = this.media.muted = muted;\n }\n\n /**\n * This is called when wavesurfer is destroyed\n *\n */\n destroy() {\n this.pause();\n this.unAll();\n this.destroyed = true;\n\n // cleanup media event listeners\n Object.keys(this.mediaListeners).forEach(id => {\n if (this.media) {\n this.media.removeEventListener(id, this.mediaListeners[id]);\n }\n });\n\n if (\n this.params.removeMediaElementOnDestroy &&\n this.media &&\n this.media.parentNode\n ) {\n this.media.parentNode.removeChild(this.media);\n }\n\n this.media = null;\n }\n}\n","/**\n * Caches the decoded peaks data to improve rendering speed for large audio\n *\n * Is used if the option parameter `partialRender` is set to `true`\n */\nexport default class PeakCache {\n /**\n * Instantiate cache\n */\n constructor() {\n this.clearPeakCache();\n }\n\n /**\n * Empty the cache\n */\n clearPeakCache() {\n /**\n * Flat array with entries that are always in pairs to mark the\n * beginning and end of each subrange. This is a convenience so we can\n * iterate over the pairs for easy set difference operations.\n * @private\n */\n this.peakCacheRanges = [];\n /**\n * Length of the entire cachable region, used for resetting the cache\n * when this changes (zoom events, for instance).\n * @private\n */\n this.peakCacheLength = -1;\n }\n\n /**\n * Add a range of peaks to the cache\n *\n * @param {number} length The length of the range\n * @param {number} start The x offset of the start of the range\n * @param {number} end The x offset of the end of the range\n * @return {Number.<Array[]>} Array with arrays of numbers\n */\n addRangeToPeakCache(length, start, end) {\n if (length != this.peakCacheLength) {\n this.clearPeakCache();\n this.peakCacheLength = length;\n }\n\n // Return ranges that weren't in the cache before the call.\n let uncachedRanges = [];\n let i = 0;\n // Skip ranges before the current start.\n while (\n i < this.peakCacheRanges.length &&\n this.peakCacheRanges[i] < start\n ) {\n i++;\n }\n // If |i| is even, |start| falls after an existing range. Otherwise,\n // |start| falls between an existing range, and the uncached region\n // starts when we encounter the next node in |peakCacheRanges| or\n // |end|, whichever comes first.\n if (i % 2 == 0) {\n uncachedRanges.push(start);\n }\n while (\n i < this.peakCacheRanges.length &&\n this.peakCacheRanges[i] <= end\n ) {\n uncachedRanges.push(this.peakCacheRanges[i]);\n i++;\n }\n // If |i| is even, |end| is after all existing ranges.\n if (i % 2 == 0) {\n uncachedRanges.push(end);\n }\n\n // Filter out the 0-length ranges.\n uncachedRanges = uncachedRanges.filter((item, pos, arr) => {\n if (pos == 0) {\n return item != arr[pos + 1];\n } else if (pos == arr.length - 1) {\n return item != arr[pos - 1];\n }\n return item != arr[pos - 1] && item != arr[pos + 1];\n });\n\n // Merge the two ranges together, uncachedRanges will either contain\n // wholly new points, or duplicates of points in peakCacheRanges. If\n // duplicates are detected, remove both and extend the range.\n this.peakCacheRanges = this.peakCacheRanges.concat(uncachedRanges);\n this.peakCacheRanges = this.peakCacheRanges\n .sort((a, b) => a - b)\n .filter((item, pos, arr) => {\n if (pos == 0) {\n return item != arr[pos + 1];\n } else if (pos == arr.length - 1) {\n return item != arr[pos - 1];\n }\n return item != arr[pos - 1] && item != arr[pos + 1];\n });\n\n // Push the uncached ranges into an array of arrays for ease of\n // iteration in the functions that call this.\n const uncachedRangePairs = [];\n for (i = 0; i < uncachedRanges.length; i += 2) {\n uncachedRangePairs.push([uncachedRanges[i], uncachedRanges[i + 1]]);\n }\n\n return uncachedRangePairs;\n }\n\n /**\n * For testing\n *\n * @return {Number.<Array[]>} Array with arrays of numbers\n */\n getCacheRanges() {\n const peakCacheRangePairs = [];\n let i;\n for (i = 0; i < this.peakCacheRanges.length; i += 2) {\n peakCacheRangePairs.push([\n this.peakCacheRanges[i],\n this.peakCacheRanges[i + 1]\n ]);\n }\n return peakCacheRangePairs;\n }\n}\n","import utilMax from './max';\r\nimport utilmin from './min';\r\n\r\n/**\r\n * Get the largest absolute value in an array\r\n *\r\n * @param {Array} values Array of numbers\r\n * @returns {Number} Largest number found\r\n * @example console.log(max([-3, 2, 1]), max([-3, 2, 4])); // logs 3 4\r\n * @since 4.3.0\r\n */\r\nexport default function absMax(values) {\r\n const max = utilMax(values);\r\n const min = utilmin(values);\r\n return -min > max ? -min : max;\r\n}\r\n","/**\n * Returns a number limited to the given range.\n *\n * @param {number} val The number to be limited to a range\n * @param {number} min The lower boundary of the limit range\n * @param {number} max The upper boundary of the limit range\n * @returns {number} A number in the range [min, max]\n */\nexport default function clamp(val, min, max) {\n return Math.min(Math.max(min, val), max);\n}\n","/**\n * @since 3.0.0\n */\n\nimport Observer from './observer';\n\nclass ProgressHandler {\n /**\n * Instantiate ProgressHandler\n *\n * @param {Observer} instance The `fetchFile` observer instance.\n * @param {Number} contentLength Content length.\n * @param {Response} response Response object.\n */\n constructor(instance, contentLength, response) {\n this.instance = instance;\n this.instance._reader = response.body.getReader();\n\n this.total = parseInt(contentLength, 10);\n this.loaded = 0;\n }\n\n /**\n * A method that is called once, immediately after the `ReadableStream``\n * is constructed.\n *\n * @param {ReadableStreamDefaultController} controller Controller instance\n * used to control the stream.\n */\n start(controller) {\n const read = () => {\n // instance._reader.read() returns a promise that resolves\n // when a value has been received\n this.instance._reader\n .read()\n .then(({ done, value }) => {\n // result objects contain two properties:\n // done - true if the stream has already given you all its data.\n // value - some data. Always undefined when done is true.\n if (done) {\n // ensure onProgress called when content-length=0\n if (this.total === 0) {\n this.instance.onProgress.call(this.instance, {\n loaded: this.loaded,\n total: this.total,\n lengthComputable: false\n });\n }\n // no more data needs to be consumed, close the stream\n controller.close();\n return;\n }\n\n this.loaded += value.byteLength;\n this.instance.onProgress.call(this.instance, {\n loaded: this.loaded,\n total: this.total,\n lengthComputable: !(this.total === 0)\n });\n // enqueue the next data chunk into our target stream\n controller.enqueue(value);\n read();\n })\n .catch(error => {\n controller.error(error);\n });\n };\n\n read();\n }\n}\n\n/**\n * Load a file using `fetch`.\n *\n * @param {object} options Request options to use. See example below.\n * @returns {Observer} Observer instance\n * @example\n * // default options\n * let options = {\n * url: undefined,\n * method: 'GET',\n * mode: 'cors',\n * credentials: 'same-origin',\n * cache: 'default',\n * responseType: 'json',\n * requestHeaders: [],\n * redirect: 'follow',\n * referrer: 'client'\n * };\n *\n * // override some options\n * options.url = '../media/demo.wav';\n\n * // available types: 'arraybuffer', 'blob', 'json' or 'text'\n * options.responseType = 'arraybuffer';\n *\n * // make fetch call\n * let request = util.fetchFile(options);\n *\n * // listen for events\n * request.on('progress', e => {\n * console.log('progress', e);\n * });\n *\n * request.on('success', data => {\n * console.log('success!', data);\n * });\n *\n * request.on('error', e => {\n * console.warn('fetchFile error: ', e);\n * });\n */\nexport default function fetchFile(options) {\n if (!options) {\n throw new Error('fetch options missing');\n } else if (!options.url) {\n throw new Error('fetch url missing');\n }\n const instance = new Observer();\n const fetchHeaders = new Headers();\n const fetchRequest = new Request(options.url);\n\n // add ability to abort\n instance.controller = new AbortController();\n\n // check if headers have to be added\n if (options && options.requestHeaders) {\n // add custom request headers\n options.requestHeaders.forEach(header => {\n fetchHeaders.append(header.key, header.value);\n });\n }\n\n // parse fetch options\n const responseType = options.responseType || 'json';\n const fetchOptions = {\n method: options.method || 'GET',\n headers: fetchHeaders,\n mode: options.mode || 'cors',\n credentials: options.credentials || 'same-origin',\n cache: options.cache || 'default',\n redirect: options.redirect || 'follow',\n referrer: options.referrer || 'client',\n signal: instance.controller.signal\n };\n\n fetch(fetchRequest, fetchOptions)\n .then(response => {\n // store response reference\n instance.response = response;\n\n let progressAvailable = true;\n if (!response.body) {\n // ReadableStream is not yet supported in this browser\n // see https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream\n progressAvailable = false;\n }\n\n // Server must send CORS header \"Access-Control-Expose-Headers: content-length\"\n const contentLength = response.headers.get('content-length');\n if (contentLength === null) {\n // Content-Length server response header missing.\n // Don't evaluate download progress if we can't compare against a total size\n // see https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Access-Control-Expose-Headers\n progressAvailable = false;\n }\n\n if (!progressAvailable) {\n // not able to check download progress so skip it\n return response;\n }\n\n // fire progress event when during load\n instance.onProgress = e => {\n instance.fireEvent('progress', e);\n };\n\n return new Response(\n new ReadableStream(\n new ProgressHandler(instance, contentLength, response)\n ),\n fetchOptions\n );\n })\n .then(response => {\n let errMsg;\n if (response.ok) {\n switch (responseType) {\n case 'arraybuffer':\n return response.arrayBuffer();\n\n case 'json':\n return response.json();\n\n case 'blob':\n return response.blob();\n\n case 'text':\n return response.text();\n\n default:\n errMsg = 'Unknown responseType: ' + responseType;\n break;\n }\n }\n if (!errMsg) {\n errMsg = 'HTTP error status: ' + response.status;\n }\n throw new Error(errMsg);\n })\n .then(response => {\n instance.fireEvent('success', response);\n })\n .catch(error => {\n instance.fireEvent('error', error);\n });\n\n // return the fetch request\n instance.fetchRequest = fetchRequest;\n return instance;\n}\n","import reqAnimationFrame from './request-animation-frame';\n\n/**\n * Create a function which will be called at the next requestAnimationFrame\n * cycle\n *\n * @param {function} func The function to call\n *\n * @return {func} The function wrapped within a requestAnimationFrame\n */\nexport default function frame(func) {\n return (...args) => reqAnimationFrame(() => func(...args));\n}\n","/**\n * Get a random prefixed ID\n *\n * @param {String} prefix Prefix to use. Default is `'wavesurfer_'`.\n * @returns {String} Random prefixed ID\n * @example\n * console.log(getId()); // logs 'wavesurfer_b5pors4ru6g'\n *\n * let prefix = 'foo-';\n * console.log(getId(prefix)); // logs 'foo-b5pors4ru6g'\n */\nexport default function getId(prefix) {\n if (prefix === undefined) {\n prefix = 'wavesurfer_';\n }\n return (\n prefix +\n Math.random()\n .toString(32)\n .substring(2)\n );\n}\n","export { default as getId } from './get-id';\nexport { default as max } from './max';\nexport { default as min } from './min';\nexport { default as absMax } from './absMax';\nexport { default as Observer } from './observer';\nexport { default as style } from './style';\nexport { default as requestAnimationFrame } from './request-animation-frame';\nexport { default as frame } from './frame';\nexport { default as debounce } from 'debounce';\nexport { default as preventClick } from './prevent-click';\nexport { default as fetchFile } from './fetch';\nexport { default as clamp } from './clamp';\nexport { default as withOrientation } from './orientation';\n","/**\n * Get the largest value\n *\n * @param {Array} values Array of numbers\n * @returns {Number} Largest number found\n * @example console.log(max([1, 2, 3])); // logs 3\n */\nexport default function max(values) {\n let largest = -Infinity;\n Object.keys(values).forEach(i => {\n if (values[i] > largest) {\n largest = values[i];\n }\n });\n return largest;\n}\n","/**\n * Get the smallest value\n *\n * @param {Array} values Array of numbers\n * @returns {Number} Smallest number found\n * @example console.log(min([1, 2, 3])); // logs 1\n */\nexport default function min(values) {\n let smallest = Number(Infinity);\n Object.keys(values).forEach(i => {\n if (values[i] < smallest) {\n smallest = values[i];\n }\n });\n return smallest;\n}\n","/**\n * @typedef {Object} ListenerDescriptor\n * @property {string} name The name of the event\n * @property {function} callback The callback\n * @property {function} un The function to call to remove the listener\n */\n\n/**\n * Observer class\n */\nexport default class Observer {\n /**\n * Instantiate Observer\n */\n constructor() {\n /**\n * @private\n * @todo Initialise the handlers here already and remove the conditional\n * assignment in `on()`\n */\n this._disabledEventEmissions = [];\n this.handlers = null;\n }\n /**\n * Attach a handler function for an event.\n *\n * @param {string} event Name of the event to listen to\n * @param {function} fn The callback to trigger when the event is fired\n * @return {ListenerDescriptor} The event descriptor\n */\n on(event, fn) {\n if (!this.handlers) {\n this.handlers = {};\n }\n\n let handlers = this.handlers[event];\n if (!handlers) {\n handlers = this.handlers[event] = [];\n }\n handlers.push(fn);\n\n // Return an event descriptor\n return {\n name: event,\n callback: fn,\n un: (e, fn) => this.un(e, fn)\n };\n }\n\n /**\n * Remove an event handler.\n *\n * @param {string} event Name of the event the listener that should be\n * removed listens to\n * @param {function} fn The callback that should be removed\n */\n un(event, fn) {\n if (!this.handlers) {\n return;\n }\n\n const handlers = this.handlers[event];\n let i;\n if (handlers) {\n if (fn) {\n for (i = handlers.length - 1; i >= 0; i--) {\n if (handlers[i] == fn) {\n handlers.splice(i, 1);\n }\n }\n } else {\n handlers.length = 0;\n }\n }\n }\n\n /**\n * Remove all event handlers.\n */\n unAll() {\n this.handlers = null;\n }\n\n /**\n * Attach a handler to an event. The handler is executed at most once per\n * event type.\n *\n * @param {string} event The event to listen to\n * @param {function} handler The callback that is only to be called once\n * @return {ListenerDescriptor} The event descriptor\n */\n once(event, handler) {\n const fn = (...args) => {\n /* eslint-disable no-invalid-this */\n handler.apply(this, args);\n /* eslint-enable no-invalid-this */\n setTimeout(() => {\n this.un(event, fn);\n }, 0);\n };\n return this.on(event, fn);\n }\n\n /**\n * Disable firing a list of events by name. When specified, event handlers for any event type\n * passed in here will not be called.\n *\n * @since 4.0.0\n * @param {string[]} eventNames an array of event names to disable emissions for\n * @example\n * // disable seek and interaction events\n * wavesurfer.setDisabledEventEmissions(['seek', 'interaction']);\n */\n setDisabledEventEmissions(eventNames) {\n this._disabledEventEmissions = eventNames;\n }\n\n /**\n * plugins borrow part of this class without calling the constructor,\n * so we have to be careful about _disabledEventEmissions\n */\n\n _isDisabledEventEmission(event) {\n return this._disabledEventEmissions && this._disabledEventEmissions.includes(event);\n }\n\n /**\n * Manually fire an event\n *\n * @param {string} event The event to fire manually\n * @param {...any} args The arguments with which to call the listeners\n */\n fireEvent(event, ...args) {\n if (!this.handlers || this._isDisabledEventEmission(event)) {\n return;\n }\n\n const handlers = this.handlers[event];\n handlers &&\n handlers.forEach(fn => {\n fn(...args);\n });\n }\n}\n","const verticalPropMap = {\n width: 'height',\n height: 'width',\n\n overflowX: 'overflowY',\n overflowY: 'overflowX',\n\n clientWidth: 'clientHeight',\n clientHeight: 'clientWidth',\n\n clientX: 'clientY',\n clientY: 'clientX',\n\n scrollWidth: 'scrollHeight',\n scrollLeft: 'scrollTop',\n\n offsetLeft: 'offsetTop',\n offsetTop: 'offsetLeft',\n offsetHeight: 'offsetWidth',\n offsetWidth: 'offsetHeight',\n\n left: 'top',\n right: 'bottom',\n top: 'left',\n bottom: 'right',\n\n borderRightStyle: 'borderBottomStyle',\n borderRightWidth: 'borderBottomWidth',\n borderRightColor: 'borderBottomColor'\n};\n\n/**\n * Convert a horizontally-oriented property name to a vertical one.\n *\n * @param {string} prop A property name\n * @param {bool} vertical Whether the element is oriented vertically\n * @returns {string} prop, converted appropriately\n */\nfunction mapProp(prop, vertical) {\n if (Object.prototype.hasOwnProperty.call(verticalPropMap, prop)) {\n return vertical ? verticalPropMap[prop] : prop;\n } else {\n return prop;\n }\n}\n\nconst isProxy = Symbol(\"isProxy\");\n\n/**\n * Returns an appropriately oriented object based on vertical.\n * If vertical is true, attribute getting and setting will be mapped through\n * verticalPropMap, so that e.g. getting the object's .width will give its\n * .height instead.\n * Certain methods of an oriented object will return oriented objects as well.\n * Oriented objects can't be added to the DOM directly since they are Proxy objects\n * and thus fail typechecks. Use domElement to get the actual element for this.\n *\n * @param {object} target The object to be wrapped and oriented\n * @param {bool} vertical Whether the element is oriented vertically\n * @returns {Proxy} An oriented object with attr translation via verticalAttrMap\n * @since 5.0.0\n */\nexport default function withOrientation(target, vertical) {\n if (target[isProxy]) {\n return target;\n } else {\n return new Proxy(\n target, {\n get: function(obj, prop, receiver) {\n if (prop === isProxy) {\n return true;\n } else if (prop === 'domElement') {\n return obj;\n } else if (prop === 'style') {\n return withOrientation(obj.style, vertical);\n } else if (prop === 'canvas') {\n return withOrientation(obj.canvas, vertical);\n } else if (prop === 'getBoundingClientRect') {\n return function(...args) {\n return withOrientation(obj.getBoundingClientRect(...args), vertical);\n };\n } else if (prop === 'getContext') {\n return function(...args) {\n return withOrientation(obj.getContext(...args), vertical);\n };\n } else {\n let value = obj[mapProp(prop, vertical)];\n return typeof value == 'function' ? value.bind(obj) : value;\n }\n },\n set: function(obj, prop, value) {\n obj[mapProp(prop, vertical)] = value;\n return true;\n }\n }\n );\n }\n}\n","/**\n * Stops propagation of click event and removes event listener\n *\n * @private\n * @param {object} event The click event\n */\nfunction preventClickHandler(event) {\n event.stopPropagation();\n document.body.removeEventListener('click', preventClickHandler, true);\n}\n\n/**\n * Starts listening for click event and prevent propagation\n *\n * @param {object} values Values\n */\nexport default function preventClick(values) {\n document.body.addEventListener('click', preventClickHandler, true);\n}\n","/* eslint-disable valid-jsdoc */\n/**\n * Returns the `requestAnimationFrame` function for the browser, or a shim with\n * `setTimeout` if the function is not found\n *\n * @return {function} Available `requestAnimationFrame` function for the browser\n */\nexport default (\n window.requestAnimationFrame ||\n window.webkitRequestAnimationFrame ||\n window.mozRequestAnimationFrame ||\n window.oRequestAnimationFrame ||\n window.msRequestAnimationFrame ||\n ((callback, element) => setTimeout(callback, 1000 / 60))\n).bind(window);\n","/**\n * Apply a map of styles to an element\n *\n * @param {HTMLElement} el The element that the styles will be applied to\n * @param {Object} styles The map of propName: attribute, both are used as-is\n *\n * @return {HTMLElement} el\n */\nexport default function style(el, styles) {\n Object.keys(styles).forEach(prop => {\n if (el.style[prop] !== styles[prop]) {\n el.style[prop] = styles[prop];\n }\n });\n return el;\n}\n","import * as util from './util';\nimport MultiCanvas from './drawer.multicanvas';\nimport WebAudio from './webaudio';\nimport MediaElement from './mediaelement';\nimport PeakCache from './peakcache';\nimport MediaElementWebAudio from './mediaelement-webaudio';\n\n/*\n * This work is licensed under a BSD-3-Clause License.\n */\n\n/** @external {HTMLElement} https://developer.mozilla.org/en/docs/Web/API/HTMLElement */\n/** @external {OfflineAudioContext} https://developer.mozilla.org/en-US/docs/Web/API/OfflineAudioContext */\n/** @external {File} https://developer.mozilla.org/en-US/docs/Web/API/File */\n/** @external {Blob} https://developer.mozilla.org/en-US/docs/Web/API/Blob */\n/** @external {CanvasRenderingContext2D} https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D */\n/** @external {MediaStreamConstraints} https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamConstraints */\n/** @external {AudioNode} https://developer.mozilla.org/de/docs/Web/API/AudioNode */\n\n/**\n * @typedef {Object} WavesurferParams\n * @property {AudioContext} audioContext=null Use your own previously\n * initialized AudioContext or leave blank.\n * @property {number} audioRate=1 Speed at which to play audio. Lower number is\n * slower.\n * @property {ScriptProcessorNode} audioScriptProcessor=null Use your own previously\n * initialized ScriptProcessorNode or leave blank.\n * @property {boolean} autoCenter=true If a scrollbar is present, center the\n * waveform on current progress\n * @property {number} autoCenterRate=5 If autoCenter is active, rate at which the\n * waveform is centered\n * @property {boolean} autoCenterImmediately=false If autoCenter is active, immediately\n * center waveform on current progress\n * @property {string} backend='WebAudio' `'WebAudio'|'MediaElement'|'MediaElementWebAudio'` In most cases\n * you don't have to set this manually. MediaElement is a fallback for unsupported browsers.\n * MediaElementWebAudio allows to use WebAudio API also with big audio files, loading audio like with\n * MediaElement backend (HTML5 audio tag). You have to use the same methods of MediaElement backend for loading and\n * playback, giving also peaks, so the audio data are not decoded. In this way you can use WebAudio features, like filters,\n * also with audio with big duration. For example:\n * ` wavesurfer.load(url | HTMLMediaElement, peaks, preload, duration);\n * wavesurfer.play();\n * wavesurfer.setFilter(customFilter);\n * `\n * @property {string} backgroundColor=null Change background color of the\n * waveform container.\n * @property {number} barHeight=1 The height of the wave bars.\n * @property {number} barRadius=0 The radius of the wave bars. Makes bars rounded\n * @property {number} barGap=null The optional spacing between bars of the wave,\n * if not provided will be calculated in legacy format.\n * @property {number} barWidth=null Draw the waveform using bars.\n * @property {number} barMinHeight=null If specified, draw at least a bar of this height,\n * eliminating waveform gaps\n * @property {boolean} closeAudioContext=false Close and nullify all audio\n * contexts when the destroy method is called.\n * @property {!string|HTMLElement} container CSS selector or HTML element where\n * the waveform should be drawn. This is the only required parameter.\n * @property {string} cursorColor='#333' The fill color of the cursor indicating\n * the playhead position.\n * @property {number} cursorWidth=1 Measured in pixels.\n * @property {object} drawingContextAttributes={desynchronized: false} Drawing context\n * attributes.\n * @property {number} duration=null Optional audio length so pre-rendered peaks\n * can be display immediately for example.\n * @property {boolean} fillParent=true Whether to fill the entire container or\n * draw only according to `minPxPerSec`.\n * @property {boolean} forceDecode=false Force decoding of audio using web audio\n * when zooming to get a more detailed waveform.\n * @property {number} height=128 The height of the waveform. Measured in\n * pixels.\n * @property {boolean} hideScrollbar=false Whether to hide the horizontal\n * scrollbar when one would normally be shown.\n * @property {boolean} interact=true Whether the mouse interaction will be\n * enabled at initialization. You can switch this parameter at any time later\n * on.\n * @property {boolean} loopSelection=true (Use with regions plugin) Enable\n * looping of selected regions\n * @property {number} maxCanvasWidth=4000 Maximum width of a single canvas in\n * pixels, excluding a small overlap (2 * `pixelRatio`, rounded up to the next\n * even integer). If the waveform is longer than this value, additional canvases\n * will be used to render the waveform, which is useful for very large waveforms\n * that may be too wide for browsers to draw on a single canvas.\n * @property {boolean} mediaControls=false (Use with backend `MediaElement` or `MediaElementWebAudio`)\n * this enables the native controls for the media element\n * @property {string} mediaType='audio' (Use with backend `MediaElement` or `MediaElementWebAudio`)\n * `'audio'|'video'` ('video' only for `MediaElement`)\n * @property {number} minPxPerSec=20 Minimum number of pixels per second of\n * audio.\n * @property {boolean} normalize=false If true, normalize by the maximum peak\n * instead of 1.0.\n * @property {boolean} partialRender=false Use the PeakCache to improve\n * rendering speed of large waveforms\n * @property {number} pixelRatio=window.devicePixelRatio The pixel ratio used to\n * calculate display\n * @property {PluginDefinition[]} plugins=[] An array of plugin definitions to\n * register during instantiation, they will be directly initialised unless they\n * are added with the `deferInit` property set to true.\n * @property {string} progressColor='#555' The fill color of the part of the\n * waveform behind the cursor. When `progressColor` and `waveColor` are the same\n * the progress wave is not rendered at all.\n * @property {boolean} removeMediaElementOnDestroy=true Set to false to keep the\n * media element in the DOM when the player is destroyed. This is useful when\n * reusing an existing media element via the `loadMediaElement` method.\n * @property {Object} renderer=MultiCanvas Can be used to inject a custom\n * renderer.\n * @property {boolean|number} responsive=false If set to `true` resize the\n * waveform, when the window is resized. This is debounced with a `100ms`\n * timeout by default. If this parameter is a number it represents that timeout.\n * @property {boolean} rtl=false If set to `true`, renders waveform from\n * right-to-left.\n * @property {boolean} scrollParent=false Whether to scroll the container with a\n * lengthy waveform. Otherwise the waveform is shrunk to the container width\n * (see fillParent).\n * @property {number} skipLength=2 Number of seconds to skip with the\n * skipForward() and skipBackward() methods.\n * @property {boolean} splitChannels=false Render with separate waveforms for\n * the channels of the audio\n * @property {SplitChannelOptions} splitChannelsOptions={} Options for splitChannel rendering\n * @property {boolean} vertical=false Render the waveform vertically instead of horizontally.\n * @property {string} waveColor='#999' The fill color of the waveform after the\n * cursor.\n * @property {object} xhr={} XHR options. For example:\n * `let xhr = {\n * cache: 'default',\n * mode: 'cors',\n * method: 'GET',\n * credentials: 'same-origin',\n * redirect: 'follow',\n * referrer: 'client',\n * requestHeaders: [\n * {\n * key: 'Authorization',\n * value: 'my-token'\n * }\n * ]\n * };`\n */\n\n/**\n * @typedef {Object} PluginDefinition\n * @desc The Object used to describe a plugin\n * @example wavesurfer.addPlugin(pluginDefinition);\n * @property {string} name The name of the plugin, the plugin instance will be\n * added as a property to the wavesurfer instance under this name\n * @property {?Object} staticProps The properties that should be added to the\n * wavesurfer instance as static properties\n * @property {?boolean} deferInit Don't initialise plugin\n * automatically\n * @property {Object} params={} The plugin parameters, they are the first parameter\n * passed to the plugin class constructor function\n * @property {PluginClass} instance The plugin instance factory, is called with\n * the dependency specified in extends. Returns the plugin class.\n */\n\n/**\n * @typedef {Object} SplitChannelOptions\n * @desc parameters applied when splitChannels option is true\n * @property {boolean} overlay=false determines whether channels are rendered on top of each other or on separate tracks\n * @property {object} channelColors={} object describing color for each channel. Example:\n * {\n * 0: {\n * progressColor: 'green',\n * waveColor: 'pink'\n * },\n * 1: {\n * progressColor: 'orange',\n * waveColor: 'purple'\n * }\n * }\n * @property {number[]} filterChannels=[] indexes of channels to be hidden from rendering\n * @property {boolean} relativeNormalization=false determines whether\n * normalization is done per channel or maintains proportionality between\n * channels. Only applied when normalize and splitChannels are both true.\n * @since 4.3.0\n */\n\n/**\n * @interface PluginClass\n *\n * @desc This is the interface which is implemented by all plugin classes. Note\n * that this only turns into an observer after being passed through\n * `wavesurfer.addPlugin`.\n *\n * @extends {Observer}\n */\nclass PluginClass {\n /**\n * Plugin definition factory\n *\n * This function must be used to create a plugin definition which can be\n * used by wavesurfer to correctly instantiate the plugin.\n *\n * It returns a `PluginDefinition` object representing the plugin.\n *\n * @param {Object} params={} The plugin params (specific to the plugin)\n */\n create(params) {}\n /**\n * Construct the plugin\n *\n * @param {Object} params={} The plugin params (specific to the plugin)\n * @param {Object} ws The wavesurfer instance\n */\n constructor(params, ws) {}\n /**\n * Initialise the plugin\n *\n * Start doing something. This is called by\n * `wavesurfer.initPlugin(pluginName)`\n */\n init() {}\n /**\n * Destroy the plugin instance\n *\n * Stop doing something. This is called by\n * `wavesurfer.destroyPlugin(pluginName)`\n */\n destroy() {}\n}\n\n/**\n * WaveSurfer core library class\n *\n * @extends {Observer}\n * @example\n * const params = {\n * container: '#waveform',\n * waveColor: 'violet',\n * progressColor: 'purple'\n * };\n *\n * // initialise like this\n * const wavesurfer = WaveSurfer.create(params);\n *\n * // or like this ...\n * const wavesurfer = new WaveSurfer(params);\n * wavesurfer.init();\n *\n * // load audio file\n * wavesurfer.load('example/media/demo.wav');\n */\nexport default class WaveSurfer extends util.Observer {\n /** @private */\n defaultParams = {\n audioContext: null,\n audioScriptProcessor: null,\n audioRate: 1,\n autoCenter: true,\n autoCenterRate: 5,\n autoCenterImmediately: false,\n backend: 'WebAudio',\n backgroundColor: null,\n barHeight: 1,\n barRadius: 0,\n barGap: null,\n barMinHeight: null,\n container: null,\n cursorColor: '#333',\n cursorWidth: 1,\n dragSelection: true,\n drawingContextAttributes: {\n // Boolean that hints the user agent to reduce the latency\n // by desynchronizing the canvas paint cycle from the event\n // loop\n desynchronized: false\n },\n duration: null,\n fillParent: true,\n forceDecode: false,\n height: 128,\n hideScrollbar: false,\n interact: true,\n loopSelection: true,\n maxCanvasWidth: 4000,\n mediaContainer: null,\n mediaControls: false,\n mediaType: 'audio',\n minPxPerSec: 20,\n normalize: false,\n partialRender: false,\n pixelRatio:\n window.devicePixelRatio || screen.deviceXDPI / screen.logicalXDPI,\n plugins: [],\n progressColor: '#555',\n removeMediaElementOnDestroy: true,\n renderer: MultiCanvas,\n responsive: false,\n rtl: false,\n scrollParent: false,\n skipLength: 2,\n splitChannels: false,\n splitChannelsOptions: {\n overlay: false,\n channelColors: {},\n filterChannels: [],\n relativeNormalization: false\n },\n vertical: false,\n waveColor: '#999',\n xhr: {}\n };\n\n /** @private */\n backends = {\n MediaElement,\n WebAudio,\n MediaElementWebAudio\n };\n\n /**\n * Instantiate this class, call its `init` function and returns it\n *\n * @param {WavesurferParams} params The wavesurfer parameters\n * @return {Object} WaveSurfer instance\n * @example const wavesurfer = WaveSurfer.create(params);\n */\n static create(params) {\n const wavesurfer = new WaveSurfer(params);\n return wavesurfer.init();\n }\n\n /**\n * The library version number is available as a static property of the\n * WaveSurfer class\n *\n * @type {String}\n * @example\n * console.log('Using wavesurfer.js ' + WaveSurfer.VERSION);\n */\n static VERSION = __VERSION__;\n\n /**\n * Functions in the `util` property are available as a prototype property to\n * all instances\n *\n * @type {Object}\n * @example\n * const wavesurfer = WaveSurfer.create(params);\n * wavesurfer.util.style(myElement, { background: 'blue' });\n */\n util = util;\n\n /**\n * Functions in the `util` property are available as a static property of the\n * WaveSurfer class\n *\n * @type {Object}\n * @example\n * WaveSurfer.util.style(myElement, { background: 'blue' });\n */\n static util = util;\n\n /**\n * Initialise wavesurfer instance\n *\n * @param {WavesurferParams} params Instantiation options for wavesurfer\n * @example\n * const wavesurfer = new WaveSurfer(params);\n * @returns {this} Wavesurfer instance\n */\n constructor(params) {\n super();\n /**\n * Extract relevant parameters (or defaults)\n * @private\n */\n this.params = Object.assign({}, this.defaultParams, params);\n this.params.splitChannelsOptions = Object.assign(\n {},\n this.defaultParams.splitChannelsOptions,\n params.splitChannelsOptions\n );\n /** @private */\n this.container =\n 'string' == typeof params.container\n ? document.querySelector(this.params.container)\n : this.params.container;\n\n if (!this.container) {\n throw new Error('Container element not found');\n }\n\n if (this.params.mediaContainer == null) {\n /** @private */\n this.mediaContainer = this.container;\n } else if (typeof this.params.mediaContainer == 'string') {\n /** @private */\n this.mediaContainer = document.querySelector(\n this.params.mediaContainer\n );\n } else {\n /** @private */\n this.mediaContainer = this.params.mediaContainer;\n }\n\n if (!this.mediaContainer) {\n throw new Error('Media Container element not found');\n }\n\n if (this.params.maxCanvasWidth <= 1) {\n throw new Error('maxCanvasWidth must be greater than 1');\n } else if (this.params.maxCanvasWidth % 2 == 1) {\n throw new Error('maxCanvasWidth must be an even number');\n }\n\n if (this.params.rtl === true) {\n if (this.params.vertical === true) {\n util.style(this.container, { transform: 'rotateX(180deg)' });\n } else {\n util.style(this.container, { transform: 'rotateY(180deg)' });\n }\n }\n\n if (this.params.backgroundColor) {\n this.setBackgroundColor(this.params.backgroundColor);\n }\n\n /**\n * @private Used to save the current volume when muting so we can\n * restore once unmuted\n * @type {number}\n */\n this.savedVolume = 0;\n\n /**\n * @private The current muted state\n * @type {boolean}\n */\n this.isMuted = false;\n\n /**\n * @private Will hold a list of event descriptors that need to be\n * canceled on subsequent loads of audio\n * @type {Object[]}\n */\n this.tmpEvents = [];\n\n /**\n * @private Holds any running audio downloads\n * @type {Observer}\n */\n this.currentRequest = null;\n /** @private */\n this.arraybuffer = null;\n /** @private */\n this.drawer = null;\n /** @private */\n this.backend = null;\n /** @private */\n this.peakCache = null;\n\n // cache constructor objects\n if (typeof this.params.renderer !== 'function') {\n throw new Error('Renderer parameter is invalid');\n }\n /**\n * @private The uninitialised Drawer class\n */\n this.Drawer = this.params.renderer;\n /**\n * @private The uninitialised Backend class\n */\n // Back compat\n if (this.params.backend == 'AudioElement') {\n this.params.backend = 'MediaElement';\n }\n\n if (\n (this.params.backend == 'WebAudio' ||\n this.params.backend === 'MediaElementWebAudio') &&\n !WebAudio.prototype.supportsWebAudio.call(null)\n ) {\n this.params.backend = 'MediaElement';\n }\n this.Backend = this.backends[this.params.backend];\n\n /**\n * @private map of plugin names that are currently initialised\n */\n this.initialisedPluginList = {};\n /** @private */\n this.isDestroyed = false;\n\n /**\n * Get the current ready status.\n *\n * @example const isReady = wavesurfer.isReady;\n * @return {boolean}\n */\n this.isReady = false;\n\n // responsive debounced event listener. If this.params.responsive is not\n // set, this is never called. Use 100ms or this.params.responsive as\n // timeout for the debounce function.\n let prevWidth = 0;\n this._onResize = util.debounce(\n () => {\n if (\n prevWidth != this.drawer.wrapper.clientWidth &&\n !this.params.scrollParent\n ) {\n prevWidth = this.drawer.wrapper.clientWidth;\n this.drawer.fireEvent('redraw');\n }\n },\n typeof this.params.responsive === 'number'\n ? this.params.responsive\n : 100\n );\n\n return this;\n }\n\n /**\n * Initialise the wave\n *\n * @example\n * var wavesurfer = new WaveSurfer(params);\n * wavesurfer.init();\n * @return {this} The wavesurfer instance\n */\n init() {\n this.registerPlugins(this.params.plugins);\n this.createDrawer();\n this.createBackend();\n this.createPeakCache();\n return this;\n }\n\n /**\n * Add and initialise array of plugins (if `plugin.deferInit` is falsey),\n * this function is called in the init function of wavesurfer\n *\n * @param {PluginDefinition[]} plugins An array of plugin definitions\n * @emits {WaveSurfer#plugins-registered} Called with the array of plugin definitions\n * @return {this} The wavesurfer instance\n */\n registerPlugins(plugins) {\n // first instantiate all the plugins\n plugins.forEach(plugin => this.addPlugin(plugin));\n\n // now run the init functions\n plugins.forEach(plugin => {\n // call init function of the plugin if deferInit is falsey\n // in that case you would manually use initPlugins()\n if (!plugin.deferInit) {\n this.initPlugin(plugin.name);\n }\n });\n this.fireEvent('plugins-registered', plugins);\n return this;\n }\n\n /**\n * Get a map of plugin names that are currently initialised\n *\n * @example wavesurfer.getPlugins();\n * @return {Object} Object with plugin names\n */\n getActivePlugins() {\n return this.initialisedPluginList;\n }\n\n /**\n * Add a plugin object to wavesurfer\n *\n * @param {PluginDefinition} plugin A plugin definition\n * @emits {WaveSurfer#plugin-added} Called with the name of the plugin that was added\n * @example wavesurfer.addPlugin(WaveSurfer.minimap());\n * @return {this} The wavesurfer instance\n */\n addPlugin(plugin) {\n if (!plugin.name) {\n throw new Error('Plugin does not have a name!');\n }\n if (!plugin.instance) {\n throw new Error(\n `Plugin ${plugin.name} does not have an instance property!`\n );\n }\n\n // staticProps properties are applied to wavesurfer instance\n if (plugin.staticProps) {\n Object.keys(plugin.staticProps).forEach(pluginStaticProp => {\n /**\n * Properties defined in a plugin definition's `staticProps` property are added as\n * staticProps properties of the WaveSurfer instance\n */\n this[pluginStaticProp] = plugin.staticProps[pluginStaticProp];\n });\n }\n\n const Instance = plugin.instance;\n\n // turn the plugin instance into an observer\n const observerPrototypeKeys = Object.getOwnPropertyNames(\n util.Observer.prototype\n );\n observerPrototypeKeys.forEach(key => {\n Instance.prototype[key] = util.Observer.prototype[key];\n });\n\n /**\n * Instantiated plugin classes are added as a property of the wavesurfer\n * instance\n * @type {Object}\n */\n this[plugin.name] = new Instance(plugin.params || {}, this);\n this.fireEvent('plugin-added', plugin.name);\n return this;\n }\n\n /**\n * Initialise a plugin\n *\n * @param {string} name A plugin name\n * @emits WaveSurfer#plugin-initialised\n * @example wavesurfer.initPlugin('minimap');\n * @return {this} The wavesurfer instance\n */\n initPlugin(name) {\n if (!this[name]) {\n throw new Error(`Plugin ${name} has not been added yet!`);\n }\n if (this.initialisedPluginList[name]) {\n // destroy any already initialised plugins\n this.destroyPlugin(name);\n }\n this[name].init();\n this.initialisedPluginList[name] = true;\n this.fireEvent('plugin-initialised', name);\n return this;\n }\n\n /**\n * Destroy a plugin\n *\n * @param {string} name A plugin name\n * @emits WaveSurfer#plugin-destroyed\n * @example wavesurfer.destroyPlugin('minimap');\n * @returns {this} The wavesurfer instance\n */\n destroyPlugin(name) {\n if (!this[name]) {\n throw new Error(\n `Plugin ${name} has not been added yet and cannot be destroyed!`\n );\n }\n if (!this.initialisedPluginList[name]) {\n throw new Error(\n `Plugin ${name} is not active and cannot be destroyed!`\n );\n }\n if (typeof this[name].destroy !== 'function') {\n throw new Error(`Plugin ${name} does not have a destroy function!`);\n }\n\n this[name].destroy();\n delete this.initialisedPluginList[name];\n this.fireEvent('plugin-destroyed', name);\n return this;\n }\n\n /**\n * Destroy all initialised plugins. Convenience function to use when\n * wavesurfer is removed\n *\n * @private\n */\n destroyAllPlugins() {\n Object.keys(this.initialisedPluginList).forEach(name =>\n this.destroyPlugin(name)\n );\n }\n\n /**\n * Create the drawer and draw the waveform\n *\n * @private\n * @emits WaveSurfer#drawer-created\n */\n createDrawer() {\n this.drawer = new this.Drawer(this.container, this.params);\n this.drawer.init();\n this.fireEvent('drawer-created', this.drawer);\n\n if (this.params.responsive !== false) {\n window.addEventListener('resize', this._onResize, true);\n window.addEventListener('orientationchange', this._onResize, true);\n }\n\n this.drawer.on('redraw', () => {\n this.drawBuffer();\n this.drawer.progress(this.backend.getPlayedPercents());\n });\n\n // Click-to-seek\n this.drawer.on('click', (e, progress) => {\n setTimeout(() => this.seekTo(progress), 0);\n });\n\n // Relay the scroll event from the drawer\n this.drawer.on('scroll', e => {\n if (this.params.partialRender) {\n this.drawBuffer();\n }\n this.fireEvent('scroll', e);\n });\n }\n\n /**\n * Create the backend\n *\n * @private\n * @emits WaveSurfer#backend-created\n */\n createBackend() {\n if (this.backend) {\n this.backend.destroy();\n }\n\n this.backend = new this.Backend(this.params);\n this.backend.init();\n this.fireEvent('backend-created', this.backend);\n\n this.backend.on('finish', () => {\n this.drawer.progress(this.backend.getPlayedPercents());\n this.fireEvent('finish');\n });\n this.backend.on('play', () => this.fireEvent('play'));\n this.backend.on('pause', () => this.fireEvent('pause'));\n\n this.backend.on('audioprocess', time => {\n this.drawer.progress(this.backend.getPlayedPercents());\n this.fireEvent('audioprocess', time);\n });\n\n // only needed for MediaElement and MediaElementWebAudio backend\n if (\n this.params.backend === 'MediaElement' ||\n this.params.backend === 'MediaElementWebAudio'\n ) {\n this.backend.on('seek', () => {\n this.drawer.progress(this.backend.getPlayedPercents());\n });\n\n this.backend.on('volume', () => {\n let newVolume = this.getVolume();\n this.fireEvent('volume', newVolume);\n\n if (this.backend.isMuted !== this.isMuted) {\n this.isMuted = this.backend.isMuted;\n this.fireEvent('mute', this.isMuted);\n }\n });\n }\n }\n\n /**\n * Create the peak cache\n *\n * @private\n */\n createPeakCache() {\n if (this.params.partialRender) {\n this.peakCache = new PeakCache();\n }\n }\n\n /**\n * Get the duration of the audio clip\n *\n * @example const duration = wavesurfer.getDuration();\n * @return {number} Duration in seconds\n */\n getDuration() {\n return this.backend.getDuration();\n }\n\n /**\n * Get the current playback position\n *\n * @example const currentTime = wavesurfer.getCurrentTime();\n * @return {number} Playback position in seconds\n */\n getCurrentTime() {\n return this.backend.getCurrentTime();\n }\n\n /**\n * Set the current play time in seconds.\n *\n * @param {number} seconds A positive number in seconds. E.g. 10 means 10\n * seconds, 60 means 1 minute\n */\n setCurrentTime(seconds) {\n if (seconds >= this.getDuration()) {\n this.seekTo(1);\n } else {\n this.seekTo(seconds / this.getDuration());\n }\n }\n\n /**\n * Starts playback from the current position. Optional start and end\n * measured in seconds can be used to set the range of audio to play.\n *\n * @param {?number} start Position to start at\n * @param {?number} end Position to end at\n * @emits WaveSurfer#interaction\n * @return {Promise} Result of the backend play method\n * @example\n * // play from second 1 to 5\n * wavesurfer.play(1, 5);\n */\n play(start, end) {\n this.fireEvent('interaction', () => this.play(start, end));\n return this.backend.play(start, end);\n }\n\n /**\n * Set a point in seconds for playback to stop at.\n *\n * @param {number} position Position (in seconds) to stop at\n * @version 3.3.0\n */\n setPlayEnd(position) {\n this.backend.setPlayEnd(position);\n }\n\n /**\n * Stops and pauses playback\n *\n * @example wavesurfer.pause();\n * @return {Promise} Result of the backend pause method\n */\n pause() {\n if (!this.backend.isPaused()) {\n return this.backend.pause();\n }\n }\n\n /**\n * Toggle playback\n *\n * @example wavesurfer.playPause();\n * @return {Promise} Result of the backend play or pause method\n */\n playPause() {\n return this.backend.isPaused() ? this.play() : this.pause();\n }\n\n /**\n * Get the current playback state\n *\n * @example const isPlaying = wavesurfer.isPlaying();\n * @return {boolean} False if paused, true if playing\n */\n isPlaying() {\n return !this.backend.isPaused();\n }\n\n /**\n * Skip backward\n *\n * @param {?number} seconds Amount to skip back, if not specified `skipLength`\n * is used\n * @example wavesurfer.skipBackward();\n */\n skipBackward(seconds) {\n this.skip(-seconds || -this.params.skipLength);\n }\n\n /**\n * Skip forward\n *\n * @param {?number} seconds Amount to skip back, if not specified `skipLength`\n * is used\n * @example wavesurfer.skipForward();\n */\n skipForward(seconds) {\n this.skip(seconds || this.params.skipLength);\n }\n\n /**\n * Skip a number of seconds from the current position (use a negative value\n * to go backwards).\n *\n * @param {number} offset Amount to skip back or forwards\n * @example\n * // go back 2 seconds\n * wavesurfer.skip(-2);\n */\n skip(offset) {\n const duration = this.getDuration() || 1;\n let position = this.getCurrentTime() || 0;\n position = Math.max(0, Math.min(duration, position + (offset || 0)));\n this.seekAndCenter(position / duration);\n }\n\n /**\n * Seeks to a position and centers the view\n *\n * @param {number} progress Between 0 (=beginning) and 1 (=end)\n * @example\n * // seek and go to the middle of the audio\n * wavesurfer.seekTo(0.5);\n */\n seekAndCenter(progress) {\n this.seekTo(progress);\n this.drawer.recenter(progress);\n }\n\n /**\n * Seeks to a position\n *\n * @param {number} progress Between 0 (=beginning) and 1 (=end)\n * @emits WaveSurfer#interaction\n * @emits WaveSurfer#seek\n * @example\n * // seek to the middle of the audio\n * wavesurfer.seekTo(0.5);\n */\n seekTo(progress) {\n // return an error if progress is not a number between 0 and 1\n if (\n typeof progress !== 'number' ||\n !isFinite(progress) ||\n progress < 0 ||\n progress > 1\n ) {\n throw new Error(\n 'Error calling wavesurfer.seekTo, parameter must be a number between 0 and 1!'\n );\n }\n this.fireEvent('interaction', () => this.seekTo(progress));\n\n const isWebAudioBackend = this.params.backend === 'WebAudio';\n const paused = this.backend.isPaused();\n\n if (isWebAudioBackend && !paused) {\n this.backend.pause();\n }\n\n // avoid small scrolls while paused seeking\n const oldScrollParent = this.params.scrollParent;\n this.params.scrollParent = false;\n this.backend.seekTo(progress * this.getDuration());\n this.drawer.progress(progress);\n\n if (isWebAudioBackend && !paused) {\n this.backend.play();\n }\n\n this.params.scrollParent = oldScrollParent;\n this.fireEvent('seek', progress);\n }\n\n /**\n * Stops and goes to the beginning.\n *\n * @example wavesurfer.stop();\n */\n stop() {\n this.pause();\n this.seekTo(0);\n this.drawer.progress(0);\n }\n\n /**\n * Sets the ID of the audio device to use for output and returns a Promise.\n *\n * @param {string} deviceId String value representing underlying output\n * device\n * @returns {Promise} `Promise` that resolves to `undefined` when there are\n * no errors detected.\n */\n setSinkId(deviceId) {\n return this.backend.setSinkId(deviceId);\n }\n\n /**\n * Set the playback volume.\n *\n * @param {number} newVolume A value between 0 and 1, 0 being no\n * volume and 1 being full volume.\n * @emits WaveSurfer#volume\n */\n setVolume(newVolume) {\n this.backend.setVolume(newVolume);\n this.fireEvent('volume', newVolume);\n }\n\n /**\n * Get the playback volume.\n *\n * @return {number} A value between 0 and 1, 0 being no\n * volume and 1 being full volume.\n */\n getVolume() {\n return this.backend.getVolume();\n }\n\n /**\n * Set the playback rate.\n *\n * @param {number} rate A positive number. E.g. 0.5 means half the normal\n * speed, 2 means double speed and so on.\n * @example wavesurfer.setPlaybackRate(2);\n */\n setPlaybackRate(rate) {\n this.backend.setPlaybackRate(rate);\n }\n\n /**\n * Get the playback rate.\n *\n * @return {number} The current playback rate.\n */\n getPlaybackRate() {\n return this.backend.getPlaybackRate();\n }\n\n /**\n * Toggle the volume on and off. If not currently muted it will save the\n * current volume value and turn the volume off. If currently muted then it\n * will restore the volume to the saved value, and then rest the saved\n * value.\n *\n * @example wavesurfer.toggleMute();\n */\n toggleMute() {\n this.setMute(!this.isMuted);\n }\n\n /**\n * Enable or disable muted audio\n *\n * @param {boolean} mute Specify `true` to mute audio.\n * @emits WaveSurfer#volume\n * @emits WaveSurfer#mute\n * @example\n * // unmute\n * wavesurfer.setMute(false);\n * console.log(wavesurfer.getMute()) // logs false\n */\n setMute(mute) {\n // ignore all muting requests if the audio is already in that state\n if (mute === this.isMuted) {\n this.fireEvent('mute', this.isMuted);\n return;\n }\n\n if (this.backend.setMute) {\n // Backends such as the MediaElement backend have their own handling\n // of mute, let them handle it.\n this.backend.setMute(mute);\n this.isMuted = mute;\n } else {\n if (mute) {\n // If currently not muted then save current volume,\n // turn off the volume and update the mute properties\n this.savedVolume = this.backend.getVolume();\n this.backend.setVolume(0);\n this.isMuted = true;\n this.fireEvent('volume', 0);\n } else {\n // If currently muted then restore to the saved volume\n // and update the mute properties\n this.backend.setVolume(this.savedVolume);\n this.isMuted = false;\n this.fireEvent('volume', this.savedVolume);\n }\n }\n this.fireEvent('mute', this.isMuted);\n }\n\n /**\n * Get the current mute status.\n *\n * @example const isMuted = wavesurfer.getMute();\n * @return {boolean} Current mute status\n */\n getMute() {\n return this.isMuted;\n }\n\n /**\n * Get the list of current set filters as an array.\n *\n * Filters must be set with setFilters method first\n *\n * @return {array} List of enabled filters\n */\n getFilters() {\n return this.backend.filters || [];\n }\n\n /**\n * Toggles `scrollParent` and redraws\n *\n * @example wavesurfer.toggleScroll();\n */\n toggleScroll() {\n this.params.scrollParent = !this.params.scrollParent;\n this.drawBuffer();\n }\n\n /**\n * Toggle mouse interaction\n *\n * @example wavesurfer.toggleInteraction();\n */\n toggleInteraction() {\n this.params.interact = !this.params.interact;\n }\n\n /**\n * Get the fill color of the waveform after the cursor.\n *\n * @return {string} A CSS color string.\n */\n getWaveColor() {\n return this.params.waveColor;\n }\n\n /**\n * Set the fill color of the waveform after the cursor.\n *\n * @param {string} color A CSS color string.\n * @example wavesurfer.setWaveColor('#ddd');\n */\n setWaveColor(color) {\n this.params.waveColor = color;\n this.drawBuffer();\n }\n\n /**\n * Get the fill color of the waveform behind the cursor.\n *\n * @return {string} A CSS color string.\n */\n getProgressColor() {\n return this.params.progressColor;\n }\n\n /**\n * Set the fill color of the waveform behind the cursor.\n *\n * @param {string} color A CSS color string.\n * @example wavesurfer.setProgressColor('#400');\n */\n setProgressColor(color) {\n this.params.progressColor = color;\n this.drawBuffer();\n }\n\n /**\n * Get the background color of the waveform container.\n *\n * @return {string} A CSS color string.\n */\n getBackgroundColor() {\n return this.params.backgroundColor;\n }\n\n /**\n * Set the background color of the waveform container.\n *\n * @param {string} color A CSS color string.\n * @example wavesurfer.setBackgroundColor('#FF00FF');\n */\n setBackgroundColor(color) {\n this.params.backgroundColor = color;\n util.style(this.container, { background: this.params.backgroundColor });\n }\n\n /**\n * Get the fill color of the cursor indicating the playhead\n * position.\n *\n * @return {string} A CSS color string.\n */\n getCursorColor() {\n return this.params.cursorColor;\n }\n\n /**\n * Set the fill color of the cursor indicating the playhead\n * position.\n *\n * @param {string} color A CSS color string.\n * @example wavesurfer.setCursorColor('#222');\n */\n setCursorColor(color) {\n this.params.cursorColor = color;\n this.drawer.updateCursor();\n }\n\n /**\n * Get the height of the waveform.\n *\n * @return {number} Height measured in pixels.\n */\n getHeight() {\n return this.params.height;\n }\n\n /**\n * Set the height of the waveform.\n *\n * @param {number} height Height measured in pixels.\n * @example wavesurfer.setHeight(200);\n */\n setHeight(height) {\n this.params.height = height;\n this.drawer.setHeight(height * this.params.pixelRatio);\n this.drawBuffer();\n }\n\n /**\n * Hide channels from being drawn on the waveform if splitting channels.\n *\n * For example, if we want to draw only the peaks for the right stereo channel:\n *\n * const wavesurfer = new WaveSurfer.create({...splitChannels: true});\n * wavesurfer.load('stereo_audio.mp3');\n *\n * wavesurfer.setFilteredChannel([0]); <-- hide left channel peaks.\n *\n * @param {array} channelIndices Channels to be filtered out from drawing.\n * @version 4.0.0\n */\n setFilteredChannels(channelIndices) {\n this.params.splitChannelsOptions.filterChannels = channelIndices;\n this.drawBuffer();\n }\n\n /**\n * Get the correct peaks for current wave view-port and render wave\n *\n * @private\n * @emits WaveSurfer#redraw\n */\n drawBuffer() {\n const nominalWidth = Math.round(\n this.getDuration() *\n this.params.minPxPerSec *\n this.params.pixelRatio\n );\n const parentWidth = this.drawer.getWidth();\n let width = nominalWidth;\n // always start at 0 after zooming for scrolling : issue redraw left part\n let start = 0;\n let end = Math.max(start + parentWidth, width);\n // Fill container\n if (\n this.params.fillParent &&\n (!this.params.scrollParent || nominalWidth < parentWidth)\n ) {\n width = parentWidth;\n start = 0;\n end = width;\n }\n\n let peaks;\n if (this.params.partialRender) {\n const newRanges = this.peakCache.addRangeToPeakCache(\n width,\n start,\n end\n );\n let i;\n for (i = 0; i < newRanges.length; i++) {\n peaks = this.backend.getPeaks(\n width,\n newRanges[i][0],\n newRanges[i][1]\n );\n this.drawer.drawPeaks(\n peaks,\n width,\n newRanges[i][0],\n newRanges[i][1]\n );\n }\n } else {\n peaks = this.backend.getPeaks(width, start, end);\n this.drawer.drawPeaks(peaks, width, start, end);\n }\n this.fireEvent('redraw', peaks, width);\n }\n\n /**\n * Horizontally zooms the waveform in and out. It also changes the parameter\n * `minPxPerSec` and enables the `scrollParent` option. Calling the function\n * with a falsey parameter will reset the zoom state.\n *\n * @param {?number} pxPerSec Number of horizontal pixels per second of\n * audio, if none is set the waveform returns to unzoomed state\n * @emits WaveSurfer#zoom\n * @example wavesurfer.zoom(20);\n */\n zoom(pxPerSec) {\n if (!pxPerSec) {\n this.params.minPxPerSec = this.defaultParams.minPxPerSec;\n this.params.scrollParent = false;\n } else {\n this.params.minPxPerSec = pxPerSec;\n this.params.scrollParent = true;\n }\n\n this.drawBuffer();\n this.drawer.progress(this.backend.getPlayedPercents());\n\n this.drawer.recenter(this.getCurrentTime() / this.getDuration());\n this.fireEvent('zoom', pxPerSec);\n }\n\n /**\n * Decode buffer and load\n *\n * @private\n * @param {ArrayBuffer} arraybuffer Buffer to process\n */\n loadArrayBuffer(arraybuffer) {\n this.decodeArrayBuffer(arraybuffer, data => {\n if (!this.isDestroyed) {\n this.loadDecodedBuffer(data);\n }\n });\n }\n\n /**\n * Directly load an externally decoded AudioBuffer\n *\n * @private\n * @param {AudioBuffer} buffer Buffer to process\n * @emits WaveSurfer#ready\n */\n loadDecodedBuffer(buffer) {\n this.backend.load(buffer);\n this.drawBuffer();\n this.isReady = true;\n this.fireEvent('ready');\n }\n\n /**\n * Loads audio data from a Blob or File object\n *\n * @param {Blob|File} blob Audio data\n * @example\n */\n loadBlob(blob) {\n // Create file reader\n const reader = new FileReader();\n reader.addEventListener('progress', e => this.onProgress(e));\n reader.addEventListener('load', e =>\n this.loadArrayBuffer(e.target.result)\n );\n reader.addEventListener('error', () =>\n this.fireEvent('error', 'Error reading file')\n );\n reader.readAsArrayBuffer(blob);\n this.empty();\n }\n\n /**\n * Loads audio and re-renders the waveform.\n *\n * @param {string|HTMLMediaElement} url The url of the audio file or the\n * audio element with the audio\n * @param {number[]|Number.<Array[]>} peaks Wavesurfer does not have to decode\n * the audio to render the waveform if this is specified\n * @param {?string} preload (Use with backend `MediaElement` and `MediaElementWebAudio`)\n * `'none'|'metadata'|'auto'` Preload attribute for the media element\n * @param {?number} duration The duration of the audio. This is used to\n * render the peaks data in the correct size for the audio duration (as\n * befits the current `minPxPerSec` and zoom value) without having to decode\n * the audio.\n * @returns {void}\n * @throws Will throw an error if the `url` argument is empty.\n * @example\n * // uses fetch or media element to load file (depending on backend)\n * wavesurfer.load('http://example.com/demo.wav');\n *\n * // setting preload attribute with media element backend and supplying\n * // peaks\n * wavesurfer.load(\n * 'http://example.com/demo.wav',\n * [0.0218, 0.0183, 0.0165, 0.0198, 0.2137, 0.2888],\n * true\n * );\n */\n load(url, peaks, preload, duration) {\n if (!url) {\n throw new Error('url parameter cannot be empty');\n }\n this.empty();\n if (preload) {\n // check whether the preload attribute will be usable and if not log\n // a warning listing the reasons why not and nullify the variable\n const preloadIgnoreReasons = {\n \"Preload is not 'auto', 'none' or 'metadata'\":\n ['auto', 'metadata', 'none'].indexOf(preload) === -1,\n 'Peaks are not provided': !peaks,\n \"Backend is not of type 'MediaElement' or 'MediaElementWebAudio'\":\n ['MediaElement', 'MediaElementWebAudio'].indexOf(\n this.params.backend\n ) === -1,\n 'Url is not of type string': typeof url !== 'string'\n };\n const activeReasons = Object.keys(preloadIgnoreReasons).filter(\n reason => preloadIgnoreReasons[reason]\n );\n if (activeReasons.length) {\n // eslint-disable-next-line no-console\n console.warn(\n 'Preload parameter of wavesurfer.load will be ignored because:\\n\\t- ' +\n activeReasons.join('\\n\\t- ')\n );\n // stop invalid values from being used\n preload = null;\n }\n }\n\n // loadBuffer(url, peaks, duration) requires that url is a string\n // but users can pass in a HTMLMediaElement to WaveSurfer\n if (this.params.backend === 'WebAudio' && url instanceof HTMLMediaElement) {\n url = url.src;\n }\n\n switch (this.params.backend) {\n case 'WebAudio':\n return this.loadBuffer(url, peaks, duration);\n case 'MediaElement':\n case 'MediaElementWebAudio':\n return this.loadMediaElement(url, peaks, preload, duration);\n }\n }\n\n /**\n * Loads audio using Web Audio buffer backend.\n *\n * @private\n * @emits WaveSurfer#waveform-ready\n * @param {string} url URL of audio file\n * @param {number[]|Number.<Array[]>} peaks Peaks data\n * @param {?number} duration Optional duration of audio file\n * @returns {void}\n */\n loadBuffer(url, peaks, duration) {\n const load = action => {\n if (action) {\n this.tmpEvents.push(this.once('ready', action));\n }\n return this.getArrayBuffer(url, data => this.loadArrayBuffer(data));\n };\n\n if (peaks) {\n this.backend.setPeaks(peaks, duration);\n this.drawBuffer();\n this.fireEvent('waveform-ready');\n this.tmpEvents.push(this.once('interaction', load));\n } else {\n return load();\n }\n }\n\n /**\n * Either create a media element, or load an existing media element.\n *\n * @private\n * @emits WaveSurfer#waveform-ready\n * @param {string|HTMLMediaElement} urlOrElt Either a path to a media file, or an\n * existing HTML5 Audio/Video Element\n * @param {number[]|Number.<Array[]>} peaks Array of peaks. Required to bypass web audio\n * dependency\n * @param {?boolean} preload Set to true if the preload attribute of the\n * audio element should be enabled\n * @param {?number} duration Optional duration of audio file\n */\n loadMediaElement(urlOrElt, peaks, preload, duration) {\n let url = urlOrElt;\n\n if (typeof urlOrElt === 'string') {\n this.backend.load(url, this.mediaContainer, peaks, preload);\n } else {\n const elt = urlOrElt;\n this.backend.loadElt(elt, peaks);\n\n // If peaks are not provided,\n // url = element.src so we can get peaks with web audio\n url = elt.src;\n }\n\n this.tmpEvents.push(\n this.backend.once('canplay', () => {\n // ignore when backend was already destroyed\n if (!this.backend.destroyed) {\n this.drawBuffer();\n this.isReady = true;\n this.fireEvent('ready');\n }\n }),\n this.backend.once('error', err => this.fireEvent('error', err))\n );\n\n // If peaks are provided, render them and fire the `waveform-ready` event.\n if (peaks) {\n this.backend.setPeaks(peaks, duration);\n this.drawBuffer();\n this.fireEvent('waveform-ready');\n }\n\n // If no pre-decoded peaks are provided, or are provided with\n // forceDecode flag, attempt to download the audio file and decode it\n // with Web Audio.\n if (\n (!peaks || this.params.forceDecode) &&\n this.backend.supportsWebAudio()\n ) {\n this.getArrayBuffer(url, arraybuffer => {\n this.decodeArrayBuffer(arraybuffer, buffer => {\n this.backend.buffer = buffer;\n this.backend.setPeaks(null);\n this.drawBuffer();\n this.fireEvent('waveform-ready');\n });\n });\n }\n }\n\n /**\n * Decode an array buffer and pass data to a callback\n *\n * @private\n * @param {Object} arraybuffer The array buffer to decode\n * @param {function} callback The function to call on complete\n */\n decodeArrayBuffer(arraybuffer, callback) {\n this.arraybuffer = arraybuffer;\n this.backend.decodeArrayBuffer(\n arraybuffer,\n data => {\n // Only use the decoded data if we haven't been destroyed or\n // another decode started in the meantime\n if (!this.isDestroyed && this.arraybuffer == arraybuffer) {\n callback(data);\n this.arraybuffer = null;\n }\n },\n () => this.fireEvent('error', 'Error decoding audiobuffer')\n );\n }\n\n /**\n * Load an array buffer using fetch and pass the result to a callback\n *\n * @param {string} url The URL of the file object\n * @param {function} callback The function to call on complete\n * @returns {util.fetchFile} fetch call\n * @private\n */\n getArrayBuffer(url, callback) {\n let options = Object.assign(\n {\n url: url,\n responseType: 'arraybuffer'\n },\n this.params.xhr\n );\n const request = util.fetchFile(options);\n\n this.currentRequest = request;\n\n this.tmpEvents.push(\n request.on('progress', e => {\n this.onProgress(e);\n }),\n request.on('success', data => {\n callback(data);\n this.currentRequest = null;\n }),\n request.on('error', e => {\n this.fireEvent('error', e);\n this.currentRequest = null;\n })\n );\n\n return request;\n }\n\n /**\n * Called while the audio file is loading\n *\n * @private\n * @param {Event} e Progress event\n * @emits WaveSurfer#loading\n */\n onProgress(e) {\n let percentComplete;\n if (e.lengthComputable) {\n percentComplete = e.loaded / e.total;\n } else {\n // Approximate progress with an asymptotic\n // function, and assume downloads in the 1-3 MB range.\n percentComplete = e.loaded / (e.loaded + 1000000);\n }\n this.fireEvent('loading', Math.round(percentComplete * 100), e.target);\n }\n\n /**\n * Exports PCM data into a JSON array and optionally opens in a new window\n * as valid JSON Blob instance.\n *\n * @param {number} length=1024 The scale in which to export the peaks\n * @param {number} accuracy=10000\n * @param {?boolean} noWindow Set to true to disable opening a new\n * window with the JSON\n * @param {number} start Start index\n * @param {number} end End index\n * @return {Promise} Promise that resolves with array of peaks\n */\n exportPCM(length, accuracy, noWindow, start, end) {\n length = length || 1024;\n start = start || 0;\n accuracy = accuracy || 10000;\n noWindow = noWindow || false;\n const peaks = this.backend.getPeaks(length, start, end);\n const arr = [].map.call(\n peaks,\n val => Math.round(val * accuracy) / accuracy\n );\n\n return new Promise((resolve, reject) => {\n if (!noWindow){\n const blobJSON = new Blob(\n [JSON.stringify(arr)],\n {type: 'application/json;charset=utf-8'}\n );\n const objURL = URL.createObjectURL(blobJSON);\n window.open(objURL);\n URL.revokeObjectURL(objURL);\n }\n resolve(arr);\n });\n }\n\n /**\n * Save waveform image as data URI.\n *\n * The default format is `'image/png'`. Other supported types are\n * `'image/jpeg'` and `'image/webp'`.\n *\n * @param {string} format='image/png' A string indicating the image format.\n * The default format type is `'image/png'`.\n * @param {number} quality=1 A number between 0 and 1 indicating the image\n * quality to use for image formats that use lossy compression such as\n * `'image/jpeg'`` and `'image/webp'`.\n * @param {string} type Image data type to return. Either 'dataURL' (default)\n * or 'blob'.\n * @return {string|string[]|Promise} When using `'dataURL'` type this returns\n * a single data URL or an array of data URLs, one for each canvas. When using\n * `'blob'` type this returns a `Promise` resolving with an array of `Blob`\n * instances, one for each canvas.\n */\n exportImage(format, quality, type) {\n if (!format) {\n format = 'image/png';\n }\n if (!quality) {\n quality = 1;\n }\n if (!type) {\n type = 'dataURL';\n }\n\n return this.drawer.getImage(format, quality, type);\n }\n\n /**\n * Cancel any fetch request currently in progress\n */\n cancelAjax() {\n if (this.currentRequest && this.currentRequest.controller) {\n // If the current request has a ProgressHandler, then its ReadableStream might need to be cancelled too\n // See: Wavesurfer issue #2042\n // See Firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1583815\n if (this.currentRequest._reader) {\n // Ignoring exceptions thrown by call to cancel()\n this.currentRequest._reader.cancel().catch(err => {});\n }\n\n this.currentRequest.controller.abort();\n this.currentRequest = null;\n }\n }\n\n /**\n * @private\n */\n clearTmpEvents() {\n this.tmpEvents.forEach(e => e.un());\n }\n\n /**\n * Display empty waveform.\n */\n empty() {\n if (!this.backend.isPaused()) {\n this.stop();\n this.backend.disconnectSource();\n }\n this.isReady = false;\n this.cancelAjax();\n this.clearTmpEvents();\n\n // empty drawer\n this.drawer.progress(0);\n this.drawer.setWidth(0);\n this.drawer.drawPeaks({ length: this.drawer.getWidth() }, 0);\n }\n\n /**\n * Remove events, elements and disconnect WebAudio nodes.\n *\n * @emits WaveSurfer#destroy\n */\n destroy() {\n this.destroyAllPlugins();\n this.fireEvent('destroy');\n this.cancelAjax();\n this.clearTmpEvents();\n this.unAll();\n if (this.params.responsive !== false) {\n window.removeEventListener('resize', this._onResize, true);\n window.removeEventListener(\n 'orientationchange',\n this._onResize,\n true\n );\n }\n if (this.backend) {\n this.backend.destroy();\n // clears memory usage\n this.backend = null;\n }\n if (this.drawer) {\n this.drawer.destroy();\n }\n this.isDestroyed = true;\n this.isReady = false;\n this.arraybuffer = null;\n }\n}\n","import * as util from './util';\n\n// using constants to prevent someone writing the string wrong\nconst PLAYING = 'playing';\nconst PAUSED = 'paused';\nconst FINISHED = 'finished';\n\n/**\n * WebAudio backend\n *\n * @extends {Observer}\n */\nexport default class WebAudio extends util.Observer {\n /** scriptBufferSize: size of the processing buffer */\n static scriptBufferSize = 256;\n /** audioContext: allows to process audio with WebAudio API */\n audioContext = null;\n /** @private */\n offlineAudioContext = null;\n /** @private */\n stateBehaviors = {\n [PLAYING]: {\n init() {\n this.addOnAudioProcess();\n },\n getPlayedPercents() {\n const duration = this.getDuration();\n return this.getCurrentTime() / duration || 0;\n },\n getCurrentTime() {\n return this.startPosition + this.getPlayedTime();\n }\n },\n [PAUSED]: {\n init() {\n this.removeOnAudioProcess();\n },\n getPlayedPercents() {\n const duration = this.getDuration();\n return this.getCurrentTime() / duration || 0;\n },\n getCurrentTime() {\n return this.startPosition;\n }\n },\n [FINISHED]: {\n init() {\n this.removeOnAudioProcess();\n this.fireEvent('finish');\n },\n getPlayedPercents() {\n return 1;\n },\n getCurrentTime() {\n return this.getDuration();\n }\n }\n };\n\n /**\n * Does the browser support this backend\n *\n * @return {boolean} Whether or not this browser supports this backend\n */\n supportsWebAudio() {\n return !!(window.AudioContext || window.webkitAudioContext);\n }\n\n /**\n * Get the audio context used by this backend or create one\n *\n * @return {AudioContext} Existing audio context, or creates a new one\n */\n getAudioContext() {\n if (!window.WaveSurferAudioContext) {\n window.WaveSurferAudioContext = new (window.AudioContext ||\n window.webkitAudioContext)();\n }\n return window.WaveSurferAudioContext;\n }\n\n /**\n * Get the offline audio context used by this backend or create one\n *\n * @param {number} sampleRate The sample rate to use\n * @return {OfflineAudioContext} Existing offline audio context, or creates\n * a new one\n */\n getOfflineAudioContext(sampleRate) {\n if (!window.WaveSurferOfflineAudioContext) {\n window.WaveSurferOfflineAudioContext = new (window.OfflineAudioContext ||\n window.webkitOfflineAudioContext)(1, 2, sampleRate);\n }\n return window.WaveSurferOfflineAudioContext;\n }\n\n /**\n * Construct the backend\n *\n * @param {WavesurferParams} params Wavesurfer parameters\n */\n constructor(params) {\n super();\n /** @private */\n this.params = params;\n /** ac: Audio Context instance */\n this.ac =\n params.audioContext ||\n (this.supportsWebAudio() ? this.getAudioContext() : {});\n /**@private */\n this.lastPlay = this.ac.currentTime;\n /** @private */\n this.startPosition = 0;\n /** @private */\n this.scheduledPause = null;\n /** @private */\n this.states = {\n [PLAYING]: Object.create(this.stateBehaviors[PLAYING]),\n [PAUSED]: Object.create(this.stateBehaviors[PAUSED]),\n [FINISHED]: Object.create(this.stateBehaviors[FINISHED])\n };\n /** @private */\n this.buffer = null;\n /** @private */\n this.filters = [];\n /** gainNode: allows to control audio volume */\n this.gainNode = null;\n /** @private */\n this.mergedPeaks = null;\n /** @private */\n this.offlineAc = null;\n /** @private */\n this.peaks = null;\n /** @private */\n this.playbackRate = 1;\n /** analyser: provides audio analysis information */\n this.analyser = null;\n /** scriptNode: allows processing audio */\n this.scriptNode = null;\n /** @private */\n this.source = null;\n /** @private */\n this.splitPeaks = [];\n /** @private */\n this.state = null;\n /** @private */\n this.explicitDuration = params.duration;\n /**\n * Boolean indicating if the backend was destroyed.\n */\n this.destroyed = false;\n }\n\n /**\n * Initialise the backend, called in `wavesurfer.createBackend()`\n */\n init() {\n this.createVolumeNode();\n this.createScriptNode();\n this.createAnalyserNode();\n\n this.setState(PAUSED);\n this.setPlaybackRate(this.params.audioRate);\n this.setLength(0);\n }\n\n /** @private */\n disconnectFilters() {\n if (this.filters) {\n this.filters.forEach(filter => {\n filter && filter.disconnect();\n });\n this.filters = null;\n // Reconnect direct path\n this.analyser.connect(this.gainNode);\n }\n }\n\n /**\n * @private\n *\n * @param {string} state The new state\n */\n setState(state) {\n if (this.state !== this.states[state]) {\n this.state = this.states[state];\n this.state.init.call(this);\n }\n }\n\n /**\n * Unpacked `setFilters()`\n *\n * @param {...AudioNode} filters One or more filters to set\n */\n setFilter(...filters) {\n this.setFilters(filters);\n }\n\n /**\n * Insert custom Web Audio nodes into the graph\n *\n * @param {AudioNode[]} filters Packed filters array\n * @example\n * const lowpass = wavesurfer.backend.ac.createBiquadFilter();\n * wavesurfer.backend.setFilter(lowpass);\n */\n setFilters(filters) {\n // Remove existing filters\n this.disconnectFilters();\n\n // Insert filters if filter array not empty\n if (filters && filters.length) {\n this.filters = filters;\n\n // Disconnect direct path before inserting filters\n this.analyser.disconnect();\n\n // Connect each filter in turn\n filters\n .reduce((prev, curr) => {\n prev.connect(curr);\n return curr;\n }, this.analyser)\n .connect(this.gainNode);\n }\n }\n /** Create ScriptProcessorNode to process audio */\n createScriptNode() {\n if (this.params.audioScriptProcessor) {\n this.scriptNode = this.params.audioScriptProcessor;\n } else {\n if (this.ac.createScriptProcessor) {\n this.scriptNode = this.ac.createScriptProcessor(\n WebAudio.scriptBufferSize\n );\n } else {\n this.scriptNode = this.ac.createJavaScriptNode(\n WebAudio.scriptBufferSize\n );\n }\n }\n this.scriptNode.connect(this.ac.destination);\n }\n\n /** @private */\n addOnAudioProcess() {\n this.scriptNode.onaudioprocess = () => {\n const time = this.getCurrentTime();\n\n if (time >= this.getDuration()) {\n this.setState(FINISHED);\n this.fireEvent('pause');\n } else if (time >= this.scheduledPause) {\n this.pause();\n } else if (this.state === this.states[PLAYING]) {\n this.fireEvent('audioprocess', time);\n }\n };\n }\n\n /** @private */\n removeOnAudioProcess() {\n this.scriptNode.onaudioprocess = null;\n }\n /** Create analyser node to perform audio analysis */\n createAnalyserNode() {\n this.analyser = this.ac.createAnalyser();\n this.analyser.connect(this.gainNode);\n }\n\n /**\n * Create the gain node needed to control the playback volume.\n *\n */\n createVolumeNode() {\n // Create gain node using the AudioContext\n if (this.ac.createGain) {\n this.gainNode = this.ac.createGain();\n } else {\n this.gainNode = this.ac.createGainNode();\n }\n // Add the gain node to the graph\n this.gainNode.connect(this.ac.destination);\n }\n\n /**\n * Set the sink id for the media player\n *\n * @param {string} deviceId String value representing audio device id.\n * @returns {Promise} A Promise that resolves to `undefined` when there\n * are no errors.\n */\n setSinkId(deviceId) {\n if (deviceId) {\n /**\n * The webaudio API doesn't currently support setting the device\n * output. Here we create an HTMLAudioElement, connect the\n * webaudio stream to that element and setSinkId there.\n */\n let audio = new window.Audio();\n if (!audio.setSinkId) {\n return Promise.reject(\n new Error('setSinkId is not supported in your browser')\n );\n }\n audio.autoplay = true;\n const dest = this.ac.createMediaStreamDestination();\n this.gainNode.disconnect();\n this.gainNode.connect(dest);\n audio.srcObject = dest.stream;\n\n return audio.setSinkId(deviceId);\n } else {\n return Promise.reject(new Error('Invalid deviceId: ' + deviceId));\n }\n }\n\n /**\n * Set the audio volume\n *\n * @param {number} value A floating point value between 0 and 1.\n */\n setVolume(value) {\n this.gainNode.gain.setValueAtTime(value, this.ac.currentTime);\n }\n\n /**\n * Get the current volume\n *\n * @return {number} value A floating point value between 0 and 1.\n */\n getVolume() {\n return this.gainNode.gain.value;\n }\n\n /**\n * Decode an array buffer and pass data to a callback\n *\n * @private\n * @param {ArrayBuffer} arraybuffer The array buffer to decode\n * @param {function} callback The function to call on complete.\n * @param {function} errback The function to call on error.\n */\n decodeArrayBuffer(arraybuffer, callback, errback) {\n if (!this.offlineAc) {\n this.offlineAc = this.getOfflineAudioContext(\n this.ac && this.ac.sampleRate ? this.ac.sampleRate : 44100\n );\n }\n if ('webkitAudioContext' in window) {\n // Safari: no support for Promise-based decodeAudioData enabled\n // Enable it in Safari using the Experimental Features > Modern WebAudio API option\n this.offlineAc.decodeAudioData(\n arraybuffer,\n data => callback(data),\n errback\n );\n } else {\n this.offlineAc.decodeAudioData(arraybuffer).then(\n (data) => callback(data)\n ).catch(\n (err) => errback(err)\n );\n }\n }\n\n /**\n * Set pre-decoded peaks\n *\n * @param {number[]|Number.<Array[]>} peaks Peaks data\n * @param {?number} duration Explicit duration\n */\n setPeaks(peaks, duration) {\n if (duration != null) {\n this.explicitDuration = duration;\n }\n this.peaks = peaks;\n }\n\n /**\n * Set the rendered length (different from the length of the audio)\n *\n * @param {number} length The rendered length\n */\n setLength(length) {\n // No resize, we can preserve the cached peaks.\n if (this.mergedPeaks && length == 2 * this.mergedPeaks.length - 1 + 2) {\n return;\n }\n\n this.splitPeaks = [];\n this.mergedPeaks = [];\n // Set the last element of the sparse array so the peak arrays are\n // appropriately sized for other calculations.\n const channels = this.buffer ? this.buffer.numberOfChannels : 1;\n let c;\n for (c = 0; c < channels; c++) {\n this.splitPeaks[c] = [];\n this.splitPeaks[c][2 * (length - 1)] = 0;\n this.splitPeaks[c][2 * (length - 1) + 1] = 0;\n }\n this.mergedPeaks[2 * (length - 1)] = 0;\n this.mergedPeaks[2 * (length - 1) + 1] = 0;\n }\n\n /**\n * Compute the max and min value of the waveform when broken into <length> subranges.\n *\n * @param {number} length How many subranges to break the waveform into.\n * @param {number} first First sample in the required range.\n * @param {number} last Last sample in the required range.\n * @return {number[]|Number.<Array[]>} Array of 2*<length> peaks or array of arrays of\n * peaks consisting of (max, min) values for each subrange.\n */\n getPeaks(length, first, last) {\n if (this.peaks) {\n return this.peaks;\n }\n if (!this.buffer) {\n return [];\n }\n\n first = first || 0;\n last = last || length - 1;\n\n this.setLength(length);\n\n if (!this.buffer) {\n return this.params.splitChannels\n ? this.splitPeaks\n : this.mergedPeaks;\n }\n\n /**\n * The following snippet fixes a buffering data issue on the Safari\n * browser which returned undefined It creates the missing buffer based\n * on 1 channel, 4096 samples and the sampleRate from the current\n * webaudio context 4096 samples seemed to be the best fit for rendering\n * will review this code once a stable version of Safari TP is out\n */\n if (!this.buffer.length) {\n const newBuffer = this.createBuffer(1, 4096, this.sampleRate);\n this.buffer = newBuffer.buffer;\n }\n\n const sampleSize = this.buffer.length / length;\n const sampleStep = ~~(sampleSize / 10) || 1;\n const channels = this.buffer.numberOfChannels;\n let c;\n\n for (c = 0; c < channels; c++) {\n const peaks = this.splitPeaks[c];\n const chan = this.buffer.getChannelData(c);\n let i;\n\n for (i = first; i <= last; i++) {\n const start = ~~(i * sampleSize);\n const end = ~~(start + sampleSize);\n /**\n * Initialize the max and min to the first sample of this\n * subrange, so that even if the samples are entirely\n * on one side of zero, we still return the true max and\n * min values in the subrange.\n */\n let min = chan[start];\n let max = min;\n let j;\n\n for (j = start; j < end; j += sampleStep) {\n const value = chan[j];\n\n if (value > max) {\n max = value;\n }\n\n if (value < min) {\n min = value;\n }\n }\n\n peaks[2 * i] = max;\n peaks[2 * i + 1] = min;\n\n if (c == 0 || max > this.mergedPeaks[2 * i]) {\n this.mergedPeaks[2 * i] = max;\n }\n\n if (c == 0 || min < this.mergedPeaks[2 * i + 1]) {\n this.mergedPeaks[2 * i + 1] = min;\n }\n }\n }\n\n return this.params.splitChannels ? this.splitPeaks : this.mergedPeaks;\n }\n\n /**\n * Get the position from 0 to 1\n *\n * @return {number} Position\n */\n getPlayedPercents() {\n return this.state.getPlayedPercents.call(this);\n }\n\n /** @private */\n disconnectSource() {\n if (this.source) {\n this.source.disconnect();\n }\n }\n /**\n * Destroy all references with WebAudio, disconnecting audio nodes and closing Audio Context\n */\n destroyWebAudio() {\n this.disconnectFilters();\n this.disconnectSource();\n this.gainNode.disconnect();\n this.scriptNode.disconnect();\n this.analyser.disconnect();\n\n // close the audioContext if closeAudioContext option is set to true\n if (this.params.closeAudioContext) {\n // check if browser supports AudioContext.close()\n if (\n typeof this.ac.close === 'function' &&\n this.ac.state != 'closed'\n ) {\n this.ac.close();\n }\n // clear the reference to the audiocontext\n this.ac = null;\n // clear the actual audiocontext, either passed as param or the\n // global singleton\n if (!this.params.audioContext) {\n window.WaveSurferAudioContext = null;\n } else {\n this.params.audioContext = null;\n }\n // clear the offlineAudioContext\n window.WaveSurferOfflineAudioContext = null;\n }\n }\n /**\n * This is called when wavesurfer is destroyed\n */\n destroy() {\n if (!this.isPaused()) {\n this.pause();\n }\n this.unAll();\n this.buffer = null;\n this.destroyed = true;\n\n this.destroyWebAudio();\n }\n\n /**\n * Loaded a decoded audio buffer\n *\n * @param {Object} buffer Decoded audio buffer to load\n */\n load(buffer) {\n this.startPosition = 0;\n this.lastPlay = this.ac.currentTime;\n this.buffer = buffer;\n this.createSource();\n }\n\n /** @private */\n createSource() {\n this.disconnectSource();\n this.source = this.ac.createBufferSource();\n\n // adjust for old browsers\n this.source.start = this.source.start || this.source.noteGrainOn;\n this.source.stop = this.source.stop || this.source.noteOff;\n\n this.setPlaybackRate(this.playbackRate);\n this.source.buffer = this.buffer;\n this.source.connect(this.analyser);\n }\n\n /**\n * @private\n *\n * some browsers require an explicit call to #resume before they will play back audio\n */\n resumeAudioContext() {\n if (this.ac.state == 'suspended') {\n this.ac.resume && this.ac.resume();\n }\n }\n\n /**\n * Used by `wavesurfer.isPlaying()` and `wavesurfer.playPause()`\n *\n * @return {boolean} Whether or not this backend is currently paused\n */\n isPaused() {\n return this.state !== this.states[PLAYING];\n }\n\n /**\n * Used by `wavesurfer.getDuration()`\n *\n * @return {number} Duration of loaded buffer\n */\n getDuration() {\n if (this.explicitDuration) {\n return this.explicitDuration;\n }\n if (!this.buffer) {\n return 0;\n }\n return this.buffer.duration;\n }\n\n /**\n * Used by `wavesurfer.seekTo()`\n *\n * @param {number} start Position to start at in seconds\n * @param {number} end Position to end at in seconds\n * @return {{start: number, end: number}} Object containing start and end\n * positions\n */\n seekTo(start, end) {\n if (!this.buffer) {\n return;\n }\n\n this.scheduledPause = null;\n\n if (start == null) {\n start = this.getCurrentTime();\n if (start >= this.getDuration()) {\n start = 0;\n }\n }\n if (end == null) {\n end = this.getDuration();\n }\n\n this.startPosition = start;\n this.lastPlay = this.ac.currentTime;\n\n if (this.state === this.states[FINISHED]) {\n this.setState(PAUSED);\n }\n\n return {\n start: start,\n end: end\n };\n }\n\n /**\n * Get the playback position in seconds\n *\n * @return {number} The playback position in seconds\n */\n getPlayedTime() {\n return (this.ac.currentTime - this.lastPlay) * this.playbackRate;\n }\n\n /**\n * Plays the loaded audio region.\n *\n * @param {number} start Start offset in seconds, relative to the beginning\n * of a clip.\n * @param {number} end When to stop relative to the beginning of a clip.\n */\n play(start, end) {\n if (!this.buffer) {\n return;\n }\n\n // need to re-create source on each playback\n this.createSource();\n\n const adjustedTime = this.seekTo(start, end);\n\n start = adjustedTime.start;\n end = adjustedTime.end;\n\n this.scheduledPause = end;\n\n this.source.start(0, start);\n\n this.resumeAudioContext();\n\n this.setState(PLAYING);\n\n this.fireEvent('play');\n }\n\n /**\n * Pauses the loaded audio.\n */\n pause() {\n this.scheduledPause = null;\n\n this.startPosition += this.getPlayedTime();\n this.source && this.source.stop(0);\n\n this.setState(PAUSED);\n\n this.fireEvent('pause');\n }\n\n /**\n * Returns the current time in seconds relative to the audio-clip's\n * duration.\n *\n * @return {number} The current time in seconds\n */\n getCurrentTime() {\n return this.state.getCurrentTime.call(this);\n }\n\n /**\n * Returns the current playback rate. (0=no playback, 1=normal playback)\n *\n * @return {number} The current playback rate\n */\n getPlaybackRate() {\n return this.playbackRate;\n }\n\n /**\n * Set the audio source playback rate.\n *\n * @param {number} value The playback rate to use\n */\n setPlaybackRate(value) {\n this.playbackRate = value || 1;\n this.source && this.source.playbackRate.setValueAtTime(\n this.playbackRate,\n this.ac.currentTime\n );\n }\n\n /**\n * Set a point in seconds for playback to stop at.\n *\n * @param {number} end Position to end at\n * @version 3.3.0\n */\n setPlayEnd(end) {\n this.scheduledPause = end;\n }\n}\n","/**\n * Returns a function, that, as long as it continues to be invoked, will not\n * be triggered. The function will be called after it stops being called for\n * N milliseconds. If `immediate` is passed, trigger the function on the\n * leading edge, instead of the trailing. The function also has a property 'clear' \n * that is a function which will clear the timer to prevent previously scheduled executions. \n *\n * @source underscore.js\n * @see http://unscriptable.com/2009/03/20/debouncing-javascript-methods/\n * @param {Function} function to wrap\n * @param {Number} timeout in ms (`100`)\n * @param {Boolean} whether to execute at the beginning (`false`)\n * @api public\n */\nfunction debounce(func, wait, immediate){\n var timeout, args, context, timestamp, result;\n if (null == wait) wait = 100;\n\n function later() {\n var last = Date.now() - timestamp;\n\n if (last < wait && last >= 0) {\n timeout = setTimeout(later, wait - last);\n } else {\n timeout = null;\n if (!immediate) {\n result = func.apply(context, args);\n context = args = null;\n }\n }\n };\n\n var debounced = function(){\n context = this;\n args = arguments;\n timestamp = Date.now();\n var callNow = immediate && !timeout;\n if (!timeout) timeout = setTimeout(later, wait);\n if (callNow) {\n result = func.apply(context, args);\n context = args = null;\n }\n\n return result;\n };\n\n debounced.clear = function() {\n if (timeout) {\n clearTimeout(timeout);\n timeout = null;\n }\n };\n \n debounced.flush = function() {\n if (timeout) {\n result = func.apply(context, args);\n context = args = null;\n \n clearTimeout(timeout);\n timeout = null;\n }\n };\n\n return debounced;\n};\n\n// Adds compatibility for ES modules\ndebounce.debounce = debounce;\n\nmodule.exports = debounce;\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nvar __webpack_exports__ = __webpack_require__(631);\n"],"names":["root","factory","exports","module","define","amd","this","CanvasEntry","wave","waveCtx","progress","progressCtx","start","end","id","constructor","name","toLowerCase","canvasContextAttributes","element","getContext","elementWidth","totalWidth","width","height","offsetLeft","elementSize","hasProgressCanvas","clearRect","canvas","waveColor","progressColor","fillStyle","vertical","setTransform","x","y","radius","fillRectToContext","ctx","drawRoundedRect","fillRect","beginPath","moveTo","lineTo","quadraticCurveTo","closePath","fill","peaks","absmax","halfH","offsetY","drawLineToContext","i","peak","h","length","first","Math","round","canvasStart","canvasEnd","scale","halfOffset","absmaxHalf","halfPixel","j","format","quality","type","Promise","resolve","toBlob","toDataURL","Drawer","container","params","util","withOrientation","pixelRatio","lastPos","wrapper","el","styles","style","appendChild","document","createElement","display","position","userSelect","webkitUserSelect","fillParent","scrollParent","overflowX","hideScrollbar","overflowY","setupWrapperEvents","e","noPrevent","preventDefault","clientX","targetTouches","bbox","getBoundingClientRect","nominalWidth","parentWidth","getWidth","progressPixels","getProgressPixels","scrollLeft","scrollWidth","clamp","wrapperBbox","rtl","right","left","addEventListener","orientedEvent","scrollbarHeight","offsetHeight","clientHeight","clientY","bottom","interact","fireEvent","handleEvent","setWidth","clearWave","barWidth","drawBars","drawWave","percent","recenterOnPosition","immediate","half","clientWidth","maxScroll","target","offset","rate","autoCenterRate","max","min","newWidth","updateSize","minPxDelta","pos","autoCenter","newPos","autoCenterImmediately","updateProgress","unAll","parentNode","domElement","removeChild","channelIndex","Observer","MultiCanvas","maxCanvasWidth","maxCanvasElementWidth","canvases","progressWave","EntryClass","drawingContextAttributes","overlap","ceil","barRadius","createWrapper","createElements","zIndex","top","overflow","boxSizing","borderRightStyle","pointerEvents","addCanvas","updateCursor","borderRightWidth","cursorWidth","borderRightColor","cursorColor","requiredCanvases","removeCanvas","canvasWidth","lastCanvas","forEach","entry","updateDimensions","leftOffset","initWave","initProgress","push","lastEntry","parentElement","destroy","pop","frame","prepareDraw","hasMinVals","ch","undefined","peakIndexScale","bar","step","barGap","last","floor","barMinHeight","reflectedPeaks","len","drawLine","splitChannelsOptions","channelColors","setFillStyles","applyCanvasTransforms","drawLines","startCanvas","endCanvas","intersection","x1","y1","x2","y2","fillRects","splitChannels","filterChannels","includes","fn","drawIndex","normalizedMax","Array","channels","overallAbsMax","filteredChannels","filter","c","hideChannel","overlay","setHeight","relativeNormalization","map","channelPeaks","absMax","indexOf","barHeight","normalize","some","call","val","all","getImage","images","MediaElementWebAudio","sourceMediaElement","setPlaybackRate","audioRate","createTimer","createVolumeNode","createScriptNode","createAnalyserNode","media","preload","createMediaElementSource","mediaElement","ac","connect","analyser","resumeAudioContext","destroyWebAudio","MediaElement","currentTime","duration","paused","playbackRate","play","pause","volume","mediaType","elementPosition","isMuted","buffer","onPlayEnd","mediaListeners","error","canplay","ended","seeked","event","volumechange","muted","Object","keys","removeEventListener","on","onAudioProcess","isPaused","getCurrentTime","url","controls","mediaControls","autoplay","src","prevMedia","querySelector","_load","elt","HTMLMediaElement","Error","load","_setupMediaListeners","setVolume","explicitDuration","Infinity","seekable","getDuration","value","clearPlayEnd","seekTo","promise","setPlayEnd","_onPlayEnd","time","un","deviceId","setSinkId","reject","destroyed","removeMediaElementOnDestroy","WebAudio","PeakCache","clearPeakCache","peakCacheRanges","peakCacheLength","uncachedRanges","item","arr","concat","sort","a","b","uncachedRangePairs","peakCacheRangePairs","values","options","instance","fetchHeaders","Headers","fetchRequest","Request","controller","AbortController","requestHeaders","header","append","key","responseType","fetchOptions","method","headers","mode","credentials","cache","redirect","referrer","signal","fetch","then","response","progressAvailable","body","contentLength","get","onProgress","Response","ReadableStream","ProgressHandler","errMsg","ok","arrayBuffer","json","blob","text","status","catch","_reader","getReader","total","parseInt","loaded","read","done","lengthComputable","close","byteLength","enqueue","func","args","prefix","random","toString","substring","largest","smallest","Number","_disabledEventEmissions","handlers","callback","splice","handler","apply","setTimeout","eventNames","_isDisabledEventEmission","isProxy","Proxy","obj","prop","receiver","mapProp","bind","set","verticalPropMap","offsetTop","offsetWidth","prototype","hasOwnProperty","Symbol","preventClickHandler","stopPropagation","window","requestAnimationFrame","webkitRequestAnimationFrame","mozRequestAnimationFrame","oRequestAnimationFrame","msRequestAnimationFrame","WaveSurfer","defaultParams","audioContext","audioScriptProcessor","backend","backgroundColor","dragSelection","desynchronized","forceDecode","loopSelection","mediaContainer","minPxPerSec","partialRender","devicePixelRatio","screen","deviceXDPI","logicalXDPI","plugins","renderer","responsive","skipLength","xhr","backends","assign","transform","setBackgroundColor","savedVolume","tmpEvents","currentRequest","arraybuffer","drawer","peakCache","supportsWebAudio","Backend","initialisedPluginList","isDestroyed","isReady","prevWidth","_onResize","debounce","registerPlugins","createDrawer","createBackend","createPeakCache","plugin","addPlugin","deferInit","initPlugin","staticProps","pluginStaticProp","Instance","getOwnPropertyNames","destroyPlugin","init","drawBuffer","getPlayedPercents","newVolume","getVolume","seconds","skip","seekAndCenter","recenter","isFinite","isWebAudioBackend","oldScrollParent","getPlaybackRate","setMute","mute","filters","color","background","channelIndices","newRanges","addRangeToPeakCache","getPeaks","drawPeaks","pxPerSec","decodeArrayBuffer","data","loadDecodedBuffer","reader","FileReader","loadArrayBuffer","result","readAsArrayBuffer","empty","preloadIgnoreReasons","activeReasons","reason","console","warn","join","loadBuffer","loadMediaElement","action","once","getArrayBuffer","setPeaks","urlOrElt","loadElt","err","request","fetchFile","percentComplete","accuracy","noWindow","blobJSON","Blob","JSON","stringify","objURL","URL","createObjectURL","open","revokeObjectURL","cancel","abort","stop","disconnectSource","cancelAjax","clearTmpEvents","destroyAllPlugins","VERSION","__VERSION__","PLAYING","PAUSED","FINISHED","offlineAudioContext","stateBehaviors","addOnAudioProcess","startPosition","getPlayedTime","removeOnAudioProcess","getAudioContext","lastPlay","scheduledPause","states","create","gainNode","mergedPeaks","offlineAc","scriptNode","source","splitPeaks","state","AudioContext","webkitAudioContext","WaveSurferAudioContext","sampleRate","WaveSurferOfflineAudioContext","OfflineAudioContext","webkitOfflineAudioContext","setState","setLength","disconnect","setFilters","disconnectFilters","reduce","prev","curr","createScriptProcessor","scriptBufferSize","createJavaScriptNode","destination","onaudioprocess","createAnalyser","createGain","createGainNode","audio","Audio","dest","createMediaStreamDestination","srcObject","stream","gain","setValueAtTime","errback","getOfflineAudioContext","decodeAudioData","numberOfChannels","newBuffer","createBuffer","sampleSize","sampleStep","chan","getChannelData","closeAudioContext","createSource","createBufferSource","noteGrainOn","noteOff","resume","adjustedTime","wait","timeout","context","timestamp","later","Date","now","debounced","arguments","callNow","clear","clearTimeout","flush","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__"],"sourceRoot":""}