From 4da91fb9728048a62acd7b84afa28962e54f90e7 Mon Sep 17 00:00:00 2001 From: Astound Date: Mon, 22 Jan 2024 12:10:13 +0800 Subject: update plyr --- youtube/static/modules/plyr/plyr.css | 2 +- youtube/static/modules/plyr/plyr.js | 10133 ++++++++++++-------------- youtube/static/modules/plyr/plyr.min.js | 4 +- youtube/static/modules/plyr/plyr.min.js.map | 1 + youtube/templates/watch.html | 2 +- 5 files changed, 4820 insertions(+), 5322 deletions(-) create mode 100644 youtube/static/modules/plyr/plyr.min.js.map diff --git a/youtube/static/modules/plyr/plyr.css b/youtube/static/modules/plyr/plyr.css index 7d98f20..fdc1e26 100644 --- a/youtube/static/modules/plyr/plyr.css +++ b/youtube/static/modules/plyr/plyr.css @@ -1 +1 @@ -@keyframes plyr-progress{to{background-position:25px 0;background-position:var(--plyr-progress-loading-size,25px) 0}}@keyframes plyr-popup{0%{opacity:.5;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@keyframes plyr-fade-in{from{opacity:0}to{opacity:1}}.plyr{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;align-items:center;direction:ltr;display:flex;flex-direction:column;font-family:inherit;font-family:var(--plyr-font-family,inherit);font-variant-numeric:tabular-nums;font-weight:400;font-weight:var(--plyr-font-weight-regular,400);height:100%;line-height:1.7;line-height:var(--plyr-line-height,1.7);max-width:100%;min-width:200px;position:relative;text-shadow:none;transition:box-shadow .3s ease;z-index:0}.plyr audio,.plyr iframe,.plyr video{display:block;height:100%;width:100%}.plyr button{font:inherit;line-height:inherit;width:auto}.plyr:focus{outline:0}.plyr--full-ui{box-sizing:border-box}.plyr--full-ui *,.plyr--full-ui ::after,.plyr--full-ui ::before{box-sizing:inherit}.plyr--full-ui a,.plyr--full-ui button,.plyr--full-ui input,.plyr--full-ui label{touch-action:manipulation}.plyr__badge{background:#4a5464;background:var(--plyr-badge-background,#4a5464);border-radius:2px;border-radius:var(--plyr-badge-border-radius,2px);color:#fff;color:var(--plyr-badge-text-color,#fff);font-size:9px;font-size:var(--plyr-font-size-badge,9px);line-height:1;padding:3px 4px}.plyr--full-ui ::-webkit-media-text-track-container{display:none}.plyr__captions{animation:plyr-fade-in .3s ease;bottom:0;display:none;font-size:13px;font-size:var(--plyr-font-size-small,13px);left:0;padding:10px;padding:var(--plyr-control-spacing,10px);position:absolute;text-align:center;transition:transform .4s ease-in-out;width:100%}.plyr__captions span:empty{display:none}@media (min-width:480px){.plyr__captions{font-size:15px;font-size:var(--plyr-font-size-base,15px);padding:calc(10px * 2);padding:calc(var(--plyr-control-spacing,10px) * 2)}}@media (min-width:768px){.plyr__captions{font-size:18px;font-size:var(--plyr-font-size-large,18px)}}.plyr--captions-active .plyr__captions{display:block}.plyr:not(.plyr--hide-controls) .plyr__controls:not(:empty)~.plyr__captions{transform:translateY(calc(10px * -4));transform:translateY(calc(var(--plyr-control-spacing,10px) * -4))}.plyr__caption{background:rgba(0,0,0,.8);background:var(--plyr-captions-background,rgba(0,0,0,.8));border-radius:2px;-webkit-box-decoration-break:clone;box-decoration-break:clone;color:#fff;color:var(--plyr-captions-text-color,#fff);line-height:185%;padding:.2em .5em;white-space:pre-wrap}.plyr__caption div{display:inline}.plyr__control{background:0 0;border:0;border-radius:3px;border-radius:var(--plyr-control-radius,3px);color:inherit;cursor:pointer;flex-shrink:0;overflow:visible;padding:calc(10px * .7);padding:calc(var(--plyr-control-spacing,10px) * .7);position:relative;transition:all .3s ease}.plyr__control svg{display:block;fill:currentColor;height:18px;height:var(--plyr-control-icon-size,18px);pointer-events:none;width:18px;width:var(--plyr-control-icon-size,18px)}.plyr__control:focus{outline:0}.plyr__control.plyr__tab-focus{outline-color:#00b3ff;outline-color:var(--plyr-tab-focus-color,var(--plyr-color-main,var(--plyr-color-main,#00b3ff)));outline-offset:2px;outline-style:dotted;outline-width:3px}a.plyr__control{text-decoration:none}a.plyr__control::after,a.plyr__control::before{display:none}.plyr__control.plyr__control--pressed .icon--not-pressed,.plyr__control.plyr__control--pressed .label--not-pressed,.plyr__control:not(.plyr__control--pressed) .icon--pressed,.plyr__control:not(.plyr__control--pressed) .label--pressed{display:none}.plyr--full-ui ::-webkit-media-controls{display:none}.plyr__controls{align-items:center;display:flex;justify-content:flex-end;text-align:center}.plyr__controls .plyr__progress__container{flex:1;min-width:0}.plyr__controls .plyr__controls__item{margin-left:calc(10px / 4);margin-left:calc(var(--plyr-control-spacing,10px)/ 4)}.plyr__controls .plyr__controls__item:first-child{margin-left:0;margin-right:auto}.plyr__controls .plyr__controls__item.plyr__progress__container{padding-left:calc(10px / 4);padding-left:calc(var(--plyr-control-spacing,10px)/ 4)}.plyr__controls .plyr__controls__item.plyr__time{padding:0 calc(10px / 2);padding:0 calc(var(--plyr-control-spacing,10px)/ 2)}.plyr__controls .plyr__controls__item.plyr__progress__container:first-child,.plyr__controls .plyr__controls__item.plyr__time+.plyr__time,.plyr__controls .plyr__controls__item.plyr__time:first-child{padding-left:0}.plyr__controls:empty{display:none}.plyr [data-plyr=airplay],.plyr [data-plyr=captions],.plyr [data-plyr=fullscreen],.plyr [data-plyr=pip]{display:none}.plyr--airplay-supported [data-plyr=airplay],.plyr--captions-enabled [data-plyr=captions],.plyr--fullscreen-enabled [data-plyr=fullscreen],.plyr--pip-supported [data-plyr=pip]{display:inline-block}.plyr__menu{display:flex;position:relative}.plyr__menu .plyr__control svg{transition:transform .3s ease}.plyr__menu .plyr__control[aria-expanded=true] svg{transform:rotate(90deg)}.plyr__menu .plyr__control[aria-expanded=true] .plyr__tooltip{display:none}.plyr__menu__container{animation:plyr-popup .2s ease;background:rgba(255,255,255,.9);background:var(--plyr-menu-background,rgba(255,255,255,.9));border-radius:4px;bottom:100%;box-shadow:0 1px 2px rgba(0,0,0,.15);box-shadow:var(--plyr-menu-shadow,0 1px 2px rgba(0,0,0,.15));color:#4a5464;color:var(--plyr-menu-color,#4a5464);font-size:15px;font-size:var(--plyr-font-size-base,15px);margin-bottom:10px;position:absolute;right:-3px;text-align:left;white-space:nowrap;z-index:3}.plyr__menu__container>div{overflow:hidden;transition:height .35s cubic-bezier(.4,0,.2,1),width .35s cubic-bezier(.4,0,.2,1)}.plyr__menu__container::after{border:4px solid transparent;border:var(--plyr-menu-arrow-size,4px) solid transparent;border-top-color:rgba(255,255,255,.9);border-top-color:var(--plyr-menu-background,rgba(255,255,255,.9));content:'';height:0;position:absolute;right:calc(((18px / 2) + calc(10px * .7)) - (4px / 2));right:calc(((var(--plyr-control-icon-size,18px)/ 2) + calc(var(--plyr-control-spacing,10px) * .7)) - (var(--plyr-menu-arrow-size,4px)/ 2));top:100%;width:0}.plyr__menu__container [role=menu]{padding:calc(10px * .7);padding:calc(var(--plyr-control-spacing,10px) * .7)}.plyr__menu__container [role=menucaptions]{padding:calc(10px * .7);padding:calc(var(--plyr-control-spacing,10px) * .7);max-height:320px;overflow-y:auto}.plyr__menu__container [role=menuitem],.plyr__menu__container [role=menuitemradio]{margin-top:2px}.plyr__menu__container [role=menuitem]:first-child,.plyr__menu__container [role=menuitemradio]:first-child{margin-top:0}.plyr__menu__container .plyr__control{align-items:center;color:#4a5464;color:var(--plyr-menu-color,#4a5464);display:flex;font-size:13px;font-size:var(--plyr-font-size-menu,var(--plyr-font-size-small,13px));padding-bottom:calc(calc(10px * .7)/ 1.5);padding-bottom:calc(calc(var(--plyr-control-spacing,10px) * .7)/ 1.5);padding-left:calc(calc(10px * .7) * 1.5);padding-left:calc(calc(var(--plyr-control-spacing,10px) * .7) * 1.5);padding-right:calc(calc(10px * .7) * 1.5);padding-right:calc(calc(var(--plyr-control-spacing,10px) * .7) * 1.5);padding-top:calc(calc(10px * .7)/ 1.5);padding-top:calc(calc(var(--plyr-control-spacing,10px) * .7)/ 1.5);-webkit-user-select:none;-ms-user-select:none;user-select:none;width:100%}.plyr__menu__container .plyr__control>span{align-items:inherit;display:flex;width:100%}.plyr__menu__container .plyr__control::after{border:4px solid transparent;border:var(--plyr-menu-item-arrow-size,4px) solid transparent;content:'';position:absolute;top:50%;transform:translateY(-50%)}.plyr__menu__container .plyr__control--forward{padding-right:calc(calc(10px * .7) * 4);padding-right:calc(calc(var(--plyr-control-spacing,10px) * .7) * 4)}.plyr__menu__container .plyr__control--forward::after{border-left-color:#728197;border-left-color:var(--plyr-menu-arrow-color,#728197);right:calc((calc(10px * .7) * 1.5) - 4px);right:calc((calc(var(--plyr-control-spacing,10px) * .7) * 1.5) - var(--plyr-menu-item-arrow-size,4px))}.plyr__menu__container .plyr__control--forward.plyr__tab-focus::after,.plyr__menu__container .plyr__control--forward:hover::after{border-left-color:currentColor}.plyr__menu__container .plyr__control--back{font-weight:400;font-weight:var(--plyr-font-weight-regular,400);margin:calc(10px * .7);margin:calc(var(--plyr-control-spacing,10px) * .7);margin-bottom:calc(calc(10px * .7)/ 2);margin-bottom:calc(calc(var(--plyr-control-spacing,10px) * .7)/ 2);padding-left:calc(calc(10px * .7) * 4);padding-left:calc(calc(var(--plyr-control-spacing,10px) * .7) * 4);position:relative;width:calc(100% - (calc(10px * .7) * 2));width:calc(100% - (calc(var(--plyr-control-spacing,10px) * .7) * 2))}.plyr__menu__container .plyr__control--back::after{border-right-color:#728197;border-right-color:var(--plyr-menu-arrow-color,#728197);left:calc((calc(10px * .7) * 1.5) - 4px);left:calc((calc(var(--plyr-control-spacing,10px) * .7) * 1.5) - var(--plyr-menu-item-arrow-size,4px))}.plyr__menu__container .plyr__control--back::before{background:#dcdfe5;background:var(--plyr-menu-back-border-color,#dcdfe5);box-shadow:0 1px 0 #fff;box-shadow:0 1px 0 var(--plyr-menu-back-border-shadow-color,#fff);content:'';height:1px;left:0;margin-top:calc(calc(10px * .7)/ 2);margin-top:calc(calc(var(--plyr-control-spacing,10px) * .7)/ 2);overflow:hidden;position:absolute;right:0;top:100%}.plyr__menu__container .plyr__control--back.plyr__tab-focus::after,.plyr__menu__container .plyr__control--back:hover::after{border-right-color:currentColor}.plyr__menu__container .plyr__control[role=menuitemradio]{padding-left:calc(10px * .7);padding-left:calc(var(--plyr-control-spacing,10px) * .7)}.plyr__menu__container .plyr__control[role=menuitemradio]::after,.plyr__menu__container .plyr__control[role=menuitemradio]::before{border-radius:100%}.plyr__menu__container .plyr__control[role=menuitemradio]::before{background:rgba(0,0,0,.1);content:'';display:block;flex-shrink:0;height:16px;margin-right:10px;margin-right:var(--plyr-control-spacing,10px);transition:all .3s ease;width:16px}.plyr__menu__container .plyr__control[role=menuitemradio]::after{background:#fff;border:0;height:6px;left:12px;opacity:0;top:50%;transform:translateY(-50%) scale(0);transition:transform .3s ease,opacity .3s ease;width:6px}.plyr__menu__container .plyr__control[role=menuitemradio][aria-checked=true]::before{background:#00b3ff;background:var(--plyr-control-toggle-checked-background,var(--plyr-color-main,var(--plyr-color-main,#00b3ff)))}.plyr__menu__container .plyr__control[role=menuitemradio][aria-checked=true]::after{opacity:1;transform:translateY(-50%) scale(1)}.plyr__menu__container .plyr__control[role=menuitemradio].plyr__tab-focus::before,.plyr__menu__container .plyr__control[role=menuitemradio]:hover::before{background:rgba(35,40,47,.1)}.plyr__menu__container .plyr__menu__value{align-items:center;display:flex;margin-left:auto;margin-right:calc((calc(10px * .7) - 2) * -1);margin-right:calc((calc(var(--plyr-control-spacing,10px) * .7) - 2) * -1);overflow:hidden;padding-left:calc(calc(10px * .7) * 3.5);padding-left:calc(calc(var(--plyr-control-spacing,10px) * .7) * 3.5);pointer-events:none}.plyr--full-ui input[type=range]{-webkit-appearance:none;background:0 0;border:0;border-radius:calc(13px * 2);border-radius:calc(var(--plyr-range-thumb-height,13px) * 2);color:#00b3ff;color:var(--plyr-range-fill-background,var(--plyr-color-main,var(--plyr-color-main,#00b3ff)));display:block;height:calc((3px * 2) + 13px);height:calc((var(--plyr-range-thumb-active-shadow-width,3px) * 2) + var(--plyr-range-thumb-height,13px));margin:0;padding:0;transition:box-shadow .3s ease;width:100%}.plyr--full-ui input[type=range]::-webkit-slider-runnable-track{background:0 0;border:0;border-radius:calc(5px / 2);border-radius:calc(var(--plyr-range-track-height,5px)/ 2);height:5px;height:var(--plyr-range-track-height,5px);-webkit-transition:box-shadow .3s ease;transition:box-shadow .3s ease;-webkit-user-select:none;user-select:none;background-image:linear-gradient(to right,currentColor 0,transparent 0);background-image:linear-gradient(to right,currentColor var(--value,0),transparent var(--value,0))}.plyr--full-ui input[type=range]::-webkit-slider-thumb{background:#fff;background:var(--plyr-range-thumb-background,#fff);border:0;border-radius:100%;box-shadow:0 1px 1px rgba(35,40,47,.15),0 0 0 1px rgba(35,40,47,.2);box-shadow:var(--plyr-range-thumb-shadow,0 1px 1px rgba(35,40,47,.15),0 0 0 1px rgba(35,40,47,.2));height:13px;height:var(--plyr-range-thumb-height,13px);position:relative;-webkit-transition:all .2s ease;transition:all .2s ease;width:13px;width:var(--plyr-range-thumb-height,13px);-webkit-appearance:none;margin-top:calc(((13px - 5px)/ 2) * -1);margin-top:calc(((var(--plyr-range-thumb-height,13px) - var(--plyr-range-track-height,5px))/ 2) * -1)}.plyr--full-ui input[type=range]::-moz-range-track{background:0 0;border:0;border-radius:calc(5px / 2);border-radius:calc(var(--plyr-range-track-height,5px)/ 2);height:5px;height:var(--plyr-range-track-height,5px);-moz-transition:box-shadow .3s ease;transition:box-shadow .3s ease;user-select:none}.plyr--full-ui input[type=range]::-moz-range-thumb{background:#fff;background:var(--plyr-range-thumb-background,#fff);border:0;border-radius:100%;box-shadow:0 1px 1px rgba(35,40,47,.15),0 0 0 1px rgba(35,40,47,.2);box-shadow:var(--plyr-range-thumb-shadow,0 1px 1px rgba(35,40,47,.15),0 0 0 1px rgba(35,40,47,.2));height:13px;height:var(--plyr-range-thumb-height,13px);position:relative;-moz-transition:all .2s ease;transition:all .2s ease;width:13px;width:var(--plyr-range-thumb-height,13px)}.plyr--full-ui input[type=range]::-moz-range-progress{background:currentColor;border-radius:calc(5px / 2);border-radius:calc(var(--plyr-range-track-height,5px)/ 2);height:5px;height:var(--plyr-range-track-height,5px)}.plyr--full-ui input[type=range]::-ms-track{background:0 0;border:0;border-radius:calc(5px / 2);border-radius:calc(var(--plyr-range-track-height,5px)/ 2);height:5px;height:var(--plyr-range-track-height,5px);-ms-transition:box-shadow .3s ease;transition:box-shadow .3s ease;-ms-user-select:none;user-select:none;color:transparent}.plyr--full-ui input[type=range]::-ms-fill-upper{background:0 0;border:0;border-radius:calc(5px / 2);border-radius:calc(var(--plyr-range-track-height,5px)/ 2);height:5px;height:var(--plyr-range-track-height,5px);-ms-transition:box-shadow .3s ease;transition:box-shadow .3s ease;-ms-user-select:none;user-select:none}.plyr--full-ui input[type=range]::-ms-fill-lower{background:0 0;border:0;border-radius:calc(5px / 2);border-radius:calc(var(--plyr-range-track-height,5px)/ 2);height:5px;height:var(--plyr-range-track-height,5px);-ms-transition:box-shadow .3s ease;transition:box-shadow .3s ease;-ms-user-select:none;user-select:none;background:currentColor}.plyr--full-ui input[type=range]::-ms-thumb{background:#fff;background:var(--plyr-range-thumb-background,#fff);border:0;border-radius:100%;box-shadow:0 1px 1px rgba(35,40,47,.15),0 0 0 1px rgba(35,40,47,.2);box-shadow:var(--plyr-range-thumb-shadow,0 1px 1px rgba(35,40,47,.15),0 0 0 1px rgba(35,40,47,.2));height:13px;height:var(--plyr-range-thumb-height,13px);position:relative;-ms-transition:all .2s ease;transition:all .2s ease;width:13px;width:var(--plyr-range-thumb-height,13px);margin-top:0}.plyr--full-ui input[type=range]::-ms-tooltip{display:none}.plyr--full-ui input[type=range]:focus{outline:0}.plyr--full-ui input[type=range]::-moz-focus-outer{border:0}.plyr--full-ui input[type=range].plyr__tab-focus::-webkit-slider-runnable-track{outline-color:#00b3ff;outline-color:var(--plyr-tab-focus-color,var(--plyr-color-main,var(--plyr-color-main,#00b3ff)));outline-offset:2px;outline-style:dotted;outline-width:3px}.plyr--full-ui input[type=range].plyr__tab-focus::-moz-range-track{outline-color:#00b3ff;outline-color:var(--plyr-tab-focus-color,var(--plyr-color-main,var(--plyr-color-main,#00b3ff)));outline-offset:2px;outline-style:dotted;outline-width:3px}.plyr--full-ui input[type=range].plyr__tab-focus::-ms-track{outline-color:#00b3ff;outline-color:var(--plyr-tab-focus-color,var(--plyr-color-main,var(--plyr-color-main,#00b3ff)));outline-offset:2px;outline-style:dotted;outline-width:3px}.plyr__poster{background-color:#000;background-position:50% 50%;background-repeat:no-repeat;background-size:contain;height:100%;left:0;opacity:0;position:absolute;top:0;transition:opacity .2s ease;width:100%;z-index:1}.plyr--stopped.plyr__poster-enabled .plyr__poster{opacity:1}.plyr__time{font-size:13px;font-size:var(--plyr-font-size-time,var(--plyr-font-size-small,13px))}.plyr__time+.plyr__time::before{content:'\2044';margin-right:10px;margin-right:var(--plyr-control-spacing,10px)}@media (max-width:calc(768px - 1)){.plyr__time+.plyr__time{display:none}}.plyr__tooltip{background:rgba(255,255,255,.9);background:var(--plyr-tooltip-background,rgba(255,255,255,.9));border-radius:3px;border-radius:var(--plyr-tooltip-radius,3px);bottom:100%;box-shadow:0 1px 2px rgba(0,0,0,.15);box-shadow:var(--plyr-tooltip-shadow,0 1px 2px rgba(0,0,0,.15));color:#4a5464;color:var(--plyr-tooltip-color,#4a5464);font-size:13px;font-size:var(--plyr-font-size-small,13px);font-weight:400;font-weight:var(--plyr-font-weight-regular,400);left:50%;line-height:1.3;margin-bottom:calc(calc(10px / 2) * 2);margin-bottom:calc(calc(var(--plyr-control-spacing,10px)/ 2) * 2);opacity:0;padding:calc(10px / 2) calc(calc(10px / 2) * 1.5);padding:calc(var(--plyr-control-spacing,10px)/ 2) calc(calc(var(--plyr-control-spacing,10px)/ 2) * 1.5);pointer-events:none;position:absolute;transform:translate(-50%,10px) scale(.8);transform-origin:50% 100%;transition:transform .2s .1s ease,opacity .2s .1s ease;white-space:nowrap;z-index:2}.plyr__tooltip::before{border-left:4px solid transparent;border-left:var(--plyr-tooltip-arrow-size,4px) solid transparent;border-right:4px solid transparent;border-right:var(--plyr-tooltip-arrow-size,4px) solid transparent;border-top:4px solid rgba(255,255,255,.9);border-top:var(--plyr-tooltip-arrow-size,4px) solid var(--plyr-tooltip-background,rgba(255,255,255,.9));bottom:calc(4px * -1);bottom:calc(var(--plyr-tooltip-arrow-size,4px) * -1);content:'';height:0;left:50%;position:absolute;transform:translateX(-50%);width:0;z-index:2}.plyr .plyr__control.plyr__tab-focus .plyr__tooltip,.plyr .plyr__control:hover .plyr__tooltip,.plyr__tooltip--visible{opacity:1;transform:translate(-50%,0) scale(1)}.plyr .plyr__control:hover .plyr__tooltip{z-index:3}.plyr__controls>.plyr__control:first-child .plyr__tooltip,.plyr__controls>.plyr__control:first-child+.plyr__control .plyr__tooltip{left:0;transform:translate(0,10px) scale(.8);transform-origin:0 100%}.plyr__controls>.plyr__control:first-child .plyr__tooltip::before,.plyr__controls>.plyr__control:first-child+.plyr__control .plyr__tooltip::before{left:calc((18px / 2) + calc(10px * .7));left:calc((var(--plyr-control-icon-size,18px)/ 2) + calc(var(--plyr-control-spacing,10px) * .7))}.plyr__controls>.plyr__control:last-child .plyr__tooltip{left:auto;right:0;transform:translate(0,10px) scale(.8);transform-origin:100% 100%}.plyr__controls>.plyr__control:last-child .plyr__tooltip::before{left:auto;right:calc((18px / 2) + calc(10px * .7));right:calc((var(--plyr-control-icon-size,18px)/ 2) + calc(var(--plyr-control-spacing,10px) * .7));transform:translateX(50%)}.plyr__controls>.plyr__control:first-child .plyr__tooltip--visible,.plyr__controls>.plyr__control:first-child+.plyr__control .plyr__tooltip--visible,.plyr__controls>.plyr__control:first-child+.plyr__control.plyr__tab-focus .plyr__tooltip,.plyr__controls>.plyr__control:first-child+.plyr__control:hover .plyr__tooltip,.plyr__controls>.plyr__control:first-child.plyr__tab-focus .plyr__tooltip,.plyr__controls>.plyr__control:first-child:hover .plyr__tooltip,.plyr__controls>.plyr__control:last-child .plyr__tooltip--visible,.plyr__controls>.plyr__control:last-child.plyr__tab-focus .plyr__tooltip,.plyr__controls>.plyr__control:last-child:hover .plyr__tooltip{transform:translate(0,0) scale(1)}.plyr__progress{left:calc(13px * .5);left:calc(var(--plyr-range-thumb-height,13px) * .5);margin-right:13px;margin-right:var(--plyr-range-thumb-height,13px);position:relative}.plyr__progress input[type=range],.plyr__progress__buffer{margin-left:calc(13px * -.5);margin-left:calc(var(--plyr-range-thumb-height,13px) * -.5);margin-right:calc(13px * -.5);margin-right:calc(var(--plyr-range-thumb-height,13px) * -.5);width:calc(100% + 13px);width:calc(100% + var(--plyr-range-thumb-height,13px))}.plyr__progress input[type=range]{position:relative;z-index:2}.plyr__progress .plyr__tooltip{font-size:13px;font-size:var(--plyr-font-size-time,var(--plyr-font-size-small,13px));left:0}.plyr__progress__buffer{-webkit-appearance:none;background:0 0;border:0;border-radius:100px;height:5px;height:var(--plyr-range-track-height,5px);left:0;margin-top:calc((5px / 2) * -1);margin-top:calc((var(--plyr-range-track-height,5px)/ 2) * -1);padding:0;position:absolute;top:50%}.plyr__progress__buffer::-webkit-progress-bar{background:0 0}.plyr__progress__buffer::-webkit-progress-value{background:currentColor;border-radius:100px;min-width:5px;min-width:var(--plyr-range-track-height,5px);-webkit-transition:width .2s ease;transition:width .2s ease}.plyr__progress__buffer::-moz-progress-bar{background:currentColor;border-radius:100px;min-width:5px;min-width:var(--plyr-range-track-height,5px);-moz-transition:width .2s ease;transition:width .2s ease}.plyr__progress__buffer::-ms-fill{border-radius:100px;-ms-transition:width .2s ease;transition:width .2s ease}.plyr--loading .plyr__progress__buffer{animation:plyr-progress 1s linear infinite;background-image:linear-gradient(-45deg,rgba(35,40,47,.6) 25%,transparent 25%,transparent 50%,rgba(35,40,47,.6) 50%,rgba(35,40,47,.6) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,var(--plyr-progress-loading-background,rgba(35,40,47,.6)) 25%,transparent 25%,transparent 50%,var(--plyr-progress-loading-background,rgba(35,40,47,.6)) 50%,var(--plyr-progress-loading-background,rgba(35,40,47,.6)) 75%,transparent 75%,transparent);background-repeat:repeat-x;background-size:25px 25px;background-size:var(--plyr-progress-loading-size,25px) var(--plyr-progress-loading-size,25px);color:transparent}.plyr--video.plyr--loading .plyr__progress__buffer{background-color:rgba(255,255,255,.25);background-color:var(--plyr-video-progress-buffered-background,rgba(255,255,255,.25))}.plyr--audio.plyr--loading .plyr__progress__buffer{background-color:rgba(193,200,209,.6);background-color:var(--plyr-audio-progress-buffered-background,rgba(193,200,209,.6))}.plyr__volume{align-items:center;display:flex;max-width:110px;min-width:80px;position:relative;width:20%}.plyr__volume input[type=range]{margin-left:calc(10px / 2);margin-left:calc(var(--plyr-control-spacing,10px)/ 2);margin-right:calc(10px / 2);margin-right:calc(var(--plyr-control-spacing,10px)/ 2);position:relative;z-index:2}.plyr--is-ios .plyr__volume{min-width:0;width:auto}.plyr--audio{display:block}.plyr--audio .plyr__controls{background:#fff;background:var(--plyr-audio-controls-background,#fff);border-radius:inherit;color:#4a5464;color:var(--plyr-audio-control-color,#4a5464);padding:10px;padding:var(--plyr-control-spacing,10px)}.plyr--audio .plyr__control.plyr__tab-focus,.plyr--audio .plyr__control:hover,.plyr--audio .plyr__control[aria-expanded=true]{background:#00b3ff;background:var(--plyr-audio-control-background-hover,var(--plyr-color-main,var(--plyr-color-main,#00b3ff)));color:#fff;color:var(--plyr-audio-control-color-hover,#fff)}.plyr--full-ui.plyr--audio input[type=range]::-webkit-slider-runnable-track{background-color:rgba(193,200,209,.6);background-color:var(--plyr-audio-range-track-background,var(--plyr-audio-progress-buffered-background,rgba(193,200,209,.6)))}.plyr--full-ui.plyr--audio input[type=range]::-moz-range-track{background-color:rgba(193,200,209,.6);background-color:var(--plyr-audio-range-track-background,var(--plyr-audio-progress-buffered-background,rgba(193,200,209,.6)))}.plyr--full-ui.plyr--audio input[type=range]::-ms-track{background-color:rgba(193,200,209,.6);background-color:var(--plyr-audio-range-track-background,var(--plyr-audio-progress-buffered-background,rgba(193,200,209,.6)))}.plyr--full-ui.plyr--audio input[type=range]:active::-webkit-slider-thumb{box-shadow:0 1px 1px rgba(35,40,47,.15),0 0 0 1px rgba(35,40,47,.2),0 0 0 3px rgba(35,40,47,.1);box-shadow:var(--plyr-range-thumb-shadow,0 1px 1px rgba(35,40,47,.15),0 0 0 1px rgba(35,40,47,.2)),0 0 0 var(--plyr-range-thumb-active-shadow-width,3px) var(--plyr-audio-range-thumb-active-shadow-color,rgba(35,40,47,.1))}.plyr--full-ui.plyr--audio input[type=range]:active::-moz-range-thumb{box-shadow:0 1px 1px rgba(35,40,47,.15),0 0 0 1px rgba(35,40,47,.2),0 0 0 3px rgba(35,40,47,.1);box-shadow:var(--plyr-range-thumb-shadow,0 1px 1px rgba(35,40,47,.15),0 0 0 1px rgba(35,40,47,.2)),0 0 0 var(--plyr-range-thumb-active-shadow-width,3px) var(--plyr-audio-range-thumb-active-shadow-color,rgba(35,40,47,.1))}.plyr--full-ui.plyr--audio input[type=range]:active::-ms-thumb{box-shadow:0 1px 1px rgba(35,40,47,.15),0 0 0 1px rgba(35,40,47,.2),0 0 0 3px rgba(35,40,47,.1);box-shadow:var(--plyr-range-thumb-shadow,0 1px 1px rgba(35,40,47,.15),0 0 0 1px rgba(35,40,47,.2)),0 0 0 var(--plyr-range-thumb-active-shadow-width,3px) var(--plyr-audio-range-thumb-active-shadow-color,rgba(35,40,47,.1))}.plyr--audio .plyr__progress__buffer{color:rgba(193,200,209,.6);color:var(--plyr-audio-progress-buffered-background,rgba(193,200,209,.6))}.plyr--video{background:#000;overflow:hidden}.plyr--video.plyr--menu-open{overflow:visible}.plyr__video-wrapper{background:#000;height:100%;margin:auto;overflow:hidden;position:relative;width:100%}.plyr__video-embed,.plyr__video-wrapper--fixed-ratio{height:0;padding-bottom:56.25%}.plyr__video-embed iframe,.plyr__video-wrapper--fixed-ratio video{border:0;left:0;position:absolute;top:0}.plyr--full-ui .plyr__video-embed>.plyr__video-embed__container{padding-bottom:240%;position:relative;transform:translateY(-38.28125%)}.plyr--video .plyr__controls{background:linear-gradient(rgba(0,0,0,0),rgba(0,0,0,.75));background:var(--plyr-video-controls-background,linear-gradient(rgba(0,0,0,0),rgba(0,0,0,.75)));border-bottom-left-radius:inherit;border-bottom-right-radius:inherit;bottom:0;color:#fff;color:var(--plyr-video-control-color,#fff);left:0;padding:calc(10px / 2);padding:calc(var(--plyr-control-spacing,10px)/ 2);padding-top:calc(10px * 2);padding-top:calc(var(--plyr-control-spacing,10px) * 2);position:absolute;right:0;transition:opacity .4s ease-in-out,transform .4s ease-in-out;z-index:3}@media (min-width:480px){.plyr--video .plyr__controls{padding:10px;padding:var(--plyr-control-spacing,10px);padding-top:calc(10px * 3.5);padding-top:calc(var(--plyr-control-spacing,10px) * 3.5)}}.plyr--video.plyr--hide-controls .plyr__controls{opacity:0;pointer-events:none;transform:translateY(100%)}.plyr--video .plyr__control.plyr__tab-focus,.plyr--video .plyr__control:hover,.plyr--video .plyr__control[aria-expanded=true]{background:#00b3ff;background:var(--plyr-video-control-background-hover,var(--plyr-color-main,var(--plyr-color-main,#00b3ff)));color:#fff;color:var(--plyr-video-control-color-hover,#fff)}.plyr__control--overlaid{background:#00b3ff;background:var(--plyr-video-control-background-hover,var(--plyr-color-main,var(--plyr-color-main,#00b3ff)));border:0;border-radius:100%;color:#fff;color:var(--plyr-video-control-color,#fff);display:none;left:50%;opacity:.9;padding:calc(10px * 1.5);padding:calc(var(--plyr-control-spacing,10px) * 1.5);position:absolute;top:50%;transform:translate(-50%,-50%);transition:.3s;z-index:2}.plyr__control--overlaid svg{left:2px;position:relative}.plyr__control--overlaid:focus,.plyr__control--overlaid:hover{opacity:1}.plyr--playing .plyr__control--overlaid{opacity:0;visibility:hidden}.plyr--full-ui.plyr--video .plyr__control--overlaid{display:block}.plyr--full-ui.plyr--video input[type=range]::-webkit-slider-runnable-track{background-color:rgba(255,255,255,.25);background-color:var(--plyr-video-range-track-background,var(--plyr-video-progress-buffered-background,rgba(255,255,255,.25)))}.plyr--full-ui.plyr--video input[type=range]::-moz-range-track{background-color:rgba(255,255,255,.25);background-color:var(--plyr-video-range-track-background,var(--plyr-video-progress-buffered-background,rgba(255,255,255,.25)))}.plyr--full-ui.plyr--video input[type=range]::-ms-track{background-color:rgba(255,255,255,.25);background-color:var(--plyr-video-range-track-background,var(--plyr-video-progress-buffered-background,rgba(255,255,255,.25)))}.plyr--full-ui.plyr--video input[type=range]:active::-webkit-slider-thumb{box-shadow:0 1px 1px rgba(35,40,47,.15),0 0 0 1px rgba(35,40,47,.2),0 0 0 3px rgba(255,255,255,.5);box-shadow:var(--plyr-range-thumb-shadow,0 1px 1px rgba(35,40,47,.15),0 0 0 1px rgba(35,40,47,.2)),0 0 0 var(--plyr-range-thumb-active-shadow-width,3px) var(--plyr-audio-range-thumb-active-shadow-color,rgba(255,255,255,.5))}.plyr--full-ui.plyr--video input[type=range]:active::-moz-range-thumb{box-shadow:0 1px 1px rgba(35,40,47,.15),0 0 0 1px rgba(35,40,47,.2),0 0 0 3px rgba(255,255,255,.5);box-shadow:var(--plyr-range-thumb-shadow,0 1px 1px rgba(35,40,47,.15),0 0 0 1px rgba(35,40,47,.2)),0 0 0 var(--plyr-range-thumb-active-shadow-width,3px) var(--plyr-audio-range-thumb-active-shadow-color,rgba(255,255,255,.5))}.plyr--full-ui.plyr--video input[type=range]:active::-ms-thumb{box-shadow:0 1px 1px rgba(35,40,47,.15),0 0 0 1px rgba(35,40,47,.2),0 0 0 3px rgba(255,255,255,.5);box-shadow:var(--plyr-range-thumb-shadow,0 1px 1px rgba(35,40,47,.15),0 0 0 1px rgba(35,40,47,.2)),0 0 0 var(--plyr-range-thumb-active-shadow-width,3px) var(--plyr-audio-range-thumb-active-shadow-color,rgba(255,255,255,.5))}.plyr--video .plyr__progress__buffer{color:rgba(255,255,255,.25);color:var(--plyr-video-progress-buffered-background,rgba(255,255,255,.25))}.plyr:-webkit-full-screen{background:#000;border-radius:0!important;height:100%;margin:0;width:100%}.plyr:-ms-fullscreen{background:#000;border-radius:0!important;height:100%;margin:0;width:100%}.plyr:fullscreen{background:#000;border-radius:0!important;height:100%;margin:0;width:100%}.plyr:-webkit-full-screen video{height:100%}.plyr:-ms-fullscreen video{height:100%}.plyr:fullscreen video{height:100%}.plyr:-webkit-full-screen .plyr__video-wrapper{height:100%;position:static}.plyr:-ms-fullscreen .plyr__video-wrapper{height:100%;position:static}.plyr:fullscreen .plyr__video-wrapper{height:100%;position:static}.plyr:-webkit-full-screen.plyr--vimeo .plyr__video-wrapper{height:0;position:relative}.plyr:-ms-fullscreen.plyr--vimeo .plyr__video-wrapper{height:0;position:relative}.plyr:fullscreen.plyr--vimeo .plyr__video-wrapper{height:0;position:relative}.plyr:-webkit-full-screen .plyr__control .icon--exit-fullscreen{display:block}.plyr:-ms-fullscreen .plyr__control .icon--exit-fullscreen{display:block}.plyr:fullscreen .plyr__control .icon--exit-fullscreen{display:block}.plyr:-webkit-full-screen .plyr__control .icon--exit-fullscreen+svg{display:none}.plyr:-ms-fullscreen .plyr__control .icon--exit-fullscreen+svg{display:none}.plyr:fullscreen .plyr__control .icon--exit-fullscreen+svg{display:none}.plyr:-webkit-full-screen.plyr--hide-controls{cursor:none}.plyr:-ms-fullscreen.plyr--hide-controls{cursor:none}.plyr:fullscreen.plyr--hide-controls{cursor:none}@media (min-width:1024px){.plyr:-webkit-full-screen .plyr__captions{font-size:21px;font-size:var(--plyr-font-size-xlarge,21px)}.plyr:-ms-fullscreen .plyr__captions{font-size:21px;font-size:var(--plyr-font-size-xlarge,21px)}.plyr:fullscreen .plyr__captions{font-size:21px;font-size:var(--plyr-font-size-xlarge,21px)}}.plyr:-webkit-full-screen{background:#000;border-radius:0!important;height:100%;margin:0;width:100%}.plyr:-webkit-full-screen video{height:100%}.plyr:-webkit-full-screen .plyr__video-wrapper{height:100%;position:static}.plyr:-webkit-full-screen.plyr--vimeo .plyr__video-wrapper{height:0;position:relative}.plyr:-webkit-full-screen .plyr__control .icon--exit-fullscreen{display:block}.plyr:-webkit-full-screen .plyr__control .icon--exit-fullscreen+svg{display:none}.plyr:-webkit-full-screen.plyr--hide-controls{cursor:none}@media (min-width:1024px){.plyr:-webkit-full-screen .plyr__captions{font-size:21px;font-size:var(--plyr-font-size-xlarge,21px)}}.plyr:-moz-full-screen{background:#000;border-radius:0!important;height:100%;margin:0;width:100%}.plyr:-moz-full-screen video{height:100%}.plyr:-moz-full-screen .plyr__video-wrapper{height:100%;position:static}.plyr:-moz-full-screen.plyr--vimeo .plyr__video-wrapper{height:0;position:relative}.plyr:-moz-full-screen .plyr__control .icon--exit-fullscreen{display:block}.plyr:-moz-full-screen .plyr__control .icon--exit-fullscreen+svg{display:none}.plyr:-moz-full-screen.plyr--hide-controls{cursor:none}@media (min-width:1024px){.plyr:-moz-full-screen .plyr__captions{font-size:21px;font-size:var(--plyr-font-size-xlarge,21px)}}.plyr:-ms-fullscreen{background:#000;border-radius:0!important;height:100%;margin:0;width:100%}.plyr:-ms-fullscreen video{height:100%}.plyr:-ms-fullscreen .plyr__video-wrapper{height:100%;position:static}.plyr:-ms-fullscreen.plyr--vimeo .plyr__video-wrapper{height:0;position:relative}.plyr:-ms-fullscreen .plyr__control .icon--exit-fullscreen{display:block}.plyr:-ms-fullscreen .plyr__control .icon--exit-fullscreen+svg{display:none}.plyr:-ms-fullscreen.plyr--hide-controls{cursor:none}@media (min-width:1024px){.plyr:-ms-fullscreen .plyr__captions{font-size:21px;font-size:var(--plyr-font-size-xlarge,21px)}}.plyr--fullscreen-fallback{background:#000;border-radius:0!important;height:100%;margin:0;width:100%;bottom:0;display:block;left:0;position:fixed;right:0;top:0;z-index:10000000}.plyr--fullscreen-fallback video{height:100%}.plyr--fullscreen-fallback .plyr__video-wrapper{height:100%;position:static}.plyr--fullscreen-fallback.plyr--vimeo .plyr__video-wrapper{height:0;position:relative}.plyr--fullscreen-fallback .plyr__control .icon--exit-fullscreen{display:block}.plyr--fullscreen-fallback .plyr__control .icon--exit-fullscreen+svg{display:none}.plyr--fullscreen-fallback.plyr--hide-controls{cursor:none}@media (min-width:1024px){.plyr--fullscreen-fallback .plyr__captions{font-size:21px;font-size:var(--plyr-font-size-xlarge,21px)}}.plyr__ads{border-radius:inherit;bottom:0;cursor:pointer;left:0;overflow:hidden;position:absolute;right:0;top:0;z-index:-1}.plyr__ads>div,.plyr__ads>div iframe{height:100%;position:absolute;width:100%}.plyr__ads::after{background:#23282f;border-radius:2px;bottom:10px;bottom:var(--plyr-control-spacing,10px);color:#fff;content:attr(data-badge-text);font-size:11px;padding:2px 6px;pointer-events:none;position:absolute;right:10px;right:var(--plyr-control-spacing,10px);z-index:3}.plyr__ads::after:empty{display:none}.plyr__cues{background:currentColor;display:block;height:5px;height:var(--plyr-range-track-height,5px);left:0;margin:-var(--plyr-range-track-height,5px)/2 0 0;opacity:.8;position:absolute;top:50%;width:3px;z-index:3}.plyr__preview-thumb{background-color:rgba(255,255,255,.9);background-color:var(--plyr-tooltip-background,rgba(255,255,255,.9));border-radius:3px;bottom:100%;box-shadow:0 1px 2px rgba(0,0,0,.15);box-shadow:var(--plyr-tooltip-shadow,0 1px 2px rgba(0,0,0,.15));margin-bottom:calc(calc(10px / 2) * 2);margin-bottom:calc(calc(var(--plyr-control-spacing,10px)/ 2) * 2);opacity:0;padding:3px;padding:var(--plyr-tooltip-radius,3px);pointer-events:none;position:absolute;transform:translate(0,10px) scale(.8);transform-origin:50% 100%;transition:transform .2s .1s ease,opacity .2s .1s ease;z-index:2}.plyr__preview-thumb--is-shown{opacity:1;transform:translate(0,0) scale(1)}.plyr__preview-thumb::before{border-left:4px solid transparent;border-left:var(--plyr-tooltip-arrow-size,4px) solid transparent;border-right:4px solid transparent;border-right:var(--plyr-tooltip-arrow-size,4px) solid transparent;border-top:4px solid rgba(255,255,255,.9);border-top:var(--plyr-tooltip-arrow-size,4px) solid var(--plyr-tooltip-background,rgba(255,255,255,.9));bottom:calc(4px * -1);bottom:calc(var(--plyr-tooltip-arrow-size,4px) * -1);content:'';height:0;left:50%;position:absolute;transform:translateX(-50%);width:0;z-index:2}.plyr__preview-thumb__image-container{background:#c1c8d1;border-radius:calc(3px - 1px);border-radius:calc(var(--plyr-tooltip-radius,3px) - 1px);overflow:hidden;position:relative;z-index:0}.plyr__preview-thumb__image-container img{height:100%;left:0;max-height:none;max-width:none;position:absolute;top:0;width:100%}.plyr__preview-thumb__time-container{bottom:6px;left:0;position:absolute;right:0;white-space:nowrap;z-index:3}.plyr__preview-thumb__time-container span{background-color:rgba(0,0,0,.55);border-radius:calc(3px - 1px);border-radius:calc(var(--plyr-tooltip-radius,3px) - 1px);color:#fff;font-size:13px;font-size:var(--plyr-font-size-time,var(--plyr-font-size-small,13px));padding:3px 6px}.plyr__preview-scrubbing{bottom:0;filter:blur(1px);height:100%;left:0;margin:auto;opacity:0;overflow:hidden;pointer-events:none;position:absolute;right:0;top:0;transition:opacity .3s ease;width:100%;z-index:1}.plyr__preview-scrubbing--is-shown{opacity:1}.plyr__preview-scrubbing img{height:100%;left:0;max-height:none;max-width:none;object-fit:contain;position:absolute;top:0;width:100%}.plyr--no-transition{transition:none!important}.plyr__sr-only{clip:rect(1px,1px,1px,1px);overflow:hidden;border:0!important;height:1px!important;padding:0!important;position:absolute!important;width:1px!important}.plyr [hidden]{display:none!important} \ No newline at end of file +@charset "UTF-8";@keyframes plyr-progress{to{background-position:25px 0;background-position:var(--plyr-progress-loading-size,25px) 0}}@keyframes plyr-popup{0%{opacity:.5;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@keyframes plyr-fade-in{0%{opacity:0}to{opacity:1}}.plyr{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;align-items:center;direction:ltr;display:flex;flex-direction:column;font-family:inherit;font-family:var(--plyr-font-family,inherit);font-variant-numeric:tabular-nums;font-weight:400;font-weight:var(--plyr-font-weight-regular,400);line-height:1.7;line-height:var(--plyr-line-height,1.7);max-width:100%;min-width:200px;position:relative;text-shadow:none;transition:box-shadow .3s ease;z-index:0}.plyr audio,.plyr iframe,.plyr video{display:block;height:100%;width:100%}.plyr button{font:inherit;line-height:inherit;width:auto}.plyr:focus{outline:0}.plyr--full-ui{box-sizing:border-box}.plyr--full-ui *,.plyr--full-ui :after,.plyr--full-ui :before{box-sizing:inherit}.plyr--full-ui a,.plyr--full-ui button,.plyr--full-ui input,.plyr--full-ui label{touch-action:manipulation}.plyr__badge{background:#4a5464;background:var(--plyr-badge-background,#4a5464);border-radius:2px;border-radius:var(--plyr-badge-border-radius,2px);color:#fff;color:var(--plyr-badge-text-color,#fff);font-size:9px;font-size:var(--plyr-font-size-badge,9px);line-height:1;padding:3px 4px}.plyr--full-ui ::-webkit-media-text-track-container{display:none}.plyr__captions{animation:plyr-fade-in .3s ease;bottom:0;display:none;font-size:13px;font-size:var(--plyr-font-size-small,13px);left:0;padding:10px;padding:var(--plyr-control-spacing,10px);position:absolute;text-align:center;transition:transform .4s ease-in-out;width:100%}.plyr__captions span:empty{display:none}@media (min-width:480px){.plyr__captions{font-size:15px;font-size:var(--plyr-font-size-base,15px);padding:20px;padding:calc(var(--plyr-control-spacing, 10px)*2)}}@media (min-width:768px){.plyr__captions{font-size:18px;font-size:var(--plyr-font-size-large,18px)}}.plyr--captions-active .plyr__captions{display:block}.plyr:not(.plyr--hide-controls) .plyr__controls:not(:empty)~.plyr__captions{transform:translateY(-40px);transform:translateY(calc(var(--plyr-control-spacing, 10px)*-4))}.plyr__caption{background:#000c;background:var(--plyr-captions-background,#000c);border-radius:2px;-webkit-box-decoration-break:clone;box-decoration-break:clone;color:#fff;color:var(--plyr-captions-text-color,#fff);line-height:185%;padding:.2em .5em;white-space:pre-wrap}.plyr__caption div{display:inline}.plyr__control{background:#0000;border:0;border-radius:4px;border-radius:var(--plyr-control-radius,4px);color:inherit;cursor:pointer;flex-shrink:0;overflow:visible;padding:7px;padding:calc(var(--plyr-control-spacing, 10px)*.7);position:relative;transition:all .3s ease}.plyr__control svg{fill:currentColor;display:block;height:18px;height:var(--plyr-control-icon-size,18px);pointer-events:none;width:18px;width:var(--plyr-control-icon-size,18px)}.plyr__control:focus{outline:0}.plyr__control:focus-visible{outline:2px dashed #00b2ff;outline:2px dashed var(--plyr-focus-visible-color,var(--plyr-color-main,var(--plyr-color-main,#00b2ff)));outline-offset:2px}a.plyr__control{text-decoration:none}.plyr__control.plyr__control--pressed .icon--not-pressed,.plyr__control.plyr__control--pressed .label--not-pressed,.plyr__control:not(.plyr__control--pressed) .icon--pressed,.plyr__control:not(.plyr__control--pressed) .label--pressed,a.plyr__control:after,a.plyr__control:before{display:none}.plyr--full-ui ::-webkit-media-controls{display:none}.plyr__controls{align-items:center;display:flex;justify-content:flex-end;text-align:center}.plyr__controls .plyr__progress__container{flex:1;min-width:0}.plyr__controls .plyr__controls__item{margin-left:2.5px;margin-left:calc(var(--plyr-control-spacing, 10px)/4)}.plyr__controls .plyr__controls__item:first-child{margin-left:0;margin-right:auto}.plyr__controls .plyr__controls__item.plyr__progress__container{padding-left:2.5px;padding-left:calc(var(--plyr-control-spacing, 10px)/4)}.plyr__controls .plyr__controls__item.plyr__time{padding:0 5px;padding:0 calc(var(--plyr-control-spacing, 10px)/2)}.plyr__controls .plyr__controls__item.plyr__progress__container:first-child,.plyr__controls .plyr__controls__item.plyr__time+.plyr__time,.plyr__controls .plyr__controls__item.plyr__time:first-child{padding-left:0}.plyr [data-plyr=airplay],.plyr [data-plyr=captions],.plyr [data-plyr=fullscreen],.plyr [data-plyr=pip],.plyr__controls:empty{display:none}.plyr--airplay-supported [data-plyr=airplay],.plyr--captions-enabled [data-plyr=captions],.plyr--fullscreen-enabled [data-plyr=fullscreen],.plyr--pip-supported [data-plyr=pip]{display:inline-block}.plyr__menu{display:flex;position:relative}.plyr__menu .plyr__control svg{transition:transform .3s ease}.plyr__menu .plyr__control[aria-expanded=true] svg{transform:rotate(90deg)}.plyr__menu .plyr__control[aria-expanded=true] .plyr__tooltip{display:none}.plyr__menu__container{animation:plyr-popup .2s ease;background:#ffffffe6;background:var(--plyr-menu-background,#ffffffe6);border-radius:8px;border-radius:var(--plyr-menu-radius,8px);bottom:100%;box-shadow:0 1px 2px #00000026;box-shadow:var(--plyr-menu-shadow,0 1px 2px #00000026);color:#4a5464;color:var(--plyr-menu-color,#4a5464);font-size:15px;font-size:var(--plyr-font-size-base,15px);margin-bottom:10px;position:absolute;right:-3px;text-align:left;white-space:nowrap;z-index:3}.plyr__menu__container>div{overflow:hidden;transition:height .35s cubic-bezier(.4,0,.2,1),width .35s cubic-bezier(.4,0,.2,1)}.plyr__menu__container:after{border:4px solid #0000;border-top-color:#ffffffe6;border:var(--plyr-menu-arrow-size,4px) solid #0000;border-top-color:var(--plyr-menu-background,#ffffffe6);content:"";height:0;position:absolute;right:14px;right:calc(var(--plyr-control-icon-size, 18px)/2 + var(--plyr-control-spacing, 10px)*.7 - var(--plyr-menu-arrow-size, 4px)/2);top:100%;width:0}.plyr__menu__container [role=menu]{max-height:320px;overflow-y:auto;padding:7px;padding:calc(var(--plyr-control-spacing, 10px)*.7)}.plyr__menu__container [role=menuitem],.plyr__menu__container [role=menuitemradio]{margin-top:2px}.plyr__menu__container [role=menuitem]:first-child,.plyr__menu__container [role=menuitemradio]:first-child{margin-top:0}.plyr__menu__container .plyr__control{align-items:center;color:#4a5464;color:var(--plyr-menu-color,#4a5464);display:flex;font-size:13px;font-size:var(--plyr-font-size-menu,var(--plyr-font-size-small,13px));padding:4.66667px 10.5px;padding:calc(var(--plyr-control-spacing, 10px)*.7/1.5) calc(var(--plyr-control-spacing, 10px)*.7*1.5);-webkit-user-select:none;user-select:none;width:100%}.plyr__menu__container .plyr__control>span{align-items:inherit;display:flex;width:100%}.plyr__menu__container .plyr__control:after{border:4px solid #0000;border:var(--plyr-menu-item-arrow-size,4px) solid #0000;content:"";position:absolute;top:50%;transform:translateY(-50%)}.plyr__menu__container .plyr__control--forward{padding-right:28px;padding-right:calc(var(--plyr-control-spacing, 10px)*.7*4)}.plyr__menu__container .plyr__control--forward:after{border-left-color:#728197;border-left-color:var(--plyr-menu-arrow-color,#728197);right:6.5px;right:calc(var(--plyr-control-spacing, 10px)*.7*1.5 - var(--plyr-menu-item-arrow-size, 4px))}.plyr__menu__container .plyr__control--forward:focus-visible:after,.plyr__menu__container .plyr__control--forward:hover:after{border-left-color:initial}.plyr__menu__container .plyr__control--back{font-weight:400;font-weight:var(--plyr-font-weight-regular,400);margin:7px;margin:calc(var(--plyr-control-spacing, 10px)*.7);margin-bottom:3.5px;margin-bottom:calc(var(--plyr-control-spacing, 10px)*.7/2);padding-left:28px;padding-left:calc(var(--plyr-control-spacing, 10px)*.7*4);position:relative;width:calc(100% - 14px);width:calc(100% - var(--plyr-control-spacing, 10px)*.7*2)}.plyr__menu__container .plyr__control--back:after{border-right-color:#728197;border-right-color:var(--plyr-menu-arrow-color,#728197);left:6.5px;left:calc(var(--plyr-control-spacing, 10px)*.7*1.5 - var(--plyr-menu-item-arrow-size, 4px))}.plyr__menu__container .plyr__control--back:before{background:#dcdfe5;background:var(--plyr-menu-back-border-color,#dcdfe5);box-shadow:0 1px 0 #fff;box-shadow:0 1px 0 var(--plyr-menu-back-border-shadow-color,#fff);content:"";height:1px;left:0;margin-top:3.5px;margin-top:calc(var(--plyr-control-spacing, 10px)*.7/2);overflow:hidden;position:absolute;right:0;top:100%}.plyr__menu__container .plyr__control--back:focus-visible:after,.plyr__menu__container .plyr__control--back:hover:after{border-right-color:initial}.plyr__menu__container .plyr__control[role=menuitemradio]{padding-left:7px;padding-left:calc(var(--plyr-control-spacing, 10px)*.7)}.plyr__menu__container .plyr__control[role=menuitemradio]:after,.plyr__menu__container .plyr__control[role=menuitemradio]:before{border-radius:100%}.plyr__menu__container .plyr__control[role=menuitemradio]:before{background:#0000001a;content:"";display:block;flex-shrink:0;height:16px;margin-right:10px;margin-right:var(--plyr-control-spacing,10px);transition:all .3s ease;width:16px}.plyr__menu__container .plyr__control[role=menuitemradio]:after{background:#fff;border:0;height:6px;left:12px;opacity:0;top:50%;transform:translateY(-50%) scale(0);transition:transform .3s ease,opacity .3s ease;width:6px}.plyr__menu__container .plyr__control[role=menuitemradio][aria-checked=true]:before{background:#00b2ff;background:var(--plyr-control-toggle-checked-background,var(--plyr-color-main,var(--plyr-color-main,#00b2ff)))}.plyr__menu__container .plyr__control[role=menuitemradio][aria-checked=true]:after{opacity:1;transform:translateY(-50%) scale(1)}.plyr__menu__container .plyr__control[role=menuitemradio]:focus-visible:before,.plyr__menu__container .plyr__control[role=menuitemradio]:hover:before{background:#23282f1a}.plyr__menu__container .plyr__menu__value{align-items:center;display:flex;margin-left:auto;margin-right:-5px;margin-right:calc(var(--plyr-control-spacing, 10px)*.7*-1 - -2px);overflow:hidden;padding-left:24.5px;padding-left:calc(var(--plyr-control-spacing, 10px)*.7*3.5);pointer-events:none}.plyr--full-ui input[type=range]{-webkit-appearance:none;appearance:none;background:#0000;border:0;border-radius:26px;border-radius:calc(var(--plyr-range-thumb-height, 13px)*2);color:#00b2ff;color:var(--plyr-range-fill-background,var(--plyr-color-main,var(--plyr-color-main,#00b2ff)));display:block;height:19px;height:calc(var(--plyr-range-thumb-active-shadow-width, 3px)*2 + var(--plyr-range-thumb-height, 13px));margin:0;min-width:0;padding:0;transition:box-shadow .3s ease;width:100%}.plyr--full-ui input[type=range]::-webkit-slider-runnable-track{background:#0000;background-image:linear-gradient(90deg,currentColor 0,#0000 0);background-image:linear-gradient(to right,currentColor var(--value,0),#0000 var(--value,0));border:0;border-radius:2.5px;border-radius:calc(var(--plyr-range-track-height, 5px)/2);height:5px;height:var(--plyr-range-track-height,5px);-webkit-transition:box-shadow .3s ease;transition:box-shadow .3s ease;-webkit-user-select:none;user-select:none}.plyr--full-ui input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;background:#fff;background:var(--plyr-range-thumb-background,#fff);border:0;border-radius:100%;box-shadow:0 1px 1px #23282f26,0 0 0 1px #23282f33;box-shadow:var(--plyr-range-thumb-shadow,0 1px 1px #23282f26,0 0 0 1px #23282f33);height:13px;height:var(--plyr-range-thumb-height,13px);margin-top:-4px;margin-top:calc((var(--plyr-range-thumb-height, 13px) - var(--plyr-range-track-height, 5px))/2*-1);position:relative;-webkit-transition:all .2s ease;transition:all .2s ease;width:13px;width:var(--plyr-range-thumb-height,13px)}.plyr--full-ui input[type=range]::-moz-range-track{background:#0000;border:0;border-radius:2.5px;border-radius:calc(var(--plyr-range-track-height, 5px)/2);height:5px;height:var(--plyr-range-track-height,5px);-moz-transition:box-shadow .3s ease;transition:box-shadow .3s ease;user-select:none}.plyr--full-ui input[type=range]::-moz-range-thumb{background:#fff;background:var(--plyr-range-thumb-background,#fff);border:0;border-radius:100%;box-shadow:0 1px 1px #23282f26,0 0 0 1px #23282f33;box-shadow:var(--plyr-range-thumb-shadow,0 1px 1px #23282f26,0 0 0 1px #23282f33);height:13px;height:var(--plyr-range-thumb-height,13px);position:relative;-moz-transition:all .2s ease;transition:all .2s ease;width:13px;width:var(--plyr-range-thumb-height,13px)}.plyr--full-ui input[type=range]::-moz-range-progress{background:currentColor;border-radius:2.5px;border-radius:calc(var(--plyr-range-track-height, 5px)/2);height:5px;height:var(--plyr-range-track-height,5px)}.plyr--full-ui input[type=range]::-ms-track{color:#0000}.plyr--full-ui input[type=range]::-ms-fill-upper,.plyr--full-ui input[type=range]::-ms-track{background:#0000;border:0;border-radius:2.5px;border-radius:calc(var(--plyr-range-track-height, 5px)/2);height:5px;height:var(--plyr-range-track-height,5px);-ms-transition:box-shadow .3s ease;transition:box-shadow .3s ease;user-select:none}.plyr--full-ui input[type=range]::-ms-fill-lower{background:#0000;background:currentColor;border:0;border-radius:2.5px;border-radius:calc(var(--plyr-range-track-height, 5px)/2);height:5px;height:var(--plyr-range-track-height,5px);-ms-transition:box-shadow .3s ease;transition:box-shadow .3s ease;user-select:none}.plyr--full-ui input[type=range]::-ms-thumb{background:#fff;background:var(--plyr-range-thumb-background,#fff);border:0;border-radius:100%;box-shadow:0 1px 1px #23282f26,0 0 0 1px #23282f33;box-shadow:var(--plyr-range-thumb-shadow,0 1px 1px #23282f26,0 0 0 1px #23282f33);height:13px;height:var(--plyr-range-thumb-height,13px);margin-top:0;position:relative;-ms-transition:all .2s ease;transition:all .2s ease;width:13px;width:var(--plyr-range-thumb-height,13px)}.plyr--full-ui input[type=range]::-ms-tooltip{display:none}.plyr--full-ui input[type=range]::-moz-focus-outer{border:0}.plyr--full-ui input[type=range]:focus{outline:0}.plyr--full-ui input[type=range]:focus-visible::-webkit-slider-runnable-track{outline:2px dashed #00b2ff;outline:2px dashed var(--plyr-focus-visible-color,var(--plyr-color-main,var(--plyr-color-main,#00b2ff)));outline-offset:2px}.plyr--full-ui input[type=range]:focus-visible::-moz-range-track{outline:2px dashed #00b2ff;outline:2px dashed var(--plyr-focus-visible-color,var(--plyr-color-main,var(--plyr-color-main,#00b2ff)));outline-offset:2px}.plyr--full-ui input[type=range]:focus-visible::-ms-track{outline:2px dashed #00b2ff;outline:2px dashed var(--plyr-focus-visible-color,var(--plyr-color-main,var(--plyr-color-main,#00b2ff)));outline-offset:2px}.plyr__poster{background-color:#000;background-color:var(--plyr-video-background,var(--plyr-video-background,#000));background-position:50% 50%;background-repeat:no-repeat;background-size:contain;height:100%;left:0;opacity:0;position:absolute;top:0;transition:opacity .2s ease;width:100%;z-index:1}.plyr--stopped.plyr__poster-enabled .plyr__poster{opacity:1}.plyr--youtube.plyr--paused.plyr__poster-enabled:not(.plyr--stopped) .plyr__poster{display:none}.plyr__time{font-size:13px;font-size:var(--plyr-font-size-time,var(--plyr-font-size-small,13px))}.plyr__time+.plyr__time:before{content:"⁄";margin-right:10px;margin-right:var(--plyr-control-spacing,10px)}@media (max-width:767px){.plyr__time+.plyr__time{display:none}}.plyr__tooltip{background:#fff;background:var(--plyr-tooltip-background,#fff);border-radius:5px;border-radius:var(--plyr-tooltip-radius,5px);bottom:100%;box-shadow:0 1px 2px #00000026;box-shadow:var(--plyr-tooltip-shadow,0 1px 2px #00000026);color:#4a5464;color:var(--plyr-tooltip-color,#4a5464);font-size:13px;font-size:var(--plyr-font-size-small,13px);font-weight:400;font-weight:var(--plyr-font-weight-regular,400);left:50%;line-height:1.3;margin-bottom:10px;margin-bottom:calc(var(--plyr-control-spacing, 10px)/2*2);opacity:0;padding:5px 7.5px;padding:calc(var(--plyr-control-spacing, 10px)/2) calc(var(--plyr-control-spacing, 10px)/2*1.5);pointer-events:none;position:absolute;transform:translate(-50%,10px) scale(.8);transform-origin:50% 100%;transition:transform .2s ease .1s,opacity .2s ease .1s;white-space:nowrap;z-index:2}.plyr__tooltip:before{border-left:4px solid #0000;border-left:var(--plyr-tooltip-arrow-size,4px) solid #0000;border-right:4px solid #0000;border-right:var(--plyr-tooltip-arrow-size,4px) solid #0000;border-top:4px solid #fff;border-top:var(--plyr-tooltip-arrow-size,4px) solid var(--plyr-tooltip-background,#fff);bottom:-4px;bottom:calc(var(--plyr-tooltip-arrow-size, 4px)*-1);content:"";height:0;left:50%;position:absolute;transform:translateX(-50%);width:0;z-index:2}.plyr .plyr__control:focus-visible .plyr__tooltip,.plyr .plyr__control:hover .plyr__tooltip,.plyr__tooltip--visible{opacity:1;transform:translate(-50%) scale(1)}.plyr .plyr__control:hover .plyr__tooltip{z-index:3}.plyr__controls>.plyr__control:first-child .plyr__tooltip,.plyr__controls>.plyr__control:first-child+.plyr__control .plyr__tooltip{left:0;transform:translateY(10px) scale(.8);transform-origin:0 100%}.plyr__controls>.plyr__control:first-child .plyr__tooltip:before,.plyr__controls>.plyr__control:first-child+.plyr__control .plyr__tooltip:before{left:16px;left:calc(var(--plyr-control-icon-size, 18px)/2 + var(--plyr-control-spacing, 10px)*.7)}.plyr__controls>.plyr__control:last-child .plyr__tooltip{left:auto;right:0;transform:translateY(10px) scale(.8);transform-origin:100% 100%}.plyr__controls>.plyr__control:last-child .plyr__tooltip:before{left:auto;right:16px;right:calc(var(--plyr-control-icon-size, 18px)/2 + var(--plyr-control-spacing, 10px)*.7);transform:translateX(50%)}.plyr__controls>.plyr__control:first-child .plyr__tooltip--visible,.plyr__controls>.plyr__control:first-child+.plyr__control .plyr__tooltip--visible,.plyr__controls>.plyr__control:first-child+.plyr__control:focus-visible .plyr__tooltip,.plyr__controls>.plyr__control:first-child+.plyr__control:hover .plyr__tooltip,.plyr__controls>.plyr__control:first-child:focus-visible .plyr__tooltip,.plyr__controls>.plyr__control:first-child:hover .plyr__tooltip,.plyr__controls>.plyr__control:last-child .plyr__tooltip--visible,.plyr__controls>.plyr__control:last-child:focus-visible .plyr__tooltip,.plyr__controls>.plyr__control:last-child:hover .plyr__tooltip{transform:translate(0) scale(1)}.plyr__progress{left:6.5px;left:calc(var(--plyr-range-thumb-height, 13px)*.5);margin-right:13px;margin-right:var(--plyr-range-thumb-height,13px);position:relative}.plyr__progress input[type=range],.plyr__progress__buffer{margin-left:-6.5px;margin-left:calc(var(--plyr-range-thumb-height, 13px)*-.5);margin-right:-6.5px;margin-right:calc(var(--plyr-range-thumb-height, 13px)*-.5);width:calc(100% + 13px);width:calc(100% + var(--plyr-range-thumb-height, 13px))}.plyr__progress input[type=range]{position:relative;z-index:2}.plyr__progress .plyr__tooltip{left:0;max-width:120px;overflow-wrap:break-word}.plyr__progress__buffer{-webkit-appearance:none;background:#0000;border:0;border-radius:100px;height:5px;height:var(--plyr-range-track-height,5px);left:0;margin-top:-2.5px;margin-top:calc(var(--plyr-range-track-height, 5px)/2*-1);padding:0;position:absolute;top:50%}.plyr__progress__buffer::-webkit-progress-bar{background:#0000}.plyr__progress__buffer::-webkit-progress-value{background:currentColor;border-radius:100px;min-width:5px;min-width:var(--plyr-range-track-height,5px);-webkit-transition:width .2s ease;transition:width .2s ease}.plyr__progress__buffer::-moz-progress-bar{background:currentColor;border-radius:100px;min-width:5px;min-width:var(--plyr-range-track-height,5px);-moz-transition:width .2s ease;transition:width .2s ease}.plyr__progress__buffer::-ms-fill{border-radius:100px;-ms-transition:width .2s ease;transition:width .2s ease}.plyr--loading .plyr__progress__buffer{animation:plyr-progress 1s linear infinite;background-image:linear-gradient(-45deg,#23282f99 25%,#0000 0,#0000 50%,#23282f99 0,#23282f99 75%,#0000 0,#0000);background-image:linear-gradient(-45deg,var(--plyr-progress-loading-background,#23282f99) 25%,#0000 25%,#0000 50%,var(--plyr-progress-loading-background,#23282f99) 50%,var(--plyr-progress-loading-background,#23282f99) 75%,#0000 75%,#0000);background-repeat:repeat-x;background-size:25px 25px;background-size:var(--plyr-progress-loading-size,25px) var(--plyr-progress-loading-size,25px);color:#0000}.plyr--video.plyr--loading .plyr__progress__buffer{background-color:#ffffff40;background-color:var(--plyr-video-progress-buffered-background,#ffffff40)}.plyr--audio.plyr--loading .plyr__progress__buffer{background-color:#c1c8d199;background-color:var(--plyr-audio-progress-buffered-background,#c1c8d199)}.plyr__progress__marker{background-color:#fff;background-color:var(--plyr-progress-marker-background,#fff);border-radius:1px;height:5px;height:var(--plyr-range-track-height,5px);position:absolute;top:50%;transform:translate(-50%,-50%);width:3px;width:var(--plyr-progress-marker-width,3px);z-index:3}.plyr__volume{align-items:center;display:flex;position:relative}.plyr__volume input[type=range]{margin-left:5px;margin-left:calc(var(--plyr-control-spacing, 10px)/2);margin-right:5px;margin-right:calc(var(--plyr-control-spacing, 10px)/2);max-width:90px;min-width:60px;position:relative;z-index:2}.plyr--audio{display:block}.plyr--audio .plyr__controls{background:#fff;background:var(--plyr-audio-controls-background,#fff);border-radius:inherit;color:#4a5464;color:var(--plyr-audio-control-color,#4a5464);padding:10px;padding:var(--plyr-control-spacing,10px)}.plyr--audio .plyr__control:focus-visible,.plyr--audio .plyr__control:hover,.plyr--audio .plyr__control[aria-expanded=true]{background:#00b2ff;background:var(--plyr-audio-control-background-hover,var(--plyr-color-main,var(--plyr-color-main,#00b2ff)));color:#fff;color:var(--plyr-audio-control-color-hover,#fff)}.plyr--full-ui.plyr--audio input[type=range]::-webkit-slider-runnable-track{background-color:#c1c8d199;background-color:var(--plyr-audio-range-track-background,var(--plyr-audio-progress-buffered-background,#c1c8d199))}.plyr--full-ui.plyr--audio input[type=range]::-moz-range-track{background-color:#c1c8d199;background-color:var(--plyr-audio-range-track-background,var(--plyr-audio-progress-buffered-background,#c1c8d199))}.plyr--full-ui.plyr--audio input[type=range]::-ms-track{background-color:#c1c8d199;background-color:var(--plyr-audio-range-track-background,var(--plyr-audio-progress-buffered-background,#c1c8d199))}.plyr--full-ui.plyr--audio input[type=range]:active::-webkit-slider-thumb{box-shadow:0 1px 1px #23282f26,0 0 0 1px #23282f33,0 0 0 3px #23282f1a;box-shadow:var(--plyr-range-thumb-shadow,0 1px 1px #23282f26,0 0 0 1px #23282f33),0 0 0 var(--plyr-range-thumb-active-shadow-width,3px) var(--plyr-audio-range-thumb-active-shadow-color,#23282f1a)}.plyr--full-ui.plyr--audio input[type=range]:active::-moz-range-thumb{box-shadow:0 1px 1px #23282f26,0 0 0 1px #23282f33,0 0 0 3px #23282f1a;box-shadow:var(--plyr-range-thumb-shadow,0 1px 1px #23282f26,0 0 0 1px #23282f33),0 0 0 var(--plyr-range-thumb-active-shadow-width,3px) var(--plyr-audio-range-thumb-active-shadow-color,#23282f1a)}.plyr--full-ui.plyr--audio input[type=range]:active::-ms-thumb{box-shadow:0 1px 1px #23282f26,0 0 0 1px #23282f33,0 0 0 3px #23282f1a;box-shadow:var(--plyr-range-thumb-shadow,0 1px 1px #23282f26,0 0 0 1px #23282f33),0 0 0 var(--plyr-range-thumb-active-shadow-width,3px) var(--plyr-audio-range-thumb-active-shadow-color,#23282f1a)}.plyr--audio .plyr__progress__buffer{color:#c1c8d199;color:var(--plyr-audio-progress-buffered-background,#c1c8d199)}.plyr--video{overflow:hidden}.plyr--video.plyr--menu-open{overflow:visible}.plyr__video-wrapper{background:#000;background:var(--plyr-video-background,var(--plyr-video-background,#000));border-radius:inherit;height:100%;margin:auto;overflow:hidden;position:relative;width:100%}.plyr__video-embed,.plyr__video-wrapper--fixed-ratio{aspect-ratio:16/9}@supports not (aspect-ratio:16/9){.plyr__video-embed,.plyr__video-wrapper--fixed-ratio{height:0;padding-bottom:56.25%;position:relative}}.plyr__video-embed iframe,.plyr__video-wrapper--fixed-ratio video{border:0;height:100%;left:0;position:absolute;top:0;width:100%}.plyr--full-ui .plyr__video-embed>.plyr__video-embed__container{padding-bottom:240%;position:relative;transform:translateY(-38.28125%)}.plyr--video .plyr__controls{background:linear-gradient(#0000,#000c);background:var(--plyr-video-controls-background,linear-gradient(#0000,#000c));border-bottom-left-radius:inherit;border-bottom-right-radius:inherit;bottom:0;color:#fff;color:var(--plyr-video-control-color,#fff);left:0;padding:5px;padding:calc(var(--plyr-control-spacing, 10px)/2);padding-top:20px;padding-top:calc(var(--plyr-control-spacing, 10px)*2);position:absolute;right:0;transition:opacity .4s ease-in-out,transform .4s ease-in-out;z-index:3}@media (min-width:480px){.plyr--video .plyr__controls{padding:10px;padding:var(--plyr-control-spacing,10px);padding-top:35px;padding-top:calc(var(--plyr-control-spacing, 10px)*3.5)}}.plyr--video.plyr--hide-controls .plyr__controls{opacity:0;pointer-events:none;transform:translateY(100%)}.plyr--video .plyr__control:focus-visible,.plyr--video .plyr__control:hover,.plyr--video .plyr__control[aria-expanded=true]{background:#00b2ff;background:var(--plyr-video-control-background-hover,var(--plyr-color-main,var(--plyr-color-main,#00b2ff)));color:#fff;color:var(--plyr-video-control-color-hover,#fff)}.plyr__control--overlaid{background:#00b2ff;background:var(--plyr-video-control-background-hover,var(--plyr-color-main,var(--plyr-color-main,#00b2ff)));border:0;border-radius:100%;color:#fff;color:var(--plyr-video-control-color,#fff);display:none;left:50%;opacity:.9;padding:15px;padding:calc(var(--plyr-control-spacing, 10px)*1.5);position:absolute;top:50%;transform:translate(-50%,-50%);transition:.3s;z-index:2}.plyr__control--overlaid svg{left:2px;position:relative}.plyr__control--overlaid:focus,.plyr__control--overlaid:hover{opacity:1}.plyr--playing .plyr__control--overlaid{opacity:0;visibility:hidden}.plyr--full-ui.plyr--video .plyr__control--overlaid{display:block}.plyr--full-ui.plyr--video input[type=range]::-webkit-slider-runnable-track{background-color:#ffffff40;background-color:var(--plyr-video-range-track-background,var(--plyr-video-progress-buffered-background,#ffffff40))}.plyr--full-ui.plyr--video input[type=range]::-moz-range-track{background-color:#ffffff40;background-color:var(--plyr-video-range-track-background,var(--plyr-video-progress-buffered-background,#ffffff40))}.plyr--full-ui.plyr--video input[type=range]::-ms-track{background-color:#ffffff40;background-color:var(--plyr-video-range-track-background,var(--plyr-video-progress-buffered-background,#ffffff40))}.plyr--full-ui.plyr--video input[type=range]:active::-webkit-slider-thumb{box-shadow:0 1px 1px #23282f26,0 0 0 1px #23282f33,0 0 0 3px #ffffff80;box-shadow:var(--plyr-range-thumb-shadow,0 1px 1px #23282f26,0 0 0 1px #23282f33),0 0 0 var(--plyr-range-thumb-active-shadow-width,3px) var(--plyr-audio-range-thumb-active-shadow-color,#ffffff80)}.plyr--full-ui.plyr--video input[type=range]:active::-moz-range-thumb{box-shadow:0 1px 1px #23282f26,0 0 0 1px #23282f33,0 0 0 3px #ffffff80;box-shadow:var(--plyr-range-thumb-shadow,0 1px 1px #23282f26,0 0 0 1px #23282f33),0 0 0 var(--plyr-range-thumb-active-shadow-width,3px) var(--plyr-audio-range-thumb-active-shadow-color,#ffffff80)}.plyr--full-ui.plyr--video input[type=range]:active::-ms-thumb{box-shadow:0 1px 1px #23282f26,0 0 0 1px #23282f33,0 0 0 3px #ffffff80;box-shadow:var(--plyr-range-thumb-shadow,0 1px 1px #23282f26,0 0 0 1px #23282f33),0 0 0 var(--plyr-range-thumb-active-shadow-width,3px) var(--plyr-audio-range-thumb-active-shadow-color,#ffffff80)}.plyr--video .plyr__progress__buffer{color:#ffffff40;color:var(--plyr-video-progress-buffered-background,#ffffff40)}.plyr:fullscreen{background:#000;border-radius:0!important;height:100%;margin:0;width:100%}.plyr:fullscreen video{height:100%}.plyr:fullscreen .plyr__control .icon--exit-fullscreen{display:block}.plyr:fullscreen .plyr__control .icon--exit-fullscreen+svg{display:none}.plyr:fullscreen.plyr--hide-controls{cursor:none}@media (min-width:1024px){.plyr:fullscreen .plyr__captions{font-size:21px;font-size:var(--plyr-font-size-xlarge,21px)}}.plyr--fullscreen-fallback{background:#000;border-radius:0!important;bottom:0;height:100%;left:0;margin:0;position:fixed;right:0;top:0;width:100%;z-index:10000000}.plyr--fullscreen-fallback video{height:100%}.plyr--fullscreen-fallback .plyr__control .icon--exit-fullscreen{display:block}.plyr--fullscreen-fallback .plyr__control .icon--exit-fullscreen+svg{display:none}.plyr--fullscreen-fallback.plyr--hide-controls{cursor:none}@media (min-width:1024px){.plyr--fullscreen-fallback .plyr__captions{font-size:21px;font-size:var(--plyr-font-size-xlarge,21px)}}.plyr__ads{border-radius:inherit;bottom:0;cursor:pointer;left:0;overflow:hidden;position:absolute;right:0;top:0;z-index:-1}.plyr__ads>div,.plyr__ads>div iframe{height:100%;position:absolute;width:100%}.plyr__ads:after{background:#23282f;border-radius:2px;bottom:10px;bottom:var(--plyr-control-spacing,10px);color:#fff;content:attr(data-badge-text);font-size:11px;padding:2px 6px;pointer-events:none;position:absolute;right:10px;right:var(--plyr-control-spacing,10px);z-index:3}.plyr__ads:empty:after{display:none}.plyr__cues{background:currentColor;display:block;height:5px;height:var(--plyr-range-track-height,5px);left:0;opacity:.8;position:absolute;top:50%;transform:translateY(-50%);width:3px;z-index:3}.plyr__preview-thumb{background-color:#fff;background-color:var(--plyr-tooltip-background,#fff);border-radius:8px;border-radius:var(--plyr-menu-radius,8px);bottom:100%;box-shadow:0 1px 2px #00000026;box-shadow:var(--plyr-tooltip-shadow,0 1px 2px #00000026);margin-bottom:10px;margin-bottom:calc(var(--plyr-control-spacing, 10px)/2*2);opacity:0;padding:3px;pointer-events:none;position:absolute;transform:translateY(10px) scale(.8);transform-origin:50% 100%;transition:transform .2s ease .1s,opacity .2s ease .1s;z-index:2}.plyr__preview-thumb--is-shown{opacity:1;transform:translate(0) scale(1)}.plyr__preview-thumb:before{border-left:4px solid #0000;border-left:var(--plyr-tooltip-arrow-size,4px) solid #0000;border-right:4px solid #0000;border-right:var(--plyr-tooltip-arrow-size,4px) solid #0000;border-top:4px solid #fff;border-top:var(--plyr-tooltip-arrow-size,4px) solid var(--plyr-tooltip-background,#fff);bottom:-4px;bottom:calc(var(--plyr-tooltip-arrow-size, 4px)*-1);content:"";height:0;left:calc(50% + var(--preview-arrow-offset));position:absolute;transform:translateX(-50%);width:0;z-index:2}.plyr__preview-thumb__image-container{background:#c1c8d1;border-radius:7px;border-radius:calc(var(--plyr-menu-radius, 8px) - 1px);overflow:hidden;position:relative;z-index:0}.plyr__preview-thumb__image-container img,.plyr__preview-thumb__image-container:after{height:100%;left:0;position:absolute;top:0;width:100%}.plyr__preview-thumb__image-container:after{border-radius:inherit;box-shadow:inset 0 0 0 1px #00000026;content:"";pointer-events:none}.plyr__preview-thumb__image-container img{max-height:none;max-width:none}.plyr__preview-thumb__time-container{background:linear-gradient(#0000,#000000bf);background:var(--plyr-video-controls-background,linear-gradient(#0000,#000000bf));border-bottom-left-radius:7px;border-bottom-left-radius:calc(var(--plyr-menu-radius, 8px) - 1px);border-bottom-right-radius:7px;border-bottom-right-radius:calc(var(--plyr-menu-radius, 8px) - 1px);bottom:0;left:0;line-height:1.1;padding:20px 6px 6px;position:absolute;right:0;z-index:3}.plyr__preview-thumb__time-container span{color:#fff;font-size:13px;font-size:var(--plyr-font-size-time,var(--plyr-font-size-small,13px))}.plyr__preview-scrubbing{bottom:0;filter:blur(1px);height:100%;left:0;margin:auto;opacity:0;overflow:hidden;pointer-events:none;position:absolute;right:0;top:0;transition:opacity .3s ease;width:100%;z-index:1}.plyr__preview-scrubbing--is-shown{opacity:1}.plyr__preview-scrubbing img{height:100%;left:0;max-height:none;max-width:none;object-fit:contain;position:absolute;top:0;width:100%}.plyr--no-transition{transition:none!important}.plyr__sr-only{clip:rect(1px,1px,1px,1px);border:0!important;height:1px!important;overflow:hidden;padding:0!important;position:absolute!important;width:1px!important}.plyr [hidden]{display:none!important} \ No newline at end of file diff --git a/youtube/static/modules/plyr/plyr.js b/youtube/static/modules/plyr/plyr.js index d5cc84e..7d571e5 100644 --- a/youtube/static/modules/plyr/plyr.js +++ b/youtube/static/modules/plyr/plyr.js @@ -1,32 +1,25 @@ typeof navigator === "object" && (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define('Plyr', factory) : - (global = global || self, global.Plyr = factory()); -}(this, (function () { 'use strict'; - - function _classCallCheck(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } - } - - function _defineProperties(target, props) { - for (var i = 0; i < props.length; i++) { - var descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ("value" in descriptor) descriptor.writable = true; - Object.defineProperty(target, descriptor.key, descriptor); + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Plyr = factory()); +})(this, (function () { 'use strict'; + + function _toPrimitive(t, r) { + if ("object" != typeof t || !t) return t; + var e = t[Symbol.toPrimitive]; + if (void 0 !== e) { + var i = e.call(t, r || "default"); + if ("object" != typeof i) return i; + throw new TypeError("@@toPrimitive must return a primitive value."); } + return ("string" === r ? String : Number)(t); } - - function _createClass(Constructor, protoProps, staticProps) { - if (protoProps) _defineProperties(Constructor.prototype, protoProps); - if (staticProps) _defineProperties(Constructor, staticProps); - return Constructor; + function _toPropertyKey(t) { + var i = _toPrimitive(t, "string"); + return "symbol" == typeof i ? i : String(i); } - - function _defineProperty(obj, key, value) { + function _defineProperty$1(obj, key, value) { + key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, @@ -37,168 +30,22 @@ typeof navigator === "object" && (function (global, factory) { } else { obj[key] = value; } - return obj; } - function ownKeys(object, enumerableOnly) { - var keys = Object.keys(object); - - if (Object.getOwnPropertySymbols) { - var symbols = Object.getOwnPropertySymbols(object); - if (enumerableOnly) symbols = symbols.filter(function (sym) { - return Object.getOwnPropertyDescriptor(object, sym).enumerable; - }); - keys.push.apply(keys, symbols); - } - - return keys; - } - - function _objectSpread2(target) { - for (var i = 1; i < arguments.length; i++) { - var source = arguments[i] != null ? arguments[i] : {}; - - if (i % 2) { - ownKeys(Object(source), true).forEach(function (key) { - _defineProperty(target, key, source[key]); - }); - } else if (Object.getOwnPropertyDescriptors) { - Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); - } else { - ownKeys(Object(source)).forEach(function (key) { - Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); - }); - } - } - - return target; - } - - function _objectWithoutPropertiesLoose(source, excluded) { - if (source == null) return {}; - var target = {}; - var sourceKeys = Object.keys(source); - var key, i; - - for (i = 0; i < sourceKeys.length; i++) { - key = sourceKeys[i]; - if (excluded.indexOf(key) >= 0) continue; - target[key] = source[key]; - } - - return target; - } - - function _objectWithoutProperties(source, excluded) { - if (source == null) return {}; - - var target = _objectWithoutPropertiesLoose(source, excluded); - - var key, i; - - if (Object.getOwnPropertySymbols) { - var sourceSymbolKeys = Object.getOwnPropertySymbols(source); - - for (i = 0; i < sourceSymbolKeys.length; i++) { - key = sourceSymbolKeys[i]; - if (excluded.indexOf(key) >= 0) continue; - if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; - target[key] = source[key]; - } - } - - return target; - } - - function _slicedToArray(arr, i) { - return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); - } - - function _toConsumableArray(arr) { - return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); - } - - function _arrayWithoutHoles(arr) { - if (Array.isArray(arr)) return _arrayLikeToArray(arr); - } - - function _arrayWithHoles(arr) { - if (Array.isArray(arr)) return arr; - } - - function _iterableToArray(iter) { - if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); - } - - function _iterableToArrayLimit(arr, i) { - if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; - var _arr = []; - var _n = true; - var _d = false; - var _e = undefined; - - try { - for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { - _arr.push(_s.value); - - if (i && _arr.length === i) break; - } - } catch (err) { - _d = true; - _e = err; - } finally { - try { - if (!_n && _i["return"] != null) _i["return"](); - } finally { - if (_d) throw _e; - } - } - - return _arr; - } - - function _unsupportedIterableToArray(o, minLen) { - if (!o) return; - if (typeof o === "string") return _arrayLikeToArray(o, minLen); - var n = Object.prototype.toString.call(o).slice(8, -1); - if (n === "Object" && o.constructor) n = o.constructor.name; - if (n === "Map" || n === "Set") return Array.from(o); - if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); - } - - function _arrayLikeToArray(arr, len) { - if (len == null || len > arr.length) len = arr.length; - - for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; - - return arr2; - } - - function _nonIterableSpread() { - throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); - } - - function _nonIterableRest() { - throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); - } - - function _classCallCheck$1(e, t) { + function _classCallCheck(e, t) { if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function"); } - - function _defineProperties$1(e, t) { + function _defineProperties(e, t) { for (var n = 0; n < t.length; n++) { var r = t[n]; r.enumerable = r.enumerable || !1, r.configurable = !0, "value" in r && (r.writable = !0), Object.defineProperty(e, r.key, r); } } - - function _createClass$1(e, t, n) { - return t && _defineProperties$1(e.prototype, t), n && _defineProperties$1(e, n), e; + function _createClass(e, t, n) { + return t && _defineProperties(e.prototype, t), n && _defineProperties(e, n), e; } - - function _defineProperty$1(e, t, n) { + function _defineProperty(e, t, n) { return t in e ? Object.defineProperty(e, t, { value: n, enumerable: !0, @@ -206,45 +53,37 @@ typeof navigator === "object" && (function (global, factory) { writable: !0 }) : e[t] = n, e; } - - function ownKeys$1(e, t) { + function ownKeys(e, t) { var n = Object.keys(e); - if (Object.getOwnPropertySymbols) { var r = Object.getOwnPropertySymbols(e); t && (r = r.filter(function (t) { return Object.getOwnPropertyDescriptor(e, t).enumerable; })), n.push.apply(n, r); } - return n; } - - function _objectSpread2$1(e) { + function _objectSpread2(e) { for (var t = 1; t < arguments.length; t++) { var n = null != arguments[t] ? arguments[t] : {}; - t % 2 ? ownKeys$1(Object(n), !0).forEach(function (t) { - _defineProperty$1(e, t, n[t]); - }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(n)) : ownKeys$1(Object(n)).forEach(function (t) { + t % 2 ? ownKeys(Object(n), !0).forEach(function (t) { + _defineProperty(e, t, n[t]); + }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(n)) : ownKeys(Object(n)).forEach(function (t) { Object.defineProperty(e, t, Object.getOwnPropertyDescriptor(n, t)); }); } - return e; } - - var defaults = { + var defaults$1 = { addCSS: !0, thumbWidth: 15, watch: !0 }; - - function matches(e, t) { + function matches$1(e, t) { return function () { return Array.from(document.querySelectorAll(t)).includes(this); }.call(e, t); } - function trigger(e, t) { if (e && t) { var n = new Event(t, { @@ -253,94 +92,88 @@ typeof navigator === "object" && (function (global, factory) { e.dispatchEvent(n); } } - - var getConstructor = function getConstructor(e) { - return null != e ? e.constructor : null; - }, - instanceOf = function instanceOf(e, t) { - return !!(e && t && e instanceof t); - }, - isNullOrUndefined = function isNullOrUndefined(e) { - return null == e; - }, - isObject = function isObject(e) { - return getConstructor(e) === Object; - }, - isNumber = function isNumber(e) { - return getConstructor(e) === Number && !Number.isNaN(e); - }, - isString = function isString(e) { - return getConstructor(e) === String; - }, - isBoolean = function isBoolean(e) { - return getConstructor(e) === Boolean; - }, - isFunction = function isFunction(e) { - return getConstructor(e) === Function; - }, - isArray = function isArray(e) { - return Array.isArray(e); - }, - isNodeList = function isNodeList(e) { - return instanceOf(e, NodeList); - }, - isElement = function isElement(e) { - return instanceOf(e, Element); - }, - isEvent = function isEvent(e) { - return instanceOf(e, Event); - }, - isEmpty = function isEmpty(e) { - return isNullOrUndefined(e) || (isString(e) || isArray(e) || isNodeList(e)) && !e.length || isObject(e) && !Object.keys(e).length; - }, - is = { - nullOrUndefined: isNullOrUndefined, - object: isObject, - number: isNumber, - string: isString, - boolean: isBoolean, - function: isFunction, - array: isArray, - nodeList: isNodeList, - element: isElement, - event: isEvent, - empty: isEmpty - }; - + var getConstructor$1 = function (e) { + return null != e ? e.constructor : null; + }, + instanceOf$1 = function (e, t) { + return !!(e && t && e instanceof t); + }, + isNullOrUndefined$1 = function (e) { + return null == e; + }, + isObject$1 = function (e) { + return getConstructor$1(e) === Object; + }, + isNumber$1 = function (e) { + return getConstructor$1(e) === Number && !Number.isNaN(e); + }, + isString$1 = function (e) { + return getConstructor$1(e) === String; + }, + isBoolean$1 = function (e) { + return getConstructor$1(e) === Boolean; + }, + isFunction$1 = function (e) { + return getConstructor$1(e) === Function; + }, + isArray$1 = function (e) { + return Array.isArray(e); + }, + isNodeList$1 = function (e) { + return instanceOf$1(e, NodeList); + }, + isElement$1 = function (e) { + return instanceOf$1(e, Element); + }, + isEvent$1 = function (e) { + return instanceOf$1(e, Event); + }, + isEmpty$1 = function (e) { + return isNullOrUndefined$1(e) || (isString$1(e) || isArray$1(e) || isNodeList$1(e)) && !e.length || isObject$1(e) && !Object.keys(e).length; + }, + is$1 = { + nullOrUndefined: isNullOrUndefined$1, + object: isObject$1, + number: isNumber$1, + string: isString$1, + boolean: isBoolean$1, + function: isFunction$1, + array: isArray$1, + nodeList: isNodeList$1, + element: isElement$1, + event: isEvent$1, + empty: isEmpty$1 + }; function getDecimalPlaces(e) { var t = "".concat(e).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/); return t ? Math.max(0, (t[1] ? t[1].length : 0) - (t[2] ? +t[2] : 0)) : 0; } - function round(e, t) { if (1 > t) { var n = getDecimalPlaces(t); return parseFloat(e.toFixed(n)); } - return Math.round(e / t) * t; } - var RangeTouch = function () { function e(t, n) { - _classCallCheck$1(this, e), is.element(t) ? this.element = t : is.string(t) && (this.element = document.querySelector(t)), is.element(this.element) && is.empty(this.element.rangeTouch) && (this.config = _objectSpread2$1({}, defaults, {}, n), this.init()); + _classCallCheck(this, e), is$1.element(t) ? this.element = t : is$1.string(t) && (this.element = document.querySelector(t)), is$1.element(this.element) && is$1.empty(this.element.rangeTouch) && (this.config = _objectSpread2({}, defaults$1, {}, n), this.init()); } - - return _createClass$1(e, [{ + return _createClass(e, [{ key: "init", - value: function value() { + value: function () { e.enabled && (this.config.addCSS && (this.element.style.userSelect = "none", this.element.style.webKitUserSelect = "none", this.element.style.touchAction = "manipulation"), this.listeners(!0), this.element.rangeTouch = this); } }, { key: "destroy", - value: function value() { + value: function () { e.enabled && (this.config.addCSS && (this.element.style.userSelect = "", this.element.style.webKitUserSelect = "", this.element.style.touchAction = ""), this.listeners(!1), this.element.rangeTouch = null); } }, { key: "listeners", - value: function value(e) { + value: function (e) { var t = this, - n = e ? "addEventListener" : "removeEventListener"; + n = e ? "addEventListener" : "removeEventListener"; ["touchstart", "touchmove", "touchend"].forEach(function (e) { t.element[n](e, function (e) { return t.set(e); @@ -349,37 +182,35 @@ typeof navigator === "object" && (function (global, factory) { } }, { key: "get", - value: function value(t) { - if (!e.enabled || !is.event(t)) return null; + value: function (t) { + if (!e.enabled || !is$1.event(t)) return null; var n, - r = t.target, - i = t.changedTouches[0], - o = parseFloat(r.getAttribute("min")) || 0, - s = parseFloat(r.getAttribute("max")) || 100, - u = parseFloat(r.getAttribute("step")) || 1, - c = r.getBoundingClientRect(), - a = 100 / c.width * (this.config.thumbWidth / 2) / 100; + r = t.target, + i = t.changedTouches[0], + o = parseFloat(r.getAttribute("min")) || 0, + s = parseFloat(r.getAttribute("max")) || 100, + u = parseFloat(r.getAttribute("step")) || 1, + c = r.getBoundingClientRect(), + a = 100 / c.width * (this.config.thumbWidth / 2) / 100; return 0 > (n = 100 / c.width * (i.clientX - c.left)) ? n = 0 : 100 < n && (n = 100), 50 > n ? n -= (100 - 2 * n) * a : 50 < n && (n += 2 * (n - 50) * a), o + round(n / 100 * (s - o), u); } }, { key: "set", - value: function value(t) { - e.enabled && is.event(t) && !t.target.disabled && (t.preventDefault(), t.target.value = this.get(t), trigger(t.target, "touchend" === t.type ? "change" : "input")); + value: function (t) { + e.enabled && is$1.event(t) && !t.target.disabled && (t.preventDefault(), t.target.value = this.get(t), trigger(t.target, "touchend" === t.type ? "change" : "input")); } }], [{ key: "setup", - value: function value(t) { + value: function (t) { var n = 1 < arguments.length && void 0 !== arguments[1] ? arguments[1] : {}, - r = null; - if (is.empty(t) || is.string(t) ? r = Array.from(document.querySelectorAll(is.string(t) ? t : 'input[type="range"]')) : is.element(t) ? r = [t] : is.nodeList(t) ? r = Array.from(t) : is.array(t) && (r = t.filter(is.element)), is.empty(r)) return null; - - var i = _objectSpread2$1({}, defaults, {}, n); - - if (is.string(t) && i.watch) { + r = null; + if (is$1.empty(t) || is$1.string(t) ? r = Array.from(document.querySelectorAll(is$1.string(t) ? t : 'input[type="range"]')) : is$1.element(t) ? r = [t] : is$1.nodeList(t) ? r = Array.from(t) : is$1.array(t) && (r = t.filter(is$1.element)), is$1.empty(r)) return null; + var i = _objectSpread2({}, defaults$1, {}, n); + if (is$1.string(t) && i.watch) { var o = new MutationObserver(function (n) { Array.from(n).forEach(function (n) { Array.from(n.addedNodes).forEach(function (n) { - is.element(n) && matches(n, t) && new e(n, i); + is$1.element(n) && matches$1(n, t) && new e(n, i); }); }); }); @@ -388,14 +219,13 @@ typeof navigator === "object" && (function (global, factory) { subtree: !0 }); } - return r.map(function (t) { return new e(t, n); }); } }, { key: "enabled", - get: function get() { + get: function () { return "ontouchstart" in document.documentElement; } }]), e; @@ -404,153 +234,99 @@ typeof navigator === "object" && (function (global, factory) { // ========================================================================== // Type checking utils // ========================================================================== - var getConstructor$1 = function getConstructor(input) { - return input !== null && typeof input !== 'undefined' ? input.constructor : null; - }; - - var instanceOf$1 = function instanceOf(input, constructor) { - return Boolean(input && constructor && input instanceof constructor); - }; - - var isNullOrUndefined$1 = function isNullOrUndefined(input) { - return input === null || typeof input === 'undefined'; - }; - - var isObject$1 = function isObject(input) { - return getConstructor$1(input) === Object; - }; - - var isNumber$1 = function isNumber(input) { - return getConstructor$1(input) === Number && !Number.isNaN(input); - }; - - var isString$1 = function isString(input) { - return getConstructor$1(input) === String; - }; - - var isBoolean$1 = function isBoolean(input) { - return getConstructor$1(input) === Boolean; - }; - var isFunction$1 = function isFunction(input) { - return getConstructor$1(input) === Function; - }; - - var isArray$1 = function isArray(input) { - return Array.isArray(input); - }; - - var isWeakMap = function isWeakMap(input) { - return instanceOf$1(input, WeakMap); - }; - - var isNodeList$1 = function isNodeList(input) { - return instanceOf$1(input, NodeList); - }; - - var isElement$1 = function isElement(input) { - return instanceOf$1(input, Element); - }; - - var isTextNode = function isTextNode(input) { - return getConstructor$1(input) === Text; - }; - - var isEvent$1 = function isEvent(input) { - return instanceOf$1(input, Event); - }; - - var isKeyboardEvent = function isKeyboardEvent(input) { - return instanceOf$1(input, KeyboardEvent); - }; - - var isCue = function isCue(input) { - return instanceOf$1(input, window.TextTrackCue) || instanceOf$1(input, window.VTTCue); - }; - - var isTrack = function isTrack(input) { - return instanceOf$1(input, TextTrack) || !isNullOrUndefined$1(input) && isString$1(input.kind); - }; - - var isPromise = function isPromise(input) { - return instanceOf$1(input, Promise) && isFunction$1(input.then); - }; - - var isEmpty$1 = function isEmpty(input) { - return isNullOrUndefined$1(input) || (isString$1(input) || isArray$1(input) || isNodeList$1(input)) && !input.length || isObject$1(input) && !Object.keys(input).length; - }; - - var isUrl = function isUrl(input) { + const getConstructor = input => input !== null && typeof input !== 'undefined' ? input.constructor : null; + const instanceOf = (input, constructor) => Boolean(input && constructor && input instanceof constructor); + const isNullOrUndefined = input => input === null || typeof input === 'undefined'; + const isObject = input => getConstructor(input) === Object; + const isNumber = input => getConstructor(input) === Number && !Number.isNaN(input); + const isString = input => getConstructor(input) === String; + const isBoolean = input => getConstructor(input) === Boolean; + const isFunction = input => typeof input === 'function'; + const isArray = input => Array.isArray(input); + const isWeakMap = input => instanceOf(input, WeakMap); + const isNodeList = input => instanceOf(input, NodeList); + const isTextNode = input => getConstructor(input) === Text; + const isEvent = input => instanceOf(input, Event); + const isKeyboardEvent = input => instanceOf(input, KeyboardEvent); + const isCue = input => instanceOf(input, window.TextTrackCue) || instanceOf(input, window.VTTCue); + const isTrack = input => instanceOf(input, TextTrack) || !isNullOrUndefined(input) && isString(input.kind); + const isPromise = input => instanceOf(input, Promise) && isFunction(input.then); + const isElement = input => input !== null && typeof input === 'object' && input.nodeType === 1 && typeof input.style === 'object' && typeof input.ownerDocument === 'object'; + const isEmpty = input => isNullOrUndefined(input) || (isString(input) || isArray(input) || isNodeList(input)) && !input.length || isObject(input) && !Object.keys(input).length; + const isUrl = input => { // Accept a URL object - if (instanceOf$1(input, window.URL)) { + if (instanceOf(input, window.URL)) { return true; - } // Must be string from here - + } - if (!isString$1(input)) { + // Must be string from here + if (!isString(input)) { return false; - } // Add the protocol if required - - - var string = input; + } + // Add the protocol if required + let string = input; if (!input.startsWith('http://') || !input.startsWith('https://')) { - string = "http://".concat(input); + string = `http://${input}`; } - try { - return !isEmpty$1(new URL(string).hostname); - } catch (e) { + return !isEmpty(new URL(string).hostname); + } catch (_) { return false; } }; - - var is$1 = { - nullOrUndefined: isNullOrUndefined$1, - object: isObject$1, - number: isNumber$1, - string: isString$1, - boolean: isBoolean$1, - function: isFunction$1, - array: isArray$1, + var is = { + nullOrUndefined: isNullOrUndefined, + object: isObject, + number: isNumber, + string: isString, + boolean: isBoolean, + function: isFunction, + array: isArray, weakMap: isWeakMap, - nodeList: isNodeList$1, - element: isElement$1, + nodeList: isNodeList, + element: isElement, textNode: isTextNode, - event: isEvent$1, + event: isEvent, keyboardEvent: isKeyboardEvent, cue: isCue, track: isTrack, promise: isPromise, url: isUrl, - empty: isEmpty$1 + empty: isEmpty }; // ========================================================================== - var transitionEndEvent = function () { - var element = document.createElement('span'); - var events = { + // Animation utils + // ========================================================================== + + const transitionEndEvent = (() => { + const element = document.createElement('span'); + const events = { WebkitTransition: 'webkitTransitionEnd', MozTransition: 'transitionend', OTransition: 'oTransitionEnd otransitionend', transition: 'transitionend' }; - var type = Object.keys(events).find(function (event) { - return element.style[event] !== undefined; - }); - return is$1.string(type) ? events[type] : false; - }(); // Force repaint of element + const type = Object.keys(events).find(event => element.style[event] !== undefined); + return is.string(type) ? events[type] : false; + })(); + // Force repaint of element function repaint(element, delay) { - setTimeout(function () { + setTimeout(() => { try { // eslint-disable-next-line no-param-reassign - element.hidden = true; // eslint-disable-next-line no-unused-expressions + element.hidden = true; - element.offsetHeight; // eslint-disable-next-line no-param-reassign + // eslint-disable-next-line no-unused-expressions + element.offsetHeight; + // eslint-disable-next-line no-param-reassign element.hidden = false; - } catch (e) {// Do nothing + } catch (_) { + // Do nothing } }, delay); } @@ -559,212 +335,202 @@ typeof navigator === "object" && (function (global, factory) { // Browser sniffing // Unfortunately, due to mixed support, UA sniffing is required // ========================================================================== + + const isIE = Boolean(window.document.documentMode); + const isEdge = /Edge/g.test(navigator.userAgent); + const isWebKit = 'WebkitAppearance' in document.documentElement.style && !/Edge/g.test(navigator.userAgent); + const isIPhone = /iPhone|iPod/gi.test(navigator.userAgent) && navigator.maxTouchPoints > 1; + // navigator.platform may be deprecated but this check is still required + const isIPadOS = navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1; + const isIos = /iPad|iPhone|iPod/gi.test(navigator.userAgent) && navigator.maxTouchPoints > 1; var browser = { - isIE: - /* @cc_on!@ */ - !!document.documentMode, - isEdge: window.navigator.userAgent.includes('Edge'), - isWebkit: 'WebkitAppearance' in document.documentElement.style && !/Edge/.test(navigator.userAgent), - isIPhone: /(iPhone|iPod)/gi.test(navigator.platform), - isIos: /(iPad|iPhone|iPod)/gi.test(navigator.platform) + isIE, + isEdge, + isWebKit, + isIPhone, + isIPadOS, + isIos }; + // ========================================================================== + // Object utils + // ========================================================================== + + + // Clone nested objects function cloneDeep(object) { return JSON.parse(JSON.stringify(object)); - } // Get a nested value in an object + } + // Get a nested value in an object function getDeep(object, path) { - return path.split('.').reduce(function (obj, key) { - return obj && obj[key]; - }, object); - } // Deep extend destination object with N more objects - - function extend() { - var target = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - - for (var _len = arguments.length, sources = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - sources[_key - 1] = arguments[_key]; - } + return path.split('.').reduce((obj, key) => obj && obj[key], object); + } + // Deep extend destination object with N more objects + function extend(target = {}, ...sources) { if (!sources.length) { return target; } - - var source = sources.shift(); - - if (!is$1.object(source)) { + const source = sources.shift(); + if (!is.object(source)) { return target; } - - Object.keys(source).forEach(function (key) { - if (is$1.object(source[key])) { + Object.keys(source).forEach(key => { + if (is.object(source[key])) { if (!Object.keys(target).includes(key)) { - Object.assign(target, _defineProperty({}, key, {})); + Object.assign(target, { + [key]: {} + }); } - extend(target[key], source[key]); } else { - Object.assign(target, _defineProperty({}, key, source[key])); + Object.assign(target, { + [key]: source[key] + }); } }); - return extend.apply(void 0, [target].concat(sources)); + return extend(target, ...sources); } + // ========================================================================== + // Element utils + // ========================================================================== + + + // Wrap an element function wrap(elements, wrapper) { // Convert `elements` to an array, if necessary. - var targets = elements.length ? elements : [elements]; // Loops backwards to prevent having to clone the wrapper on the - // first element (see `child` below). + const targets = elements.length ? elements : [elements]; - Array.from(targets).reverse().forEach(function (element, index) { - var child = index > 0 ? wrapper.cloneNode(true) : wrapper; // Cache the current parent and sibling. + // Loops backwards to prevent having to clone the wrapper on the + // first element (see `child` below). + Array.from(targets).reverse().forEach((element, index) => { + const child = index > 0 ? wrapper.cloneNode(true) : wrapper; + // Cache the current parent and sibling. + const parent = element.parentNode; + const sibling = element.nextSibling; - var parent = element.parentNode; - var sibling = element.nextSibling; // Wrap the element (is automatically removed from its current + // Wrap the element (is automatically removed from its current // parent). + child.appendChild(element); - child.appendChild(element); // If the element had a sibling, insert the wrapper before + // If the element had a sibling, insert the wrapper before // the sibling to maintain the HTML structure; otherwise, just // append it to the parent. - if (sibling) { parent.insertBefore(child, sibling); } else { parent.appendChild(child); } }); - } // Set attributes + } + // Set attributes function setAttributes(element, attributes) { - if (!is$1.element(element) || is$1.empty(attributes)) { - return; - } // Assume null and undefined attributes should be left out, - // Setting them would otherwise convert them to "null" and "undefined" - - - Object.entries(attributes).filter(function (_ref) { - var _ref2 = _slicedToArray(_ref, 2), - value = _ref2[1]; - - return !is$1.nullOrUndefined(value); - }).forEach(function (_ref3) { - var _ref4 = _slicedToArray(_ref3, 2), - key = _ref4[0], - value = _ref4[1]; + if (!is.element(element) || is.empty(attributes)) return; - return element.setAttribute(key, value); - }); - } // Create a DocumentFragment + // Assume null and undefined attributes should be left out, + // Setting them would otherwise convert them to "null" and "undefined" + Object.entries(attributes).filter(([, value]) => !is.nullOrUndefined(value)).forEach(([key, value]) => element.setAttribute(key, value)); + } + // Create a DocumentFragment function createElement(type, attributes, text) { // Create a new - var element = document.createElement(type); // Set all passed attributes + const element = document.createElement(type); - if (is$1.object(attributes)) { + // Set all passed attributes + if (is.object(attributes)) { setAttributes(element, attributes); - } // Add text node - + } - if (is$1.string(text)) { + // Add text node + if (is.string(text)) { element.innerText = text; - } // Return built element - + } + // Return built element return element; - } // Inaert an element after another + } + // Insert an element after another function insertAfter(element, target) { - if (!is$1.element(element) || !is$1.element(target)) { - return; - } - + if (!is.element(element) || !is.element(target)) return; target.parentNode.insertBefore(element, target.nextSibling); - } // Insert a DocumentFragment + } + // Insert a DocumentFragment function insertElement(type, parent, attributes, text) { - if (!is$1.element(parent)) { - return; - } - + if (!is.element(parent)) return; parent.appendChild(createElement(type, attributes, text)); - } // Remove element(s) + } + // Remove element(s) function removeElement(element) { - if (is$1.nodeList(element) || is$1.array(element)) { + if (is.nodeList(element) || is.array(element)) { Array.from(element).forEach(removeElement); return; } - - if (!is$1.element(element) || !is$1.element(element.parentNode)) { + if (!is.element(element) || !is.element(element.parentNode)) { return; } - element.parentNode.removeChild(element); - } // Remove all child elements + } + // Remove all child elements function emptyElement(element) { - if (!is$1.element(element)) { - return; - } - - var length = element.childNodes.length; - + if (!is.element(element)) return; + let { + length + } = element.childNodes; while (length > 0) { element.removeChild(element.lastChild); length -= 1; } - } // Replace element + } + // Replace element function replaceElement(newChild, oldChild) { - if (!is$1.element(oldChild) || !is$1.element(oldChild.parentNode) || !is$1.element(newChild)) { - return null; - } - + if (!is.element(oldChild) || !is.element(oldChild.parentNode) || !is.element(newChild)) return null; oldChild.parentNode.replaceChild(newChild, oldChild); return newChild; - } // Get an attribute object from a string selector + } + // Get an attribute object from a string selector function getAttributesFromSelector(sel, existingAttributes) { // For example: // '.test' to { class: 'test' } // '#test' to { id: 'test' } // '[data-test="test"]' to { 'data-test': 'test' } - if (!is$1.string(sel) || is$1.empty(sel)) { - return {}; - } - var attributes = {}; - var existing = extend({}, existingAttributes); - sel.split(',').forEach(function (s) { + if (!is.string(sel) || is.empty(sel)) return {}; + const attributes = {}; + const existing = extend({}, existingAttributes); + sel.split(',').forEach(s => { // Remove whitespace - var selector = s.trim(); - var className = selector.replace('.', ''); - var stripped = selector.replace(/[[\]]/g, ''); // Get the parts and value - - var parts = stripped.split('='); - - var _parts = _slicedToArray(parts, 1), - key = _parts[0]; - - var value = parts.length > 1 ? parts[1].replace(/["']/g, '') : ''; // Get the first character - - var start = selector.charAt(0); - + const selector = s.trim(); + const className = selector.replace('.', ''); + const stripped = selector.replace(/[[\]]/g, ''); + // Get the parts and value + const parts = stripped.split('='); + const [key] = parts; + const value = parts.length > 1 ? parts[1].replace(/["']/g, '') : ''; + // Get the first character + const start = selector.charAt(0); switch (start) { case '.': // Add to existing classname - if (is$1.string(existing.class)) { - attributes.class = "".concat(existing.class, " ").concat(className); + if (is.string(existing.class)) { + attributes.class = `${existing.class} ${className}`; } else { attributes.class = className; } - break; - case '#': // ID selector attributes.id = selector.replace('#', ''); break; - case '[': // Attribute selector attributes[key] = value; @@ -772,192 +538,184 @@ typeof navigator === "object" && (function (global, factory) { } }); return extend(existing, attributes); - } // Toggle hidden + } + // Toggle hidden function toggleHidden(element, hidden) { - if (!is$1.element(element)) { - return; - } - - var hide = hidden; - - if (!is$1.boolean(hide)) { + if (!is.element(element)) return; + let hide = hidden; + if (!is.boolean(hide)) { hide = !element.hidden; - } // eslint-disable-next-line no-param-reassign - + } + // eslint-disable-next-line no-param-reassign element.hidden = hide; - } // Mirror Element.classList.toggle, with IE compatibility for "force" argument + } + // Mirror Element.classList.toggle, with IE compatibility for "force" argument function toggleClass(element, className, force) { - if (is$1.nodeList(element)) { - return Array.from(element).map(function (e) { - return toggleClass(e, className, force); - }); + if (is.nodeList(element)) { + return Array.from(element).map(e => toggleClass(e, className, force)); } - - if (is$1.element(element)) { - var method = 'toggle'; - + if (is.element(element)) { + let method = 'toggle'; if (typeof force !== 'undefined') { method = force ? 'add' : 'remove'; } - element.classList[method](className); return element.classList.contains(className); } - return false; - } // Has class name + } + // Has class name function hasClass(element, className) { - return is$1.element(element) && element.classList.contains(className); - } // Element matches selector - - function matches$1(element, selector) { - var _Element = Element, - prototype = _Element.prototype; + return is.element(element) && element.classList.contains(className); + } + // Element matches selector + function matches(element, selector) { + const { + prototype + } = Element; function match() { return Array.from(document.querySelectorAll(selector)).includes(this); } - - var method = prototype.matches || prototype.webkitMatchesSelector || prototype.mozMatchesSelector || prototype.msMatchesSelector || match; + const method = prototype.matches || prototype.webkitMatchesSelector || prototype.mozMatchesSelector || prototype.msMatchesSelector || match; return method.call(element, selector); - } // Closest ancestor element matching selector (also tests element itself) + } - function closest(element, selector) { - var _Element2 = Element, - prototype = _Element2.prototype; // https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill + // Closest ancestor element matching selector (also tests element itself) + function closest$1(element, selector) { + const { + prototype + } = Element; + // https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill function closestElement() { - var el = this; - + let el = this; do { - if (matches$1.matches(el, selector)) return el; + if (matches.matches(el, selector)) return el; el = el.parentElement || el.parentNode; } while (el !== null && el.nodeType === 1); - return null; } - - var method = prototype.closest || closestElement; + const method = prototype.closest || closestElement; return method.call(element, selector); - } // Find all elements + } + // Find all elements function getElements(selector) { return this.elements.container.querySelectorAll(selector); - } // Find a single element + } + // Find a single element function getElement(selector) { return this.elements.container.querySelector(selector); - } // Set focus and tab focus class - - function setFocus() { - var element = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; - var tabFocus = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; - - if (!is$1.element(element)) { - return; - } // Set regular focus + } + // Set focus and tab focus class + function setFocus(element = null, focusVisible = false) { + if (!is.element(element)) return; + // Set regular focus element.focus({ - preventScroll: true - }); // If we want to mimic keyboard focus via tab - - if (tabFocus) { - toggleClass(element, this.config.classNames.tabFocus); - } + preventScroll: true, + focusVisible + }); } - var defaultCodecs = { + // ========================================================================== + // Plyr support checks + // ========================================================================== + + + // Default codecs for checking mimetype support + const defaultCodecs = { 'audio/ogg': 'vorbis', 'audio/wav': '1', 'video/webm': 'vp8, vorbis', 'video/mp4': 'avc1.42E01E, mp4a.40.2', 'video/ogg': 'theora' - }; // Check for feature support + }; - var support = { + // Check for feature support + const support = { // Basic support audio: 'canPlayType' in document.createElement('audio'), video: 'canPlayType' in document.createElement('video'), // Check for support // Basic functionality vs full UI - check: function check(type, provider, playsinline) { - var canPlayInline = browser.isIPhone && playsinline && support.playsinline; - var api = support[type] || provider !== 'html5'; - var ui = api && support.rangeInput && (type !== 'video' || !browser.isIPhone || canPlayInline); + check(type, provider) { + const api = support[type] || provider !== 'html5'; + const ui = api && support.rangeInput; return { - api: api, - ui: ui + api, + ui }; }, // Picture-in-picture support // Safari & Chrome only currently - pip: function () { + pip: (() => { + // While iPhone's support picture-in-picture for some apps, seemingly Safari isn't one of them + // It will throw the following error when trying to enter picture-in-picture + // `NotSupportedError: The Picture-in-Picture mode is not supported.` if (browser.isIPhone) { return false; - } // Safari - // https://developer.apple.com/documentation/webkitjs/adding_picture_in_picture_to_your_safari_media_controls - + } - if (is$1.function(createElement('video').webkitSetPresentationMode)) { + // Safari + // https://developer.apple.com/documentation/webkitjs/adding_picture_in_picture_to_your_safari_media_controls + if (is.function(createElement('video').webkitSetPresentationMode)) { return true; - } // Chrome - // https://developers.google.com/web/updates/2018/10/watch-video-using-picture-in-picture - + } + // Chrome + // https://developers.google.com/web/updates/2018/10/watch-video-using-picture-in-picture if (document.pictureInPictureEnabled && !createElement('video').disablePictureInPicture) { return true; } - return false; - }(), + })(), // Airplay support // Safari only currently - airplay: is$1.function(window.WebKitPlaybackTargetAvailabilityEvent), + airplay: is.function(window.WebKitPlaybackTargetAvailabilityEvent), // Inline playback support // https://webkit.org/blog/6784/new-video-policies-for-ios/ playsinline: 'playsInline' in document.createElement('video'), // Check for mime type support against a player instance // Credits: http://diveintohtml5.info/everything.html // Related: http://www.leanbackplayer.com/test/h5mt.html - mime: function mime(input) { - if (is$1.empty(input)) { + mime(input) { + if (is.empty(input)) { return false; } + const [mediaType] = input.split('/'); + let type = input; - var _input$split = input.split('/'), - _input$split2 = _slicedToArray(_input$split, 1), - mediaType = _input$split2[0]; - - var type = input; // Verify we're using HTML5 and there's no media type mismatch - + // Verify we're using HTML5 and there's no media type mismatch if (!this.isHTML5 || mediaType !== this.type) { return false; - } // Add codec if required - + } + // Add codec if required if (Object.keys(defaultCodecs).includes(type)) { - type += "; codecs=\"".concat(defaultCodecs[input], "\""); + type += `; codecs="${defaultCodecs[input]}"`; } - try { return Boolean(type && this.media.canPlayType(type).replace(/no/, '')); - } catch (e) { + } catch (_) { return false; } }, // Check for textTracks support textTracks: 'textTracks' in document.createElement('video'), // Sliders - rangeInput: function () { - var range = document.createElement('input'); + rangeInput: (() => { + const range = document.createElement('input'); range.type = 'range'; return range.type === 'range'; - }(), + })(), // Touch // NOTE: Remember a device can be mouse + touch enabled so we check on first touch event touch: 'ontouchstart' in document.documentElement, @@ -968,149 +726,128 @@ typeof navigator === "object" && (function (global, factory) { reducedMotion: 'matchMedia' in window && window.matchMedia('(prefers-reduced-motion)').matches }; + // ========================================================================== + // Event utils + // ========================================================================== + + + // Check for passive event listener support // https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md // https://www.youtube.com/watch?v=NPM6172J22g - - var supportsPassiveListeners = function () { + const supportsPassiveListeners = (() => { // Test via a getter in the options object to see if the passive property is accessed - var supported = false; - + let supported = false; try { - var options = Object.defineProperty({}, 'passive', { - get: function get() { + const options = Object.defineProperty({}, 'passive', { + get() { supported = true; return null; } }); window.addEventListener('test', null, options); window.removeEventListener('test', null, options); - } catch (e) {// Do nothing + } catch (_) { + // Do nothing } - return supported; - }(); // Toggle event listener - - - function toggleListener(element, event, callback) { - var _this = this; - - var toggle = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; - var passive = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true; - var capture = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false; + })(); + // Toggle event listener + function toggleListener(element, event, callback, toggle = false, passive = true, capture = false) { // Bail if no element, event, or callback - if (!element || !('addEventListener' in element) || is$1.empty(event) || !is$1.function(callback)) { + if (!element || !('addEventListener' in element) || is.empty(event) || !is.function(callback)) { return; - } // Allow multiple events - + } - var events = event.split(' '); // Build options + // Allow multiple events + const events = event.split(' '); + // Build options // Default to just the capture boolean for browsers with no passive listener support + let options = capture; - var options = capture; // If passive events listeners are supported - + // If passive events listeners are supported if (supportsPassiveListeners) { options = { // Whether the listener can be passive (i.e. default never prevented) - passive: passive, + passive, // Whether the listener is a capturing listener or not - capture: capture + capture }; - } // If a single node is passed, bind the event listener - + } - events.forEach(function (type) { - if (_this && _this.eventListeners && toggle) { + // If a single node is passed, bind the event listener + events.forEach(type => { + if (this && this.eventListeners && toggle) { // Cache event listener - _this.eventListeners.push({ - element: element, - type: type, - callback: callback, - options: options + this.eventListeners.push({ + element, + type, + callback, + options }); } - element[toggle ? 'addEventListener' : 'removeEventListener'](type, callback, options); }); - } // Bind event handler + } - function on(element) { - var events = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; - var callback = arguments.length > 2 ? arguments[2] : undefined; - var passive = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true; - var capture = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; + // Bind event handler + function on(element, events = '', callback, passive = true, capture = false) { toggleListener.call(this, element, events, callback, true, passive, capture); - } // Unbind event handler + } - function off(element) { - var events = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; - var callback = arguments.length > 2 ? arguments[2] : undefined; - var passive = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true; - var capture = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; + // Unbind event handler + function off(element, events = '', callback, passive = true, capture = false) { toggleListener.call(this, element, events, callback, false, passive, capture); - } // Bind once-only event handler - - function once(element) { - var _this2 = this; - - var events = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; - var callback = arguments.length > 2 ? arguments[2] : undefined; - var passive = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true; - var capture = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; + } - var onceCallback = function onceCallback() { + // Bind once-only event handler + function once(element, events = '', callback, passive = true, capture = false) { + const onceCallback = (...args) => { off(element, events, onceCallback, passive, capture); - - for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { - args[_key] = arguments[_key]; - } - - callback.apply(_this2, args); + callback.apply(this, args); }; - toggleListener.call(this, element, events, onceCallback, true, passive, capture); - } // Trigger event - - function triggerEvent(element) { - var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; - var bubbles = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; - var detail = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; + } + // Trigger event + function triggerEvent(element, type = '', bubbles = false, detail = {}) { // Bail if no element - if (!is$1.element(element) || is$1.empty(type)) { + if (!is.element(element) || is.empty(type)) { return; - } // Create and dispatch the event - + } - var event = new CustomEvent(type, { - bubbles: bubbles, - detail: _objectSpread2(_objectSpread2({}, detail), {}, { + // Create and dispatch the event + const event = new CustomEvent(type, { + bubbles, + detail: { + ...detail, plyr: this - }) - }); // Dispatch the event + } + }); + // Dispatch the event element.dispatchEvent(event); - } // Unbind all cached event listeners + } + // Unbind all cached event listeners function unbindListeners() { if (this && this.eventListeners) { - this.eventListeners.forEach(function (item) { - var element = item.element, - type = item.type, - callback = item.callback, - options = item.options; + this.eventListeners.forEach(item => { + const { + element, + type, + callback, + options + } = item; element.removeEventListener(type, callback, options); }); this.eventListeners = []; } - } // Run method when / if player is ready + } + // Run method when / if player is ready function ready() { - var _this3 = this; - - return new Promise(function (resolve) { - return _this3.ready ? setTimeout(resolve, 0) : on.call(_this3, _this3.elements.container, 'ready', resolve); - }).then(function () {}); + return new Promise(resolve => this.ready ? setTimeout(resolve, 0) : on.call(this, this.elements.container, 'ready', resolve)).then(() => {}); } /** @@ -1119,198 +856,266 @@ typeof navigator === "object" && (function (global, factory) { * play promise" rejection error messages. * @param {Object} value An object that may or may not be `Promise`-like. */ - function silencePromise(value) { - if (is$1.promise(value)) { - value.then(null, function () {}); + if (is.promise(value)) { + value.then(null, () => {}); } } - function validateRatio(input) { - if (!is$1.array(input) && (!is$1.string(input) || !input.includes(':'))) { - return false; - } + // ========================================================================== + // Array utils + // ========================================================================== + - var ratio = is$1.array(input) ? input : input.split(':'); - return ratio.map(Number).every(is$1.number); + // Remove duplicates in an array + function dedupe(array) { + if (!is.array(array)) { + return array; + } + return array.filter((item, index) => array.indexOf(item) === index); } - function reduceAspectRatio(ratio) { - if (!is$1.array(ratio) || !ratio.every(is$1.number)) { + + // Get the closest value in an array + function closest(array, value) { + if (!is.array(array) || !array.length) { return null; } + return array.reduce((prev, curr) => Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev); + } - var _ratio = _slicedToArray(ratio, 2), - width = _ratio[0], - height = _ratio[1]; + // ========================================================================== + // Style utils + // ========================================================================== - var getDivider = function getDivider(w, h) { - return h === 0 ? w : getDivider(h, w % h); - }; - var divider = getDivider(width, height); - return [width / divider, height / divider]; + // Check support for a CSS declaration + function supportsCSS(declaration) { + if (!window || !window.CSS) { + return false; + } + return window.CSS.supports(declaration); } - function getAspectRatio(input) { - var parse = function parse(ratio) { - return validateRatio(ratio) ? ratio.split(':').map(Number) : null; - }; // Try provided ratio + // Standard/common aspect ratios + const standardRatios = [[1, 1], [4, 3], [3, 4], [5, 4], [4, 5], [3, 2], [2, 3], [16, 10], [10, 16], [16, 9], [9, 16], [21, 9], [9, 21], [32, 9], [9, 32]].reduce((out, [x, y]) => ({ + ...out, + [x / y]: [x, y] + }), {}); - var ratio = parse(input); // Get from config + // Validate an aspect ratio + function validateAspectRatio(input) { + if (!is.array(input) && (!is.string(input) || !input.includes(':'))) { + return false; + } + const ratio = is.array(input) ? input : input.split(':'); + return ratio.map(Number).every(is.number); + } - if (ratio === null) { - ratio = parse(this.config.ratio); - } // Get from embed + // Reduce an aspect ratio to it's lowest form + function reduceAspectRatio(ratio) { + if (!is.array(ratio) || !ratio.every(is.number)) { + return null; + } + const [width, height] = ratio; + const getDivider = (w, h) => h === 0 ? w : getDivider(h, w % h); + const divider = getDivider(width, height); + return [width / divider, height / divider]; + } + // Calculate an aspect ratio + function getAspectRatio(input) { + const parse = ratio => validateAspectRatio(ratio) ? ratio.split(':').map(Number) : null; + // Try provided ratio + let ratio = parse(input); - if (ratio === null && !is$1.empty(this.embed) && is$1.array(this.embed.ratio)) { - ratio = this.embed.ratio; - } // Get from HTML5 video + // Get from config + if (ratio === null) { + ratio = parse(this.config.ratio); + } + // Get from embed + if (ratio === null && !is.empty(this.embed) && is.array(this.embed.ratio)) { + ({ + ratio + } = this.embed); + } + // Get from HTML5 video if (ratio === null && this.isHTML5) { - var _this$media = this.media, - videoWidth = _this$media.videoWidth, - videoHeight = _this$media.videoHeight; - ratio = reduceAspectRatio([videoWidth, videoHeight]); + const { + videoWidth, + videoHeight + } = this.media; + ratio = [videoWidth, videoHeight]; } + return reduceAspectRatio(ratio); + } - return ratio; - } // Set aspect ratio for responsive container - + // Set aspect ratio for responsive container function setAspectRatio(input) { if (!this.isVideo) { return {}; } + const { + wrapper + } = this.elements; + const ratio = getAspectRatio.call(this, input); + if (!is.array(ratio)) { + return {}; + } + const [x, y] = reduceAspectRatio(ratio); + const useNative = supportsCSS(`aspect-ratio: ${x}/${y}`); + const padding = 100 / x * y; + if (useNative) { + wrapper.style.aspectRatio = `${x}/${y}`; + } else { + wrapper.style.paddingBottom = `${padding}%`; + } - var wrapper = this.elements.wrapper; - var ratio = getAspectRatio.call(this, input); - - var _ref = is$1.array(ratio) ? ratio : [0, 0], - _ref2 = _slicedToArray(_ref, 2), - w = _ref2[0], - h = _ref2[1]; - - var padding = 100 / w * h; - wrapper.style.paddingBottom = "".concat(padding, "%"); // For Vimeo we have an extra
to hide the standard controls and UI - + // For Vimeo we have an extra
to hide the standard controls and UI if (this.isVimeo && !this.config.vimeo.premium && this.supported.ui) { - var height = 100 / this.media.offsetWidth * parseInt(window.getComputedStyle(this.media).paddingBottom, 10); - var offset = (height - padding) / (height / 50); - this.media.style.transform = "translateY(-".concat(offset, "%)"); + const height = 100 / this.media.offsetWidth * parseInt(window.getComputedStyle(this.media).paddingBottom, 10); + const offset = (height - padding) / (height / 50); + if (this.fullscreen.active) { + wrapper.style.paddingBottom = null; + } else { + this.media.style.transform = `translateY(-${offset}%)`; + } } else if (this.isHTML5) { - wrapper.classList.toggle(this.config.classNames.videoFixedRatio, ratio !== null); + wrapper.classList.add(this.config.classNames.videoFixedRatio); } - return { - padding: padding, - ratio: ratio + padding, + ratio }; } + // Round an aspect ratio to closest standard ratio + function roundAspectRatio(x, y, tolerance = 0.05) { + const ratio = x / y; + const closestRatio = closest(Object.keys(standardRatios), ratio); + + // Check match is within tolerance + if (Math.abs(closestRatio - ratio) <= tolerance) { + return standardRatios[closestRatio]; + } + + // No match + return [x, y]; + } + + // Get the size of the viewport + // https://stackoverflow.com/questions/1248081/how-to-get-the-browser-viewport-dimensions + function getViewportSize() { + const width = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0); + const height = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0); + return [width, height]; + } + + // ========================================================================== + // Plyr HTML5 helpers // ========================================================================== - var html5 = { - getSources: function getSources() { - var _this = this; + const html5 = { + getSources() { if (!this.isHTML5) { return []; } + const sources = Array.from(this.media.querySelectorAll('source')); - var sources = Array.from(this.media.querySelectorAll('source')); // Filter out unsupported sources (if type is specified) - - return sources.filter(function (source) { - var type = source.getAttribute('type'); - - if (is$1.empty(type)) { + // Filter out unsupported sources (if type is specified) + return sources.filter(source => { + const type = source.getAttribute('type'); + if (is.empty(type)) { return true; } - - return support.mime.call(_this, type); + return support.mime.call(this, type); }); }, // Get quality levels - getQualityOptions: function getQualityOptions() { + getQualityOptions() { // Whether we're forcing all options (e.g. for streaming) if (this.config.quality.forced) { return this.config.quality.options; - } // Get sizes from elements - + } - return html5.getSources.call(this).map(function (source) { - return Number(source.getAttribute('data-res')); - }).filter(Boolean); + // Get sizes from elements + return html5.getSources.call(this).map(source => Number(source.getAttribute('data-res'))).filter(Boolean); }, - setup: function setup() { + setup() { if (!this.isHTML5) { return; } + const player = this; - var player = this; // Set speed options from config + // Set speed options from config + player.options.speed = player.config.speed.options; - player.options.speed = player.config.speed.options; // Set aspect ratio if fixed - - if (!is$1.empty(this.config.ratio)) { + // Set aspect ratio if fixed + if (!is.empty(this.config.ratio)) { setAspectRatio.call(player); - } // Quality - + } + // Quality Object.defineProperty(player.media, 'quality', { - get: function get() { + get() { // Get sources - var sources = html5.getSources.call(player); - var source = sources.find(function (s) { - return s.getAttribute('src') === player.source; - }); // Return size, if match is found + const sources = html5.getSources.call(player); + const source = sources.find(s => s.getAttribute('src') === player.source); + // Return size, if match is found return source && Number(source.getAttribute('data-res')); }, - set: function set(input) { + set(input) { if (player.quality === input) { return; - } // If we're using an an external handler... - + } - if (player.config.quality.forced && is$1.function(player.config.quality.onChange)) { + // If we're using an external handler... + if (player.config.quality.forced && is.function(player.config.quality.onChange)) { player.config.quality.onChange(input); } else { // Get sources - var sources = html5.getSources.call(player); // Get first match for requested size - - var source = sources.find(function (s) { - return Number(s.getAttribute('data-res')) === input; - }); // No matching source found + const sources = html5.getSources.call(player); + // Get first match for requested size + const source = sources.find(s => Number(s.getAttribute('data-res')) === input); + // No matching source found if (!source) { return; - } // Get current state - + } - var _player$media = player.media, - currentTime = _player$media.currentTime, - paused = _player$media.paused, - preload = _player$media.preload, - readyState = _player$media.readyState, - playbackRate = _player$media.playbackRate; // Set new source + // Get current state + const { + currentTime, + paused, + preload, + readyState, + playbackRate + } = player.media; - player.media.src = source.getAttribute('src'); // Prevent loading if preload="none" and the current source isn't loaded (#1044) + // Set new source + player.media.src = source.getAttribute('src'); + // Prevent loading if preload="none" and the current source isn't loaded (#1044) if (preload !== 'none' || readyState) { // Restore time - player.once('loadedmetadata', function () { + player.once('loadedmetadata', () => { player.speed = playbackRate; - player.currentTime = currentTime; // Resume playing + player.currentTime = currentTime; + // Resume playing if (!paused) { silencePromise(player.play()); } - }); // Load new source + }); + // Load new source player.media.load(); } - } // Trigger change event - + } + // Trigger change event triggerEvent.call(player, player.media, 'qualitychange', false, { quality: input }); @@ -1319,401 +1124,368 @@ typeof navigator === "object" && (function (global, factory) { }, // Cancel current network requests // See https://github.com/sampotts/plyr/issues/174 - cancelRequests: function cancelRequests() { + cancelRequests() { if (!this.isHTML5) { return; - } // Remove child sources + } + // Remove child sources + removeElement(html5.getSources.call(this)); - removeElement(html5.getSources.call(this)); // Set blank video src attribute + // Set blank video src attribute // This is to prevent a MEDIA_ERR_SRC_NOT_SUPPORTED error // Info: http://stackoverflow.com/questions/32231579/how-to-properly-dispose-of-an-html5-video-and-close-socket-or-connection + this.media.setAttribute('src', this.config.blankVideo); - this.media.setAttribute('src', this.config.blankVideo); // Load the new empty source + // Load the new empty source // This will cancel existing requests // See https://github.com/sampotts/plyr/issues/174 + this.media.load(); - this.media.load(); // Debugging - + // Debugging this.debug.log('Cancelled network requests'); } }; // ========================================================================== - - function dedupe(array) { - if (!is$1.array(array)) { - return array; - } - - return array.filter(function (item, index) { - return array.indexOf(item) === index; - }); - } // Get the closest value in an array - - function closest$1(array, value) { - if (!is$1.array(array) || !array.length) { - return null; - } - - return array.reduce(function (prev, curr) { - return Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev; - }); - } - + // String utils // ========================================================================== - function generateId(prefix) { - return "".concat(prefix, "-").concat(Math.floor(Math.random() * 10000)); - } // Format string - - function format(input) { - for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - args[_key - 1] = arguments[_key]; - } - if (is$1.empty(input)) { - return input; - } + // Generate a random ID + function generateId(prefix) { + return `${prefix}-${Math.floor(Math.random() * 10000)}`; + } - return input.toString().replace(/{(\d+)}/g, function (match, i) { - return args[i].toString(); - }); - } // Get percentage + // Format string + function format(input, ...args) { + if (is.empty(input)) return input; + return input.toString().replace(/{(\d+)}/g, (_, i) => args[i].toString()); + } + // Get percentage function getPercentage(current, max) { if (current === 0 || max === 0 || Number.isNaN(current) || Number.isNaN(max)) { return 0; } - return (current / max * 100).toFixed(2); - } // Replace all occurances of a string in a string - - var replaceAll = function replaceAll() { - var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; - var find = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; - var replace = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ''; - return input.replace(new RegExp(find.toString().replace(/([.*+?^=!:${}()|[\]/\\])/g, '\\$1'), 'g'), replace.toString()); - }; // Convert to title case - - var toTitleCase = function toTitleCase() { - var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; - return input.toString().replace(/\w\S*/g, function (text) { - return text.charAt(0).toUpperCase() + text.substr(1).toLowerCase(); - }); - }; // Convert string to pascalCase + } - function toPascalCase() { - var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; - var string = input.toString(); // Convert kebab case + // Replace all occurrences of a string in a string + const replaceAll = (input = '', find = '', replace = '') => input.replace(new RegExp(find.toString().replace(/([.*+?^=!:${}()|[\]/\\])/g, '\\$1'), 'g'), replace.toString()); - string = replaceAll(string, '-', ' '); // Convert snake case + // Convert to title case + const toTitleCase = (input = '') => input.toString().replace(/\w\S*/g, text => text.charAt(0).toUpperCase() + text.slice(1).toLowerCase()); - string = replaceAll(string, '_', ' '); // Convert to title case + // Convert string to pascalCase + function toPascalCase(input = '') { + let string = input.toString(); - string = toTitleCase(string); // Convert to pascal case + // Convert kebab case + string = replaceAll(string, '-', ' '); + // Convert snake case + string = replaceAll(string, '_', ' '); + + // Convert to title case + string = toTitleCase(string); + + // Convert to pascal case return replaceAll(string, ' ', ''); - } // Convert string to pascalCase + } - function toCamelCase() { - var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; - var string = input.toString(); // Convert to pascal case + // Convert string to pascalCase + function toCamelCase(input = '') { + let string = input.toString(); - string = toPascalCase(string); // Convert first character to lowercase + // Convert to pascal case + string = toPascalCase(string); + // Convert first character to lowercase return string.charAt(0).toLowerCase() + string.slice(1); - } // Remove HTML from a string + } + // Remove HTML from a string function stripHTML(source) { - var fragment = document.createDocumentFragment(); - var element = document.createElement('div'); + const fragment = document.createDocumentFragment(); + const element = document.createElement('div'); fragment.appendChild(element); element.innerHTML = source; return fragment.firstChild.innerText; - } // Like outerHTML, but also works for DocumentFragment + } + // Like outerHTML, but also works for DocumentFragment function getHTML(element) { - var wrapper = document.createElement('div'); + const wrapper = document.createElement('div'); wrapper.appendChild(element); return wrapper.innerHTML; } - var resources = { + // ========================================================================== + // Plyr internationalization + // ========================================================================== + + + // Skip i18n for abbreviations and brand names + const resources = { pip: 'PIP', airplay: 'AirPlay', html5: 'HTML5', vimeo: 'Vimeo', youtube: 'YouTube' }; - var i18n = { - get: function get() { - var key = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; - var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - - if (is$1.empty(key) || is$1.empty(config)) { + const i18n = { + get(key = '', config = {}) { + if (is.empty(key) || is.empty(config)) { return ''; } - - var string = getDeep(config.i18n, key); - - if (is$1.empty(string)) { + let string = getDeep(config.i18n, key); + if (is.empty(string)) { if (Object.keys(resources).includes(key)) { return resources[key]; } - return ''; } - - var replace = { + const replace = { '{seektime}': config.seekTime, '{title}': config.title }; - Object.entries(replace).forEach(function (_ref) { - var _ref2 = _slicedToArray(_ref, 2), - k = _ref2[0], - v = _ref2[1]; - + Object.entries(replace).forEach(([k, v]) => { string = replaceAll(string, k, v); }); return string; } }; - var Storage = /*#__PURE__*/function () { - function Storage(player) { - _classCallCheck(this, Storage); - - this.enabled = player.config.storage.enabled; - this.key = player.config.storage.key; - } // Check for actual support (see if we can use it) - - - _createClass(Storage, [{ - key: "get", - value: function get(key) { + class Storage { + constructor(player) { + _defineProperty$1(this, "get", key => { if (!Storage.supported || !this.enabled) { return null; } - - var store = window.localStorage.getItem(this.key); - - if (is$1.empty(store)) { + const store = window.localStorage.getItem(this.key); + if (is.empty(store)) { return null; } - - var json = JSON.parse(store); - return is$1.string(key) && key.length ? json[key] : json; - } - }, { - key: "set", - value: function set(object) { + const json = JSON.parse(store); + return is.string(key) && key.length ? json[key] : json; + }); + _defineProperty$1(this, "set", object => { // Bail if we don't have localStorage support or it's disabled if (!Storage.supported || !this.enabled) { return; - } // Can only store objectst - + } - if (!is$1.object(object)) { + // Can only store objectst + if (!is.object(object)) { return; - } // Get current storage - + } - var storage = this.get(); // Default to empty object + // Get current storage + let storage = this.get(); - if (is$1.empty(storage)) { + // Default to empty object + if (is.empty(storage)) { storage = {}; - } // Update the working copy of the values - + } - extend(storage, object); // Update storage + // Update the working copy of the values + extend(storage, object); - window.localStorage.setItem(this.key, JSON.stringify(storage)); - } - }], [{ - key: "supported", - get: function get() { + // Update storage try { - if (!('localStorage' in window)) { - return false; - } - - var test = '___test'; // Try to use it (it might be disabled, e.g. user is in private mode) - // see: https://github.com/sampotts/plyr/issues/131 + window.localStorage.setItem(this.key, JSON.stringify(storage)); + } catch (_) { + // Do nothing + } + }); + this.enabled = player.config.storage.enabled; + this.key = player.config.storage.key; + } - window.localStorage.setItem(test, test); - window.localStorage.removeItem(test); - return true; - } catch (e) { + // Check for actual support (see if we can use it) + static get supported() { + try { + if (!('localStorage' in window)) { return false; } - } - }]); + const test = '___test'; - return Storage; - }(); + // Try to use it (it might be disabled, e.g. user is in private mode) + // see: https://github.com/sampotts/plyr/issues/131 + window.localStorage.setItem(test, test); + window.localStorage.removeItem(test); + return true; + } catch (_) { + return false; + } + } + } // ========================================================================== // Fetch wrapper // Using XHR to avoid issues with older browsers // ========================================================================== - function fetch(url) { - var responseType = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'text'; - return new Promise(function (resolve, reject) { + + function fetch(url, responseType = 'text') { + return new Promise((resolve, reject) => { try { - var request = new XMLHttpRequest(); // Check for CORS support + const request = new XMLHttpRequest(); + // Check for CORS support if (!('withCredentials' in request)) { return; } - - request.addEventListener('load', function () { + request.addEventListener('load', () => { if (responseType === 'text') { try { resolve(JSON.parse(request.responseText)); - } catch (e) { + } catch (_) { resolve(request.responseText); } } else { resolve(request.response); } }); - request.addEventListener('error', function () { + request.addEventListener('error', () => { throw new Error(request.status); }); - request.open('GET', url, true); // Set the required response type + request.open('GET', url, true); + // Set the required response type request.responseType = responseType; request.send(); - } catch (e) { - reject(e); + } catch (error) { + reject(error); } }); } // ========================================================================== + // Sprite loader + // ========================================================================== + + // Load an external SVG sprite function loadSprite(url, id) { - if (!is$1.string(url)) { + if (!is.string(url)) { return; } - - var prefix = 'cache'; - var hasId = is$1.string(id); - var isCached = false; - - var exists = function exists() { - return document.getElementById(id) !== null; - }; - - var update = function update(container, data) { + const prefix = 'cache'; + const hasId = is.string(id); + let isCached = false; + const exists = () => document.getElementById(id) !== null; + const update = (container, data) => { // eslint-disable-next-line no-param-reassign - container.innerHTML = data; // Check again incase of race condition + container.innerHTML = data; + // Check again incase of race condition if (hasId && exists()) { return; - } // Inject the SVG to the body - + } + // Inject the SVG to the body document.body.insertAdjacentElement('afterbegin', container); - }; // Only load once if ID set - + }; + // Only load once if ID set if (!hasId || !exists()) { - var useStorage = Storage.supported; // Create container - - var container = document.createElement('div'); + const useStorage = Storage.supported; + // Create container + const container = document.createElement('div'); container.setAttribute('hidden', ''); - if (hasId) { container.setAttribute('id', id); - } // Check in cache - + } + // Check in cache if (useStorage) { - var cached = window.localStorage.getItem("".concat(prefix, "-").concat(id)); + const cached = window.localStorage.getItem(`${prefix}-${id}`); isCached = cached !== null; - if (isCached) { - var data = JSON.parse(cached); + const data = JSON.parse(cached); update(container, data.content); } - } // Get the sprite - + } - fetch(url).then(function (result) { - if (is$1.empty(result)) { + // Get the sprite + fetch(url).then(result => { + if (is.empty(result)) { return; } - if (useStorage) { - window.localStorage.setItem("".concat(prefix, "-").concat(id), JSON.stringify({ - content: result - })); + try { + window.localStorage.setItem(`${prefix}-${id}`, JSON.stringify({ + content: result + })); + } catch (_) { + // Do nothing + } } - update(container, result); - }).catch(function () {}); + }).catch(() => {}); } } // ========================================================================== + // Time utils + // ========================================================================== - var getHours = function getHours(value) { - return Math.trunc(value / 60 / 60 % 60, 10); - }; - var getMinutes = function getMinutes(value) { - return Math.trunc(value / 60 % 60, 10); - }; - var getSeconds = function getSeconds(value) { - return Math.trunc(value % 60, 10); - }; // Format time to UI friendly string - function formatTime() { - var time = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; - var displayHours = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; - var inverted = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + // Time helpers + const getHours = value => Math.trunc(value / 60 / 60 % 60, 10); + const getMinutes = value => Math.trunc(value / 60 % 60, 10); + const getSeconds = value => Math.trunc(value % 60, 10); + // Format time to UI friendly string + function formatTime(time = 0, displayHours = false, inverted = false) { // Bail if the value isn't a number - if (!is$1.number(time)) { + if (!is.number(time)) { return formatTime(undefined, displayHours, inverted); - } // Format time component to add leading zero - - - var format = function format(value) { - return "0".concat(value).slice(-2); - }; // Breakdown to hours, mins, secs - + } - var hours = getHours(time); - var mins = getMinutes(time); - var secs = getSeconds(time); // Do we need to display hours? + // Format time component to add leading zero + const format = value => `0${value}`.slice(-2); + // Breakdown to hours, mins, secs + let hours = getHours(time); + const mins = getMinutes(time); + const secs = getSeconds(time); + // Do we need to display hours? if (displayHours || hours > 0) { - hours = "".concat(hours, ":"); + hours = `${hours}:`; } else { hours = ''; - } // Render - + } - return "".concat(inverted && time > 0 ? '-' : '').concat(hours).concat(format(mins), ":").concat(format(secs)); + // Render + return `${inverted && time > 0 ? '-' : ''}${hours}${format(mins)}:${format(secs)}`; } - var controls = { - // Get icon URL - getIconUrl: function getIconUrl() { - var url = new URL(this.config.iconUrl, window.location); - var cors = url.host !== window.location.host || browser.isIE && !window.svg4everybody; + // ========================================================================== + // Plyr controls + // TODO: This needs to be split into smaller files and cleaned up + // ========================================================================== + + + // TODO: Don't export a massive object - break down and create class + const controls = { + // Get icon URL + getIconUrl() { + const url = new URL(this.config.iconUrl, window.location); + const host = window.location.host ? window.location.host : window.top.location.host; + const cors = url.host !== host || browser.isIE && !window.svg4everybody; return { url: this.config.iconUrl, - cors: cors + cors }; }, // Find the UI controls - findElements: function findElements() { + findElements() { try { - this.elements.controls = getElement.call(this, this.config.selectors.controls.wrapper); // Buttons + this.elements.controls = getElement.call(this, this.config.selectors.controls.wrapper); + // Buttons this.elements.buttons = { play: getElements.call(this, this.config.selectors.buttons.play), pause: getElement.call(this, this.config.selectors.buttons.pause), @@ -1726,79 +1498,83 @@ typeof navigator === "object" && (function (global, factory) { settings: getElement.call(this, this.config.selectors.buttons.settings), captions: getElement.call(this, this.config.selectors.buttons.captions), fullscreen: getElement.call(this, this.config.selectors.buttons.fullscreen) - }; // Progress + }; - this.elements.progress = getElement.call(this, this.config.selectors.progress); // Inputs + // Progress + this.elements.progress = getElement.call(this, this.config.selectors.progress); + // Inputs this.elements.inputs = { seek: getElement.call(this, this.config.selectors.inputs.seek), volume: getElement.call(this, this.config.selectors.inputs.volume) - }; // Display + }; + // Display this.elements.display = { buffer: getElement.call(this, this.config.selectors.display.buffer), currentTime: getElement.call(this, this.config.selectors.display.currentTime), duration: getElement.call(this, this.config.selectors.display.duration) - }; // Seek tooltip + }; - if (is$1.element(this.elements.progress)) { - this.elements.display.seekTooltip = this.elements.progress.querySelector(".".concat(this.config.classNames.tooltip)); + // Seek tooltip + if (is.element(this.elements.progress)) { + this.elements.display.seekTooltip = this.elements.progress.querySelector(`.${this.config.classNames.tooltip}`); } - return true; } catch (error) { // Log it - this.debug.warn('It looks like there is a problem with your custom controls HTML', error); // Restore native video controls + this.debug.warn('It looks like there is a problem with your custom controls HTML', error); + // Restore native video controls this.toggleNativeControls(true); return false; } }, // Create icon - createIcon: function createIcon(type, attributes) { - var namespace = 'http://www.w3.org/2000/svg'; - var iconUrl = controls.getIconUrl.call(this); - var iconPath = "".concat(!iconUrl.cors ? iconUrl.url : '', "#").concat(this.config.iconPrefix); // Create - - var icon = document.createElementNS(namespace, 'svg'); + createIcon(type, attributes) { + const namespace = 'http://www.w3.org/2000/svg'; + const iconUrl = controls.getIconUrl.call(this); + const iconPath = `${!iconUrl.cors ? iconUrl.url : ''}#${this.config.iconPrefix}`; + // Create + const icon = document.createElementNS(namespace, 'svg'); setAttributes(icon, extend(attributes, { 'aria-hidden': 'true', focusable: 'false' - })); // Create the to reference sprite + })); + + // Create the to reference sprite + const use = document.createElementNS(namespace, 'use'); + const path = `${iconPath}-${type}`; - var use = document.createElementNS(namespace, 'use'); - var path = "".concat(iconPath, "-").concat(type); // Set `href` attributes + // Set `href` attributes // https://github.com/sampotts/plyr/issues/460 // https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/xlink:href - if ('href' in use) { use.setAttributeNS('http://www.w3.org/1999/xlink', 'href', path); - } // Always set the older attribute even though it's "deprecated" (it'll be around for ages) - + } - use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', path); // Add to + // Always set the older attribute even though it's "deprecated" (it'll be around for ages) + use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', path); + // Add to icon.appendChild(use); return icon; }, // Create hidden text label - createLabel: function createLabel(key) { - var attr = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - var text = i18n.get(key, this.config); - - var attributes = _objectSpread2(_objectSpread2({}, attr), {}, { + createLabel(key, attr = {}) { + const text = i18n.get(key, this.config); + const attributes = { + ...attr, class: [attr.class, this.config.classNames.hidden].filter(Boolean).join(' ') - }); - + }; return createElement('span', attributes, text); }, // Create a badge - createBadge: function createBadge(text) { - if (is$1.empty(text)) { + createBadge(text) { + if (is.empty(text)) { return null; } - - var badge = createElement('span', { + const badge = createElement('span', { class: this.config.classNames.menu.value }); badge.appendChild(createElement('span', { @@ -1807,12 +1583,10 @@ typeof navigator === "object" && (function (global, factory) { return badge; }, // Create a
`); + } - if (is$1.event(event) && ['mouseenter', 'mouseleave'].includes(event.type)) { + // Set position + tipElement.style.left = `${percent}%`; + + // Show/hide the tooltip + // If the event is a moues in/out and percentage is inside bounds + if (is.event(event) && ['mouseenter', 'mouseleave'].includes(event.type)) { toggle(event.type === 'mouseenter'); } }, // Handle time change event - timeUpdate: function timeUpdate(event) { + timeUpdate(event) { // Only invert if only one time element is displayed and used for both duration and currentTime - var invert = !is$1.element(this.elements.display.duration) && this.config.invertTime; // Duration + const invert = !is.element(this.elements.display.duration) && this.config.invertTime; - controls.updateTimeDisplay.call(this, this.elements.display.currentTime, invert ? this.duration - this.currentTime : this.currentTime, invert); // Ignore updates while seeking + // Duration + controls.updateTimeDisplay.call(this, this.elements.display.currentTime, invert ? this.duration - this.currentTime : this.currentTime, invert); + // Ignore updates while seeking if (event && event.type === 'timeupdate' && this.media.seeking) { return; - } // Playing progress - + } + // Playing progress controls.updateProgress.call(this, event); }, // Show the duration on metadataloaded or durationchange events - durationUpdate: function durationUpdate() { + durationUpdate() { // Bail if no UI or durationchange event triggered after playing/seek when invertTime is false if (!this.supported.ui || !this.config.invertTime && this.currentTime) { return; - } // If duration is the 2**32 (shaka), Infinity (HLS), DASH-IF (Number.MAX_SAFE_INTEGER || Number.MAX_VALUE) indicating live we hide the currentTime and progressbar. + } + + // If duration is the 2**32 (shaka), Infinity (HLS), DASH-IF (Number.MAX_SAFE_INTEGER || Number.MAX_VALUE) indicating live we hide the currentTime and progressbar. // https://github.com/video-dev/hls.js/blob/5820d29d3c4c8a46e8b75f1e3afa3e68c1a9a2db/src/controller/buffer-controller.js#L415 // https://github.com/google/shaka-player/blob/4d889054631f4e1cf0fbd80ddd2b71887c02e232/lib/media/streaming_engine.js#L1062 // https://github.com/Dash-Industry-Forum/dash.js/blob/69859f51b969645b234666800d4cb596d89c602d/src/dash/models/DashManifestModel.js#L338 - - - if (this.duration >= Math.pow(2, 32)) { + if (this.duration >= 2 ** 32) { toggleHidden(this.elements.display.currentTime, true); toggleHidden(this.elements.progress, true); return; - } // Update ARIA values - + } - if (is$1.element(this.elements.inputs.seek)) { + // Update ARIA values + if (is.element(this.elements.inputs.seek)) { this.elements.inputs.seek.setAttribute('aria-valuemax', this.duration); - } // If there's a spot to display duration - + } - var hasDuration = is$1.element(this.elements.display.duration); // If there's only one time display, display duration there + // If there's a spot to display duration + const hasDuration = is.element(this.elements.display.duration); + // If there's only one time display, display duration there if (!hasDuration && this.config.displayDuration && this.paused) { controls.updateTimeDisplay.call(this, this.elements.display.currentTime, this.duration); - } // If there's a duration element, update content - + } + // If there's a duration element, update content if (hasDuration) { controls.updateTimeDisplay.call(this, this.elements.display.duration, this.duration); - } // Update the tooltip (if visible) - + } + if (this.config.markers.enabled) { + controls.setMarkers.call(this); + } + // Update the tooltip (if visible) controls.updateSeekTooltip.call(this); }, // Hide/show a tab - toggleMenuButton: function toggleMenuButton(setting, toggle) { + toggleMenuButton(setting, toggle) { toggleHidden(this.elements.settings.buttons[setting], !toggle); }, // Update the selected setting - updateSetting: function updateSetting(setting, container, input) { - var pane = this.elements.settings.panels[setting]; - var value = null; - var list = container; - + updateSetting(setting, container, input) { + const pane = this.elements.settings.panels[setting]; + let value = null; + let list = container; if (setting === 'captions') { value = this.currentTrack; } else { - value = !is$1.empty(input) ? input : this[setting]; // Get default + value = !is.empty(input) ? input : this[setting]; - if (is$1.empty(value)) { + // Get default + if (is.empty(value)) { value = this.config[setting].default; - } // Unsupported value - + } - if (!is$1.empty(this.options[setting]) && !this.options[setting].includes(value)) { - this.debug.warn("Unsupported value of '".concat(value, "' for ").concat(setting)); + // Unsupported value + if (!is.empty(this.options[setting]) && !this.options[setting].includes(value)) { + this.debug.warn(`Unsupported value of '${value}' for ${setting}`); return; - } // Disabled value - + } + // Disabled value if (!this.config[setting].options.includes(value)) { - this.debug.warn("Disabled value of '".concat(value, "' for ").concat(setting)); + this.debug.warn(`Disabled value of '${value}' for ${setting}`); return; } - } // Get the list if we need to - + } - if (!is$1.element(list)) { + // Get the list if we need to + if (!is.element(list)) { list = pane && pane.querySelector('[role="menu"]'); - } // If there's no list it means it's not been rendered... - + } - if (!is$1.element(list)) { + // If there's no list it means it's not been rendered... + if (!is.element(list)) { return; - } // Update the label - - - var label = this.elements.settings.buttons[setting].querySelector(".".concat(this.config.classNames.menu.value)); - label.innerHTML = controls.getLabel.call(this, setting, value); // Find the radio option and check it + } - var target = list && list.querySelector("[value=\"".concat(value, "\"]")); + // Update the label + const label = this.elements.settings.buttons[setting].querySelector(`.${this.config.classNames.menu.value}`); + label.innerHTML = controls.getLabel.call(this, setting, value); - if (is$1.element(target)) { + // Find the radio option and check it + const target = list && list.querySelector(`[value="${value}"]`); + if (is.element(target)) { target.checked = true; } }, // Translate a value into a nice label - getLabel: function getLabel(setting, value) { + getLabel(setting, value) { switch (setting) { case 'speed': - return value === 1 ? i18n.get('normal', this.config) : "".concat(value, "×"); - + return value === 1 ? i18n.get('normal', this.config) : `${value}×`; case 'quality': - if (is$1.number(value)) { - var label = i18n.get("qualityLabel.".concat(value), this.config); - + if (is.number(value)) { + const label = i18n.get(`qualityLabel.${value}`, this.config); if (!label.length) { - return "".concat(value, "p"); + return `${value}p`; } - return label; } - return toTitleCase(value); - case 'captions': return captions.getLabel.call(this); - default: return null; } }, // Set the quality menu - setQualityMenu: function setQualityMenu(options) { - var _this6 = this; - + setQualityMenu(options) { // Menu required - if (!is$1.element(this.elements.settings.panels.quality)) { + if (!is.element(this.elements.settings.panels.quality)) { return; } + const type = 'quality'; + const list = this.elements.settings.panels.quality.querySelector('[role="menu"]'); - var type = 'quality'; - var list = this.elements.settings.panels.quality.querySelector('[role="menu"]'); // Set options if passed and filter based on uniqueness and config - - if (is$1.array(options)) { - this.options.quality = dedupe(options).filter(function (quality) { - return _this6.config.quality.options.includes(quality); - }); - } // Toggle the pane and tab - + // Set options if passed and filter based on uniqueness and config + if (is.array(options)) { + this.options.quality = dedupe(options).filter(quality => this.config.quality.options.includes(quality)); + } - var toggle = !is$1.empty(this.options.quality) && this.options.quality.length > 1; - controls.toggleMenuButton.call(this, type, toggle); // Empty the menu + // Toggle the pane and tab + const toggle = !is.empty(this.options.quality) && this.options.quality.length > 1; + controls.toggleMenuButton.call(this, type, toggle); - emptyElement(list); // Check if we need to toggle the parent + // Empty the menu + emptyElement(list); - controls.checkMenu.call(this); // If we're hiding, nothing more to do + // Check if we need to toggle the parent + controls.checkMenu.call(this); + // If we're hiding, nothing more to do if (!toggle) { return; - } // Get the badge HTML for HD, 4K etc - - - var getBadge = function getBadge(quality) { - var label = i18n.get("qualityBadge.".concat(quality), _this6.config); + } + // Get the badge HTML for HD, 4K etc + const getBadge = quality => { + const label = i18n.get(`qualityBadge.${quality}`, this.config); if (!label.length) { return null; } + return controls.createBadge.call(this, label); + }; - return controls.createBadge.call(_this6, label); - }; // Sort options by the config and then render options - - - this.options.quality.sort(function (a, b) { - var sorting = _this6.config.quality.options; + // Sort options by the config and then render options + this.options.quality.sort((a, b) => { + const sorting = this.config.quality.options; return sorting.indexOf(a) > sorting.indexOf(b) ? 1 : -1; - }).forEach(function (quality) { - controls.createMenuItem.call(_this6, { + }).forEach(quality => { + controls.createMenuItem.call(this, { value: quality, - list: list, - type: type, - title: controls.getLabel.call(_this6, 'quality', quality), + list, + type, + title: controls.getLabel.call(this, 'quality', quality), badge: getBadge(quality) }); }); controls.updateSetting.call(this, type, list); }, // Set the looping options - /* setLoopMenu() { // Menu required if (!is.element(this.elements.settings.panels.loop)) { @@ -2532,562 +2275,589 @@ typeof navigator === "object" && (function (global, factory) { list.appendChild(item); }); }, */ + // Get current selected caption language // TODO: rework this to user the getter in the API? - // Set a list of available captions languages - setCaptionsMenu: function setCaptionsMenu() { - var _this7 = this; + // Set a list of available captions languages + setCaptionsMenu() { // Menu required - if (!is$1.element(this.elements.settings.panels.captions)) { + if (!is.element(this.elements.settings.panels.captions)) { return; - } // TODO: Captions or language? Currently it's mixed - + } - var type = 'captions'; - var list = this.elements.settings.panels.captions.querySelector('[role="menucaptions"]'); - var tracks = captions.getTracks.call(this); - var toggle = Boolean(tracks.length); // Toggle the pane and tab + // TODO: Captions or language? Currently it's mixed + const type = 'captions'; + const list = this.elements.settings.panels.captions.querySelector('[role="menu"]'); + const tracks = captions.getTracks.call(this); + const toggle = Boolean(tracks.length); - controls.toggleMenuButton.call(this, type, toggle); // Empty the menu + // Toggle the pane and tab + controls.toggleMenuButton.call(this, type, toggle); - emptyElement(list); // Check if we need to toggle the parent + // Empty the menu + emptyElement(list); - controls.checkMenu.call(this); // If there's no captions, bail + // Check if we need to toggle the parent + controls.checkMenu.call(this); + // If there's no captions, bail if (!toggle) { return; - } // Generate options data - + } - var options = tracks.map(function (track, value) { - return { - value: value, - checked: _this7.captions.toggled && _this7.currentTrack === value, - title: captions.getLabel.call(_this7, track), - badge: track.language && controls.createBadge.call(_this7, track.language.toUpperCase()), - list: list, - type: 'language' - }; - }); // Add the "Disabled" option to turn off captions + // Generate options data + const options = tracks.map((track, value) => ({ + value, + checked: this.captions.toggled && this.currentTrack === value, + title: captions.getLabel.call(this, track), + badge: track.language && controls.createBadge.call(this, track.language.toUpperCase()), + list, + type: 'language' + })); + // Add the "Disabled" option to turn off captions options.unshift({ value: -1, checked: !this.captions.toggled, title: i18n.get('disabled', this.config), - list: list, + list, type: 'language' - }); // Generate options + }); + // Generate options options.forEach(controls.createMenuItem.bind(this)); controls.updateSetting.call(this, type, list); }, // Set a list of available captions languages - setSpeedMenu: function setSpeedMenu() { - var _this8 = this; - + setSpeedMenu() { // Menu required - if (!is$1.element(this.elements.settings.panels.speed)) { + if (!is.element(this.elements.settings.panels.speed)) { return; } + const type = 'speed'; + const list = this.elements.settings.panels.speed.querySelector('[role="menu"]'); - var type = 'speed'; - var list = this.elements.settings.panels.speed.querySelector('[role="menu"]'); // Filter out invalid speeds - - this.options.speed = this.options.speed.filter(function (o) { - return o >= _this8.minimumSpeed && o <= _this8.maximumSpeed; - }); // Toggle the pane and tab + // Filter out invalid speeds + this.options.speed = this.options.speed.filter(o => o >= this.minimumSpeed && o <= this.maximumSpeed); - var toggle = !is$1.empty(this.options.speed) && this.options.speed.length > 1; - controls.toggleMenuButton.call(this, type, toggle); // Empty the menu + // Toggle the pane and tab + const toggle = !is.empty(this.options.speed) && this.options.speed.length > 1; + controls.toggleMenuButton.call(this, type, toggle); - emptyElement(list); // Check if we need to toggle the parent + // Empty the menu + emptyElement(list); - controls.checkMenu.call(this); // If we're hiding, nothing more to do + // Check if we need to toggle the parent + controls.checkMenu.call(this); + // If we're hiding, nothing more to do if (!toggle) { return; - } // Create items - + } - this.options.speed.forEach(function (speed) { - controls.createMenuItem.call(_this8, { + // Create items + this.options.speed.forEach(speed => { + controls.createMenuItem.call(this, { value: speed, - list: list, - type: type, - title: controls.getLabel.call(_this8, 'speed', speed) + list, + type, + title: controls.getLabel.call(this, 'speed', speed) }); }); controls.updateSetting.call(this, type, list); }, // Check if we need to hide/show the settings menu - checkMenu: function checkMenu() { - var buttons = this.elements.settings.buttons; - var visible = !is$1.empty(buttons) && Object.values(buttons).some(function (button) { - return !button.hidden; - }); + checkMenu() { + const { + buttons + } = this.elements.settings; + const visible = !is.empty(buttons) && Object.values(buttons).some(button => !button.hidden); toggleHidden(this.elements.settings.menu, !visible); }, // Focus the first menu item in a given (or visible) menu - focusFirstMenuItem: function focusFirstMenuItem(pane) { - var tabFocus = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; - + focusFirstMenuItem(pane, focusVisible = false) { if (this.elements.settings.popup.hidden) { return; } - - var target = pane; - - if (!is$1.element(target)) { - target = Object.values(this.elements.settings.panels).find(function (p) { - return !p.hidden; - }); + let target = pane; + if (!is.element(target)) { + target = Object.values(this.elements.settings.panels).find(p => !p.hidden); } - - var firstItem = target.querySelector('[role^="menuitem"]'); - setFocus.call(this, firstItem, tabFocus); + const firstItem = target.querySelector('[role^="menuitem"]'); + setFocus.call(this, firstItem, focusVisible); }, // Show/hide menu - toggleMenu: function toggleMenu(input) { - var popup = this.elements.settings.popup; - var button = this.elements.buttons.settings; // Menu and button are required - - if (!is$1.element(popup) || !is$1.element(button)) { + toggleMenu(input) { + const { + popup + } = this.elements.settings; + const button = this.elements.buttons.settings; + + // Menu and button are required + if (!is.element(popup) || !is.element(button)) { return; - } // True toggle by default - - - var hidden = popup.hidden; - var show = hidden; + } - if (is$1.boolean(input)) { + // True toggle by default + const { + hidden + } = popup; + let show = hidden; + if (is.boolean(input)) { show = input; - } else if (is$1.keyboardEvent(input) && input.which === 27) { + } else if (is.keyboardEvent(input) && input.key === 'Escape') { show = false; - } else if (is$1.event(input)) { + } else if (is.event(input)) { // If Plyr is in a shadowDOM, the event target is set to the component, instead of the // Element in the shadowDOM. The path, if available, is complete. - var target = is$1.function(input.composedPath) ? input.composedPath()[0] : input.target; - var isMenuItem = popup.contains(target); // If the click was inside the menu or if the click + const target = is.function(input.composedPath) ? input.composedPath()[0] : input.target; + const isMenuItem = popup.contains(target); + + // If the click was inside the menu or if the click // wasn't the button or menu item and we're trying to // show the menu (a doc click shouldn't show the menu) - if (isMenuItem || !isMenuItem && input.target !== button && show) { return; } - } // Set button attributes - + } - button.setAttribute('aria-expanded', show); // Show the actual popup + // Set button attributes + button.setAttribute('aria-expanded', show); - toggleHidden(popup, !show); // Add class hook + // Show the actual popup + toggleHidden(popup, !show); - toggleClass(this.elements.container, this.config.classNames.menu.open, show); // Focus the first item if key interaction + // Add class hook + toggleClass(this.elements.container, this.config.classNames.menu.open, show); - if (show && is$1.keyboardEvent(input)) { + // Focus the first item if key interaction + if (show && is.keyboardEvent(input)) { controls.focusFirstMenuItem.call(this, null, true); } else if (!show && !hidden) { // If closing, re-focus the button - setFocus.call(this, button, is$1.keyboardEvent(input)); + setFocus.call(this, button, is.keyboardEvent(input)); } }, // Get the natural size of a menu panel - getMenuSize: function getMenuSize(tab) { - var clone = tab.cloneNode(true); + getMenuSize(tab) { + const clone = tab.cloneNode(true); clone.style.position = 'absolute'; clone.style.opacity = 0; - clone.removeAttribute('hidden'); // Append to parent so we get the "real" size + clone.removeAttribute('hidden'); - tab.parentNode.appendChild(clone); // Get the sizes before we remove + // Append to parent so we get the "real" size + tab.parentNode.appendChild(clone); - var width = clone.scrollWidth; - var height = clone.scrollHeight; // Remove from the DOM + // Get the sizes before we remove + const width = clone.scrollWidth; + const height = clone.scrollHeight; + // Remove from the DOM removeElement(clone); return { - width: width, - height: height + width, + height }; }, // Show a panel in the menu - showMenuPanel: function showMenuPanel() { - var _this9 = this; + showMenuPanel(type = '', focusVisible = false) { + const target = this.elements.container.querySelector(`#plyr-settings-${this.id}-${type}`); - var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; - var tabFocus = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; - var target = this.elements.container.querySelector("#plyr-settings-".concat(this.id, "-").concat(type)); // Nothing to show, bail - - if (!is$1.element(target)) { + // Nothing to show, bail + if (!is.element(target)) { return; - } // Hide all other panels - + } - var container = target.parentNode; - var current = Array.from(container.children).find(function (node) { - return !node.hidden; - }); // If we can do fancy animations, we'll animate the height/width + // Hide all other panels + const container = target.parentNode; + const current = Array.from(container.children).find(node => !node.hidden); + // If we can do fancy animations, we'll animate the height/width if (support.transitions && !support.reducedMotion) { // Set the current width as a base - container.style.width = "".concat(current.scrollWidth, "px"); - container.style.height = "".concat(current.scrollHeight, "px"); // Get potential sizes + container.style.width = `${current.scrollWidth}px`; + container.style.height = `${current.scrollHeight}px`; - var size = controls.getMenuSize.call(this, target); // Restore auto height/width + // Get potential sizes + const size = controls.getMenuSize.call(this, target); - var restore = function restore(event) { + // Restore auto height/width + const restore = event => { // We're only bothered about height and width on the container if (event.target !== container || !['width', 'height'].includes(event.propertyName)) { return; - } // Revert back to auto - + } + // Revert back to auto container.style.width = ''; - container.style.height = ''; // Only listen once - - off.call(_this9, container, transitionEndEvent, restore); - }; // Listen for the transition finishing and restore auto height/width + container.style.height = ''; + // Only listen once + off.call(this, container, transitionEndEvent, restore); + }; - on.call(this, container, transitionEndEvent, restore); // Set dimensions to target - - container.style.width = "".concat(size.width, "px"); - container.style.height = "".concat(size.height, "px"); - } // Set attributes on current tab + // Listen for the transition finishing and restore auto height/width + on.call(this, container, transitionEndEvent, restore); + // Set dimensions to target + container.style.width = `${size.width}px`; + container.style.height = `${size.height}px`; + } - toggleHidden(current, true); // Set attributes on target + // Set attributes on current tab + toggleHidden(current, true); - toggleHidden(target, false); // Focus the first item + // Set attributes on target + toggleHidden(target, false); - controls.focusFirstMenuItem.call(this, target, tabFocus); + // Focus the first item + controls.focusFirstMenuItem.call(this, target, focusVisible); }, // Set the download URL - setDownloadUrl: function setDownloadUrl() { - var button = this.elements.buttons.download; // Bail if no button + setDownloadUrl() { + const button = this.elements.buttons.download; - if (!is$1.element(button)) { + // Bail if no button + if (!is.element(button)) { return; - } // Set attribute - + } + // Set attribute button.setAttribute('href', this.download); }, // Build the default HTML - create: function create(data) { - var _this10 = this; - - var bindMenuItemShortcuts = controls.bindMenuItemShortcuts, - createButton = controls.createButton, - createProgress = controls.createProgress, - createRange = controls.createRange, - createTime = controls.createTime, - setQualityMenu = controls.setQualityMenu, - setSpeedMenu = controls.setSpeedMenu, - showMenuPanel = controls.showMenuPanel; - this.elements.controls = null; // Larger overlaid play button - - if (is$1.array(this.config.controls) && this.config.controls.includes('play-large')) { + create(data) { + const { + bindMenuItemShortcuts, + createButton, + createProgress, + createRange, + createTime, + setQualityMenu, + setSpeedMenu, + showMenuPanel + } = controls; + this.elements.controls = null; + + // Larger overlaid play button + if (is.array(this.config.controls) && this.config.controls.includes('play-large')) { this.elements.container.appendChild(createButton.call(this, 'play-large')); - } // Create the container - + } - var container = createElement('div', getAttributesFromSelector(this.config.selectors.controls.wrapper)); - this.elements.controls = container; // Default item attributes + // Create the container + const container = createElement('div', getAttributesFromSelector(this.config.selectors.controls.wrapper)); + this.elements.controls = container; - var defaultAttributes = { + // Default item attributes + const defaultAttributes = { class: 'plyr__controls__item' - }; // Loop through controls in order + }; - dedupe(is$1.array(this.config.controls) ? this.config.controls : []).forEach(function (control) { + // Loop through controls in order + dedupe(is.array(this.config.controls) ? this.config.controls : []).forEach(control => { // Restart button if (control === 'restart') { - container.appendChild(createButton.call(_this10, 'restart', defaultAttributes)); - } // Rewind button - + container.appendChild(createButton.call(this, 'restart', defaultAttributes)); + } + // Rewind button if (control === 'rewind') { - container.appendChild(createButton.call(_this10, 'rewind', defaultAttributes)); - } // Play/Pause button - + container.appendChild(createButton.call(this, 'rewind', defaultAttributes)); + } + // Play/Pause button if (control === 'play') { - container.appendChild(createButton.call(_this10, 'play', defaultAttributes)); - } // Fast forward button - + container.appendChild(createButton.call(this, 'play', defaultAttributes)); + } + // Fast forward button if (control === 'fast-forward') { - container.appendChild(createButton.call(_this10, 'fast-forward', defaultAttributes)); - } // Progress - + container.appendChild(createButton.call(this, 'fast-forward', defaultAttributes)); + } + // Progress if (control === 'progress') { - var progressContainer = createElement('div', { - class: "".concat(defaultAttributes.class, " plyr__progress__container") + const progressContainer = createElement('div', { + class: `${defaultAttributes.class} plyr__progress__container` }); - var progress = createElement('div', getAttributesFromSelector(_this10.config.selectors.progress)); // Seek range slider + const progress = createElement('div', getAttributesFromSelector(this.config.selectors.progress)); - progress.appendChild(createRange.call(_this10, 'seek', { - id: "plyr-seek-".concat(data.id) - })); // Buffer progress + // Seek range slider + progress.appendChild(createRange.call(this, 'seek', { + id: `plyr-seek-${data.id}` + })); - progress.appendChild(createProgress.call(_this10, 'buffer')); // TODO: Add loop display indicator - // Seek tooltip + // Buffer progress + progress.appendChild(createProgress.call(this, 'buffer')); - if (_this10.config.tooltips.seek) { - var tooltip = createElement('span', { - class: _this10.config.classNames.tooltip + // TODO: Add loop display indicator + + // Seek tooltip + if (this.config.tooltips.seek) { + const tooltip = createElement('span', { + class: this.config.classNames.tooltip }, '00:00'); progress.appendChild(tooltip); - _this10.elements.display.seekTooltip = tooltip; + this.elements.display.seekTooltip = tooltip; } - - _this10.elements.progress = progress; - progressContainer.appendChild(_this10.elements.progress); + this.elements.progress = progress; + progressContainer.appendChild(this.elements.progress); container.appendChild(progressContainer); - } // Media current time display - + } + // Media current time display if (control === 'current-time') { - container.appendChild(createTime.call(_this10, 'currentTime', defaultAttributes)); - } // Media duration display - + container.appendChild(createTime.call(this, 'currentTime', defaultAttributes)); + } + // Media duration display if (control === 'duration') { - container.appendChild(createTime.call(_this10, 'duration', defaultAttributes)); - } // Volume controls - + container.appendChild(createTime.call(this, 'duration', defaultAttributes)); + } + // Volume controls if (control === 'mute' || control === 'volume') { - var volume = _this10.elements.volume; // Create the volume container if needed + let { + volume + } = this.elements; - if (!is$1.element(volume) || !container.contains(volume)) { + // Create the volume container if needed + if (!is.element(volume) || !container.contains(volume)) { volume = createElement('div', extend({}, defaultAttributes, { - class: "".concat(defaultAttributes.class, " plyr__volume").trim() + class: `${defaultAttributes.class} plyr__volume`.trim() })); - _this10.elements.volume = volume; + this.elements.volume = volume; container.appendChild(volume); - } // Toggle mute button - + } + // Toggle mute button if (control === 'mute') { - volume.appendChild(createButton.call(_this10, 'mute')); - } // Volume range control + volume.appendChild(createButton.call(this, 'mute')); + } + + // Volume range control // Ignored on iOS as it's handled globally // https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html - - - if (control === 'volume' && !browser.isIos) { + if (control === 'volume' && !browser.isIos && !browser.isIPadOS) { // Set the attributes - var attributes = { + const attributes = { max: 1, step: 0.05, - value: _this10.config.volume - }; // Create the volume range slider + value: this.config.volume + }; - volume.appendChild(createRange.call(_this10, 'volume', extend(attributes, { - id: "plyr-volume-".concat(data.id) + // Create the volume range slider + volume.appendChild(createRange.call(this, 'volume', extend(attributes, { + id: `plyr-volume-${data.id}` }))); } - } // Toggle captions button - + } + // Toggle captions button if (control === 'captions') { - container.appendChild(createButton.call(_this10, 'captions', defaultAttributes)); - } // Settings button / menu - + container.appendChild(createButton.call(this, 'captions', defaultAttributes)); + } - if (control === 'settings' && !is$1.empty(_this10.config.settings)) { - var wrapper = createElement('div', extend({}, defaultAttributes, { - class: "".concat(defaultAttributes.class, " plyr__menu").trim(), + // Settings button / menu + if (control === 'settings' && !is.empty(this.config.settings)) { + const wrapper = createElement('div', extend({}, defaultAttributes, { + class: `${defaultAttributes.class} plyr__menu`.trim(), hidden: '' })); - wrapper.appendChild(createButton.call(_this10, 'settings', { + wrapper.appendChild(createButton.call(this, 'settings', { 'aria-haspopup': true, - 'aria-controls': "plyr-settings-".concat(data.id), + 'aria-controls': `plyr-settings-${data.id}`, 'aria-expanded': false })); - var popup = createElement('div', { + const popup = createElement('div', { class: 'plyr__menu__container', - id: "plyr-settings-".concat(data.id), + id: `plyr-settings-${data.id}`, hidden: '' }); - var inner = createElement('div'); - var home = createElement('div', { - id: "plyr-settings-".concat(data.id, "-home") - }); // Create the menu + const inner = createElement('div'); + const home = createElement('div', { + id: `plyr-settings-${data.id}-home` + }); - var menu = createElement('div', { + // Create the menu + const menu = createElement('div', { role: 'menu' }); home.appendChild(menu); inner.appendChild(home); - _this10.elements.settings.panels.home = home; // Build the menu items + this.elements.settings.panels.home = home; - _this10.config.settings.forEach(function (type) { + // Build the menu items + this.config.settings.forEach(type => { // TODO: bundle this with the createMenuItem helper and bindings - var menuItem = createElement('button', extend(getAttributesFromSelector(_this10.config.selectors.buttons.settings), { + const menuItem = createElement('button', extend(getAttributesFromSelector(this.config.selectors.buttons.settings), { type: 'button', - class: "".concat(_this10.config.classNames.control, " ").concat(_this10.config.classNames.control, "--forward"), + class: `${this.config.classNames.control} ${this.config.classNames.control}--forward`, role: 'menuitem', 'aria-haspopup': true, hidden: '' - })); // Bind menu shortcuts for keyboard users + })); - bindMenuItemShortcuts.call(_this10, menuItem, type); // Show menu on click + // Bind menu shortcuts for keyboard users + bindMenuItemShortcuts.call(this, menuItem, type); - on.call(_this10, menuItem, 'click', function () { - showMenuPanel.call(_this10, type, false); + // Show menu on click + on.call(this, menuItem, 'click', () => { + showMenuPanel.call(this, type, false); + }); + const flex = createElement('span', null, i18n.get(type, this.config)); + const value = createElement('span', { + class: this.config.classNames.menu.value }); - var flex = createElement('span', null, i18n.get(type, _this10.config)); - var value = createElement('span', { - class: _this10.config.classNames.menu.value - }); // Speed contains HTML entities + // Speed contains HTML entities value.innerHTML = data[type]; flex.appendChild(value); menuItem.appendChild(flex); - menu.appendChild(menuItem); // Build the panes + menu.appendChild(menuItem); - var pane = createElement('div', { - id: "plyr-settings-".concat(data.id, "-").concat(type), + // Build the panes + const pane = createElement('div', { + id: `plyr-settings-${data.id}-${type}`, hidden: '' - }); // Back button + }); - var backButton = createElement('button', { + // Back button + const backButton = createElement('button', { type: 'button', - class: "".concat(_this10.config.classNames.control, " ").concat(_this10.config.classNames.control, "--back") - }); // Visible label + class: `${this.config.classNames.control} ${this.config.classNames.control}--back` + }); + // Visible label backButton.appendChild(createElement('span', { 'aria-hidden': true - }, i18n.get(type, _this10.config))); // Screen reader label + }, i18n.get(type, this.config))); + // Screen reader label backButton.appendChild(createElement('span', { - class: _this10.config.classNames.hidden - }, i18n.get('menuBack', _this10.config))); // Go back via keyboard - - on.call(_this10, pane, 'keydown', function (event) { - // We only care about <- - if (event.which !== 37) { - return; - } // Prevent seek + class: this.config.classNames.hidden + }, i18n.get('menuBack', this.config))); + // Go back via keyboard + on.call(this, pane, 'keydown', event => { + if (event.key !== 'ArrowLeft') return; + // Prevent seek event.preventDefault(); - event.stopPropagation(); // Show the respective menu + event.stopPropagation(); - showMenuPanel.call(_this10, 'home', true); - }, false); // Go back via button click + // Show the respective menu + showMenuPanel.call(this, 'home', true); + }, false); - on.call(_this10, backButton, 'click', function () { - showMenuPanel.call(_this10, 'home', false); - }); // Add to pane + // Go back via button click + on.call(this, backButton, 'click', () => { + showMenuPanel.call(this, 'home', false); + }); - pane.appendChild(backButton); // Menu + // Add to pane + pane.appendChild(backButton); + // Menu pane.appendChild(createElement('div', { role: 'menu' - })); // Menu Captions - - pane.appendChild(createElement('div', { - role: 'menucaptions' })); inner.appendChild(pane); - _this10.elements.settings.buttons[type] = menuItem; - _this10.elements.settings.panels[type] = pane; + this.elements.settings.buttons[type] = menuItem; + this.elements.settings.panels[type] = pane; }); - popup.appendChild(inner); wrapper.appendChild(popup); container.appendChild(wrapper); - _this10.elements.settings.popup = popup; - _this10.elements.settings.menu = wrapper; - } // Picture in picture button - + this.elements.settings.popup = popup; + this.elements.settings.menu = wrapper; + } + // Picture in picture button if (control === 'pip' && support.pip) { - container.appendChild(createButton.call(_this10, 'pip', defaultAttributes)); - } // Airplay button - + container.appendChild(createButton.call(this, 'pip', defaultAttributes)); + } + // Airplay button if (control === 'airplay' && support.airplay) { - container.appendChild(createButton.call(_this10, 'airplay', defaultAttributes)); - } // Download button - + container.appendChild(createButton.call(this, 'airplay', defaultAttributes)); + } + // Download button if (control === 'download') { - var _attributes = extend({}, defaultAttributes, { + const attributes = extend({}, defaultAttributes, { element: 'a', - href: _this10.download, + href: this.download, target: '_blank' - }); // Set download attribute for HTML5 only - + }); - if (_this10.isHTML5) { - _attributes.download = ''; + // Set download attribute for HTML5 only + if (this.isHTML5) { + attributes.download = ''; } - - var download = _this10.config.urls.download; - - if (!is$1.url(download) && _this10.isEmbed) { - extend(_attributes, { - icon: "logo-".concat(_this10.provider), - label: _this10.provider + const { + download + } = this.config.urls; + if (!is.url(download) && this.isEmbed) { + extend(attributes, { + icon: `logo-${this.provider}`, + label: this.provider }); } + container.appendChild(createButton.call(this, 'download', attributes)); + } - container.appendChild(createButton.call(_this10, 'download', _attributes)); - } // Toggle fullscreen button - - + // Toggle fullscreen button if (control === 'fullscreen') { - container.appendChild(createButton.call(_this10, 'fullscreen', defaultAttributes)); + container.appendChild(createButton.call(this, 'fullscreen', defaultAttributes)); } - }); // Set available quality levels + }); + // Set available quality levels if (this.isHTML5) { setQualityMenu.call(this, html5.getQualityOptions.call(this)); } - setSpeedMenu.call(this); return container; }, // Insert controls - inject: function inject() { - var _this11 = this; - + inject() { // Sprite if (this.config.loadSprite) { - var icon = controls.getIconUrl.call(this); // Only load external sprite using AJAX + const icon = controls.getIconUrl.call(this); + // Only load external sprite using AJAX if (icon.cors) { loadSprite(icon.url, 'sprite-plyr'); } - } // Create a unique ID - + } - this.id = Math.floor(Math.random() * 10000); // Null by default + // Create a unique ID + this.id = Math.floor(Math.random() * 10000); - var container = null; - this.elements.controls = null; // Set template properties + // Null by default + let container = null; + this.elements.controls = null; - var props = { + // Set template properties + const props = { id: this.id, seektime: this.config.seekTime, title: this.config.title }; - var update = true; // If function, run it and use output + let update = true; - if (is$1.function(this.config.controls)) { + // If function, run it and use output + if (is.function(this.config.controls)) { this.config.controls = this.config.controls.call(this, props); - } // Convert falsy controls to empty array (primarily for empty strings) - + } + // Convert falsy controls to empty array (primarily for empty strings) if (!this.config.controls) { this.config.controls = []; } - - if (is$1.element(this.config.controls) || is$1.string(this.config.controls)) { + if (is.element(this.config.controls) || is.string(this.config.controls)) { // HTMLElement or Non-empty string passed as the option container = this.config.controls; } else { @@ -3097,292 +2867,358 @@ typeof navigator === "object" && (function (global, factory) { seektime: this.config.seekTime, speed: this.speed, quality: this.quality, - captions: captions.getLabel.call(this) // TODO: Looping + captions: captions.getLabel.call(this) + // TODO: Looping // loop: 'None', - }); update = false; - } // Replace props with their value - - - var replace = function replace(input) { - var result = input; - Object.entries(props).forEach(function (_ref2) { - var _ref3 = _slicedToArray(_ref2, 2), - key = _ref3[0], - value = _ref3[1]; + } - result = replaceAll(result, "{".concat(key, "}"), value); + // Replace props with their value + const replace = input => { + let result = input; + Object.entries(props).forEach(([key, value]) => { + result = replaceAll(result, `{${key}}`, value); }); return result; - }; // Update markup - + }; + // Update markup if (update) { - if (is$1.string(this.config.controls)) { + if (is.string(this.config.controls)) { container = replace(container); } - } // Controls container - + } - var target; // Inject to custom location + // Controls container + let target; - if (is$1.string(this.config.selectors.controls.container)) { + // Inject to custom location + if (is.string(this.config.selectors.controls.container)) { target = document.querySelector(this.config.selectors.controls.container); - } // Inject into the container by default - + } - if (!is$1.element(target)) { + // Inject into the container by default + if (!is.element(target)) { target = this.elements.container; - } // Inject controls HTML (needs to be before captions, hence "afterbegin") - + } - var insertMethod = is$1.element(container) ? 'insertAdjacentElement' : 'insertAdjacentHTML'; - target[insertMethod]('afterbegin', container); // Find the elements if need be + // Inject controls HTML (needs to be before captions, hence "afterbegin") + const insertMethod = is.element(container) ? 'insertAdjacentElement' : 'insertAdjacentHTML'; + target[insertMethod]('afterbegin', container); - if (!is$1.element(this.elements.controls)) { + // Find the elements if need be + if (!is.element(this.elements.controls)) { controls.findElements.call(this); - } // Add pressed property to buttons - + } - if (!is$1.empty(this.elements.buttons)) { - var addProperty = function addProperty(button) { - var className = _this11.config.classNames.controlPressed; + // Add pressed property to buttons + if (!is.empty(this.elements.buttons)) { + const addProperty = button => { + const className = this.config.classNames.controlPressed; + button.setAttribute('aria-pressed', 'false'); Object.defineProperty(button, 'pressed', { + configurable: true, enumerable: true, - get: function get() { + get() { return hasClass(button, className); }, - set: function set() { - var pressed = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; + set(pressed = false) { toggleClass(button, className, pressed); + button.setAttribute('aria-pressed', pressed ? 'true' : 'false'); } }); - }; // Toggle classname when pressed property is set - + }; - Object.values(this.elements.buttons).filter(Boolean).forEach(function (button) { - if (is$1.array(button) || is$1.nodeList(button)) { + // Toggle classname when pressed property is set + Object.values(this.elements.buttons).filter(Boolean).forEach(button => { + if (is.array(button) || is.nodeList(button)) { Array.from(button).filter(Boolean).forEach(addProperty); } else { addProperty(button); } }); - } // Edge sometimes doesn't finish the paint so force a repaint - + } + // Edge sometimes doesn't finish the paint so force a repaint if (browser.isEdge) { repaint(target); - } // Setup tooltips - + } + // Setup tooltips if (this.config.tooltips.controls) { - var _this$config = this.config, - classNames = _this$config.classNames, - selectors = _this$config.selectors; - var selector = "".concat(selectors.controls.wrapper, " ").concat(selectors.labels, " .").concat(classNames.hidden); - var labels = getElements.call(this, selector); - Array.from(labels).forEach(function (label) { - toggleClass(label, _this11.config.classNames.hidden, false); - toggleClass(label, _this11.config.classNames.tooltip, true); + const { + classNames, + selectors + } = this.config; + const selector = `${selectors.controls.wrapper} ${selectors.labels} .${classNames.hidden}`; + const labels = getElements.call(this, selector); + Array.from(labels).forEach(label => { + toggleClass(label, this.config.classNames.hidden, false); + toggleClass(label, this.config.classNames.tooltip, true); }); } + }, + // Set media metadata + setMediaMetadata() { + try { + if ('mediaSession' in navigator) { + navigator.mediaSession.metadata = new window.MediaMetadata({ + title: this.config.mediaMetadata.title, + artist: this.config.mediaMetadata.artist, + album: this.config.mediaMetadata.album, + artwork: this.config.mediaMetadata.artwork + }); + } + } catch (_) { + // Do nothing + } + }, + // Add markers + setMarkers() { + var _this$config$markers2, _this$config$markers3; + if (!this.duration || this.elements.markers) return; + + // Get valid points + const points = (_this$config$markers2 = this.config.markers) === null || _this$config$markers2 === void 0 ? void 0 : (_this$config$markers3 = _this$config$markers2.points) === null || _this$config$markers3 === void 0 ? void 0 : _this$config$markers3.filter(({ + time + }) => time > 0 && time < this.duration); + if (!(points !== null && points !== void 0 && points.length)) return; + const containerFragment = document.createDocumentFragment(); + const pointsFragment = document.createDocumentFragment(); + let tipElement = null; + const tipVisible = `${this.config.classNames.tooltip}--visible`; + const toggleTip = show => toggleClass(tipElement, tipVisible, show); + + // Inject markers to progress container + points.forEach(point => { + const markerElement = createElement('span', { + class: this.config.classNames.marker + }, ''); + const left = `${point.time / this.duration * 100}%`; + if (tipElement) { + // Show on hover + markerElement.addEventListener('mouseenter', () => { + if (point.label) return; + tipElement.style.left = left; + tipElement.innerHTML = point.label; + toggleTip(true); + }); + + // Hide on leave + markerElement.addEventListener('mouseleave', () => { + toggleTip(false); + }); + } + markerElement.addEventListener('click', () => { + this.currentTime = point.time; + }); + markerElement.style.left = left; + pointsFragment.appendChild(markerElement); + }); + containerFragment.appendChild(pointsFragment); + + // Inject a tooltip if needed + if (!this.config.tooltips.seek) { + tipElement = createElement('span', { + class: this.config.classNames.tooltip + }, ''); + containerFragment.appendChild(tipElement); + } + this.elements.markers = { + points: pointsFragment, + tip: tipElement + }; + this.elements.progress.appendChild(containerFragment); } }; + // ========================================================================== + // URL utils + // ========================================================================== + + /** * Parse a string to a URL object * @param {String} input - the URL to be parsed * @param {Boolean} safe - failsafe parsing */ - - function parseUrl(input) { - var safe = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; - var url = input; - + function parseUrl(input, safe = true) { + let url = input; if (safe) { - var parser = document.createElement('a'); + const parser = document.createElement('a'); parser.href = url; url = parser.href; } - try { return new URL(url); - } catch (e) { + } catch (_) { return null; } - } // Convert object to URLSearchParams + } + // Convert object to URLSearchParams function buildUrlParams(input) { - var params = new URLSearchParams(); - - if (is$1.object(input)) { - Object.entries(input).forEach(function (_ref) { - var _ref2 = _slicedToArray(_ref, 2), - key = _ref2[0], - value = _ref2[1]; - + const params = new URLSearchParams(); + if (is.object(input)) { + Object.entries(input).forEach(([key, value]) => { params.set(key, value); }); } - return params; } - var captions = { + // ========================================================================== + // Plyr Captions + // TODO: Create as class + // ========================================================================== + + const captions = { // Setup captions - setup: function setup() { + setup() { // Requires UI support if (!this.supported.ui) { return; - } // Only Vimeo and HTML5 video supported at this point - + } + // Only Vimeo and HTML5 video supported at this point if (!this.isVideo || this.isYouTube || this.isHTML5 && !support.textTracks) { // Clear menu and hide - if (is$1.array(this.config.controls) && this.config.controls.includes('settings') && this.config.settings.includes('captions')) { + if (is.array(this.config.controls) && this.config.controls.includes('settings') && this.config.settings.includes('captions')) { controls.setCaptionsMenu.call(this); } - return; - } // Inject the container - + } - if (!is$1.element(this.elements.captions)) { + // Inject the container + if (!is.element(this.elements.captions)) { this.elements.captions = createElement('div', getAttributesFromSelector(this.config.selectors.captions)); + this.elements.captions.setAttribute('dir', 'auto'); insertAfter(this.elements.captions, this.elements.wrapper); - } // Fix IE captions if CORS is used - // Fetch captions and inject as blobs instead (data URIs not supported!) - + } + // Fix IE captions if CORS is used + // Fetch captions and inject as blobs instead (data URIs not supported!) if (browser.isIE && window.URL) { - var elements = this.media.querySelectorAll('track'); - Array.from(elements).forEach(function (track) { - var src = track.getAttribute('src'); - var url = parseUrl(src); - + const elements = this.media.querySelectorAll('track'); + Array.from(elements).forEach(track => { + const src = track.getAttribute('src'); + const url = parseUrl(src); if (url !== null && url.hostname !== window.location.href.hostname && ['http:', 'https:'].includes(url.protocol)) { - fetch(src, 'blob').then(function (blob) { + fetch(src, 'blob').then(blob => { track.setAttribute('src', window.URL.createObjectURL(blob)); - }).catch(function () { + }).catch(() => { removeElement(track); }); } }); - } // Get and set initial data + } + + // Get and set initial data // The "preferred" options are not realized unless / until the wanted language has a match // * languages: Array of user's browser languages. // * language: The language preferred by user settings or config // * active: The state preferred by user settings or config // * toggled: The real captions state + const browserLanguages = navigator.languages || [navigator.language || navigator.userLanguage || 'en']; + const languages = dedupe(browserLanguages.map(language => language.split('-')[0])); + let language = (this.storage.get('language') || this.config.captions.language || 'auto').toLowerCase(); - var browserLanguages = navigator.languages || [navigator.language || navigator.userLanguage || 'en']; - var languages = dedupe(browserLanguages.map(function (language) { - return language.split('-')[0]; - })); - var language = (this.storage.get('language') || this.config.captions.language || 'auto').toLowerCase(); // Use first browser language when language is 'auto' - + // Use first browser language when language is 'auto' if (language === 'auto') { - var _languages = _slicedToArray(languages, 1); - - language = _languages[0]; + [language] = languages; } - - var active = this.storage.get('captions'); - - if (!is$1.boolean(active)) { - active = this.config.captions.active; + let active = this.storage.get('captions'); + if (!is.boolean(active)) { + ({ + active + } = this.config.captions); } - Object.assign(this.captions, { toggled: false, - active: active, - language: language, - languages: languages - }); // Watch changes to textTracks and update captions menu + active, + language, + languages + }); + // Watch changes to textTracks and update captions menu if (this.isHTML5) { - var trackEvents = this.config.captions.update ? 'addtrack removetrack' : 'removetrack'; + const trackEvents = this.config.captions.update ? 'addtrack removetrack' : 'removetrack'; on.call(this, this.media.textTracks, trackEvents, captions.update.bind(this)); - } // Update available languages in list next tick (the event must not be triggered before the listeners) - + } + // Update available languages in list next tick (the event must not be triggered before the listeners) setTimeout(captions.update.bind(this), 0); }, // Update available language options in settings based on tracks - update: function update() { - var _this = this; - - var tracks = captions.getTracks.call(this, true); // Get the wanted language - - var _this$captions = this.captions, - active = _this$captions.active, - language = _this$captions.language, - meta = _this$captions.meta, - currentTrackNode = _this$captions.currentTrackNode; - var languageExists = Boolean(tracks.find(function (track) { - return track.language === language; - })); // Handle tracks (add event listener and "pseudo"-default) - + update() { + const tracks = captions.getTracks.call(this, true); + // Get the wanted language + const { + active, + language, + meta, + currentTrackNode + } = this.captions; + const languageExists = Boolean(tracks.find(track => track.language === language)); + + // Handle tracks (add event listener and "pseudo"-default) if (this.isHTML5 && this.isVideo) { - tracks.filter(function (track) { - return !meta.get(track); - }).forEach(function (track) { - _this.debug.log('Track added', track); // Attempt to store if the original dom element was "default" - + tracks.filter(track => !meta.get(track)).forEach(track => { + this.debug.log('Track added', track); + // Attempt to store if the original dom element was "default" meta.set(track, { default: track.mode === 'showing' - }); // Turn off native caption rendering to avoid double captions + }); + + // Turn off native caption rendering to avoid double captions // Note: mode='hidden' forces a track to download. To ensure every track // isn't downloaded at once, only 'showing' tracks should be reassigned // eslint-disable-next-line no-param-reassign - if (track.mode === 'showing') { // eslint-disable-next-line no-param-reassign track.mode = 'hidden'; - } // Add event listener for cue changes - + } - on.call(_this, track, 'cuechange', function () { - return captions.updateCues.call(_this); - }); + // Add event listener for cue changes + on.call(this, track, 'cuechange', () => captions.updateCues.call(this)); }); - } // Update language first time it matches, or if the previous matching track was removed - + } + // Update language first time it matches, or if the previous matching track was removed if (languageExists && this.language !== language || !tracks.includes(currentTrackNode)) { captions.setLanguage.call(this, language); captions.toggle.call(this, active && languageExists); - } // Enable or disable captions based on track length - + } - toggleClass(this.elements.container, this.config.classNames.captions.enabled, !is$1.empty(tracks)); // Update available languages in list + // Enable or disable captions based on track length + if (this.elements) { + toggleClass(this.elements.container, this.config.classNames.captions.enabled, !is.empty(tracks)); + } - if (is$1.array(this.config.controls) && this.config.controls.includes('settings') && this.config.settings.includes('captions')) { + // Update available languages in list + if (is.array(this.config.controls) && this.config.controls.includes('settings') && this.config.settings.includes('captions')) { controls.setCaptionsMenu.call(this); } }, // Toggle captions display // Used internally for the toggleCaptions method, with the passive option forced to false - toggle: function toggle(input) { - var _this2 = this; - - var passive = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; - + toggle(input, passive = true) { // If there's no full support if (!this.supported.ui) { return; } - - var toggled = this.captions.toggled; // Current state - - var activeClass = this.config.classNames.captions.active; // Get the next state + const { + toggled + } = this.captions; // Current state + const activeClass = this.config.classNames.captions.active; + // Get the next state // If the method is called without parameter, toggle based on current value + const active = is.nullOrUndefined(input) ? !toggled : input; - var active = is$1.nullOrUndefined(input) ? !toggled : input; // Update state and trigger event - + // Update state and trigger event if (active !== toggled) { // When passive, don't override user preferences if (!passive) { @@ -3390,93 +3226,95 @@ typeof navigator === "object" && (function (global, factory) { this.storage.set({ captions: active }); - } // Force language if the call isn't passive and there is no matching language to toggle to - + } + // Force language if the call isn't passive and there is no matching language to toggle to if (!this.language && active && !passive) { - var tracks = captions.getTracks.call(this); - var track = captions.findTrack.call(this, [this.captions.language].concat(_toConsumableArray(this.captions.languages)), true); // Override user preferences to avoid switching languages if a matching track is added + const tracks = captions.getTracks.call(this); + const track = captions.findTrack.call(this, [this.captions.language, ...this.captions.languages], true); - this.captions.language = track.language; // Set caption, but don't store in localStorage as user preference + // Override user preferences to avoid switching languages if a matching track is added + this.captions.language = track.language; + // Set caption, but don't store in localStorage as user preference captions.set.call(this, tracks.indexOf(track)); return; - } // Toggle button if it's enabled - + } + // Toggle button if it's enabled if (this.elements.buttons.captions) { this.elements.buttons.captions.pressed = active; - } // Add class hook - + } + // Add class hook toggleClass(this.elements.container, activeClass, active); - this.captions.toggled = active; // Update settings menu + this.captions.toggled = active; - controls.updateSetting.call(this, 'captions'); // Trigger event (not used internally) + // Update settings menu + controls.updateSetting.call(this, 'captions'); + // Trigger event (not used internally) triggerEvent.call(this, this.media, active ? 'captionsenabled' : 'captionsdisabled'); - } // Wait for the call stack to clear before setting mode='hidden' - // on the active track - forcing the browser to download it - + } - setTimeout(function () { - if (active && _this2.captions.toggled) { - _this2.captions.currentTrackNode.mode = 'hidden'; + // Wait for the call stack to clear before setting mode='hidden' + // on the active track - forcing the browser to download it + setTimeout(() => { + if (active && this.captions.toggled) { + this.captions.currentTrackNode.mode = 'hidden'; } }); }, // Set captions by track index // Used internally for the currentTrack setter with the passive option forced to false - set: function set(index) { - var passive = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; - var tracks = captions.getTracks.call(this); // Disable captions if setting to -1 + set(index, passive = true) { + const tracks = captions.getTracks.call(this); + // Disable captions if setting to -1 if (index === -1) { captions.toggle.call(this, false, passive); return; } - - if (!is$1.number(index)) { + if (!is.number(index)) { this.debug.warn('Invalid caption argument', index); return; } - if (!(index in tracks)) { this.debug.warn('Track not found', index); return; } - if (this.captions.currentTrack !== index) { this.captions.currentTrack = index; - var track = tracks[index]; - - var _ref = track || {}, - language = _ref.language; // Store reference to node for invalidation on remove - + const track = tracks[index]; + const { + language + } = track || {}; - this.captions.currentTrackNode = track; // Update settings menu + // Store reference to node for invalidation on remove + this.captions.currentTrackNode = track; - controls.updateSetting.call(this, 'captions'); // When passive, don't override user preferences + // Update settings menu + controls.updateSetting.call(this, 'captions'); + // When passive, don't override user preferences if (!passive) { this.captions.language = language; this.storage.set({ - language: language + language }); - } // Handle Vimeo captions - + } + // Handle Vimeo captions if (this.isVimeo) { this.embed.enableTextTrack(language); - } // Trigger event - + } + // Trigger event triggerEvent.call(this, this.media, 'languagechange'); - } // Show captions - + } + // Show captions captions.toggle.call(this, true, passive); - if (this.isHTML5 && this.isVideo) { // If we change the active track while a cue is already displayed we need to update it captions.updateCues.call(this); @@ -3484,130 +3322,101 @@ typeof navigator === "object" && (function (global, factory) { }, // Set captions by language // Used internally for the language setter with the passive option forced to false - setLanguage: function setLanguage(input) { - var passive = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; - - if (!is$1.string(input)) { + setLanguage(input, passive = true) { + if (!is.string(input)) { this.debug.warn('Invalid language argument', input); return; - } // Normalize - - - var language = input.toLowerCase(); - this.captions.language = language; // Set currentTrack + } + // Normalize + const language = input.toLowerCase(); + this.captions.language = language; - var tracks = captions.getTracks.call(this); - var track = captions.findTrack.call(this, [language]); + // Set currentTrack + const tracks = captions.getTracks.call(this); + const track = captions.findTrack.call(this, [language]); captions.set.call(this, tracks.indexOf(track), passive); }, // Get current valid caption tracks // If update is false it will also ignore tracks without metadata // This is used to "freeze" the language options when captions.update is false - getTracks: function getTracks() { - var _this3 = this; - - var update = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; + getTracks(update = false) { // Handle media or textTracks missing or null - var tracks = Array.from((this.media || {}).textTracks || []); // For HTML5, use cache instead of current tracks when it exists (if captions.update is false) + const tracks = Array.from((this.media || {}).textTracks || []); + // For HTML5, use cache instead of current tracks when it exists (if captions.update is false) // Filter out removed tracks and tracks that aren't captions/subtitles (for example metadata) - - return tracks.filter(function (track) { - return !_this3.isHTML5 || update || _this3.captions.meta.has(track); - }).filter(function (track) { - return ['captions', 'subtitles'].includes(track.kind); - }); + return tracks.filter(track => !this.isHTML5 || update || this.captions.meta.has(track)).filter(track => ['captions', 'subtitles'].includes(track.kind)); }, // Match tracks based on languages and get the first - findTrack: function findTrack(languages) { - var _this4 = this; - - var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; - var tracks = captions.getTracks.call(this); - - var sortIsDefault = function sortIsDefault(track) { - return Number((_this4.captions.meta.get(track) || {}).default); - }; - - var sorted = Array.from(tracks).sort(function (a, b) { - return sortIsDefault(b) - sortIsDefault(a); - }); - var track; - languages.every(function (language) { - track = sorted.find(function (t) { - return t.language === language; - }); + findTrack(languages, force = false) { + const tracks = captions.getTracks.call(this); + const sortIsDefault = track => Number((this.captions.meta.get(track) || {}).default); + const sorted = Array.from(tracks).sort((a, b) => sortIsDefault(b) - sortIsDefault(a)); + let track; + languages.every(language => { + track = sorted.find(t => t.language === language); return !track; // Break iteration if there is a match - }); // If no match is found but is required, get first + }); + // If no match is found but is required, get first return track || (force ? sorted[0] : undefined); }, // Get the current track - getCurrentTrack: function getCurrentTrack() { + getCurrentTrack() { return captions.getTracks.call(this)[this.currentTrack]; }, // Get UI label for track - getLabel: function getLabel(track) { - var currentTrack = track; - - if (!is$1.track(currentTrack) && support.textTracks && this.captions.toggled) { + getLabel(track) { + let currentTrack = track; + if (!is.track(currentTrack) && support.textTracks && this.captions.toggled) { currentTrack = captions.getCurrentTrack.call(this); } - - if (is$1.track(currentTrack)) { - if (!is$1.empty(currentTrack.label)) { + if (is.track(currentTrack)) { + if (!is.empty(currentTrack.label)) { return currentTrack.label; } - - if (!is$1.empty(currentTrack.language)) { + if (!is.empty(currentTrack.language)) { return track.language.toUpperCase(); } - return i18n.get('enabled', this.config); } - return i18n.get('disabled', this.config); }, // Update captions using current track's active cues // Also optional array argument in case there isn't any track (ex: vimeo) - updateCues: function updateCues(input) { + updateCues(input) { // Requires UI if (!this.supported.ui) { return; } - - if (!is$1.element(this.elements.captions)) { + if (!is.element(this.elements.captions)) { this.debug.warn('No captions element to render to'); return; - } // Only accept array or empty input - + } - if (!is$1.nullOrUndefined(input) && !Array.isArray(input)) { + // Only accept array or empty input + if (!is.nullOrUndefined(input) && !Array.isArray(input)) { this.debug.warn('updateCues: Invalid input', input); return; } + let cues = input; - var cues = input; // Get cues from track - + // Get cues from track if (!cues) { - var track = captions.getCurrentTrack.call(this); - cues = Array.from((track || {}).activeCues || []).map(function (cue) { - return cue.getCueAsHTML(); - }).map(getHTML); - } // Set new caption text - - - var content = cues.map(function (cueText) { - return cueText.trim(); - }).join('\n'); - var changed = content !== this.elements.captions.innerHTML; + const track = captions.getCurrentTrack.call(this); + cues = Array.from((track || {}).activeCues || []).map(cue => cue.getCueAsHTML()).map(getHTML); + } + // Set new caption text + const content = cues.map(cueText => cueText.trim()).join('\n'); + const changed = content !== this.elements.captions.innerHTML; if (changed) { // Empty the container and create a new child element emptyElement(this.elements.captions); - var caption = createElement('span', getAttributesFromSelector(this.config.selectors.caption)); + const caption = createElement('span', getAttributesFromSelector(this.config.selectors.caption)); caption.innerHTML = content; - this.elements.captions.appendChild(caption); // Trigger event + this.elements.captions.appendChild(caption); + // Trigger event triggerEvent.call(this, this.media, 'cuechange'); } } @@ -3616,7 +3425,8 @@ typeof navigator === "object" && (function (global, factory) { // ========================================================================== // Plyr default config // ========================================================================== - var defaults$1 = { + + const defaults = { // Disable enabled: true, // Custom media title @@ -3627,8 +3437,7 @@ typeof navigator === "object" && (function (global, factory) { autoplay: false, // Only allow one media playing at once (vimeo only) autopause: true, - // Allow inline playback on iOS (this effects YouTube/Vimeo - HTML5 requires the attribute present) - // TODO: Remove iosNative fullscreen option in favour of this (logic needs work) + // Allow inline playback on iOS playsinline: true, // Default time to skip when rewind/fast forward seekTime: 10, @@ -3658,9 +3467,9 @@ typeof navigator === "object" && (function (global, factory) { // Sprite (for icons) loadSprite: true, iconPrefix: 'plyr', - iconUrl: '/theme/modules/plyr/plyr.svg', + iconUrl: 'https://cdn.plyr.io/3.7.8/plyr.svg', // Blank video (used to prevent errors on source change) - blankVideo: '/theme/modules/plyr/blank.webm', + blankVideo: 'https://cdn.plyr.io/static/blank.mp4', // Quality default quality: { default: 576, @@ -3671,9 +3480,9 @@ typeof navigator === "object" && (function (global, factory) { }, // Set loops loop: { - active: false // start: null, + active: false + // start: null, // end: null, - }, // Speed default and options to display speed: { @@ -3709,7 +3518,6 @@ typeof navigator === "object" && (function (global, factory) { // Selector for the fullscreen container so contextual / non-player content can remain visible in fullscreen mode // Non-ancestors of the player element will be ignored // container: null, // defaults to the player element - }, // Local storage storage: { @@ -3717,11 +3525,15 @@ typeof navigator === "object" && (function (global, factory) { key: 'plyr' }, // Default controls - controls: ['play-large', // 'restart', + controls: ['play-large', + // 'restart', // 'rewind', - 'play', // 'fast-forward', - 'progress', 'current-time', // 'duration', - 'mute', 'volume', 'captions', 'settings', 'pip', 'airplay', // 'download', + 'play', + // 'fast-forward', + 'progress', 'current-time', + // 'duration', + 'mute', 'volume', 'captions', 'settings', 'pip', 'airplay', + // 'download', 'fullscreen'], settings: ['captions', 'quality', 'speed'], // Localisation @@ -3776,7 +3588,7 @@ typeof navigator === "object" && (function (global, factory) { vimeo: { sdk: 'https://player.vimeo.com/api/player.js', iframe: 'https://player.vimeo.com/video/{0}?{1}', - api: 'https://vimeo.com/api/v2/video/{0}.json' + api: 'https://vimeo.com/api/oembed.json?url={0}' }, youtube: { sdk: 'https://www.youtube.com/iframe_api', @@ -3807,12 +3619,17 @@ typeof navigator === "object" && (function (global, factory) { language: null }, // Events to watch and bubble - events: [// Events to watch on HTML5 media elements and bubble + events: [ + // Events to watch on HTML5 media elements and bubble // https://developer.mozilla.org/en/docs/Web/Guide/Events/Media_events - 'ended', 'progress', 'stalled', 'playing', 'waiting', 'canplay', 'canplaythrough', 'loadstart', 'loadeddata', 'loadedmetadata', 'timeupdate', 'volumechange', 'play', 'pause', 'error', 'seeking', 'seeked', 'emptied', 'ratechange', 'cuechange', // Custom events - 'download', 'enterfullscreen', 'exitfullscreen', 'captionsenabled', 'captionsdisabled', 'languagechange', 'controlshidden', 'controlsshown', 'ready', // YouTube - 'statechange', // Quality - 'qualitychange', // Ads + 'ended', 'progress', 'stalled', 'playing', 'waiting', 'canplay', 'canplaythrough', 'loadstart', 'loadeddata', 'loadedmetadata', 'timeupdate', 'volumechange', 'play', 'pause', 'error', 'seeking', 'seeked', 'emptied', 'ratechange', 'cuechange', + // Custom events + 'download', 'enterfullscreen', 'exitfullscreen', 'captionsenabled', 'captionsdisabled', 'languagechange', 'controlshidden', 'controlsshown', 'ready', + // YouTube + 'statechange', + // Quality + 'qualitychange', + // Ads 'adsloaded', 'adscontentpause', 'adscontentresume', 'adstarted', 'adsmidpoint', 'adscomplete', 'adsallcomplete', 'adsimpression', 'adsclick'], // Selectors // Change these to match your template if using custom HTML @@ -3878,9 +3695,9 @@ typeof navigator === "object" && (function (global, factory) { hover: 'plyr--hover', tooltip: 'plyr__tooltip', cues: 'plyr__cues', + marker: 'plyr__progress__marker', hidden: 'plyr__sr-only', hideControls: 'plyr--hide-controls', - isIos: 'plyr--is-ios', isTouch: 'plyr--is-touch', uiSupported: 'plyr--full-ui', noTransition: 'plyr--no-transition', @@ -3908,7 +3725,6 @@ typeof navigator === "object" && (function (global, factory) { supported: 'plyr--airplay-supported', active: 'plyr--airplay-active' }, - tabFocus: 'plyr__tab-focus', previewThumbnails: { // Tooltip thumbs thumbContainer: 'plyr__preview-thumb', @@ -3924,7 +3740,8 @@ typeof navigator === "object" && (function (global, factory) { attributes: { embed: { provider: 'data-plyr-provider', - id: 'data-plyr-embed-id' + id: 'data-plyr-embed-id', + hash: 'data-plyr-embed-hash' } }, // Advertisements plugin @@ -3946,32 +3763,47 @@ typeof navigator === "object" && (function (global, factory) { title: false, speed: true, transparent: false, + // Custom settings from Plyr + customControls: true, + referrerPolicy: null, + // https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/referrerPolicy // Whether the owner of the video has a Pro or Business account // (which allows us to properly hide controls without CSS hacks, etc) - premium: false, - // Custom settings from Plyr - referrerPolicy: null // https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/referrerPolicy - + premium: false }, // YouTube plugin youtube: { - noCookie: true, - // Whether to use an alternative version of YouTube without cookies rel: 0, // No related vids showinfo: 0, // Hide info iv_load_policy: 3, // Hide annotations - modestbranding: 1 // Hide logos as much as possible (they still show one in the corner when paused) - + modestbranding: 1, + // Hide logos as much as possible (they still show one in the corner when paused) + // Custom settings from Plyr + customControls: true, + noCookie: false // Whether to use an alternative version of YouTube without cookies + }, + // Media Metadata + mediaMetadata: { + title: '', + artist: '', + album: '', + artwork: [] + }, + // Markers + markers: { + enabled: false, + points: [] } }; // ========================================================================== // Plyr states // ========================================================================== - var pip = { + + const pip = { active: 'picture-in-picture', inactive: 'inline' }; @@ -3979,203 +3811,127 @@ typeof navigator === "object" && (function (global, factory) { // ========================================================================== // Plyr supported types and providers // ========================================================================== - var providers = { + + const providers = { html5: 'html5', youtube: 'youtube', vimeo: 'vimeo' }; - var types = { + const types = { audio: 'audio', video: 'video' }; + /** * Get provider by URL * @param {String} url */ - function getProviderByUrl(url) { // YouTube if (/^(https?:\/\/)?(www\.)?(youtube\.com|youtube-nocookie\.com|youtu\.?be)\/.+$/.test(url)) { return providers.youtube; - } // Vimeo - + } + // Vimeo if (/^https?:\/\/player.vimeo.com\/video\/\d{0,9}(?=\b|\/)/.test(url)) { return providers.vimeo; } - return null; } // ========================================================================== // Console wrapper // ========================================================================== - var noop = function noop() {}; - - var Console = /*#__PURE__*/function () { - function Console() { - var enabled = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; - - _classCallCheck(this, Console); + const noop = () => {}; + class Console { + constructor(enabled = false) { this.enabled = window.console && enabled; - if (this.enabled) { this.log('Debugging enabled'); } } + get log() { + // eslint-disable-next-line no-console + return this.enabled ? Function.prototype.bind.call(console.log, console) : noop; + } + get warn() { + // eslint-disable-next-line no-console + return this.enabled ? Function.prototype.bind.call(console.warn, console) : noop; + } + get error() { + // eslint-disable-next-line no-console + return this.enabled ? Function.prototype.bind.call(console.error, console) : noop; + } + } - _createClass(Console, [{ - key: "log", - get: function get() { - // eslint-disable-next-line no-console - return this.enabled ? Function.prototype.bind.call(console.log, console) : noop; - } - }, { - key: "warn", - get: function get() { - // eslint-disable-next-line no-console - return this.enabled ? Function.prototype.bind.call(console.warn, console) : noop; - } - }, { - key: "error", - get: function get() { - // eslint-disable-next-line no-console - return this.enabled ? Function.prototype.bind.call(console.error, console) : noop; - } - }]); - - return Console; - }(); - - var Fullscreen = /*#__PURE__*/function () { - function Fullscreen(player) { - var _this = this; - - _classCallCheck(this, Fullscreen); - - // Keep reference to parent - this.player = player; // Get prefix - - this.prefix = Fullscreen.prefix; - this.property = Fullscreen.property; // Scroll position - - this.scrollPosition = { - x: 0, - y: 0 - }; // Force the use of 'full window/browser' rather than fullscreen - - this.forceFallback = player.config.fullscreen.fallback === 'force'; // Get the fullscreen element - // Checks container is an ancestor, defaults to null - - this.player.elements.fullscreen = player.config.fullscreen.container && closest(this.player.elements.container, player.config.fullscreen.container); // Register event listeners - // Handle event (incase user presses escape etc) - - on.call(this.player, document, this.prefix === 'ms' ? 'MSFullscreenChange' : "".concat(this.prefix, "fullscreenchange"), function () { - // TODO: Filter for target?? - _this.onChange(); - }); // Fullscreen toggle on double click - - on.call(this.player, this.player.elements.container, 'dblclick', function (event) { - // Ignore double click in controls - if (is$1.element(_this.player.elements.controls) && _this.player.elements.controls.contains(event.target)) { - return; - } - - _this.toggle(); - }); // Tap focus when in fullscreen - - on.call(this, this.player.elements.container, 'keydown', function (event) { - return _this.trapFocus(event); - }); // Update the UI - - this.update(); - } // Determine if native supported - - - _createClass(Fullscreen, [{ - key: "onChange", - value: function onChange() { - if (!this.enabled) { - return; - } // Update toggle button - - - var button = this.player.elements.buttons.fullscreen; + class Fullscreen { + constructor(player) { + _defineProperty$1(this, "onChange", () => { + if (!this.supported) return; - if (is$1.element(button)) { + // Update toggle button + const button = this.player.elements.buttons.fullscreen; + if (is.element(button)) { button.pressed = this.active; - } // Trigger an event - - - triggerEvent.call(this.player, this.target, this.active ? 'enterfullscreen' : 'exitfullscreen', true); - } - }, { - key: "toggleFallback", - value: function toggleFallback() { - var toggle = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; + } + // Always trigger events on the plyr / media element (not a fullscreen container) and let them bubble up + const target = this.target === this.player.media ? this.target : this.player.elements.container; + // Trigger an event + triggerEvent.call(this.player, target, this.active ? 'enterfullscreen' : 'exitfullscreen', true); + }); + _defineProperty$1(this, "toggleFallback", (toggle = false) => { // Store or restore scroll position if (toggle) { this.scrollPosition = { - x: window.scrollX || 0, - y: window.scrollY || 0 + x: window.scrollX ?? 0, + y: window.scrollY ?? 0 }; } else { window.scrollTo(this.scrollPosition.x, this.scrollPosition.y); - } // Toggle scroll - + } - document.body.style.overflow = toggle ? 'hidden' : ''; // Toggle class hook + // Toggle scroll + document.body.style.overflow = toggle ? 'hidden' : ''; - toggleClass(this.target, this.player.config.classNames.fullscreen.fallback, toggle); // Force full viewport on iPhone X+ + // Toggle class hook + toggleClass(this.target, this.player.config.classNames.fullscreen.fallback, toggle); + // Force full viewport on iPhone X+ if (browser.isIos) { - var viewport = document.head.querySelector('meta[name="viewport"]'); - var property = 'viewport-fit=cover'; // Inject the viewport meta if required + let viewport = document.head.querySelector('meta[name="viewport"]'); + const property = 'viewport-fit=cover'; + // Inject the viewport meta if required if (!viewport) { viewport = document.createElement('meta'); viewport.setAttribute('name', 'viewport'); - } // Check if the property already exists - - - var hasProperty = is$1.string(viewport.content) && viewport.content.includes(property); + } + // Check if the property already exists + const hasProperty = is.string(viewport.content) && viewport.content.includes(property); if (toggle) { this.cleanupViewport = !hasProperty; - - if (!hasProperty) { - viewport.content += ",".concat(property); - } + if (!hasProperty) viewport.content += `,${property}`; } else if (this.cleanupViewport) { - viewport.content = viewport.content.split(',').filter(function (part) { - return part.trim() !== property; - }).join(','); + viewport.content = viewport.content.split(',').filter(part => part.trim() !== property).join(','); } - } // Toggle button and fire events - + } + // Toggle button and fire events this.onChange(); - } // Trap focus inside container - - }, { - key: "trapFocus", - value: function trapFocus(event) { - // Bail if iOS, not active, not the tab key - if (browser.isIos || !this.active || event.key !== 'Tab' || event.keyCode !== 9) { - return; - } // Get the current focused element - - - var focused = document.activeElement; - var focusable = getElements.call(this.player, 'a[href], button:not(:disabled), input:not(:disabled), [tabindex]'); - - var _focusable = _slicedToArray(focusable, 1), - first = _focusable[0]; - - var last = focusable[focusable.length - 1]; - + }); + // Trap focus inside container + _defineProperty$1(this, "trapFocus", event => { + // Bail if iOS/iPadOS, not active, not the tab key + if (browser.isIos || browser.isIPadOS || !this.active || event.key !== 'Tab') return; + + // Get the current focused element + const focused = document.activeElement; + const focusable = getElements.call(this.player, 'a[href], button:not(:disabled), input:not(:disabled), [tabindex]'); + const [first] = focusable; + const last = focusable[focusable.length - 1]; if (focused === last && !event.shiftKey) { // Move focus to first element that can be tabbed if Shift isn't used first.focus(); @@ -4185,186 +3941,205 @@ typeof navigator === "object" && (function (global, factory) { last.focus(); event.preventDefault(); } - } // Update UI - - }, { - key: "update", - value: function update() { - if (this.enabled) { - var mode; - - if (this.forceFallback) { - mode = 'Fallback (forced)'; - } else if (Fullscreen.native) { - mode = 'Native'; - } else { - mode = 'Fallback'; - } - - this.player.debug.log("".concat(mode, " fullscreen enabled")); + }); + // Update UI + _defineProperty$1(this, "update", () => { + if (this.supported) { + let mode; + if (this.forceFallback) mode = 'Fallback (forced)';else if (Fullscreen.nativeSupported) mode = 'Native';else mode = 'Fallback'; + this.player.debug.log(`${mode} fullscreen enabled`); } else { this.player.debug.log('Fullscreen not supported and fallback disabled'); - } // Add styling hook to show button - - - toggleClass(this.player.elements.container, this.player.config.classNames.fullscreen.enabled, this.enabled); - } // Make an element fullscreen - - }, { - key: "enter", - value: function enter() { - if (!this.enabled) { - return; - } // iOS native fullscreen doesn't need the request step + } + // Add styling hook to show button + toggleClass(this.player.elements.container, this.player.config.classNames.fullscreen.enabled, this.supported); + }); + // Make an element fullscreen + _defineProperty$1(this, "enter", () => { + if (!this.supported) return; + // iOS native fullscreen doesn't need the request step if (browser.isIos && this.player.config.fullscreen.iosNative) { - this.target.webkitEnterFullscreen(); - } else if (!Fullscreen.native || this.forceFallback) { + if (this.player.isVimeo) { + this.player.embed.requestFullscreen(); + } else { + this.target.webkitEnterFullscreen(); + } + } else if (!Fullscreen.nativeSupported || this.forceFallback) { this.toggleFallback(true); } else if (!this.prefix) { this.target.requestFullscreen({ navigationUI: 'hide' }); - } else if (!is$1.empty(this.prefix)) { - this.target["".concat(this.prefix, "Request").concat(this.property)](); + } else if (!is.empty(this.prefix)) { + this.target[`${this.prefix}Request${this.property}`](); } - } // Bail from fullscreen - - }, { - key: "exit", - value: function exit() { - if (!this.enabled) { - return; - } // iOS native fullscreen - + }); + // Bail from fullscreen + _defineProperty$1(this, "exit", () => { + if (!this.supported) return; + // iOS native fullscreen if (browser.isIos && this.player.config.fullscreen.iosNative) { - this.target.webkitExitFullscreen(); + if (this.player.isVimeo) { + this.player.embed.exitFullscreen(); + } else { + this.target.webkitEnterFullscreen(); + } silencePromise(this.player.play()); - } else if (!Fullscreen.native || this.forceFallback) { + } else if (!Fullscreen.nativeSupported || this.forceFallback) { this.toggleFallback(false); } else if (!this.prefix) { (document.cancelFullScreen || document.exitFullscreen).call(document); - } else if (!is$1.empty(this.prefix)) { - var action = this.prefix === 'moz' ? 'Cancel' : 'Exit'; - document["".concat(this.prefix).concat(action).concat(this.property)](); + } else if (!is.empty(this.prefix)) { + const action = this.prefix === 'moz' ? 'Cancel' : 'Exit'; + document[`${this.prefix}${action}${this.property}`](); } - } // Toggle state + }); + // Toggle state + _defineProperty$1(this, "toggle", () => { + if (!this.active) this.enter();else this.exit(); + }); + // Keep reference to parent + this.player = player; - }, { - key: "toggle", - value: function toggle() { - if (!this.active) { - this.enter(); - } else { - this.exit(); + // Get prefix + this.prefix = Fullscreen.prefix; + this.property = Fullscreen.property; + + // Scroll position + this.scrollPosition = { + x: 0, + y: 0 + }; + + // Force the use of 'full window/browser' rather than fullscreen + this.forceFallback = player.config.fullscreen.fallback === 'force'; + + // Get the fullscreen element + // Checks container is an ancestor, defaults to null + this.player.elements.fullscreen = player.config.fullscreen.container && closest$1(this.player.elements.container, player.config.fullscreen.container); + + // Register event listeners + // Handle event (incase user presses escape etc) + on.call(this.player, document, this.prefix === 'ms' ? 'MSFullscreenChange' : `${this.prefix}fullscreenchange`, () => { + // TODO: Filter for target?? + this.onChange(); + }); + + // Fullscreen toggle on double click + on.call(this.player, this.player.elements.container, 'dblclick', event => { + // Ignore double click in controls + if (is.element(this.player.elements.controls) && this.player.elements.controls.contains(event.target)) { + return; } - } - }, { - key: "usingNative", - // If we're actually using native - get: function get() { - return Fullscreen.native && !this.forceFallback; - } // Get the prefix for handlers + this.player.listeners.proxy(event, this.toggle, 'fullscreen'); + }); - }, { - key: "enabled", - // Determine if fullscreen is enabled - get: function get() { - return (Fullscreen.native || this.player.config.fullscreen.fallback) && this.player.config.fullscreen.enabled && this.player.supported.ui && this.player.isVideo; - } // Get active state + // Tap focus when in fullscreen + on.call(this, this.player.elements.container, 'keydown', event => this.trapFocus(event)); - }, { - key: "active", - get: function get() { - if (!this.enabled) { - return false; - } // Fallback using classname + // Update the UI + this.update(); + } + + // Determine if native supported + static get nativeSupported() { + return !!(document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled); + } + // If we're actually using native + get useNative() { + return Fullscreen.nativeSupported && !this.forceFallback; + } - if (!Fullscreen.native || this.forceFallback) { - return hasClass(this.target, this.player.config.classNames.fullscreen.fallback); + // Get the prefix for handlers + static get prefix() { + // No prefix + if (is.function(document.exitFullscreen)) return ''; + + // Check for fullscreen support by vendor prefix + let value = ''; + const prefixes = ['webkit', 'moz', 'ms']; + prefixes.some(pre => { + if (is.function(document[`${pre}ExitFullscreen`]) || is.function(document[`${pre}CancelFullScreen`])) { + value = pre; + return true; } + return false; + }); + return value; + } + static get property() { + return this.prefix === 'moz' ? 'FullScreen' : 'Fullscreen'; + } - var element = !this.prefix ? document.fullscreenElement : document["".concat(this.prefix).concat(this.property, "Element")]; - return element && element.shadowRoot ? element === this.target.getRootNode().host : element === this.target; - } // Get target element + // Determine if fullscreen is supported + get supported() { + return [ + // Fullscreen is enabled in config + this.player.config.fullscreen.enabled, + // Must be a video + this.player.isVideo, + // Either native is supported or fallback enabled + Fullscreen.nativeSupported || this.player.config.fullscreen.fallback, + // YouTube has no way to trigger fullscreen, so on devices with no native support, playsinline + // must be enabled and iosNative fullscreen must be disabled to offer the fullscreen fallback + !this.player.isYouTube || Fullscreen.nativeSupported || !browser.isIos || this.player.config.playsinline && !this.player.config.fullscreen.iosNative].every(Boolean); + } - }, { - key: "target", - get: function get() { - return browser.isIos && this.player.config.fullscreen.iosNative ? this.player.media : this.player.elements.fullscreen || this.player.elements.container; - } - }], [{ - key: "native", - get: function get() { - return !!(document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled); - } - }, { - key: "prefix", - get: function get() { - // No prefix - if (is$1.function(document.exitFullscreen)) { - return ''; - } // Check for fullscreen support by vendor prefix - - - var value = ''; - var prefixes = ['webkit', 'moz', 'ms']; - prefixes.some(function (pre) { - if (is$1.function(document["".concat(pre, "ExitFullscreen")]) || is$1.function(document["".concat(pre, "CancelFullScreen")])) { - value = pre; - return true; - } + // Get active state + get active() { + if (!this.supported) return false; - return false; - }); - return value; + // Fallback using classname + if (!Fullscreen.nativeSupported || this.forceFallback) { + return hasClass(this.target, this.player.config.classNames.fullscreen.fallback); } - }, { - key: "property", - get: function get() { - return this.prefix === 'moz' ? 'FullScreen' : 'Fullscreen'; - } - }]); + const element = !this.prefix ? this.target.getRootNode().fullscreenElement : this.target.getRootNode()[`${this.prefix}${this.property}Element`]; + return element && element.shadowRoot ? element === this.target.getRootNode().host : element === this.target; + } - return Fullscreen; - }(); + // Get target element + get target() { + return browser.isIos && this.player.config.fullscreen.iosNative ? this.player.media : this.player.elements.fullscreen ?? this.player.elements.container; + } + } // ========================================================================== // Load image avoiding xhr/fetch CORS issues // Server status can't be obtained this way unfortunately, so this uses "naturalWidth" to determine if the image has loaded // By default it checks if it is at least 1px, but you can add a second argument to change this // ========================================================================== - function loadImage(src) { - var minWidth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; - return new Promise(function (resolve, reject) { - var image = new Image(); - var handler = function handler() { + function loadImage(src, minWidth = 1) { + return new Promise((resolve, reject) => { + const image = new Image(); + const handler = () => { delete image.onload; delete image.onerror; (image.naturalWidth >= minWidth ? resolve : reject)(image); }; - Object.assign(image, { onload: handler, onerror: handler, - src: src + src }); }); } - var ui = { - addStyleHook: function addStyleHook() { + // ========================================================================== + // Plyr UI + // ========================================================================== + + const ui = { + addStyleHook() { toggleClass(this.elements.container, this.config.selectors.container.replace('.', ''), true); toggleClass(this.elements.container, this.config.classNames.uiSupported, this.supported.ui); }, // Toggle native HTML5 media controls - toggleNativeControls: function toggleNativeControls() { - var toggle = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; - + toggleNativeControls(toggle = false) { if (toggle && this.isHTML5) { this.media.setAttribute('controls', ''); } else { @@ -4372,835 +4147,671 @@ typeof navigator === "object" && (function (global, factory) { } }, // Setup the UI - build: function build() { - var _this = this; - + build() { // Re-attach media element listeners // TODO: Use event bubbling? - this.listeners.media(); // Don't setup interface if no support + this.listeners.media(); + // Don't setup interface if no support if (!this.supported.ui) { - this.debug.warn("Basic support only for ".concat(this.provider, " ").concat(this.type)); // Restore native controls + this.debug.warn(`Basic support only for ${this.provider} ${this.type}`); - ui.toggleNativeControls.call(this, true); // Bail + // Restore native controls + ui.toggleNativeControls.call(this, true); + // Bail return; - } // Inject custom controls if not present - + } - if (!is$1.element(this.elements.controls)) { + // Inject custom controls if not present + if (!is.element(this.elements.controls)) { // Inject custom controls - controls.inject.call(this); // Re-attach control listeners + controls.inject.call(this); + // Re-attach control listeners this.listeners.controls(); - } // Remove native controls - + } - ui.toggleNativeControls.call(this); // Setup captions for HTML5 + // Remove native controls + ui.toggleNativeControls.call(this); + // Setup captions for HTML5 if (this.isHTML5) { captions.setup.call(this); - } // Reset volume - + } - this.volume = null; // Reset mute state + // Reset volume + this.volume = null; - this.muted = null; // Reset loop state + // Reset mute state + this.muted = null; - this.loop = null; // Reset quality setting + // Reset loop state + this.loop = null; - this.quality = null; // Reset speed + // Reset quality setting + this.quality = null; - this.speed = null; // Reset volume display + // Reset speed + this.speed = null; - controls.updateVolume.call(this); // Reset time display + // Reset volume display + controls.updateVolume.call(this); - controls.timeUpdate.call(this); // Update the UI + // Reset time display + controls.timeUpdate.call(this); - ui.checkPlaying.call(this); // Check for picture-in-picture support + // Reset duration display + controls.durationUpdate.call(this); - toggleClass(this.elements.container, this.config.classNames.pip.supported, support.pip && this.isHTML5 && this.isVideo); // Check for airplay support + // Update the UI + ui.checkPlaying.call(this); - toggleClass(this.elements.container, this.config.classNames.airplay.supported, support.airplay && this.isHTML5); // Add iOS class + // Check for picture-in-picture support + toggleClass(this.elements.container, this.config.classNames.pip.supported, support.pip && this.isHTML5 && this.isVideo); - toggleClass(this.elements.container, this.config.classNames.isIos, browser.isIos); // Add touch class + // Check for airplay support + toggleClass(this.elements.container, this.config.classNames.airplay.supported, support.airplay && this.isHTML5); - toggleClass(this.elements.container, this.config.classNames.isTouch, this.touch); // Ready for API calls + // Add touch class + toggleClass(this.elements.container, this.config.classNames.isTouch, this.touch); - this.ready = true; // Ready event at end of execution stack + // Ready for API calls + this.ready = true; - setTimeout(function () { - triggerEvent.call(_this, _this.media, 'ready'); - }, 0); // Set the title + // Ready event at end of execution stack + setTimeout(() => { + triggerEvent.call(this, this.media, 'ready'); + }, 0); - ui.setTitle.call(this); // Assure the poster image is set, if the property was added before the element was created + // Set the title + ui.setTitle.call(this); + // Assure the poster image is set, if the property was added before the element was created if (this.poster) { - ui.setPoster.call(this, this.poster, false).catch(function () {}); - } // Manually set the duration if user has overridden it. - // The event listeners for it doesn't get called if preload is disabled (#701) - + ui.setPoster.call(this, this.poster, false).catch(() => {}); + } + // Manually set the duration if user has overridden it. + // The event listeners for it doesn't get called if preload is disabled (#701) if (this.config.duration) { controls.durationUpdate.call(this); } + + // Media metadata + if (this.config.mediaMetadata) { + controls.setMediaMetadata.call(this); + } }, // Setup aria attribute for play and iframe title - setTitle: function setTitle() { + setTitle() { // Find the current text - var label = i18n.get('play', this.config); // If there's a media title set, use that for the label - - if (is$1.string(this.config.title) && !is$1.empty(this.config.title)) { - label += ", ".concat(this.config.title); - } // If there's a play button, set label + let label = i18n.get('play', this.config); + // If there's a media title set, use that for the label + if (is.string(this.config.title) && !is.empty(this.config.title)) { + label += `, ${this.config.title}`; + } - Array.from(this.elements.buttons.play || []).forEach(function (button) { + // If there's a play button, set label + Array.from(this.elements.buttons.play || []).forEach(button => { button.setAttribute('aria-label', label); - }); // Set iframe title - // https://github.com/sampotts/plyr/issues/124 + }); + // Set iframe title + // https://github.com/sampotts/plyr/issues/124 if (this.isEmbed) { - var iframe = getElement.call(this, 'iframe'); - - if (!is$1.element(iframe)) { + const iframe = getElement.call(this, 'iframe'); + if (!is.element(iframe)) { return; - } // Default to media type - + } - var title = !is$1.empty(this.config.title) ? this.config.title : 'video'; - var format = i18n.get('frameTitle', this.config); + // Default to media type + const title = !is.empty(this.config.title) ? this.config.title : 'video'; + const format = i18n.get('frameTitle', this.config); iframe.setAttribute('title', format.replace('{title}', title)); } }, // Toggle poster - togglePoster: function togglePoster(enable) { + togglePoster(enable) { toggleClass(this.elements.container, this.config.classNames.posterEnabled, enable); }, // Set the poster image (async) // Used internally for the poster setter, with the passive option forced to false - setPoster: function setPoster(poster) { - var _this2 = this; - - var passive = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; - + setPoster(poster, passive = true) { // Don't override if call is passive if (passive && this.poster) { return Promise.reject(new Error('Poster already set')); - } // Set property synchronously to respect the call order + } + // Set property synchronously to respect the call order + this.media.setAttribute('data-poster', poster); - this.media.setAttribute('data-poster', poster); // Wait until ui is ready + // Show the poster + this.elements.poster.removeAttribute('hidden'); - return ready.call(this) // Load image - .then(function () { - return loadImage(poster); - }).catch(function (err) { + // Wait until ui is ready + return ready.call(this) + // Load image + .then(() => loadImage(poster)).catch(error => { // Hide poster on error unless it's been set by another call - if (poster === _this2.poster) { - ui.togglePoster.call(_this2, false); - } // Rethrow - - - throw err; - }).then(function () { + if (poster === this.poster) { + ui.togglePoster.call(this, false); + } + // Rethrow + throw error; + }).then(() => { // Prevent race conditions - if (poster !== _this2.poster) { + if (poster !== this.poster) { throw new Error('setPoster cancelled by later call to setPoster'); } - }).then(function () { - Object.assign(_this2.elements.poster.style, { - backgroundImage: "url('".concat(poster, "')"), + }).then(() => { + Object.assign(this.elements.poster.style, { + backgroundImage: `url('${poster}')`, // Reset backgroundSize as well (since it can be set to "cover" for padded thumbnails for youtube) backgroundSize: '' }); - ui.togglePoster.call(_this2, true); + ui.togglePoster.call(this, true); return poster; }); }, // Check playing state - checkPlaying: function checkPlaying(event) { - var _this3 = this; - + checkPlaying(event) { // Class hooks toggleClass(this.elements.container, this.config.classNames.playing, this.playing); toggleClass(this.elements.container, this.config.classNames.paused, this.paused); - toggleClass(this.elements.container, this.config.classNames.stopped, this.stopped); // Set state + toggleClass(this.elements.container, this.config.classNames.stopped, this.stopped); - Array.from(this.elements.buttons.play || []).forEach(function (target) { + // Set state + Array.from(this.elements.buttons.play || []).forEach(target => { Object.assign(target, { - pressed: _this3.playing + pressed: this.playing }); - target.setAttribute('aria-label', i18n.get(_this3.playing ? 'pause' : 'play', _this3.config)); - }); // Only update controls on non timeupdate events + target.setAttribute('aria-label', i18n.get(this.playing ? 'pause' : 'play', this.config)); + }); - if (is$1.event(event) && event.type === 'timeupdate') { + // Only update controls on non timeupdate events + if (is.event(event) && event.type === 'timeupdate') { return; - } // Toggle controls - + } + // Toggle controls ui.toggleControls.call(this); }, // Check if media is loading - checkLoading: function checkLoading(event) { - var _this4 = this; - - this.loading = ['stalled', 'waiting'].includes(event.type); // Clear timer + checkLoading(event) { + this.loading = ['stalled', 'waiting'].includes(event.type); - clearTimeout(this.timers.loading); // Timer to prevent flicker when seeking + // Clear timer + clearTimeout(this.timers.loading); - this.timers.loading = setTimeout(function () { + // Timer to prevent flicker when seeking + this.timers.loading = setTimeout(() => { // Update progress bar loading class state - toggleClass(_this4.elements.container, _this4.config.classNames.loading, _this4.loading); // Update controls visibility + toggleClass(this.elements.container, this.config.classNames.loading, this.loading); - ui.toggleControls.call(_this4); + // Update controls visibility + ui.toggleControls.call(this); }, this.loading ? 250 : 0); }, // Toggle controls based on state and `force` argument - toggleControls: function toggleControls(force) { - var controlsElement = this.elements.controls; - + toggleControls(force) { + const { + controls: controlsElement + } = this.elements; if (controlsElement && this.config.hideControls) { // Don't hide controls if a touch-device user recently seeked. (Must be limited to touch devices, or it occasionally prevents desktop controls from hiding.) - var recentTouchSeek = this.touch && this.lastSeekTime + 2000 > Date.now(); // Show controls if force, loading, paused, button interaction, or recent seek, otherwise hide + const recentTouchSeek = this.touch && this.lastSeekTime + 2000 > Date.now(); + // Show controls if force, loading, paused, button interaction, or recent seek, otherwise hide this.toggleControls(Boolean(force || this.loading || this.paused || controlsElement.pressed || controlsElement.hover || recentTouchSeek)); } }, // Migrate any custom properties from the media to the parent - migrateStyles: function migrateStyles() { - var _this5 = this; - + migrateStyles() { // Loop through values (as they are the keys when the object is spread 🤔) - Object.values(_objectSpread2({}, this.media.style)) // We're only fussed about Plyr specific properties - .filter(function (key) { - return !is$1.empty(key) && key.startsWith('--plyr'); - }).forEach(function (key) { + Object.values({ + ...this.media.style + }) + // We're only fussed about Plyr specific properties + .filter(key => !is.empty(key) && is.string(key) && key.startsWith('--plyr')).forEach(key => { // Set on the container - _this5.elements.container.style.setProperty(key, _this5.media.style.getPropertyValue(key)); // Clean up from media element - + this.elements.container.style.setProperty(key, this.media.style.getPropertyValue(key)); - _this5.media.style.removeProperty(key); - }); // Remove attribute if empty + // Clean up from media element + this.media.style.removeProperty(key); + }); - if (is$1.empty(this.media.style)) { + // Remove attribute if empty + if (is.empty(this.media.style)) { this.media.removeAttribute('style'); } } }; - var Listeners = /*#__PURE__*/function () { - function Listeners(player) { - _classCallCheck(this, Listeners); - - this.player = player; - this.lastKey = null; - this.focusTimer = null; - this.lastKeyDown = null; - this.handleKey = this.handleKey.bind(this); - this.toggleMenu = this.toggleMenu.bind(this); - this.setTabFocus = this.setTabFocus.bind(this); - this.firstTouch = this.firstTouch.bind(this); - } // Handle key presses - - - _createClass(Listeners, [{ - key: "handleKey", - value: function handleKey(event) { - var player = this.player; - var elements = player.elements; - var code = event.keyCode ? event.keyCode : event.which; - var pressed = event.type === 'keydown'; - var repeat = pressed && code === this.lastKey; // Bail if a modifier key is set - - if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) { - return; - } // If the event is bubbled from the media element - // Firefox doesn't get the keycode for whatever reason - - - if (!is$1.number(code)) { - return; - } // Seek by the number keys - - - var seekByKey = function seekByKey() { - // Divide the max duration into 10th's and times by the number value - player.currentTime = player.duration / 10 * (code - 48); - }; // Handle the key on keydown - // Reset on keyup - - - if (pressed) { - // Check focused element - // and if the focused element is not editable (e.g. text input) - // and any that accept key input http://webaim.org/techniques/keyboard/ - var focused = document.activeElement; - - if (is$1.element(focused)) { - var editable = player.config.selectors.editable; - var seek = elements.inputs.seek; - - if (focused !== seek && matches$1(focused, editable)) { - return; - } - - if (event.which === 32 && matches$1(focused, 'button, [role^="menuitem"]')) { - return; - } - } // Which keycodes should we prevent default - - - var preventDefault = [32, 37, 38, 39, 40, 48, 49, 50, 51, 52, 53, 54, 56, 57, 67, 70, 73, 75, 76, 77, 79]; // If the code is found prevent default (e.g. prevent scrolling for arrows) - - if (preventDefault.includes(code)) { - event.preventDefault(); - event.stopPropagation(); - } - - switch (code) { - case 48: - case 49: - case 50: - case 51: - case 52: - case 53: - case 54: - case 55: - case 56: - case 57: - // 0-9 - if (!repeat) { - seekByKey(); - } - - break; - - case 32: - case 75: - // Space and K key - if (!repeat) { - silencePromise(player.togglePlay()); - } - - break; - - case 38: - // Arrow up - player.increaseVolume(0.1); - break; - - case 40: - // Arrow down - player.decreaseVolume(0.1); - break; - - case 77: - // M key - if (!repeat) { - player.muted = !player.muted; - } - - break; - - case 39: - // Arrow forward - player.forward(); - break; - - case 37: - // Arrow back - player.rewind(); - break; - - case 70: - // F key - player.fullscreen.toggle(); - break; - - case 67: - // C key - if (!repeat) { - player.toggleCaptions(); - } - - break; - - case 76: - // L key - player.loop = !player.loop; - break; - } // Escape is handle natively when in full screen - // So we only need to worry about non native - - - if (code === 27 && !player.fullscreen.usingNative && player.fullscreen.active) { - player.fullscreen.toggle(); - } // Store last code for next cycle - - - this.lastKey = code; - } else { - this.lastKey = null; - } - } // Toggle menu - - }, { - key: "toggleMenu", - value: function toggleMenu(event) { - controls.toggleMenu.call(this.player, event); - } // Device is touch enabled - - }, { - key: "firstTouch", - value: function firstTouch() { - var player = this.player; - var elements = player.elements; - player.touch = true; // Add touch class - + class Listeners { + constructor(_player) { + // Device is touch enabled + _defineProperty$1(this, "firstTouch", () => { + const { + player + } = this; + const { + elements + } = player; + player.touch = true; + + // Add touch class toggleClass(elements.container, player.config.classNames.isTouch, true); - } - }, { - key: "setTabFocus", - value: function setTabFocus(event) { - var player = this.player; - var elements = player.elements; - clearTimeout(this.focusTimer); // Ignore any key other than tab - - if (event.type === 'keydown' && event.which !== 9) { - return; - } // Store reference to event timeStamp - - - if (event.type === 'keydown') { - this.lastKeyDown = event.timeStamp; - } // Remove current classes - - - var removeCurrent = function removeCurrent() { - var className = player.config.classNames.tabFocus; - var current = getElements.call(player, ".".concat(className)); - toggleClass(current, className, false); - }; // Determine if a key was pressed to trigger this event - - - var wasKeyDown = event.timeStamp - this.lastKeyDown <= 20; // Ignore focus events if a key was pressed prior - - if (event.type === 'focus' && !wasKeyDown) { - return; - } // Remove all current - - - removeCurrent(); // Delay the adding of classname until the focus has changed - // This event fires before the focusin event - - if (event.type !== 'focusout') { - this.focusTimer = setTimeout(function () { - var focused = document.activeElement; // Ignore if current focus element isn't inside the player - - if (!elements.container.contains(focused)) { - return; - } - - toggleClass(document.activeElement, player.config.classNames.tabFocus, true); - }, 10); - } - } // Global window & document listeners - - }, { - key: "global", - value: function global() { - var toggle = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; - var player = this.player; // Keyboard shortcuts + }); + // Global window & document listeners + _defineProperty$1(this, "global", (toggle = true) => { + const { + player + } = this; + // Keyboard shortcuts if (player.config.keyboard.global) { toggleListener.call(player, window, 'keydown keyup', this.handleKey, toggle, false); - } // Click anywhere closes menu - - - toggleListener.call(player, document.body, 'click', this.toggleMenu, toggle); // Detect touch by events - - once.call(player, document.body, 'touchstart', this.firstTouch); // Tab focus detection - - toggleListener.call(player, document.body, 'keydown focus blur focusout', this.setTabFocus, toggle, false, true); - } // Container listeners + } - }, { - key: "container", - value: function container() { - var player = this.player; - var config = player.config, - elements = player.elements, - timers = player.timers; // Keyboard shortcuts + // Click anywhere closes menu + toggleListener.call(player, document.body, 'click', this.toggleMenu, toggle); + // Detect touch by events + once.call(player, document.body, 'touchstart', this.firstTouch); + }); + // Container listeners + _defineProperty$1(this, "container", () => { + const { + player + } = this; + const { + config, + elements, + timers + } = player; + + // Keyboard shortcuts if (!config.keyboard.global && config.keyboard.focused) { on.call(player, elements.container, 'keydown keyup', this.handleKey, false); - } // Toggle controls on mouse events and entering fullscreen - + } - on.call(player, elements.container, 'mousemove mouseleave touchstart touchmove enterfullscreen exitfullscreen', function (event) { - var controlsElement = elements.controls; // Remove button states for fullscreen + // Toggle controls on mouse events and entering fullscreen + on.call(player, elements.container, 'mousemove mouseleave touchstart touchmove enterfullscreen exitfullscreen', event => { + const { + controls: controlsElement + } = elements; + // Remove button states for fullscreen if (controlsElement && event.type === 'enterfullscreen') { controlsElement.pressed = false; controlsElement.hover = false; - } // Show, then hide after a timeout unless another control event occurs - - - var show = ['touchstart', 'touchmove', 'mousemove'].includes(event.type); - var delay = 0; + } + // Show, then hide after a timeout unless another control event occurs + const show = ['touchstart', 'touchmove', 'mousemove'].includes(event.type); + let delay = 0; if (show) { - ui.toggleControls.call(player, true); // Use longer timeout for touch devices - + ui.toggleControls.call(player, true); + // Use longer timeout for touch devices delay = player.touch ? 3000 : 2000; - } // Clear timer - + } - clearTimeout(timers.controls); // Set new timer to prevent flicker when seeking + // Clear timer + clearTimeout(timers.controls); - timers.controls = setTimeout(function () { - return ui.toggleControls.call(player, false); - }, delay); - }); // Set a gutter for Vimeo + // Set new timer to prevent flicker when seeking + timers.controls = setTimeout(() => ui.toggleControls.call(player, false), delay); + }); - var setGutter = function setGutter(ratio, padding, toggle) { + // Set a gutter for Vimeo + const setGutter = () => { if (!player.isVimeo || player.config.vimeo.premium) { return; } - - var target = player.elements.wrapper.firstChild; - - var _ratio = _slicedToArray(ratio, 2), - y = _ratio[1]; - - var _getAspectRatio$call = getAspectRatio.call(player), - _getAspectRatio$call2 = _slicedToArray(_getAspectRatio$call, 2), - videoX = _getAspectRatio$call2[0], - videoY = _getAspectRatio$call2[1]; - - target.style.maxWidth = toggle ? "".concat(y / videoY * videoX, "px") : null; - target.style.margin = toggle ? '0 auto' : null; - }; // Resize on fullscreen change - - - var setPlayerSize = function setPlayerSize(measure) { - // If we don't need to measure the viewport - if (!measure) { - return setAspectRatio.call(player); + const target = elements.wrapper; + const { + active + } = player.fullscreen; + const [videoWidth, videoHeight] = getAspectRatio.call(player); + const useNativeAspectRatio = supportsCSS(`aspect-ratio: ${videoWidth} / ${videoHeight}`); + + // If not active, remove styles + if (!active) { + if (useNativeAspectRatio) { + target.style.width = null; + target.style.height = null; + } else { + target.style.maxWidth = null; + target.style.margin = null; + } + return; } - var rect = elements.container.getBoundingClientRect(); - var width = rect.width, - height = rect.height; - return setAspectRatio.call(player, "".concat(width, ":").concat(height)); + // Determine which dimension will overflow and constrain view + const [viewportWidth, viewportHeight] = getViewportSize(); + const overflow = viewportWidth / viewportHeight > videoWidth / videoHeight; + if (useNativeAspectRatio) { + target.style.width = overflow ? 'auto' : '100%'; + target.style.height = overflow ? '100%' : 'auto'; + } else { + target.style.maxWidth = overflow ? `${viewportHeight / videoHeight * videoWidth}px` : null; + target.style.margin = overflow ? '0 auto' : null; + } }; - var resized = function resized() { + // Handle resizing + const resized = () => { clearTimeout(timers.resized); - timers.resized = setTimeout(setPlayerSize, 50); + timers.resized = setTimeout(setGutter, 50); }; + on.call(player, elements.container, 'enterfullscreen exitfullscreen', event => { + const { + target + } = player.fullscreen; - on.call(player, elements.container, 'enterfullscreen exitfullscreen', function (event) { - var _player$fullscreen = player.fullscreen, - target = _player$fullscreen.target, - usingNative = _player$fullscreen.usingNative; // Ignore events not from target - + // Ignore events not from target if (target !== elements.container) { return; - } // If it's not an embed and no ratio specified - + } - if (!player.isEmbed && is$1.empty(player.config.ratio)) { + // If it's not an embed and no ratio specified + if (!player.isEmbed && is.empty(player.config.ratio)) { return; } - var isEnter = event.type === 'enterfullscreen'; // Set the player size when entering fullscreen to viewport size - - var _setPlayerSize = setPlayerSize(isEnter), - padding = _setPlayerSize.padding, - ratio = _setPlayerSize.ratio; // Set Vimeo gutter - - - setGutter(ratio, padding, isEnter); // If not using native browser fullscreen API, we need to check for resizes of viewport + // Set Vimeo gutter + setGutter(); - if (!usingNative) { - if (isEnter) { - on.call(player, window, 'resize', resized); - } else { - off.call(player, window, 'resize', resized); - } - } + // Watch for resizes + const method = event.type === 'enterfullscreen' ? on : off; + method.call(player, window, 'resize', resized); }); - } // Listen for media events - - }, { - key: "media", - value: function media() { - var _this = this; - - var player = this.player; - var elements = player.elements; // Time change on media - - on.call(player, player.media, 'timeupdate seeking seeked', function (event) { - return controls.timeUpdate.call(player, event); - }); // Display duration - - on.call(player, player.media, 'durationchange loadeddata loadedmetadata', function (event) { - return controls.durationUpdate.call(player, event); - }); // Handle the media finishing - - on.call(player, player.media, 'ended', function () { + }); + // Listen for media events + _defineProperty$1(this, "media", () => { + const { + player + } = this; + const { + elements + } = player; + + // Time change on media + on.call(player, player.media, 'timeupdate seeking seeked', event => controls.timeUpdate.call(player, event)); + + // Display duration + on.call(player, player.media, 'durationchange loadeddata loadedmetadata', event => controls.durationUpdate.call(player, event)); + + // Handle the media finishing + on.call(player, player.media, 'ended', () => { // Show poster on end if (player.isHTML5 && player.isVideo && player.config.resetOnEnd) { // Restart - player.restart(); // Call pause otherwise IE11 will start playing the video again + player.restart(); + // Call pause otherwise IE11 will start playing the video again player.pause(); } - }); // Check for buffer progress + }); - on.call(player, player.media, 'progress playing seeking seeked', function (event) { - return controls.updateProgress.call(player, event); - }); // Handle volume changes + // Check for buffer progress + on.call(player, player.media, 'progress playing seeking seeked', event => controls.updateProgress.call(player, event)); - on.call(player, player.media, 'volumechange', function (event) { - return controls.updateVolume.call(player, event); - }); // Handle play/pause + // Handle volume changes + on.call(player, player.media, 'volumechange', event => controls.updateVolume.call(player, event)); - on.call(player, player.media, 'playing play pause ended emptied timeupdate', function (event) { - return ui.checkPlaying.call(player, event); - }); // Loading state + // Handle play/pause + on.call(player, player.media, 'playing play pause ended emptied timeupdate', event => ui.checkPlaying.call(player, event)); - on.call(player, player.media, 'waiting canplay seeked playing', function (event) { - return ui.checkLoading.call(player, event); - }); // Click video + // Loading state + on.call(player, player.media, 'waiting canplay seeked playing', event => ui.checkLoading.call(player, event)); + // Click video if (player.supported.ui && player.config.clickToPlay && !player.isAudio) { // Re-fetch the wrapper - var wrapper = getElement.call(player, ".".concat(player.config.classNames.video)); // Bail if there's no wrapper (this should never happen) + const wrapper = getElement.call(player, `.${player.config.classNames.video}`); - if (!is$1.element(wrapper)) { + // Bail if there's no wrapper (this should never happen) + if (!is.element(wrapper)) { return; - } // On click play, pause or restart - + } - on.call(player, elements.container, 'click', function (event) { - var targets = [elements.container, wrapper]; // Ignore if click if not container or in video wrapper + // On click play, pause or restart + on.call(player, elements.container, 'click', event => { + const targets = [elements.container, wrapper]; + // Ignore if click if not container or in video wrapper if (!targets.includes(event.target) && !wrapper.contains(event.target)) { return; - } // Touch devices will just show controls (if hidden) - + } + // Touch devices will just show controls (if hidden) if (player.touch && player.config.hideControls) { return; } - if (player.ended) { - _this.proxy(event, player.restart, 'restart'); - - _this.proxy(event, function () { + this.proxy(event, player.restart, 'restart'); + this.proxy(event, () => { silencePromise(player.play()); }, 'play'); } else { - _this.proxy(event, function () { + this.proxy(event, () => { silencePromise(player.togglePlay()); }, 'play'); } }); - } // Disable right click - + } + // Disable right click if (player.supported.ui && player.config.disableContextMenu) { - on.call(player, elements.wrapper, 'contextmenu', function (event) { + on.call(player, elements.wrapper, 'contextmenu', event => { event.preventDefault(); }, false); - } // Volume change - + } - on.call(player, player.media, 'volumechange', function () { + // Volume change + on.call(player, player.media, 'volumechange', () => { // Save to storage player.storage.set({ volume: player.volume, muted: player.muted }); - }); // Speed change + }); - on.call(player, player.media, 'ratechange', function () { + // Speed change + on.call(player, player.media, 'ratechange', () => { // Update UI - controls.updateSetting.call(player, 'speed'); // Save to storage - + controls.updateSetting.call(player, 'speed'); + // Save to storage player.storage.set({ speed: player.speed }); - }); // Quality change + }); - on.call(player, player.media, 'qualitychange', function (event) { + // Quality change + on.call(player, player.media, 'qualitychange', event => { // Update UI controls.updateSetting.call(player, 'quality', null, event.detail.quality); - }); // Update download link when ready and if quality changes + }); - on.call(player, player.media, 'ready qualitychange', function () { + // Update download link when ready and if quality changes + on.call(player, player.media, 'ready qualitychange', () => { controls.setDownloadUrl.call(player); - }); // Proxy events to container - // Bubble up key events for Edge + }); - var proxyEvents = player.config.events.concat(['keyup', 'keydown']).join(' '); - on.call(player, player.media, proxyEvents, function (event) { - var _event$detail = event.detail, - detail = _event$detail === void 0 ? {} : _event$detail; // Get error details from media + // Proxy events to container + // Bubble up key events for Edge + const proxyEvents = player.config.events.concat(['keyup', 'keydown']).join(' '); + on.call(player, player.media, proxyEvents, event => { + let { + detail = {} + } = event; + // Get error details from media if (event.type === 'error') { detail = player.media.error; } - triggerEvent.call(player, elements.container, event.type, true, detail); }); - } // Run default and custom handlers - - }, { - key: "proxy", - value: function proxy(event, defaultHandler, customHandlerKey) { - var player = this.player; - var customHandler = player.config.listeners[customHandlerKey]; - var hasCustomHandler = is$1.function(customHandler); - var returned = true; // Execute custom handler - + }); + // Run default and custom handlers + _defineProperty$1(this, "proxy", (event, defaultHandler, customHandlerKey) => { + const { + player + } = this; + const customHandler = player.config.listeners[customHandlerKey]; + const hasCustomHandler = is.function(customHandler); + let returned = true; + + // Execute custom handler if (hasCustomHandler) { returned = customHandler.call(player, event); - } // Only call default handler if not prevented in custom handler - + } - if (returned !== false && is$1.function(defaultHandler)) { + // Only call default handler if not prevented in custom handler + if (returned !== false && is.function(defaultHandler)) { defaultHandler.call(player, event); } - } // Trigger custom and default handlers + }); + // Trigger custom and default handlers + _defineProperty$1(this, "bind", (element, type, defaultHandler, customHandlerKey, passive = true) => { + const { + player + } = this; + const customHandler = player.config.listeners[customHandlerKey]; + const hasCustomHandler = is.function(customHandler); + on.call(player, element, type, event => this.proxy(event, defaultHandler, customHandlerKey), passive && !hasCustomHandler); + }); + // Listen for control events + _defineProperty$1(this, "controls", () => { + const { + player + } = this; + const { + elements + } = player; + // IE doesn't support input event, so we fallback to change + const inputEvent = browser.isIE ? 'change' : 'input'; + + // Play/pause toggle + if (elements.buttons.play) { + Array.from(elements.buttons.play).forEach(button => { + this.bind(button, 'click', () => { + silencePromise(player.togglePlay()); + }, 'play'); + }); + } - }, { - key: "bind", - value: function bind(element, type, defaultHandler, customHandlerKey) { - var _this2 = this; - - var passive = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true; - var player = this.player; - var customHandler = player.config.listeners[customHandlerKey]; - var hasCustomHandler = is$1.function(customHandler); - on.call(player, element, type, function (event) { - return _this2.proxy(event, defaultHandler, customHandlerKey); - }, passive && !hasCustomHandler); - } // Listen for control events - - }, { - key: "controls", - value: function controls$1() { - var _this3 = this; - - var player = this.player; - var elements = player.elements; // IE doesn't support input event, so we fallback to change - - var inputEvent = browser.isIE ? 'change' : 'input'; // Play/pause toggle - - if (elements.buttons.play) { - Array.from(elements.buttons.play).forEach(function (button) { - _this3.bind(button, 'click', function () { - silencePromise(player.togglePlay()); - }, 'play'); - }); - } // Pause - - - this.bind(elements.buttons.restart, 'click', player.restart, 'restart'); // Rewind - - this.bind(elements.buttons.rewind, 'click', player.rewind, 'rewind'); // Rewind - - this.bind(elements.buttons.fastForward, 'click', player.forward, 'fastForward'); // Mute toggle - - this.bind(elements.buttons.mute, 'click', function () { + // Pause + this.bind(elements.buttons.restart, 'click', player.restart, 'restart'); + + // Rewind + this.bind(elements.buttons.rewind, 'click', () => { + // Record seek time so we can prevent hiding controls for a few seconds after rewind + player.lastSeekTime = Date.now(); + player.rewind(); + }, 'rewind'); + + // Rewind + this.bind(elements.buttons.fastForward, 'click', () => { + // Record seek time so we can prevent hiding controls for a few seconds after fast forward + player.lastSeekTime = Date.now(); + player.forward(); + }, 'fastForward'); + + // Mute toggle + this.bind(elements.buttons.mute, 'click', () => { player.muted = !player.muted; - }, 'mute'); // Captions toggle + }, 'mute'); - this.bind(elements.buttons.captions, 'click', function () { - return player.toggleCaptions(); - }); // Download + // Captions toggle + this.bind(elements.buttons.captions, 'click', () => player.toggleCaptions()); - this.bind(elements.buttons.download, 'click', function () { + // Download + this.bind(elements.buttons.download, 'click', () => { triggerEvent.call(player, player.media, 'download'); - }, 'download'); // Fullscreen toggle + }, 'download'); - this.bind(elements.buttons.fullscreen, 'click', function () { + // Fullscreen toggle + this.bind(elements.buttons.fullscreen, 'click', () => { player.fullscreen.toggle(); - }, 'fullscreen'); // Picture-in-Picture + }, 'fullscreen'); - this.bind(elements.buttons.pip, 'click', function () { + // Picture-in-Picture + this.bind(elements.buttons.pip, 'click', () => { player.pip = 'toggle'; - }, 'pip'); // Airplay + }, 'pip'); - this.bind(elements.buttons.airplay, 'click', player.airplay, 'airplay'); // Settings menu - click toggle + // Airplay + this.bind(elements.buttons.airplay, 'click', player.airplay, 'airplay'); - this.bind(elements.buttons.settings, 'click', function (event) { + // Settings menu - click toggle + this.bind(elements.buttons.settings, 'click', event => { // Prevent the document click listener closing the menu event.stopPropagation(); event.preventDefault(); - controls.toggleMenu.call(player, event); }, null, false); // Can't be passive as we're preventing default + // Settings menu - keyboard toggle // We have to bind to keyup otherwise Firefox triggers a click when a keydown event handler shifts focus // https://bugzilla.mozilla.org/show_bug.cgi?id=1220143 - - this.bind(elements.buttons.settings, 'keyup', function (event) { - var code = event.which; // We only care about space and return - - if (![13, 32].includes(code)) { + this.bind(elements.buttons.settings, 'keyup', event => { + if (![' ', 'Enter'].includes(event.key)) { return; - } // Because return triggers a click anyway, all we need to do is set focus - + } - if (code === 13) { + // Because return triggers a click anyway, all we need to do is set focus + if (event.key === 'Enter') { controls.focusFirstMenuItem.call(player, null, true); - return; - } // Prevent scroll - + } - event.preventDefault(); // Prevent playing video (Firefox) + // Prevent scroll + event.preventDefault(); - event.stopPropagation(); // Toggle menu + // Prevent playing video (Firefox) + event.stopPropagation(); + // Toggle menu controls.toggleMenu.call(player, event); }, null, false // Can't be passive as we're preventing default - ); // Escape closes menu + ); - this.bind(elements.settings.menu, 'keydown', function (event) { - if (event.which === 27) { + // Escape closes menu + this.bind(elements.settings.menu, 'keydown', event => { + if (event.key === 'Escape') { controls.toggleMenu.call(player, event); } - }); // Set range input alternative "value", which matches the tooltip time (#954) + }); - this.bind(elements.inputs.seek, 'mousedown mousemove', function (event) { - var rect = elements.progress.getBoundingClientRect(); - var percent = 100 / rect.width * (event.pageX - rect.left); + // Set range input alternative "value", which matches the tooltip time (#954) + this.bind(elements.inputs.seek, 'mousedown mousemove', event => { + const rect = elements.progress.getBoundingClientRect(); + const percent = 100 / rect.width * (event.pageX - rect.left); event.currentTarget.setAttribute('seek-value', percent); - }); // Pause while seeking - - this.bind(elements.inputs.seek, 'mousedown mouseup keydown keyup touchstart touchend', function (event) { - var seek = event.currentTarget; - var code = event.keyCode ? event.keyCode : event.which; - var attribute = 'play-on-seeked'; + }); - if (is$1.keyboardEvent(event) && code !== 39 && code !== 37) { + // Pause while seeking + this.bind(elements.inputs.seek, 'mousedown mouseup keydown keyup touchstart touchend', event => { + const seek = event.currentTarget; + const attribute = 'play-on-seeked'; + if (is.keyboardEvent(event) && !['ArrowLeft', 'ArrowRight'].includes(event.key)) { return; - } // Record seek time so we can prevent hiding controls for a few seconds after seek - - - player.lastSeekTime = Date.now(); // Was playing before? + } - var play = seek.hasAttribute(attribute); // Done seeking + // Record seek time so we can prevent hiding controls for a few seconds after seek + player.lastSeekTime = Date.now(); - var done = ['mouseup', 'touchend', 'keyup'].includes(event.type); // If we're done seeking and it was playing, resume playback + // Was playing before? + const play = seek.hasAttribute(attribute); + // Done seeking + const done = ['mouseup', 'touchend', 'keyup'].includes(event.type); + // If we're done seeking and it was playing, resume playback if (play && done) { seek.removeAttribute(attribute); silencePromise(player.play()); @@ -5208,165 +4819,310 @@ typeof navigator === "object" && (function (global, factory) { seek.setAttribute(attribute, ''); player.pause(); } - }); // Fix range inputs on iOS + }); + + // Fix range inputs on iOS // Super weird iOS bug where after you interact with an , // it takes over further interactions on the page. This is a hack - if (browser.isIos) { - var inputs = getElements.call(player, 'input[type="range"]'); - Array.from(inputs).forEach(function (input) { - return _this3.bind(input, inputEvent, function (event) { - return repaint(event.target); - }); - }); - } // Seek - - - this.bind(elements.inputs.seek, inputEvent, function (event) { - var seek = event.currentTarget; // If it exists, use seek-value instead of "value" for consistency with tooltip time (#954) - - var seekTo = seek.getAttribute('seek-value'); + const inputs = getElements.call(player, 'input[type="range"]'); + Array.from(inputs).forEach(input => this.bind(input, inputEvent, event => repaint(event.target))); + } - if (is$1.empty(seekTo)) { + // Seek + this.bind(elements.inputs.seek, inputEvent, event => { + const seek = event.currentTarget; + // If it exists, use seek-value instead of "value" for consistency with tooltip time (#954) + let seekTo = seek.getAttribute('seek-value'); + if (is.empty(seekTo)) { seekTo = seek.value; } - seek.removeAttribute('seek-value'); player.currentTime = seekTo / seek.max * player.duration; - }, 'seek'); // Seek tooltip - - this.bind(elements.progress, 'mouseenter mouseleave mousemove', function (event) { - return controls.updateSeekTooltip.call(player, event); - }); // Preview thumbnails plugin - // TODO: Really need to work on some sort of plug-in wide event bus or pub-sub for this + }, 'seek'); - this.bind(elements.progress, 'mousemove touchmove', function (event) { - var previewThumbnails = player.previewThumbnails; + // Seek tooltip + this.bind(elements.progress, 'mouseenter mouseleave mousemove', event => controls.updateSeekTooltip.call(player, event)); + // Preview thumbnails plugin + // TODO: Really need to work on some sort of plug-in wide event bus or pub-sub for this + this.bind(elements.progress, 'mousemove touchmove', event => { + const { + previewThumbnails + } = player; if (previewThumbnails && previewThumbnails.loaded) { previewThumbnails.startMove(event); } - }); // Hide thumbnail preview - on mouse click, mouse leave, and video play/seek. All four are required, e.g., for buffering - - this.bind(elements.progress, 'mouseleave touchend click', function () { - var previewThumbnails = player.previewThumbnails; + }); + // Hide thumbnail preview - on mouse click, mouse leave, and video play/seek. All four are required, e.g., for buffering + this.bind(elements.progress, 'mouseleave touchend click', () => { + const { + previewThumbnails + } = player; if (previewThumbnails && previewThumbnails.loaded) { previewThumbnails.endMove(false, true); } - }); // Show scrubbing preview - - this.bind(elements.progress, 'mousedown touchstart', function (event) { - var previewThumbnails = player.previewThumbnails; + }); + // Show scrubbing preview + this.bind(elements.progress, 'mousedown touchstart', event => { + const { + previewThumbnails + } = player; if (previewThumbnails && previewThumbnails.loaded) { previewThumbnails.startScrubbing(event); } }); - this.bind(elements.progress, 'mouseup touchend', function (event) { - var previewThumbnails = player.previewThumbnails; - + this.bind(elements.progress, 'mouseup touchend', event => { + const { + previewThumbnails + } = player; if (previewThumbnails && previewThumbnails.loaded) { previewThumbnails.endScrubbing(event); } - }); // Polyfill for lower fill in for webkit + }); - if (browser.isWebkit) { - Array.from(getElements.call(player, 'input[type="range"]')).forEach(function (element) { - _this3.bind(element, 'input', function (event) { - return controls.updateRangeFill.call(player, event.target); - }); + // Polyfill for lower fill in for webkit + if (browser.isWebKit) { + Array.from(getElements.call(player, 'input[type="range"]')).forEach(element => { + this.bind(element, 'input', event => controls.updateRangeFill.call(player, event.target)); }); - } // Current time invert - // Only if one time element is used for both currentTime and duration - + } - if (player.config.toggleInvert && !is$1.element(elements.display.duration)) { - this.bind(elements.display.currentTime, 'click', function () { + // Current time invert + // Only if one time element is used for both currentTime and duration + if (player.config.toggleInvert && !is.element(elements.display.duration)) { + this.bind(elements.display.currentTime, 'click', () => { // Do nothing if we're at the start if (player.currentTime === 0) { return; } - player.config.invertTime = !player.config.invertTime; - controls.timeUpdate.call(player); }); - } // Volume - + } - this.bind(elements.inputs.volume, inputEvent, function (event) { + // Volume + this.bind(elements.inputs.volume, inputEvent, event => { player.volume = event.target.value; - }, 'volume'); // Update controls.hover state (used for ui.toggleControls to avoid hiding when interacting) + }, 'volume'); - this.bind(elements.controls, 'mouseenter mouseleave', function (event) { + // Update controls.hover state (used for ui.toggleControls to avoid hiding when interacting) + this.bind(elements.controls, 'mouseenter mouseleave', event => { elements.controls.hover = !player.touch && event.type === 'mouseenter'; - }); // Also update controls.hover state for any non-player children of fullscreen element (as above) + }); + // Also update controls.hover state for any non-player children of fullscreen element (as above) if (elements.fullscreen) { - Array.from(elements.fullscreen.children).filter(function (c) { - return !c.contains(elements.container); - }).forEach(function (child) { - _this3.bind(child, 'mouseenter mouseleave', function (event) { - elements.controls.hover = !player.touch && event.type === 'mouseenter'; + Array.from(elements.fullscreen.children).filter(c => !c.contains(elements.container)).forEach(child => { + this.bind(child, 'mouseenter mouseleave', event => { + if (elements.controls) { + elements.controls.hover = !player.touch && event.type === 'mouseenter'; + } }); }); - } // Update controls.pressed state (used for ui.toggleControls to avoid hiding when interacting) - + } - this.bind(elements.controls, 'mousedown mouseup touchstart touchend touchcancel', function (event) { + // Update controls.pressed state (used for ui.toggleControls to avoid hiding when interacting) + this.bind(elements.controls, 'mousedown mouseup touchstart touchend touchcancel', event => { elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type); - }); // Show controls when they receive focus (e.g., when using keyboard tab key) + }); - this.bind(elements.controls, 'focusin', function () { - var config = player.config, - timers = player.timers; // Skip transition to prevent focus from scrolling the parent element + // Show controls when they receive focus (e.g., when using keyboard tab key) + this.bind(elements.controls, 'focusin', () => { + const { + config, + timers + } = player; - toggleClass(elements.controls, config.classNames.noTransition, true); // Toggle + // Skip transition to prevent focus from scrolling the parent element + toggleClass(elements.controls, config.classNames.noTransition, true); - ui.toggleControls.call(player, true); // Restore transition + // Toggle + ui.toggleControls.call(player, true); - setTimeout(function () { + // Restore transition + setTimeout(() => { toggleClass(elements.controls, config.classNames.noTransition, false); - }, 0); // Delay a little more for mouse users + }, 0); - var delay = _this3.touch ? 3000 : 4000; // Clear timer + // Delay a little more for mouse users + const delay = this.touch ? 3000 : 4000; - clearTimeout(timers.controls); // Hide again after delay + // Clear timer + clearTimeout(timers.controls); - timers.controls = setTimeout(function () { - return ui.toggleControls.call(player, false); - }, delay); - }); // Mouse wheel for volume + // Hide again after delay + timers.controls = setTimeout(() => ui.toggleControls.call(player, false), delay); + }); - this.bind(elements.inputs.volume, 'wheel', function (event) { - // Detect "natural" scroll - suppored on OS X Safari only + // Mouse wheel for volume + this.bind(elements.inputs.volume, 'wheel', event => { + // Detect "natural" scroll - supported on OS X Safari only // Other browsers on OS X will be inverted until support improves - var inverted = event.webkitDirectionInvertedFromDevice; // Get delta from event. Invert if `inverted` is true + const inverted = event.webkitDirectionInvertedFromDevice; + // Get delta from event. Invert if `inverted` is true + const [x, y] = [event.deltaX, -event.deltaY].map(value => inverted ? -value : value); + // Using the biggest delta, normalize to 1 or -1 (or 0 if no delta) + const direction = Math.sign(Math.abs(x) > Math.abs(y) ? x : y); + + // Change the volume by 2% + player.increaseVolume(direction / 50); + + // Don't break page scrolling at max and min + const { + volume + } = player.media; + if (direction === 1 && volume < 1 || direction === -1 && volume > 0) { + event.preventDefault(); + } + }, 'volume', false); + }); + this.player = _player; + this.lastKey = null; + this.focusTimer = null; + this.lastKeyDown = null; + this.handleKey = this.handleKey.bind(this); + this.toggleMenu = this.toggleMenu.bind(this); + this.firstTouch = this.firstTouch.bind(this); + } - var _map = [event.deltaX, -event.deltaY].map(function (value) { - return inverted ? -value : value; - }), - _map2 = _slicedToArray(_map, 2), - x = _map2[0], - y = _map2[1]; // Using the biggest delta, normalize to 1 or -1 (or 0 if no delta) + // Handle key presses + handleKey(event) { + const { + player + } = this; + const { + elements + } = player; + const { + key, + type, + altKey, + ctrlKey, + metaKey, + shiftKey + } = event; + const pressed = type === 'keydown'; + const repeat = pressed && key === this.lastKey; + + // Bail if a modifier key is set + if (altKey || ctrlKey || metaKey || shiftKey) { + return; + } + // If the event is bubbled from the media element + // Firefox doesn't get the key for whatever reason + if (!key) { + return; + } - var direction = Math.sign(Math.abs(x) > Math.abs(y) ? x : y); // Change the volume by 2% + // Seek by increment + const seekByIncrement = increment => { + // Divide the max duration into 10th's and times by the number value + player.currentTime = player.duration / 10 * increment; + }; - player.increaseVolume(direction / 50); // Don't break page scrolling at max and min + // Handle the key on keydown + // Reset on keyup + if (pressed) { + // Check focused element + // and if the focused element is not editable (e.g. text input) + // and any that accept key input http://webaim.org/techniques/keyboard/ + const focused = document.activeElement; + if (is.element(focused)) { + const { + editable + } = player.config.selectors; + const { + seek + } = elements.inputs; + if (focused !== seek && matches(focused, editable)) { + return; + } + if (event.key === ' ' && matches(focused, 'button, [role^="menuitem"]')) { + return; + } + } - var volume = player.media.volume; + // Which keys should we prevent default + const preventDefault = [' ', 'ArrowLeft', 'ArrowUp', 'ArrowRight', 'ArrowDown', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'c', 'f', 'k', 'l', 'm']; - if (direction === 1 && volume < 1 || direction === -1 && volume > 0) { - event.preventDefault(); - } - }, 'volume', false); + // If the key is found prevent default (e.g. prevent scrolling for arrows) + if (preventDefault.includes(key)) { + event.preventDefault(); + event.stopPropagation(); + } + switch (key) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (!repeat) { + seekByIncrement(parseInt(key, 10)); + } + break; + case ' ': + case 'k': + if (!repeat) { + silencePromise(player.togglePlay()); + } + break; + case 'ArrowUp': + player.increaseVolume(0.1); + break; + case 'ArrowDown': + player.decreaseVolume(0.1); + break; + case 'm': + if (!repeat) { + player.muted = !player.muted; + } + break; + case 'ArrowRight': + player.forward(); + break; + case 'ArrowLeft': + player.rewind(); + break; + case 'f': + player.fullscreen.toggle(); + break; + case 'c': + if (!repeat) { + player.toggleCaptions(); + } + break; + case 'l': + player.loop = !player.loop; + break; + } + + // Escape is handle natively when in full screen + // So we only need to worry about non native + if (key === 'Escape' && !player.fullscreen.usingNative && player.fullscreen.active) { + player.fullscreen.toggle(); + } + + // Store last key for next cycle + this.lastKey = key; + } else { + this.lastKey = null; } - }]); + } - return Listeners; - }(); + // Toggle menu + toggleMenu(event) { + controls.toggleMenu.call(this.player, event); + } + } var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; @@ -5384,112 +5140,115 @@ typeof navigator === "object" && (function (global, factory) { * Global dependencies. * @global {Object} document - DOM */ - var devnull = function devnull() {}, - bundleIdCache = {}, - bundleResultCache = {}, - bundleCallbackQueue = {}; + + var devnull = function () {}, + bundleIdCache = {}, + bundleResultCache = {}, + bundleCallbackQueue = {}; + /** * Subscribe to bundle load event. * @param {string[]} bundleIds - Bundle ids * @param {Function} callbackFn - The callback function */ - - function subscribe(bundleIds, callbackFn) { // listify bundleIds = bundleIds.push ? bundleIds : [bundleIds]; var depsNotFound = [], - i = bundleIds.length, - numWaiting = i, - fn, - bundleId, - r, - q; // define callback function - - fn = function fn(bundleId, pathsNotFound) { + i = bundleIds.length, + numWaiting = i, + fn, + bundleId, + r, + q; + + // define callback function + fn = function (bundleId, pathsNotFound) { if (pathsNotFound.length) depsNotFound.push(bundleId); numWaiting--; if (!numWaiting) callbackFn(depsNotFound); - }; // register callback - + }; + // register callback while (i--) { - bundleId = bundleIds[i]; // execute callback if in result cache + bundleId = bundleIds[i]; + // execute callback if in result cache r = bundleResultCache[bundleId]; - if (r) { fn(bundleId, r); continue; - } // add to callback queue - + } + // add to callback queue q = bundleCallbackQueue[bundleId] = bundleCallbackQueue[bundleId] || []; q.push(fn); } } + /** * Publish bundle load event. * @param {string} bundleId - Bundle id * @param {string[]} pathsNotFound - List of files not found */ - - function publish(bundleId, pathsNotFound) { // exit if id isn't defined if (!bundleId) return; - var q = bundleCallbackQueue[bundleId]; // cache result + var q = bundleCallbackQueue[bundleId]; - bundleResultCache[bundleId] = pathsNotFound; // exit if queue is empty + // cache result + bundleResultCache[bundleId] = pathsNotFound; - if (!q) return; // empty callback queue + // exit if queue is empty + if (!q) return; + // empty callback queue while (q.length) { q[0](bundleId, pathsNotFound); q.splice(0, 1); } } + /** * Execute callbacks. * @param {Object or Function} args - The callback args * @param {string[]} depsNotFound - List of dependencies not found */ - - function executeCallbacks(args, depsNotFound) { // accept function as argument if (args.call) args = { success: args - }; // success and error callbacks + }; + // success and error callbacks if (depsNotFound.length) (args.error || devnull)(depsNotFound);else (args.success || devnull)(args); } + /** * Load individual file. * @param {string} path - The file path * @param {Function} callbackFn - The callback function */ - - function loadFile(path, callbackFn, args, numTries) { var doc = document, - async = args.async, - maxTries = (args.numRetries || 0) + 1, - beforeCallbackFn = args.before || devnull, - pathname = path.replace(/[\?|#].*$/, ''), - pathStripped = path.replace(/^(css|img)!/, ''), - isLegacyIECss, - e; + async = args.async, + maxTries = (args.numRetries || 0) + 1, + beforeCallbackFn = args.before || devnull, + pathname = path.replace(/[\?|#].*$/, ''), + pathStripped = path.replace(/^(css|img)!/, ''), + isLegacyIECss, + e; numTries = numTries || 0; - if (/(^css!|\.css$)/.test(pathname)) { // css e = doc.createElement('link'); e.rel = 'stylesheet'; - e.href = pathStripped; // tag IE9+ + e.href = pathStripped; - isLegacyIECss = 'hideFocus' in e; // use preload in IE Edge (to detect load errors) + // tag IE9+ + isLegacyIECss = 'hideFocus' in e; + // use preload in IE Edge (to detect load errors) if (isLegacyIECss && e.relList) { isLegacyIECss = 0; e.rel = 'preload'; @@ -5505,11 +5264,11 @@ typeof navigator === "object" && (function (global, factory) { e.src = path; e.async = async === undefined ? true : async; } - e.onload = e.onerror = e.onbeforeload = function (ev) { - var result = ev.type[0]; // treat empty stylesheets as failures to get around lack of onerror - // support in IE9-11 + var result = ev.type[0]; + // treat empty stylesheets as failures to get around lack of onerror + // support in IE9-11 if (isLegacyIECss) { try { if (!e.sheet.cssText.length) result = 'e'; @@ -5518,62 +5277,62 @@ typeof navigator === "object" && (function (global, factory) { // `cssText` (unless error is Code:18 SecurityError) if (x.code != 18) result = 'e'; } - } // handle retries in case of load failure - + } + // handle retries in case of load failure if (result == 'e') { // increment counter - numTries += 1; // exit function and try again + numTries += 1; + // exit function and try again if (numTries < maxTries) { return loadFile(path, callbackFn, args, numTries); } } else if (e.rel == 'preload' && e.as == 'style') { // activate preloaded stylesheets return e.rel = 'stylesheet'; // jshint ignore:line - } // execute callback - + } + // execute callback callbackFn(path, result, ev.defaultPrevented); - }; // add to document (unless callback returns `false`) - + }; + // add to document (unless callback returns `false`) if (beforeCallbackFn(path, e) !== false) doc.head.appendChild(e); } + /** * Load multiple files. * @param {string[]} paths - The file paths * @param {Function} callbackFn - The callback function */ - - function loadFiles(paths, callbackFn, args) { // listify paths paths = paths.push ? paths : [paths]; var numWaiting = paths.length, - x = numWaiting, - pathsNotFound = [], - fn, - i; // define callback function + x = numWaiting, + pathsNotFound = [], + fn, + i; - fn = function fn(path, result, defaultPrevented) { + // define callback function + fn = function (path, result, defaultPrevented) { // handle error - if (result == 'e') pathsNotFound.push(path); // handle beforeload event. If defaultPrevented then that means the load - // will be blocked (ex. Ghostery/ABP on Safari) + if (result == 'e') pathsNotFound.push(path); + // handle beforeload event. If defaultPrevented then that means the load + // will be blocked (ex. Ghostery/ABP on Safari) if (result == 'b') { if (defaultPrevented) pathsNotFound.push(path);else return; } - numWaiting--; if (!numWaiting) callbackFn(pathsNotFound); - }; // load scripts - + }; - for (i = 0; i < x; i++) { - loadFile(paths[i], fn, args); - } + // load scripts + for (i = 0; i < x; i++) loadFile(paths[i], fn, args); } + /** * Initiate script load and register bundle. * @param {(string|string[])} paths - The file paths @@ -5583,15 +5342,16 @@ typeof navigator === "object" && (function (global, factory) { * @param {(Function|Object)} [arg2] - The (1) success callback or (2) object * literal with success/error arguments, numRetries, etc. */ - - function loadjs(paths, arg1, arg2) { - var bundleId, args; // bundleId (if string) + var bundleId, args; - if (arg1 && arg1.trim) bundleId = arg1; // args (default is {}) + // bundleId (if string) + if (arg1 && arg1.trim) bundleId = arg1; - args = (bundleId ? arg2 : arg1) || {}; // throw error if bundle is already defined + // args (default is {}) + args = (bundleId ? arg2 : arg1) || {}; + // throw error if bundle is already defined if (bundleId) { if (bundleId in bundleIdCache) { throw "LoadJS"; @@ -5599,33 +5359,31 @@ typeof navigator === "object" && (function (global, factory) { bundleIdCache[bundleId] = true; } } - function loadFn(resolve, reject) { loadFiles(paths, function (pathsNotFound) { // execute callbacks - executeCallbacks(args, pathsNotFound); // resolve Promise + executeCallbacks(args, pathsNotFound); + // resolve Promise if (resolve) { executeCallbacks({ success: resolve, error: reject }, pathsNotFound); - } // publish bundle load event - + } + // publish bundle load event publish(bundleId, pathsNotFound); }, args); } - if (args.returnPromise) return new Promise(loadFn);else loadFn(); } + /** * Execute callbacks when dependencies have been satisfied. * @param {(string|string[])} deps - List of bundle ids * @param {Object} args - success/error arguments */ - - loadjs.ready = function ready(deps, args) { // subscribe to bundle load event subscribe(deps, function (depsNotFound) { @@ -5634,43 +5392,43 @@ typeof navigator === "object" && (function (global, factory) { }); return loadjs; }; + /** * Manually satisfy bundle dependencies. * @param {string} bundleId - The bundle id */ - - loadjs.done = function done(bundleId) { publish(bundleId, []); }; + /** * Reset loadjs dependencies statuses */ - - loadjs.reset = function reset() { bundleIdCache = {}; bundleResultCache = {}; bundleCallbackQueue = {}; }; + /** * Determine if bundle has already been defined * @param String} bundleId - The bundle id */ - - loadjs.isDefined = function isDefined(bundleId) { return bundleId in bundleIdCache; - }; // export - + }; + // export return loadjs; }); }); // ========================================================================== + // Load an external script + // ========================================================================== + function loadScript(url) { - return new Promise(function (resolve, reject) { + return new Promise((resolve, reject) => { loadjs_umd(url, { success: resolve, error: reject @@ -5678,45 +5436,65 @@ typeof navigator === "object" && (function (global, factory) { }); } - function parseId(url) { - if (is$1.empty(url)) { + // ========================================================================== + // Vimeo plugin + // ========================================================================== + + + // Parse Vimeo ID from URL + function parseId$1(url) { + if (is.empty(url)) { return null; } - - if (is$1.number(Number(url))) { + if (is.number(Number(url))) { return url; } - - var regex = /^.*(vimeo.com\/|video\/)(\d+).*/; + const regex = /^.*(vimeo.com\/|video\/)(\d+).*/; return url.match(regex) ? RegExp.$2 : url; - } // Set playback state and trigger change (only on actual change) + } + // Try to extract a hash for private videos from the URL + function parseHash(url) { + /* This regex matches a hexadecimal hash if given in any of these forms: + * - [https://player.]vimeo.com/video/{id}/{hash}[?params] + * - [https://player.]vimeo.com/video/{id}?h={hash}[¶ms] + * - [https://player.]vimeo.com/video/{id}?[params]&h={hash} + * - video/{id}/{hash} + * If matched, the hash is available in capture group 4 + */ + const regex = /^.*(vimeo.com\/|video\/)(\d+)(\?.*&*h=|\/)+([\d,a-f]+)/; + const found = url.match(regex); + return found && found.length === 5 ? found[4] : null; + } - function assurePlaybackState(play) { + // Set playback state and trigger change (only on actual change) + function assurePlaybackState$1(play) { if (play && !this.embed.hasPlayed) { this.embed.hasPlayed = true; } - if (this.media.paused === play) { this.media.paused = !play; triggerEvent.call(this, this.media, play ? 'play' : 'pause'); } } + const vimeo = { + setup() { + const player = this; - var vimeo = { - setup: function setup() { - var player = this; // Add embed class for responsive - - toggleClass(player.elements.wrapper, player.config.classNames.embed, true); // Set speed options from config + // Add embed class for responsive + toggleClass(player.elements.wrapper, player.config.classNames.embed, true); - player.options.speed = player.config.speed.options; // Set intial ratio + // Set speed options from config + player.options.speed = player.config.speed.options; - setAspectRatio.call(player); // Load the SDK if not already + // Set intial ratio + setAspectRatio.call(player); - if (!is$1.object(window.Vimeo)) { - loadScript(player.config.urls.vimeo.sdk).then(function () { + // Load the SDK if not already + if (!is.object(window.Vimeo)) { + loadScript(player.config.urls.vimeo.sdk).then(() => { vimeo.ready.call(player); - }).catch(function (error) { + }).catch(error => { player.debug.warn('Vimeo SDK (player.js) failed to load', error); }); } else { @@ -5724,479 +5502,497 @@ typeof navigator === "object" && (function (global, factory) { } }, // API Ready - ready: function ready() { - var _this = this; - - var player = this; - var config = player.config.vimeo; - - var premium = config.premium, - referrerPolicy = config.referrerPolicy, - frameParams = _objectWithoutProperties(config, ["premium", "referrerPolicy"]); // If the owner has a pro or premium account then we can hide controls etc - + ready() { + const player = this; + const config = player.config.vimeo; + const { + premium, + referrerPolicy, + ...frameParams + } = config; + // Get the source URL or ID + let source = player.media.getAttribute('src'); + let hash = ''; + // Get from
if needed + if (is.empty(source)) { + source = player.media.getAttribute(player.config.attributes.embed.id); + // hash can also be set as attribute on the
+ hash = player.media.getAttribute(player.config.attributes.embed.hash); + } else { + hash = parseHash(source); + } + const hashParam = hash ? { + h: hash + } : {}; + // If the owner has a pro or premium account then we can hide controls etc if (premium) { Object.assign(frameParams, { controls: false, sidedock: false }); - } // Get Vimeo params for the iframe - + } - var params = buildUrlParams(_objectSpread2({ + // Get Vimeo params for the iframe + const params = buildUrlParams({ loop: player.config.loop.active, autoplay: player.autoplay, muted: player.muted, gesture: 'media', - playsinline: !this.config.fullscreen.iosNative - }, frameParams)); // Get the source URL or ID - - var source = player.media.getAttribute('src'); // Get from
if needed - - if (is$1.empty(source)) { - source = player.media.getAttribute(player.config.attributes.embed.id); - } - - var id = parseId(source); // Build an iframe - - var iframe = createElement('iframe'); - var src = format(player.config.urls.vimeo.iframe, id, params); + playsinline: player.config.playsinline, + // hash has to be added to iframe-URL + ...hashParam, + ...frameParams + }); + const id = parseId$1(source); + // Build an iframe + const iframe = createElement('iframe'); + const src = format(player.config.urls.vimeo.iframe, id, params); iframe.setAttribute('src', src); iframe.setAttribute('allowfullscreen', ''); - iframe.setAttribute('allow', 'autoplay,fullscreen,picture-in-picture'); // Set the referrer policy if required + iframe.setAttribute('allow', ['autoplay', 'fullscreen', 'picture-in-picture', 'encrypted-media', 'accelerometer', 'gyroscope'].join('; ')); - if (!is$1.empty(referrerPolicy)) { + // Set the referrer policy if required + if (!is.empty(referrerPolicy)) { iframe.setAttribute('referrerPolicy', referrerPolicy); - } // Inject the package - - - var poster = player.poster; + } - if (premium) { - iframe.setAttribute('data-poster', poster); + // Inject the package + if (premium || !config.customControls) { + iframe.setAttribute('data-poster', player.poster); player.media = replaceElement(iframe, player.media); } else { - var wrapper = createElement('div', { + const wrapper = createElement('div', { class: player.config.classNames.embedContainer, - 'data-poster': poster + 'data-poster': player.poster }); wrapper.appendChild(iframe); player.media = replaceElement(wrapper, player.media); - } // Get poster image - - - fetch(format(player.config.urls.vimeo.api, id), 'json').then(function (response) { - if (is$1.empty(response)) { - return; - } // Get the URL for thumbnail - + } - var url = new URL(response[0].thumbnail_large); // Get original image + // Get poster image + if (!config.customControls) { + fetch(format(player.config.urls.vimeo.api, src)).then(response => { + if (is.empty(response) || !response.thumbnail_url) { + return; + } - url.pathname = "".concat(url.pathname.split('_')[0], ".jpg"); // Set and show poster + // Set and show poster + ui.setPoster.call(player, response.thumbnail_url).catch(() => {}); + }); + } - ui.setPoster.call(player, url.href).catch(function () {}); - }); // Setup instance + // Setup instance // https://github.com/vimeo/player.js - player.embed = new window.Vimeo.Player(iframe, { autopause: player.config.autopause, muted: player.muted }); player.media.paused = true; - player.media.currentTime = 0; // Disable native text track rendering + player.media.currentTime = 0; + // Disable native text track rendering if (player.supported.ui) { player.embed.disableTextTrack(); - } // Create a faux HTML5 API using the Vimeo API - + } - player.media.play = function () { - assurePlaybackState.call(player, true); + // Create a faux HTML5 API using the Vimeo API + player.media.play = () => { + assurePlaybackState$1.call(player, true); return player.embed.play(); }; - - player.media.pause = function () { - assurePlaybackState.call(player, false); + player.media.pause = () => { + assurePlaybackState$1.call(player, false); return player.embed.pause(); }; - - player.media.stop = function () { + player.media.stop = () => { player.pause(); player.currentTime = 0; - }; // Seeking - + }; - var currentTime = player.media.currentTime; + // Seeking + let { + currentTime + } = player.media; Object.defineProperty(player.media, 'currentTime', { - get: function get() { + get() { return currentTime; }, - set: function set(time) { + set(time) { // Vimeo will automatically play on seek if the video hasn't been played before - // Get current paused state and volume etc - var embed = player.embed, - media = player.media, - paused = player.paused, - volume = player.volume; - var restorePause = paused && !embed.hasPlayed; // Set seeking state and trigger event + // Get current paused state and volume etc + const { + embed, + media, + paused, + volume + } = player; + const restorePause = paused && !embed.hasPlayed; + + // Set seeking state and trigger event media.seeking = true; - triggerEvent.call(player, media, 'seeking'); // If paused, mute until seek is complete - - Promise.resolve(restorePause && embed.setVolume(0)) // Seek - .then(function () { - return embed.setCurrentTime(time); - }) // Restore paused - .then(function () { - return restorePause && embed.pause(); - }) // Restore volume - .then(function () { - return restorePause && embed.setVolume(volume); - }).catch(function () {// Do nothing + triggerEvent.call(player, media, 'seeking'); + + // If paused, mute until seek is complete + Promise.resolve(restorePause && embed.setVolume(0)) + // Seek + .then(() => embed.setCurrentTime(time)) + // Restore paused + .then(() => restorePause && embed.pause()) + // Restore volume + .then(() => restorePause && embed.setVolume(volume)).catch(() => { + // Do nothing }); } - }); // Playback speed + }); - var speed = player.config.speed.selected; + // Playback speed + let speed = player.config.speed.selected; Object.defineProperty(player.media, 'playbackRate', { - get: function get() { + get() { return speed; }, - set: function set(input) { - player.embed.setPlaybackRate(input).then(function () { + set(input) { + player.embed.setPlaybackRate(input).then(() => { speed = input; triggerEvent.call(player, player.media, 'ratechange'); - }).catch(function () { + }).catch(() => { // Cannot set Playback Rate, Video is probably not on Pro account player.options.speed = [1]; }); } - }); // Volume + }); - var volume = player.config.volume; + // Volume + let { + volume + } = player.config; Object.defineProperty(player.media, 'volume', { - get: function get() { + get() { return volume; }, - set: function set(input) { - player.embed.setVolume(input).then(function () { + set(input) { + player.embed.setVolume(input).then(() => { volume = input; triggerEvent.call(player, player.media, 'volumechange'); }); } - }); // Muted + }); - var muted = player.config.muted; + // Muted + let { + muted + } = player.config; Object.defineProperty(player.media, 'muted', { - get: function get() { + get() { return muted; }, - set: function set(input) { - var toggle = is$1.boolean(input) ? input : false; - player.embed.setVolume(toggle ? 0 : player.config.volume).then(function () { + set(input) { + const toggle = is.boolean(input) ? input : false; + player.embed.setMuted(toggle ? true : player.config.muted).then(() => { muted = toggle; triggerEvent.call(player, player.media, 'volumechange'); }); } - }); // Loop + }); - var loop = player.config.loop; + // Loop + let { + loop + } = player.config; Object.defineProperty(player.media, 'loop', { - get: function get() { + get() { return loop; }, - set: function set(input) { - var toggle = is$1.boolean(input) ? input : player.config.loop.active; - player.embed.setLoop(toggle).then(function () { + set(input) { + const toggle = is.boolean(input) ? input : player.config.loop.active; + player.embed.setLoop(toggle).then(() => { loop = toggle; }); } - }); // Source + }); - var currentSrc; - player.embed.getVideoUrl().then(function (value) { + // Source + let currentSrc; + player.embed.getVideoUrl().then(value => { currentSrc = value; controls.setDownloadUrl.call(player); - }).catch(function (error) { - _this.debug.warn(error); + }).catch(error => { + this.debug.warn(error); }); Object.defineProperty(player.media, 'currentSrc', { - get: function get() { + get() { return currentSrc; } - }); // Ended + }); + // Ended Object.defineProperty(player.media, 'ended', { - get: function get() { + get() { return player.currentTime === player.duration; } - }); // Set aspect ratio based on video size - - Promise.all([player.embed.getVideoWidth(), player.embed.getVideoHeight()]).then(function (dimensions) { - var _dimensions = _slicedToArray(dimensions, 2), - width = _dimensions[0], - height = _dimensions[1]; + }); - player.embed.ratio = [width, height]; - setAspectRatio.call(_this); - }); // Set autopause + // Set aspect ratio based on video size + Promise.all([player.embed.getVideoWidth(), player.embed.getVideoHeight()]).then(dimensions => { + const [width, height] = dimensions; + player.embed.ratio = roundAspectRatio(width, height); + setAspectRatio.call(this); + }); - player.embed.setAutopause(player.config.autopause).then(function (state) { + // Set autopause + player.embed.setAutopause(player.config.autopause).then(state => { player.config.autopause = state; - }); // Get title + }); - player.embed.getVideoTitle().then(function (title) { + // Get title + player.embed.getVideoTitle().then(title => { player.config.title = title; - ui.setTitle.call(_this); - }); // Get current time + ui.setTitle.call(this); + }); - player.embed.getCurrentTime().then(function (value) { + // Get current time + player.embed.getCurrentTime().then(value => { currentTime = value; triggerEvent.call(player, player.media, 'timeupdate'); - }); // Get duration + }); - player.embed.getDuration().then(function (value) { + // Get duration + player.embed.getDuration().then(value => { player.media.duration = value; triggerEvent.call(player, player.media, 'durationchange'); - }); // Get captions + }); - player.embed.getTextTracks().then(function (tracks) { + // Get captions + player.embed.getTextTracks().then(tracks => { player.media.textTracks = tracks; captions.setup.call(player); }); - player.embed.on('cuechange', function (_ref) { - var _ref$cues = _ref.cues, - cues = _ref$cues === void 0 ? [] : _ref$cues; - var strippedCues = cues.map(function (cue) { - return stripHTML(cue.text); - }); + player.embed.on('cuechange', ({ + cues = [] + }) => { + const strippedCues = cues.map(cue => stripHTML(cue.text)); captions.updateCues.call(player, strippedCues); }); - player.embed.on('loaded', function () { + player.embed.on('loaded', () => { // Assure state and events are updated on autoplay - player.embed.getPaused().then(function (paused) { - assurePlaybackState.call(player, !paused); - + player.embed.getPaused().then(paused => { + assurePlaybackState$1.call(player, !paused); if (!paused) { triggerEvent.call(player, player.media, 'playing'); } }); + if (is.element(player.embed.element) && player.supported.ui) { + const frame = player.embed.element; - if (is$1.element(player.embed.element) && player.supported.ui) { - var frame = player.embed.element; // Fix keyboard focus issues + // Fix keyboard focus issues // https://github.com/sampotts/plyr/issues/317 - frame.setAttribute('tabindex', -1); } }); - player.embed.on('bufferstart', function () { + player.embed.on('bufferstart', () => { triggerEvent.call(player, player.media, 'waiting'); }); - player.embed.on('bufferend', function () { + player.embed.on('bufferend', () => { triggerEvent.call(player, player.media, 'playing'); }); - player.embed.on('play', function () { - assurePlaybackState.call(player, true); + player.embed.on('play', () => { + assurePlaybackState$1.call(player, true); triggerEvent.call(player, player.media, 'playing'); }); - player.embed.on('pause', function () { - assurePlaybackState.call(player, false); + player.embed.on('pause', () => { + assurePlaybackState$1.call(player, false); }); - player.embed.on('timeupdate', function (data) { + player.embed.on('timeupdate', data => { player.media.seeking = false; currentTime = data.seconds; triggerEvent.call(player, player.media, 'timeupdate'); }); - player.embed.on('progress', function (data) { + player.embed.on('progress', data => { player.media.buffered = data.percent; - triggerEvent.call(player, player.media, 'progress'); // Check all loaded + triggerEvent.call(player, player.media, 'progress'); + // Check all loaded if (parseInt(data.percent, 10) === 1) { triggerEvent.call(player, player.media, 'canplaythrough'); - } // Get duration as if we do it before load, it gives an incorrect value - // https://github.com/sampotts/plyr/issues/891 - + } - player.embed.getDuration().then(function (value) { + // Get duration as if we do it before load, it gives an incorrect value + // https://github.com/sampotts/plyr/issues/891 + player.embed.getDuration().then(value => { if (value !== player.media.duration) { player.media.duration = value; triggerEvent.call(player, player.media, 'durationchange'); } }); }); - player.embed.on('seeked', function () { + player.embed.on('seeked', () => { player.media.seeking = false; triggerEvent.call(player, player.media, 'seeked'); }); - player.embed.on('ended', function () { + player.embed.on('ended', () => { player.media.paused = true; triggerEvent.call(player, player.media, 'ended'); }); - player.embed.on('error', function (detail) { + player.embed.on('error', detail => { player.media.error = detail; triggerEvent.call(player, player.media, 'error'); - }); // Rebuild UI + }); - setTimeout(function () { - return ui.build.call(player); - }, 0); + // Rebuild UI + if (config.customControls) { + setTimeout(() => ui.build.call(player), 0); + } } }; // ========================================================================== + // YouTube plugin + // ========================================================================== - function parseId$1(url) { - if (is$1.empty(url)) { + + // Parse YouTube ID from URL + function parseId(url) { + if (is.empty(url)) { return null; } - - var regex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/; + const regex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/; return url.match(regex) ? RegExp.$2 : url; - } // Set playback state and trigger change (only on actual change) - + } - function assurePlaybackState$1(play) { + // Set playback state and trigger change (only on actual change) + function assurePlaybackState(play) { if (play && !this.embed.hasPlayed) { this.embed.hasPlayed = true; } - if (this.media.paused === play) { this.media.paused = !play; triggerEvent.call(this, this.media, play ? 'play' : 'pause'); } } - function getHost(config) { if (config.noCookie) { return 'https://www.youtube-nocookie.com'; } - if (window.location.protocol === 'http:') { return 'http://www.youtube.com'; - } // Use YouTube's default - + } + // Use YouTube's default return undefined; } - - var youtube = { - setup: function setup() { - var _this = this; - + const youtube = { + setup() { // Add embed class for responsive - toggleClass(this.elements.wrapper, this.config.classNames.embed, true); // Setup API + toggleClass(this.elements.wrapper, this.config.classNames.embed, true); - if (is$1.object(window.YT) && is$1.function(window.YT.Player)) { + // Setup API + if (is.object(window.YT) && is.function(window.YT.Player)) { youtube.ready.call(this); } else { // Reference current global callback - var callback = window.onYouTubeIframeAPIReady; // Set callback to process queue + const callback = window.onYouTubeIframeAPIReady; - window.onYouTubeIframeAPIReady = function () { + // Set callback to process queue + window.onYouTubeIframeAPIReady = () => { // Call global callback if set - if (is$1.function(callback)) { + if (is.function(callback)) { callback(); } + youtube.ready.call(this); + }; - youtube.ready.call(_this); - }; // Load the SDK - - - loadScript(this.config.urls.youtube.sdk).catch(function (error) { - _this.debug.warn('YouTube API failed to load', error); + // Load the SDK + loadScript(this.config.urls.youtube.sdk).catch(error => { + this.debug.warn('YouTube API failed to load', error); }); } }, // Get the media title - getTitle: function getTitle(videoId) { - var _this2 = this; - - var url = format(this.config.urls.youtube.api, videoId); - fetch(url).then(function (data) { - if (is$1.object(data)) { - var title = data.title, - height = data.height, - width = data.width; // Set title - - _this2.config.title = title; - ui.setTitle.call(_this2); // Set aspect ratio - - _this2.embed.ratio = [width, height]; + getTitle(videoId) { + const url = format(this.config.urls.youtube.api, videoId); + fetch(url).then(data => { + if (is.object(data)) { + const { + title, + height, + width + } = data; + + // Set title + this.config.title = title; + ui.setTitle.call(this); + + // Set aspect ratio + this.embed.ratio = roundAspectRatio(width, height); } - - setAspectRatio.call(_this2); - }).catch(function () { + setAspectRatio.call(this); + }).catch(() => { // Set aspect ratio - setAspectRatio.call(_this2); + setAspectRatio.call(this); }); }, // API ready - ready: function ready() { - var player = this; // Ignore already setup (race condition) - - var currentId = player.media && player.media.getAttribute('id'); - - if (!is$1.empty(currentId) && currentId.startsWith('youtube-')) { + ready() { + const player = this; + const config = player.config.youtube; + // Ignore already setup (race condition) + const currentId = player.media && player.media.getAttribute('id'); + if (!is.empty(currentId) && currentId.startsWith('youtube-')) { return; - } // Get the source URL or ID - + } - var source = player.media.getAttribute('src'); // Get from
if needed + // Get the source URL or ID + let source = player.media.getAttribute('src'); - if (is$1.empty(source)) { + // Get from
if needed + if (is.empty(source)) { source = player.media.getAttribute(this.config.attributes.embed.id); - } // Replace the