From 1079d1cee4a1389a6b697ae7e08a1c6835adcd52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs?= Date: Mon, 28 Feb 2022 07:51:38 +0800 Subject: Add plyr as player default and remove NodeJS, npm and videojs --- extlib/jquery | 1 + extlib/leaflet | 1 + extlib/plyr/blank.webm | Bin 0 -> 594 bytes extlib/plyr/build-instructions.md | 23 + extlib/plyr/plyr.css | 1 + extlib/plyr/plyr.js | 8678 +++++++++++++++++++++++++++++ extlib/plyr/plyr.min.js | 2 + extlib/plyr/plyr.min.js.map | 1 + extlib/plyr/plyr.min.mjs | 1 + extlib/plyr/plyr.min.mjs.map | 1 + extlib/plyr/plyr.mjs | 8670 +++++++++++++++++++++++++++++ extlib/plyr/plyr.polyfilled.js | 9215 +++++++++++++++++++++++++++++++ extlib/plyr/plyr.polyfilled.min.js | 2 + extlib/plyr/plyr.polyfilled.min.js.map | 1 + extlib/plyr/plyr.polyfilled.min.mjs | 1 + extlib/plyr/plyr.polyfilled.min.mjs.map | 1 + extlib/plyr/plyr.polyfilled.mjs | 9207 ++++++++++++++++++++++++++++++ extlib/plyr/plyr.svg | 1 + 18 files changed, 35807 insertions(+) create mode 160000 extlib/jquery create mode 160000 extlib/leaflet create mode 100644 extlib/plyr/blank.webm create mode 100644 extlib/plyr/build-instructions.md create mode 100644 extlib/plyr/plyr.css create mode 100644 extlib/plyr/plyr.js create mode 100644 extlib/plyr/plyr.min.js create mode 100644 extlib/plyr/plyr.min.js.map create mode 100644 extlib/plyr/plyr.min.mjs create mode 100644 extlib/plyr/plyr.min.mjs.map create mode 100644 extlib/plyr/plyr.mjs create mode 100644 extlib/plyr/plyr.polyfilled.js create mode 100644 extlib/plyr/plyr.polyfilled.min.js create mode 100644 extlib/plyr/plyr.polyfilled.min.js.map create mode 100644 extlib/plyr/plyr.polyfilled.min.mjs create mode 100644 extlib/plyr/plyr.polyfilled.min.mjs.map create mode 100644 extlib/plyr/plyr.polyfilled.mjs create mode 100644 extlib/plyr/plyr.svg (limited to 'extlib') diff --git a/extlib/jquery b/extlib/jquery new file mode 160000 index 00000000..683ceb8f --- /dev/null +++ b/extlib/jquery @@ -0,0 +1 @@ +Subproject commit 683ceb8ff067ac53a7cb464ba1ec3f88e353e3f5 diff --git a/extlib/leaflet b/extlib/leaflet new file mode 160000 index 00000000..8a5fdfc6 --- /dev/null +++ b/extlib/leaflet @@ -0,0 +1 @@ +Subproject commit 8a5fdfc6e3db2807b8f0dd617474e4ab2949142b diff --git a/extlib/plyr/blank.webm b/extlib/plyr/blank.webm new file mode 100644 index 00000000..6ec17ef3 Binary files /dev/null and b/extlib/plyr/blank.webm differ diff --git a/extlib/plyr/build-instructions.md b/extlib/plyr/build-instructions.md new file mode 100644 index 00000000..4323281d --- /dev/null +++ b/extlib/plyr/build-instructions.md @@ -0,0 +1,23 @@ +# Build steps for Plyr (3.6.8) + +Tested on Hyperbola GNU with Linux-libre. + +First install npm (node package manager). + +Clone the repo to a location of your choosing: +``` +git clone https://git.sr.ht/~heckyel/plyr +cd plyr +``` + +Install Plyr's dependencies: +``` +npm install +``` + +Build with npm: +``` +npm run build +``` + +plyr.js and other files will be in the `dist` directory. diff --git a/extlib/plyr/plyr.css b/extlib/plyr/plyr.css new file mode 100644 index 00000000..17dab36f --- /dev/null +++ b/extlib/plyr/plyr.css @@ -0,0 +1 @@ +@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: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:transparent;border:0;border-radius:3px;border-radius:var(--plyr-control-radius,3px);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.plyr__tab-focus{outline:3px dotted #00b3ff;outline:var(--plyr-tab-focus-color,var(--plyr-color-main,var(--plyr-color-main,#00b3ff))) dotted 3px;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:hsla(0,0%,100%,.9);background:var(--plyr-menu-background,hsla(0,0%,100%,.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-top-color:hsla(0,0%,100%,.9);border:var(--plyr-menu-arrow-size,4px) solid transparent;border-top-color:var(--plyr-menu-background,hsla(0,0%,100%,.9));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 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: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.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: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.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: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: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(-7px - -2);margin-right:calc(var(--plyr-control-spacing, 10px)*.7*-1 - -2);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:transparent;border:0;border-radius:26px;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: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:transparent;background-image:linear-gradient(90deg,currentColor 0,transparent 0);background-image:linear-gradient(to right,currentColor var(--value,0),transparent 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 0 0 0 1px rgba(35,40,47,.15) 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);margin-top:-4px;margin-top:calc(var(--plyr-range-thumb-height, 13px)/2*-1 - 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:transparent;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 0 0 0 1px rgba(35,40,47,.15) 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: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:transparent}.plyr--full-ui input[type=range]::-ms-fill-upper,.plyr--full-ui input[type=range]::-ms-track{background:transparent;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:transparent;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 0 0 0 1px rgba(35,40,47,.15) 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);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].plyr__tab-focus::-webkit-slider-runnable-track{outline:3px dotted #00b3ff;outline:var(--plyr-tab-focus-color,var(--plyr-color-main,var(--plyr-color-main,#00b3ff))) dotted 3px;outline-offset:2px}.plyr--full-ui input[type=range].plyr__tab-focus::-moz-range-track{outline:3px dotted #00b3ff;outline:var(--plyr-tab-focus-color,var(--plyr-color-main,var(--plyr-color-main,#00b3ff))) dotted 3px;outline-offset:2px}.plyr--full-ui input[type=range].plyr__tab-focus::-ms-track{outline:3px dotted #00b3ff;outline:var(--plyr-tab-focus-color,var(--plyr-color-main,var(--plyr-color-main,#00b3ff))) dotted 3px;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:hsla(0,0%,100%,.9);background:var(--plyr-tooltip-background,hsla(0,0%,100%,.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: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 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 hsla(0,0%,100%,.9);border-top:var(--plyr-tooltip-arrow-size,4px) solid var(--plyr-tooltip-background,hsla(0,0%,100%,.9));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.plyr__tab-focus .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__control .plyr__tooltip,.plyr__controls>.plyr__control:first-child .plyr__tooltip{left:0;transform:translateY(10px) scale(.8);transform-origin:0 100%}.plyr__controls>.plyr__control:first-child+.plyr__control .plyr__tooltip:before,.plyr__controls>.plyr__control:first-child .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__control.plyr__tab-focus .plyr__tooltip,.plyr__controls>.plyr__control:first-child+.plyr__control .plyr__tooltip--visible,.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 .plyr__tooltip--visible,.plyr__controls>.plyr__control:first-child:hover .plyr__tooltip,.plyr__controls>.plyr__control:last-child.plyr__tab-focus .plyr__tooltip,.plyr__controls>.plyr__control:last-child .plyr__tooltip--visible,.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__buffer,.plyr__progress input[type=range]{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{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:transparent;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:transparent}.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 0,transparent 50%,rgba(35,40,47,.6) 0,rgba(35,40,47,.6) 75%,transparent 0,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:hsla(0,0%,100%,.25);background-color:var(--plyr-video-progress-buffered-background,hsla(0,0%,100%,.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:5px;margin-left:calc(var(--plyr-control-spacing, 10px)/2);margin-right:5px;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 0 0 0 1px rgba(35,40,47,.15) 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 0 0 0 1px rgba(35,40,47,.15) 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 0 0 0 1px rgba(35,40,47,.15) 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;background:var(--plyr-video-background,var(--plyr-video-background,#000));overflow:hidden}.plyr--video.plyr--menu-open{overflow:visible}.plyr__video-wrapper{background:#000;background:var(--plyr-video-background,var(--plyr-video-background,#000));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(transparent,rgba(0,0,0,.75));background:var(--plyr-video-controls-background,linear-gradient(transparent,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: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.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: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:hsla(0,0%,100%,.25);background-color:var(--plyr-video-range-track-background,var(--plyr-video-progress-buffered-background,hsla(0,0%,100%,.25)))}.plyr--full-ui.plyr--video input[type=range]::-moz-range-track{background-color:hsla(0,0%,100%,.25);background-color:var(--plyr-video-range-track-background,var(--plyr-video-progress-buffered-background,hsla(0,0%,100%,.25)))}.plyr--full-ui.plyr--video input[type=range]::-ms-track{background-color:hsla(0,0%,100%,.25);background-color:var(--plyr-video-range-track-background,var(--plyr-video-progress-buffered-background,hsla(0,0%,100%,.25)))}.plyr--full-ui.plyr--video input[type=range]:active::-webkit-slider-thumb{box-shadow:0 1px 1px 0 0 0 1px rgba(35,40,47,.15) 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,hsla(0,0%,100%,.5))}.plyr--full-ui.plyr--video input[type=range]:active::-moz-range-thumb{box-shadow:0 1px 1px 0 0 0 1px rgba(35,40,47,.15) 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,hsla(0,0%,100%,.5))}.plyr--full-ui.plyr--video input[type=range]:active::-ms-thumb{box-shadow:0 1px 1px 0 0 0 1px rgba(35,40,47,.15) 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,hsla(0,0%,100%,.5))}.plyr--video .plyr__progress__buffer{color:hsla(0,0%,100%,.25);color:var(--plyr-video-progress-buffered-background,hsla(0,0%,100%,.25))}.plyr:-webkit-full-screen{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:fullscreen video{height:100%}.plyr:-webkit-full-screen .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:fullscreen .plyr__control .icon--exit-fullscreen+svg{display:none}.plyr:-webkit-full-screen.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: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;display:block;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:hsla(0,0%,100%,.9);background-color:var(--plyr-tooltip-background,hsla(0,0%,100%,.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:10px;margin-bottom:calc(var(--plyr-control-spacing, 10px)/2*2);opacity:0;padding:3px;padding:var(--plyr-tooltip-radius,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 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 hsla(0,0%,100%,.9);border-top:var(--plyr-tooltip-arrow-size,4px) solid var(--plyr-tooltip-background,hsla(0,0%,100%,.9));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__preview-thumb__image-container{background:#c1c8d1;border-radius:2px;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:2px;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;-o-object-fit:contain;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/extlib/plyr/plyr.js b/extlib/plyr/plyr.js new file mode 100644 index 00000000..fe1b6ae9 --- /dev/null +++ b/extlib/plyr/plyr.js @@ -0,0 +1,8678 @@ +typeof navigator === "object" && (function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define('Plyr', factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Plyr = factory()); +})(this, (function () { 'use strict'; + + function _defineProperty$1(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; + } + + function _classCallCheck(e, t) { + if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function"); + } + + 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(e, t, n) { + return t && _defineProperties(e.prototype, t), n && _defineProperties(e, n), e; + } + + function _defineProperty(e, t, n) { + return t in e ? Object.defineProperty(e, t, { + value: n, + enumerable: !0, + configurable: !0, + writable: !0 + }) : e[t] = n, e; + } + + 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(e) { + for (var t = 1; t < arguments.length; t++) { + var n = null != arguments[t] ? arguments[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$1 = { + addCSS: !0, + thumbWidth: 15, + watch: !0 + }; + + 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, { + bubbles: !0 + }); + e.dispatchEvent(n); + } + } + + 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(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(e, [{ + key: "init", + 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 () { + 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 (e) { + var t = this, + n = e ? "addEventListener" : "removeEventListener"; + ["touchstart", "touchmove", "touchend"].forEach(function (e) { + t.element[n](e, function (e) { + return t.set(e); + }, !1); + }); + } + }, { + key: "get", + 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; + 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 (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 (t) { + var n = 1 < arguments.length && void 0 !== arguments[1] ? arguments[1] : {}, + 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$1.element(n) && matches$1(n, t) && new e(n, i); + }); + }); + }); + o.observe(document.body, { + childList: !0, + subtree: !0 + }); + } + + return r.map(function (t) { + return new e(t, n); + }); + } + }, { + key: "enabled", + get: function () { + return "ontouchstart" in document.documentElement; + } + }]), e; + }(); + + // ========================================================================== + // Type checking utils + // ========================================================================== + 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 => getConstructor(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(input, window.URL)) { + return true; + } // Must be string from here + + + if (!isString(input)) { + return false; + } // Add the protocol if required + + + let string = input; + + if (!input.startsWith('http://') || !input.startsWith('https://')) { + string = `http://${input}`; + } + + try { + return !isEmpty(new URL(string).hostname); + } catch (_) { + return false; + } + }; + + var is = { + nullOrUndefined: isNullOrUndefined, + object: isObject, + number: isNumber, + string: isString, + boolean: isBoolean, + function: isFunction, + array: isArray, + weakMap: isWeakMap, + nodeList: isNodeList, + element: isElement, + textNode: isTextNode, + event: isEvent, + keyboardEvent: isKeyboardEvent, + cue: isCue, + track: isTrack, + promise: isPromise, + url: isUrl, + empty: isEmpty + }; + + // ========================================================================== + const transitionEndEvent = (() => { + const element = document.createElement('span'); + const events = { + WebkitTransition: 'webkitTransitionEnd', + MozTransition: 'transitionend', + OTransition: 'oTransitionEnd otransitionend', + transition: 'transitionend' + }; + 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(() => { + try { + // eslint-disable-next-line no-param-reassign + element.hidden = true; // eslint-disable-next-line no-unused-expressions + + element.offsetHeight; // eslint-disable-next-line no-param-reassign + + element.hidden = false; + } catch (_) {// Do nothing + } + }, delay); + } + + // ========================================================================== + // Browser sniffing + // Unfortunately, due to mixed support, UA sniffing is required + // ========================================================================== + const browser = { + isIE: Boolean(window.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: navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1 || /(iPad|iPhone|iPod)/gi.test(navigator.platform) + }; + + // ========================================================================== + + function cloneDeep(object) { + return JSON.parse(JSON.stringify(object)); + } // Get a nested value in an object + + function getDeep(object, path) { + 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; + } + + const source = sources.shift(); + + if (!is.object(source)) { + return target; + } + + Object.keys(source).forEach(key => { + if (is.object(source[key])) { + if (!Object.keys(target).includes(key)) { + Object.assign(target, { + [key]: {} + }); + } + + extend(target[key], source[key]); + } else { + Object.assign(target, { + [key]: source[key] + }); + } + }); + return extend(target, ...sources); + } + + // ========================================================================== + + function wrap(elements, wrapper) { + // Convert `elements` to an array, if necessary. + const targets = elements.length ? elements : [elements]; // 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; // Wrap the element (is automatically removed from its current + // parent). + + child.appendChild(element); // 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 + + function setAttributes(element, attributes) { + if (!is.element(element) || is.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(([, value]) => !is.nullOrUndefined(value)).forEach(([key, value]) => element.setAttribute(key, value)); + } // Create a DocumentFragment + + function createElement(type, attributes, text) { + // Create a new + const element = document.createElement(type); // Set all passed attributes + + if (is.object(attributes)) { + setAttributes(element, attributes); + } // Add text node + + + if (is.string(text)) { + element.innerText = text; + } // Return built element + + + return element; + } // Inaert an element after another + + function insertAfter(element, target) { + if (!is.element(element) || !is.element(target)) { + return; + } + + target.parentNode.insertBefore(element, target.nextSibling); + } // Insert a DocumentFragment + + function insertElement(type, parent, attributes, text) { + if (!is.element(parent)) { + return; + } + + parent.appendChild(createElement(type, attributes, text)); + } // Remove element(s) + + function removeElement(element) { + if (is.nodeList(element) || is.array(element)) { + Array.from(element).forEach(removeElement); + return; + } + + if (!is.element(element) || !is.element(element.parentNode)) { + return; + } + + element.parentNode.removeChild(element); + } // Remove all child elements + + function emptyElement(element) { + if (!is.element(element)) { + return; + } + + let { + length + } = element.childNodes; + + while (length > 0) { + element.removeChild(element.lastChild); + length -= 1; + } + } // Replace element + + function replaceElement(newChild, oldChild) { + 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 + + function getAttributesFromSelector(sel, existingAttributes) { + // For example: + // '.test' to { class: 'test' } + // '#test' to { id: 'test' } + // '[data-test="test"]' to { 'data-test': 'test' } + if (!is.string(sel) || is.empty(sel)) { + return {}; + } + + const attributes = {}; + const existing = extend({}, existingAttributes); + sel.split(',').forEach(s => { + // Remove whitespace + 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.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; + break; + } + }); + return extend(existing, attributes); + } // Toggle hidden + + function toggleHidden(element, hidden) { + if (!is.element(element)) { + return; + } + + let hide = hidden; + + if (!is.boolean(hide)) { + hide = !element.hidden; + } // eslint-disable-next-line no-param-reassign + + + element.hidden = hide; + } // Mirror Element.classList.toggle, with IE compatibility for "force" argument + + function toggleClass(element, className, force) { + if (is.nodeList(element)) { + return Array.from(element).map(e => toggleClass(e, className, force)); + } + + 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 + + function hasClass(element, className) { + 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); + } + + 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$1(element, selector) { + const { + prototype + } = Element; // https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill + + function closestElement() { + let el = this; + + do { + if (matches.matches(el, selector)) return el; + el = el.parentElement || el.parentNode; + } while (el !== null && el.nodeType === 1); + + return null; + } + + const method = prototype.closest || closestElement; + return method.call(element, selector); + } // Find all elements + + function getElements(selector) { + return this.elements.container.querySelectorAll(selector); + } // Find a single element + + function getElement(selector) { + return this.elements.container.querySelector(selector); + } // Set focus and tab focus class + + function setFocus(element = null, tabFocus = 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); + } + } + + // ========================================================================== + + 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 + + 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(type, provider, playsinline) { + const canPlayInline = browser.isIPhone && playsinline && support.playsinline; + const api = support[type] || provider !== 'html5'; + const ui = api && support.rangeInput && (type !== 'video' || !browser.isIPhone || canPlayInline); + return { + api, + ui + }; + }, + + // Picture-in-picture support + // Safari & Chrome only currently + pip: (() => { + if (browser.isIPhone) { + return false; + } // 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 + + + if (document.pictureInPictureEnabled && !createElement('video').disablePictureInPicture) { + return true; + } + + return false; + })(), + // Airplay support + // Safari only currently + 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(input) { + if (is.empty(input)) { + return false; + } + + const [mediaType] = input.split('/'); + let type = input; // Verify we're using HTML5 and there's no media type mismatch + + if (!this.isHTML5 || mediaType !== this.type) { + return false; + } // Add codec if required + + + if (Object.keys(defaultCodecs).includes(type)) { + type += `; codecs="${defaultCodecs[input]}"`; + } + + try { + return Boolean(type && this.media.canPlayType(type).replace(/no/, '')); + } catch (_) { + return false; + } + }, + + // Check for textTracks support + textTracks: 'textTracks' in document.createElement('video'), + // Sliders + 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, + // Detect transitions support + transitions: transitionEndEvent !== false, + // Reduced motion iOS & MacOS setting + // https://webkit.org/blog/7551/responsive-design-for-motion/ + reducedMotion: 'matchMedia' in window && window.matchMedia('(prefers-reduced-motion)').matches + }; + + // ========================================================================== + // https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md + // https://www.youtube.com/watch?v=NPM6172J22g + + const supportsPassiveListeners = (() => { + // Test via a getter in the options object to see if the passive property is accessed + let supported = false; + + try { + const options = Object.defineProperty({}, 'passive', { + get() { + supported = true; + return null; + } + + }); + window.addEventListener('test', null, options); + window.removeEventListener('test', null, options); + } catch (_) {// Do nothing + } + + return supported; + })(); // 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.empty(event) || !is.function(callback)) { + return; + } // 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; // If passive events listeners are supported + + if (supportsPassiveListeners) { + options = { + // Whether the listener can be passive (i.e. default never prevented) + passive, + // Whether the listener is a capturing listener or not + capture + }; + } // 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, + type, + callback, + options + }); + } + + element[toggle ? 'addEventListener' : 'removeEventListener'](type, callback, options); + }); + } // 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, events = '', callback, passive = true, capture = false) { + toggleListener.call(this, element, events, callback, false, passive, capture); + } // Bind once-only event handler + + function once(element, events = '', callback, passive = true, capture = false) { + const onceCallback = (...args) => { + off(element, events, onceCallback, passive, capture); + callback.apply(this, args); + }; + + toggleListener.call(this, element, events, onceCallback, true, passive, capture); + } // Trigger event + + function triggerEvent(element, type = '', bubbles = false, detail = {}) { + // Bail if no element + if (!is.element(element) || is.empty(type)) { + return; + } // Create and dispatch the event + + + const event = new CustomEvent(type, { + bubbles, + detail: { ...detail, + plyr: this + } + }); // Dispatch the event + + element.dispatchEvent(event); + } // Unbind all cached event listeners + + function unbindListeners() { + if (this && this.eventListeners) { + this.eventListeners.forEach(item => { + const { + element, + type, + callback, + options + } = item; + element.removeEventListener(type, callback, options); + }); + this.eventListeners = []; + } + } // Run method when / if player is ready + + function ready() { + return new Promise(resolve => this.ready ? setTimeout(resolve, 0) : on.call(this, this.elements.container, 'ready', resolve)).then(() => {}); + } + + /** + * Silence a Promise-like object. + * This is useful for avoiding non-harmful, but potentially confusing "uncaught + * play promise" rejection error messages. + * @param {Object} value An object that may or may not be `Promise`-like. + */ + + function silencePromise(value) { + if (is.promise(value)) { + value.then(null, () => {}); + } + } + + // ========================================================================== + + function dedupe(array) { + if (!is.array(array)) { + return array; + } + + return array.filter((item, index) => array.indexOf(item) === index); + } // 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); + } + + // ========================================================================== + + function supportsCSS(declaration) { + if (!window || !window.CSS) { + return false; + } + + return window.CSS.supports(declaration); + } // 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] + }), {}); // 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); + } // 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); // 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) { + const { + videoWidth, + videoHeight + } = this.media; + ratio = [videoWidth, videoHeight]; + } + + return reduceAspectRatio(ratio); + } // 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}%`; + } // For Vimeo we have an extra
to hide the standard controls and UI + + + if (this.isVimeo && !this.config.vimeo.premium && this.supported.ui) { + 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.add(this.config.classNames.videoFixedRatio); + } + + return { + 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]; + } + + // ========================================================================== + const html5 = { + getSources() { + if (!this.isHTML5) { + return []; + } + + const sources = Array.from(this.media.querySelectorAll('source')); // 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); + }); + }, + + // Get quality levels + 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(source => Number(source.getAttribute('data-res'))).filter(Boolean); + }, + + setup() { + if (!this.isHTML5) { + return; + } + + const player = this; // Set speed options from config + + player.options.speed = player.config.speed.options; // Set aspect ratio if fixed + + if (!is.empty(this.config.ratio)) { + setAspectRatio.call(player); + } // Quality + + + Object.defineProperty(player.media, 'quality', { + get() { + // Get sources + 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(input) { + if (player.quality === input) { + return; + } // 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 + 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 + + + const { + currentTime, + paused, + preload, + readyState, + playbackRate + } = player.media; // 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', () => { + player.speed = playbackRate; + player.currentTime = currentTime; // Resume playing + + if (!paused) { + silencePromise(player.play()); + } + }); // Load new source + + player.media.load(); + } + } // Trigger change event + + + triggerEvent.call(player, player.media, 'qualitychange', false, { + quality: input + }); + } + + }); + }, + + // Cancel current network requests + // See https://github.com/sampotts/plyr/issues/174 + cancelRequests() { + if (!this.isHTML5) { + return; + } // Remove child sources + + + removeElement(html5.getSources.call(this)); // 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); // Load the new empty source + // This will cancel existing requests + // See https://github.com/sampotts/plyr/issues/174 + + this.media.load(); // Debugging + + this.debug.log('Cancelled network requests'); + } + + }; + + // ========================================================================== + + function generateId(prefix) { + return `${prefix}-${Math.floor(Math.random() * 10000)}`; + } // Format string + + function format(input, ...args) { + if (is.empty(input)) { + return input; + } + + return input.toString().replace(/{(\d+)}/g, (match, 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 + + const replaceAll = (input = '', find = '', replace = '') => input.replace(new RegExp(find.toString().replace(/([.*+?^=!:${}()|[\]/\\])/g, '\\$1'), 'g'), replace.toString()); // Convert to title case + + const toTitleCase = (input = '') => input.toString().replace(/\w\S*/g, text => text.charAt(0).toUpperCase() + text.substr(1).toLowerCase()); // Convert string to pascalCase + + function toPascalCase(input = '') { + let string = input.toString(); // 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(input = '') { + let string = input.toString(); // 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 + + function stripHTML(source) { + 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 + + function getHTML(element) { + const wrapper = document.createElement('div'); + wrapper.appendChild(element); + return wrapper.innerHTML; + } + + // ========================================================================== + + const resources = { + pip: 'PIP', + airplay: 'AirPlay', + html5: 'HTML5', + vimeo: 'Vimeo', + youtube: 'YouTube' + }; + const i18n = { + get(key = '', config = {}) { + if (is.empty(key) || is.empty(config)) { + return ''; + } + + let string = getDeep(config.i18n, key); + + if (is.empty(string)) { + if (Object.keys(resources).includes(key)) { + return resources[key]; + } + + return ''; + } + + const replace = { + '{seektime}': config.seekTime, + '{title}': config.title + }; + Object.entries(replace).forEach(([k, v]) => { + string = replaceAll(string, k, v); + }); + return string; + } + + }; + + class Storage { + constructor(player) { + _defineProperty$1(this, "get", key => { + if (!Storage.supported || !this.enabled) { + return null; + } + + const store = window.localStorage.getItem(this.key); + + if (is.empty(store)) { + return null; + } + + 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.object(object)) { + return; + } // Get current storage + + + let storage = this.get(); // Default to empty object + + if (is.empty(storage)) { + storage = {}; + } // Update the working copy of the values + + + extend(storage, object); // Update storage + + try { + window.localStorage.setItem(this.key, JSON.stringify(storage)); + } catch (_) {// Do nothing + } + }); + + this.enabled = player.config.storage.enabled; + this.key = player.config.storage.key; + } // Check for actual support (see if we can use it) + + + static get supported() { + try { + if (!('localStorage' in window)) { + return false; + } + + const 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(test, test); + window.localStorage.removeItem(test); + return true; + } catch (_) { + return false; + } + } + + } + + // ========================================================================== + // Fetch wrapper + // Using XHR to avoid issues with older browsers + // ========================================================================== + function fetch(url, responseType = 'text') { + return new Promise((resolve, reject) => { + try { + const request = new XMLHttpRequest(); // Check for CORS support + + if (!('withCredentials' in request)) { + return; + } + + request.addEventListener('load', () => { + if (responseType === 'text') { + try { + resolve(JSON.parse(request.responseText)); + } catch (_) { + resolve(request.responseText); + } + } else { + resolve(request.response); + } + }); + request.addEventListener('error', () => { + throw new Error(request.status); + }); + request.open('GET', url, true); // Set the required response type + + request.responseType = responseType; + request.send(); + } catch (error) { + reject(error); + } + }); + } + + // ========================================================================== + + function loadSprite(url, id) { + if (!is.string(url)) { + return; + } + + 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 + + if (hasId && exists()) { + return; + } // Inject the SVG to the body + + + document.body.insertAdjacentElement('afterbegin', container); + }; // Only load once if ID set + + + if (!hasId || !exists()) { + const useStorage = Storage.supported; // Create container + + const container = document.createElement('div'); + container.setAttribute('hidden', ''); + + if (hasId) { + container.setAttribute('id', id); + } // Check in cache + + + if (useStorage) { + const cached = window.localStorage.getItem(`${prefix}-${id}`); + isCached = cached !== null; + + if (isCached) { + const data = JSON.parse(cached); + update(container, data.content); + } + } // Get the sprite + + + fetch(url).then(result => { + if (is.empty(result)) { + return; + } + + if (useStorage) { + try { + window.localStorage.setItem(`${prefix}-${id}`, JSON.stringify({ + content: result + })); + } catch (_) {// Do nothing + } + } + + update(container, result); + }).catch(() => {}); + } + } + + // ========================================================================== + + 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.number(time)) { + return formatTime(undefined, displayHours, inverted); + } // 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 = `${hours}:`; + } else { + hours = ''; + } // Render + + + return `${inverted && time > 0 ? '-' : ''}${hours}${format(mins)}:${format(secs)}`; + } + + // ========================================================================== + + 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 + }; + }, + + // Find the UI controls + findElements() { + try { + 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), + restart: getElement.call(this, this.config.selectors.buttons.restart), + rewind: getElement.call(this, this.config.selectors.buttons.rewind), + fastForward: getElement.call(this, this.config.selectors.buttons.fastForward), + mute: getElement.call(this, this.config.selectors.buttons.mute), + pip: getElement.call(this, this.config.selectors.buttons.pip), + airplay: getElement.call(this, this.config.selectors.buttons.airplay), + 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 + + this.elements.inputs = { + seek: getElement.call(this, this.config.selectors.inputs.seek), + volume: getElement.call(this, this.config.selectors.inputs.volume) + }; // 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.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.toggleNativeControls(true); + return false; + } + }, + + // Create icon + 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 + + const use = document.createElementNS(namespace, 'use'); + const path = `${iconPath}-${type}`; // 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 + + icon.appendChild(use); + return icon; + }, + + // Create hidden text label + 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(text) { + if (is.empty(text)) { + return null; + } + + const badge = createElement('span', { + class: this.config.classNames.menu.value + }); + badge.appendChild(createElement('span', { + class: this.config.classNames.menu.badge + }, text)); + return badge; + }, + + // Create a
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 + + + const params = buildUrlParams({ + loop: player.config.loop.active, + autoplay: player.autoplay, + muted: player.muted, + gesture: 'media', + playsinline: !this.config.fullscreen.iosNative, + // 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', 'encrypted-media', 'accelerometer', 'gyroscope'].join('; ')); // Set the referrer policy if required + + if (!is.empty(referrerPolicy)) { + iframe.setAttribute('referrerPolicy', referrerPolicy); + } // Inject the package + + + if (premium || !config.customControls) { + iframe.setAttribute('data-poster', player.poster); + player.media = replaceElement(iframe, player.media); + } else { + const wrapper = createElement('div', { + class: player.config.classNames.embedContainer, + 'data-poster': player.poster + }); + wrapper.appendChild(iframe); + player.media = replaceElement(wrapper, player.media); + } // Get poster image + + + if (!config.customControls) { + fetch(format(player.config.urls.vimeo.api, src)).then(response => { + if (is.empty(response) || !response.thumbnail_url) { + return; + } // Set and show poster + + + ui.setPoster.call(player, response.thumbnail_url).catch(() => {}); + }); + } // 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 + + if (player.supported.ui) { + player.embed.disableTextTrack(); + } // Create a faux HTML5 API using the Vimeo API + + + player.media.play = () => { + assurePlaybackState$1.call(player, true); + return player.embed.play(); + }; + + player.media.pause = () => { + assurePlaybackState$1.call(player, false); + return player.embed.pause(); + }; + + player.media.stop = () => { + player.pause(); + player.currentTime = 0; + }; // Seeking + + + let { + currentTime + } = player.media; + Object.defineProperty(player.media, 'currentTime', { + get() { + return currentTime; + }, + + set(time) { + // Vimeo will automatically play on seek if the video hasn't been played before + // 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(() => embed.setCurrentTime(time)) // Restore paused + .then(() => restorePause && embed.pause()) // Restore volume + .then(() => restorePause && embed.setVolume(volume)).catch(() => {// Do nothing + }); + } + + }); // Playback speed + + let speed = player.config.speed.selected; + Object.defineProperty(player.media, 'playbackRate', { + get() { + return speed; + }, + + set(input) { + player.embed.setPlaybackRate(input).then(() => { + speed = input; + triggerEvent.call(player, player.media, 'ratechange'); + }).catch(() => { + // Cannot set Playback Rate, Video is probably not on Pro account + player.options.speed = [1]; + }); + } + + }); // Volume + + let { + volume + } = player.config; + Object.defineProperty(player.media, 'volume', { + get() { + return volume; + }, + + set(input) { + player.embed.setVolume(input).then(() => { + volume = input; + triggerEvent.call(player, player.media, 'volumechange'); + }); + } + + }); // Muted + + let { + muted + } = player.config; + Object.defineProperty(player.media, 'muted', { + get() { + return muted; + }, + + set(input) { + const toggle = is.boolean(input) ? input : false; + player.embed.setVolume(toggle ? 0 : player.config.volume).then(() => { + muted = toggle; + triggerEvent.call(player, player.media, 'volumechange'); + }); + } + + }); // Loop + + let { + loop + } = player.config; + Object.defineProperty(player.media, 'loop', { + get() { + return loop; + }, + + set(input) { + const toggle = is.boolean(input) ? input : player.config.loop.active; + player.embed.setLoop(toggle).then(() => { + loop = toggle; + }); + } + + }); // Source + + let currentSrc; + player.embed.getVideoUrl().then(value => { + currentSrc = value; + controls.setDownloadUrl.call(player); + }).catch(error => { + this.debug.warn(error); + }); + Object.defineProperty(player.media, 'currentSrc', { + get() { + return currentSrc; + } + + }); // Ended + + Object.defineProperty(player.media, 'ended', { + get() { + return player.currentTime === player.duration; + } + + }); // 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); + }); // Set autopause + + player.embed.setAutopause(player.config.autopause).then(state => { + player.config.autopause = state; + }); // Get title + + player.embed.getVideoTitle().then(title => { + player.config.title = title; + ui.setTitle.call(this); + }); // Get current time + + player.embed.getCurrentTime().then(value => { + currentTime = value; + triggerEvent.call(player, player.media, 'timeupdate'); + }); // Get duration + + player.embed.getDuration().then(value => { + player.media.duration = value; + triggerEvent.call(player, player.media, 'durationchange'); + }); // Get captions + + player.embed.getTextTracks().then(tracks => { + player.media.textTracks = tracks; + captions.setup.call(player); + }); + player.embed.on('cuechange', ({ + cues = [] + }) => { + const strippedCues = cues.map(cue => stripHTML(cue.text)); + captions.updateCues.call(player, strippedCues); + }); + player.embed.on('loaded', () => { + // Assure state and events are updated on autoplay + 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; // Fix keyboard focus issues + // https://github.com/sampotts/plyr/issues/317 + + frame.setAttribute('tabindex', -1); + } + }); + player.embed.on('bufferstart', () => { + triggerEvent.call(player, player.media, 'waiting'); + }); + player.embed.on('bufferend', () => { + triggerEvent.call(player, player.media, 'playing'); + }); + player.embed.on('play', () => { + assurePlaybackState$1.call(player, true); + triggerEvent.call(player, player.media, 'playing'); + }); + player.embed.on('pause', () => { + assurePlaybackState$1.call(player, false); + }); + player.embed.on('timeupdate', data => { + player.media.seeking = false; + currentTime = data.seconds; + triggerEvent.call(player, player.media, 'timeupdate'); + }); + player.embed.on('progress', data => { + player.media.buffered = data.percent; + 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(value => { + if (value !== player.media.duration) { + player.media.duration = value; + triggerEvent.call(player, player.media, 'durationchange'); + } + }); + }); + player.embed.on('seeked', () => { + player.media.seeking = false; + triggerEvent.call(player, player.media, 'seeked'); + }); + player.embed.on('ended', () => { + player.media.paused = true; + triggerEvent.call(player, player.media, 'ended'); + }); + player.embed.on('error', detail => { + player.media.error = detail; + triggerEvent.call(player, player.media, 'error'); + }); // Rebuild UI + + if (config.customControls) { + setTimeout(() => ui.build.call(player), 0); + } + } + + }; + + // ========================================================================== + + function parseId(url) { + if (is.empty(url)) { + return null; + } + + 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(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 + + + return undefined; + } + + const youtube = { + setup() { + // Add embed class for responsive + toggleClass(this.elements.wrapper, this.config.classNames.embed, true); // Setup API + + if (is.object(window.YT) && is.function(window.YT.Player)) { + youtube.ready.call(this); + } else { + // Reference current global callback + const callback = window.onYouTubeIframeAPIReady; // Set callback to process queue + + window.onYouTubeIframeAPIReady = () => { + // Call global callback if set + if (is.function(callback)) { + callback(); + } + + youtube.ready.call(this); + }; // 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(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(this); + }).catch(() => { + // Set aspect ratio + setAspectRatio.call(this); + }); + }, + + // API ready + 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 + + + let source = player.media.getAttribute('src'); // Get from
if needed + + if (is.empty(source)) { + source = player.media.getAttribute(this.config.attributes.embed.id); + } // Replace the