DPlayer.js 69 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452
  1. console.log("\n %c DPlayer 1.1.4 %c http://dplayer.js.org \n\n","color: #fadfa3; background: #030307; padding:5px 0;","background: #fadfa3; padding:5px 0;");
  2. require('./DPlayer.scss');
  3. const defaultApiBackend = require('./api.js');
  4. let index = 0;
  5. class DPlayer {
  6. /**
  7. * DPlayer constructor function
  8. *
  9. * @param {Object} option - See README
  10. * @constructor
  11. */
  12. constructor(option) {
  13. const svg = {
  14. 'play': ['0 0 16 32', 'M15.552 15.168q0.448 0.32 0.448 0.832 0 0.448-0.448 0.768l-13.696 8.512q-0.768 0.512-1.312 0.192t-0.544-1.28v-16.448q0-0.96 0.544-1.28t1.312 0.192z'],
  15. 'pause': ['0 0 17 32', 'M14.080 4.8q2.88 0 2.88 2.048v18.24q0 2.112-2.88 2.112t-2.88-2.112v-18.24q0-2.048 2.88-2.048zM2.88 4.8q2.88 0 2.88 2.048v18.24q0 2.112-2.88 2.112t-2.88-2.112v-18.24q0-2.048 2.88-2.048z'],
  16. 'volume-up': ['0 0 21 32', 'M13.728 6.272v19.456q0 0.448-0.352 0.8t-0.8 0.32-0.8-0.32l-5.952-5.952h-4.672q-0.48 0-0.8-0.352t-0.352-0.8v-6.848q0-0.48 0.352-0.8t0.8-0.352h4.672l5.952-5.952q0.32-0.32 0.8-0.32t0.8 0.32 0.352 0.8zM20.576 16q0 1.344-0.768 2.528t-2.016 1.664q-0.16 0.096-0.448 0.096-0.448 0-0.8-0.32t-0.32-0.832q0-0.384 0.192-0.64t0.544-0.448 0.608-0.384 0.512-0.64 0.192-1.024-0.192-1.024-0.512-0.64-0.608-0.384-0.544-0.448-0.192-0.64q0-0.48 0.32-0.832t0.8-0.32q0.288 0 0.448 0.096 1.248 0.48 2.016 1.664t0.768 2.528zM25.152 16q0 2.72-1.536 5.056t-4 3.36q-0.256 0.096-0.448 0.096-0.48 0-0.832-0.352t-0.32-0.8q0-0.704 0.672-1.056 1.024-0.512 1.376-0.8 1.312-0.96 2.048-2.4t0.736-3.104-0.736-3.104-2.048-2.4q-0.352-0.288-1.376-0.8-0.672-0.352-0.672-1.056 0-0.448 0.32-0.8t0.8-0.352q0.224 0 0.48 0.096 2.496 1.056 4 3.36t1.536 5.056zM29.728 16q0 4.096-2.272 7.552t-6.048 5.056q-0.224 0.096-0.448 0.096-0.48 0-0.832-0.352t-0.32-0.8q0-0.64 0.704-1.056 0.128-0.064 0.384-0.192t0.416-0.192q0.8-0.448 1.44-0.896 2.208-1.632 3.456-4.064t1.216-5.152-1.216-5.152-3.456-4.064q-0.64-0.448-1.44-0.896-0.128-0.096-0.416-0.192t-0.384-0.192q-0.704-0.416-0.704-1.056 0-0.448 0.32-0.8t0.832-0.352q0.224 0 0.448 0.096 3.776 1.632 6.048 5.056t2.272 7.552z'],
  17. 'volume-down': ['0 0 21 32', 'M13.728 6.272v19.456q0 0.448-0.352 0.8t-0.8 0.32-0.8-0.32l-5.952-5.952h-4.672q-0.48 0-0.8-0.352t-0.352-0.8v-6.848q0-0.48 0.352-0.8t0.8-0.352h4.672l5.952-5.952q0.32-0.32 0.8-0.32t0.8 0.32 0.352 0.8zM20.576 16q0 1.344-0.768 2.528t-2.016 1.664q-0.16 0.096-0.448 0.096-0.448 0-0.8-0.32t-0.32-0.832q0-0.384 0.192-0.64t0.544-0.448 0.608-0.384 0.512-0.64 0.192-1.024-0.192-1.024-0.512-0.64-0.608-0.384-0.544-0.448-0.192-0.64q0-0.48 0.32-0.832t0.8-0.32q0.288 0 0.448 0.096 1.248 0.48 2.016 1.664t0.768 2.528z'],
  18. 'volume-off': ['0 0 21 32', 'M13.728 6.272v19.456q0 0.448-0.352 0.8t-0.8 0.32-0.8-0.32l-5.952-5.952h-4.672q-0.48 0-0.8-0.352t-0.352-0.8v-6.848q0-0.48 0.352-0.8t0.8-0.352h4.672l5.952-5.952q0.32-0.32 0.8-0.32t0.8 0.32 0.352 0.8z'],
  19. 'loop': ['0 0 32 32', 'M1.882 16.941c0 4.152 3.221 7.529 7.177 7.529v1.882c-4.996 0-9.060-4.222-9.060-9.412s4.064-9.412 9.060-9.412h7.96l-3.098-3.098 1.331-1.331 5.372 5.37-5.37 5.372-1.333-1.333 3.1-3.098h-7.962c-3.957 0-7.177 3.377-7.177 7.529zM22.94 7.529v1.882c3.957 0 7.177 3.377 7.177 7.529s-3.221 7.529-7.177 7.529h-7.962l3.098-3.098-1.331-1.331-5.37 5.37 5.372 5.372 1.331-1.331-3.1-3.1h7.96c4.998 0 9.062-4.222 9.062-9.412s-4.064-9.412-9.060-9.412z'],
  20. 'full': ['0 0 32 33', 'M6.667 28h-5.333c-0.8 0-1.333-0.533-1.333-1.333v-5.333c0-0.8 0.533-1.333 1.333-1.333s1.333 0.533 1.333 1.333v4h4c0.8 0 1.333 0.533 1.333 1.333s-0.533 1.333-1.333 1.333zM30.667 28h-5.333c-0.8 0-1.333-0.533-1.333-1.333s0.533-1.333 1.333-1.333h4v-4c0-0.8 0.533-1.333 1.333-1.333s1.333 0.533 1.333 1.333v5.333c0 0.8-0.533 1.333-1.333 1.333zM30.667 12c-0.8 0-1.333-0.533-1.333-1.333v-4h-4c-0.8 0-1.333-0.533-1.333-1.333s0.533-1.333 1.333-1.333h5.333c0.8 0 1.333 0.533 1.333 1.333v5.333c0 0.8-0.533 1.333-1.333 1.333zM1.333 12c-0.8 0-1.333-0.533-1.333-1.333v-5.333c0-0.8 0.533-1.333 1.333-1.333h5.333c0.8 0 1.333 0.533 1.333 1.333s-0.533 1.333-1.333 1.333h-4v4c0 0.8-0.533 1.333-1.333 1.333z'],
  21. 'full-in': ['0 0 32 33', 'M24.965 24.38h-18.132c-1.366 0-2.478-1.113-2.478-2.478v-11.806c0-1.364 1.111-2.478 2.478-2.478h18.132c1.366 0 2.478 1.113 2.478 2.478v11.806c0 1.364-1.11 2.478-2.478 2.478zM6.833 10.097v11.806h18.134l-0.002-11.806h-18.132zM2.478 28.928h5.952c0.684 0 1.238-0.554 1.238-1.239 0-0.684-0.554-1.238-1.238-1.238h-5.952v-5.802c0-0.684-0.554-1.239-1.238-1.239s-1.239 0.556-1.239 1.239v5.802c0 1.365 1.111 2.478 2.478 2.478zM30.761 19.412c-0.684 0-1.238 0.554-1.238 1.238v5.801h-5.951c-0.686 0-1.239 0.554-1.239 1.238 0 0.686 0.554 1.239 1.239 1.239h5.951c1.366 0 2.478-1.111 2.478-2.478v-5.801c0-0.683-0.554-1.238-1.239-1.238zM0 5.55v5.802c0 0.683 0.554 1.238 1.238 1.238s1.238-0.555 1.238-1.238v-5.802h5.952c0.684 0 1.238-0.554 1.238-1.238s-0.554-1.238-1.238-1.238h-5.951c-1.366-0.001-2.478 1.111-2.478 2.476zM32 11.35v-5.801c0-1.365-1.11-2.478-2.478-2.478h-5.951c-0.686 0-1.239 0.554-1.239 1.238s0.554 1.238 1.239 1.238h5.951v5.801c0 0.683 0.554 1.237 1.238 1.237 0.686 0.002 1.239-0.553 1.239-1.236z'],
  22. 'setting': ['0 0 32 28', 'M28.633 17.104c0.035 0.21 0.026 0.463-0.026 0.76s-0.14 0.598-0.262 0.904c-0.122 0.306-0.271 0.581-0.445 0.825s-0.367 0.419-0.576 0.524c-0.209 0.105-0.393 0.157-0.55 0.157s-0.332-0.035-0.524-0.105c-0.175-0.052-0.393-0.1-0.655-0.144s-0.528-0.052-0.799-0.026c-0.271 0.026-0.541 0.083-0.812 0.17s-0.502 0.236-0.694 0.445c-0.419 0.437-0.664 0.934-0.734 1.493s0.009 1.092 0.236 1.598c0.175 0.349 0.148 0.699-0.079 1.048-0.105 0.14-0.271 0.284-0.498 0.432s-0.476 0.284-0.747 0.406-0.555 0.218-0.851 0.288c-0.297 0.070-0.559 0.105-0.786 0.105-0.157 0-0.306-0.061-0.445-0.183s-0.236-0.253-0.288-0.393h-0.026c-0.192-0.541-0.52-1.009-0.982-1.402s-1-0.589-1.611-0.589c-0.594 0-1.131 0.197-1.611 0.589s-0.816 0.851-1.009 1.375c-0.087 0.21-0.218 0.362-0.393 0.458s-0.367 0.144-0.576 0.144c-0.244 0-0.52-0.044-0.825-0.131s-0.611-0.197-0.917-0.327c-0.306-0.131-0.581-0.284-0.825-0.458s-0.428-0.349-0.55-0.524c-0.087-0.122-0.135-0.266-0.144-0.432s0.057-0.397 0.197-0.694c0.192-0.402 0.266-0.86 0.223-1.375s-0.266-0.991-0.668-1.428c-0.244-0.262-0.541-0.432-0.891-0.511s-0.681-0.109-0.995-0.092c-0.367 0.017-0.742 0.087-1.127 0.21-0.244 0.070-0.489 0.052-0.734-0.052-0.192-0.070-0.371-0.231-0.537-0.485s-0.314-0.533-0.445-0.838c-0.131-0.306-0.231-0.62-0.301-0.943s-0.087-0.59-0.052-0.799c0.052-0.384 0.227-0.629 0.524-0.734 0.524-0.21 0.995-0.555 1.415-1.035s0.629-1.017 0.629-1.611c0-0.611-0.21-1.144-0.629-1.598s-0.891-0.786-1.415-0.996c-0.157-0.052-0.288-0.179-0.393-0.38s-0.157-0.406-0.157-0.616c0-0.227 0.035-0.48 0.105-0.76s0.162-0.55 0.275-0.812 0.244-0.502 0.393-0.72c0.148-0.218 0.31-0.38 0.485-0.485 0.14-0.087 0.275-0.122 0.406-0.105s0.275 0.052 0.432 0.105c0.524 0.21 1.070 0.275 1.637 0.197s1.070-0.327 1.506-0.747c0.21-0.209 0.362-0.467 0.458-0.773s0.157-0.607 0.183-0.904c0.026-0.297 0.026-0.568 0-0.812s-0.048-0.419-0.065-0.524c-0.035-0.105-0.066-0.227-0.092-0.367s-0.013-0.262 0.039-0.367c0.105-0.244 0.293-0.458 0.563-0.642s0.563-0.336 0.878-0.458c0.314-0.122 0.62-0.214 0.917-0.275s0.533-0.092 0.707-0.092c0.227 0 0.406 0.074 0.537 0.223s0.223 0.301 0.275 0.458c0.192 0.471 0.507 0.886 0.943 1.244s0.952 0.537 1.546 0.537c0.611 0 1.153-0.17 1.624-0.511s0.803-0.773 0.996-1.297c0.070-0.14 0.179-0.284 0.327-0.432s0.301-0.223 0.458-0.223c0.244 0 0.511 0.035 0.799 0.105s0.572 0.166 0.851 0.288c0.279 0.122 0.537 0.279 0.773 0.472s0.423 0.402 0.563 0.629c0.087 0.14 0.113 0.293 0.079 0.458s-0.070 0.284-0.105 0.354c-0.227 0.506-0.297 1.039-0.21 1.598s0.341 1.048 0.76 1.467c0.419 0.419 0.934 0.651 1.546 0.694s1.179-0.057 1.703-0.301c0.14-0.087 0.31-0.122 0.511-0.105s0.371 0.096 0.511 0.236c0.262 0.244 0.493 0.616 0.694 1.113s0.336 1 0.406 1.506c0.035 0.297-0.013 0.528-0.144 0.694s-0.266 0.275-0.406 0.327c-0.542 0.192-1.004 0.528-1.388 1.009s-0.576 1.026-0.576 1.637c0 0.594 0.162 1.113 0.485 1.559s0.747 0.764 1.27 0.956c0.122 0.070 0.227 0.14 0.314 0.21 0.192 0.157 0.323 0.358 0.393 0.602v0zM16.451 19.462c0.786 0 1.528-0.149 2.227-0.445s1.305-0.707 1.821-1.231c0.515-0.524 0.921-1.131 1.218-1.821s0.445-1.428 0.445-2.214c0-0.786-0.148-1.524-0.445-2.214s-0.703-1.292-1.218-1.808c-0.515-0.515-1.122-0.921-1.821-1.218s-1.441-0.445-2.227-0.445c-0.786 0-1.524 0.148-2.214 0.445s-1.292 0.703-1.808 1.218c-0.515 0.515-0.921 1.118-1.218 1.808s-0.445 1.428-0.445 2.214c0 0.786 0.149 1.524 0.445 2.214s0.703 1.297 1.218 1.821c0.515 0.524 1.118 0.934 1.808 1.231s1.428 0.445 2.214 0.445v0z'],
  23. 'right': ['0 0 32 32', 'M22 16l-10.105-10.6-1.895 1.987 8.211 8.613-8.211 8.612 1.895 1.988 8.211-8.613z'],
  24. 'comment': ['0 0 32 32', 'M27.128 0.38h-22.553c-2.336 0-4.229 1.825-4.229 4.076v16.273c0 2.251 1.893 4.076 4.229 4.076h4.229v-2.685h8.403l-8.784 8.072 1.566 1.44 7.429-6.827h9.71c2.335 0 4.229-1.825 4.229-4.076v-16.273c0-2.252-1.894-4.076-4.229-4.076zM28.538 19.403c0 1.5-1.262 2.717-2.819 2.717h-8.36l-0.076-0.070-0.076 0.070h-11.223c-1.557 0-2.819-1.217-2.819-2.717v-13.589c0-1.501 1.262-2.718 2.819-2.718h19.734c1.557 0 2.819-0.141 2.819 1.359v14.947zM9.206 10.557c-1.222 0-2.215 0.911-2.215 2.036s0.992 2.035 2.215 2.035c1.224 0 2.216-0.911 2.216-2.035s-0.992-2.036-2.216-2.036zM22.496 10.557c-1.224 0-2.215 0.911-2.215 2.036s0.991 2.035 2.215 2.035c1.224 0 2.215-0.911 2.215-2.035s-0.991-2.036-2.215-2.036zM15.852 10.557c-1.224 0-2.215 0.911-2.215 2.036s0.991 2.035 2.215 2.035c1.222 0 2.215-0.911 2.215-2.035s-0.992-2.036-2.215-2.036z'],
  25. 'comment-off': ['0 0 32 32', 'M27.090 0.131h-22.731c-2.354 0-4.262 1.839-4.262 4.109v16.401c0 2.269 1.908 4.109 4.262 4.109h4.262v-2.706h8.469l-8.853 8.135 1.579 1.451 7.487-6.88h9.787c2.353 0 4.262-1.84 4.262-4.109v-16.401c0-2.27-1.909-4.109-4.262-4.109v0zM28.511 19.304c0 1.512-1.272 2.738-2.841 2.738h-8.425l-0.076-0.070-0.076 0.070h-11.311c-1.569 0-2.841-1.226-2.841-2.738v-13.696c0-1.513 1.272-2.739 2.841-2.739h19.889c1.569 0 2.841-0.142 2.841 1.37v15.064z'],
  26. 'send': ['0 0 32 32', 'M13.725 30l3.9-5.325-3.9-1.125v6.45zM0 17.5l11.050 3.35 13.6-11.55-10.55 12.425 11.8 3.65 6.1-23.375-32 15.5z'],
  27. 'menu': ['0 0 22 32', 'M20.8 14.4q0.704 0 1.152 0.48t0.448 1.12-0.48 1.12-1.12 0.48h-19.2q-0.64 0-1.12-0.48t-0.48-1.12 0.448-1.12 1.152-0.48h19.2zM1.6 11.2q-0.64 0-1.12-0.48t-0.48-1.12 0.448-1.12 1.152-0.48h19.2q0.704 0 1.152 0.48t0.448 1.12-0.48 1.12-1.12 0.48h-19.2zM20.8 20.8q0.704 0 1.152 0.48t0.448 1.12-0.48 1.12-1.12 0.48h-19.2q-0.64 0-1.12-0.48t-0.48-1.12 0.448-1.12 1.152-0.48h19.2z'],
  28. 'camera': ['0 0 32 32', 'M16 23c-3.309 0-6-2.691-6-6s2.691-6 6-6 6 2.691 6 6-2.691 6-6 6zM16 13c-2.206 0-4 1.794-4 4s1.794 4 4 4c2.206 0 4-1.794 4-4s-1.794-4-4-4zM27 28h-22c-1.654 0-3-1.346-3-3v-16c0-1.654 1.346-3 3-3h3c0.552 0 1 0.448 1 1s-0.448 1-1 1h-3c-0.551 0-1 0.449-1 1v16c0 0.552 0.449 1 1 1h22c0.552 0 1-0.448 1-1v-16c0-0.551-0.448-1-1-1h-11c-0.552 0-1-0.448-1-1s0.448-1 1-1h11c1.654 0 3 1.346 3 3v16c0 1.654-1.346 3-3 3zM24 10.5c0 0.828 0.672 1.5 1.5 1.5s1.5-0.672 1.5-1.5c0-0.828-0.672-1.5-1.5-1.5s-1.5 0.672-1.5 1.5zM15 4c0 0.552-0.448 1-1 1h-4c-0.552 0-1-0.448-1-1v0c0-0.552 0.448-1 1-1h4c0.552 0 1 0.448 1 1v0z']
  29. };
  30. this.getSVG = (type) => {
  31. return `
  32. <svg xmlns:xlink="http://www.w3.org/1999/xlink" height="100%" version="1.1" viewBox="${svg[type][0]}" width="100%">
  33. <use xlink:href="#dplayer-${type}"></use>
  34. <path class="dplayer-fill" d="${svg[type][1]}" id="dplayer-${type}"></path>
  35. </svg>
  36. `;
  37. };
  38. this.option = option;
  39. const isMobile = /mobile/i.test(window.navigator.userAgent);
  40. // compatibility: some mobile browsers don't suppose autoplay
  41. if (isMobile) {
  42. this.option.autoplay = false;
  43. }
  44. // default options
  45. const defaultOption = {
  46. element: document.getElementsByClassName('dplayer')[0],
  47. autoplay: false,
  48. theme: '#b7daff',
  49. loop: false,
  50. lang: navigator.language.indexOf('zh') !== -1 ? 'zh' : 'en',
  51. screenshot: false,
  52. hotkey: true,
  53. preload: 'auto',
  54. apiBackend: defaultApiBackend
  55. };
  56. for (let defaultKey in defaultOption) {
  57. if (defaultOption.hasOwnProperty(defaultKey) && !this.option.hasOwnProperty(defaultKey)) {
  58. this.option[defaultKey] = defaultOption[defaultKey];
  59. }
  60. }
  61. if (!this.option.video.hasOwnProperty('type')) {
  62. this.option.video.type = 'auto';
  63. }
  64. const tranZH = {
  65. 'Danmaku is loading': '弹幕加载中',
  66. 'Top': '顶部',
  67. 'Bottom': '底部',
  68. 'Rolling': '滚动',
  69. 'Input danmaku, hit Enter': '输入弹幕,回车发送',
  70. 'About author': '关于作者',
  71. 'DPlayer feedback': '播放器意见反馈',
  72. 'About DPlayer': '关于 DPlay 播放器',
  73. 'Loop': '洗脑循环',
  74. 'Speed': '速度',
  75. 'Opacity for danmaku': '弹幕透明度',
  76. 'Normal': '正常',
  77. 'Please input danmaku!': '要输入弹幕内容啊喂!',
  78. 'Set danmaku color': '设置弹幕颜色',
  79. 'Set danmaku type': '设置弹幕类型',
  80. 'Danmaku': '弹幕'
  81. };
  82. const getTran = (text) => {
  83. if (this.option.lang === 'en') {
  84. return text;
  85. }
  86. else if (this.option.lang === 'zh') {
  87. return tranZH[text];
  88. }
  89. };
  90. /**
  91. * Update progress bar, including loading progress bar and play progress bar
  92. *
  93. * @param {String} type - Point out which bar it is, should be played loaded or volume
  94. * @param {Number} percentage
  95. * @param {String} direction - Point out the direction of this bar, Should be height or width
  96. */
  97. this.updateBar = (type, percentage, direction) => {
  98. percentage = percentage > 0 ? percentage : 0;
  99. percentage = percentage < 1 ? percentage : 1;
  100. bar[type + 'Bar'].style[direction] = percentage * 100 + '%';
  101. };
  102. // define DPlayer events
  103. const eventTypes = ['play', 'pause', 'canplay', 'playing', 'ended', 'error'];
  104. this.event = {};
  105. for (let i = 0; i < eventTypes.length; i++) {
  106. this.event[eventTypes[i]] = [];
  107. }
  108. this.trigger = (type) => {
  109. for (let i = 0; i < this.event[type].length; i++) {
  110. this.event[type][i]();
  111. }
  112. };
  113. this.element = this.option.element;
  114. if (!this.option.danmaku) {
  115. this.element.classList.add('dplayer-no-danmaku');
  116. }
  117. if (isMobile) {
  118. this.element.classList.add('dplayer-mobile');
  119. }
  120. this.element.innerHTML = `
  121. <div class="dplayer-mask"></div>
  122. <div class="dplayer-video-wrap">
  123. <video class="dplayer-video" ${this.option.video.pic ? `poster="${this.option.video.pic}"` : ``} webkit-playsinline playsinline ${this.option.screenshot ? `crossorigin="anonymous"` : ``} preload="${this.option.preload}" src="${this.option.video.url}"></video>
  124. <div class="dplayer-danmaku">
  125. <div class="dplayer-danmaku-item dplayer-danmaku-item--demo"></div>
  126. </div>
  127. <div class="dplayer-bezel">
  128. <span class="dplayer-bezel-icon"></span>
  129. ${this.option.danmaku ? `<span class="dplayer-danloading">${getTran('Danmaku is loading')}</span>` : ``}
  130. <span class="diplayer-loading-icon">
  131. <svg height="100%" version="1.1" viewBox="0 0 22 22" width="100%">
  132. <svg x="7" y="1">
  133. <circle class="diplayer-loading-dot diplayer-loading-dot-0" cx="4" cy="4" r="2"></circle>
  134. </svg>
  135. <svg x="11" y="3">
  136. <circle class="diplayer-loading-dot diplayer-loading-dot-1" cx="4" cy="4" r="2"></circle>
  137. </svg>
  138. <svg x="13" y="7">
  139. <circle class="diplayer-loading-dot diplayer-loading-dot-2" cx="4" cy="4" r="2"></circle>
  140. </svg>
  141. <svg x="11" y="11">
  142. <circle class="diplayer-loading-dot diplayer-loading-dot-3" cx="4" cy="4" r="2"></circle>
  143. </svg>
  144. <svg x="7" y="13">
  145. <circle class="diplayer-loading-dot diplayer-loading-dot-4" cx="4" cy="4" r="2"></circle>
  146. </svg>
  147. <svg x="3" y="11">
  148. <circle class="diplayer-loading-dot diplayer-loading-dot-5" cx="4" cy="4" r="2"></circle>
  149. </svg>
  150. <svg x="1" y="7">
  151. <circle class="diplayer-loading-dot diplayer-loading-dot-6" cx="4" cy="4" r="2"></circle>
  152. </svg>
  153. <svg x="3" y="3">
  154. <circle class="diplayer-loading-dot diplayer-loading-dot-7" cx="4" cy="4" r="2"></circle>
  155. </svg>
  156. </svg>
  157. </span>
  158. </div>
  159. </div>
  160. <div class="dplayer-controller-mask"></div>
  161. <div class="dplayer-controller">
  162. <div class="dplayer-icons dplayer-icons-left">
  163. <button class="dplayer-icon dplayer-play-icon">`
  164. + this.getSVG('play')
  165. + ` </button>
  166. <div class="dplayer-volume">
  167. <button class="dplayer-icon dplayer-volume-icon">`
  168. + this.getSVG('volume-down')
  169. + ` </button>
  170. <div class="dplayer-volume-bar-wrap">
  171. <div class="dplayer-volume-bar">
  172. <div class="dplayer-volume-bar-inner" style="width: 70%; background: ${this.option.theme};">
  173. <span class="dplayer-thumb" style="background: ${this.option.theme}"></span>
  174. </div>
  175. </div>
  176. </div>
  177. </div>
  178. <span class="dplayer-time"><span class="dplayer-ptime">0:00</span> / <span class="dplayer-dtime">0:00</span></span>
  179. </div>
  180. <div class="dplayer-icons dplayer-icons-right">
  181. ${this.option.screenshot ? `
  182. <a href="#" class="dplayer-icon dplayer-camera-icon"}dplayer-volume>`
  183. + this.getSVG('camera')
  184. + ` </a>
  185. ` : ``}
  186. <div class="dplayer-comment">
  187. <button class="dplayer-icon dplayer-comment-icon">`
  188. + this.getSVG('comment')
  189. + ` </button>
  190. <div class="dplayer-comment-box">
  191. <button class="dplayer-icon dplayer-comment-setting-icon">`
  192. + this.getSVG('menu')
  193. + ` </button>
  194. <div class="dplayer-comment-setting-box">
  195. <div class="dplayer-comment-setting-color">
  196. <div class="dplayer-comment-setting-title">${getTran('Set danmaku color')}</div>
  197. <label>
  198. <input type="radio" name="dplayer-danmaku-color-${index}" value="#fff" checked>
  199. <span style="background: #fff; border: 1px solid rgba(0,0,0,.1);"></span>
  200. </label>
  201. <label>
  202. <input type="radio" name="dplayer-danmaku-color-${index}" value="#e54256">
  203. <span style="background: #e54256"></span>
  204. </label>
  205. <label>
  206. <input type="radio" name="dplayer-danmaku-color-${index}" value="#ffe133">
  207. <span style="background: #ffe133"></span>
  208. </label>
  209. <label>
  210. <input type="radio" name="dplayer-danmaku-color-${index}" value="#64DD17">
  211. <span style="background: #64DD17"></span>
  212. </label>
  213. <label>
  214. <input type="radio" name="dplayer-danmaku-color-${index}" value="#39ccff">
  215. <span style="background: #39ccff"></span>
  216. </label>
  217. <label>
  218. <input type="radio" name="dplayer-danmaku-color-${index}" value="#D500F9">
  219. <span style="background: #D500F9"></span>
  220. </label>
  221. </div>
  222. <div class="dplayer-comment-setting-type">
  223. <div class="dplayer-comment-setting-title">${getTran('Set danmaku type')}</div>
  224. <label>
  225. <input type="radio" name="dplayer-danmaku-type-${index}" value="top">
  226. <span>${getTran('Top')}</span>
  227. </label>
  228. <label>
  229. <input type="radio" name="dplayer-danmaku-type-${index}" value="right" checked>
  230. <span>${getTran('Rolling')}</span>
  231. </label>
  232. <label>
  233. <input type="radio" name="dplayer-danmaku-type-${index}" value="bottom">
  234. <span>${getTran('Bottom')}</span>
  235. </label>
  236. </div>
  237. </div>
  238. <input class="dplayer-comment-input" type="text" placeholder="${getTran('Input danmaku, hit Enter')}" maxlength="30">
  239. <button class="dplayer-icon dplayer-send-icon">`
  240. + this.getSVG('send')
  241. + ` </button>
  242. </div>
  243. </div>
  244. <div class="dplayer-setting">
  245. <button class="dplayer-icon dplayer-setting-icon">`
  246. + this.getSVG('setting')
  247. + ` </button>
  248. <div class="dplayer-setting-box"></div>
  249. </div>
  250. <div class="dplayer-full">
  251. <button class="dplayer-icon dplayer-full-in-icon">`
  252. + this.getSVG('full-in')
  253. + ` </button>
  254. <button class="dplayer-icon dplayer-full-icon">`
  255. + this.getSVG('full')
  256. + ` </button>
  257. </div>
  258. </div>
  259. <div class="dplayer-bar-wrap">
  260. <div class="dplayer-bar">
  261. <div class="dplayer-loaded" style="width: 0;"></div>
  262. <div class="dplayer-played" style="width: 0; background: ${this.option.theme}">
  263. <span class="dplayer-thumb" style="background: ${this.option.theme}"></span>
  264. </div>
  265. </div>
  266. </div>
  267. </div>
  268. <div class="dplayer-menu">
  269. <div class="dplayer-menu-item"><span class="dplayer-menu-label"><a target="_blank" href="http://diygod.me/">${getTran('About author')}</a></span></div>
  270. <div class="dplayer-menu-item"><span class="dplayer-menu-label"><a target="_blank" href="https://github.com/DIYgod/DPlayer/issues">${getTran('DPlayer feedback')}</a></span></div>
  271. <div class="dplayer-menu-item"><span class="dplayer-menu-label"><a target="_blank" href="https://github.com/DIYgod/DPlayer">${getTran('About DPlayer')}</a></span></div>
  272. </div>
  273. `;
  274. // arrow style
  275. var arrow = this.element.offsetWidth <= 500;
  276. if (arrow) {
  277. var arrowStyle = document.createElement('style');
  278. arrowStyle.innerHTML = `.dplayer .dplayer-danmaku{font-size:18px}`;
  279. document.head.appendChild(arrowStyle);
  280. }
  281. // get this video object
  282. this.video = this.element.getElementsByClassName('dplayer-video')[0];
  283. // Support HTTP Live Streaming
  284. let enablehls;
  285. if (this.option.video.type === 'auto') {
  286. enablehls = /m3u8(#|\?|$)/i.exec(this.option.video.url);
  287. }
  288. else if (this.option.video.type === 'hls') {
  289. enablehls = true;
  290. }
  291. else {
  292. enableflv = false;
  293. }
  294. if (enablehls && Hls.isSupported()) {
  295. // this.element.getElementsByClassName('dplayer-time')[0].style.display = 'none';
  296. const hls = new Hls();
  297. hls.attachMedia(this.video);
  298. hls.on(Hls.Events.MEDIA_ATTACHED, () => {
  299. hls.loadSource(this.option.video.url);
  300. hls.on(Hls.Events.MANIFEST_PARSED, function (event, data) {
  301. console.log("manifest loaded, found " + data.levels.length + " quality level");
  302. });
  303. });
  304. }
  305. // Support FLV
  306. let enableflv;
  307. if (this.option.video.type === 'auto') {
  308. enableflv = /.flv(#|\?|$)/i.exec(this.option.video.url);
  309. }
  310. else if (this.option.video.type === 'flv') {
  311. enableflv = true;
  312. }
  313. else {
  314. enableflv = false;
  315. }
  316. if (enableflv && flvjs.isSupported()) {
  317. const flvPlayer = flvjs.createPlayer({
  318. type: 'flv',
  319. url: this.option.video.url
  320. });
  321. flvPlayer.attachMediaElement(this.video);
  322. flvPlayer.load();
  323. }
  324. this.bezel = this.element.getElementsByClassName('dplayer-bezel-icon')[0];
  325. this.bezel.addEventListener('animationend', () => {
  326. this.bezel.classList.remove('dplayer-bezel-transition');
  327. });
  328. // play and pause button
  329. this.playButton = this.element.getElementsByClassName('dplayer-play-icon')[0];
  330. this.paused = true;
  331. this.playButton.addEventListener('click', () => {
  332. this.toggle();
  333. });
  334. const videoWrap = this.element.getElementsByClassName('dplayer-video-wrap')[0];
  335. const conMask = this.element.getElementsByClassName('dplayer-controller-mask')[0];
  336. if (!isMobile) {
  337. videoWrap.addEventListener('click', () => {
  338. this.toggle();
  339. });
  340. conMask.addEventListener('click', () => {
  341. this.toggle();
  342. });
  343. }
  344. else {
  345. const toggleController = () => {
  346. if (this.element.classList.contains('dplayer-hide-controller')) {
  347. this.element.classList.remove('dplayer-hide-controller');
  348. }
  349. else {
  350. this.element.classList.add('dplayer-hide-controller');
  351. }
  352. };
  353. videoWrap.addEventListener('click', toggleController);
  354. conMask.addEventListener('click', toggleController);
  355. }
  356. /**
  357. * Parse second to 00:00 format
  358. *
  359. * @param {Number} second
  360. * @return {String} 00:00 format
  361. */
  362. const secondToTime = (second) => {
  363. const add0 = (num) => {
  364. return num < 10 ? '0' + num : '' + num;
  365. };
  366. const min = parseInt(second / 60);
  367. const sec = parseInt(second - min * 60);
  368. return add0(min) + ':' + add0(sec);
  369. };
  370. /**
  371. * control play progress
  372. */
  373. // get element's view position
  374. const getElementViewLeft = (element) => {
  375. let actualLeft = element.offsetLeft;
  376. let current = element.offsetParent;
  377. let elementScrollLeft;
  378. if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement) {
  379. while (current !== null) {
  380. actualLeft += current.offsetLeft;
  381. current = current.offsetParent;
  382. }
  383. }
  384. else {
  385. while (current !== null && current !== this.element) {
  386. actualLeft += current.offsetLeft;
  387. current = current.offsetParent;
  388. }
  389. }
  390. elementScrollLeft = document.body.scrollLeft + document.documentElement.scrollLeft;
  391. return actualLeft - elementScrollLeft;
  392. };
  393. const getElementViewTop = (element) => {
  394. let actualTop = element.offsetTop;
  395. let current = element.offsetParent;
  396. let elementScrollTop;
  397. if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement) {
  398. while (current !== null) {
  399. actualTop += current.offsetTop;
  400. current = current.offsetParent;
  401. }
  402. }
  403. else {
  404. while (current !== null && current !== this.element) {
  405. actualTop += current.offsetTop;
  406. current = current.offsetParent;
  407. }
  408. }
  409. elementScrollTop = document.body.scrollTop + document.documentElement.scrollTop;
  410. return actualTop - elementScrollTop;
  411. };
  412. let bar = {};
  413. bar.playedBar = this.element.getElementsByClassName('dplayer-played')[0];
  414. bar.loadedBar = this.element.getElementsByClassName('dplayer-loaded')[0];
  415. const pbar = this.element.getElementsByClassName('dplayer-bar-wrap')[0];
  416. let barWidth;
  417. if (this.option.danmaku) {
  418. this.video.addEventListener('seeking', () => {
  419. for (let i = 0; i < this.dan.length; i++) {
  420. if (this.dan[i].time >= this.video.currentTime) {
  421. this.danIndex = i;
  422. return;
  423. }
  424. this.danIndex = this.dan.length;
  425. }
  426. });
  427. }
  428. let lastPlayPos = 0;
  429. let currentPlayPos = 0;
  430. let bufferingDetected = false;
  431. let danmakuTime;
  432. this.setTime = () => {
  433. this.playedTime = setInterval(() => {
  434. // whether the video is buffering
  435. currentPlayPos = this.video.currentTime;
  436. if (!bufferingDetected
  437. && currentPlayPos < (lastPlayPos + 0.01)
  438. && !this.video.paused) {
  439. this.element.classList.add('dplayer-loading');
  440. bufferingDetected = true;
  441. }
  442. if (bufferingDetected
  443. && currentPlayPos > (lastPlayPos + 0.01)
  444. && !this.video.paused) {
  445. this.element.classList.remove('dplayer-loading');
  446. bufferingDetected = false;
  447. }
  448. lastPlayPos = currentPlayPos;
  449. this.updateBar('played', this.video.currentTime / this.video.duration, 'width');
  450. this.element.getElementsByClassName('dplayer-ptime')[0].innerHTML = secondToTime(this.video.currentTime);
  451. this.trigger('playing');
  452. }, 100);
  453. if (this.option.danmaku && showdan) {
  454. danmakuTime = setInterval(() => {
  455. let item = this.dan[this.danIndex];
  456. while (item && this.video.currentTime > parseFloat(item.time)) {
  457. danmakuIn(item.text, item.color, item.type);
  458. item = this.dan[++this.danIndex];
  459. }
  460. }, 0);
  461. }
  462. };
  463. this.clearTime = () => {
  464. clearInterval(this.playedTime);
  465. if (this.option.danmaku) {
  466. clearInterval(danmakuTime);
  467. }
  468. };
  469. pbar.addEventListener('click', (event) => {
  470. const e = event || window.event;
  471. barWidth = pbar.clientWidth;
  472. let percentage = (e.clientX - getElementViewLeft(pbar)) / barWidth;
  473. percentage = percentage > 0 ? percentage : 0;
  474. percentage = percentage < 1 ? percentage : 1;
  475. this.updateBar('played', percentage, 'width');
  476. this.video.currentTime = parseFloat(bar.playedBar.style.width) / 100 * this.video.duration;
  477. });
  478. const thumbMove = (event) => {
  479. const e = event || window.event;
  480. let percentage = (e.clientX - getElementViewLeft(pbar)) / barWidth;
  481. percentage = percentage > 0 ? percentage : 0;
  482. percentage = percentage < 1 ? percentage : 1;
  483. this.updateBar('played', percentage, 'width');
  484. this.element.getElementsByClassName('dplayer-ptime')[0].innerHTML = secondToTime(percentage * this.video.duration);
  485. };
  486. const thumbUp = () => {
  487. document.removeEventListener('mouseup', thumbUp);
  488. document.removeEventListener('mousemove', thumbMove);
  489. this.video.currentTime = parseFloat(bar.playedBar.style.width) / 100 * this.video.duration;
  490. this.setTime();
  491. };
  492. pbar.addEventListener('mousedown', () => {
  493. barWidth = pbar.clientWidth;
  494. this.clearTime();
  495. document.addEventListener('mousemove', thumbMove);
  496. document.addEventListener('mouseup', thumbUp);
  497. });
  498. /**
  499. * control volume
  500. */
  501. bar.volumeBar = this.element.getElementsByClassName('dplayer-volume-bar-inner')[0];
  502. const volumeEle = this.element.getElementsByClassName('dplayer-volume')[0];
  503. const volumeBarWrapWrap = this.element.getElementsByClassName('dplayer-volume-bar-wrap')[0];
  504. const volumeBarWrap = this.element.getElementsByClassName('dplayer-volume-bar')[0];
  505. const volumeicon = this.element.getElementsByClassName('dplayer-volume-icon')[0];
  506. const vWidth = 35;
  507. this.switchVolumeIcon = () => {
  508. const volumeicon = this.element.getElementsByClassName('dplayer-volume-icon')[0];
  509. if (this.video.volume >= 0.8) {
  510. volumeicon.innerHTML = this.getSVG('volume-up');
  511. }
  512. else if (this.video.volume > 0) {
  513. volumeicon.innerHTML = this.getSVG('volume-down');
  514. }
  515. else {
  516. volumeicon.innerHTML = this.getSVG('volume-off');
  517. }
  518. };
  519. const volumeMove = (event) => {
  520. const e = event || window.event;
  521. let percentage = (e.clientX - getElementViewLeft(volumeBarWrap) - 5.5) / vWidth;
  522. this.volume(percentage);
  523. };
  524. const volumeUp = () => {
  525. document.removeEventListener('mouseup', volumeUp);
  526. document.removeEventListener('mousemove', volumeMove);
  527. volumeEle.classList.remove('dplayer-volume-active');
  528. };
  529. volumeBarWrapWrap.addEventListener('click', (event) => {
  530. const e = event || window.event;
  531. let percentage = (e.clientX - getElementViewLeft(volumeBarWrap) - 5.5) / vWidth;
  532. this.volume(percentage);
  533. });
  534. volumeBarWrapWrap.addEventListener('mousedown', () => {
  535. document.addEventListener('mousemove', volumeMove);
  536. document.addEventListener('mouseup', volumeUp);
  537. volumeEle.classList.add('dplayer-volume-active');
  538. });
  539. volumeicon.addEventListener('click', () => {
  540. if (this.video.muted) {
  541. this.video.muted = false;
  542. this.switchVolumeIcon();
  543. this.updateBar('volume', this.video.volume, 'width');
  544. }
  545. else {
  546. this.video.muted = true;
  547. volumeicon.innerHTML = this.getSVG('volume-off');
  548. this.updateBar('volume', 0, 'width');
  549. }
  550. });
  551. /**
  552. * auto hide controller
  553. */
  554. let hideTime = 0;
  555. if (!isMobile) {
  556. const hideController = () => {
  557. this.element.classList.remove('dplayer-hide-controller');
  558. clearTimeout(hideTime);
  559. hideTime = setTimeout(() => {
  560. if (this.video.played.length) {
  561. this.element.classList.add('dplayer-hide-controller');
  562. closeSetting();
  563. closeComment();
  564. }
  565. }, 2000);
  566. };
  567. this.element.addEventListener('mousemove', hideController);
  568. this.element.addEventListener('click', hideController);
  569. }
  570. /***
  571. * setting
  572. */
  573. let danOpacity = localStorage.getItem('DPlayer-opacity') || 0.7;
  574. const settingHTML = {
  575. 'original': `
  576. <div class="dplayer-setting-item dplayer-setting-speed">
  577. <span class="dplayer-label">${getTran('Speed')}</span>
  578. <div class="dplayer-toggle">`
  579. + this.getSVG('right')
  580. + ` </div>
  581. </div>
  582. <div class="dplayer-setting-item dplayer-setting-loop">
  583. <span class="dplayer-label">${getTran('Loop')}</span>
  584. <div class="dplayer-toggle">
  585. <input class="dplayer-toggle-setting-input" type="checkbox" name="dplayer-toggle">
  586. <label for="dplayer-toggle"></label>
  587. </div>
  588. </div>
  589. <div class="dplayer-setting-item dplayer-setting-showdan">
  590. <span class="dplayer-label">${getTran('Danmaku')}</span>
  591. <div class="dplayer-toggle">
  592. <input class="dplayer-showdan-setting-input" type="checkbox" name="dplayer-toggle-dan">
  593. <label for="dplayer-toggle-dan"></label>
  594. </div>
  595. </div>
  596. <div class="dplayer-setting-item dplayer-setting-danmaku">
  597. <span class="dplayer-label">${getTran('Opacity for danmaku')}</span>
  598. <div class="dplayer-danmaku-bar-wrap">
  599. <div class="dplayer-danmaku-bar">
  600. <div class="dplayer-danmaku-bar-inner" style="width: ${danOpacity * 100}%">
  601. <span class="dplayer-thumb"></span>
  602. </div>
  603. </div>
  604. </div>
  605. </div>`,
  606. 'speed': `
  607. <div class="dplayer-setting-speed-item" data-speed="0.5">
  608. <span class="dplayer-label">0.5</span>
  609. </div>
  610. <div class="dplayer-setting-speed-item" data-speed="0.75">
  611. <span class="dplayer-label">0.75</span>
  612. </div>
  613. <div class="dplayer-setting-speed-item" data-speed="1">
  614. <span class="dplayer-label">${getTran('Normal')}</span>
  615. </div>
  616. <div class="dplayer-setting-speed-item" data-speed="1.25">
  617. <span class="dplayer-label">1.25</span>
  618. </div>
  619. <div class="dplayer-setting-speed-item" data-speed="1.5">
  620. <span class="dplayer-label">1.5</span>
  621. </div>
  622. <div class="dplayer-setting-speed-item" data-speed="2">
  623. <span class="dplayer-label">2</span>
  624. </div>`
  625. };
  626. // toggle setting box
  627. const settingIcon = this.element.getElementsByClassName('dplayer-setting-icon')[0];
  628. const settingBox = this.element.getElementsByClassName('dplayer-setting-box')[0];
  629. const mask = this.element.getElementsByClassName('dplayer-mask')[0];
  630. settingBox.innerHTML = settingHTML.original;
  631. const closeSetting = () => {
  632. if (settingBox.classList.contains('dplayer-setting-box-open')) {
  633. settingBox.classList.remove('dplayer-setting-box-open');
  634. mask.classList.remove('dplayer-mask-show');
  635. setTimeout(() => {
  636. settingBox.classList.remove('dplayer-setting-box-narrow');
  637. settingBox.innerHTML = settingHTML.original;
  638. settingEvent();
  639. }, 300);
  640. }
  641. };
  642. const openSetting = () => {
  643. settingBox.classList.add('dplayer-setting-box-open');
  644. mask.classList.add('dplayer-mask-show');
  645. };
  646. mask.addEventListener('click', () => {
  647. closeSetting();
  648. });
  649. settingIcon.addEventListener('click', () => {
  650. openSetting();
  651. });
  652. let loop = this.option.loop;
  653. const danContainer = this.element.getElementsByClassName('dplayer-danmaku')[0];
  654. let showdan = true;
  655. const settingEvent = () => {
  656. // loop control
  657. const loopEle = this.element.getElementsByClassName('dplayer-setting-loop')[0];
  658. const loopToggle = loopEle.getElementsByClassName('dplayer-toggle-setting-input')[0];
  659. loopToggle.checked = loop;
  660. loopEle.addEventListener('click', () => {
  661. loopToggle.checked = !loopToggle.checked;
  662. if (loopToggle.checked) {
  663. loop = true;
  664. this.video.loop = loop;
  665. }
  666. else {
  667. loop = false;
  668. this.video.loop = loop;
  669. }
  670. closeSetting();
  671. });
  672. // show danmaku control
  673. const showDanEle = this.element.getElementsByClassName('dplayer-setting-showdan')[0];
  674. const showDanToggle = showDanEle.getElementsByClassName('dplayer-showdan-setting-input')[0];
  675. showDanToggle.checked = showdan;
  676. showDanEle.addEventListener('click', () => {
  677. showDanToggle.checked = !showDanToggle.checked;
  678. if (showDanToggle.checked) {
  679. showdan = true;
  680. if (this.option.danmaku) {
  681. for (let i = 0; i < this.dan.length; i++) {
  682. if (this.dan[i].time >= this.video.currentTime) {
  683. this.danIndex = i;
  684. break;
  685. }
  686. this.danIndex = this.dan.length;
  687. }
  688. danmakuTime = setInterval(() => {
  689. let item = this.dan[this.danIndex];
  690. while (item && this.video.currentTime >= parseFloat(item.time)) {
  691. danmakuIn(item.text, item.color, item.type);
  692. item = this.dan[++this.danIndex];
  693. }
  694. }, 0);
  695. }
  696. }
  697. else {
  698. showdan = false;
  699. if (this.option.danmaku) {
  700. clearInterval(danmakuTime);
  701. danContainer.innerHTML = `<div class="dplayer-danmaku-item dplayer-danmaku-item--demo"></div>`;
  702. this.danTunnel = {
  703. right: {},
  704. top: {},
  705. bottom: {}
  706. };
  707. this.itemDemo = this.element.getElementsByClassName('dplayer-danmaku-item')[0];
  708. }
  709. }
  710. closeSetting();
  711. });
  712. // speed control
  713. const speedEle = this.element.getElementsByClassName('dplayer-setting-speed')[0];
  714. speedEle.addEventListener('click', () => {
  715. settingBox.classList.add('dplayer-setting-box-narrow');
  716. settingBox.innerHTML = settingHTML.speed;
  717. const speedItem = settingBox.getElementsByClassName('dplayer-setting-speed-item');
  718. for (let i = 0; i < speedItem.length; i++) {
  719. speedItem[i].addEventListener('click', () => {
  720. this.video.playbackRate = speedItem[i].dataset.speed;
  721. closeSetting();
  722. });
  723. }
  724. });
  725. if (this.option.danmaku) {
  726. // danmaku opacity
  727. bar.danmakuBar = this.element.getElementsByClassName('dplayer-danmaku-bar-inner')[0];
  728. const danmakuBarWrapWrap = this.element.getElementsByClassName('dplayer-danmaku-bar-wrap')[0];
  729. const danmakuBarWrap = this.element.getElementsByClassName('dplayer-danmaku-bar')[0];
  730. const danmakuSettingBox = this.element.getElementsByClassName('dplayer-setting-danmaku')[0];
  731. const dWidth = 130;
  732. this.updateBar('danmaku', danOpacity, 'width');
  733. const danmakuMove = (event) => {
  734. const e = event || window.event;
  735. let percentage = (e.clientX - getElementViewLeft(danmakuBarWrap)) / dWidth;
  736. percentage = percentage > 0 ? percentage : 0;
  737. percentage = percentage < 1 ? percentage : 1;
  738. this.updateBar('danmaku', percentage, 'width');
  739. const items = this.element.getElementsByClassName('dplayer-danmaku-item');
  740. for (let i = 0; i < items.length; i++) {
  741. items[i].style.opacity = percentage;
  742. }
  743. danOpacity = percentage;
  744. localStorage.setItem('DPlayer-opacity', danOpacity);
  745. };
  746. const danmakuUp = () => {
  747. document.removeEventListener('mouseup', danmakuUp);
  748. document.removeEventListener('mousemove', danmakuMove);
  749. danmakuSettingBox.classList.remove('dplayer-setting-danmaku-active');
  750. };
  751. danmakuBarWrapWrap.addEventListener('click', (event) => {
  752. const e = event || window.event;
  753. let percentage = (e.clientX - getElementViewLeft(danmakuBarWrap)) / dWidth;
  754. percentage = percentage > 0 ? percentage : 0;
  755. percentage = percentage < 1 ? percentage : 1;
  756. this.updateBar('danmaku', percentage, 'width');
  757. const items = this.element.getElementsByClassName('dplayer-danmaku-item');
  758. for (let i = 0; i < items.length; i++) {
  759. items[i].style.opacity = percentage;
  760. }
  761. danOpacity = percentage;
  762. localStorage.setItem('DPlayer-opacity', danOpacity);
  763. });
  764. danmakuBarWrapWrap.addEventListener('mousedown', () => {
  765. document.addEventListener('mousemove', danmakuMove);
  766. document.addEventListener('mouseup', danmakuUp);
  767. danmakuSettingBox.classList.add('dplayer-setting-danmaku-active');
  768. });
  769. }
  770. };
  771. settingEvent();
  772. /**
  773. * video events
  774. */
  775. // show video time: the metadata has loaded or changed
  776. this.video.addEventListener('durationchange', () => {
  777. if (this.video.duration !== 1) { // compatibility: Android browsers will output 1 at first
  778. this.element.getElementsByClassName('dplayer-dtime')[0].innerHTML = secondToTime(this.video.duration);
  779. }
  780. });
  781. // show video loaded bar: to inform interested parties of progress downloading the media
  782. this.video.addEventListener('progress', () => {
  783. const percentage = this.video.buffered.length ? this.video.buffered.end(this.video.buffered.length - 1) / this.video.duration : 0;
  784. this.updateBar('loaded', percentage, 'width');
  785. });
  786. // video download error: an error occurs
  787. this.video.addEventListener('error', () => {
  788. this.element.getElementsByClassName('dplayer-ptime')[0].innerHTML = `Error happens ╥﹏╥`;
  789. this.trigger('pause');
  790. });
  791. // video can play: enough data is available that the media can be played
  792. this.video.addEventListener('canplay', () => {
  793. this.trigger('canplay');
  794. });
  795. // music end
  796. this.ended = false;
  797. this.video.addEventListener('ended', () => {
  798. this.updateBar('played', 1, 'width');
  799. if (!loop) {
  800. this.ended = true;
  801. this.pause();
  802. this.trigger('ended');
  803. }
  804. });
  805. this.video.addEventListener('play', () => {
  806. if (this.paused) {
  807. this.play();
  808. }
  809. });
  810. this.video.addEventListener('pause', () => {
  811. if (!this.paused) {
  812. this.pause();
  813. }
  814. });
  815. // control volume
  816. this.video.volume = parseInt(this.element.getElementsByClassName('dplayer-volume-bar-inner')[0].style.width) / 100;
  817. // loop
  818. this.video.loop = loop;
  819. // set duration time
  820. if (this.video.duration !== 1) { // compatibility: Android browsers will output 1 at first
  821. this.element.getElementsByClassName('dplayer-dtime')[0].innerHTML = this.video.duration ? secondToTime(this.video.duration) : '00:00';
  822. }
  823. /**
  824. * danmaku display
  825. */
  826. const itemHeight = arrow ? 24 : 30;
  827. let danWidth;
  828. let danHeight;
  829. let itemY;
  830. this.danTunnel = {
  831. right: {},
  832. top: {},
  833. bottom: {}
  834. };
  835. const danItemRight = (ele) => {
  836. return danContainer.getBoundingClientRect().right - ele.getBoundingClientRect().right;
  837. };
  838. const danSpeed = (width) => {
  839. return (danWidth + width) / 5;
  840. };
  841. const getTunnel = (ele, type, width) => {
  842. const tmp = danWidth / danSpeed(width);
  843. for (let i = 0; ; i++) {
  844. let item = this.danTunnel[type][i + ''];
  845. if (item && item.length) {
  846. for (let j = 0; j < item.length; j++) {
  847. const danRight = danItemRight(item[j]) - 10;
  848. if (danRight <= danWidth - (tmp * danSpeed(item[j].offsetWidth)) || danRight <= 0) {
  849. break;
  850. }
  851. if (j === item.length - 1) {
  852. this.danTunnel[type][i + ''].push(ele);
  853. ele.addEventListener('animationend', () => {
  854. this.danTunnel[type][i + ''].splice(0, 1);
  855. });
  856. return i % itemY;
  857. }
  858. }
  859. }
  860. else {
  861. this.danTunnel[type][i + ''] = [ele];
  862. ele.addEventListener('animationend', () => {
  863. this.danTunnel[type][i + ''].splice(0, 1);
  864. });
  865. return i % itemY;
  866. }
  867. }
  868. };
  869. this.itemDemo = this.element.getElementsByClassName('dplayer-danmaku-item')[0];
  870. const danmakuIn = (text, color, type) => {
  871. if (!type) {
  872. type = 'right';
  873. }
  874. danWidth = danContainer.offsetWidth;
  875. danHeight = danContainer.offsetHeight;
  876. itemY = parseInt(danHeight / itemHeight);
  877. let item = document.createElement(`div`);
  878. item.classList.add(`dplayer-danmaku-item`);
  879. item.classList.add(`dplayer-danmaku-${type}`);
  880. item.innerHTML = text;
  881. item.style.opacity = danOpacity;
  882. item.style.color = color;
  883. item.addEventListener('animationend', () => {
  884. danContainer.removeChild(item);
  885. });
  886. // measure
  887. this.itemDemo.innerHTML = text;
  888. let itemWidth = this.itemDemo.offsetWidth;
  889. // adjust
  890. switch (type) {
  891. case 'right':
  892. item.style.top = itemHeight * getTunnel(item, type, itemWidth) + 'px';
  893. item.style.width = (itemWidth + 1) + 'px';
  894. item.style.transform = `translateX(-${danWidth}px)`;
  895. break;
  896. case 'top':
  897. item.style.top = itemHeight * getTunnel(item, type) + 'px';
  898. break;
  899. case 'bottom':
  900. item.style.bottom = itemHeight * getTunnel(item, type) + 'px';
  901. break;
  902. default:
  903. console.error(`Can't handled danmaku type: ${type}`);
  904. }
  905. // insert
  906. danContainer.appendChild(item);
  907. // move
  908. item.classList.add(`dplayer-danmaku-move`);
  909. return item;
  910. };
  911. // danmaku
  912. if (this.option.danmaku) {
  913. this.danIndex = 0;
  914. this.readDanmaku();
  915. }
  916. else {
  917. // autoplay
  918. if (this.option.autoplay && !isMobile) {
  919. this.play();
  920. }
  921. else if (isMobile) {
  922. this.pause();
  923. }
  924. }
  925. /**
  926. * comment
  927. */
  928. const commentInput = this.element.getElementsByClassName('dplayer-comment-input')[0];
  929. const commentIcon = this.element.getElementsByClassName('dplayer-comment-icon')[0];
  930. const commentBox = this.element.getElementsByClassName('dplayer-comment-box')[0];
  931. const commentSettingIcon = this.element.getElementsByClassName('dplayer-comment-setting-icon')[0];
  932. const commentSettingBox = this.element.getElementsByClassName('dplayer-comment-setting-box')[0];
  933. const commentSendIcon = this.element.getElementsByClassName('dplayer-send-icon')[0];
  934. const htmlEncode = (str) => {
  935. return str.replace(/&/g, "&amp;")
  936. .replace(/</g, "&lt;")
  937. .replace(/>/g, "&gt;")
  938. .replace(/"/g, "&quot;")
  939. .replace(/'/g, "&#x27;")
  940. .replace(/\//g, "&#x2f;");
  941. };
  942. const sendComment = () => {
  943. commentInput.blur();
  944. // text can't be empty
  945. if (!commentInput.value.replace(/^\s+|\s+$/g, '')) {
  946. alert(getTran('Please input danmaku!'));
  947. return;
  948. }
  949. const danmakuData = {
  950. token: this.option.danmaku.token,
  951. player: this.option.danmaku.id,
  952. author: 'DIYgod',
  953. time: this.video.currentTime,
  954. text: commentInput.value,
  955. color: this.element.querySelector('.dplayer-comment-setting-color input:checked').value,
  956. type: this.element.querySelector('.dplayer-comment-setting-type input:checked').value
  957. };
  958. this.option.apiBackend.send(this.option.danmaku.api, danmakuData);
  959. commentInput.value = '';
  960. closeComment();
  961. this.dan.splice(this.danIndex, 0, danmakuData);
  962. this.danIndex++;
  963. const item = danmakuIn(htmlEncode(danmakuData.text), danmakuData.color, danmakuData.type);
  964. item.style.border = `2px solid ${this.option.theme}`;
  965. };
  966. const closeCommentSetting = () => {
  967. if (commentSettingBox.classList.contains('dplayer-comment-setting-open')) {
  968. commentSettingBox.classList.remove('dplayer-comment-setting-open');
  969. }
  970. };
  971. const toggleCommentSetting = () => {
  972. if (commentSettingBox.classList.contains('dplayer-comment-setting-open')) {
  973. commentSettingBox.classList.remove('dplayer-comment-setting-open');
  974. }
  975. else {
  976. commentSettingBox.classList.add('dplayer-comment-setting-open');
  977. }
  978. };
  979. let disableHide = 0;
  980. const closeComment = () => {
  981. if (commentBox.classList.contains('dplayer-comment-box-open')) {
  982. commentBox.classList.remove('dplayer-comment-box-open');
  983. mask.classList.remove('dplayer-mask-show');
  984. clearInterval(disableHide);
  985. this.element.classList.remove('dplayer-show-controller');
  986. closeCommentSetting();
  987. }
  988. };
  989. const openComment = () => {
  990. commentBox.classList.add('dplayer-comment-box-open');
  991. mask.classList.add('dplayer-mask-show');
  992. disableHide = setInterval(() => {
  993. clearTimeout(hideTime);
  994. }, 1000);
  995. this.element.classList.add('dplayer-show-controller');
  996. };
  997. mask.addEventListener('click', () => {
  998. closeComment();
  999. });
  1000. commentIcon.addEventListener('click', () => {
  1001. openComment();
  1002. setTimeout(() => {
  1003. commentInput.focus();
  1004. }, 300);
  1005. });
  1006. commentSettingIcon.addEventListener('click', () => {
  1007. toggleCommentSetting();
  1008. });
  1009. // comment setting box
  1010. this.element.getElementsByClassName('dplayer-comment-setting-color')[0].addEventListener('click', () => {
  1011. const sele = this.element.querySelector('input[name="dplayer-danmaku-color-${index}"]:checked+span');
  1012. if (sele) {
  1013. commentSettingIcon.getElementsByClassName('dplayer-fill')[0].style.fill = this.element.querySelector('input[name="dplayer-danmaku-color-${index}"]:checked').value;
  1014. }
  1015. });
  1016. commentInput.addEventListener('click', () => {
  1017. closeCommentSetting();
  1018. });
  1019. commentInput.addEventListener('keydown', (e) => {
  1020. const event = e || window.event;
  1021. if (event.keyCode === 13) {
  1022. sendComment();
  1023. }
  1024. });
  1025. commentSendIcon.addEventListener('click', sendComment);
  1026. /**
  1027. * full screen
  1028. */
  1029. const resetAnimation = () => {
  1030. danWidth = danContainer.offsetWidth;
  1031. const items = this.element.getElementsByClassName('dplayer-danmaku-item');
  1032. for (let i = 0; i < items.length; i++) {
  1033. items[i].style.transform = `translateX(-${danWidth}px)`;
  1034. }
  1035. };
  1036. this.element.addEventListener('fullscreenchange', () => {
  1037. resetAnimation();
  1038. });
  1039. this.element.addEventListener('mozfullscreenchange', () => {
  1040. resetAnimation();
  1041. });
  1042. this.element.addEventListener('webkitfullscreenchange', () => {
  1043. resetAnimation();
  1044. });
  1045. // browser full screen
  1046. this.element.getElementsByClassName('dplayer-full-icon')[0].addEventListener('click', () => {
  1047. if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement) {
  1048. if (this.element.requestFullscreen) {
  1049. this.element.requestFullscreen();
  1050. }
  1051. else if (this.element.mozRequestFullScreen) {
  1052. this.element.mozRequestFullScreen();
  1053. }
  1054. else if (this.element.webkitRequestFullscreen) {
  1055. this.element.webkitRequestFullscreen();
  1056. }
  1057. else if (this.video.webkitEnterFullscreen) { // Safari for iOS
  1058. this.video.webkitEnterFullscreen();
  1059. }
  1060. }
  1061. else {
  1062. if (document.cancelFullScreen) {
  1063. document.cancelFullScreen();
  1064. }
  1065. else if (document.mozCancelFullScreen) {
  1066. document.mozCancelFullScreen();
  1067. }
  1068. else if (document.webkitCancelFullScreen) {
  1069. document.webkitCancelFullScreen();
  1070. }
  1071. }
  1072. resetAnimation();
  1073. });
  1074. // web full screen
  1075. this.element.getElementsByClassName('dplayer-full-in-icon')[0].addEventListener('click', () => {
  1076. if (this.element.classList.contains('dplayer-fulled')) {
  1077. this.element.classList.remove('dplayer-fulled');
  1078. }
  1079. else {
  1080. this.element.classList.add('dplayer-fulled');
  1081. resetAnimation();
  1082. }
  1083. });
  1084. /**
  1085. * hot key
  1086. */
  1087. const handleKeyDown = (e) => {
  1088. const tag = document.activeElement.tagName.toUpperCase();
  1089. const editable = document.activeElement.getAttribute('contenteditable');
  1090. if (tag !== 'INPUT' && tag !== 'TEXTAREA' && editable !== '' && editable !== 'true') {
  1091. const event = e || window.event;
  1092. let percentage;
  1093. switch (event.keyCode) {
  1094. case 32:
  1095. event.preventDefault();
  1096. this.toggle();
  1097. break;
  1098. case 37:
  1099. event.preventDefault();
  1100. this.video.currentTime = this.video.currentTime - 5;
  1101. break;
  1102. case 39:
  1103. event.preventDefault();
  1104. this.video.currentTime = this.video.currentTime + 5;
  1105. break;
  1106. case 38:
  1107. event.preventDefault();
  1108. percentage = this.video.volume + 0.1;
  1109. this.volume(percentage);
  1110. break;
  1111. case 40:
  1112. event.preventDefault();
  1113. percentage = this.video.volume - 0.1;
  1114. this.volume(percentage);
  1115. break;
  1116. }
  1117. }
  1118. };
  1119. if (this.option.hotkey) {
  1120. document.addEventListener('keydown', handleKeyDown);
  1121. }
  1122. document.addEventListener('keydown', (e) => { // Press ESC to quit web full screen
  1123. const event = e || window.event;
  1124. switch (event.keyCode) {
  1125. case 27:
  1126. if (this.element.classList.contains('dplayer-fulled')) {
  1127. this.element.classList.remove('dplayer-fulled');
  1128. resetAnimation();
  1129. }
  1130. break;
  1131. }
  1132. });
  1133. /**
  1134. * right key
  1135. */
  1136. const menu = this.element.getElementsByClassName('dplayer-menu')[0];
  1137. this.element.addEventListener('contextmenu', (e) => {
  1138. const event = e || window.event;
  1139. event.preventDefault();
  1140. menu.classList.add('dplayer-menu-show');
  1141. const clientRect = this.element.getBoundingClientRect();
  1142. const menuLeft = event.clientX - clientRect.left;
  1143. const menuTop = event.clientY - clientRect.top;
  1144. if (menuLeft + menu.offsetWidth >= clientRect.width) {
  1145. menu.style.right = clientRect.width - menuLeft + 'px';
  1146. menu.style.left = 'initial';
  1147. }
  1148. else {
  1149. menu.style.left = event.clientX - this.element.getBoundingClientRect().left + 'px';
  1150. menu.style.right = 'initial';
  1151. }
  1152. if (menuTop + menu.offsetHeight >= clientRect.height) {
  1153. menu.style.bottom = clientRect.height - menuTop + 'px';
  1154. menu.style.top = 'initial';
  1155. }
  1156. else {
  1157. menu.style.top = event.clientY - this.element.getBoundingClientRect().top + 'px';
  1158. menu.style.bottom = 'initial';
  1159. }
  1160. mask.classList.add('dplayer-mask-show');
  1161. mask.addEventListener('click', () => {
  1162. mask.classList.remove('dplayer-mask-show');
  1163. menu.classList.remove('dplayer-menu-show');
  1164. });
  1165. });
  1166. /**
  1167. * Screenshot
  1168. */
  1169. if (this.option.screenshot) {
  1170. const camareIcon = this.element.getElementsByClassName('dplayer-camera-icon')[0];
  1171. camareIcon.addEventListener('click', () => {
  1172. const canvas = document.createElement("canvas");
  1173. canvas.width = this.video.videoWidth;
  1174. canvas.height = this.video.videoHeight;
  1175. canvas.getContext('2d').drawImage(this.video, 0, 0, canvas.width, canvas.height);
  1176. camareIcon.href = canvas.toDataURL();
  1177. camareIcon.download = "DPlayer.png";
  1178. });
  1179. }
  1180. index++;
  1181. }
  1182. /**
  1183. * Play music
  1184. */
  1185. play(time) {
  1186. if (Object.prototype.toString.call(time) === '[object Number]') {
  1187. this.video.currentTime = time;
  1188. }
  1189. this.paused = false;
  1190. if (this.video.paused) {
  1191. this.bezel.innerHTML = this.getSVG('play');
  1192. this.bezel.classList.add('dplayer-bezel-transition');
  1193. }
  1194. this.playButton.innerHTML = this.getSVG('pause');
  1195. this.video.play();
  1196. if (this.playedTime) {
  1197. this.clearTime();
  1198. }
  1199. this.setTime();
  1200. this.element.classList.add('dplayer-playing');
  1201. this.trigger('play');
  1202. }
  1203. /**
  1204. * Pause music
  1205. */
  1206. pause() {
  1207. this.paused = true;
  1208. this.element.classList.remove('dplayer-loading');
  1209. if (!this.video.paused) {
  1210. this.bezel.innerHTML = this.getSVG('pause');
  1211. this.bezel.classList.add('dplayer-bezel-transition');
  1212. }
  1213. this.ended = false;
  1214. this.playButton.innerHTML = this.getSVG('play');
  1215. this.video.pause();
  1216. this.clearTime();
  1217. this.element.classList.remove('dplayer-playing');
  1218. this.trigger('pause');
  1219. }
  1220. /**
  1221. * Set volume
  1222. */
  1223. volume(percentage) {
  1224. percentage = percentage > 0 ? percentage : 0;
  1225. percentage = percentage < 1 ? percentage : 1;
  1226. this.updateBar('volume', percentage, 'width');
  1227. this.video.volume = percentage;
  1228. if (this.video.muted) {
  1229. this.video.muted = false;
  1230. }
  1231. this.switchVolumeIcon();
  1232. }
  1233. /**
  1234. * Toggle between play and pause
  1235. */
  1236. toggle() {
  1237. if (this.video.paused) {
  1238. this.play();
  1239. }
  1240. else {
  1241. this.pause();
  1242. }
  1243. }
  1244. /**
  1245. * attach event
  1246. */
  1247. on(name, func) {
  1248. if (typeof func === 'function') {
  1249. this.event[name].push(func);
  1250. }
  1251. }
  1252. /**
  1253. * Asynchronously read danmaku from all API endpoints
  1254. */
  1255. _readAllEndpoints (endpoints, finish) {
  1256. let results = [];
  1257. let readCount = 0;
  1258. let cbk = (i) => (err, data) => {
  1259. ++readCount;
  1260. if (err) {
  1261. if (err.response)
  1262. alert(err.response.msg);
  1263. else
  1264. console.log('Request was unsuccessful: ' + err.status);
  1265. results[i] = [];
  1266. }
  1267. else {
  1268. results[i] = data;
  1269. }
  1270. if (readCount == endpoints.length) {
  1271. return finish(results);
  1272. }
  1273. };
  1274. for (let i = 0; i < endpoints.length; ++i) {
  1275. this.option.apiBackend.read(endpoints[i], cbk(i));
  1276. }
  1277. }
  1278. /**
  1279. * Read danmaku from API
  1280. */
  1281. readDanmaku() {
  1282. const isMobile = /mobile/i.test(window.navigator.userAgent);
  1283. let apiurl;
  1284. if (this.option.danmaku.maximum) {
  1285. apiurl = `${this.option.danmaku.api}?id=${this.option.danmaku.id}&max=${this.option.danmaku.maximum}`;
  1286. }
  1287. else {
  1288. apiurl = `${this.option.danmaku.api}?id=${this.option.danmaku.id}`;
  1289. }
  1290. let endpoints = (this.option.danmaku.addition || []).slice(0);
  1291. endpoints.push(apiurl);
  1292. this._readAllEndpoints(endpoints, (results) => {
  1293. this.danIndex = 0;
  1294. this.dan = [].concat.apply([], results).sort((a, b) => a.time - b.time);
  1295. this.element.getElementsByClassName('dplayer-danloading')[0].style.display = 'none';
  1296. // autoplay
  1297. if (this.option.autoplay && !isMobile) {
  1298. this.play();
  1299. }
  1300. else if (isMobile) {
  1301. this.pause();
  1302. }
  1303. });
  1304. }
  1305. /**
  1306. * Switch to a new video
  1307. *
  1308. * @param {Object} video - new video info
  1309. * @param {Object} danmaku - new danmaku info
  1310. */
  1311. switchVideo(video, danmaku) {
  1312. this.video.src = video.url;
  1313. this.video.poster = video.pic ? video.pic : '';
  1314. this.video.currentTime = 0;
  1315. this.pause();
  1316. if (danmaku) {
  1317. this.dan = [];
  1318. this.danIndex = 0;
  1319. this.element.getElementsByClassName('dplayer-danloading')[0].style.display = 'block';
  1320. this.updateBar('played', 0, 'width');
  1321. this.updateBar('loaded', 0, 'width');
  1322. this.element.getElementsByClassName('dplayer-ptime')[0].innerHTML = '00:00';
  1323. this.element.getElementsByClassName('dplayer-danmaku')[0].innerHTML = `<div class="dplayer-danmaku-item dplayer-danmaku-item--demo"></div>`;
  1324. this.danTunnel = {
  1325. right: {},
  1326. top: {},
  1327. bottom: {}
  1328. };
  1329. this.itemDemo = this.element.getElementsByClassName('dplayer-danmaku-item')[0];
  1330. this.option.danmaku = danmaku;
  1331. this.readDanmaku();
  1332. }
  1333. }
  1334. }
  1335. module.exports = DPlayer;