diff --git a/.gitignore b/.gitignore
index c7134f8..31b19be 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,5 @@
node_modules
-site
+site/live
config/local.toml
config/generated.toml
install/db_setup.sql
diff --git a/site/styles.css b/site/styles.css
new file mode 100644
index 0000000..e3b4b52
--- /dev/null
+++ b/site/styles.css
@@ -0,0 +1,68 @@
+body {
+ color: white;
+ background-color: #1f1f1f;
+}
+
+a {
+ color: #368cf3;
+ text-decoration: none;
+}
+
+#wrapper {
+ display: flex;
+ justify-content: center;
+ flex-wrap: nowrap;
+ flex-direction: column;
+ margin-top: 0px;
+ min-height: 100vh;
+}
+
+#header {
+ text-align: center;
+ margin-top: 0px;
+ margin-bottom: 10px;
+}
+
+#content {
+ text-align: center;
+ flex: 1;
+ margin-left: 5%;
+ margin-right: 5%;
+ height: 100%;
+}
+
+#jscontainer {
+ display: flex;
+ justify-content: center;
+ flex-wrap: nowrap;
+ flex-direction: row;
+ width: 100%;
+ min-height: 500px;
+ height: 70%;
+ margin: 0px;
+}
+#jschild {
+ display: inline;
+ margin: 0;
+}
+
+#footer {
+ text-align: center;
+ color: #bdbdbd;
+}
+
+#footer > div {
+ display: flex;
+ justify-content: center;
+ flex-wrap: nowrap;
+ flex-direction: row;
+}
+
+#footer > div > div {
+ flex: 0;
+ display: inline-block;
+ margin-top: 25px;
+ margin-left: 20px;
+ margin-right: 20px;
+ align-self: flex-end;
+}
\ No newline at end of file
diff --git a/site/videojs/alt/video-js-cdn.css b/site/videojs/alt/video-js-cdn.css
new file mode 100644
index 0000000..8aadce6
--- /dev/null
+++ b/site/videojs/alt/video-js-cdn.css
@@ -0,0 +1,1386 @@
+.video-js .vjs-big-play-button .vjs-icon-placeholder:before, .vjs-button > .vjs-icon-placeholder:before, .video-js .vjs-modal-dialog, .vjs-modal-dialog .vjs-modal-dialog-content {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%; }
+
+.video-js .vjs-big-play-button .vjs-icon-placeholder:before, .vjs-button > .vjs-icon-placeholder:before {
+ text-align: center; }
+
+@font-face {
+ font-family: VideoJS;
+ src: url(data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAABBIAAsAAAAAGoQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADsAAABUIIslek9TLzIAAAFEAAAAPgAAAFZRiV3RY21hcAAAAYQAAADQAAADIjn098ZnbHlmAAACVAAACv4AABEIAwnSw2hlYWQAAA1UAAAAKwAAADYV1OgpaGhlYQAADYAAAAAbAAAAJA4DByFobXR4AAANnAAAAA8AAACE4AAAAGxvY2EAAA2sAAAARAAAAEQ9NEHGbWF4cAAADfAAAAAfAAAAIAEyAIFuYW1lAAAOEAAAASUAAAIK1cf1oHBvc3QAAA84AAABDwAAAZ5AAl/0eJxjYGRgYOBiMGCwY2BycfMJYeDLSSzJY5BiYGGAAJA8MpsxJzM9kYEDxgPKsYBpDiBmg4gCACY7BUgAeJxjYGQ7xTiBgZWBgaWQ5RkDA8MvCM0cwxDOeI6BgYmBlZkBKwhIc01hcPjI+FGBHcRdyA4RZgQRAC4HCwEAAHic7dFprsIgAEXhg8U61XmeWcBb1FuQP4w7ZQXK5boMm3yclFDSANAHmuKviBBeBPQ8ymyo8w3jOh/5r2ui5nN6v8sYNJb3WMdeWRvLji0DhozKdxM6psyYs2DJijUbtuzYc+DIiTMXrty4k8oGLb+n0xCe37ekM7Z66j1DbUy3l6PpHnLfdLO5NdSBoQ4NdWSoY9ON54mhdqa/y1NDnRnq3FAXhro01JWhrg11Y6hbQ90Z6t5QD4Z6NNSToZ4N9WKoV0O9GerdUJORPqkhTd54nJ1YDXBU1RV+576/JBs2bPYPkrDZt5vsJrv53V/I5mclhGDCTwgGBQQSTEji4hCkYIAGd4TGIWFAhV0RQTpWmQp1xv6hA4OTOlNr2zFANbHUYbq2OtNCpViRqsk+e+7bTQAhzti8vPfuPffcc88959zznbcMMPjHD/KDDGEY0ABpYX384NhlomIYlo4JISGEY9mMh2FSidYiqkEUphtNYDSY/dXg9023l4DdxlqUl0chuZRhncJKrsCQHIwcGuwfnhMIzBnuH4Sym+1D2zaGjheXlhYfD238z80mKYMmvJ5XeOTzd8z9eujbMxJNhu4C9xPE/bCMiDuSNIWgkTQwBE55hLSAE7ZwhrHLnAHZOGV/kmBGTiNjZxzI77Hb7Hqjz68TjT6vh+5JT/cCIkqS0D6CqPf5jX4Qjdx5j6vlDfZM4aZFdbVXIxtOlJaP/WottMnH6CJQ3bTiue3PrY23HjnChtuamxwvvzFjxkPrNj3z0tG9T561HDYf6OgmRWvlY3JQHoQb8ltV2Yet7YfWctEjR1AtxS/cSX6U4alf6NJEBQ7YKg9wrXQKd0IeZCb2ux75Uhh1Un+Nz+9LTOE7PK777nN5xqdTneTBhCbx446mZrhnUkrCz2YhA9dSMxaG0SYmT8hi9ZPu1E94PJYQSH6LRmhxec7Q7ZeXntgQuVpbh+a4qWNsckVyTdn0P7o7DpgPW84+uRcq0BITflBikGdUjAZ9wYBVI3mtrNvr9kpg1UsaK6t3690aoorC1lg0GpMH2HAMtkZjsSi5Ig9ESVosOh7GQfLjKNLvKpMKkLSKNFAka710GdgSi8oDMSoNhqjkKBXTgn3swtaxyzGkUzIzae9RtLdWkSlZ1KDX6EzgllzV4NV4SoDFSOGD4+HCeQUF8wrZ5Hs8zIb5EaVxy8DYFTbMCJPnLIWZxugZE2NlivC0gc1qEQUR8jEKgZcAXeH18BiCgl5nlHh0CrjB4Hb5fX4gb0J7c9PuHVsfgkx2n/vTY/JV8kn8PGxf7faOZ8qX8JVByuIf4whk9sqXli2hvPJV9hrp0hY7l8r2x37ydaVsb4xvXv/47v2NjfCl8m5oRDJclFMoE1yk0Uh1Te4/m8lFXe9qBZD0EkheicebXvzI2PLCuoKCukLuhPIeKwaHPEouxw3kMqaIUXDQ1p0mip+MyCORSCQaoUsnY1VZ38nUTrG21WvVo4f1OsEJFhvSfAFwGfT8VHRMeAVUpwLOoLzjT/REIj3O3FhuURE+nERF+0pTId5Fyxv5sfwGyg4O+my4vZv0sZm7oeQlFZORiB+tG0MweVNraeitl7yxiPIHTk4/diVxs94o5lEYishB2iAtkchEnsActoEpx44Fo8XnsQMaA22BlqC20RmhBKzYojZyYaxg+JggMc4HHY2m+L9EkWSYljirOisrO7d3VorxzyZ6Vc4lJqITAu1b2wOBdrLElAP+bFc2eGaZFVbkmJktv5uT6Jlz5D/MnBFor6ig/JPnRViBsV3LNKGGqB1ChJ0tgQywlVLFJIuQgTFttwkiKxhyQdAZMdMYtSaoAewqfvXVYPAbDT6/1mez85YS8FSDywQ6NfAnef6FNEGMilnppyvn5rB6tTyq1pOceRWnp2WJEZFXHeX5oyoem1nTTgdqc4heDY7bOeKz63vnz+/dRx+s31Ht2JGanQ5seirfWJL9tjozU/12TnEjn5oux9OzU3ckGbBzBwNOyk69JykKH0n/0LM9A72tuwM3zQpIRu4AxiToseEpgPOmbROyFe9/X2yeUvoUsCyEvjcgs7fpWP3/aKlFN0+6HFUe6D9HFz/XPwBlN9tTqNyZjFJ8UO2RUT5/h4CptCctEyeisnOyXjALEp7dXKaQKf6O7IMnGjNNACRMLxqdYJX8eMLvmmd68D+ayBLyKKYZwYxDt/GNhzETDJ05Qxlyi3pi3/Z93ndYVSumgj0V/KkIFlO6+1K3fF2+3g0q+YtuSIf0bvmLqV09nnobI6hwcjIP8aPCKayjsF5JBY3LaKAeRLSyYB1h81oTwe9SlPMkXB7G0mfL9q71gaqqwPqu67QRKS1+ObTx+sbQy9QV2OQHEScGkdFBeT7v7qisqqrs6N52i78/R+6S0qQONVj26agOVoswCyQWIV5D86vH53bxNUeXV0K+XZaHv/nm/KsHhOvylwsWnJX/HE8l/4WCv5x+l5n08z6UU8bUMa3MBpSmM7F63AxntdC9eBCKEZW9Hr+ABNqtxgAQrSbMtmrW7lKQuoSgBhSrTazWVU2QAKWY8wiiuhqFmQgWJBgoXiuWIm42N7hqZbBsgXz52O5P5uSvaNgFGnOuvsRw8I8Laha91wMvDuxqWFheN7/8GVtTltdS83DQsXRmqc5ZtcJXEVrlV2doTWk5+Yunm71dG5f55m/qY0MjI93vv9/NfpxXV9sUXrxy2fbNy1or65cOlDRnOoKFeeXcbw42H/bNDT5Qs3flgs31gWC1lD1nfUV/X7NdCnSUdHY2e8afzfKsqZ5ZljfDqjLOmk3UebNXB+aHArPYDRs+/HDDxeT5DiP+sFg7OpRaVQMGBV89PpeBdj22hCE0Uub0UqwLrNWsG0cuyadgLXTeR5rbO4+3c/vl15cur2nRq+TXCQDcS3SO+s6ak+e5/eMS+1dw3btu3YG2tvFL8XdIZvdjdW6TO/4B7IdrZWVPmctm5/59AgsPItTSbCiIBr2OqIGzmu20SMKAS7yqwGBUfGfgjDYlLLDeF0SfcLB2LSx8flT+08/kzz6yOj96rft4rpTjdPQcmLd47uKibbDq7ZSz/XtbH2nN717Nd62rU+c8Icevvv7I09wA6WvjVcafb+FsbNG+ZQ80Rn6ZZsvrP7teP2dzTdoETvNhjCmsr8FID2sJ69VYvdUcxk4AzYRlKcaE38eXNRlfW9H1as9i6acLHp1XpuNB5K7DIvkX08y1ZYvh3KfWaiCzH+ztrSDmD7LuX73x/mJelB8Yj39t8nhNQJJ2CAthpoFGLsGgtSOCJooCGoaJAMTjSWHVZ08YAa1Fg9lPI5U6DOsGVjDasJeZZ+YyhfCwfOzCxlBA69M9XLXtza7H/rav+9Tjq5xNi0wpKQIRNO4Lrzz7yp5QVYM6Jd/oc1Uvn/mQhhuWh6ENXoS2YTZ8QT42bF5d/559zp5r0Uff2VnR2tdf2/WCOd2cO0Mw6qpWPnvxpV0nrt5fZd2yItc199GWe8vlNfNDq+CH/7yAAnB9hn7T4QO4c1g9ScxsZgmzntnE/IDGndtHMw69lFwoCnYsMGx+rBp8JSBqdLzBr9QRPq/PbhWMWFtQZp1xguy/haw3TEHm3TWAnxFWQQWgt7M5OV0lCz1VRYucpWliy7z6Zd4urwPIyeZQqli2Lgg7szJV09PysATbOQtYIrB2YzbkJYkGgJ0m4AjPUap1pvYu1K9qr97z0Yl3p332b2LYB78ncYIlRkau/8GObSsOlZancACE5d5ily+c2+7h5Yj4lqhVmXXB+iXLfvdqSgqfKtQvfHDV0OnvQR1qhw42XS/vkvsh/hXcrDFP0a+SJNIomEfD1nsrYGO+1bgTOJhM8Hv6ek+7vVglxuSRwoKn17S937bm6YJCeSSG0Op1n+7tE37tcZ/p7dsTv4EUrGpDbWueKigsLHhqTVsoEj+JU0kaSjnj9tz8/gryQWwJ9BcJXBC/7smO+I/IFURJetFPrdt5WcoL6DbEJaygI8CTHfQTjf40ofD+DwalTqIAAHicY2BkYGAA4gDud4bx/DZfGbjZGUDg+q1z05BpdkawOAcDE4gCAB45CXEAeJxjYGRgYGcAARD5/z87IwMjAypQBAAtgwI4AHicY2BgYGAfYAwAOkQA4QAAAAAAAA4AaAB+AMwA4AECAUIBbAGYAcICGAJYArQC4AMwA7AD3gQwBJYE3AUkBWYFigYgBmYGtAbqB1gIEghYCG4IhHicY2BkYGBQZChlYGcAASYg5gJCBob/YD4DABfTAbQAeJxdkE1qg0AYhl8Tk9AIoVDaVSmzahcF87PMARLIMoFAl0ZHY1BHdBJIT9AT9AQ9RQ9Qeqy+yteNMzDzfM+88w0K4BY/cNAMB6N2bUaPPBLukybCLvleeAAPj8JD+hfhMV7hC3u4wxs7OO4NzQSZcI/8Ltwnfwi75E/hAR7wJTyk/xYeY49fYQ/PztM+jbTZ7LY6OWdBJdX/pqs6NYWa+zMxa13oKrA6Uoerqi/JwtpYxZXJ1coUVmeZUWVlTjq0/tHacjmdxuL90OR8O0UEDYMNdtiSEpz5XQGqzlm30kzUdAYFFOb8R7NOZk0q2lwAyz1i7oAr1xoXvrOgtYhZx8wY5KRV269JZ5yGpmzPTjQhvY9je6vEElPOuJP3mWKnP5M3V+YAAAB4nG2PyXLCMBBE3YCNDWEL2ffk7o8S8oCnkCVHC5C/jzBQlUP6IHVPzYyekl5y0iL5X5/ooY8BUmQYIkeBEca4wgRTzDDHAtdY4ga3uMM9HvCIJzzjBa94wzs+8ImvZNAq8TM+HqVkKxWlrQiOxjujQkNlEzyNzl6Z/cU2XF06at7U83VQyklLpEvSnuzsb+HAPnPfQVgaupa1Jlu4sPLsFblcitaz0dHU0ZF1qatjZ1+aTXYCmp6u0gSvWNPyHLtFZ+ZeXWVSaEkqs3T8S74WklbGbNNNq4LL4+CWKtZDv2cfX8l8aFbKFhEnJnJ+IULFpqwoQnNHlHaVQtPBl+ypmbSWdmyC61KS/AKZC3Y+AA==) format("woff");
+ font-weight: normal;
+ font-style: normal; }
+
+.vjs-icon-play, .video-js .vjs-big-play-button .vjs-icon-placeholder:before, .video-js .vjs-play-control .vjs-icon-placeholder {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-play:before, .video-js .vjs-big-play-button .vjs-icon-placeholder:before, .video-js .vjs-play-control .vjs-icon-placeholder:before {
+ content: "\f101"; }
+
+.vjs-icon-play-circle {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-play-circle:before {
+ content: "\f102"; }
+
+.vjs-icon-pause, .video-js .vjs-play-control.vjs-playing .vjs-icon-placeholder {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-pause:before, .video-js .vjs-play-control.vjs-playing .vjs-icon-placeholder:before {
+ content: "\f103"; }
+
+.vjs-icon-volume-mute, .video-js .vjs-mute-control.vjs-vol-0 .vjs-icon-placeholder {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-volume-mute:before, .video-js .vjs-mute-control.vjs-vol-0 .vjs-icon-placeholder:before {
+ content: "\f104"; }
+
+.vjs-icon-volume-low, .video-js .vjs-mute-control.vjs-vol-1 .vjs-icon-placeholder {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-volume-low:before, .video-js .vjs-mute-control.vjs-vol-1 .vjs-icon-placeholder:before {
+ content: "\f105"; }
+
+.vjs-icon-volume-mid, .video-js .vjs-mute-control.vjs-vol-2 .vjs-icon-placeholder {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-volume-mid:before, .video-js .vjs-mute-control.vjs-vol-2 .vjs-icon-placeholder:before {
+ content: "\f106"; }
+
+.vjs-icon-volume-high, .video-js .vjs-mute-control .vjs-icon-placeholder {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-volume-high:before, .video-js .vjs-mute-control .vjs-icon-placeholder:before {
+ content: "\f107"; }
+
+.vjs-icon-fullscreen-enter, .video-js .vjs-fullscreen-control .vjs-icon-placeholder {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-fullscreen-enter:before, .video-js .vjs-fullscreen-control .vjs-icon-placeholder:before {
+ content: "\f108"; }
+
+.vjs-icon-fullscreen-exit, .video-js.vjs-fullscreen .vjs-fullscreen-control .vjs-icon-placeholder {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-fullscreen-exit:before, .video-js.vjs-fullscreen .vjs-fullscreen-control .vjs-icon-placeholder:before {
+ content: "\f109"; }
+
+.vjs-icon-square {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-square:before {
+ content: "\f10a"; }
+
+.vjs-icon-spinner {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-spinner:before {
+ content: "\f10b"; }
+
+.vjs-icon-subtitles, .video-js .vjs-subtitles-button .vjs-icon-placeholder, .video-js .vjs-subs-caps-button .vjs-icon-placeholder,
+.video-js.video-js:lang(en-GB) .vjs-subs-caps-button .vjs-icon-placeholder,
+.video-js.video-js:lang(en-IE) .vjs-subs-caps-button .vjs-icon-placeholder,
+.video-js.video-js:lang(en-AU) .vjs-subs-caps-button .vjs-icon-placeholder,
+.video-js.video-js:lang(en-NZ) .vjs-subs-caps-button .vjs-icon-placeholder {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-subtitles:before, .video-js .vjs-subtitles-button .vjs-icon-placeholder:before, .video-js .vjs-subs-caps-button .vjs-icon-placeholder:before,
+ .video-js.video-js:lang(en-GB) .vjs-subs-caps-button .vjs-icon-placeholder:before,
+ .video-js.video-js:lang(en-IE) .vjs-subs-caps-button .vjs-icon-placeholder:before,
+ .video-js.video-js:lang(en-AU) .vjs-subs-caps-button .vjs-icon-placeholder:before,
+ .video-js.video-js:lang(en-NZ) .vjs-subs-caps-button .vjs-icon-placeholder:before {
+ content: "\f10c"; }
+
+.vjs-icon-captions, .video-js .vjs-captions-button .vjs-icon-placeholder, .video-js:lang(en) .vjs-subs-caps-button .vjs-icon-placeholder,
+.video-js:lang(fr-CA) .vjs-subs-caps-button .vjs-icon-placeholder {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-captions:before, .video-js .vjs-captions-button .vjs-icon-placeholder:before, .video-js:lang(en) .vjs-subs-caps-button .vjs-icon-placeholder:before,
+ .video-js:lang(fr-CA) .vjs-subs-caps-button .vjs-icon-placeholder:before {
+ content: "\f10d"; }
+
+.vjs-icon-chapters, .video-js .vjs-chapters-button .vjs-icon-placeholder {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-chapters:before, .video-js .vjs-chapters-button .vjs-icon-placeholder:before {
+ content: "\f10e"; }
+
+.vjs-icon-share {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-share:before {
+ content: "\f10f"; }
+
+.vjs-icon-cog {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-cog:before {
+ content: "\f110"; }
+
+.vjs-icon-circle, .video-js .vjs-play-progress, .video-js .vjs-volume-level, .vjs-seek-to-live-control .vjs-icon-placeholder {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-circle:before, .video-js .vjs-play-progress:before, .video-js .vjs-volume-level:before, .vjs-seek-to-live-control .vjs-icon-placeholder:before {
+ content: "\f111"; }
+
+.vjs-icon-circle-outline {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-circle-outline:before {
+ content: "\f112"; }
+
+.vjs-icon-circle-inner-circle {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-circle-inner-circle:before {
+ content: "\f113"; }
+
+.vjs-icon-hd {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-hd:before {
+ content: "\f114"; }
+
+.vjs-icon-cancel, .video-js .vjs-control.vjs-close-button .vjs-icon-placeholder {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-cancel:before, .video-js .vjs-control.vjs-close-button .vjs-icon-placeholder:before {
+ content: "\f115"; }
+
+.vjs-icon-replay, .video-js .vjs-play-control.vjs-ended .vjs-icon-placeholder {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-replay:before, .video-js .vjs-play-control.vjs-ended .vjs-icon-placeholder:before {
+ content: "\f116"; }
+
+.vjs-icon-facebook {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-facebook:before {
+ content: "\f117"; }
+
+.vjs-icon-gplus {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-gplus:before {
+ content: "\f118"; }
+
+.vjs-icon-linkedin {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-linkedin:before {
+ content: "\f119"; }
+
+.vjs-icon-twitter {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-twitter:before {
+ content: "\f11a"; }
+
+.vjs-icon-tumblr {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-tumblr:before {
+ content: "\f11b"; }
+
+.vjs-icon-pinterest {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-pinterest:before {
+ content: "\f11c"; }
+
+.vjs-icon-audio-description, .video-js .vjs-descriptions-button .vjs-icon-placeholder {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-audio-description:before, .video-js .vjs-descriptions-button .vjs-icon-placeholder:before {
+ content: "\f11d"; }
+
+.vjs-icon-audio, .video-js .vjs-audio-button .vjs-icon-placeholder {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-audio:before, .video-js .vjs-audio-button .vjs-icon-placeholder:before {
+ content: "\f11e"; }
+
+.vjs-icon-next-item {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-next-item:before {
+ content: "\f11f"; }
+
+.vjs-icon-previous-item {
+ font-family: VideoJS;
+ font-weight: normal;
+ font-style: normal; }
+ .vjs-icon-previous-item:before {
+ content: "\f120"; }
+
+.video-js {
+ display: block;
+ vertical-align: top;
+ box-sizing: border-box;
+ color: #fff;
+ background-color: #000;
+ position: relative;
+ padding: 0;
+ font-size: 10px;
+ line-height: 1;
+ font-weight: normal;
+ font-style: normal;
+ font-family: Arial, Helvetica, sans-serif;
+ word-break: initial; }
+ .video-js:-moz-full-screen {
+ position: absolute; }
+ .video-js:-webkit-full-screen {
+ width: 100% !important;
+ height: 100% !important; }
+
+.video-js[tabindex="-1"] {
+ outline: none; }
+
+.video-js *,
+.video-js *:before,
+.video-js *:after {
+ box-sizing: inherit; }
+
+.video-js ul {
+ font-family: inherit;
+ font-size: inherit;
+ line-height: inherit;
+ list-style-position: outside;
+ margin-left: 0;
+ margin-right: 0;
+ margin-top: 0;
+ margin-bottom: 0; }
+
+.video-js.vjs-fluid,
+.video-js.vjs-16-9,
+.video-js.vjs-4-3 {
+ width: 100%;
+ max-width: 100%;
+ height: 0; }
+
+.video-js.vjs-16-9 {
+ padding-top: 56.25%; }
+
+.video-js.vjs-4-3 {
+ padding-top: 75%; }
+
+.video-js.vjs-fill {
+ width: 100%;
+ height: 100%; }
+
+.video-js .vjs-tech {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%; }
+
+body.vjs-full-window {
+ padding: 0;
+ margin: 0;
+ height: 100%; }
+
+.vjs-full-window .video-js.vjs-fullscreen {
+ position: fixed;
+ overflow: hidden;
+ z-index: 1000;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ right: 0; }
+
+.video-js.vjs-fullscreen {
+ width: 100% !important;
+ height: 100% !important;
+ padding-top: 0 !important; }
+
+.video-js.vjs-fullscreen.vjs-user-inactive {
+ cursor: none; }
+
+.vjs-hidden {
+ display: none !important; }
+
+.vjs-disabled {
+ opacity: 0.5;
+ cursor: default; }
+
+.video-js .vjs-offscreen {
+ height: 1px;
+ left: -9999px;
+ position: absolute;
+ top: 0;
+ width: 1px; }
+
+.vjs-lock-showing {
+ display: block !important;
+ opacity: 1;
+ visibility: visible; }
+
+.vjs-no-js {
+ padding: 20px;
+ color: #fff;
+ background-color: #000;
+ font-size: 18px;
+ font-family: Arial, Helvetica, sans-serif;
+ text-align: center;
+ width: 300px;
+ height: 150px;
+ margin: 0px auto; }
+
+.vjs-no-js a,
+.vjs-no-js a:visited {
+ color: #66A8CC; }
+
+.video-js .vjs-big-play-button {
+ font-size: 3em;
+ line-height: 1.5em;
+ height: 1.63332em;
+ width: 3em;
+ display: block;
+ position: absolute;
+ top: 10px;
+ left: 10px;
+ padding: 0;
+ cursor: pointer;
+ opacity: 1;
+ border: 0.06666em solid #fff;
+ background-color: #2B333F;
+ background-color: rgba(43, 51, 63, 0.7);
+ border-radius: 0.3em;
+ transition: all 0.4s; }
+
+.vjs-big-play-centered .vjs-big-play-button {
+ top: 50%;
+ left: 50%;
+ margin-top: -0.81666em;
+ margin-left: -1.5em; }
+
+.video-js:hover .vjs-big-play-button,
+.video-js .vjs-big-play-button:focus {
+ border-color: #fff;
+ background-color: #73859f;
+ background-color: rgba(115, 133, 159, 0.5);
+ transition: all 0s; }
+
+.vjs-controls-disabled .vjs-big-play-button,
+.vjs-has-started .vjs-big-play-button,
+.vjs-using-native-controls .vjs-big-play-button,
+.vjs-error .vjs-big-play-button {
+ display: none; }
+
+.vjs-has-started.vjs-paused.vjs-show-big-play-button-on-pause .vjs-big-play-button {
+ display: block; }
+
+.video-js button {
+ background: none;
+ border: none;
+ color: inherit;
+ display: inline-block;
+ font-size: inherit;
+ line-height: inherit;
+ text-transform: none;
+ text-decoration: none;
+ transition: none;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none; }
+
+.vjs-control .vjs-button {
+ width: 100%;
+ height: 100%; }
+
+.video-js .vjs-control.vjs-close-button {
+ cursor: pointer;
+ height: 3em;
+ position: absolute;
+ right: 0;
+ top: 0.5em;
+ z-index: 2; }
+
+.video-js .vjs-modal-dialog {
+ background: rgba(0, 0, 0, 0.8);
+ background: linear-gradient(180deg, rgba(0, 0, 0, 0.8), rgba(255, 255, 255, 0));
+ overflow: auto; }
+
+.video-js .vjs-modal-dialog > * {
+ box-sizing: border-box; }
+
+.vjs-modal-dialog .vjs-modal-dialog-content {
+ font-size: 1.2em;
+ line-height: 1.5;
+ padding: 20px 24px;
+ z-index: 1; }
+
+.vjs-menu-button {
+ cursor: pointer; }
+
+.vjs-menu-button.vjs-disabled {
+ cursor: default; }
+
+.vjs-workinghover .vjs-menu-button.vjs-disabled:hover .vjs-menu {
+ display: none; }
+
+.vjs-menu .vjs-menu-content {
+ display: block;
+ padding: 0;
+ margin: 0;
+ font-family: Arial, Helvetica, sans-serif;
+ overflow: auto; }
+
+.vjs-menu .vjs-menu-content > * {
+ box-sizing: border-box; }
+
+.vjs-scrubbing .vjs-control.vjs-menu-button:hover .vjs-menu {
+ display: none; }
+
+.vjs-menu li {
+ list-style: none;
+ margin: 0;
+ padding: 0.2em 0;
+ line-height: 1.4em;
+ font-size: 1.2em;
+ text-align: center;
+ text-transform: lowercase; }
+
+.vjs-menu li.vjs-menu-item:focus,
+.vjs-menu li.vjs-menu-item:hover,
+.js-focus-visible .vjs-menu li.vjs-menu-item:hover {
+ background-color: #73859f;
+ background-color: rgba(115, 133, 159, 0.5); }
+
+.vjs-menu li.vjs-selected,
+.vjs-menu li.vjs-selected:focus,
+.vjs-menu li.vjs-selected:hover,
+.js-focus-visible .vjs-menu li.vjs-selected:hover {
+ background-color: #fff;
+ color: #2B333F; }
+
+.vjs-menu li.vjs-menu-title {
+ text-align: center;
+ text-transform: uppercase;
+ font-size: 1em;
+ line-height: 2em;
+ padding: 0;
+ margin: 0 0 0.3em 0;
+ font-weight: bold;
+ cursor: default; }
+
+.vjs-menu-button-popup .vjs-menu {
+ display: none;
+ position: absolute;
+ bottom: 0;
+ width: 10em;
+ left: -3em;
+ height: 0em;
+ margin-bottom: 1.5em;
+ border-top-color: rgba(43, 51, 63, 0.7); }
+
+.vjs-menu-button-popup .vjs-menu .vjs-menu-content {
+ background-color: #2B333F;
+ background-color: rgba(43, 51, 63, 0.7);
+ position: absolute;
+ width: 100%;
+ bottom: 1.5em;
+ max-height: 15em; }
+
+.vjs-layout-tiny .vjs-menu-button-popup .vjs-menu .vjs-menu-content,
+.vjs-layout-x-small .vjs-menu-button-popup .vjs-menu .vjs-menu-content {
+ max-height: 5em; }
+
+.vjs-layout-small .vjs-menu-button-popup .vjs-menu .vjs-menu-content {
+ max-height: 10em; }
+
+.vjs-layout-medium .vjs-menu-button-popup .vjs-menu .vjs-menu-content {
+ max-height: 14em; }
+
+.vjs-layout-large .vjs-menu-button-popup .vjs-menu .vjs-menu-content,
+.vjs-layout-x-large .vjs-menu-button-popup .vjs-menu .vjs-menu-content,
+.vjs-layout-huge .vjs-menu-button-popup .vjs-menu .vjs-menu-content {
+ max-height: 25em; }
+
+.vjs-workinghover .vjs-menu-button-popup:hover .vjs-menu,
+.vjs-menu-button-popup .vjs-menu.vjs-lock-showing {
+ display: block; }
+
+.video-js .vjs-menu-button-inline {
+ transition: all 0.4s;
+ overflow: hidden; }
+
+.video-js .vjs-menu-button-inline:before {
+ width: 2.222222222em; }
+
+.video-js .vjs-menu-button-inline:hover,
+.video-js .vjs-menu-button-inline:focus,
+.video-js .vjs-menu-button-inline.vjs-slider-active,
+.video-js.vjs-no-flex .vjs-menu-button-inline {
+ width: 12em; }
+
+.vjs-menu-button-inline .vjs-menu {
+ opacity: 0;
+ height: 100%;
+ width: auto;
+ position: absolute;
+ left: 4em;
+ top: 0;
+ padding: 0;
+ margin: 0;
+ transition: all 0.4s; }
+
+.vjs-menu-button-inline:hover .vjs-menu,
+.vjs-menu-button-inline:focus .vjs-menu,
+.vjs-menu-button-inline.vjs-slider-active .vjs-menu {
+ display: block;
+ opacity: 1; }
+
+.vjs-no-flex .vjs-menu-button-inline .vjs-menu {
+ display: block;
+ opacity: 1;
+ position: relative;
+ width: auto; }
+
+.vjs-no-flex .vjs-menu-button-inline:hover .vjs-menu,
+.vjs-no-flex .vjs-menu-button-inline:focus .vjs-menu,
+.vjs-no-flex .vjs-menu-button-inline.vjs-slider-active .vjs-menu {
+ width: auto; }
+
+.vjs-menu-button-inline .vjs-menu-content {
+ width: auto;
+ height: 100%;
+ margin: 0;
+ overflow: hidden; }
+
+.video-js .vjs-control-bar {
+ display: none;
+ width: 100%;
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ height: 3.0em;
+ background-color: #2B333F;
+ background-color: rgba(43, 51, 63, 0.7); }
+
+.vjs-has-started .vjs-control-bar {
+ display: flex;
+ visibility: visible;
+ opacity: 1;
+ transition: visibility 0.1s, opacity 0.1s; }
+
+.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar {
+ visibility: visible;
+ opacity: 0;
+ transition: visibility 1s, opacity 1s; }
+
+.vjs-controls-disabled .vjs-control-bar,
+.vjs-using-native-controls .vjs-control-bar,
+.vjs-error .vjs-control-bar {
+ display: none !important; }
+
+.vjs-audio.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar {
+ opacity: 1;
+ visibility: visible; }
+
+.vjs-has-started.vjs-no-flex .vjs-control-bar {
+ display: table; }
+
+.video-js .vjs-control {
+ position: relative;
+ text-align: center;
+ margin: 0;
+ padding: 0;
+ height: 100%;
+ width: 4em;
+ flex: none; }
+
+.vjs-button > .vjs-icon-placeholder:before {
+ font-size: 1.8em;
+ line-height: 1.67; }
+
+.video-js .vjs-control:focus:before,
+.video-js .vjs-control:hover:before,
+.video-js .vjs-control:focus {
+ text-shadow: 0em 0em 1em white; }
+
+.video-js .vjs-control-text {
+ border: 0;
+ clip: rect(0 0 0 0);
+ height: 1px;
+ overflow: hidden;
+ padding: 0;
+ position: absolute;
+ width: 1px; }
+
+.vjs-no-flex .vjs-control {
+ display: table-cell;
+ vertical-align: middle; }
+
+.video-js .vjs-custom-control-spacer {
+ display: none; }
+
+.video-js .vjs-progress-control {
+ cursor: pointer;
+ flex: auto;
+ display: flex;
+ align-items: center;
+ min-width: 4em;
+ touch-action: none; }
+
+.video-js .vjs-progress-control.disabled {
+ cursor: default; }
+
+.vjs-live .vjs-progress-control {
+ display: none; }
+
+.vjs-liveui .vjs-progress-control {
+ display: flex;
+ align-items: center; }
+
+.vjs-no-flex .vjs-progress-control {
+ width: auto; }
+
+.video-js .vjs-progress-holder {
+ flex: auto;
+ transition: all 0.2s;
+ height: 0.3em; }
+
+.video-js .vjs-progress-control .vjs-progress-holder {
+ margin: 0 10px; }
+
+.video-js .vjs-progress-control:hover .vjs-progress-holder {
+ font-size: 1.666666666666666666em; }
+
+.video-js .vjs-progress-control:hover .vjs-progress-holder.disabled {
+ font-size: 1em; }
+
+.video-js .vjs-progress-holder .vjs-play-progress,
+.video-js .vjs-progress-holder .vjs-load-progress,
+.video-js .vjs-progress-holder .vjs-load-progress div {
+ position: absolute;
+ display: block;
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ width: 0; }
+
+.video-js .vjs-play-progress {
+ background-color: #fff; }
+ .video-js .vjs-play-progress:before {
+ font-size: 0.9em;
+ position: absolute;
+ right: -0.5em;
+ top: -0.333333333333333em;
+ z-index: 1; }
+
+.video-js .vjs-load-progress {
+ background: rgba(115, 133, 159, 0.5); }
+
+.video-js .vjs-load-progress div {
+ background: rgba(115, 133, 159, 0.75); }
+
+.video-js .vjs-time-tooltip {
+ background-color: #fff;
+ background-color: rgba(255, 255, 255, 0.8);
+ border-radius: 0.3em;
+ color: #000;
+ float: right;
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 1em;
+ padding: 6px 8px 8px 8px;
+ pointer-events: none;
+ position: absolute;
+ top: -3.4em;
+ visibility: hidden;
+ z-index: 1; }
+
+.video-js .vjs-progress-holder:focus .vjs-time-tooltip {
+ display: none; }
+
+.video-js .vjs-progress-control:hover .vjs-time-tooltip,
+.video-js .vjs-progress-control:hover .vjs-progress-holder:focus .vjs-time-tooltip {
+ display: block;
+ font-size: 0.6em;
+ visibility: visible; }
+
+.video-js .vjs-progress-control.disabled:hover .vjs-time-tooltip {
+ font-size: 1em; }
+
+.video-js .vjs-progress-control .vjs-mouse-display {
+ display: none;
+ position: absolute;
+ width: 1px;
+ height: 100%;
+ background-color: #000;
+ z-index: 1; }
+
+.vjs-no-flex .vjs-progress-control .vjs-mouse-display {
+ z-index: 0; }
+
+.video-js .vjs-progress-control:hover .vjs-mouse-display {
+ display: block; }
+
+.video-js.vjs-user-inactive .vjs-progress-control .vjs-mouse-display {
+ visibility: hidden;
+ opacity: 0;
+ transition: visibility 1s, opacity 1s; }
+
+.video-js.vjs-user-inactive.vjs-no-flex .vjs-progress-control .vjs-mouse-display {
+ display: none; }
+
+.vjs-mouse-display .vjs-time-tooltip {
+ color: #fff;
+ background-color: #000;
+ background-color: rgba(0, 0, 0, 0.8); }
+
+.video-js .vjs-slider {
+ position: relative;
+ cursor: pointer;
+ padding: 0;
+ margin: 0 0.45em 0 0.45em;
+ /* iOS Safari */
+ -webkit-touch-callout: none;
+ /* Safari */
+ -webkit-user-select: none;
+ /* Konqueror HTML */
+ /* Firefox */
+ -moz-user-select: none;
+ /* Internet Explorer/Edge */
+ -ms-user-select: none;
+ /* Non-prefixed version, currently supported by Chrome and Opera */
+ user-select: none;
+ background-color: #73859f;
+ background-color: rgba(115, 133, 159, 0.5); }
+
+.video-js .vjs-slider.disabled {
+ cursor: default; }
+
+.video-js .vjs-slider:focus {
+ text-shadow: 0em 0em 1em white;
+ box-shadow: 0 0 1em #fff; }
+
+.video-js .vjs-mute-control {
+ cursor: pointer;
+ flex: none; }
+
+.video-js .vjs-volume-control {
+ cursor: pointer;
+ margin-right: 1em;
+ display: flex; }
+
+.video-js .vjs-volume-control.vjs-volume-horizontal {
+ width: 5em; }
+
+.video-js .vjs-volume-panel .vjs-volume-control {
+ visibility: visible;
+ opacity: 0;
+ width: 1px;
+ height: 1px;
+ margin-left: -1px; }
+
+.video-js .vjs-volume-panel {
+ transition: width 1s; }
+ .video-js .vjs-volume-panel:hover .vjs-volume-control,
+ .video-js .vjs-volume-panel:active .vjs-volume-control,
+ .video-js .vjs-volume-panel:focus .vjs-volume-control,
+ .video-js .vjs-volume-panel .vjs-volume-control:hover,
+ .video-js .vjs-volume-panel .vjs-volume-control:active,
+ .video-js .vjs-volume-panel .vjs-mute-control:hover ~ .vjs-volume-control,
+ .video-js .vjs-volume-panel .vjs-volume-control.vjs-slider-active {
+ visibility: visible;
+ opacity: 1;
+ position: relative;
+ transition: visibility 0.1s, opacity 0.1s, height 0.1s, width 0.1s, left 0s, top 0s; }
+ .video-js .vjs-volume-panel:hover .vjs-volume-control.vjs-volume-horizontal,
+ .video-js .vjs-volume-panel:active .vjs-volume-control.vjs-volume-horizontal,
+ .video-js .vjs-volume-panel:focus .vjs-volume-control.vjs-volume-horizontal,
+ .video-js .vjs-volume-panel .vjs-volume-control:hover.vjs-volume-horizontal,
+ .video-js .vjs-volume-panel .vjs-volume-control:active.vjs-volume-horizontal,
+ .video-js .vjs-volume-panel .vjs-mute-control:hover ~ .vjs-volume-control.vjs-volume-horizontal,
+ .video-js .vjs-volume-panel .vjs-volume-control.vjs-slider-active.vjs-volume-horizontal {
+ width: 5em;
+ height: 3em; }
+ .video-js .vjs-volume-panel:hover .vjs-volume-control.vjs-volume-vertical,
+ .video-js .vjs-volume-panel:active .vjs-volume-control.vjs-volume-vertical,
+ .video-js .vjs-volume-panel:focus .vjs-volume-control.vjs-volume-vertical,
+ .video-js .vjs-volume-panel .vjs-volume-control:hover.vjs-volume-vertical,
+ .video-js .vjs-volume-panel .vjs-volume-control:active.vjs-volume-vertical,
+ .video-js .vjs-volume-panel .vjs-mute-control:hover ~ .vjs-volume-control.vjs-volume-vertical,
+ .video-js .vjs-volume-panel .vjs-volume-control.vjs-slider-active.vjs-volume-vertical {
+ left: -3.5em; }
+ .video-js .vjs-volume-panel.vjs-volume-panel-horizontal:hover, .video-js .vjs-volume-panel.vjs-volume-panel-horizontal:active, .video-js .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-slider-active {
+ width: 9em;
+ transition: width 0.1s; }
+ .video-js .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-mute-toggle-only {
+ width: 4em; }
+
+.video-js .vjs-volume-panel .vjs-volume-control.vjs-volume-vertical {
+ height: 8em;
+ width: 3em;
+ left: -3000em;
+ transition: visibility 1s, opacity 1s, height 1s 1s, width 1s 1s, left 1s 1s, top 1s 1s; }
+
+.video-js .vjs-volume-panel .vjs-volume-control.vjs-volume-horizontal {
+ transition: visibility 1s, opacity 1s, height 1s 1s, width 1s, left 1s 1s, top 1s 1s; }
+
+.video-js.vjs-no-flex .vjs-volume-panel .vjs-volume-control.vjs-volume-horizontal {
+ width: 5em;
+ height: 3em;
+ visibility: visible;
+ opacity: 1;
+ position: relative;
+ transition: none; }
+
+.video-js.vjs-no-flex .vjs-volume-control.vjs-volume-vertical,
+.video-js.vjs-no-flex .vjs-volume-panel .vjs-volume-control.vjs-volume-vertical {
+ position: absolute;
+ bottom: 3em;
+ left: 0.5em; }
+
+.video-js .vjs-volume-panel {
+ display: flex; }
+
+.video-js .vjs-volume-bar {
+ margin: 1.35em 0.45em; }
+
+.vjs-volume-bar.vjs-slider-horizontal {
+ width: 5em;
+ height: 0.3em; }
+
+.vjs-volume-bar.vjs-slider-vertical {
+ width: 0.3em;
+ height: 5em;
+ margin: 1.35em auto; }
+
+.video-js .vjs-volume-level {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ background-color: #fff; }
+ .video-js .vjs-volume-level:before {
+ position: absolute;
+ font-size: 0.9em; }
+
+.vjs-slider-vertical .vjs-volume-level {
+ width: 0.3em; }
+ .vjs-slider-vertical .vjs-volume-level:before {
+ top: -0.5em;
+ left: -0.3em; }
+
+.vjs-slider-horizontal .vjs-volume-level {
+ height: 0.3em; }
+ .vjs-slider-horizontal .vjs-volume-level:before {
+ top: -0.3em;
+ right: -0.5em; }
+
+.video-js .vjs-volume-panel.vjs-volume-panel-vertical {
+ width: 4em; }
+
+.vjs-volume-bar.vjs-slider-vertical .vjs-volume-level {
+ height: 100%; }
+
+.vjs-volume-bar.vjs-slider-horizontal .vjs-volume-level {
+ width: 100%; }
+
+.video-js .vjs-volume-vertical {
+ width: 3em;
+ height: 8em;
+ bottom: 8em;
+ background-color: #2B333F;
+ background-color: rgba(43, 51, 63, 0.7); }
+
+.video-js .vjs-volume-horizontal .vjs-menu {
+ left: -2em; }
+
+.vjs-poster {
+ display: inline-block;
+ vertical-align: middle;
+ background-repeat: no-repeat;
+ background-position: 50% 50%;
+ background-size: contain;
+ background-color: #000000;
+ cursor: pointer;
+ margin: 0;
+ padding: 0;
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ height: 100%; }
+
+.vjs-has-started .vjs-poster {
+ display: none; }
+
+.vjs-audio.vjs-has-started .vjs-poster {
+ display: block; }
+
+.vjs-using-native-controls .vjs-poster {
+ display: none; }
+
+.video-js .vjs-live-control {
+ display: flex;
+ align-items: flex-start;
+ flex: auto;
+ font-size: 1em;
+ line-height: 3em; }
+
+.vjs-no-flex .vjs-live-control {
+ display: table-cell;
+ width: auto;
+ text-align: left; }
+
+.video-js:not(.vjs-live) .vjs-live-control,
+.video-js.vjs-liveui .vjs-live-control {
+ display: none; }
+
+.video-js .vjs-seek-to-live-control {
+ cursor: pointer;
+ flex: none;
+ display: inline-flex;
+ height: 100%;
+ padding-left: 0.5em;
+ padding-right: 0.5em;
+ font-size: 1em;
+ line-height: 3em;
+ width: auto;
+ min-width: 4em; }
+
+.vjs-no-flex .vjs-seek-to-live-control {
+ display: table-cell;
+ width: auto;
+ text-align: left; }
+
+.video-js.vjs-live:not(.vjs-liveui) .vjs-seek-to-live-control,
+.video-js:not(.vjs-live) .vjs-seek-to-live-control {
+ display: none; }
+
+.vjs-seek-to-live-control.vjs-control.vjs-at-live-edge {
+ cursor: auto; }
+
+.vjs-seek-to-live-control .vjs-icon-placeholder {
+ margin-right: 0.5em;
+ color: #888; }
+
+.vjs-seek-to-live-control.vjs-control.vjs-at-live-edge .vjs-icon-placeholder {
+ color: red; }
+
+.video-js .vjs-time-control {
+ flex: none;
+ font-size: 1em;
+ line-height: 3em;
+ min-width: 2em;
+ width: auto;
+ padding-left: 1em;
+ padding-right: 1em; }
+
+.vjs-live .vjs-time-control {
+ display: none; }
+
+.video-js .vjs-current-time,
+.vjs-no-flex .vjs-current-time {
+ display: none; }
+
+.video-js .vjs-duration,
+.vjs-no-flex .vjs-duration {
+ display: none; }
+
+.vjs-time-divider {
+ display: none;
+ line-height: 3em; }
+
+.vjs-live .vjs-time-divider {
+ display: none; }
+
+.video-js .vjs-play-control {
+ cursor: pointer; }
+
+.video-js .vjs-play-control .vjs-icon-placeholder {
+ flex: none; }
+
+.vjs-text-track-display {
+ position: absolute;
+ bottom: 3em;
+ left: 0;
+ right: 0;
+ top: 0;
+ pointer-events: none; }
+
+.video-js.vjs-user-inactive.vjs-playing .vjs-text-track-display {
+ bottom: 1em; }
+
+.video-js .vjs-text-track {
+ font-size: 1.4em;
+ text-align: center;
+ margin-bottom: 0.1em; }
+
+.vjs-subtitles {
+ color: #fff; }
+
+.vjs-captions {
+ color: #fc6; }
+
+.vjs-tt-cue {
+ display: block; }
+
+video::-webkit-media-text-track-display {
+ -webkit-transform: translateY(-3em);
+ transform: translateY(-3em); }
+
+.video-js.vjs-user-inactive.vjs-playing video::-webkit-media-text-track-display {
+ -webkit-transform: translateY(-1.5em);
+ transform: translateY(-1.5em); }
+
+.video-js .vjs-fullscreen-control {
+ cursor: pointer;
+ flex: none; }
+
+.vjs-playback-rate > .vjs-menu-button,
+.vjs-playback-rate .vjs-playback-rate-value {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%; }
+
+.vjs-playback-rate .vjs-playback-rate-value {
+ pointer-events: none;
+ font-size: 1.5em;
+ line-height: 2;
+ text-align: center; }
+
+.vjs-playback-rate .vjs-menu {
+ width: 4em;
+ left: 0em; }
+
+.vjs-error .vjs-error-display .vjs-modal-dialog-content {
+ font-size: 1.4em;
+ text-align: center; }
+
+.vjs-error .vjs-error-display:before {
+ color: #fff;
+ content: 'X';
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 4em;
+ left: 0;
+ line-height: 1;
+ margin-top: -0.5em;
+ position: absolute;
+ text-shadow: 0.05em 0.05em 0.1em #000;
+ text-align: center;
+ top: 50%;
+ vertical-align: middle;
+ width: 100%; }
+
+.vjs-loading-spinner {
+ display: none;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ margin: -25px 0 0 -25px;
+ opacity: 0.85;
+ text-align: left;
+ border: 6px solid rgba(43, 51, 63, 0.7);
+ box-sizing: border-box;
+ background-clip: padding-box;
+ width: 50px;
+ height: 50px;
+ border-radius: 25px;
+ visibility: hidden; }
+
+.vjs-seeking .vjs-loading-spinner,
+.vjs-waiting .vjs-loading-spinner {
+ display: block;
+ -webkit-animation: vjs-spinner-show 0s linear 0.3s forwards;
+ animation: vjs-spinner-show 0s linear 0.3s forwards; }
+
+.vjs-loading-spinner:before,
+.vjs-loading-spinner:after {
+ content: "";
+ position: absolute;
+ margin: -6px;
+ box-sizing: inherit;
+ width: inherit;
+ height: inherit;
+ border-radius: inherit;
+ opacity: 1;
+ border: inherit;
+ border-color: transparent;
+ border-top-color: white; }
+
+.vjs-seeking .vjs-loading-spinner:before,
+.vjs-seeking .vjs-loading-spinner:after,
+.vjs-waiting .vjs-loading-spinner:before,
+.vjs-waiting .vjs-loading-spinner:after {
+ -webkit-animation: vjs-spinner-spin 1.1s cubic-bezier(0.6, 0.2, 0, 0.8) infinite, vjs-spinner-fade 1.1s linear infinite;
+ animation: vjs-spinner-spin 1.1s cubic-bezier(0.6, 0.2, 0, 0.8) infinite, vjs-spinner-fade 1.1s linear infinite; }
+
+.vjs-seeking .vjs-loading-spinner:before,
+.vjs-waiting .vjs-loading-spinner:before {
+ border-top-color: white; }
+
+.vjs-seeking .vjs-loading-spinner:after,
+.vjs-waiting .vjs-loading-spinner:after {
+ border-top-color: white;
+ -webkit-animation-delay: 0.44s;
+ animation-delay: 0.44s; }
+
+@keyframes vjs-spinner-show {
+ to {
+ visibility: visible; } }
+
+@-webkit-keyframes vjs-spinner-show {
+ to {
+ visibility: visible; } }
+
+@keyframes vjs-spinner-spin {
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg); } }
+
+@-webkit-keyframes vjs-spinner-spin {
+ 100% {
+ -webkit-transform: rotate(360deg); } }
+
+@keyframes vjs-spinner-fade {
+ 0% {
+ border-top-color: #73859f; }
+ 20% {
+ border-top-color: #73859f; }
+ 35% {
+ border-top-color: white; }
+ 60% {
+ border-top-color: #73859f; }
+ 100% {
+ border-top-color: #73859f; } }
+
+@-webkit-keyframes vjs-spinner-fade {
+ 0% {
+ border-top-color: #73859f; }
+ 20% {
+ border-top-color: #73859f; }
+ 35% {
+ border-top-color: white; }
+ 60% {
+ border-top-color: #73859f; }
+ 100% {
+ border-top-color: #73859f; } }
+
+.vjs-chapters-button .vjs-menu ul {
+ width: 24em; }
+
+.video-js .vjs-subs-caps-button + .vjs-menu .vjs-captions-menu-item .vjs-menu-item-text .vjs-icon-placeholder {
+ vertical-align: middle;
+ display: inline-block;
+ margin-bottom: -0.1em; }
+
+.video-js .vjs-subs-caps-button + .vjs-menu .vjs-captions-menu-item .vjs-menu-item-text .vjs-icon-placeholder:before {
+ font-family: VideoJS;
+ content: "\f10d";
+ font-size: 1.5em;
+ line-height: inherit; }
+
+.video-js .vjs-audio-button + .vjs-menu .vjs-main-desc-menu-item .vjs-menu-item-text .vjs-icon-placeholder {
+ vertical-align: middle;
+ display: inline-block;
+ margin-bottom: -0.1em; }
+
+.video-js .vjs-audio-button + .vjs-menu .vjs-main-desc-menu-item .vjs-menu-item-text .vjs-icon-placeholder:before {
+ font-family: VideoJS;
+ content: " \f11d";
+ font-size: 1.5em;
+ line-height: inherit; }
+
+.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-current-time,
+.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-time-divider,
+.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-duration,
+.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-remaining-time,
+.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-playback-rate,
+.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-chapters-button,
+.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-descriptions-button,
+.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-captions-button,
+.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-subtitles-button,
+.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-audio-button,
+.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-volume-control, .video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-current-time,
+.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-time-divider,
+.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-duration,
+.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-remaining-time,
+.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-playback-rate,
+.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-chapters-button,
+.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-descriptions-button,
+.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-captions-button,
+.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-subtitles-button,
+.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-audio-button,
+.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-volume-control, .video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-current-time,
+.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-time-divider,
+.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-duration,
+.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-remaining-time,
+.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-playback-rate,
+.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-chapters-button,
+.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-descriptions-button,
+.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-captions-button,
+.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-subtitles-button,
+.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-audio-button,
+.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-volume-control {
+ display: none; }
+
+.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-volume-panel.vjs-volume-panel-horizontal:hover,
+.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-volume-panel.vjs-volume-panel-horizontal:active,
+.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-slider-active, .video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-volume-panel.vjs-volume-panel-horizontal:hover,
+.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-volume-panel.vjs-volume-panel-horizontal:active,
+.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-slider-active, .video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-volume-panel.vjs-volume-panel-horizontal:hover,
+.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-volume-panel.vjs-volume-panel-horizontal:active,
+.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-slider-active {
+ width: auto;
+ width: initial; }
+
+.video-js:not(.vjs-fullscreen).vjs-layout-x-small:not(.vjs-liveui) .vjs-subs-caps-button, .video-js:not(.vjs-fullscreen).vjs-layout-x-small:not(.vjs-live) .vjs-subs-caps-button, .video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-subs-caps-button {
+ display: none; }
+
+.video-js:not(.vjs-fullscreen).vjs-layout-x-small.vjs-liveui .vjs-custom-control-spacer, .video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-custom-control-spacer {
+ flex: auto;
+ display: block; }
+
+.video-js:not(.vjs-fullscreen).vjs-layout-x-small.vjs-liveui.vjs-no-flex .vjs-custom-control-spacer, .video-js:not(.vjs-fullscreen).vjs-layout-tiny.vjs-no-flex .vjs-custom-control-spacer {
+ width: auto; }
+
+.video-js:not(.vjs-fullscreen).vjs-layout-x-small.vjs-liveui .vjs-progress-control, .video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-progress-control {
+ display: none; }
+
+.vjs-modal-dialog.vjs-text-track-settings {
+ background-color: #2B333F;
+ background-color: rgba(43, 51, 63, 0.75);
+ color: #fff;
+ height: 70%; }
+
+.vjs-text-track-settings .vjs-modal-dialog-content {
+ display: table; }
+
+.vjs-text-track-settings .vjs-track-settings-colors,
+.vjs-text-track-settings .vjs-track-settings-font,
+.vjs-text-track-settings .vjs-track-settings-controls {
+ display: table-cell; }
+
+.vjs-text-track-settings .vjs-track-settings-controls {
+ text-align: right;
+ vertical-align: bottom; }
+
+@supports (display: grid) {
+ .vjs-text-track-settings .vjs-modal-dialog-content {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ grid-template-rows: 1fr;
+ padding: 20px 24px 0px 24px; }
+ .vjs-track-settings-controls .vjs-default-button {
+ margin-bottom: 20px; }
+ .vjs-text-track-settings .vjs-track-settings-controls {
+ grid-column: 1 / -1; }
+ .vjs-layout-small .vjs-text-track-settings .vjs-modal-dialog-content,
+ .vjs-layout-x-small .vjs-text-track-settings .vjs-modal-dialog-content,
+ .vjs-layout-tiny .vjs-text-track-settings .vjs-modal-dialog-content {
+ grid-template-columns: 1fr; } }
+
+.vjs-track-setting > select {
+ margin-right: 1em;
+ margin-bottom: 0.5em; }
+
+.vjs-text-track-settings fieldset {
+ margin: 5px;
+ padding: 3px;
+ border: none; }
+
+.vjs-text-track-settings fieldset span {
+ display: inline-block; }
+
+.vjs-text-track-settings fieldset span > select {
+ max-width: 7.3em; }
+
+.vjs-text-track-settings legend {
+ color: #fff;
+ margin: 0 0 5px 0; }
+
+.vjs-text-track-settings .vjs-label {
+ position: absolute;
+ clip: rect(1px 1px 1px 1px);
+ clip: rect(1px, 1px, 1px, 1px);
+ display: block;
+ margin: 0 0 5px 0;
+ padding: 0;
+ border: 0;
+ height: 1px;
+ width: 1px;
+ overflow: hidden; }
+
+.vjs-track-settings-controls button:focus,
+.vjs-track-settings-controls button:active {
+ outline-style: solid;
+ outline-width: medium;
+ background-image: linear-gradient(0deg, #fff 88%, #73859f 100%); }
+
+.vjs-track-settings-controls button:hover {
+ color: rgba(43, 51, 63, 0.75); }
+
+.vjs-track-settings-controls button {
+ background-color: #fff;
+ background-image: linear-gradient(-180deg, #fff 88%, #73859f 100%);
+ color: #2B333F;
+ cursor: pointer;
+ border-radius: 2px; }
+
+.vjs-track-settings-controls .vjs-default-button {
+ margin-right: 1em; }
+
+@media print {
+ .video-js > *:not(.vjs-tech):not(.vjs-poster) {
+ visibility: hidden; } }
+
+.vjs-resize-manager {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ border: none;
+ z-index: -1000; }
+
+.js-focus-visible .video-js *:focus:not(.focus-visible) {
+ outline: none;
+ background: none; }
+
+.video-js *:focus:not(:focus-visible),
+.video-js .vjs-menu *:focus:not(:focus-visible) {
+ outline: none;
+ background: none; }
diff --git a/site/videojs/alt/video-js-cdn.min.css b/site/videojs/alt/video-js-cdn.min.css
new file mode 100644
index 0000000..c080f8f
--- /dev/null
+++ b/site/videojs/alt/video-js-cdn.min.css
@@ -0,0 +1 @@
+.video-js .vjs-big-play-button .vjs-icon-placeholder:before,.video-js .vjs-modal-dialog,.vjs-button>.vjs-icon-placeholder:before,.vjs-modal-dialog .vjs-modal-dialog-content{position:absolute;top:0;left:0;width:100%;height:100%}.video-js .vjs-big-play-button .vjs-icon-placeholder:before,.vjs-button>.vjs-icon-placeholder:before{text-align:center}@font-face{font-family:VideoJS;src:url(data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAABBIAAsAAAAAGoQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADsAAABUIIslek9TLzIAAAFEAAAAPgAAAFZRiV3RY21hcAAAAYQAAADQAAADIjn098ZnbHlmAAACVAAACv4AABEIAwnSw2hlYWQAAA1UAAAAKwAAADYV1OgpaGhlYQAADYAAAAAbAAAAJA4DByFobXR4AAANnAAAAA8AAACE4AAAAGxvY2EAAA2sAAAARAAAAEQ9NEHGbWF4cAAADfAAAAAfAAAAIAEyAIFuYW1lAAAOEAAAASUAAAIK1cf1oHBvc3QAAA84AAABDwAAAZ5AAl/0eJxjYGRgYOBiMGCwY2BycfMJYeDLSSzJY5BiYGGAAJA8MpsxJzM9kYEDxgPKsYBpDiBmg4gCACY7BUgAeJxjYGQ7xTiBgZWBgaWQ5RkDA8MvCM0cwxDOeI6BgYmBlZkBKwhIc01hcPjI+FGBHcRdyA4RZgQRAC4HCwEAAHic7dFprsIgAEXhg8U61XmeWcBb1FuQP4w7ZQXK5boMm3yclFDSANAHmuKviBBeBPQ8ymyo8w3jOh/5r2ui5nN6v8sYNJb3WMdeWRvLji0DhozKdxM6psyYs2DJijUbtuzYc+DIiTMXrty4k8oGLb+n0xCe37ekM7Z66j1DbUy3l6PpHnLfdLO5NdSBoQ4NdWSoY9ON54mhdqa/y1NDnRnq3FAXhro01JWhrg11Y6hbQ90Z6t5QD4Z6NNSToZ4N9WKoV0O9GerdUJORPqkhTd54nJ1YDXBU1RV+576/JBs2bPYPkrDZt5vsJrv53V/I5mclhGDCTwgGBQQSTEji4hCkYIAGd4TGIWFAhV0RQTpWmQp1xv6hA4OTOlNr2zFANbHUYbq2OtNCpViRqsk+e+7bTQAhzti8vPfuPffcc88959zznbcMMPjHD/KDDGEY0ABpYX384NhlomIYlo4JISGEY9mMh2FSidYiqkEUphtNYDSY/dXg9023l4DdxlqUl0chuZRhncJKrsCQHIwcGuwfnhMIzBnuH4Sym+1D2zaGjheXlhYfD238z80mKYMmvJ5XeOTzd8z9eujbMxJNhu4C9xPE/bCMiDuSNIWgkTQwBE55hLSAE7ZwhrHLnAHZOGV/kmBGTiNjZxzI77Hb7Hqjz68TjT6vh+5JT/cCIkqS0D6CqPf5jX4Qjdx5j6vlDfZM4aZFdbVXIxtOlJaP/WottMnH6CJQ3bTiue3PrY23HjnChtuamxwvvzFjxkPrNj3z0tG9T561HDYf6OgmRWvlY3JQHoQb8ltV2Yet7YfWctEjR1AtxS/cSX6U4alf6NJEBQ7YKg9wrXQKd0IeZCb2ux75Uhh1Un+Nz+9LTOE7PK777nN5xqdTneTBhCbx446mZrhnUkrCz2YhA9dSMxaG0SYmT8hi9ZPu1E94PJYQSH6LRmhxec7Q7ZeXntgQuVpbh+a4qWNsckVyTdn0P7o7DpgPW84+uRcq0BITflBikGdUjAZ9wYBVI3mtrNvr9kpg1UsaK6t3690aoorC1lg0GpMH2HAMtkZjsSi5Ig9ESVosOh7GQfLjKNLvKpMKkLSKNFAka710GdgSi8oDMSoNhqjkKBXTgn3swtaxyzGkUzIzae9RtLdWkSlZ1KDX6EzgllzV4NV4SoDFSOGD4+HCeQUF8wrZ5Hs8zIb5EaVxy8DYFTbMCJPnLIWZxugZE2NlivC0gc1qEQUR8jEKgZcAXeH18BiCgl5nlHh0CrjB4Hb5fX4gb0J7c9PuHVsfgkx2n/vTY/JV8kn8PGxf7faOZ8qX8JVByuIf4whk9sqXli2hvPJV9hrp0hY7l8r2x37ydaVsb4xvXv/47v2NjfCl8m5oRDJclFMoE1yk0Uh1Te4/m8lFXe9qBZD0EkheicebXvzI2PLCuoKCukLuhPIeKwaHPEouxw3kMqaIUXDQ1p0mip+MyCORSCQaoUsnY1VZ38nUTrG21WvVo4f1OsEJFhvSfAFwGfT8VHRMeAVUpwLOoLzjT/REIj3O3FhuURE+nERF+0pTId5Fyxv5sfwGyg4O+my4vZv0sZm7oeQlFZORiB+tG0MweVNraeitl7yxiPIHTk4/diVxs94o5lEYishB2iAtkchEnsActoEpx44Fo8XnsQMaA22BlqC20RmhBKzYojZyYaxg+JggMc4HHY2m+L9EkWSYljirOisrO7d3VorxzyZ6Vc4lJqITAu1b2wOBdrLElAP+bFc2eGaZFVbkmJktv5uT6Jlz5D/MnBFor6ig/JPnRViBsV3LNKGGqB1ChJ0tgQywlVLFJIuQgTFttwkiKxhyQdAZMdMYtSaoAewqfvXVYPAbDT6/1mez85YS8FSDywQ6NfAnef6FNEGMilnppyvn5rB6tTyq1pOceRWnp2WJEZFXHeX5oyoem1nTTgdqc4heDY7bOeKz63vnz+/dRx+s31Ht2JGanQ5seirfWJL9tjozU/12TnEjn5oux9OzU3ckGbBzBwNOyk69JykKH0n/0LM9A72tuwM3zQpIRu4AxiToseEpgPOmbROyFe9/X2yeUvoUsCyEvjcgs7fpWP3/aKlFN0+6HFUe6D9HFz/XPwBlN9tTqNyZjFJ8UO2RUT5/h4CptCctEyeisnOyXjALEp7dXKaQKf6O7IMnGjNNACRMLxqdYJX8eMLvmmd68D+ayBLyKKYZwYxDt/GNhzETDJ05Qxlyi3pi3/Z93ndYVSumgj0V/KkIFlO6+1K3fF2+3g0q+YtuSIf0bvmLqV09nnobI6hwcjIP8aPCKayjsF5JBY3LaKAeRLSyYB1h81oTwe9SlPMkXB7G0mfL9q71gaqqwPqu67QRKS1+ObTx+sbQy9QV2OQHEScGkdFBeT7v7qisqqrs6N52i78/R+6S0qQONVj26agOVoswCyQWIV5D86vH53bxNUeXV0K+XZaHv/nm/KsHhOvylwsWnJX/HE8l/4WCv5x+l5n08z6UU8bUMa3MBpSmM7F63AxntdC9eBCKEZW9Hr+ABNqtxgAQrSbMtmrW7lKQuoSgBhSrTazWVU2QAKWY8wiiuhqFmQgWJBgoXiuWIm42N7hqZbBsgXz52O5P5uSvaNgFGnOuvsRw8I8Laha91wMvDuxqWFheN7/8GVtTltdS83DQsXRmqc5ZtcJXEVrlV2doTWk5+Yunm71dG5f55m/qY0MjI93vv9/NfpxXV9sUXrxy2fbNy1or65cOlDRnOoKFeeXcbw42H/bNDT5Qs3flgs31gWC1lD1nfUV/X7NdCnSUdHY2e8afzfKsqZ5ZljfDqjLOmk3UebNXB+aHArPYDRs+/HDDxeT5DiP+sFg7OpRaVQMGBV89PpeBdj22hCE0Uub0UqwLrNWsG0cuyadgLXTeR5rbO4+3c/vl15cur2nRq+TXCQDcS3SO+s6ak+e5/eMS+1dw3btu3YG2tvFL8XdIZvdjdW6TO/4B7IdrZWVPmctm5/59AgsPItTSbCiIBr2OqIGzmu20SMKAS7yqwGBUfGfgjDYlLLDeF0SfcLB2LSx8flT+08/kzz6yOj96rft4rpTjdPQcmLd47uKibbDq7ZSz/XtbH2nN717Nd62rU+c8Icevvv7I09wA6WvjVcafb+FsbNG+ZQ80Rn6ZZsvrP7teP2dzTdoETvNhjCmsr8FID2sJ69VYvdUcxk4AzYRlKcaE38eXNRlfW9H1as9i6acLHp1XpuNB5K7DIvkX08y1ZYvh3KfWaiCzH+ztrSDmD7LuX73x/mJelB8Yj39t8nhNQJJ2CAthpoFGLsGgtSOCJooCGoaJAMTjSWHVZ08YAa1Fg9lPI5U6DOsGVjDasJeZZ+YyhfCwfOzCxlBA69M9XLXtza7H/rav+9Tjq5xNi0wpKQIRNO4Lrzz7yp5QVYM6Jd/oc1Uvn/mQhhuWh6ENXoS2YTZ8QT42bF5d/559zp5r0Uff2VnR2tdf2/WCOd2cO0Mw6qpWPnvxpV0nrt5fZd2yItc199GWe8vlNfNDq+CH/7yAAnB9hn7T4QO4c1g9ScxsZgmzntnE/IDGndtHMw69lFwoCnYsMGx+rBp8JSBqdLzBr9QRPq/PbhWMWFtQZp1xguy/haw3TEHm3TWAnxFWQQWgt7M5OV0lCz1VRYucpWliy7z6Zd4urwPIyeZQqli2Lgg7szJV09PysATbOQtYIrB2YzbkJYkGgJ0m4AjPUap1pvYu1K9qr97z0Yl3p332b2LYB78ncYIlRkau/8GObSsOlZancACE5d5ily+c2+7h5Yj4lqhVmXXB+iXLfvdqSgqfKtQvfHDV0OnvQR1qhw42XS/vkvsh/hXcrDFP0a+SJNIomEfD1nsrYGO+1bgTOJhM8Hv6ek+7vVglxuSRwoKn17S937bm6YJCeSSG0Op1n+7tE37tcZ/p7dsTv4EUrGpDbWueKigsLHhqTVsoEj+JU0kaSjnj9tz8/gryQWwJ9BcJXBC/7smO+I/IFURJetFPrdt5WcoL6DbEJaygI8CTHfQTjf40ofD+DwalTqIAAHicY2BkYGAA4gDud4bx/DZfGbjZGUDg+q1z05BpdkawOAcDE4gCAB45CXEAeJxjYGRgYGcAARD5/z87IwMjAypQBAAtgwI4AHicY2BgYGAfYAwAOkQA4QAAAAAAAA4AaAB+AMwA4AECAUIBbAGYAcICGAJYArQC4AMwA7AD3gQwBJYE3AUkBWYFigYgBmYGtAbqB1gIEghYCG4IhHicY2BkYGBQZChlYGcAASYg5gJCBob/YD4DABfTAbQAeJxdkE1qg0AYhl8Tk9AIoVDaVSmzahcF87PMARLIMoFAl0ZHY1BHdBJIT9AT9AQ9RQ9Qeqy+yteNMzDzfM+88w0K4BY/cNAMB6N2bUaPPBLukybCLvleeAAPj8JD+hfhMV7hC3u4wxs7OO4NzQSZcI/8Ltwnfwi75E/hAR7wJTyk/xYeY49fYQ/PztM+jbTZ7LY6OWdBJdX/pqs6NYWa+zMxa13oKrA6Uoerqi/JwtpYxZXJ1coUVmeZUWVlTjq0/tHacjmdxuL90OR8O0UEDYMNdtiSEpz5XQGqzlm30kzUdAYFFOb8R7NOZk0q2lwAyz1i7oAr1xoXvrOgtYhZx8wY5KRV269JZ5yGpmzPTjQhvY9je6vEElPOuJP3mWKnP5M3V+YAAAB4nG2PyXLCMBBE3YCNDWEL2ffk7o8S8oCnkCVHC5C/jzBQlUP6IHVPzYyekl5y0iL5X5/ooY8BUmQYIkeBEca4wgRTzDDHAtdY4ga3uMM9HvCIJzzjBa94wzs+8ImvZNAq8TM+HqVkKxWlrQiOxjujQkNlEzyNzl6Z/cU2XF06at7U83VQyklLpEvSnuzsb+HAPnPfQVgaupa1Jlu4sPLsFblcitaz0dHU0ZF1qatjZ1+aTXYCmp6u0gSvWNPyHLtFZ+ZeXWVSaEkqs3T8S74WklbGbNNNq4LL4+CWKtZDv2cfX8l8aFbKFhEnJnJ+IULFpqwoQnNHlHaVQtPBl+ypmbSWdmyC61KS/AKZC3Y+AA==) format("woff");font-weight:400;font-style:normal}.video-js .vjs-big-play-button .vjs-icon-placeholder:before,.video-js .vjs-play-control .vjs-icon-placeholder,.vjs-icon-play{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-big-play-button .vjs-icon-placeholder:before,.video-js .vjs-play-control .vjs-icon-placeholder:before,.vjs-icon-play:before{content:"\f101"}.vjs-icon-play-circle{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-play-circle:before{content:"\f102"}.video-js .vjs-play-control.vjs-playing .vjs-icon-placeholder,.vjs-icon-pause{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-play-control.vjs-playing .vjs-icon-placeholder:before,.vjs-icon-pause:before{content:"\f103"}.video-js .vjs-mute-control.vjs-vol-0 .vjs-icon-placeholder,.vjs-icon-volume-mute{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-mute-control.vjs-vol-0 .vjs-icon-placeholder:before,.vjs-icon-volume-mute:before{content:"\f104"}.video-js .vjs-mute-control.vjs-vol-1 .vjs-icon-placeholder,.vjs-icon-volume-low{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-mute-control.vjs-vol-1 .vjs-icon-placeholder:before,.vjs-icon-volume-low:before{content:"\f105"}.video-js .vjs-mute-control.vjs-vol-2 .vjs-icon-placeholder,.vjs-icon-volume-mid{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-mute-control.vjs-vol-2 .vjs-icon-placeholder:before,.vjs-icon-volume-mid:before{content:"\f106"}.video-js .vjs-mute-control .vjs-icon-placeholder,.vjs-icon-volume-high{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-mute-control .vjs-icon-placeholder:before,.vjs-icon-volume-high:before{content:"\f107"}.video-js .vjs-fullscreen-control .vjs-icon-placeholder,.vjs-icon-fullscreen-enter{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-fullscreen-control .vjs-icon-placeholder:before,.vjs-icon-fullscreen-enter:before{content:"\f108"}.video-js.vjs-fullscreen .vjs-fullscreen-control .vjs-icon-placeholder,.vjs-icon-fullscreen-exit{font-family:VideoJS;font-weight:400;font-style:normal}.video-js.vjs-fullscreen .vjs-fullscreen-control .vjs-icon-placeholder:before,.vjs-icon-fullscreen-exit:before{content:"\f109"}.vjs-icon-square{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-square:before{content:"\f10a"}.vjs-icon-spinner{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-spinner:before{content:"\f10b"}.video-js .vjs-subs-caps-button .vjs-icon-placeholder,.video-js .vjs-subtitles-button .vjs-icon-placeholder,.video-js.video-js:lang(en-AU) .vjs-subs-caps-button .vjs-icon-placeholder,.video-js.video-js:lang(en-GB) .vjs-subs-caps-button .vjs-icon-placeholder,.video-js.video-js:lang(en-IE) .vjs-subs-caps-button .vjs-icon-placeholder,.video-js.video-js:lang(en-NZ) .vjs-subs-caps-button .vjs-icon-placeholder,.vjs-icon-subtitles{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-subs-caps-button .vjs-icon-placeholder:before,.video-js .vjs-subtitles-button .vjs-icon-placeholder:before,.video-js.video-js:lang(en-AU) .vjs-subs-caps-button .vjs-icon-placeholder:before,.video-js.video-js:lang(en-GB) .vjs-subs-caps-button .vjs-icon-placeholder:before,.video-js.video-js:lang(en-IE) .vjs-subs-caps-button .vjs-icon-placeholder:before,.video-js.video-js:lang(en-NZ) .vjs-subs-caps-button .vjs-icon-placeholder:before,.vjs-icon-subtitles:before{content:"\f10c"}.video-js .vjs-captions-button .vjs-icon-placeholder,.video-js:lang(en) .vjs-subs-caps-button .vjs-icon-placeholder,.video-js:lang(fr-CA) .vjs-subs-caps-button .vjs-icon-placeholder,.vjs-icon-captions{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-captions-button .vjs-icon-placeholder:before,.video-js:lang(en) .vjs-subs-caps-button .vjs-icon-placeholder:before,.video-js:lang(fr-CA) .vjs-subs-caps-button .vjs-icon-placeholder:before,.vjs-icon-captions:before{content:"\f10d"}.video-js .vjs-chapters-button .vjs-icon-placeholder,.vjs-icon-chapters{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-chapters-button .vjs-icon-placeholder:before,.vjs-icon-chapters:before{content:"\f10e"}.vjs-icon-share{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-share:before{content:"\f10f"}.vjs-icon-cog{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-cog:before{content:"\f110"}.video-js .vjs-play-progress,.video-js .vjs-volume-level,.vjs-icon-circle,.vjs-seek-to-live-control .vjs-icon-placeholder{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-play-progress:before,.video-js .vjs-volume-level:before,.vjs-icon-circle:before,.vjs-seek-to-live-control .vjs-icon-placeholder:before{content:"\f111"}.vjs-icon-circle-outline{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-circle-outline:before{content:"\f112"}.vjs-icon-circle-inner-circle{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-circle-inner-circle:before{content:"\f113"}.vjs-icon-hd{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-hd:before{content:"\f114"}.video-js .vjs-control.vjs-close-button .vjs-icon-placeholder,.vjs-icon-cancel{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-control.vjs-close-button .vjs-icon-placeholder:before,.vjs-icon-cancel:before{content:"\f115"}.video-js .vjs-play-control.vjs-ended .vjs-icon-placeholder,.vjs-icon-replay{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-play-control.vjs-ended .vjs-icon-placeholder:before,.vjs-icon-replay:before{content:"\f116"}.vjs-icon-facebook{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-facebook:before{content:"\f117"}.vjs-icon-gplus{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-gplus:before{content:"\f118"}.vjs-icon-linkedin{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-linkedin:before{content:"\f119"}.vjs-icon-twitter{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-twitter:before{content:"\f11a"}.vjs-icon-tumblr{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-tumblr:before{content:"\f11b"}.vjs-icon-pinterest{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-pinterest:before{content:"\f11c"}.video-js .vjs-descriptions-button .vjs-icon-placeholder,.vjs-icon-audio-description{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-descriptions-button .vjs-icon-placeholder:before,.vjs-icon-audio-description:before{content:"\f11d"}.video-js .vjs-audio-button .vjs-icon-placeholder,.vjs-icon-audio{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-audio-button .vjs-icon-placeholder:before,.vjs-icon-audio:before{content:"\f11e"}.vjs-icon-next-item{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-next-item:before{content:"\f11f"}.vjs-icon-previous-item{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-previous-item:before{content:"\f120"}.video-js{display:block;vertical-align:top;box-sizing:border-box;color:#fff;background-color:#000;position:relative;padding:0;font-size:10px;line-height:1;font-weight:400;font-style:normal;font-family:Arial,Helvetica,sans-serif;word-break:initial}.video-js:-moz-full-screen{position:absolute}.video-js:-webkit-full-screen{width:100%!important;height:100%!important}.video-js[tabindex="-1"]{outline:0}.video-js *,.video-js :after,.video-js :before{box-sizing:inherit}.video-js ul{font-family:inherit;font-size:inherit;line-height:inherit;list-style-position:outside;margin-left:0;margin-right:0;margin-top:0;margin-bottom:0}.video-js.vjs-16-9,.video-js.vjs-4-3,.video-js.vjs-fluid{width:100%;max-width:100%;height:0}.video-js.vjs-16-9{padding-top:56.25%}.video-js.vjs-4-3{padding-top:75%}.video-js.vjs-fill{width:100%;height:100%}.video-js .vjs-tech{position:absolute;top:0;left:0;width:100%;height:100%}body.vjs-full-window{padding:0;margin:0;height:100%}.vjs-full-window .video-js.vjs-fullscreen{position:fixed;overflow:hidden;z-index:1000;left:0;top:0;bottom:0;right:0}.video-js.vjs-fullscreen{width:100%!important;height:100%!important;padding-top:0!important}.video-js.vjs-fullscreen.vjs-user-inactive{cursor:none}.vjs-hidden{display:none!important}.vjs-disabled{opacity:.5;cursor:default}.video-js .vjs-offscreen{height:1px;left:-9999px;position:absolute;top:0;width:1px}.vjs-lock-showing{display:block!important;opacity:1;visibility:visible}.vjs-no-js{padding:20px;color:#fff;background-color:#000;font-size:18px;font-family:Arial,Helvetica,sans-serif;text-align:center;width:300px;height:150px;margin:0 auto}.vjs-no-js a,.vjs-no-js a:visited{color:#66a8cc}.video-js .vjs-big-play-button{font-size:3em;line-height:1.5em;height:1.63332em;width:3em;display:block;position:absolute;top:10px;left:10px;padding:0;cursor:pointer;opacity:1;border:.06666em solid #fff;background-color:#2b333f;background-color:rgba(43,51,63,.7);border-radius:.3em;transition:all .4s}.vjs-big-play-centered .vjs-big-play-button{top:50%;left:50%;margin-top:-.81666em;margin-left:-1.5em}.video-js .vjs-big-play-button:focus,.video-js:hover .vjs-big-play-button{border-color:#fff;background-color:#73859f;background-color:rgba(115,133,159,.5);transition:all 0s}.vjs-controls-disabled .vjs-big-play-button,.vjs-error .vjs-big-play-button,.vjs-has-started .vjs-big-play-button,.vjs-using-native-controls .vjs-big-play-button{display:none}.vjs-has-started.vjs-paused.vjs-show-big-play-button-on-pause .vjs-big-play-button{display:block}.video-js button{background:0 0;border:none;color:inherit;display:inline-block;font-size:inherit;line-height:inherit;text-transform:none;text-decoration:none;transition:none;-webkit-appearance:none;-moz-appearance:none;appearance:none}.vjs-control .vjs-button{width:100%;height:100%}.video-js .vjs-control.vjs-close-button{cursor:pointer;height:3em;position:absolute;right:0;top:.5em;z-index:2}.video-js .vjs-modal-dialog{background:rgba(0,0,0,.8);background:linear-gradient(180deg,rgba(0,0,0,.8),rgba(255,255,255,0));overflow:auto}.video-js .vjs-modal-dialog>*{box-sizing:border-box}.vjs-modal-dialog .vjs-modal-dialog-content{font-size:1.2em;line-height:1.5;padding:20px 24px;z-index:1}.vjs-menu-button{cursor:pointer}.vjs-menu-button.vjs-disabled{cursor:default}.vjs-workinghover .vjs-menu-button.vjs-disabled:hover .vjs-menu{display:none}.vjs-menu .vjs-menu-content{display:block;padding:0;margin:0;font-family:Arial,Helvetica,sans-serif;overflow:auto}.vjs-menu .vjs-menu-content>*{box-sizing:border-box}.vjs-scrubbing .vjs-control.vjs-menu-button:hover .vjs-menu{display:none}.vjs-menu li{list-style:none;margin:0;padding:.2em 0;line-height:1.4em;font-size:1.2em;text-align:center;text-transform:lowercase}.js-focus-visible .vjs-menu li.vjs-menu-item:hover,.vjs-menu li.vjs-menu-item:focus,.vjs-menu li.vjs-menu-item:hover{background-color:#73859f;background-color:rgba(115,133,159,.5)}.js-focus-visible .vjs-menu li.vjs-selected:hover,.vjs-menu li.vjs-selected,.vjs-menu li.vjs-selected:focus,.vjs-menu li.vjs-selected:hover{background-color:#fff;color:#2b333f}.vjs-menu li.vjs-menu-title{text-align:center;text-transform:uppercase;font-size:1em;line-height:2em;padding:0;margin:0 0 .3em 0;font-weight:700;cursor:default}.vjs-menu-button-popup .vjs-menu{display:none;position:absolute;bottom:0;width:10em;left:-3em;height:0;margin-bottom:1.5em;border-top-color:rgba(43,51,63,.7)}.vjs-menu-button-popup .vjs-menu .vjs-menu-content{background-color:#2b333f;background-color:rgba(43,51,63,.7);position:absolute;width:100%;bottom:1.5em;max-height:15em}.vjs-layout-tiny .vjs-menu-button-popup .vjs-menu .vjs-menu-content,.vjs-layout-x-small .vjs-menu-button-popup .vjs-menu .vjs-menu-content{max-height:5em}.vjs-layout-small .vjs-menu-button-popup .vjs-menu .vjs-menu-content{max-height:10em}.vjs-layout-medium .vjs-menu-button-popup .vjs-menu .vjs-menu-content{max-height:14em}.vjs-layout-huge .vjs-menu-button-popup .vjs-menu .vjs-menu-content,.vjs-layout-large .vjs-menu-button-popup .vjs-menu .vjs-menu-content,.vjs-layout-x-large .vjs-menu-button-popup .vjs-menu .vjs-menu-content{max-height:25em}.vjs-menu-button-popup .vjs-menu.vjs-lock-showing,.vjs-workinghover .vjs-menu-button-popup:hover .vjs-menu{display:block}.video-js .vjs-menu-button-inline{transition:all .4s;overflow:hidden}.video-js .vjs-menu-button-inline:before{width:2.222222222em}.video-js .vjs-menu-button-inline.vjs-slider-active,.video-js .vjs-menu-button-inline:focus,.video-js .vjs-menu-button-inline:hover,.video-js.vjs-no-flex .vjs-menu-button-inline{width:12em}.vjs-menu-button-inline .vjs-menu{opacity:0;height:100%;width:auto;position:absolute;left:4em;top:0;padding:0;margin:0;transition:all .4s}.vjs-menu-button-inline.vjs-slider-active .vjs-menu,.vjs-menu-button-inline:focus .vjs-menu,.vjs-menu-button-inline:hover .vjs-menu{display:block;opacity:1}.vjs-no-flex .vjs-menu-button-inline .vjs-menu{display:block;opacity:1;position:relative;width:auto}.vjs-no-flex .vjs-menu-button-inline.vjs-slider-active .vjs-menu,.vjs-no-flex .vjs-menu-button-inline:focus .vjs-menu,.vjs-no-flex .vjs-menu-button-inline:hover .vjs-menu{width:auto}.vjs-menu-button-inline .vjs-menu-content{width:auto;height:100%;margin:0;overflow:hidden}.video-js .vjs-control-bar{display:none;width:100%;position:absolute;bottom:0;left:0;right:0;height:3em;background-color:#2b333f;background-color:rgba(43,51,63,.7)}.vjs-has-started .vjs-control-bar{display:flex;visibility:visible;opacity:1;transition:visibility .1s,opacity .1s}.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar{visibility:visible;opacity:0;transition:visibility 1s,opacity 1s}.vjs-controls-disabled .vjs-control-bar,.vjs-error .vjs-control-bar,.vjs-using-native-controls .vjs-control-bar{display:none!important}.vjs-audio.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar{opacity:1;visibility:visible}.vjs-has-started.vjs-no-flex .vjs-control-bar{display:table}.video-js .vjs-control{position:relative;text-align:center;margin:0;padding:0;height:100%;width:4em;flex:none}.vjs-button>.vjs-icon-placeholder:before{font-size:1.8em;line-height:1.67}.video-js .vjs-control:focus,.video-js .vjs-control:focus:before,.video-js .vjs-control:hover:before{text-shadow:0 0 1em #fff}.video-js .vjs-control-text{border:0;clip:rect(0 0 0 0);height:1px;overflow:hidden;padding:0;position:absolute;width:1px}.vjs-no-flex .vjs-control{display:table-cell;vertical-align:middle}.video-js .vjs-custom-control-spacer{display:none}.video-js .vjs-progress-control{cursor:pointer;flex:auto;display:flex;align-items:center;min-width:4em;touch-action:none}.video-js .vjs-progress-control.disabled{cursor:default}.vjs-live .vjs-progress-control{display:none}.vjs-liveui .vjs-progress-control{display:flex;align-items:center}.vjs-no-flex .vjs-progress-control{width:auto}.video-js .vjs-progress-holder{flex:auto;transition:all .2s;height:.3em}.video-js .vjs-progress-control .vjs-progress-holder{margin:0 10px}.video-js .vjs-progress-control:hover .vjs-progress-holder{font-size:1.666666666666666666em}.video-js .vjs-progress-control:hover .vjs-progress-holder.disabled{font-size:1em}.video-js .vjs-progress-holder .vjs-load-progress,.video-js .vjs-progress-holder .vjs-load-progress div,.video-js .vjs-progress-holder .vjs-play-progress{position:absolute;display:block;height:100%;margin:0;padding:0;width:0}.video-js .vjs-play-progress{background-color:#fff}.video-js .vjs-play-progress:before{font-size:.9em;position:absolute;right:-.5em;top:-.333333333333333em;z-index:1}.video-js .vjs-load-progress{background:rgba(115,133,159,.5)}.video-js .vjs-load-progress div{background:rgba(115,133,159,.75)}.video-js .vjs-time-tooltip{background-color:#fff;background-color:rgba(255,255,255,.8);border-radius:.3em;color:#000;float:right;font-family:Arial,Helvetica,sans-serif;font-size:1em;padding:6px 8px 8px 8px;pointer-events:none;position:absolute;top:-3.4em;visibility:hidden;z-index:1}.video-js .vjs-progress-holder:focus .vjs-time-tooltip{display:none}.video-js .vjs-progress-control:hover .vjs-progress-holder:focus .vjs-time-tooltip,.video-js .vjs-progress-control:hover .vjs-time-tooltip{display:block;font-size:.6em;visibility:visible}.video-js .vjs-progress-control.disabled:hover .vjs-time-tooltip{font-size:1em}.video-js .vjs-progress-control .vjs-mouse-display{display:none;position:absolute;width:1px;height:100%;background-color:#000;z-index:1}.vjs-no-flex .vjs-progress-control .vjs-mouse-display{z-index:0}.video-js .vjs-progress-control:hover .vjs-mouse-display{display:block}.video-js.vjs-user-inactive .vjs-progress-control .vjs-mouse-display{visibility:hidden;opacity:0;transition:visibility 1s,opacity 1s}.video-js.vjs-user-inactive.vjs-no-flex .vjs-progress-control .vjs-mouse-display{display:none}.vjs-mouse-display .vjs-time-tooltip{color:#fff;background-color:#000;background-color:rgba(0,0,0,.8)}.video-js .vjs-slider{position:relative;cursor:pointer;padding:0;margin:0 .45em 0 .45em;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:#73859f;background-color:rgba(115,133,159,.5)}.video-js .vjs-slider.disabled{cursor:default}.video-js .vjs-slider:focus{text-shadow:0 0 1em #fff;box-shadow:0 0 1em #fff}.video-js .vjs-mute-control{cursor:pointer;flex:none}.video-js .vjs-volume-control{cursor:pointer;margin-right:1em;display:flex}.video-js .vjs-volume-control.vjs-volume-horizontal{width:5em}.video-js .vjs-volume-panel .vjs-volume-control{visibility:visible;opacity:0;width:1px;height:1px;margin-left:-1px}.video-js .vjs-volume-panel{transition:width 1s}.video-js .vjs-volume-panel .vjs-mute-control:hover~.vjs-volume-control,.video-js .vjs-volume-panel .vjs-volume-control.vjs-slider-active,.video-js .vjs-volume-panel .vjs-volume-control:active,.video-js .vjs-volume-panel .vjs-volume-control:hover,.video-js .vjs-volume-panel:active .vjs-volume-control,.video-js .vjs-volume-panel:focus .vjs-volume-control,.video-js .vjs-volume-panel:hover .vjs-volume-control{visibility:visible;opacity:1;position:relative;transition:visibility .1s,opacity .1s,height .1s,width .1s,left 0s,top 0s}.video-js .vjs-volume-panel .vjs-mute-control:hover~.vjs-volume-control.vjs-volume-horizontal,.video-js .vjs-volume-panel .vjs-volume-control.vjs-slider-active.vjs-volume-horizontal,.video-js .vjs-volume-panel .vjs-volume-control:active.vjs-volume-horizontal,.video-js .vjs-volume-panel .vjs-volume-control:hover.vjs-volume-horizontal,.video-js .vjs-volume-panel:active .vjs-volume-control.vjs-volume-horizontal,.video-js .vjs-volume-panel:focus .vjs-volume-control.vjs-volume-horizontal,.video-js .vjs-volume-panel:hover .vjs-volume-control.vjs-volume-horizontal{width:5em;height:3em}.video-js .vjs-volume-panel .vjs-mute-control:hover~.vjs-volume-control.vjs-volume-vertical,.video-js .vjs-volume-panel .vjs-volume-control.vjs-slider-active.vjs-volume-vertical,.video-js .vjs-volume-panel .vjs-volume-control:active.vjs-volume-vertical,.video-js .vjs-volume-panel .vjs-volume-control:hover.vjs-volume-vertical,.video-js .vjs-volume-panel:active .vjs-volume-control.vjs-volume-vertical,.video-js .vjs-volume-panel:focus .vjs-volume-control.vjs-volume-vertical,.video-js .vjs-volume-panel:hover .vjs-volume-control.vjs-volume-vertical{left:-3.5em}.video-js .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-slider-active,.video-js .vjs-volume-panel.vjs-volume-panel-horizontal:active,.video-js .vjs-volume-panel.vjs-volume-panel-horizontal:hover{width:9em;transition:width .1s}.video-js .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-mute-toggle-only{width:4em}.video-js .vjs-volume-panel .vjs-volume-control.vjs-volume-vertical{height:8em;width:3em;left:-3000em;transition:visibility 1s,opacity 1s,height 1s 1s,width 1s 1s,left 1s 1s,top 1s 1s}.video-js .vjs-volume-panel .vjs-volume-control.vjs-volume-horizontal{transition:visibility 1s,opacity 1s,height 1s 1s,width 1s,left 1s 1s,top 1s 1s}.video-js.vjs-no-flex .vjs-volume-panel .vjs-volume-control.vjs-volume-horizontal{width:5em;height:3em;visibility:visible;opacity:1;position:relative;transition:none}.video-js.vjs-no-flex .vjs-volume-control.vjs-volume-vertical,.video-js.vjs-no-flex .vjs-volume-panel .vjs-volume-control.vjs-volume-vertical{position:absolute;bottom:3em;left:.5em}.video-js .vjs-volume-panel{display:flex}.video-js .vjs-volume-bar{margin:1.35em .45em}.vjs-volume-bar.vjs-slider-horizontal{width:5em;height:.3em}.vjs-volume-bar.vjs-slider-vertical{width:.3em;height:5em;margin:1.35em auto}.video-js .vjs-volume-level{position:absolute;bottom:0;left:0;background-color:#fff}.video-js .vjs-volume-level:before{position:absolute;font-size:.9em}.vjs-slider-vertical .vjs-volume-level{width:.3em}.vjs-slider-vertical .vjs-volume-level:before{top:-.5em;left:-.3em}.vjs-slider-horizontal .vjs-volume-level{height:.3em}.vjs-slider-horizontal .vjs-volume-level:before{top:-.3em;right:-.5em}.video-js .vjs-volume-panel.vjs-volume-panel-vertical{width:4em}.vjs-volume-bar.vjs-slider-vertical .vjs-volume-level{height:100%}.vjs-volume-bar.vjs-slider-horizontal .vjs-volume-level{width:100%}.video-js .vjs-volume-vertical{width:3em;height:8em;bottom:8em;background-color:#2b333f;background-color:rgba(43,51,63,.7)}.video-js .vjs-volume-horizontal .vjs-menu{left:-2em}.vjs-poster{display:inline-block;vertical-align:middle;background-repeat:no-repeat;background-position:50% 50%;background-size:contain;background-color:#000;cursor:pointer;margin:0;padding:0;position:absolute;top:0;right:0;bottom:0;left:0;height:100%}.vjs-has-started .vjs-poster{display:none}.vjs-audio.vjs-has-started .vjs-poster{display:block}.vjs-using-native-controls .vjs-poster{display:none}.video-js .vjs-live-control{display:flex;align-items:flex-start;flex:auto;font-size:1em;line-height:3em}.vjs-no-flex .vjs-live-control{display:table-cell;width:auto;text-align:left}.video-js.vjs-liveui .vjs-live-control,.video-js:not(.vjs-live) .vjs-live-control{display:none}.video-js .vjs-seek-to-live-control{cursor:pointer;flex:none;display:inline-flex;height:100%;padding-left:.5em;padding-right:.5em;font-size:1em;line-height:3em;width:auto;min-width:4em}.vjs-no-flex .vjs-seek-to-live-control{display:table-cell;width:auto;text-align:left}.video-js.vjs-live:not(.vjs-liveui) .vjs-seek-to-live-control,.video-js:not(.vjs-live) .vjs-seek-to-live-control{display:none}.vjs-seek-to-live-control.vjs-control.vjs-at-live-edge{cursor:auto}.vjs-seek-to-live-control .vjs-icon-placeholder{margin-right:.5em;color:#888}.vjs-seek-to-live-control.vjs-control.vjs-at-live-edge .vjs-icon-placeholder{color:red}.video-js .vjs-time-control{flex:none;font-size:1em;line-height:3em;min-width:2em;width:auto;padding-left:1em;padding-right:1em}.vjs-live .vjs-time-control{display:none}.video-js .vjs-current-time,.vjs-no-flex .vjs-current-time{display:none}.video-js .vjs-duration,.vjs-no-flex .vjs-duration{display:none}.vjs-time-divider{display:none;line-height:3em}.vjs-live .vjs-time-divider{display:none}.video-js .vjs-play-control{cursor:pointer}.video-js .vjs-play-control .vjs-icon-placeholder{flex:none}.vjs-text-track-display{position:absolute;bottom:3em;left:0;right:0;top:0;pointer-events:none}.video-js.vjs-user-inactive.vjs-playing .vjs-text-track-display{bottom:1em}.video-js .vjs-text-track{font-size:1.4em;text-align:center;margin-bottom:.1em}.vjs-subtitles{color:#fff}.vjs-captions{color:#fc6}.vjs-tt-cue{display:block}video::-webkit-media-text-track-display{-webkit-transform:translateY(-3em);transform:translateY(-3em)}.video-js.vjs-user-inactive.vjs-playing video::-webkit-media-text-track-display{-webkit-transform:translateY(-1.5em);transform:translateY(-1.5em)}.video-js .vjs-fullscreen-control{cursor:pointer;flex:none}.vjs-playback-rate .vjs-playback-rate-value,.vjs-playback-rate>.vjs-menu-button{position:absolute;top:0;left:0;width:100%;height:100%}.vjs-playback-rate .vjs-playback-rate-value{pointer-events:none;font-size:1.5em;line-height:2;text-align:center}.vjs-playback-rate .vjs-menu{width:4em;left:0}.vjs-error .vjs-error-display .vjs-modal-dialog-content{font-size:1.4em;text-align:center}.vjs-error .vjs-error-display:before{color:#fff;content:'X';font-family:Arial,Helvetica,sans-serif;font-size:4em;left:0;line-height:1;margin-top:-.5em;position:absolute;text-shadow:.05em .05em .1em #000;text-align:center;top:50%;vertical-align:middle;width:100%}.vjs-loading-spinner{display:none;position:absolute;top:50%;left:50%;margin:-25px 0 0 -25px;opacity:.85;text-align:left;border:6px solid rgba(43,51,63,.7);box-sizing:border-box;background-clip:padding-box;width:50px;height:50px;border-radius:25px;visibility:hidden}.vjs-seeking .vjs-loading-spinner,.vjs-waiting .vjs-loading-spinner{display:block;-webkit-animation:vjs-spinner-show 0s linear .3s forwards;animation:vjs-spinner-show 0s linear .3s forwards}.vjs-loading-spinner:after,.vjs-loading-spinner:before{content:"";position:absolute;margin:-6px;box-sizing:inherit;width:inherit;height:inherit;border-radius:inherit;opacity:1;border:inherit;border-color:transparent;border-top-color:#fff}.vjs-seeking .vjs-loading-spinner:after,.vjs-seeking .vjs-loading-spinner:before,.vjs-waiting .vjs-loading-spinner:after,.vjs-waiting .vjs-loading-spinner:before{-webkit-animation:vjs-spinner-spin 1.1s cubic-bezier(.6,.2,0,.8) infinite,vjs-spinner-fade 1.1s linear infinite;animation:vjs-spinner-spin 1.1s cubic-bezier(.6,.2,0,.8) infinite,vjs-spinner-fade 1.1s linear infinite}.vjs-seeking .vjs-loading-spinner:before,.vjs-waiting .vjs-loading-spinner:before{border-top-color:#fff}.vjs-seeking .vjs-loading-spinner:after,.vjs-waiting .vjs-loading-spinner:after{border-top-color:#fff;-webkit-animation-delay:.44s;animation-delay:.44s}@keyframes vjs-spinner-show{to{visibility:visible}}@-webkit-keyframes vjs-spinner-show{to{visibility:visible}}@keyframes vjs-spinner-spin{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes vjs-spinner-spin{100%{-webkit-transform:rotate(360deg)}}@keyframes vjs-spinner-fade{0%{border-top-color:#73859f}20%{border-top-color:#73859f}35%{border-top-color:#fff}60%{border-top-color:#73859f}100%{border-top-color:#73859f}}@-webkit-keyframes vjs-spinner-fade{0%{border-top-color:#73859f}20%{border-top-color:#73859f}35%{border-top-color:#fff}60%{border-top-color:#73859f}100%{border-top-color:#73859f}}.vjs-chapters-button .vjs-menu ul{width:24em}.video-js .vjs-subs-caps-button+.vjs-menu .vjs-captions-menu-item .vjs-menu-item-text .vjs-icon-placeholder{vertical-align:middle;display:inline-block;margin-bottom:-.1em}.video-js .vjs-subs-caps-button+.vjs-menu .vjs-captions-menu-item .vjs-menu-item-text .vjs-icon-placeholder:before{font-family:VideoJS;content:"\f10d";font-size:1.5em;line-height:inherit}.video-js .vjs-audio-button+.vjs-menu .vjs-main-desc-menu-item .vjs-menu-item-text .vjs-icon-placeholder{vertical-align:middle;display:inline-block;margin-bottom:-.1em}.video-js .vjs-audio-button+.vjs-menu .vjs-main-desc-menu-item .vjs-menu-item-text .vjs-icon-placeholder:before{font-family:VideoJS;content:" \f11d";font-size:1.5em;line-height:inherit}.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-audio-button,.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-captions-button,.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-chapters-button,.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-current-time,.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-descriptions-button,.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-duration,.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-playback-rate,.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-remaining-time,.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-subtitles-button,.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-time-divider,.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-volume-control,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-audio-button,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-captions-button,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-chapters-button,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-current-time,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-descriptions-button,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-duration,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-playback-rate,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-remaining-time,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-subtitles-button,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-time-divider,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-volume-control,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-audio-button,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-captions-button,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-chapters-button,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-current-time,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-descriptions-button,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-duration,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-playback-rate,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-remaining-time,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-subtitles-button,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-time-divider,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-volume-control{display:none}.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-slider-active,.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-volume-panel.vjs-volume-panel-horizontal:active,.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-volume-panel.vjs-volume-panel-horizontal:hover,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-slider-active,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-volume-panel.vjs-volume-panel-horizontal:active,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-volume-panel.vjs-volume-panel-horizontal:hover,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-slider-active,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-volume-panel.vjs-volume-panel-horizontal:active,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-volume-panel.vjs-volume-panel-horizontal:hover{width:auto;width:initial}.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-subs-caps-button,.video-js:not(.vjs-fullscreen).vjs-layout-x-small:not(.vjs-live) .vjs-subs-caps-button,.video-js:not(.vjs-fullscreen).vjs-layout-x-small:not(.vjs-liveui) .vjs-subs-caps-button{display:none}.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-custom-control-spacer,.video-js:not(.vjs-fullscreen).vjs-layout-x-small.vjs-liveui .vjs-custom-control-spacer{flex:auto;display:block}.video-js:not(.vjs-fullscreen).vjs-layout-tiny.vjs-no-flex .vjs-custom-control-spacer,.video-js:not(.vjs-fullscreen).vjs-layout-x-small.vjs-liveui.vjs-no-flex .vjs-custom-control-spacer{width:auto}.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-progress-control,.video-js:not(.vjs-fullscreen).vjs-layout-x-small.vjs-liveui .vjs-progress-control{display:none}.vjs-modal-dialog.vjs-text-track-settings{background-color:#2b333f;background-color:rgba(43,51,63,.75);color:#fff;height:70%}.vjs-text-track-settings .vjs-modal-dialog-content{display:table}.vjs-text-track-settings .vjs-track-settings-colors,.vjs-text-track-settings .vjs-track-settings-controls,.vjs-text-track-settings .vjs-track-settings-font{display:table-cell}.vjs-text-track-settings .vjs-track-settings-controls{text-align:right;vertical-align:bottom}@supports (display:grid){.vjs-text-track-settings .vjs-modal-dialog-content{display:grid;grid-template-columns:1fr 1fr;grid-template-rows:1fr;padding:20px 24px 0 24px}.vjs-track-settings-controls .vjs-default-button{margin-bottom:20px}.vjs-text-track-settings .vjs-track-settings-controls{grid-column:1/-1}.vjs-layout-small .vjs-text-track-settings .vjs-modal-dialog-content,.vjs-layout-tiny .vjs-text-track-settings .vjs-modal-dialog-content,.vjs-layout-x-small .vjs-text-track-settings .vjs-modal-dialog-content{grid-template-columns:1fr}}.vjs-track-setting>select{margin-right:1em;margin-bottom:.5em}.vjs-text-track-settings fieldset{margin:5px;padding:3px;border:none}.vjs-text-track-settings fieldset span{display:inline-block}.vjs-text-track-settings fieldset span>select{max-width:7.3em}.vjs-text-track-settings legend{color:#fff;margin:0 0 5px 0}.vjs-text-track-settings .vjs-label{position:absolute;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px);display:block;margin:0 0 5px 0;padding:0;border:0;height:1px;width:1px;overflow:hidden}.vjs-track-settings-controls button:active,.vjs-track-settings-controls button:focus{outline-style:solid;outline-width:medium;background-image:linear-gradient(0deg,#fff 88%,#73859f 100%)}.vjs-track-settings-controls button:hover{color:rgba(43,51,63,.75)}.vjs-track-settings-controls button{background-color:#fff;background-image:linear-gradient(-180deg,#fff 88%,#73859f 100%);color:#2b333f;cursor:pointer;border-radius:2px}.vjs-track-settings-controls .vjs-default-button{margin-right:1em}@media print{.video-js>:not(.vjs-tech):not(.vjs-poster){visibility:hidden}}.vjs-resize-manager{position:absolute;top:0;left:0;width:100%;height:100%;border:none;z-index:-1000}.js-focus-visible .video-js :focus:not(.focus-visible){outline:0;background:0 0}.video-js .vjs-menu :focus:not(:focus-visible),.video-js :focus:not(:focus-visible){outline:0;background:0 0}
\ No newline at end of file
diff --git a/site/videojs/alt/video.core.js b/site/videojs/alt/video.core.js
new file mode 100644
index 0000000..a517eaa
--- /dev/null
+++ b/site/videojs/alt/video.core.js
@@ -0,0 +1,28357 @@
+/**
+ * @license
+ * Video.js 7.5.5
+ * Copyright Brightcove, Inc.
+ * Available under Apache License Version 2.0
+ *
+ *
+ * Includes vtt.js
+ * Available under Apache License Version 2.0
+ *
+ */
+
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('global/window'), require('global/document')) :
+ typeof define === 'function' && define.amd ? define(['global/window', 'global/document'], factory) :
+ (global = global || self, global.videojs = factory(global.window, global.document));
+}(this, function (window$1, document) {
+ window$1 = window$1 && window$1.hasOwnProperty('default') ? window$1['default'] : window$1;
+ document = document && document.hasOwnProperty('default') ? document['default'] : document;
+
+ var version = "7.5.5";
+
+ function _inheritsLoose(subClass, superClass) {
+ subClass.prototype = Object.create(superClass.prototype);
+ subClass.prototype.constructor = subClass;
+ subClass.__proto__ = superClass;
+ }
+
+ function _setPrototypeOf(o, p) {
+ _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
+ o.__proto__ = p;
+ return o;
+ };
+
+ return _setPrototypeOf(o, p);
+ }
+
+ function isNativeReflectConstruct() {
+ if (typeof Reflect === "undefined" || !Reflect.construct) return false;
+ if (Reflect.construct.sham) return false;
+ if (typeof Proxy === "function") return true;
+
+ try {
+ Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }
+
+ function _construct(Parent, args, Class) {
+ if (isNativeReflectConstruct()) {
+ _construct = Reflect.construct;
+ } else {
+ _construct = function _construct(Parent, args, Class) {
+ var a = [null];
+ a.push.apply(a, args);
+ var Constructor = Function.bind.apply(Parent, a);
+ var instance = new Constructor();
+ if (Class) _setPrototypeOf(instance, Class.prototype);
+ return instance;
+ };
+ }
+
+ return _construct.apply(null, arguments);
+ }
+
+ function _assertThisInitialized(self) {
+ if (self === void 0) {
+ throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
+ }
+
+ return self;
+ }
+
+ function _taggedTemplateLiteralLoose(strings, raw) {
+ if (!raw) {
+ raw = strings.slice(0);
+ }
+
+ strings.raw = raw;
+ return strings;
+ }
+
+ /**
+ * @file create-logger.js
+ * @module create-logger
+ */
+
+ var history = [];
+ /**
+ * Log messages to the console and history based on the type of message
+ *
+ * @private
+ * @param {string} type
+ * The name of the console method to use.
+ *
+ * @param {Array} args
+ * The arguments to be passed to the matching console method.
+ */
+
+ var LogByTypeFactory = function LogByTypeFactory(name, log) {
+ return function (type, level, args) {
+ var lvl = log.levels[level];
+ var lvlRegExp = new RegExp("^(" + lvl + ")$");
+
+ if (type !== 'log') {
+ // Add the type to the front of the message when it's not "log".
+ args.unshift(type.toUpperCase() + ':');
+ } // Add console prefix after adding to history.
+
+
+ args.unshift(name + ':'); // Add a clone of the args at this point to history.
+
+ if (history) {
+ history.push([].concat(args));
+ } // If there's no console then don't try to output messages, but they will
+ // still be stored in history.
+
+
+ if (!window$1.console) {
+ return;
+ } // Was setting these once outside of this function, but containing them
+ // in the function makes it easier to test cases where console doesn't exist
+ // when the module is executed.
+
+
+ var fn = window$1.console[type];
+
+ if (!fn && type === 'debug') {
+ // Certain browsers don't have support for console.debug. For those, we
+ // should default to the closest comparable log.
+ fn = window$1.console.info || window$1.console.log;
+ } // Bail out if there's no console or if this type is not allowed by the
+ // current logging level.
+
+
+ if (!fn || !lvl || !lvlRegExp.test(type)) {
+ return;
+ }
+
+ fn[Array.isArray(args) ? 'apply' : 'call'](window$1.console, args);
+ };
+ };
+
+ function createLogger(name) {
+ // This is the private tracking variable for logging level.
+ var level = 'info'; // the curried logByType bound to the specific log and history
+
+ var logByType;
+ /**
+ * Logs plain debug messages. Similar to `console.log`.
+ *
+ * Due to [limitations](https://github.com/jsdoc3/jsdoc/issues/955#issuecomment-313829149)
+ * of our JSDoc template, we cannot properly document this as both a function
+ * and a namespace, so its function signature is documented here.
+ *
+ * #### Arguments
+ * ##### *args
+ * Mixed[]
+ *
+ * Any combination of values that could be passed to `console.log()`.
+ *
+ * #### Return Value
+ *
+ * `undefined`
+ *
+ * @namespace
+ * @param {Mixed[]} args
+ * One or more messages or objects that should be logged.
+ */
+
+ var log = function log() {
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ logByType('log', level, args);
+ }; // This is the logByType helper that the logging methods below use
+
+
+ logByType = LogByTypeFactory(name, log);
+ /**
+ * Create a new sublogger which chains the old name to the new name.
+ *
+ * For example, doing `videojs.log.createLogger('player')` and then using that logger will log the following:
+ * ```js
+ * mylogger('foo');
+ * // > VIDEOJS: player: foo
+ * ```
+ *
+ * @param {string} name
+ * The name to add call the new logger
+ * @return {Object}
+ */
+
+ log.createLogger = function (subname) {
+ return createLogger(name + ': ' + subname);
+ };
+ /**
+ * Enumeration of available logging levels, where the keys are the level names
+ * and the values are `|`-separated strings containing logging methods allowed
+ * in that logging level. These strings are used to create a regular expression
+ * matching the function name being called.
+ *
+ * Levels provided by Video.js are:
+ *
+ * - `off`: Matches no calls. Any value that can be cast to `false` will have
+ * this effect. The most restrictive.
+ * - `all`: Matches only Video.js-provided functions (`debug`, `log`,
+ * `log.warn`, and `log.error`).
+ * - `debug`: Matches `log.debug`, `log`, `log.warn`, and `log.error` calls.
+ * - `info` (default): Matches `log`, `log.warn`, and `log.error` calls.
+ * - `warn`: Matches `log.warn` and `log.error` calls.
+ * - `error`: Matches only `log.error` calls.
+ *
+ * @type {Object}
+ */
+
+
+ log.levels = {
+ all: 'debug|log|warn|error',
+ off: '',
+ debug: 'debug|log|warn|error',
+ info: 'log|warn|error',
+ warn: 'warn|error',
+ error: 'error',
+ DEFAULT: level
+ };
+ /**
+ * Get or set the current logging level.
+ *
+ * If a string matching a key from {@link module:log.levels} is provided, acts
+ * as a setter.
+ *
+ * @param {string} [lvl]
+ * Pass a valid level to set a new logging level.
+ *
+ * @return {string}
+ * The current logging level.
+ */
+
+ log.level = function (lvl) {
+ if (typeof lvl === 'string') {
+ if (!log.levels.hasOwnProperty(lvl)) {
+ throw new Error("\"" + lvl + "\" in not a valid log level");
+ }
+
+ level = lvl;
+ }
+
+ return level;
+ };
+ /**
+ * Returns an array containing everything that has been logged to the history.
+ *
+ * This array is a shallow clone of the internal history record. However, its
+ * contents are _not_ cloned; so, mutating objects inside this array will
+ * mutate them in history.
+ *
+ * @return {Array}
+ */
+
+
+ log.history = function () {
+ return history ? [].concat(history) : [];
+ };
+ /**
+ * Allows you to filter the history by the given logger name
+ *
+ * @param {string} fname
+ * The name to filter by
+ *
+ * @return {Array}
+ * The filtered list to return
+ */
+
+
+ log.history.filter = function (fname) {
+ return (history || []).filter(function (historyItem) {
+ // if the first item in each historyItem includes `fname`, then it's a match
+ return new RegExp(".*" + fname + ".*").test(historyItem[0]);
+ });
+ };
+ /**
+ * Clears the internal history tracking, but does not prevent further history
+ * tracking.
+ */
+
+
+ log.history.clear = function () {
+ if (history) {
+ history.length = 0;
+ }
+ };
+ /**
+ * Disable history tracking if it is currently enabled.
+ */
+
+
+ log.history.disable = function () {
+ if (history !== null) {
+ history.length = 0;
+ history = null;
+ }
+ };
+ /**
+ * Enable history tracking if it is currently disabled.
+ */
+
+
+ log.history.enable = function () {
+ if (history === null) {
+ history = [];
+ }
+ };
+ /**
+ * Logs error messages. Similar to `console.error`.
+ *
+ * @param {Mixed[]} args
+ * One or more messages or objects that should be logged as an error
+ */
+
+
+ log.error = function () {
+ for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
+ args[_key2] = arguments[_key2];
+ }
+
+ return logByType('error', level, args);
+ };
+ /**
+ * Logs warning messages. Similar to `console.warn`.
+ *
+ * @param {Mixed[]} args
+ * One or more messages or objects that should be logged as a warning.
+ */
+
+
+ log.warn = function () {
+ for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
+ args[_key3] = arguments[_key3];
+ }
+
+ return logByType('warn', level, args);
+ };
+ /**
+ * Logs debug messages. Similar to `console.debug`, but may also act as a comparable
+ * log if `console.debug` is not available
+ *
+ * @param {Mixed[]} args
+ * One or more messages or objects that should be logged as debug.
+ */
+
+
+ log.debug = function () {
+ for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
+ args[_key4] = arguments[_key4];
+ }
+
+ return logByType('debug', level, args);
+ };
+
+ return log;
+ }
+
+ /**
+ * @file log.js
+ * @module log
+ */
+ var log = createLogger('VIDEOJS');
+ var createLogger$1 = log.createLogger;
+
+ function clean(s) {
+ return s.replace(/\n\r?\s*/g, '');
+ }
+
+ var tsml = function tsml(sa) {
+ var s = '',
+ i = 0;
+
+ for (; i < arguments.length; i++) {
+ s += clean(sa[i]) + (arguments[i + 1] || '');
+ }
+
+ return s;
+ };
+
+ /**
+ * @file obj.js
+ * @module obj
+ */
+
+ /**
+ * @callback obj:EachCallback
+ *
+ * @param {Mixed} value
+ * The current key for the object that is being iterated over.
+ *
+ * @param {string} key
+ * The current key-value for object that is being iterated over
+ */
+
+ /**
+ * @callback obj:ReduceCallback
+ *
+ * @param {Mixed} accum
+ * The value that is accumulating over the reduce loop.
+ *
+ * @param {Mixed} value
+ * The current key for the object that is being iterated over.
+ *
+ * @param {string} key
+ * The current key-value for object that is being iterated over
+ *
+ * @return {Mixed}
+ * The new accumulated value.
+ */
+ var toString = Object.prototype.toString;
+ /**
+ * Get the keys of an Object
+ *
+ * @param {Object}
+ * The Object to get the keys from
+ *
+ * @return {string[]}
+ * An array of the keys from the object. Returns an empty array if the
+ * object passed in was invalid or had no keys.
+ *
+ * @private
+ */
+
+ var keys = function keys(object) {
+ return isObject(object) ? Object.keys(object) : [];
+ };
+ /**
+ * Array-like iteration for objects.
+ *
+ * @param {Object} object
+ * The object to iterate over
+ *
+ * @param {obj:EachCallback} fn
+ * The callback function which is called for each key in the object.
+ */
+
+
+ function each(object, fn) {
+ keys(object).forEach(function (key) {
+ return fn(object[key], key);
+ });
+ }
+ /**
+ * Array-like reduce for objects.
+ *
+ * @param {Object} object
+ * The Object that you want to reduce.
+ *
+ * @param {Function} fn
+ * A callback function which is called for each key in the object. It
+ * receives the accumulated value and the per-iteration value and key
+ * as arguments.
+ *
+ * @param {Mixed} [initial = 0]
+ * Starting value
+ *
+ * @return {Mixed}
+ * The final accumulated value.
+ */
+
+ function reduce(object, fn, initial) {
+ if (initial === void 0) {
+ initial = 0;
+ }
+
+ return keys(object).reduce(function (accum, key) {
+ return fn(accum, object[key], key);
+ }, initial);
+ }
+ /**
+ * Object.assign-style object shallow merge/extend.
+ *
+ * @param {Object} target
+ * @param {Object} ...sources
+ * @return {Object}
+ */
+
+ function assign(target) {
+ for (var _len = arguments.length, sources = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+ sources[_key - 1] = arguments[_key];
+ }
+
+ if (Object.assign) {
+ return Object.assign.apply(Object, [target].concat(sources));
+ }
+
+ sources.forEach(function (source) {
+ if (!source) {
+ return;
+ }
+
+ each(source, function (value, key) {
+ target[key] = value;
+ });
+ });
+ return target;
+ }
+ /**
+ * Returns whether a value is an object of any kind - including DOM nodes,
+ * arrays, regular expressions, etc. Not functions, though.
+ *
+ * This avoids the gotcha where using `typeof` on a `null` value
+ * results in `'object'`.
+ *
+ * @param {Object} value
+ * @return {boolean}
+ */
+
+ function isObject(value) {
+ return !!value && typeof value === 'object';
+ }
+ /**
+ * Returns whether an object appears to be a "plain" object - that is, a
+ * direct instance of `Object`.
+ *
+ * @param {Object} value
+ * @return {boolean}
+ */
+
+ function isPlain(value) {
+ return isObject(value) && toString.call(value) === '[object Object]' && value.constructor === Object;
+ }
+
+ /**
+ * @file computed-style.js
+ * @module computed-style
+ */
+ /**
+ * A safe getComputedStyle.
+ *
+ * This is needed because in Firefox, if the player is loaded in an iframe with
+ * `display:none`, then `getComputedStyle` returns `null`, so, we do a
+ * null-check to make sure that the player doesn't break in these cases.
+ *
+ * @function
+ * @param {Element} el
+ * The element you want the computed style of
+ *
+ * @param {string} prop
+ * The property name you want
+ *
+ * @see https://bugzilla.mozilla.org/show_bug.cgi?id=548397
+ */
+
+ function computedStyle(el, prop) {
+ if (!el || !prop) {
+ return '';
+ }
+
+ if (typeof window$1.getComputedStyle === 'function') {
+ var cs = window$1.getComputedStyle(el);
+ return cs ? cs[prop] : '';
+ }
+
+ return '';
+ }
+
+ function _templateObject() {
+ var data = _taggedTemplateLiteralLoose(["Setting attributes in the second argument of createEl()\n has been deprecated. Use the third argument instead.\n createEl(type, properties, attributes). Attempting to set ", " to ", "."]);
+
+ _templateObject = function _templateObject() {
+ return data;
+ };
+
+ return data;
+ }
+ /**
+ * Detect if a value is a string with any non-whitespace characters.
+ *
+ * @private
+ * @param {string} str
+ * The string to check
+ *
+ * @return {boolean}
+ * Will be `true` if the string is non-blank, `false` otherwise.
+ *
+ */
+
+ function isNonBlankString(str) {
+ return typeof str === 'string' && /\S/.test(str);
+ }
+ /**
+ * Throws an error if the passed string has whitespace. This is used by
+ * class methods to be relatively consistent with the classList API.
+ *
+ * @private
+ * @param {string} str
+ * The string to check for whitespace.
+ *
+ * @throws {Error}
+ * Throws an error if there is whitespace in the string.
+ */
+
+
+ function throwIfWhitespace(str) {
+ if (/\s/.test(str)) {
+ throw new Error('class has illegal whitespace characters');
+ }
+ }
+ /**
+ * Produce a regular expression for matching a className within an elements className.
+ *
+ * @private
+ * @param {string} className
+ * The className to generate the RegExp for.
+ *
+ * @return {RegExp}
+ * The RegExp that will check for a specific `className` in an elements
+ * className.
+ */
+
+
+ function classRegExp(className) {
+ return new RegExp('(^|\\s)' + className + '($|\\s)');
+ }
+ /**
+ * Whether the current DOM interface appears to be real (i.e. not simulated).
+ *
+ * @return {boolean}
+ * Will be `true` if the DOM appears to be real, `false` otherwise.
+ */
+
+
+ function isReal() {
+ // Both document and window will never be undefined thanks to `global`.
+ return document === window$1.document;
+ }
+ /**
+ * Determines, via duck typing, whether or not a value is a DOM element.
+ *
+ * @param {Mixed} value
+ * The value to check.
+ *
+ * @return {boolean}
+ * Will be `true` if the value is a DOM element, `false` otherwise.
+ */
+
+ function isEl(value) {
+ return isObject(value) && value.nodeType === 1;
+ }
+ /**
+ * Determines if the current DOM is embedded in an iframe.
+ *
+ * @return {boolean}
+ * Will be `true` if the DOM is embedded in an iframe, `false`
+ * otherwise.
+ */
+
+ function isInFrame() {
+ // We need a try/catch here because Safari will throw errors when attempting
+ // to get either `parent` or `self`
+ try {
+ return window$1.parent !== window$1.self;
+ } catch (x) {
+ return true;
+ }
+ }
+ /**
+ * Creates functions to query the DOM using a given method.
+ *
+ * @private
+ * @param {string} method
+ * The method to create the query with.
+ *
+ * @return {Function}
+ * The query method
+ */
+
+ function createQuerier(method) {
+ return function (selector, context) {
+ if (!isNonBlankString(selector)) {
+ return document[method](null);
+ }
+
+ if (isNonBlankString(context)) {
+ context = document.querySelector(context);
+ }
+
+ var ctx = isEl(context) ? context : document;
+ return ctx[method] && ctx[method](selector);
+ };
+ }
+ /**
+ * Creates an element and applies properties, attributes, and inserts content.
+ *
+ * @param {string} [tagName='div']
+ * Name of tag to be created.
+ *
+ * @param {Object} [properties={}]
+ * Element properties to be applied.
+ *
+ * @param {Object} [attributes={}]
+ * Element attributes to be applied.
+ *
+ * @param {module:dom~ContentDescriptor} content
+ * A content descriptor object.
+ *
+ * @return {Element}
+ * The element that was created.
+ */
+
+
+ function createEl(tagName, properties, attributes, content) {
+ if (tagName === void 0) {
+ tagName = 'div';
+ }
+
+ if (properties === void 0) {
+ properties = {};
+ }
+
+ if (attributes === void 0) {
+ attributes = {};
+ }
+
+ var el = document.createElement(tagName);
+ Object.getOwnPropertyNames(properties).forEach(function (propName) {
+ var val = properties[propName]; // See #2176
+ // We originally were accepting both properties and attributes in the
+ // same object, but that doesn't work so well.
+
+ if (propName.indexOf('aria-') !== -1 || propName === 'role' || propName === 'type') {
+ log.warn(tsml(_templateObject(), propName, val));
+ el.setAttribute(propName, val); // Handle textContent since it's not supported everywhere and we have a
+ // method for it.
+ } else if (propName === 'textContent') {
+ textContent(el, val);
+ } else {
+ el[propName] = val;
+ }
+ });
+ Object.getOwnPropertyNames(attributes).forEach(function (attrName) {
+ el.setAttribute(attrName, attributes[attrName]);
+ });
+
+ if (content) {
+ appendContent(el, content);
+ }
+
+ return el;
+ }
+ /**
+ * Injects text into an element, replacing any existing contents entirely.
+ *
+ * @param {Element} el
+ * The element to add text content into
+ *
+ * @param {string} text
+ * The text content to add.
+ *
+ * @return {Element}
+ * The element with added text content.
+ */
+
+ function textContent(el, text) {
+ if (typeof el.textContent === 'undefined') {
+ el.innerText = text;
+ } else {
+ el.textContent = text;
+ }
+
+ return el;
+ }
+ /**
+ * Insert an element as the first child node of another
+ *
+ * @param {Element} child
+ * Element to insert
+ *
+ * @param {Element} parent
+ * Element to insert child into
+ */
+
+ function prependTo(child, parent) {
+ if (parent.firstChild) {
+ parent.insertBefore(child, parent.firstChild);
+ } else {
+ parent.appendChild(child);
+ }
+ }
+ /**
+ * Check if an element has a class name.
+ *
+ * @param {Element} element
+ * Element to check
+ *
+ * @param {string} classToCheck
+ * Class name to check for
+ *
+ * @return {boolean}
+ * Will be `true` if the element has a class, `false` otherwise.
+ *
+ * @throws {Error}
+ * Throws an error if `classToCheck` has white space.
+ */
+
+ function hasClass(element, classToCheck) {
+ throwIfWhitespace(classToCheck);
+
+ if (element.classList) {
+ return element.classList.contains(classToCheck);
+ }
+
+ return classRegExp(classToCheck).test(element.className);
+ }
+ /**
+ * Add a class name to an element.
+ *
+ * @param {Element} element
+ * Element to add class name to.
+ *
+ * @param {string} classToAdd
+ * Class name to add.
+ *
+ * @return {Element}
+ * The DOM element with the added class name.
+ */
+
+ function addClass(element, classToAdd) {
+ if (element.classList) {
+ element.classList.add(classToAdd); // Don't need to `throwIfWhitespace` here because `hasElClass` will do it
+ // in the case of classList not being supported.
+ } else if (!hasClass(element, classToAdd)) {
+ element.className = (element.className + ' ' + classToAdd).trim();
+ }
+
+ return element;
+ }
+ /**
+ * Remove a class name from an element.
+ *
+ * @param {Element} element
+ * Element to remove a class name from.
+ *
+ * @param {string} classToRemove
+ * Class name to remove
+ *
+ * @return {Element}
+ * The DOM element with class name removed.
+ */
+
+ function removeClass(element, classToRemove) {
+ if (element.classList) {
+ element.classList.remove(classToRemove);
+ } else {
+ throwIfWhitespace(classToRemove);
+ element.className = element.className.split(/\s+/).filter(function (c) {
+ return c !== classToRemove;
+ }).join(' ');
+ }
+
+ return element;
+ }
+ /**
+ * The callback definition for toggleClass.
+ *
+ * @callback module:dom~PredicateCallback
+ * @param {Element} element
+ * The DOM element of the Component.
+ *
+ * @param {string} classToToggle
+ * The `className` that wants to be toggled
+ *
+ * @return {boolean|undefined}
+ * If `true` is returned, the `classToToggle` will be added to the
+ * `element`. If `false`, the `classToToggle` will be removed from
+ * the `element`. If `undefined`, the callback will be ignored.
+ */
+
+ /**
+ * Adds or removes a class name to/from an element depending on an optional
+ * condition or the presence/absence of the class name.
+ *
+ * @param {Element} element
+ * The element to toggle a class name on.
+ *
+ * @param {string} classToToggle
+ * The class that should be toggled.
+ *
+ * @param {boolean|module:dom~PredicateCallback} [predicate]
+ * See the return value for {@link module:dom~PredicateCallback}
+ *
+ * @return {Element}
+ * The element with a class that has been toggled.
+ */
+
+ function toggleClass(element, classToToggle, predicate) {
+ // This CANNOT use `classList` internally because IE11 does not support the
+ // second parameter to the `classList.toggle()` method! Which is fine because
+ // `classList` will be used by the add/remove functions.
+ var has = hasClass(element, classToToggle);
+
+ if (typeof predicate === 'function') {
+ predicate = predicate(element, classToToggle);
+ }
+
+ if (typeof predicate !== 'boolean') {
+ predicate = !has;
+ } // If the necessary class operation matches the current state of the
+ // element, no action is required.
+
+
+ if (predicate === has) {
+ return;
+ }
+
+ if (predicate) {
+ addClass(element, classToToggle);
+ } else {
+ removeClass(element, classToToggle);
+ }
+
+ return element;
+ }
+ /**
+ * Apply attributes to an HTML element.
+ *
+ * @param {Element} el
+ * Element to add attributes to.
+ *
+ * @param {Object} [attributes]
+ * Attributes to be applied.
+ */
+
+ function setAttributes(el, attributes) {
+ Object.getOwnPropertyNames(attributes).forEach(function (attrName) {
+ var attrValue = attributes[attrName];
+
+ if (attrValue === null || typeof attrValue === 'undefined' || attrValue === false) {
+ el.removeAttribute(attrName);
+ } else {
+ el.setAttribute(attrName, attrValue === true ? '' : attrValue);
+ }
+ });
+ }
+ /**
+ * Get an element's attribute values, as defined on the HTML tag.
+ *
+ * Attributes are not the same as properties. They're defined on the tag
+ * or with setAttribute.
+ *
+ * @param {Element} tag
+ * Element from which to get tag attributes.
+ *
+ * @return {Object}
+ * All attributes of the element. Boolean attributes will be `true` or
+ * `false`, others will be strings.
+ */
+
+ function getAttributes(tag) {
+ var obj = {}; // known boolean attributes
+ // we can check for matching boolean properties, but not all browsers
+ // and not all tags know about these attributes, so, we still want to check them manually
+
+ var knownBooleans = ',' + 'autoplay,controls,playsinline,loop,muted,default,defaultMuted' + ',';
+
+ if (tag && tag.attributes && tag.attributes.length > 0) {
+ var attrs = tag.attributes;
+
+ for (var i = attrs.length - 1; i >= 0; i--) {
+ var attrName = attrs[i].name;
+ var attrVal = attrs[i].value; // check for known booleans
+ // the matching element property will return a value for typeof
+
+ if (typeof tag[attrName] === 'boolean' || knownBooleans.indexOf(',' + attrName + ',') !== -1) {
+ // the value of an included boolean attribute is typically an empty
+ // string ('') which would equal false if we just check for a false value.
+ // we also don't want support bad code like autoplay='false'
+ attrVal = attrVal !== null ? true : false;
+ }
+
+ obj[attrName] = attrVal;
+ }
+ }
+
+ return obj;
+ }
+ /**
+ * Get the value of an element's attribute.
+ *
+ * @param {Element} el
+ * A DOM element.
+ *
+ * @param {string} attribute
+ * Attribute to get the value of.
+ *
+ * @return {string}
+ * The value of the attribute.
+ */
+
+ function getAttribute(el, attribute) {
+ return el.getAttribute(attribute);
+ }
+ /**
+ * Set the value of an element's attribute.
+ *
+ * @param {Element} el
+ * A DOM element.
+ *
+ * @param {string} attribute
+ * Attribute to set.
+ *
+ * @param {string} value
+ * Value to set the attribute to.
+ */
+
+ function setAttribute(el, attribute, value) {
+ el.setAttribute(attribute, value);
+ }
+ /**
+ * Remove an element's attribute.
+ *
+ * @param {Element} el
+ * A DOM element.
+ *
+ * @param {string} attribute
+ * Attribute to remove.
+ */
+
+ function removeAttribute(el, attribute) {
+ el.removeAttribute(attribute);
+ }
+ /**
+ * Attempt to block the ability to select text.
+ */
+
+ function blockTextSelection() {
+ document.body.focus();
+
+ document.onselectstart = function () {
+ return false;
+ };
+ }
+ /**
+ * Turn off text selection blocking.
+ */
+
+ function unblockTextSelection() {
+ document.onselectstart = function () {
+ return true;
+ };
+ }
+ /**
+ * Identical to the native `getBoundingClientRect` function, but ensures that
+ * the method is supported at all (it is in all browsers we claim to support)
+ * and that the element is in the DOM before continuing.
+ *
+ * This wrapper function also shims properties which are not provided by some
+ * older browsers (namely, IE8).
+ *
+ * Additionally, some browsers do not support adding properties to a
+ * `ClientRect`/`DOMRect` object; so, we shallow-copy it with the standard
+ * properties (except `x` and `y` which are not widely supported). This helps
+ * avoid implementations where keys are non-enumerable.
+ *
+ * @param {Element} el
+ * Element whose `ClientRect` we want to calculate.
+ *
+ * @return {Object|undefined}
+ * Always returns a plain object - or `undefined` if it cannot.
+ */
+
+ function getBoundingClientRect(el) {
+ if (el && el.getBoundingClientRect && el.parentNode) {
+ var rect = el.getBoundingClientRect();
+ var result = {};
+ ['bottom', 'height', 'left', 'right', 'top', 'width'].forEach(function (k) {
+ if (rect[k] !== undefined) {
+ result[k] = rect[k];
+ }
+ });
+
+ if (!result.height) {
+ result.height = parseFloat(computedStyle(el, 'height'));
+ }
+
+ if (!result.width) {
+ result.width = parseFloat(computedStyle(el, 'width'));
+ }
+
+ return result;
+ }
+ }
+ /**
+ * Represents the position of a DOM element on the page.
+ *
+ * @typedef {Object} module:dom~Position
+ *
+ * @property {number} left
+ * Pixels to the left.
+ *
+ * @property {number} top
+ * Pixels from the top.
+ */
+
+ /**
+ * Get the position of an element in the DOM.
+ *
+ * Uses `getBoundingClientRect` technique from John Resig.
+ *
+ * @see http://ejohn.org/blog/getboundingclientrect-is-awesome/
+ *
+ * @param {Element} el
+ * Element from which to get offset.
+ *
+ * @return {module:dom~Position}
+ * The position of the element that was passed in.
+ */
+
+ function findPosition(el) {
+ var box;
+
+ if (el.getBoundingClientRect && el.parentNode) {
+ box = el.getBoundingClientRect();
+ }
+
+ if (!box) {
+ return {
+ left: 0,
+ top: 0
+ };
+ }
+
+ var docEl = document.documentElement;
+ var body = document.body;
+ var clientLeft = docEl.clientLeft || body.clientLeft || 0;
+ var scrollLeft = window$1.pageXOffset || body.scrollLeft;
+ var left = box.left + scrollLeft - clientLeft;
+ var clientTop = docEl.clientTop || body.clientTop || 0;
+ var scrollTop = window$1.pageYOffset || body.scrollTop;
+ var top = box.top + scrollTop - clientTop; // Android sometimes returns slightly off decimal values, so need to round
+
+ return {
+ left: Math.round(left),
+ top: Math.round(top)
+ };
+ }
+ /**
+ * Represents x and y coordinates for a DOM element or mouse pointer.
+ *
+ * @typedef {Object} module:dom~Coordinates
+ *
+ * @property {number} x
+ * x coordinate in pixels
+ *
+ * @property {number} y
+ * y coordinate in pixels
+ */
+
+ /**
+ * Get the pointer position within an element.
+ *
+ * The base on the coordinates are the bottom left of the element.
+ *
+ * @param {Element} el
+ * Element on which to get the pointer position on.
+ *
+ * @param {EventTarget~Event} event
+ * Event object.
+ *
+ * @return {module:dom~Coordinates}
+ * A coordinates object corresponding to the mouse position.
+ *
+ */
+
+ function getPointerPosition(el, event) {
+ var position = {};
+ var box = findPosition(el);
+ var boxW = el.offsetWidth;
+ var boxH = el.offsetHeight;
+ var boxY = box.top;
+ var boxX = box.left;
+ var pageY = event.pageY;
+ var pageX = event.pageX;
+
+ if (event.changedTouches) {
+ pageX = event.changedTouches[0].pageX;
+ pageY = event.changedTouches[0].pageY;
+ }
+
+ position.y = Math.max(0, Math.min(1, (boxY - pageY + boxH) / boxH));
+ position.x = Math.max(0, Math.min(1, (pageX - boxX) / boxW));
+ return position;
+ }
+ /**
+ * Determines, via duck typing, whether or not a value is a text node.
+ *
+ * @param {Mixed} value
+ * Check if this value is a text node.
+ *
+ * @return {boolean}
+ * Will be `true` if the value is a text node, `false` otherwise.
+ */
+
+ function isTextNode(value) {
+ return isObject(value) && value.nodeType === 3;
+ }
+ /**
+ * Empties the contents of an element.
+ *
+ * @param {Element} el
+ * The element to empty children from
+ *
+ * @return {Element}
+ * The element with no children
+ */
+
+ function emptyEl(el) {
+ while (el.firstChild) {
+ el.removeChild(el.firstChild);
+ }
+
+ return el;
+ }
+ /**
+ * This is a mixed value that describes content to be injected into the DOM
+ * via some method. It can be of the following types:
+ *
+ * Type | Description
+ * -----------|-------------
+ * `string` | The value will be normalized into a text node.
+ * `Element` | The value will be accepted as-is.
+ * `TextNode` | The value will be accepted as-is.
+ * `Array` | A one-dimensional array of strings, elements, text nodes, or functions. These functions should return a string, element, or text node (any other return value, like an array, will be ignored).
+ * `Function` | A function, which is expected to return a string, element, text node, or array - any of the other possible values described above. This means that a content descriptor could be a function that returns an array of functions, but those second-level functions must return strings, elements, or text nodes.
+ *
+ * @typedef {string|Element|TextNode|Array|Function} module:dom~ContentDescriptor
+ */
+
+ /**
+ * Normalizes content for eventual insertion into the DOM.
+ *
+ * This allows a wide range of content definition methods, but helps protect
+ * from falling into the trap of simply writing to `innerHTML`, which could
+ * be an XSS concern.
+ *
+ * The content for an element can be passed in multiple types and
+ * combinations, whose behavior is as follows:
+ *
+ * @param {module:dom~ContentDescriptor} content
+ * A content descriptor value.
+ *
+ * @return {Array}
+ * All of the content that was passed in, normalized to an array of
+ * elements or text nodes.
+ */
+
+ function normalizeContent(content) {
+ // First, invoke content if it is a function. If it produces an array,
+ // that needs to happen before normalization.
+ if (typeof content === 'function') {
+ content = content();
+ } // Next up, normalize to an array, so one or many items can be normalized,
+ // filtered, and returned.
+
+
+ return (Array.isArray(content) ? content : [content]).map(function (value) {
+ // First, invoke value if it is a function to produce a new value,
+ // which will be subsequently normalized to a Node of some kind.
+ if (typeof value === 'function') {
+ value = value();
+ }
+
+ if (isEl(value) || isTextNode(value)) {
+ return value;
+ }
+
+ if (typeof value === 'string' && /\S/.test(value)) {
+ return document.createTextNode(value);
+ }
+ }).filter(function (value) {
+ return value;
+ });
+ }
+ /**
+ * Normalizes and appends content to an element.
+ *
+ * @param {Element} el
+ * Element to append normalized content to.
+ *
+ * @param {module:dom~ContentDescriptor} content
+ * A content descriptor value.
+ *
+ * @return {Element}
+ * The element with appended normalized content.
+ */
+
+ function appendContent(el, content) {
+ normalizeContent(content).forEach(function (node) {
+ return el.appendChild(node);
+ });
+ return el;
+ }
+ /**
+ * Normalizes and inserts content into an element; this is identical to
+ * `appendContent()`, except it empties the element first.
+ *
+ * @param {Element} el
+ * Element to insert normalized content into.
+ *
+ * @param {module:dom~ContentDescriptor} content
+ * A content descriptor value.
+ *
+ * @return {Element}
+ * The element with inserted normalized content.
+ */
+
+ function insertContent(el, content) {
+ return appendContent(emptyEl(el), content);
+ }
+ /**
+ * Check if an event was a single left click.
+ *
+ * @param {EventTarget~Event} event
+ * Event object.
+ *
+ * @return {boolean}
+ * Will be `true` if a single left click, `false` otherwise.
+ */
+
+ function isSingleLeftClick(event) {
+ // Note: if you create something draggable, be sure to
+ // call it on both `mousedown` and `mousemove` event,
+ // otherwise `mousedown` should be enough for a button
+ if (event.button === undefined && event.buttons === undefined) {
+ // Why do we need `buttons` ?
+ // Because, middle mouse sometimes have this:
+ // e.button === 0 and e.buttons === 4
+ // Furthermore, we want to prevent combination click, something like
+ // HOLD middlemouse then left click, that would be
+ // e.button === 0, e.buttons === 5
+ // just `button` is not gonna work
+ // Alright, then what this block does ?
+ // this is for chrome `simulate mobile devices`
+ // I want to support this as well
+ return true;
+ }
+
+ if (event.button === 0 && event.buttons === undefined) {
+ // Touch screen, sometimes on some specific device, `buttons`
+ // doesn't have anything (safari on ios, blackberry...)
+ return true;
+ }
+
+ if (event.button !== 0 || event.buttons !== 1) {
+ // This is the reason we have those if else block above
+ // if any special case we can catch and let it slide
+ // we do it above, when get to here, this definitely
+ // is-not-left-click
+ return false;
+ }
+
+ return true;
+ }
+ /**
+ * Finds a single DOM element matching `selector` within the optional
+ * `context` of another DOM element (defaulting to `document`).
+ *
+ * @param {string} selector
+ * A valid CSS selector, which will be passed to `querySelector`.
+ *
+ * @param {Element|String} [context=document]
+ * A DOM element within which to query. Can also be a selector
+ * string in which case the first matching element will be used
+ * as context. If missing (or no element matches selector), falls
+ * back to `document`.
+ *
+ * @return {Element|null}
+ * The element that was found or null.
+ */
+
+ var $ = createQuerier('querySelector');
+ /**
+ * Finds a all DOM elements matching `selector` within the optional
+ * `context` of another DOM element (defaulting to `document`).
+ *
+ * @param {string} selector
+ * A valid CSS selector, which will be passed to `querySelectorAll`.
+ *
+ * @param {Element|String} [context=document]
+ * A DOM element within which to query. Can also be a selector
+ * string in which case the first matching element will be used
+ * as context. If missing (or no element matches selector), falls
+ * back to `document`.
+ *
+ * @return {NodeList}
+ * A element list of elements that were found. Will be empty if none
+ * were found.
+ *
+ */
+
+ var $$ = createQuerier('querySelectorAll');
+
+ var Dom = /*#__PURE__*/Object.freeze({
+ isReal: isReal,
+ isEl: isEl,
+ isInFrame: isInFrame,
+ createEl: createEl,
+ textContent: textContent,
+ prependTo: prependTo,
+ hasClass: hasClass,
+ addClass: addClass,
+ removeClass: removeClass,
+ toggleClass: toggleClass,
+ setAttributes: setAttributes,
+ getAttributes: getAttributes,
+ getAttribute: getAttribute,
+ setAttribute: setAttribute,
+ removeAttribute: removeAttribute,
+ blockTextSelection: blockTextSelection,
+ unblockTextSelection: unblockTextSelection,
+ getBoundingClientRect: getBoundingClientRect,
+ findPosition: findPosition,
+ getPointerPosition: getPointerPosition,
+ isTextNode: isTextNode,
+ emptyEl: emptyEl,
+ normalizeContent: normalizeContent,
+ appendContent: appendContent,
+ insertContent: insertContent,
+ isSingleLeftClick: isSingleLeftClick,
+ $: $,
+ $$: $$
+ });
+
+ /**
+ * @file guid.js
+ * @module guid
+ */
+
+ /**
+ * Unique ID for an element or function
+ * @type {Number}
+ */
+ var _guid = 1;
+ /**
+ * Get a unique auto-incrementing ID by number that has not been returned before.
+ *
+ * @return {number}
+ * A new unique ID.
+ */
+
+ function newGUID() {
+ return _guid++;
+ }
+
+ /**
+ * @file dom-data.js
+ * @module dom-data
+ */
+ /**
+ * Element Data Store.
+ *
+ * Allows for binding data to an element without putting it directly on the
+ * element. Ex. Event listeners are stored here.
+ * (also from jsninja.com, slightly modified and updated for closure compiler)
+ *
+ * @type {Object}
+ * @private
+ */
+
+ var elData = {};
+ /*
+ * Unique attribute name to store an element's guid in
+ *
+ * @type {String}
+ * @constant
+ * @private
+ */
+
+ var elIdAttr = 'vdata' + new Date().getTime();
+ /**
+ * Returns the cache object where data for an element is stored
+ *
+ * @param {Element} el
+ * Element to store data for.
+ *
+ * @return {Object}
+ * The cache object for that el that was passed in.
+ */
+
+ function getData(el) {
+ var id = el[elIdAttr];
+
+ if (!id) {
+ id = el[elIdAttr] = newGUID();
+ }
+
+ if (!elData[id]) {
+ elData[id] = {};
+ }
+
+ return elData[id];
+ }
+ /**
+ * Returns whether or not an element has cached data
+ *
+ * @param {Element} el
+ * Check if this element has cached data.
+ *
+ * @return {boolean}
+ * - True if the DOM element has cached data.
+ * - False otherwise.
+ */
+
+ function hasData(el) {
+ var id = el[elIdAttr];
+
+ if (!id) {
+ return false;
+ }
+
+ return !!Object.getOwnPropertyNames(elData[id]).length;
+ }
+ /**
+ * Delete data for the element from the cache and the guid attr from getElementById
+ *
+ * @param {Element} el
+ * Remove cached data for this element.
+ */
+
+ function removeData(el) {
+ var id = el[elIdAttr];
+
+ if (!id) {
+ return;
+ } // Remove all stored data
+
+
+ delete elData[id]; // Remove the elIdAttr property from the DOM node
+
+ try {
+ delete el[elIdAttr];
+ } catch (e) {
+ if (el.removeAttribute) {
+ el.removeAttribute(elIdAttr);
+ } else {
+ // IE doesn't appear to support removeAttribute on the document element
+ el[elIdAttr] = null;
+ }
+ }
+ }
+
+ /**
+ * @file events.js. An Event System (John Resig - Secrets of a JS Ninja http://jsninja.com/)
+ * (Original book version wasn't completely usable, so fixed some things and made Closure Compiler compatible)
+ * This should work very similarly to jQuery's events, however it's based off the book version which isn't as
+ * robust as jquery's, so there's probably some differences.
+ *
+ * @file events.js
+ * @module events
+ */
+ /**
+ * Clean up the listener cache and dispatchers
+ *
+ * @param {Element|Object} elem
+ * Element to clean up
+ *
+ * @param {string} type
+ * Type of event to clean up
+ */
+
+ function _cleanUpEvents(elem, type) {
+ var data = getData(elem); // Remove the events of a particular type if there are none left
+
+ if (data.handlers[type].length === 0) {
+ delete data.handlers[type]; // data.handlers[type] = null;
+ // Setting to null was causing an error with data.handlers
+ // Remove the meta-handler from the element
+
+ if (elem.removeEventListener) {
+ elem.removeEventListener(type, data.dispatcher, false);
+ } else if (elem.detachEvent) {
+ elem.detachEvent('on' + type, data.dispatcher);
+ }
+ } // Remove the events object if there are no types left
+
+
+ if (Object.getOwnPropertyNames(data.handlers).length <= 0) {
+ delete data.handlers;
+ delete data.dispatcher;
+ delete data.disabled;
+ } // Finally remove the element data if there is no data left
+
+
+ if (Object.getOwnPropertyNames(data).length === 0) {
+ removeData(elem);
+ }
+ }
+ /**
+ * Loops through an array of event types and calls the requested method for each type.
+ *
+ * @param {Function} fn
+ * The event method we want to use.
+ *
+ * @param {Element|Object} elem
+ * Element or object to bind listeners to
+ *
+ * @param {string} type
+ * Type of event to bind to.
+ *
+ * @param {EventTarget~EventListener} callback
+ * Event listener.
+ */
+
+
+ function _handleMultipleEvents(fn, elem, types, callback) {
+ types.forEach(function (type) {
+ // Call the event method for each one of the types
+ fn(elem, type, callback);
+ });
+ }
+ /**
+ * Fix a native event to have standard property values
+ *
+ * @param {Object} event
+ * Event object to fix.
+ *
+ * @return {Object}
+ * Fixed event object.
+ */
+
+
+ function fixEvent(event) {
+ function returnTrue() {
+ return true;
+ }
+
+ function returnFalse() {
+ return false;
+ } // Test if fixing up is needed
+ // Used to check if !event.stopPropagation instead of isPropagationStopped
+ // But native events return true for stopPropagation, but don't have
+ // other expected methods like isPropagationStopped. Seems to be a problem
+ // with the Javascript Ninja code. So we're just overriding all events now.
+
+
+ if (!event || !event.isPropagationStopped) {
+ var old = event || window$1.event;
+ event = {}; // Clone the old object so that we can modify the values event = {};
+ // IE8 Doesn't like when you mess with native event properties
+ // Firefox returns false for event.hasOwnProperty('type') and other props
+ // which makes copying more difficult.
+ // TODO: Probably best to create a whitelist of event props
+
+ for (var key in old) {
+ // Safari 6.0.3 warns you if you try to copy deprecated layerX/Y
+ // Chrome warns you if you try to copy deprecated keyboardEvent.keyLocation
+ // and webkitMovementX/Y
+ if (key !== 'layerX' && key !== 'layerY' && key !== 'keyLocation' && key !== 'webkitMovementX' && key !== 'webkitMovementY') {
+ // Chrome 32+ warns if you try to copy deprecated returnValue, but
+ // we still want to if preventDefault isn't supported (IE8).
+ if (!(key === 'returnValue' && old.preventDefault)) {
+ event[key] = old[key];
+ }
+ }
+ } // The event occurred on this element
+
+
+ if (!event.target) {
+ event.target = event.srcElement || document;
+ } // Handle which other element the event is related to
+
+
+ if (!event.relatedTarget) {
+ event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
+ } // Stop the default browser action
+
+
+ event.preventDefault = function () {
+ if (old.preventDefault) {
+ old.preventDefault();
+ }
+
+ event.returnValue = false;
+ old.returnValue = false;
+ event.defaultPrevented = true;
+ };
+
+ event.defaultPrevented = false; // Stop the event from bubbling
+
+ event.stopPropagation = function () {
+ if (old.stopPropagation) {
+ old.stopPropagation();
+ }
+
+ event.cancelBubble = true;
+ old.cancelBubble = true;
+ event.isPropagationStopped = returnTrue;
+ };
+
+ event.isPropagationStopped = returnFalse; // Stop the event from bubbling and executing other handlers
+
+ event.stopImmediatePropagation = function () {
+ if (old.stopImmediatePropagation) {
+ old.stopImmediatePropagation();
+ }
+
+ event.isImmediatePropagationStopped = returnTrue;
+ event.stopPropagation();
+ };
+
+ event.isImmediatePropagationStopped = returnFalse; // Handle mouse position
+
+ if (event.clientX !== null && event.clientX !== undefined) {
+ var doc = document.documentElement;
+ var body = document.body;
+ event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
+ event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
+ } // Handle key presses
+
+
+ event.which = event.charCode || event.keyCode; // Fix button for mouse clicks:
+ // 0 == left; 1 == middle; 2 == right
+
+ if (event.button !== null && event.button !== undefined) {
+ // The following is disabled because it does not pass videojs-standard
+ // and... yikes.
+
+ /* eslint-disable */
+ event.button = event.button & 1 ? 0 : event.button & 4 ? 1 : event.button & 2 ? 2 : 0;
+ /* eslint-enable */
+ }
+ } // Returns fixed-up instance
+
+
+ return event;
+ }
+ /**
+ * Whether passive event listeners are supported
+ */
+
+ var _supportsPassive = false;
+
+ (function () {
+ try {
+ var opts = Object.defineProperty({}, 'passive', {
+ get: function get() {
+ _supportsPassive = true;
+ }
+ });
+ window$1.addEventListener('test', null, opts);
+ window$1.removeEventListener('test', null, opts);
+ } catch (e) {// disregard
+ }
+ })();
+ /**
+ * Touch events Chrome expects to be passive
+ */
+
+
+ var passiveEvents = ['touchstart', 'touchmove'];
+ /**
+ * Add an event listener to element
+ * It stores the handler function in a separate cache object
+ * and adds a generic handler to the element's event,
+ * along with a unique id (guid) to the element.
+ *
+ * @param {Element|Object} elem
+ * Element or object to bind listeners to
+ *
+ * @param {string|string[]} type
+ * Type of event to bind to.
+ *
+ * @param {EventTarget~EventListener} fn
+ * Event listener.
+ */
+
+ function on(elem, type, fn) {
+ if (Array.isArray(type)) {
+ return _handleMultipleEvents(on, elem, type, fn);
+ }
+
+ var data = getData(elem); // We need a place to store all our handler data
+
+ if (!data.handlers) {
+ data.handlers = {};
+ }
+
+ if (!data.handlers[type]) {
+ data.handlers[type] = [];
+ }
+
+ if (!fn.guid) {
+ fn.guid = newGUID();
+ }
+
+ data.handlers[type].push(fn);
+
+ if (!data.dispatcher) {
+ data.disabled = false;
+
+ data.dispatcher = function (event, hash) {
+ if (data.disabled) {
+ return;
+ }
+
+ event = fixEvent(event);
+ var handlers = data.handlers[event.type];
+
+ if (handlers) {
+ // Copy handlers so if handlers are added/removed during the process it doesn't throw everything off.
+ var handlersCopy = handlers.slice(0);
+
+ for (var m = 0, n = handlersCopy.length; m < n; m++) {
+ if (event.isImmediatePropagationStopped()) {
+ break;
+ } else {
+ try {
+ handlersCopy[m].call(elem, event, hash);
+ } catch (e) {
+ log.error(e);
+ }
+ }
+ }
+ }
+ };
+ }
+
+ if (data.handlers[type].length === 1) {
+ if (elem.addEventListener) {
+ var options = false;
+
+ if (_supportsPassive && passiveEvents.indexOf(type) > -1) {
+ options = {
+ passive: true
+ };
+ }
+
+ elem.addEventListener(type, data.dispatcher, options);
+ } else if (elem.attachEvent) {
+ elem.attachEvent('on' + type, data.dispatcher);
+ }
+ }
+ }
+ /**
+ * Removes event listeners from an element
+ *
+ * @param {Element|Object} elem
+ * Object to remove listeners from.
+ *
+ * @param {string|string[]} [type]
+ * Type of listener to remove. Don't include to remove all events from element.
+ *
+ * @param {EventTarget~EventListener} [fn]
+ * Specific listener to remove. Don't include to remove listeners for an event
+ * type.
+ */
+
+ function off(elem, type, fn) {
+ // Don't want to add a cache object through getElData if not needed
+ if (!hasData(elem)) {
+ return;
+ }
+
+ var data = getData(elem); // If no events exist, nothing to unbind
+
+ if (!data.handlers) {
+ return;
+ }
+
+ if (Array.isArray(type)) {
+ return _handleMultipleEvents(off, elem, type, fn);
+ } // Utility function
+
+
+ var removeType = function removeType(el, t) {
+ data.handlers[t] = [];
+
+ _cleanUpEvents(el, t);
+ }; // Are we removing all bound events?
+
+
+ if (type === undefined) {
+ for (var t in data.handlers) {
+ if (Object.prototype.hasOwnProperty.call(data.handlers || {}, t)) {
+ removeType(elem, t);
+ }
+ }
+
+ return;
+ }
+
+ var handlers = data.handlers[type]; // If no handlers exist, nothing to unbind
+
+ if (!handlers) {
+ return;
+ } // If no listener was provided, remove all listeners for type
+
+
+ if (!fn) {
+ removeType(elem, type);
+ return;
+ } // We're only removing a single handler
+
+
+ if (fn.guid) {
+ for (var n = 0; n < handlers.length; n++) {
+ if (handlers[n].guid === fn.guid) {
+ handlers.splice(n--, 1);
+ }
+ }
+ }
+
+ _cleanUpEvents(elem, type);
+ }
+ /**
+ * Trigger an event for an element
+ *
+ * @param {Element|Object} elem
+ * Element to trigger an event on
+ *
+ * @param {EventTarget~Event|string} event
+ * A string (the type) or an event object with a type attribute
+ *
+ * @param {Object} [hash]
+ * data hash to pass along with the event
+ *
+ * @return {boolean|undefined}
+ * Returns the opposite of `defaultPrevented` if default was
+ * prevented. Otherwise, returns `undefined`
+ */
+
+ function trigger(elem, event, hash) {
+ // Fetches element data and a reference to the parent (for bubbling).
+ // Don't want to add a data object to cache for every parent,
+ // so checking hasElData first.
+ var elemData = hasData(elem) ? getData(elem) : {};
+ var parent = elem.parentNode || elem.ownerDocument; // type = event.type || event,
+ // handler;
+ // If an event name was passed as a string, creates an event out of it
+
+ if (typeof event === 'string') {
+ event = {
+ type: event,
+ target: elem
+ };
+ } else if (!event.target) {
+ event.target = elem;
+ } // Normalizes the event properties.
+
+
+ event = fixEvent(event); // If the passed element has a dispatcher, executes the established handlers.
+
+ if (elemData.dispatcher) {
+ elemData.dispatcher.call(elem, event, hash);
+ } // Unless explicitly stopped or the event does not bubble (e.g. media events)
+ // recursively calls this function to bubble the event up the DOM.
+
+
+ if (parent && !event.isPropagationStopped() && event.bubbles === true) {
+ trigger.call(null, parent, event, hash); // If at the top of the DOM, triggers the default action unless disabled.
+ } else if (!parent && !event.defaultPrevented && event.target && event.target[event.type]) {
+ var targetData = getData(event.target); // Checks if the target has a default action for this event.
+
+ if (event.target[event.type]) {
+ // Temporarily disables event dispatching on the target as we have already executed the handler.
+ targetData.disabled = true; // Executes the default action.
+
+ if (typeof event.target[event.type] === 'function') {
+ event.target[event.type]();
+ } // Re-enables event dispatching.
+
+
+ targetData.disabled = false;
+ }
+ } // Inform the triggerer if the default was prevented by returning false
+
+
+ return !event.defaultPrevented;
+ }
+ /**
+ * Trigger a listener only once for an event.
+ *
+ * @param {Element|Object} elem
+ * Element or object to bind to.
+ *
+ * @param {string|string[]} type
+ * Name/type of event
+ *
+ * @param {Event~EventListener} fn
+ * Event listener function
+ */
+
+ function one(elem, type, fn) {
+ if (Array.isArray(type)) {
+ return _handleMultipleEvents(one, elem, type, fn);
+ }
+
+ var func = function func() {
+ off(elem, type, func);
+ fn.apply(this, arguments);
+ }; // copy the guid to the new function so it can removed using the original function's ID
+
+
+ func.guid = fn.guid = fn.guid || newGUID();
+ on(elem, type, func);
+ }
+
+ var Events = /*#__PURE__*/Object.freeze({
+ fixEvent: fixEvent,
+ on: on,
+ off: off,
+ trigger: trigger,
+ one: one
+ });
+
+ /**
+ * @file setup.js - Functions for setting up a player without
+ * user interaction based on the data-setup `attribute` of the video tag.
+ *
+ * @module setup
+ */
+ var _windowLoaded = false;
+ var videojs;
+ /**
+ * Set up any tags that have a data-setup `attribute` when the player is started.
+ */
+
+ var autoSetup = function autoSetup() {
+ // Protect against breakage in non-browser environments and check global autoSetup option.
+ if (!isReal() || videojs.options.autoSetup === false) {
+ return;
+ }
+
+ var vids = Array.prototype.slice.call(document.getElementsByTagName('video'));
+ var audios = Array.prototype.slice.call(document.getElementsByTagName('audio'));
+ var divs = Array.prototype.slice.call(document.getElementsByTagName('video-js'));
+ var mediaEls = vids.concat(audios, divs); // Check if any media elements exist
+
+ if (mediaEls && mediaEls.length > 0) {
+ for (var i = 0, e = mediaEls.length; i < e; i++) {
+ var mediaEl = mediaEls[i]; // Check if element exists, has getAttribute func.
+
+ if (mediaEl && mediaEl.getAttribute) {
+ // Make sure this player hasn't already been set up.
+ if (mediaEl.player === undefined) {
+ var options = mediaEl.getAttribute('data-setup'); // Check if data-setup attr exists.
+ // We only auto-setup if they've added the data-setup attr.
+
+ if (options !== null) {
+ // Create new video.js instance.
+ videojs(mediaEl);
+ }
+ } // If getAttribute isn't defined, we need to wait for the DOM.
+
+ } else {
+ autoSetupTimeout(1);
+ break;
+ }
+ } // No videos were found, so keep looping unless page is finished loading.
+
+ } else if (!_windowLoaded) {
+ autoSetupTimeout(1);
+ }
+ };
+ /**
+ * Wait until the page is loaded before running autoSetup. This will be called in
+ * autoSetup if `hasLoaded` returns false.
+ *
+ * @param {number} wait
+ * How long to wait in ms
+ *
+ * @param {module:videojs} [vjs]
+ * The videojs library function
+ */
+
+
+ function autoSetupTimeout(wait, vjs) {
+ if (vjs) {
+ videojs = vjs;
+ }
+
+ window$1.setTimeout(autoSetup, wait);
+ }
+
+ if (isReal() && document.readyState === 'complete') {
+ _windowLoaded = true;
+ } else {
+ /**
+ * Listen for the load event on window, and set _windowLoaded to true.
+ *
+ * @listens load
+ */
+ one(window$1, 'load', function () {
+ _windowLoaded = true;
+ });
+ }
+
+ /**
+ * @file stylesheet.js
+ * @module stylesheet
+ */
+ /**
+ * Create a DOM syle element given a className for it.
+ *
+ * @param {string} className
+ * The className to add to the created style element.
+ *
+ * @return {Element}
+ * The element that was created.
+ */
+
+ var createStyleElement = function createStyleElement(className) {
+ var style = document.createElement('style');
+ style.className = className;
+ return style;
+ };
+ /**
+ * Add text to a DOM element.
+ *
+ * @param {Element} el
+ * The Element to add text content to.
+ *
+ * @param {string} content
+ * The text to add to the element.
+ */
+
+ var setTextContent = function setTextContent(el, content) {
+ if (el.styleSheet) {
+ el.styleSheet.cssText = content;
+ } else {
+ el.textContent = content;
+ }
+ };
+
+ /**
+ * @file fn.js
+ * @module fn
+ */
+ /**
+ * Bind (a.k.a proxy or context). A simple method for changing the context of
+ * a function.
+ *
+ * It also stores a unique id on the function so it can be easily removed from
+ * events.
+ *
+ * @function
+ * @param {Mixed} context
+ * The object to bind as scope.
+ *
+ * @param {Function} fn
+ * The function to be bound to a scope.
+ *
+ * @param {number} [uid]
+ * An optional unique ID for the function to be set
+ *
+ * @return {Function}
+ * The new function that will be bound into the context given
+ */
+
+ var bind = function bind(context, fn, uid) {
+ // Make sure the function has a unique ID
+ if (!fn.guid) {
+ fn.guid = newGUID();
+ } // Create the new function that changes the context
+
+
+ var bound = function bound() {
+ return fn.apply(context, arguments);
+ }; // Allow for the ability to individualize this function
+ // Needed in the case where multiple objects might share the same prototype
+ // IF both items add an event listener with the same function, then you try to remove just one
+ // it will remove both because they both have the same guid.
+ // when using this, you need to use the bind method when you remove the listener as well.
+ // currently used in text tracks
+
+
+ bound.guid = uid ? uid + '_' + fn.guid : fn.guid;
+ return bound;
+ };
+ /**
+ * Wraps the given function, `fn`, with a new function that only invokes `fn`
+ * at most once per every `wait` milliseconds.
+ *
+ * @function
+ * @param {Function} fn
+ * The function to be throttled.
+ *
+ * @param {number} wait
+ * The number of milliseconds by which to throttle.
+ *
+ * @return {Function}
+ */
+
+ var throttle = function throttle(fn, wait) {
+ var last = Date.now();
+
+ var throttled = function throttled() {
+ var now = Date.now();
+
+ if (now - last >= wait) {
+ fn.apply(void 0, arguments);
+ last = now;
+ }
+ };
+
+ return throttled;
+ };
+ /**
+ * Creates a debounced function that delays invoking `func` until after `wait`
+ * milliseconds have elapsed since the last time the debounced function was
+ * invoked.
+ *
+ * Inspired by lodash and underscore implementations.
+ *
+ * @function
+ * @param {Function} func
+ * The function to wrap with debounce behavior.
+ *
+ * @param {number} wait
+ * The number of milliseconds to wait after the last invocation.
+ *
+ * @param {boolean} [immediate]
+ * Whether or not to invoke the function immediately upon creation.
+ *
+ * @param {Object} [context=window]
+ * The "context" in which the debounced function should debounce. For
+ * example, if this function should be tied to a Video.js player,
+ * the player can be passed here. Alternatively, defaults to the
+ * global `window` object.
+ *
+ * @return {Function}
+ * A debounced function.
+ */
+
+ var debounce = function debounce(func, wait, immediate, context) {
+ if (context === void 0) {
+ context = window$1;
+ }
+
+ var timeout;
+
+ var cancel = function cancel() {
+ context.clearTimeout(timeout);
+ timeout = null;
+ };
+ /* eslint-disable consistent-this */
+
+
+ var debounced = function debounced() {
+ var self = this;
+ var args = arguments;
+
+ var _later = function later() {
+ timeout = null;
+ _later = null;
+
+ if (!immediate) {
+ func.apply(self, args);
+ }
+ };
+
+ if (!timeout && immediate) {
+ func.apply(self, args);
+ }
+
+ context.clearTimeout(timeout);
+ timeout = context.setTimeout(_later, wait);
+ };
+ /* eslint-enable consistent-this */
+
+
+ debounced.cancel = cancel;
+ return debounced;
+ };
+
+ /**
+ * @file src/js/event-target.js
+ */
+ /**
+ * `EventTarget` is a class that can have the same API as the DOM `EventTarget`. It
+ * adds shorthand functions that wrap around lengthy functions. For example:
+ * the `on` function is a wrapper around `addEventListener`.
+ *
+ * @see [EventTarget Spec]{@link https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget}
+ * @class EventTarget
+ */
+
+ var EventTarget = function EventTarget() {};
+ /**
+ * A Custom DOM event.
+ *
+ * @typedef {Object} EventTarget~Event
+ * @see [Properties]{@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent}
+ */
+
+ /**
+ * All event listeners should follow the following format.
+ *
+ * @callback EventTarget~EventListener
+ * @this {EventTarget}
+ *
+ * @param {EventTarget~Event} event
+ * the event that triggered this function
+ *
+ * @param {Object} [hash]
+ * hash of data sent during the event
+ */
+
+ /**
+ * An object containing event names as keys and booleans as values.
+ *
+ * > NOTE: If an event name is set to a true value here {@link EventTarget#trigger}
+ * will have extra functionality. See that function for more information.
+ *
+ * @property EventTarget.prototype.allowedEvents_
+ * @private
+ */
+
+
+ EventTarget.prototype.allowedEvents_ = {};
+ /**
+ * Adds an `event listener` to an instance of an `EventTarget`. An `event listener` is a
+ * function that will get called when an event with a certain name gets triggered.
+ *
+ * @param {string|string[]} type
+ * An event name or an array of event names.
+ *
+ * @param {EventTarget~EventListener} fn
+ * The function to call with `EventTarget`s
+ */
+
+ EventTarget.prototype.on = function (type, fn) {
+ // Remove the addEventListener alias before calling Events.on
+ // so we don't get into an infinite type loop
+ var ael = this.addEventListener;
+
+ this.addEventListener = function () {};
+
+ on(this, type, fn);
+ this.addEventListener = ael;
+ };
+ /**
+ * An alias of {@link EventTarget#on}. Allows `EventTarget` to mimic
+ * the standard DOM API.
+ *
+ * @function
+ * @see {@link EventTarget#on}
+ */
+
+
+ EventTarget.prototype.addEventListener = EventTarget.prototype.on;
+ /**
+ * Removes an `event listener` for a specific event from an instance of `EventTarget`.
+ * This makes it so that the `event listener` will no longer get called when the
+ * named event happens.
+ *
+ * @param {string|string[]} type
+ * An event name or an array of event names.
+ *
+ * @param {EventTarget~EventListener} fn
+ * The function to remove.
+ */
+
+ EventTarget.prototype.off = function (type, fn) {
+ off(this, type, fn);
+ };
+ /**
+ * An alias of {@link EventTarget#off}. Allows `EventTarget` to mimic
+ * the standard DOM API.
+ *
+ * @function
+ * @see {@link EventTarget#off}
+ */
+
+
+ EventTarget.prototype.removeEventListener = EventTarget.prototype.off;
+ /**
+ * This function will add an `event listener` that gets triggered only once. After the
+ * first trigger it will get removed. This is like adding an `event listener`
+ * with {@link EventTarget#on} that calls {@link EventTarget#off} on itself.
+ *
+ * @param {string|string[]} type
+ * An event name or an array of event names.
+ *
+ * @param {EventTarget~EventListener} fn
+ * The function to be called once for each event name.
+ */
+
+ EventTarget.prototype.one = function (type, fn) {
+ // Remove the addEventListener alialing Events.on
+ // so we don't get into an infinite type loop
+ var ael = this.addEventListener;
+
+ this.addEventListener = function () {};
+
+ one(this, type, fn);
+ this.addEventListener = ael;
+ };
+ /**
+ * This function causes an event to happen. This will then cause any `event listeners`
+ * that are waiting for that event, to get called. If there are no `event listeners`
+ * for an event then nothing will happen.
+ *
+ * If the name of the `Event` that is being triggered is in `EventTarget.allowedEvents_`.
+ * Trigger will also call the `on` + `uppercaseEventName` function.
+ *
+ * Example:
+ * 'click' is in `EventTarget.allowedEvents_`, so, trigger will attempt to call
+ * `onClick` if it exists.
+ *
+ * @param {string|EventTarget~Event|Object} event
+ * The name of the event, an `Event`, or an object with a key of type set to
+ * an event name.
+ */
+
+
+ EventTarget.prototype.trigger = function (event) {
+ var type = event.type || event; // deprecation
+ // In a future version we should default target to `this`
+ // similar to how we default the target to `elem` in
+ // `Events.trigger`. Right now the default `target` will be
+ // `document` due to the `Event.fixEvent` call.
+
+ if (typeof event === 'string') {
+ event = {
+ type: type
+ };
+ }
+
+ event = fixEvent(event);
+
+ if (this.allowedEvents_[type] && this['on' + type]) {
+ this['on' + type](event);
+ }
+
+ trigger(this, event);
+ };
+ /**
+ * An alias of {@link EventTarget#trigger}. Allows `EventTarget` to mimic
+ * the standard DOM API.
+ *
+ * @function
+ * @see {@link EventTarget#trigger}
+ */
+
+
+ EventTarget.prototype.dispatchEvent = EventTarget.prototype.trigger;
+ var EVENT_MAP;
+
+ EventTarget.prototype.queueTrigger = function (event) {
+ var _this = this;
+
+ // only set up EVENT_MAP if it'll be used
+ if (!EVENT_MAP) {
+ EVENT_MAP = new Map();
+ }
+
+ var type = event.type || event;
+ var map = EVENT_MAP.get(this);
+
+ if (!map) {
+ map = new Map();
+ EVENT_MAP.set(this, map);
+ }
+
+ var oldTimeout = map.get(type);
+ map.delete(type);
+ window$1.clearTimeout(oldTimeout);
+ var timeout = window$1.setTimeout(function () {
+ // if we cleared out all timeouts for the current target, delete its map
+ if (map.size === 0) {
+ map = null;
+ EVENT_MAP.delete(_this);
+ }
+
+ _this.trigger(event);
+ }, 0);
+ map.set(type, timeout);
+ };
+
+ /**
+ * @file mixins/evented.js
+ * @module evented
+ */
+ /**
+ * Returns whether or not an object has had the evented mixin applied.
+ *
+ * @param {Object} object
+ * An object to test.
+ *
+ * @return {boolean}
+ * Whether or not the object appears to be evented.
+ */
+
+ var isEvented = function isEvented(object) {
+ return object instanceof EventTarget || !!object.eventBusEl_ && ['on', 'one', 'off', 'trigger'].every(function (k) {
+ return typeof object[k] === 'function';
+ });
+ };
+ /**
+ * Adds a callback to run after the evented mixin applied.
+ *
+ * @param {Object} object
+ * An object to Add
+ * @param {Function} callback
+ * The callback to run.
+ */
+
+
+ var addEventedCallback = function addEventedCallback(target, callback) {
+ if (isEvented(target)) {
+ callback();
+ } else {
+ if (!target.eventedCallbacks) {
+ target.eventedCallbacks = [];
+ }
+
+ target.eventedCallbacks.push(callback);
+ }
+ };
+ /**
+ * Whether a value is a valid event type - non-empty string or array.
+ *
+ * @private
+ * @param {string|Array} type
+ * The type value to test.
+ *
+ * @return {boolean}
+ * Whether or not the type is a valid event type.
+ */
+
+
+ var isValidEventType = function isValidEventType(type) {
+ return (// The regex here verifies that the `type` contains at least one non-
+ // whitespace character.
+ typeof type === 'string' && /\S/.test(type) || Array.isArray(type) && !!type.length
+ );
+ };
+ /**
+ * Validates a value to determine if it is a valid event target. Throws if not.
+ *
+ * @private
+ * @throws {Error}
+ * If the target does not appear to be a valid event target.
+ *
+ * @param {Object} target
+ * The object to test.
+ */
+
+
+ var validateTarget = function validateTarget(target) {
+ if (!target.nodeName && !isEvented(target)) {
+ throw new Error('Invalid target; must be a DOM node or evented object.');
+ }
+ };
+ /**
+ * Validates a value to determine if it is a valid event target. Throws if not.
+ *
+ * @private
+ * @throws {Error}
+ * If the type does not appear to be a valid event type.
+ *
+ * @param {string|Array} type
+ * The type to test.
+ */
+
+
+ var validateEventType = function validateEventType(type) {
+ if (!isValidEventType(type)) {
+ throw new Error('Invalid event type; must be a non-empty string or array.');
+ }
+ };
+ /**
+ * Validates a value to determine if it is a valid listener. Throws if not.
+ *
+ * @private
+ * @throws {Error}
+ * If the listener is not a function.
+ *
+ * @param {Function} listener
+ * The listener to test.
+ */
+
+
+ var validateListener = function validateListener(listener) {
+ if (typeof listener !== 'function') {
+ throw new Error('Invalid listener; must be a function.');
+ }
+ };
+ /**
+ * Takes an array of arguments given to `on()` or `one()`, validates them, and
+ * normalizes them into an object.
+ *
+ * @private
+ * @param {Object} self
+ * The evented object on which `on()` or `one()` was called. This
+ * object will be bound as the `this` value for the listener.
+ *
+ * @param {Array} args
+ * An array of arguments passed to `on()` or `one()`.
+ *
+ * @return {Object}
+ * An object containing useful values for `on()` or `one()` calls.
+ */
+
+
+ var normalizeListenArgs = function normalizeListenArgs(self, args) {
+ // If the number of arguments is less than 3, the target is always the
+ // evented object itself.
+ var isTargetingSelf = args.length < 3 || args[0] === self || args[0] === self.eventBusEl_;
+ var target;
+ var type;
+ var listener;
+
+ if (isTargetingSelf) {
+ target = self.eventBusEl_; // Deal with cases where we got 3 arguments, but we are still listening to
+ // the evented object itself.
+
+ if (args.length >= 3) {
+ args.shift();
+ }
+
+ type = args[0];
+ listener = args[1];
+ } else {
+ target = args[0];
+ type = args[1];
+ listener = args[2];
+ }
+
+ validateTarget(target);
+ validateEventType(type);
+ validateListener(listener);
+ listener = bind(self, listener);
+ return {
+ isTargetingSelf: isTargetingSelf,
+ target: target,
+ type: type,
+ listener: listener
+ };
+ };
+ /**
+ * Adds the listener to the event type(s) on the target, normalizing for
+ * the type of target.
+ *
+ * @private
+ * @param {Element|Object} target
+ * A DOM node or evented object.
+ *
+ * @param {string} method
+ * The event binding method to use ("on" or "one").
+ *
+ * @param {string|Array} type
+ * One or more event type(s).
+ *
+ * @param {Function} listener
+ * A listener function.
+ */
+
+
+ var listen = function listen(target, method, type, listener) {
+ validateTarget(target);
+
+ if (target.nodeName) {
+ Events[method](target, type, listener);
+ } else {
+ target[method](type, listener);
+ }
+ };
+ /**
+ * Contains methods that provide event capabilities to an object which is passed
+ * to {@link module:evented|evented}.
+ *
+ * @mixin EventedMixin
+ */
+
+
+ var EventedMixin = {
+ /**
+ * Add a listener to an event (or events) on this object or another evented
+ * object.
+ *
+ * @param {string|Array|Element|Object} targetOrType
+ * If this is a string or array, it represents the event type(s)
+ * that will trigger the listener.
+ *
+ * Another evented object can be passed here instead, which will
+ * cause the listener to listen for events on _that_ object.
+ *
+ * In either case, the listener's `this` value will be bound to
+ * this object.
+ *
+ * @param {string|Array|Function} typeOrListener
+ * If the first argument was a string or array, this should be the
+ * listener function. Otherwise, this is a string or array of event
+ * type(s).
+ *
+ * @param {Function} [listener]
+ * If the first argument was another evented object, this will be
+ * the listener function.
+ */
+ on: function on$$1() {
+ var _this = this;
+
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ var _normalizeListenArgs = normalizeListenArgs(this, args),
+ isTargetingSelf = _normalizeListenArgs.isTargetingSelf,
+ target = _normalizeListenArgs.target,
+ type = _normalizeListenArgs.type,
+ listener = _normalizeListenArgs.listener;
+
+ listen(target, 'on', type, listener); // If this object is listening to another evented object.
+
+ if (!isTargetingSelf) {
+ // If this object is disposed, remove the listener.
+ var removeListenerOnDispose = function removeListenerOnDispose() {
+ return _this.off(target, type, listener);
+ }; // Use the same function ID as the listener so we can remove it later it
+ // using the ID of the original listener.
+
+
+ removeListenerOnDispose.guid = listener.guid; // Add a listener to the target's dispose event as well. This ensures
+ // that if the target is disposed BEFORE this object, we remove the
+ // removal listener that was just added. Otherwise, we create a memory leak.
+
+ var removeRemoverOnTargetDispose = function removeRemoverOnTargetDispose() {
+ return _this.off('dispose', removeListenerOnDispose);
+ }; // Use the same function ID as the listener so we can remove it later
+ // it using the ID of the original listener.
+
+
+ removeRemoverOnTargetDispose.guid = listener.guid;
+ listen(this, 'on', 'dispose', removeListenerOnDispose);
+ listen(target, 'on', 'dispose', removeRemoverOnTargetDispose);
+ }
+ },
+
+ /**
+ * Add a listener to an event (or events) on this object or another evented
+ * object. The listener will only be called once and then removed.
+ *
+ * @param {string|Array|Element|Object} targetOrType
+ * If this is a string or array, it represents the event type(s)
+ * that will trigger the listener.
+ *
+ * Another evented object can be passed here instead, which will
+ * cause the listener to listen for events on _that_ object.
+ *
+ * In either case, the listener's `this` value will be bound to
+ * this object.
+ *
+ * @param {string|Array|Function} typeOrListener
+ * If the first argument was a string or array, this should be the
+ * listener function. Otherwise, this is a string or array of event
+ * type(s).
+ *
+ * @param {Function} [listener]
+ * If the first argument was another evented object, this will be
+ * the listener function.
+ */
+ one: function one$$1() {
+ var _this2 = this;
+
+ for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
+ args[_key2] = arguments[_key2];
+ }
+
+ var _normalizeListenArgs2 = normalizeListenArgs(this, args),
+ isTargetingSelf = _normalizeListenArgs2.isTargetingSelf,
+ target = _normalizeListenArgs2.target,
+ type = _normalizeListenArgs2.type,
+ listener = _normalizeListenArgs2.listener; // Targeting this evented object.
+
+
+ if (isTargetingSelf) {
+ listen(target, 'one', type, listener); // Targeting another evented object.
+ } else {
+ var wrapper = function wrapper() {
+ _this2.off(target, type, wrapper);
+
+ for (var _len3 = arguments.length, largs = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
+ largs[_key3] = arguments[_key3];
+ }
+
+ listener.apply(null, largs);
+ }; // Use the same function ID as the listener so we can remove it later
+ // it using the ID of the original listener.
+
+
+ wrapper.guid = listener.guid;
+ listen(target, 'one', type, wrapper);
+ }
+ },
+
+ /**
+ * Removes listener(s) from event(s) on an evented object.
+ *
+ * @param {string|Array|Element|Object} [targetOrType]
+ * If this is a string or array, it represents the event type(s).
+ *
+ * Another evented object can be passed here instead, in which case
+ * ALL 3 arguments are _required_.
+ *
+ * @param {string|Array|Function} [typeOrListener]
+ * If the first argument was a string or array, this may be the
+ * listener function. Otherwise, this is a string or array of event
+ * type(s).
+ *
+ * @param {Function} [listener]
+ * If the first argument was another evented object, this will be
+ * the listener function; otherwise, _all_ listeners bound to the
+ * event type(s) will be removed.
+ */
+ off: function off$$1(targetOrType, typeOrListener, listener) {
+ // Targeting this evented object.
+ if (!targetOrType || isValidEventType(targetOrType)) {
+ off(this.eventBusEl_, targetOrType, typeOrListener); // Targeting another evented object.
+ } else {
+ var target = targetOrType;
+ var type = typeOrListener; // Fail fast and in a meaningful way!
+
+ validateTarget(target);
+ validateEventType(type);
+ validateListener(listener); // Ensure there's at least a guid, even if the function hasn't been used
+
+ listener = bind(this, listener); // Remove the dispose listener on this evented object, which was given
+ // the same guid as the event listener in on().
+
+ this.off('dispose', listener);
+
+ if (target.nodeName) {
+ off(target, type, listener);
+ off(target, 'dispose', listener);
+ } else if (isEvented(target)) {
+ target.off(type, listener);
+ target.off('dispose', listener);
+ }
+ }
+ },
+
+ /**
+ * Fire an event on this evented object, causing its listeners to be called.
+ *
+ * @param {string|Object} event
+ * An event type or an object with a type property.
+ *
+ * @param {Object} [hash]
+ * An additional object to pass along to listeners.
+ *
+ * @return {boolean}
+ * Whether or not the default behavior was prevented.
+ */
+ trigger: function trigger$$1(event, hash) {
+ return trigger(this.eventBusEl_, event, hash);
+ }
+ };
+ /**
+ * Applies {@link module:evented~EventedMixin|EventedMixin} to a target object.
+ *
+ * @param {Object} target
+ * The object to which to add event methods.
+ *
+ * @param {Object} [options={}]
+ * Options for customizing the mixin behavior.
+ *
+ * @param {string} [options.eventBusKey]
+ * By default, adds a `eventBusEl_` DOM element to the target object,
+ * which is used as an event bus. If the target object already has a
+ * DOM element that should be used, pass its key here.
+ *
+ * @return {Object}
+ * The target object.
+ */
+
+ function evented(target, options) {
+ if (options === void 0) {
+ options = {};
+ }
+
+ var _options = options,
+ eventBusKey = _options.eventBusKey; // Set or create the eventBusEl_.
+
+ if (eventBusKey) {
+ if (!target[eventBusKey].nodeName) {
+ throw new Error("The eventBusKey \"" + eventBusKey + "\" does not refer to an element.");
+ }
+
+ target.eventBusEl_ = target[eventBusKey];
+ } else {
+ target.eventBusEl_ = createEl('span', {
+ className: 'vjs-event-bus'
+ });
+ }
+
+ assign(target, EventedMixin);
+
+ if (target.eventedCallbacks) {
+ target.eventedCallbacks.forEach(function (callback) {
+ callback();
+ });
+ } // When any evented object is disposed, it removes all its listeners.
+
+
+ target.on('dispose', function () {
+ target.off();
+ window$1.setTimeout(function () {
+ target.eventBusEl_ = null;
+ }, 0);
+ });
+ return target;
+ }
+
+ /**
+ * @file mixins/stateful.js
+ * @module stateful
+ */
+ /**
+ * Contains methods that provide statefulness to an object which is passed
+ * to {@link module:stateful}.
+ *
+ * @mixin StatefulMixin
+ */
+
+ var StatefulMixin = {
+ /**
+ * A hash containing arbitrary keys and values representing the state of
+ * the object.
+ *
+ * @type {Object}
+ */
+ state: {},
+
+ /**
+ * Set the state of an object by mutating its
+ * {@link module:stateful~StatefulMixin.state|state} object in place.
+ *
+ * @fires module:stateful~StatefulMixin#statechanged
+ * @param {Object|Function} stateUpdates
+ * A new set of properties to shallow-merge into the plugin state.
+ * Can be a plain object or a function returning a plain object.
+ *
+ * @return {Object|undefined}
+ * An object containing changes that occurred. If no changes
+ * occurred, returns `undefined`.
+ */
+ setState: function setState(stateUpdates) {
+ var _this = this;
+
+ // Support providing the `stateUpdates` state as a function.
+ if (typeof stateUpdates === 'function') {
+ stateUpdates = stateUpdates();
+ }
+
+ var changes;
+ each(stateUpdates, function (value, key) {
+ // Record the change if the value is different from what's in the
+ // current state.
+ if (_this.state[key] !== value) {
+ changes = changes || {};
+ changes[key] = {
+ from: _this.state[key],
+ to: value
+ };
+ }
+
+ _this.state[key] = value;
+ }); // Only trigger "statechange" if there were changes AND we have a trigger
+ // function. This allows us to not require that the target object be an
+ // evented object.
+
+ if (changes && isEvented(this)) {
+ /**
+ * An event triggered on an object that is both
+ * {@link module:stateful|stateful} and {@link module:evented|evented}
+ * indicating that its state has changed.
+ *
+ * @event module:stateful~StatefulMixin#statechanged
+ * @type {Object}
+ * @property {Object} changes
+ * A hash containing the properties that were changed and
+ * the values they were changed `from` and `to`.
+ */
+ this.trigger({
+ changes: changes,
+ type: 'statechanged'
+ });
+ }
+
+ return changes;
+ }
+ };
+ /**
+ * Applies {@link module:stateful~StatefulMixin|StatefulMixin} to a target
+ * object.
+ *
+ * If the target object is {@link module:evented|evented} and has a
+ * `handleStateChanged` method, that method will be automatically bound to the
+ * `statechanged` event on itself.
+ *
+ * @param {Object} target
+ * The object to be made stateful.
+ *
+ * @param {Object} [defaultState]
+ * A default set of properties to populate the newly-stateful object's
+ * `state` property.
+ *
+ * @return {Object}
+ * Returns the `target`.
+ */
+
+ function stateful(target, defaultState) {
+ assign(target, StatefulMixin); // This happens after the mixing-in because we need to replace the `state`
+ // added in that step.
+
+ target.state = assign({}, target.state, defaultState); // Auto-bind the `handleStateChanged` method of the target object if it exists.
+
+ if (typeof target.handleStateChanged === 'function' && isEvented(target)) {
+ target.on('statechanged', target.handleStateChanged);
+ }
+
+ return target;
+ }
+
+ /**
+ * @file to-title-case.js
+ * @module to-title-case
+ */
+
+ /**
+ * Uppercase the first letter of a string.
+ *
+ * @param {string} string
+ * String to be uppercased
+ *
+ * @return {string}
+ * The string with an uppercased first letter
+ */
+ function toTitleCase(string) {
+ if (typeof string !== 'string') {
+ return string;
+ }
+
+ return string.charAt(0).toUpperCase() + string.slice(1);
+ }
+ /**
+ * Compares the TitleCase versions of the two strings for equality.
+ *
+ * @param {string} str1
+ * The first string to compare
+ *
+ * @param {string} str2
+ * The second string to compare
+ *
+ * @return {boolean}
+ * Whether the TitleCase versions of the strings are equal
+ */
+
+ function titleCaseEquals(str1, str2) {
+ return toTitleCase(str1) === toTitleCase(str2);
+ }
+
+ /**
+ * @file merge-options.js
+ * @module merge-options
+ */
+ /**
+ * Merge two objects recursively.
+ *
+ * Performs a deep merge like
+ * {@link https://lodash.com/docs/4.17.10#merge|lodash.merge}, but only merges
+ * plain objects (not arrays, elements, or anything else).
+ *
+ * Non-plain object values will be copied directly from the right-most
+ * argument.
+ *
+ * @static
+ * @param {Object[]} sources
+ * One or more objects to merge into a new object.
+ *
+ * @return {Object}
+ * A new object that is the merged result of all sources.
+ */
+
+ function mergeOptions() {
+ var result = {};
+
+ for (var _len = arguments.length, sources = new Array(_len), _key = 0; _key < _len; _key++) {
+ sources[_key] = arguments[_key];
+ }
+
+ sources.forEach(function (source) {
+ if (!source) {
+ return;
+ }
+
+ each(source, function (value, key) {
+ if (!isPlain(value)) {
+ result[key] = value;
+ return;
+ }
+
+ if (!isPlain(result[key])) {
+ result[key] = {};
+ }
+
+ result[key] = mergeOptions(result[key], value);
+ });
+ });
+ return result;
+ }
+
+ /**
+ * Player Component - Base class for all UI objects
+ *
+ * @file component.js
+ */
+ /**
+ * Base class for all UI Components.
+ * Components are UI objects which represent both a javascript object and an element
+ * in the DOM. They can be children of other components, and can have
+ * children themselves.
+ *
+ * Components can also use methods from {@link EventTarget}
+ */
+
+ var Component =
+ /*#__PURE__*/
+ function () {
+ /**
+ * A callback that is called when a component is ready. Does not have any
+ * paramters and any callback value will be ignored.
+ *
+ * @callback Component~ReadyCallback
+ * @this Component
+ */
+
+ /**
+ * Creates an instance of this class.
+ *
+ * @param {Player} player
+ * The `Player` that this class should be attached to.
+ *
+ * @param {Object} [options]
+ * The key/value store of player options.
+ *
+ * @param {Object[]} [options.children]
+ * An array of children objects to intialize this component with. Children objects have
+ * a name property that will be used if more than one component of the same type needs to be
+ * added.
+ *
+ * @param {Component~ReadyCallback} [ready]
+ * Function that gets called when the `Component` is ready.
+ */
+ function Component(player, options, ready) {
+ // The component might be the player itself and we can't pass `this` to super
+ if (!player && this.play) {
+ this.player_ = player = this; // eslint-disable-line
+ } else {
+ this.player_ = player;
+ } // Hold the reference to the parent component via `addChild` method
+
+
+ this.parentComponent_ = null; // Make a copy of prototype.options_ to protect against overriding defaults
+
+ this.options_ = mergeOptions({}, this.options_); // Updated options with supplied options
+
+ options = this.options_ = mergeOptions(this.options_, options); // Get ID from options or options element if one is supplied
+
+ this.id_ = options.id || options.el && options.el.id; // If there was no ID from the options, generate one
+
+ if (!this.id_) {
+ // Don't require the player ID function in the case of mock players
+ var id = player && player.id && player.id() || 'no_player';
+ this.id_ = id + "_component_" + newGUID();
+ }
+
+ this.name_ = options.name || null; // Create element if one wasn't provided in options
+
+ if (options.el) {
+ this.el_ = options.el;
+ } else if (options.createEl !== false) {
+ this.el_ = this.createEl();
+ } // if evented is anything except false, we want to mixin in evented
+
+
+ if (options.evented !== false) {
+ // Make this an evented object and use `el_`, if available, as its event bus
+ evented(this, {
+ eventBusKey: this.el_ ? 'el_' : null
+ });
+ }
+
+ stateful(this, this.constructor.defaultState);
+ this.children_ = [];
+ this.childIndex_ = {};
+ this.childNameIndex_ = {}; // Add any child components in options
+
+ if (options.initChildren !== false) {
+ this.initChildren();
+ }
+
+ this.ready(ready); // Don't want to trigger ready here or it will before init is actually
+ // finished for all children that run this constructor
+
+ if (options.reportTouchActivity !== false) {
+ this.enableTouchActivity();
+ }
+ }
+ /**
+ * Dispose of the `Component` and all child components.
+ *
+ * @fires Component#dispose
+ */
+
+
+ var _proto = Component.prototype;
+
+ _proto.dispose = function dispose() {
+ /**
+ * Triggered when a `Component` is disposed.
+ *
+ * @event Component#dispose
+ * @type {EventTarget~Event}
+ *
+ * @property {boolean} [bubbles=false]
+ * set to false so that the close event does not
+ * bubble up
+ */
+ this.trigger({
+ type: 'dispose',
+ bubbles: false
+ }); // Dispose all children.
+
+ if (this.children_) {
+ for (var i = this.children_.length - 1; i >= 0; i--) {
+ if (this.children_[i].dispose) {
+ this.children_[i].dispose();
+ }
+ }
+ } // Delete child references
+
+
+ this.children_ = null;
+ this.childIndex_ = null;
+ this.childNameIndex_ = null;
+ this.parentComponent_ = null;
+
+ if (this.el_) {
+ // Remove element from DOM
+ if (this.el_.parentNode) {
+ this.el_.parentNode.removeChild(this.el_);
+ }
+
+ removeData(this.el_);
+ this.el_ = null;
+ } // remove reference to the player after disposing of the element
+
+
+ this.player_ = null;
+ }
+ /**
+ * Return the {@link Player} that the `Component` has attached to.
+ *
+ * @return {Player}
+ * The player that this `Component` has attached to.
+ */
+ ;
+
+ _proto.player = function player() {
+ return this.player_;
+ }
+ /**
+ * Deep merge of options objects with new options.
+ * > Note: When both `obj` and `options` contain properties whose values are objects.
+ * The two properties get merged using {@link module:mergeOptions}
+ *
+ * @param {Object} obj
+ * The object that contains new options.
+ *
+ * @return {Object}
+ * A new object of `this.options_` and `obj` merged together.
+ *
+ * @deprecated since version 5
+ */
+ ;
+
+ _proto.options = function options(obj) {
+ log.warn('this.options() has been deprecated and will be moved to the constructor in 6.0');
+
+ if (!obj) {
+ return this.options_;
+ }
+
+ this.options_ = mergeOptions(this.options_, obj);
+ return this.options_;
+ }
+ /**
+ * Get the `Component`s DOM element
+ *
+ * @return {Element}
+ * The DOM element for this `Component`.
+ */
+ ;
+
+ _proto.el = function el() {
+ return this.el_;
+ }
+ /**
+ * Create the `Component`s DOM element.
+ *
+ * @param {string} [tagName]
+ * Element's DOM node type. e.g. 'div'
+ *
+ * @param {Object} [properties]
+ * An object of properties that should be set.
+ *
+ * @param {Object} [attributes]
+ * An object of attributes that should be set.
+ *
+ * @return {Element}
+ * The element that gets created.
+ */
+ ;
+
+ _proto.createEl = function createEl$$1(tagName, properties, attributes) {
+ return createEl(tagName, properties, attributes);
+ }
+ /**
+ * Localize a string given the string in english.
+ *
+ * If tokens are provided, it'll try and run a simple token replacement on the provided string.
+ * The tokens it looks for look like `{1}` with the index being 1-indexed into the tokens array.
+ *
+ * If a `defaultValue` is provided, it'll use that over `string`,
+ * if a value isn't found in provided language files.
+ * This is useful if you want to have a descriptive key for token replacement
+ * but have a succinct localized string and not require `en.json` to be included.
+ *
+ * Currently, it is used for the progress bar timing.
+ * ```js
+ * {
+ * "progress bar timing: currentTime={1} duration={2}": "{1} of {2}"
+ * }
+ * ```
+ * It is then used like so:
+ * ```js
+ * this.localize('progress bar timing: currentTime={1} duration{2}',
+ * [this.player_.currentTime(), this.player_.duration()],
+ * '{1} of {2}');
+ * ```
+ *
+ * Which outputs something like: `01:23 of 24:56`.
+ *
+ *
+ * @param {string} string
+ * The string to localize and the key to lookup in the language files.
+ * @param {string[]} [tokens]
+ * If the current item has token replacements, provide the tokens here.
+ * @param {string} [defaultValue]
+ * Defaults to `string`. Can be a default value to use for token replacement
+ * if the lookup key is needed to be separate.
+ *
+ * @return {string}
+ * The localized string or if no localization exists the english string.
+ */
+ ;
+
+ _proto.localize = function localize(string, tokens, defaultValue) {
+ if (defaultValue === void 0) {
+ defaultValue = string;
+ }
+
+ var code = this.player_.language && this.player_.language();
+ var languages = this.player_.languages && this.player_.languages();
+ var language = languages && languages[code];
+ var primaryCode = code && code.split('-')[0];
+ var primaryLang = languages && languages[primaryCode];
+ var localizedString = defaultValue;
+
+ if (language && language[string]) {
+ localizedString = language[string];
+ } else if (primaryLang && primaryLang[string]) {
+ localizedString = primaryLang[string];
+ }
+
+ if (tokens) {
+ localizedString = localizedString.replace(/\{(\d+)\}/g, function (match, index) {
+ var value = tokens[index - 1];
+ var ret = value;
+
+ if (typeof value === 'undefined') {
+ ret = match;
+ }
+
+ return ret;
+ });
+ }
+
+ return localizedString;
+ }
+ /**
+ * Return the `Component`s DOM element. This is where children get inserted.
+ * This will usually be the the same as the element returned in {@link Component#el}.
+ *
+ * @return {Element}
+ * The content element for this `Component`.
+ */
+ ;
+
+ _proto.contentEl = function contentEl() {
+ return this.contentEl_ || this.el_;
+ }
+ /**
+ * Get this `Component`s ID
+ *
+ * @return {string}
+ * The id of this `Component`
+ */
+ ;
+
+ _proto.id = function id() {
+ return this.id_;
+ }
+ /**
+ * Get the `Component`s name. The name gets used to reference the `Component`
+ * and is set during registration.
+ *
+ * @return {string}
+ * The name of this `Component`.
+ */
+ ;
+
+ _proto.name = function name() {
+ return this.name_;
+ }
+ /**
+ * Get an array of all child components
+ *
+ * @return {Array}
+ * The children
+ */
+ ;
+
+ _proto.children = function children() {
+ return this.children_;
+ }
+ /**
+ * Returns the child `Component` with the given `id`.
+ *
+ * @param {string} id
+ * The id of the child `Component` to get.
+ *
+ * @return {Component|undefined}
+ * The child `Component` with the given `id` or undefined.
+ */
+ ;
+
+ _proto.getChildById = function getChildById(id) {
+ return this.childIndex_[id];
+ }
+ /**
+ * Returns the child `Component` with the given `name`.
+ *
+ * @param {string} name
+ * The name of the child `Component` to get.
+ *
+ * @return {Component|undefined}
+ * The child `Component` with the given `name` or undefined.
+ */
+ ;
+
+ _proto.getChild = function getChild(name) {
+ if (!name) {
+ return;
+ }
+
+ name = toTitleCase(name);
+ return this.childNameIndex_[name];
+ }
+ /**
+ * Add a child `Component` inside the current `Component`.
+ *
+ *
+ * @param {string|Component} child
+ * The name or instance of a child to add.
+ *
+ * @param {Object} [options={}]
+ * The key/value store of options that will get passed to children of
+ * the child.
+ *
+ * @param {number} [index=this.children_.length]
+ * The index to attempt to add a child into.
+ *
+ * @return {Component}
+ * The `Component` that gets added as a child. When using a string the
+ * `Component` will get created by this process.
+ */
+ ;
+
+ _proto.addChild = function addChild(child, options, index) {
+ if (options === void 0) {
+ options = {};
+ }
+
+ if (index === void 0) {
+ index = this.children_.length;
+ }
+
+ var component;
+ var componentName; // If child is a string, create component with options
+
+ if (typeof child === 'string') {
+ componentName = toTitleCase(child);
+ var componentClassName = options.componentClass || componentName; // Set name through options
+
+ options.name = componentName; // Create a new object & element for this controls set
+ // If there's no .player_, this is a player
+
+ var ComponentClass = Component.getComponent(componentClassName);
+
+ if (!ComponentClass) {
+ throw new Error("Component " + componentClassName + " does not exist");
+ } // data stored directly on the videojs object may be
+ // misidentified as a component to retain
+ // backwards-compatibility with 4.x. check to make sure the
+ // component class can be instantiated.
+
+
+ if (typeof ComponentClass !== 'function') {
+ return null;
+ }
+
+ component = new ComponentClass(this.player_ || this, options); // child is a component instance
+ } else {
+ component = child;
+ }
+
+ if (component.parentComponent_) {
+ component.parentComponent_.removeChild(component);
+ }
+
+ this.children_.splice(index, 0, component);
+ component.parentComponent_ = this;
+
+ if (typeof component.id === 'function') {
+ this.childIndex_[component.id()] = component;
+ } // If a name wasn't used to create the component, check if we can use the
+ // name function of the component
+
+
+ componentName = componentName || component.name && toTitleCase(component.name());
+
+ if (componentName) {
+ this.childNameIndex_[componentName] = component;
+ } // Add the UI object's element to the container div (box)
+ // Having an element is not required
+
+
+ if (typeof component.el === 'function' && component.el()) {
+ var childNodes = this.contentEl().children;
+ var refNode = childNodes[index] || null;
+ this.contentEl().insertBefore(component.el(), refNode);
+ } // Return so it can stored on parent object if desired.
+
+
+ return component;
+ }
+ /**
+ * Remove a child `Component` from this `Component`s list of children. Also removes
+ * the child `Component`s element from this `Component`s element.
+ *
+ * @param {Component} component
+ * The child `Component` to remove.
+ */
+ ;
+
+ _proto.removeChild = function removeChild(component) {
+ if (typeof component === 'string') {
+ component = this.getChild(component);
+ }
+
+ if (!component || !this.children_) {
+ return;
+ }
+
+ var childFound = false;
+
+ for (var i = this.children_.length - 1; i >= 0; i--) {
+ if (this.children_[i] === component) {
+ childFound = true;
+ this.children_.splice(i, 1);
+ break;
+ }
+ }
+
+ if (!childFound) {
+ return;
+ }
+
+ component.parentComponent_ = null;
+ this.childIndex_[component.id()] = null;
+ this.childNameIndex_[component.name()] = null;
+ var compEl = component.el();
+
+ if (compEl && compEl.parentNode === this.contentEl()) {
+ this.contentEl().removeChild(component.el());
+ }
+ }
+ /**
+ * Add and initialize default child `Component`s based upon options.
+ */
+ ;
+
+ _proto.initChildren = function initChildren() {
+ var _this = this;
+
+ var children = this.options_.children;
+
+ if (children) {
+ // `this` is `parent`
+ var parentOptions = this.options_;
+
+ var handleAdd = function handleAdd(child) {
+ var name = child.name;
+ var opts = child.opts; // Allow options for children to be set at the parent options
+ // e.g. videojs(id, { controlBar: false });
+ // instead of videojs(id, { children: { controlBar: false });
+
+ if (parentOptions[name] !== undefined) {
+ opts = parentOptions[name];
+ } // Allow for disabling default components
+ // e.g. options['children']['posterImage'] = false
+
+
+ if (opts === false) {
+ return;
+ } // Allow options to be passed as a simple boolean if no configuration
+ // is necessary.
+
+
+ if (opts === true) {
+ opts = {};
+ } // We also want to pass the original player options
+ // to each component as well so they don't need to
+ // reach back into the player for options later.
+
+
+ opts.playerOptions = _this.options_.playerOptions; // Create and add the child component.
+ // Add a direct reference to the child by name on the parent instance.
+ // If two of the same component are used, different names should be supplied
+ // for each
+
+ var newChild = _this.addChild(name, opts);
+
+ if (newChild) {
+ _this[name] = newChild;
+ }
+ }; // Allow for an array of children details to passed in the options
+
+
+ var workingChildren;
+ var Tech = Component.getComponent('Tech');
+
+ if (Array.isArray(children)) {
+ workingChildren = children;
+ } else {
+ workingChildren = Object.keys(children);
+ }
+
+ workingChildren // children that are in this.options_ but also in workingChildren would
+ // give us extra children we do not want. So, we want to filter them out.
+ .concat(Object.keys(this.options_).filter(function (child) {
+ return !workingChildren.some(function (wchild) {
+ if (typeof wchild === 'string') {
+ return child === wchild;
+ }
+
+ return child === wchild.name;
+ });
+ })).map(function (child) {
+ var name;
+ var opts;
+
+ if (typeof child === 'string') {
+ name = child;
+ opts = children[name] || _this.options_[name] || {};
+ } else {
+ name = child.name;
+ opts = child;
+ }
+
+ return {
+ name: name,
+ opts: opts
+ };
+ }).filter(function (child) {
+ // we have to make sure that child.name isn't in the techOrder since
+ // techs are registerd as Components but can't aren't compatible
+ // See https://github.com/videojs/video.js/issues/2772
+ var c = Component.getComponent(child.opts.componentClass || toTitleCase(child.name));
+ return c && !Tech.isTech(c);
+ }).forEach(handleAdd);
+ }
+ }
+ /**
+ * Builds the default DOM class name. Should be overriden by sub-components.
+ *
+ * @return {string}
+ * The DOM class name for this object.
+ *
+ * @abstract
+ */
+ ;
+
+ _proto.buildCSSClass = function buildCSSClass() {
+ // Child classes can include a function that does:
+ // return 'CLASS NAME' + this._super();
+ return '';
+ }
+ /**
+ * Bind a listener to the component's ready state.
+ * Different from event listeners in that if the ready event has already happened
+ * it will trigger the function immediately.
+ *
+ * @return {Component}
+ * Returns itself; method can be chained.
+ */
+ ;
+
+ _proto.ready = function ready(fn, sync) {
+ if (sync === void 0) {
+ sync = false;
+ }
+
+ if (!fn) {
+ return;
+ }
+
+ if (!this.isReady_) {
+ this.readyQueue_ = this.readyQueue_ || [];
+ this.readyQueue_.push(fn);
+ return;
+ }
+
+ if (sync) {
+ fn.call(this);
+ } else {
+ // Call the function asynchronously by default for consistency
+ this.setTimeout(fn, 1);
+ }
+ }
+ /**
+ * Trigger all the ready listeners for this `Component`.
+ *
+ * @fires Component#ready
+ */
+ ;
+
+ _proto.triggerReady = function triggerReady() {
+ this.isReady_ = true; // Ensure ready is triggered asynchronously
+
+ this.setTimeout(function () {
+ var readyQueue = this.readyQueue_; // Reset Ready Queue
+
+ this.readyQueue_ = [];
+
+ if (readyQueue && readyQueue.length > 0) {
+ readyQueue.forEach(function (fn) {
+ fn.call(this);
+ }, this);
+ } // Allow for using event listeners also
+
+ /**
+ * Triggered when a `Component` is ready.
+ *
+ * @event Component#ready
+ * @type {EventTarget~Event}
+ */
+
+
+ this.trigger('ready');
+ }, 1);
+ }
+ /**
+ * Find a single DOM element matching a `selector`. This can be within the `Component`s
+ * `contentEl()` or another custom context.
+ *
+ * @param {string} selector
+ * A valid CSS selector, which will be passed to `querySelector`.
+ *
+ * @param {Element|string} [context=this.contentEl()]
+ * A DOM element within which to query. Can also be a selector string in
+ * which case the first matching element will get used as context. If
+ * missing `this.contentEl()` gets used. If `this.contentEl()` returns
+ * nothing it falls back to `document`.
+ *
+ * @return {Element|null}
+ * the dom element that was found, or null
+ *
+ * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
+ */
+ ;
+
+ _proto.$ = function $$$1(selector, context) {
+ return $(selector, context || this.contentEl());
+ }
+ /**
+ * Finds all DOM element matching a `selector`. This can be within the `Component`s
+ * `contentEl()` or another custom context.
+ *
+ * @param {string} selector
+ * A valid CSS selector, which will be passed to `querySelectorAll`.
+ *
+ * @param {Element|string} [context=this.contentEl()]
+ * A DOM element within which to query. Can also be a selector string in
+ * which case the first matching element will get used as context. If
+ * missing `this.contentEl()` gets used. If `this.contentEl()` returns
+ * nothing it falls back to `document`.
+ *
+ * @return {NodeList}
+ * a list of dom elements that were found
+ *
+ * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
+ */
+ ;
+
+ _proto.$$ = function $$$$1(selector, context) {
+ return $$(selector, context || this.contentEl());
+ }
+ /**
+ * Check if a component's element has a CSS class name.
+ *
+ * @param {string} classToCheck
+ * CSS class name to check.
+ *
+ * @return {boolean}
+ * - True if the `Component` has the class.
+ * - False if the `Component` does not have the class`
+ */
+ ;
+
+ _proto.hasClass = function hasClass$$1(classToCheck) {
+ return hasClass(this.el_, classToCheck);
+ }
+ /**
+ * Add a CSS class name to the `Component`s element.
+ *
+ * @param {string} classToAdd
+ * CSS class name to add
+ */
+ ;
+
+ _proto.addClass = function addClass$$1(classToAdd) {
+ addClass(this.el_, classToAdd);
+ }
+ /**
+ * Remove a CSS class name from the `Component`s element.
+ *
+ * @param {string} classToRemove
+ * CSS class name to remove
+ */
+ ;
+
+ _proto.removeClass = function removeClass$$1(classToRemove) {
+ removeClass(this.el_, classToRemove);
+ }
+ /**
+ * Add or remove a CSS class name from the component's element.
+ * - `classToToggle` gets added when {@link Component#hasClass} would return false.
+ * - `classToToggle` gets removed when {@link Component#hasClass} would return true.
+ *
+ * @param {string} classToToggle
+ * The class to add or remove based on (@link Component#hasClass}
+ *
+ * @param {boolean|Dom~predicate} [predicate]
+ * An {@link Dom~predicate} function or a boolean
+ */
+ ;
+
+ _proto.toggleClass = function toggleClass$$1(classToToggle, predicate) {
+ toggleClass(this.el_, classToToggle, predicate);
+ }
+ /**
+ * Show the `Component`s element if it is hidden by removing the
+ * 'vjs-hidden' class name from it.
+ */
+ ;
+
+ _proto.show = function show() {
+ this.removeClass('vjs-hidden');
+ }
+ /**
+ * Hide the `Component`s element if it is currently showing by adding the
+ * 'vjs-hidden` class name to it.
+ */
+ ;
+
+ _proto.hide = function hide() {
+ this.addClass('vjs-hidden');
+ }
+ /**
+ * Lock a `Component`s element in its visible state by adding the 'vjs-lock-showing'
+ * class name to it. Used during fadeIn/fadeOut.
+ *
+ * @private
+ */
+ ;
+
+ _proto.lockShowing = function lockShowing() {
+ this.addClass('vjs-lock-showing');
+ }
+ /**
+ * Unlock a `Component`s element from its visible state by removing the 'vjs-lock-showing'
+ * class name from it. Used during fadeIn/fadeOut.
+ *
+ * @private
+ */
+ ;
+
+ _proto.unlockShowing = function unlockShowing() {
+ this.removeClass('vjs-lock-showing');
+ }
+ /**
+ * Get the value of an attribute on the `Component`s element.
+ *
+ * @param {string} attribute
+ * Name of the attribute to get the value from.
+ *
+ * @return {string|null}
+ * - The value of the attribute that was asked for.
+ * - Can be an empty string on some browsers if the attribute does not exist
+ * or has no value
+ * - Most browsers will return null if the attibute does not exist or has
+ * no value.
+ *
+ * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute}
+ */
+ ;
+
+ _proto.getAttribute = function getAttribute$$1(attribute) {
+ return getAttribute(this.el_, attribute);
+ }
+ /**
+ * Set the value of an attribute on the `Component`'s element
+ *
+ * @param {string} attribute
+ * Name of the attribute to set.
+ *
+ * @param {string} value
+ * Value to set the attribute to.
+ *
+ * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute}
+ */
+ ;
+
+ _proto.setAttribute = function setAttribute$$1(attribute, value) {
+ setAttribute(this.el_, attribute, value);
+ }
+ /**
+ * Remove an attribute from the `Component`s element.
+ *
+ * @param {string} attribute
+ * Name of the attribute to remove.
+ *
+ * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute}
+ */
+ ;
+
+ _proto.removeAttribute = function removeAttribute$$1(attribute) {
+ removeAttribute(this.el_, attribute);
+ }
+ /**
+ * Get or set the width of the component based upon the CSS styles.
+ * See {@link Component#dimension} for more detailed information.
+ *
+ * @param {number|string} [num]
+ * The width that you want to set postfixed with '%', 'px' or nothing.
+ *
+ * @param {boolean} [skipListeners]
+ * Skip the componentresize event trigger
+ *
+ * @return {number|string}
+ * The width when getting, zero if there is no width. Can be a string
+ * postpixed with '%' or 'px'.
+ */
+ ;
+
+ _proto.width = function width(num, skipListeners) {
+ return this.dimension('width', num, skipListeners);
+ }
+ /**
+ * Get or set the height of the component based upon the CSS styles.
+ * See {@link Component#dimension} for more detailed information.
+ *
+ * @param {number|string} [num]
+ * The height that you want to set postfixed with '%', 'px' or nothing.
+ *
+ * @param {boolean} [skipListeners]
+ * Skip the componentresize event trigger
+ *
+ * @return {number|string}
+ * The width when getting, zero if there is no width. Can be a string
+ * postpixed with '%' or 'px'.
+ */
+ ;
+
+ _proto.height = function height(num, skipListeners) {
+ return this.dimension('height', num, skipListeners);
+ }
+ /**
+ * Set both the width and height of the `Component` element at the same time.
+ *
+ * @param {number|string} width
+ * Width to set the `Component`s element to.
+ *
+ * @param {number|string} height
+ * Height to set the `Component`s element to.
+ */
+ ;
+
+ _proto.dimensions = function dimensions(width, height) {
+ // Skip componentresize listeners on width for optimization
+ this.width(width, true);
+ this.height(height);
+ }
+ /**
+ * Get or set width or height of the `Component` element. This is the shared code
+ * for the {@link Component#width} and {@link Component#height}.
+ *
+ * Things to know:
+ * - If the width or height in an number this will return the number postfixed with 'px'.
+ * - If the width/height is a percent this will return the percent postfixed with '%'
+ * - Hidden elements have a width of 0 with `window.getComputedStyle`. This function
+ * defaults to the `Component`s `style.width` and falls back to `window.getComputedStyle`.
+ * See [this]{@link http://www.foliotek.com/devblog/getting-the-width-of-a-hidden-element-with-jquery-using-width/}
+ * for more information
+ * - If you want the computed style of the component, use {@link Component#currentWidth}
+ * and {@link {Component#currentHeight}
+ *
+ * @fires Component#componentresize
+ *
+ * @param {string} widthOrHeight
+ 8 'width' or 'height'
+ *
+ * @param {number|string} [num]
+ 8 New dimension
+ *
+ * @param {boolean} [skipListeners]
+ * Skip componentresize event trigger
+ *
+ * @return {number}
+ * The dimension when getting or 0 if unset
+ */
+ ;
+
+ _proto.dimension = function dimension(widthOrHeight, num, skipListeners) {
+ if (num !== undefined) {
+ // Set to zero if null or literally NaN (NaN !== NaN)
+ if (num === null || num !== num) {
+ num = 0;
+ } // Check if using css width/height (% or px) and adjust
+
+
+ if (('' + num).indexOf('%') !== -1 || ('' + num).indexOf('px') !== -1) {
+ this.el_.style[widthOrHeight] = num;
+ } else if (num === 'auto') {
+ this.el_.style[widthOrHeight] = '';
+ } else {
+ this.el_.style[widthOrHeight] = num + 'px';
+ } // skipListeners allows us to avoid triggering the resize event when setting both width and height
+
+
+ if (!skipListeners) {
+ /**
+ * Triggered when a component is resized.
+ *
+ * @event Component#componentresize
+ * @type {EventTarget~Event}
+ */
+ this.trigger('componentresize');
+ }
+
+ return;
+ } // Not setting a value, so getting it
+ // Make sure element exists
+
+
+ if (!this.el_) {
+ return 0;
+ } // Get dimension value from style
+
+
+ var val = this.el_.style[widthOrHeight];
+ var pxIndex = val.indexOf('px');
+
+ if (pxIndex !== -1) {
+ // Return the pixel value with no 'px'
+ return parseInt(val.slice(0, pxIndex), 10);
+ } // No px so using % or no style was set, so falling back to offsetWidth/height
+ // If component has display:none, offset will return 0
+ // TODO: handle display:none and no dimension style using px
+
+
+ return parseInt(this.el_['offset' + toTitleCase(widthOrHeight)], 10);
+ }
+ /**
+ * Get the computed width or the height of the component's element.
+ *
+ * Uses `window.getComputedStyle`.
+ *
+ * @param {string} widthOrHeight
+ * A string containing 'width' or 'height'. Whichever one you want to get.
+ *
+ * @return {number}
+ * The dimension that gets asked for or 0 if nothing was set
+ * for that dimension.
+ */
+ ;
+
+ _proto.currentDimension = function currentDimension(widthOrHeight) {
+ var computedWidthOrHeight = 0;
+
+ if (widthOrHeight !== 'width' && widthOrHeight !== 'height') {
+ throw new Error('currentDimension only accepts width or height value');
+ }
+
+ if (typeof window$1.getComputedStyle === 'function') {
+ var computedStyle = window$1.getComputedStyle(this.el_);
+ computedWidthOrHeight = computedStyle.getPropertyValue(widthOrHeight) || computedStyle[widthOrHeight];
+ } // remove 'px' from variable and parse as integer
+
+
+ computedWidthOrHeight = parseFloat(computedWidthOrHeight); // if the computed value is still 0, it's possible that the browser is lying
+ // and we want to check the offset values.
+ // This code also runs wherever getComputedStyle doesn't exist.
+
+ if (computedWidthOrHeight === 0) {
+ var rule = "offset" + toTitleCase(widthOrHeight);
+ computedWidthOrHeight = this.el_[rule];
+ }
+
+ return computedWidthOrHeight;
+ }
+ /**
+ * An object that contains width and height values of the `Component`s
+ * computed style. Uses `window.getComputedStyle`.
+ *
+ * @typedef {Object} Component~DimensionObject
+ *
+ * @property {number} width
+ * The width of the `Component`s computed style.
+ *
+ * @property {number} height
+ * The height of the `Component`s computed style.
+ */
+
+ /**
+ * Get an object that contains computed width and height values of the
+ * component's element.
+ *
+ * Uses `window.getComputedStyle`.
+ *
+ * @return {Component~DimensionObject}
+ * The computed dimensions of the component's element.
+ */
+ ;
+
+ _proto.currentDimensions = function currentDimensions() {
+ return {
+ width: this.currentDimension('width'),
+ height: this.currentDimension('height')
+ };
+ }
+ /**
+ * Get the computed width of the component's element.
+ *
+ * Uses `window.getComputedStyle`.
+ *
+ * @return {number}
+ * The computed width of the component's element.
+ */
+ ;
+
+ _proto.currentWidth = function currentWidth() {
+ return this.currentDimension('width');
+ }
+ /**
+ * Get the computed height of the component's element.
+ *
+ * Uses `window.getComputedStyle`.
+ *
+ * @return {number}
+ * The computed height of the component's element.
+ */
+ ;
+
+ _proto.currentHeight = function currentHeight() {
+ return this.currentDimension('height');
+ }
+ /**
+ * Set the focus to this component
+ */
+ ;
+
+ _proto.focus = function focus() {
+ this.el_.focus();
+ }
+ /**
+ * Remove the focus from this component
+ */
+ ;
+
+ _proto.blur = function blur() {
+ this.el_.blur();
+ }
+ /**
+ * When this Component receives a `keydown` event which it does not process,
+ * it passes the event to the Player for handling.
+ *
+ * @param {EventTarget~Event} event
+ * The `keydown` event that caused this function to be called.
+ */
+ ;
+
+ _proto.handleKeyDown = function handleKeyDown(event) {
+ if (this.player_) {
+ // We only stop propagation here because we want unhandled events to fall
+ // back to the browser.
+ event.stopPropagation();
+ this.player_.handleKeyDown(event);
+ }
+ }
+ /**
+ * Many components used to have a `handleKeyPress` method, which was poorly
+ * named because it listened to a `keydown` event. This method name now
+ * delegates to `handleKeyDown`. This means anyone calling `handleKeyPress`
+ * will not see their method calls stop working.
+ *
+ * @param {EventTarget~Event} event
+ * The event that caused this function to be called.
+ */
+ ;
+
+ _proto.handleKeyPress = function handleKeyPress(event) {
+ this.handleKeyDown(event);
+ }
+ /**
+ * Emit a 'tap' events when touch event support gets detected. This gets used to
+ * support toggling the controls through a tap on the video. They get enabled
+ * because every sub-component would have extra overhead otherwise.
+ *
+ * @private
+ * @fires Component#tap
+ * @listens Component#touchstart
+ * @listens Component#touchmove
+ * @listens Component#touchleave
+ * @listens Component#touchcancel
+ * @listens Component#touchend
+ */
+ ;
+
+ _proto.emitTapEvents = function emitTapEvents() {
+ // Track the start time so we can determine how long the touch lasted
+ var touchStart = 0;
+ var firstTouch = null; // Maximum movement allowed during a touch event to still be considered a tap
+ // Other popular libs use anywhere from 2 (hammer.js) to 15,
+ // so 10 seems like a nice, round number.
+
+ var tapMovementThreshold = 10; // The maximum length a touch can be while still being considered a tap
+
+ var touchTimeThreshold = 200;
+ var couldBeTap;
+ this.on('touchstart', function (event) {
+ // If more than one finger, don't consider treating this as a click
+ if (event.touches.length === 1) {
+ // Copy pageX/pageY from the object
+ firstTouch = {
+ pageX: event.touches[0].pageX,
+ pageY: event.touches[0].pageY
+ }; // Record start time so we can detect a tap vs. "touch and hold"
+
+ touchStart = new Date().getTime(); // Reset couldBeTap tracking
+
+ couldBeTap = true;
+ }
+ });
+ this.on('touchmove', function (event) {
+ // If more than one finger, don't consider treating this as a click
+ if (event.touches.length > 1) {
+ couldBeTap = false;
+ } else if (firstTouch) {
+ // Some devices will throw touchmoves for all but the slightest of taps.
+ // So, if we moved only a small distance, this could still be a tap
+ var xdiff = event.touches[0].pageX - firstTouch.pageX;
+ var ydiff = event.touches[0].pageY - firstTouch.pageY;
+ var touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff);
+
+ if (touchDistance > tapMovementThreshold) {
+ couldBeTap = false;
+ }
+ }
+ });
+
+ var noTap = function noTap() {
+ couldBeTap = false;
+ }; // TODO: Listen to the original target. http://youtu.be/DujfpXOKUp8?t=13m8s
+
+
+ this.on('touchleave', noTap);
+ this.on('touchcancel', noTap); // When the touch ends, measure how long it took and trigger the appropriate
+ // event
+
+ this.on('touchend', function (event) {
+ firstTouch = null; // Proceed only if the touchmove/leave/cancel event didn't happen
+
+ if (couldBeTap === true) {
+ // Measure how long the touch lasted
+ var touchTime = new Date().getTime() - touchStart; // Make sure the touch was less than the threshold to be considered a tap
+
+ if (touchTime < touchTimeThreshold) {
+ // Don't let browser turn this into a click
+ event.preventDefault();
+ /**
+ * Triggered when a `Component` is tapped.
+ *
+ * @event Component#tap
+ * @type {EventTarget~Event}
+ */
+
+ this.trigger('tap'); // It may be good to copy the touchend event object and change the
+ // type to tap, if the other event properties aren't exact after
+ // Events.fixEvent runs (e.g. event.target)
+ }
+ }
+ });
+ }
+ /**
+ * This function reports user activity whenever touch events happen. This can get
+ * turned off by any sub-components that wants touch events to act another way.
+ *
+ * Report user touch activity when touch events occur. User activity gets used to
+ * determine when controls should show/hide. It is simple when it comes to mouse
+ * events, because any mouse event should show the controls. So we capture mouse
+ * events that bubble up to the player and report activity when that happens.
+ * With touch events it isn't as easy as `touchstart` and `touchend` toggle player
+ * controls. So touch events can't help us at the player level either.
+ *
+ * User activity gets checked asynchronously. So what could happen is a tap event
+ * on the video turns the controls off. Then the `touchend` event bubbles up to
+ * the player. Which, if it reported user activity, would turn the controls right
+ * back on. We also don't want to completely block touch events from bubbling up.
+ * Furthermore a `touchmove` event and anything other than a tap, should not turn
+ * controls back on.
+ *
+ * @listens Component#touchstart
+ * @listens Component#touchmove
+ * @listens Component#touchend
+ * @listens Component#touchcancel
+ */
+ ;
+
+ _proto.enableTouchActivity = function enableTouchActivity() {
+ // Don't continue if the root player doesn't support reporting user activity
+ if (!this.player() || !this.player().reportUserActivity) {
+ return;
+ } // listener for reporting that the user is active
+
+
+ var report = bind(this.player(), this.player().reportUserActivity);
+ var touchHolding;
+ this.on('touchstart', function () {
+ report(); // For as long as the they are touching the device or have their mouse down,
+ // we consider them active even if they're not moving their finger or mouse.
+ // So we want to continue to update that they are active
+
+ this.clearInterval(touchHolding); // report at the same interval as activityCheck
+
+ touchHolding = this.setInterval(report, 250);
+ });
+
+ var touchEnd = function touchEnd(event) {
+ report(); // stop the interval that maintains activity if the touch is holding
+
+ this.clearInterval(touchHolding);
+ };
+
+ this.on('touchmove', report);
+ this.on('touchend', touchEnd);
+ this.on('touchcancel', touchEnd);
+ }
+ /**
+ * A callback that has no parameters and is bound into `Component`s context.
+ *
+ * @callback Component~GenericCallback
+ * @this Component
+ */
+
+ /**
+ * Creates a function that runs after an `x` millisecond timeout. This function is a
+ * wrapper around `window.setTimeout`. There are a few reasons to use this one
+ * instead though:
+ * 1. It gets cleared via {@link Component#clearTimeout} when
+ * {@link Component#dispose} gets called.
+ * 2. The function callback will gets turned into a {@link Component~GenericCallback}
+ *
+ * > Note: You can't use `window.clearTimeout` on the id returned by this function. This
+ * will cause its dispose listener not to get cleaned up! Please use
+ * {@link Component#clearTimeout} or {@link Component#dispose} instead.
+ *
+ * @param {Component~GenericCallback} fn
+ * The function that will be run after `timeout`.
+ *
+ * @param {number} timeout
+ * Timeout in milliseconds to delay before executing the specified function.
+ *
+ * @return {number}
+ * Returns a timeout ID that gets used to identify the timeout. It can also
+ * get used in {@link Component#clearTimeout} to clear the timeout that
+ * was set.
+ *
+ * @listens Component#dispose
+ * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout}
+ */
+ ;
+
+ _proto.setTimeout = function setTimeout(fn, timeout) {
+ var _this2 = this;
+
+ // declare as variables so they are properly available in timeout function
+ // eslint-disable-next-line
+ var timeoutId, disposeFn;
+ fn = bind(this, fn);
+ timeoutId = window$1.setTimeout(function () {
+ _this2.off('dispose', disposeFn);
+
+ fn();
+ }, timeout);
+
+ disposeFn = function disposeFn() {
+ return _this2.clearTimeout(timeoutId);
+ };
+
+ disposeFn.guid = "vjs-timeout-" + timeoutId;
+ this.on('dispose', disposeFn);
+ return timeoutId;
+ }
+ /**
+ * Clears a timeout that gets created via `window.setTimeout` or
+ * {@link Component#setTimeout}. If you set a timeout via {@link Component#setTimeout}
+ * use this function instead of `window.clearTimout`. If you don't your dispose
+ * listener will not get cleaned up until {@link Component#dispose}!
+ *
+ * @param {number} timeoutId
+ * The id of the timeout to clear. The return value of
+ * {@link Component#setTimeout} or `window.setTimeout`.
+ *
+ * @return {number}
+ * Returns the timeout id that was cleared.
+ *
+ * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearTimeout}
+ */
+ ;
+
+ _proto.clearTimeout = function clearTimeout(timeoutId) {
+ window$1.clearTimeout(timeoutId);
+
+ var disposeFn = function disposeFn() {};
+
+ disposeFn.guid = "vjs-timeout-" + timeoutId;
+ this.off('dispose', disposeFn);
+ return timeoutId;
+ }
+ /**
+ * Creates a function that gets run every `x` milliseconds. This function is a wrapper
+ * around `window.setInterval`. There are a few reasons to use this one instead though.
+ * 1. It gets cleared via {@link Component#clearInterval} when
+ * {@link Component#dispose} gets called.
+ * 2. The function callback will be a {@link Component~GenericCallback}
+ *
+ * @param {Component~GenericCallback} fn
+ * The function to run every `x` seconds.
+ *
+ * @param {number} interval
+ * Execute the specified function every `x` milliseconds.
+ *
+ * @return {number}
+ * Returns an id that can be used to identify the interval. It can also be be used in
+ * {@link Component#clearInterval} to clear the interval.
+ *
+ * @listens Component#dispose
+ * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval}
+ */
+ ;
+
+ _proto.setInterval = function setInterval(fn, interval) {
+ var _this3 = this;
+
+ fn = bind(this, fn);
+ var intervalId = window$1.setInterval(fn, interval);
+
+ var disposeFn = function disposeFn() {
+ return _this3.clearInterval(intervalId);
+ };
+
+ disposeFn.guid = "vjs-interval-" + intervalId;
+ this.on('dispose', disposeFn);
+ return intervalId;
+ }
+ /**
+ * Clears an interval that gets created via `window.setInterval` or
+ * {@link Component#setInterval}. If you set an inteval via {@link Component#setInterval}
+ * use this function instead of `window.clearInterval`. If you don't your dispose
+ * listener will not get cleaned up until {@link Component#dispose}!
+ *
+ * @param {number} intervalId
+ * The id of the interval to clear. The return value of
+ * {@link Component#setInterval} or `window.setInterval`.
+ *
+ * @return {number}
+ * Returns the interval id that was cleared.
+ *
+ * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearInterval}
+ */
+ ;
+
+ _proto.clearInterval = function clearInterval(intervalId) {
+ window$1.clearInterval(intervalId);
+
+ var disposeFn = function disposeFn() {};
+
+ disposeFn.guid = "vjs-interval-" + intervalId;
+ this.off('dispose', disposeFn);
+ return intervalId;
+ }
+ /**
+ * Queues up a callback to be passed to requestAnimationFrame (rAF), but
+ * with a few extra bonuses:
+ *
+ * - Supports browsers that do not support rAF by falling back to
+ * {@link Component#setTimeout}.
+ *
+ * - The callback is turned into a {@link Component~GenericCallback} (i.e.
+ * bound to the component).
+ *
+ * - Automatic cancellation of the rAF callback is handled if the component
+ * is disposed before it is called.
+ *
+ * @param {Component~GenericCallback} fn
+ * A function that will be bound to this component and executed just
+ * before the browser's next repaint.
+ *
+ * @return {number}
+ * Returns an rAF ID that gets used to identify the timeout. It can
+ * also be used in {@link Component#cancelAnimationFrame} to cancel
+ * the animation frame callback.
+ *
+ * @listens Component#dispose
+ * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame}
+ */
+ ;
+
+ _proto.requestAnimationFrame = function requestAnimationFrame(fn) {
+ var _this4 = this;
+
+ // declare as variables so they are properly available in rAF function
+ // eslint-disable-next-line
+ var id, disposeFn;
+
+ if (this.supportsRaf_) {
+ fn = bind(this, fn);
+ id = window$1.requestAnimationFrame(function () {
+ _this4.off('dispose', disposeFn);
+
+ fn();
+ });
+
+ disposeFn = function disposeFn() {
+ return _this4.cancelAnimationFrame(id);
+ };
+
+ disposeFn.guid = "vjs-raf-" + id;
+ this.on('dispose', disposeFn);
+ return id;
+ } // Fall back to using a timer.
+
+
+ return this.setTimeout(fn, 1000 / 60);
+ }
+ /**
+ * Cancels a queued callback passed to {@link Component#requestAnimationFrame}
+ * (rAF).
+ *
+ * If you queue an rAF callback via {@link Component#requestAnimationFrame},
+ * use this function instead of `window.cancelAnimationFrame`. If you don't,
+ * your dispose listener will not get cleaned up until {@link Component#dispose}!
+ *
+ * @param {number} id
+ * The rAF ID to clear. The return value of {@link Component#requestAnimationFrame}.
+ *
+ * @return {number}
+ * Returns the rAF ID that was cleared.
+ *
+ * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/cancelAnimationFrame}
+ */
+ ;
+
+ _proto.cancelAnimationFrame = function cancelAnimationFrame(id) {
+ if (this.supportsRaf_) {
+ window$1.cancelAnimationFrame(id);
+
+ var disposeFn = function disposeFn() {};
+
+ disposeFn.guid = "vjs-raf-" + id;
+ this.off('dispose', disposeFn);
+ return id;
+ } // Fall back to using a timer.
+
+
+ return this.clearTimeout(id);
+ }
+ /**
+ * Register a `Component` with `videojs` given the name and the component.
+ *
+ * > NOTE: {@link Tech}s should not be registered as a `Component`. {@link Tech}s
+ * should be registered using {@link Tech.registerTech} or
+ * {@link videojs:videojs.registerTech}.
+ *
+ * > NOTE: This function can also be seen on videojs as
+ * {@link videojs:videojs.registerComponent}.
+ *
+ * @param {string} name
+ * The name of the `Component` to register.
+ *
+ * @param {Component} ComponentToRegister
+ * The `Component` class to register.
+ *
+ * @return {Component}
+ * The `Component` that was registered.
+ */
+ ;
+
+ Component.registerComponent = function registerComponent(name, ComponentToRegister) {
+ if (typeof name !== 'string' || !name) {
+ throw new Error("Illegal component name, \"" + name + "\"; must be a non-empty string.");
+ }
+
+ var Tech = Component.getComponent('Tech'); // We need to make sure this check is only done if Tech has been registered.
+
+ var isTech = Tech && Tech.isTech(ComponentToRegister);
+ var isComp = Component === ComponentToRegister || Component.prototype.isPrototypeOf(ComponentToRegister.prototype);
+
+ if (isTech || !isComp) {
+ var reason;
+
+ if (isTech) {
+ reason = 'techs must be registered using Tech.registerTech()';
+ } else {
+ reason = 'must be a Component subclass';
+ }
+
+ throw new Error("Illegal component, \"" + name + "\"; " + reason + ".");
+ }
+
+ name = toTitleCase(name);
+
+ if (!Component.components_) {
+ Component.components_ = {};
+ }
+
+ var Player = Component.getComponent('Player');
+
+ if (name === 'Player' && Player && Player.players) {
+ var players = Player.players;
+ var playerNames = Object.keys(players); // If we have players that were disposed, then their name will still be
+ // in Players.players. So, we must loop through and verify that the value
+ // for each item is not null. This allows registration of the Player component
+ // after all players have been disposed or before any were created.
+
+ if (players && playerNames.length > 0 && playerNames.map(function (pname) {
+ return players[pname];
+ }).every(Boolean)) {
+ throw new Error('Can not register Player component after player has been created.');
+ }
+ }
+
+ Component.components_[name] = ComponentToRegister;
+ return ComponentToRegister;
+ }
+ /**
+ * Get a `Component` based on the name it was registered with.
+ *
+ * @param {string} name
+ * The Name of the component to get.
+ *
+ * @return {Component}
+ * The `Component` that got registered under the given name.
+ *
+ * @deprecated In `videojs` 6 this will not return `Component`s that were not
+ * registered using {@link Component.registerComponent}. Currently we
+ * check the global `videojs` object for a `Component` name and
+ * return that if it exists.
+ */
+ ;
+
+ Component.getComponent = function getComponent(name) {
+ if (!name) {
+ return;
+ }
+
+ name = toTitleCase(name);
+
+ if (Component.components_ && Component.components_[name]) {
+ return Component.components_[name];
+ }
+ };
+
+ return Component;
+ }();
+ /**
+ * Whether or not this component supports `requestAnimationFrame`.
+ *
+ * This is exposed primarily for testing purposes.
+ *
+ * @private
+ * @type {Boolean}
+ */
+
+
+ Component.prototype.supportsRaf_ = typeof window$1.requestAnimationFrame === 'function' && typeof window$1.cancelAnimationFrame === 'function';
+ Component.registerComponent('Component', Component);
+
+ /**
+ * @file browser.js
+ * @module browser
+ */
+ var USER_AGENT = window$1.navigator && window$1.navigator.userAgent || '';
+ var webkitVersionMap = /AppleWebKit\/([\d.]+)/i.exec(USER_AGENT);
+ var appleWebkitVersion = webkitVersionMap ? parseFloat(webkitVersionMap.pop()) : null;
+ /**
+ * Whether or not this device is an iPad.
+ *
+ * @static
+ * @const
+ * @type {Boolean}
+ */
+
+ var IS_IPAD = /iPad/i.test(USER_AGENT);
+ /**
+ * Whether or not this device is an iPhone.
+ *
+ * @static
+ * @const
+ * @type {Boolean}
+ */
+ // The Facebook app's UIWebView identifies as both an iPhone and iPad, so
+ // to identify iPhones, we need to exclude iPads.
+ // http://artsy.github.io/blog/2012/10/18/the-perils-of-ios-user-agent-sniffing/
+
+ var IS_IPHONE = /iPhone/i.test(USER_AGENT) && !IS_IPAD;
+ /**
+ * Whether or not this device is an iPod.
+ *
+ * @static
+ * @const
+ * @type {Boolean}
+ */
+
+ var IS_IPOD = /iPod/i.test(USER_AGENT);
+ /**
+ * Whether or not this is an iOS device.
+ *
+ * @static
+ * @const
+ * @type {Boolean}
+ */
+
+ var IS_IOS = IS_IPHONE || IS_IPAD || IS_IPOD;
+ /**
+ * The detected iOS version - or `null`.
+ *
+ * @static
+ * @const
+ * @type {string|null}
+ */
+
+ var IOS_VERSION = function () {
+ var match = USER_AGENT.match(/OS (\d+)_/i);
+
+ if (match && match[1]) {
+ return match[1];
+ }
+
+ return null;
+ }();
+ /**
+ * Whether or not this is an Android device.
+ *
+ * @static
+ * @const
+ * @type {Boolean}
+ */
+
+ var IS_ANDROID = /Android/i.test(USER_AGENT);
+ /**
+ * The detected Android version - or `null`.
+ *
+ * @static
+ * @const
+ * @type {number|string|null}
+ */
+
+ var ANDROID_VERSION = function () {
+ // This matches Android Major.Minor.Patch versions
+ // ANDROID_VERSION is Major.Minor as a Number, if Minor isn't available, then only Major is returned
+ var match = USER_AGENT.match(/Android (\d+)(?:\.(\d+))?(?:\.(\d+))*/i);
+
+ if (!match) {
+ return null;
+ }
+
+ var major = match[1] && parseFloat(match[1]);
+ var minor = match[2] && parseFloat(match[2]);
+
+ if (major && minor) {
+ return parseFloat(match[1] + '.' + match[2]);
+ } else if (major) {
+ return major;
+ }
+
+ return null;
+ }();
+ /**
+ * Whether or not this is a native Android browser.
+ *
+ * @static
+ * @const
+ * @type {Boolean}
+ */
+
+ var IS_NATIVE_ANDROID = IS_ANDROID && ANDROID_VERSION < 5 && appleWebkitVersion < 537;
+ /**
+ * Whether or not this is Mozilla Firefox.
+ *
+ * @static
+ * @const
+ * @type {Boolean}
+ */
+
+ var IS_FIREFOX = /Firefox/i.test(USER_AGENT);
+ /**
+ * Whether or not this is Microsoft Edge.
+ *
+ * @static
+ * @const
+ * @type {Boolean}
+ */
+
+ var IS_EDGE = /Edge/i.test(USER_AGENT);
+ /**
+ * Whether or not this is Google Chrome.
+ *
+ * This will also be `true` for Chrome on iOS, which will have different support
+ * as it is actually Safari under the hood.
+ *
+ * @static
+ * @const
+ * @type {Boolean}
+ */
+
+ var IS_CHROME = !IS_EDGE && (/Chrome/i.test(USER_AGENT) || /CriOS/i.test(USER_AGENT));
+ /**
+ * The detected Google Chrome version - or `null`.
+ *
+ * @static
+ * @const
+ * @type {number|null}
+ */
+
+ var CHROME_VERSION = function () {
+ var match = USER_AGENT.match(/(Chrome|CriOS)\/(\d+)/);
+
+ if (match && match[2]) {
+ return parseFloat(match[2]);
+ }
+
+ return null;
+ }();
+ /**
+ * The detected Internet Explorer version - or `null`.
+ *
+ * @static
+ * @const
+ * @type {number|null}
+ */
+
+ var IE_VERSION = function () {
+ var result = /MSIE\s(\d+)\.\d/.exec(USER_AGENT);
+ var version = result && parseFloat(result[1]);
+
+ if (!version && /Trident\/7.0/i.test(USER_AGENT) && /rv:11.0/.test(USER_AGENT)) {
+ // IE 11 has a different user agent string than other IE versions
+ version = 11.0;
+ }
+
+ return version;
+ }();
+ /**
+ * Whether or not this is desktop Safari.
+ *
+ * @static
+ * @const
+ * @type {Boolean}
+ */
+
+ var IS_SAFARI = /Safari/i.test(USER_AGENT) && !IS_CHROME && !IS_ANDROID && !IS_EDGE;
+ /**
+ * Whether or not this is any flavor of Safari - including iOS.
+ *
+ * @static
+ * @const
+ * @type {Boolean}
+ */
+
+ var IS_ANY_SAFARI = (IS_SAFARI || IS_IOS) && !IS_CHROME;
+ /**
+ * Whether or not this device is touch-enabled.
+ *
+ * @static
+ * @const
+ * @type {Boolean}
+ */
+
+ var TOUCH_ENABLED = isReal() && ('ontouchstart' in window$1 || window$1.navigator.maxTouchPoints || window$1.DocumentTouch && window$1.document instanceof window$1.DocumentTouch);
+
+ var browser = /*#__PURE__*/Object.freeze({
+ IS_IPAD: IS_IPAD,
+ IS_IPHONE: IS_IPHONE,
+ IS_IPOD: IS_IPOD,
+ IS_IOS: IS_IOS,
+ IOS_VERSION: IOS_VERSION,
+ IS_ANDROID: IS_ANDROID,
+ ANDROID_VERSION: ANDROID_VERSION,
+ IS_NATIVE_ANDROID: IS_NATIVE_ANDROID,
+ IS_FIREFOX: IS_FIREFOX,
+ IS_EDGE: IS_EDGE,
+ IS_CHROME: IS_CHROME,
+ CHROME_VERSION: CHROME_VERSION,
+ IE_VERSION: IE_VERSION,
+ IS_SAFARI: IS_SAFARI,
+ IS_ANY_SAFARI: IS_ANY_SAFARI,
+ TOUCH_ENABLED: TOUCH_ENABLED
+ });
+
+ /**
+ * @file time-ranges.js
+ * @module time-ranges
+ */
+
+ /**
+ * Returns the time for the specified index at the start or end
+ * of a TimeRange object.
+ *
+ * @typedef {Function} TimeRangeIndex
+ *
+ * @param {number} [index=0]
+ * The range number to return the time for.
+ *
+ * @return {number}
+ * The time offset at the specified index.
+ *
+ * @deprecated The index argument must be provided.
+ * In the future, leaving it out will throw an error.
+ */
+
+ /**
+ * An object that contains ranges of time.
+ *
+ * @typedef {Object} TimeRange
+ *
+ * @property {number} length
+ * The number of time ranges represented by this object.
+ *
+ * @property {module:time-ranges~TimeRangeIndex} start
+ * Returns the time offset at which a specified time range begins.
+ *
+ * @property {module:time-ranges~TimeRangeIndex} end
+ * Returns the time offset at which a specified time range ends.
+ *
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TimeRanges
+ */
+
+ /**
+ * Check if any of the time ranges are over the maximum index.
+ *
+ * @private
+ * @param {string} fnName
+ * The function name to use for logging
+ *
+ * @param {number} index
+ * The index to check
+ *
+ * @param {number} maxIndex
+ * The maximum possible index
+ *
+ * @throws {Error} if the timeRanges provided are over the maxIndex
+ */
+ function rangeCheck(fnName, index, maxIndex) {
+ if (typeof index !== 'number' || index < 0 || index > maxIndex) {
+ throw new Error("Failed to execute '" + fnName + "' on 'TimeRanges': The index provided (" + index + ") is non-numeric or out of bounds (0-" + maxIndex + ").");
+ }
+ }
+ /**
+ * Get the time for the specified index at the start or end
+ * of a TimeRange object.
+ *
+ * @private
+ * @param {string} fnName
+ * The function name to use for logging
+ *
+ * @param {string} valueIndex
+ * The property that should be used to get the time. should be
+ * 'start' or 'end'
+ *
+ * @param {Array} ranges
+ * An array of time ranges
+ *
+ * @param {Array} [rangeIndex=0]
+ * The index to start the search at
+ *
+ * @return {number}
+ * The time that offset at the specified index.
+ *
+ * @deprecated rangeIndex must be set to a value, in the future this will throw an error.
+ * @throws {Error} if rangeIndex is more than the length of ranges
+ */
+
+
+ function getRange(fnName, valueIndex, ranges, rangeIndex) {
+ rangeCheck(fnName, rangeIndex, ranges.length - 1);
+ return ranges[rangeIndex][valueIndex];
+ }
+ /**
+ * Create a time range object given ranges of time.
+ *
+ * @private
+ * @param {Array} [ranges]
+ * An array of time ranges.
+ */
+
+
+ function createTimeRangesObj(ranges) {
+ if (ranges === undefined || ranges.length === 0) {
+ return {
+ length: 0,
+ start: function start() {
+ throw new Error('This TimeRanges object is empty');
+ },
+ end: function end() {
+ throw new Error('This TimeRanges object is empty');
+ }
+ };
+ }
+
+ return {
+ length: ranges.length,
+ start: getRange.bind(null, 'start', 0, ranges),
+ end: getRange.bind(null, 'end', 1, ranges)
+ };
+ }
+ /**
+ * Create a `TimeRange` object which mimics an
+ * {@link https://developer.mozilla.org/en-US/docs/Web/API/TimeRanges|HTML5 TimeRanges instance}.
+ *
+ * @param {number|Array[]} start
+ * The start of a single range (a number) or an array of ranges (an
+ * array of arrays of two numbers each).
+ *
+ * @param {number} end
+ * The end of a single range. Cannot be used with the array form of
+ * the `start` argument.
+ */
+
+
+ function createTimeRanges(start, end) {
+ if (Array.isArray(start)) {
+ return createTimeRangesObj(start);
+ } else if (start === undefined || end === undefined) {
+ return createTimeRangesObj();
+ }
+
+ return createTimeRangesObj([[start, end]]);
+ }
+
+ /**
+ * @file buffer.js
+ * @module buffer
+ */
+ /**
+ * Compute the percentage of the media that has been buffered.
+ *
+ * @param {TimeRange} buffered
+ * The current `TimeRange` object representing buffered time ranges
+ *
+ * @param {number} duration
+ * Total duration of the media
+ *
+ * @return {number}
+ * Percent buffered of the total duration in decimal form.
+ */
+
+ function bufferedPercent(buffered, duration) {
+ var bufferedDuration = 0;
+ var start;
+ var end;
+
+ if (!duration) {
+ return 0;
+ }
+
+ if (!buffered || !buffered.length) {
+ buffered = createTimeRanges(0, 0);
+ }
+
+ for (var i = 0; i < buffered.length; i++) {
+ start = buffered.start(i);
+ end = buffered.end(i); // buffered end can be bigger than duration by a very small fraction
+
+ if (end > duration) {
+ end = duration;
+ }
+
+ bufferedDuration += end - start;
+ }
+
+ return bufferedDuration / duration;
+ }
+
+ /**
+ * @file fullscreen-api.js
+ * @module fullscreen-api
+ * @private
+ */
+ /**
+ * Store the browser-specific methods for the fullscreen API.
+ *
+ * @type {Object}
+ * @see [Specification]{@link https://fullscreen.spec.whatwg.org}
+ * @see [Map Approach From Screenfull.js]{@link https://github.com/sindresorhus/screenfull.js}
+ */
+
+ var FullscreenApi = {}; // browser API methods
+
+ var apiMap = [['requestFullscreen', 'exitFullscreen', 'fullscreenElement', 'fullscreenEnabled', 'fullscreenchange', 'fullscreenerror', 'fullscreen'], // WebKit
+ ['webkitRequestFullscreen', 'webkitExitFullscreen', 'webkitFullscreenElement', 'webkitFullscreenEnabled', 'webkitfullscreenchange', 'webkitfullscreenerror', '-webkit-full-screen'], // Mozilla
+ ['mozRequestFullScreen', 'mozCancelFullScreen', 'mozFullScreenElement', 'mozFullScreenEnabled', 'mozfullscreenchange', 'mozfullscreenerror', '-moz-full-screen'], // Microsoft
+ ['msRequestFullscreen', 'msExitFullscreen', 'msFullscreenElement', 'msFullscreenEnabled', 'MSFullscreenChange', 'MSFullscreenError', '-ms-fullscreen']];
+ var specApi = apiMap[0];
+ var browserApi;
+ var prefixedAPI = false; // determine the supported set of functions
+
+ for (var i = 0; i < apiMap.length; i++) {
+ // check for exitFullscreen function
+ if (apiMap[i][1] in document) {
+ browserApi = apiMap[i];
+ break;
+ }
+ } // map the browser API names to the spec API names
+
+
+ if (browserApi) {
+ for (var _i = 0; _i < browserApi.length; _i++) {
+ FullscreenApi[specApi[_i]] = browserApi[_i];
+ }
+
+ prefixedAPI = browserApi[0] === specApi[0];
+ }
+
+ /**
+ * @file media-error.js
+ */
+ /**
+ * A Custom `MediaError` class which mimics the standard HTML5 `MediaError` class.
+ *
+ * @param {number|string|Object|MediaError} value
+ * This can be of multiple types:
+ * - number: should be a standard error code
+ * - string: an error message (the code will be 0)
+ * - Object: arbitrary properties
+ * - `MediaError` (native): used to populate a video.js `MediaError` object
+ * - `MediaError` (video.js): will return itself if it's already a
+ * video.js `MediaError` object.
+ *
+ * @see [MediaError Spec]{@link https://dev.w3.org/html5/spec-author-view/video.html#mediaerror}
+ * @see [Encrypted MediaError Spec]{@link https://www.w3.org/TR/2013/WD-encrypted-media-20130510/#error-codes}
+ *
+ * @class MediaError
+ */
+
+ function MediaError(value) {
+ // Allow redundant calls to this constructor to avoid having `instanceof`
+ // checks peppered around the code.
+ if (value instanceof MediaError) {
+ return value;
+ }
+
+ if (typeof value === 'number') {
+ this.code = value;
+ } else if (typeof value === 'string') {
+ // default code is zero, so this is a custom error
+ this.message = value;
+ } else if (isObject(value)) {
+ // We assign the `code` property manually because native `MediaError` objects
+ // do not expose it as an own/enumerable property of the object.
+ if (typeof value.code === 'number') {
+ this.code = value.code;
+ }
+
+ assign(this, value);
+ }
+
+ if (!this.message) {
+ this.message = MediaError.defaultMessages[this.code] || '';
+ }
+ }
+ /**
+ * The error code that refers two one of the defined `MediaError` types
+ *
+ * @type {Number}
+ */
+
+
+ MediaError.prototype.code = 0;
+ /**
+ * An optional message that to show with the error. Message is not part of the HTML5
+ * video spec but allows for more informative custom errors.
+ *
+ * @type {String}
+ */
+
+ MediaError.prototype.message = '';
+ /**
+ * An optional status code that can be set by plugins to allow even more detail about
+ * the error. For example a plugin might provide a specific HTTP status code and an
+ * error message for that code. Then when the plugin gets that error this class will
+ * know how to display an error message for it. This allows a custom message to show
+ * up on the `Player` error overlay.
+ *
+ * @type {Array}
+ */
+
+ MediaError.prototype.status = null;
+ /**
+ * Errors indexed by the W3C standard. The order **CANNOT CHANGE**! See the
+ * specification listed under {@link MediaError} for more information.
+ *
+ * @enum {array}
+ * @readonly
+ * @property {string} 0 - MEDIA_ERR_CUSTOM
+ * @property {string} 1 - MEDIA_ERR_ABORTED
+ * @property {string} 2 - MEDIA_ERR_NETWORK
+ * @property {string} 3 - MEDIA_ERR_DECODE
+ * @property {string} 4 - MEDIA_ERR_SRC_NOT_SUPPORTED
+ * @property {string} 5 - MEDIA_ERR_ENCRYPTED
+ */
+
+ MediaError.errorTypes = ['MEDIA_ERR_CUSTOM', 'MEDIA_ERR_ABORTED', 'MEDIA_ERR_NETWORK', 'MEDIA_ERR_DECODE', 'MEDIA_ERR_SRC_NOT_SUPPORTED', 'MEDIA_ERR_ENCRYPTED'];
+ /**
+ * The default `MediaError` messages based on the {@link MediaError.errorTypes}.
+ *
+ * @type {Array}
+ * @constant
+ */
+
+ MediaError.defaultMessages = {
+ 1: 'You aborted the media playback',
+ 2: 'A network error caused the media download to fail part-way.',
+ 3: 'The media playback was aborted due to a corruption problem or because the media used features your browser did not support.',
+ 4: 'The media could not be loaded, either because the server or network failed or because the format is not supported.',
+ 5: 'The media is encrypted and we do not have the keys to decrypt it.'
+ }; // Add types as properties on MediaError
+ // e.g. MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED = 4;
+
+ for (var errNum = 0; errNum < MediaError.errorTypes.length; errNum++) {
+ MediaError[MediaError.errorTypes[errNum]] = errNum; // values should be accessible on both the class and instance
+
+ MediaError.prototype[MediaError.errorTypes[errNum]] = errNum;
+ } // jsdocs for instance/static members added above
+
+ var tuple = SafeParseTuple;
+
+ function SafeParseTuple(obj, reviver) {
+ var json;
+ var error = null;
+
+ try {
+ json = JSON.parse(obj, reviver);
+ } catch (err) {
+ error = err;
+ }
+
+ return [error, json];
+ }
+
+ /**
+ * Returns whether an object is `Promise`-like (i.e. has a `then` method).
+ *
+ * @param {Object} value
+ * An object that may or may not be `Promise`-like.
+ *
+ * @return {boolean}
+ * Whether or not the object is `Promise`-like.
+ */
+ function isPromise(value) {
+ return value !== undefined && value !== null && typeof value.then === 'function';
+ }
+ /**
+ * 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 (isPromise(value)) {
+ value.then(null, function (e) {});
+ }
+ }
+
+ /**
+ * @file text-track-list-converter.js Utilities for capturing text track state and
+ * re-creating tracks based on a capture.
+ *
+ * @module text-track-list-converter
+ */
+
+ /**
+ * Examine a single {@link TextTrack} and return a JSON-compatible javascript object that
+ * represents the {@link TextTrack}'s state.
+ *
+ * @param {TextTrack} track
+ * The text track to query.
+ *
+ * @return {Object}
+ * A serializable javascript representation of the TextTrack.
+ * @private
+ */
+ var trackToJson_ = function trackToJson_(track) {
+ var ret = ['kind', 'label', 'language', 'id', 'inBandMetadataTrackDispatchType', 'mode', 'src'].reduce(function (acc, prop, i) {
+ if (track[prop]) {
+ acc[prop] = track[prop];
+ }
+
+ return acc;
+ }, {
+ cues: track.cues && Array.prototype.map.call(track.cues, function (cue) {
+ return {
+ startTime: cue.startTime,
+ endTime: cue.endTime,
+ text: cue.text,
+ id: cue.id
+ };
+ })
+ });
+ return ret;
+ };
+ /**
+ * Examine a {@link Tech} and return a JSON-compatible javascript array that represents the
+ * state of all {@link TextTrack}s currently configured. The return array is compatible with
+ * {@link text-track-list-converter:jsonToTextTracks}.
+ *
+ * @param {Tech} tech
+ * The tech object to query
+ *
+ * @return {Array}
+ * A serializable javascript representation of the {@link Tech}s
+ * {@link TextTrackList}.
+ */
+
+
+ var textTracksToJson = function textTracksToJson(tech) {
+ var trackEls = tech.$$('track');
+ var trackObjs = Array.prototype.map.call(trackEls, function (t) {
+ return t.track;
+ });
+ var tracks = Array.prototype.map.call(trackEls, function (trackEl) {
+ var json = trackToJson_(trackEl.track);
+
+ if (trackEl.src) {
+ json.src = trackEl.src;
+ }
+
+ return json;
+ });
+ return tracks.concat(Array.prototype.filter.call(tech.textTracks(), function (track) {
+ return trackObjs.indexOf(track) === -1;
+ }).map(trackToJson_));
+ };
+ /**
+ * Create a set of remote {@link TextTrack}s on a {@link Tech} based on an array of javascript
+ * object {@link TextTrack} representations.
+ *
+ * @param {Array} json
+ * An array of `TextTrack` representation objects, like those that would be
+ * produced by `textTracksToJson`.
+ *
+ * @param {Tech} tech
+ * The `Tech` to create the `TextTrack`s on.
+ */
+
+
+ var jsonToTextTracks = function jsonToTextTracks(json, tech) {
+ json.forEach(function (track) {
+ var addedTrack = tech.addRemoteTextTrack(track).track;
+
+ if (!track.src && track.cues) {
+ track.cues.forEach(function (cue) {
+ return addedTrack.addCue(cue);
+ });
+ }
+ });
+ return tech.textTracks();
+ };
+
+ var textTrackConverter = {
+ textTracksToJson: textTracksToJson,
+ jsonToTextTracks: jsonToTextTracks,
+ trackToJson_: trackToJson_
+ };
+
+ function createCommonjsModule(fn, module) {
+ return module = { exports: {} }, fn(module, module.exports), module.exports;
+ }
+
+ var keycode = createCommonjsModule(function (module, exports) {
+ // Source: http://jsfiddle.net/vWx8V/
+ // http://stackoverflow.com/questions/5603195/full-list-of-javascript-keycodes
+
+ /**
+ * Conenience method returns corresponding value for given keyName or keyCode.
+ *
+ * @param {Mixed} keyCode {Number} or keyName {String}
+ * @return {Mixed}
+ * @api public
+ */
+ function keyCode(searchInput) {
+ // Keyboard Events
+ if (searchInput && 'object' === typeof searchInput) {
+ var hasKeyCode = searchInput.which || searchInput.keyCode || searchInput.charCode;
+ if (hasKeyCode) searchInput = hasKeyCode;
+ } // Numbers
+
+
+ if ('number' === typeof searchInput) return names[searchInput]; // Everything else (cast to string)
+
+ var search = String(searchInput); // check codes
+
+ var foundNamedKey = codes[search.toLowerCase()];
+ if (foundNamedKey) return foundNamedKey; // check aliases
+
+ var foundNamedKey = aliases[search.toLowerCase()];
+ if (foundNamedKey) return foundNamedKey; // weird character?
+
+ if (search.length === 1) return search.charCodeAt(0);
+ return undefined;
+ }
+ /**
+ * Compares a keyboard event with a given keyCode or keyName.
+ *
+ * @param {Event} event Keyboard event that should be tested
+ * @param {Mixed} keyCode {Number} or keyName {String}
+ * @return {Boolean}
+ * @api public
+ */
+
+
+ keyCode.isEventKey = function isEventKey(event, nameOrCode) {
+ if (event && 'object' === typeof event) {
+ var keyCode = event.which || event.keyCode || event.charCode;
+
+ if (keyCode === null || keyCode === undefined) {
+ return false;
+ }
+
+ if (typeof nameOrCode === 'string') {
+ // check codes
+ var foundNamedKey = codes[nameOrCode.toLowerCase()];
+
+ if (foundNamedKey) {
+ return foundNamedKey === keyCode;
+ } // check aliases
+
+
+ var foundNamedKey = aliases[nameOrCode.toLowerCase()];
+
+ if (foundNamedKey) {
+ return foundNamedKey === keyCode;
+ }
+ } else if (typeof nameOrCode === 'number') {
+ return nameOrCode === keyCode;
+ }
+
+ return false;
+ }
+ };
+
+ exports = module.exports = keyCode;
+ /**
+ * Get by name
+ *
+ * exports.code['enter'] // => 13
+ */
+
+ var codes = exports.code = exports.codes = {
+ 'backspace': 8,
+ 'tab': 9,
+ 'enter': 13,
+ 'shift': 16,
+ 'ctrl': 17,
+ 'alt': 18,
+ 'pause/break': 19,
+ 'caps lock': 20,
+ 'esc': 27,
+ 'space': 32,
+ 'page up': 33,
+ 'page down': 34,
+ 'end': 35,
+ 'home': 36,
+ 'left': 37,
+ 'up': 38,
+ 'right': 39,
+ 'down': 40,
+ 'insert': 45,
+ 'delete': 46,
+ 'command': 91,
+ 'left command': 91,
+ 'right command': 93,
+ 'numpad *': 106,
+ 'numpad +': 107,
+ 'numpad -': 109,
+ 'numpad .': 110,
+ 'numpad /': 111,
+ 'num lock': 144,
+ 'scroll lock': 145,
+ 'my computer': 182,
+ 'my calculator': 183,
+ ';': 186,
+ '=': 187,
+ ',': 188,
+ '-': 189,
+ '.': 190,
+ '/': 191,
+ '`': 192,
+ '[': 219,
+ '\\': 220,
+ ']': 221,
+ "'": 222 // Helper aliases
+
+ };
+ var aliases = exports.aliases = {
+ 'windows': 91,
+ '⇧': 16,
+ '⌥': 18,
+ '⌃': 17,
+ '⌘': 91,
+ 'ctl': 17,
+ 'control': 17,
+ 'option': 18,
+ 'pause': 19,
+ 'break': 19,
+ 'caps': 20,
+ 'return': 13,
+ 'escape': 27,
+ 'spc': 32,
+ 'spacebar': 32,
+ 'pgup': 33,
+ 'pgdn': 34,
+ 'ins': 45,
+ 'del': 46,
+ 'cmd': 91
+ /*!
+ * Programatically add the following
+ */
+ // lower case chars
+
+ };
+
+ for (i = 97; i < 123; i++) {
+ codes[String.fromCharCode(i)] = i - 32;
+ } // numbers
+
+
+ for (var i = 48; i < 58; i++) {
+ codes[i - 48] = i;
+ } // function keys
+
+
+ for (i = 1; i < 13; i++) {
+ codes['f' + i] = i + 111;
+ } // numpad keys
+
+
+ for (i = 0; i < 10; i++) {
+ codes['numpad ' + i] = i + 96;
+ }
+ /**
+ * Get by code
+ *
+ * exports.name[13] // => 'Enter'
+ */
+
+
+ var names = exports.names = exports.title = {}; // title for backward compat
+ // Create reverse mapping
+
+ for (i in codes) {
+ names[codes[i]] = i;
+ } // Add aliases
+
+
+ for (var alias in aliases) {
+ codes[alias] = aliases[alias];
+ }
+ });
+ var keycode_1 = keycode.code;
+ var keycode_2 = keycode.codes;
+ var keycode_3 = keycode.aliases;
+ var keycode_4 = keycode.names;
+ var keycode_5 = keycode.title;
+
+ var MODAL_CLASS_NAME = 'vjs-modal-dialog';
+ /**
+ * The `ModalDialog` displays over the video and its controls, which blocks
+ * interaction with the player until it is closed.
+ *
+ * Modal dialogs include a "Close" button and will close when that button
+ * is activated - or when ESC is pressed anywhere.
+ *
+ * @extends Component
+ */
+
+ var ModalDialog =
+ /*#__PURE__*/
+ function (_Component) {
+ _inheritsLoose(ModalDialog, _Component);
+
+ /**
+ * Create an instance of this class.
+ *
+ * @param {Player} player
+ * The `Player` that this class should be attached to.
+ *
+ * @param {Object} [options]
+ * The key/value store of player options.
+ *
+ * @param {Mixed} [options.content=undefined]
+ * Provide customized content for this modal.
+ *
+ * @param {string} [options.description]
+ * A text description for the modal, primarily for accessibility.
+ *
+ * @param {boolean} [options.fillAlways=false]
+ * Normally, modals are automatically filled only the first time
+ * they open. This tells the modal to refresh its content
+ * every time it opens.
+ *
+ * @param {string} [options.label]
+ * A text label for the modal, primarily for accessibility.
+ *
+ * @param {boolean} [options.pauseOnOpen=true]
+ * If `true`, playback will will be paused if playing when
+ * the modal opens, and resumed when it closes.
+ *
+ * @param {boolean} [options.temporary=true]
+ * If `true`, the modal can only be opened once; it will be
+ * disposed as soon as it's closed.
+ *
+ * @param {boolean} [options.uncloseable=false]
+ * If `true`, the user will not be able to close the modal
+ * through the UI in the normal ways. Programmatic closing is
+ * still possible.
+ */
+ function ModalDialog(player, options) {
+ var _this;
+
+ _this = _Component.call(this, player, options) || this;
+ _this.opened_ = _this.hasBeenOpened_ = _this.hasBeenFilled_ = false;
+
+ _this.closeable(!_this.options_.uncloseable);
+
+ _this.content(_this.options_.content); // Make sure the contentEl is defined AFTER any children are initialized
+ // because we only want the contents of the modal in the contentEl
+ // (not the UI elements like the close button).
+
+
+ _this.contentEl_ = createEl('div', {
+ className: MODAL_CLASS_NAME + "-content"
+ }, {
+ role: 'document'
+ });
+ _this.descEl_ = createEl('p', {
+ className: MODAL_CLASS_NAME + "-description vjs-control-text",
+ id: _this.el().getAttribute('aria-describedby')
+ });
+ textContent(_this.descEl_, _this.description());
+
+ _this.el_.appendChild(_this.descEl_);
+
+ _this.el_.appendChild(_this.contentEl_);
+
+ return _this;
+ }
+ /**
+ * Create the `ModalDialog`'s DOM element
+ *
+ * @return {Element}
+ * The DOM element that gets created.
+ */
+
+
+ var _proto = ModalDialog.prototype;
+
+ _proto.createEl = function createEl$$1() {
+ return _Component.prototype.createEl.call(this, 'div', {
+ className: this.buildCSSClass(),
+ tabIndex: -1
+ }, {
+ 'aria-describedby': this.id() + "_description",
+ 'aria-hidden': 'true',
+ 'aria-label': this.label(),
+ 'role': 'dialog'
+ });
+ };
+
+ _proto.dispose = function dispose() {
+ this.contentEl_ = null;
+ this.descEl_ = null;
+ this.previouslyActiveEl_ = null;
+
+ _Component.prototype.dispose.call(this);
+ }
+ /**
+ * Builds the default DOM `className`.
+ *
+ * @return {string}
+ * The DOM `className` for this object.
+ */
+ ;
+
+ _proto.buildCSSClass = function buildCSSClass() {
+ return MODAL_CLASS_NAME + " vjs-hidden " + _Component.prototype.buildCSSClass.call(this);
+ }
+ /**
+ * Returns the label string for this modal. Primarily used for accessibility.
+ *
+ * @return {string}
+ * the localized or raw label of this modal.
+ */
+ ;
+
+ _proto.label = function label() {
+ return this.localize(this.options_.label || 'Modal Window');
+ }
+ /**
+ * Returns the description string for this modal. Primarily used for
+ * accessibility.
+ *
+ * @return {string}
+ * The localized or raw description of this modal.
+ */
+ ;
+
+ _proto.description = function description() {
+ var desc = this.options_.description || this.localize('This is a modal window.'); // Append a universal closeability message if the modal is closeable.
+
+ if (this.closeable()) {
+ desc += ' ' + this.localize('This modal can be closed by pressing the Escape key or activating the close button.');
+ }
+
+ return desc;
+ }
+ /**
+ * Opens the modal.
+ *
+ * @fires ModalDialog#beforemodalopen
+ * @fires ModalDialog#modalopen
+ */
+ ;
+
+ _proto.open = function open() {
+ if (!this.opened_) {
+ var player = this.player();
+ /**
+ * Fired just before a `ModalDialog` is opened.
+ *
+ * @event ModalDialog#beforemodalopen
+ * @type {EventTarget~Event}
+ */
+
+ this.trigger('beforemodalopen');
+ this.opened_ = true; // Fill content if the modal has never opened before and
+ // never been filled.
+
+ if (this.options_.fillAlways || !this.hasBeenOpened_ && !this.hasBeenFilled_) {
+ this.fill();
+ } // If the player was playing, pause it and take note of its previously
+ // playing state.
+
+
+ this.wasPlaying_ = !player.paused();
+
+ if (this.options_.pauseOnOpen && this.wasPlaying_) {
+ player.pause();
+ }
+
+ this.on('keydown', this.handleKeyDown); // Hide controls and note if they were enabled.
+
+ this.hadControls_ = player.controls();
+ player.controls(false);
+ this.show();
+ this.conditionalFocus_();
+ this.el().setAttribute('aria-hidden', 'false');
+ /**
+ * Fired just after a `ModalDialog` is opened.
+ *
+ * @event ModalDialog#modalopen
+ * @type {EventTarget~Event}
+ */
+
+ this.trigger('modalopen');
+ this.hasBeenOpened_ = true;
+ }
+ }
+ /**
+ * If the `ModalDialog` is currently open or closed.
+ *
+ * @param {boolean} [value]
+ * If given, it will open (`true`) or close (`false`) the modal.
+ *
+ * @return {boolean}
+ * the current open state of the modaldialog
+ */
+ ;
+
+ _proto.opened = function opened(value) {
+ if (typeof value === 'boolean') {
+ this[value ? 'open' : 'close']();
+ }
+
+ return this.opened_;
+ }
+ /**
+ * Closes the modal, does nothing if the `ModalDialog` is
+ * not open.
+ *
+ * @fires ModalDialog#beforemodalclose
+ * @fires ModalDialog#modalclose
+ */
+ ;
+
+ _proto.close = function close() {
+ if (!this.opened_) {
+ return;
+ }
+
+ var player = this.player();
+ /**
+ * Fired just before a `ModalDialog` is closed.
+ *
+ * @event ModalDialog#beforemodalclose
+ * @type {EventTarget~Event}
+ */
+
+ this.trigger('beforemodalclose');
+ this.opened_ = false;
+
+ if (this.wasPlaying_ && this.options_.pauseOnOpen) {
+ player.play();
+ }
+
+ this.off('keydown', this.handleKeyDown);
+
+ if (this.hadControls_) {
+ player.controls(true);
+ }
+
+ this.hide();
+ this.el().setAttribute('aria-hidden', 'true');
+ /**
+ * Fired just after a `ModalDialog` is closed.
+ *
+ * @event ModalDialog#modalclose
+ * @type {EventTarget~Event}
+ */
+
+ this.trigger('modalclose');
+ this.conditionalBlur_();
+
+ if (this.options_.temporary) {
+ this.dispose();
+ }
+ }
+ /**
+ * Check to see if the `ModalDialog` is closeable via the UI.
+ *
+ * @param {boolean} [value]
+ * If given as a boolean, it will set the `closeable` option.
+ *
+ * @return {boolean}
+ * Returns the final value of the closable option.
+ */
+ ;
+
+ _proto.closeable = function closeable(value) {
+ if (typeof value === 'boolean') {
+ var closeable = this.closeable_ = !!value;
+ var close = this.getChild('closeButton'); // If this is being made closeable and has no close button, add one.
+
+ if (closeable && !close) {
+ // The close button should be a child of the modal - not its
+ // content element, so temporarily change the content element.
+ var temp = this.contentEl_;
+ this.contentEl_ = this.el_;
+ close = this.addChild('closeButton', {
+ controlText: 'Close Modal Dialog'
+ });
+ this.contentEl_ = temp;
+ this.on(close, 'close', this.close);
+ } // If this is being made uncloseable and has a close button, remove it.
+
+
+ if (!closeable && close) {
+ this.off(close, 'close', this.close);
+ this.removeChild(close);
+ close.dispose();
+ }
+ }
+
+ return this.closeable_;
+ }
+ /**
+ * Fill the modal's content element with the modal's "content" option.
+ * The content element will be emptied before this change takes place.
+ */
+ ;
+
+ _proto.fill = function fill() {
+ this.fillWith(this.content());
+ }
+ /**
+ * Fill the modal's content element with arbitrary content.
+ * The content element will be emptied before this change takes place.
+ *
+ * @fires ModalDialog#beforemodalfill
+ * @fires ModalDialog#modalfill
+ *
+ * @param {Mixed} [content]
+ * The same rules apply to this as apply to the `content` option.
+ */
+ ;
+
+ _proto.fillWith = function fillWith(content) {
+ var contentEl = this.contentEl();
+ var parentEl = contentEl.parentNode;
+ var nextSiblingEl = contentEl.nextSibling;
+ /**
+ * Fired just before a `ModalDialog` is filled with content.
+ *
+ * @event ModalDialog#beforemodalfill
+ * @type {EventTarget~Event}
+ */
+
+ this.trigger('beforemodalfill');
+ this.hasBeenFilled_ = true; // Detach the content element from the DOM before performing
+ // manipulation to avoid modifying the live DOM multiple times.
+
+ parentEl.removeChild(contentEl);
+ this.empty();
+ insertContent(contentEl, content);
+ /**
+ * Fired just after a `ModalDialog` is filled with content.
+ *
+ * @event ModalDialog#modalfill
+ * @type {EventTarget~Event}
+ */
+
+ this.trigger('modalfill'); // Re-inject the re-filled content element.
+
+ if (nextSiblingEl) {
+ parentEl.insertBefore(contentEl, nextSiblingEl);
+ } else {
+ parentEl.appendChild(contentEl);
+ } // make sure that the close button is last in the dialog DOM
+
+
+ var closeButton = this.getChild('closeButton');
+
+ if (closeButton) {
+ parentEl.appendChild(closeButton.el_);
+ }
+ }
+ /**
+ * Empties the content element. This happens anytime the modal is filled.
+ *
+ * @fires ModalDialog#beforemodalempty
+ * @fires ModalDialog#modalempty
+ */
+ ;
+
+ _proto.empty = function empty() {
+ /**
+ * Fired just before a `ModalDialog` is emptied.
+ *
+ * @event ModalDialog#beforemodalempty
+ * @type {EventTarget~Event}
+ */
+ this.trigger('beforemodalempty');
+ emptyEl(this.contentEl());
+ /**
+ * Fired just after a `ModalDialog` is emptied.
+ *
+ * @event ModalDialog#modalempty
+ * @type {EventTarget~Event}
+ */
+
+ this.trigger('modalempty');
+ }
+ /**
+ * Gets or sets the modal content, which gets normalized before being
+ * rendered into the DOM.
+ *
+ * This does not update the DOM or fill the modal, but it is called during
+ * that process.
+ *
+ * @param {Mixed} [value]
+ * If defined, sets the internal content value to be used on the
+ * next call(s) to `fill`. This value is normalized before being
+ * inserted. To "clear" the internal content value, pass `null`.
+ *
+ * @return {Mixed}
+ * The current content of the modal dialog
+ */
+ ;
+
+ _proto.content = function content(value) {
+ if (typeof value !== 'undefined') {
+ this.content_ = value;
+ }
+
+ return this.content_;
+ }
+ /**
+ * conditionally focus the modal dialog if focus was previously on the player.
+ *
+ * @private
+ */
+ ;
+
+ _proto.conditionalFocus_ = function conditionalFocus_() {
+ var activeEl = document.activeElement;
+ var playerEl = this.player_.el_;
+ this.previouslyActiveEl_ = null;
+
+ if (playerEl.contains(activeEl) || playerEl === activeEl) {
+ this.previouslyActiveEl_ = activeEl;
+ this.focus();
+ }
+ }
+ /**
+ * conditionally blur the element and refocus the last focused element
+ *
+ * @private
+ */
+ ;
+
+ _proto.conditionalBlur_ = function conditionalBlur_() {
+ if (this.previouslyActiveEl_) {
+ this.previouslyActiveEl_.focus();
+ this.previouslyActiveEl_ = null;
+ }
+ }
+ /**
+ * Keydown handler. Attached when modal is focused.
+ *
+ * @listens keydown
+ */
+ ;
+
+ _proto.handleKeyDown = function handleKeyDown(event) {
+ // Do not allow keydowns to reach out of the modal dialog.
+ event.stopPropagation();
+
+ if (keycode.isEventKey(event, 'Escape') && this.closeable()) {
+ event.preventDefault();
+ this.close();
+ return;
+ } // exit early if it isn't a tab key
+
+
+ if (!keycode.isEventKey(event, 'Tab')) {
+ return;
+ }
+
+ var focusableEls = this.focusableEls_();
+ var activeEl = this.el_.querySelector(':focus');
+ var focusIndex;
+
+ for (var i = 0; i < focusableEls.length; i++) {
+ if (activeEl === focusableEls[i]) {
+ focusIndex = i;
+ break;
+ }
+ }
+
+ if (document.activeElement === this.el_) {
+ focusIndex = 0;
+ }
+
+ if (event.shiftKey && focusIndex === 0) {
+ focusableEls[focusableEls.length - 1].focus();
+ event.preventDefault();
+ } else if (!event.shiftKey && focusIndex === focusableEls.length - 1) {
+ focusableEls[0].focus();
+ event.preventDefault();
+ }
+ }
+ /**
+ * get all focusable elements
+ *
+ * @private
+ */
+ ;
+
+ _proto.focusableEls_ = function focusableEls_() {
+ var allChildren = this.el_.querySelectorAll('*');
+ return Array.prototype.filter.call(allChildren, function (child) {
+ return (child instanceof window$1.HTMLAnchorElement || child instanceof window$1.HTMLAreaElement) && child.hasAttribute('href') || (child instanceof window$1.HTMLInputElement || child instanceof window$1.HTMLSelectElement || child instanceof window$1.HTMLTextAreaElement || child instanceof window$1.HTMLButtonElement) && !child.hasAttribute('disabled') || child instanceof window$1.HTMLIFrameElement || child instanceof window$1.HTMLObjectElement || child instanceof window$1.HTMLEmbedElement || child.hasAttribute('tabindex') && child.getAttribute('tabindex') !== -1 || child.hasAttribute('contenteditable');
+ });
+ };
+
+ return ModalDialog;
+ }(Component);
+ /**
+ * Default options for `ModalDialog` default options.
+ *
+ * @type {Object}
+ * @private
+ */
+
+
+ ModalDialog.prototype.options_ = {
+ pauseOnOpen: true,
+ temporary: true
+ };
+ Component.registerComponent('ModalDialog', ModalDialog);
+
+ /**
+ * Common functionaliy between {@link TextTrackList}, {@link AudioTrackList}, and
+ * {@link VideoTrackList}
+ *
+ * @extends EventTarget
+ */
+
+ var TrackList =
+ /*#__PURE__*/
+ function (_EventTarget) {
+ _inheritsLoose(TrackList, _EventTarget);
+
+ /**
+ * Create an instance of this class
+ *
+ * @param {Track[]} tracks
+ * A list of tracks to initialize the list with.
+ *
+ * @abstract
+ */
+ function TrackList(tracks) {
+ var _this;
+
+ if (tracks === void 0) {
+ tracks = [];
+ }
+
+ _this = _EventTarget.call(this) || this;
+ _this.tracks_ = [];
+ /**
+ * @memberof TrackList
+ * @member {number} length
+ * The current number of `Track`s in the this Trackist.
+ * @instance
+ */
+
+ Object.defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), 'length', {
+ get: function get() {
+ return this.tracks_.length;
+ }
+ });
+
+ for (var i = 0; i < tracks.length; i++) {
+ _this.addTrack(tracks[i]);
+ }
+
+ return _this;
+ }
+ /**
+ * Add a {@link Track} to the `TrackList`
+ *
+ * @param {Track} track
+ * The audio, video, or text track to add to the list.
+ *
+ * @fires TrackList#addtrack
+ */
+
+
+ var _proto = TrackList.prototype;
+
+ _proto.addTrack = function addTrack(track) {
+ var index = this.tracks_.length;
+
+ if (!('' + index in this)) {
+ Object.defineProperty(this, index, {
+ get: function get() {
+ return this.tracks_[index];
+ }
+ });
+ } // Do not add duplicate tracks
+
+
+ if (this.tracks_.indexOf(track) === -1) {
+ this.tracks_.push(track);
+ /**
+ * Triggered when a track is added to a track list.
+ *
+ * @event TrackList#addtrack
+ * @type {EventTarget~Event}
+ * @property {Track} track
+ * A reference to track that was added.
+ */
+
+ this.trigger({
+ track: track,
+ type: 'addtrack',
+ target: this
+ });
+ }
+ }
+ /**
+ * Remove a {@link Track} from the `TrackList`
+ *
+ * @param {Track} rtrack
+ * The audio, video, or text track to remove from the list.
+ *
+ * @fires TrackList#removetrack
+ */
+ ;
+
+ _proto.removeTrack = function removeTrack(rtrack) {
+ var track;
+
+ for (var i = 0, l = this.length; i < l; i++) {
+ if (this[i] === rtrack) {
+ track = this[i];
+
+ if (track.off) {
+ track.off();
+ }
+
+ this.tracks_.splice(i, 1);
+ break;
+ }
+ }
+
+ if (!track) {
+ return;
+ }
+ /**
+ * Triggered when a track is removed from track list.
+ *
+ * @event TrackList#removetrack
+ * @type {EventTarget~Event}
+ * @property {Track} track
+ * A reference to track that was removed.
+ */
+
+
+ this.trigger({
+ track: track,
+ type: 'removetrack',
+ target: this
+ });
+ }
+ /**
+ * Get a Track from the TrackList by a tracks id
+ *
+ * @param {string} id - the id of the track to get
+ * @method getTrackById
+ * @return {Track}
+ * @private
+ */
+ ;
+
+ _proto.getTrackById = function getTrackById(id) {
+ var result = null;
+
+ for (var i = 0, l = this.length; i < l; i++) {
+ var track = this[i];
+
+ if (track.id === id) {
+ result = track;
+ break;
+ }
+ }
+
+ return result;
+ };
+
+ return TrackList;
+ }(EventTarget);
+ /**
+ * Triggered when a different track is selected/enabled.
+ *
+ * @event TrackList#change
+ * @type {EventTarget~Event}
+ */
+
+ /**
+ * Events that can be called with on + eventName. See {@link EventHandler}.
+ *
+ * @property {Object} TrackList#allowedEvents_
+ * @private
+ */
+
+
+ TrackList.prototype.allowedEvents_ = {
+ change: 'change',
+ addtrack: 'addtrack',
+ removetrack: 'removetrack'
+ }; // emulate attribute EventHandler support to allow for feature detection
+
+ for (var event in TrackList.prototype.allowedEvents_) {
+ TrackList.prototype['on' + event] = null;
+ }
+
+ /**
+ * Anywhere we call this function we diverge from the spec
+ * as we only support one enabled audiotrack at a time
+ *
+ * @param {AudioTrackList} list
+ * list to work on
+ *
+ * @param {AudioTrack} track
+ * The track to skip
+ *
+ * @private
+ */
+
+ var disableOthers = function disableOthers(list, track) {
+ for (var i = 0; i < list.length; i++) {
+ if (!Object.keys(list[i]).length || track.id === list[i].id) {
+ continue;
+ } // another audio track is enabled, disable it
+
+
+ list[i].enabled = false;
+ }
+ };
+ /**
+ * The current list of {@link AudioTrack} for a media file.
+ *
+ * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist}
+ * @extends TrackList
+ */
+
+
+ var AudioTrackList =
+ /*#__PURE__*/
+ function (_TrackList) {
+ _inheritsLoose(AudioTrackList, _TrackList);
+
+ /**
+ * Create an instance of this class.
+ *
+ * @param {AudioTrack[]} [tracks=[]]
+ * A list of `AudioTrack` to instantiate the list with.
+ */
+ function AudioTrackList(tracks) {
+ var _this;
+
+ if (tracks === void 0) {
+ tracks = [];
+ }
+
+ // make sure only 1 track is enabled
+ // sorted from last index to first index
+ for (var i = tracks.length - 1; i >= 0; i--) {
+ if (tracks[i].enabled) {
+ disableOthers(tracks, tracks[i]);
+ break;
+ }
+ }
+
+ _this = _TrackList.call(this, tracks) || this;
+ _this.changing_ = false;
+ return _this;
+ }
+ /**
+ * Add an {@link AudioTrack} to the `AudioTrackList`.
+ *
+ * @param {AudioTrack} track
+ * The AudioTrack to add to the list
+ *
+ * @fires TrackList#addtrack
+ */
+
+
+ var _proto = AudioTrackList.prototype;
+
+ _proto.addTrack = function addTrack(track) {
+ var _this2 = this;
+
+ if (track.enabled) {
+ disableOthers(this, track);
+ }
+
+ _TrackList.prototype.addTrack.call(this, track); // native tracks don't have this
+
+
+ if (!track.addEventListener) {
+ return;
+ }
+
+ track.enabledChange_ = function () {
+ // when we are disabling other tracks (since we don't support
+ // more than one track at a time) we will set changing_
+ // to true so that we don't trigger additional change events
+ if (_this2.changing_) {
+ return;
+ }
+
+ _this2.changing_ = true;
+ disableOthers(_this2, track);
+ _this2.changing_ = false;
+
+ _this2.trigger('change');
+ };
+ /**
+ * @listens AudioTrack#enabledchange
+ * @fires TrackList#change
+ */
+
+
+ track.addEventListener('enabledchange', track.enabledChange_);
+ };
+
+ _proto.removeTrack = function removeTrack(rtrack) {
+ _TrackList.prototype.removeTrack.call(this, rtrack);
+
+ if (rtrack.removeEventListener && rtrack.enabledChange_) {
+ rtrack.removeEventListener('enabledchange', rtrack.enabledChange_);
+ rtrack.enabledChange_ = null;
+ }
+ };
+
+ return AudioTrackList;
+ }(TrackList);
+
+ /**
+ * Un-select all other {@link VideoTrack}s that are selected.
+ *
+ * @param {VideoTrackList} list
+ * list to work on
+ *
+ * @param {VideoTrack} track
+ * The track to skip
+ *
+ * @private
+ */
+
+ var disableOthers$1 = function disableOthers(list, track) {
+ for (var i = 0; i < list.length; i++) {
+ if (!Object.keys(list[i]).length || track.id === list[i].id) {
+ continue;
+ } // another video track is enabled, disable it
+
+
+ list[i].selected = false;
+ }
+ };
+ /**
+ * The current list of {@link VideoTrack} for a video.
+ *
+ * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist}
+ * @extends TrackList
+ */
+
+
+ var VideoTrackList =
+ /*#__PURE__*/
+ function (_TrackList) {
+ _inheritsLoose(VideoTrackList, _TrackList);
+
+ /**
+ * Create an instance of this class.
+ *
+ * @param {VideoTrack[]} [tracks=[]]
+ * A list of `VideoTrack` to instantiate the list with.
+ */
+ function VideoTrackList(tracks) {
+ var _this;
+
+ if (tracks === void 0) {
+ tracks = [];
+ }
+
+ // make sure only 1 track is enabled
+ // sorted from last index to first index
+ for (var i = tracks.length - 1; i >= 0; i--) {
+ if (tracks[i].selected) {
+ disableOthers$1(tracks, tracks[i]);
+ break;
+ }
+ }
+
+ _this = _TrackList.call(this, tracks) || this;
+ _this.changing_ = false;
+ /**
+ * @member {number} VideoTrackList#selectedIndex
+ * The current index of the selected {@link VideoTrack`}.
+ */
+
+ Object.defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), 'selectedIndex', {
+ get: function get() {
+ for (var _i = 0; _i < this.length; _i++) {
+ if (this[_i].selected) {
+ return _i;
+ }
+ }
+
+ return -1;
+ },
+ set: function set() {}
+ });
+ return _this;
+ }
+ /**
+ * Add a {@link VideoTrack} to the `VideoTrackList`.
+ *
+ * @param {VideoTrack} track
+ * The VideoTrack to add to the list
+ *
+ * @fires TrackList#addtrack
+ */
+
+
+ var _proto = VideoTrackList.prototype;
+
+ _proto.addTrack = function addTrack(track) {
+ var _this2 = this;
+
+ if (track.selected) {
+ disableOthers$1(this, track);
+ }
+
+ _TrackList.prototype.addTrack.call(this, track); // native tracks don't have this
+
+
+ if (!track.addEventListener) {
+ return;
+ }
+
+ track.selectedChange_ = function () {
+ if (_this2.changing_) {
+ return;
+ }
+
+ _this2.changing_ = true;
+ disableOthers$1(_this2, track);
+ _this2.changing_ = false;
+
+ _this2.trigger('change');
+ };
+ /**
+ * @listens VideoTrack#selectedchange
+ * @fires TrackList#change
+ */
+
+
+ track.addEventListener('selectedchange', track.selectedChange_);
+ };
+
+ _proto.removeTrack = function removeTrack(rtrack) {
+ _TrackList.prototype.removeTrack.call(this, rtrack);
+
+ if (rtrack.removeEventListener && rtrack.selectedChange_) {
+ rtrack.removeEventListener('selectedchange', rtrack.selectedChange_);
+ rtrack.selectedChange_ = null;
+ }
+ };
+
+ return VideoTrackList;
+ }(TrackList);
+
+ /**
+ * The current list of {@link TextTrack} for a media file.
+ *
+ * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttracklist}
+ * @extends TrackList
+ */
+
+ var TextTrackList =
+ /*#__PURE__*/
+ function (_TrackList) {
+ _inheritsLoose(TextTrackList, _TrackList);
+
+ function TextTrackList() {
+ return _TrackList.apply(this, arguments) || this;
+ }
+
+ var _proto = TextTrackList.prototype;
+
+ /**
+ * Add a {@link TextTrack} to the `TextTrackList`
+ *
+ * @param {TextTrack} track
+ * The text track to add to the list.
+ *
+ * @fires TrackList#addtrack
+ */
+ _proto.addTrack = function addTrack(track) {
+ var _this = this;
+
+ _TrackList.prototype.addTrack.call(this, track);
+
+ if (!this.queueChange_) {
+ this.queueChange_ = function () {
+ return _this.queueTrigger('change');
+ };
+ }
+
+ if (!this.triggerSelectedlanguagechange) {
+ this.triggerSelectedlanguagechange_ = function () {
+ return _this.trigger('selectedlanguagechange');
+ };
+ }
+ /**
+ * @listens TextTrack#modechange
+ * @fires TrackList#change
+ */
+
+
+ track.addEventListener('modechange', this.queueChange_);
+ var nonLanguageTextTrackKind = ['metadata', 'chapters'];
+
+ if (nonLanguageTextTrackKind.indexOf(track.kind) === -1) {
+ track.addEventListener('modechange', this.triggerSelectedlanguagechange_);
+ }
+ };
+
+ _proto.removeTrack = function removeTrack(rtrack) {
+ _TrackList.prototype.removeTrack.call(this, rtrack); // manually remove the event handlers we added
+
+
+ if (rtrack.removeEventListener) {
+ if (this.queueChange_) {
+ rtrack.removeEventListener('modechange', this.queueChange_);
+ }
+
+ if (this.selectedlanguagechange_) {
+ rtrack.removeEventListener('modechange', this.triggerSelectedlanguagechange_);
+ }
+ }
+ };
+
+ return TextTrackList;
+ }(TrackList);
+
+ /**
+ * @file html-track-element-list.js
+ */
+
+ /**
+ * The current list of {@link HtmlTrackElement}s.
+ */
+ var HtmlTrackElementList =
+ /*#__PURE__*/
+ function () {
+ /**
+ * Create an instance of this class.
+ *
+ * @param {HtmlTrackElement[]} [tracks=[]]
+ * A list of `HtmlTrackElement` to instantiate the list with.
+ */
+ function HtmlTrackElementList(trackElements) {
+ if (trackElements === void 0) {
+ trackElements = [];
+ }
+
+ this.trackElements_ = [];
+ /**
+ * @memberof HtmlTrackElementList
+ * @member {number} length
+ * The current number of `Track`s in the this Trackist.
+ * @instance
+ */
+
+ Object.defineProperty(this, 'length', {
+ get: function get() {
+ return this.trackElements_.length;
+ }
+ });
+
+ for (var i = 0, length = trackElements.length; i < length; i++) {
+ this.addTrackElement_(trackElements[i]);
+ }
+ }
+ /**
+ * Add an {@link HtmlTrackElement} to the `HtmlTrackElementList`
+ *
+ * @param {HtmlTrackElement} trackElement
+ * The track element to add to the list.
+ *
+ * @private
+ */
+
+
+ var _proto = HtmlTrackElementList.prototype;
+
+ _proto.addTrackElement_ = function addTrackElement_(trackElement) {
+ var index = this.trackElements_.length;
+
+ if (!('' + index in this)) {
+ Object.defineProperty(this, index, {
+ get: function get() {
+ return this.trackElements_[index];
+ }
+ });
+ } // Do not add duplicate elements
+
+
+ if (this.trackElements_.indexOf(trackElement) === -1) {
+ this.trackElements_.push(trackElement);
+ }
+ }
+ /**
+ * Get an {@link HtmlTrackElement} from the `HtmlTrackElementList` given an
+ * {@link TextTrack}.
+ *
+ * @param {TextTrack} track
+ * The track associated with a track element.
+ *
+ * @return {HtmlTrackElement|undefined}
+ * The track element that was found or undefined.
+ *
+ * @private
+ */
+ ;
+
+ _proto.getTrackElementByTrack_ = function getTrackElementByTrack_(track) {
+ var trackElement_;
+
+ for (var i = 0, length = this.trackElements_.length; i < length; i++) {
+ if (track === this.trackElements_[i].track) {
+ trackElement_ = this.trackElements_[i];
+ break;
+ }
+ }
+
+ return trackElement_;
+ }
+ /**
+ * Remove a {@link HtmlTrackElement} from the `HtmlTrackElementList`
+ *
+ * @param {HtmlTrackElement} trackElement
+ * The track element to remove from the list.
+ *
+ * @private
+ */
+ ;
+
+ _proto.removeTrackElement_ = function removeTrackElement_(trackElement) {
+ for (var i = 0, length = this.trackElements_.length; i < length; i++) {
+ if (trackElement === this.trackElements_[i]) {
+ if (this.trackElements_[i].track && typeof this.trackElements_[i].track.off === 'function') {
+ this.trackElements_[i].track.off();
+ }
+
+ if (typeof this.trackElements_[i].off === 'function') {
+ this.trackElements_[i].off();
+ }
+
+ this.trackElements_.splice(i, 1);
+ break;
+ }
+ }
+ };
+
+ return HtmlTrackElementList;
+ }();
+
+ /**
+ * @file text-track-cue-list.js
+ */
+
+ /**
+ * @typedef {Object} TextTrackCueList~TextTrackCue
+ *
+ * @property {string} id
+ * The unique id for this text track cue
+ *
+ * @property {number} startTime
+ * The start time for this text track cue
+ *
+ * @property {number} endTime
+ * The end time for this text track cue
+ *
+ * @property {boolean} pauseOnExit
+ * Pause when the end time is reached if true.
+ *
+ * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcue}
+ */
+
+ /**
+ * A List of TextTrackCues.
+ *
+ * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcuelist}
+ */
+ var TextTrackCueList =
+ /*#__PURE__*/
+ function () {
+ /**
+ * Create an instance of this class..
+ *
+ * @param {Array} cues
+ * A list of cues to be initialized with
+ */
+ function TextTrackCueList(cues) {
+ TextTrackCueList.prototype.setCues_.call(this, cues);
+ /**
+ * @memberof TextTrackCueList
+ * @member {number} length
+ * The current number of `TextTrackCue`s in the TextTrackCueList.
+ * @instance
+ */
+
+ Object.defineProperty(this, 'length', {
+ get: function get() {
+ return this.length_;
+ }
+ });
+ }
+ /**
+ * A setter for cues in this list. Creates getters
+ * an an index for the cues.
+ *
+ * @param {Array} cues
+ * An array of cues to set
+ *
+ * @private
+ */
+
+
+ var _proto = TextTrackCueList.prototype;
+
+ _proto.setCues_ = function setCues_(cues) {
+ var oldLength = this.length || 0;
+ var i = 0;
+ var l = cues.length;
+ this.cues_ = cues;
+ this.length_ = cues.length;
+
+ var defineProp = function defineProp(index) {
+ if (!('' + index in this)) {
+ Object.defineProperty(this, '' + index, {
+ get: function get() {
+ return this.cues_[index];
+ }
+ });
+ }
+ };
+
+ if (oldLength < l) {
+ i = oldLength;
+
+ for (; i < l; i++) {
+ defineProp.call(this, i);
+ }
+ }
+ }
+ /**
+ * Get a `TextTrackCue` that is currently in the `TextTrackCueList` by id.
+ *
+ * @param {string} id
+ * The id of the cue that should be searched for.
+ *
+ * @return {TextTrackCueList~TextTrackCue|null}
+ * A single cue or null if none was found.
+ */
+ ;
+
+ _proto.getCueById = function getCueById(id) {
+ var result = null;
+
+ for (var i = 0, l = this.length; i < l; i++) {
+ var cue = this[i];
+
+ if (cue.id === id) {
+ result = cue;
+ break;
+ }
+ }
+
+ return result;
+ };
+
+ return TextTrackCueList;
+ }();
+
+ /**
+ * @file track-kinds.js
+ */
+
+ /**
+ * All possible `VideoTrackKind`s
+ *
+ * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-videotrack-kind
+ * @typedef VideoTrack~Kind
+ * @enum
+ */
+ var VideoTrackKind = {
+ alternative: 'alternative',
+ captions: 'captions',
+ main: 'main',
+ sign: 'sign',
+ subtitles: 'subtitles',
+ commentary: 'commentary'
+ };
+ /**
+ * All possible `AudioTrackKind`s
+ *
+ * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-audiotrack-kind
+ * @typedef AudioTrack~Kind
+ * @enum
+ */
+
+ var AudioTrackKind = {
+ 'alternative': 'alternative',
+ 'descriptions': 'descriptions',
+ 'main': 'main',
+ 'main-desc': 'main-desc',
+ 'translation': 'translation',
+ 'commentary': 'commentary'
+ };
+ /**
+ * All possible `TextTrackKind`s
+ *
+ * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-texttrack-kind
+ * @typedef TextTrack~Kind
+ * @enum
+ */
+
+ var TextTrackKind = {
+ subtitles: 'subtitles',
+ captions: 'captions',
+ descriptions: 'descriptions',
+ chapters: 'chapters',
+ metadata: 'metadata'
+ };
+ /**
+ * All possible `TextTrackMode`s
+ *
+ * @see https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackmode
+ * @typedef TextTrack~Mode
+ * @enum
+ */
+
+ var TextTrackMode = {
+ disabled: 'disabled',
+ hidden: 'hidden',
+ showing: 'showing'
+ };
+
+ /**
+ * A Track class that contains all of the common functionality for {@link AudioTrack},
+ * {@link VideoTrack}, and {@link TextTrack}.
+ *
+ * > Note: This class should not be used directly
+ *
+ * @see {@link https://html.spec.whatwg.org/multipage/embedded-content.html}
+ * @extends EventTarget
+ * @abstract
+ */
+
+ var Track =
+ /*#__PURE__*/
+ function (_EventTarget) {
+ _inheritsLoose(Track, _EventTarget);
+
+ /**
+ * Create an instance of this class.
+ *
+ * @param {Object} [options={}]
+ * Object of option names and values
+ *
+ * @param {string} [options.kind='']
+ * A valid kind for the track type you are creating.
+ *
+ * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
+ * A unique id for this AudioTrack.
+ *
+ * @param {string} [options.label='']
+ * The menu label for this track.
+ *
+ * @param {string} [options.language='']
+ * A valid two character language code.
+ *
+ * @abstract
+ */
+ function Track(options) {
+ var _this;
+
+ if (options === void 0) {
+ options = {};
+ }
+
+ _this = _EventTarget.call(this) || this;
+ var trackProps = {
+ id: options.id || 'vjs_track_' + newGUID(),
+ kind: options.kind || '',
+ label: options.label || '',
+ language: options.language || ''
+ };
+ /**
+ * @memberof Track
+ * @member {string} id
+ * The id of this track. Cannot be changed after creation.
+ * @instance
+ *
+ * @readonly
+ */
+
+ /**
+ * @memberof Track
+ * @member {string} kind
+ * The kind of track that this is. Cannot be changed after creation.
+ * @instance
+ *
+ * @readonly
+ */
+
+ /**
+ * @memberof Track
+ * @member {string} label
+ * The label of this track. Cannot be changed after creation.
+ * @instance
+ *
+ * @readonly
+ */
+
+ /**
+ * @memberof Track
+ * @member {string} language
+ * The two letter language code for this track. Cannot be changed after
+ * creation.
+ * @instance
+ *
+ * @readonly
+ */
+
+ var _loop = function _loop(key) {
+ Object.defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), key, {
+ get: function get() {
+ return trackProps[key];
+ },
+ set: function set() {}
+ });
+ };
+
+ for (var key in trackProps) {
+ _loop(key);
+ }
+
+ return _this;
+ }
+
+ return Track;
+ }(EventTarget);
+
+ /**
+ * @file url.js
+ * @module url
+ */
+ /**
+ * @typedef {Object} url:URLObject
+ *
+ * @property {string} protocol
+ * The protocol of the url that was parsed.
+ *
+ * @property {string} hostname
+ * The hostname of the url that was parsed.
+ *
+ * @property {string} port
+ * The port of the url that was parsed.
+ *
+ * @property {string} pathname
+ * The pathname of the url that was parsed.
+ *
+ * @property {string} search
+ * The search query of the url that was parsed.
+ *
+ * @property {string} hash
+ * The hash of the url that was parsed.
+ *
+ * @property {string} host
+ * The host of the url that was parsed.
+ */
+
+ /**
+ * Resolve and parse the elements of a URL.
+ *
+ * @function
+ * @param {String} url
+ * The url to parse
+ *
+ * @return {url:URLObject}
+ * An object of url details
+ */
+
+ var parseUrl = function parseUrl(url) {
+ var props = ['protocol', 'hostname', 'port', 'pathname', 'search', 'hash', 'host']; // add the url to an anchor and let the browser parse the URL
+
+ var a = document.createElement('a');
+ a.href = url; // IE8 (and 9?) Fix
+ // ie8 doesn't parse the URL correctly until the anchor is actually
+ // added to the body, and an innerHTML is needed to trigger the parsing
+
+ var addToBody = a.host === '' && a.protocol !== 'file:';
+ var div;
+
+ if (addToBody) {
+ div = document.createElement('div');
+ div.innerHTML = "";
+ a = div.firstChild; // prevent the div from affecting layout
+
+ div.setAttribute('style', 'display:none; position:absolute;');
+ document.body.appendChild(div);
+ } // Copy the specific URL properties to a new object
+ // This is also needed for IE8 because the anchor loses its
+ // properties when it's removed from the dom
+
+
+ var details = {};
+
+ for (var i = 0; i < props.length; i++) {
+ details[props[i]] = a[props[i]];
+ } // IE9 adds the port to the host property unlike everyone else. If
+ // a port identifier is added for standard ports, strip it.
+
+
+ if (details.protocol === 'http:') {
+ details.host = details.host.replace(/:80$/, '');
+ }
+
+ if (details.protocol === 'https:') {
+ details.host = details.host.replace(/:443$/, '');
+ }
+
+ if (!details.protocol) {
+ details.protocol = window$1.location.protocol;
+ }
+
+ if (addToBody) {
+ document.body.removeChild(div);
+ }
+
+ return details;
+ };
+ /**
+ * Get absolute version of relative URL. Used to tell Flash the correct URL.
+ *
+ * @function
+ * @param {string} url
+ * URL to make absolute
+ *
+ * @return {string}
+ * Absolute URL
+ *
+ * @see http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue
+ */
+
+ var getAbsoluteURL = function getAbsoluteURL(url) {
+ // Check if absolute URL
+ if (!url.match(/^https?:\/\//)) {
+ // Convert to absolute URL. Flash hosted off-site needs an absolute URL.
+ var div = document.createElement('div');
+ div.innerHTML = "x";
+ url = div.firstChild.href;
+ }
+
+ return url;
+ };
+ /**
+ * Returns the extension of the passed file name. It will return an empty string
+ * if passed an invalid path.
+ *
+ * @function
+ * @param {string} path
+ * The fileName path like '/path/to/file.mp4'
+ *
+ * @return {string}
+ * The extension in lower case or an empty string if no
+ * extension could be found.
+ */
+
+ var getFileExtension = function getFileExtension(path) {
+ if (typeof path === 'string') {
+ var splitPathRe = /^(\/?)([\s\S]*?)((?:\.{1,2}|[^\/]+?)(\.([^\.\/\?]+)))(?:[\/]*|[\?].*)$/i;
+ var pathParts = splitPathRe.exec(path);
+
+ if (pathParts) {
+ return pathParts.pop().toLowerCase();
+ }
+ }
+
+ return '';
+ };
+ /**
+ * Returns whether the url passed is a cross domain request or not.
+ *
+ * @function
+ * @param {string} url
+ * The url to check.
+ *
+ * @return {boolean}
+ * Whether it is a cross domain request or not.
+ */
+
+ var isCrossOrigin = function isCrossOrigin(url) {
+ var winLoc = window$1.location;
+ var urlInfo = parseUrl(url); // IE8 protocol relative urls will return ':' for protocol
+
+ var srcProtocol = urlInfo.protocol === ':' ? winLoc.protocol : urlInfo.protocol; // Check if url is for another domain/origin
+ // IE8 doesn't know location.origin, so we won't rely on it here
+
+ var crossOrigin = srcProtocol + urlInfo.host !== winLoc.protocol + winLoc.host;
+ return crossOrigin;
+ };
+
+ var Url = /*#__PURE__*/Object.freeze({
+ parseUrl: parseUrl,
+ getAbsoluteURL: getAbsoluteURL,
+ getFileExtension: getFileExtension,
+ isCrossOrigin: isCrossOrigin
+ });
+
+ var isFunction_1 = isFunction;
+ var toString$1 = Object.prototype.toString;
+
+ function isFunction(fn) {
+ var string = toString$1.call(fn);
+ return string === '[object Function]' || typeof fn === 'function' && string !== '[object RegExp]' || typeof window !== 'undefined' && ( // IE8 and below
+ fn === window.setTimeout || fn === window.alert || fn === window.confirm || fn === window.prompt);
+ }
+
+ var trim_1 = createCommonjsModule(function (module, exports) {
+ exports = module.exports = trim;
+
+ function trim(str) {
+ return str.replace(/^\s*|\s*$/g, '');
+ }
+
+ exports.left = function (str) {
+ return str.replace(/^\s*/, '');
+ };
+
+ exports.right = function (str) {
+ return str.replace(/\s*$/, '');
+ };
+ });
+ var trim_2 = trim_1.left;
+ var trim_3 = trim_1.right;
+
+ var fnToStr = Function.prototype.toString;
+ var constructorRegex = /^\s*class\b/;
+
+ var isES6ClassFn = function isES6ClassFunction(value) {
+ try {
+ var fnStr = fnToStr.call(value);
+ return constructorRegex.test(fnStr);
+ } catch (e) {
+ return false; // not a function
+ }
+ };
+
+ var tryFunctionObject = function tryFunctionToStr(value) {
+ try {
+ if (isES6ClassFn(value)) {
+ return false;
+ }
+
+ fnToStr.call(value);
+ return true;
+ } catch (e) {
+ return false;
+ }
+ };
+
+ var toStr = Object.prototype.toString;
+ var fnClass = '[object Function]';
+ var genClass = '[object GeneratorFunction]';
+ var hasToStringTag = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
+
+ var isCallable = function isCallable(value) {
+ if (!value) {
+ return false;
+ }
+
+ if (typeof value !== 'function' && typeof value !== 'object') {
+ return false;
+ }
+
+ if (typeof value === 'function' && !value.prototype) {
+ return true;
+ }
+
+ if (hasToStringTag) {
+ return tryFunctionObject(value);
+ }
+
+ if (isES6ClassFn(value)) {
+ return false;
+ }
+
+ var strClass = toStr.call(value);
+ return strClass === fnClass || strClass === genClass;
+ };
+
+ var toStr$1 = Object.prototype.toString;
+ var hasOwnProperty = Object.prototype.hasOwnProperty;
+
+ var forEachArray = function forEachArray(array, iterator, receiver) {
+ for (var i = 0, len = array.length; i < len; i++) {
+ if (hasOwnProperty.call(array, i)) {
+ if (receiver == null) {
+ iterator(array[i], i, array);
+ } else {
+ iterator.call(receiver, array[i], i, array);
+ }
+ }
+ }
+ };
+
+ var forEachString = function forEachString(string, iterator, receiver) {
+ for (var i = 0, len = string.length; i < len; i++) {
+ // no such thing as a sparse string.
+ if (receiver == null) {
+ iterator(string.charAt(i), i, string);
+ } else {
+ iterator.call(receiver, string.charAt(i), i, string);
+ }
+ }
+ };
+
+ var forEachObject = function forEachObject(object, iterator, receiver) {
+ for (var k in object) {
+ if (hasOwnProperty.call(object, k)) {
+ if (receiver == null) {
+ iterator(object[k], k, object);
+ } else {
+ iterator.call(receiver, object[k], k, object);
+ }
+ }
+ }
+ };
+
+ var forEach = function forEach(list, iterator, thisArg) {
+ if (!isCallable(iterator)) {
+ throw new TypeError('iterator must be a function');
+ }
+
+ var receiver;
+
+ if (arguments.length >= 3) {
+ receiver = thisArg;
+ }
+
+ if (toStr$1.call(list) === '[object Array]') {
+ forEachArray(list, iterator, receiver);
+ } else if (typeof list === 'string') {
+ forEachString(list, iterator, receiver);
+ } else {
+ forEachObject(list, iterator, receiver);
+ }
+ };
+
+ var forEach_1 = forEach;
+
+ var isArray = function isArray(arg) {
+ return Object.prototype.toString.call(arg) === '[object Array]';
+ };
+
+ var parseHeaders = function parseHeaders(headers) {
+ if (!headers) return {};
+ var result = {};
+ forEach_1(trim_1(headers).split('\n'), function (row) {
+ var index = row.indexOf(':'),
+ key = trim_1(row.slice(0, index)).toLowerCase(),
+ value = trim_1(row.slice(index + 1));
+
+ if (typeof result[key] === 'undefined') {
+ result[key] = value;
+ } else if (isArray(result[key])) {
+ result[key].push(value);
+ } else {
+ result[key] = [result[key], value];
+ }
+ });
+ return result;
+ };
+
+ var immutable = extend;
+ var hasOwnProperty$1 = Object.prototype.hasOwnProperty;
+
+ function extend() {
+ var target = {};
+
+ for (var i = 0; i < arguments.length; i++) {
+ var source = arguments[i];
+
+ for (var key in source) {
+ if (hasOwnProperty$1.call(source, key)) {
+ target[key] = source[key];
+ }
+ }
+ }
+
+ return target;
+ }
+
+ var xhr = createXHR;
+ createXHR.XMLHttpRequest = window$1.XMLHttpRequest || noop;
+ createXHR.XDomainRequest = "withCredentials" in new createXHR.XMLHttpRequest() ? createXHR.XMLHttpRequest : window$1.XDomainRequest;
+ forEachArray$1(["get", "put", "post", "patch", "head", "delete"], function (method) {
+ createXHR[method === "delete" ? "del" : method] = function (uri, options, callback) {
+ options = initParams(uri, options, callback);
+ options.method = method.toUpperCase();
+ return _createXHR(options);
+ };
+ });
+
+ function forEachArray$1(array, iterator) {
+ for (var i = 0; i < array.length; i++) {
+ iterator(array[i]);
+ }
+ }
+
+ function isEmpty(obj) {
+ for (var i in obj) {
+ if (obj.hasOwnProperty(i)) return false;
+ }
+
+ return true;
+ }
+
+ function initParams(uri, options, callback) {
+ var params = uri;
+
+ if (isFunction_1(options)) {
+ callback = options;
+
+ if (typeof uri === "string") {
+ params = {
+ uri: uri
+ };
+ }
+ } else {
+ params = immutable(options, {
+ uri: uri
+ });
+ }
+
+ params.callback = callback;
+ return params;
+ }
+
+ function createXHR(uri, options, callback) {
+ options = initParams(uri, options, callback);
+ return _createXHR(options);
+ }
+
+ function _createXHR(options) {
+ if (typeof options.callback === "undefined") {
+ throw new Error("callback argument missing");
+ }
+
+ var called = false;
+
+ var callback = function cbOnce(err, response, body) {
+ if (!called) {
+ called = true;
+ options.callback(err, response, body);
+ }
+ };
+
+ function readystatechange() {
+ if (xhr.readyState === 4) {
+ setTimeout(loadFunc, 0);
+ }
+ }
+
+ function getBody() {
+ // Chrome with requestType=blob throws errors arround when even testing access to responseText
+ var body = undefined;
+
+ if (xhr.response) {
+ body = xhr.response;
+ } else {
+ body = xhr.responseText || getXml(xhr);
+ }
+
+ if (isJson) {
+ try {
+ body = JSON.parse(body);
+ } catch (e) {}
+ }
+
+ return body;
+ }
+
+ function errorFunc(evt) {
+ clearTimeout(timeoutTimer);
+
+ if (!(evt instanceof Error)) {
+ evt = new Error("" + (evt || "Unknown XMLHttpRequest Error"));
+ }
+
+ evt.statusCode = 0;
+ return callback(evt, failureResponse);
+ } // will load the data & process the response in a special response object
+
+
+ function loadFunc() {
+ if (aborted) return;
+ var status;
+ clearTimeout(timeoutTimer);
+
+ if (options.useXDR && xhr.status === undefined) {
+ //IE8 CORS GET successful response doesn't have a status field, but body is fine
+ status = 200;
+ } else {
+ status = xhr.status === 1223 ? 204 : xhr.status;
+ }
+
+ var response = failureResponse;
+ var err = null;
+
+ if (status !== 0) {
+ response = {
+ body: getBody(),
+ statusCode: status,
+ method: method,
+ headers: {},
+ url: uri,
+ rawRequest: xhr
+ };
+
+ if (xhr.getAllResponseHeaders) {
+ //remember xhr can in fact be XDR for CORS in IE
+ response.headers = parseHeaders(xhr.getAllResponseHeaders());
+ }
+ } else {
+ err = new Error("Internal XMLHttpRequest Error");
+ }
+
+ return callback(err, response, response.body);
+ }
+
+ var xhr = options.xhr || null;
+
+ if (!xhr) {
+ if (options.cors || options.useXDR) {
+ xhr = new createXHR.XDomainRequest();
+ } else {
+ xhr = new createXHR.XMLHttpRequest();
+ }
+ }
+
+ var key;
+ var aborted;
+ var uri = xhr.url = options.uri || options.url;
+ var method = xhr.method = options.method || "GET";
+ var body = options.body || options.data;
+ var headers = xhr.headers = options.headers || {};
+ var sync = !!options.sync;
+ var isJson = false;
+ var timeoutTimer;
+ var failureResponse = {
+ body: undefined,
+ headers: {},
+ statusCode: 0,
+ method: method,
+ url: uri,
+ rawRequest: xhr
+ };
+
+ if ("json" in options && options.json !== false) {
+ isJson = true;
+ headers["accept"] || headers["Accept"] || (headers["Accept"] = "application/json"); //Don't override existing accept header declared by user
+
+ if (method !== "GET" && method !== "HEAD") {
+ headers["content-type"] || headers["Content-Type"] || (headers["Content-Type"] = "application/json"); //Don't override existing accept header declared by user
+
+ body = JSON.stringify(options.json === true ? body : options.json);
+ }
+ }
+
+ xhr.onreadystatechange = readystatechange;
+ xhr.onload = loadFunc;
+ xhr.onerror = errorFunc; // IE9 must have onprogress be set to a unique function.
+
+ xhr.onprogress = function () {// IE must die
+ };
+
+ xhr.onabort = function () {
+ aborted = true;
+ };
+
+ xhr.ontimeout = errorFunc;
+ xhr.open(method, uri, !sync, options.username, options.password); //has to be after open
+
+ if (!sync) {
+ xhr.withCredentials = !!options.withCredentials;
+ } // Cannot set timeout with sync request
+ // not setting timeout on the xhr object, because of old webkits etc. not handling that correctly
+ // both npm's request and jquery 1.x use this kind of timeout, so this is being consistent
+
+
+ if (!sync && options.timeout > 0) {
+ timeoutTimer = setTimeout(function () {
+ if (aborted) return;
+ aborted = true; //IE9 may still call readystatechange
+
+ xhr.abort("timeout");
+ var e = new Error("XMLHttpRequest timeout");
+ e.code = "ETIMEDOUT";
+ errorFunc(e);
+ }, options.timeout);
+ }
+
+ if (xhr.setRequestHeader) {
+ for (key in headers) {
+ if (headers.hasOwnProperty(key)) {
+ xhr.setRequestHeader(key, headers[key]);
+ }
+ }
+ } else if (options.headers && !isEmpty(options.headers)) {
+ throw new Error("Headers cannot be set on an XDomainRequest object");
+ }
+
+ if ("responseType" in options) {
+ xhr.responseType = options.responseType;
+ }
+
+ if ("beforeSend" in options && typeof options.beforeSend === "function") {
+ options.beforeSend(xhr);
+ } // Microsoft Edge browser sends "undefined" when send is called with undefined value.
+ // XMLHttpRequest spec says to pass null as body to indicate no body
+ // See https://github.com/naugtur/xhr/issues/100.
+
+
+ xhr.send(body || null);
+ return xhr;
+ }
+
+ function getXml(xhr) {
+ if (xhr.responseType === "document") {
+ return xhr.responseXML;
+ }
+
+ var firefoxBugTakenEffect = xhr.responseXML && xhr.responseXML.documentElement.nodeName === "parsererror";
+
+ if (xhr.responseType === "" && !firefoxBugTakenEffect) {
+ return xhr.responseXML;
+ }
+
+ return null;
+ }
+
+ function noop() {}
+
+ /**
+ * Takes a webvtt file contents and parses it into cues
+ *
+ * @param {string} srcContent
+ * webVTT file contents
+ *
+ * @param {TextTrack} track
+ * TextTrack to add cues to. Cues come from the srcContent.
+ *
+ * @private
+ */
+
+ var parseCues = function parseCues(srcContent, track) {
+ var parser = new window$1.WebVTT.Parser(window$1, window$1.vttjs, window$1.WebVTT.StringDecoder());
+ var errors = [];
+
+ parser.oncue = function (cue) {
+ track.addCue(cue);
+ };
+
+ parser.onparsingerror = function (error) {
+ errors.push(error);
+ };
+
+ parser.onflush = function () {
+ track.trigger({
+ type: 'loadeddata',
+ target: track
+ });
+ };
+
+ parser.parse(srcContent);
+
+ if (errors.length > 0) {
+ if (window$1.console && window$1.console.groupCollapsed) {
+ window$1.console.groupCollapsed("Text Track parsing errors for " + track.src);
+ }
+
+ errors.forEach(function (error) {
+ return log.error(error);
+ });
+
+ if (window$1.console && window$1.console.groupEnd) {
+ window$1.console.groupEnd();
+ }
+ }
+
+ parser.flush();
+ };
+ /**
+ * Load a `TextTrack` from a specified url.
+ *
+ * @param {string} src
+ * Url to load track from.
+ *
+ * @param {TextTrack} track
+ * Track to add cues to. Comes from the content at the end of `url`.
+ *
+ * @private
+ */
+
+
+ var loadTrack = function loadTrack(src, track) {
+ var opts = {
+ uri: src
+ };
+ var crossOrigin = isCrossOrigin(src);
+
+ if (crossOrigin) {
+ opts.cors = crossOrigin;
+ }
+
+ xhr(opts, bind(this, function (err, response, responseBody) {
+ if (err) {
+ return log.error(err, response);
+ }
+
+ track.loaded_ = true; // Make sure that vttjs has loaded, otherwise, wait till it finished loading
+ // NOTE: this is only used for the alt/video.novtt.js build
+
+ if (typeof window$1.WebVTT !== 'function') {
+ if (track.tech_) {
+ // to prevent use before define eslint error, we define loadHandler
+ // as a let here
+ var loadHandler;
+
+ var errorHandler = function errorHandler() {
+ log.error("vttjs failed to load, stopping trying to process " + track.src);
+ track.tech_.off('vttjsloaded', loadHandler);
+ };
+
+ loadHandler = function loadHandler() {
+ track.tech_.off('vttjserror', errorHandler);
+ return parseCues(responseBody, track);
+ };
+
+ track.tech_.one('vttjsloaded', loadHandler);
+ track.tech_.one('vttjserror', errorHandler);
+ }
+ } else {
+ parseCues(responseBody, track);
+ }
+ }));
+ };
+ /**
+ * A representation of a single `TextTrack`.
+ *
+ * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrack}
+ * @extends Track
+ */
+
+
+ var TextTrack =
+ /*#__PURE__*/
+ function (_Track) {
+ _inheritsLoose(TextTrack, _Track);
+
+ /**
+ * Create an instance of this class.
+ *
+ * @param {Object} options={}
+ * Object of option names and values
+ *
+ * @param {Tech} options.tech
+ * A reference to the tech that owns this TextTrack.
+ *
+ * @param {TextTrack~Kind} [options.kind='subtitles']
+ * A valid text track kind.
+ *
+ * @param {TextTrack~Mode} [options.mode='disabled']
+ * A valid text track mode.
+ *
+ * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
+ * A unique id for this TextTrack.
+ *
+ * @param {string} [options.label='']
+ * The menu label for this track.
+ *
+ * @param {string} [options.language='']
+ * A valid two character language code.
+ *
+ * @param {string} [options.srclang='']
+ * A valid two character language code. An alternative, but deprioritized
+ * version of `options.language`
+ *
+ * @param {string} [options.src]
+ * A url to TextTrack cues.
+ *
+ * @param {boolean} [options.default]
+ * If this track should default to on or off.
+ */
+ function TextTrack(options) {
+ var _this;
+
+ if (options === void 0) {
+ options = {};
+ }
+
+ if (!options.tech) {
+ throw new Error('A tech was not provided.');
+ }
+
+ var settings = mergeOptions(options, {
+ kind: TextTrackKind[options.kind] || 'subtitles',
+ language: options.language || options.srclang || ''
+ });
+ var mode = TextTrackMode[settings.mode] || 'disabled';
+ var default_ = settings.default;
+
+ if (settings.kind === 'metadata' || settings.kind === 'chapters') {
+ mode = 'hidden';
+ }
+
+ _this = _Track.call(this, settings) || this;
+ _this.tech_ = settings.tech;
+ _this.cues_ = [];
+ _this.activeCues_ = [];
+ var cues = new TextTrackCueList(_this.cues_);
+ var activeCues = new TextTrackCueList(_this.activeCues_);
+ var changed = false;
+ var timeupdateHandler = bind(_assertThisInitialized(_assertThisInitialized(_this)), function () {
+ // Accessing this.activeCues for the side-effects of updating itself
+ // due to its nature as a getter function. Do not remove or cues will
+ // stop updating!
+ // Use the setter to prevent deletion from uglify (pure_getters rule)
+ this.activeCues = this.activeCues;
+
+ if (changed) {
+ this.trigger('cuechange');
+ changed = false;
+ }
+ });
+
+ if (mode !== 'disabled') {
+ _this.tech_.ready(function () {
+ _this.tech_.on('timeupdate', timeupdateHandler);
+ }, true);
+ }
+
+ Object.defineProperties(_assertThisInitialized(_assertThisInitialized(_this)), {
+ /**
+ * @memberof TextTrack
+ * @member {boolean} default
+ * If this track was set to be on or off by default. Cannot be changed after
+ * creation.
+ * @instance
+ *
+ * @readonly
+ */
+ default: {
+ get: function get() {
+ return default_;
+ },
+ set: function set() {}
+ },
+
+ /**
+ * @memberof TextTrack
+ * @member {string} mode
+ * Set the mode of this TextTrack to a valid {@link TextTrack~Mode}. Will
+ * not be set if setting to an invalid mode.
+ * @instance
+ *
+ * @fires TextTrack#modechange
+ */
+ mode: {
+ get: function get() {
+ return mode;
+ },
+ set: function set(newMode) {
+ var _this2 = this;
+
+ if (!TextTrackMode[newMode]) {
+ return;
+ }
+
+ mode = newMode;
+
+ if (mode !== 'disabled') {
+ this.tech_.ready(function () {
+ _this2.tech_.on('timeupdate', timeupdateHandler);
+ }, true);
+ } else {
+ this.tech_.off('timeupdate', timeupdateHandler);
+ }
+ /**
+ * An event that fires when mode changes on this track. This allows
+ * the TextTrackList that holds this track to act accordingly.
+ *
+ * > Note: This is not part of the spec!
+ *
+ * @event TextTrack#modechange
+ * @type {EventTarget~Event}
+ */
+
+
+ this.trigger('modechange');
+ }
+ },
+
+ /**
+ * @memberof TextTrack
+ * @member {TextTrackCueList} cues
+ * The text track cue list for this TextTrack.
+ * @instance
+ */
+ cues: {
+ get: function get() {
+ if (!this.loaded_) {
+ return null;
+ }
+
+ return cues;
+ },
+ set: function set() {}
+ },
+
+ /**
+ * @memberof TextTrack
+ * @member {TextTrackCueList} activeCues
+ * The list text track cues that are currently active for this TextTrack.
+ * @instance
+ */
+ activeCues: {
+ get: function get() {
+ if (!this.loaded_) {
+ return null;
+ } // nothing to do
+
+
+ if (this.cues.length === 0) {
+ return activeCues;
+ }
+
+ var ct = this.tech_.currentTime();
+ var active = [];
+
+ for (var i = 0, l = this.cues.length; i < l; i++) {
+ var cue = this.cues[i];
+
+ if (cue.startTime <= ct && cue.endTime >= ct) {
+ active.push(cue);
+ } else if (cue.startTime === cue.endTime && cue.startTime <= ct && cue.startTime + 0.5 >= ct) {
+ active.push(cue);
+ }
+ }
+
+ changed = false;
+
+ if (active.length !== this.activeCues_.length) {
+ changed = true;
+ } else {
+ for (var _i = 0; _i < active.length; _i++) {
+ if (this.activeCues_.indexOf(active[_i]) === -1) {
+ changed = true;
+ }
+ }
+ }
+
+ this.activeCues_ = active;
+ activeCues.setCues_(this.activeCues_);
+ return activeCues;
+ },
+ // /!\ Keep this setter empty (see the timeupdate handler above)
+ set: function set() {}
+ }
+ });
+
+ if (settings.src) {
+ _this.src = settings.src;
+ loadTrack(settings.src, _assertThisInitialized(_assertThisInitialized(_this)));
+ } else {
+ _this.loaded_ = true;
+ }
+
+ return _this;
+ }
+ /**
+ * Add a cue to the internal list of cues.
+ *
+ * @param {TextTrack~Cue} cue
+ * The cue to add to our internal list
+ */
+
+
+ var _proto = TextTrack.prototype;
+
+ _proto.addCue = function addCue(originalCue) {
+ var cue = originalCue;
+
+ if (window$1.vttjs && !(originalCue instanceof window$1.vttjs.VTTCue)) {
+ cue = new window$1.vttjs.VTTCue(originalCue.startTime, originalCue.endTime, originalCue.text);
+
+ for (var prop in originalCue) {
+ if (!(prop in cue)) {
+ cue[prop] = originalCue[prop];
+ }
+ } // make sure that `id` is copied over
+
+
+ cue.id = originalCue.id;
+ cue.originalCue_ = originalCue;
+ }
+
+ var tracks = this.tech_.textTracks();
+
+ for (var i = 0; i < tracks.length; i++) {
+ if (tracks[i] !== this) {
+ tracks[i].removeCue(cue);
+ }
+ }
+
+ this.cues_.push(cue);
+ this.cues.setCues_(this.cues_);
+ }
+ /**
+ * Remove a cue from our internal list
+ *
+ * @param {TextTrack~Cue} removeCue
+ * The cue to remove from our internal list
+ */
+ ;
+
+ _proto.removeCue = function removeCue(_removeCue) {
+ var i = this.cues_.length;
+
+ while (i--) {
+ var cue = this.cues_[i];
+
+ if (cue === _removeCue || cue.originalCue_ && cue.originalCue_ === _removeCue) {
+ this.cues_.splice(i, 1);
+ this.cues.setCues_(this.cues_);
+ break;
+ }
+ }
+ };
+
+ return TextTrack;
+ }(Track);
+ /**
+ * cuechange - One or more cues in the track have become active or stopped being active.
+ */
+
+
+ TextTrack.prototype.allowedEvents_ = {
+ cuechange: 'cuechange'
+ };
+
+ /**
+ * A representation of a single `AudioTrack`. If it is part of an {@link AudioTrackList}
+ * only one `AudioTrack` in the list will be enabled at a time.
+ *
+ * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotrack}
+ * @extends Track
+ */
+
+ var AudioTrack =
+ /*#__PURE__*/
+ function (_Track) {
+ _inheritsLoose(AudioTrack, _Track);
+
+ /**
+ * Create an instance of this class.
+ *
+ * @param {Object} [options={}]
+ * Object of option names and values
+ *
+ * @param {AudioTrack~Kind} [options.kind='']
+ * A valid audio track kind
+ *
+ * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
+ * A unique id for this AudioTrack.
+ *
+ * @param {string} [options.label='']
+ * The menu label for this track.
+ *
+ * @param {string} [options.language='']
+ * A valid two character language code.
+ *
+ * @param {boolean} [options.enabled]
+ * If this track is the one that is currently playing. If this track is part of
+ * an {@link AudioTrackList}, only one {@link AudioTrack} will be enabled.
+ */
+ function AudioTrack(options) {
+ var _this;
+
+ if (options === void 0) {
+ options = {};
+ }
+
+ var settings = mergeOptions(options, {
+ kind: AudioTrackKind[options.kind] || ''
+ });
+ _this = _Track.call(this, settings) || this;
+ var enabled = false;
+ /**
+ * @memberof AudioTrack
+ * @member {boolean} enabled
+ * If this `AudioTrack` is enabled or not. When setting this will
+ * fire {@link AudioTrack#enabledchange} if the state of enabled is changed.
+ * @instance
+ *
+ * @fires VideoTrack#selectedchange
+ */
+
+ Object.defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), 'enabled', {
+ get: function get() {
+ return enabled;
+ },
+ set: function set(newEnabled) {
+ // an invalid or unchanged value
+ if (typeof newEnabled !== 'boolean' || newEnabled === enabled) {
+ return;
+ }
+
+ enabled = newEnabled;
+ /**
+ * An event that fires when enabled changes on this track. This allows
+ * the AudioTrackList that holds this track to act accordingly.
+ *
+ * > Note: This is not part of the spec! Native tracks will do
+ * this internally without an event.
+ *
+ * @event AudioTrack#enabledchange
+ * @type {EventTarget~Event}
+ */
+
+ this.trigger('enabledchange');
+ }
+ }); // if the user sets this track to selected then
+ // set selected to that true value otherwise
+ // we keep it false
+
+ if (settings.enabled) {
+ _this.enabled = settings.enabled;
+ }
+
+ _this.loaded_ = true;
+ return _this;
+ }
+
+ return AudioTrack;
+ }(Track);
+
+ /**
+ * A representation of a single `VideoTrack`.
+ *
+ * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotrack}
+ * @extends Track
+ */
+
+ var VideoTrack =
+ /*#__PURE__*/
+ function (_Track) {
+ _inheritsLoose(VideoTrack, _Track);
+
+ /**
+ * Create an instance of this class.
+ *
+ * @param {Object} [options={}]
+ * Object of option names and values
+ *
+ * @param {string} [options.kind='']
+ * A valid {@link VideoTrack~Kind}
+ *
+ * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
+ * A unique id for this AudioTrack.
+ *
+ * @param {string} [options.label='']
+ * The menu label for this track.
+ *
+ * @param {string} [options.language='']
+ * A valid two character language code.
+ *
+ * @param {boolean} [options.selected]
+ * If this track is the one that is currently playing.
+ */
+ function VideoTrack(options) {
+ var _this;
+
+ if (options === void 0) {
+ options = {};
+ }
+
+ var settings = mergeOptions(options, {
+ kind: VideoTrackKind[options.kind] || ''
+ });
+ _this = _Track.call(this, settings) || this;
+ var selected = false;
+ /**
+ * @memberof VideoTrack
+ * @member {boolean} selected
+ * If this `VideoTrack` is selected or not. When setting this will
+ * fire {@link VideoTrack#selectedchange} if the state of selected changed.
+ * @instance
+ *
+ * @fires VideoTrack#selectedchange
+ */
+
+ Object.defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), 'selected', {
+ get: function get() {
+ return selected;
+ },
+ set: function set(newSelected) {
+ // an invalid or unchanged value
+ if (typeof newSelected !== 'boolean' || newSelected === selected) {
+ return;
+ }
+
+ selected = newSelected;
+ /**
+ * An event that fires when selected changes on this track. This allows
+ * the VideoTrackList that holds this track to act accordingly.
+ *
+ * > Note: This is not part of the spec! Native tracks will do
+ * this internally without an event.
+ *
+ * @event VideoTrack#selectedchange
+ * @type {EventTarget~Event}
+ */
+
+ this.trigger('selectedchange');
+ }
+ }); // if the user sets this track to selected then
+ // set selected to that true value otherwise
+ // we keep it false
+
+ if (settings.selected) {
+ _this.selected = settings.selected;
+ }
+
+ return _this;
+ }
+
+ return VideoTrack;
+ }(Track);
+
+ /**
+ * @memberof HTMLTrackElement
+ * @typedef {HTMLTrackElement~ReadyState}
+ * @enum {number}
+ */
+
+ var NONE = 0;
+ var LOADING = 1;
+ var LOADED = 2;
+ var ERROR = 3;
+ /**
+ * A single track represented in the DOM.
+ *
+ * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#htmltrackelement}
+ * @extends EventTarget
+ */
+
+ var HTMLTrackElement =
+ /*#__PURE__*/
+ function (_EventTarget) {
+ _inheritsLoose(HTMLTrackElement, _EventTarget);
+
+ /**
+ * Create an instance of this class.
+ *
+ * @param {Object} options={}
+ * Object of option names and values
+ *
+ * @param {Tech} options.tech
+ * A reference to the tech that owns this HTMLTrackElement.
+ *
+ * @param {TextTrack~Kind} [options.kind='subtitles']
+ * A valid text track kind.
+ *
+ * @param {TextTrack~Mode} [options.mode='disabled']
+ * A valid text track mode.
+ *
+ * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
+ * A unique id for this TextTrack.
+ *
+ * @param {string} [options.label='']
+ * The menu label for this track.
+ *
+ * @param {string} [options.language='']
+ * A valid two character language code.
+ *
+ * @param {string} [options.srclang='']
+ * A valid two character language code. An alternative, but deprioritized
+ * vesion of `options.language`
+ *
+ * @param {string} [options.src]
+ * A url to TextTrack cues.
+ *
+ * @param {boolean} [options.default]
+ * If this track should default to on or off.
+ */
+ function HTMLTrackElement(options) {
+ var _this;
+
+ if (options === void 0) {
+ options = {};
+ }
+
+ _this = _EventTarget.call(this) || this;
+ var readyState;
+ var track = new TextTrack(options);
+ _this.kind = track.kind;
+ _this.src = track.src;
+ _this.srclang = track.language;
+ _this.label = track.label;
+ _this.default = track.default;
+ Object.defineProperties(_assertThisInitialized(_assertThisInitialized(_this)), {
+ /**
+ * @memberof HTMLTrackElement
+ * @member {HTMLTrackElement~ReadyState} readyState
+ * The current ready state of the track element.
+ * @instance
+ */
+ readyState: {
+ get: function get() {
+ return readyState;
+ }
+ },
+
+ /**
+ * @memberof HTMLTrackElement
+ * @member {TextTrack} track
+ * The underlying TextTrack object.
+ * @instance
+ *
+ */
+ track: {
+ get: function get() {
+ return track;
+ }
+ }
+ });
+ readyState = NONE;
+ /**
+ * @listens TextTrack#loadeddata
+ * @fires HTMLTrackElement#load
+ */
+
+ track.addEventListener('loadeddata', function () {
+ readyState = LOADED;
+
+ _this.trigger({
+ type: 'load',
+ target: _assertThisInitialized(_assertThisInitialized(_this))
+ });
+ });
+ return _this;
+ }
+
+ return HTMLTrackElement;
+ }(EventTarget);
+
+ HTMLTrackElement.prototype.allowedEvents_ = {
+ load: 'load'
+ };
+ HTMLTrackElement.NONE = NONE;
+ HTMLTrackElement.LOADING = LOADING;
+ HTMLTrackElement.LOADED = LOADED;
+ HTMLTrackElement.ERROR = ERROR;
+
+ /*
+ * This file contains all track properties that are used in
+ * player.js, tech.js, html5.js and possibly other techs in the future.
+ */
+
+ var NORMAL = {
+ audio: {
+ ListClass: AudioTrackList,
+ TrackClass: AudioTrack,
+ capitalName: 'Audio'
+ },
+ video: {
+ ListClass: VideoTrackList,
+ TrackClass: VideoTrack,
+ capitalName: 'Video'
+ },
+ text: {
+ ListClass: TextTrackList,
+ TrackClass: TextTrack,
+ capitalName: 'Text'
+ }
+ };
+ Object.keys(NORMAL).forEach(function (type) {
+ NORMAL[type].getterName = type + "Tracks";
+ NORMAL[type].privateName = type + "Tracks_";
+ });
+ var REMOTE = {
+ remoteText: {
+ ListClass: TextTrackList,
+ TrackClass: TextTrack,
+ capitalName: 'RemoteText',
+ getterName: 'remoteTextTracks',
+ privateName: 'remoteTextTracks_'
+ },
+ remoteTextEl: {
+ ListClass: HtmlTrackElementList,
+ TrackClass: HTMLTrackElement,
+ capitalName: 'RemoteTextTrackEls',
+ getterName: 'remoteTextTrackEls',
+ privateName: 'remoteTextTrackEls_'
+ }
+ };
+ var ALL = mergeOptions(NORMAL, REMOTE);
+ REMOTE.names = Object.keys(REMOTE);
+ NORMAL.names = Object.keys(NORMAL);
+ ALL.names = [].concat(REMOTE.names).concat(NORMAL.names);
+
+ /**
+ * Copyright 2013 vtt.js Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
+ var _objCreate = Object.create || function () {
+ function F() {}
+
+ return function (o) {
+ if (arguments.length !== 1) {
+ throw new Error('Object.create shim only accepts one parameter.');
+ }
+
+ F.prototype = o;
+ return new F();
+ };
+ }(); // Creates a new ParserError object from an errorData object. The errorData
+ // object should have default code and message properties. The default message
+ // property can be overriden by passing in a message parameter.
+ // See ParsingError.Errors below for acceptable errors.
+
+
+ function ParsingError(errorData, message) {
+ this.name = "ParsingError";
+ this.code = errorData.code;
+ this.message = message || errorData.message;
+ }
+
+ ParsingError.prototype = _objCreate(Error.prototype);
+ ParsingError.prototype.constructor = ParsingError; // ParsingError metadata for acceptable ParsingErrors.
+
+ ParsingError.Errors = {
+ BadSignature: {
+ code: 0,
+ message: "Malformed WebVTT signature."
+ },
+ BadTimeStamp: {
+ code: 1,
+ message: "Malformed time stamp."
+ }
+ }; // Try to parse input as a time stamp.
+
+ function parseTimeStamp(input) {
+ function computeSeconds(h, m, s, f) {
+ return (h | 0) * 3600 + (m | 0) * 60 + (s | 0) + (f | 0) / 1000;
+ }
+
+ var m = input.match(/^(\d+):(\d{2})(:\d{2})?\.(\d{3})/);
+
+ if (!m) {
+ return null;
+ }
+
+ if (m[3]) {
+ // Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds]
+ return computeSeconds(m[1], m[2], m[3].replace(":", ""), m[4]);
+ } else if (m[1] > 59) {
+ // Timestamp takes the form of [hours]:[minutes].[milliseconds]
+ // First position is hours as it's over 59.
+ return computeSeconds(m[1], m[2], 0, m[4]);
+ } else {
+ // Timestamp takes the form of [minutes]:[seconds].[milliseconds]
+ return computeSeconds(0, m[1], m[2], m[4]);
+ }
+ } // A settings object holds key/value pairs and will ignore anything but the first
+ // assignment to a specific key.
+
+
+ function Settings() {
+ this.values = _objCreate(null);
+ }
+
+ Settings.prototype = {
+ // Only accept the first assignment to any key.
+ set: function set(k, v) {
+ if (!this.get(k) && v !== "") {
+ this.values[k] = v;
+ }
+ },
+ // Return the value for a key, or a default value.
+ // If 'defaultKey' is passed then 'dflt' is assumed to be an object with
+ // a number of possible default values as properties where 'defaultKey' is
+ // the key of the property that will be chosen; otherwise it's assumed to be
+ // a single value.
+ get: function get(k, dflt, defaultKey) {
+ if (defaultKey) {
+ return this.has(k) ? this.values[k] : dflt[defaultKey];
+ }
+
+ return this.has(k) ? this.values[k] : dflt;
+ },
+ // Check whether we have a value for a key.
+ has: function has(k) {
+ return k in this.values;
+ },
+ // Accept a setting if its one of the given alternatives.
+ alt: function alt(k, v, a) {
+ for (var n = 0; n < a.length; ++n) {
+ if (v === a[n]) {
+ this.set(k, v);
+ break;
+ }
+ }
+ },
+ // Accept a setting if its a valid (signed) integer.
+ integer: function integer(k, v) {
+ if (/^-?\d+$/.test(v)) {
+ // integer
+ this.set(k, parseInt(v, 10));
+ }
+ },
+ // Accept a setting if its a valid percentage.
+ percent: function percent(k, v) {
+ var m;
+
+ if (m = v.match(/^([\d]{1,3})(\.[\d]*)?%$/)) {
+ v = parseFloat(v);
+
+ if (v >= 0 && v <= 100) {
+ this.set(k, v);
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }; // Helper function to parse input into groups separated by 'groupDelim', and
+ // interprete each group as a key/value pair separated by 'keyValueDelim'.
+
+ function parseOptions(input, callback, keyValueDelim, groupDelim) {
+ var groups = groupDelim ? input.split(groupDelim) : [input];
+
+ for (var i in groups) {
+ if (typeof groups[i] !== "string") {
+ continue;
+ }
+
+ var kv = groups[i].split(keyValueDelim);
+
+ if (kv.length !== 2) {
+ continue;
+ }
+
+ var k = kv[0];
+ var v = kv[1];
+ callback(k, v);
+ }
+ }
+
+ function parseCue(input, cue, regionList) {
+ // Remember the original input if we need to throw an error.
+ var oInput = input; // 4.1 WebVTT timestamp
+
+ function consumeTimeStamp() {
+ var ts = parseTimeStamp(input);
+
+ if (ts === null) {
+ throw new ParsingError(ParsingError.Errors.BadTimeStamp, "Malformed timestamp: " + oInput);
+ } // Remove time stamp from input.
+
+
+ input = input.replace(/^[^\sa-zA-Z-]+/, "");
+ return ts;
+ } // 4.4.2 WebVTT cue settings
+
+
+ function consumeCueSettings(input, cue) {
+ var settings = new Settings();
+ parseOptions(input, function (k, v) {
+ switch (k) {
+ case "region":
+ // Find the last region we parsed with the same region id.
+ for (var i = regionList.length - 1; i >= 0; i--) {
+ if (regionList[i].id === v) {
+ settings.set(k, regionList[i].region);
+ break;
+ }
+ }
+
+ break;
+
+ case "vertical":
+ settings.alt(k, v, ["rl", "lr"]);
+ break;
+
+ case "line":
+ var vals = v.split(","),
+ vals0 = vals[0];
+ settings.integer(k, vals0);
+ settings.percent(k, vals0) ? settings.set("snapToLines", false) : null;
+ settings.alt(k, vals0, ["auto"]);
+
+ if (vals.length === 2) {
+ settings.alt("lineAlign", vals[1], ["start", "middle", "end"]);
+ }
+
+ break;
+
+ case "position":
+ vals = v.split(",");
+ settings.percent(k, vals[0]);
+
+ if (vals.length === 2) {
+ settings.alt("positionAlign", vals[1], ["start", "middle", "end"]);
+ }
+
+ break;
+
+ case "size":
+ settings.percent(k, v);
+ break;
+
+ case "align":
+ settings.alt(k, v, ["start", "middle", "end", "left", "right"]);
+ break;
+ }
+ }, /:/, /\s/); // Apply default values for any missing fields.
+
+ cue.region = settings.get("region", null);
+ cue.vertical = settings.get("vertical", "");
+ cue.line = settings.get("line", "auto");
+ cue.lineAlign = settings.get("lineAlign", "start");
+ cue.snapToLines = settings.get("snapToLines", true);
+ cue.size = settings.get("size", 100);
+ cue.align = settings.get("align", "middle");
+ cue.position = settings.get("position", {
+ start: 0,
+ left: 0,
+ middle: 50,
+ end: 100,
+ right: 100
+ }, cue.align);
+ cue.positionAlign = settings.get("positionAlign", {
+ start: "start",
+ left: "start",
+ middle: "middle",
+ end: "end",
+ right: "end"
+ }, cue.align);
+ }
+
+ function skipWhitespace() {
+ input = input.replace(/^\s+/, "");
+ } // 4.1 WebVTT cue timings.
+
+
+ skipWhitespace();
+ cue.startTime = consumeTimeStamp(); // (1) collect cue start time
+
+ skipWhitespace();
+
+ if (input.substr(0, 3) !== "-->") {
+ // (3) next characters must match "-->"
+ throw new ParsingError(ParsingError.Errors.BadTimeStamp, "Malformed time stamp (time stamps must be separated by '-->'): " + oInput);
+ }
+
+ input = input.substr(3);
+ skipWhitespace();
+ cue.endTime = consumeTimeStamp(); // (5) collect cue end time
+ // 4.1 WebVTT cue settings list.
+
+ skipWhitespace();
+ consumeCueSettings(input, cue);
+ }
+
+ var ESCAPE = {
+ "&": "&",
+ "<": "<",
+ ">": ">",
+ "": "\u200E",
+ "": "\u200F",
+ " ": "\xA0"
+ };
+ var TAG_NAME = {
+ c: "span",
+ i: "i",
+ b: "b",
+ u: "u",
+ ruby: "ruby",
+ rt: "rt",
+ v: "span",
+ lang: "span"
+ };
+ var TAG_ANNOTATION = {
+ v: "title",
+ lang: "lang"
+ };
+ var NEEDS_PARENT = {
+ rt: "ruby"
+ }; // Parse content into a document fragment.
+
+ function parseContent(window, input) {
+ function nextToken() {
+ // Check for end-of-string.
+ if (!input) {
+ return null;
+ } // Consume 'n' characters from the input.
+
+
+ function consume(result) {
+ input = input.substr(result.length);
+ return result;
+ }
+
+ var m = input.match(/^([^<]*)(<[^>]*>?)?/); // If there is some text before the next tag, return it, otherwise return
+ // the tag.
+
+ return consume(m[1] ? m[1] : m[2]);
+ } // Unescape a string 's'.
+
+
+ function unescape1(e) {
+ return ESCAPE[e];
+ }
+
+ function unescape(s) {
+ while (m = s.match(/&(amp|lt|gt|lrm|rlm|nbsp);/)) {
+ s = s.replace(m[0], unescape1);
+ }
+
+ return s;
+ }
+
+ function shouldAdd(current, element) {
+ return !NEEDS_PARENT[element.localName] || NEEDS_PARENT[element.localName] === current.localName;
+ } // Create an element for this tag.
+
+
+ function createElement(type, annotation) {
+ var tagName = TAG_NAME[type];
+
+ if (!tagName) {
+ return null;
+ }
+
+ var element = window.document.createElement(tagName);
+ element.localName = tagName;
+ var name = TAG_ANNOTATION[type];
+
+ if (name && annotation) {
+ element[name] = annotation.trim();
+ }
+
+ return element;
+ }
+
+ var rootDiv = window.document.createElement("div"),
+ current = rootDiv,
+ t,
+ tagStack = [];
+
+ while ((t = nextToken()) !== null) {
+ if (t[0] === '<') {
+ if (t[1] === "/") {
+ // If the closing tag matches, move back up to the parent node.
+ if (tagStack.length && tagStack[tagStack.length - 1] === t.substr(2).replace(">", "")) {
+ tagStack.pop();
+ current = current.parentNode;
+ } // Otherwise just ignore the end tag.
+
+
+ continue;
+ }
+
+ var ts = parseTimeStamp(t.substr(1, t.length - 2));
+ var node;
+
+ if (ts) {
+ // Timestamps are lead nodes as well.
+ node = window.document.createProcessingInstruction("timestamp", ts);
+ current.appendChild(node);
+ continue;
+ }
+
+ var m = t.match(/^<([^.\s/0-9>]+)(\.[^\s\\>]+)?([^>\\]+)?(\\?)>?$/); // If we can't parse the tag, skip to the next tag.
+
+ if (!m) {
+ continue;
+ } // Try to construct an element, and ignore the tag if we couldn't.
+
+
+ node = createElement(m[1], m[3]);
+
+ if (!node) {
+ continue;
+ } // Determine if the tag should be added based on the context of where it
+ // is placed in the cuetext.
+
+
+ if (!shouldAdd(current, node)) {
+ continue;
+ } // Set the class list (as a list of classes, separated by space).
+
+
+ if (m[2]) {
+ node.className = m[2].substr(1).replace('.', ' ');
+ } // Append the node to the current node, and enter the scope of the new
+ // node.
+
+
+ tagStack.push(m[1]);
+ current.appendChild(node);
+ current = node;
+ continue;
+ } // Text nodes are leaf nodes.
+
+
+ current.appendChild(window.document.createTextNode(unescape(t)));
+ }
+
+ return rootDiv;
+ } // This is a list of all the Unicode characters that have a strong
+ // right-to-left category. What this means is that these characters are
+ // written right-to-left for sure. It was generated by pulling all the strong
+ // right-to-left characters out of the Unicode data table. That table can
+ // found at: http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
+
+
+ var strongRTLRanges = [[0x5be, 0x5be], [0x5c0, 0x5c0], [0x5c3, 0x5c3], [0x5c6, 0x5c6], [0x5d0, 0x5ea], [0x5f0, 0x5f4], [0x608, 0x608], [0x60b, 0x60b], [0x60d, 0x60d], [0x61b, 0x61b], [0x61e, 0x64a], [0x66d, 0x66f], [0x671, 0x6d5], [0x6e5, 0x6e6], [0x6ee, 0x6ef], [0x6fa, 0x70d], [0x70f, 0x710], [0x712, 0x72f], [0x74d, 0x7a5], [0x7b1, 0x7b1], [0x7c0, 0x7ea], [0x7f4, 0x7f5], [0x7fa, 0x7fa], [0x800, 0x815], [0x81a, 0x81a], [0x824, 0x824], [0x828, 0x828], [0x830, 0x83e], [0x840, 0x858], [0x85e, 0x85e], [0x8a0, 0x8a0], [0x8a2, 0x8ac], [0x200f, 0x200f], [0xfb1d, 0xfb1d], [0xfb1f, 0xfb28], [0xfb2a, 0xfb36], [0xfb38, 0xfb3c], [0xfb3e, 0xfb3e], [0xfb40, 0xfb41], [0xfb43, 0xfb44], [0xfb46, 0xfbc1], [0xfbd3, 0xfd3d], [0xfd50, 0xfd8f], [0xfd92, 0xfdc7], [0xfdf0, 0xfdfc], [0xfe70, 0xfe74], [0xfe76, 0xfefc], [0x10800, 0x10805], [0x10808, 0x10808], [0x1080a, 0x10835], [0x10837, 0x10838], [0x1083c, 0x1083c], [0x1083f, 0x10855], [0x10857, 0x1085f], [0x10900, 0x1091b], [0x10920, 0x10939], [0x1093f, 0x1093f], [0x10980, 0x109b7], [0x109be, 0x109bf], [0x10a00, 0x10a00], [0x10a10, 0x10a13], [0x10a15, 0x10a17], [0x10a19, 0x10a33], [0x10a40, 0x10a47], [0x10a50, 0x10a58], [0x10a60, 0x10a7f], [0x10b00, 0x10b35], [0x10b40, 0x10b55], [0x10b58, 0x10b72], [0x10b78, 0x10b7f], [0x10c00, 0x10c48], [0x1ee00, 0x1ee03], [0x1ee05, 0x1ee1f], [0x1ee21, 0x1ee22], [0x1ee24, 0x1ee24], [0x1ee27, 0x1ee27], [0x1ee29, 0x1ee32], [0x1ee34, 0x1ee37], [0x1ee39, 0x1ee39], [0x1ee3b, 0x1ee3b], [0x1ee42, 0x1ee42], [0x1ee47, 0x1ee47], [0x1ee49, 0x1ee49], [0x1ee4b, 0x1ee4b], [0x1ee4d, 0x1ee4f], [0x1ee51, 0x1ee52], [0x1ee54, 0x1ee54], [0x1ee57, 0x1ee57], [0x1ee59, 0x1ee59], [0x1ee5b, 0x1ee5b], [0x1ee5d, 0x1ee5d], [0x1ee5f, 0x1ee5f], [0x1ee61, 0x1ee62], [0x1ee64, 0x1ee64], [0x1ee67, 0x1ee6a], [0x1ee6c, 0x1ee72], [0x1ee74, 0x1ee77], [0x1ee79, 0x1ee7c], [0x1ee7e, 0x1ee7e], [0x1ee80, 0x1ee89], [0x1ee8b, 0x1ee9b], [0x1eea1, 0x1eea3], [0x1eea5, 0x1eea9], [0x1eeab, 0x1eebb], [0x10fffd, 0x10fffd]];
+
+ function isStrongRTLChar(charCode) {
+ for (var i = 0; i < strongRTLRanges.length; i++) {
+ var currentRange = strongRTLRanges[i];
+
+ if (charCode >= currentRange[0] && charCode <= currentRange[1]) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ function determineBidi(cueDiv) {
+ var nodeStack = [],
+ text = "",
+ charCode;
+
+ if (!cueDiv || !cueDiv.childNodes) {
+ return "ltr";
+ }
+
+ function pushNodes(nodeStack, node) {
+ for (var i = node.childNodes.length - 1; i >= 0; i--) {
+ nodeStack.push(node.childNodes[i]);
+ }
+ }
+
+ function nextTextNode(nodeStack) {
+ if (!nodeStack || !nodeStack.length) {
+ return null;
+ }
+
+ var node = nodeStack.pop(),
+ text = node.textContent || node.innerText;
+
+ if (text) {
+ // TODO: This should match all unicode type B characters (paragraph
+ // separator characters). See issue #115.
+ var m = text.match(/^.*(\n|\r)/);
+
+ if (m) {
+ nodeStack.length = 0;
+ return m[0];
+ }
+
+ return text;
+ }
+
+ if (node.tagName === "ruby") {
+ return nextTextNode(nodeStack);
+ }
+
+ if (node.childNodes) {
+ pushNodes(nodeStack, node);
+ return nextTextNode(nodeStack);
+ }
+ }
+
+ pushNodes(nodeStack, cueDiv);
+
+ while (text = nextTextNode(nodeStack)) {
+ for (var i = 0; i < text.length; i++) {
+ charCode = text.charCodeAt(i);
+
+ if (isStrongRTLChar(charCode)) {
+ return "rtl";
+ }
+ }
+ }
+
+ return "ltr";
+ }
+
+ function computeLinePos(cue) {
+ if (typeof cue.line === "number" && (cue.snapToLines || cue.line >= 0 && cue.line <= 100)) {
+ return cue.line;
+ }
+
+ if (!cue.track || !cue.track.textTrackList || !cue.track.textTrackList.mediaElement) {
+ return -1;
+ }
+
+ var track = cue.track,
+ trackList = track.textTrackList,
+ count = 0;
+
+ for (var i = 0; i < trackList.length && trackList[i] !== track; i++) {
+ if (trackList[i].mode === "showing") {
+ count++;
+ }
+ }
+
+ return ++count * -1;
+ }
+
+ function StyleBox() {} // Apply styles to a div. If there is no div passed then it defaults to the
+ // div on 'this'.
+
+
+ StyleBox.prototype.applyStyles = function (styles, div) {
+ div = div || this.div;
+
+ for (var prop in styles) {
+ if (styles.hasOwnProperty(prop)) {
+ div.style[prop] = styles[prop];
+ }
+ }
+ };
+
+ StyleBox.prototype.formatStyle = function (val, unit) {
+ return val === 0 ? 0 : val + unit;
+ }; // Constructs the computed display state of the cue (a div). Places the div
+ // into the overlay which should be a block level element (usually a div).
+
+
+ function CueStyleBox(window, cue, styleOptions) {
+ StyleBox.call(this);
+ this.cue = cue; // Parse our cue's text into a DOM tree rooted at 'cueDiv'. This div will
+ // have inline positioning and will function as the cue background box.
+
+ this.cueDiv = parseContent(window, cue.text);
+ var styles = {
+ color: "rgba(255, 255, 255, 1)",
+ backgroundColor: "rgba(0, 0, 0, 0.8)",
+ position: "relative",
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0,
+ display: "inline",
+ writingMode: cue.vertical === "" ? "horizontal-tb" : cue.vertical === "lr" ? "vertical-lr" : "vertical-rl",
+ unicodeBidi: "plaintext"
+ };
+ this.applyStyles(styles, this.cueDiv); // Create an absolutely positioned div that will be used to position the cue
+ // div. Note, all WebVTT cue-setting alignments are equivalent to the CSS
+ // mirrors of them except "middle" which is "center" in CSS.
+
+ this.div = window.document.createElement("div");
+ styles = {
+ direction: determineBidi(this.cueDiv),
+ writingMode: cue.vertical === "" ? "horizontal-tb" : cue.vertical === "lr" ? "vertical-lr" : "vertical-rl",
+ unicodeBidi: "plaintext",
+ textAlign: cue.align === "middle" ? "center" : cue.align,
+ font: styleOptions.font,
+ whiteSpace: "pre-line",
+ position: "absolute"
+ };
+ this.applyStyles(styles);
+ this.div.appendChild(this.cueDiv); // Calculate the distance from the reference edge of the viewport to the text
+ // position of the cue box. The reference edge will be resolved later when
+ // the box orientation styles are applied.
+
+ var textPos = 0;
+
+ switch (cue.positionAlign) {
+ case "start":
+ textPos = cue.position;
+ break;
+
+ case "middle":
+ textPos = cue.position - cue.size / 2;
+ break;
+
+ case "end":
+ textPos = cue.position - cue.size;
+ break;
+ } // Horizontal box orientation; textPos is the distance from the left edge of the
+ // area to the left edge of the box and cue.size is the distance extending to
+ // the right from there.
+
+
+ if (cue.vertical === "") {
+ this.applyStyles({
+ left: this.formatStyle(textPos, "%"),
+ width: this.formatStyle(cue.size, "%")
+ }); // Vertical box orientation; textPos is the distance from the top edge of the
+ // area to the top edge of the box and cue.size is the height extending
+ // downwards from there.
+ } else {
+ this.applyStyles({
+ top: this.formatStyle(textPos, "%"),
+ height: this.formatStyle(cue.size, "%")
+ });
+ }
+
+ this.move = function (box) {
+ this.applyStyles({
+ top: this.formatStyle(box.top, "px"),
+ bottom: this.formatStyle(box.bottom, "px"),
+ left: this.formatStyle(box.left, "px"),
+ right: this.formatStyle(box.right, "px"),
+ height: this.formatStyle(box.height, "px"),
+ width: this.formatStyle(box.width, "px")
+ });
+ };
+ }
+
+ CueStyleBox.prototype = _objCreate(StyleBox.prototype);
+ CueStyleBox.prototype.constructor = CueStyleBox; // Represents the co-ordinates of an Element in a way that we can easily
+ // compute things with such as if it overlaps or intersects with another Element.
+ // Can initialize it with either a StyleBox or another BoxPosition.
+
+ function BoxPosition(obj) {
+ // Either a BoxPosition was passed in and we need to copy it, or a StyleBox
+ // was passed in and we need to copy the results of 'getBoundingClientRect'
+ // as the object returned is readonly. All co-ordinate values are in reference
+ // to the viewport origin (top left).
+ var lh, height, width, top;
+
+ if (obj.div) {
+ height = obj.div.offsetHeight;
+ width = obj.div.offsetWidth;
+ top = obj.div.offsetTop;
+ var rects = (rects = obj.div.childNodes) && (rects = rects[0]) && rects.getClientRects && rects.getClientRects();
+ obj = obj.div.getBoundingClientRect(); // In certain cases the outter div will be slightly larger then the sum of
+ // the inner div's lines. This could be due to bold text, etc, on some platforms.
+ // In this case we should get the average line height and use that. This will
+ // result in the desired behaviour.
+
+ lh = rects ? Math.max(rects[0] && rects[0].height || 0, obj.height / rects.length) : 0;
+ }
+
+ this.left = obj.left;
+ this.right = obj.right;
+ this.top = obj.top || top;
+ this.height = obj.height || height;
+ this.bottom = obj.bottom || top + (obj.height || height);
+ this.width = obj.width || width;
+ this.lineHeight = lh !== undefined ? lh : obj.lineHeight;
+ } // Move the box along a particular axis. Optionally pass in an amount to move
+ // the box. If no amount is passed then the default is the line height of the
+ // box.
+
+
+ BoxPosition.prototype.move = function (axis, toMove) {
+ toMove = toMove !== undefined ? toMove : this.lineHeight;
+
+ switch (axis) {
+ case "+x":
+ this.left += toMove;
+ this.right += toMove;
+ break;
+
+ case "-x":
+ this.left -= toMove;
+ this.right -= toMove;
+ break;
+
+ case "+y":
+ this.top += toMove;
+ this.bottom += toMove;
+ break;
+
+ case "-y":
+ this.top -= toMove;
+ this.bottom -= toMove;
+ break;
+ }
+ }; // Check if this box overlaps another box, b2.
+
+
+ BoxPosition.prototype.overlaps = function (b2) {
+ return this.left < b2.right && this.right > b2.left && this.top < b2.bottom && this.bottom > b2.top;
+ }; // Check if this box overlaps any other boxes in boxes.
+
+
+ BoxPosition.prototype.overlapsAny = function (boxes) {
+ for (var i = 0; i < boxes.length; i++) {
+ if (this.overlaps(boxes[i])) {
+ return true;
+ }
+ }
+
+ return false;
+ }; // Check if this box is within another box.
+
+
+ BoxPosition.prototype.within = function (container) {
+ return this.top >= container.top && this.bottom <= container.bottom && this.left >= container.left && this.right <= container.right;
+ }; // Check if this box is entirely within the container or it is overlapping
+ // on the edge opposite of the axis direction passed. For example, if "+x" is
+ // passed and the box is overlapping on the left edge of the container, then
+ // return true.
+
+
+ BoxPosition.prototype.overlapsOppositeAxis = function (container, axis) {
+ switch (axis) {
+ case "+x":
+ return this.left < container.left;
+
+ case "-x":
+ return this.right > container.right;
+
+ case "+y":
+ return this.top < container.top;
+
+ case "-y":
+ return this.bottom > container.bottom;
+ }
+ }; // Find the percentage of the area that this box is overlapping with another
+ // box.
+
+
+ BoxPosition.prototype.intersectPercentage = function (b2) {
+ var x = Math.max(0, Math.min(this.right, b2.right) - Math.max(this.left, b2.left)),
+ y = Math.max(0, Math.min(this.bottom, b2.bottom) - Math.max(this.top, b2.top)),
+ intersectArea = x * y;
+ return intersectArea / (this.height * this.width);
+ }; // Convert the positions from this box to CSS compatible positions using
+ // the reference container's positions. This has to be done because this
+ // box's positions are in reference to the viewport origin, whereas, CSS
+ // values are in referecne to their respective edges.
+
+
+ BoxPosition.prototype.toCSSCompatValues = function (reference) {
+ return {
+ top: this.top - reference.top,
+ bottom: reference.bottom - this.bottom,
+ left: this.left - reference.left,
+ right: reference.right - this.right,
+ height: this.height,
+ width: this.width
+ };
+ }; // Get an object that represents the box's position without anything extra.
+ // Can pass a StyleBox, HTMLElement, or another BoxPositon.
+
+
+ BoxPosition.getSimpleBoxPosition = function (obj) {
+ var height = obj.div ? obj.div.offsetHeight : obj.tagName ? obj.offsetHeight : 0;
+ var width = obj.div ? obj.div.offsetWidth : obj.tagName ? obj.offsetWidth : 0;
+ var top = obj.div ? obj.div.offsetTop : obj.tagName ? obj.offsetTop : 0;
+ obj = obj.div ? obj.div.getBoundingClientRect() : obj.tagName ? obj.getBoundingClientRect() : obj;
+ var ret = {
+ left: obj.left,
+ right: obj.right,
+ top: obj.top || top,
+ height: obj.height || height,
+ bottom: obj.bottom || top + (obj.height || height),
+ width: obj.width || width
+ };
+ return ret;
+ }; // Move a StyleBox to its specified, or next best, position. The containerBox
+ // is the box that contains the StyleBox, such as a div. boxPositions are
+ // a list of other boxes that the styleBox can't overlap with.
+
+
+ function moveBoxToLinePosition(window, styleBox, containerBox, boxPositions) {
+ // Find the best position for a cue box, b, on the video. The axis parameter
+ // is a list of axis, the order of which, it will move the box along. For example:
+ // Passing ["+x", "-x"] will move the box first along the x axis in the positive
+ // direction. If it doesn't find a good position for it there it will then move
+ // it along the x axis in the negative direction.
+ function findBestPosition(b, axis) {
+ var bestPosition,
+ specifiedPosition = new BoxPosition(b),
+ percentage = 1; // Highest possible so the first thing we get is better.
+
+ for (var i = 0; i < axis.length; i++) {
+ while (b.overlapsOppositeAxis(containerBox, axis[i]) || b.within(containerBox) && b.overlapsAny(boxPositions)) {
+ b.move(axis[i]);
+ } // We found a spot where we aren't overlapping anything. This is our
+ // best position.
+
+
+ if (b.within(containerBox)) {
+ return b;
+ }
+
+ var p = b.intersectPercentage(containerBox); // If we're outside the container box less then we were on our last try
+ // then remember this position as the best position.
+
+ if (percentage > p) {
+ bestPosition = new BoxPosition(b);
+ percentage = p;
+ } // Reset the box position to the specified position.
+
+
+ b = new BoxPosition(specifiedPosition);
+ }
+
+ return bestPosition || specifiedPosition;
+ }
+
+ var boxPosition = new BoxPosition(styleBox),
+ cue = styleBox.cue,
+ linePos = computeLinePos(cue),
+ axis = []; // If we have a line number to align the cue to.
+
+ if (cue.snapToLines) {
+ var size;
+
+ switch (cue.vertical) {
+ case "":
+ axis = ["+y", "-y"];
+ size = "height";
+ break;
+
+ case "rl":
+ axis = ["+x", "-x"];
+ size = "width";
+ break;
+
+ case "lr":
+ axis = ["-x", "+x"];
+ size = "width";
+ break;
+ }
+
+ var step = boxPosition.lineHeight,
+ position = step * Math.round(linePos),
+ maxPosition = containerBox[size] + step,
+ initialAxis = axis[0]; // If the specified intial position is greater then the max position then
+ // clamp the box to the amount of steps it would take for the box to
+ // reach the max position.
+
+ if (Math.abs(position) > maxPosition) {
+ position = position < 0 ? -1 : 1;
+ position *= Math.ceil(maxPosition / step) * step;
+ } // If computed line position returns negative then line numbers are
+ // relative to the bottom of the video instead of the top. Therefore, we
+ // need to increase our initial position by the length or width of the
+ // video, depending on the writing direction, and reverse our axis directions.
+
+
+ if (linePos < 0) {
+ position += cue.vertical === "" ? containerBox.height : containerBox.width;
+ axis = axis.reverse();
+ } // Move the box to the specified position. This may not be its best
+ // position.
+
+
+ boxPosition.move(initialAxis, position);
+ } else {
+ // If we have a percentage line value for the cue.
+ var calculatedPercentage = boxPosition.lineHeight / containerBox.height * 100;
+
+ switch (cue.lineAlign) {
+ case "middle":
+ linePos -= calculatedPercentage / 2;
+ break;
+
+ case "end":
+ linePos -= calculatedPercentage;
+ break;
+ } // Apply initial line position to the cue box.
+
+
+ switch (cue.vertical) {
+ case "":
+ styleBox.applyStyles({
+ top: styleBox.formatStyle(linePos, "%")
+ });
+ break;
+
+ case "rl":
+ styleBox.applyStyles({
+ left: styleBox.formatStyle(linePos, "%")
+ });
+ break;
+
+ case "lr":
+ styleBox.applyStyles({
+ right: styleBox.formatStyle(linePos, "%")
+ });
+ break;
+ }
+
+ axis = ["+y", "-x", "+x", "-y"]; // Get the box position again after we've applied the specified positioning
+ // to it.
+
+ boxPosition = new BoxPosition(styleBox);
+ }
+
+ var bestPosition = findBestPosition(boxPosition, axis);
+ styleBox.move(bestPosition.toCSSCompatValues(containerBox));
+ }
+
+ function WebVTT$1() {} // Nothing
+ // Helper to allow strings to be decoded instead of the default binary utf8 data.
+
+
+ WebVTT$1.StringDecoder = function () {
+ return {
+ decode: function decode(data) {
+ if (!data) {
+ return "";
+ }
+
+ if (typeof data !== "string") {
+ throw new Error("Error - expected string data.");
+ }
+
+ return decodeURIComponent(encodeURIComponent(data));
+ }
+ };
+ };
+
+ WebVTT$1.convertCueToDOMTree = function (window, cuetext) {
+ if (!window || !cuetext) {
+ return null;
+ }
+
+ return parseContent(window, cuetext);
+ };
+
+ var FONT_SIZE_PERCENT = 0.05;
+ var FONT_STYLE = "sans-serif";
+ var CUE_BACKGROUND_PADDING = "1.5%"; // Runs the processing model over the cues and regions passed to it.
+ // @param overlay A block level element (usually a div) that the computed cues
+ // and regions will be placed into.
+
+ WebVTT$1.processCues = function (window, cues, overlay) {
+ if (!window || !cues || !overlay) {
+ return null;
+ } // Remove all previous children.
+
+
+ while (overlay.firstChild) {
+ overlay.removeChild(overlay.firstChild);
+ }
+
+ var paddedOverlay = window.document.createElement("div");
+ paddedOverlay.style.position = "absolute";
+ paddedOverlay.style.left = "0";
+ paddedOverlay.style.right = "0";
+ paddedOverlay.style.top = "0";
+ paddedOverlay.style.bottom = "0";
+ paddedOverlay.style.margin = CUE_BACKGROUND_PADDING;
+ overlay.appendChild(paddedOverlay); // Determine if we need to compute the display states of the cues. This could
+ // be the case if a cue's state has been changed since the last computation or
+ // if it has not been computed yet.
+
+ function shouldCompute(cues) {
+ for (var i = 0; i < cues.length; i++) {
+ if (cues[i].hasBeenReset || !cues[i].displayState) {
+ return true;
+ }
+ }
+
+ return false;
+ } // We don't need to recompute the cues' display states. Just reuse them.
+
+
+ if (!shouldCompute(cues)) {
+ for (var i = 0; i < cues.length; i++) {
+ paddedOverlay.appendChild(cues[i].displayState);
+ }
+
+ return;
+ }
+
+ var boxPositions = [],
+ containerBox = BoxPosition.getSimpleBoxPosition(paddedOverlay),
+ fontSize = Math.round(containerBox.height * FONT_SIZE_PERCENT * 100) / 100;
+ var styleOptions = {
+ font: fontSize + "px " + FONT_STYLE
+ };
+
+ (function () {
+ var styleBox, cue;
+
+ for (var i = 0; i < cues.length; i++) {
+ cue = cues[i]; // Compute the intial position and styles of the cue div.
+
+ styleBox = new CueStyleBox(window, cue, styleOptions);
+ paddedOverlay.appendChild(styleBox.div); // Move the cue div to it's correct line position.
+
+ moveBoxToLinePosition(window, styleBox, containerBox, boxPositions); // Remember the computed div so that we don't have to recompute it later
+ // if we don't have too.
+
+ cue.displayState = styleBox.div;
+ boxPositions.push(BoxPosition.getSimpleBoxPosition(styleBox));
+ }
+ })();
+ };
+
+ WebVTT$1.Parser = function (window, vttjs, decoder) {
+ if (!decoder) {
+ decoder = vttjs;
+ vttjs = {};
+ }
+
+ if (!vttjs) {
+ vttjs = {};
+ }
+
+ this.window = window;
+ this.vttjs = vttjs;
+ this.state = "INITIAL";
+ this.buffer = "";
+ this.decoder = decoder || new TextDecoder("utf8");
+ this.regionList = [];
+ };
+
+ WebVTT$1.Parser.prototype = {
+ // If the error is a ParsingError then report it to the consumer if
+ // possible. If it's not a ParsingError then throw it like normal.
+ reportOrThrowError: function reportOrThrowError(e) {
+ if (e instanceof ParsingError) {
+ this.onparsingerror && this.onparsingerror(e);
+ } else {
+ throw e;
+ }
+ },
+ parse: function parse(data) {
+ var self = this; // If there is no data then we won't decode it, but will just try to parse
+ // whatever is in buffer already. This may occur in circumstances, for
+ // example when flush() is called.
+
+ if (data) {
+ // Try to decode the data that we received.
+ self.buffer += self.decoder.decode(data, {
+ stream: true
+ });
+ }
+
+ function collectNextLine() {
+ var buffer = self.buffer;
+ var pos = 0;
+
+ while (pos < buffer.length && buffer[pos] !== '\r' && buffer[pos] !== '\n') {
+ ++pos;
+ }
+
+ var line = buffer.substr(0, pos); // Advance the buffer early in case we fail below.
+
+ if (buffer[pos] === '\r') {
+ ++pos;
+ }
+
+ if (buffer[pos] === '\n') {
+ ++pos;
+ }
+
+ self.buffer = buffer.substr(pos);
+ return line;
+ } // 3.4 WebVTT region and WebVTT region settings syntax
+
+
+ function parseRegion(input) {
+ var settings = new Settings();
+ parseOptions(input, function (k, v) {
+ switch (k) {
+ case "id":
+ settings.set(k, v);
+ break;
+
+ case "width":
+ settings.percent(k, v);
+ break;
+
+ case "lines":
+ settings.integer(k, v);
+ break;
+
+ case "regionanchor":
+ case "viewportanchor":
+ var xy = v.split(',');
+
+ if (xy.length !== 2) {
+ break;
+ } // We have to make sure both x and y parse, so use a temporary
+ // settings object here.
+
+
+ var anchor = new Settings();
+ anchor.percent("x", xy[0]);
+ anchor.percent("y", xy[1]);
+
+ if (!anchor.has("x") || !anchor.has("y")) {
+ break;
+ }
+
+ settings.set(k + "X", anchor.get("x"));
+ settings.set(k + "Y", anchor.get("y"));
+ break;
+
+ case "scroll":
+ settings.alt(k, v, ["up"]);
+ break;
+ }
+ }, /=/, /\s/); // Create the region, using default values for any values that were not
+ // specified.
+
+ if (settings.has("id")) {
+ var region = new (self.vttjs.VTTRegion || self.window.VTTRegion)();
+ region.width = settings.get("width", 100);
+ region.lines = settings.get("lines", 3);
+ region.regionAnchorX = settings.get("regionanchorX", 0);
+ region.regionAnchorY = settings.get("regionanchorY", 100);
+ region.viewportAnchorX = settings.get("viewportanchorX", 0);
+ region.viewportAnchorY = settings.get("viewportanchorY", 100);
+ region.scroll = settings.get("scroll", ""); // Register the region.
+
+ self.onregion && self.onregion(region); // Remember the VTTRegion for later in case we parse any VTTCues that
+ // reference it.
+
+ self.regionList.push({
+ id: settings.get("id"),
+ region: region
+ });
+ }
+ } // draft-pantos-http-live-streaming-20
+ // https://tools.ietf.org/html/draft-pantos-http-live-streaming-20#section-3.5
+ // 3.5 WebVTT
+
+
+ function parseTimestampMap(input) {
+ var settings = new Settings();
+ parseOptions(input, function (k, v) {
+ switch (k) {
+ case "MPEGT":
+ settings.integer(k + 'S', v);
+ break;
+
+ case "LOCA":
+ settings.set(k + 'L', parseTimeStamp(v));
+ break;
+ }
+ }, /[^\d]:/, /,/);
+ self.ontimestampmap && self.ontimestampmap({
+ "MPEGTS": settings.get("MPEGTS"),
+ "LOCAL": settings.get("LOCAL")
+ });
+ } // 3.2 WebVTT metadata header syntax
+
+
+ function parseHeader(input) {
+ if (input.match(/X-TIMESTAMP-MAP/)) {
+ // This line contains HLS X-TIMESTAMP-MAP metadata
+ parseOptions(input, function (k, v) {
+ switch (k) {
+ case "X-TIMESTAMP-MAP":
+ parseTimestampMap(v);
+ break;
+ }
+ }, /=/);
+ } else {
+ parseOptions(input, function (k, v) {
+ switch (k) {
+ case "Region":
+ // 3.3 WebVTT region metadata header syntax
+ parseRegion(v);
+ break;
+ }
+ }, /:/);
+ }
+ } // 5.1 WebVTT file parsing.
+
+
+ try {
+ var line;
+
+ if (self.state === "INITIAL") {
+ // We can't start parsing until we have the first line.
+ if (!/\r\n|\n/.test(self.buffer)) {
+ return this;
+ }
+
+ line = collectNextLine();
+ var m = line.match(/^WEBVTT([ \t].*)?$/);
+
+ if (!m || !m[0]) {
+ throw new ParsingError(ParsingError.Errors.BadSignature);
+ }
+
+ self.state = "HEADER";
+ }
+
+ var alreadyCollectedLine = false;
+
+ while (self.buffer) {
+ // We can't parse a line until we have the full line.
+ if (!/\r\n|\n/.test(self.buffer)) {
+ return this;
+ }
+
+ if (!alreadyCollectedLine) {
+ line = collectNextLine();
+ } else {
+ alreadyCollectedLine = false;
+ }
+
+ switch (self.state) {
+ case "HEADER":
+ // 13-18 - Allow a header (metadata) under the WEBVTT line.
+ if (/:/.test(line)) {
+ parseHeader(line);
+ } else if (!line) {
+ // An empty line terminates the header and starts the body (cues).
+ self.state = "ID";
+ }
+
+ continue;
+
+ case "NOTE":
+ // Ignore NOTE blocks.
+ if (!line) {
+ self.state = "ID";
+ }
+
+ continue;
+
+ case "ID":
+ // Check for the start of NOTE blocks.
+ if (/^NOTE($|[ \t])/.test(line)) {
+ self.state = "NOTE";
+ break;
+ } // 19-29 - Allow any number of line terminators, then initialize new cue values.
+
+
+ if (!line) {
+ continue;
+ }
+
+ self.cue = new (self.vttjs.VTTCue || self.window.VTTCue)(0, 0, "");
+ self.state = "CUE"; // 30-39 - Check if self line contains an optional identifier or timing data.
+
+ if (line.indexOf("-->") === -1) {
+ self.cue.id = line;
+ continue;
+ }
+
+ // Process line as start of a cue.
+
+ /*falls through*/
+
+ case "CUE":
+ // 40 - Collect cue timings and settings.
+ try {
+ parseCue(line, self.cue, self.regionList);
+ } catch (e) {
+ self.reportOrThrowError(e); // In case of an error ignore rest of the cue.
+
+ self.cue = null;
+ self.state = "BADCUE";
+ continue;
+ }
+
+ self.state = "CUETEXT";
+ continue;
+
+ case "CUETEXT":
+ var hasSubstring = line.indexOf("-->") !== -1; // 34 - If we have an empty line then report the cue.
+ // 35 - If we have the special substring '-->' then report the cue,
+ // but do not collect the line as we need to process the current
+ // one as a new cue.
+
+ if (!line || hasSubstring && (alreadyCollectedLine = true)) {
+ // We are done parsing self cue.
+ self.oncue && self.oncue(self.cue);
+ self.cue = null;
+ self.state = "ID";
+ continue;
+ }
+
+ if (self.cue.text) {
+ self.cue.text += "\n";
+ }
+
+ self.cue.text += line;
+ continue;
+
+ case "BADCUE":
+ // BADCUE
+ // 54-62 - Collect and discard the remaining cue.
+ if (!line) {
+ self.state = "ID";
+ }
+
+ continue;
+ }
+ }
+ } catch (e) {
+ self.reportOrThrowError(e); // If we are currently parsing a cue, report what we have.
+
+ if (self.state === "CUETEXT" && self.cue && self.oncue) {
+ self.oncue(self.cue);
+ }
+
+ self.cue = null; // Enter BADWEBVTT state if header was not parsed correctly otherwise
+ // another exception occurred so enter BADCUE state.
+
+ self.state = self.state === "INITIAL" ? "BADWEBVTT" : "BADCUE";
+ }
+
+ return this;
+ },
+ flush: function flush() {
+ var self = this;
+
+ try {
+ // Finish decoding the stream.
+ self.buffer += self.decoder.decode(); // Synthesize the end of the current cue or region.
+
+ if (self.cue || self.state === "HEADER") {
+ self.buffer += "\n\n";
+ self.parse();
+ } // If we've flushed, parsed, and we're still on the INITIAL state then
+ // that means we don't have enough of the stream to parse the first
+ // line.
+
+
+ if (self.state === "INITIAL") {
+ throw new ParsingError(ParsingError.Errors.BadSignature);
+ }
+ } catch (e) {
+ self.reportOrThrowError(e);
+ }
+
+ self.onflush && self.onflush();
+ return this;
+ }
+ };
+ var vtt = WebVTT$1;
+
+ /**
+ * Copyright 2013 vtt.js Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ var autoKeyword = "auto";
+ var directionSetting = {
+ "": 1,
+ "lr": 1,
+ "rl": 1
+ };
+ var alignSetting = {
+ "start": 1,
+ "middle": 1,
+ "end": 1,
+ "left": 1,
+ "right": 1
+ };
+
+ function findDirectionSetting(value) {
+ if (typeof value !== "string") {
+ return false;
+ }
+
+ var dir = directionSetting[value.toLowerCase()];
+ return dir ? value.toLowerCase() : false;
+ }
+
+ function findAlignSetting(value) {
+ if (typeof value !== "string") {
+ return false;
+ }
+
+ var align = alignSetting[value.toLowerCase()];
+ return align ? value.toLowerCase() : false;
+ }
+
+ function VTTCue(startTime, endTime, text) {
+ /**
+ * Shim implementation specific properties. These properties are not in
+ * the spec.
+ */
+ // Lets us know when the VTTCue's data has changed in such a way that we need
+ // to recompute its display state. This lets us compute its display state
+ // lazily.
+ this.hasBeenReset = false;
+ /**
+ * VTTCue and TextTrackCue properties
+ * http://dev.w3.org/html5/webvtt/#vttcue-interface
+ */
+
+ var _id = "";
+ var _pauseOnExit = false;
+ var _startTime = startTime;
+ var _endTime = endTime;
+ var _text = text;
+ var _region = null;
+ var _vertical = "";
+ var _snapToLines = true;
+ var _line = "auto";
+ var _lineAlign = "start";
+ var _position = 50;
+ var _positionAlign = "middle";
+ var _size = 50;
+ var _align = "middle";
+ Object.defineProperties(this, {
+ "id": {
+ enumerable: true,
+ get: function get() {
+ return _id;
+ },
+ set: function set(value) {
+ _id = "" + value;
+ }
+ },
+ "pauseOnExit": {
+ enumerable: true,
+ get: function get() {
+ return _pauseOnExit;
+ },
+ set: function set(value) {
+ _pauseOnExit = !!value;
+ }
+ },
+ "startTime": {
+ enumerable: true,
+ get: function get() {
+ return _startTime;
+ },
+ set: function set(value) {
+ if (typeof value !== "number") {
+ throw new TypeError("Start time must be set to a number.");
+ }
+
+ _startTime = value;
+ this.hasBeenReset = true;
+ }
+ },
+ "endTime": {
+ enumerable: true,
+ get: function get() {
+ return _endTime;
+ },
+ set: function set(value) {
+ if (typeof value !== "number") {
+ throw new TypeError("End time must be set to a number.");
+ }
+
+ _endTime = value;
+ this.hasBeenReset = true;
+ }
+ },
+ "text": {
+ enumerable: true,
+ get: function get() {
+ return _text;
+ },
+ set: function set(value) {
+ _text = "" + value;
+ this.hasBeenReset = true;
+ }
+ },
+ "region": {
+ enumerable: true,
+ get: function get() {
+ return _region;
+ },
+ set: function set(value) {
+ _region = value;
+ this.hasBeenReset = true;
+ }
+ },
+ "vertical": {
+ enumerable: true,
+ get: function get() {
+ return _vertical;
+ },
+ set: function set(value) {
+ var setting = findDirectionSetting(value); // Have to check for false because the setting an be an empty string.
+
+ if (setting === false) {
+ throw new SyntaxError("An invalid or illegal string was specified.");
+ }
+
+ _vertical = setting;
+ this.hasBeenReset = true;
+ }
+ },
+ "snapToLines": {
+ enumerable: true,
+ get: function get() {
+ return _snapToLines;
+ },
+ set: function set(value) {
+ _snapToLines = !!value;
+ this.hasBeenReset = true;
+ }
+ },
+ "line": {
+ enumerable: true,
+ get: function get() {
+ return _line;
+ },
+ set: function set(value) {
+ if (typeof value !== "number" && value !== autoKeyword) {
+ throw new SyntaxError("An invalid number or illegal string was specified.");
+ }
+
+ _line = value;
+ this.hasBeenReset = true;
+ }
+ },
+ "lineAlign": {
+ enumerable: true,
+ get: function get() {
+ return _lineAlign;
+ },
+ set: function set(value) {
+ var setting = findAlignSetting(value);
+
+ if (!setting) {
+ throw new SyntaxError("An invalid or illegal string was specified.");
+ }
+
+ _lineAlign = setting;
+ this.hasBeenReset = true;
+ }
+ },
+ "position": {
+ enumerable: true,
+ get: function get() {
+ return _position;
+ },
+ set: function set(value) {
+ if (value < 0 || value > 100) {
+ throw new Error("Position must be between 0 and 100.");
+ }
+
+ _position = value;
+ this.hasBeenReset = true;
+ }
+ },
+ "positionAlign": {
+ enumerable: true,
+ get: function get() {
+ return _positionAlign;
+ },
+ set: function set(value) {
+ var setting = findAlignSetting(value);
+
+ if (!setting) {
+ throw new SyntaxError("An invalid or illegal string was specified.");
+ }
+
+ _positionAlign = setting;
+ this.hasBeenReset = true;
+ }
+ },
+ "size": {
+ enumerable: true,
+ get: function get() {
+ return _size;
+ },
+ set: function set(value) {
+ if (value < 0 || value > 100) {
+ throw new Error("Size must be between 0 and 100.");
+ }
+
+ _size = value;
+ this.hasBeenReset = true;
+ }
+ },
+ "align": {
+ enumerable: true,
+ get: function get() {
+ return _align;
+ },
+ set: function set(value) {
+ var setting = findAlignSetting(value);
+
+ if (!setting) {
+ throw new SyntaxError("An invalid or illegal string was specified.");
+ }
+
+ _align = setting;
+ this.hasBeenReset = true;
+ }
+ }
+ });
+ /**
+ * Other