diff --git a/form/.editorconfig b/form/.editorconfig
new file mode 100644
index 0000000000000000000000000000000000000000..e89330a618c137cdaccde46f87923736cc04dfb4
--- /dev/null
+++ b/form/.editorconfig
@@ -0,0 +1,13 @@
+# Editor configuration, see https://editorconfig.org
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+max_line_length = off
+trim_trailing_whitespace = false
diff --git a/form/.gitignore b/form/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..6b772c3b1a96c53eb307155fefcf698aef757188
--- /dev/null
+++ b/form/.gitignore
@@ -0,0 +1,47 @@
+# See http://help.github.com/ignore-files/ for more about ignoring files.
+
+# compiled output
+/dist
+/tmp
+/out-tsc
+# Only exists if Bazel was run
+/bazel-out
+
+# dependencies
+/node_modules
+/publish
+
+# profiling files
+chrome-profiler-events*.json
+speed-measure-plugin*.json
+
+# IDEs and editors
+/.idea
+.project
+.classpath
+.c9/
+*.launch
+.settings/
+*.sublime-workspace
+
+# IDE - VSCode
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+.history/*
+
+# misc
+/.sass-cache
+/connect.lock
+/coverage
+/libpeerconnection.log
+npm-debug.log
+yarn-error.log
+testem.log
+/typings
+
+# System Files
+.DS_Store
+Thumbs.db
diff --git a/form/README.md b/form/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..265f0022e807a1ed9d7be9bdc03cdb0d051190df
--- /dev/null
+++ b/form/README.md
@@ -0,0 +1,356 @@
+# ng-template-generator
+
+angularjs技术框架下的H5互动模板框架脚手架,基于NG-ZORRO实现快速开发基于绘玩云的H5互动课件。
+
+# 使用简介
+
+## 前期准备
+
+* git下载  https://git-scm.com/downloads 
+* nodejs下载  https://nodejs.org/zh-cn/download/
+* 谷歌浏览器下载  https://www.google.cn/chrome/
+
+都下载最新版就行,然后默认安装就可以
+
+
+## 生成项目
+
+* 登录绘玩云课件开发平台:http://staging-teach.ireadabc.com/ 
+* 点击“登录账号,查看我的课件”
+* 输入测试的用户名/密码:developers/12345678
+* 在右上角“个人中心”的下拉菜单里,点击“我的模板” 菜单,然后点击“新建模板”, 填写必要的信息,在“技术选型”一项上选择“Angular”
+* 点击“确定”后,列表页就会出现一个新生成的模板项目
+* 在项目的卡片下找到“开发”按钮,则会弹出相对应的git地址
+
+## 获取并启动项目
+
+```
+// xxx 是上面项目对应的Git地址
+git clone xxx
+cd 项目名称/
+npm install -g yarn
+yarn install
+npm start
+
+//启动成功后打开浏览器,输入:http://localhost:4200 则可以看到这个项目的初始化效果了
+
+```
+
+## 项目结构
+
+|-- bin
+
+|-- dist
+
+|-- e2e
+
+|-- node_modules
+
+|-- publish
+
+|-- src
+
+| |-- app
+
+| | |-- common
+
+| | |-- form
+
+| | |-- pipes
+
+| | |-- play
+
+| | |-- services
+
+| | |-- style
+
+| | |-- app.component.html
+
+| | |-- app.component.scss
+
+| | |-- app.component.ts
+
+| | |-- app.module.ts
+
+| |-- assets
+
+| |-- environments
+
+| |-- services
+
+| |-- index.html
+
+| |-- main.ts
+
+| |--.......
+
+
+
+* 其中 play 文件夹是H5模板展示页面,form文件夹是模板的配置页面
+* common文件夹是以往做过的模板积累的一些比较常用的 AngularJs 的组件
+* assets文件夹存放模板样式的静态资源,例如小图标、背景图片、字体、样式等等
+* 其他文件夹开发者可以不做任何更改
+
+## 开始开发
+
+本脚手架的页面类UI框架是基于  [NG-ZORRO](https://ng.ant.design/docs/introduce/zh "With a Title") 框架实现的,在配置页面(form文件夹),一些表单输入,上传图片等组件也可以使用 NG-ZORRO 现成的组件
+
+### 开发配置页面(form/)
+
+配置页面主要作用是为了呈现多样化模板,提高H5模板的高复用性、灵活性提供的一个配置工具,根据H5模板的设计规划或特性,会规定某些元素是可以配置的,比如背景图片、音频、选项、标题等等等等。(课件制作者) 通过配置页面,就可以灵活的更换这些,从而形成多种多样的课件。
+
+
+配置页面的开发要求很简单,我们不关心布局、样式、交互等所有实现细节与效果,只需调用两个内置的全局接口即可:
+
+* setData 接口,用于将配置的数据存储到云端。
+
+  此方法是一个异步的方法,接收三个必要参数
+
+  obj: 一个json对象
+
+  callback: 回调函数
+
+  t_name: 模板名称
+
+  一般使用例子如下:
+
+  ```
+  // ts语法,不能直接使用js的window对象自定义方法
+  (<any> window).courseware.setData(obj, callback, t_name);
+  ```
+
+* getData 接口,用于当点击修改的时候将配置的数据从云端获取下来。
+
+  此方法是一个异步的方法,接收两个必要参数 
+
+  > callback: 回调函数, 回调函数里会得到云端保存的数据
+
+  > t_name: 模板名称
+
+  一般使用例子如下:
+
+  ```
+  // ts语法,不能直接使用js的window对象自定义方法
+  const getData = (<any> window).courseware.getData;
+  getData((data) => {
+    //data 就是云端获取的数据,也就是setData方法存到云端的那个obj
+    //模板开发者需要根据数据,依次填写到表单的相应位置,方便用户二次修改编辑
+  });
+  ```
+
+### 开发展示页面(play/)
+
+展示页面主要是呈现模板,通过H5技术,实现多样化的交互与展示方式。
+
+
+展示页面更简单,开发者可以运用各种技术,实现Web端可以实现的任何效果,包括2D、3D、动画、游戏等各种场景,把表单配置的数据获取到,用于渲染相对应的页面即可:
+ 
+* getData 接口,用于当点击修改的时候将配置的数据从云端获取下来。
+
+  此方法是一个异步的方法,接收两个必要参数 
+
+  > callback: 回调函数, 回调函数里会得到云端保存的数据
+
+  > t_name: 模板名称
+
+  一般使用例子如下:
+
+  ```
+  // ts语法,不能直接使用js的window对象自定义方法
+  const getData = (<any> window).courseware.getData;
+  getData((data) => {
+    //data 就是云端获取的数据,也就是setData方法存到云端的那个obj
+    //模板开发者需要根据数据来渲染或控制展示页面的呈现,达到模板的互动目的 
+  });
+  ```
+
+  ### 互动课件
+
+  通过H5的交互方式,我们可以很轻松的制作在线的互动课件,使老师端与学生端实现联动,使在线课堂更具有交互性。
+
+  * onEvent 定义事件:用于自定义同步事件,配合多端互动使用,与sendEvent方法配合使用,用于监听多端发送的同步事件
+
+  参数
+
+  > evtName : 自定义事件的名称
+
+  > callback : 回调函数,回调函数里会得到传过来的数据
+
+  使用例子:
+
+  ```
+  const cw = (<any> window).courseware;
+  //订阅一个事件
+  cw.onEvent('testEvent', (data,next) => {
+    console.log(data); 
+    //处理事件的同步逻辑
+
+    //在逻辑处理完之后一定要执行next方法,用于解锁事件队列
+    next();
+  }); 
+  ```
+
+  * sendEvent 发送事件:用于自定义同步事件,配合多端互动使用,与onEvent方法配合使用,用于对端发送同步事件
+
+  参数
+
+  > evtName : 自定义事件的名称
+
+  > data : 传递的参数 
+
+  使用例子:
+
+  ```
+  const cw = (<any> window).courseware;
+  //发送事件
+  //这样,所有端(教师、学生) 都会触发 testEvent 方法
+  cw.sendEvent('testEvent', 'Hello world'); 
+
+  ```
+
+  * storeAspect 存储切面数据: 课件的切面状态数据;互动课件需要每一个开发者设计切面状态,用于学生进入已经进行的课件,恢复课件同步。例如老师讲图片轮播到第四页,这个时候学生进入教师,要恢复到第四页,才能跟老师同步上课。
+
+  参数
+
+  > data : 保存的数据,一个JSON对象
+
+  使用例子:
+
+  ```
+  const cw = (<any> window).courseware; 
+  //存储切面数据,用于学生进入教室,同步课堂信息等
+  cw.storeAspect({page: 5}); 
+  ```
+
+  * getAspect 获取切面数据: 课件的切面状态数据;互动课件需要每一个开发者设计切面状态,用于学生进入已经进行的课件,恢复课件同步。例如老师讲图片轮播到第四页,这个时候学生进入教师,要恢复到第四页,才能跟老师同步上课。
+
+  参数
+
+  > callback : 获取切面数据的回调函数
+
+  使用例子:
+
+  ```
+  const cw = (<any> window).courseware; 
+  //存储切面数据,用于学生进入教室,同步课堂信息等
+  cw.getAspect(function(aspect){
+    //恢复切面状态的逻辑
+  }); 
+  ```
+
+  * 补充项
+
+  1)  有时候我们在实现模板效果的时候,需要一些基本的信息,比如教室信息、当前用户信息等等,例如,老师会显示某个按钮,而学生不显示,我们可以用如下方式获取教室信息;后续大部分静态信息都会完善到这个对象里
+
+  ```
+  //获取教室信息,值得注意的是获取这个信息一定要再 getData方法之后,或者确保页面完成之后
+  getData((data, aspect) => {
+    let airClassInfo = window["air"].airClassInfo;
+    console.log(airClassInfo);
+  }); 
+  ```
+
+  2)  互动课件的 getData 方法的回调函数里多返回了一个切面参数,类似如下:
+
+  ```
+  const getData = (<any> window).courseware.getData;
+  getData((data, aspect) => {
+    //data 就是云端获取的数据,也就是setData方法存到云端的那个obj
+    //aspect 切面参数,用于恢复页面的状态
+    //模板开发者需要根据数据来渲染或控制展示页面的呈现,达到模板的互动目的 
+  });
+  ``` 
+
+  3)  一些必要的内置事件,通过监听这些事件,我们可以实现一些相关的功能
+
+      * userchange 事件,用来监听教室内部的人员变动情况,例如某人进入,某人退出都会触发此事件
+
+        示例:
+
+        ```
+        const cw = (<any> window).courseware;
+        //订阅一个事件
+        cw.onEvent('userchange', (data,next) => {
+          //data的结构如下
+          /*{
+           *  id: '变动用户的ID',
+           *  connected: true/false,  true: 连接的,false:d断开的
+           *  status: 'connect/reconnect',  connect:新建连接,reconnect:重新连接;这个字段在断开连接的事件里是缺失的
+           *  all_user: []  这个是现有的用户列表 
+           *}
+           */
+          console.log(data); 
+          //处理事件的同步逻辑
+
+          //在逻辑处理完之后一定要执行next方法,用于解锁事件队列
+          next();
+        }); 
+        
+        ```
+
+  4)  测试互动课件
+
+      我们在本地开发中,无法模拟线上老师和学生互动的交互场景,所以提供了一套开发者测试的账号,如下:
+
+      测试地址:http://staging-ac.ireadabc.com/ 
+
+      老师用户名/密码:devtea / 1
+
+      学生用户名/密码: 学生1(13877777711 / 1)    学生2(13877777712 / 1)
+
+
+      测试方法: 
+
+      1、首先开发者发布完模板之后,在制作课件的菜单下用这个模板制作一个课件
+
+      2、在本地打开两个浏览器窗口  一个登陆老师用户,另一个登陆学生用户
+
+      3、老师用户进去之后,就会有很多课件,这个时候双击自己制作的课件,学生的窗口就会同步的打开课件了
+
+      4、测试自己的交互事件,看看老师端和学生端是否是自己想要的展示效果
+
+
+
+  ### 补充方法
+
+  在模板的编辑或展示过程中,还会经常遇到如下两个场景,特提供针对性的解决办法
+
+  1)  表单录入往往需要上传图片、音视频等多媒体文件,所以脚手架内置如下方法,用于获取一个上传到云端的接口方法
+
+  ```
+  const uploadUrl = (<any> window).courseware.uploadUrl;
+  const uploadData = (<any> window).courseware.uploadData;
+
+  //例如用于NG-ZORRO的上传组件则如下写法:
+  <nz-upload class="p-image-uploader" [nzDisabled]="disableUpload"
+    [nzShowUploadList]="false"
+    nzAccept = "image/*"
+    [nzAction]="uploadUrl"    <!-- 注意这里 -->
+    [nzData]="uploadData"     <!-- 注意这里 -->
+    (nzChange)="handleChange($event)">
+  </nz-upload>
+
+  ```
+
+  2)  在展示页面加载完成的时候,需要调用一下下面的方法,用于释放切换课件的遮罩层
+
+  ```
+  //此方法为固定写法,
+  //其中t_name为模板名称,obj是云端存储的配置数据
+  window["air"].hideAirClassLoading(t_name,obj);
+  ```
+
+
+  ## 打包发布
+
+  模板开发完成之后,要推送到云平台上使用则需要进行打包发布操作
+
+  ```
+  npm run publish
+  ```
+  在项目根目录下执行上述命令,则在 ./publish 目录下生产一个 .zip的压缩包,此时打开云平台
+  http://staging-teach.ireadabc.com/ 
+  点击“模板管理” 菜单,找到对应的模板卡片,点击“发布”按钮,在弹出的对话框中选中压缩包,然后点击“确定”,上传完成后,则发布就成功了
+
+
+  
\ No newline at end of file
diff --git a/form/angular.json b/form/angular.json
new file mode 100644
index 0000000000000000000000000000000000000000..5952480a9ced5a16dc48a17e46fb1669d4e89426
--- /dev/null
+++ b/form/angular.json
@@ -0,0 +1,132 @@
+{
+  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
+  "version": 1,
+  "newProjectRoot": "projects",
+  "projects": {
+    "ng-template-generator": {
+      "projectType": "application",
+      "schematics": {},
+      "root": "",
+      "sourceRoot": "src",
+      "prefix": "app",
+      "architect": {
+        "build": {
+          "builder": "@angular-devkit/build-angular:browser",
+          "options": {
+            "outputPath": "dist/ng-template-generator",
+            "index": "src/index.html",
+            "main": "src/main.ts",
+            "polyfills": "src/polyfills.ts",
+            "tsConfig": "tsconfig.app.json",
+            "aot": true,
+            "assets": [
+              "src/favicon.ico",
+              "src/assets",
+              {
+                "glob": "**/*",
+                "input": "./node_modules/@ant-design/icons-angular/src/inline-svg/",
+                "output": "/assets/"
+              }
+            ],
+            "styles": [
+              "./node_modules/ng-zorro-antd/ng-zorro-antd.min.css",
+              "src/styles.css"
+            ],
+            "scripts": []
+          },
+          "configurations": {
+            "production": {
+              "fileReplacements": [
+                {
+                  "replace": "src/environments/environment.ts",
+                  "with": "src/environments/environment.prod.ts"
+                }
+              ],
+              "optimization": true,
+              "outputHashing": "all",
+              "sourceMap": false,
+              "extractCss": true,
+              "namedChunks": false,
+              "extractLicenses": true,
+              "vendorChunk": false,
+              "buildOptimizer": true,
+              "budgets": [
+                {
+                  "type": "initial",
+                  "maximumWarning": "2mb",
+                  "maximumError": "5mb"
+                },
+                {
+                  "type": "anyComponentStyle",
+                  "maximumWarning": "6kb",
+                  "maximumError": "10kb"
+                }
+              ]
+            }
+          }
+        },
+        "serve": {
+          "builder": "@angular-devkit/build-angular:dev-server",
+          "options": {
+            "browserTarget": "ng-template-generator:build"
+          },
+          "configurations": {
+            "production": {
+              "browserTarget": "ng-template-generator:build:production"
+            }
+          }
+        },
+        "extract-i18n": {
+          "builder": "@angular-devkit/build-angular:extract-i18n",
+          "options": {
+            "browserTarget": "ng-template-generator:build"
+          }
+        },
+        "test": {
+          "builder": "@angular-devkit/build-angular:karma",
+          "options": {
+            "main": "src/test.ts",
+            "polyfills": "src/polyfills.ts",
+            "tsConfig": "tsconfig.spec.json",
+            "karmaConfig": "karma.conf.js",
+            "assets": [
+              "src/favicon.ico",
+              "src/assets"
+            ],
+            "styles": [
+              "./node_modules/ng-zorro-antd/ng-zorro-antd.min.css",
+              "src/styles.css"
+            ],
+            "scripts": []
+          }
+        },
+        "lint": {
+          "builder": "@angular-devkit/build-angular:tslint",
+          "options": {
+            "tsConfig": [
+              "tsconfig.app.json",
+              "tsconfig.spec.json",
+              "e2e/tsconfig.json"
+            ],
+            "exclude": [
+              "**/node_modules/**"
+            ]
+          }
+        },
+        "e2e": {
+          "builder": "@angular-devkit/build-angular:protractor",
+          "options": {
+            "protractorConfig": "e2e/protractor.conf.js",
+            "devServerTarget": "ng-template-generator:serve"
+          },
+          "configurations": {
+            "production": {
+              "devServerTarget": "ng-template-generator:serve:production"
+            }
+          }
+        }
+      }
+    }
+  },
+  "defaultProject": "ng-template-generator"
+}
diff --git a/form/bin/publish.js b/form/bin/publish.js
new file mode 100644
index 0000000000000000000000000000000000000000..6a7490ec42146a10a2ca12acffe1952cce456914
--- /dev/null
+++ b/form/bin/publish.js
@@ -0,0 +1,114 @@
+/****
+ * 批量编译打包模板工具
+ * 运行 npm run publish T_01 命令来打包T_01模板
+ * 运行 npm run publish T_01,T_02,T_03,T_04  命令来分别打包 T_01,T_02,T_03,T_04 这四个模板,注意逗号要用英文的
+ * 运行 npm run publish all 命令来打包所有模板
+ */
+
+const spawn = require('child_process').spawn;
+const path = require("path");
+const fs = require("fs");
+const os = require('os');
+const compressing = require("compressing");
+
+//Linux系统上'Linux'
+//macOS 系统上'Darwin'
+//Windows系统上'Windows_NT'
+let sysType = os.type();
+
+Date.prototype.Format = function(fmt) {
+  var o = {
+      "M+" : this.getMonth() + 1,
+      "d+" : this.getDate(),
+      "h+" : this.getHours(),
+      "m+" : this.getMinutes(),
+      "s+" : this.getSeconds(),
+      "q+" : Math.floor((this.getMonth() + 3) / 3),
+      "S" : this.getMilliseconds()
+  };
+  if (/(y+)/.test(fmt))
+      fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
+  for (var k in o)
+  if (new RegExp("(" + k + ")").test(fmt))
+      fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
+  return fmt;
+}
+
+function clean(zipPath){
+  if(fs.existsSync(zipPath)){
+    fs.unlinkSync(zipPath);
+  }
+}
+
+const runSpawn = async function (){
+
+  await new Promise(function(resolve,reject){
+
+    let pkg = require("../package.json");
+
+    let ls;
+    if(sysType==="Windows_NT"){
+      //ng build --prod --build--optimizer --base-href /ng-one/
+      ls = spawn("cmd.exe", ['/c', 'ng', 'build', '--prod', '--build--optimizer', '--base-href', '/template-base-href/'] );
+    }else{
+      ls = spawn("ng", ['build', '--prod', '--build--optimizer', '--base-href', '/template-base-href/'] );
+    }
+
+    ls.stdout.on('data', (data) => {
+      console.log(` ${data}`);
+    });
+
+    ls.stderr.on('data', (data) => {
+      console.log(`stderr: ${data}`);
+      reject();
+    });
+
+    ls.on('close', (code) => {
+      console.log(`child process exited with code ${code}`);
+      //要压缩的目录
+      let zippath = path.resolve(__dirname,"../dist", pkg.name);
+      //压缩包的存放目录
+      let date = new Date();
+      let zipname = pkg.name+"_"+date.Format("yyyyMMdd hh-mm-ss");
+      let zipdir = path.resolve(__dirname,"../publish/"+zipname+".zip");
+      clean(zipdir); //删除原有的包
+
+      const tarStream = new compressing.zip.Stream();
+      fs.readdir(zippath,function(err,files){
+        if(err){
+          console.log("======文件打开异常======");
+          console.log(err);
+          reject();
+        }
+        for(let i=0;i<files.length;i++){
+          tarStream.addEntry(zippath+"/"+files[i]);
+        }
+        let writeStream = fs.createWriteStream(zipdir);
+        tarStream.pipe(writeStream);
+        writeStream.on('close', () => {
+            console.log(`模板 ${pkg.name} 打包已完成!`);
+            resolve();
+        })
+      });
+
+    });
+
+  });
+}
+
+// let projects = "";
+// if(process.argv.length<3){
+//   console.log("缺少参数");
+//   return;
+// }
+// projects = process.argv[2];
+
+let exec = async function(){
+  //压缩模板
+  await runSpawn();
+}
+
+exec();
+
+
+
diff --git a/form/browserslist b/form/browserslist
new file mode 100644
index 0000000000000000000000000000000000000000..80848532e47d58cc7a4b618f600b438960f9f045
--- /dev/null
+++ b/form/browserslist
@@ -0,0 +1,12 @@
+# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
+# For additional information regarding the format and rule options, please see:
+# https://github.com/browserslist/browserslist#queries
+
+# You can see what browsers were selected by your queries by running:
+#   npx browserslist
+
+> 0.5%
+last 2 versions
+Firefox ESR
+not dead
+not IE 9-11 # For IE 9-11 support, remove 'not'.
\ No newline at end of file
diff --git a/form/e2e/protractor.conf.js b/form/e2e/protractor.conf.js
new file mode 100644
index 0000000000000000000000000000000000000000..73e4e6806cd6b451dc89181ce574c386c6c837da
--- /dev/null
+++ b/form/e2e/protractor.conf.js
@@ -0,0 +1,32 @@
+// @ts-check
+// Protractor configuration file, see link for more information
+// https://github.com/angular/protractor/blob/master/lib/config.ts
+
+const { SpecReporter } = require('jasmine-spec-reporter');
+
+/**
+ * @type { import("protractor").Config }
+ */
+exports.config = {
+  allScriptsTimeout: 11000,
+  specs: [
+    './src/**/*.e2e-spec.ts'
+  ],
+  capabilities: {
+    'browserName': 'chrome'
+  },
+  directConnect: true,
+  baseUrl: 'http://localhost:4200/',
+  framework: 'jasmine',
+  jasmineNodeOpts: {
+    showColors: true,
+    defaultTimeoutInterval: 30000,
+    print: function() {}
+  },
+  onPrepare() {
+    require('ts-node').register({
+      project: require('path').join(__dirname, './tsconfig.json')
+    });
+    jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
+  }
+};
\ No newline at end of file
diff --git a/form/e2e/src/app.e2e-spec.ts b/form/e2e/src/app.e2e-spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..88795455ca1701631269349ec710f974f5246262
--- /dev/null
+++ b/form/e2e/src/app.e2e-spec.ts
@@ -0,0 +1,23 @@
+import { AppPage } from './app.po';
+import { browser, logging } from 'protractor';
+
+describe('workspace-project App', () => {
+  let page: AppPage;
+
+  beforeEach(() => {
+    page = new AppPage();
+  });
+
+  it('should display welcome message', () => {
+    page.navigateTo();
+    expect(page.getTitleText()).toEqual('Welcome to ng-one!');
+  });
+
+  afterEach(async () => {
+    // Assert that there are no errors emitted from the browser
+    const logs = await browser.manage().logs().get(logging.Type.BROWSER);
+    expect(logs).not.toContain(jasmine.objectContaining({
+      level: logging.Level.SEVERE,
+    } as logging.Entry));
+  });
+});
diff --git a/form/e2e/src/app.po.ts b/form/e2e/src/app.po.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5776aa9eb80d8e5ad05ed75ec9a04e7ee5060ba9
--- /dev/null
+++ b/form/e2e/src/app.po.ts
@@ -0,0 +1,11 @@
+import { browser, by, element } from 'protractor';
+
+export class AppPage {
+  navigateTo() {
+    return browser.get(browser.baseUrl) as Promise<any>;
+  }
+
+  getTitleText() {
+    return element(by.css('app-root h1')).getText() as Promise<string>;
+  }
+}
diff --git a/form/e2e/tsconfig.json b/form/e2e/tsconfig.json
new file mode 100644
index 0000000000000000000000000000000000000000..39b800f789619f0fb8063aba2e05bd5a57850e57
--- /dev/null
+++ b/form/e2e/tsconfig.json
@@ -0,0 +1,13 @@
+{
+  "extends": "../tsconfig.json",
+  "compilerOptions": {
+    "outDir": "../out-tsc/e2e",
+    "module": "commonjs",
+    "target": "es5",
+    "types": [
+      "jasmine",
+      "jasminewd2",
+      "node"
+    ]
+  }
+}
diff --git a/form/karma.conf.js b/form/karma.conf.js
new file mode 100644
index 0000000000000000000000000000000000000000..4a9333c426e179201c431f16b2f2ea26cee7fc77
--- /dev/null
+++ b/form/karma.conf.js
@@ -0,0 +1,32 @@
+// Karma configuration file, see link for more information
+// https://karma-runner.github.io/1.0/config/configuration-file.html
+
+module.exports = function (config) {
+  config.set({
+    basePath: '',
+    frameworks: ['jasmine', '@angular-devkit/build-angular'],
+    plugins: [
+      require('karma-jasmine'),
+      require('karma-chrome-launcher'),
+      require('karma-jasmine-html-reporter'),
+      require('karma-coverage-istanbul-reporter'),
+      require('@angular-devkit/build-angular/plugins/karma')
+    ],
+    client: {
+      clearContext: false // leave Jasmine Spec Runner output visible in browser
+    },
+    coverageIstanbulReporter: {
+      dir: require('path').join(__dirname, './coverage/ng-one'),
+      reports: ['html', 'lcovonly', 'text-summary'],
+      fixWebpackSourcePaths: true
+    },
+    reporters: ['progress', 'kjhtml'],
+    port: 9876,
+    colors: true,
+    logLevel: config.LOG_INFO,
+    autoWatch: true,
+    browsers: ['Chrome'],
+    singleRun: false,
+    restartOnFileChange: true
+  });
+};
diff --git a/form/package-lock.json b/form/package-lock.json
new file mode 100644
index 0000000000000000000000000000000000000000..b03c7c395da22a8bfc5e118dd25816b146931901
--- /dev/null
+++ b/form/package-lock.json
@@ -0,0 +1,12897 @@
+{
+  "name": "ng-template-generator",
+  "version": "0.0.1",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "@angular-devkit/architect": {
+      "version": "0.900.7",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.900.7.tgz",
+      "integrity": "sha512-hfiTVYc72kzbXrzK4tea6jnTDnSKpE1D+vEptBXN2tdXEVNEAQI5Qm5L1zVDtt16UdqoUTUypIgUc9jcNH1mUQ==",
+      "dev": true,
+      "requires": {
+        "@angular-devkit/core": "9.0.7",
+        "rxjs": "6.5.3"
+      },
+      "dependencies": {
+        "rxjs": {
+          "version": "6.5.3",
+          "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz",
+          "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==",
+          "dev": true,
+          "requires": {
+            "tslib": "^1.9.0"
+          }
+        }
+      }
+    },
+    "@angular-devkit/build-angular": {
+      "version": "0.900.7",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.900.7.tgz",
+      "integrity": "sha512-Yv2y3OEaYEd0fE0pKvtqBpmkQYs9xJws7thHnJYCwIfYO55RfolYsXkJgAXke/4NPLrD3EsIDqoPxF7l+uw2/Q==",
+      "dev": true,
+      "requires": {
+        "@angular-devkit/architect": "0.900.7",
+        "@angular-devkit/build-optimizer": "0.900.7",
+        "@angular-devkit/build-webpack": "0.900.7",
+        "@angular-devkit/core": "9.0.7",
+        "@babel/core": "7.7.7",
+        "@babel/generator": "7.7.7",
+        "@babel/preset-env": "7.7.7",
+        "@ngtools/webpack": "9.0.7",
+        "ajv": "6.10.2",
+        "autoprefixer": "9.7.1",
+        "babel-loader": "8.0.6",
+        "browserslist": "^4.9.1",
+        "cacache": "13.0.1",
+        "caniuse-lite": "^1.0.30001032",
+        "circular-dependency-plugin": "5.2.0",
+        "copy-webpack-plugin": "5.1.1",
+        "core-js": "3.6.4",
+        "coverage-istanbul-loader": "2.0.3",
+        "cssnano": "4.1.10",
+        "file-loader": "4.2.0",
+        "find-cache-dir": "3.0.0",
+        "glob": "7.1.5",
+        "jest-worker": "24.9.0",
+        "karma-source-map-support": "1.4.0",
+        "less": "3.10.3",
+        "less-loader": "5.0.0",
+        "license-webpack-plugin": "2.1.3",
+        "loader-utils": "1.2.3",
+        "magic-string": "0.25.4",
+        "mini-css-extract-plugin": "0.8.0",
+        "minimatch": "3.0.4",
+        "open": "7.0.0",
+        "parse5": "4.0.0",
+        "postcss": "7.0.21",
+        "postcss-import": "12.0.1",
+        "postcss-loader": "3.0.0",
+        "raw-loader": "3.1.0",
+        "regenerator-runtime": "0.13.3",
+        "rimraf": "3.0.0",
+        "rollup": "1.25.2",
+        "rxjs": "6.5.3",
+        "sass": "1.23.3",
+        "sass-loader": "8.0.0",
+        "semver": "6.3.0",
+        "source-map": "0.7.3",
+        "source-map-loader": "0.2.4",
+        "source-map-support": "0.5.16",
+        "speed-measure-webpack-plugin": "1.3.1",
+        "style-loader": "1.0.0",
+        "stylus": "0.54.7",
+        "stylus-loader": "3.0.2",
+        "terser": "4.5.1",
+        "terser-webpack-plugin": "2.3.3",
+        "tree-kill": "1.2.2",
+        "webpack": "4.41.2",
+        "webpack-dev-middleware": "3.7.2",
+        "webpack-dev-server": "3.9.0",
+        "webpack-merge": "4.2.2",
+        "webpack-sources": "1.4.3",
+        "webpack-subresource-integrity": "1.3.4",
+        "worker-plugin": "3.2.0"
+      },
+      "dependencies": {
+        "glob": {
+          "version": "7.1.5",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz",
+          "integrity": "sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==",
+          "dev": true,
+          "requires": {
+            "fs.realpath": "^1.0.0",
+            "inflight": "^1.0.4",
+            "inherits": "2",
+            "minimatch": "^3.0.4",
+            "once": "^1.3.0",
+            "path-is-absolute": "^1.0.0"
+          }
+        },
+        "parse5": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz",
+          "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==",
+          "dev": true
+        },
+        "rimraf": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz",
+          "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==",
+          "dev": true,
+          "requires": {
+            "glob": "^7.1.3"
+          }
+        },
+        "rxjs": {
+          "version": "6.5.3",
+          "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz",
+          "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==",
+          "dev": true,
+          "requires": {
+            "tslib": "^1.9.0"
+          }
+        },
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+          "dev": true
+        },
+        "source-map": {
+          "version": "0.7.3",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+          "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+          "dev": true
+        }
+      }
+    },
+    "@angular-devkit/build-optimizer": {
+      "version": "0.900.7",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.900.7.tgz",
+      "integrity": "sha512-gxin2oPNMN+PYo82At2JP1Q+uxnvwyDFWA1Wl+Ufuc5zHGhjKqxdQjkdMF7OT0ihtmkllN+t/NTB7rcx/Sx9Wg==",
+      "dev": true,
+      "requires": {
+        "loader-utils": "1.2.3",
+        "source-map": "0.7.3",
+        "tslib": "1.10.0",
+        "typescript": "3.6.4",
+        "webpack-sources": "1.4.3"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.7.3",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+          "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+          "dev": true
+        },
+        "tslib": {
+          "version": "1.10.0",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
+          "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==",
+          "dev": true
+        },
+        "typescript": {
+          "version": "3.6.4",
+          "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz",
+          "integrity": "sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==",
+          "dev": true
+        }
+      }
+    },
+    "@angular-devkit/build-webpack": {
+      "version": "0.900.7",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.900.7.tgz",
+      "integrity": "sha512-Nwwqjo1ZpHFLavN+nXOmuBgGjhoMBZGelDCvHtiQlQ9N6i7k9cKnP7eU5pY7jbalBguS+gWg5wJIGnbqk1K9Rg==",
+      "dev": true,
+      "requires": {
+        "@angular-devkit/architect": "0.900.7",
+        "@angular-devkit/core": "9.0.7",
+        "rxjs": "6.5.3"
+      },
+      "dependencies": {
+        "rxjs": {
+          "version": "6.5.3",
+          "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz",
+          "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==",
+          "dev": true,
+          "requires": {
+            "tslib": "^1.9.0"
+          }
+        }
+      }
+    },
+    "@angular-devkit/core": {
+      "version": "9.0.7",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-9.0.7.tgz",
+      "integrity": "sha512-tMrz36sM1xrwvFf9Qm59GwALscVlMP7rQBjtd0fIR/QbsiOAIX4AQbV+vN6Vtwnzo5NIRZY1IXJUhesWms+h5w==",
+      "dev": true,
+      "requires": {
+        "ajv": "6.10.2",
+        "fast-json-stable-stringify": "2.0.0",
+        "magic-string": "0.25.4",
+        "rxjs": "6.5.3",
+        "source-map": "0.7.3"
+      },
+      "dependencies": {
+        "rxjs": {
+          "version": "6.5.3",
+          "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz",
+          "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==",
+          "dev": true,
+          "requires": {
+            "tslib": "^1.9.0"
+          }
+        },
+        "source-map": {
+          "version": "0.7.3",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+          "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+          "dev": true
+        }
+      }
+    },
+    "@angular-devkit/schematics": {
+      "version": "9.0.7",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-9.0.7.tgz",
+      "integrity": "sha512-ryPC+l24f3gX5DFMTLkDM/q2Kp6LPzBn6400k7j4qVdb1cIrZx+JUQd7F4iAksTTkX15EQPanptQXeztUrl9Ng==",
+      "dev": true,
+      "requires": {
+        "@angular-devkit/core": "9.0.7",
+        "ora": "4.0.2",
+        "rxjs": "6.5.3"
+      },
+      "dependencies": {
+        "rxjs": {
+          "version": "6.5.3",
+          "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz",
+          "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==",
+          "dev": true,
+          "requires": {
+            "tslib": "^1.9.0"
+          }
+        }
+      }
+    },
+    "@angular/animations": {
+      "version": "9.0.7",
+      "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-9.0.7.tgz",
+      "integrity": "sha512-74gY7onajmmnksy5E0/32bFv3B9NuWxV64kqD15YjGrh8AWe1BHt5enQI+rJ2tO8m2DKnwZsctis6k0Kcy+YKQ=="
+    },
+    "@angular/cdk": {
+      "version": "8.2.3",
+      "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-8.2.3.tgz",
+      "integrity": "sha512-ZwO5Sn720RA2YvBqud0JAHkZXjmjxM0yNzCO8RVtRE9i8Gl26Wk0j0nQeJkVm4zwv2QO8MwbKUKGTMt8evsokA==",
+      "requires": {
+        "parse5": "^5.0.0",
+        "tslib": "^1.7.1"
+      }
+    },
+    "@angular/cli": {
+      "version": "9.0.7",
+      "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-9.0.7.tgz",
+      "integrity": "sha512-/9CUNSSVyTtTNUADZ/VXJDEdhineMN/rfd35w6VsHiob49tKkeOTggaoiSne3RY4VCTqlo7GGf4KhhVXEMGnDQ==",
+      "dev": true,
+      "requires": {
+        "@angular-devkit/architect": "0.900.7",
+        "@angular-devkit/core": "9.0.7",
+        "@angular-devkit/schematics": "9.0.7",
+        "@schematics/angular": "9.0.7",
+        "@schematics/update": "0.900.7",
+        "@yarnpkg/lockfile": "1.1.0",
+        "ansi-colors": "4.1.1",
+        "debug": "^4.1.1",
+        "ini": "1.3.5",
+        "inquirer": "7.0.0",
+        "npm-package-arg": "6.1.1",
+        "npm-pick-manifest": "3.0.2",
+        "open": "7.0.0",
+        "pacote": "9.5.8",
+        "read-package-tree": "5.3.1",
+        "rimraf": "3.0.0",
+        "semver": "6.3.0",
+        "symbol-observable": "1.2.0",
+        "universal-analytics": "^0.4.20",
+        "uuid": "^3.3.2"
+      },
+      "dependencies": {
+        "ansi-colors": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+          "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+          "dev": true
+        },
+        "debug": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+          "dev": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "rimraf": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz",
+          "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==",
+          "dev": true,
+          "requires": {
+            "glob": "^7.1.3"
+          }
+        },
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+          "dev": true
+        }
+      }
+    },
+    "@angular/common": {
+      "version": "9.0.7",
+      "resolved": "https://registry.npmjs.org/@angular/common/-/common-9.0.7.tgz",
+      "integrity": "sha512-B58YgxZva1DBaeayOBsaUOOkoyR+GRibuNC3gfOMm2vXeW9eCNX+jvDtw767GnKm2yGzIq8wB3x6GHojN00dPw=="
+    },
+    "@angular/compiler": {
+      "version": "9.0.7",
+      "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-9.0.7.tgz",
+      "integrity": "sha512-hFpkuGpzxpK5h59LHHAjTFWsY6DCXZwgJFqvCuTPxWi/srvLGZRXrpC6Z1SlgHI9xxXaPfoa4uWw2VfA3BnqEg=="
+    },
+    "@angular/compiler-cli": {
+      "version": "9.0.7",
+      "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-9.0.7.tgz",
+      "integrity": "sha512-+RXghex63v0Vi8vpQtDpWiqpAAnrTaN3bHT5fntRenq5+Ok5vL1MJ1mzbTmBXs2tuwTqNlwMm2AlZB7G/xcDMQ==",
+      "dev": true,
+      "requires": {
+        "canonical-path": "1.0.0",
+        "chokidar": "^3.0.0",
+        "convert-source-map": "^1.5.1",
+        "dependency-graph": "^0.7.2",
+        "fs-extra": "4.0.2",
+        "magic-string": "^0.25.0",
+        "minimist": "^1.2.0",
+        "reflect-metadata": "^0.1.2",
+        "semver": "^6.3.0",
+        "source-map": "^0.6.1",
+        "sourcemap-codec": "^1.4.8",
+        "yargs": "13.1.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+          "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+          "dev": true
+        },
+        "emoji-regex": {
+          "version": "7.0.3",
+          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+          "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+          "dev": true
+        },
+        "get-caller-file": {
+          "version": "2.0.5",
+          "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+          "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+          "dev": true
+        },
+        "require-main-filename": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+          "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+          "dev": true
+        },
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+          "dev": true
+        },
+        "string-width": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+          "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+          "dev": true,
+          "requires": {
+            "emoji-regex": "^7.0.1",
+            "is-fullwidth-code-point": "^2.0.0",
+            "strip-ansi": "^5.1.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+          "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^4.1.0"
+          }
+        },
+        "yargs": {
+          "version": "13.1.0",
+          "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.1.0.tgz",
+          "integrity": "sha512-1UhJbXfzHiPqkfXNHYhiz79qM/kZqjTE8yGlEjZa85Q+3+OwcV6NRkV7XOV1W2Eom2bzILeUn55pQYffjVOLAg==",
+          "dev": true,
+          "requires": {
+            "cliui": "^4.0.0",
+            "find-up": "^3.0.0",
+            "get-caller-file": "^2.0.1",
+            "os-locale": "^3.1.0",
+            "require-directory": "^2.1.1",
+            "require-main-filename": "^2.0.0",
+            "set-blocking": "^2.0.0",
+            "string-width": "^3.0.0",
+            "which-module": "^2.0.0",
+            "y18n": "^4.0.0",
+            "yargs-parser": "^13.0.0"
+          }
+        },
+        "yargs-parser": {
+          "version": "13.1.2",
+          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+          "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+          "dev": true,
+          "requires": {
+            "camelcase": "^5.0.0",
+            "decamelize": "^1.2.0"
+          }
+        }
+      }
+    },
+    "@angular/core": {
+      "version": "9.0.7",
+      "resolved": "https://registry.npmjs.org/@angular/core/-/core-9.0.7.tgz",
+      "integrity": "sha512-E9XZH5Dl+9MWG3MDC6wrKllhA8Rljpz66HOIeqKv2fHPed8kzuJZU3WJWLtbhDAXFwtGTyTZ4c82ZLSmqwTorg=="
+    },
+    "@angular/forms": {
+      "version": "9.0.7",
+      "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-9.0.7.tgz",
+      "integrity": "sha512-PaHAmjMJDtg/3aGCPuq5BCRC1eZ/DBCpva9f7NrA1kqk0LcLdebm0v2uHwTOBtiz/VEgPvxiS4tXC4rjvUtfEg=="
+    },
+    "@angular/language-service": {
+      "version": "9.0.7",
+      "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-9.0.7.tgz",
+      "integrity": "sha512-IZG1kvw48JyFRy7bfMHqBixWrEHZmXmkP5DWsi5Tw6KusaczkMghI20BevCkodPcajXWHAUHNKyp1tlE3OnH0w==",
+      "dev": true
+    },
+    "@angular/platform-browser": {
+      "version": "9.0.7",
+      "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-9.0.7.tgz",
+      "integrity": "sha512-Por8omrEiSV2U/K2mm/Kuv+2R2rJkbAZ3ctEM6CWj9Y4Gz2akjOCxmEgWhhBeqdigcC3T1v707f52osf9jWBkg=="
+    },
+    "@angular/platform-browser-dynamic": {
+      "version": "9.0.7",
+      "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-9.0.7.tgz",
+      "integrity": "sha512-jwpyd93ofcRtchbayKD5v4GN4Lc7vbPe6dMUiwfnVnVAql0bOD/3YRI7w5qJ0Xx0sgQT+9Xo6jTXYnyUsZpEww=="
+    },
+    "@angular/router": {
+      "version": "9.0.7",
+      "resolved": "https://registry.npmjs.org/@angular/router/-/router-9.0.7.tgz",
+      "integrity": "sha512-uKru9F/Zju//gg6INl54abnlpLdEUUO/GpCfMk4zqu8LCZGNFta6OY7VT+9DK9Vdrh/XUD70oE9WoelcRwwTYA=="
+    },
+    "@ant-design/colors": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-3.2.2.tgz",
+      "integrity": "sha512-YKgNbG2dlzqMhA9NtI3/pbY16m3Yl/EeWBRa+lB1X1YaYxHrxNexiQYCLTWO/uDvAjLFMEDU+zR901waBtMtjQ==",
+      "requires": {
+        "tinycolor2": "^1.4.1"
+      }
+    },
+    "@ant-design/icons-angular": {
+      "version": "8.1.1",
+      "resolved": "https://registry.npmjs.org/@ant-design/icons-angular/-/icons-angular-8.1.1.tgz",
+      "integrity": "sha512-JMfavPHwahJcGWT13bTCt4IHzrsNgbJzzB+VWYtzjwWszMCVkkOOn+aJbslupHOl72KWDvklN/LjCQPBgu7xLQ==",
+      "requires": {
+        "@ant-design/colors": "^3.1.0",
+        "tslib": "^1.9.0"
+      }
+    },
+    "@babel/code-frame": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+      "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+      "dev": true,
+      "requires": {
+        "@babel/highlight": "^7.10.4"
+      }
+    },
+    "@babel/core": {
+      "version": "7.7.7",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.7.tgz",
+      "integrity": "sha512-jlSjuj/7z138NLZALxVgrx13AOtqip42ATZP7+kYl53GvDV6+4dCek1mVUo8z8c8Xnw/mx2q3d9HWh3griuesQ==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.5.5",
+        "@babel/generator": "^7.7.7",
+        "@babel/helpers": "^7.7.4",
+        "@babel/parser": "^7.7.7",
+        "@babel/template": "^7.7.4",
+        "@babel/traverse": "^7.7.4",
+        "@babel/types": "^7.7.4",
+        "convert-source-map": "^1.7.0",
+        "debug": "^4.1.0",
+        "json5": "^2.1.0",
+        "lodash": "^4.17.13",
+        "resolve": "^1.3.2",
+        "semver": "^5.4.1",
+        "source-map": "^0.5.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+          "dev": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "json5": {
+          "version": "2.1.3",
+          "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
+          "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
+          "dev": true,
+          "requires": {
+            "minimist": "^1.2.5"
+          }
+        },
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+          "dev": true
+        }
+      }
+    },
+    "@babel/generator": {
+      "version": "7.7.7",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.7.tgz",
+      "integrity": "sha512-/AOIBpHh/JU1l0ZFS4kiRCBnLi6OTHzh0RPk3h9isBxkkqELtQNFi1Vr/tiG9p1yfoUdKVwISuXWQR+hwwM4VQ==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.7.4",
+        "jsesc": "^2.5.1",
+        "lodash": "^4.17.13",
+        "source-map": "^0.5.0"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+          "dev": true
+        }
+      }
+    },
+    "@babel/helper-annotate-as-pure": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz",
+      "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.10.4"
+      }
+    },
+    "@babel/helper-builder-binary-assignment-operator-visitor": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz",
+      "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-explode-assignable-expression": "^7.10.4",
+        "@babel/types": "^7.10.4"
+      }
+    },
+    "@babel/helper-create-regexp-features-plugin": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz",
+      "integrity": "sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-annotate-as-pure": "^7.10.4",
+        "@babel/helper-regex": "^7.10.4",
+        "regexpu-core": "^4.7.0"
+      }
+    },
+    "@babel/helper-define-map": {
+      "version": "7.10.5",
+      "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz",
+      "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-function-name": "^7.10.4",
+        "@babel/types": "^7.10.5",
+        "lodash": "^4.17.19"
+      }
+    },
+    "@babel/helper-explode-assignable-expression": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.4.tgz",
+      "integrity": "sha512-4K71RyRQNPRrR85sr5QY4X3VwG4wtVoXZB9+L3r1Gp38DhELyHCtovqydRi7c1Ovb17eRGiQ/FD5s8JdU0Uy5A==",
+      "dev": true,
+      "requires": {
+        "@babel/traverse": "^7.10.4",
+        "@babel/types": "^7.10.4"
+      }
+    },
+    "@babel/helper-function-name": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz",
+      "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-get-function-arity": "^7.10.4",
+        "@babel/template": "^7.10.4",
+        "@babel/types": "^7.10.4"
+      }
+    },
+    "@babel/helper-get-function-arity": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz",
+      "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.10.4"
+      }
+    },
+    "@babel/helper-hoist-variables": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz",
+      "integrity": "sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.10.4"
+      }
+    },
+    "@babel/helper-member-expression-to-functions": {
+      "version": "7.11.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz",
+      "integrity": "sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.11.0"
+      }
+    },
+    "@babel/helper-module-imports": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz",
+      "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.10.4"
+      }
+    },
+    "@babel/helper-module-transforms": {
+      "version": "7.11.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz",
+      "integrity": "sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-module-imports": "^7.10.4",
+        "@babel/helper-replace-supers": "^7.10.4",
+        "@babel/helper-simple-access": "^7.10.4",
+        "@babel/helper-split-export-declaration": "^7.11.0",
+        "@babel/template": "^7.10.4",
+        "@babel/types": "^7.11.0",
+        "lodash": "^4.17.19"
+      }
+    },
+    "@babel/helper-optimise-call-expression": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz",
+      "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.10.4"
+      }
+    },
+    "@babel/helper-plugin-utils": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz",
+      "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==",
+      "dev": true
+    },
+    "@babel/helper-regex": {
+      "version": "7.10.5",
+      "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.5.tgz",
+      "integrity": "sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg==",
+      "dev": true,
+      "requires": {
+        "lodash": "^4.17.19"
+      }
+    },
+    "@babel/helper-remap-async-to-generator": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.4.tgz",
+      "integrity": "sha512-86Lsr6NNw3qTNl+TBcF1oRZMaVzJtbWTyTko+CQL/tvNvcGYEFKbLXDPxtW0HKk3McNOk4KzY55itGWCAGK5tg==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-annotate-as-pure": "^7.10.4",
+        "@babel/helper-wrap-function": "^7.10.4",
+        "@babel/template": "^7.10.4",
+        "@babel/traverse": "^7.10.4",
+        "@babel/types": "^7.10.4"
+      }
+    },
+    "@babel/helper-replace-supers": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz",
+      "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-member-expression-to-functions": "^7.10.4",
+        "@babel/helper-optimise-call-expression": "^7.10.4",
+        "@babel/traverse": "^7.10.4",
+        "@babel/types": "^7.10.4"
+      }
+    },
+    "@babel/helper-simple-access": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz",
+      "integrity": "sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==",
+      "dev": true,
+      "requires": {
+        "@babel/template": "^7.10.4",
+        "@babel/types": "^7.10.4"
+      }
+    },
+    "@babel/helper-skip-transparent-expression-wrappers": {
+      "version": "7.11.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz",
+      "integrity": "sha512-0XIdiQln4Elglgjbwo9wuJpL/K7AGCY26kmEt0+pRP0TAj4jjyNq1MjoRvikrTVqKcx4Gysxt4cXvVFXP/JO2Q==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.11.0"
+      }
+    },
+    "@babel/helper-split-export-declaration": {
+      "version": "7.11.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz",
+      "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.11.0"
+      }
+    },
+    "@babel/helper-validator-identifier": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+      "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+      "dev": true
+    },
+    "@babel/helper-wrap-function": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz",
+      "integrity": "sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-function-name": "^7.10.4",
+        "@babel/template": "^7.10.4",
+        "@babel/traverse": "^7.10.4",
+        "@babel/types": "^7.10.4"
+      }
+    },
+    "@babel/helpers": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.4.tgz",
+      "integrity": "sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==",
+      "dev": true,
+      "requires": {
+        "@babel/template": "^7.10.4",
+        "@babel/traverse": "^7.10.4",
+        "@babel/types": "^7.10.4"
+      }
+    },
+    "@babel/highlight": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
+      "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-validator-identifier": "^7.10.4",
+        "chalk": "^2.0.0",
+        "js-tokens": "^4.0.0"
+      }
+    },
+    "@babel/parser": {
+      "version": "7.11.0",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.0.tgz",
+      "integrity": "sha512-qvRvi4oI8xii8NllyEc4MDJjuZiNaRzyb7Y7lup1NqJV8TZHF4O27CcP+72WPn/k1zkgJ6WJfnIbk4jTsVAZHw==",
+      "dev": true
+    },
+    "@babel/plugin-proposal-async-generator-functions": {
+      "version": "7.10.5",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz",
+      "integrity": "sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4",
+        "@babel/helper-remap-async-to-generator": "^7.10.4",
+        "@babel/plugin-syntax-async-generators": "^7.8.0"
+      }
+    },
+    "@babel/plugin-proposal-dynamic-import": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz",
+      "integrity": "sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4",
+        "@babel/plugin-syntax-dynamic-import": "^7.8.0"
+      }
+    },
+    "@babel/plugin-proposal-json-strings": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz",
+      "integrity": "sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4",
+        "@babel/plugin-syntax-json-strings": "^7.8.0"
+      }
+    },
+    "@babel/plugin-proposal-object-rest-spread": {
+      "version": "7.11.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.11.0.tgz",
+      "integrity": "sha512-wzch41N4yztwoRw0ak+37wxwJM2oiIiy6huGCoqkvSTA9acYWcPfn9Y4aJqmFFJ70KTJUu29f3DQ43uJ9HXzEA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4",
+        "@babel/plugin-syntax-object-rest-spread": "^7.8.0",
+        "@babel/plugin-transform-parameters": "^7.10.4"
+      }
+    },
+    "@babel/plugin-proposal-optional-catch-binding": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz",
+      "integrity": "sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4",
+        "@babel/plugin-syntax-optional-catch-binding": "^7.8.0"
+      }
+    },
+    "@babel/plugin-proposal-unicode-property-regex": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz",
+      "integrity": "sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-create-regexp-features-plugin": "^7.10.4",
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-syntax-async-generators": {
+      "version": "7.8.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
+      "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      }
+    },
+    "@babel/plugin-syntax-dynamic-import": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
+      "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      }
+    },
+    "@babel/plugin-syntax-json-strings": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
+      "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      }
+    },
+    "@babel/plugin-syntax-object-rest-spread": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
+      "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      }
+    },
+    "@babel/plugin-syntax-optional-catch-binding": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
+      "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      }
+    },
+    "@babel/plugin-syntax-top-level-await": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz",
+      "integrity": "sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-transform-arrow-functions": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz",
+      "integrity": "sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-transform-async-to-generator": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz",
+      "integrity": "sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-module-imports": "^7.10.4",
+        "@babel/helper-plugin-utils": "^7.10.4",
+        "@babel/helper-remap-async-to-generator": "^7.10.4"
+      }
+    },
+    "@babel/plugin-transform-block-scoped-functions": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz",
+      "integrity": "sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-transform-block-scoping": {
+      "version": "7.10.5",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.10.5.tgz",
+      "integrity": "sha512-6Ycw3hjpQti0qssQcA6AMSFDHeNJ++R6dIMnpRqUjFeBBTmTDPa8zgF90OVfTvAo11mXZTlVUViY1g8ffrURLg==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-transform-classes": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz",
+      "integrity": "sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-annotate-as-pure": "^7.10.4",
+        "@babel/helper-define-map": "^7.10.4",
+        "@babel/helper-function-name": "^7.10.4",
+        "@babel/helper-optimise-call-expression": "^7.10.4",
+        "@babel/helper-plugin-utils": "^7.10.4",
+        "@babel/helper-replace-supers": "^7.10.4",
+        "@babel/helper-split-export-declaration": "^7.10.4",
+        "globals": "^11.1.0"
+      }
+    },
+    "@babel/plugin-transform-computed-properties": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz",
+      "integrity": "sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-transform-destructuring": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz",
+      "integrity": "sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-transform-dotall-regex": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz",
+      "integrity": "sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-create-regexp-features-plugin": "^7.10.4",
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-transform-duplicate-keys": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz",
+      "integrity": "sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-transform-exponentiation-operator": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz",
+      "integrity": "sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4",
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-transform-for-of": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz",
+      "integrity": "sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-transform-function-name": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz",
+      "integrity": "sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-function-name": "^7.10.4",
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-transform-literals": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz",
+      "integrity": "sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-transform-member-expression-literals": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz",
+      "integrity": "sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-transform-modules-amd": {
+      "version": "7.10.5",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz",
+      "integrity": "sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-module-transforms": "^7.10.5",
+        "@babel/helper-plugin-utils": "^7.10.4",
+        "babel-plugin-dynamic-import-node": "^2.3.3"
+      }
+    },
+    "@babel/plugin-transform-modules-commonjs": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz",
+      "integrity": "sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-module-transforms": "^7.10.4",
+        "@babel/helper-plugin-utils": "^7.10.4",
+        "@babel/helper-simple-access": "^7.10.4",
+        "babel-plugin-dynamic-import-node": "^2.3.3"
+      }
+    },
+    "@babel/plugin-transform-modules-systemjs": {
+      "version": "7.10.5",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz",
+      "integrity": "sha512-f4RLO/OL14/FP1AEbcsWMzpbUz6tssRaeQg11RH1BP/XnPpRoVwgeYViMFacnkaw4k4wjRSjn3ip1Uw9TaXuMw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-hoist-variables": "^7.10.4",
+        "@babel/helper-module-transforms": "^7.10.5",
+        "@babel/helper-plugin-utils": "^7.10.4",
+        "babel-plugin-dynamic-import-node": "^2.3.3"
+      }
+    },
+    "@babel/plugin-transform-modules-umd": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz",
+      "integrity": "sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-module-transforms": "^7.10.4",
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-transform-named-capturing-groups-regex": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz",
+      "integrity": "sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-create-regexp-features-plugin": "^7.10.4"
+      }
+    },
+    "@babel/plugin-transform-new-target": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz",
+      "integrity": "sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-transform-object-super": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz",
+      "integrity": "sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4",
+        "@babel/helper-replace-supers": "^7.10.4"
+      }
+    },
+    "@babel/plugin-transform-parameters": {
+      "version": "7.10.5",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz",
+      "integrity": "sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-get-function-arity": "^7.10.4",
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-transform-property-literals": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz",
+      "integrity": "sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-transform-regenerator": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz",
+      "integrity": "sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw==",
+      "dev": true,
+      "requires": {
+        "regenerator-transform": "^0.14.2"
+      }
+    },
+    "@babel/plugin-transform-reserved-words": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz",
+      "integrity": "sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-transform-shorthand-properties": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz",
+      "integrity": "sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-transform-spread": {
+      "version": "7.11.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.11.0.tgz",
+      "integrity": "sha512-UwQYGOqIdQJe4aWNyS7noqAnN2VbaczPLiEtln+zPowRNlD+79w3oi2TWfYe0eZgd+gjZCbsydN7lzWysDt+gw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4",
+        "@babel/helper-skip-transparent-expression-wrappers": "^7.11.0"
+      }
+    },
+    "@babel/plugin-transform-sticky-regex": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz",
+      "integrity": "sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4",
+        "@babel/helper-regex": "^7.10.4"
+      }
+    },
+    "@babel/plugin-transform-template-literals": {
+      "version": "7.10.5",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz",
+      "integrity": "sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-annotate-as-pure": "^7.10.4",
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-transform-typeof-symbol": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz",
+      "integrity": "sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-transform-unicode-regex": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz",
+      "integrity": "sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-create-regexp-features-plugin": "^7.10.4",
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/preset-env": {
+      "version": "7.7.7",
+      "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.7.7.tgz",
+      "integrity": "sha512-pCu0hrSSDVI7kCVUOdcMNQEbOPJ52E+LrQ14sN8uL2ALfSqePZQlKrOy+tM4uhEdYlCHi4imr8Zz2cZe9oSdIg==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-module-imports": "^7.7.4",
+        "@babel/helper-plugin-utils": "^7.0.0",
+        "@babel/plugin-proposal-async-generator-functions": "^7.7.4",
+        "@babel/plugin-proposal-dynamic-import": "^7.7.4",
+        "@babel/plugin-proposal-json-strings": "^7.7.4",
+        "@babel/plugin-proposal-object-rest-spread": "^7.7.7",
+        "@babel/plugin-proposal-optional-catch-binding": "^7.7.4",
+        "@babel/plugin-proposal-unicode-property-regex": "^7.7.7",
+        "@babel/plugin-syntax-async-generators": "^7.7.4",
+        "@babel/plugin-syntax-dynamic-import": "^7.7.4",
+        "@babel/plugin-syntax-json-strings": "^7.7.4",
+        "@babel/plugin-syntax-object-rest-spread": "^7.7.4",
+        "@babel/plugin-syntax-optional-catch-binding": "^7.7.4",
+        "@babel/plugin-syntax-top-level-await": "^7.7.4",
+        "@babel/plugin-transform-arrow-functions": "^7.7.4",
+        "@babel/plugin-transform-async-to-generator": "^7.7.4",
+        "@babel/plugin-transform-block-scoped-functions": "^7.7.4",
+        "@babel/plugin-transform-block-scoping": "^7.7.4",
+        "@babel/plugin-transform-classes": "^7.7.4",
+        "@babel/plugin-transform-computed-properties": "^7.7.4",
+        "@babel/plugin-transform-destructuring": "^7.7.4",
+        "@babel/plugin-transform-dotall-regex": "^7.7.7",
+        "@babel/plugin-transform-duplicate-keys": "^7.7.4",
+        "@babel/plugin-transform-exponentiation-operator": "^7.7.4",
+        "@babel/plugin-transform-for-of": "^7.7.4",
+        "@babel/plugin-transform-function-name": "^7.7.4",
+        "@babel/plugin-transform-literals": "^7.7.4",
+        "@babel/plugin-transform-member-expression-literals": "^7.7.4",
+        "@babel/plugin-transform-modules-amd": "^7.7.5",
+        "@babel/plugin-transform-modules-commonjs": "^7.7.5",
+        "@babel/plugin-transform-modules-systemjs": "^7.7.4",
+        "@babel/plugin-transform-modules-umd": "^7.7.4",
+        "@babel/plugin-transform-named-capturing-groups-regex": "^7.7.4",
+        "@babel/plugin-transform-new-target": "^7.7.4",
+        "@babel/plugin-transform-object-super": "^7.7.4",
+        "@babel/plugin-transform-parameters": "^7.7.7",
+        "@babel/plugin-transform-property-literals": "^7.7.4",
+        "@babel/plugin-transform-regenerator": "^7.7.5",
+        "@babel/plugin-transform-reserved-words": "^7.7.4",
+        "@babel/plugin-transform-shorthand-properties": "^7.7.4",
+        "@babel/plugin-transform-spread": "^7.7.4",
+        "@babel/plugin-transform-sticky-regex": "^7.7.4",
+        "@babel/plugin-transform-template-literals": "^7.7.4",
+        "@babel/plugin-transform-typeof-symbol": "^7.7.4",
+        "@babel/plugin-transform-unicode-regex": "^7.7.4",
+        "@babel/types": "^7.7.4",
+        "browserslist": "^4.6.0",
+        "core-js-compat": "^3.6.0",
+        "invariant": "^2.2.2",
+        "js-levenshtein": "^1.1.3",
+        "semver": "^5.5.0"
+      }
+    },
+    "@babel/runtime": {
+      "version": "7.11.0",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.0.tgz",
+      "integrity": "sha512-qArkXsjJq7H+T86WrIFV0Fnu/tNOkZ4cgXmjkzAu3b/58D5mFIO8JH/y77t7C9q0OdDRdh9s7Ue5GasYssxtXw==",
+      "dev": true,
+      "requires": {
+        "regenerator-runtime": "^0.13.4"
+      },
+      "dependencies": {
+        "regenerator-runtime": {
+          "version": "0.13.7",
+          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
+          "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==",
+          "dev": true
+        }
+      }
+    },
+    "@babel/template": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz",
+      "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.10.4",
+        "@babel/parser": "^7.10.4",
+        "@babel/types": "^7.10.4"
+      }
+    },
+    "@babel/traverse": {
+      "version": "7.11.0",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz",
+      "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.10.4",
+        "@babel/generator": "^7.11.0",
+        "@babel/helper-function-name": "^7.10.4",
+        "@babel/helper-split-export-declaration": "^7.11.0",
+        "@babel/parser": "^7.11.0",
+        "@babel/types": "^7.11.0",
+        "debug": "^4.1.0",
+        "globals": "^11.1.0",
+        "lodash": "^4.17.19"
+      },
+      "dependencies": {
+        "@babel/generator": {
+          "version": "7.11.0",
+          "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.0.tgz",
+          "integrity": "sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ==",
+          "dev": true,
+          "requires": {
+            "@babel/types": "^7.11.0",
+            "jsesc": "^2.5.1",
+            "source-map": "^0.5.0"
+          }
+        },
+        "debug": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+          "dev": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+          "dev": true
+        }
+      }
+    },
+    "@babel/types": {
+      "version": "7.11.0",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
+      "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-validator-identifier": "^7.10.4",
+        "lodash": "^4.17.19",
+        "to-fast-properties": "^2.0.0"
+      }
+    },
+    "@fortawesome/angular-fontawesome": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.6.1.tgz",
+      "integrity": "sha512-ARQjtRuT+ZskzJDJKPwuiGO3+7nS0iyNLU/uHVJHfG4LwGJxwVIGldwg1SU957sra0Z0OtWEajHMhiS4vB9LwQ=="
+    },
+    "@fortawesome/fontawesome-common-types": {
+      "version": "0.2.30",
+      "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.30.tgz",
+      "integrity": "sha512-TsRwpTuKwFNiPhk1UfKgw7zNPeV5RhNp2Uw3pws+9gDAkPGKrtjR1y2lI3SYn7+YzyfuNknflpBA1LRKjt7hMg=="
+    },
+    "@fortawesome/fontawesome-svg-core": {
+      "version": "1.2.30",
+      "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.30.tgz",
+      "integrity": "sha512-E3sAXATKCSVnT17HYmZjjbcmwihrNOCkoU7dVMlasrcwiJAHxSKeZ+4WN5O+ElgO/FaYgJmASl8p9N7/B/RttA==",
+      "requires": {
+        "@fortawesome/fontawesome-common-types": "^0.2.30"
+      }
+    },
+    "@fortawesome/free-regular-svg-icons": {
+      "version": "5.14.0",
+      "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.14.0.tgz",
+      "integrity": "sha512-6LCFvjGSMPoUQbn3NVlgiG4CY5iIY8fOm+to/D6QS/GvdqhDt+xZklQeERdCvVRbnFa1ITc1rJHPRXqkX5wztQ==",
+      "requires": {
+        "@fortawesome/fontawesome-common-types": "^0.2.30"
+      }
+    },
+    "@fortawesome/free-solid-svg-icons": {
+      "version": "5.14.0",
+      "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.14.0.tgz",
+      "integrity": "sha512-M933RDM8cecaKMWDSk3FRYdnzWGW7kBBlGNGfvqLVwcwhUPNj9gcw+xZMrqBdRqxnSXdl3zWzTCNNGEtFUq67Q==",
+      "requires": {
+        "@fortawesome/fontawesome-common-types": "^0.2.30"
+      }
+    },
+    "@istanbuljs/schema": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz",
+      "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==",
+      "dev": true
+    },
+    "@ngtools/webpack": {
+      "version": "9.0.7",
+      "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-9.0.7.tgz",
+      "integrity": "sha512-MvoMaErkjESefoIrbt8F2RpKDr9KavwvH4v3hwSAKooVNFdFKNsjJ7m3gCQehumEfsYFq2mrEK2sTW4/CpFlMQ==",
+      "dev": true,
+      "requires": {
+        "@angular-devkit/core": "9.0.7",
+        "enhanced-resolve": "4.1.1",
+        "rxjs": "6.5.3",
+        "webpack-sources": "1.4.3"
+      },
+      "dependencies": {
+        "rxjs": {
+          "version": "6.5.3",
+          "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz",
+          "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==",
+          "dev": true,
+          "requires": {
+            "tslib": "^1.9.0"
+          }
+        }
+      }
+    },
+    "@schematics/angular": {
+      "version": "9.0.7",
+      "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-9.0.7.tgz",
+      "integrity": "sha512-3UCeexYx/YVo3kboyPZ8KgqBTduMA18AAm3s2yrC0qj41fBFVVZAZLa74uouTf4RYVgy9kR7J3uv6VLxrJPOnQ==",
+      "dev": true,
+      "requires": {
+        "@angular-devkit/core": "9.0.7",
+        "@angular-devkit/schematics": "9.0.7"
+      }
+    },
+    "@schematics/update": {
+      "version": "0.900.7",
+      "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.900.7.tgz",
+      "integrity": "sha512-e9tX2DGNYfj/k9mVICpQt2bWIYyD92dlsip7LzPeZGt+R9zCp5w19uBLa8Z00OgEGzFR1krhRvkQE5OxkkAnVw==",
+      "dev": true,
+      "requires": {
+        "@angular-devkit/core": "9.0.7",
+        "@angular-devkit/schematics": "9.0.7",
+        "@yarnpkg/lockfile": "1.1.0",
+        "ini": "1.3.5",
+        "npm-package-arg": "^7.0.0",
+        "pacote": "9.5.8",
+        "rxjs": "6.5.3",
+        "semver": "6.3.0",
+        "semver-intersect": "1.4.0"
+      },
+      "dependencies": {
+        "npm-package-arg": {
+          "version": "7.0.0",
+          "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-7.0.0.tgz",
+          "integrity": "sha512-xXxr8y5U0kl8dVkz2oK7yZjPBvqM2fwaO5l3Yg13p03v8+E3qQcD0JNhHzjL1vyGgxcKkD0cco+NLR72iuPk3g==",
+          "dev": true,
+          "requires": {
+            "hosted-git-info": "^3.0.2",
+            "osenv": "^0.1.5",
+            "semver": "^5.6.0",
+            "validate-npm-package-name": "^3.0.0"
+          },
+          "dependencies": {
+            "semver": {
+              "version": "5.7.1",
+              "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+              "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+              "dev": true
+            }
+          }
+        },
+        "rxjs": {
+          "version": "6.5.3",
+          "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz",
+          "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==",
+          "dev": true,
+          "requires": {
+            "tslib": "^1.9.0"
+          }
+        },
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+          "dev": true
+        }
+      }
+    },
+    "@tweenjs/tween.js": {
+      "version": "18.5.0",
+      "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-18.5.0.tgz",
+      "integrity": "sha512-/qyhdvbc6vWsFuiK7Wq74ZMu1F/9sFRlBv2qnjeu70XI0HRC8zOfUqbXT2irVLCSVuCI/fI7HphGdbsbyQ/Q5w=="
+    },
+    "@types/estree": {
+      "version": "0.0.45",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.45.tgz",
+      "integrity": "sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g==",
+      "dev": true
+    },
+    "@types/glob": {
+      "version": "7.1.3",
+      "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz",
+      "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==",
+      "dev": true,
+      "requires": {
+        "@types/minimatch": "*",
+        "@types/node": "*"
+      }
+    },
+    "@types/jasmine": {
+      "version": "3.5.12",
+      "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.5.12.tgz",
+      "integrity": "sha512-vJaQ58oceFao+NzpKNqLOWwHPsqA7YEhKv+mOXvYU4/qh+BfVWIxaBtL0Ck5iCS67yOkNwGkDCrzepnzIWF+7g==",
+      "dev": true
+    },
+    "@types/jasminewd2": {
+      "version": "2.0.8",
+      "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.8.tgz",
+      "integrity": "sha512-d9p31r7Nxk0ZH0U39PTH0hiDlJ+qNVGjlt1ucOoTUptxb2v+Y5VMnsxfwN+i3hK4yQnqBi3FMmoMFcd1JHDxdg==",
+      "dev": true,
+      "requires": {
+        "@types/jasmine": "*"
+      }
+    },
+    "@types/json-schema": {
+      "version": "7.0.5",
+      "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz",
+      "integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==",
+      "dev": true
+    },
+    "@types/minimatch": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
+      "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==",
+      "dev": true
+    },
+    "@types/node": {
+      "version": "12.12.53",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.53.tgz",
+      "integrity": "sha512-51MYTDTyCziHb70wtGNFRwB4l+5JNvdqzFSkbDvpbftEgVUBEE+T5f7pROhWMp/fxp07oNIEQZd5bbfAH22ohQ==",
+      "dev": true
+    },
+    "@types/q": {
+      "version": "1.5.4",
+      "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz",
+      "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==",
+      "dev": true
+    },
+    "@types/selenium-webdriver": {
+      "version": "3.0.17",
+      "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.17.tgz",
+      "integrity": "sha512-tGomyEuzSC1H28y2zlW6XPCaDaXFaD6soTdb4GNdmte2qfHtrKqhy0ZFs4r/1hpazCfEZqeTSRLvSasmEx89uw==",
+      "dev": true
+    },
+    "@types/source-list-map": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz",
+      "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==",
+      "dev": true
+    },
+    "@types/webpack-sources": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.8.tgz",
+      "integrity": "sha512-JHB2/xZlXOjzjBB6fMOpH1eQAfsrpqVVIbneE0Rok16WXwFaznaI5vfg75U5WgGJm7V9W1c4xeRQDjX/zwvghA==",
+      "dev": true,
+      "requires": {
+        "@types/node": "*",
+        "@types/source-list-map": "*",
+        "source-map": "^0.6.1"
+      }
+    },
+    "@webassemblyjs/ast": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz",
+      "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/helper-module-context": "1.8.5",
+        "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
+        "@webassemblyjs/wast-parser": "1.8.5"
+      }
+    },
+    "@webassemblyjs/floating-point-hex-parser": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz",
+      "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==",
+      "dev": true
+    },
+    "@webassemblyjs/helper-api-error": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz",
+      "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==",
+      "dev": true
+    },
+    "@webassemblyjs/helper-buffer": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz",
+      "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==",
+      "dev": true
+    },
+    "@webassemblyjs/helper-code-frame": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz",
+      "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/wast-printer": "1.8.5"
+      }
+    },
+    "@webassemblyjs/helper-fsm": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz",
+      "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==",
+      "dev": true
+    },
+    "@webassemblyjs/helper-module-context": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz",
+      "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.8.5",
+        "mamacro": "^0.0.3"
+      }
+    },
+    "@webassemblyjs/helper-wasm-bytecode": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz",
+      "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==",
+      "dev": true
+    },
+    "@webassemblyjs/helper-wasm-section": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz",
+      "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.8.5",
+        "@webassemblyjs/helper-buffer": "1.8.5",
+        "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
+        "@webassemblyjs/wasm-gen": "1.8.5"
+      }
+    },
+    "@webassemblyjs/ieee754": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz",
+      "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==",
+      "dev": true,
+      "requires": {
+        "@xtuc/ieee754": "^1.2.0"
+      }
+    },
+    "@webassemblyjs/leb128": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz",
+      "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==",
+      "dev": true,
+      "requires": {
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "@webassemblyjs/utf8": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz",
+      "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==",
+      "dev": true
+    },
+    "@webassemblyjs/wasm-edit": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz",
+      "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.8.5",
+        "@webassemblyjs/helper-buffer": "1.8.5",
+        "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
+        "@webassemblyjs/helper-wasm-section": "1.8.5",
+        "@webassemblyjs/wasm-gen": "1.8.5",
+        "@webassemblyjs/wasm-opt": "1.8.5",
+        "@webassemblyjs/wasm-parser": "1.8.5",
+        "@webassemblyjs/wast-printer": "1.8.5"
+      }
+    },
+    "@webassemblyjs/wasm-gen": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz",
+      "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.8.5",
+        "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
+        "@webassemblyjs/ieee754": "1.8.5",
+        "@webassemblyjs/leb128": "1.8.5",
+        "@webassemblyjs/utf8": "1.8.5"
+      }
+    },
+    "@webassemblyjs/wasm-opt": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz",
+      "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.8.5",
+        "@webassemblyjs/helper-buffer": "1.8.5",
+        "@webassemblyjs/wasm-gen": "1.8.5",
+        "@webassemblyjs/wasm-parser": "1.8.5"
+      }
+    },
+    "@webassemblyjs/wasm-parser": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz",
+      "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.8.5",
+        "@webassemblyjs/helper-api-error": "1.8.5",
+        "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
+        "@webassemblyjs/ieee754": "1.8.5",
+        "@webassemblyjs/leb128": "1.8.5",
+        "@webassemblyjs/utf8": "1.8.5"
+      }
+    },
+    "@webassemblyjs/wast-parser": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz",
+      "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.8.5",
+        "@webassemblyjs/floating-point-hex-parser": "1.8.5",
+        "@webassemblyjs/helper-api-error": "1.8.5",
+        "@webassemblyjs/helper-code-frame": "1.8.5",
+        "@webassemblyjs/helper-fsm": "1.8.5",
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "@webassemblyjs/wast-printer": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz",
+      "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.8.5",
+        "@webassemblyjs/wast-parser": "1.8.5",
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "@xtuc/ieee754": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+      "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
+      "dev": true
+    },
+    "@xtuc/long": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
+      "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
+      "dev": true
+    },
+    "@yarnpkg/lockfile": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
+      "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==",
+      "dev": true
+    },
+    "JSONStream": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
+      "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
+      "dev": true,
+      "requires": {
+        "jsonparse": "^1.2.0",
+        "through": ">=2.2.7 <3"
+      }
+    },
+    "accepts": {
+      "version": "1.3.7",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
+      "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
+      "dev": true,
+      "requires": {
+        "mime-types": "~2.1.24",
+        "negotiator": "0.6.2"
+      }
+    },
+    "acorn": {
+      "version": "7.3.1",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz",
+      "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==",
+      "dev": true
+    },
+    "address": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/address/-/address-1.1.2.tgz",
+      "integrity": "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA=="
+    },
+    "adm-zip": {
+      "version": "0.4.16",
+      "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz",
+      "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==",
+      "dev": true
+    },
+    "after": {
+      "version": "0.8.2",
+      "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
+      "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=",
+      "dev": true
+    },
+    "agent-base": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
+      "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
+      "requires": {
+        "es6-promisify": "^5.0.0"
+      }
+    },
+    "agentkeepalive": {
+      "version": "3.5.2",
+      "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz",
+      "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==",
+      "requires": {
+        "humanize-ms": "^1.2.1"
+      }
+    },
+    "aggregate-error": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz",
+      "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==",
+      "dev": true,
+      "requires": {
+        "clean-stack": "^2.0.0",
+        "indent-string": "^4.0.0"
+      }
+    },
+    "ajv": {
+      "version": "6.10.2",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
+      "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
+      "dev": true,
+      "requires": {
+        "fast-deep-equal": "^2.0.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      }
+    },
+    "ajv-errors": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
+      "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==",
+      "dev": true
+    },
+    "ajv-keywords": {
+      "version": "3.5.2",
+      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+      "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+      "dev": true
+    },
+    "ali-oss": {
+      "version": "6.10.0",
+      "resolved": "https://registry.npmjs.org/ali-oss/-/ali-oss-6.10.0.tgz",
+      "integrity": "sha512-kWguelLe4QZCvqisN3i9i+kyaG2JCj+4AqXbc+HKJ3b4WYwkn2hS9INJHtrUSlo95yn9FKSNXwTAfLMrXyQlnA==",
+      "requires": {
+        "address": "^1.0.0",
+        "agentkeepalive": "^3.4.1",
+        "any-promise": "^1.3.0",
+        "bowser": "^1.6.0",
+        "co-defer": "^1.0.0",
+        "copy-to": "^2.0.1",
+        "dateformat": "^2.0.0",
+        "debug": "^2.2.0",
+        "destroy": "^1.0.4",
+        "end-or-error": "^1.0.1",
+        "get-ready": "^1.0.0",
+        "humanize-ms": "^1.2.0",
+        "is-type-of": "^1.0.0",
+        "js-base64": "^2.5.2",
+        "jstoxml": "^0.2.3",
+        "merge-descriptors": "^1.0.1",
+        "mime": "^2.4.5",
+        "mz-modules": "^2.1.0",
+        "platform": "^1.3.1",
+        "pump": "^3.0.0",
+        "sdk-base": "^2.0.1",
+        "stream-http": "2.8.2",
+        "stream-wormhole": "^1.0.4",
+        "urllib": "^2.33.1",
+        "utility": "^1.8.0",
+        "xml2js": "^0.4.16"
+      }
+    },
+    "alphanum-sort": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz",
+      "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=",
+      "dev": true
+    },
+    "ansi-colors": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz",
+      "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==",
+      "dev": true
+    },
+    "ansi-escapes": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz",
+      "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==",
+      "dev": true,
+      "requires": {
+        "type-fest": "^0.11.0"
+      }
+    },
+    "ansi-html": {
+      "version": "0.0.7",
+      "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
+      "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=",
+      "dev": true
+    },
+    "ansi-regex": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+      "dev": true
+    },
+    "ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+      "dev": true,
+      "requires": {
+        "color-convert": "^1.9.0"
+      }
+    },
+    "any-promise": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+      "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8="
+    },
+    "anymatch": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
+      "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
+      "dev": true,
+      "requires": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      }
+    },
+    "app-root-path": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.2.1.tgz",
+      "integrity": "sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA==",
+      "dev": true
+    },
+    "append-transform": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz",
+      "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==",
+      "dev": true,
+      "requires": {
+        "default-require-extensions": "^2.0.0"
+      }
+    },
+    "aproba": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+      "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
+      "dev": true
+    },
+    "arg": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+      "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+      "dev": true
+    },
+    "argparse": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+      "dev": true,
+      "requires": {
+        "sprintf-js": "~1.0.2"
+      }
+    },
+    "aria-query": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz",
+      "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=",
+      "dev": true,
+      "requires": {
+        "ast-types-flow": "0.0.7",
+        "commander": "^2.11.0"
+      }
+    },
+    "arr-diff": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+      "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
+      "dev": true
+    },
+    "arr-flatten": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+      "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+      "dev": true
+    },
+    "arr-union": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+      "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
+      "dev": true
+    },
+    "array-flatten": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",
+      "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==",
+      "dev": true
+    },
+    "array-union": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+      "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
+      "dev": true,
+      "requires": {
+        "array-uniq": "^1.0.1"
+      }
+    },
+    "array-uniq": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+      "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
+      "dev": true
+    },
+    "array-unique": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+      "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
+      "dev": true
+    },
+    "arraybuffer.slice": {
+      "version": "0.0.7",
+      "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
+      "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==",
+      "dev": true
+    },
+    "arrify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
+      "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
+      "dev": true
+    },
+    "asap": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+      "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=",
+      "dev": true
+    },
+    "asn1": {
+      "version": "0.2.4",
+      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
+      "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
+      "dev": true,
+      "requires": {
+        "safer-buffer": "~2.1.0"
+      }
+    },
+    "asn1.js": {
+      "version": "4.10.1",
+      "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz",
+      "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==",
+      "dev": true,
+      "requires": {
+        "bn.js": "^4.0.0",
+        "inherits": "^2.0.1",
+        "minimalistic-assert": "^1.0.0"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.11.9",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+          "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+          "dev": true
+        }
+      }
+    },
+    "assert": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz",
+      "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==",
+      "dev": true,
+      "requires": {
+        "object-assign": "^4.1.1",
+        "util": "0.10.3"
+      },
+      "dependencies": {
+        "inherits": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
+          "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=",
+          "dev": true
+        },
+        "util": {
+          "version": "0.10.3",
+          "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
+          "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
+          "dev": true,
+          "requires": {
+            "inherits": "2.0.1"
+          }
+        }
+      }
+    },
+    "assert-plus": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+      "dev": true
+    },
+    "assign-symbols": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+      "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
+      "dev": true
+    },
+    "ast-types": {
+      "version": "0.13.3",
+      "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.3.tgz",
+      "integrity": "sha512-XTZ7xGML849LkQP86sWdQzfhwbt3YwIO6MqbX9mUNYY98VKaaVZP7YNNm70IpwecbkkxmfC5IYAzOQ/2p29zRA=="
+    },
+    "ast-types-flow": {
+      "version": "0.0.7",
+      "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
+      "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=",
+      "dev": true
+    },
+    "async": {
+      "version": "2.6.3",
+      "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
+      "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
+      "dev": true,
+      "requires": {
+        "lodash": "^4.17.14"
+      }
+    },
+    "async-each": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
+      "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
+      "dev": true
+    },
+    "async-limiter": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
+      "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
+      "dev": true
+    },
+    "asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+      "dev": true
+    },
+    "atob": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+      "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+      "dev": true
+    },
+    "autoprefixer": {
+      "version": "9.7.1",
+      "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.1.tgz",
+      "integrity": "sha512-w3b5y1PXWlhYulevrTJ0lizkQ5CyqfeU6BIRDbuhsMupstHQOeb1Ur80tcB1zxSu7AwyY/qCQ7Vvqklh31ZBFw==",
+      "dev": true,
+      "requires": {
+        "browserslist": "^4.7.2",
+        "caniuse-lite": "^1.0.30001006",
+        "chalk": "^2.4.2",
+        "normalize-range": "^0.1.2",
+        "num2fraction": "^1.2.2",
+        "postcss": "^7.0.21",
+        "postcss-value-parser": "^4.0.2"
+      }
+    },
+    "aws-sign2": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+      "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
+      "dev": true
+    },
+    "aws4": {
+      "version": "1.10.0",
+      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz",
+      "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==",
+      "dev": true
+    },
+    "axobject-query": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz",
+      "integrity": "sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww==",
+      "dev": true,
+      "requires": {
+        "ast-types-flow": "0.0.7"
+      }
+    },
+    "babel-loader": {
+      "version": "8.0.6",
+      "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.6.tgz",
+      "integrity": "sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw==",
+      "dev": true,
+      "requires": {
+        "find-cache-dir": "^2.0.0",
+        "loader-utils": "^1.0.2",
+        "mkdirp": "^0.5.1",
+        "pify": "^4.0.1"
+      },
+      "dependencies": {
+        "find-cache-dir": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
+          "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==",
+          "dev": true,
+          "requires": {
+            "commondir": "^1.0.1",
+            "make-dir": "^2.0.0",
+            "pkg-dir": "^3.0.0"
+          }
+        }
+      }
+    },
+    "babel-plugin-dynamic-import-node": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz",
+      "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==",
+      "dev": true,
+      "requires": {
+        "object.assign": "^4.1.0"
+      }
+    },
+    "backo2": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
+      "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=",
+      "dev": true
+    },
+    "balanced-match": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
+    },
+    "base": {
+      "version": "0.11.2",
+      "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+      "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+      "dev": true,
+      "requires": {
+        "cache-base": "^1.0.1",
+        "class-utils": "^0.3.5",
+        "component-emitter": "^1.2.1",
+        "define-property": "^1.0.0",
+        "isobject": "^3.0.1",
+        "mixin-deep": "^1.2.0",
+        "pascalcase": "^0.1.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^1.0.0"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "^1.0.0",
+            "is-data-descriptor": "^1.0.0",
+            "kind-of": "^6.0.2"
+          }
+        }
+      }
+    },
+    "base64-arraybuffer": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
+      "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=",
+      "dev": true
+    },
+    "base64-js": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
+      "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==",
+      "dev": true
+    },
+    "base64id": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz",
+      "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=",
+      "dev": true
+    },
+    "batch": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
+      "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=",
+      "dev": true
+    },
+    "bcrypt-pbkdf": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+      "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+      "dev": true,
+      "requires": {
+        "tweetnacl": "^0.14.3"
+      }
+    },
+    "better-assert": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
+      "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
+      "dev": true,
+      "requires": {
+        "callsite": "1.0.0"
+      }
+    },
+    "big.js": {
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
+      "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
+      "dev": true
+    },
+    "binary-extensions": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
+      "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==",
+      "dev": true
+    },
+    "bl": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
+      "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==",
+      "requires": {
+        "readable-stream": "^2.3.5",
+        "safe-buffer": "^5.1.1"
+      }
+    },
+    "blob": {
+      "version": "0.0.5",
+      "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
+      "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==",
+      "dev": true
+    },
+    "blocking-proxy": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz",
+      "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==",
+      "dev": true,
+      "requires": {
+        "minimist": "^1.2.0"
+      }
+    },
+    "bluebird": {
+      "version": "3.7.2",
+      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+      "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
+      "dev": true
+    },
+    "bn.js": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.2.tgz",
+      "integrity": "sha512-40rZaf3bUNKTVYu9sIeeEGOg7g14Yvnj9kH7b50EiwX0Q7A6umbvfI5tvHaOERH0XigqKkfLkFQxzb4e6CIXnA==",
+      "dev": true
+    },
+    "body-parser": {
+      "version": "1.19.0",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
+      "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
+      "dev": true,
+      "requires": {
+        "bytes": "3.1.0",
+        "content-type": "~1.0.4",
+        "debug": "2.6.9",
+        "depd": "~1.1.2",
+        "http-errors": "1.7.2",
+        "iconv-lite": "0.4.24",
+        "on-finished": "~2.3.0",
+        "qs": "6.7.0",
+        "raw-body": "2.4.0",
+        "type-is": "~1.6.17"
+      },
+      "dependencies": {
+        "http-errors": {
+          "version": "1.7.2",
+          "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
+          "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
+          "dev": true,
+          "requires": {
+            "depd": "~1.1.2",
+            "inherits": "2.0.3",
+            "setprototypeof": "1.1.1",
+            "statuses": ">= 1.5.0 < 2",
+            "toidentifier": "1.0.0"
+          }
+        },
+        "inherits": {
+          "version": "2.0.3",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+          "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+          "dev": true
+        },
+        "qs": {
+          "version": "6.7.0",
+          "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
+          "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
+          "dev": true
+        },
+        "raw-body": {
+          "version": "2.4.0",
+          "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
+          "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
+          "dev": true,
+          "requires": {
+            "bytes": "3.1.0",
+            "http-errors": "1.7.2",
+            "iconv-lite": "0.4.24",
+            "unpipe": "1.0.0"
+          }
+        }
+      }
+    },
+    "bonjour": {
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz",
+      "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=",
+      "dev": true,
+      "requires": {
+        "array-flatten": "^2.1.0",
+        "deep-equal": "^1.0.1",
+        "dns-equal": "^1.0.0",
+        "dns-txt": "^2.0.2",
+        "multicast-dns": "^6.0.1",
+        "multicast-dns-service-types": "^1.1.0"
+      }
+    },
+    "boolbase": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+      "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
+      "dev": true
+    },
+    "bowser": {
+      "version": "1.9.4",
+      "resolved": "https://registry.npmjs.org/bowser/-/bowser-1.9.4.tgz",
+      "integrity": "sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ=="
+    },
+    "brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "requires": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "dev": true,
+      "requires": {
+        "fill-range": "^7.0.1"
+      }
+    },
+    "brorand": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+      "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
+      "dev": true
+    },
+    "browserify-aes": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+      "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
+      "dev": true,
+      "requires": {
+        "buffer-xor": "^1.0.3",
+        "cipher-base": "^1.0.0",
+        "create-hash": "^1.1.0",
+        "evp_bytestokey": "^1.0.3",
+        "inherits": "^2.0.1",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "browserify-cipher": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
+      "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
+      "dev": true,
+      "requires": {
+        "browserify-aes": "^1.0.4",
+        "browserify-des": "^1.0.0",
+        "evp_bytestokey": "^1.0.0"
+      }
+    },
+    "browserify-des": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
+      "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
+      "dev": true,
+      "requires": {
+        "cipher-base": "^1.0.1",
+        "des.js": "^1.0.0",
+        "inherits": "^2.0.1",
+        "safe-buffer": "^5.1.2"
+      }
+    },
+    "browserify-rsa": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
+      "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
+      "dev": true,
+      "requires": {
+        "bn.js": "^4.1.0",
+        "randombytes": "^2.0.1"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.11.9",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+          "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+          "dev": true
+        }
+      }
+    },
+    "browserify-sign": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.0.tgz",
+      "integrity": "sha512-hEZC1KEeYuoHRqhGhTy6gWrpJA3ZDjFWv0DE61643ZnOXAKJb3u7yWcrU0mMc9SwAqK1n7myPGndkp0dFG7NFA==",
+      "dev": true,
+      "requires": {
+        "bn.js": "^5.1.1",
+        "browserify-rsa": "^4.0.1",
+        "create-hash": "^1.2.0",
+        "create-hmac": "^1.1.7",
+        "elliptic": "^6.5.2",
+        "inherits": "^2.0.4",
+        "parse-asn1": "^5.1.5",
+        "readable-stream": "^3.6.0",
+        "safe-buffer": "^5.2.0"
+      },
+      "dependencies": {
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+          "dev": true,
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.2.1",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+          "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+          "dev": true
+        }
+      }
+    },
+    "browserify-zlib": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
+      "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
+      "dev": true,
+      "requires": {
+        "pako": "~1.0.5"
+      }
+    },
+    "browserslist": {
+      "version": "4.13.0",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.13.0.tgz",
+      "integrity": "sha512-MINatJ5ZNrLnQ6blGvePd/QOz9Xtu+Ne+x29iQSCHfkU5BugKVJwZKn/iiL8UbpIpa3JhviKjz+XxMo0m2caFQ==",
+      "dev": true,
+      "requires": {
+        "caniuse-lite": "^1.0.30001093",
+        "electron-to-chromium": "^1.3.488",
+        "escalade": "^3.0.1",
+        "node-releases": "^1.1.58"
+      }
+    },
+    "browserstack": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.6.0.tgz",
+      "integrity": "sha512-HJDJ0TSlmkwnt9RZ+v5gFpa1XZTBYTj0ywvLwJ3241J7vMw2jAsGNVhKHtmCOyg+VxeLZyaibO9UL71AsUeDIw==",
+      "dev": true,
+      "requires": {
+        "https-proxy-agent": "^2.2.1"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "3.2.6",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+          "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+          "dev": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "https-proxy-agent": {
+          "version": "2.2.4",
+          "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
+          "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
+          "dev": true,
+          "requires": {
+            "agent-base": "^4.3.0",
+            "debug": "^3.1.0"
+          }
+        }
+      }
+    },
+    "buffer": {
+      "version": "4.9.2",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
+      "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
+      "dev": true,
+      "requires": {
+        "base64-js": "^1.0.2",
+        "ieee754": "^1.1.4",
+        "isarray": "^1.0.0"
+      }
+    },
+    "buffer-alloc": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
+      "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
+      "requires": {
+        "buffer-alloc-unsafe": "^1.1.0",
+        "buffer-fill": "^1.0.0"
+      }
+    },
+    "buffer-alloc-unsafe": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
+      "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg=="
+    },
+    "buffer-crc32": {
+      "version": "0.2.13",
+      "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+      "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI="
+    },
+    "buffer-fill": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
+      "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw="
+    },
+    "buffer-from": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+      "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+      "dev": true
+    },
+    "buffer-indexof": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz",
+      "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==",
+      "dev": true
+    },
+    "buffer-xor": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
+      "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
+      "dev": true
+    },
+    "builtin-modules": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+      "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
+      "dev": true
+    },
+    "builtin-status-codes": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
+      "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug="
+    },
+    "builtins": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz",
+      "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=",
+      "dev": true
+    },
+    "bytes": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
+      "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
+    },
+    "cacache": {
+      "version": "13.0.1",
+      "resolved": "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz",
+      "integrity": "sha512-5ZvAxd05HDDU+y9BVvcqYu2LLXmPnQ0hW62h32g4xBTgL/MppR4/04NHfj/ycM2y6lmTnbw6HVi+1eN0Psba6w==",
+      "dev": true,
+      "requires": {
+        "chownr": "^1.1.2",
+        "figgy-pudding": "^3.5.1",
+        "fs-minipass": "^2.0.0",
+        "glob": "^7.1.4",
+        "graceful-fs": "^4.2.2",
+        "infer-owner": "^1.0.4",
+        "lru-cache": "^5.1.1",
+        "minipass": "^3.0.0",
+        "minipass-collect": "^1.0.2",
+        "minipass-flush": "^1.0.5",
+        "minipass-pipeline": "^1.2.2",
+        "mkdirp": "^0.5.1",
+        "move-concurrently": "^1.0.1",
+        "p-map": "^3.0.0",
+        "promise-inflight": "^1.0.1",
+        "rimraf": "^2.7.1",
+        "ssri": "^7.0.0",
+        "unique-filename": "^1.1.1"
+      }
+    },
+    "cache-base": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+      "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+      "dev": true,
+      "requires": {
+        "collection-visit": "^1.0.0",
+        "component-emitter": "^1.2.1",
+        "get-value": "^2.0.6",
+        "has-value": "^1.0.0",
+        "isobject": "^3.0.1",
+        "set-value": "^2.0.0",
+        "to-object-path": "^0.3.0",
+        "union-value": "^1.0.0",
+        "unset-value": "^1.0.0"
+      }
+    },
+    "caller-callsite": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
+      "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=",
+      "dev": true,
+      "requires": {
+        "callsites": "^2.0.0"
+      }
+    },
+    "caller-path": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz",
+      "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=",
+      "dev": true,
+      "requires": {
+        "caller-callsite": "^2.0.0"
+      }
+    },
+    "callsite": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
+      "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=",
+      "dev": true
+    },
+    "callsites": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
+      "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=",
+      "dev": true
+    },
+    "camelcase": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+      "dev": true
+    },
+    "caniuse-api": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
+      "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==",
+      "dev": true,
+      "requires": {
+        "browserslist": "^4.0.0",
+        "caniuse-lite": "^1.0.0",
+        "lodash.memoize": "^4.1.2",
+        "lodash.uniq": "^4.5.0"
+      }
+    },
+    "caniuse-lite": {
+      "version": "1.0.30001109",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001109.tgz",
+      "integrity": "sha512-4JIXRodHzdS3HdK8nSgIqXYLExOvG+D2/EenSvcub2Kp3QEADjo2v2oUn5g0n0D+UNwG9BtwKOyGcSq2qvQXvQ==",
+      "dev": true
+    },
+    "canonical-path": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-1.0.0.tgz",
+      "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==",
+      "dev": true
+    },
+    "caseless": {
+      "version": "0.12.0",
+      "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+      "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
+      "dev": true
+    },
+    "chalk": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
+      }
+    },
+    "chardet": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
+      "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
+      "dev": true
+    },
+    "chokidar": {
+      "version": "3.4.1",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.1.tgz",
+      "integrity": "sha512-TQTJyr2stihpC4Sya9hs2Xh+O2wf+igjL36Y75xx2WdHuiICcn/XJza46Jwt0eT5hVpQOzo3FpY3cj3RVYLX0g==",
+      "dev": true,
+      "requires": {
+        "anymatch": "~3.1.1",
+        "braces": "~3.0.2",
+        "fsevents": "~2.1.2",
+        "glob-parent": "~5.1.0",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.4.0"
+      },
+      "dependencies": {
+        "glob-parent": {
+          "version": "5.1.1",
+          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
+          "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
+          "dev": true,
+          "requires": {
+            "is-glob": "^4.0.1"
+          }
+        }
+      }
+    },
+    "chownr": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+      "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+      "dev": true
+    },
+    "chrome-trace-event": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz",
+      "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==",
+      "dev": true,
+      "requires": {
+        "tslib": "^1.9.0"
+      }
+    },
+    "cipher-base": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
+      "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
+      "dev": true,
+      "requires": {
+        "inherits": "^2.0.1",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "circular-dependency-plugin": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.0.tgz",
+      "integrity": "sha512-7p4Kn/gffhQaavNfyDFg7LS5S/UT1JAjyGd4UqR2+jzoYF02eDkj0Ec3+48TsIa4zghjLY87nQHIh/ecK9qLdw==",
+      "dev": true
+    },
+    "class-utils": {
+      "version": "0.3.6",
+      "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+      "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+      "dev": true,
+      "requires": {
+        "arr-union": "^3.1.0",
+        "define-property": "^0.2.5",
+        "isobject": "^3.0.0",
+        "static-extend": "^0.1.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        }
+      }
+    },
+    "clean-stack": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
+      "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
+      "dev": true
+    },
+    "cli-cursor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+      "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+      "dev": true,
+      "requires": {
+        "restore-cursor": "^3.1.0"
+      }
+    },
+    "cli-spinners": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.4.0.tgz",
+      "integrity": "sha512-sJAofoarcm76ZGpuooaO0eDy8saEy+YoZBLjC4h8srt4jeBnkYeOgqxgsJQTpyt2LjI5PTfLJHSL+41Yu4fEJA==",
+      "dev": true
+    },
+    "cli-width": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz",
+      "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==",
+      "dev": true
+    },
+    "cliui": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
+      "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==",
+      "dev": true,
+      "requires": {
+        "string-width": "^2.1.1",
+        "strip-ansi": "^4.0.0",
+        "wrap-ansi": "^2.0.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+          "dev": true
+        },
+        "strip-ansi": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^3.0.0"
+          }
+        }
+      }
+    },
+    "clone": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+      "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=",
+      "dev": true
+    },
+    "clone-deep": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
+      "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
+      "dev": true,
+      "requires": {
+        "is-plain-object": "^2.0.4",
+        "kind-of": "^6.0.2",
+        "shallow-clone": "^3.0.0"
+      }
+    },
+    "co": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+      "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
+    },
+    "co-defer": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/co-defer/-/co-defer-1.0.0.tgz",
+      "integrity": "sha1-Pkp4eo7tawoh7ih8CU9+jeDTyBg="
+    },
+    "coa": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz",
+      "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==",
+      "dev": true,
+      "requires": {
+        "@types/q": "^1.5.1",
+        "chalk": "^2.4.1",
+        "q": "^1.1.2"
+      }
+    },
+    "code-point-at": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+      "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
+      "dev": true
+    },
+    "codelyzer": {
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-5.2.2.tgz",
+      "integrity": "sha512-jB4FZ1Sx7kZhvZVdf+N2BaKTdrrNZOL0Bj10RRfrhHrb3zEvXjJvvq298JPMJAiyiCS/v4zs1QlGU0ip7xGqeA==",
+      "dev": true,
+      "requires": {
+        "app-root-path": "^2.2.1",
+        "aria-query": "^3.0.0",
+        "axobject-query": "2.0.2",
+        "css-selector-tokenizer": "^0.7.1",
+        "cssauron": "^1.4.0",
+        "damerau-levenshtein": "^1.0.4",
+        "semver-dsl": "^1.0.1",
+        "source-map": "^0.5.7",
+        "sprintf-js": "^1.1.2"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+          "dev": true
+        },
+        "sprintf-js": {
+          "version": "1.1.2",
+          "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
+          "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==",
+          "dev": true
+        }
+      }
+    },
+    "collection-visit": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+      "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+      "dev": true,
+      "requires": {
+        "map-visit": "^1.0.0",
+        "object-visit": "^1.0.0"
+      }
+    },
+    "color": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz",
+      "integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==",
+      "dev": true,
+      "requires": {
+        "color-convert": "^1.9.1",
+        "color-string": "^1.5.2"
+      }
+    },
+    "color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "dev": true,
+      "requires": {
+        "color-name": "1.1.3"
+      }
+    },
+    "color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+      "dev": true
+    },
+    "color-string": {
+      "version": "1.5.3",
+      "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz",
+      "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==",
+      "dev": true,
+      "requires": {
+        "color-name": "^1.0.0",
+        "simple-swizzle": "^0.2.2"
+      }
+    },
+    "colors": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
+      "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
+      "dev": true
+    },
+    "combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "dev": true,
+      "requires": {
+        "delayed-stream": "~1.0.0"
+      }
+    },
+    "commander": {
+      "version": "2.20.3",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+      "dev": true
+    },
+    "commondir": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+      "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
+      "dev": true
+    },
+    "compare-versions": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz",
+      "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==",
+      "dev": true
+    },
+    "component-bind": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
+      "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=",
+      "dev": true
+    },
+    "component-emitter": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+      "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+      "dev": true
+    },
+    "component-inherit": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
+      "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=",
+      "dev": true
+    },
+    "compressible": {
+      "version": "2.0.18",
+      "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+      "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+      "dev": true,
+      "requires": {
+        "mime-db": ">= 1.43.0 < 2"
+      }
+    },
+    "compressing": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/compressing/-/compressing-1.5.1.tgz",
+      "integrity": "sha512-1AGBR4Vh0/rpPKSXHoV9cerLo0CUlAhZ/xsJqU3a9+2cbSaFjWT3fk2MIGkCnbpk//DGWrcWw03rhKFQrA9RQw==",
+      "requires": {
+        "flushwritable": "^1.0.0",
+        "get-ready": "^1.0.0",
+        "iconv-lite": "^0.5.0",
+        "mkdirp": "^0.5.1",
+        "pump": "^3.0.0",
+        "streamifier": "^0.1.1",
+        "tar-stream": "^1.5.2",
+        "yauzl": "^2.7.0",
+        "yazl": "^2.4.2"
+      },
+      "dependencies": {
+        "iconv-lite": {
+          "version": "0.5.2",
+          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz",
+          "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==",
+          "requires": {
+            "safer-buffer": ">= 2.1.2 < 3"
+          }
+        }
+      }
+    },
+    "compression": {
+      "version": "1.7.4",
+      "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
+      "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
+      "dev": true,
+      "requires": {
+        "accepts": "~1.3.5",
+        "bytes": "3.0.0",
+        "compressible": "~2.0.16",
+        "debug": "2.6.9",
+        "on-headers": "~1.0.2",
+        "safe-buffer": "5.1.2",
+        "vary": "~1.1.2"
+      },
+      "dependencies": {
+        "bytes": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+          "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=",
+          "dev": true
+        }
+      }
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+    },
+    "concat-stream": {
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+      "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+      "dev": true,
+      "requires": {
+        "buffer-from": "^1.0.0",
+        "inherits": "^2.0.3",
+        "readable-stream": "^2.2.2",
+        "typedarray": "^0.0.6"
+      }
+    },
+    "connect": {
+      "version": "3.7.0",
+      "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz",
+      "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==",
+      "dev": true,
+      "requires": {
+        "debug": "2.6.9",
+        "finalhandler": "1.1.2",
+        "parseurl": "~1.3.3",
+        "utils-merge": "1.0.1"
+      }
+    },
+    "connect-history-api-fallback": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz",
+      "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==",
+      "dev": true
+    },
+    "console-browserify": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz",
+      "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==",
+      "dev": true
+    },
+    "constants-browserify": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
+      "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=",
+      "dev": true
+    },
+    "content-disposition": {
+      "version": "0.5.3",
+      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
+      "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
+      "dev": true,
+      "requires": {
+        "safe-buffer": "5.1.2"
+      }
+    },
+    "content-type": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+      "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
+    },
+    "convert-source-map": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
+      "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==",
+      "dev": true,
+      "requires": {
+        "safe-buffer": "~5.1.1"
+      }
+    },
+    "cookie": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
+      "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==",
+      "dev": true
+    },
+    "cookie-signature": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+      "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=",
+      "dev": true
+    },
+    "copy-concurrently": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz",
+      "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==",
+      "dev": true,
+      "requires": {
+        "aproba": "^1.1.1",
+        "fs-write-stream-atomic": "^1.0.8",
+        "iferr": "^0.1.5",
+        "mkdirp": "^0.5.1",
+        "rimraf": "^2.5.4",
+        "run-queue": "^1.0.0"
+      }
+    },
+    "copy-descriptor": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+      "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
+      "dev": true
+    },
+    "copy-to": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/copy-to/-/copy-to-2.0.1.tgz",
+      "integrity": "sha1-JoD7uAaKSNCGVrYJgJK9r8kG9KU="
+    },
+    "copy-webpack-plugin": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.1.1.tgz",
+      "integrity": "sha512-P15M5ZC8dyCjQHWwd4Ia/dm0SgVvZJMYeykVIVYXbGyqO4dWB5oyPHp9i7wjwo5LhtlhKbiBCdS2NvM07Wlybg==",
+      "dev": true,
+      "requires": {
+        "cacache": "^12.0.3",
+        "find-cache-dir": "^2.1.0",
+        "glob-parent": "^3.1.0",
+        "globby": "^7.1.1",
+        "is-glob": "^4.0.1",
+        "loader-utils": "^1.2.3",
+        "minimatch": "^3.0.4",
+        "normalize-path": "^3.0.0",
+        "p-limit": "^2.2.1",
+        "schema-utils": "^1.0.0",
+        "serialize-javascript": "^2.1.2",
+        "webpack-log": "^2.0.0"
+      },
+      "dependencies": {
+        "cacache": {
+          "version": "12.0.4",
+          "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz",
+          "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==",
+          "dev": true,
+          "requires": {
+            "bluebird": "^3.5.5",
+            "chownr": "^1.1.1",
+            "figgy-pudding": "^3.5.1",
+            "glob": "^7.1.4",
+            "graceful-fs": "^4.1.15",
+            "infer-owner": "^1.0.3",
+            "lru-cache": "^5.1.1",
+            "mississippi": "^3.0.0",
+            "mkdirp": "^0.5.1",
+            "move-concurrently": "^1.0.1",
+            "promise-inflight": "^1.0.1",
+            "rimraf": "^2.6.3",
+            "ssri": "^6.0.1",
+            "unique-filename": "^1.1.1",
+            "y18n": "^4.0.0"
+          }
+        },
+        "find-cache-dir": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
+          "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==",
+          "dev": true,
+          "requires": {
+            "commondir": "^1.0.1",
+            "make-dir": "^2.0.0",
+            "pkg-dir": "^3.0.0"
+          }
+        },
+        "ssri": {
+          "version": "6.0.1",
+          "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz",
+          "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
+          "dev": true,
+          "requires": {
+            "figgy-pudding": "^3.5.1"
+          }
+        }
+      }
+    },
+    "core-js": {
+      "version": "3.6.4",
+      "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz",
+      "integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==",
+      "dev": true
+    },
+    "core-js-compat": {
+      "version": "3.6.5",
+      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz",
+      "integrity": "sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==",
+      "dev": true,
+      "requires": {
+        "browserslist": "^4.8.5",
+        "semver": "7.0.0"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "7.0.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
+          "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
+          "dev": true
+        }
+      }
+    },
+    "core-util-is": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+    },
+    "cosmiconfig": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
+      "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
+      "dev": true,
+      "requires": {
+        "import-fresh": "^2.0.0",
+        "is-directory": "^0.3.1",
+        "js-yaml": "^3.13.1",
+        "parse-json": "^4.0.0"
+      }
+    },
+    "coverage-istanbul-loader": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/coverage-istanbul-loader/-/coverage-istanbul-loader-2.0.3.tgz",
+      "integrity": "sha512-LiGRvyIuzVYs3M1ZYK1tF0HekjH0DJ8zFdUwAZq378EJzqOgToyb1690dp3TAUlP6Y+82uu42LRjuROVeJ54CA==",
+      "dev": true,
+      "requires": {
+        "convert-source-map": "^1.7.0",
+        "istanbul-lib-instrument": "^4.0.0",
+        "loader-utils": "^1.2.3",
+        "merge-source-map": "^1.1.0",
+        "schema-utils": "^2.6.1"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "6.12.3",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz",
+          "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==",
+          "dev": true,
+          "requires": {
+            "fast-deep-equal": "^3.1.1",
+            "fast-json-stable-stringify": "^2.0.0",
+            "json-schema-traverse": "^0.4.1",
+            "uri-js": "^4.2.2"
+          }
+        },
+        "fast-deep-equal": {
+          "version": "3.1.3",
+          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+          "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+          "dev": true
+        },
+        "schema-utils": {
+          "version": "2.7.0",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
+          "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
+          "dev": true,
+          "requires": {
+            "@types/json-schema": "^7.0.4",
+            "ajv": "^6.12.2",
+            "ajv-keywords": "^3.4.1"
+          }
+        }
+      }
+    },
+    "create-ecdh": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz",
+      "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==",
+      "dev": true,
+      "requires": {
+        "bn.js": "^4.1.0",
+        "elliptic": "^6.0.0"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.11.9",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+          "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+          "dev": true
+        }
+      }
+    },
+    "create-hash": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+      "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
+      "dev": true,
+      "requires": {
+        "cipher-base": "^1.0.1",
+        "inherits": "^2.0.1",
+        "md5.js": "^1.3.4",
+        "ripemd160": "^2.0.1",
+        "sha.js": "^2.4.0"
+      }
+    },
+    "create-hmac": {
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+      "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
+      "dev": true,
+      "requires": {
+        "cipher-base": "^1.0.3",
+        "create-hash": "^1.1.0",
+        "inherits": "^2.0.1",
+        "ripemd160": "^2.0.0",
+        "safe-buffer": "^5.0.1",
+        "sha.js": "^2.4.8"
+      }
+    },
+    "cross-spawn": {
+      "version": "6.0.5",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+      "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+      "dev": true,
+      "requires": {
+        "nice-try": "^1.0.4",
+        "path-key": "^2.0.1",
+        "semver": "^5.5.0",
+        "shebang-command": "^1.2.0",
+        "which": "^1.2.9"
+      }
+    },
+    "crypto-browserify": {
+      "version": "3.12.0",
+      "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
+      "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
+      "dev": true,
+      "requires": {
+        "browserify-cipher": "^1.0.0",
+        "browserify-sign": "^4.0.0",
+        "create-ecdh": "^4.0.0",
+        "create-hash": "^1.1.0",
+        "create-hmac": "^1.1.0",
+        "diffie-hellman": "^5.0.0",
+        "inherits": "^2.0.1",
+        "pbkdf2": "^3.0.3",
+        "public-encrypt": "^4.0.0",
+        "randombytes": "^2.0.0",
+        "randomfill": "^1.0.3"
+      }
+    },
+    "css": {
+      "version": "2.2.4",
+      "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz",
+      "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==",
+      "dev": true,
+      "requires": {
+        "inherits": "^2.0.3",
+        "source-map": "^0.6.1",
+        "source-map-resolve": "^0.5.2",
+        "urix": "^0.1.0"
+      }
+    },
+    "css-color-names": {
+      "version": "0.0.4",
+      "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
+      "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=",
+      "dev": true
+    },
+    "css-declaration-sorter": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz",
+      "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==",
+      "dev": true,
+      "requires": {
+        "postcss": "^7.0.1",
+        "timsort": "^0.3.0"
+      }
+    },
+    "css-parse": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz",
+      "integrity": "sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q=",
+      "dev": true,
+      "requires": {
+        "css": "^2.0.0"
+      }
+    },
+    "css-select": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz",
+      "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==",
+      "dev": true,
+      "requires": {
+        "boolbase": "^1.0.0",
+        "css-what": "^3.2.1",
+        "domutils": "^1.7.0",
+        "nth-check": "^1.0.2"
+      }
+    },
+    "css-select-base-adapter": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz",
+      "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==",
+      "dev": true
+    },
+    "css-selector-tokenizer": {
+      "version": "0.7.3",
+      "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz",
+      "integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==",
+      "dev": true,
+      "requires": {
+        "cssesc": "^3.0.0",
+        "fastparse": "^1.1.2"
+      }
+    },
+    "css-tree": {
+      "version": "1.0.0-alpha.37",
+      "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz",
+      "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==",
+      "dev": true,
+      "requires": {
+        "mdn-data": "2.0.4",
+        "source-map": "^0.6.1"
+      }
+    },
+    "css-what": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.3.0.tgz",
+      "integrity": "sha512-pv9JPyatiPaQ6pf4OvD/dbfm0o5LviWmwxNWzblYf/1u9QZd0ihV+PMwy5jdQWQ3349kZmKEx9WXuSka2dM4cg==",
+      "dev": true
+    },
+    "cssauron": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz",
+      "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=",
+      "dev": true,
+      "requires": {
+        "through": "X.X.X"
+      }
+    },
+    "cssesc": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+      "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+      "dev": true
+    },
+    "cssnano": {
+      "version": "4.1.10",
+      "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz",
+      "integrity": "sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==",
+      "dev": true,
+      "requires": {
+        "cosmiconfig": "^5.0.0",
+        "cssnano-preset-default": "^4.0.7",
+        "is-resolvable": "^1.0.0",
+        "postcss": "^7.0.0"
+      }
+    },
+    "cssnano-preset-default": {
+      "version": "4.0.7",
+      "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz",
+      "integrity": "sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==",
+      "dev": true,
+      "requires": {
+        "css-declaration-sorter": "^4.0.1",
+        "cssnano-util-raw-cache": "^4.0.1",
+        "postcss": "^7.0.0",
+        "postcss-calc": "^7.0.1",
+        "postcss-colormin": "^4.0.3",
+        "postcss-convert-values": "^4.0.1",
+        "postcss-discard-comments": "^4.0.2",
+        "postcss-discard-duplicates": "^4.0.2",
+        "postcss-discard-empty": "^4.0.1",
+        "postcss-discard-overridden": "^4.0.1",
+        "postcss-merge-longhand": "^4.0.11",
+        "postcss-merge-rules": "^4.0.3",
+        "postcss-minify-font-values": "^4.0.2",
+        "postcss-minify-gradients": "^4.0.2",
+        "postcss-minify-params": "^4.0.2",
+        "postcss-minify-selectors": "^4.0.2",
+        "postcss-normalize-charset": "^4.0.1",
+        "postcss-normalize-display-values": "^4.0.2",
+        "postcss-normalize-positions": "^4.0.2",
+        "postcss-normalize-repeat-style": "^4.0.2",
+        "postcss-normalize-string": "^4.0.2",
+        "postcss-normalize-timing-functions": "^4.0.2",
+        "postcss-normalize-unicode": "^4.0.1",
+        "postcss-normalize-url": "^4.0.1",
+        "postcss-normalize-whitespace": "^4.0.2",
+        "postcss-ordered-values": "^4.1.2",
+        "postcss-reduce-initial": "^4.0.3",
+        "postcss-reduce-transforms": "^4.0.2",
+        "postcss-svgo": "^4.0.2",
+        "postcss-unique-selectors": "^4.0.1"
+      }
+    },
+    "cssnano-util-get-arguments": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz",
+      "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=",
+      "dev": true
+    },
+    "cssnano-util-get-match": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz",
+      "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=",
+      "dev": true
+    },
+    "cssnano-util-raw-cache": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz",
+      "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==",
+      "dev": true,
+      "requires": {
+        "postcss": "^7.0.0"
+      }
+    },
+    "cssnano-util-same-parent": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz",
+      "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==",
+      "dev": true
+    },
+    "csso": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/csso/-/csso-4.0.3.tgz",
+      "integrity": "sha512-NL3spysxUkcrOgnpsT4Xdl2aiEiBG6bXswAABQVHcMrfjjBisFOKwLDOmf4wf32aPdcJws1zds2B0Rg+jqMyHQ==",
+      "dev": true,
+      "requires": {
+        "css-tree": "1.0.0-alpha.39"
+      },
+      "dependencies": {
+        "css-tree": {
+          "version": "1.0.0-alpha.39",
+          "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.39.tgz",
+          "integrity": "sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA==",
+          "dev": true,
+          "requires": {
+            "mdn-data": "2.0.6",
+            "source-map": "^0.6.1"
+          }
+        },
+        "mdn-data": {
+          "version": "2.0.6",
+          "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.6.tgz",
+          "integrity": "sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA==",
+          "dev": true
+        }
+      }
+    },
+    "custom-event": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
+      "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=",
+      "dev": true
+    },
+    "cyclist": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
+      "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
+      "dev": true
+    },
+    "damerau-levenshtein": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz",
+      "integrity": "sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug==",
+      "dev": true
+    },
+    "dashdash": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+      "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+      "dev": true,
+      "requires": {
+        "assert-plus": "^1.0.0"
+      }
+    },
+    "data-uri-to-buffer": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz",
+      "integrity": "sha512-vKQ9DTQPN1FLYiiEEOQ6IBGFqvjCa5rSK3cWMy/Nespm5d/x3dGFT9UBZnkLxCwua/IXBi2TYnwTEpsOvhC4UQ=="
+    },
+    "date-fns": {
+      "version": "1.30.1",
+      "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz",
+      "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw=="
+    },
+    "date-format": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz",
+      "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==",
+      "dev": true
+    },
+    "dateformat": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz",
+      "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI="
+    },
+    "debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "requires": {
+        "ms": "2.0.0"
+      },
+      "dependencies": {
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        }
+      }
+    },
+    "debuglog": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz",
+      "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=",
+      "dev": true
+    },
+    "decamelize": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+      "dev": true
+    },
+    "decode-uri-component": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+      "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
+      "dev": true
+    },
+    "deep-equal": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
+      "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==",
+      "dev": true,
+      "requires": {
+        "is-arguments": "^1.0.4",
+        "is-date-object": "^1.0.1",
+        "is-regex": "^1.0.4",
+        "object-is": "^1.0.1",
+        "object-keys": "^1.1.1",
+        "regexp.prototype.flags": "^1.2.0"
+      }
+    },
+    "deep-is": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+      "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ="
+    },
+    "default-gateway": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz",
+      "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==",
+      "dev": true,
+      "requires": {
+        "execa": "^1.0.0",
+        "ip-regex": "^2.1.0"
+      }
+    },
+    "default-require-extensions": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz",
+      "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=",
+      "dev": true,
+      "requires": {
+        "strip-bom": "^3.0.0"
+      }
+    },
+    "default-user-agent": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/default-user-agent/-/default-user-agent-1.0.0.tgz",
+      "integrity": "sha1-FsRu/cq6PtxF8k8r1IaLAbfCrcY=",
+      "requires": {
+        "os-name": "~1.0.3"
+      }
+    },
+    "defaults": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz",
+      "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=",
+      "dev": true,
+      "requires": {
+        "clone": "^1.0.2"
+      },
+      "dependencies": {
+        "clone": {
+          "version": "1.0.4",
+          "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+          "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=",
+          "dev": true
+        }
+      }
+    },
+    "define-properties": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+      "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+      "dev": true,
+      "requires": {
+        "object-keys": "^1.0.12"
+      }
+    },
+    "define-property": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+      "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+      "dev": true,
+      "requires": {
+        "is-descriptor": "^1.0.2",
+        "isobject": "^3.0.1"
+      },
+      "dependencies": {
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "^1.0.0",
+            "is-data-descriptor": "^1.0.0",
+            "kind-of": "^6.0.2"
+          }
+        }
+      }
+    },
+    "degenerator": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-1.0.4.tgz",
+      "integrity": "sha1-/PSQo37OJmRk2cxDGrmMWBnO0JU=",
+      "requires": {
+        "ast-types": "0.x.x",
+        "escodegen": "1.x.x",
+        "esprima": "3.x.x"
+      }
+    },
+    "del": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz",
+      "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==",
+      "dev": true,
+      "requires": {
+        "@types/glob": "^7.1.1",
+        "globby": "^6.1.0",
+        "is-path-cwd": "^2.0.0",
+        "is-path-in-cwd": "^2.0.0",
+        "p-map": "^2.0.0",
+        "pify": "^4.0.1",
+        "rimraf": "^2.6.3"
+      },
+      "dependencies": {
+        "globby": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
+          "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
+          "dev": true,
+          "requires": {
+            "array-union": "^1.0.1",
+            "glob": "^7.0.3",
+            "object-assign": "^4.0.1",
+            "pify": "^2.0.0",
+            "pinkie-promise": "^2.0.0"
+          },
+          "dependencies": {
+            "pify": {
+              "version": "2.3.0",
+              "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+              "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+              "dev": true
+            }
+          }
+        },
+        "p-map": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz",
+          "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==",
+          "dev": true
+        }
+      }
+    },
+    "delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+      "dev": true
+    },
+    "depd": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+      "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
+    },
+    "dependency-graph": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.7.2.tgz",
+      "integrity": "sha512-KqtH4/EZdtdfWX0p6MGP9jljvxSY6msy/pRUD4jgNwVpv3v1QmNLlsB3LDSSUg79BRVSn7jI1QPRtArGABovAQ==",
+      "dev": true
+    },
+    "des.js": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
+      "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==",
+      "dev": true,
+      "requires": {
+        "inherits": "^2.0.1",
+        "minimalistic-assert": "^1.0.0"
+      }
+    },
+    "destroy": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+      "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
+    },
+    "detect-node": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz",
+      "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==",
+      "dev": true
+    },
+    "dezalgo": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz",
+      "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=",
+      "dev": true,
+      "requires": {
+        "asap": "^2.0.0",
+        "wrappy": "1"
+      }
+    },
+    "di": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz",
+      "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=",
+      "dev": true
+    },
+    "diff": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+      "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+      "dev": true
+    },
+    "diffie-hellman": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
+      "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
+      "dev": true,
+      "requires": {
+        "bn.js": "^4.1.0",
+        "miller-rabin": "^4.0.0",
+        "randombytes": "^2.0.0"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.11.9",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+          "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+          "dev": true
+        }
+      }
+    },
+    "digest-header": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/digest-header/-/digest-header-0.0.1.tgz",
+      "integrity": "sha1-Ecz23uxXZqw3l0TZAcEsuklRS+Y=",
+      "requires": {
+        "utility": "0.1.11"
+      },
+      "dependencies": {
+        "utility": {
+          "version": "0.1.11",
+          "resolved": "https://registry.npmjs.org/utility/-/utility-0.1.11.tgz",
+          "integrity": "sha1-/eYM+bTkdRlHoM9dEEzik2ciZxU=",
+          "requires": {
+            "address": ">=0.0.1"
+          }
+        }
+      }
+    },
+    "dir-glob": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz",
+      "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==",
+      "dev": true,
+      "requires": {
+        "path-type": "^3.0.0"
+      }
+    },
+    "dns-equal": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
+      "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=",
+      "dev": true
+    },
+    "dns-packet": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz",
+      "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==",
+      "dev": true,
+      "requires": {
+        "ip": "^1.1.0",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "dns-txt": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz",
+      "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=",
+      "dev": true,
+      "requires": {
+        "buffer-indexof": "^1.0.0"
+      }
+    },
+    "dom-serialize": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz",
+      "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=",
+      "dev": true,
+      "requires": {
+        "custom-event": "~1.0.0",
+        "ent": "~2.2.0",
+        "extend": "^3.0.0",
+        "void-elements": "^2.0.0"
+      }
+    },
+    "dom-serializer": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
+      "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==",
+      "dev": true,
+      "requires": {
+        "domelementtype": "^2.0.1",
+        "entities": "^2.0.0"
+      },
+      "dependencies": {
+        "domelementtype": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz",
+          "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==",
+          "dev": true
+        }
+      }
+    },
+    "domain-browser": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
+      "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==",
+      "dev": true
+    },
+    "domelementtype": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
+      "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
+      "dev": true
+    },
+    "domutils": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
+      "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==",
+      "dev": true,
+      "requires": {
+        "dom-serializer": "0",
+        "domelementtype": "1"
+      }
+    },
+    "dot-prop": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz",
+      "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==",
+      "dev": true,
+      "requires": {
+        "is-obj": "^2.0.0"
+      }
+    },
+    "duplexify": {
+      "version": "3.7.1",
+      "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
+      "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
+      "dev": true,
+      "requires": {
+        "end-of-stream": "^1.0.0",
+        "inherits": "^2.0.1",
+        "readable-stream": "^2.0.0",
+        "stream-shift": "^1.0.0"
+      }
+    },
+    "ecc-jsbn": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+      "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+      "dev": true,
+      "requires": {
+        "jsbn": "~0.1.0",
+        "safer-buffer": "^2.1.0"
+      }
+    },
+    "ee-first": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+      "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+    },
+    "electron-to-chromium": {
+      "version": "1.3.516",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.516.tgz",
+      "integrity": "sha512-WDM5AAQdOrvLqSX8g3Zd5AujBXfMxf96oeZkff0U2HF5op3tjShE+on2yay3r1UD4M9I3p0iHpAS4+yV8U8A9A==",
+      "dev": true
+    },
+    "elliptic": {
+      "version": "6.5.3",
+      "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
+      "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
+      "dev": true,
+      "requires": {
+        "bn.js": "^4.4.0",
+        "brorand": "^1.0.1",
+        "hash.js": "^1.0.0",
+        "hmac-drbg": "^1.0.0",
+        "inherits": "^2.0.1",
+        "minimalistic-assert": "^1.0.0",
+        "minimalistic-crypto-utils": "^1.0.0"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.11.9",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+          "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+          "dev": true
+        }
+      }
+    },
+    "emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "dev": true
+    },
+    "emojis-list": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
+      "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
+      "dev": true
+    },
+    "encodeurl": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+      "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
+      "dev": true
+    },
+    "encoding": {
+      "version": "0.1.13",
+      "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
+      "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
+      "dev": true,
+      "requires": {
+        "iconv-lite": "^0.6.2"
+      },
+      "dependencies": {
+        "iconv-lite": {
+          "version": "0.6.2",
+          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
+          "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==",
+          "dev": true,
+          "requires": {
+            "safer-buffer": ">= 2.1.2 < 3.0.0"
+          }
+        }
+      }
+    },
+    "end-of-stream": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+      "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+      "requires": {
+        "once": "^1.4.0"
+      }
+    },
+    "end-or-error": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/end-or-error/-/end-or-error-1.0.1.tgz",
+      "integrity": "sha1-3HpiEP5403L+4kqLSJnb0VVBTcs="
+    },
+    "engine.io": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz",
+      "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==",
+      "dev": true,
+      "requires": {
+        "accepts": "~1.3.4",
+        "base64id": "1.0.0",
+        "cookie": "0.3.1",
+        "debug": "~3.1.0",
+        "engine.io-parser": "~2.1.0",
+        "ws": "~3.3.1"
+      },
+      "dependencies": {
+        "cookie": {
+          "version": "0.3.1",
+          "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
+          "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=",
+          "dev": true
+        },
+        "debug": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        },
+        "ws": {
+          "version": "3.3.3",
+          "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
+          "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
+          "dev": true,
+          "requires": {
+            "async-limiter": "~1.0.0",
+            "safe-buffer": "~5.1.0",
+            "ultron": "~1.1.0"
+          }
+        }
+      }
+    },
+    "engine.io-client": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz",
+      "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==",
+      "dev": true,
+      "requires": {
+        "component-emitter": "1.2.1",
+        "component-inherit": "0.0.3",
+        "debug": "~3.1.0",
+        "engine.io-parser": "~2.1.1",
+        "has-cors": "1.1.0",
+        "indexof": "0.0.1",
+        "parseqs": "0.0.5",
+        "parseuri": "0.0.5",
+        "ws": "~3.3.1",
+        "xmlhttprequest-ssl": "~1.5.4",
+        "yeast": "0.1.2"
+      },
+      "dependencies": {
+        "component-emitter": {
+          "version": "1.2.1",
+          "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+          "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
+          "dev": true
+        },
+        "debug": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        },
+        "ws": {
+          "version": "3.3.3",
+          "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
+          "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
+          "dev": true,
+          "requires": {
+            "async-limiter": "~1.0.0",
+            "safe-buffer": "~5.1.0",
+            "ultron": "~1.1.0"
+          }
+        }
+      }
+    },
+    "engine.io-parser": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz",
+      "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==",
+      "dev": true,
+      "requires": {
+        "after": "0.8.2",
+        "arraybuffer.slice": "~0.0.7",
+        "base64-arraybuffer": "0.1.5",
+        "blob": "0.0.5",
+        "has-binary2": "~1.0.2"
+      }
+    },
+    "enhanced-resolve": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz",
+      "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.1.2",
+        "memory-fs": "^0.5.0",
+        "tapable": "^1.0.0"
+      }
+    },
+    "ent": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz",
+      "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=",
+      "dev": true
+    },
+    "entities": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz",
+      "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==",
+      "dev": true
+    },
+    "err-code": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz",
+      "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=",
+      "dev": true
+    },
+    "errno": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
+      "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
+      "dev": true,
+      "requires": {
+        "prr": "~1.0.1"
+      }
+    },
+    "error-ex": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+      "dev": true,
+      "requires": {
+        "is-arrayish": "^0.2.1"
+      }
+    },
+    "es-abstract": {
+      "version": "1.17.6",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
+      "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
+      "dev": true,
+      "requires": {
+        "es-to-primitive": "^1.2.1",
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3",
+        "has-symbols": "^1.0.1",
+        "is-callable": "^1.2.0",
+        "is-regex": "^1.1.0",
+        "object-inspect": "^1.7.0",
+        "object-keys": "^1.1.1",
+        "object.assign": "^4.1.0",
+        "string.prototype.trimend": "^1.0.1",
+        "string.prototype.trimstart": "^1.0.1"
+      }
+    },
+    "es-to-primitive": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+      "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+      "dev": true,
+      "requires": {
+        "is-callable": "^1.1.4",
+        "is-date-object": "^1.0.1",
+        "is-symbol": "^1.0.2"
+      }
+    },
+    "es6-promise": {
+      "version": "4.2.8",
+      "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
+      "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
+    },
+    "es6-promisify": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
+      "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
+      "requires": {
+        "es6-promise": "^4.0.3"
+      }
+    },
+    "escalade": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.0.2.tgz",
+      "integrity": "sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ==",
+      "dev": true
+    },
+    "escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+    },
+    "escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+      "dev": true
+    },
+    "escodegen": {
+      "version": "1.14.3",
+      "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
+      "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
+      "requires": {
+        "esprima": "^4.0.1",
+        "estraverse": "^4.2.0",
+        "esutils": "^2.0.2",
+        "optionator": "^0.8.1",
+        "source-map": "~0.6.1"
+      },
+      "dependencies": {
+        "esprima": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+          "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
+        }
+      }
+    },
+    "eslint-scope": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
+      "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
+      "dev": true,
+      "requires": {
+        "esrecurse": "^4.1.0",
+        "estraverse": "^4.1.1"
+      }
+    },
+    "esprima": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
+      "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM="
+    },
+    "esrecurse": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
+      "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
+      "dev": true,
+      "requires": {
+        "estraverse": "^4.1.0"
+      }
+    },
+    "estraverse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+      "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="
+    },
+    "esutils": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="
+    },
+    "etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
+      "dev": true
+    },
+    "eventemitter3": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz",
+      "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==",
+      "dev": true
+    },
+    "events": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz",
+      "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==",
+      "dev": true
+    },
+    "eventsource": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz",
+      "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==",
+      "dev": true,
+      "requires": {
+        "original": "^1.0.0"
+      }
+    },
+    "evp_bytestokey": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
+      "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
+      "dev": true,
+      "requires": {
+        "md5.js": "^1.3.4",
+        "safe-buffer": "^5.1.1"
+      }
+    },
+    "execa": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
+      "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
+      "dev": true,
+      "requires": {
+        "cross-spawn": "^6.0.0",
+        "get-stream": "^4.0.0",
+        "is-stream": "^1.1.0",
+        "npm-run-path": "^2.0.0",
+        "p-finally": "^1.0.0",
+        "signal-exit": "^3.0.0",
+        "strip-eof": "^1.0.0"
+      }
+    },
+    "exit": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+      "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
+      "dev": true
+    },
+    "expand-brackets": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+      "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+      "dev": true,
+      "requires": {
+        "debug": "^2.3.3",
+        "define-property": "^0.2.5",
+        "extend-shallow": "^2.0.1",
+        "posix-character-classes": "^0.1.0",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        }
+      }
+    },
+    "express": {
+      "version": "4.17.1",
+      "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
+      "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
+      "dev": true,
+      "requires": {
+        "accepts": "~1.3.7",
+        "array-flatten": "1.1.1",
+        "body-parser": "1.19.0",
+        "content-disposition": "0.5.3",
+        "content-type": "~1.0.4",
+        "cookie": "0.4.0",
+        "cookie-signature": "1.0.6",
+        "debug": "2.6.9",
+        "depd": "~1.1.2",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "finalhandler": "~1.1.2",
+        "fresh": "0.5.2",
+        "merge-descriptors": "1.0.1",
+        "methods": "~1.1.2",
+        "on-finished": "~2.3.0",
+        "parseurl": "~1.3.3",
+        "path-to-regexp": "0.1.7",
+        "proxy-addr": "~2.0.5",
+        "qs": "6.7.0",
+        "range-parser": "~1.2.1",
+        "safe-buffer": "5.1.2",
+        "send": "0.17.1",
+        "serve-static": "1.14.1",
+        "setprototypeof": "1.1.1",
+        "statuses": "~1.5.0",
+        "type-is": "~1.6.18",
+        "utils-merge": "1.0.1",
+        "vary": "~1.1.2"
+      },
+      "dependencies": {
+        "array-flatten": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+          "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
+          "dev": true
+        },
+        "qs": {
+          "version": "6.7.0",
+          "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
+          "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
+          "dev": true
+        }
+      }
+    },
+    "extend": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
+    },
+    "extend-shallow": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+      "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+      "requires": {
+        "is-extendable": "^0.1.0"
+      }
+    },
+    "external-editor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
+      "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
+      "dev": true,
+      "requires": {
+        "chardet": "^0.7.0",
+        "iconv-lite": "^0.4.24",
+        "tmp": "^0.0.33"
+      }
+    },
+    "extglob": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+      "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+      "dev": true,
+      "requires": {
+        "array-unique": "^0.3.2",
+        "define-property": "^1.0.0",
+        "expand-brackets": "^2.1.4",
+        "extend-shallow": "^2.0.1",
+        "fragment-cache": "^0.2.1",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^1.0.0"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "^1.0.0",
+            "is-data-descriptor": "^1.0.0",
+            "kind-of": "^6.0.2"
+          }
+        }
+      }
+    },
+    "extsprintf": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+      "dev": true
+    },
+    "fast-deep-equal": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+      "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
+      "dev": true
+    },
+    "fast-json-stable-stringify": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
+      "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
+      "dev": true
+    },
+    "fast-levenshtein": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
+    },
+    "fastparse": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz",
+      "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==",
+      "dev": true
+    },
+    "faye-websocket": {
+      "version": "0.10.0",
+      "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz",
+      "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=",
+      "dev": true,
+      "requires": {
+        "websocket-driver": ">=0.5.1"
+      }
+    },
+    "fd-slicer": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
+      "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=",
+      "requires": {
+        "pend": "~1.2.0"
+      }
+    },
+    "figgy-pudding": {
+      "version": "3.5.2",
+      "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz",
+      "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==",
+      "dev": true
+    },
+    "figures": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
+      "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
+      "dev": true,
+      "requires": {
+        "escape-string-regexp": "^1.0.5"
+      }
+    },
+    "file-loader": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-4.2.0.tgz",
+      "integrity": "sha512-+xZnaK5R8kBJrHK0/6HRlrKNamvVS5rjyuju+rnyxRGuwUJwpAMsVzUl5dz6rK8brkzjV6JpcFNjp6NqV0g1OQ==",
+      "dev": true,
+      "requires": {
+        "loader-utils": "^1.2.3",
+        "schema-utils": "^2.0.0"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "6.12.3",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz",
+          "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==",
+          "dev": true,
+          "requires": {
+            "fast-deep-equal": "^3.1.1",
+            "fast-json-stable-stringify": "^2.0.0",
+            "json-schema-traverse": "^0.4.1",
+            "uri-js": "^4.2.2"
+          }
+        },
+        "fast-deep-equal": {
+          "version": "3.1.3",
+          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+          "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+          "dev": true
+        },
+        "schema-utils": {
+          "version": "2.7.0",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
+          "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
+          "dev": true,
+          "requires": {
+            "@types/json-schema": "^7.0.4",
+            "ajv": "^6.12.2",
+            "ajv-keywords": "^3.4.1"
+          }
+        }
+      }
+    },
+    "file-uri-to-path": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+      "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
+    },
+    "fileset": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz",
+      "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=",
+      "dev": true,
+      "requires": {
+        "glob": "^7.0.3",
+        "minimatch": "^3.0.3"
+      }
+    },
+    "fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "dev": true,
+      "requires": {
+        "to-regex-range": "^5.0.1"
+      }
+    },
+    "finalhandler": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+      "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+      "dev": true,
+      "requires": {
+        "debug": "2.6.9",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "on-finished": "~2.3.0",
+        "parseurl": "~1.3.3",
+        "statuses": "~1.5.0",
+        "unpipe": "~1.0.0"
+      }
+    },
+    "find-cache-dir": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.0.0.tgz",
+      "integrity": "sha512-t7ulV1fmbxh5G9l/492O1p5+EBbr3uwpt6odhFTMc+nWyhmbloe+ja9BZ8pIBtqFWhOmCWVjx+pTW4zDkFoclw==",
+      "dev": true,
+      "requires": {
+        "commondir": "^1.0.1",
+        "make-dir": "^3.0.0",
+        "pkg-dir": "^4.1.0"
+      },
+      "dependencies": {
+        "find-up": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+          "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+          "dev": true,
+          "requires": {
+            "locate-path": "^5.0.0",
+            "path-exists": "^4.0.0"
+          }
+        },
+        "locate-path": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+          "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+          "dev": true,
+          "requires": {
+            "p-locate": "^4.1.0"
+          }
+        },
+        "make-dir": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+          "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+          "dev": true,
+          "requires": {
+            "semver": "^6.0.0"
+          }
+        },
+        "p-locate": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+          "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+          "dev": true,
+          "requires": {
+            "p-limit": "^2.2.0"
+          }
+        },
+        "path-exists": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+          "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+          "dev": true
+        },
+        "pkg-dir": {
+          "version": "4.2.0",
+          "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+          "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+          "dev": true,
+          "requires": {
+            "find-up": "^4.0.0"
+          }
+        },
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+          "dev": true
+        }
+      }
+    },
+    "find-up": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+      "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+      "dev": true,
+      "requires": {
+        "locate-path": "^3.0.0"
+      }
+    },
+    "flatted": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
+      "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==",
+      "dev": true
+    },
+    "flush-write-stream": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
+      "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==",
+      "dev": true,
+      "requires": {
+        "inherits": "^2.0.3",
+        "readable-stream": "^2.3.6"
+      }
+    },
+    "flushwritable": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/flushwritable/-/flushwritable-1.0.0.tgz",
+      "integrity": "sha1-PjKNj95BKtR+c44751C00pAENJg="
+    },
+    "follow-redirects": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz",
+      "integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==",
+      "dev": true
+    },
+    "for-in": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+      "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
+      "dev": true
+    },
+    "forever-agent": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+      "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+      "dev": true
+    },
+    "form-data": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+      "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+      "dev": true,
+      "requires": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.6",
+        "mime-types": "^2.1.12"
+      }
+    },
+    "formstream": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/formstream/-/formstream-1.1.0.tgz",
+      "integrity": "sha1-UfOXDyYTbrCtRDBN5M67UCB7RHk=",
+      "requires": {
+        "destroy": "^1.0.4",
+        "mime": "^1.3.4",
+        "pause-stream": "~0.0.11"
+      },
+      "dependencies": {
+        "mime": {
+          "version": "1.6.0",
+          "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+          "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
+        }
+      }
+    },
+    "forwarded": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
+      "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=",
+      "dev": true
+    },
+    "fragment-cache": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+      "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+      "dev": true,
+      "requires": {
+        "map-cache": "^0.2.2"
+      }
+    },
+    "fresh": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+      "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
+      "dev": true
+    },
+    "from2": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
+      "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
+      "dev": true,
+      "requires": {
+        "inherits": "^2.0.1",
+        "readable-stream": "^2.0.0"
+      }
+    },
+    "fs-constants": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+      "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
+    },
+    "fs-extra": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.2.tgz",
+      "integrity": "sha1-+RcExT0bRh+JNFKwwwfZmXZHq2s=",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.1.2",
+        "jsonfile": "^4.0.0",
+        "universalify": "^0.1.0"
+      }
+    },
+    "fs-minipass": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+      "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+      "dev": true,
+      "requires": {
+        "minipass": "^3.0.0"
+      }
+    },
+    "fs-write-stream-atomic": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
+      "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.1.2",
+        "iferr": "^0.1.5",
+        "imurmurhash": "^0.1.4",
+        "readable-stream": "1 || 2"
+      }
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+    },
+    "fsevents": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
+      "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+      "dev": true,
+      "optional": true
+    },
+    "ftp": {
+      "version": "0.3.10",
+      "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz",
+      "integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=",
+      "requires": {
+        "readable-stream": "1.1.x",
+        "xregexp": "2.0.0"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+        },
+        "readable-stream": {
+          "version": "1.1.14",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+          "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.1",
+            "isarray": "0.0.1",
+            "string_decoder": "~0.10.x"
+          }
+        },
+        "string_decoder": {
+          "version": "0.10.31",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+        }
+      }
+    },
+    "function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+      "dev": true
+    },
+    "genfun": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz",
+      "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==",
+      "dev": true
+    },
+    "get-caller-file": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
+      "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
+      "dev": true
+    },
+    "get-ready": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/get-ready/-/get-ready-1.0.0.tgz",
+      "integrity": "sha1-+RgX8emt7P6hOlYq38jeiDqzR4I="
+    },
+    "get-stream": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+      "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+      "dev": true,
+      "requires": {
+        "pump": "^3.0.0"
+      }
+    },
+    "get-uri": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-2.0.4.tgz",
+      "integrity": "sha512-v7LT/s8kVjs+Tx0ykk1I+H/rbpzkHvuIq87LmeXptcf5sNWm9uQiwjNAt94SJPA1zOlCntmnOlJvVWKmzsxG8Q==",
+      "requires": {
+        "data-uri-to-buffer": "1",
+        "debug": "2",
+        "extend": "~3.0.2",
+        "file-uri-to-path": "1",
+        "ftp": "~0.3.10",
+        "readable-stream": "2"
+      }
+    },
+    "get-value": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+      "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
+      "dev": true
+    },
+    "getpass": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+      "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+      "dev": true,
+      "requires": {
+        "assert-plus": "^1.0.0"
+      }
+    },
+    "glob": {
+      "version": "7.1.6",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+      "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+      "requires": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.4",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      }
+    },
+    "glob-parent": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+      "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+      "dev": true,
+      "requires": {
+        "is-glob": "^3.1.0",
+        "path-dirname": "^1.0.0"
+      },
+      "dependencies": {
+        "is-glob": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+          "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+          "dev": true,
+          "requires": {
+            "is-extglob": "^2.1.0"
+          }
+        }
+      }
+    },
+    "globals": {
+      "version": "11.12.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+      "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+      "dev": true
+    },
+    "globby": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz",
+      "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=",
+      "dev": true,
+      "requires": {
+        "array-union": "^1.0.1",
+        "dir-glob": "^2.0.0",
+        "glob": "^7.1.2",
+        "ignore": "^3.3.5",
+        "pify": "^3.0.0",
+        "slash": "^1.0.0"
+      },
+      "dependencies": {
+        "pify": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+          "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+          "dev": true
+        }
+      }
+    },
+    "graceful-fs": {
+      "version": "4.2.4",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+      "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+      "dev": true
+    },
+    "handle-thing": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
+      "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==",
+      "dev": true
+    },
+    "har-schema": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
+      "dev": true
+    },
+    "har-validator": {
+      "version": "5.1.5",
+      "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
+      "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
+      "dev": true,
+      "requires": {
+        "ajv": "^6.12.3",
+        "har-schema": "^2.0.0"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "6.12.3",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz",
+          "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==",
+          "dev": true,
+          "requires": {
+            "fast-deep-equal": "^3.1.1",
+            "fast-json-stable-stringify": "^2.0.0",
+            "json-schema-traverse": "^0.4.1",
+            "uri-js": "^4.2.2"
+          }
+        },
+        "fast-deep-equal": {
+          "version": "3.1.3",
+          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+          "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+          "dev": true
+        }
+      }
+    },
+    "has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+      "dev": true,
+      "requires": {
+        "function-bind": "^1.1.1"
+      }
+    },
+    "has-ansi": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+      "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+      "dev": true,
+      "requires": {
+        "ansi-regex": "^2.0.0"
+      }
+    },
+    "has-binary2": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
+      "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
+      "dev": true,
+      "requires": {
+        "isarray": "2.0.1"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+          "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=",
+          "dev": true
+        }
+      }
+    },
+    "has-cors": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
+      "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=",
+      "dev": true
+    },
+    "has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+      "dev": true
+    },
+    "has-symbols": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+      "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
+      "dev": true
+    },
+    "has-value": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+      "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+      "dev": true,
+      "requires": {
+        "get-value": "^2.0.6",
+        "has-values": "^1.0.0",
+        "isobject": "^3.0.0"
+      }
+    },
+    "has-values": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+      "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+      "dev": true,
+      "requires": {
+        "is-number": "^3.0.0",
+        "kind-of": "^4.0.0"
+      },
+      "dependencies": {
+        "is-number": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+          "dev": true,
+          "requires": {
+            "kind-of": "^3.0.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "dev": true,
+              "requires": {
+                "is-buffer": "^1.1.5"
+              }
+            }
+          }
+        },
+        "kind-of": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+          "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "hash-base": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
+      "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
+      "dev": true,
+      "requires": {
+        "inherits": "^2.0.4",
+        "readable-stream": "^3.6.0",
+        "safe-buffer": "^5.2.0"
+      },
+      "dependencies": {
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+          "dev": true,
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.2.1",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+          "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+          "dev": true
+        }
+      }
+    },
+    "hash.js": {
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
+      "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+      "dev": true,
+      "requires": {
+        "inherits": "^2.0.3",
+        "minimalistic-assert": "^1.0.1"
+      }
+    },
+    "hex-color-regex": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
+      "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==",
+      "dev": true
+    },
+    "hmac-drbg": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+      "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
+      "dev": true,
+      "requires": {
+        "hash.js": "^1.0.3",
+        "minimalistic-assert": "^1.0.0",
+        "minimalistic-crypto-utils": "^1.0.1"
+      }
+    },
+    "hosted-git-info": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.5.tgz",
+      "integrity": "sha512-i4dpK6xj9BIpVOTboXIlKG9+8HMKggcrMX7WA24xZtKwX0TPelq/rbaS5rCKeNX8sJXZJGdSxpnEGtta+wismQ==",
+      "dev": true,
+      "requires": {
+        "lru-cache": "^6.0.0"
+      },
+      "dependencies": {
+        "lru-cache": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+          "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+          "dev": true,
+          "requires": {
+            "yallist": "^4.0.0"
+          }
+        },
+        "yallist": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+          "dev": true
+        }
+      }
+    },
+    "hpack.js": {
+      "version": "2.1.6",
+      "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
+      "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=",
+      "dev": true,
+      "requires": {
+        "inherits": "^2.0.1",
+        "obuf": "^1.0.0",
+        "readable-stream": "^2.0.1",
+        "wbuf": "^1.1.0"
+      }
+    },
+    "hsl-regex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz",
+      "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=",
+      "dev": true
+    },
+    "hsla-regex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz",
+      "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=",
+      "dev": true
+    },
+    "html-comment-regex": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz",
+      "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==",
+      "dev": true
+    },
+    "html-entities": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz",
+      "integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==",
+      "dev": true
+    },
+    "html-escaper": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
+      "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+      "dev": true
+    },
+    "http-cache-semantics": {
+      "version": "3.8.1",
+      "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz",
+      "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==",
+      "dev": true
+    },
+    "http-deceiver": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz",
+      "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=",
+      "dev": true
+    },
+    "http-errors": {
+      "version": "1.7.3",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz",
+      "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==",
+      "requires": {
+        "depd": "~1.1.2",
+        "inherits": "2.0.4",
+        "setprototypeof": "1.1.1",
+        "statuses": ">= 1.5.0 < 2",
+        "toidentifier": "1.0.0"
+      }
+    },
+    "http-parser-js": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.2.tgz",
+      "integrity": "sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ==",
+      "dev": true
+    },
+    "http-proxy": {
+      "version": "1.18.1",
+      "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
+      "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
+      "dev": true,
+      "requires": {
+        "eventemitter3": "^4.0.0",
+        "follow-redirects": "^1.0.0",
+        "requires-port": "^1.0.0"
+      }
+    },
+    "http-proxy-agent": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz",
+      "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==",
+      "requires": {
+        "agent-base": "4",
+        "debug": "3.1.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        }
+      }
+    },
+    "http-proxy-middleware": {
+      "version": "0.19.1",
+      "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz",
+      "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==",
+      "dev": true,
+      "requires": {
+        "http-proxy": "^1.17.0",
+        "is-glob": "^4.0.0",
+        "lodash": "^4.17.11",
+        "micromatch": "^3.1.10"
+      }
+    },
+    "http-signature": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+      "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+      "dev": true,
+      "requires": {
+        "assert-plus": "^1.0.0",
+        "jsprim": "^1.2.2",
+        "sshpk": "^1.7.0"
+      }
+    },
+    "https-browserify": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
+      "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
+      "dev": true
+    },
+    "https-proxy-agent": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz",
+      "integrity": "sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg==",
+      "requires": {
+        "agent-base": "^4.3.0",
+        "debug": "^3.1.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "3.2.6",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+          "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        }
+      }
+    },
+    "humanize-ms": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
+      "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=",
+      "requires": {
+        "ms": "^2.0.0"
+      }
+    },
+    "iconv-lite": {
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "requires": {
+        "safer-buffer": ">= 2.1.2 < 3"
+      }
+    },
+    "ieee754": {
+      "version": "1.1.13",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
+      "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==",
+      "dev": true
+    },
+    "iferr": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
+      "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=",
+      "dev": true
+    },
+    "ignore": {
+      "version": "3.3.10",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz",
+      "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==",
+      "dev": true
+    },
+    "ignore-walk": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz",
+      "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==",
+      "dev": true,
+      "requires": {
+        "minimatch": "^3.0.4"
+      }
+    },
+    "image-size": {
+      "version": "0.5.5",
+      "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
+      "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=",
+      "dev": true,
+      "optional": true
+    },
+    "immediate": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+      "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=",
+      "dev": true
+    },
+    "import-cwd": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz",
+      "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=",
+      "dev": true,
+      "requires": {
+        "import-from": "^2.1.0"
+      }
+    },
+    "import-fresh": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
+      "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
+      "dev": true,
+      "requires": {
+        "caller-path": "^2.0.0",
+        "resolve-from": "^3.0.0"
+      }
+    },
+    "import-from": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz",
+      "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=",
+      "dev": true,
+      "requires": {
+        "resolve-from": "^3.0.0"
+      }
+    },
+    "import-local": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",
+      "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==",
+      "dev": true,
+      "requires": {
+        "pkg-dir": "^3.0.0",
+        "resolve-cwd": "^2.0.0"
+      }
+    },
+    "imurmurhash": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+      "dev": true
+    },
+    "indent-string": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+      "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+      "dev": true
+    },
+    "indexes-of": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
+      "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
+      "dev": true
+    },
+    "indexof": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
+      "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=",
+      "dev": true
+    },
+    "infer-owner": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
+      "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==",
+      "dev": true
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "requires": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+    },
+    "ini": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
+      "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
+      "dev": true
+    },
+    "inquirer": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.0.tgz",
+      "integrity": "sha512-rSdC7zelHdRQFkWnhsMu2+2SO41mpv2oF2zy4tMhmiLWkcKbOAs87fWAJhVXttKVwhdZvymvnuM95EyEXg2/tQ==",
+      "dev": true,
+      "requires": {
+        "ansi-escapes": "^4.2.1",
+        "chalk": "^2.4.2",
+        "cli-cursor": "^3.1.0",
+        "cli-width": "^2.0.0",
+        "external-editor": "^3.0.3",
+        "figures": "^3.0.0",
+        "lodash": "^4.17.15",
+        "mute-stream": "0.0.8",
+        "run-async": "^2.2.0",
+        "rxjs": "^6.4.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^5.1.0",
+        "through": "^2.3.6"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+          "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+          "dev": true
+        },
+        "is-fullwidth-code-point": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+          "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+          "dev": true
+        },
+        "string-width": {
+          "version": "4.2.0",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
+          "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+          "dev": true,
+          "requires": {
+            "emoji-regex": "^8.0.0",
+            "is-fullwidth-code-point": "^3.0.0",
+            "strip-ansi": "^6.0.0"
+          },
+          "dependencies": {
+            "strip-ansi": {
+              "version": "6.0.0",
+              "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+              "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+              "dev": true,
+              "requires": {
+                "ansi-regex": "^5.0.0"
+              }
+            }
+          }
+        },
+        "strip-ansi": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+          "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^4.1.0"
+          },
+          "dependencies": {
+            "ansi-regex": {
+              "version": "4.1.0",
+              "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+              "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+              "dev": true
+            }
+          }
+        }
+      }
+    },
+    "internal-ip": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz",
+      "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==",
+      "dev": true,
+      "requires": {
+        "default-gateway": "^4.2.0",
+        "ipaddr.js": "^1.9.0"
+      }
+    },
+    "invariant": {
+      "version": "2.2.4",
+      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+      "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+      "dev": true,
+      "requires": {
+        "loose-envify": "^1.0.0"
+      }
+    },
+    "invert-kv": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
+      "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
+      "dev": true
+    },
+    "ip": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
+      "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
+    },
+    "ip-regex": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
+      "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=",
+      "dev": true
+    },
+    "ipaddr.js": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+      "dev": true
+    },
+    "is-absolute-url": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz",
+      "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=",
+      "dev": true
+    },
+    "is-accessor-descriptor": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+      "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+      "dev": true,
+      "requires": {
+        "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "is-arguments": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz",
+      "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==",
+      "dev": true
+    },
+    "is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+      "dev": true
+    },
+    "is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "dev": true,
+      "requires": {
+        "binary-extensions": "^2.0.0"
+      }
+    },
+    "is-buffer": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+      "dev": true
+    },
+    "is-callable": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz",
+      "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==",
+      "dev": true
+    },
+    "is-class-hotfix": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/is-class-hotfix/-/is-class-hotfix-0.0.6.tgz",
+      "integrity": "sha512-0n+pzCC6ICtVr/WXnN2f03TK/3BfXY7me4cjCAqT8TYXEl0+JBRoqBo94JJHXcyDSLUeWbNX8Fvy5g5RJdAstQ=="
+    },
+    "is-color-stop": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz",
+      "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=",
+      "dev": true,
+      "requires": {
+        "css-color-names": "^0.0.4",
+        "hex-color-regex": "^1.1.0",
+        "hsl-regex": "^1.0.0",
+        "hsla-regex": "^1.0.0",
+        "rgb-regex": "^1.0.1",
+        "rgba-regex": "^1.0.0"
+      }
+    },
+    "is-data-descriptor": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+      "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+      "dev": true,
+      "requires": {
+        "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "is-date-object": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
+      "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==",
+      "dev": true
+    },
+    "is-descriptor": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+      "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+      "dev": true,
+      "requires": {
+        "is-accessor-descriptor": "^0.1.6",
+        "is-data-descriptor": "^0.1.4",
+        "kind-of": "^5.0.0"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "5.1.0",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+          "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+          "dev": true
+        }
+      }
+    },
+    "is-directory": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
+      "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=",
+      "dev": true
+    },
+    "is-docker": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.0.tgz",
+      "integrity": "sha512-mB2WygGsSeoXtLKpSYzP6sa0Z9DyU9ZyKlnvuZWxCociaI0qsF8u12sR72DFTX236g1u6oWSWYFuUk09nGQEjg==",
+      "dev": true
+    },
+    "is-extendable": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+      "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
+    },
+    "is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+      "dev": true
+    },
+    "is-fullwidth-code-point": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+      "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+      "dev": true
+    },
+    "is-glob": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+      "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+      "dev": true,
+      "requires": {
+        "is-extglob": "^2.1.1"
+      }
+    },
+    "is-interactive": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
+      "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
+      "dev": true
+    },
+    "is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true
+    },
+    "is-obj": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
+      "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
+      "dev": true
+    },
+    "is-path-cwd": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz",
+      "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==",
+      "dev": true
+    },
+    "is-path-in-cwd": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz",
+      "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==",
+      "dev": true,
+      "requires": {
+        "is-path-inside": "^2.1.0"
+      }
+    },
+    "is-path-inside": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz",
+      "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==",
+      "dev": true,
+      "requires": {
+        "path-is-inside": "^1.0.2"
+      }
+    },
+    "is-plain-obj": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
+      "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
+      "dev": true
+    },
+    "is-plain-object": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+      "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+      "dev": true,
+      "requires": {
+        "isobject": "^3.0.1"
+      }
+    },
+    "is-regex": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz",
+      "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==",
+      "dev": true,
+      "requires": {
+        "has-symbols": "^1.0.1"
+      }
+    },
+    "is-resolvable": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz",
+      "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==",
+      "dev": true
+    },
+    "is-stream": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+      "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
+      "dev": true
+    },
+    "is-svg": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz",
+      "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==",
+      "dev": true,
+      "requires": {
+        "html-comment-regex": "^1.1.0"
+      }
+    },
+    "is-symbol": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
+      "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
+      "dev": true,
+      "requires": {
+        "has-symbols": "^1.0.1"
+      }
+    },
+    "is-type-of": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/is-type-of/-/is-type-of-1.2.1.tgz",
+      "integrity": "sha512-uK0kyX9LZYhSDS7H2sVJQJop1UnWPWmo5RvR3q2kFH6AUHYs7sOrVg0b4nyBHw29kRRNFofYN/JbHZDlHiItTA==",
+      "requires": {
+        "core-util-is": "^1.0.2",
+        "is-class-hotfix": "~0.0.6",
+        "isstream": "~0.1.2"
+      }
+    },
+    "is-typedarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+      "dev": true
+    },
+    "is-windows": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+      "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+      "dev": true
+    },
+    "is-wsl": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+      "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+      "dev": true,
+      "requires": {
+        "is-docker": "^2.0.0"
+      }
+    },
+    "isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+    },
+    "isbinaryfile": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz",
+      "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==",
+      "dev": true,
+      "requires": {
+        "buffer-alloc": "^1.2.0"
+      }
+    },
+    "isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+      "dev": true
+    },
+    "isobject": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+      "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+      "dev": true
+    },
+    "isstream": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
+    },
+    "istanbul-api": {
+      "version": "2.1.7",
+      "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.7.tgz",
+      "integrity": "sha512-LYTOa2UrYFyJ/aSczZi/6lBykVMjCCvUmT64gOe+jPZFy4w6FYfPGqFT2IiQ2BxVHHDOvCD7qrIXb0EOh4uGWw==",
+      "dev": true,
+      "requires": {
+        "async": "^2.6.2",
+        "compare-versions": "^3.4.0",
+        "fileset": "^2.0.3",
+        "istanbul-lib-coverage": "^2.0.5",
+        "istanbul-lib-hook": "^2.0.7",
+        "istanbul-lib-instrument": "^3.3.0",
+        "istanbul-lib-report": "^2.0.8",
+        "istanbul-lib-source-maps": "^3.0.6",
+        "istanbul-reports": "^2.2.5",
+        "js-yaml": "^3.13.1",
+        "make-dir": "^2.1.0",
+        "minimatch": "^3.0.4",
+        "once": "^1.4.0"
+      },
+      "dependencies": {
+        "istanbul-lib-coverage": {
+          "version": "2.0.5",
+          "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz",
+          "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==",
+          "dev": true
+        },
+        "istanbul-lib-instrument": {
+          "version": "3.3.0",
+          "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz",
+          "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==",
+          "dev": true,
+          "requires": {
+            "@babel/generator": "^7.4.0",
+            "@babel/parser": "^7.4.3",
+            "@babel/template": "^7.4.0",
+            "@babel/traverse": "^7.4.3",
+            "@babel/types": "^7.4.0",
+            "istanbul-lib-coverage": "^2.0.5",
+            "semver": "^6.0.0"
+          }
+        },
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+          "dev": true
+        }
+      }
+    },
+    "istanbul-lib-coverage": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz",
+      "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==",
+      "dev": true
+    },
+    "istanbul-lib-hook": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz",
+      "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==",
+      "dev": true,
+      "requires": {
+        "append-transform": "^1.0.0"
+      }
+    },
+    "istanbul-lib-instrument": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz",
+      "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==",
+      "dev": true,
+      "requires": {
+        "@babel/core": "^7.7.5",
+        "@istanbuljs/schema": "^0.1.2",
+        "istanbul-lib-coverage": "^3.0.0",
+        "semver": "^6.3.0"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+          "dev": true
+        }
+      }
+    },
+    "istanbul-lib-report": {
+      "version": "2.0.8",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz",
+      "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==",
+      "dev": true,
+      "requires": {
+        "istanbul-lib-coverage": "^2.0.5",
+        "make-dir": "^2.1.0",
+        "supports-color": "^6.1.0"
+      },
+      "dependencies": {
+        "istanbul-lib-coverage": {
+          "version": "2.0.5",
+          "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz",
+          "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+          "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        }
+      }
+    },
+    "istanbul-lib-source-maps": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz",
+      "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==",
+      "dev": true,
+      "requires": {
+        "debug": "^4.1.1",
+        "istanbul-lib-coverage": "^2.0.5",
+        "make-dir": "^2.1.0",
+        "rimraf": "^2.6.3",
+        "source-map": "^0.6.1"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+          "dev": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "istanbul-lib-coverage": {
+          "version": "2.0.5",
+          "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz",
+          "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==",
+          "dev": true
+        }
+      }
+    },
+    "istanbul-reports": {
+      "version": "2.2.7",
+      "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz",
+      "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==",
+      "dev": true,
+      "requires": {
+        "html-escaper": "^2.0.0"
+      }
+    },
+    "jasmine": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz",
+      "integrity": "sha1-awicChFXax8W3xG4AUbZHU6Lij4=",
+      "dev": true,
+      "requires": {
+        "exit": "^0.1.2",
+        "glob": "^7.0.6",
+        "jasmine-core": "~2.8.0"
+      },
+      "dependencies": {
+        "jasmine-core": {
+          "version": "2.8.0",
+          "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz",
+          "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=",
+          "dev": true
+        }
+      }
+    },
+    "jasmine-core": {
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.5.0.tgz",
+      "integrity": "sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==",
+      "dev": true
+    },
+    "jasmine-spec-reporter": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz",
+      "integrity": "sha512-FZBoZu7VE5nR7Nilzy+Np8KuVIOxF4oXDPDknehCYBDE080EnlPu0afdZNmpGDBRCUBv3mj5qgqCRmk6W/K8vg==",
+      "dev": true,
+      "requires": {
+        "colors": "1.1.2"
+      }
+    },
+    "jasminewd2": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz",
+      "integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=",
+      "dev": true
+    },
+    "jest-worker": {
+      "version": "24.9.0",
+      "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz",
+      "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==",
+      "dev": true,
+      "requires": {
+        "merge-stream": "^2.0.0",
+        "supports-color": "^6.1.0"
+      },
+      "dependencies": {
+        "supports-color": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+          "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        }
+      }
+    },
+    "js-base64": {
+      "version": "2.6.4",
+      "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz",
+      "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ=="
+    },
+    "js-levenshtein": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz",
+      "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==",
+      "dev": true
+    },
+    "js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+      "dev": true
+    },
+    "js-yaml": {
+      "version": "3.14.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
+      "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
+      "dev": true,
+      "requires": {
+        "argparse": "^1.0.7",
+        "esprima": "^4.0.0"
+      },
+      "dependencies": {
+        "esprima": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+          "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+          "dev": true
+        }
+      }
+    },
+    "jsbn": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+      "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+      "dev": true
+    },
+    "jsesc": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+      "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+      "dev": true
+    },
+    "json-parse-better-errors": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+      "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+      "dev": true
+    },
+    "json-schema": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
+      "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
+      "dev": true
+    },
+    "json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true
+    },
+    "json-stringify-safe": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+      "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+      "dev": true
+    },
+    "json3": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz",
+      "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==",
+      "dev": true
+    },
+    "json5": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+      "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+      "dev": true,
+      "requires": {
+        "minimist": "^1.2.0"
+      }
+    },
+    "jsonfile": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+      "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "jsonparse": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+      "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=",
+      "dev": true
+    },
+    "jsprim": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
+      "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+      "dev": true,
+      "requires": {
+        "assert-plus": "1.0.0",
+        "extsprintf": "1.3.0",
+        "json-schema": "0.2.3",
+        "verror": "1.10.0"
+      }
+    },
+    "jstoxml": {
+      "version": "0.2.4",
+      "resolved": "https://registry.npmjs.org/jstoxml/-/jstoxml-0.2.4.tgz",
+      "integrity": "sha1-/z+2eFaIOgMpU8fOjOdIYhD0hEc="
+    },
+    "jszip": {
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.5.0.tgz",
+      "integrity": "sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==",
+      "dev": true,
+      "requires": {
+        "lie": "~3.3.0",
+        "pako": "~1.0.2",
+        "readable-stream": "~2.3.6",
+        "set-immediate-shim": "~1.0.1"
+      }
+    },
+    "karma": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/karma/-/karma-4.3.0.tgz",
+      "integrity": "sha512-NSPViHOt+RW38oJklvYxQC4BSQsv737oQlr/r06pCM+slDOr4myuI1ivkRmp+3dVpJDfZt2DmaPJ2wkx+ZZuMQ==",
+      "dev": true,
+      "requires": {
+        "bluebird": "^3.3.0",
+        "body-parser": "^1.16.1",
+        "braces": "^3.0.2",
+        "chokidar": "^3.0.0",
+        "colors": "^1.1.0",
+        "connect": "^3.6.0",
+        "core-js": "^3.1.3",
+        "di": "^0.0.1",
+        "dom-serialize": "^2.2.0",
+        "flatted": "^2.0.0",
+        "glob": "^7.1.1",
+        "graceful-fs": "^4.1.2",
+        "http-proxy": "^1.13.0",
+        "isbinaryfile": "^3.0.0",
+        "lodash": "^4.17.14",
+        "log4js": "^4.0.0",
+        "mime": "^2.3.1",
+        "minimatch": "^3.0.2",
+        "optimist": "^0.6.1",
+        "qjobs": "^1.1.4",
+        "range-parser": "^1.2.0",
+        "rimraf": "^2.6.0",
+        "safe-buffer": "^5.0.1",
+        "socket.io": "2.1.1",
+        "source-map": "^0.6.1",
+        "tmp": "0.0.33",
+        "useragent": "2.3.0"
+      }
+    },
+    "karma-chrome-launcher": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz",
+      "integrity": "sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg==",
+      "dev": true,
+      "requires": {
+        "which": "^1.2.1"
+      }
+    },
+    "karma-coverage-istanbul-reporter": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-2.1.1.tgz",
+      "integrity": "sha512-CH8lTi8+kKXGvrhy94+EkEMldLCiUA0xMOiL31vvli9qK0T+qcXJAwWBRVJWnVWxYkTmyWar8lPz63dxX6/z1A==",
+      "dev": true,
+      "requires": {
+        "istanbul-api": "^2.1.6",
+        "minimatch": "^3.0.4"
+      }
+    },
+    "karma-jasmine": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-2.0.1.tgz",
+      "integrity": "sha512-iuC0hmr9b+SNn1DaUD2QEYtUxkS1J+bSJSn7ejdEexs7P8EYvA1CWkEdrDQ+8jVH3AgWlCNwjYsT1chjcNW9lA==",
+      "dev": true,
+      "requires": {
+        "jasmine-core": "^3.3"
+      }
+    },
+    "karma-jasmine-html-reporter": {
+      "version": "1.5.4",
+      "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.5.4.tgz",
+      "integrity": "sha512-PtilRLno5O6wH3lDihRnz0Ba8oSn0YUJqKjjux1peoYGwo0AQqrWRbdWk/RLzcGlb+onTyXAnHl6M+Hu3UxG/Q==",
+      "dev": true
+    },
+    "karma-source-map-support": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz",
+      "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==",
+      "dev": true,
+      "requires": {
+        "source-map-support": "^0.5.5"
+      }
+    },
+    "killable": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
+      "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==",
+      "dev": true
+    },
+    "kind-of": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+      "dev": true
+    },
+    "ko-sleep": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/ko-sleep/-/ko-sleep-1.0.3.tgz",
+      "integrity": "sha1-KKKgoUhei39BX/SI3uF9JHiKsII=",
+      "requires": {
+        "ms": "^2.0.0"
+      }
+    },
+    "lcid": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
+      "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
+      "dev": true,
+      "requires": {
+        "invert-kv": "^2.0.0"
+      }
+    },
+    "less": {
+      "version": "3.10.3",
+      "resolved": "https://registry.npmjs.org/less/-/less-3.10.3.tgz",
+      "integrity": "sha512-vz32vqfgmoxF1h3K4J+yKCtajH0PWmjkIFgbs5d78E/c/e+UQTnI+lWK+1eQRE95PXM2mC3rJlLSSP9VQHnaow==",
+      "dev": true,
+      "requires": {
+        "clone": "^2.1.2",
+        "errno": "^0.1.1",
+        "graceful-fs": "^4.1.2",
+        "image-size": "~0.5.0",
+        "mime": "^1.4.1",
+        "mkdirp": "^0.5.0",
+        "promise": "^7.1.1",
+        "request": "^2.83.0",
+        "source-map": "~0.6.0"
+      },
+      "dependencies": {
+        "mime": {
+          "version": "1.6.0",
+          "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+          "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+          "dev": true,
+          "optional": true
+        }
+      }
+    },
+    "less-loader": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-5.0.0.tgz",
+      "integrity": "sha512-bquCU89mO/yWLaUq0Clk7qCsKhsF/TZpJUzETRvJa9KSVEL9SO3ovCvdEHISBhrC81OwC8QSVX7E0bzElZj9cg==",
+      "dev": true,
+      "requires": {
+        "clone": "^2.1.1",
+        "loader-utils": "^1.1.0",
+        "pify": "^4.0.1"
+      }
+    },
+    "levn": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+      "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+      "requires": {
+        "prelude-ls": "~1.1.2",
+        "type-check": "~0.3.2"
+      }
+    },
+    "license-webpack-plugin": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-2.1.3.tgz",
+      "integrity": "sha512-vTSY5r9HOq4sxR2BIxdIXWKI+9n3b+DoQkhKHedB3TdSxTfXUDRxKXdAj5iejR+qNXprXsxvEu9W+zOhgGIkAw==",
+      "dev": true,
+      "requires": {
+        "@types/webpack-sources": "^0.1.5",
+        "webpack-sources": "^1.2.0"
+      }
+    },
+    "lie": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+      "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+      "dev": true,
+      "requires": {
+        "immediate": "~3.0.5"
+      }
+    },
+    "loader-runner": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz",
+      "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==",
+      "dev": true
+    },
+    "loader-utils": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
+      "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
+      "dev": true,
+      "requires": {
+        "big.js": "^5.2.2",
+        "emojis-list": "^2.0.0",
+        "json5": "^1.0.1"
+      }
+    },
+    "locate-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+      "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+      "dev": true,
+      "requires": {
+        "p-locate": "^3.0.0",
+        "path-exists": "^3.0.0"
+      }
+    },
+    "lodash": {
+      "version": "4.17.19",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
+      "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
+      "dev": true
+    },
+    "lodash.clonedeep": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+      "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
+      "dev": true
+    },
+    "lodash.memoize": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
+      "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
+      "dev": true
+    },
+    "lodash.uniq": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+      "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=",
+      "dev": true
+    },
+    "log-symbols": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
+      "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==",
+      "dev": true,
+      "requires": {
+        "chalk": "^2.4.2"
+      }
+    },
+    "log4js": {
+      "version": "4.5.1",
+      "resolved": "https://registry.npmjs.org/log4js/-/log4js-4.5.1.tgz",
+      "integrity": "sha512-EEEgFcE9bLgaYUKuozyFfytQM2wDHtXn4tAN41pkaxpNjAykv11GVdeI4tHtmPWW4Xrgh9R/2d7XYghDVjbKKw==",
+      "dev": true,
+      "requires": {
+        "date-format": "^2.0.0",
+        "debug": "^4.1.1",
+        "flatted": "^2.0.0",
+        "rfdc": "^1.1.4",
+        "streamroller": "^1.0.6"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+          "dev": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        }
+      }
+    },
+    "loglevel": {
+      "version": "1.6.8",
+      "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.8.tgz",
+      "integrity": "sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA==",
+      "dev": true
+    },
+    "loose-envify": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+      "dev": true,
+      "requires": {
+        "js-tokens": "^3.0.0 || ^4.0.0"
+      }
+    },
+    "lru-cache": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+      "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+      "requires": {
+        "yallist": "^3.0.2"
+      }
+    },
+    "magic-string": {
+      "version": "0.25.4",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.4.tgz",
+      "integrity": "sha512-oycWO9nEVAP2RVPbIoDoA4Y7LFIJ3xRYov93gAyJhZkET1tNuB0u7uWkZS2LpBWTJUWnmau/To8ECWRC+jKNfw==",
+      "dev": true,
+      "requires": {
+        "sourcemap-codec": "^1.4.4"
+      }
+    },
+    "make-dir": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+      "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+      "dev": true,
+      "requires": {
+        "pify": "^4.0.1",
+        "semver": "^5.6.0"
+      }
+    },
+    "make-error": {
+      "version": "1.3.6",
+      "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+      "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+      "dev": true
+    },
+    "make-fetch-happen": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz",
+      "integrity": "sha512-07JHC0r1ykIoruKO8ifMXu+xEU8qOXDFETylktdug6vJDACnP+HKevOu3PXyNPzFyTSlz8vrBYlBO1JZRe8Cag==",
+      "dev": true,
+      "requires": {
+        "agentkeepalive": "^3.4.1",
+        "cacache": "^12.0.0",
+        "http-cache-semantics": "^3.8.1",
+        "http-proxy-agent": "^2.1.0",
+        "https-proxy-agent": "^2.2.3",
+        "lru-cache": "^5.1.1",
+        "mississippi": "^3.0.0",
+        "node-fetch-npm": "^2.0.2",
+        "promise-retry": "^1.1.1",
+        "socks-proxy-agent": "^4.0.0",
+        "ssri": "^6.0.0"
+      },
+      "dependencies": {
+        "cacache": {
+          "version": "12.0.4",
+          "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz",
+          "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==",
+          "dev": true,
+          "requires": {
+            "bluebird": "^3.5.5",
+            "chownr": "^1.1.1",
+            "figgy-pudding": "^3.5.1",
+            "glob": "^7.1.4",
+            "graceful-fs": "^4.1.15",
+            "infer-owner": "^1.0.3",
+            "lru-cache": "^5.1.1",
+            "mississippi": "^3.0.0",
+            "mkdirp": "^0.5.1",
+            "move-concurrently": "^1.0.1",
+            "promise-inflight": "^1.0.1",
+            "rimraf": "^2.6.3",
+            "ssri": "^6.0.1",
+            "unique-filename": "^1.1.1",
+            "y18n": "^4.0.0"
+          }
+        },
+        "debug": {
+          "version": "3.2.6",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+          "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+          "dev": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "https-proxy-agent": {
+          "version": "2.2.4",
+          "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
+          "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
+          "dev": true,
+          "requires": {
+            "agent-base": "^4.3.0",
+            "debug": "^3.1.0"
+          }
+        },
+        "ssri": {
+          "version": "6.0.1",
+          "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz",
+          "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
+          "dev": true,
+          "requires": {
+            "figgy-pudding": "^3.5.1"
+          }
+        }
+      }
+    },
+    "mamacro": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz",
+      "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==",
+      "dev": true
+    },
+    "map-age-cleaner": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
+      "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==",
+      "dev": true,
+      "requires": {
+        "p-defer": "^1.0.0"
+      }
+    },
+    "map-cache": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+      "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
+      "dev": true
+    },
+    "map-visit": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+      "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+      "dev": true,
+      "requires": {
+        "object-visit": "^1.0.0"
+      }
+    },
+    "md5.js": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
+      "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
+      "dev": true,
+      "requires": {
+        "hash-base": "^3.0.0",
+        "inherits": "^2.0.1",
+        "safe-buffer": "^5.1.2"
+      }
+    },
+    "mdn-data": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz",
+      "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==",
+      "dev": true
+    },
+    "media-typer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
+      "dev": true
+    },
+    "mem": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz",
+      "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==",
+      "dev": true,
+      "requires": {
+        "map-age-cleaner": "^0.1.1",
+        "mimic-fn": "^2.0.0",
+        "p-is-promise": "^2.0.0"
+      }
+    },
+    "memory-fs": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
+      "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==",
+      "dev": true,
+      "requires": {
+        "errno": "^0.1.3",
+        "readable-stream": "^2.0.1"
+      }
+    },
+    "merge-descriptors": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+      "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
+    },
+    "merge-source-map": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz",
+      "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==",
+      "dev": true,
+      "requires": {
+        "source-map": "^0.6.1"
+      }
+    },
+    "merge-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+      "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+      "dev": true
+    },
+    "methods": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+      "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
+      "dev": true
+    },
+    "micromatch": {
+      "version": "3.1.10",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+      "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+      "dev": true,
+      "requires": {
+        "arr-diff": "^4.0.0",
+        "array-unique": "^0.3.2",
+        "braces": "^2.3.1",
+        "define-property": "^2.0.2",
+        "extend-shallow": "^3.0.2",
+        "extglob": "^2.0.4",
+        "fragment-cache": "^0.2.1",
+        "kind-of": "^6.0.2",
+        "nanomatch": "^1.2.9",
+        "object.pick": "^1.3.0",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.2"
+      },
+      "dependencies": {
+        "braces": {
+          "version": "2.3.2",
+          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+          "dev": true,
+          "requires": {
+            "arr-flatten": "^1.1.0",
+            "array-unique": "^0.3.2",
+            "extend-shallow": "^2.0.1",
+            "fill-range": "^4.0.0",
+            "isobject": "^3.0.1",
+            "repeat-element": "^1.1.2",
+            "snapdragon": "^0.8.1",
+            "snapdragon-node": "^2.0.1",
+            "split-string": "^3.0.2",
+            "to-regex": "^3.0.1"
+          },
+          "dependencies": {
+            "extend-shallow": {
+              "version": "2.0.1",
+              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+              "dev": true,
+              "requires": {
+                "is-extendable": "^0.1.0"
+              }
+            }
+          }
+        },
+        "extend-shallow": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+          "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+          "dev": true,
+          "requires": {
+            "assign-symbols": "^1.0.0",
+            "is-extendable": "^1.0.1"
+          },
+          "dependencies": {
+            "is-extendable": {
+              "version": "1.0.1",
+              "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+              "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+              "dev": true,
+              "requires": {
+                "is-plain-object": "^2.0.4"
+              }
+            }
+          }
+        },
+        "fill-range": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+          "dev": true,
+          "requires": {
+            "extend-shallow": "^2.0.1",
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1",
+            "to-regex-range": "^2.1.0"
+          },
+          "dependencies": {
+            "extend-shallow": {
+              "version": "2.0.1",
+              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+              "dev": true,
+              "requires": {
+                "is-extendable": "^0.1.0"
+              }
+            }
+          }
+        },
+        "is-number": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+          "dev": true,
+          "requires": {
+            "kind-of": "^3.0.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "dev": true,
+              "requires": {
+                "is-buffer": "^1.1.5"
+              }
+            }
+          }
+        },
+        "to-regex-range": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+          "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+          "dev": true,
+          "requires": {
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1"
+          }
+        }
+      }
+    },
+    "miller-rabin": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
+      "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
+      "dev": true,
+      "requires": {
+        "bn.js": "^4.0.0",
+        "brorand": "^1.0.1"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.11.9",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+          "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+          "dev": true
+        }
+      }
+    },
+    "mime": {
+      "version": "2.4.6",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz",
+      "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA=="
+    },
+    "mime-db": {
+      "version": "1.44.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
+      "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==",
+      "dev": true
+    },
+    "mime-types": {
+      "version": "2.1.27",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
+      "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
+      "dev": true,
+      "requires": {
+        "mime-db": "1.44.0"
+      }
+    },
+    "mimic-fn": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+      "dev": true
+    },
+    "mini-css-extract-plugin": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.0.tgz",
+      "integrity": "sha512-MNpRGbNA52q6U92i0qbVpQNsgk7LExy41MdAlG84FeytfDOtRIf/mCHdEgG8rpTKOaNKiqUnZdlptF469hxqOw==",
+      "dev": true,
+      "requires": {
+        "loader-utils": "^1.1.0",
+        "normalize-url": "1.9.1",
+        "schema-utils": "^1.0.0",
+        "webpack-sources": "^1.1.0"
+      },
+      "dependencies": {
+        "normalize-url": {
+          "version": "1.9.1",
+          "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz",
+          "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=",
+          "dev": true,
+          "requires": {
+            "object-assign": "^4.0.1",
+            "prepend-http": "^1.0.0",
+            "query-string": "^4.1.0",
+            "sort-keys": "^1.0.0"
+          }
+        }
+      }
+    },
+    "minimalistic-assert": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+      "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+      "dev": true
+    },
+    "minimalistic-crypto-utils": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+      "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
+      "dev": true
+    },
+    "minimatch": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+      "requires": {
+        "brace-expansion": "^1.1.7"
+      }
+    },
+    "minimist": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+      "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
+    },
+    "minipass": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz",
+      "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==",
+      "dev": true,
+      "requires": {
+        "yallist": "^4.0.0"
+      },
+      "dependencies": {
+        "yallist": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+          "dev": true
+        }
+      }
+    },
+    "minipass-collect": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz",
+      "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==",
+      "dev": true,
+      "requires": {
+        "minipass": "^3.0.0"
+      }
+    },
+    "minipass-flush": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz",
+      "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==",
+      "dev": true,
+      "requires": {
+        "minipass": "^3.0.0"
+      }
+    },
+    "minipass-pipeline": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
+      "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
+      "dev": true,
+      "requires": {
+        "minipass": "^3.0.0"
+      }
+    },
+    "minizlib": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
+      "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
+      "dev": true,
+      "requires": {
+        "minipass": "^2.9.0"
+      },
+      "dependencies": {
+        "minipass": {
+          "version": "2.9.0",
+          "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
+          "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
+          "dev": true,
+          "requires": {
+            "safe-buffer": "^5.1.2",
+            "yallist": "^3.0.0"
+          }
+        }
+      }
+    },
+    "mississippi": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
+      "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==",
+      "dev": true,
+      "requires": {
+        "concat-stream": "^1.5.0",
+        "duplexify": "^3.4.2",
+        "end-of-stream": "^1.1.0",
+        "flush-write-stream": "^1.0.0",
+        "from2": "^2.1.0",
+        "parallel-transform": "^1.1.0",
+        "pump": "^3.0.0",
+        "pumpify": "^1.3.3",
+        "stream-each": "^1.1.0",
+        "through2": "^2.0.0"
+      }
+    },
+    "mixin-deep": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
+      "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+      "dev": true,
+      "requires": {
+        "for-in": "^1.0.2",
+        "is-extendable": "^1.0.1"
+      },
+      "dependencies": {
+        "is-extendable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+          "dev": true,
+          "requires": {
+            "is-plain-object": "^2.0.4"
+          }
+        }
+      }
+    },
+    "mkdirp": {
+      "version": "0.5.5",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+      "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+      "requires": {
+        "minimist": "^1.2.5"
+      }
+    },
+    "move-concurrently": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
+      "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=",
+      "dev": true,
+      "requires": {
+        "aproba": "^1.1.1",
+        "copy-concurrently": "^1.0.0",
+        "fs-write-stream-atomic": "^1.0.8",
+        "mkdirp": "^0.5.1",
+        "rimraf": "^2.5.4",
+        "run-queue": "^1.0.3"
+      }
+    },
+    "ms": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+    },
+    "multicast-dns": {
+      "version": "6.2.3",
+      "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz",
+      "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==",
+      "dev": true,
+      "requires": {
+        "dns-packet": "^1.3.1",
+        "thunky": "^1.0.2"
+      }
+    },
+    "multicast-dns-service-types": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
+      "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=",
+      "dev": true
+    },
+    "mute-stream": {
+      "version": "0.0.8",
+      "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
+      "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
+      "dev": true
+    },
+    "mz": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+      "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+      "requires": {
+        "any-promise": "^1.0.0",
+        "object-assign": "^4.0.1",
+        "thenify-all": "^1.0.0"
+      }
+    },
+    "mz-modules": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/mz-modules/-/mz-modules-2.1.0.tgz",
+      "integrity": "sha512-sjk8lcRW3vrVYnZ+W+67L/2rL+jbO5K/N6PFGIcLWTiYytNr22Ah9FDXFs+AQntTM1boZcoHi5qS+CV1seuPog==",
+      "requires": {
+        "glob": "^7.1.2",
+        "ko-sleep": "^1.0.3",
+        "mkdirp": "^0.5.1",
+        "pump": "^3.0.0",
+        "rimraf": "^2.6.1"
+      }
+    },
+    "nanomatch": {
+      "version": "1.2.13",
+      "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
+      "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+      "dev": true,
+      "requires": {
+        "arr-diff": "^4.0.0",
+        "array-unique": "^0.3.2",
+        "define-property": "^2.0.2",
+        "extend-shallow": "^3.0.2",
+        "fragment-cache": "^0.2.1",
+        "is-windows": "^1.0.2",
+        "kind-of": "^6.0.2",
+        "object.pick": "^1.3.0",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+          "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+          "dev": true,
+          "requires": {
+            "assign-symbols": "^1.0.0",
+            "is-extendable": "^1.0.1"
+          }
+        },
+        "is-extendable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+          "dev": true,
+          "requires": {
+            "is-plain-object": "^2.0.4"
+          }
+        }
+      }
+    },
+    "negotiator": {
+      "version": "0.6.2",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
+      "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==",
+      "dev": true
+    },
+    "neo-async": {
+      "version": "2.6.2",
+      "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+      "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+      "dev": true
+    },
+    "netmask": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/netmask/-/netmask-1.0.6.tgz",
+      "integrity": "sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU="
+    },
+    "ng-zorro-antd": {
+      "version": "8.5.2",
+      "resolved": "https://registry.npmjs.org/ng-zorro-antd/-/ng-zorro-antd-8.5.2.tgz",
+      "integrity": "sha512-x+QJULyU1IXiAbf/lFPxYC/JUJvBGzU9UXnXSyLnMPZpdtEXodaF/L1N1CpSUr5SimLiHRe/jsVHDWkg0HqfYg==",
+      "requires": {
+        "@angular/cdk": "^8.0.0",
+        "@ant-design/icons-angular": "^8.0.2",
+        "date-fns": "^1.30.1",
+        "tslib": "^1.9.0"
+      }
+    },
+    "nice-try": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+      "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+      "dev": true
+    },
+    "node-fetch-npm": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.4.tgz",
+      "integrity": "sha512-iOuIQDWDyjhv9qSDrj9aq/klt6F9z1p2otB3AV7v3zBDcL/x+OfGsvGQZZCcMZbUf4Ujw1xGNQkjvGnVT22cKg==",
+      "dev": true,
+      "requires": {
+        "encoding": "^0.1.11",
+        "json-parse-better-errors": "^1.0.0",
+        "safe-buffer": "^5.1.1"
+      }
+    },
+    "node-forge": {
+      "version": "0.9.0",
+      "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz",
+      "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==",
+      "dev": true
+    },
+    "node-libs-browser": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz",
+      "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==",
+      "dev": true,
+      "requires": {
+        "assert": "^1.1.1",
+        "browserify-zlib": "^0.2.0",
+        "buffer": "^4.3.0",
+        "console-browserify": "^1.1.0",
+        "constants-browserify": "^1.0.0",
+        "crypto-browserify": "^3.11.0",
+        "domain-browser": "^1.1.1",
+        "events": "^3.0.0",
+        "https-browserify": "^1.0.0",
+        "os-browserify": "^0.3.0",
+        "path-browserify": "0.0.1",
+        "process": "^0.11.10",
+        "punycode": "^1.2.4",
+        "querystring-es3": "^0.2.0",
+        "readable-stream": "^2.3.3",
+        "stream-browserify": "^2.0.1",
+        "stream-http": "^2.7.2",
+        "string_decoder": "^1.0.0",
+        "timers-browserify": "^2.0.4",
+        "tty-browserify": "0.0.0",
+        "url": "^0.11.0",
+        "util": "^0.11.0",
+        "vm-browserify": "^1.0.1"
+      },
+      "dependencies": {
+        "punycode": {
+          "version": "1.4.1",
+          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+          "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+          "dev": true
+        }
+      }
+    },
+    "node-releases": {
+      "version": "1.1.60",
+      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.60.tgz",
+      "integrity": "sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA==",
+      "dev": true
+    },
+    "normalize-package-data": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+      "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+      "dev": true,
+      "requires": {
+        "hosted-git-info": "^2.1.4",
+        "resolve": "^1.10.0",
+        "semver": "2 || 3 || 4 || 5",
+        "validate-npm-package-license": "^3.0.1"
+      },
+      "dependencies": {
+        "hosted-git-info": {
+          "version": "2.8.8",
+          "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
+          "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
+          "dev": true
+        }
+      }
+    },
+    "normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true
+    },
+    "normalize-range": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
+      "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=",
+      "dev": true
+    },
+    "normalize-url": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz",
+      "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==",
+      "dev": true
+    },
+    "npm-bundled": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz",
+      "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==",
+      "dev": true,
+      "requires": {
+        "npm-normalize-package-bin": "^1.0.1"
+      }
+    },
+    "npm-normalize-package-bin": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
+      "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==",
+      "dev": true
+    },
+    "npm-package-arg": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz",
+      "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==",
+      "dev": true,
+      "requires": {
+        "hosted-git-info": "^2.7.1",
+        "osenv": "^0.1.5",
+        "semver": "^5.6.0",
+        "validate-npm-package-name": "^3.0.0"
+      },
+      "dependencies": {
+        "hosted-git-info": {
+          "version": "2.8.8",
+          "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
+          "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
+          "dev": true
+        }
+      }
+    },
+    "npm-packlist": {
+      "version": "1.4.8",
+      "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz",
+      "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==",
+      "dev": true,
+      "requires": {
+        "ignore-walk": "^3.0.1",
+        "npm-bundled": "^1.0.1",
+        "npm-normalize-package-bin": "^1.0.1"
+      }
+    },
+    "npm-pick-manifest": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-3.0.2.tgz",
+      "integrity": "sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw==",
+      "dev": true,
+      "requires": {
+        "figgy-pudding": "^3.5.1",
+        "npm-package-arg": "^6.0.0",
+        "semver": "^5.4.1"
+      }
+    },
+    "npm-registry-fetch": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.5.tgz",
+      "integrity": "sha512-yQ0/U4fYpCCqmueB2g8sc+89ckQ3eXpmU4+Yi2j5o/r0WkKvE2+Y0tK3DEILAtn2UaQTkjTHxIXe2/CSdit+/Q==",
+      "dev": true,
+      "requires": {
+        "JSONStream": "^1.3.4",
+        "bluebird": "^3.5.1",
+        "figgy-pudding": "^3.4.1",
+        "lru-cache": "^5.1.1",
+        "make-fetch-happen": "^5.0.0",
+        "npm-package-arg": "^6.1.0",
+        "safe-buffer": "^5.2.0"
+      },
+      "dependencies": {
+        "safe-buffer": {
+          "version": "5.2.1",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+          "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+          "dev": true
+        }
+      }
+    },
+    "npm-run-path": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
+      "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
+      "dev": true,
+      "requires": {
+        "path-key": "^2.0.0"
+      }
+    },
+    "nth-check": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
+      "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
+      "dev": true,
+      "requires": {
+        "boolbase": "~1.0.0"
+      }
+    },
+    "num2fraction": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz",
+      "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=",
+      "dev": true
+    },
+    "number-is-nan": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+      "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
+      "dev": true
+    },
+    "oauth-sign": {
+      "version": "0.9.0",
+      "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+      "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+      "dev": true
+    },
+    "object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+    },
+    "object-component": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
+      "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=",
+      "dev": true
+    },
+    "object-copy": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+      "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+      "dev": true,
+      "requires": {
+        "copy-descriptor": "^0.1.0",
+        "define-property": "^0.2.5",
+        "kind-of": "^3.0.3"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        },
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "object-inspect": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
+      "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==",
+      "dev": true
+    },
+    "object-is": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz",
+      "integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==",
+      "dev": true,
+      "requires": {
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.5"
+      }
+    },
+    "object-keys": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+      "dev": true
+    },
+    "object-visit": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+      "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+      "dev": true,
+      "requires": {
+        "isobject": "^3.0.0"
+      }
+    },
+    "object.assign": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+      "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+      "dev": true,
+      "requires": {
+        "define-properties": "^1.1.2",
+        "function-bind": "^1.1.1",
+        "has-symbols": "^1.0.0",
+        "object-keys": "^1.0.11"
+      }
+    },
+    "object.getownpropertydescriptors": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz",
+      "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==",
+      "dev": true,
+      "requires": {
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.0-next.1"
+      }
+    },
+    "object.pick": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+      "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
+      "dev": true,
+      "requires": {
+        "isobject": "^3.0.1"
+      }
+    },
+    "object.values": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz",
+      "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==",
+      "dev": true,
+      "requires": {
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.0-next.1",
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3"
+      }
+    },
+    "obuf": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
+      "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
+      "dev": true
+    },
+    "on-finished": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+      "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+      "dev": true,
+      "requires": {
+        "ee-first": "1.1.1"
+      }
+    },
+    "on-headers": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+      "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
+      "dev": true
+    },
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "requires": {
+        "wrappy": "1"
+      }
+    },
+    "onetime": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.1.tgz",
+      "integrity": "sha512-ZpZpjcJeugQfWsfyQlshVoowIIQ1qBGSVll4rfDq6JJVO//fesjoX808hXWfBjY+ROZgpKDI5TRSRBSoJiZ8eg==",
+      "dev": true,
+      "requires": {
+        "mimic-fn": "^2.1.0"
+      }
+    },
+    "open": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/open/-/open-7.0.0.tgz",
+      "integrity": "sha512-K6EKzYqnwQzk+/dzJAQSBORub3xlBTxMz+ntpZpH/LyCa1o6KjXhuN+2npAaI9jaSmU3R1Q8NWf4KUWcyytGsQ==",
+      "dev": true,
+      "requires": {
+        "is-wsl": "^2.1.0"
+      }
+    },
+    "opn": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz",
+      "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==",
+      "dev": true,
+      "requires": {
+        "is-wsl": "^1.1.0"
+      },
+      "dependencies": {
+        "is-wsl": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
+          "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
+          "dev": true
+        }
+      }
+    },
+    "optimist": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
+      "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
+      "dev": true,
+      "requires": {
+        "minimist": "~0.0.1",
+        "wordwrap": "~0.0.2"
+      },
+      "dependencies": {
+        "minimist": {
+          "version": "0.0.10",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
+          "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=",
+          "dev": true
+        }
+      }
+    },
+    "optionator": {
+      "version": "0.8.3",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+      "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+      "requires": {
+        "deep-is": "~0.1.3",
+        "fast-levenshtein": "~2.0.6",
+        "levn": "~0.3.0",
+        "prelude-ls": "~1.1.2",
+        "type-check": "~0.3.2",
+        "word-wrap": "~1.2.3"
+      }
+    },
+    "ora": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/ora/-/ora-4.0.2.tgz",
+      "integrity": "sha512-YUOZbamht5mfLxPmk4M35CD/5DuOkAacxlEUbStVXpBAt4fyhBf+vZHI/HRkI++QUp3sNoeA2Gw4C+hi4eGSig==",
+      "dev": true,
+      "requires": {
+        "chalk": "^2.4.2",
+        "cli-cursor": "^3.1.0",
+        "cli-spinners": "^2.2.0",
+        "is-interactive": "^1.0.0",
+        "log-symbols": "^3.0.0",
+        "strip-ansi": "^5.2.0",
+        "wcwidth": "^1.0.1"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+          "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+          "dev": true
+        },
+        "strip-ansi": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+          "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^4.1.0"
+          }
+        }
+      }
+    },
+    "original": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz",
+      "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==",
+      "dev": true,
+      "requires": {
+        "url-parse": "^1.4.3"
+      }
+    },
+    "os-browserify": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
+      "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=",
+      "dev": true
+    },
+    "os-homedir": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+      "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
+      "dev": true
+    },
+    "os-locale": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
+      "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
+      "dev": true,
+      "requires": {
+        "execa": "^1.0.0",
+        "lcid": "^2.0.0",
+        "mem": "^4.0.0"
+      }
+    },
+    "os-name": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/os-name/-/os-name-1.0.3.tgz",
+      "integrity": "sha1-GzefZINa98Wn9JizV8uVIVwVnt8=",
+      "requires": {
+        "osx-release": "^1.0.0",
+        "win-release": "^1.0.0"
+      }
+    },
+    "os-tmpdir": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+      "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+      "dev": true
+    },
+    "osenv": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
+      "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
+      "dev": true,
+      "requires": {
+        "os-homedir": "^1.0.0",
+        "os-tmpdir": "^1.0.0"
+      }
+    },
+    "osx-release": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/osx-release/-/osx-release-1.1.0.tgz",
+      "integrity": "sha1-8heRGigTaUmvG/kwiyQeJzfTzWw=",
+      "requires": {
+        "minimist": "^1.1.0"
+      }
+    },
+    "p-defer": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
+      "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=",
+      "dev": true
+    },
+    "p-finally": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
+      "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
+      "dev": true
+    },
+    "p-is-promise": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz",
+      "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==",
+      "dev": true
+    },
+    "p-limit": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+      "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+      "dev": true,
+      "requires": {
+        "p-try": "^2.0.0"
+      }
+    },
+    "p-locate": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+      "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+      "dev": true,
+      "requires": {
+        "p-limit": "^2.0.0"
+      }
+    },
+    "p-map": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz",
+      "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==",
+      "dev": true,
+      "requires": {
+        "aggregate-error": "^3.0.0"
+      }
+    },
+    "p-retry": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz",
+      "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==",
+      "dev": true,
+      "requires": {
+        "retry": "^0.12.0"
+      }
+    },
+    "p-try": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+      "dev": true
+    },
+    "pac-proxy-agent": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-3.0.1.tgz",
+      "integrity": "sha512-44DUg21G/liUZ48dJpUSjZnFfZro/0K5JTyFYLBcmh9+T6Ooi4/i4efwUiEy0+4oQusCBqWdhv16XohIj1GqnQ==",
+      "requires": {
+        "agent-base": "^4.2.0",
+        "debug": "^4.1.1",
+        "get-uri": "^2.0.0",
+        "http-proxy-agent": "^2.1.0",
+        "https-proxy-agent": "^3.0.0",
+        "pac-resolver": "^3.0.0",
+        "raw-body": "^2.2.0",
+        "socks-proxy-agent": "^4.0.1"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        }
+      }
+    },
+    "pac-resolver": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-3.0.0.tgz",
+      "integrity": "sha512-tcc38bsjuE3XZ5+4vP96OfhOugrX+JcnpUbhfuc4LuXBLQhoTthOstZeoQJBDnQUDYzYmdImKsbz0xSl1/9qeA==",
+      "requires": {
+        "co": "^4.6.0",
+        "degenerator": "^1.0.4",
+        "ip": "^1.1.5",
+        "netmask": "^1.0.6",
+        "thunkify": "^2.1.2"
+      }
+    },
+    "pacote": {
+      "version": "9.5.8",
+      "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.5.8.tgz",
+      "integrity": "sha512-0Tl8Oi/K0Lo4MZmH0/6IsT3gpGf9eEAznLXEQPKgPq7FscnbUOyopnVpwXlnQdIbCUaojWy1Wd7VMyqfVsRrIw==",
+      "dev": true,
+      "requires": {
+        "bluebird": "^3.5.3",
+        "cacache": "^12.0.2",
+        "chownr": "^1.1.2",
+        "figgy-pudding": "^3.5.1",
+        "get-stream": "^4.1.0",
+        "glob": "^7.1.3",
+        "infer-owner": "^1.0.4",
+        "lru-cache": "^5.1.1",
+        "make-fetch-happen": "^5.0.0",
+        "minimatch": "^3.0.4",
+        "minipass": "^2.3.5",
+        "mississippi": "^3.0.0",
+        "mkdirp": "^0.5.1",
+        "normalize-package-data": "^2.4.0",
+        "npm-package-arg": "^6.1.0",
+        "npm-packlist": "^1.1.12",
+        "npm-pick-manifest": "^3.0.0",
+        "npm-registry-fetch": "^4.0.0",
+        "osenv": "^0.1.5",
+        "promise-inflight": "^1.0.1",
+        "promise-retry": "^1.1.1",
+        "protoduck": "^5.0.1",
+        "rimraf": "^2.6.2",
+        "safe-buffer": "^5.1.2",
+        "semver": "^5.6.0",
+        "ssri": "^6.0.1",
+        "tar": "^4.4.10",
+        "unique-filename": "^1.1.1",
+        "which": "^1.3.1"
+      },
+      "dependencies": {
+        "cacache": {
+          "version": "12.0.4",
+          "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz",
+          "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==",
+          "dev": true,
+          "requires": {
+            "bluebird": "^3.5.5",
+            "chownr": "^1.1.1",
+            "figgy-pudding": "^3.5.1",
+            "glob": "^7.1.4",
+            "graceful-fs": "^4.1.15",
+            "infer-owner": "^1.0.3",
+            "lru-cache": "^5.1.1",
+            "mississippi": "^3.0.0",
+            "mkdirp": "^0.5.1",
+            "move-concurrently": "^1.0.1",
+            "promise-inflight": "^1.0.1",
+            "rimraf": "^2.6.3",
+            "ssri": "^6.0.1",
+            "unique-filename": "^1.1.1",
+            "y18n": "^4.0.0"
+          }
+        },
+        "minipass": {
+          "version": "2.9.0",
+          "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
+          "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
+          "dev": true,
+          "requires": {
+            "safe-buffer": "^5.1.2",
+            "yallist": "^3.0.0"
+          }
+        },
+        "ssri": {
+          "version": "6.0.1",
+          "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz",
+          "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
+          "dev": true,
+          "requires": {
+            "figgy-pudding": "^3.5.1"
+          }
+        }
+      }
+    },
+    "pako": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+      "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+      "dev": true
+    },
+    "parallel-transform": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz",
+      "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==",
+      "dev": true,
+      "requires": {
+        "cyclist": "^1.0.1",
+        "inherits": "^2.0.3",
+        "readable-stream": "^2.1.5"
+      }
+    },
+    "parse-asn1": {
+      "version": "5.1.5",
+      "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz",
+      "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==",
+      "dev": true,
+      "requires": {
+        "asn1.js": "^4.0.0",
+        "browserify-aes": "^1.0.0",
+        "create-hash": "^1.1.0",
+        "evp_bytestokey": "^1.0.0",
+        "pbkdf2": "^3.0.3",
+        "safe-buffer": "^5.1.1"
+      }
+    },
+    "parse-json": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+      "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+      "dev": true,
+      "requires": {
+        "error-ex": "^1.3.1",
+        "json-parse-better-errors": "^1.0.1"
+      }
+    },
+    "parse5": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
+      "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
+      "optional": true
+    },
+    "parseqs": {
+      "version": "0.0.5",
+      "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
+      "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
+      "dev": true,
+      "requires": {
+        "better-assert": "~1.0.0"
+      }
+    },
+    "parseuri": {
+      "version": "0.0.5",
+      "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
+      "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
+      "dev": true,
+      "requires": {
+        "better-assert": "~1.0.0"
+      }
+    },
+    "parseurl": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+      "dev": true
+    },
+    "pascalcase": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+      "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
+      "dev": true
+    },
+    "path-browserify": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz",
+      "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==",
+      "dev": true
+    },
+    "path-dirname": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
+      "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
+      "dev": true
+    },
+    "path-exists": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+      "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+      "dev": true
+    },
+    "path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+    },
+    "path-is-inside": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+      "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
+      "dev": true
+    },
+    "path-key": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+      "dev": true
+    },
+    "path-parse": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+      "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+      "dev": true
+    },
+    "path-to-regexp": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+      "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=",
+      "dev": true
+    },
+    "path-type": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+      "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
+      "dev": true,
+      "requires": {
+        "pify": "^3.0.0"
+      },
+      "dependencies": {
+        "pify": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+          "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+          "dev": true
+        }
+      }
+    },
+    "pause-stream": {
+      "version": "0.0.11",
+      "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
+      "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=",
+      "requires": {
+        "through": "~2.3"
+      }
+    },
+    "pbkdf2": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz",
+      "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==",
+      "dev": true,
+      "requires": {
+        "create-hash": "^1.1.2",
+        "create-hmac": "^1.1.4",
+        "ripemd160": "^2.0.1",
+        "safe-buffer": "^5.0.1",
+        "sha.js": "^2.4.8"
+      }
+    },
+    "pend": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+      "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
+    },
+    "performance-now": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
+      "dev": true
+    },
+    "picomatch": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
+      "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
+      "dev": true
+    },
+    "pify": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+      "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+      "dev": true
+    },
+    "pinkie": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+      "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
+      "dev": true
+    },
+    "pinkie-promise": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+      "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+      "dev": true,
+      "requires": {
+        "pinkie": "^2.0.0"
+      }
+    },
+    "pkg-dir": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
+      "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
+      "dev": true,
+      "requires": {
+        "find-up": "^3.0.0"
+      }
+    },
+    "platform": {
+      "version": "1.3.6",
+      "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz",
+      "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg=="
+    },
+    "portfinder": {
+      "version": "1.0.28",
+      "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz",
+      "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==",
+      "dev": true,
+      "requires": {
+        "async": "^2.6.2",
+        "debug": "^3.1.1",
+        "mkdirp": "^0.5.5"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "3.2.6",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+          "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+          "dev": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        }
+      }
+    },
+    "posix-character-classes": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+      "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
+      "dev": true
+    },
+    "postcss": {
+      "version": "7.0.21",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.21.tgz",
+      "integrity": "sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ==",
+      "dev": true,
+      "requires": {
+        "chalk": "^2.4.2",
+        "source-map": "^0.6.1",
+        "supports-color": "^6.1.0"
+      },
+      "dependencies": {
+        "supports-color": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+          "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        }
+      }
+    },
+    "postcss-calc": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.2.tgz",
+      "integrity": "sha512-rofZFHUg6ZIrvRwPeFktv06GdbDYLcGqh9EwiMutZg+a0oePCCw1zHOEiji6LCpyRcjTREtPASuUqeAvYlEVvQ==",
+      "dev": true,
+      "requires": {
+        "postcss": "^7.0.27",
+        "postcss-selector-parser": "^6.0.2",
+        "postcss-value-parser": "^4.0.2"
+      },
+      "dependencies": {
+        "postcss": {
+          "version": "7.0.32",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz",
+          "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==",
+          "dev": true,
+          "requires": {
+            "chalk": "^2.4.2",
+            "source-map": "^0.6.1",
+            "supports-color": "^6.1.0"
+          }
+        },
+        "supports-color": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+          "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        }
+      }
+    },
+    "postcss-colormin": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz",
+      "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==",
+      "dev": true,
+      "requires": {
+        "browserslist": "^4.0.0",
+        "color": "^3.0.0",
+        "has": "^1.0.0",
+        "postcss": "^7.0.0",
+        "postcss-value-parser": "^3.0.0"
+      },
+      "dependencies": {
+        "postcss-value-parser": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+          "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+          "dev": true
+        }
+      }
+    },
+    "postcss-convert-values": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz",
+      "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==",
+      "dev": true,
+      "requires": {
+        "postcss": "^7.0.0",
+        "postcss-value-parser": "^3.0.0"
+      },
+      "dependencies": {
+        "postcss-value-parser": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+          "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+          "dev": true
+        }
+      }
+    },
+    "postcss-discard-comments": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz",
+      "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==",
+      "dev": true,
+      "requires": {
+        "postcss": "^7.0.0"
+      }
+    },
+    "postcss-discard-duplicates": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz",
+      "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==",
+      "dev": true,
+      "requires": {
+        "postcss": "^7.0.0"
+      }
+    },
+    "postcss-discard-empty": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz",
+      "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==",
+      "dev": true,
+      "requires": {
+        "postcss": "^7.0.0"
+      }
+    },
+    "postcss-discard-overridden": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz",
+      "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==",
+      "dev": true,
+      "requires": {
+        "postcss": "^7.0.0"
+      }
+    },
+    "postcss-import": {
+      "version": "12.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-12.0.1.tgz",
+      "integrity": "sha512-3Gti33dmCjyKBgimqGxL3vcV8w9+bsHwO5UrBawp796+jdardbcFl4RP5w/76BwNL7aGzpKstIfF9I+kdE8pTw==",
+      "dev": true,
+      "requires": {
+        "postcss": "^7.0.1",
+        "postcss-value-parser": "^3.2.3",
+        "read-cache": "^1.0.0",
+        "resolve": "^1.1.7"
+      },
+      "dependencies": {
+        "postcss-value-parser": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+          "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+          "dev": true
+        }
+      }
+    },
+    "postcss-load-config": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz",
+      "integrity": "sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q==",
+      "dev": true,
+      "requires": {
+        "cosmiconfig": "^5.0.0",
+        "import-cwd": "^2.0.0"
+      }
+    },
+    "postcss-loader": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz",
+      "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==",
+      "dev": true,
+      "requires": {
+        "loader-utils": "^1.1.0",
+        "postcss": "^7.0.0",
+        "postcss-load-config": "^2.0.0",
+        "schema-utils": "^1.0.0"
+      }
+    },
+    "postcss-merge-longhand": {
+      "version": "4.0.11",
+      "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz",
+      "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==",
+      "dev": true,
+      "requires": {
+        "css-color-names": "0.0.4",
+        "postcss": "^7.0.0",
+        "postcss-value-parser": "^3.0.0",
+        "stylehacks": "^4.0.0"
+      },
+      "dependencies": {
+        "postcss-value-parser": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+          "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+          "dev": true
+        }
+      }
+    },
+    "postcss-merge-rules": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz",
+      "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==",
+      "dev": true,
+      "requires": {
+        "browserslist": "^4.0.0",
+        "caniuse-api": "^3.0.0",
+        "cssnano-util-same-parent": "^4.0.0",
+        "postcss": "^7.0.0",
+        "postcss-selector-parser": "^3.0.0",
+        "vendors": "^1.0.0"
+      },
+      "dependencies": {
+        "postcss-selector-parser": {
+          "version": "3.1.2",
+          "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz",
+          "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==",
+          "dev": true,
+          "requires": {
+            "dot-prop": "^5.2.0",
+            "indexes-of": "^1.0.1",
+            "uniq": "^1.0.1"
+          }
+        }
+      }
+    },
+    "postcss-minify-font-values": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz",
+      "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==",
+      "dev": true,
+      "requires": {
+        "postcss": "^7.0.0",
+        "postcss-value-parser": "^3.0.0"
+      },
+      "dependencies": {
+        "postcss-value-parser": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+          "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+          "dev": true
+        }
+      }
+    },
+    "postcss-minify-gradients": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz",
+      "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==",
+      "dev": true,
+      "requires": {
+        "cssnano-util-get-arguments": "^4.0.0",
+        "is-color-stop": "^1.0.0",
+        "postcss": "^7.0.0",
+        "postcss-value-parser": "^3.0.0"
+      },
+      "dependencies": {
+        "postcss-value-parser": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+          "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+          "dev": true
+        }
+      }
+    },
+    "postcss-minify-params": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz",
+      "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==",
+      "dev": true,
+      "requires": {
+        "alphanum-sort": "^1.0.0",
+        "browserslist": "^4.0.0",
+        "cssnano-util-get-arguments": "^4.0.0",
+        "postcss": "^7.0.0",
+        "postcss-value-parser": "^3.0.0",
+        "uniqs": "^2.0.0"
+      },
+      "dependencies": {
+        "postcss-value-parser": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+          "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+          "dev": true
+        }
+      }
+    },
+    "postcss-minify-selectors": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz",
+      "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==",
+      "dev": true,
+      "requires": {
+        "alphanum-sort": "^1.0.0",
+        "has": "^1.0.0",
+        "postcss": "^7.0.0",
+        "postcss-selector-parser": "^3.0.0"
+      },
+      "dependencies": {
+        "postcss-selector-parser": {
+          "version": "3.1.2",
+          "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz",
+          "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==",
+          "dev": true,
+          "requires": {
+            "dot-prop": "^5.2.0",
+            "indexes-of": "^1.0.1",
+            "uniq": "^1.0.1"
+          }
+        }
+      }
+    },
+    "postcss-normalize-charset": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz",
+      "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==",
+      "dev": true,
+      "requires": {
+        "postcss": "^7.0.0"
+      }
+    },
+    "postcss-normalize-display-values": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz",
+      "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==",
+      "dev": true,
+      "requires": {
+        "cssnano-util-get-match": "^4.0.0",
+        "postcss": "^7.0.0",
+        "postcss-value-parser": "^3.0.0"
+      },
+      "dependencies": {
+        "postcss-value-parser": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+          "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+          "dev": true
+        }
+      }
+    },
+    "postcss-normalize-positions": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz",
+      "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==",
+      "dev": true,
+      "requires": {
+        "cssnano-util-get-arguments": "^4.0.0",
+        "has": "^1.0.0",
+        "postcss": "^7.0.0",
+        "postcss-value-parser": "^3.0.0"
+      },
+      "dependencies": {
+        "postcss-value-parser": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+          "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+          "dev": true
+        }
+      }
+    },
+    "postcss-normalize-repeat-style": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz",
+      "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==",
+      "dev": true,
+      "requires": {
+        "cssnano-util-get-arguments": "^4.0.0",
+        "cssnano-util-get-match": "^4.0.0",
+        "postcss": "^7.0.0",
+        "postcss-value-parser": "^3.0.0"
+      },
+      "dependencies": {
+        "postcss-value-parser": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+          "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+          "dev": true
+        }
+      }
+    },
+    "postcss-normalize-string": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz",
+      "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==",
+      "dev": true,
+      "requires": {
+        "has": "^1.0.0",
+        "postcss": "^7.0.0",
+        "postcss-value-parser": "^3.0.0"
+      },
+      "dependencies": {
+        "postcss-value-parser": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+          "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+          "dev": true
+        }
+      }
+    },
+    "postcss-normalize-timing-functions": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz",
+      "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==",
+      "dev": true,
+      "requires": {
+        "cssnano-util-get-match": "^4.0.0",
+        "postcss": "^7.0.0",
+        "postcss-value-parser": "^3.0.0"
+      },
+      "dependencies": {
+        "postcss-value-parser": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+          "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+          "dev": true
+        }
+      }
+    },
+    "postcss-normalize-unicode": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz",
+      "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==",
+      "dev": true,
+      "requires": {
+        "browserslist": "^4.0.0",
+        "postcss": "^7.0.0",
+        "postcss-value-parser": "^3.0.0"
+      },
+      "dependencies": {
+        "postcss-value-parser": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+          "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+          "dev": true
+        }
+      }
+    },
+    "postcss-normalize-url": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz",
+      "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==",
+      "dev": true,
+      "requires": {
+        "is-absolute-url": "^2.0.0",
+        "normalize-url": "^3.0.0",
+        "postcss": "^7.0.0",
+        "postcss-value-parser": "^3.0.0"
+      },
+      "dependencies": {
+        "postcss-value-parser": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+          "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+          "dev": true
+        }
+      }
+    },
+    "postcss-normalize-whitespace": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz",
+      "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==",
+      "dev": true,
+      "requires": {
+        "postcss": "^7.0.0",
+        "postcss-value-parser": "^3.0.0"
+      },
+      "dependencies": {
+        "postcss-value-parser": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+          "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+          "dev": true
+        }
+      }
+    },
+    "postcss-ordered-values": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz",
+      "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==",
+      "dev": true,
+      "requires": {
+        "cssnano-util-get-arguments": "^4.0.0",
+        "postcss": "^7.0.0",
+        "postcss-value-parser": "^3.0.0"
+      },
+      "dependencies": {
+        "postcss-value-parser": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+          "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+          "dev": true
+        }
+      }
+    },
+    "postcss-reduce-initial": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz",
+      "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==",
+      "dev": true,
+      "requires": {
+        "browserslist": "^4.0.0",
+        "caniuse-api": "^3.0.0",
+        "has": "^1.0.0",
+        "postcss": "^7.0.0"
+      }
+    },
+    "postcss-reduce-transforms": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz",
+      "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==",
+      "dev": true,
+      "requires": {
+        "cssnano-util-get-match": "^4.0.0",
+        "has": "^1.0.0",
+        "postcss": "^7.0.0",
+        "postcss-value-parser": "^3.0.0"
+      },
+      "dependencies": {
+        "postcss-value-parser": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+          "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+          "dev": true
+        }
+      }
+    },
+    "postcss-selector-parser": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz",
+      "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==",
+      "dev": true,
+      "requires": {
+        "cssesc": "^3.0.0",
+        "indexes-of": "^1.0.1",
+        "uniq": "^1.0.1"
+      }
+    },
+    "postcss-svgo": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz",
+      "integrity": "sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==",
+      "dev": true,
+      "requires": {
+        "is-svg": "^3.0.0",
+        "postcss": "^7.0.0",
+        "postcss-value-parser": "^3.0.0",
+        "svgo": "^1.0.0"
+      },
+      "dependencies": {
+        "postcss-value-parser": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+          "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+          "dev": true
+        }
+      }
+    },
+    "postcss-unique-selectors": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz",
+      "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==",
+      "dev": true,
+      "requires": {
+        "alphanum-sort": "^1.0.0",
+        "postcss": "^7.0.0",
+        "uniqs": "^2.0.0"
+      }
+    },
+    "postcss-value-parser": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz",
+      "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==",
+      "dev": true
+    },
+    "prelude-ls": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+      "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ="
+    },
+    "prepend-http": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
+      "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
+      "dev": true
+    },
+    "process": {
+      "version": "0.11.10",
+      "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+      "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=",
+      "dev": true
+    },
+    "process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+    },
+    "promise": {
+      "version": "7.3.1",
+      "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
+      "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "asap": "~2.0.3"
+      }
+    },
+    "promise-inflight": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
+      "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
+      "dev": true
+    },
+    "promise-retry": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz",
+      "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=",
+      "dev": true,
+      "requires": {
+        "err-code": "^1.0.0",
+        "retry": "^0.10.0"
+      },
+      "dependencies": {
+        "retry": {
+          "version": "0.10.1",
+          "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz",
+          "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=",
+          "dev": true
+        }
+      }
+    },
+    "protoduck": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.1.tgz",
+      "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==",
+      "dev": true,
+      "requires": {
+        "genfun": "^5.0.0"
+      }
+    },
+    "protractor": {
+      "version": "5.4.4",
+      "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.4.4.tgz",
+      "integrity": "sha512-BaL4vePgu3Vfa/whvTUAlgaCAId4uNSGxIFSCXMgj7LMYENPWLp85h5RBi9pdpX/bWQ8SF6flP7afmi2TC4eHw==",
+      "dev": true,
+      "requires": {
+        "@types/q": "^0.0.32",
+        "@types/selenium-webdriver": "^3.0.0",
+        "blocking-proxy": "^1.0.0",
+        "browserstack": "^1.5.1",
+        "chalk": "^1.1.3",
+        "glob": "^7.0.3",
+        "jasmine": "2.8.0",
+        "jasminewd2": "^2.1.0",
+        "q": "1.4.1",
+        "saucelabs": "^1.5.0",
+        "selenium-webdriver": "3.6.0",
+        "source-map-support": "~0.4.0",
+        "webdriver-js-extender": "2.1.0",
+        "webdriver-manager": "^12.0.6",
+        "yargs": "^12.0.5"
+      },
+      "dependencies": {
+        "@types/q": {
+          "version": "0.0.32",
+          "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz",
+          "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=",
+          "dev": true
+        },
+        "ansi-styles": {
+          "version": "2.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+          "dev": true
+        },
+        "chalk": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^2.2.1",
+            "escape-string-regexp": "^1.0.2",
+            "has-ansi": "^2.0.0",
+            "strip-ansi": "^3.0.0",
+            "supports-color": "^2.0.0"
+          }
+        },
+        "del": {
+          "version": "2.2.2",
+          "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
+          "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=",
+          "dev": true,
+          "requires": {
+            "globby": "^5.0.0",
+            "is-path-cwd": "^1.0.0",
+            "is-path-in-cwd": "^1.0.0",
+            "object-assign": "^4.0.1",
+            "pify": "^2.0.0",
+            "pinkie-promise": "^2.0.0",
+            "rimraf": "^2.2.8"
+          }
+        },
+        "globby": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
+          "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=",
+          "dev": true,
+          "requires": {
+            "array-union": "^1.0.1",
+            "arrify": "^1.0.0",
+            "glob": "^7.0.3",
+            "object-assign": "^4.0.1",
+            "pify": "^2.0.0",
+            "pinkie-promise": "^2.0.0"
+          }
+        },
+        "is-path-cwd": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
+          "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=",
+          "dev": true
+        },
+        "is-path-in-cwd": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz",
+          "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==",
+          "dev": true,
+          "requires": {
+            "is-path-inside": "^1.0.0"
+          }
+        },
+        "is-path-inside": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
+          "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
+          "dev": true,
+          "requires": {
+            "path-is-inside": "^1.0.1"
+          }
+        },
+        "pify": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+          "dev": true
+        },
+        "q": {
+          "version": "1.4.1",
+          "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz",
+          "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=",
+          "dev": true
+        },
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+          "dev": true
+        },
+        "source-map-support": {
+          "version": "0.4.18",
+          "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
+          "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
+          "dev": true,
+          "requires": {
+            "source-map": "^0.5.6"
+          }
+        },
+        "supports-color": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+          "dev": true
+        },
+        "webdriver-manager": {
+          "version": "12.1.7",
+          "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.7.tgz",
+          "integrity": "sha512-XINj6b8CYuUYC93SG3xPkxlyUc3IJbD6Vvo75CVGuG9uzsefDzWQrhz0Lq8vbPxtb4d63CZdYophF8k8Or/YiA==",
+          "dev": true,
+          "requires": {
+            "adm-zip": "^0.4.9",
+            "chalk": "^1.1.1",
+            "del": "^2.2.0",
+            "glob": "^7.0.3",
+            "ini": "^1.3.4",
+            "minimist": "^1.2.0",
+            "q": "^1.4.1",
+            "request": "^2.87.0",
+            "rimraf": "^2.5.2",
+            "semver": "^5.3.0",
+            "xml2js": "^0.4.17"
+          }
+        }
+      }
+    },
+    "proxy-addr": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
+      "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
+      "dev": true,
+      "requires": {
+        "forwarded": "~0.1.2",
+        "ipaddr.js": "1.9.1"
+      }
+    },
+    "proxy-agent": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-3.1.1.tgz",
+      "integrity": "sha512-WudaR0eTsDx33O3EJE16PjBRZWcX8GqCEeERw1W3hZJgH/F2a46g7jty6UGty6NeJ4CKQy8ds2CJPMiyeqaTvw==",
+      "requires": {
+        "agent-base": "^4.2.0",
+        "debug": "4",
+        "http-proxy-agent": "^2.1.0",
+        "https-proxy-agent": "^3.0.0",
+        "lru-cache": "^5.1.1",
+        "pac-proxy-agent": "^3.0.1",
+        "proxy-from-env": "^1.0.0",
+        "socks-proxy-agent": "^4.0.1"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        }
+      }
+    },
+    "proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+    },
+    "prr": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
+      "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
+      "dev": true
+    },
+    "pseudomap": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
+      "dev": true
+    },
+    "psl": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
+      "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==",
+      "dev": true
+    },
+    "public-encrypt": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
+      "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
+      "dev": true,
+      "requires": {
+        "bn.js": "^4.1.0",
+        "browserify-rsa": "^4.0.0",
+        "create-hash": "^1.1.0",
+        "parse-asn1": "^5.0.0",
+        "randombytes": "^2.0.1",
+        "safe-buffer": "^5.1.2"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.11.9",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+          "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+          "dev": true
+        }
+      }
+    },
+    "pump": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+      "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+      "requires": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
+      }
+    },
+    "pumpify": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
+      "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
+      "dev": true,
+      "requires": {
+        "duplexify": "^3.6.0",
+        "inherits": "^2.0.3",
+        "pump": "^2.0.0"
+      },
+      "dependencies": {
+        "pump": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
+          "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
+          "dev": true,
+          "requires": {
+            "end-of-stream": "^1.1.0",
+            "once": "^1.3.1"
+          }
+        }
+      }
+    },
+    "punycode": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+      "dev": true
+    },
+    "q": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
+      "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=",
+      "dev": true
+    },
+    "qjobs": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz",
+      "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==",
+      "dev": true
+    },
+    "qs": {
+      "version": "6.9.4",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz",
+      "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ=="
+    },
+    "query-string": {
+      "version": "4.3.4",
+      "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
+      "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
+      "dev": true,
+      "requires": {
+        "object-assign": "^4.1.0",
+        "strict-uri-encode": "^1.0.0"
+      }
+    },
+    "querystring": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+      "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
+      "dev": true
+    },
+    "querystring-es3": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
+      "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=",
+      "dev": true
+    },
+    "querystringify": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz",
+      "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==",
+      "dev": true
+    },
+    "randombytes": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+      "dev": true,
+      "requires": {
+        "safe-buffer": "^5.1.0"
+      }
+    },
+    "randomfill": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
+      "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
+      "dev": true,
+      "requires": {
+        "randombytes": "^2.0.5",
+        "safe-buffer": "^5.1.0"
+      }
+    },
+    "range-parser": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+      "dev": true
+    },
+    "raw-body": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.1.tgz",
+      "integrity": "sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA==",
+      "requires": {
+        "bytes": "3.1.0",
+        "http-errors": "1.7.3",
+        "iconv-lite": "0.4.24",
+        "unpipe": "1.0.0"
+      }
+    },
+    "raw-loader": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-3.1.0.tgz",
+      "integrity": "sha512-lzUVMuJ06HF4rYveaz9Tv0WRlUMxJ0Y1hgSkkgg+50iEdaI0TthyEDe08KIHb0XsF6rn8WYTqPCaGTZg3sX+qA==",
+      "dev": true,
+      "requires": {
+        "loader-utils": "^1.1.0",
+        "schema-utils": "^2.0.1"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "6.12.3",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz",
+          "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==",
+          "dev": true,
+          "requires": {
+            "fast-deep-equal": "^3.1.1",
+            "fast-json-stable-stringify": "^2.0.0",
+            "json-schema-traverse": "^0.4.1",
+            "uri-js": "^4.2.2"
+          }
+        },
+        "fast-deep-equal": {
+          "version": "3.1.3",
+          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+          "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+          "dev": true
+        },
+        "schema-utils": {
+          "version": "2.7.0",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
+          "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
+          "dev": true,
+          "requires": {
+            "@types/json-schema": "^7.0.4",
+            "ajv": "^6.12.2",
+            "ajv-keywords": "^3.4.1"
+          }
+        }
+      }
+    },
+    "read-cache": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
+      "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=",
+      "dev": true,
+      "requires": {
+        "pify": "^2.3.0"
+      },
+      "dependencies": {
+        "pify": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+          "dev": true
+        }
+      }
+    },
+    "read-package-json": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.1.tgz",
+      "integrity": "sha512-dAiqGtVc/q5doFz6096CcnXhpYk0ZN8dEKVkGLU0CsASt8SrgF6SF7OTKAYubfvFhWaqofl+Y8HK19GR8jwW+A==",
+      "dev": true,
+      "requires": {
+        "glob": "^7.1.1",
+        "graceful-fs": "^4.1.2",
+        "json-parse-better-errors": "^1.0.1",
+        "normalize-package-data": "^2.0.0",
+        "npm-normalize-package-bin": "^1.0.0"
+      }
+    },
+    "read-package-tree": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz",
+      "integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==",
+      "dev": true,
+      "requires": {
+        "read-package-json": "^2.0.0",
+        "readdir-scoped-modules": "^1.0.0",
+        "util-promisify": "^2.1.0"
+      }
+    },
+    "readable-stream": {
+      "version": "2.3.7",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+      "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+      "requires": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
+      }
+    },
+    "readdir-scoped-modules": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz",
+      "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==",
+      "dev": true,
+      "requires": {
+        "debuglog": "^1.0.1",
+        "dezalgo": "^1.0.0",
+        "graceful-fs": "^4.1.2",
+        "once": "^1.3.0"
+      }
+    },
+    "readdirp": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz",
+      "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==",
+      "dev": true,
+      "requires": {
+        "picomatch": "^2.2.1"
+      }
+    },
+    "reflect-metadata": {
+      "version": "0.1.13",
+      "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
+      "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==",
+      "dev": true
+    },
+    "regenerate": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz",
+      "integrity": "sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==",
+      "dev": true
+    },
+    "regenerate-unicode-properties": {
+      "version": "8.2.0",
+      "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz",
+      "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==",
+      "dev": true,
+      "requires": {
+        "regenerate": "^1.4.0"
+      }
+    },
+    "regenerator-runtime": {
+      "version": "0.13.3",
+      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
+      "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==",
+      "dev": true
+    },
+    "regenerator-transform": {
+      "version": "0.14.5",
+      "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz",
+      "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==",
+      "dev": true,
+      "requires": {
+        "@babel/runtime": "^7.8.4"
+      }
+    },
+    "regex-not": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+      "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+      "dev": true,
+      "requires": {
+        "extend-shallow": "^3.0.2",
+        "safe-regex": "^1.1.0"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+          "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+          "dev": true,
+          "requires": {
+            "assign-symbols": "^1.0.0",
+            "is-extendable": "^1.0.1"
+          }
+        },
+        "is-extendable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+          "dev": true,
+          "requires": {
+            "is-plain-object": "^2.0.4"
+          }
+        }
+      }
+    },
+    "regexp.prototype.flags": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz",
+      "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==",
+      "dev": true,
+      "requires": {
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.0-next.1"
+      }
+    },
+    "regexpu-core": {
+      "version": "4.7.0",
+      "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz",
+      "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==",
+      "dev": true,
+      "requires": {
+        "regenerate": "^1.4.0",
+        "regenerate-unicode-properties": "^8.2.0",
+        "regjsgen": "^0.5.1",
+        "regjsparser": "^0.6.4",
+        "unicode-match-property-ecmascript": "^1.0.4",
+        "unicode-match-property-value-ecmascript": "^1.2.0"
+      }
+    },
+    "regjsgen": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz",
+      "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==",
+      "dev": true
+    },
+    "regjsparser": {
+      "version": "0.6.4",
+      "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz",
+      "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==",
+      "dev": true,
+      "requires": {
+        "jsesc": "~0.5.0"
+      },
+      "dependencies": {
+        "jsesc": {
+          "version": "0.5.0",
+          "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
+          "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
+          "dev": true
+        }
+      }
+    },
+    "remove-trailing-separator": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+      "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
+      "dev": true
+    },
+    "repeat-element": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
+      "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==",
+      "dev": true
+    },
+    "repeat-string": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+      "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+      "dev": true
+    },
+    "request": {
+      "version": "2.88.2",
+      "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
+      "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
+      "dev": true,
+      "requires": {
+        "aws-sign2": "~0.7.0",
+        "aws4": "^1.8.0",
+        "caseless": "~0.12.0",
+        "combined-stream": "~1.0.6",
+        "extend": "~3.0.2",
+        "forever-agent": "~0.6.1",
+        "form-data": "~2.3.2",
+        "har-validator": "~5.1.3",
+        "http-signature": "~1.2.0",
+        "is-typedarray": "~1.0.0",
+        "isstream": "~0.1.2",
+        "json-stringify-safe": "~5.0.1",
+        "mime-types": "~2.1.19",
+        "oauth-sign": "~0.9.0",
+        "performance-now": "^2.1.0",
+        "qs": "~6.5.2",
+        "safe-buffer": "^5.1.2",
+        "tough-cookie": "~2.5.0",
+        "tunnel-agent": "^0.6.0",
+        "uuid": "^3.3.2"
+      },
+      "dependencies": {
+        "qs": {
+          "version": "6.5.2",
+          "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+          "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+          "dev": true
+        }
+      }
+    },
+    "require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+      "dev": true
+    },
+    "require-main-filename": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
+      "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
+      "dev": true
+    },
+    "requires-port": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+      "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
+      "dev": true
+    },
+    "resolve": {
+      "version": "1.17.0",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+      "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
+      "dev": true,
+      "requires": {
+        "path-parse": "^1.0.6"
+      }
+    },
+    "resolve-cwd": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
+      "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
+      "dev": true,
+      "requires": {
+        "resolve-from": "^3.0.0"
+      }
+    },
+    "resolve-from": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
+      "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
+      "dev": true
+    },
+    "resolve-url": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+      "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
+      "dev": true
+    },
+    "restore-cursor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+      "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+      "dev": true,
+      "requires": {
+        "onetime": "^5.1.0",
+        "signal-exit": "^3.0.2"
+      }
+    },
+    "ret": {
+      "version": "0.1.15",
+      "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+      "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+      "dev": true
+    },
+    "retry": {
+      "version": "0.12.0",
+      "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
+      "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
+      "dev": true
+    },
+    "rfdc": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz",
+      "integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==",
+      "dev": true
+    },
+    "rgb-regex": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz",
+      "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=",
+      "dev": true
+    },
+    "rgba-regex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz",
+      "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=",
+      "dev": true
+    },
+    "rimraf": {
+      "version": "2.7.1",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+      "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+      "requires": {
+        "glob": "^7.1.3"
+      }
+    },
+    "ripemd160": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
+      "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
+      "dev": true,
+      "requires": {
+        "hash-base": "^3.0.0",
+        "inherits": "^2.0.1"
+      }
+    },
+    "rollup": {
+      "version": "1.25.2",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.25.2.tgz",
+      "integrity": "sha512-+7z6Wab/L45QCPcfpuTZKwKiB0tynj05s/+s2U3F2Bi7rOLPr9UcjUwO7/xpjlPNXA/hwnth6jBExFRGyf3tMg==",
+      "dev": true,
+      "requires": {
+        "@types/estree": "*",
+        "@types/node": "*",
+        "acorn": "^7.1.0"
+      }
+    },
+    "run-async": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
+      "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
+      "dev": true
+    },
+    "run-queue": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
+      "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=",
+      "dev": true,
+      "requires": {
+        "aproba": "^1.1.1"
+      }
+    },
+    "rxjs": {
+      "version": "6.5.5",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz",
+      "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==",
+      "requires": {
+        "tslib": "^1.9.0"
+      }
+    },
+    "safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+    },
+    "safe-regex": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+      "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+      "dev": true,
+      "requires": {
+        "ret": "~0.1.10"
+      }
+    },
+    "safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+    },
+    "sass": {
+      "version": "1.23.3",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.23.3.tgz",
+      "integrity": "sha512-1DKRZxJMOh4Bme16AbWTyYeJAjTlrvw2+fWshHHaepeJfGq2soFZTnt0YhWit+bohtDu4LdyPoEj6VFD4APHog==",
+      "dev": true,
+      "requires": {
+        "chokidar": ">=2.0.0 <4.0.0"
+      }
+    },
+    "sass-loader": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-8.0.0.tgz",
+      "integrity": "sha512-+qeMu563PN7rPdit2+n5uuYVR0SSVwm0JsOUsaJXzgYcClWSlmX0iHDnmeOobPkf5kUglVot3QS6SyLyaQoJ4w==",
+      "dev": true,
+      "requires": {
+        "clone-deep": "^4.0.1",
+        "loader-utils": "^1.2.3",
+        "neo-async": "^2.6.1",
+        "schema-utils": "^2.1.0",
+        "semver": "^6.3.0"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "6.12.3",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz",
+          "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==",
+          "dev": true,
+          "requires": {
+            "fast-deep-equal": "^3.1.1",
+            "fast-json-stable-stringify": "^2.0.0",
+            "json-schema-traverse": "^0.4.1",
+            "uri-js": "^4.2.2"
+          }
+        },
+        "fast-deep-equal": {
+          "version": "3.1.3",
+          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+          "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+          "dev": true
+        },
+        "schema-utils": {
+          "version": "2.7.0",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
+          "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
+          "dev": true,
+          "requires": {
+            "@types/json-schema": "^7.0.4",
+            "ajv": "^6.12.2",
+            "ajv-keywords": "^3.4.1"
+          }
+        },
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+          "dev": true
+        }
+      }
+    },
+    "saucelabs": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz",
+      "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==",
+      "dev": true,
+      "requires": {
+        "https-proxy-agent": "^2.2.1"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "3.2.6",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+          "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+          "dev": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "https-proxy-agent": {
+          "version": "2.2.4",
+          "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
+          "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
+          "dev": true,
+          "requires": {
+            "agent-base": "^4.3.0",
+            "debug": "^3.1.0"
+          }
+        }
+      }
+    },
+    "sax": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+      "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
+    },
+    "schema-utils": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+      "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+      "dev": true,
+      "requires": {
+        "ajv": "^6.1.0",
+        "ajv-errors": "^1.0.0",
+        "ajv-keywords": "^3.1.0"
+      }
+    },
+    "sdk-base": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/sdk-base/-/sdk-base-2.0.1.tgz",
+      "integrity": "sha1-ukAonovfJy7RHdnql+r5jgNtJMY=",
+      "requires": {
+        "get-ready": "~1.0.0"
+      }
+    },
+    "select-hose": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
+      "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=",
+      "dev": true
+    },
+    "selenium-webdriver": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz",
+      "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==",
+      "dev": true,
+      "requires": {
+        "jszip": "^3.1.3",
+        "rimraf": "^2.5.4",
+        "tmp": "0.0.30",
+        "xml2js": "^0.4.17"
+      },
+      "dependencies": {
+        "tmp": {
+          "version": "0.0.30",
+          "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz",
+          "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=",
+          "dev": true,
+          "requires": {
+            "os-tmpdir": "~1.0.1"
+          }
+        }
+      }
+    },
+    "selfsigned": {
+      "version": "1.10.7",
+      "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz",
+      "integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==",
+      "dev": true,
+      "requires": {
+        "node-forge": "0.9.0"
+      }
+    },
+    "semver": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+    },
+    "semver-dsl": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz",
+      "integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=",
+      "dev": true,
+      "requires": {
+        "semver": "^5.3.0"
+      }
+    },
+    "semver-intersect": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/semver-intersect/-/semver-intersect-1.4.0.tgz",
+      "integrity": "sha512-d8fvGg5ycKAq0+I6nfWeCx6ffaWJCsBYU0H2Rq56+/zFePYfT8mXkB3tWBSjR5BerkHNZ5eTPIk1/LBYas35xQ==",
+      "dev": true,
+      "requires": {
+        "semver": "^5.0.0"
+      }
+    },
+    "send": {
+      "version": "0.17.1",
+      "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
+      "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
+      "dev": true,
+      "requires": {
+        "debug": "2.6.9",
+        "depd": "~1.1.2",
+        "destroy": "~1.0.4",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "fresh": "0.5.2",
+        "http-errors": "~1.7.2",
+        "mime": "1.6.0",
+        "ms": "2.1.1",
+        "on-finished": "~2.3.0",
+        "range-parser": "~1.2.1",
+        "statuses": "~1.5.0"
+      },
+      "dependencies": {
+        "mime": {
+          "version": "1.6.0",
+          "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+          "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+          "dev": true
+        },
+        "ms": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+          "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+          "dev": true
+        }
+      }
+    },
+    "serialize-javascript": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz",
+      "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==",
+      "dev": true
+    },
+    "serve-index": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
+      "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=",
+      "dev": true,
+      "requires": {
+        "accepts": "~1.3.4",
+        "batch": "0.6.1",
+        "debug": "2.6.9",
+        "escape-html": "~1.0.3",
+        "http-errors": "~1.6.2",
+        "mime-types": "~2.1.17",
+        "parseurl": "~1.3.2"
+      },
+      "dependencies": {
+        "http-errors": {
+          "version": "1.6.3",
+          "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+          "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
+          "dev": true,
+          "requires": {
+            "depd": "~1.1.2",
+            "inherits": "2.0.3",
+            "setprototypeof": "1.1.0",
+            "statuses": ">= 1.4.0 < 2"
+          }
+        },
+        "inherits": {
+          "version": "2.0.3",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+          "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+          "dev": true
+        },
+        "setprototypeof": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+          "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
+          "dev": true
+        }
+      }
+    },
+    "serve-static": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
+      "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
+      "dev": true,
+      "requires": {
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "parseurl": "~1.3.3",
+        "send": "0.17.1"
+      }
+    },
+    "set-blocking": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+      "dev": true
+    },
+    "set-immediate-shim": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
+      "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=",
+      "dev": true
+    },
+    "set-value": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
+      "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+      "dev": true,
+      "requires": {
+        "extend-shallow": "^2.0.1",
+        "is-extendable": "^0.1.1",
+        "is-plain-object": "^2.0.3",
+        "split-string": "^3.0.1"
+      }
+    },
+    "setimmediate": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+      "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=",
+      "dev": true
+    },
+    "setprototypeof": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
+      "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
+    },
+    "sha.js": {
+      "version": "2.4.11",
+      "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+      "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
+      "dev": true,
+      "requires": {
+        "inherits": "^2.0.1",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "shallow-clone": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
+      "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
+      "dev": true,
+      "requires": {
+        "kind-of": "^6.0.2"
+      }
+    },
+    "shebang-command": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+      "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+      "dev": true,
+      "requires": {
+        "shebang-regex": "^1.0.0"
+      }
+    },
+    "shebang-regex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+      "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+      "dev": true
+    },
+    "signal-exit": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
+      "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
+      "dev": true
+    },
+    "simple-swizzle": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+      "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
+      "dev": true,
+      "requires": {
+        "is-arrayish": "^0.3.1"
+      },
+      "dependencies": {
+        "is-arrayish": {
+          "version": "0.3.2",
+          "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+          "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
+          "dev": true
+        }
+      }
+    },
+    "slash": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
+      "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
+      "dev": true
+    },
+    "smart-buffer": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz",
+      "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw=="
+    },
+    "snapdragon": {
+      "version": "0.8.2",
+      "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+      "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+      "dev": true,
+      "requires": {
+        "base": "^0.11.1",
+        "debug": "^2.2.0",
+        "define-property": "^0.2.5",
+        "extend-shallow": "^2.0.1",
+        "map-cache": "^0.2.2",
+        "source-map": "^0.5.6",
+        "source-map-resolve": "^0.5.0",
+        "use": "^3.1.0"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        },
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+          "dev": true
+        }
+      }
+    },
+    "snapdragon-node": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+      "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+      "dev": true,
+      "requires": {
+        "define-property": "^1.0.0",
+        "isobject": "^3.0.0",
+        "snapdragon-util": "^3.0.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^1.0.0"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "^1.0.0",
+            "is-data-descriptor": "^1.0.0",
+            "kind-of": "^6.0.2"
+          }
+        }
+      }
+    },
+    "snapdragon-util": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+      "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+      "dev": true,
+      "requires": {
+        "kind-of": "^3.2.0"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "socket.io": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz",
+      "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==",
+      "dev": true,
+      "requires": {
+        "debug": "~3.1.0",
+        "engine.io": "~3.2.0",
+        "has-binary2": "~1.0.2",
+        "socket.io-adapter": "~1.1.0",
+        "socket.io-client": "2.1.1",
+        "socket.io-parser": "~3.2.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        }
+      }
+    },
+    "socket.io-adapter": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz",
+      "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==",
+      "dev": true
+    },
+    "socket.io-client": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz",
+      "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==",
+      "dev": true,
+      "requires": {
+        "backo2": "1.0.2",
+        "base64-arraybuffer": "0.1.5",
+        "component-bind": "1.0.0",
+        "component-emitter": "1.2.1",
+        "debug": "~3.1.0",
+        "engine.io-client": "~3.2.0",
+        "has-binary2": "~1.0.2",
+        "has-cors": "1.1.0",
+        "indexof": "0.0.1",
+        "object-component": "0.0.3",
+        "parseqs": "0.0.5",
+        "parseuri": "0.0.5",
+        "socket.io-parser": "~3.2.0",
+        "to-array": "0.1.4"
+      },
+      "dependencies": {
+        "component-emitter": {
+          "version": "1.2.1",
+          "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+          "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
+          "dev": true
+        },
+        "debug": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        }
+      }
+    },
+    "socket.io-parser": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz",
+      "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==",
+      "dev": true,
+      "requires": {
+        "component-emitter": "1.2.1",
+        "debug": "~3.1.0",
+        "isarray": "2.0.1"
+      },
+      "dependencies": {
+        "component-emitter": {
+          "version": "1.2.1",
+          "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+          "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
+          "dev": true
+        },
+        "debug": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "isarray": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+          "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=",
+          "dev": true
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        }
+      }
+    },
+    "sockjs": {
+      "version": "0.3.19",
+      "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz",
+      "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==",
+      "dev": true,
+      "requires": {
+        "faye-websocket": "^0.10.0",
+        "uuid": "^3.0.1"
+      }
+    },
+    "sockjs-client": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz",
+      "integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==",
+      "dev": true,
+      "requires": {
+        "debug": "^3.2.5",
+        "eventsource": "^1.0.7",
+        "faye-websocket": "~0.11.1",
+        "inherits": "^2.0.3",
+        "json3": "^3.3.2",
+        "url-parse": "^1.4.3"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "3.2.6",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+          "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+          "dev": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "faye-websocket": {
+          "version": "0.11.3",
+          "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz",
+          "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==",
+          "dev": true,
+          "requires": {
+            "websocket-driver": ">=0.5.1"
+          }
+        }
+      }
+    },
+    "socks": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.3.tgz",
+      "integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==",
+      "requires": {
+        "ip": "1.1.5",
+        "smart-buffer": "^4.1.0"
+      }
+    },
+    "socks-proxy-agent": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz",
+      "integrity": "sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg==",
+      "requires": {
+        "agent-base": "~4.2.1",
+        "socks": "~2.3.2"
+      },
+      "dependencies": {
+        "agent-base": {
+          "version": "4.2.1",
+          "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
+          "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==",
+          "requires": {
+            "es6-promisify": "^5.0.0"
+          }
+        }
+      }
+    },
+    "sort-keys": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
+      "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=",
+      "dev": true,
+      "requires": {
+        "is-plain-obj": "^1.0.0"
+      }
+    },
+    "source-list-map": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
+      "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==",
+      "dev": true
+    },
+    "source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+    },
+    "source-map-loader": {
+      "version": "0.2.4",
+      "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-0.2.4.tgz",
+      "integrity": "sha512-OU6UJUty+i2JDpTItnizPrlpOIBLmQbWMuBg9q5bVtnHACqw1tn9nNwqJLbv0/00JjnJb/Ee5g5WS5vrRv7zIQ==",
+      "dev": true,
+      "requires": {
+        "async": "^2.5.0",
+        "loader-utils": "^1.1.0"
+      }
+    },
+    "source-map-resolve": {
+      "version": "0.5.3",
+      "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
+      "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
+      "dev": true,
+      "requires": {
+        "atob": "^2.1.2",
+        "decode-uri-component": "^0.2.0",
+        "resolve-url": "^0.2.1",
+        "source-map-url": "^0.4.0",
+        "urix": "^0.1.0"
+      }
+    },
+    "source-map-support": {
+      "version": "0.5.16",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz",
+      "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==",
+      "dev": true,
+      "requires": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
+    "source-map-url": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
+      "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
+      "dev": true
+    },
+    "sourcemap-codec": {
+      "version": "1.4.8",
+      "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
+      "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
+      "dev": true
+    },
+    "spark-md5": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.1.tgz",
+      "integrity": "sha512-0tF3AGSD1ppQeuffsLDIOWlKUd3lS92tFxcsrh5Pe3ZphhnoK+oXIBTzOAThZCiuINZLvpiLH/1VS1/ANEJVig=="
+    },
+    "spdx-correct": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+      "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
+      "dev": true,
+      "requires": {
+        "spdx-expression-parse": "^3.0.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "spdx-exceptions": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+      "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
+      "dev": true
+    },
+    "spdx-expression-parse": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+      "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+      "dev": true,
+      "requires": {
+        "spdx-exceptions": "^2.1.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "spdx-license-ids": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
+      "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==",
+      "dev": true
+    },
+    "spdy": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz",
+      "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==",
+      "dev": true,
+      "requires": {
+        "debug": "^4.1.0",
+        "handle-thing": "^2.0.0",
+        "http-deceiver": "^1.2.7",
+        "select-hose": "^2.0.0",
+        "spdy-transport": "^3.0.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+          "dev": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        }
+      }
+    },
+    "spdy-transport": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz",
+      "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==",
+      "dev": true,
+      "requires": {
+        "debug": "^4.1.0",
+        "detect-node": "^2.0.4",
+        "hpack.js": "^2.1.6",
+        "obuf": "^1.1.2",
+        "readable-stream": "^3.0.6",
+        "wbuf": "^1.7.3"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+          "dev": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+          "dev": true,
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        }
+      }
+    },
+    "speed-measure-webpack-plugin": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.3.1.tgz",
+      "integrity": "sha512-qVIkJvbtS9j/UeZumbdfz0vg+QfG/zxonAjzefZrqzkr7xOncLVXkeGbTpzd1gjCBM4PmVNkWlkeTVhgskAGSQ==",
+      "dev": true,
+      "requires": {
+        "chalk": "^2.0.1"
+      }
+    },
+    "split-string": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+      "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+      "dev": true,
+      "requires": {
+        "extend-shallow": "^3.0.0"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+          "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+          "dev": true,
+          "requires": {
+            "assign-symbols": "^1.0.0",
+            "is-extendable": "^1.0.1"
+          }
+        },
+        "is-extendable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+          "dev": true,
+          "requires": {
+            "is-plain-object": "^2.0.4"
+          }
+        }
+      }
+    },
+    "sprintf-js": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+      "dev": true
+    },
+    "sshpk": {
+      "version": "1.16.1",
+      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
+      "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
+      "dev": true,
+      "requires": {
+        "asn1": "~0.2.3",
+        "assert-plus": "^1.0.0",
+        "bcrypt-pbkdf": "^1.0.0",
+        "dashdash": "^1.12.0",
+        "ecc-jsbn": "~0.1.1",
+        "getpass": "^0.1.1",
+        "jsbn": "~0.1.0",
+        "safer-buffer": "^2.0.2",
+        "tweetnacl": "~0.14.0"
+      }
+    },
+    "ssri": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.0.tgz",
+      "integrity": "sha512-77/WrDZUWocK0mvA5NTRQyveUf+wsrIc6vyrxpS8tVvYBcX215QbafrJR3KtkpskIzoFLqqNuuYQvxaMjXJ/0g==",
+      "dev": true,
+      "requires": {
+        "figgy-pudding": "^3.5.1",
+        "minipass": "^3.1.1"
+      }
+    },
+    "stable": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
+      "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
+      "dev": true
+    },
+    "static-extend": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+      "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
+      "dev": true,
+      "requires": {
+        "define-property": "^0.2.5",
+        "object-copy": "^0.1.0"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        }
+      }
+    },
+    "statuses": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+      "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
+    },
+    "stream-browserify": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
+      "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==",
+      "dev": true,
+      "requires": {
+        "inherits": "~2.0.1",
+        "readable-stream": "^2.0.2"
+      }
+    },
+    "stream-each": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz",
+      "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==",
+      "dev": true,
+      "requires": {
+        "end-of-stream": "^1.1.0",
+        "stream-shift": "^1.0.0"
+      }
+    },
+    "stream-http": {
+      "version": "2.8.2",
+      "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.2.tgz",
+      "integrity": "sha512-QllfrBhqF1DPcz46WxKTs6Mz1Bpc+8Qm6vbqOpVav5odAXwbyzwnEczoWqtxrsmlO+cJqtPrp/8gWKWjaKLLlA==",
+      "requires": {
+        "builtin-status-codes": "^3.0.0",
+        "inherits": "^2.0.1",
+        "readable-stream": "^2.3.6",
+        "to-arraybuffer": "^1.0.0",
+        "xtend": "^4.0.0"
+      }
+    },
+    "stream-shift": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
+      "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
+      "dev": true
+    },
+    "stream-wormhole": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/stream-wormhole/-/stream-wormhole-1.1.0.tgz",
+      "integrity": "sha512-gHFfL3px0Kctd6Po0M8TzEvt3De/xu6cnRrjlfYNhwbhLPLwigI2t1nc6jrzNuaYg5C4YF78PPFuQPzRiqn9ew=="
+    },
+    "streamifier": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/streamifier/-/streamifier-0.1.1.tgz",
+      "integrity": "sha1-l+mNj6TRBdYqJpHR3AfoINuN/E8="
+    },
+    "streamroller": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-1.0.6.tgz",
+      "integrity": "sha512-3QC47Mhv3/aZNFpDDVO44qQb9gwB9QggMEE0sQmkTAwBVYdBRWISdsywlkfm5II1Q5y/pmrHflti/IgmIzdDBg==",
+      "dev": true,
+      "requires": {
+        "async": "^2.6.2",
+        "date-format": "^2.0.0",
+        "debug": "^3.2.6",
+        "fs-extra": "^7.0.1",
+        "lodash": "^4.17.14"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "3.2.6",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+          "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+          "dev": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "fs-extra": {
+          "version": "7.0.1",
+          "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
+          "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "^4.1.2",
+            "jsonfile": "^4.0.0",
+            "universalify": "^0.1.0"
+          }
+        }
+      }
+    },
+    "strict-uri-encode": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
+      "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
+      "dev": true
+    },
+    "string-width": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+      "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+      "dev": true,
+      "requires": {
+        "is-fullwidth-code-point": "^2.0.0",
+        "strip-ansi": "^4.0.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+          "dev": true
+        },
+        "strip-ansi": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^3.0.0"
+          }
+        }
+      }
+    },
+    "string.prototype.trimend": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
+      "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
+      "dev": true,
+      "requires": {
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.5"
+      }
+    },
+    "string.prototype.trimstart": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
+      "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
+      "dev": true,
+      "requires": {
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.5"
+      }
+    },
+    "string_decoder": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+      "requires": {
+        "safe-buffer": "~5.1.0"
+      }
+    },
+    "strip-ansi": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+      "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+      "dev": true,
+      "requires": {
+        "ansi-regex": "^2.0.0"
+      }
+    },
+    "strip-bom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+      "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+      "dev": true
+    },
+    "strip-eof": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
+      "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
+      "dev": true
+    },
+    "style-loader": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.0.0.tgz",
+      "integrity": "sha512-B0dOCFwv7/eY31a5PCieNwMgMhVGFe9w+rh7s/Bx8kfFkrth9zfTZquoYvdw8URgiqxObQKcpW51Ugz1HjfdZw==",
+      "dev": true,
+      "requires": {
+        "loader-utils": "^1.2.3",
+        "schema-utils": "^2.0.1"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "6.12.3",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz",
+          "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==",
+          "dev": true,
+          "requires": {
+            "fast-deep-equal": "^3.1.1",
+            "fast-json-stable-stringify": "^2.0.0",
+            "json-schema-traverse": "^0.4.1",
+            "uri-js": "^4.2.2"
+          }
+        },
+        "fast-deep-equal": {
+          "version": "3.1.3",
+          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+          "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+          "dev": true
+        },
+        "schema-utils": {
+          "version": "2.7.0",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
+          "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
+          "dev": true,
+          "requires": {
+            "@types/json-schema": "^7.0.4",
+            "ajv": "^6.12.2",
+            "ajv-keywords": "^3.4.1"
+          }
+        }
+      }
+    },
+    "stylehacks": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz",
+      "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==",
+      "dev": true,
+      "requires": {
+        "browserslist": "^4.0.0",
+        "postcss": "^7.0.0",
+        "postcss-selector-parser": "^3.0.0"
+      },
+      "dependencies": {
+        "postcss-selector-parser": {
+          "version": "3.1.2",
+          "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz",
+          "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==",
+          "dev": true,
+          "requires": {
+            "dot-prop": "^5.2.0",
+            "indexes-of": "^1.0.1",
+            "uniq": "^1.0.1"
+          }
+        }
+      }
+    },
+    "stylus": {
+      "version": "0.54.7",
+      "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.7.tgz",
+      "integrity": "sha512-Yw3WMTzVwevT6ZTrLCYNHAFmanMxdylelL3hkWNgPMeTCpMwpV3nXjpOHuBXtFv7aiO2xRuQS6OoAdgkNcSNug==",
+      "dev": true,
+      "requires": {
+        "css-parse": "~2.0.0",
+        "debug": "~3.1.0",
+        "glob": "^7.1.3",
+        "mkdirp": "~0.5.x",
+        "safer-buffer": "^2.1.2",
+        "sax": "~1.2.4",
+        "semver": "^6.0.0",
+        "source-map": "^0.7.3"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        },
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+          "dev": true
+        },
+        "source-map": {
+          "version": "0.7.3",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+          "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+          "dev": true
+        }
+      }
+    },
+    "stylus-loader": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-3.0.2.tgz",
+      "integrity": "sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA==",
+      "dev": true,
+      "requires": {
+        "loader-utils": "^1.0.2",
+        "lodash.clonedeep": "^4.5.0",
+        "when": "~3.6.x"
+      }
+    },
+    "supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+      "dev": true,
+      "requires": {
+        "has-flag": "^3.0.0"
+      }
+    },
+    "svgo": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz",
+      "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==",
+      "dev": true,
+      "requires": {
+        "chalk": "^2.4.1",
+        "coa": "^2.0.2",
+        "css-select": "^2.0.0",
+        "css-select-base-adapter": "^0.1.1",
+        "css-tree": "1.0.0-alpha.37",
+        "csso": "^4.0.2",
+        "js-yaml": "^3.13.1",
+        "mkdirp": "~0.5.1",
+        "object.values": "^1.1.0",
+        "sax": "~1.2.4",
+        "stable": "^0.1.8",
+        "unquote": "~1.1.1",
+        "util.promisify": "~1.0.0"
+      }
+    },
+    "symbol-observable": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
+      "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==",
+      "dev": true
+    },
+    "tapable": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
+      "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+      "dev": true
+    },
+    "tar": {
+      "version": "4.4.13",
+      "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz",
+      "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==",
+      "dev": true,
+      "requires": {
+        "chownr": "^1.1.1",
+        "fs-minipass": "^1.2.5",
+        "minipass": "^2.8.6",
+        "minizlib": "^1.2.1",
+        "mkdirp": "^0.5.0",
+        "safe-buffer": "^5.1.2",
+        "yallist": "^3.0.3"
+      },
+      "dependencies": {
+        "fs-minipass": {
+          "version": "1.2.7",
+          "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
+          "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
+          "dev": true,
+          "requires": {
+            "minipass": "^2.6.0"
+          }
+        },
+        "minipass": {
+          "version": "2.9.0",
+          "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
+          "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
+          "dev": true,
+          "requires": {
+            "safe-buffer": "^5.1.2",
+            "yallist": "^3.0.0"
+          }
+        }
+      }
+    },
+    "tar-stream": {
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz",
+      "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==",
+      "requires": {
+        "bl": "^1.0.0",
+        "buffer-alloc": "^1.2.0",
+        "end-of-stream": "^1.0.0",
+        "fs-constants": "^1.0.0",
+        "readable-stream": "^2.3.0",
+        "to-buffer": "^1.1.1",
+        "xtend": "^4.0.0"
+      }
+    },
+    "terser": {
+      "version": "4.5.1",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-4.5.1.tgz",
+      "integrity": "sha512-lH9zLIbX8PRBEFCTvfHGCy0s9HEKnNso1Dx9swSopF3VUnFLB8DpQ61tHxoofovNC/sG0spajJM3EIIRSTByiQ==",
+      "dev": true,
+      "requires": {
+        "commander": "^2.20.0",
+        "source-map": "~0.6.1",
+        "source-map-support": "~0.5.12"
+      }
+    },
+    "terser-webpack-plugin": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.3.tgz",
+      "integrity": "sha512-gWHkaGzGYjmDoYxksFZynWTzvXOAjQ5dd7xuTMYlv4zpWlLSb6v0QLSZjELzP5dMs1ox30O1BIPs9dgqlMHuLQ==",
+      "dev": true,
+      "requires": {
+        "cacache": "^13.0.1",
+        "find-cache-dir": "^3.2.0",
+        "jest-worker": "^25.1.0",
+        "p-limit": "^2.2.2",
+        "schema-utils": "^2.6.4",
+        "serialize-javascript": "^2.1.2",
+        "source-map": "^0.6.1",
+        "terser": "^4.4.3",
+        "webpack-sources": "^1.4.3"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "6.12.3",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz",
+          "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==",
+          "dev": true,
+          "requires": {
+            "fast-deep-equal": "^3.1.1",
+            "fast-json-stable-stringify": "^2.0.0",
+            "json-schema-traverse": "^0.4.1",
+            "uri-js": "^4.2.2"
+          }
+        },
+        "fast-deep-equal": {
+          "version": "3.1.3",
+          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+          "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+          "dev": true
+        },
+        "find-cache-dir": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz",
+          "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==",
+          "dev": true,
+          "requires": {
+            "commondir": "^1.0.1",
+            "make-dir": "^3.0.2",
+            "pkg-dir": "^4.1.0"
+          }
+        },
+        "find-up": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+          "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+          "dev": true,
+          "requires": {
+            "locate-path": "^5.0.0",
+            "path-exists": "^4.0.0"
+          }
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "jest-worker": {
+          "version": "25.5.0",
+          "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.5.0.tgz",
+          "integrity": "sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw==",
+          "dev": true,
+          "requires": {
+            "merge-stream": "^2.0.0",
+            "supports-color": "^7.0.0"
+          }
+        },
+        "locate-path": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+          "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+          "dev": true,
+          "requires": {
+            "p-locate": "^4.1.0"
+          }
+        },
+        "make-dir": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+          "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+          "dev": true,
+          "requires": {
+            "semver": "^6.0.0"
+          }
+        },
+        "p-locate": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+          "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+          "dev": true,
+          "requires": {
+            "p-limit": "^2.2.0"
+          }
+        },
+        "path-exists": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+          "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+          "dev": true
+        },
+        "pkg-dir": {
+          "version": "4.2.0",
+          "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+          "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+          "dev": true,
+          "requires": {
+            "find-up": "^4.0.0"
+          }
+        },
+        "schema-utils": {
+          "version": "2.7.0",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
+          "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
+          "dev": true,
+          "requires": {
+            "@types/json-schema": "^7.0.4",
+            "ajv": "^6.12.2",
+            "ajv-keywords": "^3.4.1"
+          }
+        },
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+          "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
+    },
+    "thenify": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
+      "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
+      "requires": {
+        "any-promise": "^1.0.0"
+      }
+    },
+    "thenify-all": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+      "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=",
+      "requires": {
+        "thenify": ">= 3.1.0 < 4"
+      }
+    },
+    "through": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+      "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
+    },
+    "through2": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+      "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+      "dev": true,
+      "requires": {
+        "readable-stream": "~2.3.6",
+        "xtend": "~4.0.1"
+      }
+    },
+    "thunkify": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/thunkify/-/thunkify-2.1.2.tgz",
+      "integrity": "sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0="
+    },
+    "thunky": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
+      "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
+      "dev": true
+    },
+    "timers-browserify": {
+      "version": "2.0.11",
+      "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz",
+      "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==",
+      "dev": true,
+      "requires": {
+        "setimmediate": "^1.0.4"
+      }
+    },
+    "timsort": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
+      "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
+      "dev": true
+    },
+    "tinycolor2": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
+      "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g="
+    },
+    "tmp": {
+      "version": "0.0.33",
+      "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+      "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+      "dev": true,
+      "requires": {
+        "os-tmpdir": "~1.0.2"
+      }
+    },
+    "to-array": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
+      "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=",
+      "dev": true
+    },
+    "to-arraybuffer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
+      "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M="
+    },
+    "to-buffer": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz",
+      "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg=="
+    },
+    "to-fast-properties": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+      "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+      "dev": true
+    },
+    "to-object-path": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+      "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+      "dev": true,
+      "requires": {
+        "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "to-regex": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+      "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+      "dev": true,
+      "requires": {
+        "define-property": "^2.0.2",
+        "extend-shallow": "^3.0.2",
+        "regex-not": "^1.0.2",
+        "safe-regex": "^1.1.0"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+          "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+          "dev": true,
+          "requires": {
+            "assign-symbols": "^1.0.0",
+            "is-extendable": "^1.0.1"
+          }
+        },
+        "is-extendable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+          "dev": true,
+          "requires": {
+            "is-plain-object": "^2.0.4"
+          }
+        }
+      }
+    },
+    "to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dev": true,
+      "requires": {
+        "is-number": "^7.0.0"
+      }
+    },
+    "toidentifier": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
+      "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
+    },
+    "tough-cookie": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+      "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+      "dev": true,
+      "requires": {
+        "psl": "^1.1.28",
+        "punycode": "^2.1.1"
+      }
+    },
+    "tree-kill": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+      "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
+      "dev": true
+    },
+    "ts-node": {
+      "version": "8.3.0",
+      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz",
+      "integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==",
+      "dev": true,
+      "requires": {
+        "arg": "^4.1.0",
+        "diff": "^4.0.1",
+        "make-error": "^1.1.1",
+        "source-map-support": "^0.5.6",
+        "yn": "^3.0.0"
+      }
+    },
+    "tslib": {
+      "version": "1.13.0",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
+      "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q=="
+    },
+    "tslint": {
+      "version": "5.18.0",
+      "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.18.0.tgz",
+      "integrity": "sha512-Q3kXkuDEijQ37nXZZLKErssQVnwCV/+23gFEMROi8IlbaBG6tXqLPQJ5Wjcyt/yHPKBC+hD5SzuGaMora+ZS6w==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.0.0",
+        "builtin-modules": "^1.1.1",
+        "chalk": "^2.3.0",
+        "commander": "^2.12.1",
+        "diff": "^3.2.0",
+        "glob": "^7.1.1",
+        "js-yaml": "^3.13.1",
+        "minimatch": "^3.0.4",
+        "mkdirp": "^0.5.1",
+        "resolve": "^1.3.2",
+        "semver": "^5.3.0",
+        "tslib": "^1.8.0",
+        "tsutils": "^2.29.0"
+      },
+      "dependencies": {
+        "diff": {
+          "version": "3.5.0",
+          "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
+          "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
+          "dev": true
+        }
+      }
+    },
+    "tsutils": {
+      "version": "2.29.0",
+      "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
+      "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
+      "dev": true,
+      "requires": {
+        "tslib": "^1.8.1"
+      }
+    },
+    "tty-browserify": {
+      "version": "0.0.0",
+      "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
+      "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=",
+      "dev": true
+    },
+    "tunnel-agent": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+      "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+      "dev": true,
+      "requires": {
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "tweetnacl": {
+      "version": "0.14.5",
+      "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+      "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+      "dev": true
+    },
+    "type-check": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+      "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+      "requires": {
+        "prelude-ls": "~1.1.2"
+      }
+    },
+    "type-fest": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz",
+      "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==",
+      "dev": true
+    },
+    "type-is": {
+      "version": "1.6.18",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+      "dev": true,
+      "requires": {
+        "media-typer": "0.3.0",
+        "mime-types": "~2.1.24"
+      }
+    },
+    "typedarray": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+      "dev": true
+    },
+    "typescript": {
+      "version": "3.7.5",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz",
+      "integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==",
+      "dev": true
+    },
+    "ultron": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
+      "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==",
+      "dev": true
+    },
+    "unescape": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/unescape/-/unescape-1.0.1.tgz",
+      "integrity": "sha512-O0+af1Gs50lyH1nUu3ZyYS1cRh01Q/kUKatTOkSs7jukXE6/NebucDVxyiDsA9AQ4JC1V1jUH9EO8JX2nMDgGQ==",
+      "requires": {
+        "extend-shallow": "^2.0.1"
+      }
+    },
+    "unicode-canonical-property-names-ecmascript": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
+      "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==",
+      "dev": true
+    },
+    "unicode-match-property-ecmascript": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz",
+      "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==",
+      "dev": true,
+      "requires": {
+        "unicode-canonical-property-names-ecmascript": "^1.0.4",
+        "unicode-property-aliases-ecmascript": "^1.0.4"
+      }
+    },
+    "unicode-match-property-value-ecmascript": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz",
+      "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==",
+      "dev": true
+    },
+    "unicode-property-aliases-ecmascript": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz",
+      "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==",
+      "dev": true
+    },
+    "union-value": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
+      "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+      "dev": true,
+      "requires": {
+        "arr-union": "^3.1.0",
+        "get-value": "^2.0.6",
+        "is-extendable": "^0.1.1",
+        "set-value": "^2.0.1"
+      }
+    },
+    "uniq": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
+      "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=",
+      "dev": true
+    },
+    "uniqs": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz",
+      "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=",
+      "dev": true
+    },
+    "unique-filename": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
+      "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
+      "dev": true,
+      "requires": {
+        "unique-slug": "^2.0.0"
+      }
+    },
+    "unique-slug": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz",
+      "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==",
+      "dev": true,
+      "requires": {
+        "imurmurhash": "^0.1.4"
+      }
+    },
+    "universal-analytics": {
+      "version": "0.4.23",
+      "resolved": "https://registry.npmjs.org/universal-analytics/-/universal-analytics-0.4.23.tgz",
+      "integrity": "sha512-lgMIH7XBI6OgYn1woDEmxhGdj8yDefMKg7GkWdeATAlQZFrMrNyxSkpDzY57iY0/6fdlzTbBV03OawvvzG+q7A==",
+      "dev": true,
+      "requires": {
+        "debug": "^4.1.1",
+        "request": "^2.88.2",
+        "uuid": "^3.0.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+          "dev": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        }
+      }
+    },
+    "universalify": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+      "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+      "dev": true
+    },
+    "unpipe": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
+    },
+    "unquote": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz",
+      "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=",
+      "dev": true
+    },
+    "unset-value": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+      "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+      "dev": true,
+      "requires": {
+        "has-value": "^0.3.1",
+        "isobject": "^3.0.0"
+      },
+      "dependencies": {
+        "has-value": {
+          "version": "0.3.1",
+          "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+          "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
+          "dev": true,
+          "requires": {
+            "get-value": "^2.0.3",
+            "has-values": "^0.1.4",
+            "isobject": "^2.0.0"
+          },
+          "dependencies": {
+            "isobject": {
+              "version": "2.1.0",
+              "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+              "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+              "dev": true,
+              "requires": {
+                "isarray": "1.0.0"
+              }
+            }
+          }
+        },
+        "has-values": {
+          "version": "0.1.4",
+          "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+          "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
+          "dev": true
+        }
+      }
+    },
+    "upath": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
+      "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
+      "dev": true
+    },
+    "uri-js": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
+      "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+      "dev": true,
+      "requires": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "urix": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+      "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
+      "dev": true
+    },
+    "url": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
+      "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
+      "dev": true,
+      "requires": {
+        "punycode": "1.3.2",
+        "querystring": "0.2.0"
+      },
+      "dependencies": {
+        "punycode": {
+          "version": "1.3.2",
+          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+          "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
+          "dev": true
+        }
+      }
+    },
+    "url-parse": {
+      "version": "1.4.7",
+      "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz",
+      "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==",
+      "dev": true,
+      "requires": {
+        "querystringify": "^2.1.1",
+        "requires-port": "^1.0.0"
+      }
+    },
+    "urllib": {
+      "version": "2.36.1",
+      "resolved": "https://registry.npmjs.org/urllib/-/urllib-2.36.1.tgz",
+      "integrity": "sha512-g0Gh7bH5AwfPUzFetxPtJwumGHE6D7KQn0K68MwcJXPgO2K0AliwEIxLAwGMF+TpY75DYAsvz1h9ekagYoq33w==",
+      "requires": {
+        "any-promise": "^1.3.0",
+        "content-type": "^1.0.2",
+        "debug": "^2.6.9",
+        "default-user-agent": "^1.0.0",
+        "digest-header": "^0.0.1",
+        "ee-first": "~1.1.1",
+        "formstream": "^1.1.0",
+        "humanize-ms": "^1.2.0",
+        "iconv-lite": "^0.4.15",
+        "ip": "^1.1.5",
+        "proxy-agent": "^3.1.0",
+        "pump": "^3.0.0",
+        "qs": "^6.4.0",
+        "statuses": "^1.3.1",
+        "utility": "^1.16.1"
+      }
+    },
+    "use": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
+      "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+      "dev": true
+    },
+    "useragent": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz",
+      "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==",
+      "dev": true,
+      "requires": {
+        "lru-cache": "4.1.x",
+        "tmp": "0.0.x"
+      },
+      "dependencies": {
+        "lru-cache": {
+          "version": "4.1.5",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+          "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+          "dev": true,
+          "requires": {
+            "pseudomap": "^1.0.2",
+            "yallist": "^2.1.2"
+          }
+        },
+        "yallist": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+          "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+          "dev": true
+        }
+      }
+    },
+    "util": {
+      "version": "0.11.1",
+      "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
+      "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==",
+      "dev": true,
+      "requires": {
+        "inherits": "2.0.3"
+      },
+      "dependencies": {
+        "inherits": {
+          "version": "2.0.3",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+          "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+          "dev": true
+        }
+      }
+    },
+    "util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+    },
+    "util-promisify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/util-promisify/-/util-promisify-2.1.0.tgz",
+      "integrity": "sha1-PCI2R2xNMsX/PEcAKt18E7moKlM=",
+      "dev": true,
+      "requires": {
+        "object.getownpropertydescriptors": "^2.0.3"
+      }
+    },
+    "util.promisify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz",
+      "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==",
+      "dev": true,
+      "requires": {
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.2",
+        "has-symbols": "^1.0.1",
+        "object.getownpropertydescriptors": "^2.1.0"
+      }
+    },
+    "utility": {
+      "version": "1.16.3",
+      "resolved": "https://registry.npmjs.org/utility/-/utility-1.16.3.tgz",
+      "integrity": "sha512-kYghm8fknkPkUd9ncODj/b2Zojc23gnRC29QmpmL3BBsSX6W++RNRTQ1tB7l0UA7d4SisIjUyvRfVET3lBwurw==",
+      "requires": {
+        "copy-to": "^2.0.1",
+        "escape-html": "^1.0.3",
+        "mkdirp": "^0.5.1",
+        "mz": "^2.7.0",
+        "unescape": "^1.0.1"
+      }
+    },
+    "utils-merge": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+      "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
+      "dev": true
+    },
+    "uuid": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+      "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+      "dev": true
+    },
+    "validate-npm-package-license": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+      "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+      "dev": true,
+      "requires": {
+        "spdx-correct": "^3.0.0",
+        "spdx-expression-parse": "^3.0.0"
+      }
+    },
+    "validate-npm-package-name": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz",
+      "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=",
+      "dev": true,
+      "requires": {
+        "builtins": "^1.0.3"
+      }
+    },
+    "vary": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+      "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
+      "dev": true
+    },
+    "vendors": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz",
+      "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==",
+      "dev": true
+    },
+    "verror": {
+      "version": "1.10.0",
+      "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+      "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+      "dev": true,
+      "requires": {
+        "assert-plus": "^1.0.0",
+        "core-util-is": "1.0.2",
+        "extsprintf": "^1.2.0"
+      }
+    },
+    "vm-browserify": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz",
+      "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
+      "dev": true
+    },
+    "void-elements": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
+      "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=",
+      "dev": true
+    },
+    "watchpack": {
+      "version": "1.7.4",
+      "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.4.tgz",
+      "integrity": "sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg==",
+      "dev": true,
+      "requires": {
+        "chokidar": "^3.4.1",
+        "graceful-fs": "^4.1.2",
+        "neo-async": "^2.5.0",
+        "watchpack-chokidar2": "^2.0.0"
+      }
+    },
+    "watchpack-chokidar2": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz",
+      "integrity": "sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "chokidar": "^2.1.8"
+      },
+      "dependencies": {
+        "anymatch": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+          "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "micromatch": "^3.1.4",
+            "normalize-path": "^2.1.1"
+          },
+          "dependencies": {
+            "normalize-path": {
+              "version": "2.1.1",
+              "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+              "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+              "dev": true,
+              "optional": true,
+              "requires": {
+                "remove-trailing-separator": "^1.0.1"
+              }
+            }
+          }
+        },
+        "binary-extensions": {
+          "version": "1.13.1",
+          "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
+          "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
+          "dev": true,
+          "optional": true
+        },
+        "braces": {
+          "version": "2.3.2",
+          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "arr-flatten": "^1.1.0",
+            "array-unique": "^0.3.2",
+            "extend-shallow": "^2.0.1",
+            "fill-range": "^4.0.0",
+            "isobject": "^3.0.1",
+            "repeat-element": "^1.1.2",
+            "snapdragon": "^0.8.1",
+            "snapdragon-node": "^2.0.1",
+            "split-string": "^3.0.2",
+            "to-regex": "^3.0.1"
+          }
+        },
+        "chokidar": {
+          "version": "2.1.8",
+          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
+          "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "anymatch": "^2.0.0",
+            "async-each": "^1.0.1",
+            "braces": "^2.3.2",
+            "fsevents": "^1.2.7",
+            "glob-parent": "^3.1.0",
+            "inherits": "^2.0.3",
+            "is-binary-path": "^1.0.0",
+            "is-glob": "^4.0.0",
+            "normalize-path": "^3.0.0",
+            "path-is-absolute": "^1.0.0",
+            "readdirp": "^2.2.1",
+            "upath": "^1.1.1"
+          }
+        },
+        "fill-range": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "extend-shallow": "^2.0.1",
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1",
+            "to-regex-range": "^2.1.0"
+          }
+        },
+        "fsevents": {
+          "version": "1.2.13",
+          "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
+          "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
+          "dev": true,
+          "optional": true
+        },
+        "is-binary-path": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
+          "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "binary-extensions": "^1.0.0"
+          }
+        },
+        "is-number": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "kind-of": "^3.0.2"
+          }
+        },
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        },
+        "readdirp": {
+          "version": "2.2.1",
+          "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+          "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "graceful-fs": "^4.1.11",
+            "micromatch": "^3.1.10",
+            "readable-stream": "^2.0.2"
+          }
+        },
+        "to-regex-range": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+          "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1"
+          }
+        }
+      }
+    },
+    "wbuf": {
+      "version": "1.7.3",
+      "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz",
+      "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==",
+      "dev": true,
+      "requires": {
+        "minimalistic-assert": "^1.0.0"
+      }
+    },
+    "wcwidth": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
+      "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=",
+      "dev": true,
+      "requires": {
+        "defaults": "^1.0.3"
+      }
+    },
+    "webdriver-js-extender": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz",
+      "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==",
+      "dev": true,
+      "requires": {
+        "@types/selenium-webdriver": "^3.0.0",
+        "selenium-webdriver": "^3.0.1"
+      }
+    },
+    "webpack": {
+      "version": "4.41.2",
+      "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.2.tgz",
+      "integrity": "sha512-Zhw69edTGfbz9/8JJoyRQ/pq8FYUoY0diOXqW0T6yhgdhCv6wr0hra5DwwWexNRns2Z2+gsnrNcbe9hbGBgk/A==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.8.5",
+        "@webassemblyjs/helper-module-context": "1.8.5",
+        "@webassemblyjs/wasm-edit": "1.8.5",
+        "@webassemblyjs/wasm-parser": "1.8.5",
+        "acorn": "^6.2.1",
+        "ajv": "^6.10.2",
+        "ajv-keywords": "^3.4.1",
+        "chrome-trace-event": "^1.0.2",
+        "enhanced-resolve": "^4.1.0",
+        "eslint-scope": "^4.0.3",
+        "json-parse-better-errors": "^1.0.2",
+        "loader-runner": "^2.4.0",
+        "loader-utils": "^1.2.3",
+        "memory-fs": "^0.4.1",
+        "micromatch": "^3.1.10",
+        "mkdirp": "^0.5.1",
+        "neo-async": "^2.6.1",
+        "node-libs-browser": "^2.2.1",
+        "schema-utils": "^1.0.0",
+        "tapable": "^1.1.3",
+        "terser-webpack-plugin": "^1.4.1",
+        "watchpack": "^1.6.0",
+        "webpack-sources": "^1.4.1"
+      },
+      "dependencies": {
+        "acorn": {
+          "version": "6.4.1",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
+          "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==",
+          "dev": true
+        },
+        "cacache": {
+          "version": "12.0.4",
+          "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz",
+          "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==",
+          "dev": true,
+          "requires": {
+            "bluebird": "^3.5.5",
+            "chownr": "^1.1.1",
+            "figgy-pudding": "^3.5.1",
+            "glob": "^7.1.4",
+            "graceful-fs": "^4.1.15",
+            "infer-owner": "^1.0.3",
+            "lru-cache": "^5.1.1",
+            "mississippi": "^3.0.0",
+            "mkdirp": "^0.5.1",
+            "move-concurrently": "^1.0.1",
+            "promise-inflight": "^1.0.1",
+            "rimraf": "^2.6.3",
+            "ssri": "^6.0.1",
+            "unique-filename": "^1.1.1",
+            "y18n": "^4.0.0"
+          }
+        },
+        "find-cache-dir": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
+          "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==",
+          "dev": true,
+          "requires": {
+            "commondir": "^1.0.1",
+            "make-dir": "^2.0.0",
+            "pkg-dir": "^3.0.0"
+          }
+        },
+        "is-wsl": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
+          "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
+          "dev": true
+        },
+        "memory-fs": {
+          "version": "0.4.1",
+          "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
+          "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
+          "dev": true,
+          "requires": {
+            "errno": "^0.1.3",
+            "readable-stream": "^2.0.1"
+          }
+        },
+        "serialize-javascript": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz",
+          "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==",
+          "dev": true,
+          "requires": {
+            "randombytes": "^2.1.0"
+          }
+        },
+        "ssri": {
+          "version": "6.0.1",
+          "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz",
+          "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
+          "dev": true,
+          "requires": {
+            "figgy-pudding": "^3.5.1"
+          }
+        },
+        "terser-webpack-plugin": {
+          "version": "1.4.4",
+          "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.4.tgz",
+          "integrity": "sha512-U4mACBHIegmfoEe5fdongHESNJWqsGU+W0S/9+BmYGVQDw1+c2Ow05TpMhxjPK1sRb7cuYq1BPl1e5YHJMTCqA==",
+          "dev": true,
+          "requires": {
+            "cacache": "^12.0.2",
+            "find-cache-dir": "^2.1.0",
+            "is-wsl": "^1.1.0",
+            "schema-utils": "^1.0.0",
+            "serialize-javascript": "^3.1.0",
+            "source-map": "^0.6.1",
+            "terser": "^4.1.2",
+            "webpack-sources": "^1.4.0",
+            "worker-farm": "^1.7.0"
+          }
+        }
+      }
+    },
+    "webpack-dev-middleware": {
+      "version": "3.7.2",
+      "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz",
+      "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==",
+      "dev": true,
+      "requires": {
+        "memory-fs": "^0.4.1",
+        "mime": "^2.4.4",
+        "mkdirp": "^0.5.1",
+        "range-parser": "^1.2.1",
+        "webpack-log": "^2.0.0"
+      },
+      "dependencies": {
+        "memory-fs": {
+          "version": "0.4.1",
+          "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
+          "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
+          "dev": true,
+          "requires": {
+            "errno": "^0.1.3",
+            "readable-stream": "^2.0.1"
+          }
+        }
+      }
+    },
+    "webpack-dev-server": {
+      "version": "3.9.0",
+      "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.9.0.tgz",
+      "integrity": "sha512-E6uQ4kRrTX9URN9s/lIbqTAztwEPdvzVrcmHE8EQ9YnuT9J8Es5Wrd8n9BKg1a0oZ5EgEke/EQFgUsp18dSTBw==",
+      "dev": true,
+      "requires": {
+        "ansi-html": "0.0.7",
+        "bonjour": "^3.5.0",
+        "chokidar": "^2.1.8",
+        "compression": "^1.7.4",
+        "connect-history-api-fallback": "^1.6.0",
+        "debug": "^4.1.1",
+        "del": "^4.1.1",
+        "express": "^4.17.1",
+        "html-entities": "^1.2.1",
+        "http-proxy-middleware": "0.19.1",
+        "import-local": "^2.0.0",
+        "internal-ip": "^4.3.0",
+        "ip": "^1.1.5",
+        "is-absolute-url": "^3.0.3",
+        "killable": "^1.0.1",
+        "loglevel": "^1.6.4",
+        "opn": "^5.5.0",
+        "p-retry": "^3.0.1",
+        "portfinder": "^1.0.25",
+        "schema-utils": "^1.0.0",
+        "selfsigned": "^1.10.7",
+        "semver": "^6.3.0",
+        "serve-index": "^1.9.1",
+        "sockjs": "0.3.19",
+        "sockjs-client": "1.4.0",
+        "spdy": "^4.0.1",
+        "strip-ansi": "^3.0.1",
+        "supports-color": "^6.1.0",
+        "url": "^0.11.0",
+        "webpack-dev-middleware": "^3.7.2",
+        "webpack-log": "^2.0.0",
+        "ws": "^6.2.1",
+        "yargs": "12.0.5"
+      },
+      "dependencies": {
+        "anymatch": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+          "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+          "dev": true,
+          "requires": {
+            "micromatch": "^3.1.4",
+            "normalize-path": "^2.1.1"
+          },
+          "dependencies": {
+            "normalize-path": {
+              "version": "2.1.1",
+              "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+              "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+              "dev": true,
+              "requires": {
+                "remove-trailing-separator": "^1.0.1"
+              }
+            }
+          }
+        },
+        "binary-extensions": {
+          "version": "1.13.1",
+          "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
+          "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
+          "dev": true
+        },
+        "braces": {
+          "version": "2.3.2",
+          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+          "dev": true,
+          "requires": {
+            "arr-flatten": "^1.1.0",
+            "array-unique": "^0.3.2",
+            "extend-shallow": "^2.0.1",
+            "fill-range": "^4.0.0",
+            "isobject": "^3.0.1",
+            "repeat-element": "^1.1.2",
+            "snapdragon": "^0.8.1",
+            "snapdragon-node": "^2.0.1",
+            "split-string": "^3.0.2",
+            "to-regex": "^3.0.1"
+          }
+        },
+        "chokidar": {
+          "version": "2.1.8",
+          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
+          "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
+          "dev": true,
+          "requires": {
+            "anymatch": "^2.0.0",
+            "async-each": "^1.0.1",
+            "braces": "^2.3.2",
+            "fsevents": "^1.2.7",
+            "glob-parent": "^3.1.0",
+            "inherits": "^2.0.3",
+            "is-binary-path": "^1.0.0",
+            "is-glob": "^4.0.0",
+            "normalize-path": "^3.0.0",
+            "path-is-absolute": "^1.0.0",
+            "readdirp": "^2.2.1",
+            "upath": "^1.1.1"
+          }
+        },
+        "debug": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+          "dev": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "fill-range": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+          "dev": true,
+          "requires": {
+            "extend-shallow": "^2.0.1",
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1",
+            "to-regex-range": "^2.1.0"
+          }
+        },
+        "fsevents": {
+          "version": "1.2.13",
+          "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
+          "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
+          "dev": true,
+          "optional": true
+        },
+        "is-absolute-url": {
+          "version": "3.0.3",
+          "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz",
+          "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==",
+          "dev": true
+        },
+        "is-binary-path": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
+          "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+          "dev": true,
+          "requires": {
+            "binary-extensions": "^1.0.0"
+          }
+        },
+        "is-number": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+          "dev": true,
+          "requires": {
+            "kind-of": "^3.0.2"
+          }
+        },
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        },
+        "readdirp": {
+          "version": "2.2.1",
+          "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+          "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "^4.1.11",
+            "micromatch": "^3.1.10",
+            "readable-stream": "^2.0.2"
+          }
+        },
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+          "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        },
+        "to-regex-range": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+          "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+          "dev": true,
+          "requires": {
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1"
+          }
+        }
+      }
+    },
+    "webpack-log": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz",
+      "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==",
+      "dev": true,
+      "requires": {
+        "ansi-colors": "^3.0.0",
+        "uuid": "^3.3.2"
+      }
+    },
+    "webpack-merge": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz",
+      "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==",
+      "dev": true,
+      "requires": {
+        "lodash": "^4.17.15"
+      }
+    },
+    "webpack-sources": {
+      "version": "1.4.3",
+      "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
+      "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==",
+      "dev": true,
+      "requires": {
+        "source-list-map": "^2.0.0",
+        "source-map": "~0.6.1"
+      }
+    },
+    "webpack-subresource-integrity": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.3.4.tgz",
+      "integrity": "sha512-6XbGYzjh30cGQT/NsC+9IAkJP8IL7/t47sbwR5DLSsamiD56Rwv4/+hsgEHsviPvrEFZ0JRAQtCRN3UsR2Pw9g==",
+      "dev": true,
+      "requires": {
+        "webpack-sources": "^1.3.0"
+      }
+    },
+    "websocket-driver": {
+      "version": "0.7.4",
+      "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
+      "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
+      "dev": true,
+      "requires": {
+        "http-parser-js": ">=0.5.1",
+        "safe-buffer": ">=5.1.0",
+        "websocket-extensions": ">=0.1.1"
+      }
+    },
+    "websocket-extensions": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
+      "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
+      "dev": true
+    },
+    "when": {
+      "version": "3.6.4",
+      "resolved": "https://registry.npmjs.org/when/-/when-3.6.4.tgz",
+      "integrity": "sha1-RztRfsFZ4rhQBUl6E5g/CVQS404=",
+      "dev": true
+    },
+    "which": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+      "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+      "dev": true,
+      "requires": {
+        "isexe": "^2.0.0"
+      }
+    },
+    "which-module": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+      "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
+      "dev": true
+    },
+    "win-release": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/win-release/-/win-release-1.1.1.tgz",
+      "integrity": "sha1-X6VeAr58qTTt/BJmVjLoSbcuUgk=",
+      "requires": {
+        "semver": "^5.0.1"
+      }
+    },
+    "word-wrap": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+      "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ=="
+    },
+    "wordwrap": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
+      "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=",
+      "dev": true
+    },
+    "worker-farm": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
+      "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==",
+      "dev": true,
+      "requires": {
+        "errno": "~0.1.7"
+      }
+    },
+    "worker-plugin": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/worker-plugin/-/worker-plugin-3.2.0.tgz",
+      "integrity": "sha512-W5nRkw7+HlbsEt3qRP6MczwDDISjiRj2GYt9+bpe8A2La00TmJdwzG5bpdMXhRt1qcWmwAvl1TiKaHRa+XDS9Q==",
+      "dev": true,
+      "requires": {
+        "loader-utils": "^1.1.0"
+      }
+    },
+    "wrap-ansi": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+      "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
+      "dev": true,
+      "requires": {
+        "string-width": "^1.0.1",
+        "strip-ansi": "^3.0.1"
+      },
+      "dependencies": {
+        "is-fullwidth-code-point": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+          "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+          "dev": true,
+          "requires": {
+            "number-is-nan": "^1.0.0"
+          }
+        },
+        "string-width": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+          "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+          "dev": true,
+          "requires": {
+            "code-point-at": "^1.0.0",
+            "is-fullwidth-code-point": "^1.0.0",
+            "strip-ansi": "^3.0.0"
+          }
+        }
+      }
+    },
+    "wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+    },
+    "ws": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
+      "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
+      "dev": true,
+      "requires": {
+        "async-limiter": "~1.0.0"
+      }
+    },
+    "xml2js": {
+      "version": "0.4.23",
+      "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
+      "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
+      "requires": {
+        "sax": ">=0.6.0",
+        "xmlbuilder": "~11.0.0"
+      }
+    },
+    "xmlbuilder": {
+      "version": "11.0.1",
+      "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
+      "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="
+    },
+    "xmlhttprequest-ssl": {
+      "version": "1.5.5",
+      "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
+      "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=",
+      "dev": true
+    },
+    "xregexp": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz",
+      "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM="
+    },
+    "xtend": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
+    },
+    "y18n": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
+      "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
+      "dev": true
+    },
+    "yallist": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+      "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
+    },
+    "yargs": {
+      "version": "12.0.5",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
+      "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==",
+      "dev": true,
+      "requires": {
+        "cliui": "^4.0.0",
+        "decamelize": "^1.2.0",
+        "find-up": "^3.0.0",
+        "get-caller-file": "^1.0.1",
+        "os-locale": "^3.0.0",
+        "require-directory": "^2.1.1",
+        "require-main-filename": "^1.0.1",
+        "set-blocking": "^2.0.0",
+        "string-width": "^2.0.0",
+        "which-module": "^2.0.0",
+        "y18n": "^3.2.1 || ^4.0.0",
+        "yargs-parser": "^11.1.1"
+      }
+    },
+    "yargs-parser": {
+      "version": "11.1.1",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz",
+      "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==",
+      "dev": true,
+      "requires": {
+        "camelcase": "^5.0.0",
+        "decamelize": "^1.2.0"
+      }
+    },
+    "yauzl": {
+      "version": "2.10.0",
+      "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
+      "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=",
+      "requires": {
+        "buffer-crc32": "~0.2.3",
+        "fd-slicer": "~1.1.0"
+      }
+    },
+    "yazl": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz",
+      "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==",
+      "requires": {
+        "buffer-crc32": "~0.2.3"
+      }
+    },
+    "yeast": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
+      "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=",
+      "dev": true
+    },
+    "yn": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+      "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+      "dev": true
+    },
+    "zone.js": {
+      "version": "0.10.3",
+      "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.10.3.tgz",
+      "integrity": "sha512-LXVLVEq0NNOqK/fLJo3d0kfzd4sxwn2/h67/02pjCjfKDxgx1i9QqpvtHD8CrBnSSwMw5+dy11O7FRX5mkO7Cg=="
+    }
+  }
+}
diff --git a/form/package.json b/form/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..c9142eda9628c3fc9cf5db44b62bc836760bdad5
--- /dev/null
+++ b/form/package.json
@@ -0,0 +1,57 @@
+{
+  "name": "ng-template-generator",
+  "version": "0.0.1",
+  "scripts": {
+    "start": "ng serve",
+    "build": "ng build --build--optimizer --aot --base-href /JGT/v3/",
+    "publish": "node ./bin/publish.js",
+    "ng": "ng",
+    "test": "ng test",
+    "lint": "ng lint",
+    "e2e": "ng e2e"
+  },
+  "private": true,
+  "dependencies": {
+    "@angular/animations": "~9.0.2",
+    "@angular/common": "~9.0.2",
+    "@angular/compiler": "~9.0.2",
+    "@angular/core": "~9.0.2",
+    "@angular/forms": "~9.0.2",
+    "@angular/platform-browser": "~9.0.2",
+    "@angular/platform-browser-dynamic": "~9.0.2",
+    "@angular/router": "~9.0.2",
+    "@fortawesome/angular-fontawesome": "^0.6.0",
+    "@fortawesome/fontawesome-svg-core": "^1.2.27",
+    "@fortawesome/free-regular-svg-icons": "^5.12.1",
+    "@fortawesome/free-solid-svg-icons": "^5.12.1",
+    "@tweenjs/tween.js": "~18.5.0",
+    "ali-oss": "^6.5.1",
+    "compressing": "^1.5.0",
+    "ng-zorro-antd": "^8.5.2",
+    "rxjs": "~6.5.4",
+    "spark-md5": "^3.0.0",
+    "tslib": "^1.10.0",
+    "zone.js": "~0.10.2"
+  },
+  "devDependencies": {
+    "@angular-devkit/build-angular": "~0.900.3",
+    "@angular/cli": "~9.0.3",
+    "@angular/compiler-cli": "~9.0.2",
+    "@angular/language-service": "~9.0.2",
+    "@types/jasmine": "~3.5.0",
+    "@types/jasminewd2": "~2.0.3",
+    "@types/node": "^12.11.1",
+    "codelyzer": "^5.1.2",
+    "jasmine-core": "~3.5.0",
+    "jasmine-spec-reporter": "~4.2.1",
+    "karma": "~4.3.0",
+    "karma-chrome-launcher": "~3.1.0",
+    "karma-coverage-istanbul-reporter": "~2.1.0",
+    "karma-jasmine": "~2.0.1",
+    "karma-jasmine-html-reporter": "^1.4.2",
+    "protractor": "~5.4.3",
+    "ts-node": "~8.3.0",
+    "tslint": "~5.18.0",
+    "typescript": "~3.7.5"
+  }
+}
diff --git a/form/src/app/MyError.ts b/form/src/app/MyError.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e1264c74bd3eb232216890bc6eae9cbd1ef90948
--- /dev/null
+++ b/form/src/app/MyError.ts
@@ -0,0 +1,9 @@
+
+import { ErrorHandler } from '@angular/core';
+
+export class MyErrorHandler implements ErrorHandler {
+  handleError(error) { 
+    console.log(error.stack);
+    (<any> window).courseware.sendErrorLog(error);
+  }
+}
\ No newline at end of file
diff --git a/form/src/app/app.component.html b/form/src/app/app.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..44b4b71485c2ca9ca3fa1b37ea62d72a301864ba
--- /dev/null
+++ b/form/src/app/app.component.html
@@ -0,0 +1,3 @@
+<app-form *ngIf="type==='form'"></app-form>
+
+<app-play *ngIf="type==='play'"></app-play>
\ No newline at end of file
diff --git a/form/src/app/app.component.scss b/form/src/app/app.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/form/src/app/app.component.ts b/form/src/app/app.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..43e76cf1f63519fac5fd34a9ac14b582542b9197
--- /dev/null
+++ b/form/src/app/app.component.ts
@@ -0,0 +1,29 @@
+import { Component , OnInit} from '@angular/core';
+
+@Component({
+  selector: 'app-root',
+  templateUrl: './app.component.html',
+  styleUrls: ['./app.component.scss']
+})
+export class AppComponent implements OnInit {
+  type = 'play';
+
+  constructor() {
+    const tp = this.getQueryString('type');
+    if (tp) {
+      this.type = tp;
+    }
+  }
+
+  ngOnInit() {
+
+  }
+  getQueryString(name) {
+    const reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i');
+    const r = window.location.search.substr(1).match(reg);
+    if (r != null) {
+      return unescape(r[2]);
+    }
+    return null;
+  }
+}
diff --git a/form/src/app/app.module.ts b/form/src/app/app.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a0995a75739d55061953bdc16761ee2f6524aed1
--- /dev/null
+++ b/form/src/app/app.module.ts
@@ -0,0 +1,65 @@
+import { BrowserModule } from '@angular/platform-browser';
+import { NgModule, ErrorHandler } from '@angular/core';
+
+import {MyErrorHandler} from './MyError';
+
+import { AppComponent } from './app.component';
+import { NgZorroAntdModule, NZ_I18N, zh_CN } from 'ng-zorro-antd';
+import { FormsModule } from '@angular/forms';
+import { HttpClientModule } from '@angular/common/http';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { registerLocaleData } from '@angular/common';
+import zh from '@angular/common/locales/zh';
+import {FormComponent} from './form/form.component';
+import {PlayComponent} from './play/play.component';
+import {LessonTitleConfigComponent} from './common/lesson-title-config/lesson-title-config.component';
+import {BackgroundImagePipe} from './pipes/background-image.pipe';
+import {UploadImageWithPreviewComponent} from './common/upload-image-with-preview/upload-image-with-preview.component';
+import {PlayerContentWrapperComponent} from './common/player-content-wrapper/player-content-wrapper.component';
+import {CustomHotZoneComponent} from './common/custom-hot-zone/custom-hot-zone.component';
+import {UploadVideoComponent} from './common/upload-video/upload-video.component';
+import {TimePipe} from './pipes/time.pipe';
+import {ResourcePipe} from './pipes/resource.pipe';
+import {AudioRecorderComponent} from './common/audio-recorder/audio-recorder.component';
+import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
+import { fas } from '@fortawesome/free-solid-svg-icons';
+import { far } from '@fortawesome/free-regular-svg-icons';
+
+registerLocaleData(zh);
+
+@NgModule({
+  declarations: [
+    AppComponent,
+    FormComponent,
+    PlayComponent,
+    LessonTitleConfigComponent,
+    AudioRecorderComponent,
+    UploadImageWithPreviewComponent,
+    BackgroundImagePipe,
+    ResourcePipe,
+    TimePipe,
+    UploadVideoComponent,
+    CustomHotZoneComponent,
+
+    PlayerContentWrapperComponent
+
+  ],
+  imports: [
+    BrowserModule,
+    NgZorroAntdModule,
+    FormsModule,
+    HttpClientModule,
+    BrowserAnimationsModule,
+    FontAwesomeModule
+  ],
+  providers: [
+    {provide: ErrorHandler, useClass: MyErrorHandler},
+    { provide: NZ_I18N, useValue: zh_CN }
+  ],
+  bootstrap: [AppComponent]
+})
+export class AppModule {
+  constructor(library: FaIconLibrary) {
+    library.addIconPacks(fas, far);
+  }
+}
diff --git a/form/src/app/common/audio-recorder/audio-recorder.component.html b/form/src/app/common/audio-recorder/audio-recorder.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..e174c34da6591aeefc1a0d1f9fb3576e6499d645
--- /dev/null
+++ b/form/src/app/common/audio-recorder/audio-recorder.component.html
@@ -0,0 +1,61 @@
+<div class="d-flex">
+  <div class="p-btn-record d-flex">
+    <div class="btn-clear" style="cursor: pointer" (click)="onBtnClearAudio()" *ngIf="withRmBtn && (audioUrl || audioBlob)">
+      <fa-icon icon="times"></fa-icon>
+    </div>
+    <div class="btn-record" *ngIf="type===Type.RECORD && !isUploading"
+         [class.p-recording]="isRecording"
+         (click)="onBtnRecord()">
+      <fa-icon icon="microphone"></fa-icon>
+      Record Audio
+    </div>
+    <nz-upload
+        [nzAccept] = "'.mp3'"
+        [nzShowUploadList]="false"
+        [nzAction]="uploadUrl"
+        [nzData]="uploadData"
+        (nzChange)="handleChange($event)">
+
+      <div class="btn-upload" [ngClass]="{'has-clear': withRmBtn && (audioUrl || audioBlob)}" *ngIf="type===Type.UPLOAD && !isUploading">
+        <fa-icon icon="upload"></fa-icon>
+        Upload Audio
+      </div>
+    </nz-upload>
+    <div class="p-upload-progress-bg" *ngIf="isUploading">
+      <div class="i-bg" [style.width]="progress+'%'"></div>
+      <div class="i-text">
+        <fa-icon icon="cloud-upload-alt"></fa-icon>
+        Uploading...
+      </div>
+    </div>
+
+
+    <div *ngIf="audioUrl && needRemove; then truthyTemplate else falsyTemplate"></div>
+
+    <ng-template #truthyTemplate >
+
+      <div class="btn-delete" (click)="onBtnDeleteAudio()">
+        <fa-icon icon="close"></fa-icon>
+      </div>
+
+    </ng-template>
+
+    <ng-template #falsyTemplate>
+      <div class="btn-switch" (click)="onBtnSwitchType()">
+        <fa-icon icon="cog"></fa-icon>
+      </div>
+    </ng-template>
+
+
+
+
+  </div>
+
+  <div class="p-progress ml-2" (click)="onBtnPlay()" *ngIf="audioUrl || audioBlob">
+    <nz-progress [nzPercent]="percent" [nzWidth]="30" [nzFormat]="progressText"
+                 nzType="circle"></nz-progress>
+    <div class="p-btn-play" [style.left]="isPlaying?'8px':''">
+      <fa-icon [icon]="playIcon"></fa-icon>
+    </div>
+  </div>
+</div>
diff --git a/form/src/app/common/audio-recorder/audio-recorder.component.scss b/form/src/app/common/audio-recorder/audio-recorder.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..8267c9d059853b3be3cbf4cb51ee9dd66214379a
--- /dev/null
+++ b/form/src/app/common/audio-recorder/audio-recorder.component.scss
@@ -0,0 +1,107 @@
+.d-flex{
+  display: flex;
+}
+.p-btn-record {
+  font-size: 0.9rem;
+  color: #555;
+  font-family: "Monospaced Number", "Chinese Quote", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif;
+
+  .btn-record, .btn-upload {
+    cursor: pointer;
+    text-align: center;
+    width: 130px;
+    height: 33px;
+    line-height: 33px;
+    border-radius: 0.5rem 0 0 0.5rem;
+    border: 1px solid #ddd;
+    border-right: 0.5px;
+    &:hover {
+      color: rgb(64, 169, 255);
+    }
+  }
+  .btn-record.has-clear, .btn-upload.has-clear{
+    border-radius:0;
+    border-left: 0;
+  }
+  .btn-clear{
+    text-align: center;
+    color: #aaa;
+    padding: 0 0.5rem;
+    border: 1px solid #ddd;
+    border-radius: 0.5rem 0 0 0.5rem;
+    height: 33px;
+    line-height: 33px;
+  }
+  .btn-switch {
+    text-align: center;
+    color: #aaa;
+    padding: 0 0.5rem;
+    border: 1px solid #ddd;
+    border-radius: 0 0.5rem 0.5rem 0;
+    height: 33px;
+    line-height: 33px;
+    &:hover {
+      color: rgb(64, 169, 255);
+    }
+  }
+  .btn-delete {
+    text-align: center;
+    color: #aaa;
+    padding: 0 0.5rem;
+    border: 1px solid #ddd;
+    border-radius: 0 0.5rem 0.5rem 0;
+    height: 33px;
+    line-height: 33px;
+    &:hover {
+      color: #ec5b56;
+    }
+  }
+}
+
+.p-recording {
+  background: orangered;
+  color: white !important;
+}
+
+.p-upload-progress-bg {
+  position: relative;
+  width: 130px;
+  height: 33px;
+  line-height: 33px;
+  & .i-text {
+    position: absolute;
+    width: 100%;
+    height: 100%;
+    text-align: center;
+    border-radius: 0.5rem 0 0 0.5rem;
+    border: 1px solid #ddd;
+    border-right: 0.5px;
+    color: white;
+    text-shadow: 0 0 2px rgba(0, 0, 0, .85);
+  }
+  & .i-bg {
+    position: absolute;
+    left: 0;
+    top: 0;
+    height: 100%;
+    background-color: #27b43f;
+    border-radius: 0.5rem 0 0 0.5rem;
+  }
+}
+
+.p-progress {
+  margin-top: 2px;
+  position: relative;
+  line-height: 26px;
+  .p-btn-play {
+    position: absolute;
+    left: 10px;
+    top: 3px;
+    color: #555;
+  }
+}
+
+:host ::ng-deep nz-upload {
+  line-height: 33px;
+}
+
diff --git a/form/src/app/common/audio-recorder/audio-recorder.component.ts b/form/src/app/common/audio-recorder/audio-recorder.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..605db0ab773819d944f4b9932a82f5cfb6111f94
--- /dev/null
+++ b/form/src/app/common/audio-recorder/audio-recorder.component.ts
@@ -0,0 +1,234 @@
+import {Component, EventEmitter, Input, OnDestroy, OnInit, Output, NgZone, OnChanges} from '@angular/core';
+import {NzMessageService, NzNotificationService, UploadFile} from 'ng-zorro-antd';
+import {HttpClient, HttpEvent, HttpEventType, HttpRequest} from '@angular/common/http';
+import {environment} from '../../../environments/environment';
+
+declare var Recorder;
+
+@Component({
+  selector: 'app-audio-recorder',
+  templateUrl: './audio-recorder.component.html',
+  styleUrls: ['./audio-recorder.component.scss']
+})
+export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy {
+  _audioUrl: string;
+  audio = new Audio();
+  playIcon = 'play';
+  isPlaying = false;
+  isRecording = false;
+  isUploading = false;
+  type = Type.UPLOAD; // record | upload
+  Type = Type;
+  @Input()
+  withRmBtn = false;
+
+  uploadUrl;
+  uploadData;
+
+  @Input()
+  needRemove = false;
+
+  @Input()
+  audioItem: any = null;
+
+  @Input()
+  set audioUrl(url) {
+    this._audioUrl = url;
+    if (url) {
+      this.audio.src = this._audioUrl;
+      this.audio.load();
+    }
+    this.init();
+  }
+
+  get audioUrl() {
+    return this._audioUrl;
+  }
+
+  @Output() audioUploaded = new EventEmitter();
+  @Output() audioUploadFailure = new EventEmitter();
+  @Output() audioRemoved = new EventEmitter();
+  percent = 0;
+  progress = 0;
+  recorder: any;
+  audioBlob: any;
+
+
+  constructor( private nzMessageService: NzMessageService ) {
+    this.uploadUrl = (<any> window).courseware.uploadUrl();
+    this.uploadData = (<any> window).courseware.uploadData();
+
+    window['air'].getUploadCallback = (url, data) => {
+      this.uploadUrl = url;
+      this.uploadData = data;
+    };
+  }
+
+  init() {
+    this.playIcon = 'play';
+    this.isPlaying = false;
+    this.isRecording = false;
+    this.isUploading = false;
+    this.percent = 0;
+    this.progress = 0;
+    this.audioBlob = null;
+  }
+  ngOnChanges() {
+    // if (!this.audioItem || !this.audioItem.type) {
+    //   return;
+    // }
+    // this.beforeUpload(this.audioItem);
+  }
+  ngOnInit() {
+
+    this.audio.onplay = () => {
+      this.onPlay();
+    };
+    this.audio.onpause = () => {
+      this.onPause();
+    };
+    this.audio.ontimeupdate = (event) => {
+      this.onTimeUpdate(event);
+    };
+    this.audio.onended = (event) => {
+      this.onEnded();
+    };
+  }
+
+  ngOnDestroy() {
+    this.audio.pause();
+    this.isPlaying = false;
+    this.audio.remove();
+    // if (this.recorder.worker) {
+    //   this.recorder.worker.terminate();
+    // }
+  }
+
+  progressText(percent) {
+    return ``;
+  }
+
+  onPlay() {
+    console.log('play');
+    this.playIcon = 'pause';
+    this.isPlaying = true;
+  }
+
+  onPause() {
+    console.log('pause');
+    this.playIcon = 'play';
+    this.isPlaying = false;
+  }
+
+  onEnded() {
+    console.log('on end');
+    this.playIcon = 'play';
+    this.percent = 0;
+    this.isPlaying = false;
+  }
+
+  onTimeUpdate(event) {
+    this.percent = Math.floor((this.audio.currentTime / this.audio.duration) * 100);
+  }
+
+  onBtnPlay() {
+    if (this.isRecording) {
+      this.nzMessageService.warning('In Recording');
+      return;
+    }
+    if (this.isPlaying) {
+      this.audio.pause();
+    } else {
+      this.audio.play();
+    }
+  }
+
+  // 开始录音
+  onBtnRecord = () => {
+  }
+
+  // 切换模式
+  onBtnSwitchType() {
+  }
+  onBtnClearAudio() {
+    this.audioUrl = null;
+    this.audioRemoved.emit();
+  }
+
+  onBtnDeleteAudio() {
+    this.audioUrl = null;
+    this.audioRemoved.emit();
+  }
+
+  handleChange(info: { type: string, file: UploadFile, event: any }): void {
+    switch (info.type) {
+      case 'start':
+        this.isUploading = true;
+        this.progress = 0;
+        break;
+      case 'success':
+        this.isUploading = false;
+        this.uploadSuccess(info.file.response);
+        this.audioUploaded.emit(info.file.response);
+        break;
+      case 'progress':
+        this.progress = parseInt(info.event.percent, 10);
+        break;
+    }
+  }
+  checkSelectFile(file: any) {
+    if (!file) {
+      return;
+    }
+    const isAudio = ['audio/mp3', 'audio/wav', 'audio/ogg'].includes(file.type);
+    if (!isAudio) {
+      this.nzMessageService.error('You can only upload Audio file ( mp3 | wav |ogg)');
+      return;
+    }
+    const delta =  25;
+    const isOverSize = (file.size / 1024 / 1024) < delta;
+    if (!isOverSize) {
+      this.nzMessageService.error(`audio file  must smaller than ${delta}MB!`);
+      return false;
+    }
+    return true;
+  }
+  beforeUpload = (file: File) => {
+
+    this.audioUrl = null;
+    if (!this.checkSelectFile(file)) {
+      return false;
+    }
+    this.isUploading = true;
+    this.progress = 0;
+  }
+  uploadSuccess = (url) => {
+    this.nzMessageService.info('Upload Success');
+    this.isUploading = false;
+    this.audioUrl = url;
+  }
+  uploadFailure = (err, file) => {
+    this.isUploading = false;
+    if (err.name && err.name === 'cancel') {
+      return;
+    }
+    console.log(err);
+    this.nzMessageService.error('Upload Error ' + err.message);
+    this.audioUploadFailure.emit(file);
+  }
+  doProgress = (p) => {
+    if (p > 1) {
+      p = 1;
+    }
+    if (p < 0) {
+      p = 0;
+    }
+    // console.log(Math.floor(p * 100));
+    this.progress =  Math.floor(p * 100);
+  }
+
+}
+
+enum Type {
+  RECORD = 1, UPLOAD
+}
diff --git a/form/src/app/common/custom-hot-zone/Unit.ts b/form/src/app/common/custom-hot-zone/Unit.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7b99189ae1e79f71bf621d6c98beff55cb7df827
--- /dev/null
+++ b/form/src/app/common/custom-hot-zone/Unit.ts
@@ -0,0 +1,3108 @@
+
+import TWEEN from '@tweenjs/tween.js';
+
+interface AirWindow  extends Window {
+  air: any;
+  curCtx: any;
+}
+declare let window: AirWindow;
+
+class Sprite {
+  x = 0;
+  y = 0;
+  color = '';
+  radius = 0;
+  alive = false;
+  margin = 0;
+  angle = 0;
+  ctx;
+
+  constructor(ctx = null) {
+    if (!ctx) {
+      this.ctx = window.curCtx;
+    } else {
+      this.ctx = ctx;
+    }
+  }
+  update($event) {
+    this.draw();
+  }
+  draw() {
+
+  }
+
+}
+
+
+
+
+
+export class MySprite extends Sprite {
+
+  _width = 0;
+  _height = 0;
+  _anchorX = 0;
+  _anchorY = 0;
+  _offX = 0;
+  _offY = 0;
+  scaleX = 1;
+  scaleY = 1;
+  _alpha = 1;
+  rotation = 0;
+  visible = true;
+  skewX = 0;
+  skewY = 0;
+
+  _shadowFlag = false;
+  _shadowColor;
+  _shadowOffsetX = 0;
+  _shadowOffsetY = 0;
+  _shadowBlur = 5;
+
+  _radius = 0;
+
+  children = [this];
+
+  childDepandVisible = true;
+  childDepandAlpha = false;
+
+  img;
+  _z = 0;
+
+  _showRect;
+
+
+  _bitmapFlag = false;
+  _offCanvas;
+  _offCtx;
+
+
+  init(imgObj = null, anchorX: number = 0.5, anchorY: number = 0.5) {
+
+    if (imgObj) {
+
+      this.img = imgObj;
+
+      this.width = this.img.width;
+      this.height = this.img.height;
+
+
+    }
+
+    this.anchorX = anchorX;
+    this.anchorY = anchorY;
+  }
+
+
+  setShowRect(rect) {
+    this._showRect = rect;
+  }
+
+  setShadow(offX, offY, blur, color = 'rgba(0, 0, 0, 0.3)') {
+
+
+    this._shadowFlag = true;
+    this._shadowColor = color;
+    this._shadowOffsetX = offX;
+    this._shadowOffsetY = offY;
+    this._shadowBlur = blur;
+
+  }
+
+  setRadius(r) {
+    this._radius = r;
+  }
+
+
+
+
+  update($event = null) {
+    if (!this.visible && this.childDepandVisible) {
+      return;
+    }
+
+    this.draw();
+
+  }
+  draw() {
+
+    this.ctx.save();
+
+    this.drawInit();
+
+    this.updateChildren();
+
+    this.ctx.restore();
+
+
+  }
+
+  drawInit() {
+
+    this.ctx.translate(this.x, this.y);
+
+    this.ctx.rotate(this.rotation * Math.PI / 180);
+
+    this.ctx.scale(this.scaleX, this.scaleY);
+
+    this.ctx.globalAlpha = this.alpha;
+
+    this.ctx.transform(1, this.skewX, this.skewY, 1, 0, 0);
+
+
+
+
+    //
+    // if (this._radius) {
+    //
+    //   const r = this._radius;
+    //   const w = this.width;
+    //   const h = this.height;
+    //
+    //   this.ctx.lineTo(-w / 2, h / 2);          // 创建水平线
+    //   this.ctx.arcTo(-w / 2, -h / 2, -w / 2 + r, -h / 2, r);
+    //   this.ctx.arcTo(w / 2, -h / 2, w / 2, -h / 2 + r, r);
+    //   this.ctx.arcTo(w / 2, h / 2, w / 2 - r, h / 2, r);
+    //   this.ctx.arcTo(-w / 2, h / 2, -w / 2, h / 2 - r, r);
+    //
+    //   this.ctx.clip();
+    // }
+
+  }
+
+  drawSelf() {
+
+
+
+    if (this._shadowFlag) {
+
+      this.ctx.shadowOffsetX = this._shadowOffsetX;
+      this.ctx.shadowOffsetY = this._shadowOffsetY;
+      this.ctx.shadowBlur = this._shadowBlur;
+      this.ctx.shadowColor = this._shadowColor;
+    } else {
+      this.ctx.shadowOffsetX = 0;
+      this.ctx.shadowOffsetY = 0;
+      this.ctx.shadowBlur = null;
+      this.ctx.shadowColor = null;
+    }
+
+
+    if (this.img) {
+
+      if (this._showRect) {
+        const rect = this._showRect;
+        this.ctx.drawImage(this.img, rect.x, rect.y, rect.width, rect.height, this._offX, this._offY + rect.y, this.width, rect.height);
+      } else {
+        this.ctx.drawImage(this.img, this._offX, this._offY);
+      }
+    }
+
+
+
+
+  }
+
+  updateChildren() {
+
+    if (this.children.length <= 0) { return; }
+
+    for (const child of  this.children) {
+      if (child === this) {
+        if (this.visible) {
+          this.drawSelf();
+        }
+      } else {
+        child.update();
+      }
+    }
+  }
+
+
+  load(url, anchorX = 0.5, anchorY = 0.5) {
+
+    return new Promise((resolve, reject) => {
+      const img = new Image();
+      img.onload = () => resolve(img);
+      img.onerror = reject;
+      img.src = url;
+    }).then(img => {
+
+      this.init(img, anchorX, anchorY);
+      return img;
+    });
+  }
+
+  addChild(child, z = 1) {
+    if (this.children.indexOf(child) === -1) {
+      this.children.push(child);
+      child._z = z;
+      child.parent = this;
+    }
+
+    this.children.sort((a, b) => {
+      return a._z - b._z;
+    });
+
+
+    if (this.childDepandAlpha) {
+      child.alpha = this.alpha;
+    }
+
+  }
+  removeChild(child) {
+    const index = this.children.indexOf(child);
+    if (index !== -1) {
+      this.children.splice(index, 1);
+    }
+  }
+
+  removeChildren() {
+    for (let i = 0; i < this.children.length; i++) {
+      if (this.children[i]) {
+        if (this.children[i] !== this) {
+          this.children.splice(i, 1);
+          i --;
+        }
+      }
+    }
+  }
+
+  _changeChildAlpha(alpha) {
+    for (const child of  this.children) {
+      if (child !== this) {
+        child.alpha = alpha;
+      }
+    }
+  }
+
+
+  set btimapFlag(v) {
+    this._bitmapFlag = v;
+  }
+  get btimapFlag() {
+    return this._bitmapFlag;
+  }
+  set alpha(v) {
+    this._alpha = v;
+    if (this.childDepandAlpha) {
+      this._changeChildAlpha(v);
+    }
+  }
+  get alpha() {
+    return this._alpha;
+  }
+  set width(v) {
+    this._width = v;
+    this.refreshAnchorOff();
+  }
+  get width() {
+    return this._width;
+  }
+  set height(v) {
+    this._height = v;
+    this.refreshAnchorOff();
+  }
+  get height() {
+    return this._height;
+  }
+  set anchorX(value) {
+    this._anchorX = value;
+    this.refreshAnchorOff();
+  }
+  get anchorX() {
+    return this._anchorX;
+  }
+  set anchorY(value) {
+    this._anchorY = value;
+    this.refreshAnchorOff();
+  }
+  get anchorY() {
+    return this._anchorY;
+  }
+  refreshAnchorOff() {
+    this._offX = -this._width * this.anchorX;
+    this._offY = -this._height * this.anchorY;
+  }
+
+  setScaleXY(value) {
+    this.scaleX = this.scaleY = value;
+  }
+
+  getBoundingBox() {
+
+    const getParentData = (item) => {
+
+      let px = item.x;
+      let py = item.y;
+
+      let sx = item.scaleX;
+      let sy = item.scaleY;
+      const parent = item.parent;
+      if (parent) {
+
+        const obj = getParentData(parent);
+
+        const _x = obj.px;
+        const _y = obj.py;
+        const _sx = obj.sx;
+        const _sy = obj.sy;
+
+        px = _x + item.x * _sx;
+        py = _y + item.y * _sy;
+
+        sx *= _sx;
+        sy *= _sy;
+
+      }
+
+      return {px, py, sx, sy};
+    };
+
+
+    const data = getParentData(this);
+
+
+    const x = data.px + this._offX * Math.abs(data.sx);
+    const y = data.py + this._offY * Math.abs(data.sy);
+    const width = this.width * Math.abs(data.sx);
+    const height = this.height * Math.abs(data.sy);
+
+    return {x, y, width, height};
+  }
+
+}
+
+
+export class RoundSprite extends MySprite {
+
+  _newCtx;
+
+  init(imgObj = null, anchorX: number = 0.5, anchorY: number = 0.5) {
+
+    if (imgObj) {
+
+      this.img = imgObj;
+
+      this.width = this.img.width;
+      this.height = this.img.height;
+
+
+    }
+
+    this.anchorX = anchorX;
+    this.anchorY = anchorY;
+
+    const canvas = window['curCanvas'];
+    const w = canvas.nativeElement.width;
+    const h = canvas.nativeElement.height;
+
+
+    this._offCanvas = document.createElement('canvas');
+    this._offCanvas.width = w;
+    this._offCanvas.height = h;
+
+    this._offCtx = this._offCanvas.getContext('2d');
+
+    // this._newCtx = this.ctx;
+    // this.ctx = this._offCtx;
+
+  }
+
+
+
+  drawSelf() {
+    //
+    // if (this._shadowFlag) {
+    //
+    //   this.ctx.shadowOffsetX = this._shadowOffsetX;
+    //   this.ctx.shadowOffsetY = this._shadowOffsetY;
+    //   this.ctx.shadowBlur = this._shadowBlur;
+    //   this.ctx.shadowColor = this._shadowColor;
+    // } else {
+    //   this.ctx.shadowOffsetX = 0;
+    //   this.ctx.shadowOffsetY = 0;
+    //   this.ctx.shadowBlur = null;
+    //   this.ctx.shadowColor = null;
+    // }
+
+
+    if (this._radius) {
+
+      const r = this._radius;
+      const w = this.width;
+      const h = this.height;
+      const x = -this._offX;
+      const y = -this._offY;
+
+      this._offCtx.lineTo(x - w / 2, y + h / 2);          // 创建水平线
+      this._offCtx.arcTo(x - w / 2, y - h / 2, x - w / 2 + r, y - h / 2, r);
+      this._offCtx.arcTo(x + w / 2, y - h / 2, x + w / 2, y - h / 2 + r, r);
+      this._offCtx.arcTo(x + w / 2, y + h / 2, x + w / 2 - r, y + h / 2, r);
+      this._offCtx.arcTo(x - w / 2, y + h / 2, x - w / 2, y + h / 2 - r, r);
+
+      this._offCtx.clip();
+    }
+
+
+    if (this.img) {
+
+      this._offCtx.drawImage(this.img, 0, 0);
+      this.ctx.drawImage(this._offCanvas,this._offX, this._offX);
+    }
+
+  }
+}
+
+
+
+
+
+export class ColorSpr extends MySprite {
+
+  r = 0;
+  g = 0;
+  b = 0;
+
+  createGSCanvas() {
+
+    if (!this.img) {
+      return;
+    }
+
+
+
+    const rect = this.getBoundingBox();
+    if (rect.width <= 1 || rect.height <= 1) {
+      return;
+    }
+
+
+    const c = this.ctx.getImageData(rect.x, rect.y, rect.width, rect.height);
+    for ( let i = 0; i < c.height; i++) {
+      for ( let j = 0; j < c.width; j++) {
+
+        const x = (i * 4) * c.width + ( j * 4 );
+        const r = c.data[x];
+        const g = c.data[x + 1];
+        const b = c.data[x + 2];
+
+        c.data[x] = this.r;
+        c.data[x + 1] = this.g;
+        c.data[x + 2] = this.b;
+
+
+
+        // c.data[x] = c.data[x + 1] = c.data[x + 2] = (r + g + b) / 3 ;
+        // // c.data[x + 3] = 255;
+      }
+    }
+
+    this.ctx.putImageData(c, rect.x, rect.y, 0, 0, rect.width, rect.height);
+  }
+
+
+  drawSelf() {
+    super.drawSelf();
+    this.createGSCanvas();
+  }
+
+}
+
+
+export class GrayscaleSpr extends MySprite {
+
+  grayScale = 120;
+
+  createGSCanvas() {
+
+    if (!this.img) {
+      return;
+    }
+
+
+
+    const rect = this.getBoundingBox();
+
+    const c = this.ctx.getImageData(rect.x, rect.y, rect.width, rect.height);
+    for ( let i = 0; i < c.height; i++) {
+      for ( let j = 0; j < c.width; j++) {
+
+        const x = (i * 4) * c.width + ( j * 4 );
+        const r = c.data[x];
+        const g = c.data[x + 1];
+        const b = c.data[x + 2];
+        // const a = c.data[x + 3];
+
+        c.data[x] = c.data[x + 1] = c.data[x + 2] = this.grayScale; // (r + g + b) / 3;
+        // c.data[x + 3] = 255;
+      }
+    }
+
+    this.ctx.putImageData(c, rect.x, rect.y, 0, 0, rect.width, rect.height);
+  }
+
+
+  drawSelf() {
+    super.drawSelf();
+    this.createGSCanvas();
+  }
+
+}
+
+
+
+
+
+
+export class BitMapLabel extends MySprite {
+
+
+  labelArr;
+  baseUrl;
+
+  setText(data, text) {
+
+    this.labelArr = [];
+
+    const labelArr = [];
+    const tmpArr = text.split('');
+    let totalW = 0;
+    let h = 0;
+    for (const tmp of tmpArr) {
+
+      const label = new MySprite(this.ctx);
+      label.init(data[tmp], 0);
+      this.addChild(label);
+      labelArr.push(label);
+
+      totalW += label.width;
+      h = label.height;
+    }
+
+
+    this.width = totalW;
+    this.height = h;
+
+    let offX = -totalW / 2;
+    for (const label of  labelArr) {
+      label.x = offX;
+      offX += label.width;
+    }
+
+    this.labelArr = labelArr;
+  }
+
+}
+
+
+
+
+export class Label extends MySprite {
+  private _text: string;
+
+  // fontSize:String = '40px';
+  fontName = 'Verdana';
+  textAlign = 'left';
+  fontSize = 40;
+  fontColor = '#000000';
+  fontWeight = 900;
+  _maxWidth;
+  outline = 0;
+  outlineColor = '#ffffff';
+  // _shadowFlag = false;
+  // _shadowColor;
+  // _shadowOffsetX;
+  // _shadowOffsetY;
+  // _shadowBlur;
+
+  _outlineFlag = false;
+
+  _outLineWidth;
+  _outLineColor;
+  constructor(ctx = null) {
+    super(ctx);
+    this.init();
+  }
+
+
+  get text(): string {
+    return this._text;
+  }
+
+  set text(value: string) {
+    this._text = value;
+    this.refreshSize();
+  }
+
+  refreshSize() {
+
+    this.ctx.save();
+
+    this.ctx.font = `${this.fontSize * this.scaleX}px ${this.fontName}`;
+    this.ctx.textAlign = this.textAlign;
+    this.ctx.textBaseline = 'middle';
+    this.ctx.fontWeight = this.fontWeight;
+
+    this._width = this.ctx.measureText(this.text).width;
+    this._height = this.fontSize;
+    this.refreshAnchorOff();
+
+    this.ctx.restore();
+
+  }
+
+  setMaxSize(w) {
+
+    this._maxWidth = w;
+    this.refreshSize();
+    if (this.width >= w) {
+      this.scaleX *= w / this.width;
+      this.scaleY *= w / this.width;
+    }
+  }
+
+  show(callBack = null) {
+
+    this.visible = true;
+
+    if (this.alpha >= 1) {
+      return;
+    }
+
+    const tween = new TWEEN.Tween(this)
+      .to({ alpha: 1 }, 800)
+      // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
+      .onComplete(() => {
+        if (callBack) {
+          callBack();
+        }
+      })
+      .start(); // Start the tween immediately.
+  }
+
+  setOutline(width = 5, color = '#ffffff') {
+
+    this._outlineFlag = true;
+    this._outLineWidth = width;
+    this._outLineColor = color;
+
+  }
+
+  drawText() {
+
+
+    if (!this.text) { return; }
+
+
+    this.ctx.font = `${this.fontSize}px ${this.fontName}`;
+    this.ctx.textAlign = this.textAlign;
+    this.ctx.textBaseline = 'middle';
+    this.ctx.fontWeight = this.fontWeight;
+
+    if (this._outlineFlag) {
+      this.ctx.lineWidth = this._outLineWidth;
+      this.ctx.strokeStyle = this._outLineColor;
+      this.ctx.strokeText(this.text, 0, 0);
+    }
+
+    this.ctx.fillStyle = this.fontColor;
+
+
+
+    if (this.outline > 0) {
+      this.ctx.lineWidth = this.outline;
+      this.ctx.strokeStyle = this.outlineColor;
+      this.ctx.strokeText(this.text, 0, 0);
+
+    }
+
+    this.ctx.fillText(this.text, 0, 0);
+
+
+  }
+
+
+  drawSelf() {
+    super.drawSelf();
+    this.drawText();
+  }
+
+}
+
+export class RichTextOld extends Label {
+
+  textArr = [];
+  fontSize = 40;
+
+  setText(text: string, words) {
+
+    let newText = text;
+    for (const word of words) {
+
+      const re = new RegExp(word, 'g');
+      newText = newText.replace( re, `#${word}#`);
+      // newText = newText.replace(word, `#${word}#`);
+    }
+
+    this.textArr = newText.split('#');
+    this.text = newText;
+
+    // this.setSize();
+  }
+
+  refreshSize() {
+
+    this.ctx.save();
+
+    this.ctx.font = `${this.fontSize}px ${this.fontName}`;
+    this.ctx.textAlign = this.textAlign;
+    this.ctx.textBaseline = 'middle';
+    this.ctx.fontWeight = this.fontWeight;
+
+    let curX = 0;
+    for (const text of this.textArr) {
+      const w = this.ctx.measureText(text).width;
+      curX += w;
+    }
+
+    this.width = curX;
+    this.height = this.fontSize;
+    this.refreshAnchorOff();
+
+    this.ctx.restore();
+
+  }
+
+
+  show(callBack = null) {
+
+    // console.log(' in show ');
+    this.visible = true;
+    // this.alpha = 0;
+
+    const tween = new TWEEN.Tween(this)
+      .to({ alpha: 1 }, 800)
+      // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
+      .onComplete(() => {
+        if (callBack) {
+          callBack();
+        }
+      })
+      .start(); // Start the tween immediately.
+
+  }
+
+
+
+  drawText() {
+
+    // console.log('in drawText', this.text);
+
+    if (!this.text) { return; }
+
+
+    this.ctx.font = `${this.fontSize}px ${this.fontName}`;
+    this.ctx.textAlign = this.textAlign;
+    this.ctx.textBaseline = 'middle';
+    this.ctx.fontWeight = 900;
+
+    this.ctx.lineWidth = 5;
+    this.ctx.strokeStyle = '#ffffff';
+    // this.ctx.strokeText(this.text, 0, 0);
+
+    this.ctx.fillStyle = '#000000';
+    // this.ctx.fillText(this.text, 0, 0);
+
+
+    let curX = 0;
+    for (let i = 0; i < this.textArr.length; i++) {
+
+      const w = this.ctx.measureText(this.textArr[i]).width;
+
+      if ((i + 1) % 2 === 0) {
+        this.ctx.fillStyle = '#c8171e';
+      } else {
+        this.ctx.fillStyle = '#000000';
+      }
+
+      this.ctx.fillText(this.textArr[i], curX, 0);
+      curX += w;
+
+    }
+
+
+  }
+
+}
+
+
+
+
+
+export class RichText extends Label {
+
+
+  disH =  30;
+
+  constructor(ctx?: any) {
+    super(ctx);
+
+    // this.dataArr = dataArr;
+  }
+
+
+
+  drawText() {
+
+    if (!this.text) {
+      return;
+    }
+
+
+    this.ctx.font = `${this.fontSize}px ${this.fontName}`;
+    this.ctx.textAlign = this.textAlign;
+    this.ctx.textBaseline = 'middle';
+    this.ctx.fontWeight = this.fontWeight;
+    this.ctx.fillStyle = this.fontColor;
+
+
+    const selfW = this.width * this.scaleX;
+
+
+    const chr = this.text.split(' ');
+    let temp = '';
+    const row = [];
+    const w = selfW - 80;
+    const disH = (this.fontSize + this.disH) * this.scaleY;
+
+
+
+
+    for (const c of  chr) {
+      if (this.ctx.measureText(temp).width < w && this.ctx.measureText(temp + (c)).width <= w) {
+        temp += ' ' + c;
+      } else {
+        row.push(temp);
+        temp = ' ' + c;
+      }
+    }
+    row.push(temp);
+
+    const x = 0;
+    const y = -row.length * disH / 2;
+    // for (let b = 0 ; b < row.length; b++) {
+    //   this.ctx.strokeText(row[b], x, y + ( b + 1 ) * disH ); // 每行字体y坐标间隔20
+    // }
+
+
+    if (this._outlineFlag) {
+      this.ctx.lineWidth = this._outLineWidth;
+      this.ctx.strokeStyle = this._outLineColor;
+      for (let b = 0 ; b < row.length; b++) {
+        this.ctx.strokeText(row[b], x, y + ( b + 1 ) * disH ); // 每行字体y坐标间隔20
+      }
+      // this.ctx.strokeText(this.text, 0, 0);
+    }
+
+
+    // this.ctx.fillStyle = '#ff7600';
+    for (let b = 0 ; b < row.length; b++) {
+      this.ctx.fillText(row[b], x, y + ( b + 1 ) * disH ); // 每行字体y坐标间隔20
+    }
+
+  }
+
+
+  drawSelf() {
+    super.drawSelf();
+    this.drawText();
+  }
+
+}
+
+
+
+
+export class LineRect extends MySprite {
+
+  lineColor = '#ffffff';
+  lineWidth = 10;
+
+  setSize(w, h) {
+    this.width = w;
+    this.height = h;
+  }
+
+  drawLine() {
+
+    this.ctx.beginPath();
+    this.ctx.moveTo(this._offX, this._offY);
+    this.ctx.lineTo(this._offX + this.width, this._offY);
+    this.ctx.lineTo(this._offX + this.width, this._offY + this.height);
+    this.ctx.lineTo(this._offX, this._offY + this.height);
+    this.ctx.closePath();
+
+    this.ctx.lineWidth = this.lineWidth;
+    // this.ctx.fillStyle = "rgb(2,33,42)";  //指定填充颜色
+    // this.ctx.fill();   //对多边形进行填充
+
+    this.ctx.strokeStyle = this.lineColor; // "#ffffff";
+    this.ctx.stroke();
+
+  }
+
+
+  drawSelf() {
+    super.drawSelf();
+    this.drawLine();
+  }
+}
+
+
+export class ShapeRect extends MySprite {
+
+  fillColor = '#FF0000';
+
+  setSize(w, h) {
+    this.width = w;
+    this.height = h;
+
+    // console.log('w:', w);
+    // console.log('h:', h);
+  }
+
+  drawShape() {
+
+    this.ctx.fillStyle = this.fillColor;
+    this.ctx.fillRect(this._offX, this._offY, this.width, this.height);
+
+  }
+
+
+  drawSelf() {
+    super.drawSelf();
+    this.drawShape();
+  }
+}
+
+
+
+export class Line extends MySprite {
+
+
+  lineWidth = 5;
+  lineColor = '#000000';
+  _pointArr = [];
+
+  roundFlag = true;
+
+  _pointS = 1;
+
+
+  imgObj;
+  bitMap;
+  _offCtx;
+  _offCanvas;
+
+  lastPointIndex = 0;
+
+  init() {
+
+    const canvas = window['curCanvas'];
+    const w = canvas.nativeElement.width;
+    const h = canvas.nativeElement.height;
+
+    console.log('w: ', w);
+    console.log('h: ', h);
+
+
+    this._offCanvas = document.createElement('canvas');
+    this._offCanvas.width = w;
+    this._offCanvas.height = h;
+    // this._offCanvas = _offCanvas;
+    // this._offCtx = this._offCanvas.getContext('2d');
+
+
+    // this._offCanvas = new OffscreenCanvas(w, h);
+    this._offCtx = this._offCanvas.getContext('2d');
+  }
+
+  addPoint(x, y) {
+
+    this._pointArr.push([x, y]);
+
+    if (this._pointArr.length < 2) {
+      return;
+    }
+    //
+    // const lastP = this._pointArr[this._pointArr.length - 1];
+    //
+    //
+    // const context = this._offCtx;
+    // context.moveTo (lastP[0], lastP[1]);       // 设置起点状态
+    // context.lineTo (x, y);       // 设置末端状态
+    //
+    // context.lineWidth = this.lineWidth;          //设置线宽状态
+    // context.strokeStyle = this.lineColor;
+    // context.stroke();
+    //
+    //
+    // this.bitMap = this._offCanvas.transferToImageBitmap();
+
+
+
+    // const tmpLine = new MySprite(this._offCtx);
+    // tmpLine.init(this.imgObj);
+    // tmpLine.anchorY = 1;
+    // tmpLine.anchorX = 0.5;
+    // tmpLine.x = lastP[0];
+    // tmpLine.y = lastP[1];
+    //
+    // const disH = getPosDistance(lastP[0], lastP[1], x, y);
+    // tmpLine.scaleX = this.lineWidth / tmpLine.width;
+    // tmpLine.scaleY = disH / tmpLine.height * 1.1;
+    //
+    // const angle = getAngleByPos(lastP[0], lastP[1], x, y);
+    // tmpLine.rotation = angle;
+    //
+    // this.addChild(tmpLine);
+
+  }
+
+  setPointArr(arr, imgObj) {
+
+    this.removeChildren();
+
+    if (arr.length < 2) {
+      return;
+    }
+
+    let p1 = arr[0];
+    let p2;
+    for (let i = 1; i < arr.length; i++) {
+
+      p2 = arr[i];
+
+      const tmpLine = new MySprite();
+      tmpLine.init(imgObj);
+      tmpLine.anchorY = 1;
+      tmpLine.anchorX = 0.5;
+      tmpLine.x = p1[0];
+      tmpLine.y = p1[1];
+
+      const disH = getPosDistance(p1[0], p1[1], p2[0], p2[1]);
+      tmpLine.scaleX = this.lineWidth / tmpLine.width;
+      tmpLine.scaleY = disH / tmpLine.height * 1.1;
+
+      const angle = getAngleByPos(p1[0], p1[1], p2[0], p2[1]);
+      tmpLine.rotation = angle;
+
+      this.addChild(tmpLine);
+
+      p1 = p2;
+    }
+  }
+
+
+
+
+  drawLine() {
+
+    if (this._pointArr.length < 2) {
+      return;
+    }
+
+
+    const curMaxPointIndex = this._pointArr.length - 1;
+    if (curMaxPointIndex > this.lastPointIndex) {
+
+      const arr = this._pointArr;
+
+      const context = this._offCtx;
+      context.moveTo (arr[this.lastPointIndex][0] * this._pointS, arr[this.lastPointIndex][1] * this._pointS);       // 设置起点状态
+      for (let i = this.lastPointIndex + 1; i < arr.length; i++) {
+        context.lineTo (arr[i][0] * this._pointS, arr[i][1] * this._pointS);       // 设置末端状态
+      }
+
+      if (this.roundFlag) {
+        context.lineCap = "round";
+      }
+      context.lineWidth = this.lineWidth;          //设置线宽状态
+      context.strokeStyle = this.lineColor;
+      context.stroke();
+
+
+      this.lastPointIndex = curMaxPointIndex;
+
+      // this.bitMap = this._offCanvas.transferToImageBitmap();
+
+    }
+
+
+    // this.ctx.drawImage(this.bitMap, this._offX, this._offY);
+    this.ctx.drawImage(this._offCanvas, this._offX, this._offY);
+
+  }
+
+  drawSelf() {
+    super.drawSelf();
+    this.drawLine();
+
+
+    // if (this.img) {
+    //   this.ctx.drawImage(this._offCanvas, 0, 0, this.width, this.height);
+    // }
+
+    // if (this.bitMap) {
+    //   this.bitMap = this._offCanvas.transferToImageBitmap();
+    //   this.ctx.drawImage(this.bitMap, 0, 0, this.width, this.height);
+    // }
+
+  }
+}
+
+
+export class ShapeCircle extends MySprite {
+
+  fillColor = '#FF0000';
+  radius = 0;
+
+
+  setRadius(r) {
+    this.anchorX = this.anchorY = 0.5;
+
+    this.radius = r;
+    this.width = r * 2;
+    this.height = r * 2;
+  }
+
+  drawShape() {
+
+    this.ctx.beginPath();
+    this.ctx.fillStyle = this.fillColor;
+    this.ctx.arc(0, 0, this.radius, 0, angleToRadian(360));
+    this.ctx.fill();
+  }
+
+  drawSelf() {
+    super.drawSelf();
+    this.drawShape();
+  }
+}
+
+
+
+
+export class ShapeRectNew extends MySprite {
+
+
+  radius = 0;
+  fillColor = '#ffffff';
+  strokeColor = '#000000';
+  fill = true;
+  stroke = false;
+  lineWidth = 1;
+
+  setSize(w, h, r) {
+    this.width = w;
+    this.height = h;
+    this.radius = r;
+  }
+
+  setOutLine(color, lineWidth) {
+    this.stroke = true;
+    this.strokeColor = color;
+    this.lineWidth = lineWidth;
+  }
+
+
+  drawShape() {
+
+    const ctx = this.ctx;
+    const width = this.width;
+    const height = this.height;
+    const radius = this.radius;
+
+    ctx.save();
+    ctx.beginPath(0);
+    // 从右下角顺时针绘制,弧度从0到1/2PI
+    ctx.arc(width - radius, height - radius, radius, 0, Math.PI / 2);
+
+    // 矩形下边线
+    ctx.lineTo(radius, height);
+
+    // 左下角圆弧,弧度从1/2PI到PI
+    ctx.arc(radius, height - radius, radius, Math.PI / 2, Math.PI);
+
+    // 矩形左边线
+    ctx.lineTo(0, radius);
+
+    // 左上角圆弧,弧度从PI到3/2PI
+    ctx.arc(radius, radius, radius, Math.PI, Math.PI * 3 / 2);
+
+    // 上边线
+    ctx.lineTo(width - radius, 0);
+
+    // 右上角圆弧
+    ctx.arc(width - radius, radius, radius, Math.PI * 3 / 2, Math.PI * 2);
+
+    // 右边线
+    ctx.lineTo(width, height - radius);
+    ctx.closePath();
+
+    if (this.fill) {
+      ctx.fillStyle = this.fillColor;
+      ctx.fill();
+    }
+    if (this.stroke) {
+      ctx.lineWidth = this.lineWidth;
+      ctx.strokeStyle = this.strokeColor;
+      ctx.stroke();
+    }
+    ctx.restore();
+
+  }
+
+
+  drawSelf() {
+    super.drawSelf();
+    this.drawShape();
+  }
+
+}
+export class MyAnimation extends MySprite {
+
+
+  frameArr = [];
+  frameIndex = 0;
+  playFlag = false;
+  lastDateTime;
+  curDelay = 0;
+
+
+  loop = false;
+  playEndFunc;
+  delayPerUnit = 1;
+
+  restartFlag = false;
+  reverseFlag = false;
+
+  addFrameByImg(img) {
+
+    const spr = new MySprite(this.ctx);
+    spr.init(img);
+    this._refreshSize(img);
+
+    spr.visible = false;
+
+    this.addChild(spr);
+    this.frameArr.push(spr);
+
+    this.frameArr[this.frameIndex].visible = true;
+  }
+
+  addFrameByUrl(url) {
+
+    const spr = new MySprite(this.ctx);
+    spr.load(url).then(img => {
+
+      this._refreshSize(img);
+    });
+    spr.visible = false;
+
+    this.addChild(spr);
+    this.frameArr.push(spr);
+
+    this.frameArr[this.frameIndex].visible = true;
+  }
+
+  _refreshSize(img: any) {
+
+    if (this.width < img.width) {
+      this.width = img.width;
+    }
+    if (this.height < img.height) {
+      this.height = img.height;
+    }
+  }
+
+
+
+  play() {
+    this.playFlag = true;
+    this.lastDateTime = new Date().getTime();
+  }
+
+  stop() {
+    this.playFlag = false;
+  }
+
+
+  replay() {
+    this.restartFlag = true;
+    this.play();
+  }
+
+
+  reverse() {
+    this.reverseFlag = !this.reverseFlag;
+    this.frameArr.reverse();
+    this.frameIndex = 0;
+  }
+
+  showAllFrame() {
+    for (const frame of this.frameArr ) {
+      frame.alpha = 1;
+    }
+  }
+
+  hideAllFrame() {
+    for (const frame of this.frameArr) {
+      frame.alpha = 0;
+    }
+  }
+
+
+  playEnd() {
+
+    this.playFlag = false;
+    this.curDelay = 0;
+
+    this.frameArr[this.frameIndex].visible = true;
+
+    if (this.playEndFunc) {
+      this.playEndFunc();
+      this.playEndFunc = null;
+    }
+  }
+
+  updateFrame() {
+    if (this.frameArr[this.frameIndex]) {
+      this.frameArr[this.frameIndex].visible = false;
+    }
+
+    this.frameIndex ++;
+    if (this.frameIndex >= this.frameArr.length) {
+      if (this.loop) {
+        this.frameIndex = 0;
+      } else if (this.restartFlag) {
+        this.restartFlag = false;
+        this.frameIndex = 0;
+      } else {
+        this.frameIndex -- ;
+        this.playEnd();
+        return;
+      }
+    }
+
+    this.frameArr[this.frameIndex].visible = true;
+
+  }
+
+
+
+
+  _updateDelay(delay) {
+
+    this.curDelay += delay;
+    if (this.curDelay < this.delayPerUnit) {
+      return;
+    }
+    this.curDelay -= this.delayPerUnit;
+    this.updateFrame();
+  }
+
+  _updateLastDate() {
+    if (!this.playFlag) { return; }
+
+    let delay = 0;
+    if (this.lastDateTime) {
+      delay = (new Date().getTime() - this.lastDateTime) / 1000;
+    }
+    this.lastDateTime = new Date().getTime();
+    this._updateDelay(delay);
+  }
+
+  update($event: any = null) {
+    super.update($event);
+    this._updateLastDate();
+  }
+
+
+}
+
+
+
+
+// --------===========  util func  =============-------------
+
+
+export function tweenChange(item, obj, time = 0.8, callBack = null, easing = null, update = null) {
+
+  const tween = new TWEEN.Tween(item).to(obj, time * 1000);
+
+  if (callBack) {
+    tween.onComplete(() => {
+      callBack();
+    });
+  }
+  if (easing) {
+    tween.easing(easing);
+  }
+  if (update) {
+    tween.onUpdate( (a, b) => {
+      update(a, b);
+    });
+  }
+
+  tween.start();
+  return tween;
+}
+
+
+
+
+export function rotateItem(item, rotation, time = 0.8, callBack = null, easing = null) {
+
+  const tween = new TWEEN.Tween(item).to({ rotation }, time * 1000);
+
+  if (callBack) {
+    tween.onComplete(() => {
+      callBack();
+    });
+  }
+  if (easing) {
+    tween.easing(easing);
+  }
+
+  tween.start();
+}
+
+
+
+export function scaleItem(item, scale, time = 0.8, callBack = null, easing = null) {
+
+  const tween = new TWEEN.Tween(item).to({ scaleX: scale, scaleY: scale}, time * 1000);
+
+  if (callBack) {
+    tween.onComplete(() => {
+      callBack();
+    });
+  }
+  if (easing) {
+    tween.easing(easing);
+  }
+
+  tween.start();
+  return tween;
+}
+
+
+export function moveItem(item, x, y, time = 0.8, callBack = null, easing = null) {
+
+  const tween = new TWEEN.Tween(item).to({ x, y}, time * 1000);
+
+  if (callBack) {
+    tween.onComplete(() => {
+      callBack();
+    });
+  }
+  if (easing) {
+    tween.easing(easing);
+  }
+
+  tween.start();
+
+  return tween;
+}
+
+
+
+
+
+
+
+export function endShow(item, s = 1) {
+
+  item.scaleX = item.scaleY = 0;
+  item.alpha = 0;
+
+  const tween = new TWEEN.Tween(item)
+    .to({ alpha: 1, scaleX: s, scaleY: s }, 800)
+    .easing(TWEEN.Easing.Elastic.Out) // Use an easing function to make the animation smooth.
+    .onComplete(() => {
+
+    })
+    .start();
+}
+
+export function hideItem(item, time = 0.8, callBack = null, easing = null) {
+
+  if (item.alpha === 0) {
+    return;
+  }
+
+  const tween = new TWEEN.Tween(item)
+    .to({alpha: 0}, time * 1000)
+    // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
+    .onComplete(() => {
+      if (callBack) {
+        callBack();
+      }
+    });
+
+  if (easing) {
+    tween.easing(easing);
+  }
+
+  tween.start();
+}
+
+
+export function showItem(item, time = 0.8, callBack = null, easing = null) {
+
+  if (item.alpha === 1) {
+    if (callBack) {
+      callBack();
+    }
+    return;
+  }
+
+  item.visible = true;
+  const tween = new TWEEN.Tween(item)
+    .to({alpha: 1}, time * 1000)
+    // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
+    .onComplete(() => {
+      if (callBack) {
+        callBack();
+      }
+    });
+
+  if (easing) {
+    tween.easing(easing);
+  }
+
+  tween.start();
+}
+
+
+export function alphaItem(item, alpha, time = 0.8, callBack = null, easing = null) {
+
+
+  const tween = new TWEEN.Tween(item)
+    .to({alpha}, time * 1000)
+    // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
+    .onComplete(() => {
+      if (callBack) {
+        callBack();
+      }
+    });
+
+  if (easing) {
+    tween.easing(easing);
+  }
+
+  tween.start();
+}
+
+
+export function showStar(item, time = 0.8, callBack = null, easing = null) {
+
+
+  const tween = new TWEEN.Tween(item)
+    .to({alpha: 1, scale: 1}, time * 1000)
+    // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
+    .onComplete(() => {
+      if (callBack) {
+        callBack();
+      }
+    });
+
+  if (easing) {
+    tween.easing(easing);
+  }
+
+  tween.start();
+}
+
+
+export function randomSortByArr(arr) {
+  if (!arr) {
+    return;
+  }
+  const newArr = [];
+  const tmpArr = arr.concat();
+  while (tmpArr.length > 0) {
+    const randomIndex = Math.floor( tmpArr.length * Math.random() );
+    newArr.push(tmpArr[randomIndex]);
+    tmpArr.splice(randomIndex, 1);
+  }
+  return newArr;
+}
+
+
+export function radianToAngle(radian) {
+  return radian * 180 / Math.PI;
+  // 角度 = 弧度 * 180 / Math.PI;
+}
+
+export function angleToRadian(angle) {
+  return angle * Math.PI / 180;
+  // 弧度= 角度 * Math.PI / 180;
+}
+
+export function getPosByAngle(angle, len) {
+
+  const radian = angle * Math.PI / 180;
+  const x = Math.sin(radian) * len;
+  const y = Math.cos(radian) * len;
+
+  return {x, y};
+
+}
+
+export function getAngleByPos(px, py, mx, my) {
+
+  const x = Math.abs(px - mx);
+  const y = Math.abs(py - my);
+
+  const z = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
+  const cos = y / z;
+  const radina = Math.acos(cos); // 用反三角函数求弧度
+  let angle = Math.floor(180 / (Math.PI / radina) * 100) / 100; // 将弧度转换成角度
+
+  if (mx > px && my > py) {// 鼠标在第四象限
+    angle = 180 - angle;
+  }
+  if (mx === px && my > py) {// 鼠标在y轴负方向上
+    angle = 180;
+  }
+  if (mx > px && my === py) {// 鼠标在x轴正方向上
+    angle = 90;
+  }
+  if (mx < px && my > py) {// 鼠标在第三象限
+    angle = 180 + angle;
+  }
+  if (mx < px && my === py) {// 鼠标在x轴负方向
+    angle = 270;
+  }
+  if (mx < px && my < py) {// 鼠标在第二象限
+    angle = 360 - angle;
+  }
+
+  // console.log('angle: ', angle);
+  return angle;
+
+}
+
+
+export function removeItemFromArr(arr, item) {
+  const index = arr.indexOf(item);
+  if (index !== -1) {
+    arr.splice(index, 1);
+  }
+}
+
+
+
+
+
+
+export function circleMove(item, x0, y0, time = 2, addR = 360, xPer = 1, yPer = 1, callBack = null, easing = null) {
+
+  const r = getPosDistance(item.x, item.y, x0, y0);
+  let a = getAngleByPos(item.x, item.y, x0, y0);
+  a += 90;
+  const obj = {r, a};
+
+  item._circleAngle = a;
+  const targetA = a + addR;
+
+  const tween = new TWEEN.Tween(item).to({_circleAngle: targetA}, time * 1000);
+
+  if (callBack) {
+    tween.onComplete(() => {
+      callBack();
+    });
+  }
+  if (easing) {
+    tween.easing(easing);
+  }
+
+  tween.onUpdate( (item, progress) => {
+
+    // console.log(item._circleAngle);
+    const r = obj.r;
+    const a = item._circleAngle;
+
+    const x = x0 + r * xPer * Math.cos(a * Math.PI / 180);
+    const y = y0 + r * yPer * Math.sin(a * Math.PI / 180);
+
+    item.x = x;
+    item.y = y;
+
+    // obj.a ++;
+  });
+
+  tween.start();
+}
+
+
+export function getPosDistance(sx, sy, ex, ey) {
+
+  const _x = ex - sx;
+  const _y = ey - sy;
+  const len = Math.sqrt( Math.pow(_x, 2) + Math.pow(_y, 2) );
+  return len;
+}
+
+export function delayCall(callback, second) {
+  const tween = new TWEEN.Tween(this)
+    .delay(second * 1000)
+    .onComplete(() => {
+      if (callback) {
+        callback();
+      }
+    })
+    .start();
+}
+
+
+export function formatTime(fmt, date) {
+
+  // "yyyy-MM-dd HH:mm:ss";
+
+  const o = {
+    'M+': date.getMonth() + 1, // 月份
+    'd+': date.getDate(), // æ—¥
+    'h+': date.getHours(), // 小时
+    'm+': date.getMinutes(), // 分
+    's+': date.getSeconds(), // ç§’
+    'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
+    S: date.getMilliseconds() // 毫秒
+  };
+  if (/(y+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)); }
+  for (const k in o) {
+    if (new RegExp('(' + k + ')').test(fmt)) { fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1)
+      ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)));
+    }
+  }
+  return fmt;
+}
+
+export function getMinScale(item, maxLen) {
+  const sx = maxLen / item.width;
+  const sy = maxLen / item.height;
+  const minS = Math.min(sx, sy);
+  return minS;
+}
+
+
+
+export function jelly(item, time = 0.7) {
+
+
+  if (item.jellyTween) {
+    TWEEN.remove(item.jellyTween);
+  }
+
+  const t = time / 9;
+  const baseSX = item.scaleX;
+  const baseSY = item.scaleY;
+  let index = 0;
+
+  const run = () => {
+    if (index >= arr.length) {
+      item.jellyTween = null;
+      return;
+    }
+    const data = arr[index];
+    const t = tweenChange(item, {scaleX: data[0], scaleY: data[1]}, data[2], () => {
+      index ++;
+      run();
+    }, TWEEN.Easing.Sinusoidal.InOut);
+    item.jellyTween = t;
+  };
+
+  const arr = [
+    [baseSX * 1.1, baseSY * 0.9, t],
+    [baseSX * 0.98, baseSY * 1.02, t * 2],
+    [baseSX * 1.02, baseSY * 0.98, t * 2],
+    [baseSX * 0.99, baseSY * 1.01, t * 2],
+    [baseSX * 1.0, baseSY * 1.0, t * 2],
+  ];
+
+
+  run();
+}
+
+
+
+export function showPopParticle(img, pos, parent, num = 20, minLen = 40, maxLen = 80, showTime = 0.4) {
+
+
+  for (let i = 0; i < num; i ++) {
+
+    const particle = new MySprite();
+    particle.init(img);
+    particle.x = pos.x;
+    particle.y = pos.y;
+    parent.addChild(particle);
+
+    const randomR = 360 * Math.random();
+    particle.rotation = randomR;
+
+    const randomS = 0.3 + Math.random() * 0.7;
+    particle.setScaleXY(randomS * 0.3);
+
+    const randomX = Math.random() * 20 - 10;
+    particle.x += randomX;
+
+    const randomY = Math.random() * 20 - 10;
+    particle.y += randomY;
+
+    const randomL = minLen + Math.random() * (maxLen - minLen);
+    const randomA = 360 * Math.random();
+    const randomT = getPosByAngle(randomA, randomL);
+    moveItem(particle, particle.x + randomT.x, particle.y + randomT.y, showTime, () => {
+
+
+
+    }, TWEEN.Easing.Exponential.Out);
+
+    // scaleItem(particle, 0, 0.6, () => {
+    //
+    // });
+
+    scaleItem(particle, randomS, 0.6, () => {
+
+    }, TWEEN.Easing.Exponential.Out);
+
+    setTimeout(() => {
+
+      hideItem(particle, 0.4, () => {
+
+      }, TWEEN.Easing.Cubic.In);
+
+    }, showTime * 0.5 * 1000);
+
+
+  }
+}
+
+
+
+export function shake(item, time = 0.5, callback = null, rate = 1) {
+
+  if (item.shakeTween) {
+    return;
+  }
+
+  item.shakeTween = true;
+  const offX = 15 * item.scaleX * rate;
+  const offY = 15 * item.scaleX * rate;
+  const baseX = item.x;
+  const baseY = item.y;
+  const easing = TWEEN.Easing.Sinusoidal.InOut;
+
+
+  const move4 = () => {
+    moveItem(item, baseX, baseY, time / 4, () => {
+      item.shakeTween = false;
+      if (callback) {
+        callback();
+      }
+    }, easing);
+  };
+
+  const move3 = () => {
+    moveItem(item, baseX + offX / 4, baseY + offY / 4, time / 4, () => {
+      move4();
+    }, easing);
+  };
+
+  const move2 = () => {
+    moveItem(item, baseX - offX / 4 * 3, baseY - offY / 4 * 3, time / 4, () => {
+      move3();
+    }, easing);
+  };
+
+  const move1 = () => {
+    moveItem(item, baseX + offX, baseY + offY, time / 7.5, () => {
+      move2();
+    }, easing);
+  };
+
+  move1();
+
+}
+
+// --------------- custom class --------------------
+
+
+
+export class HotZoneItem extends MySprite {
+
+
+  lineDashFlag = false;
+  arrow: MySprite;
+  label: Label;
+  title;
+
+  arrowTop;
+  arrowRight;
+
+  audio_url;
+  pic_url;
+  text;
+  private _itemType;
+  private shapeRect: ShapeRect;
+
+  get itemType() {
+    return this._itemType;
+  }
+  set itemType(value) {
+    this._itemType = value;
+  }
+
+  setSize(w, h) {
+    this.width = w;
+    this.height = h;
+
+
+    const rect = new ShapeRect(this.ctx);
+    rect.x = -w / 2;
+    rect.y = -h / 2;
+    rect.setSize(w, h);
+    rect.fillColor = '#ffffff';
+    rect.alpha = 0.2;
+    this.addChild(rect);
+  }
+
+  showLabel(text = null) {
+
+
+    if (!this.label) {
+      this.label = new Label(this.ctx);
+      this.label.anchorY = 0;
+      this.label.fontSize = 40;
+      this.label.textAlign = 'center';
+      this.addChild(this.label);
+      // this.label.scaleX = 1 / this.scaleX;
+      // this.label.scaleY = 1 / this.scaleY;
+
+      this.refreshLabelScale();
+
+    }
+
+    if (text) {
+      this.label.text = text;
+    } else if (this.title) {
+      this.label.text = this.title;
+    }
+    this.label.visible = true;
+
+  }
+
+  hideLabel() {
+    if (!this.label) { return; }
+
+    this.label.visible = false;
+  }
+
+  refreshLabelScale() {
+    if (this.scaleX == this.scaleY) {
+      this.label.setScaleXY(1);
+    }
+
+    if (this.scaleX > this.scaleY) {
+      this.label.scaleX = this.scaleY / this.scaleX;
+    } else {
+      this.label.scaleY = this.scaleX / this.scaleY;
+    }
+  }
+
+  showLineDash() {
+    this.lineDashFlag = true;
+
+    if (this.arrow) {
+      this.arrow.visible = true;
+    } else {
+      this.arrow = new MySprite(this.ctx);
+      this.arrow.load('assets/common/arrow.png', 1, 0);
+      this.arrow.setScaleXY(0.06);
+
+      this.arrowTop = new MySprite(this.ctx);
+      this.arrowTop.load('assets/common/arrow_top.png', 0.5, 0);
+      this.arrowTop.setScaleXY(0.06);
+
+      this.arrowRight = new MySprite(this.ctx);
+      this.arrowRight.load('assets/common/arrow_right.png', 1, 0.5);
+      this.arrowRight.setScaleXY(0.06);
+    }
+
+    this.showLabel();
+  }
+
+  hideLineDash() {
+
+    this.lineDashFlag = false;
+
+    if (this.arrow) {
+      this.arrow.visible = false;
+    }
+
+    this.hideLabel();
+  }
+
+
+
+  drawArrow() {
+    if (!this.arrow) { return; }
+
+    const rect = this.getBoundingBox();
+    this.arrow.x = rect.x + rect.width;
+    this.arrow.y = rect.y;
+
+    this.arrow.update();
+
+
+    this.arrowTop.x = rect.x + rect.width / 2;
+    this.arrowTop.y = rect.y;
+    this.arrowTop.update();
+
+    this.arrowRight.x = rect.x + rect.width;
+    this.arrowRight.y = rect.y + rect.height / 2;
+    this.arrowRight.update();
+  }
+
+  drawFrame() {
+
+
+    this.ctx.save();
+
+
+    const rect = this.getBoundingBox();
+
+    const w = rect.width;
+    const h = rect.height;
+    const x = rect.x + w / 2;
+    const y = rect.y + h / 2;
+
+    this.ctx.setLineDash([5, 5]);
+    this.ctx.lineWidth = 2;
+    this.ctx.strokeStyle = '#1bfff7';
+    // this.ctx.fillStyle = '#ffffff';
+
+    this.ctx.beginPath();
+
+    this.ctx.moveTo( x - w / 2, y - h / 2);
+
+    this.ctx.lineTo(x + w / 2, y - h / 2);
+
+    this.ctx.lineTo(x + w / 2, y + h / 2);
+
+    this.ctx.lineTo(x - w / 2, y + h / 2);
+
+    this.ctx.lineTo(x - w / 2, y - h / 2);
+
+    // this.ctx.fill();
+    this.ctx.stroke();
+
+
+
+
+    this.ctx.restore();
+
+  }
+
+  draw() {
+    super.draw();
+
+    if (this.lineDashFlag) {
+      this.drawFrame();
+      this.drawArrow();
+    }
+  }
+}
+
+export class HotZoneImg extends MySprite {
+
+  drawFrame() {
+
+    this.ctx.save();
+
+    const rect = this.getBoundingBox();
+    const w = rect.width;
+    const h = rect.height;
+    const x = rect.x + w / 2;
+    const y = rect.y + h / 2;
+
+    this.ctx.setLineDash([5, 5]);
+    this.ctx.lineWidth = 2;
+    this.ctx.strokeStyle = '#1bfff7';
+
+    this.ctx.beginPath();
+    this.ctx.moveTo( x - w / 2, y - h / 2);
+    this.ctx.lineTo(x + w / 2, y - h / 2);
+    this.ctx.lineTo(x + w / 2, y + h / 2);
+    this.ctx.lineTo(x - w / 2, y + h / 2);
+    this.ctx.lineTo(x - w / 2, y - h / 2);
+
+    // this.ctx.fill();
+    this.ctx.stroke();
+
+    this.ctx.restore();
+  }
+
+  draw() {
+    super.draw();
+
+    this.drawFrame();
+  }
+}
+
+export class HotZoneLabel extends Label {
+
+  drawFrame() {
+
+    this.ctx.save();
+
+    const rect = this.getBoundingBox();
+    const w = rect.width / this.scaleX;
+    const h = this.height * this.scaleY;
+    const x = this.x;
+    const y = this.y;
+
+    this.ctx.setLineDash([5, 5]);
+    this.ctx.lineWidth = 2;
+    this.ctx.strokeStyle = '#1bfff7';
+
+    this.ctx.beginPath();
+    this.ctx.moveTo( x - w / 2, y - h / 2);
+    this.ctx.lineTo(x + w / 2, y - h / 2);
+    this.ctx.lineTo(x + w / 2, y + h / 2);
+    this.ctx.lineTo(x - w / 2, y + h / 2);
+    this.ctx.lineTo(x - w / 2, y - h / 2);
+
+    // this.ctx.fill();
+    this.ctx.stroke();
+
+    this.ctx.restore();
+  }
+
+  draw() {
+    super.draw();
+
+    this.drawFrame();
+  }
+}
+
+
+
+
+export class EditorItem extends MySprite {
+
+  lineDashFlag = false;
+  arrow: MySprite;
+  label: Label;
+  text;
+
+  showLabel(text = null) {
+
+
+    if (!this.label) {
+      this.label = new Label(this.ctx);
+      this.label.anchorY = 0;
+      this.label.fontSize = 50;
+      this.label.textAlign = 'center';
+      this.addChild(this.label);
+      this.label.setScaleXY(1 / this.scaleX);
+    }
+
+    if (text) {
+      this.label.text = text;
+    } else if (this.text) {
+      this.label.text = this.text;
+    }
+    this.label.visible = true;
+
+  }
+
+  hideLabel() {
+    if (!this.label) { return; }
+
+    this.label.visible = false;
+  }
+
+  showLineDash() {
+    this.lineDashFlag = true;
+
+    if (this.arrow) {
+      this.arrow.visible = true;
+    } else {
+      this.arrow = new MySprite(this.ctx);
+      this.arrow.load('assets/common/arrow.png', 1, 0);
+      this.arrow.setScaleXY(0.06);
+
+    }
+
+    this.showLabel();
+  }
+
+  hideLineDash() {
+
+    this.lineDashFlag = false;
+
+    if (this.arrow) {
+      this.arrow.visible = false;
+    }
+
+    this.hideLabel();
+  }
+
+
+
+  drawArrow() {
+    if (!this.arrow) { return; }
+
+    const rect = this.getBoundingBox();
+    this.arrow.x = rect.x + rect.width;
+    this.arrow.y = rect.y;
+
+    this.arrow.update();
+  }
+
+  drawFrame() {
+
+
+    this.ctx.save();
+
+
+    const rect = this.getBoundingBox();
+
+    const w = rect.width;
+    const h = rect.height;
+    const x = rect.x + w / 2;
+    const y = rect.y + h / 2;
+
+    this.ctx.setLineDash([5, 5]);
+    this.ctx.lineWidth = 2;
+    this.ctx.strokeStyle = '#1bfff7';
+    // this.ctx.fillStyle = '#ffffff';
+
+    this.ctx.beginPath();
+
+    this.ctx.moveTo( x - w / 2, y - h / 2);
+
+    this.ctx.lineTo(x + w / 2, y - h / 2);
+
+    this.ctx.lineTo(x + w / 2, y + h / 2);
+
+    this.ctx.lineTo(x - w / 2, y + h / 2);
+
+    this.ctx.lineTo(x - w / 2, y - h / 2);
+
+    // this.ctx.fill();
+    this.ctx.stroke();
+
+
+
+
+    this.ctx.restore();
+
+  }
+
+  draw() {
+    super.draw();
+
+    if (this.lineDashFlag) {
+      this.drawFrame();
+      this.drawArrow();
+    }
+  }
+}
+
+
+
+//
+//
+// import TWEEN from '@tweenjs/tween.js';
+//
+//
+// class Sprite {
+//   x = 0;
+//   y = 0;
+//   color = '';
+//   radius = 0;
+//   alive = false;
+//   margin = 0;
+//   angle = 0;
+//   ctx;
+//
+//   constructor(ctx) {
+//     this.ctx = ctx;
+//   }
+//   update($event) {
+//     this.draw();
+//   }
+//   draw() {
+//
+//   }
+//
+// }
+//
+//
+//
+//
+//
+// export class MySprite extends Sprite {
+//
+//   width = 0;
+//   height = 0;
+//   _anchorX = 0;
+//   _anchorY = 0;
+//   _offX = 0;
+//   _offY = 0;
+//   scaleX = 1;
+//   scaleY = 1;
+//   alpha = 1;
+//   rotation = 0;
+//   visible = true;
+//
+//   children = [this];
+//
+//   img;
+//   _z = 0;
+//
+//
+//   init(imgObj = null, anchorX:number = 0.5, anchorY:number = 0.5) {
+//
+//     if (imgObj) {
+//
+//       this.img = imgObj;
+//
+//       this.width = this.img.width;
+//       this.height = this.img.height;
+//     }
+//
+//     this.anchorX = anchorX;
+//     this.anchorY = anchorY;
+//   }
+//
+//
+//
+//   update($event = null) {
+//     if (this.visible) {
+//       this.draw();
+//     }
+//   }
+//   draw() {
+//
+//     this.ctx.save();
+//
+//     this.drawInit();
+//
+//     this.updateChildren();
+//
+//     this.ctx.restore();
+//   }
+//
+//   drawInit() {
+//
+//     this.ctx.translate(this.x, this.y);
+//
+//     this.ctx.rotate(this.rotation * Math.PI / 180);
+//
+//     this.ctx.scale(this.scaleX, this.scaleY);
+//
+//     this.ctx.globalAlpha = this.alpha;
+//
+//   }
+//
+//   drawSelf() {
+//     if (this.img) {
+//       this.ctx.drawImage(this.img, this._offX, this._offY);
+//     }
+//   }
+//
+//   updateChildren() {
+//
+//     if (this.children.length <= 0) { return; }
+//
+//     for (let i = 0; i < this.children.length; i++) {
+//
+//       if (this.children[i] === this) {
+//
+//         this.drawSelf();
+//       } else {
+//
+//         this.children[i].update();
+//       }
+//     }
+//   }
+//
+//
+//   load(url, anchorX = 0.5, anchorY = 0.5) {
+//
+//     return new Promise((resolve, reject) => {
+//       const img = new Image();
+//       img.onload = () => resolve(img);
+//       img.onerror = reject;
+//       img.src = url;
+//     }).then(img => {
+//
+//       this.init(img, anchorX, anchorY);
+//       return img;
+//     });
+//   }
+//
+//   addChild(child, z = 1) {
+//     if (this.children.indexOf(child) === -1) {
+//       this.children.push(child);
+//       child._z = z;
+//       child.parent = this;
+//     }
+//
+//     this.children.sort((a, b) => {
+//       return a._z - b._z;
+//     });
+//
+//   }
+//   removeChild(child) {
+//     const index = this.children.indexOf(child);
+//     if (index !== -1) {
+//       this.children.splice(index, 1);
+//     }
+//   }
+//
+//   set anchorX(value) {
+//     this._anchorX = value;
+//     this.refreshAnchorOff();
+//   }
+//   get anchorX() {
+//     return this._anchorX;
+//   }
+//   set anchorY(value) {
+//     this._anchorY = value;
+//     this.refreshAnchorOff();
+//   }
+//   get anchorY() {
+//     return this._anchorY;
+//   }
+//   refreshAnchorOff() {
+//     this._offX = -this.width * this.anchorX;
+//     this._offY = -this.height * this.anchorY;
+//   }
+//
+//   setScaleXY(value) {
+//     this.scaleX = this.scaleY = value;
+//   }
+//
+//   getBoundingBox() {
+//
+//     const x = this.x + this._offX * this.scaleX;
+//     const y = this.y + this._offY * this.scaleY;
+//     const width = this.width * this.scaleX;
+//     const height = this.height * this.scaleY;
+//
+//     return {x, y, width, height};
+//   }
+//
+// }
+//
+//
+//
+//
+//
+// export class Item extends MySprite {
+//
+//   baseX;
+//
+//   move(targetY, callBack) {
+//
+//     const self = this;
+//
+//     const tween = new TWEEN.Tween(this)
+//       .to({ y: targetY }, 2500)
+//       .easing(TWEEN.Easing.Quintic.Out)
+//       .onComplete(function() {
+//
+//         self.hide(callBack);
+//         // if (callBack) {
+//         //   callBack();
+//         // }
+//       })
+//       .start();
+//
+//   }
+//
+//   show(callBack = null) {
+//
+//     const tween = new TWEEN.Tween(this)
+//       .to({ alpha: 1 }, 800)
+//       // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
+//       .onComplete(function() {
+//         if (callBack) {
+//           callBack();
+//         }
+//       })
+//       .start(); // Start the tween immediately.
+//
+//   }
+//
+//   hide(callBack = null) {
+//
+//     const tween = new TWEEN.Tween(this)
+//       .to({ alpha: 0 }, 800)
+//       // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
+//       .onComplete(function() {
+//         if (callBack) {
+//           callBack();
+//         }
+//       })
+//       .start(); // Start the tween immediately.
+//   }
+//
+//
+//   shake(id) {
+//
+//
+//     if (!this.baseX) {
+//       this.baseX = this.x;
+//     }
+//
+//     const baseX = this.baseX;
+//     const baseTime = 50;
+//     const sequence = [
+//       {target: {x: baseX + 40 * id}, time: baseTime - 25},
+//       {target: {x: baseX - 20 * id}, time: baseTime},
+//       {target: {x: baseX + 10 * id}, time: baseTime},
+//       {target: {x: baseX - 5 * id}, time: baseTime},
+//       {target: {x: baseX + 2 * id}, time: baseTime},
+//       {target: {x: baseX - 1 * id}, time: baseTime},
+//       {target: {x: baseX}, time: baseTime},
+//
+//     ];
+//
+//
+//     const self = this;
+//
+//     function runSequence() {
+//
+//       if (self['shakeTween']) {
+//         self['shakeTween'].stop();
+//       }
+//
+//       const tween = new TWEEN.Tween(self);
+//
+//       if (sequence.length > 0) {
+//         // console.log('sequence.length: ', sequence.length);
+//         const action = sequence.shift();
+//         tween.to(action['target'], action['time']);
+//         tween.onComplete( () => {
+//           runSequence();
+//         });
+//         tween.start();
+//
+//         self['shakeTween'] = tween;
+//       }
+//     }
+//
+//     runSequence();
+//
+//   }
+//
+//
+//
+//   drop(targetY, callBack = null) {
+//
+//     const self = this;
+//
+//     const time = Math.abs(targetY - this.y) * 2.4;
+//
+//     this.alpha = 1;
+//
+//     const tween = new TWEEN.Tween(this)
+//       .to({ y: targetY }, time)
+//       .easing(TWEEN.Easing.Cubic.In)
+//       .onComplete(function() {
+//
+//         // self.hideItem(callBack);
+//         if (callBack) {
+//           callBack();
+//         }
+//       })
+//       .start();
+//
+//
+//   }
+//
+//
+// }
+//
+//
+// export class EndSpr extends MySprite {
+//
+//   show(s) {
+//
+//     this.scaleX = this.scaleY = 0;
+//     this.alpha = 0;
+//
+//     const tween = new TWEEN.Tween(this)
+//       .to({ alpha: 1, scaleX: s, scaleY: s }, 800)
+//       .easing(TWEEN.Easing.Elastic.Out) // Use an easing function to make the animation smooth.
+//       .onComplete(function() {
+//
+//       })
+//       .start(); // Start the tween immediately.
+//
+//   }
+// }
+//
+//
+//
+// export class ShapeRect extends MySprite {
+//
+//   fillColor = '#FF0000';
+//
+//   setSize(w, h) {
+//     this.width = w;
+//     this.height = h;
+//
+//     console.log('w:', w);
+//     console.log('h:', h);
+//   }
+//
+//   drawShape() {
+//
+//     this.ctx.fillStyle = this.fillColor;
+//     this.ctx.fillRect(this._offX, this._offY, this.width, this.height);
+//
+//   }
+//
+//
+//   drawSelf() {
+//     super.drawSelf();
+//     this.drawShape();
+//   }
+// }
+//
+//
+// export class HotZoneItem extends MySprite {
+//
+//
+//   lineDashFlag = false;
+//   arrow: MySprite;
+//   label: Label;
+//   title;
+//
+//   arrowTop;
+//   arrowRight;
+//
+//   audio_url;
+//   pic_url;
+//   text;
+//   private _itemType;
+//   private shapeRect: ShapeRect;
+//
+//   get itemType() {
+//     return this._itemType;
+//   }
+//   set itemType(value) {
+//     this._itemType = value;
+//   }
+//
+//   setSize(w, h) {
+//     this.width = w;
+//     this.height = h;
+//
+//
+//     const rect = new ShapeRect(this.ctx);
+//     rect.x = -w / 2;
+//     rect.y = -h / 2;
+//     rect.setSize(w, h);
+//     rect.fillColor = '#ffffff';
+//     rect.alpha = 0.2;
+//     this.addChild(rect);
+//   }
+//
+//   showLabel(text = null) {
+//
+//
+//     if (!this.label) {
+//       this.label = new Label(this.ctx);
+//       this.label.anchorY = 0;
+//       this.label.fontSize = '40px';
+//       this.label.textAlign = 'center';
+//       this.addChild(this.label);
+//       // this.label.scaleX = 1 / this.scaleX;
+//       // this.label.scaleY = 1 / this.scaleY;
+//
+//       this.refreshLabelScale();
+//
+//     }
+//
+//     if (text) {
+//       this.label.text = text;
+//     } else if (this.title) {
+//       this.label.text = this.title;
+//     }
+//     this.label.visible = true;
+//
+//   }
+//
+//   hideLabel() {
+//     if (!this.label) { return; }
+//
+//     this.label.visible = false;
+//   }
+//
+//   refreshLabelScale() {
+//     if (this.scaleX == this.scaleY) {
+//       this.label.setScaleXY(1);
+//     }
+//
+//     if (this.scaleX > this.scaleY) {
+//       this.label.scaleX = this.scaleY / this.scaleX;
+//     } else {
+//       this.label.scaleY = this.scaleX / this.scaleY;
+//     }
+//   }
+//
+//   showLineDash() {
+//     this.lineDashFlag = true;
+//
+//     if (this.arrow) {
+//       this.arrow.visible = true;
+//     } else {
+//       this.arrow = new MySprite(this.ctx);
+//       this.arrow.load('assets/common/arrow.png', 1, 0);
+//       this.arrow.setScaleXY(0.06);
+//
+//       this.arrowTop = new MySprite(this.ctx);
+//       this.arrowTop.load('assets/common/arrow_top.png', 0.5, 0);
+//       this.arrowTop.setScaleXY(0.06);
+//
+//       this.arrowRight = new MySprite(this.ctx);
+//       this.arrowRight.load('assets/common/arrow_right.png', 1, 0.5);
+//       this.arrowRight.setScaleXY(0.06);
+//     }
+//
+//     this.showLabel();
+//   }
+//
+//   hideLineDash() {
+//
+//     this.lineDashFlag = false;
+//
+//     if (this.arrow) {
+//       this.arrow.visible = false;
+//     }
+//
+//     this.hideLabel();
+//   }
+//
+//
+//
+//   drawArrow() {
+//     if (!this.arrow) { return; }
+//
+//     const rect = this.getBoundingBox();
+//     this.arrow.x = rect.x + rect.width;
+//     this.arrow.y = rect.y;
+//
+//     this.arrow.update();
+//
+//
+//     this.arrowTop.x = rect.x + rect.width / 2;
+//     this.arrowTop.y = rect.y;
+//     this.arrowTop.update();
+//
+//     this.arrowRight.x = rect.x + rect.width;
+//     this.arrowRight.y = rect.y + rect.height / 2;
+//     this.arrowRight.update();
+//   }
+//
+//   drawFrame() {
+//
+//
+//     this.ctx.save();
+//
+//
+//     const rect = this.getBoundingBox();
+//
+//     const w = rect.width;
+//     const h = rect.height;
+//     const x = rect.x + w / 2;
+//     const y = rect.y + h / 2;
+//
+//     this.ctx.setLineDash([5, 5]);
+//     this.ctx.lineWidth = 2;
+//     this.ctx.strokeStyle = '#1bfff7';
+//     // this.ctx.fillStyle = '#ffffff';
+//
+//     this.ctx.beginPath();
+//
+//     this.ctx.moveTo( x - w / 2, y - h / 2);
+//
+//     this.ctx.lineTo(x + w / 2, y - h / 2);
+//
+//     this.ctx.lineTo(x + w / 2, y + h / 2);
+//
+//     this.ctx.lineTo(x - w / 2, y + h / 2);
+//
+//     this.ctx.lineTo(x - w / 2, y - h / 2);
+//
+//     // this.ctx.fill();
+//     this.ctx.stroke();
+//
+//
+//
+//
+//     this.ctx.restore();
+//
+//   }
+//
+//   draw() {
+//     super.draw();
+//
+//     if (this.lineDashFlag) {
+//       this.drawFrame();
+//       this.drawArrow();
+//     }
+//   }
+// }
+//
+//
+// export class EditorItem extends MySprite {
+//
+//   lineDashFlag = false;
+//   arrow: MySprite;
+//   label:Label;
+//   text;
+//
+//   showLabel(text = null) {
+//
+//
+//     if (!this.label) {
+//       this.label = new Label(this.ctx);
+//       this.label.anchorY = 0;
+//       this.label.fontSize = '50px';
+//       this.label.textAlign = 'center';
+//       this.addChild(this.label);
+//       this.label.setScaleXY(1 / this.scaleX);
+//     }
+//
+//     if (text) {
+//       this.label.text = text;
+//     } else if (this.text) {
+//       this.label.text = this.text;
+//     }
+//     this.label.visible = true;
+//
+//   }
+//
+//   hideLabel() {
+//     if (!this.label) { return; }
+//
+//     this.label.visible = false;
+//   }
+//
+//   showLineDash() {
+//     this.lineDashFlag = true;
+//
+//     if (this.arrow) {
+//       this.arrow.visible = true;
+//     } else {
+//       this.arrow = new MySprite(this.ctx);
+//       this.arrow.load('assets/common/arrow.png', 1, 0);
+//       this.arrow.setScaleXY(0.06);
+//
+//     }
+//
+//     this.showLabel();
+//   }
+//
+//   hideLineDash() {
+//
+//     this.lineDashFlag = false;
+//
+//     if (this.arrow) {
+//       this.arrow.visible = false;
+//     }
+//
+//     this.hideLabel();
+//   }
+//
+//
+//
+//   drawArrow() {
+//     if (!this.arrow) { return; }
+//
+//     const rect = this.getBoundingBox();
+//     this.arrow.x = rect.x + rect.width;
+//     this.arrow.y = rect.y;
+//
+//     this.arrow.update();
+//   }
+//
+//   drawFrame() {
+//
+//
+//     this.ctx.save();
+//
+//
+//     const rect = this.getBoundingBox();
+//
+//     const w = rect.width;
+//     const h = rect.height;
+//     const x = rect.x + w / 2;
+//     const y = rect.y + h / 2;
+//
+//     this.ctx.setLineDash([5, 5]);
+//     this.ctx.lineWidth = 2;
+//     this.ctx.strokeStyle = '#1bfff7';
+//     // this.ctx.fillStyle = '#ffffff';
+//
+//     this.ctx.beginPath();
+//
+//     this.ctx.moveTo( x - w / 2, y - h / 2);
+//
+//     this.ctx.lineTo(x + w / 2, y - h / 2);
+//
+//     this.ctx.lineTo(x + w / 2, y + h / 2);
+//
+//     this.ctx.lineTo(x - w / 2, y + h / 2);
+//
+//     this.ctx.lineTo(x - w / 2, y - h / 2);
+//
+//     // this.ctx.fill();
+//     this.ctx.stroke();
+//
+//
+//
+//
+//     this.ctx.restore();
+//
+//   }
+//
+//   draw() {
+//     super.draw();
+//
+//     if (this.lineDashFlag) {
+//       this.drawFrame();
+//       this.drawArrow();
+//     }
+//   }
+// }
+//
+//
+//
+// export class Label extends MySprite {
+//
+//   text:String;
+//   fontSize:String = '40px';
+//   fontName:String = 'Verdana';
+//   textAlign:String = 'left';
+//
+//
+//   constructor(ctx) {
+//     super(ctx);
+//     this.init();
+//   }
+//
+//   drawText() {
+//
+//     // console.log('in drawText', this.text);
+//
+//     if (!this.text) { return; }
+//
+//     this.ctx.font = `${this.fontSize} ${this.fontName}`;
+//     this.ctx.textAlign = this.textAlign;
+//     this.ctx.textBaseline = 'middle';
+//     this.ctx.fontWeight = 900;
+//
+//     this.ctx.lineWidth = 5;
+//     this.ctx.strokeStyle = '#ffffff';
+//     this.ctx.strokeText(this.text, 0, 0);
+//
+//     this.ctx.fillStyle = '#000000';
+//     this.ctx.fillText(this.text, 0, 0);
+//
+//
+//   }
+//
+//
+//   drawSelf() {
+//     super.drawSelf();
+//     this.drawText();
+//   }
+//
+// }
+//
+//
+//
+// export function getPosByAngle(angle, len) {
+//
+//   const radian = angle * Math.PI / 180;
+//   const x = Math.sin(radian) * len;
+//   const y = Math.cos(radian) * len;
+//
+//   return {x, y};
+//
+// }
+//
+// export function getAngleByPos(px, py, mx, my) {
+//
+//   // const _x = p2x - p1x;
+//   // const _y = p2y - p1y;
+//   // const tan = _y / _x;
+//   //
+//   // const radina = Math.atan(tan); // 用反三角函数求弧度
+//   // const angle = Math.floor(180 / (Math.PI / radina)); //
+//   //
+//   // console.log('r: ' , angle);
+//   // return angle;
+//   //
+//
+//
+//
+//   const x = Math.abs(px - mx);
+//   const y = Math.abs(py - my);
+//   // const x = Math.abs(mx - px);
+//   // const y = Math.abs(my - py);
+//   const z = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
+//   const cos = y / z;
+//   const radina = Math.acos(cos); // 用反三角函数求弧度
+//   let angle = Math.floor(180 / (Math.PI / radina) * 100) / 100; // 将弧度转换成角度
+//
+//   if(mx > px && my > py) {// 鼠标在第四象限
+//     angle = 180 - angle;
+//   }
+//
+//   if(mx === px && my > py) {// 鼠标在y轴负方向上
+//     angle = 180;
+//   }
+//
+//   if(mx > px && my === py) {// 鼠标在x轴正方向上
+//     angle = 90;
+//   }
+//
+//   if(mx < px && my > py) {// 鼠标在第三象限
+//     angle = 180 + angle;
+//   }
+//
+//   if(mx < px && my === py) {// 鼠标在x轴负方向
+//     angle = 270;
+//   }
+//
+//   if(mx < px && my < py) {// 鼠标在第二象限
+//     angle = 360 - angle;
+//   }
+//
+//   // console.log('angle: ', angle);
+//   return angle;
+//
+// }
diff --git a/form/src/app/common/custom-hot-zone/custom-hot-zone.component.html b/form/src/app/common/custom-hot-zone/custom-hot-zone.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..9028527061890eefb1557c53f49394c7ea827b11
--- /dev/null
+++ b/form/src/app/common/custom-hot-zone/custom-hot-zone.component.html
@@ -0,0 +1,100 @@
+<div class="p-image-children-editor">
+
+  <h5 style="margin-left: 2.5%;"> preview: </h5>
+
+  <div class="preview-box" #wrap>
+    <canvas id="canvas" #canvas></canvas>
+  </div>
+
+  <div nz-row nzType="flex" nzAlign="middle">
+    <div nz-col nzSpan="5" nzOffset="1">
+
+      <h5> add background: </h5>
+
+      <div class="bg-box">
+        <app-upload-image-with-preview
+          [picUrl]="bgItem?.url"
+          (imageUploaded)="onBackgroundUploadSuccess($event)">
+        </app-upload-image-with-preview>
+      </div>
+    </div>
+
+    <div nz-col nzSpan="5" nzOffset="1" class="img-box"
+         *ngFor="let it of hotZoneArr; let i = index">
+
+      <div
+        style="margin: auto; padding: 5px;  margin-top: 30px; width:90%; border: 2px dashed #ddd; border-radius: 10px">
+        <span style="margin-left: 40%;"> item-{{i + 1}}
+        </span>
+        <button style="float: right;" nz-button nzType="danger" nzSize="small" (click)="deleteBtnClick(i)">
+          X
+        </button>
+
+        <nz-divider style="margin-top: 10px;"></nz-divider>
+
+
+        <div style="margin-top: -20px; margin-bottom: 5px">
+          <nz-radio-group [ngModel]="it.itemType" (ngModelChange)="radioChange($event, it)">
+            <label *ngIf="isHasRect" nz-radio nzValue="rect">矩形</label>
+            <label *ngIf="isHasPic" nz-radio nzValue="pic">图片</label>
+            <label *ngIf="isHasText" nz-radio nzValue="text">文本</label>
+          </nz-radio-group>
+        </div>
+
+        <div *ngIf="it.itemType == 'pic'">
+          <app-upload-image-with-preview
+            [picUrl]="it?.pic_url"
+            (imageUploaded)="onItemImgUploadSuccess($event, it)">
+          </app-upload-image-with-preview>
+        </div>
+
+        <div *ngIf="it.itemType == 'text'">
+          <input type="text" nz-input [(ngModel)]="it.text" (blur)="saveText(it)">
+        </div>
+
+        <div style="width: 100%; margin-top: 5px;">
+          <app-audio-recorder
+            [audioUrl]="it.audio_url"
+            (audioUploaded)="onItemAudioUploadSuccess($event, it)"
+          ></app-audio-recorder>
+        </div>
+
+      </div>
+
+    </div>
+
+
+    <div nz-col nzSpan="5" nzOffset="1">
+
+      <div class="bg-box">
+        <button nz-button nzType="dashed" (click)="addBtnClick()"
+                class="add-btn">
+          <i nz-icon nzType="plus-circle" nzTheme="outline"></i>
+          <!--Add Image-->
+          Add hot zone
+        </button>
+      </div>
+
+    </div>
+  </div>
+
+
+  <nz-divider></nz-divider>
+
+  <div class="save-box">
+
+    <button class="save-btn" nz-button nzType="primary" [nzSize]="'large'" nzShape="round"
+            (click)="saveClick()" >
+      <i nz-icon nzType="save"></i>
+      Save
+    </button>
+
+  </div>
+
+
+</div>
+
+
+<label style="opacity: 0; position: absolute; top: 0px; font-family: 'BRLNSR_1'">1</label>
+
+
diff --git a/form/src/app/common/custom-hot-zone/custom-hot-zone.component.scss b/form/src/app/common/custom-hot-zone/custom-hot-zone.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..9e2c2bf053571db2d78708132b23fe87e0acc9b2
--- /dev/null
+++ b/form/src/app/common/custom-hot-zone/custom-hot-zone.component.scss
@@ -0,0 +1,210 @@
+
+.p-image-children-editor {
+
+  width: 100%;
+  height: 100%;
+  border-radius: 0.5rem;
+
+  border: 2px solid #ddd;
+
+
+
+  .preview-box {
+
+    margin: auto;
+    width: 95%;
+    height: 35vw;
+
+    border: 2px dashed #ddd;
+    border-radius: 0.5rem;
+    background-color: #fafafa;
+    text-align: center;
+    color: #aaa;
+
+
+    .preview-img {
+      height: 100%;
+      width: auto;
+    }
+
+  }
+
+
+
+  .bg-box{
+    //width: 100%;
+    margin-bottom: 1rem;
+
+  }
+
+  .img-box {
+    margin-bottom: 1rem;
+
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+  }
+  .img-box-upload{
+    width: 80%;
+  }
+
+  .add-btn {
+    margin-top: 1rem;
+    width: 200px;
+    height: 90px;
+
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+}
+
+
+.save-box {
+  width: 100%;
+
+  display: flex;
+  align-items: center;
+  justify-content: center;
+
+  .save-btn {
+    width: 160px;
+    height: 40px;
+
+    //margin-top: 20px;
+    margin-bottom: 20px;
+
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+}
+
+h5 {
+  margin-top: 1rem;
+}
+
+
+
+@font-face
+{
+  font-family: 'BRLNSR_1';
+  src: url("/assets/font/BRLNSR_1.TTF") ;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//@import '../../../style/common_mixin';
+//
+//.p-image-uploader {
+//  position: relative;
+//  display: block;
+//  width: 100%;
+//  height: 0;
+//  padding-bottom: 56.25%;
+//  .p-box {
+//    position: absolute;
+//    left: 0;
+//    top: 0;
+//    right: 0;
+//    bottom: 0;
+//    border: 2px dashed #ddd;
+//    border-radius: 0.5rem;
+//    background-color: #fafafa;
+//    text-align: center;
+//    color: #aaa;
+//    .p-upload-icon {
+//      text-align: center;
+//      margin: auto;
+//      .anticon-upload {
+//        color: #888;
+//        font-size: 5rem;
+//      }
+//      .p-progress-bar {
+//        position: relative;
+//        width: 20rem;
+//        height: 1.5rem;
+//        border: 1px solid #ccc;
+//        border-radius: 1rem;
+//        .p-progress-bg {
+//          background-color: #1890ff;
+//          border-radius: 1rem;
+//          height: 100%;
+//        }
+//        .p-progress-value {
+//          position: absolute;
+//          top: 0;
+//          left: 0;
+//          width: 100%;
+//          height: 100%;
+//          text-shadow: 0 0 4px #000;
+//          color: white;
+//          text-align: center;
+//          font-size: 0.9rem;
+//          line-height: 1.5rem;
+//        }
+//      }
+//    }
+//    .p-preview {
+//      width: 100%;
+//      height: 100%;
+//      //background-image: url("https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png");
+//      @include k-img-bg();
+//    }
+//  }
+//}
+//
+//.p-btn-delete {
+//  position: absolute;
+//  right: -0.5rem;
+//  top: -0.5rem;
+//  width: 2rem;
+//  height: 2rem;
+//  border: 0.2rem solid white;
+//  border-radius: 50%;
+//  font-size: 1.2rem;
+//  background-color: #fb781a;
+//  color: white;
+//  text-align: center;
+//}
+//
+//.p-upload-progress-bg {
+//  position: absolute;
+//  width: 100%;
+//  height: 100%;
+//  display: flex;
+//  align-items: center;
+//  justify-content: center;
+//  & .i-text {
+//    position: absolute;
+//    text-align: center;
+//    color: white;
+//    text-shadow: 0 0 2px rgba(0, 0, 0, .85);
+//  }
+//  & .i-bg {
+//    position: absolute;
+//    left: 0;
+//    top: 0;
+//    height: 100%;
+//    background-color: #27b43f;
+//    border-radius: 0.5rem;
+//  }
+//}
+//
+//
+//:host ::ng-deep .ant-upload {
+//  display: block;
+//}
diff --git a/form/src/app/common/custom-hot-zone/custom-hot-zone.component.ts b/form/src/app/common/custom-hot-zone/custom-hot-zone.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dc2a4cf04d4592cc6a399cf5708c713dbb3439fd
--- /dev/null
+++ b/form/src/app/common/custom-hot-zone/custom-hot-zone.component.ts
@@ -0,0 +1,986 @@
+import {
+  Component,
+  ElementRef,
+  EventEmitter,
+  HostListener,
+  Input,
+  OnChanges,
+  OnDestroy,
+  OnInit,
+  Output,
+  ViewChild
+} from '@angular/core';
+import {Subject} from 'rxjs';
+import {debounceTime} from 'rxjs/operators';
+import {EditorItem, HotZoneImg, HotZoneItem, HotZoneLabel, Label, MySprite, removeItemFromArr} from './Unit';
+import TWEEN from '@tweenjs/tween.js';
+import {getMinScale} from "../../play/Unit";
+import {tar} from "compressing";
+
+
+@Component({
+  selector: 'app-custom-hot-zone',
+  templateUrl: './custom-hot-zone.component.html',
+  styleUrls: ['./custom-hot-zone.component.scss']
+})
+export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
+
+
+  @Input()
+  imgItemArr = null;
+  @Input()
+  hotZoneItemArr = null;
+  @Input()
+  hotZoneArr = null;
+  @Output()
+  save = new EventEmitter();
+  @ViewChild('canvas', {static: true}) canvas: ElementRef;
+  @ViewChild('wrap', {static: true}) wrap: ElementRef;
+
+  @Input()
+  isHasRect = true;
+  @Input()
+  isHasPic = true;
+  @Input()
+  isHasText = true;
+  @Input()
+  hotZoneFontObj = {
+    size: 50,
+    name: 'BRLNSR_1',
+    color: '#8f3758'
+  }
+  @Input()
+  defaultItemType = 'text';
+
+  @Input()
+  hotZoneImgSize = 190;
+
+  saveDisabled = true;
+
+  canvasWidth = 1280;
+  canvasHeight = 720;
+  canvasBaseW = 1280;
+  // @HostListener('window:resize', ['$event'])
+  canvasBaseH = 720;
+  mapScale = 1;
+  ctx;
+  mx;
+  my; // 点击坐标
+  // 声音
+  bgAudio = new Audio();
+  images = new Map();
+  animationId: any;
+
+
+  // 资源
+  // rawImages = new Map(res);
+  winResizeEventStream = new Subject();
+  canvasLeft;
+  canvasTop;
+  renderArr;
+  imgArr = [];
+  oldPos;
+  radioValue;
+  curItem;
+  bg: MySprite;
+  changeSizeFlag = false;
+  changeTopSizeFlag = false;
+  changeRightSizeFlag = false;
+
+  constructor() {
+  }
+
+  _bgItem = null;
+
+  get bgItem() {
+    return this._bgItem;
+  }
+
+  @Input()
+  set bgItem(v) {
+    this._bgItem = v;
+
+    this.init();
+  }
+
+  onResize(event) {
+    this.winResizeEventStream.next();
+  }
+
+
+  ngOnInit() {
+
+    this.initListener();
+
+    // this.init();
+    this.update();
+
+  }
+
+  ngOnDestroy() {
+    window.cancelAnimationFrame(this.animationId);
+  }
+
+
+  ngOnChanges() {
+
+  }
+
+
+  onBackgroundUploadSuccess(e) {
+    console.log('e: ', e);
+    this.bgItem.url = e.url;
+    this.refreshBackground();
+  }
+
+  onItemImgUploadSuccess(e, item) {
+    item.pic_url = e.url;
+    this.loadHotZonePic(item.pic, e.url);
+  }
+
+  onItemAudioUploadSuccess(e, item) {
+    item.audio_url = e.url;
+  }
+
+
+  refreshBackground(callBack = null) {
+
+    if (!this.bg) {
+      this.bg = new MySprite(this.ctx);
+      this.renderArr.push(this.bg);
+    }
+
+    const bg = this.bg;
+    if (this.bgItem.url) {
+
+      bg.load(this.bgItem.url).then(() => {
+        const rate1 = this.canvasWidth / bg.width;
+        const rate2 = this.canvasHeight / bg.height;
+
+        const rate = Math.min(rate1, rate2);
+        bg.setScaleXY(rate);
+
+
+        bg.x = this.canvasWidth / 2;
+        bg.y = this.canvasHeight / 2;
+
+        if (callBack) {
+          callBack();
+        }
+      });
+    }
+
+  }
+
+
+  addBtnClick() {
+    // this.imgArr.push({});
+    // this.hotZoneArr.push({});
+
+    const item = this.getHotZoneItem();
+    this.hotZoneArr.push(item);
+
+    this.refreshItem(item);
+
+    this.refreshHotZoneId();
+
+  }
+
+  deleteBtnClick(index) {
+
+    const item = this.hotZoneArr.splice(index, 1)[0];
+    removeItemFromArr(this.renderArr, item.pic);
+    removeItemFromArr(this.renderArr, item.textLabel);
+
+    this.refreshHotZoneId();
+
+  }
+
+  onImgUploadSuccessByImg(e, img) {
+    img.pic_url = e.url;
+    this.refreshImage(img);
+  }
+
+  refreshImage(img) {
+
+    this.hideAllLineDash();
+
+    img.picItem = this.getPicItem(img);
+
+    this.refreshImageId();
+
+  }
+
+
+  refreshHotZoneId() {
+    for (let i = 0; i < this.hotZoneArr.length; i++) {
+      this.hotZoneArr[i].index = i;
+
+      if (this.hotZoneArr[i]) {
+        this.hotZoneArr[i].title = 'item-' + (i + 1);
+
+      }
+    }
+  }
+
+
+  refreshImageId() {
+    for (let i = 0; i < this.imgArr.length; i++) {
+      this.imgArr[i].id = i;
+
+      if (this.imgArr[i].picItem) {
+        this.imgArr[i].picItem.text = 'Image-' + (i + 1);
+      }
+    }
+
+  }
+
+  getHotZoneItem(saveData = null) {
+
+
+    const itemW = 200;
+    const itemH = 200;
+    const item = new HotZoneItem(this.ctx);
+    item.setSize(itemW, itemH);
+    item.anchorX = 0.5;
+    item.anchorY = 0.5;
+
+    item.x = this.canvasWidth / 2;
+    item.y = this.canvasHeight / 2;
+
+    item.itemType = this.defaultItemType;
+
+    if (saveData) {
+
+      const saveRect = saveData.rect;
+      item.scaleX = saveRect.width / item.width;
+      item.scaleY = saveRect.height / item.height;
+      item.x = saveRect.x + saveRect.width / 2;
+      item.y = saveRect.y + saveRect.height / 2;
+
+    }
+
+    item.showLineDash();
+
+    const pic = new HotZoneImg(this.ctx);
+    pic.visible = false;
+    item['pic'] = pic;
+    if (saveData && saveData.pic_url) {
+      this.loadHotZonePic(pic, saveData.pic_url);
+    }
+    pic.x = item.x;
+    pic.y = item.y;
+    this.renderArr.push(pic);
+
+    const textLabel = new HotZoneLabel(this.ctx);
+    textLabel.fontSize = this.hotZoneFontObj.size;
+    textLabel.fontName = this.hotZoneFontObj.name;
+    textLabel.fontColor = this.hotZoneFontObj.color;
+    textLabel.textAlign = 'center';
+    // textLabel.setOutline();
+    // console.log('saveData:', saveData);
+    item['textLabel'] = textLabel;
+    textLabel.setScaleXY(this.mapScale);
+    if (saveData && saveData.text) {
+      textLabel.text = saveData.text;
+      textLabel.refreshSize();
+    }
+    textLabel.x = item.x;
+    textLabel.y = item.y;
+    this.renderArr.push(textLabel);
+
+    return item;
+  }
+
+
+  getPicItem(img, saveData = null) {
+
+
+    const item = new EditorItem(this.ctx);
+    item.load(img.pic_url).then(img => {
+
+      let maxW, maxH;
+      if (this.bg) {
+        maxW = this.bg.width * this.bg.scaleX;
+        maxH = this.bg.height * this.bg.scaleY;
+      } else {
+        maxW = this.canvasWidth;
+        maxH = this.canvasHeight;
+      }
+
+      let scaleX = maxW / 3 / item.width;
+      let scaleY = maxH / 3 / item.height;
+
+      if (item.height * scaleX < this.canvasHeight) {
+        item.setScaleXY(scaleX);
+      } else {
+        item.setScaleXY(scaleY);
+      }
+      item.x = this.canvasWidth / 2;
+      item.y = this.canvasHeight / 2;
+
+      if (saveData) {
+
+        const saveRect = saveData.rect;
+        item.setScaleXY(saveRect.width / item.width);
+        item.x = saveRect.x + saveRect.width / 2;
+        item.y = saveRect.y + saveRect.height / 2;
+
+      } else {
+        item.showLineDash();
+      }
+
+    });
+    return item;
+  }
+
+
+  onAudioUploadSuccessByImg(e, img) {
+    img.audio_url = e.url;
+  }
+
+  deleteItem(e, i) {
+    // this.imgArr.splice(i , 1);
+    // this.refreshImageId();
+
+    this.hotZoneArr.splice(i, 1);
+    this.refreshHotZoneId();
+  }
+
+
+  radioChange(e, item) {
+    item.itemType = e;
+
+    this.refreshItem(item);
+
+    // console.log(' in radioChange e: ', e);
+  }
+
+  refreshItem(item) {
+    switch (item.itemType) {
+      case 'rect':
+        this.setRectState(item);
+        break;
+      case 'pic':
+        this.setPicState(item);
+        break;
+      case 'text':
+        this.setTextState(item);
+        break;
+      default:
+
+    }
+  }
+
+
+  init() {
+
+    this.initData();
+    this.initCtx();
+
+    this.initItem();
+  }
+
+  initItem() {
+    if (!this.bgItem) {
+      this.bgItem = {};
+    } else {
+      this.refreshBackground(() => {
+
+        // if (!this.imgItemArr) {
+        //   this.imgItemArr = [];
+        // } else {
+        //   this.initImgArr();
+        // }
+
+        // console.log('aaaaa');
+
+        if (!this.hotZoneItemArr) {
+          this.hotZoneItemArr = [];
+        } else {
+          this.initHotZoneArr();
+        }
+
+      });
+    }
+
+  }
+
+  initHotZoneArr() {
+
+    // console.log('this.hotZoneArr: ', this.hotZoneArr);
+    let curBgRect;
+    if (this.bg) {
+      curBgRect = this.bg.getBoundingBox();
+    } else {
+      curBgRect = {x: 0, y: 0, width: this.canvasWidth, height: this.canvasHeight};
+    }
+
+    let oldBgRect = this.bgItem.rect;
+    if (!oldBgRect) {
+      oldBgRect = curBgRect;
+    }
+
+    const rate = curBgRect.width / oldBgRect.width;
+
+    console.log('rate: ', rate);
+
+    this.hotZoneArr = [];
+    const arr = this.hotZoneItemArr.concat();
+    for (let i = 0; i < arr.length; i++) {
+
+      const data = JSON.parse(JSON.stringify(arr[i]));
+      // const img = {pic_url: data.pic_url};
+
+      data.rect.x *= rate;
+      data.rect.y *= rate;
+      data.rect.width *= rate;
+      data.rect.height *= rate;
+
+      data.rect.x += curBgRect.x;
+      data.rect.y += curBgRect.y;
+
+      // img['picItem'] = this.getPicItem(img, data);
+      // img['audio_url'] = arr[i].audio_url;
+      // this.imgArr.push(img);
+
+      const item = this.getHotZoneItem(data);
+      item.audio_url = data.audio_url;
+      item.pic_url = data.pic_url;
+      item.text = data.text;
+      item.itemType = data.itemType;
+      this.refreshItem(item);
+
+      console.log('item: ', item);
+      this.hotZoneArr.push(item);
+
+    }
+
+    this.refreshHotZoneId();
+
+    // this.refreshImageId();
+
+  }
+
+
+  initImgArr() {
+
+    console.log('this.imgItemArr: ', this.imgItemArr);
+    let curBgRect;
+    if (this.bg) {
+      curBgRect = this.bg.getBoundingBox();
+    } else {
+      curBgRect = {x: 0, y: 0, width: this.canvasWidth, height: this.canvasHeight};
+    }
+
+    let oldBgRect = this.bgItem.rect;
+    if (!oldBgRect) {
+      oldBgRect = curBgRect;
+    }
+
+    const rate = curBgRect.width / oldBgRect.width;
+
+    console.log('rate: ', rate);
+
+    this.imgArr = [];
+    const arr = this.imgItemArr.concat();
+    for (let i = 0; i < arr.length; i++) {
+
+      const data = JSON.parse(JSON.stringify(arr[i]));
+      const img = {pic_url: data.pic_url};
+
+      data.rect.x *= rate;
+      data.rect.y *= rate;
+      data.rect.width *= rate;
+      data.rect.height *= rate;
+
+      data.rect.x += curBgRect.x;
+      data.rect.y += curBgRect.y;
+
+      img['picItem'] = this.getPicItem(img, data);
+      img['audio_url'] = arr[i].audio_url;
+      this.imgArr.push(img);
+    }
+    this.refreshImageId();
+  }
+
+
+  initData() {
+
+    this.canvasWidth = this.wrap.nativeElement.clientWidth;
+    this.canvasHeight = this.wrap.nativeElement.clientHeight;
+    this.mapScale = this.canvasWidth / this.canvasBaseW;
+    this.renderArr = [];
+    this.bg = null;
+
+    this.imgArr = [];
+    this.hotZoneArr = [];
+  }
+
+  initCtx() {
+    this.ctx = this.canvas.nativeElement.getContext('2d');
+    this.canvas.nativeElement.width = this.canvasWidth;
+    this.canvas.nativeElement.height = this.canvasHeight;
+  }
+
+
+  mapDown(event) {
+
+    this.oldPos = {x: this.mx, y: this.my};
+
+    for (let i = 0; i < this.hotZoneArr.length; i++) {
+
+      const item = this.hotZoneArr[i];
+      let callback;
+      let target;
+      switch (item.itemType) {
+        case 'rect':
+          target = item;
+          callback = this.clickedHotZoneRect.bind(this);
+          break;
+        case 'pic':
+          target = item.pic;
+          callback = this.clickedHotZonePic.bind(this);
+          break;
+        case 'text':
+          target = item.textLabel;
+          callback = this.clickedHotZoneText.bind(this);
+          break;
+      }
+
+      if (this.checkClickTarget(target)) {
+        callback(target);
+        return;
+      }
+    }
+
+
+  }
+
+
+  mapMove(event) {
+    if (!this.curItem) {
+      return;
+    }
+
+    if (this.changeSizeFlag) {
+      this.changeSize();
+
+    } else if (this.changeTopSizeFlag) {
+      this.changeTopSize();
+
+    } else if (this.changeRightSizeFlag) {
+      this.changeRightSize();
+
+    } else {
+
+      const addX = this.mx - this.oldPos.x;
+      const addY = this.my - this.oldPos.y;
+      this.curItem.x += addX;
+      this.curItem.y += addY;
+    }
+
+    this.oldPos = {x: this.mx, y: this.my};
+
+    this.saveDisabled = true;
+
+  }
+
+  mapUp(event) {
+    this.curItem = null;
+    this.changeSizeFlag = false;
+    this.changeTopSizeFlag = false;
+    this.changeRightSizeFlag = false;
+  }
+
+
+  changeSize() {
+    const rect = this.curItem.getBoundingBox();
+
+    let lenW = (this.mx - (rect.x + rect.width / 2)) * 2;
+    let lenH = ((rect.y + rect.height / 2) - this.my) * 2;
+
+
+    let minLen = 20;
+    let s;
+    if (lenW < lenH) {
+      if (lenW < minLen) {
+        lenW = minLen;
+      }
+      s = lenW / this.curItem.width;
+
+    } else {
+      if (lenH < minLen) {
+        lenH = minLen;
+      }
+      s = lenH / this.curItem.height;
+    }
+
+
+    // console.log('s: ', s);
+    this.curItem.setScaleXY(s);
+    this.curItem.refreshLabelScale();
+  }
+
+
+  changeTopSize() {
+    const rect = this.curItem.getBoundingBox();
+
+    // let lenW = ( this.mx - (rect.x + rect.width / 2) ) * 2;
+    let lenH = ((rect.y + rect.height / 2) - this.my) * 2;
+
+
+    let minLen = 20;
+    let s;
+    // if (lenW < lenH) {
+    //   if (lenW < minLen) {
+    //     lenW = minLen;
+    //   }
+    //   s = lenW / this.curItem.width;
+    //
+    // } else {
+    if (lenH < minLen) {
+      lenH = minLen;
+    }
+    s = lenH / this.curItem.height;
+    // }
+
+
+    // console.log('s: ', s);
+    this.curItem.scaleY = s;
+    this.curItem.refreshLabelScale();
+  }
+
+  changeRightSize() {
+    const rect = this.curItem.getBoundingBox();
+
+    let lenW = (this.mx - (rect.x + rect.width / 2)) * 2;
+    // let lenH = ( (rect.y + rect.height / 2) - this.my ) * 2;
+
+    let minLen = 20;
+    let s;
+    if (lenW < minLen) {
+      lenW = minLen;
+    }
+    s = lenW / this.curItem.width;
+
+    this.curItem.scaleX = s;
+    this.curItem.refreshLabelScale();
+  }
+
+  changeItemSize(item) {
+    this.curItem = item;
+    this.changeSizeFlag = true;
+  }
+
+  changeItemTopSize(item) {
+    this.curItem = item;
+    this.changeTopSizeFlag = true;
+  }
+
+  changeItemRightSize(item) {
+    this.curItem = item;
+    this.changeRightSizeFlag = true;
+  }
+
+  changeCurItem(item) {
+
+    this.hideAllLineDash();
+
+    this.curItem = item;
+    this.curItem.showLineDash();
+  }
+
+  hideAllLineDash() {
+
+    for (let i = 0; i < this.imgArr.length; i++) {
+      if (this.imgArr[i].picItem) {
+        this.imgArr[i].picItem.hideLineDash();
+      }
+    }
+  }
+
+
+  update() {
+    if (!this.ctx) {
+      return;
+    }
+
+
+    this.animationId = window.requestAnimationFrame(this.update.bind(this));
+    // 清除画布内容
+    this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
+
+    for (let i = 0; i < this.renderArr.length; i++) {
+      this.renderArr[i].update(this);
+    }
+
+    // for (let i = 0; i < this.imgArr.length; i++) {
+    //   const picItem = this.imgArr[i].picItem;
+    //   if (picItem) {
+    //     picItem.update(this);
+    //   }
+    // }
+
+    this.updateArr(this.hotZoneArr);
+    this.updatePos()
+
+
+    TWEEN.update();
+  }
+
+  updateArr(arr) {
+    if (arr) {
+      for (let i = 0; i < arr.length; i++) {
+        arr[i].update();
+      }
+    }
+  }
+
+
+  renderAfterResize() {
+    this.canvasWidth = this.wrap.nativeElement.clientWidth;
+    this.canvasHeight = this.wrap.nativeElement.clientHeight;
+    this.init();
+  }
+
+  initListener() {
+
+    this.winResizeEventStream
+      .pipe(debounceTime(500))
+      .subscribe(data => {
+        this.renderAfterResize();
+      });
+
+    if (this.IsPC()) {
+
+      this.canvas.nativeElement.addEventListener('mousedown', (event) => {
+        setMxMyByMouse(event);
+        this.mapDown(event);
+      });
+
+      this.canvas.nativeElement.addEventListener('mousemove', (event) => {
+        setMxMyByMouse(event);
+        this.mapMove(event);
+      });
+
+      this.canvas.nativeElement.addEventListener('mouseup', (event) => {
+        setMxMyByMouse(event);
+        this.mapUp(event);
+      });
+
+      const setMxMyByMouse = (event) => {
+        this.mx = event.offsetX;
+        this.my = event.offsetY;
+      };
+
+    } else {
+
+      this.canvas.nativeElement.addEventListener('touchstart', (event) => {
+        setMxMyByTouch(event);
+        this.mapDown(event);
+      });
+
+      this.canvas.nativeElement.addEventListener('touchmove', (event) => {
+        setMxMyByTouch(event);
+        this.mapMove(event);
+      });
+
+      this.canvas.nativeElement.addEventListener('touchend', (event) => {
+        setMxMyByTouch(event);
+        this.mapUp(event);
+      });
+
+      this.canvas.nativeElement.addEventListener('touchcancel', (event) => {
+        setMxMyByTouch(event);
+        this.mapUp(event);
+      });
+
+
+      const setMxMyByTouch = (event) => {
+        if (event.touches.length <= 0) {
+          return;
+        }
+
+        if (this.canvasLeft == null) {
+          setParentOffset();
+        }
+
+        this.mx = event.touches[0].pageX - this.canvasLeft;
+        this.my = event.touches[0].pageY - this.canvasTop;
+      };
+
+
+      const setParentOffset = () => {
+
+        const rect = this.canvas.nativeElement.getBoundingClientRect();
+        this.canvasLeft = rect.left;
+        this.canvasTop = rect.top;
+      };
+    }
+  }
+
+  IsPC() {
+    if (window['ELECTRON']) {
+      return false; // 封装客户端标记
+    }
+    if (document.body.ontouchstart !== undefined) {
+      return false;
+    } else {
+      return true;
+    }
+  }
+
+
+  checkClickTarget(target) {
+
+    const rect = target.getBoundingBox();
+    if (this.checkPointInRect(this.mx, this.my, rect)) {
+      return true;
+    }
+    return false;
+  }
+
+  checkPointInRect(x, y, rect) {
+    if (x >= rect.x && x <= rect.x + rect.width) {
+      if (y >= rect.y && y <= rect.y + rect.height) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+
+  saveClick() {
+    const bgItem = this.bgItem;
+    if (this.bg) {
+      bgItem['rect'] = this.bg.getBoundingBox();
+    } else {
+      bgItem['rect'] = {
+        x: 0,
+        y: 0,
+        width: Math.round(this.canvasWidth * 100) / 100,
+        height: Math.round(this.canvasHeight * 100) / 100
+      };
+    }
+
+
+    const hotZoneItemArr = [];
+    const hotZoneArr = this.hotZoneArr;
+    for (let i = 0; i < hotZoneArr.length; i++) {
+
+      const hotZoneItem = {
+        index: hotZoneArr[i].index,
+        pic_url: hotZoneArr[i].pic_url,
+        text: hotZoneArr[i].text,
+        audio_url: hotZoneArr[i].audio_url,
+        itemType: hotZoneArr[i].itemType,
+        fontSize: this.hotZoneFontObj.size,
+        fontName: this.hotZoneFontObj.name,
+        fontColor: this.hotZoneFontObj.color,
+        fontScale: hotZoneArr[i].textLabel ? hotZoneArr[i].textLabel.scaleX : 1,
+        imgScale: hotZoneArr[i].pic ? hotZoneArr[i].pic.scaleX : 1,
+        mapScale: this.mapScale
+      };
+      hotZoneItem['rect'] = hotZoneArr[i].getBoundingBox();
+      hotZoneItem['rect'].x = Math.round((hotZoneItem['rect'].x - bgItem['rect'].x) * 100) / 100;
+      hotZoneItem['rect'].y = Math.round((hotZoneItem['rect'].y - bgItem['rect'].y) * 100) / 100;
+      hotZoneItem['rect'].width = Math.round((hotZoneItem['rect'].width) * 100) / 100;
+      hotZoneItem['rect'].height = Math.round((hotZoneItem['rect'].height) * 100) / 100;
+
+
+      hotZoneItemArr.push(hotZoneItem);
+    }
+
+    console.log('hotZoneItemArr: ', hotZoneItemArr);
+
+    this.save.emit({bgItem, hotZoneItemArr});
+  }
+
+  private updatePos() {
+    this.hotZoneArr.forEach((item) => {
+      let x, y;
+      switch (item.itemType) {
+        case 'rect':
+          x = item.x;
+          y = item.y;
+          break;
+
+        case 'pic':
+          x = item.pic.x;
+          y = item.pic.y;
+          break;
+
+        case 'text':
+          x = item.textLabel.x;
+          y = item.textLabel.y;
+          break;
+      }
+
+      item.x = x;
+      item.y = y;
+      item.pic.x = x;
+      item.pic.y = y;
+      item.textLabel.x = x;
+      item.textLabel.y = y;
+    });
+  }
+
+  private setPicState(item: any) {
+    item.visible = false;
+    item.textLabel.visible = false;
+    item.pic.visible = true;
+  }
+
+  private setRectState(item: any) {
+    item.visible = true;
+    item.textLabel.visible = false;
+    item.pic.visible = false;
+  }
+
+  private setTextState(item: any) {
+    item.visible = false;
+    item.pic.visible = false;
+    item.textLabel.visible = true;
+  }
+
+  private clickedHotZoneRect(item: any) {
+    if (this.checkClickTarget(item)) {
+
+      if (item.lineDashFlag && this.checkClickTarget(item.arrow)) {
+        this.changeItemSize(item);
+      } else if (item.lineDashFlag && this.checkClickTarget(item.arrowTop)) {
+        this.changeItemTopSize(item);
+      } else if (item.lineDashFlag && this.checkClickTarget(item.arrowRight)) {
+        this.changeItemRightSize(item);
+      } else {
+        this.changeCurItem(item);
+      }
+      return;
+    }
+  }
+
+  private clickedHotZonePic(item: any) {
+    if (this.checkClickTarget(item)) {
+      this.curItem = item;
+    }
+  }
+
+  private clickedHotZoneText(item: any) {
+    if (this.checkClickTarget(item)) {
+      this.curItem = item;
+    }
+  }
+
+  saveText(item) {
+    item.textLabel.text = item.text;
+  }
+
+  private loadHotZonePic(pic: HotZoneImg, url) {
+    const baseLen = this.hotZoneImgSize * this.mapScale;
+    pic.load(url).then(() => {
+      const s = getMinScale(pic, baseLen);
+      pic.setScaleXY(s);
+    });
+  }
+}
diff --git a/form/src/app/common/lesson-title-config/lesson-title-config.component.html b/form/src/app/common/lesson-title-config/lesson-title-config.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..996dc651e4b301c3f940ae9cb200d14bc224a086
--- /dev/null
+++ b/form/src/app/common/lesson-title-config/lesson-title-config.component.html
@@ -0,0 +1,93 @@
+
+<div class="title-config">
+
+  <div class="title-wrap">
+    <div class="row" style="margin: 5px;">
+      <div class="p-content" style="width:100%">
+        <div class="p-tool-box d-flex" style="background: #fff;">
+          <nz-select class="ml-1" style="width: 120px;" [(ngModel)]="__fontFamily"
+                     (ngModelChange)="onChangeFontFamily($event)"
+                     nzPlaceHolder="Font Family"
+                     [nzDropdownMatchSelectWidth]="false">
+            <nz-option [nzValue]="font" nzCustomContent [nzLabel]="font" *ngFor="let font of fontFamilyList">
+              <span  [ngStyle]="{'font-family': font}" >{{font}}</span>
+            </nz-option>
+          </nz-select>
+          <nz-select class="ml-1" style="width: 110px;" [(ngModel)]="__fontSize"
+                     (ngModelChange)="onChangeFontSize()"
+                     nzPlaceHolder="Font Size">
+            <nz-option [nzValue]="i" [nzLabel]="'Size - ' + i" *ngFor="let i of fontSizeRange"></nz-option>
+          </nz-select>
+          <div class="p-divider"></div>
+          <div class="i-tool-font-btn d-flex mr-2">
+            <div class="position-relative fa-icon" (click)="onChangeBold()">
+<!--              <div class="fa fa-bold"></div>-->
+              <fa-icon icon="bold"></fa-icon>
+            </div>
+          </div>
+          <div class="i-tool-font-btn d-flex mr-2">
+            <div class="position-relative fa-icon" (click)="onChangeItalic()">
+<!--              <div class="fa fa-italic"></div>-->
+              <fa-icon icon="italic"></fa-icon>
+            </div>
+          </div>
+          <div class="i-tool-font-btn d-flex mr-2">
+            <div class="position-relative fa-icon" (click)="onChangeUnderline()">
+<!--              <div class="fa fa-underline"></div>-->
+              <fa-icon icon="underline"></fa-icon>
+            </div>
+          </div>
+          <div class="i-tool-font-btn d-flex">
+            <div class="position-relative fa-icon" (click)="onChangeStrikethrough()">
+<!--              <div class="fa fa-strikethrough"></div>-->
+              <fa-icon icon="strikethrough"></fa-icon>
+            </div>
+          </div>
+          <div class="p-divider"></div>
+          <div class="i-tool-font-color d-flex">
+            <div class="position-relative i-left flex-fill" (click)="onChangeFontColor($event)">
+<!--              <div class="fa fa-font"></div>-->
+              <fa-icon icon="palette"></fa-icon>
+              <div class="i-color" [style.background-color]="__fontColor"></div>
+            </div>
+            <div class="i-dropdown-menu" nzPlacement="bottom"
+                 nz-popover [(nzVisible)]="isShowFontColorPane" nzTrigger="click"
+                 [nzContent]="colorPane">
+              <i nz-icon type="down" theme="outline"></i>
+            </div>
+          </div>
+          <div class="p-divider"></div>
+          <div style="background: #fff;display: block;">
+            <div class="position-relative">
+              <app-audio-recorder [audioUrl]="titleObj && titleObj.audio_url" (audioUploaded)="titleAudioUploaded($event)"></app-audio-recorder>
+            </div>
+          </div>
+        </div>
+
+        <div class="width-100 d-flex">
+          <iframe #titleEl id="titleContentEgret"  frameborder="0" style="overflow: hidden;width: 100%; height:48px; margin: 0; padding: 0;"></iframe>
+        </div>
+      </div>
+
+      <ng-template #colorPane>
+        <div class="p-color-pane" nz-row nzGutter="16" nzType="flex">
+          <div nz-col class="p-color-item" *ngFor="let color of colorList"
+               [style.background-color]="color"
+               (click)="onSelectColor(color)"
+          >
+          </div>
+        </div>
+      </ng-template>
+
+    </div>
+
+
+
+
+
+
+  </div>
+
+
+
+</div>
diff --git a/form/src/app/common/lesson-title-config/lesson-title-config.component.scss b/form/src/app/common/lesson-title-config/lesson-title-config.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..d8dca5324cc4a7d988176d83f887b9feaa3dffdf
--- /dev/null
+++ b/form/src/app/common/lesson-title-config/lesson-title-config.component.scss
@@ -0,0 +1,365 @@
+@import '../../style/common_mixin.css';
+
+.title-config {
+  .letter-wrap{
+    width: 3rem;
+    flex: 0 0 3rem;
+  }
+  .str-wrap{
+    margin-left: 1rem;flex: 1 1;
+  }
+  .type-row{
+    margin: 0;padding-top: 1rem;
+  }
+
+}
+@font-face
+{
+  font-family: 'BRLNSDB';
+  src: url("../../../assets/font/BRLNSDB.TTF") ;
+}
+
+@font-face
+{
+  font-family: 'BRLNSB';
+  src: url("../../../assets/font/BRLNSB.TTF") ;
+}
+
+@font-face
+{
+  font-family: 'BRLNSR';
+  src: url("../../../assets/font/BRLNSR.TTF") ;
+}
+
+@font-face
+{
+  font-family: 'GOTHIC';
+  src: url("../../../assets/font/GOTHIC.TTF") ;
+}
+
+@font-face
+{
+  font-family: 'GOTHICB';
+  src: url("../../../assets/font/GOTHICB.TTF") ;
+}
+
+@font-face
+{
+  font-family: 'GOTHICBI';
+  src: url("../../../assets/font/GOTHICBI.TTF") ;
+}
+
+@font-face
+{
+  font-family: 'GOTHICI';
+  src: url("../../../assets/font/GOTHICI.TTF") ;
+}
+
+@font-face
+{
+  font-family: 'MMTextBook';
+  src: url("../../../assets/font/MMTextBook.otf") ;
+}
+
+@font-face
+{
+  font-family: 'MMTextBook-Bold';
+  src: url("../../../assets/font/MMTextBook-Bold.otf") ;
+}
+
+@font-face
+{
+  font-family: 'MMTextBook-BoldItalic';
+  src: url("../../../assets/font/MMTextBook-BoldItalic.otf") ;
+}
+
+@font-face
+{
+  font-family: 'MMTextBook-Italic';
+  src: url("../../../assets/font/MMTextBook-Italic.otf") ;
+}
+@mixin tool-btn {
+  border: 1px solid #ddd;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  height: 100%;
+  outline: none;
+  border-radius: 6px;
+  color: #555;
+}
+.d-flex{
+  display: flex;
+}
+
+.position-relative {
+  position: relative;
+}
+ .flex-fill {
+   -webkit-box-flex: 1;
+   flex: 1 1 auto;
+   justify-content: center;
+   display: flex;
+ }
+
+.i-dropdown-menu{
+  width: 15px;
+  font-size: 10px;
+  border-left: 1px solid #ddd;
+  display: -webkit-box;
+  display: flex;
+  -webkit-box-align: center;
+  align-items: center;
+  flex: 0
+}
+.p-title-box .p-title {
+    font-size: 20px;
+  }
+.p-title-box input {
+    width: 300px;
+    margin-left: 10px;
+  }
+
+.p-content {
+  border: 1px solid #ddd;
+  box-shadow: 0 0 8px #eee;
+  margin-top: 10px;
+}
+
+.p-divider {
+  border-left: 1px solid #ccc;
+  margin: 5px 8px;
+  height: 85%;
+}
+
+.p-tool-box {
+  background-color: #efefef;
+  padding: 2px;
+  height: 37px;
+  display: flex;
+  align-items: center;
+  border-bottom: 1px solid #ddd;
+
+
+
+}
+// save
+.i-tool-save {
+  //@include tool-btn();
+  color: white;
+}
+.i-tool-save:disabled {
+  color: #555;
+}
+
+// font-size
+.i-tool-font-size {
+  //@include tool-btn();
+  width: 37px;
+
+}
+.i-tool-font-size:hover {
+  color: black;
+  border-color: #bbb;
+}
+
+// font-color
+.i-tool-font-color, .i-tool-font-btn {
+  border: 1px solid #ddd;
+  //padding: 3px 7px;
+  border-radius: 6px;
+  width: 45px;
+  height: 31px;
+  background-color: white;
+  color: #555;
+  ::ng-deep > span {
+    display: flex;
+    position: absolute;
+    left: 0;
+    right: 0;
+    top: 0;
+    bottom: 0;
+    padding: 3px 7px;
+  }
+  .i-left {
+    .fa-font,.fa-bold,.fa-italic,.fa-strikethrough, .fa-underline {
+      font-size: 10px;
+      position: absolute;
+      color: #555;
+      left: 8px;
+      top: 7px;
+    }
+    .i-color {
+      width: 68%;
+      height: 5px;
+      background-color: black;
+      position: absolute;
+      top: 21px;
+      left: 5px;
+    }
+  }
+  .i-dropdown-menu {
+    width: 15px;
+    font-size: 10px;
+    border-left: 1px solid #ddd;
+    display: flex;
+    align-items: center;
+    .anticon-down {
+      transform: scale(0.6);
+    }
+  }
+}
+.i-tool-font-btn{
+  width: 31px;
+}
+.fa-icon{
+  width: 100%;
+  height: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  cursor: pointer;
+}
+// bg-color
+.i-tool-bg-color {
+  @include tool-btn();
+  padding: 0 9px;
+  ::ng-deep > span {
+    display: flex;
+    align-items: center;
+  }
+
+  .i-color {
+    display: block;
+    width: 16px;
+    height: 16px;
+    background-color: white;
+    margin-left: 10px;
+  }
+}
+
+
+// horizontal-center
+.i-tool-horizontal-center {
+  @include tool-btn();
+  width: 37px;
+}
+.p-box {
+  width: 1280px;
+  height: 720px;
+  left: 0;
+  top: 0;
+  transform-origin: top left;
+  overflow: hidden;
+}
+
+
+
+.p-animation-index-box {
+  .i-animation-index {
+    position: absolute;
+    font-size: 34px;
+    font-family: Arial;
+    border: 1px solid #ddd;
+    border-radius: 4px;
+    min-width: 50px;
+    min-height: 50px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    background-color: darkorange;
+    color: white;
+  }
+}
+
+.p-edit {
+  background-color: white;
+  border: 1px solid black;
+  padding: 2px 15px;
+}
+
+.p-selected {
+  border: 1px solid darkorange;
+  box-shadow: 0 0 8px #ddd;
+}
+
+.p-tool-item-disable {
+  color: rgba(0, 0, 0, .25) !important;
+  background-color: transparent !important;
+  pointer-events: none;
+}
+
+// -----------
+.p-color-pane {
+  width: 80px;
+  .p-color-item {
+    width: 17px;
+    height: 17px;
+    cursor: pointer;
+    margin: 4px;
+    border: 1px solid #bbb;
+  }
+  .p-color-item-active {
+    border: 1px solid white;
+    transform: scale(1.1);
+  }
+}
+
+.p-user-guide {
+  font-size: 40px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.p-animation-box {
+  width: 400px;
+  background-color: #efefef;
+  border-left: 1px solid #ddd;
+  padding: 10px;
+  text-align: center;
+
+  .p-animation-label {
+    z-index: 1;
+    position: relative;
+  }
+
+  ::ng-deep .ant-radio-button-wrapper {
+    padding: 0 10px;
+  }
+
+  .i-toolbox {
+    & > div {
+      font-size: 20px;
+      cursor: pointer;
+      color: #555;
+      padding: 0 3px;
+      margin: 0 5px;
+    }
+    & > div:hover {
+      color: #1585ff;
+      transform: scale(1.1);
+    }
+  }
+
+  .p-animation-list {
+    background-color: white;
+    border-radius: 10px;
+    height: calc(100% - 86px);
+    overflow-y: auto;
+    .p-animation-item {
+      padding: 0 20px;
+      background-color: aliceblue;
+      margin-top: 6px;
+      border-top: 1px solid #ddd;
+      border-bottom: 1px solid #ddd;
+      cursor: pointer;
+      text-align: left;
+      display: flex;
+    }
+    .i-active {
+      background-color: antiquewhite;
+    }
+  }
+}
+
diff --git a/form/src/app/common/lesson-title-config/lesson-title-config.component.ts b/form/src/app/common/lesson-title-config/lesson-title-config.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6b7601fbea4baa9a2ea8f4a4cd5baed833259249
--- /dev/null
+++ b/form/src/app/common/lesson-title-config/lesson-title-config.component.ts
@@ -0,0 +1,339 @@
+
+import {
+  Component,
+  ElementRef,
+  EventEmitter,
+  Input,
+  OnChanges,
+  OnDestroy,
+  OnInit,
+  Output,
+  ViewChild
+} from '@angular/core';
+
+const editorTpl = `<html lang="en"><head><meta charset="utf-8">
+  <meta name="viewport"
+          content="width=device-width,initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no"/>
+        <style>
+@font-face{
+  font-family: 'BRLNSDB';
+  src: url("../../../assets/font/BRLNSDB.TTF") ;
+}
+
+@font-face{
+  font-family: 'BRLNSB';
+  src: url("../../../assets/font/BRLNSB.TTF") ;
+}
+
+@font-face{
+  font-family: 'BRLNSR';
+  src: url("../../../assets/font/BRLNSR.TTF") ;
+}
+
+@font-face{
+  font-family: 'GOTHIC';
+  src: url("../../../assets/font/GOTHIC.TTF") ;
+}
+
+@font-face{
+  font-family: 'GOTHICB';
+  src: url("../../../assets/font/GOTHICB.TTF") ;
+}
+
+@font-face{
+  font-family: 'GOTHICBI';
+  src: url("../../../assets/font/GOTHICBI.TTF") ;
+}
+
+@font-face{
+  font-family: 'GOTHICI';
+  src: url("../../../assets/font/GOTHICI.TTF") ;
+}
+
+@font-face{
+  font-family: 'MMTextBook';
+  src: url("../../../assets/font/MMTextBook.otf") ;
+}
+
+@font-face{
+  font-family: 'MMTextBook-Bold';
+  src: url("../../../assets/font/MMTextBook-Bold.otf") ;
+}
+
+@font-face{
+  font-family: 'MMTextBook-BoldItalic';
+  src: url("../../../assets/font/MMTextBook-BoldItalic.otf") ;
+}
+
+@font-face{
+  font-family: 'MMTextBook-Italic';
+  src: url("../../../assets/font/MMTextBook-Italic.otf") ;
+}
+html, body{
+/*font-size: 30px;*/
+}
+body{
+    height:48px;
+
+  overflow: hidden;
+  margin: 0;
+  padding: 0 .5rem;
+  font-family: 'BRLNSB, BRLNSDB, BRLNSR, GOTHIC, GOTHICB, MMTextBook';
+  background: #FFF;
+  line-height: 48px;
+}
+</style>
+  </head>
+  <body>{{content}}</body>
+</html>`;
+
+@Component({
+  selector: 'app-lesson-title-config',
+  templateUrl: './lesson-title-config.component.html',
+  styleUrls: ['./lesson-title-config.component.scss']
+})
+export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy, OnChanges {
+
+  fontFamilyList = [
+    'Arial',
+    'BRLNSB',
+    'BRLNSDB',
+    'BRLNSR',
+    'GOTHIC',
+    'GOTHICB',
+    // "GOTHICBI",
+    // "GOTHICI",
+    'MMTextBook',
+    // "MMTextBook-Bold",
+    // "MMTextBook-Italic",
+    // "MMTextBook-BoldItalic",
+  ];
+
+
+
+  colorList = [
+    '#000000',
+    '#ffffff',
+    '#595959',
+    '#0075c2',
+    '#c61c1e',
+    '#9cbc3a',
+    '#008000',
+    '#FF0000',
+    '#D2691E',
+  ];
+  MIN_FONT_SIZE = 1;
+  MAX_FONT_SIZE = 7;
+  isShowFontColorPane = false;
+  isShowBGColorPane = false;
+  fontSizeRange = [
+    // {name: '1号', value: 9},
+    // {name: '2号', value: 13},
+    // {name: '3号', value: 16},
+    // {name: '4号', value: 18},
+    // {name: '5号', value: 24},
+    // {name: '6号', value: 32},
+
+  ];
+
+  editorContent = '';
+
+
+  __fontFamily = 'Arial';
+  __fontColor = '';
+  __fontSize = 3;
+
+  loopCnt = 0;
+  maxLoops = 20;
+
+  @ViewChild('titleEl', {static: true}) titleEl: ElementRef;
+  titleEW = null;
+
+
+  @Input()
+  titleObj = {
+    content: '',
+    audio_url: ''
+  };
+
+
+  @Output()
+  titleUpdated = new EventEmitter();
+
+  constructor() {
+    this.fontSizeRange = [];
+    for (let i = this.MIN_FONT_SIZE; i <= this.MAX_FONT_SIZE; ++i) {
+      this.fontSizeRange.push(i);
+    }
+    this.__fontSize = 3;
+    this.__fontColor = this.colorList[0];
+
+  }
+  ngOnChanges(vars) {
+    if (!vars.titleObj.previousValue) {// 初始化,内容是空
+      return;
+    }
+    let defObj = this.titleObj;
+    if (!vars.titleObj.currentValue) {
+      defObj = {
+        content: '',
+        audio_url: ''
+      };
+    } else {
+      defObj = vars.titleObj.currentValue;
+    }
+    this.titleObj.content = defObj.content || '';
+    this.titleObj.audio_url = defObj.audio_url || '';
+    this.titleEW.document.body.innerHTML = this.titleObj.content;
+  }
+
+  ngOnInit() {
+    if (!this.titleObj) {
+      this.titleObj = {
+        content: '',
+        audio_url: ''
+      };
+    }
+    this.titleObj.content = this.titleObj.content || '';
+    this.titleObj.audio_url = this.titleObj.audio_url || '';
+
+    this.editorContent = editorTpl.replace('{{content}}', this.titleObj.content) ;
+    this.titleEW = this.titleEl.nativeElement.contentWindow;
+    console.log('this.titleEW', this.titleEW);
+    const tdoc = this.titleEW.document;
+    tdoc.designMode = 'on';
+    tdoc.open('text/html', 'replace');
+    tdoc.write(this.editorContent);
+    tdoc.close();
+    tdoc.addEventListener('keypress', this.keyPress, true);
+    tdoc.addEventListener('blur', () => {
+      if (this.titleObj.content === this.titleEW.document.body.innerHTML.trim()) {
+        return;
+      }
+      this.shouldSave();
+    }, true);
+  }
+  htmlEncode(text) {
+    if (!text) {
+      return '';
+    }
+    return text.replace(/\&/ig, '&amp;')
+      .replace(/\</ig, '&lt;')
+      .replace(/\>/ig, '&gt;')
+      .replace(/\"/ig, '&quot;');
+  }
+
+  htmlDecode(text) {
+    if (!text) {
+      return '';
+    }
+    return text.replace(/\&amp\;/ig, '&')
+      .replace(/\&lt\;/ig, '<')
+      .replace(/\&gt\;/ig, '>')
+      .replace(/\&quot\;/ig, '"');
+  }
+
+
+  ngOnDestroy(): void {
+
+  }
+
+  keyPress(evt) {
+    try {
+
+      if (evt.charCode === 13) {
+        evt.preventDefault();
+        evt.stopPropagation();
+        return;
+      }
+      if (evt.ctrlKey) {
+        const key = String.fromCharCode(evt.charCode).toLowerCase();
+        let cmd = '';
+        switch (key) {
+          case 'b': cmd = 'bold'; break;
+          case 'i': cmd = 'italic'; break;
+          case 'u': cmd = 'underline'; break;
+        }
+
+
+        if (cmd) {
+          this.execEditorCommand(cmd);
+
+          // stop the event bubble
+          evt.preventDefault();
+          evt.stopPropagation();
+        }
+      }
+    } catch (e) {
+      console.log(1, e);
+      alert(e);
+    }
+  }
+
+  execEditorCommand(command, option?: any) {
+    console.log('sssss');
+    try {
+      this.titleEW.focus();
+      const result = this.titleEW.document.execCommand(command, false, option);
+      console.log(result);
+      this.loopCnt = 0;
+
+      return false;
+    } catch (e) {
+      alert(e);
+      if (this.loopCnt < this.maxLoops) {
+        setTimeout(() => {
+          this.execEditorCommand(command, option);
+        }, 100);
+        this.loopCnt += 1;
+      } else {
+        alert('Error executing command.');
+      }
+    }
+  }
+
+  onSelectColor(color) {
+    this.execEditorCommand('forecolor', color);
+    this.__fontColor = color;
+  }
+  onChangeFontColor(val) {
+    this.execEditorCommand('forecolor', this.__fontColor);
+  }
+  onChangeFontFamily(font) {
+    this.execEditorCommand('fontName', font);
+  }
+  onChangeFontSize(size?: any) {
+
+    if (size) {
+      size += this.__fontSize;
+    } else {
+      size = this.__fontSize;
+    }
+    size = Math.max(this.MIN_FONT_SIZE, size);
+    size = Math.min(this.MAX_FONT_SIZE, size);
+    this.execEditorCommand('fontsize', size);
+  }
+  onChangeBold() {
+    this.execEditorCommand('bold');
+  }
+  onChangeItalic() {
+    this.execEditorCommand('italic');
+  }
+  onChangeUnderline() {
+    this.execEditorCommand('underline');
+  }
+  onChangeStrikethrough() {
+    this.execEditorCommand('strikethrough');
+  }
+  titleAudioUploaded(res) {
+    this.titleObj.audio_url = res.url;
+    this.titleUpdated.emit(this.titleObj);
+  }
+  shouldSave = () => {
+    console.log('title shouldSave',  this.titleObj);
+    this.titleObj.content = this.titleEW.document.body.innerHTML.trim();
+    this.titleUpdated.emit(this.titleObj);
+  }
+}
+
+
diff --git a/form/src/app/common/player-content-wrapper/player-content-wrapper.component.html b/form/src/app/common/player-content-wrapper/player-content-wrapper.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..864df8e812e3a5f90261869b915ee1e0767ba648
--- /dev/null
+++ b/form/src/app/common/player-content-wrapper/player-content-wrapper.component.html
@@ -0,0 +1,8 @@
+<div class="cmp-player-content-wrapper" #wrapperEl>
+  <div class="cmp-cnt-box"> 
+    <img [src]="'assets/'+ratio+'.png'" alt="">
+    <div class="cmp-cnt-main">
+      <ng-content></ng-content>
+    </div>
+  </div>
+</div>
\ No newline at end of file
diff --git a/form/src/app/common/player-content-wrapper/player-content-wrapper.component.scss b/form/src/app/common/player-content-wrapper/player-content-wrapper.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..6aec336fb2e4155b1375abdd53191d4f23dc900e
--- /dev/null
+++ b/form/src/app/common/player-content-wrapper/player-content-wrapper.component.scss
@@ -0,0 +1,54 @@
+@import '../../style/common_mixin.css';
+
+.cmp-player-content-wrapper{
+  max-height: 100%;
+  display: block;
+  position: relative;
+  height: 100%;
+  .cmp-cnt-box{
+    display: inline-block;
+    max-width: 100%;
+    max-height: 100%;
+    width: auto;
+    height: 100%;
+    position: relative;
+    img{
+      height: 100%;
+      width: auto;
+    }
+
+  }
+  .cmp-cnt-main{
+    position: absolute;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    display: flex;
+    flex-direction: column;
+  }
+}
+
+
+[style*="--aspect-ratio"] > :first-child {
+  width: 100%;
+}
+[style*="--aspect-ratio"] > img {
+  height: auto;
+}
+@supports (--custom:property) {
+  [style*="--aspect-ratio"] {
+    position: relative;
+  }
+  [style*="--aspect-ratio"]::before {
+    content: "";
+    display: block;
+    padding-bottom: calc(100% / (var(--aspect-ratio)));
+  }
+  [style*="--aspect-ratio"] > :first-child {
+    position: absolute;
+    top: 0;
+    left: 0;
+    height: 100%;
+  }
+}
diff --git a/form/src/app/common/player-content-wrapper/player-content-wrapper.component.ts b/form/src/app/common/player-content-wrapper/player-content-wrapper.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0780a42be855a6e3fff637a01a0f4d0002e20e4a
--- /dev/null
+++ b/form/src/app/common/player-content-wrapper/player-content-wrapper.component.ts
@@ -0,0 +1,55 @@
+import {
+  AfterViewInit,
+  Component,
+  ElementRef,
+  Input,
+  OnChanges,
+  OnDestroy,
+  OnInit,
+  ViewChild
+} from '@angular/core';
+
+
+@Component({
+  selector: 'app-player-content-wrapper',
+  templateUrl: './player-content-wrapper.component.html',
+  styleUrls: ['./player-content-wrapper.component.scss']
+})
+export class PlayerContentWrapperComponent implements OnInit, OnChanges, OnDestroy, AfterViewInit {
+
+
+  @ViewChild('wrapperEl', {static: true }) wrapperEl: ElementRef;
+
+  // // aspect ratio?
+  @Input() ratio;
+  _w: string;
+  _h: string;
+
+  constructor() {
+    if (window.innerHeight < window.innerWidth) {
+      this._h = '100%';
+      this._w = 'auto';
+    } else {
+      this._w = '100%';
+      this._h = 'auto';
+    }
+  }
+  ngOnInit() {
+    if (!this.ratio) {
+      this.ratio = '20-9';
+    }
+
+
+  }
+  ngOnChanges() {
+
+  }
+  ngOnDestroy(): void {
+
+  }
+
+  ngAfterViewInit() {
+
+  }
+}
+
diff --git a/form/src/app/common/upload-image-with-preview/upload-image-with-preview.component.html b/form/src/app/common/upload-image-with-preview/upload-image-with-preview.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..b5c26bb34504cb78bc873f9e811802831b5f348e
--- /dev/null
+++ b/form/src/app/common/upload-image-with-preview/upload-image-with-preview.component.html
@@ -0,0 +1,37 @@
+<div class="position-relative">
+  <nz-upload class="p-image-uploader" [nzDisabled]="disableUpload"
+             [nzShowUploadList]="false"
+             nzAccept = "image/*"
+             [nzAction]="uploadUrl"
+             [nzData]="uploadData"
+              (nzChange)="handleChange($event)">
+             <!--[nzBeforeUpload]="customUpload">-->
+    <div class="p-box d-flex align-items-center">
+      <div class="p-upload-icon" *ngIf="!picUrl && !uploading">
+        <i nz-icon nzType="cloud-upload" nzTheme="outline" [style.font-size]="iconSize + 'em'"></i>
+        <div class="m-3"></div>
+        <span>{{TIP}}</span>
+        <!--<div class="mt-5 p-progress-bar" *ngIf="uploading">-->
+        <!--<div class="p-progress-bg" [style.width]="progress*0.2+'rem'"></div>-->
+        <!--<div class="p-progress-value">{{progress}}%</div>-->
+        <!--</div>-->
+      </div>
+      <div class="p-upload-progress-bg" *ngIf="uploading">
+        <div class="i-bg" [style.width]="progress+'%'"></div>
+        <div class="i-text">
+          <fa-icon icon="cloud-upload-alt"></fa-icon>
+          Uploading...
+        </div>
+      </div>
+      <div class="p-preview" *ngIf="!uploading && picUrl "
+           [style.background-image]="picUrl | backgroundImage ">
+      </div>
+    </div>
+  </nz-upload>
+  <div class="p-btn-delete" *ngIf="canDelete"
+       nz-popconfirm nzTitle="Are you sure ?"
+       (nzOnConfirm)="onDelete()"
+  >
+    <i nz-icon nzType="close" nzTheme="outline"></i>
+  </div>
+</div>
diff --git a/form/src/app/common/upload-image-with-preview/upload-image-with-preview.component.scss b/form/src/app/common/upload-image-with-preview/upload-image-with-preview.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..98dad112e7375a490e4481a61439e04462c3f85d
--- /dev/null
+++ b/form/src/app/common/upload-image-with-preview/upload-image-with-preview.component.scss
@@ -0,0 +1,106 @@
+@import '../../style/common_mixin.css';
+
+.p-image-uploader {
+  position: relative;
+  display: block;
+  width: 100%;
+  height: 0;
+  padding-bottom: 56.25%;
+  .p-box {
+    position: absolute;
+    left: 0;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    border: 2px dashed #ddd;
+    border-radius: 0.5rem;
+    background-color: #fafafa;
+    text-align: center;
+    color: #aaa;
+    .p-upload-icon {
+      text-align: center;
+      margin: auto;
+      .anticon-upload {
+        color: #888;
+        font-size: 5rem;
+      }
+      .p-progress-bar {
+        position: relative;
+        width: 20rem;
+        height: 1.5rem;
+        border: 1px solid #ccc;
+        border-radius: 1rem;
+        .p-progress-bg {
+          background-color: #1890ff;
+          border-radius: 1rem;
+          height: 100%;
+        }
+        .p-progress-value {
+          position: absolute;
+          top: 0;
+          left: 0;
+          width: 100%;
+          height: 100%;
+          text-shadow: 0 0 4px #000;
+          color: white;
+          text-align: center;
+          font-size: 0.9rem;
+          line-height: 1.5rem;
+        }
+      }
+    }
+    .p-preview {
+      width: 100%;
+      height: 100%;
+      background-size: contain;
+      background-repeat: no-repeat;
+      background-position: 50% 50%;
+      //background-image: url("https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png");
+    }
+  }
+  .d-flex{
+    display: flex;
+  }
+}
+
+.p-btn-delete {
+  position: absolute;
+  right: -0.5rem;
+  top: -0.5rem;
+  width: 2rem;
+  height: 2rem;
+  border: 0.2rem solid white;
+  border-radius: 50%;
+  font-size: 1.2rem;
+  background-color: #fb781a;
+  color: white;
+  text-align: center;
+}
+
+.p-upload-progress-bg {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  & .i-text {
+    position: absolute;
+    text-align: center;
+    color: white;
+    text-shadow: 0 0 2px rgba(0, 0, 0, .85);
+  }
+  & .i-bg {
+    position: absolute;
+    left: 0;
+    top: 0;
+    height: 100%;
+    background-color: #27b43f;
+    border-radius: 0.5rem;
+  }
+}
+
+
+:host ::ng-deep .ant-upload {
+  display: block;
+}
diff --git a/form/src/app/common/upload-image-with-preview/upload-image-with-preview.component.ts b/form/src/app/common/upload-image-with-preview/upload-image-with-preview.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f4694a1f1ae4b582b3c121e601c6322437888820
--- /dev/null
+++ b/form/src/app/common/upload-image-with-preview/upload-image-with-preview.component.ts
@@ -0,0 +1,158 @@
+import {Component, EventEmitter, Input, OnChanges, OnDestroy, Output} from '@angular/core';
+import { NzMessageService, UploadXHRArgs, UploadFile } from 'ng-zorro-antd';
+
+
+@Component({
+  selector: 'app-upload-image-with-preview',
+  templateUrl: './upload-image-with-preview.component.html',
+  styleUrls: ['./upload-image-with-preview.component.scss']
+})
+export class UploadImageWithPreviewComponent implements OnDestroy, OnChanges {
+  uploading = false;
+  progress = 0;
+  @Input()
+  picUrl;
+  @Input()
+  canDelete = false;
+  @Output()
+  imageUploaded = new EventEmitter();
+  @Output()
+  imageUploadFailure = new EventEmitter();
+  @Output()
+  delete = new EventEmitter();
+  @Input()
+  picItem = null;
+  @Input()
+  iconSize = 2;
+  @Input()
+  TIP = 'Click here to upload image';
+  @Input()
+  disableUpload = false;
+
+  uploadUrl;
+  uploadData;
+
+  constructor(private nzMessageService: NzMessageService) {
+
+    this.uploadUrl = (<any> window).courseware.uploadUrl();
+    this.uploadData = (<any> window).courseware.uploadData();
+
+    window['air'].getUploadCallback = (url, data) => {
+      this.uploadUrl = url;
+      this.uploadData = data;
+    };
+
+  }
+  ngOnChanges() {
+    if (!this.picItem) {
+      return;
+    }
+  }
+
+
+
+
+
+  handleChange(info: { type: string, file: UploadFile, event: any }): void {
+
+    console.log('info:' , info);
+
+    switch (info.type) {
+      case 'start':
+        // this.isUploading = true;
+        // this.progress = 0;
+
+        this.picUrl = null;
+        // this.beforeUpload(item.file as any);
+        if (!this.checkSelectFile(info.file)) {
+          return;
+        }
+        this.uploading = true;
+        this.progress = 0;
+
+        break;
+
+      case 'success':
+        // this.isUploading = false;
+        // this.uploadSuccess(info.file.response);
+        // this.audioUploaded.emit(info.file.response);
+
+        this.uploadSuccess(info.file);
+
+        break;
+      case 'progress':
+        this.progress = parseInt(info.event.percent, 10);
+        this.doProgress(this.progress);
+        break;
+    }
+  }
+
+  onDelete() {
+    this.delete.emit();
+  }
+  checkSelectFile(file) {
+    const isImg = ['image/jpeg', 'image/png', 'image/jpeg', 'image/gif', 'image/bmp'].includes(file.type);
+    if (!isImg) {
+      this.nzMessageService.error('You can only upload Image file (jpg|gif|png|bmp)');
+      return false;
+    }
+    const isGif = !['image/jpeg', 'image/png', 'image/jpeg', 'image/bmp'].includes(file.type);
+    const delta = isGif ? 20 : 5;
+    const isOverSize = file.size / 1024 / 1024 < delta;
+    if (!isOverSize) {
+      this.nzMessageService.error(`${isGif ? 'Gif' : 'Image'} must smaller than ${delta}MB!`);
+      return false;
+    }
+    return true;
+  }
+
+  uploadSuccess = (file) => {
+    this.nzMessageService.info('Upload Success');
+    this.uploading = false;
+    this.picUrl = file.response.url ;
+    // this.uploadFinished(url);
+    // if (!inOSS) {
+    const img = new Image();
+    img.addEventListener('load', () => {
+
+      const height = img.naturalHeight;
+      const width = img.naturalWidth;
+      file['height'] = height;
+      file['width'] = width;
+      img.remove();
+      this.imageUploaded.emit(file.response);
+      // this.resService.updateImage(id, {width, height}).then( () => {
+      //   this.imageUploaded.emit({res_id: id, id, name, hash, url});
+      // });
+
+    });
+    img.src = file.response.url;
+    // } else {
+    //   this.imageUploaded.emit({res_id: id, id, name, hash, url});
+    // }
+  }
+
+  uploadFailure = (err, file) => {
+    this.uploading = false;
+    if (err.name && err.name === 'cancel') {
+      return;
+    }
+    console.log(err);
+    this.nzMessageService.error('Upload Error ' + err.message);
+    this.imageUploadFailure.emit(file);
+  }
+
+  doProgress = function(p) {
+    if (p > 1) {
+      p = 1;
+    }
+    if (p < 0) {
+      p = 0;
+    }
+    // console.log(Math.floor(p * 100));
+    this.progress =  Math.floor(p * 100);
+  }
+  ngOnDestroy() {
+  }
+
+}
diff --git a/form/src/app/common/upload-video/upload-video.component.html b/form/src/app/common/upload-video/upload-video.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..8f1533993f2bdcf3d5f3e0cd4337f3fa96a1ebd7
--- /dev/null
+++ b/form/src/app/common/upload-video/upload-video.component.html
@@ -0,0 +1,94 @@
+<div class="p-video-box">
+<div class="up-video" style="display: flex;">
+  <!--<nz-upload class="" [nzDisabled]="!showUploadBtn"-->
+               <!--[nzShowUploadList]="false"-->
+               <!--[nzBeforeUpload]="beforeUpload"-->
+               <!--nzAccept = ".mp4"-->
+               <!--style="margin-right: 1rem">-->
+
+    <nz-upload class="p-image-uploader" [nzDisabled]="uploading"
+               [nzShowUploadList]="false"
+               nzAccept = ".mp4"
+               [nzAction]="uploadUrl"
+               [nzData]="uploadData"
+               (nzChange)="handleChange($event)"
+               style="margin-right: 1rem">
+
+      <button type="button" nz-button nzType="default" *ngIf="showUploadBtn" [disabled]="uploading"
+              [nzLoading]="uploading" >
+        <i nz-icon nzType="plus" nzTheme="outline"></i>
+        <span>{{ uploading ? 'Uploading' : 'Select Video' }}</span>
+        <!--<span>Select Video</span>-->
+      </button>
+
+
+
+
+    </nz-upload>
+  <!--<button nz-button nzType="primary"  *ngIf="showUploadBtn"-->
+            <!--style="margin-right: 1rem"-->
+            <!--type="button"-->
+            <!--[nzLoading]="uploading"-->
+            <!--(click)="handleUpload()"-->
+            <!--[disabled]="!videoFile || uploading">-->
+      <!--<i nz-icon type="upload" theme="outline"></i>-->
+      <!--<span>{{ uploading ? 'Uploading' : 'Start Upload' }}</span>-->
+    <!--</button>-->
+
+    <!--
+    <button type="button"
+            (click)="extraCover()"
+            nz-button nzType="default"
+            [disabled]=" !(videoUrl && videoUrl.constructor.name === 'String')">设置封面</button>
+    -->
+    <!--
+   <nz-divider></nz-divider>
+
+   <div [style.display]="!uploading?'none':''" style="position:relative">
+     <div style="width: calc( 100% - 20px);">
+       <nz-progress [nzPercent]="progress">
+       </nz-progress>
+     </div>
+     <i (click)="cancelUpload()" class="anticon anticon-close-circle" style="position: absolute;top: 50%;right: 0;transform: translateY(-50%);cursor: pointer"></i>
+   </div>
+   -->
+
+
+</div>
+<div class="p-box d-flex align-items-center p-video-uploader">
+  <div class="p-upload-icon" *ngIf="!showUploadBtn && !videoUrl && !uploading">
+    <i nz-icon nzType="upload" nzTheme="outline"></i>
+    <div class="m-3"></div>
+    <span>Click here to upload video</span>
+    <div class="mt-5 p-progress-bar" *ngIf="uploading">
+      <div class="p-progress-bg" [style.width]="progress*0.2+'rem'"></div>
+      <div class="p-progress-value">{{progress}}%</div>
+    </div>
+  </div>
+  <div class="p-upload-progress-bg dddd " *ngIf="uploading"
+       [ngClass]="{'smart-bar': showUploadBtn}" >
+    <div class="i-bg" [style.width]="progress+'%'"></div>
+    <div class="i-text">
+      <fa-icon icon="cloud-upload-alt"></fa-icon>
+      Uploading...
+    </div>
+  </div>
+  <div class="p-upload-check-bg" *ngIf="checking">
+    <div class="i-bg" [style.width]="progress+'%'"></div>
+    <div class="i-text">
+      <fa-icon icon="cloud-upload-alt"></fa-icon>
+      <i nz-icon nzType="loading" nzTheme="outline"></i>Checking...
+    </div>
+  </div>
+  <div class="p-preview" *ngIf="!uploading && videoUrl " >
+    <!--<video crossorigin="anonymous" [src]="videoUrl" controls #videoNode></video>-->
+    <video [src]="safeVideoUrl(videoUrl)" controls #videoNode></video>
+  </div>
+</div>
+<div [style.display]="!checkVideoExists?'none':''">
+  <span><i nz-icon nzType="loading" nzTheme="outline"></i>  checking file to upload</span>
+</div>
+</div>
+
+
+
diff --git a/form/src/app/common/upload-video/upload-video.component.scss b/form/src/app/common/upload-video/upload-video.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..3e0fc1ce96f94d17a3d5da5af6a1e88824c08f09
--- /dev/null
+++ b/form/src/app/common/upload-video/upload-video.component.scss
@@ -0,0 +1,152 @@
+@import '../../style/common_mixin.css';
+/*.p-video-box{
+  bottom: 0;
+  border: 2px dashed #ddd;
+  border-radius: 0.5rem;
+  background-color: #fafafa;
+  text-align: center;
+  color: #aaa;
+  padding-top: 56.25%;
+  //font-size: 4rem;
+  position: relative;
+  .p-upload-icon{
+    left: 50%;
+    position: absolute;
+    top: 50%;
+    transform: translate(-50% ,-50%);
+  }
+}*/
+.p-video-uploader {
+  position: relative;
+  display: block;
+  width: 100%;
+  height: 0;
+  padding-bottom: 56.25%;
+  .p-box {
+    position: absolute;
+    left: 0;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    border: 2px dashed #ddd;
+    //border-radius: 0.5rem;
+    background-color: #fafafa;
+    text-align: center;
+    color: #aaa;
+
+
+  }
+}
+.p-upload-icon {
+  text-align: center;
+  margin: auto;
+
+}
+.p-upload-icon .anticon-upload {
+  color: #888;
+  font-size: 5rem;
+}
+p-progress-bar {
+  position: relative;
+  width: 20rem;
+  height: 1.5rem;
+  border: 1px solid #ccc;
+  border-radius: 1rem;
+
+}
+.p-progress-bar .p-progress-bg {
+  background-color: #1890ff;
+  border-radius: 1rem;
+  height: 100%;
+}
+.p-progress-bar .p-progress-value {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  text-shadow: 0 0 4px #000;
+  color: white;
+  text-align: center;
+  font-size: 0.9rem;
+  line-height: 1.5rem;
+}
+.p-preview {
+  width: 100%;
+  height: 100%;
+  //background-image: url("https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png");
+
+}
+.p-preview video{
+   max-height: 100%;
+   max-width: 100%;
+  position: absolute;
+  display: flex;
+ }
+.p-btn-delete {
+  position: absolute;
+  right: -0.5rem;
+  top: -0.5rem;
+  width: 2rem;
+  height: 2rem;
+  border: 0.2rem solid white;
+  border-radius: 50%;
+  font-size: 1.2rem;
+  background-color: #fb781a;
+  color: white;
+  text-align: center;
+}
+
+.p-upload-progress-bg, .p-upload-check-bg {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  & .i-text {
+    position: absolute;
+    text-align: center;
+    color: white;
+    text-shadow: 0 0 2px rgba(0, 0, 0, .85);
+  }
+  & .i-bg {
+    position: absolute;
+    left: 0;
+    top: 0;
+    height: 100%;
+    background-color: #1890ff;
+    //border-radius: 0.5rem 0 0 0.5rem;
+  }
+}
+.p-upload-progress-bg.smart-bar{
+  height: 50px!important;
+  margin-top: 20px!important;
+}
+.p-upload-check-bg {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  & .i-text {
+    position: absolute;
+    text-align: center;
+    color: white;
+    text-shadow: 0 0 2px rgba(0, 0, 0, .85);
+  }
+  & .i-bg {
+    position: absolute;
+    left: 0;
+    top: 0;
+    height: 100%;
+    background-color: #17a2b8;
+    //border-radius: 0.5rem 0 0 0.5rem;
+  }
+}
+
+
+//:host ::ng-deep .ant-upload {
+//  display: block;
+//}
diff --git a/form/src/app/common/upload-video/upload-video.component.ts b/form/src/app/common/upload-video/upload-video.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..013f8ad224a9a0bc09a015e2254b1cbaa4136bce
--- /dev/null
+++ b/form/src/app/common/upload-video/upload-video.component.ts
@@ -0,0 +1,211 @@
+import {Component, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, Output, SecurityContext, ViewChild} from '@angular/core';
+import {NzMessageService, UploadChangeParam, UploadFile, UploadFileStatus} from 'ng-zorro-antd';
+import {DomSanitizer, SafeResourceUrl} from '@angular/platform-browser';
+
+
+@Component({
+  selector: 'app-upload-video',
+  templateUrl: './upload-video.component.html',
+  styleUrls: ['./upload-video.component.scss']
+})
+export class UploadVideoComponent implements OnChanges, OnDestroy {
+  uploading = false;
+  checking = false;
+  checkVideoExists = false;
+  uploadClicked = false;
+
+  @Input()
+  videoFile = null;
+  uploaderInst = null;
+
+  progress = 0;
+  // @Input()
+  // setCovering = false;
+
+  @Input()
+  videoUrl = '';
+  @ViewChild('videoNode', {static: true })
+  videoNode: ElementRef;
+
+
+
+
+  // @Input()
+  // extractCoverFunc = null;
+
+  @Output()
+  videoUploaded = new EventEmitter();
+  @Output()
+  videoUploadFailure = new EventEmitter();
+  @Output()
+  extractVideoCover = new EventEmitter();
+
+
+  @Input()
+  showUploadBtn = true;
+  @Input()
+  item: any;
+  // videoItem = null;
+
+  uploadUrl;
+  uploadData;
+
+  constructor(private nzMessageService: NzMessageService,
+              private sanitization: DomSanitizer
+              // private cwService: _coursewareService,
+              // private resService: ResourceService
+              ) {
+    this.uploading = false;
+    this.videoFile = null;
+
+
+    this.uploadUrl = (<any> window).courseware.uploadUrl();
+    this.uploadData = (<any> window).courseware.uploadData();
+
+    window['air'].getUploadCallback = (url, data) => {
+
+      this.uploadUrl = url;
+      this.uploadData = data;
+    };
+
+  }
+  ngOnChanges() {
+    // if (!this.videoFile || this.showUploadBtn) {
+    //   return;
+    // }
+    // this.beforeUpload(this.videoFile);
+    // this.handleUpload();
+  }
+  ngOnDestroy(): void {
+    URL.revokeObjectURL(this.videoUrl);
+  }
+
+  safeVideoUrl(url) {
+    console.log(url);
+    return this.sanitization.bypassSecurityTrustResourceUrl(url); // `${url}`;
+  }
+  videoLoadedMetaData() {
+
+  }
+
+
+  handleChange(info: UploadChangeParam/* { type: string, file: UploadFile, event: any }*/): void {
+
+    console.log('info:' , info);
+
+    switch (info.type) {
+      case 'start':
+
+        // this.beforeUpload(info.file);
+
+
+        if (!this.checkSelectFile(info.file)) {
+          return;
+        }
+
+
+        this.uploading = true;
+        this.progress = 0;
+
+        break;
+
+      case 'success':
+
+
+        this.uploadSuccess(info.file);
+
+        // this.beforeUpload(info.file);
+        // this.uploadSuccess(info.file);
+
+        break;
+      case 'progress':
+        this.progress = info.event.percent;
+        this.doProgress(this.progress);
+        break;
+    }
+  }
+
+
+  checkSelectFile(file) {
+    if (!file.lastModified) {
+      return false;
+    }
+    const delta = 500;
+    const isOverSize = (file.size / 1024 / 1024) < delta;
+    if (!isOverSize) {
+      this.nzMessageService.error(`video must smaller than ${delta}MB!`);
+      return false;
+    }
+    return true;
+  }
+  checkHashFinish = (hash) => {
+    this.checking = false;
+    this.uploading = true;
+  }
+
+  uploadSuccess = (file) => {
+
+    this.nzMessageService.info('Upload Success');
+    // this.updateStatus(false);
+    this.uploading = false;
+    this.videoFile = null;
+    // this.updateSource(url);
+    this.videoUrl = file.response.url;
+    // console.log(this.picUrl)
+    // this.uploadFinished(url);
+    // if (!inOSS) {
+      const vid = document.createElement('video');
+      vid.addEventListener('loadedmetadata', () => {
+        const height = vid.videoHeight;
+        const width = vid.videoWidth;
+        let duration = vid.duration;
+        if (duration) {
+          duration = duration * 1000;
+        }
+        file.height = height;
+        file.width = width;
+        file.duration = duration;
+        vid.preload = 'none';
+        vid.src = '';
+        vid.remove();
+
+        this.videoUploaded.emit(file.response);
+
+        // this.resService.updateVideo(id, {width, height, duration}).then( () => {
+        //   this.videoUploaded.emit({res_id: id, id, name, hash, url});
+        // });
+
+      });
+      vid.src = file.response.url;
+    // } else {
+    //   this.videoUploaded.emit(file.response);
+    // }
+
+
+
+  }
+
+
+
+  uploadFailure = (err, file) => {
+    this.uploading = false;
+    if (err.name && err.name === 'cancel') {
+      return;
+    }
+    console.log(err);
+    this.nzMessageService.error('Upload Error ' + err.message);
+    this.videoUploadFailure.emit(file);
+  }
+
+  doProgress =  (p) => {
+    if (p > 1) {
+      p = 1;
+    }
+    if (p < 0) {
+      p = 0;
+    }
+    // console.log(Math.floor(p * 100));
+    this.progress =  Math.floor(p * 100);
+  }
+}
+
diff --git a/form/src/app/form/form.component.css b/form/src/app/form/form.component.css
new file mode 100644
index 0000000000000000000000000000000000000000..5d78cd06c5f047021baddbe66edf8dae0954fb5d
--- /dev/null
+++ b/form/src/app/form/form.component.css
@@ -0,0 +1,35 @@
+@import '../style/common_mixin.css';
+
+
+.model-content {
+  width: 100%;
+  height: 100%;
+
+}
+
+.radioPaire {
+  float: left;
+  margin: 3px;
+  border-style: dashed;
+  border-color: #000;
+  border-width: 1px;
+}
+
+.border {
+  border-radius: 20px;
+  border-style: dashed;
+  padding: 20px;
+  margin: 20px;
+  /*width: 500px; */
+  /*//border-radius: 20px;*/
+  /*//border-width: 2px;*/
+  /*//border-color: #000000;*/
+}
+
+
+.border-lite {
+  border: 2px dashed #ddd;
+  border-radius: 0.5rem;
+  padding: 10px;
+  margin: 10px;
+}
diff --git a/form/src/app/form/form.component.html b/form/src/app/form/form.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..3836ff944244c9fac11d53e1301227e43db81b31
--- /dev/null
+++ b/form/src/app/form/form.component.html
@@ -0,0 +1,36 @@
+<div class="model-content">
+  <!-- <div class="border" style=" width: 520px; height: 120px;">
+    <span style="height: 30px; font-size: 18px;">单词音频:</span>
+    <br>
+    <app-audio-recorder [audioUrl]=" item.audio" (audioUploaded)="onAudioUploadSuccess($event)">
+    </app-audio-recorder>
+  </div>
+  <div class="border" style="width: 520px;">
+    <span style="height: 30px; font-size: 18px;">正确字母:</span>
+    <div>
+      <div *ngFor="let rightWord of item.rightWordList; let i = index">
+        <div style="padding: 2px;">
+          <input style="width: 150px;float: left;" type="text" nz-input [(ngModel)]="rightWord.word" (blur)="save()">
+          <app-audio-recorder style="float: left;" [audioUrl]="rightWord.audio"
+            (audioUploaded)="onRightAudioUploadSuccess($event, i)">
+          </app-audio-recorder>
+          <button style="float: right; margin-left: 10px; height: 32px; color: red;" nz-button nzType="dashed"
+            class="add-btn" (click)="removeRightWord(i)">
+            <i nz-icon nzType="minus-circle" nzTheme="outline"></i>
+            删除字母
+          </button>
+        </div>
+        <br> <br>
+      </div>
+    </div>
+    <div *ngIf="(item.rightWordList.length +item.wrongWordList.length < 5)">
+      <button style="margin-left: 2px; margin-top: 2px; height: 32px;" nz-button nzType="dashed" class="add-btn"
+        (click)="addRightWord()">
+        <i nz-icon nzType="plus-circle" nzTheme="outline"></i>
+        添加<span style="color: #44bb44;">正确</span>的字母
+      </button>
+    </div>
+  </div>
+ -->
+
+</div>
diff --git a/form/src/app/form/form.component.ts b/form/src/app/form/form.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..082e3ececafbaecfd34e33722fae3e1549440a28
--- /dev/null
+++ b/form/src/app/form/form.component.ts
@@ -0,0 +1,115 @@
+import { Component, EventEmitter, Input, OnDestroy, OnChanges, OnInit, Output, ApplicationRef, ChangeDetectorRef } from '@angular/core';
+import { JsonPipe } from '@angular/common';
+
+
+@Component({
+  selector: 'app-form',
+  templateUrl: './form.component.html',
+  styleUrls: ['./form.component.css']
+})
+export class FormComponent implements OnInit, OnChanges, OnDestroy {
+
+  // 储存数据用
+  saveKey = "test_001";
+  // 储存对象
+  item;
+
+  constructor(private appRef: ApplicationRef, private changeDetectorRef: ChangeDetectorRef) {
+
+  }
+
+  createShell() {
+    this.item.wordList.push({
+      word: '',
+      audio: '',
+      backWord: '',
+      backWordAudio: '',
+    });
+    this.save();
+  }
+
+  removeShell(idx) {
+    this.item.wordList.splice(idx, 1);
+    this.save();
+  }
+
+  ngOnInit() {
+
+    this.item = {
+      wordList: []
+    };
+
+    // 获取存储的数据
+    (<any>window).courseware.getData((data) => {
+
+      if (data) {
+        this.item = data;
+      }
+
+      this.init();
+      this.changeDetectorRef.markForCheck();
+      this.changeDetectorRef.detectChanges();
+      this.refresh();
+
+    }, this.saveKey);
+  }
+
+  ngOnChanges() {
+  }
+
+  ngOnDestroy() {
+  }
+
+  init() {
+
+  }
+
+
+  /**
+   * 储存图片数据
+   * @param e
+   */
+  onImageUploadSuccess(e, key) {
+
+    this.item[key] = e.url;
+    this.save();
+  }
+
+  /**
+   * 储存音频数据
+   * @param e
+   */
+  onAudioUploadSuccess(e) {
+    this.item.audio = e.url;
+    this.save();
+  }
+
+  onWordAudioUploadSuccess(e, idx) {
+    this.item.wordList[idx].audio = e.url;
+    this.save();
+  }
+
+  onBackWordAudioUploadSuccess(e, idx) {
+    this.item.wordList[idx].backWordAudio = e.url;
+    this.save();
+  }
+
+  /**
+   * 储存数据
+   */
+  save() {
+    (<any>window).courseware.setData(this.item, null, this.saveKey);
+    this.refresh();
+    console.log('this.item = ' + JSON.stringify(this.item));
+  }
+
+  /**
+   * 刷新 渲染页面
+   */
+  refresh() {
+    setTimeout(() => {
+      this.appRef.tick();
+    }, 1);
+  }
+
+}
\ No newline at end of file
diff --git a/form/src/app/pipes/background-image.pipe.ts b/form/src/app/pipes/background-image.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bc15f66b9a285399cf75b9719b345f0de67c2902
--- /dev/null
+++ b/form/src/app/pipes/background-image.pipe.ts
@@ -0,0 +1,23 @@
+import {Pipe, PipeTransform} from '@angular/core';
+// import {ElectronUtil} from '../util/ElectronUtil';
+
+declare const APP_PATH;
+// const isInElectron = localStorage.getItem('electron');
+
+@Pipe({
+  name: 'backgroundImage'
+})
+export class BackgroundImagePipe implements PipeTransform {
+
+  transform(value: any, coursewareSid?: string) {
+    // const ret = value ? `url(${value})` : '';
+    const ret = value ? `url(${value})` : '';
+    return ret;
+    // let ret = value > 0 ? `url(/api/media-resource/${value}/file)` : '';
+    // if (isInElectron && ElectronUtil.hasCached(coursewareSid, value)) {
+    //   ret = `url(file://${APP_PATH}/courseware_cache/${coursewareSid}/${value})`;
+    // }
+    // return ret;
+  }
+
+}
diff --git a/form/src/app/pipes/resource.pipe.ts b/form/src/app/pipes/resource.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d7f5f8df579c3d7981a2556ef149990a9d6ba50b
--- /dev/null
+++ b/form/src/app/pipes/resource.pipe.ts
@@ -0,0 +1,28 @@
+import {Pipe, PipeTransform} from '@angular/core';
+// import {ElectronUtil} from '../util/ElectronUtil';
+
+declare const APP_PATH;
+// const isInElectron = localStorage.getItem('electron');
+
+@Pipe({
+  name: 'resource'
+})
+export class ResourcePipe implements PipeTransform {
+
+  transform(pic_url: any, coursewareSid?: string): string {
+    // return pic_url;
+    if (pic_url &&  typeof pic_url === 'object') {
+      return pic_url;
+    }
+    // console.log('resource', pic_url)
+    return `${pic_url}?t=${Math.random()}`;
+
+    // let ret = value ? `/api/resource/${value}/file` : '';
+    // let ret = res_id ? `/resource/audio/${res_id}` : '';
+    // if (isInElectron && ElectronUtil.hasCached(coursewareSid, res_id)) {
+    //   ret = `file://${APP_PATH}/courseware_cache/${coursewareSid}/${res_id}`;
+    // }
+    // return ret;
+  }
+
+}
diff --git a/form/src/app/pipes/time.pipe.ts b/form/src/app/pipes/time.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..af84a0c697319c7f0c1f41115a113a6f4540d423
--- /dev/null
+++ b/form/src/app/pipes/time.pipe.ts
@@ -0,0 +1,19 @@
+import {Pipe, PipeTransform} from '@angular/core';
+import * as _ from 'lodash';
+
+@Pipe({
+  name: 'time'
+})
+export class TimePipe implements PipeTransform {
+
+  transform(value: any, args?: any): any {
+    let ret = '00:00';
+    if (_.isNumber(value) && value > 0) {
+      const minutes = Math.floor(value / 60);
+      const seconds = Math.floor(value % 60);
+      ret = _.padStart(minutes, 2, '0') + ':' + _.padStart(seconds, 2, '0');
+    }
+    return ret;
+  }
+
+}
diff --git a/form/src/app/play/Unit.ts b/form/src/app/play/Unit.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4a84782db4c8f7a50a9e45a2bd7e50d7f32d2f0c
--- /dev/null
+++ b/form/src/app/play/Unit.ts
@@ -0,0 +1,1676 @@
+
+import TWEEN from '@tweenjs/tween.js';
+
+interface AirWindow  extends Window {
+  air: any;
+  curCtx: any;
+}
+declare let window: AirWindow;
+
+class Sprite {
+  x = 0;
+  y = 0;
+  color = '';
+  radius = 0;
+  alive = false;
+  margin = 0;
+  angle = 0;
+  ctx;
+
+  constructor(ctx = null) {
+    if (!ctx) {
+      this.ctx = window.curCtx;
+    } else {
+      this.ctx = ctx;
+    }
+  }
+  update($event) {
+    this.draw();
+  }
+  draw() {
+
+  }
+
+}
+
+
+
+
+
+export class MySprite extends Sprite {
+
+  _width = 0;
+  _height = 0;
+  _anchorX = 0;
+  _anchorY = 0;
+  _offX = 0;
+  _offY = 0;
+  scaleX = 1;
+  scaleY = 1;
+  _alpha = 1;
+  rotation = 0;
+  visible = true;
+  skewX = 0;
+  skewY = 0;
+
+  _shadowFlag = false;
+  _shadowColor;
+  _shadowOffsetX = 0;
+  _shadowOffsetY = 0;
+  _shadowBlur = 5;
+
+  _radius = 0;
+
+  children = [this];
+
+  childDepandVisible = true;
+  childDepandAlpha = false;
+
+  img;
+  _z = 0;
+
+
+  init(imgObj = null, anchorX: number = 0.5, anchorY: number = 0.5) {
+
+    if (imgObj) {
+
+      this.img = imgObj;
+
+      this.width = this.img.width;
+      this.height = this.img.height;
+
+
+    }
+
+    this.anchorX = anchorX;
+    this.anchorY = anchorY;
+  }
+
+
+  setShadow(offX, offY, blur, color = 'rgba(0, 0, 0, 0.3)') {
+
+
+    this._shadowFlag = true;
+    this._shadowColor = color;
+    this._shadowOffsetX = offX;
+    this._shadowOffsetY = offY;
+    this._shadowBlur = blur;
+
+  }
+
+  setRadius(r) {
+    this._radius = r;
+  }
+
+
+  update($event = null) {
+    if (!this.visible && this.childDepandVisible) {
+      return;
+    }
+
+    this.draw();
+
+  }
+  draw() {
+
+    this.ctx.save();
+
+    this.drawInit();
+
+    this.updateChildren();
+
+    this.ctx.restore();
+
+
+  }
+
+  drawInit() {
+
+    this.ctx.translate(this.x, this.y);
+
+    this.ctx.rotate(this.rotation * Math.PI / 180);
+
+    this.ctx.scale(this.scaleX, this.scaleY);
+
+    this.ctx.globalAlpha = this.alpha;
+
+    this.ctx.transform(1, this.skewX, this.skewY, 1, 0, 0);
+
+
+
+
+
+    if (this._radius) {
+
+      const r = this._radius;
+      const w = this.width;
+      const h = this.height;
+
+      this.ctx.lineTo(-w / 2, h / 2);          // 创建水平线
+      this.ctx.arcTo(-w / 2, -h / 2, -w / 2 + r, -h / 2, r);
+      this.ctx.arcTo(w / 2, -h / 2, w / 2, -h / 2 + r, r);
+      this.ctx.arcTo(w / 2, h / 2, w / 2 - r, h / 2, r);
+      this.ctx.arcTo(-w / 2, h / 2, -w / 2, h / 2 - r, r);
+
+      this.ctx.clip();
+    }
+
+  }
+
+  drawSelf() {
+
+
+
+    if (this._shadowFlag) {
+
+      this.ctx.shadowOffsetX = this._shadowOffsetX;
+      this.ctx.shadowOffsetY = this._shadowOffsetY;
+      this.ctx.shadowBlur = this._shadowBlur;
+      this.ctx.shadowColor = this._shadowColor;
+    } else {
+      this.ctx.shadowOffsetX = 0;
+      this.ctx.shadowOffsetY = 0;
+      this.ctx.shadowBlur = null;
+      this.ctx.shadowColor = null;
+    }
+
+
+    if (this.img) {
+      this.ctx.drawImage(this.img, this._offX, this._offY);
+    }
+
+
+
+
+  }
+
+  updateChildren() {
+
+    if (this.children.length <= 0) { return; }
+
+    for (const child of  this.children) {
+      if (child === this) {
+        if (this.visible) {
+          this.drawSelf();
+        }
+      } else {
+        child.update();
+      }
+    }
+  }
+
+
+  load(url, anchorX = 0.5, anchorY = 0.5) {
+
+    return new Promise((resolve, reject) => {
+      const img = new Image();
+      img.onload = () => resolve(img);
+      img.onerror = reject;
+      img.src = url;
+    }).then(img => {
+
+      this.init(img, anchorX, anchorY);
+      return img;
+    });
+  }
+
+  addChild(child, z = 1) {
+    if (this.children.indexOf(child) === -1) {
+      this.children.push(child);
+      child._z = z;
+      child.parent = this;
+    }
+
+    this.children.sort((a, b) => {
+      return a._z - b._z;
+    });
+
+
+    if (this.childDepandAlpha) {
+      child.alpha = this.alpha;
+    }
+
+  }
+  removeChild(child) {
+    const index = this.children.indexOf(child);
+    if (index !== -1) {
+      this.children.splice(index, 1);
+    }
+  }
+
+  removeChildren() {
+    for (let i = 0; i < this.children.length; i++) {
+      if (this.children[i]) {
+        if (this.children[i] !== this) {
+          this.children.splice(i, 1);
+          i --;
+        }
+      }
+    }
+  }
+
+  _changeChildAlpha(alpha) {
+    for (const child of  this.children) {
+      if (child !== this) {
+        child.alpha = alpha;
+      }
+    }
+  }
+
+  set alpha(v) {
+    this._alpha = v;
+    if (this.childDepandAlpha) {
+      this._changeChildAlpha(v);
+    }
+  }
+  get alpha() {
+    return this._alpha;
+  }
+  set width(v) {
+    this._width = v;
+    this.refreshAnchorOff();
+  }
+  get width() {
+    return this._width;
+  }
+  set height(v) {
+    this._height = v;
+    this.refreshAnchorOff();
+  }
+  get height() {
+    return this._height;
+  }
+  set anchorX(value) {
+    this._anchorX = value;
+    this.refreshAnchorOff();
+  }
+  get anchorX() {
+    return this._anchorX;
+  }
+  set anchorY(value) {
+    this._anchorY = value;
+    this.refreshAnchorOff();
+  }
+  get anchorY() {
+    return this._anchorY;
+  }
+  refreshAnchorOff() {
+    this._offX = -this._width * this.anchorX;
+    this._offY = -this._height * this.anchorY;
+  }
+
+  setScaleXY(value) {
+    this.scaleX = this.scaleY = value;
+  }
+
+  getBoundingBox() {
+
+
+    const getParentData = (item) => {
+
+      let px = item.x;
+      let py = item.y;
+
+      let sx = item.scaleX;
+      let sy = item.scaleY;
+      const parent = item.parent;
+      if (parent) {
+
+        const obj = getParentData(parent);
+
+        const _x = obj.px;
+        const _y = obj.py;
+        const _sx = obj.sx;
+        const _sy = obj.sy;
+
+        px = _x + item.x * _sx;
+        py = _y + item.y * _sy;
+
+        sx *= _sx;
+        sy *= _sy;
+
+      }
+
+      return {px, py, sx, sy};
+    };
+
+
+    const data = getParentData(this);
+
+
+    const x = data.px + this._offX * Math.abs(data.sx);
+    const y = data.py + this._offY * Math.abs(data.sy);
+    const width = this.width * Math.abs(data.sx);
+    const height = this.height * Math.abs(data.sy);
+
+    // const x = this.x + this._offX * Math.abs(this.scaleX);
+    // const y = this.y + this._offY * Math.abs(this.scaleY);
+    // const width = this.width * Math.abs(this.scaleX);
+    // const height = this.height * Math.abs(this.scaleY);
+
+    return {x, y, width, height};
+  }
+
+}
+
+
+
+
+
+export class ColorSpr extends MySprite {
+
+  r = 0;
+  g = 0;
+  b = 0;
+
+  createGSCanvas() {
+
+    if (!this.img) {
+      return;
+    }
+
+
+
+    const rect = this.getBoundingBox();
+    if (rect.width <= 1 || rect.height <= 1) {
+      return;
+    }
+
+
+    const c = this.ctx.getImageData(rect.x, rect.y, rect.width, rect.height);
+    for ( let i = 0; i < c.height; i++) {
+      for ( let j = 0; j < c.width; j++) {
+
+        const x = (i * 4) * c.width + ( j * 4 );
+        const r = c.data[x];
+        const g = c.data[x + 1];
+        const b = c.data[x + 2];
+
+        c.data[x] = this.r;
+        c.data[x + 1] = this.g;
+        c.data[x + 2] = this.b;
+
+
+
+        // c.data[x] = c.data[x + 1] = c.data[x + 2] = (r + g + b) / 3 ;
+        // // c.data[x + 3] = 255;
+      }
+    }
+
+    this.ctx.putImageData(c, rect.x, rect.y, 0, 0, rect.width, rect.height);
+  }
+
+
+  drawSelf() {
+    super.drawSelf();
+    this.createGSCanvas();
+  }
+
+}
+
+
+export class GrayscaleSpr extends MySprite {
+
+  grayScale = 120;
+
+  createGSCanvas() {
+
+    if (!this.img) {
+      return;
+    }
+
+
+
+    const rect = this.getBoundingBox();
+
+    const c = this.ctx.getImageData(rect.x, rect.y, rect.width, rect.height);
+    for ( let i = 0; i < c.height; i++) {
+      for ( let j = 0; j < c.width; j++) {
+
+        const x = (i * 4) * c.width + ( j * 4 );
+        const r = c.data[x];
+        const g = c.data[x + 1];
+        const b = c.data[x + 2];
+        // const a = c.data[x + 3];
+
+        c.data[x] = c.data[x + 1] = c.data[x + 2] = this.grayScale; // (r + g + b) / 3;
+        // c.data[x + 3] = 255;
+      }
+    }
+
+    this.ctx.putImageData(c, rect.x, rect.y, 0, 0, rect.width, rect.height);
+  }
+
+
+  drawSelf() {
+    super.drawSelf();
+    this.createGSCanvas();
+  }
+
+}
+
+
+
+
+
+
+export class BitMapLabel extends MySprite {
+
+
+  labelArr;
+  baseUrl;
+
+  setText(data, text) {
+
+    this.labelArr = [];
+
+    const labelArr = [];
+    const tmpArr = text.split('');
+    let totalW = 0;
+    let h = 0;
+    for (const tmp of tmpArr) {
+
+      const label = new MySprite(this.ctx);
+      label.init(data[tmp], 0);
+      this.addChild(label);
+      labelArr.push(label);
+
+      totalW += label.width;
+      h = label.height;
+    }
+
+
+    this.width = totalW;
+    this.height = h;
+
+    let offX = -totalW / 2;
+    for (const label of  labelArr) {
+      label.x = offX;
+      offX += label.width;
+    }
+
+    this.labelArr = labelArr;
+  }
+
+}
+
+
+
+
+export class Label extends MySprite {
+
+  text: string;
+  // fontSize:String = '40px';
+  fontName = 'Verdana';
+  textAlign = 'left';
+  fontSize = 40;
+  fontColor = '#000000';
+  fontWeight = 900;
+  _maxWidth;
+  outline = 0;
+  outlineColor = '#ffffff';
+
+  // _shadowFlag = false;
+  // _shadowColor;
+  // _shadowOffsetX;
+  // _shadowOffsetY;
+  // _shadowBlur;
+
+  _outlineFlag = false;
+  _outLineWidth;
+  _outLineColor;
+
+
+  constructor(ctx = null) {
+    super(ctx);
+    this.init();
+  }
+
+  refreshSize() {
+
+    this.ctx.save();
+
+    this.ctx.font = `${this.fontSize}px ${this.fontName}`;
+    this.ctx.textAlign = this.textAlign;
+    this.ctx.textBaseline = 'middle';
+    this.ctx.fontWeight = this.fontWeight;
+
+    this._width = this.ctx.measureText(this.text).width;
+    this._height = this.fontSize;
+    this.refreshAnchorOff();
+
+    this.ctx.restore();
+
+  }
+
+  setMaxSize(w) {
+
+    this._maxWidth = w;
+    this.refreshSize();
+    if (this.width >= w) {
+      this.scaleX *= w / this.width;
+      this.scaleY *= w / this.width;
+    }
+  }
+
+  show(callBack = null) {
+
+    this.visible = true;
+
+    if (this.alpha >= 1) {
+      return;
+    }
+
+    const tween = new TWEEN.Tween(this)
+      .to({ alpha: 1 }, 800)
+      // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
+      .onComplete(() => {
+        if (callBack) {
+          callBack();
+        }
+      })
+      .start(); // Start the tween immediately.
+  }
+
+  // setShadow(offX = 0, offY = 2, blur = 2, color = 'rgba(0, 0, 0, 0.2)') {
+  //
+  //   this._shadowFlag = true;
+  //   this._shadowColor = color;
+  //   // 将阴影向右移动15px,向上移动10px
+  //   this._shadowOffsetX = 5;
+  //   this._shadowOffsetY = 5;
+  //   // 轻微模糊阴影
+  //   this._shadowBlur = 5;
+  // }
+
+  setOutline(width = 5, color = '#ffffff') {
+
+    this._outlineFlag = true;
+    this._outLineWidth = width;
+    this._outLineColor = color;
+
+  }
+
+  drawText() {
+
+    // console.log('in drawText', this.text);
+
+    if (!this.text) { return; }
+
+
+    // if (this._shadowFlag) {
+    //
+    //   this.ctx.shadowColor = this._shadowColor;
+    //   // 将阴影向右移动15px,向上移动10px
+    //   this.ctx.shadowOffsetX = this._shadowOffsetX;
+    //   this.ctx.shadowOffsetY = this._shadowOffsetY;
+    //   // 轻微模糊阴影
+    //   this.ctx.shadowBlur = this._shadowBlur;
+    // }
+
+
+
+    this.ctx.font = `${this.fontSize}px ${this.fontName}`;
+    this.ctx.textAlign = this.textAlign;
+    this.ctx.textBaseline = 'middle';
+    this.ctx.fontWeight = this.fontWeight;
+
+    if (this._outlineFlag) {
+      this.ctx.lineWidth = this._outLineWidth;
+      this.ctx.strokeStyle = this._outLineColor;
+      this.ctx.strokeText(this.text, 0, 0);
+    }
+
+    this.ctx.fillStyle = this.fontColor;
+
+
+
+    if (this.outline > 0) {
+      this.ctx.lineWidth = this.outline;
+      this.ctx.strokeStyle = this.outlineColor;
+      this.ctx.strokeText(this.text, 0, 0);
+
+    }
+
+    this.ctx.fillText(this.text, 0, 0);
+
+
+  }
+
+
+  drawSelf() {
+    super.drawSelf();
+    this.drawText();
+  }
+
+}
+
+export class RichTextOld extends Label {
+
+  textArr = [];
+  fontSize = 40;
+
+  setText(text: string, words) {
+
+    let newText = text;
+    for (const word of words) {
+
+      const re = new RegExp(word, 'g');
+      newText = newText.replace( re, `#${word}#`);
+      // newText = newText.replace(word, `#${word}#`);
+    }
+
+    this.textArr = newText.split('#');
+    this.text = newText;
+
+    // this.setSize();
+  }
+
+  refreshSize() {
+
+    this.ctx.save();
+
+    this.ctx.font = `${this.fontSize}px ${this.fontName}`;
+    this.ctx.textAlign = this.textAlign;
+    this.ctx.textBaseline = 'middle';
+    this.ctx.fontWeight = this.fontWeight;
+
+    let curX = 0;
+    for (const text of this.textArr) {
+      const w = this.ctx.measureText(text).width;
+      curX += w;
+    }
+
+    this.width = curX;
+    this.height = this.fontSize;
+    this.refreshAnchorOff();
+
+    this.ctx.restore();
+
+  }
+
+
+  show(callBack = null) {
+
+    // console.log(' in show ');
+    this.visible = true;
+    // this.alpha = 0;
+
+    const tween = new TWEEN.Tween(this)
+      .to({ alpha: 1 }, 800)
+      // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
+      .onComplete(() => {
+        if (callBack) {
+          callBack();
+        }
+      })
+      .start(); // Start the tween immediately.
+
+  }
+
+
+
+  drawText() {
+
+    // console.log('in drawText', this.text);
+
+    if (!this.text) { return; }
+
+
+    this.ctx.font = `${this.fontSize}px ${this.fontName}`;
+    this.ctx.textAlign = this.textAlign;
+    this.ctx.textBaseline = 'middle';
+    this.ctx.fontWeight = 900;
+
+    this.ctx.lineWidth = 5;
+    this.ctx.strokeStyle = '#ffffff';
+    // this.ctx.strokeText(this.text, 0, 0);
+
+    this.ctx.fillStyle = '#000000';
+    // this.ctx.fillText(this.text, 0, 0);
+
+
+    let curX = 0;
+    for (let i = 0; i < this.textArr.length; i++) {
+
+      const w = this.ctx.measureText(this.textArr[i]).width;
+
+      if ((i + 1) % 2 === 0) {
+        this.ctx.fillStyle = '#c8171e';
+      } else {
+        this.ctx.fillStyle = '#000000';
+      }
+
+      this.ctx.fillText(this.textArr[i], curX, 0);
+      curX += w;
+
+    }
+
+
+  }
+
+}
+
+
+
+
+
+export class RichText extends Label {
+
+
+  disH =  30;
+
+  constructor(ctx?: any) {
+    super(ctx);
+
+    // this.dataArr = dataArr;
+  }
+
+
+
+  drawText() {
+
+    if (!this.text) {
+      return;
+    }
+
+
+    this.ctx.font = `${this.fontSize}px ${this.fontName}`;
+    this.ctx.textAlign = this.textAlign;
+    this.ctx.textBaseline = 'middle';
+    this.ctx.fontWeight = this.fontWeight;
+    this.ctx.fillStyle = this.fontColor;
+
+
+    const selfW = this.width * this.scaleX;
+
+
+    const chr = this.text.split(' ');
+    let temp = '';
+    const row = [];
+    const w = selfW - 80;
+    const disH = (this.fontSize + this.disH) * this.scaleY;
+
+
+
+
+    for (const c of  chr) {
+      if (this.ctx.measureText(temp).width < w && this.ctx.measureText(temp + (c)).width <= w) {
+        temp += ' ' + c;
+      } else {
+        row.push(temp);
+        temp = ' ' + c;
+      }
+    }
+    row.push(temp);
+
+    const x = 0;
+    const y = -row.length * disH / 2;
+    // for (let b = 0 ; b < row.length; b++) {
+    //   this.ctx.strokeText(row[b], x, y + ( b + 1 ) * disH ); // 每行字体y坐标间隔20
+    // }
+
+
+    if (this._outlineFlag) {
+      this.ctx.lineWidth = this._outLineWidth;
+      this.ctx.strokeStyle = this._outLineColor;
+      for (let b = 0 ; b < row.length; b++) {
+        this.ctx.strokeText(row[b], x, y + ( b + 1 ) * disH ); // 每行字体y坐标间隔20
+      }
+      // this.ctx.strokeText(this.text, 0, 0);
+    }
+
+
+    // this.ctx.fillStyle = '#ff7600';
+    for (let b = 0 ; b < row.length; b++) {
+      this.ctx.fillText(row[b], x, y + ( b + 1 ) * disH ); // 每行字体y坐标间隔20
+    }
+
+  }
+
+
+  drawSelf() {
+    super.drawSelf();
+    this.drawText();
+  }
+
+}
+
+
+
+
+export class LineRect extends MySprite {
+
+  lineColor = '#ffffff';
+  lineWidth = 10;
+
+  setSize(w, h) {
+    this.width = w;
+    this.height = h;
+  }
+
+  drawLine() {
+
+    this.ctx.beginPath();
+    this.ctx.moveTo(this._offX, this._offY);
+    this.ctx.lineTo(this._offX + this.width, this._offY);
+    this.ctx.lineTo(this._offX + this.width, this._offY + this.height);
+    this.ctx.lineTo(this._offX, this._offY + this.height);
+    this.ctx.closePath();
+
+    this.ctx.lineWidth = this.lineWidth;
+    // this.ctx.fillStyle = "rgb(2,33,42)";  //指定填充颜色
+    // this.ctx.fill();   //对多边形进行填充
+
+    this.ctx.strokeStyle = this.lineColor; // "#ffffff";
+    this.ctx.stroke();
+
+  }
+
+
+  drawSelf() {
+    super.drawSelf();
+    this.drawLine();
+  }
+}
+
+
+export class ShapeRect extends MySprite {
+
+  fillColor = '#FF0000';
+
+  setSize(w, h) {
+    this.width = w;
+    this.height = h;
+
+    // console.log('w:', w);
+    // console.log('h:', h);
+  }
+
+  drawShape() {
+
+    this.ctx.fillStyle = this.fillColor;
+    this.ctx.fillRect(this._offX, this._offY, this.width, this.height);
+
+  }
+
+
+  drawSelf() {
+    super.drawSelf();
+    this.drawShape();
+  }
+}
+
+
+
+export class ShapeCircle extends MySprite {
+
+  fillColor = '#FF0000';
+  radius = 0;
+
+
+  setRadius(r) {
+    this.anchorX = this.anchorY = 0.5;
+
+    this.radius = r;
+    this.width = r * 2;
+    this.height = r * 2;
+  }
+
+  drawShape() {
+
+    this.ctx.beginPath();
+    this.ctx.fillStyle = this.fillColor;
+    this.ctx.arc(0, 0, this.radius, 0, angleToRadian(360));
+    this.ctx.fill();
+  }
+
+  drawSelf() {
+    super.drawSelf();
+    this.drawShape();
+  }
+}
+
+export class ShapeRectNew extends MySprite {
+
+
+  radius = 0;
+  fillColor = '#ffffff';
+  strokeColor = '#000000';
+  fill = true;
+  stroke = false;
+  lineWidth = 1;
+
+  setSize(w, h, r) {
+    this.width = w;
+    this.height = h;
+    this.radius = r;
+  }
+
+  setOutLine(color, lineWidth) {
+    this.stroke = true;
+    this.strokeColor = color;
+    this.lineWidth = lineWidth;
+  }
+
+
+  drawShape() {
+
+    const ctx = this.ctx;
+    const width = this.width;
+    const height = this.height;
+    const radius = this.radius;
+
+    ctx.save();
+    ctx.beginPath(0);
+    // 从右下角顺时针绘制,弧度从0到1/2PI
+    ctx.arc(width - radius, height - radius, radius, 0, Math.PI / 2);
+
+    // 矩形下边线
+    ctx.lineTo(radius, height);
+
+    // 左下角圆弧,弧度从1/2PI到PI
+    ctx.arc(radius, height - radius, radius, Math.PI / 2, Math.PI);
+
+    // 矩形左边线
+    ctx.lineTo(0, radius);
+
+    // 左上角圆弧,弧度从PI到3/2PI
+    ctx.arc(radius, radius, radius, Math.PI, Math.PI * 3 / 2);
+
+    // 上边线
+    ctx.lineTo(width - radius, 0);
+
+    // 右上角圆弧
+    ctx.arc(width - radius, radius, radius, Math.PI * 3 / 2, Math.PI * 2);
+
+    // 右边线
+    ctx.lineTo(width, height - radius);
+    ctx.closePath();
+
+    if (this.fill) {
+      ctx.fillStyle = this.fillColor;
+      ctx.fill();
+    }
+    if (this.stroke) {
+      ctx.lineWidth = this.lineWidth;
+      ctx.strokeStyle = this.strokeColor;
+      ctx.stroke();
+    }
+    ctx.restore();
+
+  }
+
+
+  drawSelf() {
+    super.drawSelf();
+    this.drawShape();
+  }
+
+}
+export class MyAnimation extends MySprite {
+
+
+  frameArr = [];
+  frameIndex = 0;
+  playFlag = false;
+  lastDateTime;
+  curDelay = 0;
+
+
+  loop = false;
+  playEndFunc;
+  delayPerUnit = 1;
+
+  restartFlag = false;
+  reverseFlag = false;
+
+  addFrameByImg(img) {
+
+    const spr = new MySprite(this.ctx);
+    spr.init(img);
+    this._refreshSize(img);
+
+    spr.visible = false;
+
+    this.addChild(spr);
+    this.frameArr.push(spr);
+
+    this.frameArr[this.frameIndex].visible = true;
+  }
+
+  addFrameByUrl(url) {
+
+    const spr = new MySprite(this.ctx);
+    spr.load(url).then(img => {
+
+      this._refreshSize(img);
+    });
+    spr.visible = false;
+
+    this.addChild(spr);
+    this.frameArr.push(spr);
+
+    this.frameArr[this.frameIndex].visible = true;
+  }
+
+  _refreshSize(img: any) {
+
+    if (this.width < img.width) {
+      this.width = img.width;
+    }
+    if (this.height < img.height) {
+      this.height = img.height;
+    }
+  }
+
+
+
+  play() {
+    this.playFlag = true;
+    this.lastDateTime = new Date().getTime();
+  }
+
+  stop() {
+    this.playFlag = false;
+  }
+
+
+  replay() {
+    this.restartFlag = true;
+    this.play();
+  }
+
+
+  reverse() {
+    this.reverseFlag = !this.reverseFlag;
+    this.frameArr.reverse();
+    this.frameIndex = 0;
+  }
+
+  showAllFrame() {
+    for (const frame of this.frameArr ) {
+      frame.alpha = 1;
+    }
+  }
+
+  hideAllFrame() {
+    for (const frame of this.frameArr) {
+      frame.alpha = 0;
+    }
+  }
+
+
+  playEnd() {
+
+    this.playFlag = false;
+    this.curDelay = 0;
+
+    this.frameArr[this.frameIndex].visible = true;
+
+    if (this.playEndFunc) {
+      this.playEndFunc();
+      this.playEndFunc = null;
+    }
+  }
+
+  updateFrame() {
+    if (this.frameArr[this.frameIndex]) {
+      this.frameArr[this.frameIndex].visible = false;
+    }
+
+    this.frameIndex ++;
+    if (this.frameIndex >= this.frameArr.length) {
+      if (this.loop) {
+        this.frameIndex = 0;
+      } else if (this.restartFlag) {
+        this.restartFlag = false;
+        this.frameIndex = 0;
+      } else {
+        this.frameIndex -- ;
+        this.playEnd();
+        return;
+      }
+    }
+
+    this.frameArr[this.frameIndex].visible = true;
+
+  }
+
+
+
+
+  _updateDelay(delay) {
+
+    this.curDelay += delay;
+    if (this.curDelay < this.delayPerUnit) {
+      return;
+    }
+    this.curDelay -= this.delayPerUnit;
+    this.updateFrame();
+  }
+
+  _updateLastDate() {
+    if (!this.playFlag) { return; }
+
+    let delay = 0;
+    if (this.lastDateTime) {
+      delay = (new Date().getTime() - this.lastDateTime) / 1000;
+    }
+    this.lastDateTime = new Date().getTime();
+    this._updateDelay(delay);
+  }
+
+  update($event: any = null) {
+    super.update($event);
+    this._updateLastDate();
+  }
+
+
+}
+
+
+
+
+// --------===========  util func  =============-------------
+
+
+export function tweenChange(item, obj, time = 0.8, callBack = null, easing = null, update = null) {
+
+  const tween = new TWEEN.Tween(item).to(obj, time * 1000);
+
+  if (callBack) {
+    tween.onComplete(() => {
+      callBack();
+    });
+  }
+  if (easing) {
+    tween.easing(easing);
+  }
+  if (update) {
+    tween.onUpdate( (a, b) => {
+      update(a, b);
+    });
+  }
+
+  tween.start();
+  return tween;
+}
+
+
+
+
+export function rotateItem(item, rotation, time = 0.8, callBack = null, easing = null) {
+
+  const tween = new TWEEN.Tween(item).to({ rotation }, time * 1000);
+
+  if (callBack) {
+    tween.onComplete(() => {
+      callBack();
+    });
+  }
+  if (easing) {
+    tween.easing(easing);
+  }
+
+  tween.start();
+}
+
+
+
+export function scaleItem(item, scale, time = 0.8, callBack = null, easing = null) {
+
+  const tween = new TWEEN.Tween(item).to({ scaleX: scale, scaleY: scale}, time * 1000);
+
+  if (callBack) {
+    tween.onComplete(() => {
+      callBack();
+    });
+  }
+  if (easing) {
+    tween.easing(easing);
+  }
+
+  tween.start();
+  return tween;
+}
+
+
+export function moveItem(item, x, y, time = 0.8, callBack = null, easing = null) {
+
+  const tween = new TWEEN.Tween(item).to({ x, y}, time * 1000);
+
+  if (callBack) {
+    tween.onComplete(() => {
+      callBack();
+    });
+  }
+  if (easing) {
+    tween.easing(easing);
+  }
+
+  tween.start();
+
+  return tween;
+}
+
+
+
+
+
+
+
+export function endShow(item, s = 1) {
+
+  item.scaleX = item.scaleY = 0;
+  item.alpha = 0;
+
+  const tween = new TWEEN.Tween(item)
+    .to({ alpha: 1, scaleX: s, scaleY: s }, 800)
+    .easing(TWEEN.Easing.Elastic.Out) // Use an easing function to make the animation smooth.
+    .onComplete(() => {
+
+    })
+    .start();
+}
+
+export function hideItem(item, time = 0.8, callBack = null, easing = null) {
+
+  if (item.alpha === 0) {
+    return;
+  }
+
+  const tween = new TWEEN.Tween(item)
+    .to({alpha: 0}, time * 1000)
+    // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
+    .onComplete(() => {
+      if (callBack) {
+        callBack();
+      }
+    });
+
+  if (easing) {
+    tween.easing(easing);
+  }
+
+  tween.start();
+}
+
+
+export function showItem(item, time = 0.8, callBack = null, easing = null) {
+
+  if (item.alpha === 1) {
+    if (callBack) {
+      callBack();
+    }
+    return;
+  }
+
+  item.visible = true;
+  const tween = new TWEEN.Tween(item)
+    .to({alpha: 1}, time * 1000)
+    // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
+    .onComplete(() => {
+      if (callBack) {
+        callBack();
+      }
+    });
+
+  if (easing) {
+    tween.easing(easing);
+  }
+
+  tween.start();
+}
+
+
+export function alphaItem(item, alpha, time = 0.8, callBack = null, easing = null) {
+
+
+  const tween = new TWEEN.Tween(item)
+    .to({alpha}, time * 1000)
+    // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
+    .onComplete(() => {
+      if (callBack) {
+        callBack();
+      }
+    });
+
+  if (easing) {
+    tween.easing(easing);
+  }
+
+  tween.start();
+}
+
+
+export function showStar(item, time = 0.8, callBack = null, easing = null) {
+
+
+  const tween = new TWEEN.Tween(item)
+    .to({alpha: 1, scale: 1}, time * 1000)
+    // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
+    .onComplete(() => {
+      if (callBack) {
+        callBack();
+      }
+    });
+
+  if (easing) {
+    tween.easing(easing);
+  }
+
+  tween.start();
+}
+
+
+export function randomSortByArr(arr) {
+  const newArr = [];
+  const tmpArr = arr.concat();
+  while (tmpArr.length > 0) {
+    const randomIndex = Math.floor( tmpArr.length * Math.random() );
+    newArr.push(tmpArr[randomIndex]);
+    tmpArr.splice(randomIndex, 1);
+  }
+  return newArr;
+}
+
+
+export function radianToAngle(radian) {
+  return radian * 180 / Math.PI;
+  // 角度 = 弧度 * 180 / Math.PI;
+}
+
+export function angleToRadian(angle) {
+  return angle * Math.PI / 180;
+  // 弧度= 角度 * Math.PI / 180;
+}
+
+export function getPosByAngle(angle, len) {
+
+  const radian = angle * Math.PI / 180;
+  const x = Math.sin(radian) * len;
+  const y = Math.cos(radian) * len;
+
+  return {x, y};
+
+}
+
+export function getAngleByPos(px, py, mx, my) {
+
+  const x = Math.abs(px - mx);
+  const y = Math.abs(py - my);
+
+  const z = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
+  const cos = y / z;
+  const radina = Math.acos(cos); // 用反三角函数求弧度
+  let angle = Math.floor(180 / (Math.PI / radina) * 100) / 100; // 将弧度转换成角度
+
+  if (mx > px && my > py) {// 鼠标在第四象限
+    angle = 180 - angle;
+  }
+  if (mx === px && my > py) {// 鼠标在y轴负方向上
+    angle = 180;
+  }
+  if (mx > px && my === py) {// 鼠标在x轴正方向上
+    angle = 90;
+  }
+  if (mx < px && my > py) {// 鼠标在第三象限
+    angle = 180 + angle;
+  }
+  if (mx < px && my === py) {// 鼠标在x轴负方向
+    angle = 270;
+  }
+  if (mx < px && my < py) {// 鼠标在第二象限
+    angle = 360 - angle;
+  }
+
+  // console.log('angle: ', angle);
+  return angle;
+
+}
+
+
+export function removeItemFromArr(arr, item) {
+  const index = arr.indexOf(item);
+  if (index !== -1) {
+    arr.splice(index, 1);
+  }
+}
+
+
+
+
+
+
+export function circleMove(item, x0, y0, time = 2, addR = 360, xPer = 1, yPer = 1, callBack = null, easing = null) {
+
+  const r = getPosDistance(item.x, item.y, x0, y0);
+  let a = getAngleByPos(item.x, item.y, x0, y0);
+  a += 90;
+  const obj = {r, a};
+
+  item._circleAngle = a;
+  const targetA = a + addR;
+
+  const tween = new TWEEN.Tween(item).to({_circleAngle: targetA}, time * 1000);
+
+  if (callBack) {
+    tween.onComplete(() => {
+      callBack();
+    });
+  }
+  if (easing) {
+    tween.easing(easing);
+  }
+
+  tween.onUpdate( (item, progress) => {
+
+    // console.log(item._circleAngle);
+    const r = obj.r;
+    const a = item._circleAngle;
+
+    const x = x0 + r * xPer * Math.cos(a * Math.PI / 180);
+    const y = y0 + r * yPer * Math.sin(a * Math.PI / 180);
+
+    item.x = x;
+    item.y = y;
+
+    // obj.a ++;
+  });
+
+  tween.start();
+}
+
+
+export function getPosDistance(sx, sy, ex, ey) {
+
+  const _x = ex - sx;
+  const _y = ey - sy;
+  const len = Math.sqrt( Math.pow(_x, 2) + Math.pow(_y, 2) );
+  return len;
+}
+
+export function delayCall(callback, second) {
+  const tween = new TWEEN.Tween(this)
+    .delay(second * 1000)
+    .onComplete(() => {
+      if (callback) {
+        callback();
+      }
+    })
+    .start();
+}
+
+
+export function formatTime(fmt, date) {
+
+  // "yyyy-MM-dd HH:mm:ss";
+
+  const o = {
+    'M+': date.getMonth() + 1, // 月份
+    'd+': date.getDate(), // æ—¥
+    'h+': date.getHours(), // 小时
+    'm+': date.getMinutes(), // 分
+    's+': date.getSeconds(), // ç§’
+    'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
+    S: date.getMilliseconds() // 毫秒
+  };
+  if (/(y+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)); }
+  for (const k in o) {
+    if (new RegExp('(' + k + ')').test(fmt)) { fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1)
+      ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)));
+    }
+  }
+  return fmt;
+}
+
+export function getMinScale(item, maxLen) {
+  const sx = maxLen / item.width;
+  const sy = maxLen / item.height;
+  const minS = Math.min(sx, sy);
+  return minS;
+}
+
+
+
+export function jelly(item, time = 0.7) {
+
+
+  if (item.jellyTween) {
+    TWEEN.remove(item.jellyTween);
+  }
+
+  const t = time / 9;
+  const baseSX = item.scaleX;
+  const baseSY = item.scaleY;
+  let index = 0;
+
+  const run = () => {
+    if (index >= arr.length) {
+      item.jellyTween = null;
+      return;
+    }
+    const data = arr[index];
+    const t = tweenChange(item, {scaleX: data[0], scaleY: data[1]}, data[2], () => {
+      index ++;
+      run();
+    }, TWEEN.Easing.Sinusoidal.InOut);
+    item.jellyTween = t;
+  };
+
+  const arr = [
+    [baseSX * 1.1, baseSY * 0.9, t],
+    [baseSX * 0.98, baseSY * 1.02, t * 2],
+    [baseSX * 1.02, baseSY * 0.98, t * 2],
+    [baseSX * 0.99, baseSY * 1.01, t * 2],
+    [baseSX * 1.0, baseSY * 1.0, t * 2],
+  ];
+
+
+  run();
+}
+
+
+
+export function showPopParticle(img, pos, parent, num = 20, minLen = 40, maxLen = 80, showTime = 0.4) {
+
+
+  for (let i = 0; i < num; i ++) {
+
+    const particle = new MySprite();
+    particle.init(img);
+    particle.x = pos.x;
+    particle.y = pos.y;
+    parent.addChild(particle);
+
+    const randomR = 360 * Math.random();
+    particle.rotation = randomR;
+
+    const randomS = 0.3 + Math.random() * 0.7;
+    particle.setScaleXY(randomS * 0.3);
+
+    const randomX = Math.random() * 20 - 10;
+    particle.x += randomX;
+
+    const randomY = Math.random() * 20 - 10;
+    particle.y += randomY;
+
+    const randomL = minLen + Math.random() * (maxLen - minLen);
+    const randomA = 360 * Math.random();
+    const randomT = getPosByAngle(randomA, randomL);
+    moveItem(particle, particle.x + randomT.x, particle.y + randomT.y, showTime, () => {
+
+
+
+    }, TWEEN.Easing.Exponential.Out);
+
+    // scaleItem(particle, 0, 0.6, () => {
+    //
+    // });
+
+    scaleItem(particle, randomS, 0.6, () => {
+
+    }, TWEEN.Easing.Exponential.Out);
+
+    setTimeout(() => {
+
+      hideItem(particle, 0.4, () => {
+
+      }, TWEEN.Easing.Cubic.In);
+
+    }, showTime * 0.5 * 1000);
+
+
+  }
+}
+
+
+
+export function shake(item, time = 0.5, callback = null, rate = 1) {
+
+  if (item.shakeTween) {
+    return;
+  }
+
+  item.shakeTween = true;
+  const offX = 15 * item.scaleX * rate;
+  const offY = 15 * item.scaleX * rate;
+  const baseX = item.x;
+  const baseY = item.y;
+  const easing = TWEEN.Easing.Sinusoidal.InOut;
+
+
+  const move4 = () => {
+    moveItem(item, baseX, baseY, time / 4, () => {
+      item.shakeTween = false;
+      if (callback) {
+        callback();
+      }
+    }, easing);
+  };
+
+  const move3 = () => {
+    moveItem(item, baseX + offX / 4, baseY + offY / 4, time / 4, () => {
+      move4();
+    }, easing);
+  };
+
+  const move2 = () => {
+    moveItem(item, baseX - offX / 4 * 3, baseY - offY / 4 * 3, time / 4, () => {
+      move3();
+    }, easing);
+  };
+
+  const move1 = () => {
+    moveItem(item, baseX + offX, baseY + offY, time / 7.5, () => {
+      move2();
+    }, easing);
+  };
+
+  move1();
+
+}
+
+// --------------- custom class --------------------
+
diff --git a/form/src/app/play/play.component.css b/form/src/app/play/play.component.css
new file mode 100644
index 0000000000000000000000000000000000000000..bbda49566c0a6aa2c621e2f863caf1849e492572
--- /dev/null
+++ b/form/src/app/play/play.component.css
@@ -0,0 +1,19 @@
+.game-container {
+  width: 100%;
+  height: 100%;
+  background: #ffffff;
+  background-size: cover;
+}
+
+#canvas {
+
+}
+
+
+
+@font-face
+{
+  font-family: 'BRLNSDB';
+  src: url("../../assets/font/BRLNSDB.TTF") ;
+}
+
diff --git a/form/src/app/play/play.component.html b/form/src/app/play/play.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..960fb495a359c3c7803c4370d8c7d8457517d70e
--- /dev/null
+++ b/form/src/app/play/play.component.html
@@ -0,0 +1,3 @@
+<div class="game-container" #wrap>
+  <canvas id="canvas" #canvas></canvas>
+</div>
diff --git a/form/src/app/play/play.component.ts b/form/src/app/play/play.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f4b8a863d45aa9f51852483802422d4dfacce939
--- /dev/null
+++ b/form/src/app/play/play.component.ts
@@ -0,0 +1,679 @@
+import {Component, ElementRef, ViewChild, OnInit, Input, OnDestroy, HostListener} from '@angular/core';
+
+import {
+  Label,
+  MySprite, tweenChange,
+
+} from './Unit';
+import {res, resAudio} from './resources';
+
+import {Subject} from 'rxjs';
+import {debounceTime} from 'rxjs/operators';
+
+import TWEEN from '@tweenjs/tween.js';
+
+
+
+
+@Component({
+  selector: 'app-play',
+  templateUrl: './play.component.html',
+  styleUrls: ['./play.component.css']
+})
+export class PlayComponent implements OnInit, OnDestroy {
+
+  @ViewChild('canvas', {static: true }) canvas: ElementRef;
+  @ViewChild('wrap', {static: true }) wrap: ElementRef;
+
+  // 数据
+  data;
+
+  ctx;
+
+  canvasWidth = 1280; // canvas实际宽度
+  canvasHeight = 720; // canvas实际高度
+
+  canvasBaseW = 1280; // canvas 资源预设宽度
+  canvasBaseH = 720;  // canvas 资源预设高度
+
+  mx; // 点击x坐标
+  my; // 点击y坐标
+
+
+  // 资源
+  rawImages = new Map(res);
+  rawAudios = new Map(resAudio);
+
+  images = new Map();
+
+  animationId: any;
+  winResizeEventStream = new Subject();
+
+  audioObj = {};
+
+  renderArr;
+  mapScale = 1;
+
+  canvasLeft;
+  canvasTop;
+
+  saveKey = 'test_0011';
+
+
+  btnLeft;
+  btnRight;
+  pic1;
+  pic2;
+
+  canTouch = true;
+
+  curPic;
+
+  @HostListener('window:resize', ['$event'])
+  onResize(event) {
+    this.winResizeEventStream.next();
+  }
+
+
+  ngOnInit() {
+
+    this.data = {};
+
+    // 获取数据
+    const getData = (<any> window).courseware.getData;
+    getData((data) => {
+
+      if (data && typeof data == 'object') {
+        this.data = data;
+      }
+      // console.log('data:' , data);
+
+      // 初始化 各事件监听
+      this.initListener();
+
+      // 若无数据 则为预览模式 需要填充一些默认数据用来显示
+      this.initDefaultData();
+
+      // 初始化 音频资源
+      this.initAudio();
+      // 初始化 图片资源
+      this.initImg();
+      // 开始预加载资源
+      this.load();
+
+    }, this.saveKey);
+
+  }
+
+  ngOnDestroy() {
+    window['curCtx'] = null;
+    window.cancelAnimationFrame(this.animationId);
+  }
+
+
+  load() {
+
+    // 预加载资源
+    this.loadResources().then(() => {
+      window["air"].hideAirClassLoading(this.saveKey, this.data);
+      this.init();
+      this.update();
+    });
+  }
+
+
+  init() {
+
+    this.initCtx();
+    this.initData();
+    this.initView();
+  }
+
+  initCtx() {
+    this.canvasWidth = this.wrap.nativeElement.clientWidth;
+    this.canvasHeight = this.wrap.nativeElement.clientHeight;
+    this.canvas.nativeElement.width = this.wrap.nativeElement.clientWidth;
+    this.canvas.nativeElement.height = this.wrap.nativeElement.clientHeight;
+
+
+    this.ctx = this.canvas.nativeElement.getContext('2d');
+    this.canvas.nativeElement.width = this.canvasWidth;
+    this.canvas.nativeElement.height = this.canvasHeight;
+
+    window['curCtx'] = this.ctx;
+  }
+
+
+
+
+
+
+  updateItem(item) {
+    if (item) {
+      item.update();
+    }
+  }
+
+  updateArr(arr) {
+    if (!arr) {
+      return;
+    }
+    for (let i = 0; i < arr.length; i++) {
+      arr[i].update(this);
+    }
+  }
+
+
+
+
+
+
+
+  initListener() {
+
+    this.winResizeEventStream
+      .pipe(debounceTime(500))
+      .subscribe(data => {
+        this.renderAfterResize();
+      });
+
+
+    // ---------------------------------------------
+    const setParentOffset = () => {
+      const rect = this.canvas.nativeElement.getBoundingClientRect();
+      this.canvasLeft = rect.left;
+      this.canvasTop = rect.top;
+    };
+    const setMxMyByTouch = (event) => {
+      if (event.touches.length <= 0) {
+        return;
+      }
+      if (this.canvasLeft == null) {
+        setParentOffset();
+      }
+      this.mx = event.touches[0].pageX - this.canvasLeft;
+      this.my = event.touches[0].pageY - this.canvasTop;
+    };
+
+    const setMxMyByMouse = (event) => {
+      this.mx = event.offsetX;
+      this.my = event.offsetY;
+    };
+    // ---------------------------------------------
+
+
+    let firstTouch = true;
+
+    const touchDownFunc = (e) => {
+      if (firstTouch) {
+        firstTouch = false;
+        removeMouseListener();
+      }
+      setMxMyByTouch(e);
+      this.mapDown(e);
+    };
+    const touchMoveFunc = (e) => {
+      setMxMyByTouch(e);
+      this.mapMove(e);
+    };
+    const touchUpFunc = (e) => {
+      setMxMyByTouch(e);
+      this.mapUp(e);
+    };
+
+    const mouseDownFunc = (e) => {
+      if (firstTouch) {
+        firstTouch = false;
+        removeTouchListener();
+      }
+      setMxMyByMouse(e);
+      this.mapDown(e);
+    };
+    const mouseMoveFunc = (e) => {
+      setMxMyByMouse(e);
+      this.mapMove(e);
+    };
+    const mouseUpFunc = (e) => {
+      setMxMyByMouse(e);
+      this.mapUp(e);
+    };
+
+
+    const element = this.canvas.nativeElement;
+
+    const addTouchListener = () => {
+      element.addEventListener('touchstart', touchDownFunc);
+      element.addEventListener('touchmove', touchMoveFunc);
+      element.addEventListener('touchend', touchUpFunc);
+      element.addEventListener('touchcancel', touchUpFunc);
+    };
+    const removeTouchListener = () => {
+      element.removeEventListener('touchstart', touchDownFunc);
+      element.removeEventListener('touchmove', touchMoveFunc);
+      element.removeEventListener('touchend', touchUpFunc);
+      element.removeEventListener('touchcancel', touchUpFunc);
+    };
+
+    const addMouseListener = () => {
+      element.addEventListener('mousedown', mouseDownFunc);
+      element.addEventListener('mousemove', mouseMoveFunc);
+      element.addEventListener('mouseup', mouseUpFunc);
+    };
+    const removeMouseListener = () => {
+      element.removeEventListener('mousedown', mouseDownFunc);
+      element.removeEventListener('mousemove', mouseMoveFunc);
+      element.removeEventListener('mouseup', mouseUpFunc);
+    };
+
+    addMouseListener();
+    addTouchListener();
+  }
+
+
+  playAudio(key, now = false, callback = null) {
+
+    const audio = this.audioObj[key];
+    if (audio) {
+      if (now) {
+        audio.pause();
+        audio.currentTime = 0;
+      }
+
+      if (callback) {
+        audio.onended = () => {
+          callback();
+        };
+      }
+      audio.play();
+    }
+  }
+
+
+
+  loadResources() {
+    const pr = [];
+    this.rawImages.forEach((value, key) => {// 预加载图片
+
+      const p = this.preload(value)
+        .then(img => {
+          this.images.set(key, img);
+        })
+        .catch(err => console.log(err));
+
+      pr.push(p);
+    });
+
+    this.rawAudios.forEach((value, key) => {// 预加载音频
+
+      const a = this.preloadAudio(value)
+        .then(() => {
+          // this.images.set(key, img);
+        })
+        .catch(err => console.log(err));
+
+      pr.push(a);
+    });
+    return Promise.all(pr);
+  }
+
+  preload(url) {
+    return new Promise((resolve, reject) => {
+      const img = new Image();
+      // img.crossOrigin = "anonymous";
+      img.onload = () => resolve(img);
+      img.onerror = reject;
+      img.src = url;
+    });
+  }
+
+  preloadAudio(url) {
+    return new Promise((resolve, reject) => {
+      const audio = new Audio();
+      audio.oncanplay = (a) => {
+        resolve();
+      };
+      audio.onerror = () => {
+        reject();
+      };
+      audio.src = url;
+      audio.load();
+    });
+  }
+
+
+  renderAfterResize() {
+    this.canvasWidth = this.wrap.nativeElement.clientWidth;
+    this.canvasHeight = this.wrap.nativeElement.clientHeight;
+    this.init();
+  }
+
+
+
+
+
+  checkClickTarget(target) {
+
+    const rect = target.getBoundingBox();
+
+    if (this.checkPointInRect(this.mx, this.my, rect)) {
+      return true;
+    }
+    return false;
+  }
+
+  getWorlRect(target) {
+
+    let rect = target.getBoundingBox();
+
+    if (target.parent) {
+
+      const pRect = this.getWorlRect(target.parent);
+      rect.x += pRect.x;
+      rect.y += pRect.y;
+    }
+    return rect;
+  }
+
+  checkPointInRect(x, y, rect) {
+    if (x >= rect.x && x <= rect.x + rect.width) {
+      if (y >= rect.y && y <= rect.y + rect.height) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+
+
+
+
+  addUrlToAudioObj(key, url = null, vlomue = 1, loop = false, callback = null) {
+
+    const audioObj = this.audioObj;
+
+    if (url == null) {
+      url = key;
+    }
+
+    this.rawAudios.set(key, url);
+
+    const audio = new Audio();
+    audio.src = url;
+    audio.load();
+    audio.loop = loop;
+    audio.volume = vlomue;
+
+    audioObj[key] = audio;
+
+    if (callback) {
+      audio.onended = () => {
+        callback();
+      };
+    }
+  }
+
+  addUrlToImages(url) {
+    this.rawImages.set(url, url);
+  }
+
+
+
+
+
+
+  // ======================================================编写区域==========================================================================
+
+
+
+
+
+  /**
+   * 添加默认数据 便于无数据时的展示
+   */
+  initDefaultData() {
+
+    if (!this.data.pic_url) {
+      this.data.pic_url = 'assets/play/default/pic.jpg';
+      this.data.pic_url_2 = 'assets/play/default/pic.jpg';
+    }
+  }
+
+
+  /**
+   * 添加预加载图片
+   */
+  initImg() {
+
+    this.addUrlToImages(this.data.pic_url);
+    this.addUrlToImages(this.data.pic_url_2);
+
+  }
+
+  /**
+   * 添加预加载音频
+   */
+  initAudio() {
+
+    // 音频资源
+    this.addUrlToAudioObj(this.data.audio_url);
+    this.addUrlToAudioObj(this.data.audio_url_2);
+
+    // 音效
+    this.addUrlToAudioObj('click', this.rawAudios.get('click'), 0.3);
+
+  }
+
+
+
+  /**
+   * 初始化数据
+   */
+  initData() {
+
+    const sx = this.canvasWidth / this.canvasBaseW;
+    const sy = this.canvasHeight / this.canvasBaseH;
+    const s = Math.min(sx, sy);
+    this.mapScale = s;
+
+    // this.mapScale = sx;
+    // this.mapScale = sy;
+
+
+    this.renderArr = [];
+
+
+
+  }
+
+
+
+  /**
+   * 初始化试图
+   */
+  initView() {
+
+
+    this.initPic();
+
+    this.initBottomPart();
+
+  }
+
+  initBottomPart() {
+
+    const btnLeft = new MySprite();
+    btnLeft.init(this.images.get('btn_left'));
+    btnLeft.x = this.canvasWidth - 150 * this.mapScale;
+    btnLeft.y = this.canvasHeight - 100 * this.mapScale;
+
+    btnLeft.setScaleXY(this.mapScale);
+
+    this.renderArr.push(btnLeft);
+
+    this.btnLeft = btnLeft;
+
+
+
+    const btnRight = new MySprite();
+    btnRight.init(this.images.get('btn_right'));
+    btnRight.x = this.canvasWidth - 50 * this.mapScale;
+    btnRight.y = this.canvasHeight - 100 * this.mapScale;
+    btnRight.setScaleXY(this.mapScale);
+
+    this.renderArr.push(btnRight);
+
+    this.btnRight = btnRight;
+  }
+
+  initPic() {
+
+    const maxW = this.canvasWidth * 0.7;
+
+    const pic1 = new MySprite();
+    pic1.init(this.images.get(this.data.pic_url));
+    pic1.x = this.canvasWidth / 2;
+    pic1.y = this.canvasHeight / 2;
+    pic1.setScaleXY(maxW / pic1.width);
+
+    this.renderArr.push(pic1);
+    this.pic1 = pic1;
+
+
+    const label1 = new Label();
+    label1.text = this.data.text;
+    label1.textAlign = 'center';
+    label1.fontSize = 50;
+    label1.fontName = 'BRLNSDB';
+    label1.fontColor = '#ffffff';
+
+    pic1.addChild(label1);
+
+
+
+
+
+    const pic2 = new MySprite();
+    pic2.init(this.images.get(this.data.pic_url_2));
+    pic2.x = this.canvasWidth / 2 + this.canvasWidth;
+    pic2.y = this.canvasHeight / 2;
+    pic2.setScaleXY(maxW / pic2.width);
+
+    this.renderArr.push(pic2);
+    this.pic2 = pic2;
+
+    this.curPic = pic1;
+  }
+
+
+  btnLeftClicked() {
+
+    this.lastPage();
+  }
+
+  btnRightClicked() {
+
+    this.nextPage();
+  }
+
+  lastPage() {
+
+    if (this.curPic == this.pic1) {
+      return;
+    }
+
+    this.canTouch = false;
+
+    const moveLen = this.canvasWidth;
+    tweenChange(this.pic1, {x: this.pic1.x + moveLen}, 1);
+    tweenChange(this.pic2, {x: this.pic2.x + moveLen}, 1, () => {
+      this.canTouch = true;
+      this.curPic = this.pic1;
+    });
+  }
+
+  nextPage() {
+
+    if (this.curPic == this.pic2) {
+      return;
+    }
+
+    this.canTouch = false;
+
+    const moveLen = this.canvasWidth;
+    tweenChange(this.pic1, {x: this.pic1.x - moveLen}, 1);
+    tweenChange(this.pic2, {x: this.pic2.x - moveLen}, 1, () => {
+      this.canTouch = true;
+      this.curPic = this.pic2;
+    });
+  }
+
+  pic1Clicked() {
+    this.playAudio(this.data.audio_url);
+  }
+
+  pic2Clicked() {
+    this.playAudio(this.data.audio_url_2);
+  }
+
+
+
+
+
+  mapDown(event) {
+
+    if (!this.canTouch) {
+      return;
+    }
+
+    if ( this.checkClickTarget(this.btnLeft) ) {
+      this.btnLeftClicked();
+      return;
+    }
+
+    if ( this.checkClickTarget(this.btnRight) ) {
+      this.btnRightClicked();
+      return;
+    }
+
+    if ( this.checkClickTarget(this.pic1) ) {
+      this.pic1Clicked();
+      return;
+    }
+
+    if ( this.checkClickTarget(this.pic2) ) {
+      this.pic2Clicked();
+      return;
+    }
+
+  }
+
+  mapMove(event) {
+
+  }
+
+  mapUp(event) {
+
+  }
+
+
+
+  update() {
+
+    // ----------------------------------------------------------
+    this.animationId = window.requestAnimationFrame(this.update.bind(this));
+    // 清除画布内容
+    this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
+    // tween 更新动画
+    TWEEN.update();
+    // ----------------------------------------------------------
+
+
+
+    this.updateArr(this.renderArr);
+
+
+  }
+
+
+
+}
diff --git a/form/src/app/play/resources.js b/form/src/app/play/resources.js
new file mode 100644
index 0000000000000000000000000000000000000000..d9b584c92bc24d0c04374cb1568478fdbefba9c7
--- /dev/null
+++ b/form/src/app/play/resources.js
@@ -0,0 +1,18 @@
+const res = [
+
+  // ['bg', "assets/play/bg.jpg"],
+  ['btn_left', "assets/play/btn_left.png"],
+  ['btn_right', "assets/play/btn_right.png"],
+  // ['text_bg', "assets/play/text_bg.png"],
+
+];
+
+
+
+const resAudio = [
+
+  ['click', "assets/play/music/click.mp3"],
+
+];
+
+export {res, resAudio};
diff --git a/form/src/app/style/colors.scss b/form/src/app/style/colors.scss
new file mode 100644
index 0000000000000000000000000000000000000000..13a0b006f187cb8e40157579e8f7a66014aec551
--- /dev/null
+++ b/form/src/app/style/colors.scss
@@ -0,0 +1,16 @@
+$color_black: #111111;
+$color_gray: #aaaaaa;
+$color_silver: #dddddd;
+$color_navy: #001f3f;
+$color_blue: #0074d9;
+$color_aqua: #7fdbff;
+$color_teal: #39cccc;
+$color_olive: #3d9970;
+$color_green: #2ecc40;
+$color_lime: #01ff70;
+$color_yellow: #ffdc00;
+$color_orange: #ff851b;
+$color_red: #ff4136;
+$color_maroon: #85144b;
+$color_fuchsia: #f012be;
+$color_purple: #b10dc9;
diff --git a/form/src/app/style/common_mixin.css b/form/src/app/style/common_mixin.css
new file mode 100644
index 0000000000000000000000000000000000000000..3e7ed8cfc0608eda7287079af94a1e9fd11d6f68
--- /dev/null
+++ b/form/src/app/style/common_mixin.css
@@ -0,0 +1,6 @@
+/*
+global css to mixin
+*/
+
+
+
diff --git a/form/src/assets/.gitkeep b/form/src/assets/.gitkeep
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/form/src/assets/common/arrow.png b/form/src/assets/common/arrow.png
new file mode 100644
index 0000000000000000000000000000000000000000..d69b353b4da29da2a821723bee68d51e9aaeee05
Binary files /dev/null and b/form/src/assets/common/arrow.png differ
diff --git a/form/src/assets/common/arrow_right.png b/form/src/assets/common/arrow_right.png
new file mode 100644
index 0000000000000000000000000000000000000000..65859da4bfbcf54c25e481b232ce21f9184c7425
Binary files /dev/null and b/form/src/assets/common/arrow_right.png differ
diff --git a/form/src/assets/common/arrow_top.png b/form/src/assets/common/arrow_top.png
new file mode 100644
index 0000000000000000000000000000000000000000..d4bcadc92d0400e6d2bdbb4c47fb4b7c55ed77ea
Binary files /dev/null and b/form/src/assets/common/arrow_top.png differ
diff --git a/form/src/assets/default/video-not-upload.jpg b/form/src/assets/default/video-not-upload.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..31fbbcc7703958c0153d883aeded25e2a53e0542
Binary files /dev/null and b/form/src/assets/default/video-not-upload.jpg differ
diff --git a/form/src/assets/font/BRLNSB.TTF b/form/src/assets/font/BRLNSB.TTF
new file mode 100644
index 0000000000000000000000000000000000000000..509477ede1b11d912a8f5503001a917720522d08
Binary files /dev/null and b/form/src/assets/font/BRLNSB.TTF differ
diff --git a/form/src/assets/font/BRLNSDB.TTF b/form/src/assets/font/BRLNSDB.TTF
new file mode 100644
index 0000000000000000000000000000000000000000..e5f34b2f342ffcd820a8a0956af3ab36bb1f9e1f
Binary files /dev/null and b/form/src/assets/font/BRLNSDB.TTF differ
diff --git a/form/src/assets/font/BRLNSR.TTF b/form/src/assets/font/BRLNSR.TTF
new file mode 100644
index 0000000000000000000000000000000000000000..efaf22ff70a2b17519804a3f616208e73ff5896a
Binary files /dev/null and b/form/src/assets/font/BRLNSR.TTF differ
diff --git a/form/src/assets/font/GOTHIC.TTF b/form/src/assets/font/GOTHIC.TTF
new file mode 100644
index 0000000000000000000000000000000000000000..c60a324123eaaeab58c400c139bcabbc6d0d2a9f
Binary files /dev/null and b/form/src/assets/font/GOTHIC.TTF differ
diff --git a/form/src/assets/font/GOTHICB.TTF b/form/src/assets/font/GOTHICB.TTF
new file mode 100644
index 0000000000000000000000000000000000000000..d3577b960846dd2a9d2cbaceab7b8715b3c5fd54
Binary files /dev/null and b/form/src/assets/font/GOTHICB.TTF differ
diff --git a/form/src/assets/font/GOTHICBI.TTF b/form/src/assets/font/GOTHICBI.TTF
new file mode 100644
index 0000000000000000000000000000000000000000..d01cefabe3f5aac0dca611b750d07215cfe00dbf
Binary files /dev/null and b/form/src/assets/font/GOTHICBI.TTF differ
diff --git a/form/src/assets/font/GOTHICI.TTF b/form/src/assets/font/GOTHICI.TTF
new file mode 100644
index 0000000000000000000000000000000000000000..777a6d87f5de328998284ce7c4a0df169a6474a2
Binary files /dev/null and b/form/src/assets/font/GOTHICI.TTF differ
diff --git a/form/src/assets/font/MMTextBook-Bold.otf b/form/src/assets/font/MMTextBook-Bold.otf
new file mode 100644
index 0000000000000000000000000000000000000000..8e378d817bec7b1acd2a82cfeabcb53c90fd5960
Binary files /dev/null and b/form/src/assets/font/MMTextBook-Bold.otf differ
diff --git a/form/src/assets/font/MMTextBook-BoldItalic.otf b/form/src/assets/font/MMTextBook-BoldItalic.otf
new file mode 100644
index 0000000000000000000000000000000000000000..04d6fb6e1ca10bec6d4df267184f8179b9ba1468
Binary files /dev/null and b/form/src/assets/font/MMTextBook-BoldItalic.otf differ
diff --git a/form/src/assets/font/MMTextBook-Italic.otf b/form/src/assets/font/MMTextBook-Italic.otf
new file mode 100644
index 0000000000000000000000000000000000000000..05e5aaa409dc78ee5c98167653a7ed18eab92b49
Binary files /dev/null and b/form/src/assets/font/MMTextBook-Italic.otf differ
diff --git a/form/src/assets/font/MMTextBook.otf b/form/src/assets/font/MMTextBook.otf
new file mode 100644
index 0000000000000000000000000000000000000000..f8ca9b645c27bcd4bfe1f351f4ff30f4558e302f
Binary files /dev/null and b/form/src/assets/font/MMTextBook.otf differ
diff --git a/form/src/assets/libs/audio-recorder/lame.min.js b/form/src/assets/libs/audio-recorder/lame.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..32bfe8c66168578389d30380ca553d2b2150a47b
--- /dev/null
+++ b/form/src/assets/libs/audio-recorder/lame.min.js
@@ -0,0 +1,308 @@
+function lamejs(){function Y(d){return new Int32Array(d)}function H(d){return new Float32Array(d)}function ra(d){if(1==d.length)return H(d[0]);var k=d[0];d=d.slice(1);for(var q=[],C=0;C<k;C++)q.push(ra(d));return q}function db(d){if(1==d.length)return Y(d[0]);var k=d[0];d=d.slice(1);for(var q=[],C=0;C<k;C++)q.push(db(d));return q}function vc(d){if(1==d.length)return new Int16Array(d[0]);var k=d[0];d=d.slice(1);for(var q=[],C=0;C<k;C++)q.push(vc(d));return q}function Zb(d){if(1==d.length)return Array(d[0]);
+var k=d[0];d=d.slice(1);for(var q=[],C=0;C<k;C++)q.push(Zb(d));return q}function sa(d){this.ordinal=d}function M(d){this.ordinal=d}function ka(d){this.ordinal=function(){return d}}function Fc(){this.getLameVersion=function(){return"3.98.4"};this.getLameShortVersion=function(){return"3.98.4"};this.getLameVeryShortVersion=function(){return"LAME3.98r"};this.getPsyVersion=function(){return"0.93"};this.getLameUrl=function(){return"http://www.mp3dev.org/"};this.getLameOsBitness=function(){return"32bits"}}
+function Gc(){function d(b,g,a,h,k,e,n,c,f,E,z,w,A,v,p){this.vbr_q=b;this.quant_comp=g;this.quant_comp_s=a;this.expY=h;this.st_lrm=k;this.st_s=e;this.masking_adj=n;this.masking_adj_short=c;this.ath_lower=f;this.ath_curve=E;this.ath_sensitivity=z;this.interch=w;this.safejoint=A;this.sfb21mod=v;this.msfix=p}function k(b,g,a,d,k,e,n,c,f,E,z,w,A,v){this.quant_comp=g;this.quant_comp_s=a;this.safejoint=d;this.nsmsfix=k;this.st_lrm=e;this.st_s=n;this.nsbass=c;this.scale=f;this.masking_adj=E;this.ath_lower=
+z;this.ath_curve=w;this.interch=A;this.sfscale=v}function q(b,g,a){var d=b.VBR==M.vbr_rh?t:r,k=b.VBR_q_frac,e=d[g],d=d[g+1];e.st_lrm+=k*(d.st_lrm-e.st_lrm);e.st_s+=k*(d.st_s-e.st_s);e.masking_adj+=k*(d.masking_adj-e.masking_adj);e.masking_adj_short+=k*(d.masking_adj_short-e.masking_adj_short);e.ath_lower+=k*(d.ath_lower-e.ath_lower);e.ath_curve+=k*(d.ath_curve-e.ath_curve);e.ath_sensitivity+=k*(d.ath_sensitivity-e.ath_sensitivity);e.interch+=k*(d.interch-e.interch);e.msfix+=k*(d.msfix-e.msfix);d=
+e.vbr_q;0>d&&(d=0);9<d&&(d=9);b.VBR_q=d;b.VBR_q_frac=0;0!=a?b.quant_comp=e.quant_comp:0<Math.abs(b.quant_comp- -1)||(b.quant_comp=e.quant_comp);0!=a?b.quant_comp_short=e.quant_comp_s:0<Math.abs(b.quant_comp_short- -1)||(b.quant_comp_short=e.quant_comp_s);0!=e.expY&&(b.experimentalY=0!=e.expY);0!=a?b.internal_flags.nsPsy.attackthre=e.st_lrm:0<Math.abs(b.internal_flags.nsPsy.attackthre- -1)||(b.internal_flags.nsPsy.attackthre=e.st_lrm);0!=a?b.internal_flags.nsPsy.attackthre_s=e.st_s:0<Math.abs(b.internal_flags.nsPsy.attackthre_s-
+-1)||(b.internal_flags.nsPsy.attackthre_s=e.st_s);0!=a?b.maskingadjust=e.masking_adj:0<Math.abs(b.maskingadjust-0)||(b.maskingadjust=e.masking_adj);0!=a?b.maskingadjust_short=e.masking_adj_short:0<Math.abs(b.maskingadjust_short-0)||(b.maskingadjust_short=e.masking_adj_short);0!=a?b.ATHlower=-e.ath_lower/10:0<Math.abs(10*-b.ATHlower-0)||(b.ATHlower=-e.ath_lower/10);0!=a?b.ATHcurve=e.ath_curve:0<Math.abs(b.ATHcurve- -1)||(b.ATHcurve=e.ath_curve);0!=a?b.athaa_sensitivity=e.ath_sensitivity:0<Math.abs(b.athaa_sensitivity-
+-1)||(b.athaa_sensitivity=e.ath_sensitivity);0<e.interch&&(0!=a?b.interChRatio=e.interch:0<Math.abs(b.interChRatio- -1)||(b.interChRatio=e.interch));0<e.safejoint&&(b.exp_nspsytune|=e.safejoint);0<e.sfb21mod&&(b.exp_nspsytune|=e.sfb21mod<<20);0!=a?b.msfix=e.msfix:0<Math.abs(b.msfix- -1)||(b.msfix=e.msfix);0==a&&(b.VBR_q=g,b.VBR_q_frac=k)}function C(b,d,a){var h=B.nearestBitrateFullIndex(d);b.VBR=M.vbr_abr;b.VBR_mean_bitrate_kbps=d;b.VBR_mean_bitrate_kbps=Math.min(b.VBR_mean_bitrate_kbps,320);b.VBR_mean_bitrate_kbps=
+Math.max(b.VBR_mean_bitrate_kbps,8);b.brate=b.VBR_mean_bitrate_kbps;320<b.VBR_mean_bitrate_kbps&&(b.disable_reservoir=!0);0<g[h].safejoint&&(b.exp_nspsytune|=2);0<g[h].sfscale&&(b.internal_flags.noise_shaping=2);if(0<Math.abs(g[h].nsbass)){var k=int(4*g[h].nsbass);0>k&&(k+=64);b.exp_nspsytune|=k<<2}0!=a?b.quant_comp=g[h].quant_comp:0<Math.abs(b.quant_comp- -1)||(b.quant_comp=g[h].quant_comp);0!=a?b.quant_comp_short=g[h].quant_comp_s:0<Math.abs(b.quant_comp_short- -1)||(b.quant_comp_short=g[h].quant_comp_s);
+0!=a?b.msfix=g[h].nsmsfix:0<Math.abs(b.msfix- -1)||(b.msfix=g[h].nsmsfix);0!=a?b.internal_flags.nsPsy.attackthre=g[h].st_lrm:0<Math.abs(b.internal_flags.nsPsy.attackthre- -1)||(b.internal_flags.nsPsy.attackthre=g[h].st_lrm);0!=a?b.internal_flags.nsPsy.attackthre_s=g[h].st_s:0<Math.abs(b.internal_flags.nsPsy.attackthre_s- -1)||(b.internal_flags.nsPsy.attackthre_s=g[h].st_s);0!=a?b.scale=g[h].scale:0<Math.abs(b.scale- -1)||(b.scale=g[h].scale);0!=a?b.maskingadjust=g[h].masking_adj:0<Math.abs(b.maskingadjust-
+0)||(b.maskingadjust=g[h].masking_adj);0<g[h].masking_adj?0!=a?b.maskingadjust_short=.9*g[h].masking_adj:0<Math.abs(b.maskingadjust_short-0)||(b.maskingadjust_short=.9*g[h].masking_adj):0!=a?b.maskingadjust_short=1.1*g[h].masking_adj:0<Math.abs(b.maskingadjust_short-0)||(b.maskingadjust_short=1.1*g[h].masking_adj);0!=a?b.ATHlower=-g[h].ath_lower/10:0<Math.abs(10*-b.ATHlower-0)||(b.ATHlower=-g[h].ath_lower/10);0!=a?b.ATHcurve=g[h].ath_curve:0<Math.abs(b.ATHcurve- -1)||(b.ATHcurve=g[h].ath_curve);0!=
+a?b.interChRatio=g[h].interch:0<Math.abs(b.interChRatio- -1)||(b.interChRatio=g[h].interch);return d}var B;this.setModules=function(b){B=b};var t=[new d(0,9,9,0,5.2,125,-4.2,-6.3,4.8,1,0,0,2,21,.97),new d(1,9,9,0,5.3,125,-3.6,-5.6,4.5,1.5,0,0,2,21,1.35),new d(2,9,9,0,5.6,125,-2.2,-3.5,2.8,2,0,0,2,21,1.49),new d(3,9,9,1,5.8,130,-1.8,-2.8,2.6,3,-4,0,2,20,1.64),new d(4,9,9,1,6,135,-.7,-1.1,1.1,3.5,-8,0,2,0,1.79),new d(5,9,9,1,6.4,140,.5,.4,-7.5,4,-12,2E-4,0,0,1.95),new d(6,9,9,1,6.6,145,.67,.65,-14.7,
+6.5,-19,4E-4,0,0,2.3),new d(7,9,9,1,6.6,145,.8,.75,-19.7,8,-22,6E-4,0,0,2.7),new d(8,9,9,1,6.6,145,1.2,1.15,-27.5,10,-23,7E-4,0,0,0),new d(9,9,9,1,6.6,145,1.6,1.6,-36,11,-25,8E-4,0,0,0),new d(10,9,9,1,6.6,145,2,2,-36,12,-25,8E-4,0,0,0)],r=[new d(0,9,9,0,4.2,25,-7,-4,7.5,1,0,0,2,26,.97),new d(1,9,9,0,4.2,25,-5.6,-3.6,4.5,1.5,0,0,2,21,1.35),new d(2,9,9,0,4.2,25,-4.4,-1.8,2,2,0,0,2,18,1.49),new d(3,9,9,1,4.2,25,-3.4,-1.25,1.1,3,-4,0,2,15,1.64),new d(4,9,9,1,4.2,25,-2.2,.1,0,3.5,-8,0,2,0,1.79),new d(5,
+9,9,1,4.2,25,-1,1.65,-7.7,4,-12,2E-4,0,0,1.95),new d(6,9,9,1,4.2,25,-0,2.47,-7.7,6.5,-19,4E-4,0,0,2),new d(7,9,9,1,4.2,25,.5,2,-14.5,8,-22,6E-4,0,0,2),new d(8,9,9,1,4.2,25,1,2.4,-22,10,-23,7E-4,0,0,2),new d(9,9,9,1,4.2,25,1.5,2.95,-30,11,-25,8E-4,0,0,2),new d(10,9,9,1,4.2,25,2,2.95,-36,12,-30,8E-4,0,0,2)],g=[new k(8,9,9,0,0,6.6,145,0,.95,0,-30,11,.0012,1),new k(16,9,9,0,0,6.6,145,0,.95,0,-25,11,.001,1),new k(24,9,9,0,0,6.6,145,0,.95,0,-20,11,.001,1),new k(32,9,9,0,0,6.6,145,0,.95,0,-15,11,.001,1),
+new k(40,9,9,0,0,6.6,145,0,.95,0,-10,11,9E-4,1),new k(48,9,9,0,0,6.6,145,0,.95,0,-10,11,9E-4,1),new k(56,9,9,0,0,6.6,145,0,.95,0,-6,11,8E-4,1),new k(64,9,9,0,0,6.6,145,0,.95,0,-2,11,8E-4,1),new k(80,9,9,0,0,6.6,145,0,.95,0,0,8,7E-4,1),new k(96,9,9,0,2.5,6.6,145,0,.95,0,1,5.5,6E-4,1),new k(112,9,9,0,2.25,6.6,145,0,.95,0,2,4.5,5E-4,1),new k(128,9,9,0,1.95,6.4,140,0,.95,0,3,4,2E-4,1),new k(160,9,9,1,1.79,6,135,0,.95,-2,5,3.5,0,1),new k(192,9,9,1,1.49,5.6,125,0,.97,-4,7,3,0,0),new k(224,9,9,1,1.25,5.2,
+125,0,.98,-6,9,2,0,0),new k(256,9,9,1,.97,5.2,125,0,1,-8,10,1,0,0),new k(320,9,9,1,.9,5.2,125,0,1,-10,12,0,0,0)];this.apply_preset=function(b,d,a){switch(d){case V.R3MIX:d=V.V3;b.VBR=M.vbr_mtrh;break;case V.MEDIUM:d=V.V4;b.VBR=M.vbr_rh;break;case V.MEDIUM_FAST:d=V.V4;b.VBR=M.vbr_mtrh;break;case V.STANDARD:d=V.V2;b.VBR=M.vbr_rh;break;case V.STANDARD_FAST:d=V.V2;b.VBR=M.vbr_mtrh;break;case V.EXTREME:d=V.V0;b.VBR=M.vbr_rh;break;case V.EXTREME_FAST:d=V.V0;b.VBR=M.vbr_mtrh;break;case V.INSANE:return d=
+320,b.preset=d,C(b,d,a),b.VBR=M.vbr_off,d}b.preset=d;switch(d){case V.V9:return q(b,9,a),d;case V.V8:return q(b,8,a),d;case V.V7:return q(b,7,a),d;case V.V6:return q(b,6,a),d;case V.V5:return q(b,5,a),d;case V.V4:return q(b,4,a),d;case V.V3:return q(b,3,a),d;case V.V2:return q(b,2,a),d;case V.V1:return q(b,1,a),d;case V.V0:return q(b,0,a),d}if(8<=d&&320>=d)return C(b,d,a);b.preset=0;return d}}function O(){function d(g,b,l,a,h,k){for(;0!=h--;)l[a]=1E-10+g[b+0]*k[0]-l[a-1]*k[1]+g[b-1]*k[2]-l[a-2]*k[3]+
+g[b-2]*k[4]-l[a-3]*k[5]+g[b-3]*k[6]-l[a-4]*k[7]+g[b-4]*k[8]-l[a-5]*k[9]+g[b-5]*k[10]-l[a-6]*k[11]+g[b-6]*k[12]-l[a-7]*k[13]+g[b-7]*k[14]-l[a-8]*k[15]+g[b-8]*k[16]-l[a-9]*k[17]+g[b-9]*k[18]-l[a-10]*k[19]+g[b-10]*k[20],++a,++b}function k(d,b,l,a,h,k){for(;0!=h--;)l[a]=d[b+0]*k[0]-l[a-1]*k[1]+d[b-1]*k[2]-l[a-2]*k[3]+d[b-2]*k[4],++a,++b}function q(d){return d*d}var C=O.RMS_WINDOW_TIME_NUMERATOR,B=O.RMS_WINDOW_TIME_DENOMINATOR,t=[[.038575994352,-3.84664617118067,-.02160367184185,7.81501653005538,-.00123395316851,
+-11.34170355132042,-9.291677959E-5,13.05504219327545,-.01655260341619,-12.28759895145294,.02161526843274,9.4829380631979,-.02074045215285,-5.87257861775999,.00594298065125,2.75465861874613,.00306428023191,-.86984376593551,1.2025322027E-4,.13919314567432,.00288463683916],[.0541865640643,-3.47845948550071,-.02911007808948,6.36317777566148,-.00848709379851,-8.54751527471874,-.00851165645469,9.4769360780128,-.00834990904936,-8.81498681370155,.02245293253339,6.85401540936998,-.02596338512915,-4.39470996079559,
+.01624864962975,2.19611684890774,-.00240879051584,-.75104302451432,.00674613682247,.13149317958808,-.00187763777362],[.15457299681924,-2.37898834973084,-.09331049056315,2.84868151156327,-.06247880153653,-2.64577170229825,.02163541888798,2.23697657451713,-.05588393329856,-1.67148153367602,.04781476674921,1.00595954808547,.00222312597743,-.45953458054983,.03174092540049,.16378164858596,-.01390589421898,-.05032077717131,.00651420667831,.0234789740702,-.00881362733839],[.30296907319327,-1.61273165137247,
+-.22613988682123,1.0797749225997,-.08587323730772,-.2565625775407,.03282930172664,-.1627671912044,-.00915702933434,-.22638893773906,-.02364141202522,.39120800788284,-.00584456039913,-.22138138954925,.06276101321749,.04500235387352,-8.28086748E-6,.02005851806501,.00205861885564,.00302439095741,-.02950134983287],[.33642304856132,-1.49858979367799,-.2557224142557,.87350271418188,-.11828570177555,.12205022308084,.11921148675203,-.80774944671438,-.07834489609479,.47854794562326,-.0046997791438,-.12453458140019,
+-.0058950022444,-.04067510197014,.05724228140351,.08333755284107,.00832043980773,-.04237348025746,-.0163538138454,.02977207319925,-.0176017656815],[.4491525660845,-.62820619233671,-.14351757464547,.29661783706366,-.22784394429749,-.372563729424,-.01419140100551,.00213767857124,.04078262797139,-.42029820170918,-.12398163381748,.22199650564824,.04097565135648,.00613424350682,.10478503600251,.06747620744683,-.01863887810927,.05784820375801,-.03193428438915,.03222754072173,.00541907748707],[.56619470757641,
+-1.04800335126349,-.75464456939302,.29156311971249,.1624213774223,-.26806001042947,.16744243493672,.00819999645858,-.18901604199609,.45054734505008,.3093178284183,-.33032403314006,-.27562961986224,.0673936833311,.00647310677246,-.04784254229033,.08647503780351,.01639907836189,-.0378898455484,.01807364323573,-.00588215443421],[.58100494960553,-.51035327095184,-.53174909058578,-.31863563325245,-.14289799034253,-.20256413484477,.17520704835522,.1472815413433,.02377945217615,.38952639978999,.15558449135573,
+-.23313271880868,-.25344790059353,-.05246019024463,.01628462406333,-.02505961724053,.06920467763959,.02442357316099,-.03721611395801,.01818801111503,-.00749618797172],[.53648789255105,-.2504987195602,-.42163034350696,-.43193942311114,-.00275953611929,-.03424681017675,.04267842219415,-.04678328784242,-.10214864179676,.26408300200955,.14590772289388,.15113130533216,-.02459864859345,-.17556493366449,-.11202315195388,-.18823009262115,-.04060034127,.05477720428674,.0478866554818,.0470440968812,-.02217936801134]],
+r=[[.98621192462708,-1.97223372919527,-1.97242384925416,.97261396931306,.98621192462708],[.98500175787242,-1.96977855582618,-1.97000351574484,.9702284756635,.98500175787242],[.97938932735214,-1.95835380975398,-1.95877865470428,.95920349965459,.97938932735214],[.97531843204928,-1.95002759149878,-1.95063686409857,.95124613669835,.97531843204928],[.97316523498161,-1.94561023566527,-1.94633046996323,.94705070426118,.97316523498161],[.96454515552826,-1.92783286977036,-1.92909031105652,.93034775234268,
+.96454515552826],[.96009142950541,-1.91858953033784,-1.92018285901082,.92177618768381,.96009142950541],[.95856916599601,-1.9154210807478,-1.91713833199203,.91885558323625,.95856916599601],[.94597685600279,-1.88903307939452,-1.89195371200558,.89487434461664,.94597685600279]];this.InitGainAnalysis=function(d,b){var l;a:{for(l=0;l<MAX_ORDER;l++)d.linprebuf[l]=d.lstepbuf[l]=d.loutbuf[l]=d.rinprebuf[l]=d.rstepbuf[l]=d.routbuf[l]=0;switch(0|b){case 48E3:d.reqindex=0;break;case 44100:d.reqindex=1;break;
+case 32E3:d.reqindex=2;break;case 24E3:d.reqindex=3;break;case 22050:d.reqindex=4;break;case 16E3:d.reqindex=5;break;case 12E3:d.reqindex=6;break;case 11025:d.reqindex=7;break;case 8E3:d.reqindex=8;break;default:l=INIT_GAIN_ANALYSIS_ERROR;break a}d.sampleWindow=0|(b*C+B-1)/B;d.lsum=0;d.rsum=0;d.totsamp=0;Ia.ill(d.A,0);l=INIT_GAIN_ANALYSIS_OK}if(l!=INIT_GAIN_ANALYSIS_OK)return INIT_GAIN_ANALYSIS_ERROR;d.linpre=MAX_ORDER;d.rinpre=MAX_ORDER;d.lstep=MAX_ORDER;d.rstep=MAX_ORDER;d.lout=MAX_ORDER;d.rout=
+MAX_ORDER;Ia.fill(d.B,0);return INIT_GAIN_ANALYSIS_OK};this.AnalyzeSamples=function(g,b,l,a,h,D,e){var n,c,f,E,z,w;if(0==D)return GAIN_ANALYSIS_OK;w=0;E=D;switch(e){case 1:a=b;h=l;break;case 2:break;default:return GAIN_ANALYSIS_ERROR}D<MAX_ORDER?(K.arraycopy(b,l,g.linprebuf,MAX_ORDER,D),K.arraycopy(a,h,g.rinprebuf,MAX_ORDER,D)):(K.arraycopy(b,l,g.linprebuf,MAX_ORDER,MAX_ORDER),K.arraycopy(a,h,g.rinprebuf,MAX_ORDER,MAX_ORDER));for(;0<E;){z=E>g.sampleWindow-g.totsamp?g.sampleWindow-g.totsamp:E;w<MAX_ORDER?
+(e=g.linpre+w,n=g.linprebuf,c=g.rinpre+w,f=g.rinprebuf,z>MAX_ORDER-w&&(z=MAX_ORDER-w)):(e=l+w,n=b,c=h+w,f=a);d(n,e,g.lstepbuf,g.lstep+g.totsamp,z,t[g.reqindex]);d(f,c,g.rstepbuf,g.rstep+g.totsamp,z,t[g.reqindex]);k(g.lstepbuf,g.lstep+g.totsamp,g.loutbuf,g.lout+g.totsamp,z,r[g.reqindex]);k(g.rstepbuf,g.rstep+g.totsamp,g.routbuf,g.rout+g.totsamp,z,r[g.reqindex]);e=g.lout+g.totsamp;n=g.loutbuf;c=g.rout+g.totsamp;f=g.routbuf;for(var A=z%8;0!=A--;)g.lsum+=q(n[e++]),g.rsum+=q(f[c++]);for(A=z/8;0!=A--;)g.lsum+=
+q(n[e+0])+q(n[e+1])+q(n[e+2])+q(n[e+3])+q(n[e+4])+q(n[e+5])+q(n[e+6])+q(n[e+7]),e+=8,g.rsum+=q(f[c+0])+q(f[c+1])+q(f[c+2])+q(f[c+3])+q(f[c+4])+q(f[c+5])+q(f[c+6])+q(f[c+7]),c+=8;E-=z;w+=z;g.totsamp+=z;g.totsamp==g.sampleWindow&&(e=10*O.STEPS_per_dB*Math.log10((g.lsum+g.rsum)/g.totsamp*.5+1E-37),e=0>=e?0:0|e,e>=g.A.length&&(e=g.A.length-1),g.A[e]++,g.lsum=g.rsum=0,K.arraycopy(g.loutbuf,g.totsamp,g.loutbuf,0,MAX_ORDER),K.arraycopy(g.routbuf,g.totsamp,g.routbuf,0,MAX_ORDER),K.arraycopy(g.lstepbuf,g.totsamp,
+g.lstepbuf,0,MAX_ORDER),K.arraycopy(g.rstepbuf,g.totsamp,g.rstepbuf,0,MAX_ORDER),g.totsamp=0);if(g.totsamp>g.sampleWindow)return GAIN_ANALYSIS_ERROR}D<MAX_ORDER?(K.arraycopy(g.linprebuf,D,g.linprebuf,0,MAX_ORDER-D),K.arraycopy(g.rinprebuf,D,g.rinprebuf,0,MAX_ORDER-D),K.arraycopy(b,l,g.linprebuf,MAX_ORDER-D,D),K.arraycopy(a,h,g.rinprebuf,MAX_ORDER-D,D)):(K.arraycopy(b,l+D-MAX_ORDER,g.linprebuf,0,MAX_ORDER),K.arraycopy(a,h+D-MAX_ORDER,g.rinprebuf,0,MAX_ORDER));return GAIN_ANALYSIS_OK};this.GetTitleGain=
+function(d){var b;b=d.A;var l=d.A.length,a,h=0;for(a=0;a<l;a++)h+=b[a];if(0==h)b=GAIN_NOT_ENOUGH_SAMPLES;else{h=0|Math.ceil(h*(1-.95));for(a=l;0<a--&&!(0>=(h-=b[a])););b=64.82-a/O.STEPS_per_dB}for(l=0;l<d.A.length;l++)d.B[l]+=d.A[l],d.A[l]=0;for(l=0;l<MAX_ORDER;l++)d.linprebuf[l]=d.lstepbuf[l]=d.loutbuf[l]=d.rinprebuf[l]=d.rstepbuf[l]=d.routbuf[l]=0;d.totsamp=0;d.lsum=d.rsum=0;return b}}function tb(){function J(a){this.bits=0|a}function k(a,c,b,d,e,p){c=.5946/c;for(a>>=1;0!=a--;)e[p++]=c>b[d++]?0:
+1,e[p++]=c>b[d++]?0:1}function q(a,c,b,d,e,p){a>>=1;var m=a%2;for(a>>=1;0!=a--;){var f,n,g,u,h,l,k;f=b[d++]*c;n=b[d++]*c;h=0|f;g=b[d++]*c;l=0|n;u=b[d++]*c;k=0|g;f+=t.adj43[h];h=0|u;n+=t.adj43[l];e[p++]=0|f;g+=t.adj43[k];e[p++]=0|n;u+=t.adj43[h];e[p++]=0|g;e[p++]=0|u}0!=m&&(f=b[d++]*c,n=b[d++]*c,f+=t.adj43[0|f],n+=t.adj43[0|n],e[p++]=0|f,e[p++]=0|n)}function C(a,c,b,d){var e,p=c,m=e=0;do{var f=a[p++],n=a[p++];e<f&&(e=f);m<n&&(m=n)}while(p<b);e<m&&(e=m);switch(e){case 0:return e;case 1:p=c;c=0;e=r.ht[1].hlen;
+do m=2*a[p+0]+a[p+1],p+=2,c+=e[m];while(p<b);d.bits+=c;return 1;case 2:case 3:p=c;c=g[e-1];e=0;m=r.ht[c].xlen;f=2==c?r.table23:r.table56;do n=a[p+0]*m+a[p+1],p+=2,e+=f[n];while(p<b);a=e&65535;e>>=16;e>a&&(e=a,c++);d.bits+=e;return c;case 4:case 5:case 6:case 7:case 8:case 9:case 10:case 11:case 12:case 13:case 14:case 15:p=c;c=g[e-1];var f=m=e=0,n=r.ht[c].xlen,h=r.ht[c].hlen,u=r.ht[c+1].hlen,l=r.ht[c+2].hlen;do{var k=a[p+0]*n+a[p+1],p=p+2;e+=h[k];m+=u[k];f+=l[k]}while(p<b);a=c;e>m&&(e=m,a++);e>f&&
+(e=f,a=c+2);d.bits+=e;return a;default:if(e>ma.IXMAX_VAL)return d.bits=ma.LARGE_BITS,-1;e-=15;for(p=24;32>p&&!(r.ht[p].linmax>=e);p++);for(m=p-8;24>m&&!(r.ht[m].linmax>=e);m++);e=m;f=65536*r.ht[e].xlen+r.ht[p].xlen;m=0;do n=a[c++],h=a[c++],0!=n&&(14<n&&(n=15,m+=f),n*=16),0!=h&&(14<h&&(h=15,m+=f),n+=h),m+=r.largetbl[n];while(c<b);a=m&65535;m>>=16;m>a&&(m=a,e=p);d.bits+=m;return e}}function B(a,c,e,b,f,p,m,n){for(var y=c.big_values,g=2;g<d.SBMAX_l+1;g++){var h=a.scalefac_band.l[g];if(h>=y)break;var l=
+f[g-2]+c.count1bits;if(e.part2_3_length<=l)break;l=new J(l);h=C(b,h,y,l);l=l.bits;e.part2_3_length<=l||(e.assign(c),e.part2_3_length=l,e.region0_count=p[g-2],e.region1_count=g-2-p[g-2],e.table_select[0]=m[g-2],e.table_select[1]=n[g-2],e.table_select[2]=h)}}var t=null;this.qupvt=null;this.setModules=function(a){t=this.qupvt=a};var ta=[[0,0],[0,0],[0,0],[0,0],[0,0],[0,1],[1,1],[1,1],[1,2],[2,2],[2,3],[2,3],[3,4],[3,4],[3,4],[4,5],[4,5],[4,6],[5,6],[5,6],[5,7],[6,7],[6,7]],g=[1,2,5,7,7,10,10,13,13,13,
+13,13,13,13,13];this.noquant_count_bits=function(a,c,e){var b=c.l3_enc,f=Math.min(576,c.max_nonzero_coeff+2>>1<<1);null!=e&&(e.sfb_count1=0);for(;1<f&&0==(b[f-1]|b[f-2]);f-=2);c.count1=f;for(var p=0,m=0;3<f;f-=4){var n;if(1<((b[f-1]|b[f-2]|b[f-3]|b[f-4])&2147483647))break;n=2*(2*(2*b[f-4]+b[f-3])+b[f-2])+b[f-1];p+=r.t32l[n];m+=r.t33l[n]}n=p;c.count1table_select=0;p>m&&(n=m,c.count1table_select=1);c.count1bits=n;c.big_values=f;if(0==f)return n;c.block_type==d.SHORT_TYPE?(p=3*a.scalefac_band.s[3],p>
+c.big_values&&(p=c.big_values),m=c.big_values):c.block_type==d.NORM_TYPE?(p=c.region0_count=a.bv_scf[f-2],m=c.region1_count=a.bv_scf[f-1],m=a.scalefac_band.l[p+m+2],p=a.scalefac_band.l[p+1],m<f&&(n=new J(n),c.table_select[2]=C(b,m,f,n),n=n.bits)):(c.region0_count=7,c.region1_count=d.SBMAX_l-1-7-1,p=a.scalefac_band.l[8],m=f,p>m&&(p=m));p=Math.min(p,f);m=Math.min(m,f);0<p&&(n=new J(n),c.table_select[0]=C(b,0,p,n),n=n.bits);p<m&&(n=new J(n),c.table_select[1]=C(b,p,m,n),n=n.bits);2==a.use_best_huffman&&
+(c.part2_3_length=n,best_huffman_divide(a,c),n=c.part2_3_length);if(null!=e&&c.block_type==d.NORM_TYPE){for(b=0;a.scalefac_band.l[b]<c.big_values;)b++;e.sfb_count1=b}return n};this.count_bits=function(a,c,e,b){var f=e.l3_enc,n=ma.IXMAX_VAL/t.IPOW20(e.global_gain);if(e.xrpow_max>n)return ma.LARGE_BITS;var n=t.IPOW20(e.global_gain),m,g,y=0,h,u=0,l=0,la=0,F=0,G=f,I=0,T=c,U=0;h=null!=b&&e.global_gain==b.global_gain;g=e.block_type==d.SHORT_TYPE?38:21;for(m=0;m<=g;m++){var D=-1;if(h||e.block_type==d.NORM_TYPE)D=
+e.global_gain-(e.scalefac[m]+(0!=e.preflag?t.pretab[m]:0)<<e.scalefac_scale+1)-8*e.subblock_gain[e.window[m]];if(h&&b.step[m]==D)0!=u&&(q(u,n,T,U,G,I),u=0),0!=l&&(k(l,n,T,U,G,I),l=0);else{var ia=e.width[m];y+e.width[m]>e.max_nonzero_coeff&&(m=e.max_nonzero_coeff-y+1,Ia.fill(f,e.max_nonzero_coeff,576,0),ia=m,0>ia&&(ia=0),m=g+1);0==u&&0==l&&(G=f,I=F,T=c,U=la);null!=b&&0<b.sfb_count1&&m>=b.sfb_count1&&0<b.step[m]&&D>=b.step[m]?(0!=u&&(q(u,n,T,U,G,I),u=0,G=f,I=F,T=c,U=la),l+=ia):(0!=l&&(k(l,n,T,U,G,I),
+l=0,G=f,I=F,T=c,U=la),u+=ia);if(0>=ia){0!=l&&(k(l,n,T,U,G,I),l=0);0!=u&&(q(u,n,T,U,G,I),u=0);break}}m<=g&&(F+=e.width[m],la+=e.width[m],y+=e.width[m])}0!=u&&q(u,n,T,U,G,I);0!=l&&k(l,n,T,U,G,I);if(0!=(a.substep_shaping&2))for(n=0,g=.634521682242439/t.IPOW20(e.global_gain+e.scalefac_scale),y=0;y<e.sfbmax;y++)if(h=e.width[y],0==a.pseudohalf[y])n+=h;else for(u=n,n+=h;u<n;++u)f[u]=c[u]>=g?f[u]:0;return this.noquant_count_bits(a,e,b)};this.best_huffman_divide=function(a,c){var e=new Ab,b=c.l3_enc,f=Y(23),
+n=Y(23),m=Y(23),g=Y(23);if(c.block_type!=d.SHORT_TYPE||1!=a.mode_gr){e.assign(c);if(c.block_type==d.NORM_TYPE){for(var y=c.big_values,h=0;22>=h;h++)f[h]=ma.LARGE_BITS;for(h=0;16>h;h++){var l=a.scalefac_band.l[h+1];if(l>=y)break;for(var k=0,q=new J(k),F=C(b,0,l,q),k=q.bits,G=0;8>G;G++){var I=a.scalefac_band.l[h+G+2];if(I>=y)break;q=k;q=new J(q);I=C(b,l,I,q);q=q.bits;f[h+G]>q&&(f[h+G]=q,n[h+G]=h,m[h+G]=F,g[h+G]=I)}}B(a,e,c,b,f,n,m,g)}y=e.big_values;if(!(0==y||1<(b[y-2]|b[y-1])||(y=c.count1+2,576<y))){e.assign(c);
+e.count1=y;for(l=h=0;y>e.big_values;y-=4)k=2*(2*(2*b[y-4]+b[y-3])+b[y-2])+b[y-1],h+=r.t32l[k],l+=r.t33l[k];e.big_values=y;e.count1table_select=0;h>l&&(h=l,e.count1table_select=1);e.count1bits=h;e.block_type==d.NORM_TYPE?B(a,e,c,b,f,n,m,g):(e.part2_3_length=h,h=a.scalefac_band.l[8],h>y&&(h=y),0<h&&(f=new J(e.part2_3_length),e.table_select[0]=C(b,0,h,f),e.part2_3_length=f.bits),y>h&&(f=new J(e.part2_3_length),e.table_select[1]=C(b,h,y,f),e.part2_3_length=f.bits),c.part2_3_length>e.part2_3_length&&c.assign(e))}}};
+var b=[1,1,1,1,8,2,2,2,4,4,4,8,8,8,16,16],l=[1,2,4,8,1,2,4,8,2,4,8,2,4,8,4,8],a=[0,0,0,0,3,1,1,1,2,2,2,3,3,3,4,4],h=[0,1,2,3,0,1,2,3,1,2,3,1,2,3,2,3];tb.slen1_tab=a;tb.slen2_tab=h;this.best_scalefac_store=function(c,e,f,n){var g=n.tt[e][f],p,m,x,y=0;for(p=m=0;p<g.sfbmax;p++){x=g.width[p];m+=x;for(x=-x;0>x&&0==g.l3_enc[x+m];x++);0==x&&(g.scalefac[p]=y=-2)}if(0==g.scalefac_scale&&0==g.preflag){for(p=m=0;p<g.sfbmax;p++)0<g.scalefac[p]&&(m|=g.scalefac[p]);if(0==(m&1)&&0!=m){for(p=0;p<g.sfbmax;p++)0<g.scalefac[p]&&
+(g.scalefac[p]>>=1);g.scalefac_scale=y=1}}if(0==g.preflag&&g.block_type!=d.SHORT_TYPE&&2==c.mode_gr){for(p=11;p<d.SBPSY_l&&!(g.scalefac[p]<t.pretab[p]&&-2!=g.scalefac[p]);p++);if(p==d.SBPSY_l){for(p=11;p<d.SBPSY_l;p++)0<g.scalefac[p]&&(g.scalefac[p]-=t.pretab[p]);g.preflag=y=1}}for(p=0;4>p;p++)n.scfsi[f][p]=0;if(2==c.mode_gr&&1==e&&n.tt[0][f].block_type!=d.SHORT_TYPE&&n.tt[1][f].block_type!=d.SHORT_TYPE){e=n.tt[1][f];m=n.tt[0][f];for(y=0;y<r.scfsi_band.length-1;y++){for(p=r.scfsi_band[y];p<r.scfsi_band[y+
+1]&&!(m.scalefac[p]!=e.scalefac[p]&&0<=e.scalefac[p]);p++);if(p==r.scfsi_band[y+1]){for(p=r.scfsi_band[y];p<r.scfsi_band[y+1];p++)e.scalefac[p]=-1;n.scfsi[f][y]=1}}for(p=n=f=0;11>p;p++)-1!=e.scalefac[p]&&(n++,f<e.scalefac[p]&&(f=e.scalefac[p]));for(x=m=0;p<d.SBPSY_l;p++)-1!=e.scalefac[p]&&(x++,m<e.scalefac[p]&&(m=e.scalefac[p]));for(y=0;16>y;y++)f<b[y]&&m<l[y]&&(p=a[y]*n+h[y]*x,e.part2_length>p&&(e.part2_length=p,e.scalefac_compress=y));y=0}for(p=0;p<g.sfbmax;p++)-2==g.scalefac[p]&&(g.scalefac[p]=
+0);0!=y&&(2==c.mode_gr?this.scale_bitcount(g):this.scale_bitcount_lsf(c,g))};var D=[0,18,36,54,54,36,54,72,54,72,90,72,90,108,108,126],e=[0,18,36,54,51,35,53,71,52,70,88,69,87,105,104,122],n=[0,10,20,30,33,21,31,41,32,42,52,43,53,63,64,74];this.scale_bitcount=function(a){var c,f=0,g=0,h,p=a.scalefac;if(a.block_type==d.SHORT_TYPE)h=D,0!=a.mixed_block_flag&&(h=e);else if(h=n,0==a.preflag){for(c=11;c<d.SBPSY_l&&!(p[c]<t.pretab[c]);c++);if(c==d.SBPSY_l)for(a.preflag=1,c=11;c<d.SBPSY_l;c++)p[c]-=t.pretab[c]}for(c=
+0;c<a.sfbdivide;c++)f<p[c]&&(f=p[c]);for(;c<a.sfbmax;c++)g<p[c]&&(g=p[c]);a.part2_length=ma.LARGE_BITS;for(c=0;16>c;c++)f<b[c]&&g<l[c]&&a.part2_length>h[c]&&(a.part2_length=h[c],a.scalefac_compress=c);return a.part2_length==ma.LARGE_BITS};var c=[[15,15,7,7],[15,15,7,0],[7,3,0,0],[15,31,31,0],[7,7,7,0],[3,3,0,0]];this.scale_bitcount_lsf=function(a,e){var b,n,g,p,m,h,y,l=Y(4),u=e.scalefac;b=0!=e.preflag?2:0;for(h=0;4>h;h++)l[h]=0;if(e.block_type==d.SHORT_TYPE){n=1;var k=t.nr_of_sfb_block[b][n];for(g=
+y=0;4>g;g++)for(p=k[g]/3,h=0;h<p;h++,y++)for(m=0;3>m;m++)u[3*y+m]>l[g]&&(l[g]=u[3*y+m])}else for(n=0,k=t.nr_of_sfb_block[b][n],g=y=0;4>g;g++)for(p=k[g],h=0;h<p;h++,y++)u[y]>l[g]&&(l[g]=u[y]);p=!1;for(g=0;4>g;g++)l[g]>c[b][g]&&(p=!0);if(!p){e.sfb_partition_table=t.nr_of_sfb_block[b][n];for(g=0;4>g;g++)e.slen[g]=f[l[g]];n=e.slen[0];g=e.slen[1];l=e.slen[2];m=e.slen[3];switch(b){case 0:e.scalefac_compress=(5*n+g<<4)+(l<<2)+m;break;case 1:e.scalefac_compress=400+(5*n+g<<2)+l;break;case 2:e.scalefac_compress=
+500+3*n+g;break;default:K.err.printf("intensity stereo not implemented yet\n")}}if(!p)for(g=e.part2_length=0;4>g;g++)e.part2_length+=e.slen[g]*e.sfb_partition_table[g];return p};var f=[0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4];this.huffman_init=function(a){for(var c=2;576>=c;c+=2){for(var e=0,b;a.scalefac_band.l[++e]<c;);for(b=ta[e][0];a.scalefac_band.l[b+1]>c;)b--;0>b&&(b=ta[e][0]);a.bv_scf[c-2]=b;for(b=ta[e][1];a.scalefac_band.l[b+a.bv_scf[c-2]+2]>c;)b--;0>b&&(b=ta[e][1]);a.bv_scf[c-1]=b}}}function Hc(){var d;
+this.setModules=function(k){d=k};this.ResvFrameBegin=function(k,q){var C=k.internal_flags,B,t=C.l3_side,r=d.getframebits(k);q.bits=(r-8*C.sideinfo_len)/C.mode_gr;var g=2048*C.mode_gr-8;320<k.brate?B=8*int(1E3*k.brate/(k.out_samplerate/1152)/8+.5):(B=11520,k.strict_ISO&&(B=8*int(32E4/(k.out_samplerate/1152)/8+.5)));C.ResvMax=B-r;C.ResvMax>g&&(C.ResvMax=g);if(0>C.ResvMax||k.disable_reservoir)C.ResvMax=0;r=q.bits*C.mode_gr+Math.min(C.ResvSize,C.ResvMax);r>B&&(r=B);t.resvDrain_pre=0;null!=C.pinfo&&(C.pinfo.mean_bits=
+q.bits/2,C.pinfo.resvsize=C.ResvSize);return r};this.ResvMaxBits=function(d,q,J,B){var t=d.internal_flags,r=t.ResvSize,g=t.ResvMax;0!=B&&(r+=q);0!=(t.substep_shaping&1)&&(g*=.9);J.bits=q;10*r>9*g?(B=r-9*g/10,J.bits+=B,t.substep_shaping|=128):(B=0,t.substep_shaping&=127,d.disable_reservoir||0!=(t.substep_shaping&1)||(J.bits-=.1*q));d=r<6*t.ResvMax/10?r:6*t.ResvMax/10;d-=B;0>d&&(d=0);return d};this.ResvAdjust=function(d,q){d.ResvSize-=q.part2_3_length+q.part2_length};this.ResvFrameEnd=function(d,q){var J,
+B=d.l3_side;d.ResvSize+=q*d.mode_gr;var t=0;B.resvDrain_post=0;B.resvDrain_pre=0;0!=(J=d.ResvSize%8)&&(t+=J);J=d.ResvSize-t-d.ResvMax;0<J&&(t+=J);J=Math.min(8*B.main_data_begin,t)/8;B.resvDrain_pre+=8*J;t-=8*J;d.ResvSize-=8*J;B.main_data_begin-=J;B.resvDrain_post+=t;d.ResvSize-=t}}function ua(){function J(a,e,b){for(;0<b;){var d;0==z&&(z=8,E++,a.header[a.w_ptr].write_timing==f&&(d=a,K.arraycopy(d.header[d.w_ptr].buf,0,c,E,d.sideinfo_len),E+=d.sideinfo_len,f+=8*d.sideinfo_len,d.w_ptr=d.w_ptr+1&da.MAX_HEADER_BUF-
+1),c[E]=0);d=Math.min(b,z);b-=d;z-=d;c[E]|=e>>b<<z;f+=d}}function k(a,c){var b=a.internal_flags,d;8<=c&&(J(b,76,8),c-=8);8<=c&&(J(b,65,8),c-=8);8<=c&&(J(b,77,8),c-=8);8<=c&&(J(b,69,8),c-=8);if(32<=c){var m=e.getLameShortVersion();if(32<=c)for(d=0;d<m.length&&8<=c;++d)c-=8,J(b,m.charAt(d),8)}for(;1<=c;--c)J(b,b.ancillary_flag,1),b.ancillary_flag^=a.disable_reservoir?0:1}function q(a,c,e){for(var b=a.header[a.h_ptr].ptr;0<e;){var m=Math.min(e,8-(b&7));e-=m;a.header[a.h_ptr].buf[b>>3]|=c>>e<<8-(b&7)-
+m;b+=m}a.header[a.h_ptr].ptr=b}function C(a,c){a<<=8;for(var e=0;8>e;e++)a<<=1,c<<=1,0!=((c^a)&65536)&&(c^=32773);return c}function B(a,c){var e=r.ht[c.count1table_select+32],b,m=0,d=c.big_values,f=c.big_values;for(b=(c.count1-c.big_values)/4;0<b;--b){var n=0,g=0,h;h=c.l3_enc[d+0];0!=h&&(g+=8,0>c.xr[f+0]&&n++);h=c.l3_enc[d+1];0!=h&&(g+=4,n*=2,0>c.xr[f+1]&&n++);h=c.l3_enc[d+2];0!=h&&(g+=2,n*=2,0>c.xr[f+2]&&n++);h=c.l3_enc[d+3];0!=h&&(g++,n*=2,0>c.xr[f+3]&&n++);d+=4;f+=4;J(a,n+e.table[g],e.hlen[g]);
+m+=e.hlen[g]}return m}function t(c,a,e,b,m){var d=r.ht[a],f=0;if(0==a)return f;for(;e<b;e+=2){var n=0,g=0,h=d.xlen,l=d.xlen,k=0,G=m.l3_enc[e],I=m.l3_enc[e+1];0!=G&&(0>m.xr[e]&&k++,n--);15<a&&(14<G&&(k|=G-15<<1,g=h,G=15),14<I&&(l=I-15,k<<=h,k|=l,g+=h,I=15),l=16);0!=I&&(k<<=1,0>m.xr[e+1]&&k++,n--);G=G*l+I;g-=n;n+=d.hlen[G];J(c,d.table[G],n);J(c,k,g);f+=n+g}return f}function H(c,a){var e=3*c.scalefac_band.s[3];e>a.big_values&&(e=a.big_values);var b=t(c,a.table_select[0],0,e,a);return b+=t(c,a.table_select[1],
+e,a.big_values,a)}function g(c,a){var e,b,m,d;e=a.big_values;b=a.region0_count+1;m=c.scalefac_band.l[b];b+=a.region1_count+1;d=c.scalefac_band.l[b];m>e&&(m=e);d>e&&(d=e);b=t(c,a.table_select[0],0,m,a);b+=t(c,a.table_select[1],m,d,a);return b+=t(c,a.table_select[2],d,e,a)}function b(){this.total=0}function l(c,e){var b=c.internal_flags,d,m,n,g;g=b.w_ptr;n=b.h_ptr-1;-1==n&&(n=da.MAX_HEADER_BUF-1);d=b.header[n].write_timing-f;e.total=d;0<=d&&(m=1+n-g,n<g&&(m=1+n-g+da.MAX_HEADER_BUF),d-=8*m*b.sideinfo_len);
+b=a.getframebits(c);d+=b;e.total+=b;e.total=0!=e.total%8?1+e.total/8:e.total/8;e.total+=E+1;0>d&&K.err.println("strange error flushing buffer ... \n");return d}var a=this,h=null,D=null,e=null,n=null;this.setModules=function(a,c,b,d){h=a;D=c;e=b;n=d};var c=null,f=0,E=0,z=0;this.getframebits=function(a){var c=a.internal_flags;return 8*(0|72E3*(a.version+1)*(0!=c.bitrate_index?r.bitrate_table[a.version][c.bitrate_index]:a.brate)/a.out_samplerate+c.padding)};this.CRC_writeheader=function(a,c){var e;e=
+C(c[2]&255,65535);e=C(c[3]&255,e);for(var b=6;b<a.sideinfo_len;b++)e=C(c[b]&255,e);c[4]=byte(e>>8);c[5]=byte(e&255)};this.flush_bitstream=function(c){var a=c.internal_flags,e,d;e=a.l3_side;0>(d=l(c,new b))||(k(c,d),a.ResvSize=0,e.main_data_begin=0,a.findReplayGain&&(e=h.GetTitleGain(a.rgdata),a.RadioGain=Math.floor(10*e+.5)|0),a.findPeakSample&&(a.noclipGainChange=Math.ceil(200*Math.log10(a.PeakSample/32767))|0,0<a.noclipGainChange?EQ(c.scale,1)||EQ(c.scale,0)?a.noclipScale=Math.floor(32767/a.PeakSample*
+100)/100:a.noclipScale=-1:a.noclipScale=-1))};this.add_dummy_byte=function(a,e,b){a=a.internal_flags;for(var d;0<b--;){d=e;for(var m=8;0<m;){var n;0==z&&(z=8,E++,c[E]=0);n=Math.min(m,z);m-=n;z-=n;c[E]|=d>>m<<z;f+=n}for(d=0;d<da.MAX_HEADER_BUF;++d)a.header[d].write_timing+=8}};this.format_bitstream=function(a){var c=a.internal_flags,e;e=c.l3_side;var n=this.getframebits(a);k(a,e.resvDrain_pre);var m=a.internal_flags,h,y,E;h=m.l3_side;m.header[m.h_ptr].ptr=0;Ia.fill(m.header[m.h_ptr].buf,0,m.sideinfo_len,
+0);16E3>a.out_samplerate?q(m,4094,12):q(m,4095,12);q(m,a.version,1);q(m,1,2);q(m,a.error_protection?0:1,1);q(m,m.bitrate_index,4);q(m,m.samplerate_index,2);q(m,m.padding,1);q(m,a.extension,1);q(m,a.mode.ordinal(),2);q(m,m.mode_ext,2);q(m,a.copyright,1);q(m,a.original,1);q(m,a.emphasis,2);a.error_protection&&q(m,0,16);if(1==a.version){q(m,h.main_data_begin,9);2==m.channels_out?q(m,h.private_bits,3):q(m,h.private_bits,5);for(E=0;E<m.channels_out;E++)for(y=0;4>y;y++)q(m,h.scfsi[E][y],1);for(y=0;2>y;y++)for(E=
+0;E<m.channels_out;E++){var u=h.tt[y][E];q(m,u.part2_3_length+u.part2_length,12);q(m,u.big_values/2,9);q(m,u.global_gain,8);q(m,u.scalefac_compress,4);u.block_type!=d.NORM_TYPE?(q(m,1,1),q(m,u.block_type,2),q(m,u.mixed_block_flag,1),14==u.table_select[0]&&(u.table_select[0]=16),q(m,u.table_select[0],5),14==u.table_select[1]&&(u.table_select[1]=16),q(m,u.table_select[1],5),q(m,u.subblock_gain[0],3),q(m,u.subblock_gain[1],3),q(m,u.subblock_gain[2],3)):(q(m,0,1),14==u.table_select[0]&&(u.table_select[0]=
+16),q(m,u.table_select[0],5),14==u.table_select[1]&&(u.table_select[1]=16),q(m,u.table_select[1],5),14==u.table_select[2]&&(u.table_select[2]=16),q(m,u.table_select[2],5),q(m,u.region0_count,4),q(m,u.region1_count,3));q(m,u.preflag,1);q(m,u.scalefac_scale,1);q(m,u.count1table_select,1)}}else for(q(m,h.main_data_begin,8),q(m,h.private_bits,m.channels_out),E=y=0;E<m.channels_out;E++)u=h.tt[y][E],q(m,u.part2_3_length+u.part2_length,12),q(m,u.big_values/2,9),q(m,u.global_gain,8),q(m,u.scalefac_compress,
+9),u.block_type!=d.NORM_TYPE?(q(m,1,1),q(m,u.block_type,2),q(m,u.mixed_block_flag,1),14==u.table_select[0]&&(u.table_select[0]=16),q(m,u.table_select[0],5),14==u.table_select[1]&&(u.table_select[1]=16),q(m,u.table_select[1],5),q(m,u.subblock_gain[0],3),q(m,u.subblock_gain[1],3),q(m,u.subblock_gain[2],3)):(q(m,0,1),14==u.table_select[0]&&(u.table_select[0]=16),q(m,u.table_select[0],5),14==u.table_select[1]&&(u.table_select[1]=16),q(m,u.table_select[1],5),14==u.table_select[2]&&(u.table_select[2]=16),
+q(m,u.table_select[2],5),q(m,u.region0_count,4),q(m,u.region1_count,3)),q(m,u.scalefac_scale,1),q(m,u.count1table_select,1);a.error_protection&&CRC_writeheader(m,m.header[m.h_ptr].buf);h=m.h_ptr;m.h_ptr=h+1&da.MAX_HEADER_BUF-1;m.header[m.h_ptr].write_timing=m.header[h].write_timing+n;m.h_ptr==m.w_ptr&&K.err.println("Error: MAX_HEADER_BUF too small in bitstream.c \n");var m=8*c.sideinfo_len,z=0,t=a.internal_flags,F=t.l3_side;if(1==a.version)for(h=0;2>h;h++)for(E=0;E<t.channels_out;E++){var G=F.tt[h][E],
+I=tb.slen1_tab[G.scalefac_compress],T=tb.slen2_tab[G.scalefac_compress];for(y=u=0;y<G.sfbdivide;y++)-1!=G.scalefac[y]&&(J(t,G.scalefac[y],I),u+=I);for(;y<G.sfbmax;y++)-1!=G.scalefac[y]&&(J(t,G.scalefac[y],T),u+=T);u=G.block_type==d.SHORT_TYPE?u+H(t,G):u+g(t,G);u+=B(t,G);z+=u}else for(E=h=0;E<t.channels_out;E++){var G=F.tt[h][E],U=0,T=y=u=0;if(G.block_type==d.SHORT_TYPE){for(;4>T;T++)for(var D=G.sfb_partition_table[T]/3,ia=G.slen[T],I=0;I<D;I++,y++)J(t,Math.max(G.scalefac[3*y+0],0),ia),J(t,Math.max(G.scalefac[3*
+y+1],0),ia),J(t,Math.max(G.scalefac[3*y+2],0),ia),U+=3*ia;u+=H(t,G)}else{for(;4>T;T++)for(D=G.sfb_partition_table[T],ia=G.slen[T],I=0;I<D;I++,y++)J(t,Math.max(G.scalefac[y],0),ia),U+=ia;u+=g(t,G)}u+=B(t,G);z+=U+u}m+=z;k(a,e.resvDrain_post);m+=e.resvDrain_post;e.main_data_begin+=(n-m)/8;l(a,new b)!=c.ResvSize&&K.err.println("Internal buffer inconsistency. flushbits <> ResvSize");8*e.main_data_begin!=c.ResvSize&&(K.err.printf("bit reservoir error: \nl3_side.main_data_begin: %d \nResvoir size:             %d \nresv drain (post)         %d \nresv drain (pre)          %d \nheader and sideinfo:      %d \ndata bits:                %d \ntotal bits:               %d (remainder: %d) \nbitsperframe:             %d \n",
+8*e.main_data_begin,c.ResvSize,e.resvDrain_post,e.resvDrain_pre,8*c.sideinfo_len,m-e.resvDrain_post-8*c.sideinfo_len,m,m%8,n),K.err.println("This is a fatal error.  It has several possible causes:"),K.err.println("90%%  LAME compiled with buggy version of gcc using advanced optimizations"),K.err.println(" 9%%  Your system is overclocked"),K.err.println(" 1%%  bug in LAME encoding library"),c.ResvSize=8*e.main_data_begin);if(1E9<f){for(a=0;a<da.MAX_HEADER_BUF;++a)c.header[a].write_timing-=f;f=0}return 0};
+this.copy_buffer=function(a,e,b,d,m){var f=E+1;if(0>=f)return 0;if(0!=d&&f>d)return-1;K.arraycopy(c,0,e,b,f);E=-1;z=0;if(0!=m&&(d=Y(1),d[0]=a.nMusicCRC,n.updateMusicCRC(d,e,b,f),a.nMusicCRC=d[0],0<f&&(a.VBR_seek_table.nBytesWritten+=f),a.decode_on_the_fly)){d=ra([2,1152]);m=f;for(var g=-1,l;0!=g;)if(g=D.hip_decode1_unclipped(a.hip,e,b,m,d[0],d[1]),m=0,-1==g&&(g=0),0<g){if(a.findPeakSample){for(l=0;l<g;l++)d[0][l]>a.PeakSample?a.PeakSample=d[0][l]:-d[0][l]>a.PeakSample&&(a.PeakSample=-d[0][l]);if(1<
+a.channels_out)for(l=0;l<g;l++)d[1][l]>a.PeakSample?a.PeakSample=d[1][l]:-d[1][l]>a.PeakSample&&(a.PeakSample=-d[1][l])}if(a.findReplayGain&&h.AnalyzeSamples(a.rgdata,d[0],0,d[1],0,g,a.channels_out)==O.GAIN_ANALYSIS_ERROR)return-6}}return f};this.init_bit_stream_w=function(a){c=new Int8Array(V.LAME_MAXMP3BUFFER);a.h_ptr=a.w_ptr=0;a.header[a.h_ptr].write_timing=0;E=-1;f=z=0}}function ub(){function d(a,b){var c=a[b+0]&255,c=c<<8|a[b+1]&255,c=c<<8,c=c|a[b+2]&255,c=c<<8;return c|=a[b+3]&255}function k(a,
+b,c){a[b+0]=c>>24&255;a[b+1]=c>>16&255;a[b+2]=c>>8&255;a[b+3]=c&255}function q(a,b,c){a[b+0]=c>>8&255;a[b+1]=c&255}function C(a,b,c){return 255&(a<<b|c&~(-1<<b))}function B(a,b){var c=a.internal_flags;b[0]=C(b[0],8,255);b[1]=C(b[1],3,7);b[1]=C(b[1],1,16E3>a.out_samplerate?0:1);b[1]=C(b[1],1,a.version);b[1]=C(b[1],2,1);b[1]=C(b[1],1,a.error_protection?0:1);b[2]=C(b[2],4,c.bitrate_index);b[2]=C(b[2],2,c.samplerate_index);b[2]=C(b[2],1,0);b[2]=C(b[2],1,a.extension);b[3]=C(b[3],2,a.mode.ordinal());b[3]=
+C(b[3],2,c.mode_ext);b[3]=C(b[3],1,a.copyright);b[3]=C(b[3],1,a.original);b[3]=C(b[3],2,a.emphasis);b[0]=255;var c=b[1]&241,d;d=1==a.version?128:16E3>a.out_samplerate?32:64;a.VBR==M.vbr_off&&(d=a.brate);d=a.free_format?0:255&16*H.BitrateIndex(d,a.version,a.out_samplerate);b[1]=1==a.version?255&(c|10):255&(c|2);c=b[2]&13;b[2]=255&(d|c)}function t(a,b){return b=b>>8^D[(b^a)&255]}var H,g,b;this.setModules=function(a,d,c){H=a;g=d;b=c};var l=ub.NUMTOCENTRIES,a=ub.MAXFRAMESIZE,h=l+4+4+4+4+4+9+1+1+8+1+1+
+3+1+1+2+4+2+2,D=[0,49345,49537,320,49921,960,640,49729,50689,1728,1920,51009,1280,50625,50305,1088,52225,3264,3456,52545,3840,53185,52865,3648,2560,51905,52097,2880,51457,2496,2176,51265,55297,6336,6528,55617,6912,56257,55937,6720,7680,57025,57217,8E3,56577,7616,7296,56385,5120,54465,54657,5440,55041,6080,5760,54849,53761,4800,4992,54081,4352,53697,53377,4160,61441,12480,12672,61761,13056,62401,62081,12864,13824,63169,63361,14144,62721,13760,13440,62529,15360,64705,64897,15680,65281,16320,16E3,65089,
+64001,15040,15232,64321,14592,63937,63617,14400,10240,59585,59777,10560,60161,11200,10880,59969,60929,11968,12160,61249,11520,60865,60545,11328,58369,9408,9600,58689,9984,59329,59009,9792,8704,58049,58241,9024,57601,8640,8320,57409,40961,24768,24960,41281,25344,41921,41601,25152,26112,42689,42881,26432,42241,26048,25728,42049,27648,44225,44417,27968,44801,28608,28288,44609,43521,27328,27520,43841,26880,43457,43137,26688,30720,47297,47489,31040,47873,31680,31360,47681,48641,32448,32640,48961,32E3,
+48577,48257,31808,46081,29888,30080,46401,30464,47041,46721,30272,29184,45761,45953,29504,45313,29120,28800,45121,20480,37057,37249,20800,37633,21440,21120,37441,38401,22208,22400,38721,21760,38337,38017,21568,39937,23744,23936,40257,24320,40897,40577,24128,23040,39617,39809,23360,39169,22976,22656,38977,34817,18624,18816,35137,19200,35777,35457,19008,19968,36545,36737,20288,36097,19904,19584,35905,17408,33985,34177,17728,34561,18368,18048,34369,33281,17088,17280,33601,16640,33217,32897,16448];this.addVbrFrame=
+function(a){var b=a.internal_flags;var c=b.VBR_seek_table;a=r.bitrate_table[a.version][b.bitrate_index];c.nVbrNumFrames++;c.sum+=a;c.seen++;if(!(c.seen<c.want)&&(c.pos<c.size&&(c.bag[c.pos]=c.sum,c.pos++,c.seen=0),c.pos==c.size)){for(a=1;a<c.size;a+=2)c.bag[a/2]=c.bag[a];c.want*=2;c.pos/=2}};this.getVbrTag=function(a){var b=new VBRTagData,c=0;b.flags=0;var f=a[c+1]>>3&1,g=a[c+2]>>2&3,h=a[c+3]>>6&3,k=a[c+2]>>4&15,k=r.bitrate_table[f][k];b.samprate=14==a[c+1]>>4?r.samplerate_table[2][g]:r.samplerate_table[f][g];
+g=c=0!=f?3!=h?c+36:c+21:3!=h?c+21:c+13;if(!(new String(a,g,4(),null)).equals("Xing")&&!(new String(a,g,4(),null)).equals("Info"))return null;c+=4;b.hId=f;g=b.flags=d(a,c);c+=4;0!=(g&1)&&(b.frames=d(a,c),c+=4);0!=(g&2)&&(b.bytes=d(a,c),c+=4);if(0!=(g&4)){if(null!=b.toc)for(h=0;h<l;h++)b.toc[h]=a[c+h];c+=l}b.vbrScale=-1;0!=(g&8)&&(b.vbrScale=d(a,c),c+=4);b.headersize=72E3*(f+1)*k/b.samprate;c+=21;f=a[c+0]<<4;f+=a[c+1]>>4;k=(a[c+1]&15)<<8;k+=a[c+2]&255;if(0>f||3E3<f)f=-1;if(0>k||3E3<k)k=-1;b.encDelay=
+f;b.encPadding=k;return b};this.InitVbrTag=function(b){var d=b.internal_flags,c;c=1==b.version?128:16E3>b.out_samplerate?32:64;b.VBR==M.vbr_off&&(c=b.brate);c=72E3*(b.version+1)*c/b.out_samplerate;var f=d.sideinfo_len+h;d.VBR_seek_table.TotalFrameSize=c;if(c<f||c>a)b.bWriteVbrTag=!1;else for(d.VBR_seek_table.nVbrNumFrames=0,d.VBR_seek_table.nBytesWritten=0,d.VBR_seek_table.sum=0,d.VBR_seek_table.seen=0,d.VBR_seek_table.want=1,d.VBR_seek_table.pos=0,null==d.VBR_seek_table.bag&&(d.VBR_seek_table.bag=
+new int[400],d.VBR_seek_table.size=400),c=new Int8Array(a),B(b,c),d=d.VBR_seek_table.TotalFrameSize,f=0;f<d;++f)g.add_dummy_byte(b,c[f]&255,1)};this.updateMusicCRC=function(a,b,c,d){for(var g=0;g<d;++g)a[0]=t(b[c+g],a[0])};this.getLameTagFrame=function(a,d){var c=a.internal_flags;if(!a.bWriteVbrTag||c.Class_ID!=V.LAME_ID||0>=c.VBR_seek_table.pos)return 0;if(d.length<c.VBR_seek_table.TotalFrameSize)return c.VBR_seek_table.TotalFrameSize;Ia.fill(d,0,c.VBR_seek_table.TotalFrameSize,0);B(a,d);var f=new Int8Array(l);
+if(a.free_format)for(var h=1;h<l;++h)f[h]=255&255*h/100;else{var z=c.VBR_seek_table;if(!(0>=z.pos))for(h=1;h<l;++h){var w=0|Math.floor(h/l*z.pos);w>z.pos-1&&(w=z.pos-1);w=0|256*z.bag[w]/z.sum;255<w&&(w=255);f[h]=255&w}}w=c.sideinfo_len;a.error_protection&&(w-=2);d[w++]=0;d[w++]=0;d[w++]=0;d[w++]=0;k(d,w,15);w+=4;k(d,w,c.VBR_seek_table.nVbrNumFrames);w+=4;z=c.VBR_seek_table.nBytesWritten+c.VBR_seek_table.TotalFrameSize;k(d,w,0|z);w+=4;K.arraycopy(f,0,d,w,f.length);w+=f.length;a.error_protection&&g.CRC_writeheader(c,
+d);for(var A=0,h=0;h<w;h++)A=t(d[h],A);var f=w,h=A,v=a.internal_flags,w=0,A=a.encoder_delay,p=a.encoder_padding,m=100-10*a.VBR_q-a.quality,x=b.getLameVeryShortVersion(),y;y=[1,5,3,2,4,0,3];var W=0|(255<a.lowpassfreq/100+.5?255:a.lowpassfreq/100+.5),u=0,D=0,J=a.internal_flags.noise_shaping,F=0,G=0,I=0,T=0,F=0,F=0!=(a.exp_nspsytune&1),I=0!=(a.exp_nspsytune&2),U=!1,qa=!1,ia=a.internal_flags.nogap_total,ya=a.internal_flags.nogap_current,T=a.ATHtype,r=0,C;switch(a.VBR){case vbr_abr:C=a.VBR_mean_bitrate_kbps;
+break;case vbr_off:C=a.brate;break;default:C=a.VBR_min_bitrate_kbps}y=0+(a.VBR.ordinal()<y.length?y[a.VBR.ordinal()]:0);v.findReplayGain&&(510<v.RadioGain&&(v.RadioGain=510),-510>v.RadioGain&&(v.RadioGain=-510),D=11264,D=0<=v.RadioGain?D|v.RadioGain:D|512|-v.RadioGain);v.findPeakSample&&(u=Math.abs(0|v.PeakSample/32767*Math.pow(2,23)+.5));-1!=ia&&(0<ya&&(qa=!0),ya<ia-1&&(U=!0));r=T+((F?1:0)<<4)+((I?1:0)<<5)+((U?1:0)<<6)+((qa?1:0)<<7);0>m&&(m=0);switch(a.mode){case MONO:F=0;break;case STEREO:F=1;break;
+case DUAL_CHANNEL:F=2;break;case JOINT_STEREO:F=a.force_ms?4:3;break;default:F=7}I=32E3>=a.in_samplerate?0:48E3==a.in_samplerate?2:48E3<a.in_samplerate?3:1;if(a.short_blocks==sa.short_block_forced||a.short_blocks==sa.short_block_dispensed||-1==a.lowpassfreq&&-1==a.highpassfreq||a.scale_left<a.scale_right||a.scale_left>a.scale_right||a.disable_reservoir&&320>a.brate||a.noATH||a.ATHonly||0==T||32E3>=a.in_samplerate)G=1;T=J+(F<<2)+(G<<5)+(I<<6);F=v.nMusicCRC;k(d,f+w,m);w+=4;for(v=0;9>v;v++)d[f+w+v]=
+255&x.charAt(v);w+=9;d[f+w]=255&y;w++;d[f+w]=255&W;w++;k(d,f+w,u);w+=4;q(d,f+w,D);w+=2;q(d,f+w,0);w+=2;d[f+w]=255&r;w++;d[f+w]=255<=C?255:255&C;w++;d[f+w]=255&A>>4;d[f+w+1]=255&(A<<4)+(p>>8);d[f+w+2]=255&p;w+=3;d[f+w]=255&T;w++;d[f+w++]=0;q(d,f+w,a.preset);w+=2;k(d,f+w,z);w+=4;q(d,f+w,F);w+=2;for(z=0;z<w;z++)h=t(d[f+z],h);q(d,f+w,h);return c.VBR_seek_table.TotalFrameSize};this.putVbrTag=function(b,d){if(0>=b.internal_flags.VBR_seek_table.pos)return-1;d.seek(d.length());if(0==d.length())return-1;d.seek(0);
+var c=new Int8Array(10);d.readFully(c);c=(new String(c,"ISO-8859-1")).startsWith("ID3")?0:((c[6]&127)<<21|(c[7]&127)<<14|(c[8]&127)<<7|c[9]&127)+c.length;d.seek(c);var c=new Int8Array(a),f=getLameTagFrame(b,c);if(f>c.length)return-1;if(1>f)return 0;d.write(c,0,f);return 0}}function Q(d,k,q,r){this.xlen=d;this.linmax=k;this.table=q;this.hlen=r}function eb(d){this.bits=d}function Ic(){this.setModules=function(d,k){}}function $b(){this.bits=this.over_SSD=this.over_count=this.max_noise=this.tot_noise=
+this.over_noise=0}function Jc(){this.scale_right=this.scale_left=this.scale=this.out_samplerate=this.in_samplerate=this.num_channels=this.num_samples=this.class_id=0;this.decode_only=this.bWriteVbrTag=this.analysis=!1;this.quality=0;this.mode=ka.STEREO;this.write_id3tag_automatic=this.decode_on_the_fly=this.findReplayGain=this.free_format=this.force_ms=!1;this.error_protection=this.emphasis=this.extension=this.original=this.copyright=this.compression_ratio=this.brate=0;this.disable_reservoir=this.strict_ISO=
+!1;this.quant_comp_short=this.quant_comp=0;this.experimentalY=!1;this.preset=this.exp_nspsytune=this.experimentalZ=0;this.VBR=null;this.maskingadjust_short=this.maskingadjust=this.highpasswidth=this.lowpasswidth=this.highpassfreq=this.lowpassfreq=this.VBR_hard_min=this.VBR_max_bitrate_kbps=this.VBR_min_bitrate_kbps=this.VBR_mean_bitrate_kbps=this.VBR_q=this.VBR_q_frac=0;this.noATH=this.ATHshort=this.ATHonly=!1;this.athaa_sensitivity=this.athaa_loudapprox=this.athaa_type=this.ATHlower=this.ATHcurve=
+this.ATHtype=0;this.short_blocks=null;this.useTemporal=!1;this.msfix=this.interChRatio=0;this.tune=!1;this.lame_allocated_gfp=this.frameNum=this.framesize=this.encoder_padding=this.encoder_delay=this.version=this.tune_value_a=0;this.internal_flags=null}function Kc(){this.linprebuf=H(2*O.MAX_ORDER);this.linpre=0;this.lstepbuf=H(O.MAX_SAMPLES_PER_WINDOW+O.MAX_ORDER);this.lstep=0;this.loutbuf=H(O.MAX_SAMPLES_PER_WINDOW+O.MAX_ORDER);this.lout=0;this.rinprebuf=H(2*O.MAX_ORDER);this.rinpre=0;this.rstepbuf=
+H(O.MAX_SAMPLES_PER_WINDOW+O.MAX_ORDER);this.rstep=0;this.routbuf=H(O.MAX_SAMPLES_PER_WINDOW+O.MAX_ORDER);this.first=this.freqindex=this.rsum=this.lsum=this.totsamp=this.sampleWindow=this.rout=0;this.A=Y(0|O.STEPS_per_dB*O.MAX_dB);this.B=Y(0|O.STEPS_per_dB*O.MAX_dB)}function Lc(){this.floor=this.decay=this.adjustLimit=this.adjust=this.aaSensitivityP=this.useAdjust=0;this.l=H(d.SBMAX_l);this.s=H(d.SBMAX_s);this.psfb21=H(d.PSFB21);this.psfb12=H(d.PSFB12);this.cb_l=H(d.CBANDS);this.cb_s=H(d.CBANDS);
+this.eql_w=H(d.BLKSIZE/2)}function Mc(J){this.quantize=J;this.iteration_loop=function(k,q,J,B){var t=k.internal_flags,r=H(na.SFBMAX),g=H(576),b=Y(2),l,a,h=t.l3_side;l=new eb(0);this.quantize.rv.ResvFrameBegin(k,l);l=l.bits;for(var D=0;D<t.mode_gr;D++)for(a=this.quantize.qupvt.on_pe(k,q,b,l,D,D),t.mode_ext==d.MPG_MD_MS_LR&&(this.quantize.ms_convert(t.l3_side,D),this.quantize.qupvt.reduce_side(b,J[D],l,a)),a=0;a<t.channels_out;a++){var e,n=h.tt[D][a];n.block_type!=d.SHORT_TYPE?(e=0,e=t.PSY.mask_adjust-
+e):(e=0,e=t.PSY.mask_adjust_short-e);t.masking_lower=Math.pow(10,.1*e);this.quantize.init_outer_loop(t,n);this.quantize.init_xrpow(t,n,g)&&(this.quantize.qupvt.calc_xmin(k,B[D][a],n,r),this.quantize.outer_loop(k,n,r,g,a,b[a]));this.quantize.iteration_finish_one(t,D,a)}this.quantize.rv.ResvFrameEnd(t,l)}}function ha(J,k,q,r){this.l=Y(1+d.SBMAX_l);this.s=Y(1+d.SBMAX_s);this.psfb21=Y(1+d.PSFB21);this.psfb12=Y(1+d.PSFB12);var B=this.l,t=this.s;4==arguments.length&&(this.arrL=arguments[0],this.arrS=arguments[1],
+this.arr21=arguments[2],this.arr12=arguments[3],K.arraycopy(this.arrL,0,B,0,Math.min(this.arrL.length,this.l.length)),K.arraycopy(this.arrS,0,t,0,Math.min(this.arrS.length,this.s.length)),K.arraycopy(this.arr21,0,this.psfb21,0,Math.min(this.arr21.length,this.psfb21.length)),K.arraycopy(this.arr12,0,this.psfb12,0,Math.min(this.arr12.length,this.psfb12.length)))}function ma(){function J(a,b){var c=B.ATHformula(b,a);return c=Math.pow(10,(c-100)/10+a.ATHlower)}function k(a){this.s=a}var q=null,r=null,
+B=null;this.setModules=function(a,b,c){q=a;r=b;B=c};this.IPOW20=function(b){return a[b]};var t=ma.IXMAX_VAL+2,ta=ma.Q_MAX,g=ma.Q_MAX2;this.nr_of_sfb_block=[[[6,5,5,5],[9,9,9,9],[6,9,9,9]],[[6,5,7,3],[9,9,12,6],[6,9,12,6]],[[11,10,0,0],[18,18,0,0],[15,18,0,0]],[[7,7,7,0],[12,12,12,0],[6,15,12,0]],[[6,6,6,3],[12,9,9,6],[6,12,9,6]],[[8,8,5,0],[15,12,9,0],[6,18,9,0]]];var b=[0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,2,2,3,3,3,2,0];this.pretab=b;this.sfBandIndex=[new ha([0,6,12,18,24,30,36,44,54,66,80,96,116,140,
+168,200,238,284,336,396,464,522,576],[0,4,8,12,18,24,32,42,56,74,100,132,174,192],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0]),new ha([0,6,12,18,24,30,36,44,54,66,80,96,114,136,162,194,232,278,332,394,464,540,576],[0,4,8,12,18,26,36,48,62,80,104,136,180,192],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0]),new ha([0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576],[0,4,8,12,18,26,36,48,62,80,104,134,174,192],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0]),new ha([0,4,8,12,16,20,24,30,36,44,52,62,74,90,110,134,162,
+196,238,288,342,418,576],[0,4,8,12,16,22,30,40,52,66,84,106,136,192],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0]),new ha([0,4,8,12,16,20,24,30,36,42,50,60,72,88,106,128,156,190,230,276,330,384,576],[0,4,8,12,16,22,28,38,50,64,80,100,126,192],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0]),new ha([0,4,8,12,16,20,24,30,36,44,54,66,82,102,126,156,194,240,296,364,448,550,576],[0,4,8,12,16,22,30,42,58,78,104,138,180,192],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0]),new ha([0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,
+522,576],[0,4,8,12,18,26,36,48,62,80,104,134,174,192],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0]),new ha([0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576],[0,4,8,12,18,26,36,48,62,80,104,134,174,192],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0]),new ha([0,12,24,36,48,60,72,88,108,132,160,192,232,280,336,400,476,566,568,570,572,574,576],[0,8,16,24,36,52,72,96,124,160,162,164,166,192],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0])];var l=H(ta+g+1),a=H(ta),h=H(t),D=H(t);this.adj43=D;this.iteration_init=function(b){var n=
+b.internal_flags,c=n.l3_side;if(0==n.iteration_init_init){n.iteration_init_init=1;c.main_data_begin=0;for(var c=b.internal_flags.ATH.l,f=b.internal_flags.ATH.psfb21,k=b.internal_flags.ATH.s,z=b.internal_flags.ATH.psfb12,w=b.internal_flags,A=b.out_samplerate,v=0;v<d.SBMAX_l;v++){var p=w.scalefac_band.l[v],m=w.scalefac_band.l[v+1];for(c[v]=vb.MAX_VALUE;p<m;p++){var x=p*A/1152,x=J(b,x);c[v]=Math.min(c[v],x)}}for(v=0;v<d.PSFB21;v++)for(p=w.scalefac_band.psfb21[v],m=w.scalefac_band.psfb21[v+1],f[v]=vb.MAX_VALUE;p<
+m;p++)x=p*A/1152,x=J(b,x),f[v]=Math.min(f[v],x);for(v=0;v<d.SBMAX_s;v++){p=w.scalefac_band.s[v];m=w.scalefac_band.s[v+1];for(k[v]=vb.MAX_VALUE;p<m;p++)x=p*A/384,x=J(b,x),k[v]=Math.min(k[v],x);k[v]*=w.scalefac_band.s[v+1]-w.scalefac_band.s[v]}for(v=0;v<d.PSFB12;v++){p=w.scalefac_band.psfb12[v];m=w.scalefac_band.psfb12[v+1];for(z[v]=vb.MAX_VALUE;p<m;p++)x=p*A/384,x=J(b,x),z[v]=Math.min(z[v],x);z[v]*=w.scalefac_band.s[13]-w.scalefac_band.s[12]}if(b.noATH){for(v=0;v<d.SBMAX_l;v++)c[v]=1E-20;for(v=0;v<
+d.PSFB21;v++)f[v]=1E-20;for(v=0;v<d.SBMAX_s;v++)k[v]=1E-20;for(v=0;v<d.PSFB12;v++)z[v]=1E-20}w.ATH.floor=10*Math.log10(J(b,-1));h[0]=0;for(c=1;c<t;c++)h[c]=Math.pow(c,4/3);for(c=0;c<t-1;c++)D[c]=c+1-Math.pow(.5*(h[c]+h[c+1]),.75);D[c]=.5;for(c=0;c<ta;c++)a[c]=Math.pow(2,-.1875*(c-210));for(c=0;c<=ta+g;c++)l[c]=Math.pow(2,.25*(c-210-g));q.huffman_init(n);c=b.exp_nspsytune>>2&63;32<=c&&(c-=64);f=Math.pow(10,c/4/10);c=b.exp_nspsytune>>8&63;32<=c&&(c-=64);k=Math.pow(10,c/4/10);c=b.exp_nspsytune>>14&63;
+32<=c&&(c-=64);z=Math.pow(10,c/4/10);c=b.exp_nspsytune>>20&63;32<=c&&(c-=64);b=z*Math.pow(10,c/4/10);for(c=0;c<d.SBMAX_l;c++)w=6>=c?f:13>=c?k:20>=c?z:b,n.nsPsy.longfact[c]=w;for(c=0;c<d.SBMAX_s;c++)w=5>=c?f:10>=c?k:11>=c?z:b,n.nsPsy.shortfact[c]=w}};this.on_pe=function(a,b,c,d,g,h){var l=a.internal_flags,k=0,v=Y(2),p,k=new eb(k);a=r.ResvMaxBits(a,d,k,h);var k=k.bits,m=k+a;m>da.MAX_BITS_PER_GRANULE&&(m=da.MAX_BITS_PER_GRANULE);for(p=h=0;p<l.channels_out;++p)c[p]=Math.min(da.MAX_BITS_PER_CHANNEL,k/
+l.channels_out),v[p]=0|c[p]*b[g][p]/700-c[p],v[p]>3*d/4&&(v[p]=3*d/4),0>v[p]&&(v[p]=0),v[p]+c[p]>da.MAX_BITS_PER_CHANNEL&&(v[p]=Math.max(0,da.MAX_BITS_PER_CHANNEL-c[p])),h+=v[p];if(h>a)for(p=0;p<l.channels_out;++p)v[p]=a*v[p]/h;for(p=0;p<l.channels_out;++p)c[p]+=v[p],a-=v[p];for(p=h=0;p<l.channels_out;++p)h+=c[p];if(h>da.MAX_BITS_PER_GRANULE)for(p=0;p<l.channels_out;++p)c[p]*=da.MAX_BITS_PER_GRANULE,c[p]/=h;return m};this.reduce_side=function(a,b,c,d){b=.33*(.5-b)/.5;0>b&&(b=0);.5<b&&(b=.5);b=0|.5*
+b*(a[0]+a[1]);b>da.MAX_BITS_PER_CHANNEL-a[0]&&(b=da.MAX_BITS_PER_CHANNEL-a[0]);0>b&&(b=0);125<=a[1]&&(125<a[1]-b?(a[0]<c&&(a[0]+=b),a[1]-=b):(a[0]+=a[1]-125,a[1]=125));b=a[0]+a[1];b>d&&(a[0]=d*a[0]/b,a[1]=d*a[1]/b)};this.athAdjust=function(a,b,c){b=X.FAST_LOG10_X(b,10);a*=a;var d=0;b-=c;1E-20<a&&(d=1+X.FAST_LOG10_X(a,10/90.30873362));0>d&&(d=0);b=b*d+(c+90.30873362-94.82444863);return Math.pow(10,.1*b)};this.calc_xmin=function(a,b,c,f){var g=0,h=a.internal_flags,l,k=0,v=0,p=h.ATH,m=c.xr,x=a.VBR==
+M.vbr_mtrh?1:0,y=h.masking_lower;if(a.VBR==M.vbr_mtrh||a.VBR==M.vbr_mt)y=1;for(l=0;l<c.psy_lmax;l++){var q,u,t,D,F,G;u=a.VBR==M.vbr_rh||a.VBR==M.vbr_mtrh?athAdjust(p.adjust,p.l[l],p.floor):p.adjust*p.l[l];F=c.width[l];t=u/F;D=2.220446049250313E-16;G=F>>1;q=0;do{var I;I=m[k]*m[k];q+=I;D+=I<t?I:t;k++;I=m[k]*m[k];q+=I;D+=I<t?I:t;k++}while(0<--G);q>u&&v++;l==d.SBPSY_l&&(t=u*h.nsPsy.longfact[l],D<t&&(D=t));0!=x&&(u=D);a.ATHonly||(D=b.en.l[l],0<D&&(t=q*b.thm.l[l]*y/D,0!=x&&(t*=h.nsPsy.longfact[l]),u<t&&
+(u=t)));0!=x?f[g++]=u:f[g++]=u*h.nsPsy.longfact[l]}q=575;if(c.block_type!=d.SHORT_TYPE)for(u=576;0!=u--&&ua.EQ(m[u],0);)q=u;c.max_nonzero_coeff=q;for(var T=c.sfb_smin;l<c.psymax;T++,l+=3){var U,J;J=a.VBR==M.vbr_rh||a.VBR==M.vbr_mtrh?athAdjust(p.adjust,p.s[T],p.floor):p.adjust*p.s[T];F=c.width[l];for(U=0;3>U;U++){q=0;G=F>>1;t=J/F;D=2.220446049250313E-16;do I=m[k]*m[k],q+=I,D+=I<t?I:t,k++,I=m[k]*m[k],q+=I,D+=I<t?I:t,k++;while(0<--G);q>J&&v++;T==d.SBPSY_s&&(t=J*h.nsPsy.shortfact[T],D<t&&(D=t));u=0!=
+x?D:J;a.ATHonly||a.ATHshort||(D=b.en.s[T][U],0<D&&(t=q*b.thm.s[T][U]*y/D,0!=x&&(t*=h.nsPsy.shortfact[T]),u<t&&(u=t)));0!=x?f[g++]=u:f[g++]=u*h.nsPsy.shortfact[T]}a.useTemporal&&(f[g-3]>f[g-3+1]&&(f[g-3+1]+=(f[g-3]-f[g-3+1])*h.decay),f[g-3+1]>f[g-3+2]&&(f[g-3+2]+=(f[g-3+1]-f[g-3+2])*h.decay))}return v};this.calc_noise_core=function(a,b,c,d){var g=0,l=b.s,k=a.l3_enc;if(l>a.count1)for(;0!=c--;){var q;q=a.xr[l];l++;g+=q*q;q=a.xr[l];l++;g+=q*q}else if(l>a.big_values){var v=H(2);v[0]=0;for(v[1]=d;0!=c--;)q=
+Math.abs(a.xr[l])-v[k[l]],l++,g+=q*q,q=Math.abs(a.xr[l])-v[k[l]],l++,g+=q*q}else for(;0!=c--;)q=Math.abs(a.xr[l])-h[k[l]]*d,l++,g+=q*q,q=Math.abs(a.xr[l])-h[k[l]]*d,l++,g+=q*q;b.s=l;return g};this.calc_noise=function(a,d,c,f,g){var h=0,q=0,t,v,p=0,m=0,x=0,y=-20,D=0,u=a.scalefac,J=0;for(t=f.over_SSD=0;t<a.psymax;t++){var B=a.global_gain-(u[J++]+(0!=a.preflag?b[t]:0)<<a.scalefac_scale+1)-8*a.subblock_gain[a.window[t]],F=0;null!=g&&g.step[t]==B?(F=g.noise[t],D+=a.width[t],c[h++]=F/d[q++],F=g.noise_log[t]):
+(F=l[B+ma.Q_MAX2],v=a.width[t]>>1,D+a.width[t]>a.max_nonzero_coeff&&(v=a.max_nonzero_coeff-D+1,v=0<v?v>>1:0),D=new k(D),F=this.calc_noise_core(a,D,v,F),D=D.s,null!=g&&(g.step[t]=B,g.noise[t]=F),F=c[h++]=F/d[q++],F=X.FAST_LOG10(Math.max(F,1E-20)),null!=g&&(g.noise_log[t]=F));null!=g&&(g.global_gain=a.global_gain);x+=F;0<F&&(B=Math.max(0|10*F+.5,1),f.over_SSD+=B*B,p++,m+=F);y=Math.max(y,F)}f.over_count=p;f.tot_noise=x;f.over_noise=m;f.max_noise=y;return p};this.set_pinfo=function(a,g,c,f,h){var l=a.internal_flags,
+k,q,t,p,m=0==g.scalefac_scale?.5:1,x=g.scalefac,y=H(na.SFBMAX),D=H(na.SFBMAX),u=new $b;calc_xmin(a,c,g,y);calc_noise(g,y,D,u,null);var J=0;q=g.sfb_lmax;g.block_type!=d.SHORT_TYPE&&0==g.mixed_block_flag&&(q=22);for(k=0;k<q;k++){var B=l.scalefac_band.l[k],F=l.scalefac_band.l[k+1],G=F-B;for(p=0;J<F;J++)p+=g.xr[J]*g.xr[J];p/=G;t=1E15;l.pinfo.en[f][h][k]=t*p;l.pinfo.xfsf[f][h][k]=t*y[k]*D[k]/G;p=0<c.en.l[k]&&!a.ATHonly?p/c.en.l[k]:0;l.pinfo.thr[f][h][k]=t*Math.max(p*c.thm.l[k],l.ATH.l[k]);l.pinfo.LAMEsfb[f][h][k]=
+0;0!=g.preflag&&11<=k&&(l.pinfo.LAMEsfb[f][h][k]=-m*b[k]);k<d.SBPSY_l&&(l.pinfo.LAMEsfb[f][h][k]-=m*x[k])}if(g.block_type==d.SHORT_TYPE)for(q=k,k=g.sfb_smin;k<d.SBMAX_s;k++)for(var B=l.scalefac_band.s[k],F=l.scalefac_band.s[k+1],G=F-B,I=0;3>I;I++){p=0;for(t=B;t<F;t++)p+=g.xr[J]*g.xr[J],J++;p=Math.max(p/G,1E-20);t=1E15;l.pinfo.en_s[f][h][3*k+I]=t*p;l.pinfo.xfsf_s[f][h][3*k+I]=t*y[q]*D[q]/G;p=0<c.en.s[k][I]?p/c.en.s[k][I]:0;if(a.ATHonly||a.ATHshort)p=0;l.pinfo.thr_s[f][h][3*k+I]=t*Math.max(p*c.thm.s[k][I],
+l.ATH.s[k]);l.pinfo.LAMEsfb_s[f][h][3*k+I]=-2*g.subblock_gain[I];k<d.SBPSY_s&&(l.pinfo.LAMEsfb_s[f][h][3*k+I]-=m*x[q]);q++}l.pinfo.LAMEqss[f][h]=g.global_gain;l.pinfo.LAMEmainbits[f][h]=g.part2_3_length+g.part2_length;l.pinfo.LAMEsfbits[f][h]=g.part2_length;l.pinfo.over[f][h]=u.over_count;l.pinfo.max_noise[f][h]=10*u.max_noise;l.pinfo.over_noise[f][h]=10*u.over_noise;l.pinfo.tot_noise[f][h]=10*u.tot_noise;l.pinfo.over_SSD[f][h]=u.over_SSD}}function Nc(){this.sfb_count1=this.global_gain=0;this.step=
+Y(39);this.noise=H(39);this.noise_log=H(39)}function Ab(){this.xr=H(576);this.l3_enc=Y(576);this.scalefac=Y(na.SFBMAX);this.mixed_block_flag=this.block_type=this.scalefac_compress=this.global_gain=this.count1=this.big_values=this.part2_3_length=this.xrpow_max=0;this.table_select=Y(3);this.subblock_gain=Y(4);this.sfbdivide=this.psymax=this.sfbmax=this.psy_lmax=this.sfb_smin=this.sfb_lmax=this.part2_length=this.count1table_select=this.scalefac_scale=this.preflag=this.region1_count=this.region0_count=
+0;this.width=Y(na.SFBMAX);this.window=Y(na.SFBMAX);this.count1bits=0;this.sfb_partition_table=null;this.slen=Y(4);this.max_nonzero_coeff=0;var d=this;this.assign=function(k){d.xr=new Float32Array(k.xr);d.l3_enc=new Int32Array(k.l3_enc);d.scalefac=new Int32Array(k.scalefac);d.xrpow_max=k.xrpow_max;d.part2_3_length=k.part2_3_length;d.big_values=k.big_values;d.count1=k.count1;d.global_gain=k.global_gain;d.scalefac_compress=k.scalefac_compress;d.block_type=k.block_type;d.mixed_block_flag=k.mixed_block_flag;
+d.table_select=new Int32Array(k.table_select);d.subblock_gain=new Int32Array(k.subblock_gain);d.region0_count=k.region0_count;d.region1_count=k.region1_count;d.preflag=k.preflag;d.scalefac_scale=k.scalefac_scale;d.count1table_select=k.count1table_select;d.part2_length=k.part2_length;d.sfb_lmax=k.sfb_lmax;d.sfb_smin=k.sfb_smin;d.psy_lmax=k.psy_lmax;d.sfbmax=k.sfbmax;d.psymax=k.psymax;d.sfbdivide=k.sfbdivide;d.width=new Int32Array(k.width);d.window=new Int32Array(k.window);d.count1bits=k.count1bits;
+d.sfb_partition_table=k.sfb_partition_table.slice(0);d.slen=new Int32Array(k.slen);d.max_nonzero_coeff=k.max_nonzero_coeff}}function Oc(){function J(d){this.ordinal=d}function k(d){for(var b=0;b<d.sfbmax;b++)if(0==d.scalefac[b]+d.subblock_gain[d.window[b]])return!1;return!0}var q;this.rv=null;var r;this.qupvt=null;var B,t=new Ic,ta;this.setModules=function(d,b,l,a){q=d;this.rv=r=b;this.qupvt=B=l;ta=a;t.setModules(B,ta)};this.ms_convert=function(d,b){for(var l=0;576>l;++l){var a=d.tt[b][0].xr[l],h=
+d.tt[b][1].xr[l];d.tt[b][0].xr[l]=.5*(a+h)*X.SQRT2;d.tt[b][1].xr[l]=.5*(a-h)*X.SQRT2}};this.init_xrpow=function(d,b,l){var a=0,h=0|b.max_nonzero_coeff;b.xrpow_max=0;Ia.fill(l,h,576,0);for(var k=a=0;k<=h;++k){var e=Math.abs(b.xr[k]),a=a+e;l[k]=Math.sqrt(e*Math.sqrt(e));l[k]>b.xrpow_max&&(b.xrpow_max=l[k])}if(1E-20<a){l=0;0!=(d.substep_shaping&2)&&(l=1);for(h=0;h<b.psymax;h++)d.pseudohalf[h]=l;return!0}Ia.fill(b.l3_enc,0,576,0);return!1};this.init_outer_loop=function(g,b){b.part2_3_length=0;b.big_values=
+0;b.count1=0;b.global_gain=210;b.scalefac_compress=0;b.table_select[0]=0;b.table_select[1]=0;b.table_select[2]=0;b.subblock_gain[0]=0;b.subblock_gain[1]=0;b.subblock_gain[2]=0;b.subblock_gain[3]=0;b.region0_count=0;b.region1_count=0;b.preflag=0;b.scalefac_scale=0;b.count1table_select=0;b.part2_length=0;b.sfb_lmax=d.SBPSY_l;b.sfb_smin=d.SBPSY_s;b.psy_lmax=g.sfb21_extra?d.SBMAX_l:d.SBPSY_l;b.psymax=b.psy_lmax;b.sfbmax=b.sfb_lmax;b.sfbdivide=11;for(var l=0;l<d.SBMAX_l;l++)b.width[l]=g.scalefac_band.l[l+
+1]-g.scalefac_band.l[l],b.window[l]=3;if(b.block_type==d.SHORT_TYPE){var a=H(576);b.sfb_smin=0;b.sfb_lmax=0;0!=b.mixed_block_flag&&(b.sfb_smin=3,b.sfb_lmax=2*g.mode_gr+4);b.psymax=b.sfb_lmax+3*((g.sfb21_extra?d.SBMAX_s:d.SBPSY_s)-b.sfb_smin);b.sfbmax=b.sfb_lmax+3*(d.SBPSY_s-b.sfb_smin);b.sfbdivide=b.sfbmax-18;b.psy_lmax=b.sfb_lmax;var h=g.scalefac_band.l[b.sfb_lmax];K.arraycopy(b.xr,0,a,0,576);for(l=b.sfb_smin;l<d.SBMAX_s;l++)for(var k=g.scalefac_band.s[l],e=g.scalefac_band.s[l+1],n=0;3>n;n++)for(var c=
+k;c<e;c++)b.xr[h++]=a[3*c+n];a=b.sfb_lmax;for(l=b.sfb_smin;l<d.SBMAX_s;l++)b.width[a]=b.width[a+1]=b.width[a+2]=g.scalefac_band.s[l+1]-g.scalefac_band.s[l],b.window[a]=0,b.window[a+1]=1,b.window[a+2]=2,a+=3}b.count1bits=0;b.sfb_partition_table=B.nr_of_sfb_block[0][0];b.slen[0]=0;b.slen[1]=0;b.slen[2]=0;b.slen[3]=0;b.max_nonzero_coeff=575;Ia.fill(b.scalefac,0);l=g.ATH;a=b.xr;if(b.block_type!=d.SHORT_TYPE)for(h=!1,k=d.PSFB21-1;0<=k&&!h;k--)for(e=g.scalefac_band.psfb21[k],n=g.scalefac_band.psfb21[k+
+1],c=B.athAdjust(l.adjust,l.psfb21[k],l.floor),1E-12<g.nsPsy.longfact[21]&&(c*=g.nsPsy.longfact[21]),--n;n>=e;n--)if(Math.abs(a[n])<c)a[n]=0;else{h=!0;break}else for(c=0;3>c;c++)for(h=!1,k=d.PSFB12-1;0<=k&&!h;k--){var e=3*g.scalefac_band.s[12]+(g.scalefac_band.s[13]-g.scalefac_band.s[12])*c+(g.scalefac_band.psfb12[k]-g.scalefac_band.psfb12[0]),n=e+(g.scalefac_band.psfb12[k+1]-g.scalefac_band.psfb12[k]),f=B.athAdjust(l.adjust,l.psfb12[k],l.floor);1E-12<g.nsPsy.shortfact[12]&&(f*=g.nsPsy.shortfact[12]);
+for(--n;n>=e;n--)if(Math.abs(a[n])<f)a[n]=0;else{h=!0;break}}};J.BINSEARCH_NONE=new J(0);J.BINSEARCH_UP=new J(1);J.BINSEARCH_DOWN=new J(2);this.trancate_smallspectrums=function(g,b,l,a){var h=H(na.SFBMAX);if((0!=(g.substep_shaping&4)||b.block_type!=d.SHORT_TYPE)&&0==(g.substep_shaping&128)){B.calc_noise(b,l,h,new $b,null);for(var k=0;576>k;k++){var e=0;0!=b.l3_enc[k]&&(e=Math.abs(b.xr[k]));a[k]=e}k=0;e=8;b.block_type==d.SHORT_TYPE&&(e=6);do{var n,c,f,q,t=b.width[e],k=k+t;if(!(1<=h[e]||(Ia.sort(a,
+k-t,t),ua.EQ(a[k-1],0)))){n=(1-h[e])*l[e];q=c=0;do{var w;for(f=1;q+f<t&&!ua.NEQ(a[q+k-t],a[q+k+f-t]);f++);w=a[q+k-t]*a[q+k-t]*f;if(n<w){0!=q&&(c=a[q+k-t-1]);break}n-=w;q+=f}while(q<t);if(!ua.EQ(c,0)){do Math.abs(b.xr[k-t])<=c&&(b.l3_enc[k-t]=0);while(0<--t)}}}while(++e<b.psymax);b.part2_3_length=ta.noquant_count_bits(g,b,null)}};this.outer_loop=function(g,b,l,a,h,q){var e=g.internal_flags,n=new Ab,c=H(576),f=H(na.SFBMAX),t=new $b,z,w=new Nc,A=9999999,v=!1,p=!1,m=0,x,y,W=e.CurrentStep[h],u=!1,r=e.OldValue[h];
+z=J.BINSEARCH_NONE;b.global_gain=r;for(x=q-b.part2_length;;){y=ta.count_bits(e,a,b,null);if(1==W||y==x)break;y>x?(z==J.BINSEARCH_DOWN&&(u=!0),u&&(W/=2),z=J.BINSEARCH_UP,y=W):(z==J.BINSEARCH_UP&&(u=!0),u&&(W/=2),z=J.BINSEARCH_DOWN,y=-W);b.global_gain+=y;0>b.global_gain&&(b.global_gain=0,u=!0);255<b.global_gain&&(b.global_gain=255,u=!0)}for(;y>x&&255>b.global_gain;)b.global_gain++,y=ta.count_bits(e,a,b,null);e.CurrentStep[h]=4<=r-b.global_gain?4:2;e.OldValue[h]=b.global_gain;b.part2_3_length=y;if(0==
+e.noise_shaping)return 100;B.calc_noise(b,l,f,t,w);t.bits=b.part2_3_length;n.assign(b);h=0;for(K.arraycopy(a,0,c,0,576);!v;){do{x=new $b;u=255;W=0!=(e.substep_shaping&2)?20:3;if(e.sfb21_extra){if(1<f[n.sfbmax])break;if(n.block_type==d.SHORT_TYPE&&(1<f[n.sfbmax+1]||1<f[n.sfbmax+2]))break}r=n;y=a;z=g.internal_flags;for(var C=r,F=f,G=y,I=g.internal_flags,T=void 0,T=0==C.scalefac_scale?1.2968395546510096:1.6817928305074292,U=0,qa=0;qa<C.sfbmax;qa++)U<F[qa]&&(U=F[qa]);qa=I.noise_shaping_amp;3==qa&&(qa=
+p?2:1);switch(qa){case 2:break;case 1:U=1<U?Math.pow(U,.5):.95*U;break;default:U=1<U?1:.95*U}for(var ia=0,qa=0;qa<C.sfbmax;qa++){var ya=C.width[qa],ia=ia+ya;if(!(F[qa]<U)){if(0!=(I.substep_shaping&2)&&(I.pseudohalf[qa]=0==I.pseudohalf[qa]?1:0,0==I.pseudohalf[qa]&&2==I.noise_shaping_amp))break;C.scalefac[qa]++;for(ya=-ya;0>ya;ya++)G[ia+ya]*=T,G[ia+ya]>C.xrpow_max&&(C.xrpow_max=G[ia+ya]);if(2==I.noise_shaping_amp)break}}if(C=k(r))r=!1;else if(C=2==z.mode_gr?ta.scale_bitcount(r):ta.scale_bitcount_lsf(z,
+r)){if(1<z.noise_shaping)if(Ia.fill(z.pseudohalf,0),0==r.scalefac_scale){C=r;for(G=F=0;G<C.sfbmax;G++){T=C.width[G];I=C.scalefac[G];0!=C.preflag&&(I+=B.pretab[G]);F+=T;if(0!=(I&1))for(I++,T=-T;0>T;T++)y[F+T]*=1.2968395546510096,y[F+T]>C.xrpow_max&&(C.xrpow_max=y[F+T]);C.scalefac[G]=I>>1}C.preflag=0;C.scalefac_scale=1;C=!1}else if(r.block_type==d.SHORT_TYPE&&0<z.subblock_gain){b:{C=z;F=r;G=void 0;I=F.scalefac;for(G=0;G<F.sfb_lmax;G++)if(16<=I[G]){y=!0;break b}for(T=0;3>T;T++){qa=U=0;for(G=F.sfb_lmax+
+T;G<F.sfbdivide;G+=3)U<I[G]&&(U=I[G]);for(;G<F.sfbmax;G+=3)qa<I[G]&&(qa=I[G]);if(!(16>U&&8>qa)){if(7<=F.subblock_gain[T]){y=!0;break b}F.subblock_gain[T]++;U=C.scalefac_band.l[F.sfb_lmax];for(G=F.sfb_lmax+T;G<F.sfbmax;G+=3)if(qa=F.width[G],ia=I[G],ia-=4>>F.scalefac_scale,0<=ia)I[G]=ia,U+=3*qa;else{I[G]=0;ia=B.IPOW20(210+(ia<<F.scalefac_scale+1));U+=qa*(T+1);for(ya=-qa;0>ya;ya++)y[U+ya]*=ia,y[U+ya]>F.xrpow_max&&(F.xrpow_max=y[U+ya]);U+=qa*(3-T-1)}ia=B.IPOW20(202);U+=F.width[G]*(T+1);for(ya=-F.width[G];0>
+ya;ya++)y[U+ya]*=ia,y[U+ya]>F.xrpow_max&&(F.xrpow_max=y[U+ya])}}y=!1}C=y||k(r)}C||(C=2==z.mode_gr?ta.scale_bitcount(r):ta.scale_bitcount_lsf(z,r));r=!C}else r=!0;if(!r)break;0!=n.scalefac_scale&&(u=254);r=q-n.part2_length;if(0>=r)break;for(;(n.part2_3_length=ta.count_bits(e,a,n,w))>r&&n.global_gain<=u;)n.global_gain++;if(n.global_gain>u)break;if(0==t.over_count){for(;(n.part2_3_length=ta.count_bits(e,a,n,w))>A&&n.global_gain<=u;)n.global_gain++;if(n.global_gain>u)break}B.calc_noise(n,l,f,x,w);x.bits=
+n.part2_3_length;z=b.block_type!=d.SHORT_TYPE?g.quant_comp:g.quant_comp_short;u=t;r=x;C=n;y=f;F=void 0;switch(z){default:case 9:0<u.over_count?(F=r.over_SSD<=u.over_SSD,r.over_SSD==u.over_SSD&&(F=r.bits<u.bits)):F=0>r.max_noise&&10*r.max_noise+r.bits<=10*u.max_noise+u.bits;break;case 0:F=r.over_count<u.over_count||r.over_count==u.over_count&&r.over_noise<u.over_noise||r.over_count==u.over_count&&ua.EQ(r.over_noise,u.over_noise)&&r.tot_noise<u.tot_noise;break;case 8:z=r;G=1E-37;for(F=0;F<C.psymax;F++)I=
+y[F],I=X.FAST_LOG10(.368+.632*I*I*I),G+=I;y=Math.max(1E-20,G);z.max_noise=y;case 1:F=r.max_noise<u.max_noise;break;case 2:F=r.tot_noise<u.tot_noise;break;case 3:F=r.tot_noise<u.tot_noise&&r.max_noise<u.max_noise;break;case 4:F=0>=r.max_noise&&.2<u.max_noise||0>=r.max_noise&&0>u.max_noise&&u.max_noise>r.max_noise-.2&&r.tot_noise<u.tot_noise||0>=r.max_noise&&0<u.max_noise&&u.max_noise>r.max_noise-.2&&r.tot_noise<u.tot_noise+u.over_noise||0<r.max_noise&&-.05<u.max_noise&&u.max_noise>r.max_noise-.1&&
+r.tot_noise+r.over_noise<u.tot_noise+u.over_noise||0<r.max_noise&&-.1<u.max_noise&&u.max_noise>r.max_noise-.15&&r.tot_noise+r.over_noise+r.over_noise<u.tot_noise+u.over_noise+u.over_noise;break;case 5:F=r.over_noise<u.over_noise||ua.EQ(r.over_noise,u.over_noise)&&r.tot_noise<u.tot_noise;break;case 6:F=r.over_noise<u.over_noise||ua.EQ(r.over_noise,u.over_noise)&&(r.max_noise<u.max_noise||ua.EQ(r.max_noise,u.max_noise)&&r.tot_noise<=u.tot_noise);break;case 7:F=r.over_count<u.over_count||r.over_noise<
+u.over_noise}0==u.over_count&&(F=F&&r.bits<u.bits);z=F?1:0;if(0!=z)A=b.part2_3_length,t=x,b.assign(n),h=0,K.arraycopy(a,0,c,0,576);else if(0==e.full_outer_loop){if(++h>W&&0==t.over_count)break;if(3==e.noise_shaping_amp&&p&&30<h)break;if(3==e.noise_shaping_amp&&p&&15<n.global_gain-m)break}}while(255>n.global_gain+n.scalefac_scale);3==e.noise_shaping_amp?p?v=!0:(n.assign(b),K.arraycopy(c,0,a,0,576),h=0,m=n.global_gain,p=!0):v=!0}g.VBR==M.vbr_rh||g.VBR==M.vbr_mtrh?K.arraycopy(c,0,a,0,576):0!=(e.substep_shaping&
+1)&&trancate_smallspectrums(e,b,l,a);return t.over_count};this.iteration_finish_one=function(d,b,l){var a=d.l3_side,h=a.tt[b][l];ta.best_scalefac_store(d,b,l,a);1==d.use_best_huffman&&ta.best_huffman_divide(d,h);r.ResvAdjust(d,h)};this.VBR_encode_granule=function(d,b,l,a,h,k,e){var n=d.internal_flags,c=new Ab,f=H(576),q=e,t=e+1,t=(e+k)/2,r,A=0,v=n.sfb21_extra;Ia.fill(c.l3_enc,0);do n.sfb21_extra=t>q-42?!1:v,r=outer_loop(d,b,l,a,h,t),0>=r?(A=1,t=b.part2_3_length,c.assign(b),K.arraycopy(a,0,f,0,576),
+e=t-32,r=e-k,t=(e+k)/2):(k=t+32,r=e-k,t=(e+k)/2,0!=A&&(A=2,b.assign(c),K.arraycopy(f,0,a,0,576)));while(12<r);n.sfb21_extra=v;2==A&&K.arraycopy(c.l3_enc,0,b.l3_enc,0,576)};this.get_framebits=function(d,b){var l=d.internal_flags;l.bitrate_index=l.VBR_min_bitrate;var a=q.getframebits(d);l.bitrate_index=1;for(var a=q.getframebits(d),h=1;h<=l.VBR_max_bitrate;h++)l.bitrate_index=h,a=new eb(a),b[h]=r.ResvFrameBegin(d,a),a=a.bits};this.VBR_old_prepare=function(g,b,l,a,h,k,e,n,c){var f=g.internal_flags,q;
+q=0;var t=1,w=0;f.bitrate_index=f.VBR_max_bitrate;var A=r.ResvFrameBegin(g,new eb(0))/f.mode_gr;get_framebits(g,k);for(var v=0;v<f.mode_gr;v++){var p=B.on_pe(g,b,n[v],A,v,0);f.mode_ext==d.MPG_MD_MS_LR&&(ms_convert(f.l3_side,v),B.reduce_side(n[v],l[v],A,p));for(p=0;p<f.channels_out;++p){var m=f.l3_side.tt[v][p];m.block_type!=d.SHORT_TYPE?(q=1.28/(1+Math.exp(3.5-b[v][p]/300))-.05,q=f.PSY.mask_adjust-q):(q=2.56/(1+Math.exp(3.5-b[v][p]/300))-.14,q=f.PSY.mask_adjust_short-q);f.masking_lower=Math.pow(10,
+.1*q);init_outer_loop(f,m);c[v][p]=B.calc_xmin(g,a[v][p],m,h[v][p]);0!=c[v][p]&&(t=0);e[v][p]=126;w+=n[v][p]}}for(v=0;v<f.mode_gr;v++)for(p=0;p<f.channels_out;p++)w>k[f.VBR_max_bitrate]&&(n[v][p]*=k[f.VBR_max_bitrate],n[v][p]/=w),e[v][p]>n[v][p]&&(e[v][p]=n[v][p]);return t};this.bitpressure_strategy=function(g,b,l,a){for(var h=0;h<g.mode_gr;h++)for(var k=0;k<g.channels_out;k++){for(var e=g.l3_side.tt[h][k],n=b[h][k],c=0,f=0;f<e.psy_lmax;f++)n[c++]*=1+.029*f*f/d.SBMAX_l/d.SBMAX_l;if(e.block_type==
+d.SHORT_TYPE)for(f=e.sfb_smin;f<d.SBMAX_s;f++)n[c++]*=1+.029*f*f/d.SBMAX_s/d.SBMAX_s,n[c++]*=1+.029*f*f/d.SBMAX_s/d.SBMAX_s,n[c++]*=1+.029*f*f/d.SBMAX_s/d.SBMAX_s;a[h][k]=0|Math.max(l[h][k],.9*a[h][k])}};this.VBR_new_prepare=function(g,b,k,a,h,q){var e=g.internal_flags,n=1,c=0,f=0,t;g.free_format?(e.bitrate_index=0,c=new eb(c),t=r.ResvFrameBegin(g,c),c=c.bits,h[0]=t):(e.bitrate_index=e.VBR_max_bitrate,c=new eb(c),r.ResvFrameBegin(g,c),c=c.bits,get_framebits(g,h),t=h[e.VBR_max_bitrate]);for(h=0;h<
+e.mode_gr;h++){B.on_pe(g,b,q[h],c,h,0);e.mode_ext==d.MPG_MD_MS_LR&&ms_convert(e.l3_side,h);for(var z=0;z<e.channels_out;++z){var w=e.l3_side.tt[h][z];e.masking_lower=Math.pow(10,.1*e.PSY.mask_adjust);init_outer_loop(e,w);0!=B.calc_xmin(g,k[h][z],w,a[h][z])&&(n=0);f+=q[h][z]}}for(h=0;h<e.mode_gr;h++)for(z=0;z<e.channels_out;z++)f>t&&(q[h][z]*=t,q[h][z]/=f);return n};this.calc_target_bits=function(g,b,k,a,h,t){var e=g.internal_flags,n=e.l3_side,c,f;e.bitrate_index=e.VBR_max_bitrate;f=new eb(0);t[0]=
+r.ResvFrameBegin(g,f);e.bitrate_index=1;f=q.getframebits(g)-8*e.sideinfo_len;h[0]=f/(e.mode_gr*e.channels_out);f=g.VBR_mean_bitrate_kbps*g.framesize*1E3;0!=(e.substep_shaping&1)&&(f*=1.09);f/=g.out_samplerate;f-=8*e.sideinfo_len;f/=e.mode_gr*e.channels_out;c=.93+.07*(11-g.compression_ratio)/5.5;.9>c&&(c=.9);1<c&&(c=1);for(g=0;g<e.mode_gr;g++){var E=0;for(h=0;h<e.channels_out;h++){a[g][h]=int(c*f);if(700<b[g][h]){var z=int((b[g][h]-700)/1.4),w=n.tt[g][h];a[g][h]=int(c*f);w.block_type==d.SHORT_TYPE&&
+z<f/2&&(z=f/2);z>3*f/2?z=3*f/2:0>z&&(z=0);a[g][h]+=z}a[g][h]>da.MAX_BITS_PER_CHANNEL&&(a[g][h]=da.MAX_BITS_PER_CHANNEL);E+=a[g][h]}if(E>da.MAX_BITS_PER_GRANULE)for(h=0;h<e.channels_out;++h)a[g][h]*=da.MAX_BITS_PER_GRANULE,a[g][h]/=E}if(e.mode_ext==d.MPG_MD_MS_LR)for(g=0;g<e.mode_gr;g++)B.reduce_side(a[g],k[g],f*e.channels_out,da.MAX_BITS_PER_GRANULE);for(g=b=0;g<e.mode_gr;g++)for(h=0;h<e.channels_out;h++)a[g][h]>da.MAX_BITS_PER_CHANNEL&&(a[g][h]=da.MAX_BITS_PER_CHANNEL),b+=a[g][h];if(b>t[0])for(g=
+0;g<e.mode_gr;g++)for(h=0;h<e.channels_out;h++)a[g][h]*=t[0],a[g][h]/=b}}function Qa(){this.thm=new ic;this.en=new ic}function Pc(){function r(b,d,a){for(var g=10,q=d+238-14-286,e=-15;0>e;e++){var n,c,f;n=k[g+-10];c=b[q+-224]*n;f=b[d+224]*n;n=k[g+-9];c+=b[q+-160]*n;f+=b[d+160]*n;n=k[g+-8];c+=b[q+-96]*n;f+=b[d+96]*n;n=k[g+-7];c+=b[q+-32]*n;f+=b[d+32]*n;n=k[g+-6];c+=b[q+32]*n;f+=b[d+-32]*n;n=k[g+-5];c+=b[q+96]*n;f+=b[d+-96]*n;n=k[g+-4];c+=b[q+160]*n;f+=b[d+-160]*n;n=k[g+-3];c+=b[q+224]*n;f+=b[d+-224]*
+n;n=k[g+-2];c+=b[d+-256]*n;f-=b[q+256]*n;n=k[g+-1];c+=b[d+-192]*n;f-=b[q+192]*n;n=k[g+0];c+=b[d+-128]*n;f-=b[q+128]*n;n=k[g+1];c+=b[d+-64]*n;f-=b[q+64]*n;n=k[g+2];c+=b[d+0]*n;f-=b[q+0]*n;n=k[g+3];c+=b[d+64]*n;f-=b[q+-64]*n;n=k[g+4];c+=b[d+128]*n;f-=b[q+-128]*n;n=k[g+5];c+=b[d+192]*n;f-=b[q+-192]*n;c*=k[g+6];n=f-c;a[30+2*e]=f+c;a[31+2*e]=k[g+7]*n;g+=18;d--;q++}f=b[d+-16]*k[g+-10];c=b[d+-32]*k[g+-2];f+=(b[d+-48]-b[d+16])*k[g+-9];c+=b[d+-96]*k[g+-1];f+=(b[d+-80]+b[d+48])*k[g+-8];c+=b[d+-160]*k[g+0];
+f+=(b[d+-112]-b[d+80])*k[g+-7];c+=b[d+-224]*k[g+1];f+=(b[d+-144]+b[d+112])*k[g+-6];c-=b[d+32]*k[g+2];f+=(b[d+-176]-b[d+144])*k[g+-5];c-=b[d+96]*k[g+3];f+=(b[d+-208]+b[d+176])*k[g+-4];c-=b[d+160]*k[g+4];f+=(b[d+-240]-b[d+208])*k[g+-3];c-=b[d+224];b=c-f;d=c+f;f=a[14];c=a[15]-f;a[31]=d+f;a[30]=b+c;a[15]=b-c;a[14]=d-f;c=a[28]-a[0];a[0]+=a[28];a[28]=c*k[g+-36+7];c=a[29]-a[1];a[1]+=a[29];a[29]=c*k[g+-36+7];c=a[26]-a[2];a[2]+=a[26];a[26]=c*k[g+-72+7];c=a[27]-a[3];a[3]+=a[27];a[27]=c*k[g+-72+7];c=a[24]-a[4];
+a[4]+=a[24];a[24]=c*k[g+-108+7];c=a[25]-a[5];a[5]+=a[25];a[25]=c*k[g+-108+7];c=a[22]-a[6];a[6]+=a[22];a[22]=c*X.SQRT2;c=a[23]-a[7];a[7]+=a[23];a[23]=c*X.SQRT2-a[7];a[7]-=a[6];a[22]-=a[7];a[23]-=a[22];c=a[6];a[6]=a[31]-c;a[31]+=c;c=a[7];a[7]=a[30]-c;a[30]+=c;c=a[22];a[22]=a[15]-c;a[15]+=c;c=a[23];a[23]=a[14]-c;a[14]+=c;c=a[20]-a[8];a[8]+=a[20];a[20]=c*k[g+-180+7];c=a[21]-a[9];a[9]+=a[21];a[21]=c*k[g+-180+7];c=a[18]-a[10];a[10]+=a[18];a[18]=c*k[g+-216+7];c=a[19]-a[11];a[11]+=a[19];a[19]=c*k[g+-216+
+7];c=a[16]-a[12];a[12]+=a[16];a[16]=c*k[g+-252+7];c=a[17]-a[13];a[13]+=a[17];a[17]=c*k[g+-252+7];c=-a[20]+a[24];a[20]+=a[24];a[24]=c*k[g+-216+7];c=-a[21]+a[25];a[21]+=a[25];a[25]=c*k[g+-216+7];c=a[4]-a[8];a[4]+=a[8];a[8]=c*k[g+-216+7];c=a[5]-a[9];a[5]+=a[9];a[9]=c*k[g+-216+7];c=a[0]-a[12];a[0]+=a[12];a[12]=c*k[g+-72+7];c=a[1]-a[13];a[1]+=a[13];a[13]=c*k[g+-72+7];c=a[16]-a[28];a[16]+=a[28];a[28]=c*k[g+-72+7];c=-a[17]+a[29];a[17]+=a[29];a[29]=c*k[g+-72+7];c=X.SQRT2*(a[2]-a[10]);a[2]+=a[10];a[10]=c;
+c=X.SQRT2*(a[3]-a[11]);a[3]+=a[11];a[11]=c;c=X.SQRT2*(-a[18]+a[26]);a[18]+=a[26];a[26]=c-a[18];c=X.SQRT2*(-a[19]+a[27]);a[19]+=a[27];a[27]=c-a[19];c=a[2];a[19]-=a[3];a[3]-=c;a[2]=a[31]-c;a[31]+=c;c=a[3];a[11]-=a[19];a[18]-=c;a[3]=a[30]-c;a[30]+=c;c=a[18];a[27]-=a[11];a[19]-=c;a[18]=a[15]-c;a[15]+=c;c=a[19];a[10]-=c;a[19]=a[14]-c;a[14]+=c;c=a[10];a[11]-=c;a[10]=a[23]-c;a[23]+=c;c=a[11];a[26]-=c;a[11]=a[22]-c;a[22]+=c;c=a[26];a[27]-=c;a[26]=a[7]-c;a[7]+=c;c=a[27];a[27]=a[6]-c;a[6]+=c;c=X.SQRT2*(a[0]-
+a[4]);a[0]+=a[4];a[4]=c;c=X.SQRT2*(a[1]-a[5]);a[1]+=a[5];a[5]=c;c=X.SQRT2*(a[16]-a[20]);a[16]+=a[20];a[20]=c;c=X.SQRT2*(a[17]-a[21]);a[17]+=a[21];a[21]=c;c=-X.SQRT2*(a[8]-a[12]);a[8]+=a[12];a[12]=c-a[8];c=-X.SQRT2*(a[9]-a[13]);a[9]+=a[13];a[13]=c-a[9];c=-X.SQRT2*(a[25]-a[29]);a[25]+=a[29];a[29]=c-a[25];c=-X.SQRT2*(a[24]+a[28]);a[24]-=a[28];a[28]=c-a[24];c=a[24]-a[16];a[24]=c;c=a[20]-c;a[20]=c;c=a[28]-c;a[28]=c;c=a[25]-a[17];a[25]=c;c=a[21]-c;a[21]=c;c=a[29]-c;a[29]=c;c=a[17]-a[1];a[17]=c;c=a[9]-c;
+a[9]=c;c=a[25]-c;a[25]=c;c=a[5]-c;a[5]=c;c=a[21]-c;a[21]=c;c=a[13]-c;a[13]=c;c=a[29]-c;a[29]=c;c=a[1]-a[0];a[1]=c;c=a[16]-c;a[16]=c;c=a[17]-c;a[17]=c;c=a[8]-c;a[8]=c;c=a[9]-c;a[9]=c;c=a[24]-c;a[24]=c;c=a[25]-c;a[25]=c;c=a[4]-c;a[4]=c;c=a[5]-c;a[5]=c;c=a[20]-c;a[20]=c;c=a[21]-c;a[21]=c;c=a[12]-c;a[12]=c;c=a[13]-c;a[13]=c;c=a[28]-c;a[28]=c;c=a[29]-c;a[29]=c;c=a[0];a[0]+=a[31];a[31]-=c;c=a[1];a[1]+=a[30];a[30]-=c;c=a[16];a[16]+=a[15];a[15]-=c;c=a[17];a[17]+=a[14];a[14]-=c;c=a[8];a[8]+=a[23];a[23]-=c;
+c=a[9];a[9]+=a[22];a[22]-=c;c=a[24];a[24]+=a[7];a[7]-=c;c=a[25];a[25]+=a[6];a[6]-=c;c=a[4];a[4]+=a[27];a[27]-=c;c=a[5];a[5]+=a[26];a[26]-=c;c=a[20];a[20]+=a[11];a[11]-=c;c=a[21];a[21]+=a[10];a[10]-=c;c=a[12];a[12]+=a[19];a[19]-=c;c=a[13];a[13]+=a[18];a[18]-=c;c=a[28];a[28]+=a[3];a[3]-=c;c=a[29];a[29]+=a[2];a[2]-=c}var k=[-.1482523854003001,32.308141959636465,296.40344946382766,883.1344870032432,11113.947376231741,1057.2713659324597,305.7402417275812,30.825928907280012,3.8533188138216365,59.42900443849514,
+709.5899960123345,5281.91112291017,-5829.66483675846,-817.6293103748613,-76.91656988279972,-4.594269939176596,.9063471690191471,.1960342806591213,-.15466694054279598,34.324387823855965,301.8067566458425,817.599602898885,11573.795901679885,1181.2520595540152,321.59731579894424,31.232021761053772,3.7107095756221318,53.650946155329365,684.167428119626,5224.56624370173,-6366.391851890084,-908.9766368219582,-89.83068876699639,-5.411397422890401,.8206787908286602,.3901806440322567,-.16070888947830023,36.147034243915876,
+304.11815768187864,732.7429163887613,11989.60988270091,1300.012278487897,335.28490093152146,31.48816102859945,3.373875931311736,47.232241542899175,652.7371796173471,5132.414255594984,-6909.087078780055,-1001.9990371107289,-103.62185754286375,-6.104916304710272,.7416505462720353,.5805693545089249,-.16636367662261495,37.751650073343995,303.01103387567713,627.9747488785183,12358.763425278165,1412.2779918482834,346.7496836825721,31.598286663170416,3.1598635433980946,40.57878626349686,616.1671130880391,
+5007.833007176154,-7454.040671756168,-1095.7960341867115,-118.24411666465777,-6.818469345853504,.6681786379192989,.7653668647301797,-.1716176790982088,39.11551877123304,298.3413246578966,503.5259106886539,12679.589408408976,1516.5821921214542,355.9850766329023,31.395241710249053,2.9164211881972335,33.79716964664243,574.8943997801362,4853.234992253242,-7997.57021486075,-1189.7624067269965,-133.6444792601766,-7.7202770609839915,.5993769336819237,.9427934736519954,-.17645823955292173,40.21879108166477,
+289.9982036694474,359.3226160751053,12950.259102786438,1612.1013903507662,362.85067106591504,31.045922092242872,2.822222032597987,26.988862316190684,529.8996541764288,4671.371946949588,-8535.899136645805,-1282.5898586244496,-149.58553632943463,-8.643494270763135,.5345111359507916,1.111140466039205,-.36174739330527045,41.04429910497807,277.5463268268618,195.6386023135583,13169.43812144731,1697.6433561479398,367.40983966190305,30.557037410382826,2.531473372857427,20.070154905927314,481.50208566532336,
+4464.970341588308,-9065.36882077239,-1373.62841526722,-166.1660487028118,-9.58289321133207,.4729647758913199,1.268786568327291,-.36970682634889585,41.393213350082036,261.2935935556502,12.935476055240873,13336.131683328815,1772.508612059496,369.76534388639965,29.751323653701338,2.4023193045459172,13.304795348228817,430.5615775526625,4237.0568611071185,-9581.931701634761,-1461.6913552409758,-183.12733958476446,-10.718010163869403,.41421356237309503,1.414213562373095,-.37677560326535325,41.619486213528496,
+241.05423794991074,-187.94665032361226,13450.063605744153,1836.153896465782,369.4908799925761,29.001847876923147,2.0714759319987186,6.779591200894186,377.7767837205709,3990.386575512536,-10081.709459700915,-1545.947424837898,-200.3762958015653,-11.864482073055006,.3578057213145241,1.546020906725474,-.3829366947518991,41.1516456456653,216.47684307105183,-406.1569483347166,13511.136535077321,1887.8076599260432,367.3025214564151,28.136213436723654,1.913880671464418,.3829366947518991,323.85365704338597,
+3728.1472257487526,-10561.233882199509,-1625.2025997821418,-217.62525175416,-13.015432208941645,.3033466836073424,1.66293922460509,-.5822628872992417,40.35639251440489,188.20071124269245,-640.2706748618148,13519.21490106562,1927.6022433578062,362.8197642637487,26.968821921868447,1.7463817695935329,-5.62650678237171,269.3016715297017,3453.386536448852,-11016.145278780888,-1698.6569643425091,-234.7658734267683,-14.16351421663124,.2504869601913055,1.76384252869671,-.5887180101749253,39.23429103868072,
+155.76096234403798,-889.2492977967378,13475.470561874661,1955.0535223723712,356.4450994756727,25.894952980042156,1.5695032905781554,-11.181939564328772,214.80884394039484,3169.1640829158237,-11443.321309975563,-1765.1588461316153,-251.68908574481912,-15.49755935939164,.198912367379658,1.847759065022573,-.7912582233652842,37.39369355329111,119.699486012458,-1151.0956593239027,13380.446257078214,1970.3952110853447,348.01959814116185,24.731487364283044,1.3850130831637748,-16.421408865300393,161.05030052864092,
+2878.3322807850063,-11838.991423510031,-1823.985884688674,-268.2854986386903,-16.81724543849939,.1483359875383474,1.913880671464418,-.7960642926861912,35.2322109610459,80.01928065061526,-1424.0212633405113,13235.794061869668,1973.804052543835,337.9908651258184,23.289159354463873,1.3934255946442087,-21.099669467133474,108.48348407242611,2583.700758091299,-12199.726194855148,-1874.2780658979746,-284.2467154529415,-18.11369784385905,.09849140335716425,1.961570560806461,-.998795456205172,32.56307803611191,
+36.958364584370486,-1706.075448829146,13043.287458812016,1965.3831106103316,326.43182772364605,22.175018750622293,1.198638339011324,-25.371248002043963,57.53505923036915,2288.41886619975,-12522.674544337233,-1914.8400385312243,-299.26241273417224,-19.37805630698734,.04912684976946725,1.990369453344394,.0178904535*X.SQRT2/2.384E-6,.008938074*X.SQRT2/2.384E-6,.0015673635*X.SQRT2/2.384E-6,.001228571*X.SQRT2/2.384E-6,4.856585E-4*X.SQRT2/2.384E-6,1.09434E-4*X.SQRT2/2.384E-6,5.0783E-5*X.SQRT2/2.384E-6,
+6.914E-6*X.SQRT2/2.384E-6,12804.797818791945,1945.5515939597317,313.4244966442953,20.801593959731544,1995.1556208053692,9.000838926174497,-29.20218120805369],q=[[2.382191739347913E-13,6.423305872147834E-13,9.400849094049688E-13,1.122435026096556E-12,1.183840321267481E-12,1.122435026096556E-12,9.40084909404969E-13,6.423305872147839E-13,2.382191739347918E-13,5.456116108943412E-12,4.878985199565852E-12,4.240448995017367E-12,3.559909094758252E-12,2.858043359288075E-12,2.156177623817898E-12,1.475637723558783E-12,
+8.371015190102974E-13,2.599706096327376E-13,-5.456116108943412E-12,-4.878985199565852E-12,-4.240448995017367E-12,-3.559909094758252E-12,-2.858043359288076E-12,-2.156177623817898E-12,-1.475637723558783E-12,-8.371015190102975E-13,-2.599706096327376E-13,-2.382191739347923E-13,-6.423305872147843E-13,-9.400849094049696E-13,-1.122435026096556E-12,-1.183840321267481E-12,-1.122435026096556E-12,-9.400849094049694E-13,-6.42330587214784E-13,-2.382191739347918E-13],[2.382191739347913E-13,6.423305872147834E-13,
+9.400849094049688E-13,1.122435026096556E-12,1.183840321267481E-12,1.122435026096556E-12,9.400849094049688E-13,6.423305872147841E-13,2.382191739347918E-13,5.456116108943413E-12,4.878985199565852E-12,4.240448995017367E-12,3.559909094758253E-12,2.858043359288075E-12,2.156177623817898E-12,1.475637723558782E-12,8.371015190102975E-13,2.599706096327376E-13,-5.461314069809755E-12,-4.921085770524055E-12,-4.343405037091838E-12,-3.732668368707687E-12,-3.093523840190885E-12,-2.430835727329465E-12,-1.734679010007751E-12,
+-9.74825365660928E-13,-2.797435120168326E-13,0,0,0,0,0,0,-2.283748241799531E-13,-4.037858874020686E-13,-2.146547464825323E-13],[.1316524975873958,.414213562373095,.7673269879789602,1.091308501069271,1.303225372841206,1.56968557711749,1.920982126971166,2.414213562373094,3.171594802363212,4.510708503662055,7.595754112725146,22.90376554843115,.984807753012208,.6427876096865394,.3420201433256688,.9396926207859084,-.1736481776669303,-.7660444431189779,.8660254037844387,.5,-.5144957554275265,-.4717319685649723,
+-.3133774542039019,-.1819131996109812,-.09457419252642064,-.04096558288530405,-.01419856857247115,-.003699974673760037,.8574929257125442,.8817419973177052,.9496286491027329,.9833145924917901,.9955178160675857,.9991605581781475,.999899195244447,.9999931550702802],[0,0,0,0,0,0,2.283748241799531E-13,4.037858874020686E-13,2.146547464825323E-13,5.461314069809755E-12,4.921085770524055E-12,4.343405037091838E-12,3.732668368707687E-12,3.093523840190885E-12,2.430835727329466E-12,1.734679010007751E-12,9.74825365660928E-13,
+2.797435120168326E-13,-5.456116108943413E-12,-4.878985199565852E-12,-4.240448995017367E-12,-3.559909094758253E-12,-2.858043359288075E-12,-2.156177623817898E-12,-1.475637723558782E-12,-8.371015190102975E-13,-2.599706096327376E-13,-2.382191739347913E-13,-6.423305872147834E-13,-9.400849094049688E-13,-1.122435026096556E-12,-1.183840321267481E-12,-1.122435026096556E-12,-9.400849094049688E-13,-6.423305872147841E-13,-2.382191739347918E-13]],C=q[d.SHORT_TYPE],B=q[d.SHORT_TYPE],t=q[d.SHORT_TYPE],M=q[d.SHORT_TYPE],
+g=[0,1,16,17,8,9,24,25,4,5,20,21,12,13,28,29,2,3,18,19,10,11,26,27,6,7,22,23,14,15,30,31];this.mdct_sub48=function(b,k,a){for(var h=286,D=0;D<b.channels_out;D++){for(var e=0;e<b.mode_gr;e++){for(var n,c=b.l3_side.tt[e][D],f=c.xr,E=0,z=b.sb_sample[D][1-e],w=0,A=0;9>A;A++)for(r(k,h,z[w]),r(k,h+32,z[w+1]),w+=2,h+=64,n=1;32>n;n+=2)z[w-1][n]*=-1;for(n=0;32>n;n++,E+=18){var z=c.block_type,w=b.sb_sample[D][e],v=b.sb_sample[D][1-e];0!=c.mixed_block_flag&&2>n&&(z=0);if(1E-12>b.amp_filter[n])Ia.fill(f,E+0,
+E+18,0);else{if(1>b.amp_filter[n])for(A=0;18>A;A++)v[A][g[n]]*=b.amp_filter[n];if(z==d.SHORT_TYPE){for(A=-3;0>A;A++){var p=q[d.SHORT_TYPE][A+3];f[E+3*A+9]=w[9+A][g[n]]*p-w[8-A][g[n]];f[E+3*A+18]=w[14-A][g[n]]*p+w[15+A][g[n]];f[E+3*A+10]=w[15+A][g[n]]*p-w[14-A][g[n]];f[E+3*A+19]=v[2-A][g[n]]*p+v[3+A][g[n]];f[E+3*A+11]=v[3+A][g[n]]*p-v[2-A][g[n]];f[E+3*A+20]=v[8-A][g[n]]*p+v[9+A][g[n]]}A=f;w=E;for(p=0;3>p;p++){var m,x,y,W,u;y=A[w+6]*q[d.SHORT_TYPE][0]-A[w+15];v=A[w+0]*q[d.SHORT_TYPE][2]-A[w+9];m=y+
+v;x=y-v;y=A[w+15]*q[d.SHORT_TYPE][0]+A[w+6];v=A[w+9]*q[d.SHORT_TYPE][2]+A[w+0];W=y+v;u=-y+v;v=2.069978111953089E-11*(A[w+3]*q[d.SHORT_TYPE][1]-A[w+12]);y=2.069978111953089E-11*(A[w+12]*q[d.SHORT_TYPE][1]+A[w+3]);A[w+0]=1.90752519173728E-11*m+v;A[w+15]=1.90752519173728E-11*-W+y;x*=1.6519652744032674E-11;W=9.537625958686404E-12*W+y;A[w+3]=x-W;A[w+6]=x+W;m=9.537625958686404E-12*m-v;u*=1.6519652744032674E-11;A[w+9]=m+u;A[w+12]=m-u;w++}}else{p=H(18);for(A=-9;0>A;A++)m=q[z][A+27]*v[A+9][g[n]]+q[z][A+36]*
+v[8-A][g[n]],x=q[z][A+9]*w[A+9][g[n]]-q[z][A+18]*w[8-A][g[n]],p[A+9]=m-x*C[3+A+9],p[A+18]=m*C[3+A+9]+x;var A=f,w=E,pa=u=W=y=x=m=v=void 0,la=void 0,F=void 0,G=void 0;x=p[17]-p[9];W=p[15]-p[11];u=p[14]-p[12];pa=p[0]+p[8];la=p[1]+p[7];F=p[2]+p[6];G=p[3]+p[5];A[w+17]=pa+F-G-(la-p[4]);m=(pa+F-G)*B[19]+(la-p[4]);v=(x-W-u)*B[18];A[w+5]=v+m;A[w+6]=v-m;y=(p[16]-p[10])*B[18];la=la*B[19]+p[4];v=x*B[12]+y+W*B[13]+u*B[14];m=-pa*B[16]+la-F*B[17]+G*B[15];A[w+1]=v+m;A[w+2]=v-m;v=x*B[13]-y-W*B[14]+u*B[12];m=-pa*B[17]+
+la-F*B[15]+G*B[16];A[w+9]=v+m;A[w+10]=v-m;v=x*B[14]-y+W*B[12]-u*B[13];m=pa*B[15]-la+F*B[16]-G*B[17];A[w+13]=v+m;A[w+14]=v-m;G=F=la=pa=u=W=y=x=void 0;x=p[8]-p[0];W=p[6]-p[2];u=p[5]-p[3];pa=p[17]+p[9];la=p[16]+p[10];F=p[15]+p[11];G=p[14]+p[12];A[w+0]=pa+F+G+(la+p[13]);v=(pa+F+G)*B[19]-(la+p[13]);m=(x-W+u)*B[18];A[w+11]=v+m;A[w+12]=v-m;y=(p[7]-p[1])*B[18];la=p[13]-la*B[19];v=pa*B[15]-la+F*B[16]+G*B[17];m=x*B[14]+y+W*B[12]+u*B[13];A[w+3]=v+m;A[w+4]=v-m;v=-pa*B[17]+la-F*B[15]-G*B[16];m=x*B[13]+y-W*B[14]-
+u*B[12];A[w+7]=v+m;A[w+8]=v-m;v=-pa*B[16]+la-F*B[17]-G*B[15];m=x*B[12]-y+W*B[13]-u*B[14];A[w+15]=v+m;A[w+16]=v-m}}if(z!=d.SHORT_TYPE&&0!=n)for(A=7;0<=A;--A)z=f[E+A]*t[20+A]+f[E+-1-A]*M[28+A],w=f[E+A]*M[28+A]-f[E+-1-A]*t[20+A],f[E+-1-A]=z,f[E+A]=w}}k=a;h=286;if(1==b.mode_gr)for(e=0;18>e;e++)K.arraycopy(b.sb_sample[D][1][e],0,b.sb_sample[D][0][e],0,32)}}}function d(){var r=d.FFTOFFSET,k=d.MPG_MD_MS_LR,q=null,C=this.psy=null,B=null,t=null;this.setModules=function(d,b,k,a){q=d;C=this.psy=b;B=a;t=k};var ta=
+new Pc;this.lame_encode_mp3_frame=function(g,b,l,a,h,D){var e=Zb([2,2]);e[0][0]=new Qa;e[0][1]=new Qa;e[1][0]=new Qa;e[1][1]=new Qa;var n=Zb([2,2]);n[0][0]=new Qa;n[0][1]=new Qa;n[1][0]=new Qa;n[1][1]=new Qa;var c=[null,null],f=g.internal_flags,E=ra([2,4]),z=[.5,.5],w=[[0,0],[0,0]],A=[[0,0],[0,0]];c[0]=b;c[1]=l;if(0==f.lame_encode_frame_init){b=g.internal_flags;var v,p;if(0==b.lame_encode_frame_init){l=H(2014);var m=H(2014);b.lame_encode_frame_init=1;for(p=v=0;v<286+576*(1+b.mode_gr);++v)v<576*b.mode_gr?
+(l[v]=0,2==b.channels_out&&(m[v]=0)):(l[v]=c[0][p],2==b.channels_out&&(m[v]=c[1][p]),++p);for(p=0;p<b.mode_gr;p++)for(v=0;v<b.channels_out;v++)b.l3_side.tt[p][v].block_type=d.SHORT_TYPE;ta.mdct_sub48(b,l,m)}}f.padding=0;0>(f.slot_lag-=f.frac_SpF)&&(f.slot_lag+=g.out_samplerate,f.padding=1);if(0!=f.psymodel)for(m=[null,null],v=0,p=Y(2),l=0;l<f.mode_gr;l++){for(b=0;b<f.channels_out;b++)m[b]=c[b],v=576+576*l-d.FFTOFFSET;b=g.VBR==M.vbr_mtrh||g.VBR==M.vbr_mt?C.L3psycho_anal_vbr(g,m,v,l,e,n,w[l],A[l],E[l],
+p):C.L3psycho_anal_ns(g,m,v,l,e,n,w[l],A[l],E[l],p);if(0!=b)return-4;g.mode==ka.JOINT_STEREO&&(z[l]=E[l][2]+E[l][3],0<z[l]&&(z[l]=E[l][3]/z[l]));for(b=0;b<f.channels_out;b++){var x=f.l3_side.tt[l][b];x.block_type=p[b];x.mixed_block_flag=0}}else for(l=0;l<f.mode_gr;l++)for(b=0;b<f.channels_out;b++)f.l3_side.tt[l][b].block_type=d.NORM_TYPE,f.l3_side.tt[l][b].mixed_block_flag=0,A[l][b]=w[l][b]=700;0==f.ATH.useAdjust?f.ATH.adjust=1:(b=f.loudness_sq[0][0],E=f.loudness_sq[1][0],2==f.channels_out?(b+=f.loudness_sq[0][1],
+E+=f.loudness_sq[1][1]):(b+=b,E+=E),2==f.mode_gr&&(b=Math.max(b,E)),b=.5*b*f.ATH.aaSensitivityP,.03125<b?(1<=f.ATH.adjust?f.ATH.adjust=1:f.ATH.adjust<f.ATH.adjustLimit&&(f.ATH.adjust=f.ATH.adjustLimit),f.ATH.adjustLimit=1):(E=31.98*b+6.25E-4,f.ATH.adjust>=E?(f.ATH.adjust*=.075*E+.925,f.ATH.adjust<E&&(f.ATH.adjust=E)):f.ATH.adjustLimit>=E?f.ATH.adjust=E:f.ATH.adjust<f.ATH.adjustLimit&&(f.ATH.adjust=f.ATH.adjustLimit),f.ATH.adjustLimit=E));ta.mdct_sub48(f,c[0],c[1]);f.mode_ext=d.MPG_MD_LR_LR;if(g.force_ms)f.mode_ext=
+d.MPG_MD_MS_LR;else if(g.mode==ka.JOINT_STEREO){for(l=m=E=0;l<f.mode_gr;l++)for(b=0;b<f.channels_out;b++)E+=A[l][b],m+=w[l][b];E<=1*m&&(E=f.l3_side.tt[0],b=f.l3_side.tt[f.mode_gr-1],E[0].block_type==E[1].block_type&&b[0].block_type==b[1].block_type&&(f.mode_ext=d.MPG_MD_MS_LR))}f.mode_ext==k&&(e=n,w=A);if(g.analysis&&null!=f.pinfo)for(l=0;l<f.mode_gr;l++)for(b=0;b<f.channels_out;b++)f.pinfo.ms_ratio[l]=f.ms_ratio[l],f.pinfo.ms_ener_ratio[l]=z[l],f.pinfo.blocktype[l][b]=f.l3_side.tt[l][b].block_type,
+f.pinfo.pe[l][b]=w[l][b],K.arraycopy(f.l3_side.tt[l][b].xr,0,f.pinfo.xr[l][b],0,576),f.mode_ext==k&&(f.pinfo.ers[l][b]=f.pinfo.ers[l][b+2],K.arraycopy(f.pinfo.energy[l][b+2],0,f.pinfo.energy[l][b],0,f.pinfo.energy[l][b].length));if(g.VBR==M.vbr_off||g.VBR==M.vbr_abr){for(n=0;18>n;n++)f.nsPsy.pefirbuf[n]=f.nsPsy.pefirbuf[n+1];for(l=A=0;l<f.mode_gr;l++)for(b=0;b<f.channels_out;b++)A+=w[l][b];f.nsPsy.pefirbuf[18]=A;A=f.nsPsy.pefirbuf[9];for(n=0;9>n;n++)A+=(f.nsPsy.pefirbuf[n]+f.nsPsy.pefirbuf[18-n])*
+d.fircoef[n];A=3350*f.mode_gr*f.channels_out/A;for(l=0;l<f.mode_gr;l++)for(b=0;b<f.channels_out;b++)w[l][b]*=A}f.iteration_loop.iteration_loop(g,w,z,e);q.format_bitstream(g);a=q.copy_buffer(f,a,h,D,1);g.bWriteVbrTag&&B.addVbrFrame(g);if(g.analysis&&null!=f.pinfo){for(b=0;b<f.channels_out;b++){for(h=0;h<r;h++)f.pinfo.pcmdata[b][h]=f.pinfo.pcmdata[b][h+g.framesize];for(h=r;1600>h;h++)f.pinfo.pcmdata[b][h]=c[b][h-r]}t.set_frame_pinfo(g,e)}f.bitrate_stereoMode_Hist[f.bitrate_index][4]++;f.bitrate_stereoMode_Hist[15][4]++;
+2==f.channels_out&&(f.bitrate_stereoMode_Hist[f.bitrate_index][f.mode_ext]++,f.bitrate_stereoMode_Hist[15][f.mode_ext]++);for(g=0;g<f.mode_gr;++g)for(c=0;c<f.channels_out;++c)h=f.l3_side.tt[g][c].block_type|0,0!=f.l3_side.tt[g][c].mixed_block_flag&&(h=4),f.bitrate_blockType_Hist[f.bitrate_index][h]++,f.bitrate_blockType_Hist[f.bitrate_index][5]++,f.bitrate_blockType_Hist[15][h]++,f.bitrate_blockType_Hist[15][5]++;return a}}function Qc(){this.size=this.pos=this.want=this.seen=this.sum=0;this.bag=null;
+this.TotalFrameSize=this.nBytesWritten=this.nVbrNumFrames=0}function Rc(){this.tt=[[null,null],[null,null]];this.resvDrain_post=this.resvDrain_pre=this.private_bits=this.main_data_begin=0;this.scfsi=[Y(4),Y(4)];for(var d=0;2>d;d++)for(var k=0;2>k;k++)this.tt[d][k]=new Ab}function Sc(){this.last_en_subshort=ra([4,9]);this.lastAttacks=Y(4);this.pefirbuf=H(19);this.longfact=H(d.SBMAX_l);this.shortfact=H(d.SBMAX_s);this.attackthre_s=this.attackthre=0}function ic(){this.l=H(d.SBMAX_l);this.s=ra([d.SBMAX_s,
+3]);var r=this;this.assign=function(k){K.arraycopy(k.l,0,r.l,0,d.SBMAX_l);for(var q=0;q<d.SBMAX_s;q++)for(var C=0;3>C;C++)r.s[q][C]=k.s[q][C]}}function da(){function r(){this.ptr=this.write_timing=0;this.buf=new Int8Array(40)}this.fill_buffer_resample_init=this.iteration_init_init=this.lame_encode_frame_init=this.Class_ID=0;this.mfbuf=ra([2,da.MFSIZE]);this.full_outer_loop=this.use_best_huffman=this.subblock_gain=this.noise_shaping_stop=this.psymodel=this.substep_shaping=this.noise_shaping_amp=this.noise_shaping=
+this.highpass2=this.highpass1=this.lowpass2=this.lowpass1=this.mode_ext=this.samplerate_index=this.bitrate_index=this.VBR_max_bitrate=this.VBR_min_bitrate=this.mf_size=this.mf_samples_to_encode=this.resample_ratio=this.channels_out=this.channels_in=this.mode_gr=0;this.l3_side=new Rc;this.ms_ratio=H(2);this.slot_lag=this.frac_SpF=this.padding=0;this.tag_spec=null;this.nMusicCRC=0;this.OldValue=Y(2);this.CurrentStep=Y(2);this.masking_lower=0;this.bv_scf=Y(576);this.pseudohalf=Y(na.SFBMAX);this.sfb21_extra=
+!1;this.inbuf_old=Array(2);this.blackfilt=Array(2*da.BPC+1);this.itime=new Float64Array(2);this.sideinfo_len=0;this.sb_sample=ra([2,2,18,d.SBLIMIT]);this.amp_filter=H(32);this.header=Array(da.MAX_HEADER_BUF);this.ResvMax=this.ResvSize=this.ancillary_flag=this.w_ptr=this.h_ptr=0;this.scalefac_band=new ha;this.minval_l=H(d.CBANDS);this.minval_s=H(d.CBANDS);this.nb_1=ra([4,d.CBANDS]);this.nb_2=ra([4,d.CBANDS]);this.nb_s1=ra([4,d.CBANDS]);this.nb_s2=ra([4,d.CBANDS]);this.s3_ll=this.s3_ss=null;this.decay=
+0;this.thm=Array(4);this.en=Array(4);this.tot_ener=H(4);this.loudness_sq=ra([2,2]);this.loudness_sq_save=H(2);this.mld_l=H(d.SBMAX_l);this.mld_s=H(d.SBMAX_s);this.bm_l=Y(d.SBMAX_l);this.bo_l=Y(d.SBMAX_l);this.bm_s=Y(d.SBMAX_s);this.bo_s=Y(d.SBMAX_s);this.npart_s=this.npart_l=0;this.s3ind=db([d.CBANDS,2]);this.s3ind_s=db([d.CBANDS,2]);this.numlines_s=Y(d.CBANDS);this.numlines_l=Y(d.CBANDS);this.rnumlines_l=H(d.CBANDS);this.mld_cb_l=H(d.CBANDS);this.mld_cb_s=H(d.CBANDS);this.numlines_l_num1=this.numlines_s_num1=
+0;this.pe=H(4);this.ms_ener_ratio_old=this.ms_ratio_l_old=this.ms_ratio_s_old=0;this.blocktype_old=Y(2);this.nsPsy=new Sc;this.VBR_seek_table=new Qc;this.PSY=this.ATH=null;this.nogap_current=this.nogap_total=0;this.findPeakSample=this.findReplayGain=this.decode_on_the_fly=!0;this.AudiophileGain=this.RadioGain=this.PeakSample=0;this.rgdata=null;this.noclipScale=this.noclipGainChange=0;this.bitrate_stereoMode_Hist=db([16,5]);this.bitrate_blockType_Hist=db([16,6]);this.hip=this.pinfo=null;this.in_buffer_nsamples=
+0;this.iteration_loop=this.in_buffer_1=this.in_buffer_0=null;for(var k=0;k<this.en.length;k++)this.en[k]=new ic;for(k=0;k<this.thm.length;k++)this.thm[k]=new ic;for(k=0;k<this.header.length;k++)this.header[k]=new r}function Tc(){function r(d,k,g){var b=0,l,a,h;g<<=1;var q=k+g;l=4;do{var e,n,c,f,B,z,w;w=l>>1;f=l;B=l<<1;z=B+f;l=B<<1;a=k;h=a+w;do{var A,v,p,m;v=d[a+0]-d[a+f];A=d[a+0]+d[a+f];m=d[a+B]-d[a+z];p=d[a+B]+d[a+z];d[a+B]=A-p;d[a+0]=A+p;d[a+z]=v-m;d[a+f]=v+m;v=d[h+0]-d[h+f];A=d[h+0]+d[h+f];m=X.SQRT2*
+d[h+z];p=X.SQRT2*d[h+B];d[h+B]=A-p;d[h+0]=A+p;d[h+z]=v-m;d[h+f]=v+m;h+=l;a+=l}while(a<q);n=C[b+0];e=C[b+1];for(c=1;c<w;c++){var x,y;x=1-2*e*e;y=2*e*n;a=k+c;h=k+f-c;do{var W,u,J,H,F;W=y*d[a+f]-x*d[h+f];p=x*d[a+f]+y*d[h+f];v=d[a+0]-p;A=d[a+0]+p;J=d[h+0]-W;u=d[h+0]+W;W=y*d[a+z]-x*d[h+z];p=x*d[a+z]+y*d[h+z];m=d[a+B]-p;p=d[a+B]+p;F=d[h+B]-W;H=d[h+B]+W;W=e*p-n*F;p=n*p+e*F;d[a+B]=A-p;d[a+0]=A+p;d[h+z]=J-W;d[h+f]=J+W;W=n*H-e*m;p=e*H+n*m;d[h+B]=u-p;d[h+0]=u+p;d[a+z]=v-W;d[a+f]=v+W;h+=l;a+=l}while(a<q);x=n;
+n=x*C[b+0]-e*C[b+1];e=x*C[b+1]+e*C[b+0]}b+=2}while(l<g)}var k=H(d.BLKSIZE),q=H(d.BLKSIZE_s/2),C=[.9238795325112867,.3826834323650898,.9951847266721969,.0980171403295606,.9996988186962042,.02454122852291229,.9999811752826011,.006135884649154475],B=[0,128,64,192,32,160,96,224,16,144,80,208,48,176,112,240,8,136,72,200,40,168,104,232,24,152,88,216,56,184,120,248,4,132,68,196,36,164,100,228,20,148,84,212,52,180,116,244,12,140,76,204,44,172,108,236,28,156,92,220,60,188,124,252,2,130,66,194,34,162,98,226,
+18,146,82,210,50,178,114,242,10,138,74,202,42,170,106,234,26,154,90,218,58,186,122,250,6,134,70,198,38,166,102,230,22,150,86,214,54,182,118,246,14,142,78,206,46,174,110,238,30,158,94,222,62,190,126,254];this.fft_short=function(k,C,g,b,l){for(k=0;3>k;k++){var a=d.BLKSIZE_s/2,h=65535&192*(k+1),D=d.BLKSIZE_s/8-1;do{var e,n,c,f,E,z=B[D<<2]&255;e=q[z]*b[g][l+z+h];E=q[127-z]*b[g][l+z+h+128];n=e-E;e+=E;c=q[z+64]*b[g][l+z+h+64];E=q[63-z]*b[g][l+z+h+192];f=c-E;c+=E;a-=4;C[k][a+0]=e+c;C[k][a+2]=e-c;C[k][a+
+1]=n+f;C[k][a+3]=n-f;e=q[z+1]*b[g][l+z+h+1];E=q[126-z]*b[g][l+z+h+129];n=e-E;e+=E;c=q[z+65]*b[g][l+z+h+65];E=q[62-z]*b[g][l+z+h+193];f=c-E;c+=E;C[k][a+d.BLKSIZE_s/2+0]=e+c;C[k][a+d.BLKSIZE_s/2+2]=e-c;C[k][a+d.BLKSIZE_s/2+1]=n+f;C[k][a+d.BLKSIZE_s/2+3]=n-f}while(0<=--D);r(C[k],a,d.BLKSIZE_s/2)}};this.fft_long=function(q,C,g,b,l){q=d.BLKSIZE/8-1;var a=d.BLKSIZE/2;do{var h,D,e,n,c,f=B[q]&255;h=k[f]*b[g][l+f];c=k[f+512]*b[g][l+f+512];D=h-c;h+=c;e=k[f+256]*b[g][l+f+256];c=k[f+768]*b[g][l+f+768];n=e-c;
+e+=c;a-=4;C[a+0]=h+e;C[a+2]=h-e;C[a+1]=D+n;C[a+3]=D-n;h=k[f+1]*b[g][l+f+1];c=k[f+513]*b[g][l+f+513];D=h-c;h+=c;e=k[f+257]*b[g][l+f+257];c=k[f+769]*b[g][l+f+769];n=e-c;e+=c;C[a+d.BLKSIZE/2+0]=h+e;C[a+d.BLKSIZE/2+2]=h-e;C[a+d.BLKSIZE/2+1]=D+n;C[a+d.BLKSIZE/2+3]=D-n}while(0<=--q);r(C,a,d.BLKSIZE/2)};this.init_fft=function(r){for(r=0;r<d.BLKSIZE;r++)k[r]=.42-.5*Math.cos(2*Math.PI*(r+.5)/d.BLKSIZE)+.08*Math.cos(4*Math.PI*(r+.5)/d.BLKSIZE);for(r=0;r<d.BLKSIZE_s/2;r++)q[r]=.5*(1-Math.cos(2*Math.PI*(r+.5)/
+d.BLKSIZE_s))}}function jc(){function r(a,c){for(var b=0,m=0;m<d.BLKSIZE/2;++m)b+=a[m]*c.ATH.eql_w[m];return b*=z}function k(a,d,c,b,g,f){var e;if(d>a)if(d<a*A)e=d/a;else return a+d;else{if(a>=d*A)return a+d;e=a/d}a+=d;if(6>=b+3){if(e>=w)return a;b=0|X.FAST_LOG10_X(e,16);return a*x[b]}b=0|X.FAST_LOG10_X(e,16);d=0!=f?g.ATH.cb_s[c]*g.ATH.adjust:g.ATH.cb_l[c]*g.ATH.adjust;return a<v*d?a>d?(c=1,13>=b&&(c=y[b]),d=X.FAST_LOG10_X(a/d,10/15),a*((m[b]-c)*d+c)):13<b?a:a*y[b]:a*m[b]}function q(a,d,c){var b;
+0>a&&(a=0);0>d&&(d=0);if(0>=a)return d;if(0>=d)return a;b=d>a?d/a:a/d;if(-2<=c&&2>=c){if(b>=w)return a+d;c=0|X.FAST_LOG10_X(b,16);return(a+d)*W[c]}if(b<A)return a+d;a<d&&(a=d);return a}function C(a,c,b,m,g){var f,e,k=0,h=0;for(f=e=0;f<d.SBMAX_s;++e,++f){for(var l=a.bo_s[f],n=a.npart_s,l=l<n?l:n;e<l;)k+=c[e],h+=b[e],e++;a.en[m].s[f][g]=k;a.thm[m].s[f][g]=h;if(e>=n){++f;break}h=a.PSY.bo_s_weight[f];n=1-h;k=h*c[e];h*=b[e];a.en[m].s[f][g]+=k;a.thm[m].s[f][g]+=h;k=n*c[e];h=n*b[e]}for(;f<d.SBMAX_s;++f)a.en[m].s[f][g]=
+0,a.thm[m].s[f][g]=0}function B(a,c,b,m){var g,f,e=0,k=0;for(g=f=0;g<d.SBMAX_l;++f,++g){for(var h=a.bo_l[g],l=a.npart_l,h=h<l?h:l;f<h;)e+=c[f],k+=b[f],f++;a.en[m].l[g]=e;a.thm[m].l[g]=k;if(f>=l){++g;break}k=a.PSY.bo_l_weight[g];l=1-k;e=k*c[f];k*=b[f];a.en[m].l[g]+=e;a.thm[m].l[g]+=k;e=l*c[f];k=l*b[f]}for(;g<d.SBMAX_l;++g)a.en[m].l[g]=0,a.thm[m].l[g]=0}function t(a,d,c){return 1<=c?a:0>=c?d:0<d?Math.pow(a/d,c)*d:0}function V(a,c){for(var b=309.07,m=0;m<d.SBMAX_s-1;m++)for(var g=0;3>g;g++){var f=a.thm.s[m][g];
+if(0<f){var f=f*c,e=a.en.s[m][g];e>f&&(b=e>1E10*f?b+23.02585092994046*u[m]:b+u[m]*X.FAST_LOG10(e/f))}}return b}function g(a,c){for(var b=281.0575,m=0;m<d.SBMAX_l-1;m++){var g=a.thm.l[m];if(0<g){var g=g*c,f=a.en.l[m];f>g&&(b=f>1E10*g?b+23.02585092994046*pa[m]:b+pa[m]*X.FAST_LOG10(f/g))}}return b}function b(a,d,c,b,m){var g,f;for(g=f=0;g<a.npart_l;++g){var e=0,k=0,h;for(h=0;h<a.numlines_l[g];++h,++f){var l=d[f],e=e+l;k<l&&(k=l)}c[g]=e;b[g]=k;m[g]=e*a.rnumlines_l[g]}}function l(a,d,c,b){var m=p.length-
+1,g=0,f=c[g]+c[g+1];if(0<f){var e=d[g];e<d[g+1]&&(e=d[g+1]);f=20*(2*e-f)/(f*(a.numlines_l[g]+a.numlines_l[g+1]-1));f|=0;f>m&&(f=m);b[g]=f}else b[g]=0;for(g=1;g<a.npart_l-1;g++)f=c[g-1]+c[g]+c[g+1],0<f?(e=d[g-1],e<d[g]&&(e=d[g]),e<d[g+1]&&(e=d[g+1]),f=20*(3*e-f)/(f*(a.numlines_l[g-1]+a.numlines_l[g]+a.numlines_l[g+1]-1)),f|=0,f>m&&(f=m),b[g]=f):b[g]=0;f=c[g-1]+c[g];0<f?(e=d[g-1],e<d[g]&&(e=d[g]),f=20*(2*e-f)/(f*(a.numlines_l[g-1]+a.numlines_l[g]-1)),f|=0,f>m&&(f=m),b[g]=f):b[g]=0}function a(a,d,c,
+b,m,g,f){var e=2*g;m=0<g?Math.pow(10,m):1;for(var k,h,l=0;l<f;++l){var n=a[2][l],p=a[3][l],y=d[0][l],q=d[1][l],u=d[2][l],x=d[3][l];y<=1.58*q&&q<=1.58*y?(k=c[l]*n,h=Math.max(u,Math.min(x,c[l]*p)),k=Math.max(x,Math.min(u,k))):(h=u,k=x);0<g&&(x=b[l]*m,y=Math.min(Math.max(y,x),Math.max(q,x)),u=Math.max(h,x),x=Math.max(k,x),q=u+x,0<q&&y*e<q&&(y=y*e/q,u*=y,x*=y),h=Math.min(u,h),k=Math.min(x,k));h>n&&(h=n);k>p&&(k=p);d[2][l]=h;d[3][l]=k}}function h(a,d){var c;c=0<=a?27*-a:a*d;return-72>=c?0:Math.exp(.2302585093*
+c)}function D(a){0>a&&(a=0);a*=.001;return 13*Math.atan(.76*a)+3.5*Math.atan(a*a/56.25)}function e(a,c,b,m,g,f,e,k,h,l,n,p){var y=H(d.CBANDS+1),q=k/(15<p?1152:384),u=Y(d.HBLKSIZE),x;k/=h;var r=0,v=0;for(x=0;x<d.CBANDS;x++){var t,w;t=D(k*r);y[x]=k*r;for(w=r;.34>D(k*w)-t&&w<=h/2;w++);a[x]=w-r;for(v=x+1;r<w;)u[r++]=x;if(r>h/2){r=h/2;++x;break}}y[x]=k*r;for(r=0;r<p;r++)x=l[r],t=l[r+1],x=0|Math.floor(.5+n*(x-.5)),0>x&&(x=0),w=0|Math.floor(.5+n*(t-.5)),w>h/2&&(w=h/2),b[r]=(u[x]+u[w])/2,c[r]=u[w],e[r]=(q*
+t-y[c[r]])/(y[c[r]+1]-y[c[r]]),0>e[r]?e[r]=0:1<e[r]&&(e[r]=1),t=D(k*l[r]*n),t=Math.min(t,15.5)/15.5,f[r]=Math.pow(10,1.25*(1-Math.cos(Math.PI*t))-2.5);for(c=r=0;c<v;c++)b=a[c],t=D(k*r),f=D(k*(r+b-1)),m[c]=.5*(t+f),t=D(k*(r-.5)),f=D(k*(r+b-.5)),g[c]=f-t,r+=b;return v}function n(a,c,b,m,g,f){var e=ra([d.CBANDS,d.CBANDS]),k=0;if(f)for(var l=0;l<c;l++)for(f=0;f<c;f++){var n;var y=n=void 0,y=n=void 0;n=b[l]-b[f];n=0<=n?3*n:1.5*n;.5<=n&&2.5>=n?(y=n-.5,y=8*(y*y-2*y)):y=0;n+=.474;n=15.811389+7.5*n-17.5*Math.sqrt(1+
+n*n);-60>=n?n=0:(n=Math.exp(.2302585093*(y+n)),n/=.6609193);y=n*m[f];e[l][f]=y*g[l]}else for(f=0;f<c;f++){n=15+Math.min(21/b[f],12);var p;var l=n,q=y=0;p=0;var u=q=void 0;for(p=0;1E-20<h(p,l);--p);q=p;for(u=0;1E-12<Math.abs(u-q);)p=(u+q)/2,0<h(p,l)?u=p:q=p;y=q;for(p=0;1E-20<h(p,l);p+=1);q=0;for(u=p;1E-12<Math.abs(u-q);)p=(u+q)/2,0<h(p,l)?q=p:u=p;for(var q=u,u=0,x=void 0,x=0;1E3>=x;++x)p=y+x*(q-y)/1E3,p=h(p,l),u+=p;p=1001/(u*(q-y));for(l=0;l<c;l++)y=p*h(b[l]-b[f],n)*m[f],e[l][f]=y*g[l]}for(l=0;l<c;l++){for(f=
+0;f<c&&!(0<e[l][f]);f++);a[l][0]=f;for(f=c-1;0<f&&!(0<e[l][f]);f--);a[l][1]=f;k+=a[l][1]-a[l][0]+1}b=H(k);for(l=m=0;l<c;l++)for(f=a[l][0];f<=a[l][1];f++)b[m++]=e[l][f];return b}function c(a){a=D(a);a=Math.min(a,15.5)/15.5;return Math.pow(10,1.25*(1-Math.cos(Math.PI*a))-2.5)}function f(a,c){-.3>a&&(a=3410);a=Math.max(.1,a/1E3);return 3.64*Math.pow(a,-.8)-6.8*Math.exp(-.6*Math.pow(a-3.4,2))+6*Math.exp(-.15*Math.pow(a-8.7,2))+.001*(.6+.04*c)*Math.pow(a,4)}var E=new Tc,z=1/217621504/(d.BLKSIZE/2),w,A,
+v,p=[1,.79433,.63096,.63096,.63096,.63096,.63096,.25119,.11749],m=[3.3246*3.3246,3.23837*3.23837,9.9500500969,9.0247369744,8.1854926609,7.0440875649,2.46209*2.46209,2.284*2.284,4.4892710641,1.96552*1.96552,1.82335*1.82335,1.69146*1.69146,2.4621061921,2.1508568964,1.37074*1.37074,1.31036*1.31036,1.5691069696,1.4555939904,1.16203*1.16203,1.2715945225,1.09428*1.09428,1.0659*1.0659,1.0779838276,1.0382591025,1],x=[1.7782755904,1.35879*1.35879,1.38454*1.38454,1.39497*1.39497,1.40548*1.40548,1.3537*1.3537,
+1.6999465924,1.22321*1.22321,1.3169398564,1],y=[5.5396212496,2.29259*2.29259,4.9868695969,2.12675*2.12675,2.02545*2.02545,1.87894*1.87894,1.74303*1.74303,1.61695*1.61695,2.2499700001,1.39148*1.39148,1.29083*1.29083,1.19746*1.19746,1.2339655056,1.0779838276],W=[1.7782755904,1.35879*1.35879,1.38454*1.38454,1.39497*1.39497,1.40548*1.40548,1.3537*1.3537,1.6999465924,1.22321*1.22321,1.3169398564,1],u=[11.8,13.6,17.2,32,46.5,51.3,57.5,67.1,71.5,84.6,97.6,130],pa=[6.8,5.8,5.8,6.4,6.5,9.9,12.1,14.4,15,18.9,
+21.6,26.9,34.2,40.2,46.8,56.5,60.7,73.9,85.7,93.4,126.1],la=[-1.730326E-17,-.01703172,-1.349528E-17,.0418072,-6.73278E-17,-.0876324,-3.0835E-17,.1863476,-1.104424E-16,-.627638];this.L3psycho_anal_ns=function(a,c,m,f,e,h,n,y,q,u){var x=a.internal_flags,v=ra([2,d.BLKSIZE]),w=ra([2,3,d.BLKSIZE_s]),F=H(d.CBANDS+1),z=H(d.CBANDS+1),A=H(d.CBANDS+2),W=Y(2),D=Y(2),pa,R,Fa,L,K,Z,Da,O,Q=ra([2,576]),P,da=Y(d.CBANDS+2),N=Y(d.CBANDS+2);Ia.fill(N,0);pa=x.channels_out;a.mode==ka.JOINT_STEREO&&(pa=4);P=a.VBR==M.vbr_off?
+0==x.ResvMax?0:x.ResvSize/x.ResvMax*.5:a.VBR==M.vbr_rh||a.VBR==M.vbr_mtrh||a.VBR==M.vbr_mt?.6:1;for(R=0;R<x.channels_out;R++){var ua=c[R],ma=m+576-350-21+192;for(L=0;576>L;L++){var Ga,Sa;Ga=ua[ma+L+10];for(K=Sa=0;9>K;K+=2)Ga+=la[K]*(ua[ma+L+K]+ua[ma+L+21-K]),Sa+=la[K+1]*(ua[ma+L+K+1]+ua[ma+L+21-K-1]);Q[R][L]=Ga+Sa}e[f][R].en.assign(x.en[R]);e[f][R].thm.assign(x.thm[R]);2<pa&&(h[f][R].en.assign(x.en[R+2]),h[f][R].thm.assign(x.thm[R+2]))}for(R=0;R<pa;R++){var ea=H(12),Oa=[0,0,0,0],kc=H(12),na=1,Ca,
+ha=H(d.CBANDS),Qa=H(d.CBANDS),va=[0,0,0,0],db=H(d.HBLKSIZE),fb=ra([3,d.HBLKSIZE_s]);for(L=0;3>L;L++)ea[L]=x.nsPsy.last_en_subshort[R][L+6],kc[L]=ea[L]/x.nsPsy.last_en_subshort[R][L+4],Oa[0]+=ea[L];if(2==R)for(L=0;576>L;L++){var eb,Gb;eb=Q[0][L];Gb=Q[1][L];Q[0][L]=eb+Gb;Q[1][L]=eb-Gb}var vb=Q[R&1],Hb=0;for(L=0;9>L;L++){for(var tb=Hb+64,Ta=1;Hb<tb;Hb++)Ta<Math.abs(vb[Hb])&&(Ta=Math.abs(vb[Hb]));x.nsPsy.last_en_subshort[R][L]=ea[L+3]=Ta;Oa[1+L/3]+=Ta;Ta=Ta>ea[L+3-2]?Ta/ea[L+3-2]:ea[L+3-2]>10*Ta?ea[L+
+3-2]/(10*Ta):0;kc[L+3]=Ta}if(a.analysis){var Pb=kc[0];for(L=1;12>L;L++)Pb<kc[L]&&(Pb=kc[L]);x.pinfo.ers[f][R]=x.pinfo.ers_save[R];x.pinfo.ers_save[R]=Pb}Ca=3==R?x.nsPsy.attackthre_s:x.nsPsy.attackthre;for(L=0;12>L;L++)0==va[L/3]&&kc[L]>Ca&&(va[L/3]=L%3+1);for(L=1;4>L;L++)1.7>(Oa[L-1]>Oa[L]?Oa[L-1]/Oa[L]:Oa[L]/Oa[L-1])&&(va[L]=0,1==L&&(va[0]=0));0!=va[0]&&0!=x.nsPsy.lastAttacks[R]&&(va[0]=0);if(3==x.nsPsy.lastAttacks[R]||0!=va[0]+va[1]+va[2]+va[3])na=0,0!=va[1]&&0!=va[0]&&(va[1]=0),0!=va[2]&&0!=va[1]&&
+(va[2]=0),0!=va[3]&&0!=va[2]&&(va[3]=0);2>R?D[R]=na:0==na&&(D[0]=D[1]=0);q[R]=x.tot_ener[R];var S=a,Pa=db,Qb=fb,Ua=v,wb=R&1,Ya=w,Va=R&1,mb=f,Ja=R,za=c,ub=m,$a=S.internal_flags;if(2>Ja)E.fft_long($a,Ua[wb],Ja,za,ub),E.fft_short($a,Ya[Va],Ja,za,ub);else if(2==Ja){for(var fa=d.BLKSIZE-1;0<=fa;--fa){var Rb=Ua[wb+0][fa],Ib=Ua[wb+1][fa];Ua[wb+0][fa]=(Rb+Ib)*X.SQRT2*.5;Ua[wb+1][fa]=(Rb-Ib)*X.SQRT2*.5}for(var Ka=2;0<=Ka;--Ka)for(fa=d.BLKSIZE_s-1;0<=fa;--fa)Rb=Ya[Va+0][Ka][fa],Ib=Ya[Va+1][Ka][fa],Ya[Va+0][Ka][fa]=
+(Rb+Ib)*X.SQRT2*.5,Ya[Va+1][Ka][fa]=(Rb-Ib)*X.SQRT2*.5}Pa[0]=Ua[wb+0][0];Pa[0]*=Pa[0];for(fa=d.BLKSIZE/2-1;0<=fa;--fa){var ac=Ua[wb+0][d.BLKSIZE/2-fa],Bb=Ua[wb+0][d.BLKSIZE/2+fa];Pa[d.BLKSIZE/2-fa]=.5*(ac*ac+Bb*Bb)}for(Ka=2;0<=Ka;--Ka)for(Qb[Ka][0]=Ya[Va+0][Ka][0],Qb[Ka][0]*=Qb[Ka][0],fa=d.BLKSIZE_s/2-1;0<=fa;--fa)ac=Ya[Va+0][Ka][d.BLKSIZE_s/2-fa],Bb=Ya[Va+0][Ka][d.BLKSIZE_s/2+fa],Qb[Ka][d.BLKSIZE_s/2-fa]=.5*(ac*ac+Bb*Bb);for(var Ea=0,fa=11;fa<d.HBLKSIZE;fa++)Ea+=Pa[fa];$a.tot_ener[Ja]=Ea;if(S.analysis){for(fa=
+0;fa<d.HBLKSIZE;fa++)$a.pinfo.energy[mb][Ja][fa]=$a.pinfo.energy_save[Ja][fa],$a.pinfo.energy_save[Ja][fa]=Pa[fa];$a.pinfo.pe[mb][Ja]=$a.pe[Ja]}2==S.athaa_loudapprox&&2>Ja&&($a.loudness_sq[mb][Ja]=$a.loudness_sq_save[Ja],$a.loudness_sq_save[Ja]=r(Pa,$a));b(x,db,F,ha,Qa);l(x,ha,Qa,da);for(O=0;3>O;O++){for(var qc,Ba,gb=fb,hb=z,Sb=A,Jb=R,bc=O,aa=a.internal_flags,nb=void 0,oa=void 0,oa=nb=0;oa<aa.npart_s;++oa){for(var ab=0,lc=0,Ab=aa.numlines_s[oa],ib=0;ib<Ab;++ib,++nb){var rc=gb[bc][nb],ab=ab+rc;lc<
+rc&&(lc=rc)}hb[oa]=ab}for(nb=oa=0;oa<aa.npart_s;oa++){var ob=aa.s3ind_s[oa][0],Cb=aa.s3_ss[nb++]*hb[ob];for(++ob;ob<=aa.s3ind_s[oa][1];)Cb+=aa.s3_ss[nb]*hb[ob],++nb,++ob;var Tb=2*aa.nb_s1[Jb][oa];Sb[oa]=Math.min(Cb,Tb);aa.blocktype_old[Jb&1]==d.SHORT_TYPE&&(Tb=16*aa.nb_s2[Jb][oa],Sb[oa]=Math.min(Tb,Sb[oa]));aa.nb_s2[Jb][oa]=aa.nb_s1[Jb][oa];aa.nb_s1[Jb][oa]=Cb}for(;oa<=d.CBANDS;++oa)hb[oa]=0,Sb[oa]=0;C(x,z,A,R,O);for(Da=0;Da<d.SBMAX_s;Da++){Ba=x.thm[R].s[Da][O];Ba*=.8;if(2<=va[O]||1==va[O+1]){var Kb=
+0!=O?O-1:2,Ta=t(x.thm[R].s[Da][Kb],Ba,.6*P);Ba=Math.min(Ba,Ta)}if(1==va[O])Kb=0!=O?O-1:2,Ta=t(x.thm[R].s[Da][Kb],Ba,.3*P),Ba=Math.min(Ba,Ta);else if(0!=O&&3==va[O-1]||0==O&&3==x.nsPsy.lastAttacks[R])Kb=2!=O?O+1:0,Ta=t(x.thm[R].s[Da][Kb],Ba,.3*P),Ba=Math.min(Ba,Ta);qc=ea[3*O+3]+ea[3*O+4]+ea[3*O+5];6*ea[3*O+5]<qc&&(Ba*=.5,6*ea[3*O+4]<qc&&(Ba*=.5));x.thm[R].s[Da][O]=Ba}}x.nsPsy.lastAttacks[R]=va[2];for(Fa=Z=0;Fa<x.npart_l;Fa++){for(var pb=x.s3ind[Fa][0],Ub=F[pb]*p[da[pb]],qb=x.s3_ll[Z++]*Ub;++pb<=x.s3ind[Fa][1];)Ub=
+F[pb]*p[da[pb]],qb=k(qb,x.s3_ll[Z++]*Ub,pb,pb-Fa,x,0);qb*=.158489319246111;A[Fa]=x.blocktype_old[R&1]==d.SHORT_TYPE?qb:t(Math.min(qb,Math.min(2*x.nb_1[R][Fa],16*x.nb_2[R][Fa])),qb,P);x.nb_2[R][Fa]=x.nb_1[R][Fa];x.nb_1[R][Fa]=qb}for(;Fa<=d.CBANDS;++Fa)F[Fa]=0,A[Fa]=0;B(x,F,A,R)}if((a.mode==ka.STEREO||a.mode==ka.JOINT_STEREO)&&0<a.interChRatio){var Db=a.interChRatio,ba=a.internal_flags;if(1<ba.channels_out){for(var La=0;La<d.SBMAX_l;La++){var cc=ba.thm[0].l[La],Lb=ba.thm[1].l[La];ba.thm[0].l[La]+=Lb*
+Db;ba.thm[1].l[La]+=cc*Db}for(La=0;La<d.SBMAX_s;La++)for(var rb=0;3>rb;rb++)cc=ba.thm[0].s[La][rb],Lb=ba.thm[1].s[La][rb],ba.thm[0].s[La][rb]+=Lb*Db,ba.thm[1].s[La][rb]+=cc*Db}}if(a.mode==ka.JOINT_STEREO){for(var Wa,ga=0;ga<d.SBMAX_l;ga++)if(!(x.thm[0].l[ga]>1.58*x.thm[1].l[ga]||x.thm[1].l[ga]>1.58*x.thm[0].l[ga])){var Za=x.mld_l[ga]*x.en[3].l[ga],sb=Math.max(x.thm[2].l[ga],Math.min(x.thm[3].l[ga],Za)),Za=x.mld_l[ga]*x.en[2].l[ga],sc=Math.max(x.thm[3].l[ga],Math.min(x.thm[2].l[ga],Za));x.thm[2].l[ga]=
+sb;x.thm[3].l[ga]=sc}for(ga=0;ga<d.SBMAX_s;ga++)for(var wa=0;3>wa;wa++)x.thm[0].s[ga][wa]>1.58*x.thm[1].s[ga][wa]||x.thm[1].s[ga][wa]>1.58*x.thm[0].s[ga][wa]||(Za=x.mld_s[ga]*x.en[3].s[ga][wa],sb=Math.max(x.thm[2].s[ga][wa],Math.min(x.thm[3].s[ga][wa],Za)),Za=x.mld_s[ga]*x.en[2].s[ga][wa],sc=Math.max(x.thm[3].s[ga][wa],Math.min(x.thm[2].s[ga][wa],Za)),x.thm[2].s[ga][wa]=sb,x.thm[3].s[ga][wa]=sc);Wa=a.msfix;if(0<Math.abs(Wa)){for(var Mb=Wa,mc=Mb,dc=Math.pow(10,a.ATHlower*x.ATH.adjust),Mb=2*Mb,mc=2*
+mc,Aa=0;Aa<d.SBMAX_l;Aa++){var Vb,Ra,Ha,bb;bb=x.ATH.cb_l[x.bm_l[Aa]]*dc;Vb=Math.min(Math.max(x.thm[0].l[Aa],bb),Math.max(x.thm[1].l[Aa],bb));Ra=Math.max(x.thm[2].l[Aa],bb);Ha=Math.max(x.thm[3].l[Aa],bb);if(Vb*Mb<Ra+Ha){var ec=Vb*mc/(Ra+Ha);Ra*=ec;Ha*=ec}x.thm[2].l[Aa]=Math.min(Ra,x.thm[2].l[Aa]);x.thm[3].l[Aa]=Math.min(Ha,x.thm[3].l[Aa])}dc*=d.BLKSIZE_s/d.BLKSIZE;for(Aa=0;Aa<d.SBMAX_s;Aa++)for(var xa=0;3>xa;xa++)bb=x.ATH.cb_s[x.bm_s[Aa]]*dc,Vb=Math.min(Math.max(x.thm[0].s[Aa][xa],bb),Math.max(x.thm[1].s[Aa][xa],
+bb)),Ra=Math.max(x.thm[2].s[Aa][xa],bb),Ha=Math.max(x.thm[3].s[Aa][xa],bb),Vb*Mb<Ra+Ha&&(ec=Vb*Mb/(Ra+Ha),Ra*=ec,Ha*=ec),x.thm[2].s[Aa][xa]=Math.min(x.thm[2].s[Aa][xa],Ra),x.thm[3].s[Aa][xa]=Math.min(x.thm[3].s[Aa][xa],Ha)}}var jb=a.internal_flags;a.short_blocks!=sa.short_block_coupled||0!=D[0]&&0!=D[1]||(D[0]=D[1]=0);for(var Na=0;Na<jb.channels_out;Na++)W[Na]=d.NORM_TYPE,a.short_blocks==sa.short_block_dispensed&&(D[Na]=1),a.short_blocks==sa.short_block_forced&&(D[Na]=0),0!=D[Na]?jb.blocktype_old[Na]==
+d.SHORT_TYPE&&(W[Na]=d.STOP_TYPE):(W[Na]=d.SHORT_TYPE,jb.blocktype_old[Na]==d.NORM_TYPE&&(jb.blocktype_old[Na]=d.START_TYPE),jb.blocktype_old[Na]==d.STOP_TYPE&&(jb.blocktype_old[Na]=d.SHORT_TYPE)),u[Na]=jb.blocktype_old[Na],jb.blocktype_old[Na]=W[Na];for(R=0;R<pa;R++){var Wb,kb=0,ca,Eb;if(1<R){Wb=y;kb=-2;ca=d.NORM_TYPE;if(u[0]==d.SHORT_TYPE||u[1]==d.SHORT_TYPE)ca=d.SHORT_TYPE;Eb=h[f][R-2]}else Wb=n,kb=0,ca=u[R],Eb=e[f][R];Wb[kb+R]=ca==d.SHORT_TYPE?V(Eb,x.masking_lower):g(Eb,x.masking_lower);a.analysis&&
+(x.pinfo.pe[f][R]=Wb[kb+R])}return 0};var F=[-1.730326E-17,-.01703172,-1.349528E-17,.0418072,-6.73278E-17,-.0876324,-3.0835E-17,.1863476,-1.104424E-16,-.627638];this.L3psycho_anal_vbr=function(c,m,f,e,k,h,n,x,y,u){for(var v=c.internal_flags,w,z,A=H(d.HBLKSIZE),W=ra([3,d.HBLKSIZE_s]),D=ra([2,d.BLKSIZE]),M=ra([2,3,d.BLKSIZE_s]),la=ra([4,d.CBANDS]),O=ra([4,d.CBANDS]),R=ra([4,3]),K=[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]],L=Y(2),pa=c.mode==ka.JOINT_STEREO?4:v.channels_out,Z=ra([2,576]),Q=c.internal_flags,
+da=Q.channels_out,ua=c.mode==ka.JOINT_STEREO?4:da,P=0;P<da;P++){firbuf=m[P];for(var ma=f+576-350-21+192,N=0;576>N;N++){var na,Ca;na=firbuf[ma+N+10];for(var Ga=Ca=0;9>Ga;Ga+=2)na+=F[Ga]*(firbuf[ma+N+Ga]+firbuf[ma+N+21-Ga]),Ca+=F[Ga+1]*(firbuf[ma+N+Ga+1]+firbuf[ma+N+21-Ga-1]);Z[P][N]=na+Ca}k[e][P].en.assign(Q.en[P]);k[e][P].thm.assign(Q.thm[P]);2<ua&&(h[e][P].en.assign(Q.en[P+2]),h[e][P].thm.assign(Q.thm[P+2]))}for(P=0;P<ua;P++){var Sa=H(12),ea=H(12),Oa=[0,0,0,0],Ia=Z[P&1],ha=0,eb=3==P?Q.nsPsy.attackthre_s:
+Q.nsPsy.attackthre,Qa=1;if(2==P)for(N=0,Ga=576;0<Ga;++N,--Ga){var db=Z[0][N],va=Z[1][N];Z[0][N]=db+va;Z[1][N]=db-va}for(N=0;3>N;N++)ea[N]=Q.nsPsy.last_en_subshort[P][N+6],Sa[N]=ea[N]/Q.nsPsy.last_en_subshort[P][N+4],Oa[0]+=ea[N];for(N=0;9>N;N++){for(var vb=ha+64,fb=1;ha<vb;ha++)fb<Math.abs(Ia[ha])&&(fb=Math.abs(Ia[ha]));Q.nsPsy.last_en_subshort[P][N]=ea[N+3]=fb;Oa[1+N/3]+=fb;fb=fb>ea[N+3-2]?fb/ea[N+3-2]:ea[N+3-2]>10*fb?ea[N+3-2]/(10*fb):0;Sa[N+3]=fb}for(N=0;3>N;++N){var ub=ea[3*N+3]+ea[3*N+4]+ea[3*
+N+5],Gb=1;6*ea[3*N+5]<ub&&(Gb*=.5,6*ea[3*N+4]<ub&&(Gb*=.5));R[P][N]=Gb}if(c.analysis){for(var tb=Sa[0],N=1;12>N;N++)tb<Sa[N]&&(tb=Sa[N]);Q.pinfo.ers[e][P]=Q.pinfo.ers_save[P];Q.pinfo.ers_save[P]=tb}for(N=0;12>N;N++)0==K[P][N/3]&&Sa[N]>eb&&(K[P][N/3]=N%3+1);for(N=1;4>N;N++){var Hb=Oa[N-1],Ab=Oa[N];4E4>Math.max(Hb,Ab)&&Hb<1.7*Ab&&Ab<1.7*Hb&&(1==N&&K[P][0]<=K[P][N]&&(K[P][0]=0),K[P][N]=0)}K[P][0]<=Q.nsPsy.lastAttacks[P]&&(K[P][0]=0);if(3==Q.nsPsy.lastAttacks[P]||0!=K[P][0]+K[P][1]+K[P][2]+K[P][3])Qa=
+0,0!=K[P][1]&&0!=K[P][0]&&(K[P][1]=0),0!=K[P][2]&&0!=K[P][1]&&(K[P][2]=0),0!=K[P][3]&&0!=K[P][2]&&(K[P][3]=0);2>P?L[P]=Qa:0==Qa&&(L[0]=L[1]=0);y[P]=Q.tot_ener[P]}var Ta=c.internal_flags;c.short_blocks!=sa.short_block_coupled||0!=L[0]&&0!=L[1]||(L[0]=L[1]=0);for(var Pb=0;Pb<Ta.channels_out;Pb++)c.short_blocks==sa.short_block_dispensed&&(L[Pb]=1),c.short_blocks==sa.short_block_forced&&(L[Pb]=0);for(var S=0;S<pa;S++){var Pa=S&1;w=D;var Qb=c,Ua=S,wb=e,Ya=A,Va=w,mb=Pa,Ja=Qb.internal_flags;if(2>Ua)E.fft_long(Ja,
+Va[mb],Ua,m,f);else if(2==Ua)for(var za=d.BLKSIZE-1;0<=za;--za){var $b=Va[mb+0][za],$a=Va[mb+1][za];Va[mb+0][za]=($b+$a)*X.SQRT2*.5;Va[mb+1][za]=($b-$a)*X.SQRT2*.5}Ya[0]=Va[mb+0][0];Ya[0]*=Ya[0];for(za=d.BLKSIZE/2-1;0<=za;--za){var fa=Va[mb+0][d.BLKSIZE/2-za],Rb=Va[mb+0][d.BLKSIZE/2+za];Ya[d.BLKSIZE/2-za]=.5*(fa*fa+Rb*Rb)}for(var Ib=0,za=11;za<d.HBLKSIZE;za++)Ib+=Ya[za];Ja.tot_ener[Ua]=Ib;if(Qb.analysis){for(za=0;za<d.HBLKSIZE;za++)Ja.pinfo.energy[wb][Ua][za]=Ja.pinfo.energy_save[Ua][za],Ja.pinfo.energy_save[Ua][za]=
+Ya[za];Ja.pinfo.pe[wb][Ua]=Ja.pe[Ua]}var Ka=S,ac=A,Bb=c.internal_flags;2==c.athaa_loudapprox&&2>Ka&&(Bb.loudness_sq[e][Ka]=Bb.loudness_sq_save[Ka],Bb.loudness_sq_save[Ka]=r(ac,Bb));if(0!=L[Pa]){var Ea=v,qc=A,Ba=la[S],gb=O[S],hb=S,Sb=H(d.CBANDS),Jb=H(d.CBANDS),bc=Y(d.CBANDS+2),aa=void 0;b(Ea,qc,Ba,Sb,Jb);l(Ea,Sb,Jb,bc);for(var nb=0,aa=0;aa<Ea.npart_l;aa++){var oa,ab,lc,ic,ib=Ea.s3ind[aa][0],rc=Ea.s3ind[aa][1],ob=0,Cb=0,ob=bc[ib],Cb=Cb+1;ab=Ea.s3_ll[nb]*Ba[ib]*p[bc[ib]];++nb;for(++ib;ib<=rc;)ob+=bc[ib],
+Cb+=1,oa=Ea.s3_ll[nb]*Ba[ib]*p[bc[ib]],ab=ic=q(ab,oa,ib-aa),++nb,++ib;ob=(1+2*ob)/(2*Cb);lc=.5*p[ob];ab*=lc;if(Ea.blocktype_old[hb&1]==d.SHORT_TYPE){var Tb=2*Ea.nb_1[hb][aa];gb[aa]=0<Tb?Math.min(ab,Tb):Math.min(ab,.3*Ba[aa])}else{var Kb=16*Ea.nb_2[hb][aa],pb=2*Ea.nb_1[hb][aa];0>=Kb&&(Kb=ab);0>=pb&&(pb=ab);Tb=Ea.blocktype_old[hb&1]==d.NORM_TYPE?Math.min(pb,Kb):pb;gb[aa]=Math.min(ab,Tb)}Ea.nb_2[hb][aa]=Ea.nb_1[hb][aa];Ea.nb_1[hb][aa]=ab;oa=Sb[aa];oa*=Ea.minval_l[aa];oa*=lc;gb[aa]>oa&&(gb[aa]=oa);1<
+Ea.masking_lower&&(gb[aa]*=Ea.masking_lower);gb[aa]>Ba[aa]&&(gb[aa]=Ba[aa]);1>Ea.masking_lower&&(gb[aa]*=Ea.masking_lower)}for(;aa<d.CBANDS;++aa)Ba[aa]=0,gb[aa]=0}else for(var Ub=v,qb=S,Db=0;Db<Ub.npart_l;Db++)Ub.nb_2[qb][Db]=Ub.nb_1[qb][Db],Ub.nb_1[qb][Db]=0}2==L[0]+L[1]&&c.mode==ka.JOINT_STEREO&&a(la,O,v.mld_cb_l,v.ATH.cb_l,c.ATHlower*v.ATH.adjust,c.msfix,v.npart_l);for(S=0;S<pa;S++)Pa=S&1,0!=L[Pa]&&B(v,la[S],O[S],S);for(var ba=0;3>ba;ba++){for(S=0;S<pa;++S)if(Pa=S&1,0!=L[Pa]){var La=v,cc=S;if(0==
+ba)for(var Lb=0;Lb<La.npart_s;Lb++)La.nb_s2[cc][Lb]=La.nb_s1[cc][Lb],La.nb_s1[cc][Lb]=0}else{z=M;var rb=S,Wa=ba,ga=W,Za=z,sb=Pa,sc=c.internal_flags;0==Wa&&2>rb&&E.fft_short(sc,Za[sb],rb,m,f);if(2==rb)for(var wa=d.BLKSIZE_s-1;0<=wa;--wa){var Mb=Za[sb+0][Wa][wa],mc=Za[sb+1][Wa][wa];Za[sb+0][Wa][wa]=(Mb+mc)*X.SQRT2*.5;Za[sb+1][Wa][wa]=(Mb-mc)*X.SQRT2*.5}ga[Wa][0]=Za[sb+0][Wa][0];ga[Wa][0]*=ga[Wa][0];for(wa=d.BLKSIZE_s/2-1;0<=wa;--wa){var dc=Za[sb+0][Wa][d.BLKSIZE_s/2-wa],Aa=Za[sb+0][Wa][d.BLKSIZE_s/
+2+wa];ga[Wa][d.BLKSIZE_s/2-wa]=.5*(dc*dc+Aa*Aa)}for(var Vb=W,Ra=la[S],Ha=O[S],bb=S,ec=ba,xa=c.internal_flags,jb=new float[d.CBANDS],Na=H(d.CBANDS),Wb=void 0,kb=void 0,ca=void 0,Eb=new int[d.CBANDS],ca=kb=0;ca<xa.npart_s;++ca){for(var Ob=0,Zb=0,jc=xa.numlines_s[ca],Wb=0;Wb<jc;++Wb,++kb){var Bc=Vb[ec][kb],Ob=Ob+Bc;Zb<Bc&&(Zb=Bc)}Ra[ca]=Ob;jb[ca]=Zb;Na[ca]=Ob/jc}for(;ca<d.CBANDS;++ca)jb[ca]=0,Na[ca]=0;var Xb=xa,xb=jb,fc=Na,nc=Eb,oc=p.length-1,ja=0,Xa=fc[ja]+fc[ja+1];if(0<Xa){var lb=xb[ja];lb<xb[ja+1]&&
+(lb=xb[ja+1]);var Xa=20*(2*lb-Xa)/(Xa*(Xb.numlines_s[ja]+Xb.numlines_s[ja+1]-1)),yb=0|Xa;yb>oc&&(yb=oc);nc[ja]=yb}else nc[ja]=0;for(ja=1;ja<Xb.npart_s-1;ja++)Xa=fc[ja-1]+fc[ja]+fc[ja+1],0<Xa?(lb=xb[ja-1],lb<xb[ja]&&(lb=xb[ja]),lb<xb[ja+1]&&(lb=xb[ja+1]),Xa=20*(3*lb-Xa)/(Xa*(Xb.numlines_s[ja-1]+Xb.numlines_s[ja]+Xb.numlines_s[ja+1]-1)),yb=0|Xa,yb>oc&&(yb=oc),nc[ja]=yb):nc[ja]=0;Xa=fc[ja-1]+fc[ja];0<Xa?(lb=xb[ja-1],lb<xb[ja]&&(lb=xb[ja]),Xa=20*(2*lb-Xa)/(Xa*(Xb.numlines_s[ja-1]+Xb.numlines_s[ja]-1)),
+yb=0|Xa,yb>oc&&(yb=oc),nc[ja]=yb):nc[ja]=0;for(kb=ca=0;ca<xa.npart_s;ca++){var Fb=xa.s3ind_s[ca][0],vc=xa.s3ind_s[ca][1],pc,wc,Nb,gc,Cc;pc=Eb[Fb];wc=1;gc=xa.s3_ss[kb]*Ra[Fb]*p[Eb[Fb]];++kb;for(++Fb;Fb<=vc;)pc+=Eb[Fb],wc+=1,Nb=xa.s3_ss[kb]*Ra[Fb]*p[Eb[Fb]],gc=q(gc,Nb,Fb-ca),++kb,++Fb;pc=(1+2*pc)/(2*wc);Cc=.5*p[pc];gc*=Cc;Ha[ca]=gc;xa.nb_s2[bb][ca]=xa.nb_s1[bb][ca];xa.nb_s1[bb][ca]=gc;Nb=jb[ca];Nb*=xa.minval_s[ca];Nb*=Cc;Ha[ca]>Nb&&(Ha[ca]=Nb);1<xa.masking_lower&&(Ha[ca]*=xa.masking_lower);Ha[ca]>Ra[ca]&&
+(Ha[ca]=Ra[ca]);1>xa.masking_lower&&(Ha[ca]*=xa.masking_lower)}for(;ca<d.CBANDS;++ca)Ra[ca]=0,Ha[ca]=0}0==L[0]+L[1]&&c.mode==ka.JOINT_STEREO&&a(la,O,v.mld_cb_s,v.ATH.cb_s,c.ATHlower*v.ATH.adjust,c.msfix,v.npart_s);for(S=0;S<pa;++S)Pa=S&1,0==L[Pa]&&C(v,la[S],O[S],S,ba)}for(S=0;S<pa;S++)if(Pa=S&1,0==L[Pa])for(var hc=0;hc<d.SBMAX_s;hc++){for(var Ec=H(3),ba=0;3>ba;ba++){var cb=v.thm[S].s[hc][ba],cb=.8*cb;if(2<=K[S][ba]||1==K[S][ba+1])var tc=0!=ba?ba-1:2,uc=t(v.thm[S].s[hc][tc],cb,.36),cb=Math.min(cb,
+uc);else if(1==K[S][ba])tc=0!=ba?ba-1:2,uc=t(v.thm[S].s[hc][tc],cb,.18),cb=Math.min(cb,uc);else if(0!=ba&&3==K[S][ba-1]||0==ba&&3==v.nsPsy.lastAttacks[S])tc=2!=ba?ba+1:0,uc=t(v.thm[S].s[hc][tc],cb,.18),cb=Math.min(cb,uc);cb*=R[S][ba];Ec[ba]=cb}for(ba=0;3>ba;ba++)v.thm[S].s[hc][ba]=Ec[ba]}for(S=0;S<pa;S++)v.nsPsy.lastAttacks[S]=K[S][2];for(var Yb=c.internal_flags,zb=0;zb<Yb.channels_out;zb++){var Dc=d.NORM_TYPE;0!=L[zb]?Yb.blocktype_old[zb]==d.SHORT_TYPE&&(Dc=d.STOP_TYPE):(Dc=d.SHORT_TYPE,Yb.blocktype_old[zb]==
+d.NORM_TYPE&&(Yb.blocktype_old[zb]=d.START_TYPE),Yb.blocktype_old[zb]==d.STOP_TYPE&&(Yb.blocktype_old[zb]=d.SHORT_TYPE));u[zb]=Yb.blocktype_old[zb];Yb.blocktype_old[zb]=Dc}for(S=0;S<pa;S++){var xc,yc,zc,Ac;if(1<S){xc=x;yc=-2;zc=d.NORM_TYPE;if(u[0]==d.SHORT_TYPE||u[1]==d.SHORT_TYPE)zc=d.SHORT_TYPE;Ac=h[e][S-2]}else xc=n,yc=0,zc=u[S],Ac=k[e][S];xc[yc+S]=zc==d.SHORT_TYPE?V(Ac,v.masking_lower):g(Ac,v.masking_lower);c.analysis&&(v.pinfo.pe[e][S]=xc[yc+S])}return 0};this.psymodel_init=function(a){var b=
+a.internal_flags,m,f=!0,g=13,k=0,h=0,l=-8.25,p=-4.5,x=H(d.CBANDS),y=H(d.CBANDS),q=H(d.CBANDS),u=a.out_samplerate;switch(a.experimentalZ){default:case 0:f=!0;break;case 1:f=a.VBR==M.vbr_mtrh||a.VBR==M.vbr_mt?!1:!0;break;case 2:f=!1;break;case 3:g=8,k=-1.75,h=-.0125,l=-8.25,p=-2.25}b.ms_ener_ratio_old=.25;b.blocktype_old[0]=b.blocktype_old[1]=d.NORM_TYPE;for(m=0;4>m;++m){for(var r=0;r<d.CBANDS;++r)b.nb_1[m][r]=1E20,b.nb_2[m][r]=1E20,b.nb_s1[m][r]=b.nb_s2[m][r]=1;for(var t=0;t<d.SBMAX_l;t++)b.en[m].l[t]=
+1E20,b.thm[m].l[t]=1E20;for(r=0;3>r;++r){for(t=0;t<d.SBMAX_s;t++)b.en[m].s[t][r]=1E20,b.thm[m].s[t][r]=1E20;b.nsPsy.lastAttacks[m]=0}for(r=0;9>r;r++)b.nsPsy.last_en_subshort[m][r]=10}b.loudness_sq_save[0]=b.loudness_sq_save[1]=0;b.npart_l=e(b.numlines_l,b.bo_l,b.bm_l,x,y,b.mld_l,b.PSY.bo_l_weight,u,d.BLKSIZE,b.scalefac_band.l,d.BLKSIZE/1152,d.SBMAX_l);for(m=0;m<b.npart_l;m++)t=k,x[m]>=g&&(t=h*(x[m]-g)/(24-g)+k*(24-x[m])/(24-g)),q[m]=Math.pow(10,t/10),b.rnumlines_l[m]=0<b.numlines_l[m]?1/b.numlines_l[m]:
+0;b.s3_ll=n(b.s3ind,b.npart_l,x,y,q,f);for(m=r=0;m<b.npart_l;m++){h=vb.MAX_VALUE;for(t=0;t<b.numlines_l[m];t++,r++)k=u*r/(1E3*d.BLKSIZE),k=this.ATHformula(1E3*k,a)-20,k=Math.pow(10,.1*k),k*=b.numlines_l[m],h>k&&(h=k);b.ATH.cb_l[m]=h;h=-20+20*x[m]/10;6<h&&(h=100);-15>h&&(h=-15);h-=8;b.minval_l[m]=Math.pow(10,h/10)*b.numlines_l[m]}b.npart_s=e(b.numlines_s,b.bo_s,b.bm_s,x,y,b.mld_s,b.PSY.bo_s_weight,u,d.BLKSIZE_s,b.scalefac_band.s,d.BLKSIZE_s/384,d.SBMAX_s);for(m=r=0;m<b.npart_s;m++){t=l;x[m]>=g&&(t=
+p*(x[m]-g)/(24-g)+l*(24-x[m])/(24-g));q[m]=Math.pow(10,t/10);h=vb.MAX_VALUE;for(t=0;t<b.numlines_s[m];t++,r++)k=u*r/(1E3*d.BLKSIZE_s),k=this.ATHformula(1E3*k,a)-20,k=Math.pow(10,.1*k),k*=b.numlines_s[m],h>k&&(h=k);b.ATH.cb_s[m]=h;h=-7+7*x[m]/12;12<x[m]&&(h*=1+3.1*Math.log(1+h));12>x[m]&&(h*=1+2.3*Math.log(1-h));-15>h&&(h=-15);h-=8;b.minval_s[m]=Math.pow(10,h/10)*b.numlines_s[m]}b.s3_ss=n(b.s3ind_s,b.npart_s,x,y,q,f);w=Math.pow(10,.5625);A=Math.pow(10,1.5);v=Math.pow(10,1.5);E.init_fft(b);b.decay=
+Math.exp(-2.302585092994046/(.01*u/192));m=3.5;0!=(a.exp_nspsytune&2)&&(m=1);0<Math.abs(a.msfix)&&(m=a.msfix);a.msfix=m;for(f=0;f<b.npart_l;f++)b.s3ind[f][1]>b.npart_l-1&&(b.s3ind[f][1]=b.npart_l-1);b.ATH.decay=Math.pow(10,576*b.mode_gr/u*-1.2);b.ATH.adjust=.01;b.ATH.adjustLimit=1;if(-1!=a.ATHtype){r=a.out_samplerate/d.BLKSIZE;for(m=k=f=0;m<d.BLKSIZE/2;++m)k+=r,b.ATH.eql_w[m]=1/Math.pow(10,this.ATHformula(k,a)/10),f+=b.ATH.eql_w[m];f=1/f;for(m=d.BLKSIZE/2;0<=--m;)b.ATH.eql_w[m]*=f}for(f=r=0;f<b.npart_s;++f)for(m=
+0;m<b.numlines_s[f];++m)++r;for(f=r=0;f<b.npart_l;++f)for(m=0;m<b.numlines_l[f];++m)++r;for(m=r=0;m<b.npart_l;m++)k=u*(r+b.numlines_l[m]/2)/(1*d.BLKSIZE),b.mld_cb_l[m]=c(k),r+=b.numlines_l[m];for(;m<d.CBANDS;++m)b.mld_cb_l[m]=1;for(m=r=0;m<b.npart_s;m++)k=u*(r+b.numlines_s[m]/2)/(1*d.BLKSIZE_s),b.mld_cb_s[m]=c(k),r+=b.numlines_s[m];for(;m<d.CBANDS;++m)b.mld_cb_s[m]=1;return 0};this.ATHformula=function(a,c){var b;switch(c.ATHtype){case 0:b=f(a,9);break;case 1:b=f(a,-1);break;case 2:b=f(a,0);break;
+case 3:b=f(a,1)+6;break;case 4:b=f(a,c.ATHcurve);break;default:b=f(a,0)}return b}}function V(){function J(){this.mask_adjust_short=this.mask_adjust=0;this.bo_l_weight=H(d.SBMAX_l);this.bo_s_weight=H(d.SBMAX_s)}function k(){this.lowerlimit=0}function q(a,c){this.lowpass=c}function C(a){return 1<a?0:0>=a?1:Math.cos(Math.PI/2*a)}function B(a,c){switch(a){case 44100:return c.version=1,0;case 48E3:return c.version=1;case 32E3:return c.version=1,2;case 22050:return c.version=0;case 24E3:return c.version=
+0,1;case 16E3:return c.version=0,2;case 11025:return c.version=0;case 12E3:return c.version=0,1;case 8E3:return c.version=0,2;default:return c.version=0,-1}}function t(a,c,b){16E3>b&&(c=2);b=r.bitrate_table[c][1];for(var d=2;14>=d;d++)0<r.bitrate_table[c][d]&&Math.abs(r.bitrate_table[c][d]-a)<Math.abs(b-a)&&(b=r.bitrate_table[c][d]);return b}function Q(a,c,b){16E3>b&&(c=2);for(b=0;14>=b;b++)if(0<r.bitrate_table[c][b]&&r.bitrate_table[c][b]==a)return b;return-1}function g(a,c){var b=[new q(8,2E3),
+new q(16,3700),new q(24,3900),new q(32,5500),new q(40,7E3),new q(48,7500),new q(56,1E4),new q(64,11E3),new q(80,13500),new q(96,15100),new q(112,15600),new q(128,17E3),new q(160,17500),new q(192,18600),new q(224,19400),new q(256,19700),new q(320,20500)],d=e.nearestBitrateFullIndex(c);a.lowerlimit=b[d].lowpass}function b(a){var c=d.BLKSIZE+a.framesize-d.FFTOFFSET;return c=Math.max(c,512+a.framesize-32)}function l(m,f,g,k,l,p,q){var r=m.internal_flags,v=0,t,w,A=[null,null],z=[null,null];if(4294479419!=
+r.Class_ID)return-3;if(0==k)return 0;t=c.copy_buffer(r,l,p,q,0);if(0>t)return t;p+=t;v+=t;z[0]=f;z[1]=g;if(ua.NEQ(m.scale,0)&&ua.NEQ(m.scale,1))for(t=0;t<k;++t)z[0][t]*=m.scale,2==r.channels_out&&(z[1][t]*=m.scale);if(ua.NEQ(m.scale_left,0)&&ua.NEQ(m.scale_left,1))for(t=0;t<k;++t)z[0][t]*=m.scale_left;if(ua.NEQ(m.scale_right,0)&&ua.NEQ(m.scale_right,1))for(t=0;t<k;++t)z[1][t]*=m.scale_right;if(2==m.num_channels&&1==r.channels_out)for(t=0;t<k;++t)z[0][t]=.5*(z[0][t]+z[1][t]),z[1][t]=0;f=b(m);A[0]=
+r.mfbuf[0];A[1]=r.mfbuf[1];for(g=0;0<k;){var B=[null,null];t=w=0;B[0]=z[0];B[1]=z[1];t=new a;var C=m;w=A;var E=g,J=k,K=t,M=C.internal_flags;if(.9999>M.resample_ratio||1.0001<M.resample_ratio)for(var Q=0;Q<M.channels_out;Q++){var V=new h,Y=K,X=w[Q],ta=M.mf_size,ka=C.framesize,ma=B[Q],ra=E,R=J,Fa=V,L=Q,Ma=C.internal_flags,Z=void 0,Da=0,na=void 0,sa=C.out_samplerate/D(C.out_samplerate,C.in_samplerate);sa>da.BPC&&(sa=da.BPC);var P=1E-4>Math.abs(Ma.resample_ratio-Math.floor(.5+Ma.resample_ratio))?1:0,
+na=1/Ma.resample_ratio;1<na&&(na=1);var ha=31;0==ha%2&&--ha;ha+=P;P=ha+1;if(0==Ma.fill_buffer_resample_init){Ma.inbuf_old[0]=H(P);Ma.inbuf_old[1]=H(P);for(Z=0;Z<=2*sa;++Z)Ma.blackfilt[Z]=H(P);Ma.itime[0]=0;for(Da=Ma.itime[1]=0;Da<=2*sa;Da++){for(var N=0,Ca=(Da-sa)/(2*sa),Z=0;Z<=ha;Z++){var Ia=Ma.blackfilt[Da],Ga=Z,Sa,ea=Z-Ca;Sa=ha;var Oa=Math.PI*na,ea=ea/Sa;0>ea&&(ea=0);1<ea&&(ea=1);var Qa=ea-.5,ea=.42-.5*Math.cos(2*ea*Math.PI)+.08*Math.cos(4*ea*Math.PI);Sa=1E-9>Math.abs(Qa)?Oa/Math.PI:ea*Math.sin(Sa*
+Oa*Qa)/(Math.PI*Sa*Qa);N+=Ia[Ga]=Sa}for(Z=0;Z<=ha;Z++)Ma.blackfilt[Da][Z]/=N}Ma.fill_buffer_resample_init=1}N=Ma.inbuf_old[L];for(na=0;na<ka;na++){Z=na*Ma.resample_ratio;Da=0|Math.floor(Z-Ma.itime[L]);if(ha+Da-ha/2>=R)break;Ca=Z-Ma.itime[L]-(Da+ha%2*.5);Ca=0|Math.floor(2*Ca*sa+sa+.5);for(Z=Ia=0;Z<=ha;++Z)Ga=Z+Da-ha/2,Ia+=(0>Ga?N[P+Ga]:ma[ra+Ga])*Ma.blackfilt[Ca][Z];X[ta+na]=Ia}Fa.num_used=Math.min(R,ha+Da-ha/2);Ma.itime[L]+=Fa.num_used-na*Ma.resample_ratio;if(Fa.num_used>=P)for(Z=0;Z<P;Z++)N[Z]=ma[ra+
+Fa.num_used+Z-P];else{X=P-Fa.num_used;for(Z=0;Z<X;++Z)N[Z]=N[Z+Fa.num_used];for(Da=0;Z<P;++Z,++Da)N[Z]=ma[ra+Da]}Y.n_out=na;K.n_in=V.num_used}else for(K.n_out=Math.min(C.framesize,J),K.n_in=K.n_out,C=0;C<K.n_out;++C)w[0][M.mf_size+C]=B[0][E+C],2==M.channels_out&&(w[1][M.mf_size+C]=B[1][E+C]);w=t.n_in;t=t.n_out;if(r.findReplayGain&&!r.decode_on_the_fly&&n.AnalyzeSamples(r.rgdata,A[0],r.mf_size,A[1],r.mf_size,t,r.channels_out)==O.GAIN_ANALYSIS_ERROR)return-6;k-=w;g+=w;r.mf_size+=t;1>r.mf_samples_to_encode&&
+(r.mf_samples_to_encode=d.ENCDELAY+d.POSTDELAY);r.mf_samples_to_encode+=t;if(r.mf_size>=f){w=q-v;0==q&&(w=0);t=m;w=e.enc.lame_encode_mp3_frame(t,A[0],A[1],l,p,w);t.frameNum++;t=w;if(0>t)return t;p+=t;v+=t;r.mf_size-=m.framesize;r.mf_samples_to_encode-=m.framesize;for(w=0;w<r.channels_out;w++)for(t=0;t<r.mf_size;t++)A[w][t]=A[w][t+m.framesize]}}return v}function a(){this.n_out=this.n_in=0}function h(){this.num_used=0}function D(a,c){return 0!=c?D(c,a%c):a}var e=this;V.V9=410;V.V8=420;V.V7=430;V.V6=
+440;V.V5=450;V.V4=460;V.V3=470;V.V2=480;V.V1=490;V.V0=500;V.R3MIX=1E3;V.STANDARD=1001;V.EXTREME=1002;V.INSANE=1003;V.STANDARD_FAST=1004;V.EXTREME_FAST=1005;V.MEDIUM=1006;V.MEDIUM_FAST=1007;V.LAME_MAXMP3BUFFER=147456;var n,c,f,E,z,w=new jc,A,v,p;this.enc=new d;this.setModules=function(a,b,d,g,e,k,h,l,q){n=a;c=b;f=d;E=g;z=e;A=k;v=l;p=q;this.enc.setModules(c,w,E,A)};this.lame_init=function(){var a=new Jc,c;a.class_id=4294479419;c=a.internal_flags=new da;a.mode=ka.NOT_SET;a.original=1;a.in_samplerate=
+44100;a.num_channels=2;a.num_samples=-1;a.bWriteVbrTag=!0;a.quality=-1;a.short_blocks=null;c.subblock_gain=-1;a.lowpassfreq=0;a.highpassfreq=0;a.lowpasswidth=-1;a.highpasswidth=-1;a.VBR=M.vbr_off;a.VBR_q=4;a.ATHcurve=-1;a.VBR_mean_bitrate_kbps=128;a.VBR_min_bitrate_kbps=0;a.VBR_max_bitrate_kbps=0;a.VBR_hard_min=0;c.VBR_min_bitrate=1;c.VBR_max_bitrate=13;a.quant_comp=-1;a.quant_comp_short=-1;a.msfix=-1;c.resample_ratio=1;c.OldValue[0]=180;c.OldValue[1]=180;c.CurrentStep[0]=4;c.CurrentStep[1]=4;c.masking_lower=
+1;c.nsPsy.attackthre=-1;c.nsPsy.attackthre_s=-1;a.scale=-1;a.athaa_type=-1;a.ATHtype=-1;a.athaa_loudapprox=-1;a.athaa_sensitivity=0;a.useTemporal=null;a.interChRatio=-1;c.mf_samples_to_encode=d.ENCDELAY+d.POSTDELAY;a.encoder_padding=0;c.mf_size=d.ENCDELAY-d.MDCTDELAY;a.findReplayGain=!1;a.decode_on_the_fly=!1;c.decode_on_the_fly=!1;c.findReplayGain=!1;c.findPeakSample=!1;c.RadioGain=0;c.AudiophileGain=0;c.noclipGainChange=0;c.noclipScale=-1;a.preset=0;a.write_id3tag_automatic=!0;a.lame_allocated_gfp=
+1;return a};this.nearestBitrateFullIndex=function(a){for(var c=[8,16,24,32,40,48,56,64,80,96,112,128,160,192,224,256,320],b=0,d=0,f=0,g=0,g=c[16],f=16,d=c[16],b=16,e=0;16>e;e++)if(Math.max(a,c[e+1])!=a){g=c[e+1];f=e+1;d=c[e];b=e;break}return g-a>a-d?b:f};this.lame_init_params=function(a){var b=a.internal_flags;b.Class_ID=0;null==b.ATH&&(b.ATH=new Lc);null==b.PSY&&(b.PSY=new J);null==b.rgdata&&(b.rgdata=new Kc);b.channels_in=a.num_channels;1==b.channels_in&&(a.mode=ka.MONO);b.channels_out=a.mode==
+ka.MONO?1:2;b.mode_ext=d.MPG_MD_MS_LR;a.mode==ka.MONO&&(a.force_ms=!1);a.VBR==M.vbr_off&&128!=a.VBR_mean_bitrate_kbps&&0==a.brate&&(a.brate=a.VBR_mean_bitrate_kbps);a.VBR!=M.vbr_off&&a.VBR!=M.vbr_mtrh&&a.VBR!=M.vbr_mt&&(a.free_format=!1);a.VBR==M.vbr_off&&0==a.brate&&ua.EQ(a.compression_ratio,0)&&(a.compression_ratio=11.025);a.VBR==M.vbr_off&&0<a.compression_ratio&&(0==a.out_samplerate&&(a.out_samplerate=map2MP3Frequency(int(.97*a.in_samplerate))),a.brate=0|16*a.out_samplerate*b.channels_out/(1E3*
+a.compression_ratio),b.samplerate_index=B(a.out_samplerate,a),a.free_format||(a.brate=t(a.brate,a.version,a.out_samplerate)));0!=a.out_samplerate&&(16E3>a.out_samplerate?(a.VBR_mean_bitrate_kbps=Math.max(a.VBR_mean_bitrate_kbps,8),a.VBR_mean_bitrate_kbps=Math.min(a.VBR_mean_bitrate_kbps,64)):32E3>a.out_samplerate?(a.VBR_mean_bitrate_kbps=Math.max(a.VBR_mean_bitrate_kbps,8),a.VBR_mean_bitrate_kbps=Math.min(a.VBR_mean_bitrate_kbps,160)):(a.VBR_mean_bitrate_kbps=Math.max(a.VBR_mean_bitrate_kbps,32),
+a.VBR_mean_bitrate_kbps=Math.min(a.VBR_mean_bitrate_kbps,320)));if(0==a.lowpassfreq){var e=16E3;switch(a.VBR){case M.vbr_off:e=new k;g(e,a.brate);e=e.lowerlimit;break;case M.vbr_abr:e=new k;g(e,a.VBR_mean_bitrate_kbps);e=e.lowerlimit;break;case M.vbr_rh:var h=[19500,19E3,18600,18E3,17500,16E3,15600,14900,12500,1E4,3950];if(0<=a.VBR_q&&9>=a.VBR_q)var e=h[a.VBR_q],h=h[a.VBR_q+1],l=a.VBR_q_frac,e=linear_int(e,h,l);else e=19500;break;default:h=[19500,19E3,18500,18E3,17500,16500,15500,14500,12500,9500,
+3950],0<=a.VBR_q&&9>=a.VBR_q?(e=h[a.VBR_q],h=h[a.VBR_q+1],l=a.VBR_q_frac,e=linear_int(e,h,l)):e=19500}a.mode!=ka.MONO||a.VBR!=M.vbr_off&&a.VBR!=M.vbr_abr||(e*=1.5);a.lowpassfreq=e|0}0==a.out_samplerate&&(2*a.lowpassfreq>a.in_samplerate&&(a.lowpassfreq=a.in_samplerate/2),e=a.lowpassfreq|0,h=a.in_samplerate,l=44100,48E3<=h?l=48E3:44100<=h?l=44100:32E3<=h?l=32E3:24E3<=h?l=24E3:22050<=h?l=22050:16E3<=h?l=16E3:12E3<=h?l=12E3:11025<=h?l=11025:8E3<=h&&(l=8E3),-1==e?e=l:(15960>=e&&(l=44100),15250>=e&&(l=
+32E3),11220>=e&&(l=24E3),9970>=e&&(l=22050),7230>=e&&(l=16E3),5420>=e&&(l=12E3),4510>=e&&(l=11025),3970>=e&&(l=8E3),e=h<l?44100<h?48E3:32E3<h?44100:24E3<h?32E3:22050<h?24E3:16E3<h?22050:12E3<h?16E3:11025<h?12E3:8E3<h?11025:8E3:l),a.out_samplerate=e);a.lowpassfreq=Math.min(20500,a.lowpassfreq);a.lowpassfreq=Math.min(a.out_samplerate/2,a.lowpassfreq);a.VBR==M.vbr_off&&(a.compression_ratio=16*a.out_samplerate*b.channels_out/(1E3*a.brate));a.VBR==M.vbr_abr&&(a.compression_ratio=16*a.out_samplerate*b.channels_out/
+(1E3*a.VBR_mean_bitrate_kbps));a.bWriteVbrTag||(a.findReplayGain=!1,a.decode_on_the_fly=!1,b.findPeakSample=!1);b.findReplayGain=a.findReplayGain;b.decode_on_the_fly=a.decode_on_the_fly;b.decode_on_the_fly&&(b.findPeakSample=!0);if(b.findReplayGain&&n.InitGainAnalysis(b.rgdata,a.out_samplerate)==O.INIT_GAIN_ANALYSIS_ERROR)return a.internal_flags=null,-6;b.decode_on_the_fly&&!a.decode_only&&(null!=b.hip&&p.hip_decode_exit(b.hip),b.hip=p.hip_decode_init());b.mode_gr=24E3>=a.out_samplerate?1:2;a.framesize=
+576*b.mode_gr;a.encoder_delay=d.ENCDELAY;b.resample_ratio=a.in_samplerate/a.out_samplerate;switch(a.VBR){case M.vbr_mt:case M.vbr_rh:case M.vbr_mtrh:a.compression_ratio=[5.7,6.5,7.3,8.2,10,11.9,13,14,15,16.5][a.VBR_q];break;case M.vbr_abr:a.compression_ratio=16*a.out_samplerate*b.channels_out/(1E3*a.VBR_mean_bitrate_kbps);break;default:a.compression_ratio=16*a.out_samplerate*b.channels_out/(1E3*a.brate)}a.mode==ka.NOT_SET&&(a.mode=ka.JOINT_STEREO);0<a.highpassfreq?(b.highpass1=2*a.highpassfreq,b.highpass2=
+0<=a.highpasswidth?2*(a.highpassfreq+a.highpasswidth):2*a.highpassfreq,b.highpass1/=a.out_samplerate,b.highpass2/=a.out_samplerate):(b.highpass1=0,b.highpass2=0);0<a.lowpassfreq?(b.lowpass2=2*a.lowpassfreq,0<=a.lowpasswidth?(b.lowpass1=2*(a.lowpassfreq-a.lowpasswidth),0>b.lowpass1&&(b.lowpass1=0)):b.lowpass1=2*a.lowpassfreq,b.lowpass1/=a.out_samplerate,b.lowpass2/=a.out_samplerate):(b.lowpass1=0,b.lowpass2=0);var e=a.internal_flags,q=32,D=-1;if(0<e.lowpass1){for(var F=999,h=0;31>=h;h++)l=h/31,l>=
+e.lowpass2&&(q=Math.min(q,h)),e.lowpass1<l&&l<e.lowpass2&&(F=Math.min(F,h));e.lowpass1=999==F?(q-.75)/31:(F-.75)/31;e.lowpass2=q/31}0<e.highpass2&&e.highpass2<.75/31*.9&&(e.highpass1=0,e.highpass2=0,K.err.println("Warning: highpass filter disabled.  highpass frequency too small\n"));if(0<e.highpass2){q=-1;for(h=0;31>=h;h++)l=h/31,l<=e.highpass1&&(D=Math.max(D,h)),e.highpass1<l&&l<e.highpass2&&(q=Math.max(q,h));e.highpass1=D/31;e.highpass2=-1==q?(D+.75)/31:(q+.75)/31}for(h=0;32>h;h++)l=h/31,D=e.highpass2>
+e.highpass1?C((e.highpass2-l)/(e.highpass2-e.highpass1+1E-20)):1,l=e.lowpass2>e.lowpass1?C((l-e.lowpass1)/(e.lowpass2-e.lowpass1+1E-20)):1,e.amp_filter[h]=D*l;b.samplerate_index=B(a.out_samplerate,a);if(0>b.samplerate_index)return a.internal_flags=null,-1;if(a.VBR==M.vbr_off)if(a.free_format)b.bitrate_index=0;else{if(a.brate=t(a.brate,a.version,a.out_samplerate),b.bitrate_index=Q(a.brate,a.version,a.out_samplerate),0>=b.bitrate_index)return a.internal_flags=null,-1}else b.bitrate_index=1;a.analysis&&
+(a.bWriteVbrTag=!1);null!=b.pinfo&&(a.bWriteVbrTag=!1);c.init_bit_stream_w(b);e=b.samplerate_index+3*a.version+6*(16E3>a.out_samplerate?1:0);for(h=0;h<d.SBMAX_l+1;h++)b.scalefac_band.l[h]=E.sfBandIndex[e].l[h];for(h=0;h<d.PSFB21+1;h++)l=(b.scalefac_band.l[22]-b.scalefac_band.l[21])/d.PSFB21,l=b.scalefac_band.l[21]+h*l,b.scalefac_band.psfb21[h]=l;b.scalefac_band.psfb21[d.PSFB21]=576;for(h=0;h<d.SBMAX_s+1;h++)b.scalefac_band.s[h]=E.sfBandIndex[e].s[h];for(h=0;h<d.PSFB12+1;h++)l=(b.scalefac_band.s[13]-
+b.scalefac_band.s[12])/d.PSFB12,l=b.scalefac_band.s[12]+h*l,b.scalefac_band.psfb12[h]=l;b.scalefac_band.psfb12[d.PSFB12]=192;b.sideinfo_len=1==a.version?1==b.channels_out?21:36:1==b.channels_out?13:21;a.error_protection&&(b.sideinfo_len+=2);e=a.internal_flags;a.frameNum=0;a.write_id3tag_automatic&&v.id3tag_write_v2(a);e.bitrate_stereoMode_Hist=db([16,5]);e.bitrate_blockType_Hist=db([16,6]);e.PeakSample=0;a.bWriteVbrTag&&A.InitVbrTag(a);b.Class_ID=4294479419;for(e=0;19>e;e++)b.nsPsy.pefirbuf[e]=700*
+b.mode_gr*b.channels_out;-1==a.ATHtype&&(a.ATHtype=4);switch(a.VBR){case M.vbr_mt:a.VBR=M.vbr_mtrh;case M.vbr_mtrh:null==a.useTemporal&&(a.useTemporal=!1);f.apply_preset(a,500-10*a.VBR_q,0);0>a.quality&&(a.quality=LAME_DEFAULT_QUALITY);5>a.quality&&(a.quality=0);5<a.quality&&(a.quality=5);b.PSY.mask_adjust=a.maskingadjust;b.PSY.mask_adjust_short=a.maskingadjust_short;b.sfb21_extra=a.experimentalY?!1:44E3<a.out_samplerate;b.iteration_loop=new VBRNewIterationLoop(z);break;case M.vbr_rh:f.apply_preset(a,
+500-10*a.VBR_q,0);b.PSY.mask_adjust=a.maskingadjust;b.PSY.mask_adjust_short=a.maskingadjust_short;b.sfb21_extra=a.experimentalY?!1:44E3<a.out_samplerate;6<a.quality&&(a.quality=6);0>a.quality&&(a.quality=LAME_DEFAULT_QUALITY);b.iteration_loop=new VBROldIterationLoop(z);break;default:b.sfb21_extra=!1,0>a.quality&&(a.quality=LAME_DEFAULT_QUALITY),e=a.VBR,e==M.vbr_off&&(a.VBR_mean_bitrate_kbps=a.brate),f.apply_preset(a,a.VBR_mean_bitrate_kbps,0),a.VBR=e,b.PSY.mask_adjust=a.maskingadjust,b.PSY.mask_adjust_short=
+a.maskingadjust_short,b.iteration_loop=e==M.vbr_off?new Mc(z):new ABRIterationLoop(z)}if(a.VBR!=M.vbr_off){b.VBR_min_bitrate=1;b.VBR_max_bitrate=14;16E3>a.out_samplerate&&(b.VBR_max_bitrate=8);if(0!=a.VBR_min_bitrate_kbps&&(a.VBR_min_bitrate_kbps=t(a.VBR_min_bitrate_kbps,a.version,a.out_samplerate),b.VBR_min_bitrate=Q(a.VBR_min_bitrate_kbps,a.version,a.out_samplerate),0>b.VBR_min_bitrate)||0!=a.VBR_max_bitrate_kbps&&(a.VBR_max_bitrate_kbps=t(a.VBR_max_bitrate_kbps,a.version,a.out_samplerate),b.VBR_max_bitrate=
+Q(a.VBR_max_bitrate_kbps,a.version,a.out_samplerate),0>b.VBR_max_bitrate))return-1;a.VBR_min_bitrate_kbps=r.bitrate_table[a.version][b.VBR_min_bitrate];a.VBR_max_bitrate_kbps=r.bitrate_table[a.version][b.VBR_max_bitrate];a.VBR_mean_bitrate_kbps=Math.min(r.bitrate_table[a.version][b.VBR_max_bitrate],a.VBR_mean_bitrate_kbps);a.VBR_mean_bitrate_kbps=Math.max(r.bitrate_table[a.version][b.VBR_min_bitrate],a.VBR_mean_bitrate_kbps)}a.tune&&(b.PSY.mask_adjust+=a.tune_value_a,b.PSY.mask_adjust_short+=a.tune_value_a);
+e=a.internal_flags;switch(a.quality){default:case 9:e.psymodel=0;e.noise_shaping=0;e.noise_shaping_amp=0;e.noise_shaping_stop=0;e.use_best_huffman=0;e.full_outer_loop=0;break;case 8:a.quality=7;case 7:e.psymodel=1;e.noise_shaping=0;e.noise_shaping_amp=0;e.noise_shaping_stop=0;e.use_best_huffman=0;e.full_outer_loop=0;break;case 6:e.psymodel=1;0==e.noise_shaping&&(e.noise_shaping=1);e.noise_shaping_amp=0;e.noise_shaping_stop=0;-1==e.subblock_gain&&(e.subblock_gain=1);e.use_best_huffman=0;e.full_outer_loop=
+0;break;case 5:e.psymodel=1;0==e.noise_shaping&&(e.noise_shaping=1);e.noise_shaping_amp=0;e.noise_shaping_stop=0;-1==e.subblock_gain&&(e.subblock_gain=1);e.use_best_huffman=0;e.full_outer_loop=0;break;case 4:e.psymodel=1;0==e.noise_shaping&&(e.noise_shaping=1);e.noise_shaping_amp=0;e.noise_shaping_stop=0;-1==e.subblock_gain&&(e.subblock_gain=1);e.use_best_huffman=1;e.full_outer_loop=0;break;case 3:e.psymodel=1;0==e.noise_shaping&&(e.noise_shaping=1);e.noise_shaping_amp=1;e.noise_shaping_stop=1;-1==
+e.subblock_gain&&(e.subblock_gain=1);e.use_best_huffman=1;e.full_outer_loop=0;break;case 2:e.psymodel=1;0==e.noise_shaping&&(e.noise_shaping=1);0==e.substep_shaping&&(e.substep_shaping=2);e.noise_shaping_amp=1;e.noise_shaping_stop=1;-1==e.subblock_gain&&(e.subblock_gain=1);e.use_best_huffman=1;e.full_outer_loop=0;break;case 1:e.psymodel=1;0==e.noise_shaping&&(e.noise_shaping=1);0==e.substep_shaping&&(e.substep_shaping=2);e.noise_shaping_amp=2;e.noise_shaping_stop=1;-1==e.subblock_gain&&(e.subblock_gain=
+1);e.use_best_huffman=1;e.full_outer_loop=0;break;case 0:e.psymodel=1,0==e.noise_shaping&&(e.noise_shaping=1),0==e.substep_shaping&&(e.substep_shaping=2),e.noise_shaping_amp=2,e.noise_shaping_stop=1,-1==e.subblock_gain&&(e.subblock_gain=1),e.use_best_huffman=1,e.full_outer_loop=0}b.ATH.useAdjust=0>a.athaa_type?3:a.athaa_type;b.ATH.aaSensitivityP=Math.pow(10,a.athaa_sensitivity/-10);null==a.short_blocks&&(a.short_blocks=sa.short_block_allowed);a.short_blocks!=sa.short_block_allowed||a.mode!=ka.JOINT_STEREO&&
+a.mode!=ka.STEREO||(a.short_blocks=sa.short_block_coupled);0>a.quant_comp&&(a.quant_comp=1);0>a.quant_comp_short&&(a.quant_comp_short=0);0>a.msfix&&(a.msfix=0);a.exp_nspsytune|=1;0>a.internal_flags.nsPsy.attackthre&&(a.internal_flags.nsPsy.attackthre=jc.NSATTACKTHRE);0>a.internal_flags.nsPsy.attackthre_s&&(a.internal_flags.nsPsy.attackthre_s=jc.NSATTACKTHRE_S);0>a.scale&&(a.scale=1);0>a.ATHtype&&(a.ATHtype=4);0>a.ATHcurve&&(a.ATHcurve=4);0>a.athaa_loudapprox&&(a.athaa_loudapprox=2);0>a.interChRatio&&
+(a.interChRatio=0);null==a.useTemporal&&(a.useTemporal=!0);b.slot_lag=b.frac_SpF=0;a.VBR==M.vbr_off&&(b.slot_lag=b.frac_SpF=72E3*(a.version+1)*a.brate%a.out_samplerate|0);E.iteration_init(a);w.psymodel_init(a);return 0};this.lame_encode_flush=function(a,e,f,g){var h=a.internal_flags,k=vc([2,1152]),l=0,n,p,q=h.mf_samples_to_encode-d.POSTDELAY,r=b(a);if(1>h.mf_samples_to_encode)return 0;n=0;a.in_samplerate!=a.out_samplerate&&(q+=16*a.out_samplerate/a.in_samplerate);p=a.framesize-q%a.framesize;576>p&&
+(p+=a.framesize);a.encoder_padding=p;for(p=(q+p)/a.framesize;0<p&&0<=l;){var t=r-h.mf_size,q=a.frameNum,t=t*a.in_samplerate,t=t/a.out_samplerate;1152<t&&(t=1152);1>t&&(t=1);l=g-n;0==g&&(l=0);l=this.lame_encode_buffer(a,k[0],k[1],t,e,f,l);f+=l;n+=l;p-=q!=a.frameNum?1:0}h.mf_samples_to_encode=0;if(0>l)return l;l=g-n;0==g&&(l=0);c.flush_bitstream(a);l=c.copy_buffer(h,e,f,l,1);if(0>l)return l;f+=l;n+=l;l=g-n;0==g&&(l=0);if(a.write_id3tag_automatic){v.id3tag_write_v1(a);l=c.copy_buffer(h,e,f,l,0);if(0>
+l)return l;n+=l}return n};this.lame_encode_buffer=function(a,b,c,d,e,f,g){var h=a.internal_flags,k=[null,null];if(4294479419!=h.Class_ID)return-3;if(0==d)return 0;if(null==h.in_buffer_0||h.in_buffer_nsamples<d)h.in_buffer_0=H(d),h.in_buffer_1=H(d),h.in_buffer_nsamples=d;k[0]=h.in_buffer_0;k[1]=h.in_buffer_1;for(var n=0;n<d;n++)k[0][n]=b[n],1<h.channels_in&&(k[1][n]=c[n]);return l(a,k[0],k[1],d,e,f,g)}}function Uc(){this.setModules=function(d,k){}}function Vc(){this.setModules=function(d,k,q){}}function Wc(){}
+function Xc(){this.setModules=function(d,k){}}function Ca(){this.sampleRate=this.channels=this.dataLen=this.dataOffset=0}function Ob(d){return d.charCodeAt(0)<<24|d.charCodeAt(1)<<16|d.charCodeAt(2)<<8|d.charCodeAt(3)}var Ia={fill:function(d,k,q,r){if(2==arguments.length)for(var B=0;B<d.length;B++)d[B]=arguments[1];else for(B=k;B<q;B++)d[B]=r}},K={arraycopy:function(d,k,q,r,B){for(B=k+B;k<B;)q[r++]=d[k++]}},X={SQRT2:1.4142135623730951,FAST_LOG10:function(d){return Math.log10(d)},FAST_LOG10_X:function(d,
+k){return Math.log10(d)*k}};sa.short_block_allowed=new sa(0);sa.short_block_coupled=new sa(1);sa.short_block_dispensed=new sa(2);sa.short_block_forced=new sa(3);var vb={MAX_VALUE:3.4028235E38};M.vbr_off=new M(0);M.vbr_mt=new M(1);M.vbr_rh=new M(2);M.vbr_abr=new M(3);M.vbr_mtrh=new M(4);M.vbr_default=M.vbr_mtrh;ka.STEREO=new ka(0);ka.JOINT_STEREO=new ka(1);ka.DUAL_CHANNEL=new ka(2);ka.MONO=new ka(3);ka.NOT_SET=new ka(4);O.STEPS_per_dB=100;O.MAX_dB=120;O.GAIN_NOT_ENOUGH_SAMPLES=-24601;O.GAIN_ANALYSIS_ERROR=
+0;O.GAIN_ANALYSIS_OK=1;O.INIT_GAIN_ANALYSIS_ERROR=0;O.INIT_GAIN_ANALYSIS_OK=1;O.YULE_ORDER=10;O.MAX_ORDER=O.YULE_ORDER;O.MAX_SAMP_FREQ=48E3;O.RMS_WINDOW_TIME_NUMERATOR=1;O.RMS_WINDOW_TIME_DENOMINATOR=20;O.MAX_SAMPLES_PER_WINDOW=O.MAX_SAMP_FREQ*O.RMS_WINDOW_TIME_NUMERATOR/O.RMS_WINDOW_TIME_DENOMINATOR+1;ua.EQ=function(d,k){return Math.abs(d)>Math.abs(k)?Math.abs(d-k)<=1E-6*Math.abs(d):Math.abs(d-k)<=1E-6*Math.abs(k)};ua.NEQ=function(d,k){return!ua.EQ(d,k)};ub.NUMTOCENTRIES=100;ub.MAXFRAMESIZE=2880;
+var r={t1HB:[1,1,1,0],t2HB:[1,2,1,3,1,1,3,2,0],t3HB:[3,2,1,1,1,1,3,2,0],t5HB:[1,2,6,5,3,1,4,4,7,5,7,1,6,1,1,0],t6HB:[7,3,5,1,6,2,3,2,5,4,4,1,3,3,2,0],t7HB:[1,2,10,19,16,10,3,3,7,10,5,3,11,4,13,17,8,4,12,11,18,15,11,2,7,6,9,14,3,1,6,4,5,3,2,0],t8HB:[3,4,6,18,12,5,5,1,2,16,9,3,7,3,5,14,7,3,19,17,15,13,10,4,13,5,8,11,5,1,12,4,4,1,1,0],t9HB:[7,5,9,14,15,7,6,4,5,5,6,7,7,6,8,8,8,5,15,6,9,10,5,1,11,7,9,6,4,1,14,4,6,2,6,0],t10HB:[1,2,10,23,35,30,12,17,3,3,8,12,18,21,12,7,11,9,15,21,32,40,19,6,14,13,22,34,
+46,23,18,7,20,19,33,47,27,22,9,3,31,22,41,26,21,20,5,3,14,13,10,11,16,6,5,1,9,8,7,8,4,4,2,0],t11HB:[3,4,10,24,34,33,21,15,5,3,4,10,32,17,11,10,11,7,13,18,30,31,20,5,25,11,19,59,27,18,12,5,35,33,31,58,30,16,7,5,28,26,32,19,17,15,8,14,14,12,9,13,14,9,4,1,11,4,6,6,6,3,2,0],t12HB:[9,6,16,33,41,39,38,26,7,5,6,9,23,16,26,11,17,7,11,14,21,30,10,7,17,10,15,12,18,28,14,5,32,13,22,19,18,16,9,5,40,17,31,29,17,13,4,2,27,12,11,15,10,7,4,1,27,12,8,12,6,3,1,0],t13HB:[1,5,14,21,34,51,46,71,42,52,68,52,67,44,43,19,
+3,4,12,19,31,26,44,33,31,24,32,24,31,35,22,14,15,13,23,36,59,49,77,65,29,40,30,40,27,33,42,16,22,20,37,61,56,79,73,64,43,76,56,37,26,31,25,14,35,16,60,57,97,75,114,91,54,73,55,41,48,53,23,24,58,27,50,96,76,70,93,84,77,58,79,29,74,49,41,17,47,45,78,74,115,94,90,79,69,83,71,50,59,38,36,15,72,34,56,95,92,85,91,90,86,73,77,65,51,44,43,42,43,20,30,44,55,78,72,87,78,61,46,54,37,30,20,16,53,25,41,37,44,59,54,81,66,76,57,54,37,18,39,11,35,33,31,57,42,82,72,80,47,58,55,21,22,26,38,22,53,25,23,38,70,60,51,
+36,55,26,34,23,27,14,9,7,34,32,28,39,49,75,30,52,48,40,52,28,18,17,9,5,45,21,34,64,56,50,49,45,31,19,12,15,10,7,6,3,48,23,20,39,36,35,53,21,16,23,13,10,6,1,4,2,16,15,17,27,25,20,29,11,17,12,16,8,1,1,0,1],t15HB:[7,12,18,53,47,76,124,108,89,123,108,119,107,81,122,63,13,5,16,27,46,36,61,51,42,70,52,83,65,41,59,36,19,17,15,24,41,34,59,48,40,64,50,78,62,80,56,33,29,28,25,43,39,63,55,93,76,59,93,72,54,75,50,29,52,22,42,40,67,57,95,79,72,57,89,69,49,66,46,27,77,37,35,66,58,52,91,74,62,48,79,63,90,62,40,
+38,125,32,60,56,50,92,78,65,55,87,71,51,73,51,70,30,109,53,49,94,88,75,66,122,91,73,56,42,64,44,21,25,90,43,41,77,73,63,56,92,77,66,47,67,48,53,36,20,71,34,67,60,58,49,88,76,67,106,71,54,38,39,23,15,109,53,51,47,90,82,58,57,48,72,57,41,23,27,62,9,86,42,40,37,70,64,52,43,70,55,42,25,29,18,11,11,118,68,30,55,50,46,74,65,49,39,24,16,22,13,14,7,91,44,39,38,34,63,52,45,31,52,28,19,14,8,9,3,123,60,58,53,47,43,32,22,37,24,17,12,15,10,2,1,71,37,34,30,28,20,17,26,21,16,10,6,8,6,2,0],t16HB:[1,5,14,44,74,63,
+110,93,172,149,138,242,225,195,376,17,3,4,12,20,35,62,53,47,83,75,68,119,201,107,207,9,15,13,23,38,67,58,103,90,161,72,127,117,110,209,206,16,45,21,39,69,64,114,99,87,158,140,252,212,199,387,365,26,75,36,68,65,115,101,179,164,155,264,246,226,395,382,362,9,66,30,59,56,102,185,173,265,142,253,232,400,388,378,445,16,111,54,52,100,184,178,160,133,257,244,228,217,385,366,715,10,98,48,91,88,165,157,148,261,248,407,397,372,380,889,884,8,85,84,81,159,156,143,260,249,427,401,392,383,727,713,708,7,154,76,73,
+141,131,256,245,426,406,394,384,735,359,710,352,11,139,129,67,125,247,233,229,219,393,743,737,720,885,882,439,4,243,120,118,115,227,223,396,746,742,736,721,712,706,223,436,6,202,224,222,218,216,389,386,381,364,888,443,707,440,437,1728,4,747,211,210,208,370,379,734,723,714,1735,883,877,876,3459,865,2,377,369,102,187,726,722,358,711,709,866,1734,871,3458,870,434,0,12,10,7,11,10,17,11,9,13,12,10,7,5,3,1,3],t24HB:[15,13,46,80,146,262,248,434,426,669,653,649,621,517,1032,88,14,12,21,38,71,130,122,216,
+209,198,327,345,319,297,279,42,47,22,41,74,68,128,120,221,207,194,182,340,315,295,541,18,81,39,75,70,134,125,116,220,204,190,178,325,311,293,271,16,147,72,69,135,127,118,112,210,200,188,352,323,306,285,540,14,263,66,129,126,119,114,214,202,192,180,341,317,301,281,262,12,249,123,121,117,113,215,206,195,185,347,330,308,291,272,520,10,435,115,111,109,211,203,196,187,353,332,313,298,283,531,381,17,427,212,208,205,201,193,186,177,169,320,303,286,268,514,377,16,335,199,197,191,189,181,174,333,321,305,289,
+275,521,379,371,11,668,184,183,179,175,344,331,314,304,290,277,530,383,373,366,10,652,346,171,168,164,318,309,299,287,276,263,513,375,368,362,6,648,322,316,312,307,302,292,284,269,261,512,376,370,364,359,4,620,300,296,294,288,282,273,266,515,380,374,369,365,361,357,2,1033,280,278,274,267,264,259,382,378,372,367,363,360,358,356,0,43,20,19,17,15,13,11,9,7,6,4,7,5,3,1,3],t32HB:[1,10,8,20,12,20,16,32,14,12,24,0,28,16,24,16],t33HB:[15,28,26,48,22,40,36,64,14,24,20,32,12,16,8,0],t1l:[1,4,3,5],t2l:[1,4,
+7,4,5,7,6,7,8],t3l:[2,3,7,4,4,7,6,7,8],t5l:[1,4,7,8,4,5,8,9,7,8,9,10,8,8,9,10],t6l:[3,4,6,8,4,4,6,7,5,6,7,8,7,7,8,9],t7l:[1,4,7,9,9,10,4,6,8,9,9,10,7,7,9,10,10,11,8,9,10,11,11,11,8,9,10,11,11,12,9,10,11,12,12,12],t8l:[2,4,7,9,9,10,4,4,6,10,10,10,7,6,8,10,10,11,9,10,10,11,11,12,9,9,10,11,12,12,10,10,11,11,13,13],t9l:[3,4,6,7,9,10,4,5,6,7,8,10,5,6,7,8,9,10,7,7,8,9,9,10,8,8,9,9,10,11,9,9,10,10,11,11],t10l:[1,4,7,9,10,10,10,11,4,6,8,9,10,11,10,10,7,8,9,10,11,12,11,11,8,9,10,11,12,12,11,12,9,10,11,12,
+12,12,12,12,10,11,12,12,13,13,12,13,9,10,11,12,12,12,13,13,10,10,11,12,12,13,13,13],t11l:[2,4,6,8,9,10,9,10,4,5,6,8,10,10,9,10,6,7,8,9,10,11,10,10,8,8,9,11,10,12,10,11,9,10,10,11,11,12,11,12,9,10,11,12,12,13,12,13,9,9,9,10,11,12,12,12,9,9,10,11,12,12,12,12],t12l:[4,4,6,8,9,10,10,10,4,5,6,7,9,9,10,10,6,6,7,8,9,10,9,10,7,7,8,8,9,10,10,10,8,8,9,9,10,10,10,11,9,9,10,10,10,11,10,11,9,9,9,10,10,11,11,12,10,10,10,11,11,11,11,12],t13l:[1,5,7,8,9,10,10,11,10,11,12,12,13,13,14,14,4,6,8,9,10,10,11,11,11,11,
+12,12,13,14,14,14,7,8,9,10,11,11,12,12,11,12,12,13,13,14,15,15,8,9,10,11,11,12,12,12,12,13,13,13,13,14,15,15,9,9,11,11,12,12,13,13,12,13,13,14,14,15,15,16,10,10,11,12,12,12,13,13,13,13,14,13,15,15,16,16,10,11,12,12,13,13,13,13,13,14,14,14,15,15,16,16,11,11,12,13,13,13,14,14,14,14,15,15,15,16,18,18,10,10,11,12,12,13,13,14,14,14,14,15,15,16,17,17,11,11,12,12,13,13,13,15,14,15,15,16,16,16,18,17,11,12,12,13,13,14,14,15,14,15,16,15,16,17,18,19,12,12,12,13,14,14,14,14,15,15,15,16,17,17,17,18,12,13,13,14,
+14,15,14,15,16,16,17,17,17,18,18,18,13,13,14,15,15,15,16,16,16,16,16,17,18,17,18,18,14,14,14,15,15,15,17,16,16,19,17,17,17,19,18,18,13,14,15,16,16,16,17,16,17,17,18,18,21,20,21,18],t15l:[3,5,6,8,8,9,10,10,10,11,11,12,12,12,13,14,5,5,7,8,9,9,10,10,10,11,11,12,12,12,13,13,6,7,7,8,9,9,10,10,10,11,11,12,12,13,13,13,7,8,8,9,9,10,10,11,11,11,12,12,12,13,13,13,8,8,9,9,10,10,11,11,11,11,12,12,12,13,13,13,9,9,9,10,10,10,11,11,11,11,12,12,13,13,13,14,10,9,10,10,10,11,11,11,11,12,12,12,13,13,14,14,10,10,10,
+11,11,11,11,12,12,12,12,12,13,13,13,14,10,10,10,11,11,11,11,12,12,12,12,13,13,14,14,14,10,10,11,11,11,11,12,12,12,13,13,13,13,14,14,14,11,11,11,11,12,12,12,12,12,13,13,13,13,14,15,14,11,11,11,11,12,12,12,12,13,13,13,13,14,14,14,15,12,12,11,12,12,12,13,13,13,13,13,13,14,14,15,15,12,12,12,12,12,13,13,13,13,14,14,14,14,14,15,15,13,13,13,13,13,13,13,13,14,14,14,14,15,15,14,15,13,13,13,13,13,13,13,14,14,14,14,14,15,15,15,15],t16_5l:[1,5,7,9,10,10,11,11,12,12,12,13,13,13,14,11,4,6,8,9,10,11,11,11,12,12,
+12,13,14,13,14,11,7,8,9,10,11,11,12,12,13,12,13,13,13,14,14,12,9,9,10,11,11,12,12,12,13,13,14,14,14,15,15,13,10,10,11,11,12,12,13,13,13,14,14,14,15,15,15,12,10,10,11,11,12,13,13,14,13,14,14,15,15,15,16,13,11,11,11,12,13,13,13,13,14,14,14,14,15,15,16,13,11,11,12,12,13,13,13,14,14,15,15,15,15,17,17,13,11,12,12,13,13,13,14,14,15,15,15,15,16,16,16,13,12,12,12,13,13,14,14,15,15,15,15,16,15,16,15,14,12,13,12,13,14,14,14,14,15,16,16,16,17,17,16,13,13,13,13,13,14,14,15,16,16,16,16,16,16,15,16,14,13,14,14,
+14,14,15,15,15,15,17,16,16,16,16,18,14,15,14,14,14,15,15,16,16,16,18,17,17,17,19,17,14,14,15,13,14,16,16,15,16,16,17,18,17,19,17,16,14,11,11,11,12,12,13,13,13,14,14,14,14,14,14,14,12],t16l:[1,5,7,9,10,10,11,11,12,12,12,13,13,13,14,10,4,6,8,9,10,11,11,11,12,12,12,13,14,13,14,10,7,8,9,10,11,11,12,12,13,12,13,13,13,14,14,11,9,9,10,11,11,12,12,12,13,13,14,14,14,15,15,12,10,10,11,11,12,12,13,13,13,14,14,14,15,15,15,11,10,10,11,11,12,13,13,14,13,14,14,15,15,15,16,12,11,11,11,12,13,13,13,13,14,14,14,14,
+15,15,16,12,11,11,12,12,13,13,13,14,14,15,15,15,15,17,17,12,11,12,12,13,13,13,14,14,15,15,15,15,16,16,16,12,12,12,12,13,13,14,14,15,15,15,15,16,15,16,15,13,12,13,12,13,14,14,14,14,15,16,16,16,17,17,16,12,13,13,13,13,14,14,15,16,16,16,16,16,16,15,16,13,13,14,14,14,14,15,15,15,15,17,16,16,16,16,18,13,15,14,14,14,15,15,16,16,16,18,17,17,17,19,17,13,14,15,13,14,16,16,15,16,16,17,18,17,19,17,16,13,10,10,10,11,11,12,12,12,13,13,13,13,13,13,13,10],t24l:[4,5,7,8,9,10,10,11,11,12,12,12,12,12,13,10,5,6,7,8,
+9,10,10,11,11,11,12,12,12,12,12,10,7,7,8,9,9,10,10,11,11,11,11,12,12,12,13,9,8,8,9,9,10,10,10,11,11,11,11,12,12,12,12,9,9,9,9,10,10,10,10,11,11,11,12,12,12,12,13,9,10,9,10,10,10,10,11,11,11,11,12,12,12,12,12,9,10,10,10,10,10,11,11,11,11,12,12,12,12,12,13,9,11,10,10,10,11,11,11,11,12,12,12,12,12,13,13,10,11,11,11,11,11,11,11,11,11,12,12,12,12,13,13,10,11,11,11,11,11,11,11,12,12,12,12,12,13,13,13,10,12,11,11,11,11,12,12,12,12,12,12,13,13,13,13,10,12,12,11,11,11,12,12,12,12,12,12,13,13,13,13,10,12,12,
+12,12,12,12,12,12,12,12,13,13,13,13,13,10,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,10,13,12,12,12,12,12,12,13,13,13,13,13,13,13,13,10,9,9,9,9,9,9,9,9,9,9,9,10,10,10,10,6],t32l:[1,5,5,7,5,8,7,9,5,7,7,9,7,9,9,10],t33l:[4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8]};r.ht=[new Q(0,0,null,null),new Q(2,0,r.t1HB,r.t1l),new Q(3,0,r.t2HB,r.t2l),new Q(3,0,r.t3HB,r.t3l),new Q(0,0,null,null),new Q(4,0,r.t5HB,r.t5l),new Q(4,0,r.t6HB,r.t6l),new Q(6,0,r.t7HB,r.t7l),new Q(6,0,r.t8HB,r.t8l),new Q(6,0,r.t9HB,r.t9l),new Q(8,
+0,r.t10HB,r.t10l),new Q(8,0,r.t11HB,r.t11l),new Q(8,0,r.t12HB,r.t12l),new Q(16,0,r.t13HB,r.t13l),new Q(0,0,null,r.t16_5l),new Q(16,0,r.t15HB,r.t15l),new Q(1,1,r.t16HB,r.t16l),new Q(2,3,r.t16HB,r.t16l),new Q(3,7,r.t16HB,r.t16l),new Q(4,15,r.t16HB,r.t16l),new Q(6,63,r.t16HB,r.t16l),new Q(8,255,r.t16HB,r.t16l),new Q(10,1023,r.t16HB,r.t16l),new Q(13,8191,r.t16HB,r.t16l),new Q(4,15,r.t24HB,r.t24l),new Q(5,31,r.t24HB,r.t24l),new Q(6,63,r.t24HB,r.t24l),new Q(7,127,r.t24HB,r.t24l),new Q(8,255,r.t24HB,r.t24l),
+new Q(9,511,r.t24HB,r.t24l),new Q(11,2047,r.t24HB,r.t24l),new Q(13,8191,r.t24HB,r.t24l),new Q(0,0,r.t32HB,r.t32l),new Q(0,0,r.t33HB,r.t33l)];r.largetbl=[65540,327685,458759,589832,655369,655370,720906,720907,786443,786444,786444,851980,851980,851980,917517,655370,262149,393222,524295,589832,655369,720906,720906,720907,786443,786443,786444,851980,917516,851980,917516,655370,458759,524295,589832,655369,720905,720906,786442,786443,851979,786443,851979,851980,851980,917516,917517,720905,589832,589832,
+655369,720905,720906,786442,786442,786443,851979,851979,917515,917516,917516,983052,983052,786441,655369,655369,720905,720906,786442,786442,851978,851979,851979,917515,917516,917516,983052,983052,983053,720905,655370,655369,720906,720906,786442,851978,851979,917515,851979,917515,917516,983052,983052,983052,1048588,786441,720906,720906,720906,786442,851978,851979,851979,851979,917515,917516,917516,917516,983052,983052,1048589,786441,720907,720906,786442,786442,851979,851979,851979,917515,917516,983052,
+983052,983052,983052,1114125,1114125,786442,720907,786443,786443,851979,851979,851979,917515,917515,983051,983052,983052,983052,1048588,1048589,1048589,786442,786443,786443,786443,851979,851979,917515,917515,983052,983052,983052,983052,1048588,983053,1048589,983053,851978,786444,851979,786443,851979,917515,917516,917516,917516,983052,1048588,1048588,1048589,1114125,1114125,1048589,786442,851980,851980,851979,851979,917515,917516,983052,1048588,1048588,1048588,1048588,1048589,1048589,983053,1048589,
+851978,851980,917516,917516,917516,917516,983052,983052,983052,983052,1114124,1048589,1048589,1048589,1048589,1179661,851978,983052,917516,917516,917516,983052,983052,1048588,1048588,1048589,1179661,1114125,1114125,1114125,1245197,1114125,851978,917517,983052,851980,917516,1048588,1048588,983052,1048589,1048589,1114125,1179661,1114125,1245197,1114125,1048589,851978,655369,655369,655369,720905,720905,786441,786441,786441,851977,851977,851977,851978,851978,851978,851978,655366];r.table23=[65538,262147,
+458759,262148,327684,458759,393222,458759,524296];r.table56=[65539,262148,458758,524296,262148,327684,524294,589831,458757,524294,589831,655368,524295,524295,589832,655369];r.bitrate_table=[[0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,-1],[0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,-1],[0,8,16,24,32,40,48,56,64,-1,-1,-1,-1,-1,-1,-1]];r.samplerate_table=[[22050,24E3,16E3,-1],[44100,48E3,32E3,-1],[11025,12E3,8E3,-1]];r.scfsi_band=[0,6,11,16,21];ma.Q_MAX=257;ma.Q_MAX2=116;ma.LARGE_BITS=1E5;
+ma.IXMAX_VAL=8206;var na={};na.SFBMAX=3*d.SBMAX_s;d.ENCDELAY=576;d.POSTDELAY=1152;d.MDCTDELAY=48;d.FFTOFFSET=224+d.MDCTDELAY;d.DECDELAY=528;d.SBLIMIT=32;d.CBANDS=64;d.SBPSY_l=21;d.SBPSY_s=12;d.SBMAX_l=22;d.SBMAX_s=13;d.PSFB21=6;d.PSFB12=6;d.BLKSIZE=1024;d.HBLKSIZE=d.BLKSIZE/2+1;d.BLKSIZE_s=256;d.HBLKSIZE_s=d.BLKSIZE_s/2+1;d.NORM_TYPE=0;d.START_TYPE=1;d.SHORT_TYPE=2;d.STOP_TYPE=3;d.MPG_MD_LR_LR=0;d.MPG_MD_LR_I=1;d.MPG_MD_MS_LR=2;d.MPG_MD_MS_I=3;d.fircoef=[-.1039435,-.1892065,-.0432472*5,-.155915,3.898045E-17,
+.0467745*5,.50455,.756825,.187098*5];da.MFSIZE=3456+d.ENCDELAY-d.MDCTDELAY;da.MAX_HEADER_BUF=256;da.MAX_BITS_PER_CHANNEL=4095;da.MAX_BITS_PER_GRANULE=7680;da.BPC=320;Ca.RIFF=Ob("RIFF");Ca.WAVE=Ob("WAVE");Ca.fmt_=Ob("fmt ");Ca.data=Ob("data");Ca.readHeader=function(d){var k=new Ca,q=d.getUint32(0,!1);if(Ca.RIFF==q&&(d.getUint32(4,!0),Ca.WAVE==d.getUint32(8,!1)&&Ca.fmt_==d.getUint32(12,!1))){var r=d.getUint32(16,!0),B=20;switch(r){case 16:case 18:k.channels=d.getUint16(B+2,!0);k.sampleRate=d.getUint32(B+
+4,!0);break;default:throw"extended fmt chunk not implemented";}for(var B=B+r,r=Ca.data,t=0;r!=q;){q=d.getUint32(B,!1);t=d.getUint32(B+4,!0);if(r==q)break;B+=t+8}k.dataLen=t;k.dataOffset=B+8;return k}};na.SFBMAX=3*d.SBMAX_s;this.Mp3Encoder=function(d,k,q){3!=arguments.length&&(console.error("WARN: Mp3Encoder(channels, samplerate, kbps) not specified"),d=1,k=44100,q=128);var r=new V,B=new Uc,t=new O,H=new ua,g=new Gc,b=new ma,l=new Oc,a=new ub,h=new Fc,D=new Xc,e=new Hc,n=new tb,c=new Vc,f=new Wc;r.setModules(t,
+H,g,b,l,a,h,D,f);H.setModules(t,f,h,a);D.setModules(H,h);g.setModules(r);l.setModules(H,e,b,n);b.setModules(n,e,r.enc.psy);e.setModules(H);n.setModules(b);a.setModules(r,H,h);B.setModules(c,f);c.setModules(h,D,g);var E=r.lame_init();E.num_channels=d;E.in_samplerate=k;E.brate=q;E.mode=ka.STEREO;E.quality=3;E.bWriteVbrTag=!1;E.disable_reservoir=!0;E.write_id3tag_automatic=!1;r.lame_init_params(E);var z=1152,w=0|1.25*z+7200,A=new Int8Array(w);this.encodeBuffer=function(a,b){1==d&&(b=a);a.length>z&&(z=
+a.length,w=0|1.25*z+7200,A=new Int8Array(w));var c=r.lame_encode_buffer(E,a,b,a.length,A,0,w);return new Int8Array(A.subarray(0,c))};this.flush=function(){var a=r.lame_encode_flush(E,A,0,w);return new Int8Array(A.subarray(0,a))}};this.WavHeader=Ca};
diff --git a/form/src/assets/libs/audio-recorder/recorder.js b/form/src/assets/libs/audio-recorder/recorder.js
new file mode 100644
index 0000000000000000000000000000000000000000..fba724169978e87c9c776d977d90c2fe4fe11431
--- /dev/null
+++ b/form/src/assets/libs/audio-recorder/recorder.js
@@ -0,0 +1,141 @@
+(function (exports) {
+  //公共方法
+  var Util = {
+    //初始化
+    init: function () {
+      navigator.getUserMedia = navigator.getUserMedia ||
+        navigator.webkitGetUserMedia ||
+        navigator.mozGetUserMedia ||
+        navigator.msGetUserMedia;
+
+      window.AudioContext = window.AudioContext ||
+        window.webkitAudioContext;
+    },
+    //日志
+    log: function () {
+      console.log.apply(console, arguments);
+    }
+  };
+  //构造函数
+  var Recorder = function (config) {
+
+    var _this = this;
+    config = config || {}; //初始化配置对象
+    config.sampleRate = config.sampleRate || 44100; //采样频率,默认为44100Hz(标准MP3采样率)
+    config.bitRate = config.bitRate || 128; //比特率,默认为128kbps(标准MP3质量)
+
+    Util.init();
+
+    if (navigator.getUserMedia) {
+      navigator.getUserMedia({
+          audio: true //配置对象
+        },
+        function (stream) { //成功回调
+          var context = new AudioContext(),
+            microphone = context.createMediaStreamSource(stream), //媒体流音频源
+            processor = context.createScriptProcessor(16384, 1, 1), //js音频处理器
+            successCallback, errorCallback;
+
+          config.sampleRate = context.sampleRate;
+
+          processor.onaudioprocess = function (event) {
+            //监听音频录制过程
+            var array = event.inputBuffer.getChannelData(0);
+            realTimeWorker.postMessage({cmd: 'encode', buf: array});
+          };
+
+          var realTimeWorker = new Worker('assets/libs/audio-recorder/worker.js'); //开启后台线程
+          _this.worker = realTimeWorker;
+          realTimeWorker.onmessage = function (e) { //主线程监听后台线程,实时通信
+            switch (e.data.cmd) {
+              case 'init':
+                Util.log('初始化成功');
+                if (config.success) {
+                  config.success();
+                }
+                break;
+              case 'end':
+                if (successCallback) {
+                  var blob = new Blob(e.data.buf, {type: 'audio/mp3'});
+                  successCallback(blob);
+                  Util.log('MP3大小:' + blob.size + '%cB', 'color:#0000EE');
+                }
+                break;
+              case 'error':
+                Util.log('错误信息:' + e.data.error);
+                if (errorCallback) {
+                  errorCallback(e.data.error);
+                }
+                break;
+              default:
+                Util.log('未知信息:' + e.data);
+            }
+          };
+          //接口列表
+          //开始录音
+          _this.start = function () {
+            if (processor && microphone) {
+              microphone.connect(processor);
+              processor.connect(context.destination);
+              Util.log('开始录音');
+            }
+          };
+          //结束录音
+          _this.stop = function () {
+            if (processor && microphone) {
+              microphone.disconnect();
+              processor.disconnect();
+              Util.log('录音结束');
+            }
+          };
+          //获取blob格式录音文件
+          _this.getBlob = function (onSuccess, onError) {
+            successCallback = onSuccess;
+            errorCallback = onError;
+            realTimeWorker.postMessage({cmd: 'finish'});
+          };
+
+          realTimeWorker.postMessage({
+            cmd: 'init',
+            config: {
+              sampleRate: config.sampleRate,
+              bitRate: config.bitRate
+            }
+          });
+        },
+        function (error) { //失败回调
+          var msg;
+          switch (error.code || error.name) {
+            case 'PermissionDeniedError':
+            case 'PERMISSION_DENIED':
+            case 'NotAllowedError':
+              msg = '用户拒绝访问麦克风';
+              break;
+            case 'NOT_SUPPORTED_ERROR':
+            case 'NotSupportedError':
+              msg = '浏览器不支持麦克风';
+              break;
+            case 'MANDATORY_UNSATISFIED_ERROR':
+            case 'MandatoryUnsatisfiedError':
+              msg = '找不到麦克风设备';
+              break;
+            default:
+              msg = '无法打开麦克风,异常信息:' + (error.code || error.name);
+              break;
+          }
+          Util.log(msg);
+          if (config.error) {
+            config.error(msg);
+          }
+        });
+    } else {
+      Util.log('当前浏览器不支持录音功能');
+      if (config.fix) {
+        config.fix('当前浏览器不支持录音功能');
+      }
+    }
+
+  };
+  //模块接口
+  exports.Recorder = Recorder;
+})(window);
diff --git a/form/src/assets/libs/audio-recorder/worker.js b/form/src/assets/libs/audio-recorder/worker.js
new file mode 100644
index 0000000000000000000000000000000000000000..41e7c93988ef1bbbf49a3115fc61e9849149c44e
--- /dev/null
+++ b/form/src/assets/libs/audio-recorder/worker.js
@@ -0,0 +1,108 @@
+(function () {
+  'use strict';
+  if (!importScripts) {
+    var importScripts = (function (globalEval) {
+      var xhr = new XMLHttpRequest;
+      return function importScripts() {
+        var
+          args = Array.prototype.slice.call(arguments)
+          , len = args.length
+          , i = 0
+          , meta
+          , data
+          , content
+        ;
+        for (; i < len; i++) {
+          if (args[i].substr(0, 5).toLowerCase() === "data:") {
+            data = args[i];
+            content = data.indexOf(",");
+            meta = data.substr(5, content).toLowerCase();
+            data = decodeURIComponent(data.substr(content + 1));
+            if (/;\s*base64\s*[;,]/.test(meta)) {
+              data = atob(data);
+            }
+            if (/;\s*charset=[uU][tT][fF]-?8\s*[;,]/.test(meta)) {
+              data = decodeURIComponent(escape(data));
+            }
+          } else {
+            xhr.open("GET", args[i], false);
+            xhr.send(null);
+            data = xhr.responseText;
+          }
+          globalEval(data);
+        }
+      };
+    }(eval));
+  }
+
+
+  importScripts('assets/libs/audio-recorder/lame.min.js');
+
+  var mp3Encoder, maxSamples = 1152, samplesMono, lame, config, dataBuffer;
+
+  var clearBuffer = function () {
+    dataBuffer = [];
+  };
+
+  var appendToBuffer = function (mp3Buf) {
+    dataBuffer.push(new Int8Array(mp3Buf));
+  };
+
+  var init = function (prefConfig) {
+    config = prefConfig || {};
+    lame = new lamejs();
+    mp3Encoder = new lame.Mp3Encoder(1, config.sampleRate || 44100, config.bitRate || 128);
+    clearBuffer();
+    self.postMessage({
+      cmd: 'init'
+    });
+  };
+
+  var floatTo16BitPCM = function (input, output) {
+    for (var i = 0; i < input.length; i++) {
+      var s = Math.max(-1, Math.min(1, input[i]));
+      output[i] = (s < 0 ? s * 0x8000 : s * 0x7FFF);
+    }
+  };
+
+  var convertBuffer = function (arrayBuffer) {
+    var data = new Float32Array(arrayBuffer);
+    var out = new Int16Array(arrayBuffer.length);
+    floatTo16BitPCM(data, out);
+    return out;
+  };
+
+  var encode = function (arrayBuffer) {
+    samplesMono = convertBuffer(arrayBuffer);
+    var remaining = samplesMono.length;
+    for (var i = 0; remaining >= 0; i += maxSamples) {
+      var left = samplesMono.subarray(i, i + maxSamples);
+      var mp3buf = mp3Encoder.encodeBuffer(left);
+      appendToBuffer(mp3buf);
+      remaining -= maxSamples;
+    }
+  };
+
+  var finish = function () {
+    appendToBuffer(mp3Encoder.flush());
+    self.postMessage({
+      cmd: 'end',
+      buf: dataBuffer
+    });
+    clearBuffer();
+  };
+
+  self.onmessage = function (e) {
+    switch (e.data.cmd) {
+      case 'init':
+        init(e.data.config);
+        break;
+      case 'encode':
+        encode(e.data.buf);
+        break;
+      case 'finish':
+        finish();
+        break;
+    }
+  };
+})();
diff --git a/form/src/assets/libs/service-worker/cache-service.js b/form/src/assets/libs/service-worker/cache-service.js
new file mode 100644
index 0000000000000000000000000000000000000000..d9111ae6c9fe6486339ab52c4df819a9e0b42e0d
--- /dev/null
+++ b/form/src/assets/libs/service-worker/cache-service.js
@@ -0,0 +1,535 @@
+/*
+ Copyright 2014 Google Inc. All Rights Reserved.
+ 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.
+ */
+
+// While overkill for this specific sample in which there is only one cache,
+// this is one best practice that can be followed in general to keep track of
+// multiple caches used by a given service worker, and keep them all versioned.
+// It maps a shorthand identifier for a cache to a specific, versioned cache name.
+
+// Note that since global state is discarded in between service worker restarts, these
+// variables will be reinitialized each time the service worker handles an event, and you
+// should not attempt to change their values inside an event handler. (Treat them as constants.)
+
+// If at any point you want to force pages that use this service worker to start using a fresh
+// cache, then increment the CACHE_VERSION value. It will kick off the service worker update
+// flow and the old cache(s) will be purged as part of the activate event handler when the
+// updated service worker is activated.
+var CACHE_SID = 0;
+var CACHE_VER = '';
+var isPlayerPage = false;
+// var CURRENT_CACHES = {};
+// var CURRENT_CACHES = null;
+
+async function progressDownloader(urlOrRequest, port, sid) {
+  let url = urlOrRequest;
+  let response = await fetch(url);
+  if (urlOrRequest instanceof Request) {
+    url = urlOrRequest.url;
+  }
+  let isBin = false;
+  if (url.endsWith('.mp4')
+    || url.endsWith('.mp3')
+    || url.endsWith('.jpg')
+    || url.endsWith('.png')){
+    isBin = true;
+  }
+
+  const reader = response.body.getReader();
+  const contentLength = +response.headers.get('Content-Length');
+  port.postMessage({
+    len: contentLength,
+    status: 'init',
+    sid
+  });
+  let receivedLength = 0;
+  let mb = 0;
+  let timeS = new Date().getTime();
+  let timeD = 0;
+  let speed = 0;
+  let chunks = []; // array of received binary chunks (comprises the body)
+  while(true) {
+    const {done, value} = await reader.read();
+    if (done) {
+      break;
+    }
+    chunks.push(value);
+    receivedLength += value.length;
+    /*mb += value.length;
+    if(mb > 1000 * 1000) {
+      timeD = new Date().getTime() - timeS;
+      // timeD = timeD /1000;
+      speed = mb / timeD;
+      mb = 0;
+      timeS = new Date().getTime();
+    }*/
+    // console.log(`Received ${receivedLength} of ${contentLength}`)
+    // cb && cb(receivedLength , contentLength)
+    port.postMessage({
+      loaded: value.length,
+      status: 'downloading',
+      sid
+    });
+  }
+
+  if (isBin) {
+    return new Blob(chunks);
+  } else {
+    let chunksAll = new Uint8Array(receivedLength);
+    let position = 0;
+    for(let chunk of chunks) {
+      chunksAll.set(chunk, position);
+      position += chunk.length;
+    }
+    return chunksAll; //new TextDecoder("utf-8").decode(chunksAll);
+  }
+
+}
+self.addEventListener('install', function(event) {
+  event.waitUntil(self.skipWaiting()); // Activate worker immediately
+});
+
+self.addEventListener('activate', function(event) {
+  event.waitUntil(self.clients.claim({
+    includeUncontrolled: true
+  }).then(function() {
+    // After the activation and claiming is complete, send a message to each of the controlled
+    // pages letting it know that it's active.
+    // This will trigger navigator.serviceWorker.onmessage in each client.
+    return self.clients.matchAll().then(function(clients) {
+      return Promise.all(clients.map(function(client) {
+        // return client.postMessage('The service worker has activated and ' +
+        //   'taken control.');
+        return client.postMessage({
+          name: 'initOk',
+        });
+
+      }));
+    });
+  })); // Become available to all pages
+});
+/*
+self.addEventListener('activate', function(event) {
+  // Delete all caches that aren't named in CURRENT_CACHES.
+  // While there is only one cache in this example, the same logic will handle the case where
+  // there are multiple versioned caches.
+  // var expectedCacheNames = Object.keys(CURRENT_CACHES).map(function(key) {
+  //   return CURRENT_CACHES[key];
+  // });
+
+  event.waitUntil(
+    caches.keys().then(function(cacheNames) {
+      // return Promise.all(
+      //   cacheNames.map(function(cacheName) {
+      //     if (expectedCacheNames.indexOf(cacheName) === -1) {
+      //       // If this cache name isn't present in the array of "expected" cache names, then delete it.
+      //       console.log('Deleting out of date cache:', cacheName);
+      //       return caches.delete(cacheName);
+      //     }
+      //   })
+      // );
+    }).then(function() {
+      return clients.claim();
+    }).then(function() {
+      // After the activation and claiming is complete, send a message to each of the controlled
+      // pages letting it know that it's active.
+      // This will trigger navigator.serviceWorker.onmessage in each client.
+      return self.clients.matchAll().then(function(clients) {
+        return Promise.all(clients.map(function(client) {
+          return client.postMessage('The service worker has activated and ' +
+            'taken control.');
+        }));
+      });
+    })
+  );
+});*/
+
+self.addEventListener('message', function(event) {
+  // console.log('Handling message event:', event);
+  if (event.data.command === 'remove') {
+    if (!event.data.key) {
+      return;
+    }
+    caches.delete(event.data.key)
+    /*caches.keys().then(function(cacheNames) {
+      console.log('clear', cacheNames);
+      // return caches.delete(event.data.ver);
+      // CACHE_VER = event.data.newVer;
+      // CACHE_SID = event.data.sid;
+      if (!event.data.key) {
+        return;
+      }
+      return caches.delete(event.data.key);
+      return Promise.all(
+        cacheNames.map(function(cacheName) {
+          if ( cache_ver === cacheName) {
+            // If this cache name isn't present in the array of "expected" cache names, then delete it.
+            console.log('Deleting out of date cache:', cacheName);
+            // localStorage.removeItem('item-cache-'+event.data.sid);
+            // self.clients.matchAll().then(function(clients) {
+            //   clients.forEach(function(client) {
+            //     client.postMessage({
+            //
+            //     });
+            //   });
+            // });
+            console.log('delete', cacheName)
+            return caches.delete(cacheName);
+          }
+        })
+      );
+    })*/
+      /*.then(function() {
+      return clients.claim();
+    })*/
+      /*.then(function() {
+      // After the activation and claiming is complete, send a message to each of the controlled
+      // pages letting it know that it's active.
+      // This will trigger navigator.serviceWorker.onmessage in each client.
+      return self.clients.matchAll().then(function(clients) {
+        return Promise.all(clients.map(function(client) {
+          return client.postMessage('The service worker has activated and ' +
+            'taken control.');
+        }));
+      });
+    })*/
+      .then(function() {
+        console.log('delete finished')
+      event.ports[0].postMessage({
+        error: null
+      });
+    });
+    return;
+  }
+  if (event.data.command === 'clearAll') {
+    event.waitUntil(
+      caches.keys().then(function(cacheNames) {
+        return Promise.all(
+          cacheNames.map(function(cacheName) {
+            return caches.delete(cacheName);
+          })
+        );
+      }).then(function() {
+        event.ports[0].postMessage({
+          error: null
+        });
+      }).catch(err => {
+        event.ports[0].postMessage({
+          error: err,
+        });
+      })
+    );
+    return;
+  }
+  if (event.data.command === 'listCachedCoursewares') {
+    var cacheData = {}
+    // caches.open('item-cache-c965d3a0630911e994fa058136a96bb1-1556518161000').then(r=>{console.log(r.keys().then(items=>console.log(items[0])))})
+    caches.keys().then((cacheNames) => {
+      cacheNames.map((cacheName) => {
+        if (cacheName.indexOf('item-cache') > -1) {
+          var size = 0;
+          caches.open(cacheName).then((cache) => {
+            console.log(cacheName)
+            cache.keys().then((requests) => {
+              console.log(requests[0])
+              for(var req of requests) {
+                console.log(req)
+                cache.match(req.url).then(resp => {
+                  console.log(resp.headers.entries())
+                })
+              //   cache.match(req.url).then((resp) => {
+              //     console.log(req.url, resp.length)
+              //     size += parseInt(resp.headers['Content-Length'])
+              //   }).catch(err => {
+              //     console.log(err)
+              //   });
+              }
+            }).then(() => {
+              cacheData.cacheName = size;
+            })
+          });
+
+        }
+      });
+    }).then(function(result) {
+      // event.ports[0].postMessage({
+      //   error: null,
+      //   urls: urls
+      // });
+    });
+    return;
+  }
+
+  // var key = 'item-cache-' + event.data.sid + '-'+ event.data.ver;
+  var key = event.data.key;
+  var p = caches.open(key).then(function(cache) {
+    // throw {name: 'QuotaExceededError'}
+    switch (event.data.command) {
+      // This command returns a list of the URLs corresponding to the Request objects
+      // that serve as keys for the current cache.
+      case 'keys':
+        return cache.keys().then(function(requests) {
+          var urls = requests.map(function(request) {
+            return request.url;
+          });
+
+          return urls.sort();
+        }).then(function(urls) {
+          // event.ports[0] corresponds to the MessagePort that was transferred as part of the controlled page's
+          // call to controller.postMessage(). Therefore, event.ports[0].postMessage() will trigger the onmessage
+          // handler from the controlled page.
+          // It's up to you how to structure the messages that you send back; this is just one example.
+          event.ports[0].postMessage({
+            error: null,
+            urls: urls
+          });
+        });
+
+      // This command adds a new request/response pair to the cache.
+      case 'add':
+        // If event.data.url isn't a valid URL, new Request() will throw a TypeError which will be handled
+        // by the outer .catch().
+        // Hardcode {mode: 'no-cors} since the default for new Requests constructed from strings is to require
+        // CORS, and we don't have any way of knowing whether an arbitrary URL that a user entered supports CORS.
+        var request = new Request(event.data.url, {mode: 'cors', cache: 'reload'}); //  {mode: 'no-cors'}
+        // console.log('start', event.data.url);
+        // return fetch(request)
+        //   .then(function(res){
+        //     return res.arrayBuffer();
+        //   })
+          const startTime = new Date().getTime();
+          let len = 0;
+          return progressDownloader(request, event.ports[0], event.data.sid).then((ab) => {
+            var cl = ab.size ? ab.size : ab.byteLength;
+            len = cl;
+            return cache.put(event.data.url, new Response(ab, {
+              // url: event.data.url,
+              // type: 'cors',
+              status: 200,
+              statusText: 'OK',
+              headers: [
+                ['Content-Length', cl+''],
+              ]
+            }));
+          }).then(function(r) {
+            event.ports[0].postMessage({
+              error: null,
+              status: 'finished',
+              len: len,
+              time: new Date().getTime() - startTime
+            });
+          });
+
+      // This command removes a request/response pair from the cache (assuming it exists).
+      case 'delete':
+        return cache.delete(event.data.url).then(function(success) {
+          event.ports[0].postMessage({
+            error: success ? null : 'Item was not found in the cache.'
+          });
+        });
+
+      default:
+        // This will be handled by the outer .catch().
+        throw Error('Unknown command: ' + event.data.command);
+    }
+  }).catch(function(error) {
+    // If the promise rejects, handle it by returning a standardized error message to the controlled page.
+    console.log('Message handling failed:', event.data.url, error);
+    // if (error.name == 'QuotaExceededError') {
+      event.ports[0].postMessage({
+        //error: {name: 'QuotaExceededError'}
+        error: {name: error.name, message: error.message},
+        status: 'error'
+      });
+    // }
+  });
+
+  // Beginning in Chrome 51, event is an ExtendableMessageEvent, which supports
+  // the waitUntil() method for extending the lifetime of the event handler
+  // until the promise is resolved.
+  if ('waitUntil' in event) {
+    event.waitUntil(p);
+  }
+
+  // Without support for waitUntil(), there's a chance that if the promise chain
+  // takes "too long" to execute, the service worker might be automatically
+  // stopped before it's complete.
+});
+
+
+self.addEventListener('fetch', function(event) {
+
+  // console.log('Handling fetch event for', event.request.method, event.request.url);
+  // if (event.request.method !== 'GET' || !CACHE_SID || !CACHE_VER) {
+  //   event.respondWith(function(){
+  //     return fetch(request).then(networkResponse => {
+  //       return networkResponse;
+  //     }).catch(_ => {
+  //       console.error('direct Fetching failed:', error);
+  //
+  //       throw error;
+  //     })
+  //   });
+  //
+  //   return;
+  // }
+
+  /*if (!CACHE_SID || !CACHE_VER) {
+    event.request.mode = 'no-cors';
+    return fetch(event.request).then(function(response) {
+      // console.log('Response from network is:', response);
+      console.log(999)
+      return response;
+    }).catch(function(error) {
+      // This catch() will handle exceptions thrown from the fetch() operation.
+      // Note that a HTTP error response (e.g. 404) will NOT trigger an exception.
+      // It will return a normal response object that has the appropriate error code set.
+      console.error('Fetching failed 9:', error);
+
+      throw error;
+    });
+  }*/
+  // var isNormReq = !CACHE_SID || !CACHE_VER;
+  // var key = 'item-cache-' + CACHE_SID + '-'+ CACHE_VER;
+  if (event.request.method == 'GET' ) {
+    if (event.request.headers.get('range')) {
+      var pos =
+        Number(/^bytes\=(\d+)\-$/g.exec(event.request.headers.get('range'))[1]);
+      // console.log('Range request for', event.request.url,
+      //   ', starting position:', pos);
+      event.respondWith(
+        caches.match(event.request.url, {
+          ignoreSearch: true,
+          ignoreVary: true
+        }).then(function(response) {
+          if (response) {
+            console.log('Found response in cache:', event.request.url);
+            return response.arrayBuffer().then(ab => {
+              return new Response(
+                ab.slice(pos),
+                {
+                  status: 206,
+                  statusText: 'Partial Content',
+                  headers: [
+                    ['Content-Length', ab.byteLength+''],
+                    ['Content-Range', 'bytes ' + pos + '-' +
+                    (ab.byteLength - 1) + '/' + ab.byteLength]]
+                });
+            });
+
+            // return response;
+          }
+          // event.request.mode = 'cors';
+          // event.request.cache = 'reload';
+          var request = new Request(event.request.url,
+            {
+              method: event.request.method,
+              mode: 'cors',
+              cache: 'reload',
+              headers: event.request.headers
+            });
+          return fetch(request).then(function(response) {
+            console.log('Response from network is:', event.request.url);
+            return response;
+          })/*.then(res => {
+            return res.arrayBuffer();
+          }).then(ab => {
+            return new Response(
+              ab.slice(pos),
+              {
+                status: 206,
+                statusText: 'Partial Content',
+                headers: [
+                  ['Content-Length', ab.byteLength+''],
+                  ['Content-Range', 'bytes ' + pos + '-' +
+                  (ab.byteLength - 1) + '/' + (pos + ab.byteLength)]]
+              });
+          })*/
+        })
+        /*caches.open(key)
+          .then(function(cache) {
+            return cache.match(event.request.url);
+          }).then(function(res) {
+          if (!res) {
+            return fetch(event.request)
+              .then(res => {
+                return res.arrayBuffer();
+              });
+          }
+          return res.arrayBuffer();
+        }).then(function(ab) {
+          return new Response(
+            ab.slice(pos),
+            {
+              status: 206,
+              statusText: 'Partial Content',
+              headers: [
+                ['Content-Length', ab.byteLength+''],
+                ['Content-Range', 'bytes ' + pos + '-' +
+                (ab.byteLength - 1) + '/' + ab.byteLength]]
+            });
+        })*/
+      );
+    } else {
+      // console.log('Non-range request for', event.request.url);
+      event.respondWith(
+        // caches.match() will look for a cache entry in all of the caches available to the service worker.
+        // It's an alternative to first opening a specific named cache and then matching on that.
+        caches.match(event.request, {
+          ignoreSearch: true,
+          ignoreVary: true
+        }).then(function(response) {
+          if (response) {
+            // console.log('%c Response in cache:', 'color:DodgerBlue;', response);
+            return response;
+          }
+
+          // event.request will always have the proper mode set ('cors, 'no-cors', etc.) so we don't
+          // have to hardcode 'no-cors' like we do when fetch()ing in the install handler.
+          // event.request.mode = 'cors';
+          // cache: 'reload'
+          if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') {
+            return;
+          }
+          // console.log('No response found in cache.', event.request.url);
+          return fetch(event.request).then(function(response) {
+            // console.log('%c Response from network is:', 'color:#bada55', response);
+
+            return response;
+          }).catch(function(error) {
+            // This catch() will handle exceptions thrown from the fetch() operation.
+            // Note that a HTTP error response (e.g. 404) will NOT trigger an exception.
+            // It will return a normal response object that has the appropriate error code set.
+            console.error('Fetching failed:', error);
+
+            // throw error;
+          });
+        })
+      );
+    }
+  } else {
+    event.respondWith(
+      fetch(event.request).then(function(response) {
+        // console.log('not get from network:', response);
+        // console.log(888)
+        return response;
+      }).catch(function(error) {
+        // This catch() will handle exceptions thrown from the fetch() operation.
+        // Note that a HTTP error response (e.g. 404) will NOT trigger an exception.
+        // It will return a normal response object that has the appropriate error code set.
+        console.error('Fetching failed 8:', error);
+        throw error;
+      })
+    );
+  }
+});
+
diff --git a/form/src/assets/libs/video-js-7.0.0/video-js.css b/form/src/assets/libs/video-js-7.0.0/video-js.css
new file mode 100644
index 0000000000000000000000000000000000000000..aff41ca123d0a129efcdd11456356eee91347973
--- /dev/null
+++ b/form/src/assets/libs/video-js-7.0.0/video-js.css
@@ -0,0 +1,1699 @@
+.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,d09GRgABAAAAABBIAAsAAAAAGoQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADsAAABUIIslek9TLzIAAAFEAAAAPgAAAFZRiV3RY21hcAAAAYQAAADQAAADIjn098ZnbHlmAAACVAAACv4AABEIAwnSw2hlYWQAAA1UAAAAKgAAADYUHzoRaGhlYQAADYAAAAAbAAAAJA4DByFobXR4AAANnAAAAA8AAACE4AAAAGxvY2EAAA2sAAAARAAAAEQ9NEHGbWF4cAAADfAAAAAfAAAAIAEyAIFuYW1lAAAOEAAAASUAAAIK1cf1oHBvc3QAAA84AAABDwAAAZ5AAl/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+DwalTqIAAHicY2BkYGAA4uByr8R4fpuvDNzsDCBw7f/3LmSanREszsHABKIAKi0J7gAAeJxjYGRgYGcAARD5/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 {
+  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 {
+  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.5em;
+  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);
+  -webkit-border-radius: 0.3em;
+  -moz-border-radius: 0.3em;
+  border-radius: 0.3em;
+  -webkit-transition: all 0.4s;
+  -moz-transition: all 0.4s;
+  -ms-transition: all 0.4s;
+  -o-transition: all 0.4s;
+  transition: all 0.4s;
+}
+
+.vjs-big-play-centered .vjs-big-play-button {
+  top: 50%;
+  left: 50%;
+  margin-top: -0.75em;
+  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);
+  -webkit-transition: all 0s;
+  -moz-transition: all 0s;
+  -ms-transition: all 0s;
+  -o-transition: all 0s;
+  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: -webkit-linear-gradient(-90deg, rgba(0, 0, 0, 0.8), rgba(255, 255, 255, 0));
+  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 {
+  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 {
+  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-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 {
+  -webkit-transition: all 0.4s;
+  -moz-transition: all 0.4s;
+  -ms-transition: all 0.4s;
+  -o-transition: all 0.4s;
+  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;
+  -webkit-transition: all 0.4s;
+  -moz-transition: all 0.4s;
+  -ms-transition: all 0.4s;
+  -o-transition: all 0.4s;
+  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: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  display: flex;
+  visibility: visible;
+  opacity: 1;
+  -webkit-transition: visibility 0.1s, opacity 0.1s;
+  -moz-transition: visibility 0.1s, opacity 0.1s;
+  -ms-transition: visibility 0.1s, opacity 0.1s;
+  -o-transition: visibility 0.1s, opacity 0.1s;
+  transition: visibility 0.1s, opacity 0.1s;
+}
+
+.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar {
+  visibility: visible;
+  opacity: 0;
+  -webkit-transition: visibility 1s, opacity 1s;
+  -moz-transition: visibility 1s, opacity 1s;
+  -ms-transition: visibility 1s, opacity 1s;
+  -o-transition: visibility 1s, opacity 1s;
+  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;
+  -webkit-box-flex: none;
+  -moz-box-flex: none;
+  -webkit-flex: none;
+  -ms-flex: none;
+  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;
+  -webkit-box-flex: auto;
+  -moz-box-flex: auto;
+  -webkit-flex: auto;
+  -ms-flex: auto;
+  flex: auto;
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-box-align: center;
+  -webkit-align-items: center;
+  -ms-flex-align: center;
+  align-items: center;
+  min-width: 4em;
+}
+
+.video-js .vjs-progress-control.disabled {
+  cursor: default;
+}
+
+.vjs-live .vjs-progress-control {
+  display: none;
+}
+
+.vjs-no-flex .vjs-progress-control {
+  width: auto;
+}
+
+.video-js .vjs-progress-holder {
+  -webkit-box-flex: auto;
+  -moz-box-flex: auto;
+  -webkit-flex: auto;
+  -ms-flex: auto;
+  flex: auto;
+  -webkit-transition: all 0.2s;
+  -moz-transition: all 0.2s;
+  -ms-transition: all 0.2s;
+  -o-transition: all 0.2s;
+  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);
+  -webkit-border-radius: 0.3em;
+  -moz-border-radius: 0.3em;
+  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: relative;
+  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;
+  -webkit-transition: visibility 1s, opacity 1s;
+  -moz-transition: visibility 1s, opacity 1s;
+  -ms-transition: visibility 1s, opacity 1s;
+  -o-transition: visibility 1s, opacity 1s;
+  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 */
+  -khtml-user-select: none;
+  /* 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;
+  -webkit-box-shadow: 0 0 1em #fff;
+  -moz-box-shadow: 0 0 1em #fff;
+  box-shadow: 0 0 1em #fff;
+}
+
+.video-js .vjs-mute-control {
+  cursor: pointer;
+  -webkit-box-flex: none;
+  -moz-box-flex: none;
+  -webkit-flex: none;
+  -ms-flex: none;
+  flex: none;
+}
+
+.video-js .vjs-volume-control {
+  cursor: pointer;
+  margin-right: 1em;
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  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 {
+  -webkit-transition: width 1s;
+  -moz-transition: width 1s;
+  -ms-transition: width 1s;
+  -o-transition: width 1s;
+  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;
+  -webkit-transition: visibility 0.1s, opacity 0.1s, height 0.1s, width 0.1s, left 0s, top 0s;
+  -moz-transition: visibility 0.1s, opacity 0.1s, height 0.1s, width 0.1s, left 0s, top 0s;
+  -ms-transition: visibility 0.1s, opacity 0.1s, height 0.1s, width 0.1s, left 0s, top 0s;
+  -o-transition: visibility 0.1s, opacity 0.1s, height 0.1s, width 0.1s, left 0s, top 0s;
+  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.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;
+  -webkit-transition: width 0.1s;
+  -moz-transition: width 0.1s;
+  -ms-transition: width 0.1s;
+  -o-transition: width 0.1s;
+  transition: width 0.1s;
+}
+
+.video-js .vjs-volume-panel .vjs-volume-control.vjs-volume-vertical {
+  height: 8em;
+  width: 3em;
+  left: -3.5em;
+  -webkit-transition: visibility 1s, opacity 1s, height 1s 1s, width 1s 1s, left 1s 1s, top 1s 1s;
+  -moz-transition: visibility 1s, opacity 1s, height 1s 1s, width 1s 1s, left 1s 1s, top 1s 1s;
+  -ms-transition: visibility 1s, opacity 1s, height 1s 1s, width 1s 1s, left 1s 1s, top 1s 1s;
+  -o-transition: visibility 1s, opacity 1s, height 1s 1s, width 1s 1s, left 1s 1s, top 1s 1s;
+  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 {
+  -webkit-transition: visibility 1s, opacity 1s, height 1s 1s, width 1s, left 1s 1s, top 1s 1s;
+  -moz-transition: visibility 1s, opacity 1s, height 1s 1s, width 1s, left 1s 1s, top 1s 1s;
+  -ms-transition: visibility 1s, opacity 1s, height 1s 1s, width 1s, left 1s 1s, top 1s 1s;
+  -o-transition: visibility 1s, opacity 1s, height 1s 1s, width 1s, left 1s 1s, top 1s 1s;
+  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;
+  -webkit-transition: none;
+  -moz-transition: none;
+  -ms-transition: none;
+  -o-transition: none;
+  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: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  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: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-box-align: flex-start;
+  -webkit-align-items: flex-start;
+  -ms-flex-align: flex-start;
+  align-items: flex-start;
+  -webkit-box-flex: auto;
+  -moz-box-flex: auto;
+  -webkit-flex: auto;
+  -ms-flex: auto;
+  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-time-control {
+  -webkit-box-flex: none;
+  -moz-box-flex: none;
+  -webkit-flex: none;
+  -ms-flex: none;
+  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 .vjs-icon-placeholder {
+  cursor: pointer;
+  -webkit-box-flex: none;
+  -moz-box-flex: none;
+  -webkit-flex: none;
+  -ms-flex: none;
+  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 {
+  -moz-transform: translateY(-3em);
+  -ms-transform: translateY(-3em);
+  -o-transform: translateY(-3em);
+  -webkit-transform: translateY(-3em);
+  transform: translateY(-3em);
+}
+
+.video-js.vjs-user-inactive.vjs-playing video::-webkit-media-text-track-display {
+  -moz-transform: translateY(-1.5em);
+  -ms-transform: translateY(-1.5em);
+  -o-transform: translateY(-1.5em);
+  -webkit-transform: translateY(-1.5em);
+  transform: translateY(-1.5em);
+}
+
+.video-js .vjs-fullscreen-control {
+  cursor: pointer;
+  -webkit-box-flex: none;
+  -moz-box-flex: none;
+  -webkit-flex: none;
+  -ms-flex: none;
+  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;
+  animation: 0s linear 0.3s forwards vjs-spinner-show;
+}
+
+.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% {
+    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 {
+  position: absolute;
+}
+
+.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-layout-tiny:not(.vjs-fullscreen) .vjs-custom-control-spacer {
+  -webkit-box-flex: auto;
+  -moz-box-flex: auto;
+  -webkit-flex: auto;
+  -ms-flex: auto;
+  flex: auto;
+}
+
+.video-js.vjs-layout-tiny:not(.vjs-fullscreen).vjs-no-flex .vjs-custom-control-spacer {
+  width: auto;
+}
+
+.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-current-time, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-time-divider, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-duration, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-remaining-time,
+.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-playback-rate, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-progress-control,
+.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-mute-control, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-volume-control,
+.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-chapters-button, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-descriptions-button, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-captions-button,
+.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-subtitles-button, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-audio-button {
+  display: none;
+}
+
+.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-current-time, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-time-divider, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-duration, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-remaining-time,
+.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-playback-rate,
+.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-mute-control, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-volume-control,
+.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-chapters-button, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-descriptions-button, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-captions-button,
+.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-subtitles-button, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-audio-button {
+  display: none;
+}
+
+.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-current-time, .video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-time-divider, .video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-duration, .video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-remaining-time,
+.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-playback-rate,
+.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-mute-control, .video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-volume-control,
+.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-chapters-button, .video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-descriptions-button, .video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-captions-button,
+.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-subtitles-button .vjs-audio-button {
+  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 auto;
+  }
+
+  .vjs-text-track-settings .vjs-track-settings-colors {
+    display: block;
+    grid-column: 1;
+    grid-row: 1;
+  }
+
+  .vjs-text-track-settings .vjs-track-settings-font {
+    grid-column: 2;
+    grid-row: 1;
+  }
+
+  .vjs-text-track-settings .vjs-track-settings-controls {
+    grid-column: 2;
+    grid-row: 2;
+  }
+}
+
+.vjs-track-setting > select {
+  margin-right: 5px;
+}
+
+.vjs-text-track-settings fieldset {
+  margin: 5px;
+  padding: 3px;
+  border: none;
+}
+
+.vjs-text-track-settings fieldset span {
+  display: inline-block;
+}
+
+.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;
+  visibility: hidden;
+}
diff --git a/form/src/assets/libs/video-js-7.0.0/video-js.min.css b/form/src/assets/libs/video-js-7.0.0/video-js.min.css
new file mode 100644
index 0000000000000000000000000000000000000000..10c2521f9f101f826a611c3eaedf8d489a3f04d4
--- /dev/null
+++ b/form/src/assets/libs/video-js-7.0.0/video-js.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,d09GRgABAAAAABBIAAsAAAAAGoQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADsAAABUIIslek9TLzIAAAFEAAAAPgAAAFZRiV3RY21hcAAAAYQAAADQAAADIjn098ZnbHlmAAACVAAACv4AABEIAwnSw2hlYWQAAA1UAAAAKgAAADYUHzoRaGhlYQAADYAAAAAbAAAAJA4DByFobXR4AAANnAAAAA8AAACE4AAAAGxvY2EAAA2sAAAARAAAAEQ9NEHGbWF4cAAADfAAAAAfAAAAIAEyAIFuYW1lAAAOEAAAASUAAAIK1cf1oHBvc3QAAA84AAABDwAAAZ5AAl/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+DwalTqIAAHicY2BkYGAA4uByr8R4fpuvDNzsDCBw7f/3LmSanREszsHABKIAKi0J7gAAeJxjYGRgYGcAARD5/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{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{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.5em;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);-webkit-border-radius:.3em;-moz-border-radius:.3em;border-radius:.3em;-webkit-transition:all .4s;-moz-transition:all .4s;-ms-transition:all .4s;-o-transition:all .4s;transition:all .4s}.vjs-big-play-centered .vjs-big-play-button{top:50%;left:50%;margin-top:-.75em;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);-webkit-transition:all 0s;-moz-transition:all 0s;-ms-transition:all 0s;-o-transition:all 0s;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:-webkit-linear-gradient(-90deg,rgba(0,0,0,.8),rgba(255,255,255,0));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}.vjs-menu li.vjs-menu-item:focus,.vjs-menu li.vjs-menu-item:hover{background-color:#73859f;background-color:rgba(115,133,159,.5)}.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-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{-webkit-transition:all .4s;-moz-transition:all .4s;-ms-transition:all .4s;-o-transition:all .4s;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;-webkit-transition:all .4s;-moz-transition:all .4s;-ms-transition:all .4s;-o-transition:all .4s;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:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;visibility:visible;opacity:1;-webkit-transition:visibility .1s,opacity .1s;-moz-transition:visibility .1s,opacity .1s;-ms-transition:visibility .1s,opacity .1s;-o-transition:visibility .1s,opacity .1s;transition:visibility .1s,opacity .1s}.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar{visibility:visible;opacity:0;-webkit-transition:visibility 1s,opacity 1s;-moz-transition:visibility 1s,opacity 1s;-ms-transition:visibility 1s,opacity 1s;-o-transition:visibility 1s,opacity 1s;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;-webkit-box-flex:none;-moz-box-flex:none;-webkit-flex:none;-ms-flex:none;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;-webkit-box-flex:auto;-moz-box-flex:auto;-webkit-flex:auto;-ms-flex:auto;flex:auto;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;min-width:4em}.video-js .vjs-progress-control.disabled{cursor:default}.vjs-live .vjs-progress-control{display:none}.vjs-no-flex .vjs-progress-control{width:auto}.video-js .vjs-progress-holder{-webkit-box-flex:auto;-moz-box-flex:auto;-webkit-flex:auto;-ms-flex:auto;flex:auto;-webkit-transition:all .2s;-moz-transition:all .2s;-ms-transition:all .2s;-o-transition:all .2s;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);-webkit-border-radius:.3em;-moz-border-radius:.3em;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:relative;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;-webkit-transition:visibility 1s,opacity 1s;-moz-transition:visibility 1s,opacity 1s;-ms-transition:visibility 1s,opacity 1s;-o-transition:visibility 1s,opacity 1s;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;-khtml-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;-webkit-box-shadow:0 0 1em #fff;-moz-box-shadow:0 0 1em #fff;box-shadow:0 0 1em #fff}.video-js .vjs-mute-control{cursor:pointer;-webkit-box-flex:none;-moz-box-flex:none;-webkit-flex:none;-ms-flex:none;flex:none}.video-js .vjs-volume-control{cursor:pointer;margin-right:1em;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;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{-webkit-transition:width 1s;-moz-transition:width 1s;-ms-transition:width 1s;-o-transition:width 1s;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;-webkit-transition:visibility .1s,opacity .1s,height .1s,width .1s,left 0s,top 0s;-moz-transition:visibility .1s,opacity .1s,height .1s,width .1s,left 0s,top 0s;-ms-transition:visibility .1s,opacity .1s,height .1s,width .1s,left 0s,top 0s;-o-transition:visibility .1s,opacity .1s,height .1s,width .1s,left 0s,top 0s;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-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;-webkit-transition:width .1s;-moz-transition:width .1s;-ms-transition:width .1s;-o-transition:width .1s;transition:width .1s}.video-js .vjs-volume-panel .vjs-volume-control.vjs-volume-vertical{height:8em;width:3em;left:-3.5em;-webkit-transition:visibility 1s,opacity 1s,height 1s 1s,width 1s 1s,left 1s 1s,top 1s 1s;-moz-transition:visibility 1s,opacity 1s,height 1s 1s,width 1s 1s,left 1s 1s,top 1s 1s;-ms-transition:visibility 1s,opacity 1s,height 1s 1s,width 1s 1s,left 1s 1s,top 1s 1s;-o-transition:visibility 1s,opacity 1s,height 1s 1s,width 1s 1s,left 1s 1s,top 1s 1s;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{-webkit-transition:visibility 1s,opacity 1s,height 1s 1s,width 1s,left 1s 1s,top 1s 1s;-moz-transition:visibility 1s,opacity 1s,height 1s 1s,width 1s,left 1s 1s,top 1s 1s;-ms-transition:visibility 1s,opacity 1s,height 1s 1s,width 1s,left 1s 1s,top 1s 1s;-o-transition:visibility 1s,opacity 1s,height 1s 1s,width 1s,left 1s 1s,top 1s 1s;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;-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;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:-webkit-box;display:-webkit-flex;display:-ms-flexbox;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:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-flex:auto;-moz-box-flex:auto;-webkit-flex:auto;-ms-flex:auto;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-time-control{-webkit-box-flex:none;-moz-box-flex:none;-webkit-flex:none;-ms-flex:none;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 .vjs-icon-placeholder{cursor:pointer;-webkit-box-flex:none;-moz-box-flex:none;-webkit-flex:none;-ms-flex:none;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{-moz-transform:translateY(-3em);-ms-transform:translateY(-3em);-o-transform:translateY(-3em);-webkit-transform:translateY(-3em);transform:translateY(-3em)}.video-js.vjs-user-inactive.vjs-playing video::-webkit-media-text-track-display{-moz-transform:translateY(-1.5em);-ms-transform:translateY(-1.5em);-o-transform:translateY(-1.5em);-webkit-transform:translateY(-1.5em);transform:translateY(-1.5em)}.video-js .vjs-fullscreen-control{cursor:pointer;-webkit-box-flex:none;-moz-box-flex:none;-webkit-flex:none;-ms-flex:none;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;animation:0s linear .3s forwards vjs-spinner-show}.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%{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{position:absolute}.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-layout-tiny:not(.vjs-fullscreen) .vjs-custom-control-spacer{-webkit-box-flex:auto;-moz-box-flex:auto;-webkit-flex:auto;-ms-flex:auto;flex:auto}.video-js.vjs-layout-tiny:not(.vjs-fullscreen).vjs-no-flex .vjs-custom-control-spacer{width:auto}.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-audio-button,.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-captions-button,.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-chapters-button,.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-current-time,.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-descriptions-button,.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-duration,.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-mute-control,.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-playback-rate,.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-progress-control,.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-remaining-time,.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-subtitles-button,.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-time-divider,.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-volume-control{display:none}.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-audio-button,.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-captions-button,.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-chapters-button,.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-current-time,.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-descriptions-button,.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-duration,.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-mute-control,.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-playback-rate,.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-remaining-time,.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-subtitles-button,.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-time-divider,.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-volume-control{display:none}.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-captions-button,.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-chapters-button,.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-current-time,.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-descriptions-button,.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-duration,.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-mute-control,.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-playback-rate,.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-remaining-time,.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-subtitles-button .vjs-audio-button,.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-time-divider,.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-volume-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 auto}.vjs-text-track-settings .vjs-track-settings-colors{display:block;grid-column:1;grid-row:1}.vjs-text-track-settings .vjs-track-settings-font{grid-column:2;grid-row:1}.vjs-text-track-settings .vjs-track-settings-controls{grid-column:2;grid-row:2}}.vjs-track-setting>select{margin-right:5px}.vjs-text-track-settings fieldset{margin:5px;padding:3px;border:none}.vjs-text-track-settings fieldset span{display:inline-block}.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;visibility:hidden}
diff --git a/form/src/assets/libs/video-js-7.0.0/video.js b/form/src/assets/libs/video-js-7.0.0/video.js
new file mode 100644
index 0000000000000000000000000000000000000000..7fbbd14e6924cad421d0cce2f555276b99bde878
--- /dev/null
+++ b/form/src/assets/libs/video-js-7.0.0/video.js
@@ -0,0 +1,47623 @@
+/**
+ * @license
+ * Video.js 7.0.0 <http://videojs.com/>
+ * Copyright Brightcove, Inc. <https://www.brightcove.com/>
+ * Available under Apache License Version 2.0
+ * <https://github.com/videojs/video.js/blob/master/LICENSE>
+ *
+ * Includes vtt.js <https://github.com/mozilla/vtt.js>
+ * Available under Apache License Version 2.0
+ * <https://github.com/mozilla/vtt.js/blob/master/LICENSE>
+ */
+
+(function (global, factory) {
+  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+      (global.videojs = factory());
+}(this, (function () {
+
+  var version = "7.0.0";
+
+  var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
+
+  function createCommonjsModule(fn, module) {
+    return module = {exports: {}}, fn(module, module.exports), module.exports;
+  }
+
+  var win;
+
+  if (typeof window !== "undefined") {
+    win = window;
+  } else if (typeof commonjsGlobal !== "undefined") {
+    win = commonjsGlobal;
+  } else if (typeof self !== "undefined") {
+    win = self;
+  } else {
+    win = {};
+  }
+
+  var window_1 = win;
+
+  var empty = {};
+
+  var empty$1 = /*#__PURE__*/Object.freeze({
+    default: empty
+  });
+
+  var minDoc = (empty$1 && empty) || empty$1;
+
+  var topLevel = typeof commonjsGlobal !== 'undefined' ? commonjsGlobal : typeof window !== 'undefined' ? window : {};
+
+  var doccy;
+
+  if (typeof document !== 'undefined') {
+    doccy = document;
+  } else {
+    doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'];
+
+    if (!doccy) {
+      doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc;
+    }
+  }
+
+  var document_1 = doccy;
+
+  /**
+   * @file log.js
+   * @module log
+   */
+
+  var log = void 0;
+
+  // This is the private tracking variable for logging level.
+  var level = 'info';
+
+  // This is the private tracking variable for the logging history.
+  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 logByType = function logByType(type, 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 a clone of the args at this point to history.
+    if (history) {
+      history.push([].concat(args));
+    }
+
+    // Add console prefix after adding to history.
+    args.unshift('VIDEOJS:');
+
+    // 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);
+  };
+
+  /**
+   * Logs plain debug messages. Similar to `console.log`.
+   *
+   * @class
+   * @param    {Mixed[]} args
+   *           One or more messages or objects that should be logged.
+   */
+  log = function log() {
+    for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+      args[_key] = arguments[_key];
+    }
+
+    logByType('log', args);
+  };
+
+  /**
+   * 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 log.levels} is provided, acts as a setter. Regardless of argument,
+   * returns the current logging level.
+   *
+   * @param  {string} [lvl]
+   *         Pass 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) : [];
+  };
+
+  /**
+   * 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 = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
+      args[_key2] = arguments[_key2];
+    }
+
+    return logByType('error', 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 = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
+      args[_key3] = arguments[_key3];
+    }
+
+    return logByType('warn', 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 = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
+      args[_key4] = arguments[_key4];
+    }
+
+    return logByType('debug', args);
+  };
+
+  var log$1 = log;
+
+  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;
+  };
+
+  var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
+    return typeof obj;
+  } : function (obj) {
+    return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
+  };
+
+  var classCallCheck = function (instance, Constructor) {
+    if (!(instance instanceof Constructor)) {
+      throw new TypeError("Cannot call a class as a function");
+    }
+  };
+
+  var inherits = function (subClass, superClass) {
+    if (typeof superClass !== "function" && superClass !== null) {
+      throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
+    }
+
+    subClass.prototype = Object.create(superClass && superClass.prototype, {
+      constructor: {
+        value: subClass,
+        enumerable: false,
+        writable: true,
+        configurable: true
+      }
+    });
+    if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
+  };
+
+  var possibleConstructorReturn = function (self, call) {
+    if (!self) {
+      throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
+    }
+
+    return call && (typeof call === "object" || typeof call === "function") ? call : self;
+  };
+
+  var taggedTemplateLiteralLoose = function (strings, raw) {
+    strings.raw = raw;
+    return strings;
+  };
+
+  /**
+   * @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) {
+    var initial = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 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 = 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 === 'undefined' ? 'undefined' : _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.
+   *
+   * @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
+   *
+   * @static
+   * @const
+   */
+  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 '';
+  }
+
+  var _templateObject = 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 ', '.'], ['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 ', '.']);
+
+  /**
+   * Detect if a value is a string with any non-whitespace characters.
+   *
+   * @param {string} str
+   *        The string to check
+   *
+   * @return {boolean}
+   *         - 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.
+   *
+   * @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.
+   *
+   * @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.
+   *
+   * @return {Boolean}
+   */
+  function isReal() {
+    // Both document and window will never be undefined thanks to `global`.
+    return document_1 === window_1.document;
+  }
+
+  /**
+   * Determines, via duck typing, whether or not a value is a DOM element.
+   *
+   * @param {Mixed} value
+   *        The thing to check
+   *
+   * @return {boolean}
+   *         - True if it 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}
+   *
+   */
+  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.
+   *
+   * @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_1[method](null);
+      }
+      if (isNonBlankString(context)) {
+        context = document_1.querySelector(context);
+      }
+
+      var ctx = isEl(context) ? context : document_1;
+
+      return ctx[method] && ctx[method](selector);
+    };
+  }
+
+  /**
+   * Creates an element and applies properties.
+   *
+   * @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 {String|Element|TextNode|Array|Function} [content]
+   *         Contents for the element (see: {@link dom:normalizeContent})
+   *
+   * @return {Element}
+   *         The element that was created.
+   */
+  function createEl() {
+    var tagName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'div';
+    var properties = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+    var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
+    var content = arguments[3];
+
+    var el = document_1.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$1.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 CSS class
+   *
+   * @param {Element} element
+   *        Element to check
+   *
+   * @param {string} classToCheck
+   *        Class name to check for
+   *
+   * @return {boolean}
+   *         - True if the element had the 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 CSS 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 CSS 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 toggleElClass.
+   *
+   * @callback 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 the `classToToggle` will get added to `element`.
+   *         - If false the `classToToggle` will get removed from `element`.
+   *         - If undefined this callback will be ignored
+   */
+
+  /**
+   * Adds or removes a CSS class name on 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|PredicateCallback} [predicate]
+   *        See the return value for {@link 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 (which shouldn't be used with HTML)
+   * This will return true or false for boolean attributes.
+   *
+   * @param {Element} tag
+   *        Element from which to get tag attributes.
+   *
+   * @return {Object}
+   *         All attributes of the element.
+   */
+  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}
+   *         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 while dragging controls
+   */
+  function blockTextSelection() {
+    document_1.body.focus();
+    document_1.onselectstart = function () {
+      return false;
+    };
+  }
+
+  /**
+   * Turn off text selection blocking
+   */
+  function unblockTextSelection() {
+    document_1.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
+   */
+  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;
+    }
+  }
+
+  /**
+   * The postion of a DOM element on the page.
+   *
+   * @typedef {Object} module:dom~Position
+   *
+   * @property {number} left
+   *           Pixels to the left
+   *
+   * @property {number} top
+   *           Pixels on top
+   */
+
+  /**
+   * Offset Left.
+   * 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 = void 0;
+
+    if (el.getBoundingClientRect && el.parentNode) {
+      box = el.getBoundingClientRect();
+    }
+
+    if (!box) {
+      return {
+        left: 0,
+        top: 0
+      };
+    }
+
+    var docEl = document_1.documentElement;
+    var body = document_1.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)
+    };
+  }
+
+  /**
+   * x and y coordinates for a dom element or mouse pointer
+   *
+   * @typedef {Object} Dom~Coordinates
+   *
+   * @property {number} x
+   *           x coordinate in pixels
+   *
+   * @property {number} y
+   *           y coordinate in pixels
+   */
+
+  /**
+   * Get pointer position in element
+   * Returns an object with x and y coordinates.
+   * 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 {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}
+   *         - True if it 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;
+  }
+
+  /**
+   * Normalizes content for eventual insertion into the DOM.
+   *
+   * This allows a wide range of content definition methods, but protects
+   * from falling into the trap of simply writing to `innerHTML`, which is
+   * an XSS concern.
+   *
+   * The content for an element can be passed in multiple types and
+   * combinations, whose behavior is as follows:
+   *
+   * @param {String|Element|TextNode|Array|Function} content
+   *        - String: Normalized into a text node.
+   *        - Element/TextNode: Passed through.
+   *        - Array: A one-dimensional array of strings, elements, nodes, or functions
+   *          (which return single strings, elements, or nodes).
+   *        - Function: If the sole argument, is expected to produce a string, element,
+   *          node, or array as defined above.
+   *
+   * @return {Array}
+   *         All of the content that was passed in normalized.
+   */
+  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_1.createTextNode(value);
+      }
+    }).filter(function (value) {
+      return value;
+    });
+  }
+
+  /**
+   * Normalizes and appends content to an element.
+   *
+   * @param {Element} el
+   *        Element to append normalized content to.
+   *
+   *
+   * @param {String|Element|TextNode|Array|Function} content
+   *        See the `content` argument of {@link dom:normalizeContent}
+   *
+   * @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 {String|Element|TextNode|Array|Function} content
+   *        See the `content` argument of {@link dom:normalizeContent}
+   *
+   * @return {Element}
+   *         The element with inserted normalized content.
+   *
+   */
+  function insertContent(el, content) {
+    return appendContent(emptyEl(el), content);
+  }
+
+  /**
+   * Check if event was a single left click
+   *
+   * @param {EventTarget~Event} event
+   *        Event object
+   *
+   * @return {boolean}
+   *         - True if a left click
+   *         - False if not a left click
+   */
+  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.
+   *
+   * @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_1;
+      }
+
+      // 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_1.documentElement;
+        var body = document_1.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$1.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) {
+      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 = void 0;
+
+  /**
+   * 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_1.getElementsByTagName('video'));
+    var audios = Array.prototype.slice.call(document_1.getElementsByTagName('audio'));
+    var divs = Array.prototype.slice.call(document_1.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_1.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_1.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.
+   *
+   * @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.
+   *
+   * @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(undefined, 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.
+   *
+   * @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) {
+    var context = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : window_1;
+
+    var timeout = void 0;
+
+    /* eslint-disable consistent-this */
+    return function () {
+      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 */
+  };
+
+  /**
+   * @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;
+
+    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;
+
+  /**
+   * @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';
+    });
+  };
+
+  /**
+   * 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 = void 0;
+    var type = void 0;
+    var listener = void 0;
+
+    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 = 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 = 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() {
+          for (var _len3 = arguments.length, largs = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
+            largs[_key3] = arguments[_key3];
+          }
+
+          _this2.off(target, type, wrapper);
+          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.
+     *
+     * @returns {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) {
+    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+    var 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);
+
+    // 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.
+     *
+     * @returns {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 = void 0;
+
+      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.
+   *
+   * @returns {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
+   */
+
+  /**
+   * Deep-merge one or more options objects, recursively merging **only** plain
+   * object properties.
+   *
+   * @param   {Object[]} sources
+   *          One or more objects to merge into a new object.
+   *
+   * @returns {Object}
+   *          A new object that is the merged result of all sources.
+   */
+  function mergeOptions() {
+    var result = {};
+
+    for (var _len = arguments.length, sources = 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 = 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) {
+      classCallCheck(this, Component);
+
+
+      // 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;
+      }
+
+      // 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
+     */
+
+
+    Component.prototype.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;
+
+      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.
+     */
+
+
+    Component.prototype.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
+     */
+
+
+    Component.prototype.options = function options(obj) {
+      log$1.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`.
+     */
+
+
+    Component.prototype.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.
+     */
+
+
+    Component.prototype.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.
+     */
+
+
+    Component.prototype.localize = function localize(string, tokens) {
+      var defaultValue = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 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`.
+     */
+
+
+    Component.prototype.contentEl = function contentEl() {
+      return this.contentEl_ || this.el_;
+    };
+
+    /**
+     * Get this `Component`s ID
+     *
+     * @return {string}
+     *         The id of this `Component`
+     */
+
+
+    Component.prototype.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`.
+     */
+
+
+    Component.prototype.name = function name() {
+      return this.name_;
+    };
+
+    /**
+     * Get an array of all child components
+     *
+     * @return {Array}
+     *         The children
+     */
+
+
+    Component.prototype.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.
+     */
+
+
+    Component.prototype.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.
+     */
+
+
+    Component.prototype.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.
+     */
+
+
+    Component.prototype.addChild = function addChild(child) {
+      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+      var index = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.children_.length;
+
+      var component = void 0;
+      var componentName = void 0;
+
+      // 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;
+      }
+
+      this.children_.splice(index, 0, component);
+
+      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.
+     */
+
+
+    Component.prototype.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;
+      }
+
+      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.
+     */
+
+
+    Component.prototype.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 = void 0;
+        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 = void 0;
+          var opts = void 0;
+
+          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
+     */
+
+
+    Component.prototype.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.
+     */
+
+
+    Component.prototype.ready = function ready(fn) {
+      var sync = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 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
+     */
+
+
+    Component.prototype.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)
+     */
+
+
+    Component.prototype.$ = 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)
+     */
+
+
+    Component.prototype.$$ = 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`
+     */
+
+
+    Component.prototype.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
+     */
+
+
+    Component.prototype.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
+     */
+
+
+    Component.prototype.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
+     */
+
+
+    Component.prototype.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.
+     */
+
+
+    Component.prototype.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.
+     */
+
+
+    Component.prototype.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
+     */
+
+
+    Component.prototype.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
+     */
+
+
+    Component.prototype.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}
+     */
+
+
+    Component.prototype.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}
+     */
+
+
+    Component.prototype.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}
+     */
+
+
+    Component.prototype.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'.
+     */
+
+
+    Component.prototype.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'.
+     */
+
+
+    Component.prototype.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.
+     */
+
+
+    Component.prototype.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
+     */
+
+
+    Component.prototype.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 width or the height of the `Component` elements computed style. 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.
+     */
+
+
+    Component.prototype.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 width and height values of the `Component`s
+     * computed style.
+     *
+     * @return {Component~DimensionObject}
+     *         The dimensions of the components element
+     */
+
+
+    Component.prototype.currentDimensions = function currentDimensions() {
+      return {
+        width: this.currentDimension('width'),
+        height: this.currentDimension('height')
+      };
+    };
+
+    /**
+     * Get the width of the `Component`s computed style. Uses `window.getComputedStyle`.
+     *
+     * @return {number} width
+     *           The width of the `Component`s computed style.
+     */
+
+
+    Component.prototype.currentWidth = function currentWidth() {
+      return this.currentDimension('width');
+    };
+
+    /**
+     * Get the height of the `Component`s computed style. Uses `window.getComputedStyle`.
+     *
+     * @return {number} height
+     *           The height of the `Component`s computed style.
+     */
+
+
+    Component.prototype.currentHeight = function currentHeight() {
+      return this.currentDimension('height');
+    };
+
+    /**
+     * Set the focus to this component
+     */
+
+
+    Component.prototype.focus = function focus() {
+      this.el_.focus();
+    };
+
+    /**
+     * Remove the focus from this component
+     */
+
+
+    Component.prototype.blur = function blur() {
+      this.el_.blur();
+    };
+
+    /**
+     * 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
+     */
+
+
+    Component.prototype.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 = void 0;
+
+      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
+     */
+
+
+    Component.prototype.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 = void 0;
+
+      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 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}.
+     *
+     * @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}
+     */
+
+
+    Component.prototype.setTimeout = function setTimeout(fn, timeout) {
+      var _this2 = this;
+
+      fn = bind(this, fn);
+
+      var timeoutId = window_1.setTimeout(fn, timeout);
+      var 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}
+     */
+
+
+    Component.prototype.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}
+     */
+
+
+    Component.prototype.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}
+     */
+
+
+    Component.prototype.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}
+     */
+
+
+    Component.prototype.requestAnimationFrame = function requestAnimationFrame(fn) {
+      var _this4 = this;
+
+      if (this.supportsRaf_) {
+        fn = bind(this, fn);
+
+        var id = window_1.requestAnimationFrame(fn);
+        var 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}
+     */
+
+
+    Component.prototype.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 = void 0;
+
+        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;
+
+  /*
+   * Device is an iPhone
+   *
+   * @type {Boolean}
+   * @constant
+   * @private
+   */
+  var IS_IPAD = /iPad/i.test(USER_AGENT);
+
+  // 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;
+  var IS_IPOD = /iPod/i.test(USER_AGENT);
+  var IS_IOS = IS_IPHONE || IS_IPAD || IS_IPOD;
+
+  var IOS_VERSION = function () {
+    var match = USER_AGENT.match(/OS (\d+)_/i);
+
+    if (match && match[1]) {
+      return match[1];
+    }
+    return null;
+  }();
+
+  var IS_ANDROID = /Android/i.test(USER_AGENT);
+  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;
+  }();
+
+  var IS_NATIVE_ANDROID = IS_ANDROID && ANDROID_VERSION < 5 && appleWebkitVersion < 537;
+
+  var IS_FIREFOX = /Firefox/i.test(USER_AGENT);
+  var IS_EDGE = /Edge/i.test(USER_AGENT);
+  var IS_CHROME = !IS_EDGE && /Chrome/i.test(USER_AGENT);
+  var CHROME_VERSION = function () {
+    var match = USER_AGENT.match(/Chrome\/(\d+)/);
+
+    if (match && match[1]) {
+      return parseFloat(match[1]);
+    }
+    return 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;
+  }();
+
+  var IS_SAFARI = /Safari/i.test(USER_AGENT) && !IS_CHROME && !IS_ANDROID && !IS_EDGE;
+  var IS_ANY_SAFARI = IS_SAFARI || IS_IOS;
+
+  var TOUCH_ENABLED = isReal() && ('ontouchstart' in window_1 || 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.
+   *
+   * @function time-ranges:indexFunction
+   *
+   * @param {number} [index=0]
+   *        The range number to return the time for.
+   *
+   * @return {number}
+   *         The time that offset at the specified index.
+   *
+   * @depricated index must be set to a value, in the future this will throw an error.
+   */
+
+  /**
+   * An object that contains ranges of time for various reasons.
+   *
+   * @typedef {Object} TimeRange
+   *
+   * @property {number} length
+   *           The number of time ranges represented by this Object
+   *
+   * @property {time-ranges:indexFunction} start
+   *           Returns the time offset at which a specified time range begins.
+   *
+   * @property {time-ranges:indexFunction} 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.
+   *
+   * @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.
+   *
+   * @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.
+   *
+   *
+   * @depricated 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.
+   *
+   * @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)
+    };
+  }
+
+  /**
+   * Should create a fake `TimeRange` object which mimics an HTML5 time range instance.
+   *
+   * @param {number|Array} start
+   *        The start of a single range or an array of ranges
+   *
+   * @param {number} end
+   *        The end of a single range.
+   *
+   * @private
+   */
+  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 = void 0;
+    var end = void 0;
+
+    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'],
+    // WebKit
+    ['webkitRequestFullscreen', 'webkitExitFullscreen', 'webkitFullscreenElement', 'webkitFullscreenEnabled', 'webkitfullscreenchange', 'webkitfullscreenerror'],
+    // Old WebKit (Safari 5.1)
+    ['webkitRequestFullScreen', 'webkitCancelFullScreen', 'webkitCurrentFullScreenElement', 'webkitCancelFullScreen', 'webkitfullscreenchange', 'webkitfullscreenerror'],
+    // Mozilla
+    ['mozRequestFullScreen', 'mozCancelFullScreen', 'mozFullScreenElement', 'mozFullScreenEnabled', 'mozfullscreenchange', 'mozfullscreenerror'],
+    // Microsoft
+    ['msRequestFullscreen', 'msExitFullscreen', 'msFullscreenElement', 'msFullscreenEnabled', 'MSFullscreenChange', 'MSFullscreenError']];
+
+  var specApi = apiMap[0];
+  var browserApi = void 0;
+
+  // determine the supported set of functions
+  for (var i = 0; i < apiMap.length; i++) {
+    // check for exitFullscreen function
+    if (apiMap[i][1] in document_1) {
+      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];
+    }
+  }
+
+  /**
+   * @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_CUSTOM
+   * @property {string} 2 - MEDIA_ERR_ABORTED
+   * @property {string} 3 - MEDIA_ERR_NETWORK
+   * @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;
+  }
+
+  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
+   * playOld 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_
+  };
+
+  /**
+   * @file modal-dialog.js
+   */
+
+  var MODAL_CLASS_NAME = 'vjs-modal-dialog';
+  var ESC = 27;
+
+  /**
+   * 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 = function (_Component) {
+    inherits(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.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) {
+      classCallCheck(this, ModalDialog);
+
+      var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
+
+      _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.
+     */
+
+
+    ModalDialog.prototype.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'
+      });
+    };
+
+    ModalDialog.prototype.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.
+     */
+
+
+    ModalDialog.prototype.buildCSSClass = function buildCSSClass() {
+      return MODAL_CLASS_NAME + ' vjs-hidden ' + _Component.prototype.buildCSSClass.call(this);
+    };
+
+    /**
+     * Handles `keydown` events on the document, looking for ESC, which closes
+     * the modal.
+     *
+     * @param {EventTarget~Event} e
+     *        The keypress that triggered this event.
+     *
+     * @listens keydown
+     */
+
+
+    ModalDialog.prototype.handleKeyPress = function handleKeyPress(e) {
+      if (e.which === ESC && this.closeable()) {
+        this.close();
+      }
+    };
+
+    /**
+     * Returns the label string for this modal. Primarily used for accessibility.
+     *
+     * @return {string}
+     *         the localized or raw label of this modal.
+     */
+
+
+    ModalDialog.prototype.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.
+     */
+
+
+    ModalDialog.prototype.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
+     */
+
+
+    ModalDialog.prototype.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();
+        }
+
+        if (this.closeable()) {
+          this.on(this.el_.ownerDocument, 'keydown', bind(this, this.handleKeyPress));
+        }
+
+        // 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
+     */
+
+
+    ModalDialog.prototype.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
+     */
+
+
+    ModalDialog.prototype.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();
+      }
+
+      if (this.closeable()) {
+        this.off(this.el_.ownerDocument, 'keydown', bind(this, this.handleKeyPress));
+      }
+
+      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.
+     */
+
+
+    ModalDialog.prototype.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.
+     */
+
+
+    ModalDialog.prototype.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.
+     */
+
+
+    ModalDialog.prototype.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
+     */
+
+
+    ModalDialog.prototype.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
+     */
+
+
+    ModalDialog.prototype.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
+     */
+
+
+    ModalDialog.prototype.conditionalFocus_ = function conditionalFocus_() {
+      var activeEl = document_1.activeElement;
+      var playerEl = this.player_.el_;
+
+      this.previouslyActiveEl_ = null;
+
+      if (playerEl.contains(activeEl) || playerEl === activeEl) {
+        this.previouslyActiveEl_ = activeEl;
+
+        this.focus();
+
+        this.on(document_1, 'keydown', this.handleKeyDown);
+      }
+    };
+
+    /**
+     * conditionally blur the element and refocus the last focused element
+     *
+     * @private
+     */
+
+
+    ModalDialog.prototype.conditionalBlur_ = function conditionalBlur_() {
+      if (this.previouslyActiveEl_) {
+        this.previouslyActiveEl_.focus();
+        this.previouslyActiveEl_ = null;
+      }
+
+      this.off(document_1, 'keydown', this.handleKeyDown);
+    };
+
+    /**
+     * Keydown handler. Attached when modal is focused.
+     *
+     * @listens keydown
+     */
+
+
+    ModalDialog.prototype.handleKeyDown = function handleKeyDown(event) {
+      // exit early if it isn't a tab key
+      if (event.which !== 9) {
+        return;
+      }
+
+      var focusableEls = this.focusableEls_();
+      var activeEl = this.el_.querySelector(':focus');
+      var focusIndex = void 0;
+
+      for (var i = 0; i < focusableEls.length; i++) {
+        if (activeEl === focusableEls[i]) {
+          focusIndex = i;
+          break;
+        }
+      }
+
+      if (document_1.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
+     */
+
+
+    ModalDialog.prototype.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);
+
+  /**
+   * @file track-list.js
+   */
+
+  /**
+   * Common functionaliy between {@link TextTrackList}, {@link AudioTrackList}, and
+   * {@link VideoTrackList}
+   *
+   * @extends EventTarget
+   */
+
+  var TrackList = function (_EventTarget) {
+    inherits(TrackList, _EventTarget);
+
+    /**
+     * Create an instance of this class
+     *
+     * @param {Track[]} tracks
+     *        A list of tracks to initialize the list with.
+     *
+     * @abstract
+     */
+    function TrackList() {
+      var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
+      classCallCheck(this, TrackList);
+
+      var _this = possibleConstructorReturn(this, _EventTarget.call(this));
+
+      _this.tracks_ = [];
+
+      /**
+       * @memberof TrackList
+       * @member {number} length
+       *         The current number of `Track`s in the this Trackist.
+       * @instance
+       */
+      Object.defineProperty(_this, 'length', {
+        get: function get$$1() {
+          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
+     */
+
+
+    TrackList.prototype.addTrack = function addTrack(track) {
+      var index = this.tracks_.length;
+
+      if (!('' + index in this)) {
+        Object.defineProperty(this, index, {
+          get: function get$$1() {
+            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'
+        });
+      }
+    };
+
+    /**
+     * Remove a {@link Track} from the `TrackList`
+     *
+     * @param {Track} rtrack
+     *        The audio, video, or text track to remove from the list.
+     *
+     * @fires TrackList#removetrack
+     */
+
+
+    TrackList.prototype.removeTrack = function removeTrack(rtrack) {
+      var track = void 0;
+
+      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'
+      });
+    };
+
+    /**
+     * 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
+     */
+
+
+    TrackList.prototype.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;
+  }
+
+  /**
+   * @file audio-track-list.js
+   */
+
+  /**
+   * 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 = function (_TrackList) {
+    inherits(AudioTrackList, _TrackList);
+
+    /**
+     * Create an instance of this class.
+     *
+     * @param {AudioTrack[]} [tracks=[]]
+     *        A list of `AudioTrack` to instantiate the list with.
+     */
+    function AudioTrackList() {
+      var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
+      classCallCheck(this, AudioTrackList);
+
+      // 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;
+        }
+      }
+
+      var _this = possibleConstructorReturn(this, _TrackList.call(this, tracks));
+
+      _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
+     */
+
+
+    AudioTrackList.prototype.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;
+      }
+
+      /**
+       * @listens AudioTrack#enabledchange
+       * @fires TrackList#change
+       */
+      track.addEventListener('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');
+      });
+    };
+
+    return AudioTrackList;
+  }(TrackList);
+
+  /**
+   * @file video-track-list.js
+   */
+
+  /**
+   * 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 = function (_TrackList) {
+    inherits(VideoTrackList, _TrackList);
+
+    /**
+     * Create an instance of this class.
+     *
+     * @param {VideoTrack[]} [tracks=[]]
+     *        A list of `VideoTrack` to instantiate the list with.
+     */
+    function VideoTrackList() {
+      var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
+      classCallCheck(this, VideoTrackList);
+
+      // 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;
+        }
+      }
+
+      var _this = possibleConstructorReturn(this, _TrackList.call(this, tracks));
+
+      _this.changing_ = false;
+
+      /**
+       * @member {number} VideoTrackList#selectedIndex
+       *         The current index of the selected {@link VideoTrack`}.
+       */
+      Object.defineProperty(_this, 'selectedIndex', {
+        get: function get$$1() {
+          for (var _i = 0; _i < this.length; _i++) {
+            if (this[_i].selected) {
+              return _i;
+            }
+          }
+          return -1;
+        },
+        set: function set$$1() {
+        }
+      });
+      return _this;
+    }
+
+    /**
+     * Add a {@link VideoTrack} to the `VideoTrackList`.
+     *
+     * @param {VideoTrack} track
+     *        The VideoTrack to add to the list
+     *
+     * @fires TrackList#addtrack
+     */
+
+
+    VideoTrackList.prototype.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;
+      }
+
+      /**
+       * @listens VideoTrack#selectedchange
+       * @fires TrackList#change
+       */
+      track.addEventListener('selectedchange', function () {
+        if (_this2.changing_) {
+          return;
+        }
+        _this2.changing_ = true;
+        disableOthers$1(_this2, track);
+        _this2.changing_ = false;
+        _this2.trigger('change');
+      });
+    };
+
+    return VideoTrackList;
+  }(TrackList);
+
+  /**
+   * @file text-track-list.js
+   */
+
+  /**
+   * 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 = function (_TrackList) {
+    inherits(TextTrackList, _TrackList);
+
+    function TextTrackList() {
+      classCallCheck(this, TextTrackList);
+      return possibleConstructorReturn(this, _TrackList.apply(this, arguments));
+    }
+
+    /**
+     * Add a {@link TextTrack} to the `TextTrackList`
+     *
+     * @param {TextTrack} track
+     *        The text track to add to the list.
+     *
+     * @fires TrackList#addtrack
+     */
+    TextTrackList.prototype.addTrack = function addTrack(track) {
+      _TrackList.prototype.addTrack.call(this, track);
+
+      /**
+       * @listens TextTrack#modechange
+       * @fires TrackList#change
+       */
+      track.addEventListener('modechange', bind(this, function () {
+        this.trigger('change');
+      }));
+
+      var nonLanguageTextTrackKind = ['metadata', 'chapters'];
+
+      if (nonLanguageTextTrackKind.indexOf(track.kind) === -1) {
+        track.addEventListener('modechange', bind(this, function () {
+          this.trigger('selectedlanguagechange');
+        }));
+      }
+    };
+
+    return TextTrackList;
+  }(TrackList);
+
+  /**
+   * @file html-track-element-list.js
+   */
+
+  /**
+   * The current list of {@link HtmlTrackElement}s.
+   */
+  var HtmlTrackElementList = function () {
+
+    /**
+     * Create an instance of this class.
+     *
+     * @param {HtmlTrackElement[]} [tracks=[]]
+     *        A list of `HtmlTrackElement` to instantiate the list with.
+     */
+    function HtmlTrackElementList() {
+      var trackElements = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
+      classCallCheck(this, HtmlTrackElementList);
+
+      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$$1() {
+          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
+     */
+
+
+    HtmlTrackElementList.prototype.addTrackElement_ = function addTrackElement_(trackElement) {
+      var index = this.trackElements_.length;
+
+      if (!('' + index in this)) {
+        Object.defineProperty(this, index, {
+          get: function get$$1() {
+            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
+     */
+
+
+    HtmlTrackElementList.prototype.getTrackElementByTrack_ = function getTrackElementByTrack_(track) {
+      var trackElement_ = void 0;
+
+      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
+     */
+
+
+    HtmlTrackElementList.prototype.removeTrackElement_ = function removeTrackElement_(trackElement) {
+      for (var i = 0, length = this.trackElements_.length; i < length; i++) {
+        if (trackElement === this.trackElements_[i]) {
+          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 = function () {
+
+    /**
+     * Create an instance of this class..
+     *
+     * @param {Array} cues
+     *        A list of cues to be initialized with
+     */
+    function TextTrackCueList(cues) {
+      classCallCheck(this, TextTrackCueList);
+
+      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$$1() {
+          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
+     */
+
+
+    TextTrackCueList.prototype.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$$1() {
+              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.
+     */
+
+
+    TextTrackCueList.prototype.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'
+  };
+
+  /**
+   * @file track.js
+   */
+
+  /**
+   * 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 = function (_EventTarget) {
+    inherits(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() {
+      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+      classCallCheck(this, Track);
+
+      var _this = possibleConstructorReturn(this, _EventTarget.call(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(_this, key, {
+          get: function get$$1() {
+            return trackProps[key];
+          },
+          set: function set$$1() {
+          }
+        });
+      };
+
+      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.
+   *
+   * @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_1.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 = void 0;
+
+    if (addToBody) {
+      div = document_1.createElement('div');
+      div.innerHTML = '<a href="' + url + '"></a>';
+      a = div.firstChild;
+      // prevent the div from affecting layout
+      div.setAttribute('style', 'display:none; position:absolute;');
+      document_1.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_1.body.removeChild(div);
+    }
+
+    return details;
+  };
+
+  /**
+   * Get absolute version of relative URL. Used to tell flash correct URL.
+   *
+   *
+   * @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_1.createElement('div');
+
+      div.innerHTML = '<a href="' + url + '">x</a>';
+      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.
+   *
+   * @param {string} path
+   *        The fileName path like '/path/to/file.mp4'
+   *
+   * @returns {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.
+   *
+   * @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 isFunction$1 = /*#__PURE__*/Object.freeze({
+    default: isFunction_1,
+    __moduleExports: isFunction_1
+  });
+
+  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 trim = /*#__PURE__*/Object.freeze({
+    default: trim_1,
+    __moduleExports: trim_1,
+    left: trim_2,
+    right: trim_3
+  });
+
+  var isFunction$2 = (isFunction$1 && isFunction_1) || isFunction$1;
+
+  var forEach_1 = forEach;
+
+  var toString$2 = Object.prototype.toString;
+  var hasOwnProperty = Object.prototype.hasOwnProperty;
+
+  function forEach(list, iterator, context) {
+    if (!isFunction$2(iterator)) {
+      throw new TypeError('iterator must be a function');
+    }
+
+    if (arguments.length < 3) {
+      context = this;
+    }
+
+    if (toString$2.call(list) === '[object Array]') forEachArray(list, iterator, context); else if (typeof list === 'string') forEachString(list, iterator, context); else forEachObject(list, iterator, context);
+  }
+
+  function forEachArray(array, iterator, context) {
+    for (var i = 0, len = array.length; i < len; i++) {
+      if (hasOwnProperty.call(array, i)) {
+        iterator.call(context, array[i], i, array);
+      }
+    }
+  }
+
+  function forEachString(string, iterator, context) {
+    for (var i = 0, len = string.length; i < len; i++) {
+      // no such thing as a sparse string.
+      iterator.call(context, string.charAt(i), i, string);
+    }
+  }
+
+  function forEachObject(object, iterator, context) {
+    for (var k in object) {
+      if (hasOwnProperty.call(object, k)) {
+        iterator.call(context, object[k], k, object);
+      }
+    }
+  }
+
+  var forEach$1 = /*#__PURE__*/Object.freeze({
+    default: forEach_1,
+    __moduleExports: forEach_1
+  });
+
+  var trim$1 = (trim && trim_1) || trim;
+
+  var forEach$2 = (forEach$1 && forEach_1) || forEach$1;
+
+  var isArray = function isArray(arg) {
+    return Object.prototype.toString.call(arg) === '[object Array]';
+  };
+
+  var parseHeaders = function parseHeaders(headers) {
+    if (!headers) return {};
+
+    var result = {};
+
+    forEach$2(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 parseHeaders$1 = /*#__PURE__*/Object.freeze({
+    default: parseHeaders,
+    __moduleExports: parseHeaders
+  });
+
+  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 immutable$1 = /*#__PURE__*/Object.freeze({
+    default: immutable,
+    __moduleExports: immutable
+  });
+
+  var parseHeaders$2 = (parseHeaders$1 && parseHeaders) || parseHeaders$1;
+
+  var xtend = (immutable$1 && immutable) || immutable$1;
+
+  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$2(options)) {
+      callback = options;
+      if (typeof uri === "string") {
+        params = {uri: uri};
+      }
+    } else {
+      params = xtend(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$2(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() {
+  }
+
+  /**
+   * @file text-track.js
+   */
+
+  /**
+   * 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$1.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$1.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_) {
+          var loadHandler = function loadHandler() {
+            return parseCues(responseBody, track);
+          };
+
+          track.tech_.on('vttjsloaded', loadHandler);
+          track.tech_.on('vttjserror', function () {
+            log$1.error('vttjs failed to load, stopping trying to process ' + track.src);
+            track.tech_.off('vttjsloaded', loadHandler);
+          });
+        }
+      } 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 = function (_Track) {
+    inherits(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() {
+      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+      classCallCheck(this, TextTrack);
+
+      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';
+      }
+
+      var _this = possibleConstructorReturn(this, _Track.call(this, settings));
+
+      _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(_this, function () {
+
+        // Accessing this.activeCues for the side-effects of updating itself
+        // due to it's nature as a getter function. Do not remove or cues will
+        // stop updating!
+        /* eslint-disable no-unused-expressions */
+        this.activeCues;
+        /* eslint-enable no-unused-expressions */
+        if (changed) {
+          this.trigger('cuechange');
+          changed = false;
+        }
+      });
+
+      if (mode !== 'disabled') {
+        _this.tech_.ready(function () {
+          _this.tech_.on('timeupdate', timeupdateHandler);
+        }, true);
+      }
+
+      Object.defineProperties(_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$$1() {
+            return default_;
+          },
+          set: function set$$1() {
+          }
+        },
+
+        /**
+         * @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$$1() {
+            return mode;
+          },
+          set: function set$$1(newMode) {
+            var _this2 = this;
+
+            if (!TextTrackMode[newMode]) {
+              return;
+            }
+            mode = newMode;
+            if (mode === 'showing') {
+
+              this.tech_.ready(function () {
+                _this2.tech_.on('timeupdate', timeupdateHandler);
+              }, true);
+            }
+            /**
+             * 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$$1() {
+            if (!this.loaded_) {
+              return null;
+            }
+
+            return cues;
+          },
+          set: function set$$1() {
+          }
+        },
+
+        /**
+         * @memberof TextTrack
+         * @member {TextTrackCueList} activeCues
+         *         The list text track cues that are currently active for this TextTrack.
+         * @instance
+         */
+        activeCues: {
+          get: function get$$1() {
+            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;
+          },
+          set: function set$$1() {
+          }
+        }
+      });
+
+      if (settings.src) {
+        _this.src = settings.src;
+        loadTrack(settings.src, _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
+     */
+
+
+    TextTrack.prototype.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
+     */
+
+
+    TextTrack.prototype.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 = function (_Track) {
+    inherits(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() {
+      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+      classCallCheck(this, AudioTrack);
+
+      var settings = mergeOptions(options, {
+        kind: AudioTrackKind[options.kind] || ''
+      });
+
+      var _this = possibleConstructorReturn(this, _Track.call(this, settings));
+
+      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(_this, 'enabled', {
+        get: function get$$1() {
+          return enabled;
+        },
+        set: function set$$1(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 = function (_Track) {
+    inherits(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() {
+      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+      classCallCheck(this, VideoTrack);
+
+      var settings = mergeOptions(options, {
+        kind: VideoTrackKind[options.kind] || ''
+      });
+
+      var _this = possibleConstructorReturn(this, _Track.call(this, settings));
+
+      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(_this, 'selected', {
+        get: function get$$1() {
+          return selected;
+        },
+        set: function set$$1(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);
+
+  /**
+   * @file html-track-element.js
+   */
+
+  /**
+   * @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 = function (_EventTarget) {
+    inherits(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() {
+      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+      classCallCheck(this, HTMLTrackElement);
+
+      var _this = possibleConstructorReturn(this, _EventTarget.call(this));
+
+      var readyState = void 0;
+
+      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(_this, {
+
+        /**
+         * @memberof HTMLTrackElement
+         * @member {HTMLTrackElement~ReadyState} readyState
+         *         The current ready state of the track element.
+         * @instance
+         */
+        readyState: {
+          get: function get$$1() {
+            return readyState;
+          }
+        },
+
+        /**
+         * @memberof HTMLTrackElement
+         * @member {TextTrack} track
+         *         The underlying TextTrack object.
+         * @instance
+         *
+         */
+        track: {
+          get: function get$$1() {
+            return track;
+          }
+        }
+      });
+
+      readyState = NONE;
+
+      /**
+       * @listens TextTrack#loadeddata
+       * @fires HTMLTrackElement#load
+       */
+      track.addEventListener('loadeddata', function () {
+        readyState = LOADED;
+
+        _this.trigger({
+          type: 'load',
+          target: _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 = {
+    "&amp;": "&",
+    "&lt;": "<",
+    "&gt;": ">",
+    "&lrm;": "\u200E",
+    "&rlm;": "\u200F",
+    "&nbsp;": "\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;
+
+  var vtt$1 = /*#__PURE__*/Object.freeze({
+    default: vtt,
+    __moduleExports: vtt
+  });
+
+  /**
+   * 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 <track> spec defined properties
+     */
+
+    // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#text-track-cue-display-state
+    this.displayState = undefined;
+  }
+
+  /**
+   * VTTCue methods
+   */
+
+  VTTCue.prototype.getCueAsHTML = function () {
+    // Assume WebVTT.convertCueToDOMTree is on the global.
+    return WebVTT.convertCueToDOMTree(window, this.text);
+  };
+
+  var vttcue = VTTCue;
+
+  var vttcue$1 = /*#__PURE__*/Object.freeze({
+    default: vttcue,
+    __moduleExports: vttcue
+  });
+
+  /**
+   * 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 scrollSetting = {
+    "": true,
+    "up": true
+  };
+
+  function findScrollSetting(value) {
+    if (typeof value !== "string") {
+      return false;
+    }
+    var scroll = scrollSetting[value.toLowerCase()];
+    return scroll ? value.toLowerCase() : false;
+  }
+
+  function isValidPercentValue(value) {
+    return typeof value === "number" && value >= 0 && value <= 100;
+  }
+
+  // VTTRegion shim http://dev.w3.org/html5/webvtt/#vttregion-interface
+  function VTTRegion() {
+    var _width = 100;
+    var _lines = 3;
+    var _regionAnchorX = 0;
+    var _regionAnchorY = 100;
+    var _viewportAnchorX = 0;
+    var _viewportAnchorY = 100;
+    var _scroll = "";
+
+    Object.defineProperties(this, {
+      "width": {
+        enumerable: true,
+        get: function get() {
+          return _width;
+        },
+        set: function set(value) {
+          if (!isValidPercentValue(value)) {
+            throw new Error("Width must be between 0 and 100.");
+          }
+          _width = value;
+        }
+      },
+      "lines": {
+        enumerable: true,
+        get: function get() {
+          return _lines;
+        },
+        set: function set(value) {
+          if (typeof value !== "number") {
+            throw new TypeError("Lines must be set to a number.");
+          }
+          _lines = value;
+        }
+      },
+      "regionAnchorY": {
+        enumerable: true,
+        get: function get() {
+          return _regionAnchorY;
+        },
+        set: function set(value) {
+          if (!isValidPercentValue(value)) {
+            throw new Error("RegionAnchorX must be between 0 and 100.");
+          }
+          _regionAnchorY = value;
+        }
+      },
+      "regionAnchorX": {
+        enumerable: true,
+        get: function get() {
+          return _regionAnchorX;
+        },
+        set: function set(value) {
+          if (!isValidPercentValue(value)) {
+            throw new Error("RegionAnchorY must be between 0 and 100.");
+          }
+          _regionAnchorX = value;
+        }
+      },
+      "viewportAnchorY": {
+        enumerable: true,
+        get: function get() {
+          return _viewportAnchorY;
+        },
+        set: function set(value) {
+          if (!isValidPercentValue(value)) {
+            throw new Error("ViewportAnchorY must be between 0 and 100.");
+          }
+          _viewportAnchorY = value;
+        }
+      },
+      "viewportAnchorX": {
+        enumerable: true,
+        get: function get() {
+          return _viewportAnchorX;
+        },
+        set: function set(value) {
+          if (!isValidPercentValue(value)) {
+            throw new Error("ViewportAnchorX must be between 0 and 100.");
+          }
+          _viewportAnchorX = value;
+        }
+      },
+      "scroll": {
+        enumerable: true,
+        get: function get() {
+          return _scroll;
+        },
+        set: function set(value) {
+          var setting = findScrollSetting(value);
+          // Have to check for false as an empty string is a legal value.
+          if (setting === false) {
+            throw new SyntaxError("An invalid or illegal string was specified.");
+          }
+          _scroll = setting;
+        }
+      }
+    });
+  }
+
+  var vttregion = VTTRegion;
+
+  var vttregion$1 = /*#__PURE__*/Object.freeze({
+    default: vttregion,
+    __moduleExports: vttregion
+  });
+
+  var require$$0 = (vtt$1 && vtt) || vtt$1;
+
+  var require$$1 = (vttcue$1 && vttcue) || vttcue$1;
+
+  var require$$2 = (vttregion$1 && vttregion) || vttregion$1;
+
+  var browserIndex = createCommonjsModule(function (module) {
+    /**
+     * 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.
+     */
+
+      // Default exports for Node. Export the extended versions of VTTCue and
+      // VTTRegion in Node since we likely want the capability to convert back and
+      // forth between JSON. If we don't then it's not that big of a deal since we're
+      // off browser.
+
+
+    var vttjs = module.exports = {
+        WebVTT: require$$0,
+        VTTCue: require$$1,
+        VTTRegion: require$$2
+      };
+
+    window_1.vttjs = vttjs;
+    window_1.WebVTT = vttjs.WebVTT;
+
+    var cueShim = vttjs.VTTCue;
+    var regionShim = vttjs.VTTRegion;
+    var nativeVTTCue = window_1.VTTCue;
+    var nativeVTTRegion = window_1.VTTRegion;
+
+    vttjs.shim = function () {
+      window_1.VTTCue = cueShim;
+      window_1.VTTRegion = regionShim;
+    };
+
+    vttjs.restore = function () {
+      window_1.VTTCue = nativeVTTCue;
+      window_1.VTTRegion = nativeVTTRegion;
+    };
+
+    if (!window_1.VTTCue) {
+      vttjs.shim();
+    }
+  });
+  var browserIndex_1 = browserIndex.WebVTT;
+  var browserIndex_2 = browserIndex.VTTCue;
+  var browserIndex_3 = browserIndex.VTTRegion;
+
+  /**
+   * @file tech.js
+   */
+
+  /**
+   * An Object containing a structure like: `{src: 'url', type: 'mimetype'}` or string
+   * that just contains the src url alone.
+   * * `var SourceObject = {src: 'http://ex.com/video.mp4', type: 'video/mp4'};`
+   * `var SourceString = 'http://example.com/some-video.mp4';`
+   *
+   * @typedef {Object|string} Tech~SourceObject
+   *
+   * @property {string} src
+   *           The url to the source
+   *
+   * @property {string} type
+   *           The mime type of the source
+   */
+
+  /**
+   * A function used by {@link Tech} to create a new {@link TextTrack}.
+   *
+   * @private
+   *
+   * @param {Tech} self
+   *        An instance of the Tech class.
+   *
+   * @param {string} kind
+   *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
+   *
+   * @param {string} [label]
+   *        Label to identify the text track
+   *
+   * @param {string} [language]
+   *        Two letter language abbreviation
+   *
+   * @param {Object} [options={}]
+   *        An object with additional text track options
+   *
+   * @return {TextTrack}
+   *          The text track that was created.
+   */
+  function createTrackHelper(self, kind, label, language) {
+    var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
+
+    var tracks = self.textTracks();
+
+    options.kind = kind;
+
+    if (label) {
+      options.label = label;
+    }
+    if (language) {
+      options.language = language;
+    }
+    options.tech = self;
+
+    var track = new ALL.text.TrackClass(options);
+
+    tracks.addTrack(track);
+
+    return track;
+  }
+
+  /**
+   * This is the base class for media playback technology controllers, such as
+   * {@link Flash} and {@link HTML5}
+   *
+   * @extends Component
+   */
+
+  var Tech = function (_Component) {
+    inherits(Tech, _Component);
+
+    /**
+     * Create an instance of this Tech.
+     *
+     * @param {Object} [options]
+     *        The key/value store of player options.
+     *
+     * @param {Component~ReadyCallback} ready
+     *        Callback function to call when the `HTML5` Tech is ready.
+     */
+    function Tech() {
+      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+      var ready = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {
+      };
+      classCallCheck(this, Tech);
+
+      // we don't want the tech to report user activity automatically.
+      // This is done manually in addControlsListeners
+      options.reportTouchActivity = false;
+
+      // keep track of whether the current source has played at all to
+      // implement a very limited played()
+      var _this = possibleConstructorReturn(this, _Component.call(this, null, options, ready));
+
+      _this.hasStarted_ = false;
+      _this.on('playing', function () {
+        this.hasStarted_ = true;
+      });
+      _this.on('loadstart', function () {
+        this.hasStarted_ = false;
+      });
+
+      ALL.names.forEach(function (name) {
+        var props = ALL[name];
+
+        if (options && options[props.getterName]) {
+          _this[props.privateName] = options[props.getterName];
+        }
+      });
+
+      // Manually track progress in cases where the browser/flash player doesn't report it.
+      if (!_this.featuresProgressEvents) {
+        _this.manualProgressOn();
+      }
+
+      // Manually track timeupdates in cases where the browser/flash player doesn't report it.
+      if (!_this.featuresTimeupdateEvents) {
+        _this.manualTimeUpdatesOn();
+      }
+
+      ['Text', 'Audio', 'Video'].forEach(function (track) {
+        if (options['native' + track + 'Tracks'] === false) {
+          _this['featuresNative' + track + 'Tracks'] = false;
+        }
+      });
+
+      if (options.nativeCaptions === false || options.nativeTextTracks === false) {
+        _this.featuresNativeTextTracks = false;
+      } else if (options.nativeCaptions === true || options.nativeTextTracks === true) {
+        _this.featuresNativeTextTracks = true;
+      }
+
+      if (!_this.featuresNativeTextTracks) {
+        _this.emulateTextTracks();
+      }
+
+      _this.autoRemoteTextTracks_ = new ALL.text.ListClass();
+
+      _this.initTrackListeners();
+
+      // Turn on component tap events only if not using native controls
+      if (!options.nativeControlsForTouch) {
+        _this.emitTapEvents();
+      }
+
+      if (_this.constructor) {
+        _this.name_ = _this.constructor.name || 'Unknown Tech';
+      }
+      return _this;
+    }
+
+    /**
+     * A special function to trigger source set in a way that will allow player
+     * to re-trigger if the player or tech are not ready yet.
+     *
+     * @fires Tech#sourceset
+     * @param {string} src The source string at the time of the source changing.
+     */
+
+
+    Tech.prototype.triggerSourceset = function triggerSourceset(src) {
+      var _this2 = this;
+
+      if (!this.isReady_) {
+        // on initial ready we have to trigger source set
+        // 1ms after ready so that player can watch for it.
+        this.one('ready', function () {
+          return _this2.setTimeout(function () {
+            return _this2.triggerSourceset(src);
+          }, 1);
+        });
+      }
+
+      /**
+       * Fired when the source is set on the tech causing the media element
+       * to reload.
+       *
+       * @see {@link Player#event:sourceset}
+       * @event Tech#sourceset
+       * @type {EventTarget~Event}
+       */
+      this.trigger({
+        src: src,
+        type: 'sourceset'
+      });
+    };
+
+    /* Fallbacks for unsupported event types
+    ================================================================================ */
+
+    /**
+     * Polyfill the `progress` event for browsers that don't support it natively.
+     *
+     * @see {@link Tech#trackProgress}
+     */
+
+
+    Tech.prototype.manualProgressOn = function manualProgressOn() {
+      this.on('durationchange', this.onDurationChange);
+
+      this.manualProgress = true;
+
+      // Trigger progress watching when a source begins loading
+      this.one('ready', this.trackProgress);
+    };
+
+    /**
+     * Turn off the polyfill for `progress` events that was created in
+     * {@link Tech#manualProgressOn}
+     */
+
+
+    Tech.prototype.manualProgressOff = function manualProgressOff() {
+      this.manualProgress = false;
+      this.stopTrackingProgress();
+
+      this.off('durationchange', this.onDurationChange);
+    };
+
+    /**
+     * This is used to trigger a `progress` event when the buffered percent changes. It
+     * sets an interval function that will be called every 500 milliseconds to check if the
+     * buffer end percent has changed.
+     *
+     * > This function is called by {@link Tech#manualProgressOn}
+     *
+     * @param {EventTarget~Event} event
+     *        The `ready` event that caused this to run.
+     *
+     * @listens Tech#ready
+     * @fires Tech#progress
+     */
+
+
+    Tech.prototype.trackProgress = function trackProgress(event) {
+      this.stopTrackingProgress();
+      this.progressInterval = this.setInterval(bind(this, function () {
+        // Don't trigger unless buffered amount is greater than last time
+
+        var numBufferedPercent = this.bufferedPercent();
+
+        if (this.bufferedPercent_ !== numBufferedPercent) {
+          /**
+           * See {@link Player#progress}
+           *
+           * @event Tech#progress
+           * @type {EventTarget~Event}
+           */
+          this.trigger('progress');
+        }
+
+        this.bufferedPercent_ = numBufferedPercent;
+
+        if (numBufferedPercent === 1) {
+          this.stopTrackingProgress();
+        }
+      }), 500);
+    };
+
+    /**
+     * Update our internal duration on a `durationchange` event by calling
+     * {@link Tech#duration}.
+     *
+     * @param {EventTarget~Event} event
+     *        The `durationchange` event that caused this to run.
+     *
+     * @listens Tech#durationchange
+     */
+
+
+    Tech.prototype.onDurationChange = function onDurationChange(event) {
+      this.duration_ = this.duration();
+    };
+
+    /**
+     * Get and create a `TimeRange` object for buffering.
+     *
+     * @return {TimeRange}
+     *         The time range object that was created.
+     */
+
+
+    Tech.prototype.buffered = function buffered() {
+      return createTimeRanges(0, 0);
+    };
+
+    /**
+     * Get the percentage of the current video that is currently buffered.
+     *
+     * @return {number}
+     *         A number from 0 to 1 that represents the decimal percentage of the
+     *         video that is buffered.
+     *
+     */
+
+
+    Tech.prototype.bufferedPercent = function bufferedPercent$$1() {
+      return bufferedPercent(this.buffered(), this.duration_);
+    };
+
+    /**
+     * Turn off the polyfill for `progress` events that was created in
+     * {@link Tech#manualProgressOn}
+     * Stop manually tracking progress events by clearing the interval that was set in
+     * {@link Tech#trackProgress}.
+     */
+
+
+    Tech.prototype.stopTrackingProgress = function stopTrackingProgress() {
+      this.clearInterval(this.progressInterval);
+    };
+
+    /**
+     * Polyfill the `timeupdate` event for browsers that don't support it.
+     *
+     * @see {@link Tech#trackCurrentTime}
+     */
+
+
+    Tech.prototype.manualTimeUpdatesOn = function manualTimeUpdatesOn() {
+      this.manualTimeUpdates = true;
+
+      this.on('play', this.trackCurrentTime);
+      this.on('pause', this.stopTrackingCurrentTime);
+    };
+
+    /**
+     * Turn off the polyfill for `timeupdate` events that was created in
+     * {@link Tech#manualTimeUpdatesOn}
+     */
+
+
+    Tech.prototype.manualTimeUpdatesOff = function manualTimeUpdatesOff() {
+      this.manualTimeUpdates = false;
+      this.stopTrackingCurrentTime();
+      this.off('play', this.trackCurrentTime);
+      this.off('pause', this.stopTrackingCurrentTime);
+    };
+
+    /**
+     * Sets up an interval function to track current time and trigger `timeupdate` every
+     * 250 milliseconds.
+     *
+     * @listens Tech#play
+     * @triggers Tech#timeupdate
+     */
+
+
+    Tech.prototype.trackCurrentTime = function trackCurrentTime() {
+      if (this.currentTimeInterval) {
+        this.stopTrackingCurrentTime();
+      }
+      this.currentTimeInterval = this.setInterval(function () {
+        /**
+         * Triggered at an interval of 250ms to indicated that time is passing in the video.
+         *
+         * @event Tech#timeupdate
+         * @type {EventTarget~Event}
+         */
+        this.trigger({type: 'timeupdate', target: this, manuallyTriggered: true});
+
+        // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
+      }, 250);
+    };
+
+    /**
+     * Stop the interval function created in {@link Tech#trackCurrentTime} so that the
+     * `timeupdate` event is no longer triggered.
+     *
+     * @listens {Tech#pause}
+     */
+
+
+    Tech.prototype.stopTrackingCurrentTime = function stopTrackingCurrentTime() {
+      this.clearInterval(this.currentTimeInterval);
+
+      // #1002 - if the video ends right before the next timeupdate would happen,
+      // the progress bar won't make it all the way to the end
+      this.trigger({type: 'timeupdate', target: this, manuallyTriggered: true});
+    };
+
+    /**
+     * Turn off all event polyfills, clear the `Tech`s {@link AudioTrackList},
+     * {@link VideoTrackList}, and {@link TextTrackList}, and dispose of this Tech.
+     *
+     * @fires Component#dispose
+     */
+
+
+    Tech.prototype.dispose = function dispose() {
+
+      // clear out all tracks because we can't reuse them between techs
+      this.clearTracks(NORMAL.names);
+
+      // Turn off any manual progress or timeupdate tracking
+      if (this.manualProgress) {
+        this.manualProgressOff();
+      }
+
+      if (this.manualTimeUpdates) {
+        this.manualTimeUpdatesOff();
+      }
+
+      _Component.prototype.dispose.call(this);
+    };
+
+    /**
+     * Clear out a single `TrackList` or an array of `TrackLists` given their names.
+     *
+     * > Note: Techs without source handlers should call this between sources for `video`
+     *         & `audio` tracks. You don't want to use them between tracks!
+     *
+     * @param {string[]|string} types
+     *        TrackList names to clear, valid names are `video`, `audio`, and
+     *        `text`.
+     */
+
+
+    Tech.prototype.clearTracks = function clearTracks(types) {
+      var _this3 = this;
+
+      types = [].concat(types);
+      // clear out all tracks because we can't reuse them between techs
+      types.forEach(function (type) {
+        var list = _this3[type + 'Tracks']() || [];
+        var i = list.length;
+
+        while (i--) {
+          var track = list[i];
+
+          if (type === 'text') {
+            _this3.removeRemoteTextTrack(track);
+          }
+          list.removeTrack(track);
+        }
+      });
+    };
+
+    /**
+     * Remove any TextTracks added via addRemoteTextTrack that are
+     * flagged for automatic garbage collection
+     */
+
+
+    Tech.prototype.cleanupAutoTextTracks = function cleanupAutoTextTracks() {
+      var list = this.autoRemoteTextTracks_ || [];
+      var i = list.length;
+
+      while (i--) {
+        var track = list[i];
+
+        this.removeRemoteTextTrack(track);
+      }
+    };
+
+    /**
+     * Reset the tech, which will removes all sources and reset the internal readyState.
+     *
+     * @abstract
+     */
+
+
+    Tech.prototype.reset = function reset() {
+    };
+
+    /**
+     * Get or set an error on the Tech.
+     *
+     * @param {MediaError} [err]
+     *        Error to set on the Tech
+     *
+     * @return {MediaError|null}
+     *         The current error object on the tech, or null if there isn't one.
+     */
+
+
+    Tech.prototype.error = function error(err) {
+      if (err !== undefined) {
+        this.error_ = new MediaError(err);
+        this.trigger('error');
+      }
+      return this.error_;
+    };
+
+    /**
+     * Returns the `TimeRange`s that have been played through for the current source.
+     *
+     * > NOTE: This implementation is incomplete. It does not track the played `TimeRange`.
+     *         It only checks whether the source has played at all or not.
+     *
+     * @return {TimeRange}
+     *         - A single time range if this video has played
+     *         - An empty set of ranges if not.
+     */
+
+
+    Tech.prototype.played = function played() {
+      if (this.hasStarted_) {
+        return createTimeRanges(0, 0);
+      }
+      return createTimeRanges();
+    };
+
+    /**
+     * Causes a manual time update to occur if {@link Tech#manualTimeUpdatesOn} was
+     * previously called.
+     *
+     * @fires Tech#timeupdate
+     */
+
+
+    Tech.prototype.setCurrentTime = function setCurrentTime() {
+      // improve the accuracy of manual timeupdates
+      if (this.manualTimeUpdates) {
+        /**
+         * A manual `timeupdate` event.
+         *
+         * @event Tech#timeupdate
+         * @type {EventTarget~Event}
+         */
+        this.trigger({type: 'timeupdate', target: this, manuallyTriggered: true});
+      }
+    };
+
+    /**
+     * Turn on listeners for {@link VideoTrackList}, {@link {AudioTrackList}, and
+     * {@link TextTrackList} events.
+     *
+     * This adds {@link EventTarget~EventListeners} for `addtrack`, and  `removetrack`.
+     *
+     * @fires Tech#audiotrackchange
+     * @fires Tech#videotrackchange
+     * @fires Tech#texttrackchange
+     */
+
+
+    Tech.prototype.initTrackListeners = function initTrackListeners() {
+      var _this4 = this;
+
+      /**
+       * Triggered when tracks are added or removed on the Tech {@link AudioTrackList}
+       *
+       * @event Tech#audiotrackchange
+       * @type {EventTarget~Event}
+       */
+
+      /**
+       * Triggered when tracks are added or removed on the Tech {@link VideoTrackList}
+       *
+       * @event Tech#videotrackchange
+       * @type {EventTarget~Event}
+       */
+
+      /**
+       * Triggered when tracks are added or removed on the Tech {@link TextTrackList}
+       *
+       * @event Tech#texttrackchange
+       * @type {EventTarget~Event}
+       */
+      NORMAL.names.forEach(function (name) {
+        var props = NORMAL[name];
+        var trackListChanges = function trackListChanges() {
+          _this4.trigger(name + 'trackchange');
+        };
+
+        var tracks = _this4[props.getterName]();
+
+        tracks.addEventListener('removetrack', trackListChanges);
+        tracks.addEventListener('addtrack', trackListChanges);
+
+        _this4.on('dispose', function () {
+          tracks.removeEventListener('removetrack', trackListChanges);
+          tracks.removeEventListener('addtrack', trackListChanges);
+        });
+      });
+    };
+
+    /**
+     * Emulate TextTracks using vtt.js if necessary
+     *
+     * @fires Tech#vttjsloaded
+     * @fires Tech#vttjserror
+     */
+
+
+    Tech.prototype.addWebVttScript_ = function addWebVttScript_() {
+      var _this5 = this;
+
+      if (window_1.WebVTT) {
+        return;
+      }
+
+      // Initially, Tech.el_ is a child of a dummy-div wait until the Component system
+      // signals that the Tech is ready at which point Tech.el_ is part of the DOM
+      // before inserting the WebVTT script
+      if (document_1.body.contains(this.el())) {
+
+        // load via require if available and vtt.js script location was not passed in
+        // as an option. novtt builds will turn the above require call into an empty object
+        // which will cause this if check to always fail.
+        if (!this.options_['vtt.js'] && isPlain(browserIndex) && Object.keys(browserIndex).length > 0) {
+          this.trigger('vttjsloaded');
+          return;
+        }
+
+        // load vtt.js via the script location option or the cdn of no location was
+        // passed in
+        var script = document_1.createElement('script');
+
+        script.src = this.options_['vtt.js'] || 'https://vjs.zencdn.net/vttjs/0.14.1/vtt.min.js';
+        script.onload = function () {
+          /**
+           * Fired when vtt.js is loaded.
+           *
+           * @event Tech#vttjsloaded
+           * @type {EventTarget~Event}
+           */
+          _this5.trigger('vttjsloaded');
+        };
+        script.onerror = function () {
+          /**
+           * Fired when vtt.js was not loaded due to an error
+           *
+           * @event Tech#vttjsloaded
+           * @type {EventTarget~Event}
+           */
+          _this5.trigger('vttjserror');
+        };
+        this.on('dispose', function () {
+          script.onload = null;
+          script.onerror = null;
+        });
+        // but have not loaded yet and we set it to true before the inject so that
+        // we don't overwrite the injected window.WebVTT if it loads right away
+        window_1.WebVTT = true;
+        this.el().parentNode.appendChild(script);
+      } else {
+        this.ready(this.addWebVttScript_);
+      }
+    };
+
+    /**
+     * Emulate texttracks
+     *
+     */
+
+
+    Tech.prototype.emulateTextTracks = function emulateTextTracks() {
+      var _this6 = this;
+
+      var tracks = this.textTracks();
+      var remoteTracks = this.remoteTextTracks();
+      var handleAddTrack = function handleAddTrack(e) {
+        return tracks.addTrack(e.track);
+      };
+      var handleRemoveTrack = function handleRemoveTrack(e) {
+        return tracks.removeTrack(e.track);
+      };
+
+      remoteTracks.on('addtrack', handleAddTrack);
+      remoteTracks.on('removetrack', handleRemoveTrack);
+
+      this.addWebVttScript_();
+
+      var updateDisplay = function updateDisplay() {
+        return _this6.trigger('texttrackchange');
+      };
+
+      var textTracksChanges = function textTracksChanges() {
+        updateDisplay();
+
+        for (var i = 0; i < tracks.length; i++) {
+          var track = tracks[i];
+
+          track.removeEventListener('cuechange', updateDisplay);
+          if (track.mode === 'showing') {
+            track.addEventListener('cuechange', updateDisplay);
+          }
+        }
+      };
+
+      textTracksChanges();
+      tracks.addEventListener('change', textTracksChanges);
+      tracks.addEventListener('addtrack', textTracksChanges);
+      tracks.addEventListener('removetrack', textTracksChanges);
+
+      this.on('dispose', function () {
+        remoteTracks.off('addtrack', handleAddTrack);
+        remoteTracks.off('removetrack', handleRemoveTrack);
+        tracks.removeEventListener('change', textTracksChanges);
+        tracks.removeEventListener('addtrack', textTracksChanges);
+        tracks.removeEventListener('removetrack', textTracksChanges);
+
+        for (var i = 0; i < tracks.length; i++) {
+          var track = tracks[i];
+
+          track.removeEventListener('cuechange', updateDisplay);
+        }
+      });
+    };
+
+    /**
+     * Create and returns a remote {@link TextTrack} object.
+     *
+     * @param {string} kind
+     *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
+     *
+     * @param {string} [label]
+     *        Label to identify the text track
+     *
+     * @param {string} [language]
+     *        Two letter language abbreviation
+     *
+     * @return {TextTrack}
+     *         The TextTrack that gets created.
+     */
+
+
+    Tech.prototype.addTextTrack = function addTextTrack(kind, label, language) {
+      if (!kind) {
+        throw new Error('TextTrack kind is required but was not provided');
+      }
+
+      return createTrackHelper(this, kind, label, language);
+    };
+
+    /**
+     * Create an emulated TextTrack for use by addRemoteTextTrack
+     *
+     * This is intended to be overridden by classes that inherit from
+     * Tech in order to create native or custom TextTracks.
+     *
+     * @param {Object} options
+     *        The object should contain the options to initialize the TextTrack with.
+     *
+     * @param {string} [options.kind]
+     *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata).
+     *
+     * @param {string} [options.label].
+     *        Label to identify the text track
+     *
+     * @param {string} [options.language]
+     *        Two letter language abbreviation.
+     *
+     * @return {HTMLTrackElement}
+     *         The track element that gets created.
+     */
+
+
+    Tech.prototype.createRemoteTextTrack = function createRemoteTextTrack(options) {
+      var track = mergeOptions(options, {
+        tech: this
+      });
+
+      return new REMOTE.remoteTextEl.TrackClass(track);
+    };
+
+    /**
+     * Creates a remote text track object and returns an html track element.
+     *
+     * > Note: This can be an emulated {@link HTMLTrackElement} or a native one.
+     *
+     * @param {Object} options
+     *        See {@link Tech#createRemoteTextTrack} for more detailed properties.
+     *
+     * @param {boolean} [manualCleanup=true]
+     *        - When false: the TextTrack will be automatically removed from the video
+     *          element whenever the source changes
+     *        - When True: The TextTrack will have to be cleaned up manually
+     *
+     * @return {HTMLTrackElement}
+     *         An Html Track Element.
+     *
+     * @deprecated The default functionality for this function will be equivalent
+     *             to "manualCleanup=false" in the future. The manualCleanup parameter will
+     *             also be removed.
+     */
+
+
+    Tech.prototype.addRemoteTextTrack = function addRemoteTextTrack() {
+      var _this7 = this;
+
+      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+      var manualCleanup = arguments[1];
+
+      var htmlTrackElement = this.createRemoteTextTrack(options);
+
+      if (manualCleanup !== true && manualCleanup !== false) {
+        // deprecation warning
+        log$1.warn('Calling addRemoteTextTrack without explicitly setting the "manualCleanup" parameter to `true` is deprecated and default to `false` in future version of video.js');
+        manualCleanup = true;
+      }
+
+      // store HTMLTrackElement and TextTrack to remote list
+      this.remoteTextTrackEls().addTrackElement_(htmlTrackElement);
+      this.remoteTextTracks().addTrack(htmlTrackElement.track);
+
+      if (manualCleanup !== true) {
+        // create the TextTrackList if it doesn't exist
+        this.ready(function () {
+          return _this7.autoRemoteTextTracks_.addTrack(htmlTrackElement.track);
+        });
+      }
+
+      return htmlTrackElement;
+    };
+
+    /**
+     * Remove a remote text track from the remote `TextTrackList`.
+     *
+     * @param {TextTrack} track
+     *        `TextTrack` to remove from the `TextTrackList`
+     */
+
+
+    Tech.prototype.removeRemoteTextTrack = function removeRemoteTextTrack(track) {
+      var trackElement = this.remoteTextTrackEls().getTrackElementByTrack_(track);
+
+      // remove HTMLTrackElement and TextTrack from remote list
+      this.remoteTextTrackEls().removeTrackElement_(trackElement);
+      this.remoteTextTracks().removeTrack(track);
+      this.autoRemoteTextTracks_.removeTrack(track);
+    };
+
+    /**
+     * Gets available media playback quality metrics as specified by the W3C's Media
+     * Playback Quality API.
+     *
+     * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
+     *
+     * @return {Object}
+     *         An object with supported media playback quality metrics
+     *
+     * @abstract
+     */
+
+
+    Tech.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
+      return {};
+    };
+
+    /**
+     * A method to set a poster from a `Tech`.
+     *
+     * @abstract
+     */
+
+
+    Tech.prototype.setPoster = function setPoster() {
+    };
+
+    /**
+     * A method to check for the presence of the 'playsinline' <video> attribute.
+     *
+     * @abstract
+     */
+
+
+    Tech.prototype.playsinline = function playsinline() {
+    };
+
+    /**
+     * A method to set or unset the 'playsinline' <video> attribute.
+     *
+     * @abstract
+     */
+
+
+    Tech.prototype.setPlaysinline = function setPlaysinline() {
+    };
+
+    /**
+     * Attempt to force override of native audio tracks.
+     *
+     * @param {Boolean} override - If set to true native audio will be overridden,
+     * otherwise native audio will potentially be used.
+     *
+     * @abstract
+     */
+
+
+    Tech.prototype.overrideNativeAudioTracks = function overrideNativeAudioTracks() {
+    };
+
+    /**
+     * Attempt to force override of native video tracks.
+     *
+     * @param {Boolean} override - If set to true native video will be overridden,
+     * otherwise native video will potentially be used.
+     *
+     * @abstract
+     */
+
+
+    Tech.prototype.overrideNativeVideoTracks = function overrideNativeVideoTracks() {
+    };
+
+    /*
+     * Check if the tech can support the given mime-type.
+     *
+     * The base tech does not support any type, but source handlers might
+     * overwrite this.
+     *
+     * @param  {string} type
+     *         The mimetype to check for support
+     *
+     * @return {string}
+     *         'probably', 'maybe', or empty string
+     *
+     * @see [Spec]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canPlayType}
+     *
+     * @abstract
+     */
+
+
+    Tech.prototype.canPlayType = function canPlayType() {
+      return '';
+    };
+
+    /**
+     * Check if the type is supported by this tech.
+     *
+     * The base tech does not support any type, but source handlers might
+     * overwrite this.
+     *
+     * @param {string} type
+     *        The media type to check
+     * @return {string} Returns the native video element's response
+     */
+
+
+    Tech.canPlayType = function canPlayType() {
+      return '';
+    };
+
+    /**
+     * Check if the tech can support the given source
+     * @param {Object} srcObj
+     *        The source object
+     * @param {Object} options
+     *        The options passed to the tech
+     * @return {string} 'probably', 'maybe', or '' (empty string)
+     */
+
+
+    Tech.canPlaySource = function canPlaySource(srcObj, options) {
+      return Tech.canPlayType(srcObj.type);
+    };
+
+    /*
+     * Return whether the argument is a Tech or not.
+     * Can be passed either a Class like `Html5` or a instance like `player.tech_`
+     *
+     * @param {Object} component
+     *        The item to check
+     *
+     * @return {boolean}
+     *         Whether it is a tech or not
+     *         - True if it is a tech
+     *         - False if it is not
+     */
+
+
+    Tech.isTech = function isTech(component) {
+      return component.prototype instanceof Tech || component instanceof Tech || component === Tech;
+    };
+
+    /**
+     * Registers a `Tech` into a shared list for videojs.
+     *
+     * @param {string} name
+     *        Name of the `Tech` to register.
+     *
+     * @param {Object} tech
+     *        The `Tech` class to register.
+     */
+
+
+    Tech.registerTech = function registerTech(name, tech) {
+      if (!Tech.techs_) {
+        Tech.techs_ = {};
+      }
+
+      if (!Tech.isTech(tech)) {
+        throw new Error('Tech ' + name + ' must be a Tech');
+      }
+
+      if (!Tech.canPlayType) {
+        throw new Error('Techs must have a static canPlayType method on them');
+      }
+      if (!Tech.canPlaySource) {
+        throw new Error('Techs must have a static canPlaySource method on them');
+      }
+
+      name = toTitleCase(name);
+
+      Tech.techs_[name] = tech;
+      if (name !== 'Tech') {
+        // camel case the techName for use in techOrder
+        Tech.defaultTechOrder_.push(name);
+      }
+      return tech;
+    };
+
+    /**
+     * Get a `Tech` from the shared list by name.
+     *
+     * @param {string} name
+     *        `camelCase` or `TitleCase` name of the Tech to get
+     *
+     * @return {Tech|undefined}
+     *         The `Tech` or undefined if there was no tech with the name requested.
+     */
+
+
+    Tech.getTech = function getTech(name) {
+      if (!name) {
+        return;
+      }
+
+      name = toTitleCase(name);
+
+      if (Tech.techs_ && Tech.techs_[name]) {
+        return Tech.techs_[name];
+      }
+
+      if (window_1 && window_1.videojs && window_1.videojs[name]) {
+        log$1.warn('The ' + name + ' tech was added to the videojs object when it should be registered using videojs.registerTech(name, tech)');
+        return window_1.videojs[name];
+      }
+    };
+
+    return Tech;
+  }(Component);
+
+  /**
+   * Get the {@link VideoTrackList}
+   *
+   * @returns {VideoTrackList}
+   * @method Tech.prototype.videoTracks
+   */
+
+  /**
+   * Get the {@link AudioTrackList}
+   *
+   * @returns {AudioTrackList}
+   * @method Tech.prototype.audioTracks
+   */
+
+  /**
+   * Get the {@link TextTrackList}
+   *
+   * @returns {TextTrackList}
+   * @method Tech.prototype.textTracks
+   */
+
+  /**
+   * Get the remote element {@link TextTrackList}
+   *
+   * @returns {TextTrackList}
+   * @method Tech.prototype.remoteTextTracks
+   */
+
+  /**
+   * Get the remote element {@link HtmlTrackElementList}
+   *
+   * @returns {HtmlTrackElementList}
+   * @method Tech.prototype.remoteTextTrackEls
+   */
+
+  ALL.names.forEach(function (name) {
+    var props = ALL[name];
+
+    Tech.prototype[props.getterName] = function () {
+      this[props.privateName] = this[props.privateName] || new props.ListClass();
+      return this[props.privateName];
+    };
+  });
+
+  /**
+   * List of associated text tracks
+   *
+   * @type {TextTrackList}
+   * @private
+   * @property Tech#textTracks_
+   */
+
+  /**
+   * List of associated audio tracks.
+   *
+   * @type {AudioTrackList}
+   * @private
+   * @property Tech#audioTracks_
+   */
+
+  /**
+   * List of associated video tracks.
+   *
+   * @type {VideoTrackList}
+   * @private
+   * @property Tech#videoTracks_
+   */
+
+  /**
+   * Boolean indicating whether the `Tech` supports volume control.
+   *
+   * @type {boolean}
+   * @default
+   */
+  Tech.prototype.featuresVolumeControl = true;
+
+  /**
+   * Boolean indicating whether the `Tech` supports fullscreen resize control.
+   * Resizing plugins using request fullscreen reloads the plugin
+   *
+   * @type {boolean}
+   * @default
+   */
+  Tech.prototype.featuresFullscreenResize = false;
+
+  /**
+   * Boolean indicating whether the `Tech` supports changing the speed at which the video
+   * plays. Examples:
+   *   - Set player to playOld 2x (twice) as fast
+   *   - Set player to playOld 0.5x (half) as fast
+   *
+   * @type {boolean}
+   * @default
+   */
+  Tech.prototype.featuresPlaybackRate = false;
+
+  /**
+   * Boolean indicating whether the `Tech` supports the `progress` event. This is currently
+   * not triggered by video-js-swf. This will be used to determine if
+   * {@link Tech#manualProgressOn} should be called.
+   *
+   * @type {boolean}
+   * @default
+   */
+  Tech.prototype.featuresProgressEvents = false;
+
+  /**
+   * Boolean indicating whether the `Tech` supports the `sourceset` event.
+   *
+   * A tech should set this to `true` and then use {@link Tech#triggerSourceset}
+   * to trigger a {@link Tech#event:sourceset} at the earliest time after getting
+   * a new source.
+   *
+   * @type {boolean}
+   * @default
+   */
+  Tech.prototype.featuresSourceset = false;
+
+  /**
+   * Boolean indicating whether the `Tech` supports the `timeupdate` event. This is currently
+   * not triggered by video-js-swf. This will be used to determine if
+   * {@link Tech#manualTimeUpdates} should be called.
+   *
+   * @type {boolean}
+   * @default
+   */
+  Tech.prototype.featuresTimeupdateEvents = false;
+
+  /**
+   * Boolean indicating whether the `Tech` supports the native `TextTrack`s.
+   * This will help us integrate with native `TextTrack`s if the browser supports them.
+   *
+   * @type {boolean}
+   * @default
+   */
+  Tech.prototype.featuresNativeTextTracks = false;
+
+  /**
+   * A functional mixin for techs that want to use the Source Handler pattern.
+   * Source handlers are scripts for handling specific formats.
+   * The source handler pattern is used for adaptive formats (HLS, DASH) that
+   * manually load video data and feed it into a Source Buffer (Media Source Extensions)
+   * Example: `Tech.withSourceHandlers.call(MyTech);`
+   *
+   * @param {Tech} _Tech
+   *        The tech to add source handler functions to.
+   *
+   * @mixes Tech~SourceHandlerAdditions
+   */
+  Tech.withSourceHandlers = function (_Tech) {
+
+    /**
+     * Register a source handler
+     *
+     * @param {Function} handler
+     *        The source handler class
+     *
+     * @param {number} [index]
+     *        Register it at the following index
+     */
+    _Tech.registerSourceHandler = function (handler, index) {
+      var handlers = _Tech.sourceHandlers;
+
+      if (!handlers) {
+        handlers = _Tech.sourceHandlers = [];
+      }
+
+      if (index === undefined) {
+        // add to the end of the list
+        index = handlers.length;
+      }
+
+      handlers.splice(index, 0, handler);
+    };
+
+    /**
+     * Check if the tech can support the given type. Also checks the
+     * Techs sourceHandlers.
+     *
+     * @param {string} type
+     *         The mimetype to check.
+     *
+     * @return {string}
+     *         'probably', 'maybe', or '' (empty string)
+     */
+    _Tech.canPlayType = function (type) {
+      var handlers = _Tech.sourceHandlers || [];
+      var can = void 0;
+
+      for (var i = 0; i < handlers.length; i++) {
+        can = handlers[i].canPlayType(type);
+
+        if (can) {
+          return can;
+        }
+      }
+
+      return '';
+    };
+
+    /**
+     * Returns the first source handler that supports the source.
+     *
+     * TODO: Answer question: should 'probably' be prioritized over 'maybe'
+     *
+     * @param {Tech~SourceObject} source
+     *        The source object
+     *
+     * @param {Object} options
+     *        The options passed to the tech
+     *
+     * @return {SourceHandler|null}
+     *          The first source handler that supports the source or null if
+     *          no SourceHandler supports the source
+     */
+    _Tech.selectSourceHandler = function (source, options) {
+      var handlers = _Tech.sourceHandlers || [];
+      var can = void 0;
+
+      for (var i = 0; i < handlers.length; i++) {
+        can = handlers[i].canHandleSource(source, options);
+
+        if (can) {
+          return handlers[i];
+        }
+      }
+
+      return null;
+    };
+
+    /**
+     * Check if the tech can support the given source.
+     *
+     * @param {Tech~SourceObject} srcObj
+     *        The source object
+     *
+     * @param {Object} options
+     *        The options passed to the tech
+     *
+     * @return {string}
+     *         'probably', 'maybe', or '' (empty string)
+     */
+    _Tech.canPlaySource = function (srcObj, options) {
+      var sh = _Tech.selectSourceHandler(srcObj, options);
+
+      if (sh) {
+        return sh.canHandleSource(srcObj, options);
+      }
+
+      return '';
+    };
+
+    /**
+     * When using a source handler, prefer its implementation of
+     * any function normally provided by the tech.
+     */
+    var deferrable = ['seekable', 'seeking', 'duration'];
+
+    /**
+     * A wrapper around {@link Tech#seekable} that will call a `SourceHandler`s seekable
+     * function if it exists, with a fallback to the Techs seekable function.
+     *
+     * @method _Tech.seekable
+     */
+
+    /**
+     * A wrapper around {@link Tech#duration} that will call a `SourceHandler`s duration
+     * function if it exists, otherwise it will fallback to the techs duration function.
+     *
+     * @method _Tech.duration
+     */
+
+    deferrable.forEach(function (fnName) {
+      var originalFn = this[fnName];
+
+      if (typeof originalFn !== 'function') {
+        return;
+      }
+
+      this[fnName] = function () {
+        if (this.sourceHandler_ && this.sourceHandler_[fnName]) {
+          return this.sourceHandler_[fnName].apply(this.sourceHandler_, arguments);
+        }
+        return originalFn.apply(this, arguments);
+      };
+    }, _Tech.prototype);
+
+    /**
+     * Create a function for setting the source using a source object
+     * and source handlers.
+     * Should never be called unless a source handler was found.
+     *
+     * @param {Tech~SourceObject} source
+     *        A source object with src and type keys
+     */
+    _Tech.prototype.setSource = function (source) {
+      var sh = _Tech.selectSourceHandler(source, this.options_);
+
+      if (!sh) {
+        // Fall back to a native source hander when unsupported sources are
+        // deliberately set
+        if (_Tech.nativeSourceHandler) {
+          sh = _Tech.nativeSourceHandler;
+        } else {
+          log$1.error('No source handler found for the current source.');
+        }
+      }
+
+      // Dispose any existing source handler
+      this.disposeSourceHandler();
+      this.off('dispose', this.disposeSourceHandler);
+
+      if (sh !== _Tech.nativeSourceHandler) {
+        this.currentSource_ = source;
+      }
+
+      this.sourceHandler_ = sh.handleSource(source, this, this.options_);
+      this.on('dispose', this.disposeSourceHandler);
+    };
+
+    /**
+     * Clean up any existing SourceHandlers and listeners when the Tech is disposed.
+     *
+     * @listens Tech#dispose
+     */
+    _Tech.prototype.disposeSourceHandler = function () {
+      // if we have a source and get another one
+      // then we are loading something new
+      // than clear all of our current tracks
+      if (this.currentSource_) {
+        this.clearTracks(['audio', 'video']);
+        this.currentSource_ = null;
+      }
+
+      // always clean up auto-text tracks
+      this.cleanupAutoTextTracks();
+
+      if (this.sourceHandler_) {
+
+        if (this.sourceHandler_.dispose) {
+          this.sourceHandler_.dispose();
+        }
+
+        this.sourceHandler_ = null;
+      }
+    };
+  };
+
+  // The base Tech class needs to be registered as a Component. It is the only
+  // Tech that can be registered as a Component.
+  Component.registerComponent('Tech', Tech);
+  Tech.registerTech('Tech', Tech);
+
+  /**
+   * A list of techs that should be added to techOrder on Players
+   *
+   * @private
+   */
+  Tech.defaultTechOrder_ = [];
+
+  var middlewares = {};
+  var middlewareInstances = {};
+
+  var TERMINATOR = {};
+
+  function use(type, middleware) {
+    middlewares[type] = middlewares[type] || [];
+    middlewares[type].push(middleware);
+  }
+
+  function setSource(player, src, next) {
+    player.setTimeout(function () {
+      return setSourceHelper(src, middlewares[src.type], next, player);
+    }, 1);
+  }
+
+  function setTech(middleware, tech) {
+    middleware.forEach(function (mw) {
+      return mw.setTech && mw.setTech(tech);
+    });
+  }
+
+  /**
+   * Calls a getter on the tech first, through each middleware
+   * from right to left to the player.
+   */
+  function get$1(middleware, tech, method) {
+    return middleware.reduceRight(middlewareIterator(method), tech[method]());
+  }
+
+  /**
+   * Takes the argument given to the player and calls the setter method on each
+   * middleware from left to right to the tech.
+   */
+  function set$1(middleware, tech, method, arg) {
+    return tech[method](middleware.reduce(middlewareIterator(method), arg));
+  }
+
+  /**
+   * Takes the argument given to the player and calls the `call` version of the method
+   * on each middleware from left to right.
+   * Then, call the passed in method on the tech and return the result unchanged
+   * back to the player, through middleware, this time from right to left.
+   */
+  function mediate(middleware, tech, method) {
+    var arg = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
+
+    var callMethod = 'call' + toTitleCase(method);
+    var middlewareValue = middleware.reduce(middlewareIterator(callMethod), arg);
+    var terminated = middlewareValue === TERMINATOR;
+    var returnValue = terminated ? null : tech[method](middlewareValue);
+
+    executeRight(middleware, method, returnValue, terminated);
+
+    return returnValue;
+  }
+
+  var allowedGetters = {
+    buffered: 1,
+    currentTime: 1,
+    duration: 1,
+    seekable: 1,
+    played: 1,
+    paused: 1
+  };
+
+  var allowedSetters = {
+    setCurrentTime: 1
+  };
+
+  var allowedMediators = {
+    play: 1,
+    pause: 1
+  };
+
+  function middlewareIterator(method) {
+    return function (value, mw) {
+      // if the previous middleware terminated, pass along the termination
+      if (value === TERMINATOR) {
+        return TERMINATOR;
+      }
+
+      if (mw[method]) {
+        return mw[method](value);
+      }
+
+      return value;
+    };
+  }
+
+  function executeRight(mws, method, value, terminated) {
+    for (var i = mws.length - 1; i >= 0; i--) {
+      var mw = mws[i];
+
+      if (mw[method]) {
+        mw[method](terminated, value);
+      }
+    }
+  }
+
+  function clearCacheForPlayer(player) {
+    middlewareInstances[player.id()] = null;
+  }
+
+  /**
+   * {
+   *  [playerId]: [[mwFactory, mwInstance], ...]
+   * }
+   */
+  function getOrCreateFactory(player, mwFactory) {
+    var mws = middlewareInstances[player.id()];
+    var mw = null;
+
+    if (mws === undefined || mws === null) {
+      mw = mwFactory(player);
+      middlewareInstances[player.id()] = [[mwFactory, mw]];
+      return mw;
+    }
+
+    for (var i = 0; i < mws.length; i++) {
+      var _mws$i = mws[i],
+        mwf = _mws$i[0],
+        mwi = _mws$i[1];
+
+
+      if (mwf !== mwFactory) {
+        continue;
+      }
+
+      mw = mwi;
+    }
+
+    if (mw === null) {
+      mw = mwFactory(player);
+      mws.push([mwFactory, mw]);
+    }
+
+    return mw;
+  }
+
+  function setSourceHelper() {
+    var src = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+    var middleware = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
+    var next = arguments[2];
+    var player = arguments[3];
+    var acc = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : [];
+    var lastRun = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;
+    var mwFactory = middleware[0],
+      mwrest = middleware.slice(1);
+
+    // if mwFactory is a string, then we're at a fork in the road
+
+    if (typeof mwFactory === 'string') {
+      setSourceHelper(src, middlewares[mwFactory], next, player, acc, lastRun);
+
+      // if we have an mwFactory, call it with the player to get the mw,
+      // then call the mw's setSource method
+    } else if (mwFactory) {
+      var mw = getOrCreateFactory(player, mwFactory);
+
+      mw.setSource(assign({}, src), function (err, _src) {
+
+        // something happened, try the next middleware on the current level
+        // make sure to use the old src
+        if (err) {
+          return setSourceHelper(src, mwrest, next, player, acc, lastRun);
+        }
+
+        // we've succeeded, now we need to go deeper
+        acc.push(mw);
+
+        // if it's the same type, continue down the current chain
+        // otherwise, we want to go down the new chain
+        setSourceHelper(_src, src.type === _src.type ? mwrest : middlewares[_src.type], next, player, acc, lastRun);
+      });
+    } else if (mwrest.length) {
+      setSourceHelper(src, mwrest, next, player, acc, lastRun);
+    } else if (lastRun) {
+      next(src, acc);
+    } else {
+      setSourceHelper(src, middlewares['*'], next, player, acc, true);
+    }
+  }
+
+  /**
+   * Mimetypes
+   *
+   * @see http://hul.harvard.edu/ois/////systems/wax/wax-public-help/mimetypes.htm
+   * @typedef Mimetypes~Kind
+   * @enum
+   */
+  var MimetypesKind = {
+    opus: 'video/ogg',
+    ogv: 'video/ogg',
+    mp4: 'video/mp4',
+    mov: 'video/mp4',
+    m4v: 'video/mp4',
+    mkv: 'video/x-matroska',
+    mp3: 'audio/mpeg',
+    aac: 'audio/aac',
+    oga: 'audio/ogg',
+    m3u8: 'application/x-mpegURL'
+  };
+
+  /**
+   * Get the mimetype of a given src url if possible
+   *
+   * @param {string} src
+   *        The url to the src
+   *
+   * @return {string}
+   *         return the mimetype if it was known or empty string otherwise
+   */
+  var getMimetype = function getMimetype() {
+    var src = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
+
+    var ext = getFileExtension(src);
+    var mimetype = MimetypesKind[ext.toLowerCase()];
+
+    return mimetype || '';
+  };
+
+  /**
+   * Find the mime type of a given source string if possible. Uses the player
+   * source cache.
+   *
+   * @param {Player} player
+   *        The player object
+   *
+   * @param {string} src
+   *        The source string
+   *
+   * @return {string}
+   *         The type that was found
+   */
+  var findMimetype = function findMimetype(player, src) {
+    if (!src) {
+      return '';
+    }
+
+    // 1. check for the type in the `source` cache
+    if (player.cache_.source.src === src && player.cache_.source.type) {
+      return player.cache_.source.type;
+    }
+
+    // 2. see if we have this source in our `currentSources` cache
+    var matchingSources = player.cache_.sources.filter(function (s) {
+      return s.src === src;
+    });
+
+    if (matchingSources.length) {
+      return matchingSources[0].type;
+    }
+
+    // 3. look for the src url in source elements and use the type there
+    var sources = player.$$('source');
+
+    for (var i = 0; i < sources.length; i++) {
+      var s = sources[i];
+
+      if (s.type && s.src && s.src === src) {
+        return s.type;
+      }
+    }
+
+    // 4. finally fallback to our list of mime types based on src url extension
+    return getMimetype(src);
+  };
+
+  /**
+   * @module filter-source
+   */
+
+  /**
+   * Filter out single bad source objects or multiple source objects in an
+   * array. Also flattens nested source object arrays into a 1 dimensional
+   * array of source objects.
+   *
+   * @param {Tech~SourceObject|Tech~SourceObject[]} src
+   *        The src object to filter
+   *
+   * @return {Tech~SourceObject[]}
+   *         An array of sourceobjects containing only valid sources
+   *
+   * @private
+   */
+  var filterSource = function filterSource(src) {
+    // traverse array
+    if (Array.isArray(src)) {
+      var newsrc = [];
+
+      src.forEach(function (srcobj) {
+        srcobj = filterSource(srcobj);
+
+        if (Array.isArray(srcobj)) {
+          newsrc = newsrc.concat(srcobj);
+        } else if (isObject(srcobj)) {
+          newsrc.push(srcobj);
+        }
+      });
+
+      src = newsrc;
+    } else if (typeof src === 'string' && src.trim()) {
+      // convert string into object
+      src = [fixSource({src: src})];
+    } else if (isObject(src) && typeof src.src === 'string' && src.src && src.src.trim()) {
+      // src is already valid
+      src = [fixSource(src)];
+    } else {
+      // invalid source, turn it into an empty array
+      src = [];
+    }
+
+    return src;
+  };
+
+  /**
+   * Checks src mimetype, adding it when possible
+   *
+   * @param {Tech~SourceObject} src
+   *        The src object to check
+   * @return {Tech~SourceObject}
+   *        src Object with known type
+   */
+  function fixSource(src) {
+    var mimetype = getMimetype(src.src);
+
+    if (!src.type && mimetype) {
+      src.type = mimetype;
+    }
+
+    return src;
+  }
+
+  /**
+   * @file loader.js
+   */
+
+  /**
+   * The `MediaLoader` is the `Component` that decides which playback technology to load
+   * when a player is initialized.
+   *
+   * @extends Component
+   */
+
+  var MediaLoader = function (_Component) {
+    inherits(MediaLoader, _Component);
+
+    /**
+     * Create an instance of this class.
+     *
+     * @param {Player} player
+     *        The `Player` that this class should attach to.
+     *
+     * @param {Object} [options]
+     *        The key/value store of player options.
+     *
+     * @param {Component~ReadyCallback} [ready]
+     *        The function that is run when this component is ready.
+     */
+    function MediaLoader(player, options, ready) {
+      classCallCheck(this, MediaLoader);
+
+      // MediaLoader has no element
+      var options_ = mergeOptions({createEl: false}, options);
+
+      // If there are no sources when the player is initialized,
+      // load the first supported playback technology.
+
+      var _this = possibleConstructorReturn(this, _Component.call(this, player, options_, ready));
+
+      if (!options.playerOptions.sources || options.playerOptions.sources.length === 0) {
+        for (var i = 0, j = options.playerOptions.techOrder; i < j.length; i++) {
+          var techName = toTitleCase(j[i]);
+          var tech = Tech.getTech(techName);
+
+          // Support old behavior of techs being registered as components.
+          // Remove once that deprecated behavior is removed.
+          if (!techName) {
+            tech = Component.getComponent(techName);
+          }
+
+          // Check if the browser supports this technology
+          if (tech && tech.isSupported()) {
+            player.loadTech_(techName);
+            break;
+          }
+        }
+      } else {
+        // Loop through playback technologies (HTML5, Flash) and check for support.
+        // Then load the best source.
+        // A few assumptions here:
+        //   All playback technologies respect preload false.
+        player.src(options.playerOptions.sources);
+      }
+      return _this;
+    }
+
+    return MediaLoader;
+  }(Component);
+
+  Component.registerComponent('MediaLoader', MediaLoader);
+
+  /**
+   * @file clickable-component.js
+   */
+
+  /**
+   * Clickable Component which is clickable or keyboard actionable,
+   * but is not a native HTML button.
+   *
+   * @extends Component
+   */
+
+  var ClickableComponent = function (_Component) {
+    inherits(ClickableComponent, _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.
+     */
+    function ClickableComponent(player, options) {
+      classCallCheck(this, ClickableComponent);
+
+      var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
+
+      _this.emitTapEvents();
+
+      _this.enable();
+      return _this;
+    }
+
+    /**
+     * Create the `Component`s DOM element.
+     *
+     * @param {string} [tag=div]
+     *        The element's node type.
+     *
+     * @param {Object} [props={}]
+     *        An object of properties that should be set on the element.
+     *
+     * @param {Object} [attributes={}]
+     *        An object of attributes that should be set on the element.
+     *
+     * @return {Element}
+     *         The element that gets created.
+     */
+
+
+    ClickableComponent.prototype.createEl = function createEl$$1() {
+      var tag = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'div';
+      var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+      var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
+
+      props = assign({
+        innerHTML: '<span aria-hidden="true" class="vjs-icon-placeholder"></span>',
+        className: this.buildCSSClass(),
+        tabIndex: 0
+      }, props);
+
+      if (tag === 'button') {
+        log$1.error('Creating a ClickableComponent with an HTML element of ' + tag + ' is not supported; use a Button instead.');
+      }
+
+      // Add ARIA attributes for clickable element which is not a native HTML button
+      attributes = assign({
+        role: 'button'
+      }, attributes);
+
+      this.tabIndex_ = props.tabIndex;
+
+      var el = _Component.prototype.createEl.call(this, tag, props, attributes);
+
+      this.createControlTextEl(el);
+
+      return el;
+    };
+
+    ClickableComponent.prototype.dispose = function dispose() {
+      // remove controlTextEl_ on dispose
+      this.controlTextEl_ = null;
+
+      _Component.prototype.dispose.call(this);
+    };
+
+    /**
+     * Create a control text element on this `Component`
+     *
+     * @param {Element} [el]
+     *        Parent element for the control text.
+     *
+     * @return {Element}
+     *         The control text element that gets created.
+     */
+
+
+    ClickableComponent.prototype.createControlTextEl = function createControlTextEl(el) {
+      this.controlTextEl_ = createEl('span', {
+        className: 'vjs-control-text'
+      }, {
+        // let the screen reader user know that the text of the element may change
+        'aria-live': 'polite'
+      });
+
+      if (el) {
+        el.appendChild(this.controlTextEl_);
+      }
+
+      this.controlText(this.controlText_, el);
+
+      return this.controlTextEl_;
+    };
+
+    /**
+     * Get or set the localize text to use for the controls on the `Component`.
+     *
+     * @param {string} [text]
+     *        Control text for element.
+     *
+     * @param {Element} [el=this.el()]
+     *        Element to set the title on.
+     *
+     * @return {string}
+     *         - The control text when getting
+     */
+
+
+    ClickableComponent.prototype.controlText = function controlText(text) {
+      var el = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.el();
+
+      if (text === undefined) {
+        return this.controlText_ || 'Need Text';
+      }
+
+      var localizedText = this.localize(text);
+
+      this.controlText_ = text;
+      textContent(this.controlTextEl_, localizedText);
+      if (!this.nonIconControl) {
+        // Set title attribute if only an icon is shown
+        el.setAttribute('title', localizedText);
+      }
+    };
+
+    /**
+     * Builds the default DOM `className`.
+     *
+     * @return {string}
+     *         The DOM `className` for this object.
+     */
+
+
+    ClickableComponent.prototype.buildCSSClass = function buildCSSClass() {
+      return 'vjs-control vjs-button ' + _Component.prototype.buildCSSClass.call(this);
+    };
+
+    /**
+     * Enable this `Component`s element.
+     */
+
+
+    ClickableComponent.prototype.enable = function enable() {
+      if (!this.enabled_) {
+        this.enabled_ = true;
+        this.removeClass('vjs-disabled');
+        this.el_.setAttribute('aria-disabled', 'false');
+        if (typeof this.tabIndex_ !== 'undefined') {
+          this.el_.setAttribute('tabIndex', this.tabIndex_);
+        }
+        this.on(['tap', 'click'], this.handleClick);
+        this.on('focus', this.handleFocus);
+        this.on('blur', this.handleBlur);
+      }
+    };
+
+    /**
+     * Disable this `Component`s element.
+     */
+
+
+    ClickableComponent.prototype.disable = function disable() {
+      this.enabled_ = false;
+      this.addClass('vjs-disabled');
+      this.el_.setAttribute('aria-disabled', 'true');
+      if (typeof this.tabIndex_ !== 'undefined') {
+        this.el_.removeAttribute('tabIndex');
+      }
+      this.off(['tap', 'click'], this.handleClick);
+      this.off('focus', this.handleFocus);
+      this.off('blur', this.handleBlur);
+    };
+
+    /**
+     * This gets called when a `ClickableComponent` gets:
+     * - Clicked (via the `click` event, listening starts in the constructor)
+     * - Tapped (via the `tap` event, listening starts in the constructor)
+     * - The following things happen in order:
+     *   1. {@link ClickableComponent#handleFocus} is called via a `focus` event on the
+     *      `ClickableComponent`.
+     *   2. {@link ClickableComponent#handleFocus} adds a listener for `keydown` on using
+     *      {@link ClickableComponent#handleKeyPress}.
+     *   3. `ClickableComponent` has not had a `blur` event (`blur` means that focus was lost). The user presses
+     *      the space or enter key.
+     *   4. {@link ClickableComponent#handleKeyPress} calls this function with the `keydown`
+     *      event as a parameter.
+     *
+     * @param {EventTarget~Event} event
+     *        The `keydown`, `tap`, or `click` event that caused this function to be
+     *        called.
+     *
+     * @listens tap
+     * @listens click
+     * @abstract
+     */
+
+
+    ClickableComponent.prototype.handleClick = function handleClick(event) {
+    };
+
+    /**
+     * This gets called when a `ClickableComponent` gains focus via a `focus` event.
+     * Turns on listening for `keydown` events. When they happen it
+     * calls `this.handleKeyPress`.
+     *
+     * @param {EventTarget~Event} event
+     *        The `focus` event that caused this function to be called.
+     *
+     * @listens focus
+     */
+
+
+    ClickableComponent.prototype.handleFocus = function handleFocus(event) {
+      on(document_1, 'keydown', bind(this, this.handleKeyPress));
+    };
+
+    /**
+     * Called when this ClickableComponent has focus and a key gets pressed down. By
+     * default it will call `this.handleClick` when the key is space or enter.
+     *
+     * @param {EventTarget~Event} event
+     *        The `keydown` event that caused this function to be called.
+     *
+     * @listens keydown
+     */
+
+
+    ClickableComponent.prototype.handleKeyPress = function handleKeyPress(event) {
+
+      // Support Space (32) or Enter (13) key operation to fire a click event
+      if (event.which === 32 || event.which === 13) {
+        event.preventDefault();
+        this.trigger('click');
+      } else if (_Component.prototype.handleKeyPress) {
+
+        // Pass keypress handling up for unsupported keys
+        _Component.prototype.handleKeyPress.call(this, event);
+      }
+    };
+
+    /**
+     * Called when a `ClickableComponent` loses focus. Turns off the listener for
+     * `keydown` events. Which Stops `this.handleKeyPress` from getting called.
+     *
+     * @param {EventTarget~Event} event
+     *        The `blur` event that caused this function to be called.
+     *
+     * @listens blur
+     */
+
+
+    ClickableComponent.prototype.handleBlur = function handleBlur(event) {
+      off(document_1, 'keydown', bind(this, this.handleKeyPress));
+    };
+
+    return ClickableComponent;
+  }(Component);
+
+  Component.registerComponent('ClickableComponent', ClickableComponent);
+
+  /**
+   * @file poster-image.js
+   */
+
+  /**
+   * A `ClickableComponent` that handles showing the poster image for the player.
+   *
+   * @extends ClickableComponent
+   */
+
+  var PosterImage = function (_ClickableComponent) {
+    inherits(PosterImage, _ClickableComponent);
+
+    /**
+     * Create an instance of this class.
+     *
+     * @param {Player} player
+     *        The `Player` that this class should attach to.
+     *
+     * @param {Object} [options]
+     *        The key/value store of player options.
+     */
+    function PosterImage(player, options) {
+      classCallCheck(this, PosterImage);
+
+      var _this = possibleConstructorReturn(this, _ClickableComponent.call(this, player, options));
+
+      _this.update();
+      player.on('posterchange', bind(_this, _this.update));
+      return _this;
+    }
+
+    /**
+     * Clean up and dispose of the `PosterImage`.
+     */
+
+
+    PosterImage.prototype.dispose = function dispose() {
+      this.player().off('posterchange', this.update);
+      _ClickableComponent.prototype.dispose.call(this);
+    };
+
+    /**
+     * Create the `PosterImage`s DOM element.
+     *
+     * @return {Element}
+     *         The element that gets created.
+     */
+
+
+    PosterImage.prototype.createEl = function createEl$$1() {
+      var el = createEl('div', {
+        className: 'vjs-poster',
+
+        // Don't want poster to be tabbable.
+        tabIndex: -1
+      });
+
+      return el;
+    };
+
+    /**
+     * An {@link EventTarget~EventListener} for {@link Player#posterchange} events.
+     *
+     * @listens Player#posterchange
+     *
+     * @param {EventTarget~Event} [event]
+     *        The `Player#posterchange` event that triggered this function.
+     */
+
+
+    PosterImage.prototype.update = function update(event) {
+      var url = this.player().poster();
+
+      this.setSrc(url);
+
+      // If there's no poster source we should display:none on this component
+      // so it's not still clickable or right-clickable
+      if (url) {
+        this.show();
+      } else {
+        this.hide();
+      }
+    };
+
+    /**
+     * Set the source of the `PosterImage` depending on the display method.
+     *
+     * @param {string} url
+     *        The URL to the source for the `PosterImage`.
+     */
+
+
+    PosterImage.prototype.setSrc = function setSrc(url) {
+      var backgroundImage = '';
+
+      // Any falsy value should stay as an empty string, otherwise
+      // this will throw an extra error
+      if (url) {
+        backgroundImage = 'url("' + url + '")';
+      }
+
+      this.el_.style.backgroundImage = backgroundImage;
+    };
+
+    /**
+     * An {@link EventTarget~EventListener} for clicks on the `PosterImage`. See
+     * {@link ClickableComponent#handleClick} for instances where this will be triggered.
+     *
+     * @listens tap
+     * @listens click
+     * @listens keydown
+     *
+     * @param {EventTarget~Event} event
+     +        The `click`, `tap` or `keydown` event that caused this function to be called.
+     */
+
+
+    PosterImage.prototype.handleClick = function handleClick(event) {
+      // We don't want a click to trigger playback when controls are disabled
+      if (!this.player_.controls()) {
+        return;
+      }
+
+      if (this.player_.paused()) {
+        this.player_.play();
+      } else {
+        this.player_.pause();
+      }
+    };
+
+    return PosterImage;
+  }(ClickableComponent);
+
+  Component.registerComponent('PosterImage', PosterImage);
+
+  /**
+   * @file text-track-display.js
+   */
+
+  var darkGray = '#222';
+  var lightGray = '#ccc';
+  var fontMap = {
+    monospace: 'monospace',
+    sansSerif: 'sans-serif',
+    serif: 'serif',
+    monospaceSansSerif: '"Andale Mono", "Lucida Console", monospace',
+    monospaceSerif: '"Courier New", monospace',
+    proportionalSansSerif: 'sans-serif',
+    proportionalSerif: 'serif',
+    casual: '"Comic Sans MS", Impact, fantasy',
+    script: '"Monotype Corsiva", cursive',
+    smallcaps: '"Andale Mono", "Lucida Console", monospace, sans-serif'
+  };
+
+  /**
+   * Construct an rgba color from a given hex color code.
+   *
+   * @param {number} color
+   *        Hex number for color, like #f0e.
+   *
+   * @param {number} opacity
+   *        Value for opacity, 0.0 - 1.0.
+   *
+   * @return {string}
+   *         The rgba color that was created, like 'rgba(255, 0, 0, 0.3)'.
+   *
+   * @private
+   */
+  function constructColor(color, opacity) {
+    return 'rgba(' +
+      // color looks like "#f0e"
+      parseInt(color[1] + color[1], 16) + ',' + parseInt(color[2] + color[2], 16) + ',' + parseInt(color[3] + color[3], 16) + ',' + opacity + ')';
+  }
+
+  /**
+   * Try to update the style of a DOM element. Some style changes will throw an error,
+   * particularly in IE8. Those should be noops.
+   *
+   * @param {Element} el
+   *        The DOM element to be styled.
+   *
+   * @param {string} style
+   *        The CSS property on the element that should be styled.
+   *
+   * @param {string} rule
+   *        The style rule that should be applied to the property.
+   *
+   * @private
+   */
+  function tryUpdateStyle(el, style, rule) {
+    try {
+      el.style[style] = rule;
+    } catch (e) {
+
+      // Satisfies linter.
+      return;
+    }
+  }
+
+  /**
+   * The component for displaying text track cues.
+   *
+   * @extends Component
+   */
+
+  var TextTrackDisplay = function (_Component) {
+    inherits(TextTrackDisplay, _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 {Component~ReadyCallback} [ready]
+     *        The function to call when `TextTrackDisplay` is ready.
+     */
+    function TextTrackDisplay(player, options, ready) {
+      classCallCheck(this, TextTrackDisplay);
+
+      var _this = possibleConstructorReturn(this, _Component.call(this, player, options, ready));
+
+      player.on('loadstart', bind(_this, _this.toggleDisplay));
+      player.on('texttrackchange', bind(_this, _this.updateDisplay));
+      player.on('loadstart', bind(_this, _this.preselectTrack));
+
+      // This used to be called during player init, but was causing an error
+      // if a track should show by default and the display hadn't loaded yet.
+      // Should probably be moved to an external track loader when we support
+      // tracks that don't need a display.
+      player.ready(bind(_this, function () {
+        if (player.tech_ && player.tech_.featuresNativeTextTracks) {
+          this.hide();
+          return;
+        }
+
+        player.on('fullscreenchange', bind(this, this.updateDisplay));
+
+        var tracks = this.options_.playerOptions.tracks || [];
+
+        for (var i = 0; i < tracks.length; i++) {
+          this.player_.addRemoteTextTrack(tracks[i], true);
+        }
+
+        this.preselectTrack();
+      }));
+      return _this;
+    }
+
+    /**
+     * Preselect a track following this precedence:
+     * - matches the previously selected {@link TextTrack}'s language and kind
+     * - matches the previously selected {@link TextTrack}'s language only
+     * - is the first default captions track
+     * - is the first default descriptions track
+     *
+     * @listens Player#loadstart
+     */
+
+
+    TextTrackDisplay.prototype.preselectTrack = function preselectTrack() {
+      var modes = {captions: 1, subtitles: 1};
+      var trackList = this.player_.textTracks();
+      var userPref = this.player_.cache_.selectedLanguage;
+      var firstDesc = void 0;
+      var firstCaptions = void 0;
+      var preferredTrack = void 0;
+
+      for (var i = 0; i < trackList.length; i++) {
+        var track = trackList[i];
+
+        if (userPref && userPref.enabled && userPref.language === track.language) {
+          // Always choose the track that matches both language and kind
+          if (track.kind === userPref.kind) {
+            preferredTrack = track;
+            // or choose the first track that matches language
+          } else if (!preferredTrack) {
+            preferredTrack = track;
+          }
+
+          // clear everything if offTextTrackMenuItem was clicked
+        } else if (userPref && !userPref.enabled) {
+          preferredTrack = null;
+          firstDesc = null;
+          firstCaptions = null;
+        } else if (track.default) {
+          if (track.kind === 'descriptions' && !firstDesc) {
+            firstDesc = track;
+          } else if (track.kind in modes && !firstCaptions) {
+            firstCaptions = track;
+          }
+        }
+      }
+
+      // The preferredTrack matches the user preference and takes
+      // precedence over all the other tracks.
+      // So, display the preferredTrack before the first default track
+      // and the subtitles/captions track before the descriptions track
+      if (preferredTrack) {
+        preferredTrack.mode = 'showing';
+      } else if (firstCaptions) {
+        firstCaptions.mode = 'showing';
+      } else if (firstDesc) {
+        firstDesc.mode = 'showing';
+      }
+    };
+
+    /**
+     * Turn display of {@link TextTrack}'s from the current state into the other state.
+     * There are only two states:
+     * - 'shown'
+     * - 'hidden'
+     *
+     * @listens Player#loadstart
+     */
+
+
+    TextTrackDisplay.prototype.toggleDisplay = function toggleDisplay() {
+      if (this.player_.tech_ && this.player_.tech_.featuresNativeTextTracks) {
+        this.hide();
+      } else {
+        this.show();
+      }
+    };
+
+    /**
+     * Create the {@link Component}'s DOM element.
+     *
+     * @return {Element}
+     *         The element that was created.
+     */
+
+
+    TextTrackDisplay.prototype.createEl = function createEl() {
+      return _Component.prototype.createEl.call(this, 'div', {
+        className: 'vjs-text-track-display'
+      }, {
+        'aria-live': 'off',
+        'aria-atomic': 'true'
+      });
+    };
+
+    /**
+     * Clear all displayed {@link TextTrack}s.
+     */
+
+
+    TextTrackDisplay.prototype.clearDisplay = function clearDisplay() {
+      if (typeof window_1.WebVTT === 'function') {
+        window_1.WebVTT.processCues(window_1, [], this.el_);
+      }
+    };
+
+    /**
+     * Update the displayed TextTrack when a either a {@link Player#texttrackchange} or
+     * a {@link Player#fullscreenchange} is fired.
+     *
+     * @listens Player#texttrackchange
+     * @listens Player#fullscreenchange
+     */
+
+
+    TextTrackDisplay.prototype.updateDisplay = function updateDisplay() {
+      var tracks = this.player_.textTracks();
+
+      this.clearDisplay();
+
+      // Track display prioritization model: if multiple tracks are 'showing',
+      //  display the first 'subtitles' or 'captions' track which is 'showing',
+      //  otherwise display the first 'descriptions' track which is 'showing'
+
+      var descriptionsTrack = null;
+      var captionsSubtitlesTrack = null;
+      var i = tracks.length;
+
+      while (i--) {
+        var track = tracks[i];
+
+        if (track.mode === 'showing') {
+          if (track.kind === 'descriptions') {
+            descriptionsTrack = track;
+          } else {
+            captionsSubtitlesTrack = track;
+          }
+        }
+      }
+
+      if (captionsSubtitlesTrack) {
+        if (this.getAttribute('aria-live') !== 'off') {
+          this.setAttribute('aria-live', 'off');
+        }
+        this.updateForTrack(captionsSubtitlesTrack);
+      } else if (descriptionsTrack) {
+        if (this.getAttribute('aria-live') !== 'assertive') {
+          this.setAttribute('aria-live', 'assertive');
+        }
+        this.updateForTrack(descriptionsTrack);
+      }
+    };
+
+    /**
+     * Add an {@link TextTrack} to to the {@link Tech}s {@link TextTrackList}.
+     *
+     * @param {TextTrack} track
+     *        Text track object to be added to the list.
+     */
+
+
+    TextTrackDisplay.prototype.updateForTrack = function updateForTrack(track) {
+      if (typeof window_1.WebVTT !== 'function' || !track.activeCues) {
+        return;
+      }
+
+      var cues = [];
+
+      for (var _i = 0; _i < track.activeCues.length; _i++) {
+        cues.push(track.activeCues[_i]);
+      }
+
+      window_1.WebVTT.processCues(window_1, cues, this.el_);
+
+      if (!this.player_.textTrackSettings) {
+        return;
+      }
+
+      var overrides = this.player_.textTrackSettings.getValues();
+
+      var i = cues.length;
+
+      while (i--) {
+        var cue = cues[i];
+
+        if (!cue) {
+          continue;
+        }
+
+        var cueDiv = cue.displayState;
+
+        if (overrides.color) {
+          cueDiv.firstChild.style.color = overrides.color;
+        }
+        if (overrides.textOpacity) {
+          tryUpdateStyle(cueDiv.firstChild, 'color', constructColor(overrides.color || '#fff', overrides.textOpacity));
+        }
+        if (overrides.backgroundColor) {
+          cueDiv.firstChild.style.backgroundColor = overrides.backgroundColor;
+        }
+        if (overrides.backgroundOpacity) {
+          tryUpdateStyle(cueDiv.firstChild, 'backgroundColor', constructColor(overrides.backgroundColor || '#000', overrides.backgroundOpacity));
+        }
+        if (overrides.windowColor) {
+          if (overrides.windowOpacity) {
+            tryUpdateStyle(cueDiv, 'backgroundColor', constructColor(overrides.windowColor, overrides.windowOpacity));
+          } else {
+            cueDiv.style.backgroundColor = overrides.windowColor;
+          }
+        }
+        if (overrides.edgeStyle) {
+          if (overrides.edgeStyle === 'dropshadow') {
+            cueDiv.firstChild.style.textShadow = '2px 2px 3px ' + darkGray + ', 2px 2px 4px ' + darkGray + ', 2px 2px 5px ' + darkGray;
+          } else if (overrides.edgeStyle === 'raised') {
+            cueDiv.firstChild.style.textShadow = '1px 1px ' + darkGray + ', 2px 2px ' + darkGray + ', 3px 3px ' + darkGray;
+          } else if (overrides.edgeStyle === 'depressed') {
+            cueDiv.firstChild.style.textShadow = '1px 1px ' + lightGray + ', 0 1px ' + lightGray + ', -1px -1px ' + darkGray + ', 0 -1px ' + darkGray;
+          } else if (overrides.edgeStyle === 'uniform') {
+            cueDiv.firstChild.style.textShadow = '0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray;
+          }
+        }
+        if (overrides.fontPercent && overrides.fontPercent !== 1) {
+          var fontSize = window_1.parseFloat(cueDiv.style.fontSize);
+
+          cueDiv.style.fontSize = fontSize * overrides.fontPercent + 'px';
+          cueDiv.style.height = 'auto';
+          cueDiv.style.top = 'auto';
+          cueDiv.style.bottom = '2px';
+        }
+        if (overrides.fontFamily && overrides.fontFamily !== 'default') {
+          if (overrides.fontFamily === 'small-caps') {
+            cueDiv.firstChild.style.fontVariant = 'small-caps';
+          } else {
+            cueDiv.firstChild.style.fontFamily = fontMap[overrides.fontFamily];
+          }
+        }
+      }
+    };
+
+    return TextTrackDisplay;
+  }(Component);
+
+  Component.registerComponent('TextTrackDisplay', TextTrackDisplay);
+
+  /**
+   * @file loading-spinner.js
+   */
+
+  /**
+   * A loading spinner for use during waiting/loading events.
+   *
+   * @extends Component
+   */
+
+  var LoadingSpinner = function (_Component) {
+    inherits(LoadingSpinner, _Component);
+
+    function LoadingSpinner() {
+      classCallCheck(this, LoadingSpinner);
+      return possibleConstructorReturn(this, _Component.apply(this, arguments));
+    }
+
+    /**
+     * Create the `LoadingSpinner`s DOM element.
+     *
+     * @return {Element}
+     *         The dom element that gets created.
+     */
+    LoadingSpinner.prototype.createEl = function createEl$$1() {
+      var isAudio = this.player_.isAudio();
+      var playerType = this.localize(isAudio ? 'Audio Player' : 'Video Player');
+      var controlText = createEl('span', {
+        className: 'vjs-control-text',
+        innerHTML: this.localize('{1} is loading.', [playerType])
+      });
+
+      var el = _Component.prototype.createEl.call(this, 'div', {
+        className: 'vjs-loading-spinner',
+        dir: 'ltr'
+      });
+
+      el.appendChild(controlText);
+
+      return el;
+    };
+
+    return LoadingSpinner;
+  }(Component);
+
+  Component.registerComponent('LoadingSpinner', LoadingSpinner);
+
+  /**
+   * @file button.js
+   */
+
+  /**
+   * Base class for all buttons.
+   *
+   * @extends ClickableComponent
+   */
+
+  var Button = function (_ClickableComponent) {
+    inherits(Button, _ClickableComponent);
+
+    function Button() {
+      classCallCheck(this, Button);
+      return possibleConstructorReturn(this, _ClickableComponent.apply(this, arguments));
+    }
+
+    /**
+     * Create the `Button`s DOM element.
+     *
+     * @param {string} [tag="button"]
+     *        The element's node type. This argument is IGNORED: no matter what
+     *        is passed, it will always create a `button` element.
+     *
+     * @param {Object} [props={}]
+     *        An object of properties that should be set on the element.
+     *
+     * @param {Object} [attributes={}]
+     *        An object of attributes that should be set on the element.
+     *
+     * @return {Element}
+     *         The element that gets created.
+     */
+    Button.prototype.createEl = function createEl(tag) {
+      var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+      var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
+
+      tag = 'button';
+
+      props = assign({
+        innerHTML: '<span aria-hidden="true" class="vjs-icon-placeholder"></span>',
+        className: this.buildCSSClass()
+      }, props);
+
+      // Add attributes for button element
+      attributes = assign({
+
+        // Necessary since the default button type is "submit"
+        type: 'button'
+      }, attributes);
+
+      var el = Component.prototype.createEl.call(this, tag, props, attributes);
+
+      this.createControlTextEl(el);
+
+      return el;
+    };
+
+    /**
+     * Add a child `Component` inside of this `Button`.
+     *
+     * @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.
+     *
+     * @return {Component}
+     *         The `Component` that gets added as a child. When using a string the
+     *         `Component` will get created by this process.
+     *
+     * @deprecated since version 5
+     */
+
+
+    Button.prototype.addChild = function addChild(child) {
+      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+
+      var className = this.constructor.name;
+
+      log$1.warn('Adding an actionable (user controllable) child to a Button (' + className + ') is not supported; use a ClickableComponent instead.');
+
+      // Avoid the error message generated by ClickableComponent's addChild method
+      return Component.prototype.addChild.call(this, child, options);
+    };
+
+    /**
+     * Enable the `Button` element so that it can be activated or clicked. Use this with
+     * {@link Button#disable}.
+     */
+
+
+    Button.prototype.enable = function enable() {
+      _ClickableComponent.prototype.enable.call(this);
+      this.el_.removeAttribute('disabled');
+    };
+
+    /**
+     * Disable the `Button` element so that it cannot be activated or clicked. Use this with
+     * {@link Button#enable}.
+     */
+
+
+    Button.prototype.disable = function disable() {
+      _ClickableComponent.prototype.disable.call(this);
+      this.el_.setAttribute('disabled', 'disabled');
+    };
+
+    /**
+     * This gets called when a `Button` has focus and `keydown` is triggered via a key
+     * press.
+     *
+     * @param {EventTarget~Event} event
+     *        The event that caused this function to get called.
+     *
+     * @listens keydown
+     */
+
+
+    Button.prototype.handleKeyPress = function handleKeyPress(event) {
+
+      // Ignore Space (32) or Enter (13) key operation, which is handled by the browser for a button.
+      if (event.which === 32 || event.which === 13) {
+        return;
+      }
+
+      // Pass keypress handling up for unsupported keys
+      _ClickableComponent.prototype.handleKeyPress.call(this, event);
+    };
+
+    return Button;
+  }(ClickableComponent);
+
+  Component.registerComponent('Button', Button);
+
+  /**
+   * @file big-playOld-button.js
+   */
+
+  /**
+   * The initial playOld button that shows before the video has played. The hiding of the
+   * `BigPlayButton` get done via CSS and `Player` states.
+   *
+   * @extends Button
+   */
+
+  var BigPlayButton = function (_Button) {
+    inherits(BigPlayButton, _Button);
+
+    function BigPlayButton(player, options) {
+      classCallCheck(this, BigPlayButton);
+
+      var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
+
+      _this.mouseused_ = false;
+
+      _this.on('mousedown', _this.handleMouseDown);
+      return _this;
+    }
+
+    /**
+     * Builds the default DOM `className`.
+     *
+     * @return {string}
+     *         The DOM `className` for this object. Always returns 'vjs-big-playOld-button'.
+     */
+
+
+    BigPlayButton.prototype.buildCSSClass = function buildCSSClass() {
+      return 'vjs-big-playOld-button';
+    };
+
+    /**
+     * This gets called when a `BigPlayButton` "clicked". See {@link ClickableComponent}
+     * for more detailed information on what a click can be.
+     *
+     * @param {EventTarget~Event} event
+     *        The `keydown`, `tap`, or `click` event that caused this function to be
+     *        called.
+     *
+     * @listens tap
+     * @listens click
+     */
+
+
+    BigPlayButton.prototype.handleClick = function handleClick(event) {
+      var playPromise = this.player_.play();
+
+      // exit early if clicked via the mouse
+      if (this.mouseused_ && event.clientX && event.clientY) {
+        return;
+      }
+
+      var cb = this.player_.getChild('controlBar');
+      var playToggle = cb && cb.getChild('playToggle');
+
+      if (!playToggle) {
+        this.player_.focus();
+        return;
+      }
+
+      var playFocus = function playFocus() {
+        return playToggle.focus();
+      };
+
+      if (isPromise(playPromise)) {
+        playPromise.then(playFocus, function () {
+        });
+      } else {
+        this.setTimeout(playFocus, 1);
+      }
+    };
+
+    BigPlayButton.prototype.handleKeyPress = function handleKeyPress(event) {
+      this.mouseused_ = false;
+
+      _Button.prototype.handleKeyPress.call(this, event);
+    };
+
+    BigPlayButton.prototype.handleMouseDown = function handleMouseDown(event) {
+      this.mouseused_ = true;
+    };
+
+    return BigPlayButton;
+  }(Button);
+
+  /**
+   * The text that should display over the `BigPlayButton`s controls. Added to for localization.
+   *
+   * @type {string}
+   * @private
+   */
+
+
+  BigPlayButton.prototype.controlText_ = 'Play Video';
+
+  Component.registerComponent('BigPlayButton', BigPlayButton);
+
+  /**
+   * @file close-button.js
+   */
+
+  /**
+   * The `CloseButton` is a `{@link Button}` that fires a `close` event when
+   * it gets clicked.
+   *
+   * @extends Button
+   */
+
+  var CloseButton = function (_Button) {
+    inherits(CloseButton, _Button);
+
+    /**
+     * Creates an instance of the this class.
+     *
+     * @param  {Player} player
+     *         The `Player` that this class should be attached to.
+     *
+     * @param  {Object} [options]
+     *         The key/value store of player options.
+     */
+    function CloseButton(player, options) {
+      classCallCheck(this, CloseButton);
+
+      var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
+
+      _this.controlText(options && options.controlText || _this.localize('Close'));
+      return _this;
+    }
+
+    /**
+     * Builds the default DOM `className`.
+     *
+     * @return {string}
+     *         The DOM `className` for this object.
+     */
+
+
+    CloseButton.prototype.buildCSSClass = function buildCSSClass() {
+      return 'vjs-close-button ' + _Button.prototype.buildCSSClass.call(this);
+    };
+
+    /**
+     * This gets called when a `CloseButton` gets clicked. See
+     * {@link ClickableComponent#handleClick} for more information on when this will be
+     * triggered
+     *
+     * @param {EventTarget~Event} event
+     *        The `keydown`, `tap`, or `click` event that caused this function to be
+     *        called.
+     *
+     * @listens tap
+     * @listens click
+     * @fires CloseButton#close
+     */
+
+
+    CloseButton.prototype.handleClick = function handleClick(event) {
+
+      /**
+       * Triggered when the a `CloseButton` is clicked.
+       *
+       * @event CloseButton#close
+       * @type {EventTarget~Event}
+       *
+       * @property {boolean} [bubbles=false]
+       *           set to false so that the close event does not
+       *           bubble up to parents if there is no listener
+       */
+      this.trigger({type: 'close', bubbles: false});
+    };
+
+    return CloseButton;
+  }(Button);
+
+  Component.registerComponent('CloseButton', CloseButton);
+
+  /**
+   * @file playOld-toggle.js
+   */
+
+  /**
+   * Button to toggle between playOld and pause.
+   *
+   * @extends Button
+   */
+
+  var PlayToggle = function (_Button) {
+    inherits(PlayToggle, _Button);
+
+    /**
+     * 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.
+     */
+    function PlayToggle(player, options) {
+      classCallCheck(this, PlayToggle);
+
+      var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
+
+      _this.on(player, 'play', _this.handlePlay);
+      _this.on(player, 'pause', _this.handlePause);
+      _this.on(player, 'ended', _this.handleEnded);
+      return _this;
+    }
+
+    /**
+     * Builds the default DOM `className`.
+     *
+     * @return {string}
+     *         The DOM `className` for this object.
+     */
+
+
+    PlayToggle.prototype.buildCSSClass = function buildCSSClass() {
+      return 'vjs-playOld-control ' + _Button.prototype.buildCSSClass.call(this);
+    };
+
+    /**
+     * This gets called when an `PlayToggle` is "clicked". See
+     * {@link ClickableComponent} for more detailed information on what a click can be.
+     *
+     * @param {EventTarget~Event} [event]
+     *        The `keydown`, `tap`, or `click` event that caused this function to be
+     *        called.
+     *
+     * @listens tap
+     * @listens click
+     */
+
+
+    PlayToggle.prototype.handleClick = function handleClick(event) {
+      if (this.player_.paused()) {
+        this.player_.play();
+      } else {
+        this.player_.pause();
+      }
+    };
+
+    /**
+     * This gets called once after the video has ended and the user seeks so that
+     * we can change the replay button back to a playOld button.
+     *
+     * @param {EventTarget~Event} [event]
+     *        The event that caused this function to run.
+     *
+     * @listens Player#seeked
+     */
+
+
+    PlayToggle.prototype.handleSeeked = function handleSeeked(event) {
+      this.removeClass('vjs-ended');
+
+      if (this.player_.paused()) {
+        this.handlePause(event);
+      } else {
+        this.handlePlay(event);
+      }
+    };
+
+    /**
+     * Add the vjs-playing class to the element so it can change appearance.
+     *
+     * @param {EventTarget~Event} [event]
+     *        The event that caused this function to run.
+     *
+     * @listens Player#play
+     */
+
+
+    PlayToggle.prototype.handlePlay = function handlePlay(event) {
+      this.removeClass('vjs-ended');
+      this.removeClass('vjs-paused');
+      this.addClass('vjs-playing');
+      // change the button text to "Pause"
+      this.controlText('Pause');
+    };
+
+    /**
+     * Add the vjs-paused class to the element so it can change appearance.
+     *
+     * @param {EventTarget~Event} [event]
+     *        The event that caused this function to run.
+     *
+     * @listens Player#pause
+     */
+
+
+    PlayToggle.prototype.handlePause = function handlePause(event) {
+      this.removeClass('vjs-playing');
+      this.addClass('vjs-paused');
+      // change the button text to "Play"
+      this.controlText('Play');
+    };
+
+    /**
+     * Add the vjs-ended class to the element so it can change appearance
+     *
+     * @param {EventTarget~Event} [event]
+     *        The event that caused this function to run.
+     *
+     * @listens Player#ended
+     */
+
+
+    PlayToggle.prototype.handleEnded = function handleEnded(event) {
+      this.removeClass('vjs-playing');
+      this.addClass('vjs-ended');
+      // change the button text to "Replay"
+      this.controlText('Replay');
+
+      // on the next seek remove the replay button
+      this.one(this.player_, 'seeked', this.handleSeeked);
+    };
+
+    return PlayToggle;
+  }(Button);
+
+  /**
+   * The text that should display over the `PlayToggle`s controls. Added for localization.
+   *
+   * @type {string}
+   * @private
+   */
+
+
+  PlayToggle.prototype.controlText_ = 'Play';
+
+  Component.registerComponent('PlayToggle', PlayToggle);
+
+  /**
+   * @file format-time.js
+   * @module format-time
+   */
+
+  /**
+   * Format seconds as a time string, H:MM:SS or M:SS. Supplying a guide (in seconds)
+   * will force a number of leading zeros to cover the length of the guide.
+   *
+   * @param {number} seconds
+   *        Number of seconds to be turned into a string
+   *
+   * @param {number} guide
+   *        Number (in seconds) to model the string after
+   *
+   * @return {string}
+   *         Time formatted as H:MM:SS or M:SS
+   */
+  var defaultImplementation = function defaultImplementation(seconds, guide) {
+    seconds = seconds < 0 ? 0 : seconds;
+    var s = Math.floor(seconds % 60);
+    var m = Math.floor(seconds / 60 % 60);
+    var h = Math.floor(seconds / 3600);
+    var gm = Math.floor(guide / 60 % 60);
+    var gh = Math.floor(guide / 3600);
+
+    // handle invalid times
+    if (isNaN(seconds) || seconds === Infinity) {
+      // '-' is false for all relational operators (e.g. <, >=) so this setting
+      // will add the minimum number of fields specified by the guide
+      h = m = s = '-';
+    }
+
+    // Check if we need to show hours
+    h = h > 0 || gh > 0 ? h + ':' : '';
+
+    // If hours are showing, we may need to add a leading zero.
+    // Always show at least one digit of minutes.
+    m = ((h || gm >= 10) && m < 10 ? '0' + m : m) + ':';
+
+    // Check if leading zero is need for seconds
+    s = s < 10 ? '0' + s : s;
+
+    return h + m + s;
+  };
+
+  var implementation = defaultImplementation;
+
+  /**
+   * Replaces the default formatTime implementation with a custom implementation.
+   *
+   * @param {Function} customImplementation
+   *        A function which will be used in place of the default formatTime implementation.
+   *        Will receive the current time in seconds and the guide (in seconds) as arguments.
+   */
+  function setFormatTime(customImplementation) {
+    implementation = customImplementation;
+  }
+
+  /**
+   * Resets formatTime to the default implementation.
+   */
+  function resetFormatTime() {
+    implementation = defaultImplementation;
+  }
+
+  function formatTime(seconds) {
+    var guide = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : seconds;
+
+    return implementation(seconds, guide);
+  }
+
+  /**
+   * @file time-display.js
+   */
+
+  /**
+   * Displays the time left in the video
+   *
+   * @extends Component
+   */
+
+  var TimeDisplay = function (_Component) {
+    inherits(TimeDisplay, _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.
+     */
+    function TimeDisplay(player, options) {
+      classCallCheck(this, TimeDisplay);
+
+      var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
+
+      _this.throttledUpdateContent = throttle(bind(_this, _this.updateContent), 25);
+      _this.on(player, 'timeupdate', _this.throttledUpdateContent);
+      return _this;
+    }
+
+    /**
+     * Create the `Component`'s DOM element
+     *
+     * @return {Element}
+     *         The element that was created.
+     */
+
+
+    TimeDisplay.prototype.createEl = function createEl$$1(plainName) {
+      var className = this.buildCSSClass();
+      var el = _Component.prototype.createEl.call(this, 'div', {
+        className: className + ' vjs-time-control vjs-control',
+        innerHTML: '<span class="vjs-control-text">' + this.localize(this.labelText_) + '\xA0</span>'
+      });
+
+      this.contentEl_ = createEl('span', {
+        className: className + '-display'
+      }, {
+        // tell screen readers not to automatically read the time as it changes
+        'aria-live': 'off'
+      });
+
+      this.updateTextNode_();
+      el.appendChild(this.contentEl_);
+      return el;
+    };
+
+    TimeDisplay.prototype.dispose = function dispose() {
+      this.contentEl_ = null;
+      this.textNode_ = null;
+
+      _Component.prototype.dispose.call(this);
+    };
+
+    /**
+     * Updates the "remaining time" text node with new content using the
+     * contents of the `formattedTime_` property.
+     *
+     * @private
+     */
+
+
+    TimeDisplay.prototype.updateTextNode_ = function updateTextNode_() {
+      if (!this.contentEl_) {
+        return;
+      }
+
+      while (this.contentEl_.firstChild) {
+        this.contentEl_.removeChild(this.contentEl_.firstChild);
+      }
+
+      this.textNode_ = document_1.createTextNode(this.formattedTime_ || this.formatTime_(0));
+      this.contentEl_.appendChild(this.textNode_);
+    };
+
+    /**
+     * Generates a formatted time for this component to use in display.
+     *
+     * @param  {number} time
+     *         A numeric time, in seconds.
+     *
+     * @return {string}
+     *         A formatted time
+     *
+     * @private
+     */
+
+
+    TimeDisplay.prototype.formatTime_ = function formatTime_(time) {
+      return formatTime(time);
+    };
+
+    /**
+     * Updates the time display text node if it has what was passed in changed
+     * the formatted time.
+     *
+     * @param {number} time
+     *        The time to update to
+     *
+     * @private
+     */
+
+
+    TimeDisplay.prototype.updateFormattedTime_ = function updateFormattedTime_(time) {
+      var formattedTime = this.formatTime_(time);
+
+      if (formattedTime === this.formattedTime_) {
+        return;
+      }
+
+      this.formattedTime_ = formattedTime;
+      this.requestAnimationFrame(this.updateTextNode_);
+    };
+
+    /**
+     * To be filled out in the child class, should update the displayed time
+     * in accordance with the fact that the current time has changed.
+     *
+     * @param {EventTarget~Event} [event]
+     *        The `timeupdate`  event that caused this to run.
+     *
+     * @listens Player#timeupdate
+     */
+
+
+    TimeDisplay.prototype.updateContent = function updateContent(event) {
+    };
+
+    return TimeDisplay;
+  }(Component);
+
+  /**
+   * The text that is added to the `TimeDisplay` for screen reader users.
+   *
+   * @type {string}
+   * @private
+   */
+
+
+  TimeDisplay.prototype.labelText_ = 'Time';
+
+  /**
+   * The text that should display over the `TimeDisplay`s controls. Added to for localization.
+   *
+   * @type {string}
+   * @private
+   *
+   * @deprecated in v7; controlText_ is not used in non-active display Components
+   */
+  TimeDisplay.prototype.controlText_ = 'Time';
+
+  Component.registerComponent('TimeDisplay', TimeDisplay);
+
+  /**
+   * @file current-time-display.js
+   */
+
+  /**
+   * Displays the current time
+   *
+   * @extends Component
+   */
+
+  var CurrentTimeDisplay = function (_TimeDisplay) {
+    inherits(CurrentTimeDisplay, _TimeDisplay);
+
+    /**
+     * 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.
+     */
+    function CurrentTimeDisplay(player, options) {
+      classCallCheck(this, CurrentTimeDisplay);
+
+      var _this = possibleConstructorReturn(this, _TimeDisplay.call(this, player, options));
+
+      _this.on(player, 'ended', _this.handleEnded);
+      return _this;
+    }
+
+    /**
+     * Builds the default DOM `className`.
+     *
+     * @return {string}
+     *         The DOM `className` for this object.
+     */
+
+
+    CurrentTimeDisplay.prototype.buildCSSClass = function buildCSSClass() {
+      return 'vjs-current-time';
+    };
+
+    /**
+     * Update current time display
+     *
+     * @param {EventTarget~Event} [event]
+     *        The `timeupdate` event that caused this function to run.
+     *
+     * @listens Player#timeupdate
+     */
+
+
+    CurrentTimeDisplay.prototype.updateContent = function updateContent(event) {
+      // Allows for smooth scrubbing, when player can't keep up.
+      var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
+
+      this.updateFormattedTime_(time);
+    };
+
+    /**
+     * When the player fires ended there should be no time left. Sadly
+     * this is not always the case, lets make it seem like that is the case
+     * for users.
+     *
+     * @param {EventTarget~Event} [event]
+     *        The `ended` event that caused this to run.
+     *
+     * @listens Player#ended
+     */
+
+
+    CurrentTimeDisplay.prototype.handleEnded = function handleEnded(event) {
+      if (!this.player_.duration()) {
+        return;
+      }
+      this.updateFormattedTime_(this.player_.duration());
+    };
+
+    return CurrentTimeDisplay;
+  }(TimeDisplay);
+
+  /**
+   * The text that is added to the `CurrentTimeDisplay` for screen reader users.
+   *
+   * @type {string}
+   * @private
+   */
+
+
+  CurrentTimeDisplay.prototype.labelText_ = 'Current Time';
+
+  /**
+   * The text that should display over the `CurrentTimeDisplay`s controls. Added to for localization.
+   *
+   * @type {string}
+   * @private
+   *
+   * @deprecated in v7; controlText_ is not used in non-active display Components
+   */
+  CurrentTimeDisplay.prototype.controlText_ = 'Current Time';
+
+  Component.registerComponent('CurrentTimeDisplay', CurrentTimeDisplay);
+
+  /**
+   * @file duration-display.js
+   */
+
+  /**
+   * Displays the duration
+   *
+   * @extends Component
+   */
+
+  var DurationDisplay = function (_TimeDisplay) {
+    inherits(DurationDisplay, _TimeDisplay);
+
+    /**
+     * 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.
+     */
+    function DurationDisplay(player, options) {
+      classCallCheck(this, DurationDisplay);
+
+      // we do not want to/need to throttle duration changes,
+      // as they should always display the changed duration as
+      // it has changed
+      var _this = possibleConstructorReturn(this, _TimeDisplay.call(this, player, options));
+
+      _this.on(player, 'durationchange', _this.updateContent);
+
+      // Also listen for timeupdate (in the parent) and loadedmetadata because removing those
+      // listeners could have broken dependent applications/libraries. These
+      // can likely be removed for 7.0.
+      _this.on(player, 'loadedmetadata', _this.throttledUpdateContent);
+      return _this;
+    }
+
+    /**
+     * Builds the default DOM `className`.
+     *
+     * @return {string}
+     *         The DOM `className` for this object.
+     */
+
+
+    DurationDisplay.prototype.buildCSSClass = function buildCSSClass() {
+      return 'vjs-duration';
+    };
+
+    /**
+     * Update duration time display.
+     *
+     * @param {EventTarget~Event} [event]
+     *        The `durationchange`, `timeupdate`, or `loadedmetadata` event that caused
+     *        this function to be called.
+     *
+     * @listens Player#durationchange
+     * @listens Player#timeupdate
+     * @listens Player#loadedmetadata
+     */
+
+
+    DurationDisplay.prototype.updateContent = function updateContent(event) {
+      var duration = this.player_.duration();
+
+      if (duration && this.duration_ !== duration) {
+        this.duration_ = duration;
+        this.updateFormattedTime_(duration);
+      }
+    };
+
+    return DurationDisplay;
+  }(TimeDisplay);
+
+  /**
+   * The text that is added to the `DurationDisplay` for screen reader users.
+   *
+   * @type {string}
+   * @private
+   */
+
+
+  DurationDisplay.prototype.labelText_ = 'Duration';
+
+  /**
+   * The text that should display over the `DurationDisplay`s controls. Added to for localization.
+   *
+   * @type {string}
+   * @private
+   *
+   * @deprecated in v7; controlText_ is not used in non-active display Components
+   */
+  DurationDisplay.prototype.controlText_ = 'Duration';
+
+  Component.registerComponent('DurationDisplay', DurationDisplay);
+
+  /**
+   * @file time-divider.js
+   */
+
+  /**
+   * The separator between the current time and duration.
+   * Can be hidden if it's not needed in the design.
+   *
+   * @extends Component
+   */
+
+  var TimeDivider = function (_Component) {
+    inherits(TimeDivider, _Component);
+
+    function TimeDivider() {
+      classCallCheck(this, TimeDivider);
+      return possibleConstructorReturn(this, _Component.apply(this, arguments));
+    }
+
+    /**
+     * Create the component's DOM element
+     *
+     * @return {Element}
+     *         The element that was created.
+     */
+    TimeDivider.prototype.createEl = function createEl() {
+      return _Component.prototype.createEl.call(this, 'div', {
+        className: 'vjs-time-control vjs-time-divider',
+        innerHTML: '<div><span>/</span></div>'
+      });
+    };
+
+    return TimeDivider;
+  }(Component);
+
+  Component.registerComponent('TimeDivider', TimeDivider);
+
+  /**
+   * @file remaining-time-display.js
+   */
+  /**
+   * Displays the time left in the video
+   *
+   * @extends Component
+   */
+
+  var RemainingTimeDisplay = function (_TimeDisplay) {
+    inherits(RemainingTimeDisplay, _TimeDisplay);
+
+    /**
+     * 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.
+     */
+    function RemainingTimeDisplay(player, options) {
+      classCallCheck(this, RemainingTimeDisplay);
+
+      var _this = possibleConstructorReturn(this, _TimeDisplay.call(this, player, options));
+
+      _this.on(player, 'durationchange', _this.throttledUpdateContent);
+      _this.on(player, 'ended', _this.handleEnded);
+      return _this;
+    }
+
+    /**
+     * Builds the default DOM `className`.
+     *
+     * @return {string}
+     *         The DOM `className` for this object.
+     */
+
+
+    RemainingTimeDisplay.prototype.buildCSSClass = function buildCSSClass() {
+      return 'vjs-remaining-time';
+    };
+
+    /**
+     * The remaining time display prefixes numbers with a "minus" character.
+     *
+     * @param  {number} time
+     *         A numeric time, in seconds.
+     *
+     * @return {string}
+     *         A formatted time
+     *
+     * @private
+     */
+
+
+    RemainingTimeDisplay.prototype.formatTime_ = function formatTime_(time) {
+      // TODO: The "-" should be decorative, and not announced by a screen reader
+      return '-' + _TimeDisplay.prototype.formatTime_.call(this, time);
+    };
+
+    /**
+     * Update remaining time display.
+     *
+     * @param {EventTarget~Event} [event]
+     *        The `timeupdate` or `durationchange` event that caused this to run.
+     *
+     * @listens Player#timeupdate
+     * @listens Player#durationchange
+     */
+
+
+    RemainingTimeDisplay.prototype.updateContent = function updateContent(event) {
+      if (!this.player_.duration()) {
+        return;
+      }
+
+      // @deprecated We should only use remainingTimeDisplay
+      // as of video.js 7
+      if (this.player_.remainingTimeDisplay) {
+        this.updateFormattedTime_(this.player_.remainingTimeDisplay());
+      } else {
+        this.updateFormattedTime_(this.player_.remainingTime());
+      }
+    };
+
+    /**
+     * When the player fires ended there should be no time left. Sadly
+     * this is not always the case, lets make it seem like that is the case
+     * for users.
+     *
+     * @param {EventTarget~Event} [event]
+     *        The `ended` event that caused this to run.
+     *
+     * @listens Player#ended
+     */
+
+
+    RemainingTimeDisplay.prototype.handleEnded = function handleEnded(event) {
+      if (!this.player_.duration()) {
+        return;
+      }
+      this.updateFormattedTime_(0);
+    };
+
+    return RemainingTimeDisplay;
+  }(TimeDisplay);
+
+  /**
+   * The text that is added to the `RemainingTimeDisplay` for screen reader users.
+   *
+   * @type {string}
+   * @private
+   */
+
+
+  RemainingTimeDisplay.prototype.labelText_ = 'Remaining Time';
+
+  /**
+   * The text that should display over the `RemainingTimeDisplay`s controls. Added to for localization.
+   *
+   * @type {string}
+   * @private
+   *
+   * @deprecated in v7; controlText_ is not used in non-active display Components
+   */
+  RemainingTimeDisplay.prototype.controlText_ = 'Remaining Time';
+
+  Component.registerComponent('RemainingTimeDisplay', RemainingTimeDisplay);
+
+  /**
+   * @file live-display.js
+   */
+
+  // TODO - Future make it click to snap to live
+
+  /**
+   * Displays the live indicator when duration is Infinity.
+   *
+   * @extends Component
+   */
+
+  var LiveDisplay = function (_Component) {
+    inherits(LiveDisplay, _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.
+     */
+    function LiveDisplay(player, options) {
+      classCallCheck(this, LiveDisplay);
+
+      var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
+
+      _this.updateShowing();
+      _this.on(_this.player(), 'durationchange', _this.updateShowing);
+      return _this;
+    }
+
+    /**
+     * Create the `Component`'s DOM element
+     *
+     * @return {Element}
+     *         The element that was created.
+     */
+
+
+    LiveDisplay.prototype.createEl = function createEl$$1() {
+      var el = _Component.prototype.createEl.call(this, 'div', {
+        className: 'vjs-live-control vjs-control'
+      });
+
+      this.contentEl_ = createEl('div', {
+        className: 'vjs-live-display',
+        innerHTML: '<span class="vjs-control-text">' + this.localize('Stream Type') + '\xA0</span>' + this.localize('LIVE')
+      }, {
+        'aria-live': 'off'
+      });
+
+      el.appendChild(this.contentEl_);
+      return el;
+    };
+
+    LiveDisplay.prototype.dispose = function dispose() {
+      this.contentEl_ = null;
+
+      _Component.prototype.dispose.call(this);
+    };
+
+    /**
+     * Check the duration to see if the LiveDisplay should be showing or not. Then show/hide
+     * it accordingly
+     *
+     * @param {EventTarget~Event} [event]
+     *        The {@link Player#durationchange} event that caused this function to run.
+     *
+     * @listens Player#durationchange
+     */
+
+
+    LiveDisplay.prototype.updateShowing = function updateShowing(event) {
+      if (this.player().duration() === Infinity) {
+        this.show();
+      } else {
+        this.hide();
+      }
+    };
+
+    return LiveDisplay;
+  }(Component);
+
+  Component.registerComponent('LiveDisplay', LiveDisplay);
+
+  /**
+   * @file slider.js
+   */
+
+  /**
+   * The base functionality for a slider. Can be vertical or horizontal.
+   * For instance the volume bar or the seek bar on a video is a slider.
+   *
+   * @extends Component
+   */
+
+  var Slider = function (_Component) {
+    inherits(Slider, _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.
+     */
+    function Slider(player, options) {
+      classCallCheck(this, Slider);
+
+      // Set property names to bar to match with the child Slider class is looking for
+      var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
+
+      _this.bar = _this.getChild(_this.options_.barName);
+
+      // Set a horizontal or vertical class on the slider depending on the slider type
+      _this.vertical(!!_this.options_.vertical);
+
+      _this.enable();
+      return _this;
+    }
+
+    /**
+     * Are controls are currently enabled for this slider or not.
+     *
+     * @return {boolean}
+     *         true if controls are enabled, false otherwise
+     */
+
+
+    Slider.prototype.enabled = function enabled() {
+      return this.enabled_;
+    };
+
+    /**
+     * Enable controls for this slider if they are disabled
+     */
+
+
+    Slider.prototype.enable = function enable() {
+      if (this.enabled()) {
+        return;
+      }
+
+      this.on('mousedown', this.handleMouseDown);
+      this.on('touchstart', this.handleMouseDown);
+      this.on('focus', this.handleFocus);
+      this.on('blur', this.handleBlur);
+      this.on('click', this.handleClick);
+
+      this.on(this.player_, 'controlsvisible', this.update);
+
+      if (this.playerEvent) {
+        this.on(this.player_, this.playerEvent, this.update);
+      }
+
+      this.removeClass('disabled');
+      this.setAttribute('tabindex', 0);
+
+      this.enabled_ = true;
+    };
+
+    /**
+     * Disable controls for this slider if they are enabled
+     */
+
+
+    Slider.prototype.disable = function disable() {
+      if (!this.enabled()) {
+        return;
+      }
+      var doc = this.bar.el_.ownerDocument;
+
+      this.off('mousedown', this.handleMouseDown);
+      this.off('touchstart', this.handleMouseDown);
+      this.off('focus', this.handleFocus);
+      this.off('blur', this.handleBlur);
+      this.off('click', this.handleClick);
+      this.off(this.player_, 'controlsvisible', this.update);
+      this.off(doc, 'mousemove', this.handleMouseMove);
+      this.off(doc, 'mouseup', this.handleMouseUp);
+      this.off(doc, 'touchmove', this.handleMouseMove);
+      this.off(doc, 'touchend', this.handleMouseUp);
+      this.removeAttribute('tabindex');
+
+      this.addClass('disabled');
+
+      if (this.playerEvent) {
+        this.off(this.player_, this.playerEvent, this.update);
+      }
+      this.enabled_ = false;
+    };
+
+    /**
+     * Create the `Slider`s DOM element.
+     *
+     * @param {string} type
+     *        Type of element to create.
+     *
+     * @param {Object} [props={}]
+     *        List of properties in Object form.
+     *
+     * @param {Object} [attributes={}]
+     *        list of attributes in Object form.
+     *
+     * @return {Element}
+     *         The element that gets created.
+     */
+
+
+    Slider.prototype.createEl = function createEl$$1(type) {
+      var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+      var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
+
+      // Add the slider element class to all sub classes
+      props.className = props.className + ' vjs-slider';
+      props = assign({
+        tabIndex: 0
+      }, props);
+
+      attributes = assign({
+        'role': 'slider',
+        'aria-valuenow': 0,
+        'aria-valuemin': 0,
+        'aria-valuemax': 100,
+        'tabIndex': 0
+      }, attributes);
+
+      return _Component.prototype.createEl.call(this, type, props, attributes);
+    };
+
+    /**
+     * Handle `mousedown` or `touchstart` events on the `Slider`.
+     *
+     * @param {EventTarget~Event} event
+     *        `mousedown` or `touchstart` event that triggered this function
+     *
+     * @listens mousedown
+     * @listens touchstart
+     * @fires Slider#slideractive
+     */
+
+
+    Slider.prototype.handleMouseDown = function handleMouseDown(event) {
+      var doc = this.bar.el_.ownerDocument;
+
+      event.preventDefault();
+      blockTextSelection();
+
+      this.addClass('vjs-sliding');
+      /**
+       * Triggered when the slider is in an active state
+       *
+       * @event Slider#slideractive
+       * @type {EventTarget~Event}
+       */
+      this.trigger('slideractive');
+
+      this.on(doc, 'mousemove', this.handleMouseMove);
+      this.on(doc, 'mouseup', this.handleMouseUp);
+      this.on(doc, 'touchmove', this.handleMouseMove);
+      this.on(doc, 'touchend', this.handleMouseUp);
+
+      this.handleMouseMove(event);
+    };
+
+    /**
+     * Handle the `mousemove`, `touchmove`, and `mousedown` events on this `Slider`.
+     * The `mousemove` and `touchmove` events will only only trigger this function during
+     * `mousedown` and `touchstart`. This is due to {@link Slider#handleMouseDown} and
+     * {@link Slider#handleMouseUp}.
+     *
+     * @param {EventTarget~Event} event
+     *        `mousedown`, `mousemove`, `touchstart`, or `touchmove` event that triggered
+     *        this function
+     *
+     * @listens mousemove
+     * @listens touchmove
+     */
+
+
+    Slider.prototype.handleMouseMove = function handleMouseMove(event) {
+    };
+
+    /**
+     * Handle `mouseup` or `touchend` events on the `Slider`.
+     *
+     * @param {EventTarget~Event} event
+     *        `mouseup` or `touchend` event that triggered this function.
+     *
+     * @listens touchend
+     * @listens mouseup
+     * @fires Slider#sliderinactive
+     */
+
+
+    Slider.prototype.handleMouseUp = function handleMouseUp() {
+      var doc = this.bar.el_.ownerDocument;
+
+      unblockTextSelection();
+
+      this.removeClass('vjs-sliding');
+      /**
+       * Triggered when the slider is no longer in an active state.
+       *
+       * @event Slider#sliderinactive
+       * @type {EventTarget~Event}
+       */
+      this.trigger('sliderinactive');
+
+      this.off(doc, 'mousemove', this.handleMouseMove);
+      this.off(doc, 'mouseup', this.handleMouseUp);
+      this.off(doc, 'touchmove', this.handleMouseMove);
+      this.off(doc, 'touchend', this.handleMouseUp);
+
+      this.update();
+    };
+
+    /**
+     * Update the progress bar of the `Slider`.
+     *
+     * @returns {number}
+     *          The percentage of progress the progress bar represents as a
+     *          number from 0 to 1.
+     */
+
+
+    Slider.prototype.update = function update() {
+
+      // In VolumeBar init we have a setTimeout for update that pops and update
+      // to the end of the execution stack. The player is destroyed before then
+      // update will cause an error
+      if (!this.el_) {
+        return;
+      }
+
+      // If scrubbing, we could use a cached value to make the handle keep up
+      // with the user's mouse. On HTML5 browsers scrubbing is really smooth, but
+      // some flash players are slow, so we might want to utilize this later.
+      // var progress =  (this.player_.scrubbing()) ? this.player_.getCache().currentTime / this.player_.duration() : this.player_.currentTime() / this.player_.duration();
+      var progress = this.getPercent();
+      var bar = this.bar;
+
+      // If there's no bar...
+      if (!bar) {
+        return;
+      }
+
+      // Protect against no duration and other division issues
+      if (typeof progress !== 'number' || progress !== progress || progress < 0 || progress === Infinity) {
+        progress = 0;
+      }
+
+      // Convert to a percentage for setting
+      var percentage = (progress * 100).toFixed(2) + '%';
+      var style = bar.el().style;
+
+      // Set the new bar width or height
+      if (this.vertical()) {
+        style.height = percentage;
+      } else {
+        style.width = percentage;
+      }
+
+      return progress;
+    };
+
+    /**
+     * Calculate distance for slider
+     *
+     * @param {EventTarget~Event} event
+     *        The event that caused this function to run.
+     *
+     * @return {number}
+     *         The current position of the Slider.
+     *         - position.x for vertical `Slider`s
+     *         - position.y for horizontal `Slider`s
+     */
+
+
+    Slider.prototype.calculateDistance = function calculateDistance(event) {
+      var position = getPointerPosition(this.el_, event);
+
+      if (this.vertical()) {
+        return position.y;
+      }
+      return position.x;
+    };
+
+    /**
+     * Handle a `focus` event on this `Slider`.
+     *
+     * @param {EventTarget~Event} event
+     *        The `focus` event that caused this function to run.
+     *
+     * @listens focus
+     */
+
+
+    Slider.prototype.handleFocus = function handleFocus() {
+      this.on(this.bar.el_.ownerDocument, 'keydown', this.handleKeyPress);
+    };
+
+    /**
+     * Handle a `keydown` event on the `Slider`. Watches for left, rigth, up, and down
+     * arrow keys. This function will only be called when the slider has focus. See
+     * {@link Slider#handleFocus} and {@link Slider#handleBlur}.
+     *
+     * @param {EventTarget~Event} event
+     *        the `keydown` event that caused this function to run.
+     *
+     * @listens keydown
+     */
+
+
+    Slider.prototype.handleKeyPress = function handleKeyPress(event) {
+      // Left and Down Arrows
+      if (event.which === 37 || event.which === 40) {
+        event.preventDefault();
+        this.stepBack();
+
+        // Up and Right Arrows
+      } else if (event.which === 38 || event.which === 39) {
+        event.preventDefault();
+        this.stepForward();
+      }
+    };
+
+    /**
+     * Handle a `blur` event on this `Slider`.
+     *
+     * @param {EventTarget~Event} event
+     *        The `blur` event that caused this function to run.
+     *
+     * @listens blur
+     */
+
+    Slider.prototype.handleBlur = function handleBlur() {
+      this.off(this.bar.el_.ownerDocument, 'keydown', this.handleKeyPress);
+    };
+
+    /**
+     * Listener for click events on slider, used to prevent clicks
+     *   from bubbling up to parent elements like button menus.
+     *
+     * @param {Object} event
+     *        Event that caused this object to run
+     */
+
+
+    Slider.prototype.handleClick = function handleClick(event) {
+      event.stopImmediatePropagation();
+      event.preventDefault();
+    };
+
+    /**
+     * Get/set if slider is horizontal for vertical
+     *
+     * @param {boolean} [bool]
+     *        - true if slider is vertical,
+     *        - false is horizontal
+     *
+     * @return {boolean}
+     *         - true if slider is vertical, and getting
+     *         - false if the slider is horizontal, and getting
+     */
+
+
+    Slider.prototype.vertical = function vertical(bool) {
+      if (bool === undefined) {
+        return this.vertical_ || false;
+      }
+
+      this.vertical_ = !!bool;
+
+      if (this.vertical_) {
+        this.addClass('vjs-slider-vertical');
+      } else {
+        this.addClass('vjs-slider-horizontal');
+      }
+    };
+
+    return Slider;
+  }(Component);
+
+  Component.registerComponent('Slider', Slider);
+
+  /**
+   * @file load-progress-bar.js
+   */
+
+  /**
+   * Shows loading progress
+   *
+   * @extends Component
+   */
+
+  var LoadProgressBar = function (_Component) {
+    inherits(LoadProgressBar, _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.
+     */
+    function LoadProgressBar(player, options) {
+      classCallCheck(this, LoadProgressBar);
+
+      var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
+
+      _this.partEls_ = [];
+      _this.on(player, 'progress', _this.update);
+      return _this;
+    }
+
+    /**
+     * Create the `Component`'s DOM element
+     *
+     * @return {Element}
+     *         The element that was created.
+     */
+
+
+    LoadProgressBar.prototype.createEl = function createEl$$1() {
+      return _Component.prototype.createEl.call(this, 'div', {
+        className: 'vjs-load-progress',
+        innerHTML: '<span class="vjs-control-text"><span>' + this.localize('Loaded') + '</span>: 0%</span>'
+      });
+    };
+
+    LoadProgressBar.prototype.dispose = function dispose() {
+      this.partEls_ = null;
+
+      _Component.prototype.dispose.call(this);
+    };
+
+    /**
+     * Update progress bar
+     *
+     * @param {EventTarget~Event} [event]
+     *        The `progress` event that caused this function to run.
+     *
+     * @listens Player#progress
+     */
+
+
+    LoadProgressBar.prototype.update = function update(event) {
+      var buffered = this.player_.buffered();
+      var duration = this.player_.duration();
+      var bufferedEnd = this.player_.bufferedEnd();
+      var children = this.partEls_;
+
+      // get the percent width of a time compared to the total end
+      var percentify = function percentify(time, end) {
+        // no NaN
+        var percent = time / end || 0;
+
+        return (percent >= 1 ? 1 : percent) * 100 + '%';
+      };
+
+      // update the width of the progress bar
+      this.el_.style.width = percentify(bufferedEnd, duration);
+
+      // add child elements to represent the individual buffered time ranges
+      for (var i = 0; i < buffered.length; i++) {
+        var start = buffered.start(i);
+        var end = buffered.end(i);
+        var part = children[i];
+
+        if (!part) {
+          part = this.el_.appendChild(createEl());
+          children[i] = part;
+        }
+
+        // set the percent based on the width of the progress bar (bufferedEnd)
+        part.style.left = percentify(start, bufferedEnd);
+        part.style.width = percentify(end - start, bufferedEnd);
+      }
+
+      // remove unused buffered range elements
+      for (var _i = children.length; _i > buffered.length; _i--) {
+        this.el_.removeChild(children[_i - 1]);
+      }
+      children.length = buffered.length;
+    };
+
+    return LoadProgressBar;
+  }(Component);
+
+  Component.registerComponent('LoadProgressBar', LoadProgressBar);
+
+  /**
+   * @file time-tooltip.js
+   */
+
+  /**
+   * Time tooltips display a time above the progress bar.
+   *
+   * @extends Component
+   */
+
+  var TimeTooltip = function (_Component) {
+    inherits(TimeTooltip, _Component);
+
+    function TimeTooltip() {
+      classCallCheck(this, TimeTooltip);
+      return possibleConstructorReturn(this, _Component.apply(this, arguments));
+    }
+
+    /**
+     * Create the time tooltip DOM element
+     *
+     * @return {Element}
+     *         The element that was created.
+     */
+    TimeTooltip.prototype.createEl = function createEl$$1() {
+      return _Component.prototype.createEl.call(this, 'div', {
+        className: 'vjs-time-tooltip'
+      });
+    };
+
+    /**
+     * Updates the position of the time tooltip relative to the `SeekBar`.
+     *
+     * @param {Object} seekBarRect
+     *        The `ClientRect` for the {@link SeekBar} element.
+     *
+     * @param {number} seekBarPoint
+     *        A number from 0 to 1, representing a horizontal reference point
+     *        from the left edge of the {@link SeekBar}
+     */
+
+
+    TimeTooltip.prototype.update = function update(seekBarRect, seekBarPoint, content) {
+      var tooltipRect = getBoundingClientRect(this.el_);
+      var playerRect = getBoundingClientRect(this.player_.el());
+      var seekBarPointPx = seekBarRect.width * seekBarPoint;
+
+      // do nothing if either rect isn't available
+      // for example, if the player isn't in the DOM for testing
+      if (!playerRect || !tooltipRect) {
+        return;
+      }
+
+      // This is the space left of the `seekBarPoint` available within the bounds
+      // of the player. We calculate any gap between the left edge of the player
+      // and the left edge of the `SeekBar` and add the number of pixels in the
+      // `SeekBar` before hitting the `seekBarPoint`
+      var spaceLeftOfPoint = seekBarRect.left - playerRect.left + seekBarPointPx;
+
+      // This is the space right of the `seekBarPoint` available within the bounds
+      // of the player. We calculate the number of pixels from the `seekBarPoint`
+      // to the right edge of the `SeekBar` and add to that any gap between the
+      // right edge of the `SeekBar` and the player.
+      var spaceRightOfPoint = seekBarRect.width - seekBarPointPx + (playerRect.right - seekBarRect.right);
+
+      // This is the number of pixels by which the tooltip will need to be pulled
+      // further to the right to center it over the `seekBarPoint`.
+      var pullTooltipBy = tooltipRect.width / 2;
+
+      // Adjust the `pullTooltipBy` distance to the left or right depending on
+      // the results of the space calculations above.
+      if (spaceLeftOfPoint < pullTooltipBy) {
+        pullTooltipBy += pullTooltipBy - spaceLeftOfPoint;
+      } else if (spaceRightOfPoint < pullTooltipBy) {
+        pullTooltipBy = spaceRightOfPoint;
+      }
+
+      // Due to the imprecision of decimal/ratio based calculations and varying
+      // rounding behaviors, there are cases where the spacing adjustment is off
+      // by a pixel or two. This adds insurance to these calculations.
+      if (pullTooltipBy < 0) {
+        pullTooltipBy = 0;
+      } else if (pullTooltipBy > tooltipRect.width) {
+        pullTooltipBy = tooltipRect.width;
+      }
+
+      this.el_.style.right = '-' + pullTooltipBy + 'px';
+      textContent(this.el_, content);
+    };
+
+    return TimeTooltip;
+  }(Component);
+
+  Component.registerComponent('TimeTooltip', TimeTooltip);
+
+  /**
+   * @file playOld-progress-bar.js
+   */
+
+  /**
+   * Used by {@link SeekBar} to display media playback progress as part of the
+   * {@link ProgressControl}.
+   *
+   * @extends Component
+   */
+
+  var PlayProgressBar = function (_Component) {
+    inherits(PlayProgressBar, _Component);
+
+    function PlayProgressBar() {
+      classCallCheck(this, PlayProgressBar);
+      return possibleConstructorReturn(this, _Component.apply(this, arguments));
+    }
+
+    /**
+     * Create the the DOM element for this class.
+     *
+     * @return {Element}
+     *         The element that was created.
+     */
+    PlayProgressBar.prototype.createEl = function createEl() {
+      return _Component.prototype.createEl.call(this, 'div', {
+        className: 'vjs-playOld-progress vjs-slider-bar',
+        innerHTML: '<span class="vjs-control-text"><span>' + this.localize('Progress') + '</span>: 0%</span>'
+      });
+    };
+
+    /**
+     * Enqueues updates to its own DOM as well as the DOM of its
+     * {@link TimeTooltip} child.
+     *
+     * @param {Object} seekBarRect
+     *        The `ClientRect` for the {@link SeekBar} element.
+     *
+     * @param {number} seekBarPoint
+     *        A number from 0 to 1, representing a horizontal reference point
+     *        from the left edge of the {@link SeekBar}
+     */
+
+
+    PlayProgressBar.prototype.update = function update(seekBarRect, seekBarPoint) {
+      var _this2 = this;
+
+      // If there is an existing rAF ID, cancel it so we don't over-queue.
+      if (this.rafId_) {
+        this.cancelAnimationFrame(this.rafId_);
+      }
+
+      this.rafId_ = this.requestAnimationFrame(function () {
+        var time = _this2.player_.scrubbing() ? _this2.player_.getCache().currentTime : _this2.player_.currentTime();
+
+        var content = formatTime(time, _this2.player_.duration());
+        var timeTooltip = _this2.getChild('timeTooltip');
+
+        if (timeTooltip) {
+          timeTooltip.update(seekBarRect, seekBarPoint, content);
+        }
+      });
+    };
+
+    return PlayProgressBar;
+  }(Component);
+
+  /**
+   * Default options for {@link PlayProgressBar}.
+   *
+   * @type {Object}
+   * @private
+   */
+
+
+  PlayProgressBar.prototype.options_ = {
+    children: []
+  };
+
+  // Time tooltips should not be added to a player on mobile devices
+  if (!IS_IOS && !IS_ANDROID) {
+    PlayProgressBar.prototype.options_.children.push('timeTooltip');
+  }
+
+  Component.registerComponent('PlayProgressBar', PlayProgressBar);
+
+  /**
+   * @file mouse-time-display.js
+   */
+
+  /**
+   * The {@link MouseTimeDisplay} component tracks mouse movement over the
+   * {@link ProgressControl}. It displays an indicator and a {@link TimeTooltip}
+   * indicating the time which is represented by a given point in the
+   * {@link ProgressControl}.
+   *
+   * @extends Component
+   */
+
+  var MouseTimeDisplay = function (_Component) {
+    inherits(MouseTimeDisplay, _Component);
+
+    /**
+     * Creates an instance of this class.
+     *
+     * @param {Player} player
+     *        The {@link Player} that this class should be attached to.
+     *
+     * @param {Object} [options]
+     *        The key/value store of player options.
+     */
+    function MouseTimeDisplay(player, options) {
+      classCallCheck(this, MouseTimeDisplay);
+
+      var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
+
+      _this.update = throttle(bind(_this, _this.update), 25);
+      return _this;
+    }
+
+    /**
+     * Create the DOM element for this class.
+     *
+     * @return {Element}
+     *         The element that was created.
+     */
+
+
+    MouseTimeDisplay.prototype.createEl = function createEl() {
+      return _Component.prototype.createEl.call(this, 'div', {
+        className: 'vjs-mouse-display'
+      });
+    };
+
+    /**
+     * Enqueues updates to its own DOM as well as the DOM of its
+     * {@link TimeTooltip} child.
+     *
+     * @param {Object} seekBarRect
+     *        The `ClientRect` for the {@link SeekBar} element.
+     *
+     * @param {number} seekBarPoint
+     *        A number from 0 to 1, representing a horizontal reference point
+     *        from the left edge of the {@link SeekBar}
+     */
+
+
+    MouseTimeDisplay.prototype.update = function update(seekBarRect, seekBarPoint) {
+      var _this2 = this;
+
+      // If there is an existing rAF ID, cancel it so we don't over-queue.
+      if (this.rafId_) {
+        this.cancelAnimationFrame(this.rafId_);
+      }
+
+      this.rafId_ = this.requestAnimationFrame(function () {
+        var duration = _this2.player_.duration();
+        var content = formatTime(seekBarPoint * duration, duration);
+
+        _this2.el_.style.left = seekBarRect.width * seekBarPoint + 'px';
+        _this2.getChild('timeTooltip').update(seekBarRect, seekBarPoint, content);
+      });
+    };
+
+    return MouseTimeDisplay;
+  }(Component);
+
+  /**
+   * Default options for `MouseTimeDisplay`
+   *
+   * @type {Object}
+   * @private
+   */
+
+
+  MouseTimeDisplay.prototype.options_ = {
+    children: ['timeTooltip']
+  };
+
+  Component.registerComponent('MouseTimeDisplay', MouseTimeDisplay);
+
+  /**
+   * @file seek-bar.js
+   */
+
+    // The number of seconds the `step*` functions move the timeline.
+  var STEP_SECONDS = 5;
+
+  // The interval at which the bar should update as it progresses.
+  var UPDATE_REFRESH_INTERVAL = 30;
+
+  /**
+   * Seek bar and container for the progress bars. Uses {@link PlayProgressBar}
+   * as its `bar`.
+   *
+   * @extends Slider
+   */
+
+  var SeekBar = function (_Slider) {
+    inherits(SeekBar, _Slider);
+
+    /**
+     * 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.
+     */
+    function SeekBar(player, options) {
+      classCallCheck(this, SeekBar);
+
+      var _this = possibleConstructorReturn(this, _Slider.call(this, player, options));
+
+      _this.setEventHandlers_();
+      return _this;
+    }
+
+    /**
+     * Sets the event handlers
+     *
+     * @private
+     */
+
+
+    SeekBar.prototype.setEventHandlers_ = function setEventHandlers_() {
+      var _this2 = this;
+
+      this.update = throttle(bind(this, this.update), UPDATE_REFRESH_INTERVAL);
+
+      this.on(this.player_, 'timeupdate', this.update);
+      this.on(this.player_, 'ended', this.handleEnded);
+
+      // when playing, let's ensure we smoothly update the playOld progress bar
+      // via an interval
+      this.updateInterval = null;
+
+      this.on(this.player_, ['playing'], function () {
+        _this2.clearInterval(_this2.updateInterval);
+
+        _this2.updateInterval = _this2.setInterval(function () {
+          _this2.requestAnimationFrame(function () {
+            _this2.update();
+          });
+        }, UPDATE_REFRESH_INTERVAL);
+      });
+
+      this.on(this.player_, ['ended', 'pause', 'waiting'], function () {
+        _this2.clearInterval(_this2.updateInterval);
+      });
+
+      this.on(this.player_, ['timeupdate', 'ended'], this.update);
+    };
+
+    /**
+     * Create the `Component`'s DOM element
+     *
+     * @return {Element}
+     *         The element that was created.
+     */
+
+
+    SeekBar.prototype.createEl = function createEl$$1() {
+      return _Slider.prototype.createEl.call(this, 'div', {
+        className: 'vjs-progress-holder'
+      }, {
+        'aria-label': this.localize('Progress Bar')
+      });
+    };
+
+    /**
+     * This function updates the playOld progress bar and accessibility
+     * attributes to whatever is passed in.
+     *
+     * @param {number} currentTime
+     *        The currentTime value that should be used for accessibility
+     *
+     * @param {number} percent
+     *        The percentage as a decimal that the bar should be filled from 0-1.
+     *
+     * @private
+     */
+
+
+    SeekBar.prototype.update_ = function update_(currentTime, percent) {
+      var duration = this.player_.duration();
+
+      // machine readable value of progress bar (percentage complete)
+      this.el_.setAttribute('aria-valuenow', (percent * 100).toFixed(2));
+
+      // human readable value of progress bar (time complete)
+      this.el_.setAttribute('aria-valuetext', this.localize('progress bar timing: currentTime={1} duration={2}', [formatTime(currentTime, duration), formatTime(duration, duration)], '{1} of {2}'));
+
+      // Update the `PlayProgressBar`.
+      this.bar.update(getBoundingClientRect(this.el_), percent);
+    };
+
+    /**
+     * Update the seek bar's UI.
+     *
+     * @param {EventTarget~Event} [event]
+     *        The `timeupdate` or `ended` event that caused this to run.
+     *
+     * @listens Player#timeupdate
+     *
+     * @returns {number}
+     *          The current percent at a number from 0-1
+     */
+
+
+    SeekBar.prototype.update = function update(event) {
+      var percent = _Slider.prototype.update.call(this);
+
+      this.update_(this.getCurrentTime_(), percent);
+      return percent;
+    };
+
+    /**
+     * Get the value of current time but allows for smooth scrubbing,
+     * when player can't keep up.
+     *
+     * @return {number}
+     *         The current time value to display
+     *
+     * @private
+     */
+
+
+    SeekBar.prototype.getCurrentTime_ = function getCurrentTime_() {
+      return this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
+    };
+
+    /**
+     * We want the seek bar to be full on ended
+     * no matter what the actual internal values are. so we force it.
+     *
+     * @param {EventTarget~Event} [event]
+     *        The `timeupdate` or `ended` event that caused this to run.
+     *
+     * @listens Player#ended
+     */
+
+
+    SeekBar.prototype.handleEnded = function handleEnded(event) {
+      this.update_(this.player_.duration(), 1);
+    };
+
+    /**
+     * Get the percentage of media played so far.
+     *
+     * @return {number}
+     *         The percentage of media played so far (0 to 1).
+     */
+
+
+    SeekBar.prototype.getPercent = function getPercent() {
+      var percent = this.getCurrentTime_() / this.player_.duration();
+
+      return percent >= 1 ? 1 : percent || 0;
+    };
+
+    /**
+     * Handle mouse down on seek bar
+     *
+     * @param {EventTarget~Event} event
+     *        The `mousedown` event that caused this to run.
+     *
+     * @listens mousedown
+     */
+
+
+    SeekBar.prototype.handleMouseDown = function handleMouseDown(event) {
+      if (!isSingleLeftClick(event)) {
+        return;
+      }
+
+      // Stop event propagation to prevent double fire in progress-control.js
+      event.stopPropagation();
+      this.player_.scrubbing(true);
+
+      this.videoWasPlaying = !this.player_.paused();
+      this.player_.pause();
+
+      _Slider.prototype.handleMouseDown.call(this, event);
+    };
+
+    /**
+     * Handle mouse move on seek bar
+     *
+     * @param {EventTarget~Event} event
+     *        The `mousemove` event that caused this to run.
+     *
+     * @listens mousemove
+     */
+
+
+    SeekBar.prototype.handleMouseMove = function handleMouseMove(event) {
+      if (!isSingleLeftClick(event)) {
+        return;
+      }
+
+      var newTime = this.calculateDistance(event) * this.player_.duration();
+
+      // Don't let video end while scrubbing.
+      if (newTime === this.player_.duration()) {
+        newTime = newTime - 0.1;
+      }
+
+      // Set new time (tell player to seek to new time)
+      this.player_.currentTime(newTime);
+    };
+
+    SeekBar.prototype.enable = function enable() {
+      _Slider.prototype.enable.call(this);
+      var mouseTimeDisplay = this.getChild('mouseTimeDisplay');
+
+      if (!mouseTimeDisplay) {
+        return;
+      }
+
+      mouseTimeDisplay.show();
+    };
+
+    SeekBar.prototype.disable = function disable() {
+      _Slider.prototype.disable.call(this);
+      var mouseTimeDisplay = this.getChild('mouseTimeDisplay');
+
+      if (!mouseTimeDisplay) {
+        return;
+      }
+
+      mouseTimeDisplay.hide();
+    };
+
+    /**
+     * Handle mouse up on seek bar
+     *
+     * @param {EventTarget~Event} event
+     *        The `mouseup` event that caused this to run.
+     *
+     * @listens mouseup
+     */
+
+
+    SeekBar.prototype.handleMouseUp = function handleMouseUp(event) {
+      _Slider.prototype.handleMouseUp.call(this, event);
+
+      // Stop event propagation to prevent double fire in progress-control.js
+      if (event) {
+        event.stopPropagation();
+      }
+      this.player_.scrubbing(false);
+
+      /**
+       * Trigger timeupdate because we're done seeking and the time has changed.
+       * This is particularly useful for if the player is paused to time the time displays.
+       *
+       * @event Tech#timeupdate
+       * @type {EventTarget~Event}
+       */
+      this.player_.trigger({type: 'timeupdate', target: this, manuallyTriggered: true});
+      if (this.videoWasPlaying) {
+        silencePromise(this.player_.play());
+      }
+    };
+
+    /**
+     * Move more quickly fast forward for keyboard-only users
+     */
+
+
+    SeekBar.prototype.stepForward = function stepForward() {
+      this.player_.currentTime(this.player_.currentTime() + STEP_SECONDS);
+    };
+
+    /**
+     * Move more quickly rewind for keyboard-only users
+     */
+
+
+    SeekBar.prototype.stepBack = function stepBack() {
+      this.player_.currentTime(this.player_.currentTime() - STEP_SECONDS);
+    };
+
+    /**
+     * Toggles the playback state of the player
+     * This gets called when enter or space is used on the seekbar
+     *
+     * @param {EventTarget~Event} event
+     *        The `keydown` event that caused this function to be called
+     *
+     */
+
+
+    SeekBar.prototype.handleAction = function handleAction(event) {
+      if (this.player_.paused()) {
+        this.player_.play();
+      } else {
+        this.player_.pause();
+      }
+    };
+
+    /**
+     * Called when this SeekBar has focus and a key gets pressed down. By
+     * default it will call `this.handleAction` when the key is space or enter.
+     *
+     * @param {EventTarget~Event} event
+     *        The `keydown` event that caused this function to be called.
+     *
+     * @listens keydown
+     */
+
+
+    SeekBar.prototype.handleKeyPress = function handleKeyPress(event) {
+
+      // Support Space (32) or Enter (13) key operation to fire a click event
+      if (event.which === 32 || event.which === 13) {
+        event.preventDefault();
+        this.handleAction(event);
+      } else if (_Slider.prototype.handleKeyPress) {
+
+        // Pass keypress handling up for unsupported keys
+        _Slider.prototype.handleKeyPress.call(this, event);
+      }
+    };
+
+    return SeekBar;
+  }(Slider);
+
+  /**
+   * Default options for the `SeekBar`
+   *
+   * @type {Object}
+   * @private
+   */
+
+
+  SeekBar.prototype.options_ = {
+    children: ['loadProgressBar', 'playProgressBar'],
+    barName: 'playProgressBar'
+  };
+
+  // MouseTimeDisplay tooltips should not be added to a player on mobile devices
+  if (!IS_IOS && !IS_ANDROID) {
+    SeekBar.prototype.options_.children.splice(1, 0, 'mouseTimeDisplay');
+  }
+
+  /**
+   * Call the update event for this Slider when this event happens on the player.
+   *
+   * @type {string}
+   */
+  SeekBar.prototype.playerEvent = 'timeupdate';
+
+  Component.registerComponent('SeekBar', SeekBar);
+
+  /**
+   * @file progress-control.js
+   */
+
+  /**
+   * The Progress Control component contains the seek bar, load progress,
+   * and playOld progress.
+   *
+   * @extends Component
+   */
+
+  var ProgressControl = function (_Component) {
+    inherits(ProgressControl, _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.
+     */
+    function ProgressControl(player, options) {
+      classCallCheck(this, ProgressControl);
+
+      var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
+
+      _this.handleMouseMove = throttle(bind(_this, _this.handleMouseMove), 25);
+      _this.throttledHandleMouseSeek = throttle(bind(_this, _this.handleMouseSeek), 25);
+
+      _this.enable();
+      return _this;
+    }
+
+    /**
+     * Create the `Component`'s DOM element
+     *
+     * @return {Element}
+     *         The element that was created.
+     */
+
+
+    ProgressControl.prototype.createEl = function createEl$$1() {
+      return _Component.prototype.createEl.call(this, 'div', {
+        className: 'vjs-progress-control vjs-control'
+      });
+    };
+
+    /**
+     * When the mouse moves over the `ProgressControl`, the pointer position
+     * gets passed down to the `MouseTimeDisplay` component.
+     *
+     * @param {EventTarget~Event} event
+     *        The `mousemove` event that caused this function to run.
+     *
+     * @listen mousemove
+     */
+
+
+    ProgressControl.prototype.handleMouseMove = function handleMouseMove(event) {
+      var seekBar = this.getChild('seekBar');
+
+      if (seekBar) {
+        var mouseTimeDisplay = seekBar.getChild('mouseTimeDisplay');
+        var seekBarEl = seekBar.el();
+        var seekBarRect = getBoundingClientRect(seekBarEl);
+        var seekBarPoint = getPointerPosition(seekBarEl, event).x;
+
+        // The default skin has a gap on either side of the `SeekBar`. This means
+        // that it's possible to trigger this behavior outside the boundaries of
+        // the `SeekBar`. This ensures we stay within it at all times.
+        if (seekBarPoint > 1) {
+          seekBarPoint = 1;
+        } else if (seekBarPoint < 0) {
+          seekBarPoint = 0;
+        }
+
+        if (mouseTimeDisplay) {
+          mouseTimeDisplay.update(seekBarRect, seekBarPoint);
+        }
+      }
+    };
+
+    /**
+     * A throttled version of the {@link ProgressControl#handleMouseSeek} listener.
+     *
+     * @method ProgressControl#throttledHandleMouseSeek
+     * @param {EventTarget~Event} event
+     *        The `mousemove` event that caused this function to run.
+     *
+     * @listen mousemove
+     * @listen touchmove
+     */
+
+    /**
+     * Handle `mousemove` or `touchmove` events on the `ProgressControl`.
+     *
+     * @param {EventTarget~Event} event
+     *        `mousedown` or `touchstart` event that triggered this function
+     *
+     * @listens mousemove
+     * @listens touchmove
+     */
+
+
+    ProgressControl.prototype.handleMouseSeek = function handleMouseSeek(event) {
+      var seekBar = this.getChild('seekBar');
+
+      if (seekBar) {
+        seekBar.handleMouseMove(event);
+      }
+    };
+
+    /**
+     * Are controls are currently enabled for this progress control.
+     *
+     * @return {boolean}
+     *         true if controls are enabled, false otherwise
+     */
+
+
+    ProgressControl.prototype.enabled = function enabled() {
+      return this.enabled_;
+    };
+
+    /**
+     * Disable all controls on the progress control and its children
+     */
+
+
+    ProgressControl.prototype.disable = function disable() {
+      this.children().forEach(function (child) {
+        return child.disable && child.disable();
+      });
+
+      if (!this.enabled()) {
+        return;
+      }
+
+      this.off(['mousedown', 'touchstart'], this.handleMouseDown);
+      this.off(this.el_, 'mousemove', this.handleMouseMove);
+      this.handleMouseUp();
+
+      this.addClass('disabled');
+
+      this.enabled_ = false;
+    };
+
+    /**
+     * Enable all controls on the progress control and its children
+     */
+
+
+    ProgressControl.prototype.enable = function enable() {
+      this.children().forEach(function (child) {
+        return child.enable && child.enable();
+      });
+
+      if (this.enabled()) {
+        return;
+      }
+
+      this.on(['mousedown', 'touchstart'], this.handleMouseDown);
+      this.on(this.el_, 'mousemove', this.handleMouseMove);
+      this.removeClass('disabled');
+
+      this.enabled_ = true;
+    };
+
+    /**
+     * Handle `mousedown` or `touchstart` events on the `ProgressControl`.
+     *
+     * @param {EventTarget~Event} event
+     *        `mousedown` or `touchstart` event that triggered this function
+     *
+     * @listens mousedown
+     * @listens touchstart
+     */
+
+
+    ProgressControl.prototype.handleMouseDown = function handleMouseDown(event) {
+      var doc = this.el_.ownerDocument;
+      var seekBar = this.getChild('seekBar');
+
+      if (seekBar) {
+        seekBar.handleMouseDown(event);
+      }
+
+      this.on(doc, 'mousemove', this.throttledHandleMouseSeek);
+      this.on(doc, 'touchmove', this.throttledHandleMouseSeek);
+      this.on(doc, 'mouseup', this.handleMouseUp);
+      this.on(doc, 'touchend', this.handleMouseUp);
+    };
+
+    /**
+     * Handle `mouseup` or `touchend` events on the `ProgressControl`.
+     *
+     * @param {EventTarget~Event} event
+     *        `mouseup` or `touchend` event that triggered this function.
+     *
+     * @listens touchend
+     * @listens mouseup
+     */
+
+
+    ProgressControl.prototype.handleMouseUp = function handleMouseUp(event) {
+      var doc = this.el_.ownerDocument;
+      var seekBar = this.getChild('seekBar');
+
+      if (seekBar) {
+        seekBar.handleMouseUp(event);
+      }
+
+      this.off(doc, 'mousemove', this.throttledHandleMouseSeek);
+      this.off(doc, 'touchmove', this.throttledHandleMouseSeek);
+      this.off(doc, 'mouseup', this.handleMouseUp);
+      this.off(doc, 'touchend', this.handleMouseUp);
+    };
+
+    return ProgressControl;
+  }(Component);
+
+  /**
+   * Default options for `ProgressControl`
+   *
+   * @type {Object}
+   * @private
+   */
+
+
+  ProgressControl.prototype.options_ = {
+    children: ['seekBar']
+  };
+
+  Component.registerComponent('ProgressControl', ProgressControl);
+
+  /**
+   * @file fullscreen-toggle.js
+   */
+
+  /**
+   * Toggle fullscreen video
+   *
+   * @extends Button
+   */
+
+  var FullscreenToggle = function (_Button) {
+    inherits(FullscreenToggle, _Button);
+
+    /**
+     * 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.
+     */
+    function FullscreenToggle(player, options) {
+      classCallCheck(this, FullscreenToggle);
+
+      var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
+
+      _this.on(player, 'fullscreenchange', _this.handleFullscreenChange);
+      return _this;
+    }
+
+    /**
+     * Builds the default DOM `className`.
+     *
+     * @return {string}
+     *         The DOM `className` for this object.
+     */
+
+
+    FullscreenToggle.prototype.buildCSSClass = function buildCSSClass() {
+      return 'vjs-fullscreen-control ' + _Button.prototype.buildCSSClass.call(this);
+    };
+
+    /**
+     * Handles fullscreenchange on the player and change control text accordingly.
+     *
+     * @param {EventTarget~Event} [event]
+     *        The {@link Player#fullscreenchange} event that caused this function to be
+     *        called.
+     *
+     * @listens Player#fullscreenchange
+     */
+
+
+    FullscreenToggle.prototype.handleFullscreenChange = function handleFullscreenChange(event) {
+      if (this.player_.isFullscreen()) {
+        this.controlText('Non-Fullscreen');
+      } else {
+        this.controlText('Fullscreen');
+      }
+    };
+
+    /**
+     * This gets called when an `FullscreenToggle` is "clicked". See
+     * {@link ClickableComponent} for more detailed information on what a click can be.
+     *
+     * @param {EventTarget~Event} [event]
+     *        The `keydown`, `tap`, or `click` event that caused this function to be
+     *        called.
+     *
+     * @listens tap
+     * @listens click
+     */
+
+
+    FullscreenToggle.prototype.handleClick = function handleClick(event) {
+      if (!this.player_.isFullscreen()) {
+        this.player_.requestFullscreen();
+      } else {
+        this.player_.exitFullscreen();
+      }
+    };
+
+    return FullscreenToggle;
+  }(Button);
+
+  /**
+   * The text that should display over the `FullscreenToggle`s controls. Added for localization.
+   *
+   * @type {string}
+   * @private
+   */
+
+
+  FullscreenToggle.prototype.controlText_ = 'Fullscreen';
+
+  Component.registerComponent('FullscreenToggle', FullscreenToggle);
+
+  /**
+   * Check if volume control is supported and if it isn't hide the
+   * `Component` that was passed  using the `vjs-hidden` class.
+   *
+   * @param {Component} self
+   *        The component that should be hidden if volume is unsupported
+   *
+   * @param {Player} player
+   *        A reference to the player
+   *
+   * @private
+   */
+  var checkVolumeSupport = function checkVolumeSupport(self, player) {
+    // hide volume controls when they're not supported by the current tech
+    if (player.tech_ && !player.tech_.featuresVolumeControl) {
+      self.addClass('vjs-hidden');
+    }
+
+    self.on(player, 'loadstart', function () {
+      if (!player.tech_.featuresVolumeControl) {
+        self.addClass('vjs-hidden');
+      } else {
+        self.removeClass('vjs-hidden');
+      }
+    });
+  };
+
+  /**
+   * @file volume-level.js
+   */
+
+  /**
+   * Shows volume level
+   *
+   * @extends Component
+   */
+
+  var VolumeLevel = function (_Component) {
+    inherits(VolumeLevel, _Component);
+
+    function VolumeLevel() {
+      classCallCheck(this, VolumeLevel);
+      return possibleConstructorReturn(this, _Component.apply(this, arguments));
+    }
+
+    /**
+     * Create the `Component`'s DOM element
+     *
+     * @return {Element}
+     *         The element that was created.
+     */
+    VolumeLevel.prototype.createEl = function createEl() {
+      return _Component.prototype.createEl.call(this, 'div', {
+        className: 'vjs-volume-level',
+        innerHTML: '<span class="vjs-control-text"></span>'
+      });
+    };
+
+    return VolumeLevel;
+  }(Component);
+
+  Component.registerComponent('VolumeLevel', VolumeLevel);
+
+  /**
+   * @file volume-bar.js
+   */
+
+  /**
+   * The bar that contains the volume level and can be clicked on to adjust the level
+   *
+   * @extends Slider
+   */
+
+  var VolumeBar = function (_Slider) {
+    inherits(VolumeBar, _Slider);
+
+    /**
+     * 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.
+     */
+    function VolumeBar(player, options) {
+      classCallCheck(this, VolumeBar);
+
+      var _this = possibleConstructorReturn(this, _Slider.call(this, player, options));
+
+      _this.on('slideractive', _this.updateLastVolume_);
+      _this.on(player, 'volumechange', _this.updateARIAAttributes);
+      player.ready(function () {
+        return _this.updateARIAAttributes();
+      });
+      return _this;
+    }
+
+    /**
+     * Create the `Component`'s DOM element
+     *
+     * @return {Element}
+     *         The element that was created.
+     */
+
+
+    VolumeBar.prototype.createEl = function createEl$$1() {
+      return _Slider.prototype.createEl.call(this, 'div', {
+        className: 'vjs-volume-bar vjs-slider-bar'
+      }, {
+        'aria-label': this.localize('Volume Level'),
+        'aria-live': 'polite'
+      });
+    };
+
+    /**
+     * Handle mouse down on volume bar
+     *
+     * @param {EventTarget~Event} event
+     *        The `mousedown` event that caused this to run.
+     *
+     * @listens mousedown
+     */
+
+
+    VolumeBar.prototype.handleMouseDown = function handleMouseDown(event) {
+      if (!isSingleLeftClick(event)) {
+        return;
+      }
+
+      _Slider.prototype.handleMouseDown.call(this, event);
+    };
+
+    /**
+     * Handle movement events on the {@link VolumeMenuButton}.
+     *
+     * @param {EventTarget~Event} event
+     *        The event that caused this function to run.
+     *
+     * @listens mousemove
+     */
+
+
+    VolumeBar.prototype.handleMouseMove = function handleMouseMove(event) {
+      if (!isSingleLeftClick(event)) {
+        return;
+      }
+
+      this.checkMuted();
+      this.player_.volume(this.calculateDistance(event));
+    };
+
+    /**
+     * If the player is muted unmute it.
+     */
+
+
+    VolumeBar.prototype.checkMuted = function checkMuted() {
+      if (this.player_.muted()) {
+        this.player_.muted(false);
+      }
+    };
+
+    /**
+     * Get percent of volume level
+     *
+     * @return {number}
+     *         Volume level percent as a decimal number.
+     */
+
+
+    VolumeBar.prototype.getPercent = function getPercent() {
+      if (this.player_.muted()) {
+        return 0;
+      }
+      return this.player_.volume();
+    };
+
+    /**
+     * Increase volume level for keyboard users
+     */
+
+
+    VolumeBar.prototype.stepForward = function stepForward() {
+      this.checkMuted();
+      this.player_.volume(this.player_.volume() + 0.1);
+    };
+
+    /**
+     * Decrease volume level for keyboard users
+     */
+
+
+    VolumeBar.prototype.stepBack = function stepBack() {
+      this.checkMuted();
+      this.player_.volume(this.player_.volume() - 0.1);
+    };
+
+    /**
+     * Update ARIA accessibility attributes
+     *
+     * @param {EventTarget~Event} [event]
+     *        The `volumechange` event that caused this function to run.
+     *
+     * @listens Player#volumechange
+     */
+
+
+    VolumeBar.prototype.updateARIAAttributes = function updateARIAAttributes(event) {
+      var ariaValue = this.player_.muted() ? 0 : this.volumeAsPercentage_();
+
+      this.el_.setAttribute('aria-valuenow', ariaValue);
+      this.el_.setAttribute('aria-valuetext', ariaValue + '%');
+    };
+
+    /**
+     * Returns the current value of the player volume as a percentage
+     *
+     * @private
+     */
+
+
+    VolumeBar.prototype.volumeAsPercentage_ = function volumeAsPercentage_() {
+      return Math.round(this.player_.volume() * 100);
+    };
+
+    /**
+     * When user starts dragging the VolumeBar, store the volume and listen for
+     * the end of the drag. When the drag ends, if the volume was set to zero,
+     * set lastVolume to the stored volume.
+     *
+     * @listens slideractive
+     * @private
+     */
+
+
+    VolumeBar.prototype.updateLastVolume_ = function updateLastVolume_() {
+      var _this2 = this;
+
+      var volumeBeforeDrag = this.player_.volume();
+
+      this.one('sliderinactive', function () {
+        if (_this2.player_.volume() === 0) {
+          _this2.player_.lastVolume_(volumeBeforeDrag);
+        }
+      });
+    };
+
+    return VolumeBar;
+  }(Slider);
+
+  /**
+   * Default options for the `VolumeBar`
+   *
+   * @type {Object}
+   * @private
+   */
+
+
+  VolumeBar.prototype.options_ = {
+    children: ['volumeLevel'],
+    barName: 'volumeLevel'
+  };
+
+  /**
+   * Call the update event for this Slider when this event happens on the player.
+   *
+   * @type {string}
+   */
+  VolumeBar.prototype.playerEvent = 'volumechange';
+
+  Component.registerComponent('VolumeBar', VolumeBar);
+
+  /**
+   * @file volume-control.js
+   */
+
+  /**
+   * The component for controlling the volume level
+   *
+   * @extends Component
+   */
+
+  var VolumeControl = function (_Component) {
+    inherits(VolumeControl, _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.
+     */
+    function VolumeControl(player) {
+      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+      classCallCheck(this, VolumeControl);
+
+      options.vertical = options.vertical || false;
+
+      // Pass the vertical option down to the VolumeBar if
+      // the VolumeBar is turned on.
+      if (typeof options.volumeBar === 'undefined' || isPlain(options.volumeBar)) {
+        options.volumeBar = options.volumeBar || {};
+        options.volumeBar.vertical = options.vertical;
+      }
+
+      // hide this control if volume support is missing
+      var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
+
+      checkVolumeSupport(_this, player);
+
+      _this.throttledHandleMouseMove = throttle(bind(_this, _this.handleMouseMove), 25);
+
+      _this.on('mousedown', _this.handleMouseDown);
+      _this.on('touchstart', _this.handleMouseDown);
+
+      // while the slider is active (the mouse has been pressed down and
+      // is dragging) or in focus we do not want to hide the VolumeBar
+      _this.on(_this.volumeBar, ['focus', 'slideractive'], function () {
+        _this.volumeBar.addClass('vjs-slider-active');
+        _this.addClass('vjs-slider-active');
+        _this.trigger('slideractive');
+      });
+
+      _this.on(_this.volumeBar, ['blur', 'sliderinactive'], function () {
+        _this.volumeBar.removeClass('vjs-slider-active');
+        _this.removeClass('vjs-slider-active');
+        _this.trigger('sliderinactive');
+      });
+      return _this;
+    }
+
+    /**
+     * Create the `Component`'s DOM element
+     *
+     * @return {Element}
+     *         The element that was created.
+     */
+
+
+    VolumeControl.prototype.createEl = function createEl() {
+      var orientationClass = 'vjs-volume-horizontal';
+
+      if (this.options_.vertical) {
+        orientationClass = 'vjs-volume-vertical';
+      }
+
+      return _Component.prototype.createEl.call(this, 'div', {
+        className: 'vjs-volume-control vjs-control ' + orientationClass
+      });
+    };
+
+    /**
+     * Handle `mousedown` or `touchstart` events on the `VolumeControl`.
+     *
+     * @param {EventTarget~Event} event
+     *        `mousedown` or `touchstart` event that triggered this function
+     *
+     * @listens mousedown
+     * @listens touchstart
+     */
+
+
+    VolumeControl.prototype.handleMouseDown = function handleMouseDown(event) {
+      var doc = this.el_.ownerDocument;
+
+      this.on(doc, 'mousemove', this.throttledHandleMouseMove);
+      this.on(doc, 'touchmove', this.throttledHandleMouseMove);
+      this.on(doc, 'mouseup', this.handleMouseUp);
+      this.on(doc, 'touchend', this.handleMouseUp);
+    };
+
+    /**
+     * Handle `mouseup` or `touchend` events on the `VolumeControl`.
+     *
+     * @param {EventTarget~Event} event
+     *        `mouseup` or `touchend` event that triggered this function.
+     *
+     * @listens touchend
+     * @listens mouseup
+     */
+
+
+    VolumeControl.prototype.handleMouseUp = function handleMouseUp(event) {
+      var doc = this.el_.ownerDocument;
+
+      this.off(doc, 'mousemove', this.throttledHandleMouseMove);
+      this.off(doc, 'touchmove', this.throttledHandleMouseMove);
+      this.off(doc, 'mouseup', this.handleMouseUp);
+      this.off(doc, 'touchend', this.handleMouseUp);
+    };
+
+    /**
+     * Handle `mousedown` or `touchstart` events on the `VolumeControl`.
+     *
+     * @param {EventTarget~Event} event
+     *        `mousedown` or `touchstart` event that triggered this function
+     *
+     * @listens mousedown
+     * @listens touchstart
+     */
+
+
+    VolumeControl.prototype.handleMouseMove = function handleMouseMove(event) {
+      this.volumeBar.handleMouseMove(event);
+    };
+
+    return VolumeControl;
+  }(Component);
+
+  /**
+   * Default options for the `VolumeControl`
+   *
+   * @type {Object}
+   * @private
+   */
+
+
+  VolumeControl.prototype.options_ = {
+    children: ['volumeBar']
+  };
+
+  Component.registerComponent('VolumeControl', VolumeControl);
+
+  /**
+   * @file mute-toggle.js
+   */
+
+  /**
+   * A button component for muting the audio.
+   *
+   * @extends Button
+   */
+
+  var MuteToggle = function (_Button) {
+    inherits(MuteToggle, _Button);
+
+    /**
+     * 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.
+     */
+    function MuteToggle(player, options) {
+      classCallCheck(this, MuteToggle);
+
+      // hide this control if volume support is missing
+      var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
+
+      checkVolumeSupport(_this, player);
+
+      _this.on(player, ['loadstart', 'volumechange'], _this.update);
+      return _this;
+    }
+
+    /**
+     * Builds the default DOM `className`.
+     *
+     * @return {string}
+     *         The DOM `className` for this object.
+     */
+
+
+    MuteToggle.prototype.buildCSSClass = function buildCSSClass() {
+      return 'vjs-mute-control ' + _Button.prototype.buildCSSClass.call(this);
+    };
+
+    /**
+     * This gets called when an `MuteToggle` is "clicked". See
+     * {@link ClickableComponent} for more detailed information on what a click can be.
+     *
+     * @param {EventTarget~Event} [event]
+     *        The `keydown`, `tap`, or `click` event that caused this function to be
+     *        called.
+     *
+     * @listens tap
+     * @listens click
+     */
+
+
+    MuteToggle.prototype.handleClick = function handleClick(event) {
+      var vol = this.player_.volume();
+      var lastVolume = this.player_.lastVolume_();
+
+      if (vol === 0) {
+        var volumeToSet = lastVolume < 0.1 ? 0.1 : lastVolume;
+
+        this.player_.volume(volumeToSet);
+        this.player_.muted(false);
+      } else {
+        this.player_.muted(this.player_.muted() ? false : true);
+      }
+    };
+
+    /**
+     * Update the `MuteToggle` button based on the state of `volume` and `muted`
+     * on the player.
+     *
+     * @param {EventTarget~Event} [event]
+     *        The {@link Player#loadstart} event if this function was called
+     *        through an event.
+     *
+     * @listens Player#loadstart
+     * @listens Player#volumechange
+     */
+
+
+    MuteToggle.prototype.update = function update(event) {
+      this.updateIcon_();
+      this.updateControlText_();
+    };
+
+    /**
+     * Update the appearance of the `MuteToggle` icon.
+     *
+     * Possible states (given `level` variable below):
+     * - 0: crossed out
+     * - 1: zero bars of volume
+     * - 2: one bar of volume
+     * - 3: two bars of volume
+     *
+     * @private
+     */
+
+
+    MuteToggle.prototype.updateIcon_ = function updateIcon_() {
+      var vol = this.player_.volume();
+      var level = 3;
+
+      if (vol === 0 || this.player_.muted()) {
+        level = 0;
+      } else if (vol < 0.33) {
+        level = 1;
+      } else if (vol < 0.67) {
+        level = 2;
+      }
+
+      // TODO improve muted icon classes
+      for (var i = 0; i < 4; i++) {
+        removeClass(this.el_, 'vjs-vol-' + i);
+      }
+      addClass(this.el_, 'vjs-vol-' + level);
+    };
+
+    /**
+     * If `muted` has changed on the player, update the control text
+     * (`title` attribute on `vjs-mute-control` element and content of
+     * `vjs-control-text` element).
+     *
+     * @private
+     */
+
+
+    MuteToggle.prototype.updateControlText_ = function updateControlText_() {
+      var soundOff = this.player_.muted() || this.player_.volume() === 0;
+      var text = soundOff ? 'Unmute' : 'Mute';
+
+      if (this.controlText() !== text) {
+        this.controlText(text);
+      }
+    };
+
+    return MuteToggle;
+  }(Button);
+
+  /**
+   * The text that should display over the `MuteToggle`s controls. Added for localization.
+   *
+   * @type {string}
+   * @private
+   */
+
+
+  MuteToggle.prototype.controlText_ = 'Mute';
+
+  Component.registerComponent('MuteToggle', MuteToggle);
+
+  /**
+   * @file volume-control.js
+   */
+
+  /**
+   * A Component to contain the MuteToggle and VolumeControl so that
+   * they can work together.
+   *
+   * @extends Component
+   */
+
+  var VolumePanel = function (_Component) {
+    inherits(VolumePanel, _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.
+     */
+    function VolumePanel(player) {
+      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+      classCallCheck(this, VolumePanel);
+
+      if (typeof options.inline !== 'undefined') {
+        options.inline = options.inline;
+      } else {
+        options.inline = true;
+      }
+
+      // pass the inline option down to the VolumeControl as vertical if
+      // the VolumeControl is on.
+      if (typeof options.volumeControl === 'undefined' || isPlain(options.volumeControl)) {
+        options.volumeControl = options.volumeControl || {};
+        options.volumeControl.vertical = !options.inline;
+      }
+
+      // hide this control if volume support is missing
+      var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
+
+      checkVolumeSupport(_this, player);
+
+      // while the slider is active (the mouse has been pressed down and
+      // is dragging) we do not want to hide the VolumeBar
+      _this.on(_this.volumeControl, ['slideractive'], _this.sliderActive_);
+
+      _this.on(_this.volumeControl, ['sliderinactive'], _this.sliderInactive_);
+      return _this;
+    }
+
+    /**
+     * Add vjs-slider-active class to the VolumePanel
+     *
+     * @listens VolumeControl#slideractive
+     * @private
+     */
+
+
+    VolumePanel.prototype.sliderActive_ = function sliderActive_() {
+      this.addClass('vjs-slider-active');
+    };
+
+    /**
+     * Removes vjs-slider-active class to the VolumePanel
+     *
+     * @listens VolumeControl#sliderinactive
+     * @private
+     */
+
+
+    VolumePanel.prototype.sliderInactive_ = function sliderInactive_() {
+      this.removeClass('vjs-slider-active');
+    };
+
+    /**
+     * Create the `Component`'s DOM element
+     *
+     * @return {Element}
+     *         The element that was created.
+     */
+
+
+    VolumePanel.prototype.createEl = function createEl() {
+      var orientationClass = 'vjs-volume-panel-horizontal';
+
+      if (!this.options_.inline) {
+        orientationClass = 'vjs-volume-panel-vertical';
+      }
+
+      return _Component.prototype.createEl.call(this, 'div', {
+        className: 'vjs-volume-panel vjs-control ' + orientationClass
+      });
+    };
+
+    return VolumePanel;
+  }(Component);
+
+  /**
+   * Default options for the `VolumeControl`
+   *
+   * @type {Object}
+   * @private
+   */
+
+
+  VolumePanel.prototype.options_ = {
+    children: ['muteToggle', 'volumeControl']
+  };
+
+  Component.registerComponent('VolumePanel', VolumePanel);
+
+  /**
+   * @file menu.js
+   */
+
+  /**
+   * The Menu component is used to build popup menus, including subtitle and
+   * captions selection menus.
+   *
+   * @extends Component
+   */
+
+  var Menu = function (_Component) {
+    inherits(Menu, _Component);
+
+    /**
+     * Create an instance of this class.
+     *
+     * @param {Player} player
+     *        the player that this component should attach to
+     *
+     * @param {Object} [options]
+     *        Object of option names and values
+     *
+     */
+    function Menu(player, options) {
+      classCallCheck(this, Menu);
+
+      var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
+
+      if (options) {
+        _this.menuButton_ = options.menuButton;
+      }
+
+      _this.focusedChild_ = -1;
+
+      _this.on('keydown', _this.handleKeyPress);
+      return _this;
+    }
+
+    /**
+     * Add a {@link MenuItem} to the menu.
+     *
+     * @param {Object|string} component
+     *        The name or instance of the `MenuItem` to add.
+     *
+     */
+
+
+    Menu.prototype.addItem = function addItem(component) {
+      this.addChild(component);
+      component.on('click', bind(this, function (event) {
+        // Unpress the associated MenuButton, and move focus back to it
+        if (this.menuButton_) {
+          this.menuButton_.unpressButton();
+
+          // don't focus menu button if item is a caption settings item
+          // because focus will move elsewhere
+          if (component.name() !== 'CaptionSettingsMenuItem') {
+            this.menuButton_.focus();
+          }
+        }
+      }));
+    };
+
+    /**
+     * Create the `Menu`s DOM element.
+     *
+     * @return {Element}
+     *         the element that was created
+     */
+
+
+    Menu.prototype.createEl = function createEl$$1() {
+      var contentElType = this.options_.contentElType || 'ul';
+
+      this.contentEl_ = createEl(contentElType, {
+        className: 'vjs-menu-content'
+      });
+
+      this.contentEl_.setAttribute('role', 'menu');
+
+      var el = _Component.prototype.createEl.call(this, 'div', {
+        append: this.contentEl_,
+        className: 'vjs-menu'
+      });
+
+      el.appendChild(this.contentEl_);
+
+      // Prevent clicks from bubbling up. Needed for Menu Buttons,
+      // where a click on the parent is significant
+      on(el, 'click', function (event) {
+        event.preventDefault();
+        event.stopImmediatePropagation();
+      });
+
+      return el;
+    };
+
+    Menu.prototype.dispose = function dispose() {
+      this.contentEl_ = null;
+
+      _Component.prototype.dispose.call(this);
+    };
+
+    /**
+     * Handle a `keydown` event on this menu. This listener is added in the constructor.
+     *
+     * @param {EventTarget~Event} event
+     *        A `keydown` event that happened on the menu.
+     *
+     * @listens keydown
+     */
+
+
+    Menu.prototype.handleKeyPress = function handleKeyPress(event) {
+      // Left and Down Arrows
+      if (event.which === 37 || event.which === 40) {
+        event.preventDefault();
+        this.stepForward();
+
+        // Up and Right Arrows
+      } else if (event.which === 38 || event.which === 39) {
+        event.preventDefault();
+        this.stepBack();
+      }
+    };
+
+    /**
+     * Move to next (lower) menu item for keyboard users.
+     */
+
+
+    Menu.prototype.stepForward = function stepForward() {
+      var stepChild = 0;
+
+      if (this.focusedChild_ !== undefined) {
+        stepChild = this.focusedChild_ + 1;
+      }
+      this.focus(stepChild);
+    };
+
+    /**
+     * Move to previous (higher) menu item for keyboard users.
+     */
+
+
+    Menu.prototype.stepBack = function stepBack() {
+      var stepChild = 0;
+
+      if (this.focusedChild_ !== undefined) {
+        stepChild = this.focusedChild_ - 1;
+      }
+      this.focus(stepChild);
+    };
+
+    /**
+     * Set focus on a {@link MenuItem} in the `Menu`.
+     *
+     * @param {Object|string} [item=0]
+     *        Index of child item set focus on.
+     */
+
+
+    Menu.prototype.focus = function focus() {
+      var item = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
+
+      var children = this.children().slice();
+      var haveTitle = children.length && children[0].className && /vjs-menu-title/.test(children[0].className);
+
+      if (haveTitle) {
+        children.shift();
+      }
+
+      if (children.length > 0) {
+        if (item < 0) {
+          item = 0;
+        } else if (item >= children.length) {
+          item = children.length - 1;
+        }
+
+        this.focusedChild_ = item;
+
+        children[item].el_.focus();
+      }
+    };
+
+    return Menu;
+  }(Component);
+
+  Component.registerComponent('Menu', Menu);
+
+  /**
+   * @file menu-button.js
+   */
+
+  /**
+   * A `MenuButton` class for any popup {@link Menu}.
+   *
+   * @extends Component
+   */
+
+  var MenuButton = function (_Component) {
+    inherits(MenuButton, _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.
+     */
+    function MenuButton(player) {
+      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+      classCallCheck(this, MenuButton);
+
+      var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
+
+      _this.menuButton_ = new Button(player, options);
+
+      _this.menuButton_.controlText(_this.controlText_);
+      _this.menuButton_.el_.setAttribute('aria-haspopup', 'true');
+
+      // Add buildCSSClass values to the button, not the wrapper
+      var buttonClass = Button.prototype.buildCSSClass();
+
+      _this.menuButton_.el_.className = _this.buildCSSClass() + ' ' + buttonClass;
+      _this.menuButton_.removeClass('vjs-control');
+
+      _this.addChild(_this.menuButton_);
+
+      _this.update();
+
+      _this.enabled_ = true;
+
+      _this.on(_this.menuButton_, 'tap', _this.handleClick);
+      _this.on(_this.menuButton_, 'click', _this.handleClick);
+      _this.on(_this.menuButton_, 'focus', _this.handleFocus);
+      _this.on(_this.menuButton_, 'blur', _this.handleBlur);
+
+      _this.on('keydown', _this.handleSubmenuKeyPress);
+      return _this;
+    }
+
+    /**
+     * Update the menu based on the current state of its items.
+     */
+
+
+    MenuButton.prototype.update = function update() {
+      var menu = this.createMenu();
+
+      if (this.menu) {
+        this.menu.dispose();
+        this.removeChild(this.menu);
+      }
+
+      this.menu = menu;
+      this.addChild(menu);
+
+      /**
+       * Track the state of the menu button
+       *
+       * @type {Boolean}
+       * @private
+       */
+      this.buttonPressed_ = false;
+      this.menuButton_.el_.setAttribute('aria-expanded', 'false');
+
+      if (this.items && this.items.length <= this.hideThreshold_) {
+        this.hide();
+      } else {
+        this.show();
+      }
+    };
+
+    /**
+     * Create the menu and add all items to it.
+     *
+     * @return {Menu}
+     *         The constructed menu
+     */
+
+
+    MenuButton.prototype.createMenu = function createMenu() {
+      var menu = new Menu(this.player_, {menuButton: this});
+
+      /**
+       * Hide the menu if the number of items is less than or equal to this threshold. This defaults
+       * to 0 and whenever we add items which can be hidden to the menu we'll increment it. We list
+       * it here because every time we run `createMenu` we need to reset the value.
+       *
+       * @protected
+       * @type {Number}
+       */
+      this.hideThreshold_ = 0;
+
+      // Add a title list item to the top
+      if (this.options_.title) {
+        var title = createEl('li', {
+          className: 'vjs-menu-title',
+          innerHTML: toTitleCase(this.options_.title),
+          tabIndex: -1
+        });
+
+        this.hideThreshold_ += 1;
+
+        menu.children_.unshift(title);
+        prependTo(title, menu.contentEl());
+      }
+
+      this.items = this.createItems();
+
+      if (this.items) {
+        // Add menu items to the menu
+        for (var i = 0; i < this.items.length; i++) {
+          menu.addItem(this.items[i]);
+        }
+      }
+
+      return menu;
+    };
+
+    /**
+     * Create the list of menu items. Specific to each subclass.
+     *
+     * @abstract
+     */
+
+
+    MenuButton.prototype.createItems = function createItems() {
+    };
+
+    /**
+     * Create the `MenuButtons`s DOM element.
+     *
+     * @return {Element}
+     *         The element that gets created.
+     */
+
+
+    MenuButton.prototype.createEl = function createEl$$1() {
+      return _Component.prototype.createEl.call(this, 'div', {
+        className: this.buildWrapperCSSClass()
+      }, {});
+    };
+
+    /**
+     * Allow sub components to stack CSS class names for the wrapper element
+     *
+     * @return {string}
+     *         The constructed wrapper DOM `className`
+     */
+
+
+    MenuButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
+      var menuButtonClass = 'vjs-menu-button';
+
+      // If the inline option is passed, we want to use different styles altogether.
+      if (this.options_.inline === true) {
+        menuButtonClass += '-inline';
+      } else {
+        menuButtonClass += '-popup';
+      }
+
+      // TODO: Fix the CSS so that this isn't necessary
+      var buttonClass = Button.prototype.buildCSSClass();
+
+      return 'vjs-menu-button ' + menuButtonClass + ' ' + buttonClass + ' ' + _Component.prototype.buildCSSClass.call(this);
+    };
+
+    /**
+     * Builds the default DOM `className`.
+     *
+     * @return {string}
+     *         The DOM `className` for this object.
+     */
+
+
+    MenuButton.prototype.buildCSSClass = function buildCSSClass() {
+      var menuButtonClass = 'vjs-menu-button';
+
+      // If the inline option is passed, we want to use different styles altogether.
+      if (this.options_.inline === true) {
+        menuButtonClass += '-inline';
+      } else {
+        menuButtonClass += '-popup';
+      }
+
+      return 'vjs-menu-button ' + menuButtonClass + ' ' + _Component.prototype.buildCSSClass.call(this);
+    };
+
+    /**
+     * Get or set the localized control text that will be used for accessibility.
+     *
+     * > NOTE: This will come from the internal `menuButton_` element.
+     *
+     * @param {string} [text]
+     *        Control text for element.
+     *
+     * @param {Element} [el=this.menuButton_.el()]
+     *        Element to set the title on.
+     *
+     * @return {string}
+     *         - The control text when getting
+     */
+
+
+    MenuButton.prototype.controlText = function controlText(text) {
+      var el = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.menuButton_.el();
+
+      return this.menuButton_.controlText(text, el);
+    };
+
+    /**
+     * Handle a click on a `MenuButton`.
+     * See {@link ClickableComponent#handleClick} for instances where this is called.
+     *
+     * @param {EventTarget~Event} event
+     *        The `keydown`, `tap`, or `click` event that caused this function to be
+     *        called.
+     *
+     * @listens tap
+     * @listens click
+     */
+
+
+    MenuButton.prototype.handleClick = function handleClick(event) {
+      // When you click the button it adds focus, which will show the menu.
+      // So we'll remove focus when the mouse leaves the button. Focus is needed
+      // for tab navigation.
+
+      this.one(this.menu.contentEl(), 'mouseleave', bind(this, function (e) {
+        this.unpressButton();
+        this.el_.blur();
+      }));
+      if (this.buttonPressed_) {
+        this.unpressButton();
+      } else {
+        this.pressButton();
+      }
+    };
+
+    /**
+     * Set the focus to the actual button, not to this element
+     */
+
+
+    MenuButton.prototype.focus = function focus() {
+      this.menuButton_.focus();
+    };
+
+    /**
+     * Remove the focus from the actual button, not this element
+     */
+
+
+    MenuButton.prototype.blur = function blur() {
+      this.menuButton_.blur();
+    };
+
+    /**
+     * This gets called when a `MenuButton` gains focus via a `focus` event.
+     * Turns on listening for `keydown` events. When they happen it
+     * calls `this.handleKeyPress`.
+     *
+     * @param {EventTarget~Event} event
+     *        The `focus` event that caused this function to be called.
+     *
+     * @listens focus
+     */
+
+
+    MenuButton.prototype.handleFocus = function handleFocus() {
+      on(document_1, 'keydown', bind(this, this.handleKeyPress));
+    };
+
+    /**
+     * Called when a `MenuButton` loses focus. Turns off the listener for
+     * `keydown` events. Which Stops `this.handleKeyPress` from getting called.
+     *
+     * @param {EventTarget~Event} event
+     *        The `blur` event that caused this function to be called.
+     *
+     * @listens blur
+     */
+
+
+    MenuButton.prototype.handleBlur = function handleBlur() {
+      off(document_1, 'keydown', bind(this, this.handleKeyPress));
+    };
+
+    /**
+     * Handle tab, escape, down arrow, and up arrow keys for `MenuButton`. See
+     * {@link ClickableComponent#handleKeyPress} for instances where this is called.
+     *
+     * @param {EventTarget~Event} event
+     *        The `keydown` event that caused this function to be called.
+     *
+     * @listens keydown
+     */
+
+
+    MenuButton.prototype.handleKeyPress = function handleKeyPress(event) {
+
+      // Escape (27) key or Tab (9) key unpress the 'button'
+      if (event.which === 27 || event.which === 9) {
+        if (this.buttonPressed_) {
+          this.unpressButton();
+        }
+        // Don't preventDefault for Tab key - we still want to lose focus
+        if (event.which !== 9) {
+          event.preventDefault();
+          // Set focus back to the menu button's button
+          this.menuButton_.el_.focus();
+        }
+        // Up (38) key or Down (40) key press the 'button'
+      } else if (event.which === 38 || event.which === 40) {
+        if (!this.buttonPressed_) {
+          this.pressButton();
+          event.preventDefault();
+        }
+      }
+    };
+
+    /**
+     * Handle a `keydown` event on a sub-menu. The listener for this is added in
+     * the constructor.
+     *
+     * @param {EventTarget~Event} event
+     *        Key press event
+     *
+     * @listens keydown
+     */
+
+
+    MenuButton.prototype.handleSubmenuKeyPress = function handleSubmenuKeyPress(event) {
+
+      // Escape (27) key or Tab (9) key unpress the 'button'
+      if (event.which === 27 || event.which === 9) {
+        if (this.buttonPressed_) {
+          this.unpressButton();
+        }
+        // Don't preventDefault for Tab key - we still want to lose focus
+        if (event.which !== 9) {
+          event.preventDefault();
+          // Set focus back to the menu button's button
+          this.menuButton_.el_.focus();
+        }
+      }
+    };
+
+    /**
+     * Put the current `MenuButton` into a pressed state.
+     */
+
+
+    MenuButton.prototype.pressButton = function pressButton() {
+      if (this.enabled_) {
+        this.buttonPressed_ = true;
+        this.menu.lockShowing();
+        this.menuButton_.el_.setAttribute('aria-expanded', 'true');
+
+        // set the focus into the submenu, except on iOS where it is resulting in
+        // undesired scrolling behavior when the player is in an iframe
+        if (IS_IOS && isInFrame()) {
+          // Return early so that the menu isn't focused
+          return;
+        }
+
+        this.menu.focus();
+      }
+    };
+
+    /**
+     * Take the current `MenuButton` out of a pressed state.
+     */
+
+
+    MenuButton.prototype.unpressButton = function unpressButton() {
+      if (this.enabled_) {
+        this.buttonPressed_ = false;
+        this.menu.unlockShowing();
+        this.menuButton_.el_.setAttribute('aria-expanded', 'false');
+      }
+    };
+
+    /**
+     * Disable the `MenuButton`. Don't allow it to be clicked.
+     */
+
+
+    MenuButton.prototype.disable = function disable() {
+      this.unpressButton();
+
+      this.enabled_ = false;
+      this.addClass('vjs-disabled');
+
+      this.menuButton_.disable();
+    };
+
+    /**
+     * Enable the `MenuButton`. Allow it to be clicked.
+     */
+
+
+    MenuButton.prototype.enable = function enable() {
+      this.enabled_ = true;
+      this.removeClass('vjs-disabled');
+
+      this.menuButton_.enable();
+    };
+
+    return MenuButton;
+  }(Component);
+
+  Component.registerComponent('MenuButton', MenuButton);
+
+  /**
+   * @file track-button.js
+   */
+
+  /**
+   * The base class for buttons that toggle specific  track types (e.g. subtitles).
+   *
+   * @extends MenuButton
+   */
+
+  var TrackButton = function (_MenuButton) {
+    inherits(TrackButton, _MenuButton);
+
+    /**
+     * 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.
+     */
+    function TrackButton(player, options) {
+      classCallCheck(this, TrackButton);
+
+      var tracks = options.tracks;
+
+      var _this = possibleConstructorReturn(this, _MenuButton.call(this, player, options));
+
+      if (_this.items.length <= 1) {
+        _this.hide();
+      }
+
+      if (!tracks) {
+        return possibleConstructorReturn(_this);
+      }
+
+      var updateHandler = bind(_this, _this.update);
+
+      tracks.addEventListener('removetrack', updateHandler);
+      tracks.addEventListener('addtrack', updateHandler);
+      _this.player_.on('ready', updateHandler);
+
+      _this.player_.on('dispose', function () {
+        tracks.removeEventListener('removetrack', updateHandler);
+        tracks.removeEventListener('addtrack', updateHandler);
+      });
+      return _this;
+    }
+
+    return TrackButton;
+  }(MenuButton);
+
+  Component.registerComponent('TrackButton', TrackButton);
+
+  /**
+   * @file menu-item.js
+   */
+
+  /**
+   * The component for a menu item. `<li>`
+   *
+   * @extends ClickableComponent
+   */
+
+  var MenuItem = function (_ClickableComponent) {
+    inherits(MenuItem, _ClickableComponent);
+
+    /**
+     * Creates an instance of the this class.
+     *
+     * @param {Player} player
+     *        The `Player` that this class should be attached to.
+     *
+     * @param {Object} [options={}]
+     *        The key/value store of player options.
+     *
+     */
+    function MenuItem(player, options) {
+      classCallCheck(this, MenuItem);
+
+      var _this = possibleConstructorReturn(this, _ClickableComponent.call(this, player, options));
+
+      _this.selectable = options.selectable;
+      _this.isSelected_ = options.selected || false;
+
+      _this.selected(_this.isSelected_);
+
+      if (_this.selectable) {
+        // TODO: May need to be either menuitemcheckbox or menuitemradio,
+        //       and may need logical grouping of menu items.
+        _this.el_.setAttribute('role', 'menuitemcheckbox');
+      } else {
+        _this.el_.setAttribute('role', 'menuitem');
+      }
+      return _this;
+    }
+
+    /**
+     * Create the `MenuItem's DOM element
+     *
+     * @param {string} [type=li]
+     *        Element's node type, not actually used, always set to `li`.
+     *
+     * @param {Object} [props={}]
+     *        An object of properties that should be set on the element
+     *
+     * @param {Object} [attrs={}]
+     *        An object of attributes that should be set on the element
+     *
+     * @return {Element}
+     *         The element that gets created.
+     */
+
+
+    MenuItem.prototype.createEl = function createEl(type, props, attrs) {
+      // The control is textual, not just an icon
+      this.nonIconControl = true;
+
+      return _ClickableComponent.prototype.createEl.call(this, 'li', assign({
+        className: 'vjs-menu-item',
+        innerHTML: '<span class="vjs-menu-item-text">' + this.localize(this.options_.label) + '</span>',
+        tabIndex: -1
+      }, props), attrs);
+    };
+
+    /**
+     * Any click on a `MenuItem` puts int into the selected state.
+     * See {@link ClickableComponent#handleClick} for instances where this is called.
+     *
+     * @param {EventTarget~Event} event
+     *        The `keydown`, `tap`, or `click` event that caused this function to be
+     *        called.
+     *
+     * @listens tap
+     * @listens click
+     */
+
+
+    MenuItem.prototype.handleClick = function handleClick(event) {
+      this.selected(true);
+    };
+
+    /**
+     * Set the state for this menu item as selected or not.
+     *
+     * @param {boolean} selected
+     *        if the menu item is selected or not
+     */
+
+
+    MenuItem.prototype.selected = function selected(_selected) {
+      if (this.selectable) {
+        if (_selected) {
+          this.addClass('vjs-selected');
+          this.el_.setAttribute('aria-checked', 'true');
+          // aria-checked isn't fully supported by browsers/screen readers,
+          // so indicate selected state to screen reader in the control text.
+          this.controlText(', selected');
+          this.isSelected_ = true;
+        } else {
+          this.removeClass('vjs-selected');
+          this.el_.setAttribute('aria-checked', 'false');
+          // Indicate un-selected state to screen reader
+          this.controlText('');
+          this.isSelected_ = false;
+        }
+      }
+    };
+
+    return MenuItem;
+  }(ClickableComponent);
+
+  Component.registerComponent('MenuItem', MenuItem);
+
+  /**
+   * @file text-track-menu-item.js
+   */
+
+  /**
+   * The specific menu item type for selecting a language within a text track kind
+   *
+   * @extends MenuItem
+   */
+
+  var TextTrackMenuItem = function (_MenuItem) {
+    inherits(TextTrackMenuItem, _MenuItem);
+
+    /**
+     * 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.
+     */
+    function TextTrackMenuItem(player, options) {
+      classCallCheck(this, TextTrackMenuItem);
+
+      var track = options.track;
+      var tracks = player.textTracks();
+
+      // Modify options for parent MenuItem class's init.
+      options.label = track.label || track.language || 'Unknown';
+      options.selected = track.mode === 'showing';
+
+      var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options));
+
+      _this.track = track;
+      var changeHandler = function changeHandler() {
+        for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+          args[_key] = arguments[_key];
+        }
+
+        _this.handleTracksChange.apply(_this, args);
+      };
+      var selectedLanguageChangeHandler = function selectedLanguageChangeHandler() {
+        for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
+          args[_key2] = arguments[_key2];
+        }
+
+        _this.handleSelectedLanguageChange.apply(_this, args);
+      };
+
+      player.on(['loadstart', 'texttrackchange'], changeHandler);
+      tracks.addEventListener('change', changeHandler);
+      tracks.addEventListener('selectedlanguagechange', selectedLanguageChangeHandler);
+      _this.on('dispose', function () {
+        player.off(['loadstart', 'texttrackchange'], changeHandler);
+        tracks.removeEventListener('change', changeHandler);
+        tracks.removeEventListener('selectedlanguagechange', selectedLanguageChangeHandler);
+      });
+
+      // iOS7 doesn't dispatch change events to TextTrackLists when an
+      // associated track's mode changes. Without something like
+      // Object.observe() (also not present on iOS7), it's not
+      // possible to detect changes to the mode attribute and polyfill
+      // the change event. As a poor substitute, we manually dispatch
+      // change events whenever the controls modify the mode.
+      if (tracks.onchange === undefined) {
+        var event = void 0;
+
+        _this.on(['tap', 'click'], function () {
+          if (_typeof(window_1.Event) !== 'object') {
+            // Android 2.3 throws an Illegal Constructor error for window.Event
+            try {
+              event = new window_1.Event('change');
+            } catch (err) {
+              // continue regardless of error
+            }
+          }
+
+          if (!event) {
+            event = document_1.createEvent('Event');
+            event.initEvent('change', true, true);
+          }
+
+          tracks.dispatchEvent(event);
+        });
+      }
+
+      // set the default state based on current tracks
+      _this.handleTracksChange();
+      return _this;
+    }
+
+    /**
+     * This gets called when an `TextTrackMenuItem` is "clicked". See
+     * {@link ClickableComponent} for more detailed information on what a click can be.
+     *
+     * @param {EventTarget~Event} event
+     *        The `keydown`, `tap`, or `click` event that caused this function to be
+     *        called.
+     *
+     * @listens tap
+     * @listens click
+     */
+
+
+    TextTrackMenuItem.prototype.handleClick = function handleClick(event) {
+      var kind = this.track.kind;
+      var kinds = this.track.kinds;
+      var tracks = this.player_.textTracks();
+
+      if (!kinds) {
+        kinds = [kind];
+      }
+
+      _MenuItem.prototype.handleClick.call(this, event);
+
+      if (!tracks) {
+        return;
+      }
+
+      for (var i = 0; i < tracks.length; i++) {
+        var track = tracks[i];
+
+        if (track === this.track && kinds.indexOf(track.kind) > -1) {
+          if (track.mode !== 'showing') {
+            track.mode = 'showing';
+          }
+        } else if (track.mode !== 'disabled') {
+          track.mode = 'disabled';
+        }
+      }
+    };
+
+    /**
+     * Handle text track list change
+     *
+     * @param {EventTarget~Event} event
+     *        The `change` event that caused this function to be called.
+     *
+     * @listens TextTrackList#change
+     */
+
+
+    TextTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) {
+      var shouldBeSelected = this.track.mode === 'showing';
+
+      // Prevent redundant selected() calls because they may cause
+      // screen readers to read the appended control text unnecessarily
+      if (shouldBeSelected !== this.isSelected_) {
+        this.selected(shouldBeSelected);
+      }
+    };
+
+    TextTrackMenuItem.prototype.handleSelectedLanguageChange = function handleSelectedLanguageChange(event) {
+      if (this.track.mode === 'showing') {
+        var selectedLanguage = this.player_.cache_.selectedLanguage;
+
+        // Don't replace the kind of track across the same language
+        if (selectedLanguage && selectedLanguage.enabled && selectedLanguage.language === this.track.language && selectedLanguage.kind !== this.track.kind) {
+          return;
+        }
+
+        this.player_.cache_.selectedLanguage = {
+          enabled: true,
+          language: this.track.language,
+          kind: this.track.kind
+        };
+      }
+    };
+
+    TextTrackMenuItem.prototype.dispose = function dispose() {
+      // remove reference to track object on dispose
+      this.track = null;
+
+      _MenuItem.prototype.dispose.call(this);
+    };
+
+    return TextTrackMenuItem;
+  }(MenuItem);
+
+  Component.registerComponent('TextTrackMenuItem', TextTrackMenuItem);
+
+  /**
+   * @file off-text-track-menu-item.js
+   */
+
+  /**
+   * A special menu item for turning of a specific type of text track
+   *
+   * @extends TextTrackMenuItem
+   */
+
+  var OffTextTrackMenuItem = function (_TextTrackMenuItem) {
+    inherits(OffTextTrackMenuItem, _TextTrackMenuItem);
+
+    /**
+     * 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.
+     */
+    function OffTextTrackMenuItem(player, options) {
+      classCallCheck(this, OffTextTrackMenuItem);
+
+      // Create pseudo track info
+      // Requires options['kind']
+      options.track = {
+        player: player,
+        kind: options.kind,
+        kinds: options.kinds,
+        default: false,
+        mode: 'disabled'
+      };
+
+      if (!options.kinds) {
+        options.kinds = [options.kind];
+      }
+
+      if (options.label) {
+        options.track.label = options.label;
+      } else {
+        options.track.label = options.kinds.join(' and ') + ' off';
+      }
+
+      // MenuItem is selectable
+      options.selectable = true;
+
+      return possibleConstructorReturn(this, _TextTrackMenuItem.call(this, player, options));
+    }
+
+    /**
+     * Handle text track change
+     *
+     * @param {EventTarget~Event} event
+     *        The event that caused this function to run
+     */
+
+
+    OffTextTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) {
+      var tracks = this.player().textTracks();
+      var shouldBeSelected = true;
+
+      for (var i = 0, l = tracks.length; i < l; i++) {
+        var track = tracks[i];
+
+        if (this.options_.kinds.indexOf(track.kind) > -1 && track.mode === 'showing') {
+          shouldBeSelected = false;
+          break;
+        }
+      }
+
+      // Prevent redundant selected() calls because they may cause
+      // screen readers to read the appended control text unnecessarily
+      if (shouldBeSelected !== this.isSelected_) {
+        this.selected(shouldBeSelected);
+      }
+    };
+
+    OffTextTrackMenuItem.prototype.handleSelectedLanguageChange = function handleSelectedLanguageChange(event) {
+      var tracks = this.player().textTracks();
+      var allHidden = true;
+
+      for (var i = 0, l = tracks.length; i < l; i++) {
+        var track = tracks[i];
+
+        if (['captions', 'descriptions', 'subtitles'].indexOf(track.kind) > -1 && track.mode === 'showing') {
+          allHidden = false;
+          break;
+        }
+      }
+
+      if (allHidden) {
+        this.player_.cache_.selectedLanguage = {
+          enabled: false
+        };
+      }
+    };
+
+    return OffTextTrackMenuItem;
+  }(TextTrackMenuItem);
+
+  Component.registerComponent('OffTextTrackMenuItem', OffTextTrackMenuItem);
+
+  /**
+   * @file text-track-button.js
+   */
+
+  /**
+   * The base class for buttons that toggle specific text track types (e.g. subtitles)
+   *
+   * @extends MenuButton
+   */
+
+  var TextTrackButton = function (_TrackButton) {
+    inherits(TextTrackButton, _TrackButton);
+
+    /**
+     * 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.
+     */
+    function TextTrackButton(player) {
+      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+      classCallCheck(this, TextTrackButton);
+
+      options.tracks = player.textTracks();
+
+      return possibleConstructorReturn(this, _TrackButton.call(this, player, options));
+    }
+
+    /**
+     * Create a menu item for each text track
+     *
+     * @param {TextTrackMenuItem[]} [items=[]]
+     *        Existing array of items to use during creation
+     *
+     * @return {TextTrackMenuItem[]}
+     *         Array of menu items that were created
+     */
+
+
+    TextTrackButton.prototype.createItems = function createItems() {
+      var items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
+      var TrackMenuItem = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : TextTrackMenuItem;
+
+
+      // Label is an override for the [track] off label
+      // USed to localise captions/subtitles
+      var label = void 0;
+
+      if (this.label_) {
+        label = this.label_ + ' off';
+      }
+      // Add an OFF menu item to turn all tracks off
+      items.push(new OffTextTrackMenuItem(this.player_, {
+        kinds: this.kinds_,
+        kind: this.kind_,
+        label: label
+      }));
+
+      this.hideThreshold_ += 1;
+
+      var tracks = this.player_.textTracks();
+
+      if (!Array.isArray(this.kinds_)) {
+        this.kinds_ = [this.kind_];
+      }
+
+      for (var i = 0; i < tracks.length; i++) {
+        var track = tracks[i];
+
+        // only add tracks that are of an appropriate kind and have a label
+        if (this.kinds_.indexOf(track.kind) > -1) {
+
+          var item = new TrackMenuItem(this.player_, {
+            track: track,
+            // MenuItem is selectable
+            selectable: true
+          });
+
+          item.addClass('vjs-' + track.kind + '-menu-item');
+          items.push(item);
+        }
+      }
+
+      return items;
+    };
+
+    return TextTrackButton;
+  }(TrackButton);
+
+  Component.registerComponent('TextTrackButton', TextTrackButton);
+
+  /**
+   * @file chapters-track-menu-item.js
+   */
+
+  /**
+   * The chapter track menu item
+   *
+   * @extends MenuItem
+   */
+
+  var ChaptersTrackMenuItem = function (_MenuItem) {
+    inherits(ChaptersTrackMenuItem, _MenuItem);
+
+    /**
+     * 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.
+     */
+    function ChaptersTrackMenuItem(player, options) {
+      classCallCheck(this, ChaptersTrackMenuItem);
+
+      var track = options.track;
+      var cue = options.cue;
+      var currentTime = player.currentTime();
+
+      // Modify options for parent MenuItem class's init.
+      options.selectable = true;
+      options.label = cue.text;
+      options.selected = cue.startTime <= currentTime && currentTime < cue.endTime;
+
+      var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options));
+
+      _this.track = track;
+      _this.cue = cue;
+      track.addEventListener('cuechange', bind(_this, _this.update));
+      return _this;
+    }
+
+    /**
+     * This gets called when an `ChaptersTrackMenuItem` is "clicked". See
+     * {@link ClickableComponent} for more detailed information on what a click can be.
+     *
+     * @param {EventTarget~Event} [event]
+     *        The `keydown`, `tap`, or `click` event that caused this function to be
+     *        called.
+     *
+     * @listens tap
+     * @listens click
+     */
+
+
+    ChaptersTrackMenuItem.prototype.handleClick = function handleClick(event) {
+      _MenuItem.prototype.handleClick.call(this);
+      this.player_.currentTime(this.cue.startTime);
+      this.update(this.cue.startTime);
+    };
+
+    /**
+     * Update chapter menu item
+     *
+     * @param {EventTarget~Event} [event]
+     *        The `cuechange` event that caused this function to run.
+     *
+     * @listens TextTrack#cuechange
+     */
+
+
+    ChaptersTrackMenuItem.prototype.update = function update(event) {
+      var cue = this.cue;
+      var currentTime = this.player_.currentTime();
+
+      // vjs.log(currentTime, cue.startTime);
+      this.selected(cue.startTime <= currentTime && currentTime < cue.endTime);
+    };
+
+    return ChaptersTrackMenuItem;
+  }(MenuItem);
+
+  Component.registerComponent('ChaptersTrackMenuItem', ChaptersTrackMenuItem);
+
+  /**
+   * @file chapters-button.js
+   */
+
+  /**
+   * The button component for toggling and selecting chapters
+   * Chapters act much differently than other text tracks
+   * Cues are navigation vs. other tracks of alternative languages
+   *
+   * @extends TextTrackButton
+   */
+
+  var ChaptersButton = function (_TextTrackButton) {
+    inherits(ChaptersButton, _TextTrackButton);
+
+    /**
+     * 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 {Component~ReadyCallback} [ready]
+     *        The function to call when this function is ready.
+     */
+    function ChaptersButton(player, options, ready) {
+      classCallCheck(this, ChaptersButton);
+      return possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
+    }
+
+    /**
+     * Builds the default DOM `className`.
+     *
+     * @return {string}
+     *         The DOM `className` for this object.
+     */
+
+
+    ChaptersButton.prototype.buildCSSClass = function buildCSSClass() {
+      return 'vjs-chapters-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
+    };
+
+    ChaptersButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
+      return 'vjs-chapters-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
+    };
+
+    /**
+     * Update the menu based on the current state of its items.
+     *
+     * @param {EventTarget~Event} [event]
+     *        An event that triggered this function to run.
+     *
+     * @listens TextTrackList#addtrack
+     * @listens TextTrackList#removetrack
+     * @listens TextTrackList#change
+     */
+
+
+    ChaptersButton.prototype.update = function update(event) {
+      if (!this.track_ || event && (event.type === 'addtrack' || event.type === 'removetrack')) {
+        this.setTrack(this.findChaptersTrack());
+      }
+      _TextTrackButton.prototype.update.call(this);
+    };
+
+    /**
+     * Set the currently selected track for the chapters button.
+     *
+     * @param {TextTrack} track
+     *        The new track to select. Nothing will change if this is the currently selected
+     *        track.
+     */
+
+
+    ChaptersButton.prototype.setTrack = function setTrack(track) {
+      if (this.track_ === track) {
+        return;
+      }
+
+      if (!this.updateHandler_) {
+        this.updateHandler_ = this.update.bind(this);
+      }
+
+      // here this.track_ refers to the old track instance
+      if (this.track_) {
+        var remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_);
+
+        if (remoteTextTrackEl) {
+          remoteTextTrackEl.removeEventListener('load', this.updateHandler_);
+        }
+
+        this.track_ = null;
+      }
+
+      this.track_ = track;
+
+      // here this.track_ refers to the new track instance
+      if (this.track_) {
+        this.track_.mode = 'hidden';
+
+        var _remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_);
+
+        if (_remoteTextTrackEl) {
+          _remoteTextTrackEl.addEventListener('load', this.updateHandler_);
+        }
+      }
+    };
+
+    /**
+     * Find the track object that is currently in use by this ChaptersButton
+     *
+     * @return {TextTrack|undefined}
+     *         The current track or undefined if none was found.
+     */
+
+
+    ChaptersButton.prototype.findChaptersTrack = function findChaptersTrack() {
+      var tracks = this.player_.textTracks() || [];
+
+      for (var i = tracks.length - 1; i >= 0; i--) {
+        // We will always choose the last track as our chaptersTrack
+        var track = tracks[i];
+
+        if (track.kind === this.kind_) {
+          return track;
+        }
+      }
+    };
+
+    /**
+     * Get the caption for the ChaptersButton based on the track label. This will also
+     * use the current tracks localized kind as a fallback if a label does not exist.
+     *
+     * @return {string}
+     *         The tracks current label or the localized track kind.
+     */
+
+
+    ChaptersButton.prototype.getMenuCaption = function getMenuCaption() {
+      if (this.track_ && this.track_.label) {
+        return this.track_.label;
+      }
+      return this.localize(toTitleCase(this.kind_));
+    };
+
+    /**
+     * Create menu from chapter track
+     *
+     * @return {Menu}
+     *         New menu for the chapter buttons
+     */
+
+
+    ChaptersButton.prototype.createMenu = function createMenu() {
+      this.options_.title = this.getMenuCaption();
+      return _TextTrackButton.prototype.createMenu.call(this);
+    };
+
+    /**
+     * Create a menu item for each text track
+     *
+     * @return {TextTrackMenuItem[]}
+     *         Array of menu items
+     */
+
+
+    ChaptersButton.prototype.createItems = function createItems() {
+      var items = [];
+
+      if (!this.track_) {
+        return items;
+      }
+
+      var cues = this.track_.cues;
+
+      if (!cues) {
+        return items;
+      }
+
+      for (var i = 0, l = cues.length; i < l; i++) {
+        var cue = cues[i];
+        var mi = new ChaptersTrackMenuItem(this.player_, {track: this.track_, cue: cue});
+
+        items.push(mi);
+      }
+
+      return items;
+    };
+
+    return ChaptersButton;
+  }(TextTrackButton);
+
+  /**
+   * `kind` of TextTrack to look for to associate it with this menu.
+   *
+   * @type {string}
+   * @private
+   */
+
+
+  ChaptersButton.prototype.kind_ = 'chapters';
+
+  /**
+   * The text that should display over the `ChaptersButton`s controls. Added for localization.
+   *
+   * @type {string}
+   * @private
+   */
+  ChaptersButton.prototype.controlText_ = 'Chapters';
+
+  Component.registerComponent('ChaptersButton', ChaptersButton);
+
+  /**
+   * @file descriptions-button.js
+   */
+
+  /**
+   * The button component for toggling and selecting descriptions
+   *
+   * @extends TextTrackButton
+   */
+
+  var DescriptionsButton = function (_TextTrackButton) {
+    inherits(DescriptionsButton, _TextTrackButton);
+
+    /**
+     * 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 {Component~ReadyCallback} [ready]
+     *        The function to call when this component is ready.
+     */
+    function DescriptionsButton(player, options, ready) {
+      classCallCheck(this, DescriptionsButton);
+
+      var _this = possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
+
+      var tracks = player.textTracks();
+      var changeHandler = bind(_this, _this.handleTracksChange);
+
+      tracks.addEventListener('change', changeHandler);
+      _this.on('dispose', function () {
+        tracks.removeEventListener('change', changeHandler);
+      });
+      return _this;
+    }
+
+    /**
+     * Handle text track change
+     *
+     * @param {EventTarget~Event} event
+     *        The event that caused this function to run
+     *
+     * @listens TextTrackList#change
+     */
+
+
+    DescriptionsButton.prototype.handleTracksChange = function handleTracksChange(event) {
+      var tracks = this.player().textTracks();
+      var disabled = false;
+
+      // Check whether a track of a different kind is showing
+      for (var i = 0, l = tracks.length; i < l; i++) {
+        var track = tracks[i];
+
+        if (track.kind !== this.kind_ && track.mode === 'showing') {
+          disabled = true;
+          break;
+        }
+      }
+
+      // If another track is showing, disable this menu button
+      if (disabled) {
+        this.disable();
+      } else {
+        this.enable();
+      }
+    };
+
+    /**
+     * Builds the default DOM `className`.
+     *
+     * @return {string}
+     *         The DOM `className` for this object.
+     */
+
+
+    DescriptionsButton.prototype.buildCSSClass = function buildCSSClass() {
+      return 'vjs-descriptions-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
+    };
+
+    DescriptionsButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
+      return 'vjs-descriptions-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
+    };
+
+    return DescriptionsButton;
+  }(TextTrackButton);
+
+  /**
+   * `kind` of TextTrack to look for to associate it with this menu.
+   *
+   * @type {string}
+   * @private
+   */
+
+
+  DescriptionsButton.prototype.kind_ = 'descriptions';
+
+  /**
+   * The text that should display over the `DescriptionsButton`s controls. Added for localization.
+   *
+   * @type {string}
+   * @private
+   */
+  DescriptionsButton.prototype.controlText_ = 'Descriptions';
+
+  Component.registerComponent('DescriptionsButton', DescriptionsButton);
+
+  /**
+   * @file subtitles-button.js
+   */
+
+  /**
+   * The button component for toggling and selecting subtitles
+   *
+   * @extends TextTrackButton
+   */
+
+  var SubtitlesButton = function (_TextTrackButton) {
+    inherits(SubtitlesButton, _TextTrackButton);
+
+    /**
+     * 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 {Component~ReadyCallback} [ready]
+     *        The function to call when this component is ready.
+     */
+    function SubtitlesButton(player, options, ready) {
+      classCallCheck(this, SubtitlesButton);
+      return possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
+    }
+
+    /**
+     * Builds the default DOM `className`.
+     *
+     * @return {string}
+     *         The DOM `className` for this object.
+     */
+
+
+    SubtitlesButton.prototype.buildCSSClass = function buildCSSClass() {
+      return 'vjs-subtitles-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
+    };
+
+    SubtitlesButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
+      return 'vjs-subtitles-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
+    };
+
+    return SubtitlesButton;
+  }(TextTrackButton);
+
+  /**
+   * `kind` of TextTrack to look for to associate it with this menu.
+   *
+   * @type {string}
+   * @private
+   */
+
+
+  SubtitlesButton.prototype.kind_ = 'subtitles';
+
+  /**
+   * The text that should display over the `SubtitlesButton`s controls. Added for localization.
+   *
+   * @type {string}
+   * @private
+   */
+  SubtitlesButton.prototype.controlText_ = 'Subtitles';
+
+  Component.registerComponent('SubtitlesButton', SubtitlesButton);
+
+  /**
+   * @file caption-settings-menu-item.js
+   */
+
+  /**
+   * The menu item for caption track settings menu
+   *
+   * @extends TextTrackMenuItem
+   */
+
+  var CaptionSettingsMenuItem = function (_TextTrackMenuItem) {
+    inherits(CaptionSettingsMenuItem, _TextTrackMenuItem);
+
+    /**
+     * 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.
+     */
+    function CaptionSettingsMenuItem(player, options) {
+      classCallCheck(this, CaptionSettingsMenuItem);
+
+      options.track = {
+        player: player,
+        kind: options.kind,
+        label: options.kind + ' settings',
+        selectable: false,
+        default: false,
+        mode: 'disabled'
+      };
+
+      // CaptionSettingsMenuItem has no concept of 'selected'
+      options.selectable = false;
+
+      options.name = 'CaptionSettingsMenuItem';
+
+      var _this = possibleConstructorReturn(this, _TextTrackMenuItem.call(this, player, options));
+
+      _this.addClass('vjs-texttrack-settings');
+      _this.controlText(', opens ' + options.kind + ' settings dialog');
+      return _this;
+    }
+
+    /**
+     * This gets called when an `CaptionSettingsMenuItem` is "clicked". See
+     * {@link ClickableComponent} for more detailed information on what a click can be.
+     *
+     * @param {EventTarget~Event} [event]
+     *        The `keydown`, `tap`, or `click` event that caused this function to be
+     *        called.
+     *
+     * @listens tap
+     * @listens click
+     */
+
+
+    CaptionSettingsMenuItem.prototype.handleClick = function handleClick(event) {
+      this.player().getChild('textTrackSettings').open();
+    };
+
+    return CaptionSettingsMenuItem;
+  }(TextTrackMenuItem);
+
+  Component.registerComponent('CaptionSettingsMenuItem', CaptionSettingsMenuItem);
+
+  /**
+   * @file captions-button.js
+   */
+
+  /**
+   * The button component for toggling and selecting captions
+   *
+   * @extends TextTrackButton
+   */
+
+  var CaptionsButton = function (_TextTrackButton) {
+    inherits(CaptionsButton, _TextTrackButton);
+
+    /**
+     * 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 {Component~ReadyCallback} [ready]
+     *        The function to call when this component is ready.
+     */
+    function CaptionsButton(player, options, ready) {
+      classCallCheck(this, CaptionsButton);
+      return possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
+    }
+
+    /**
+     * Builds the default DOM `className`.
+     *
+     * @return {string}
+     *         The DOM `className` for this object.
+     */
+
+
+    CaptionsButton.prototype.buildCSSClass = function buildCSSClass() {
+      return 'vjs-captions-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
+    };
+
+    CaptionsButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
+      return 'vjs-captions-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
+    };
+
+    /**
+     * Create caption menu items
+     *
+     * @return {CaptionSettingsMenuItem[]}
+     *         The array of current menu items.
+     */
+
+
+    CaptionsButton.prototype.createItems = function createItems() {
+      var items = [];
+
+      if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks) && this.player().getChild('textTrackSettings')) {
+        items.push(new CaptionSettingsMenuItem(this.player_, {kind: this.kind_}));
+
+        this.hideThreshold_ += 1;
+      }
+
+      return _TextTrackButton.prototype.createItems.call(this, items);
+    };
+
+    return CaptionsButton;
+  }(TextTrackButton);
+
+  /**
+   * `kind` of TextTrack to look for to associate it with this menu.
+   *
+   * @type {string}
+   * @private
+   */
+
+
+  CaptionsButton.prototype.kind_ = 'captions';
+
+  /**
+   * The text that should display over the `CaptionsButton`s controls. Added for localization.
+   *
+   * @type {string}
+   * @private
+   */
+  CaptionsButton.prototype.controlText_ = 'Captions';
+
+  Component.registerComponent('CaptionsButton', CaptionsButton);
+
+  /**
+   * @file subs-caps-menu-item.js
+   */
+
+  /**
+   * SubsCapsMenuItem has an [cc] icon to distinguish captions from subtitles
+   * in the SubsCapsMenu.
+   *
+   * @extends TextTrackMenuItem
+   */
+
+  var SubsCapsMenuItem = function (_TextTrackMenuItem) {
+    inherits(SubsCapsMenuItem, _TextTrackMenuItem);
+
+    function SubsCapsMenuItem() {
+      classCallCheck(this, SubsCapsMenuItem);
+      return possibleConstructorReturn(this, _TextTrackMenuItem.apply(this, arguments));
+    }
+
+    SubsCapsMenuItem.prototype.createEl = function createEl(type, props, attrs) {
+      var innerHTML = '<span class="vjs-menu-item-text">' + this.localize(this.options_.label);
+
+      if (this.options_.track.kind === 'captions') {
+        innerHTML += '\n        <span aria-hidden="true" class="vjs-icon-placeholder"></span>\n        <span class="vjs-control-text"> ' + this.localize('Captions') + '</span>\n      ';
+      }
+
+      innerHTML += '</span>';
+
+      var el = _TextTrackMenuItem.prototype.createEl.call(this, type, assign({
+        innerHTML: innerHTML
+      }, props), attrs);
+
+      return el;
+    };
+
+    return SubsCapsMenuItem;
+  }(TextTrackMenuItem);
+
+  Component.registerComponent('SubsCapsMenuItem', SubsCapsMenuItem);
+
+  /**
+   * @file sub-caps-button.js
+   */
+  /**
+   * The button component for toggling and selecting captions and/or subtitles
+   *
+   * @extends TextTrackButton
+   */
+
+  var SubsCapsButton = function (_TextTrackButton) {
+    inherits(SubsCapsButton, _TextTrackButton);
+
+    function SubsCapsButton(player) {
+      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+      classCallCheck(this, SubsCapsButton);
+
+      // Although North America uses "captions" in most cases for
+      // "captions and subtitles" other locales use "subtitles"
+      var _this = possibleConstructorReturn(this, _TextTrackButton.call(this, player, options));
+
+      _this.label_ = 'subtitles';
+      if (['en', 'en-us', 'en-ca', 'fr-ca'].indexOf(_this.player_.language_) > -1) {
+        _this.label_ = 'captions';
+      }
+      _this.menuButton_.controlText(toTitleCase(_this.label_));
+      return _this;
+    }
+
+    /**
+     * Builds the default DOM `className`.
+     *
+     * @return {string}
+     *         The DOM `className` for this object.
+     */
+
+
+    SubsCapsButton.prototype.buildCSSClass = function buildCSSClass() {
+      return 'vjs-subs-caps-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
+    };
+
+    SubsCapsButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
+      return 'vjs-subs-caps-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
+    };
+
+    /**
+     * Create caption/subtitles menu items
+     *
+     * @return {CaptionSettingsMenuItem[]}
+     *         The array of current menu items.
+     */
+
+
+    SubsCapsButton.prototype.createItems = function createItems() {
+      var items = [];
+
+      if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks) && this.player().getChild('textTrackSettings')) {
+        items.push(new CaptionSettingsMenuItem(this.player_, {kind: this.label_}));
+
+        this.hideThreshold_ += 1;
+      }
+
+      items = _TextTrackButton.prototype.createItems.call(this, items, SubsCapsMenuItem);
+      return items;
+    };
+
+    return SubsCapsButton;
+  }(TextTrackButton);
+
+  /**
+   * `kind`s of TextTrack to look for to associate it with this menu.
+   *
+   * @type {array}
+   * @private
+   */
+
+
+  SubsCapsButton.prototype.kinds_ = ['captions', 'subtitles'];
+
+  /**
+   * The text that should display over the `SubsCapsButton`s controls.
+   *
+   *
+   * @type {string}
+   * @private
+   */
+  SubsCapsButton.prototype.controlText_ = 'Subtitles';
+
+  Component.registerComponent('SubsCapsButton', SubsCapsButton);
+
+  /**
+   * @file audio-track-menu-item.js
+   */
+
+  /**
+   * An {@link AudioTrack} {@link MenuItem}
+   *
+   * @extends MenuItem
+   */
+
+  var AudioTrackMenuItem = function (_MenuItem) {
+    inherits(AudioTrackMenuItem, _MenuItem);
+
+    /**
+     * 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.
+     */
+    function AudioTrackMenuItem(player, options) {
+      classCallCheck(this, AudioTrackMenuItem);
+
+      var track = options.track;
+      var tracks = player.audioTracks();
+
+      // Modify options for parent MenuItem class's init.
+      options.label = track.label || track.language || 'Unknown';
+      options.selected = track.enabled;
+
+      var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options));
+
+      _this.track = track;
+
+      var changeHandler = function changeHandler() {
+        for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+          args[_key] = arguments[_key];
+        }
+
+        _this.handleTracksChange.apply(_this, args);
+      };
+
+      tracks.addEventListener('change', changeHandler);
+      _this.on('dispose', function () {
+        tracks.removeEventListener('change', changeHandler);
+      });
+      return _this;
+    }
+
+    /**
+     * This gets called when an `AudioTrackMenuItem is "clicked". See {@link ClickableComponent}
+     * for more detailed information on what a click can be.
+     *
+     * @param {EventTarget~Event} [event]
+     *        The `keydown`, `tap`, or `click` event that caused this function to be
+     *        called.
+     *
+     * @listens tap
+     * @listens click
+     */
+
+
+    AudioTrackMenuItem.prototype.handleClick = function handleClick(event) {
+      var tracks = this.player_.audioTracks();
+
+      _MenuItem.prototype.handleClick.call(this, event);
+
+      for (var i = 0; i < tracks.length; i++) {
+        var track = tracks[i];
+
+        track.enabled = track === this.track;
+      }
+    };
+
+    /**
+     * Handle any {@link AudioTrack} change.
+     *
+     * @param {EventTarget~Event} [event]
+     *        The {@link AudioTrackList#change} event that caused this to run.
+     *
+     * @listens AudioTrackList#change
+     */
+
+
+    AudioTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) {
+      this.selected(this.track.enabled);
+    };
+
+    return AudioTrackMenuItem;
+  }(MenuItem);
+
+  Component.registerComponent('AudioTrackMenuItem', AudioTrackMenuItem);
+
+  /**
+   * @file audio-track-button.js
+   */
+
+  /**
+   * The base class for buttons that toggle specific {@link AudioTrack} types.
+   *
+   * @extends TrackButton
+   */
+
+  var AudioTrackButton = function (_TrackButton) {
+    inherits(AudioTrackButton, _TrackButton);
+
+    /**
+     * 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.
+     */
+    function AudioTrackButton(player) {
+      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+      classCallCheck(this, AudioTrackButton);
+
+      options.tracks = player.audioTracks();
+
+      return possibleConstructorReturn(this, _TrackButton.call(this, player, options));
+    }
+
+    /**
+     * Builds the default DOM `className`.
+     *
+     * @return {string}
+     *         The DOM `className` for this object.
+     */
+
+
+    AudioTrackButton.prototype.buildCSSClass = function buildCSSClass() {
+      return 'vjs-audio-button ' + _TrackButton.prototype.buildCSSClass.call(this);
+    };
+
+    AudioTrackButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
+      return 'vjs-audio-button ' + _TrackButton.prototype.buildWrapperCSSClass.call(this);
+    };
+
+    /**
+     * Create a menu item for each audio track
+     *
+     * @param {AudioTrackMenuItem[]} [items=[]]
+     *        An array of existing menu items to use.
+     *
+     * @return {AudioTrackMenuItem[]}
+     *         An array of menu items
+     */
+
+
+    AudioTrackButton.prototype.createItems = function createItems() {
+      var items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
+
+      // if there's only one audio track, there no point in showing it
+      this.hideThreshold_ = 1;
+
+      var tracks = this.player_.audioTracks();
+
+      for (var i = 0; i < tracks.length; i++) {
+        var track = tracks[i];
+
+        items.push(new AudioTrackMenuItem(this.player_, {
+          track: track,
+          // MenuItem is selectable
+          selectable: true
+        }));
+      }
+
+      return items;
+    };
+
+    return AudioTrackButton;
+  }(TrackButton);
+
+  /**
+   * The text that should display over the `AudioTrackButton`s controls. Added for localization.
+   *
+   * @type {string}
+   * @private
+   */
+
+
+  AudioTrackButton.prototype.controlText_ = 'Audio Track';
+  Component.registerComponent('AudioTrackButton', AudioTrackButton);
+
+  /**
+   * @file playback-rate-menu-item.js
+   */
+
+  /**
+   * The specific menu item type for selecting a playback rate.
+   *
+   * @extends MenuItem
+   */
+
+  var PlaybackRateMenuItem = function (_MenuItem) {
+    inherits(PlaybackRateMenuItem, _MenuItem);
+
+    /**
+     * 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.
+     */
+    function PlaybackRateMenuItem(player, options) {
+      classCallCheck(this, PlaybackRateMenuItem);
+
+      var label = options.rate;
+      var rate = parseFloat(label, 10);
+
+      // Modify options for parent MenuItem class's init.
+      options.label = label;
+      options.selected = rate === 1;
+      options.selectable = true;
+
+      var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options));
+
+      _this.label = label;
+      _this.rate = rate;
+
+      _this.on(player, 'ratechange', _this.update);
+      return _this;
+    }
+
+    /**
+     * This gets called when an `PlaybackRateMenuItem` is "clicked". See
+     * {@link ClickableComponent} for more detailed information on what a click can be.
+     *
+     * @param {EventTarget~Event} [event]
+     *        The `keydown`, `tap`, or `click` event that caused this function to be
+     *        called.
+     *
+     * @listens tap
+     * @listens click
+     */
+
+
+    PlaybackRateMenuItem.prototype.handleClick = function handleClick(event) {
+      _MenuItem.prototype.handleClick.call(this);
+      this.player().playbackRate(this.rate);
+    };
+
+    /**
+     * Update the PlaybackRateMenuItem when the playbackrate changes.
+     *
+     * @param {EventTarget~Event} [event]
+     *        The `ratechange` event that caused this function to run.
+     *
+     * @listens Player#ratechange
+     */
+
+
+    PlaybackRateMenuItem.prototype.update = function update(event) {
+      this.selected(this.player().playbackRate() === this.rate);
+    };
+
+    return PlaybackRateMenuItem;
+  }(MenuItem);
+
+  /**
+   * The text that should display over the `PlaybackRateMenuItem`s controls. Added for localization.
+   *
+   * @type {string}
+   * @private
+   */
+
+
+  PlaybackRateMenuItem.prototype.contentElType = 'button';
+
+  Component.registerComponent('PlaybackRateMenuItem', PlaybackRateMenuItem);
+
+  /**
+   * @file playback-rate-menu-button.js
+   */
+
+  /**
+   * The component for controlling the playback rate.
+   *
+   * @extends MenuButton
+   */
+
+  var PlaybackRateMenuButton = function (_MenuButton) {
+    inherits(PlaybackRateMenuButton, _MenuButton);
+
+    /**
+     * 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.
+     */
+    function PlaybackRateMenuButton(player, options) {
+      classCallCheck(this, PlaybackRateMenuButton);
+
+      var _this = possibleConstructorReturn(this, _MenuButton.call(this, player, options));
+
+      _this.updateVisibility();
+      _this.updateLabel();
+
+      _this.on(player, 'loadstart', _this.updateVisibility);
+      _this.on(player, 'ratechange', _this.updateLabel);
+      return _this;
+    }
+
+    /**
+     * Create the `Component`'s DOM element
+     *
+     * @return {Element}
+     *         The element that was created.
+     */
+
+
+    PlaybackRateMenuButton.prototype.createEl = function createEl$$1() {
+      var el = _MenuButton.prototype.createEl.call(this);
+
+      this.labelEl_ = createEl('div', {
+        className: 'vjs-playback-rate-value',
+        innerHTML: '1x'
+      });
+
+      el.appendChild(this.labelEl_);
+
+      return el;
+    };
+
+    PlaybackRateMenuButton.prototype.dispose = function dispose() {
+      this.labelEl_ = null;
+
+      _MenuButton.prototype.dispose.call(this);
+    };
+
+    /**
+     * Builds the default DOM `className`.
+     *
+     * @return {string}
+     *         The DOM `className` for this object.
+     */
+
+
+    PlaybackRateMenuButton.prototype.buildCSSClass = function buildCSSClass() {
+      return 'vjs-playback-rate ' + _MenuButton.prototype.buildCSSClass.call(this);
+    };
+
+    PlaybackRateMenuButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
+      return 'vjs-playback-rate ' + _MenuButton.prototype.buildWrapperCSSClass.call(this);
+    };
+
+    /**
+     * Create the playback rate menu
+     *
+     * @return {Menu}
+     *         Menu object populated with {@link PlaybackRateMenuItem}s
+     */
+
+
+    PlaybackRateMenuButton.prototype.createMenu = function createMenu() {
+      var menu = new Menu(this.player());
+      var rates = this.playbackRates();
+
+      if (rates) {
+        for (var i = rates.length - 1; i >= 0; i--) {
+          menu.addChild(new PlaybackRateMenuItem(this.player(), {rate: rates[i] + 'x'}));
+        }
+      }
+
+      return menu;
+    };
+
+    /**
+     * Updates ARIA accessibility attributes
+     */
+
+
+    PlaybackRateMenuButton.prototype.updateARIAAttributes = function updateARIAAttributes() {
+      // Current playback rate
+      this.el().setAttribute('aria-valuenow', this.player().playbackRate());
+    };
+
+    /**
+     * This gets called when an `PlaybackRateMenuButton` is "clicked". See
+     * {@link ClickableComponent} for more detailed information on what a click can be.
+     *
+     * @param {EventTarget~Event} [event]
+     *        The `keydown`, `tap`, or `click` event that caused this function to be
+     *        called.
+     *
+     * @listens tap
+     * @listens click
+     */
+
+
+    PlaybackRateMenuButton.prototype.handleClick = function handleClick(event) {
+      // select next rate option
+      var currentRate = this.player().playbackRate();
+      var rates = this.playbackRates();
+
+      // this will select first one if the last one currently selected
+      var newRate = rates[0];
+
+      for (var i = 0; i < rates.length; i++) {
+        if (rates[i] > currentRate) {
+          newRate = rates[i];
+          break;
+        }
+      }
+      this.player().playbackRate(newRate);
+    };
+
+    /**
+     * Get possible playback rates
+     *
+     * @return {Array}
+     *         All possible playback rates
+     */
+
+
+    PlaybackRateMenuButton.prototype.playbackRates = function playbackRates() {
+      return this.options_.playbackRates || this.options_.playerOptions && this.options_.playerOptions.playbackRates;
+    };
+
+    /**
+     * Get whether playback rates is supported by the tech
+     * and an array of playback rates exists
+     *
+     * @return {boolean}
+     *         Whether changing playback rate is supported
+     */
+
+
+    PlaybackRateMenuButton.prototype.playbackRateSupported = function playbackRateSupported() {
+      return this.player().tech_ && this.player().tech_.featuresPlaybackRate && this.playbackRates() && this.playbackRates().length > 0;
+    };
+
+    /**
+     * Hide playback rate controls when they're no playback rate options to select
+     *
+     * @param {EventTarget~Event} [event]
+     *        The event that caused this function to run.
+     *
+     * @listens Player#loadstart
+     */
+
+
+    PlaybackRateMenuButton.prototype.updateVisibility = function updateVisibility(event) {
+      if (this.playbackRateSupported()) {
+        this.removeClass('vjs-hidden');
+      } else {
+        this.addClass('vjs-hidden');
+      }
+    };
+
+    /**
+     * Update button label when rate changed
+     *
+     * @param {EventTarget~Event} [event]
+     *        The event that caused this function to run.
+     *
+     * @listens Player#ratechange
+     */
+
+
+    PlaybackRateMenuButton.prototype.updateLabel = function updateLabel(event) {
+      if (this.playbackRateSupported()) {
+        this.labelEl_.innerHTML = this.player().playbackRate() + 'x';
+      }
+    };
+
+    return PlaybackRateMenuButton;
+  }(MenuButton);
+
+  /**
+   * The text that should display over the `FullscreenToggle`s controls. Added for localization.
+   *
+   * @type {string}
+   * @private
+   */
+
+
+  PlaybackRateMenuButton.prototype.controlText_ = 'Playback Rate';
+
+  Component.registerComponent('PlaybackRateMenuButton', PlaybackRateMenuButton);
+
+  /**
+   * @file spacer.js
+   */
+
+  /**
+   * Just an empty spacer element that can be used as an append point for plugins, etc.
+   * Also can be used to create space between elements when necessary.
+   *
+   * @extends Component
+   */
+
+  var Spacer = function (_Component) {
+    inherits(Spacer, _Component);
+
+    function Spacer() {
+      classCallCheck(this, Spacer);
+      return possibleConstructorReturn(this, _Component.apply(this, arguments));
+    }
+
+    /**
+     * Builds the default DOM `className`.
+     *
+     * @return {string}
+     *         The DOM `className` for this object.
+     */
+    Spacer.prototype.buildCSSClass = function buildCSSClass() {
+      return 'vjs-spacer ' + _Component.prototype.buildCSSClass.call(this);
+    };
+
+    /**
+     * Create the `Component`'s DOM element
+     *
+     * @return {Element}
+     *         The element that was created.
+     */
+
+
+    Spacer.prototype.createEl = function createEl() {
+      return _Component.prototype.createEl.call(this, 'div', {
+        className: this.buildCSSClass()
+      });
+    };
+
+    return Spacer;
+  }(Component);
+
+  Component.registerComponent('Spacer', Spacer);
+
+  /**
+   * @file custom-control-spacer.js
+   */
+
+  /**
+   * Spacer specifically meant to be used as an insertion point for new plugins, etc.
+   *
+   * @extends Spacer
+   */
+
+  var CustomControlSpacer = function (_Spacer) {
+    inherits(CustomControlSpacer, _Spacer);
+
+    function CustomControlSpacer() {
+      classCallCheck(this, CustomControlSpacer);
+      return possibleConstructorReturn(this, _Spacer.apply(this, arguments));
+    }
+
+    /**
+     * Builds the default DOM `className`.
+     *
+     * @return {string}
+     *         The DOM `className` for this object.
+     */
+    CustomControlSpacer.prototype.buildCSSClass = function buildCSSClass() {
+      return 'vjs-custom-control-spacer ' + _Spacer.prototype.buildCSSClass.call(this);
+    };
+
+    /**
+     * Create the `Component`'s DOM element
+     *
+     * @return {Element}
+     *         The element that was created.
+     */
+
+
+    CustomControlSpacer.prototype.createEl = function createEl() {
+      var el = _Spacer.prototype.createEl.call(this, {
+        className: this.buildCSSClass()
+      });
+
+      // No-flex/table-cell mode requires there be some content
+      // in the cell to fill the remaining space of the table.
+      el.innerHTML = '\xA0';
+      return el;
+    };
+
+    return CustomControlSpacer;
+  }(Spacer);
+
+  Component.registerComponent('CustomControlSpacer', CustomControlSpacer);
+
+  /**
+   * @file control-bar.js
+   */
+
+  /**
+   * Container of main controls.
+   *
+   * @extends Component
+   */
+
+  var ControlBar = function (_Component) {
+    inherits(ControlBar, _Component);
+
+    function ControlBar() {
+      classCallCheck(this, ControlBar);
+      return possibleConstructorReturn(this, _Component.apply(this, arguments));
+    }
+
+    /**
+     * Create the `Component`'s DOM element
+     *
+     * @return {Element}
+     *         The element that was created.
+     */
+    ControlBar.prototype.createEl = function createEl() {
+      return _Component.prototype.createEl.call(this, 'div', {
+        className: 'vjs-control-bar',
+        dir: 'ltr'
+      });
+    };
+
+    return ControlBar;
+  }(Component);
+
+  /**
+   * Default options for `ControlBar`
+   *
+   * @type {Object}
+   * @private
+   */
+
+
+  ControlBar.prototype.options_ = {
+    children: ['playToggle', 'volumePanel', 'currentTimeDisplay', 'timeDivider', 'durationDisplay', 'progressControl', 'liveDisplay', 'remainingTimeDisplay', 'customControlSpacer', 'playbackRateMenuButton', 'chaptersButton', 'descriptionsButton', 'subsCapsButton', 'audioTrackButton', 'fullscreenToggle']
+  };
+
+  Component.registerComponent('ControlBar', ControlBar);
+
+  /**
+   * @file error-display.js
+   */
+
+  /**
+   * A display that indicates an error has occurred. This means that the video
+   * is unplayable.
+   *
+   * @extends ModalDialog
+   */
+
+  var ErrorDisplay = function (_ModalDialog) {
+    inherits(ErrorDisplay, _ModalDialog);
+
+    /**
+     * 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.
+     */
+    function ErrorDisplay(player, options) {
+      classCallCheck(this, ErrorDisplay);
+
+      var _this = possibleConstructorReturn(this, _ModalDialog.call(this, player, options));
+
+      _this.on(player, 'error', _this.open);
+      return _this;
+    }
+
+    /**
+     * Builds the default DOM `className`.
+     *
+     * @return {string}
+     *         The DOM `className` for this object.
+     *
+     * @deprecated Since version 5.
+     */
+
+
+    ErrorDisplay.prototype.buildCSSClass = function buildCSSClass() {
+      return 'vjs-error-display ' + _ModalDialog.prototype.buildCSSClass.call(this);
+    };
+
+    /**
+     * Gets the localized error message based on the `Player`s error.
+     *
+     * @return {string}
+     *         The `Player`s error message localized or an empty string.
+     */
+
+
+    ErrorDisplay.prototype.content = function content() {
+      var error = this.player().error();
+
+      return error ? this.localize(error.message) : '';
+    };
+
+    return ErrorDisplay;
+  }(ModalDialog);
+
+  /**
+   * The default options for an `ErrorDisplay`.
+   *
+   * @private
+   */
+
+
+  ErrorDisplay.prototype.options_ = mergeOptions(ModalDialog.prototype.options_, {
+    pauseOnOpen: false,
+    fillAlways: true,
+    temporary: false,
+    uncloseable: true
+  });
+
+  Component.registerComponent('ErrorDisplay', ErrorDisplay);
+
+  /**
+   * @file text-track-settings.js
+   */
+
+  var LOCAL_STORAGE_KEY = 'vjs-text-track-settings';
+
+  var COLOR_BLACK = ['#000', 'Black'];
+  var COLOR_BLUE = ['#00F', 'Blue'];
+  var COLOR_CYAN = ['#0FF', 'Cyan'];
+  var COLOR_GREEN = ['#0F0', 'Green'];
+  var COLOR_MAGENTA = ['#F0F', 'Magenta'];
+  var COLOR_RED = ['#F00', 'Red'];
+  var COLOR_WHITE = ['#FFF', 'White'];
+  var COLOR_YELLOW = ['#FF0', 'Yellow'];
+
+  var OPACITY_OPAQUE = ['1', 'Opaque'];
+  var OPACITY_SEMI = ['0.5', 'Semi-Transparent'];
+  var OPACITY_TRANS = ['0', 'Transparent'];
+
+  // Configuration for the various <select> elements in the DOM of this component.
+  //
+  // Possible keys include:
+  //
+  // `default`:
+  //   The default option index. Only needs to be provided if not zero.
+  // `parser`:
+  //   A function which is used to parse the value from the selected option in
+  //   a customized way.
+  // `selector`:
+  //   The selector used to find the associated <select> element.
+  var selectConfigs = {
+    backgroundColor: {
+      selector: '.vjs-bg-color > select',
+      id: 'captions-background-color-%s',
+      label: 'Color',
+      options: [COLOR_BLACK, COLOR_WHITE, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN]
+    },
+
+    backgroundOpacity: {
+      selector: '.vjs-bg-opacity > select',
+      id: 'captions-background-opacity-%s',
+      label: 'Transparency',
+      options: [OPACITY_OPAQUE, OPACITY_SEMI, OPACITY_TRANS]
+    },
+
+    color: {
+      selector: '.vjs-fg-color > select',
+      id: 'captions-foreground-color-%s',
+      label: 'Color',
+      options: [COLOR_WHITE, COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN]
+    },
+
+    edgeStyle: {
+      selector: '.vjs-edge-style > select',
+      id: '%s',
+      label: 'Text Edge Style',
+      options: [['none', 'None'], ['raised', 'Raised'], ['depressed', 'Depressed'], ['uniform', 'Uniform'], ['dropshadow', 'Dropshadow']]
+    },
+
+    fontFamily: {
+      selector: '.vjs-font-family > select',
+      id: 'captions-font-family-%s',
+      label: 'Font Family',
+      options: [['proportionalSansSerif', 'Proportional Sans-Serif'], ['monospaceSansSerif', 'Monospace Sans-Serif'], ['proportionalSerif', 'Proportional Serif'], ['monospaceSerif', 'Monospace Serif'], ['casual', 'Casual'], ['script', 'Script'], ['small-caps', 'Small Caps']]
+    },
+
+    fontPercent: {
+      selector: '.vjs-font-percent > select',
+      id: 'captions-font-size-%s',
+      label: 'Font Size',
+      options: [['0.50', '50%'], ['0.75', '75%'], ['1.00', '100%'], ['1.25', '125%'], ['1.50', '150%'], ['1.75', '175%'], ['2.00', '200%'], ['3.00', '300%'], ['4.00', '400%']],
+      default: 2,
+      parser: function parser(v) {
+        return v === '1.00' ? null : Number(v);
+      }
+    },
+
+    textOpacity: {
+      selector: '.vjs-text-opacity > select',
+      id: 'captions-foreground-opacity-%s',
+      label: 'Transparency',
+      options: [OPACITY_OPAQUE, OPACITY_SEMI]
+    },
+
+    // Options for this object are defined below.
+    windowColor: {
+      selector: '.vjs-window-color > select',
+      id: 'captions-window-color-%s',
+      label: 'Color'
+    },
+
+    // Options for this object are defined below.
+    windowOpacity: {
+      selector: '.vjs-window-opacity > select',
+      id: 'captions-window-opacity-%s',
+      label: 'Transparency',
+      options: [OPACITY_TRANS, OPACITY_SEMI, OPACITY_OPAQUE]
+    }
+  };
+
+  selectConfigs.windowColor.options = selectConfigs.backgroundColor.options;
+
+  /**
+   * Get the actual value of an option.
+   *
+   * @param  {string} value
+   *         The value to get
+   *
+   * @param  {Function} [parser]
+   *         Optional function to adjust the value.
+   *
+   * @return {Mixed}
+   *         - Will be `undefined` if no value exists
+   *         - Will be `undefined` if the given value is "none".
+   *         - Will be the actual value otherwise.
+   *
+   * @private
+   */
+  function parseOptionValue(value, parser) {
+    if (parser) {
+      value = parser(value);
+    }
+
+    if (value && value !== 'none') {
+      return value;
+    }
+  }
+
+  /**
+   * Gets the value of the selected <option> element within a <select> element.
+   *
+   * @param  {Element} el
+   *         the element to look in
+   *
+   * @param  {Function} [parser]
+   *         Optional function to adjust the value.
+   *
+   * @return {Mixed}
+   *         - Will be `undefined` if no value exists
+   *         - Will be `undefined` if the given value is "none".
+   *         - Will be the actual value otherwise.
+   *
+   * @private
+   */
+  function getSelectedOptionValue(el, parser) {
+    var value = el.options[el.options.selectedIndex].value;
+
+    return parseOptionValue(value, parser);
+  }
+
+  /**
+   * Sets the selected <option> element within a <select> element based on a
+   * given value.
+   *
+   * @param {Element} el
+   *        The element to look in.
+   *
+   * @param {string} value
+   *        the property to look on.
+   *
+   * @param {Function} [parser]
+   *        Optional function to adjust the value before comparing.
+   *
+   * @private
+   */
+  function setSelectedOption(el, value, parser) {
+    if (!value) {
+      return;
+    }
+
+    for (var i = 0; i < el.options.length; i++) {
+      if (parseOptionValue(el.options[i].value, parser) === value) {
+        el.selectedIndex = i;
+        break;
+      }
+    }
+  }
+
+  /**
+   * Manipulate Text Tracks settings.
+   *
+   * @extends ModalDialog
+   */
+
+  var TextTrackSettings = function (_ModalDialog) {
+    inherits(TextTrackSettings, _ModalDialog);
+
+    /**
+     * 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.
+     */
+    function TextTrackSettings(player, options) {
+      classCallCheck(this, TextTrackSettings);
+
+      options.temporary = false;
+
+      var _this = possibleConstructorReturn(this, _ModalDialog.call(this, player, options));
+
+      _this.updateDisplay = bind(_this, _this.updateDisplay);
+
+      // fill the modal and pretend we have opened it
+      _this.fill();
+      _this.hasBeenOpened_ = _this.hasBeenFilled_ = true;
+
+      _this.endDialog = createEl('p', {
+        className: 'vjs-control-text',
+        textContent: _this.localize('End of dialog window.')
+      });
+      _this.el().appendChild(_this.endDialog);
+
+      _this.setDefaults();
+
+      // Grab `persistTextTrackSettings` from the player options if not passed in child options
+      if (options.persistTextTrackSettings === undefined) {
+        _this.options_.persistTextTrackSettings = _this.options_.playerOptions.persistTextTrackSettings;
+      }
+
+      _this.on(_this.$('.vjs-done-button'), 'click', function () {
+        _this.saveSettings();
+        _this.close();
+      });
+
+      _this.on(_this.$('.vjs-default-button'), 'click', function () {
+        _this.setDefaults();
+        _this.updateDisplay();
+      });
+
+      each(selectConfigs, function (config) {
+        _this.on(_this.$(config.selector), 'change', _this.updateDisplay);
+      });
+
+      if (_this.options_.persistTextTrackSettings) {
+        _this.restoreSettings();
+      }
+      return _this;
+    }
+
+    TextTrackSettings.prototype.dispose = function dispose() {
+      this.endDialog = null;
+
+      _ModalDialog.prototype.dispose.call(this);
+    };
+
+    /**
+     * Create a <select> element with configured options.
+     *
+     * @param {string} key
+     *        Configuration key to use during creation.
+     *
+     * @return {string}
+     *         An HTML string.
+     *
+     * @private
+     */
+
+
+    TextTrackSettings.prototype.createElSelect_ = function createElSelect_(key) {
+      var _this2 = this;
+
+      var legendId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
+      var type = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'label';
+
+      var config = selectConfigs[key];
+      var id = config.id.replace('%s', this.id_);
+      var selectLabelledbyIds = [legendId, id].join(' ').trim();
+
+      return ['<' + type + ' id="' + id + '" class="' + (type === 'label' ? 'vjs-label' : '') + '">', this.localize(config.label), '</' + type + '>', '<select aria-labelledby="' + selectLabelledbyIds + '">'].concat(config.options.map(function (o) {
+        var optionId = id + '-' + o[1].replace(/\W+/g, '');
+
+        return ['<option id="' + optionId + '" value="' + o[0] + '" ', 'aria-labelledby="' + selectLabelledbyIds + ' ' + optionId + '">', _this2.localize(o[1]), '</option>'].join('');
+      })).concat('</select>').join('');
+    };
+
+    /**
+     * Create foreground color element for the component
+     *
+     * @return {string}
+     *         An HTML string.
+     *
+     * @private
+     */
+
+
+    TextTrackSettings.prototype.createElFgColor_ = function createElFgColor_() {
+      var legendId = 'captions-text-legend-' + this.id_;
+
+      return ['<fieldset class="vjs-fg-color vjs-track-setting">', '<legend id="' + legendId + '">', this.localize('Text'), '</legend>', this.createElSelect_('color', legendId), '<span class="vjs-text-opacity vjs-opacity">', this.createElSelect_('textOpacity', legendId), '</span>', '</fieldset>'].join('');
+    };
+
+    /**
+     * Create background color element for the component
+     *
+     * @return {string}
+     *         An HTML string.
+     *
+     * @private
+     */
+
+
+    TextTrackSettings.prototype.createElBgColor_ = function createElBgColor_() {
+      var legendId = 'captions-background-' + this.id_;
+
+      return ['<fieldset class="vjs-bg-color vjs-track-setting">', '<legend id="' + legendId + '">', this.localize('Background'), '</legend>', this.createElSelect_('backgroundColor', legendId), '<span class="vjs-bg-opacity vjs-opacity">', this.createElSelect_('backgroundOpacity', legendId), '</span>', '</fieldset>'].join('');
+    };
+
+    /**
+     * Create window color element for the component
+     *
+     * @return {string}
+     *         An HTML string.
+     *
+     * @private
+     */
+
+
+    TextTrackSettings.prototype.createElWinColor_ = function createElWinColor_() {
+      var legendId = 'captions-window-' + this.id_;
+
+      return ['<fieldset class="vjs-window-color vjs-track-setting">', '<legend id="' + legendId + '">', this.localize('Window'), '</legend>', this.createElSelect_('windowColor', legendId), '<span class="vjs-window-opacity vjs-opacity">', this.createElSelect_('windowOpacity', legendId), '</span>', '</fieldset>'].join('');
+    };
+
+    /**
+     * Create color elements for the component
+     *
+     * @return {Element}
+     *         The element that was created
+     *
+     * @private
+     */
+
+
+    TextTrackSettings.prototype.createElColors_ = function createElColors_() {
+      return createEl('div', {
+        className: 'vjs-track-settings-colors',
+        innerHTML: [this.createElFgColor_(), this.createElBgColor_(), this.createElWinColor_()].join('')
+      });
+    };
+
+    /**
+     * Create font elements for the component
+     *
+     * @return {Element}
+     *         The element that was created.
+     *
+     * @private
+     */
+
+
+    TextTrackSettings.prototype.createElFont_ = function createElFont_() {
+      return createEl('div', {
+        className: 'vjs-track-settings-font',
+        innerHTML: ['<fieldset class="vjs-font-percent vjs-track-setting">', this.createElSelect_('fontPercent', '', 'legend'), '</fieldset>', '<fieldset class="vjs-edge-style vjs-track-setting">', this.createElSelect_('edgeStyle', '', 'legend'), '</fieldset>', '<fieldset class="vjs-font-family vjs-track-setting">', this.createElSelect_('fontFamily', '', 'legend'), '</fieldset>'].join('')
+      });
+    };
+
+    /**
+     * Create controls for the component
+     *
+     * @return {Element}
+     *         The element that was created.
+     *
+     * @private
+     */
+
+
+    TextTrackSettings.prototype.createElControls_ = function createElControls_() {
+      var defaultsDescription = this.localize('restore all settings to the default values');
+
+      return createEl('div', {
+        className: 'vjs-track-settings-controls',
+        innerHTML: ['<button class="vjs-default-button" title="' + defaultsDescription + '">', this.localize('Reset'), '<span class="vjs-control-text"> ' + defaultsDescription + '</span>', '</button>', '<button class="vjs-done-button">' + this.localize('Done') + '</button>'].join('')
+      });
+    };
+
+    TextTrackSettings.prototype.content = function content() {
+      return [this.createElColors_(), this.createElFont_(), this.createElControls_()];
+    };
+
+    TextTrackSettings.prototype.label = function label() {
+      return this.localize('Caption Settings Dialog');
+    };
+
+    TextTrackSettings.prototype.description = function description() {
+      return this.localize('Beginning of dialog window. Escape will cancel and close the window.');
+    };
+
+    TextTrackSettings.prototype.buildCSSClass = function buildCSSClass() {
+      return _ModalDialog.prototype.buildCSSClass.call(this) + ' vjs-text-track-settings';
+    };
+
+    /**
+     * Gets an object of text track settings (or null).
+     *
+     * @return {Object}
+     *         An object with config values parsed from the DOM or localStorage.
+     */
+
+
+    TextTrackSettings.prototype.getValues = function getValues() {
+      var _this3 = this;
+
+      return reduce(selectConfigs, function (accum, config, key) {
+        var value = getSelectedOptionValue(_this3.$(config.selector), config.parser);
+
+        if (value !== undefined) {
+          accum[key] = value;
+        }
+
+        return accum;
+      }, {});
+    };
+
+    /**
+     * Sets text track settings from an object of values.
+     *
+     * @param {Object} values
+     *        An object with config values parsed from the DOM or localStorage.
+     */
+
+
+    TextTrackSettings.prototype.setValues = function setValues(values) {
+      var _this4 = this;
+
+      each(selectConfigs, function (config, key) {
+        setSelectedOption(_this4.$(config.selector), values[key], config.parser);
+      });
+    };
+
+    /**
+     * Sets all `<select>` elements to their default values.
+     */
+
+
+    TextTrackSettings.prototype.setDefaults = function setDefaults() {
+      var _this5 = this;
+
+      each(selectConfigs, function (config) {
+        var index = config.hasOwnProperty('default') ? config.default : 0;
+
+        _this5.$(config.selector).selectedIndex = index;
+      });
+    };
+
+    /**
+     * Restore texttrack settings from localStorage
+     */
+
+
+    TextTrackSettings.prototype.restoreSettings = function restoreSettings() {
+      var values = void 0;
+
+      try {
+        values = JSON.parse(window_1.localStorage.getItem(LOCAL_STORAGE_KEY));
+      } catch (err) {
+        log$1.warn(err);
+      }
+
+      if (values) {
+        this.setValues(values);
+      }
+    };
+
+    /**
+     * Save text track settings to localStorage
+     */
+
+
+    TextTrackSettings.prototype.saveSettings = function saveSettings() {
+      if (!this.options_.persistTextTrackSettings) {
+        return;
+      }
+
+      var values = this.getValues();
+
+      try {
+        if (Object.keys(values).length) {
+          window_1.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(values));
+        } else {
+          window_1.localStorage.removeItem(LOCAL_STORAGE_KEY);
+        }
+      } catch (err) {
+        log$1.warn(err);
+      }
+    };
+
+    /**
+     * Update display of text track settings
+     */
+
+
+    TextTrackSettings.prototype.updateDisplay = function updateDisplay() {
+      var ttDisplay = this.player_.getChild('textTrackDisplay');
+
+      if (ttDisplay) {
+        ttDisplay.updateDisplay();
+      }
+    };
+
+    /**
+     * conditionally blur the element and refocus the captions button
+     *
+     * @private
+     */
+
+
+    TextTrackSettings.prototype.conditionalBlur_ = function conditionalBlur_() {
+      this.previouslyActiveEl_ = null;
+      this.off(document_1, 'keydown', this.handleKeyDown);
+
+      var cb = this.player_.controlBar;
+      var subsCapsBtn = cb && cb.subsCapsButton;
+      var ccBtn = cb && cb.captionsButton;
+
+      if (subsCapsBtn) {
+        subsCapsBtn.focus();
+      } else if (ccBtn) {
+        ccBtn.focus();
+      }
+    };
+
+    return TextTrackSettings;
+  }(ModalDialog);
+
+  Component.registerComponent('TextTrackSettings', TextTrackSettings);
+
+  /**
+   * @file resize-manager.js
+   */
+
+  /**
+   * A Resize Manager. It is in charge of triggering `playerresize` on the player in the right conditions.
+   *
+   * It'll either create an iframe and use a debounced resize handler on it or use the new {@link https://wicg.github.io/ResizeObserver/|ResizeObserver}.
+   *
+   * If the ResizeObserver is available natively, it will be used. A polyfill can be passed in as an option.
+   * If a `playerresize` event is not needed, the ResizeManager component can be removed from the player, see the example below.
+   * @example <caption>How to disable the resize manager</caption>
+   * const player = videojs('#vid', {
+   *   resizeManager: false
+   * });
+   *
+   * @see {@link https://wicg.github.io/ResizeObserver/|ResizeObserver specification}
+   *
+   * @extends Component
+   */
+
+  var ResizeManager = function (_Component) {
+    inherits(ResizeManager, _Component);
+
+    /**
+     * Create the ResizeManager.
+     *
+     * @param {Object} player
+     *        The `Player` that this class should be attached to.
+     *
+     * @param {Object} [options]
+     *        The key/value store of ResizeManager options.
+     *
+     * @param {Object} [options.ResizeObserver]
+     *        A polyfill for ResizeObserver can be passed in here.
+     *        If this is set to null it will ignore the native ResizeObserver and fall back to the iframe fallback.
+     */
+    function ResizeManager(player, options) {
+      classCallCheck(this, ResizeManager);
+
+      var RESIZE_OBSERVER_AVAILABLE = options.ResizeObserver || window_1.ResizeObserver;
+
+      // if `null` was passed, we want to disable the ResizeObserver
+      if (options.ResizeObserver === null) {
+        RESIZE_OBSERVER_AVAILABLE = false;
+      }
+
+      // Only create an element when ResizeObserver isn't available
+      var options_ = mergeOptions({createEl: !RESIZE_OBSERVER_AVAILABLE}, options);
+
+      var _this = possibleConstructorReturn(this, _Component.call(this, player, options_));
+
+      _this.ResizeObserver = options.ResizeObserver || window_1.ResizeObserver;
+      _this.loadListener_ = null;
+      _this.resizeObserver_ = null;
+      _this.debouncedHandler_ = debounce(function () {
+        _this.resizeHandler();
+      }, 100, false, player);
+
+      if (RESIZE_OBSERVER_AVAILABLE) {
+        _this.resizeObserver_ = new _this.ResizeObserver(_this.debouncedHandler_);
+        _this.resizeObserver_.observe(player.el());
+      } else {
+        _this.loadListener_ = function () {
+          if (_this.el_.contentWindow) {
+            on(_this.el_.contentWindow, 'resize', _this.debouncedHandler_);
+          }
+          _this.off('load', _this.loadListener_);
+        };
+
+        _this.on('load', _this.loadListener_);
+      }
+      return _this;
+    }
+
+    ResizeManager.prototype.createEl = function createEl() {
+      return _Component.prototype.createEl.call(this, 'iframe', {
+        className: 'vjs-resize-manager'
+      });
+    };
+
+    /**
+     * Called when a resize is triggered on the iframe or a resize is observed via the ResizeObserver
+     *
+     * @fires Player#playerresize
+     */
+
+
+    ResizeManager.prototype.resizeHandler = function resizeHandler() {
+      /**
+       * Called when the player size has changed
+       *
+       * @event Player#playerresize
+       * @type {EventTarget~Event}
+       */
+      this.player_.trigger('playerresize');
+    };
+
+    ResizeManager.prototype.dispose = function dispose() {
+      if (this.resizeObserver_) {
+        this.resizeObserver_.unobserve(this.player_.el());
+        this.resizeObserver_.disconnect();
+      }
+
+      if (this.el_ && this.el_.contentWindow) {
+        off(this.el_.contentWindow, 'resize', this.debouncedHandler_);
+      }
+
+      if (this.loadListener_) {
+        this.off('load', this.loadListener_);
+      }
+
+      this.ResizeObserver = null;
+      this.resizeObserver = null;
+      this.debouncedHandler_ = null;
+      this.loadListener_ = null;
+    };
+
+    return ResizeManager;
+  }(Component);
+
+  Component.registerComponent('ResizeManager', ResizeManager);
+
+  /**
+   * This function is used to fire a sourceset when there is something
+   * similar to `mediaEl.load()` being called. It will try to find the source via
+   * the `src` attribute and then the `<source>` elements. It will then fire `sourceset`
+   * with the source that was found or empty string if we cannot know. If it cannot
+   * find a source then `sourceset` will not be fired.
+   *
+   * @param {Html5} tech
+   *        The tech object that sourceset was setup on
+   *
+   * @return {boolean}
+   *         returns false if the sourceset was not fired and true otherwise.
+   */
+  var sourcesetLoad = function sourcesetLoad(tech) {
+    var el = tech.el();
+
+    // if `el.src` is set, that source will be loaded.
+    if (el.hasAttribute('src')) {
+      tech.triggerSourceset(el.src);
+      return true;
+    }
+
+    /**
+     * Since there isn't a src property on the media element, source elements will be used for
+     * implementing the source selection algorithm. This happens asynchronously and
+     * for most cases were there is more than one source we cannot tell what source will
+     * be loaded, without re-implementing the source selection algorithm. At this time we are not
+     * going to do that. There are three special cases that we do handle here though:
+     *
+     * 1. If there are no sources, do not fire `sourceset`.
+     * 2. If there is only one `<source>` with a `src` property/attribute that is our `src`
+     * 3. If there is more than one `<source>` but all of them have the same `src` url.
+     *    That will be our src.
+     */
+    var sources = tech.$$('source');
+    var srcUrls = [];
+    var src = '';
+
+    // if there are no sources, do not fire sourceset
+    if (!sources.length) {
+      return false;
+    }
+
+    // only count valid/non-duplicate source elements
+    for (var i = 0; i < sources.length; i++) {
+      var url = sources[i].src;
+
+      if (url && srcUrls.indexOf(url) === -1) {
+        srcUrls.push(url);
+      }
+    }
+
+    // there were no valid sources
+    if (!srcUrls.length) {
+      return false;
+    }
+
+    // there is only one valid source element url
+    // use that
+    if (srcUrls.length === 1) {
+      src = srcUrls[0];
+    }
+
+    tech.triggerSourceset(src);
+    return true;
+  };
+
+  /**
+   * our implementation of an `innerHTML` descriptor for browsers
+   * that do not have one.
+   */
+  var innerHTMLDescriptorPolyfill = Object.defineProperty({}, 'innerHTML', {
+    get: function get() {
+      return this.cloneNode(true).innerHTML;
+    },
+    set: function set(v) {
+      // make a dummy node to use innerHTML on
+      var dummy = document_1.createElement(this.nodeName.toLowerCase());
+
+      // set innerHTML to the value provided
+      dummy.innerHTML = v;
+
+      // make a document fragment to hold the nodes from dummy
+      var docFrag = document_1.createDocumentFragment();
+
+      // copy all of the nodes created by the innerHTML on dummy
+      // to the document fragment
+      while (dummy.childNodes.length) {
+        docFrag.appendChild(dummy.childNodes[0]);
+      }
+
+      // remove content
+      this.innerText = '';
+
+      // now we add all of that html in one by appending the
+      // document fragment. This is how innerHTML does it.
+      window_1.Element.prototype.appendChild.call(this, docFrag);
+
+      // then return the result that innerHTML's setter would
+      return this.innerHTML;
+    }
+  });
+
+  /**
+   * Get a property descriptor given a list of priorities and the
+   * property to get.
+   */
+  var getDescriptor = function getDescriptor(priority, prop) {
+    var descriptor = {};
+
+    for (var i = 0; i < priority.length; i++) {
+      descriptor = Object.getOwnPropertyDescriptor(priority[i], prop);
+
+      if (descriptor && descriptor.set && descriptor.get) {
+        break;
+      }
+    }
+
+    descriptor.enumerable = true;
+    descriptor.configurable = true;
+
+    return descriptor;
+  };
+
+  var getInnerHTMLDescriptor = function getInnerHTMLDescriptor(tech) {
+    return getDescriptor([tech.el(), window_1.HTMLMediaElement.prototype, window_1.Element.prototype, innerHTMLDescriptorPolyfill], 'innerHTML');
+  };
+
+  /**
+   * Patches browser internal functions so that we can tell synchronously
+   * if a `<source>` was appended to the media element. For some reason this
+   * causes a `sourceset` if the the media element is ready and has no source.
+   * This happens when:
+   * - The page has just loaded and the media element does not have a source.
+   * - The media element was emptied of all sources, then `load()` was called.
+   *
+   * It does this by patching the following functions/properties when they are supported:
+   *
+   * - `append()` - can be used to add a `<source>` element to the media element
+   * - `appendChild()` - can be used to add a `<source>` element to the media element
+   * - `insertAdjacentHTML()` -  can be used to add a `<source>` element to the media element
+   * - `innerHTML` -  can be used to add a `<source>` element to the media element
+   *
+   * @param {Html5} tech
+   *        The tech object that sourceset is being setup on.
+   */
+  var firstSourceWatch = function firstSourceWatch(tech) {
+    var el = tech.el();
+
+    // make sure firstSourceWatch isn't setup twice.
+    if (el.resetSourceWatch_) {
+      return;
+    }
+
+    var old = {};
+    var innerDescriptor = getInnerHTMLDescriptor(tech);
+    var appendWrapper = function appendWrapper(appendFn) {
+      return function () {
+        for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+          args[_key] = arguments[_key];
+        }
+
+        var retval = appendFn.apply(el, args);
+
+        sourcesetLoad(tech);
+
+        return retval;
+      };
+    };
+
+    ['append', 'appendChild', 'insertAdjacentHTML'].forEach(function (k) {
+      if (!el[k]) {
+        return;
+      }
+
+      // store the old function
+      old[k] = el[k];
+
+      // call the old function with a sourceset if a source
+      // was loaded
+      el[k] = appendWrapper(old[k]);
+    });
+
+    Object.defineProperty(el, 'innerHTML', mergeOptions(innerDescriptor, {
+      set: appendWrapper(innerDescriptor.set)
+    }));
+
+    el.resetSourceWatch_ = function () {
+      el.resetSourceWatch_ = null;
+      Object.keys(old).forEach(function (k) {
+        el[k] = old[k];
+      });
+
+      Object.defineProperty(el, 'innerHTML', innerDescriptor);
+    };
+
+    // on the first sourceset, we need to revert our changes
+    tech.one('sourceset', el.resetSourceWatch_);
+  };
+
+  /**
+   * our implementation of a `src` descriptor for browsers
+   * that do not have one.
+   */
+  var srcDescriptorPolyfill = Object.defineProperty({}, 'src', {
+    get: function get() {
+      if (this.hasAttribute('src')) {
+        return getAbsoluteURL(window_1.Element.prototype.getAttribute.call(this, 'src'));
+      }
+
+      return '';
+    },
+    set: function set(v) {
+      window_1.Element.prototype.setAttribute.call(this, 'src', v);
+
+      return v;
+    }
+  });
+
+  var getSrcDescriptor = function getSrcDescriptor(tech) {
+    return getDescriptor([tech.el(), window_1.HTMLMediaElement.prototype, srcDescriptorPolyfill], 'src');
+  };
+
+  /**
+   * setup `sourceset` handling on the `Html5` tech. This function
+   * patches the following element properties/functions:
+   *
+   * - `src` - to determine when `src` is set
+   * - `setAttribute()` - to determine when `src` is set
+   * - `load()` - this re-triggers the source selection algorithm, and can
+   *              cause a sourceset.
+   *
+   * If there is no source when we are adding `sourceset` support or during a `load()`
+   * we also patch the functions listed in `firstSourceWatch`.
+   *
+   * @param {Html5} tech
+   *        The tech to patch
+   */
+  var setupSourceset = function setupSourceset(tech) {
+    if (!tech.featuresSourceset) {
+      return;
+    }
+
+    var el = tech.el();
+
+    // make sure sourceset isn't setup twice.
+    if (el.resetSourceset_) {
+      return;
+    }
+
+    var srcDescriptor = getSrcDescriptor(tech);
+    var oldSetAttribute = el.setAttribute;
+    var oldLoad = el.load;
+
+    Object.defineProperty(el, 'src', mergeOptions(srcDescriptor, {
+      set: function set(v) {
+        var retval = srcDescriptor.set.call(el, v);
+
+        // we use the getter here to get the actual value set on src
+        tech.triggerSourceset(el.src);
+
+        return retval;
+      }
+    }));
+
+    el.setAttribute = function (n, v) {
+      var retval = oldSetAttribute.call(el, n, v);
+
+      if (/src/i.test(n)) {
+        tech.triggerSourceset(el.src);
+      }
+
+      return retval;
+    };
+
+    el.load = function () {
+      var retval = oldLoad.call(el);
+
+      // if load was called, but there was no source to fire
+      // sourceset on. We have to watch for a source append
+      // as that can trigger a `sourceset` when the media element
+      // has no source
+      if (!sourcesetLoad(tech)) {
+        tech.triggerSourceset('');
+        firstSourceWatch(tech);
+      }
+
+      return retval;
+    };
+
+    if (el.currentSrc) {
+      tech.triggerSourceset(el.currentSrc);
+    } else if (!sourcesetLoad(tech)) {
+      firstSourceWatch(tech);
+    }
+
+    el.resetSourceset_ = function () {
+      el.resetSourceset_ = null;
+      el.load = oldLoad;
+      el.setAttribute = oldSetAttribute;
+      Object.defineProperty(el, 'src', srcDescriptor);
+      if (el.resetSourceWatch_) {
+        el.resetSourceWatch_();
+      }
+    };
+  };
+
+  var _templateObject$1 = taggedTemplateLiteralLoose(['Text Tracks are being loaded from another origin but the crossorigin attribute isn\'t used.\n            This may prevent text tracks from loading.'], ['Text Tracks are being loaded from another origin but the crossorigin attribute isn\'t used.\n            This may prevent text tracks from loading.']);
+
+  /**
+   * HTML5 Media Controller - Wrapper for HTML5 Media API
+   *
+   * @mixes Tech~SourceHandlerAdditions
+   * @extends Tech
+   */
+
+  var Html5 = function (_Tech) {
+    inherits(Html5, _Tech);
+
+    /**
+     * Create an instance of this Tech.
+     *
+     * @param {Object} [options]
+     *        The key/value store of player options.
+     *
+     * @param {Component~ReadyCallback} ready
+     *        Callback function to call when the `HTML5` Tech is ready.
+     */
+    function Html5(options, ready) {
+      classCallCheck(this, Html5);
+
+      var _this = possibleConstructorReturn(this, _Tech.call(this, options, ready));
+
+      var source = options.source;
+      var crossoriginTracks = false;
+
+      // Set the source if one is provided
+      // 1) Check if the source is new (if not, we want to keep the original so playback isn't interrupted)
+      // 2) Check to see if the network state of the tag was failed at init, and if so, reset the source
+      // anyway so the error gets fired.
+      if (source && (_this.el_.currentSrc !== source.src || options.tag && options.tag.initNetworkState_ === 3)) {
+        _this.setSource(source);
+      } else {
+        _this.handleLateInit_(_this.el_);
+      }
+
+      // setup sourceset after late sourceset/init
+      if (options.enableSourceset) {
+        _this.setupSourcesetHandling_();
+      }
+
+      if (_this.el_.hasChildNodes()) {
+
+        var nodes = _this.el_.childNodes;
+        var nodesLength = nodes.length;
+        var removeNodes = [];
+
+        while (nodesLength--) {
+          var node = nodes[nodesLength];
+          var nodeName = node.nodeName.toLowerCase();
+
+          if (nodeName === 'track') {
+            if (!_this.featuresNativeTextTracks) {
+              // Empty video tag tracks so the built-in player doesn't use them also.
+              // This may not be fast enough to stop HTML5 browsers from reading the tags
+              // so we'll need to turn off any default tracks if we're manually doing
+              // captions and subtitles. videoElement.textTracks
+              removeNodes.push(node);
+            } else {
+              // store HTMLTrackElement and TextTrack to remote list
+              _this.remoteTextTrackEls().addTrackElement_(node);
+              _this.remoteTextTracks().addTrack(node.track);
+              _this.textTracks().addTrack(node.track);
+              if (!crossoriginTracks && !_this.el_.hasAttribute('crossorigin') && isCrossOrigin(node.src)) {
+                crossoriginTracks = true;
+              }
+            }
+          }
+        }
+
+        for (var i = 0; i < removeNodes.length; i++) {
+          _this.el_.removeChild(removeNodes[i]);
+        }
+      }
+
+      _this.proxyNativeTracks_();
+      if (_this.featuresNativeTextTracks && crossoriginTracks) {
+        log$1.warn(tsml(_templateObject$1));
+      }
+
+      // prevent iOS Safari from disabling metadata text tracks during native playback
+      _this.restoreMetadataTracksInIOSNativePlayer_();
+
+      // Determine if native controls should be used
+      // Our goal should be to get the custom controls on mobile solid everywhere
+      // so we can remove this all together. Right now this will block custom
+      // controls on touch enabled laptops like the Chrome Pixel
+      if ((TOUCH_ENABLED || IS_IPHONE || IS_NATIVE_ANDROID) && options.nativeControlsForTouch === true) {
+        _this.setControls(true);
+      }
+
+      // on iOS, we want to proxy `webkitbeginfullscreen` and `webkitendfullscreen`
+      // into a `fullscreenchange` event
+      _this.proxyWebkitFullscreen_();
+
+      _this.triggerReady();
+      return _this;
+    }
+
+    /**
+     * Dispose of `HTML5` media element and remove all tracks.
+     */
+
+
+    Html5.prototype.dispose = function dispose() {
+      if (this.el_.resetSourceset_) {
+        this.el_.resetSourceset_();
+      }
+      Html5.disposeMediaElement(this.el_);
+      this.options_ = null;
+
+      // tech will handle clearing of the emulated track list
+      _Tech.prototype.dispose.call(this);
+    };
+
+    /**
+     * Modify the media element so that we can detect when
+     * the source is changed. Fires `sourceset` just after the source has changed
+     */
+
+
+    Html5.prototype.setupSourcesetHandling_ = function setupSourcesetHandling_() {
+      setupSourceset(this);
+    };
+
+    /**
+     * When a captions track is enabled in the iOS Safari native player, all other
+     * tracks are disabled (including metadata tracks), which nulls all of their
+     * associated cue points. This will restore metadata tracks to their pre-fullscreen
+     * state in those cases so that cue points are not needlessly lost.
+     *
+     * @private
+     */
+
+
+    Html5.prototype.restoreMetadataTracksInIOSNativePlayer_ = function restoreMetadataTracksInIOSNativePlayer_() {
+      var textTracks = this.textTracks();
+      var metadataTracksPreFullscreenState = void 0;
+
+      // captures a snapshot of every metadata track's current state
+      var takeMetadataTrackSnapshot = function takeMetadataTrackSnapshot() {
+        metadataTracksPreFullscreenState = [];
+
+        for (var i = 0; i < textTracks.length; i++) {
+          var track = textTracks[i];
+
+          if (track.kind === 'metadata') {
+            metadataTracksPreFullscreenState.push({
+              track: track,
+              storedMode: track.mode
+            });
+          }
+        }
+      };
+
+      // snapshot each metadata track's initial state, and update the snapshot
+      // each time there is a track 'change' event
+      takeMetadataTrackSnapshot();
+      textTracks.addEventListener('change', takeMetadataTrackSnapshot);
+
+      this.on('dispose', function () {
+        return textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
+      });
+
+      var restoreTrackMode = function restoreTrackMode() {
+        for (var i = 0; i < metadataTracksPreFullscreenState.length; i++) {
+          var storedTrack = metadataTracksPreFullscreenState[i];
+
+          if (storedTrack.track.mode === 'disabled' && storedTrack.track.mode !== storedTrack.storedMode) {
+            storedTrack.track.mode = storedTrack.storedMode;
+          }
+        }
+        // we only want this handler to be executed on the first 'change' event
+        textTracks.removeEventListener('change', restoreTrackMode);
+      };
+
+      // when we enter fullscreen playback, stop updating the snapshot and
+      // restore all track modes to their pre-fullscreen state
+      this.on('webkitbeginfullscreen', function () {
+        textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
+
+        // remove the listener before adding it just in case it wasn't previously removed
+        textTracks.removeEventListener('change', restoreTrackMode);
+        textTracks.addEventListener('change', restoreTrackMode);
+      });
+
+      // start updating the snapshot again after leaving fullscreen
+      this.on('webkitendfullscreen', function () {
+        // remove the listener before adding it just in case it wasn't previously removed
+        textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
+        textTracks.addEventListener('change', takeMetadataTrackSnapshot);
+
+        // remove the restoreTrackMode handler in case it wasn't triggered during fullscreen playback
+        textTracks.removeEventListener('change', restoreTrackMode);
+      });
+    };
+
+    /**
+     * Attempt to force override of tracks for the given type
+     *
+     * @param {String} type - Track type to override, possible values include 'Audio',
+     * 'Video', and 'Text'.
+     * @param {Boolean} override - If set to true native audio/video will be overridden,
+     * otherwise native audio/video will potentially be used.
+     * @private
+     */
+
+
+    Html5.prototype.overrideNative_ = function overrideNative_(type, override) {
+      var _this2 = this;
+
+      // If there is no behavioral change don't add/remove listeners
+      if (override !== this['featuresNative' + type + 'Tracks']) {
+        return;
+      }
+
+      var lowerCaseType = type.toLowerCase();
+
+      if (this[lowerCaseType + 'TracksListeners_']) {
+        Object.keys(this[lowerCaseType + 'TracksListeners_']).forEach(function (eventName) {
+          var elTracks = _this2.el()[lowerCaseType + 'Tracks'];
+
+          elTracks.removeEventListener(eventName, _this2[lowerCaseType + 'TracksListeners_'][eventName]);
+        });
+      }
+
+      this['featuresNative' + type + 'Tracks'] = !override;
+      this[lowerCaseType + 'TracksListeners_'] = null;
+
+      this.proxyNativeTracksForType_(lowerCaseType);
+    };
+
+    /**
+     * Attempt to force override of native audio tracks.
+     *
+     * @param {Boolean} override - If set to true native audio will be overridden,
+     * otherwise native audio will potentially be used.
+     */
+
+
+    Html5.prototype.overrideNativeAudioTracks = function overrideNativeAudioTracks(override) {
+      this.overrideNative_('Audio', override);
+    };
+
+    /**
+     * Attempt to force override of native video tracks.
+     *
+     * @param {Boolean} override - If set to true native video will be overridden,
+     * otherwise native video will potentially be used.
+     */
+
+
+    Html5.prototype.overrideNativeVideoTracks = function overrideNativeVideoTracks(override) {
+      this.overrideNative_('Video', override);
+    };
+
+    /**
+     * Proxy native track list events for the given type to our track
+     * lists if the browser we are playing in supports that type of track list.
+     *
+     * @param {string} name - Track type; values include 'audio', 'video', and 'text'
+     * @private
+     */
+
+
+    Html5.prototype.proxyNativeTracksForType_ = function proxyNativeTracksForType_(name) {
+      var _this3 = this;
+
+      var props = NORMAL[name];
+      var elTracks = this.el()[props.getterName];
+      var techTracks = this[props.getterName]();
+
+      if (!this['featuresNative' + props.capitalName + 'Tracks'] || !elTracks || !elTracks.addEventListener) {
+        return;
+      }
+      var listeners = {
+        change: function change(e) {
+          techTracks.trigger({
+            type: 'change',
+            target: techTracks,
+            currentTarget: techTracks,
+            srcElement: techTracks
+          });
+        },
+        addtrack: function addtrack(e) {
+          techTracks.addTrack(e.track);
+        },
+        removetrack: function removetrack(e) {
+          techTracks.removeTrack(e.track);
+        }
+      };
+      var removeOldTracks = function removeOldTracks() {
+        var removeTracks = [];
+
+        for (var i = 0; i < techTracks.length; i++) {
+          var found = false;
+
+          for (var j = 0; j < elTracks.length; j++) {
+            if (elTracks[j] === techTracks[i]) {
+              found = true;
+              break;
+            }
+          }
+
+          if (!found) {
+            removeTracks.push(techTracks[i]);
+          }
+        }
+
+        while (removeTracks.length) {
+          techTracks.removeTrack(removeTracks.shift());
+        }
+      };
+
+      this[props.getterName + 'Listeners_'] = listeners;
+
+      Object.keys(listeners).forEach(function (eventName) {
+        var listener = listeners[eventName];
+
+        elTracks.addEventListener(eventName, listener);
+        _this3.on('dispose', function (e) {
+          return elTracks.removeEventListener(eventName, listener);
+        });
+      });
+
+      // Remove (native) tracks that are not used anymore
+      this.on('loadstart', removeOldTracks);
+      this.on('dispose', function (e) {
+        return _this3.off('loadstart', removeOldTracks);
+      });
+    };
+
+    /**
+     * Proxy all native track list events to our track lists if the browser we are playing
+     * in supports that type of track list.
+     *
+     * @private
+     */
+
+
+    Html5.prototype.proxyNativeTracks_ = function proxyNativeTracks_() {
+      var _this4 = this;
+
+      NORMAL.names.forEach(function (name) {
+        _this4.proxyNativeTracksForType_(name);
+      });
+    };
+
+    /**
+     * Create the `Html5` Tech's DOM element.
+     *
+     * @return {Element}
+     *         The element that gets created.
+     */
+
+
+    Html5.prototype.createEl = function createEl$$1() {
+      var el = this.options_.tag;
+
+      // Check if this browser supports moving the element into the box.
+      // On the iPhone video will break if you move the element,
+      // So we have to create a brand new element.
+      // If we ingested the player div, we do not need to move the media element.
+      if (!el || !(this.options_.playerElIngest || this.movingMediaElementInDOM)) {
+
+        // If the original tag is still there, clone and remove it.
+        if (el) {
+          var clone = el.cloneNode(true);
+
+          if (el.parentNode) {
+            el.parentNode.insertBefore(clone, el);
+          }
+          Html5.disposeMediaElement(el);
+          el = clone;
+        } else {
+          el = document_1.createElement('video');
+
+          // determine if native controls should be used
+          var tagAttributes = this.options_.tag && getAttributes(this.options_.tag);
+          var attributes = mergeOptions({}, tagAttributes);
+
+          if (!TOUCH_ENABLED || this.options_.nativeControlsForTouch !== true) {
+            delete attributes.controls;
+          }
+
+          setAttributes(el, assign(attributes, {
+            id: this.options_.techId,
+            class: 'vjs-tech'
+          }));
+        }
+
+        el.playerId = this.options_.playerId;
+      }
+
+      if (typeof this.options_.preload !== 'undefined') {
+        setAttribute(el, 'preload', this.options_.preload);
+      }
+
+      // Update specific tag settings, in case they were overridden
+      // `autoplay` has to be *last* so that `muted` and `playsinline` are present
+      // when iOS/Safari or other browsers attempt to autoplay.
+      var settingsAttrs = ['loop', 'muted', 'playsinline', 'autoplay'];
+
+      for (var i = 0; i < settingsAttrs.length; i++) {
+        var attr = settingsAttrs[i];
+        var value = this.options_[attr];
+
+        if (typeof value !== 'undefined') {
+          if (value) {
+            setAttribute(el, attr, attr);
+          } else {
+            removeAttribute(el, attr);
+          }
+          el[attr] = value;
+        }
+      }
+
+      return el;
+    };
+
+    /**
+     * This will be triggered if the loadstart event has already fired, before videojs was
+     * ready. Two known examples of when this can happen are:
+     * 1. If we're loading the playback object after it has started loading
+     * 2. The media is already playing the (often with autoplay on) then
+     *
+     * This function will fire another loadstart so that videojs can catchup.
+     *
+     * @fires Tech#loadstart
+     *
+     * @return {undefined}
+     *         returns nothing.
+     */
+
+
+    Html5.prototype.handleLateInit_ = function handleLateInit_(el) {
+      if (el.networkState === 0 || el.networkState === 3) {
+        // The video element hasn't started loading the source yet
+        // or didn't find a source
+        return;
+      }
+
+      if (el.readyState === 0) {
+        // NetworkState is set synchronously BUT loadstart is fired at the
+        // end of the current stack, usually before setInterval(fn, 0).
+        // So at this point we know loadstart may have already fired or is
+        // about to fire, and either way the player hasn't seen it yet.
+        // We don't want to fire loadstart prematurely here and cause a
+        // double loadstart so we'll wait and see if it happens between now
+        // and the next loop, and fire it if not.
+        // HOWEVER, we also want to make sure it fires before loadedmetadata
+        // which could also happen between now and the next loop, so we'll
+        // watch for that also.
+        var loadstartFired = false;
+        var setLoadstartFired = function setLoadstartFired() {
+          loadstartFired = true;
+        };
+
+        this.on('loadstart', setLoadstartFired);
+
+        var triggerLoadstart = function triggerLoadstart() {
+          // We did miss the original loadstart. Make sure the player
+          // sees loadstart before loadedmetadata
+          if (!loadstartFired) {
+            this.trigger('loadstart');
+          }
+        };
+
+        this.on('loadedmetadata', triggerLoadstart);
+
+        this.ready(function () {
+          this.off('loadstart', setLoadstartFired);
+          this.off('loadedmetadata', triggerLoadstart);
+
+          if (!loadstartFired) {
+            // We did miss the original native loadstart. Fire it now.
+            this.trigger('loadstart');
+          }
+        });
+
+        return;
+      }
+
+      // From here on we know that loadstart already fired and we missed it.
+      // The other readyState events aren't as much of a problem if we double
+      // them, so not going to go to as much trouble as loadstart to prevent
+      // that unless we find reason to.
+      var eventsToTrigger = ['loadstart'];
+
+      // loadedmetadata: newly equal to HAVE_METADATA (1) or greater
+      eventsToTrigger.push('loadedmetadata');
+
+      // loadeddata: newly increased to HAVE_CURRENT_DATA (2) or greater
+      if (el.readyState >= 2) {
+        eventsToTrigger.push('loadeddata');
+      }
+
+      // canplay: newly increased to HAVE_FUTURE_DATA (3) or greater
+      if (el.readyState >= 3) {
+        eventsToTrigger.push('canplay');
+      }
+
+      // canplaythrough: newly equal to HAVE_ENOUGH_DATA (4)
+      if (el.readyState >= 4) {
+        eventsToTrigger.push('canplaythrough');
+      }
+
+      // We still need to give the player time to add event listeners
+      this.ready(function () {
+        eventsToTrigger.forEach(function (type) {
+          this.trigger(type);
+        }, this);
+      });
+    };
+
+    /**
+     * Set current time for the `HTML5` tech.
+     *
+     * @param {number} seconds
+     *        Set the current time of the media to this.
+     */
+
+
+    Html5.prototype.setCurrentTime = function setCurrentTime(seconds) {
+      try {
+        this.el_.currentTime = seconds;
+      } catch (e) {
+        log$1(e, 'Video is not ready. (Video.js)');
+        // this.warning(VideoJS.warnings.videoNotReady);
+      }
+    };
+
+    /**
+     * Get the current duration of the HTML5 media element.
+     *
+     * @return {number}
+     *         The duration of the media or 0 if there is no duration.
+     */
+
+
+    Html5.prototype.duration = function duration() {
+      var _this5 = this;
+
+      // Android Chrome will report duration as Infinity for VOD HLS until after
+      // playback has started, which triggers the live display erroneously.
+      // Return NaN if playback has not started and trigger a durationupdate once
+      // the duration can be reliably known.
+      if (this.el_.duration === Infinity && IS_ANDROID && IS_CHROME && this.el_.currentTime === 0) {
+        // Wait for the first `timeupdate` with currentTime > 0 - there may be
+        // several with 0
+        var checkProgress = function checkProgress() {
+          if (_this5.el_.currentTime > 0) {
+            // Trigger durationchange for genuinely live video
+            if (_this5.el_.duration === Infinity) {
+              _this5.trigger('durationchange');
+            }
+            _this5.off('timeupdate', checkProgress);
+          }
+        };
+
+        this.on('timeupdate', checkProgress);
+        return NaN;
+      }
+      return this.el_.duration || NaN;
+    };
+
+    /**
+     * Get the current width of the HTML5 media element.
+     *
+     * @return {number}
+     *         The width of the HTML5 media element.
+     */
+
+
+    Html5.prototype.width = function width() {
+      return this.el_.offsetWidth;
+    };
+
+    /**
+     * Get the current height of the HTML5 media element.
+     *
+     * @return {number}
+     *         The height of the HTML5 media element.
+     */
+
+
+    Html5.prototype.height = function height() {
+      return this.el_.offsetHeight;
+    };
+
+    /**
+     * Proxy iOS `webkitbeginfullscreen` and `webkitendfullscreen` into
+     * `fullscreenchange` event.
+     *
+     * @private
+     * @fires fullscreenchange
+     * @listens webkitendfullscreen
+     * @listens webkitbeginfullscreen
+     * @listens webkitbeginfullscreen
+     */
+
+
+    Html5.prototype.proxyWebkitFullscreen_ = function proxyWebkitFullscreen_() {
+      var _this6 = this;
+
+      if (!('webkitDisplayingFullscreen' in this.el_)) {
+        return;
+      }
+
+      var endFn = function endFn() {
+        this.trigger('fullscreenchange', {isFullscreen: false});
+      };
+
+      var beginFn = function beginFn() {
+        if ('webkitPresentationMode' in this.el_ && this.el_.webkitPresentationMode !== 'picture-in-picture') {
+          this.one('webkitendfullscreen', endFn);
+
+          this.trigger('fullscreenchange', {isFullscreen: true});
+        }
+      };
+
+      this.on('webkitbeginfullscreen', beginFn);
+      this.on('dispose', function () {
+        _this6.off('webkitbeginfullscreen', beginFn);
+        _this6.off('webkitendfullscreen', endFn);
+      });
+    };
+
+    /**
+     * Check if fullscreen is supported on the current playback device.
+     *
+     * @return {boolean}
+     *         - True if fullscreen is supported.
+     *         - False if fullscreen is not supported.
+     */
+
+
+    Html5.prototype.supportsFullScreen = function supportsFullScreen() {
+      if (typeof this.el_.webkitEnterFullScreen === 'function') {
+        var userAgent = window_1.navigator && window_1.navigator.userAgent || '';
+
+        // Seems to be broken in Chromium/Chrome && Safari in Leopard
+        if (/Android/.test(userAgent) || !/Chrome|Mac OS X 10.5/.test(userAgent)) {
+          return true;
+        }
+      }
+      return false;
+    };
+
+    /**
+     * Request that the `HTML5` Tech enter fullscreen.
+     */
+
+
+    Html5.prototype.enterFullScreen = function enterFullScreen() {
+      var video = this.el_;
+
+      if (video.paused && video.networkState <= video.HAVE_METADATA) {
+        // attempt to prime the video element for programmatic access
+        // this isn't necessary on the desktop but shouldn't hurt
+        this.el_.play();
+
+        // playing and pausing synchronously during the transition to fullscreen
+        // can get iOS ~6.1 devices into a playOld/pause loop
+        this.setTimeout(function () {
+          video.pause();
+          video.webkitEnterFullScreen();
+        }, 0);
+      } else {
+        video.webkitEnterFullScreen();
+      }
+    };
+
+    /**
+     * Request that the `HTML5` Tech exit fullscreen.
+     */
+
+
+    Html5.prototype.exitFullScreen = function exitFullScreen() {
+      this.el_.webkitExitFullScreen();
+    };
+
+    /**
+     * A getter/setter for the `Html5` Tech's source object.
+     * > Note: Please use {@link Html5#setSource}
+     *
+     * @param {Tech~SourceObject} [src]
+     *        The source object you want to set on the `HTML5` techs element.
+     *
+     * @return {Tech~SourceObject|undefined}
+     *         - The current source object when a source is not passed in.
+     *         - undefined when setting
+     *
+     * @deprecated Since version 5.
+     */
+
+
+    Html5.prototype.src = function src(_src) {
+      if (_src === undefined) {
+        return this.el_.src;
+      }
+
+      // Setting src through `src` instead of `setSrc` will be deprecated
+      this.setSrc(_src);
+    };
+
+    /**
+     * Reset the tech by removing all sources and then calling
+     * {@link Html5.resetMediaElement}.
+     */
+
+
+    Html5.prototype.reset = function reset() {
+      Html5.resetMediaElement(this.el_);
+    };
+
+    /**
+     * Get the current source on the `HTML5` Tech. Falls back to returning the source from
+     * the HTML5 media element.
+     *
+     * @return {Tech~SourceObject}
+     *         The current source object from the HTML5 tech. With a fallback to the
+     *         elements source.
+     */
+
+
+    Html5.prototype.currentSrc = function currentSrc() {
+      if (this.currentSource_) {
+        return this.currentSource_.src;
+      }
+      return this.el_.currentSrc;
+    };
+
+    /**
+     * Set controls attribute for the HTML5 media Element.
+     *
+     * @param {string} val
+     *        Value to set the controls attribute to
+     */
+
+
+    Html5.prototype.setControls = function setControls(val) {
+      this.el_.controls = !!val;
+    };
+
+    /**
+     * Create and returns a remote {@link TextTrack} object.
+     *
+     * @param {string} kind
+     *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
+     *
+     * @param {string} [label]
+     *        Label to identify the text track
+     *
+     * @param {string} [language]
+     *        Two letter language abbreviation
+     *
+     * @return {TextTrack}
+     *         The TextTrack that gets created.
+     */
+
+
+    Html5.prototype.addTextTrack = function addTextTrack(kind, label, language) {
+      if (!this.featuresNativeTextTracks) {
+        return _Tech.prototype.addTextTrack.call(this, kind, label, language);
+      }
+
+      return this.el_.addTextTrack(kind, label, language);
+    };
+
+    /**
+     * Creates either native TextTrack or an emulated TextTrack depending
+     * on the value of `featuresNativeTextTracks`
+     *
+     * @param {Object} options
+     *        The object should contain the options to initialize the TextTrack with.
+     *
+     * @param {string} [options.kind]
+     *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata).
+     *
+     * @param {string} [options.label]
+     *        Label to identify the text track
+     *
+     * @param {string} [options.language]
+     *        Two letter language abbreviation.
+     *
+     * @param {boolean} [options.default]
+     *        Default this track to on.
+     *
+     * @param {string} [options.id]
+     *        The internal id to assign this track.
+     *
+     * @param {string} [options.src]
+     *        A source url for the track.
+     *
+     * @return {HTMLTrackElement}
+     *         The track element that gets created.
+     */
+
+
+    Html5.prototype.createRemoteTextTrack = function createRemoteTextTrack(options) {
+      if (!this.featuresNativeTextTracks) {
+        return _Tech.prototype.createRemoteTextTrack.call(this, options);
+      }
+      var htmlTrackElement = document_1.createElement('track');
+
+      if (options.kind) {
+        htmlTrackElement.kind = options.kind;
+      }
+      if (options.label) {
+        htmlTrackElement.label = options.label;
+      }
+      if (options.language || options.srclang) {
+        htmlTrackElement.srclang = options.language || options.srclang;
+      }
+      if (options.default) {
+        htmlTrackElement.default = options.default;
+      }
+      if (options.id) {
+        htmlTrackElement.id = options.id;
+      }
+      if (options.src) {
+        htmlTrackElement.src = options.src;
+      }
+
+      return htmlTrackElement;
+    };
+
+    /**
+     * Creates a remote text track object and returns an html track element.
+     *
+     * @param {Object} options The object should contain values for
+     * kind, language, label, and src (location of the WebVTT file)
+     * @param {Boolean} [manualCleanup=true] if set to false, the TextTrack will be
+     * automatically removed from the video element whenever the source changes
+     * @return {HTMLTrackElement} An Html Track Element.
+     * This can be an emulated {@link HTMLTrackElement} or a native one.
+     * @deprecated The default value of the "manualCleanup" parameter will default
+     * to "false" in upcoming versions of Video.js
+     */
+
+
+    Html5.prototype.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
+      var htmlTrackElement = _Tech.prototype.addRemoteTextTrack.call(this, options, manualCleanup);
+
+      if (this.featuresNativeTextTracks) {
+        this.el().appendChild(htmlTrackElement);
+      }
+
+      return htmlTrackElement;
+    };
+
+    /**
+     * Remove remote `TextTrack` from `TextTrackList` object
+     *
+     * @param {TextTrack} track
+     *        `TextTrack` object to remove
+     */
+
+
+    Html5.prototype.removeRemoteTextTrack = function removeRemoteTextTrack(track) {
+      _Tech.prototype.removeRemoteTextTrack.call(this, track);
+
+      if (this.featuresNativeTextTracks) {
+        var tracks = this.$$('track');
+
+        var i = tracks.length;
+
+        while (i--) {
+          if (track === tracks[i] || track === tracks[i].track) {
+            this.el().removeChild(tracks[i]);
+          }
+        }
+      }
+    };
+
+    /**
+     * Gets available media playback quality metrics as specified by the W3C's Media
+     * Playback Quality API.
+     *
+     * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
+     *
+     * @return {Object}
+     *         An object with supported media playback quality metrics
+     */
+
+
+    Html5.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
+      if (typeof this.el().getVideoPlaybackQuality === 'function') {
+        return this.el().getVideoPlaybackQuality();
+      }
+
+      var videoPlaybackQuality = {};
+
+      if (typeof this.el().webkitDroppedFrameCount !== 'undefined' && typeof this.el().webkitDecodedFrameCount !== 'undefined') {
+        videoPlaybackQuality.droppedVideoFrames = this.el().webkitDroppedFrameCount;
+        videoPlaybackQuality.totalVideoFrames = this.el().webkitDecodedFrameCount;
+      }
+
+      if (window_1.performance && typeof window_1.performance.now === 'function') {
+        videoPlaybackQuality.creationTime = window_1.performance.now();
+      } else if (window_1.performance && window_1.performance.timing && typeof window_1.performance.timing.navigationStart === 'number') {
+        videoPlaybackQuality.creationTime = window_1.Date.now() - window_1.performance.timing.navigationStart;
+      }
+
+      return videoPlaybackQuality;
+    };
+
+    return Html5;
+  }(Tech);
+
+  /* HTML5 Support Testing ---------------------------------------------------- */
+
+  if (isReal()) {
+
+    /**
+     * Element for testing browser HTML5 media capabilities
+     *
+     * @type {Element}
+     * @constant
+     * @private
+     */
+    Html5.TEST_VID = document_1.createElement('video');
+    var track = document_1.createElement('track');
+
+    track.kind = 'captions';
+    track.srclang = 'en';
+    track.label = 'English';
+    Html5.TEST_VID.appendChild(track);
+  }
+
+  /**
+   * Check if HTML5 media is supported by this browser/device.
+   *
+   * @return {boolean}
+   *         - True if HTML5 media is supported.
+   *         - False if HTML5 media is not supported.
+   */
+  Html5.isSupported = function () {
+    // IE with no Media Player is a LIAR! (#984)
+    try {
+      Html5.TEST_VID.volume = 0.5;
+    } catch (e) {
+      return false;
+    }
+
+    return !!(Html5.TEST_VID && Html5.TEST_VID.canPlayType);
+  };
+
+  /**
+   * Check if the tech can support the given type
+   *
+   * @param {string} type
+   *        The mimetype to check
+   * @return {string} 'probably', 'maybe', or '' (empty string)
+   */
+  Html5.canPlayType = function (type) {
+    return Html5.TEST_VID.canPlayType(type);
+  };
+
+  /**
+   * Check if the tech can support the given source
+   * @param {Object} srcObj
+   *        The source object
+   * @param {Object} options
+   *        The options passed to the tech
+   * @return {string} 'probably', 'maybe', or '' (empty string)
+   */
+  Html5.canPlaySource = function (srcObj, options) {
+    return Html5.canPlayType(srcObj.type);
+  };
+
+  /**
+   * Check if the volume can be changed in this browser/device.
+   * Volume cannot be changed in a lot of mobile devices.
+   * Specifically, it can't be changed from 1 on iOS.
+   *
+   * @return {boolean}
+   *         - True if volume can be controlled
+   *         - False otherwise
+   */
+  Html5.canControlVolume = function () {
+    // IE will error if Windows Media Player not installed #3315
+    try {
+      var volume = Html5.TEST_VID.volume;
+
+      Html5.TEST_VID.volume = volume / 2 + 0.1;
+      return volume !== Html5.TEST_VID.volume;
+    } catch (e) {
+      return false;
+    }
+  };
+
+  /**
+   * Check if the playback rate can be changed in this browser/device.
+   *
+   * @return {boolean}
+   *         - True if playback rate can be controlled
+   *         - False otherwise
+   */
+  Html5.canControlPlaybackRate = function () {
+    // Playback rate API is implemented in Android Chrome, but doesn't do anything
+    // https://github.com/videojs/video.js/issues/3180
+    if (IS_ANDROID && IS_CHROME && CHROME_VERSION < 58) {
+      return false;
+    }
+    // IE will error if Windows Media Player not installed #3315
+    try {
+      var playbackRate = Html5.TEST_VID.playbackRate;
+
+      Html5.TEST_VID.playbackRate = playbackRate / 2 + 0.1;
+      return playbackRate !== Html5.TEST_VID.playbackRate;
+    } catch (e) {
+      return false;
+    }
+  };
+
+  /**
+   * Check if we can override a video/audio elements attributes, with
+   * Object.defineProperty.
+   *
+   * @return {boolean}
+   *         - True if builtin attributes can be overridden
+   *         - False otherwise
+   */
+  Html5.canOverrideAttributes = function () {
+    // if we cannot overwrite the src/innerHTML property, there is no support
+    // iOS 7 safari for instance cannot do this.
+    try {
+      var noop = function noop() {
+      };
+
+      Object.defineProperty(document_1.createElement('video'), 'src', {get: noop, set: noop});
+      Object.defineProperty(document_1.createElement('audio'), 'src', {get: noop, set: noop});
+      Object.defineProperty(document_1.createElement('video'), 'innerHTML', {get: noop, set: noop});
+      Object.defineProperty(document_1.createElement('audio'), 'innerHTML', {get: noop, set: noop});
+    } catch (e) {
+      return false;
+    }
+
+    return true;
+  };
+
+  /**
+   * Check to see if native `TextTrack`s are supported by this browser/device.
+   *
+   * @return {boolean}
+   *         - True if native `TextTrack`s are supported.
+   *         - False otherwise
+   */
+  Html5.supportsNativeTextTracks = function () {
+    return IS_ANY_SAFARI;
+  };
+
+  /**
+   * Check to see if native `VideoTrack`s are supported by this browser/device
+   *
+   * @return {boolean}
+   *        - True if native `VideoTrack`s are supported.
+   *        - False otherwise
+   */
+  Html5.supportsNativeVideoTracks = function () {
+    return !!(Html5.TEST_VID && Html5.TEST_VID.videoTracks);
+  };
+
+  /**
+   * Check to see if native `AudioTrack`s are supported by this browser/device
+   *
+   * @return {boolean}
+   *        - True if native `AudioTrack`s are supported.
+   *        - False otherwise
+   */
+  Html5.supportsNativeAudioTracks = function () {
+    return !!(Html5.TEST_VID && Html5.TEST_VID.audioTracks);
+  };
+
+  /**
+   * An array of events available on the Html5 tech.
+   *
+   * @private
+   * @type {Array}
+   */
+  Html5.Events = ['loadstart', 'suspend', 'abort', 'error', 'emptied', 'stalled', 'loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough', 'playing', 'waiting', 'seeking', 'seeked', 'ended', 'durationchange', 'timeupdate', 'progress', 'play', 'pause', 'ratechange', 'resize', 'volumechange'];
+
+  /**
+   * Boolean indicating whether the `Tech` supports volume control.
+   *
+   * @type {boolean}
+   * @default {@link Html5.canControlVolume}
+   */
+  Html5.prototype.featuresVolumeControl = Html5.canControlVolume();
+
+  /**
+   * Boolean indicating whether the `Tech` supports changing the speed at which the media
+   * plays. Examples:
+   *   - Set player to playOld 2x (twice) as fast
+   *   - Set player to playOld 0.5x (half) as fast
+   *
+   * @type {boolean}
+   * @default {@link Html5.canControlPlaybackRate}
+   */
+  Html5.prototype.featuresPlaybackRate = Html5.canControlPlaybackRate();
+
+  /**
+   * Boolean indicating whether the `Tech` supports the `sourceset` event.
+   *
+   * @type {boolean}
+   * @default
+   */
+  Html5.prototype.featuresSourceset = Html5.canOverrideAttributes();
+
+  /**
+   * Boolean indicating whether the `HTML5` tech currently supports the media element
+   * moving in the DOM. iOS breaks if you move the media element, so this is set this to
+   * false there. Everywhere else this should be true.
+   *
+   * @type {boolean}
+   * @default
+   */
+  Html5.prototype.movingMediaElementInDOM = !IS_IOS;
+
+  // TODO: Previous comment: No longer appears to be used. Can probably be removed.
+  //       Is this true?
+  /**
+   * Boolean indicating whether the `HTML5` tech currently supports automatic media resize
+   * when going into fullscreen.
+   *
+   * @type {boolean}
+   * @default
+   */
+  Html5.prototype.featuresFullscreenResize = true;
+
+  /**
+   * Boolean indicating whether the `HTML5` tech currently supports the progress event.
+   * If this is false, manual `progress` events will be triggered instead.
+   *
+   * @type {boolean}
+   * @default
+   */
+  Html5.prototype.featuresProgressEvents = true;
+
+  /**
+   * Boolean indicating whether the `HTML5` tech currently supports the timeupdate event.
+   * If this is false, manual `timeupdate` events will be triggered instead.
+   *
+   * @default
+   */
+  Html5.prototype.featuresTimeupdateEvents = true;
+
+  /**
+   * Boolean indicating whether the `HTML5` tech currently supports native `TextTrack`s.
+   *
+   * @type {boolean}
+   * @default {@link Html5.supportsNativeTextTracks}
+   */
+  Html5.prototype.featuresNativeTextTracks = Html5.supportsNativeTextTracks();
+
+  /**
+   * Boolean indicating whether the `HTML5` tech currently supports native `VideoTrack`s.
+   *
+   * @type {boolean}
+   * @default {@link Html5.supportsNativeVideoTracks}
+   */
+  Html5.prototype.featuresNativeVideoTracks = Html5.supportsNativeVideoTracks();
+
+  /**
+   * Boolean indicating whether the `HTML5` tech currently supports native `AudioTrack`s.
+   *
+   * @type {boolean}
+   * @default {@link Html5.supportsNativeAudioTracks}
+   */
+  Html5.prototype.featuresNativeAudioTracks = Html5.supportsNativeAudioTracks();
+
+  // HTML5 Feature detection and Device Fixes --------------------------------- //
+  var canPlayType = Html5.TEST_VID && Html5.TEST_VID.constructor.prototype.canPlayType;
+  var mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i;
+
+  Html5.patchCanPlayType = function () {
+
+    // Android 4.0 and above can playOld HLS to some extent but it reports being unable to do so
+    // Firefox and Chrome report correctly
+    if (ANDROID_VERSION >= 4.0 && !IS_FIREFOX && !IS_CHROME) {
+      Html5.TEST_VID.constructor.prototype.canPlayType = function (type) {
+        if (type && mpegurlRE.test(type)) {
+          return 'maybe';
+        }
+        return canPlayType.call(this, type);
+      };
+    }
+  };
+
+  Html5.unpatchCanPlayType = function () {
+    var r = Html5.TEST_VID.constructor.prototype.canPlayType;
+
+    Html5.TEST_VID.constructor.prototype.canPlayType = canPlayType;
+    return r;
+  };
+
+  // by default, patch the media element
+  Html5.patchCanPlayType();
+
+  Html5.disposeMediaElement = function (el) {
+    if (!el) {
+      return;
+    }
+
+    if (el.parentNode) {
+      el.parentNode.removeChild(el);
+    }
+
+    // remove any child track or source nodes to prevent their loading
+    while (el.hasChildNodes()) {
+      el.removeChild(el.firstChild);
+    }
+
+    // remove any src reference. not setting `src=''` because that causes a warning
+    // in firefox
+    el.removeAttribute('src');
+
+    // force the media element to update its loading state by calling load()
+    // however IE on Windows 7N has a bug that throws an error so need a try/catch (#793)
+    if (typeof el.load === 'function') {
+      // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473)
+      (function () {
+        try {
+          el.load();
+        } catch (e) {
+          // not supported
+        }
+      })();
+    }
+  };
+
+  Html5.resetMediaElement = function (el) {
+    if (!el) {
+      return;
+    }
+
+    var sources = el.querySelectorAll('source');
+    var i = sources.length;
+
+    while (i--) {
+      el.removeChild(sources[i]);
+    }
+
+    // remove any src reference.
+    // not setting `src=''` because that throws an error
+    el.removeAttribute('src');
+
+    if (typeof el.load === 'function') {
+      // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473)
+      (function () {
+        try {
+          el.load();
+        } catch (e) {
+          // satisfy linter
+        }
+      })();
+    }
+  };
+
+  /* Native HTML5 element property wrapping ----------------------------------- */
+  // Wrap native boolean attributes with getters that check both property and attribute
+  // The list is as followed:
+  // muted, defaultMuted, autoplay, controls, loop, playsinline
+  [
+    /**
+     * Get the value of `muted` from the media element. `muted` indicates
+     * that the volume for the media should be set to silent. This does not actually change
+     * the `volume` attribute.
+     *
+     * @method Html5#muted
+     * @return {boolean}
+     *         - True if the value of `volume` should be ignored and the audio set to silent.
+     *         - False if the value of `volume` should be used.
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
+     */
+    'muted',
+
+    /**
+     * Get the value of `defaultMuted` from the media element. `defaultMuted` indicates
+     * whether the media should start muted or not. Only changes the default state of the
+     * media. `muted` and `defaultMuted` can have different values. {@link Html5#muted} indicates the
+     * current state.
+     *
+     * @method Html5#defaultMuted
+     * @return {boolean}
+     *         - The value of `defaultMuted` from the media element.
+     *         - True indicates that the media should start muted.
+     *         - False indicates that the media should not start muted
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
+     */
+    'defaultMuted',
+
+    /**
+     * Get the value of `autoplay` from the media element. `autoplay` indicates
+     * that the media should start to playOld as soon as the page is ready.
+     *
+     * @method Html5#autoplay
+     * @return {boolean}
+     *         - The value of `autoplay` from the media element.
+     *         - True indicates that the media should start as soon as the page loads.
+     *         - False indicates that the media should not start as soon as the page loads.
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
+     */
+    'autoplay',
+
+    /**
+     * Get the value of `controls` from the media element. `controls` indicates
+     * whether the native media controls should be shown or hidden.
+     *
+     * @method Html5#controls
+     * @return {boolean}
+     *         - The value of `controls` from the media element.
+     *         - True indicates that native controls should be showing.
+     *         - False indicates that native controls should be hidden.
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-controls}
+     */
+    'controls',
+
+    /**
+     * Get the value of `loop` from the media element. `loop` indicates
+     * that the media should return to the start of the media and continue playing once
+     * it reaches the end.
+     *
+     * @method Html5#loop
+     * @return {boolean}
+     *         - The value of `loop` from the media element.
+     *         - True indicates that playback should seek back to start once
+     *           the end of a media is reached.
+     *         - False indicates that playback should not loop back to the start when the
+     *           end of the media is reached.
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
+     */
+    'loop',
+
+    /**
+     * Get the value of `playsinline` from the media element. `playsinline` indicates
+     * to the browser that non-fullscreen playback is preferred when fullscreen
+     * playback is the native default, such as in iOS Safari.
+     *
+     * @method Html5#playsinline
+     * @return {boolean}
+     *         - The value of `playsinline` from the media element.
+     *         - True indicates that the media should playOld inline.
+     *         - False indicates that the media should not playOld inline.
+     *
+     * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
+     */
+    'playsinline'].forEach(function (prop) {
+    Html5.prototype[prop] = function () {
+      return this.el_[prop] || this.el_.hasAttribute(prop);
+    };
+  });
+
+  // Wrap native boolean attributes with setters that set both property and attribute
+  // The list is as followed:
+  // setMuted, setDefaultMuted, setAutoplay, setLoop, setPlaysinline
+  // setControls is special-cased above
+  [
+    /**
+     * Set the value of `muted` on the media element. `muted` indicates that the current
+     * audio level should be silent.
+     *
+     * @method Html5#setMuted
+     * @param {boolean} muted
+     *        - True if the audio should be set to silent
+     *        - False otherwise
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
+     */
+    'muted',
+
+    /**
+     * Set the value of `defaultMuted` on the media element. `defaultMuted` indicates that the current
+     * audio level should be silent, but will only effect the muted level on intial playback..
+     *
+     * @method Html5.prototype.setDefaultMuted
+     * @param {boolean} defaultMuted
+     *        - True if the audio should be set to silent
+     *        - False otherwise
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
+     */
+    'defaultMuted',
+
+    /**
+     * Set the value of `autoplay` on the media element. `autoplay` indicates
+     * that the media should start to playOld as soon as the page is ready.
+     *
+     * @method Html5#setAutoplay
+     * @param {boolean} autoplay
+     *         - True indicates that the media should start as soon as the page loads.
+     *         - False indicates that the media should not start as soon as the page loads.
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
+     */
+    'autoplay',
+
+    /**
+     * Set the value of `loop` on the media element. `loop` indicates
+     * that the media should return to the start of the media and continue playing once
+     * it reaches the end.
+     *
+     * @method Html5#setLoop
+     * @param {boolean} loop
+     *         - True indicates that playback should seek back to start once
+     *           the end of a media is reached.
+     *         - False indicates that playback should not loop back to the start when the
+     *           end of the media is reached.
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
+     */
+    'loop',
+
+    /**
+     * Set the value of `playsinline` from the media element. `playsinline` indicates
+     * to the browser that non-fullscreen playback is preferred when fullscreen
+     * playback is the native default, such as in iOS Safari.
+     *
+     * @method Html5#setPlaysinline
+     * @param {boolean} playsinline
+     *         - True indicates that the media should playOld inline.
+     *         - False indicates that the media should not playOld inline.
+     *
+     * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
+     */
+    'playsinline'].forEach(function (prop) {
+    Html5.prototype['set' + toTitleCase(prop)] = function (v) {
+      this.el_[prop] = v;
+
+      if (v) {
+        this.el_.setAttribute(prop, prop);
+      } else {
+        this.el_.removeAttribute(prop);
+      }
+    };
+  });
+
+  // Wrap native properties with a getter
+  // The list is as followed
+  // paused, currentTime, buffered, volume, poster, preload, error, seeking
+  // seekable, ended, playbackRate, defaultPlaybackRate, played, networkState
+  // readyState, videoWidth, videoHeight
+  [
+    /**
+     * Get the value of `paused` from the media element. `paused` indicates whether the media element
+     * is currently paused or not.
+     *
+     * @method Html5#paused
+     * @return {boolean}
+     *         The value of `paused` from the media element.
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-paused}
+     */
+    'paused',
+
+    /**
+     * Get the value of `currentTime` from the media element. `currentTime` indicates
+     * the current second that the media is at in playback.
+     *
+     * @method Html5#currentTime
+     * @return {number}
+     *         The value of `currentTime` from the media element.
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-currenttime}
+     */
+    'currentTime',
+
+    /**
+     * Get the value of `buffered` from the media element. `buffered` is a `TimeRange`
+     * object that represents the parts of the media that are already downloaded and
+     * available for playback.
+     *
+     * @method Html5#buffered
+     * @return {TimeRange}
+     *         The value of `buffered` from the media element.
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-buffered}
+     */
+    'buffered',
+
+    /**
+     * Get the value of `volume` from the media element. `volume` indicates
+     * the current playback volume of audio for a media. `volume` will be a value from 0
+     * (silent) to 1 (loudest and default).
+     *
+     * @method Html5#volume
+     * @return {number}
+     *         The value of `volume` from the media element. Value will be between 0-1.
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
+     */
+    'volume',
+
+    /**
+     * Get the value of `poster` from the media element. `poster` indicates
+     * that the url of an image file that can/will be shown when no media data is available.
+     *
+     * @method Html5#poster
+     * @return {string}
+     *         The value of `poster` from the media element. Value will be a url to an
+     *         image.
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-video-poster}
+     */
+    'poster',
+
+    /**
+     * Get the value of `preload` from the media element. `preload` indicates
+     * what should download before the media is interacted with. It can have the following
+     * values:
+     * - none: nothing should be downloaded
+     * - metadata: poster and the first few frames of the media may be downloaded to get
+     *   media dimensions and other metadata
+     * - auto: allow the media and metadata for the media to be downloaded before
+     *    interaction
+     *
+     * @method Html5#preload
+     * @return {string}
+     *         The value of `preload` from the media element. Will be 'none', 'metadata',
+     *         or 'auto'.
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
+     */
+    'preload',
+
+    /**
+     * Get the value of the `error` from the media element. `error` indicates any
+     * MediaError that may have occurred during playback. If error returns null there is no
+     * current error.
+     *
+     * @method Html5#error
+     * @return {MediaError|null}
+     *         The value of `error` from the media element. Will be `MediaError` if there
+     *         is a current error and null otherwise.
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-error}
+     */
+    'error',
+
+    /**
+     * Get the value of `seeking` from the media element. `seeking` indicates whether the
+     * media is currently seeking to a new position or not.
+     *
+     * @method Html5#seeking
+     * @return {boolean}
+     *         - The value of `seeking` from the media element.
+     *         - True indicates that the media is currently seeking to a new position.
+     *         - False indicates that the media is not seeking to a new position at this time.
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seeking}
+     */
+    'seeking',
+
+    /**
+     * Get the value of `seekable` from the media element. `seekable` returns a
+     * `TimeRange` object indicating ranges of time that can currently be `seeked` to.
+     *
+     * @method Html5#seekable
+     * @return {TimeRange}
+     *         The value of `seekable` from the media element. A `TimeRange` object
+     *         indicating the current ranges of time that can be seeked to.
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seekable}
+     */
+    'seekable',
+
+    /**
+     * Get the value of `ended` from the media element. `ended` indicates whether
+     * the media has reached the end or not.
+     *
+     * @method Html5#ended
+     * @return {boolean}
+     *         - The value of `ended` from the media element.
+     *         - True indicates that the media has ended.
+     *         - False indicates that the media has not ended.
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-ended}
+     */
+    'ended',
+
+    /**
+     * Get the value of `playbackRate` from the media element. `playbackRate` indicates
+     * the rate at which the media is currently playing back. Examples:
+     *   - if playbackRate is set to 2, media will playOld twice as fast.
+     *   - if playbackRate is set to 0.5, media will playOld half as fast.
+     *
+     * @method Html5#playbackRate
+     * @return {number}
+     *         The value of `playbackRate` from the media element. A number indicating
+     *         the current playback speed of the media, where 1 is normal speed.
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
+     */
+    'playbackRate',
+
+    /**
+     * Get the value of `defaultPlaybackRate` from the media element. `defaultPlaybackRate` indicates
+     * the rate at which the media is currently playing back. This value will not indicate the current
+     * `playbackRate` after playback has started, use {@link Html5#playbackRate} for that.
+     *
+     * Examples:
+     *   - if defaultPlaybackRate is set to 2, media will playOld twice as fast.
+     *   - if defaultPlaybackRate is set to 0.5, media will playOld half as fast.
+     *
+     * @method Html5.prototype.defaultPlaybackRate
+     * @return {number}
+     *         The value of `defaultPlaybackRate` from the media element. A number indicating
+     *         the current playback speed of the media, where 1 is normal speed.
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
+     */
+    'defaultPlaybackRate',
+
+    /**
+     * Get the value of `played` from the media element. `played` returns a `TimeRange`
+     * object representing points in the media timeline that have been played.
+     *
+     * @method Html5#played
+     * @return {TimeRange}
+     *         The value of `played` from the media element. A `TimeRange` object indicating
+     *         the ranges of time that have been played.
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-played}
+     */
+    'played',
+
+    /**
+     * Get the value of `networkState` from the media element. `networkState` indicates
+     * the current network state. It returns an enumeration from the following list:
+     * - 0: NETWORK_EMPTY
+     * - 1: NETWORK_IDLE
+     * - 2: NETWORK_LOADING
+     * - 3: NETWORK_NO_SOURCE
+     *
+     * @method Html5#networkState
+     * @return {number}
+     *         The value of `networkState` from the media element. This will be a number
+     *         from the list in the description.
+     *
+     * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-networkstate}
+     */
+    'networkState',
+
+    /**
+     * Get the value of `readyState` from the media element. `readyState` indicates
+     * the current state of the media element. It returns an enumeration from the
+     * following list:
+     * - 0: HAVE_NOTHING
+     * - 1: HAVE_METADATA
+     * - 2: HAVE_CURRENT_DATA
+     * - 3: HAVE_FUTURE_DATA
+     * - 4: HAVE_ENOUGH_DATA
+     *
+     * @method Html5#readyState
+     * @return {number}
+     *         The value of `readyState` from the media element. This will be a number
+     *         from the list in the description.
+     *
+     * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#ready-states}
+     */
+    'readyState',
+
+    /**
+     * Get the value of `videoWidth` from the video element. `videoWidth` indicates
+     * the current width of the video in css pixels.
+     *
+     * @method Html5#videoWidth
+     * @return {number}
+     *         The value of `videoWidth` from the video element. This will be a number
+     *         in css pixels.
+     *
+     * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
+     */
+    'videoWidth',
+
+    /**
+     * Get the value of `videoHeight` from the video element. `videoHeight` indicates
+     * the current height of the video in css pixels.
+     *
+     * @method Html5#videoHeight
+     * @return {number}
+     *         The value of `videoHeight` from the video element. This will be a number
+     *         in css pixels.
+     *
+     * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
+     */
+    'videoHeight'].forEach(function (prop) {
+    Html5.prototype[prop] = function () {
+      return this.el_[prop];
+    };
+  });
+
+  // Wrap native properties with a setter in this format:
+  // set + toTitleCase(name)
+  // The list is as follows:
+  // setVolume, setSrc, setPoster, setPreload, setPlaybackRate, setDefaultPlaybackRate
+  [
+    /**
+     * Set the value of `volume` on the media element. `volume` indicates the current
+     * audio level as a percentage in decimal form. This means that 1 is 100%, 0.5 is 50%, and
+     * so on.
+     *
+     * @method Html5#setVolume
+     * @param {number} percentAsDecimal
+     *        The volume percent as a decimal. Valid range is from 0-1.
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
+     */
+    'volume',
+
+    /**
+     * Set the value of `src` on the media element. `src` indicates the current
+     * {@link Tech~SourceObject} for the media.
+     *
+     * @method Html5#setSrc
+     * @param {Tech~SourceObject} src
+     *        The source object to set as the current source.
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-src}
+     */
+    'src',
+
+    /**
+     * Set the value of `poster` on the media element. `poster` is the url to
+     * an image file that can/will be shown when no media data is available.
+     *
+     * @method Html5#setPoster
+     * @param {string} poster
+     *        The url to an image that should be used as the `poster` for the media
+     *        element.
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-poster}
+     */
+    'poster',
+
+    /**
+     * Set the value of `preload` on the media element. `preload` indicates
+     * what should download before the media is interacted with. It can have the following
+     * values:
+     * - none: nothing should be downloaded
+     * - metadata: poster and the first few frames of the media may be downloaded to get
+     *   media dimensions and other metadata
+     * - auto: allow the media and metadata for the media to be downloaded before
+     *    interaction
+     *
+     * @method Html5#setPreload
+     * @param {string} preload
+     *         The value of `preload` to set on the media element. Must be 'none', 'metadata',
+     *         or 'auto'.
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
+     */
+    'preload',
+
+    /**
+     * Set the value of `playbackRate` on the media element. `playbackRate` indicates
+     * the rate at which the media should playOld back. Examples:
+     *   - if playbackRate is set to 2, media will playOld twice as fast.
+     *   - if playbackRate is set to 0.5, media will playOld half as fast.
+     *
+     * @method Html5#setPlaybackRate
+     * @return {number}
+     *         The value of `playbackRate` from the media element. A number indicating
+     *         the current playback speed of the media, where 1 is normal speed.
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
+     */
+    'playbackRate',
+
+    /**
+     * Set the value of `defaultPlaybackRate` on the media element. `defaultPlaybackRate` indicates
+     * the rate at which the media should playOld back upon initial startup. Changing this value
+     * after a video has started will do nothing. Instead you should used {@link Html5#setPlaybackRate}.
+     *
+     * Example Values:
+     *   - if playbackRate is set to 2, media will playOld twice as fast.
+     *   - if playbackRate is set to 0.5, media will playOld half as fast.
+     *
+     * @method Html5.prototype.setDefaultPlaybackRate
+     * @return {number}
+     *         The value of `defaultPlaybackRate` from the media element. A number indicating
+     *         the current playback speed of the media, where 1 is normal speed.
+     *
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultplaybackrate}
+     */
+    'defaultPlaybackRate'].forEach(function (prop) {
+    Html5.prototype['set' + toTitleCase(prop)] = function (v) {
+      this.el_[prop] = v;
+    };
+  });
+
+  // wrap native functions with a function
+  // The list is as follows:
+  // pause, load, playOld
+  [
+    /**
+     * A wrapper around the media elements `pause` function. This will call the `HTML5`
+     * media elements `pause` function.
+     *
+     * @method Html5#pause
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-pause}
+     */
+    'pause',
+
+    /**
+     * A wrapper around the media elements `load` function. This will call the `HTML5`s
+     * media element `load` function.
+     *
+     * @method Html5#load
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-load}
+     */
+    'load',
+
+    /**
+     * A wrapper around the media elements `playOld` function. This will call the `HTML5`s
+     * media element `playOld` function.
+     *
+     * @method Html5#playOld
+     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-play}
+     */
+    'play'].forEach(function (prop) {
+    Html5.prototype[prop] = function () {
+      return this.el_[prop]();
+    };
+  });
+
+  Tech.withSourceHandlers(Html5);
+
+  /**
+   * Native source handler for Html5, simply passes the source to the media element.
+   *
+   * @property {Tech~SourceObject} source
+   *        The source object
+   *
+   * @property {Html5} tech
+   *        The instance of the HTML5 tech.
+   */
+  Html5.nativeSourceHandler = {};
+
+  /**
+   * Check if the media element can playOld the given mime type.
+   *
+   * @param {string} type
+   *        The mimetype to check
+   *
+   * @return {string}
+   *         'probably', 'maybe', or '' (empty string)
+   */
+  Html5.nativeSourceHandler.canPlayType = function (type) {
+    // IE without MediaPlayer throws an error (#519)
+    try {
+      return Html5.TEST_VID.canPlayType(type);
+    } catch (e) {
+      return '';
+    }
+  };
+
+  /**
+   * Check if the media element can handle a source natively.
+   *
+   * @param {Tech~SourceObject} source
+   *         The source object
+   *
+   * @param {Object} [options]
+   *         Options to be passed to the tech.
+   *
+   * @return {string}
+   *         'probably', 'maybe', or '' (empty string).
+   */
+  Html5.nativeSourceHandler.canHandleSource = function (source, options) {
+
+    // If a type was provided we should rely on that
+    if (source.type) {
+      return Html5.nativeSourceHandler.canPlayType(source.type);
+
+      // If no type, fall back to checking 'video/[EXTENSION]'
+    } else if (source.src) {
+      var ext = getFileExtension(source.src);
+
+      return Html5.nativeSourceHandler.canPlayType('video/' + ext);
+    }
+
+    return '';
+  };
+
+  /**
+   * Pass the source to the native media element.
+   *
+   * @param {Tech~SourceObject} source
+   *        The source object
+   *
+   * @param {Html5} tech
+   *        The instance of the Html5 tech
+   *
+   * @param {Object} [options]
+   *        The options to pass to the source
+   */
+  Html5.nativeSourceHandler.handleSource = function (source, tech, options) {
+    tech.setSrc(source.src);
+  };
+
+  /**
+   * A noop for the native dispose function, as cleanup is not needed.
+   */
+  Html5.nativeSourceHandler.dispose = function () {
+  };
+
+  // Register the native source handler
+  Html5.registerSourceHandler(Html5.nativeSourceHandler);
+
+  Tech.registerTech('Html5', Html5);
+
+  var _templateObject$2 = taggedTemplateLiteralLoose(['\n        Using the tech directly can be dangerous. I hope you know what you\'re doing.\n        See https://github.com/videojs/video.js/issues/2617 for more info.\n      '], ['\n        Using the tech directly can be dangerous. I hope you know what you\'re doing.\n        See https://github.com/videojs/video.js/issues/2617 for more info.\n      ']);
+
+  // The following tech events are simply re-triggered
+  // on the player when they happen
+  var TECH_EVENTS_RETRIGGER = [
+    /**
+     * Fired while the user agent is downloading media data.
+     *
+     * @event Player#progress
+     * @type {EventTarget~Event}
+     */
+    /**
+     * Retrigger the `progress` event that was triggered by the {@link Tech}.
+     *
+     * @private
+     * @method Player#handleTechProgress_
+     * @fires Player#progress
+     * @listens Tech#progress
+     */
+    'progress',
+
+    /**
+     * Fires when the loading of an audio/video is aborted.
+     *
+     * @event Player#abort
+     * @type {EventTarget~Event}
+     */
+    /**
+     * Retrigger the `abort` event that was triggered by the {@link Tech}.
+     *
+     * @private
+     * @method Player#handleTechAbort_
+     * @fires Player#abort
+     * @listens Tech#abort
+     */
+    'abort',
+
+    /**
+     * Fires when the browser is intentionally not getting media data.
+     *
+     * @event Player#suspend
+     * @type {EventTarget~Event}
+     */
+    /**
+     * Retrigger the `suspend` event that was triggered by the {@link Tech}.
+     *
+     * @private
+     * @method Player#handleTechSuspend_
+     * @fires Player#suspend
+     * @listens Tech#suspend
+     */
+    'suspend',
+
+    /**
+     * Fires when the current playlist is empty.
+     *
+     * @event Player#emptied
+     * @type {EventTarget~Event}
+     */
+    /**
+     * Retrigger the `emptied` event that was triggered by the {@link Tech}.
+     *
+     * @private
+     * @method Player#handleTechEmptied_
+     * @fires Player#emptied
+     * @listens Tech#emptied
+     */
+    'emptied',
+    /**
+     * Fires when the browser is trying to get media data, but data is not available.
+     *
+     * @event Player#stalled
+     * @type {EventTarget~Event}
+     */
+    /**
+     * Retrigger the `stalled` event that was triggered by the {@link Tech}.
+     *
+     * @private
+     * @method Player#handleTechStalled_
+     * @fires Player#stalled
+     * @listens Tech#stalled
+     */
+    'stalled',
+
+    /**
+     * Fires when the browser has loaded meta data for the audio/video.
+     *
+     * @event Player#loadedmetadata
+     * @type {EventTarget~Event}
+     */
+    /**
+     * Retrigger the `stalled` event that was triggered by the {@link Tech}.
+     *
+     * @private
+     * @method Player#handleTechLoadedmetadata_
+     * @fires Player#loadedmetadata
+     * @listens Tech#loadedmetadata
+     */
+    'loadedmetadata',
+
+    /**
+     * Fires when the browser has loaded the current frame of the audio/video.
+     *
+     * @event Player#loadeddata
+     * @type {event}
+     */
+    /**
+     * Retrigger the `loadeddata` event that was triggered by the {@link Tech}.
+     *
+     * @private
+     * @method Player#handleTechLoaddeddata_
+     * @fires Player#loadeddata
+     * @listens Tech#loadeddata
+     */
+    'loadeddata',
+
+    /**
+     * Fires when the current playback position has changed.
+     *
+     * @event Player#timeupdate
+     * @type {event}
+     */
+    /**
+     * Retrigger the `timeupdate` event that was triggered by the {@link Tech}.
+     *
+     * @private
+     * @method Player#handleTechTimeUpdate_
+     * @fires Player#timeupdate
+     * @listens Tech#timeupdate
+     */
+    'timeupdate',
+
+    /**
+     * Fires when the video's intrinsic dimensions change
+     *
+     * @event Player#resize
+     * @type {event}
+     */
+    /**
+     * Retrigger the `resize` event that was triggered by the {@link Tech}.
+     *
+     * @private
+     * @method Player#handleTechResize_
+     * @fires Player#resize
+     * @listens Tech#resize
+     */
+    'resize',
+
+    /**
+     * Fires when the volume has been changed
+     *
+     * @event Player#volumechange
+     * @type {event}
+     */
+    /**
+     * Retrigger the `volumechange` event that was triggered by the {@link Tech}.
+     *
+     * @private
+     * @method Player#handleTechVolumechange_
+     * @fires Player#volumechange
+     * @listens Tech#volumechange
+     */
+    'volumechange',
+
+    /**
+     * Fires when the text track has been changed
+     *
+     * @event Player#texttrackchange
+     * @type {event}
+     */
+    /**
+     * Retrigger the `texttrackchange` event that was triggered by the {@link Tech}.
+     *
+     * @private
+     * @method Player#handleTechTexttrackchange_
+     * @fires Player#texttrackchange
+     * @listens Tech#texttrackchange
+     */
+    'texttrackchange'];
+
+  // events to queue when playback rate is zero
+  // this is a hash for the sole purpose of mapping non-camel-cased event names
+  // to camel-cased function names
+  var TECH_EVENTS_QUEUE = {
+    canplay: 'CanPlay',
+    canplaythrough: 'CanPlayThrough',
+    playing: 'Playing',
+    seeked: 'Seeked'
+  };
+
+  /**
+   * An instance of the `Player` class is created when any of the Video.js setup methods
+   * are used to initialize a video.
+   *
+   * After an instance has been created it can be accessed globally in two ways:
+   * 1. By calling `videojs('example_video_1');`
+   * 2. By using it directly via  `videojs.players.example_video_1;`
+   *
+   * @extends Component
+   */
+
+  var Player = function (_Component) {
+    inherits(Player, _Component);
+
+    /**
+     * Create an instance of this class.
+     *
+     * @param {Element} tag
+     *        The original video DOM element used for configuring options.
+     *
+     * @param {Object} [options]
+     *        Object of option names and values.
+     *
+     * @param {Component~ReadyCallback} [ready]
+     *        Ready callback function.
+     */
+    function Player(tag, options, ready) {
+      classCallCheck(this, Player);
+
+      // Make sure tag ID exists
+      tag.id = tag.id || options.id || 'vjs_video_' + newGUID();
+
+      // Set Options
+      // The options argument overrides options set in the video tag
+      // which overrides globally set options.
+      // This latter part coincides with the load order
+      // (tag must exist before Player)
+      options = assign(Player.getTagSettings(tag), options);
+
+      // Delay the initialization of children because we need to set up
+      // player properties first, and can't use `this` before `super()`
+      options.initChildren = false;
+
+      // Same with creating the element
+      options.createEl = false;
+
+      // don't auto mixin the evented mixin
+      options.evented = false;
+
+      // we don't want the player to report touch activity on itself
+      // see enableTouchActivity in Component
+      options.reportTouchActivity = false;
+
+      // If language is not set, get the closest lang attribute
+      if (!options.language) {
+        if (typeof tag.closest === 'function') {
+          var closest = tag.closest('[lang]');
+
+          if (closest && closest.getAttribute) {
+            options.language = closest.getAttribute('lang');
+          }
+        } else {
+          var element = tag;
+
+          while (element && element.nodeType === 1) {
+            if (getAttributes(element).hasOwnProperty('lang')) {
+              options.language = element.getAttribute('lang');
+              break;
+            }
+            element = element.parentNode;
+          }
+        }
+      }
+
+      // Run base component initializing with new options
+
+      // Tracks when a tech changes the poster
+      var _this = possibleConstructorReturn(this, _Component.call(this, null, options, ready));
+
+      _this.isPosterFromTech_ = false;
+
+      // Holds callback info that gets queued when playback rate is zero
+      // and a seek is happening
+      _this.queuedCallbacks_ = [];
+
+      // Turn off API access because we're loading a new tech that might load asynchronously
+      _this.isReady_ = false;
+
+      // Init state hasStarted_
+      _this.hasStarted_ = false;
+
+      // Init state userActive_
+      _this.userActive_ = false;
+
+      // if the global option object was accidentally blown away by
+      // someone, bail early with an informative error
+      if (!_this.options_ || !_this.options_.techOrder || !_this.options_.techOrder.length) {
+        throw new Error('No techOrder specified. Did you overwrite ' + 'videojs.options instead of just changing the ' + 'properties you want to override?');
+      }
+
+      // Store the original tag used to set options
+      _this.tag = tag;
+
+      // Store the tag attributes used to restore html5 element
+      _this.tagAttributes = tag && getAttributes(tag);
+
+      // Update current language
+      _this.language(_this.options_.language);
+
+      // Update Supported Languages
+      if (options.languages) {
+        // Normalise player option languages to lowercase
+        var languagesToLower = {};
+
+        Object.getOwnPropertyNames(options.languages).forEach(function (name$$1) {
+          languagesToLower[name$$1.toLowerCase()] = options.languages[name$$1];
+        });
+        _this.languages_ = languagesToLower;
+      } else {
+        _this.languages_ = Player.prototype.options_.languages;
+      }
+
+      // Cache for video property values.
+      _this.cache_ = {};
+
+      // Set poster
+      _this.poster_ = options.poster || '';
+
+      // Set controls
+      _this.controls_ = !!options.controls;
+
+      // Set default values for lastVolume
+      _this.cache_.lastVolume = 1;
+
+      // Original tag settings stored in options
+      // now remove immediately so native controls don't flash.
+      // May be turned back on by HTML5 tech if nativeControlsForTouch is true
+      tag.controls = false;
+      tag.removeAttribute('controls');
+
+      /*
+       * Store the internal state of scrubbing
+       *
+       * @private
+       * @return {Boolean} True if the user is scrubbing
+       */
+      _this.scrubbing_ = false;
+
+      _this.el_ = _this.createEl();
+
+      // Set default value for lastPlaybackRate
+      _this.cache_.lastPlaybackRate = _this.defaultPlaybackRate();
+
+      // Make this an evented object and use `el_` as its event bus.
+      evented(_this, {eventBusKey: 'el_'});
+
+      // We also want to pass the original player options to each component and plugin
+      // as well so they don't need to reach back into the player for options later.
+      // We also need to do another copy of this.options_ so we don't end up with
+      // an infinite loop.
+      var playerOptionsCopy = mergeOptions(_this.options_);
+
+      // Load plugins
+      if (options.plugins) {
+        var plugins = options.plugins;
+
+        Object.keys(plugins).forEach(function (name$$1) {
+          if (typeof this[name$$1] === 'function') {
+            this[name$$1](plugins[name$$1]);
+          } else {
+            throw new Error('plugin "' + name$$1 + '" does not exist');
+          }
+        }, _this);
+      }
+
+      _this.options_.playerOptions = playerOptionsCopy;
+
+      _this.middleware_ = [];
+
+      _this.initChildren();
+
+      // Set isAudio based on whether or not an audio tag was used
+      _this.isAudio(tag.nodeName.toLowerCase() === 'audio');
+
+      // Update controls className. Can't do this when the controls are initially
+      // set because the element doesn't exist yet.
+      if (_this.controls()) {
+        _this.addClass('vjs-controls-enabled');
+      } else {
+        _this.addClass('vjs-controls-disabled');
+      }
+
+      // Set ARIA label and region role depending on player type
+      _this.el_.setAttribute('role', 'region');
+      if (_this.isAudio()) {
+        _this.el_.setAttribute('aria-label', _this.localize('Audio Player'));
+      } else {
+        _this.el_.setAttribute('aria-label', _this.localize('Video Player'));
+      }
+
+      if (_this.isAudio()) {
+        _this.addClass('vjs-audio');
+      }
+
+      if (_this.flexNotSupported_()) {
+        _this.addClass('vjs-no-flex');
+      }
+
+      // TODO: Make this smarter. Toggle user state between touching/mousing
+      // using events, since devices can have both touch and mouse events.
+      // if (browser.TOUCH_ENABLED) {
+      //   this.addClass('vjs-touch-enabled');
+      // }
+
+      // iOS Safari has broken hover handling
+      if (!IS_IOS) {
+        _this.addClass('vjs-workinghover');
+      }
+
+      // Make player easily findable by ID
+      Player.players[_this.id_] = _this;
+
+      // Add a major version class to aid css in plugins
+      var majorVersion = version.split('.')[0];
+
+      _this.addClass('vjs-v' + majorVersion);
+
+      // When the player is first initialized, trigger activity so components
+      // like the control bar show themselves if needed
+      _this.userActive(true);
+      _this.reportUserActivity();
+
+      _this.one('play', _this.listenForUserActivity_);
+      _this.on('fullscreenchange', _this.handleFullscreenChange_);
+      _this.on('stageclick', _this.handleStageClick_);
+
+      _this.changingSrc_ = false;
+      _this.playWaitingForReady_ = false;
+      _this.playOnLoadstart_ = null;
+      return _this;
+    }
+
+    /**
+     * Destroys the video player and does any necessary cleanup.
+     *
+     * This is especially helpful if you are dynamically adding and removing videos
+     * to/from the DOM.
+     *
+     * @fires Player#dispose
+     */
+
+
+    Player.prototype.dispose = function dispose() {
+      /**
+       * Called when the player is being disposed of.
+       *
+       * @event Player#dispose
+       * @type {EventTarget~Event}
+       */
+      this.trigger('dispose');
+      // prevent dispose from being called twice
+      this.off('dispose');
+
+      if (this.styleEl_ && this.styleEl_.parentNode) {
+        this.styleEl_.parentNode.removeChild(this.styleEl_);
+        this.styleEl_ = null;
+      }
+
+      // Kill reference to this player
+      Player.players[this.id_] = null;
+
+      if (this.tag && this.tag.player) {
+        this.tag.player = null;
+      }
+
+      if (this.el_ && this.el_.player) {
+        this.el_.player = null;
+      }
+
+      if (this.tech_) {
+        this.tech_.dispose();
+        this.isPosterFromTech_ = false;
+        this.poster_ = '';
+      }
+
+      if (this.playerElIngest_) {
+        this.playerElIngest_ = null;
+      }
+
+      if (this.tag) {
+        this.tag = null;
+      }
+
+      clearCacheForPlayer(this);
+
+      // the actual .el_ is removed here
+      _Component.prototype.dispose.call(this);
+    };
+
+    /**
+     * Create the `Player`'s DOM element.
+     *
+     * @return {Element}
+     *         The DOM element that gets created.
+     */
+
+
+    Player.prototype.createEl = function createEl$$1() {
+      var tag = this.tag;
+      var el = void 0;
+      var playerElIngest = this.playerElIngest_ = tag.parentNode && tag.parentNode.hasAttribute && tag.parentNode.hasAttribute('data-vjs-player');
+      var divEmbed = this.tag.tagName.toLowerCase() === 'video-js';
+
+      if (playerElIngest) {
+        el = this.el_ = tag.parentNode;
+      } else if (!divEmbed) {
+        el = this.el_ = _Component.prototype.createEl.call(this, 'div');
+      }
+
+      // Copy over all the attributes from the tag, including ID and class
+      // ID will now reference player box, not the video tag
+      var attrs = getAttributes(tag);
+
+      if (divEmbed) {
+        el = this.el_ = tag;
+        tag = this.tag = document_1.createElement('video');
+        while (el.children.length) {
+          tag.appendChild(el.firstChild);
+        }
+
+        if (!hasClass(el, 'video-js')) {
+          addClass(el, 'video-js');
+        }
+
+        el.appendChild(tag);
+
+        playerElIngest = this.playerElIngest_ = el;
+        // move properties over from our custom `video-js` element
+        // to our new `video` element. This will move things like
+        // `src` or `controls` that were set via js before the player
+        // was initialized.
+        Object.keys(el).forEach(function (k) {
+          tag[k] = el[k];
+        });
+      }
+
+      // set tabindex to -1 so we could focus on the player element
+      tag.setAttribute('tabindex', '-1');
+
+      // Remove width/height attrs from tag so CSS can make it 100% width/height
+      tag.removeAttribute('width');
+      tag.removeAttribute('height');
+
+      Object.getOwnPropertyNames(attrs).forEach(function (attr) {
+        el.setAttribute(attr, attrs[attr]);
+
+        if (divEmbed) {
+          tag.setAttribute(attr, attrs[attr]);
+        }
+      });
+
+      // Update tag id/class for use as HTML5 playback tech
+      // Might think we should do this after embedding in container so .vjs-tech class
+      // doesn't flash 100% width/height, but class only applies with .video-js parent
+      tag.playerId = tag.id;
+      tag.id += '_html5_api';
+      tag.className = 'vjs-tech';
+
+      // Make player findable on elements
+      tag.player = el.player = this;
+      // Default state of video is paused
+      this.addClass('vjs-paused');
+
+      // Add a style element in the player that we'll use to set the width/height
+      // of the player in a way that's still overrideable by CSS, just like the
+      // video element
+      if (window_1.VIDEOJS_NO_DYNAMIC_STYLE !== true) {
+        this.styleEl_ = createStyleElement('vjs-styles-dimensions');
+        var defaultsStyleEl = $('.vjs-styles-defaults');
+        var head = $('head');
+
+        head.insertBefore(this.styleEl_, defaultsStyleEl ? defaultsStyleEl.nextSibling : head.firstChild);
+      }
+
+      // Pass in the width/height/aspectRatio options which will update the style el
+      this.width(this.options_.width);
+      this.height(this.options_.height);
+      this.fluid(this.options_.fluid);
+      this.aspectRatio(this.options_.aspectRatio);
+
+      // Hide any links within the video/audio tag,
+      // because IE doesn't hide them completely from screen readers.
+      var links = tag.getElementsByTagName('a');
+
+      for (var i = 0; i < links.length; i++) {
+        var linkEl = links.item(i);
+
+        addClass(linkEl, 'vjs-hidden');
+        linkEl.setAttribute('hidden', 'hidden');
+      }
+
+      // insertElFirst seems to cause the networkState to flicker from 3 to 2, so
+      // keep track of the original for later so we can know if the source originally failed
+      tag.initNetworkState_ = tag.networkState;
+
+      // Wrap video tag in div (el/box) container
+      if (tag.parentNode && !playerElIngest) {
+        tag.parentNode.insertBefore(el, tag);
+      }
+
+      // insert the tag as the first child of the player element
+      // then manually add it to the children array so that this.addChild
+      // will work properly for other components
+      //
+      // Breaks iPhone, fixed in HTML5 setup.
+      prependTo(tag, el);
+      this.children_.unshift(tag);
+
+      // Set lang attr on player to ensure CSS :lang() in consistent with player
+      // if it's been set to something different to the doc
+      this.el_.setAttribute('lang', this.language_);
+
+      this.el_ = el;
+
+      return el;
+    };
+
+    /**
+     * A getter/setter for the `Player`'s width. Returns the player's configured value.
+     * To get the current width use `currentWidth()`.
+     *
+     * @param {number} [value]
+     *        The value to set the `Player`'s width to.
+     *
+     * @return {number}
+     *         The current width of the `Player` when getting.
+     */
+
+
+    Player.prototype.width = function width(value) {
+      return this.dimension('width', value);
+    };
+
+    /**
+     * A getter/setter for the `Player`'s height. Returns the player's configured value.
+     * To get the current height use `currentheight()`.
+     *
+     * @param {number} [value]
+     *        The value to set the `Player`'s heigth to.
+     *
+     * @return {number}
+     *         The current height of the `Player` when getting.
+     */
+
+
+    Player.prototype.height = function height(value) {
+      return this.dimension('height', value);
+    };
+
+    /**
+     * A getter/setter for the `Player`'s width & height.
+     *
+     * @param {string} dimension
+     *        This string can be:
+     *        - 'width'
+     *        - 'height'
+     *
+     * @param {number} [value]
+     *        Value for dimension specified in the first argument.
+     *
+     * @return {number}
+     *         The dimension arguments value when getting (width/height).
+     */
+
+
+    Player.prototype.dimension = function dimension(_dimension, value) {
+      var privDimension = _dimension + '_';
+
+      if (value === undefined) {
+        return this[privDimension] || 0;
+      }
+
+      if (value === '') {
+        // If an empty string is given, reset the dimension to be automatic
+        this[privDimension] = undefined;
+        this.updateStyleEl_();
+        return;
+      }
+
+      var parsedVal = parseFloat(value);
+
+      if (isNaN(parsedVal)) {
+        log$1.error('Improper value "' + value + '" supplied for for ' + _dimension);
+        return;
+      }
+
+      this[privDimension] = parsedVal;
+      this.updateStyleEl_();
+    };
+
+    /**
+     * A getter/setter/toggler for the vjs-fluid `className` on the `Player`.
+     *
+     * @param {boolean} [bool]
+     *        - A value of true adds the class.
+     *        - A value of false removes the class.
+     *        - No value will toggle the fluid class.
+     *
+     * @return {boolean|undefined}
+     *         - The value of fluid when getting.
+     *         - `undefined` when setting.
+     */
+
+
+    Player.prototype.fluid = function fluid(bool) {
+      if (bool === undefined) {
+        return !!this.fluid_;
+      }
+
+      this.fluid_ = !!bool;
+
+      if (bool) {
+        this.addClass('vjs-fluid');
+      } else {
+        this.removeClass('vjs-fluid');
+      }
+
+      this.updateStyleEl_();
+    };
+
+    /**
+     * Get/Set the aspect ratio
+     *
+     * @param {string} [ratio]
+     *        Aspect ratio for player
+     *
+     * @return {string|undefined}
+     *         returns the current aspect ratio when getting
+     */
+
+    /**
+     * A getter/setter for the `Player`'s aspect ratio.
+     *
+     * @param {string} [ratio]
+     *        The value to set the `Player's aspect ratio to.
+     *
+     * @return {string|undefined}
+     *         - The current aspect ratio of the `Player` when getting.
+     *         - undefined when setting
+     */
+
+
+    Player.prototype.aspectRatio = function aspectRatio(ratio) {
+      if (ratio === undefined) {
+        return this.aspectRatio_;
+      }
+
+      // Check for width:height format
+      if (!/^\d+\:\d+$/.test(ratio)) {
+        throw new Error('Improper value supplied for aspect ratio. The format should be width:height, for example 16:9.');
+      }
+      this.aspectRatio_ = ratio;
+
+      // We're assuming if you set an aspect ratio you want fluid mode,
+      // because in fixed mode you could calculate width and height yourself.
+      this.fluid(true);
+
+      this.updateStyleEl_();
+    };
+
+    /**
+     * Update styles of the `Player` element (height, width and aspect ratio).
+     *
+     * @private
+     * @listens Tech#loadedmetadata
+     */
+
+
+    Player.prototype.updateStyleEl_ = function updateStyleEl_() {
+      if (window_1.VIDEOJS_NO_DYNAMIC_STYLE === true) {
+        var _width = typeof this.width_ === 'number' ? this.width_ : this.options_.width;
+        var _height = typeof this.height_ === 'number' ? this.height_ : this.options_.height;
+        var techEl = this.tech_ && this.tech_.el();
+
+        if (techEl) {
+          if (_width >= 0) {
+            techEl.width = _width;
+          }
+          if (_height >= 0) {
+            techEl.height = _height;
+          }
+        }
+
+        return;
+      }
+
+      var width = void 0;
+      var height = void 0;
+      var aspectRatio = void 0;
+      var idClass = void 0;
+
+      // The aspect ratio is either used directly or to calculate width and height.
+      if (this.aspectRatio_ !== undefined && this.aspectRatio_ !== 'auto') {
+        // Use any aspectRatio that's been specifically set
+        aspectRatio = this.aspectRatio_;
+      } else if (this.videoWidth() > 0) {
+        // Otherwise try to get the aspect ratio from the video metadata
+        aspectRatio = this.videoWidth() + ':' + this.videoHeight();
+      } else {
+        // Or use a default. The video element's is 2:1, but 16:9 is more common.
+        aspectRatio = '16:9';
+      }
+
+      // Get the ratio as a decimal we can use to calculate dimensions
+      var ratioParts = aspectRatio.split(':');
+      var ratioMultiplier = ratioParts[1] / ratioParts[0];
+
+      if (this.width_ !== undefined) {
+        // Use any width that's been specifically set
+        width = this.width_;
+      } else if (this.height_ !== undefined) {
+        // Or calulate the width from the aspect ratio if a height has been set
+        width = this.height_ / ratioMultiplier;
+      } else {
+        // Or use the video's metadata, or use the video el's default of 300
+        width = this.videoWidth() || 300;
+      }
+
+      if (this.height_ !== undefined) {
+        // Use any height that's been specifically set
+        height = this.height_;
+      } else {
+        // Otherwise calculate the height from the ratio and the width
+        height = width * ratioMultiplier;
+      }
+
+      // Ensure the CSS class is valid by starting with an alpha character
+      if (/^[^a-zA-Z]/.test(this.id())) {
+        idClass = 'dimensions-' + this.id();
+      } else {
+        idClass = this.id() + '-dimensions';
+      }
+
+      // Ensure the right class is still on the player for the style element
+      this.addClass(idClass);
+
+      setTextContent(this.styleEl_, '\n      .' + idClass + ' {\n        width: ' + width + 'px;\n        height: ' + height + 'px;\n      }\n\n      .' + idClass + '.vjs-fluid {\n        padding-top: ' + ratioMultiplier * 100 + '%;\n      }\n    ');
+    };
+
+    /**
+     * Load/Create an instance of playback {@link Tech} including element
+     * and API methods. Then append the `Tech` element in `Player` as a child.
+     *
+     * @param {string} techName
+     *        name of the playback technology
+     *
+     * @param {string} source
+     *        video source
+     *
+     * @private
+     */
+
+
+    Player.prototype.loadTech_ = function loadTech_(techName, source) {
+      var _this2 = this;
+
+      // Pause and remove current playback technology
+      if (this.tech_) {
+        this.unloadTech_();
+      }
+
+      var titleTechName = toTitleCase(techName);
+      var camelTechName = techName.charAt(0).toLowerCase() + techName.slice(1);
+
+      // get rid of the HTML5 video tag as soon as we are using another tech
+      if (titleTechName !== 'Html5' && this.tag) {
+        Tech.getTech('Html5').disposeMediaElement(this.tag);
+        this.tag.player = null;
+        this.tag = null;
+      }
+
+      this.techName_ = titleTechName;
+
+      // Turn off API access because we're loading a new tech that might load asynchronously
+      this.isReady_ = false;
+
+      // Grab tech-specific options from player options and add source and parent element to use.
+      var techOptions = {
+        source: source,
+        'nativeControlsForTouch': this.options_.nativeControlsForTouch,
+        'playerId': this.id(),
+        'techId': this.id() + '_' + titleTechName + '_api',
+        'autoplay': this.options_.autoplay,
+        'playsinline': this.options_.playsinline,
+        'preload': this.options_.preload,
+        'loop': this.options_.loop,
+        'muted': this.options_.muted,
+        'poster': this.poster(),
+        'language': this.language(),
+        'playerElIngest': this.playerElIngest_ || false,
+        'vtt.js': this.options_['vtt.js'],
+        'canOverridePoster': !!this.options_.techCanOverridePoster,
+        'enableSourceset': this.options_.enableSourceset
+      };
+
+      ALL.names.forEach(function (name$$1) {
+        var props = ALL[name$$1];
+
+        techOptions[props.getterName] = _this2[props.privateName];
+      });
+
+      assign(techOptions, this.options_[titleTechName]);
+      assign(techOptions, this.options_[camelTechName]);
+      assign(techOptions, this.options_[techName.toLowerCase()]);
+
+      if (this.tag) {
+        techOptions.tag = this.tag;
+      }
+
+      if (source && source.src === this.cache_.src && this.cache_.currentTime > 0) {
+        techOptions.startTime = this.cache_.currentTime;
+      }
+
+      // Initialize tech instance
+      var TechClass = Tech.getTech(techName);
+
+      if (!TechClass) {
+        throw new Error('No Tech named \'' + titleTechName + '\' exists! \'' + titleTechName + '\' should be registered using videojs.registerTech()\'');
+      }
+
+      this.tech_ = new TechClass(techOptions);
+
+      // player.triggerReady is always async, so don't need this to be async
+      this.tech_.ready(bind(this, this.handleTechReady_), true);
+
+      textTrackConverter.jsonToTextTracks(this.textTracksJson_ || [], this.tech_);
+
+      // Listen to all HTML5-defined events and trigger them on the player
+      TECH_EVENTS_RETRIGGER.forEach(function (event) {
+        _this2.on(_this2.tech_, event, _this2['handleTech' + toTitleCase(event) + '_']);
+      });
+
+      Object.keys(TECH_EVENTS_QUEUE).forEach(function (event) {
+        _this2.on(_this2.tech_, event, function (eventObj) {
+          if (_this2.tech_.playbackRate() === 0 && _this2.tech_.seeking()) {
+            _this2.queuedCallbacks_.push({
+              callback: _this2['handleTech' + TECH_EVENTS_QUEUE[event] + '_'].bind(_this2),
+              event: eventObj
+            });
+            return;
+          }
+          _this2['handleTech' + TECH_EVENTS_QUEUE[event] + '_'](eventObj);
+        });
+      });
+
+      this.on(this.tech_, 'loadstart', this.handleTechLoadStart_);
+      this.on(this.tech_, 'sourceset', this.handleTechSourceset_);
+      this.on(this.tech_, 'waiting', this.handleTechWaiting_);
+      this.on(this.tech_, 'ended', this.handleTechEnded_);
+      this.on(this.tech_, 'seeking', this.handleTechSeeking_);
+      this.on(this.tech_, 'play', this.handleTechPlay_);
+      this.on(this.tech_, 'firstplay', this.handleTechFirstPlay_);
+      this.on(this.tech_, 'pause', this.handleTechPause_);
+      this.on(this.tech_, 'durationchange', this.handleTechDurationChange_);
+      this.on(this.tech_, 'fullscreenchange', this.handleTechFullscreenChange_);
+      this.on(this.tech_, 'error', this.handleTechError_);
+      this.on(this.tech_, 'loadedmetadata', this.updateStyleEl_);
+      this.on(this.tech_, 'posterchange', this.handleTechPosterChange_);
+      this.on(this.tech_, 'textdata', this.handleTechTextData_);
+      this.on(this.tech_, 'ratechange', this.handleTechRateChange_);
+
+      this.usingNativeControls(this.techGet_('controls'));
+
+      if (this.controls() && !this.usingNativeControls()) {
+        this.addTechControlsListeners_();
+      }
+
+      // Add the tech element in the DOM if it was not already there
+      // Make sure to not insert the original video element if using Html5
+      if (this.tech_.el().parentNode !== this.el() && (titleTechName !== 'Html5' || !this.tag)) {
+        prependTo(this.tech_.el(), this.el());
+      }
+
+      // Get rid of the original video tag reference after the first tech is loaded
+      if (this.tag) {
+        this.tag.player = null;
+        this.tag = null;
+      }
+    };
+
+    /**
+     * Unload and dispose of the current playback {@link Tech}.
+     *
+     * @private
+     */
+
+
+    Player.prototype.unloadTech_ = function unloadTech_() {
+      var _this3 = this;
+
+      // Save the current text tracks so that we can reuse the same text tracks with the next tech
+      ALL.names.forEach(function (name$$1) {
+        var props = ALL[name$$1];
+
+        _this3[props.privateName] = _this3[props.getterName]();
+      });
+      this.textTracksJson_ = textTrackConverter.textTracksToJson(this.tech_);
+
+      this.isReady_ = false;
+
+      this.tech_.dispose();
+
+      this.tech_ = false;
+
+      if (this.isPosterFromTech_) {
+        this.poster_ = '';
+        this.trigger('posterchange');
+      }
+
+      this.isPosterFromTech_ = false;
+    };
+
+    /**
+     * Return a reference to the current {@link Tech}.
+     * It will print a warning by default about the danger of using the tech directly
+     * but any argument that is passed in will silence the warning.
+     *
+     * @param {*} [safety]
+     *        Anything passed in to silence the warning
+     *
+     * @return {Tech}
+     *         The Tech
+     */
+
+
+    Player.prototype.tech = function tech(safety) {
+      if (safety === undefined) {
+        log$1.warn(tsml(_templateObject$2));
+      }
+
+      return this.tech_;
+    };
+
+    /**
+     * Set up click and touch listeners for the playback element
+     *
+     * - On desktops: a click on the video itself will toggle playback
+     * - On mobile devices: a click on the video toggles controls
+     *   which is done by toggling the user state between active and
+     *   inactive
+     * - A tap can signal that a user has become active or has become inactive
+     *   e.g. a quick tap on an iPhone movie should reveal the controls. Another
+     *   quick tap should hide them again (signaling the user is in an inactive
+     *   viewing state)
+     * - In addition to this, we still want the user to be considered inactive after
+     *   a few seconds of inactivity.
+     *
+     * > Note: the only part of iOS interaction we can't mimic with this setup
+     * is a touch and hold on the video element counting as activity in order to
+     * keep the controls showing, but that shouldn't be an issue. A touch and hold
+     * on any controls will still keep the user active
+     *
+     * @private
+     */
+
+
+    Player.prototype.addTechControlsListeners_ = function addTechControlsListeners_() {
+      // Make sure to remove all the previous listeners in case we are called multiple times.
+      this.removeTechControlsListeners_();
+
+      // Some browsers (Chrome & IE) don't trigger a click on a flash swf, but do
+      // trigger mousedown/up.
+      // http://stackoverflow.com/questions/1444562/javascript-onclick-event-over-flash-object
+      // Any touch events are set to block the mousedown event from happening
+      this.on(this.tech_, 'mousedown', this.handleTechClick_);
+
+      // If the controls were hidden we don't want that to change without a tap event
+      // so we'll check if the controls were already showing before reporting user
+      // activity
+      this.on(this.tech_, 'touchstart', this.handleTechTouchStart_);
+      this.on(this.tech_, 'touchmove', this.handleTechTouchMove_);
+      this.on(this.tech_, 'touchend', this.handleTechTouchEnd_);
+
+      // The tap listener needs to come after the touchend listener because the tap
+      // listener cancels out any reportedUserActivity when setting userActive(false)
+      this.on(this.tech_, 'tap', this.handleTechTap_);
+    };
+
+    /**
+     * Remove the listeners used for click and tap controls. This is needed for
+     * toggling to controls disabled, where a tap/touch should do nothing.
+     *
+     * @private
+     */
+
+
+    Player.prototype.removeTechControlsListeners_ = function removeTechControlsListeners_() {
+      // We don't want to just use `this.off()` because there might be other needed
+      // listeners added by techs that extend this.
+      this.off(this.tech_, 'tap', this.handleTechTap_);
+      this.off(this.tech_, 'touchstart', this.handleTechTouchStart_);
+      this.off(this.tech_, 'touchmove', this.handleTechTouchMove_);
+      this.off(this.tech_, 'touchend', this.handleTechTouchEnd_);
+      this.off(this.tech_, 'mousedown', this.handleTechClick_);
+    };
+
+    /**
+     * Player waits for the tech to be ready
+     *
+     * @private
+     */
+
+
+    Player.prototype.handleTechReady_ = function handleTechReady_() {
+      this.triggerReady();
+
+      // Keep the same volume as before
+      if (this.cache_.volume) {
+        this.techCall_('setVolume', this.cache_.volume);
+      }
+
+      // Look if the tech found a higher resolution poster while loading
+      this.handleTechPosterChange_();
+
+      // Update the duration if available
+      this.handleTechDurationChange_();
+
+      // Chrome and Safari both have issues with autoplay.
+      // In Safari (5.1.1), when we move the video element into the container div, autoplay doesn't work.
+      // In Chrome (15), if you have autoplay + a poster + no controls, the video gets hidden (but audio plays)
+      // This fixes both issues. Need to wait for API, so it updates displays correctly
+      if ((this.src() || this.currentSrc()) && this.tag && this.options_.autoplay && this.paused()) {
+        try {
+          // Chrome Fix. Fixed in Chrome v16.
+          delete this.tag.poster;
+        } catch (e) {
+          log$1('deleting tag.poster throws in some browsers', e);
+        }
+      }
+    };
+
+    /**
+     * Retrigger the `loadstart` event that was triggered by the {@link Tech}. This
+     * function will also trigger {@link Player#firstplay} if it is the first loadstart
+     * for a video.
+     *
+     * @fires Player#loadstart
+     * @fires Player#firstplay
+     * @listens Tech#loadstart
+     * @private
+     */
+
+
+    Player.prototype.handleTechLoadStart_ = function handleTechLoadStart_() {
+      // TODO: Update to use `emptied` event instead. See #1277.
+
+      this.removeClass('vjs-ended');
+      this.removeClass('vjs-seeking');
+
+      // reset the error state
+      this.error(null);
+
+      // If it's already playing we want to trigger a firstplay event now.
+      // The firstplay event relies on both the playOld and loadstart events
+      // which can happen in any order for a new source
+      if (!this.paused()) {
+        /**
+         * Fired when the user agent begins looking for media data
+         *
+         * @event Player#loadstart
+         * @type {EventTarget~Event}
+         */
+        this.trigger('loadstart');
+        this.trigger('firstplay');
+      } else {
+        // reset the hasStarted state
+        this.hasStarted(false);
+        this.trigger('loadstart');
+      }
+    };
+
+    /**
+     * Update the internal source caches so that we return the correct source from
+     * `src()`, `currentSource()`, and `currentSources()`.
+     *
+     * > Note: `currentSources` will not be updated if the source that is passed in exists
+     *         in the current `currentSources` cache.
+     *
+     *
+     * @param {Tech~SourceObject} srcObj
+     *        A string or object source to update our caches to.
+     */
+
+
+    Player.prototype.updateSourceCaches_ = function updateSourceCaches_() {
+      var srcObj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
+
+
+      var src = srcObj;
+      var type = '';
+
+      if (typeof src !== 'string') {
+        src = srcObj.src;
+        type = srcObj.type;
+      }
+      // make sure all the caches are set to default values
+      // to prevent null checking
+      this.cache_.source = this.cache_.source || {};
+      this.cache_.sources = this.cache_.sources || [];
+
+      // try to get the type of the src that was passed in
+      if (src && !type) {
+        type = findMimetype(this, src);
+      }
+
+      // update `currentSource` cache always
+      this.cache_.source = {src: src, type: type};
+
+      var matchingSources = this.cache_.sources.filter(function (s) {
+        return s.src && s.src === src;
+      });
+      var sourceElSources = [];
+      var sourceEls = this.$$('source');
+      var matchingSourceEls = [];
+
+      for (var i = 0; i < sourceEls.length; i++) {
+        var sourceObj = getAttributes(sourceEls[i]);
+
+        sourceElSources.push(sourceObj);
+
+        if (sourceObj.src && sourceObj.src === src) {
+          matchingSourceEls.push(sourceObj.src);
+        }
+      }
+
+      // if we have matching source els but not matching sources
+      // the current source cache is not up to date
+      if (matchingSourceEls.length && !matchingSources.length) {
+        this.cache_.sources = sourceElSources;
+        // if we don't have matching source or source els set the
+        // sources cache to the `currentSource` cache
+      } else if (!matchingSources.length) {
+        this.cache_.sources = [this.cache_.source];
+      }
+
+      // update the tech `src` cache
+      this.cache_.src = src;
+    };
+
+    /**
+     * *EXPERIMENTAL* Fired when the source is set or changed on the {@link Tech}
+     * causing the media element to reload.
+     *
+     * It will fire for the initial source and each subsequent source.
+     * This event is a custom event from Video.js and is triggered by the {@link Tech}.
+     *
+     * The event object for this event contains a `src` property that will contain the source
+     * that was available when the event was triggered. This is generally only necessary if Video.js
+     * is switching techs while the source was being changed.
+     *
+     * It is also fired when `load` is called on the player (or media element)
+     * because the {@link https://html.spec.whatwg.org/multipage/media.html#dom-media-load|specification for `load`}
+     * says that the resource selection algorithm needs to be aborted and restarted.
+     * In this case, it is very likely that the `src` property will be set to the
+     * empty string `""` to indicate we do not know what the source will be but
+     * that it is changing.
+     *
+     * *This event is currently still experimental and may change in minor releases.*
+     * __To use this, pass `enableSourceset` option to the player.__
+     *
+     * @event Player#sourceset
+     * @type {EventTarget~Event}
+     * @prop {string} src
+     *                The source url available when the `sourceset` was triggered.
+     *                It will be an empty string if we cannot know what the source is
+     *                but know that the source will change.
+     */
+    /**
+     * Retrigger the `sourceset` event that was triggered by the {@link Tech}.
+     *
+     * @fires Player#sourceset
+     * @listens Tech#sourceset
+     * @private
+     */
+
+
+    Player.prototype.handleTechSourceset_ = function handleTechSourceset_(event) {
+      var _this4 = this;
+
+      // only update the source cache when the source
+      // was not updated using the player api
+      if (!this.changingSrc_) {
+        // update the source to the intial source right away
+        // in some cases this will be empty string
+        this.updateSourceCaches_(event.src);
+
+        // if the `sourceset` `src` was an empty string
+        // wait for a `loadstart` to update the cache to `currentSrc`.
+        // If a sourceset happens before a `loadstart`, we reset the state
+        // as this function will be called again.
+        if (!event.src) {
+          var updateCache = function updateCache(e) {
+            if (e.type !== 'sourceset') {
+              _this4.updateSourceCaches_(_this4.techGet_('currentSrc'));
+            }
+
+            _this4.tech_.off(['sourceset', 'loadstart'], updateCache);
+          };
+
+          this.tech_.one(['sourceset', 'loadstart'], updateCache);
+        }
+      }
+
+      this.trigger({
+        src: event.src,
+        type: 'sourceset'
+      });
+    };
+
+    /**
+     * Add/remove the vjs-has-started class
+     *
+     * @fires Player#firstplay
+     *
+     * @param {boolean} request
+     *        - true: adds the class
+     *        - false: remove the class
+     *
+     * @return {boolean}
+     *         the boolean value of hasStarted_
+     */
+
+
+    Player.prototype.hasStarted = function hasStarted(request) {
+      if (request === undefined) {
+        // act as getter, if we have no request to change
+        return this.hasStarted_;
+      }
+
+      if (request === this.hasStarted_) {
+        return;
+      }
+
+      this.hasStarted_ = request;
+
+      if (this.hasStarted_) {
+        this.addClass('vjs-has-started');
+        this.trigger('firstplay');
+      } else {
+        this.removeClass('vjs-has-started');
+      }
+    };
+
+    /**
+     * Fired whenever the media begins or resumes playback
+     *
+     * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-play}
+     * @fires Player#play
+     * @listens Tech#play
+     * @private
+     */
+
+
+    Player.prototype.handleTechPlay_ = function handleTechPlay_() {
+      this.removeClass('vjs-ended');
+      this.removeClass('vjs-paused');
+      this.addClass('vjs-playing');
+
+      // hide the poster when the user hits playOld
+      this.hasStarted(true);
+      /**
+       * Triggered whenever an {@link Tech#play} event happens. Indicates that
+       * playback has started or resumed.
+       *
+       * @event Player#play
+       * @type {EventTarget~Event}
+       */
+      this.trigger('play');
+    };
+
+    /**
+     * Retrigger the `ratechange` event that was triggered by the {@link Tech}.
+     *
+     * If there were any events queued while the playback rate was zero, fire
+     * those events now.
+     *
+     * @private
+     * @method Player#handleTechRateChange_
+     * @fires Player#ratechange
+     * @listens Tech#ratechange
+     */
+
+
+    Player.prototype.handleTechRateChange_ = function handleTechRateChange_() {
+      if (this.tech_.playbackRate() > 0 && this.cache_.lastPlaybackRate === 0) {
+        this.queuedCallbacks_.forEach(function (queued) {
+          return queued.callback(queued.event);
+        });
+        this.queuedCallbacks_ = [];
+      }
+      this.cache_.lastPlaybackRate = this.tech_.playbackRate();
+      /**
+       * Fires when the playing speed of the audio/video is changed
+       *
+       * @event Player#ratechange
+       * @type {event}
+       */
+      this.trigger('ratechange');
+    };
+
+    /**
+     * Retrigger the `waiting` event that was triggered by the {@link Tech}.
+     *
+     * @fires Player#waiting
+     * @listens Tech#waiting
+     * @private
+     */
+
+
+    Player.prototype.handleTechWaiting_ = function handleTechWaiting_() {
+      var _this5 = this;
+
+      this.addClass('vjs-waiting');
+      /**
+       * A readyState change on the DOM element has caused playback to stop.
+       *
+       * @event Player#waiting
+       * @type {EventTarget~Event}
+       */
+      this.trigger('waiting');
+      this.one('timeupdate', function () {
+        return _this5.removeClass('vjs-waiting');
+      });
+    };
+
+    /**
+     * Retrigger the `canplay` event that was triggered by the {@link Tech}.
+     * > Note: This is not consistent between browsers. See #1351
+     *
+     * @fires Player#canplay
+     * @listens Tech#canplay
+     * @private
+     */
+
+
+    Player.prototype.handleTechCanPlay_ = function handleTechCanPlay_() {
+      this.removeClass('vjs-waiting');
+      /**
+       * The media has a readyState of HAVE_FUTURE_DATA or greater.
+       *
+       * @event Player#canplay
+       * @type {EventTarget~Event}
+       */
+      this.trigger('canplay');
+    };
+
+    /**
+     * Retrigger the `canplaythrough` event that was triggered by the {@link Tech}.
+     *
+     * @fires Player#canplaythrough
+     * @listens Tech#canplaythrough
+     * @private
+     */
+
+
+    Player.prototype.handleTechCanPlayThrough_ = function handleTechCanPlayThrough_() {
+      this.removeClass('vjs-waiting');
+      /**
+       * The media has a readyState of HAVE_ENOUGH_DATA or greater. This means that the
+       * entire media file can be played without buffering.
+       *
+       * @event Player#canplaythrough
+       * @type {EventTarget~Event}
+       */
+      this.trigger('canplaythrough');
+    };
+
+    /**
+     * Retrigger the `playing` event that was triggered by the {@link Tech}.
+     *
+     * @fires Player#playing
+     * @listens Tech#playing
+     * @private
+     */
+
+
+    Player.prototype.handleTechPlaying_ = function handleTechPlaying_() {
+      this.removeClass('vjs-waiting');
+      /**
+       * The media is no longer blocked from playback, and has started playing.
+       *
+       * @event Player#playing
+       * @type {EventTarget~Event}
+       */
+      this.trigger('playing');
+    };
+
+    /**
+     * Retrigger the `seeking` event that was triggered by the {@link Tech}.
+     *
+     * @fires Player#seeking
+     * @listens Tech#seeking
+     * @private
+     */
+
+
+    Player.prototype.handleTechSeeking_ = function handleTechSeeking_() {
+      this.addClass('vjs-seeking');
+      /**
+       * Fired whenever the player is jumping to a new time
+       *
+       * @event Player#seeking
+       * @type {EventTarget~Event}
+       */
+      this.trigger('seeking');
+    };
+
+    /**
+     * Retrigger the `seeked` event that was triggered by the {@link Tech}.
+     *
+     * @fires Player#seeked
+     * @listens Tech#seeked
+     * @private
+     */
+
+
+    Player.prototype.handleTechSeeked_ = function handleTechSeeked_() {
+      this.removeClass('vjs-seeking');
+      /**
+       * Fired when the player has finished jumping to a new time
+       *
+       * @event Player#seeked
+       * @type {EventTarget~Event}
+       */
+      this.trigger('seeked');
+    };
+
+    /**
+     * Retrigger the `firstplay` event that was triggered by the {@link Tech}.
+     *
+     * @fires Player#firstplay
+     * @listens Tech#firstplay
+     * @deprecated As of 6.0 firstplay event is deprecated.
+     * @deprecated As of 6.0 passing the `starttime` option to the player and the firstplay event are deprecated.
+     * @private
+     */
+
+
+    Player.prototype.handleTechFirstPlay_ = function handleTechFirstPlay_() {
+      // If the first starttime attribute is specified
+      // then we will start at the given offset in seconds
+      if (this.options_.starttime) {
+        log$1.warn('Passing the `starttime` option to the player will be deprecated in 6.0');
+        this.currentTime(this.options_.starttime);
+      }
+
+      this.addClass('vjs-has-started');
+      /**
+       * Fired the first time a video is played. Not part of the HLS spec, and this is
+       * probably not the best implementation yet, so use sparingly. If you don't have a
+       * reason to prevent playback, use `myPlayer.one('playOld');` instead.
+       *
+       * @event Player#firstplay
+       * @deprecated As of 6.0 firstplay event is deprecated.
+       * @type {EventTarget~Event}
+       */
+      this.trigger('firstplay');
+    };
+
+    /**
+     * Retrigger the `pause` event that was triggered by the {@link Tech}.
+     *
+     * @fires Player#pause
+     * @listens Tech#pause
+     * @private
+     */
+
+
+    Player.prototype.handleTechPause_ = function handleTechPause_() {
+      this.removeClass('vjs-playing');
+      this.addClass('vjs-paused');
+      /**
+       * Fired whenever the media has been paused
+       *
+       * @event Player#pause
+       * @type {EventTarget~Event}
+       */
+      this.trigger('pause');
+    };
+
+    /**
+     * Retrigger the `ended` event that was triggered by the {@link Tech}.
+     *
+     * @fires Player#ended
+     * @listens Tech#ended
+     * @private
+     */
+
+
+    Player.prototype.handleTechEnded_ = function handleTechEnded_() {
+      this.addClass('vjs-ended');
+      if (this.options_.loop) {
+        this.currentTime(0);
+        this.play();
+      } else if (!this.paused()) {
+        this.pause();
+      }
+
+      /**
+       * Fired when the end of the media resource is reached (currentTime == duration)
+       *
+       * @event Player#ended
+       * @type {EventTarget~Event}
+       */
+      this.trigger('ended');
+    };
+
+    /**
+     * Fired when the duration of the media resource is first known or changed
+     *
+     * @listens Tech#durationchange
+     * @private
+     */
+
+
+    Player.prototype.handleTechDurationChange_ = function handleTechDurationChange_() {
+      this.duration(this.techGet_('duration'));
+    };
+
+    /**
+     * Handle a click on the media element to playOld/pause
+     *
+     * @param {EventTarget~Event} event
+     *        the event that caused this function to trigger
+     *
+     * @listens Tech#mousedown
+     * @private
+     */
+
+
+    Player.prototype.handleTechClick_ = function handleTechClick_(event) {
+      if (!isSingleLeftClick(event)) {
+        return;
+      }
+
+      // When controls are disabled a click should not toggle playback because
+      // the click is considered a control
+      if (!this.controls_) {
+        return;
+      }
+
+      if (this.paused()) {
+        this.play();
+      } else {
+        this.pause();
+      }
+    };
+
+    /**
+     * Handle a tap on the media element. It will toggle the user
+     * activity state, which hides and shows the controls.
+     *
+     * @listens Tech#tap
+     * @private
+     */
+
+
+    Player.prototype.handleTechTap_ = function handleTechTap_() {
+      this.userActive(!this.userActive());
+    };
+
+    /**
+     * Handle touch to start
+     *
+     * @listens Tech#touchstart
+     * @private
+     */
+
+
+    Player.prototype.handleTechTouchStart_ = function handleTechTouchStart_() {
+      this.userWasActive = this.userActive();
+    };
+
+    /**
+     * Handle touch to move
+     *
+     * @listens Tech#touchmove
+     * @private
+     */
+
+
+    Player.prototype.handleTechTouchMove_ = function handleTechTouchMove_() {
+      if (this.userWasActive) {
+        this.reportUserActivity();
+      }
+    };
+
+    /**
+     * Handle touch to end
+     *
+     * @param {EventTarget~Event} event
+     *        the touchend event that triggered
+     *        this function
+     *
+     * @listens Tech#touchend
+     * @private
+     */
+
+
+    Player.prototype.handleTechTouchEnd_ = function handleTechTouchEnd_(event) {
+      // Stop the mouse events from also happening
+      event.preventDefault();
+    };
+
+    /**
+     * Fired when the player switches in or out of fullscreen mode
+     *
+     * @private
+     * @listens Player#fullscreenchange
+     */
+
+
+    Player.prototype.handleFullscreenChange_ = function handleFullscreenChange_() {
+      if (this.isFullscreen()) {
+        this.addClass('vjs-fullscreen');
+      } else {
+        this.removeClass('vjs-fullscreen');
+      }
+    };
+
+    /**
+     * native click events on the SWF aren't triggered on IE11, Win8.1RT
+     * use stageclick events triggered from inside the SWF instead
+     *
+     * @private
+     * @listens stageclick
+     */
+
+
+    Player.prototype.handleStageClick_ = function handleStageClick_() {
+      this.reportUserActivity();
+    };
+
+    /**
+     * Handle Tech Fullscreen Change
+     *
+     * @param {EventTarget~Event} event
+     *        the fullscreenchange event that triggered this function
+     *
+     * @param {Object} data
+     *        the data that was sent with the event
+     *
+     * @private
+     * @listens Tech#fullscreenchange
+     * @fires Player#fullscreenchange
+     */
+
+
+    Player.prototype.handleTechFullscreenChange_ = function handleTechFullscreenChange_(event, data) {
+      if (data) {
+        this.isFullscreen(data.isFullscreen);
+      }
+      /**
+       * Fired when going in and out of fullscreen.
+       *
+       * @event Player#fullscreenchange
+       * @type {EventTarget~Event}
+       */
+      this.trigger('fullscreenchange');
+    };
+
+    /**
+     * Fires when an error occurred during the loading of an audio/video.
+     *
+     * @private
+     * @listens Tech#error
+     */
+
+
+    Player.prototype.handleTechError_ = function handleTechError_() {
+      var error = this.tech_.error();
+
+      this.error(error);
+    };
+
+    /**
+     * Retrigger the `textdata` event that was triggered by the {@link Tech}.
+     *
+     * @fires Player#textdata
+     * @listens Tech#textdata
+     * @private
+     */
+
+
+    Player.prototype.handleTechTextData_ = function handleTechTextData_() {
+      var data = null;
+
+      if (arguments.length > 1) {
+        data = arguments[1];
+      }
+
+      /**
+       * Fires when we get a textdata event from tech
+       *
+       * @event Player#textdata
+       * @type {EventTarget~Event}
+       */
+      this.trigger('textdata', data);
+    };
+
+    /**
+     * Get object for cached values.
+     *
+     * @return {Object}
+     *         get the current object cache
+     */
+
+
+    Player.prototype.getCache = function getCache() {
+      return this.cache_;
+    };
+
+    /**
+     * Pass values to the playback tech
+     *
+     * @param {string} [method]
+     *        the method to call
+     *
+     * @param {Object} arg
+     *        the argument to pass
+     *
+     * @private
+     */
+
+
+    Player.prototype.techCall_ = function techCall_(method, arg) {
+      // If it's not ready yet, call method when it is
+
+      this.ready(function () {
+        if (method in allowedSetters) {
+          return set$1(this.middleware_, this.tech_, method, arg);
+        } else if (method in allowedMediators) {
+          return mediate(this.middleware_, this.tech_, method, arg);
+        }
+
+        try {
+          if (this.tech_) {
+            this.tech_[method](arg);
+          }
+        } catch (e) {
+          log$1(e);
+          throw e;
+        }
+      }, true);
+    };
+
+    /**
+     * Get calls can't wait for the tech, and sometimes don't need to.
+     *
+     * @param {string} method
+     *        Tech method
+     *
+     * @return {Function|undefined}
+     *         the method or undefined
+     *
+     * @private
+     */
+
+
+    Player.prototype.techGet_ = function techGet_(method) {
+      if (!this.tech_ || !this.tech_.isReady_) {
+        return;
+      }
+
+      if (method in allowedGetters) {
+        return get$1(this.middleware_, this.tech_, method);
+      } else if (method in allowedMediators) {
+        return mediate(this.middleware_, this.tech_, method);
+      }
+
+      // Flash likes to die and reload when you hide or reposition it.
+      // In these cases the object methods go away and we get errors.
+      // When that happens we'll catch the errors and inform tech that it's not ready any more.
+      try {
+        return this.tech_[method]();
+      } catch (e) {
+
+        // When building additional tech libs, an expected method may not be defined yet
+        if (this.tech_[method] === undefined) {
+          log$1('Video.js: ' + method + ' method not defined for ' + this.techName_ + ' playback technology.', e);
+          throw e;
+        }
+
+        // When a method isn't available on the object it throws a TypeError
+        if (e.name === 'TypeError') {
+          log$1('Video.js: ' + method + ' unavailable on ' + this.techName_ + ' playback technology element.', e);
+          this.tech_.isReady_ = false;
+          throw e;
+        }
+
+        // If error unknown, just log and throw
+        log$1(e);
+        throw e;
+      }
+    };
+
+    /**
+     * Attempt to begin playback at the first opportunity.
+     *
+     * @return {Promise|undefined}
+     *         Returns a `Promise` only if the browser returns one and the player
+     *         is ready to begin playback. For some browsers and all non-ready
+     *         situations, this will return `undefined`.
+     */
+
+
+    Player.prototype.play = function play() {
+      var _this6 = this;
+
+      // If this is called while we have a playOld queued up on a loadstart, remove
+      // that listener to avoid getting in a potentially bad state.
+      if (this.playOnLoadstart_) {
+        this.off('loadstart', this.playOnLoadstart_);
+      }
+
+      // If the player/tech is not ready, queue up another call to `playOld()` for
+      // when it is. This will loop back into this method for another attempt at
+      // playback when the tech is ready.
+      if (!this.isReady_) {
+
+        // Bail out if we're already waiting for `ready`!
+        if (this.playWaitingForReady_) {
+          return;
+        }
+
+        this.playWaitingForReady_ = true;
+        this.ready(function () {
+          _this6.playWaitingForReady_ = false;
+          silencePromise(_this6.play());
+        });
+
+        // If the player/tech is ready and we have a source, we can attempt playback.
+      } else if (!this.changingSrc_ && (this.src() || this.currentSrc())) {
+        return this.techGet_('play');
+
+        // If the tech is ready, but we do not have a source, we'll need to wait
+        // for both the `ready` and a `loadstart` when the source is finally
+        // resolved by middleware and set on the player.
+        //
+        // This can happen if `playOld()` is called while changing sources or before
+        // one has been set on the player.
+      } else {
+
+        this.playOnLoadstart_ = function () {
+          _this6.playOnLoadstart_ = null;
+          silencePromise(_this6.play());
+        };
+
+        this.one('loadstart', this.playOnLoadstart_);
+      }
+    };
+
+    /**
+     * Pause the video playback
+     *
+     * @return {Player}
+     *         A reference to the player object this function was called on
+     */
+
+
+    Player.prototype.pause = function pause() {
+      this.techCall_('pause');
+    };
+
+    /**
+     * Check if the player is paused or has yet to playOld
+     *
+     * @return {boolean}
+     *         - false: if the media is currently playing
+     *         - true: if media is not currently playing
+     */
+
+
+    Player.prototype.paused = function paused() {
+      // The initial state of paused should be true (in Safari it's actually false)
+      return this.techGet_('paused') === false ? false : true;
+    };
+
+    /**
+     * Get a TimeRange object representing the current ranges of time that the user
+     * has played.
+     *
+     * @return {TimeRange}
+     *         A time range object that represents all the increments of time that have
+     *         been played.
+     */
+
+
+    Player.prototype.played = function played() {
+      return this.techGet_('played') || createTimeRanges(0, 0);
+    };
+
+    /**
+     * Returns whether or not the user is "scrubbing". Scrubbing is
+     * when the user has clicked the progress bar handle and is
+     * dragging it along the progress bar.
+     *
+     * @param {boolean} [isScrubbing]
+     *        whether the user is or is not scrubbing
+     *
+     * @return {boolean}
+     *         The value of scrubbing when getting
+     */
+
+
+    Player.prototype.scrubbing = function scrubbing(isScrubbing) {
+      if (typeof isScrubbing === 'undefined') {
+        return this.scrubbing_;
+      }
+      this.scrubbing_ = !!isScrubbing;
+
+      if (isScrubbing) {
+        this.addClass('vjs-scrubbing');
+      } else {
+        this.removeClass('vjs-scrubbing');
+      }
+    };
+
+    /**
+     * Get or set the current time (in seconds)
+     *
+     * @param {number|string} [seconds]
+     *        The time to seek to in seconds
+     *
+     * @return {number}
+     *         - the current time in seconds when getting
+     */
+
+
+    Player.prototype.currentTime = function currentTime(seconds) {
+      if (typeof seconds !== 'undefined') {
+        if (seconds < 0) {
+          seconds = 0;
+        }
+        this.techCall_('setCurrentTime', seconds);
+        return;
+      }
+
+      // cache last currentTime and return. default to 0 seconds
+      //
+      // Caching the currentTime is meant to prevent a massive amount of reads on the tech's
+      // currentTime when scrubbing, but may not provide much performance benefit afterall.
+      // Should be tested. Also something has to read the actual current time or the cache will
+      // never get updated.
+      this.cache_.currentTime = this.techGet_('currentTime') || 0;
+      return this.cache_.currentTime;
+    };
+
+    /**
+     * Normally gets the length in time of the video in seconds;
+     * in all but the rarest use cases an argument will NOT be passed to the method
+     *
+     * > **NOTE**: The video must have started loading before the duration can be
+     * known, and in the case of Flash, may not be known until the video starts
+     * playing.
+     *
+     * @fires Player#durationchange
+     *
+     * @param {number} [seconds]
+     *        The duration of the video to set in seconds
+     *
+     * @return {number}
+     *         - The duration of the video in seconds when getting
+     */
+
+
+    Player.prototype.duration = function duration(seconds) {
+      if (seconds === undefined) {
+        // return NaN if the duration is not known
+        return this.cache_.duration !== undefined ? this.cache_.duration : NaN;
+      }
+
+      seconds = parseFloat(seconds);
+
+      // Standardize on Infinity for signaling video is live
+      if (seconds < 0) {
+        seconds = Infinity;
+      }
+
+      if (seconds !== this.cache_.duration) {
+        // Cache the last set value for optimized scrubbing (esp. Flash)
+        this.cache_.duration = seconds;
+
+        if (seconds === Infinity) {
+          this.addClass('vjs-live');
+        } else {
+          this.removeClass('vjs-live');
+        }
+        /**
+         * @event Player#durationchange
+         * @type {EventTarget~Event}
+         */
+        this.trigger('durationchange');
+      }
+    };
+
+    /**
+     * Calculates how much time is left in the video. Not part
+     * of the native video API.
+     *
+     * @return {number}
+     *         The time remaining in seconds
+     */
+
+
+    Player.prototype.remainingTime = function remainingTime() {
+      return this.duration() - this.currentTime();
+    };
+
+    /**
+     * A remaining time function that is intented to be used when
+     * the time is to be displayed directly to the user.
+     *
+     * @return {number}
+     *         The rounded time remaining in seconds
+     */
+
+
+    Player.prototype.remainingTimeDisplay = function remainingTimeDisplay() {
+      return Math.floor(this.duration()) - Math.floor(this.currentTime());
+    };
+
+    //
+    // Kind of like an array of portions of the video that have been downloaded.
+
+    /**
+     * Get a TimeRange object with an array of the times of the video
+     * that have been downloaded. If you just want the percent of the
+     * video that's been downloaded, use bufferedPercent.
+     *
+     * @see [Buffered Spec]{@link http://dev.w3.org/html5/spec/video.html#dom-media-buffered}
+     *
+     * @return {TimeRange}
+     *         A mock TimeRange object (following HTML spec)
+     */
+
+
+    Player.prototype.buffered = function buffered() {
+      var buffered = this.techGet_('buffered');
+
+      if (!buffered || !buffered.length) {
+        buffered = createTimeRanges(0, 0);
+      }
+
+      return buffered;
+    };
+
+    /**
+     * Get the percent (as a decimal) of the video that's been downloaded.
+     * This method is not a part of the native HTML video API.
+     *
+     * @return {number}
+     *         A decimal between 0 and 1 representing the percent
+     *         that is buffered 0 being 0% and 1 being 100%
+     */
+
+
+    Player.prototype.bufferedPercent = function bufferedPercent$$1() {
+      return bufferedPercent(this.buffered(), this.duration());
+    };
+
+    /**
+     * Get the ending time of the last buffered time range
+     * This is used in the progress bar to encapsulate all time ranges.
+     *
+     * @return {number}
+     *         The end of the last buffered time range
+     */
+
+
+    Player.prototype.bufferedEnd = function bufferedEnd() {
+      var buffered = this.buffered();
+      var duration = this.duration();
+      var end = buffered.end(buffered.length - 1);
+
+      if (end > duration) {
+        end = duration;
+      }
+
+      return end;
+    };
+
+    /**
+     * Get or set the current volume of the media
+     *
+     * @param  {number} [percentAsDecimal]
+     *         The new volume as a decimal percent:
+     *         - 0 is muted/0%/off
+     *         - 1.0 is 100%/full
+     *         - 0.5 is half volume or 50%
+     *
+     * @return {number}
+     *         The current volume as a percent when getting
+     */
+
+
+    Player.prototype.volume = function volume(percentAsDecimal) {
+      var vol = void 0;
+
+      if (percentAsDecimal !== undefined) {
+        // Force value to between 0 and 1
+        vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal)));
+        this.cache_.volume = vol;
+        this.techCall_('setVolume', vol);
+
+        if (vol > 0) {
+          this.lastVolume_(vol);
+        }
+
+        return;
+      }
+
+      // Default to 1 when returning current volume.
+      vol = parseFloat(this.techGet_('volume'));
+      return isNaN(vol) ? 1 : vol;
+    };
+
+    /**
+     * Get the current muted state, or turn mute on or off
+     *
+     * @param {boolean} [muted]
+     *        - true to mute
+     *        - false to unmute
+     *
+     * @return {boolean}
+     *         - true if mute is on and getting
+     *         - false if mute is off and getting
+     */
+
+
+    Player.prototype.muted = function muted(_muted) {
+      if (_muted !== undefined) {
+        this.techCall_('setMuted', _muted);
+        return;
+      }
+      return this.techGet_('muted') || false;
+    };
+
+    /**
+     * Get the current defaultMuted state, or turn defaultMuted on or off. defaultMuted
+     * indicates the state of muted on initial playback.
+     *
+     * ```js
+     *   var myPlayer = videojs('some-player-id');
+     *
+     *   myPlayer.src("http://www.example.com/path/to/video.mp4");
+     *
+     *   // get, should be false
+     *   console.log(myPlayer.defaultMuted());
+     *   // set to true
+     *   myPlayer.defaultMuted(true);
+     *   // get should be true
+     *   console.log(myPlayer.defaultMuted());
+     * ```
+     *
+     * @param {boolean} [defaultMuted]
+     *        - true to mute
+     *        - false to unmute
+     *
+     * @return {boolean|Player}
+     *         - true if defaultMuted is on and getting
+     *         - false if defaultMuted is off and getting
+     *         - A reference to the current player when setting
+     */
+
+
+    Player.prototype.defaultMuted = function defaultMuted(_defaultMuted) {
+      if (_defaultMuted !== undefined) {
+        return this.techCall_('setDefaultMuted', _defaultMuted);
+      }
+      return this.techGet_('defaultMuted') || false;
+    };
+
+    /**
+     * Get the last volume, or set it
+     *
+     * @param  {number} [percentAsDecimal]
+     *         The new last volume as a decimal percent:
+     *         - 0 is muted/0%/off
+     *         - 1.0 is 100%/full
+     *         - 0.5 is half volume or 50%
+     *
+     * @return {number}
+     *         the current value of lastVolume as a percent when getting
+     *
+     * @private
+     */
+
+
+    Player.prototype.lastVolume_ = function lastVolume_(percentAsDecimal) {
+      if (percentAsDecimal !== undefined && percentAsDecimal !== 0) {
+        this.cache_.lastVolume = percentAsDecimal;
+        return;
+      }
+      return this.cache_.lastVolume;
+    };
+
+    /**
+     * Check if current tech can support native fullscreen
+     * (e.g. with built in controls like iOS, so not our flash swf)
+     *
+     * @return {boolean}
+     *         if native fullscreen is supported
+     */
+
+
+    Player.prototype.supportsFullScreen = function supportsFullScreen() {
+      return this.techGet_('supportsFullScreen') || false;
+    };
+
+    /**
+     * Check if the player is in fullscreen mode or tell the player that it
+     * is or is not in fullscreen mode.
+     *
+     * > NOTE: As of the latest HTML5 spec, isFullscreen is no longer an official
+     * property and instead document.fullscreenElement is used. But isFullscreen is
+     * still a valuable property for internal player workings.
+     *
+     * @param  {boolean} [isFS]
+     *         Set the players current fullscreen state
+     *
+     * @return {boolean}
+     *         - true if fullscreen is on and getting
+     *         - false if fullscreen is off and getting
+     */
+
+
+    Player.prototype.isFullscreen = function isFullscreen(isFS) {
+      if (isFS !== undefined) {
+        this.isFullscreen_ = !!isFS;
+        return;
+      }
+      return !!this.isFullscreen_;
+    };
+
+    /**
+     * Increase the size of the video to full screen
+     * In some browsers, full screen is not supported natively, so it enters
+     * "full window mode", where the video fills the browser window.
+     * In browsers and devices that support native full screen, sometimes the
+     * browser's default controls will be shown, and not the Video.js custom skin.
+     * This includes most mobile devices (iOS, Android) and older versions of
+     * Safari.
+     *
+     * @fires Player#fullscreenchange
+     */
+
+
+    Player.prototype.requestFullscreen = function requestFullscreen() {
+      var fsApi = FullscreenApi;
+
+      this.isFullscreen(true);
+
+      if (fsApi.requestFullscreen) {
+        // the browser supports going fullscreen at the element level so we can
+        // take the controls fullscreen as well as the video
+
+        // Trigger fullscreenchange event after change
+        // We have to specifically add this each time, and remove
+        // when canceling fullscreen. Otherwise if there's multiple
+        // players on a page, they would all be reacting to the same fullscreen
+        // events
+        on(document_1, fsApi.fullscreenchange, bind(this, function documentFullscreenChange(e) {
+          this.isFullscreen(document_1[fsApi.fullscreenElement]);
+
+          // If cancelling fullscreen, remove event listener.
+          if (this.isFullscreen() === false) {
+            off(document_1, fsApi.fullscreenchange, documentFullscreenChange);
+          }
+          /**
+           * @event Player#fullscreenchange
+           * @type {EventTarget~Event}
+           */
+          this.trigger('fullscreenchange');
+        }));
+
+        this.el_[fsApi.requestFullscreen]();
+      } else if (this.tech_.supportsFullScreen()) {
+        // we can't take the video.js controls fullscreen but we can go fullscreen
+        // with native controls
+        this.techCall_('enterFullScreen');
+      } else {
+        // fullscreen isn't supported so we'll just stretch the video element to
+        // fill the viewport
+        this.enterFullWindow();
+        /**
+         * @event Player#fullscreenchange
+         * @type {EventTarget~Event}
+         */
+        this.trigger('fullscreenchange');
+      }
+    };
+
+    /**
+     * Return the video to its normal size after having been in full screen mode
+     *
+     * @fires Player#fullscreenchange
+     */
+
+
+    Player.prototype.exitFullscreen = function exitFullscreen() {
+      var fsApi = FullscreenApi;
+
+      this.isFullscreen(false);
+
+      // Check for browser element fullscreen support
+      if (fsApi.requestFullscreen) {
+        document_1[fsApi.exitFullscreen]();
+      } else if (this.tech_.supportsFullScreen()) {
+        this.techCall_('exitFullScreen');
+      } else {
+        this.exitFullWindow();
+        /**
+         * @event Player#fullscreenchange
+         * @type {EventTarget~Event}
+         */
+        this.trigger('fullscreenchange');
+      }
+    };
+
+    /**
+     * When fullscreen isn't supported we can stretch the
+     * video container to as wide as the browser will let us.
+     *
+     * @fires Player#enterFullWindow
+     */
+
+
+    Player.prototype.enterFullWindow = function enterFullWindow() {
+      this.isFullWindow = true;
+
+      // Storing original doc overflow value to return to when fullscreen is off
+      this.docOrigOverflow = document_1.documentElement.style.overflow;
+
+      // Add listener for esc key to exit fullscreen
+      on(document_1, 'keydown', bind(this, this.fullWindowOnEscKey));
+
+      // Hide any scroll bars
+      document_1.documentElement.style.overflow = 'hidden';
+
+      // Apply fullscreen styles
+      addClass(document_1.body, 'vjs-full-window');
+
+      /**
+       * @event Player#enterFullWindow
+       * @type {EventTarget~Event}
+       */
+      this.trigger('enterFullWindow');
+    };
+
+    /**
+     * Check for call to either exit full window or
+     * full screen on ESC key
+     *
+     * @param {string} event
+     *        Event to check for key press
+     */
+
+
+    Player.prototype.fullWindowOnEscKey = function fullWindowOnEscKey(event) {
+      if (event.keyCode === 27) {
+        if (this.isFullscreen() === true) {
+          this.exitFullscreen();
+        } else {
+          this.exitFullWindow();
+        }
+      }
+    };
+
+    /**
+     * Exit full window
+     *
+     * @fires Player#exitFullWindow
+     */
+
+
+    Player.prototype.exitFullWindow = function exitFullWindow() {
+      this.isFullWindow = false;
+      off(document_1, 'keydown', this.fullWindowOnEscKey);
+
+      // Unhide scroll bars.
+      document_1.documentElement.style.overflow = this.docOrigOverflow;
+
+      // Remove fullscreen styles
+      removeClass(document_1.body, 'vjs-full-window');
+
+      // Resize the box, controller, and poster to original sizes
+      // this.positionAll();
+      /**
+       * @event Player#exitFullWindow
+       * @type {EventTarget~Event}
+       */
+      this.trigger('exitFullWindow');
+    };
+
+    /**
+     * Check whether the player can playOld a given mimetype
+     *
+     * @see https://www.w3.org/TR/2011/WD-html5-20110113/video.html#dom-navigator-canplaytype
+     *
+     * @param {string} type
+     *        The mimetype to check
+     *
+     * @return {string}
+     *         'probably', 'maybe', or '' (empty string)
+     */
+
+
+    Player.prototype.canPlayType = function canPlayType(type) {
+      var can = void 0;
+
+      // Loop through each playback technology in the options order
+      for (var i = 0, j = this.options_.techOrder; i < j.length; i++) {
+        var techName = j[i];
+        var tech = Tech.getTech(techName);
+
+        // Support old behavior of techs being registered as components.
+        // Remove once that deprecated behavior is removed.
+        if (!tech) {
+          tech = Component.getComponent(techName);
+        }
+
+        // Check if the current tech is defined before continuing
+        if (!tech) {
+          log$1.error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.');
+          continue;
+        }
+
+        // Check if the browser supports this technology
+        if (tech.isSupported()) {
+          can = tech.canPlayType(type);
+
+          if (can) {
+            return can;
+          }
+        }
+      }
+
+      return '';
+    };
+
+    /**
+     * Select source based on tech-order or source-order
+     * Uses source-order selection if `options.sourceOrder` is truthy. Otherwise,
+     * defaults to tech-order selection
+     *
+     * @param {Array} sources
+     *        The sources for a media asset
+     *
+     * @return {Object|boolean}
+     *         Object of source and tech order or false
+     */
+
+
+    Player.prototype.selectSource = function selectSource(sources) {
+      var _this7 = this;
+
+      // Get only the techs specified in `techOrder` that exist and are supported by the
+      // current platform
+      var techs = this.options_.techOrder.map(function (techName) {
+        return [techName, Tech.getTech(techName)];
+      }).filter(function (_ref) {
+        var techName = _ref[0],
+          tech = _ref[1];
+
+        // Check if the current tech is defined before continuing
+        if (tech) {
+          // Check if the browser supports this technology
+          return tech.isSupported();
+        }
+
+        log$1.error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.');
+        return false;
+      });
+
+      // Iterate over each `innerArray` element once per `outerArray` element and execute
+      // `tester` with both. If `tester` returns a non-falsy value, exit early and return
+      // that value.
+      var findFirstPassingTechSourcePair = function findFirstPassingTechSourcePair(outerArray, innerArray, tester) {
+        var found = void 0;
+
+        outerArray.some(function (outerChoice) {
+          return innerArray.some(function (innerChoice) {
+            found = tester(outerChoice, innerChoice);
+
+            if (found) {
+              return true;
+            }
+          });
+        });
+
+        return found;
+      };
+
+      var foundSourceAndTech = void 0;
+      var flip = function flip(fn) {
+        return function (a, b) {
+          return fn(b, a);
+        };
+      };
+      var finder = function finder(_ref2, source) {
+        var techName = _ref2[0],
+          tech = _ref2[1];
+
+        if (tech.canPlaySource(source, _this7.options_[techName.toLowerCase()])) {
+          return {source: source, tech: techName};
+        }
+      };
+
+      // Depending on the truthiness of `options.sourceOrder`, we swap the order of techs and sources
+      // to select from them based on their priority.
+      if (this.options_.sourceOrder) {
+        // Source-first ordering
+        foundSourceAndTech = findFirstPassingTechSourcePair(sources, techs, flip(finder));
+      } else {
+        // Tech-first ordering
+        foundSourceAndTech = findFirstPassingTechSourcePair(techs, sources, finder);
+      }
+
+      return foundSourceAndTech || false;
+    };
+
+    /**
+     * Get or set the video source.
+     *
+     * @param {Tech~SourceObject|Tech~SourceObject[]|string} [source]
+     *        A SourceObject, an array of SourceObjects, or a string referencing
+     *        a URL to a media source. It is _highly recommended_ that an object
+     *        or array of objects is used here, so that source selection
+     *        algorithms can take the `type` into account.
+     *
+     *        If not provided, this method acts as a getter.
+     *
+     * @return {string|undefined}
+     *         If the `source` argument is missing, returns the current source
+     *         URL. Otherwise, returns nothing/undefined.
+     */
+
+
+    Player.prototype.src = function src(source) {
+      var _this8 = this;
+
+      // getter usage
+      if (typeof source === 'undefined') {
+        return this.cache_.src || '';
+      }
+      // filter out invalid sources and turn our source into
+      // an array of source objects
+      var sources = filterSource(source);
+
+      // if a source was passed in then it is invalid because
+      // it was filtered to a zero length Array. So we have to
+      // show an error
+      if (!sources.length) {
+        this.setTimeout(function () {
+          this.error({code: 4, message: this.localize(this.options_.notSupportedMessage)});
+        }, 0);
+        return;
+      }
+
+      // intial sources
+      this.changingSrc_ = true;
+
+      this.cache_.sources = sources;
+      this.updateSourceCaches_(sources[0]);
+
+      // middlewareSource is the source after it has been changed by middleware
+      setSource(this, sources[0], function (middlewareSource, mws) {
+        _this8.middleware_ = mws;
+
+        // since sourceSet is async we have to update the cache again after we select a source since
+        // the source that is selected could be out of order from the cache update above this callback.
+        _this8.cache_.sources = sources;
+        _this8.updateSourceCaches_(middlewareSource);
+
+        var err = _this8.src_(middlewareSource);
+
+        if (err) {
+          if (sources.length > 1) {
+            return _this8.src(sources.slice(1));
+          }
+
+          _this8.changingSrc_ = false;
+
+          // We need to wrap this in a timeout to give folks a chance to add error event handlers
+          _this8.setTimeout(function () {
+            this.error({code: 4, message: this.localize(this.options_.notSupportedMessage)});
+          }, 0);
+
+          // we could not find an appropriate tech, but let's still notify the delegate that this is it
+          // this needs a better comment about why this is needed
+          _this8.triggerReady();
+
+          return;
+        }
+
+        setTech(mws, _this8.tech_);
+      });
+    };
+
+    /**
+     * Set the source object on the tech, returns a boolean that indicates whether
+     * there is a tech that can playOld the source or not
+     *
+     * @param {Tech~SourceObject} source
+     *        The source object to set on the Tech
+     *
+     * @return {Boolean}
+     *         - True if there is no Tech to playback this source
+     *         - False otherwise
+     *
+     * @private
+     */
+
+
+    Player.prototype.src_ = function src_(source) {
+      var _this9 = this;
+
+      var sourceTech = this.selectSource([source]);
+
+      if (!sourceTech) {
+        return true;
+      }
+
+      if (!titleCaseEquals(sourceTech.tech, this.techName_)) {
+        this.changingSrc_ = true;
+        // load this technology with the chosen source
+        this.loadTech_(sourceTech.tech, sourceTech.source);
+        this.tech_.ready(function () {
+          _this9.changingSrc_ = false;
+        });
+        return false;
+      }
+
+      // wait until the tech is ready to set the source
+      // and set it synchronously if possible (#2326)
+      this.ready(function () {
+
+        // The setSource tech method was added with source handlers
+        // so older techs won't support it
+        // We need to check the direct prototype for the case where subclasses
+        // of the tech do not support source handlers
+        if (this.tech_.constructor.prototype.hasOwnProperty('setSource')) {
+          this.techCall_('setSource', source);
+        } else {
+          this.techCall_('src', source.src);
+        }
+
+        this.changingSrc_ = false;
+      }, true);
+
+      return false;
+    };
+
+    /**
+     * Begin loading the src data.
+     */
+
+
+    Player.prototype.load = function load() {
+      this.techCall_('load');
+    };
+
+    /**
+     * Reset the player. Loads the first tech in the techOrder,
+     * and calls `reset` on the tech`.
+     */
+
+
+    Player.prototype.reset = function reset() {
+      this.loadTech_(this.options_.techOrder[0], null);
+      this.techCall_('reset');
+    };
+
+    /**
+     * Returns all of the current source objects.
+     *
+     * @return {Tech~SourceObject[]}
+     *         The current source objects
+     */
+
+
+    Player.prototype.currentSources = function currentSources() {
+      var source = this.currentSource();
+      var sources = [];
+
+      // assume `{}` or `{ src }`
+      if (Object.keys(source).length !== 0) {
+        sources.push(source);
+      }
+
+      return this.cache_.sources || sources;
+    };
+
+    /**
+     * Returns the current source object.
+     *
+     * @return {Tech~SourceObject}
+     *         The current source object
+     */
+
+
+    Player.prototype.currentSource = function currentSource() {
+      return this.cache_.source || {};
+    };
+
+    /**
+     * Returns the fully qualified URL of the current source value e.g. http://mysite.com/video.mp4
+     * Can be used in conjunction with `currentType` to assist in rebuilding the current source object.
+     *
+     * @return {string}
+     *         The current source
+     */
+
+
+    Player.prototype.currentSrc = function currentSrc() {
+      return this.currentSource() && this.currentSource().src || '';
+    };
+
+    /**
+     * Get the current source type e.g. video/mp4
+     * This can allow you rebuild the current source object so that you could load the same
+     * source and tech later
+     *
+     * @return {string}
+     *         The source MIME type
+     */
+
+
+    Player.prototype.currentType = function currentType() {
+      return this.currentSource() && this.currentSource().type || '';
+    };
+
+    /**
+     * Get or set the preload attribute
+     *
+     * @param {boolean} [value]
+     *        - true means that we should preload
+     *        - false means that we should not preload
+     *
+     * @return {string}
+     *         The preload attribute value when getting
+     */
+
+
+    Player.prototype.preload = function preload(value) {
+      if (value !== undefined) {
+        this.techCall_('setPreload', value);
+        this.options_.preload = value;
+        return;
+      }
+      return this.techGet_('preload');
+    };
+
+    /**
+     * Get or set the autoplay attribute.
+     *
+     * @param {boolean} [value]
+     *        - true means that we should autoplay
+     *        - false means that we should not autoplay
+     *
+     * @return {string}
+     *         The current value of autoplay when getting
+     */
+
+
+    Player.prototype.autoplay = function autoplay(value) {
+      if (value !== undefined) {
+        this.techCall_('setAutoplay', value);
+        this.options_.autoplay = value;
+        return;
+      }
+      return this.techGet_('autoplay', value);
+    };
+
+    /**
+     * Set or unset the playsinline attribute.
+     * Playsinline tells the browser that non-fullscreen playback is preferred.
+     *
+     * @param {boolean} [value]
+     *        - true means that we should try to playOld inline by default
+     *        - false means that we should use the browser's default playback mode,
+     *          which in most cases is inline. iOS Safari is a notable exception
+     *          and plays fullscreen by default.
+     *
+     * @return {string|Player}
+     *         - the current value of playsinline
+     *         - the player when setting
+     *
+     * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
+     */
+
+
+    Player.prototype.playsinline = function playsinline(value) {
+      if (value !== undefined) {
+        this.techCall_('setPlaysinline', value);
+        this.options_.playsinline = value;
+        return this;
+      }
+      return this.techGet_('playsinline');
+    };
+
+    /**
+     * Get or set the loop attribute on the video element.
+     *
+     * @param {boolean} [value]
+     *        - true means that we should loop the video
+     *        - false means that we should not loop the video
+     *
+     * @return {string}
+     *         The current value of loop when getting
+     */
+
+
+    Player.prototype.loop = function loop(value) {
+      if (value !== undefined) {
+        this.techCall_('setLoop', value);
+        this.options_.loop = value;
+        return;
+      }
+      return this.techGet_('loop');
+    };
+
+    /**
+     * Get or set the poster image source url
+     *
+     * @fires Player#posterchange
+     *
+     * @param {string} [src]
+     *        Poster image source URL
+     *
+     * @return {string}
+     *         The current value of poster when getting
+     */
+
+
+    Player.prototype.poster = function poster(src) {
+      if (src === undefined) {
+        return this.poster_;
+      }
+
+      // The correct way to remove a poster is to set as an empty string
+      // other falsey values will throw errors
+      if (!src) {
+        src = '';
+      }
+
+      if (src === this.poster_) {
+        return;
+      }
+
+      // update the internal poster variable
+      this.poster_ = src;
+
+      // update the tech's poster
+      this.techCall_('setPoster', src);
+
+      this.isPosterFromTech_ = false;
+
+      // alert components that the poster has been set
+      /**
+       * This event fires when the poster image is changed on the player.
+       *
+       * @event Player#posterchange
+       * @type {EventTarget~Event}
+       */
+      this.trigger('posterchange');
+    };
+
+    /**
+     * Some techs (e.g. YouTube) can provide a poster source in an
+     * asynchronous way. We want the poster component to use this
+     * poster source so that it covers up the tech's controls.
+     * (YouTube's playOld button). However we only want to use this
+     * source if the player user hasn't set a poster through
+     * the normal APIs.
+     *
+     * @fires Player#posterchange
+     * @listens Tech#posterchange
+     * @private
+     */
+
+
+    Player.prototype.handleTechPosterChange_ = function handleTechPosterChange_() {
+      if ((!this.poster_ || this.options_.techCanOverridePoster) && this.tech_ && this.tech_.poster) {
+        var newPoster = this.tech_.poster() || '';
+
+        if (newPoster !== this.poster_) {
+          this.poster_ = newPoster;
+          this.isPosterFromTech_ = true;
+
+          // Let components know the poster has changed
+          this.trigger('posterchange');
+        }
+      }
+    };
+
+    /**
+     * Get or set whether or not the controls are showing.
+     *
+     * @fires Player#controlsenabled
+     *
+     * @param {boolean} [bool]
+     *        - true to turn controls on
+     *        - false to turn controls off
+     *
+     * @return {boolean}
+     *         The current value of controls when getting
+     */
+
+
+    Player.prototype.controls = function controls(bool) {
+      if (bool === undefined) {
+        return !!this.controls_;
+      }
+
+      bool = !!bool;
+
+      // Don't trigger a change event unless it actually changed
+      if (this.controls_ === bool) {
+        return;
+      }
+
+      this.controls_ = bool;
+
+      if (this.usingNativeControls()) {
+        this.techCall_('setControls', bool);
+      }
+
+      if (this.controls_) {
+        this.removeClass('vjs-controls-disabled');
+        this.addClass('vjs-controls-enabled');
+        /**
+         * @event Player#controlsenabled
+         * @type {EventTarget~Event}
+         */
+        this.trigger('controlsenabled');
+        if (!this.usingNativeControls()) {
+          this.addTechControlsListeners_();
+        }
+      } else {
+        this.removeClass('vjs-controls-enabled');
+        this.addClass('vjs-controls-disabled');
+        /**
+         * @event Player#controlsdisabled
+         * @type {EventTarget~Event}
+         */
+        this.trigger('controlsdisabled');
+        if (!this.usingNativeControls()) {
+          this.removeTechControlsListeners_();
+        }
+      }
+    };
+
+    /**
+     * Toggle native controls on/off. Native controls are the controls built into
+     * devices (e.g. default iPhone controls), Flash, or other techs
+     * (e.g. Vimeo Controls)
+     * **This should only be set by the current tech, because only the tech knows
+     * if it can support native controls**
+     *
+     * @fires Player#usingnativecontrols
+     * @fires Player#usingcustomcontrols
+     *
+     * @param {boolean} [bool]
+     *        - true to turn native controls on
+     *        - false to turn native controls off
+     *
+     * @return {boolean}
+     *         The current value of native controls when getting
+     */
+
+
+    Player.prototype.usingNativeControls = function usingNativeControls(bool) {
+      if (bool === undefined) {
+        return !!this.usingNativeControls_;
+      }
+
+      bool = !!bool;
+
+      // Don't trigger a change event unless it actually changed
+      if (this.usingNativeControls_ === bool) {
+        return;
+      }
+
+      this.usingNativeControls_ = bool;
+
+      if (this.usingNativeControls_) {
+        this.addClass('vjs-using-native-controls');
+
+        /**
+         * player is using the native device controls
+         *
+         * @event Player#usingnativecontrols
+         * @type {EventTarget~Event}
+         */
+        this.trigger('usingnativecontrols');
+      } else {
+        this.removeClass('vjs-using-native-controls');
+
+        /**
+         * player is using the custom HTML controls
+         *
+         * @event Player#usingcustomcontrols
+         * @type {EventTarget~Event}
+         */
+        this.trigger('usingcustomcontrols');
+      }
+    };
+
+    /**
+     * Set or get the current MediaError
+     *
+     * @fires Player#error
+     *
+     * @param  {MediaError|string|number} [err]
+     *         A MediaError or a string/number to be turned
+     *         into a MediaError
+     *
+     * @return {MediaError|null}
+     *         The current MediaError when getting (or null)
+     */
+
+
+    Player.prototype.error = function error(err) {
+      if (err === undefined) {
+        return this.error_ || null;
+      }
+
+      // restoring to default
+      if (err === null) {
+        this.error_ = err;
+        this.removeClass('vjs-error');
+        if (this.errorDisplay) {
+          this.errorDisplay.close();
+        }
+        return;
+      }
+
+      this.error_ = new MediaError(err);
+
+      // add the vjs-error classname to the player
+      this.addClass('vjs-error');
+
+      // log the name of the error type and any message
+      // IE11 logs "[object object]" and required you to expand message to see error object
+      log$1.error('(CODE:' + this.error_.code + ' ' + MediaError.errorTypes[this.error_.code] + ')', this.error_.message, this.error_);
+
+      /**
+       * @event Player#error
+       * @type {EventTarget~Event}
+       */
+      this.trigger('error');
+
+      return;
+    };
+
+    /**
+     * Report user activity
+     *
+     * @param {Object} event
+     *        Event object
+     */
+
+
+    Player.prototype.reportUserActivity = function reportUserActivity(event) {
+      this.userActivity_ = true;
+    };
+
+    /**
+     * Get/set if user is active
+     *
+     * @fires Player#useractive
+     * @fires Player#userinactive
+     *
+     * @param {boolean} [bool]
+     *        - true if the user is active
+     *        - false if the user is inactive
+     *
+     * @return {boolean}
+     *         The current value of userActive when getting
+     */
+
+
+    Player.prototype.userActive = function userActive(bool) {
+      if (bool === undefined) {
+        return this.userActive_;
+      }
+
+      bool = !!bool;
+
+      if (bool === this.userActive_) {
+        return;
+      }
+
+      this.userActive_ = bool;
+
+      if (this.userActive_) {
+        this.userActivity_ = true;
+        this.removeClass('vjs-user-inactive');
+        this.addClass('vjs-user-active');
+        /**
+         * @event Player#useractive
+         * @type {EventTarget~Event}
+         */
+        this.trigger('useractive');
+        return;
+      }
+
+      // Chrome/Safari/IE have bugs where when you change the cursor it can
+      // trigger a mousemove event. This causes an issue when you're hiding
+      // the cursor when the user is inactive, and a mousemove signals user
+      // activity. Making it impossible to go into inactive mode. Specifically
+      // this happens in fullscreen when we really need to hide the cursor.
+      //
+      // When this gets resolved in ALL browsers it can be removed
+      // https://code.google.com/p/chromium/issues/detail?id=103041
+      if (this.tech_) {
+        this.tech_.one('mousemove', function (e) {
+          e.stopPropagation();
+          e.preventDefault();
+        });
+      }
+
+      this.userActivity_ = false;
+      this.removeClass('vjs-user-active');
+      this.addClass('vjs-user-inactive');
+      /**
+       * @event Player#userinactive
+       * @type {EventTarget~Event}
+       */
+      this.trigger('userinactive');
+    };
+
+    /**
+     * Listen for user activity based on timeout value
+     *
+     * @private
+     */
+
+
+    Player.prototype.listenForUserActivity_ = function listenForUserActivity_() {
+      var mouseInProgress = void 0;
+      var lastMoveX = void 0;
+      var lastMoveY = void 0;
+      var handleActivity = bind(this, this.reportUserActivity);
+
+      var handleMouseMove = function handleMouseMove(e) {
+        // #1068 - Prevent mousemove spamming
+        // Chrome Bug: https://code.google.com/p/chromium/issues/detail?id=366970
+        if (e.screenX !== lastMoveX || e.screenY !== lastMoveY) {
+          lastMoveX = e.screenX;
+          lastMoveY = e.screenY;
+          handleActivity();
+        }
+      };
+
+      var handleMouseDown = function handleMouseDown() {
+        handleActivity();
+        // 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(mouseInProgress);
+        // Setting userActivity=true now and setting the interval to the same time
+        // as the activityCheck interval (250) should ensure we never miss the
+        // next activityCheck
+        mouseInProgress = this.setInterval(handleActivity, 250);
+      };
+
+      var handleMouseUp = function handleMouseUp(event) {
+        handleActivity();
+        // Stop the interval that maintains activity if the mouse/touch is down
+        this.clearInterval(mouseInProgress);
+      };
+
+      // Any mouse movement will be considered user activity
+      this.on('mousedown', handleMouseDown);
+      this.on('mousemove', handleMouseMove);
+      this.on('mouseup', handleMouseUp);
+
+      // Listen for keyboard navigation
+      // Shouldn't need to use inProgress interval because of key repeat
+      this.on('keydown', handleActivity);
+      this.on('keyup', handleActivity);
+
+      // Run an interval every 250 milliseconds instead of stuffing everything into
+      // the mousemove/touchmove function itself, to prevent performance degradation.
+      // `this.reportUserActivity` simply sets this.userActivity_ to true, which
+      // then gets picked up by this loop
+      // http://ejohn.org/blog/learning-from-twitter/
+      var inactivityTimeout = void 0;
+
+      this.setInterval(function () {
+        // Check to see if mouse/touch activity has happened
+        if (!this.userActivity_) {
+          return;
+        }
+
+        // Reset the activity tracker
+        this.userActivity_ = false;
+
+        // If the user state was inactive, set the state to active
+        this.userActive(true);
+
+        // Clear any existing inactivity timeout to start the timer over
+        this.clearTimeout(inactivityTimeout);
+
+        var timeout = this.options_.inactivityTimeout;
+
+        if (timeout <= 0) {
+          return;
+        }
+
+        // In <timeout> milliseconds, if no more activity has occurred the
+        // user will be considered inactive
+        inactivityTimeout = this.setTimeout(function () {
+          // Protect against the case where the inactivityTimeout can trigger just
+          // before the next user activity is picked up by the activity check loop
+          // causing a flicker
+          if (!this.userActivity_) {
+            this.userActive(false);
+          }
+        }, timeout);
+      }, 250);
+    };
+
+    /**
+     * Gets or sets the current playback rate. A playback rate of
+     * 1.0 represents normal speed and 0.5 would indicate half-speed
+     * playback, for instance.
+     *
+     * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-playbackrate
+     *
+     * @param {number} [rate]
+     *       New playback rate to set.
+     *
+     * @return {number}
+     *         The current playback rate when getting or 1.0
+     */
+
+
+    Player.prototype.playbackRate = function playbackRate(rate) {
+      if (rate !== undefined) {
+        // NOTE: this.cache_.lastPlaybackRate is set from the tech handler
+        // that is registered above
+        this.techCall_('setPlaybackRate', rate);
+        return;
+      }
+
+      if (this.tech_ && this.tech_.featuresPlaybackRate) {
+        return this.cache_.lastPlaybackRate || this.techGet_('playbackRate');
+      }
+      return 1.0;
+    };
+
+    /**
+     * Gets or sets the current default playback rate. A default playback rate of
+     * 1.0 represents normal speed and 0.5 would indicate half-speed playback, for instance.
+     * defaultPlaybackRate will only represent what the initial playbackRate of a video was, not
+     * not the current playbackRate.
+     *
+     * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-defaultplaybackrate
+     *
+     * @param {number} [rate]
+     *       New default playback rate to set.
+     *
+     * @return {number|Player}
+     *         - The default playback rate when getting or 1.0
+     *         - the player when setting
+     */
+
+
+    Player.prototype.defaultPlaybackRate = function defaultPlaybackRate(rate) {
+      if (rate !== undefined) {
+        return this.techCall_('setDefaultPlaybackRate', rate);
+      }
+
+      if (this.tech_ && this.tech_.featuresPlaybackRate) {
+        return this.techGet_('defaultPlaybackRate');
+      }
+      return 1.0;
+    };
+
+    /**
+     * Gets or sets the audio flag
+     *
+     * @param {boolean} bool
+     *        - true signals that this is an audio player
+     *        - false signals that this is not an audio player
+     *
+     * @return {boolean}
+     *         The current value of isAudio when getting
+     */
+
+
+    Player.prototype.isAudio = function isAudio(bool) {
+      if (bool !== undefined) {
+        this.isAudio_ = !!bool;
+        return;
+      }
+
+      return !!this.isAudio_;
+    };
+
+    /**
+     * A helper method for adding a {@link TextTrack} to our
+     * {@link TextTrackList}.
+     *
+     * In addition to the W3C settings we allow adding additional info through options.
+     *
+     * @see http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-addtexttrack
+     *
+     * @param {string} [kind]
+     *        the kind of TextTrack you are adding
+     *
+     * @param {string} [label]
+     *        the label to give the TextTrack label
+     *
+     * @param {string} [language]
+     *        the language to set on the TextTrack
+     *
+     * @return {TextTrack|undefined}
+     *         the TextTrack that was added or undefined
+     *         if there is no tech
+     */
+
+
+    Player.prototype.addTextTrack = function addTextTrack(kind, label, language) {
+      if (this.tech_) {
+        return this.tech_.addTextTrack(kind, label, language);
+      }
+    };
+
+    /**
+     * Create a remote {@link TextTrack} and an {@link HTMLTrackElement}. It will
+     * automatically removed from the video element whenever the source changes, unless
+     * manualCleanup is set to false.
+     *
+     * @param {Object} options
+     *        Options to pass to {@link HTMLTrackElement} during creation. See
+     *        {@link HTMLTrackElement} for object properties that you should use.
+     *
+     * @param {boolean} [manualCleanup=true] if set to false, the TextTrack will be
+     *
+     * @return {HtmlTrackElement}
+     *         the HTMLTrackElement that was created and added
+     *         to the HtmlTrackElementList and the remote
+     *         TextTrackList
+     *
+     * @deprecated The default value of the "manualCleanup" parameter will default
+     *             to "false" in upcoming versions of Video.js
+     */
+
+
+    Player.prototype.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
+      if (this.tech_) {
+        return this.tech_.addRemoteTextTrack(options, manualCleanup);
+      }
+    };
+
+    /**
+     * Remove a remote {@link TextTrack} from the respective
+     * {@link TextTrackList} and {@link HtmlTrackElementList}.
+     *
+     * @param {Object} track
+     *        Remote {@link TextTrack} to remove
+     *
+     * @return {undefined}
+     *         does not return anything
+     */
+
+
+    Player.prototype.removeRemoteTextTrack = function removeRemoteTextTrack() {
+      var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
+        _ref3$track = _ref3.track,
+        track = _ref3$track === undefined ? arguments[0] : _ref3$track;
+
+      // destructure the input into an object with a track argument, defaulting to arguments[0]
+      // default the whole argument to an empty object if nothing was passed in
+
+      if (this.tech_) {
+        return this.tech_.removeRemoteTextTrack(track);
+      }
+    };
+
+    /**
+     * Gets available media playback quality metrics as specified by the W3C's Media
+     * Playback Quality API.
+     *
+     * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
+     *
+     * @return {Object|undefined}
+     *         An object with supported media playback quality metrics or undefined if there
+     *         is no tech or the tech does not support it.
+     */
+
+
+    Player.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
+      return this.techGet_('getVideoPlaybackQuality');
+    };
+
+    /**
+     * Get video width
+     *
+     * @return {number}
+     *         current video width
+     */
+
+
+    Player.prototype.videoWidth = function videoWidth() {
+      return this.tech_ && this.tech_.videoWidth && this.tech_.videoWidth() || 0;
+    };
+
+    /**
+     * Get video height
+     *
+     * @return {number}
+     *         current video height
+     */
+
+
+    Player.prototype.videoHeight = function videoHeight() {
+      return this.tech_ && this.tech_.videoHeight && this.tech_.videoHeight() || 0;
+    };
+
+    /**
+     * The player's language code
+     * NOTE: The language should be set in the player options if you want the
+     * the controls to be built with a specific language. Changing the language
+     * later will not update controls text.
+     *
+     * @param {string} [code]
+     *        the language code to set the player to
+     *
+     * @return {string}
+     *         The current language code when getting
+     */
+
+
+    Player.prototype.language = function language(code) {
+      if (code === undefined) {
+        return this.language_;
+      }
+
+      this.language_ = String(code).toLowerCase();
+    };
+
+    /**
+     * Get the player's language dictionary
+     * Merge every time, because a newly added plugin might call videojs.addLanguage() at any time
+     * Languages specified directly in the player options have precedence
+     *
+     * @return {Array}
+     *         An array of of supported languages
+     */
+
+
+    Player.prototype.languages = function languages() {
+      return mergeOptions(Player.prototype.options_.languages, this.languages_);
+    };
+
+    /**
+     * returns a JavaScript object reperesenting the current track
+     * information. **DOES not return it as JSON**
+     *
+     * @return {Object}
+     *         Object representing the current of track info
+     */
+
+
+    Player.prototype.toJSON = function toJSON() {
+      var options = mergeOptions(this.options_);
+      var tracks = options.tracks;
+
+      options.tracks = [];
+
+      for (var i = 0; i < tracks.length; i++) {
+        var track = tracks[i];
+
+        // deep merge tracks and null out player so no circular references
+        track = mergeOptions(track);
+        track.player = undefined;
+        options.tracks[i] = track;
+      }
+
+      return options;
+    };
+
+    /**
+     * Creates a simple modal dialog (an instance of the {@link ModalDialog}
+     * component) that immediately overlays the player with arbitrary
+     * content and removes itself when closed.
+     *
+     * @param {string|Function|Element|Array|null} content
+     *        Same as {@link ModalDialog#content}'s param of the same name.
+     *        The most straight-forward usage is to provide a string or DOM
+     *        element.
+     *
+     * @param {Object} [options]
+     *        Extra options which will be passed on to the {@link ModalDialog}.
+     *
+     * @return {ModalDialog}
+     *         the {@link ModalDialog} that was created
+     */
+
+
+    Player.prototype.createModal = function createModal(content, options) {
+      var _this10 = this;
+
+      options = options || {};
+      options.content = content || '';
+
+      var modal = new ModalDialog(this, options);
+
+      this.addChild(modal);
+      modal.on('dispose', function () {
+        _this10.removeChild(modal);
+      });
+
+      modal.open();
+      return modal;
+    };
+
+    /**
+     * Gets tag settings
+     *
+     * @param {Element} tag
+     *        The player tag
+     *
+     * @return {Object}
+     *         An object containing all of the settings
+     *         for a player tag
+     */
+
+
+    Player.getTagSettings = function getTagSettings(tag) {
+      var baseOptions = {
+        sources: [],
+        tracks: []
+      };
+
+      var tagOptions = getAttributes(tag);
+      var dataSetup = tagOptions['data-setup'];
+
+      if (hasClass(tag, 'vjs-fluid')) {
+        tagOptions.fluid = true;
+      }
+
+      // Check if data-setup attr exists.
+      if (dataSetup !== null) {
+        // Parse options JSON
+        // If empty string, make it a parsable json object.
+        var _safeParseTuple = tuple(dataSetup || '{}'),
+          err = _safeParseTuple[0],
+          data = _safeParseTuple[1];
+
+        if (err) {
+          log$1.error(err);
+        }
+        assign(tagOptions, data);
+      }
+
+      assign(baseOptions, tagOptions);
+
+      // Get tag children settings
+      if (tag.hasChildNodes()) {
+        var children = tag.childNodes;
+
+        for (var i = 0, j = children.length; i < j; i++) {
+          var child = children[i];
+          // Change case needed: http://ejohn.org/blog/nodename-case-sensitivity/
+          var childName = child.nodeName.toLowerCase();
+
+          if (childName === 'source') {
+            baseOptions.sources.push(getAttributes(child));
+          } else if (childName === 'track') {
+            baseOptions.tracks.push(getAttributes(child));
+          }
+        }
+      }
+
+      return baseOptions;
+    };
+
+    /**
+     * Determine whether or not flexbox is supported
+     *
+     * @return {boolean}
+     *         - true if flexbox is supported
+     *         - false if flexbox is not supported
+     */
+
+
+    Player.prototype.flexNotSupported_ = function flexNotSupported_() {
+      var elem = document_1.createElement('i');
+
+      // Note: We don't actually use flexBasis (or flexOrder), but it's one of the more
+      // common flex features that we can rely on when checking for flex support.
+      return !('flexBasis' in elem.style || 'webkitFlexBasis' in elem.style || 'mozFlexBasis' in elem.style || 'msFlexBasis' in elem.style ||
+        // IE10-specific (2012 flex spec), available for completeness
+        'msFlexOrder' in elem.style);
+    };
+
+    return Player;
+  }(Component);
+
+  /**
+   * Get the {@link VideoTrackList}
+   * @link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist
+   *
+   * @return {VideoTrackList}
+   *         the current video track list
+   *
+   * @method Player.prototype.videoTracks
+   */
+
+  /**
+   * Get the {@link AudioTrackList}
+   * @link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist
+   *
+   * @return {AudioTrackList}
+   *         the current audio track list
+   *
+   * @method Player.prototype.audioTracks
+   */
+
+  /**
+   * Get the {@link TextTrackList}
+   *
+   * @link http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-texttracks
+   *
+   * @return {TextTrackList}
+   *         the current text track list
+   *
+   * @method Player.prototype.textTracks
+   */
+
+  /**
+   * Get the remote {@link TextTrackList}
+   *
+   * @return {TextTrackList}
+   *         The current remote text track list
+   *
+   * @method Player.prototype.remoteTextTracks
+   */
+
+  /**
+   * Get the remote {@link HtmlTrackElementList} tracks.
+   *
+   * @return {HtmlTrackElementList}
+   *         The current remote text track element list
+   *
+   * @method Player.prototype.remoteTextTrackEls
+   */
+
+  ALL.names.forEach(function (name$$1) {
+    var props = ALL[name$$1];
+
+    Player.prototype[props.getterName] = function () {
+      if (this.tech_) {
+        return this.tech_[props.getterName]();
+      }
+
+      // if we have not yet loadTech_, we create {video,audio,text}Tracks_
+      // these will be passed to the tech during loading
+      this[props.privateName] = this[props.privateName] || new props.ListClass();
+      return this[props.privateName];
+    };
+  });
+
+  /**
+   * Global player list
+   *
+   * @type {Object}
+   */
+  Player.players = {};
+
+  var navigator = window_1.navigator;
+
+  /*
+   * Player instance options, surfaced using options
+   * options = Player.prototype.options_
+   * Make changes in options, not here.
+   *
+   * @type {Object}
+   * @private
+   */
+  Player.prototype.options_ = {
+    // Default order of fallback technology
+    techOrder: Tech.defaultTechOrder_,
+
+    html5: {},
+    flash: {},
+
+    // default inactivity timeout
+    inactivityTimeout: 2000,
+
+    // default playback rates
+    playbackRates: [],
+    // Add playback rate selection by adding rates
+    // 'playbackRates': [0.5, 1, 1.5, 2],
+
+    // Included control sets
+    children: ['mediaLoader', 'posterImage', 'textTrackDisplay', 'loadingSpinner', 'bigPlayButton', 'controlBar', 'errorDisplay', 'textTrackSettings', 'resizeManager'],
+
+    language: navigator && (navigator.languages && navigator.languages[0] || navigator.userLanguage || navigator.language) || 'en',
+
+    // locales and their language translations
+    languages: {},
+
+    // Default message to show when a video cannot be played.
+    notSupportedMessage: 'No compatible source was found for this media.'
+  };
+
+  [
+    /**
+     * Returns whether or not the player is in the "ended" state.
+     *
+     * @return {Boolean} True if the player is in the ended state, false if not.
+     * @method Player#ended
+     */
+    'ended',
+    /**
+     * Returns whether or not the player is in the "seeking" state.
+     *
+     * @return {Boolean} True if the player is in the seeking state, false if not.
+     * @method Player#seeking
+     */
+    'seeking',
+    /**
+     * Returns the TimeRanges of the media that are currently available
+     * for seeking to.
+     *
+     * @return {TimeRanges} the seekable intervals of the media timeline
+     * @method Player#seekable
+     */
+    'seekable',
+    /**
+     * Returns the current state of network activity for the element, from
+     * the codes in the list below.
+     * - NETWORK_EMPTY (numeric value 0)
+     *   The element has not yet been initialised. All attributes are in
+     *   their initial states.
+     * - NETWORK_IDLE (numeric value 1)
+     *   The element's resource selection algorithm is active and has
+     *   selected a resource, but it is not actually using the network at
+     *   this time.
+     * - NETWORK_LOADING (numeric value 2)
+     *   The user agent is actively trying to download data.
+     * - NETWORK_NO_SOURCE (numeric value 3)
+     *   The element's resource selection algorithm is active, but it has
+     *   not yet found a resource to use.
+     *
+     * @see https://html.spec.whatwg.org/multipage/embedded-content.html#network-states
+     * @return {number} the current network activity state
+     * @method Player#networkState
+     */
+    'networkState',
+    /**
+     * Returns a value that expresses the current state of the element
+     * with respect to rendering the current playback position, from the
+     * codes in the list below.
+     * - HAVE_NOTHING (numeric value 0)
+     *   No information regarding the media resource is available.
+     * - HAVE_METADATA (numeric value 1)
+     *   Enough of the resource has been obtained that the duration of the
+     *   resource is available.
+     * - HAVE_CURRENT_DATA (numeric value 2)
+     *   Data for the immediate current playback position is available.
+     * - HAVE_FUTURE_DATA (numeric value 3)
+     *   Data for the immediate current playback position is available, as
+     *   well as enough data for the user agent to advance the current
+     *   playback position in the direction of playback.
+     * - HAVE_ENOUGH_DATA (numeric value 4)
+     *   The user agent estimates that enough data is available for
+     *   playback to proceed uninterrupted.
+     *
+     * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-readystate
+     * @return {number} the current playback rendering state
+     * @method Player#readyState
+     */
+    'readyState'].forEach(function (fn) {
+    Player.prototype[fn] = function () {
+      return this.techGet_(fn);
+    };
+  });
+
+  TECH_EVENTS_RETRIGGER.forEach(function (event) {
+    Player.prototype['handleTech' + toTitleCase(event) + '_'] = function () {
+      return this.trigger(event);
+    };
+  });
+
+  /**
+   * Fired when the player has initial duration and dimension information
+   *
+   * @event Player#loadedmetadata
+   * @type {EventTarget~Event}
+   */
+
+  /**
+   * Fired when the player has downloaded data at the current playback position
+   *
+   * @event Player#loadeddata
+   * @type {EventTarget~Event}
+   */
+
+  /**
+   * Fired when the current playback position has changed *
+   * During playback this is fired every 15-250 milliseconds, depending on the
+   * playback technology in use.
+   *
+   * @event Player#timeupdate
+   * @type {EventTarget~Event}
+   */
+
+  /**
+   * Fired when the volume changes
+   *
+   * @event Player#volumechange
+   * @type {EventTarget~Event}
+   */
+
+  /**
+   * Reports whether or not a player has a plugin available.
+   *
+   * This does not report whether or not the plugin has ever been initialized
+   * on this player. For that, [usingPlugin]{@link Player#usingPlugin}.
+   *
+   * @method Player#hasPlugin
+   * @param  {string}  name
+   *         The name of a plugin.
+   *
+   * @return {boolean}
+   *         Whether or not this player has the requested plugin available.
+   */
+
+  /**
+   * Reports whether or not a player is using a plugin by name.
+   *
+   * For basic plugins, this only reports whether the plugin has _ever_ been
+   * initialized on this player.
+   *
+   * @method Player#usingPlugin
+   * @param  {string} name
+   *         The name of a plugin.
+   *
+   * @return {boolean}
+   *         Whether or not this player is using the requested plugin.
+   */
+
+  Component.registerComponent('Player', Player);
+
+  /**
+   * @file plugin.js
+   */
+
+  /**
+   * The base plugin name.
+   *
+   * @private
+   * @constant
+   * @type {string}
+   */
+  var BASE_PLUGIN_NAME = 'plugin';
+
+  /**
+   * The key on which a player's active plugins cache is stored.
+   *
+   * @private
+   * @constant
+   * @type     {string}
+   */
+  var PLUGIN_CACHE_KEY = 'activePlugins_';
+
+  /**
+   * Stores registered plugins in a private space.
+   *
+   * @private
+   * @type    {Object}
+   */
+  var pluginStorage = {};
+
+  /**
+   * Reports whether or not a plugin has been registered.
+   *
+   * @private
+   * @param   {string} name
+   *          The name of a plugin.
+   *
+   * @returns {boolean}
+   *          Whether or not the plugin has been registered.
+   */
+  var pluginExists = function pluginExists(name) {
+    return pluginStorage.hasOwnProperty(name);
+  };
+
+  /**
+   * Get a single registered plugin by name.
+   *
+   * @private
+   * @param   {string} name
+   *          The name of a plugin.
+   *
+   * @returns {Function|undefined}
+   *          The plugin (or undefined).
+   */
+  var getPlugin = function getPlugin(name) {
+    return pluginExists(name) ? pluginStorage[name] : undefined;
+  };
+
+  /**
+   * Marks a plugin as "active" on a player.
+   *
+   * Also, ensures that the player has an object for tracking active plugins.
+   *
+   * @private
+   * @param   {Player} player
+   *          A Video.js player instance.
+   *
+   * @param   {string} name
+   *          The name of a plugin.
+   */
+  var markPluginAsActive = function markPluginAsActive(player, name) {
+    player[PLUGIN_CACHE_KEY] = player[PLUGIN_CACHE_KEY] || {};
+    player[PLUGIN_CACHE_KEY][name] = true;
+  };
+
+  /**
+   * Triggers a pair of plugin setup events.
+   *
+   * @private
+   * @param  {Player} player
+   *         A Video.js player instance.
+   *
+   * @param  {Plugin~PluginEventHash} hash
+   *         A plugin event hash.
+   *
+   * @param  {Boolean} [before]
+   *         If true, prefixes the event name with "before". In other words,
+   *         use this to trigger "beforepluginsetup" instead of "pluginsetup".
+   */
+  var triggerSetupEvent = function triggerSetupEvent(player, hash, before) {
+    var eventName = (before ? 'before' : '') + 'pluginsetup';
+
+    player.trigger(eventName, hash);
+    player.trigger(eventName + ':' + hash.name, hash);
+  };
+
+  /**
+   * Takes a basic plugin function and returns a wrapper function which marks
+   * on the player that the plugin has been activated.
+   *
+   * @private
+   * @param   {string} name
+   *          The name of the plugin.
+   *
+   * @param   {Function} plugin
+   *          The basic plugin.
+   *
+   * @returns {Function}
+   *          A wrapper function for the given plugin.
+   */
+  var createBasicPlugin = function createBasicPlugin(name, plugin) {
+    var basicPluginWrapper = function basicPluginWrapper() {
+
+      // We trigger the "beforepluginsetup" and "pluginsetup" events on the player
+      // regardless, but we want the hash to be consistent with the hash provided
+      // for advanced plugins.
+      //
+      // The only potentially counter-intuitive thing here is the `instance` in
+      // the "pluginsetup" event is the value returned by the `plugin` function.
+      triggerSetupEvent(this, {name: name, plugin: plugin, instance: null}, true);
+
+      var instance = plugin.apply(this, arguments);
+
+      markPluginAsActive(this, name);
+      triggerSetupEvent(this, {name: name, plugin: plugin, instance: instance});
+
+      return instance;
+    };
+
+    Object.keys(plugin).forEach(function (prop) {
+      basicPluginWrapper[prop] = plugin[prop];
+    });
+
+    return basicPluginWrapper;
+  };
+
+  /**
+   * Takes a plugin sub-class and returns a factory function for generating
+   * instances of it.
+   *
+   * This factory function will replace itself with an instance of the requested
+   * sub-class of Plugin.
+   *
+   * @private
+   * @param   {string} name
+   *          The name of the plugin.
+   *
+   * @param   {Plugin} PluginSubClass
+   *          The advanced plugin.
+   *
+   * @returns {Function}
+   */
+  var createPluginFactory = function createPluginFactory(name, PluginSubClass) {
+
+    // Add a `name` property to the plugin prototype so that each plugin can
+    // refer to itself by name.
+    PluginSubClass.prototype.name = name;
+
+    return function () {
+      triggerSetupEvent(this, {name: name, plugin: PluginSubClass, instance: null}, true);
+
+      for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+        args[_key] = arguments[_key];
+      }
+
+      var instance = new (Function.prototype.bind.apply(PluginSubClass, [null].concat([this].concat(args))))();
+
+      // The plugin is replaced by a function that returns the current instance.
+      this[name] = function () {
+        return instance;
+      };
+
+      triggerSetupEvent(this, instance.getEventHash());
+
+      return instance;
+    };
+  };
+
+  /**
+   * Parent class for all advanced plugins.
+   *
+   * @mixes   module:evented~EventedMixin
+   * @mixes   module:stateful~StatefulMixin
+   * @fires   Player#beforepluginsetup
+   * @fires   Player#beforepluginsetup:$name
+   * @fires   Player#pluginsetup
+   * @fires   Player#pluginsetup:$name
+   * @listens Player#dispose
+   * @throws  {Error}
+   *          If attempting to instantiate the base {@link Plugin} class
+   *          directly instead of via a sub-class.
+   */
+
+  var Plugin = function () {
+
+    /**
+     * Creates an instance of this class.
+     *
+     * Sub-classes should call `super` to ensure plugins are properly initialized.
+     *
+     * @param {Player} player
+     *        A Video.js player instance.
+     */
+    function Plugin(player) {
+      classCallCheck(this, Plugin);
+
+      if (this.constructor === Plugin) {
+        throw new Error('Plugin must be sub-classed; not directly instantiated.');
+      }
+
+      this.player = player;
+
+      // Make this object evented, but remove the added `trigger` method so we
+      // use the prototype version instead.
+      evented(this);
+      delete this.trigger;
+
+      stateful(this, this.constructor.defaultState);
+      markPluginAsActive(player, this.name);
+
+      // Auto-bind the dispose method so we can use it as a listener and unbind
+      // it later easily.
+      this.dispose = bind(this, this.dispose);
+
+      // If the player is disposed, dispose the plugin.
+      player.on('dispose', this.dispose);
+    }
+
+    /**
+     * Get the version of the plugin that was set on <pluginName>.VERSION
+     */
+
+
+    Plugin.prototype.version = function version() {
+      return this.constructor.VERSION;
+    };
+
+    /**
+     * Each event triggered by plugins includes a hash of additional data with
+     * conventional properties.
+     *
+     * This returns that object or mutates an existing hash.
+     *
+     * @param   {Object} [hash={}]
+     *          An object to be used as event an event hash.
+     *
+     * @returns {Plugin~PluginEventHash}
+     *          An event hash object with provided properties mixed-in.
+     */
+
+
+    Plugin.prototype.getEventHash = function getEventHash() {
+      var hash = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+
+      hash.name = this.name;
+      hash.plugin = this.constructor;
+      hash.instance = this;
+      return hash;
+    };
+
+    /**
+     * Triggers an event on the plugin object and overrides
+     * {@link module:evented~EventedMixin.trigger|EventedMixin.trigger}.
+     *
+     * @param   {string|Object} event
+     *          An event type or an object with a type property.
+     *
+     * @param   {Object} [hash={}]
+     *          Additional data hash to merge with a
+     *          {@link Plugin~PluginEventHash|PluginEventHash}.
+     *
+     * @returns {boolean}
+     *          Whether or not default was prevented.
+     */
+
+
+    Plugin.prototype.trigger = function trigger$$1(event) {
+      var hash = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+
+      return trigger(this.eventBusEl_, event, this.getEventHash(hash));
+    };
+
+    /**
+     * Handles "statechanged" events on the plugin. No-op by default, override by
+     * subclassing.
+     *
+     * @abstract
+     * @param    {Event} e
+     *           An event object provided by a "statechanged" event.
+     *
+     * @param    {Object} e.changes
+     *           An object describing changes that occurred with the "statechanged"
+     *           event.
+     */
+
+
+    Plugin.prototype.handleStateChanged = function handleStateChanged(e) {
+    };
+
+    /**
+     * Disposes a plugin.
+     *
+     * Subclasses can override this if they want, but for the sake of safety,
+     * it's probably best to subscribe the "dispose" event.
+     *
+     * @fires Plugin#dispose
+     */
+
+
+    Plugin.prototype.dispose = function dispose() {
+      var name = this.name,
+        player = this.player;
+
+      /**
+       * Signals that a advanced plugin is about to be disposed.
+       *
+       * @event Plugin#dispose
+       * @type  {EventTarget~Event}
+       */
+
+      this.trigger('dispose');
+      this.off();
+      player.off('dispose', this.dispose);
+
+      // Eliminate any possible sources of leaking memory by clearing up
+      // references between the player and the plugin instance and nulling out
+      // the plugin's state and replacing methods with a function that throws.
+      player[PLUGIN_CACHE_KEY][name] = false;
+      this.player = this.state = null;
+
+      // Finally, replace the plugin name on the player with a new factory
+      // function, so that the plugin is ready to be set up again.
+      player[name] = createPluginFactory(name, pluginStorage[name]);
+    };
+
+    /**
+     * Determines if a plugin is a basic plugin (i.e. not a sub-class of `Plugin`).
+     *
+     * @param   {string|Function} plugin
+     *          If a string, matches the name of a plugin. If a function, will be
+     *          tested directly.
+     *
+     * @returns {boolean}
+     *          Whether or not a plugin is a basic plugin.
+     */
+
+
+    Plugin.isBasic = function isBasic(plugin) {
+      var p = typeof plugin === 'string' ? getPlugin(plugin) : plugin;
+
+      return typeof p === 'function' && !Plugin.prototype.isPrototypeOf(p.prototype);
+    };
+
+    /**
+     * Register a Video.js plugin.
+     *
+     * @param   {string} name
+     *          The name of the plugin to be registered. Must be a string and
+     *          must not match an existing plugin or a method on the `Player`
+     *          prototype.
+     *
+     * @param   {Function} plugin
+     *          A sub-class of `Plugin` or a function for basic plugins.
+     *
+     * @returns {Function}
+     *          For advanced plugins, a factory function for that plugin. For
+     *          basic plugins, a wrapper function that initializes the plugin.
+     */
+
+
+    Plugin.registerPlugin = function registerPlugin(name, plugin) {
+      if (typeof name !== 'string') {
+        throw new Error('Illegal plugin name, "' + name + '", must be a string, was ' + (typeof name === 'undefined' ? 'undefined' : _typeof(name)) + '.');
+      }
+
+      if (pluginExists(name)) {
+        log$1.warn('A plugin named "' + name + '" already exists. You may want to avoid re-registering plugins!');
+      } else if (Player.prototype.hasOwnProperty(name)) {
+        throw new Error('Illegal plugin name, "' + name + '", cannot share a name with an existing player method!');
+      }
+
+      if (typeof plugin !== 'function') {
+        throw new Error('Illegal plugin for "' + name + '", must be a function, was ' + (typeof plugin === 'undefined' ? 'undefined' : _typeof(plugin)) + '.');
+      }
+
+      pluginStorage[name] = plugin;
+
+      // Add a player prototype method for all sub-classed plugins (but not for
+      // the base Plugin class).
+      if (name !== BASE_PLUGIN_NAME) {
+        if (Plugin.isBasic(plugin)) {
+          Player.prototype[name] = createBasicPlugin(name, plugin);
+        } else {
+          Player.prototype[name] = createPluginFactory(name, plugin);
+        }
+      }
+
+      return plugin;
+    };
+
+    /**
+     * De-register a Video.js plugin.
+     *
+     * @param {string} name
+     *        The name of the plugin to be deregistered.
+     */
+
+
+    Plugin.deregisterPlugin = function deregisterPlugin(name) {
+      if (name === BASE_PLUGIN_NAME) {
+        throw new Error('Cannot de-register base plugin.');
+      }
+      if (pluginExists(name)) {
+        delete pluginStorage[name];
+        delete Player.prototype[name];
+      }
+    };
+
+    /**
+     * Gets an object containing multiple Video.js plugins.
+     *
+     * @param   {Array} [names]
+     *          If provided, should be an array of plugin names. Defaults to _all_
+     *          plugin names.
+     *
+     * @returns {Object|undefined}
+     *          An object containing plugin(s) associated with their name(s) or
+     *          `undefined` if no matching plugins exist).
+     */
+
+
+    Plugin.getPlugins = function getPlugins() {
+      var names = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : Object.keys(pluginStorage);
+
+      var result = void 0;
+
+      names.forEach(function (name) {
+        var plugin = getPlugin(name);
+
+        if (plugin) {
+          result = result || {};
+          result[name] = plugin;
+        }
+      });
+
+      return result;
+    };
+
+    /**
+     * Gets a plugin's version, if available
+     *
+     * @param   {string} name
+     *          The name of a plugin.
+     *
+     * @returns {string}
+     *          The plugin's version or an empty string.
+     */
+
+
+    Plugin.getPluginVersion = function getPluginVersion(name) {
+      var plugin = getPlugin(name);
+
+      return plugin && plugin.VERSION || '';
+    };
+
+    return Plugin;
+  }();
+
+  /**
+   * Gets a plugin by name if it exists.
+   *
+   * @static
+   * @method   getPlugin
+   * @memberOf Plugin
+   * @param    {string} name
+   *           The name of a plugin.
+   *
+   * @returns  {Function|undefined}
+   *           The plugin (or `undefined`).
+   */
+
+
+  Plugin.getPlugin = getPlugin;
+
+  /**
+   * The name of the base plugin class as it is registered.
+   *
+   * @type {string}
+   */
+  Plugin.BASE_PLUGIN_NAME = BASE_PLUGIN_NAME;
+
+  Plugin.registerPlugin(BASE_PLUGIN_NAME, Plugin);
+
+  /**
+   * Documented in player.js
+   *
+   * @ignore
+   */
+  Player.prototype.usingPlugin = function (name) {
+    return !!this[PLUGIN_CACHE_KEY] && this[PLUGIN_CACHE_KEY][name] === true;
+  };
+
+  /**
+   * Documented in player.js
+   *
+   * @ignore
+   */
+  Player.prototype.hasPlugin = function (name) {
+    return !!pluginExists(name);
+  };
+
+  /**
+   * @file extend.js
+   * @module extend
+   */
+
+  /**
+   * A combination of node inherits and babel's inherits (after transpile).
+   * Both work the same but node adds `super_` to the subClass
+   * and Bable adds the superClass as __proto__. Both seem useful.
+   *
+   * @param {Object} subClass
+   *        The class to inherit to
+   *
+   * @param {Object} superClass
+   *        The class to inherit from
+   *
+   * @private
+   */
+  var _inherits = function _inherits(subClass, superClass) {
+    if (typeof superClass !== 'function' && superClass !== null) {
+      throw new TypeError('Super expression must either be null or a function, not ' + (typeof superClass === 'undefined' ? 'undefined' : _typeof(superClass)));
+    }
+
+    subClass.prototype = Object.create(superClass && superClass.prototype, {
+      constructor: {
+        value: subClass,
+        enumerable: false,
+        writable: true,
+        configurable: true
+      }
+    });
+
+    if (superClass) {
+      // node
+      subClass.super_ = superClass;
+    }
+  };
+
+  /**
+   * Function for subclassing using the same inheritance that
+   * videojs uses internally
+   *
+   * @static
+   * @const
+   *
+   * @param {Object} superClass
+   *        The class to inherit from
+   *
+   * @param {Object} [subClassMethods={}]
+   *        The class to inherit to
+   *
+   * @return {Object}
+   *         The new object with subClassMethods that inherited superClass.
+   */
+  var extendFn = function extendFn(superClass) {
+    var subClassMethods = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+
+    var subClass = function subClass() {
+      superClass.apply(this, arguments);
+    };
+
+    var methods = {};
+
+    if ((typeof subClassMethods === 'undefined' ? 'undefined' : _typeof(subClassMethods)) === 'object') {
+      if (subClassMethods.constructor !== Object.prototype.constructor) {
+        subClass = subClassMethods.constructor;
+      }
+      methods = subClassMethods;
+    } else if (typeof subClassMethods === 'function') {
+      subClass = subClassMethods;
+    }
+
+    _inherits(subClass, superClass);
+
+    // Extend subObj's prototype with functions and other properties from props
+    for (var name in methods) {
+      if (methods.hasOwnProperty(name)) {
+        subClass.prototype[name] = methods[name];
+      }
+    }
+
+    return subClass;
+  };
+
+  /**
+   * @file video.js
+   * @module videojs
+   */
+
+  /**
+   * Normalize an `id` value by trimming off a leading `#`
+   *
+   * @param   {string} id
+   *          A string, maybe with a leading `#`.
+   *
+   * @returns {string}
+   *          The string, without any leading `#`.
+   */
+  var normalizeId = function normalizeId(id) {
+    return id.indexOf('#') === 0 ? id.slice(1) : id;
+  };
+
+  /**
+   * Doubles as the main function for users to create a player instance and also
+   * the main library object.
+   * The `videojs` function can be used to initialize or retrieve a player.
+   *
+   * @param {string|Element} id
+   *        Video element or video element ID
+   *
+   * @param {Object} [options]
+   *        Optional options object for config/settings
+   *
+   * @param {Component~ReadyCallback} [ready]
+   *        Optional ready callback
+   *
+   * @return {Player}
+   *         A player instance
+   */
+  function videojs$1(id, options, ready) {
+    var player = videojs$1.getPlayer(id);
+
+    if (player) {
+      if (options) {
+        log$1.warn('Player "' + id + '" is already initialised. Options will not be applied.');
+      }
+      if (ready) {
+        player.ready(ready);
+      }
+      return player;
+    }
+
+    var el = typeof id === 'string' ? $('#' + normalizeId(id)) : id;
+
+    if (!isEl(el)) {
+      throw new TypeError('The element or ID supplied is not valid. (videojs)');
+    }
+
+    if (!document_1.body.contains(el)) {
+      log$1.warn('The element supplied is not included in the DOM');
+    }
+
+    options = options || {};
+
+    videojs$1.hooks('beforesetup').forEach(function (hookFunction) {
+      var opts = hookFunction(el, mergeOptions(options));
+
+      if (!isObject(opts) || Array.isArray(opts)) {
+        log$1.error('please return an object in beforesetup hooks');
+        return;
+      }
+
+      options = mergeOptions(options, opts);
+    });
+
+    // We get the current "Player" component here in case an integration has
+    // replaced it with a custom player.
+    var PlayerComponent = Component.getComponent('Player');
+
+    player = new PlayerComponent(el, options, ready);
+
+    videojs$1.hooks('setup').forEach(function (hookFunction) {
+      return hookFunction(player);
+    });
+
+    return player;
+  }
+
+  /**
+   * An Object that contains lifecycle hooks as keys which point to an array
+   * of functions that are run when a lifecycle is triggered
+   */
+  videojs$1.hooks_ = {};
+
+  /**
+   * Get a list of hooks for a specific lifecycle
+   * @function videojs.hooks
+   *
+   * @param {string} type
+   *        the lifecyle to get hooks from
+   *
+   * @param {Function|Function[]} [fn]
+   *        Optionally add a hook (or hooks) to the lifecycle that your are getting.
+   *
+   * @return {Array}
+   *         an array of hooks, or an empty array if there are none.
+   */
+  videojs$1.hooks = function (type, fn) {
+    videojs$1.hooks_[type] = videojs$1.hooks_[type] || [];
+    if (fn) {
+      videojs$1.hooks_[type] = videojs$1.hooks_[type].concat(fn);
+    }
+    return videojs$1.hooks_[type];
+  };
+
+  /**
+   * Add a function hook to a specific videojs lifecycle.
+   *
+   * @param {string} type
+   *        the lifecycle to hook the function to.
+   *
+   * @param {Function|Function[]}
+   *        The function or array of functions to attach.
+   */
+  videojs$1.hook = function (type, fn) {
+    videojs$1.hooks(type, fn);
+  };
+
+  /**
+   * Add a function hook that will only run once to a specific videojs lifecycle.
+   *
+   * @param {string} type
+   *        the lifecycle to hook the function to.
+   *
+   * @param {Function|Function[]}
+   *        The function or array of functions to attach.
+   */
+  videojs$1.hookOnce = function (type, fn) {
+    videojs$1.hooks(type, [].concat(fn).map(function (original) {
+      var wrapper = function wrapper() {
+        videojs$1.removeHook(type, wrapper);
+        return original.apply(undefined, arguments);
+      };
+
+      return wrapper;
+    }));
+  };
+
+  /**
+   * Remove a hook from a specific videojs lifecycle.
+   *
+   * @param {string} type
+   *        the lifecycle that the function hooked to
+   *
+   * @param {Function} fn
+   *        The hooked function to remove
+   *
+   * @return {boolean}
+   *         The function that was removed or undef
+   */
+  videojs$1.removeHook = function (type, fn) {
+    var index = videojs$1.hooks(type).indexOf(fn);
+
+    if (index <= -1) {
+      return false;
+    }
+
+    videojs$1.hooks_[type] = videojs$1.hooks_[type].slice();
+    videojs$1.hooks_[type].splice(index, 1);
+
+    return true;
+  };
+
+  // Add default styles
+  if (window_1.VIDEOJS_NO_DYNAMIC_STYLE !== true && isReal()) {
+    var style$1 = $('.vjs-styles-defaults');
+
+    if (!style$1) {
+      style$1 = createStyleElement('vjs-styles-defaults');
+      var head = $('head');
+
+      if (head) {
+        head.insertBefore(style$1, head.firstChild);
+      }
+      setTextContent(style$1, '\n      .video-js {\n        width: 300px;\n        height: 150px;\n      }\n\n      .vjs-fluid {\n        padding-top: 56.25%\n      }\n    ');
+    }
+  }
+
+  // Run Auto-load players
+  // You have to wait at least once in case this script is loaded after your
+  // video in the DOM (weird behavior only with minified version)
+  autoSetupTimeout(1, videojs$1);
+
+  /**
+   * Current software version. Follows semver.
+   *
+   * @type {string}
+   */
+  videojs$1.VERSION = version;
+
+  /**
+   * The global options object. These are the settings that take effect
+   * if no overrides are specified when the player is created.
+   *
+   * @type {Object}
+   */
+  videojs$1.options = Player.prototype.options_;
+
+  /**
+   * Get an object with the currently created players, keyed by player ID
+   *
+   * @return {Object}
+   *         The created players
+   */
+  videojs$1.getPlayers = function () {
+    return Player.players;
+  };
+
+  /**
+   * Get a single player based on an ID or DOM element.
+   *
+   * This is useful if you want to check if an element or ID has an associated
+   * Video.js player, but not create one if it doesn't.
+   *
+   * @param   {string|Element} id
+   *          An HTML element - `<video>`, `<audio>`, or `<video-js>` -
+   *          or a string matching the `id` of such an element.
+   *
+   * @returns {Player|undefined}
+   *          A player instance or `undefined` if there is no player instance
+   *          matching the argument.
+   */
+  videojs$1.getPlayer = function (id) {
+    var players = Player.players;
+    var tag = void 0;
+
+    if (typeof id === 'string') {
+      var nId = normalizeId(id);
+      var player = players[nId];
+
+      if (player) {
+        return player;
+      }
+
+      tag = $('#' + nId);
+    } else {
+      tag = id;
+    }
+
+    if (isEl(tag)) {
+      var _tag = tag,
+        _player = _tag.player,
+        playerId = _tag.playerId;
+
+      // Element may have a `player` property referring to an already created
+      // player instance. If so, return that.
+
+      if (_player || players[playerId]) {
+        return _player || players[playerId];
+      }
+    }
+  };
+
+  /**
+   * Returns an array of all current players.
+   *
+   * @return {Array}
+   *         An array of all players. The array will be in the order that
+   *         `Object.keys` provides, which could potentially vary between
+   *         JavaScript engines.
+   *
+   */
+  videojs$1.getAllPlayers = function () {
+    return (
+
+      // Disposed players leave a key with a `null` value, so we need to make sure
+      // we filter those out.
+      Object.keys(Player.players).map(function (k) {
+        return Player.players[k];
+      }).filter(Boolean)
+    );
+  };
+
+  /**
+   * Expose players object.
+   *
+   * @memberOf videojs
+   * @property {Object} players
+   */
+  videojs$1.players = Player.players;
+
+  /**
+   * Get a component class object by name
+   *
+   * @borrows Component.getComponent as videojs.getComponent
+   */
+  videojs$1.getComponent = Component.getComponent;
+
+  /**
+   * Register a component so it can referred to by name. Used when adding to other
+   * components, either through addChild `component.addChild('myComponent')` or through
+   * default children options  `{ children: ['myComponent'] }`.
+   *
+   * > NOTE: You could also just initialize the component before adding.
+   * `component.addChild(new MyComponent());`
+   *
+   * @param {string} name
+   *        The class name of the component
+   *
+   * @param {Component} comp
+   *        The component class
+   *
+   * @return {Component}
+   *         The newly registered component
+   */
+  videojs$1.registerComponent = function (name$$1, comp) {
+    if (Tech.isTech(comp)) {
+      log$1.warn('The ' + name$$1 + ' tech was registered as a component. It should instead be registered using videojs.registerTech(name, tech)');
+    }
+
+    Component.registerComponent.call(Component, name$$1, comp);
+  };
+
+  /**
+   * Get a Tech class object by name
+   *
+   * @borrows Tech.getTech as videojs.getTech
+   */
+  videojs$1.getTech = Tech.getTech;
+
+  /**
+   * Register a Tech so it can referred to by name.
+   * This is used in the tech order for the player.
+   *
+   * @borrows Tech.registerTech as videojs.registerTech
+   */
+  videojs$1.registerTech = Tech.registerTech;
+
+  /**
+   * Register a middleware to a source type.
+   *
+   * @param {String} type A string representing a MIME type.
+   * @param {function(player):object} middleware A middleware factory that takes a player.
+   */
+  videojs$1.use = use;
+
+  /**
+   * An object that can be returned by a middleware to signify
+   * that the middleware is being terminated.
+   *
+   * @type {object}
+   * @memberOf {videojs}
+   * @property {object} middleware.TERMINATOR
+   */
+  Object.defineProperty(videojs$1, 'middleware', {
+    value: {},
+    writeable: false,
+    enumerable: true
+  });
+
+  Object.defineProperty(videojs$1.middleware, 'TERMINATOR', {
+    value: TERMINATOR,
+    writeable: false,
+    enumerable: true
+  });
+
+  /**
+   * A suite of browser and device tests from {@link browser}.
+   *
+   * @type {Object}
+   * @private
+   */
+  videojs$1.browser = browser;
+
+  /**
+   * Whether or not the browser supports touch events. Included for backward
+   * compatibility with 4.x, but deprecated. Use `videojs.browser.TOUCH_ENABLED`
+   * instead going forward.
+   *
+   * @deprecated since version 5.0
+   * @type {boolean}
+   */
+  videojs$1.TOUCH_ENABLED = TOUCH_ENABLED;
+
+  /**
+   * Subclass an existing class
+   * Mimics ES6 subclassing with the `extend` keyword
+   *
+   * @borrows extend:extendFn as videojs.extend
+   */
+  videojs$1.extend = extendFn;
+
+  /**
+   * Merge two options objects recursively
+   * Performs a deep merge like lodash.merge but **only merges plain objects**
+   * (not arrays, elements, anything else)
+   * Other values will be copied directly from the second object.
+   *
+   * @borrows merge-options:mergeOptions as videojs.mergeOptions
+   */
+  videojs$1.mergeOptions = mergeOptions;
+
+  /**
+   * Change the context (this) of a function
+   *
+   * > NOTE: as of v5.0 we require an ES5 shim, so you should use the native
+   * `function() {}.bind(newContext);` instead of this.
+   *
+   * @borrows fn:bind as videojs.bind
+   */
+  videojs$1.bind = bind;
+
+  /**
+   * Register a Video.js plugin.
+   *
+   * @borrows plugin:registerPlugin as videojs.registerPlugin
+   * @method registerPlugin
+   *
+   * @param  {string} name
+   *         The name of the plugin to be registered. Must be a string and
+   *         must not match an existing plugin or a method on the `Player`
+   *         prototype.
+   *
+   * @param  {Function} plugin
+   *         A sub-class of `Plugin` or a function for basic plugins.
+   *
+   * @return {Function}
+   *         For advanced plugins, a factory function for that plugin. For
+   *         basic plugins, a wrapper function that initializes the plugin.
+   */
+  videojs$1.registerPlugin = Plugin.registerPlugin;
+
+  /**
+   * Deprecated method to register a plugin with Video.js
+   *
+   * @deprecated
+   *        videojs.plugin() is deprecated; use videojs.registerPlugin() instead
+   *
+   * @param {string} name
+   *        The plugin name
+   *
+   * @param {Plugin|Function} plugin
+   *         The plugin sub-class or function
+   */
+  videojs$1.plugin = function (name$$1, plugin) {
+    log$1.warn('videojs.plugin() is deprecated; use videojs.registerPlugin() instead');
+    return Plugin.registerPlugin(name$$1, plugin);
+  };
+
+  /**
+   * Gets an object containing multiple Video.js plugins.
+   *
+   * @param  {Array} [names]
+   *         If provided, should be an array of plugin names. Defaults to _all_
+   *         plugin names.
+   *
+   * @return {Object|undefined}
+   *         An object containing plugin(s) associated with their name(s) or
+   *         `undefined` if no matching plugins exist).
+   */
+  videojs$1.getPlugins = Plugin.getPlugins;
+
+  /**
+   * Gets a plugin by name if it exists.
+   *
+   * @param  {string} name
+   *         The name of a plugin.
+   *
+   * @return {Function|undefined}
+   *         The plugin (or `undefined`).
+   */
+  videojs$1.getPlugin = Plugin.getPlugin;
+
+  /**
+   * Gets a plugin's version, if available
+   *
+   * @param  {string} name
+   *         The name of a plugin.
+   *
+   * @return {string}
+   *         The plugin's version or an empty string.
+   */
+  videojs$1.getPluginVersion = Plugin.getPluginVersion;
+
+  /**
+   * Adding languages so that they're available to all players.
+   * Example: `videojs.addLanguage('es', { 'Hello': 'Hola' });`
+   *
+   * @param {string} code
+   *        The language code or dictionary property
+   *
+   * @param {Object} data
+   *        The data values to be translated
+   *
+   * @return {Object}
+   *         The resulting language dictionary object
+   */
+  videojs$1.addLanguage = function (code, data) {
+    var _mergeOptions;
+
+    code = ('' + code).toLowerCase();
+
+    videojs$1.options.languages = mergeOptions(videojs$1.options.languages, (_mergeOptions = {}, _mergeOptions[code] = data, _mergeOptions));
+
+    return videojs$1.options.languages[code];
+  };
+
+  /**
+   * Log messages
+   *
+   * @borrows log:log as videojs.log
+   */
+  videojs$1.log = log$1;
+
+  /**
+   * Creates an emulated TimeRange object.
+   *
+   * @borrows time-ranges:createTimeRanges as videojs.createTimeRange
+   */
+  /**
+   * @borrows time-ranges:createTimeRanges as videojs.createTimeRanges
+   */
+  videojs$1.createTimeRange = videojs$1.createTimeRanges = createTimeRanges;
+
+  /**
+   * Format seconds as a time string, H:MM:SS or M:SS
+   * Supplying a guide (in seconds) will force a number of leading zeros
+   * to cover the length of the guide
+   *
+   * @borrows format-time:formatTime as videojs.formatTime
+   */
+  videojs$1.formatTime = formatTime;
+
+  /**
+   * Replaces format-time with a custom implementation, to be used in place of the default.
+   *
+   * @borrows format-time:setFormatTime as videojs.setFormatTime
+   *
+   * @method setFormatTime
+   *
+   * @param {Function} customFn
+   *        A custom format-time function which will be called with the current time and guide (in seconds) as arguments.
+   *        Passed fn should return a string.
+   */
+  videojs$1.setFormatTime = setFormatTime;
+
+  /**
+   * Resets format-time to the default implementation.
+   *
+   * @borrows format-time:resetFormatTime as videojs.resetFormatTime
+   *
+   * @method resetFormatTime
+   */
+  videojs$1.resetFormatTime = resetFormatTime;
+
+  /**
+   * Resolve and parse the elements of a URL
+   *
+   * @borrows url:parseUrl as videojs.parseUrl
+   *
+   */
+  videojs$1.parseUrl = parseUrl;
+
+  /**
+   * Returns whether the url passed is a cross domain request or not.
+   *
+   * @borrows url:isCrossOrigin as videojs.isCrossOrigin
+   */
+  videojs$1.isCrossOrigin = isCrossOrigin;
+
+  /**
+   * Event target class.
+   *
+   * @borrows EventTarget as videojs.EventTarget
+   */
+  videojs$1.EventTarget = EventTarget;
+
+  /**
+   * 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.
+   *
+   * @borrows events:on as videojs.on
+   */
+  videojs$1.on = on;
+
+  /**
+   * Trigger a listener only once for an event
+   *
+   * @borrows events:one as videojs.one
+   */
+  videojs$1.one = one;
+
+  /**
+   * Removes event listeners from an element
+   *
+   * @borrows events:off as videojs.off
+   */
+  videojs$1.off = off;
+
+  /**
+   * Trigger an event for an element
+   *
+   * @borrows events:trigger as videojs.trigger
+   */
+  videojs$1.trigger = trigger;
+
+  /**
+   * A cross-browser XMLHttpRequest wrapper. Here's a simple example:
+   *
+   * @param {Object} options
+   *        settings for the request.
+   *
+   * @return {XMLHttpRequest|XDomainRequest}
+   *         The request object.
+   *
+   * @see https://github.com/Raynos/xhr
+   */
+  videojs$1.xhr = xhr;
+
+  /**
+   * TextTrack class
+   *
+   * @borrows TextTrack as videojs.TextTrack
+   */
+  videojs$1.TextTrack = TextTrack;
+
+  /**
+   * export the AudioTrack class so that source handlers can create
+   * AudioTracks and then add them to the players AudioTrackList
+   *
+   * @borrows AudioTrack as videojs.AudioTrack
+   */
+  videojs$1.AudioTrack = AudioTrack;
+
+  /**
+   * export the VideoTrack class so that source handlers can create
+   * VideoTracks and then add them to the players VideoTrackList
+   *
+   * @borrows VideoTrack as videojs.VideoTrack
+   */
+  videojs$1.VideoTrack = VideoTrack;
+
+  /**
+   * Determines, via duck typing, whether or not a value is a DOM element.
+   *
+   * @borrows dom:isEl as videojs.isEl
+   * @deprecated Use videojs.dom.isEl() instead
+   */
+
+  /**
+   * Determines, via duck typing, whether or not a value is a text node.
+   *
+   * @borrows dom:isTextNode as videojs.isTextNode
+   * @deprecated Use videojs.dom.isTextNode() instead
+   */
+
+  /**
+   * Creates an element and applies properties.
+   *
+   * @borrows dom:createEl as videojs.createEl
+   * @deprecated Use videojs.dom.createEl() instead
+   */
+
+  /**
+   * Check if an element has a CSS class
+   *
+   * @borrows dom:hasElClass as videojs.hasClass
+   * @deprecated Use videojs.dom.hasClass() instead
+   */
+
+  /**
+   * Add a CSS class name to an element
+   *
+   * @borrows dom:addElClass as videojs.addClass
+   * @deprecated Use videojs.dom.addClass() instead
+   */
+
+  /**
+   * Remove a CSS class name from an element
+   *
+   * @borrows dom:removeElClass as videojs.removeClass
+   * @deprecated Use videojs.dom.removeClass() instead
+   */
+
+  /**
+   * Adds or removes a CSS class name on an element depending on an optional
+   * condition or the presence/absence of the class name.
+   *
+   * @borrows dom:toggleElClass as videojs.toggleClass
+   * @deprecated Use videojs.dom.toggleClass() instead
+   */
+
+  /**
+   * Apply attributes to an HTML element.
+   *
+   * @borrows dom:setElAttributes as videojs.setAttribute
+   * @deprecated Use videojs.dom.setAttributes() instead
+   */
+
+  /**
+   * 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 (which shouldn't be used with HTML)
+   * This will return true or false for boolean attributes.
+   *
+   * @borrows dom:getElAttributes as videojs.getAttributes
+   * @deprecated Use videojs.dom.getAttributes() instead
+   */
+
+  /**
+   * Empties the contents of an element.
+   *
+   * @borrows dom:emptyEl as videojs.emptyEl
+   * @deprecated Use videojs.dom.emptyEl() instead
+   */
+
+  /**
+   * Normalizes and appends content to an element.
+   *
+   * The content for an element can be passed in multiple types and
+   * combinations, whose behavior is as follows:
+   *
+   * - String
+   *   Normalized into a text node.
+   *
+   * - Element, TextNode
+   *   Passed through.
+   *
+   * - Array
+   *   A one-dimensional array of strings, elements, nodes, or functions (which
+   *   return single strings, elements, or nodes).
+   *
+   * - Function
+   *   If the sole argument, is expected to produce a string, element,
+   *   node, or array.
+   *
+   * @borrows dom:appendContents as videojs.appendContet
+   * @deprecated Use videojs.dom.appendContent() instead
+   */
+
+  /**
+   * Normalizes and inserts content into an element; this is identical to
+   * `appendContent()`, except it empties the element first.
+   *
+   * The content for an element can be passed in multiple types and
+   * combinations, whose behavior is as follows:
+   *
+   * - String
+   *   Normalized into a text node.
+   *
+   * - Element, TextNode
+   *   Passed through.
+   *
+   * - Array
+   *   A one-dimensional array of strings, elements, nodes, or functions (which
+   *   return single strings, elements, or nodes).
+   *
+   * - Function
+   *   If the sole argument, is expected to produce a string, element,
+   *   node, or array.
+   *
+   * @borrows dom:insertContent as videojs.insertContent
+   * @deprecated Use videojs.dom.insertContent() instead
+   */
+  ['isEl', 'isTextNode', 'createEl', 'hasClass', 'addClass', 'removeClass', 'toggleClass', 'setAttributes', 'getAttributes', 'emptyEl', 'appendContent', 'insertContent'].forEach(function (k) {
+    videojs$1[k] = function () {
+      log$1.warn('videojs.' + k + '() is deprecated; use videojs.dom.' + k + '() instead');
+      return Dom[k].apply(null, arguments);
+    };
+  });
+
+  /**
+   * A safe getComputedStyle.
+   *
+   * This is 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.
+   * See https://bugzilla.mozilla.org/show_bug.cgi?id=548397 for more details.
+   *
+   * @borrows computed-style:computedStyle as videojs.computedStyle
+   */
+  videojs$1.computedStyle = computedStyle;
+
+  /**
+   * Export the Dom utilities for use in external plugins
+   * and Tech's
+   */
+  videojs$1.dom = Dom;
+
+  /**
+   * Export the Url utilities for use in external plugins
+   * and Tech's
+   */
+  videojs$1.url = Url;
+
+  var urlToolkit = createCommonjsModule(function (module, exports) {
+    // see https://tools.ietf.org/html/rfc1808
+
+    /* jshint ignore:start */
+    (function (root) {
+      /* jshint ignore:end */
+
+      var URL_REGEX = /^((?:[a-zA-Z0-9+\-.]+:)?)(\/\/[^\/\;?#]*)?(.*?)??(;.*?)?(\?.*?)?(#.*?)?$/;
+      var FIRST_SEGMENT_REGEX = /^([^\/;?#]*)(.*)$/;
+      var SLASH_DOT_REGEX = /(?:\/|^)\.(?=\/)/g;
+      var SLASH_DOT_DOT_REGEX = /(?:\/|^)\.\.\/(?!\.\.\/).*?(?=\/)/g;
+
+      var URLToolkit = { // jshint ignore:line
+        // If opts.alwaysNormalize is true then the path will always be normalized even when it starts with / or //
+        // E.g
+        // With opts.alwaysNormalize = false (default, spec compliant)
+        // http://a.com/b/cd + /e/f/../g => http://a.com/e/f/../g
+        // With opts.alwaysNormalize = true (not spec compliant)
+        // http://a.com/b/cd + /e/f/../g => http://a.com/e/g
+        buildAbsoluteURL: function buildAbsoluteURL(baseURL, relativeURL, opts) {
+          opts = opts || {};
+          // remove any remaining space and CRLF
+          baseURL = baseURL.trim();
+          relativeURL = relativeURL.trim();
+          if (!relativeURL) {
+            // 2a) If the embedded URL is entirely empty, it inherits the
+            // entire base URL (i.e., is set equal to the base URL)
+            // and we are done.
+            if (!opts.alwaysNormalize) {
+              return baseURL;
+            }
+            var basePartsForNormalise = URLToolkit.parseURL(baseURL);
+            if (!basePartsForNormalise) {
+              throw new Error('Error trying to parse base URL.');
+            }
+            basePartsForNormalise.path = URLToolkit.normalizePath(basePartsForNormalise.path);
+            return URLToolkit.buildURLFromParts(basePartsForNormalise);
+          }
+          var relativeParts = URLToolkit.parseURL(relativeURL);
+          if (!relativeParts) {
+            throw new Error('Error trying to parse relative URL.');
+          }
+          if (relativeParts.scheme) {
+            // 2b) If the embedded URL starts with a scheme name, it is
+            // interpreted as an absolute URL and we are done.
+            if (!opts.alwaysNormalize) {
+              return relativeURL;
+            }
+            relativeParts.path = URLToolkit.normalizePath(relativeParts.path);
+            return URLToolkit.buildURLFromParts(relativeParts);
+          }
+          var baseParts = URLToolkit.parseURL(baseURL);
+          if (!baseParts) {
+            throw new Error('Error trying to parse base URL.');
+          }
+          if (!baseParts.netLoc && baseParts.path && baseParts.path[0] !== '/') {
+            // If netLoc missing and path doesn't start with '/', assume everthing before the first '/' is the netLoc
+            // This causes 'example.com/a' to be handled as '//example.com/a' instead of '/example.com/a'
+            var pathParts = FIRST_SEGMENT_REGEX.exec(baseParts.path);
+            baseParts.netLoc = pathParts[1];
+            baseParts.path = pathParts[2];
+          }
+          if (baseParts.netLoc && !baseParts.path) {
+            baseParts.path = '/';
+          }
+          var builtParts = {
+            // 2c) Otherwise, the embedded URL inherits the scheme of
+            // the base URL.
+            scheme: baseParts.scheme,
+            netLoc: relativeParts.netLoc,
+            path: null,
+            params: relativeParts.params,
+            query: relativeParts.query,
+            fragment: relativeParts.fragment
+          };
+          if (!relativeParts.netLoc) {
+            // 3) If the embedded URL's <net_loc> is non-empty, we skip to
+            // Step 7.  Otherwise, the embedded URL inherits the <net_loc>
+            // (if any) of the base URL.
+            builtParts.netLoc = baseParts.netLoc;
+            // 4) If the embedded URL path is preceded by a slash "/", the
+            // path is not relative and we skip to Step 7.
+            if (relativeParts.path[0] !== '/') {
+              if (!relativeParts.path) {
+                // 5) If the embedded URL path is empty (and not preceded by a
+                // slash), then the embedded URL inherits the base URL path
+                builtParts.path = baseParts.path;
+                // 5a) if the embedded URL's <params> is non-empty, we skip to
+                // step 7; otherwise, it inherits the <params> of the base
+                // URL (if any) and
+                if (!relativeParts.params) {
+                  builtParts.params = baseParts.params;
+                  // 5b) if the embedded URL's <query> is non-empty, we skip to
+                  // step 7; otherwise, it inherits the <query> of the base
+                  // URL (if any) and we skip to step 7.
+                  if (!relativeParts.query) {
+                    builtParts.query = baseParts.query;
+                  }
+                }
+              } else {
+                // 6) The last segment of the base URL's path (anything
+                // following the rightmost slash "/", or the entire path if no
+                // slash is present) is removed and the embedded URL's path is
+                // appended in its place.
+                var baseURLPath = baseParts.path;
+                var newPath = baseURLPath.substring(0, baseURLPath.lastIndexOf('/') + 1) + relativeParts.path;
+                builtParts.path = URLToolkit.normalizePath(newPath);
+              }
+            }
+          }
+          if (builtParts.path === null) {
+            builtParts.path = opts.alwaysNormalize ? URLToolkit.normalizePath(relativeParts.path) : relativeParts.path;
+          }
+          return URLToolkit.buildURLFromParts(builtParts);
+        },
+        parseURL: function parseURL(url) {
+          var parts = URL_REGEX.exec(url);
+          if (!parts) {
+            return null;
+          }
+          return {
+            scheme: parts[1] || '',
+            netLoc: parts[2] || '',
+            path: parts[3] || '',
+            params: parts[4] || '',
+            query: parts[5] || '',
+            fragment: parts[6] || ''
+          };
+        },
+        normalizePath: function normalizePath(path) {
+          // The following operations are
+          // then applied, in order, to the new path:
+          // 6a) All occurrences of "./", where "." is a complete path
+          // segment, are removed.
+          // 6b) If the path ends with "." as a complete path segment,
+          // that "." is removed.
+          path = path.split('').reverse().join('').replace(SLASH_DOT_REGEX, '');
+          // 6c) All occurrences of "<segment>/../", where <segment> is a
+          // complete path segment not equal to "..", are removed.
+          // Removal of these path segments is performed iteratively,
+          // removing the leftmost matching pattern on each iteration,
+          // until no matching pattern remains.
+          // 6d) If the path ends with "<segment>/..", where <segment> is a
+          // complete path segment not equal to "..", that
+          // "<segment>/.." is removed.
+          while (path.length !== (path = path.replace(SLASH_DOT_DOT_REGEX, '')).length) {
+          } // jshint ignore:line
+          return path.split('').reverse().join('');
+        },
+        buildURLFromParts: function buildURLFromParts(parts) {
+          return parts.scheme + parts.netLoc + parts.path + parts.params + parts.query + parts.fragment;
+        }
+      };
+
+      /* jshint ignore:start */
+      module.exports = URLToolkit;
+    })(commonjsGlobal);
+    /* jshint ignore:end */
+  });
+
+  var classCallCheck$1 = function classCallCheck$$1(instance, Constructor) {
+    if (!(instance instanceof Constructor)) {
+      throw new TypeError("Cannot call a class as a function");
+    }
+  };
+
+  var _extends$1 = Object.assign || function (target) {
+    for (var i = 1; i < arguments.length; i++) {
+      var source = arguments[i];
+
+      for (var key in source) {
+        if (Object.prototype.hasOwnProperty.call(source, key)) {
+          target[key] = source[key];
+        }
+      }
+    }
+
+    return target;
+  };
+
+  var inherits$1 = function inherits$$1(subClass, superClass) {
+    if (typeof superClass !== "function" && superClass !== null) {
+      throw new TypeError("Super expression must either be null or a function, not " + (typeof superClass === "undefined" ? "undefined" : _typeof(superClass)));
+    }
+
+    subClass.prototype = Object.create(superClass && superClass.prototype, {
+      constructor: {
+        value: subClass,
+        enumerable: false,
+        writable: true,
+        configurable: true
+      }
+    });
+    if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
+  };
+
+  var possibleConstructorReturn$1 = function possibleConstructorReturn$$1(self, call) {
+    if (!self) {
+      throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
+    }
+
+    return call && ((typeof call === "undefined" ? "undefined" : _typeof(call)) === "object" || typeof call === "function") ? call : self;
+  };
+
+  /**
+   * @file stream.js
+   */
+  /**
+   * A lightweight readable stream implemention that handles event dispatching.
+   *
+   * @class Stream
+   */
+  var Stream = function () {
+    function Stream() {
+      classCallCheck$1(this, Stream);
+
+      this.listeners = {};
+    }
+
+    /**
+     * Add a listener for a specified event type.
+     *
+     * @param {String} type the event name
+     * @param {Function} listener the callback to be invoked when an event of
+     * the specified type occurs
+     */
+
+    Stream.prototype.on = function on(type, listener) {
+      if (!this.listeners[type]) {
+        this.listeners[type] = [];
+      }
+      this.listeners[type].push(listener);
+    };
+
+    /**
+     * Remove a listener for a specified event type.
+     *
+     * @param {String} type the event name
+     * @param {Function} listener  a function previously registered for this
+     * type of event through `on`
+     * @return {Boolean} if we could turn it off or not
+     */
+
+    Stream.prototype.off = function off(type, listener) {
+      if (!this.listeners[type]) {
+        return false;
+      }
+
+      var index = this.listeners[type].indexOf(listener);
+
+      this.listeners[type].splice(index, 1);
+      return index > -1;
+    };
+
+    /**
+     * Trigger an event of the specified type on this stream. Any additional
+     * arguments to this function are passed as parameters to event listeners.
+     *
+     * @param {String} type the event name
+     */
+
+    Stream.prototype.trigger = function trigger(type) {
+      var callbacks = this.listeners[type];
+      var i = void 0;
+      var length = void 0;
+      var args = void 0;
+
+      if (!callbacks) {
+        return;
+      }
+      // Slicing the arguments on every invocation of this method
+      // can add a significant amount of overhead. Avoid the
+      // intermediate object creation for the common case of a
+      // single callback argument
+      if (arguments.length === 2) {
+        length = callbacks.length;
+        for (i = 0; i < length; ++i) {
+          callbacks[i].call(this, arguments[1]);
+        }
+      } else {
+        args = Array.prototype.slice.call(arguments, 1);
+        length = callbacks.length;
+        for (i = 0; i < length; ++i) {
+          callbacks[i].apply(this, args);
+        }
+      }
+    };
+
+    /**
+     * Destroys the stream and cleans up.
+     */
+
+    Stream.prototype.dispose = function dispose() {
+      this.listeners = {};
+    };
+    /**
+     * Forwards all `data` events on this stream to the destination stream. The
+     * destination stream should provide a method `push` to receive the data
+     * events as they arrive.
+     *
+     * @param {Stream} destination the stream that will receive all `data` events
+     * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
+     */
+
+    Stream.prototype.pipe = function pipe(destination) {
+      this.on('data', function (data) {
+        destination.push(data);
+      });
+    };
+
+    return Stream;
+  }();
+
+  /**
+   * @file m3u8/line-stream.js
+   */
+  /**
+   * A stream that buffers string input and generates a `data` event for each
+   * line.
+   *
+   * @class LineStream
+   * @extends Stream
+   */
+
+  var LineStream = function (_Stream) {
+    inherits$1(LineStream, _Stream);
+
+    function LineStream() {
+      classCallCheck$1(this, LineStream);
+
+      var _this = possibleConstructorReturn$1(this, _Stream.call(this));
+
+      _this.buffer = '';
+      return _this;
+    }
+
+    /**
+     * Add new data to be parsed.
+     *
+     * @param {String} data the text to process
+     */
+
+    LineStream.prototype.push = function push(data) {
+      var nextNewline = void 0;
+
+      this.buffer += data;
+      nextNewline = this.buffer.indexOf('\n');
+
+      for (; nextNewline > -1; nextNewline = this.buffer.indexOf('\n')) {
+        this.trigger('data', this.buffer.substring(0, nextNewline));
+        this.buffer = this.buffer.substring(nextNewline + 1);
+      }
+    };
+
+    return LineStream;
+  }(Stream);
+
+  /**
+   * @file m3u8/parse-stream.js
+   */
+  /**
+   * "forgiving" attribute list psuedo-grammar:
+   * attributes -> keyvalue (',' keyvalue)*
+   * keyvalue   -> key '=' value
+   * key        -> [^=]*
+   * value      -> '"' [^"]* '"' | [^,]*
+   */
+  var attributeSeparator = function attributeSeparator() {
+    var key = '[^=]*';
+    var value = '"[^"]*"|[^,]*';
+    var keyvalue = '(?:' + key + ')=(?:' + value + ')';
+
+    return new RegExp('(?:^|,)(' + keyvalue + ')');
+  };
+
+  /**
+   * Parse attributes from a line given the seperator
+   *
+   * @param {String} attributes the attibute line to parse
+   */
+  var parseAttributes = function parseAttributes(attributes) {
+    // split the string using attributes as the separator
+    var attrs = attributes.split(attributeSeparator());
+    var result = {};
+    var i = attrs.length;
+    var attr = void 0;
+
+    while (i--) {
+      // filter out unmatched portions of the string
+      if (attrs[i] === '') {
+        continue;
+      }
+
+      // split the key and value
+      attr = /([^=]*)=(.*)/.exec(attrs[i]).slice(1);
+      // trim whitespace and remove optional quotes around the value
+      attr[0] = attr[0].replace(/^\s+|\s+$/g, '');
+      attr[1] = attr[1].replace(/^\s+|\s+$/g, '');
+      attr[1] = attr[1].replace(/^['"](.*)['"]$/g, '$1');
+      result[attr[0]] = attr[1];
+    }
+    return result;
+  };
+
+  /**
+   * A line-level M3U8 parser event stream. It expects to receive input one
+   * line at a time and performs a context-free parse of its contents. A stream
+   * interpretation of a manifest can be useful if the manifest is expected to
+   * be too large to fit comfortably into memory or the entirety of the input
+   * is not immediately available. Otherwise, it's probably much easier to work
+   * with a regular `Parser` object.
+   *
+   * Produces `data` events with an object that captures the parser's
+   * interpretation of the input. That object has a property `tag` that is one
+   * of `uri`, `comment`, or `tag`. URIs only have a single additional
+   * property, `line`, which captures the entirety of the input without
+   * interpretation. Comments similarly have a single additional property
+   * `text` which is the input without the leading `#`.
+   *
+   * Tags always have a property `tagType` which is the lower-cased version of
+   * the M3U8 directive without the `#EXT` or `#EXT-X-` prefix. For instance,
+   * `#EXT-X-MEDIA-SEQUENCE` becomes `media-sequence` when parsed. Unrecognized
+   * tags are given the tag type `unknown` and a single additional property
+   * `data` with the remainder of the input.
+   *
+   * @class ParseStream
+   * @extends Stream
+   */
+
+  var ParseStream = function (_Stream) {
+    inherits$1(ParseStream, _Stream);
+
+    function ParseStream() {
+      classCallCheck$1(this, ParseStream);
+
+      var _this = possibleConstructorReturn$1(this, _Stream.call(this));
+
+      _this.customParsers = [];
+      return _this;
+    }
+
+    /**
+     * Parses an additional line of input.
+     *
+     * @param {String} line a single line of an M3U8 file to parse
+     */
+
+    ParseStream.prototype.push = function push(line) {
+      var match = void 0;
+      var event = void 0;
+
+      // strip whitespace
+      line = line.replace(/^[\u0000\s]+|[\u0000\s]+$/g, '');
+      if (line.length === 0) {
+        // ignore empty lines
+        return;
+      }
+
+      // URIs
+      if (line[0] !== '#') {
+        this.trigger('data', {
+          type: 'uri',
+          uri: line
+        });
+        return;
+      }
+
+      for (var i = 0; i < this.customParsers.length; i++) {
+        if (this.customParsers[i].call(this, line)) {
+          return;
+        }
+      }
+
+      // Comments
+      if (line.indexOf('#EXT') !== 0) {
+        this.trigger('data', {
+          type: 'comment',
+          text: line.slice(1)
+        });
+        return;
+      }
+
+      // strip off any carriage returns here so the regex matching
+      // doesn't have to account for them.
+      line = line.replace('\r', '');
+
+      // Tags
+      match = /^#EXTM3U/.exec(line);
+      if (match) {
+        this.trigger('data', {
+          type: 'tag',
+          tagType: 'm3u'
+        });
+        return;
+      }
+      match = /^#EXTINF:?([0-9\.]*)?,?(.*)?$/.exec(line);
+      if (match) {
+        event = {
+          type: 'tag',
+          tagType: 'inf'
+        };
+        if (match[1]) {
+          event.duration = parseFloat(match[1]);
+        }
+        if (match[2]) {
+          event.title = match[2];
+        }
+        this.trigger('data', event);
+        return;
+      }
+      match = /^#EXT-X-TARGETDURATION:?([0-9.]*)?/.exec(line);
+      if (match) {
+        event = {
+          type: 'tag',
+          tagType: 'targetduration'
+        };
+        if (match[1]) {
+          event.duration = parseInt(match[1], 10);
+        }
+        this.trigger('data', event);
+        return;
+      }
+      match = /^#ZEN-TOTAL-DURATION:?([0-9.]*)?/.exec(line);
+      if (match) {
+        event = {
+          type: 'tag',
+          tagType: 'totalduration'
+        };
+        if (match[1]) {
+          event.duration = parseInt(match[1], 10);
+        }
+        this.trigger('data', event);
+        return;
+      }
+      match = /^#EXT-X-VERSION:?([0-9.]*)?/.exec(line);
+      if (match) {
+        event = {
+          type: 'tag',
+          tagType: 'version'
+        };
+        if (match[1]) {
+          event.version = parseInt(match[1], 10);
+        }
+        this.trigger('data', event);
+        return;
+      }
+      match = /^#EXT-X-MEDIA-SEQUENCE:?(\-?[0-9.]*)?/.exec(line);
+      if (match) {
+        event = {
+          type: 'tag',
+          tagType: 'media-sequence'
+        };
+        if (match[1]) {
+          event.number = parseInt(match[1], 10);
+        }
+        this.trigger('data', event);
+        return;
+      }
+      match = /^#EXT-X-DISCONTINUITY-SEQUENCE:?(\-?[0-9.]*)?/.exec(line);
+      if (match) {
+        event = {
+          type: 'tag',
+          tagType: 'discontinuity-sequence'
+        };
+        if (match[1]) {
+          event.number = parseInt(match[1], 10);
+        }
+        this.trigger('data', event);
+        return;
+      }
+      match = /^#EXT-X-PLAYLIST-TYPE:?(.*)?$/.exec(line);
+      if (match) {
+        event = {
+          type: 'tag',
+          tagType: 'playlist-type'
+        };
+        if (match[1]) {
+          event.playlistType = match[1];
+        }
+        this.trigger('data', event);
+        return;
+      }
+      match = /^#EXT-X-BYTERANGE:?([0-9.]*)?@?([0-9.]*)?/.exec(line);
+      if (match) {
+        event = {
+          type: 'tag',
+          tagType: 'byterange'
+        };
+        if (match[1]) {
+          event.length = parseInt(match[1], 10);
+        }
+        if (match[2]) {
+          event.offset = parseInt(match[2], 10);
+        }
+        this.trigger('data', event);
+        return;
+      }
+      match = /^#EXT-X-ALLOW-CACHE:?(YES|NO)?/.exec(line);
+      if (match) {
+        event = {
+          type: 'tag',
+          tagType: 'allow-cache'
+        };
+        if (match[1]) {
+          event.allowed = !/NO/.test(match[1]);
+        }
+        this.trigger('data', event);
+        return;
+      }
+      match = /^#EXT-X-MAP:?(.*)$/.exec(line);
+      if (match) {
+        event = {
+          type: 'tag',
+          tagType: 'map'
+        };
+
+        if (match[1]) {
+          var attributes = parseAttributes(match[1]);
+
+          if (attributes.URI) {
+            event.uri = attributes.URI;
+          }
+          if (attributes.BYTERANGE) {
+            var _attributes$BYTERANGE = attributes.BYTERANGE.split('@'),
+              length = _attributes$BYTERANGE[0],
+              offset = _attributes$BYTERANGE[1];
+
+            event.byterange = {};
+            if (length) {
+              event.byterange.length = parseInt(length, 10);
+            }
+            if (offset) {
+              event.byterange.offset = parseInt(offset, 10);
+            }
+          }
+        }
+
+        this.trigger('data', event);
+        return;
+      }
+      match = /^#EXT-X-STREAM-INF:?(.*)$/.exec(line);
+      if (match) {
+        event = {
+          type: 'tag',
+          tagType: 'stream-inf'
+        };
+        if (match[1]) {
+          event.attributes = parseAttributes(match[1]);
+
+          if (event.attributes.RESOLUTION) {
+            var split = event.attributes.RESOLUTION.split('x');
+            var resolution = {};
+
+            if (split[0]) {
+              resolution.width = parseInt(split[0], 10);
+            }
+            if (split[1]) {
+              resolution.height = parseInt(split[1], 10);
+            }
+            event.attributes.RESOLUTION = resolution;
+          }
+          if (event.attributes.BANDWIDTH) {
+            event.attributes.BANDWIDTH = parseInt(event.attributes.BANDWIDTH, 10);
+          }
+          if (event.attributes['PROGRAM-ID']) {
+            event.attributes['PROGRAM-ID'] = parseInt(event.attributes['PROGRAM-ID'], 10);
+          }
+        }
+        this.trigger('data', event);
+        return;
+      }
+      match = /^#EXT-X-MEDIA:?(.*)$/.exec(line);
+      if (match) {
+        event = {
+          type: 'tag',
+          tagType: 'media'
+        };
+        if (match[1]) {
+          event.attributes = parseAttributes(match[1]);
+        }
+        this.trigger('data', event);
+        return;
+      }
+      match = /^#EXT-X-ENDLIST/.exec(line);
+      if (match) {
+        this.trigger('data', {
+          type: 'tag',
+          tagType: 'endlist'
+        });
+        return;
+      }
+      match = /^#EXT-X-DISCONTINUITY/.exec(line);
+      if (match) {
+        this.trigger('data', {
+          type: 'tag',
+          tagType: 'discontinuity'
+        });
+        return;
+      }
+      match = /^#EXT-X-PROGRAM-DATE-TIME:?(.*)$/.exec(line);
+      if (match) {
+        event = {
+          type: 'tag',
+          tagType: 'program-date-time'
+        };
+        if (match[1]) {
+          event.dateTimeString = match[1];
+          event.dateTimeObject = new Date(match[1]);
+        }
+        this.trigger('data', event);
+        return;
+      }
+      match = /^#EXT-X-KEY:?(.*)$/.exec(line);
+      if (match) {
+        event = {
+          type: 'tag',
+          tagType: 'key'
+        };
+        if (match[1]) {
+          event.attributes = parseAttributes(match[1]);
+          // parse the IV string into a Uint32Array
+          if (event.attributes.IV) {
+            if (event.attributes.IV.substring(0, 2).toLowerCase() === '0x') {
+              event.attributes.IV = event.attributes.IV.substring(2);
+            }
+
+            event.attributes.IV = event.attributes.IV.match(/.{8}/g);
+            event.attributes.IV[0] = parseInt(event.attributes.IV[0], 16);
+            event.attributes.IV[1] = parseInt(event.attributes.IV[1], 16);
+            event.attributes.IV[2] = parseInt(event.attributes.IV[2], 16);
+            event.attributes.IV[3] = parseInt(event.attributes.IV[3], 16);
+            event.attributes.IV = new Uint32Array(event.attributes.IV);
+          }
+        }
+        this.trigger('data', event);
+        return;
+      }
+      match = /^#EXT-X-START:?(.*)$/.exec(line);
+      if (match) {
+        event = {
+          type: 'tag',
+          tagType: 'start'
+        };
+        if (match[1]) {
+          event.attributes = parseAttributes(match[1]);
+
+          event.attributes['TIME-OFFSET'] = parseFloat(event.attributes['TIME-OFFSET']);
+          event.attributes.PRECISE = /YES/.test(event.attributes.PRECISE);
+        }
+        this.trigger('data', event);
+        return;
+      }
+      match = /^#EXT-X-CUE-OUT-CONT:?(.*)?$/.exec(line);
+      if (match) {
+        event = {
+          type: 'tag',
+          tagType: 'cue-out-cont'
+        };
+        if (match[1]) {
+          event.data = match[1];
+        } else {
+          event.data = '';
+        }
+        this.trigger('data', event);
+        return;
+      }
+      match = /^#EXT-X-CUE-OUT:?(.*)?$/.exec(line);
+      if (match) {
+        event = {
+          type: 'tag',
+          tagType: 'cue-out'
+        };
+        if (match[1]) {
+          event.data = match[1];
+        } else {
+          event.data = '';
+        }
+        this.trigger('data', event);
+        return;
+      }
+      match = /^#EXT-X-CUE-IN:?(.*)?$/.exec(line);
+      if (match) {
+        event = {
+          type: 'tag',
+          tagType: 'cue-in'
+        };
+        if (match[1]) {
+          event.data = match[1];
+        } else {
+          event.data = '';
+        }
+        this.trigger('data', event);
+        return;
+      }
+
+      // unknown tag type
+      this.trigger('data', {
+        type: 'tag',
+        data: line.slice(4)
+      });
+    };
+
+    /**
+     * Add a parser for custom headers
+     *
+     * @param {Object}   options              a map of options for the added parser
+     * @param {RegExp}   options.expression   a regular expression to match the custom header
+     * @param {string}   options.customType   the custom type to register to the output
+     * @param {Function} [options.dataParser] function to parse the line into an object
+     * @param {boolean}  [options.segment]    should tag data be attached to the segment object
+     */
+
+    ParseStream.prototype.addParser = function addParser(_ref) {
+      var _this2 = this;
+
+      var expression = _ref.expression,
+        customType = _ref.customType,
+        dataParser = _ref.dataParser,
+        segment = _ref.segment;
+
+      if (typeof dataParser !== 'function') {
+        dataParser = function dataParser(line) {
+          return line;
+        };
+      }
+      this.customParsers.push(function (line) {
+        var match = expression.exec(line);
+
+        if (match) {
+          _this2.trigger('data', {
+            type: 'custom',
+            data: dataParser(line),
+            customType: customType,
+            segment: segment
+          });
+          return true;
+        }
+      });
+    };
+
+    return ParseStream;
+  }(Stream);
+
+  /**
+   * @file m3u8/parser.js
+   */
+  /**
+   * A parser for M3U8 files. The current interpretation of the input is
+   * exposed as a property `manifest` on parser objects. It's just two lines to
+   * create and parse a manifest once you have the contents available as a string:
+   *
+   * ```js
+   * var parser = new m3u8.Parser();
+   * parser.push(xhr.responseText);
+   * ```
+   *
+   * New input can later be applied to update the manifest object by calling
+   * `push` again.
+   *
+   * The parser attempts to create a usable manifest object even if the
+   * underlying input is somewhat nonsensical. It emits `info` and `warning`
+   * events during the parse if it encounters input that seems invalid or
+   * requires some property of the manifest object to be defaulted.
+   *
+   * @class Parser
+   * @extends Stream
+   */
+
+  var Parser = function (_Stream) {
+    inherits$1(Parser, _Stream);
+
+    function Parser() {
+      classCallCheck$1(this, Parser);
+
+      var _this = possibleConstructorReturn$1(this, _Stream.call(this));
+
+      _this.lineStream = new LineStream();
+      _this.parseStream = new ParseStream();
+      _this.lineStream.pipe(_this.parseStream);
+
+      /* eslint-disable consistent-this */
+      var self = _this;
+      /* eslint-enable consistent-this */
+      var uris = [];
+      var currentUri = {};
+      // if specified, the active EXT-X-MAP definition
+      var currentMap = void 0;
+      // if specified, the active decryption key
+      var _key = void 0;
+      var noop = function noop() {
+      };
+      var defaultMediaGroups = {
+        'AUDIO': {},
+        'VIDEO': {},
+        'CLOSED-CAPTIONS': {},
+        'SUBTITLES': {}
+      };
+      // group segments into numbered timelines delineated by discontinuities
+      var currentTimeline = 0;
+
+      // the manifest is empty until the parse stream begins delivering data
+      _this.manifest = {
+        allowCache: true,
+        discontinuityStarts: [],
+        segments: []
+      };
+
+      // update the manifest with the m3u8 entry from the parse stream
+      _this.parseStream.on('data', function (entry) {
+        var mediaGroup = void 0;
+        var rendition = void 0;
+
+        ({
+          tag: function tag() {
+            // switch based on the tag type
+            (({
+              'allow-cache': function allowCache() {
+                this.manifest.allowCache = entry.allowed;
+                if (!('allowed' in entry)) {
+                  this.trigger('info', {
+                    message: 'defaulting allowCache to YES'
+                  });
+                  this.manifest.allowCache = true;
+                }
+              },
+              byterange: function byterange() {
+                var byterange = {};
+
+                if ('length' in entry) {
+                  currentUri.byterange = byterange;
+                  byterange.length = entry.length;
+
+                  if (!('offset' in entry)) {
+                    this.trigger('info', {
+                      message: 'defaulting offset to zero'
+                    });
+                    entry.offset = 0;
+                  }
+                }
+                if ('offset' in entry) {
+                  currentUri.byterange = byterange;
+                  byterange.offset = entry.offset;
+                }
+              },
+              endlist: function endlist() {
+                this.manifest.endList = true;
+              },
+              inf: function inf() {
+                if (!('mediaSequence' in this.manifest)) {
+                  this.manifest.mediaSequence = 0;
+                  this.trigger('info', {
+                    message: 'defaulting media sequence to zero'
+                  });
+                }
+                if (!('discontinuitySequence' in this.manifest)) {
+                  this.manifest.discontinuitySequence = 0;
+                  this.trigger('info', {
+                    message: 'defaulting discontinuity sequence to zero'
+                  });
+                }
+                if (entry.duration > 0) {
+                  currentUri.duration = entry.duration;
+                }
+
+                if (entry.duration === 0) {
+                  currentUri.duration = 0.01;
+                  this.trigger('info', {
+                    message: 'updating zero segment duration to a small value'
+                  });
+                }
+
+                this.manifest.segments = uris;
+              },
+              key: function key() {
+                if (!entry.attributes) {
+                  this.trigger('warn', {
+                    message: 'ignoring key declaration without attribute list'
+                  });
+                  return;
+                }
+                // clear the active encryption key
+                if (entry.attributes.METHOD === 'NONE') {
+                  _key = null;
+                  return;
+                }
+                if (!entry.attributes.URI) {
+                  this.trigger('warn', {
+                    message: 'ignoring key declaration without URI'
+                  });
+                  return;
+                }
+                if (!entry.attributes.METHOD) {
+                  this.trigger('warn', {
+                    message: 'defaulting key method to AES-128'
+                  });
+                }
+
+                // setup an encryption key for upcoming segments
+                _key = {
+                  method: entry.attributes.METHOD || 'AES-128',
+                  uri: entry.attributes.URI
+                };
+
+                if (typeof entry.attributes.IV !== 'undefined') {
+                  _key.iv = entry.attributes.IV;
+                }
+              },
+              'media-sequence': function mediaSequence() {
+                if (!isFinite(entry.number)) {
+                  this.trigger('warn', {
+                    message: 'ignoring invalid media sequence: ' + entry.number
+                  });
+                  return;
+                }
+                this.manifest.mediaSequence = entry.number;
+              },
+              'discontinuity-sequence': function discontinuitySequence() {
+                if (!isFinite(entry.number)) {
+                  this.trigger('warn', {
+                    message: 'ignoring invalid discontinuity sequence: ' + entry.number
+                  });
+                  return;
+                }
+                this.manifest.discontinuitySequence = entry.number;
+                currentTimeline = entry.number;
+              },
+              'playlist-type': function playlistType() {
+                if (!/VOD|EVENT/.test(entry.playlistType)) {
+                  this.trigger('warn', {
+                    message: 'ignoring unknown playlist type: ' + entry.playlist
+                  });
+                  return;
+                }
+                this.manifest.playlistType = entry.playlistType;
+              },
+              map: function map() {
+                currentMap = {};
+                if (entry.uri) {
+                  currentMap.uri = entry.uri;
+                }
+                if (entry.byterange) {
+                  currentMap.byterange = entry.byterange;
+                }
+              },
+              'stream-inf': function streamInf() {
+                this.manifest.playlists = uris;
+                this.manifest.mediaGroups = this.manifest.mediaGroups || defaultMediaGroups;
+
+                if (!entry.attributes) {
+                  this.trigger('warn', {
+                    message: 'ignoring empty stream-inf attributes'
+                  });
+                  return;
+                }
+
+                if (!currentUri.attributes) {
+                  currentUri.attributes = {};
+                }
+                _extends$1(currentUri.attributes, entry.attributes);
+              },
+              media: function media() {
+                this.manifest.mediaGroups = this.manifest.mediaGroups || defaultMediaGroups;
+
+                if (!(entry.attributes && entry.attributes.TYPE && entry.attributes['GROUP-ID'] && entry.attributes.NAME)) {
+                  this.trigger('warn', {
+                    message: 'ignoring incomplete or missing media group'
+                  });
+                  return;
+                }
+
+                // find the media group, creating defaults as necessary
+                var mediaGroupType = this.manifest.mediaGroups[entry.attributes.TYPE];
+
+                mediaGroupType[entry.attributes['GROUP-ID']] = mediaGroupType[entry.attributes['GROUP-ID']] || {};
+                mediaGroup = mediaGroupType[entry.attributes['GROUP-ID']];
+
+                // collect the rendition metadata
+                rendition = {
+                  'default': /yes/i.test(entry.attributes.DEFAULT)
+                };
+                if (rendition['default']) {
+                  rendition.autoselect = true;
+                } else {
+                  rendition.autoselect = /yes/i.test(entry.attributes.AUTOSELECT);
+                }
+                if (entry.attributes.LANGUAGE) {
+                  rendition.language = entry.attributes.LANGUAGE;
+                }
+                if (entry.attributes.URI) {
+                  rendition.uri = entry.attributes.URI;
+                }
+                if (entry.attributes['INSTREAM-ID']) {
+                  rendition.instreamId = entry.attributes['INSTREAM-ID'];
+                }
+                if (entry.attributes.CHARACTERISTICS) {
+                  rendition.characteristics = entry.attributes.CHARACTERISTICS;
+                }
+                if (entry.attributes.FORCED) {
+                  rendition.forced = /yes/i.test(entry.attributes.FORCED);
+                }
+
+                // insert the new rendition
+                mediaGroup[entry.attributes.NAME] = rendition;
+              },
+              discontinuity: function discontinuity() {
+                currentTimeline += 1;
+                currentUri.discontinuity = true;
+                this.manifest.discontinuityStarts.push(uris.length);
+              },
+              'program-date-time': function programDateTime() {
+                if (typeof this.manifest.dateTimeString === 'undefined') {
+                  // PROGRAM-DATE-TIME is a media-segment tag, but for backwards
+                  // compatibility, we add the first occurence of the PROGRAM-DATE-TIME tag
+                  // to the manifest object
+                  // TODO: Consider removing this in future major version
+                  this.manifest.dateTimeString = entry.dateTimeString;
+                  this.manifest.dateTimeObject = entry.dateTimeObject;
+                }
+
+                currentUri.dateTimeString = entry.dateTimeString;
+                currentUri.dateTimeObject = entry.dateTimeObject;
+              },
+              targetduration: function targetduration() {
+                if (!isFinite(entry.duration) || entry.duration < 0) {
+                  this.trigger('warn', {
+                    message: 'ignoring invalid target duration: ' + entry.duration
+                  });
+                  return;
+                }
+                this.manifest.targetDuration = entry.duration;
+              },
+              totalduration: function totalduration() {
+                if (!isFinite(entry.duration) || entry.duration < 0) {
+                  this.trigger('warn', {
+                    message: 'ignoring invalid total duration: ' + entry.duration
+                  });
+                  return;
+                }
+                this.manifest.totalDuration = entry.duration;
+              },
+              start: function start() {
+                if (!entry.attributes || isNaN(entry.attributes['TIME-OFFSET'])) {
+                  this.trigger('warn', {
+                    message: 'ignoring start declaration without appropriate attribute list'
+                  });
+                  return;
+                }
+                this.manifest.start = {
+                  timeOffset: entry.attributes['TIME-OFFSET'],
+                  precise: entry.attributes.PRECISE
+                };
+              },
+              'cue-out': function cueOut() {
+                currentUri.cueOut = entry.data;
+              },
+              'cue-out-cont': function cueOutCont() {
+                currentUri.cueOutCont = entry.data;
+              },
+              'cue-in': function cueIn() {
+                currentUri.cueIn = entry.data;
+              }
+            })[entry.tagType] || noop).call(self);
+          },
+          uri: function uri() {
+            currentUri.uri = entry.uri;
+            uris.push(currentUri);
+
+            // if no explicit duration was declared, use the target duration
+            if (this.manifest.targetDuration && !('duration' in currentUri)) {
+              this.trigger('warn', {
+                message: 'defaulting segment duration to the target duration'
+              });
+              currentUri.duration = this.manifest.targetDuration;
+            }
+            // annotate with encryption information, if necessary
+            if (_key) {
+              currentUri.key = _key;
+            }
+            currentUri.timeline = currentTimeline;
+            // annotate with initialization segment information, if necessary
+            if (currentMap) {
+              currentUri.map = currentMap;
+            }
+
+            // prepare for the next URI
+            currentUri = {};
+          },
+          comment: function comment() {
+            // comments are not important for playback
+          },
+          custom: function custom() {
+            // if this is segment-level data attach the output to the segment
+            if (entry.segment) {
+              currentUri.custom = currentUri.custom || {};
+              currentUri.custom[entry.customType] = entry.data;
+              // if this is manifest-level data attach to the top level manifest object
+            } else {
+              this.manifest.custom = this.manifest.custom || {};
+              this.manifest.custom[entry.customType] = entry.data;
+            }
+          }
+        })[entry.type].call(self);
+      });
+      return _this;
+    }
+
+    /**
+     * Parse the input string and update the manifest object.
+     *
+     * @param {String} chunk a potentially incomplete portion of the manifest
+     */
+
+    Parser.prototype.push = function push(chunk) {
+      this.lineStream.push(chunk);
+    };
+
+    /**
+     * Flush any remaining input. This can be handy if the last line of an M3U8
+     * manifest did not contain a trailing newline but the file has been
+     * completely received.
+     */
+
+    Parser.prototype.end = function end() {
+      // flush any buffered input
+      this.lineStream.push('\n');
+    };
+    /**
+     * Add an additional parser for non-standard tags
+     *
+     * @param {Object}   options              a map of options for the added parser
+     * @param {RegExp}   options.expression   a regular expression to match the custom header
+     * @param {string}   options.type         the type to register to the output
+     * @param {Function} [options.dataParser] function to parse the line into an object
+     * @param {boolean}  [options.segment]    should tag data be attached to the segment object
+     */
+
+    Parser.prototype.addParser = function addParser(options) {
+      this.parseStream.addParser(options);
+    };
+
+    return Parser;
+  }(Stream);
+
+  /**
+   * mpd-parser
+   * @version 0.6.0
+   * @copyright 2018 Brightcove, Inc
+   * @license Apache-2.0
+   */
+
+  var formatAudioPlaylist = function formatAudioPlaylist(_ref) {
+    var _attributes;
+
+    var attributes = _ref.attributes,
+      segments = _ref.segments;
+
+    var playlist = {
+      attributes: (_attributes = {
+        NAME: attributes.id,
+        BANDWIDTH: attributes.bandwidth,
+        CODECS: attributes.codecs
+      }, _attributes['PROGRAM-ID'] = 1, _attributes),
+      uri: '',
+      endList: (attributes.type || 'static') === 'static',
+      timeline: attributes.periodIndex,
+      resolvedUri: '',
+      targetDuration: attributes.duration,
+      segments: segments,
+      mediaSequence: segments.length ? segments[0].number : 1
+    };
+
+    if (attributes.contentProtection) {
+      playlist.contentProtection = attributes.contentProtection;
+    }
+
+    return playlist;
+  };
+
+  var formatVttPlaylist = function formatVttPlaylist(_ref2) {
+    var _attributes2;
+
+    var attributes = _ref2.attributes,
+      segments = _ref2.segments;
+
+    if (typeof segments === 'undefined') {
+      // vtt tracks may use single file in BaseURL
+      segments = [{
+        uri: attributes.baseUrl,
+        timeline: attributes.periodIndex,
+        resolvedUri: attributes.baseUrl || '',
+        duration: attributes.sourceDuration,
+        number: 0
+      }];
+      // targetDuration should be the same duration as the only segment
+      attributes.duration = attributes.sourceDuration;
+    }
+    return {
+      attributes: (_attributes2 = {
+        NAME: attributes.id,
+        BANDWIDTH: attributes.bandwidth
+      }, _attributes2['PROGRAM-ID'] = 1, _attributes2),
+      uri: '',
+      endList: (attributes.type || 'static') === 'static',
+      timeline: attributes.periodIndex,
+      resolvedUri: attributes.baseUrl || '',
+      targetDuration: attributes.duration,
+      segments: segments,
+      mediaSequence: segments.length ? segments[0].number : 1
+    };
+  };
+
+  var organizeAudioPlaylists = function organizeAudioPlaylists(playlists) {
+    return playlists.reduce(function (a, playlist) {
+      var role = playlist.attributes.role && playlist.attributes.role.value || 'main';
+      var language = playlist.attributes.lang || '';
+
+      var label = 'main';
+
+      if (language) {
+        label = playlist.attributes.lang + ' (' + role + ')';
+      }
+
+      // skip if we already have the highest quality audio for a language
+      if (a[label] && a[label].playlists[0].attributes.BANDWIDTH > playlist.attributes.bandwidth) {
+        return a;
+      }
+
+      a[label] = {
+        language: language,
+        autoselect: true,
+        default: role === 'main',
+        playlists: [formatAudioPlaylist(playlist)],
+        uri: ''
+      };
+
+      return a;
+    }, {});
+  };
+
+  var organizeVttPlaylists = function organizeVttPlaylists(playlists) {
+    return playlists.reduce(function (a, playlist) {
+      var label = playlist.attributes.lang || 'text';
+
+      // skip if we already have subtitles
+      if (a[label]) {
+        return a;
+      }
+
+      a[label] = {
+        language: label,
+        default: false,
+        autoselect: false,
+        playlists: [formatVttPlaylist(playlist)],
+        uri: ''
+      };
+
+      return a;
+    }, {});
+  };
+
+  var formatVideoPlaylist = function formatVideoPlaylist(_ref3) {
+    var _attributes3;
+
+    var attributes = _ref3.attributes,
+      segments = _ref3.segments;
+
+    var playlist = {
+      attributes: (_attributes3 = {
+        NAME: attributes.id,
+        AUDIO: 'audio',
+        SUBTITLES: 'subs',
+        RESOLUTION: {
+          width: attributes.width,
+          height: attributes.height
+        },
+        CODECS: attributes.codecs,
+        BANDWIDTH: attributes.bandwidth
+      }, _attributes3['PROGRAM-ID'] = 1, _attributes3),
+      uri: '',
+      endList: (attributes.type || 'static') === 'static',
+      timeline: attributes.periodIndex,
+      resolvedUri: '',
+      targetDuration: attributes.duration,
+      segments: segments,
+      mediaSequence: segments.length ? segments[0].number : 1
+    };
+
+    if (attributes.contentProtection) {
+      playlist.contentProtection = attributes.contentProtection;
+    }
+
+    return playlist;
+  };
+
+  var toM3u8 = function toM3u8(dashPlaylists) {
+    var _mediaGroups;
+
+    if (!dashPlaylists.length) {
+      return {};
+    }
+
+    // grab all master attributes
+    var _dashPlaylists$0$attr = dashPlaylists[0].attributes,
+      duration = _dashPlaylists$0$attr.sourceDuration,
+      _dashPlaylists$0$attr2 = _dashPlaylists$0$attr.minimumUpdatePeriod,
+      minimumUpdatePeriod = _dashPlaylists$0$attr2 === undefined ? 0 : _dashPlaylists$0$attr2;
+
+
+    var videoOnly = function videoOnly(_ref4) {
+      var attributes = _ref4.attributes;
+      return attributes.mimeType === 'video/mp4' || attributes.contentType === 'video';
+    };
+    var audioOnly = function audioOnly(_ref5) {
+      var attributes = _ref5.attributes;
+      return attributes.mimeType === 'audio/mp4' || attributes.contentType === 'audio';
+    };
+    var vttOnly = function vttOnly(_ref6) {
+      var attributes = _ref6.attributes;
+      return attributes.mimeType === 'text/vtt' || attributes.contentType === 'text';
+    };
+
+    var videoPlaylists = dashPlaylists.filter(videoOnly).map(formatVideoPlaylist);
+    var audioPlaylists = dashPlaylists.filter(audioOnly);
+    var vttPlaylists = dashPlaylists.filter(vttOnly);
+
+    var master = {
+      allowCache: true,
+      discontinuityStarts: [],
+      segments: [],
+      endList: true,
+      mediaGroups: (_mediaGroups = {
+        AUDIO: {},
+        VIDEO: {}
+      }, _mediaGroups['CLOSED-CAPTIONS'] = {}, _mediaGroups.SUBTITLES = {}, _mediaGroups),
+      uri: '',
+      duration: duration,
+      playlists: videoPlaylists,
+      minimumUpdatePeriod: minimumUpdatePeriod * 1000
+    };
+
+    if (audioPlaylists.length) {
+      master.mediaGroups.AUDIO.audio = organizeAudioPlaylists(audioPlaylists);
+    }
+
+    if (vttPlaylists.length) {
+      master.mediaGroups.SUBTITLES.subs = organizeVttPlaylists(vttPlaylists);
+    }
+
+    return master;
+  };
+
+  var isObject$1 = function isObject(obj) {
+    return !!obj && (typeof obj === 'undefined' ? 'undefined' : _typeof(obj)) === 'object';
+  };
+
+  var merge = function merge() {
+    for (var _len = arguments.length, objects = Array(_len), _key = 0; _key < _len; _key++) {
+      objects[_key] = arguments[_key];
+    }
+
+    return objects.reduce(function (result, source) {
+
+      Object.keys(source).forEach(function (key) {
+
+        if (Array.isArray(result[key]) && Array.isArray(source[key])) {
+          result[key] = result[key].concat(source[key]);
+        } else if (isObject$1(result[key]) && isObject$1(source[key])) {
+          result[key] = merge(result[key], source[key]);
+        } else {
+          result[key] = source[key];
+        }
+      });
+      return result;
+    }, {});
+  };
+
+  var resolveUrl = function resolveUrl(baseUrl, relativeUrl) {
+    // return early if we don't need to resolve
+    if (/^[a-z]+:/i.test(relativeUrl)) {
+      return relativeUrl;
+    }
+
+    // if the base URL is relative then combine with the current location
+    if (!/\/\//i.test(baseUrl)) {
+      baseUrl = urlToolkit.buildAbsoluteURL(window_1.location.href, baseUrl);
+    }
+
+    return urlToolkit.buildAbsoluteURL(baseUrl, relativeUrl);
+  };
+
+  /**
+   * @typedef {Object} SingleUri
+   * @property {string} uri - relative location of segment
+   * @property {string} resolvedUri - resolved location of segment
+   * @property {Object} byterange - Object containing information on how to make byte range
+   *   requests following byte-range-spec per RFC2616.
+   * @property {String} byterange.length - length of range request
+   * @property {String} byterange.offset - byte offset of range request
+   *
+   * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.1
+   */
+
+  /**
+   * Converts a URLType node (5.3.9.2.3 Table 13) to a segment object
+   * that conforms to how m3u8-parser is structured
+   *
+   * @see https://github.com/videojs/m3u8-parser
+   *
+   * @param {string} baseUrl - baseUrl provided by <BaseUrl> nodes
+   * @param {string} source - source url for segment
+   * @param {string} range - optional range used for range calls, follows
+   * @return {SingleUri} full segment information transformed into a format similar
+   *   to m3u8-parser
+   */
+  var urlTypeToSegment = function urlTypeToSegment(_ref7) {
+    var _ref7$baseUrl = _ref7.baseUrl,
+      baseUrl = _ref7$baseUrl === undefined ? '' : _ref7$baseUrl,
+      _ref7$source = _ref7.source,
+      source = _ref7$source === undefined ? '' : _ref7$source,
+      _ref7$range = _ref7.range,
+      range = _ref7$range === undefined ? '' : _ref7$range;
+
+    var init = {
+      uri: source,
+      resolvedUri: resolveUrl(baseUrl || '', source)
+    };
+
+    if (range) {
+      var ranges = range.split('-');
+      var startRange = parseInt(ranges[0], 10);
+      var endRange = parseInt(ranges[1], 10);
+
+      init.byterange = {
+        length: endRange - startRange,
+        offset: startRange
+      };
+    }
+
+    return init;
+  };
+
+  /**
+   * Calculates the R (repetition) value for a live stream (for the final segment
+   * in a manifest where the r value is negative 1)
+   *
+   * @param {Object} attributes
+   *        Object containing all inherited attributes from parent elements with attribute
+   *        names as keys
+   * @param {number} time
+   *        current time (typically the total time up until the final segment)
+   * @param {number} duration
+   *        duration property for the given <S />
+   *
+   * @return {number}
+   *        R value to reach the end of the given period
+   */
+  var getLiveRValue = function getLiveRValue(attributes, time, duration) {
+    var NOW = attributes.NOW,
+      clientOffset = attributes.clientOffset,
+      availabilityStartTime = attributes.availabilityStartTime,
+      _attributes$timescale = attributes.timescale,
+      timescale = _attributes$timescale === undefined ? 1 : _attributes$timescale,
+      _attributes$start = attributes.start,
+      start = _attributes$start === undefined ? 0 : _attributes$start,
+      _attributes$minimumUp = attributes.minimumUpdatePeriod,
+      minimumUpdatePeriod = _attributes$minimumUp === undefined ? 0 : _attributes$minimumUp;
+
+    var now = (NOW + clientOffset) / 1000;
+    var periodStartWC = availabilityStartTime + start;
+    var periodEndWC = now + minimumUpdatePeriod;
+    var periodDuration = periodEndWC - periodStartWC;
+
+    return Math.ceil((periodDuration * timescale - time) / duration);
+  };
+
+  /**
+   * Uses information provided by SegmentTemplate.SegmentTimeline to determine segment
+   * timing and duration
+   *
+   * @param {Object} attributes
+   *        Object containing all inherited attributes from parent elements with attribute
+   *        names as keys
+   * @param {Object[]} segmentTimeline
+   *        List of objects representing the attributes of each S element contained within
+   *
+   * @return {{number: number, duration: number, time: number, timeline: number}[]}
+   *         List of Objects with segment timing and duration info
+   */
+  var parseByTimeline = function parseByTimeline(attributes, segmentTimeline) {
+    var _attributes$type = attributes.type,
+      type = _attributes$type === undefined ? 'static' : _attributes$type,
+      _attributes$minimumUp2 = attributes.minimumUpdatePeriod,
+      minimumUpdatePeriod = _attributes$minimumUp2 === undefined ? 0 : _attributes$minimumUp2,
+      _attributes$media = attributes.media,
+      media = _attributes$media === undefined ? '' : _attributes$media,
+      sourceDuration = attributes.sourceDuration,
+      _attributes$timescale2 = attributes.timescale,
+      timescale = _attributes$timescale2 === undefined ? 1 : _attributes$timescale2,
+      _attributes$startNumb = attributes.startNumber,
+      startNumber = _attributes$startNumb === undefined ? 1 : _attributes$startNumb,
+      timeline = attributes.periodIndex;
+
+    var segments = [];
+    var time = -1;
+
+    for (var sIndex = 0; sIndex < segmentTimeline.length; sIndex++) {
+      var S = segmentTimeline[sIndex];
+      var duration = S.d;
+      var repeat = S.r || 0;
+      var segmentTime = S.t || 0;
+
+      if (time < 0) {
+        // first segment
+        time = segmentTime;
+      }
+
+      if (segmentTime && segmentTime > time) {
+        // discontinuity
+
+        // TODO: How to handle this type of discontinuity
+        // timeline++ here would treat it like HLS discontuity and content would
+        // get appended without gap
+        // E.G.
+        //  <S t="0" d="1" />
+        //  <S d="1" />
+        //  <S d="1" />
+        //  <S t="5" d="1" />
+        // would have $Time$ values of [0, 1, 2, 5]
+        // should this be appened at time positions [0, 1, 2, 3],(#EXT-X-DISCONTINUITY)
+        // or [0, 1, 2, gap, gap, 5]? (#EXT-X-GAP)
+        // does the value of sourceDuration consider this when calculating arbitrary
+        // negative @r repeat value?
+        // E.G. Same elements as above with this added at the end
+        //  <S d="1" r="-1" />
+        //  with a sourceDuration of 10
+        // Would the 2 gaps be included in the time duration calculations resulting in
+        // 8 segments with $Time$ values of [0, 1, 2, 5, 6, 7, 8, 9] or 10 segments
+        // with $Time$ values of [0, 1, 2, 5, 6, 7, 8, 9, 10, 11] ?
+
+        time = segmentTime;
+      }
+
+      var count = void 0;
+
+      if (repeat < 0) {
+        var nextS = sIndex + 1;
+
+        if (nextS === segmentTimeline.length) {
+          // last segment
+          if (type === 'dynamic' && minimumUpdatePeriod > 0 && media.indexOf('$Number$') > 0) {
+            count = getLiveRValue(attributes, time, duration);
+          } else {
+            // TODO: This may be incorrect depending on conclusion of TODO above
+            count = (sourceDuration * timescale - time) / duration;
+          }
+        } else {
+          count = (segmentTimeline[nextS].t - time) / duration;
+        }
+      } else {
+        count = repeat + 1;
+      }
+
+      var end = startNumber + segments.length + count;
+      var number = startNumber + segments.length;
+
+      while (number < end) {
+        segments.push({number: number, duration: duration / timescale, time: time, timeline: timeline});
+        time += duration;
+        number++;
+      }
+    }
+
+    return segments;
+  };
+
+  var range = function range(start, end) {
+    var result = [];
+
+    for (var i = start; i < end; i++) {
+      result.push(i);
+    }
+
+    return result;
+  };
+
+  var flatten = function flatten(lists) {
+    return lists.reduce(function (x, y) {
+      return x.concat(y);
+    }, []);
+  };
+
+  var from = function from(list) {
+    if (!list.length) {
+      return [];
+    }
+
+    var result = [];
+
+    for (var i = 0; i < list.length; i++) {
+      result.push(list[i]);
+    }
+
+    return result;
+  };
+
+  /**
+   * Functions for calculating the range of available segments in static and dynamic
+   * manifests.
+   */
+  var segmentRange = {
+    /**
+     * Returns the entire range of available segments for a static MPD
+     *
+     * @param {Object} attributes
+     *        Inheritied MPD attributes
+     * @return {{ start: number, end: number }}
+     *         The start and end numbers for available segments
+     */
+    static: function _static(attributes) {
+      var duration = attributes.duration,
+        _attributes$timescale3 = attributes.timescale,
+        timescale = _attributes$timescale3 === undefined ? 1 : _attributes$timescale3,
+        sourceDuration = attributes.sourceDuration;
+
+
+      return {
+        start: 0,
+        end: Math.ceil(sourceDuration / (duration / timescale))
+      };
+    },
+
+
+    /**
+     * Returns the current live window range of available segments for a dynamic MPD
+     *
+     * @param {Object} attributes
+     *        Inheritied MPD attributes
+     * @return {{ start: number, end: number }}
+     *         The start and end numbers for available segments
+     */
+    dynamic: function dynamic(attributes) {
+      var NOW = attributes.NOW,
+        clientOffset = attributes.clientOffset,
+        availabilityStartTime = attributes.availabilityStartTime,
+        _attributes$timescale4 = attributes.timescale,
+        timescale = _attributes$timescale4 === undefined ? 1 : _attributes$timescale4,
+        duration = attributes.duration,
+        _attributes$start2 = attributes.start,
+        start = _attributes$start2 === undefined ? 0 : _attributes$start2,
+        _attributes$minimumUp3 = attributes.minimumUpdatePeriod,
+        minimumUpdatePeriod = _attributes$minimumUp3 === undefined ? 0 : _attributes$minimumUp3,
+        _attributes$timeShift = attributes.timeShiftBufferDepth,
+        timeShiftBufferDepth = _attributes$timeShift === undefined ? Infinity : _attributes$timeShift;
+
+      var now = (NOW + clientOffset) / 1000;
+      var periodStartWC = availabilityStartTime + start;
+      var periodEndWC = now + minimumUpdatePeriod;
+      var periodDuration = periodEndWC - periodStartWC;
+      var segmentCount = Math.ceil(periodDuration * timescale / duration);
+      var availableStart = Math.floor((now - periodStartWC - timeShiftBufferDepth) * timescale / duration);
+      var availableEnd = Math.floor((now - periodStartWC) * timescale / duration);
+
+      return {
+        start: Math.max(0, availableStart),
+        end: Math.min(segmentCount, availableEnd)
+      };
+    }
+  };
+
+  /**
+   * Maps a range of numbers to objects with information needed to build the corresponding
+   * segment list
+   *
+   * @name toSegmentsCallback
+   * @function
+   * @param {number} number
+   *        Number of the segment
+   * @param {number} index
+   *        Index of the number in the range list
+   * @return {{ number: Number, duration: Number, timeline: Number, time: Number }}
+   *         Object with segment timing and duration info
+   */
+
+  /**
+   * Returns a callback for Array.prototype.map for mapping a range of numbers to
+   * information needed to build the segment list.
+   *
+   * @param {Object} attributes
+   *        Inherited MPD attributes
+   * @return {toSegmentsCallback}
+   *         Callback map function
+   */
+  var toSegments = function toSegments(attributes) {
+    return function (number, index) {
+      var duration = attributes.duration,
+        _attributes$timescale5 = attributes.timescale,
+        timescale = _attributes$timescale5 === undefined ? 1 : _attributes$timescale5,
+        periodIndex = attributes.periodIndex,
+        _attributes$startNumb2 = attributes.startNumber,
+        startNumber = _attributes$startNumb2 === undefined ? 1 : _attributes$startNumb2;
+
+
+      return {
+        number: startNumber + number,
+        duration: duration / timescale,
+        timeline: periodIndex,
+        time: index * duration
+      };
+    };
+  };
+
+  /**
+   * Returns a list of objects containing segment timing and duration info used for
+   * building the list of segments. This uses the @duration attribute specified
+   * in the MPD manifest to derive the range of segments.
+   *
+   * @param {Object} attributes
+   *        Inherited MPD attributes
+   * @return {{number: number, duration: number, time: number, timeline: number}[]}
+   *         List of Objects with segment timing and duration info
+   */
+  var parseByDuration = function parseByDuration(attributes) {
+    var _attributes$type2 = attributes.type,
+      type = _attributes$type2 === undefined ? 'static' : _attributes$type2,
+      duration = attributes.duration,
+      _attributes$timescale6 = attributes.timescale,
+      timescale = _attributes$timescale6 === undefined ? 1 : _attributes$timescale6,
+      sourceDuration = attributes.sourceDuration;
+
+    var _segmentRange$type = segmentRange[type](attributes),
+      start = _segmentRange$type.start,
+      end = _segmentRange$type.end;
+
+    var segments = range(start, end).map(toSegments(attributes));
+
+    if (type === 'static') {
+      var index = segments.length - 1;
+
+      // final segment may be less than full segment duration
+      segments[index].duration = sourceDuration - duration / timescale * index;
+    }
+
+    return segments;
+  };
+
+  var identifierPattern = /\$([A-z]*)(?:(%0)([0-9]+)d)?\$/g;
+
+  /**
+   * Replaces template identifiers with corresponding values. To be used as the callback
+   * for String.prototype.replace
+   *
+   * @name replaceCallback
+   * @function
+   * @param {string} match
+   *        Entire match of identifier
+   * @param {string} identifier
+   *        Name of matched identifier
+   * @param {string} format
+   *        Format tag string. Its presence indicates that padding is expected
+   * @param {string} width
+   *        Desired length of the replaced value. Values less than this width shall be left
+   *        zero padded
+   * @return {string}
+   *         Replacement for the matched identifier
+   */
+
+  /**
+   * Returns a function to be used as a callback for String.prototype.replace to replace
+   * template identifiers
+   *
+   * @param {Obect} values
+   *        Object containing values that shall be used to replace known identifiers
+   * @param {number} values.RepresentationID
+   *        Value of the Representation@id attribute
+   * @param {number} values.Number
+   *        Number of the corresponding segment
+   * @param {number} values.Bandwidth
+   *        Value of the Representation@bandwidth attribute.
+   * @param {number} values.Time
+   *        Timestamp value of the corresponding segment
+   * @return {replaceCallback}
+   *         Callback to be used with String.prototype.replace to replace identifiers
+   */
+  var identifierReplacement = function identifierReplacement(values) {
+    return function (match, identifier, format, width) {
+      if (match === '$$') {
+        // escape sequence
+        return '$';
+      }
+
+      if (typeof values[identifier] === 'undefined') {
+        return match;
+      }
+
+      var value = '' + values[identifier];
+
+      if (identifier === 'RepresentationID') {
+        // Format tag shall not be present with RepresentationID
+        return value;
+      }
+
+      if (!format) {
+        width = 1;
+      } else {
+        width = parseInt(width, 10);
+      }
+
+      if (value.length >= width) {
+        return value;
+      }
+
+      return '' + new Array(width - value.length + 1).join('0') + value;
+    };
+  };
+
+  /**
+   * Constructs a segment url from a template string
+   *
+   * @param {string} url
+   *        Template string to construct url from
+   * @param {Obect} values
+   *        Object containing values that shall be used to replace known identifiers
+   * @param {number} values.RepresentationID
+   *        Value of the Representation@id attribute
+   * @param {number} values.Number
+   *        Number of the corresponding segment
+   * @param {number} values.Bandwidth
+   *        Value of the Representation@bandwidth attribute.
+   * @param {number} values.Time
+   *        Timestamp value of the corresponding segment
+   * @return {string}
+   *         Segment url with identifiers replaced
+   */
+  var constructTemplateUrl = function constructTemplateUrl(url, values) {
+    return url.replace(identifierPattern, identifierReplacement(values));
+  };
+
+  /**
+   * Generates a list of objects containing timing and duration information about each
+   * segment needed to generate segment uris and the complete segment object
+   *
+   * @param {Object} attributes
+   *        Object containing all inherited attributes from parent elements with attribute
+   *        names as keys
+   * @param {Object[]|undefined} segmentTimeline
+   *        List of objects representing the attributes of each S element contained within
+   *        the SegmentTimeline element
+   * @return {{number: number, duration: number, time: number, timeline: number}[]}
+   *         List of Objects with segment timing and duration info
+   */
+  var parseTemplateInfo = function parseTemplateInfo(attributes, segmentTimeline) {
+    if (!attributes.duration && !segmentTimeline) {
+      // if neither @duration or SegmentTimeline are present, then there shall be exactly
+      // one media segment
+      return [{
+        number: attributes.startNumber || 1,
+        duration: attributes.sourceDuration,
+        time: 0,
+        timeline: attributes.periodIndex
+      }];
+    }
+
+    if (attributes.duration) {
+      return parseByDuration(attributes);
+    }
+
+    return parseByTimeline(attributes, segmentTimeline);
+  };
+
+  /**
+   * Generates a list of segments using information provided by the SegmentTemplate element
+   *
+   * @param {Object} attributes
+   *        Object containing all inherited attributes from parent elements with attribute
+   *        names as keys
+   * @param {Object[]|undefined} segmentTimeline
+   *        List of objects representing the attributes of each S element contained within
+   *        the SegmentTimeline element
+   * @return {Object[]}
+   *         List of segment objects
+   */
+  var segmentsFromTemplate = function segmentsFromTemplate(attributes, segmentTimeline) {
+    var templateValues = {
+      RepresentationID: attributes.id,
+      Bandwidth: attributes.bandwidth || 0
+    };
+
+    var _attributes$initializ = attributes.initialization,
+      initialization = _attributes$initializ === undefined ? {sourceURL: '', range: ''} : _attributes$initializ;
+
+
+    var mapSegment = urlTypeToSegment({
+      baseUrl: attributes.baseUrl,
+      source: constructTemplateUrl(initialization.sourceURL, templateValues),
+      range: initialization.range
+    });
+
+    var segments = parseTemplateInfo(attributes, segmentTimeline);
+
+    return segments.map(function (segment) {
+      templateValues.Number = segment.number;
+      templateValues.Time = segment.time;
+
+      var uri = constructTemplateUrl(attributes.media || '', templateValues);
+
+      return {
+        uri: uri,
+        timeline: segment.timeline,
+        duration: segment.duration,
+        resolvedUri: resolveUrl(attributes.baseUrl || '', uri),
+        map: mapSegment,
+        number: segment.number
+      };
+    });
+  };
+
+  var errors = {
+    INVALID_NUMBER_OF_PERIOD: 'INVALID_NUMBER_OF_PERIOD',
+    DASH_EMPTY_MANIFEST: 'DASH_EMPTY_MANIFEST',
+    DASH_INVALID_XML: 'DASH_INVALID_XML',
+    NO_BASE_URL: 'NO_BASE_URL',
+    MISSING_SEGMENT_INFORMATION: 'MISSING_SEGMENT_INFORMATION',
+    SEGMENT_TIME_UNSPECIFIED: 'SEGMENT_TIME_UNSPECIFIED',
+    UNSUPPORTED_UTC_TIMING_SCHEME: 'UNSUPPORTED_UTC_TIMING_SCHEME'
+  };
+
+  /**
+   * Converts a <SegmentUrl> (of type URLType from the DASH spec 5.3.9.2 Table 14)
+   * to an object that matches the output of a segment in videojs/mpd-parser
+   *
+   * @param {Object} attributes
+   *   Object containing all inherited attributes from parent elements with attribute
+   *   names as keys
+   * @param {Object} segmentUrl
+   *   <SegmentURL> node to translate into a segment object
+   * @return {Object} translated segment object
+   */
+  var SegmentURLToSegmentObject = function SegmentURLToSegmentObject(attributes, segmentUrl) {
+    var baseUrl = attributes.baseUrl,
+      _attributes$initializ2 = attributes.initialization,
+      initialization = _attributes$initializ2 === undefined ? {} : _attributes$initializ2;
+
+
+    var initSegment = urlTypeToSegment({
+      baseUrl: baseUrl,
+      source: initialization.sourceURL,
+      range: initialization.range
+    });
+
+    var segment = urlTypeToSegment({
+      baseUrl: baseUrl,
+      source: segmentUrl.media,
+      range: segmentUrl.mediaRange
+    });
+
+    segment.map = initSegment;
+
+    return segment;
+  };
+
+  /**
+   * Generates a list of segments using information provided by the SegmentList element
+   * SegmentList (DASH SPEC Section 5.3.9.3.2) contains a set of <SegmentURL> nodes.  Each
+   * node should be translated into segment.
+   *
+   * @param {Object} attributes
+   *   Object containing all inherited attributes from parent elements with attribute
+   *   names as keys
+   * @param {Object[]|undefined} segmentTimeline
+   *        List of objects representing the attributes of each S element contained within
+   *        the SegmentTimeline element
+   * @return {Object.<Array>} list of segments
+   */
+  var segmentsFromList = function segmentsFromList(attributes, segmentTimeline) {
+    var duration = attributes.duration,
+      _attributes$segmentUr = attributes.segmentUrls,
+      segmentUrls = _attributes$segmentUr === undefined ? [] : _attributes$segmentUr;
+
+    // Per spec (5.3.9.2.1) no way to determine segment duration OR
+    // if both SegmentTimeline and @duration are defined, it is outside of spec.
+
+    if (!duration && !segmentTimeline || duration && segmentTimeline) {
+      throw new Error(errors.SEGMENT_TIME_UNSPECIFIED);
+    }
+
+    var segmentUrlMap = segmentUrls.map(function (segmentUrlObject) {
+      return SegmentURLToSegmentObject(attributes, segmentUrlObject);
+    });
+    var segmentTimeInfo = void 0;
+
+    if (duration) {
+      segmentTimeInfo = parseByDuration(attributes);
+    }
+
+    if (segmentTimeline) {
+      segmentTimeInfo = parseByTimeline(attributes, segmentTimeline);
+    }
+
+    var segments = segmentTimeInfo.map(function (segmentTime, index) {
+      if (segmentUrlMap[index]) {
+        var segment = segmentUrlMap[index];
+
+        segment.timeline = segmentTime.timeline;
+        segment.duration = segmentTime.duration;
+        segment.number = segmentTime.number;
+        return segment;
+      }
+      // Since we're mapping we should get rid of any blank segments (in case
+      // the given SegmentTimeline is handling for more elements than we have
+      // SegmentURLs for).
+    }).filter(function (segment) {
+      return segment;
+    });
+
+    return segments;
+  };
+
+  /**
+   * Translates SegmentBase into a set of segments.
+   * (DASH SPEC Section 5.3.9.3.2) contains a set of <SegmentURL> nodes.  Each
+   * node should be translated into segment.
+   *
+   * @param {Object} attributes
+   *   Object containing all inherited attributes from parent elements with attribute
+   *   names as keys
+   * @return {Object.<Array>} list of segments
+   */
+  var segmentsFromBase = function segmentsFromBase(attributes) {
+    var baseUrl = attributes.baseUrl,
+      _attributes$initializ3 = attributes.initialization,
+      initialization = _attributes$initializ3 === undefined ? {} : _attributes$initializ3,
+      sourceDuration = attributes.sourceDuration,
+      _attributes$timescale7 = attributes.timescale,
+      timescale = _attributes$timescale7 === undefined ? 1 : _attributes$timescale7,
+      _attributes$indexRang = attributes.indexRange,
+      indexRange = _attributes$indexRang === undefined ? '' : _attributes$indexRang,
+      duration = attributes.duration;
+
+    // base url is required for SegmentBase to work, per spec (Section 5.3.9.2.1)
+
+    if (!baseUrl) {
+      throw new Error(errors.NO_BASE_URL);
+    }
+
+    var initSegment = urlTypeToSegment({
+      baseUrl: baseUrl,
+      source: initialization.sourceURL,
+      range: initialization.range
+    });
+    var segment = urlTypeToSegment({baseUrl: baseUrl, source: baseUrl, range: indexRange});
+
+    segment.map = initSegment;
+
+    // If there is a duration, use it, otherwise use the given duration of the source
+    // (since SegmentBase is only for one total segment)
+    if (duration) {
+      var segmentTimeInfo = parseByDuration(attributes);
+
+      if (segmentTimeInfo.length) {
+        segment.duration = segmentTimeInfo[0].duration;
+        segment.timeline = segmentTimeInfo[0].timeline;
+      }
+    } else if (sourceDuration) {
+      segment.duration = sourceDuration / timescale;
+      segment.timeline = 0;
+    }
+
+    // This is used for mediaSequence
+    segment.number = 0;
+
+    return [segment];
+  };
+
+  var generateSegments = function generateSegments(_ref8) {
+    var attributes = _ref8.attributes,
+      segmentInfo = _ref8.segmentInfo;
+
+    var segmentAttributes = void 0;
+    var segmentsFn = void 0;
+
+    if (segmentInfo.template) {
+      segmentsFn = segmentsFromTemplate;
+      segmentAttributes = merge(attributes, segmentInfo.template);
+    } else if (segmentInfo.base) {
+      segmentsFn = segmentsFromBase;
+      segmentAttributes = merge(attributes, segmentInfo.base);
+    } else if (segmentInfo.list) {
+      segmentsFn = segmentsFromList;
+      segmentAttributes = merge(attributes, segmentInfo.list);
+    }
+
+    if (!segmentsFn) {
+      return {attributes: attributes};
+    }
+
+    var segments = segmentsFn(segmentAttributes, segmentInfo.timeline);
+
+    // The @duration attribute will be used to determin the playlist's targetDuration which
+    // must be in seconds. Since we've generated the segment list, we no longer need
+    // @duration to be in @timescale units, so we can convert it here.
+    if (segmentAttributes.duration) {
+      var _segmentAttributes = segmentAttributes,
+        duration = _segmentAttributes.duration,
+        _segmentAttributes$ti = _segmentAttributes.timescale,
+        timescale = _segmentAttributes$ti === undefined ? 1 : _segmentAttributes$ti;
+
+
+      segmentAttributes.duration = duration / timescale;
+    } else if (segments.length) {
+      // if there is no @duration attribute, use the largest segment duration as
+      // as target duration
+      segmentAttributes.duration = segments.reduce(function (max, segment) {
+        return Math.max(max, Math.ceil(segment.duration));
+      }, 0);
+    } else {
+      segmentAttributes.duration = 0;
+    }
+
+    return {
+      attributes: segmentAttributes,
+      segments: segments
+    };
+  };
+
+  var toPlaylists = function toPlaylists(representations) {
+    return representations.map(generateSegments);
+  };
+
+  var findChildren = function findChildren(element, name) {
+    return from(element.childNodes).filter(function (_ref9) {
+      var tagName = _ref9.tagName;
+      return tagName === name;
+    });
+  };
+
+  var getContent = function getContent(element) {
+    return element.textContent.trim();
+  };
+
+  var parseDuration = function parseDuration(str) {
+    var SECONDS_IN_YEAR = 365 * 24 * 60 * 60;
+    var SECONDS_IN_MONTH = 30 * 24 * 60 * 60;
+    var SECONDS_IN_DAY = 24 * 60 * 60;
+    var SECONDS_IN_HOUR = 60 * 60;
+    var SECONDS_IN_MIN = 60;
+
+    // P10Y10M10DT10H10M10.1S
+    var durationRegex = /P(?:(\d*)Y)?(?:(\d*)M)?(?:(\d*)D)?(?:T(?:(\d*)H)?(?:(\d*)M)?(?:([\d.]*)S)?)?/;
+    var match = durationRegex.exec(str);
+
+    if (!match) {
+      return 0;
+    }
+
+    var _match$slice = match.slice(1),
+      year = _match$slice[0],
+      month = _match$slice[1],
+      day = _match$slice[2],
+      hour = _match$slice[3],
+      minute = _match$slice[4],
+      second = _match$slice[5];
+
+    return parseFloat(year || 0) * SECONDS_IN_YEAR + parseFloat(month || 0) * SECONDS_IN_MONTH + parseFloat(day || 0) * SECONDS_IN_DAY + parseFloat(hour || 0) * SECONDS_IN_HOUR + parseFloat(minute || 0) * SECONDS_IN_MIN + parseFloat(second || 0);
+  };
+
+  var parseDate = function parseDate(str) {
+    // Date format without timezone according to ISO 8601
+    // YYY-MM-DDThh:mm:ss.ssssss
+    var dateRegex = /^\d+-\d+-\d+T\d+:\d+:\d+(\.\d+)?$/;
+
+    // If the date string does not specifiy a timezone, we must specifiy UTC. This is
+    // expressed by ending with 'Z'
+    if (dateRegex.test(str)) {
+      str += 'Z';
+    }
+
+    return Date.parse(str);
+  };
+
+  // TODO: maybe order these in some way that makes it easy to find specific attributes
+  var parsers = {
+    /**
+     * Specifies the duration of the entire Media Presentation. Format is a duration string
+     * as specified in ISO 8601
+     *
+     * @param {string} value
+     *        value of attribute as a string
+     * @return {number}
+     *         The duration in seconds
+     */
+    mediaPresentationDuration: function mediaPresentationDuration(value) {
+      return parseDuration(value);
+    },
+
+
+    /**
+     * Specifies the Segment availability start time for all Segments referred to in this
+     * MPD. For a dynamic manifest, it specifies the anchor for the earliest availability
+     * time. Format is a date string as specified in ISO 8601
+     *
+     * @param {string} value
+     *        value of attribute as a string
+     * @return {number}
+     *         The date as seconds from unix epoch
+     */
+    availabilityStartTime: function availabilityStartTime(value) {
+      return parseDate(value) / 1000;
+    },
+
+
+    /**
+     * Specifies the smallest period between potential changes to the MPD. Format is a
+     * duration string as specified in ISO 8601
+     *
+     * @param {string} value
+     *        value of attribute as a string
+     * @return {number}
+     *         The duration in seconds
+     */
+    minimumUpdatePeriod: function minimumUpdatePeriod(value) {
+      return parseDuration(value);
+    },
+
+
+    /**
+     * Specifies the duration of the smallest time shifting buffer for any Representation
+     * in the MPD. Format is a duration string as specified in ISO 8601
+     *
+     * @param {string} value
+     *        value of attribute as a string
+     * @return {number}
+     *         The duration in seconds
+     */
+    timeShiftBufferDepth: function timeShiftBufferDepth(value) {
+      return parseDuration(value);
+    },
+
+
+    /**
+     * Specifies the PeriodStart time of the Period relative to the availabilityStarttime.
+     * Format is a duration string as specified in ISO 8601
+     *
+     * @param {string} value
+     *        value of attribute as a string
+     * @return {number}
+     *         The duration in seconds
+     */
+    start: function start(value) {
+      return parseDuration(value);
+    },
+
+
+    /**
+     * Specifies the width of the visual presentation
+     *
+     * @param {string} value
+     *        value of attribute as a string
+     * @return {number}
+     *         The parsed width
+     */
+    width: function width(value) {
+      return parseInt(value, 10);
+    },
+
+
+    /**
+     * Specifies the height of the visual presentation
+     *
+     * @param {string} value
+     *        value of attribute as a string
+     * @return {number}
+     *         The parsed height
+     */
+    height: function height(value) {
+      return parseInt(value, 10);
+    },
+
+
+    /**
+     * Specifies the bitrate of the representation
+     *
+     * @param {string} value
+     *        value of attribute as a string
+     * @return {number}
+     *         The parsed bandwidth
+     */
+    bandwidth: function bandwidth(value) {
+      return parseInt(value, 10);
+    },
+
+
+    /**
+     * Specifies the number of the first Media Segment in this Representation in the Period
+     *
+     * @param {string} value
+     *        value of attribute as a string
+     * @return {number}
+     *         The parsed number
+     */
+    startNumber: function startNumber(value) {
+      return parseInt(value, 10);
+    },
+
+
+    /**
+     * Specifies the timescale in units per seconds
+     *
+     * @param {string} value
+     *        value of attribute as a string
+     * @return {number}
+     *         The aprsed timescale
+     */
+    timescale: function timescale(value) {
+      return parseInt(value, 10);
+    },
+
+
+    /**
+     * Specifies the constant approximate Segment duration
+     * NOTE: The <Period> element also contains an @duration attribute. This duration
+     *       specifies the duration of the Period. This attribute is currently not
+     *       supported by the rest of the parser, however we still check for it to prevent
+     *       errors.
+     *
+     * @param {string} value
+     *        value of attribute as a string
+     * @return {number}
+     *         The parsed duration
+     */
+    duration: function duration(value) {
+      var parsedValue = parseInt(value, 10);
+
+      if (isNaN(parsedValue)) {
+        return parseDuration(value);
+      }
+
+      return parsedValue;
+    },
+
+
+    /**
+     * Specifies the Segment duration, in units of the value of the @timescale.
+     *
+     * @param {string} value
+     *        value of attribute as a string
+     * @return {number}
+     *         The parsed duration
+     */
+    d: function d(value) {
+      return parseInt(value, 10);
+    },
+
+
+    /**
+     * Specifies the MPD start time, in @timescale units, the first Segment in the series
+     * starts relative to the beginning of the Period
+     *
+     * @param {string} value
+     *        value of attribute as a string
+     * @return {number}
+     *         The parsed time
+     */
+    t: function t(value) {
+      return parseInt(value, 10);
+    },
+
+
+    /**
+     * Specifies the repeat count of the number of following contiguous Segments with the
+     * same duration expressed by the value of @d
+     *
+     * @param {string} value
+     *        value of attribute as a string
+     * @return {number}
+     *         The parsed number
+     */
+    r: function r(value) {
+      return parseInt(value, 10);
+    },
+
+
+    /**
+     * Default parser for all other attributes. Acts as a no-op and just returns the value
+     * as a string
+     *
+     * @param {string} value
+     *        value of attribute as a string
+     * @return {string}
+     *         Unparsed value
+     */
+    DEFAULT: function DEFAULT(value) {
+      return value;
+    }
+  };
+
+  /**
+   * Gets all the attributes and values of the provided node, parses attributes with known
+   * types, and returns an object with attribute names mapped to values.
+   *
+   * @param {Node} el
+   *        The node to parse attributes from
+   * @return {Object}
+   *         Object with all attributes of el parsed
+   */
+  var parseAttributes$1 = function parseAttributes(el) {
+    if (!(el && el.attributes)) {
+      return {};
+    }
+
+    return from(el.attributes).reduce(function (a, e) {
+      var parseFn = parsers[e.name] || parsers.DEFAULT;
+
+      a[e.name] = parseFn(e.value);
+
+      return a;
+    }, {});
+  };
+
+  function decodeB64ToUint8Array(b64Text) {
+    var decodedString = window_1.atob(b64Text);
+    var array = new Uint8Array(decodedString.length);
+
+    for (var i = 0; i < decodedString.length; i++) {
+      array[i] = decodedString.charCodeAt(i);
+    }
+    return array;
+  }
+
+  var keySystemsMap = {
+    'urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b': 'org.w3.clearkey',
+    'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed': 'com.widevine.alpha',
+    'urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95': 'com.microsoft.playready',
+    'urn:uuid:f239e769-efa3-4850-9c16-a903c6932efb': 'com.adobe.primetime'
+  };
+
+  /**
+   * Builds a list of urls that is the product of the reference urls and BaseURL values
+   *
+   * @param {string[]} referenceUrls
+   *        List of reference urls to resolve to
+   * @param {Node[]} baseUrlElements
+   *        List of BaseURL nodes from the mpd
+   * @return {string[]}
+   *         List of resolved urls
+   */
+  var buildBaseUrls = function buildBaseUrls(referenceUrls, baseUrlElements) {
+    if (!baseUrlElements.length) {
+      return referenceUrls;
+    }
+
+    return flatten(referenceUrls.map(function (reference) {
+      return baseUrlElements.map(function (baseUrlElement) {
+        return resolveUrl(reference, getContent(baseUrlElement));
+      });
+    }));
+  };
+
+  /**
+   * Contains all Segment information for its containing AdaptationSet
+   *
+   * @typedef {Object} SegmentInformation
+   * @property {Object|undefined} template
+   *           Contains the attributes for the SegmentTemplate node
+   * @property {Object[]|undefined} timeline
+   *           Contains a list of atrributes for each S node within the SegmentTimeline node
+   * @property {Object|undefined} list
+   *           Contains the attributes for the SegmentList node
+   * @property {Object|undefined} base
+   *           Contains the attributes for the SegmentBase node
+   */
+
+  /**
+   * Returns all available Segment information contained within the AdaptationSet node
+   *
+   * @param {Node} adaptationSet
+   *        The AdaptationSet node to get Segment information from
+   * @return {SegmentInformation}
+   *         The Segment information contained within the provided AdaptationSet
+   */
+  var getSegmentInformation = function getSegmentInformation(adaptationSet) {
+    var segmentTemplate = findChildren(adaptationSet, 'SegmentTemplate')[0];
+    var segmentList = findChildren(adaptationSet, 'SegmentList')[0];
+    var segmentUrls = segmentList && findChildren(segmentList, 'SegmentURL').map(function (s) {
+      return merge({tag: 'SegmentURL'}, parseAttributes$1(s));
+    });
+    var segmentBase = findChildren(adaptationSet, 'SegmentBase')[0];
+    var segmentTimelineParentNode = segmentList || segmentTemplate;
+    var segmentTimeline = segmentTimelineParentNode && findChildren(segmentTimelineParentNode, 'SegmentTimeline')[0];
+    var segmentInitializationParentNode = segmentList || segmentBase || segmentTemplate;
+    var segmentInitialization = segmentInitializationParentNode && findChildren(segmentInitializationParentNode, 'Initialization')[0];
+
+    // SegmentTemplate is handled slightly differently, since it can have both
+    // @initialization and an <Initialization> node.  @initialization can be templated,
+    // while the node can have a url and range specified.  If the <SegmentTemplate> has
+    // both @initialization and an <Initialization> subelement we opt to override with
+    // the node, as this interaction is not defined in the spec.
+    var template = segmentTemplate && parseAttributes$1(segmentTemplate);
+
+    if (template && segmentInitialization) {
+      template.initialization = segmentInitialization && parseAttributes$1(segmentInitialization);
+    } else if (template && template.initialization) {
+      // If it is @initialization we convert it to an object since this is the format that
+      // later functions will rely on for the initialization segment.  This is only valid
+      // for <SegmentTemplate>
+      template.initialization = {sourceURL: template.initialization};
+    }
+
+    var segmentInfo = {
+      template: template,
+      timeline: segmentTimeline && findChildren(segmentTimeline, 'S').map(function (s) {
+        return parseAttributes$1(s);
+      }),
+      list: segmentList && merge(parseAttributes$1(segmentList), {
+        segmentUrls: segmentUrls,
+        initialization: parseAttributes$1(segmentInitialization)
+      }),
+      base: segmentBase && merge(parseAttributes$1(segmentBase), {
+        initialization: parseAttributes$1(segmentInitialization)
+      })
+    };
+
+    Object.keys(segmentInfo).forEach(function (key) {
+      if (!segmentInfo[key]) {
+        delete segmentInfo[key];
+      }
+    });
+
+    return segmentInfo;
+  };
+
+  /**
+   * Contains Segment information and attributes needed to construct a Playlist object
+   * from a Representation
+   *
+   * @typedef {Object} RepresentationInformation
+   * @property {SegmentInformation} segmentInfo
+   *           Segment information for this Representation
+   * @property {Object} attributes
+   *           Inherited attributes for this Representation
+   */
+
+  /**
+   * Maps a Representation node to an object containing Segment information and attributes
+   *
+   * @name inheritBaseUrlsCallback
+   * @function
+   * @param {Node} representation
+   *        Representation node from the mpd
+   * @return {RepresentationInformation}
+   *         Representation information needed to construct a Playlist object
+   */
+
+  /**
+   * Returns a callback for Array.prototype.map for mapping Representation nodes to
+   * Segment information and attributes using inherited BaseURL nodes.
+   *
+   * @param {Object} adaptationSetAttributes
+   *        Contains attributes inherited by the AdaptationSet
+   * @param {string[]} adaptationSetBaseUrls
+   *        Contains list of resolved base urls inherited by the AdaptationSet
+   * @param {SegmentInformation} adaptationSetSegmentInfo
+   *        Contains Segment information for the AdaptationSet
+   * @return {inheritBaseUrlsCallback}
+   *         Callback map function
+   */
+  var inheritBaseUrls = function inheritBaseUrls(adaptationSetAttributes, adaptationSetBaseUrls, adaptationSetSegmentInfo) {
+    return function (representation) {
+      var repBaseUrlElements = findChildren(representation, 'BaseURL');
+      var repBaseUrls = buildBaseUrls(adaptationSetBaseUrls, repBaseUrlElements);
+      var attributes = merge(adaptationSetAttributes, parseAttributes$1(representation));
+      var representationSegmentInfo = getSegmentInformation(representation);
+
+      return repBaseUrls.map(function (baseUrl) {
+        return {
+          segmentInfo: merge(adaptationSetSegmentInfo, representationSegmentInfo),
+          attributes: merge(attributes, {baseUrl: baseUrl})
+        };
+      });
+    };
+  };
+
+  /**
+   * Tranforms a series of content protection nodes to
+   * an object containing pssh data by key system
+   *
+   * @param {Node[]} contentProtectionNodes
+   *        Content protection nodes
+   * @return {Object}
+   *        Object containing pssh data by key system
+   */
+  var generateKeySystemInformation = function generateKeySystemInformation(contentProtectionNodes) {
+    return contentProtectionNodes.reduce(function (acc, node) {
+      var attributes = parseAttributes$1(node);
+      var keySystem = keySystemsMap[attributes.schemeIdUri];
+
+      if (keySystem) {
+        acc[keySystem] = {attributes: attributes};
+
+        var psshNode = findChildren(node, 'cenc:pssh')[0];
+
+        if (psshNode) {
+          var pssh = getContent(psshNode);
+          var psshBuffer = pssh && decodeB64ToUint8Array(pssh);
+
+          acc[keySystem].pssh = psshBuffer;
+        }
+      }
+
+      return acc;
+    }, {});
+  };
+
+  /**
+   * Maps an AdaptationSet node to a list of Representation information objects
+   *
+   * @name toRepresentationsCallback
+   * @function
+   * @param {Node} adaptationSet
+   *        AdaptationSet node from the mpd
+   * @return {RepresentationInformation[]}
+   *         List of objects containing Representaion information
+   */
+
+  /**
+   * Returns a callback for Array.prototype.map for mapping AdaptationSet nodes to a list of
+   * Representation information objects
+   *
+   * @param {Object} periodAttributes
+   *        Contains attributes inherited by the Period
+   * @param {string[]} periodBaseUrls
+   *        Contains list of resolved base urls inherited by the Period
+   * @param {string[]} periodSegmentInfo
+   *        Contains Segment Information at the period level
+   * @return {toRepresentationsCallback}
+   *         Callback map function
+   */
+  var toRepresentations = function toRepresentations(periodAttributes, periodBaseUrls, periodSegmentInfo) {
+    return function (adaptationSet) {
+      var adaptationSetAttributes = parseAttributes$1(adaptationSet);
+      var adaptationSetBaseUrls = buildBaseUrls(periodBaseUrls, findChildren(adaptationSet, 'BaseURL'));
+      var role = findChildren(adaptationSet, 'Role')[0];
+      var roleAttributes = {role: parseAttributes$1(role)};
+
+      var attrs = merge(periodAttributes, adaptationSetAttributes, roleAttributes);
+
+      var contentProtection = generateKeySystemInformation(findChildren(adaptationSet, 'ContentProtection'));
+
+      if (Object.keys(contentProtection).length) {
+        attrs = merge(attrs, {contentProtection: contentProtection});
+      }
+
+      var segmentInfo = getSegmentInformation(adaptationSet);
+      var representations = findChildren(adaptationSet, 'Representation');
+      var adaptationSetSegmentInfo = merge(periodSegmentInfo, segmentInfo);
+
+      return flatten(representations.map(inheritBaseUrls(attrs, adaptationSetBaseUrls, adaptationSetSegmentInfo)));
+    };
+  };
+
+  /**
+   * Maps an Period node to a list of Representation inforamtion objects for all
+   * AdaptationSet nodes contained within the Period
+   *
+   * @name toAdaptationSetsCallback
+   * @function
+   * @param {Node} period
+   *        Period node from the mpd
+   * @param {number} periodIndex
+   *        Index of the Period within the mpd
+   * @return {RepresentationInformation[]}
+   *         List of objects containing Representaion information
+   */
+
+  /**
+   * Returns a callback for Array.prototype.map for mapping Period nodes to a list of
+   * Representation information objects
+   *
+   * @param {Object} mpdAttributes
+   *        Contains attributes inherited by the mpd
+   * @param {string[]} mpdBaseUrls
+   *        Contains list of resolved base urls inherited by the mpd
+   * @return {toAdaptationSetsCallback}
+   *         Callback map function
+   */
+  var toAdaptationSets = function toAdaptationSets(mpdAttributes, mpdBaseUrls) {
+    return function (period, periodIndex) {
+      var periodBaseUrls = buildBaseUrls(mpdBaseUrls, findChildren(period, 'BaseURL'));
+      var periodAtt = parseAttributes$1(period);
+      var periodAttributes = merge(mpdAttributes, periodAtt, {periodIndex: periodIndex});
+      var adaptationSets = findChildren(period, 'AdaptationSet');
+      var periodSegmentInfo = getSegmentInformation(period);
+
+      return flatten(adaptationSets.map(toRepresentations(periodAttributes, periodBaseUrls, periodSegmentInfo)));
+    };
+  };
+
+  /**
+   * Traverses the mpd xml tree to generate a list of Representation information objects
+   * that have inherited attributes from parent nodes
+   *
+   * @param {Node} mpd
+   *        The root node of the mpd
+   * @param {Object} options
+   *        Available options for inheritAttributes
+   * @param {string} options.manifestUri
+   *        The uri source of the mpd
+   * @param {number} options.NOW
+   *        Current time per DASH IOP.  Default is current time in ms since epoch
+   * @param {number} options.clientOffset
+   *        Client time difference from NOW (in milliseconds)
+   * @return {RepresentationInformation[]}
+   *         List of objects containing Representation information
+   */
+  var inheritAttributes = function inheritAttributes(mpd) {
+    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+    var _options$manifestUri = options.manifestUri,
+      manifestUri = _options$manifestUri === undefined ? '' : _options$manifestUri,
+      _options$NOW = options.NOW,
+      NOW = _options$NOW === undefined ? Date.now() : _options$NOW,
+      _options$clientOffset = options.clientOffset,
+      clientOffset = _options$clientOffset === undefined ? 0 : _options$clientOffset;
+
+    var periods = findChildren(mpd, 'Period');
+
+    if (periods.length !== 1) {
+      // TODO add support for multiperiod
+      throw new Error(errors.INVALID_NUMBER_OF_PERIOD);
+    }
+
+    var mpdAttributes = parseAttributes$1(mpd);
+    var mpdBaseUrls = buildBaseUrls([manifestUri], findChildren(mpd, 'BaseURL'));
+
+    mpdAttributes.sourceDuration = mpdAttributes.mediaPresentationDuration || 0;
+    mpdAttributes.NOW = NOW;
+    mpdAttributes.clientOffset = clientOffset;
+
+    return flatten(periods.map(toAdaptationSets(mpdAttributes, mpdBaseUrls)));
+  };
+
+  var stringToMpdXml = function stringToMpdXml(manifestString) {
+    if (manifestString === '') {
+      throw new Error(errors.DASH_EMPTY_MANIFEST);
+    }
+
+    var parser = new window_1.DOMParser();
+    var xml = parser.parseFromString(manifestString, 'application/xml');
+    var mpd = xml && xml.documentElement.tagName === 'MPD' ? xml.documentElement : null;
+
+    if (!mpd || mpd && mpd.getElementsByTagName('parsererror').length > 0) {
+      throw new Error(errors.DASH_INVALID_XML);
+    }
+
+    return mpd;
+  };
+
+  /**
+   * Parses the manifest for a UTCTiming node, returning the nodes attributes if found
+   *
+   * @param {string} mpd
+   *        XML string of the MPD manifest
+   * @return {Object|null}
+   *         Attributes of UTCTiming node specified in the manifest. Null if none found
+   */
+  var parseUTCTimingScheme = function parseUTCTimingScheme(mpd) {
+    var UTCTimingNode = findChildren(mpd, 'UTCTiming')[0];
+
+    if (!UTCTimingNode) {
+      return null;
+    }
+
+    var attributes = parseAttributes$1(UTCTimingNode);
+
+    switch (attributes.schemeIdUri) {
+      case 'urn:mpeg:dash:utc:http-head:2014':
+      case 'urn:mpeg:dash:utc:http-head:2012':
+        attributes.method = 'HEAD';
+        break;
+      case 'urn:mpeg:dash:utc:http-xsdate:2014':
+      case 'urn:mpeg:dash:utc:http-iso:2014':
+      case 'urn:mpeg:dash:utc:http-xsdate:2012':
+      case 'urn:mpeg:dash:utc:http-iso:2012':
+        attributes.method = 'GET';
+        break;
+      case 'urn:mpeg:dash:utc:direct:2014':
+      case 'urn:mpeg:dash:utc:direct:2012':
+        attributes.method = 'DIRECT';
+        attributes.value = Date.parse(attributes.value);
+        break;
+      case 'urn:mpeg:dash:utc:http-ntp:2014':
+      case 'urn:mpeg:dash:utc:ntp:2014':
+      case 'urn:mpeg:dash:utc:sntp:2014':
+      default:
+        throw new Error(errors.UNSUPPORTED_UTC_TIMING_SCHEME);
+    }
+
+    return attributes;
+  };
+
+  var parse = function parse(manifestString, options) {
+    return toM3u8(toPlaylists(inheritAttributes(stringToMpdXml(manifestString), options)));
+  };
+
+  /**
+   * Parses the manifest for a UTCTiming node, returning the nodes attributes if found
+   *
+   * @param {string} manifestString
+   *        XML string of the MPD manifest
+   * @return {Object|null}
+   *         Attributes of UTCTiming node specified in the manifest. Null if none found
+   */
+  var parseUTCTiming = function parseUTCTiming(manifestString) {
+    return parseUTCTimingScheme(stringToMpdXml(manifestString));
+  };
+
+  var toUnsigned = function toUnsigned(value) {
+    return value >>> 0;
+  };
+
+  var bin = {
+    toUnsigned: toUnsigned
+  };
+  var bin_1 = bin.toUnsigned;
+
+  var bin$1 = /*#__PURE__*/Object.freeze({
+    default: bin,
+    __moduleExports: bin,
+    toUnsigned: bin_1
+  });
+
+  var require$$0$1 = (bin$1 && bin) || bin$1;
+
+  var toUnsigned$1 = require$$0$1.toUnsigned;
+  var _findBox, parseType, timescale, startTime;
+
+  // Find the data for a box specified by its path
+  _findBox = function findBox(data, path) {
+    var results = [],
+      i,
+      size,
+      type,
+      end,
+      subresults;
+
+    if (!path.length) {
+      // short-circuit the search for empty paths
+      return null;
+    }
+
+    for (i = 0; i < data.byteLength;) {
+      size = toUnsigned$1(data[i] << 24 | data[i + 1] << 16 | data[i + 2] << 8 | data[i + 3]);
+
+      type = parseType(data.subarray(i + 4, i + 8));
+
+      end = size > 1 ? i + size : data.byteLength;
+
+      if (type === path[0]) {
+        if (path.length === 1) {
+          // this is the end of the path and we've found the box we were
+          // looking for
+          results.push(data.subarray(i + 8, end));
+        } else {
+          // recursively search for the next box along the path
+          subresults = _findBox(data.subarray(i + 8, end), path.slice(1));
+          if (subresults.length) {
+            results = results.concat(subresults);
+          }
+        }
+      }
+      i = end;
+    }
+
+    // we've finished searching all of data
+    return results;
+  };
+
+  /**
+   * Returns the string representation of an ASCII encoded four byte buffer.
+   * @param buffer {Uint8Array} a four-byte buffer to translate
+   * @return {string} the corresponding string
+   */
+  parseType = function parseType(buffer) {
+    var result = '';
+    result += String.fromCharCode(buffer[0]);
+    result += String.fromCharCode(buffer[1]);
+    result += String.fromCharCode(buffer[2]);
+    result += String.fromCharCode(buffer[3]);
+    return result;
+  };
+
+  /**
+   * Parses an MP4 initialization segment and extracts the timescale
+   * values for any declared tracks. Timescale values indicate the
+   * number of clock ticks per second to assume for time-based values
+   * elsewhere in the MP4.
+   *
+   * To determine the start time of an MP4, you need two pieces of
+   * information: the timescale unit and the earliest base media decode
+   * time. Multiple timescales can be specified within an MP4 but the
+   * base media decode time is always expressed in the timescale from
+   * the media header box for the track:
+   * ```
+   * moov > trak > mdia > mdhd.timescale
+   * ```
+   * @param init {Uint8Array} the bytes of the init segment
+   * @return {object} a hash of track ids to timescale values or null if
+   * the init segment is malformed.
+   */
+  timescale = function timescale(init) {
+    var result = {},
+      traks = _findBox(init, ['moov', 'trak']);
+
+    // mdhd timescale
+    return traks.reduce(function (result, trak) {
+      var tkhd, version, index, id, mdhd;
+
+      tkhd = _findBox(trak, ['tkhd'])[0];
+      if (!tkhd) {
+        return null;
+      }
+      version = tkhd[0];
+      index = version === 0 ? 12 : 20;
+      id = toUnsigned$1(tkhd[index] << 24 | tkhd[index + 1] << 16 | tkhd[index + 2] << 8 | tkhd[index + 3]);
+
+      mdhd = _findBox(trak, ['mdia', 'mdhd'])[0];
+      if (!mdhd) {
+        return null;
+      }
+      version = mdhd[0];
+      index = version === 0 ? 12 : 20;
+      result[id] = toUnsigned$1(mdhd[index] << 24 | mdhd[index + 1] << 16 | mdhd[index + 2] << 8 | mdhd[index + 3]);
+      return result;
+    }, result);
+  };
+
+  /**
+   * Determine the base media decode start time, in seconds, for an MP4
+   * fragment. If multiple fragments are specified, the earliest time is
+   * returned.
+   *
+   * The base media decode time can be parsed from track fragment
+   * metadata:
+   * ```
+   * moof > traf > tfdt.baseMediaDecodeTime
+   * ```
+   * It requires the timescale value from the mdhd to interpret.
+   *
+   * @param timescale {object} a hash of track ids to timescale values.
+   * @return {number} the earliest base media decode start time for the
+   * fragment, in seconds
+   */
+  startTime = function startTime(timescale, fragment) {
+    var trafs, baseTimes, result;
+
+    // we need info from two childrend of each track fragment box
+    trafs = _findBox(fragment, ['moof', 'traf']);
+
+    // determine the start times for each track
+    baseTimes = [].concat.apply([], trafs.map(function (traf) {
+      return _findBox(traf, ['tfhd']).map(function (tfhd) {
+        var id, scale, baseTime;
+
+        // get the track id from the tfhd
+        id = toUnsigned$1(tfhd[4] << 24 | tfhd[5] << 16 | tfhd[6] << 8 | tfhd[7]);
+        // assume a 90kHz clock if no timescale was specified
+        scale = timescale[id] || 90e3;
+
+        // get the base media decode time from the tfdt
+        baseTime = _findBox(traf, ['tfdt']).map(function (tfdt) {
+          var version, result;
+
+          version = tfdt[0];
+          result = toUnsigned$1(tfdt[4] << 24 | tfdt[5] << 16 | tfdt[6] << 8 | tfdt[7]);
+          if (version === 1) {
+            result *= Math.pow(2, 32);
+            result += toUnsigned$1(tfdt[8] << 24 | tfdt[9] << 16 | tfdt[10] << 8 | tfdt[11]);
+          }
+          return result;
+        })[0];
+        baseTime = baseTime || Infinity;
+
+        // convert base time to seconds
+        return baseTime / scale;
+      });
+    }));
+
+    // return the minimum
+    result = Math.min.apply(null, baseTimes);
+    return isFinite(result) ? result : 0;
+  };
+
+  var probe = {
+    parseType: parseType,
+    timescale: timescale,
+    startTime: startTime
+  };
+
+  var streamTypes = {
+    H264_STREAM_TYPE: 0x1B,
+    ADTS_STREAM_TYPE: 0x0F,
+    METADATA_STREAM_TYPE: 0x15
+  };
+  var streamTypes_1 = streamTypes.H264_STREAM_TYPE;
+  var streamTypes_2 = streamTypes.ADTS_STREAM_TYPE;
+  var streamTypes_3 = streamTypes.METADATA_STREAM_TYPE;
+
+  var streamTypes$1 = /*#__PURE__*/Object.freeze({
+    default: streamTypes,
+    __moduleExports: streamTypes,
+    H264_STREAM_TYPE: streamTypes_1,
+    ADTS_STREAM_TYPE: streamTypes_2,
+    METADATA_STREAM_TYPE: streamTypes_3
+  });
+
+  /**
+   * mux.js
+   *
+   * Copyright (c) 2014 Brightcove
+   * All rights reserved.
+   *
+   * A lightweight readable stream implemention that handles event dispatching.
+   * Objects that inherit from streams should call init in their constructors.
+   */
+
+  var Stream$1 = function Stream() {
+    this.init = function () {
+      var listeners = {};
+      /**
+       * Add a listener for a specified event type.
+       * @param type {string} the event name
+       * @param listener {function} the callback to be invoked when an event of
+       * the specified type occurs
+       */
+      this.on = function (type, listener) {
+        if (!listeners[type]) {
+          listeners[type] = [];
+        }
+        listeners[type] = listeners[type].concat(listener);
+      };
+      /**
+       * Remove a listener for a specified event type.
+       * @param type {string} the event name
+       * @param listener {function} a function previously registered for this
+       * type of event through `on`
+       */
+      this.off = function (type, listener) {
+        var index;
+        if (!listeners[type]) {
+          return false;
+        }
+        index = listeners[type].indexOf(listener);
+        listeners[type] = listeners[type].slice();
+        listeners[type].splice(index, 1);
+        return index > -1;
+      };
+      /**
+       * Trigger an event of the specified type on this stream. Any additional
+       * arguments to this function are passed as parameters to event listeners.
+       * @param type {string} the event name
+       */
+      this.trigger = function (type) {
+        var callbacks, i, length, args;
+        callbacks = listeners[type];
+        if (!callbacks) {
+          return;
+        }
+        // Slicing the arguments on every invocation of this method
+        // can add a significant amount of overhead. Avoid the
+        // intermediate object creation for the common case of a
+        // single callback argument
+        if (arguments.length === 2) {
+          length = callbacks.length;
+          for (i = 0; i < length; ++i) {
+            callbacks[i].call(this, arguments[1]);
+          }
+        } else {
+          args = [];
+          i = arguments.length;
+          for (i = 1; i < arguments.length; ++i) {
+            args.push(arguments[i]);
+          }
+          length = callbacks.length;
+          for (i = 0; i < length; ++i) {
+            callbacks[i].apply(this, args);
+          }
+        }
+      };
+      /**
+       * Destroys the stream and cleans up.
+       */
+      this.dispose = function () {
+        listeners = {};
+      };
+    };
+  };
+
+  /**
+   * Forwards all `data` events on this stream to the destination stream. The
+   * destination stream should provide a method `push` to receive the data
+   * events as they arrive.
+   * @param destination {stream} the stream that will receive all `data` events
+   * @param autoFlush {boolean} if false, we will not call `flush` on the destination
+   *                            when the current stream emits a 'done' event
+   * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
+   */
+  Stream$1.prototype.pipe = function (destination) {
+    this.on('data', function (data) {
+      destination.push(data);
+    });
+
+    this.on('done', function (flushSource) {
+      destination.flush(flushSource);
+    });
+
+    return destination;
+  };
+
+  // Default stream functions that are expected to be overridden to perform
+  // actual work. These are provided by the prototype as a sort of no-op
+  // implementation so that we don't have to check for their existence in the
+  // `pipe` function above.
+  Stream$1.prototype.push = function (data) {
+    this.trigger('data', data);
+  };
+
+  Stream$1.prototype.flush = function (flushSource) {
+    this.trigger('done', flushSource);
+  };
+
+  var stream = Stream$1;
+
+  var stream$1 = /*#__PURE__*/Object.freeze({
+    default: stream,
+    __moduleExports: stream
+  });
+
+  var Stream$2 = (stream$1 && stream) || stream$1;
+
+  var MAX_TS = 8589934592;
+
+  var RO_THRESH = 4294967296;
+
+  var handleRollover = function handleRollover(value, reference) {
+    var direction = 1;
+
+    if (value > reference) {
+      // If the current timestamp value is greater than our reference timestamp and we detect a
+      // timestamp rollover, this means the roll over is happening in the opposite direction.
+      // Example scenario: Enter a long stream/video just after a rollover occurred. The reference
+      // point will be set to a small number, e.g. 1. The user then seeks backwards over the
+      // rollover point. In loading this segment, the timestamp values will be very large,
+      // e.g. 2^33 - 1. Since this comes before the data we loaded previously, we want to adjust
+      // the time stamp to be `value - 2^33`.
+      direction = -1;
+    }
+
+    // Note: A seek forwards or back that is greater than the RO_THRESH (2^32, ~13 hours) will
+    // cause an incorrect adjustment.
+    while (Math.abs(reference - value) > RO_THRESH) {
+      value += direction * MAX_TS;
+    }
+
+    return value;
+  };
+
+  var TimestampRolloverStream = function TimestampRolloverStream(type) {
+    var lastDTS, referenceDTS;
+
+    TimestampRolloverStream.prototype.init.call(this);
+
+    this.type_ = type;
+
+    this.push = function (data) {
+      if (data.type !== this.type_) {
+        return;
+      }
+
+      if (referenceDTS === undefined) {
+        referenceDTS = data.dts;
+      }
+
+      data.dts = handleRollover(data.dts, referenceDTS);
+      data.pts = handleRollover(data.pts, referenceDTS);
+
+      lastDTS = data.dts;
+
+      this.trigger('data', data);
+    };
+
+    this.flush = function () {
+      referenceDTS = lastDTS;
+      this.trigger('done');
+    };
+
+    this.discontinuity = function () {
+      referenceDTS = void 0;
+      lastDTS = void 0;
+    };
+  };
+
+  TimestampRolloverStream.prototype = new Stream$2();
+
+  var timestampRolloverStream = {
+    TimestampRolloverStream: TimestampRolloverStream,
+    handleRollover: handleRollover
+  };
+  var timestampRolloverStream_1 = timestampRolloverStream.TimestampRolloverStream;
+  var timestampRolloverStream_2 = timestampRolloverStream.handleRollover;
+
+  var timestampRolloverStream$1 = /*#__PURE__*/Object.freeze({
+    default: timestampRolloverStream,
+    __moduleExports: timestampRolloverStream,
+    TimestampRolloverStream: timestampRolloverStream_1,
+    handleRollover: timestampRolloverStream_2
+  });
+
+  var StreamTypes = (streamTypes$1 && streamTypes) || streamTypes$1;
+
+  var parsePid = function parsePid(packet) {
+    var pid = packet[1] & 0x1f;
+    pid <<= 8;
+    pid |= packet[2];
+    return pid;
+  };
+
+  var parsePayloadUnitStartIndicator = function parsePayloadUnitStartIndicator(packet) {
+    return !!(packet[1] & 0x40);
+  };
+
+  var parseAdaptionField = function parseAdaptionField(packet) {
+    var offset = 0;
+    // if an adaption field is present, its length is specified by the
+    // fifth byte of the TS packet header. The adaptation field is
+    // used to add stuffing to PES packets that don't fill a complete
+    // TS packet, and to specify some forms of timing and control data
+    // that we do not currently use.
+    if ((packet[3] & 0x30) >>> 4 > 0x01) {
+      offset += packet[4] + 1;
+    }
+    return offset;
+  };
+
+  var parseType$1 = function parseType(packet, pmtPid) {
+    var pid = parsePid(packet);
+    if (pid === 0) {
+      return 'pat';
+    } else if (pid === pmtPid) {
+      return 'pmt';
+    } else if (pmtPid) {
+      return 'pes';
+    }
+    return null;
+  };
+
+  var parsePat = function parsePat(packet) {
+    var pusi = parsePayloadUnitStartIndicator(packet);
+    var offset = 4 + parseAdaptionField(packet);
+
+    if (pusi) {
+      offset += packet[offset] + 1;
+    }
+
+    return (packet[offset + 10] & 0x1f) << 8 | packet[offset + 11];
+  };
+
+  var parsePmt = function parsePmt(packet) {
+    var programMapTable = {};
+    var pusi = parsePayloadUnitStartIndicator(packet);
+    var payloadOffset = 4 + parseAdaptionField(packet);
+
+    if (pusi) {
+      payloadOffset += packet[payloadOffset] + 1;
+    }
+
+    // PMTs can be sent ahead of the time when they should actually
+    // take effect. We don't believe this should ever be the case
+    // for HLS but we'll ignore "forward" PMT declarations if we see
+    // them. Future PMT declarations have the current_next_indicator
+    // set to zero.
+    if (!(packet[payloadOffset + 5] & 0x01)) {
+      return;
+    }
+
+    var sectionLength, tableEnd, programInfoLength;
+    // the mapping table ends at the end of the current section
+    sectionLength = (packet[payloadOffset + 1] & 0x0f) << 8 | packet[payloadOffset + 2];
+    tableEnd = 3 + sectionLength - 4;
+
+    // to determine where the table is, we have to figure out how
+    // long the program info descriptors are
+    programInfoLength = (packet[payloadOffset + 10] & 0x0f) << 8 | packet[payloadOffset + 11];
+
+    // advance the offset to the first entry in the mapping table
+    var offset = 12 + programInfoLength;
+    while (offset < tableEnd) {
+      var i = payloadOffset + offset;
+      // add an entry that maps the elementary_pid to the stream_type
+      programMapTable[(packet[i + 1] & 0x1F) << 8 | packet[i + 2]] = packet[i];
+
+      // move to the next table entry
+      // skip past the elementary stream descriptors, if present
+      offset += ((packet[i + 3] & 0x0F) << 8 | packet[i + 4]) + 5;
+    }
+    return programMapTable;
+  };
+
+  var parsePesType = function parsePesType(packet, programMapTable) {
+    var pid = parsePid(packet);
+    var type = programMapTable[pid];
+    switch (type) {
+      case StreamTypes.H264_STREAM_TYPE:
+        return 'video';
+      case StreamTypes.ADTS_STREAM_TYPE:
+        return 'audio';
+      case StreamTypes.METADATA_STREAM_TYPE:
+        return 'timed-metadata';
+      default:
+        return null;
+    }
+  };
+
+  var parsePesTime = function parsePesTime(packet) {
+    var pusi = parsePayloadUnitStartIndicator(packet);
+    if (!pusi) {
+      return null;
+    }
+
+    var offset = 4 + parseAdaptionField(packet);
+
+    if (offset >= packet.byteLength) {
+      // From the H 222.0 MPEG-TS spec
+      // "For transport stream packets carrying PES packets, stuffing is needed when there
+      //  is insufficient PES packet data to completely fill the transport stream packet
+      //  payload bytes. Stuffing is accomplished by defining an adaptation field longer than
+      //  the sum of the lengths of the data elements in it, so that the payload bytes
+      //  remaining after the adaptation field exactly accommodates the available PES packet
+      //  data."
+      //
+      // If the offset is >= the length of the packet, then the packet contains no data
+      // and instead is just adaption field stuffing bytes
+      return null;
+    }
+
+    var pes = null;
+    var ptsDtsFlags;
+
+    // PES packets may be annotated with a PTS value, or a PTS value
+    // and a DTS value. Determine what combination of values is
+    // available to work with.
+    ptsDtsFlags = packet[offset + 7];
+
+    // PTS and DTS are normally stored as a 33-bit number.  Javascript
+    // performs all bitwise operations on 32-bit integers but javascript
+    // supports a much greater range (52-bits) of integer using standard
+    // mathematical operations.
+    // We construct a 31-bit value using bitwise operators over the 31
+    // most significant bits and then multiply by 4 (equal to a left-shift
+    // of 2) before we add the final 2 least significant bits of the
+    // timestamp (equal to an OR.)
+    if (ptsDtsFlags & 0xC0) {
+      pes = {};
+      // the PTS and DTS are not written out directly. For information
+      // on how they are encoded, see
+      // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
+      pes.pts = (packet[offset + 9] & 0x0E) << 27 | (packet[offset + 10] & 0xFF) << 20 | (packet[offset + 11] & 0xFE) << 12 | (packet[offset + 12] & 0xFF) << 5 | (packet[offset + 13] & 0xFE) >>> 3;
+      pes.pts *= 4; // Left shift by 2
+      pes.pts += (packet[offset + 13] & 0x06) >>> 1; // OR by the two LSBs
+      pes.dts = pes.pts;
+      if (ptsDtsFlags & 0x40) {
+        pes.dts = (packet[offset + 14] & 0x0E) << 27 | (packet[offset + 15] & 0xFF) << 20 | (packet[offset + 16] & 0xFE) << 12 | (packet[offset + 17] & 0xFF) << 5 | (packet[offset + 18] & 0xFE) >>> 3;
+        pes.dts *= 4; // Left shift by 2
+        pes.dts += (packet[offset + 18] & 0x06) >>> 1; // OR by the two LSBs
+      }
+    }
+    return pes;
+  };
+
+  var parseNalUnitType = function parseNalUnitType(type) {
+    switch (type) {
+      case 0x05:
+        return 'slice_layer_without_partitioning_rbsp_idr';
+      case 0x06:
+        return 'sei_rbsp';
+      case 0x07:
+        return 'seq_parameter_set_rbsp';
+      case 0x08:
+        return 'pic_parameter_set_rbsp';
+      case 0x09:
+        return 'access_unit_delimiter_rbsp';
+      default:
+        return null;
+    }
+  };
+
+  var videoPacketContainsKeyFrame = function videoPacketContainsKeyFrame(packet) {
+    var offset = 4 + parseAdaptionField(packet);
+    var frameBuffer = packet.subarray(offset);
+    var frameI = 0;
+    var frameSyncPoint = 0;
+    var foundKeyFrame = false;
+    var nalType;
+
+    // advance the sync point to a NAL start, if necessary
+    for (; frameSyncPoint < frameBuffer.byteLength - 3; frameSyncPoint++) {
+      if (frameBuffer[frameSyncPoint + 2] === 1) {
+        // the sync point is properly aligned
+        frameI = frameSyncPoint + 5;
+        break;
+      }
+    }
+
+    while (frameI < frameBuffer.byteLength) {
+      // look at the current byte to determine if we've hit the end of
+      // a NAL unit boundary
+      switch (frameBuffer[frameI]) {
+        case 0:
+          // skip past non-sync sequences
+          if (frameBuffer[frameI - 1] !== 0) {
+            frameI += 2;
+            break;
+          } else if (frameBuffer[frameI - 2] !== 0) {
+            frameI++;
+            break;
+          }
+
+          if (frameSyncPoint + 3 !== frameI - 2) {
+            nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
+            if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
+              foundKeyFrame = true;
+            }
+          }
+
+          // drop trailing zeroes
+          do {
+            frameI++;
+          } while (frameBuffer[frameI] !== 1 && frameI < frameBuffer.length);
+          frameSyncPoint = frameI - 2;
+          frameI += 3;
+          break;
+        case 1:
+          // skip past non-sync sequences
+          if (frameBuffer[frameI - 1] !== 0 || frameBuffer[frameI - 2] !== 0) {
+            frameI += 3;
+            break;
+          }
+
+          nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
+          if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
+            foundKeyFrame = true;
+          }
+          frameSyncPoint = frameI - 2;
+          frameI += 3;
+          break;
+        default:
+          // the current byte isn't a one or zero, so it cannot be part
+          // of a sync sequence
+          frameI += 3;
+          break;
+      }
+    }
+    frameBuffer = frameBuffer.subarray(frameSyncPoint);
+    frameI -= frameSyncPoint;
+    frameSyncPoint = 0;
+    // parse the final nal
+    if (frameBuffer && frameBuffer.byteLength > 3) {
+      nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
+      if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
+        foundKeyFrame = true;
+      }
+    }
+
+    return foundKeyFrame;
+  };
+
+  var probe$1 = {
+    parseType: parseType$1,
+    parsePat: parsePat,
+    parsePmt: parsePmt,
+    parsePayloadUnitStartIndicator: parsePayloadUnitStartIndicator,
+    parsePesType: parsePesType,
+    parsePesTime: parsePesTime,
+    videoPacketContainsKeyFrame: videoPacketContainsKeyFrame
+  };
+  var probe_1$1 = probe$1.parseType;
+  var probe_2$1 = probe$1.parsePat;
+  var probe_3$1 = probe$1.parsePmt;
+  var probe_4 = probe$1.parsePayloadUnitStartIndicator;
+  var probe_5 = probe$1.parsePesType;
+  var probe_6 = probe$1.parsePesTime;
+  var probe_7 = probe$1.videoPacketContainsKeyFrame;
+
+  var probe$2 = /*#__PURE__*/Object.freeze({
+    default: probe$1,
+    __moduleExports: probe$1,
+    parseType: probe_1$1,
+    parsePat: probe_2$1,
+    parsePmt: probe_3$1,
+    parsePayloadUnitStartIndicator: probe_4,
+    parsePesType: probe_5,
+    parsePesTime: probe_6,
+    videoPacketContainsKeyFrame: probe_7
+  });
+
+  /**
+   * mux.js
+   *
+   * Copyright (c) 2016 Brightcove
+   * All rights reserved.
+   *
+   * Utilities to detect basic properties and metadata about Aac data.
+   */
+
+  var ADTS_SAMPLING_FREQUENCIES = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
+
+  var parseSyncSafeInteger = function parseSyncSafeInteger(data) {
+    return data[0] << 21 | data[1] << 14 | data[2] << 7 | data[3];
+  };
+
+  // return a percent-encoded representation of the specified byte range
+  // @see http://en.wikipedia.org/wiki/Percent-encoding
+  var percentEncode = function percentEncode(bytes, start, end) {
+    var i,
+      result = '';
+    for (i = start; i < end; i++) {
+      result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
+    }
+    return result;
+  };
+
+  // return the string representation of the specified byte range,
+  // interpreted as ISO-8859-1.
+  var parseIso88591 = function parseIso88591(bytes, start, end) {
+    return unescape(percentEncode(bytes, start, end)); // jshint ignore:line
+  };
+
+  var parseId3TagSize = function parseId3TagSize(header, byteIndex) {
+    var returnSize = header[byteIndex + 6] << 21 | header[byteIndex + 7] << 14 | header[byteIndex + 8] << 7 | header[byteIndex + 9],
+      flags = header[byteIndex + 5],
+      footerPresent = (flags & 16) >> 4;
+
+    if (footerPresent) {
+      return returnSize + 20;
+    }
+    return returnSize + 10;
+  };
+
+  var parseAdtsSize = function parseAdtsSize(header, byteIndex) {
+    var lowThree = (header[byteIndex + 5] & 0xE0) >> 5,
+      middle = header[byteIndex + 4] << 3,
+      highTwo = header[byteIndex + 3] & 0x3 << 11;
+
+    return highTwo | middle | lowThree;
+  };
+
+  var parseType$2 = function parseType(header, byteIndex) {
+    if (header[byteIndex] === 'I'.charCodeAt(0) && header[byteIndex + 1] === 'D'.charCodeAt(0) && header[byteIndex + 2] === '3'.charCodeAt(0)) {
+      return 'timed-metadata';
+    } else if (header[byteIndex] & 0xff === 0xff && (header[byteIndex + 1] & 0xf0) === 0xf0) {
+      return 'audio';
+    }
+    return null;
+  };
+
+  var parseSampleRate = function parseSampleRate(packet) {
+    var i = 0;
+
+    while (i + 5 < packet.length) {
+      if (packet[i] !== 0xFF || (packet[i + 1] & 0xF6) !== 0xF0) {
+        // If a valid header was not found,  jump one forward and attempt to
+        // find a valid ADTS header starting at the next byte
+        i++;
+        continue;
+      }
+      return ADTS_SAMPLING_FREQUENCIES[(packet[i + 2] & 0x3c) >>> 2];
+    }
+
+    return null;
+  };
+
+  var parseAacTimestamp = function parseAacTimestamp(packet) {
+    var frameStart, frameSize, frame, frameHeader;
+
+    // find the start of the first frame and the end of the tag
+    frameStart = 10;
+    if (packet[5] & 0x40) {
+      // advance the frame start past the extended header
+      frameStart += 4; // header size field
+      frameStart += parseSyncSafeInteger(packet.subarray(10, 14));
+    }
+
+    // parse one or more ID3 frames
+    // http://id3.org/id3v2.3.0#ID3v2_frame_overview
+    do {
+      // determine the number of bytes in this frame
+      frameSize = parseSyncSafeInteger(packet.subarray(frameStart + 4, frameStart + 8));
+      if (frameSize < 1) {
+        return null;
+      }
+      frameHeader = String.fromCharCode(packet[frameStart], packet[frameStart + 1], packet[frameStart + 2], packet[frameStart + 3]);
+
+      if (frameHeader === 'PRIV') {
+        frame = packet.subarray(frameStart + 10, frameStart + frameSize + 10);
+
+        for (var i = 0; i < frame.byteLength; i++) {
+          if (frame[i] === 0) {
+            var owner = parseIso88591(frame, 0, i);
+            if (owner === 'com.apple.streaming.transportStreamTimestamp') {
+              var d = frame.subarray(i + 1);
+              var size = (d[3] & 0x01) << 30 | d[4] << 22 | d[5] << 14 | d[6] << 6 | d[7] >>> 2;
+              size *= 4;
+              size += d[7] & 0x03;
+
+              return size;
+            }
+            break;
+          }
+        }
+      }
+
+      frameStart += 10; // advance past the frame header
+      frameStart += frameSize; // advance past the frame body
+    } while (frameStart < packet.byteLength);
+    return null;
+  };
+
+  var probe$3 = {
+    parseId3TagSize: parseId3TagSize,
+    parseAdtsSize: parseAdtsSize,
+    parseType: parseType$2,
+    parseSampleRate: parseSampleRate,
+    parseAacTimestamp: parseAacTimestamp
+  };
+  var probe_1$2 = probe$3.parseId3TagSize;
+  var probe_2$2 = probe$3.parseAdtsSize;
+  var probe_3$2 = probe$3.parseType;
+  var probe_4$1 = probe$3.parseSampleRate;
+  var probe_5$1 = probe$3.parseAacTimestamp;
+
+  var probe$4 = /*#__PURE__*/Object.freeze({
+    default: probe$3,
+    __moduleExports: probe$3,
+    parseId3TagSize: probe_1$2,
+    parseAdtsSize: probe_2$2,
+    parseType: probe_3$2,
+    parseSampleRate: probe_4$1,
+    parseAacTimestamp: probe_5$1
+  });
+
+  var require$$0$2 = (timestampRolloverStream$1 && timestampRolloverStream) || timestampRolloverStream$1;
+
+  var require$$1$1 = (probe$2 && probe$1) || probe$2;
+
+  var require$$2$1 = (probe$4 && probe$3) || probe$4;
+
+  var handleRollover$1 = require$$0$2.handleRollover;
+  var probe$5 = {};
+  probe$5.ts = require$$1$1;
+  probe$5.aac = require$$2$1;
+
+  var PES_TIMESCALE = 90000,
+    MP2T_PACKET_LENGTH = 188,
+    // bytes
+    SYNC_BYTE = 0x47;
+
+  var isLikelyAacData = function isLikelyAacData(data) {
+    if (data[0] === 'I'.charCodeAt(0) && data[1] === 'D'.charCodeAt(0) && data[2] === '3'.charCodeAt(0)) {
+      return true;
+    }
+    return false;
+  };
+
+  /**
+   * walks through segment data looking for pat and pmt packets to parse out
+   * program map table information
+   */
+  var parsePsi_ = function parsePsi_(bytes, pmt) {
+    var startIndex = 0,
+      endIndex = MP2T_PACKET_LENGTH,
+      packet,
+      type;
+
+    while (endIndex < bytes.byteLength) {
+      // Look for a pair of start and end sync bytes in the data..
+      if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
+        // We found a packet
+        packet = bytes.subarray(startIndex, endIndex);
+        type = probe$5.ts.parseType(packet, pmt.pid);
+
+        switch (type) {
+          case 'pat':
+            if (!pmt.pid) {
+              pmt.pid = probe$5.ts.parsePat(packet);
+            }
+            break;
+          case 'pmt':
+            if (!pmt.table) {
+              pmt.table = probe$5.ts.parsePmt(packet);
+            }
+            break;
+          default:
+            break;
+        }
+
+        // Found the pat and pmt, we can stop walking the segment
+        if (pmt.pid && pmt.table) {
+          return;
+        }
+
+        startIndex += MP2T_PACKET_LENGTH;
+        endIndex += MP2T_PACKET_LENGTH;
+        continue;
+      }
+
+      // If we get here, we have somehow become de-synchronized and we need to step
+      // forward one byte at a time until we find a pair of sync bytes that denote
+      // a packet
+      startIndex++;
+      endIndex++;
+    }
+  };
+
+  /**
+   * walks through the segment data from the start and end to get timing information
+   * for the first and last audio pes packets
+   */
+  var parseAudioPes_ = function parseAudioPes_(bytes, pmt, result) {
+    var startIndex = 0,
+      endIndex = MP2T_PACKET_LENGTH,
+      packet,
+      type,
+      pesType,
+      pusi,
+      parsed;
+
+    var endLoop = false;
+
+    // Start walking from start of segment to get first audio packet
+    while (endIndex < bytes.byteLength) {
+      // Look for a pair of start and end sync bytes in the data..
+      if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
+        // We found a packet
+        packet = bytes.subarray(startIndex, endIndex);
+        type = probe$5.ts.parseType(packet, pmt.pid);
+
+        switch (type) {
+          case 'pes':
+            pesType = probe$5.ts.parsePesType(packet, pmt.table);
+            pusi = probe$5.ts.parsePayloadUnitStartIndicator(packet);
+            if (pesType === 'audio' && pusi) {
+              parsed = probe$5.ts.parsePesTime(packet);
+              if (parsed) {
+                parsed.type = 'audio';
+                result.audio.push(parsed);
+                endLoop = true;
+              }
+            }
+            break;
+          default:
+            break;
+        }
+
+        if (endLoop) {
+          break;
+        }
+
+        startIndex += MP2T_PACKET_LENGTH;
+        endIndex += MP2T_PACKET_LENGTH;
+        continue;
+      }
+
+      // If we get here, we have somehow become de-synchronized and we need to step
+      // forward one byte at a time until we find a pair of sync bytes that denote
+      // a packet
+      startIndex++;
+      endIndex++;
+    }
+
+    // Start walking from end of segment to get last audio packet
+    endIndex = bytes.byteLength;
+    startIndex = endIndex - MP2T_PACKET_LENGTH;
+    endLoop = false;
+    while (startIndex >= 0) {
+      // Look for a pair of start and end sync bytes in the data..
+      if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
+        // We found a packet
+        packet = bytes.subarray(startIndex, endIndex);
+        type = probe$5.ts.parseType(packet, pmt.pid);
+
+        switch (type) {
+          case 'pes':
+            pesType = probe$5.ts.parsePesType(packet, pmt.table);
+            pusi = probe$5.ts.parsePayloadUnitStartIndicator(packet);
+            if (pesType === 'audio' && pusi) {
+              parsed = probe$5.ts.parsePesTime(packet);
+              if (parsed) {
+                parsed.type = 'audio';
+                result.audio.push(parsed);
+                endLoop = true;
+              }
+            }
+            break;
+          default:
+            break;
+        }
+
+        if (endLoop) {
+          break;
+        }
+
+        startIndex -= MP2T_PACKET_LENGTH;
+        endIndex -= MP2T_PACKET_LENGTH;
+        continue;
+      }
+
+      // If we get here, we have somehow become de-synchronized and we need to step
+      // forward one byte at a time until we find a pair of sync bytes that denote
+      // a packet
+      startIndex--;
+      endIndex--;
+    }
+  };
+
+  /**
+   * walks through the segment data from the start and end to get timing information
+   * for the first and last video pes packets as well as timing information for the first
+   * key frame.
+   */
+  var parseVideoPes_ = function parseVideoPes_(bytes, pmt, result) {
+    var startIndex = 0,
+      endIndex = MP2T_PACKET_LENGTH,
+      packet,
+      type,
+      pesType,
+      pusi,
+      parsed,
+      frame,
+      i,
+      pes;
+
+    var endLoop = false;
+
+    var currentFrame = {
+      data: [],
+      size: 0
+    };
+
+    // Start walking from start of segment to get first video packet
+    while (endIndex < bytes.byteLength) {
+      // Look for a pair of start and end sync bytes in the data..
+      if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
+        // We found a packet
+        packet = bytes.subarray(startIndex, endIndex);
+        type = probe$5.ts.parseType(packet, pmt.pid);
+
+        switch (type) {
+          case 'pes':
+            pesType = probe$5.ts.parsePesType(packet, pmt.table);
+            pusi = probe$5.ts.parsePayloadUnitStartIndicator(packet);
+            if (pesType === 'video') {
+              if (pusi && !endLoop) {
+                parsed = probe$5.ts.parsePesTime(packet);
+                if (parsed) {
+                  parsed.type = 'video';
+                  result.video.push(parsed);
+                  endLoop = true;
+                }
+              }
+              if (!result.firstKeyFrame) {
+                if (pusi) {
+                  if (currentFrame.size !== 0) {
+                    frame = new Uint8Array(currentFrame.size);
+                    i = 0;
+                    while (currentFrame.data.length) {
+                      pes = currentFrame.data.shift();
+                      frame.set(pes, i);
+                      i += pes.byteLength;
+                    }
+                    if (probe$5.ts.videoPacketContainsKeyFrame(frame)) {
+                      result.firstKeyFrame = probe$5.ts.parsePesTime(frame);
+                      result.firstKeyFrame.type = 'video';
+                    }
+                    currentFrame.size = 0;
+                  }
+                }
+                currentFrame.data.push(packet);
+                currentFrame.size += packet.byteLength;
+              }
+            }
+            break;
+          default:
+            break;
+        }
+
+        if (endLoop && result.firstKeyFrame) {
+          break;
+        }
+
+        startIndex += MP2T_PACKET_LENGTH;
+        endIndex += MP2T_PACKET_LENGTH;
+        continue;
+      }
+
+      // If we get here, we have somehow become de-synchronized and we need to step
+      // forward one byte at a time until we find a pair of sync bytes that denote
+      // a packet
+      startIndex++;
+      endIndex++;
+    }
+
+    // Start walking from end of segment to get last video packet
+    endIndex = bytes.byteLength;
+    startIndex = endIndex - MP2T_PACKET_LENGTH;
+    endLoop = false;
+    while (startIndex >= 0) {
+      // Look for a pair of start and end sync bytes in the data..
+      if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
+        // We found a packet
+        packet = bytes.subarray(startIndex, endIndex);
+        type = probe$5.ts.parseType(packet, pmt.pid);
+
+        switch (type) {
+          case 'pes':
+            pesType = probe$5.ts.parsePesType(packet, pmt.table);
+            pusi = probe$5.ts.parsePayloadUnitStartIndicator(packet);
+            if (pesType === 'video' && pusi) {
+              parsed = probe$5.ts.parsePesTime(packet);
+              if (parsed) {
+                parsed.type = 'video';
+                result.video.push(parsed);
+                endLoop = true;
+              }
+            }
+            break;
+          default:
+            break;
+        }
+
+        if (endLoop) {
+          break;
+        }
+
+        startIndex -= MP2T_PACKET_LENGTH;
+        endIndex -= MP2T_PACKET_LENGTH;
+        continue;
+      }
+
+      // If we get here, we have somehow become de-synchronized and we need to step
+      // forward one byte at a time until we find a pair of sync bytes that denote
+      // a packet
+      startIndex--;
+      endIndex--;
+    }
+  };
+
+  /**
+   * Adjusts the timestamp information for the segment to account for
+   * rollover and convert to seconds based on pes packet timescale (90khz clock)
+   */
+  var adjustTimestamp_ = function adjustTimestamp_(segmentInfo, baseTimestamp) {
+    if (segmentInfo.audio && segmentInfo.audio.length) {
+      var audioBaseTimestamp = baseTimestamp;
+      if (typeof audioBaseTimestamp === 'undefined') {
+        audioBaseTimestamp = segmentInfo.audio[0].dts;
+      }
+      segmentInfo.audio.forEach(function (info) {
+        info.dts = handleRollover$1(info.dts, audioBaseTimestamp);
+        info.pts = handleRollover$1(info.pts, audioBaseTimestamp);
+        // time in seconds
+        info.dtsTime = info.dts / PES_TIMESCALE;
+        info.ptsTime = info.pts / PES_TIMESCALE;
+      });
+    }
+
+    if (segmentInfo.video && segmentInfo.video.length) {
+      var videoBaseTimestamp = baseTimestamp;
+      if (typeof videoBaseTimestamp === 'undefined') {
+        videoBaseTimestamp = segmentInfo.video[0].dts;
+      }
+      segmentInfo.video.forEach(function (info) {
+        info.dts = handleRollover$1(info.dts, videoBaseTimestamp);
+        info.pts = handleRollover$1(info.pts, videoBaseTimestamp);
+        // time in seconds
+        info.dtsTime = info.dts / PES_TIMESCALE;
+        info.ptsTime = info.pts / PES_TIMESCALE;
+      });
+      if (segmentInfo.firstKeyFrame) {
+        var frame = segmentInfo.firstKeyFrame;
+        frame.dts = handleRollover$1(frame.dts, videoBaseTimestamp);
+        frame.pts = handleRollover$1(frame.pts, videoBaseTimestamp);
+        // time in seconds
+        frame.dtsTime = frame.dts / PES_TIMESCALE;
+        frame.ptsTime = frame.dts / PES_TIMESCALE;
+      }
+    }
+  };
+
+  /**
+   * inspects the aac data stream for start and end time information
+   */
+  var inspectAac_ = function inspectAac_(bytes) {
+    var endLoop = false,
+      audioCount = 0,
+      sampleRate = null,
+      timestamp = null,
+      frameSize = 0,
+      byteIndex = 0,
+      packet;
+
+    while (bytes.length - byteIndex >= 3) {
+      var type = probe$5.aac.parseType(bytes, byteIndex);
+      switch (type) {
+        case 'timed-metadata':
+          // Exit early because we don't have enough to parse
+          // the ID3 tag header
+          if (bytes.length - byteIndex < 10) {
+            endLoop = true;
+            break;
+          }
+
+          frameSize = probe$5.aac.parseId3TagSize(bytes, byteIndex);
+
+          // Exit early if we don't have enough in the buffer
+          // to emit a full packet
+          if (frameSize > bytes.length) {
+            endLoop = true;
+            break;
+          }
+          if (timestamp === null) {
+            packet = bytes.subarray(byteIndex, byteIndex + frameSize);
+            timestamp = probe$5.aac.parseAacTimestamp(packet);
+          }
+          byteIndex += frameSize;
+          break;
+        case 'audio':
+          // Exit early because we don't have enough to parse
+          // the ADTS frame header
+          if (bytes.length - byteIndex < 7) {
+            endLoop = true;
+            break;
+          }
+
+          frameSize = probe$5.aac.parseAdtsSize(bytes, byteIndex);
+
+          // Exit early if we don't have enough in the buffer
+          // to emit a full packet
+          if (frameSize > bytes.length) {
+            endLoop = true;
+            break;
+          }
+          if (sampleRate === null) {
+            packet = bytes.subarray(byteIndex, byteIndex + frameSize);
+            sampleRate = probe$5.aac.parseSampleRate(packet);
+          }
+          audioCount++;
+          byteIndex += frameSize;
+          break;
+        default:
+          byteIndex++;
+          break;
+      }
+      if (endLoop) {
+        return null;
+      }
+    }
+    if (sampleRate === null || timestamp === null) {
+      return null;
+    }
+
+    var audioTimescale = PES_TIMESCALE / sampleRate;
+
+    var result = {
+      audio: [{
+        type: 'audio',
+        dts: timestamp,
+        pts: timestamp
+      }, {
+        type: 'audio',
+        dts: timestamp + audioCount * 1024 * audioTimescale,
+        pts: timestamp + audioCount * 1024 * audioTimescale
+      }]
+    };
+
+    return result;
+  };
+
+  /**
+   * inspects the transport stream segment data for start and end time information
+   * of the audio and video tracks (when present) as well as the first key frame's
+   * start time.
+   */
+  var inspectTs_ = function inspectTs_(bytes) {
+    var pmt = {
+      pid: null,
+      table: null
+    };
+
+    var result = {};
+
+    parsePsi_(bytes, pmt);
+
+    for (var pid in pmt.table) {
+      if (pmt.table.hasOwnProperty(pid)) {
+        var type = pmt.table[pid];
+        switch (type) {
+          case StreamTypes.H264_STREAM_TYPE:
+            result.video = [];
+            parseVideoPes_(bytes, pmt, result);
+            if (result.video.length === 0) {
+              delete result.video;
+            }
+            break;
+          case StreamTypes.ADTS_STREAM_TYPE:
+            result.audio = [];
+            parseAudioPes_(bytes, pmt, result);
+            if (result.audio.length === 0) {
+              delete result.audio;
+            }
+            break;
+          default:
+            break;
+        }
+      }
+    }
+    return result;
+  };
+
+  /**
+   * Inspects segment byte data and returns an object with start and end timing information
+   *
+   * @param {Uint8Array} bytes The segment byte data
+   * @param {Number} baseTimestamp Relative reference timestamp used when adjusting frame
+   *  timestamps for rollover. This value must be in 90khz clock.
+   * @return {Object} Object containing start and end frame timing info of segment.
+   */
+  var inspect = function inspect(bytes, baseTimestamp) {
+    var isAacData = isLikelyAacData(bytes);
+
+    var result;
+
+    if (isAacData) {
+      result = inspectAac_(bytes);
+    } else {
+      result = inspectTs_(bytes);
+    }
+
+    if (!result || !result.audio && !result.video) {
+      return null;
+    }
+
+    adjustTimestamp_(result, baseTimestamp);
+
+    return result;
+  };
+
+  var tsInspector = {
+    inspect: inspect
+  };
+
+  /*
+   * pkcs7.pad
+   * https://github.com/brightcove/pkcs7
+   *
+   * Copyright (c) 2014 Brightcove
+   * Licensed under the apache2 license.
+   */
+
+  /**
+   * Returns the subarray of a Uint8Array without PKCS#7 padding.
+   * @param padded {Uint8Array} unencrypted bytes that have been padded
+   * @return {Uint8Array} the unpadded bytes
+   * @see http://tools.ietf.org/html/rfc5652
+   */
+  function unpad(padded) {
+    return padded.subarray(0, padded.byteLength - padded[padded.byteLength - 1]);
+  }
+
+  var classCallCheck$2 = function classCallCheck$$1(instance, Constructor) {
+    if (!(instance instanceof Constructor)) {
+      throw new TypeError("Cannot call a class as a function");
+    }
+  };
+
+  var createClass$1 = function () {
+    function defineProperties(target, props) {
+      for (var i = 0; i < props.length; i++) {
+        var descriptor = props[i];
+        descriptor.enumerable = descriptor.enumerable || false;
+        descriptor.configurable = true;
+        if ("value" in descriptor) descriptor.writable = true;
+        Object.defineProperty(target, descriptor.key, descriptor);
+      }
+    }
+
+    return function (Constructor, protoProps, staticProps) {
+      if (protoProps) defineProperties(Constructor.prototype, protoProps);
+      if (staticProps) defineProperties(Constructor, staticProps);
+      return Constructor;
+    };
+  }();
+
+  var inherits$2 = function inherits$$1(subClass, superClass) {
+    if (typeof superClass !== "function" && superClass !== null) {
+      throw new TypeError("Super expression must either be null or a function, not " + (typeof superClass === "undefined" ? "undefined" : _typeof(superClass)));
+    }
+
+    subClass.prototype = Object.create(superClass && superClass.prototype, {
+      constructor: {
+        value: subClass,
+        enumerable: false,
+        writable: true,
+        configurable: true
+      }
+    });
+    if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
+  };
+
+  var possibleConstructorReturn$2 = function possibleConstructorReturn$$1(self, call) {
+    if (!self) {
+      throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
+    }
+
+    return call && ((typeof call === "undefined" ? "undefined" : _typeof(call)) === "object" || typeof call === "function") ? call : self;
+  };
+
+  /**
+   * @file aes.js
+   *
+   * This file contains an adaptation of the AES decryption algorithm
+   * from the Standford Javascript Cryptography Library. That work is
+   * covered by the following copyright and permissions notice:
+   *
+   * Copyright 2009-2010 Emily Stark, Mike Hamburg, Dan Boneh.
+   * All rights reserved.
+   *
+   * Redistribution and use in source and binary forms, with or without
+   * modification, are permitted provided that the following conditions are
+   * met:
+   *
+   * 1. Redistributions of source code must retain the above copyright
+   *    notice, this list of conditions and the following disclaimer.
+   *
+   * 2. Redistributions in binary form must reproduce the above
+   *    copyright notice, this list of conditions and the following
+   *    disclaimer in the documentation and/or other materials provided
+   *    with the distribution.
+   *
+   * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR CONTRIBUTORS BE
+   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+   * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+   * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+   * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+   *
+   * The views and conclusions contained in the software and documentation
+   * are those of the authors and should not be interpreted as representing
+   * official policies, either expressed or implied, of the authors.
+   */
+
+  /**
+   * Expand the S-box tables.
+   *
+   * @private
+   */
+  var precompute = function precompute() {
+    var tables = [[[], [], [], [], []], [[], [], [], [], []]];
+    var encTable = tables[0];
+    var decTable = tables[1];
+    var sbox = encTable[4];
+    var sboxInv = decTable[4];
+    var i = void 0;
+    var x = void 0;
+    var xInv = void 0;
+    var d = [];
+    var th = [];
+    var x2 = void 0;
+    var x4 = void 0;
+    var x8 = void 0;
+    var s = void 0;
+    var tEnc = void 0;
+    var tDec = void 0;
+
+    // Compute double and third tables
+    for (i = 0; i < 256; i++) {
+      th[(d[i] = i << 1 ^ (i >> 7) * 283) ^ i] = i;
+    }
+
+    for (x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) {
+      // Compute sbox
+      s = xInv ^ xInv << 1 ^ xInv << 2 ^ xInv << 3 ^ xInv << 4;
+      s = s >> 8 ^ s & 255 ^ 99;
+      sbox[x] = s;
+      sboxInv[s] = x;
+
+      // Compute MixColumns
+      x8 = d[x4 = d[x2 = d[x]]];
+      tDec = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100;
+      tEnc = d[s] * 0x101 ^ s * 0x1010100;
+
+      for (i = 0; i < 4; i++) {
+        encTable[i][x] = tEnc = tEnc << 24 ^ tEnc >>> 8;
+        decTable[i][s] = tDec = tDec << 24 ^ tDec >>> 8;
+      }
+    }
+
+    // Compactify. Considerable speedup on Firefox.
+    for (i = 0; i < 5; i++) {
+      encTable[i] = encTable[i].slice(0);
+      decTable[i] = decTable[i].slice(0);
+    }
+    return tables;
+  };
+  var aesTables = null;
+
+  /**
+   * Schedule out an AES key for both encryption and decryption. This
+   * is a low-level class. Use a cipher mode to do bulk encryption.
+   *
+   * @class AES
+   * @param key {Array} The key as an array of 4, 6 or 8 words.
+   */
+
+  var AES = function () {
+    function AES(key) {
+      classCallCheck$2(this, AES);
+
+      /**
+       * The expanded S-box and inverse S-box tables. These will be computed
+       * on the client so that we don't have to send them down the wire.
+       *
+       * There are two tables, _tables[0] is for encryption and
+       * _tables[1] is for decryption.
+       *
+       * The first 4 sub-tables are the expanded S-box with MixColumns. The
+       * last (_tables[01][4]) is the S-box itself.
+       *
+       * @private
+       */
+      // if we have yet to precompute the S-box tables
+      // do so now
+      if (!aesTables) {
+        aesTables = precompute();
+      }
+      // then make a copy of that object for use
+      this._tables = [[aesTables[0][0].slice(), aesTables[0][1].slice(), aesTables[0][2].slice(), aesTables[0][3].slice(), aesTables[0][4].slice()], [aesTables[1][0].slice(), aesTables[1][1].slice(), aesTables[1][2].slice(), aesTables[1][3].slice(), aesTables[1][4].slice()]];
+      var i = void 0;
+      var j = void 0;
+      var tmp = void 0;
+      var encKey = void 0;
+      var decKey = void 0;
+      var sbox = this._tables[0][4];
+      var decTable = this._tables[1];
+      var keyLen = key.length;
+      var rcon = 1;
+
+      if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) {
+        throw new Error('Invalid aes key size');
+      }
+
+      encKey = key.slice(0);
+      decKey = [];
+      this._key = [encKey, decKey];
+
+      // schedule encryption keys
+      for (i = keyLen; i < 4 * keyLen + 28; i++) {
+        tmp = encKey[i - 1];
+
+        // apply sbox
+        if (i % keyLen === 0 || keyLen === 8 && i % keyLen === 4) {
+          tmp = sbox[tmp >>> 24] << 24 ^ sbox[tmp >> 16 & 255] << 16 ^ sbox[tmp >> 8 & 255] << 8 ^ sbox[tmp & 255];
+
+          // shift rows and add rcon
+          if (i % keyLen === 0) {
+            tmp = tmp << 8 ^ tmp >>> 24 ^ rcon << 24;
+            rcon = rcon << 1 ^ (rcon >> 7) * 283;
+          }
+        }
+
+        encKey[i] = encKey[i - keyLen] ^ tmp;
+      }
+
+      // schedule decryption keys
+      for (j = 0; i; j++, i--) {
+        tmp = encKey[j & 3 ? i : i - 4];
+        if (i <= 4 || j < 4) {
+          decKey[j] = tmp;
+        } else {
+          decKey[j] = decTable[0][sbox[tmp >>> 24]] ^ decTable[1][sbox[tmp >> 16 & 255]] ^ decTable[2][sbox[tmp >> 8 & 255]] ^ decTable[3][sbox[tmp & 255]];
+        }
+      }
+    }
+
+    /**
+     * Decrypt 16 bytes, specified as four 32-bit words.
+     *
+     * @param {Number} encrypted0 the first word to decrypt
+     * @param {Number} encrypted1 the second word to decrypt
+     * @param {Number} encrypted2 the third word to decrypt
+     * @param {Number} encrypted3 the fourth word to decrypt
+     * @param {Int32Array} out the array to write the decrypted words
+     * into
+     * @param {Number} offset the offset into the output array to start
+     * writing results
+     * @return {Array} The plaintext.
+     */
+
+    AES.prototype.decrypt = function decrypt(encrypted0, encrypted1, encrypted2, encrypted3, out, offset) {
+      var key = this._key[1];
+      // state variables a,b,c,d are loaded with pre-whitened data
+      var a = encrypted0 ^ key[0];
+      var b = encrypted3 ^ key[1];
+      var c = encrypted2 ^ key[2];
+      var d = encrypted1 ^ key[3];
+      var a2 = void 0;
+      var b2 = void 0;
+      var c2 = void 0;
+
+      // key.length === 2 ?
+      var nInnerRounds = key.length / 4 - 2;
+      var i = void 0;
+      var kIndex = 4;
+      var table = this._tables[1];
+
+      // load up the tables
+      var table0 = table[0];
+      var table1 = table[1];
+      var table2 = table[2];
+      var table3 = table[3];
+      var sbox = table[4];
+
+      // Inner rounds. Cribbed from OpenSSL.
+      for (i = 0; i < nInnerRounds; i++) {
+        a2 = table0[a >>> 24] ^ table1[b >> 16 & 255] ^ table2[c >> 8 & 255] ^ table3[d & 255] ^ key[kIndex];
+        b2 = table0[b >>> 24] ^ table1[c >> 16 & 255] ^ table2[d >> 8 & 255] ^ table3[a & 255] ^ key[kIndex + 1];
+        c2 = table0[c >>> 24] ^ table1[d >> 16 & 255] ^ table2[a >> 8 & 255] ^ table3[b & 255] ^ key[kIndex + 2];
+        d = table0[d >>> 24] ^ table1[a >> 16 & 255] ^ table2[b >> 8 & 255] ^ table3[c & 255] ^ key[kIndex + 3];
+        kIndex += 4;
+        a = a2;
+        b = b2;
+        c = c2;
+      }
+
+      // Last round.
+      for (i = 0; i < 4; i++) {
+        out[(3 & -i) + offset] = sbox[a >>> 24] << 24 ^ sbox[b >> 16 & 255] << 16 ^ sbox[c >> 8 & 255] << 8 ^ sbox[d & 255] ^ key[kIndex++];
+        a2 = a;
+        a = b;
+        b = c;
+        c = d;
+        d = a2;
+      }
+    };
+
+    return AES;
+  }();
+
+  /**
+   * @file stream.js
+   */
+  /**
+   * A lightweight readable stream implemention that handles event dispatching.
+   *
+   * @class Stream
+   */
+  var Stream$3 = function () {
+    function Stream() {
+      classCallCheck$2(this, Stream);
+
+      this.listeners = {};
+    }
+
+    /**
+     * Add a listener for a specified event type.
+     *
+     * @param {String} type the event name
+     * @param {Function} listener the callback to be invoked when an event of
+     * the specified type occurs
+     */
+
+    Stream.prototype.on = function on(type, listener) {
+      if (!this.listeners[type]) {
+        this.listeners[type] = [];
+      }
+      this.listeners[type].push(listener);
+    };
+
+    /**
+     * Remove a listener for a specified event type.
+     *
+     * @param {String} type the event name
+     * @param {Function} listener  a function previously registered for this
+     * type of event through `on`
+     * @return {Boolean} if we could turn it off or not
+     */
+
+    Stream.prototype.off = function off(type, listener) {
+      if (!this.listeners[type]) {
+        return false;
+      }
+
+      var index = this.listeners[type].indexOf(listener);
+
+      this.listeners[type].splice(index, 1);
+      return index > -1;
+    };
+
+    /**
+     * Trigger an event of the specified type on this stream. Any additional
+     * arguments to this function are passed as parameters to event listeners.
+     *
+     * @param {String} type the event name
+     */
+
+    Stream.prototype.trigger = function trigger(type) {
+      var callbacks = this.listeners[type];
+
+      if (!callbacks) {
+        return;
+      }
+
+      // Slicing the arguments on every invocation of this method
+      // can add a significant amount of overhead. Avoid the
+      // intermediate object creation for the common case of a
+      // single callback argument
+      if (arguments.length === 2) {
+        var length = callbacks.length;
+
+        for (var i = 0; i < length; ++i) {
+          callbacks[i].call(this, arguments[1]);
+        }
+      } else {
+        var args = Array.prototype.slice.call(arguments, 1);
+        var _length = callbacks.length;
+
+        for (var _i = 0; _i < _length; ++_i) {
+          callbacks[_i].apply(this, args);
+        }
+      }
+    };
+
+    /**
+     * Destroys the stream and cleans up.
+     */
+
+    Stream.prototype.dispose = function dispose() {
+      this.listeners = {};
+    };
+    /**
+     * Forwards all `data` events on this stream to the destination stream. The
+     * destination stream should provide a method `push` to receive the data
+     * events as they arrive.
+     *
+     * @param {Stream} destination the stream that will receive all `data` events
+     * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
+     */
+
+    Stream.prototype.pipe = function pipe(destination) {
+      this.on('data', function (data) {
+        destination.push(data);
+      });
+    };
+
+    return Stream;
+  }();
+
+  /**
+   * @file async-stream.js
+   */
+  /**
+   * A wrapper around the Stream class to use setTiemout
+   * and run stream "jobs" Asynchronously
+   *
+   * @class AsyncStream
+   * @extends Stream
+   */
+
+  var AsyncStream = function (_Stream) {
+    inherits$2(AsyncStream, _Stream);
+
+    function AsyncStream() {
+      classCallCheck$2(this, AsyncStream);
+
+      var _this = possibleConstructorReturn$2(this, _Stream.call(this, Stream$3));
+
+      _this.jobs = [];
+      _this.delay = 1;
+      _this.timeout_ = null;
+      return _this;
+    }
+
+    /**
+     * process an async job
+     *
+     * @private
+     */
+
+    AsyncStream.prototype.processJob_ = function processJob_() {
+      this.jobs.shift()();
+      if (this.jobs.length) {
+        this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay);
+      } else {
+        this.timeout_ = null;
+      }
+    };
+
+    /**
+     * push a job into the stream
+     *
+     * @param {Function} job the job to push into the stream
+     */
+
+    AsyncStream.prototype.push = function push(job) {
+      this.jobs.push(job);
+      if (!this.timeout_) {
+        this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay);
+      }
+    };
+
+    return AsyncStream;
+  }(Stream$3);
+
+  /**
+   * @file decrypter.js
+   *
+   * An asynchronous implementation of AES-128 CBC decryption with
+   * PKCS#7 padding.
+   */
+
+  /**
+   * Convert network-order (big-endian) bytes into their little-endian
+   * representation.
+   */
+  var ntoh = function ntoh(word) {
+    return word << 24 | (word & 0xff00) << 8 | (word & 0xff0000) >> 8 | word >>> 24;
+  };
+
+  /**
+   * Decrypt bytes using AES-128 with CBC and PKCS#7 padding.
+   *
+   * @param {Uint8Array} encrypted the encrypted bytes
+   * @param {Uint32Array} key the bytes of the decryption key
+   * @param {Uint32Array} initVector the initialization vector (IV) to
+   * use for the first round of CBC.
+   * @return {Uint8Array} the decrypted bytes
+   *
+   * @see http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
+   * @see http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_.28CBC.29
+   * @see https://tools.ietf.org/html/rfc2315
+   */
+  var decrypt = function decrypt(encrypted, key, initVector) {
+    // word-level access to the encrypted bytes
+    var encrypted32 = new Int32Array(encrypted.buffer, encrypted.byteOffset, encrypted.byteLength >> 2);
+
+    var decipher = new AES(Array.prototype.slice.call(key));
+
+    // byte and word-level access for the decrypted output
+    var decrypted = new Uint8Array(encrypted.byteLength);
+    var decrypted32 = new Int32Array(decrypted.buffer);
+
+    // temporary variables for working with the IV, encrypted, and
+    // decrypted data
+    var init0 = void 0;
+    var init1 = void 0;
+    var init2 = void 0;
+    var init3 = void 0;
+    var encrypted0 = void 0;
+    var encrypted1 = void 0;
+    var encrypted2 = void 0;
+    var encrypted3 = void 0;
+
+    // iteration variable
+    var wordIx = void 0;
+
+    // pull out the words of the IV to ensure we don't modify the
+    // passed-in reference and easier access
+    init0 = initVector[0];
+    init1 = initVector[1];
+    init2 = initVector[2];
+    init3 = initVector[3];
+
+    // decrypt four word sequences, applying cipher-block chaining (CBC)
+    // to each decrypted block
+    for (wordIx = 0; wordIx < encrypted32.length; wordIx += 4) {
+      // convert big-endian (network order) words into little-endian
+      // (javascript order)
+      encrypted0 = ntoh(encrypted32[wordIx]);
+      encrypted1 = ntoh(encrypted32[wordIx + 1]);
+      encrypted2 = ntoh(encrypted32[wordIx + 2]);
+      encrypted3 = ntoh(encrypted32[wordIx + 3]);
+
+      // decrypt the block
+      decipher.decrypt(encrypted0, encrypted1, encrypted2, encrypted3, decrypted32, wordIx);
+
+      // XOR with the IV, and restore network byte-order to obtain the
+      // plaintext
+      decrypted32[wordIx] = ntoh(decrypted32[wordIx] ^ init0);
+      decrypted32[wordIx + 1] = ntoh(decrypted32[wordIx + 1] ^ init1);
+      decrypted32[wordIx + 2] = ntoh(decrypted32[wordIx + 2] ^ init2);
+      decrypted32[wordIx + 3] = ntoh(decrypted32[wordIx + 3] ^ init3);
+
+      // setup the IV for the next round
+      init0 = encrypted0;
+      init1 = encrypted1;
+      init2 = encrypted2;
+      init3 = encrypted3;
+    }
+
+    return decrypted;
+  };
+
+  /**
+   * The `Decrypter` class that manages decryption of AES
+   * data through `AsyncStream` objects and the `decrypt`
+   * function
+   *
+   * @param {Uint8Array} encrypted the encrypted bytes
+   * @param {Uint32Array} key the bytes of the decryption key
+   * @param {Uint32Array} initVector the initialization vector (IV) to
+   * @param {Function} done the function to run when done
+   * @class Decrypter
+   */
+
+  var Decrypter = function () {
+    function Decrypter(encrypted, key, initVector, done) {
+      classCallCheck$2(this, Decrypter);
+
+      var step = Decrypter.STEP;
+      var encrypted32 = new Int32Array(encrypted.buffer);
+      var decrypted = new Uint8Array(encrypted.byteLength);
+      var i = 0;
+
+      this.asyncStream_ = new AsyncStream();
+
+      // split up the encryption job and do the individual chunks asynchronously
+      this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted));
+      for (i = step; i < encrypted32.length; i += step) {
+        initVector = new Uint32Array([ntoh(encrypted32[i - 4]), ntoh(encrypted32[i - 3]), ntoh(encrypted32[i - 2]), ntoh(encrypted32[i - 1])]);
+        this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted));
+      }
+      // invoke the done() callback when everything is finished
+      this.asyncStream_.push(function () {
+        // remove pkcs#7 padding from the decrypted bytes
+        done(null, unpad(decrypted));
+      });
+    }
+
+    /**
+     * a getter for step the maximum number of bytes to process at one time
+     *
+     * @return {Number} the value of step 32000
+     */
+
+    /**
+     * @private
+     */
+    Decrypter.prototype.decryptChunk_ = function decryptChunk_(encrypted, key, initVector, decrypted) {
+      return function () {
+        var bytes = decrypt(encrypted, key, initVector);
+
+        decrypted.set(bytes, encrypted.byteOffset);
+      };
+    };
+
+    createClass$1(Decrypter, null, [{
+      key: 'STEP',
+      get: function get$$1() {
+        // 4 * 8000;
+        return 32000;
+      }
+    }]);
+    return Decrypter;
+  }();
+
+  /**
+   * @videojs/http-streaming
+   * @version 1.0.0
+   * @copyright 2018 Brightcove, Inc
+   * @license Apache-2.0
+   */
+
+  /**
+   * @file resolve-url.js
+   */
+
+  var resolveUrl$1 = function resolveUrl(baseURL, relativeURL) {
+    // return early if we don't need to resolve
+    if (/^[a-z]+:/i.test(relativeURL)) {
+      return relativeURL;
+    }
+
+    // if the base URL is relative then combine with the current location
+    if (!/\/\//i.test(baseURL)) {
+      baseURL = urlToolkit.buildAbsoluteURL(window_1.location.href, baseURL);
+    }
+
+    return urlToolkit.buildAbsoluteURL(baseURL, relativeURL);
+  };
+
+  /**
+   * @file playlist-loader.js
+   *
+   * A state machine that manages the loading, caching, and updating of
+   * M3U8 playlists.
+   *
+   */
+
+  var mergeOptions$1 = videojs$1.mergeOptions,
+    EventTarget$1 = videojs$1.EventTarget,
+    log$2 = videojs$1.log;
+
+  /**
+   * Loops through all supported media groups in master and calls the provided
+   * callback for each group
+   *
+   * @param {Object} master
+   *        The parsed master manifest object
+   * @param {Function} callback
+   *        Callback to call for each media group
+   */
+
+  var forEachMediaGroup = function forEachMediaGroup(master, callback) {
+    ['AUDIO', 'SUBTITLES'].forEach(function (mediaType) {
+      for (var groupKey in master.mediaGroups[mediaType]) {
+        for (var labelKey in master.mediaGroups[mediaType][groupKey]) {
+          var mediaProperties = master.mediaGroups[mediaType][groupKey][labelKey];
+
+          callback(mediaProperties, mediaType, groupKey, labelKey);
+        }
+      }
+    });
+  };
+
+  /**
+   * Returns a new array of segments that is the result of merging
+   * properties from an older list of segments onto an updated
+   * list. No properties on the updated playlist will be overridden.
+   *
+   * @param {Array} original the outdated list of segments
+   * @param {Array} update the updated list of segments
+   * @param {Number=} offset the index of the first update
+   * segment in the original segment list. For non-live playlists,
+   * this should always be zero and does not need to be
+   * specified. For live playlists, it should be the difference
+   * between the media sequence numbers in the original and updated
+   * playlists.
+   * @return a list of merged segment objects
+   */
+  var updateSegments = function updateSegments(original, update, offset) {
+    var result = update.slice();
+
+    offset = offset || 0;
+    var length = Math.min(original.length, update.length + offset);
+
+    for (var i = offset; i < length; i++) {
+      result[i - offset] = mergeOptions$1(original[i], result[i - offset]);
+    }
+    return result;
+  };
+
+  var resolveSegmentUris = function resolveSegmentUris(segment, baseUri) {
+    if (!segment.resolvedUri) {
+      segment.resolvedUri = resolveUrl$1(baseUri, segment.uri);
+    }
+    if (segment.key && !segment.key.resolvedUri) {
+      segment.key.resolvedUri = resolveUrl$1(baseUri, segment.key.uri);
+    }
+    if (segment.map && !segment.map.resolvedUri) {
+      segment.map.resolvedUri = resolveUrl$1(baseUri, segment.map.uri);
+    }
+  };
+
+  /**
+   * Returns a new master playlist that is the result of merging an
+   * updated media playlist into the original version. If the
+   * updated media playlist does not match any of the playlist
+   * entries in the original master playlist, null is returned.
+   *
+   * @param {Object} master a parsed master M3U8 object
+   * @param {Object} media a parsed media M3U8 object
+   * @return {Object} a new object that represents the original
+   * master playlist with the updated media playlist merged in, or
+   * null if the merge produced no change.
+   */
+  var updateMaster = function updateMaster(master, media) {
+    var result = mergeOptions$1(master, {});
+    var playlist = result.playlists[media.uri];
+
+    if (!playlist) {
+      return null;
+    }
+
+    // consider the playlist unchanged if the number of segments is equal and the media
+    // sequence number is unchanged
+    if (playlist.segments && media.segments && playlist.segments.length === media.segments.length && playlist.mediaSequence === media.mediaSequence) {
+      return null;
+    }
+
+    var mergedPlaylist = mergeOptions$1(playlist, media);
+
+    // if the update could overlap existing segment information, merge the two segment lists
+    if (playlist.segments) {
+      mergedPlaylist.segments = updateSegments(playlist.segments, media.segments, media.mediaSequence - playlist.mediaSequence);
+    }
+
+    // resolve any segment URIs to prevent us from having to do it later
+    mergedPlaylist.segments.forEach(function (segment) {
+      resolveSegmentUris(segment, mergedPlaylist.resolvedUri);
+    });
+
+    // TODO Right now in the playlists array there are two references to each playlist, one
+    // that is referenced by index, and one by URI. The index reference may no longer be
+    // necessary.
+    for (var i = 0; i < result.playlists.length; i++) {
+      if (result.playlists[i].uri === media.uri) {
+        result.playlists[i] = mergedPlaylist;
+      }
+    }
+    result.playlists[media.uri] = mergedPlaylist;
+
+    return result;
+  };
+
+  var setupMediaPlaylists = function setupMediaPlaylists(master) {
+    // setup by-URI lookups and resolve media playlist URIs
+    var i = master.playlists.length;
+
+    while (i--) {
+      var playlist = master.playlists[i];
+
+      master.playlists[playlist.uri] = playlist;
+      playlist.resolvedUri = resolveUrl$1(master.uri, playlist.uri);
+      playlist.id = i;
+
+      if (!playlist.attributes) {
+        // Although the spec states an #EXT-X-STREAM-INF tag MUST have a
+        // BANDWIDTH attribute, we can playOld the stream without it. This means a poorly
+        // formatted master playlist may not have an attribute list. An attributes
+        // property is added here to prevent undefined references when we encounter
+        // this scenario.
+        playlist.attributes = {};
+
+        log$2.warn('Invalid playlist STREAM-INF detected. Missing BANDWIDTH attribute.');
+      }
+    }
+  };
+
+  var resolveMediaGroupUris = function resolveMediaGroupUris(master) {
+    forEachMediaGroup(master, function (properties) {
+      if (properties.uri) {
+        properties.resolvedUri = resolveUrl$1(master.uri, properties.uri);
+      }
+    });
+  };
+
+  /**
+   * Calculates the time to wait before refreshing a live playlist
+   *
+   * @param {Object} media
+   *        The current media
+   * @param {Boolean} update
+   *        True if there were any updates from the last refresh, false otherwise
+   * @return {Number}
+   *         The time in ms to wait before refreshing the live playlist
+   */
+  var refreshDelay = function refreshDelay(media, update) {
+    var lastSegment = media.segments[media.segments.length - 1];
+    var delay = void 0;
+
+    if (update && lastSegment && lastSegment.duration) {
+      delay = lastSegment.duration * 1000;
+    } else {
+      // if the playlist is unchanged since the last reload or last segment duration
+      // cannot be determined, try again after half the target duration
+      delay = (media.targetDuration || 10) * 500;
+    }
+    return delay;
+  };
+
+  /**
+   * Load a playlist from a remote location
+   *
+   * @class PlaylistLoader
+   * @extends Stream
+   * @param {String} srcUrl the url to start with
+   * @param {Boolean} withCredentials the withCredentials xhr option
+   * @constructor
+   */
+
+  var PlaylistLoader = function (_EventTarget) {
+    inherits(PlaylistLoader, _EventTarget);
+
+    function PlaylistLoader(srcUrl, hls, withCredentials) {
+      classCallCheck(this, PlaylistLoader);
+
+      var _this2 = possibleConstructorReturn(this, _EventTarget.call(this));
+
+      _this2.srcUrl = srcUrl;
+      _this2.hls_ = hls;
+      _this2.withCredentials = withCredentials;
+
+      if (!_this2.srcUrl) {
+        throw new Error('A non-empty playlist URL is required');
+      }
+
+      // initialize the loader state
+      _this2.state = 'HAVE_NOTHING';
+
+      // live playlist staleness timeout
+      _this2.on('mediaupdatetimeout', function () {
+        if (_this2.state !== 'HAVE_METADATA') {
+          // only refresh the media playlist if no other activity is going on
+          return;
+        }
+
+        _this2.state = 'HAVE_CURRENT_METADATA';
+
+        _this2.request = _this2.hls_.xhr({
+          uri: resolveUrl$1(_this2.master.uri, _this2.media().uri),
+          withCredentials: _this2.withCredentials
+        }, function (error, req) {
+          // disposed
+          if (!_this2.request) {
+            return;
+          }
+
+          if (error) {
+            return _this2.playlistRequestError(_this2.request, _this2.media().uri, 'HAVE_METADATA');
+          }
+
+          _this2.haveMetadata(_this2.request, _this2.media().uri);
+        });
+      });
+      return _this2;
+    }
+
+    PlaylistLoader.prototype.playlistRequestError = function playlistRequestError(xhr, url, startingState) {
+      // any in-flight request is now finished
+      this.request = null;
+
+      if (startingState) {
+        this.state = startingState;
+      }
+
+      this.error = {
+        playlist: this.master.playlists[url],
+        status: xhr.status,
+        message: 'HLS playlist request error at URL: ' + url,
+        responseText: xhr.responseText,
+        code: xhr.status >= 500 ? 4 : 2
+      };
+
+      this.trigger('error');
+    };
+
+    // update the playlist loader's state in response to a new or
+    // updated playlist.
+
+
+    PlaylistLoader.prototype.haveMetadata = function haveMetadata(xhr, url) {
+      var _this3 = this;
+
+      // any in-flight request is now finished
+      this.request = null;
+      this.state = 'HAVE_METADATA';
+
+      var parser = new Parser();
+
+      parser.push(xhr.responseText);
+      parser.end();
+      parser.manifest.uri = url;
+      // m3u8-parser does not attach an attributes property to media playlists so make
+      // sure that the property is attached to avoid undefined reference errors
+      parser.manifest.attributes = parser.manifest.attributes || {};
+
+      // merge this playlist into the master
+      var update = updateMaster(this.master, parser.manifest);
+
+      this.targetDuration = parser.manifest.targetDuration;
+
+      if (update) {
+        this.master = update;
+        this.media_ = this.master.playlists[parser.manifest.uri];
+      } else {
+        this.trigger('playlistunchanged');
+      }
+
+      // refresh live playlists after a target duration passes
+      if (!this.media().endList) {
+        window_1.clearTimeout(this.mediaUpdateTimeout);
+        this.mediaUpdateTimeout = window_1.setTimeout(function () {
+          _this3.trigger('mediaupdatetimeout');
+        }, refreshDelay(this.media(), !!update));
+      }
+
+      this.trigger('loadedplaylist');
+    };
+
+    /**
+     * Abort any outstanding work and clean up.
+     */
+
+
+    PlaylistLoader.prototype.dispose = function dispose() {
+      this.stopRequest();
+      window_1.clearTimeout(this.mediaUpdateTimeout);
+    };
+
+    PlaylistLoader.prototype.stopRequest = function stopRequest() {
+      if (this.request) {
+        var oldRequest = this.request;
+
+        this.request = null;
+        oldRequest.onreadystatechange = null;
+        oldRequest.abort();
+      }
+    };
+
+    /**
+     * When called without any arguments, returns the currently
+     * active media playlist. When called with a single argument,
+     * triggers the playlist loader to asynchronously switch to the
+     * specified media playlist. Calling this method while the
+     * loader is in the HAVE_NOTHING causes an error to be emitted
+     * but otherwise has no effect.
+     *
+     * @param {Object=} playlist the parsed media playlist
+     * object to switch to
+     * @return {Playlist} the current loaded media
+     */
+
+
+    PlaylistLoader.prototype.media = function media(playlist) {
+      var _this4 = this;
+
+      // getter
+      if (!playlist) {
+        return this.media_;
+      }
+
+      // setter
+      if (this.state === 'HAVE_NOTHING') {
+        throw new Error('Cannot switch media playlist from ' + this.state);
+      }
+
+      var startingState = this.state;
+
+      // find the playlist object if the target playlist has been
+      // specified by URI
+      if (typeof playlist === 'string') {
+        if (!this.master.playlists[playlist]) {
+          throw new Error('Unknown playlist URI: ' + playlist);
+        }
+        playlist = this.master.playlists[playlist];
+      }
+
+      var mediaChange = !this.media_ || playlist.uri !== this.media_.uri;
+
+      // switch to fully loaded playlists immediately
+      if (this.master.playlists[playlist.uri].endList) {
+        // abort outstanding playlist requests
+        if (this.request) {
+          this.request.onreadystatechange = null;
+          this.request.abort();
+          this.request = null;
+        }
+        this.state = 'HAVE_METADATA';
+        this.media_ = playlist;
+
+        // trigger media change if the active media has been updated
+        if (mediaChange) {
+          this.trigger('mediachanging');
+          this.trigger('mediachange');
+        }
+        return;
+      }
+
+      // switching to the active playlist is a no-op
+      if (!mediaChange) {
+        return;
+      }
+
+      this.state = 'SWITCHING_MEDIA';
+
+      // there is already an outstanding playlist request
+      if (this.request) {
+        if (resolveUrl$1(this.master.uri, playlist.uri) === this.request.url) {
+          // requesting to switch to the same playlist multiple times
+          // has no effect after the first
+          return;
+        }
+        this.request.onreadystatechange = null;
+        this.request.abort();
+        this.request = null;
+      }
+
+      // request the new playlist
+      if (this.media_) {
+        this.trigger('mediachanging');
+      }
+
+      this.request = this.hls_.xhr({
+        uri: resolveUrl$1(this.master.uri, playlist.uri),
+        withCredentials: this.withCredentials
+      }, function (error, req) {
+        // disposed
+        if (!_this4.request) {
+          return;
+        }
+
+        if (error) {
+          return _this4.playlistRequestError(_this4.request, playlist.uri, startingState);
+        }
+
+        _this4.haveMetadata(req, playlist.uri);
+
+        // fire loadedmetadata the first time a media playlist is loaded
+        if (startingState === 'HAVE_MASTER') {
+          _this4.trigger('loadedmetadata');
+        } else {
+          _this4.trigger('mediachange');
+        }
+      });
+    };
+
+    /**
+     * pause loading of the playlist
+     */
+
+
+    PlaylistLoader.prototype.pause = function pause() {
+      this.stopRequest();
+      window_1.clearTimeout(this.mediaUpdateTimeout);
+      if (this.state === 'HAVE_NOTHING') {
+        // If we pause the loader before any data has been retrieved, its as if we never
+        // started, so reset to an unstarted state.
+        this.started = false;
+      }
+      // Need to restore state now that no activity is happening
+      if (this.state === 'SWITCHING_MEDIA') {
+        // if the loader was in the process of switching media, it should either return to
+        // HAVE_MASTER or HAVE_METADATA depending on if the loader has loaded a media
+        // playlist yet. This is determined by the existence of loader.media_
+        if (this.media_) {
+          this.state = 'HAVE_METADATA';
+        } else {
+          this.state = 'HAVE_MASTER';
+        }
+      } else if (this.state === 'HAVE_CURRENT_METADATA') {
+        this.state = 'HAVE_METADATA';
+      }
+    };
+
+    /**
+     * start loading of the playlist
+     */
+
+
+    PlaylistLoader.prototype.load = function load(isFinalRendition) {
+      var _this5 = this;
+
+      window_1.clearTimeout(this.mediaUpdateTimeout);
+
+      var media = this.media();
+
+      if (isFinalRendition) {
+        var delay = media ? media.targetDuration / 2 * 1000 : 5 * 1000;
+
+        this.mediaUpdateTimeout = window_1.setTimeout(function () {
+          return _this5.load();
+        }, delay);
+        return;
+      }
+
+      if (!this.started) {
+        this.start();
+        return;
+      }
+
+      if (media && !media.endList) {
+        this.trigger('mediaupdatetimeout');
+      } else {
+        this.trigger('loadedplaylist');
+      }
+    };
+
+    /**
+     * start loading of the playlist
+     */
+
+
+    PlaylistLoader.prototype.start = function start() {
+      var _this6 = this;
+
+      this.started = true;
+
+      // request the specified URL
+      this.request = this.hls_.xhr({
+        uri: this.srcUrl,
+        withCredentials: this.withCredentials
+      }, function (error, req) {
+        // disposed
+        if (!_this6.request) {
+          return;
+        }
+
+        // clear the loader's request reference
+        _this6.request = null;
+
+        if (error) {
+          _this6.error = {
+            status: req.status,
+            message: 'HLS playlist request error at URL: ' + _this6.srcUrl,
+            responseText: req.responseText,
+            // MEDIA_ERR_NETWORK
+            code: 2
+          };
+          if (_this6.state === 'HAVE_NOTHING') {
+            _this6.started = false;
+          }
+          return _this6.trigger('error');
+        }
+
+        var parser = new Parser();
+
+        parser.push(req.responseText);
+        parser.end();
+
+        _this6.state = 'HAVE_MASTER';
+
+        parser.manifest.uri = _this6.srcUrl;
+
+        // loaded a master playlist
+        if (parser.manifest.playlists) {
+          _this6.master = parser.manifest;
+
+          setupMediaPlaylists(_this6.master);
+          resolveMediaGroupUris(_this6.master);
+
+          _this6.trigger('loadedplaylist');
+          if (!_this6.request) {
+            // no media playlist was specifically selected so start
+            // from the first listed one
+            _this6.media(parser.manifest.playlists[0]);
+          }
+          return;
+        }
+
+        // loaded a media playlist
+        // infer a master playlist if none was previously requested
+        _this6.master = {
+          mediaGroups: {
+            'AUDIO': {},
+            'VIDEO': {},
+            'CLOSED-CAPTIONS': {},
+            'SUBTITLES': {}
+          },
+          uri: window_1.location.href,
+          playlists: [{
+            uri: _this6.srcUrl,
+            id: 0
+          }]
+        };
+        _this6.master.playlists[_this6.srcUrl] = _this6.master.playlists[0];
+        _this6.master.playlists[0].resolvedUri = _this6.srcUrl;
+        // m3u8-parser does not attach an attributes property to media playlists so make
+        // sure that the property is attached to avoid undefined reference errors
+        _this6.master.playlists[0].attributes = _this6.master.playlists[0].attributes || {};
+        _this6.haveMetadata(req, _this6.srcUrl);
+        return _this6.trigger('loadedmetadata');
+      });
+    };
+
+    return PlaylistLoader;
+  }(EventTarget$1);
+
+  /**
+   * @file playlist.js
+   *
+   * Playlist related utilities.
+   */
+
+  var createTimeRange = videojs$1.createTimeRange;
+
+  /**
+   * walk backward until we find a duration we can use
+   * or return a failure
+   *
+   * @param {Playlist} playlist the playlist to walk through
+   * @param {Number} endSequence the mediaSequence to stop walking on
+   */
+
+  var backwardDuration = function backwardDuration(playlist, endSequence) {
+    var result = 0;
+    var i = endSequence - playlist.mediaSequence;
+    // if a start time is available for segment immediately following
+    // the interval, use it
+    var segment = playlist.segments[i];
+
+    // Walk backward until we find the latest segment with timeline
+    // information that is earlier than endSequence
+    if (segment) {
+      if (typeof segment.start !== 'undefined') {
+        return {result: segment.start, precise: true};
+      }
+      if (typeof segment.end !== 'undefined') {
+        return {
+          result: segment.end - segment.duration,
+          precise: true
+        };
+      }
+    }
+    while (i--) {
+      segment = playlist.segments[i];
+      if (typeof segment.end !== 'undefined') {
+        return {result: result + segment.end, precise: true};
+      }
+
+      result += segment.duration;
+
+      if (typeof segment.start !== 'undefined') {
+        return {result: result + segment.start, precise: true};
+      }
+    }
+    return {result: result, precise: false};
+  };
+
+  /**
+   * walk forward until we find a duration we can use
+   * or return a failure
+   *
+   * @param {Playlist} playlist the playlist to walk through
+   * @param {Number} endSequence the mediaSequence to stop walking on
+   */
+  var forwardDuration = function forwardDuration(playlist, endSequence) {
+    var result = 0;
+    var segment = void 0;
+    var i = endSequence - playlist.mediaSequence;
+    // Walk forward until we find the earliest segment with timeline
+    // information
+
+    for (; i < playlist.segments.length; i++) {
+      segment = playlist.segments[i];
+      if (typeof segment.start !== 'undefined') {
+        return {
+          result: segment.start - result,
+          precise: true
+        };
+      }
+
+      result += segment.duration;
+
+      if (typeof segment.end !== 'undefined') {
+        return {
+          result: segment.end - result,
+          precise: true
+        };
+      }
+    }
+    // indicate we didn't find a useful duration estimate
+    return {result: -1, precise: false};
+  };
+
+  /**
+   * Calculate the media duration from the segments associated with a
+   * playlist. The duration of a subinterval of the available segments
+   * may be calculated by specifying an end index.
+   *
+   * @param {Object} playlist a media playlist object
+   * @param {Number=} endSequence an exclusive upper boundary
+   * for the playlist.  Defaults to playlist length.
+   * @param {Number} expired the amount of time that has dropped
+   * off the front of the playlist in a live scenario
+   * @return {Number} the duration between the first available segment
+   * and end index.
+   */
+  var intervalDuration = function intervalDuration(playlist, endSequence, expired) {
+    var backward = void 0;
+    var forward = void 0;
+
+    if (typeof endSequence === 'undefined') {
+      endSequence = playlist.mediaSequence + playlist.segments.length;
+    }
+
+    if (endSequence < playlist.mediaSequence) {
+      return 0;
+    }
+
+    // do a backward walk to estimate the duration
+    backward = backwardDuration(playlist, endSequence);
+    if (backward.precise) {
+      // if we were able to base our duration estimate on timing
+      // information provided directly from the Media Source, return
+      // it
+      return backward.result;
+    }
+
+    // walk forward to see if a precise duration estimate can be made
+    // that way
+    forward = forwardDuration(playlist, endSequence);
+    if (forward.precise) {
+      // we found a segment that has been buffered and so it's
+      // position is known precisely
+      return forward.result;
+    }
+
+    // return the less-precise, playlist-based duration estimate
+    return backward.result + expired;
+  };
+
+  /**
+   * Calculates the duration of a playlist. If a start and end index
+   * are specified, the duration will be for the subset of the media
+   * timeline between those two indices. The total duration for live
+   * playlists is always Infinity.
+   *
+   * @param {Object} playlist a media playlist object
+   * @param {Number=} endSequence an exclusive upper
+   * boundary for the playlist. Defaults to the playlist media
+   * sequence number plus its length.
+   * @param {Number=} expired the amount of time that has
+   * dropped off the front of the playlist in a live scenario
+   * @return {Number} the duration between the start index and end
+   * index.
+   */
+  var duration = function duration(playlist, endSequence, expired) {
+    if (!playlist) {
+      return 0;
+    }
+
+    if (typeof expired !== 'number') {
+      expired = 0;
+    }
+
+    // if a slice of the total duration is not requested, use
+    // playlist-level duration indicators when they're present
+    if (typeof endSequence === 'undefined') {
+      // if present, use the duration specified in the playlist
+      if (playlist.totalDuration) {
+        return playlist.totalDuration;
+      }
+
+      // duration should be Infinity for live playlists
+      if (!playlist.endList) {
+        return window_1.Infinity;
+      }
+    }
+
+    // calculate the total duration based on the segment durations
+    return intervalDuration(playlist, endSequence, expired);
+  };
+
+  /**
+   * Calculate the time between two indexes in the current playlist
+   * neight the start- nor the end-index need to be within the current
+   * playlist in which case, the targetDuration of the playlist is used
+   * to approximate the durations of the segments
+   *
+   * @param {Object} playlist a media playlist object
+   * @param {Number} startIndex
+   * @param {Number} endIndex
+   * @return {Number} the number of seconds between startIndex and endIndex
+   */
+  var sumDurations = function sumDurations(playlist, startIndex, endIndex) {
+    var durations = 0;
+
+    if (startIndex > endIndex) {
+      var _ref = [endIndex, startIndex];
+      startIndex = _ref[0];
+      endIndex = _ref[1];
+    }
+
+    if (startIndex < 0) {
+      for (var i = startIndex; i < Math.min(0, endIndex); i++) {
+        durations += playlist.targetDuration;
+      }
+      startIndex = 0;
+    }
+
+    for (var _i2 = startIndex; _i2 < endIndex; _i2++) {
+      durations += playlist.segments[_i2].duration;
+    }
+
+    return durations;
+  };
+
+  /**
+   * Determines the media index of the segment corresponding to the safe edge of the live
+   * window which is the duration of the last segment plus 2 target durations from the end
+   * of the playlist.
+   *
+   * @param {Object} playlist
+   *        a media playlist object
+   * @return {Number}
+   *         The media index of the segment at the safe live point. 0 if there is no "safe"
+   *         point.
+   * @function safeLiveIndex
+   */
+  var safeLiveIndex = function safeLiveIndex(playlist) {
+    if (!playlist.segments.length) {
+      return 0;
+    }
+
+    var i = playlist.segments.length - 1;
+    var distanceFromEnd = playlist.segments[i].duration || playlist.targetDuration;
+    var safeDistance = distanceFromEnd + playlist.targetDuration * 2;
+
+    while (i--) {
+      distanceFromEnd += playlist.segments[i].duration;
+
+      if (distanceFromEnd >= safeDistance) {
+        break;
+      }
+    }
+
+    return Math.max(0, i);
+  };
+
+  /**
+   * Calculates the playlist end time
+   *
+   * @param {Object} playlist a media playlist object
+   * @param {Number=} expired the amount of time that has
+   *                  dropped off the front of the playlist in a live scenario
+   * @param {Boolean|false} useSafeLiveEnd a boolean value indicating whether or not the
+   *                        playlist end calculation should consider the safe live end
+   *                        (truncate the playlist end by three segments). This is normally
+   *                        used for calculating the end of the playlist's seekable range.
+   * @returns {Number} the end time of playlist
+   * @function playlistEnd
+   */
+  var playlistEnd = function playlistEnd(playlist, expired, useSafeLiveEnd) {
+    if (!playlist || !playlist.segments) {
+      return null;
+    }
+    if (playlist.endList) {
+      return duration(playlist);
+    }
+
+    if (expired === null) {
+      return null;
+    }
+
+    expired = expired || 0;
+
+    var endSequence = useSafeLiveEnd ? safeLiveIndex(playlist) : playlist.segments.length;
+
+    return intervalDuration(playlist, playlist.mediaSequence + endSequence, expired);
+  };
+
+  /**
+   * Calculates the interval of time that is currently seekable in a
+   * playlist. The returned time ranges are relative to the earliest
+   * moment in the specified playlist that is still available. A full
+   * seekable implementation for live streams would need to offset
+   * these values by the duration of content that has expired from the
+   * stream.
+   *
+   * @param {Object} playlist a media playlist object
+   * dropped off the front of the playlist in a live scenario
+   * @param {Number=} expired the amount of time that has
+   * dropped off the front of the playlist in a live scenario
+   * @return {TimeRanges} the periods of time that are valid targets
+   * for seeking
+   */
+  var seekable = function seekable(playlist, expired) {
+    var useSafeLiveEnd = true;
+    var seekableStart = expired || 0;
+    var seekableEnd = playlistEnd(playlist, expired, useSafeLiveEnd);
+
+    if (seekableEnd === null) {
+      return createTimeRange();
+    }
+    return createTimeRange(seekableStart, seekableEnd);
+  };
+
+  var isWholeNumber = function isWholeNumber(num) {
+    return num - Math.floor(num) === 0;
+  };
+
+  var roundSignificantDigit = function roundSignificantDigit(increment, num) {
+    // If we have a whole number, just add 1 to it
+    if (isWholeNumber(num)) {
+      return num + increment * 0.1;
+    }
+
+    var numDecimalDigits = num.toString().split('.')[1].length;
+
+    for (var i = 1; i <= numDecimalDigits; i++) {
+      var scale = Math.pow(10, i);
+      var temp = num * scale;
+
+      if (isWholeNumber(temp) || i === numDecimalDigits) {
+        return (temp + increment) / scale;
+      }
+    }
+  };
+
+  var ceilLeastSignificantDigit = roundSignificantDigit.bind(null, 1);
+  var floorLeastSignificantDigit = roundSignificantDigit.bind(null, -1);
+
+  /**
+   * Determine the index and estimated starting time of the segment that
+   * contains a specified playback position in a media playlist.
+   *
+   * @param {Object} playlist the media playlist to query
+   * @param {Number} currentTime The number of seconds since the earliest
+   * possible position to determine the containing segment for
+   * @param {Number} startIndex
+   * @param {Number} startTime
+   * @return {Object}
+   */
+  var getMediaInfoForTime = function getMediaInfoForTime(playlist, currentTime, startIndex, startTime) {
+    var i = void 0;
+    var segment = void 0;
+    var numSegments = playlist.segments.length;
+
+    var time = currentTime - startTime;
+
+    if (time < 0) {
+      // Walk backward from startIndex in the playlist, adding durations
+      // until we find a segment that contains `time` and return it
+      if (startIndex > 0) {
+        for (i = startIndex - 1; i >= 0; i--) {
+          segment = playlist.segments[i];
+          time += floorLeastSignificantDigit(segment.duration);
+          if (time > 0) {
+            return {
+              mediaIndex: i,
+              startTime: startTime - sumDurations(playlist, startIndex, i)
+            };
+          }
+        }
+      }
+      // We were unable to find a good segment within the playlist
+      // so select the first segment
+      return {
+        mediaIndex: 0,
+        startTime: currentTime
+      };
+    }
+
+    // When startIndex is negative, we first walk forward to first segment
+    // adding target durations. If we "run out of time" before getting to
+    // the first segment, return the first segment
+    if (startIndex < 0) {
+      for (i = startIndex; i < 0; i++) {
+        time -= playlist.targetDuration;
+        if (time < 0) {
+          return {
+            mediaIndex: 0,
+            startTime: currentTime
+          };
+        }
+      }
+      startIndex = 0;
+    }
+
+    // Walk forward from startIndex in the playlist, subtracting durations
+    // until we find a segment that contains `time` and return it
+    for (i = startIndex; i < numSegments; i++) {
+      segment = playlist.segments[i];
+      time -= ceilLeastSignificantDigit(segment.duration);
+      if (time < 0) {
+        return {
+          mediaIndex: i,
+          startTime: startTime + sumDurations(playlist, startIndex, i)
+        };
+      }
+    }
+
+    // We are out of possible candidates so load the last one...
+    return {
+      mediaIndex: numSegments - 1,
+      startTime: currentTime
+    };
+  };
+
+  /**
+   * Check whether the playlist is blacklisted or not.
+   *
+   * @param {Object} playlist the media playlist object
+   * @return {boolean} whether the playlist is blacklisted or not
+   * @function isBlacklisted
+   */
+  var isBlacklisted = function isBlacklisted(playlist) {
+    return playlist.excludeUntil && playlist.excludeUntil > Date.now();
+  };
+
+  /**
+   * Check whether the playlist is compatible with current playback configuration or has
+   * been blacklisted permanently for being incompatible.
+   *
+   * @param {Object} playlist the media playlist object
+   * @return {boolean} whether the playlist is incompatible or not
+   * @function isIncompatible
+   */
+  var isIncompatible = function isIncompatible(playlist) {
+    return playlist.excludeUntil && playlist.excludeUntil === Infinity;
+  };
+
+  /**
+   * Check whether the playlist is enabled or not.
+   *
+   * @param {Object} playlist the media playlist object
+   * @return {boolean} whether the playlist is enabled or not
+   * @function isEnabled
+   */
+  var isEnabled = function isEnabled(playlist) {
+    var blacklisted = isBlacklisted(playlist);
+
+    return !playlist.disabled && !blacklisted;
+  };
+
+  /**
+   * Check whether the playlist has been manually disabled through the representations api.
+   *
+   * @param {Object} playlist the media playlist object
+   * @return {boolean} whether the playlist is disabled manually or not
+   * @function isDisabled
+   */
+  var isDisabled = function isDisabled(playlist) {
+    return playlist.disabled;
+  };
+
+  /**
+   * Returns whether the current playlist is an AES encrypted HLS stream
+   *
+   * @return {Boolean} true if it's an AES encrypted HLS stream
+   */
+  var isAes = function isAes(media) {
+    for (var i = 0; i < media.segments.length; i++) {
+      if (media.segments[i].key) {
+        return true;
+      }
+    }
+    return false;
+  };
+
+  /**
+   * Returns whether the current playlist contains fMP4
+   *
+   * @return {Boolean} true if the playlist contains fMP4
+   */
+  var isFmp4 = function isFmp4(media) {
+    for (var i = 0; i < media.segments.length; i++) {
+      if (media.segments[i].map) {
+        return true;
+      }
+    }
+    return false;
+  };
+
+  /**
+   * Checks if the playlist has a value for the specified attribute
+   *
+   * @param {String} attr
+   *        Attribute to check for
+   * @param {Object} playlist
+   *        The media playlist object
+   * @return {Boolean}
+   *         Whether the playlist contains a value for the attribute or not
+   * @function hasAttribute
+   */
+  var hasAttribute = function hasAttribute(attr, playlist) {
+    return playlist.attributes && playlist.attributes[attr];
+  };
+
+  /**
+   * Estimates the time required to complete a segment download from the specified playlist
+   *
+   * @param {Number} segmentDuration
+   *        Duration of requested segment
+   * @param {Number} bandwidth
+   *        Current measured bandwidth of the player
+   * @param {Object} playlist
+   *        The media playlist object
+   * @param {Number=} bytesReceived
+   *        Number of bytes already received for the request. Defaults to 0
+   * @return {Number|NaN}
+   *         The estimated time to request the segment. NaN if bandwidth information for
+   *         the given playlist is unavailable
+   * @function estimateSegmentRequestTime
+   */
+  var estimateSegmentRequestTime = function estimateSegmentRequestTime(segmentDuration, bandwidth, playlist) {
+    var bytesReceived = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
+
+    if (!hasAttribute('BANDWIDTH', playlist)) {
+      return NaN;
+    }
+
+    var size = segmentDuration * playlist.attributes.BANDWIDTH;
+
+    return (size - bytesReceived * 8) / bandwidth;
+  };
+
+  /*
+   * Returns whether the current playlist is the lowest rendition
+   *
+   * @return {Boolean} true if on lowest rendition
+   */
+  var isLowestEnabledRendition = function isLowestEnabledRendition(master, media) {
+    if (master.playlists.length === 1) {
+      return true;
+    }
+
+    var currentBandwidth = media.attributes.BANDWIDTH || Number.MAX_VALUE;
+
+    return master.playlists.filter(function (playlist) {
+      if (!isEnabled(playlist)) {
+        return false;
+      }
+
+      return (playlist.attributes.BANDWIDTH || 0) < currentBandwidth;
+    }).length === 0;
+  };
+
+  // exports
+  var Playlist = {
+    duration: duration,
+    seekable: seekable,
+    safeLiveIndex: safeLiveIndex,
+    getMediaInfoForTime: getMediaInfoForTime,
+    isEnabled: isEnabled,
+    isDisabled: isDisabled,
+    isBlacklisted: isBlacklisted,
+    isIncompatible: isIncompatible,
+    playlistEnd: playlistEnd,
+    isAes: isAes,
+    isFmp4: isFmp4,
+    hasAttribute: hasAttribute,
+    estimateSegmentRequestTime: estimateSegmentRequestTime,
+    isLowestEnabledRendition: isLowestEnabledRendition
+  };
+
+  /**
+   * @file xhr.js
+   */
+
+  var videojsXHR = videojs$1.xhr,
+    mergeOptions$1$1 = videojs$1.mergeOptions;
+
+
+  var xhrFactory = function xhrFactory() {
+    var xhr = function XhrFunction(options, callback) {
+      // Add a default timeout for all hls requests
+      options = mergeOptions$1$1({
+        timeout: 45e3
+      }, options);
+
+      // Allow an optional user-specified function to modify the option
+      // object before we construct the xhr request
+      var beforeRequest = XhrFunction.beforeRequest || videojs$1.Hls.xhr.beforeRequest;
+
+      if (beforeRequest && typeof beforeRequest === 'function') {
+        var newOptions = beforeRequest(options);
+
+        if (newOptions) {
+          options = newOptions;
+        }
+      }
+
+      var request = videojsXHR(options, function (error, response) {
+        var reqResponse = request.response;
+
+        if (!error && reqResponse) {
+          request.responseTime = Date.now();
+          request.roundTripTime = request.responseTime - request.requestTime;
+          request.bytesReceived = reqResponse.byteLength || reqResponse.length;
+          if (!request.bandwidth) {
+            request.bandwidth = Math.floor(request.bytesReceived / request.roundTripTime * 8 * 1000);
+          }
+        }
+
+        if (response.headers) {
+          request.responseHeaders = response.headers;
+        }
+
+        // videojs.xhr now uses a specific code on the error
+        // object to signal that a request has timed out instead
+        // of setting a boolean on the request object
+        if (error && error.code === 'ETIMEDOUT') {
+          request.timedout = true;
+        }
+
+        // videojs.xhr no longer considers status codes outside of 200 and 0
+        // (for file uris) to be errors, but the old XHR did, so emulate that
+        // behavior. Status 206 may be used in response to byterange requests.
+        if (!error && !request.aborted && response.statusCode !== 200 && response.statusCode !== 206 && response.statusCode !== 0) {
+          error = new Error('XHR Failed with a response of: ' + (request && (reqResponse || request.responseText)));
+        }
+
+        callback(error, request);
+      });
+      var originalAbort = request.abort;
+
+      request.abort = function () {
+        request.aborted = true;
+        return originalAbort.apply(request, arguments);
+      };
+      request.uri = options.uri;
+      request.requestTime = Date.now();
+      return request;
+    };
+
+    return xhr;
+  };
+
+  /**
+   * @file bin-utils.js
+   */
+
+  /**
+   * convert a TimeRange to text
+   *
+   * @param {TimeRange} range the timerange to use for conversion
+   * @param {Number} i the iterator on the range to convert
+   */
+  var textRange = function textRange(range, i) {
+    return range.start(i) + '-' + range.end(i);
+  };
+
+  /**
+   * format a number as hex string
+   *
+   * @param {Number} e The number
+   * @param {Number} i the iterator
+   */
+  var formatHexString = function formatHexString(e, i) {
+    var value = e.toString(16);
+
+    return '00'.substring(0, 2 - value.length) + value + (i % 2 ? ' ' : '');
+  };
+  var formatAsciiString = function formatAsciiString(e) {
+    if (e >= 0x20 && e < 0x7e) {
+      return String.fromCharCode(e);
+    }
+    return '.';
+  };
+
+  /**
+   * Creates an object for sending to a web worker modifying properties that are TypedArrays
+   * into a new object with seperated properties for the buffer, byteOffset, and byteLength.
+   *
+   * @param {Object} message
+   *        Object of properties and values to send to the web worker
+   * @return {Object}
+   *         Modified message with TypedArray values expanded
+   * @function createTransferableMessage
+   */
+  var createTransferableMessage = function createTransferableMessage(message) {
+    var transferable = {};
+
+    Object.keys(message).forEach(function (key) {
+      var value = message[key];
+
+      if (ArrayBuffer.isView(value)) {
+        transferable[key] = {
+          bytes: value.buffer,
+          byteOffset: value.byteOffset,
+          byteLength: value.byteLength
+        };
+      } else {
+        transferable[key] = value;
+      }
+    });
+
+    return transferable;
+  };
+
+  /**
+   * Returns a unique string identifier for a media initialization
+   * segment.
+   */
+  var initSegmentId = function initSegmentId(initSegment) {
+    var byterange = initSegment.byterange || {
+      length: Infinity,
+      offset: 0
+    };
+
+    return [byterange.length, byterange.offset, initSegment.resolvedUri].join(',');
+  };
+
+  /**
+   * utils to help dump binary data to the console
+   */
+  var hexDump = function hexDump(data) {
+    var bytes = Array.prototype.slice.call(data);
+    var step = 16;
+    var result = '';
+    var hex = void 0;
+    var ascii = void 0;
+
+    for (var j = 0; j < bytes.length / step; j++) {
+      hex = bytes.slice(j * step, j * step + step).map(formatHexString).join('');
+      ascii = bytes.slice(j * step, j * step + step).map(formatAsciiString).join('');
+      result += hex + ' ' + ascii + '\n';
+    }
+
+    return result;
+  };
+
+  var tagDump = function tagDump(_ref2) {
+    var bytes = _ref2.bytes;
+    return hexDump(bytes);
+  };
+
+  var textRanges = function textRanges(ranges) {
+    var result = '';
+    var i = void 0;
+
+    for (i = 0; i < ranges.length; i++) {
+      result += textRange(ranges, i) + ' ';
+    }
+    return result;
+  };
+
+  var utils = /*#__PURE__*/Object.freeze({
+    createTransferableMessage: createTransferableMessage,
+    initSegmentId: initSegmentId,
+    hexDump: hexDump,
+    tagDump: tagDump,
+    textRanges: textRanges
+  });
+
+  /**
+   * ranges
+   *
+   * Utilities for working with TimeRanges.
+   *
+   */
+
+    // Fudge factor to account for TimeRanges rounding
+  var TIME_FUDGE_FACTOR = 1 / 30;
+  // Comparisons between time values such as current time and the end of the buffered range
+  // can be misleading because of precision differences or when the current media has poorly
+  // aligned audio and video, which can cause values to be slightly off from what you would
+  // expect. This value is what we consider to be safe to use in such comparisons to account
+  // for these scenarios.
+  var SAFE_TIME_DELTA = TIME_FUDGE_FACTOR * 3;
+  var filterRanges = function filterRanges(timeRanges, predicate) {
+    var results = [];
+    var i = void 0;
+
+    if (timeRanges && timeRanges.length) {
+      // Search for ranges that match the predicate
+      for (i = 0; i < timeRanges.length; i++) {
+        if (predicate(timeRanges.start(i), timeRanges.end(i))) {
+          results.push([timeRanges.start(i), timeRanges.end(i)]);
+        }
+      }
+    }
+
+    return videojs$1.createTimeRanges(results);
+  };
+
+  /**
+   * Attempts to find the buffered TimeRange that contains the specified
+   * time.
+   * @param {TimeRanges} buffered - the TimeRanges object to query
+   * @param {number} time  - the time to filter on.
+   * @returns {TimeRanges} a new TimeRanges object
+   */
+  var findRange = function findRange(buffered, time) {
+    return filterRanges(buffered, function (start, end) {
+      return start - TIME_FUDGE_FACTOR <= time && end + TIME_FUDGE_FACTOR >= time;
+    });
+  };
+
+  /**
+   * Returns the TimeRanges that begin later than the specified time.
+   * @param {TimeRanges} timeRanges - the TimeRanges object to query
+   * @param {number} time - the time to filter on.
+   * @returns {TimeRanges} a new TimeRanges object.
+   */
+  var findNextRange = function findNextRange(timeRanges, time) {
+    return filterRanges(timeRanges, function (start) {
+      return start - TIME_FUDGE_FACTOR >= time;
+    });
+  };
+
+  /**
+   * Returns gaps within a list of TimeRanges
+   * @param {TimeRanges} buffered - the TimeRanges object
+   * @return {TimeRanges} a TimeRanges object of gaps
+   */
+  var findGaps = function findGaps(buffered) {
+    if (buffered.length < 2) {
+      return videojs$1.createTimeRanges();
+    }
+
+    var ranges = [];
+
+    for (var i = 1; i < buffered.length; i++) {
+      var start = buffered.end(i - 1);
+      var end = buffered.start(i);
+
+      ranges.push([start, end]);
+    }
+
+    return videojs$1.createTimeRanges(ranges);
+  };
+
+  /**
+   * Gets a human readable string for a TimeRange
+   *
+   * @param {TimeRange} range
+   * @returns {String} a human readable string
+   */
+  var printableRange = function printableRange(range) {
+    var strArr = [];
+
+    if (!range || !range.length) {
+      return '';
+    }
+
+    for (var i = 0; i < range.length; i++) {
+      strArr.push(range.start(i) + ' => ' + range.end(i));
+    }
+
+    return strArr.join(', ');
+  };
+
+  /**
+   * Calculates the amount of time left in seconds until the player hits the end of the
+   * buffer and causes a rebuffer
+   *
+   * @param {TimeRange} buffered
+   *        The state of the buffer
+   * @param {Numnber} currentTime
+   *        The current time of the player
+   * @param {Number} playbackRate
+   *        The current playback rate of the player. Defaults to 1.
+   * @return {Number}
+   *         Time until the player has to start rebuffering in seconds.
+   * @function timeUntilRebuffer
+   */
+  var timeUntilRebuffer = function timeUntilRebuffer(buffered, currentTime) {
+    var playbackRate = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
+
+    var bufferedEnd = buffered.length ? buffered.end(buffered.length - 1) : 0;
+
+    return (bufferedEnd - currentTime) / playbackRate;
+  };
+
+  /**
+   * Converts a TimeRanges object into an array representation
+   * @param {TimeRanges} timeRanges
+   * @returns {Array}
+   */
+  var timeRangesToArray = function timeRangesToArray(timeRanges) {
+    var timeRangesList = [];
+
+    for (var i = 0; i < timeRanges.length; i++) {
+      timeRangesList.push({
+        start: timeRanges.start(i),
+        end: timeRanges.end(i)
+      });
+    }
+
+    return timeRangesList;
+  };
+
+  /**
+   * @file create-text-tracks-if-necessary.js
+   */
+
+  /**
+   * Create text tracks on video.js if they exist on a segment.
+   *
+   * @param {Object} sourceBuffer the VSB or FSB
+   * @param {Object} mediaSource the HTML media source
+   * @param {Object} segment the segment that may contain the text track
+   * @private
+   */
+  var createTextTracksIfNecessary = function createTextTracksIfNecessary(sourceBuffer, mediaSource, segment) {
+    var player = mediaSource.player_;
+
+    // create an in-band caption track if one is present in the segment
+    if (segment.captions && segment.captions.length) {
+      if (!sourceBuffer.inbandTextTracks_) {
+        sourceBuffer.inbandTextTracks_ = {};
+      }
+
+      for (var trackId in segment.captionStreams) {
+        if (!sourceBuffer.inbandTextTracks_[trackId]) {
+          player.tech_.trigger({type: 'usage', name: 'hls-608'});
+          var track = player.textTracks().getTrackById(trackId);
+
+          if (track) {
+            // Resuse an existing track with a CC# id because this was
+            // very likely created by videojs-contrib-hls from information
+            // in the m3u8 for us to use
+            sourceBuffer.inbandTextTracks_[trackId] = track;
+          } else {
+            // Otherwise, create a track with the default `CC#` label and
+            // without a language
+            sourceBuffer.inbandTextTracks_[trackId] = player.addRemoteTextTrack({
+              kind: 'captions',
+              id: trackId,
+              label: trackId
+            }, false).track;
+          }
+        }
+      }
+    }
+
+    if (segment.metadata && segment.metadata.length && !sourceBuffer.metadataTrack_) {
+      sourceBuffer.metadataTrack_ = player.addRemoteTextTrack({
+        kind: 'metadata',
+        label: 'Timed Metadata'
+      }, false).track;
+      sourceBuffer.metadataTrack_.inBandMetadataTrackDispatchType = segment.metadata.dispatchType;
+    }
+  };
+
+  /**
+   * @file remove-cues-from-track.js
+   */
+
+  /**
+   * Remove cues from a track on video.js.
+   *
+   * @param {Double} start start of where we should remove the cue
+   * @param {Double} end end of where the we should remove the cue
+   * @param {Object} track the text track to remove the cues from
+   * @private
+   */
+  var removeCuesFromTrack = function removeCuesFromTrack(start, end, track) {
+    var i = void 0;
+    var cue = void 0;
+
+    if (!track) {
+      return;
+    }
+
+    if (!track.cues) {
+      return;
+    }
+
+    i = track.cues.length;
+
+    while (i--) {
+      cue = track.cues[i];
+
+      // Remove any overlapping cue
+      if (cue.startTime <= end && cue.endTime >= start) {
+        track.removeCue(cue);
+      }
+    }
+  };
+
+  /**
+   * @file add-text-track-data.js
+   */
+  /**
+   * Define properties on a cue for backwards compatability,
+   * but warn the user that the way that they are using it
+   * is depricated and will be removed at a later date.
+   *
+   * @param {Cue} cue the cue to add the properties on
+   * @private
+   */
+  var deprecateOldCue = function deprecateOldCue(cue) {
+    Object.defineProperties(cue.frame, {
+      id: {
+        get: function get$$1() {
+          videojs$1.log.warn('cue.frame.id is deprecated. Use cue.value.key instead.');
+          return cue.value.key;
+        }
+      },
+      value: {
+        get: function get$$1() {
+          videojs$1.log.warn('cue.frame.value is deprecated. Use cue.value.data instead.');
+          return cue.value.data;
+        }
+      },
+      privateData: {
+        get: function get$$1() {
+          videojs$1.log.warn('cue.frame.privateData is deprecated. Use cue.value.data instead.');
+          return cue.value.data;
+        }
+      }
+    });
+  };
+
+  var durationOfVideo = function durationOfVideo(duration) {
+    var dur = void 0;
+
+    if (isNaN(duration) || Math.abs(duration) === Infinity) {
+      dur = Number.MAX_VALUE;
+    } else {
+      dur = duration;
+    }
+    return dur;
+  };
+  /**
+   * Add text track data to a source handler given the captions and
+   * metadata from the buffer.
+   *
+   * @param {Object} sourceHandler the virtual source buffer
+   * @param {Array} captionArray an array of caption data
+   * @param {Array} metadataArray an array of meta data
+   * @private
+   */
+  var addTextTrackData = function addTextTrackData(sourceHandler, captionArray, metadataArray) {
+    var Cue = window_1.WebKitDataCue || window_1.VTTCue;
+
+    if (captionArray) {
+      captionArray.forEach(function (caption) {
+        var track = caption.stream;
+
+        this.inbandTextTracks_[track].addCue(new Cue(caption.startTime + this.timestampOffset, caption.endTime + this.timestampOffset, caption.text));
+      }, sourceHandler);
+    }
+
+    if (metadataArray) {
+      var videoDuration = durationOfVideo(sourceHandler.mediaSource_.duration);
+
+      metadataArray.forEach(function (metadata) {
+        var time = metadata.cueTime + this.timestampOffset;
+
+        metadata.frames.forEach(function (frame) {
+          var cue = new Cue(time, time, frame.value || frame.url || frame.data || '');
+
+          cue.frame = frame;
+          cue.value = frame;
+          deprecateOldCue(cue);
+
+          this.metadataTrack_.addCue(cue);
+        }, this);
+      }, sourceHandler);
+
+      // Updating the metadeta cues so that
+      // the endTime of each cue is the startTime of the next cue
+      // the endTime of last cue is the duration of the video
+      if (sourceHandler.metadataTrack_ && sourceHandler.metadataTrack_.cues && sourceHandler.metadataTrack_.cues.length) {
+        var cues = sourceHandler.metadataTrack_.cues;
+        var cuesArray = [];
+
+        // Create a copy of the TextTrackCueList...
+        // ...disregarding cues with a falsey value
+        for (var i = 0; i < cues.length; i++) {
+          if (cues[i]) {
+            cuesArray.push(cues[i]);
+          }
+        }
+
+        // Group cues by their startTime value
+        var cuesGroupedByStartTime = cuesArray.reduce(function (obj, cue) {
+          var timeSlot = obj[cue.startTime] || [];
+
+          timeSlot.push(cue);
+          obj[cue.startTime] = timeSlot;
+
+          return obj;
+        }, {});
+
+        // Sort startTimes by ascending order
+        var sortedStartTimes = Object.keys(cuesGroupedByStartTime).sort(function (a, b) {
+          return Number(a) - Number(b);
+        });
+
+        // Map each cue group's endTime to the next group's startTime
+        sortedStartTimes.forEach(function (startTime, idx) {
+          var cueGroup = cuesGroupedByStartTime[startTime];
+          var nextTime = Number(sortedStartTimes[idx + 1]) || videoDuration;
+
+          // Map each cue's endTime the next group's startTime
+          cueGroup.forEach(function (cue) {
+            cue.endTime = nextTime;
+          });
+        });
+      }
+    }
+  };
+
+  var win$1 = typeof window !== 'undefined' ? window : {},
+    TARGET = typeof Symbol === 'undefined' ? '__target' : Symbol(),
+    SCRIPT_TYPE = 'application/javascript',
+    BlobBuilder = win$1.BlobBuilder || win$1.WebKitBlobBuilder || win$1.MozBlobBuilder || win$1.MSBlobBuilder,
+    URL = win$1.URL || win$1.webkitURL || URL && URL.msURL,
+    Worker = win$1.Worker;
+
+  /**
+   * Returns a wrapper around Web Worker code that is constructible.
+   *
+   * @function shimWorker
+   *
+   * @param { String }    filename    The name of the file
+   * @param { Function }  fn          Function wrapping the code of the worker
+   */
+  function shimWorker(filename, fn) {
+    return function ShimWorker(forceFallback) {
+      var o = this;
+
+      if (!fn) {
+        return new Worker(filename);
+      } else if (Worker && !forceFallback) {
+        // Convert the function's inner code to a string to construct the worker
+        var source = fn.toString().replace(/^function.+?{/, '').slice(0, -1),
+          objURL = createSourceObject(source);
+
+        this[TARGET] = new Worker(objURL);
+        wrapTerminate(this[TARGET], objURL);
+        return this[TARGET];
+      } else {
+        var selfShim = {
+          postMessage: function postMessage(m) {
+            if (o.onmessage) {
+              setTimeout(function () {
+                o.onmessage({data: m, target: selfShim});
+              });
+            }
+          }
+        };
+
+        fn.call(selfShim);
+        this.postMessage = function (m) {
+          setTimeout(function () {
+            selfShim.onmessage({data: m, target: o});
+          });
+        };
+        this.isThisThread = true;
+      }
+    };
+  }
+
+  // Test Worker capabilities
+  if (Worker) {
+    var testWorker,
+      objURL = createSourceObject('self.onmessage = function () {}'),
+      testArray = new Uint8Array(1);
+
+    try {
+      testWorker = new Worker(objURL);
+
+      // Native browser on some Samsung devices throws for transferables, let's detect it
+      testWorker.postMessage(testArray, [testArray.buffer]);
+    } catch (e) {
+      Worker = null;
+    } finally {
+      URL.revokeObjectURL(objURL);
+      if (testWorker) {
+        testWorker.terminate();
+      }
+    }
+  }
+
+  function createSourceObject(str) {
+    try {
+      return URL.createObjectURL(new Blob([str], {type: SCRIPT_TYPE}));
+    } catch (e) {
+      var blob = new BlobBuilder();
+      blob.append(str);
+      return URL.createObjectURL(blob.getBlob(type));
+    }
+  }
+
+  function wrapTerminate(worker, objURL) {
+    if (!worker || !objURL) return;
+    var term = worker.terminate;
+    worker.objURL = objURL;
+    worker.terminate = function () {
+      if (worker.objURL) URL.revokeObjectURL(worker.objURL);
+      term.call(worker);
+    };
+  }
+
+  var TransmuxWorker = new shimWorker("./transmuxer-worker.worker.js", function (window, document$$1) {
+    var self = this;
+    var transmuxerWorker = function () {
+
+      var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
+
+      var win;
+
+      if (typeof window !== "undefined") {
+        win = window;
+      } else if (typeof commonjsGlobal !== "undefined") {
+        win = commonjsGlobal;
+      } else if (typeof self !== "undefined") {
+        win = self;
+      } else {
+        win = {};
+      }
+
+      var window_1$$1 = win;
+
+      /**
+       * mux.js
+       *
+       * Copyright (c) 2015 Brightcove
+       * All rights reserved.
+       *
+       * Functions that generate fragmented MP4s suitable for use with Media
+       * Source Extensions.
+       */
+
+      var UINT32_MAX = Math.pow(2, 32) - 1;
+
+      var box, dinf, esds, ftyp, mdat, mfhd, minf, moof, moov, mvex, mvhd, trak, tkhd, mdia, mdhd, hdlr, sdtp, stbl,
+        stsd, traf, trex, trun, types, MAJOR_BRAND, MINOR_VERSION, AVC1_BRAND, VIDEO_HDLR, AUDIO_HDLR, HDLR_TYPES, VMHD,
+        SMHD, DREF, STCO, STSC, STSZ, STTS;
+
+      // pre-calculate constants
+      (function () {
+        var i;
+        types = {
+          avc1: [], // codingname
+          avcC: [],
+          btrt: [],
+          dinf: [],
+          dref: [],
+          esds: [],
+          ftyp: [],
+          hdlr: [],
+          mdat: [],
+          mdhd: [],
+          mdia: [],
+          mfhd: [],
+          minf: [],
+          moof: [],
+          moov: [],
+          mp4a: [], // codingname
+          mvex: [],
+          mvhd: [],
+          sdtp: [],
+          smhd: [],
+          stbl: [],
+          stco: [],
+          stsc: [],
+          stsd: [],
+          stsz: [],
+          stts: [],
+          styp: [],
+          tfdt: [],
+          tfhd: [],
+          traf: [],
+          trak: [],
+          trun: [],
+          trex: [],
+          tkhd: [],
+          vmhd: []
+        };
+
+        // In environments where Uint8Array is undefined (e.g., IE8), skip set up so that we
+        // don't throw an error
+        if (typeof Uint8Array === 'undefined') {
+          return;
+        }
+
+        for (i in types) {
+          if (types.hasOwnProperty(i)) {
+            types[i] = [i.charCodeAt(0), i.charCodeAt(1), i.charCodeAt(2), i.charCodeAt(3)];
+          }
+        }
+
+        MAJOR_BRAND = new Uint8Array(['i'.charCodeAt(0), 's'.charCodeAt(0), 'o'.charCodeAt(0), 'm'.charCodeAt(0)]);
+        AVC1_BRAND = new Uint8Array(['a'.charCodeAt(0), 'v'.charCodeAt(0), 'c'.charCodeAt(0), '1'.charCodeAt(0)]);
+        MINOR_VERSION = new Uint8Array([0, 0, 0, 1]);
+        VIDEO_HDLR = new Uint8Array([0x00, // version 0
+          0x00, 0x00, 0x00, // flags
+          0x00, 0x00, 0x00, 0x00, // pre_defined
+          0x76, 0x69, 0x64, 0x65, // handler_type: 'vide'
+          0x00, 0x00, 0x00, 0x00, // reserved
+          0x00, 0x00, 0x00, 0x00, // reserved
+          0x00, 0x00, 0x00, 0x00, // reserved
+          0x56, 0x69, 0x64, 0x65, 0x6f, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'VideoHandler'
+        ]);
+        AUDIO_HDLR = new Uint8Array([0x00, // version 0
+          0x00, 0x00, 0x00, // flags
+          0x00, 0x00, 0x00, 0x00, // pre_defined
+          0x73, 0x6f, 0x75, 0x6e, // handler_type: 'soun'
+          0x00, 0x00, 0x00, 0x00, // reserved
+          0x00, 0x00, 0x00, 0x00, // reserved
+          0x00, 0x00, 0x00, 0x00, // reserved
+          0x53, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'SoundHandler'
+        ]);
+        HDLR_TYPES = {
+          video: VIDEO_HDLR,
+          audio: AUDIO_HDLR
+        };
+        DREF = new Uint8Array([0x00, // version 0
+          0x00, 0x00, 0x00, // flags
+          0x00, 0x00, 0x00, 0x01, // entry_count
+          0x00, 0x00, 0x00, 0x0c, // entry_size
+          0x75, 0x72, 0x6c, 0x20, // 'url' type
+          0x00, // version 0
+          0x00, 0x00, 0x01 // entry_flags
+        ]);
+        SMHD = new Uint8Array([0x00, // version
+          0x00, 0x00, 0x00, // flags
+          0x00, 0x00, // balance, 0 means centered
+          0x00, 0x00 // reserved
+        ]);
+        STCO = new Uint8Array([0x00, // version
+          0x00, 0x00, 0x00, // flags
+          0x00, 0x00, 0x00, 0x00 // entry_count
+        ]);
+        STSC = STCO;
+        STSZ = new Uint8Array([0x00, // version
+          0x00, 0x00, 0x00, // flags
+          0x00, 0x00, 0x00, 0x00, // sample_size
+          0x00, 0x00, 0x00, 0x00 // sample_count
+        ]);
+        STTS = STCO;
+        VMHD = new Uint8Array([0x00, // version
+          0x00, 0x00, 0x01, // flags
+          0x00, 0x00, // graphicsmode
+          0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // opcolor
+        ]);
+      })();
+
+      box = function box(type) {
+        var payload = [],
+          size = 0,
+          i,
+          result,
+          view;
+
+        for (i = 1; i < arguments.length; i++) {
+          payload.push(arguments[i]);
+        }
+
+        i = payload.length;
+
+        // calculate the total size we need to allocate
+        while (i--) {
+          size += payload[i].byteLength;
+        }
+        result = new Uint8Array(size + 8);
+        view = new DataView(result.buffer, result.byteOffset, result.byteLength);
+        view.setUint32(0, result.byteLength);
+        result.set(type, 4);
+
+        // copy the payload into the result
+        for (i = 0, size = 8; i < payload.length; i++) {
+          result.set(payload[i], size);
+          size += payload[i].byteLength;
+        }
+        return result;
+      };
+
+      dinf = function dinf() {
+        return box(types.dinf, box(types.dref, DREF));
+      };
+
+      esds = function esds(track) {
+        return box(types.esds, new Uint8Array([0x00, // version
+          0x00, 0x00, 0x00, // flags
+
+          // ES_Descriptor
+          0x03, // tag, ES_DescrTag
+          0x19, // length
+          0x00, 0x00, // ES_ID
+          0x00, // streamDependenceFlag, URL_flag, reserved, streamPriority
+
+          // DecoderConfigDescriptor
+          0x04, // tag, DecoderConfigDescrTag
+          0x11, // length
+          0x40, // object type
+          0x15, // streamType
+          0x00, 0x06, 0x00, // bufferSizeDB
+          0x00, 0x00, 0xda, 0xc0, // maxBitrate
+          0x00, 0x00, 0xda, 0xc0, // avgBitrate
+
+          // DecoderSpecificInfo
+          0x05, // tag, DecoderSpecificInfoTag
+          0x02, // length
+          // ISO/IEC 14496-3, AudioSpecificConfig
+          // for samplingFrequencyIndex see ISO/IEC 13818-7:2006, 8.1.3.2.2, Table 35
+          track.audioobjecttype << 3 | track.samplingfrequencyindex >>> 1, track.samplingfrequencyindex << 7 | track.channelcount << 3, 0x06, 0x01, 0x02 // GASpecificConfig
+        ]));
+      };
+
+      ftyp = function ftyp() {
+        return box(types.ftyp, MAJOR_BRAND, MINOR_VERSION, MAJOR_BRAND, AVC1_BRAND);
+      };
+
+      hdlr = function hdlr(type) {
+        return box(types.hdlr, HDLR_TYPES[type]);
+      };
+      mdat = function mdat(data) {
+        return box(types.mdat, data);
+      };
+      mdhd = function mdhd(track) {
+        var result = new Uint8Array([0x00, // version 0
+          0x00, 0x00, 0x00, // flags
+          0x00, 0x00, 0x00, 0x02, // creation_time
+          0x00, 0x00, 0x00, 0x03, // modification_time
+          0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second
+
+          track.duration >>> 24 & 0xFF, track.duration >>> 16 & 0xFF, track.duration >>> 8 & 0xFF, track.duration & 0xFF, // duration
+          0x55, 0xc4, // 'und' language (undetermined)
+          0x00, 0x00]);
+
+        // Use the sample rate from the track metadata, when it is
+        // defined. The sample rate can be parsed out of an ADTS header, for
+        // instance.
+        if (track.samplerate) {
+          result[12] = track.samplerate >>> 24 & 0xFF;
+          result[13] = track.samplerate >>> 16 & 0xFF;
+          result[14] = track.samplerate >>> 8 & 0xFF;
+          result[15] = track.samplerate & 0xFF;
+        }
+
+        return box(types.mdhd, result);
+      };
+      mdia = function mdia(track) {
+        return box(types.mdia, mdhd(track), hdlr(track.type), minf(track));
+      };
+      mfhd = function mfhd(sequenceNumber) {
+        return box(types.mfhd, new Uint8Array([0x00, 0x00, 0x00, 0x00, // flags
+          (sequenceNumber & 0xFF000000) >> 24, (sequenceNumber & 0xFF0000) >> 16, (sequenceNumber & 0xFF00) >> 8, sequenceNumber & 0xFF // sequence_number
+        ]));
+      };
+      minf = function minf(track) {
+        return box(types.minf, track.type === 'video' ? box(types.vmhd, VMHD) : box(types.smhd, SMHD), dinf(), stbl(track));
+      };
+      moof = function moof(sequenceNumber, tracks) {
+        var trackFragments = [],
+          i = tracks.length;
+        // build traf boxes for each track fragment
+        while (i--) {
+          trackFragments[i] = traf(tracks[i]);
+        }
+        return box.apply(null, [types.moof, mfhd(sequenceNumber)].concat(trackFragments));
+      };
+      /**
+       * Returns a movie box.
+       * @param tracks {array} the tracks associated with this movie
+       * @see ISO/IEC 14496-12:2012(E), section 8.2.1
+       */
+      moov = function moov(tracks) {
+        var i = tracks.length,
+          boxes = [];
+
+        while (i--) {
+          boxes[i] = trak(tracks[i]);
+        }
+
+        return box.apply(null, [types.moov, mvhd(0xffffffff)].concat(boxes).concat(mvex(tracks)));
+      };
+      mvex = function mvex(tracks) {
+        var i = tracks.length,
+          boxes = [];
+
+        while (i--) {
+          boxes[i] = trex(tracks[i]);
+        }
+        return box.apply(null, [types.mvex].concat(boxes));
+      };
+      mvhd = function mvhd(duration) {
+        var bytes = new Uint8Array([0x00, // version 0
+          0x00, 0x00, 0x00, // flags
+          0x00, 0x00, 0x00, 0x01, // creation_time
+          0x00, 0x00, 0x00, 0x02, // modification_time
+          0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second
+          (duration & 0xFF000000) >> 24, (duration & 0xFF0000) >> 16, (duration & 0xFF00) >> 8, duration & 0xFF, // duration
+          0x00, 0x01, 0x00, 0x00, // 1.0 rate
+          0x01, 0x00, // 1.0 volume
+          0x00, 0x00, // reserved
+          0x00, 0x00, 0x00, 0x00, // reserved
+          0x00, 0x00, 0x00, 0x00, // reserved
+          0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
+          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // pre_defined
+          0xff, 0xff, 0xff, 0xff // next_track_ID
+        ]);
+        return box(types.mvhd, bytes);
+      };
+
+      sdtp = function sdtp(track) {
+        var samples = track.samples || [],
+          bytes = new Uint8Array(4 + samples.length),
+          flags,
+          i;
+
+        // leave the full box header (4 bytes) all zero
+
+        // write the sample table
+        for (i = 0; i < samples.length; i++) {
+          flags = samples[i].flags;
+
+          bytes[i + 4] = flags.dependsOn << 4 | flags.isDependedOn << 2 | flags.hasRedundancy;
+        }
+
+        return box(types.sdtp, bytes);
+      };
+
+      stbl = function stbl(track) {
+        return box(types.stbl, stsd(track), box(types.stts, STTS), box(types.stsc, STSC), box(types.stsz, STSZ), box(types.stco, STCO));
+      };
+
+      (function () {
+        var videoSample, audioSample;
+
+        stsd = function stsd(track) {
+
+          return box(types.stsd, new Uint8Array([0x00, // version 0
+            0x00, 0x00, 0x00, // flags
+            0x00, 0x00, 0x00, 0x01]), track.type === 'video' ? videoSample(track) : audioSample(track));
+        };
+
+        videoSample = function videoSample(track) {
+          var sps = track.sps || [],
+            pps = track.pps || [],
+            sequenceParameterSets = [],
+            pictureParameterSets = [],
+            i;
+
+          // assemble the SPSs
+          for (i = 0; i < sps.length; i++) {
+            sequenceParameterSets.push((sps[i].byteLength & 0xFF00) >>> 8);
+            sequenceParameterSets.push(sps[i].byteLength & 0xFF); // sequenceParameterSetLength
+            sequenceParameterSets = sequenceParameterSets.concat(Array.prototype.slice.call(sps[i])); // SPS
+          }
+
+          // assemble the PPSs
+          for (i = 0; i < pps.length; i++) {
+            pictureParameterSets.push((pps[i].byteLength & 0xFF00) >>> 8);
+            pictureParameterSets.push(pps[i].byteLength & 0xFF);
+            pictureParameterSets = pictureParameterSets.concat(Array.prototype.slice.call(pps[i]));
+          }
+
+          return box(types.avc1, new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
+              0x00, 0x01, // data_reference_index
+              0x00, 0x00, // pre_defined
+              0x00, 0x00, // reserved
+              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // pre_defined
+              (track.width & 0xff00) >> 8, track.width & 0xff, // width
+              (track.height & 0xff00) >> 8, track.height & 0xff, // height
+              0x00, 0x48, 0x00, 0x00, // horizresolution
+              0x00, 0x48, 0x00, 0x00, // vertresolution
+              0x00, 0x00, 0x00, 0x00, // reserved
+              0x00, 0x01, // frame_count
+              0x13, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x6a, 0x73, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x2d, 0x68, 0x6c, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // compressorname
+              0x00, 0x18, // depth = 24
+              0x11, 0x11 // pre_defined = -1
+            ]), box(types.avcC, new Uint8Array([0x01, // configurationVersion
+              track.profileIdc, // AVCProfileIndication
+              track.profileCompatibility, // profile_compatibility
+              track.levelIdc, // AVCLevelIndication
+              0xff // lengthSizeMinusOne, hard-coded to 4 bytes
+            ].concat([sps.length // numOfSequenceParameterSets
+            ]).concat(sequenceParameterSets).concat([pps.length // numOfPictureParameterSets
+            ]).concat(pictureParameterSets))), // "PPS"
+            box(types.btrt, new Uint8Array([0x00, 0x1c, 0x9c, 0x80, // bufferSizeDB
+              0x00, 0x2d, 0xc6, 0xc0, // maxBitrate
+              0x00, 0x2d, 0xc6, 0xc0])) // avgBitrate
+          );
+        };
+
+        audioSample = function audioSample(track) {
+          return box(types.mp4a, new Uint8Array([
+
+            // SampleEntry, ISO/IEC 14496-12
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
+            0x00, 0x01, // data_reference_index
+
+            // AudioSampleEntry, ISO/IEC 14496-12
+            0x00, 0x00, 0x00, 0x00, // reserved
+            0x00, 0x00, 0x00, 0x00, // reserved
+            (track.channelcount & 0xff00) >> 8, track.channelcount & 0xff, // channelcount
+
+            (track.samplesize & 0xff00) >> 8, track.samplesize & 0xff, // samplesize
+            0x00, 0x00, // pre_defined
+            0x00, 0x00, // reserved
+
+            (track.samplerate & 0xff00) >> 8, track.samplerate & 0xff, 0x00, 0x00 // samplerate, 16.16
+
+            // MP4AudioSampleEntry, ISO/IEC 14496-14
+          ]), esds(track));
+        };
+      })();
+
+      tkhd = function tkhd(track) {
+        var result = new Uint8Array([0x00, // version 0
+          0x00, 0x00, 0x07, // flags
+          0x00, 0x00, 0x00, 0x00, // creation_time
+          0x00, 0x00, 0x00, 0x00, // modification_time
+          (track.id & 0xFF000000) >> 24, (track.id & 0xFF0000) >> 16, (track.id & 0xFF00) >> 8, track.id & 0xFF, // track_ID
+          0x00, 0x00, 0x00, 0x00, // reserved
+          (track.duration & 0xFF000000) >> 24, (track.duration & 0xFF0000) >> 16, (track.duration & 0xFF00) >> 8, track.duration & 0xFF, // duration
+          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
+          0x00, 0x00, // layer
+          0x00, 0x00, // alternate_group
+          0x01, 0x00, // non-audio track volume
+          0x00, 0x00, // reserved
+          0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
+          (track.width & 0xFF00) >> 8, track.width & 0xFF, 0x00, 0x00, // width
+          (track.height & 0xFF00) >> 8, track.height & 0xFF, 0x00, 0x00 // height
+        ]);
+
+        return box(types.tkhd, result);
+      };
+
+      /**
+       * Generate a track fragment (traf) box. A traf box collects metadata
+       * about tracks in a movie fragment (moof) box.
+       */
+      traf = function traf(track) {
+        var trackFragmentHeader, trackFragmentDecodeTime, trackFragmentRun, sampleDependencyTable, dataOffset,
+          upperWordBaseMediaDecodeTime, lowerWordBaseMediaDecodeTime;
+
+        trackFragmentHeader = box(types.tfhd, new Uint8Array([0x00, // version 0
+          0x00, 0x00, 0x3a, // flags
+          (track.id & 0xFF000000) >> 24, (track.id & 0xFF0000) >> 16, (track.id & 0xFF00) >> 8, track.id & 0xFF, // track_ID
+          0x00, 0x00, 0x00, 0x01, // sample_description_index
+          0x00, 0x00, 0x00, 0x00, // default_sample_duration
+          0x00, 0x00, 0x00, 0x00, // default_sample_size
+          0x00, 0x00, 0x00, 0x00 // default_sample_flags
+        ]));
+
+        upperWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime / (UINT32_MAX + 1));
+        lowerWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime % (UINT32_MAX + 1));
+
+        trackFragmentDecodeTime = box(types.tfdt, new Uint8Array([0x01, // version 1
+          0x00, 0x00, 0x00, // flags
+          // baseMediaDecodeTime
+          upperWordBaseMediaDecodeTime >>> 24 & 0xFF, upperWordBaseMediaDecodeTime >>> 16 & 0xFF, upperWordBaseMediaDecodeTime >>> 8 & 0xFF, upperWordBaseMediaDecodeTime & 0xFF, lowerWordBaseMediaDecodeTime >>> 24 & 0xFF, lowerWordBaseMediaDecodeTime >>> 16 & 0xFF, lowerWordBaseMediaDecodeTime >>> 8 & 0xFF, lowerWordBaseMediaDecodeTime & 0xFF]));
+
+        // the data offset specifies the number of bytes from the start of
+        // the containing moof to the first payload byte of the associated
+        // mdat
+        dataOffset = 32 + // tfhd
+          20 + // tfdt
+          8 + // traf header
+          16 + // mfhd
+          8 + // moof header
+          8; // mdat header
+
+        // audio tracks require less metadata
+        if (track.type === 'audio') {
+          trackFragmentRun = trun(track, dataOffset);
+          return box(types.traf, trackFragmentHeader, trackFragmentDecodeTime, trackFragmentRun);
+        }
+
+        // video tracks should contain an independent and disposable samples
+        // box (sdtp)
+        // generate one and adjust offsets to match
+        sampleDependencyTable = sdtp(track);
+        trackFragmentRun = trun(track, sampleDependencyTable.length + dataOffset);
+        return box(types.traf, trackFragmentHeader, trackFragmentDecodeTime, trackFragmentRun, sampleDependencyTable);
+      };
+
+      /**
+       * Generate a track box.
+       * @param track {object} a track definition
+       * @return {Uint8Array} the track box
+       */
+      trak = function trak(track) {
+        track.duration = track.duration || 0xffffffff;
+        return box(types.trak, tkhd(track), mdia(track));
+      };
+
+      trex = function trex(track) {
+        var result = new Uint8Array([0x00, // version 0
+          0x00, 0x00, 0x00, // flags
+          (track.id & 0xFF000000) >> 24, (track.id & 0xFF0000) >> 16, (track.id & 0xFF00) >> 8, track.id & 0xFF, // track_ID
+          0x00, 0x00, 0x00, 0x01, // default_sample_description_index
+          0x00, 0x00, 0x00, 0x00, // default_sample_duration
+          0x00, 0x00, 0x00, 0x00, // default_sample_size
+          0x00, 0x01, 0x00, 0x01 // default_sample_flags
+        ]);
+        // the last two bytes of default_sample_flags is the sample
+        // degradation priority, a hint about the importance of this sample
+        // relative to others. Lower the degradation priority for all sample
+        // types other than video.
+        if (track.type !== 'video') {
+          result[result.length - 1] = 0x00;
+        }
+
+        return box(types.trex, result);
+      };
+
+      (function () {
+        var audioTrun, videoTrun, trunHeader;
+
+        // This method assumes all samples are uniform. That is, if a
+        // duration is present for the first sample, it will be present for
+        // all subsequent samples.
+        // see ISO/IEC 14496-12:2012, Section 8.8.8.1
+        trunHeader = function trunHeader(samples, offset) {
+          var durationPresent = 0,
+            sizePresent = 0,
+            flagsPresent = 0,
+            compositionTimeOffset = 0;
+
+          // trun flag constants
+          if (samples.length) {
+            if (samples[0].duration !== undefined) {
+              durationPresent = 0x1;
+            }
+            if (samples[0].size !== undefined) {
+              sizePresent = 0x2;
+            }
+            if (samples[0].flags !== undefined) {
+              flagsPresent = 0x4;
+            }
+            if (samples[0].compositionTimeOffset !== undefined) {
+              compositionTimeOffset = 0x8;
+            }
+          }
+
+          return [0x00, // version 0
+            0x00, durationPresent | sizePresent | flagsPresent | compositionTimeOffset, 0x01, // flags
+            (samples.length & 0xFF000000) >>> 24, (samples.length & 0xFF0000) >>> 16, (samples.length & 0xFF00) >>> 8, samples.length & 0xFF, // sample_count
+            (offset & 0xFF000000) >>> 24, (offset & 0xFF0000) >>> 16, (offset & 0xFF00) >>> 8, offset & 0xFF // data_offset
+          ];
+        };
+
+        videoTrun = function videoTrun(track, offset) {
+          var bytes, samples, sample, i;
+
+          samples = track.samples || [];
+          offset += 8 + 12 + 16 * samples.length;
+
+          bytes = trunHeader(samples, offset);
+
+          for (i = 0; i < samples.length; i++) {
+            sample = samples[i];
+            bytes = bytes.concat([(sample.duration & 0xFF000000) >>> 24, (sample.duration & 0xFF0000) >>> 16, (sample.duration & 0xFF00) >>> 8, sample.duration & 0xFF, // sample_duration
+              (sample.size & 0xFF000000) >>> 24, (sample.size & 0xFF0000) >>> 16, (sample.size & 0xFF00) >>> 8, sample.size & 0xFF, // sample_size
+              sample.flags.isLeading << 2 | sample.flags.dependsOn, sample.flags.isDependedOn << 6 | sample.flags.hasRedundancy << 4 | sample.flags.paddingValue << 1 | sample.flags.isNonSyncSample, sample.flags.degradationPriority & 0xF0 << 8, sample.flags.degradationPriority & 0x0F, // sample_flags
+              (sample.compositionTimeOffset & 0xFF000000) >>> 24, (sample.compositionTimeOffset & 0xFF0000) >>> 16, (sample.compositionTimeOffset & 0xFF00) >>> 8, sample.compositionTimeOffset & 0xFF // sample_composition_time_offset
+            ]);
+          }
+          return box(types.trun, new Uint8Array(bytes));
+        };
+
+        audioTrun = function audioTrun(track, offset) {
+          var bytes, samples, sample, i;
+
+          samples = track.samples || [];
+          offset += 8 + 12 + 8 * samples.length;
+
+          bytes = trunHeader(samples, offset);
+
+          for (i = 0; i < samples.length; i++) {
+            sample = samples[i];
+            bytes = bytes.concat([(sample.duration & 0xFF000000) >>> 24, (sample.duration & 0xFF0000) >>> 16, (sample.duration & 0xFF00) >>> 8, sample.duration & 0xFF, // sample_duration
+              (sample.size & 0xFF000000) >>> 24, (sample.size & 0xFF0000) >>> 16, (sample.size & 0xFF00) >>> 8, sample.size & 0xFF]); // sample_size
+          }
+
+          return box(types.trun, new Uint8Array(bytes));
+        };
+
+        trun = function trun(track, offset) {
+          if (track.type === 'audio') {
+            return audioTrun(track, offset);
+          }
+
+          return videoTrun(track, offset);
+        };
+      })();
+
+      var mp4Generator = {
+        ftyp: ftyp,
+        mdat: mdat,
+        moof: moof,
+        moov: moov,
+        initSegment: function initSegment(tracks) {
+          var fileType = ftyp(),
+            movie = moov(tracks),
+            result;
+
+          result = new Uint8Array(fileType.byteLength + movie.byteLength);
+          result.set(fileType);
+          result.set(movie, fileType.byteLength);
+          return result;
+        }
+      };
+
+      /**
+       * mux.js
+       *
+       * Copyright (c) 2014 Brightcove
+       * All rights reserved.
+       *
+       * A lightweight readable stream implemention that handles event dispatching.
+       * Objects that inherit from streams should call init in their constructors.
+       */
+
+      var Stream = function Stream() {
+        this.init = function () {
+          var listeners = {};
+          /**
+           * Add a listener for a specified event type.
+           * @param type {string} the event name
+           * @param listener {function} the callback to be invoked when an event of
+           * the specified type occurs
+           */
+          this.on = function (type, listener) {
+            if (!listeners[type]) {
+              listeners[type] = [];
+            }
+            listeners[type] = listeners[type].concat(listener);
+          };
+          /**
+           * Remove a listener for a specified event type.
+           * @param type {string} the event name
+           * @param listener {function} a function previously registered for this
+           * type of event through `on`
+           */
+          this.off = function (type, listener) {
+            var index;
+            if (!listeners[type]) {
+              return false;
+            }
+            index = listeners[type].indexOf(listener);
+            listeners[type] = listeners[type].slice();
+            listeners[type].splice(index, 1);
+            return index > -1;
+          };
+          /**
+           * Trigger an event of the specified type on this stream. Any additional
+           * arguments to this function are passed as parameters to event listeners.
+           * @param type {string} the event name
+           */
+          this.trigger = function (type) {
+            var callbacks, i, length, args;
+            callbacks = listeners[type];
+            if (!callbacks) {
+              return;
+            }
+            // Slicing the arguments on every invocation of this method
+            // can add a significant amount of overhead. Avoid the
+            // intermediate object creation for the common case of a
+            // single callback argument
+            if (arguments.length === 2) {
+              length = callbacks.length;
+              for (i = 0; i < length; ++i) {
+                callbacks[i].call(this, arguments[1]);
+              }
+            } else {
+              args = [];
+              i = arguments.length;
+              for (i = 1; i < arguments.length; ++i) {
+                args.push(arguments[i]);
+              }
+              length = callbacks.length;
+              for (i = 0; i < length; ++i) {
+                callbacks[i].apply(this, args);
+              }
+            }
+          };
+          /**
+           * Destroys the stream and cleans up.
+           */
+          this.dispose = function () {
+            listeners = {};
+          };
+        };
+      };
+
+      /**
+       * Forwards all `data` events on this stream to the destination stream. The
+       * destination stream should provide a method `push` to receive the data
+       * events as they arrive.
+       * @param destination {stream} the stream that will receive all `data` events
+       * @param autoFlush {boolean} if false, we will not call `flush` on the destination
+       *                            when the current stream emits a 'done' event
+       * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
+       */
+      Stream.prototype.pipe = function (destination) {
+        this.on('data', function (data) {
+          destination.push(data);
+        });
+
+        this.on('done', function (flushSource) {
+          destination.flush(flushSource);
+        });
+
+        return destination;
+      };
+
+      // Default stream functions that are expected to be overridden to perform
+      // actual work. These are provided by the prototype as a sort of no-op
+      // implementation so that we don't have to check for their existence in the
+      // `pipe` function above.
+      Stream.prototype.push = function (data) {
+        this.trigger('data', data);
+      };
+
+      Stream.prototype.flush = function (flushSource) {
+        this.trigger('done', flushSource);
+      };
+
+      var stream = Stream;
+
+      // -----------------
+      // Link To Transport
+      // -----------------
+
+      // Supplemental enhancement information (SEI) NAL units have a
+      // payload type field to indicate how they are to be
+      // interpreted. CEAS-708 caption content is always transmitted with
+      // payload type 0x04.
+      var USER_DATA_REGISTERED_ITU_T_T35 = 4,
+        RBSP_TRAILING_BITS = 128;
+
+      /**
+       * Parse a supplemental enhancement information (SEI) NAL unit.
+       * Stops parsing once a message of type ITU T T35 has been found.
+       *
+       * @param bytes {Uint8Array} the bytes of a SEI NAL unit
+       * @return {object} the parsed SEI payload
+       * @see Rec. ITU-T H.264, 7.3.2.3.1
+       */
+      var parseSei = function parseSei(bytes) {
+        var i = 0,
+          result = {
+            payloadType: -1,
+            payloadSize: 0
+          },
+          payloadType = 0,
+          payloadSize = 0;
+
+        // go through the sei_rbsp parsing each each individual sei_message
+        while (i < bytes.byteLength) {
+          // stop once we have hit the end of the sei_rbsp
+          if (bytes[i] === RBSP_TRAILING_BITS) {
+            break;
+          }
+
+          // Parse payload type
+          while (bytes[i] === 0xFF) {
+            payloadType += 255;
+            i++;
+          }
+          payloadType += bytes[i++];
+
+          // Parse payload size
+          while (bytes[i] === 0xFF) {
+            payloadSize += 255;
+            i++;
+          }
+          payloadSize += bytes[i++];
+
+          // this sei_message is a 608/708 caption so save it and break
+          // there can only ever be one caption message in a frame's sei
+          if (!result.payload && payloadType === USER_DATA_REGISTERED_ITU_T_T35) {
+            result.payloadType = payloadType;
+            result.payloadSize = payloadSize;
+            result.payload = bytes.subarray(i, i + payloadSize);
+            break;
+          }
+
+          // skip the payload and parse the next message
+          i += payloadSize;
+          payloadType = 0;
+          payloadSize = 0;
+        }
+
+        return result;
+      };
+
+      // see ANSI/SCTE 128-1 (2013), section 8.1
+      var parseUserData = function parseUserData(sei) {
+        // itu_t_t35_contry_code must be 181 (United States) for
+        // captions
+        if (sei.payload[0] !== 181) {
+          return null;
+        }
+
+        // itu_t_t35_provider_code should be 49 (ATSC) for captions
+        if ((sei.payload[1] << 8 | sei.payload[2]) !== 49) {
+          return null;
+        }
+
+        // the user_identifier should be "GA94" to indicate ATSC1 data
+        if (String.fromCharCode(sei.payload[3], sei.payload[4], sei.payload[5], sei.payload[6]) !== 'GA94') {
+          return null;
+        }
+
+        // finally, user_data_type_code should be 0x03 for caption data
+        if (sei.payload[7] !== 0x03) {
+          return null;
+        }
+
+        // return the user_data_type_structure and strip the trailing
+        // marker bits
+        return sei.payload.subarray(8, sei.payload.length - 1);
+      };
+
+      // see CEA-708-D, section 4.4
+      var parseCaptionPackets = function parseCaptionPackets(pts, userData) {
+        var results = [],
+          i,
+          count,
+          offset,
+          data;
+
+        // if this is just filler, return immediately
+        if (!(userData[0] & 0x40)) {
+          return results;
+        }
+
+        // parse out the cc_data_1 and cc_data_2 fields
+        count = userData[0] & 0x1f;
+        for (i = 0; i < count; i++) {
+          offset = i * 3;
+          data = {
+            type: userData[offset + 2] & 0x03,
+            pts: pts
+          };
+
+          // capture cc data when cc_valid is 1
+          if (userData[offset + 2] & 0x04) {
+            data.ccData = userData[offset + 3] << 8 | userData[offset + 4];
+            results.push(data);
+          }
+        }
+        return results;
+      };
+
+      var CaptionStream = function CaptionStream() {
+
+        CaptionStream.prototype.init.call(this);
+
+        this.captionPackets_ = [];
+
+        this.ccStreams_ = [new Cea608Stream(0, 0), // eslint-disable-line no-use-before-define
+          new Cea608Stream(0, 1), // eslint-disable-line no-use-before-define
+          new Cea608Stream(1, 0), // eslint-disable-line no-use-before-define
+          new Cea608Stream(1, 1) // eslint-disable-line no-use-before-define
+        ];
+
+        this.reset();
+
+        // forward data and done events from CCs to this CaptionStream
+        this.ccStreams_.forEach(function (cc) {
+          cc.on('data', this.trigger.bind(this, 'data'));
+          cc.on('done', this.trigger.bind(this, 'done'));
+        }, this);
+      };
+
+      CaptionStream.prototype = new stream();
+      CaptionStream.prototype.push = function (event) {
+        var sei, userData;
+
+        // only examine SEI NALs
+        if (event.nalUnitType !== 'sei_rbsp') {
+          return;
+        }
+
+        // parse the sei
+        sei = parseSei(event.escapedRBSP);
+
+        // ignore everything but user_data_registered_itu_t_t35
+        if (sei.payloadType !== USER_DATA_REGISTERED_ITU_T_T35) {
+          return;
+        }
+
+        // parse out the user data payload
+        userData = parseUserData(sei);
+
+        // ignore unrecognized userData
+        if (!userData) {
+          return;
+        }
+
+        // Sometimes, the same segment # will be downloaded twice. To stop the
+        // caption data from being processed twice, we track the latest dts we've
+        // received and ignore everything with a dts before that. However, since
+        // data for a specific dts can be split across packets on either side of
+        // a segment boundary, we need to make sure we *don't* ignore the packets
+        // from the *next* segment that have dts === this.latestDts_. By constantly
+        // tracking the number of packets received with dts === this.latestDts_, we
+        // know how many should be ignored once we start receiving duplicates.
+        if (event.dts < this.latestDts_) {
+          // We've started getting older data, so set the flag.
+          this.ignoreNextEqualDts_ = true;
+          return;
+        } else if (event.dts === this.latestDts_ && this.ignoreNextEqualDts_) {
+          this.numSameDts_--;
+          if (!this.numSameDts_) {
+            // We've received the last duplicate packet, time to start processing again
+            this.ignoreNextEqualDts_ = false;
+          }
+          return;
+        }
+
+        // parse out CC data packets and save them for later
+        this.captionPackets_ = this.captionPackets_.concat(parseCaptionPackets(event.pts, userData));
+        if (this.latestDts_ !== event.dts) {
+          this.numSameDts_ = 0;
+        }
+        this.numSameDts_++;
+        this.latestDts_ = event.dts;
+      };
+
+      CaptionStream.prototype.flush = function () {
+        // make sure we actually parsed captions before proceeding
+        if (!this.captionPackets_.length) {
+          this.ccStreams_.forEach(function (cc) {
+            cc.flush();
+          }, this);
+          return;
+        }
+
+        // In Chrome, the Array#sort function is not stable so add a
+        // presortIndex that we can use to ensure we get a stable-sort
+        this.captionPackets_.forEach(function (elem, idx) {
+          elem.presortIndex = idx;
+        });
+
+        // sort caption byte-pairs based on their PTS values
+        this.captionPackets_.sort(function (a, b) {
+          if (a.pts === b.pts) {
+            return a.presortIndex - b.presortIndex;
+          }
+          return a.pts - b.pts;
+        });
+
+        this.captionPackets_.forEach(function (packet) {
+          if (packet.type < 2) {
+            // Dispatch packet to the right Cea608Stream
+            this.dispatchCea608Packet(packet);
+          }
+          // this is where an 'else' would go for a dispatching packets
+          // to a theoretical Cea708Stream that handles SERVICEn data
+        }, this);
+
+        this.captionPackets_.length = 0;
+        this.ccStreams_.forEach(function (cc) {
+          cc.flush();
+        }, this);
+        return;
+      };
+
+      CaptionStream.prototype.reset = function () {
+        this.latestDts_ = null;
+        this.ignoreNextEqualDts_ = false;
+        this.numSameDts_ = 0;
+        this.activeCea608Channel_ = [null, null];
+        this.ccStreams_.forEach(function (ccStream) {
+          ccStream.reset();
+        });
+      };
+
+      CaptionStream.prototype.dispatchCea608Packet = function (packet) {
+        // NOTE: packet.type is the CEA608 field
+        if (this.setsChannel1Active(packet)) {
+          this.activeCea608Channel_[packet.type] = 0;
+        } else if (this.setsChannel2Active(packet)) {
+          this.activeCea608Channel_[packet.type] = 1;
+        }
+        if (this.activeCea608Channel_[packet.type] === null) {
+          // If we haven't received anything to set the active channel, discard the
+          // data; we don't want jumbled captions
+          return;
+        }
+        this.ccStreams_[(packet.type << 1) + this.activeCea608Channel_[packet.type]].push(packet);
+      };
+
+      CaptionStream.prototype.setsChannel1Active = function (packet) {
+        return (packet.ccData & 0x7800) === 0x1000;
+      };
+      CaptionStream.prototype.setsChannel2Active = function (packet) {
+        return (packet.ccData & 0x7800) === 0x1800;
+      };
+
+      // ----------------------
+      // Session to Application
+      // ----------------------
+
+      var CHARACTER_TRANSLATION = {
+        0x2a: 0xe1, // á
+        0x5c: 0xe9, // é
+        0x5e: 0xed, // í
+        0x5f: 0xf3, // ó
+        0x60: 0xfa, // ú
+        0x7b: 0xe7, // ç
+        0x7c: 0xf7, // ÷
+        0x7d: 0xd1, // Ñ
+        0x7e: 0xf1, // ñ
+        0x7f: 0x2588, // â–ˆ
+        0x0130: 0xae, // ®
+        0x0131: 0xb0, // °
+        0x0132: 0xbd, // ½
+        0x0133: 0xbf, // ¿
+        0x0134: 0x2122, // â„¢
+        0x0135: 0xa2, // ¢
+        0x0136: 0xa3, // £
+        0x0137: 0x266a, // ♪
+        0x0138: 0xe0, // à
+        0x0139: 0xa0, //
+        0x013a: 0xe8, // è
+        0x013b: 0xe2, // â
+        0x013c: 0xea, // ê
+        0x013d: 0xee, // î
+        0x013e: 0xf4, // ô
+        0x013f: 0xfb, // û
+        0x0220: 0xc1, // Á
+        0x0221: 0xc9, // É
+        0x0222: 0xd3, // Ó
+        0x0223: 0xda, // Ú
+        0x0224: 0xdc, // Ü
+        0x0225: 0xfc, // ü
+        0x0226: 0x2018, // ‘
+        0x0227: 0xa1, // ¡
+        0x0228: 0x2a, // *
+        0x0229: 0x27, // '
+        0x022a: 0x2014, // —
+        0x022b: 0xa9, // ©
+        0x022c: 0x2120, // â„ 
+        0x022d: 0x2022, // •
+        0x022e: 0x201c, // “
+        0x022f: 0x201d, // ”
+        0x0230: 0xc0, // À
+        0x0231: 0xc2, // Â
+        0x0232: 0xc7, // Ç
+        0x0233: 0xc8, // È
+        0x0234: 0xca, // Ê
+        0x0235: 0xcb, // Ë
+        0x0236: 0xeb, // ë
+        0x0237: 0xce, // ÃŽ
+        0x0238: 0xcf, // Ï
+        0x0239: 0xef, // ï
+        0x023a: 0xd4, // Ô
+        0x023b: 0xd9, // Ù
+        0x023c: 0xf9, // ù
+        0x023d: 0xdb, // Û
+        0x023e: 0xab, // «
+        0x023f: 0xbb, // »
+        0x0320: 0xc3, // Ã
+        0x0321: 0xe3, // ã
+        0x0322: 0xcd, // Í
+        0x0323: 0xcc, // Ì
+        0x0324: 0xec, // ì
+        0x0325: 0xd2, // Ã’
+        0x0326: 0xf2, // ò
+        0x0327: 0xd5, // Õ
+        0x0328: 0xf5, // õ
+        0x0329: 0x7b, // {
+        0x032a: 0x7d, // }
+        0x032b: 0x5c, // \
+        0x032c: 0x5e, // ^
+        0x032d: 0x5f, // _
+        0x032e: 0x7c, // |
+        0x032f: 0x7e, // ~
+        0x0330: 0xc4, // Ä
+        0x0331: 0xe4, // ä
+        0x0332: 0xd6, // Ö
+        0x0333: 0xf6, // ö
+        0x0334: 0xdf, // ß
+        0x0335: 0xa5, // ¥
+        0x0336: 0xa4, // ¤
+        0x0337: 0x2502, // │
+        0x0338: 0xc5, // Ã…
+        0x0339: 0xe5, // å
+        0x033a: 0xd8, // Ø
+        0x033b: 0xf8, // ø
+        0x033c: 0x250c, // ┌
+        0x033d: 0x2510, // ┐
+        0x033e: 0x2514, // â””
+        0x033f: 0x2518 // ┘
+      };
+
+      var getCharFromCode = function getCharFromCode(code) {
+        if (code === null) {
+          return '';
+        }
+        code = CHARACTER_TRANSLATION[code] || code;
+        return String.fromCharCode(code);
+      };
+
+      // the index of the last row in a CEA-608 display buffer
+      var BOTTOM_ROW = 14;
+
+      // This array is used for mapping PACs -> row #, since there's no way of
+      // getting it through bit logic.
+      var ROWS = [0x1100, 0x1120, 0x1200, 0x1220, 0x1500, 0x1520, 0x1600, 0x1620, 0x1700, 0x1720, 0x1000, 0x1300, 0x1320, 0x1400, 0x1420];
+
+      // CEA-608 captions are rendered onto a 34x15 matrix of character
+      // cells. The "bottom" row is the last element in the outer array.
+      var createDisplayBuffer = function createDisplayBuffer() {
+        var result = [],
+          i = BOTTOM_ROW + 1;
+        while (i--) {
+          result.push('');
+        }
+        return result;
+      };
+
+      var Cea608Stream = function Cea608Stream(field, dataChannel) {
+        Cea608Stream.prototype.init.call(this);
+
+        this.field_ = field || 0;
+        this.dataChannel_ = dataChannel || 0;
+
+        this.name_ = 'CC' + ((this.field_ << 1 | this.dataChannel_) + 1);
+
+        this.setConstants();
+        this.reset();
+
+        this.push = function (packet) {
+          var data, swap, char0, char1, text;
+          // remove the parity bits
+          data = packet.ccData & 0x7f7f;
+
+          // ignore duplicate control codes; the spec demands they're sent twice
+          if (data === this.lastControlCode_) {
+            this.lastControlCode_ = null;
+            return;
+          }
+
+          // Store control codes
+          if ((data & 0xf000) === 0x1000) {
+            this.lastControlCode_ = data;
+          } else if (data !== this.PADDING_) {
+            this.lastControlCode_ = null;
+          }
+
+          char0 = data >>> 8;
+          char1 = data & 0xff;
+
+          if (data === this.PADDING_) {
+            return;
+          } else if (data === this.RESUME_CAPTION_LOADING_) {
+            this.mode_ = 'popOn';
+          } else if (data === this.END_OF_CAPTION_) {
+            this.clearFormatting(packet.pts);
+            // if a caption was being displayed, it's gone now
+            this.flushDisplayed(packet.pts);
+
+            // flip memory
+            swap = this.displayed_;
+            this.displayed_ = this.nonDisplayed_;
+            this.nonDisplayed_ = swap;
+
+            // start measuring the time to display the caption
+            this.startPts_ = packet.pts;
+          } else if (data === this.ROLL_UP_2_ROWS_) {
+            this.topRow_ = BOTTOM_ROW - 1;
+            this.mode_ = 'rollUp';
+          } else if (data === this.ROLL_UP_3_ROWS_) {
+            this.topRow_ = BOTTOM_ROW - 2;
+            this.mode_ = 'rollUp';
+          } else if (data === this.ROLL_UP_4_ROWS_) {
+            this.topRow_ = BOTTOM_ROW - 3;
+            this.mode_ = 'rollUp';
+          } else if (data === this.CARRIAGE_RETURN_) {
+            this.clearFormatting(packet.pts);
+            this.flushDisplayed(packet.pts);
+            this.shiftRowsUp_();
+            this.startPts_ = packet.pts;
+          } else if (data === this.BACKSPACE_) {
+            if (this.mode_ === 'popOn') {
+              this.nonDisplayed_[BOTTOM_ROW] = this.nonDisplayed_[BOTTOM_ROW].slice(0, -1);
+            } else {
+              this.displayed_[BOTTOM_ROW] = this.displayed_[BOTTOM_ROW].slice(0, -1);
+            }
+          } else if (data === this.ERASE_DISPLAYED_MEMORY_) {
+            this.flushDisplayed(packet.pts);
+            this.displayed_ = createDisplayBuffer();
+          } else if (data === this.ERASE_NON_DISPLAYED_MEMORY_) {
+            this.nonDisplayed_ = createDisplayBuffer();
+          } else if (data === this.RESUME_DIRECT_CAPTIONING_) {
+            this.mode_ = 'paintOn';
+
+            // Append special characters to caption text
+          } else if (this.isSpecialCharacter(char0, char1)) {
+            // Bitmask char0 so that we can apply character transformations
+            // regardless of field and data channel.
+            // Then byte-shift to the left and OR with char1 so we can pass the
+            // entire character code to `getCharFromCode`.
+            char0 = (char0 & 0x03) << 8;
+            text = getCharFromCode(char0 | char1);
+            this[this.mode_](packet.pts, text);
+            this.column_++;
+
+            // Append extended characters to caption text
+          } else if (this.isExtCharacter(char0, char1)) {
+            // Extended characters always follow their "non-extended" equivalents.
+            // IE if a "è" is desired, you'll always receive "eè"; non-compliant
+            // decoders are supposed to drop the "è", while compliant decoders
+            // backspace the "e" and insert "è".
+
+            // Delete the previous character
+            if (this.mode_ === 'popOn') {
+              this.nonDisplayed_[this.row_] = this.nonDisplayed_[this.row_].slice(0, -1);
+            } else {
+              this.displayed_[BOTTOM_ROW] = this.displayed_[BOTTOM_ROW].slice(0, -1);
+            }
+
+            // Bitmask char0 so that we can apply character transformations
+            // regardless of field and data channel.
+            // Then byte-shift to the left and OR with char1 so we can pass the
+            // entire character code to `getCharFromCode`.
+            char0 = (char0 & 0x03) << 8;
+            text = getCharFromCode(char0 | char1);
+            this[this.mode_](packet.pts, text);
+            this.column_++;
+
+            // Process mid-row codes
+          } else if (this.isMidRowCode(char0, char1)) {
+            // Attributes are not additive, so clear all formatting
+            this.clearFormatting(packet.pts);
+
+            // According to the standard, mid-row codes
+            // should be replaced with spaces, so add one now
+            this[this.mode_](packet.pts, ' ');
+            this.column_++;
+
+            if ((char1 & 0xe) === 0xe) {
+              this.addFormatting(packet.pts, ['i']);
+            }
+
+            if ((char1 & 0x1) === 0x1) {
+              this.addFormatting(packet.pts, ['u']);
+            }
+
+            // Detect offset control codes and adjust cursor
+          } else if (this.isOffsetControlCode(char0, char1)) {
+            // Cursor position is set by indent PAC (see below) in 4-column
+            // increments, with an additional offset code of 1-3 to reach any
+            // of the 32 columns specified by CEA-608. So all we need to do
+            // here is increment the column cursor by the given offset.
+            this.column_ += char1 & 0x03;
+
+            // Detect PACs (Preamble Address Codes)
+          } else if (this.isPAC(char0, char1)) {
+
+            // There's no logic for PAC -> row mapping, so we have to just
+            // find the row code in an array and use its index :(
+            var row = ROWS.indexOf(data & 0x1f20);
+
+            if (row !== this.row_) {
+              // formatting is only persistent for current row
+              this.clearFormatting(packet.pts);
+              this.row_ = row;
+            }
+            // All PACs can apply underline, so detect and apply
+            // (All odd-numbered second bytes set underline)
+            if (char1 & 0x1 && this.formatting_.indexOf('u') === -1) {
+              this.addFormatting(packet.pts, ['u']);
+            }
+
+            if ((data & 0x10) === 0x10) {
+              // We've got an indent level code. Each successive even number
+              // increments the column cursor by 4, so we can get the desired
+              // column position by bit-shifting to the right (to get n/2)
+              // and multiplying by 4.
+              this.column_ = ((data & 0xe) >> 1) * 4;
+            }
+
+            if (this.isColorPAC(char1)) {
+              // it's a color code, though we only support white, which
+              // can be either normal or italicized. white italics can be
+              // either 0x4e or 0x6e depending on the row, so we just
+              // bitwise-and with 0xe to see if italics should be turned on
+              if ((char1 & 0xe) === 0xe) {
+                this.addFormatting(packet.pts, ['i']);
+              }
+            }
+
+            // We have a normal character in char0, and possibly one in char1
+          } else if (this.isNormalChar(char0)) {
+            if (char1 === 0x00) {
+              char1 = null;
+            }
+            text = getCharFromCode(char0);
+            text += getCharFromCode(char1);
+            this[this.mode_](packet.pts, text);
+            this.column_ += text.length;
+          } // finish data processing
+        };
+      };
+      Cea608Stream.prototype = new stream();
+      // Trigger a cue point that captures the current state of the
+      // display buffer
+      Cea608Stream.prototype.flushDisplayed = function (pts) {
+        var content = this.displayed_
+        // remove spaces from the start and end of the string
+          .map(function (row) {
+            return row.trim();
+          })
+          // combine all text rows to display in one cue
+          .join('\n')
+          // and remove blank rows from the start and end, but not the middle
+          .replace(/^\n+|\n+$/g, '');
+
+        if (content.length) {
+          this.trigger('data', {
+            startPts: this.startPts_,
+            endPts: pts,
+            text: content,
+            stream: this.name_
+          });
+        }
+      };
+
+      /**
+       * Zero out the data, used for startup and on seek
+       */
+      Cea608Stream.prototype.reset = function () {
+        this.mode_ = 'popOn';
+        // When in roll-up mode, the index of the last row that will
+        // actually display captions. If a caption is shifted to a row
+        // with a lower index than this, it is cleared from the display
+        // buffer
+        this.topRow_ = 0;
+        this.startPts_ = 0;
+        this.displayed_ = createDisplayBuffer();
+        this.nonDisplayed_ = createDisplayBuffer();
+        this.lastControlCode_ = null;
+
+        // Track row and column for proper line-breaking and spacing
+        this.column_ = 0;
+        this.row_ = BOTTOM_ROW;
+
+        // This variable holds currently-applied formatting
+        this.formatting_ = [];
+      };
+
+      /**
+       * Sets up control code and related constants for this instance
+       */
+      Cea608Stream.prototype.setConstants = function () {
+        // The following attributes have these uses:
+        // ext_ :    char0 for mid-row codes, and the base for extended
+        //           chars (ext_+0, ext_+1, and ext_+2 are char0s for
+        //           extended codes)
+        // control_: char0 for control codes, except byte-shifted to the
+        //           left so that we can do this.control_ | CONTROL_CODE
+        // offset_:  char0 for tab offset codes
+        //
+        // It's also worth noting that control codes, and _only_ control codes,
+        // differ between field 1 and field2. Field 2 control codes are always
+        // their field 1 value plus 1. That's why there's the "| field" on the
+        // control value.
+        if (this.dataChannel_ === 0) {
+          this.BASE_ = 0x10;
+          this.EXT_ = 0x11;
+          this.CONTROL_ = (0x14 | this.field_) << 8;
+          this.OFFSET_ = 0x17;
+        } else if (this.dataChannel_ === 1) {
+          this.BASE_ = 0x18;
+          this.EXT_ = 0x19;
+          this.CONTROL_ = (0x1c | this.field_) << 8;
+          this.OFFSET_ = 0x1f;
+        }
+
+        // Constants for the LSByte command codes recognized by Cea608Stream. This
+        // list is not exhaustive. For a more comprehensive listing and semantics see
+        // http://www.gpo.gov/fdsys/pkg/CFR-2010-title47-vol1/pdf/CFR-2010-title47-vol1-sec15-119.pdf
+        // Padding
+        this.PADDING_ = 0x0000;
+        // Pop-on Mode
+        this.RESUME_CAPTION_LOADING_ = this.CONTROL_ | 0x20;
+        this.END_OF_CAPTION_ = this.CONTROL_ | 0x2f;
+        // Roll-up Mode
+        this.ROLL_UP_2_ROWS_ = this.CONTROL_ | 0x25;
+        this.ROLL_UP_3_ROWS_ = this.CONTROL_ | 0x26;
+        this.ROLL_UP_4_ROWS_ = this.CONTROL_ | 0x27;
+        this.CARRIAGE_RETURN_ = this.CONTROL_ | 0x2d;
+        // paint-on mode (not supported)
+        this.RESUME_DIRECT_CAPTIONING_ = this.CONTROL_ | 0x29;
+        // Erasure
+        this.BACKSPACE_ = this.CONTROL_ | 0x21;
+        this.ERASE_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2c;
+        this.ERASE_NON_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2e;
+      };
+
+      /**
+       * Detects if the 2-byte packet data is a special character
+       *
+       * Special characters have a second byte in the range 0x30 to 0x3f,
+       * with the first byte being 0x11 (for data channel 1) or 0x19 (for
+       * data channel 2).
+       *
+       * @param  {Integer} char0 The first byte
+       * @param  {Integer} char1 The second byte
+       * @return {Boolean}       Whether the 2 bytes are an special character
+       */
+      Cea608Stream.prototype.isSpecialCharacter = function (char0, char1) {
+        return char0 === this.EXT_ && char1 >= 0x30 && char1 <= 0x3f;
+      };
+
+      /**
+       * Detects if the 2-byte packet data is an extended character
+       *
+       * Extended characters have a second byte in the range 0x20 to 0x3f,
+       * with the first byte being 0x12 or 0x13 (for data channel 1) or
+       * 0x1a or 0x1b (for data channel 2).
+       *
+       * @param  {Integer} char0 The first byte
+       * @param  {Integer} char1 The second byte
+       * @return {Boolean}       Whether the 2 bytes are an extended character
+       */
+      Cea608Stream.prototype.isExtCharacter = function (char0, char1) {
+        return (char0 === this.EXT_ + 1 || char0 === this.EXT_ + 2) && char1 >= 0x20 && char1 <= 0x3f;
+      };
+
+      /**
+       * Detects if the 2-byte packet is a mid-row code
+       *
+       * Mid-row codes have a second byte in the range 0x20 to 0x2f, with
+       * the first byte being 0x11 (for data channel 1) or 0x19 (for data
+       * channel 2).
+       *
+       * @param  {Integer} char0 The first byte
+       * @param  {Integer} char1 The second byte
+       * @return {Boolean}       Whether the 2 bytes are a mid-row code
+       */
+      Cea608Stream.prototype.isMidRowCode = function (char0, char1) {
+        return char0 === this.EXT_ && char1 >= 0x20 && char1 <= 0x2f;
+      };
+
+      /**
+       * Detects if the 2-byte packet is an offset control code
+       *
+       * Offset control codes have a second byte in the range 0x21 to 0x23,
+       * with the first byte being 0x17 (for data channel 1) or 0x1f (for
+       * data channel 2).
+       *
+       * @param  {Integer} char0 The first byte
+       * @param  {Integer} char1 The second byte
+       * @return {Boolean}       Whether the 2 bytes are an offset control code
+       */
+      Cea608Stream.prototype.isOffsetControlCode = function (char0, char1) {
+        return char0 === this.OFFSET_ && char1 >= 0x21 && char1 <= 0x23;
+      };
+
+      /**
+       * Detects if the 2-byte packet is a Preamble Address Code
+       *
+       * PACs have a first byte in the range 0x10 to 0x17 (for data channel 1)
+       * or 0x18 to 0x1f (for data channel 2), with the second byte in the
+       * range 0x40 to 0x7f.
+       *
+       * @param  {Integer} char0 The first byte
+       * @param  {Integer} char1 The second byte
+       * @return {Boolean}       Whether the 2 bytes are a PAC
+       */
+      Cea608Stream.prototype.isPAC = function (char0, char1) {
+        return char0 >= this.BASE_ && char0 < this.BASE_ + 8 && char1 >= 0x40 && char1 <= 0x7f;
+      };
+
+      /**
+       * Detects if a packet's second byte is in the range of a PAC color code
+       *
+       * PAC color codes have the second byte be in the range 0x40 to 0x4f, or
+       * 0x60 to 0x6f.
+       *
+       * @param  {Integer} char1 The second byte
+       * @return {Boolean}       Whether the byte is a color PAC
+       */
+      Cea608Stream.prototype.isColorPAC = function (char1) {
+        return char1 >= 0x40 && char1 <= 0x4f || char1 >= 0x60 && char1 <= 0x7f;
+      };
+
+      /**
+       * Detects if a single byte is in the range of a normal character
+       *
+       * Normal text bytes are in the range 0x20 to 0x7f.
+       *
+       * @param  {Integer} char  The byte
+       * @return {Boolean}       Whether the byte is a normal character
+       */
+      Cea608Stream.prototype.isNormalChar = function (char) {
+        return char >= 0x20 && char <= 0x7f;
+      };
+
+      // Adds the opening HTML tag for the passed character to the caption text,
+      // and keeps track of it for later closing
+      Cea608Stream.prototype.addFormatting = function (pts, format) {
+        this.formatting_ = this.formatting_.concat(format);
+        var text = format.reduce(function (text, format) {
+          return text + '<' + format + '>';
+        }, '');
+        this[this.mode_](pts, text);
+      };
+
+      // Adds HTML closing tags for current formatting to caption text and
+      // clears remembered formatting
+      Cea608Stream.prototype.clearFormatting = function (pts) {
+        if (!this.formatting_.length) {
+          return;
+        }
+        var text = this.formatting_.reverse().reduce(function (text, format) {
+          return text + '</' + format + '>';
+        }, '');
+        this.formatting_ = [];
+        this[this.mode_](pts, text);
+      };
+
+      // Mode Implementations
+      Cea608Stream.prototype.popOn = function (pts, text) {
+        var baseRow = this.nonDisplayed_[this.row_];
+
+        // buffer characters
+        baseRow += text;
+        this.nonDisplayed_[this.row_] = baseRow;
+      };
+
+      Cea608Stream.prototype.rollUp = function (pts, text) {
+        var baseRow = this.displayed_[BOTTOM_ROW];
+
+        baseRow += text;
+        this.displayed_[BOTTOM_ROW] = baseRow;
+      };
+
+      Cea608Stream.prototype.shiftRowsUp_ = function () {
+        var i;
+        // clear out inactive rows
+        for (i = 0; i < this.topRow_; i++) {
+          this.displayed_[i] = '';
+        }
+        // shift displayed rows up
+        for (i = this.topRow_; i < BOTTOM_ROW; i++) {
+          this.displayed_[i] = this.displayed_[i + 1];
+        }
+        // clear out the bottom row
+        this.displayed_[BOTTOM_ROW] = '';
+      };
+
+      // paintOn mode is not implemented
+      Cea608Stream.prototype.paintOn = function () {
+      };
+
+      // exports
+      var captionStream = {
+        CaptionStream: CaptionStream,
+        Cea608Stream: Cea608Stream
+      };
+
+      var streamTypes = {
+        H264_STREAM_TYPE: 0x1B,
+        ADTS_STREAM_TYPE: 0x0F,
+        METADATA_STREAM_TYPE: 0x15
+      };
+
+      var MAX_TS = 8589934592;
+
+      var RO_THRESH = 4294967296;
+
+      var handleRollover = function handleRollover(value, reference) {
+        var direction = 1;
+
+        if (value > reference) {
+          // If the current timestamp value is greater than our reference timestamp and we detect a
+          // timestamp rollover, this means the roll over is happening in the opposite direction.
+          // Example scenario: Enter a long stream/video just after a rollover occurred. The reference
+          // point will be set to a small number, e.g. 1. The user then seeks backwards over the
+          // rollover point. In loading this segment, the timestamp values will be very large,
+          // e.g. 2^33 - 1. Since this comes before the data we loaded previously, we want to adjust
+          // the time stamp to be `value - 2^33`.
+          direction = -1;
+        }
+
+        // Note: A seek forwards or back that is greater than the RO_THRESH (2^32, ~13 hours) will
+        // cause an incorrect adjustment.
+        while (Math.abs(reference - value) > RO_THRESH) {
+          value += direction * MAX_TS;
+        }
+
+        return value;
+      };
+
+      var TimestampRolloverStream = function TimestampRolloverStream(type) {
+        var lastDTS, referenceDTS;
+
+        TimestampRolloverStream.prototype.init.call(this);
+
+        this.type_ = type;
+
+        this.push = function (data) {
+          if (data.type !== this.type_) {
+            return;
+          }
+
+          if (referenceDTS === undefined) {
+            referenceDTS = data.dts;
+          }
+
+          data.dts = handleRollover(data.dts, referenceDTS);
+          data.pts = handleRollover(data.pts, referenceDTS);
+
+          lastDTS = data.dts;
+
+          this.trigger('data', data);
+        };
+
+        this.flush = function () {
+          referenceDTS = lastDTS;
+          this.trigger('done');
+        };
+
+        this.discontinuity = function () {
+          referenceDTS = void 0;
+          lastDTS = void 0;
+        };
+      };
+
+      TimestampRolloverStream.prototype = new stream();
+
+      var timestampRolloverStream = {
+        TimestampRolloverStream: TimestampRolloverStream,
+        handleRollover: handleRollover
+      };
+
+      var percentEncode = function percentEncode(bytes, start, end) {
+          var i,
+            result = '';
+          for (i = start; i < end; i++) {
+            result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
+          }
+          return result;
+        },
+
+
+        // return the string representation of the specified byte range,
+        // interpreted as UTf-8.
+        parseUtf8 = function parseUtf8(bytes, start, end) {
+          return decodeURIComponent(percentEncode(bytes, start, end));
+        },
+
+
+        // return the string representation of the specified byte range,
+        // interpreted as ISO-8859-1.
+        parseIso88591 = function parseIso88591(bytes, start, end) {
+          return unescape(percentEncode(bytes, start, end)); // jshint ignore:line
+        },
+        parseSyncSafeInteger = function parseSyncSafeInteger(data) {
+          return data[0] << 21 | data[1] << 14 | data[2] << 7 | data[3];
+        },
+        tagParsers = {
+          TXXX: function TXXX(tag) {
+            var i;
+            if (tag.data[0] !== 3) {
+              // ignore frames with unrecognized character encodings
+              return;
+            }
+
+            for (i = 1; i < tag.data.length; i++) {
+              if (tag.data[i] === 0) {
+                // parse the text fields
+                tag.description = parseUtf8(tag.data, 1, i);
+                // do not include the null terminator in the tag value
+                tag.value = parseUtf8(tag.data, i + 1, tag.data.length).replace(/\0*$/, '');
+                break;
+              }
+            }
+            tag.data = tag.value;
+          },
+          WXXX: function WXXX(tag) {
+            var i;
+            if (tag.data[0] !== 3) {
+              // ignore frames with unrecognized character encodings
+              return;
+            }
+
+            for (i = 1; i < tag.data.length; i++) {
+              if (tag.data[i] === 0) {
+                // parse the description and URL fields
+                tag.description = parseUtf8(tag.data, 1, i);
+                tag.url = parseUtf8(tag.data, i + 1, tag.data.length);
+                break;
+              }
+            }
+          },
+          PRIV: function PRIV(tag) {
+            var i;
+
+            for (i = 0; i < tag.data.length; i++) {
+              if (tag.data[i] === 0) {
+                // parse the description and URL fields
+                tag.owner = parseIso88591(tag.data, 0, i);
+                break;
+              }
+            }
+            tag.privateData = tag.data.subarray(i + 1);
+            tag.data = tag.privateData;
+          }
+        },
+        _MetadataStream;
+
+      _MetadataStream = function MetadataStream(options) {
+        var settings = {
+            debug: !!(options && options.debug),
+
+            // the bytes of the program-level descriptor field in MP2T
+            // see ISO/IEC 13818-1:2013 (E), section 2.6 "Program and
+            // program element descriptors"
+            descriptor: options && options.descriptor
+          },
+
+
+          // the total size in bytes of the ID3 tag being parsed
+          tagSize = 0,
+
+
+          // tag data that is not complete enough to be parsed
+          buffer = [],
+
+
+          // the total number of bytes currently in the buffer
+          bufferSize = 0,
+          i;
+
+        _MetadataStream.prototype.init.call(this);
+
+        // calculate the text track in-band metadata track dispatch type
+        // https://html.spec.whatwg.org/multipage/embedded-content.html#steps-to-expose-a-media-resource-specific-text-track
+        this.dispatchType = streamTypes.METADATA_STREAM_TYPE.toString(16);
+        if (settings.descriptor) {
+          for (i = 0; i < settings.descriptor.length; i++) {
+            this.dispatchType += ('00' + settings.descriptor[i].toString(16)).slice(-2);
+          }
+        }
+
+        this.push = function (chunk) {
+          var tag, frameStart, frameSize, frame, i, frameHeader;
+          if (chunk.type !== 'timed-metadata') {
+            return;
+          }
+
+          // if data_alignment_indicator is set in the PES header,
+          // we must have the start of a new ID3 tag. Assume anything
+          // remaining in the buffer was malformed and throw it out
+          if (chunk.dataAlignmentIndicator) {
+            bufferSize = 0;
+            buffer.length = 0;
+          }
+
+          // ignore events that don't look like ID3 data
+          if (buffer.length === 0 && (chunk.data.length < 10 || chunk.data[0] !== 'I'.charCodeAt(0) || chunk.data[1] !== 'D'.charCodeAt(0) || chunk.data[2] !== '3'.charCodeAt(0))) {
+            if (settings.debug) {
+              // eslint-disable-next-line no-console
+              console.log('Skipping unrecognized metadata packet');
+            }
+            return;
+          }
+
+          // add this chunk to the data we've collected so far
+
+          buffer.push(chunk);
+          bufferSize += chunk.data.byteLength;
+
+          // grab the size of the entire frame from the ID3 header
+          if (buffer.length === 1) {
+            // the frame size is transmitted as a 28-bit integer in the
+            // last four bytes of the ID3 header.
+            // The most significant bit of each byte is dropped and the
+            // results concatenated to recover the actual value.
+            tagSize = parseSyncSafeInteger(chunk.data.subarray(6, 10));
+
+            // ID3 reports the tag size excluding the header but it's more
+            // convenient for our comparisons to include it
+            tagSize += 10;
+          }
+
+          // if the entire frame has not arrived, wait for more data
+          if (bufferSize < tagSize) {
+            return;
+          }
+
+          // collect the entire frame so it can be parsed
+          tag = {
+            data: new Uint8Array(tagSize),
+            frames: [],
+            pts: buffer[0].pts,
+            dts: buffer[0].dts
+          };
+          for (i = 0; i < tagSize;) {
+            tag.data.set(buffer[0].data.subarray(0, tagSize - i), i);
+            i += buffer[0].data.byteLength;
+            bufferSize -= buffer[0].data.byteLength;
+            buffer.shift();
+          }
+
+          // find the start of the first frame and the end of the tag
+          frameStart = 10;
+          if (tag.data[5] & 0x40) {
+            // advance the frame start past the extended header
+            frameStart += 4; // header size field
+            frameStart += parseSyncSafeInteger(tag.data.subarray(10, 14));
+
+            // clip any padding off the end
+            tagSize -= parseSyncSafeInteger(tag.data.subarray(16, 20));
+          }
+
+          // parse one or more ID3 frames
+          // http://id3.org/id3v2.3.0#ID3v2_frame_overview
+          do {
+            // determine the number of bytes in this frame
+            frameSize = parseSyncSafeInteger(tag.data.subarray(frameStart + 4, frameStart + 8));
+            if (frameSize < 1) {
+              // eslint-disable-next-line no-console
+              return console.log('Malformed ID3 frame encountered. Skipping metadata parsing.');
+            }
+            frameHeader = String.fromCharCode(tag.data[frameStart], tag.data[frameStart + 1], tag.data[frameStart + 2], tag.data[frameStart + 3]);
+
+            frame = {
+              id: frameHeader,
+              data: tag.data.subarray(frameStart + 10, frameStart + frameSize + 10)
+            };
+            frame.key = frame.id;
+            if (tagParsers[frame.id]) {
+              tagParsers[frame.id](frame);
+
+              // handle the special PRIV frame used to indicate the start
+              // time for raw AAC data
+              if (frame.owner === 'com.apple.streaming.transportStreamTimestamp') {
+                var d = frame.data,
+                  size = (d[3] & 0x01) << 30 | d[4] << 22 | d[5] << 14 | d[6] << 6 | d[7] >>> 2;
+
+                size *= 4;
+                size += d[7] & 0x03;
+                frame.timeStamp = size;
+                // in raw AAC, all subsequent data will be timestamped based
+                // on the value of this frame
+                // we couldn't have known the appropriate pts and dts before
+                // parsing this ID3 tag so set those values now
+                if (tag.pts === undefined && tag.dts === undefined) {
+                  tag.pts = frame.timeStamp;
+                  tag.dts = frame.timeStamp;
+                }
+                this.trigger('timestamp', frame);
+              }
+            }
+            tag.frames.push(frame);
+
+            frameStart += 10; // advance past the frame header
+            frameStart += frameSize; // advance past the frame body
+          } while (frameStart < tagSize);
+          this.trigger('data', tag);
+        };
+      };
+      _MetadataStream.prototype = new stream();
+
+      var metadataStream = _MetadataStream;
+
+      var TimestampRolloverStream$1 = timestampRolloverStream.TimestampRolloverStream;
+
+      // object types
+      var _TransportPacketStream, _TransportParseStream, _ElementaryStream;
+
+      // constants
+      var MP2T_PACKET_LENGTH = 188,
+
+        // bytes
+        SYNC_BYTE = 0x47;
+
+      /**
+       * Splits an incoming stream of binary data into MPEG-2 Transport
+       * Stream packets.
+       */
+      _TransportPacketStream = function TransportPacketStream() {
+        var buffer = new Uint8Array(MP2T_PACKET_LENGTH),
+          bytesInBuffer = 0;
+
+        _TransportPacketStream.prototype.init.call(this);
+
+        // Deliver new bytes to the stream.
+
+        this.push = function (bytes) {
+          var startIndex = 0,
+            endIndex = MP2T_PACKET_LENGTH,
+            everything;
+
+          // If there are bytes remaining from the last segment, prepend them to the
+          // bytes that were pushed in
+          if (bytesInBuffer) {
+            everything = new Uint8Array(bytes.byteLength + bytesInBuffer);
+            everything.set(buffer.subarray(0, bytesInBuffer));
+            everything.set(bytes, bytesInBuffer);
+            bytesInBuffer = 0;
+          } else {
+            everything = bytes;
+          }
+
+          // While we have enough data for a packet
+          while (endIndex < everything.byteLength) {
+            // Look for a pair of start and end sync bytes in the data..
+            if (everything[startIndex] === SYNC_BYTE && everything[endIndex] === SYNC_BYTE) {
+              // We found a packet so emit it and jump one whole packet forward in
+              // the stream
+              this.trigger('data', everything.subarray(startIndex, endIndex));
+              startIndex += MP2T_PACKET_LENGTH;
+              endIndex += MP2T_PACKET_LENGTH;
+              continue;
+            }
+            // If we get here, we have somehow become de-synchronized and we need to step
+            // forward one byte at a time until we find a pair of sync bytes that denote
+            // a packet
+            startIndex++;
+            endIndex++;
+          }
+
+          // If there was some data left over at the end of the segment that couldn't
+          // possibly be a whole packet, keep it because it might be the start of a packet
+          // that continues in the next segment
+          if (startIndex < everything.byteLength) {
+            buffer.set(everything.subarray(startIndex), 0);
+            bytesInBuffer = everything.byteLength - startIndex;
+          }
+        };
+
+        this.flush = function () {
+          // If the buffer contains a whole packet when we are being flushed, emit it
+          // and empty the buffer. Otherwise hold onto the data because it may be
+          // important for decoding the next segment
+          if (bytesInBuffer === MP2T_PACKET_LENGTH && buffer[0] === SYNC_BYTE) {
+            this.trigger('data', buffer);
+            bytesInBuffer = 0;
+          }
+          this.trigger('done');
+        };
+      };
+      _TransportPacketStream.prototype = new stream();
+
+      /**
+       * Accepts an MP2T TransportPacketStream and emits data events with parsed
+       * forms of the individual transport stream packets.
+       */
+      _TransportParseStream = function TransportParseStream() {
+        var parsePsi, parsePat, parsePmt, self;
+        _TransportParseStream.prototype.init.call(this);
+        self = this;
+
+        this.packetsWaitingForPmt = [];
+        this.programMapTable = undefined;
+
+        parsePsi = function parsePsi(payload, psi) {
+          var offset = 0;
+
+          // PSI packets may be split into multiple sections and those
+          // sections may be split into multiple packets. If a PSI
+          // section starts in this packet, the payload_unit_start_indicator
+          // will be true and the first byte of the payload will indicate
+          // the offset from the current position to the start of the
+          // section.
+          if (psi.payloadUnitStartIndicator) {
+            offset += payload[offset] + 1;
+          }
+
+          if (psi.type === 'pat') {
+            parsePat(payload.subarray(offset), psi);
+          } else {
+            parsePmt(payload.subarray(offset), psi);
+          }
+        };
+
+        parsePat = function parsePat(payload, pat) {
+          pat.section_number = payload[7]; // eslint-disable-line camelcase
+          pat.last_section_number = payload[8]; // eslint-disable-line camelcase
+
+          // skip the PSI header and parse the first PMT entry
+          self.pmtPid = (payload[10] & 0x1F) << 8 | payload[11];
+          pat.pmtPid = self.pmtPid;
+        };
+
+        /**
+         * Parse out the relevant fields of a Program Map Table (PMT).
+         * @param payload {Uint8Array} the PMT-specific portion of an MP2T
+         * packet. The first byte in this array should be the table_id
+         * field.
+         * @param pmt {object} the object that should be decorated with
+         * fields parsed from the PMT.
+         */
+        parsePmt = function parsePmt(payload, pmt) {
+          var sectionLength, tableEnd, programInfoLength, offset;
+
+          // PMTs can be sent ahead of the time when they should actually
+          // take effect. We don't believe this should ever be the case
+          // for HLS but we'll ignore "forward" PMT declarations if we see
+          // them. Future PMT declarations have the current_next_indicator
+          // set to zero.
+          if (!(payload[5] & 0x01)) {
+            return;
+          }
+
+          // overwrite any existing program map table
+          self.programMapTable = {
+            video: null,
+            audio: null,
+            'timed-metadata': {}
+          };
+
+          // the mapping table ends at the end of the current section
+          sectionLength = (payload[1] & 0x0f) << 8 | payload[2];
+          tableEnd = 3 + sectionLength - 4;
+
+          // to determine where the table is, we have to figure out how
+          // long the program info descriptors are
+          programInfoLength = (payload[10] & 0x0f) << 8 | payload[11];
+
+          // advance the offset to the first entry in the mapping table
+          offset = 12 + programInfoLength;
+          while (offset < tableEnd) {
+            var streamType = payload[offset];
+            var pid = (payload[offset + 1] & 0x1F) << 8 | payload[offset + 2];
+
+            // only map a single elementary_pid for audio and video stream types
+            // TODO: should this be done for metadata too? for now maintain behavior of
+            //       multiple metadata streams
+            if (streamType === streamTypes.H264_STREAM_TYPE && self.programMapTable.video === null) {
+              self.programMapTable.video = pid;
+            } else if (streamType === streamTypes.ADTS_STREAM_TYPE && self.programMapTable.audio === null) {
+              self.programMapTable.audio = pid;
+            } else if (streamType === streamTypes.METADATA_STREAM_TYPE) {
+              // map pid to stream type for metadata streams
+              self.programMapTable['timed-metadata'][pid] = streamType;
+            }
+
+            // move to the next table entry
+            // skip past the elementary stream descriptors, if present
+            offset += ((payload[offset + 3] & 0x0F) << 8 | payload[offset + 4]) + 5;
+          }
+
+          // record the map on the packet as well
+          pmt.programMapTable = self.programMapTable;
+        };
+
+        /**
+         * Deliver a new MP2T packet to the stream.
+         */
+        this.push = function (packet) {
+          var result = {},
+            offset = 4;
+
+          result.payloadUnitStartIndicator = !!(packet[1] & 0x40);
+
+          // pid is a 13-bit field starting at the last bit of packet[1]
+          result.pid = packet[1] & 0x1f;
+          result.pid <<= 8;
+          result.pid |= packet[2];
+
+          // if an adaption field is present, its length is specified by the
+          // fifth byte of the TS packet header. The adaptation field is
+          // used to add stuffing to PES packets that don't fill a complete
+          // TS packet, and to specify some forms of timing and control data
+          // that we do not currently use.
+          if ((packet[3] & 0x30) >>> 4 > 0x01) {
+            offset += packet[offset] + 1;
+          }
+
+          // parse the rest of the packet based on the type
+          if (result.pid === 0) {
+            result.type = 'pat';
+            parsePsi(packet.subarray(offset), result);
+            this.trigger('data', result);
+          } else if (result.pid === this.pmtPid) {
+            result.type = 'pmt';
+            parsePsi(packet.subarray(offset), result);
+            this.trigger('data', result);
+
+            // if there are any packets waiting for a PMT to be found, process them now
+            while (this.packetsWaitingForPmt.length) {
+              this.processPes_.apply(this, this.packetsWaitingForPmt.shift());
+            }
+          } else if (this.programMapTable === undefined) {
+            // When we have not seen a PMT yet, defer further processing of
+            // PES packets until one has been parsed
+            this.packetsWaitingForPmt.push([packet, offset, result]);
+          } else {
+            this.processPes_(packet, offset, result);
+          }
+        };
+
+        this.processPes_ = function (packet, offset, result) {
+          // set the appropriate stream type
+          if (result.pid === this.programMapTable.video) {
+            result.streamType = streamTypes.H264_STREAM_TYPE;
+          } else if (result.pid === this.programMapTable.audio) {
+            result.streamType = streamTypes.ADTS_STREAM_TYPE;
+          } else {
+            // if not video or audio, it is timed-metadata or unknown
+            // if unknown, streamType will be undefined
+            result.streamType = this.programMapTable['timed-metadata'][result.pid];
+          }
+
+          result.type = 'pes';
+          result.data = packet.subarray(offset);
+
+          this.trigger('data', result);
+        };
+      };
+      _TransportParseStream.prototype = new stream();
+      _TransportParseStream.STREAM_TYPES = {
+        h264: 0x1b,
+        adts: 0x0f
+      };
+
+      /**
+       * Reconsistutes program elementary stream (PES) packets from parsed
+       * transport stream packets. That is, if you pipe an
+       * mp2t.TransportParseStream into a mp2t.ElementaryStream, the output
+       * events will be events which capture the bytes for individual PES
+       * packets plus relevant metadata that has been extracted from the
+       * container.
+       */
+      _ElementaryStream = function ElementaryStream() {
+        var self = this,
+
+
+          // PES packet fragments
+          video = {
+            data: [],
+            size: 0
+          },
+          audio = {
+            data: [],
+            size: 0
+          },
+          timedMetadata = {
+            data: [],
+            size: 0
+          },
+          parsePes = function parsePes(payload, pes) {
+            var ptsDtsFlags;
+
+            // get the packet length, this will be 0 for video
+            pes.packetLength = 6 + (payload[4] << 8 | payload[5]);
+
+            // find out if this packets starts a new keyframe
+            pes.dataAlignmentIndicator = (payload[6] & 0x04) !== 0;
+            // PES packets may be annotated with a PTS value, or a PTS value
+            // and a DTS value. Determine what combination of values is
+            // available to work with.
+            ptsDtsFlags = payload[7];
+
+            // PTS and DTS are normally stored as a 33-bit number.  Javascript
+            // performs all bitwise operations on 32-bit integers but javascript
+            // supports a much greater range (52-bits) of integer using standard
+            // mathematical operations.
+            // We construct a 31-bit value using bitwise operators over the 31
+            // most significant bits and then multiply by 4 (equal to a left-shift
+            // of 2) before we add the final 2 least significant bits of the
+            // timestamp (equal to an OR.)
+            if (ptsDtsFlags & 0xC0) {
+              // the PTS and DTS are not written out directly. For information
+              // on how they are encoded, see
+              // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
+              pes.pts = (payload[9] & 0x0E) << 27 | (payload[10] & 0xFF) << 20 | (payload[11] & 0xFE) << 12 | (payload[12] & 0xFF) << 5 | (payload[13] & 0xFE) >>> 3;
+              pes.pts *= 4; // Left shift by 2
+              pes.pts += (payload[13] & 0x06) >>> 1; // OR by the two LSBs
+              pes.dts = pes.pts;
+              if (ptsDtsFlags & 0x40) {
+                pes.dts = (payload[14] & 0x0E) << 27 | (payload[15] & 0xFF) << 20 | (payload[16] & 0xFE) << 12 | (payload[17] & 0xFF) << 5 | (payload[18] & 0xFE) >>> 3;
+                pes.dts *= 4; // Left shift by 2
+                pes.dts += (payload[18] & 0x06) >>> 1; // OR by the two LSBs
+              }
+            }
+            // the data section starts immediately after the PES header.
+            // pes_header_data_length specifies the number of header bytes
+            // that follow the last byte of the field.
+            pes.data = payload.subarray(9 + payload[8]);
+          },
+          flushStream = function flushStream(stream$$1, type, forceFlush) {
+            var packetData = new Uint8Array(stream$$1.size),
+              event = {
+                type: type
+              },
+              i = 0,
+              offset = 0,
+              packetFlushable = false,
+              fragment;
+
+            // do nothing if there is not enough buffered data for a complete
+            // PES header
+            if (!stream$$1.data.length || stream$$1.size < 9) {
+              return;
+            }
+            event.trackId = stream$$1.data[0].pid;
+
+            // reassemble the packet
+            for (i = 0; i < stream$$1.data.length; i++) {
+              fragment = stream$$1.data[i];
+
+              packetData.set(fragment.data, offset);
+              offset += fragment.data.byteLength;
+            }
+
+            // parse assembled packet's PES header
+            parsePes(packetData, event);
+
+            // non-video PES packets MUST have a non-zero PES_packet_length
+            // check that there is enough stream data to fill the packet
+            packetFlushable = type === 'video' || event.packetLength <= stream$$1.size;
+
+            // flush pending packets if the conditions are right
+            if (forceFlush || packetFlushable) {
+              stream$$1.size = 0;
+              stream$$1.data.length = 0;
+            }
+
+            // only emit packets that are complete. this is to avoid assembling
+            // incomplete PES packets due to poor segmentation
+            if (packetFlushable) {
+              self.trigger('data', event);
+            }
+          };
+
+        _ElementaryStream.prototype.init.call(this);
+
+        this.push = function (data) {
+          ({
+            pat: function pat() {
+              // we have to wait for the PMT to arrive as well before we
+              // have any meaningful metadata
+            },
+            pes: function pes() {
+              var stream$$1, streamType;
+
+              switch (data.streamType) {
+                case streamTypes.H264_STREAM_TYPE:
+                case streamTypes.H264_STREAM_TYPE:
+                  stream$$1 = video;
+                  streamType = 'video';
+                  break;
+                case streamTypes.ADTS_STREAM_TYPE:
+                  stream$$1 = audio;
+                  streamType = 'audio';
+                  break;
+                case streamTypes.METADATA_STREAM_TYPE:
+                  stream$$1 = timedMetadata;
+                  streamType = 'timed-metadata';
+                  break;
+                default:
+                  // ignore unknown stream types
+                  return;
+              }
+
+              // if a new packet is starting, we can flush the completed
+              // packet
+              if (data.payloadUnitStartIndicator) {
+                flushStream(stream$$1, streamType, true);
+              }
+
+              // buffer this fragment until we are sure we've received the
+              // complete payload
+              stream$$1.data.push(data);
+              stream$$1.size += data.data.byteLength;
+            },
+            pmt: function pmt() {
+              var event = {
+                  type: 'metadata',
+                  tracks: []
+                },
+                programMapTable = data.programMapTable;
+
+              // translate audio and video streams to tracks
+              if (programMapTable.video !== null) {
+                event.tracks.push({
+                  timelineStartInfo: {
+                    baseMediaDecodeTime: 0
+                  },
+                  id: +programMapTable.video,
+                  codec: 'avc',
+                  type: 'video'
+                });
+              }
+              if (programMapTable.audio !== null) {
+                event.tracks.push({
+                  timelineStartInfo: {
+                    baseMediaDecodeTime: 0
+                  },
+                  id: +programMapTable.audio,
+                  codec: 'adts',
+                  type: 'audio'
+                });
+              }
+
+              self.trigger('data', event);
+            }
+          })[data.type]();
+        };
+
+        /**
+         * Flush any remaining input. Video PES packets may be of variable
+         * length. Normally, the start of a new video packet can trigger the
+         * finalization of the previous packet. That is not possible if no
+         * more video is forthcoming, however. In that case, some other
+         * mechanism (like the end of the file) has to be employed. When it is
+         * clear that no additional data is forthcoming, calling this method
+         * will flush the buffered packets.
+         */
+        this.flush = function () {
+          // !!THIS ORDER IS IMPORTANT!!
+          // video first then audio
+          flushStream(video, 'video');
+          flushStream(audio, 'audio');
+          flushStream(timedMetadata, 'timed-metadata');
+          this.trigger('done');
+        };
+      };
+      _ElementaryStream.prototype = new stream();
+
+      var m2ts = {
+        PAT_PID: 0x0000,
+        MP2T_PACKET_LENGTH: MP2T_PACKET_LENGTH,
+        TransportPacketStream: _TransportPacketStream,
+        TransportParseStream: _TransportParseStream,
+        ElementaryStream: _ElementaryStream,
+        TimestampRolloverStream: TimestampRolloverStream$1,
+        CaptionStream: captionStream.CaptionStream,
+        Cea608Stream: captionStream.Cea608Stream,
+        MetadataStream: metadataStream
+      };
+
+      for (var type in streamTypes) {
+        if (streamTypes.hasOwnProperty(type)) {
+          m2ts[type] = streamTypes[type];
+        }
+      }
+
+      var m2ts_1 = m2ts;
+
+      var _AdtsStream;
+
+      var ADTS_SAMPLING_FREQUENCIES = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
+
+      /*
+       * Accepts a ElementaryStream and emits data events with parsed
+       * AAC Audio Frames of the individual packets. Input audio in ADTS
+       * format is unpacked and re-emitted as AAC frames.
+       *
+       * @see http://wiki.multimedia.cx/index.php?title=ADTS
+       * @see http://wiki.multimedia.cx/?title=Understanding_AAC
+       */
+      _AdtsStream = function AdtsStream() {
+        var buffer;
+
+        _AdtsStream.prototype.init.call(this);
+
+        this.push = function (packet) {
+          var i = 0,
+            frameNum = 0,
+            frameLength,
+            protectionSkipBytes,
+            frameEnd,
+            oldBuffer,
+            sampleCount,
+            adtsFrameDuration;
+
+          if (packet.type !== 'audio') {
+            // ignore non-audio data
+            return;
+          }
+
+          // Prepend any data in the buffer to the input data so that we can parse
+          // aac frames the cross a PES packet boundary
+          if (buffer) {
+            oldBuffer = buffer;
+            buffer = new Uint8Array(oldBuffer.byteLength + packet.data.byteLength);
+            buffer.set(oldBuffer);
+            buffer.set(packet.data, oldBuffer.byteLength);
+          } else {
+            buffer = packet.data;
+          }
+
+          // unpack any ADTS frames which have been fully received
+          // for details on the ADTS header, see http://wiki.multimedia.cx/index.php?title=ADTS
+          while (i + 5 < buffer.length) {
+
+            // Loook for the start of an ADTS header..
+            if (buffer[i] !== 0xFF || (buffer[i + 1] & 0xF6) !== 0xF0) {
+              // If a valid header was not found,  jump one forward and attempt to
+              // find a valid ADTS header starting at the next byte
+              i++;
+              continue;
+            }
+
+            // The protection skip bit tells us if we have 2 bytes of CRC data at the
+            // end of the ADTS header
+            protectionSkipBytes = (~buffer[i + 1] & 0x01) * 2;
+
+            // Frame length is a 13 bit integer starting 16 bits from the
+            // end of the sync sequence
+            frameLength = (buffer[i + 3] & 0x03) << 11 | buffer[i + 4] << 3 | (buffer[i + 5] & 0xe0) >> 5;
+
+            sampleCount = ((buffer[i + 6] & 0x03) + 1) * 1024;
+            adtsFrameDuration = sampleCount * 90000 / ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2];
+
+            frameEnd = i + frameLength;
+
+            // If we don't have enough data to actually finish this ADTS frame, return
+            // and wait for more data
+            if (buffer.byteLength < frameEnd) {
+              return;
+            }
+
+            // Otherwise, deliver the complete AAC frame
+            this.trigger('data', {
+              pts: packet.pts + frameNum * adtsFrameDuration,
+              dts: packet.dts + frameNum * adtsFrameDuration,
+              sampleCount: sampleCount,
+              audioobjecttype: (buffer[i + 2] >>> 6 & 0x03) + 1,
+              channelcount: (buffer[i + 2] & 1) << 2 | (buffer[i + 3] & 0xc0) >>> 6,
+              samplerate: ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2],
+              samplingfrequencyindex: (buffer[i + 2] & 0x3c) >>> 2,
+              // assume ISO/IEC 14496-12 AudioSampleEntry default of 16
+              samplesize: 16,
+              data: buffer.subarray(i + 7 + protectionSkipBytes, frameEnd)
+            });
+
+            // If the buffer is empty, clear it and return
+            if (buffer.byteLength === frameEnd) {
+              buffer = undefined;
+              return;
+            }
+
+            frameNum++;
+
+            // Remove the finished frame from the buffer and start the process again
+            buffer = buffer.subarray(frameEnd);
+          }
+        };
+        this.flush = function () {
+          this.trigger('done');
+        };
+      };
+
+      _AdtsStream.prototype = new stream();
+
+      var adts = _AdtsStream;
+
+      var ExpGolomb;
+
+      /**
+       * Parser for exponential Golomb codes, a variable-bitwidth number encoding
+       * scheme used by h264.
+       */
+      ExpGolomb = function ExpGolomb(workingData) {
+        var
+          // the number of bytes left to examine in workingData
+          workingBytesAvailable = workingData.byteLength,
+
+
+          // the current word being examined
+          workingWord = 0,
+
+          // :uint
+
+          // the number of bits left to examine in the current word
+          workingBitsAvailable = 0; // :uint;
+
+        // ():uint
+        this.length = function () {
+          return 8 * workingBytesAvailable;
+        };
+
+        // ():uint
+        this.bitsAvailable = function () {
+          return 8 * workingBytesAvailable + workingBitsAvailable;
+        };
+
+        // ():void
+        this.loadWord = function () {
+          var position = workingData.byteLength - workingBytesAvailable,
+            workingBytes = new Uint8Array(4),
+            availableBytes = Math.min(4, workingBytesAvailable);
+
+          if (availableBytes === 0) {
+            throw new Error('no bytes available');
+          }
+
+          workingBytes.set(workingData.subarray(position, position + availableBytes));
+          workingWord = new DataView(workingBytes.buffer).getUint32(0);
+
+          // track the amount of workingData that has been processed
+          workingBitsAvailable = availableBytes * 8;
+          workingBytesAvailable -= availableBytes;
+        };
+
+        // (count:int):void
+        this.skipBits = function (count) {
+          var skipBytes; // :int
+          if (workingBitsAvailable > count) {
+            workingWord <<= count;
+            workingBitsAvailable -= count;
+          } else {
+            count -= workingBitsAvailable;
+            skipBytes = Math.floor(count / 8);
+
+            count -= skipBytes * 8;
+            workingBytesAvailable -= skipBytes;
+
+            this.loadWord();
+
+            workingWord <<= count;
+            workingBitsAvailable -= count;
+          }
+        };
+
+        // (size:int):uint
+        this.readBits = function (size) {
+          var bits = Math.min(workingBitsAvailable, size),
+
+            // :uint
+            valu = workingWord >>> 32 - bits; // :uint
+          // if size > 31, handle error
+          workingBitsAvailable -= bits;
+          if (workingBitsAvailable > 0) {
+            workingWord <<= bits;
+          } else if (workingBytesAvailable > 0) {
+            this.loadWord();
+          }
+
+          bits = size - bits;
+          if (bits > 0) {
+            return valu << bits | this.readBits(bits);
+          }
+          return valu;
+        };
+
+        // ():uint
+        this.skipLeadingZeros = function () {
+          var leadingZeroCount; // :uint
+          for (leadingZeroCount = 0; leadingZeroCount < workingBitsAvailable; ++leadingZeroCount) {
+            if ((workingWord & 0x80000000 >>> leadingZeroCount) !== 0) {
+              // the first bit of working word is 1
+              workingWord <<= leadingZeroCount;
+              workingBitsAvailable -= leadingZeroCount;
+              return leadingZeroCount;
+            }
+          }
+
+          // we exhausted workingWord and still have not found a 1
+          this.loadWord();
+          return leadingZeroCount + this.skipLeadingZeros();
+        };
+
+        // ():void
+        this.skipUnsignedExpGolomb = function () {
+          this.skipBits(1 + this.skipLeadingZeros());
+        };
+
+        // ():void
+        this.skipExpGolomb = function () {
+          this.skipBits(1 + this.skipLeadingZeros());
+        };
+
+        // ():uint
+        this.readUnsignedExpGolomb = function () {
+          var clz = this.skipLeadingZeros(); // :uint
+          return this.readBits(clz + 1) - 1;
+        };
+
+        // ():int
+        this.readExpGolomb = function () {
+          var valu = this.readUnsignedExpGolomb(); // :int
+          if (0x01 & valu) {
+            // the number is odd if the low order bit is set
+            return 1 + valu >>> 1; // add 1 to make it even, and divide by 2
+          }
+          return -1 * (valu >>> 1); // divide by two then make it negative
+        };
+
+        // Some convenience functions
+        // :Boolean
+        this.readBoolean = function () {
+          return this.readBits(1) === 1;
+        };
+
+        // ():int
+        this.readUnsignedByte = function () {
+          return this.readBits(8);
+        };
+
+        this.loadWord();
+      };
+
+      var expGolomb = ExpGolomb;
+
+      var _H264Stream, _NalByteStream;
+      var PROFILES_WITH_OPTIONAL_SPS_DATA;
+
+      /**
+       * Accepts a NAL unit byte stream and unpacks the embedded NAL units.
+       */
+      _NalByteStream = function NalByteStream() {
+        var syncPoint = 0,
+          i,
+          buffer;
+        _NalByteStream.prototype.init.call(this);
+
+        this.push = function (data) {
+          var swapBuffer;
+
+          if (!buffer) {
+            buffer = data.data;
+          } else {
+            swapBuffer = new Uint8Array(buffer.byteLength + data.data.byteLength);
+            swapBuffer.set(buffer);
+            swapBuffer.set(data.data, buffer.byteLength);
+            buffer = swapBuffer;
+          }
+
+          // Rec. ITU-T H.264, Annex B
+          // scan for NAL unit boundaries
+
+          // a match looks like this:
+          // 0 0 1 .. NAL .. 0 0 1
+          // ^ sync point        ^ i
+          // or this:
+          // 0 0 1 .. NAL .. 0 0 0
+          // ^ sync point        ^ i
+
+          // advance the sync point to a NAL start, if necessary
+          for (; syncPoint < buffer.byteLength - 3; syncPoint++) {
+            if (buffer[syncPoint + 2] === 1) {
+              // the sync point is properly aligned
+              i = syncPoint + 5;
+              break;
+            }
+          }
+
+          while (i < buffer.byteLength) {
+            // look at the current byte to determine if we've hit the end of
+            // a NAL unit boundary
+            switch (buffer[i]) {
+              case 0:
+                // skip past non-sync sequences
+                if (buffer[i - 1] !== 0) {
+                  i += 2;
+                  break;
+                } else if (buffer[i - 2] !== 0) {
+                  i++;
+                  break;
+                }
+
+                // deliver the NAL unit if it isn't empty
+                if (syncPoint + 3 !== i - 2) {
+                  this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
+                }
+
+                // drop trailing zeroes
+                do {
+                  i++;
+                } while (buffer[i] !== 1 && i < buffer.length);
+                syncPoint = i - 2;
+                i += 3;
+                break;
+              case 1:
+                // skip past non-sync sequences
+                if (buffer[i - 1] !== 0 || buffer[i - 2] !== 0) {
+                  i += 3;
+                  break;
+                }
+
+                // deliver the NAL unit
+                this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
+                syncPoint = i - 2;
+                i += 3;
+                break;
+              default:
+                // the current byte isn't a one or zero, so it cannot be part
+                // of a sync sequence
+                i += 3;
+                break;
+            }
+          }
+          // filter out the NAL units that were delivered
+          buffer = buffer.subarray(syncPoint);
+          i -= syncPoint;
+          syncPoint = 0;
+        };
+
+        this.flush = function () {
+          // deliver the last buffered NAL unit
+          if (buffer && buffer.byteLength > 3) {
+            this.trigger('data', buffer.subarray(syncPoint + 3));
+          }
+          // reset the stream state
+          buffer = null;
+          syncPoint = 0;
+          this.trigger('done');
+        };
+      };
+      _NalByteStream.prototype = new stream();
+
+      // values of profile_idc that indicate additional fields are included in the SPS
+      // see Recommendation ITU-T H.264 (4/2013),
+      // 7.3.2.1.1 Sequence parameter set data syntax
+      PROFILES_WITH_OPTIONAL_SPS_DATA = {
+        100: true,
+        110: true,
+        122: true,
+        244: true,
+        44: true,
+        83: true,
+        86: true,
+        118: true,
+        128: true,
+        138: true,
+        139: true,
+        134: true
+      };
+
+      /**
+       * Accepts input from a ElementaryStream and produces H.264 NAL unit data
+       * events.
+       */
+      _H264Stream = function H264Stream() {
+        var nalByteStream = new _NalByteStream(),
+          self,
+          trackId,
+          currentPts,
+          currentDts,
+          discardEmulationPreventionBytes,
+          readSequenceParameterSet,
+          skipScalingList;
+
+        _H264Stream.prototype.init.call(this);
+        self = this;
+
+        this.push = function (packet) {
+          if (packet.type !== 'video') {
+            return;
+          }
+          trackId = packet.trackId;
+          currentPts = packet.pts;
+          currentDts = packet.dts;
+
+          nalByteStream.push(packet);
+        };
+
+        nalByteStream.on('data', function (data) {
+          var event = {
+            trackId: trackId,
+            pts: currentPts,
+            dts: currentDts,
+            data: data
+          };
+
+          switch (data[0] & 0x1f) {
+            case 0x05:
+              event.nalUnitType = 'slice_layer_without_partitioning_rbsp_idr';
+              break;
+            case 0x06:
+              event.nalUnitType = 'sei_rbsp';
+              event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
+              break;
+            case 0x07:
+              event.nalUnitType = 'seq_parameter_set_rbsp';
+              event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
+              event.config = readSequenceParameterSet(event.escapedRBSP);
+              break;
+            case 0x08:
+              event.nalUnitType = 'pic_parameter_set_rbsp';
+              break;
+            case 0x09:
+              event.nalUnitType = 'access_unit_delimiter_rbsp';
+              break;
+
+            default:
+              break;
+          }
+          self.trigger('data', event);
+        });
+        nalByteStream.on('done', function () {
+          self.trigger('done');
+        });
+
+        this.flush = function () {
+          nalByteStream.flush();
+        };
+
+        /**
+         * Advance the ExpGolomb decoder past a scaling list. The scaling
+         * list is optionally transmitted as part of a sequence parameter
+         * set and is not relevant to transmuxing.
+         * @param count {number} the number of entries in this scaling list
+         * @param expGolombDecoder {object} an ExpGolomb pointed to the
+         * start of a scaling list
+         * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
+         */
+        skipScalingList = function skipScalingList(count, expGolombDecoder) {
+          var lastScale = 8,
+            nextScale = 8,
+            j,
+            deltaScale;
+
+          for (j = 0; j < count; j++) {
+            if (nextScale !== 0) {
+              deltaScale = expGolombDecoder.readExpGolomb();
+              nextScale = (lastScale + deltaScale + 256) % 256;
+            }
+
+            lastScale = nextScale === 0 ? lastScale : nextScale;
+          }
+        };
+
+        /**
+         * Expunge any "Emulation Prevention" bytes from a "Raw Byte
+         * Sequence Payload"
+         * @param data {Uint8Array} the bytes of a RBSP from a NAL
+         * unit
+         * @return {Uint8Array} the RBSP without any Emulation
+         * Prevention Bytes
+         */
+        discardEmulationPreventionBytes = function discardEmulationPreventionBytes(data) {
+          var length = data.byteLength,
+            emulationPreventionBytesPositions = [],
+            i = 1,
+            newLength,
+            newData;
+
+          // Find all `Emulation Prevention Bytes`
+          while (i < length - 2) {
+            if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) {
+              emulationPreventionBytesPositions.push(i + 2);
+              i += 2;
+            } else {
+              i++;
+            }
+          }
+
+          // If no Emulation Prevention Bytes were found just return the original
+          // array
+          if (emulationPreventionBytesPositions.length === 0) {
+            return data;
+          }
+
+          // Create a new array to hold the NAL unit data
+          newLength = length - emulationPreventionBytesPositions.length;
+          newData = new Uint8Array(newLength);
+          var sourceIndex = 0;
+
+          for (i = 0; i < newLength; sourceIndex++, i++) {
+            if (sourceIndex === emulationPreventionBytesPositions[0]) {
+              // Skip this byte
+              sourceIndex++;
+              // Remove this position index
+              emulationPreventionBytesPositions.shift();
+            }
+            newData[i] = data[sourceIndex];
+          }
+
+          return newData;
+        };
+
+        /**
+         * Read a sequence parameter set and return some interesting video
+         * properties. A sequence parameter set is the H264 metadata that
+         * describes the properties of upcoming video frames.
+         * @param data {Uint8Array} the bytes of a sequence parameter set
+         * @return {object} an object with configuration parsed from the
+         * sequence parameter set, including the dimensions of the
+         * associated video frames.
+         */
+        readSequenceParameterSet = function readSequenceParameterSet(data) {
+          var frameCropLeftOffset = 0,
+            frameCropRightOffset = 0,
+            frameCropTopOffset = 0,
+            frameCropBottomOffset = 0,
+            sarScale = 1,
+            expGolombDecoder,
+            profileIdc,
+            levelIdc,
+            profileCompatibility,
+            chromaFormatIdc,
+            picOrderCntType,
+            numRefFramesInPicOrderCntCycle,
+            picWidthInMbsMinus1,
+            picHeightInMapUnitsMinus1,
+            frameMbsOnlyFlag,
+            scalingListCount,
+            sarRatio,
+            aspectRatioIdc,
+            i;
+
+          expGolombDecoder = new expGolomb(data);
+          profileIdc = expGolombDecoder.readUnsignedByte(); // profile_idc
+          profileCompatibility = expGolombDecoder.readUnsignedByte(); // constraint_set[0-5]_flag
+          levelIdc = expGolombDecoder.readUnsignedByte(); // level_idc u(8)
+          expGolombDecoder.skipUnsignedExpGolomb(); // seq_parameter_set_id
+
+          // some profiles have more optional data we don't need
+          if (PROFILES_WITH_OPTIONAL_SPS_DATA[profileIdc]) {
+            chromaFormatIdc = expGolombDecoder.readUnsignedExpGolomb();
+            if (chromaFormatIdc === 3) {
+              expGolombDecoder.skipBits(1); // separate_colour_plane_flag
+            }
+            expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_luma_minus8
+            expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_chroma_minus8
+            expGolombDecoder.skipBits(1); // qpprime_y_zero_transform_bypass_flag
+            if (expGolombDecoder.readBoolean()) {
+              // seq_scaling_matrix_present_flag
+              scalingListCount = chromaFormatIdc !== 3 ? 8 : 12;
+              for (i = 0; i < scalingListCount; i++) {
+                if (expGolombDecoder.readBoolean()) {
+                  // seq_scaling_list_present_flag[ i ]
+                  if (i < 6) {
+                    skipScalingList(16, expGolombDecoder);
+                  } else {
+                    skipScalingList(64, expGolombDecoder);
+                  }
+                }
+              }
+            }
+          }
+
+          expGolombDecoder.skipUnsignedExpGolomb(); // log2_max_frame_num_minus4
+          picOrderCntType = expGolombDecoder.readUnsignedExpGolomb();
+
+          if (picOrderCntType === 0) {
+            expGolombDecoder.readUnsignedExpGolomb(); // log2_max_pic_order_cnt_lsb_minus4
+          } else if (picOrderCntType === 1) {
+            expGolombDecoder.skipBits(1); // delta_pic_order_always_zero_flag
+            expGolombDecoder.skipExpGolomb(); // offset_for_non_ref_pic
+            expGolombDecoder.skipExpGolomb(); // offset_for_top_to_bottom_field
+            numRefFramesInPicOrderCntCycle = expGolombDecoder.readUnsignedExpGolomb();
+            for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
+              expGolombDecoder.skipExpGolomb(); // offset_for_ref_frame[ i ]
+            }
+          }
+
+          expGolombDecoder.skipUnsignedExpGolomb(); // max_num_ref_frames
+          expGolombDecoder.skipBits(1); // gaps_in_frame_num_value_allowed_flag
+
+          picWidthInMbsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
+          picHeightInMapUnitsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
+
+          frameMbsOnlyFlag = expGolombDecoder.readBits(1);
+          if (frameMbsOnlyFlag === 0) {
+            expGolombDecoder.skipBits(1); // mb_adaptive_frame_field_flag
+          }
+
+          expGolombDecoder.skipBits(1); // direct_8x8_inference_flag
+          if (expGolombDecoder.readBoolean()) {
+            // frame_cropping_flag
+            frameCropLeftOffset = expGolombDecoder.readUnsignedExpGolomb();
+            frameCropRightOffset = expGolombDecoder.readUnsignedExpGolomb();
+            frameCropTopOffset = expGolombDecoder.readUnsignedExpGolomb();
+            frameCropBottomOffset = expGolombDecoder.readUnsignedExpGolomb();
+          }
+          if (expGolombDecoder.readBoolean()) {
+            // vui_parameters_present_flag
+            if (expGolombDecoder.readBoolean()) {
+              // aspect_ratio_info_present_flag
+              aspectRatioIdc = expGolombDecoder.readUnsignedByte();
+              switch (aspectRatioIdc) {
+                case 1:
+                  sarRatio = [1, 1];
+                  break;
+                case 2:
+                  sarRatio = [12, 11];
+                  break;
+                case 3:
+                  sarRatio = [10, 11];
+                  break;
+                case 4:
+                  sarRatio = [16, 11];
+                  break;
+                case 5:
+                  sarRatio = [40, 33];
+                  break;
+                case 6:
+                  sarRatio = [24, 11];
+                  break;
+                case 7:
+                  sarRatio = [20, 11];
+                  break;
+                case 8:
+                  sarRatio = [32, 11];
+                  break;
+                case 9:
+                  sarRatio = [80, 33];
+                  break;
+                case 10:
+                  sarRatio = [18, 11];
+                  break;
+                case 11:
+                  sarRatio = [15, 11];
+                  break;
+                case 12:
+                  sarRatio = [64, 33];
+                  break;
+                case 13:
+                  sarRatio = [160, 99];
+                  break;
+                case 14:
+                  sarRatio = [4, 3];
+                  break;
+                case 15:
+                  sarRatio = [3, 2];
+                  break;
+                case 16:
+                  sarRatio = [2, 1];
+                  break;
+                case 255: {
+                  sarRatio = [expGolombDecoder.readUnsignedByte() << 8 | expGolombDecoder.readUnsignedByte(), expGolombDecoder.readUnsignedByte() << 8 | expGolombDecoder.readUnsignedByte()];
+                  break;
+                }
+              }
+              if (sarRatio) {
+                sarScale = sarRatio[0] / sarRatio[1];
+              }
+            }
+          }
+          return {
+            profileIdc: profileIdc,
+            levelIdc: levelIdc,
+            profileCompatibility: profileCompatibility,
+            width: Math.ceil(((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2) * sarScale),
+            height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - frameCropTopOffset * 2 - frameCropBottomOffset * 2
+          };
+        };
+      };
+      _H264Stream.prototype = new stream();
+
+      var h264 = {
+        H264Stream: _H264Stream,
+        NalByteStream: _NalByteStream
+      };
+
+      // Constants
+      var _AacStream;
+
+      /**
+       * Splits an incoming stream of binary data into ADTS and ID3 Frames.
+       */
+
+      _AacStream = function AacStream() {
+        var everything = new Uint8Array(),
+          timeStamp = 0;
+
+        _AacStream.prototype.init.call(this);
+
+        this.setTimestamp = function (timestamp) {
+          timeStamp = timestamp;
+        };
+
+        this.parseId3TagSize = function (header, byteIndex) {
+          var returnSize = header[byteIndex + 6] << 21 | header[byteIndex + 7] << 14 | header[byteIndex + 8] << 7 | header[byteIndex + 9],
+            flags = header[byteIndex + 5],
+            footerPresent = (flags & 16) >> 4;
+
+          if (footerPresent) {
+            return returnSize + 20;
+          }
+          return returnSize + 10;
+        };
+
+        this.parseAdtsSize = function (header, byteIndex) {
+          var lowThree = (header[byteIndex + 5] & 0xE0) >> 5,
+            middle = header[byteIndex + 4] << 3,
+            highTwo = header[byteIndex + 3] & 0x3 << 11;
+
+          return highTwo | middle | lowThree;
+        };
+
+        this.push = function (bytes) {
+          var frameSize = 0,
+            byteIndex = 0,
+            bytesLeft,
+            chunk,
+            packet,
+            tempLength;
+
+          // If there are bytes remaining from the last segment, prepend them to the
+          // bytes that were pushed in
+          if (everything.length) {
+            tempLength = everything.length;
+            everything = new Uint8Array(bytes.byteLength + tempLength);
+            everything.set(everything.subarray(0, tempLength));
+            everything.set(bytes, tempLength);
+          } else {
+            everything = bytes;
+          }
+
+          while (everything.length - byteIndex >= 3) {
+            if (everything[byteIndex] === 'I'.charCodeAt(0) && everything[byteIndex + 1] === 'D'.charCodeAt(0) && everything[byteIndex + 2] === '3'.charCodeAt(0)) {
+
+              // Exit early because we don't have enough to parse
+              // the ID3 tag header
+              if (everything.length - byteIndex < 10) {
+                break;
+              }
+
+              // check framesize
+              frameSize = this.parseId3TagSize(everything, byteIndex);
+
+              // Exit early if we don't have enough in the buffer
+              // to emit a full packet
+              if (frameSize > everything.length) {
+                break;
+              }
+              chunk = {
+                type: 'timed-metadata',
+                data: everything.subarray(byteIndex, byteIndex + frameSize)
+              };
+              this.trigger('data', chunk);
+              byteIndex += frameSize;
+              continue;
+            } else if (everything[byteIndex] & 0xff === 0xff && (everything[byteIndex + 1] & 0xf0) === 0xf0) {
+
+              // Exit early because we don't have enough to parse
+              // the ADTS frame header
+              if (everything.length - byteIndex < 7) {
+                break;
+              }
+
+              frameSize = this.parseAdtsSize(everything, byteIndex);
+
+              // Exit early if we don't have enough in the buffer
+              // to emit a full packet
+              if (frameSize > everything.length) {
+                break;
+              }
+
+              packet = {
+                type: 'audio',
+                data: everything.subarray(byteIndex, byteIndex + frameSize),
+                pts: timeStamp,
+                dts: timeStamp
+              };
+              this.trigger('data', packet);
+              byteIndex += frameSize;
+              continue;
+            }
+            byteIndex++;
+          }
+          bytesLeft = everything.length - byteIndex;
+
+          if (bytesLeft > 0) {
+            everything = everything.subarray(byteIndex);
+          } else {
+            everything = new Uint8Array();
+          }
+        };
+      };
+
+      _AacStream.prototype = new stream();
+
+      var aac = _AacStream;
+
+      var highPrefix = [33, 16, 5, 32, 164, 27];
+      var lowPrefix = [33, 65, 108, 84, 1, 2, 4, 8, 168, 2, 4, 8, 17, 191, 252];
+      var zeroFill = function zeroFill(count) {
+        var a = [];
+        while (count--) {
+          a.push(0);
+        }
+        return a;
+      };
+
+      var makeTable = function makeTable(metaTable) {
+        return Object.keys(metaTable).reduce(function (obj, key) {
+          obj[key] = new Uint8Array(metaTable[key].reduce(function (arr, part) {
+            return arr.concat(part);
+          }, []));
+          return obj;
+        }, {});
+      };
+
+      // Frames-of-silence to use for filling in missing AAC frames
+      var coneOfSilence = {
+        96000: [highPrefix, [227, 64], zeroFill(154), [56]],
+        88200: [highPrefix, [231], zeroFill(170), [56]],
+        64000: [highPrefix, [248, 192], zeroFill(240), [56]],
+        48000: [highPrefix, [255, 192], zeroFill(268), [55, 148, 128], zeroFill(54), [112]],
+        44100: [highPrefix, [255, 192], zeroFill(268), [55, 163, 128], zeroFill(84), [112]],
+        32000: [highPrefix, [255, 192], zeroFill(268), [55, 234], zeroFill(226), [112]],
+        24000: [highPrefix, [255, 192], zeroFill(268), [55, 255, 128], zeroFill(268), [111, 112], zeroFill(126), [224]],
+        16000: [highPrefix, [255, 192], zeroFill(268), [55, 255, 128], zeroFill(268), [111, 255], zeroFill(269), [223, 108], zeroFill(195), [1, 192]],
+        12000: [lowPrefix, zeroFill(268), [3, 127, 248], zeroFill(268), [6, 255, 240], zeroFill(268), [13, 255, 224], zeroFill(268), [27, 253, 128], zeroFill(259), [56]],
+        11025: [lowPrefix, zeroFill(268), [3, 127, 248], zeroFill(268), [6, 255, 240], zeroFill(268), [13, 255, 224], zeroFill(268), [27, 255, 192], zeroFill(268), [55, 175, 128], zeroFill(108), [112]],
+        8000: [lowPrefix, zeroFill(268), [3, 121, 16], zeroFill(47), [7]]
+      };
+
+      var silence = makeTable(coneOfSilence);
+
+      var ONE_SECOND_IN_TS = 90000,
+
+        // 90kHz clock
+        secondsToVideoTs,
+        secondsToAudioTs,
+        videoTsToSeconds,
+        audioTsToSeconds,
+        audioTsToVideoTs,
+        videoTsToAudioTs;
+
+      secondsToVideoTs = function secondsToVideoTs(seconds) {
+        return seconds * ONE_SECOND_IN_TS;
+      };
+
+      secondsToAudioTs = function secondsToAudioTs(seconds, sampleRate) {
+        return seconds * sampleRate;
+      };
+
+      videoTsToSeconds = function videoTsToSeconds(timestamp) {
+        return timestamp / ONE_SECOND_IN_TS;
+      };
+
+      audioTsToSeconds = function audioTsToSeconds(timestamp, sampleRate) {
+        return timestamp / sampleRate;
+      };
+
+      audioTsToVideoTs = function audioTsToVideoTs(timestamp, sampleRate) {
+        return secondsToVideoTs(audioTsToSeconds(timestamp, sampleRate));
+      };
+
+      videoTsToAudioTs = function videoTsToAudioTs(timestamp, sampleRate) {
+        return secondsToAudioTs(videoTsToSeconds(timestamp), sampleRate);
+      };
+
+      var clock = {
+        secondsToVideoTs: secondsToVideoTs,
+        secondsToAudioTs: secondsToAudioTs,
+        videoTsToSeconds: videoTsToSeconds,
+        audioTsToSeconds: audioTsToSeconds,
+        audioTsToVideoTs: audioTsToVideoTs,
+        videoTsToAudioTs: videoTsToAudioTs
+      };
+
+      var H264Stream = h264.H264Stream;
+
+      // constants
+      var AUDIO_PROPERTIES = ['audioobjecttype', 'channelcount', 'samplerate', 'samplingfrequencyindex', 'samplesize'];
+
+      var VIDEO_PROPERTIES = ['width', 'height', 'profileIdc', 'levelIdc', 'profileCompatibility'];
+
+      var ONE_SECOND_IN_TS$1 = 90000; // 90kHz clock
+
+      // object types
+      var _VideoSegmentStream, _AudioSegmentStream, _Transmuxer, _CoalesceStream;
+
+      // Helper functions
+      var createDefaultSample, isLikelyAacData, collectDtsInfo, clearDtsInfo, calculateTrackBaseMediaDecodeTime,
+        arrayEquals, sumFrameByteLengths;
+
+      /**
+       * Default sample object
+       * see ISO/IEC 14496-12:2012, section 8.6.4.3
+       */
+      createDefaultSample = function createDefaultSample() {
+        return {
+          size: 0,
+          flags: {
+            isLeading: 0,
+            dependsOn: 1,
+            isDependedOn: 0,
+            hasRedundancy: 0,
+            degradationPriority: 0
+          }
+        };
+      };
+
+      isLikelyAacData = function isLikelyAacData(data) {
+        if (data[0] === 'I'.charCodeAt(0) && data[1] === 'D'.charCodeAt(0) && data[2] === '3'.charCodeAt(0)) {
+          return true;
+        }
+        return false;
+      };
+
+      /**
+       * Compare two arrays (even typed) for same-ness
+       */
+      arrayEquals = function arrayEquals(a, b) {
+        var i;
+
+        if (a.length !== b.length) {
+          return false;
+        }
+
+        // compare the value of each element in the array
+        for (i = 0; i < a.length; i++) {
+          if (a[i] !== b[i]) {
+            return false;
+          }
+        }
+
+        return true;
+      };
+
+      /**
+       * Sum the `byteLength` properties of the data in each AAC frame
+       */
+      sumFrameByteLengths = function sumFrameByteLengths(array) {
+        var i,
+          currentObj,
+          sum = 0;
+
+        // sum the byteLength's all each nal unit in the frame
+        for (i = 0; i < array.length; i++) {
+          currentObj = array[i];
+          sum += currentObj.data.byteLength;
+        }
+
+        return sum;
+      };
+
+      /**
+       * Constructs a single-track, ISO BMFF media segment from AAC data
+       * events. The output of this stream can be fed to a SourceBuffer
+       * configured with a suitable initialization segment.
+       * @param track {object} track metadata configuration
+       * @param options {object} transmuxer options object
+       * @param options.keepOriginalTimestamps {boolean} If true, keep the timestamps
+       *        in the source; false to adjust the first segment to start at 0.
+       */
+      _AudioSegmentStream = function AudioSegmentStream(track, options) {
+        var adtsFrames = [],
+          sequenceNumber = 0,
+          earliestAllowedDts = 0,
+          audioAppendStartTs = 0,
+          videoBaseMediaDecodeTime = Infinity;
+
+        options = options || {};
+
+        _AudioSegmentStream.prototype.init.call(this);
+
+        this.push = function (data) {
+          collectDtsInfo(track, data);
+
+          if (track) {
+            AUDIO_PROPERTIES.forEach(function (prop) {
+              track[prop] = data[prop];
+            });
+          }
+
+          // buffer audio data until end() is called
+          adtsFrames.push(data);
+        };
+
+        this.setEarliestDts = function (earliestDts) {
+          earliestAllowedDts = earliestDts - track.timelineStartInfo.baseMediaDecodeTime;
+        };
+
+        this.setVideoBaseMediaDecodeTime = function (baseMediaDecodeTime) {
+          videoBaseMediaDecodeTime = baseMediaDecodeTime;
+        };
+
+        this.setAudioAppendStart = function (timestamp) {
+          audioAppendStartTs = timestamp;
+        };
+
+        this.flush = function () {
+          var frames, moof, mdat, boxes;
+
+          // return early if no audio data has been observed
+          if (adtsFrames.length === 0) {
+            this.trigger('done', 'AudioSegmentStream');
+            return;
+          }
+
+          frames = this.trimAdtsFramesByEarliestDts_(adtsFrames);
+          track.baseMediaDecodeTime = calculateTrackBaseMediaDecodeTime(track, options.keepOriginalTimestamps);
+
+          this.prefixWithSilence_(track, frames);
+
+          // we have to build the index from byte locations to
+          // samples (that is, adts frames) in the audio data
+          track.samples = this.generateSampleTable_(frames);
+
+          // concatenate the audio data to constuct the mdat
+          mdat = mp4Generator.mdat(this.concatenateFrameData_(frames));
+
+          adtsFrames = [];
+
+          moof = mp4Generator.moof(sequenceNumber, [track]);
+          boxes = new Uint8Array(moof.byteLength + mdat.byteLength);
+
+          // bump the sequence number for next time
+          sequenceNumber++;
+
+          boxes.set(moof);
+          boxes.set(mdat, moof.byteLength);
+
+          clearDtsInfo(track);
+
+          this.trigger('data', {track: track, boxes: boxes});
+          this.trigger('done', 'AudioSegmentStream');
+        };
+
+        // Possibly pad (prefix) the audio track with silence if appending this track
+        // would lead to the introduction of a gap in the audio buffer
+        this.prefixWithSilence_ = function (track, frames) {
+          var baseMediaDecodeTimeTs,
+            frameDuration = 0,
+            audioGapDuration = 0,
+            audioFillFrameCount = 0,
+            audioFillDuration = 0,
+            silentFrame,
+            i;
+
+          if (!frames.length) {
+            return;
+          }
+
+          baseMediaDecodeTimeTs = clock.audioTsToVideoTs(track.baseMediaDecodeTime, track.samplerate);
+          // determine frame clock duration based on sample rate, round up to avoid overfills
+          frameDuration = Math.ceil(ONE_SECOND_IN_TS$1 / (track.samplerate / 1024));
+
+          if (audioAppendStartTs && videoBaseMediaDecodeTime) {
+            // insert the shortest possible amount (audio gap or audio to video gap)
+            audioGapDuration = baseMediaDecodeTimeTs - Math.max(audioAppendStartTs, videoBaseMediaDecodeTime);
+            // number of full frames in the audio gap
+            audioFillFrameCount = Math.floor(audioGapDuration / frameDuration);
+            audioFillDuration = audioFillFrameCount * frameDuration;
+          }
+
+          // don't attempt to fill gaps smaller than a single frame or larger
+          // than a half second
+          if (audioFillFrameCount < 1 || audioFillDuration > ONE_SECOND_IN_TS$1 / 2) {
+            return;
+          }
+
+          silentFrame = silence[track.samplerate];
+
+          if (!silentFrame) {
+            // we don't have a silent frame pregenerated for the sample rate, so use a frame
+            // from the content instead
+            silentFrame = frames[0].data;
+          }
+
+          for (i = 0; i < audioFillFrameCount; i++) {
+            frames.splice(i, 0, {
+              data: silentFrame
+            });
+          }
+
+          track.baseMediaDecodeTime -= Math.floor(clock.videoTsToAudioTs(audioFillDuration, track.samplerate));
+        };
+
+        // If the audio segment extends before the earliest allowed dts
+        // value, remove AAC frames until starts at or after the earliest
+        // allowed DTS so that we don't end up with a negative baseMedia-
+        // DecodeTime for the audio track
+        this.trimAdtsFramesByEarliestDts_ = function (adtsFrames) {
+          if (track.minSegmentDts >= earliestAllowedDts) {
+            return adtsFrames;
+          }
+
+          // We will need to recalculate the earliest segment Dts
+          track.minSegmentDts = Infinity;
+
+          return adtsFrames.filter(function (currentFrame) {
+            // If this is an allowed frame, keep it and record it's Dts
+            if (currentFrame.dts >= earliestAllowedDts) {
+              track.minSegmentDts = Math.min(track.minSegmentDts, currentFrame.dts);
+              track.minSegmentPts = track.minSegmentDts;
+              return true;
+            }
+            // Otherwise, discard it
+            return false;
+          });
+        };
+
+        // generate the track's raw mdat data from an array of frames
+        this.generateSampleTable_ = function (frames) {
+          var i,
+            currentFrame,
+            samples = [];
+
+          for (i = 0; i < frames.length; i++) {
+            currentFrame = frames[i];
+            samples.push({
+              size: currentFrame.data.byteLength,
+              duration: 1024 // For AAC audio, all samples contain 1024 samples
+            });
+          }
+          return samples;
+        };
+
+        // generate the track's sample table from an array of frames
+        this.concatenateFrameData_ = function (frames) {
+          var i,
+            currentFrame,
+            dataOffset = 0,
+            data = new Uint8Array(sumFrameByteLengths(frames));
+
+          for (i = 0; i < frames.length; i++) {
+            currentFrame = frames[i];
+
+            data.set(currentFrame.data, dataOffset);
+            dataOffset += currentFrame.data.byteLength;
+          }
+          return data;
+        };
+      };
+
+      _AudioSegmentStream.prototype = new stream();
+
+      /**
+       * Constructs a single-track, ISO BMFF media segment from H264 data
+       * events. The output of this stream can be fed to a SourceBuffer
+       * configured with a suitable initialization segment.
+       * @param track {object} track metadata configuration
+       * @param options {object} transmuxer options object
+       * @param options.alignGopsAtEnd {boolean} If true, start from the end of the
+       *        gopsToAlignWith list when attempting to align gop pts
+       * @param options.keepOriginalTimestamps {boolean} If true, keep the timestamps
+       *        in the source; false to adjust the first segment to start at 0.
+       */
+      _VideoSegmentStream = function VideoSegmentStream(track, options) {
+        var sequenceNumber = 0,
+          nalUnits = [],
+          gopsToAlignWith = [],
+          config,
+          pps;
+
+        options = options || {};
+
+        _VideoSegmentStream.prototype.init.call(this);
+
+        delete track.minPTS;
+
+        this.gopCache_ = [];
+
+        this.push = function (nalUnit) {
+          collectDtsInfo(track, nalUnit);
+
+          // record the track config
+          if (nalUnit.nalUnitType === 'seq_parameter_set_rbsp' && !config) {
+            config = nalUnit.config;
+            track.sps = [nalUnit.data];
+
+            VIDEO_PROPERTIES.forEach(function (prop) {
+              track[prop] = config[prop];
+            }, this);
+          }
+
+          if (nalUnit.nalUnitType === 'pic_parameter_set_rbsp' && !pps) {
+            pps = nalUnit.data;
+            track.pps = [nalUnit.data];
+          }
+
+          // buffer video until flush() is called
+          nalUnits.push(nalUnit);
+        };
+
+        this.flush = function () {
+          var frames, gopForFusion, gops, moof, mdat, boxes;
+
+          // Throw away nalUnits at the start of the byte stream until
+          // we find the first AUD
+          while (nalUnits.length) {
+            if (nalUnits[0].nalUnitType === 'access_unit_delimiter_rbsp') {
+              break;
+            }
+            nalUnits.shift();
+          }
+
+          // Return early if no video data has been observed
+          if (nalUnits.length === 0) {
+            this.resetStream_();
+            this.trigger('done', 'VideoSegmentStream');
+            return;
+          }
+
+          // Organize the raw nal-units into arrays that represent
+          // higher-level constructs such as frames and gops
+          // (group-of-pictures)
+          frames = this.groupNalsIntoFrames_(nalUnits);
+          gops = this.groupFramesIntoGops_(frames);
+
+          // If the first frame of this fragment is not a keyframe we have
+          // a problem since MSE (on Chrome) requires a leading keyframe.
+          //
+          // We have two approaches to repairing this situation:
+          // 1) GOP-FUSION:
+          //    This is where we keep track of the GOPS (group-of-pictures)
+          //    from previous fragments and attempt to find one that we can
+          //    prepend to the current fragment in order to create a valid
+          //    fragment.
+          // 2) KEYFRAME-PULLING:
+          //    Here we search for the first keyframe in the fragment and
+          //    throw away all the frames between the start of the fragment
+          //    and that keyframe. We then extend the duration and pull the
+          //    PTS of the keyframe forward so that it covers the time range
+          //    of the frames that were disposed of.
+          //
+          // #1 is far prefereable over #2 which can cause "stuttering" but
+          // requires more things to be just right.
+          if (!gops[0][0].keyFrame) {
+            // Search for a gop for fusion from our gopCache
+            gopForFusion = this.getGopForFusion_(nalUnits[0], track);
+
+            if (gopForFusion) {
+              gops.unshift(gopForFusion);
+              // Adjust Gops' metadata to account for the inclusion of the
+              // new gop at the beginning
+              gops.byteLength += gopForFusion.byteLength;
+              gops.nalCount += gopForFusion.nalCount;
+              gops.pts = gopForFusion.pts;
+              gops.dts = gopForFusion.dts;
+              gops.duration += gopForFusion.duration;
+            } else {
+              // If we didn't find a candidate gop fall back to keyframe-pulling
+              gops = this.extendFirstKeyFrame_(gops);
+            }
+          }
+
+          // Trim gops to align with gopsToAlignWith
+          if (gopsToAlignWith.length) {
+            var alignedGops;
+
+            if (options.alignGopsAtEnd) {
+              alignedGops = this.alignGopsAtEnd_(gops);
+            } else {
+              alignedGops = this.alignGopsAtStart_(gops);
+            }
+
+            if (!alignedGops) {
+              // save all the nals in the last GOP into the gop cache
+              this.gopCache_.unshift({
+                gop: gops.pop(),
+                pps: track.pps,
+                sps: track.sps
+              });
+
+              // Keep a maximum of 6 GOPs in the cache
+              this.gopCache_.length = Math.min(6, this.gopCache_.length);
+
+              // Clear nalUnits
+              nalUnits = [];
+
+              // return early no gops can be aligned with desired gopsToAlignWith
+              this.resetStream_();
+              this.trigger('done', 'VideoSegmentStream');
+              return;
+            }
+
+            // Some gops were trimmed. clear dts info so minSegmentDts and pts are correct
+            // when recalculated before sending off to CoalesceStream
+            clearDtsInfo(track);
+
+            gops = alignedGops;
+          }
+
+          collectDtsInfo(track, gops);
+
+          // First, we have to build the index from byte locations to
+          // samples (that is, frames) in the video data
+          track.samples = this.generateSampleTable_(gops);
+
+          // Concatenate the video data and construct the mdat
+          mdat = mp4Generator.mdat(this.concatenateNalData_(gops));
+
+          track.baseMediaDecodeTime = calculateTrackBaseMediaDecodeTime(track, options.keepOriginalTimestamps);
+
+          this.trigger('processedGopsInfo', gops.map(function (gop) {
+            return {
+              pts: gop.pts,
+              dts: gop.dts,
+              byteLength: gop.byteLength
+            };
+          }));
+
+          // save all the nals in the last GOP into the gop cache
+          this.gopCache_.unshift({
+            gop: gops.pop(),
+            pps: track.pps,
+            sps: track.sps
+          });
+
+          // Keep a maximum of 6 GOPs in the cache
+          this.gopCache_.length = Math.min(6, this.gopCache_.length);
+
+          // Clear nalUnits
+          nalUnits = [];
+
+          this.trigger('baseMediaDecodeTime', track.baseMediaDecodeTime);
+          this.trigger('timelineStartInfo', track.timelineStartInfo);
+
+          moof = mp4Generator.moof(sequenceNumber, [track]);
+
+          // it would be great to allocate this array up front instead of
+          // throwing away hundreds of media segment fragments
+          boxes = new Uint8Array(moof.byteLength + mdat.byteLength);
+
+          // Bump the sequence number for next time
+          sequenceNumber++;
+
+          boxes.set(moof);
+          boxes.set(mdat, moof.byteLength);
+
+          this.trigger('data', {track: track, boxes: boxes});
+
+          this.resetStream_();
+
+          // Continue with the flush process now
+          this.trigger('done', 'VideoSegmentStream');
+        };
+
+        this.resetStream_ = function () {
+          clearDtsInfo(track);
+
+          // reset config and pps because they may differ across segments
+          // for instance, when we are rendition switching
+          config = undefined;
+          pps = undefined;
+        };
+
+        // Search for a candidate Gop for gop-fusion from the gop cache and
+        // return it or return null if no good candidate was found
+        this.getGopForFusion_ = function (nalUnit) {
+          var halfSecond = 45000,
+
+            // Half-a-second in a 90khz clock
+            allowableOverlap = 10000,
+
+            // About 3 frames @ 30fps
+            nearestDistance = Infinity,
+            dtsDistance,
+            nearestGopObj,
+            currentGop,
+            currentGopObj,
+            i;
+
+          // Search for the GOP nearest to the beginning of this nal unit
+          for (i = 0; i < this.gopCache_.length; i++) {
+            currentGopObj = this.gopCache_[i];
+            currentGop = currentGopObj.gop;
+
+            // Reject Gops with different SPS or PPS
+            if (!(track.pps && arrayEquals(track.pps[0], currentGopObj.pps[0])) || !(track.sps && arrayEquals(track.sps[0], currentGopObj.sps[0]))) {
+              continue;
+            }
+
+            // Reject Gops that would require a negative baseMediaDecodeTime
+            if (currentGop.dts < track.timelineStartInfo.dts) {
+              continue;
+            }
+
+            // The distance between the end of the gop and the start of the nalUnit
+            dtsDistance = nalUnit.dts - currentGop.dts - currentGop.duration;
+
+            // Only consider GOPS that start before the nal unit and end within
+            // a half-second of the nal unit
+            if (dtsDistance >= -allowableOverlap && dtsDistance <= halfSecond) {
+
+              // Always use the closest GOP we found if there is more than
+              // one candidate
+              if (!nearestGopObj || nearestDistance > dtsDistance) {
+                nearestGopObj = currentGopObj;
+                nearestDistance = dtsDistance;
+              }
+            }
+          }
+
+          if (nearestGopObj) {
+            return nearestGopObj.gop;
+          }
+          return null;
+        };
+
+        this.extendFirstKeyFrame_ = function (gops) {
+          var currentGop;
+
+          if (!gops[0][0].keyFrame && gops.length > 1) {
+            // Remove the first GOP
+            currentGop = gops.shift();
+
+            gops.byteLength -= currentGop.byteLength;
+            gops.nalCount -= currentGop.nalCount;
+
+            // Extend the first frame of what is now the
+            // first gop to cover the time period of the
+            // frames we just removed
+            gops[0][0].dts = currentGop.dts;
+            gops[0][0].pts = currentGop.pts;
+            gops[0][0].duration += currentGop.duration;
+          }
+
+          return gops;
+        };
+
+        // Convert an array of nal units into an array of frames with each frame being
+        // composed of the nal units that make up that frame
+        // Also keep track of cummulative data about the frame from the nal units such
+        // as the frame duration, starting pts, etc.
+        this.groupNalsIntoFrames_ = function (nalUnits) {
+          var i,
+            currentNal,
+            currentFrame = [],
+            frames = [];
+
+          currentFrame.byteLength = 0;
+
+          for (i = 0; i < nalUnits.length; i++) {
+            currentNal = nalUnits[i];
+
+            // Split on 'aud'-type nal units
+            if (currentNal.nalUnitType === 'access_unit_delimiter_rbsp') {
+              // Since the very first nal unit is expected to be an AUD
+              // only push to the frames array when currentFrame is not empty
+              if (currentFrame.length) {
+                currentFrame.duration = currentNal.dts - currentFrame.dts;
+                frames.push(currentFrame);
+              }
+              currentFrame = [currentNal];
+              currentFrame.byteLength = currentNal.data.byteLength;
+              currentFrame.pts = currentNal.pts;
+              currentFrame.dts = currentNal.dts;
+            } else {
+              // Specifically flag key frames for ease of use later
+              if (currentNal.nalUnitType === 'slice_layer_without_partitioning_rbsp_idr') {
+                currentFrame.keyFrame = true;
+              }
+              currentFrame.duration = currentNal.dts - currentFrame.dts;
+              currentFrame.byteLength += currentNal.data.byteLength;
+              currentFrame.push(currentNal);
+            }
+          }
+
+          // For the last frame, use the duration of the previous frame if we
+          // have nothing better to go on
+          if (frames.length && (!currentFrame.duration || currentFrame.duration <= 0)) {
+            currentFrame.duration = frames[frames.length - 1].duration;
+          }
+
+          // Push the final frame
+          frames.push(currentFrame);
+          return frames;
+        };
+
+        // Convert an array of frames into an array of Gop with each Gop being composed
+        // of the frames that make up that Gop
+        // Also keep track of cummulative data about the Gop from the frames such as the
+        // Gop duration, starting pts, etc.
+        this.groupFramesIntoGops_ = function (frames) {
+          var i,
+            currentFrame,
+            currentGop = [],
+            gops = [];
+
+          // We must pre-set some of the values on the Gop since we
+          // keep running totals of these values
+          currentGop.byteLength = 0;
+          currentGop.nalCount = 0;
+          currentGop.duration = 0;
+          currentGop.pts = frames[0].pts;
+          currentGop.dts = frames[0].dts;
+
+          // store some metadata about all the Gops
+          gops.byteLength = 0;
+          gops.nalCount = 0;
+          gops.duration = 0;
+          gops.pts = frames[0].pts;
+          gops.dts = frames[0].dts;
+
+          for (i = 0; i < frames.length; i++) {
+            currentFrame = frames[i];
+
+            if (currentFrame.keyFrame) {
+              // Since the very first frame is expected to be an keyframe
+              // only push to the gops array when currentGop is not empty
+              if (currentGop.length) {
+                gops.push(currentGop);
+                gops.byteLength += currentGop.byteLength;
+                gops.nalCount += currentGop.nalCount;
+                gops.duration += currentGop.duration;
+              }
+
+              currentGop = [currentFrame];
+              currentGop.nalCount = currentFrame.length;
+              currentGop.byteLength = currentFrame.byteLength;
+              currentGop.pts = currentFrame.pts;
+              currentGop.dts = currentFrame.dts;
+              currentGop.duration = currentFrame.duration;
+            } else {
+              currentGop.duration += currentFrame.duration;
+              currentGop.nalCount += currentFrame.length;
+              currentGop.byteLength += currentFrame.byteLength;
+              currentGop.push(currentFrame);
+            }
+          }
+
+          if (gops.length && currentGop.duration <= 0) {
+            currentGop.duration = gops[gops.length - 1].duration;
+          }
+          gops.byteLength += currentGop.byteLength;
+          gops.nalCount += currentGop.nalCount;
+          gops.duration += currentGop.duration;
+
+          // push the final Gop
+          gops.push(currentGop);
+          return gops;
+        };
+
+        // generate the track's sample table from an array of gops
+        this.generateSampleTable_ = function (gops, baseDataOffset) {
+          var h,
+            i,
+            sample,
+            currentGop,
+            currentFrame,
+            dataOffset = baseDataOffset || 0,
+            samples = [];
+
+          for (h = 0; h < gops.length; h++) {
+            currentGop = gops[h];
+
+            for (i = 0; i < currentGop.length; i++) {
+              currentFrame = currentGop[i];
+
+              sample = createDefaultSample();
+
+              sample.dataOffset = dataOffset;
+              sample.compositionTimeOffset = currentFrame.pts - currentFrame.dts;
+              sample.duration = currentFrame.duration;
+              sample.size = 4 * currentFrame.length; // Space for nal unit size
+              sample.size += currentFrame.byteLength;
+
+              if (currentFrame.keyFrame) {
+                sample.flags.dependsOn = 2;
+              }
+
+              dataOffset += sample.size;
+
+              samples.push(sample);
+            }
+          }
+          return samples;
+        };
+
+        // generate the track's raw mdat data from an array of gops
+        this.concatenateNalData_ = function (gops) {
+          var h,
+            i,
+            j,
+            currentGop,
+            currentFrame,
+            currentNal,
+            dataOffset = 0,
+            nalsByteLength = gops.byteLength,
+            numberOfNals = gops.nalCount,
+            totalByteLength = nalsByteLength + 4 * numberOfNals,
+            data = new Uint8Array(totalByteLength),
+            view = new DataView(data.buffer);
+
+          // For each Gop..
+          for (h = 0; h < gops.length; h++) {
+            currentGop = gops[h];
+
+            // For each Frame..
+            for (i = 0; i < currentGop.length; i++) {
+              currentFrame = currentGop[i];
+
+              // For each NAL..
+              for (j = 0; j < currentFrame.length; j++) {
+                currentNal = currentFrame[j];
+
+                view.setUint32(dataOffset, currentNal.data.byteLength);
+                dataOffset += 4;
+                data.set(currentNal.data, dataOffset);
+                dataOffset += currentNal.data.byteLength;
+              }
+            }
+          }
+          return data;
+        };
+
+        // trim gop list to the first gop found that has a matching pts with a gop in the list
+        // of gopsToAlignWith starting from the START of the list
+        this.alignGopsAtStart_ = function (gops) {
+          var alignIndex, gopIndex, align, gop, byteLength, nalCount, duration, alignedGops;
+
+          byteLength = gops.byteLength;
+          nalCount = gops.nalCount;
+          duration = gops.duration;
+          alignIndex = gopIndex = 0;
+
+          while (alignIndex < gopsToAlignWith.length && gopIndex < gops.length) {
+            align = gopsToAlignWith[alignIndex];
+            gop = gops[gopIndex];
+
+            if (align.pts === gop.pts) {
+              break;
+            }
+
+            if (gop.pts > align.pts) {
+              // this current gop starts after the current gop we want to align on, so increment
+              // align index
+              alignIndex++;
+              continue;
+            }
+
+            // current gop starts before the current gop we want to align on. so increment gop
+            // index
+            gopIndex++;
+            byteLength -= gop.byteLength;
+            nalCount -= gop.nalCount;
+            duration -= gop.duration;
+          }
+
+          if (gopIndex === 0) {
+            // no gops to trim
+            return gops;
+          }
+
+          if (gopIndex === gops.length) {
+            // all gops trimmed, skip appending all gops
+            return null;
+          }
+
+          alignedGops = gops.slice(gopIndex);
+          alignedGops.byteLength = byteLength;
+          alignedGops.duration = duration;
+          alignedGops.nalCount = nalCount;
+          alignedGops.pts = alignedGops[0].pts;
+          alignedGops.dts = alignedGops[0].dts;
+
+          return alignedGops;
+        };
+
+        // trim gop list to the first gop found that has a matching pts with a gop in the list
+        // of gopsToAlignWith starting from the END of the list
+        this.alignGopsAtEnd_ = function (gops) {
+          var alignIndex, gopIndex, align, gop, alignEndIndex, matchFound;
+
+          alignIndex = gopsToAlignWith.length - 1;
+          gopIndex = gops.length - 1;
+          alignEndIndex = null;
+          matchFound = false;
+
+          while (alignIndex >= 0 && gopIndex >= 0) {
+            align = gopsToAlignWith[alignIndex];
+            gop = gops[gopIndex];
+
+            if (align.pts === gop.pts) {
+              matchFound = true;
+              break;
+            }
+
+            if (align.pts > gop.pts) {
+              alignIndex--;
+              continue;
+            }
+
+            if (alignIndex === gopsToAlignWith.length - 1) {
+              // gop.pts is greater than the last alignment candidate. If no match is found
+              // by the end of this loop, we still want to append gops that come after this
+              // point
+              alignEndIndex = gopIndex;
+            }
+
+            gopIndex--;
+          }
+
+          if (!matchFound && alignEndIndex === null) {
+            return null;
+          }
+
+          var trimIndex;
+
+          if (matchFound) {
+            trimIndex = gopIndex;
+          } else {
+            trimIndex = alignEndIndex;
+          }
+
+          if (trimIndex === 0) {
+            return gops;
+          }
+
+          var alignedGops = gops.slice(trimIndex);
+          var metadata = alignedGops.reduce(function (total, gop) {
+            total.byteLength += gop.byteLength;
+            total.duration += gop.duration;
+            total.nalCount += gop.nalCount;
+            return total;
+          }, {byteLength: 0, duration: 0, nalCount: 0});
+
+          alignedGops.byteLength = metadata.byteLength;
+          alignedGops.duration = metadata.duration;
+          alignedGops.nalCount = metadata.nalCount;
+          alignedGops.pts = alignedGops[0].pts;
+          alignedGops.dts = alignedGops[0].dts;
+
+          return alignedGops;
+        };
+
+        this.alignGopsWith = function (newGopsToAlignWith) {
+          gopsToAlignWith = newGopsToAlignWith;
+        };
+      };
+
+      _VideoSegmentStream.prototype = new stream();
+
+      /**
+       * Store information about the start and end of the track and the
+       * duration for each frame/sample we process in order to calculate
+       * the baseMediaDecodeTime
+       */
+      collectDtsInfo = function collectDtsInfo(track, data) {
+        if (typeof data.pts === 'number') {
+          if (track.timelineStartInfo.pts === undefined) {
+            track.timelineStartInfo.pts = data.pts;
+          }
+
+          if (track.minSegmentPts === undefined) {
+            track.minSegmentPts = data.pts;
+          } else {
+            track.minSegmentPts = Math.min(track.minSegmentPts, data.pts);
+          }
+
+          if (track.maxSegmentPts === undefined) {
+            track.maxSegmentPts = data.pts;
+          } else {
+            track.maxSegmentPts = Math.max(track.maxSegmentPts, data.pts);
+          }
+        }
+
+        if (typeof data.dts === 'number') {
+          if (track.timelineStartInfo.dts === undefined) {
+            track.timelineStartInfo.dts = data.dts;
+          }
+
+          if (track.minSegmentDts === undefined) {
+            track.minSegmentDts = data.dts;
+          } else {
+            track.minSegmentDts = Math.min(track.minSegmentDts, data.dts);
+          }
+
+          if (track.maxSegmentDts === undefined) {
+            track.maxSegmentDts = data.dts;
+          } else {
+            track.maxSegmentDts = Math.max(track.maxSegmentDts, data.dts);
+          }
+        }
+      };
+
+      /**
+       * Clear values used to calculate the baseMediaDecodeTime between
+       * tracks
+       */
+      clearDtsInfo = function clearDtsInfo(track) {
+        delete track.minSegmentDts;
+        delete track.maxSegmentDts;
+        delete track.minSegmentPts;
+        delete track.maxSegmentPts;
+      };
+
+      /**
+       * Calculate the track's baseMediaDecodeTime based on the earliest
+       * DTS the transmuxer has ever seen and the minimum DTS for the
+       * current track
+       * @param track {object} track metadata configuration
+       * @param keepOriginalTimestamps {boolean} If true, keep the timestamps
+       *        in the source; false to adjust the first segment to start at 0.
+       */
+      calculateTrackBaseMediaDecodeTime = function calculateTrackBaseMediaDecodeTime(track, keepOriginalTimestamps) {
+        var baseMediaDecodeTime,
+          scale,
+          minSegmentDts = track.minSegmentDts;
+
+        // Optionally adjust the time so the first segment starts at zero.
+        if (!keepOriginalTimestamps) {
+          minSegmentDts -= track.timelineStartInfo.dts;
+        }
+
+        // track.timelineStartInfo.baseMediaDecodeTime is the location, in time, where
+        // we want the start of the first segment to be placed
+        baseMediaDecodeTime = track.timelineStartInfo.baseMediaDecodeTime;
+
+        // Add to that the distance this segment is from the very first
+        baseMediaDecodeTime += minSegmentDts;
+
+        // baseMediaDecodeTime must not become negative
+        baseMediaDecodeTime = Math.max(0, baseMediaDecodeTime);
+
+        if (track.type === 'audio') {
+          // Audio has a different clock equal to the sampling_rate so we need to
+          // scale the PTS values into the clock rate of the track
+          scale = track.samplerate / ONE_SECOND_IN_TS$1;
+          baseMediaDecodeTime *= scale;
+          baseMediaDecodeTime = Math.floor(baseMediaDecodeTime);
+        }
+
+        return baseMediaDecodeTime;
+      };
+
+      /**
+       * A Stream that can combine multiple streams (ie. audio & video)
+       * into a single output segment for MSE. Also supports audio-only
+       * and video-only streams.
+       */
+      _CoalesceStream = function CoalesceStream(options, metadataStream) {
+        // Number of Tracks per output segment
+        // If greater than 1, we combine multiple
+        // tracks into a single segment
+        this.numberOfTracks = 0;
+        this.metadataStream = metadataStream;
+
+        if (typeof options.remux !== 'undefined') {
+          this.remuxTracks = !!options.remux;
+        } else {
+          this.remuxTracks = true;
+        }
+
+        this.pendingTracks = [];
+        this.videoTrack = null;
+        this.pendingBoxes = [];
+        this.pendingCaptions = [];
+        this.pendingMetadata = [];
+        this.pendingBytes = 0;
+        this.emittedTracks = 0;
+
+        _CoalesceStream.prototype.init.call(this);
+
+        // Take output from multiple
+        this.push = function (output) {
+          // buffer incoming captions until the associated video segment
+          // finishes
+          if (output.text) {
+            return this.pendingCaptions.push(output);
+          }
+          // buffer incoming id3 tags until the final flush
+          if (output.frames) {
+            return this.pendingMetadata.push(output);
+          }
+
+          // Add this track to the list of pending tracks and store
+          // important information required for the construction of
+          // the final segment
+          this.pendingTracks.push(output.track);
+          this.pendingBoxes.push(output.boxes);
+          this.pendingBytes += output.boxes.byteLength;
+
+          if (output.track.type === 'video') {
+            this.videoTrack = output.track;
+          }
+          if (output.track.type === 'audio') {
+            this.audioTrack = output.track;
+          }
+        };
+      };
+
+      _CoalesceStream.prototype = new stream();
+      _CoalesceStream.prototype.flush = function (flushSource) {
+        var offset = 0,
+          event = {
+            captions: [],
+            captionStreams: {},
+            metadata: [],
+            info: {}
+          },
+          caption,
+          id3,
+          initSegment,
+          timelineStartPts = 0,
+          i;
+
+        if (this.pendingTracks.length < this.numberOfTracks) {
+          if (flushSource !== 'VideoSegmentStream' && flushSource !== 'AudioSegmentStream') {
+            // Return because we haven't received a flush from a data-generating
+            // portion of the segment (meaning that we have only recieved meta-data
+            // or captions.)
+            return;
+          } else if (this.remuxTracks) {
+            // Return until we have enough tracks from the pipeline to remux (if we
+            // are remuxing audio and video into a single MP4)
+            return;
+          } else if (this.pendingTracks.length === 0) {
+            // In the case where we receive a flush without any data having been
+            // received we consider it an emitted track for the purposes of coalescing
+            // `done` events.
+            // We do this for the case where there is an audio and video track in the
+            // segment but no audio data. (seen in several playlists with alternate
+            // audio tracks and no audio present in the main TS segments.)
+            this.emittedTracks++;
+
+            if (this.emittedTracks >= this.numberOfTracks) {
+              this.trigger('done');
+              this.emittedTracks = 0;
+            }
+            return;
+          }
+        }
+
+        if (this.videoTrack) {
+          timelineStartPts = this.videoTrack.timelineStartInfo.pts;
+          VIDEO_PROPERTIES.forEach(function (prop) {
+            event.info[prop] = this.videoTrack[prop];
+          }, this);
+        } else if (this.audioTrack) {
+          timelineStartPts = this.audioTrack.timelineStartInfo.pts;
+          AUDIO_PROPERTIES.forEach(function (prop) {
+            event.info[prop] = this.audioTrack[prop];
+          }, this);
+        }
+
+        if (this.pendingTracks.length === 1) {
+          event.type = this.pendingTracks[0].type;
+        } else {
+          event.type = 'combined';
+        }
+
+        this.emittedTracks += this.pendingTracks.length;
+
+        initSegment = mp4Generator.initSegment(this.pendingTracks);
+
+        // Create a new typed array to hold the init segment
+        event.initSegment = new Uint8Array(initSegment.byteLength);
+
+        // Create an init segment containing a moov
+        // and track definitions
+        event.initSegment.set(initSegment);
+
+        // Create a new typed array to hold the moof+mdats
+        event.data = new Uint8Array(this.pendingBytes);
+
+        // Append each moof+mdat (one per track) together
+        for (i = 0; i < this.pendingBoxes.length; i++) {
+          event.data.set(this.pendingBoxes[i], offset);
+          offset += this.pendingBoxes[i].byteLength;
+        }
+
+        // Translate caption PTS times into second offsets into the
+        // video timeline for the segment, and add track info
+        for (i = 0; i < this.pendingCaptions.length; i++) {
+          caption = this.pendingCaptions[i];
+          caption.startTime = caption.startPts - timelineStartPts;
+          caption.startTime /= 90e3;
+          caption.endTime = caption.endPts - timelineStartPts;
+          caption.endTime /= 90e3;
+          event.captionStreams[caption.stream] = true;
+          event.captions.push(caption);
+        }
+
+        // Translate ID3 frame PTS times into second offsets into the
+        // video timeline for the segment
+        for (i = 0; i < this.pendingMetadata.length; i++) {
+          id3 = this.pendingMetadata[i];
+          id3.cueTime = id3.pts - timelineStartPts;
+          id3.cueTime /= 90e3;
+          event.metadata.push(id3);
+        }
+        // We add this to every single emitted segment even though we only need
+        // it for the first
+        event.metadata.dispatchType = this.metadataStream.dispatchType;
+
+        // Reset stream state
+        this.pendingTracks.length = 0;
+        this.videoTrack = null;
+        this.pendingBoxes.length = 0;
+        this.pendingCaptions.length = 0;
+        this.pendingBytes = 0;
+        this.pendingMetadata.length = 0;
+
+        // Emit the built segment
+        this.trigger('data', event);
+
+        // Only emit `done` if all tracks have been flushed and emitted
+        if (this.emittedTracks >= this.numberOfTracks) {
+          this.trigger('done');
+          this.emittedTracks = 0;
+        }
+      };
+      /**
+       * A Stream that expects MP2T binary data as input and produces
+       * corresponding media segments, suitable for use with Media Source
+       * Extension (MSE) implementations that support the ISO BMFF byte
+       * stream format, like Chrome.
+       */
+      _Transmuxer = function Transmuxer(options) {
+        var self = this,
+          hasFlushed = true,
+          videoTrack,
+          audioTrack;
+
+        _Transmuxer.prototype.init.call(this);
+
+        options = options || {};
+        this.baseMediaDecodeTime = options.baseMediaDecodeTime || 0;
+        this.transmuxPipeline_ = {};
+
+        this.setupAacPipeline = function () {
+          var pipeline = {};
+          this.transmuxPipeline_ = pipeline;
+
+          pipeline.type = 'aac';
+          pipeline.metadataStream = new m2ts_1.MetadataStream();
+
+          // set up the parsing pipeline
+          pipeline.aacStream = new aac();
+          pipeline.audioTimestampRolloverStream = new m2ts_1.TimestampRolloverStream('audio');
+          pipeline.timedMetadataTimestampRolloverStream = new m2ts_1.TimestampRolloverStream('timed-metadata');
+          pipeline.adtsStream = new adts();
+          pipeline.coalesceStream = new _CoalesceStream(options, pipeline.metadataStream);
+          pipeline.headOfPipeline = pipeline.aacStream;
+
+          pipeline.aacStream.pipe(pipeline.audioTimestampRolloverStream).pipe(pipeline.adtsStream);
+          pipeline.aacStream.pipe(pipeline.timedMetadataTimestampRolloverStream).pipe(pipeline.metadataStream).pipe(pipeline.coalesceStream);
+
+          pipeline.metadataStream.on('timestamp', function (frame) {
+            pipeline.aacStream.setTimestamp(frame.timeStamp);
+          });
+
+          pipeline.aacStream.on('data', function (data) {
+            if (data.type === 'timed-metadata' && !pipeline.audioSegmentStream) {
+              audioTrack = audioTrack || {
+                timelineStartInfo: {
+                  baseMediaDecodeTime: self.baseMediaDecodeTime
+                },
+                codec: 'adts',
+                type: 'audio'
+              };
+              // hook up the audio segment stream to the first track with aac data
+              pipeline.coalesceStream.numberOfTracks++;
+              pipeline.audioSegmentStream = new _AudioSegmentStream(audioTrack, options);
+              // Set up the final part of the audio pipeline
+              pipeline.adtsStream.pipe(pipeline.audioSegmentStream).pipe(pipeline.coalesceStream);
+            }
+          });
+
+          // Re-emit any data coming from the coalesce stream to the outside world
+          pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data'));
+          // Let the consumer know we have finished flushing the entire pipeline
+          pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done'));
+        };
+
+        this.setupTsPipeline = function () {
+          var pipeline = {};
+          this.transmuxPipeline_ = pipeline;
+
+          pipeline.type = 'ts';
+          pipeline.metadataStream = new m2ts_1.MetadataStream();
+
+          // set up the parsing pipeline
+          pipeline.packetStream = new m2ts_1.TransportPacketStream();
+          pipeline.parseStream = new m2ts_1.TransportParseStream();
+          pipeline.elementaryStream = new m2ts_1.ElementaryStream();
+          pipeline.videoTimestampRolloverStream = new m2ts_1.TimestampRolloverStream('video');
+          pipeline.audioTimestampRolloverStream = new m2ts_1.TimestampRolloverStream('audio');
+          pipeline.timedMetadataTimestampRolloverStream = new m2ts_1.TimestampRolloverStream('timed-metadata');
+          pipeline.adtsStream = new adts();
+          pipeline.h264Stream = new H264Stream();
+          pipeline.captionStream = new m2ts_1.CaptionStream();
+          pipeline.coalesceStream = new _CoalesceStream(options, pipeline.metadataStream);
+          pipeline.headOfPipeline = pipeline.packetStream;
+
+          // disassemble MPEG2-TS packets into elementary streams
+          pipeline.packetStream.pipe(pipeline.parseStream).pipe(pipeline.elementaryStream);
+
+          // !!THIS ORDER IS IMPORTANT!!
+          // demux the streams
+          pipeline.elementaryStream.pipe(pipeline.videoTimestampRolloverStream).pipe(pipeline.h264Stream);
+          pipeline.elementaryStream.pipe(pipeline.audioTimestampRolloverStream).pipe(pipeline.adtsStream);
+
+          pipeline.elementaryStream.pipe(pipeline.timedMetadataTimestampRolloverStream).pipe(pipeline.metadataStream).pipe(pipeline.coalesceStream);
+
+          // Hook up CEA-608/708 caption stream
+          pipeline.h264Stream.pipe(pipeline.captionStream).pipe(pipeline.coalesceStream);
+
+          pipeline.elementaryStream.on('data', function (data) {
+            var i;
+
+            if (data.type === 'metadata') {
+              i = data.tracks.length;
+
+              // scan the tracks listed in the metadata
+              while (i--) {
+                if (!videoTrack && data.tracks[i].type === 'video') {
+                  videoTrack = data.tracks[i];
+                  videoTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime;
+                } else if (!audioTrack && data.tracks[i].type === 'audio') {
+                  audioTrack = data.tracks[i];
+                  audioTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime;
+                }
+              }
+
+              // hook up the video segment stream to the first track with h264 data
+              if (videoTrack && !pipeline.videoSegmentStream) {
+                pipeline.coalesceStream.numberOfTracks++;
+                pipeline.videoSegmentStream = new _VideoSegmentStream(videoTrack, options);
+
+                pipeline.videoSegmentStream.on('timelineStartInfo', function (timelineStartInfo) {
+                  // When video emits timelineStartInfo data after a flush, we forward that
+                  // info to the AudioSegmentStream, if it exists, because video timeline
+                  // data takes precedence.
+                  if (audioTrack) {
+                    audioTrack.timelineStartInfo = timelineStartInfo;
+                    // On the first segment we trim AAC frames that exist before the
+                    // very earliest DTS we have seen in video because Chrome will
+                    // interpret any video track with a baseMediaDecodeTime that is
+                    // non-zero as a gap.
+                    pipeline.audioSegmentStream.setEarliestDts(timelineStartInfo.dts);
+                  }
+                });
+
+                pipeline.videoSegmentStream.on('processedGopsInfo', self.trigger.bind(self, 'gopInfo'));
+
+                pipeline.videoSegmentStream.on('baseMediaDecodeTime', function (baseMediaDecodeTime) {
+                  if (audioTrack) {
+                    pipeline.audioSegmentStream.setVideoBaseMediaDecodeTime(baseMediaDecodeTime);
+                  }
+                });
+
+                // Set up the final part of the video pipeline
+                pipeline.h264Stream.pipe(pipeline.videoSegmentStream).pipe(pipeline.coalesceStream);
+              }
+
+              if (audioTrack && !pipeline.audioSegmentStream) {
+                // hook up the audio segment stream to the first track with aac data
+                pipeline.coalesceStream.numberOfTracks++;
+                pipeline.audioSegmentStream = new _AudioSegmentStream(audioTrack, options);
+
+                // Set up the final part of the audio pipeline
+                pipeline.adtsStream.pipe(pipeline.audioSegmentStream).pipe(pipeline.coalesceStream);
+              }
+            }
+          });
+
+          // Re-emit any data coming from the coalesce stream to the outside world
+          pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data'));
+          // Let the consumer know we have finished flushing the entire pipeline
+          pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done'));
+        };
+
+        // hook up the segment streams once track metadata is delivered
+        this.setBaseMediaDecodeTime = function (baseMediaDecodeTime) {
+          var pipeline = this.transmuxPipeline_;
+
+          this.baseMediaDecodeTime = baseMediaDecodeTime;
+          if (audioTrack) {
+            audioTrack.timelineStartInfo.dts = undefined;
+            audioTrack.timelineStartInfo.pts = undefined;
+            clearDtsInfo(audioTrack);
+            audioTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime;
+            if (pipeline.audioTimestampRolloverStream) {
+              pipeline.audioTimestampRolloverStream.discontinuity();
+            }
+          }
+          if (videoTrack) {
+            if (pipeline.videoSegmentStream) {
+              pipeline.videoSegmentStream.gopCache_ = [];
+              pipeline.videoTimestampRolloverStream.discontinuity();
+            }
+            videoTrack.timelineStartInfo.dts = undefined;
+            videoTrack.timelineStartInfo.pts = undefined;
+            clearDtsInfo(videoTrack);
+            pipeline.captionStream.reset();
+            videoTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime;
+          }
+
+          if (pipeline.timedMetadataTimestampRolloverStream) {
+            pipeline.timedMetadataTimestampRolloverStream.discontinuity();
+          }
+        };
+
+        this.setAudioAppendStart = function (timestamp) {
+          if (audioTrack) {
+            this.transmuxPipeline_.audioSegmentStream.setAudioAppendStart(timestamp);
+          }
+        };
+
+        this.alignGopsWith = function (gopsToAlignWith) {
+          if (videoTrack && this.transmuxPipeline_.videoSegmentStream) {
+            this.transmuxPipeline_.videoSegmentStream.alignGopsWith(gopsToAlignWith);
+          }
+        };
+
+        // feed incoming data to the front of the parsing pipeline
+        this.push = function (data) {
+          if (hasFlushed) {
+            var isAac = isLikelyAacData(data);
+
+            if (isAac && this.transmuxPipeline_.type !== 'aac') {
+              this.setupAacPipeline();
+            } else if (!isAac && this.transmuxPipeline_.type !== 'ts') {
+              this.setupTsPipeline();
+            }
+            hasFlushed = false;
+          }
+          this.transmuxPipeline_.headOfPipeline.push(data);
+        };
+
+        // flush any buffered data
+        this.flush = function () {
+          hasFlushed = true;
+          // Start at the top of the pipeline and flush all pending work
+          this.transmuxPipeline_.headOfPipeline.flush();
+        };
+
+        // Caption data has to be reset when seeking outside buffered range
+        this.resetCaptions = function () {
+          if (this.transmuxPipeline_.captionStream) {
+            this.transmuxPipeline_.captionStream.reset();
+          }
+        };
+      };
+      _Transmuxer.prototype = new stream();
+
+      var transmuxer = {
+        Transmuxer: _Transmuxer,
+        VideoSegmentStream: _VideoSegmentStream,
+        AudioSegmentStream: _AudioSegmentStream,
+        AUDIO_PROPERTIES: AUDIO_PROPERTIES,
+        VIDEO_PROPERTIES: VIDEO_PROPERTIES
+      };
+
+      var mp4 = {
+        generator: mp4Generator,
+        Transmuxer: transmuxer.Transmuxer,
+        AudioSegmentStream: transmuxer.AudioSegmentStream,
+        VideoSegmentStream: transmuxer.VideoSegmentStream
+      };
+
+      var classCallCheck$$1 = function classCallCheck$$1(instance, Constructor) {
+        if (!(instance instanceof Constructor)) {
+          throw new TypeError("Cannot call a class as a function");
+        }
+      };
+
+      var createClass$$1 = function () {
+        function defineProperties(target, props) {
+          for (var i = 0; i < props.length; i++) {
+            var descriptor = props[i];
+            descriptor.enumerable = descriptor.enumerable || false;
+            descriptor.configurable = true;
+            if ("value" in descriptor) descriptor.writable = true;
+            Object.defineProperty(target, descriptor.key, descriptor);
+          }
+        }
+
+        return function (Constructor, protoProps, staticProps) {
+          if (protoProps) defineProperties(Constructor.prototype, protoProps);
+          if (staticProps) defineProperties(Constructor, staticProps);
+          return Constructor;
+        };
+      }();
+
+      /**
+       * @file transmuxer-worker.js
+       */
+
+      /**
+       * Re-emits transmuxer events by converting them into messages to the
+       * world outside the worker.
+       *
+       * @param {Object} transmuxer the transmuxer to wire events on
+       * @private
+       */
+      var wireTransmuxerEvents = function wireTransmuxerEvents(transmuxer) {
+        transmuxer.on('data', function (segment) {
+          // transfer ownership of the underlying ArrayBuffer
+          // instead of doing a copy to save memory
+          // ArrayBuffers are transferable but generic TypedArrays are not
+          // @link https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Passing_data_by_transferring_ownership_(transferable_objects)
+          var initArray = segment.initSegment;
+
+          segment.initSegment = {
+            data: initArray.buffer,
+            byteOffset: initArray.byteOffset,
+            byteLength: initArray.byteLength
+          };
+
+          var typedArray = segment.data;
+
+          segment.data = typedArray.buffer;
+          window_1$$1.postMessage({
+            action: 'data',
+            segment: segment,
+            byteOffset: typedArray.byteOffset,
+            byteLength: typedArray.byteLength
+          }, [segment.data]);
+        });
+
+        if (transmuxer.captionStream) {
+          transmuxer.captionStream.on('data', function (caption) {
+            window_1$$1.postMessage({
+              action: 'caption',
+              data: caption
+            });
+          });
+        }
+
+        transmuxer.on('done', function (data) {
+          window_1$$1.postMessage({action: 'done'});
+        });
+
+        transmuxer.on('gopInfo', function (gopInfo) {
+          window_1$$1.postMessage({
+            action: 'gopInfo',
+            gopInfo: gopInfo
+          });
+        });
+      };
+
+      /**
+       * All incoming messages route through this hash. If no function exists
+       * to handle an incoming message, then we ignore the message.
+       *
+       * @class MessageHandlers
+       * @param {Object} options the options to initialize with
+       */
+
+      var MessageHandlers = function () {
+        function MessageHandlers(options) {
+          classCallCheck$$1(this, MessageHandlers);
+
+          this.options = options || {};
+          this.init();
+        }
+
+        /**
+         * initialize our web worker and wire all the events.
+         */
+
+        createClass$$1(MessageHandlers, [{
+          key: 'init',
+          value: function init() {
+            if (this.transmuxer) {
+              this.transmuxer.dispose();
+            }
+            this.transmuxer = new mp4.Transmuxer(this.options);
+            wireTransmuxerEvents(this.transmuxer);
+          }
+
+          /**
+           * Adds data (a ts segment) to the start of the transmuxer pipeline for
+           * processing.
+           *
+           * @param {ArrayBuffer} data data to push into the muxer
+           */
+
+        }, {
+          key: 'push',
+          value: function push(data) {
+            // Cast array buffer to correct type for transmuxer
+            var segment = new Uint8Array(data.data, data.byteOffset, data.byteLength);
+
+            this.transmuxer.push(segment);
+          }
+
+          /**
+           * Recreate the transmuxer so that the next segment added via `push`
+           * start with a fresh transmuxer.
+           */
+
+        }, {
+          key: 'reset',
+          value: function reset() {
+            this.init();
+          }
+
+          /**
+           * Set the value that will be used as the `baseMediaDecodeTime` time for the
+           * next segment pushed in. Subsequent segments will have their `baseMediaDecodeTime`
+           * set relative to the first based on the PTS values.
+           *
+           * @param {Object} data used to set the timestamp offset in the muxer
+           */
+
+        }, {
+          key: 'setTimestampOffset',
+          value: function setTimestampOffset(data) {
+            var timestampOffset = data.timestampOffset || 0;
+
+            this.transmuxer.setBaseMediaDecodeTime(Math.round(timestampOffset * 90000));
+          }
+        }, {
+          key: 'setAudioAppendStart',
+          value: function setAudioAppendStart(data) {
+            this.transmuxer.setAudioAppendStart(Math.ceil(data.appendStart * 90000));
+          }
+
+          /**
+           * Forces the pipeline to finish processing the last segment and emit it's
+           * results.
+           *
+           * @param {Object} data event data, not really used
+           */
+
+        }, {
+          key: 'flush',
+          value: function flush(data) {
+            this.transmuxer.flush();
+          }
+        }, {
+          key: 'resetCaptions',
+          value: function resetCaptions() {
+            this.transmuxer.resetCaptions();
+          }
+        }, {
+          key: 'alignGopsWith',
+          value: function alignGopsWith(data) {
+            this.transmuxer.alignGopsWith(data.gopsToAlignWith.slice());
+          }
+        }]);
+        return MessageHandlers;
+      }();
+
+      /**
+       * Our web wroker interface so that things can talk to mux.js
+       * that will be running in a web worker. the scope is passed to this by
+       * webworkify.
+       *
+       * @param {Object} self the scope for the web worker
+       */
+
+      var TransmuxerWorker = function TransmuxerWorker(self) {
+        self.onmessage = function (event) {
+          if (event.data.action === 'init' && event.data.options) {
+            this.messageHandlers = new MessageHandlers(event.data.options);
+            return;
+          }
+
+          if (!this.messageHandlers) {
+            this.messageHandlers = new MessageHandlers();
+          }
+
+          if (event.data && event.data.action && event.data.action !== 'init') {
+            if (this.messageHandlers[event.data.action]) {
+              this.messageHandlers[event.data.action](event.data);
+            }
+          }
+        };
+      };
+
+      var transmuxerWorker = new TransmuxerWorker(self);
+
+      return transmuxerWorker;
+    }();
+  });
+
+  /**
+   * @file codec-utils.js
+   */
+
+  /**
+   * Check if a codec string refers to an audio codec.
+   *
+   * @param {String} codec codec string to check
+   * @return {Boolean} if this is an audio codec
+   * @private
+   */
+  var isAudioCodec = function isAudioCodec(codec) {
+    return (/mp4a\.\d+.\d+/i.test(codec)
+    );
+  };
+
+  /**
+   * Check if a codec string refers to a video codec.
+   *
+   * @param {String} codec codec string to check
+   * @return {Boolean} if this is a video codec
+   * @private
+   */
+  var isVideoCodec = function isVideoCodec(codec) {
+    return (/avc1\.[\da-f]+/i.test(codec)
+    );
+  };
+
+  /**
+   * Parse a content type header into a type and parameters
+   * object
+   *
+   * @param {String} type the content type header
+   * @return {Object} the parsed content-type
+   * @private
+   */
+  var parseContentType = function parseContentType(type) {
+    var object = {type: '', parameters: {}};
+    var parameters = type.trim().split(';');
+
+    // first parameter should always be content-type
+    object.type = parameters.shift().trim();
+    parameters.forEach(function (parameter) {
+      var pair = parameter.trim().split('=');
+
+      if (pair.length > 1) {
+        var name = pair[0].replace(/"/g, '').trim();
+        var value = pair[1].replace(/"/g, '').trim();
+
+        object.parameters[name] = value;
+      }
+    });
+
+    return object;
+  };
+
+  /**
+   * Replace the old apple-style `avc1.<dd>.<dd>` codec string with the standard
+   * `avc1.<hhhhhh>`
+   *
+   * @param {Array} codecs an array of codec strings to fix
+   * @return {Array} the translated codec array
+   * @private
+   */
+  var translateLegacyCodecs = function translateLegacyCodecs(codecs) {
+    return codecs.map(function (codec) {
+      return codec.replace(/avc1\.(\d+)\.(\d+)/i, function (orig, profile, avcLevel) {
+        var profileHex = ('00' + Number(profile).toString(16)).slice(-2);
+        var avcLevelHex = ('00' + Number(avcLevel).toString(16)).slice(-2);
+
+        return 'avc1.' + profileHex + '00' + avcLevelHex;
+      });
+    });
+  };
+
+  /**
+   * @file virtual-source-buffer.js
+   */
+
+    // We create a wrapper around the SourceBuffer so that we can manage the
+    // state of the `updating` property manually. We have to do this because
+    // Firefox changes `updating` to false long before triggering `updateend`
+    // events and that was causing strange problems in videojs-contrib-hls
+  var makeWrappedSourceBuffer = function makeWrappedSourceBuffer(mediaSource, mimeType) {
+      var sourceBuffer = mediaSource.addSourceBuffer(mimeType);
+      var wrapper = Object.create(null);
+
+      wrapper.updating = false;
+      wrapper.realBuffer_ = sourceBuffer;
+
+      var _loop = function _loop(key) {
+        if (typeof sourceBuffer[key] === 'function') {
+          wrapper[key] = function () {
+            return sourceBuffer[key].apply(sourceBuffer, arguments);
+          };
+        } else if (typeof wrapper[key] === 'undefined') {
+          Object.defineProperty(wrapper, key, {
+            get: function get$$1() {
+              return sourceBuffer[key];
+            },
+            set: function set$$1(v) {
+              return sourceBuffer[key] = v;
+            }
+          });
+        }
+      };
+
+      for (var key in sourceBuffer) {
+        _loop(key);
+      }
+
+      return wrapper;
+    };
+
+  /**
+   * Returns a list of gops in the buffer that have a pts value of 3 seconds or more in
+   * front of current time.
+   *
+   * @param {Array} buffer
+   *        The current buffer of gop information
+   * @param {Player} player
+   *        The player instance
+   * @param {Double} mapping
+   *        Offset to map display time to stream presentation time
+   * @return {Array}
+   *         List of gops considered safe to append over
+   */
+  var gopsSafeToAlignWith = function gopsSafeToAlignWith(buffer, player, mapping) {
+    if (!player || !buffer.length) {
+      return [];
+    }
+
+    // pts value for current time + 3 seconds to give a bit more wiggle room
+    var currentTimePts = Math.ceil((player.currentTime() - mapping + 3) * 90000);
+
+    var i = void 0;
+
+    for (i = 0; i < buffer.length; i++) {
+      if (buffer[i].pts > currentTimePts) {
+        break;
+      }
+    }
+
+    return buffer.slice(i);
+  };
+
+  /**
+   * Appends gop information (timing and byteLength) received by the transmuxer for the
+   * gops appended in the last call to appendBuffer
+   *
+   * @param {Array} buffer
+   *        The current buffer of gop information
+   * @param {Array} gops
+   *        List of new gop information
+   * @param {boolean} replace
+   *        If true, replace the buffer with the new gop information. If false, append the
+   *        new gop information to the buffer in the right location of time.
+   * @return {Array}
+   *         Updated list of gop information
+   */
+  var updateGopBuffer = function updateGopBuffer(buffer, gops, replace) {
+    if (!gops.length) {
+      return buffer;
+    }
+
+    if (replace) {
+      // If we are in safe append mode, then completely overwrite the gop buffer
+      // with the most recent appeneded data. This will make sure that when appending
+      // future segments, we only try to align with gops that are both ahead of current
+      // time and in the last segment appended.
+      return gops.slice();
+    }
+
+    var start = gops[0].pts;
+
+    var i = 0;
+
+    for (i; i < buffer.length; i++) {
+      if (buffer[i].pts >= start) {
+        break;
+      }
+    }
+
+    return buffer.slice(0, i).concat(gops);
+  };
+
+  /**
+   * Removes gop information in buffer that overlaps with provided start and end
+   *
+   * @param {Array} buffer
+   *        The current buffer of gop information
+   * @param {Double} start
+   *        position to start the remove at
+   * @param {Double} end
+   *        position to end the remove at
+   * @param {Double} mapping
+   *        Offset to map display time to stream presentation time
+   */
+  var removeGopBuffer = function removeGopBuffer(buffer, start, end, mapping) {
+    var startPts = Math.ceil((start - mapping) * 90000);
+    var endPts = Math.ceil((end - mapping) * 90000);
+    var updatedBuffer = buffer.slice();
+
+    var i = buffer.length;
+
+    while (i--) {
+      if (buffer[i].pts <= endPts) {
+        break;
+      }
+    }
+
+    if (i === -1) {
+      // no removal because end of remove range is before start of buffer
+      return updatedBuffer;
+    }
+
+    var j = i + 1;
+
+    while (j--) {
+      if (buffer[j].pts <= startPts) {
+        break;
+      }
+    }
+
+    // clamp remove range start to 0 index
+    j = Math.max(j, 0);
+
+    updatedBuffer.splice(j, i - j + 1);
+
+    return updatedBuffer;
+  };
+
+  /**
+   * VirtualSourceBuffers exist so that we can transmux non native formats
+   * into a native format, but keep the same api as a native source buffer.
+   * It creates a transmuxer, that works in its own thread (a web worker) and
+   * that transmuxer muxes the data into a native format. VirtualSourceBuffer will
+   * then send all of that data to the naive sourcebuffer so that it is
+   * indestinguishable from a natively supported format.
+   *
+   * @param {HtmlMediaSource} mediaSource the parent mediaSource
+   * @param {Array} codecs array of codecs that we will be dealing with
+   * @class VirtualSourceBuffer
+   * @extends video.js.EventTarget
+   */
+
+  var VirtualSourceBuffer = function (_videojs$EventTarget) {
+    inherits(VirtualSourceBuffer, _videojs$EventTarget);
+
+    function VirtualSourceBuffer(mediaSource, codecs) {
+      classCallCheck(this, VirtualSourceBuffer);
+
+      var _this7 = possibleConstructorReturn(this, _videojs$EventTarget.call(this, videojs$1.EventTarget));
+
+      _this7.timestampOffset_ = 0;
+      _this7.pendingBuffers_ = [];
+      _this7.bufferUpdating_ = false;
+
+      _this7.mediaSource_ = mediaSource;
+      _this7.codecs_ = codecs;
+      _this7.audioCodec_ = null;
+      _this7.videoCodec_ = null;
+      _this7.audioDisabled_ = false;
+      _this7.appendAudioInitSegment_ = true;
+      _this7.gopBuffer_ = [];
+      _this7.timeMapping_ = 0;
+      _this7.safeAppend_ = videojs$1.browser.IE_VERSION >= 11;
+
+      var options = {
+        remux: false,
+        alignGopsAtEnd: _this7.safeAppend_
+      };
+
+      _this7.codecs_.forEach(function (codec) {
+        if (isAudioCodec(codec)) {
+          _this7.audioCodec_ = codec;
+        } else if (isVideoCodec(codec)) {
+          _this7.videoCodec_ = codec;
+        }
+      });
+
+      // append muxed segments to their respective native buffers as
+      // soon as they are available
+      _this7.transmuxer_ = new TransmuxWorker();
+      _this7.transmuxer_.postMessage({action: 'init', options: options});
+
+      _this7.transmuxer_.onmessage = function (event) {
+        if (event.data.action === 'data') {
+          return _this7.data_(event);
+        }
+
+        if (event.data.action === 'done') {
+          return _this7.done_(event);
+        }
+
+        if (event.data.action === 'gopInfo') {
+          return _this7.appendGopInfo_(event);
+        }
+      };
+
+      // this timestampOffset is a property with the side-effect of resetting
+      // baseMediaDecodeTime in the transmuxer on the setter
+      Object.defineProperty(_this7, 'timestampOffset', {
+        get: function get$$1() {
+          return this.timestampOffset_;
+        },
+        set: function set$$1(val) {
+          if (typeof val === 'number' && val >= 0) {
+            this.timestampOffset_ = val;
+            this.appendAudioInitSegment_ = true;
+
+            // reset gop buffer on timestampoffset as this signals a change in timeline
+            this.gopBuffer_.length = 0;
+            this.timeMapping_ = 0;
+
+            // We have to tell the transmuxer to set the baseMediaDecodeTime to
+            // the desired timestampOffset for the next segment
+            this.transmuxer_.postMessage({
+              action: 'setTimestampOffset',
+              timestampOffset: val
+            });
+          }
+        }
+      });
+
+      // setting the append window affects both source buffers
+      Object.defineProperty(_this7, 'appendWindowStart', {
+        get: function get$$1() {
+          return (this.videoBuffer_ || this.audioBuffer_).appendWindowStart;
+        },
+        set: function set$$1(start) {
+          if (this.videoBuffer_) {
+            this.videoBuffer_.appendWindowStart = start;
+          }
+          if (this.audioBuffer_) {
+            this.audioBuffer_.appendWindowStart = start;
+          }
+        }
+      });
+
+      // this buffer is "updating" if either of its native buffers are
+      Object.defineProperty(_this7, 'updating', {
+        get: function get$$1() {
+          return !!(this.bufferUpdating_ || !this.audioDisabled_ && this.audioBuffer_ && this.audioBuffer_.updating || this.videoBuffer_ && this.videoBuffer_.updating);
+        }
+      });
+
+      // the buffered property is the intersection of the buffered
+      // ranges of the native source buffers
+      Object.defineProperty(_this7, 'buffered', {
+        get: function get$$1() {
+          var start = null;
+          var end = null;
+          var arity = 0;
+          var extents = [];
+          var ranges = [];
+
+          // neither buffer has been created yet
+          if (!this.videoBuffer_ && !this.audioBuffer_) {
+            return videojs$1.createTimeRange();
+          }
+
+          // only one buffer is configured
+          if (!this.videoBuffer_) {
+            return this.audioBuffer_.buffered;
+          }
+          if (!this.audioBuffer_) {
+            return this.videoBuffer_.buffered;
+          }
+
+          // both buffers are configured
+          if (this.audioDisabled_) {
+            return this.videoBuffer_.buffered;
+          }
+
+          // both buffers are empty
+          if (this.videoBuffer_.buffered.length === 0 && this.audioBuffer_.buffered.length === 0) {
+            return videojs$1.createTimeRange();
+          }
+
+          // Handle the case where we have both buffers and create an
+          // intersection of the two
+          var videoBuffered = this.videoBuffer_.buffered;
+          var audioBuffered = this.audioBuffer_.buffered;
+          var count = videoBuffered.length;
+
+          // A) Gather up all start and end times
+          while (count--) {
+            extents.push({time: videoBuffered.start(count), type: 'start'});
+            extents.push({time: videoBuffered.end(count), type: 'end'});
+          }
+          count = audioBuffered.length;
+          while (count--) {
+            extents.push({time: audioBuffered.start(count), type: 'start'});
+            extents.push({time: audioBuffered.end(count), type: 'end'});
+          }
+          // B) Sort them by time
+          extents.sort(function (a, b) {
+            return a.time - b.time;
+          });
+
+          // C) Go along one by one incrementing arity for start and decrementing
+          //    arity for ends
+          for (count = 0; count < extents.length; count++) {
+            if (extents[count].type === 'start') {
+              arity++;
+
+              // D) If arity is ever incremented to 2 we are entering an
+              //    overlapping range
+              if (arity === 2) {
+                start = extents[count].time;
+              }
+            } else if (extents[count].type === 'end') {
+              arity--;
+
+              // E) If arity is ever decremented to 1 we leaving an
+              //    overlapping range
+              if (arity === 1) {
+                end = extents[count].time;
+              }
+            }
+
+            // F) Record overlapping ranges
+            if (start !== null && end !== null) {
+              ranges.push([start, end]);
+              start = null;
+              end = null;
+            }
+          }
+
+          return videojs$1.createTimeRanges(ranges);
+        }
+      });
+      return _this7;
+    }
+
+    /**
+     * When we get a data event from the transmuxer
+     * we call this function and handle the data that
+     * was sent to us
+     *
+     * @private
+     * @param {Event} event the data event from the transmuxer
+     */
+
+
+    VirtualSourceBuffer.prototype.data_ = function data_(event) {
+      var segment = event.data.segment;
+
+      // Cast ArrayBuffer to TypedArray
+      segment.data = new Uint8Array(segment.data, event.data.byteOffset, event.data.byteLength);
+
+      segment.initSegment = new Uint8Array(segment.initSegment.data, segment.initSegment.byteOffset, segment.initSegment.byteLength);
+
+      createTextTracksIfNecessary(this, this.mediaSource_, segment);
+
+      // Add the segments to the pendingBuffers array
+      this.pendingBuffers_.push(segment);
+      return;
+    };
+
+    /**
+     * When we get a done event from the transmuxer
+     * we call this function and we process all
+     * of the pending data that we have been saving in the
+     * data_ function
+     *
+     * @private
+     * @param {Event} event the done event from the transmuxer
+     */
+
+
+    VirtualSourceBuffer.prototype.done_ = function done_(event) {
+      // Don't process and append data if the mediaSource is closed
+      if (this.mediaSource_.readyState === 'closed') {
+        this.pendingBuffers_.length = 0;
+        return;
+      }
+
+      // All buffers should have been flushed from the muxer
+      // start processing anything we have received
+      this.processPendingSegments_();
+      return;
+    };
+
+    /**
+     * Create our internal native audio/video source buffers and add
+     * event handlers to them with the following conditions:
+     * 1. they do not already exist on the mediaSource
+     * 2. this VSB has a codec for them
+     *
+     * @private
+     */
+
+
+    VirtualSourceBuffer.prototype.createRealSourceBuffers_ = function createRealSourceBuffers_() {
+      var _this8 = this;
+
+      var types = ['audio', 'video'];
+
+      types.forEach(function (type) {
+        // Don't create a SourceBuffer of this type if we don't have a
+        // codec for it
+        if (!_this8[type + 'Codec_']) {
+          return;
+        }
+
+        // Do nothing if a SourceBuffer of this type already exists
+        if (_this8[type + 'Buffer_']) {
+          return;
+        }
+
+        var buffer = null;
+
+        // If the mediasource already has a SourceBuffer for the codec
+        // use that
+        if (_this8.mediaSource_[type + 'Buffer_']) {
+          buffer = _this8.mediaSource_[type + 'Buffer_'];
+          // In multiple audio track cases, the audio source buffer is disabled
+          // on the main VirtualSourceBuffer by the HTMLMediaSource much earlier
+          // than createRealSourceBuffers_ is called to create the second
+          // VirtualSourceBuffer because that happens as a side-effect of
+          // videojs-contrib-hls starting the audioSegmentLoader. As a result,
+          // the audioBuffer is essentially "ownerless" and no one will toggle
+          // the `updating` state back to false once the `updateend` event is received
+          //
+          // Setting `updating` to false manually will work around this
+          // situation and allow work to continue
+          buffer.updating = false;
+        } else {
+          var codecProperty = type + 'Codec_';
+          var mimeType = type + '/mp4;codecs="' + _this8[codecProperty] + '"';
+
+          buffer = makeWrappedSourceBuffer(_this8.mediaSource_.nativeMediaSource_, mimeType);
+
+          _this8.mediaSource_[type + 'Buffer_'] = buffer;
+        }
+
+        _this8[type + 'Buffer_'] = buffer;
+
+        // Wire up the events to the SourceBuffer
+        ['update', 'updatestart', 'updateend'].forEach(function (event) {
+          buffer.addEventListener(event, function () {
+            // if audio is disabled
+            if (type === 'audio' && _this8.audioDisabled_) {
+              return;
+            }
+
+            if (event === 'updateend') {
+              _this8[type + 'Buffer_'].updating = false;
+            }
+
+            var shouldTrigger = types.every(function (t) {
+              // skip checking audio's updating status if audio
+              // is not enabled
+              if (t === 'audio' && _this8.audioDisabled_) {
+                return true;
+              }
+              // if the other type if updating we don't trigger
+              if (type !== t && _this8[t + 'Buffer_'] && _this8[t + 'Buffer_'].updating) {
+                return false;
+              }
+              return true;
+            });
+
+            if (shouldTrigger) {
+              return _this8.trigger(event);
+            }
+          });
+        });
+      });
+    };
+
+    /**
+     * Emulate the native mediasource function, but our function will
+     * send all of the proposed segments to the transmuxer so that we
+     * can transmux them before we append them to our internal
+     * native source buffers in the correct format.
+     *
+     * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/appendBuffer
+     * @param {Uint8Array} segment the segment to append to the buffer
+     */
+
+
+    VirtualSourceBuffer.prototype.appendBuffer = function appendBuffer(segment) {
+      // Start the internal "updating" state
+      this.bufferUpdating_ = true;
+
+      if (this.audioBuffer_ && this.audioBuffer_.buffered.length) {
+        var audioBuffered = this.audioBuffer_.buffered;
+
+        this.transmuxer_.postMessage({
+          action: 'setAudioAppendStart',
+          appendStart: audioBuffered.end(audioBuffered.length - 1)
+        });
+      }
+
+      if (this.videoBuffer_) {
+        this.transmuxer_.postMessage({
+          action: 'alignGopsWith',
+          gopsToAlignWith: gopsSafeToAlignWith(this.gopBuffer_, this.mediaSource_.player_, this.timeMapping_)
+        });
+      }
+
+      this.transmuxer_.postMessage({
+        action: 'push',
+        // Send the typed-array of data as an ArrayBuffer so that
+        // it can be sent as a "Transferable" and avoid the costly
+        // memory copy
+        data: segment.buffer,
+
+        // To recreate the original typed-array, we need information
+        // about what portion of the ArrayBuffer it was a view into
+        byteOffset: segment.byteOffset,
+        byteLength: segment.byteLength
+      }, [segment.buffer]);
+      this.transmuxer_.postMessage({action: 'flush'});
+    };
+
+    /**
+     * Appends gop information (timing and byteLength) received by the transmuxer for the
+     * gops appended in the last call to appendBuffer
+     *
+     * @param {Event} event
+     *        The gopInfo event from the transmuxer
+     * @param {Array} event.data.gopInfo
+     *        List of gop info to append
+     */
+
+
+    VirtualSourceBuffer.prototype.appendGopInfo_ = function appendGopInfo_(event) {
+      this.gopBuffer_ = updateGopBuffer(this.gopBuffer_, event.data.gopInfo, this.safeAppend_);
+    };
+
+    /**
+     * Emulate the native mediasource function and remove parts
+     * of the buffer from any of our internal buffers that exist
+     *
+     * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/remove
+     * @param {Double} start position to start the remove at
+     * @param {Double} end position to end the remove at
+     */
+
+
+    VirtualSourceBuffer.prototype.remove = function remove(start, end) {
+      if (this.videoBuffer_) {
+        this.videoBuffer_.updating = true;
+        this.videoBuffer_.remove(start, end);
+        this.gopBuffer_ = removeGopBuffer(this.gopBuffer_, start, end, this.timeMapping_);
+      }
+      if (!this.audioDisabled_ && this.audioBuffer_) {
+        this.audioBuffer_.updating = true;
+        this.audioBuffer_.remove(start, end);
+      }
+
+      // Remove Metadata Cues (id3)
+      removeCuesFromTrack(start, end, this.metadataTrack_);
+
+      // Remove Any Captions
+      if (this.inbandTextTracks_) {
+        for (var track in this.inbandTextTracks_) {
+          removeCuesFromTrack(start, end, this.inbandTextTracks_[track]);
+        }
+      }
+    };
+
+    /**
+     * Process any segments that the muxer has output
+     * Concatenate segments together based on type and append them into
+     * their respective sourceBuffers
+     *
+     * @private
+     */
+
+
+    VirtualSourceBuffer.prototype.processPendingSegments_ = function processPendingSegments_() {
+      var sortedSegments = {
+        video: {
+          segments: [],
+          bytes: 0
+        },
+        audio: {
+          segments: [],
+          bytes: 0
+        },
+        captions: [],
+        metadata: []
+      };
+
+      // Sort segments into separate video/audio arrays and
+      // keep track of their total byte lengths
+      sortedSegments = this.pendingBuffers_.reduce(function (segmentObj, segment) {
+        var type = segment.type;
+        var data = segment.data;
+        var initSegment = segment.initSegment;
+
+        segmentObj[type].segments.push(data);
+        segmentObj[type].bytes += data.byteLength;
+
+        segmentObj[type].initSegment = initSegment;
+
+        // Gather any captions into a single array
+        if (segment.captions) {
+          segmentObj.captions = segmentObj.captions.concat(segment.captions);
+        }
+
+        if (segment.info) {
+          segmentObj[type].info = segment.info;
+        }
+
+        // Gather any metadata into a single array
+        if (segment.metadata) {
+          segmentObj.metadata = segmentObj.metadata.concat(segment.metadata);
+        }
+
+        return segmentObj;
+      }, sortedSegments);
+
+      // Create the real source buffers if they don't exist by now since we
+      // finally are sure what tracks are contained in the source
+      if (!this.videoBuffer_ && !this.audioBuffer_) {
+        // Remove any codecs that may have been specified by default but
+        // are no longer applicable now
+        if (sortedSegments.video.bytes === 0) {
+          this.videoCodec_ = null;
+        }
+        if (sortedSegments.audio.bytes === 0) {
+          this.audioCodec_ = null;
+        }
+
+        this.createRealSourceBuffers_();
+      }
+
+      if (sortedSegments.audio.info) {
+        this.mediaSource_.trigger({type: 'audioinfo', info: sortedSegments.audio.info});
+      }
+      if (sortedSegments.video.info) {
+        this.mediaSource_.trigger({type: 'videoinfo', info: sortedSegments.video.info});
+      }
+
+      if (this.appendAudioInitSegment_) {
+        if (!this.audioDisabled_ && this.audioBuffer_) {
+          sortedSegments.audio.segments.unshift(sortedSegments.audio.initSegment);
+          sortedSegments.audio.bytes += sortedSegments.audio.initSegment.byteLength;
+        }
+        this.appendAudioInitSegment_ = false;
+      }
+
+      var triggerUpdateend = false;
+
+      // Merge multiple video and audio segments into one and append
+      if (this.videoBuffer_ && sortedSegments.video.bytes) {
+        sortedSegments.video.segments.unshift(sortedSegments.video.initSegment);
+        sortedSegments.video.bytes += sortedSegments.video.initSegment.byteLength;
+        this.concatAndAppendSegments_(sortedSegments.video, this.videoBuffer_);
+        // TODO: are video tracks the only ones with text tracks?
+        addTextTrackData(this, sortedSegments.captions, sortedSegments.metadata);
+      } else if (this.videoBuffer_ && (this.audioDisabled_ || !this.audioBuffer_)) {
+        // The transmuxer did not return any bytes of video, meaning it was all trimmed
+        // for gop alignment. Since we have a video buffer and audio is disabled, updateend
+        // will never be triggered by this source buffer, which will cause contrib-hls
+        // to be stuck forever waiting for updateend. If audio is not disabled, updateend
+        // will be triggered by the audio buffer, which will be sent upwards since the video
+        // buffer will not be in an updating state.
+        triggerUpdateend = true;
+      }
+
+      if (!this.audioDisabled_ && this.audioBuffer_) {
+        this.concatAndAppendSegments_(sortedSegments.audio, this.audioBuffer_);
+      }
+
+      this.pendingBuffers_.length = 0;
+
+      if (triggerUpdateend) {
+        this.trigger('updateend');
+      }
+
+      // We are no longer in the internal "updating" state
+      this.bufferUpdating_ = false;
+    };
+
+    /**
+     * Combine all segments into a single Uint8Array and then append them
+     * to the destination buffer
+     *
+     * @param {Object} segmentObj
+     * @param {SourceBuffer} destinationBuffer native source buffer to append data to
+     * @private
+     */
+
+
+    VirtualSourceBuffer.prototype.concatAndAppendSegments_ = function concatAndAppendSegments_(segmentObj, destinationBuffer) {
+      var offset = 0;
+      var tempBuffer = void 0;
+
+      if (segmentObj.bytes) {
+        tempBuffer = new Uint8Array(segmentObj.bytes);
+
+        // Combine the individual segments into one large typed-array
+        segmentObj.segments.forEach(function (segment) {
+          tempBuffer.set(segment, offset);
+          offset += segment.byteLength;
+        });
+
+        try {
+          destinationBuffer.updating = true;
+          destinationBuffer.appendBuffer(tempBuffer);
+        } catch (error) {
+          if (this.mediaSource_.player_) {
+            this.mediaSource_.player_.error({
+              code: -3,
+              type: 'APPEND_BUFFER_ERR',
+              message: error.message,
+              originalError: error
+            });
+          }
+        }
+      }
+    };
+
+    /**
+     * Emulate the native mediasource function. abort any soureBuffer
+     * actions and throw out any un-appended data.
+     *
+     * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/abort
+     */
+
+
+    VirtualSourceBuffer.prototype.abort = function abort() {
+      if (this.videoBuffer_) {
+        this.videoBuffer_.abort();
+      }
+      if (!this.audioDisabled_ && this.audioBuffer_) {
+        this.audioBuffer_.abort();
+      }
+      if (this.transmuxer_) {
+        this.transmuxer_.postMessage({action: 'reset'});
+      }
+      this.pendingBuffers_.length = 0;
+      this.bufferUpdating_ = false;
+    };
+
+    return VirtualSourceBuffer;
+  }(videojs$1.EventTarget);
+
+  /**
+   * @file html-media-source.js
+   */
+
+  /**
+   * Our MediaSource implementation in HTML, mimics native
+   * MediaSource where/if possible.
+   *
+   * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
+   * @class HtmlMediaSource
+   * @extends videojs.EventTarget
+   */
+
+
+  var HtmlMediaSource = function (_videojs$EventTarget2) {
+    inherits(HtmlMediaSource, _videojs$EventTarget2);
+
+    function HtmlMediaSource() {
+      classCallCheck(this, HtmlMediaSource);
+
+      var _this9 = possibleConstructorReturn(this, _videojs$EventTarget2.call(this));
+
+      var property = void 0;
+
+      _this9.nativeMediaSource_ = new window_1.MediaSource();
+      // delegate to the native MediaSource's methods by default
+      for (property in _this9.nativeMediaSource_) {
+        if (!(property in HtmlMediaSource.prototype) && typeof _this9.nativeMediaSource_[property] === 'function') {
+          _this9[property] = _this9.nativeMediaSource_[property].bind(_this9.nativeMediaSource_);
+        }
+      }
+
+      // emulate `duration` and `seekable` until seeking can be
+      // handled uniformly for live streams
+      // see https://github.com/w3c/media-source/issues/5
+      _this9.duration_ = NaN;
+      Object.defineProperty(_this9, 'duration', {
+        get: function get$$1() {
+          if (this.duration_ === Infinity) {
+            return this.duration_;
+          }
+          return this.nativeMediaSource_.duration;
+        },
+        set: function set$$1(duration) {
+          this.duration_ = duration;
+          if (duration !== Infinity) {
+            this.nativeMediaSource_.duration = duration;
+            return;
+          }
+        }
+      });
+      Object.defineProperty(_this9, 'seekable', {
+        get: function get$$1() {
+          if (this.duration_ === Infinity) {
+            return videojs$1.createTimeRanges([[0, this.nativeMediaSource_.duration]]);
+          }
+          return this.nativeMediaSource_.seekable;
+        }
+      });
+
+      Object.defineProperty(_this9, 'readyState', {
+        get: function get$$1() {
+          return this.nativeMediaSource_.readyState;
+        }
+      });
+
+      Object.defineProperty(_this9, 'activeSourceBuffers', {
+        get: function get$$1() {
+          return this.activeSourceBuffers_;
+        }
+      });
+
+      // the list of virtual and native SourceBuffers created by this
+      // MediaSource
+      _this9.sourceBuffers = [];
+
+      _this9.activeSourceBuffers_ = [];
+
+      /**
+       * update the list of active source buffers based upon various
+       * imformation from HLS and video.js
+       *
+       * @private
+       */
+      _this9.updateActiveSourceBuffers_ = function () {
+        // Retain the reference but empty the array
+        _this9.activeSourceBuffers_.length = 0;
+
+        // If there is only one source buffer, then it will always be active and audio will
+        // be disabled based on the codec of the source buffer
+        if (_this9.sourceBuffers.length === 1) {
+          var sourceBuffer = _this9.sourceBuffers[0];
+
+          sourceBuffer.appendAudioInitSegment_ = true;
+          sourceBuffer.audioDisabled_ = !sourceBuffer.audioCodec_;
+          _this9.activeSourceBuffers_.push(sourceBuffer);
+          return;
+        }
+
+        // There are 2 source buffers, a combined (possibly video only) source buffer and
+        // and an audio only source buffer.
+        // By default, the audio in the combined virtual source buffer is enabled
+        // and the audio-only source buffer (if it exists) is disabled.
+        var disableCombined = false;
+        var disableAudioOnly = true;
+
+        // TODO: maybe we can store the sourcebuffers on the track objects?
+        // safari may do something like this
+        for (var i = 0; i < _this9.player_.audioTracks().length; i++) {
+          var track = _this9.player_.audioTracks()[i];
+
+          if (track.enabled && track.kind !== 'main') {
+            // The enabled track is an alternate audio track so disable the audio in
+            // the combined source buffer and enable the audio-only source buffer.
+            disableCombined = true;
+            disableAudioOnly = false;
+            break;
+          }
+        }
+
+        _this9.sourceBuffers.forEach(function (sourceBuffer, index) {
+          /* eslinst-disable */
+          // TODO once codecs are required, we can switch to using the codecs to determine
+          //      what stream is the video stream, rather than relying on videoTracks
+          /* eslinst-enable */
+
+          sourceBuffer.appendAudioInitSegment_ = true;
+
+          if (sourceBuffer.videoCodec_ && sourceBuffer.audioCodec_) {
+            // combined
+            sourceBuffer.audioDisabled_ = disableCombined;
+          } else if (sourceBuffer.videoCodec_ && !sourceBuffer.audioCodec_) {
+            // If the "combined" source buffer is video only, then we do not want
+            // disable the audio-only source buffer (this is mostly for demuxed
+            // audio and video hls)
+            sourceBuffer.audioDisabled_ = true;
+            disableAudioOnly = false;
+          } else if (!sourceBuffer.videoCodec_ && sourceBuffer.audioCodec_) {
+            // audio only
+            // In the case of audio only with alternate audio and disableAudioOnly is true
+            // this means we want to disable the audio on the alternate audio sourcebuffer
+            // but not the main "combined" source buffer. The "combined" source buffer is
+            // always at index 0, so this ensures audio won't be disabled in both source
+            // buffers.
+            sourceBuffer.audioDisabled_ = index ? disableAudioOnly : !disableAudioOnly;
+            if (sourceBuffer.audioDisabled_) {
+              return;
+            }
+          }
+
+          _this9.activeSourceBuffers_.push(sourceBuffer);
+        });
+      };
+
+      _this9.onPlayerMediachange_ = function () {
+        _this9.sourceBuffers.forEach(function (sourceBuffer) {
+          sourceBuffer.appendAudioInitSegment_ = true;
+        });
+      };
+
+      _this9.onHlsReset_ = function () {
+        _this9.sourceBuffers.forEach(function (sourceBuffer) {
+          if (sourceBuffer.transmuxer_) {
+            sourceBuffer.transmuxer_.postMessage({action: 'resetCaptions'});
+          }
+        });
+      };
+
+      _this9.onHlsSegmentTimeMapping_ = function (event) {
+        _this9.sourceBuffers.forEach(function (buffer) {
+          return buffer.timeMapping_ = event.mapping;
+        });
+      };
+
+      // Re-emit MediaSource events on the polyfill
+      ['sourceopen', 'sourceclose', 'sourceended'].forEach(function (eventName) {
+        this.nativeMediaSource_.addEventListener(eventName, this.trigger.bind(this));
+      }, _this9);
+
+      // capture the associated player when the MediaSource is
+      // successfully attached
+      _this9.on('sourceopen', function (event) {
+        // Get the player this MediaSource is attached to
+        var video = document_1.querySelector('[src="' + _this9.url_ + '"]');
+
+        if (!video) {
+          return;
+        }
+
+        _this9.player_ = videojs$1(video.parentNode);
+
+        // hls-reset is fired by videojs.Hls on to the tech after the main SegmentLoader
+        // resets its state and flushes the buffer
+        _this9.player_.tech_.on('hls-reset', _this9.onHlsReset_);
+        // hls-segment-time-mapping is fired by videojs.Hls on to the tech after the main
+        // SegmentLoader inspects an MTS segment and has an accurate stream to display
+        // time mapping
+        _this9.player_.tech_.on('hls-segment-time-mapping', _this9.onHlsSegmentTimeMapping_);
+
+        if (_this9.player_.audioTracks && _this9.player_.audioTracks()) {
+          _this9.player_.audioTracks().on('change', _this9.updateActiveSourceBuffers_);
+          _this9.player_.audioTracks().on('addtrack', _this9.updateActiveSourceBuffers_);
+          _this9.player_.audioTracks().on('removetrack', _this9.updateActiveSourceBuffers_);
+        }
+
+        _this9.player_.on('mediachange', _this9.onPlayerMediachange_);
+      });
+
+      _this9.on('sourceended', function (event) {
+        var duration = durationOfVideo(_this9.duration);
+
+        for (var i = 0; i < _this9.sourceBuffers.length; i++) {
+          var sourcebuffer = _this9.sourceBuffers[i];
+          var cues = sourcebuffer.metadataTrack_ && sourcebuffer.metadataTrack_.cues;
+
+          if (cues && cues.length) {
+            cues[cues.length - 1].endTime = duration;
+          }
+        }
+      });
+
+      // explicitly terminate any WebWorkers that were created
+      // by SourceHandlers
+      _this9.on('sourceclose', function (event) {
+        this.sourceBuffers.forEach(function (sourceBuffer) {
+          if (sourceBuffer.transmuxer_) {
+            sourceBuffer.transmuxer_.terminate();
+          }
+        });
+
+        this.sourceBuffers.length = 0;
+        if (!this.player_) {
+          return;
+        }
+
+        if (this.player_.audioTracks && this.player_.audioTracks()) {
+          this.player_.audioTracks().off('change', this.updateActiveSourceBuffers_);
+          this.player_.audioTracks().off('addtrack', this.updateActiveSourceBuffers_);
+          this.player_.audioTracks().off('removetrack', this.updateActiveSourceBuffers_);
+        }
+
+        // We can only change this if the player hasn't been disposed of yet
+        // because `off` eventually tries to use the el_ property. If it has
+        // been disposed of, then don't worry about it because there are no
+        // event handlers left to unbind anyway
+        if (this.player_.el_) {
+          this.player_.off('mediachange', this.onPlayerMediachange_);
+          this.player_.tech_.off('hls-reset', this.onHlsReset_);
+          this.player_.tech_.off('hls-segment-time-mapping', this.onHlsSegmentTimeMapping_);
+        }
+      });
+      return _this9;
+    }
+
+    /**
+     * Add a range that that can now be seeked to.
+     *
+     * @param {Double} start where to start the addition
+     * @param {Double} end where to end the addition
+     * @private
+     */
+
+
+    HtmlMediaSource.prototype.addSeekableRange_ = function addSeekableRange_(start, end) {
+      var error = void 0;
+
+      if (this.duration !== Infinity) {
+        error = new Error('MediaSource.addSeekableRange() can only be invoked ' + 'when the duration is Infinity');
+        error.name = 'InvalidStateError';
+        error.code = 11;
+        throw error;
+      }
+
+      if (end > this.nativeMediaSource_.duration || isNaN(this.nativeMediaSource_.duration)) {
+        this.nativeMediaSource_.duration = end;
+      }
+    };
+
+    /**
+     * Add a source buffer to the media source.
+     *
+     * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/addSourceBuffer
+     * @param {String} type the content-type of the content
+     * @return {Object} the created source buffer
+     */
+
+
+    HtmlMediaSource.prototype.addSourceBuffer = function addSourceBuffer(type) {
+      var buffer = void 0;
+      var parsedType = parseContentType(type);
+
+      // Create a VirtualSourceBuffer to transmux MPEG-2 transport
+      // stream segments into fragmented MP4s
+      if (/^(video|audio)\/mp2t$/i.test(parsedType.type)) {
+        var codecs = [];
+
+        if (parsedType.parameters && parsedType.parameters.codecs) {
+          codecs = parsedType.parameters.codecs.split(',');
+          codecs = translateLegacyCodecs(codecs);
+          codecs = codecs.filter(function (codec) {
+            return isAudioCodec(codec) || isVideoCodec(codec);
+          });
+        }
+
+        if (codecs.length === 0) {
+          codecs = ['avc1.4d400d', 'mp4a.40.2'];
+        }
+
+        buffer = new VirtualSourceBuffer(this, codecs);
+
+        if (this.sourceBuffers.length !== 0) {
+          // If another VirtualSourceBuffer already exists, then we are creating a
+          // SourceBuffer for an alternate audio track and therefore we know that
+          // the source has both an audio and video track.
+          // That means we should trigger the manual creation of the real
+          // SourceBuffers instead of waiting for the transmuxer to return data
+          this.sourceBuffers[0].createRealSourceBuffers_();
+          buffer.createRealSourceBuffers_();
+
+          // Automatically disable the audio on the first source buffer if
+          // a second source buffer is ever created
+          this.sourceBuffers[0].audioDisabled_ = true;
+        }
+      } else {
+        // delegate to the native implementation
+        buffer = this.nativeMediaSource_.addSourceBuffer(type);
+      }
+
+      this.sourceBuffers.push(buffer);
+      return buffer;
+    };
+
+    return HtmlMediaSource;
+  }(videojs$1.EventTarget);
+
+  /**
+   * @file videojs-contrib-media-sources.js
+   */
+
+
+  var urlCount = 0;
+
+  // ------------
+  // Media Source
+  // ------------
+
+  // store references to the media sources so they can be connected
+  // to a video element (a swf object)
+  // TODO: can we store this somewhere local to this module?
+  videojs$1.mediaSources = {};
+
+  /**
+   * Provide a method for a swf object to notify JS that a
+   * media source is now open.
+   *
+   * @param {String} msObjectURL string referencing the MSE Object URL
+   * @param {String} swfId the swf id
+   */
+  var open = function open(msObjectURL, swfId) {
+    var mediaSource = videojs$1.mediaSources[msObjectURL];
+
+    if (mediaSource) {
+      mediaSource.trigger({type: 'sourceopen', swfId: swfId});
+    } else {
+      throw new Error('Media Source not found (Video.js)');
+    }
+  };
+
+  /**
+   * Check to see if the native MediaSource object exists and supports
+   * an MP4 container with both H.264 video and AAC-LC audio.
+   *
+   * @return {Boolean} if  native media sources are supported
+   */
+  var supportsNativeMediaSources = function supportsNativeMediaSources() {
+    return !!window_1.MediaSource && !!window_1.MediaSource.isTypeSupported && window_1.MediaSource.isTypeSupported('video/mp4;codecs="avc1.4d400d,mp4a.40.2"');
+  };
+
+  /**
+   * An emulation of the MediaSource API so that we can support
+   * native and non-native functionality. returns an instance of
+   * HtmlMediaSource.
+   *
+   * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/MediaSource
+   */
+  var MediaSource = function MediaSource() {
+    this.MediaSource = {
+      open: open,
+      supportsNativeMediaSources: supportsNativeMediaSources
+    };
+
+    if (supportsNativeMediaSources()) {
+      return new HtmlMediaSource();
+    }
+
+    throw new Error('Cannot use create a virtual MediaSource for this video');
+  };
+
+  MediaSource.open = open;
+  MediaSource.supportsNativeMediaSources = supportsNativeMediaSources;
+
+  /**
+   * A wrapper around the native URL for our MSE object
+   * implementation, this object is exposed under videojs.URL
+   *
+   * @link https://developer.mozilla.org/en-US/docs/Web/API/URL/URL
+   */
+  var URL$1 = {
+    /**
+     * A wrapper around the native createObjectURL for our objects.
+     * This function maps a native or emulated mediaSource to a blob
+     * url so that it can be loaded into video.js
+     *
+     * @link https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
+     * @param {MediaSource} object the object to create a blob url to
+     */
+    createObjectURL: function createObjectURL(object) {
+      var objectUrlPrefix = 'blob:vjs-media-source/';
+      var url = void 0;
+
+      // use the native MediaSource to generate an object URL
+      if (object instanceof HtmlMediaSource) {
+        url = window_1.URL.createObjectURL(object.nativeMediaSource_);
+        object.url_ = url;
+        return url;
+      }
+      // if the object isn't an emulated MediaSource, delegate to the
+      // native implementation
+      if (!(object instanceof HtmlMediaSource)) {
+        url = window_1.URL.createObjectURL(object);
+        object.url_ = url;
+        return url;
+      }
+
+      // build a URL that can be used to map back to the emulated
+      // MediaSource
+      url = objectUrlPrefix + urlCount;
+
+      urlCount++;
+
+      // setup the mapping back to object
+      videojs$1.mediaSources[url] = object;
+
+      return url;
+    }
+  };
+
+  videojs$1.MediaSource = MediaSource;
+  videojs$1.URL = URL$1;
+
+  var EventTarget$1$1 = videojs$1.EventTarget,
+    mergeOptions$2 = videojs$1.mergeOptions;
+
+  /**
+   * Returns a new master manifest that is the result of merging an updated master manifest
+   * into the original version.
+   *
+   * @param {Object} oldMaster
+   *        The old parsed mpd object
+   * @param {Object} newMaster
+   *        The updated parsed mpd object
+   * @return {Object}
+   *         A new object representing the original master manifest with the updated media
+   *         playlists merged in
+   */
+
+  var updateMaster$1 = function updateMaster$1(oldMaster, newMaster) {
+    var update = mergeOptions$2(oldMaster, {
+      // These are top level properties that can be updated
+      duration: newMaster.duration,
+      minimumUpdatePeriod: newMaster.minimumUpdatePeriod
+    });
+
+    // First update the playlists in playlist list
+    for (var i = 0; i < newMaster.playlists.length; i++) {
+      var playlistUpdate = updateMaster(update, newMaster.playlists[i]);
+
+      if (playlistUpdate) {
+        update = playlistUpdate;
+      }
+    }
+
+    // Then update media group playlists
+    forEachMediaGroup(newMaster, function (properties, type, group, label) {
+      if (properties.playlists && properties.playlists.length) {
+        var uri = properties.playlists[0].uri;
+        var _playlistUpdate = updateMaster(update, properties.playlists[0]);
+
+        if (_playlistUpdate) {
+          update = _playlistUpdate;
+          // update the playlist reference within media groups
+          update.mediaGroups[type][group][label].playlists[0] = update.playlists[uri];
+        }
+      }
+    });
+
+    return update;
+  };
+
+  var DashPlaylistLoader = function (_EventTarget$) {
+    inherits(DashPlaylistLoader, _EventTarget$);
+
+    // DashPlaylistLoader must accept either a src url or a playlist because subsequent
+    // playlist loader setups from media groups will expect to be able to pass a playlist
+    // (since there aren't external URLs to media playlists with DASH)
+    function DashPlaylistLoader(srcUrlOrPlaylist, hls, withCredentials, masterPlaylistLoader) {
+      classCallCheck(this, DashPlaylistLoader);
+
+      var _this10 = possibleConstructorReturn(this, _EventTarget$.call(this));
+
+      _this10.hls_ = hls;
+      _this10.withCredentials = withCredentials;
+
+      if (!srcUrlOrPlaylist) {
+        throw new Error('A non-empty playlist URL or playlist is required');
+      }
+
+      // event naming?
+      _this10.on('minimumUpdatePeriod', function () {
+        _this10.refreshXml_();
+      });
+
+      // live playlist staleness timeout
+      _this10.on('mediaupdatetimeout', function () {
+        _this10.refreshMedia_();
+      });
+
+      // initialize the loader state
+      if (typeof srcUrlOrPlaylist === 'string') {
+        _this10.srcUrl = srcUrlOrPlaylist;
+        _this10.state = 'HAVE_NOTHING';
+        return possibleConstructorReturn(_this10);
+      }
+
+      _this10.masterPlaylistLoader_ = masterPlaylistLoader;
+
+      _this10.state = 'HAVE_METADATA';
+      _this10.started = true;
+      // we only should have one playlist so select it
+      _this10.media(srcUrlOrPlaylist);
+      // trigger async to mimic behavior of HLS, where it must request a playlist
+      window_1.setTimeout(function () {
+        _this10.trigger('loadedmetadata');
+      }, 0);
+      return _this10;
+    }
+
+    DashPlaylistLoader.prototype.dispose = function dispose() {
+      this.stopRequest();
+      window_1.clearTimeout(this.mediaUpdateTimeout);
+    };
+
+    DashPlaylistLoader.prototype.stopRequest = function stopRequest() {
+      if (this.request) {
+        var oldRequest = this.request;
+
+        this.request = null;
+        oldRequest.onreadystatechange = null;
+        oldRequest.abort();
+      }
+    };
+
+    DashPlaylistLoader.prototype.media = function media(playlist) {
+      // getter
+      if (!playlist) {
+        return this.media_;
+      }
+
+      // setter
+      if (this.state === 'HAVE_NOTHING') {
+        throw new Error('Cannot switch media playlist from ' + this.state);
+      }
+
+      var startingState = this.state;
+
+      // find the playlist object if the target playlist has been specified by URI
+      if (typeof playlist === 'string') {
+        if (!this.master.playlists[playlist]) {
+          throw new Error('Unknown playlist URI: ' + playlist);
+        }
+        playlist = this.master.playlists[playlist];
+      }
+
+      var mediaChange = !this.media_ || playlist.uri !== this.media_.uri;
+
+      this.state = 'HAVE_METADATA';
+
+      // switching to the active playlist is a no-op
+      if (!mediaChange) {
+        return;
+      }
+
+      // switching from an already loaded playlist
+      if (this.media_) {
+        this.trigger('mediachanging');
+      }
+
+      this.media_ = playlist;
+
+      this.refreshMedia_();
+
+      // trigger media change if the active media has been updated
+      if (startingState !== 'HAVE_MASTER') {
+        this.trigger('mediachange');
+      }
+    };
+
+    DashPlaylistLoader.prototype.pause = function pause() {
+      this.stopRequest();
+      if (this.state === 'HAVE_NOTHING') {
+        // If we pause the loader before any data has been retrieved, its as if we never
+        // started, so reset to an unstarted state.
+        this.started = false;
+      }
+    };
+
+    DashPlaylistLoader.prototype.load = function load() {
+      // because the playlists are internal to the manifest, load should either load the
+      // main manifest, or do nothing but trigger an event
+      if (!this.started) {
+        this.start();
+        return;
+      }
+
+      this.trigger('loadedplaylist');
+    };
+
+    /**
+     * Parses the master xml string and updates playlist uri references
+     *
+     * @return {Object}
+     *         The parsed mpd manifest object
+     */
+
+
+    DashPlaylistLoader.prototype.parseMasterXml = function parseMasterXml() {
+      var master = parse(this.masterXml_, {
+        manifestUri: this.srcUrl,
+        clientOffset: this.clientOffset_
+      });
+
+      master.uri = this.srcUrl;
+
+      // Set up phony URIs for the playlists since we won't have external URIs for DASH
+      // but reference playlists by their URI throughout the project
+      // TODO: Should we create the dummy uris in mpd-parser as well (leaning towards yes).
+      for (var i = 0; i < master.playlists.length; i++) {
+        var phonyUri = 'placeholder-uri-' + i;
+
+        master.playlists[i].uri = phonyUri;
+        // set up by URI references
+        master.playlists[phonyUri] = master.playlists[i];
+      }
+
+      // set up phony URIs for the media group playlists since we won't have external
+      // URIs for DASH but reference playlists by their URI throughout the project
+      forEachMediaGroup(master, function (properties, mediaType, groupKey, labelKey) {
+        if (properties.playlists && properties.playlists.length) {
+          var _phonyUri = 'placeholder-uri-' + mediaType + '-' + groupKey + '-' + labelKey;
+
+          properties.playlists[0].uri = _phonyUri;
+          // setup URI references
+          master.playlists[_phonyUri] = properties.playlists[0];
+        }
+      });
+
+      setupMediaPlaylists(master);
+      resolveMediaGroupUris(master);
+
+      return master;
+    };
+
+    DashPlaylistLoader.prototype.start = function start() {
+      var _this11 = this;
+
+      this.started = true;
+
+      // request the specified URL
+      this.request = this.hls_.xhr({
+        uri: this.srcUrl,
+        withCredentials: this.withCredentials
+      }, function (error, req) {
+        // disposed
+        if (!_this11.request) {
+          return;
+        }
+
+        // clear the loader's request reference
+        _this11.request = null;
+
+        if (error) {
+          _this11.error = {
+            status: req.status,
+            message: 'DASH playlist request error at URL: ' + _this11.srcUrl,
+            responseText: req.responseText,
+            // MEDIA_ERR_NETWORK
+            code: 2
+          };
+          if (_this11.state === 'HAVE_NOTHING') {
+            _this11.started = false;
+          }
+          return _this11.trigger('error');
+        }
+
+        _this11.masterXml_ = req.responseText;
+
+        if (req.responseHeaders && req.responseHeaders.date) {
+          _this11.masterLoaded_ = Date.parse(req.responseHeaders.date);
+        } else {
+          _this11.masterLoaded_ = Date.now();
+        }
+
+        _this11.syncClientServerClock_(_this11.onClientServerClockSync_.bind(_this11));
+      });
+    };
+
+    /**
+     * Parses the master xml for UTCTiming node to sync the client clock to the server
+     * clock. If the UTCTiming node requires a HEAD or GET request, that request is made.
+     *
+     * @param {Function} done
+     *        Function to call when clock sync has completed
+     */
+
+
+    DashPlaylistLoader.prototype.syncClientServerClock_ = function syncClientServerClock_(done) {
+      var _this12 = this;
+
+      var utcTiming = parseUTCTiming(this.masterXml_);
+
+      // No UTCTiming element found in the mpd. Use Date header from mpd request as the
+      // server clock
+      if (utcTiming === null) {
+        this.clientOffset_ = this.masterLoaded_ - Date.now();
+        return done();
+      }
+
+      if (utcTiming.method === 'DIRECT') {
+        this.clientOffset_ = utcTiming.value - Date.now();
+        return done();
+      }
+
+      this.request = this.hls_.xhr({
+        uri: resolveUrl$1(this.srcUrl, utcTiming.value),
+        method: utcTiming.method,
+        withCredentials: this.withCredentials
+      }, function (error, req) {
+        // disposed
+        if (!_this12.request) {
+          return;
+        }
+
+        if (error) {
+          // sync request failed, fall back to using date header from mpd
+          // TODO: log warning
+          _this12.clientOffset_ = _this12.masterLoaded_ - Date.now();
+          return done();
+        }
+
+        var serverTime = void 0;
+
+        if (utcTiming.method === 'HEAD') {
+          if (!req.responseHeaders || !req.responseHeaders.date) {
+            // expected date header not preset, fall back to using date header from mpd
+            // TODO: log warning
+            serverTime = _this12.masterLoaded_;
+          } else {
+            serverTime = Date.parse(req.responseHeaders.date);
+          }
+        } else {
+          serverTime = Date.parse(req.responseText);
+        }
+
+        _this12.clientOffset_ = serverTime - Date.now();
+
+        done();
+      });
+    };
+
+    /**
+     * Handler for after client/server clock synchronization has happened. Sets up
+     * xml refresh timer if specificed by the manifest.
+     */
+
+
+    DashPlaylistLoader.prototype.onClientServerClockSync_ = function onClientServerClockSync_() {
+      var _this13 = this;
+
+      this.master = this.parseMasterXml();
+
+      this.state = 'HAVE_MASTER';
+
+      this.trigger('loadedplaylist');
+
+      if (!this.media_) {
+        // no media playlist was specifically selected so start
+        // from the first listed one
+        this.media(this.master.playlists[0]);
+      }
+      // trigger loadedmetadata to resolve setup of media groups
+      // trigger async to mimic behavior of HLS, where it must request a playlist
+      window_1.setTimeout(function () {
+        _this13.trigger('loadedmetadata');
+      }, 0);
+
+      // TODO: minimumUpdatePeriod can have a value of 0. Currently the manifest will not
+      // be refreshed when this is the case. The inter-op guide says that when the
+      // minimumUpdatePeriod is 0, the manifest should outline all currently available
+      // segments, but future segments may require an update. I think a good solution
+      // would be to update the manifest at the same rate that the media playlists
+      // are "refreshed", i.e. every targetDuration.
+      if (this.master.minimumUpdatePeriod) {
+        window_1.setTimeout(function () {
+          _this13.trigger('minimumUpdatePeriod');
+        }, this.master.minimumUpdatePeriod);
+      }
+    };
+
+    /**
+     * Sends request to refresh the master xml and updates the parsed master manifest
+     * TODO: Does the client offset need to be recalculated when the xml is refreshed?
+     */
+
+
+    DashPlaylistLoader.prototype.refreshXml_ = function refreshXml_() {
+      var _this14 = this;
+
+      this.request = this.hls_.xhr({
+        uri: this.srcUrl,
+        withCredentials: this.withCredentials
+      }, function (error, req) {
+        // disposed
+        if (!_this14.request) {
+          return;
+        }
+
+        // clear the loader's request reference
+        _this14.request = null;
+
+        if (error) {
+          _this14.error = {
+            status: req.status,
+            message: 'DASH playlist request error at URL: ' + _this14.srcUrl,
+            responseText: req.responseText,
+            // MEDIA_ERR_NETWORK
+            code: 2
+          };
+          if (_this14.state === 'HAVE_NOTHING') {
+            _this14.started = false;
+          }
+          return _this14.trigger('error');
+        }
+
+        _this14.masterXml_ = req.responseText;
+
+        var newMaster = _this14.parseMasterXml();
+
+        _this14.master = updateMaster$1(_this14.master, newMaster);
+
+        window_1.setTimeout(function () {
+          _this14.trigger('minimumUpdatePeriod');
+        }, _this14.master.minimumUpdatePeriod);
+      });
+    };
+
+    /**
+     * Refreshes the media playlist by re-parsing the master xml and updating playlist
+     * references. If this is an alternate loader, the updated parsed manifest is retrieved
+     * from the master loader.
+     */
+
+
+    DashPlaylistLoader.prototype.refreshMedia_ = function refreshMedia_() {
+      var _this15 = this;
+
+      var oldMaster = void 0;
+      var newMaster = void 0;
+
+      if (this.masterPlaylistLoader_) {
+        oldMaster = this.masterPlaylistLoader_.master;
+        newMaster = this.masterPlaylistLoader_.parseMasterXml();
+      } else {
+        oldMaster = this.master;
+        newMaster = this.parseMasterXml();
+      }
+
+      var updatedMaster = updateMaster$1(oldMaster, newMaster);
+
+      if (updatedMaster) {
+        if (this.masterPlaylistLoader_) {
+          this.masterPlaylistLoader_.master = updatedMaster;
+        } else {
+          this.master = updatedMaster;
+        }
+        this.media_ = updatedMaster.playlists[this.media_.uri];
+      } else {
+        this.trigger('playlistunchanged');
+      }
+
+      if (!this.media().endList) {
+        this.mediaUpdateTimeout = window_1.setTimeout(function () {
+          _this15.trigger('mediaupdatetimeout');
+        }, refreshDelay(this.media(), !!updatedMaster));
+      }
+
+      this.trigger('loadedplaylist');
+    };
+
+    return DashPlaylistLoader;
+  }(EventTarget$1$1);
+
+  var logger = function logger(source) {
+    if (videojs$1.log.debug) {
+      return videojs$1.log.debug.bind(videojs$1, 'VHS:', source + ' >');
+    }
+
+    return function () {
+    };
+  };
+
+  function noop$1() {
+  }
+
+  /**
+   * @file source-updater.js
+   */
+
+  /**
+   * A queue of callbacks to be serialized and applied when a
+   * MediaSource and its associated SourceBuffers are not in the
+   * updating state. It is used by the segment loader to update the
+   * underlying SourceBuffers when new data is loaded, for instance.
+   *
+   * @class SourceUpdater
+   * @param {MediaSource} mediaSource the MediaSource to create the
+   * SourceBuffer from
+   * @param {String} mimeType the desired MIME type of the underlying
+   * SourceBuffer
+   * @param {Object} sourceBufferEmitter an event emitter that fires when a source buffer is
+   * added to the media source
+   */
+
+  var SourceUpdater = function () {
+    function SourceUpdater(mediaSource, mimeType, type, sourceBufferEmitter) {
+      classCallCheck(this, SourceUpdater);
+
+      this.callbacks_ = [];
+      this.pendingCallback_ = null;
+      this.timestampOffset_ = 0;
+      this.mediaSource = mediaSource;
+      this.processedAppend_ = false;
+      this.type_ = type;
+      this.mimeType_ = mimeType;
+      this.logger_ = logger('SourceUpdater[' + type + '][' + mimeType + ']');
+
+      if (mediaSource.readyState === 'closed') {
+        mediaSource.addEventListener('sourceopen', this.createSourceBuffer_.bind(this, mimeType, sourceBufferEmitter));
+      } else {
+        this.createSourceBuffer_(mimeType, sourceBufferEmitter);
+      }
+    }
+
+    SourceUpdater.prototype.createSourceBuffer_ = function createSourceBuffer_(mimeType, sourceBufferEmitter) {
+      var _this16 = this;
+
+      this.sourceBuffer_ = this.mediaSource.addSourceBuffer(mimeType);
+
+      this.logger_('created SourceBuffer');
+
+      if (sourceBufferEmitter) {
+        sourceBufferEmitter.trigger('sourcebufferadded');
+
+        if (this.mediaSource.sourceBuffers.length < 2) {
+          // There's another source buffer we must wait for before we can start updating
+          // our own (or else we can get into a bad state, i.e., appending video/audio data
+          // before the other video/audio source buffer is available and leading to a video
+          // or audio only buffer).
+          sourceBufferEmitter.on('sourcebufferadded', function () {
+            _this16.start_();
+          });
+          return;
+        }
+      }
+
+      this.start_();
+    };
+
+    SourceUpdater.prototype.start_ = function start_() {
+      var _this17 = this;
+
+      this.started_ = true;
+
+      // run completion handlers and process callbacks as updateend
+      // events fire
+      this.onUpdateendCallback_ = function () {
+        var pendingCallback = _this17.pendingCallback_;
+
+        _this17.pendingCallback_ = null;
+
+        _this17.logger_('buffered [' + printableRange(_this17.buffered()) + ']');
+
+        if (pendingCallback) {
+          pendingCallback();
+        }
+
+        _this17.runCallback_();
+      };
+
+      this.sourceBuffer_.addEventListener('updateend', this.onUpdateendCallback_);
+
+      this.runCallback_();
+    };
+
+    /**
+     * Aborts the current segment and resets the segment parser.
+     *
+     * @param {Function} done function to call when done
+     * @see http://w3c.github.io/media-source/#widl-SourceBuffer-abort-void
+     */
+
+
+    SourceUpdater.prototype.abort = function abort(done) {
+      var _this18 = this;
+
+      if (this.processedAppend_) {
+        this.queueCallback_(function () {
+          _this18.sourceBuffer_.abort();
+        }, done);
+      }
+    };
+
+    /**
+     * Queue an update to append an ArrayBuffer.
+     *
+     * @param {ArrayBuffer} bytes
+     * @param {Function} done the function to call when done
+     * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-appendBuffer-void-ArrayBuffer-data
+     */
+
+
+    SourceUpdater.prototype.appendBuffer = function appendBuffer(bytes, done) {
+      var _this19 = this;
+
+      this.processedAppend_ = true;
+      this.queueCallback_(function () {
+        _this19.sourceBuffer_.appendBuffer(bytes);
+      }, done);
+    };
+
+    /**
+     * Indicates what TimeRanges are buffered in the managed SourceBuffer.
+     *
+     * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-buffered
+     */
+
+
+    SourceUpdater.prototype.buffered = function buffered() {
+      if (!this.sourceBuffer_) {
+        return videojs$1.createTimeRanges();
+      }
+      return this.sourceBuffer_.buffered;
+    };
+
+    /**
+     * Queue an update to remove a time range from the buffer.
+     *
+     * @param {Number} start where to start the removal
+     * @param {Number} end where to end the removal
+     * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-remove-void-double-start-unrestricted-double-end
+     */
+
+
+    SourceUpdater.prototype.remove = function remove(start, end) {
+      var _this20 = this;
+
+      if (this.processedAppend_) {
+        this.queueCallback_(function () {
+          _this20.logger_('remove [' + start + ' => ' + end + ']');
+          _this20.sourceBuffer_.remove(start, end);
+        }, noop$1);
+      }
+    };
+
+    /**
+     * Whether the underlying sourceBuffer is updating or not
+     *
+     * @return {Boolean} the updating status of the SourceBuffer
+     */
+
+
+    SourceUpdater.prototype.updating = function updating() {
+      return !this.sourceBuffer_ || this.sourceBuffer_.updating || this.pendingCallback_;
+    };
+
+    /**
+     * Set/get the timestampoffset on the SourceBuffer
+     *
+     * @return {Number} the timestamp offset
+     */
+
+
+    SourceUpdater.prototype.timestampOffset = function timestampOffset(offset) {
+      var _this21 = this;
+
+      if (typeof offset !== 'undefined') {
+        this.queueCallback_(function () {
+          _this21.sourceBuffer_.timestampOffset = offset;
+        });
+        this.timestampOffset_ = offset;
+      }
+      return this.timestampOffset_;
+    };
+
+    /**
+     * Queue a callback to run
+     */
+
+
+    SourceUpdater.prototype.queueCallback_ = function queueCallback_(callback, done) {
+      this.callbacks_.push([callback.bind(this), done]);
+      this.runCallback_();
+    };
+
+    /**
+     * Run a queued callback
+     */
+
+
+    SourceUpdater.prototype.runCallback_ = function runCallback_() {
+      var callbacks = void 0;
+
+      if (!this.updating() && this.callbacks_.length && this.started_) {
+        callbacks = this.callbacks_.shift();
+        this.pendingCallback_ = callbacks[1];
+        callbacks[0]();
+      }
+    };
+
+    /**
+     * dispose of the source updater and the underlying sourceBuffer
+     */
+
+
+    SourceUpdater.prototype.dispose = function dispose() {
+      this.sourceBuffer_.removeEventListener('updateend', this.onUpdateendCallback_);
+      if (this.sourceBuffer_ && this.mediaSource.readyState === 'open') {
+        this.sourceBuffer_.abort();
+      }
+    };
+
+    return SourceUpdater;
+  }();
+
+  var Config = {
+    GOAL_BUFFER_LENGTH: 30,
+    MAX_GOAL_BUFFER_LENGTH: 60,
+    GOAL_BUFFER_LENGTH_RATE: 1,
+    // A fudge factor to apply to advertised playlist bitrates to account for
+    // temporary flucations in client bandwidth
+    BANDWIDTH_VARIANCE: 1.2,
+    // How much of the buffer must be filled before we consider upswitching
+    BUFFER_LOW_WATER_LINE: 0,
+    MAX_BUFFER_LOW_WATER_LINE: 30,
+    BUFFER_LOW_WATER_LINE_RATE: 1
+  };
+
+  var REQUEST_ERRORS = {
+    FAILURE: 2,
+    TIMEOUT: -101,
+    ABORTED: -102
+  };
+
+  /**
+   * Turns segment byterange into a string suitable for use in
+   * HTTP Range requests
+   *
+   * @param {Object} byterange - an object with two values defining the start and end
+   *                             of a byte-range
+   */
+  var byterangeStr = function byterangeStr(byterange) {
+    var byterangeStart = void 0;
+    var byterangeEnd = void 0;
+
+    // `byterangeEnd` is one less than `offset + length` because the HTTP range
+    // header uses inclusive ranges
+    byterangeEnd = byterange.offset + byterange.length - 1;
+    byterangeStart = byterange.offset;
+    return 'bytes=' + byterangeStart + '-' + byterangeEnd;
+  };
+
+  /**
+   * Defines headers for use in the xhr request for a particular segment.
+   *
+   * @param {Object} segment - a simplified copy of the segmentInfo object
+   *                           from SegmentLoader
+   */
+  var segmentXhrHeaders = function segmentXhrHeaders(segment) {
+    var headers = {};
+
+    if (segment.byterange) {
+      headers.Range = byterangeStr(segment.byterange);
+    }
+    return headers;
+  };
+
+  /**
+   * Abort all requests
+   *
+   * @param {Object} activeXhrs - an object that tracks all XHR requests
+   */
+  var abortAll = function abortAll(activeXhrs) {
+    activeXhrs.forEach(function (xhr) {
+      xhr.abort();
+    });
+  };
+
+  /**
+   * Gather important bandwidth stats once a request has completed
+   *
+   * @param {Object} request - the XHR request from which to gather stats
+   */
+  var getRequestStats = function getRequestStats(request) {
+    return {
+      bandwidth: request.bandwidth,
+      bytesReceived: request.bytesReceived || 0,
+      roundTripTime: request.roundTripTime || 0
+    };
+  };
+
+  /**
+   * If possible gather bandwidth stats as a request is in
+   * progress
+   *
+   * @param {Event} progressEvent - an event object from an XHR's progress event
+   */
+  var getProgressStats = function getProgressStats(progressEvent) {
+    var request = progressEvent.target;
+    var roundTripTime = Date.now() - request.requestTime;
+    var stats = {
+      bandwidth: Infinity,
+      bytesReceived: 0,
+      roundTripTime: roundTripTime || 0
+    };
+
+    stats.bytesReceived = progressEvent.loaded;
+    // This can result in Infinity if stats.roundTripTime is 0 but that is ok
+    // because we should only use bandwidth stats on progress to determine when
+    // abort a request early due to insufficient bandwidth
+    stats.bandwidth = Math.floor(stats.bytesReceived / stats.roundTripTime * 8 * 1000);
+
+    return stats;
+  };
+
+  /**
+   * Handle all error conditions in one place and return an object
+   * with all the information
+   *
+   * @param {Error|null} error - if non-null signals an error occured with the XHR
+   * @param {Object} request -  the XHR request that possibly generated the error
+   */
+  var handleErrors = function handleErrors(error, request) {
+    if (request.timedout) {
+      return {
+        status: request.status,
+        message: 'HLS request timed-out at URL: ' + request.uri,
+        code: REQUEST_ERRORS.TIMEOUT,
+        xhr: request
+      };
+    }
+
+    if (request.aborted) {
+      return {
+        status: request.status,
+        message: 'HLS request aborted at URL: ' + request.uri,
+        code: REQUEST_ERRORS.ABORTED,
+        xhr: request
+      };
+    }
+
+    if (error) {
+      return {
+        status: request.status,
+        message: 'HLS request errored at URL: ' + request.uri,
+        code: REQUEST_ERRORS.FAILURE,
+        xhr: request
+      };
+    }
+
+    return null;
+  };
+
+  /**
+   * Handle responses for key data and convert the key data to the correct format
+   * for the decryption step later
+   *
+   * @param {Object} segment - a simplified copy of the segmentInfo object
+   *                           from SegmentLoader
+   * @param {Function} finishProcessingFn - a callback to execute to continue processing
+   *                                        this request
+   */
+  var handleKeyResponse = function handleKeyResponse(segment, finishProcessingFn) {
+    return function (error, request) {
+      var response = request.response;
+      var errorObj = handleErrors(error, request);
+
+      if (errorObj) {
+        return finishProcessingFn(errorObj, segment);
+      }
+
+      if (response.byteLength !== 16) {
+        return finishProcessingFn({
+          status: request.status,
+          message: 'Invalid HLS key at URL: ' + request.uri,
+          code: REQUEST_ERRORS.FAILURE,
+          xhr: request
+        }, segment);
+      }
+
+      var view = new DataView(response);
+
+      segment.key.bytes = new Uint32Array([view.getUint32(0), view.getUint32(4), view.getUint32(8), view.getUint32(12)]);
+      return finishProcessingFn(null, segment);
+    };
+  };
+
+  /**
+   * Handle init-segment responses
+   *
+   * @param {Object} segment - a simplified copy of the segmentInfo object
+   *                           from SegmentLoader
+   * @param {Function} finishProcessingFn - a callback to execute to continue processing
+   *                                        this request
+   */
+  var handleInitSegmentResponse = function handleInitSegmentResponse(segment, finishProcessingFn) {
+    return function (error, request) {
+      var response = request.response;
+      var errorObj = handleErrors(error, request);
+
+      if (errorObj) {
+        return finishProcessingFn(errorObj, segment);
+      }
+
+      // stop processing if received empty content
+      if (response.byteLength === 0) {
+        return finishProcessingFn({
+          status: request.status,
+          message: 'Empty HLS segment content at URL: ' + request.uri,
+          code: REQUEST_ERRORS.FAILURE,
+          xhr: request
+        }, segment);
+      }
+
+      segment.map.bytes = new Uint8Array(request.response);
+      return finishProcessingFn(null, segment);
+    };
+  };
+
+  /**
+   * Response handler for segment-requests being sure to set the correct
+   * property depending on whether the segment is encryped or not
+   * Also records and keeps track of stats that are used for ABR purposes
+   *
+   * @param {Object} segment - a simplified copy of the segmentInfo object
+   *                           from SegmentLoader
+   * @param {Function} finishProcessingFn - a callback to execute to continue processing
+   *                                        this request
+   */
+  var handleSegmentResponse = function handleSegmentResponse(segment, finishProcessingFn) {
+    return function (error, request) {
+      var response = request.response;
+      var errorObj = handleErrors(error, request);
+
+      if (errorObj) {
+        return finishProcessingFn(errorObj, segment);
+      }
+
+      // stop processing if received empty content
+      if (response.byteLength === 0) {
+        return finishProcessingFn({
+          status: request.status,
+          message: 'Empty HLS segment content at URL: ' + request.uri,
+          code: REQUEST_ERRORS.FAILURE,
+          xhr: request
+        }, segment);
+      }
+
+      segment.stats = getRequestStats(request);
+
+      if (segment.key) {
+        segment.encryptedBytes = new Uint8Array(request.response);
+      } else {
+        segment.bytes = new Uint8Array(request.response);
+      }
+
+      return finishProcessingFn(null, segment);
+    };
+  };
+
+  /**
+   * Decrypt the segment via the decryption web worker
+   *
+   * @param {WebWorker} decrypter - a WebWorker interface to AES-128 decryption routines
+   * @param {Object} segment - a simplified copy of the segmentInfo object
+   *                           from SegmentLoader
+   * @param {Function} doneFn - a callback that is executed after decryption has completed
+   */
+  var decryptSegment = function decryptSegment(decrypter, segment, doneFn) {
+    var decryptionHandler = function decryptionHandler(event) {
+      if (event.data.source === segment.requestId) {
+        decrypter.removeEventListener('message', decryptionHandler);
+        var decrypted = event.data.decrypted;
+
+        segment.bytes = new Uint8Array(decrypted.bytes, decrypted.byteOffset, decrypted.byteLength);
+        return doneFn(null, segment);
+      }
+    };
+
+    decrypter.addEventListener('message', decryptionHandler);
+
+    // this is an encrypted segment
+    // incrementally decrypt the segment
+    decrypter.postMessage(createTransferableMessage({
+      source: segment.requestId,
+      encrypted: segment.encryptedBytes,
+      key: segment.key.bytes,
+      iv: segment.key.iv
+    }), [segment.encryptedBytes.buffer, segment.key.bytes.buffer]);
+  };
+
+  /**
+   * The purpose of this function is to get the most pertinent error from the
+   * array of errors.
+   * For instance if a timeout and two aborts occur, then the aborts were
+   * likely triggered by the timeout so return that error object.
+   */
+  var getMostImportantError = function getMostImportantError(errors) {
+    return errors.reduce(function (prev, err) {
+      return err.code > prev.code ? err : prev;
+    });
+  };
+
+  /**
+   * This function waits for all XHRs to finish (with either success or failure)
+   * before continueing processing via it's callback. The function gathers errors
+   * from each request into a single errors array so that the error status for
+   * each request can be examined later.
+   *
+   * @param {Object} activeXhrs - an object that tracks all XHR requests
+   * @param {WebWorker} decrypter - a WebWorker interface to AES-128 decryption routines
+   * @param {Function} doneFn - a callback that is executed after all resources have been
+   *                            downloaded and any decryption completed
+   */
+  var waitForCompletion = function waitForCompletion(activeXhrs, decrypter, doneFn) {
+    var errors = [];
+    var count = 0;
+
+    return function (error, segment) {
+      if (error) {
+        // If there are errors, we have to abort any outstanding requests
+        abortAll(activeXhrs);
+        errors.push(error);
+      }
+      count += 1;
+
+      if (count === activeXhrs.length) {
+        // Keep track of when *all* of the requests have completed
+        segment.endOfAllRequests = Date.now();
+
+        if (errors.length > 0) {
+          var worstError = getMostImportantError(errors);
+
+          return doneFn(worstError, segment);
+        }
+        if (segment.encryptedBytes) {
+          return decryptSegment(decrypter, segment, doneFn);
+        }
+        // Otherwise, everything is ready just continue
+        return doneFn(null, segment);
+      }
+    };
+  };
+
+  /**
+   * Simple progress event callback handler that gathers some stats before
+   * executing a provided callback with the `segment` object
+   *
+   * @param {Object} segment - a simplified copy of the segmentInfo object
+   *                           from SegmentLoader
+   * @param {Function} progressFn - a callback that is executed each time a progress event
+   *                                is received
+   * @param {Event} event - the progress event object from XMLHttpRequest
+   */
+  var handleProgress = function handleProgress(segment, progressFn) {
+    return function (event) {
+      segment.stats = videojs$1.mergeOptions(segment.stats, getProgressStats(event));
+
+      // record the time that we receive the first byte of data
+      if (!segment.stats.firstBytesReceivedAt && segment.stats.bytesReceived) {
+        segment.stats.firstBytesReceivedAt = Date.now();
+      }
+
+      return progressFn(event, segment);
+    };
+  };
+
+  /**
+   * Load all resources and does any processing necessary for a media-segment
+   *
+   * Features:
+   *   decrypts the media-segment if it has a key uri and an iv
+   *   aborts *all* requests if *any* one request fails
+   *
+   * The segment object, at minimum, has the following format:
+   * {
+   *   resolvedUri: String,
+   *   [byterange]: {
+   *     offset: Number,
+   *     length: Number
+   *   },
+   *   [key]: {
+   *     resolvedUri: String
+   *     [byterange]: {
+   *       offset: Number,
+   *       length: Number
+   *     },
+   *     iv: {
+   *       bytes: Uint32Array
+   *     }
+   *   },
+   *   [map]: {
+   *     resolvedUri: String,
+   *     [byterange]: {
+   *       offset: Number,
+   *       length: Number
+   *     },
+   *     [bytes]: Uint8Array
+   *   }
+   * }
+   * ...where [name] denotes optional properties
+   *
+   * @param {Function} xhr - an instance of the xhr wrapper in xhr.js
+   * @param {Object} xhrOptions - the base options to provide to all xhr requests
+   * @param {WebWorker} decryptionWorker - a WebWorker interface to AES-128
+   *                                       decryption routines
+   * @param {Object} segment - a simplified copy of the segmentInfo object
+   *                           from SegmentLoader
+   * @param {Function} progressFn - a callback that receives progress events from the main
+   *                                segment's xhr request
+   * @param {Function} doneFn - a callback that is executed only once all requests have
+   *                            succeeded or failed
+   * @returns {Function} a function that, when invoked, immediately aborts all
+   *                     outstanding requests
+   */
+  var mediaSegmentRequest = function mediaSegmentRequest(xhr, xhrOptions, decryptionWorker, segment, progressFn, doneFn) {
+    var activeXhrs = [];
+    var finishProcessingFn = waitForCompletion(activeXhrs, decryptionWorker, doneFn);
+
+    // optionally, request the decryption key
+    if (segment.key) {
+      var keyRequestOptions = videojs$1.mergeOptions(xhrOptions, {
+        uri: segment.key.resolvedUri,
+        responseType: 'arraybuffer'
+      });
+      var keyRequestCallback = handleKeyResponse(segment, finishProcessingFn);
+      var keyXhr = xhr(keyRequestOptions, keyRequestCallback);
+
+      activeXhrs.push(keyXhr);
+    }
+
+    // optionally, request the associated media init segment
+    if (segment.map && !segment.map.bytes) {
+      var initSegmentOptions = videojs$1.mergeOptions(xhrOptions, {
+        uri: segment.map.resolvedUri,
+        responseType: 'arraybuffer',
+        headers: segmentXhrHeaders(segment.map)
+      });
+      var initSegmentRequestCallback = handleInitSegmentResponse(segment, finishProcessingFn);
+      var initSegmentXhr = xhr(initSegmentOptions, initSegmentRequestCallback);
+
+      activeXhrs.push(initSegmentXhr);
+    }
+
+    var segmentRequestOptions = videojs$1.mergeOptions(xhrOptions, {
+      uri: segment.resolvedUri,
+      responseType: 'arraybuffer',
+      headers: segmentXhrHeaders(segment)
+    });
+    var segmentRequestCallback = handleSegmentResponse(segment, finishProcessingFn);
+    var segmentXhr = xhr(segmentRequestOptions, segmentRequestCallback);
+
+    segmentXhr.addEventListener('progress', handleProgress(segment, progressFn));
+    activeXhrs.push(segmentXhr);
+
+    return function () {
+      return abortAll(activeXhrs);
+    };
+  };
+
+  /**
+   * @file - codecs.js - Handles tasks regarding codec strings such as translating them to
+   * codec strings, or translating codec strings into objects that can be examined.
+   */
+
+    // Default codec parameters if none were provided for video and/or audio
+  var defaultCodecs = {
+      videoCodec: 'avc1',
+      videoObjectTypeIndicator: '.4d400d',
+      // AAC-LC
+      audioProfile: '2'
+    };
+
+  /**
+   * Parses a codec string to retrieve the number of codecs specified,
+   * the video codec and object type indicator, and the audio profile.
+   */
+
+  var parseCodecs = function parseCodecs() {
+    var codecs = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
+
+    var result = {
+      codecCount: 0
+    };
+    var parsed = void 0;
+
+    result.codecCount = codecs.split(',').length;
+    result.codecCount = result.codecCount || 2;
+
+    // parse the video codec
+    parsed = /(^|\s|,)+(avc[13])([^ ,]*)/i.exec(codecs);
+    if (parsed) {
+      result.videoCodec = parsed[2];
+      result.videoObjectTypeIndicator = parsed[3];
+    }
+
+    // parse the last field of the audio codec
+    result.audioProfile = /(^|\s|,)+mp4a.[0-9A-Fa-f]+\.([0-9A-Fa-f]+)/i.exec(codecs);
+    result.audioProfile = result.audioProfile && result.audioProfile[2];
+
+    return result;
+  };
+
+  /**
+   * Replace codecs in the codec string with the old apple-style `avc1.<dd>.<dd>` to the
+   * standard `avc1.<hhhhhh>`.
+   *
+   * @param codecString {String} the codec string
+   * @return {String} the codec string with old apple-style codecs replaced
+   *
+   * @private
+   */
+  var mapLegacyAvcCodecs = function mapLegacyAvcCodecs(codecString) {
+    return codecString.replace(/avc1\.(\d+)\.(\d+)/i, function (match) {
+      return translateLegacyCodecs([match])[0];
+    });
+  };
+
+  /**
+   * Build a media mime-type string from a set of parameters
+   * @param {String} type either 'audio' or 'video'
+   * @param {String} container either 'mp2t' or 'mp4'
+   * @param {Array} codecs an array of codec strings to add
+   * @return {String} a valid media mime-type
+   */
+  var makeMimeTypeString = function makeMimeTypeString(type, container, codecs) {
+    // The codecs array is filtered so that falsey values are
+    // dropped and don't cause Array#join to create spurious
+    // commas
+    return type + '/' + container + '; codecs="' + codecs.filter(function (c) {
+      return !!c;
+    }).join(', ') + '"';
+  };
+
+  /**
+   * Returns the type container based on information in the playlist
+   * @param {Playlist} media the current media playlist
+   * @return {String} a valid media container type
+   */
+  var getContainerType = function getContainerType(media) {
+    // An initialization segment means the media playlist is an iframe
+    // playlist or is using the mp4 container. We don't currently
+    // support iframe playlists, so assume this is signalling mp4
+    // fragments.
+    if (media.segments && media.segments.length && media.segments[0].map) {
+      return 'mp4';
+    }
+    return 'mp2t';
+  };
+
+  /**
+   * Returns a set of codec strings parsed from the playlist or the default
+   * codec strings if no codecs were specified in the playlist
+   * @param {Playlist} media the current media playlist
+   * @return {Object} an object with the video and audio codecs
+   */
+  var getCodecs = function getCodecs(media) {
+    // if the codecs were explicitly specified, use them instead of the
+    // defaults
+    var mediaAttributes = media.attributes || {};
+
+    if (mediaAttributes.CODECS) {
+      return parseCodecs(mediaAttributes.CODECS);
+    }
+    return defaultCodecs;
+  };
+
+  var audioProfileFromDefault = function audioProfileFromDefault(master, audioGroupId) {
+    if (!master.mediaGroups.AUDIO || !audioGroupId) {
+      return null;
+    }
+
+    var audioGroup = master.mediaGroups.AUDIO[audioGroupId];
+
+    if (!audioGroup) {
+      return null;
+    }
+
+    for (var name in audioGroup) {
+      var audioType = audioGroup[name];
+
+      if (audioType.default && audioType.playlists) {
+        // codec should be the same for all playlists within the audio type
+        return parseCodecs(audioType.playlists[0].attributes.CODECS).audioProfile;
+      }
+    }
+
+    return null;
+  };
+
+  /**
+   * Calculates the MIME type strings for a working configuration of
+   * SourceBuffers to playOld variant streams in a master playlist. If
+   * there is no possible working configuration, an empty array will be
+   * returned.
+   *
+   * @param master {Object} the m3u8 object for the master playlist
+   * @param media {Object} the m3u8 object for the variant playlist
+   * @return {Array} the MIME type strings. If the array has more than
+   * one entry, the first element should be applied to the video
+   * SourceBuffer and the second to the audio SourceBuffer.
+   *
+   * @private
+   */
+  var mimeTypesForPlaylist = function mimeTypesForPlaylist(master, media) {
+    var containerType = getContainerType(media);
+    var codecInfo = getCodecs(media);
+    var mediaAttributes = media.attributes || {};
+    // Default condition for a traditional HLS (no demuxed audio/video)
+    var isMuxed = true;
+    var isMaat = false;
+
+    if (!media) {
+      // Not enough information
+      return [];
+    }
+
+    if (master.mediaGroups.AUDIO && mediaAttributes.AUDIO) {
+      var audioGroup = master.mediaGroups.AUDIO[mediaAttributes.AUDIO];
+
+      // Handle the case where we are in a multiple-audio track scenario
+      if (audioGroup) {
+        isMaat = true;
+        // Start with the everything demuxed then...
+        isMuxed = false;
+        // ...check to see if any audio group tracks are muxed (ie. lacking a uri)
+        for (var groupId in audioGroup) {
+          // either a uri is present (if the case of HLS and an external playlist), or
+          // playlists is present (in the case of DASH where we don't have external audio
+          // playlists)
+          if (!audioGroup[groupId].uri && !audioGroup[groupId].playlists) {
+            isMuxed = true;
+            break;
+          }
+        }
+      }
+    }
+
+    // HLS with multiple-audio tracks must always get an audio codec.
+    // Put another way, there is no way to have a video-only multiple-audio HLS!
+    if (isMaat && !codecInfo.audioProfile) {
+      if (!isMuxed) {
+        // It is possible for codecs to be specified on the audio media group playlist but
+        // not on the rendition playlist. This is mostly the case for DASH, where audio and
+        // video are always separate (and separately specified).
+        codecInfo.audioProfile = audioProfileFromDefault(master, mediaAttributes.AUDIO);
+      }
+
+      if (!codecInfo.audioProfile) {
+        videojs$1.log.warn('Multiple audio tracks present but no audio codec string is specified. ' + 'Attempting to use the default audio codec (mp4a.40.2)');
+        codecInfo.audioProfile = defaultCodecs.audioProfile;
+      }
+    }
+
+    // Generate the final codec strings from the codec object generated above
+    var codecStrings = {};
+
+    if (codecInfo.videoCodec) {
+      codecStrings.video = '' + codecInfo.videoCodec + codecInfo.videoObjectTypeIndicator;
+    }
+
+    if (codecInfo.audioProfile) {
+      codecStrings.audio = 'mp4a.40.' + codecInfo.audioProfile;
+    }
+
+    // Finally, make and return an array with proper mime-types depending on
+    // the configuration
+    var justAudio = makeMimeTypeString('audio', containerType, [codecStrings.audio]);
+    var justVideo = makeMimeTypeString('video', containerType, [codecStrings.video]);
+    var bothVideoAudio = makeMimeTypeString('video', containerType, [codecStrings.video, codecStrings.audio]);
+
+    if (isMaat) {
+      if (!isMuxed && codecStrings.video) {
+        return [justVideo, justAudio];
+      }
+
+      if (!isMuxed && !codecStrings.video) {
+        // There is no muxed content and no video codec string, so this is an audio only
+        // stream with alternate audio.
+        return [justAudio, justAudio];
+      }
+
+      // There exists the possiblity that this will return a `video/container`
+      // mime-type for the first entry in the array even when there is only audio.
+      // This doesn't appear to be a problem and simplifies the code.
+      return [bothVideoAudio, justAudio];
+    }
+
+    // If there is no video codec at all, always just return a single
+    // audio/<container> mime-type
+    if (!codecStrings.video) {
+      return [justAudio];
+    }
+
+    // When not using separate audio media groups, audio and video is
+    // *always* muxed
+    return [bothVideoAudio];
+  };
+
+  // Utilities
+
+  /**
+   * Returns the CSS value for the specified property on an element
+   * using `getComputedStyle`. Firefox has a long-standing issue where
+   * getComputedStyle() may return null when running in an iframe with
+   * `display: none`.
+   *
+   * @see https://bugzilla.mozilla.org/show_bug.cgi?id=548397
+   * @param {HTMLElement} el the htmlelement to work on
+   * @param {string} the proprety to get the style for
+   */
+  var safeGetComputedStyle = function safeGetComputedStyle(el, property) {
+    var result = void 0;
+
+    if (!el) {
+      return '';
+    }
+
+    result = window_1.getComputedStyle(el);
+    if (!result) {
+      return '';
+    }
+
+    return result[property];
+  };
+
+  /**
+   * Resuable stable sort function
+   *
+   * @param {Playlists} array
+   * @param {Function} sortFn Different comparators
+   * @function stableSort
+   */
+  var stableSort = function stableSort(array, sortFn) {
+    var newArray = array.slice();
+
+    array.sort(function (left, right) {
+      var cmp = sortFn(left, right);
+
+      if (cmp === 0) {
+        return newArray.indexOf(left) - newArray.indexOf(right);
+      }
+      return cmp;
+    });
+  };
+
+  /**
+   * A comparator function to sort two playlist object by bandwidth.
+   *
+   * @param {Object} left a media playlist object
+   * @param {Object} right a media playlist object
+   * @return {Number} Greater than zero if the bandwidth attribute of
+   * left is greater than the corresponding attribute of right. Less
+   * than zero if the bandwidth of right is greater than left and
+   * exactly zero if the two are equal.
+   */
+  var comparePlaylistBandwidth = function comparePlaylistBandwidth(left, right) {
+    var leftBandwidth = void 0;
+    var rightBandwidth = void 0;
+
+    if (left.attributes.BANDWIDTH) {
+      leftBandwidth = left.attributes.BANDWIDTH;
+    }
+    leftBandwidth = leftBandwidth || window_1.Number.MAX_VALUE;
+    if (right.attributes.BANDWIDTH) {
+      rightBandwidth = right.attributes.BANDWIDTH;
+    }
+    rightBandwidth = rightBandwidth || window_1.Number.MAX_VALUE;
+
+    return leftBandwidth - rightBandwidth;
+  };
+
+  /**
+   * A comparator function to sort two playlist object by resolution (width).
+   * @param {Object} left a media playlist object
+   * @param {Object} right a media playlist object
+   * @return {Number} Greater than zero if the resolution.width attribute of
+   * left is greater than the corresponding attribute of right. Less
+   * than zero if the resolution.width of right is greater than left and
+   * exactly zero if the two are equal.
+   */
+  var comparePlaylistResolution = function comparePlaylistResolution(left, right) {
+    var leftWidth = void 0;
+    var rightWidth = void 0;
+
+    if (left.attributes.RESOLUTION && left.attributes.RESOLUTION.width) {
+      leftWidth = left.attributes.RESOLUTION.width;
+    }
+
+    leftWidth = leftWidth || window_1.Number.MAX_VALUE;
+
+    if (right.attributes.RESOLUTION && right.attributes.RESOLUTION.width) {
+      rightWidth = right.attributes.RESOLUTION.width;
+    }
+
+    rightWidth = rightWidth || window_1.Number.MAX_VALUE;
+
+    // NOTE - Fallback to bandwidth sort as appropriate in cases where multiple renditions
+    // have the same media dimensions/ resolution
+    if (leftWidth === rightWidth && left.attributes.BANDWIDTH && right.attributes.BANDWIDTH) {
+      return left.attributes.BANDWIDTH - right.attributes.BANDWIDTH;
+    }
+    return leftWidth - rightWidth;
+  };
+
+  /**
+   * Chooses the appropriate media playlist based on bandwidth and player size
+   *
+   * @param {Object} master
+   *        Object representation of the master manifest
+   * @param {Number} playerBandwidth
+   *        Current calculated bandwidth of the player
+   * @param {Number} playerWidth
+   *        Current width of the player element
+   * @param {Number} playerHeight
+   *        Current height of the player element
+   * @return {Playlist} the highest bitrate playlist less than the
+   * currently detected bandwidth, accounting for some amount of
+   * bandwidth variance
+   */
+  var simpleSelector = function simpleSelector(master, playerBandwidth, playerWidth, playerHeight) {
+    // convert the playlists to an intermediary representation to make comparisons easier
+    var sortedPlaylistReps = master.playlists.map(function (playlist) {
+      var width = void 0;
+      var height = void 0;
+      var bandwidth = void 0;
+
+      width = playlist.attributes.RESOLUTION && playlist.attributes.RESOLUTION.width;
+      height = playlist.attributes.RESOLUTION && playlist.attributes.RESOLUTION.height;
+      bandwidth = playlist.attributes.BANDWIDTH;
+
+      bandwidth = bandwidth || window_1.Number.MAX_VALUE;
+
+      return {
+        bandwidth: bandwidth,
+        width: width,
+        height: height,
+        playlist: playlist
+      };
+    });
+
+    stableSort(sortedPlaylistReps, function (left, right) {
+      return left.bandwidth - right.bandwidth;
+    });
+
+    // filter out any playlists that have been excluded due to
+    // incompatible configurations
+    sortedPlaylistReps = sortedPlaylistReps.filter(function (rep) {
+      return !Playlist.isIncompatible(rep.playlist);
+    });
+
+    // filter out any playlists that have been disabled manually through the representations
+    // api or blacklisted temporarily due to playback errors.
+    var enabledPlaylistReps = sortedPlaylistReps.filter(function (rep) {
+      return Playlist.isEnabled(rep.playlist);
+    });
+
+    if (!enabledPlaylistReps.length) {
+      // if there are no enabled playlists, then they have all been blacklisted or disabled
+      // by the user through the representations api. In this case, ignore blacklisting and
+      // fallback to what the user wants by using playlists the user has not disabled.
+      enabledPlaylistReps = sortedPlaylistReps.filter(function (rep) {
+        return !Playlist.isDisabled(rep.playlist);
+      });
+    }
+
+    // filter out any variant that has greater effective bitrate
+    // than the current estimated bandwidth
+    var bandwidthPlaylistReps = enabledPlaylistReps.filter(function (rep) {
+      return rep.bandwidth * Config.BANDWIDTH_VARIANCE < playerBandwidth;
+    });
+
+    var highestRemainingBandwidthRep = bandwidthPlaylistReps[bandwidthPlaylistReps.length - 1];
+
+    // get all of the renditions with the same (highest) bandwidth
+    // and then taking the very first element
+    var bandwidthBestRep = bandwidthPlaylistReps.filter(function (rep) {
+      return rep.bandwidth === highestRemainingBandwidthRep.bandwidth;
+    })[0];
+
+    // filter out playlists without resolution information
+    var haveResolution = bandwidthPlaylistReps.filter(function (rep) {
+      return rep.width && rep.height;
+    });
+
+    // sort variants by resolution
+    stableSort(haveResolution, function (left, right) {
+      return left.width - right.width;
+    });
+
+    // if we have the exact resolution as the player use it
+    var resolutionBestRepList = haveResolution.filter(function (rep) {
+      return rep.width === playerWidth && rep.height === playerHeight;
+    });
+
+    highestRemainingBandwidthRep = resolutionBestRepList[resolutionBestRepList.length - 1];
+    // ensure that we pick the highest bandwidth variant that have exact resolution
+    var resolutionBestRep = resolutionBestRepList.filter(function (rep) {
+      return rep.bandwidth === highestRemainingBandwidthRep.bandwidth;
+    })[0];
+
+    var resolutionPlusOneList = void 0;
+    var resolutionPlusOneSmallest = void 0;
+    var resolutionPlusOneRep = void 0;
+
+    // find the smallest variant that is larger than the player
+    // if there is no match of exact resolution
+    if (!resolutionBestRep) {
+      resolutionPlusOneList = haveResolution.filter(function (rep) {
+        return rep.width > playerWidth || rep.height > playerHeight;
+      });
+
+      // find all the variants have the same smallest resolution
+      resolutionPlusOneSmallest = resolutionPlusOneList.filter(function (rep) {
+        return rep.width === resolutionPlusOneList[0].width && rep.height === resolutionPlusOneList[0].height;
+      });
+
+      // ensure that we also pick the highest bandwidth variant that
+      // is just-larger-than the video player
+      highestRemainingBandwidthRep = resolutionPlusOneSmallest[resolutionPlusOneSmallest.length - 1];
+      resolutionPlusOneRep = resolutionPlusOneSmallest.filter(function (rep) {
+        return rep.bandwidth === highestRemainingBandwidthRep.bandwidth;
+      })[0];
+    }
+
+    // fallback chain of variants
+    var chosenRep = resolutionPlusOneRep || resolutionBestRep || bandwidthBestRep || enabledPlaylistReps[0] || sortedPlaylistReps[0];
+
+    return chosenRep ? chosenRep.playlist : null;
+  };
+
+  // Playlist Selectors
+
+  /**
+   * Chooses the appropriate media playlist based on the most recent
+   * bandwidth estimate and the player size.
+   *
+   * Expects to be called within the context of an instance of HlsHandler
+   *
+   * @return {Playlist} the highest bitrate playlist less than the
+   * currently detected bandwidth, accounting for some amount of
+   * bandwidth variance
+   */
+  var lastBandwidthSelector = function lastBandwidthSelector() {
+    return simpleSelector(this.playlists.master, this.systemBandwidth, parseInt(safeGetComputedStyle(this.tech_.el(), 'width'), 10), parseInt(safeGetComputedStyle(this.tech_.el(), 'height'), 10));
+  };
+
+  /**
+   * Chooses the appropriate media playlist based on the potential to rebuffer
+   *
+   * @param {Object} settings
+   *        Object of information required to use this selector
+   * @param {Object} settings.master
+   *        Object representation of the master manifest
+   * @param {Number} settings.currentTime
+   *        The current time of the player
+   * @param {Number} settings.bandwidth
+   *        Current measured bandwidth
+   * @param {Number} settings.duration
+   *        Duration of the media
+   * @param {Number} settings.segmentDuration
+   *        Segment duration to be used in round trip time calculations
+   * @param {Number} settings.timeUntilRebuffer
+   *        Time left in seconds until the player has to rebuffer
+   * @param {Number} settings.currentTimeline
+   *        The current timeline segments are being loaded from
+   * @param {SyncController} settings.syncController
+   *        SyncController for determining if we have a sync point for a given playlist
+   * @return {Object|null}
+   *         {Object} return.playlist
+   *         The highest bandwidth playlist with the least amount of rebuffering
+   *         {Number} return.rebufferingImpact
+   *         The amount of time in seconds switching to this playlist will rebuffer. A
+   *         negative value means that switching will cause zero rebuffering.
+   */
+  var minRebufferMaxBandwidthSelector = function minRebufferMaxBandwidthSelector(settings) {
+    var master = settings.master,
+      currentTime = settings.currentTime,
+      bandwidth = settings.bandwidth,
+      duration$$1 = settings.duration,
+      segmentDuration = settings.segmentDuration,
+      timeUntilRebuffer = settings.timeUntilRebuffer,
+      currentTimeline = settings.currentTimeline,
+      syncController = settings.syncController;
+
+    // filter out any playlists that have been excluded due to
+    // incompatible configurations
+
+    var compatiblePlaylists = master.playlists.filter(function (playlist) {
+      return !Playlist.isIncompatible(playlist);
+    });
+
+    // filter out any playlists that have been disabled manually through the representations
+    // api or blacklisted temporarily due to playback errors.
+    var enabledPlaylists = compatiblePlaylists.filter(Playlist.isEnabled);
+
+    if (!enabledPlaylists.length) {
+      // if there are no enabled playlists, then they have all been blacklisted or disabled
+      // by the user through the representations api. In this case, ignore blacklisting and
+      // fallback to what the user wants by using playlists the user has not disabled.
+      enabledPlaylists = compatiblePlaylists.filter(function (playlist) {
+        return !Playlist.isDisabled(playlist);
+      });
+    }
+
+    var bandwidthPlaylists = enabledPlaylists.filter(Playlist.hasAttribute.bind(null, 'BANDWIDTH'));
+
+    var rebufferingEstimates = bandwidthPlaylists.map(function (playlist) {
+      var syncPoint = syncController.getSyncPoint(playlist, duration$$1, currentTimeline, currentTime);
+      // If there is no sync point for this playlist, switching to it will require a
+      // sync request first. This will double the request time
+      var numRequests = syncPoint ? 1 : 2;
+      var requestTimeEstimate = Playlist.estimateSegmentRequestTime(segmentDuration, bandwidth, playlist);
+      var rebufferingImpact = requestTimeEstimate * numRequests - timeUntilRebuffer;
+
+      return {
+        playlist: playlist,
+        rebufferingImpact: rebufferingImpact
+      };
+    });
+
+    var noRebufferingPlaylists = rebufferingEstimates.filter(function (estimate) {
+      return estimate.rebufferingImpact <= 0;
+    });
+
+    // Sort by bandwidth DESC
+    stableSort(noRebufferingPlaylists, function (a, b) {
+      return comparePlaylistBandwidth(b.playlist, a.playlist);
+    });
+
+    if (noRebufferingPlaylists.length) {
+      return noRebufferingPlaylists[0];
+    }
+
+    stableSort(rebufferingEstimates, function (a, b) {
+      return a.rebufferingImpact - b.rebufferingImpact;
+    });
+
+    return rebufferingEstimates[0] || null;
+  };
+
+  /**
+   * Chooses the appropriate media playlist, which in this case is the lowest bitrate
+   * one with video.  If no renditions with video exist, return the lowest audio rendition.
+   *
+   * Expects to be called within the context of an instance of HlsHandler
+   *
+   * @return {Object|null}
+   *         {Object} return.playlist
+   *         The lowest bitrate playlist that contains a video codec.  If no such rendition
+   *         exists pick the lowest audio rendition.
+   */
+  var lowestBitrateCompatibleVariantSelector = function lowestBitrateCompatibleVariantSelector() {
+    // filter out any playlists that have been excluded due to
+    // incompatible configurations or playback errors
+    var playlists = this.playlists.master.playlists.filter(Playlist.isEnabled);
+
+    // Sort ascending by bitrate
+    stableSort(playlists, function (a, b) {
+      return comparePlaylistBandwidth(a, b);
+    });
+
+    // Parse and assume that playlists with no video codec have no video
+    // (this is not necessarily true, although it is generally true).
+    //
+    // If an entire manifest has no valid videos everything will get filtered
+    // out.
+    var playlistsWithVideo = playlists.filter(function (playlist) {
+      return parseCodecs(playlist.attributes.CODECS).videoCodec;
+    });
+
+    return playlistsWithVideo[0] || null;
+  };
+
+  /**
+   * @file segment-loader.js
+   */
+
+    // in ms
+  var CHECK_BUFFER_DELAY = 500;
+
+  /**
+   * Determines if we should call endOfStream on the media source based
+   * on the state of the buffer or if appened segment was the final
+   * segment in the playlist.
+   *
+   * @param {Object} playlist a media playlist object
+   * @param {Object} mediaSource the MediaSource object
+   * @param {Number} segmentIndex the index of segment we last appended
+   * @returns {Boolean} do we need to call endOfStream on the MediaSource
+   */
+  var detectEndOfStream = function detectEndOfStream(playlist, mediaSource, segmentIndex) {
+    if (!playlist || !mediaSource) {
+      return false;
+    }
+
+    var segments = playlist.segments;
+
+    // determine a few boolean values to help make the branch below easier
+    // to read
+    var appendedLastSegment = segmentIndex === segments.length;
+
+    // if we've buffered to the end of the video, we need to call endOfStream
+    // so that MediaSources can trigger the `ended` event when it runs out of
+    // buffered data instead of waiting for me
+    return playlist.endList && mediaSource.readyState === 'open' && appendedLastSegment;
+  };
+
+  var finite = function finite(num) {
+    return typeof num === 'number' && isFinite(num);
+  };
+
+  var illegalMediaSwitch = function illegalMediaSwitch(loaderType, startingMedia, newSegmentMedia) {
+    // Although these checks should most likely cover non 'main' types, for now it narrows
+    // the scope of our checks.
+    if (loaderType !== 'main' || !startingMedia || !newSegmentMedia) {
+      return null;
+    }
+
+    if (!newSegmentMedia.containsAudio && !newSegmentMedia.containsVideo) {
+      return 'Neither audio nor video found in segment.';
+    }
+
+    if (startingMedia.containsVideo && !newSegmentMedia.containsVideo) {
+      return 'Only audio found in segment when we expected video.' + ' We can\'t switch to audio only from a stream that had video.' + ' To get rid of this message, please add codec information to the manifest.';
+    }
+
+    if (!startingMedia.containsVideo && newSegmentMedia.containsVideo) {
+      return 'Video found in segment when we expected only audio.' + ' We can\'t switch to a stream with video from an audio only stream.' + ' To get rid of this message, please add codec information to the manifest.';
+    }
+
+    return null;
+  };
+
+  /**
+   * Calculates a time value that is safe to remove from the back buffer without interupting
+   * playback.
+   *
+   * @param {TimeRange} seekable
+   *        The current seekable range
+   * @param {Number} currentTime
+   *        The current time of the player
+   * @param {Number} targetDuration
+   *        The target duration of the current playlist
+   * @return {Number}
+   *         Time that is safe to remove from the back buffer without interupting playback
+   */
+  var safeBackBufferTrimTime = function safeBackBufferTrimTime(seekable$$1, currentTime, targetDuration) {
+    var removeToTime = void 0;
+
+    if (seekable$$1.length && seekable$$1.start(0) > 0 && seekable$$1.start(0) < currentTime) {
+      // If we have a seekable range use that as the limit for what can be removed safely
+      removeToTime = seekable$$1.start(0);
+    } else {
+      // otherwise remove anything older than 30 seconds before the current playOld head
+      removeToTime = currentTime - 30;
+    }
+
+    // Don't allow removing from the buffer within target duration of current time
+    // to avoid the possibility of removing the GOP currently being played which could
+    // cause playback stalls.
+    return Math.min(removeToTime, currentTime - targetDuration);
+  };
+
+  var segmentInfoString = function segmentInfoString(segmentInfo) {
+    var _segmentInfo$segment = segmentInfo.segment,
+      start = _segmentInfo$segment.start,
+      end = _segmentInfo$segment.end,
+      _segmentInfo$playlist = segmentInfo.playlist,
+      seq = _segmentInfo$playlist.mediaSequence,
+      id = _segmentInfo$playlist.id,
+      _segmentInfo$playlist2 = _segmentInfo$playlist.segments,
+      segments = _segmentInfo$playlist2 === undefined ? [] : _segmentInfo$playlist2,
+      index = segmentInfo.mediaIndex,
+      timeline = segmentInfo.timeline;
+
+
+    return ['appending [' + index + '] of [' + seq + ', ' + (seq + segments.length) + '] from playlist [' + id + ']', '[' + start + ' => ' + end + '] in timeline [' + timeline + ']'].join(' ');
+  };
+
+  /**
+   * An object that manages segment loading and appending.
+   *
+   * @class SegmentLoader
+   * @param {Object} options required and optional options
+   * @extends videojs.EventTarget
+   */
+
+  var SegmentLoader = function (_videojs$EventTarget3) {
+    inherits(SegmentLoader, _videojs$EventTarget3);
+
+    function SegmentLoader(settings) {
+      classCallCheck(this, SegmentLoader);
+
+      // check pre-conditions
+      var _this22 = possibleConstructorReturn(this, _videojs$EventTarget3.call(this));
+
+      if (!settings) {
+        throw new TypeError('Initialization settings are required');
+      }
+      if (typeof settings.currentTime !== 'function') {
+        throw new TypeError('No currentTime getter specified');
+      }
+      if (!settings.mediaSource) {
+        throw new TypeError('No MediaSource specified');
+      }
+      // public properties
+      _this22.bandwidth = settings.bandwidth;
+      _this22.throughput = {rate: 0, count: 0};
+      _this22.roundTrip = NaN;
+      _this22.resetStats_();
+      _this22.mediaIndex = null;
+
+      // private settings
+      _this22.hasPlayed_ = settings.hasPlayed;
+      _this22.currentTime_ = settings.currentTime;
+      _this22.seekable_ = settings.seekable;
+      _this22.seeking_ = settings.seeking;
+      _this22.duration_ = settings.duration;
+      _this22.mediaSource_ = settings.mediaSource;
+      _this22.hls_ = settings.hls;
+      _this22.loaderType_ = settings.loaderType;
+      _this22.startingMedia_ = void 0;
+      _this22.segmentMetadataTrack_ = settings.segmentMetadataTrack;
+      _this22.goalBufferLength_ = settings.goalBufferLength;
+      _this22.sourceType_ = settings.sourceType;
+      _this22.state_ = 'INIT';
+
+      // private instance variables
+      _this22.checkBufferTimeout_ = null;
+      _this22.error_ = void 0;
+      _this22.currentTimeline_ = -1;
+      _this22.pendingSegment_ = null;
+      _this22.mimeType_ = null;
+      _this22.sourceUpdater_ = null;
+      _this22.xhrOptions_ = null;
+
+      // Fragmented mp4 playback
+      _this22.activeInitSegmentId_ = null;
+      _this22.initSegments_ = {};
+
+      _this22.decrypter_ = settings.decrypter;
+
+      // Manages the tracking and generation of sync-points, mappings
+      // between a time in the display time and a segment index within
+      // a playlist
+      _this22.syncController_ = settings.syncController;
+      _this22.syncPoint_ = {
+        segmentIndex: 0,
+        time: 0
+      };
+
+      _this22.syncController_.on('syncinfoupdate', function () {
+        return _this22.trigger('syncinfoupdate');
+      });
+
+      _this22.mediaSource_.addEventListener('sourceopen', function () {
+        return _this22.ended_ = false;
+      });
+
+      // ...for determining the fetch location
+      _this22.fetchAtBuffer_ = false;
+
+      _this22.logger_ = logger('SegmentLoader[' + _this22.loaderType_ + ']');
+
+      Object.defineProperty(_this22, 'state', {
+        get: function get$$1() {
+          return this.state_;
+        },
+        set: function set$$1(newState) {
+          if (newState !== this.state_) {
+            this.logger_(this.state_ + ' -> ' + newState);
+            this.state_ = newState;
+          }
+        }
+      });
+      return _this22;
+    }
+
+    /**
+     * reset all of our media stats
+     *
+     * @private
+     */
+
+
+    SegmentLoader.prototype.resetStats_ = function resetStats_() {
+      this.mediaBytesTransferred = 0;
+      this.mediaRequests = 0;
+      this.mediaRequestsAborted = 0;
+      this.mediaRequestsTimedout = 0;
+      this.mediaRequestsErrored = 0;
+      this.mediaTransferDuration = 0;
+      this.mediaSecondsLoaded = 0;
+    };
+
+    /**
+     * dispose of the SegmentLoader and reset to the default state
+     */
+
+
+    SegmentLoader.prototype.dispose = function dispose() {
+      this.state = 'DISPOSED';
+      this.pause();
+      this.abort_();
+      if (this.sourceUpdater_) {
+        this.sourceUpdater_.dispose();
+      }
+      this.resetStats_();
+    };
+
+    /**
+     * abort anything that is currently doing on with the SegmentLoader
+     * and reset to a default state
+     */
+
+
+    SegmentLoader.prototype.abort = function abort() {
+      if (this.state !== 'WAITING') {
+        if (this.pendingSegment_) {
+          this.pendingSegment_ = null;
+        }
+        return;
+      }
+
+      this.abort_();
+
+      // We aborted the requests we were waiting on, so reset the loader's state to READY
+      // since we are no longer "waiting" on any requests. XHR callback is not always run
+      // when the request is aborted. This will prevent the loader from being stuck in the
+      // WAITING state indefinitely.
+      this.state = 'READY';
+
+      // don't wait for buffer check timeouts to begin fetching the
+      // next segment
+      if (!this.paused()) {
+        this.monitorBuffer_();
+      }
+    };
+
+    /**
+     * abort all pending xhr requests and null any pending segements
+     *
+     * @private
+     */
+
+
+    SegmentLoader.prototype.abort_ = function abort_() {
+      if (this.pendingSegment_) {
+        this.pendingSegment_.abortRequests();
+      }
+
+      // clear out the segment being processed
+      this.pendingSegment_ = null;
+    };
+
+    /**
+     * set an error on the segment loader and null out any pending segements
+     *
+     * @param {Error} error the error to set on the SegmentLoader
+     * @return {Error} the error that was set or that is currently set
+     */
+
+
+    SegmentLoader.prototype.error = function error(_error) {
+      if (typeof _error !== 'undefined') {
+        this.error_ = _error;
+      }
+
+      this.pendingSegment_ = null;
+      return this.error_;
+    };
+
+    SegmentLoader.prototype.endOfStream = function endOfStream() {
+      this.ended_ = true;
+      this.pause();
+      this.trigger('ended');
+    };
+
+    /**
+     * Indicates which time ranges are buffered
+     *
+     * @return {TimeRange}
+     *         TimeRange object representing the current buffered ranges
+     */
+
+
+    SegmentLoader.prototype.buffered_ = function buffered_() {
+      if (!this.sourceUpdater_) {
+        return videojs$1.createTimeRanges();
+      }
+
+      return this.sourceUpdater_.buffered();
+    };
+
+    /**
+     * Gets and sets init segment for the provided map
+     *
+     * @param {Object} map
+     *        The map object representing the init segment to get or set
+     * @param {Boolean=} set
+     *        If true, the init segment for the provided map should be saved
+     * @return {Object}
+     *         map object for desired init segment
+     */
+
+
+    SegmentLoader.prototype.initSegment = function initSegment(map) {
+      var set$$1 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
+
+      if (!map) {
+        return null;
+      }
+
+      var id = initSegmentId(map);
+      var storedMap = this.initSegments_[id];
+
+      if (set$$1 && !storedMap && map.bytes) {
+        this.initSegments_[id] = storedMap = {
+          resolvedUri: map.resolvedUri,
+          byterange: map.byterange,
+          bytes: map.bytes
+        };
+      }
+
+      return storedMap || map;
+    };
+
+    /**
+     * Returns true if all configuration required for loading is present, otherwise false.
+     *
+     * @return {Boolean} True if the all configuration is ready for loading
+     * @private
+     */
+
+
+    SegmentLoader.prototype.couldBeginLoading_ = function couldBeginLoading_() {
+      return this.playlist_ && (
+        // the source updater is created when init_ is called, so either having a
+        // source updater or being in the INIT state with a mimeType is enough
+        // to say we have all the needed configuration to start loading.
+        this.sourceUpdater_ || this.mimeType_ && this.state === 'INIT') && !this.paused();
+    };
+
+    /**
+     * load a playlist and start to fill the buffer
+     */
+
+
+    SegmentLoader.prototype.load = function load() {
+      // un-pause
+      this.monitorBuffer_();
+
+      // if we don't have a playlist yet, keep waiting for one to be
+      // specified
+      if (!this.playlist_) {
+        return;
+      }
+
+      // not sure if this is the best place for this
+      this.syncController_.setDateTimeMapping(this.playlist_);
+
+      // if all the configuration is ready, initialize and begin loading
+      if (this.state === 'INIT' && this.couldBeginLoading_()) {
+        return this.init_();
+      }
+
+      // if we're in the middle of processing a segment already, don't
+      // kick off an additional segment request
+      if (!this.couldBeginLoading_() || this.state !== 'READY' && this.state !== 'INIT') {
+        return;
+      }
+
+      this.state = 'READY';
+    };
+
+    /**
+     * Once all the starting parameters have been specified, begin
+     * operation. This method should only be invoked from the INIT
+     * state.
+     *
+     * @private
+     */
+
+
+    SegmentLoader.prototype.init_ = function init_() {
+      this.state = 'READY';
+      this.sourceUpdater_ = new SourceUpdater(this.mediaSource_, this.mimeType_, this.loaderType_, this.sourceBufferEmitter_);
+      this.resetEverything();
+      return this.monitorBuffer_();
+    };
+
+    /**
+     * set a playlist on the segment loader
+     *
+     * @param {PlaylistLoader} media the playlist to set on the segment loader
+     */
+
+
+    SegmentLoader.prototype.playlist = function playlist(newPlaylist) {
+      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+
+      if (!newPlaylist) {
+        return;
+      }
+
+      var oldPlaylist = this.playlist_;
+      var segmentInfo = this.pendingSegment_;
+
+      this.playlist_ = newPlaylist;
+      this.xhrOptions_ = options;
+
+      // when we haven't started playing yet, the start of a live playlist
+      // is always our zero-time so force a sync update each time the playlist
+      // is refreshed from the server
+      if (!this.hasPlayed_()) {
+        newPlaylist.syncInfo = {
+          mediaSequence: newPlaylist.mediaSequence,
+          time: 0
+        };
+      }
+
+      var oldId = oldPlaylist ? oldPlaylist.id : null;
+
+      this.logger_('playlist update [' + oldId + ' => ' + newPlaylist.id + ']');
+
+      // in VOD, this is always a rendition switch (or we updated our syncInfo above)
+      // in LIVE, we always want to update with new playlists (including refreshes)
+      this.trigger('syncinfoupdate');
+
+      // if we were unpaused but waiting for a playlist, start
+      // buffering now
+      if (this.state === 'INIT' && this.couldBeginLoading_()) {
+        return this.init_();
+      }
+
+      if (!oldPlaylist || oldPlaylist.uri !== newPlaylist.uri) {
+        if (this.mediaIndex !== null) {
+          // we must "resync" the segment loader when we switch renditions and
+          // the segment loader is already synced to the previous rendition
+          this.resyncLoader();
+        }
+
+        // the rest of this function depends on `oldPlaylist` being defined
+        return;
+      }
+
+      // we reloaded the same playlist so we are in a live scenario
+      // and we will likely need to adjust the mediaIndex
+      var mediaSequenceDiff = newPlaylist.mediaSequence - oldPlaylist.mediaSequence;
+
+      this.logger_('live window shift [' + mediaSequenceDiff + ']');
+
+      // update the mediaIndex on the SegmentLoader
+      // this is important because we can abort a request and this value must be
+      // equal to the last appended mediaIndex
+      if (this.mediaIndex !== null) {
+        this.mediaIndex -= mediaSequenceDiff;
+      }
+
+      // update the mediaIndex on the SegmentInfo object
+      // this is important because we will update this.mediaIndex with this value
+      // in `handleUpdateEnd_` after the segment has been successfully appended
+      if (segmentInfo) {
+        segmentInfo.mediaIndex -= mediaSequenceDiff;
+
+        // we need to update the referenced segment so that timing information is
+        // saved for the new playlist's segment, however, if the segment fell off the
+        // playlist, we can leave the old reference and just lose the timing info
+        if (segmentInfo.mediaIndex >= 0) {
+          segmentInfo.segment = newPlaylist.segments[segmentInfo.mediaIndex];
+        }
+      }
+
+      this.syncController_.saveExpiredSegmentInfo(oldPlaylist, newPlaylist);
+    };
+
+    /**
+     * Prevent the loader from fetching additional segments. If there
+     * is a segment request outstanding, it will finish processing
+     * before the loader halts. A segment loader can be unpaused by
+     * calling load().
+     */
+
+
+    SegmentLoader.prototype.pause = function pause() {
+      if (this.checkBufferTimeout_) {
+        window_1.clearTimeout(this.checkBufferTimeout_);
+
+        this.checkBufferTimeout_ = null;
+      }
+    };
+
+    /**
+     * Returns whether the segment loader is fetching additional
+     * segments when given the opportunity. This property can be
+     * modified through calls to pause() and load().
+     */
+
+
+    SegmentLoader.prototype.paused = function paused() {
+      return this.checkBufferTimeout_ === null;
+    };
+
+    /**
+     * create/set the following mimetype on the SourceBuffer through a
+     * SourceUpdater
+     *
+     * @param {String} mimeType the mime type string to use
+     * @param {Object} sourceBufferEmitter an event emitter that fires when a source buffer
+     * is added to the media source
+     */
+
+
+    SegmentLoader.prototype.mimeType = function mimeType(_mimeType, sourceBufferEmitter) {
+      if (this.mimeType_) {
+        return;
+      }
+
+      this.mimeType_ = _mimeType;
+      this.sourceBufferEmitter_ = sourceBufferEmitter;
+      // if we were unpaused but waiting for a sourceUpdater, start
+      // buffering now
+      if (this.state === 'INIT' && this.couldBeginLoading_()) {
+        this.init_();
+      }
+    };
+
+    /**
+     * Delete all the buffered data and reset the SegmentLoader
+     */
+
+
+    SegmentLoader.prototype.resetEverything = function resetEverything() {
+      this.ended_ = false;
+      this.resetLoader();
+      this.remove(0, this.duration_());
+      this.trigger('reseteverything');
+    };
+
+    /**
+     * Force the SegmentLoader to resync and start loading around the currentTime instead
+     * of starting at the end of the buffer
+     *
+     * Useful for fast quality changes
+     */
+
+
+    SegmentLoader.prototype.resetLoader = function resetLoader() {
+      this.fetchAtBuffer_ = false;
+      this.resyncLoader();
+    };
+
+    /**
+     * Force the SegmentLoader to restart synchronization and make a conservative guess
+     * before returning to the simple walk-forward method
+     */
+
+
+    SegmentLoader.prototype.resyncLoader = function resyncLoader() {
+      this.mediaIndex = null;
+      this.syncPoint_ = null;
+      this.abort();
+    };
+
+    /**
+     * Remove any data in the source buffer between start and end times
+     * @param {Number} start - the start time of the region to remove from the buffer
+     * @param {Number} end - the end time of the region to remove from the buffer
+     */
+
+
+    SegmentLoader.prototype.remove = function remove(start, end) {
+      if (this.sourceUpdater_) {
+        this.sourceUpdater_.remove(start, end);
+      }
+      removeCuesFromTrack(start, end, this.segmentMetadataTrack_);
+    };
+
+    /**
+     * (re-)schedule monitorBufferTick_ to run as soon as possible
+     *
+     * @private
+     */
+
+
+    SegmentLoader.prototype.monitorBuffer_ = function monitorBuffer_() {
+      if (this.checkBufferTimeout_) {
+        window_1.clearTimeout(this.checkBufferTimeout_);
+      }
+
+      this.checkBufferTimeout_ = window_1.setTimeout(this.monitorBufferTick_.bind(this), 1);
+    };
+
+    /**
+     * As long as the SegmentLoader is in the READY state, periodically
+     * invoke fillBuffer_().
+     *
+     * @private
+     */
+
+
+    SegmentLoader.prototype.monitorBufferTick_ = function monitorBufferTick_() {
+      if (this.state === 'READY') {
+        this.fillBuffer_();
+      }
+
+      if (this.checkBufferTimeout_) {
+        window_1.clearTimeout(this.checkBufferTimeout_);
+      }
+
+      this.checkBufferTimeout_ = window_1.setTimeout(this.monitorBufferTick_.bind(this), CHECK_BUFFER_DELAY);
+    };
+
+    /**
+     * fill the buffer with segements unless the sourceBuffers are
+     * currently updating
+     *
+     * Note: this function should only ever be called by monitorBuffer_
+     * and never directly
+     *
+     * @private
+     */
+
+
+    SegmentLoader.prototype.fillBuffer_ = function fillBuffer_() {
+      if (this.sourceUpdater_.updating()) {
+        return;
+      }
+
+      if (!this.syncPoint_) {
+        this.syncPoint_ = this.syncController_.getSyncPoint(this.playlist_, this.duration_(), this.currentTimeline_, this.currentTime_());
+      }
+
+      // see if we need to begin loading immediately
+      var segmentInfo = this.checkBuffer_(this.buffered_(), this.playlist_, this.mediaIndex, this.hasPlayed_(), this.currentTime_(), this.syncPoint_);
+
+      if (!segmentInfo) {
+        return;
+      }
+
+      var isEndOfStream = detectEndOfStream(this.playlist_, this.mediaSource_, segmentInfo.mediaIndex);
+
+      if (isEndOfStream) {
+        this.endOfStream();
+        return;
+      }
+
+      if (segmentInfo.mediaIndex === this.playlist_.segments.length - 1 && this.mediaSource_.readyState === 'ended' && !this.seeking_()) {
+        return;
+      }
+
+      // We will need to change timestampOffset of the sourceBuffer if either of
+      // the following conditions are true:
+      // - The segment.timeline !== this.currentTimeline
+      //   (we are crossing a discontinuity somehow)
+      // - The "timestampOffset" for the start of this segment is less than
+      //   the currently set timestampOffset
+      if (segmentInfo.timeline !== this.currentTimeline_ || segmentInfo.startOfSegment !== null && segmentInfo.startOfSegment < this.sourceUpdater_.timestampOffset()) {
+        this.syncController_.reset();
+        segmentInfo.timestampOffset = segmentInfo.startOfSegment;
+      }
+
+      this.loadSegment_(segmentInfo);
+    };
+
+    /**
+     * Determines what segment request should be made, given current playback
+     * state.
+     *
+     * @param {TimeRanges} buffered - the state of the buffer
+     * @param {Object} playlist - the playlist object to fetch segments from
+     * @param {Number} mediaIndex - the previous mediaIndex fetched or null
+     * @param {Boolean} hasPlayed - a flag indicating whether we have played or not
+     * @param {Number} currentTime - the playback position in seconds
+     * @param {Object} syncPoint - a segment info object that describes the
+     * @returns {Object} a segment request object that describes the segment to load
+     */
+
+
+    SegmentLoader.prototype.checkBuffer_ = function checkBuffer_(buffered, playlist, mediaIndex, hasPlayed, currentTime, syncPoint) {
+      var lastBufferedEnd = 0;
+      var startOfSegment = void 0;
+
+      if (buffered.length) {
+        lastBufferedEnd = buffered.end(buffered.length - 1);
+      }
+
+      var bufferedTime = Math.max(0, lastBufferedEnd - currentTime);
+
+      if (!playlist.segments.length) {
+        return null;
+      }
+
+      // if there is plenty of content buffered, and the video has
+      // been played before relax for awhile
+      if (bufferedTime >= this.goalBufferLength_()) {
+        return null;
+      }
+
+      // if the video has not yet played once, and we already have
+      // one segment downloaded do nothing
+      if (!hasPlayed && bufferedTime >= 1) {
+        return null;
+      }
+
+      // When the syncPoint is null, there is no way of determining a good
+      // conservative segment index to fetch from
+      // The best thing to do here is to get the kind of sync-point data by
+      // making a request
+      if (syncPoint === null) {
+        mediaIndex = this.getSyncSegmentCandidate_(playlist);
+        return this.generateSegmentInfo_(playlist, mediaIndex, null, true);
+      }
+
+      // Under normal playback conditions fetching is a simple walk forward
+      if (mediaIndex !== null) {
+        var segment = playlist.segments[mediaIndex];
+
+        if (segment && segment.end) {
+          startOfSegment = segment.end;
+        } else {
+          startOfSegment = lastBufferedEnd;
+        }
+        return this.generateSegmentInfo_(playlist, mediaIndex + 1, startOfSegment, false);
+      }
+
+      // There is a sync-point but the lack of a mediaIndex indicates that
+      // we need to make a good conservative guess about which segment to
+      // fetch
+      if (this.fetchAtBuffer_) {
+        // Find the segment containing the end of the buffer
+        var mediaSourceInfo = Playlist.getMediaInfoForTime(playlist, lastBufferedEnd, syncPoint.segmentIndex, syncPoint.time);
+
+        mediaIndex = mediaSourceInfo.mediaIndex;
+        startOfSegment = mediaSourceInfo.startTime;
+      } else {
+        // Find the segment containing currentTime
+        var _mediaSourceInfo = Playlist.getMediaInfoForTime(playlist, currentTime, syncPoint.segmentIndex, syncPoint.time);
+
+        mediaIndex = _mediaSourceInfo.mediaIndex;
+        startOfSegment = _mediaSourceInfo.startTime;
+      }
+
+      return this.generateSegmentInfo_(playlist, mediaIndex, startOfSegment, false);
+    };
+
+    /**
+     * The segment loader has no recourse except to fetch a segment in the
+     * current playlist and use the internal timestamps in that segment to
+     * generate a syncPoint. This function returns a good candidate index
+     * for that process.
+     *
+     * @param {Object} playlist - the playlist object to look for a
+     * @returns {Number} An index of a segment from the playlist to load
+     */
+
+
+    SegmentLoader.prototype.getSyncSegmentCandidate_ = function getSyncSegmentCandidate_(playlist) {
+      var _this23 = this;
+
+      if (this.currentTimeline_ === -1) {
+        return 0;
+      }
+
+      var segmentIndexArray = playlist.segments.map(function (s, i) {
+        return {
+          timeline: s.timeline,
+          segmentIndex: i
+        };
+      }).filter(function (s) {
+        return s.timeline === _this23.currentTimeline_;
+      });
+
+      if (segmentIndexArray.length) {
+        return segmentIndexArray[Math.min(segmentIndexArray.length - 1, 1)].segmentIndex;
+      }
+
+      return Math.max(playlist.segments.length - 1, 0);
+    };
+
+    SegmentLoader.prototype.generateSegmentInfo_ = function generateSegmentInfo_(playlist, mediaIndex, startOfSegment, isSyncRequest) {
+      if (mediaIndex < 0 || mediaIndex >= playlist.segments.length) {
+        return null;
+      }
+
+      var segment = playlist.segments[mediaIndex];
+
+      return {
+        requestId: 'segment-loader-' + Math.random(),
+        // resolve the segment URL relative to the playlist
+        uri: segment.resolvedUri,
+        // the segment's mediaIndex at the time it was requested
+        mediaIndex: mediaIndex,
+        // whether or not to update the SegmentLoader's state with this
+        // segment's mediaIndex
+        isSyncRequest: isSyncRequest,
+        startOfSegment: startOfSegment,
+        // the segment's playlist
+        playlist: playlist,
+        // unencrypted bytes of the segment
+        bytes: null,
+        // when a key is defined for this segment, the encrypted bytes
+        encryptedBytes: null,
+        // The target timestampOffset for this segment when we append it
+        // to the source buffer
+        timestampOffset: null,
+        // The timeline that the segment is in
+        timeline: segment.timeline,
+        // The expected duration of the segment in seconds
+        duration: segment.duration,
+        // retain the segment in case the playlist updates while doing an async process
+        segment: segment
+      };
+    };
+
+    /**
+     * Determines if the network has enough bandwidth to complete the current segment
+     * request in a timely manner. If not, the request will be aborted early and bandwidth
+     * updated to trigger a playlist switch.
+     *
+     * @param {Object} stats
+     *        Object containing stats about the request timing and size
+     * @return {Boolean} True if the request was aborted, false otherwise
+     * @private
+     */
+
+
+    SegmentLoader.prototype.abortRequestEarly_ = function abortRequestEarly_(stats) {
+      if (this.hls_.tech_.paused() ||
+        // Don't abort if the current playlist is on the lowestEnabledRendition
+        // TODO: Replace using timeout with a boolean indicating whether this playlist is
+        //       the lowestEnabledRendition.
+        !this.xhrOptions_.timeout ||
+        // Don't abort if we have no bandwidth information to estimate segment sizes
+        !this.playlist_.attributes.BANDWIDTH) {
+        return false;
+      }
+
+      // Wait at least 1 second since the first byte of data has been received before
+      // using the calculated bandwidth from the progress event to allow the bitrate
+      // to stabilize
+      if (Date.now() - (stats.firstBytesReceivedAt || Date.now()) < 1000) {
+        return false;
+      }
+
+      var currentTime = this.currentTime_();
+      var measuredBandwidth = stats.bandwidth;
+      var segmentDuration = this.pendingSegment_.duration;
+
+      var requestTimeRemaining = Playlist.estimateSegmentRequestTime(segmentDuration, measuredBandwidth, this.playlist_, stats.bytesReceived);
+
+      // Subtract 1 from the timeUntilRebuffer so we still consider an early abort
+      // if we are only left with less than 1 second when the request completes.
+      // A negative timeUntilRebuffering indicates we are already rebuffering
+      var timeUntilRebuffer$$1 = timeUntilRebuffer(this.buffered_(), currentTime, this.hls_.tech_.playbackRate()) - 1;
+
+      // Only consider aborting early if the estimated time to finish the download
+      // is larger than the estimated time until the player runs out of forward buffer
+      if (requestTimeRemaining <= timeUntilRebuffer$$1) {
+        return false;
+      }
+
+      var switchCandidate = minRebufferMaxBandwidthSelector({
+        master: this.hls_.playlists.master,
+        currentTime: currentTime,
+        bandwidth: measuredBandwidth,
+        duration: this.duration_(),
+        segmentDuration: segmentDuration,
+        timeUntilRebuffer: timeUntilRebuffer$$1,
+        currentTimeline: this.currentTimeline_,
+        syncController: this.syncController_
+      });
+
+      if (!switchCandidate) {
+        return;
+      }
+
+      var rebufferingImpact = requestTimeRemaining - timeUntilRebuffer$$1;
+
+      var timeSavedBySwitching = rebufferingImpact - switchCandidate.rebufferingImpact;
+
+      var minimumTimeSaving = 0.5;
+
+      // If we are already rebuffering, increase the amount of variance we add to the
+      // potential round trip time of the new request so that we are not too aggressive
+      // with switching to a playlist that might save us a fraction of a second.
+      if (timeUntilRebuffer$$1 <= TIME_FUDGE_FACTOR) {
+        minimumTimeSaving = 1;
+      }
+
+      if (!switchCandidate.playlist || switchCandidate.playlist.uri === this.playlist_.uri || timeSavedBySwitching < minimumTimeSaving) {
+        return false;
+      }
+
+      // set the bandwidth to that of the desired playlist being sure to scale by
+      // BANDWIDTH_VARIANCE and add one so the playlist selector does not exclude it
+      // don't trigger a bandwidthupdate as the bandwidth is artifial
+      this.bandwidth = switchCandidate.playlist.attributes.BANDWIDTH * Config.BANDWIDTH_VARIANCE + 1;
+      this.abort();
+      this.trigger('earlyabort');
+      return true;
+    };
+
+    /**
+     * XHR `progress` event handler
+     *
+     * @param {Event}
+     *        The XHR `progress` event
+     * @param {Object} simpleSegment
+     *        A simplified segment object copy
+     * @private
+     */
+
+
+    SegmentLoader.prototype.handleProgress_ = function handleProgress_(event, simpleSegment) {
+      if (!this.pendingSegment_ || simpleSegment.requestId !== this.pendingSegment_.requestId || this.abortRequestEarly_(simpleSegment.stats)) {
+        return;
+      }
+
+      this.trigger('progress');
+    };
+
+    /**
+     * load a specific segment from a request into the buffer
+     *
+     * @private
+     */
+
+
+    SegmentLoader.prototype.loadSegment_ = function loadSegment_(segmentInfo) {
+      this.state = 'WAITING';
+      this.pendingSegment_ = segmentInfo;
+      this.trimBackBuffer_(segmentInfo);
+
+      segmentInfo.abortRequests = mediaSegmentRequest(this.hls_.xhr, this.xhrOptions_, this.decrypter_, this.createSimplifiedSegmentObj_(segmentInfo),
+        // progress callback
+        this.handleProgress_.bind(this), this.segmentRequestFinished_.bind(this));
+    };
+
+    /**
+     * trim the back buffer so that we don't have too much data
+     * in the source buffer
+     *
+     * @private
+     *
+     * @param {Object} segmentInfo - the current segment
+     */
+
+
+    SegmentLoader.prototype.trimBackBuffer_ = function trimBackBuffer_(segmentInfo) {
+      var removeToTime = safeBackBufferTrimTime(this.seekable_(), this.currentTime_(), this.playlist_.targetDuration || 10);
+
+      // Chrome has a hard limit of 150MB of
+      // buffer and a very conservative "garbage collector"
+      // We manually clear out the old buffer to ensure
+      // we don't trigger the QuotaExceeded error
+      // on the source buffer during subsequent appends
+
+      if (removeToTime > 0) {
+        this.remove(0, removeToTime);
+      }
+    };
+
+    /**
+     * created a simplified copy of the segment object with just the
+     * information necessary to perform the XHR and decryption
+     *
+     * @private
+     *
+     * @param {Object} segmentInfo - the current segment
+     * @returns {Object} a simplified segment object copy
+     */
+
+
+    SegmentLoader.prototype.createSimplifiedSegmentObj_ = function createSimplifiedSegmentObj_(segmentInfo) {
+      var segment = segmentInfo.segment;
+      var simpleSegment = {
+        resolvedUri: segment.resolvedUri,
+        byterange: segment.byterange,
+        requestId: segmentInfo.requestId
+      };
+
+      if (segment.key) {
+        // if the media sequence is greater than 2^32, the IV will be incorrect
+        // assuming 10s segments, that would be about 1300 years
+        var iv = segment.key.iv || new Uint32Array([0, 0, 0, segmentInfo.mediaIndex + segmentInfo.playlist.mediaSequence]);
+
+        simpleSegment.key = {
+          resolvedUri: segment.key.resolvedUri,
+          iv: iv
+        };
+      }
+
+      if (segment.map) {
+        simpleSegment.map = this.initSegment(segment.map);
+      }
+
+      return simpleSegment;
+    };
+
+    /**
+     * Handle the callback from the segmentRequest function and set the
+     * associated SegmentLoader state and errors if necessary
+     *
+     * @private
+     */
+
+
+    SegmentLoader.prototype.segmentRequestFinished_ = function segmentRequestFinished_(error, simpleSegment) {
+      // every request counts as a media request even if it has been aborted
+      // or canceled due to a timeout
+      this.mediaRequests += 1;
+
+      if (simpleSegment.stats) {
+        this.mediaBytesTransferred += simpleSegment.stats.bytesReceived;
+        this.mediaTransferDuration += simpleSegment.stats.roundTripTime;
+      }
+
+      // The request was aborted and the SegmentLoader has already been reset
+      if (!this.pendingSegment_) {
+        this.mediaRequestsAborted += 1;
+        return;
+      }
+
+      // the request was aborted and the SegmentLoader has already started
+      // another request. this can happen when the timeout for an aborted
+      // request triggers due to a limitation in the XHR library
+      // do not count this as any sort of request or we risk double-counting
+      if (simpleSegment.requestId !== this.pendingSegment_.requestId) {
+        return;
+      }
+
+      // an error occurred from the active pendingSegment_ so reset everything
+      if (error) {
+        this.pendingSegment_ = null;
+        this.state = 'READY';
+
+        // the requests were aborted just record the aborted stat and exit
+        // this is not a true error condition and nothing corrective needs
+        // to be done
+        if (error.code === REQUEST_ERRORS.ABORTED) {
+          this.mediaRequestsAborted += 1;
+          return;
+        }
+
+        this.pause();
+
+        // the error is really just that at least one of the requests timed-out
+        // set the bandwidth to a very low value and trigger an ABR switch to
+        // take emergency action
+        if (error.code === REQUEST_ERRORS.TIMEOUT) {
+          this.mediaRequestsTimedout += 1;
+          this.bandwidth = 1;
+          this.roundTrip = NaN;
+          this.trigger('bandwidthupdate');
+          return;
+        }
+
+        // if control-flow has arrived here, then the error is real
+        // emit an error event to blacklist the current playlist
+        this.mediaRequestsErrored += 1;
+        this.error(error);
+        this.trigger('error');
+        return;
+      }
+
+      // the response was a success so set any bandwidth stats the request
+      // generated for ABR purposes
+      this.bandwidth = simpleSegment.stats.bandwidth;
+      this.roundTrip = simpleSegment.stats.roundTripTime;
+
+      // if this request included an initialization segment, save that data
+      // to the initSegment cache
+      if (simpleSegment.map) {
+        simpleSegment.map = this.initSegment(simpleSegment.map, true);
+      }
+
+      this.processSegmentResponse_(simpleSegment);
+    };
+
+    /**
+     * Move any important data from the simplified segment object
+     * back to the real segment object for future phases
+     *
+     * @private
+     */
+
+
+    SegmentLoader.prototype.processSegmentResponse_ = function processSegmentResponse_(simpleSegment) {
+      var segmentInfo = this.pendingSegment_;
+
+      segmentInfo.bytes = simpleSegment.bytes;
+      if (simpleSegment.map) {
+        segmentInfo.segment.map.bytes = simpleSegment.map.bytes;
+      }
+
+      segmentInfo.endOfAllRequests = simpleSegment.endOfAllRequests;
+      this.handleSegment_();
+    };
+
+    /**
+     * append a decrypted segement to the SourceBuffer through a SourceUpdater
+     *
+     * @private
+     */
+
+
+    SegmentLoader.prototype.handleSegment_ = function handleSegment_() {
+      var _this24 = this;
+
+      if (!this.pendingSegment_) {
+        this.state = 'READY';
+        return;
+      }
+
+      var segmentInfo = this.pendingSegment_;
+      var segment = segmentInfo.segment;
+      var timingInfo = this.syncController_.probeSegmentInfo(segmentInfo);
+
+      // When we have our first timing info, determine what media types this loader is
+      // dealing with. Although we're maintaining extra state, it helps to preserve the
+      // separation of segment loader from the actual source buffers.
+      if (typeof this.startingMedia_ === 'undefined' && timingInfo && (
+        // Guard against cases where we're not getting timing info at all until we are
+        // certain that all streams will provide it.
+        timingInfo.containsAudio || timingInfo.containsVideo)) {
+        this.startingMedia_ = {
+          containsAudio: timingInfo.containsAudio,
+          containsVideo: timingInfo.containsVideo
+        };
+      }
+
+      var illegalMediaSwitchError = illegalMediaSwitch(this.loaderType_, this.startingMedia_, timingInfo);
+
+      if (illegalMediaSwitchError) {
+        this.error({
+          message: illegalMediaSwitchError,
+          blacklistDuration: Infinity
+        });
+        this.trigger('error');
+        return;
+      }
+
+      if (segmentInfo.isSyncRequest) {
+        this.trigger('syncinfoupdate');
+        this.pendingSegment_ = null;
+        this.state = 'READY';
+        return;
+      }
+
+      if (segmentInfo.timestampOffset !== null && segmentInfo.timestampOffset !== this.sourceUpdater_.timestampOffset()) {
+        this.sourceUpdater_.timestampOffset(segmentInfo.timestampOffset);
+        // fired when a timestamp offset is set in HLS (can also identify discontinuities)
+        this.trigger('timestampoffset');
+      }
+
+      var timelineMapping = this.syncController_.mappingForTimeline(segmentInfo.timeline);
+
+      if (timelineMapping !== null) {
+        this.trigger({
+          type: 'segmenttimemapping',
+          mapping: timelineMapping
+        });
+      }
+
+      this.state = 'APPENDING';
+
+      // if the media initialization segment is changing, append it
+      // before the content segment
+      if (segment.map) {
+        var initId = initSegmentId(segment.map);
+
+        if (!this.activeInitSegmentId_ || this.activeInitSegmentId_ !== initId) {
+          var initSegment = this.initSegment(segment.map);
+
+          this.sourceUpdater_.appendBuffer(initSegment.bytes, function () {
+            _this24.activeInitSegmentId_ = initId;
+          });
+        }
+      }
+
+      segmentInfo.byteLength = segmentInfo.bytes.byteLength;
+      if (typeof segment.start === 'number' && typeof segment.end === 'number') {
+        this.mediaSecondsLoaded += segment.end - segment.start;
+      } else {
+        this.mediaSecondsLoaded += segment.duration;
+      }
+
+      this.logger_(segmentInfoString(segmentInfo));
+
+      this.sourceUpdater_.appendBuffer(segmentInfo.bytes, this.handleUpdateEnd_.bind(this));
+    };
+
+    /**
+     * callback to run when appendBuffer is finished. detects if we are
+     * in a good state to do things with the data we got, or if we need
+     * to wait for more
+     *
+     * @private
+     */
+
+
+    SegmentLoader.prototype.handleUpdateEnd_ = function handleUpdateEnd_() {
+      if (!this.pendingSegment_) {
+        this.state = 'READY';
+        if (!this.paused()) {
+          this.monitorBuffer_();
+        }
+        return;
+      }
+
+      var segmentInfo = this.pendingSegment_;
+      var segment = segmentInfo.segment;
+      var isWalkingForward = this.mediaIndex !== null;
+
+      this.pendingSegment_ = null;
+      this.recordThroughput_(segmentInfo);
+      this.addSegmentMetadataCue_(segmentInfo);
+
+      this.state = 'READY';
+
+      this.mediaIndex = segmentInfo.mediaIndex;
+      this.fetchAtBuffer_ = true;
+      this.currentTimeline_ = segmentInfo.timeline;
+
+      // We must update the syncinfo to recalculate the seekable range before
+      // the following conditional otherwise it may consider this a bad "guess"
+      // and attempt to resync when the post-update seekable window and live
+      // point would mean that this was the perfect segment to fetch
+      this.trigger('syncinfoupdate');
+
+      // If we previously appended a segment that ends more than 3 targetDurations before
+      // the currentTime_ that means that our conservative guess was too conservative.
+      // In that case, reset the loader state so that we try to use any information gained
+      // from the previous request to create a new, more accurate, sync-point.
+      if (segment.end && this.currentTime_() - segment.end > segmentInfo.playlist.targetDuration * 3) {
+        this.resetEverything();
+        return;
+      }
+
+      // Don't do a rendition switch unless we have enough time to get a sync segment
+      // and conservatively guess
+      if (isWalkingForward) {
+        this.trigger('bandwidthupdate');
+      }
+      this.trigger('progress');
+
+      // any time an update finishes and the last segment is in the
+      // buffer, end the stream. this ensures the "ended" event will
+      // fire if playback reaches that point.
+      var isEndOfStream = detectEndOfStream(segmentInfo.playlist, this.mediaSource_, segmentInfo.mediaIndex + 1);
+
+      if (isEndOfStream) {
+        this.endOfStream();
+      }
+
+      if (!this.paused()) {
+        this.monitorBuffer_();
+      }
+    };
+
+    /**
+     * Records the current throughput of the decrypt, transmux, and append
+     * portion of the semgment pipeline. `throughput.rate` is a the cumulative
+     * moving average of the throughput. `throughput.count` is the number of
+     * data points in the average.
+     *
+     * @private
+     * @param {Object} segmentInfo the object returned by loadSegment
+     */
+
+
+    SegmentLoader.prototype.recordThroughput_ = function recordThroughput_(segmentInfo) {
+      var rate = this.throughput.rate;
+      // Add one to the time to ensure that we don't accidentally attempt to divide
+      // by zero in the case where the throughput is ridiculously high
+      var segmentProcessingTime = Date.now() - segmentInfo.endOfAllRequests + 1;
+      // Multiply by 8000 to convert from bytes/millisecond to bits/second
+      var segmentProcessingThroughput = Math.floor(segmentInfo.byteLength / segmentProcessingTime * 8 * 1000);
+
+      // This is just a cumulative moving average calculation:
+      //   newAvg = oldAvg + (sample - oldAvg) / (sampleCount + 1)
+      this.throughput.rate += (segmentProcessingThroughput - rate) / ++this.throughput.count;
+    };
+
+    /**
+     * Adds a cue to the segment-metadata track with some metadata information about the
+     * segment
+     *
+     * @private
+     * @param {Object} segmentInfo
+     *        the object returned by loadSegment
+     * @method addSegmentMetadataCue_
+     */
+
+
+    SegmentLoader.prototype.addSegmentMetadataCue_ = function addSegmentMetadataCue_(segmentInfo) {
+      if (!this.segmentMetadataTrack_) {
+        return;
+      }
+
+      var segment = segmentInfo.segment;
+      var start = segment.start;
+      var end = segment.end;
+
+      // Do not try adding the cue if the start and end times are invalid.
+      if (!finite(start) || !finite(end)) {
+        return;
+      }
+
+      removeCuesFromTrack(start, end, this.segmentMetadataTrack_);
+
+      var Cue = window_1.WebKitDataCue || window_1.VTTCue;
+      var value = {
+        bandwidth: segmentInfo.playlist.attributes.BANDWIDTH,
+        resolution: segmentInfo.playlist.attributes.RESOLUTION,
+        codecs: segmentInfo.playlist.attributes.CODECS,
+        byteLength: segmentInfo.byteLength,
+        uri: segmentInfo.uri,
+        timeline: segmentInfo.timeline,
+        playlist: segmentInfo.playlist.uri,
+        start: start,
+        end: end
+      };
+      var data = JSON.stringify(value);
+      var cue = new Cue(start, end, data);
+
+      // Attach the metadata to the value property of the cue to keep consistency between
+      // the differences of WebKitDataCue in safari and VTTCue in other browsers
+      cue.value = value;
+
+      this.segmentMetadataTrack_.addCue(cue);
+    };
+
+    return SegmentLoader;
+  }(videojs$1.EventTarget);
+
+  /**
+   * @file vtt-segment-loader.js
+   */
+
+  var VTT_LINE_TERMINATORS = new Uint8Array('\n\n'.split('').map(function (char) {
+    return char.charCodeAt(0);
+  }));
+
+  var uintToString = function uintToString(uintArray) {
+    return String.fromCharCode.apply(null, uintArray);
+  };
+
+  /**
+   * An object that manages segment loading and appending.
+   *
+   * @class VTTSegmentLoader
+   * @param {Object} options required and optional options
+   * @extends videojs.EventTarget
+   */
+
+  var VTTSegmentLoader = function (_SegmentLoader) {
+    inherits(VTTSegmentLoader, _SegmentLoader);
+
+    function VTTSegmentLoader(settings) {
+      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+      classCallCheck(this, VTTSegmentLoader);
+
+      // SegmentLoader requires a MediaSource be specified or it will throw an error;
+      // however, VTTSegmentLoader has no need of a media source, so delete the reference
+      var _this25 = possibleConstructorReturn(this, _SegmentLoader.call(this, settings, options));
+
+      _this25.mediaSource_ = null;
+
+      _this25.subtitlesTrack_ = null;
+      return _this25;
+    }
+
+    /**
+     * Indicates which time ranges are buffered
+     *
+     * @return {TimeRange}
+     *         TimeRange object representing the current buffered ranges
+     */
+
+
+    VTTSegmentLoader.prototype.buffered_ = function buffered_() {
+      if (!this.subtitlesTrack_ || !this.subtitlesTrack_.cues.length) {
+        return videojs$1.createTimeRanges();
+      }
+
+      var cues = this.subtitlesTrack_.cues;
+      var start = cues[0].startTime;
+      var end = cues[cues.length - 1].startTime;
+
+      return videojs$1.createTimeRanges([[start, end]]);
+    };
+
+    /**
+     * Gets and sets init segment for the provided map
+     *
+     * @param {Object} map
+     *        The map object representing the init segment to get or set
+     * @param {Boolean=} set
+     *        If true, the init segment for the provided map should be saved
+     * @return {Object}
+     *         map object for desired init segment
+     */
+
+
+    VTTSegmentLoader.prototype.initSegment = function initSegment(map) {
+      var set$$1 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
+
+      if (!map) {
+        return null;
+      }
+
+      var id = initSegmentId(map);
+      var storedMap = this.initSegments_[id];
+
+      if (set$$1 && !storedMap && map.bytes) {
+        // append WebVTT line terminators to the media initialization segment if it exists
+        // to follow the WebVTT spec (https://w3c.github.io/webvtt/#file-structure) that
+        // requires two or more WebVTT line terminators between the WebVTT header and the
+        // rest of the file
+        var combinedByteLength = VTT_LINE_TERMINATORS.byteLength + map.bytes.byteLength;
+        var combinedSegment = new Uint8Array(combinedByteLength);
+
+        combinedSegment.set(map.bytes);
+        combinedSegment.set(VTT_LINE_TERMINATORS, map.bytes.byteLength);
+
+        this.initSegments_[id] = storedMap = {
+          resolvedUri: map.resolvedUri,
+          byterange: map.byterange,
+          bytes: combinedSegment
+        };
+      }
+
+      return storedMap || map;
+    };
+
+    /**
+     * Returns true if all configuration required for loading is present, otherwise false.
+     *
+     * @return {Boolean} True if the all configuration is ready for loading
+     * @private
+     */
+
+
+    VTTSegmentLoader.prototype.couldBeginLoading_ = function couldBeginLoading_() {
+      return this.playlist_ && this.subtitlesTrack_ && !this.paused();
+    };
+
+    /**
+     * Once all the starting parameters have been specified, begin
+     * operation. This method should only be invoked from the INIT
+     * state.
+     *
+     * @private
+     */
+
+
+    VTTSegmentLoader.prototype.init_ = function init_() {
+      this.state = 'READY';
+      this.resetEverything();
+      return this.monitorBuffer_();
+    };
+
+    /**
+     * Set a subtitle track on the segment loader to add subtitles to
+     *
+     * @param {TextTrack=} track
+     *        The text track to add loaded subtitles to
+     * @return {TextTrack}
+     *        Returns the subtitles track
+     */
+
+
+    VTTSegmentLoader.prototype.track = function track(_track) {
+      if (typeof _track === 'undefined') {
+        return this.subtitlesTrack_;
+      }
+
+      this.subtitlesTrack_ = _track;
+
+      // if we were unpaused but waiting for a sourceUpdater, start
+      // buffering now
+      if (this.state === 'INIT' && this.couldBeginLoading_()) {
+        this.init_();
+      }
+
+      return this.subtitlesTrack_;
+    };
+
+    /**
+     * Remove any data in the source buffer between start and end times
+     * @param {Number} start - the start time of the region to remove from the buffer
+     * @param {Number} end - the end time of the region to remove from the buffer
+     */
+
+
+    VTTSegmentLoader.prototype.remove = function remove(start, end) {
+      removeCuesFromTrack(start, end, this.subtitlesTrack_);
+    };
+
+    /**
+     * fill the buffer with segements unless the sourceBuffers are
+     * currently updating
+     *
+     * Note: this function should only ever be called by monitorBuffer_
+     * and never directly
+     *
+     * @private
+     */
+
+
+    VTTSegmentLoader.prototype.fillBuffer_ = function fillBuffer_() {
+      var _this26 = this;
+
+      if (!this.syncPoint_) {
+        this.syncPoint_ = this.syncController_.getSyncPoint(this.playlist_, this.duration_(), this.currentTimeline_, this.currentTime_());
+      }
+
+      // see if we need to begin loading immediately
+      var segmentInfo = this.checkBuffer_(this.buffered_(), this.playlist_, this.mediaIndex, this.hasPlayed_(), this.currentTime_(), this.syncPoint_);
+
+      segmentInfo = this.skipEmptySegments_(segmentInfo);
+
+      if (!segmentInfo) {
+        return;
+      }
+
+      if (this.syncController_.timestampOffsetForTimeline(segmentInfo.timeline) === null) {
+        // We don't have the timestamp offset that we need to sync subtitles.
+        // Rerun on a timestamp offset or user interaction.
+        var checkTimestampOffset = function checkTimestampOffset() {
+          _this26.state = 'READY';
+          if (!_this26.paused()) {
+            // if not paused, queue a buffer check as soon as possible
+            _this26.monitorBuffer_();
+          }
+        };
+
+        this.syncController_.one('timestampoffset', checkTimestampOffset);
+        this.state = 'WAITING_ON_TIMELINE';
+        return;
+      }
+
+      this.loadSegment_(segmentInfo);
+    };
+
+    /**
+     * Prevents the segment loader from requesting segments we know contain no subtitles
+     * by walking forward until we find the next segment that we don't know whether it is
+     * empty or not.
+     *
+     * @param {Object} segmentInfo
+     *        a segment info object that describes the current segment
+     * @return {Object}
+     *         a segment info object that describes the current segment
+     */
+
+
+    VTTSegmentLoader.prototype.skipEmptySegments_ = function skipEmptySegments_(segmentInfo) {
+      while (segmentInfo && segmentInfo.segment.empty) {
+        segmentInfo = this.generateSegmentInfo_(segmentInfo.playlist, segmentInfo.mediaIndex + 1, segmentInfo.startOfSegment + segmentInfo.duration, segmentInfo.isSyncRequest);
+      }
+      return segmentInfo;
+    };
+
+    /**
+     * append a decrypted segement to the SourceBuffer through a SourceUpdater
+     *
+     * @private
+     */
+
+
+    VTTSegmentLoader.prototype.handleSegment_ = function handleSegment_() {
+      var _this27 = this;
+
+      if (!this.pendingSegment_ || !this.subtitlesTrack_) {
+        this.state = 'READY';
+        return;
+      }
+
+      this.state = 'APPENDING';
+
+      var segmentInfo = this.pendingSegment_;
+      var segment = segmentInfo.segment;
+
+      // Make sure that vttjs has loaded, otherwise, wait till it finished loading
+      if (typeof window_1.WebVTT !== 'function' && this.subtitlesTrack_ && this.subtitlesTrack_.tech_) {
+
+        var loadHandler = function loadHandler() {
+          _this27.handleSegment_();
+        };
+
+        this.state = 'WAITING_ON_VTTJS';
+        this.subtitlesTrack_.tech_.one('vttjsloaded', loadHandler);
+        this.subtitlesTrack_.tech_.one('vttjserror', function () {
+          _this27.subtitlesTrack_.tech_.off('vttjsloaded', loadHandler);
+          _this27.error({
+            message: 'Error loading vtt.js'
+          });
+          _this27.state = 'READY';
+          _this27.pause();
+          _this27.trigger('error');
+        });
+
+        return;
+      }
+
+      segment.requested = true;
+
+      try {
+        this.parseVTTCues_(segmentInfo);
+      } catch (e) {
+        this.error({
+          message: e.message
+        });
+        this.state = 'READY';
+        this.pause();
+        return this.trigger('error');
+      }
+
+      this.updateTimeMapping_(segmentInfo, this.syncController_.timelines[segmentInfo.timeline], this.playlist_);
+
+      if (segmentInfo.isSyncRequest) {
+        this.trigger('syncinfoupdate');
+        this.pendingSegment_ = null;
+        this.state = 'READY';
+        return;
+      }
+
+      segmentInfo.byteLength = segmentInfo.bytes.byteLength;
+
+      this.mediaSecondsLoaded += segment.duration;
+
+      if (segmentInfo.cues.length) {
+        // remove any overlapping cues to prevent doubling
+        this.remove(segmentInfo.cues[0].endTime, segmentInfo.cues[segmentInfo.cues.length - 1].endTime);
+      }
+
+      segmentInfo.cues.forEach(function (cue) {
+        _this27.subtitlesTrack_.addCue(cue);
+      });
+
+      this.handleUpdateEnd_();
+    };
+
+    /**
+     * Uses the WebVTT parser to parse the segment response
+     *
+     * @param {Object} segmentInfo
+     *        a segment info object that describes the current segment
+     * @private
+     */
+
+
+    VTTSegmentLoader.prototype.parseVTTCues_ = function parseVTTCues_(segmentInfo) {
+      var decoder = void 0;
+      var decodeBytesToString = false;
+
+      if (typeof window_1.TextDecoder === 'function') {
+        decoder = new window_1.TextDecoder('utf8');
+      } else {
+        decoder = window_1.WebVTT.StringDecoder();
+        decodeBytesToString = true;
+      }
+
+      var parser = new window_1.WebVTT.Parser(window_1, window_1.vttjs, decoder);
+
+      segmentInfo.cues = [];
+      segmentInfo.timestampmap = {MPEGTS: 0, LOCAL: 0};
+
+      parser.oncue = segmentInfo.cues.push.bind(segmentInfo.cues);
+      parser.ontimestampmap = function (map) {
+        return segmentInfo.timestampmap = map;
+      };
+      parser.onparsingerror = function (error) {
+        videojs$1.log.warn('Error encountered when parsing cues: ' + error.message);
+      };
+
+      if (segmentInfo.segment.map) {
+        var mapData = segmentInfo.segment.map.bytes;
+
+        if (decodeBytesToString) {
+          mapData = uintToString(mapData);
+        }
+
+        parser.parse(mapData);
+      }
+
+      var segmentData = segmentInfo.bytes;
+
+      if (decodeBytesToString) {
+        segmentData = uintToString(segmentData);
+      }
+
+      parser.parse(segmentData);
+      parser.flush();
+    };
+
+    /**
+     * Updates the start and end times of any cues parsed by the WebVTT parser using
+     * the information parsed from the X-TIMESTAMP-MAP header and a TS to media time mapping
+     * from the SyncController
+     *
+     * @param {Object} segmentInfo
+     *        a segment info object that describes the current segment
+     * @param {Object} mappingObj
+     *        object containing a mapping from TS to media time
+     * @param {Object} playlist
+     *        the playlist object containing the segment
+     * @private
+     */
+
+
+    VTTSegmentLoader.prototype.updateTimeMapping_ = function updateTimeMapping_(segmentInfo, mappingObj, playlist) {
+      var segment = segmentInfo.segment;
+
+      if (!mappingObj) {
+        // If the sync controller does not have a mapping of TS to Media Time for the
+        // timeline, then we don't have enough information to update the cue
+        // start/end times
+        return;
+      }
+
+      if (!segmentInfo.cues.length) {
+        // If there are no cues, we also do not have enough information to figure out
+        // segment timing. Mark that the segment contains no cues so we don't re-request
+        // an empty segment.
+        segment.empty = true;
+        return;
+      }
+
+      var timestampmap = segmentInfo.timestampmap;
+      var diff = timestampmap.MPEGTS / 90000 - timestampmap.LOCAL + mappingObj.mapping;
+
+      segmentInfo.cues.forEach(function (cue) {
+        // First convert cue time to TS time using the timestamp-map provided within the vtt
+        cue.startTime += diff;
+        cue.endTime += diff;
+      });
+
+      if (!playlist.syncInfo) {
+        var firstStart = segmentInfo.cues[0].startTime;
+        var lastStart = segmentInfo.cues[segmentInfo.cues.length - 1].startTime;
+
+        playlist.syncInfo = {
+          mediaSequence: playlist.mediaSequence + segmentInfo.mediaIndex,
+          time: Math.min(firstStart, lastStart - segment.duration)
+        };
+      }
+    };
+
+    return VTTSegmentLoader;
+  }(SegmentLoader);
+
+  /**
+   * @file ad-cue-tags.js
+   */
+
+  /**
+   * Searches for an ad cue that overlaps with the given mediaTime
+   */
+
+
+  var findAdCue = function findAdCue(track, mediaTime) {
+    var cues = track.cues;
+
+    for (var i = 0; i < cues.length; i++) {
+      var cue = cues[i];
+
+      if (mediaTime >= cue.adStartTime && mediaTime <= cue.adEndTime) {
+        return cue;
+      }
+    }
+    return null;
+  };
+
+  var updateAdCues = function updateAdCues(media, track) {
+    var offset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
+
+    if (!media.segments) {
+      return;
+    }
+
+    var mediaTime = offset;
+    var cue = void 0;
+
+    for (var i = 0; i < media.segments.length; i++) {
+      var segment = media.segments[i];
+
+      if (!cue) {
+        // Since the cues will span for at least the segment duration, adding a fudge
+        // factor of half segment duration will prevent duplicate cues from being
+        // created when timing info is not exact (e.g. cue start time initialized
+        // at 10.006677, but next call mediaTime is 10.003332 )
+        cue = findAdCue(track, mediaTime + segment.duration / 2);
+      }
+
+      if (cue) {
+        if ('cueIn' in segment) {
+          // Found a CUE-IN so end the cue
+          cue.endTime = mediaTime;
+          cue.adEndTime = mediaTime;
+          mediaTime += segment.duration;
+          cue = null;
+          continue;
+        }
+
+        if (mediaTime < cue.endTime) {
+          // Already processed this mediaTime for this cue
+          mediaTime += segment.duration;
+          continue;
+        }
+
+        // otherwise extend cue until a CUE-IN is found
+        cue.endTime += segment.duration;
+      } else {
+        if ('cueOut' in segment) {
+          cue = new window_1.VTTCue(mediaTime, mediaTime + segment.duration, segment.cueOut);
+          cue.adStartTime = mediaTime;
+          // Assumes tag format to be
+          // #EXT-X-CUE-OUT:30
+          cue.adEndTime = mediaTime + parseFloat(segment.cueOut);
+          track.addCue(cue);
+        }
+
+        if ('cueOutCont' in segment) {
+          // Entered into the middle of an ad cue
+          var adOffset = void 0;
+          var adTotal = void 0;
+
+          // Assumes tag formate to be
+          // #EXT-X-CUE-OUT-CONT:10/30
+
+          var _segment$cueOutCont$s = segment.cueOutCont.split('/').map(parseFloat);
+
+          adOffset = _segment$cueOutCont$s[0];
+          adTotal = _segment$cueOutCont$s[1];
+
+
+          cue = new window_1.VTTCue(mediaTime, mediaTime + segment.duration, '');
+          cue.adStartTime = mediaTime - adOffset;
+          cue.adEndTime = cue.adStartTime + adTotal;
+          track.addCue(cue);
+        }
+      }
+      mediaTime += segment.duration;
+    }
+  };
+
+  /**
+   * @file sync-controller.js
+   */
+
+  var tsprobe = tsInspector.inspect;
+
+  var syncPointStrategies = [
+    // Stategy "VOD": Handle the VOD-case where the sync-point is *always*
+    //                the equivalence display-time 0 === segment-index 0
+    {
+      name: 'VOD',
+      run: function run(syncController, playlist, duration$$1, currentTimeline, currentTime) {
+        if (duration$$1 !== Infinity) {
+          var syncPoint = {
+            time: 0,
+            segmentIndex: 0
+          };
+
+          return syncPoint;
+        }
+        return null;
+      }
+    },
+    // Stategy "ProgramDateTime": We have a program-date-time tag in this playlist
+    {
+      name: 'ProgramDateTime',
+      run: function run(syncController, playlist, duration$$1, currentTimeline, currentTime) {
+        if (!syncController.datetimeToDisplayTime) {
+          return null;
+        }
+
+        var segments = playlist.segments || [];
+        var syncPoint = null;
+        var lastDistance = null;
+
+        currentTime = currentTime || 0;
+
+        for (var i = 0; i < segments.length; i++) {
+          var segment = segments[i];
+
+          if (segment.dateTimeObject) {
+            var segmentTime = segment.dateTimeObject.getTime() / 1000;
+            var segmentStart = segmentTime + syncController.datetimeToDisplayTime;
+            var distance = Math.abs(currentTime - segmentStart);
+
+            // Once the distance begins to increase, we have passed
+            // currentTime and can stop looking for better candidates
+            if (lastDistance !== null && lastDistance < distance) {
+              break;
+            }
+
+            lastDistance = distance;
+            syncPoint = {
+              time: segmentStart,
+              segmentIndex: i
+            };
+          }
+        }
+        return syncPoint;
+      }
+    },
+    // Stategy "Segment": We have a known time mapping for a timeline and a
+    //                    segment in the current timeline with timing data
+    {
+      name: 'Segment',
+      run: function run(syncController, playlist, duration$$1, currentTimeline, currentTime) {
+        var segments = playlist.segments || [];
+        var syncPoint = null;
+        var lastDistance = null;
+
+        currentTime = currentTime || 0;
+
+        for (var i = 0; i < segments.length; i++) {
+          var segment = segments[i];
+
+          if (segment.timeline === currentTimeline && typeof segment.start !== 'undefined') {
+            var distance = Math.abs(currentTime - segment.start);
+
+            // Once the distance begins to increase, we have passed
+            // currentTime and can stop looking for better candidates
+            if (lastDistance !== null && lastDistance < distance) {
+              break;
+            }
+
+            if (!syncPoint || lastDistance === null || lastDistance >= distance) {
+              lastDistance = distance;
+              syncPoint = {
+                time: segment.start,
+                segmentIndex: i
+              };
+            }
+          }
+        }
+        return syncPoint;
+      }
+    },
+    // Stategy "Discontinuity": We have a discontinuity with a known
+    //                          display-time
+    {
+      name: 'Discontinuity',
+      run: function run(syncController, playlist, duration$$1, currentTimeline, currentTime) {
+        var syncPoint = null;
+
+        currentTime = currentTime || 0;
+
+        if (playlist.discontinuityStarts && playlist.discontinuityStarts.length) {
+          var lastDistance = null;
+
+          for (var i = 0; i < playlist.discontinuityStarts.length; i++) {
+            var segmentIndex = playlist.discontinuityStarts[i];
+            var discontinuity = playlist.discontinuitySequence + i + 1;
+            var discontinuitySync = syncController.discontinuities[discontinuity];
+
+            if (discontinuitySync) {
+              var distance = Math.abs(currentTime - discontinuitySync.time);
+
+              // Once the distance begins to increase, we have passed
+              // currentTime and can stop looking for better candidates
+              if (lastDistance !== null && lastDistance < distance) {
+                break;
+              }
+
+              if (!syncPoint || lastDistance === null || lastDistance >= distance) {
+                lastDistance = distance;
+                syncPoint = {
+                  time: discontinuitySync.time,
+                  segmentIndex: segmentIndex
+                };
+              }
+            }
+          }
+        }
+        return syncPoint;
+      }
+    },
+    // Stategy "Playlist": We have a playlist with a known mapping of
+    //                     segment index to display time
+    {
+      name: 'Playlist',
+      run: function run(syncController, playlist, duration$$1, currentTimeline, currentTime) {
+        if (playlist.syncInfo) {
+          var syncPoint = {
+            time: playlist.syncInfo.time,
+            segmentIndex: playlist.syncInfo.mediaSequence - playlist.mediaSequence
+          };
+
+          return syncPoint;
+        }
+        return null;
+      }
+    }];
+
+  var SyncController = function (_videojs$EventTarget4) {
+    inherits(SyncController, _videojs$EventTarget4);
+
+    function SyncController() {
+      classCallCheck(this, SyncController);
+
+      // Segment Loader state variables...
+      // ...for synching across variants
+      var _this28 = possibleConstructorReturn(this, _videojs$EventTarget4.call(this));
+
+      _this28.inspectCache_ = undefined;
+
+      // ...for synching across variants
+      _this28.timelines = [];
+      _this28.discontinuities = [];
+      _this28.datetimeToDisplayTime = null;
+
+      _this28.logger_ = logger('SyncController');
+      return _this28;
+    }
+
+    /**
+     * Find a sync-point for the playlist specified
+     *
+     * A sync-point is defined as a known mapping from display-time to
+     * a segment-index in the current playlist.
+     *
+     * @param {Playlist} playlist
+     *        The playlist that needs a sync-point
+     * @param {Number} duration
+     *        Duration of the MediaSource (Infinite if playing a live source)
+     * @param {Number} currentTimeline
+     *        The last timeline from which a segment was loaded
+     * @returns {Object}
+     *          A sync-point object
+     */
+
+
+    SyncController.prototype.getSyncPoint = function getSyncPoint(playlist, duration$$1, currentTimeline, currentTime) {
+      var syncPoints = this.runStrategies_(playlist, duration$$1, currentTimeline, currentTime);
+
+      if (!syncPoints.length) {
+        // Signal that we need to attempt to get a sync-point manually
+        // by fetching a segment in the playlist and constructing
+        // a sync-point from that information
+        return null;
+      }
+
+      // Now find the sync-point that is closest to the currentTime because
+      // that should result in the most accurate guess about which segment
+      // to fetch
+      return this.selectSyncPoint_(syncPoints, {key: 'time', value: currentTime});
+    };
+
+    /**
+     * Calculate the amount of time that has expired off the playlist during playback
+     *
+     * @param {Playlist} playlist
+     *        Playlist object to calculate expired from
+     * @param {Number} duration
+     *        Duration of the MediaSource (Infinity if playling a live source)
+     * @returns {Number|null}
+     *          The amount of time that has expired off the playlist during playback. Null
+     *          if no sync-points for the playlist can be found.
+     */
+
+
+    SyncController.prototype.getExpiredTime = function getExpiredTime(playlist, duration$$1) {
+      if (!playlist || !playlist.segments) {
+        return null;
+      }
+
+      var syncPoints = this.runStrategies_(playlist, duration$$1, playlist.discontinuitySequence, 0);
+
+      // Without sync-points, there is not enough information to determine the expired time
+      if (!syncPoints.length) {
+        return null;
+      }
+
+      var syncPoint = this.selectSyncPoint_(syncPoints, {
+        key: 'segmentIndex',
+        value: 0
+      });
+
+      // If the sync-point is beyond the start of the playlist, we want to subtract the
+      // duration from index 0 to syncPoint.segmentIndex instead of adding.
+      if (syncPoint.segmentIndex > 0) {
+        syncPoint.time *= -1;
+      }
+
+      return Math.abs(syncPoint.time + sumDurations(playlist, syncPoint.segmentIndex, 0));
+    };
+
+    /**
+     * Runs each sync-point strategy and returns a list of sync-points returned by the
+     * strategies
+     *
+     * @private
+     * @param {Playlist} playlist
+     *        The playlist that needs a sync-point
+     * @param {Number} duration
+     *        Duration of the MediaSource (Infinity if playing a live source)
+     * @param {Number} currentTimeline
+     *        The last timeline from which a segment was loaded
+     * @returns {Array}
+     *          A list of sync-point objects
+     */
+
+
+    SyncController.prototype.runStrategies_ = function runStrategies_(playlist, duration$$1, currentTimeline, currentTime) {
+      var syncPoints = [];
+
+      // Try to find a sync-point in by utilizing various strategies...
+      for (var i = 0; i < syncPointStrategies.length; i++) {
+        var strategy = syncPointStrategies[i];
+        var syncPoint = strategy.run(this, playlist, duration$$1, currentTimeline, currentTime);
+
+        if (syncPoint) {
+          syncPoint.strategy = strategy.name;
+          syncPoints.push({
+            strategy: strategy.name,
+            syncPoint: syncPoint
+          });
+        }
+      }
+
+      return syncPoints;
+    };
+
+    /**
+     * Selects the sync-point nearest the specified target
+     *
+     * @private
+     * @param {Array} syncPoints
+     *        List of sync-points to select from
+     * @param {Object} target
+     *        Object specifying the property and value we are targeting
+     * @param {String} target.key
+     *        Specifies the property to target. Must be either 'time' or 'segmentIndex'
+     * @param {Number} target.value
+     *        The value to target for the specified key.
+     * @returns {Object}
+     *          The sync-point nearest the target
+     */
+
+
+    SyncController.prototype.selectSyncPoint_ = function selectSyncPoint_(syncPoints, target) {
+      var bestSyncPoint = syncPoints[0].syncPoint;
+      var bestDistance = Math.abs(syncPoints[0].syncPoint[target.key] - target.value);
+      var bestStrategy = syncPoints[0].strategy;
+
+      for (var i = 1; i < syncPoints.length; i++) {
+        var newDistance = Math.abs(syncPoints[i].syncPoint[target.key] - target.value);
+
+        if (newDistance < bestDistance) {
+          bestDistance = newDistance;
+          bestSyncPoint = syncPoints[i].syncPoint;
+          bestStrategy = syncPoints[i].strategy;
+        }
+      }
+
+      this.logger_('syncPoint for [' + target.key + ': ' + target.value + '] chosen with strategy' + (' [' + bestStrategy + ']: [time:' + bestSyncPoint.time + ',') + (' segmentIndex:' + bestSyncPoint.segmentIndex + ']'));
+
+      return bestSyncPoint;
+    };
+
+    /**
+     * Save any meta-data present on the segments when segments leave
+     * the live window to the playlist to allow for synchronization at the
+     * playlist level later.
+     *
+     * @param {Playlist} oldPlaylist - The previous active playlist
+     * @param {Playlist} newPlaylist - The updated and most current playlist
+     */
+
+
+    SyncController.prototype.saveExpiredSegmentInfo = function saveExpiredSegmentInfo(oldPlaylist, newPlaylist) {
+      var mediaSequenceDiff = newPlaylist.mediaSequence - oldPlaylist.mediaSequence;
+
+      // When a segment expires from the playlist and it has a start time
+      // save that information as a possible sync-point reference in future
+      for (var i = mediaSequenceDiff - 1; i >= 0; i--) {
+        var lastRemovedSegment = oldPlaylist.segments[i];
+
+        if (lastRemovedSegment && typeof lastRemovedSegment.start !== 'undefined') {
+          newPlaylist.syncInfo = {
+            mediaSequence: oldPlaylist.mediaSequence + i,
+            time: lastRemovedSegment.start
+          };
+          this.logger_('playlist refresh sync: [time:' + newPlaylist.syncInfo.time + ',' + (' mediaSequence: ' + newPlaylist.syncInfo.mediaSequence + ']'));
+          this.trigger('syncinfoupdate');
+          break;
+        }
+      }
+    };
+
+    /**
+     * Save the mapping from playlist's ProgramDateTime to display. This should
+     * only ever happen once at the start of playback.
+     *
+     * @param {Playlist} playlist - The currently active playlist
+     */
+
+
+    SyncController.prototype.setDateTimeMapping = function setDateTimeMapping(playlist) {
+      if (!this.datetimeToDisplayTime && playlist.segments && playlist.segments.length && playlist.segments[0].dateTimeObject) {
+        var playlistTimestamp = playlist.segments[0].dateTimeObject.getTime() / 1000;
+
+        this.datetimeToDisplayTime = -playlistTimestamp;
+      }
+    };
+
+    /**
+     * Reset the state of the inspection cache when we do a rendition
+     * switch
+     */
+
+
+    SyncController.prototype.reset = function reset() {
+      this.inspectCache_ = undefined;
+    };
+
+    /**
+     * Probe or inspect a fmp4 or an mpeg2-ts segment to determine the start
+     * and end of the segment in it's internal "media time". Used to generate
+     * mappings from that internal "media time" to the display time that is
+     * shown on the player.
+     *
+     * @param {SegmentInfo} segmentInfo - The current active request information
+     */
+
+
+    SyncController.prototype.probeSegmentInfo = function probeSegmentInfo(segmentInfo) {
+      var segment = segmentInfo.segment;
+      var playlist = segmentInfo.playlist;
+      var timingInfo = void 0;
+
+      if (segment.map) {
+        timingInfo = this.probeMp4Segment_(segmentInfo);
+      } else {
+        timingInfo = this.probeTsSegment_(segmentInfo);
+      }
+
+      if (timingInfo) {
+        if (this.calculateSegmentTimeMapping_(segmentInfo, timingInfo)) {
+          this.saveDiscontinuitySyncInfo_(segmentInfo);
+
+          // If the playlist does not have sync information yet, record that information
+          // now with segment timing information
+          if (!playlist.syncInfo) {
+            playlist.syncInfo = {
+              mediaSequence: playlist.mediaSequence + segmentInfo.mediaIndex,
+              time: segment.start
+            };
+          }
+        }
+      }
+
+      return timingInfo;
+    };
+
+    /**
+     * Probe an fmp4 or an mpeg2-ts segment to determine the start of the segment
+     * in it's internal "media time".
+     *
+     * @private
+     * @param {SegmentInfo} segmentInfo - The current active request information
+     * @return {object} The start and end time of the current segment in "media time"
+     */
+
+
+    SyncController.prototype.probeMp4Segment_ = function probeMp4Segment_(segmentInfo) {
+      var segment = segmentInfo.segment;
+      var timescales = probe.timescale(segment.map.bytes);
+      var startTime = probe.startTime(timescales, segmentInfo.bytes);
+
+      if (segmentInfo.timestampOffset !== null) {
+        segmentInfo.timestampOffset -= startTime;
+      }
+
+      return {
+        start: startTime,
+        end: startTime + segment.duration
+      };
+    };
+
+    /**
+     * Probe an mpeg2-ts segment to determine the start and end of the segment
+     * in it's internal "media time".
+     *
+     * @private
+     * @param {SegmentInfo} segmentInfo - The current active request information
+     * @return {object} The start and end time of the current segment in "media time"
+     */
+
+
+    SyncController.prototype.probeTsSegment_ = function probeTsSegment_(segmentInfo) {
+      var timeInfo = tsprobe(segmentInfo.bytes, this.inspectCache_);
+      var segmentStartTime = void 0;
+      var segmentEndTime = void 0;
+
+      if (!timeInfo) {
+        return null;
+      }
+
+      if (timeInfo.video && timeInfo.video.length === 2) {
+        this.inspectCache_ = timeInfo.video[1].dts;
+        segmentStartTime = timeInfo.video[0].dtsTime;
+        segmentEndTime = timeInfo.video[1].dtsTime;
+      } else if (timeInfo.audio && timeInfo.audio.length === 2) {
+        this.inspectCache_ = timeInfo.audio[1].dts;
+        segmentStartTime = timeInfo.audio[0].dtsTime;
+        segmentEndTime = timeInfo.audio[1].dtsTime;
+      }
+
+      return {
+        start: segmentStartTime,
+        end: segmentEndTime,
+        containsVideo: timeInfo.video && timeInfo.video.length === 2,
+        containsAudio: timeInfo.audio && timeInfo.audio.length === 2
+      };
+    };
+
+    SyncController.prototype.timestampOffsetForTimeline = function timestampOffsetForTimeline(timeline) {
+      if (typeof this.timelines[timeline] === 'undefined') {
+        return null;
+      }
+      return this.timelines[timeline].time;
+    };
+
+    SyncController.prototype.mappingForTimeline = function mappingForTimeline(timeline) {
+      if (typeof this.timelines[timeline] === 'undefined') {
+        return null;
+      }
+      return this.timelines[timeline].mapping;
+    };
+
+    /**
+     * Use the "media time" for a segment to generate a mapping to "display time" and
+     * save that display time to the segment.
+     *
+     * @private
+     * @param {SegmentInfo} segmentInfo
+     *        The current active request information
+     * @param {object} timingInfo
+     *        The start and end time of the current segment in "media time"
+     * @returns {Boolean}
+     *          Returns false if segment time mapping could not be calculated
+     */
+
+
+    SyncController.prototype.calculateSegmentTimeMapping_ = function calculateSegmentTimeMapping_(segmentInfo, timingInfo) {
+      var segment = segmentInfo.segment;
+      var mappingObj = this.timelines[segmentInfo.timeline];
+
+      if (segmentInfo.timestampOffset !== null) {
+        mappingObj = {
+          time: segmentInfo.startOfSegment,
+          mapping: segmentInfo.startOfSegment - timingInfo.start
+        };
+        this.timelines[segmentInfo.timeline] = mappingObj;
+        this.trigger('timestampoffset');
+
+        this.logger_('time mapping for timeline ' + segmentInfo.timeline + ': ' + ('[time: ' + mappingObj.time + '] [mapping: ' + mappingObj.mapping + ']'));
+
+        segment.start = segmentInfo.startOfSegment;
+        segment.end = timingInfo.end + mappingObj.mapping;
+      } else if (mappingObj) {
+        segment.start = timingInfo.start + mappingObj.mapping;
+        segment.end = timingInfo.end + mappingObj.mapping;
+      } else {
+        return false;
+      }
+
+      return true;
+    };
+
+    /**
+     * Each time we have discontinuity in the playlist, attempt to calculate the location
+     * in display of the start of the discontinuity and save that. We also save an accuracy
+     * value so that we save values with the most accuracy (closest to 0.)
+     *
+     * @private
+     * @param {SegmentInfo} segmentInfo - The current active request information
+     */
+
+
+    SyncController.prototype.saveDiscontinuitySyncInfo_ = function saveDiscontinuitySyncInfo_(segmentInfo) {
+      var playlist = segmentInfo.playlist;
+      var segment = segmentInfo.segment;
+
+      // If the current segment is a discontinuity then we know exactly where
+      // the start of the range and it's accuracy is 0 (greater accuracy values
+      // mean more approximation)
+      if (segment.discontinuity) {
+        this.discontinuities[segment.timeline] = {
+          time: segment.start,
+          accuracy: 0
+        };
+      } else if (playlist.discontinuityStarts && playlist.discontinuityStarts.length) {
+        // Search for future discontinuities that we can provide better timing
+        // information for and save that information for sync purposes
+        for (var i = 0; i < playlist.discontinuityStarts.length; i++) {
+          var segmentIndex = playlist.discontinuityStarts[i];
+          var discontinuity = playlist.discontinuitySequence + i + 1;
+          var mediaIndexDiff = segmentIndex - segmentInfo.mediaIndex;
+          var accuracy = Math.abs(mediaIndexDiff);
+
+          if (!this.discontinuities[discontinuity] || this.discontinuities[discontinuity].accuracy > accuracy) {
+            var time = void 0;
+
+            if (mediaIndexDiff < 0) {
+              time = segment.start - sumDurations(playlist, segmentInfo.mediaIndex, segmentIndex);
+            } else {
+              time = segment.end + sumDurations(playlist, segmentInfo.mediaIndex + 1, segmentIndex);
+            }
+
+            this.discontinuities[discontinuity] = {
+              time: time,
+              accuracy: accuracy
+            };
+          }
+        }
+      }
+    };
+
+    return SyncController;
+  }(videojs$1.EventTarget);
+
+  var Decrypter$1 = new shimWorker("./decrypter-worker.worker.js", function (window, document$$1) {
+    var self = this;
+    var decrypterWorker = function () {
+
+      var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
+
+      var win;
+
+      if (typeof window !== "undefined") {
+        win = window;
+      } else if (typeof commonjsGlobal !== "undefined") {
+        win = commonjsGlobal;
+      } else if (typeof self !== "undefined") {
+        win = self;
+      } else {
+        win = {};
+      }
+
+      var window_1$$1 = win;
+
+      /*
+       * pkcs7.pad
+       * https://github.com/brightcove/pkcs7
+       *
+       * Copyright (c) 2014 Brightcove
+       * Licensed under the apache2 license.
+       */
+
+      /**
+       * Returns the subarray of a Uint8Array without PKCS#7 padding.
+       * @param padded {Uint8Array} unencrypted bytes that have been padded
+       * @return {Uint8Array} the unpadded bytes
+       * @see http://tools.ietf.org/html/rfc5652
+       */
+      function unpad(padded) {
+        return padded.subarray(0, padded.byteLength - padded[padded.byteLength - 1]);
+      }
+
+      var classCallCheck$$1 = function classCallCheck$$1(instance, Constructor) {
+        if (!(instance instanceof Constructor)) {
+          throw new TypeError("Cannot call a class as a function");
+        }
+      };
+
+      var createClass$$1 = function () {
+        function defineProperties(target, props) {
+          for (var i = 0; i < props.length; i++) {
+            var descriptor = props[i];
+            descriptor.enumerable = descriptor.enumerable || false;
+            descriptor.configurable = true;
+            if ("value" in descriptor) descriptor.writable = true;
+            Object.defineProperty(target, descriptor.key, descriptor);
+          }
+        }
+
+        return function (Constructor, protoProps, staticProps) {
+          if (protoProps) defineProperties(Constructor.prototype, protoProps);
+          if (staticProps) defineProperties(Constructor, staticProps);
+          return Constructor;
+        };
+      }();
+
+      var inherits$$1 = function inherits$$1(subClass, superClass) {
+        if (typeof superClass !== "function" && superClass !== null) {
+          throw new TypeError("Super expression must either be null or a function, not " + (typeof superClass === 'undefined' ? 'undefined' : _typeof(superClass)));
+        }
+
+        subClass.prototype = Object.create(superClass && superClass.prototype, {
+          constructor: {
+            value: subClass,
+            enumerable: false,
+            writable: true,
+            configurable: true
+          }
+        });
+        if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
+      };
+
+      var possibleConstructorReturn$$1 = function possibleConstructorReturn$$1(self, call) {
+        if (!self) {
+          throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
+        }
+
+        return call && ((typeof call === 'undefined' ? 'undefined' : _typeof(call)) === "object" || typeof call === "function") ? call : self;
+      };
+
+      /**
+       * @file aes.js
+       *
+       * This file contains an adaptation of the AES decryption algorithm
+       * from the Standford Javascript Cryptography Library. That work is
+       * covered by the following copyright and permissions notice:
+       *
+       * Copyright 2009-2010 Emily Stark, Mike Hamburg, Dan Boneh.
+       * All rights reserved.
+       *
+       * Redistribution and use in source and binary forms, with or without
+       * modification, are permitted provided that the following conditions are
+       * met:
+       *
+       * 1. Redistributions of source code must retain the above copyright
+       *    notice, this list of conditions and the following disclaimer.
+       *
+       * 2. Redistributions in binary form must reproduce the above
+       *    copyright notice, this list of conditions and the following
+       *    disclaimer in the documentation and/or other materials provided
+       *    with the distribution.
+       *
+       * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+       * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+       * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+       * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR CONTRIBUTORS BE
+       * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+       * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+       * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+       * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+       * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+       * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+       * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+       *
+       * The views and conclusions contained in the software and documentation
+       * are those of the authors and should not be interpreted as representing
+       * official policies, either expressed or implied, of the authors.
+       */
+
+      /**
+       * Expand the S-box tables.
+       *
+       * @private
+       */
+      var precompute = function precompute() {
+        var tables = [[[], [], [], [], []], [[], [], [], [], []]];
+        var encTable = tables[0];
+        var decTable = tables[1];
+        var sbox = encTable[4];
+        var sboxInv = decTable[4];
+        var i = void 0;
+        var x = void 0;
+        var xInv = void 0;
+        var d = [];
+        var th = [];
+        var x2 = void 0;
+        var x4 = void 0;
+        var x8 = void 0;
+        var s = void 0;
+        var tEnc = void 0;
+        var tDec = void 0;
+
+        // Compute double and third tables
+        for (i = 0; i < 256; i++) {
+          th[(d[i] = i << 1 ^ (i >> 7) * 283) ^ i] = i;
+        }
+
+        for (x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) {
+          // Compute sbox
+          s = xInv ^ xInv << 1 ^ xInv << 2 ^ xInv << 3 ^ xInv << 4;
+          s = s >> 8 ^ s & 255 ^ 99;
+          sbox[x] = s;
+          sboxInv[s] = x;
+
+          // Compute MixColumns
+          x8 = d[x4 = d[x2 = d[x]]];
+          tDec = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100;
+          tEnc = d[s] * 0x101 ^ s * 0x1010100;
+
+          for (i = 0; i < 4; i++) {
+            encTable[i][x] = tEnc = tEnc << 24 ^ tEnc >>> 8;
+            decTable[i][s] = tDec = tDec << 24 ^ tDec >>> 8;
+          }
+        }
+
+        // Compactify. Considerable speedup on Firefox.
+        for (i = 0; i < 5; i++) {
+          encTable[i] = encTable[i].slice(0);
+          decTable[i] = decTable[i].slice(0);
+        }
+        return tables;
+      };
+      var aesTables = null;
+
+      /**
+       * Schedule out an AES key for both encryption and decryption. This
+       * is a low-level class. Use a cipher mode to do bulk encryption.
+       *
+       * @class AES
+       * @param key {Array} The key as an array of 4, 6 or 8 words.
+       */
+
+      var AES = function () {
+        function AES(key) {
+          classCallCheck$$1(this, AES);
+
+          /**
+           * The expanded S-box and inverse S-box tables. These will be computed
+           * on the client so that we don't have to send them down the wire.
+           *
+           * There are two tables, _tables[0] is for encryption and
+           * _tables[1] is for decryption.
+           *
+           * The first 4 sub-tables are the expanded S-box with MixColumns. The
+           * last (_tables[01][4]) is the S-box itself.
+           *
+           * @private
+           */
+          // if we have yet to precompute the S-box tables
+          // do so now
+          if (!aesTables) {
+            aesTables = precompute();
+          }
+          // then make a copy of that object for use
+          this._tables = [[aesTables[0][0].slice(), aesTables[0][1].slice(), aesTables[0][2].slice(), aesTables[0][3].slice(), aesTables[0][4].slice()], [aesTables[1][0].slice(), aesTables[1][1].slice(), aesTables[1][2].slice(), aesTables[1][3].slice(), aesTables[1][4].slice()]];
+          var i = void 0;
+          var j = void 0;
+          var tmp = void 0;
+          var encKey = void 0;
+          var decKey = void 0;
+          var sbox = this._tables[0][4];
+          var decTable = this._tables[1];
+          var keyLen = key.length;
+          var rcon = 1;
+
+          if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) {
+            throw new Error('Invalid aes key size');
+          }
+
+          encKey = key.slice(0);
+          decKey = [];
+          this._key = [encKey, decKey];
+
+          // schedule encryption keys
+          for (i = keyLen; i < 4 * keyLen + 28; i++) {
+            tmp = encKey[i - 1];
+
+            // apply sbox
+            if (i % keyLen === 0 || keyLen === 8 && i % keyLen === 4) {
+              tmp = sbox[tmp >>> 24] << 24 ^ sbox[tmp >> 16 & 255] << 16 ^ sbox[tmp >> 8 & 255] << 8 ^ sbox[tmp & 255];
+
+              // shift rows and add rcon
+              if (i % keyLen === 0) {
+                tmp = tmp << 8 ^ tmp >>> 24 ^ rcon << 24;
+                rcon = rcon << 1 ^ (rcon >> 7) * 283;
+              }
+            }
+
+            encKey[i] = encKey[i - keyLen] ^ tmp;
+          }
+
+          // schedule decryption keys
+          for (j = 0; i; j++, i--) {
+            tmp = encKey[j & 3 ? i : i - 4];
+            if (i <= 4 || j < 4) {
+              decKey[j] = tmp;
+            } else {
+              decKey[j] = decTable[0][sbox[tmp >>> 24]] ^ decTable[1][sbox[tmp >> 16 & 255]] ^ decTable[2][sbox[tmp >> 8 & 255]] ^ decTable[3][sbox[tmp & 255]];
+            }
+          }
+        }
+
+        /**
+         * Decrypt 16 bytes, specified as four 32-bit words.
+         *
+         * @param {Number} encrypted0 the first word to decrypt
+         * @param {Number} encrypted1 the second word to decrypt
+         * @param {Number} encrypted2 the third word to decrypt
+         * @param {Number} encrypted3 the fourth word to decrypt
+         * @param {Int32Array} out the array to write the decrypted words
+         * into
+         * @param {Number} offset the offset into the output array to start
+         * writing results
+         * @return {Array} The plaintext.
+         */
+
+        AES.prototype.decrypt = function decrypt$$1(encrypted0, encrypted1, encrypted2, encrypted3, out, offset) {
+          var key = this._key[1];
+          // state variables a,b,c,d are loaded with pre-whitened data
+          var a = encrypted0 ^ key[0];
+          var b = encrypted3 ^ key[1];
+          var c = encrypted2 ^ key[2];
+          var d = encrypted1 ^ key[3];
+          var a2 = void 0;
+          var b2 = void 0;
+          var c2 = void 0;
+
+          // key.length === 2 ?
+          var nInnerRounds = key.length / 4 - 2;
+          var i = void 0;
+          var kIndex = 4;
+          var table = this._tables[1];
+
+          // load up the tables
+          var table0 = table[0];
+          var table1 = table[1];
+          var table2 = table[2];
+          var table3 = table[3];
+          var sbox = table[4];
+
+          // Inner rounds. Cribbed from OpenSSL.
+          for (i = 0; i < nInnerRounds; i++) {
+            a2 = table0[a >>> 24] ^ table1[b >> 16 & 255] ^ table2[c >> 8 & 255] ^ table3[d & 255] ^ key[kIndex];
+            b2 = table0[b >>> 24] ^ table1[c >> 16 & 255] ^ table2[d >> 8 & 255] ^ table3[a & 255] ^ key[kIndex + 1];
+            c2 = table0[c >>> 24] ^ table1[d >> 16 & 255] ^ table2[a >> 8 & 255] ^ table3[b & 255] ^ key[kIndex + 2];
+            d = table0[d >>> 24] ^ table1[a >> 16 & 255] ^ table2[b >> 8 & 255] ^ table3[c & 255] ^ key[kIndex + 3];
+            kIndex += 4;
+            a = a2;
+            b = b2;
+            c = c2;
+          }
+
+          // Last round.
+          for (i = 0; i < 4; i++) {
+            out[(3 & -i) + offset] = sbox[a >>> 24] << 24 ^ sbox[b >> 16 & 255] << 16 ^ sbox[c >> 8 & 255] << 8 ^ sbox[d & 255] ^ key[kIndex++];
+            a2 = a;
+            a = b;
+            b = c;
+            c = d;
+            d = a2;
+          }
+        };
+
+        return AES;
+      }();
+
+      /**
+       * @file stream.js
+       */
+      /**
+       * A lightweight readable stream implemention that handles event dispatching.
+       *
+       * @class Stream
+       */
+      var Stream = function () {
+        function Stream() {
+          classCallCheck$$1(this, Stream);
+
+          this.listeners = {};
+        }
+
+        /**
+         * Add a listener for a specified event type.
+         *
+         * @param {String} type the event name
+         * @param {Function} listener the callback to be invoked when an event of
+         * the specified type occurs
+         */
+
+        Stream.prototype.on = function on(type, listener) {
+          if (!this.listeners[type]) {
+            this.listeners[type] = [];
+          }
+          this.listeners[type].push(listener);
+        };
+
+        /**
+         * Remove a listener for a specified event type.
+         *
+         * @param {String} type the event name
+         * @param {Function} listener  a function previously registered for this
+         * type of event through `on`
+         * @return {Boolean} if we could turn it off or not
+         */
+
+        Stream.prototype.off = function off(type, listener) {
+          if (!this.listeners[type]) {
+            return false;
+          }
+
+          var index = this.listeners[type].indexOf(listener);
+
+          this.listeners[type].splice(index, 1);
+          return index > -1;
+        };
+
+        /**
+         * Trigger an event of the specified type on this stream. Any additional
+         * arguments to this function are passed as parameters to event listeners.
+         *
+         * @param {String} type the event name
+         */
+
+        Stream.prototype.trigger = function trigger(type) {
+          var callbacks = this.listeners[type];
+
+          if (!callbacks) {
+            return;
+          }
+
+          // Slicing the arguments on every invocation of this method
+          // can add a significant amount of overhead. Avoid the
+          // intermediate object creation for the common case of a
+          // single callback argument
+          if (arguments.length === 2) {
+            var length = callbacks.length;
+
+            for (var i = 0; i < length; ++i) {
+              callbacks[i].call(this, arguments[1]);
+            }
+          } else {
+            var args = Array.prototype.slice.call(arguments, 1);
+            var _length = callbacks.length;
+
+            for (var _i = 0; _i < _length; ++_i) {
+              callbacks[_i].apply(this, args);
+            }
+          }
+        };
+
+        /**
+         * Destroys the stream and cleans up.
+         */
+
+        Stream.prototype.dispose = function dispose() {
+          this.listeners = {};
+        };
+        /**
+         * Forwards all `data` events on this stream to the destination stream. The
+         * destination stream should provide a method `push` to receive the data
+         * events as they arrive.
+         *
+         * @param {Stream} destination the stream that will receive all `data` events
+         * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
+         */
+
+        Stream.prototype.pipe = function pipe(destination) {
+          this.on('data', function (data) {
+            destination.push(data);
+          });
+        };
+
+        return Stream;
+      }();
+
+      /**
+       * @file async-stream.js
+       */
+      /**
+       * A wrapper around the Stream class to use setTiemout
+       * and run stream "jobs" Asynchronously
+       *
+       * @class AsyncStream
+       * @extends Stream
+       */
+
+      var AsyncStream$$1 = function (_Stream) {
+        inherits$$1(AsyncStream$$1, _Stream);
+
+        function AsyncStream$$1() {
+          classCallCheck$$1(this, AsyncStream$$1);
+
+          var _this = possibleConstructorReturn$$1(this, _Stream.call(this, Stream));
+
+          _this.jobs = [];
+          _this.delay = 1;
+          _this.timeout_ = null;
+          return _this;
+        }
+
+        /**
+         * process an async job
+         *
+         * @private
+         */
+
+        AsyncStream$$1.prototype.processJob_ = function processJob_() {
+          this.jobs.shift()();
+          if (this.jobs.length) {
+            this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay);
+          } else {
+            this.timeout_ = null;
+          }
+        };
+
+        /**
+         * push a job into the stream
+         *
+         * @param {Function} job the job to push into the stream
+         */
+
+        AsyncStream$$1.prototype.push = function push(job) {
+          this.jobs.push(job);
+          if (!this.timeout_) {
+            this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay);
+          }
+        };
+
+        return AsyncStream$$1;
+      }(Stream);
+
+      /**
+       * @file decrypter.js
+       *
+       * An asynchronous implementation of AES-128 CBC decryption with
+       * PKCS#7 padding.
+       */
+
+      /**
+       * Convert network-order (big-endian) bytes into their little-endian
+       * representation.
+       */
+      var ntoh = function ntoh(word) {
+        return word << 24 | (word & 0xff00) << 8 | (word & 0xff0000) >> 8 | word >>> 24;
+      };
+
+      /**
+       * Decrypt bytes using AES-128 with CBC and PKCS#7 padding.
+       *
+       * @param {Uint8Array} encrypted the encrypted bytes
+       * @param {Uint32Array} key the bytes of the decryption key
+       * @param {Uint32Array} initVector the initialization vector (IV) to
+       * use for the first round of CBC.
+       * @return {Uint8Array} the decrypted bytes
+       *
+       * @see http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
+       * @see http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_.28CBC.29
+       * @see https://tools.ietf.org/html/rfc2315
+       */
+      var decrypt$$1 = function decrypt$$1(encrypted, key, initVector) {
+        // word-level access to the encrypted bytes
+        var encrypted32 = new Int32Array(encrypted.buffer, encrypted.byteOffset, encrypted.byteLength >> 2);
+
+        var decipher = new AES(Array.prototype.slice.call(key));
+
+        // byte and word-level access for the decrypted output
+        var decrypted = new Uint8Array(encrypted.byteLength);
+        var decrypted32 = new Int32Array(decrypted.buffer);
+
+        // temporary variables for working with the IV, encrypted, and
+        // decrypted data
+        var init0 = void 0;
+        var init1 = void 0;
+        var init2 = void 0;
+        var init3 = void 0;
+        var encrypted0 = void 0;
+        var encrypted1 = void 0;
+        var encrypted2 = void 0;
+        var encrypted3 = void 0;
+
+        // iteration variable
+        var wordIx = void 0;
+
+        // pull out the words of the IV to ensure we don't modify the
+        // passed-in reference and easier access
+        init0 = initVector[0];
+        init1 = initVector[1];
+        init2 = initVector[2];
+        init3 = initVector[3];
+
+        // decrypt four word sequences, applying cipher-block chaining (CBC)
+        // to each decrypted block
+        for (wordIx = 0; wordIx < encrypted32.length; wordIx += 4) {
+          // convert big-endian (network order) words into little-endian
+          // (javascript order)
+          encrypted0 = ntoh(encrypted32[wordIx]);
+          encrypted1 = ntoh(encrypted32[wordIx + 1]);
+          encrypted2 = ntoh(encrypted32[wordIx + 2]);
+          encrypted3 = ntoh(encrypted32[wordIx + 3]);
+
+          // decrypt the block
+          decipher.decrypt(encrypted0, encrypted1, encrypted2, encrypted3, decrypted32, wordIx);
+
+          // XOR with the IV, and restore network byte-order to obtain the
+          // plaintext
+          decrypted32[wordIx] = ntoh(decrypted32[wordIx] ^ init0);
+          decrypted32[wordIx + 1] = ntoh(decrypted32[wordIx + 1] ^ init1);
+          decrypted32[wordIx + 2] = ntoh(decrypted32[wordIx + 2] ^ init2);
+          decrypted32[wordIx + 3] = ntoh(decrypted32[wordIx + 3] ^ init3);
+
+          // setup the IV for the next round
+          init0 = encrypted0;
+          init1 = encrypted1;
+          init2 = encrypted2;
+          init3 = encrypted3;
+        }
+
+        return decrypted;
+      };
+
+      /**
+       * The `Decrypter` class that manages decryption of AES
+       * data through `AsyncStream` objects and the `decrypt`
+       * function
+       *
+       * @param {Uint8Array} encrypted the encrypted bytes
+       * @param {Uint32Array} key the bytes of the decryption key
+       * @param {Uint32Array} initVector the initialization vector (IV) to
+       * @param {Function} done the function to run when done
+       * @class Decrypter
+       */
+
+      var Decrypter$$1 = function () {
+        function Decrypter$$1(encrypted, key, initVector, done) {
+          classCallCheck$$1(this, Decrypter$$1);
+
+          var step = Decrypter$$1.STEP;
+          var encrypted32 = new Int32Array(encrypted.buffer);
+          var decrypted = new Uint8Array(encrypted.byteLength);
+          var i = 0;
+
+          this.asyncStream_ = new AsyncStream$$1();
+
+          // split up the encryption job and do the individual chunks asynchronously
+          this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted));
+          for (i = step; i < encrypted32.length; i += step) {
+            initVector = new Uint32Array([ntoh(encrypted32[i - 4]), ntoh(encrypted32[i - 3]), ntoh(encrypted32[i - 2]), ntoh(encrypted32[i - 1])]);
+            this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted));
+          }
+          // invoke the done() callback when everything is finished
+          this.asyncStream_.push(function () {
+            // remove pkcs#7 padding from the decrypted bytes
+            done(null, unpad(decrypted));
+          });
+        }
+
+        /**
+         * a getter for step the maximum number of bytes to process at one time
+         *
+         * @return {Number} the value of step 32000
+         */
+
+        /**
+         * @private
+         */
+        Decrypter$$1.prototype.decryptChunk_ = function decryptChunk_(encrypted, key, initVector, decrypted) {
+          return function () {
+            var bytes = decrypt$$1(encrypted, key, initVector);
+
+            decrypted.set(bytes, encrypted.byteOffset);
+          };
+        };
+
+        createClass$$1(Decrypter$$1, null, [{
+          key: 'STEP',
+          get: function get$$1() {
+            // 4 * 8000;
+            return 32000;
+          }
+        }]);
+        return Decrypter$$1;
+      }();
+
+      /**
+       * @file bin-utils.js
+       */
+
+      /**
+       * Creates an object for sending to a web worker modifying properties that are TypedArrays
+       * into a new object with seperated properties for the buffer, byteOffset, and byteLength.
+       *
+       * @param {Object} message
+       *        Object of properties and values to send to the web worker
+       * @return {Object}
+       *         Modified message with TypedArray values expanded
+       * @function createTransferableMessage
+       */
+      var createTransferableMessage = function createTransferableMessage(message) {
+        var transferable = {};
+
+        Object.keys(message).forEach(function (key) {
+          var value = message[key];
+
+          if (ArrayBuffer.isView(value)) {
+            transferable[key] = {
+              bytes: value.buffer,
+              byteOffset: value.byteOffset,
+              byteLength: value.byteLength
+            };
+          } else {
+            transferable[key] = value;
+          }
+        });
+
+        return transferable;
+      };
+
+      /**
+       * Our web worker interface so that things can talk to aes-decrypter
+       * that will be running in a web worker. the scope is passed to this by
+       * webworkify.
+       *
+       * @param {Object} self
+       *        the scope for the web worker
+       */
+      var DecrypterWorker = function DecrypterWorker(self) {
+        self.onmessage = function (event) {
+          var data = event.data;
+          var encrypted = new Uint8Array(data.encrypted.bytes, data.encrypted.byteOffset, data.encrypted.byteLength);
+          var key = new Uint32Array(data.key.bytes, data.key.byteOffset, data.key.byteLength / 4);
+          var iv = new Uint32Array(data.iv.bytes, data.iv.byteOffset, data.iv.byteLength / 4);
+
+          /* eslint-disable no-new, handle-callback-err */
+          new Decrypter$$1(encrypted, key, iv, function (err, bytes) {
+            window_1$$1.postMessage(createTransferableMessage({
+              source: data.source,
+              decrypted: bytes
+            }), [bytes.buffer]);
+          });
+          /* eslint-enable */
+        };
+      };
+
+      var decrypterWorker = new DecrypterWorker(self);
+
+      return decrypterWorker;
+    }();
+  });
+
+  /**
+   * Convert the properties of an HLS track into an audioTrackKind.
+   *
+   * @private
+   */
+  var audioTrackKind_ = function audioTrackKind_(properties) {
+    var kind = properties.default ? 'main' : 'alternative';
+
+    if (properties.characteristics && properties.characteristics.indexOf('public.accessibility.describes-video') >= 0) {
+      kind = 'main-desc';
+    }
+
+    return kind;
+  };
+
+  /**
+   * Pause provided segment loader and playlist loader if active
+   *
+   * @param {SegmentLoader} segmentLoader
+   *        SegmentLoader to pause
+   * @param {Object} mediaType
+   *        Active media type
+   * @function stopLoaders
+   */
+  var stopLoaders = function stopLoaders(segmentLoader, mediaType) {
+    segmentLoader.abort();
+    segmentLoader.pause();
+
+    if (mediaType && mediaType.activePlaylistLoader) {
+      mediaType.activePlaylistLoader.pause();
+      mediaType.activePlaylistLoader = null;
+    }
+  };
+
+  /**
+   * Start loading provided segment loader and playlist loader
+   *
+   * @param {PlaylistLoader} playlistLoader
+   *        PlaylistLoader to start loading
+   * @param {Object} mediaType
+   *        Active media type
+   * @function startLoaders
+   */
+  var startLoaders = function startLoaders(playlistLoader, mediaType) {
+    // Segment loader will be started after `loadedmetadata` or `loadedplaylist` from the
+    // playlist loader
+    mediaType.activePlaylistLoader = playlistLoader;
+    playlistLoader.load();
+  };
+
+  /**
+   * Returns a function to be called when the media group changes. It performs a
+   * non-destructive (preserve the buffer) resync of the SegmentLoader. This is because a
+   * change of group is merely a rendition switch of the same content at another encoding,
+   * rather than a change of content, such as switching audio from English to Spanish.
+   *
+   * @param {String} type
+   *        MediaGroup type
+   * @param {Object} settings
+   *        Object containing required information for media groups
+   * @return {Function}
+   *         Handler for a non-destructive resync of SegmentLoader when the active media
+   *         group changes.
+   * @function onGroupChanged
+   */
+  var onGroupChanged = function onGroupChanged(type, settings) {
+    return function () {
+      var _settings$segmentLoad = settings.segmentLoaders,
+        segmentLoader = _settings$segmentLoad[type],
+        mainSegmentLoader = _settings$segmentLoad.main,
+        mediaType = settings.mediaTypes[type];
+
+      var activeTrack = mediaType.activeTrack();
+      var activeGroup = mediaType.activeGroup(activeTrack);
+      var previousActiveLoader = mediaType.activePlaylistLoader;
+
+      stopLoaders(segmentLoader, mediaType);
+
+      if (!activeGroup) {
+        // there is no group active
+        return;
+      }
+
+      if (!activeGroup.playlistLoader) {
+        if (previousActiveLoader) {
+          // The previous group had a playlist loader but the new active group does not
+          // this means we are switching from demuxed to muxed audio. In this case we want to
+          // do a destructive reset of the main segment loader and not restart the audio
+          // loaders.
+          mainSegmentLoader.resetEverything();
+        }
+        return;
+      }
+
+      // Non-destructive resync
+      segmentLoader.resyncLoader();
+
+      startLoaders(activeGroup.playlistLoader, mediaType);
+    };
+  };
+
+  /**
+   * Returns a function to be called when the media track changes. It performs a
+   * destructive reset of the SegmentLoader to ensure we start loading as close to
+   * currentTime as possible.
+   *
+   * @param {String} type
+   *        MediaGroup type
+   * @param {Object} settings
+   *        Object containing required information for media groups
+   * @return {Function}
+   *         Handler for a destructive reset of SegmentLoader when the active media
+   *         track changes.
+   * @function onTrackChanged
+   */
+  var onTrackChanged = function onTrackChanged(type, settings) {
+    return function () {
+      var _settings$segmentLoad2 = settings.segmentLoaders,
+        segmentLoader = _settings$segmentLoad2[type],
+        mainSegmentLoader = _settings$segmentLoad2.main,
+        mediaType = settings.mediaTypes[type];
+
+      var activeTrack = mediaType.activeTrack();
+      var activeGroup = mediaType.activeGroup(activeTrack);
+      var previousActiveLoader = mediaType.activePlaylistLoader;
+
+      stopLoaders(segmentLoader, mediaType);
+
+      if (!activeGroup) {
+        // there is no group active so we do not want to restart loaders
+        return;
+      }
+
+      if (!activeGroup.playlistLoader) {
+        // when switching from demuxed audio/video to muxed audio/video (noted by no playlist
+        // loader for the audio group), we want to do a destructive reset of the main segment
+        // loader and not restart the audio loaders
+        mainSegmentLoader.resetEverything();
+        return;
+      }
+
+      if (previousActiveLoader === activeGroup.playlistLoader) {
+        // Nothing has actually changed. This can happen because track change events can fire
+        // multiple times for a "single" change. One for enabling the new active track, and
+        // one for disabling the track that was active
+        startLoaders(activeGroup.playlistLoader, mediaType);
+        return;
+      }
+
+      if (segmentLoader.track) {
+        // For WebVTT, set the new text track in the segmentloader
+        segmentLoader.track(activeTrack);
+      }
+
+      // destructive reset
+      segmentLoader.resetEverything();
+
+      startLoaders(activeGroup.playlistLoader, mediaType);
+    };
+  };
+
+  var onError = {
+    /**
+     * Returns a function to be called when a SegmentLoader or PlaylistLoader encounters
+     * an error.
+     *
+     * @param {String} type
+     *        MediaGroup type
+     * @param {Object} settings
+     *        Object containing required information for media groups
+     * @return {Function}
+     *         Error handler. Logs warning (or error if the playlist is blacklisted) to
+     *         console and switches back to default audio track.
+     * @function onError.AUDIO
+     */
+    AUDIO: function AUDIO(type, settings) {
+      return function () {
+        var segmentLoader = settings.segmentLoaders[type],
+          mediaType = settings.mediaTypes[type],
+          blacklistCurrentPlaylist = settings.blacklistCurrentPlaylist;
+
+
+        stopLoaders(segmentLoader, mediaType);
+
+        // switch back to default audio track
+        var activeTrack = mediaType.activeTrack();
+        var activeGroup = mediaType.activeGroup();
+        var id = (activeGroup.filter(function (group) {
+          return group.default;
+        })[0] || activeGroup[0]).id;
+        var defaultTrack = mediaType.tracks[id];
+
+        if (activeTrack === defaultTrack) {
+          // Default track encountered an error. All we can do now is blacklist the current
+          // rendition and hope another will switch audio groups
+          blacklistCurrentPlaylist({
+            message: 'Problem encountered loading the default audio track.'
+          });
+          return;
+        }
+
+        videojs$1.log.warn('Problem encountered loading the alternate audio track.' + 'Switching back to default.');
+
+        for (var trackId in mediaType.tracks) {
+          mediaType.tracks[trackId].enabled = mediaType.tracks[trackId] === defaultTrack;
+        }
+
+        mediaType.onTrackChanged();
+      };
+    },
+    /**
+     * Returns a function to be called when a SegmentLoader or PlaylistLoader encounters
+     * an error.
+     *
+     * @param {String} type
+     *        MediaGroup type
+     * @param {Object} settings
+     *        Object containing required information for media groups
+     * @return {Function}
+     *         Error handler. Logs warning to console and disables the active subtitle track
+     * @function onError.SUBTITLES
+     */
+    SUBTITLES: function SUBTITLES(type, settings) {
+      return function () {
+        var segmentLoader = settings.segmentLoaders[type],
+          mediaType = settings.mediaTypes[type];
+
+
+        videojs$1.log.warn('Problem encountered loading the subtitle track.' + 'Disabling subtitle track.');
+
+        stopLoaders(segmentLoader, mediaType);
+
+        var track = mediaType.activeTrack();
+
+        if (track) {
+          track.mode = 'disabled';
+        }
+
+        mediaType.onTrackChanged();
+      };
+    }
+  };
+
+  var setupListeners = {
+    /**
+     * Setup event listeners for audio playlist loader
+     *
+     * @param {String} type
+     *        MediaGroup type
+     * @param {PlaylistLoader|null} playlistLoader
+     *        PlaylistLoader to register listeners on
+     * @param {Object} settings
+     *        Object containing required information for media groups
+     * @function setupListeners.AUDIO
+     */
+    AUDIO: function AUDIO(type, playlistLoader, settings) {
+      if (!playlistLoader) {
+        // no playlist loader means audio will be muxed with the video
+        return;
+      }
+
+      var tech = settings.tech,
+        requestOptions = settings.requestOptions,
+        segmentLoader = settings.segmentLoaders[type];
+
+
+      playlistLoader.on('loadedmetadata', function () {
+        var media = playlistLoader.media();
+
+        segmentLoader.playlist(media, requestOptions);
+
+        // if the video is already playing, or if this isn't a live video and preload
+        // permits, start downloading segments
+        if (!tech.paused() || media.endList && tech.preload() !== 'none') {
+          segmentLoader.load();
+        }
+      });
+
+      playlistLoader.on('loadedplaylist', function () {
+        segmentLoader.playlist(playlistLoader.media(), requestOptions);
+
+        // If the player isn't paused, ensure that the segment loader is running
+        if (!tech.paused()) {
+          segmentLoader.load();
+        }
+      });
+
+      playlistLoader.on('error', onError[type](type, settings));
+    },
+    /**
+     * Setup event listeners for subtitle playlist loader
+     *
+     * @param {String} type
+     *        MediaGroup type
+     * @param {PlaylistLoader|null} playlistLoader
+     *        PlaylistLoader to register listeners on
+     * @param {Object} settings
+     *        Object containing required information for media groups
+     * @function setupListeners.SUBTITLES
+     */
+    SUBTITLES: function SUBTITLES(type, playlistLoader, settings) {
+      var tech = settings.tech,
+        requestOptions = settings.requestOptions,
+        segmentLoader = settings.segmentLoaders[type],
+        mediaType = settings.mediaTypes[type];
+
+
+      playlistLoader.on('loadedmetadata', function () {
+        var media = playlistLoader.media();
+
+        segmentLoader.playlist(media, requestOptions);
+        segmentLoader.track(mediaType.activeTrack());
+
+        // if the video is already playing, or if this isn't a live video and preload
+        // permits, start downloading segments
+        if (!tech.paused() || media.endList && tech.preload() !== 'none') {
+          segmentLoader.load();
+        }
+      });
+
+      playlistLoader.on('loadedplaylist', function () {
+        segmentLoader.playlist(playlistLoader.media(), requestOptions);
+
+        // If the player isn't paused, ensure that the segment loader is running
+        if (!tech.paused()) {
+          segmentLoader.load();
+        }
+      });
+
+      playlistLoader.on('error', onError[type](type, settings));
+    }
+  };
+
+  var byGroupId = function byGroupId(type, groupId) {
+    return function (playlist) {
+      return playlist.attributes[type] === groupId;
+    };
+  };
+
+  var byResolvedUri = function byResolvedUri(resolvedUri) {
+    return function (playlist) {
+      return playlist.resolvedUri === resolvedUri;
+    };
+  };
+
+  var initialize = {
+    /**
+     * Setup PlaylistLoaders and AudioTracks for the audio groups
+     *
+     * @param {String} type
+     *        MediaGroup type
+     * @param {Object} settings
+     *        Object containing required information for media groups
+     * @function initialize.AUDIO
+     */
+    'AUDIO': function AUDIO(type, settings) {
+      var hls = settings.hls,
+        sourceType = settings.sourceType,
+        segmentLoader = settings.segmentLoaders[type],
+        withCredentials = settings.requestOptions.withCredentials,
+        _settings$master = settings.master,
+        mediaGroups = _settings$master.mediaGroups,
+        playlists = _settings$master.playlists,
+        _settings$mediaTypes$ = settings.mediaTypes[type],
+        groups = _settings$mediaTypes$.groups,
+        tracks = _settings$mediaTypes$.tracks,
+        masterPlaylistLoader = settings.masterPlaylistLoader;
+
+      // force a default if we have none
+
+      if (!mediaGroups[type] || Object.keys(mediaGroups[type]).length === 0) {
+        mediaGroups[type] = {main: {default: {default: true}}};
+      }
+
+      for (var groupId in mediaGroups[type]) {
+        if (!groups[groupId]) {
+          groups[groupId] = [];
+        }
+
+        // List of playlists that have an AUDIO attribute value matching the current
+        // group ID
+        var groupPlaylists = playlists.filter(byGroupId(type, groupId));
+
+        for (var variantLabel in mediaGroups[type][groupId]) {
+          var properties = mediaGroups[type][groupId][variantLabel];
+
+          // List of playlists for the current group ID that have a matching uri with
+          // this alternate audio variant
+          var matchingPlaylists = groupPlaylists.filter(byResolvedUri(properties.resolvedUri));
+
+          if (matchingPlaylists.length) {
+            // If there is a playlist that has the same uri as this audio variant, assume
+            // that the playlist is audio only. We delete the resolvedUri property here
+            // to prevent a playlist loader from being created so that we don't have
+            // both the main and audio segment loaders loading the same audio segments
+            // from the same playlist.
+            delete properties.resolvedUri;
+          }
+
+          var playlistLoader = void 0;
+
+          if (properties.resolvedUri) {
+            playlistLoader = new PlaylistLoader(properties.resolvedUri, hls, withCredentials);
+          } else if (properties.playlists && sourceType === 'dash') {
+            playlistLoader = new DashPlaylistLoader(properties.playlists[0], hls, withCredentials, masterPlaylistLoader);
+          } else {
+            // no resolvedUri means the audio is muxed with the video when using this
+            // audio track
+            playlistLoader = null;
+          }
+
+          properties = videojs$1.mergeOptions({id: variantLabel, playlistLoader: playlistLoader}, properties);
+
+          setupListeners[type](type, properties.playlistLoader, settings);
+
+          groups[groupId].push(properties);
+
+          if (typeof tracks[variantLabel] === 'undefined') {
+            var track = new videojs$1.AudioTrack({
+              id: variantLabel,
+              kind: audioTrackKind_(properties),
+              enabled: false,
+              language: properties.language,
+              default: properties.default,
+              label: variantLabel
+            });
+
+            tracks[variantLabel] = track;
+          }
+        }
+      }
+
+      // setup single error event handler for the segment loader
+      segmentLoader.on('error', onError[type](type, settings));
+    },
+    /**
+     * Setup PlaylistLoaders and TextTracks for the subtitle groups
+     *
+     * @param {String} type
+     *        MediaGroup type
+     * @param {Object} settings
+     *        Object containing required information for media groups
+     * @function initialize.SUBTITLES
+     */
+    'SUBTITLES': function SUBTITLES(type, settings) {
+      var tech = settings.tech,
+        hls = settings.hls,
+        sourceType = settings.sourceType,
+        segmentLoader = settings.segmentLoaders[type],
+        withCredentials = settings.requestOptions.withCredentials,
+        mediaGroups = settings.master.mediaGroups,
+        _settings$mediaTypes$2 = settings.mediaTypes[type],
+        groups = _settings$mediaTypes$2.groups,
+        tracks = _settings$mediaTypes$2.tracks,
+        masterPlaylistLoader = settings.masterPlaylistLoader;
+
+
+      for (var groupId in mediaGroups[type]) {
+        if (!groups[groupId]) {
+          groups[groupId] = [];
+        }
+
+        for (var variantLabel in mediaGroups[type][groupId]) {
+          if (mediaGroups[type][groupId][variantLabel].forced) {
+            // Subtitle playlists with the forced attribute are not selectable in Safari.
+            // According to Apple's HLS Authoring Specification:
+            //   If content has forced subtitles and regular subtitles in a given language,
+            //   the regular subtitles track in that language MUST contain both the forced
+            //   subtitles and the regular subtitles for that language.
+            // Because of this requirement and that Safari does not add forced subtitles,
+            // forced subtitles are skipped here to maintain consistent experience across
+            // all platforms
+            continue;
+          }
+
+          var properties = mediaGroups[type][groupId][variantLabel];
+
+          var playlistLoader = void 0;
+
+          if (sourceType === 'hls') {
+            playlistLoader = new PlaylistLoader(properties.resolvedUri, hls, withCredentials);
+          } else if (sourceType === 'dash') {
+            playlistLoader = new DashPlaylistLoader(properties.playlists[0], hls, withCredentials, masterPlaylistLoader);
+          }
+
+          properties = videojs$1.mergeOptions({
+            id: variantLabel,
+            playlistLoader: playlistLoader
+          }, properties);
+
+          setupListeners[type](type, properties.playlistLoader, settings);
+
+          groups[groupId].push(properties);
+
+          if (typeof tracks[variantLabel] === 'undefined') {
+            var track = tech.addRemoteTextTrack({
+              id: variantLabel,
+              kind: 'subtitles',
+              enabled: false,
+              language: properties.language,
+              label: variantLabel
+            }, false).track;
+
+            tracks[variantLabel] = track;
+          }
+        }
+      }
+
+      // setup single error event handler for the segment loader
+      segmentLoader.on('error', onError[type](type, settings));
+    },
+    /**
+     * Setup TextTracks for the closed-caption groups
+     *
+     * @param {String} type
+     *        MediaGroup type
+     * @param {Object} settings
+     *        Object containing required information for media groups
+     * @function initialize['CLOSED-CAPTIONS']
+     */
+    'CLOSED-CAPTIONS': function CLOSEDCAPTIONS(type, settings) {
+      var tech = settings.tech,
+        mediaGroups = settings.master.mediaGroups,
+        _settings$mediaTypes$3 = settings.mediaTypes[type],
+        groups = _settings$mediaTypes$3.groups,
+        tracks = _settings$mediaTypes$3.tracks;
+
+
+      for (var groupId in mediaGroups[type]) {
+        if (!groups[groupId]) {
+          groups[groupId] = [];
+        }
+
+        for (var variantLabel in mediaGroups[type][groupId]) {
+          var properties = mediaGroups[type][groupId][variantLabel];
+
+          // We only support CEA608 captions for now, so ignore anything that
+          // doesn't use a CCx INSTREAM-ID
+          if (!properties.instreamId.match(/CC\d/)) {
+            continue;
+          }
+
+          // No PlaylistLoader is required for Closed-Captions because the captions are
+          // embedded within the video stream
+          groups[groupId].push(videojs$1.mergeOptions({id: variantLabel}, properties));
+
+          if (typeof tracks[variantLabel] === 'undefined') {
+            var track = tech.addRemoteTextTrack({
+              id: properties.instreamId,
+              kind: 'captions',
+              enabled: false,
+              language: properties.language,
+              label: variantLabel
+            }, false).track;
+
+            tracks[variantLabel] = track;
+          }
+        }
+      }
+    }
+  };
+
+  /**
+   * Returns a function used to get the active group of the provided type
+   *
+   * @param {String} type
+   *        MediaGroup type
+   * @param {Object} settings
+   *        Object containing required information for media groups
+   * @return {Function}
+   *         Function that returns the active media group for the provided type. Takes an
+   *         optional parameter {TextTrack} track. If no track is provided, a list of all
+   *         variants in the group, otherwise the variant corresponding to the provided
+   *         track is returned.
+   * @function activeGroup
+   */
+  var activeGroup = function activeGroup(type, settings) {
+    return function (track) {
+      var masterPlaylistLoader = settings.masterPlaylistLoader,
+        groups = settings.mediaTypes[type].groups;
+
+
+      var media = masterPlaylistLoader.media();
+
+      if (!media) {
+        return null;
+      }
+
+      var variants = null;
+
+      if (media.attributes[type]) {
+        variants = groups[media.attributes[type]];
+      }
+
+      variants = variants || groups.main;
+
+      if (typeof track === 'undefined') {
+        return variants;
+      }
+
+      if (track === null) {
+        // An active track was specified so a corresponding group is expected. track === null
+        // means no track is currently active so there is no corresponding group
+        return null;
+      }
+
+      return variants.filter(function (props) {
+        return props.id === track.id;
+      })[0] || null;
+    };
+  };
+
+  var activeTrack = {
+    /**
+     * Returns a function used to get the active track of type provided
+     *
+     * @param {String} type
+     *        MediaGroup type
+     * @param {Object} settings
+     *        Object containing required information for media groups
+     * @return {Function}
+     *         Function that returns the active media track for the provided type. Returns
+     *         null if no track is active
+     * @function activeTrack.AUDIO
+     */
+    AUDIO: function AUDIO(type, settings) {
+      return function () {
+        var tracks = settings.mediaTypes[type].tracks;
+
+
+        for (var id in tracks) {
+          if (tracks[id].enabled) {
+            return tracks[id];
+          }
+        }
+
+        return null;
+      };
+    },
+    /**
+     * Returns a function used to get the active track of type provided
+     *
+     * @param {String} type
+     *        MediaGroup type
+     * @param {Object} settings
+     *        Object containing required information for media groups
+     * @return {Function}
+     *         Function that returns the active media track for the provided type. Returns
+     *         null if no track is active
+     * @function activeTrack.SUBTITLES
+     */
+    SUBTITLES: function SUBTITLES(type, settings) {
+      return function () {
+        var tracks = settings.mediaTypes[type].tracks;
+
+
+        for (var id in tracks) {
+          if (tracks[id].mode === 'showing') {
+            return tracks[id];
+          }
+        }
+
+        return null;
+      };
+    }
+  };
+
+  /**
+   * Setup PlaylistLoaders and Tracks for media groups (Audio, Subtitles,
+   * Closed-Captions) specified in the master manifest.
+   *
+   * @param {Object} settings
+   *        Object containing required information for setting up the media groups
+   * @param {SegmentLoader} settings.segmentLoaders.AUDIO
+   *        Audio segment loader
+   * @param {SegmentLoader} settings.segmentLoaders.SUBTITLES
+   *        Subtitle segment loader
+   * @param {SegmentLoader} settings.segmentLoaders.main
+   *        Main segment loader
+   * @param {Tech} settings.tech
+   *        The tech of the player
+   * @param {Object} settings.requestOptions
+   *        XHR request options used by the segment loaders
+   * @param {PlaylistLoader} settings.masterPlaylistLoader
+   *        PlaylistLoader for the master source
+   * @param {HlsHandler} settings.hls
+   *        HLS SourceHandler
+   * @param {Object} settings.master
+   *        The parsed master manifest
+   * @param {Object} settings.mediaTypes
+   *        Object to store the loaders, tracks, and utility methods for each media type
+   * @param {Function} settings.blacklistCurrentPlaylist
+   *        Blacklists the current rendition and forces a rendition switch.
+   * @function setupMediaGroups
+   */
+  var setupMediaGroups = function setupMediaGroups(settings) {
+    ['AUDIO', 'SUBTITLES', 'CLOSED-CAPTIONS'].forEach(function (type) {
+      initialize[type](type, settings);
+    });
+
+    var mediaTypes = settings.mediaTypes,
+      masterPlaylistLoader = settings.masterPlaylistLoader,
+      tech = settings.tech,
+      hls = settings.hls;
+
+    // setup active group and track getters and change event handlers
+
+    ['AUDIO', 'SUBTITLES'].forEach(function (type) {
+      mediaTypes[type].activeGroup = activeGroup(type, settings);
+      mediaTypes[type].activeTrack = activeTrack[type](type, settings);
+      mediaTypes[type].onGroupChanged = onGroupChanged(type, settings);
+      mediaTypes[type].onTrackChanged = onTrackChanged(type, settings);
+    });
+
+    // DO NOT enable the default subtitle or caption track.
+    // DO enable the default audio track
+    var audioGroup = mediaTypes.AUDIO.activeGroup();
+    var groupId = (audioGroup.filter(function (group) {
+      return group.default;
+    })[0] || audioGroup[0]).id;
+
+    mediaTypes.AUDIO.tracks[groupId].enabled = true;
+    mediaTypes.AUDIO.onTrackChanged();
+
+    masterPlaylistLoader.on('mediachange', function () {
+      ['AUDIO', 'SUBTITLES'].forEach(function (type) {
+        return mediaTypes[type].onGroupChanged();
+      });
+    });
+
+    // custom audio track change event handler for usage event
+    var onAudioTrackChanged = function onAudioTrackChanged() {
+      mediaTypes.AUDIO.onTrackChanged();
+      tech.trigger({type: 'usage', name: 'hls-audio-change'});
+    };
+
+    tech.audioTracks().addEventListener('change', onAudioTrackChanged);
+    tech.remoteTextTracks().addEventListener('change', mediaTypes.SUBTITLES.onTrackChanged);
+
+    hls.on('dispose', function () {
+      tech.audioTracks().removeEventListener('change', onAudioTrackChanged);
+      tech.remoteTextTracks().removeEventListener('change', mediaTypes.SUBTITLES.onTrackChanged);
+    });
+
+    // clear existing audio tracks and add the ones we just created
+    tech.clearTracks('audio');
+
+    for (var id in mediaTypes.AUDIO.tracks) {
+      tech.audioTracks().addTrack(mediaTypes.AUDIO.tracks[id]);
+    }
+  };
+
+  /**
+   * Creates skeleton object used to store the loaders, tracks, and utility methods for each
+   * media type
+   *
+   * @return {Object}
+   *         Object to store the loaders, tracks, and utility methods for each media type
+   * @function createMediaTypes
+   */
+  var createMediaTypes = function createMediaTypes() {
+    var mediaTypes = {};
+
+    ['AUDIO', 'SUBTITLES', 'CLOSED-CAPTIONS'].forEach(function (type) {
+      mediaTypes[type] = {
+        groups: {},
+        tracks: {},
+        activePlaylistLoader: null,
+        activeGroup: noop$1,
+        activeTrack: noop$1,
+        onGroupChanged: noop$1,
+        onTrackChanged: noop$1
+      };
+    });
+
+    return mediaTypes;
+  };
+
+  /**
+   * @file master-playlist-controller.js
+   */
+
+  var ABORT_EARLY_BLACKLIST_SECONDS = 60 * 2;
+
+  var Hls = void 0;
+
+  // SegmentLoader stats that need to have each loader's
+  // values summed to calculate the final value
+  var loaderStats = ['mediaRequests', 'mediaRequestsAborted', 'mediaRequestsTimedout', 'mediaRequestsErrored', 'mediaTransferDuration', 'mediaBytesTransferred'];
+  var sumLoaderStat = function sumLoaderStat(stat) {
+    return this.audioSegmentLoader_[stat] + this.mainSegmentLoader_[stat];
+  };
+
+  /**
+   * the master playlist controller controller all interactons
+   * between playlists and segmentloaders. At this time this mainly
+   * involves a master playlist and a series of audio playlists
+   * if they are available
+   *
+   * @class MasterPlaylistController
+   * @extends videojs.EventTarget
+   */
+
+  var MasterPlaylistController = function (_videojs$EventTarget5) {
+    inherits(MasterPlaylistController, _videojs$EventTarget5);
+
+    function MasterPlaylistController(options) {
+      classCallCheck(this, MasterPlaylistController);
+
+      var _this29 = possibleConstructorReturn(this, _videojs$EventTarget5.call(this));
+
+      var url = options.url,
+        withCredentials = options.withCredentials,
+        tech = options.tech,
+        bandwidth = options.bandwidth,
+        externHls = options.externHls,
+        useCueTags = options.useCueTags,
+        blacklistDuration = options.blacklistDuration,
+        enableLowInitialPlaylist = options.enableLowInitialPlaylist,
+        sourceType = options.sourceType;
+
+
+      if (!url) {
+        throw new Error('A non-empty playlist URL is required');
+      }
+
+      Hls = externHls;
+
+      _this29.withCredentials = withCredentials;
+      _this29.tech_ = tech;
+      _this29.hls_ = tech.hls;
+      _this29.sourceType_ = sourceType;
+      _this29.useCueTags_ = useCueTags;
+      _this29.blacklistDuration = blacklistDuration;
+      _this29.enableLowInitialPlaylist = enableLowInitialPlaylist;
+      if (_this29.useCueTags_) {
+        _this29.cueTagsTrack_ = _this29.tech_.addTextTrack('metadata', 'ad-cues');
+        _this29.cueTagsTrack_.inBandMetadataTrackDispatchType = '';
+      }
+
+      _this29.requestOptions_ = {
+        withCredentials: _this29.withCredentials,
+        timeout: null
+      };
+
+      _this29.mediaTypes_ = createMediaTypes();
+
+      _this29.mediaSource = new videojs$1.MediaSource();
+
+      // load the media source into the player
+      _this29.mediaSource.addEventListener('sourceopen', _this29.handleSourceOpen_.bind(_this29));
+
+      _this29.seekable_ = videojs$1.createTimeRanges();
+      _this29.hasPlayed_ = function () {
+        return false;
+      };
+
+      _this29.syncController_ = new SyncController(options);
+      _this29.segmentMetadataTrack_ = tech.addRemoteTextTrack({
+        kind: 'metadata',
+        label: 'segment-metadata'
+      }, false).track;
+
+      _this29.decrypter_ = new Decrypter$1();
+
+      var segmentLoaderSettings = {
+        hls: _this29.hls_,
+        mediaSource: _this29.mediaSource,
+        currentTime: _this29.tech_.currentTime.bind(_this29.tech_),
+        seekable: function seekable() {
+          return _this29.seekable();
+        },
+        seeking: function seeking() {
+          return _this29.tech_.seeking();
+        },
+        duration: function duration() {
+          return _this29.mediaSource.duration;
+        },
+        hasPlayed: function hasPlayed() {
+          return _this29.hasPlayed_();
+        },
+        goalBufferLength: function goalBufferLength() {
+          return _this29.goalBufferLength();
+        },
+        bandwidth: bandwidth,
+        syncController: _this29.syncController_,
+        decrypter: _this29.decrypter_,
+        sourceType: _this29.sourceType_
+      };
+
+      _this29.masterPlaylistLoader_ = _this29.sourceType_ === 'dash' ? new DashPlaylistLoader(url, _this29.hls_, _this29.withCredentials) : new PlaylistLoader(url, _this29.hls_, _this29.withCredentials);
+      _this29.setupMasterPlaylistLoaderListeners_();
+
+      // setup segment loaders
+      // combined audio/video or just video when alternate audio track is selected
+      _this29.mainSegmentLoader_ = new SegmentLoader(videojs$1.mergeOptions(segmentLoaderSettings, {
+        segmentMetadataTrack: _this29.segmentMetadataTrack_,
+        loaderType: 'main'
+      }), options);
+
+      // alternate audio track
+      _this29.audioSegmentLoader_ = new SegmentLoader(videojs$1.mergeOptions(segmentLoaderSettings, {
+        loaderType: 'audio'
+      }), options);
+
+      _this29.subtitleSegmentLoader_ = new VTTSegmentLoader(videojs$1.mergeOptions(segmentLoaderSettings, {
+        loaderType: 'vtt'
+      }), options);
+
+      _this29.setupSegmentLoaderListeners_();
+
+      // Create SegmentLoader stat-getters
+      loaderStats.forEach(function (stat) {
+        _this29[stat + '_'] = sumLoaderStat.bind(_this29, stat);
+      });
+
+      _this29.logger_ = logger('MPC');
+
+      _this29.masterPlaylistLoader_.load();
+      return _this29;
+    }
+
+    /**
+     * Register event handlers on the master playlist loader. A helper
+     * function for construction time.
+     *
+     * @private
+     */
+
+
+    MasterPlaylistController.prototype.setupMasterPlaylistLoaderListeners_ = function setupMasterPlaylistLoaderListeners_() {
+      var _this30 = this;
+
+      this.masterPlaylistLoader_.on('loadedmetadata', function () {
+        var media = _this30.masterPlaylistLoader_.media();
+        var requestTimeout = _this30.masterPlaylistLoader_.targetDuration * 1.5 * 1000;
+
+        // If we don't have any more available playlists, we don't want to
+        // timeout the request.
+        if (isLowestEnabledRendition(_this30.masterPlaylistLoader_.master, _this30.masterPlaylistLoader_.media())) {
+          _this30.requestOptions_.timeout = 0;
+        } else {
+          _this30.requestOptions_.timeout = requestTimeout;
+        }
+
+        // if this isn't a live video and preload permits, start
+        // downloading segments
+        if (media.endList && _this30.tech_.preload() !== 'none') {
+          _this30.mainSegmentLoader_.playlist(media, _this30.requestOptions_);
+          _this30.mainSegmentLoader_.load();
+        }
+
+        setupMediaGroups({
+          sourceType: _this30.sourceType_,
+          segmentLoaders: {
+            AUDIO: _this30.audioSegmentLoader_,
+            SUBTITLES: _this30.subtitleSegmentLoader_,
+            main: _this30.mainSegmentLoader_
+          },
+          tech: _this30.tech_,
+          requestOptions: _this30.requestOptions_,
+          masterPlaylistLoader: _this30.masterPlaylistLoader_,
+          hls: _this30.hls_,
+          master: _this30.master(),
+          mediaTypes: _this30.mediaTypes_,
+          blacklistCurrentPlaylist: _this30.blacklistCurrentPlaylist.bind(_this30)
+        });
+
+        _this30.triggerPresenceUsage_(_this30.master(), media);
+
+        try {
+          _this30.setupSourceBuffers_();
+        } catch (e) {
+          videojs$1.log.warn('Failed to create SourceBuffers', e);
+          return _this30.mediaSource.endOfStream('decode');
+        }
+        _this30.setupFirstPlay();
+
+        _this30.trigger('selectedinitialmedia');
+      });
+
+      this.masterPlaylistLoader_.on('loadedplaylist', function () {
+        var updatedPlaylist = _this30.masterPlaylistLoader_.media();
+
+        if (!updatedPlaylist) {
+          // blacklist any variants that are not supported by the browser before selecting
+          // an initial media as the playlist selectors do not consider browser support
+          _this30.excludeUnsupportedVariants_();
+
+          var selectedMedia = void 0;
+
+          if (_this30.enableLowInitialPlaylist) {
+            selectedMedia = _this30.selectInitialPlaylist();
+          }
+
+          if (!selectedMedia) {
+            selectedMedia = _this30.selectPlaylist();
+          }
+
+          _this30.initialMedia_ = selectedMedia;
+          _this30.masterPlaylistLoader_.media(_this30.initialMedia_);
+          return;
+        }
+
+        if (_this30.useCueTags_) {
+          _this30.updateAdCues_(updatedPlaylist);
+        }
+
+        // TODO: Create a new event on the PlaylistLoader that signals
+        // that the segments have changed in some way and use that to
+        // update the SegmentLoader instead of doing it twice here and
+        // on `mediachange`
+        _this30.mainSegmentLoader_.playlist(updatedPlaylist, _this30.requestOptions_);
+        _this30.updateDuration();
+
+        // If the player isn't paused, ensure that the segment loader is running,
+        // as it is possible that it was temporarily stopped while waiting for
+        // a playlist (e.g., in case the playlist errored and we re-requested it).
+        if (!_this30.tech_.paused()) {
+          _this30.mainSegmentLoader_.load();
+        }
+
+        if (!updatedPlaylist.endList) {
+          var addSeekableRange = function addSeekableRange() {
+            var seekable$$1 = _this30.seekable();
+
+            if (seekable$$1.length !== 0) {
+              _this30.mediaSource.addSeekableRange_(seekable$$1.start(0), seekable$$1.end(0));
+            }
+          };
+
+          if (_this30.duration() !== Infinity) {
+            var onDurationchange = function onDurationchange() {
+              if (_this30.duration() === Infinity) {
+                addSeekableRange();
+              } else {
+                _this30.tech_.one('durationchange', onDurationchange);
+              }
+            };
+
+            _this30.tech_.one('durationchange', onDurationchange);
+          } else {
+            addSeekableRange();
+          }
+        }
+      });
+
+      this.masterPlaylistLoader_.on('error', function () {
+        _this30.blacklistCurrentPlaylist(_this30.masterPlaylistLoader_.error);
+      });
+
+      this.masterPlaylistLoader_.on('mediachanging', function () {
+        _this30.mainSegmentLoader_.abort();
+        _this30.mainSegmentLoader_.pause();
+      });
+
+      this.masterPlaylistLoader_.on('mediachange', function () {
+        var media = _this30.masterPlaylistLoader_.media();
+        var requestTimeout = _this30.masterPlaylistLoader_.targetDuration * 1.5 * 1000;
+
+        // If we don't have any more available playlists, we don't want to
+        // timeout the request.
+        if (isLowestEnabledRendition(_this30.masterPlaylistLoader_.master, _this30.masterPlaylistLoader_.media())) {
+          _this30.requestOptions_.timeout = 0;
+        } else {
+          _this30.requestOptions_.timeout = requestTimeout;
+        }
+
+        // TODO: Create a new event on the PlaylistLoader that signals
+        // that the segments have changed in some way and use that to
+        // update the SegmentLoader instead of doing it twice here and
+        // on `loadedplaylist`
+        _this30.mainSegmentLoader_.playlist(media, _this30.requestOptions_);
+        _this30.mainSegmentLoader_.load();
+
+        _this30.tech_.trigger({
+          type: 'mediachange',
+          bubbles: true
+        });
+      });
+
+      this.masterPlaylistLoader_.on('playlistunchanged', function () {
+        var updatedPlaylist = _this30.masterPlaylistLoader_.media();
+        var playlistOutdated = _this30.stuckAtPlaylistEnd_(updatedPlaylist);
+
+        if (playlistOutdated) {
+          // Playlist has stopped updating and we're stuck at its end. Try to
+          // blacklist it and switch to another playlist in the hope that that
+          // one is updating (and give the player a chance to re-adjust to the
+          // safe live point).
+          _this30.blacklistCurrentPlaylist({
+            message: 'Playlist no longer updating.'
+          });
+          // useful for monitoring QoS
+          _this30.tech_.trigger('playliststuck');
+        }
+      });
+
+      this.masterPlaylistLoader_.on('renditiondisabled', function () {
+        _this30.tech_.trigger({type: 'usage', name: 'hls-rendition-disabled'});
+      });
+      this.masterPlaylistLoader_.on('renditionenabled', function () {
+        _this30.tech_.trigger({type: 'usage', name: 'hls-rendition-enabled'});
+      });
+    };
+
+    /**
+     * A helper function for triggerring presence usage events once per source
+     *
+     * @private
+     */
+
+
+    MasterPlaylistController.prototype.triggerPresenceUsage_ = function triggerPresenceUsage_(master, media) {
+      var mediaGroups = master.mediaGroups || {};
+      var defaultDemuxed = true;
+      var audioGroupKeys = Object.keys(mediaGroups.AUDIO);
+
+      for (var mediaGroup in mediaGroups.AUDIO) {
+        for (var label in mediaGroups.AUDIO[mediaGroup]) {
+          var properties = mediaGroups.AUDIO[mediaGroup][label];
+
+          if (!properties.uri) {
+            defaultDemuxed = false;
+          }
+        }
+      }
+
+      if (defaultDemuxed) {
+        this.tech_.trigger({type: 'usage', name: 'hls-demuxed'});
+      }
+
+      if (Object.keys(mediaGroups.SUBTITLES).length) {
+        this.tech_.trigger({type: 'usage', name: 'hls-webvtt'});
+      }
+
+      if (Hls.Playlist.isAes(media)) {
+        this.tech_.trigger({type: 'usage', name: 'hls-aes'});
+      }
+
+      if (Hls.Playlist.isFmp4(media)) {
+        this.tech_.trigger({type: 'usage', name: 'hls-fmp4'});
+      }
+
+      if (audioGroupKeys.length && Object.keys(mediaGroups.AUDIO[audioGroupKeys[0]]).length > 1) {
+        this.tech_.trigger({type: 'usage', name: 'hls-alternate-audio'});
+      }
+
+      if (this.useCueTags_) {
+        this.tech_.trigger({type: 'usage', name: 'hls-playlist-cue-tags'});
+      }
+    };
+    /**
+     * Register event handlers on the segment loaders. A helper function
+     * for construction time.
+     *
+     * @private
+     */
+
+
+    MasterPlaylistController.prototype.setupSegmentLoaderListeners_ = function setupSegmentLoaderListeners_() {
+      var _this31 = this;
+
+      this.mainSegmentLoader_.on('bandwidthupdate', function () {
+        var nextPlaylist = _this31.selectPlaylist();
+        var currentPlaylist = _this31.masterPlaylistLoader_.media();
+        var buffered = _this31.tech_.buffered();
+        var forwardBuffer = buffered.length ? buffered.end(buffered.length - 1) - _this31.tech_.currentTime() : 0;
+
+        var bufferLowWaterLine = _this31.bufferLowWaterLine();
+
+        // If the playlist is live, then we want to not take low water line into account.
+        // This is because in LIVE, the player plays 3 segments from the end of the
+        // playlist, and if `BUFFER_LOW_WATER_LINE` is greater than the duration availble
+        // in those segments, a viewer will never experience a rendition upswitch.
+        if (!currentPlaylist.endList ||
+          // For the same reason as LIVE, we ignore the low water line when the VOD
+          // duration is below the max potential low water line
+          _this31.duration() < Config.MAX_BUFFER_LOW_WATER_LINE ||
+          // we want to switch down to lower resolutions quickly to continue playback, but
+          nextPlaylist.attributes.BANDWIDTH < currentPlaylist.attributes.BANDWIDTH ||
+          // ensure we have some buffer before we switch up to prevent us running out of
+          // buffer while loading a higher rendition.
+          forwardBuffer >= bufferLowWaterLine) {
+          _this31.masterPlaylistLoader_.media(nextPlaylist);
+        }
+
+        _this31.tech_.trigger('bandwidthupdate');
+      });
+      this.mainSegmentLoader_.on('progress', function () {
+        _this31.trigger('progress');
+      });
+
+      this.mainSegmentLoader_.on('error', function () {
+        _this31.blacklistCurrentPlaylist(_this31.mainSegmentLoader_.error());
+      });
+
+      this.mainSegmentLoader_.on('syncinfoupdate', function () {
+        _this31.onSyncInfoUpdate_();
+      });
+
+      this.mainSegmentLoader_.on('timestampoffset', function () {
+        _this31.tech_.trigger({type: 'usage', name: 'hls-timestamp-offset'});
+      });
+      this.audioSegmentLoader_.on('syncinfoupdate', function () {
+        _this31.onSyncInfoUpdate_();
+      });
+
+      this.mainSegmentLoader_.on('ended', function () {
+        _this31.onEndOfStream();
+      });
+
+      this.mainSegmentLoader_.on('earlyabort', function () {
+        _this31.blacklistCurrentPlaylist({
+          message: 'Aborted early because there isn\'t enough bandwidth to complete the ' + 'request without rebuffering.'
+        }, ABORT_EARLY_BLACKLIST_SECONDS);
+      });
+
+      this.mainSegmentLoader_.on('reseteverything', function () {
+        // If playing an MTS stream, a videojs.MediaSource is listening for
+        // hls-reset to reset caption parsing state in the transmuxer
+        _this31.tech_.trigger('hls-reset');
+      });
+
+      this.mainSegmentLoader_.on('segmenttimemapping', function (event) {
+        // If playing an MTS stream in html, a videojs.MediaSource is listening for
+        // hls-segment-time-mapping update its internal mapping of stream to display time
+        _this31.tech_.trigger({
+          type: 'hls-segment-time-mapping',
+          mapping: event.mapping
+        });
+      });
+
+      this.audioSegmentLoader_.on('ended', function () {
+        _this31.onEndOfStream();
+      });
+    };
+
+    MasterPlaylistController.prototype.mediaSecondsLoaded_ = function mediaSecondsLoaded_() {
+      return Math.max(this.audioSegmentLoader_.mediaSecondsLoaded + this.mainSegmentLoader_.mediaSecondsLoaded);
+    };
+
+    /**
+     * Call load on our SegmentLoaders
+     */
+
+
+    MasterPlaylistController.prototype.load = function load() {
+      this.mainSegmentLoader_.load();
+      if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
+        this.audioSegmentLoader_.load();
+      }
+      if (this.mediaTypes_.SUBTITLES.activePlaylistLoader) {
+        this.subtitleSegmentLoader_.load();
+      }
+    };
+
+    /**
+     * Re-tune playback quality level for the current player
+     * conditions. This method may perform destructive actions, like
+     * removing already buffered content, to readjust the currently
+     * active playlist quickly.
+     *
+     * @private
+     */
+
+
+    MasterPlaylistController.prototype.fastQualityChange_ = function fastQualityChange_() {
+      var media = this.selectPlaylist();
+
+      if (media !== this.masterPlaylistLoader_.media()) {
+        this.masterPlaylistLoader_.media(media);
+
+        this.mainSegmentLoader_.resetLoader();
+        // don't need to reset audio as it is reset when media changes
+      }
+    };
+
+    /**
+     * Begin playback.
+     */
+
+
+    MasterPlaylistController.prototype.play = function play() {
+      if (this.setupFirstPlay()) {
+        return;
+      }
+
+      if (this.tech_.ended()) {
+        this.tech_.setCurrentTime(0);
+      }
+
+      if (this.hasPlayed_()) {
+        this.load();
+      }
+
+      var seekable$$1 = this.tech_.seekable();
+
+      // if the viewer has paused and we fell out of the live window,
+      // seek forward to the live point
+      if (this.tech_.duration() === Infinity) {
+        if (this.tech_.currentTime() < seekable$$1.start(0)) {
+          return this.tech_.setCurrentTime(seekable$$1.end(seekable$$1.length - 1));
+        }
+      }
+    };
+
+    /**
+     * Seek to the latest media position if this is a live video and the
+     * player and video are loaded and initialized.
+     */
+
+
+    MasterPlaylistController.prototype.setupFirstPlay = function setupFirstPlay() {
+      var _this32 = this;
+
+      var media = this.masterPlaylistLoader_.media();
+
+      // Check that everything is ready to begin buffering for the first call to playOld
+      //  If 1) there is no active media
+      //     2) the player is paused
+      //     3) the first playOld has already been setup
+      // then exit early
+      if (!media || this.tech_.paused() || this.hasPlayed_()) {
+        return false;
+      }
+
+      // when the video is a live stream
+      if (!media.endList) {
+        var seekable$$1 = this.seekable();
+
+        if (!seekable$$1.length) {
+          // without a seekable range, the player cannot seek to begin buffering at the live
+          // point
+          return false;
+        }
+
+        if (videojs$1.browser.IE_VERSION && this.tech_.readyState() === 0) {
+          // IE11 throws an InvalidStateError if you try to set currentTime while the
+          // readyState is 0, so it must be delayed until the tech fires loadedmetadata.
+          this.tech_.one('loadedmetadata', function () {
+            _this32.trigger('firstplay');
+            _this32.tech_.setCurrentTime(seekable$$1.end(0));
+            _this32.hasPlayed_ = function () {
+              return true;
+            };
+          });
+
+          return false;
+        }
+
+        // trigger firstplay to inform the source handler to ignore the next seek event
+        this.trigger('firstplay');
+        // seek to the live point
+        this.tech_.setCurrentTime(seekable$$1.end(0));
+      }
+
+      this.hasPlayed_ = function () {
+        return true;
+      };
+      // we can begin loading now that everything is ready
+      this.load();
+      return true;
+    };
+
+    /**
+     * handle the sourceopen event on the MediaSource
+     *
+     * @private
+     */
+
+
+    MasterPlaylistController.prototype.handleSourceOpen_ = function handleSourceOpen_() {
+      // Only attempt to create the source buffer if none already exist.
+      // handleSourceOpen is also called when we are "re-opening" a source buffer
+      // after `endOfStream` has been called (in response to a seek for instance)
+      try {
+        this.setupSourceBuffers_();
+      } catch (e) {
+        videojs$1.log.warn('Failed to create Source Buffers', e);
+        return this.mediaSource.endOfStream('decode');
+      }
+
+      // if autoplay is enabled, begin playback. This is duplicative of
+      // code in video.js but is required because playOld() must be invoked
+      // *after* the media source has opened.
+      if (this.tech_.autoplay()) {
+        var playPromise = this.tech_.play();
+
+        // Catch/silence error when a pause interrupts a playOld request
+        // on browsers which return a promise
+        if (typeof playPromise !== 'undefined' && typeof playPromise.then === 'function') {
+          playPromise.then(null, function (e) {
+          });
+        }
+      }
+
+      this.trigger('sourceopen');
+    };
+
+    /**
+     * Calls endOfStream on the media source when all active stream types have called
+     * endOfStream
+     *
+     * @param {string} streamType
+     *        Stream type of the segment loader that called endOfStream
+     * @private
+     */
+
+
+    MasterPlaylistController.prototype.onEndOfStream = function onEndOfStream() {
+      var isEndOfStream = this.mainSegmentLoader_.ended_;
+
+      if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
+        // if the audio playlist loader exists, then alternate audio is active
+        if (!this.mainSegmentLoader_.startingMedia_ || this.mainSegmentLoader_.startingMedia_.containsVideo) {
+          // if we do not know if the main segment loader contains video yet or if we
+          // definitively know the main segment loader contains video, then we need to wait
+          // for both main and audio segment loaders to call endOfStream
+          isEndOfStream = isEndOfStream && this.audioSegmentLoader_.ended_;
+        } else {
+          // otherwise just rely on the audio loader
+          isEndOfStream = this.audioSegmentLoader_.ended_;
+        }
+      }
+
+      if (isEndOfStream) {
+        this.mediaSource.endOfStream();
+      }
+    };
+
+    /**
+     * Check if a playlist has stopped being updated
+     * @param {Object} playlist the media playlist object
+     * @return {boolean} whether the playlist has stopped being updated or not
+     */
+
+
+    MasterPlaylistController.prototype.stuckAtPlaylistEnd_ = function stuckAtPlaylistEnd_(playlist) {
+      var seekable$$1 = this.seekable();
+
+      if (!seekable$$1.length) {
+        // playlist doesn't have enough information to determine whether we are stuck
+        return false;
+      }
+
+      var expired = this.syncController_.getExpiredTime(playlist, this.mediaSource.duration);
+
+      if (expired === null) {
+        return false;
+      }
+
+      // does not use the safe live end to calculate playlist end, since we
+      // don't want to say we are stuck while there is still content
+      var absolutePlaylistEnd = Hls.Playlist.playlistEnd(playlist, expired);
+      var currentTime = this.tech_.currentTime();
+      var buffered = this.tech_.buffered();
+
+      if (!buffered.length) {
+        // return true if the playhead reached the absolute end of the playlist
+        return absolutePlaylistEnd - currentTime <= SAFE_TIME_DELTA;
+      }
+      var bufferedEnd = buffered.end(buffered.length - 1);
+
+      // return true if there is too little buffer left and buffer has reached absolute
+      // end of playlist
+      return bufferedEnd - currentTime <= SAFE_TIME_DELTA && absolutePlaylistEnd - bufferedEnd <= SAFE_TIME_DELTA;
+    };
+
+    /**
+     * Blacklists a playlist when an error occurs for a set amount of time
+     * making it unavailable for selection by the rendition selection algorithm
+     * and then forces a new playlist (rendition) selection.
+     *
+     * @param {Object=} error an optional error that may include the playlist
+     * to blacklist
+     * @param {Number=} blacklistDuration an optional number of seconds to blacklist the
+     * playlist
+     */
+
+
+    MasterPlaylistController.prototype.blacklistCurrentPlaylist = function blacklistCurrentPlaylist() {
+      var error = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+      var blacklistDuration = arguments[1];
+
+      var currentPlaylist = void 0;
+      var nextPlaylist = void 0;
+
+      // If the `error` was generated by the playlist loader, it will contain
+      // the playlist we were trying to load (but failed) and that should be
+      // blacklisted instead of the currently selected playlist which is likely
+      // out-of-date in this scenario
+      currentPlaylist = error.playlist || this.masterPlaylistLoader_.media();
+
+      blacklistDuration = blacklistDuration || error.blacklistDuration || this.blacklistDuration;
+
+      // If there is no current playlist, then an error occurred while we were
+      // trying to load the master OR while we were disposing of the tech
+      if (!currentPlaylist) {
+        this.error = error;
+
+        try {
+          return this.mediaSource.endOfStream('network');
+        } catch (e) {
+          return this.trigger('error');
+        }
+      }
+
+      var isFinalRendition = this.masterPlaylistLoader_.master.playlists.filter(isEnabled).length === 1;
+
+      if (isFinalRendition) {
+        // Never blacklisting this playlist because it's final rendition
+        videojs$1.log.warn('Problem encountered with the current ' + 'HLS playlist. Trying again since it is the final playlist.');
+
+        this.tech_.trigger('retryplaylist');
+        return this.masterPlaylistLoader_.load(isFinalRendition);
+      }
+      // Blacklist this playlist
+      currentPlaylist.excludeUntil = Date.now() + blacklistDuration * 1000;
+      this.tech_.trigger('blacklistplaylist');
+      this.tech_.trigger({type: 'usage', name: 'hls-rendition-blacklisted'});
+
+      // Select a new playlist
+      nextPlaylist = this.selectPlaylist();
+      videojs$1.log.warn('Problem encountered with the current HLS playlist.' + (error.message ? ' ' + error.message : '') + ' Switching to another playlist.');
+
+      return this.masterPlaylistLoader_.media(nextPlaylist);
+    };
+
+    /**
+     * Pause all segment loaders
+     */
+
+
+    MasterPlaylistController.prototype.pauseLoading = function pauseLoading() {
+      this.mainSegmentLoader_.pause();
+      if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
+        this.audioSegmentLoader_.pause();
+      }
+      if (this.mediaTypes_.SUBTITLES.activePlaylistLoader) {
+        this.subtitleSegmentLoader_.pause();
+      }
+    };
+
+    /**
+     * set the current time on all segment loaders
+     *
+     * @param {TimeRange} currentTime the current time to set
+     * @return {TimeRange} the current time
+     */
+
+
+    MasterPlaylistController.prototype.setCurrentTime = function setCurrentTime(currentTime) {
+      var buffered = findRange(this.tech_.buffered(), currentTime);
+
+      if (!(this.masterPlaylistLoader_ && this.masterPlaylistLoader_.media())) {
+        // return immediately if the metadata is not ready yet
+        return 0;
+      }
+
+      // it's clearly an edge-case but don't thrown an error if asked to
+      // seek within an empty playlist
+      if (!this.masterPlaylistLoader_.media().segments) {
+        return 0;
+      }
+
+      // In flash playback, the segment loaders should be reset on every seek, even
+      // in buffer seeks. If the seek location is already buffered, continue buffering as
+      // usual
+      // TODO: redo this comment
+      if (buffered && buffered.length) {
+        return currentTime;
+      }
+
+      // cancel outstanding requests so we begin buffering at the new
+      // location
+      this.mainSegmentLoader_.resetEverything();
+      this.mainSegmentLoader_.abort();
+      if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
+        this.audioSegmentLoader_.resetEverything();
+        this.audioSegmentLoader_.abort();
+      }
+      if (this.mediaTypes_.SUBTITLES.activePlaylistLoader) {
+        this.subtitleSegmentLoader_.resetEverything();
+        this.subtitleSegmentLoader_.abort();
+      }
+
+      // start segment loader loading in case they are paused
+      this.load();
+    };
+
+    /**
+     * get the current duration
+     *
+     * @return {TimeRange} the duration
+     */
+
+
+    MasterPlaylistController.prototype.duration = function duration() {
+      if (!this.masterPlaylistLoader_) {
+        return 0;
+      }
+
+      if (this.mediaSource) {
+        return this.mediaSource.duration;
+      }
+
+      return Hls.Playlist.duration(this.masterPlaylistLoader_.media());
+    };
+
+    /**
+     * check the seekable range
+     *
+     * @return {TimeRange} the seekable range
+     */
+
+
+    MasterPlaylistController.prototype.seekable = function seekable() {
+      return this.seekable_;
+    };
+
+    MasterPlaylistController.prototype.onSyncInfoUpdate_ = function onSyncInfoUpdate_() {
+      var mainSeekable = void 0;
+      var audioSeekable = void 0;
+
+      if (!this.masterPlaylistLoader_) {
+        return;
+      }
+
+      var media = this.masterPlaylistLoader_.media();
+
+      if (!media) {
+        return;
+      }
+
+      var expired = this.syncController_.getExpiredTime(media, this.mediaSource.duration);
+
+      if (expired === null) {
+        // not enough information to update seekable
+        return;
+      }
+
+      mainSeekable = Hls.Playlist.seekable(media, expired);
+
+      if (mainSeekable.length === 0) {
+        return;
+      }
+
+      if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
+        media = this.mediaTypes_.AUDIO.activePlaylistLoader.media();
+        expired = this.syncController_.getExpiredTime(media, this.mediaSource.duration);
+
+        if (expired === null) {
+          return;
+        }
+
+        audioSeekable = Hls.Playlist.seekable(media, expired);
+
+        if (audioSeekable.length === 0) {
+          return;
+        }
+      }
+
+      if (!audioSeekable) {
+        // seekable has been calculated based on buffering video data so it
+        // can be returned directly
+        this.seekable_ = mainSeekable;
+      } else if (audioSeekable.start(0) > mainSeekable.end(0) || mainSeekable.start(0) > audioSeekable.end(0)) {
+        // seekables are pretty far off, rely on main
+        this.seekable_ = mainSeekable;
+      } else {
+        this.seekable_ = videojs$1.createTimeRanges([[audioSeekable.start(0) > mainSeekable.start(0) ? audioSeekable.start(0) : mainSeekable.start(0), audioSeekable.end(0) < mainSeekable.end(0) ? audioSeekable.end(0) : mainSeekable.end(0)]]);
+      }
+
+      this.logger_('seekable updated [' + printableRange(this.seekable_) + ']');
+
+      this.tech_.trigger('seekablechanged');
+    };
+
+    /**
+     * Update the player duration
+     */
+
+
+    MasterPlaylistController.prototype.updateDuration = function updateDuration() {
+      var _this33 = this;
+
+      var oldDuration = this.mediaSource.duration;
+      var newDuration = Hls.Playlist.duration(this.masterPlaylistLoader_.media());
+      var buffered = this.tech_.buffered();
+      var setDuration = function setDuration() {
+        _this33.mediaSource.duration = newDuration;
+        _this33.tech_.trigger('durationchange');
+
+        _this33.mediaSource.removeEventListener('sourceopen', setDuration);
+      };
+
+      if (buffered.length > 0) {
+        newDuration = Math.max(newDuration, buffered.end(buffered.length - 1));
+      }
+
+      // if the duration has changed, invalidate the cached value
+      if (oldDuration !== newDuration) {
+        // update the duration
+        if (this.mediaSource.readyState !== 'open') {
+          this.mediaSource.addEventListener('sourceopen', setDuration);
+        } else {
+          setDuration();
+        }
+      }
+    };
+
+    /**
+     * dispose of the MasterPlaylistController and everything
+     * that it controls
+     */
+
+
+    MasterPlaylistController.prototype.dispose = function dispose() {
+      var _this34 = this;
+
+      this.decrypter_.terminate();
+      this.masterPlaylistLoader_.dispose();
+      this.mainSegmentLoader_.dispose();
+
+      ['AUDIO', 'SUBTITLES'].forEach(function (type) {
+        var groups = _this34.mediaTypes_[type].groups;
+
+        for (var id in groups) {
+          groups[id].forEach(function (group) {
+            if (group.playlistLoader) {
+              group.playlistLoader.dispose();
+            }
+          });
+        }
+      });
+
+      this.audioSegmentLoader_.dispose();
+      this.subtitleSegmentLoader_.dispose();
+    };
+
+    /**
+     * return the master playlist object if we have one
+     *
+     * @return {Object} the master playlist object that we parsed
+     */
+
+
+    MasterPlaylistController.prototype.master = function master() {
+      return this.masterPlaylistLoader_.master;
+    };
+
+    /**
+     * return the currently selected playlist
+     *
+     * @return {Object} the currently selected playlist object that we parsed
+     */
+
+
+    MasterPlaylistController.prototype.media = function media() {
+      // playlist loader will not return media if it has not been fully loaded
+      return this.masterPlaylistLoader_.media() || this.initialMedia_;
+    };
+
+    /**
+     * setup our internal source buffers on our segment Loaders
+     *
+     * @private
+     */
+
+
+    MasterPlaylistController.prototype.setupSourceBuffers_ = function setupSourceBuffers_() {
+      var media = this.masterPlaylistLoader_.media();
+      var mimeTypes = void 0;
+
+      // wait until a media playlist is available and the Media Source is
+      // attached
+      if (!media || this.mediaSource.readyState !== 'open') {
+        return;
+      }
+
+      mimeTypes = mimeTypesForPlaylist(this.masterPlaylistLoader_.master, media);
+      if (mimeTypes.length < 1) {
+        this.error = 'No compatible SourceBuffer configuration for the variant stream:' + media.resolvedUri;
+        return this.mediaSource.endOfStream('decode');
+      }
+
+      this.configureLoaderMimeTypes_(mimeTypes);
+      // exclude any incompatible variant streams from future playlist
+      // selection
+      this.excludeIncompatibleVariants_(media);
+    };
+
+    MasterPlaylistController.prototype.configureLoaderMimeTypes_ = function configureLoaderMimeTypes_(mimeTypes) {
+      // If the content is demuxed, we can't start appending segments to a source buffer
+      // until both source buffers are set up, or else the browser may not let us add the
+      // second source buffer (it will assume we are playing either audio only or video
+      // only).
+      var sourceBufferEmitter =
+        // If there is more than one mime type
+        mimeTypes.length > 1 &&
+        // and the first mime type does not have muxed video and audio
+        mimeTypes[0].indexOf(',') === -1 &&
+        // and the two mime types are different (they can be the same in the case of audio
+        // only with alternate audio)
+        mimeTypes[0] !== mimeTypes[1] ?
+          // then we want to wait on the second source buffer
+          new videojs$1.EventTarget() :
+          // otherwise there is no need to wait as the content is either audio only,
+          // video only, or muxed content.
+          null;
+
+      this.mainSegmentLoader_.mimeType(mimeTypes[0], sourceBufferEmitter);
+      if (mimeTypes[1]) {
+        this.audioSegmentLoader_.mimeType(mimeTypes[1], sourceBufferEmitter);
+      }
+    };
+
+    /**
+     * Blacklists playlists with codecs that are unsupported by the browser.
+     */
+
+
+    MasterPlaylistController.prototype.excludeUnsupportedVariants_ = function excludeUnsupportedVariants_() {
+      this.master().playlists.forEach(function (variant) {
+        if (variant.attributes.CODECS && window_1.MediaSource && window_1.MediaSource.isTypeSupported && !window_1.MediaSource.isTypeSupported('video/mp4; codecs="' + mapLegacyAvcCodecs(variant.attributes.CODECS) + '"')) {
+          variant.excludeUntil = Infinity;
+        }
+      });
+    };
+
+    /**
+     * Blacklist playlists that are known to be codec or
+     * stream-incompatible with the SourceBuffer configuration. For
+     * instance, Media Source Extensions would cause the video element to
+     * stall waiting for video data if you switched from a variant with
+     * video and audio to an audio-only one.
+     *
+     * @param {Object} media a media playlist compatible with the current
+     * set of SourceBuffers. Variants in the current master playlist that
+     * do not appear to have compatible codec or stream configurations
+     * will be excluded from the default playlist selection algorithm
+     * indefinitely.
+     * @private
+     */
+
+
+    MasterPlaylistController.prototype.excludeIncompatibleVariants_ = function excludeIncompatibleVariants_(media) {
+      var codecCount = 2;
+      var videoCodec = null;
+      var codecs = void 0;
+
+      if (media.attributes.CODECS) {
+        codecs = parseCodecs(media.attributes.CODECS);
+        videoCodec = codecs.videoCodec;
+        codecCount = codecs.codecCount;
+      }
+
+      this.master().playlists.forEach(function (variant) {
+        var variantCodecs = {
+          codecCount: 2,
+          videoCodec: null
+        };
+
+        if (variant.attributes.CODECS) {
+          variantCodecs = parseCodecs(variant.attributes.CODECS);
+        }
+
+        // if the streams differ in the presence or absence of audio or
+        // video, they are incompatible
+        if (variantCodecs.codecCount !== codecCount) {
+          variant.excludeUntil = Infinity;
+        }
+
+        // if h.264 is specified on the current playlist, some flavor of
+        // it must be specified on all compatible variants
+        if (variantCodecs.videoCodec !== videoCodec) {
+          variant.excludeUntil = Infinity;
+        }
+      });
+    };
+
+    MasterPlaylistController.prototype.updateAdCues_ = function updateAdCues_(media) {
+      var offset = 0;
+      var seekable$$1 = this.seekable();
+
+      if (seekable$$1.length) {
+        offset = seekable$$1.start(0);
+      }
+
+      updateAdCues(media, this.cueTagsTrack_, offset);
+    };
+
+    /**
+     * Calculates the desired forward buffer length based on current time
+     *
+     * @return {Number} Desired forward buffer length in seconds
+     */
+
+
+    MasterPlaylistController.prototype.goalBufferLength = function goalBufferLength() {
+      var currentTime = this.tech_.currentTime();
+      var initial = Config.GOAL_BUFFER_LENGTH;
+      var rate = Config.GOAL_BUFFER_LENGTH_RATE;
+      var max = Math.max(initial, Config.MAX_GOAL_BUFFER_LENGTH);
+
+      return Math.min(initial + currentTime * rate, max);
+    };
+
+    /**
+     * Calculates the desired buffer low water line based on current time
+     *
+     * @return {Number} Desired buffer low water line in seconds
+     */
+
+
+    MasterPlaylistController.prototype.bufferLowWaterLine = function bufferLowWaterLine() {
+      var currentTime = this.tech_.currentTime();
+      var initial = Config.BUFFER_LOW_WATER_LINE;
+      var rate = Config.BUFFER_LOW_WATER_LINE_RATE;
+      var max = Math.max(initial, Config.MAX_BUFFER_LOW_WATER_LINE);
+
+      return Math.min(initial + currentTime * rate, max);
+    };
+
+    return MasterPlaylistController;
+  }(videojs$1.EventTarget);
+
+  /**
+   * Returns a function that acts as the Enable/disable playlist function.
+   *
+   * @param {PlaylistLoader} loader - The master playlist loader
+   * @param {String} playlistUri - uri of the playlist
+   * @param {Function} changePlaylistFn - A function to be called after a
+   * playlist's enabled-state has been changed. Will NOT be called if a
+   * playlist's enabled-state is unchanged
+   * @param {Boolean=} enable - Value to set the playlist enabled-state to
+   * or if undefined returns the current enabled-state for the playlist
+   * @return {Function} Function for setting/getting enabled
+   */
+
+
+  var enableFunction = function enableFunction(loader, playlistUri, changePlaylistFn) {
+    return function (enable) {
+      var playlist = loader.master.playlists[playlistUri];
+      var incompatible = isIncompatible(playlist);
+      var currentlyEnabled = isEnabled(playlist);
+
+      if (typeof enable === 'undefined') {
+        return currentlyEnabled;
+      }
+
+      if (enable) {
+        delete playlist.disabled;
+      } else {
+        playlist.disabled = true;
+      }
+
+      if (enable !== currentlyEnabled && !incompatible) {
+        // Ensure the outside world knows about our changes
+        changePlaylistFn();
+        if (enable) {
+          loader.trigger('renditionenabled');
+        } else {
+          loader.trigger('renditiondisabled');
+        }
+      }
+      return enable;
+    };
+  };
+
+  /**
+   * The representation object encapsulates the publicly visible information
+   * in a media playlist along with a setter/getter-type function (enabled)
+   * for changing the enabled-state of a particular playlist entry
+   *
+   * @class Representation
+   */
+
+  var Representation = function Representation(hlsHandler, playlist, id) {
+    classCallCheck(this, Representation);
+
+    // Get a reference to a bound version of fastQualityChange_
+    var fastChangeFunction = hlsHandler.masterPlaylistController_.fastQualityChange_.bind(hlsHandler.masterPlaylistController_);
+
+    // some playlist attributes are optional
+    if (playlist.attributes.RESOLUTION) {
+      var resolution = playlist.attributes.RESOLUTION;
+
+      this.width = resolution.width;
+      this.height = resolution.height;
+    }
+
+    this.bandwidth = playlist.attributes.BANDWIDTH;
+
+    // The id is simply the ordinality of the media playlist
+    // within the master playlist
+    this.id = id;
+
+    // Partially-apply the enableFunction to create a playlist-
+    // specific variant
+    this.enabled = enableFunction(hlsHandler.playlists, playlist.uri, fastChangeFunction);
+  };
+
+  /**
+   * A mixin function that adds the `representations` api to an instance
+   * of the HlsHandler class
+   * @param {HlsHandler} hlsHandler - An instance of HlsHandler to add the
+   * representation API into
+   */
+
+
+  var renditionSelectionMixin = function renditionSelectionMixin(hlsHandler) {
+    var playlists = hlsHandler.playlists;
+
+    // Add a single API-specific function to the HlsHandler instance
+    hlsHandler.representations = function () {
+      return playlists.master.playlists.filter(function (media) {
+        return !isIncompatible(media);
+      }).map(function (e, i) {
+        return new Representation(hlsHandler, e, e.uri);
+      });
+    };
+  };
+
+  /**
+   * @file playback-watcher.js
+   *
+   * Playback starts, and now my watch begins. It shall not end until my death. I shall
+   * take no wait, hold no uncleared timeouts, father no bad seeks. I shall wear no crowns
+   * and win no glory. I shall live and die at my post. I am the corrector of the underflow.
+   * I am the watcher of gaps. I am the shield that guards the realms of seekable. I pledge
+   * my life and honor to the Playback Watch, for this Player and all the Players to come.
+   */
+
+    // Set of events that reset the playback-watcher time check logic and clear the timeout
+  var timerCancelEvents = ['seeking', 'seeked', 'pause', 'playing', 'error'];
+
+  /**
+   * @class PlaybackWatcher
+   */
+
+  var PlaybackWatcher = function () {
+    /**
+     * Represents an PlaybackWatcher object.
+     * @constructor
+     * @param {object} options an object that includes the tech and settings
+     */
+    function PlaybackWatcher(options) {
+      var _this35 = this;
+
+      classCallCheck(this, PlaybackWatcher);
+
+      this.tech_ = options.tech;
+      this.seekable = options.seekable;
+
+      this.consecutiveUpdates = 0;
+      this.lastRecordedTime = null;
+      this.timer_ = null;
+      this.checkCurrentTimeTimeout_ = null;
+      this.logger_ = logger('PlaybackWatcher');
+
+      this.logger_('initialize');
+
+      var canPlayHandler = function canPlayHandler() {
+        return _this35.monitorCurrentTime_();
+      };
+      var waitingHandler = function waitingHandler() {
+        return _this35.techWaiting_();
+      };
+      var cancelTimerHandler = function cancelTimerHandler() {
+        return _this35.cancelTimer_();
+      };
+      var fixesBadSeeksHandler = function fixesBadSeeksHandler() {
+        return _this35.fixesBadSeeks_();
+      };
+
+      this.tech_.on('seekablechanged', fixesBadSeeksHandler);
+      this.tech_.on('waiting', waitingHandler);
+      this.tech_.on(timerCancelEvents, cancelTimerHandler);
+      this.tech_.on('canplay', canPlayHandler);
+
+      // Define the dispose function to clean up our events
+      this.dispose = function () {
+        _this35.logger_('dispose');
+        _this35.tech_.off('seekablechanged', fixesBadSeeksHandler);
+        _this35.tech_.off('waiting', waitingHandler);
+        _this35.tech_.off(timerCancelEvents, cancelTimerHandler);
+        _this35.tech_.off('canplay', canPlayHandler);
+        if (_this35.checkCurrentTimeTimeout_) {
+          window_1.clearTimeout(_this35.checkCurrentTimeTimeout_);
+        }
+        _this35.cancelTimer_();
+      };
+    }
+
+    /**
+     * Periodically check current time to see if playback stopped
+     *
+     * @private
+     */
+
+
+    PlaybackWatcher.prototype.monitorCurrentTime_ = function monitorCurrentTime_() {
+      this.checkCurrentTime_();
+
+      if (this.checkCurrentTimeTimeout_) {
+        window_1.clearTimeout(this.checkCurrentTimeTimeout_);
+      }
+
+      // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
+      this.checkCurrentTimeTimeout_ = window_1.setTimeout(this.monitorCurrentTime_.bind(this), 250);
+    };
+
+    /**
+     * The purpose of this function is to emulate the "waiting" event on
+     * browsers that do not emit it when they are waiting for more
+     * data to continue playback
+     *
+     * @private
+     */
+
+
+    PlaybackWatcher.prototype.checkCurrentTime_ = function checkCurrentTime_() {
+      if (this.tech_.seeking() && this.fixesBadSeeks_()) {
+        this.consecutiveUpdates = 0;
+        this.lastRecordedTime = this.tech_.currentTime();
+        return;
+      }
+
+      if (this.tech_.paused() || this.tech_.seeking()) {
+        return;
+      }
+
+      var currentTime = this.tech_.currentTime();
+      var buffered = this.tech_.buffered();
+
+      if (this.lastRecordedTime === currentTime && (!buffered.length || currentTime + SAFE_TIME_DELTA >= buffered.end(buffered.length - 1))) {
+        // If current time is at the end of the final buffered region, then any playback
+        // stall is most likely caused by buffering in a low bandwidth environment. The tech
+        // should fire a `waiting` event in this scenario, but due to browser and tech
+        // inconsistencies. Calling `techWaiting_` here allows us to simulate
+        // responding to a native `waiting` event when the tech fails to emit one.
+        return this.techWaiting_();
+      }
+
+      if (this.consecutiveUpdates >= 5 && currentTime === this.lastRecordedTime) {
+        this.consecutiveUpdates++;
+        this.waiting_();
+      } else if (currentTime === this.lastRecordedTime) {
+        this.consecutiveUpdates++;
+      } else {
+        this.consecutiveUpdates = 0;
+        this.lastRecordedTime = currentTime;
+      }
+    };
+
+    /**
+     * Cancels any pending timers and resets the 'timeupdate' mechanism
+     * designed to detect that we are stalled
+     *
+     * @private
+     */
+
+
+    PlaybackWatcher.prototype.cancelTimer_ = function cancelTimer_() {
+      this.consecutiveUpdates = 0;
+
+      if (this.timer_) {
+        this.logger_('cancelTimer_');
+        clearTimeout(this.timer_);
+      }
+
+      this.timer_ = null;
+    };
+
+    /**
+     * Fixes situations where there's a bad seek
+     *
+     * @return {Boolean} whether an action was taken to fix the seek
+     * @private
+     */
+
+
+    PlaybackWatcher.prototype.fixesBadSeeks_ = function fixesBadSeeks_() {
+      var seeking = this.tech_.seeking();
+      var seekable = this.seekable();
+      var currentTime = this.tech_.currentTime();
+      var seekTo = void 0;
+
+      if (seeking && this.afterSeekableWindow_(seekable, currentTime)) {
+        var seekableEnd = seekable.end(seekable.length - 1);
+
+        // sync to live point (if VOD, our seekable was updated and we're simply adjusting)
+        seekTo = seekableEnd;
+      }
+
+      if (seeking && this.beforeSeekableWindow_(seekable, currentTime)) {
+        var seekableStart = seekable.start(0);
+
+        // sync to the beginning of the live window
+        // provide a buffer of .1 seconds to handle rounding/imprecise numbers
+        seekTo = seekableStart + SAFE_TIME_DELTA;
+      }
+
+      if (typeof seekTo !== 'undefined') {
+        this.logger_('Trying to seek outside of seekable at time ' + currentTime + ' with ' + ('seekable range ' + printableRange(seekable) + '. Seeking to ') + (seekTo + '.'));
+
+        this.tech_.setCurrentTime(seekTo);
+        return true;
+      }
+
+      return false;
+    };
+
+    /**
+     * Handler for situations when we determine the player is waiting.
+     *
+     * @private
+     */
+
+
+    PlaybackWatcher.prototype.waiting_ = function waiting_() {
+      if (this.techWaiting_()) {
+        return;
+      }
+
+      // All tech waiting checks failed. Use last resort correction
+      var currentTime = this.tech_.currentTime();
+      var buffered = this.tech_.buffered();
+      var currentRange = findRange(buffered, currentTime);
+
+      // Sometimes the player can stall for unknown reasons within a contiguous buffered
+      // region with no indication that anything is amiss (seen in Firefox). Seeking to
+      // currentTime is usually enough to kickstart the player. This checks that the player
+      // is currently within a buffered region before attempting a corrective seek.
+      // Chrome does not appear to continue `timeupdate` events after a `waiting` event
+      // until there is ~ 3 seconds of forward buffer available. PlaybackWatcher should also
+      // make sure there is ~3 seconds of forward buffer before taking any corrective action
+      // to avoid triggering an `unknownwaiting` event when the network is slow.
+      if (currentRange.length && currentTime + 3 <= currentRange.end(0)) {
+        this.cancelTimer_();
+        this.tech_.setCurrentTime(currentTime);
+
+        this.logger_('Stopped at ' + currentTime + ' while inside a buffered region ' + ('[' + currentRange.start(0) + ' -> ' + currentRange.end(0) + ']. Attempting to resume ') + 'playback by seeking to the current time.');
+
+        // unknown waiting corrections may be useful for monitoring QoS
+        this.tech_.trigger({type: 'usage', name: 'hls-unknown-waiting'});
+        return;
+      }
+    };
+
+    /**
+     * Handler for situations when the tech fires a `waiting` event
+     *
+     * @return {Boolean}
+     *         True if an action (or none) was needed to correct the waiting. False if no
+     *         checks passed
+     * @private
+     */
+
+
+    PlaybackWatcher.prototype.techWaiting_ = function techWaiting_() {
+      var seekable = this.seekable();
+      var currentTime = this.tech_.currentTime();
+
+      if (this.tech_.seeking() && this.fixesBadSeeks_()) {
+        // Tech is seeking or bad seek fixed, no action needed
+        return true;
+      }
+
+      if (this.tech_.seeking() || this.timer_ !== null) {
+        // Tech is seeking or already waiting on another action, no action needed
+        return true;
+      }
+
+      if (this.beforeSeekableWindow_(seekable, currentTime)) {
+        var livePoint = seekable.end(seekable.length - 1);
+
+        this.logger_('Fell out of live window at time ' + currentTime + '. Seeking to ' + ('live point (seekable end) ' + livePoint));
+        this.cancelTimer_();
+        this.tech_.setCurrentTime(livePoint);
+
+        // live window resyncs may be useful for monitoring QoS
+        this.tech_.trigger({type: 'usage', name: 'hls-live-resync'});
+        return true;
+      }
+
+      var buffered = this.tech_.buffered();
+      var nextRange = findNextRange(buffered, currentTime);
+
+      if (this.videoUnderflow_(nextRange, buffered, currentTime)) {
+        // Even though the video underflowed and was stuck in a gap, the audio overplayed
+        // the gap, leading currentTime into a buffered range. Seeking to currentTime
+        // allows the video to catch up to the audio position without losing any audio
+        // (only suffering ~3 seconds of frozen video and a pause in audio playback).
+        this.cancelTimer_();
+        this.tech_.setCurrentTime(currentTime);
+
+        // video underflow may be useful for monitoring QoS
+        this.tech_.trigger({type: 'usage', name: 'hls-video-underflow'});
+        return true;
+      }
+
+      // check for gap
+      if (nextRange.length > 0) {
+        var difference = nextRange.start(0) - currentTime;
+
+        this.logger_('Stopped at ' + currentTime + ', setting timer for ' + difference + ', seeking ' + ('to ' + nextRange.start(0)));
+
+        this.timer_ = setTimeout(this.skipTheGap_.bind(this), difference * 1000, currentTime);
+        return true;
+      }
+
+      // All checks failed. Returning false to indicate failure to correct waiting
+      return false;
+    };
+
+    PlaybackWatcher.prototype.afterSeekableWindow_ = function afterSeekableWindow_(seekable, currentTime) {
+      if (!seekable.length) {
+        // we can't make a solid case if there's no seekable, default to false
+        return false;
+      }
+
+      if (currentTime > seekable.end(seekable.length - 1) + SAFE_TIME_DELTA) {
+        return true;
+      }
+
+      return false;
+    };
+
+    PlaybackWatcher.prototype.beforeSeekableWindow_ = function beforeSeekableWindow_(seekable, currentTime) {
+      if (seekable.length &&
+        // can't fall before 0 and 0 seekable start identifies VOD stream
+        seekable.start(0) > 0 && currentTime < seekable.start(0) - SAFE_TIME_DELTA) {
+        return true;
+      }
+
+      return false;
+    };
+
+    PlaybackWatcher.prototype.videoUnderflow_ = function videoUnderflow_(nextRange, buffered, currentTime) {
+      if (nextRange.length === 0) {
+        // Even if there is no available next range, there is still a possibility we are
+        // stuck in a gap due to video underflow.
+        var gap = this.gapFromVideoUnderflow_(buffered, currentTime);
+
+        if (gap) {
+          this.logger_('Encountered a gap in video from ' + gap.start + ' to ' + gap.end + '. ' + ('Seeking to current time ' + currentTime));
+
+          return true;
+        }
+      }
+
+      return false;
+    };
+
+    /**
+     * Timer callback. If playback still has not proceeded, then we seek
+     * to the start of the next buffered region.
+     *
+     * @private
+     */
+
+
+    PlaybackWatcher.prototype.skipTheGap_ = function skipTheGap_(scheduledCurrentTime) {
+      var buffered = this.tech_.buffered();
+      var currentTime = this.tech_.currentTime();
+      var nextRange = findNextRange(buffered, currentTime);
+
+      this.cancelTimer_();
+
+      if (nextRange.length === 0 || currentTime !== scheduledCurrentTime) {
+        return;
+      }
+
+      this.logger_('skipTheGap_:', 'currentTime:', currentTime, 'scheduled currentTime:', scheduledCurrentTime, 'nextRange start:', nextRange.start(0));
+
+      // only seek if we still have not played
+      this.tech_.setCurrentTime(nextRange.start(0) + TIME_FUDGE_FACTOR);
+
+      this.tech_.trigger({type: 'usage', name: 'hls-gap-skip'});
+    };
+
+    PlaybackWatcher.prototype.gapFromVideoUnderflow_ = function gapFromVideoUnderflow_(buffered, currentTime) {
+      // At least in Chrome, if there is a gap in the video buffer, the audio will continue
+      // playing for ~3 seconds after the video gap starts. This is done to account for
+      // video buffer underflow/underrun (note that this is not done when there is audio
+      // buffer underflow/underrun -- in that case the video will stop as soon as it
+      // encounters the gap, as audio stalls are more noticeable/jarring to a user than
+      // video stalls). The player's time will reflect the playthrough of audio, so the
+      // time will appear as if we are in a buffered region, even if we are stuck in a
+      // "gap."
+      //
+      // Example:
+      // video buffer:   0 => 10.1, 10.2 => 20
+      // audio buffer:   0 => 20
+      // overall buffer: 0 => 10.1, 10.2 => 20
+      // current time: 13
+      //
+      // Chrome's video froze at 10 seconds, where the video buffer encountered the gap,
+      // however, the audio continued playing until it reached ~3 seconds past the gap
+      // (13 seconds), at which point it stops as well. Since current time is past the
+      // gap, findNextRange will return no ranges.
+      //
+      // To check for this issue, we see if there is a gap that starts somewhere within
+      // a 3 second range (3 seconds +/- 1 second) back from our current time.
+      var gaps = findGaps(buffered);
+
+      for (var i = 0; i < gaps.length; i++) {
+        var start = gaps.start(i);
+        var end = gaps.end(i);
+
+        // gap is starts no more than 4 seconds back
+        if (currentTime - start < 4 && currentTime - start > 2) {
+          return {
+            start: start,
+            end: end
+          };
+        }
+      }
+
+      return null;
+    };
+
+    return PlaybackWatcher;
+  }();
+
+  var defaultOptions = {
+    errorInterval: 30,
+    getSource: function getSource(next) {
+      var tech = this.tech({IWillNotUseThisInPlugins: true});
+      var sourceObj = tech.currentSource_;
+
+      return next(sourceObj);
+    }
+  };
+
+  /**
+   * Main entry point for the plugin
+   *
+   * @param {Player} player a reference to a videojs Player instance
+   * @param {Object} [options] an object with plugin options
+   * @private
+   */
+  var initPlugin = function initPlugin(player, options) {
+    var lastCalled = 0;
+    var seekTo = 0;
+    var localOptions = videojs$1.mergeOptions(defaultOptions, options);
+
+    player.ready(function () {
+      player.trigger({type: 'usage', name: 'hls-error-reload-initialized'});
+    });
+
+    /**
+     * Player modifications to perform that must wait until `loadedmetadata`
+     * has been triggered
+     *
+     * @private
+     */
+    var loadedMetadataHandler = function loadedMetadataHandler() {
+      if (seekTo) {
+        player.currentTime(seekTo);
+      }
+    };
+
+    /**
+     * Set the source on the player element, playOld, and seek if necessary
+     *
+     * @param {Object} sourceObj An object specifying the source url and mime-type to playOld
+     * @private
+     */
+    var setSource = function setSource(sourceObj) {
+      if (sourceObj === null || sourceObj === undefined) {
+        return;
+      }
+      seekTo = player.duration() !== Infinity && player.currentTime() || 0;
+
+      player.one('loadedmetadata', loadedMetadataHandler);
+
+      player.src(sourceObj);
+      player.trigger({type: 'usage', name: 'hls-error-reload'});
+      player.play();
+    };
+
+    /**
+     * Attempt to get a source from either the built-in getSource function
+     * or a custom function provided via the options
+     *
+     * @private
+     */
+    var errorHandler = function errorHandler() {
+      // Do not attempt to reload the source if a source-reload occurred before
+      // 'errorInterval' time has elapsed since the last source-reload
+      if (Date.now() - lastCalled < localOptions.errorInterval * 1000) {
+        player.trigger({type: 'usage', name: 'hls-error-reload-canceled'});
+        return;
+      }
+
+      if (!localOptions.getSource || typeof localOptions.getSource !== 'function') {
+        videojs$1.log.error('ERROR: reloadSourceOnError - The option getSource must be a function!');
+        return;
+      }
+      lastCalled = Date.now();
+
+      return localOptions.getSource.call(player, setSource);
+    };
+
+    /**
+     * Unbind any event handlers that were bound by the plugin
+     *
+     * @private
+     */
+    var cleanupEvents = function cleanupEvents() {
+      player.off('loadedmetadata', loadedMetadataHandler);
+      player.off('error', errorHandler);
+      player.off('dispose', cleanupEvents);
+    };
+
+    /**
+     * Cleanup before re-initializing the plugin
+     *
+     * @param {Object} [newOptions] an object with plugin options
+     * @private
+     */
+    var reinitPlugin = function reinitPlugin(newOptions) {
+      cleanupEvents();
+      initPlugin(player, newOptions);
+    };
+
+    player.on('error', errorHandler);
+    player.on('dispose', cleanupEvents);
+
+    // Overwrite the plugin function so that we can correctly cleanup before
+    // initializing the plugin
+    player.reloadSourceOnError = reinitPlugin;
+  };
+
+  /**
+   * Reload the source when an error is detected as long as there
+   * wasn't an error previously within the last 30 seconds
+   *
+   * @param {Object} [options] an object with plugin options
+   */
+  var reloadSourceOnError = function reloadSourceOnError(options) {
+    initPlugin(this, options);
+  };
+
+  var version$3 = "1.0.0";
+
+  /**
+   * @file videojs-http-streaming.js
+   *
+   * The main file for the HLS project.
+   * License: https://github.com/videojs/videojs-http-streaming/blob/master/LICENSE
+   */
+
+  var Hls$1 = {
+    PlaylistLoader: PlaylistLoader,
+    Playlist: Playlist,
+    Decrypter: Decrypter,
+    AsyncStream: AsyncStream,
+    decrypt: decrypt,
+    utils: utils,
+
+    STANDARD_PLAYLIST_SELECTOR: lastBandwidthSelector,
+    INITIAL_PLAYLIST_SELECTOR: lowestBitrateCompatibleVariantSelector,
+    comparePlaylistBandwidth: comparePlaylistBandwidth,
+    comparePlaylistResolution: comparePlaylistResolution,
+
+    xhr: xhrFactory()
+  };
+
+  // 0.5 MB/s
+  var INITIAL_BANDWIDTH = 4194304;
+
+  // Define getter/setters for config properites
+  ['GOAL_BUFFER_LENGTH', 'MAX_GOAL_BUFFER_LENGTH', 'GOAL_BUFFER_LENGTH_RATE', 'BUFFER_LOW_WATER_LINE', 'MAX_BUFFER_LOW_WATER_LINE', 'BUFFER_LOW_WATER_LINE_RATE', 'BANDWIDTH_VARIANCE'].forEach(function (prop) {
+    Object.defineProperty(Hls$1, prop, {
+      get: function get$$1() {
+        videojs$1.log.warn('using Hls.' + prop + ' is UNSAFE be sure you know what you are doing');
+        return Config[prop];
+      },
+      set: function set$$1(value) {
+        videojs$1.log.warn('using Hls.' + prop + ' is UNSAFE be sure you know what you are doing');
+
+        if (typeof value !== 'number' || value < 0) {
+          videojs$1.log.warn('value of Hls.' + prop + ' must be greater than or equal to 0');
+          return;
+        }
+
+        Config[prop] = value;
+      }
+    });
+  });
+
+  var simpleTypeFromSourceType = function simpleTypeFromSourceType(type) {
+    var mpegurlRE = /^(audio|video|application)\/(x-|vnd\.apple\.)?mpegurl/i;
+
+    if (mpegurlRE.test(type)) {
+      return 'hls';
+    }
+
+    var dashRE = /^application\/dash\+xml/i;
+
+    if (dashRE.test(type)) {
+      return 'dash';
+    }
+
+    return null;
+  };
+
+  /**
+   * Updates the selectedIndex of the QualityLevelList when a mediachange happens in hls.
+   *
+   * @param {QualityLevelList} qualityLevels The QualityLevelList to update.
+   * @param {PlaylistLoader} playlistLoader PlaylistLoader containing the new media info.
+   * @function handleHlsMediaChange
+   */
+  var handleHlsMediaChange = function handleHlsMediaChange(qualityLevels, playlistLoader) {
+    var newPlaylist = playlistLoader.media();
+    var selectedIndex = -1;
+
+    for (var i = 0; i < qualityLevels.length; i++) {
+      if (qualityLevels[i].id === newPlaylist.uri) {
+        selectedIndex = i;
+        break;
+      }
+    }
+
+    qualityLevels.selectedIndex_ = selectedIndex;
+    qualityLevels.trigger({
+      selectedIndex: selectedIndex,
+      type: 'change'
+    });
+  };
+
+  /**
+   * Adds quality levels to list once playlist metadata is available
+   *
+   * @param {QualityLevelList} qualityLevels The QualityLevelList to attach events to.
+   * @param {Object} hls Hls object to listen to for media events.
+   * @function handleHlsLoadedMetadata
+   */
+  var handleHlsLoadedMetadata = function handleHlsLoadedMetadata(qualityLevels, hls) {
+    hls.representations().forEach(function (rep) {
+      qualityLevels.addQualityLevel(rep);
+    });
+    handleHlsMediaChange(qualityLevels, hls.playlists);
+  };
+
+  // HLS is a source handler, not a tech. Make sure attempts to use it
+  // as one do not cause exceptions.
+  Hls$1.canPlaySource = function () {
+    return videojs$1.log.warn('HLS is no longer a tech. Please remove it from ' + 'your player\'s techOrder.');
+  };
+
+  var emeKeySystems = function emeKeySystems(keySystemOptions, videoPlaylist, audioPlaylist) {
+    if (!keySystemOptions) {
+      return keySystemOptions;
+    }
+
+    // upsert the content types based on the selected playlist
+    var keySystemContentTypes = {};
+
+    for (var keySystem in keySystemOptions) {
+      keySystemContentTypes[keySystem] = {
+        audioContentType: 'audio/mp4; codecs="' + audioPlaylist.attributes.CODECS + '"',
+        videoContentType: 'video/mp4; codecs="' + videoPlaylist.attributes.CODECS + '"'
+      };
+
+      if (videoPlaylist.contentProtection && videoPlaylist.contentProtection[keySystem] && videoPlaylist.contentProtection[keySystem].pssh) {
+        keySystemContentTypes[keySystem].pssh = videoPlaylist.contentProtection[keySystem].pssh;
+      }
+
+      // videojs-contrib-eme accepts the option of specifying: 'com.some.cdm': 'url'
+      // so we need to prevent overwriting the URL entirely
+      if (typeof keySystemOptions[keySystem] === 'string') {
+        keySystemContentTypes[keySystem].url = keySystemOptions[keySystem];
+      }
+    }
+
+    return videojs$1.mergeOptions(keySystemOptions, keySystemContentTypes);
+  };
+
+  var setupEmeOptions = function setupEmeOptions(hlsHandler) {
+    if (hlsHandler.options_.sourceType !== 'dash') {
+      return;
+    }
+    var player = videojs$1.players[hlsHandler.tech_.options_.playerId];
+
+    if (player.eme) {
+      var sourceOptions = emeKeySystems(hlsHandler.source_.keySystems, hlsHandler.playlists.media(), hlsHandler.masterPlaylistController_.mediaTypes_.AUDIO.activePlaylistLoader.media());
+
+      if (sourceOptions) {
+        player.currentSource().keySystems = sourceOptions;
+      }
+    }
+  };
+
+  /**
+   * Whether the browser has built-in HLS support.
+   */
+  Hls$1.supportsNativeHls = function () {
+    var video = document_1.createElement('video');
+
+    // native HLS is definitely not supported if HTML5 video isn't
+    if (!videojs$1.getTech('Html5').isSupported()) {
+      return false;
+    }
+
+    // HLS manifests can go by many mime-types
+    var canPlay = [
+      // Apple santioned
+      'application/vnd.apple.mpegurl',
+      // Apple sanctioned for backwards compatibility
+      'audio/mpegurl',
+      // Very common
+      'audio/x-mpegurl',
+      // Very common
+      'application/x-mpegurl',
+      // Included for completeness
+      'video/x-mpegurl', 'video/mpegurl', 'application/mpegurl'];
+
+    return canPlay.some(function (canItPlay) {
+      return (/maybe|probably/i.test(video.canPlayType(canItPlay))
+      );
+    });
+  }();
+
+  Hls$1.supportsNativeDash = function () {
+    if (!videojs$1.getTech('Html5').isSupported()) {
+      return false;
+    }
+
+    return (/maybe|probably/i.test(document_1.createElement('video').canPlayType('application/dash+xml'))
+    );
+  }();
+
+  Hls$1.supportsTypeNatively = function (type) {
+    if (type === 'hls') {
+      return Hls$1.supportsNativeHls;
+    }
+
+    if (type === 'dash') {
+      return Hls$1.supportsNativeDash;
+    }
+
+    return false;
+  };
+
+  /**
+   * HLS is a source handler, not a tech. Make sure attempts to use it
+   * as one do not cause exceptions.
+   */
+  Hls$1.isSupported = function () {
+    return videojs$1.log.warn('HLS is no longer a tech. Please remove it from ' + 'your player\'s techOrder.');
+  };
+
+  var Component$1 = videojs$1.getComponent('Component');
+
+  /**
+   * The Hls Handler object, where we orchestrate all of the parts
+   * of HLS to interact with video.js
+   *
+   * @class HlsHandler
+   * @extends videojs.Component
+   * @param {Object} source the soruce object
+   * @param {Tech} tech the parent tech object
+   * @param {Object} options optional and required options
+   */
+
+  var HlsHandler = function (_Component) {
+    inherits(HlsHandler, _Component);
+
+    function HlsHandler(source, tech, options) {
+      classCallCheck(this, HlsHandler);
+
+      // tech.player() is deprecated but setup a reference to HLS for
+      // backwards-compatibility
+      var _this36 = possibleConstructorReturn(this, _Component.call(this, tech, options.hls));
+
+      if (tech.options_ && tech.options_.playerId) {
+        var _player = videojs$1(tech.options_.playerId);
+
+        if (!_player.hasOwnProperty('hls')) {
+          Object.defineProperty(_player, 'hls', {
+            get: function get$$1() {
+              videojs$1.log.warn('player.hls is deprecated. Use player.tech_.hls instead.');
+              tech.trigger({type: 'usage', name: 'hls-player-access'});
+              return _this36;
+            }
+          });
+        }
+
+        // Set up a reference to the HlsHandler from player.vhs. This allows users to start
+        // migrating from player.tech_.hls... to player.vhs... for API access. Although this
+        // isn't the most appropriate form of reference for video.js (since all APIs should
+        // be provided through core video.js), it is a common pattern for plugins, and vhs
+        // will act accordingly.
+        _player.vhs = _this36;
+        // deprecated, for backwards compatibility
+        _player.dash = _this36;
+      }
+
+      _this36.tech_ = tech;
+      _this36.source_ = source;
+      _this36.stats = {};
+      _this36.ignoreNextSeekingEvent_ = false;
+      _this36.setOptions_();
+
+      // overriding native HLS only works if audio tracks have been emulated
+      // error early if we're misconfigured:
+      if (_this36.options_.overrideNative && (tech.featuresNativeVideoTracks || tech.featuresNativeAudioTracks)) {
+        throw new Error('Overriding native HLS requires emulated tracks. ' + 'See https://git.io/vMpjB');
+      }
+
+      // listen for fullscreenchange events for this player so that we
+      // can adjust our quality selection quickly
+      _this36.on(document_1, ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange'], function (event) {
+        var fullscreenElement = document_1.fullscreenElement || document_1.webkitFullscreenElement || document_1.mozFullScreenElement || document_1.msFullscreenElement;
+
+        if (fullscreenElement && fullscreenElement.contains(_this36.tech_.el())) {
+          _this36.masterPlaylistController_.fastQualityChange_();
+        }
+      });
+
+      _this36.on(_this36.tech_, 'seeking', function () {
+        if (this.ignoreNextSeekingEvent_) {
+          this.ignoreNextSeekingEvent_ = false;
+          return;
+        }
+
+        this.setCurrentTime(this.tech_.currentTime());
+      });
+      _this36.on(_this36.tech_, 'error', function () {
+        if (this.masterPlaylistController_) {
+          this.masterPlaylistController_.pauseLoading();
+        }
+      });
+
+      _this36.on(_this36.tech_, 'play', _this36.play);
+      return _this36;
+    }
+
+    HlsHandler.prototype.setOptions_ = function setOptions_() {
+      var _this37 = this;
+
+      // defaults
+      this.options_.withCredentials = this.options_.withCredentials || false;
+
+      if (typeof this.options_.blacklistDuration !== 'number') {
+        this.options_.blacklistDuration = 5 * 60;
+      }
+
+      // start playlist selection at a reasonable bandwidth for
+      // broadband internet (0.5 MB/s) or mobile (0.0625 MB/s)
+      if (typeof this.options_.bandwidth !== 'number') {
+        this.options_.bandwidth = INITIAL_BANDWIDTH;
+      }
+
+      // If the bandwidth number is unchanged from the initial setting
+      // then this takes precedence over the enableLowInitialPlaylist option
+      this.options_.enableLowInitialPlaylist = this.options_.enableLowInitialPlaylist && this.options_.bandwidth === INITIAL_BANDWIDTH;
+
+      // grab options passed to player.src
+      ['withCredentials', 'bandwidth'].forEach(function (option) {
+        if (typeof _this37.source_[option] !== 'undefined') {
+          _this37.options_[option] = _this37.source_[option];
+        }
+      });
+
+      this.bandwidth = this.options_.bandwidth;
+    };
+    /**
+     * called when player.src gets called, handle a new source
+     *
+     * @param {Object} src the source object to handle
+     */
+
+
+    HlsHandler.prototype.src = function src(_src, type) {
+      var _this38 = this;
+
+      // do nothing if the src is falsey
+      if (!_src) {
+        return;
+      }
+      this.setOptions_();
+      // add master playlist controller options
+      this.options_.url = this.source_.src;
+      this.options_.tech = this.tech_;
+      this.options_.externHls = Hls$1;
+      this.options_.sourceType = simpleTypeFromSourceType(type);
+
+      this.masterPlaylistController_ = new MasterPlaylistController(this.options_);
+      this.playbackWatcher_ = new PlaybackWatcher(videojs$1.mergeOptions(this.options_, {
+        seekable: function seekable() {
+          return _this38.seekable();
+        }
+      }));
+
+      this.masterPlaylistController_.on('error', function () {
+        var player = videojs$1.players[_this38.tech_.options_.playerId];
+
+        player.error(_this38.masterPlaylistController_.error);
+      });
+
+      // `this` in selectPlaylist should be the HlsHandler for backwards
+      // compatibility with < v2
+      this.masterPlaylistController_.selectPlaylist = this.selectPlaylist ? this.selectPlaylist.bind(this) : Hls$1.STANDARD_PLAYLIST_SELECTOR.bind(this);
+
+      this.masterPlaylistController_.selectInitialPlaylist = Hls$1.INITIAL_PLAYLIST_SELECTOR.bind(this);
+
+      // re-expose some internal objects for backwards compatibility with < v2
+      this.playlists = this.masterPlaylistController_.masterPlaylistLoader_;
+      this.mediaSource = this.masterPlaylistController_.mediaSource;
+
+      // Proxy assignment of some properties to the master playlist
+      // controller. Using a custom property for backwards compatibility
+      // with < v2
+      Object.defineProperties(this, {
+        selectPlaylist: {
+          get: function get$$1() {
+            return this.masterPlaylistController_.selectPlaylist;
+          },
+          set: function set$$1(selectPlaylist) {
+            this.masterPlaylistController_.selectPlaylist = selectPlaylist.bind(this);
+          }
+        },
+        throughput: {
+          get: function get$$1() {
+            return this.masterPlaylistController_.mainSegmentLoader_.throughput.rate;
+          },
+          set: function set$$1(throughput) {
+            this.masterPlaylistController_.mainSegmentLoader_.throughput.rate = throughput;
+            // By setting `count` to 1 the throughput value becomes the starting value
+            // for the cumulative average
+            this.masterPlaylistController_.mainSegmentLoader_.throughput.count = 1;
+          }
+        },
+        bandwidth: {
+          get: function get$$1() {
+            return this.masterPlaylistController_.mainSegmentLoader_.bandwidth;
+          },
+          set: function set$$1(bandwidth) {
+            this.masterPlaylistController_.mainSegmentLoader_.bandwidth = bandwidth;
+            // setting the bandwidth manually resets the throughput counter
+            // `count` is set to zero that current value of `rate` isn't included
+            // in the cumulative average
+            this.masterPlaylistController_.mainSegmentLoader_.throughput = {
+              rate: 0,
+              count: 0
+            };
+          }
+        },
+        /**
+         * `systemBandwidth` is a combination of two serial processes bit-rates. The first
+         * is the network bitrate provided by `bandwidth` and the second is the bitrate of
+         * the entire process after that - decryption, transmuxing, and appending - provided
+         * by `throughput`.
+         *
+         * Since the two process are serial, the overall system bandwidth is given by:
+         *   sysBandwidth = 1 / (1 / bandwidth + 1 / throughput)
+         */
+        systemBandwidth: {
+          get: function get$$1() {
+            var invBandwidth = 1 / (this.bandwidth || 1);
+            var invThroughput = void 0;
+
+            if (this.throughput > 0) {
+              invThroughput = 1 / this.throughput;
+            } else {
+              invThroughput = 0;
+            }
+
+            var systemBitrate = Math.floor(1 / (invBandwidth + invThroughput));
+
+            return systemBitrate;
+          },
+          set: function set$$1() {
+            videojs$1.log.error('The "systemBandwidth" property is read-only');
+          }
+        }
+      });
+
+      Object.defineProperties(this.stats, {
+        bandwidth: {
+          get: function get$$1() {
+            return _this38.bandwidth || 0;
+          },
+          enumerable: true
+        },
+        mediaRequests: {
+          get: function get$$1() {
+            return _this38.masterPlaylistController_.mediaRequests_() || 0;
+          },
+          enumerable: true
+        },
+        mediaRequestsAborted: {
+          get: function get$$1() {
+            return _this38.masterPlaylistController_.mediaRequestsAborted_() || 0;
+          },
+          enumerable: true
+        },
+        mediaRequestsTimedout: {
+          get: function get$$1() {
+            return _this38.masterPlaylistController_.mediaRequestsTimedout_() || 0;
+          },
+          enumerable: true
+        },
+        mediaRequestsErrored: {
+          get: function get$$1() {
+            return _this38.masterPlaylistController_.mediaRequestsErrored_() || 0;
+          },
+          enumerable: true
+        },
+        mediaTransferDuration: {
+          get: function get$$1() {
+            return _this38.masterPlaylistController_.mediaTransferDuration_() || 0;
+          },
+          enumerable: true
+        },
+        mediaBytesTransferred: {
+          get: function get$$1() {
+            return _this38.masterPlaylistController_.mediaBytesTransferred_() || 0;
+          },
+          enumerable: true
+        },
+        mediaSecondsLoaded: {
+          get: function get$$1() {
+            return _this38.masterPlaylistController_.mediaSecondsLoaded_() || 0;
+          },
+          enumerable: true
+        },
+        buffered: {
+          get: function get$$1() {
+            return timeRangesToArray(_this38.tech_.buffered());
+          },
+          enumerable: true
+        },
+        currentTime: {
+          get: function get$$1() {
+            return _this38.tech_.currentTime();
+          },
+          enumerable: true
+        },
+        currentSource: {
+          get: function get$$1() {
+            return _this38.tech_.currentSource_;
+          },
+          enumerable: true
+        },
+        currentTech: {
+          get: function get$$1() {
+            return _this38.tech_.name_;
+          },
+          enumerable: true
+        },
+        duration: {
+          get: function get$$1() {
+            return _this38.tech_.duration();
+          },
+          enumerable: true
+        },
+        master: {
+          get: function get$$1() {
+            return _this38.playlists.master;
+          },
+          enumerable: true
+        },
+        playerDimensions: {
+          get: function get$$1() {
+            return _this38.tech_.currentDimensions();
+          },
+          enumerable: true
+        },
+        seekable: {
+          get: function get$$1() {
+            return timeRangesToArray(_this38.tech_.seekable());
+          },
+          enumerable: true
+        },
+        timestamp: {
+          get: function get$$1() {
+            return Date.now();
+          },
+          enumerable: true
+        },
+        videoPlaybackQuality: {
+          get: function get$$1() {
+            return _this38.tech_.getVideoPlaybackQuality();
+          },
+          enumerable: true
+        }
+      });
+
+      this.tech_.one('canplay', this.masterPlaylistController_.setupFirstPlay.bind(this.masterPlaylistController_));
+
+      this.masterPlaylistController_.on('selectedinitialmedia', function () {
+        // Add the manual rendition mix-in to HlsHandler
+        renditionSelectionMixin(_this38);
+        setupEmeOptions(_this38);
+      });
+
+      // the bandwidth of the primary segment loader is our best
+      // estimate of overall bandwidth
+      this.on(this.masterPlaylistController_, 'progress', function () {
+        this.tech_.trigger('progress');
+      });
+
+      // In the live case, we need to ignore the very first `seeking` event since
+      // that will be the result of the seek-to-live behavior
+      this.on(this.masterPlaylistController_, 'firstplay', function () {
+        this.ignoreNextSeekingEvent_ = true;
+      });
+
+      this.tech_.ready(function () {
+        return _this38.setupQualityLevels_();
+      });
+
+      // do nothing if the tech has been disposed already
+      // this can occur if someone sets the src in player.ready(), for instance
+      if (!this.tech_.el()) {
+        return;
+      }
+
+      this.tech_.src(videojs$1.URL.createObjectURL(this.masterPlaylistController_.mediaSource));
+    };
+
+    /**
+     * Initializes the quality levels and sets listeners to update them.
+     *
+     * @method setupQualityLevels_
+     * @private
+     */
+
+
+    HlsHandler.prototype.setupQualityLevels_ = function setupQualityLevels_() {
+      var _this39 = this;
+
+      var player = videojs$1.players[this.tech_.options_.playerId];
+
+      if (player && player.qualityLevels) {
+        this.qualityLevels_ = player.qualityLevels();
+
+        this.masterPlaylistController_.on('selectedinitialmedia', function () {
+          handleHlsLoadedMetadata(_this39.qualityLevels_, _this39);
+        });
+
+        this.playlists.on('mediachange', function () {
+          handleHlsMediaChange(_this39.qualityLevels_, _this39.playlists);
+        });
+      }
+    };
+
+    /**
+     * Begin playing the video.
+     */
+
+
+    HlsHandler.prototype.play = function play() {
+      this.masterPlaylistController_.play();
+    };
+
+    /**
+     * a wrapper around the function in MasterPlaylistController
+     */
+
+
+    HlsHandler.prototype.setCurrentTime = function setCurrentTime(currentTime) {
+      this.masterPlaylistController_.setCurrentTime(currentTime);
+    };
+
+    /**
+     * a wrapper around the function in MasterPlaylistController
+     */
+
+
+    HlsHandler.prototype.duration = function duration() {
+      return this.masterPlaylistController_.duration();
+    };
+
+    /**
+     * a wrapper around the function in MasterPlaylistController
+     */
+
+
+    HlsHandler.prototype.seekable = function seekable() {
+      return this.masterPlaylistController_.seekable();
+    };
+
+    /**
+     * Abort all outstanding work and cleanup.
+     */
+
+
+    HlsHandler.prototype.dispose = function dispose() {
+      if (this.playbackWatcher_) {
+        this.playbackWatcher_.dispose();
+      }
+      if (this.masterPlaylistController_) {
+        this.masterPlaylistController_.dispose();
+      }
+      if (this.qualityLevels_) {
+        this.qualityLevels_.dispose();
+      }
+      _Component.prototype.dispose.call(this);
+    };
+
+    return HlsHandler;
+  }(Component$1);
+
+  /**
+   * The Source Handler object, which informs video.js what additional
+   * MIME types are supported and sets up playback. It is registered
+   * automatically to the appropriate tech based on the capabilities of
+   * the browser it is running in. It is not necessary to use or modify
+   * this object in normal usage.
+   */
+
+
+  var HlsSourceHandler = {
+    name: 'videojs-http-streaming',
+    VERSION: version$3,
+    canHandleSource: function canHandleSource(srcObj) {
+      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+
+      var localOptions = videojs$1.mergeOptions(videojs$1.options, options);
+
+      return HlsSourceHandler.canPlayType(srcObj.type, localOptions);
+    },
+    handleSource: function handleSource(source, tech) {
+      var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
+
+      var localOptions = videojs$1.mergeOptions(videojs$1.options, options);
+
+      tech.hls = new HlsHandler(source, tech, localOptions);
+      tech.hls.xhr = xhrFactory();
+
+      tech.hls.src(source.src, source.type);
+      return tech.hls;
+    },
+    canPlayType: function canPlayType(type) {
+      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+
+      var _videojs$mergeOptions = videojs$1.mergeOptions(videojs$1.options, options),
+        overrideNative = _videojs$mergeOptions.hls.overrideNative;
+
+      var supportedType = simpleTypeFromSourceType(type);
+      var canUseMsePlayback = supportedType && (!Hls$1.supportsTypeNatively(supportedType) || overrideNative);
+
+      return canUseMsePlayback ? 'maybe' : '';
+    }
+  };
+
+  if (typeof videojs$1.MediaSource === 'undefined' || typeof videojs$1.URL === 'undefined') {
+    videojs$1.MediaSource = MediaSource;
+    videojs$1.URL = URL$1;
+  }
+
+  // register source handlers with the appropriate techs
+  if (MediaSource.supportsNativeMediaSources()) {
+    videojs$1.getTech('Html5').registerSourceHandler(HlsSourceHandler, 0);
+  }
+
+  videojs$1.HlsHandler = HlsHandler;
+  videojs$1.HlsSourceHandler = HlsSourceHandler;
+  videojs$1.Hls = Hls$1;
+  if (!videojs$1.use) {
+    videojs$1.registerComponent('Hls', Hls$1);
+  }
+  videojs$1.options.hls = videojs$1.options.hls || {};
+
+  if (videojs$1.registerPlugin) {
+    videojs$1.registerPlugin('reloadSourceOnError', reloadSourceOnError);
+  } else {
+    videojs$1.plugin('reloadSourceOnError', reloadSourceOnError);
+  }
+
+  return videojs$1;
+
+})));
diff --git a/form/src/assets/libs/video-js-7.0.0/video.min.js b/form/src/assets/libs/video-js-7.0.0/video.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..5d05e6c367da32195e17120c2ca8742682025e31
--- /dev/null
+++ b/form/src/assets/libs/video-js-7.0.0/video.min.js
@@ -0,0 +1,12 @@
+/**
+ * @license
+ * Video.js 7.0.0 <http://videojs.com/>
+ * Copyright Brightcove, Inc. <https://www.brightcove.com/>
+ * Available under Apache License Version 2.0
+ * <https://github.com/videojs/video.js/blob/master/LICENSE>
+ *
+ * Includes vtt.js <https://github.com/mozilla/vtt.js>
+ * Available under Apache License Version 2.0
+ * <https://github.com/mozilla/vtt.js/blob/master/LICENSE>
+ */
+!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.videojs=e()}(this,function(){var d="7.0.0",t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function e(t,e){return t(e={exports:{}},e.exports),e.exports}var i,g="undefined"!=typeof window?window:"undefined"!=typeof t?t:"undefined"!=typeof self?self:{},r={},n=Object.freeze({default:r}),s=n&&r||n,a="undefined"!=typeof t?t:"undefined"!=typeof window?window:{};"undefined"!=typeof document?i=document:(i=a["__GLOBAL_DOCUMENT_CACHE@4"])||(i=a["__GLOBAL_DOCUMENT_CACHE@4"]=s);var p=i,o=void 0,u="info",l=[],c=function(t,e){var i=o.levels[u],r=new RegExp("^("+i+")$");if("log"!==t&&e.unshift(t.toUpperCase()+":"),l&&l.push([].concat(e)),e.unshift("VIDEOJS:"),g.console){var n=g.console[t];n||"debug"!==t||(n=g.console.info||g.console.log),n&&i&&r.test(t)&&n[Array.isArray(e)?"apply":"call"](g.console,e)}};(o=function(){for(var t=arguments.length,e=Array(t),i=0;i<t;i++)e[i]=arguments[i];c("log",e)}).levels={all:"debug|log|warn|error",off:"",debug:"debug|log|warn|error",info:"log|warn|error",warn:"warn|error",error:"error",DEFAULT:u},o.level=function(t){if("string"==typeof t){if(!o.levels.hasOwnProperty(t))throw new Error('"'+t+'" in not a valid log level');u=t}return u},o.history=function(){return l?[].concat(l):[]},o.history.clear=function(){l&&(l.length=0)},o.history.disable=function(){null!==l&&(l.length=0,l=null)},o.history.enable=function(){null===l&&(l=[])},o.error=function(){for(var t=arguments.length,e=Array(t),i=0;i<t;i++)e[i]=arguments[i];return c("error",e)},o.warn=function(){for(var t=arguments.length,e=Array(t),i=0;i<t;i++)e[i]=arguments[i];return c("warn",e)},o.debug=function(){for(var t=arguments.length,e=Array(t),i=0;i<t;i++)e[i]=arguments[i];return c("debug",e)};var f=o;var m=function(t){for(var e="",i=0;i<arguments.length;i++)e+=t[i].replace(/\n\r?\s*/g,"")+(arguments[i+1]||"");return e},v="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},y=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},_=function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)},b=function(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e},h=function(t,e){return t.raw=e,t},T=Object.prototype.toString,S=function(t){return E(t)?Object.keys(t):[]};function k(e,i){S(e).forEach(function(t){return i(e[t],t)})}function C(i){for(var t=arguments.length,e=Array(1<t?t-1:0),r=1;r<t;r++)e[r-1]=arguments[r];return Object.assign?Object.assign.apply(Object,[i].concat(e)):(e.forEach(function(t){t&&k(t,function(t,e){i[e]=t})}),i)}function E(t){return!!t&&"object"===("undefined"==typeof t?"undefined":v(t))}function w(t){return E(t)&&"[object Object]"===T.call(t)&&t.constructor===Object}function A(t,e){if(!t||!e)return"";if("function"==typeof g.getComputedStyle){var i=g.getComputedStyle(t);return i?i[e]:""}return""}var L=h(["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 ","."],["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 ","."]);function P(t){return"string"==typeof t&&/\S/.test(t)}function O(t){if(/\s/.test(t))throw new Error("class has illegal whitespace characters")}function x(){return p===g.document}function I(t){return E(t)&&1===t.nodeType}function D(){try{return g.parent!==g.self}catch(t){return!0}}function R(r){return function(t,e){if(!P(t))return p[r](null);P(e)&&(e=p.querySelector(e));var i=I(e)?e:p;return i[r]&&i[r](t)}}function M(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:"div",i=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{},e=2<arguments.length&&void 0!==arguments[2]?arguments[2]:{},r=arguments[3],n=p.createElement(t);return Object.getOwnPropertyNames(i).forEach(function(t){var e=i[t];-1!==t.indexOf("aria-")||"role"===t||"type"===t?(f.warn(m(L,t,e)),n.setAttribute(t,e)):"textContent"===t?U(n,e):n[t]=e}),Object.getOwnPropertyNames(e).forEach(function(t){n.setAttribute(t,e[t])}),r&&et(n,r),n}function U(t,e){return"undefined"==typeof t.textContent?t.innerText=e:t.textContent=e,t}function N(t,e){e.firstChild?e.insertBefore(t,e.firstChild):e.appendChild(t)}function B(t,e){return O(e),t.classList?t.classList.contains(e):(i=e,new RegExp("(^|\\s)"+i+"($|\\s)")).test(t.className);var i}function j(t,e){return t.classList?t.classList.add(e):B(t,e)||(t.className=(t.className+" "+e).trim()),t}function F(t,e){return t.classList?t.classList.remove(e):(O(e),t.className=t.className.split(/\s+/).filter(function(t){return t!==e}).join(" ")),t}function H(t,e,i){var r=B(t,e);if("function"==typeof i&&(i=i(t,e)),"boolean"!=typeof i&&(i=!r),i!==r)return i?j(t,e):F(t,e),t}function V(i,r){Object.getOwnPropertyNames(r).forEach(function(t){var e=r[t];null===e||"undefined"==typeof e||!1===e?i.removeAttribute(t):i.setAttribute(t,!0===e?"":e)})}function q(t){var e={},i=",autoplay,controls,playsinline,loop,muted,default,defaultMuted,";if(t&&t.attributes&&0<t.attributes.length)for(var r=t.attributes,n=r.length-1;0<=n;n--){var s=r[n].name,a=r[n].value;"boolean"!=typeof t[s]&&-1===i.indexOf(","+s+",")||(a=null!==a),e[s]=a}return e}function W(t,e){return t.getAttribute(e)}function z(t,e,i){t.setAttribute(e,i)}function G(t,e){t.removeAttribute(e)}function X(){p.body.focus(),p.onselectstart=function(){return!1}}function Y(){p.onselectstart=function(){return!0}}function $(t){if(t&&t.getBoundingClientRect&&t.parentNode){var e=t.getBoundingClientRect(),i={};return["bottom","height","left","right","top","width"].forEach(function(t){void 0!==e[t]&&(i[t]=e[t])}),i.height||(i.height=parseFloat(A(t,"height"))),i.width||(i.width=parseFloat(A(t,"width"))),i}}function K(t){var e=void 0;if(t.getBoundingClientRect&&t.parentNode&&(e=t.getBoundingClientRect()),!e)return{left:0,top:0};var i=p.documentElement,r=p.body,n=i.clientLeft||r.clientLeft||0,s=g.pageXOffset||r.scrollLeft,a=e.left+s-n,o=i.clientTop||r.clientTop||0,u=g.pageYOffset||r.scrollTop,l=e.top+u-o;return{left:Math.round(a),top:Math.round(l)}}function Q(t,e){var i={},r=K(t),n=t.offsetWidth,s=t.offsetHeight,a=r.top,o=r.left,u=e.pageY,l=e.pageX;return e.changedTouches&&(l=e.changedTouches[0].pageX,u=e.changedTouches[0].pageY),i.y=Math.max(0,Math.min(1,(a-u+s)/s)),i.x=Math.max(0,Math.min(1,(l-o)/n)),i}function J(t){return E(t)&&3===t.nodeType}function Z(t){for(;t.firstChild;)t.removeChild(t.firstChild);return t}function tt(t){return"function"==typeof t&&(t=t()),(Array.isArray(t)?t:[t]).map(function(t){return"function"==typeof t&&(t=t()),I(t)||J(t)?t:"string"==typeof t&&/\S/.test(t)?p.createTextNode(t):void 0}).filter(function(t){return t})}function et(e,t){return tt(t).forEach(function(t){return e.appendChild(t)}),e}function it(t,e){return et(Z(t),e)}function rt(t){return void 0===t.button&&void 0===t.buttons||(0===t.button&&void 0===t.buttons||0===t.button&&1===t.buttons)}var nt=R("querySelector"),st=R("querySelectorAll"),at=Object.freeze({isReal:x,isEl:I,isInFrame:D,createEl:M,textContent:U,prependTo:N,hasClass:B,addClass:j,removeClass:F,toggleClass:H,setAttributes:V,getAttributes:q,getAttribute:W,setAttribute:z,removeAttribute:G,blockTextSelection:X,unblockTextSelection:Y,getBoundingClientRect:$,findPosition:K,getPointerPosition:Q,isTextNode:J,emptyEl:Z,normalizeContent:tt,appendContent:et,insertContent:it,isSingleLeftClick:rt,$:nt,$$:st}),ot=1;function ut(){return ot++}var lt={},ct="vdata"+(new Date).getTime();function ht(t){var e=t[ct];return e||(e=t[ct]=ut()),lt[e]||(lt[e]={}),lt[e]}function dt(t){var e=t[ct];return!!e&&!!Object.getOwnPropertyNames(lt[e]).length}function pt(e){var t=e[ct];if(t){delete lt[t];try{delete e[ct]}catch(t){e.removeAttribute?e.removeAttribute(ct):e[ct]=null}}}function ft(t,e){var i=ht(t);0===i.handlers[e].length&&(delete i.handlers[e],t.removeEventListener?t.removeEventListener(e,i.dispatcher,!1):t.detachEvent&&t.detachEvent("on"+e,i.dispatcher)),Object.getOwnPropertyNames(i.handlers).length<=0&&(delete i.handlers,delete i.dispatcher,delete i.disabled),0===Object.getOwnPropertyNames(i).length&&pt(t)}function mt(e,i,t,r){t.forEach(function(t){e(i,t,r)})}function gt(t){function e(){return!0}function i(){return!1}if(!t||!t.isPropagationStopped){var r=t||g.event;for(var n in t={},r)"layerX"!==n&&"layerY"!==n&&"keyLocation"!==n&&"webkitMovementX"!==n&&"webkitMovementY"!==n&&("returnValue"===n&&r.preventDefault||(t[n]=r[n]));if(t.target||(t.target=t.srcElement||p),t.relatedTarget||(t.relatedTarget=t.fromElement===t.target?t.toElement:t.fromElement),t.preventDefault=function(){r.preventDefault&&r.preventDefault(),t.returnValue=!1,r.returnValue=!1,t.defaultPrevented=!0},t.defaultPrevented=!1,t.stopPropagation=function(){r.stopPropagation&&r.stopPropagation(),t.cancelBubble=!0,r.cancelBubble=!0,t.isPropagationStopped=e},t.isPropagationStopped=i,t.stopImmediatePropagation=function(){r.stopImmediatePropagation&&r.stopImmediatePropagation(),t.isImmediatePropagationStopped=e,t.stopPropagation()},t.isImmediatePropagationStopped=i,null!==t.clientX&&void 0!==t.clientX){var s=p.documentElement,a=p.body;t.pageX=t.clientX+(s&&s.scrollLeft||a&&a.scrollLeft||0)-(s&&s.clientLeft||a&&a.clientLeft||0),t.pageY=t.clientY+(s&&s.scrollTop||a&&a.scrollTop||0)-(s&&s.clientTop||a&&a.clientTop||0)}t.which=t.charCode||t.keyCode,null!==t.button&&void 0!==t.button&&(t.button=1&t.button?0:4&t.button?1:2&t.button?2:0)}return t}var yt=!1;!function(){try{var t=Object.defineProperty({},"passive",{get:function(){yt=!0}});g.addEventListener("test",null,t),g.removeEventListener("test",null,t)}catch(t){}}();var vt=["touchstart","touchmove"];function _t(a,t,e){if(Array.isArray(t))return mt(_t,a,t,e);var o=ht(a);if(o.handlers||(o.handlers={}),o.handlers[t]||(o.handlers[t]=[]),e.guid||(e.guid=ut()),o.handlers[t].push(e),o.dispatcher||(o.disabled=!1,o.dispatcher=function(t,e){if(!o.disabled){t=gt(t);var i=o.handlers[t.type];if(i)for(var r=i.slice(0),n=0,s=r.length;n<s&&!t.isImmediatePropagationStopped();n++)try{r[n].call(a,t,e)}catch(t){f.error(t)}}}),1===o.handlers[t].length)if(a.addEventListener){var i=!1;yt&&-1<vt.indexOf(t)&&(i={passive:!0}),a.addEventListener(t,o.dispatcher,i)}else a.attachEvent&&a.attachEvent("on"+t,o.dispatcher)}function bt(t,e,i){if(dt(t)){var r=ht(t);if(r.handlers){if(Array.isArray(e))return mt(bt,t,e,i);var n=function(t,e){r.handlers[e]=[],ft(t,e)};if(void 0!==e){var s=r.handlers[e];if(s)if(i){if(i.guid)for(var a=0;a<s.length;a++)s[a].guid===i.guid&&s.splice(a--,1);ft(t,e)}else n(t,e)}else for(var o in r.handlers)Object.prototype.hasOwnProperty.call(r.handlers||{},o)&&n(t,o)}}}function Tt(t,e,i){var r=dt(t)?ht(t):{},n=t.parentNode||t.ownerDocument;if("string"==typeof e?e={type:e,target:t}:e.target||(e.target=t),e=gt(e),r.dispatcher&&r.dispatcher.call(t,e,i),n&&!e.isPropagationStopped()&&!0===e.bubbles)Tt.call(null,n,e,i);else if(!n&&!e.defaultPrevented){var s=ht(e.target);e.target[e.type]&&(s.disabled=!0,"function"==typeof e.target[e.type]&&e.target[e.type](),s.disabled=!1)}return!e.defaultPrevented}function St(e,i,r){if(Array.isArray(i))return mt(St,e,i,r);var t=function t(){bt(e,i,t),r.apply(this,arguments)};t.guid=r.guid=r.guid||ut(),_t(e,i,t)}var kt=Object.freeze({fixEvent:gt,on:_t,off:bt,trigger:Tt,one:St}),Ct=!1,Et=void 0,wt=function(){if(x()&&!1!==Et.options.autoSetup){var t=Array.prototype.slice.call(p.getElementsByTagName("video")),e=Array.prototype.slice.call(p.getElementsByTagName("audio")),i=Array.prototype.slice.call(p.getElementsByTagName("video-js")),r=t.concat(e,i);if(r&&0<r.length)for(var n=0,s=r.length;n<s;n++){var a=r[n];if(!a||!a.getAttribute){At(1);break}void 0===a.player&&null!==a.getAttribute("data-setup")&&Et(a)}else Ct||At(1)}};function At(t,e){e&&(Et=e),g.setTimeout(wt,t)}x()&&"complete"===p.readyState?Ct=!0:St(g,"load",function(){Ct=!0});var Lt=function(t){var e=p.createElement("style");return e.className=t,e},Pt=function(t,e){t.styleSheet?t.styleSheet.cssText=e:t.textContent=e},Ot=function(t,e,i){e.guid||(e.guid=ut());var r=function(){return e.apply(t,arguments)};return r.guid=i?i+"_"+e.guid:e.guid,r},xt=function(e,i){var r=Date.now();return function(){var t=Date.now();i<=t-r&&(e.apply(void 0,arguments),r=t)}},It=function(r,n,s){var a=3<arguments.length&&void 0!==arguments[3]?arguments[3]:g,o=void 0;return function(){var t=this,e=arguments,i=function(){i=o=null,s||r.apply(t,e)};!o&&s&&r.apply(t,e),a.clearTimeout(o),o=a.setTimeout(i,n)}},Dt=function(){};Dt.prototype.allowedEvents_={},Dt.prototype.addEventListener=Dt.prototype.on=function(t,e){var i=this.addEventListener;this.addEventListener=function(){},_t(this,t,e),this.addEventListener=i},Dt.prototype.removeEventListener=Dt.prototype.off=function(t,e){bt(this,t,e)},Dt.prototype.one=function(t,e){var i=this.addEventListener;this.addEventListener=function(){},St(this,t,e),this.addEventListener=i},Dt.prototype.dispatchEvent=Dt.prototype.trigger=function(t){var e=t.type||t;"string"==typeof t&&(t={type:e}),t=gt(t),this.allowedEvents_[e]&&this["on"+e]&&this["on"+e](t),Tt(this,t)};var Rt=function(e){return e instanceof Dt||!!e.eventBusEl_&&["on","one","off","trigger"].every(function(t){return"function"==typeof e[t]})},Mt=function(t){return"string"==typeof t&&/\S/.test(t)||Array.isArray(t)&&!!t.length},Ut=function(t){if(!t.nodeName&&!Rt(t))throw new Error("Invalid target; must be a DOM node or evented object.")},Nt=function(t){if(!Mt(t))throw new Error("Invalid event type; must be a non-empty string or array.")},Bt=function(t){if("function"!=typeof t)throw new Error("Invalid listener; must be a function.")},jt=function(t,e){var i=e.length<3||e[0]===t||e[0]===t.eventBusEl_,r=void 0,n=void 0,s=void 0;return i?(r=t.eventBusEl_,3<=e.length&&e.shift(),n=e[0],s=e[1]):(r=e[0],n=e[1],s=e[2]),Ut(r),Nt(n),Bt(s),{isTargetingSelf:i,target:r,type:n,listener:s=Ot(t,s)}},Ft=function(t,e,i,r){Ut(t),t.nodeName?kt[e](t,i,r):t[e](i,r)},Ht={on:function(){for(var t=this,e=arguments.length,i=Array(e),r=0;r<e;r++)i[r]=arguments[r];var n=jt(this,i),s=n.isTargetingSelf,a=n.target,o=n.type,u=n.listener;if(Ft(a,"on",o,u),!s){var l=function(){return t.off(a,o,u)};l.guid=u.guid;var c=function(){return t.off("dispose",l)};c.guid=u.guid,Ft(this,"on","dispose",l),Ft(a,"on","dispose",c)}},one:function(){for(var n=this,t=arguments.length,e=Array(t),i=0;i<t;i++)e[i]=arguments[i];var r=jt(this,e),s=r.isTargetingSelf,a=r.target,o=r.type,u=r.listener;if(s)Ft(a,"one",o,u);else{var l=function t(){for(var e=arguments.length,i=Array(e),r=0;r<e;r++)i[r]=arguments[r];n.off(a,o,t),u.apply(null,i)};l.guid=u.guid,Ft(a,"one",o,l)}},off:function(t,e,i){if(!t||Mt(t))bt(this.eventBusEl_,t,e);else{var r=t,n=e;Ut(r),Nt(n),Bt(i),i=Ot(this,i),this.off("dispose",i),r.nodeName?(bt(r,n,i),bt(r,"dispose",i)):Rt(r)&&(r.off(n,i),r.off("dispose",i))}},trigger:function(t,e){return Tt(this.eventBusEl_,t,e)}};function Vt(t){var e=(1<arguments.length&&void 0!==arguments[1]?arguments[1]:{}).eventBusKey;if(e){if(!t[e].nodeName)throw new Error('The eventBusKey "'+e+'" does not refer to an element.');t.eventBusEl_=t[e]}else t.eventBusEl_=M("span",{className:"vjs-event-bus"});return C(t,Ht),t.on("dispose",function(){t.off(),g.setTimeout(function(){t.eventBusEl_=null},0)}),t}var qt={state:{},setState:function(t){var i=this;"function"==typeof t&&(t=t());var r=void 0;return k(t,function(t,e){i.state[e]!==t&&((r=r||{})[e]={from:i.state[e],to:t}),i.state[e]=t}),r&&Rt(this)&&this.trigger({changes:r,type:"statechanged"}),r}};function Wt(t,e){return C(t,qt),t.state=C({},t.state,e),"function"==typeof t.handleStateChanged&&Rt(t)&&t.on("statechanged",t.handleStateChanged),t}function zt(t){return"string"!=typeof t?t:t.charAt(0).toUpperCase()+t.slice(1)}function Gt(){for(var i={},t=arguments.length,e=Array(t),r=0;r<t;r++)e[r]=arguments[r];return e.forEach(function(t){t&&k(t,function(t,e){w(t)?(w(i[e])||(i[e]={}),i[e]=Gt(i[e],t)):i[e]=t})}),i}var Xt=function(){function l(t,e,i){if(y(this,l),!t&&this.play?this.player_=t=this:this.player_=t,this.options_=Gt({},this.options_),e=this.options_=Gt(this.options_,e),this.id_=e.id||e.el&&e.el.id,!this.id_){var r=t&&t.id&&t.id()||"no_player";this.id_=r+"_component_"+ut()}this.name_=e.name||null,e.el?this.el_=e.el:!1!==e.createEl&&(this.el_=this.createEl()),!1!==e.evented&&Vt(this,{eventBusKey:this.el_?"el_":null}),Wt(this,this.constructor.defaultState),this.children_=[],this.childIndex_={},!(this.childNameIndex_={})!==e.initChildren&&this.initChildren(),this.ready(i),!1!==e.reportTouchActivity&&this.enableTouchActivity()}return l.prototype.dispose=function(){if(this.trigger({type:"dispose",bubbles:!1}),this.children_)for(var t=this.children_.length-1;0<=t;t--)this.children_[t].dispose&&this.children_[t].dispose();this.children_=null,this.childIndex_=null,this.childNameIndex_=null,this.el_&&(this.el_.parentNode&&this.el_.parentNode.removeChild(this.el_),pt(this.el_),this.el_=null),this.player_=null},l.prototype.player=function(){return this.player_},l.prototype.options=function(t){return f.warn("this.options() has been deprecated and will be moved to the constructor in 6.0"),t&&(this.options_=Gt(this.options_,t)),this.options_},l.prototype.el=function(){return this.el_},l.prototype.createEl=function(t,e,i){return M(t,e,i)},l.prototype.localize=function(t,n){var e=2<arguments.length&&void 0!==arguments[2]?arguments[2]:t,i=this.player_.language&&this.player_.language(),r=this.player_.languages&&this.player_.languages(),s=r&&r[i],a=i&&i.split("-")[0],o=r&&r[a],u=e;return s&&s[t]?u=s[t]:o&&o[t]&&(u=o[t]),n&&(u=u.replace(/\{(\d+)\}/g,function(t,e){var i=n[e-1],r=i;return"undefined"==typeof i&&(r=t),r})),u},l.prototype.contentEl=function(){return this.contentEl_||this.el_},l.prototype.id=function(){return this.id_},l.prototype.name=function(){return this.name_},l.prototype.children=function(){return this.children_},l.prototype.getChildById=function(t){return this.childIndex_[t]},l.prototype.getChild=function(t){if(t)return t=zt(t),this.childNameIndex_[t]},l.prototype.addChild=function(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{},i=2<arguments.length&&void 0!==arguments[2]?arguments[2]:this.children_.length,r=void 0,n=void 0;if("string"==typeof t){n=zt(t);var s=e.componentClass||n;e.name=n;var a=l.getComponent(s);if(!a)throw new Error("Component "+s+" does not exist");if("function"!=typeof a)return null;r=new a(this.player_||this,e)}else r=t;if(this.children_.splice(i,0,r),"function"==typeof r.id&&(this.childIndex_[r.id()]=r),(n=n||r.name&&zt(r.name()))&&(this.childNameIndex_[n]=r),"function"==typeof r.el&&r.el()){var o=this.contentEl().children[i]||null;this.contentEl().insertBefore(r.el(),o)}return r},l.prototype.removeChild=function(t){if("string"==typeof t&&(t=this.getChild(t)),t&&this.children_){for(var e=!1,i=this.children_.length-1;0<=i;i--)if(this.children_[i]===t){e=!0,this.children_.splice(i,1);break}if(e){this.childIndex_[t.id()]=null,this.childNameIndex_[t.name()]=null;var r=t.el();r&&r.parentNode===this.contentEl()&&this.contentEl().removeChild(t.el())}}},l.prototype.initChildren=function(){var n=this,r=this.options_.children;if(r){var s=this.options_,t=void 0,i=l.getComponent("Tech");(t=Array.isArray(r)?r:Object.keys(r)).concat(Object.keys(this.options_).filter(function(e){return!t.some(function(t){return"string"==typeof t?e===t:e===t.name})})).map(function(t){var e=void 0,i=void 0;return"string"==typeof t?i=r[e=t]||n.options_[e]||{}:(e=t.name,i=t),{name:e,opts:i}}).filter(function(t){var e=l.getComponent(t.opts.componentClass||zt(t.name));return e&&!i.isTech(e)}).forEach(function(t){var e=t.name,i=t.opts;if(void 0!==s[e]&&(i=s[e]),!1!==i){!0===i&&(i={}),i.playerOptions=n.options_.playerOptions;var r=n.addChild(e,i);r&&(n[e]=r)}})}},l.prototype.buildCSSClass=function(){return""},l.prototype.ready=function(t){var e=1<arguments.length&&void 0!==arguments[1]&&arguments[1];if(t)return this.isReady_?void(e?t.call(this):this.setTimeout(t,1)):(this.readyQueue_=this.readyQueue_||[],void this.readyQueue_.push(t))},l.prototype.triggerReady=function(){this.isReady_=!0,this.setTimeout(function(){var t=this.readyQueue_;this.readyQueue_=[],t&&0<t.length&&t.forEach(function(t){t.call(this)},this),this.trigger("ready")},1)},l.prototype.$=function(t,e){return nt(t,e||this.contentEl())},l.prototype.$$=function(t,e){return st(t,e||this.contentEl())},l.prototype.hasClass=function(t){return B(this.el_,t)},l.prototype.addClass=function(t){j(this.el_,t)},l.prototype.removeClass=function(t){F(this.el_,t)},l.prototype.toggleClass=function(t,e){H(this.el_,t,e)},l.prototype.show=function(){this.removeClass("vjs-hidden")},l.prototype.hide=function(){this.addClass("vjs-hidden")},l.prototype.lockShowing=function(){this.addClass("vjs-lock-showing")},l.prototype.unlockShowing=function(){this.removeClass("vjs-lock-showing")},l.prototype.getAttribute=function(t){return W(this.el_,t)},l.prototype.setAttribute=function(t,e){z(this.el_,t,e)},l.prototype.removeAttribute=function(t){G(this.el_,t)},l.prototype.width=function(t,e){return this.dimension("width",t,e)},l.prototype.height=function(t,e){return this.dimension("height",t,e)},l.prototype.dimensions=function(t,e){this.width(t,!0),this.height(e)},l.prototype.dimension=function(t,e,i){if(void 0!==e)return null!==e&&e==e||(e=0),-1!==(""+e).indexOf("%")||-1!==(""+e).indexOf("px")?this.el_.style[t]=e:this.el_.style[t]="auto"===e?"":e+"px",void(i||this.trigger("componentresize"));if(!this.el_)return 0;var r=this.el_.style[t],n=r.indexOf("px");return-1!==n?parseInt(r.slice(0,n),10):parseInt(this.el_["offset"+zt(t)],10)},l.prototype.currentDimension=function(t){var e=0;if("width"!==t&&"height"!==t)throw new Error("currentDimension only accepts width or height value");if("function"==typeof g.getComputedStyle){var i=g.getComputedStyle(this.el_);e=i.getPropertyValue(t)||i[t]}if(0===(e=parseFloat(e))){var r="offset"+zt(t);e=this.el_[r]}return e},l.prototype.currentDimensions=function(){return{width:this.currentDimension("width"),height:this.currentDimension("height")}},l.prototype.currentWidth=function(){return this.currentDimension("width")},l.prototype.currentHeight=function(){return this.currentDimension("height")},l.prototype.focus=function(){this.el_.focus()},l.prototype.blur=function(){this.el_.blur()},l.prototype.emitTapEvents=function(){var e=0,r=null,n=void 0;this.on("touchstart",function(t){1===t.touches.length&&(r={pageX:t.touches[0].pageX,pageY:t.touches[0].pageY},e=(new Date).getTime(),n=!0)}),this.on("touchmove",function(t){if(1<t.touches.length)n=!1;else if(r){var e=t.touches[0].pageX-r.pageX,i=t.touches[0].pageY-r.pageY;10<Math.sqrt(e*e+i*i)&&(n=!1)}});var t=function(){n=!1};this.on("touchleave",t),this.on("touchcancel",t),this.on("touchend",function(t){!(r=null)===n&&((new Date).getTime()-e<200&&(t.preventDefault(),this.trigger("tap")))})},l.prototype.enableTouchActivity=function(){if(this.player()&&this.player().reportUserActivity){var e=Ot(this.player(),this.player().reportUserActivity),i=void 0;this.on("touchstart",function(){e(),this.clearInterval(i),i=this.setInterval(e,250)});var t=function(t){e(),this.clearInterval(i)};this.on("touchmove",e),this.on("touchend",t),this.on("touchcancel",t)}},l.prototype.setTimeout=function(t,e){var i=this;t=Ot(this,t);var r=g.setTimeout(t,e),n=function(){return i.clearTimeout(r)};return n.guid="vjs-timeout-"+r,this.on("dispose",n),r},l.prototype.clearTimeout=function(t){g.clearTimeout(t);var e=function(){};return e.guid="vjs-timeout-"+t,this.off("dispose",e),t},l.prototype.setInterval=function(t,e){var i=this;t=Ot(this,t);var r=g.setInterval(t,e),n=function(){return i.clearInterval(r)};return n.guid="vjs-interval-"+r,this.on("dispose",n),r},l.prototype.clearInterval=function(t){g.clearInterval(t);var e=function(){};return e.guid="vjs-interval-"+t,this.off("dispose",e),t},l.prototype.requestAnimationFrame=function(t){var e=this;if(this.supportsRaf_){t=Ot(this,t);var i=g.requestAnimationFrame(t),r=function(){return e.cancelAnimationFrame(i)};return r.guid="vjs-raf-"+i,this.on("dispose",r),i}return this.setTimeout(t,1e3/60)},l.prototype.cancelAnimationFrame=function(t){if(this.supportsRaf_){g.cancelAnimationFrame(t);var e=function(){};return e.guid="vjs-raf-"+t,this.off("dispose",e),t}return this.clearTimeout(t)},l.registerComponent=function(t,e){if("string"!=typeof t||!t)throw new Error('Illegal component name, "'+t+'"; must be a non-empty string.');var i=l.getComponent("Tech"),r=i&&i.isTech(e),n=l===e||l.prototype.isPrototypeOf(e.prototype);if(r||!n){var s=void 0;throw s=r?"techs must be registered using Tech.registerTech()":"must be a Component subclass",new Error('Illegal component, "'+t+'"; '+s+".")}t=zt(t),l.components_||(l.components_={});var a=l.getComponent("Player");if("Player"===t&&a&&a.players){var o=a.players,u=Object.keys(o);if(o&&0<u.length&&u.map(function(t){return o[t]}).every(Boolean))throw new Error("Can not register Player component after player has been created.")}return l.components_[t]=e},l.getComponent=function(t){if(t)return t=zt(t),l.components_&&l.components_[t]?l.components_[t]:void 0},l}();Xt.prototype.supportsRaf_="function"==typeof g.requestAnimationFrame&&"function"==typeof g.cancelAnimationFrame,Xt.registerComponent("Component",Xt);var Yt,$t,Kt,Qt,Jt=g.navigator&&g.navigator.userAgent||"",Zt=/AppleWebKit\/([\d.]+)/i.exec(Jt),te=Zt?parseFloat(Zt.pop()):null,ee=/iPad/i.test(Jt),ie=/iPhone/i.test(Jt)&&!ee,re=/iPod/i.test(Jt),ne=ie||ee||re,se=(Yt=Jt.match(/OS (\d+)_/i))&&Yt[1]?Yt[1]:null,ae=/Android/i.test(Jt),oe=function(){var t=Jt.match(/Android (\d+)(?:\.(\d+))?(?:\.(\d+))*/i);if(!t)return null;var e=t[1]&&parseFloat(t[1]),i=t[2]&&parseFloat(t[2]);return e&&i?parseFloat(t[1]+"."+t[2]):e||null}(),ue=ae&&oe<5&&te<537,le=/Firefox/i.test(Jt),ce=/Edge/i.test(Jt),he=!ce&&/Chrome/i.test(Jt),de=($t=Jt.match(/Chrome\/(\d+)/))&&$t[1]?parseFloat($t[1]):null,pe=(Kt=/MSIE\s(\d+)\.\d/.exec(Jt),!(Qt=Kt&&parseFloat(Kt[1]))&&/Trident\/7.0/i.test(Jt)&&/rv:11.0/.test(Jt)&&(Qt=11),Qt),fe=/Safari/i.test(Jt)&&!he&&!ae&&!ce,me=fe||ne,ge=x()&&("ontouchstart"in g||g.DocumentTouch&&g.document instanceof g.DocumentTouch),ye=Object.freeze({IS_IPAD:ee,IS_IPHONE:ie,IS_IPOD:re,IS_IOS:ne,IOS_VERSION:se,IS_ANDROID:ae,ANDROID_VERSION:oe,IS_NATIVE_ANDROID:ue,IS_FIREFOX:le,IS_EDGE:ce,IS_CHROME:he,CHROME_VERSION:de,IE_VERSION:pe,IS_SAFARI:fe,IS_ANY_SAFARI:me,TOUCH_ENABLED:ge});function ve(t,e,i,r){return function(t,e,i){if("number"!=typeof e||e<0||i<e)throw new Error("Failed to execute '"+t+"' on 'TimeRanges': The index provided ("+e+") is non-numeric or out of bounds (0-"+i+").")}(t,r,i.length-1),i[r][e]}function _e(t){return void 0===t||0===t.length?{length:0,start:function(){throw new Error("This TimeRanges object is empty")},end:function(){throw new Error("This TimeRanges object is empty")}}:{length:t.length,start:ve.bind(null,"start",0,t),end:ve.bind(null,"end",1,t)}}function be(t,e){return Array.isArray(t)?_e(t):void 0===t||void 0===e?_e():_e([[t,e]])}function Te(t,e){var i=0,r=void 0,n=void 0;if(!e)return 0;t&&t.length||(t=be(0,0));for(var s=0;s<t.length;s++)r=t.start(s),e<(n=t.end(s))&&(n=e),i+=n-r;return i/e}for(var Se={},ke=[["requestFullscreen","exitFullscreen","fullscreenElement","fullscreenEnabled","fullscreenchange","fullscreenerror"],["webkitRequestFullscreen","webkitExitFullscreen","webkitFullscreenElement","webkitFullscreenEnabled","webkitfullscreenchange","webkitfullscreenerror"],["webkitRequestFullScreen","webkitCancelFullScreen","webkitCurrentFullScreenElement","webkitCancelFullScreen","webkitfullscreenchange","webkitfullscreenerror"],["mozRequestFullScreen","mozCancelFullScreen","mozFullScreenElement","mozFullScreenEnabled","mozfullscreenchange","mozfullscreenerror"],["msRequestFullscreen","msExitFullscreen","msFullscreenElement","msFullscreenEnabled","MSFullscreenChange","MSFullscreenError"]],Ce=ke[0],Ee=void 0,we=0;we<ke.length;we++)if(ke[we][1]in p){Ee=ke[we];break}if(Ee)for(var Ae=0;Ae<Ee.length;Ae++)Se[Ce[Ae]]=Ee[Ae];function Le(t){if(t instanceof Le)return t;"number"==typeof t?this.code=t:"string"==typeof t?this.message=t:E(t)&&("number"==typeof t.code&&(this.code=t.code),C(this,t)),this.message||(this.message=Le.defaultMessages[this.code]||"")}Le.prototype.code=0,Le.prototype.message="",Le.prototype.status=null,Le.errorTypes=["MEDIA_ERR_CUSTOM","MEDIA_ERR_ABORTED","MEDIA_ERR_NETWORK","MEDIA_ERR_DECODE","MEDIA_ERR_SRC_NOT_SUPPORTED","MEDIA_ERR_ENCRYPTED"],Le.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."};for(var Pe=0;Pe<Le.errorTypes.length;Pe++)Le[Le.errorTypes[Pe]]=Pe,Le.prototype[Le.errorTypes[Pe]]=Pe;var Oe=function(t,e){var i,r=null;try{i=JSON.parse(t,e)}catch(t){r=t}return[r,i]};function xe(t){return null!=t&&"function"==typeof t.then}function Ie(t){xe(t)&&t.then(null,function(t){})}var De=function(r){return["kind","label","language","id","inBandMetadataTrackDispatchType","mode","src"].reduce(function(t,e,i){return r[e]&&(t[e]=r[e]),t},{cues:r.cues&&Array.prototype.map.call(r.cues,function(t){return{startTime:t.startTime,endTime:t.endTime,text:t.text,id:t.id}})})},Re=function(t){var e=t.$$("track"),i=Array.prototype.map.call(e,function(t){return t.track});return Array.prototype.map.call(e,function(t){var e=De(t.track);return t.src&&(e.src=t.src),e}).concat(Array.prototype.filter.call(t.textTracks(),function(t){return-1===i.indexOf(t)}).map(De))},Me=function(t,i){return t.forEach(function(t){var e=i.addRemoteTextTrack(t).track;!t.src&&t.cues&&t.cues.forEach(function(t){return e.addCue(t)})}),i.textTracks()},Ue="vjs-modal-dialog",Ne=function(r){function n(t,e){y(this,n);var i=b(this,r.call(this,t,e));return i.opened_=i.hasBeenOpened_=i.hasBeenFilled_=!1,i.closeable(!i.options_.uncloseable),i.content(i.options_.content),i.contentEl_=M("div",{className:Ue+"-content"},{role:"document"}),i.descEl_=M("p",{className:Ue+"-description vjs-control-text",id:i.el().getAttribute("aria-describedby")}),U(i.descEl_,i.description()),i.el_.appendChild(i.descEl_),i.el_.appendChild(i.contentEl_),i}return _(n,r),n.prototype.createEl=function(){return r.prototype.createEl.call(this,"div",{className:this.buildCSSClass(),tabIndex:-1},{"aria-describedby":this.id()+"_description","aria-hidden":"true","aria-label":this.label(),role:"dialog"})},n.prototype.dispose=function(){this.contentEl_=null,this.descEl_=null,this.previouslyActiveEl_=null,r.prototype.dispose.call(this)},n.prototype.buildCSSClass=function(){return Ue+" vjs-hidden "+r.prototype.buildCSSClass.call(this)},n.prototype.handleKeyPress=function(t){27===t.which&&this.closeable()&&this.close()},n.prototype.label=function(){return this.localize(this.options_.label||"Modal Window")},n.prototype.description=function(){var t=this.options_.description||this.localize("This is a modal window.");return this.closeable()&&(t+=" "+this.localize("This modal can be closed by pressing the Escape key or activating the close button.")),t},n.prototype.open=function(){if(!this.opened_){var t=this.player();this.trigger("beforemodalopen"),this.opened_=!0,(this.options_.fillAlways||!this.hasBeenOpened_&&!this.hasBeenFilled_)&&this.fill(),this.wasPlaying_=!t.paused(),this.options_.pauseOnOpen&&this.wasPlaying_&&t.pause(),this.closeable()&&this.on(this.el_.ownerDocument,"keydown",Ot(this,this.handleKeyPress)),this.hadControls_=t.controls(),t.controls(!1),this.show(),this.conditionalFocus_(),this.el().setAttribute("aria-hidden","false"),this.trigger("modalopen"),this.hasBeenOpened_=!0}},n.prototype.opened=function(t){return"boolean"==typeof t&&this[t?"open":"close"](),this.opened_},n.prototype.close=function(){if(this.opened_){var t=this.player();this.trigger("beforemodalclose"),this.opened_=!1,this.wasPlaying_&&this.options_.pauseOnOpen&&t.play(),this.closeable()&&this.off(this.el_.ownerDocument,"keydown",Ot(this,this.handleKeyPress)),this.hadControls_&&t.controls(!0),this.hide(),this.el().setAttribute("aria-hidden","true"),this.trigger("modalclose"),this.conditionalBlur_(),this.options_.temporary&&this.dispose()}},n.prototype.closeable=function(t){if("boolean"==typeof t){var e=this.closeable_=!!t,i=this.getChild("closeButton");if(e&&!i){var r=this.contentEl_;this.contentEl_=this.el_,i=this.addChild("closeButton",{controlText:"Close Modal Dialog"}),this.contentEl_=r,this.on(i,"close",this.close)}!e&&i&&(this.off(i,"close",this.close),this.removeChild(i),i.dispose())}return this.closeable_},n.prototype.fill=function(){this.fillWith(this.content())},n.prototype.fillWith=function(t){var e=this.contentEl(),i=e.parentNode,r=e.nextSibling;this.trigger("beforemodalfill"),this.hasBeenFilled_=!0,i.removeChild(e),this.empty(),it(e,t),this.trigger("modalfill"),r?i.insertBefore(e,r):i.appendChild(e);var n=this.getChild("closeButton");n&&i.appendChild(n.el_)},n.prototype.empty=function(){this.trigger("beforemodalempty"),Z(this.contentEl()),this.trigger("modalempty")},n.prototype.content=function(t){return"undefined"!=typeof t&&(this.content_=t),this.content_},n.prototype.conditionalFocus_=function(){var t=p.activeElement,e=this.player_.el_;this.previouslyActiveEl_=null,(e.contains(t)||e===t)&&(this.previouslyActiveEl_=t,this.focus(),this.on(p,"keydown",this.handleKeyDown))},n.prototype.conditionalBlur_=function(){this.previouslyActiveEl_&&(this.previouslyActiveEl_.focus(),this.previouslyActiveEl_=null),this.off(p,"keydown",this.handleKeyDown)},n.prototype.handleKeyDown=function(t){if(9===t.which){for(var e=this.focusableEls_(),i=this.el_.querySelector(":focus"),r=void 0,n=0;n<e.length;n++)if(i===e[n]){r=n;break}p.activeElement===this.el_&&(r=0),t.shiftKey&&0===r?(e[e.length-1].focus(),t.preventDefault()):t.shiftKey||r!==e.length-1||(e[0].focus(),t.preventDefault())}},n.prototype.focusableEls_=function(){var t=this.el_.querySelectorAll("*");return Array.prototype.filter.call(t,function(t){return(t instanceof g.HTMLAnchorElement||t instanceof g.HTMLAreaElement)&&t.hasAttribute("href")||(t instanceof g.HTMLInputElement||t instanceof g.HTMLSelectElement||t instanceof g.HTMLTextAreaElement||t instanceof g.HTMLButtonElement)&&!t.hasAttribute("disabled")||t instanceof g.HTMLIFrameElement||t instanceof g.HTMLObjectElement||t instanceof g.HTMLEmbedElement||t.hasAttribute("tabindex")&&-1!==t.getAttribute("tabindex")||t.hasAttribute("contenteditable")})},n}(Xt);Ne.prototype.options_={pauseOnOpen:!0,temporary:!0},Xt.registerComponent("ModalDialog",Ne);var Be=function(r){function n(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:[];y(this,n);var e=b(this,r.call(this));e.tracks_=[],Object.defineProperty(e,"length",{get:function(){return this.tracks_.length}});for(var i=0;i<t.length;i++)e.addTrack(t[i]);return e}return _(n,r),n.prototype.addTrack=function(t){var e=this.tracks_.length;""+e in this||Object.defineProperty(this,e,{get:function(){return this.tracks_[e]}}),-1===this.tracks_.indexOf(t)&&(this.tracks_.push(t),this.trigger({track:t,type:"addtrack"}))},n.prototype.removeTrack=function(t){for(var e=void 0,i=0,r=this.length;i<r;i++)if(this[i]===t){(e=this[i]).off&&e.off(),this.tracks_.splice(i,1);break}e&&this.trigger({track:e,type:"removetrack"})},n.prototype.getTrackById=function(t){for(var e=null,i=0,r=this.length;i<r;i++){var n=this[i];if(n.id===t){e=n;break}}return e},n}(Dt);for(var je in Be.prototype.allowedEvents_={change:"change",addtrack:"addtrack",removetrack:"removetrack"},Be.prototype.allowedEvents_)Be.prototype["on"+je]=null;var Fe=function(t,e){for(var i=0;i<t.length;i++)Object.keys(t[i]).length&&e.id!==t[i].id&&(t[i].enabled=!1)},He=function(r){function n(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:[];y(this,n);for(var e=t.length-1;0<=e;e--)if(t[e].enabled){Fe(t,t[e]);break}var i=b(this,r.call(this,t));return i.changing_=!1,i}return _(n,r),n.prototype.addTrack=function(t){var e=this;t.enabled&&Fe(this,t),r.prototype.addTrack.call(this,t),t.addEventListener&&t.addEventListener("enabledchange",function(){e.changing_||(e.changing_=!0,Fe(e,t),e.changing_=!1,e.trigger("change"))})},n}(Be),Ve=function(t,e){for(var i=0;i<t.length;i++)Object.keys(t[i]).length&&e.id!==t[i].id&&(t[i].selected=!1)},qe=function(r){function n(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:[];y(this,n);for(var e=t.length-1;0<=e;e--)if(t[e].selected){Ve(t,t[e]);break}var i=b(this,r.call(this,t));return i.changing_=!1,Object.defineProperty(i,"selectedIndex",{get:function(){for(var t=0;t<this.length;t++)if(this[t].selected)return t;return-1},set:function(){}}),i}return _(n,r),n.prototype.addTrack=function(t){var e=this;t.selected&&Ve(this,t),r.prototype.addTrack.call(this,t),t.addEventListener&&t.addEventListener("selectedchange",function(){e.changing_||(e.changing_=!0,Ve(e,t),e.changing_=!1,e.trigger("change"))})},n}(Be),We=function(e){function t(){return y(this,t),b(this,e.apply(this,arguments))}return _(t,e),t.prototype.addTrack=function(t){e.prototype.addTrack.call(this,t),t.addEventListener("modechange",Ot(this,function(){this.trigger("change")}));-1===["metadata","chapters"].indexOf(t.kind)&&t.addEventListener("modechange",Ot(this,function(){this.trigger("selectedlanguagechange")}))},t}(Be),ze=function(){function r(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:[];y(this,r),this.trackElements_=[],Object.defineProperty(this,"length",{get:function(){return this.trackElements_.length}});for(var e=0,i=t.length;e<i;e++)this.addTrackElement_(t[e])}return r.prototype.addTrackElement_=function(t){var e=this.trackElements_.length;""+e in this||Object.defineProperty(this,e,{get:function(){return this.trackElements_[e]}}),-1===this.trackElements_.indexOf(t)&&this.trackElements_.push(t)},r.prototype.getTrackElementByTrack_=function(t){for(var e=void 0,i=0,r=this.trackElements_.length;i<r;i++)if(t===this.trackElements_[i].track){e=this.trackElements_[i];break}return e},r.prototype.removeTrackElement_=function(t){for(var e=0,i=this.trackElements_.length;e<i;e++)if(t===this.trackElements_[e]){this.trackElements_.splice(e,1);break}},r}(),Ge=function(){function e(t){y(this,e),e.prototype.setCues_.call(this,t),Object.defineProperty(this,"length",{get:function(){return this.length_}})}return e.prototype.setCues_=function(t){var e=this.length||0,i=0,r=t.length;this.cues_=t,this.length_=t.length;var n=function(t){""+t in this||Object.defineProperty(this,""+t,{get:function(){return this.cues_[t]}})};if(e<r)for(i=e;i<r;i++)n.call(this,i)},e.prototype.getCueById=function(t){for(var e=null,i=0,r=this.length;i<r;i++){var n=this[i];if(n.id===t){e=n;break}}return e},e}(),Xe={alternative:"alternative",captions:"captions",main:"main",sign:"sign",subtitles:"subtitles",commentary:"commentary"},Ye={alternative:"alternative",descriptions:"descriptions",main:"main","main-desc":"main-desc",translation:"translation",commentary:"commentary"},$e={subtitles:"subtitles",captions:"captions",descriptions:"descriptions",chapters:"chapters",metadata:"metadata"},Ke={disabled:"disabled",hidden:"hidden",showing:"showing"},Qe=function(s){function a(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{};y(this,a);var e=b(this,s.call(this)),i={id:t.id||"vjs_track_"+ut(),kind:t.kind||"",label:t.label||"",language:t.language||""},r=function(t){Object.defineProperty(e,t,{get:function(){return i[t]},set:function(){}})};for(var n in i)r(n);return e}return _(a,s),a}(Dt),Je=function(t){var e=["protocol","hostname","port","pathname","search","hash","host"],i=p.createElement("a");i.href=t;var r=""===i.host&&"file:"!==i.protocol,n=void 0;r&&((n=p.createElement("div")).innerHTML='<a href="'+t+'"></a>',i=n.firstChild,n.setAttribute("style","display:none; position:absolute;"),p.body.appendChild(n));for(var s={},a=0;a<e.length;a++)s[e[a]]=i[e[a]];return"http:"===s.protocol&&(s.host=s.host.replace(/:80$/,"")),"https:"===s.protocol&&(s.host=s.host.replace(/:443$/,"")),s.protocol||(s.protocol=g.location.protocol),r&&p.body.removeChild(n),s},Ze=function(t){if(!t.match(/^https?:\/\//)){var e=p.createElement("div");e.innerHTML='<a href="'+t+'">x</a>',t=e.firstChild.href}return t},ti=function(t){if("string"==typeof t){var e=/^(\/?)([\s\S]*?)((?:\.{1,2}|[^\/]+?)(\.([^\.\/\?]+)))(?:[\/]*|[\?].*)$/i.exec(t);if(e)return e.pop().toLowerCase()}return""},ei=function(t){var e=g.location,i=Je(t);return(":"===i.protocol?e.protocol:i.protocol)+i.host!==e.protocol+e.host},ii=Object.freeze({parseUrl:Je,getAbsoluteURL:Ze,getFileExtension:ti,isCrossOrigin:ei}),ri=function(t){var e=ni.call(t);return"[object Function]"===e||"function"==typeof t&&"[object RegExp]"!==e||"undefined"!=typeof window&&(t===window.setTimeout||t===window.alert||t===window.confirm||t===window.prompt)},ni=Object.prototype.toString;var si=Object.freeze({default:ri,__moduleExports:ri}),ai=e(function(t,e){(e=t.exports=function(t){return t.replace(/^\s*|\s*$/g,"")}).left=function(t){return t.replace(/^\s*/,"")},e.right=function(t){return t.replace(/\s*$/,"")}}),oi=ai.left,ui=ai.right,li=Object.freeze({default:ai,__moduleExports:ai,left:oi,right:ui}),ci=si&&ri||si,hi=function(t,e,i){if(!ci(e))throw new TypeError("iterator must be a function");arguments.length<3&&(i=this);"[object Array]"===di.call(t)?function(t,e,i){for(var r=0,n=t.length;r<n;r++)pi.call(t,r)&&e.call(i,t[r],r,t)}(t,e,i):"string"==typeof t?function(t,e,i){for(var r=0,n=t.length;r<n;r++)e.call(i,t.charAt(r),r,t)}(t,e,i):function(t,e,i){for(var r in t)pi.call(t,r)&&e.call(i,t[r],r,t)}(t,e,i)},di=Object.prototype.toString,pi=Object.prototype.hasOwnProperty;var fi=Object.freeze({default:hi,__moduleExports:hi}),mi=li&&ai||li,gi=fi&&hi||fi,yi=function(t){if(!t)return{};var s={};return gi(mi(t).split("\n"),function(t){var e,i=t.indexOf(":"),r=mi(t.slice(0,i)).toLowerCase(),n=mi(t.slice(i+1));"undefined"==typeof s[r]?s[r]=n:(e=s[r],"[object Array]"===Object.prototype.toString.call(e)?s[r].push(n):s[r]=[s[r],n])}),s},vi=Object.freeze({default:yi,__moduleExports:yi}),_i=function(){for(var t={},e=0;e<arguments.length;e++){var i=arguments[e];for(var r in i)bi.call(i,r)&&(t[r]=i[r])}return t},bi=Object.prototype.hasOwnProperty;var Ti=Object.freeze({default:_i,__moduleExports:_i}),Si=vi&&yi||vi,ki=Ti&&_i||Ti,Ci=wi;function Ei(t,e,i){var r=t;return ci(e)?(i=e,"string"==typeof t&&(r={uri:t})):r=ki(e,{uri:t}),r.callback=i,r}function wi(t,e,i){return Ai(e=Ei(t,e,i))}function Ai(r){if("undefined"==typeof r.callback)throw new Error("callback argument missing");var n=!1,s=function(t,e,i){n||(n=!0,r.callback(t,e,i))};function e(t){return clearTimeout(u),t instanceof Error||(t=new Error(""+(t||"Unknown XMLHttpRequest Error"))),t.statusCode=0,s(t,m)}function t(){if(!a){var t;clearTimeout(u),t=r.useXDR&&void 0===o.status?200:1223===o.status?204:o.status;var e=m,i=null;return 0!==t?(e={body:function(){var t=void 0;if(t=o.response?o.response:o.responseText||function(t){if("document"===t.responseType)return t.responseXML;var e=t.responseXML&&"parsererror"===t.responseXML.documentElement.nodeName;return""!==t.responseType||e?null:t.responseXML}(o),f)try{t=JSON.parse(t)}catch(t){}return t}(),statusCode:t,method:c,headers:{},url:l,rawRequest:o},o.getAllResponseHeaders&&(e.headers=Si(o.getAllResponseHeaders()))):i=new Error("Internal XMLHttpRequest Error"),s(i,e,e.body)}}var i,a,o=r.xhr||null;o||(o=r.cors||r.useXDR?new wi.XDomainRequest:new wi.XMLHttpRequest);var u,l=o.url=r.uri||r.url,c=o.method=r.method||"GET",h=r.body||r.data,d=o.headers=r.headers||{},p=!!r.sync,f=!1,m={body:void 0,headers:{},statusCode:0,method:c,url:l,rawRequest:o};if("json"in r&&!1!==r.json&&(f=!0,d.accept||d.Accept||(d.Accept="application/json"),"GET"!==c&&"HEAD"!==c&&(d["content-type"]||d["Content-Type"]||(d["Content-Type"]="application/json"),h=JSON.stringify(!0===r.json?h:r.json))),o.onreadystatechange=function(){4===o.readyState&&setTimeout(t,0)},o.onload=t,o.onerror=e,o.onprogress=function(){},o.onabort=function(){a=!0},o.ontimeout=e,o.open(c,l,!p,r.username,r.password),p||(o.withCredentials=!!r.withCredentials),!p&&0<r.timeout&&(u=setTimeout(function(){if(!a){a=!0,o.abort("timeout");var t=new Error("XMLHttpRequest timeout");t.code="ETIMEDOUT",e(t)}},r.timeout)),o.setRequestHeader)for(i in d)d.hasOwnProperty(i)&&o.setRequestHeader(i,d[i]);else if(r.headers&&!function(t){for(var e in t)if(t.hasOwnProperty(e))return!1;return!0}(r.headers))throw new Error("Headers cannot be set on an XDomainRequest object");return"responseType"in r&&(o.responseType=r.responseType),"beforeSend"in r&&"function"==typeof r.beforeSend&&r.beforeSend(o),o.send(h||null),o}wi.XMLHttpRequest=g.XMLHttpRequest||function(){},wi.XDomainRequest="withCredentials"in new wi.XMLHttpRequest?wi.XMLHttpRequest:g.XDomainRequest,function(t,e){for(var i=0;i<t.length;i++)e(t[i])}(["get","put","post","patch","head","delete"],function(r){wi["delete"===r?"del":r]=function(t,e,i){return(e=Ei(t,e,i)).method=r.toUpperCase(),Ai(e)}});var Li=function(t,e){var i=new g.WebVTT.Parser(g,g.vttjs,g.WebVTT.StringDecoder()),r=[];i.oncue=function(t){e.addCue(t)},i.onparsingerror=function(t){r.push(t)},i.onflush=function(){e.trigger({type:"loadeddata",target:e})},i.parse(t),0<r.length&&(g.console&&g.console.groupCollapsed&&g.console.groupCollapsed("Text Track parsing errors for "+e.src),r.forEach(function(t){return f.error(t)}),g.console&&g.console.groupEnd&&g.console.groupEnd()),i.flush()},Pi=function(t,n){var e={uri:t},i=ei(t);i&&(e.cors=i),Ci(e,Ot(this,function(t,e,i){if(t)return f.error(t,e);if(n.loaded_=!0,"function"!=typeof g.WebVTT){if(n.tech_){var r=function(){return Li(i,n)};n.tech_.on("vttjsloaded",r),n.tech_.on("vttjserror",function(){f.error("vttjs failed to load, stopping trying to process "+n.src),n.tech_.off("vttjsloaded",r)})}}else Li(i,n)}))},Oi=function(l){function c(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{};if(y(this,c),!t.tech)throw new Error("A tech was not provided.");var e=Gt(t,{kind:$e[t.kind]||"subtitles",language:t.language||t.srclang||""}),i=Ke[e.mode]||"disabled",r=e.default;"metadata"!==e.kind&&"chapters"!==e.kind||(i="hidden");var n=b(this,l.call(this,e));n.tech_=e.tech,n.cues_=[],n.activeCues_=[];var s=new Ge(n.cues_),a=new Ge(n.activeCues_),o=!1,u=Ot(n,function(){this.activeCues,o&&(this.trigger("cuechange"),o=!1)});return"disabled"!==i&&n.tech_.ready(function(){n.tech_.on("timeupdate",u)},!0),Object.defineProperties(n,{default:{get:function(){return r},set:function(){}},mode:{get:function(){return i},set:function(t){var e=this;Ke[t]&&("showing"===(i=t)&&this.tech_.ready(function(){e.tech_.on("timeupdate",u)},!0),this.trigger("modechange"))}},cues:{get:function(){return this.loaded_?s:null},set:function(){}},activeCues:{get:function(){if(!this.loaded_)return null;if(0===this.cues.length)return a;for(var t=this.tech_.currentTime(),e=[],i=0,r=this.cues.length;i<r;i++){var n=this.cues[i];n.startTime<=t&&n.endTime>=t?e.push(n):n.startTime===n.endTime&&n.startTime<=t&&n.startTime+.5>=t&&e.push(n)}if(o=!1,e.length!==this.activeCues_.length)o=!0;else for(var s=0;s<e.length;s++)-1===this.activeCues_.indexOf(e[s])&&(o=!0);return this.activeCues_=e,a.setCues_(this.activeCues_),a},set:function(){}}}),e.src?(n.src=e.src,Pi(e.src,n)):n.loaded_=!0,n}return _(c,l),c.prototype.addCue=function(t){var e=t;if(g.vttjs&&!(t instanceof g.vttjs.VTTCue)){for(var i in e=new g.vttjs.VTTCue(t.startTime,t.endTime,t.text),t)i in e||(e[i]=t[i]);e.id=t.id,e.originalCue_=t}for(var r=this.tech_.textTracks(),n=0;n<r.length;n++)r[n]!==this&&r[n].removeCue(e);this.cues_.push(e),this.cues.setCues_(this.cues_)},c.prototype.removeCue=function(t){for(var e=this.cues_.length;e--;){var i=this.cues_[e];if(i===t||i.originalCue_&&i.originalCue_===t){this.cues_.splice(e,1),this.cues.setCues_(this.cues_);break}}},c}(Qe);Oi.prototype.allowedEvents_={cuechange:"cuechange"};var xi=function(n){function s(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{};y(this,s);var e=Gt(t,{kind:Ye[t.kind]||""}),i=b(this,n.call(this,e)),r=!1;return Object.defineProperty(i,"enabled",{get:function(){return r},set:function(t){"boolean"==typeof t&&t!==r&&(r=t,this.trigger("enabledchange"))}}),e.enabled&&(i.enabled=e.enabled),i.loaded_=!0,i}return _(s,n),s}(Qe),Ii=function(n){function s(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{};y(this,s);var e=Gt(t,{kind:Xe[t.kind]||""}),i=b(this,n.call(this,e)),r=!1;return Object.defineProperty(i,"selected",{get:function(){return r},set:function(t){"boolean"==typeof t&&t!==r&&(r=t,this.trigger("selectedchange"))}}),e.selected&&(i.selected=e.selected),i}return _(s,n),s}(Qe),Di=0,Ri=2,Mi=function(n){function s(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{};y(this,s);var e=b(this,n.call(this)),i=void 0,r=new Oi(t);return e.kind=r.kind,e.src=r.src,e.srclang=r.language,e.label=r.label,e.default=r.default,Object.defineProperties(e,{readyState:{get:function(){return i}},track:{get:function(){return r}}}),i=Di,r.addEventListener("loadeddata",function(){i=Ri,e.trigger({type:"load",target:e})}),e}return _(s,n),s}(Dt);Mi.prototype.allowedEvents_={load:"load"},Mi.NONE=Di,Mi.LOADING=1,Mi.LOADED=Ri,Mi.ERROR=3;var Ui={audio:{ListClass:He,TrackClass:xi,capitalName:"Audio"},video:{ListClass:qe,TrackClass:Ii,capitalName:"Video"},text:{ListClass:We,TrackClass:Oi,capitalName:"Text"}};Object.keys(Ui).forEach(function(t){Ui[t].getterName=t+"Tracks",Ui[t].privateName=t+"Tracks_"});var Ni={remoteText:{ListClass:We,TrackClass:Oi,capitalName:"RemoteText",getterName:"remoteTextTracks",privateName:"remoteTextTracks_"},remoteTextEl:{ListClass:ze,TrackClass:Mi,capitalName:"RemoteTextTrackEls",getterName:"remoteTextTrackEls",privateName:"remoteTextTrackEls_"}},Bi=Gt(Ui,Ni);Ni.names=Object.keys(Ni),Ui.names=Object.keys(Ui),Bi.names=[].concat(Ni.names).concat(Ui.names);var ji=Object.create||function(){function e(){}return function(t){if(1!==arguments.length)throw new Error("Object.create shim only accepts one parameter.");return e.prototype=t,new e}}();function Fi(t,e){this.name="ParsingError",this.code=t.code,this.message=e||t.message}function Hi(t){function e(t,e,i,r){return 3600*(0|t)+60*(0|e)+(0|i)+(0|r)/1e3}var i=t.match(/^(\d+):(\d{2})(:\d{2})?\.(\d{3})/);return i?i[3]?e(i[1],i[2],i[3].replace(":",""),i[4]):59<i[1]?e(i[1],i[2],0,i[4]):e(0,i[1],i[2],i[4]):null}function Vi(){this.values=ji(null)}function qi(t,e,i,r){var n=r?t.split(r):[t];for(var s in n)if("string"==typeof n[s]){var a=n[s].split(i);if(2===a.length)e(a[0],a[1])}}function Wi(e,t,s){var i,r,a,n=e;function o(){var t=Hi(e);if(null===t)throw new Fi(Fi.Errors.BadTimeStamp,"Malformed timestamp: "+n);return e=e.replace(/^[^\sa-zA-Z-]+/,""),t}function u(){e=e.replace(/^\s+/,"")}if(u(),t.startTime=o(),u(),"--\x3e"!==e.substr(0,3))throw new Fi(Fi.Errors.BadTimeStamp,"Malformed time stamp (time stamps must be separated by '--\x3e'): "+n);e=e.substr(3),u(),t.endTime=o(),u(),i=e,r=t,a=new Vi,qi(i,function(t,e){switch(t){case"region":for(var i=s.length-1;0<=i;i--)if(s[i].id===e){a.set(t,s[i].region);break}break;case"vertical":a.alt(t,e,["rl","lr"]);break;case"line":var r=e.split(","),n=r[0];a.integer(t,n),a.percent(t,n)&&a.set("snapToLines",!1),a.alt(t,n,["auto"]),2===r.length&&a.alt("lineAlign",r[1],["start","middle","end"]);break;case"position":r=e.split(","),a.percent(t,r[0]),2===r.length&&a.alt("positionAlign",r[1],["start","middle","end"]);break;case"size":a.percent(t,e);break;case"align":a.alt(t,e,["start","middle","end","left","right"])}},/:/,/\s/),r.region=a.get("region",null),r.vertical=a.get("vertical",""),r.line=a.get("line","auto"),r.lineAlign=a.get("lineAlign","start"),r.snapToLines=a.get("snapToLines",!0),r.size=a.get("size",100),r.align=a.get("align","middle"),r.position=a.get("position",{start:0,left:0,middle:50,end:100,right:100},r.align),r.positionAlign=a.get("positionAlign",{start:"start",left:"start",middle:"middle",end:"end",right:"end"},r.align)}((Fi.prototype=ji(Error.prototype)).constructor=Fi).Errors={BadSignature:{code:0,message:"Malformed WebVTT signature."},BadTimeStamp:{code:1,message:"Malformed time stamp."}},Vi.prototype={set:function(t,e){this.get(t)||""===e||(this.values[t]=e)},get:function(t,e,i){return i?this.has(t)?this.values[t]:e[i]:this.has(t)?this.values[t]:e},has:function(t){return t in this.values},alt:function(t,e,i){for(var r=0;r<i.length;++r)if(e===i[r]){this.set(t,e);break}},integer:function(t,e){/^-?\d+$/.test(e)&&this.set(t,parseInt(e,10))},percent:function(t,e){return!!(e.match(/^([\d]{1,3})(\.[\d]*)?%$/)&&0<=(e=parseFloat(e))&&e<=100)&&(this.set(t,e),!0)}};var zi={"&amp;":"&","&lt;":"<","&gt;":">","&lrm;":"‎","&rlm;":"‏","&nbsp;":" "},Gi={c:"span",i:"i",b:"b",u:"u",ruby:"ruby",rt:"rt",v:"span",lang:"span"},Xi={v:"title",lang:"lang"},Yi={rt:"ruby"};function $i(s,i){function t(){if(!i)return null;var t,e=i.match(/^([^<]*)(<[^>]*>?)?/);return t=e[1]?e[1]:e[2],i=i.substr(t.length),t}function e(t){return zi[t]}function r(t){for(;f=t.match(/&(amp|lt|gt|lrm|rlm|nbsp);/);)t=t.replace(f[0],e);return t}function n(t,e){var i=Gi[t];if(!i)return null;var r=s.document.createElement(i);r.localName=i;var n=Xi[t];return n&&e&&(r[n]=e.trim()),r}for(var a,o,u,l=s.document.createElement("div"),c=l,h=[];null!==(a=t());)if("<"!==a[0])c.appendChild(s.document.createTextNode(r(a)));else{if("/"===a[1]){h.length&&h[h.length-1]===a.substr(2).replace(">","")&&(h.pop(),c=c.parentNode);continue}var d,p=Hi(a.substr(1,a.length-2));if(p){d=s.document.createProcessingInstruction("timestamp",p),c.appendChild(d);continue}var f=a.match(/^<([^.\s/0-9>]+)(\.[^\s\\>]+)?([^>\\]+)?(\\?)>?$/);if(!f)continue;if(!(d=n(f[1],f[3])))continue;if(o=c,Yi[(u=d).localName]&&Yi[u.localName]!==o.localName)continue;f[2]&&(d.className=f[2].substr(1).replace("."," ")),h.push(f[1]),c.appendChild(d),c=d}return l}var Ki=[[1470,1470],[1472,1472],[1475,1475],[1478,1478],[1488,1514],[1520,1524],[1544,1544],[1547,1547],[1549,1549],[1563,1563],[1566,1610],[1645,1647],[1649,1749],[1765,1766],[1774,1775],[1786,1805],[1807,1808],[1810,1839],[1869,1957],[1969,1969],[1984,2026],[2036,2037],[2042,2042],[2048,2069],[2074,2074],[2084,2084],[2088,2088],[2096,2110],[2112,2136],[2142,2142],[2208,2208],[2210,2220],[8207,8207],[64285,64285],[64287,64296],[64298,64310],[64312,64316],[64318,64318],[64320,64321],[64323,64324],[64326,64449],[64467,64829],[64848,64911],[64914,64967],[65008,65020],[65136,65140],[65142,65276],[67584,67589],[67592,67592],[67594,67637],[67639,67640],[67644,67644],[67647,67669],[67671,67679],[67840,67867],[67872,67897],[67903,67903],[67968,68023],[68030,68031],[68096,68096],[68112,68115],[68117,68119],[68121,68147],[68160,68167],[68176,68184],[68192,68223],[68352,68405],[68416,68437],[68440,68466],[68472,68479],[68608,68680],[126464,126467],[126469,126495],[126497,126498],[126500,126500],[126503,126503],[126505,126514],[126516,126519],[126521,126521],[126523,126523],[126530,126530],[126535,126535],[126537,126537],[126539,126539],[126541,126543],[126545,126546],[126548,126548],[126551,126551],[126553,126553],[126555,126555],[126557,126557],[126559,126559],[126561,126562],[126564,126564],[126567,126570],[126572,126578],[126580,126583],[126585,126588],[126590,126590],[126592,126601],[126603,126619],[126625,126627],[126629,126633],[126635,126651],[1114109,1114109]];function Qi(t){for(var e=0;e<Ki.length;e++){var i=Ki[e];if(t>=i[0]&&t<=i[1])return!0}return!1}function Ji(){}function Zi(t,e,i){Ji.call(this),this.cue=e,this.cueDiv=$i(t,e.text);var r={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:""===e.vertical?"horizontal-tb":"lr"===e.vertical?"vertical-lr":"vertical-rl",unicodeBidi:"plaintext"};this.applyStyles(r,this.cueDiv),this.div=t.document.createElement("div"),r={direction:function(t){var e=[],i="";if(!t||!t.childNodes)return"ltr";function n(t,e){for(var i=e.childNodes.length-1;0<=i;i--)t.push(e.childNodes[i])}function s(t){if(!t||!t.length)return null;var e=t.pop(),i=e.textContent||e.innerText;if(i){var r=i.match(/^.*(\n|\r)/);return r?r[t.length=0]:i}return"ruby"===e.tagName?s(t):e.childNodes?(n(t,e),s(t)):void 0}for(n(e,t);i=s(e);)for(var r=0;r<i.length;r++)if(Qi(i.charCodeAt(r)))return"rtl";return"ltr"}(this.cueDiv),writingMode:""===e.vertical?"horizontal-tb":"lr"===e.vertical?"vertical-lr":"vertical-rl",unicodeBidi:"plaintext",textAlign:"middle"===e.align?"center":e.align,font:i.font,whiteSpace:"pre-line",position:"absolute"},this.applyStyles(r),this.div.appendChild(this.cueDiv);var n=0;switch(e.positionAlign){case"start":n=e.position;break;case"middle":n=e.position-e.size/2;break;case"end":n=e.position-e.size}""===e.vertical?this.applyStyles({left:this.formatStyle(n,"%"),width:this.formatStyle(e.size,"%")}):this.applyStyles({top:this.formatStyle(n,"%"),height:this.formatStyle(e.size,"%")}),this.move=function(t){this.applyStyles({top:this.formatStyle(t.top,"px"),bottom:this.formatStyle(t.bottom,"px"),left:this.formatStyle(t.left,"px"),right:this.formatStyle(t.right,"px"),height:this.formatStyle(t.height,"px"),width:this.formatStyle(t.width,"px")})}}function tr(t){var e,i,r,n;if(t.div){i=t.div.offsetHeight,r=t.div.offsetWidth,n=t.div.offsetTop;var s=(s=t.div.childNodes)&&(s=s[0])&&s.getClientRects&&s.getClientRects();t=t.div.getBoundingClientRect(),e=s?Math.max(s[0]&&s[0].height||0,t.height/s.length):0}this.left=t.left,this.right=t.right,this.top=t.top||n,this.height=t.height||i,this.bottom=t.bottom||n+(t.height||i),this.width=t.width||r,this.lineHeight=void 0!==e?e:t.lineHeight}function er(t,e,o,u){var i=new tr(e),r=e.cue,n=function(t){if("number"==typeof t.line&&(t.snapToLines||0<=t.line&&t.line<=100))return t.line;if(!t.track||!t.track.textTrackList||!t.track.textTrackList.mediaElement)return-1;for(var e=t.track,i=e.textTrackList,r=0,n=0;n<i.length&&i[n]!==e;n++)"showing"===i[n].mode&&r++;return-1*++r}(r),s=[];if(r.snapToLines){var a;switch(r.vertical){case"":s=["+y","-y"],a="height";break;case"rl":s=["+x","-x"],a="width";break;case"lr":s=["-x","+x"],a="width"}var l=i.lineHeight,c=l*Math.round(n),h=o[a]+l,d=s[0];Math.abs(c)>h&&(c=c<0?-1:1,c*=Math.ceil(h/l)*l),n<0&&(c+=""===r.vertical?o.height:o.width,s=s.reverse()),i.move(d,c)}else{var p=i.lineHeight/o.height*100;switch(r.lineAlign){case"middle":n-=p/2;break;case"end":n-=p}switch(r.vertical){case"":e.applyStyles({top:e.formatStyle(n,"%")});break;case"rl":e.applyStyles({left:e.formatStyle(n,"%")});break;case"lr":e.applyStyles({right:e.formatStyle(n,"%")})}s=["+y","-x","+x","-y"],i=new tr(e)}var f=function(t,e){for(var i,r=new tr(t),n=1,s=0;s<e.length;s++){for(;t.overlapsOppositeAxis(o,e[s])||t.within(o)&&t.overlapsAny(u);)t.move(e[s]);if(t.within(o))return t;var a=t.intersectPercentage(o);a<n&&(i=new tr(t),n=a),t=new tr(r)}return i||r}(i,s);e.move(f.toCSSCompatValues(o))}function ir(){}Ji.prototype.applyStyles=function(t,e){for(var i in e=e||this.div,t)t.hasOwnProperty(i)&&(e.style[i]=t[i])},Ji.prototype.formatStyle=function(t,e){return 0===t?0:t+e},(Zi.prototype=ji(Ji.prototype)).constructor=Zi,tr.prototype.move=function(t,e){switch(e=void 0!==e?e:this.lineHeight,t){case"+x":this.left+=e,this.right+=e;break;case"-x":this.left-=e,this.right-=e;break;case"+y":this.top+=e,this.bottom+=e;break;case"-y":this.top-=e,this.bottom-=e}},tr.prototype.overlaps=function(t){return this.left<t.right&&this.right>t.left&&this.top<t.bottom&&this.bottom>t.top},tr.prototype.overlapsAny=function(t){for(var e=0;e<t.length;e++)if(this.overlaps(t[e]))return!0;return!1},tr.prototype.within=function(t){return this.top>=t.top&&this.bottom<=t.bottom&&this.left>=t.left&&this.right<=t.right},tr.prototype.overlapsOppositeAxis=function(t,e){switch(e){case"+x":return this.left<t.left;case"-x":return this.right>t.right;case"+y":return this.top<t.top;case"-y":return this.bottom>t.bottom}},tr.prototype.intersectPercentage=function(t){return Math.max(0,Math.min(this.right,t.right)-Math.max(this.left,t.left))*Math.max(0,Math.min(this.bottom,t.bottom)-Math.max(this.top,t.top))/(this.height*this.width)},tr.prototype.toCSSCompatValues=function(t){return{top:this.top-t.top,bottom:t.bottom-this.bottom,left:this.left-t.left,right:t.right-this.right,height:this.height,width:this.width}},tr.getSimpleBoxPosition=function(t){var e=t.div?t.div.offsetHeight:t.tagName?t.offsetHeight:0,i=t.div?t.div.offsetWidth:t.tagName?t.offsetWidth:0,r=t.div?t.div.offsetTop:t.tagName?t.offsetTop:0;return{left:(t=t.div?t.div.getBoundingClientRect():t.tagName?t.getBoundingClientRect():t).left,right:t.right,top:t.top||r,height:t.height||e,bottom:t.bottom||r+(t.height||e),width:t.width||i}},ir.StringDecoder=function(){return{decode:function(t){if(!t)return"";if("string"!=typeof t)throw new Error("Error - expected string data.");return decodeURIComponent(encodeURIComponent(t))}}},ir.convertCueToDOMTree=function(t,e){return t&&e?$i(t,e):null};ir.processCues=function(r,n,t){if(!r||!n||!t)return null;for(;t.firstChild;)t.removeChild(t.firstChild);var s=r.document.createElement("div");if(s.style.position="absolute",s.style.left="0",s.style.right="0",s.style.top="0",s.style.bottom="0",s.style.margin="1.5%",t.appendChild(s),function(t){for(var e=0;e<t.length;e++)if(t[e].hasBeenReset||!t[e].displayState)return!0;return!1}(n)){var a=[],o=tr.getSimpleBoxPosition(s),u={font:Math.round(.05*o.height*100)/100+"px sans-serif"};!function(){for(var t,e,i=0;i<n.length;i++)e=n[i],t=new Zi(r,e,u),s.appendChild(t.div),er(0,t,o,a),e.displayState=t.div,a.push(tr.getSimpleBoxPosition(t))}()}else for(var e=0;e<n.length;e++)s.appendChild(n[e].displayState)},(ir.Parser=function(t,e,i){i||(i=e,e={}),e||(e={}),this.window=t,this.vttjs=e,this.state="INITIAL",this.buffer="",this.decoder=i||new TextDecoder("utf8"),this.regionList=[]}).prototype={reportOrThrowError:function(t){if(!(t instanceof Fi))throw t;this.onparsingerror&&this.onparsingerror(t)},parse:function(t){var s=this;function e(){for(var t=s.buffer,e=0;e<t.length&&"\r"!==t[e]&&"\n"!==t[e];)++e;var i=t.substr(0,e);return"\r"===t[e]&&++e,"\n"===t[e]&&++e,s.buffer=t.substr(e),i}function i(t){t.match(/X-TIMESTAMP-MAP/)?qi(t,function(t,e){switch(t){case"X-TIMESTAMP-MAP":i=e,r=new Vi,qi(i,function(t,e){switch(t){case"MPEGT":r.integer(t+"S",e);break;case"LOCA":r.set(t+"L",Hi(e))}},/[^\d]:/,/,/),s.ontimestampmap&&s.ontimestampmap({MPEGTS:r.get("MPEGTS"),LOCAL:r.get("LOCAL")})}var i,r},/=/):qi(t,function(t,e){switch(t){case"Region":!function(t){var n=new Vi;if(qi(t,function(t,e){switch(t){case"id":n.set(t,e);break;case"width":n.percent(t,e);break;case"lines":n.integer(t,e);break;case"regionanchor":case"viewportanchor":var i=e.split(",");if(2!==i.length)break;var r=new Vi;if(r.percent("x",i[0]),r.percent("y",i[1]),!r.has("x")||!r.has("y"))break;n.set(t+"X",r.get("x")),n.set(t+"Y",r.get("y"));break;case"scroll":n.alt(t,e,["up"])}},/=/,/\s/),n.has("id")){var e=new(s.vttjs.VTTRegion||s.window.VTTRegion);e.width=n.get("width",100),e.lines=n.get("lines",3),e.regionAnchorX=n.get("regionanchorX",0),e.regionAnchorY=n.get("regionanchorY",100),e.viewportAnchorX=n.get("viewportanchorX",0),e.viewportAnchorY=n.get("viewportanchorY",100),e.scroll=n.get("scroll",""),s.onregion&&s.onregion(e),s.regionList.push({id:n.get("id"),region:e})}}(e)}},/:/)}t&&(s.buffer+=s.decoder.decode(t,{stream:!0}));try{var r;if("INITIAL"===s.state){if(!/\r\n|\n/.test(s.buffer))return this;var n=(r=e()).match(/^WEBVTT([ \t].*)?$/);if(!n||!n[0])throw new Fi(Fi.Errors.BadSignature);s.state="HEADER"}for(var a=!1;s.buffer;){if(!/\r\n|\n/.test(s.buffer))return this;switch(a?a=!1:r=e(),s.state){case"HEADER":/:/.test(r)?i(r):r||(s.state="ID");continue;case"NOTE":r||(s.state="ID");continue;case"ID":if(/^NOTE($|[ \t])/.test(r)){s.state="NOTE";break}if(!r)continue;if(s.cue=new(s.vttjs.VTTCue||s.window.VTTCue)(0,0,""),s.state="CUE",-1===r.indexOf("--\x3e")){s.cue.id=r;continue}case"CUE":try{Wi(r,s.cue,s.regionList)}catch(t){s.reportOrThrowError(t),s.cue=null,s.state="BADCUE";continue}s.state="CUETEXT";continue;case"CUETEXT":var o=-1!==r.indexOf("--\x3e");if(!r||o&&(a=!0)){s.oncue&&s.oncue(s.cue),s.cue=null,s.state="ID";continue}s.cue.text&&(s.cue.text+="\n"),s.cue.text+=r;continue;case"BADCUE":r||(s.state="ID");continue}}}catch(t){s.reportOrThrowError(t),"CUETEXT"===s.state&&s.cue&&s.oncue&&s.oncue(s.cue),s.cue=null,s.state="INITIAL"===s.state?"BADWEBVTT":"BADCUE"}return this},flush:function(){var e=this;try{if(e.buffer+=e.decoder.decode(),(e.cue||"HEADER"===e.state)&&(e.buffer+="\n\n",e.parse()),"INITIAL"===e.state)throw new Fi(Fi.Errors.BadSignature)}catch(t){e.reportOrThrowError(t)}return e.onflush&&e.onflush(),this}};var rr=ir,nr=Object.freeze({default:rr,__moduleExports:rr}),sr="auto",ar={"":1,lr:1,rl:1},or={start:1,middle:1,end:1,left:1,right:1};function ur(t){return"string"==typeof t&&(!!or[t.toLowerCase()]&&t.toLowerCase())}function lr(t,e,i){this.hasBeenReset=!1;var r="",n=!1,s=t,a=e,o=i,u=null,l="",c=!0,h="auto",d="start",p=50,f="middle",m=50,g="middle";Object.defineProperties(this,{id:{enumerable:!0,get:function(){return r},set:function(t){r=""+t}},pauseOnExit:{enumerable:!0,get:function(){return n},set:function(t){n=!!t}},startTime:{enumerable:!0,get:function(){return s},set:function(t){if("number"!=typeof t)throw new TypeError("Start time must be set to a number.");s=t,this.hasBeenReset=!0}},endTime:{enumerable:!0,get:function(){return a},set:function(t){if("number"!=typeof t)throw new TypeError("End time must be set to a number.");a=t,this.hasBeenReset=!0}},text:{enumerable:!0,get:function(){return o},set:function(t){o=""+t,this.hasBeenReset=!0}},region:{enumerable:!0,get:function(){return u},set:function(t){u=t,this.hasBeenReset=!0}},vertical:{enumerable:!0,get:function(){return l},set:function(t){var e,i="string"==typeof(e=t)&&!!ar[e.toLowerCase()]&&e.toLowerCase();if(!1===i)throw new SyntaxError("An invalid or illegal string was specified.");l=i,this.hasBeenReset=!0}},snapToLines:{enumerable:!0,get:function(){return c},set:function(t){c=!!t,this.hasBeenReset=!0}},line:{enumerable:!0,get:function(){return h},set:function(t){if("number"!=typeof t&&t!==sr)throw new SyntaxError("An invalid number or illegal string was specified.");h=t,this.hasBeenReset=!0}},lineAlign:{enumerable:!0,get:function(){return d},set:function(t){var e=ur(t);if(!e)throw new SyntaxError("An invalid or illegal string was specified.");d=e,this.hasBeenReset=!0}},position:{enumerable:!0,get:function(){return p},set:function(t){if(t<0||100<t)throw new Error("Position must be between 0 and 100.");p=t,this.hasBeenReset=!0}},positionAlign:{enumerable:!0,get:function(){return f},set:function(t){var e=ur(t);if(!e)throw new SyntaxError("An invalid or illegal string was specified.");f=e,this.hasBeenReset=!0}},size:{enumerable:!0,get:function(){return m},set:function(t){if(t<0||100<t)throw new Error("Size must be between 0 and 100.");m=t,this.hasBeenReset=!0}},align:{enumerable:!0,get:function(){return g},set:function(t){var e=ur(t);if(!e)throw new SyntaxError("An invalid or illegal string was specified.");g=e,this.hasBeenReset=!0}}}),this.displayState=void 0}lr.prototype.getCueAsHTML=function(){return WebVTT.convertCueToDOMTree(window,this.text)};var cr=lr,hr=Object.freeze({default:cr,__moduleExports:cr}),dr={"":!0,up:!0};function pr(t){return"number"==typeof t&&0<=t&&t<=100}var fr=function(){var e=100,i=3,r=0,n=100,s=0,a=100,o="";Object.defineProperties(this,{width:{enumerable:!0,get:function(){return e},set:function(t){if(!pr(t))throw new Error("Width must be between 0 and 100.");e=t}},lines:{enumerable:!0,get:function(){return i},set:function(t){if("number"!=typeof t)throw new TypeError("Lines must be set to a number.");i=t}},regionAnchorY:{enumerable:!0,get:function(){return n},set:function(t){if(!pr(t))throw new Error("RegionAnchorX must be between 0 and 100.");n=t}},regionAnchorX:{enumerable:!0,get:function(){return r},set:function(t){if(!pr(t))throw new Error("RegionAnchorY must be between 0 and 100.");r=t}},viewportAnchorY:{enumerable:!0,get:function(){return a},set:function(t){if(!pr(t))throw new Error("ViewportAnchorY must be between 0 and 100.");a=t}},viewportAnchorX:{enumerable:!0,get:function(){return s},set:function(t){if(!pr(t))throw new Error("ViewportAnchorX must be between 0 and 100.");s=t}},scroll:{enumerable:!0,get:function(){return o},set:function(t){var e,i="string"==typeof(e=t)&&!!dr[e.toLowerCase()]&&e.toLowerCase();if(!1===i)throw new SyntaxError("An invalid or illegal string was specified.");o=i}}})},mr=Object.freeze({default:fr,__moduleExports:fr}),gr=nr&&rr||nr,yr=hr&&cr||hr,vr=mr&&fr||mr,_r=e(function(t){var e=t.exports={WebVTT:gr,VTTCue:yr,VTTRegion:vr};g.vttjs=e,g.WebVTT=e.WebVTT;var i=e.VTTCue,r=e.VTTRegion,n=g.VTTCue,s=g.VTTRegion;e.shim=function(){g.VTTCue=i,g.VTTRegion=r},e.restore=function(){g.VTTCue=n,g.VTTRegion=s},g.VTTCue||e.shim()});_r.WebVTT,_r.VTTCue,_r.VTTRegion;var br=function(e){function n(){var i=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{},t=1<arguments.length&&void 0!==arguments[1]?arguments[1]:function(){};y(this,n),i.reportTouchActivity=!1;var r=b(this,e.call(this,null,i,t));return r.hasStarted_=!1,r.on("playing",function(){this.hasStarted_=!0}),r.on("loadstart",function(){this.hasStarted_=!1}),Bi.names.forEach(function(t){var e=Bi[t];i&&i[e.getterName]&&(r[e.privateName]=i[e.getterName])}),r.featuresProgressEvents||r.manualProgressOn(),r.featuresTimeupdateEvents||r.manualTimeUpdatesOn(),["Text","Audio","Video"].forEach(function(t){!1===i["native"+t+"Tracks"]&&(r["featuresNative"+t+"Tracks"]=!1)}),!1===i.nativeCaptions||!1===i.nativeTextTracks?r.featuresNativeTextTracks=!1:!0!==i.nativeCaptions&&!0!==i.nativeTextTracks||(r.featuresNativeTextTracks=!0),r.featuresNativeTextTracks||r.emulateTextTracks(),r.autoRemoteTextTracks_=new Bi.text.ListClass,r.initTrackListeners(),i.nativeControlsForTouch||r.emitTapEvents(),r.constructor&&(r.name_=r.constructor.name||"Unknown Tech"),r}return _(n,e),n.prototype.triggerSourceset=function(t){var e=this;this.isReady_||this.one("ready",function(){return e.setTimeout(function(){return e.triggerSourceset(t)},1)}),this.trigger({src:t,type:"sourceset"})},n.prototype.manualProgressOn=function(){this.on("durationchange",this.onDurationChange),this.manualProgress=!0,this.one("ready",this.trackProgress)},n.prototype.manualProgressOff=function(){this.manualProgress=!1,this.stopTrackingProgress(),this.off("durationchange",this.onDurationChange)},n.prototype.trackProgress=function(t){this.stopTrackingProgress(),this.progressInterval=this.setInterval(Ot(this,function(){var t=this.bufferedPercent();this.bufferedPercent_!==t&&this.trigger("progress"),1===(this.bufferedPercent_=t)&&this.stopTrackingProgress()}),500)},n.prototype.onDurationChange=function(t){this.duration_=this.duration()},n.prototype.buffered=function(){return be(0,0)},n.prototype.bufferedPercent=function(){return Te(this.buffered(),this.duration_)},n.prototype.stopTrackingProgress=function(){this.clearInterval(this.progressInterval)},n.prototype.manualTimeUpdatesOn=function(){this.manualTimeUpdates=!0,this.on("play",this.trackCurrentTime),this.on("pause",this.stopTrackingCurrentTime)},n.prototype.manualTimeUpdatesOff=function(){this.manualTimeUpdates=!1,this.stopTrackingCurrentTime(),this.off("play",this.trackCurrentTime),this.off("pause",this.stopTrackingCurrentTime)},n.prototype.trackCurrentTime=function(){this.currentTimeInterval&&this.stopTrackingCurrentTime(),this.currentTimeInterval=this.setInterval(function(){this.trigger({type:"timeupdate",target:this,manuallyTriggered:!0})},250)},n.prototype.stopTrackingCurrentTime=function(){this.clearInterval(this.currentTimeInterval),this.trigger({type:"timeupdate",target:this,manuallyTriggered:!0})},n.prototype.dispose=function(){this.clearTracks(Ui.names),this.manualProgress&&this.manualProgressOff(),this.manualTimeUpdates&&this.manualTimeUpdatesOff(),e.prototype.dispose.call(this)},n.prototype.clearTracks=function(t){var n=this;(t=[].concat(t)).forEach(function(t){for(var e=n[t+"Tracks"]()||[],i=e.length;i--;){var r=e[i];"text"===t&&n.removeRemoteTextTrack(r),e.removeTrack(r)}})},n.prototype.cleanupAutoTextTracks=function(){for(var t=this.autoRemoteTextTracks_||[],e=t.length;e--;){var i=t[e];this.removeRemoteTextTrack(i)}},n.prototype.reset=function(){},n.prototype.error=function(t){return void 0!==t&&(this.error_=new Le(t),this.trigger("error")),this.error_},n.prototype.played=function(){return this.hasStarted_?be(0,0):be()},n.prototype.setCurrentTime=function(){this.manualTimeUpdates&&this.trigger({type:"timeupdate",target:this,manuallyTriggered:!0})},n.prototype.initTrackListeners=function(){var n=this;Ui.names.forEach(function(t){var e=Ui[t],i=function(){n.trigger(t+"trackchange")},r=n[e.getterName]();r.addEventListener("removetrack",i),r.addEventListener("addtrack",i),n.on("dispose",function(){r.removeEventListener("removetrack",i),r.removeEventListener("addtrack",i)})})},n.prototype.addWebVttScript_=function(){var t=this;if(!g.WebVTT)if(p.body.contains(this.el())){if(!this.options_["vtt.js"]&&w(_r)&&0<Object.keys(_r).length)return void this.trigger("vttjsloaded");var e=p.createElement("script");e.src=this.options_["vtt.js"]||"https://vjs.zencdn.net/vttjs/0.14.1/vtt.min.js",e.onload=function(){t.trigger("vttjsloaded")},e.onerror=function(){t.trigger("vttjserror")},this.on("dispose",function(){e.onload=null,e.onerror=null}),g.WebVTT=!0,this.el().parentNode.appendChild(e)}else this.ready(this.addWebVttScript_)},n.prototype.emulateTextTracks=function(){var t=this,i=this.textTracks(),e=this.remoteTextTracks(),r=function(t){return i.addTrack(t.track)},n=function(t){return i.removeTrack(t.track)};e.on("addtrack",r),e.on("removetrack",n),this.addWebVttScript_();var s=function(){return t.trigger("texttrackchange")},a=function(){s();for(var t=0;t<i.length;t++){var e=i[t];e.removeEventListener("cuechange",s),"showing"===e.mode&&e.addEventListener("cuechange",s)}};a(),i.addEventListener("change",a),i.addEventListener("addtrack",a),i.addEventListener("removetrack",a),this.on("dispose",function(){e.off("addtrack",r),e.off("removetrack",n),i.removeEventListener("change",a),i.removeEventListener("addtrack",a),i.removeEventListener("removetrack",a);for(var t=0;t<i.length;t++){i[t].removeEventListener("cuechange",s)}})},n.prototype.addTextTrack=function(t,e,i){if(!t)throw new Error("TextTrack kind is required but was not provided");return function(t,e,i,r){var n=4<arguments.length&&void 0!==arguments[4]?arguments[4]:{},s=t.textTracks();n.kind=e,i&&(n.label=i),r&&(n.language=r),n.tech=t;var a=new Bi.text.TrackClass(n);return s.addTrack(a),a}(this,t,e,i)},n.prototype.createRemoteTextTrack=function(t){var e=Gt(t,{tech:this});return new Ni.remoteTextEl.TrackClass(e)},n.prototype.addRemoteTextTrack=function(){var t=this,e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{},i=arguments[1],r=this.createRemoteTextTrack(e);return!0!==i&&!1!==i&&(f.warn('Calling addRemoteTextTrack without explicitly setting the "manualCleanup" parameter to `true` is deprecated and default to `false` in future version of video.js'),i=!0),this.remoteTextTrackEls().addTrackElement_(r),this.remoteTextTracks().addTrack(r.track),!0!==i&&this.ready(function(){return t.autoRemoteTextTracks_.addTrack(r.track)}),r},n.prototype.removeRemoteTextTrack=function(t){var e=this.remoteTextTrackEls().getTrackElementByTrack_(t);this.remoteTextTrackEls().removeTrackElement_(e),this.remoteTextTracks().removeTrack(t),this.autoRemoteTextTracks_.removeTrack(t)},n.prototype.getVideoPlaybackQuality=function(){return{}},n.prototype.setPoster=function(){},n.prototype.playsinline=function(){},n.prototype.setPlaysinline=function(){},n.prototype.overrideNativeAudioTracks=function(){},n.prototype.overrideNativeVideoTracks=function(){},n.prototype.canPlayType=function(){return""},n.canPlayType=function(){return""},n.canPlaySource=function(t,e){return n.canPlayType(t.type)},n.isTech=function(t){return t.prototype instanceof n||t instanceof n||t===n},n.registerTech=function(t,e){if(n.techs_||(n.techs_={}),!n.isTech(e))throw new Error("Tech "+t+" must be a Tech");if(!n.canPlayType)throw new Error("Techs must have a static canPlayType method on them");if(!n.canPlaySource)throw new Error("Techs must have a static canPlaySource method on them");return t=zt(t),n.techs_[t]=e,"Tech"!==t&&n.defaultTechOrder_.push(t),e},n.getTech=function(t){if(t)return t=zt(t),n.techs_&&n.techs_[t]?n.techs_[t]:g&&g.videojs&&g.videojs[t]?(f.warn("The "+t+" tech was added to the videojs object when it should be registered using videojs.registerTech(name, tech)"),g.videojs[t]):void 0},n}(Xt);Bi.names.forEach(function(t){var e=Bi[t];br.prototype[e.getterName]=function(){return this[e.privateName]=this[e.privateName]||new e.ListClass,this[e.privateName]}}),br.prototype.featuresVolumeControl=!0,br.prototype.featuresFullscreenResize=!1,br.prototype.featuresPlaybackRate=!1,br.prototype.featuresProgressEvents=!1,br.prototype.featuresSourceset=!1,br.prototype.featuresTimeupdateEvents=!1,br.prototype.featuresNativeTextTracks=!1,br.withSourceHandlers=function(n){n.registerSourceHandler=function(t,e){var i=n.sourceHandlers;i||(i=n.sourceHandlers=[]),void 0===e&&(e=i.length),i.splice(e,0,t)},n.canPlayType=function(t){for(var e=n.sourceHandlers||[],i=void 0,r=0;r<e.length;r++)if(i=e[r].canPlayType(t))return i;return""},n.selectSourceHandler=function(t,e){for(var i=n.sourceHandlers||[],r=0;r<i.length;r++)if(i[r].canHandleSource(t,e))return i[r];return null},n.canPlaySource=function(t,e){var i=n.selectSourceHandler(t,e);return i?i.canHandleSource(t,e):""};["seekable","seeking","duration"].forEach(function(t){var e=this[t];"function"==typeof e&&(this[t]=function(){return this.sourceHandler_&&this.sourceHandler_[t]?this.sourceHandler_[t].apply(this.sourceHandler_,arguments):e.apply(this,arguments)})},n.prototype),n.prototype.setSource=function(t){var e=n.selectSourceHandler(t,this.options_);e||(n.nativeSourceHandler?e=n.nativeSourceHandler:f.error("No source handler found for the current source.")),this.disposeSourceHandler(),this.off("dispose",this.disposeSourceHandler),e!==n.nativeSourceHandler&&(this.currentSource_=t),this.sourceHandler_=e.handleSource(t,this,this.options_),this.on("dispose",this.disposeSourceHandler)},n.prototype.disposeSourceHandler=function(){this.currentSource_&&(this.clearTracks(["audio","video"]),this.currentSource_=null),this.cleanupAutoTextTracks(),this.sourceHandler_&&(this.sourceHandler_.dispose&&this.sourceHandler_.dispose(),this.sourceHandler_=null)}},Xt.registerComponent("Tech",br),br.registerTech("Tech",br),br.defaultTechOrder_=[];var Tr={},Sr={},kr={};function Cr(t,e,i){t.setTimeout(function(){return function i(){var r=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{};var t=1<arguments.length&&void 0!==arguments[1]?arguments[1]:[];var n=arguments[2];var s=arguments[3];var a=4<arguments.length&&void 0!==arguments[4]?arguments[4]:[];var o=5<arguments.length&&void 0!==arguments[5]&&arguments[5];var e=t[0],u=t.slice(1);if("string"==typeof e)i(r,Tr[e],n,s,a,o);else if(e){var l=function(t,e){var i=Sr[t.id()],r=null;if(null==i)return r=e(t),Sr[t.id()]=[[e,r]],r;for(var n=0;n<i.length;n++){var s=i[n],a=s[0],o=s[1];a===e&&(r=o)}null===r&&(r=e(t),i.push([e,r]));return r}(s,e);l.setSource(C({},r),function(t,e){if(t)return i(r,u,n,s,a,o);a.push(l),i(e,r.type===e.type?u:Tr[e.type],n,s,a,o)})}else u.length?i(r,u,n,s,a,o):o?n(r,a):i(r,Tr["*"],n,s,a,!0)}(e,Tr[e.type],i,t)},1)}function Er(t,e,i){var r=3<arguments.length&&void 0!==arguments[3]?arguments[3]:null,n="call"+zt(i),s=t.reduce(Pr(n),r),a=s===kr,o=a?null:e[i](s);return function(t,e,i,r){for(var n=t.length-1;0<=n;n--){var s=t[n];s[e]&&s[e](r,i)}}(t,i,o,a),o}var wr={buffered:1,currentTime:1,duration:1,seekable:1,played:1,paused:1},Ar={setCurrentTime:1},Lr={play:1,pause:1};function Pr(i){return function(t,e){return t===kr?kr:e[i]?e[i](t):t}}var Or={opus:"video/ogg",ogv:"video/ogg",mp4:"video/mp4",mov:"video/mp4",m4v:"video/mp4",mkv:"video/x-matroska",mp3:"audio/mpeg",aac:"audio/aac",oga:"audio/ogg",m3u8:"application/x-mpegURL"},xr=function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:"",e=ti(t);return Or[e.toLowerCase()]||""};function Ir(t){var e=xr(t.src);return!t.type&&e&&(t.type=e),t}var Dr=function(l){function c(t,e,i){y(this,c);var r=Gt({createEl:!1},e),n=b(this,l.call(this,t,r,i));if(e.playerOptions.sources&&0!==e.playerOptions.sources.length)t.src(e.playerOptions.sources);else for(var s=0,a=e.playerOptions.techOrder;s<a.length;s++){var o=zt(a[s]),u=br.getTech(o);if(o||(u=Xt.getComponent(o)),u&&u.isSupported()){t.loadTech_(o);break}}return n}return _(c,l),c}(Xt);Xt.registerComponent("MediaLoader",Dr);var Rr=function(n){function r(t,e){y(this,r);var i=b(this,n.call(this,t,e));return i.emitTapEvents(),i.enable(),i}return _(r,n),r.prototype.createEl=function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:"div",e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{},i=2<arguments.length&&void 0!==arguments[2]?arguments[2]:{};e=C({innerHTML:'<span aria-hidden="true" class="vjs-icon-placeholder"></span>',className:this.buildCSSClass(),tabIndex:0},e),"button"===t&&f.error("Creating a ClickableComponent with an HTML element of "+t+" is not supported; use a Button instead."),i=C({role:"button"},i),this.tabIndex_=e.tabIndex;var r=n.prototype.createEl.call(this,t,e,i);return this.createControlTextEl(r),r},r.prototype.dispose=function(){this.controlTextEl_=null,n.prototype.dispose.call(this)},r.prototype.createControlTextEl=function(t){return this.controlTextEl_=M("span",{className:"vjs-control-text"},{"aria-live":"polite"}),t&&t.appendChild(this.controlTextEl_),this.controlText(this.controlText_,t),this.controlTextEl_},r.prototype.controlText=function(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:this.el();if(void 0===t)return this.controlText_||"Need Text";var i=this.localize(t);this.controlText_=t,U(this.controlTextEl_,i),this.nonIconControl||e.setAttribute("title",i)},r.prototype.buildCSSClass=function(){return"vjs-control vjs-button "+n.prototype.buildCSSClass.call(this)},r.prototype.enable=function(){this.enabled_||(this.enabled_=!0,this.removeClass("vjs-disabled"),this.el_.setAttribute("aria-disabled","false"),"undefined"!=typeof this.tabIndex_&&this.el_.setAttribute("tabIndex",this.tabIndex_),this.on(["tap","click"],this.handleClick),this.on("focus",this.handleFocus),this.on("blur",this.handleBlur))},r.prototype.disable=function(){this.enabled_=!1,this.addClass("vjs-disabled"),this.el_.setAttribute("aria-disabled","true"),"undefined"!=typeof this.tabIndex_&&this.el_.removeAttribute("tabIndex"),this.off(["tap","click"],this.handleClick),this.off("focus",this.handleFocus),this.off("blur",this.handleBlur)},r.prototype.handleClick=function(t){},r.prototype.handleFocus=function(t){_t(p,"keydown",Ot(this,this.handleKeyPress))},r.prototype.handleKeyPress=function(t){32===t.which||13===t.which?(t.preventDefault(),this.trigger("click")):n.prototype.handleKeyPress&&n.prototype.handleKeyPress.call(this,t)},r.prototype.handleBlur=function(t){bt(p,"keydown",Ot(this,this.handleKeyPress))},r}(Xt);Xt.registerComponent("ClickableComponent",Rr);var Mr=function(r){function n(t,e){y(this,n);var i=b(this,r.call(this,t,e));return i.update(),t.on("posterchange",Ot(i,i.update)),i}return _(n,r),n.prototype.dispose=function(){this.player().off("posterchange",this.update),r.prototype.dispose.call(this)},n.prototype.createEl=function(){return M("div",{className:"vjs-poster",tabIndex:-1})},n.prototype.update=function(t){var e=this.player().poster();this.setSrc(e),e?this.show():this.hide()},n.prototype.setSrc=function(t){var e="";t&&(e='url("'+t+'")'),this.el_.style.backgroundImage=e},n.prototype.handleClick=function(t){this.player_.controls()&&(this.player_.paused()?this.player_.play():this.player_.pause())},n}(Rr);Xt.registerComponent("PosterImage",Mr);var Ur="#222",Nr={monospace:"monospace",sansSerif:"sans-serif",serif:"serif",monospaceSansSerif:'"Andale Mono", "Lucida Console", monospace',monospaceSerif:'"Courier New", monospace',proportionalSansSerif:"sans-serif",proportionalSerif:"serif",casual:'"Comic Sans MS", Impact, fantasy',script:'"Monotype Corsiva", cursive',smallcaps:'"Andale Mono", "Lucida Console", monospace, sans-serif'};function Br(t,e){return"rgba("+parseInt(t[1]+t[1],16)+","+parseInt(t[2]+t[2],16)+","+parseInt(t[3]+t[3],16)+","+e+")"}function jr(t,e,i){try{t.style[e]=i}catch(t){return}}var Fr=function(n){function s(i,t,e){y(this,s);var r=b(this,n.call(this,i,t,e));return i.on("loadstart",Ot(r,r.toggleDisplay)),i.on("texttrackchange",Ot(r,r.updateDisplay)),i.on("loadstart",Ot(r,r.preselectTrack)),i.ready(Ot(r,function(){if(i.tech_&&i.tech_.featuresNativeTextTracks)this.hide();else{i.on("fullscreenchange",Ot(this,this.updateDisplay));for(var t=this.options_.playerOptions.tracks||[],e=0;e<t.length;e++)this.player_.addRemoteTextTrack(t[e],!0);this.preselectTrack()}})),r}return _(s,n),s.prototype.preselectTrack=function(){for(var t={captions:1,subtitles:1},e=this.player_.textTracks(),i=this.player_.cache_.selectedLanguage,r=void 0,n=void 0,s=void 0,a=0;a<e.length;a++){var o=e[a];i&&i.enabled&&i.language===o.language?o.kind===i.kind?s=o:s||(s=o):i&&!i.enabled?n=r=s=null:o.default&&("descriptions"!==o.kind||r?o.kind in t&&!n&&(n=o):r=o)}s?s.mode="showing":n?n.mode="showing":r&&(r.mode="showing")},s.prototype.toggleDisplay=function(){this.player_.tech_&&this.player_.tech_.featuresNativeTextTracks?this.hide():this.show()},s.prototype.createEl=function(){return n.prototype.createEl.call(this,"div",{className:"vjs-text-track-display"},{"aria-live":"off","aria-atomic":"true"})},s.prototype.clearDisplay=function(){"function"==typeof g.WebVTT&&g.WebVTT.processCues(g,[],this.el_)},s.prototype.updateDisplay=function(){var t=this.player_.textTracks();this.clearDisplay();for(var e=null,i=null,r=t.length;r--;){var n=t[r];"showing"===n.mode&&("descriptions"===n.kind?e=n:i=n)}i?("off"!==this.getAttribute("aria-live")&&this.setAttribute("aria-live","off"),this.updateForTrack(i)):e&&("assertive"!==this.getAttribute("aria-live")&&this.setAttribute("aria-live","assertive"),this.updateForTrack(e))},s.prototype.updateForTrack=function(t){if("function"==typeof g.WebVTT&&t.activeCues){for(var e=[],i=0;i<t.activeCues.length;i++)e.push(t.activeCues[i]);if(g.WebVTT.processCues(g,e,this.el_),this.player_.textTrackSettings)for(var r=this.player_.textTrackSettings.getValues(),n=e.length;n--;){var s=e[n];if(s){var a=s.displayState;if(r.color&&(a.firstChild.style.color=r.color),r.textOpacity&&jr(a.firstChild,"color",Br(r.color||"#fff",r.textOpacity)),r.backgroundColor&&(a.firstChild.style.backgroundColor=r.backgroundColor),r.backgroundOpacity&&jr(a.firstChild,"backgroundColor",Br(r.backgroundColor||"#000",r.backgroundOpacity)),r.windowColor&&(r.windowOpacity?jr(a,"backgroundColor",Br(r.windowColor,r.windowOpacity)):a.style.backgroundColor=r.windowColor),r.edgeStyle&&("dropshadow"===r.edgeStyle?a.firstChild.style.textShadow="2px 2px 3px #222, 2px 2px 4px #222, 2px 2px 5px "+Ur:"raised"===r.edgeStyle?a.firstChild.style.textShadow="1px 1px #222, 2px 2px #222, 3px 3px "+Ur:"depressed"===r.edgeStyle?a.firstChild.style.textShadow="1px 1px #ccc, 0 1px #ccc, -1px -1px #222, 0 -1px "+Ur:"uniform"===r.edgeStyle&&(a.firstChild.style.textShadow="0 0 4px #222, 0 0 4px #222, 0 0 4px #222, 0 0 4px "+Ur)),r.fontPercent&&1!==r.fontPercent){var o=g.parseFloat(a.style.fontSize);a.style.fontSize=o*r.fontPercent+"px",a.style.height="auto",a.style.top="auto",a.style.bottom="2px"}r.fontFamily&&"default"!==r.fontFamily&&("small-caps"===r.fontFamily?a.firstChild.style.fontVariant="small-caps":a.firstChild.style.fontFamily=Nr[r.fontFamily])}}}},s}(Xt);Xt.registerComponent("TextTrackDisplay",Fr);var Hr=function(n){function t(){return y(this,t),b(this,n.apply(this,arguments))}return _(t,n),t.prototype.createEl=function(){var t=this.player_.isAudio(),e=this.localize(t?"Audio Player":"Video Player"),i=M("span",{className:"vjs-control-text",innerHTML:this.localize("{1} is loading.",[e])}),r=n.prototype.createEl.call(this,"div",{className:"vjs-loading-spinner",dir:"ltr"});return r.appendChild(i),r},t}(Xt);Xt.registerComponent("LoadingSpinner",Hr);var Vr=function(e){function t(){return y(this,t),b(this,e.apply(this,arguments))}return _(t,e),t.prototype.createEl=function(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{},i=2<arguments.length&&void 0!==arguments[2]?arguments[2]:{};e=C({innerHTML:'<span aria-hidden="true" class="vjs-icon-placeholder"></span>',className:this.buildCSSClass()},e),i=C({type:"button"},i);var r=Xt.prototype.createEl.call(this,"button",e,i);return this.createControlTextEl(r),r},t.prototype.addChild=function(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{},i=this.constructor.name;return f.warn("Adding an actionable (user controllable) child to a Button ("+i+") is not supported; use a ClickableComponent instead."),Xt.prototype.addChild.call(this,t,e)},t.prototype.enable=function(){e.prototype.enable.call(this),this.el_.removeAttribute("disabled")},t.prototype.disable=function(){e.prototype.disable.call(this),this.el_.setAttribute("disabled","disabled")},t.prototype.handleKeyPress=function(t){32!==t.which&&13!==t.which&&e.prototype.handleKeyPress.call(this,t)},t}(Rr);Xt.registerComponent("Button",Vr);var qr=function(r){function n(t,e){y(this,n);var i=b(this,r.call(this,t,e));return i.mouseused_=!1,i.on("mousedown",i.handleMouseDown),i}return _(n,r),n.prototype.buildCSSClass=function(){return"vjs-big-playOld-button"},n.prototype.handleClick=function(t){var e=this.player_.play();if(!(this.mouseused_&&t.clientX&&t.clientY)){var i=this.player_.getChild("controlBar"),r=i&&i.getChild("playToggle");if(r){var n=function(){return r.focus()};xe(e)?e.then(n,function(){}):this.setTimeout(n,1)}else this.player_.focus()}},n.prototype.handleKeyPress=function(t){this.mouseused_=!1,r.prototype.handleKeyPress.call(this,t)},n.prototype.handleMouseDown=function(t){this.mouseused_=!0},n}(Vr);qr.prototype.controlText_="Play Video",Xt.registerComponent("BigPlayButton",qr);var Wr=function(r){function n(t,e){y(this,n);var i=b(this,r.call(this,t,e));return i.controlText(e&&e.controlText||i.localize("Close")),i}return _(n,r),n.prototype.buildCSSClass=function(){return"vjs-close-button "+r.prototype.buildCSSClass.call(this)},n.prototype.handleClick=function(t){this.trigger({type:"close",bubbles:!1})},n}(Vr);Xt.registerComponent("CloseButton",Wr);var zr=function(r){function n(t,e){y(this,n);var i=b(this,r.call(this,t,e));return i.on(t,"play",i.handlePlay),i.on(t,"pause",i.handlePause),i.on(t,"ended",i.handleEnded),i}return _(n,r),n.prototype.buildCSSClass=function(){return"vjs-playOld-control "+r.prototype.buildCSSClass.call(this)},n.prototype.handleClick=function(t){this.player_.paused()?this.player_.play():this.player_.pause()},n.prototype.handleSeeked=function(t){this.removeClass("vjs-ended"),this.player_.paused()?this.handlePause(t):this.handlePlay(t)},n.prototype.handlePlay=function(t){this.removeClass("vjs-ended"),this.removeClass("vjs-paused"),this.addClass("vjs-playing"),this.controlText("Pause")},n.prototype.handlePause=function(t){this.removeClass("vjs-playing"),this.addClass("vjs-paused"),this.controlText("Play")},n.prototype.handleEnded=function(t){this.removeClass("vjs-playing"),this.addClass("vjs-ended"),this.controlText("Replay"),this.one(this.player_,"seeked",this.handleSeeked)},n}(Vr);zr.prototype.controlText_="Play",Xt.registerComponent("PlayToggle",zr);var Gr=function(t,e){t=t<0?0:t;var i=Math.floor(t%60),r=Math.floor(t/60%60),n=Math.floor(t/3600),s=Math.floor(e/60%60),a=Math.floor(e/3600);return(isNaN(t)||t===1/0)&&(n=r=i="-"),(n=0<n||0<a?n+":":"")+(r=((n||10<=s)&&r<10?"0"+r:r)+":")+(i=i<10?"0"+i:i)},Xr=Gr;function Yr(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:t;return Xr(t,e)}var $r=function(r){function n(t,e){y(this,n);var i=b(this,r.call(this,t,e));return i.throttledUpdateContent=xt(Ot(i,i.updateContent),25),i.on(t,"timeupdate",i.throttledUpdateContent),i}return _(n,r),n.prototype.createEl=function(t){var e=this.buildCSSClass(),i=r.prototype.createEl.call(this,"div",{className:e+" vjs-time-control vjs-control",innerHTML:'<span class="vjs-control-text">'+this.localize(this.labelText_)+" </span>"});return this.contentEl_=M("span",{className:e+"-display"},{"aria-live":"off"}),this.updateTextNode_(),i.appendChild(this.contentEl_),i},n.prototype.dispose=function(){this.contentEl_=null,this.textNode_=null,r.prototype.dispose.call(this)},n.prototype.updateTextNode_=function(){if(this.contentEl_){for(;this.contentEl_.firstChild;)this.contentEl_.removeChild(this.contentEl_.firstChild);this.textNode_=p.createTextNode(this.formattedTime_||this.formatTime_(0)),this.contentEl_.appendChild(this.textNode_)}},n.prototype.formatTime_=function(t){return Yr(t)},n.prototype.updateFormattedTime_=function(t){var e=this.formatTime_(t);e!==this.formattedTime_&&(this.formattedTime_=e,this.requestAnimationFrame(this.updateTextNode_))},n.prototype.updateContent=function(t){},n}(Xt);$r.prototype.labelText_="Time",$r.prototype.controlText_="Time",Xt.registerComponent("TimeDisplay",$r);var Kr=function(r){function n(t,e){y(this,n);var i=b(this,r.call(this,t,e));return i.on(t,"ended",i.handleEnded),i}return _(n,r),n.prototype.buildCSSClass=function(){return"vjs-current-time"},n.prototype.updateContent=function(t){var e=this.player_.scrubbing()?this.player_.getCache().currentTime:this.player_.currentTime();this.updateFormattedTime_(e)},n.prototype.handleEnded=function(t){this.player_.duration()&&this.updateFormattedTime_(this.player_.duration())},n}($r);Kr.prototype.labelText_="Current Time",Kr.prototype.controlText_="Current Time",Xt.registerComponent("CurrentTimeDisplay",Kr);var Qr=function(r){function n(t,e){y(this,n);var i=b(this,r.call(this,t,e));return i.on(t,"durationchange",i.updateContent),i.on(t,"loadedmetadata",i.throttledUpdateContent),i}return _(n,r),n.prototype.buildCSSClass=function(){return"vjs-duration"},n.prototype.updateContent=function(t){var e=this.player_.duration();e&&this.duration_!==e&&(this.duration_=e,this.updateFormattedTime_(e))},n}($r);Qr.prototype.labelText_="Duration",Qr.prototype.controlText_="Duration",Xt.registerComponent("DurationDisplay",Qr);var Jr=function(t){function e(){return y(this,e),b(this,t.apply(this,arguments))}return _(e,t),e.prototype.createEl=function(){return t.prototype.createEl.call(this,"div",{className:"vjs-time-control vjs-time-divider",innerHTML:"<div><span>/</span></div>"})},e}(Xt);Xt.registerComponent("TimeDivider",Jr);var Zr=function(r){function n(t,e){y(this,n);var i=b(this,r.call(this,t,e));return i.on(t,"durationchange",i.throttledUpdateContent),i.on(t,"ended",i.handleEnded),i}return _(n,r),n.prototype.buildCSSClass=function(){return"vjs-remaining-time"},n.prototype.formatTime_=function(t){return"-"+r.prototype.formatTime_.call(this,t)},n.prototype.updateContent=function(t){this.player_.duration()&&(this.player_.remainingTimeDisplay?this.updateFormattedTime_(this.player_.remainingTimeDisplay()):this.updateFormattedTime_(this.player_.remainingTime()))},n.prototype.handleEnded=function(t){this.player_.duration()&&this.updateFormattedTime_(0)},n}($r);Zr.prototype.labelText_="Remaining Time",Zr.prototype.controlText_="Remaining Time",Xt.registerComponent("RemainingTimeDisplay",Zr);var tn=function(r){function n(t,e){y(this,n);var i=b(this,r.call(this,t,e));return i.updateShowing(),i.on(i.player(),"durationchange",i.updateShowing),i}return _(n,r),n.prototype.createEl=function(){var t=r.prototype.createEl.call(this,"div",{className:"vjs-live-control vjs-control"});return this.contentEl_=M("div",{className:"vjs-live-display",innerHTML:'<span class="vjs-control-text">'+this.localize("Stream Type")+" </span>"+this.localize("LIVE")},{"aria-live":"off"}),t.appendChild(this.contentEl_),t},n.prototype.dispose=function(){this.contentEl_=null,r.prototype.dispose.call(this)},n.prototype.updateShowing=function(t){this.player().duration()===1/0?this.show():this.hide()},n}(Xt);Xt.registerComponent("LiveDisplay",tn);var en=function(r){function n(t,e){y(this,n);var i=b(this,r.call(this,t,e));return i.bar=i.getChild(i.options_.barName),i.vertical(!!i.options_.vertical),i.enable(),i}return _(n,r),n.prototype.enabled=function(){return this.enabled_},n.prototype.enable=function(){this.enabled()||(this.on("mousedown",this.handleMouseDown),this.on("touchstart",this.handleMouseDown),this.on("focus",this.handleFocus),this.on("blur",this.handleBlur),this.on("click",this.handleClick),this.on(this.player_,"controlsvisible",this.update),this.playerEvent&&this.on(this.player_,this.playerEvent,this.update),this.removeClass("disabled"),this.setAttribute("tabindex",0),this.enabled_=!0)},n.prototype.disable=function(){if(this.enabled()){var t=this.bar.el_.ownerDocument;this.off("mousedown",this.handleMouseDown),this.off("touchstart",this.handleMouseDown),this.off("focus",this.handleFocus),this.off("blur",this.handleBlur),this.off("click",this.handleClick),this.off(this.player_,"controlsvisible",this.update),this.off(t,"mousemove",this.handleMouseMove),this.off(t,"mouseup",this.handleMouseUp),this.off(t,"touchmove",this.handleMouseMove),this.off(t,"touchend",this.handleMouseUp),this.removeAttribute("tabindex"),this.addClass("disabled"),this.playerEvent&&this.off(this.player_,this.playerEvent,this.update),this.enabled_=!1}},n.prototype.createEl=function(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{},i=2<arguments.length&&void 0!==arguments[2]?arguments[2]:{};return e.className=e.className+" vjs-slider",e=C({tabIndex:0},e),i=C({role:"slider","aria-valuenow":0,"aria-valuemin":0,"aria-valuemax":100,tabIndex:0},i),r.prototype.createEl.call(this,t,e,i)},n.prototype.handleMouseDown=function(t){var e=this.bar.el_.ownerDocument;t.preventDefault(),X(),this.addClass("vjs-sliding"),this.trigger("slideractive"),this.on(e,"mousemove",this.handleMouseMove),this.on(e,"mouseup",this.handleMouseUp),this.on(e,"touchmove",this.handleMouseMove),this.on(e,"touchend",this.handleMouseUp),this.handleMouseMove(t)},n.prototype.handleMouseMove=function(t){},n.prototype.handleMouseUp=function(){var t=this.bar.el_.ownerDocument;Y(),this.removeClass("vjs-sliding"),this.trigger("sliderinactive"),this.off(t,"mousemove",this.handleMouseMove),this.off(t,"mouseup",this.handleMouseUp),this.off(t,"touchmove",this.handleMouseMove),this.off(t,"touchend",this.handleMouseUp),this.update()},n.prototype.update=function(){if(this.el_){var t=this.getPercent(),e=this.bar;if(e){("number"!=typeof t||t!=t||t<0||t===1/0)&&(t=0);var i=(100*t).toFixed(2)+"%",r=e.el().style;return this.vertical()?r.height=i:r.width=i,t}}},n.prototype.calculateDistance=function(t){var e=Q(this.el_,t);return this.vertical()?e.y:e.x},n.prototype.handleFocus=function(){this.on(this.bar.el_.ownerDocument,"keydown",this.handleKeyPress)},n.prototype.handleKeyPress=function(t){37===t.which||40===t.which?(t.preventDefault(),this.stepBack()):38!==t.which&&39!==t.which||(t.preventDefault(),this.stepForward())},n.prototype.handleBlur=function(){this.off(this.bar.el_.ownerDocument,"keydown",this.handleKeyPress)},n.prototype.handleClick=function(t){t.stopImmediatePropagation(),t.preventDefault()},n.prototype.vertical=function(t){if(void 0===t)return this.vertical_||!1;this.vertical_=!!t,this.vertical_?this.addClass("vjs-slider-vertical"):this.addClass("vjs-slider-horizontal")},n}(Xt);Xt.registerComponent("Slider",en);var rn=function(r){function n(t,e){y(this,n);var i=b(this,r.call(this,t,e));return i.partEls_=[],i.on(t,"progress",i.update),i}return _(n,r),n.prototype.createEl=function(){return r.prototype.createEl.call(this,"div",{className:"vjs-load-progress",innerHTML:'<span class="vjs-control-text"><span>'+this.localize("Loaded")+"</span>: 0%</span>"})},n.prototype.dispose=function(){this.partEls_=null,r.prototype.dispose.call(this)},n.prototype.update=function(t){var e=this.player_.buffered(),i=this.player_.duration(),r=this.player_.bufferedEnd(),n=this.partEls_,s=function(t,e){var i=t/e||0;return 100*(1<=i?1:i)+"%"};this.el_.style.width=s(r,i);for(var a=0;a<e.length;a++){var o=e.start(a),u=e.end(a),l=n[a];l||(l=this.el_.appendChild(M()),n[a]=l),l.style.left=s(o,r),l.style.width=s(u-o,r)}for(var c=n.length;c>e.length;c--)this.el_.removeChild(n[c-1]);n.length=e.length},n}(Xt);Xt.registerComponent("LoadProgressBar",rn);var nn=function(t){function e(){return y(this,e),b(this,t.apply(this,arguments))}return _(e,t),e.prototype.createEl=function(){return t.prototype.createEl.call(this,"div",{className:"vjs-time-tooltip"})},e.prototype.update=function(t,e,i){var r=$(this.el_),n=$(this.player_.el()),s=t.width*e;if(n&&r){var a=t.left-n.left+s,o=t.width-s+(n.right-t.right),u=r.width/2;a<u?u+=u-a:o<u&&(u=o),u<0?u=0:u>r.width&&(u=r.width),this.el_.style.right="-"+u+"px",U(this.el_,i)}},e}(Xt);Xt.registerComponent("TimeTooltip",nn);var sn=function(t){function e(){return y(this,e),b(this,t.apply(this,arguments))}return _(e,t),e.prototype.createEl=function(){return t.prototype.createEl.call(this,"div",{className:"vjs-playOld-progress vjs-slider-bar",innerHTML:'<span class="vjs-control-text"><span>'+this.localize("Progress")+"</span>: 0%</span>"})},e.prototype.update=function(i,r){var n=this;this.rafId_&&this.cancelAnimationFrame(this.rafId_),this.rafId_=this.requestAnimationFrame(function(){var t=Yr(n.player_.scrubbing()?n.player_.getCache().currentTime:n.player_.currentTime(),n.player_.duration()),e=n.getChild("timeTooltip");e&&e.update(i,r,t)})},e}(Xt);sn.prototype.options_={children:[]},ne||ae||sn.prototype.options_.children.push("timeTooltip"),Xt.registerComponent("PlayProgressBar",sn);var an=function(r){function n(t,e){y(this,n);var i=b(this,r.call(this,t,e));return i.update=xt(Ot(i,i.update),25),i}return _(n,r),n.prototype.createEl=function(){return r.prototype.createEl.call(this,"div",{className:"vjs-mouse-display"})},n.prototype.update=function(i,r){var n=this;this.rafId_&&this.cancelAnimationFrame(this.rafId_),this.rafId_=this.requestAnimationFrame(function(){var t=n.player_.duration(),e=Yr(r*t,t);n.el_.style.left=i.width*r+"px",n.getChild("timeTooltip").update(i,r,e)})},n}(Xt);an.prototype.options_={children:["timeTooltip"]},Xt.registerComponent("MouseTimeDisplay",an);var on=function(r){function n(t,e){y(this,n);var i=b(this,r.call(this,t,e));return i.setEventHandlers_(),i}return _(n,r),n.prototype.setEventHandlers_=function(){var t=this;this.update=xt(Ot(this,this.update),30),this.on(this.player_,"timeupdate",this.update),this.on(this.player_,"ended",this.handleEnded),this.updateInterval=null,this.on(this.player_,["playing"],function(){t.clearInterval(t.updateInterval),t.updateInterval=t.setInterval(function(){t.requestAnimationFrame(function(){t.update()})},30)}),this.on(this.player_,["ended","pause","waiting"],function(){t.clearInterval(t.updateInterval)}),this.on(this.player_,["timeupdate","ended"],this.update)},n.prototype.createEl=function(){return r.prototype.createEl.call(this,"div",{className:"vjs-progress-holder"},{"aria-label":this.localize("Progress Bar")})},n.prototype.update_=function(t,e){var i=this.player_.duration();this.el_.setAttribute("aria-valuenow",(100*e).toFixed(2)),this.el_.setAttribute("aria-valuetext",this.localize("progress bar timing: currentTime={1} duration={2}",[Yr(t,i),Yr(i,i)],"{1} of {2}")),this.bar.update($(this.el_),e)},n.prototype.update=function(t){var e=r.prototype.update.call(this);return this.update_(this.getCurrentTime_(),e),e},n.prototype.getCurrentTime_=function(){return this.player_.scrubbing()?this.player_.getCache().currentTime:this.player_.currentTime()},n.prototype.handleEnded=function(t){this.update_(this.player_.duration(),1)},n.prototype.getPercent=function(){var t=this.getCurrentTime_()/this.player_.duration();return 1<=t?1:t||0},n.prototype.handleMouseDown=function(t){rt(t)&&(t.stopPropagation(),this.player_.scrubbing(!0),this.videoWasPlaying=!this.player_.paused(),this.player_.pause(),r.prototype.handleMouseDown.call(this,t))},n.prototype.handleMouseMove=function(t){if(rt(t)){var e=this.calculateDistance(t)*this.player_.duration();e===this.player_.duration()&&(e-=.1),this.player_.currentTime(e)}},n.prototype.enable=function(){r.prototype.enable.call(this);var t=this.getChild("mouseTimeDisplay");t&&t.show()},n.prototype.disable=function(){r.prototype.disable.call(this);var t=this.getChild("mouseTimeDisplay");t&&t.hide()},n.prototype.handleMouseUp=function(t){r.prototype.handleMouseUp.call(this,t),t&&t.stopPropagation(),this.player_.scrubbing(!1),this.player_.trigger({type:"timeupdate",target:this,manuallyTriggered:!0}),this.videoWasPlaying&&Ie(this.player_.play())},n.prototype.stepForward=function(){this.player_.currentTime(this.player_.currentTime()+5)},n.prototype.stepBack=function(){this.player_.currentTime(this.player_.currentTime()-5)},n.prototype.handleAction=function(t){this.player_.paused()?this.player_.play():this.player_.pause()},n.prototype.handleKeyPress=function(t){32===t.which||13===t.which?(t.preventDefault(),this.handleAction(t)):r.prototype.handleKeyPress&&r.prototype.handleKeyPress.call(this,t)},n}(en);on.prototype.options_={children:["loadProgressBar","playProgressBar"],barName:"playProgressBar"},ne||ae||on.prototype.options_.children.splice(1,0,"mouseTimeDisplay"),on.prototype.playerEvent="timeupdate",Xt.registerComponent("SeekBar",on);var un=function(r){function n(t,e){y(this,n);var i=b(this,r.call(this,t,e));return i.handleMouseMove=xt(Ot(i,i.handleMouseMove),25),i.throttledHandleMouseSeek=xt(Ot(i,i.handleMouseSeek),25),i.enable(),i}return _(n,r),n.prototype.createEl=function(){return r.prototype.createEl.call(this,"div",{className:"vjs-progress-control vjs-control"})},n.prototype.handleMouseMove=function(t){var e=this.getChild("seekBar");if(e){var i=e.getChild("mouseTimeDisplay"),r=e.el(),n=$(r),s=Q(r,t).x;1<s?s=1:s<0&&(s=0),i&&i.update(n,s)}},n.prototype.handleMouseSeek=function(t){var e=this.getChild("seekBar");e&&e.handleMouseMove(t)},n.prototype.enabled=function(){return this.enabled_},n.prototype.disable=function(){this.children().forEach(function(t){return t.disable&&t.disable()}),this.enabled()&&(this.off(["mousedown","touchstart"],this.handleMouseDown),this.off(this.el_,"mousemove",this.handleMouseMove),this.handleMouseUp(),this.addClass("disabled"),this.enabled_=!1)},n.prototype.enable=function(){this.children().forEach(function(t){return t.enable&&t.enable()}),this.enabled()||(this.on(["mousedown","touchstart"],this.handleMouseDown),this.on(this.el_,"mousemove",this.handleMouseMove),this.removeClass("disabled"),this.enabled_=!0)},n.prototype.handleMouseDown=function(t){var e=this.el_.ownerDocument,i=this.getChild("seekBar");i&&i.handleMouseDown(t),this.on(e,"mousemove",this.throttledHandleMouseSeek),this.on(e,"touchmove",this.throttledHandleMouseSeek),this.on(e,"mouseup",this.handleMouseUp),this.on(e,"touchend",this.handleMouseUp)},n.prototype.handleMouseUp=function(t){var e=this.el_.ownerDocument,i=this.getChild("seekBar");i&&i.handleMouseUp(t),this.off(e,"mousemove",this.throttledHandleMouseSeek),this.off(e,"touchmove",this.throttledHandleMouseSeek),this.off(e,"mouseup",this.handleMouseUp),this.off(e,"touchend",this.handleMouseUp)},n}(Xt);un.prototype.options_={children:["seekBar"]},Xt.registerComponent("ProgressControl",un);var ln=function(r){function n(t,e){y(this,n);var i=b(this,r.call(this,t,e));return i.on(t,"fullscreenchange",i.handleFullscreenChange),i}return _(n,r),n.prototype.buildCSSClass=function(){return"vjs-fullscreen-control "+r.prototype.buildCSSClass.call(this)},n.prototype.handleFullscreenChange=function(t){this.player_.isFullscreen()?this.controlText("Non-Fullscreen"):this.controlText("Fullscreen")},n.prototype.handleClick=function(t){this.player_.isFullscreen()?this.player_.exitFullscreen():this.player_.requestFullscreen()},n}(Vr);ln.prototype.controlText_="Fullscreen",Xt.registerComponent("FullscreenToggle",ln);var cn=function(t,e){e.tech_&&!e.tech_.featuresVolumeControl&&t.addClass("vjs-hidden"),t.on(e,"loadstart",function(){e.tech_.featuresVolumeControl?t.removeClass("vjs-hidden"):t.addClass("vjs-hidden")})},hn=function(t){function e(){return y(this,e),b(this,t.apply(this,arguments))}return _(e,t),e.prototype.createEl=function(){return t.prototype.createEl.call(this,"div",{className:"vjs-volume-level",innerHTML:'<span class="vjs-control-text"></span>'})},e}(Xt);Xt.registerComponent("VolumeLevel",hn);var dn=function(r){function n(t,e){y(this,n);var i=b(this,r.call(this,t,e));return i.on("slideractive",i.updateLastVolume_),i.on(t,"volumechange",i.updateARIAAttributes),t.ready(function(){return i.updateARIAAttributes()}),i}return _(n,r),n.prototype.createEl=function(){return r.prototype.createEl.call(this,"div",{className:"vjs-volume-bar vjs-slider-bar"},{"aria-label":this.localize("Volume Level"),"aria-live":"polite"})},n.prototype.handleMouseDown=function(t){rt(t)&&r.prototype.handleMouseDown.call(this,t)},n.prototype.handleMouseMove=function(t){rt(t)&&(this.checkMuted(),this.player_.volume(this.calculateDistance(t)))},n.prototype.checkMuted=function(){this.player_.muted()&&this.player_.muted(!1)},n.prototype.getPercent=function(){return this.player_.muted()?0:this.player_.volume()},n.prototype.stepForward=function(){this.checkMuted(),this.player_.volume(this.player_.volume()+.1)},n.prototype.stepBack=function(){this.checkMuted(),this.player_.volume(this.player_.volume()-.1)},n.prototype.updateARIAAttributes=function(t){var e=this.player_.muted()?0:this.volumeAsPercentage_();this.el_.setAttribute("aria-valuenow",e),this.el_.setAttribute("aria-valuetext",e+"%")},n.prototype.volumeAsPercentage_=function(){return Math.round(100*this.player_.volume())},n.prototype.updateLastVolume_=function(){var t=this,e=this.player_.volume();this.one("sliderinactive",function(){0===t.player_.volume()&&t.player_.lastVolume_(e)})},n}(en);dn.prototype.options_={children:["volumeLevel"],barName:"volumeLevel"},dn.prototype.playerEvent="volumechange",Xt.registerComponent("VolumeBar",dn);var pn=function(r){function n(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{};y(this,n),e.vertical=e.vertical||!1,("undefined"==typeof e.volumeBar||w(e.volumeBar))&&(e.volumeBar=e.volumeBar||{},e.volumeBar.vertical=e.vertical);var i=b(this,r.call(this,t,e));return cn(i,t),i.throttledHandleMouseMove=xt(Ot(i,i.handleMouseMove),25),i.on("mousedown",i.handleMouseDown),i.on("touchstart",i.handleMouseDown),i.on(i.volumeBar,["focus","slideractive"],function(){i.volumeBar.addClass("vjs-slider-active"),i.addClass("vjs-slider-active"),i.trigger("slideractive")}),i.on(i.volumeBar,["blur","sliderinactive"],function(){i.volumeBar.removeClass("vjs-slider-active"),i.removeClass("vjs-slider-active"),i.trigger("sliderinactive")}),i}return _(n,r),n.prototype.createEl=function(){var t="vjs-volume-horizontal";return this.options_.vertical&&(t="vjs-volume-vertical"),r.prototype.createEl.call(this,"div",{className:"vjs-volume-control vjs-control "+t})},n.prototype.handleMouseDown=function(t){var e=this.el_.ownerDocument;this.on(e,"mousemove",this.throttledHandleMouseMove),this.on(e,"touchmove",this.throttledHandleMouseMove),this.on(e,"mouseup",this.handleMouseUp),this.on(e,"touchend",this.handleMouseUp)},n.prototype.handleMouseUp=function(t){var e=this.el_.ownerDocument;this.off(e,"mousemove",this.throttledHandleMouseMove),this.off(e,"touchmove",this.throttledHandleMouseMove),this.off(e,"mouseup",this.handleMouseUp),this.off(e,"touchend",this.handleMouseUp)},n.prototype.handleMouseMove=function(t){this.volumeBar.handleMouseMove(t)},n}(Xt);pn.prototype.options_={children:["volumeBar"]},Xt.registerComponent("VolumeControl",pn);var fn=function(r){function n(t,e){y(this,n);var i=b(this,r.call(this,t,e));return cn(i,t),i.on(t,["loadstart","volumechange"],i.update),i}return _(n,r),n.prototype.buildCSSClass=function(){return"vjs-mute-control "+r.prototype.buildCSSClass.call(this)},n.prototype.handleClick=function(t){var e=this.player_.volume(),i=this.player_.lastVolume_();if(0===e){var r=i<.1?.1:i;this.player_.volume(r),this.player_.muted(!1)}else this.player_.muted(!this.player_.muted())},n.prototype.update=function(t){this.updateIcon_(),this.updateControlText_()},n.prototype.updateIcon_=function(){var t=this.player_.volume(),e=3;0===t||this.player_.muted()?e=0:t<.33?e=1:t<.67&&(e=2);for(var i=0;i<4;i++)F(this.el_,"vjs-vol-"+i);j(this.el_,"vjs-vol-"+e)},n.prototype.updateControlText_=function(){var t=this.player_.muted()||0===this.player_.volume()?"Unmute":"Mute";this.controlText()!==t&&this.controlText(t)},n}(Vr);fn.prototype.controlText_="Mute",Xt.registerComponent("MuteToggle",fn);var mn=function(r){function n(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{};y(this,n),"undefined"!=typeof e.inline?e.inline=e.inline:e.inline=!0,("undefined"==typeof e.volumeControl||w(e.volumeControl))&&(e.volumeControl=e.volumeControl||{},e.volumeControl.vertical=!e.inline);var i=b(this,r.call(this,t,e));return cn(i,t),i.on(i.volumeControl,["slideractive"],i.sliderActive_),i.on(i.volumeControl,["sliderinactive"],i.sliderInactive_),i}return _(n,r),n.prototype.sliderActive_=function(){this.addClass("vjs-slider-active")},n.prototype.sliderInactive_=function(){this.removeClass("vjs-slider-active")},n.prototype.createEl=function(){var t="vjs-volume-panel-horizontal";return this.options_.inline||(t="vjs-volume-panel-vertical"),r.prototype.createEl.call(this,"div",{className:"vjs-volume-panel vjs-control "+t})},n}(Xt);mn.prototype.options_={children:["muteToggle","volumeControl"]},Xt.registerComponent("VolumePanel",mn);var gn=function(r){function n(t,e){y(this,n);var i=b(this,r.call(this,t,e));return e&&(i.menuButton_=e.menuButton),i.focusedChild_=-1,i.on("keydown",i.handleKeyPress),i}return _(n,r),n.prototype.addItem=function(e){this.addChild(e),e.on("click",Ot(this,function(t){this.menuButton_&&(this.menuButton_.unpressButton(),"CaptionSettingsMenuItem"!==e.name()&&this.menuButton_.focus())}))},n.prototype.createEl=function(){var t=this.options_.contentElType||"ul";this.contentEl_=M(t,{className:"vjs-menu-content"}),this.contentEl_.setAttribute("role","menu");var e=r.prototype.createEl.call(this,"div",{append:this.contentEl_,className:"vjs-menu"});return e.appendChild(this.contentEl_),_t(e,"click",function(t){t.preventDefault(),t.stopImmediatePropagation()}),e},n.prototype.dispose=function(){this.contentEl_=null,r.prototype.dispose.call(this)},n.prototype.handleKeyPress=function(t){37===t.which||40===t.which?(t.preventDefault(),this.stepForward()):38!==t.which&&39!==t.which||(t.preventDefault(),this.stepBack())},n.prototype.stepForward=function(){var t=0;void 0!==this.focusedChild_&&(t=this.focusedChild_+1),this.focus(t)},n.prototype.stepBack=function(){var t=0;void 0!==this.focusedChild_&&(t=this.focusedChild_-1),this.focus(t)},n.prototype.focus=function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:0,e=this.children().slice();e.length&&e[0].className&&/vjs-menu-title/.test(e[0].className)&&e.shift(),0<e.length&&(t<0?t=0:t>=e.length&&(t=e.length-1),e[this.focusedChild_=t].el_.focus())},n}(Xt);Xt.registerComponent("Menu",gn);var yn=function(n){function s(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{};y(this,s);var i=b(this,n.call(this,t,e));i.menuButton_=new Vr(t,e),i.menuButton_.controlText(i.controlText_),i.menuButton_.el_.setAttribute("aria-haspopup","true");var r=Vr.prototype.buildCSSClass();return i.menuButton_.el_.className=i.buildCSSClass()+" "+r,i.menuButton_.removeClass("vjs-control"),i.addChild(i.menuButton_),i.update(),i.enabled_=!0,i.on(i.menuButton_,"tap",i.handleClick),i.on(i.menuButton_,"click",i.handleClick),i.on(i.menuButton_,"focus",i.handleFocus),i.on(i.menuButton_,"blur",i.handleBlur),i.on("keydown",i.handleSubmenuKeyPress),i}return _(s,n),s.prototype.update=function(){var t=this.createMenu();this.menu&&(this.menu.dispose(),this.removeChild(this.menu)),this.menu=t,this.addChild(t),this.buttonPressed_=!1,this.menuButton_.el_.setAttribute("aria-expanded","false"),this.items&&this.items.length<=this.hideThreshold_?this.hide():this.show()},s.prototype.createMenu=function(){var t=new gn(this.player_,{menuButton:this});if(this.hideThreshold_=0,this.options_.title){var e=M("li",{className:"vjs-menu-title",innerHTML:zt(this.options_.title),tabIndex:-1});this.hideThreshold_+=1,t.children_.unshift(e),N(e,t.contentEl())}if(this.items=this.createItems(),this.items)for(var i=0;i<this.items.length;i++)t.addItem(this.items[i]);return t},s.prototype.createItems=function(){},s.prototype.createEl=function(){return n.prototype.createEl.call(this,"div",{className:this.buildWrapperCSSClass()},{})},s.prototype.buildWrapperCSSClass=function(){var t="vjs-menu-button";return!0===this.options_.inline?t+="-inline":t+="-popup","vjs-menu-button "+t+" "+Vr.prototype.buildCSSClass()+" "+n.prototype.buildCSSClass.call(this)},s.prototype.buildCSSClass=function(){var t="vjs-menu-button";return!0===this.options_.inline?t+="-inline":t+="-popup","vjs-menu-button "+t+" "+n.prototype.buildCSSClass.call(this)},s.prototype.controlText=function(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:this.menuButton_.el();return this.menuButton_.controlText(t,e)},s.prototype.handleClick=function(t){this.one(this.menu.contentEl(),"mouseleave",Ot(this,function(t){this.unpressButton(),this.el_.blur()})),this.buttonPressed_?this.unpressButton():this.pressButton()},s.prototype.focus=function(){this.menuButton_.focus()},s.prototype.blur=function(){this.menuButton_.blur()},s.prototype.handleFocus=function(){_t(p,"keydown",Ot(this,this.handleKeyPress))},s.prototype.handleBlur=function(){bt(p,"keydown",Ot(this,this.handleKeyPress))},s.prototype.handleKeyPress=function(t){27===t.which||9===t.which?(this.buttonPressed_&&this.unpressButton(),9!==t.which&&(t.preventDefault(),this.menuButton_.el_.focus())):38!==t.which&&40!==t.which||this.buttonPressed_||(this.pressButton(),t.preventDefault())},s.prototype.handleSubmenuKeyPress=function(t){27!==t.which&&9!==t.which||(this.buttonPressed_&&this.unpressButton(),9!==t.which&&(t.preventDefault(),this.menuButton_.el_.focus()))},s.prototype.pressButton=function(){if(this.enabled_){if(this.buttonPressed_=!0,this.menu.lockShowing(),this.menuButton_.el_.setAttribute("aria-expanded","true"),ne&&D())return;this.menu.focus()}},s.prototype.unpressButton=function(){this.enabled_&&(this.buttonPressed_=!1,this.menu.unlockShowing(),this.menuButton_.el_.setAttribute("aria-expanded","false"))},s.prototype.disable=function(){this.unpressButton(),this.enabled_=!1,this.addClass("vjs-disabled"),this.menuButton_.disable()},s.prototype.enable=function(){this.enabled_=!0,this.removeClass("vjs-disabled"),this.menuButton_.enable()},s}(Xt);Xt.registerComponent("MenuButton",yn);var vn=function(s){function a(t,e){y(this,a);var i=e.tracks,r=b(this,s.call(this,t,e));if(r.items.length<=1&&r.hide(),!i)return b(r);var n=Ot(r,r.update);return i.addEventListener("removetrack",n),i.addEventListener("addtrack",n),r.player_.on("ready",n),r.player_.on("dispose",function(){i.removeEventListener("removetrack",n),i.removeEventListener("addtrack",n)}),r}return _(a,s),a}(yn);Xt.registerComponent("TrackButton",vn);var _n=function(r){function n(t,e){y(this,n);var i=b(this,r.call(this,t,e));return i.selectable=e.selectable,i.isSelected_=e.selected||!1,i.selected(i.isSelected_),i.selectable?i.el_.setAttribute("role","menuitemcheckbox"):i.el_.setAttribute("role","menuitem"),i}return _(n,r),n.prototype.createEl=function(t,e,i){return this.nonIconControl=!0,r.prototype.createEl.call(this,"li",C({className:"vjs-menu-item",innerHTML:'<span class="vjs-menu-item-text">'+this.localize(this.options_.label)+"</span>",tabIndex:-1},e),i)},n.prototype.handleClick=function(t){this.selected(!0)},n.prototype.selected=function(t){this.selectable&&(t?(this.addClass("vjs-selected"),this.el_.setAttribute("aria-checked","true"),this.controlText(", selected"),this.isSelected_=!0):(this.removeClass("vjs-selected"),this.el_.setAttribute("aria-checked","false"),this.controlText(""),this.isSelected_=!1))},n}(Rr);Xt.registerComponent("MenuItem",_n);var bn=function(u){function l(t,e){y(this,l);var i=e.track,r=t.textTracks();e.label=i.label||i.language||"Unknown",e.selected="showing"===i.mode;var n=b(this,u.call(this,t,e));n.track=i;var s=function(){for(var t=arguments.length,e=Array(t),i=0;i<t;i++)e[i]=arguments[i];n.handleTracksChange.apply(n,e)},a=function(){for(var t=arguments.length,e=Array(t),i=0;i<t;i++)e[i]=arguments[i];n.handleSelectedLanguageChange.apply(n,e)};if(t.on(["loadstart","texttrackchange"],s),r.addEventListener("change",s),r.addEventListener("selectedlanguagechange",a),n.on("dispose",function(){t.off(["loadstart","texttrackchange"],s),r.removeEventListener("change",s),r.removeEventListener("selectedlanguagechange",a)}),void 0===r.onchange){var o=void 0;n.on(["tap","click"],function(){if("object"!==v(g.Event))try{o=new g.Event("change")}catch(t){}o||(o=p.createEvent("Event")).initEvent("change",!0,!0),r.dispatchEvent(o)})}return n.handleTracksChange(),n}return _(l,u),l.prototype.handleClick=function(t){var e=this.track.kind,i=this.track.kinds,r=this.player_.textTracks();if(i||(i=[e]),u.prototype.handleClick.call(this,t),r)for(var n=0;n<r.length;n++){var s=r[n];s===this.track&&-1<i.indexOf(s.kind)?"showing"!==s.mode&&(s.mode="showing"):"disabled"!==s.mode&&(s.mode="disabled")}},l.prototype.handleTracksChange=function(t){var e="showing"===this.track.mode;e!==this.isSelected_&&this.selected(e)},l.prototype.handleSelectedLanguageChange=function(t){if("showing"===this.track.mode){var e=this.player_.cache_.selectedLanguage;if(e&&e.enabled&&e.language===this.track.language&&e.kind!==this.track.kind)return;this.player_.cache_.selectedLanguage={enabled:!0,language:this.track.language,kind:this.track.kind}}},l.prototype.dispose=function(){this.track=null,u.prototype.dispose.call(this)},l}(_n);Xt.registerComponent("TextTrackMenuItem",bn);var Tn=function(i){function r(t,e){return y(this,r),e.track={player:t,kind:e.kind,kinds:e.kinds,default:!1,mode:"disabled"},e.kinds||(e.kinds=[e.kind]),e.label?e.track.label=e.label:e.track.label=e.kinds.join(" and ")+" off",e.selectable=!0,b(this,i.call(this,t,e))}return _(r,i),r.prototype.handleTracksChange=function(t){for(var e=this.player().textTracks(),i=!0,r=0,n=e.length;r<n;r++){var s=e[r];if(-1<this.options_.kinds.indexOf(s.kind)&&"showing"===s.mode){i=!1;break}}i!==this.isSelected_&&this.selected(i)},r.prototype.handleSelectedLanguageChange=function(t){for(var e=this.player().textTracks(),i=!0,r=0,n=e.length;r<n;r++){var s=e[r];if(-1<["captions","descriptions","subtitles"].indexOf(s.kind)&&"showing"===s.mode){i=!1;break}}i&&(this.player_.cache_.selectedLanguage={enabled:!1})},r}(bn);Xt.registerComponent("OffTextTrackMenuItem",Tn);var Sn=function(i){function r(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{};return y(this,r),e.tracks=t.textTracks(),b(this,i.call(this,t,e))}return _(r,i),r.prototype.createItems=function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:[],e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:bn,i=void 0;this.label_&&(i=this.label_+" off"),t.push(new Tn(this.player_,{kinds:this.kinds_,kind:this.kind_,label:i})),this.hideThreshold_+=1;var r=this.player_.textTracks();Array.isArray(this.kinds_)||(this.kinds_=[this.kind_]);for(var n=0;n<r.length;n++){var s=r[n];if(-1<this.kinds_.indexOf(s.kind)){var a=new e(this.player_,{track:s,selectable:!0});a.addClass("vjs-"+s.kind+"-menu-item"),t.push(a)}}return t},r}(vn);Xt.registerComponent("TextTrackButton",Sn);var kn=function(a){function o(t,e){y(this,o);var i=e.track,r=e.cue,n=t.currentTime();e.selectable=!0,e.label=r.text,e.selected=r.startTime<=n&&n<r.endTime;var s=b(this,a.call(this,t,e));return s.track=i,s.cue=r,i.addEventListener("cuechange",Ot(s,s.update)),s}return _(o,a),o.prototype.handleClick=function(t){a.prototype.handleClick.call(this),this.player_.currentTime(this.cue.startTime),this.update(this.cue.startTime)},o.prototype.update=function(t){var e=this.cue,i=this.player_.currentTime();this.selected(e.startTime<=i&&i<e.endTime)},o}(_n);Xt.registerComponent("ChaptersTrackMenuItem",kn);var Cn=function(r){function n(t,e,i){return y(this,n),b(this,r.call(this,t,e,i))}return _(n,r),n.prototype.buildCSSClass=function(){return"vjs-chapters-button "+r.prototype.buildCSSClass.call(this)},n.prototype.buildWrapperCSSClass=function(){return"vjs-chapters-button "+r.prototype.buildWrapperCSSClass.call(this)},n.prototype.update=function(t){this.track_&&(!t||"addtrack"!==t.type&&"removetrack"!==t.type)||this.setTrack(this.findChaptersTrack()),r.prototype.update.call(this)},n.prototype.setTrack=function(t){if(this.track_!==t){if(this.updateHandler_||(this.updateHandler_=this.update.bind(this)),this.track_){var e=this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_);e&&e.removeEventListener("load",this.updateHandler_),this.track_=null}if(this.track_=t,this.track_){this.track_.mode="hidden";var i=this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_);i&&i.addEventListener("load",this.updateHandler_)}}},n.prototype.findChaptersTrack=function(){for(var t=this.player_.textTracks()||[],e=t.length-1;0<=e;e--){var i=t[e];if(i.kind===this.kind_)return i}},n.prototype.getMenuCaption=function(){return this.track_&&this.track_.label?this.track_.label:this.localize(zt(this.kind_))},n.prototype.createMenu=function(){return this.options_.title=this.getMenuCaption(),r.prototype.createMenu.call(this)},n.prototype.createItems=function(){var t=[];if(!this.track_)return t;var e=this.track_.cues;if(!e)return t;for(var i=0,r=e.length;i<r;i++){var n=e[i],s=new kn(this.player_,{track:this.track_,cue:n});t.push(s)}return t},n}(Sn);Cn.prototype.kind_="chapters",Cn.prototype.controlText_="Chapters",Xt.registerComponent("ChaptersButton",Cn);var En=function(a){function o(t,e,i){y(this,o);var r=b(this,a.call(this,t,e,i)),n=t.textTracks(),s=Ot(r,r.handleTracksChange);return n.addEventListener("change",s),r.on("dispose",function(){n.removeEventListener("change",s)}),r}return _(o,a),o.prototype.handleTracksChange=function(t){for(var e=this.player().textTracks(),i=!1,r=0,n=e.length;r<n;r++){var s=e[r];if(s.kind!==this.kind_&&"showing"===s.mode){i=!0;break}}i?this.disable():this.enable()},o.prototype.buildCSSClass=function(){return"vjs-descriptions-button "+a.prototype.buildCSSClass.call(this)},o.prototype.buildWrapperCSSClass=function(){return"vjs-descriptions-button "+a.prototype.buildWrapperCSSClass.call(this)},o}(Sn);En.prototype.kind_="descriptions",En.prototype.controlText_="Descriptions",Xt.registerComponent("DescriptionsButton",En);var wn=function(r){function n(t,e,i){return y(this,n),b(this,r.call(this,t,e,i))}return _(n,r),n.prototype.buildCSSClass=function(){return"vjs-subtitles-button "+r.prototype.buildCSSClass.call(this)},n.prototype.buildWrapperCSSClass=function(){return"vjs-subtitles-button "+r.prototype.buildWrapperCSSClass.call(this)},n}(Sn);wn.prototype.kind_="subtitles",wn.prototype.controlText_="Subtitles",Xt.registerComponent("SubtitlesButton",wn);var An=function(r){function n(t,e){y(this,n),e.track={player:t,kind:e.kind,label:e.kind+" settings",selectable:!1,default:!1,mode:"disabled"},e.selectable=!1,e.name="CaptionSettingsMenuItem";var i=b(this,r.call(this,t,e));return i.addClass("vjs-texttrack-settings"),i.controlText(", opens "+e.kind+" settings dialog"),i}return _(n,r),n.prototype.handleClick=function(t){this.player().getChild("textTrackSettings").open()},n}(bn);Xt.registerComponent("CaptionSettingsMenuItem",An);var Ln=function(r){function n(t,e,i){return y(this,n),b(this,r.call(this,t,e,i))}return _(n,r),n.prototype.buildCSSClass=function(){return"vjs-captions-button "+r.prototype.buildCSSClass.call(this)},n.prototype.buildWrapperCSSClass=function(){return"vjs-captions-button "+r.prototype.buildWrapperCSSClass.call(this)},n.prototype.createItems=function(){var t=[];return this.player().tech_&&this.player().tech_.featuresNativeTextTracks||!this.player().getChild("textTrackSettings")||(t.push(new An(this.player_,{kind:this.kind_})),this.hideThreshold_+=1),r.prototype.createItems.call(this,t)},n}(Sn);Ln.prototype.kind_="captions",Ln.prototype.controlText_="Captions",Xt.registerComponent("CaptionsButton",Ln);var Pn=function(n){function t(){return y(this,t),b(this,n.apply(this,arguments))}return _(t,n),t.prototype.createEl=function(t,e,i){var r='<span class="vjs-menu-item-text">'+this.localize(this.options_.label);return"captions"===this.options_.track.kind&&(r+='\n        <span aria-hidden="true" class="vjs-icon-placeholder"></span>\n        <span class="vjs-control-text"> '+this.localize("Captions")+"</span>\n      "),r+="</span>",n.prototype.createEl.call(this,t,C({innerHTML:r},e),i)},t}(bn);Xt.registerComponent("SubsCapsMenuItem",Pn);var On=function(r){function n(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{};y(this,n);var i=b(this,r.call(this,t,e));return i.label_="subtitles",-1<["en","en-us","en-ca","fr-ca"].indexOf(i.player_.language_)&&(i.label_="captions"),i.menuButton_.controlText(zt(i.label_)),i}return _(n,r),n.prototype.buildCSSClass=function(){return"vjs-subs-caps-button "+r.prototype.buildCSSClass.call(this)},n.prototype.buildWrapperCSSClass=function(){return"vjs-subs-caps-button "+r.prototype.buildWrapperCSSClass.call(this)},n.prototype.createItems=function(){var t=[];return this.player().tech_&&this.player().tech_.featuresNativeTextTracks||!this.player().getChild("textTrackSettings")||(t.push(new An(this.player_,{kind:this.label_})),this.hideThreshold_+=1),t=r.prototype.createItems.call(this,t,Pn)},n}(Sn);On.prototype.kinds_=["captions","subtitles"],On.prototype.controlText_="Subtitles",Xt.registerComponent("SubsCapsButton",On);var xn=function(a){function o(t,e){y(this,o);var i=e.track,r=t.audioTracks();e.label=i.label||i.language||"Unknown",e.selected=i.enabled;var n=b(this,a.call(this,t,e));n.track=i;var s=function(){for(var t=arguments.length,e=Array(t),i=0;i<t;i++)e[i]=arguments[i];n.handleTracksChange.apply(n,e)};return r.addEventListener("change",s),n.on("dispose",function(){r.removeEventListener("change",s)}),n}return _(o,a),o.prototype.handleClick=function(t){var e=this.player_.audioTracks();a.prototype.handleClick.call(this,t);for(var i=0;i<e.length;i++){var r=e[i];r.enabled=r===this.track}},o.prototype.handleTracksChange=function(t){this.selected(this.track.enabled)},o}(_n);Xt.registerComponent("AudioTrackMenuItem",xn);var In=function(i){function r(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{};return y(this,r),e.tracks=t.audioTracks(),b(this,i.call(this,t,e))}return _(r,i),r.prototype.buildCSSClass=function(){return"vjs-audio-button "+i.prototype.buildCSSClass.call(this)},r.prototype.buildWrapperCSSClass=function(){return"vjs-audio-button "+i.prototype.buildWrapperCSSClass.call(this)},r.prototype.createItems=function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:[];this.hideThreshold_=1;for(var e=this.player_.audioTracks(),i=0;i<e.length;i++){var r=e[i];t.push(new xn(this.player_,{track:r,selectable:!0}))}return t},r}(vn);In.prototype.controlText_="Audio Track",Xt.registerComponent("AudioTrackButton",In);var Dn=function(s){function a(t,e){y(this,a);var i=e.rate,r=parseFloat(i,10);e.label=i,e.selected=1===r,e.selectable=!0;var n=b(this,s.call(this,t,e));return n.label=i,n.rate=r,n.on(t,"ratechange",n.update),n}return _(a,s),a.prototype.handleClick=function(t){s.prototype.handleClick.call(this),this.player().playbackRate(this.rate)},a.prototype.update=function(t){this.selected(this.player().playbackRate()===this.rate)},a}(_n);Dn.prototype.contentElType="button",Xt.registerComponent("PlaybackRateMenuItem",Dn);var Rn=function(r){function n(t,e){y(this,n);var i=b(this,r.call(this,t,e));return i.updateVisibility(),i.updateLabel(),i.on(t,"loadstart",i.updateVisibility),i.on(t,"ratechange",i.updateLabel),i}return _(n,r),n.prototype.createEl=function(){var t=r.prototype.createEl.call(this);return this.labelEl_=M("div",{className:"vjs-playback-rate-value",innerHTML:"1x"}),t.appendChild(this.labelEl_),t},n.prototype.dispose=function(){this.labelEl_=null,r.prototype.dispose.call(this)},n.prototype.buildCSSClass=function(){return"vjs-playback-rate "+r.prototype.buildCSSClass.call(this)},n.prototype.buildWrapperCSSClass=function(){return"vjs-playback-rate "+r.prototype.buildWrapperCSSClass.call(this)},n.prototype.createMenu=function(){var t=new gn(this.player()),e=this.playbackRates();if(e)for(var i=e.length-1;0<=i;i--)t.addChild(new Dn(this.player(),{rate:e[i]+"x"}));return t},n.prototype.updateARIAAttributes=function(){this.el().setAttribute("aria-valuenow",this.player().playbackRate())},n.prototype.handleClick=function(t){for(var e=this.player().playbackRate(),i=this.playbackRates(),r=i[0],n=0;n<i.length;n++)if(i[n]>e){r=i[n];break}this.player().playbackRate(r)},n.prototype.playbackRates=function(){return this.options_.playbackRates||this.options_.playerOptions&&this.options_.playerOptions.playbackRates},n.prototype.playbackRateSupported=function(){return this.player().tech_&&this.player().tech_.featuresPlaybackRate&&this.playbackRates()&&0<this.playbackRates().length},n.prototype.updateVisibility=function(t){this.playbackRateSupported()?this.removeClass("vjs-hidden"):this.addClass("vjs-hidden")},n.prototype.updateLabel=function(t){this.playbackRateSupported()&&(this.labelEl_.innerHTML=this.player().playbackRate()+"x")},n}(yn);Rn.prototype.controlText_="Playback Rate",Xt.registerComponent("PlaybackRateMenuButton",Rn);var Mn=function(t){function e(){return y(this,e),b(this,t.apply(this,arguments))}return _(e,t),e.prototype.buildCSSClass=function(){return"vjs-spacer "+t.prototype.buildCSSClass.call(this)},e.prototype.createEl=function(){return t.prototype.createEl.call(this,"div",{className:this.buildCSSClass()})},e}(Xt);Xt.registerComponent("Spacer",Mn);var Un=function(e){function t(){return y(this,t),b(this,e.apply(this,arguments))}return _(t,e),t.prototype.buildCSSClass=function(){return"vjs-custom-control-spacer "+e.prototype.buildCSSClass.call(this)},t.prototype.createEl=function(){var t=e.prototype.createEl.call(this,{className:this.buildCSSClass()});return t.innerHTML=" ",t},t}(Mn);Xt.registerComponent("CustomControlSpacer",Un);var Nn=function(t){function e(){return y(this,e),b(this,t.apply(this,arguments))}return _(e,t),e.prototype.createEl=function(){return t.prototype.createEl.call(this,"div",{className:"vjs-control-bar",dir:"ltr"})},e}(Xt);Nn.prototype.options_={children:["playToggle","volumePanel","currentTimeDisplay","timeDivider","durationDisplay","progressControl","liveDisplay","remainingTimeDisplay","customControlSpacer","playbackRateMenuButton","chaptersButton","descriptionsButton","subsCapsButton","audioTrackButton","fullscreenToggle"]},Xt.registerComponent("ControlBar",Nn);var Bn=function(r){function n(t,e){y(this,n);var i=b(this,r.call(this,t,e));return i.on(t,"error",i.open),i}return _(n,r),n.prototype.buildCSSClass=function(){return"vjs-error-display "+r.prototype.buildCSSClass.call(this)},n.prototype.content=function(){var t=this.player().error();return t?this.localize(t.message):""},n}(Ne);Bn.prototype.options_=Gt(Ne.prototype.options_,{pauseOnOpen:!1,fillAlways:!0,temporary:!1,uncloseable:!0}),Xt.registerComponent("ErrorDisplay",Bn);var jn="vjs-text-track-settings",Fn=["#000","Black"],Hn=["#00F","Blue"],Vn=["#0FF","Cyan"],qn=["#0F0","Green"],Wn=["#F0F","Magenta"],zn=["#F00","Red"],Gn=["#FFF","White"],Xn=["#FF0","Yellow"],Yn=["1","Opaque"],$n=["0.5","Semi-Transparent"],Kn=["0","Transparent"],Qn={backgroundColor:{selector:".vjs-bg-color > select",id:"captions-background-color-%s",label:"Color",options:[Fn,Gn,zn,qn,Hn,Xn,Wn,Vn]},backgroundOpacity:{selector:".vjs-bg-opacity > select",id:"captions-background-opacity-%s",label:"Transparency",options:[Yn,$n,Kn]},color:{selector:".vjs-fg-color > select",id:"captions-foreground-color-%s",label:"Color",options:[Gn,Fn,zn,qn,Hn,Xn,Wn,Vn]},edgeStyle:{selector:".vjs-edge-style > select",id:"%s",label:"Text Edge Style",options:[["none","None"],["raised","Raised"],["depressed","Depressed"],["uniform","Uniform"],["dropshadow","Dropshadow"]]},fontFamily:{selector:".vjs-font-family > select",id:"captions-font-family-%s",label:"Font Family",options:[["proportionalSansSerif","Proportional Sans-Serif"],["monospaceSansSerif","Monospace Sans-Serif"],["proportionalSerif","Proportional Serif"],["monospaceSerif","Monospace Serif"],["casual","Casual"],["script","Script"],["small-caps","Small Caps"]]},fontPercent:{selector:".vjs-font-percent > select",id:"captions-font-size-%s",label:"Font Size",options:[["0.50","50%"],["0.75","75%"],["1.00","100%"],["1.25","125%"],["1.50","150%"],["1.75","175%"],["2.00","200%"],["3.00","300%"],["4.00","400%"]],default:2,parser:function(t){return"1.00"===t?null:Number(t)}},textOpacity:{selector:".vjs-text-opacity > select",id:"captions-foreground-opacity-%s",label:"Transparency",options:[Yn,$n]},windowColor:{selector:".vjs-window-color > select",id:"captions-window-color-%s",label:"Color"},windowOpacity:{selector:".vjs-window-opacity > select",id:"captions-window-opacity-%s",label:"Transparency",options:[Kn,$n,Yn]}};function Jn(t,e){if(e&&(t=e(t)),t&&"none"!==t)return t}Qn.windowColor.options=Qn.backgroundColor.options;var Zn=function(r){function n(t,e){y(this,n),e.temporary=!1;var i=b(this,r.call(this,t,e));return i.updateDisplay=Ot(i,i.updateDisplay),i.fill(),i.hasBeenOpened_=i.hasBeenFilled_=!0,i.endDialog=M("p",{className:"vjs-control-text",textContent:i.localize("End of dialog window.")}),i.el().appendChild(i.endDialog),i.setDefaults(),void 0===e.persistTextTrackSettings&&(i.options_.persistTextTrackSettings=i.options_.playerOptions.persistTextTrackSettings),i.on(i.$(".vjs-done-button"),"click",function(){i.saveSettings(),i.close()}),i.on(i.$(".vjs-default-button"),"click",function(){i.setDefaults(),i.updateDisplay()}),k(Qn,function(t){i.on(i.$(t.selector),"change",i.updateDisplay)}),i.options_.persistTextTrackSettings&&i.restoreSettings(),i}return _(n,r),n.prototype.dispose=function(){this.endDialog=null,r.prototype.dispose.call(this)},n.prototype.createElSelect_=function(t){var i=this,e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:"",r=2<arguments.length&&void 0!==arguments[2]?arguments[2]:"label",n=Qn[t],s=n.id.replace("%s",this.id_),a=[e,s].join(" ").trim();return["<"+r+' id="'+s+'" class="'+("label"===r?"vjs-label":"")+'">',this.localize(n.label),"</"+r+">",'<select aria-labelledby="'+a+'">'].concat(n.options.map(function(t){var e=s+"-"+t[1].replace(/\W+/g,"");return['<option id="'+e+'" value="'+t[0]+'" ','aria-labelledby="'+a+" "+e+'">',i.localize(t[1]),"</option>"].join("")})).concat("</select>").join("")},n.prototype.createElFgColor_=function(){var t="captions-text-legend-"+this.id_;return['<fieldset class="vjs-fg-color vjs-track-setting">','<legend id="'+t+'">',this.localize("Text"),"</legend>",this.createElSelect_("color",t),'<span class="vjs-text-opacity vjs-opacity">',this.createElSelect_("textOpacity",t),"</span>","</fieldset>"].join("")},n.prototype.createElBgColor_=function(){var t="captions-background-"+this.id_;return['<fieldset class="vjs-bg-color vjs-track-setting">','<legend id="'+t+'">',this.localize("Background"),"</legend>",this.createElSelect_("backgroundColor",t),'<span class="vjs-bg-opacity vjs-opacity">',this.createElSelect_("backgroundOpacity",t),"</span>","</fieldset>"].join("")},n.prototype.createElWinColor_=function(){var t="captions-window-"+this.id_;return['<fieldset class="vjs-window-color vjs-track-setting">','<legend id="'+t+'">',this.localize("Window"),"</legend>",this.createElSelect_("windowColor",t),'<span class="vjs-window-opacity vjs-opacity">',this.createElSelect_("windowOpacity",t),"</span>","</fieldset>"].join("")},n.prototype.createElColors_=function(){return M("div",{className:"vjs-track-settings-colors",innerHTML:[this.createElFgColor_(),this.createElBgColor_(),this.createElWinColor_()].join("")})},n.prototype.createElFont_=function(){return M("div",{className:"vjs-track-settings-font",innerHTML:['<fieldset class="vjs-font-percent vjs-track-setting">',this.createElSelect_("fontPercent","","legend"),"</fieldset>",'<fieldset class="vjs-edge-style vjs-track-setting">',this.createElSelect_("edgeStyle","","legend"),"</fieldset>",'<fieldset class="vjs-font-family vjs-track-setting">',this.createElSelect_("fontFamily","","legend"),"</fieldset>"].join("")})},n.prototype.createElControls_=function(){var t=this.localize("restore all settings to the default values");return M("div",{className:"vjs-track-settings-controls",innerHTML:['<button class="vjs-default-button" title="'+t+'">',this.localize("Reset"),'<span class="vjs-control-text"> '+t+"</span>","</button>",'<button class="vjs-done-button">'+this.localize("Done")+"</button>"].join("")})},n.prototype.content=function(){return[this.createElColors_(),this.createElFont_(),this.createElControls_()]},n.prototype.label=function(){return this.localize("Caption Settings Dialog")},n.prototype.description=function(){return this.localize("Beginning of dialog window. Escape will cancel and close the window.")},n.prototype.buildCSSClass=function(){return r.prototype.buildCSSClass.call(this)+" vjs-text-track-settings"},n.prototype.getValues=function(){var a=this;return function(i,r){var t=2<arguments.length&&void 0!==arguments[2]?arguments[2]:0;return S(i).reduce(function(t,e){return r(t,i[e],e)},t)}(Qn,function(t,e,i){var r,n,s=(r=a.$(e.selector),n=e.parser,Jn(r.options[r.options.selectedIndex].value,n));return void 0!==s&&(t[i]=s),t},{})},n.prototype.setValues=function(i){var r=this;k(Qn,function(t,e){!function(t,e,i){if(e)for(var r=0;r<t.options.length;r++)if(Jn(t.options[r].value,i)===e){t.selectedIndex=r;break}}(r.$(t.selector),i[e],t.parser)})},n.prototype.setDefaults=function(){var i=this;k(Qn,function(t){var e=t.hasOwnProperty("default")?t.default:0;i.$(t.selector).selectedIndex=e})},n.prototype.restoreSettings=function(){var t=void 0;try{t=JSON.parse(g.localStorage.getItem(jn))}catch(t){f.warn(t)}t&&this.setValues(t)},n.prototype.saveSettings=function(){if(this.options_.persistTextTrackSettings){var t=this.getValues();try{Object.keys(t).length?g.localStorage.setItem(jn,JSON.stringify(t)):g.localStorage.removeItem(jn)}catch(t){f.warn(t)}}},n.prototype.updateDisplay=function(){var t=this.player_.getChild("textTrackDisplay");t&&t.updateDisplay()},n.prototype.conditionalBlur_=function(){this.previouslyActiveEl_=null,this.off(p,"keydown",this.handleKeyDown);var t=this.player_.controlBar,e=t&&t.subsCapsButton,i=t&&t.captionsButton;e?e.focus():i&&i.focus()},n}(Ne);Xt.registerComponent("TextTrackSettings",Zn);var ts=function(s){function a(t,e){y(this,a);var i=e.ResizeObserver||g.ResizeObserver;null===e.ResizeObserver&&(i=!1);var r=Gt({createEl:!i},e),n=b(this,s.call(this,t,r));return n.ResizeObserver=e.ResizeObserver||g.ResizeObserver,n.loadListener_=null,n.resizeObserver_=null,n.debouncedHandler_=It(function(){n.resizeHandler()},100,!1,t),i?(n.resizeObserver_=new n.ResizeObserver(n.debouncedHandler_),n.resizeObserver_.observe(t.el())):(n.loadListener_=function(){n.el_.contentWindow&&_t(n.el_.contentWindow,"resize",n.debouncedHandler_),n.off("load",n.loadListener_)},n.on("load",n.loadListener_)),n}return _(a,s),a.prototype.createEl=function(){return s.prototype.createEl.call(this,"iframe",{className:"vjs-resize-manager"})},a.prototype.resizeHandler=function(){this.player_.trigger("playerresize")},a.prototype.dispose=function(){this.resizeObserver_&&(this.resizeObserver_.unobserve(this.player_.el()),this.resizeObserver_.disconnect()),this.el_&&this.el_.contentWindow&&bt(this.el_.contentWindow,"resize",this.debouncedHandler_),this.loadListener_&&this.off("load",this.loadListener_),this.ResizeObserver=null,this.resizeObserver=null,this.debouncedHandler_=null,this.loadListener_=null},a}(Xt);Xt.registerComponent("ResizeManager",ts);var es=function(t){var e=t.el();if(e.hasAttribute("src"))return t.triggerSourceset(e.src),!0;var i=t.$$("source"),r=[],n="";if(!i.length)return!1;for(var s=0;s<i.length;s++){var a=i[s].src;a&&-1===r.indexOf(a)&&r.push(a)}return!!r.length&&(1===r.length&&(n=r[0]),t.triggerSourceset(n),!0)},is=Object.defineProperty({},"innerHTML",{get:function(){return this.cloneNode(!0).innerHTML},set:function(t){var e=p.createElement(this.nodeName.toLowerCase());e.innerHTML=t;for(var i=p.createDocumentFragment();e.childNodes.length;)i.appendChild(e.childNodes[0]);return this.innerText="",g.Element.prototype.appendChild.call(this,i),this.innerHTML}}),rs=function(t,e){for(var i={},r=0;r<t.length&&!((i=Object.getOwnPropertyDescriptor(t[r],e))&&i.set&&i.get);r++);return i.enumerable=!0,i.configurable=!0,i},ns=function(s){var a=s.el();if(!a.resetSourceWatch_){var e={},t=rs([s.el(),g.HTMLMediaElement.prototype,g.Element.prototype,is],"innerHTML"),i=function(n){return function(){for(var t=arguments.length,e=Array(t),i=0;i<t;i++)e[i]=arguments[i];var r=n.apply(a,e);return es(s),r}};["append","appendChild","insertAdjacentHTML"].forEach(function(t){a[t]&&(e[t]=a[t],a[t]=i(e[t]))}),Object.defineProperty(a,"innerHTML",Gt(t,{set:i(t.set)})),a.resetSourceWatch_=function(){a.resetSourceWatch_=null,Object.keys(e).forEach(function(t){a[t]=e[t]}),Object.defineProperty(a,"innerHTML",t)},s.one("sourceset",a.resetSourceWatch_)}},ss=Object.defineProperty({},"src",{get:function(){return this.hasAttribute("src")?Ze(g.Element.prototype.getAttribute.call(this,"src")):""},set:function(t){return g.Element.prototype.setAttribute.call(this,"src",t),t}}),as=function(r){if(r.featuresSourceset){var n=r.el();if(!n.resetSourceset_){var i=rs([r.el(),g.HTMLMediaElement.prototype,ss],"src"),s=n.setAttribute,e=n.load;Object.defineProperty(n,"src",Gt(i,{set:function(t){var e=i.set.call(n,t);return r.triggerSourceset(n.src),e}})),n.setAttribute=function(t,e){var i=s.call(n,t,e);return/src/i.test(t)&&r.triggerSourceset(n.src),i},n.load=function(){var t=e.call(n);return es(r)||(r.triggerSourceset(""),ns(r)),t},n.currentSrc?r.triggerSourceset(n.currentSrc):es(r)||ns(r),n.resetSourceset_=function(){n.resetSourceset_=null,n.load=e,n.setAttribute=s,Object.defineProperty(n,"src",i),n.resetSourceWatch_&&n.resetSourceWatch_()}}}},os=h(["Text Tracks are being loaded from another origin but the crossorigin attribute isn't used.\n            This may prevent text tracks from loading."],["Text Tracks are being loaded from another origin but the crossorigin attribute isn't used.\n            This may prevent text tracks from loading."]),us=function(c){function h(t,e){y(this,h);var i=b(this,c.call(this,t,e)),r=t.source,n=!1;if(r&&(i.el_.currentSrc!==r.src||t.tag&&3===t.tag.initNetworkState_)?i.setSource(r):i.handleLateInit_(i.el_),t.enableSourceset&&i.setupSourcesetHandling_(),i.el_.hasChildNodes()){for(var s=i.el_.childNodes,a=s.length,o=[];a--;){var u=s[a];"track"===u.nodeName.toLowerCase()&&(i.featuresNativeTextTracks?(i.remoteTextTrackEls().addTrackElement_(u),i.remoteTextTracks().addTrack(u.track),i.textTracks().addTrack(u.track),n||i.el_.hasAttribute("crossorigin")||!ei(u.src)||(n=!0)):o.push(u))}for(var l=0;l<o.length;l++)i.el_.removeChild(o[l])}return i.proxyNativeTracks_(),i.featuresNativeTextTracks&&n&&f.warn(m(os)),i.restoreMetadataTracksInIOSNativePlayer_(),(ge||ie||ue)&&!0===t.nativeControlsForTouch&&i.setControls(!0),i.proxyWebkitFullscreen_(),i.triggerReady(),i}return _(h,c),h.prototype.dispose=function(){this.el_.resetSourceset_&&this.el_.resetSourceset_(),h.disposeMediaElement(this.el_),this.options_=null,c.prototype.dispose.call(this)},h.prototype.setupSourcesetHandling_=function(){as(this)},h.prototype.restoreMetadataTracksInIOSNativePlayer_=function(){var r=this.textTracks(),n=void 0,t=function(){n=[];for(var t=0;t<r.length;t++){var e=r[t];"metadata"===e.kind&&n.push({track:e,storedMode:e.mode})}};t(),r.addEventListener("change",t),this.on("dispose",function(){return r.removeEventListener("change",t)});var e=function t(){for(var e=0;e<n.length;e++){var i=n[e];"disabled"===i.track.mode&&i.track.mode!==i.storedMode&&(i.track.mode=i.storedMode)}r.removeEventListener("change",t)};this.on("webkitbeginfullscreen",function(){r.removeEventListener("change",t),r.removeEventListener("change",e),r.addEventListener("change",e)}),this.on("webkitendfullscreen",function(){r.removeEventListener("change",t),r.addEventListener("change",t),r.removeEventListener("change",e)})},h.prototype.overrideNative_=function(t,e){var i=this;if(e===this["featuresNative"+t+"Tracks"]){var r=t.toLowerCase();this[r+"TracksListeners_"]&&Object.keys(this[r+"TracksListeners_"]).forEach(function(t){i.el()[r+"Tracks"].removeEventListener(t,i[r+"TracksListeners_"][t])}),this["featuresNative"+t+"Tracks"]=!e,this[r+"TracksListeners_"]=null,this.proxyNativeTracksForType_(r)}},h.prototype.overrideNativeAudioTracks=function(t){this.overrideNative_("Audio",t)},h.prototype.overrideNativeVideoTracks=function(t){this.overrideNative_("Video",t)},h.prototype.proxyNativeTracksForType_=function(t){var r=this,e=Ui[t],n=this.el()[e.getterName],s=this[e.getterName]();if(this["featuresNative"+e.capitalName+"Tracks"]&&n&&n.addEventListener){var a={change:function(t){s.trigger({type:"change",target:s,currentTarget:s,srcElement:s})},addtrack:function(t){s.addTrack(t.track)},removetrack:function(t){s.removeTrack(t.track)}},i=function(){for(var t=[],e=0;e<s.length;e++){for(var i=!1,r=0;r<n.length;r++)if(n[r]===s[e]){i=!0;break}i||t.push(s[e])}for(;t.length;)s.removeTrack(t.shift())};this[e.getterName+"Listeners_"]=a,Object.keys(a).forEach(function(e){var i=a[e];n.addEventListener(e,i),r.on("dispose",function(t){return n.removeEventListener(e,i)})}),this.on("loadstart",i),this.on("dispose",function(t){return r.off("loadstart",i)})}},h.prototype.proxyNativeTracks_=function(){var e=this;Ui.names.forEach(function(t){e.proxyNativeTracksForType_(t)})},h.prototype.createEl=function(){var t=this.options_.tag;if(!t||!this.options_.playerElIngest&&!this.movingMediaElementInDOM){if(t){var e=t.cloneNode(!0);t.parentNode&&t.parentNode.insertBefore(e,t),h.disposeMediaElement(t),t=e}else{t=p.createElement("video");var i=Gt({},this.options_.tag&&q(this.options_.tag));ge&&!0===this.options_.nativeControlsForTouch||delete i.controls,V(t,C(i,{id:this.options_.techId,class:"vjs-tech"}))}t.playerId=this.options_.playerId}"undefined"!=typeof this.options_.preload&&z(t,"preload",this.options_.preload);for(var r=["loop","muted","playsinline","autoplay"],n=0;n<r.length;n++){var s=r[n],a=this.options_[s];"undefined"!=typeof a&&(a?z(t,s,s):G(t,s),t[s]=a)}return t},h.prototype.handleLateInit_=function(t){if(0!==t.networkState&&3!==t.networkState){if(0===t.readyState){var e=!1,i=function(){e=!0};this.on("loadstart",i);var r=function(){e||this.trigger("loadstart")};return this.on("loadedmetadata",r),void this.ready(function(){this.off("loadstart",i),this.off("loadedmetadata",r),e||this.trigger("loadstart")})}var n=["loadstart"];n.push("loadedmetadata"),2<=t.readyState&&n.push("loadeddata"),3<=t.readyState&&n.push("canplay"),4<=t.readyState&&n.push("canplaythrough"),this.ready(function(){n.forEach(function(t){this.trigger(t)},this)})}},h.prototype.setCurrentTime=function(t){try{this.el_.currentTime=t}catch(t){f(t,"Video is not ready. (Video.js)")}},h.prototype.duration=function(){var e=this;if(this.el_.duration===1/0&&ae&&he&&0===this.el_.currentTime){return this.on("timeupdate",function t(){0<e.el_.currentTime&&(e.el_.duration===1/0&&e.trigger("durationchange"),e.off("timeupdate",t))}),NaN}return this.el_.duration||NaN},h.prototype.width=function(){return this.el_.offsetWidth},h.prototype.height=function(){return this.el_.offsetHeight},h.prototype.proxyWebkitFullscreen_=function(){var t=this;if("webkitDisplayingFullscreen"in this.el_){var e=function(){this.trigger("fullscreenchange",{isFullscreen:!1})},i=function(){"webkitPresentationMode"in this.el_&&"picture-in-picture"!==this.el_.webkitPresentationMode&&(this.one("webkitendfullscreen",e),this.trigger("fullscreenchange",{isFullscreen:!0}))};this.on("webkitbeginfullscreen",i),this.on("dispose",function(){t.off("webkitbeginfullscreen",i),t.off("webkitendfullscreen",e)})}},h.prototype.supportsFullScreen=function(){if("function"==typeof this.el_.webkitEnterFullScreen){var t=g.navigator&&g.navigator.userAgent||"";if(/Android/.test(t)||!/Chrome|Mac OS X 10.5/.test(t))return!0}return!1},h.prototype.enterFullScreen=function(){var t=this.el_;t.paused&&t.networkState<=t.HAVE_METADATA?(this.el_.play(),this.setTimeout(function(){t.pause(),t.webkitEnterFullScreen()},0)):t.webkitEnterFullScreen()},h.prototype.exitFullScreen=function(){this.el_.webkitExitFullScreen()},h.prototype.src=function(t){if(void 0===t)return this.el_.src;this.setSrc(t)},h.prototype.reset=function(){h.resetMediaElement(this.el_)},h.prototype.currentSrc=function(){return this.currentSource_?this.currentSource_.src:this.el_.currentSrc},h.prototype.setControls=function(t){this.el_.controls=!!t},h.prototype.addTextTrack=function(t,e,i){return this.featuresNativeTextTracks?this.el_.addTextTrack(t,e,i):c.prototype.addTextTrack.call(this,t,e,i)},h.prototype.createRemoteTextTrack=function(t){if(!this.featuresNativeTextTracks)return c.prototype.createRemoteTextTrack.call(this,t);var e=p.createElement("track");return t.kind&&(e.kind=t.kind),t.label&&(e.label=t.label),(t.language||t.srclang)&&(e.srclang=t.language||t.srclang),t.default&&(e.default=t.default),t.id&&(e.id=t.id),t.src&&(e.src=t.src),e},h.prototype.addRemoteTextTrack=function(t,e){var i=c.prototype.addRemoteTextTrack.call(this,t,e);return this.featuresNativeTextTracks&&this.el().appendChild(i),i},h.prototype.removeRemoteTextTrack=function(t){if(c.prototype.removeRemoteTextTrack.call(this,t),this.featuresNativeTextTracks)for(var e=this.$$("track"),i=e.length;i--;)t!==e[i]&&t!==e[i].track||this.el().removeChild(e[i])},h.prototype.getVideoPlaybackQuality=function(){if("function"==typeof this.el().getVideoPlaybackQuality)return this.el().getVideoPlaybackQuality();var t={};return"undefined"!=typeof this.el().webkitDroppedFrameCount&&"undefined"!=typeof this.el().webkitDecodedFrameCount&&(t.droppedVideoFrames=this.el().webkitDroppedFrameCount,t.totalVideoFrames=this.el().webkitDecodedFrameCount),g.performance&&"function"==typeof g.performance.now?t.creationTime=g.performance.now():g.performance&&g.performance.timing&&"number"==typeof g.performance.timing.navigationStart&&(t.creationTime=g.Date.now()-g.performance.timing.navigationStart),t},h}(br);if(x()){us.TEST_VID=p.createElement("video");var ls=p.createElement("track");ls.kind="captions",ls.srclang="en",ls.label="English",us.TEST_VID.appendChild(ls)}us.isSupported=function(){try{us.TEST_VID.volume=.5}catch(t){return!1}return!(!us.TEST_VID||!us.TEST_VID.canPlayType)},us.canPlayType=function(t){return us.TEST_VID.canPlayType(t)},us.canPlaySource=function(t,e){return us.canPlayType(t.type)},us.canControlVolume=function(){try{var t=us.TEST_VID.volume;return us.TEST_VID.volume=t/2+.1,t!==us.TEST_VID.volume}catch(t){return!1}},us.canControlPlaybackRate=function(){if(ae&&he&&de<58)return!1;try{var t=us.TEST_VID.playbackRate;return us.TEST_VID.playbackRate=t/2+.1,t!==us.TEST_VID.playbackRate}catch(t){return!1}},us.canOverrideAttributes=function(){try{var t=function(){};Object.defineProperty(p.createElement("video"),"src",{get:t,set:t}),Object.defineProperty(p.createElement("audio"),"src",{get:t,set:t}),Object.defineProperty(p.createElement("video"),"innerHTML",{get:t,set:t}),Object.defineProperty(p.createElement("audio"),"innerHTML",{get:t,set:t})}catch(t){return!1}return!0},us.supportsNativeTextTracks=function(){return me},us.supportsNativeVideoTracks=function(){return!(!us.TEST_VID||!us.TEST_VID.videoTracks)},us.supportsNativeAudioTracks=function(){return!(!us.TEST_VID||!us.TEST_VID.audioTracks)},us.Events=["loadstart","suspend","abort","error","emptied","stalled","loadedmetadata","loadeddata","canplay","canplaythrough","playing","waiting","seeking","seeked","ended","durationchange","timeupdate","progress","play","pause","ratechange","resize","volumechange"],us.prototype.featuresVolumeControl=us.canControlVolume(),us.prototype.featuresPlaybackRate=us.canControlPlaybackRate(),us.prototype.featuresSourceset=us.canOverrideAttributes(),us.prototype.movingMediaElementInDOM=!ne,us.prototype.featuresFullscreenResize=!0,us.prototype.featuresProgressEvents=!0,us.prototype.featuresTimeupdateEvents=!0,us.prototype.featuresNativeTextTracks=us.supportsNativeTextTracks(),us.prototype.featuresNativeVideoTracks=us.supportsNativeVideoTracks(),us.prototype.featuresNativeAudioTracks=us.supportsNativeAudioTracks();var cs=us.TEST_VID&&us.TEST_VID.constructor.prototype.canPlayType,hs=/^application\/(?:x-|vnd\.apple\.)mpegurl/i;us.patchCanPlayType=function(){4<=oe&&!le&&!he&&(us.TEST_VID.constructor.prototype.canPlayType=function(t){return t&&hs.test(t)?"maybe":cs.call(this,t)})},us.unpatchCanPlayType=function(){var t=us.TEST_VID.constructor.prototype.canPlayType;return us.TEST_VID.constructor.prototype.canPlayType=cs,t},us.patchCanPlayType(),us.disposeMediaElement=function(t){if(t){for(t.parentNode&&t.parentNode.removeChild(t);t.hasChildNodes();)t.removeChild(t.firstChild);t.removeAttribute("src"),"function"==typeof t.load&&function(){try{t.load()}catch(t){}}()}},us.resetMediaElement=function(t){if(t){for(var e=t.querySelectorAll("source"),i=e.length;i--;)t.removeChild(e[i]);t.removeAttribute("src"),"function"==typeof t.load&&function(){try{t.load()}catch(t){}}()}},["muted","defaultMuted","autoplay","controls","loop","playsinline"].forEach(function(t){us.prototype[t]=function(){return this.el_[t]||this.el_.hasAttribute(t)}}),["muted","defaultMuted","autoplay","loop","playsinline"].forEach(function(e){us.prototype["set"+zt(e)]=function(t){(this.el_[e]=t)?this.el_.setAttribute(e,e):this.el_.removeAttribute(e)}}),["paused","currentTime","buffered","volume","poster","preload","error","seeking","seekable","ended","playbackRate","defaultPlaybackRate","played","networkState","readyState","videoWidth","videoHeight"].forEach(function(t){us.prototype[t]=function(){return this.el_[t]}}),["volume","src","poster","preload","playbackRate","defaultPlaybackRate"].forEach(function(e){us.prototype["set"+zt(e)]=function(t){this.el_[e]=t}}),["pause","load","play"].forEach(function(t){us.prototype[t]=function(){return this.el_[t]()}}),br.withSourceHandlers(us),us.nativeSourceHandler={},us.nativeSourceHandler.canPlayType=function(t){try{return us.TEST_VID.canPlayType(t)}catch(t){return""}},us.nativeSourceHandler.canHandleSource=function(t,e){if(t.type)return us.nativeSourceHandler.canPlayType(t.type);if(t.src){var i=ti(t.src);return us.nativeSourceHandler.canPlayType("video/"+i)}return""},us.nativeSourceHandler.handleSource=function(t,e,i){e.setSrc(t.src)},us.nativeSourceHandler.dispose=function(){},us.registerSourceHandler(us.nativeSourceHandler),br.registerTech("Html5",us);var ds=h(["\n        Using the tech directly can be dangerous. I hope you know what you're doing.\n        See https://github.com/videojs/video.js/issues/2617 for more info.\n      "],["\n        Using the tech directly can be dangerous. I hope you know what you're doing.\n        See https://github.com/videojs/video.js/issues/2617 for more info.\n      "]),ps=["progress","abort","suspend","emptied","stalled","loadedmetadata","loadeddata","timeupdate","resize","volumechange","texttrackchange"],fs={canplay:"CanPlay",canplaythrough:"CanPlayThrough",playing:"Playing",seeked:"Seeked"},ms=function(c){function h(t,e,i){if(y(this,h),t.id=t.id||e.id||"vjs_video_"+ut(),(e=C(h.getTagSettings(t),e)).initChildren=!1,e.createEl=!1,e.evented=!1,e.reportTouchActivity=!1,!e.language)if("function"==typeof t.closest){var r=t.closest("[lang]");r&&r.getAttribute&&(e.language=r.getAttribute("lang"))}else for(var n=t;n&&1===n.nodeType;){if(q(n).hasOwnProperty("lang")){e.language=n.getAttribute("lang");break}n=n.parentNode}var s=b(this,c.call(this,null,e,i));if(s.isPosterFromTech_=!1,s.queuedCallbacks_=[],s.isReady_=!1,s.hasStarted_=!1,s.userActive_=!1,!s.options_||!s.options_.techOrder||!s.options_.techOrder.length)throw new Error("No techOrder specified. Did you overwrite videojs.options instead of just changing the properties you want to override?");if(s.tag=t,s.tagAttributes=t&&q(t),s.language(s.options_.language),e.languages){var a={};Object.getOwnPropertyNames(e.languages).forEach(function(t){a[t.toLowerCase()]=e.languages[t]}),s.languages_=a}else s.languages_=h.prototype.options_.languages;s.cache_={},s.poster_=e.poster||"",s.controls_=!!e.controls,s.cache_.lastVolume=1,t.controls=!1,t.removeAttribute("controls"),s.scrubbing_=!1,s.el_=s.createEl(),s.cache_.lastPlaybackRate=s.defaultPlaybackRate(),Vt(s,{eventBusKey:"el_"});var o=Gt(s.options_);if(e.plugins){var u=e.plugins;Object.keys(u).forEach(function(t){if("function"!=typeof this[t])throw new Error('plugin "'+t+'" does not exist');this[t](u[t])},s)}s.options_.playerOptions=o,s.middleware_=[],s.initChildren(),s.isAudio("audio"===t.nodeName.toLowerCase()),s.controls()?s.addClass("vjs-controls-enabled"):s.addClass("vjs-controls-disabled"),s.el_.setAttribute("role","region"),s.isAudio()?s.el_.setAttribute("aria-label",s.localize("Audio Player")):s.el_.setAttribute("aria-label",s.localize("Video Player")),s.isAudio()&&s.addClass("vjs-audio"),s.flexNotSupported_()&&s.addClass("vjs-no-flex"),ne||s.addClass("vjs-workinghover"),h.players[s.id_]=s;var l=d.split(".")[0];return s.addClass("vjs-v"+l),s.userActive(!0),s.reportUserActivity(),s.one("play",s.listenForUserActivity_),s.on("fullscreenchange",s.handleFullscreenChange_),s.on("stageclick",s.handleStageClick_),s.changingSrc_=!1,s.playWaitingForReady_=!1,s.playOnLoadstart_=null,s}return _(h,c),h.prototype.dispose=function(){this.trigger("dispose"),this.off("dispose"),this.styleEl_&&this.styleEl_.parentNode&&(this.styleEl_.parentNode.removeChild(this.styleEl_),this.styleEl_=null),h.players[this.id_]=null,this.tag&&this.tag.player&&(this.tag.player=null),this.el_&&this.el_.player&&(this.el_.player=null),this.tech_&&(this.tech_.dispose(),this.isPosterFromTech_=!1,this.poster_=""),this.playerElIngest_&&(this.playerElIngest_=null),this.tag&&(this.tag=null),Sr[this.id()]=null,c.prototype.dispose.call(this)},h.prototype.createEl=function(){var e=this.tag,i=void 0,t=this.playerElIngest_=e.parentNode&&e.parentNode.hasAttribute&&e.parentNode.hasAttribute("data-vjs-player"),r="video-js"===this.tag.tagName.toLowerCase();t?i=this.el_=e.parentNode:r||(i=this.el_=c.prototype.createEl.call(this,"div"));var n=q(e);if(r){for(i=this.el_=e,e=this.tag=p.createElement("video");i.children.length;)e.appendChild(i.firstChild);B(i,"video-js")||j(i,"video-js"),i.appendChild(e),t=this.playerElIngest_=i,Object.keys(i).forEach(function(t){e[t]=i[t]})}if(e.setAttribute("tabindex","-1"),e.removeAttribute("width"),e.removeAttribute("height"),Object.getOwnPropertyNames(n).forEach(function(t){i.setAttribute(t,n[t]),r&&e.setAttribute(t,n[t])}),e.playerId=e.id,e.id+="_html5_api",e.className="vjs-tech",e.player=i.player=this,this.addClass("vjs-paused"),!0!==g.VIDEOJS_NO_DYNAMIC_STYLE){this.styleEl_=Lt("vjs-styles-dimensions");var s=nt(".vjs-styles-defaults"),a=nt("head");a.insertBefore(this.styleEl_,s?s.nextSibling:a.firstChild)}this.width(this.options_.width),this.height(this.options_.height),this.fluid(this.options_.fluid),this.aspectRatio(this.options_.aspectRatio);for(var o=e.getElementsByTagName("a"),u=0;u<o.length;u++){var l=o.item(u);j(l,"vjs-hidden"),l.setAttribute("hidden","hidden")}return e.initNetworkState_=e.networkState,e.parentNode&&!t&&e.parentNode.insertBefore(i,e),N(e,i),this.children_.unshift(e),this.el_.setAttribute("lang",this.language_),this.el_=i},h.prototype.width=function(t){return this.dimension("width",t)},h.prototype.height=function(t){return this.dimension("height",t)},h.prototype.dimension=function(t,e){var i=t+"_";if(void 0===e)return this[i]||0;if(""===e)return this[i]=void 0,void this.updateStyleEl_();var r=parseFloat(e);isNaN(r)?f.error('Improper value "'+e+'" supplied for for '+t):(this[i]=r,this.updateStyleEl_())},h.prototype.fluid=function(t){if(void 0===t)return!!this.fluid_;this.fluid_=!!t,t?this.addClass("vjs-fluid"):this.removeClass("vjs-fluid"),this.updateStyleEl_()},h.prototype.aspectRatio=function(t){if(void 0===t)return this.aspectRatio_;if(!/^\d+\:\d+$/.test(t))throw new Error("Improper value supplied for aspect ratio. The format should be width:height, for example 16:9.");this.aspectRatio_=t,this.fluid(!0),this.updateStyleEl_()},h.prototype.updateStyleEl_=function(){if(!0!==g.VIDEOJS_NO_DYNAMIC_STYLE){var t=void 0,e=void 0,i=void 0,r=(void 0!==this.aspectRatio_&&"auto"!==this.aspectRatio_?this.aspectRatio_:0<this.videoWidth()?this.videoWidth()+":"+this.videoHeight():"16:9").split(":"),n=r[1]/r[0];t=void 0!==this.width_?this.width_:void 0!==this.height_?this.height_/n:this.videoWidth()||300,e=void 0!==this.height_?this.height_:t*n,i=/^[^a-zA-Z]/.test(this.id())?"dimensions-"+this.id():this.id()+"-dimensions",this.addClass(i),Pt(this.styleEl_,"\n      ."+i+" {\n        width: "+t+"px;\n        height: "+e+"px;\n      }\n\n      ."+i+".vjs-fluid {\n        padding-top: "+100*n+"%;\n      }\n    ")}else{var s="number"==typeof this.width_?this.width_:this.options_.width,a="number"==typeof this.height_?this.height_:this.options_.height,o=this.tech_&&this.tech_.el();o&&(0<=s&&(o.width=s),0<=a&&(o.height=a))}},h.prototype.loadTech_=function(t,e){var i=this;this.tech_&&this.unloadTech_();var r=zt(t),n=t.charAt(0).toLowerCase()+t.slice(1);"Html5"!==r&&this.tag&&(br.getTech("Html5").disposeMediaElement(this.tag),this.tag.player=null,this.tag=null),this.techName_=r,this.isReady_=!1;var s={source:e,nativeControlsForTouch:this.options_.nativeControlsForTouch,playerId:this.id(),techId:this.id()+"_"+r+"_api",autoplay:this.options_.autoplay,playsinline:this.options_.playsinline,preload:this.options_.preload,loop:this.options_.loop,muted:this.options_.muted,poster:this.poster(),language:this.language(),playerElIngest:this.playerElIngest_||!1,"vtt.js":this.options_["vtt.js"],canOverridePoster:!!this.options_.techCanOverridePoster,enableSourceset:this.options_.enableSourceset};Bi.names.forEach(function(t){var e=Bi[t];s[e.getterName]=i[e.privateName]}),C(s,this.options_[r]),C(s,this.options_[n]),C(s,this.options_[t.toLowerCase()]),this.tag&&(s.tag=this.tag),e&&e.src===this.cache_.src&&0<this.cache_.currentTime&&(s.startTime=this.cache_.currentTime);var a=br.getTech(t);if(!a)throw new Error("No Tech named '"+r+"' exists! '"+r+"' should be registered using videojs.registerTech()'");this.tech_=new a(s),this.tech_.ready(Ot(this,this.handleTechReady_),!0),Me(this.textTracksJson_||[],this.tech_),ps.forEach(function(t){i.on(i.tech_,t,i["handleTech"+zt(t)+"_"])}),Object.keys(fs).forEach(function(e){i.on(i.tech_,e,function(t){0===i.tech_.playbackRate()&&i.tech_.seeking()?i.queuedCallbacks_.push({callback:i["handleTech"+fs[e]+"_"].bind(i),event:t}):i["handleTech"+fs[e]+"_"](t)})}),this.on(this.tech_,"loadstart",this.handleTechLoadStart_),this.on(this.tech_,"sourceset",this.handleTechSourceset_),this.on(this.tech_,"waiting",this.handleTechWaiting_),this.on(this.tech_,"ended",this.handleTechEnded_),this.on(this.tech_,"seeking",this.handleTechSeeking_),this.on(this.tech_,"play",this.handleTechPlay_),this.on(this.tech_,"firstplay",this.handleTechFirstPlay_),this.on(this.tech_,"pause",this.handleTechPause_),this.on(this.tech_,"durationchange",this.handleTechDurationChange_),this.on(this.tech_,"fullscreenchange",this.handleTechFullscreenChange_),this.on(this.tech_,"error",this.handleTechError_),this.on(this.tech_,"loadedmetadata",this.updateStyleEl_),this.on(this.tech_,"posterchange",this.handleTechPosterChange_),this.on(this.tech_,"textdata",this.handleTechTextData_),this.on(this.tech_,"ratechange",this.handleTechRateChange_),this.usingNativeControls(this.techGet_("controls")),this.controls()&&!this.usingNativeControls()&&this.addTechControlsListeners_(),this.tech_.el().parentNode===this.el()||"Html5"===r&&this.tag||N(this.tech_.el(),this.el()),this.tag&&(this.tag.player=null,this.tag=null)},h.prototype.unloadTech_=function(){var i=this;Bi.names.forEach(function(t){var e=Bi[t];i[e.privateName]=i[e.getterName]()}),this.textTracksJson_=Re(this.tech_),this.isReady_=!1,this.tech_.dispose(),this.tech_=!1,this.isPosterFromTech_&&(this.poster_="",this.trigger("posterchange")),this.isPosterFromTech_=!1},h.prototype.tech=function(t){return void 0===t&&f.warn(m(ds)),this.tech_},h.prototype.addTechControlsListeners_=function(){this.removeTechControlsListeners_(),this.on(this.tech_,"mousedown",this.handleTechClick_),this.on(this.tech_,"touchstart",this.handleTechTouchStart_),this.on(this.tech_,"touchmove",this.handleTechTouchMove_),this.on(this.tech_,"touchend",this.handleTechTouchEnd_),this.on(this.tech_,"tap",this.handleTechTap_)},h.prototype.removeTechControlsListeners_=function(){this.off(this.tech_,"tap",this.handleTechTap_),this.off(this.tech_,"touchstart",this.handleTechTouchStart_),this.off(this.tech_,"touchmove",this.handleTechTouchMove_),this.off(this.tech_,"touchend",this.handleTechTouchEnd_),this.off(this.tech_,"mousedown",this.handleTechClick_)},h.prototype.handleTechReady_=function(){if(this.triggerReady(),this.cache_.volume&&this.techCall_("setVolume",this.cache_.volume),this.handleTechPosterChange_(),this.handleTechDurationChange_(),(this.src()||this.currentSrc())&&this.tag&&this.options_.autoplay&&this.paused())try{delete this.tag.poster}catch(t){f("deleting tag.poster throws in some browsers",t)}},h.prototype.handleTechLoadStart_=function(){this.removeClass("vjs-ended"),this.removeClass("vjs-seeking"),this.error(null),this.paused()?(this.hasStarted(!1),this.trigger("loadstart")):(this.trigger("loadstart"),this.trigger("firstplay"))},h.prototype.updateSourceCaches_=function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:"",e=t,i="";"string"!=typeof e&&(e=t.src,i=t.type),this.cache_.source=this.cache_.source||{},this.cache_.sources=this.cache_.sources||[],e&&!i&&(i=function(t,e){if(!e)return"";if(t.cache_.source.src===e&&t.cache_.source.type)return t.cache_.source.type;var i=t.cache_.sources.filter(function(t){return t.src===e});if(i.length)return i[0].type;for(var r=t.$$("source"),n=0;n<r.length;n++){var s=r[n];if(s.type&&s.src&&s.src===e)return s.type}return xr(e)}(this,e)),this.cache_.source={src:e,type:i};for(var r=this.cache_.sources.filter(function(t){return t.src&&t.src===e}),n=[],s=this.$$("source"),a=[],o=0;o<s.length;o++){var u=q(s[o]);n.push(u),u.src&&u.src===e&&a.push(u.src)}a.length&&!r.length?this.cache_.sources=n:r.length||(this.cache_.sources=[this.cache_.source]),this.cache_.src=e},h.prototype.handleTechSourceset_=function(t){var i=this;if(!this.changingSrc_&&(this.updateSourceCaches_(t.src),!t.src)){this.tech_.one(["sourceset","loadstart"],function t(e){"sourceset"!==e.type&&i.updateSourceCaches_(i.techGet_("currentSrc")),i.tech_.off(["sourceset","loadstart"],t)})}this.trigger({src:t.src,type:"sourceset"})},h.prototype.hasStarted=function(t){if(void 0===t)return this.hasStarted_;t!==this.hasStarted_&&(this.hasStarted_=t,this.hasStarted_?(this.addClass("vjs-has-started"),this.trigger("firstplay")):this.removeClass("vjs-has-started"))},h.prototype.handleTechPlay_=function(){this.removeClass("vjs-ended"),this.removeClass("vjs-paused"),this.addClass("vjs-playing"),this.hasStarted(!0),this.trigger("play")},h.prototype.handleTechRateChange_=function(){0<this.tech_.playbackRate()&&0===this.cache_.lastPlaybackRate&&(this.queuedCallbacks_.forEach(function(t){return t.callback(t.event)}),this.queuedCallbacks_=[]),this.cache_.lastPlaybackRate=this.tech_.playbackRate(),this.trigger("ratechange")},h.prototype.handleTechWaiting_=function(){var t=this;this.addClass("vjs-waiting"),this.trigger("waiting"),this.one("timeupdate",function(){return t.removeClass("vjs-waiting")})},h.prototype.handleTechCanPlay_=function(){this.removeClass("vjs-waiting"),this.trigger("canplay")},h.prototype.handleTechCanPlayThrough_=function(){this.removeClass("vjs-waiting"),this.trigger("canplaythrough")},h.prototype.handleTechPlaying_=function(){this.removeClass("vjs-waiting"),this.trigger("playing")},h.prototype.handleTechSeeking_=function(){this.addClass("vjs-seeking"),this.trigger("seeking")},h.prototype.handleTechSeeked_=function(){this.removeClass("vjs-seeking"),this.trigger("seeked")},h.prototype.handleTechFirstPlay_=function(){this.options_.starttime&&(f.warn("Passing the `starttime` option to the player will be deprecated in 6.0"),this.currentTime(this.options_.starttime)),this.addClass("vjs-has-started"),this.trigger("firstplay")},h.prototype.handleTechPause_=function(){this.removeClass("vjs-playing"),this.addClass("vjs-paused"),this.trigger("pause")},h.prototype.handleTechEnded_=function(){this.addClass("vjs-ended"),this.options_.loop?(this.currentTime(0),this.play()):this.paused()||this.pause(),this.trigger("ended")},h.prototype.handleTechDurationChange_=function(){this.duration(this.techGet_("duration"))},h.prototype.handleTechClick_=function(t){rt(t)&&this.controls_&&(this.paused()?this.play():this.pause())},h.prototype.handleTechTap_=function(){this.userActive(!this.userActive())},h.prototype.handleTechTouchStart_=function(){this.userWasActive=this.userActive()},h.prototype.handleTechTouchMove_=function(){this.userWasActive&&this.reportUserActivity()},h.prototype.handleTechTouchEnd_=function(t){t.preventDefault()},h.prototype.handleFullscreenChange_=function(){this.isFullscreen()?this.addClass("vjs-fullscreen"):this.removeClass("vjs-fullscreen")},h.prototype.handleStageClick_=function(){this.reportUserActivity()},h.prototype.handleTechFullscreenChange_=function(t,e){e&&this.isFullscreen(e.isFullscreen),this.trigger("fullscreenchange")},h.prototype.handleTechError_=function(){var t=this.tech_.error();this.error(t)},h.prototype.handleTechTextData_=function(){var t=null;1<arguments.length&&(t=arguments[1]),this.trigger("textdata",t)},h.prototype.getCache=function(){return this.cache_},h.prototype.techCall_=function(n,s){this.ready(function(){if(n in Ar)return t=this.middleware_,e=this.tech_,r=s,e[i=n](t.reduce(Pr(i),r));if(n in Lr)return Er(this.middleware_,this.tech_,n,s);var t,e,i,r;try{this.tech_&&this.tech_[n](s)}catch(t){throw f(t),t}},!0)},h.prototype.techGet_=function(e){if(this.tech_&&this.tech_.isReady_){if(e in wr)return t=this.middleware_,i=this.tech_,r=e,t.reduceRight(Pr(r),i[r]());if(e in Lr)return Er(this.middleware_,this.tech_,e);var t,i,r;try{return this.tech_[e]()}catch(t){if(void 0===this.tech_[e])throw f("Video.js: "+e+" method not defined for "+this.techName_+" playback technology.",t),t;if("TypeError"===t.name)throw f("Video.js: "+e+" unavailable on "+this.techName_+" playback technology element.",t),this.tech_.isReady_=!1,t;throw f(t),t}}},h.prototype.play=function(){var t=this;if(this.playOnLoadstart_&&this.off("loadstart",this.playOnLoadstart_),this.isReady_){if(!this.changingSrc_&&(this.src()||this.currentSrc()))return this.techGet_("play");this.playOnLoadstart_=function(){t.playOnLoadstart_=null,Ie(t.play())},this.one("loadstart",this.playOnLoadstart_)}else{if(this.playWaitingForReady_)return;this.playWaitingForReady_=!0,this.ready(function(){t.playWaitingForReady_=!1,Ie(t.play())})}},h.prototype.pause=function(){this.techCall_("pause")},h.prototype.paused=function(){return!1!==this.techGet_("paused")},h.prototype.played=function(){return this.techGet_("played")||be(0,0)},h.prototype.scrubbing=function(t){if("undefined"==typeof t)return this.scrubbing_;this.scrubbing_=!!t,t?this.addClass("vjs-scrubbing"):this.removeClass("vjs-scrubbing")},h.prototype.currentTime=function(t){return"undefined"!=typeof t?(t<0&&(t=0),void this.techCall_("setCurrentTime",t)):(this.cache_.currentTime=this.techGet_("currentTime")||0,this.cache_.currentTime)},h.prototype.duration=function(t){if(void 0===t)return void 0!==this.cache_.duration?this.cache_.duration:NaN;(t=parseFloat(t))<0&&(t=1/0),t!==this.cache_.duration&&((this.cache_.duration=t)===1/0?this.addClass("vjs-live"):this.removeClass("vjs-live"),this.trigger("durationchange"))},h.prototype.remainingTime=function(){return this.duration()-this.currentTime()},h.prototype.remainingTimeDisplay=function(){return Math.floor(this.duration())-Math.floor(this.currentTime())},h.prototype.buffered=function(){var t=this.techGet_("buffered");return t&&t.length||(t=be(0,0)),t},h.prototype.bufferedPercent=function(){return Te(this.buffered(),this.duration())},h.prototype.bufferedEnd=function(){var t=this.buffered(),e=this.duration(),i=t.end(t.length-1);return e<i&&(i=e),i},h.prototype.volume=function(t){var e=void 0;return void 0!==t?(e=Math.max(0,Math.min(1,parseFloat(t))),this.cache_.volume=e,this.techCall_("setVolume",e),void(0<e&&this.lastVolume_(e))):(e=parseFloat(this.techGet_("volume")),isNaN(e)?1:e)},h.prototype.muted=function(t){if(void 0===t)return this.techGet_("muted")||!1;this.techCall_("setMuted",t)},h.prototype.defaultMuted=function(t){return void 0!==t?this.techCall_("setDefaultMuted",t):this.techGet_("defaultMuted")||!1},h.prototype.lastVolume_=function(t){if(void 0===t||0===t)return this.cache_.lastVolume;this.cache_.lastVolume=t},h.prototype.supportsFullScreen=function(){return this.techGet_("supportsFullScreen")||!1},h.prototype.isFullscreen=function(t){if(void 0===t)return!!this.isFullscreen_;this.isFullscreen_=!!t},h.prototype.requestFullscreen=function(){var i=Se;this.isFullscreen(!0),i.requestFullscreen?(_t(p,i.fullscreenchange,Ot(this,function t(e){this.isFullscreen(p[i.fullscreenElement]),!1===this.isFullscreen()&&bt(p,i.fullscreenchange,t),this.trigger("fullscreenchange")})),this.el_[i.requestFullscreen]()):this.tech_.supportsFullScreen()?this.techCall_("enterFullScreen"):(this.enterFullWindow(),this.trigger("fullscreenchange"))},h.prototype.exitFullscreen=function(){var t=Se;this.isFullscreen(!1),t.requestFullscreen?p[t.exitFullscreen]():this.tech_.supportsFullScreen()?this.techCall_("exitFullScreen"):(this.exitFullWindow(),this.trigger("fullscreenchange"))},h.prototype.enterFullWindow=function(){this.isFullWindow=!0,this.docOrigOverflow=p.documentElement.style.overflow,_t(p,"keydown",Ot(this,this.fullWindowOnEscKey)),p.documentElement.style.overflow="hidden",j(p.body,"vjs-full-window"),this.trigger("enterFullWindow")},h.prototype.fullWindowOnEscKey=function(t){27===t.keyCode&&(!0===this.isFullscreen()?this.exitFullscreen():this.exitFullWindow())},h.prototype.exitFullWindow=function(){this.isFullWindow=!1,bt(p,"keydown",this.fullWindowOnEscKey),p.documentElement.style.overflow=this.docOrigOverflow,F(p.body,"vjs-full-window"),this.trigger("exitFullWindow")},h.prototype.canPlayType=function(t){for(var e=void 0,i=0,r=this.options_.techOrder;i<r.length;i++){var n=r[i],s=br.getTech(n);if(s||(s=Xt.getComponent(n)),s){if(s.isSupported()&&(e=s.canPlayType(t)))return e}else f.error('The "'+n+'" tech is undefined. Skipped browser support check for that tech.')}return""},h.prototype.selectSource=function(t){var i,r=this,e=this.options_.techOrder.map(function(t){return[t,br.getTech(t)]}).filter(function(t){var e=t[0],i=t[1];return i?i.isSupported():(f.error('The "'+e+'" tech is undefined. Skipped browser support check for that tech.'),!1)}),n=function(t,i,r){var n=void 0;return t.some(function(e){return i.some(function(t){if(n=r(e,t))return!0})}),n},s=function(t,e){var i=t[0];if(t[1].canPlaySource(e,r.options_[i.toLowerCase()]))return{source:e,tech:i}};return(this.options_.sourceOrder?n(t,e,(i=s,function(t,e){return i(e,t)})):n(e,t,s))||!1},h.prototype.src=function(t){var n=this;if("undefined"==typeof t)return this.cache_.src||"";var s=function e(t){if(Array.isArray(t)){var i=[];t.forEach(function(t){t=e(t),Array.isArray(t)?i=i.concat(t):E(t)&&i.push(t)}),t=i}else t="string"==typeof t&&t.trim()?[Ir({src:t})]:E(t)&&"string"==typeof t.src&&t.src&&t.src.trim()?[Ir(t)]:[];return t}(t);s.length?(this.changingSrc_=!0,this.cache_.sources=s,this.updateSourceCaches_(s[0]),Cr(this,s[0],function(t,e){var i,r;if(n.middleware_=e,n.cache_.sources=s,n.updateSourceCaches_(t),n.src_(t))return 1<s.length?n.src(s.slice(1)):(n.changingSrc_=!1,n.setTimeout(function(){this.error({code:4,message:this.localize(this.options_.notSupportedMessage)})},0),void n.triggerReady());i=e,r=n.tech_,i.forEach(function(t){return t.setTech&&t.setTech(r)})})):this.setTimeout(function(){this.error({code:4,message:this.localize(this.options_.notSupportedMessage)})},0)},h.prototype.src_=function(t){var e,i,r=this,n=this.selectSource([t]);return!n||(e=n.tech,i=this.techName_,zt(e)!==zt(i)?(this.changingSrc_=!0,this.loadTech_(n.tech,n.source),this.tech_.ready(function(){r.changingSrc_=!1})):this.ready(function(){this.tech_.constructor.prototype.hasOwnProperty("setSource")?this.techCall_("setSource",t):this.techCall_("src",t.src),this.changingSrc_=!1},!0),!1)},h.prototype.load=function(){this.techCall_("load")},h.prototype.reset=function(){this.loadTech_(this.options_.techOrder[0],null),this.techCall_("reset")},h.prototype.currentSources=function(){var t=this.currentSource(),e=[];return 0!==Object.keys(t).length&&e.push(t),this.cache_.sources||e},h.prototype.currentSource=function(){return this.cache_.source||{}},h.prototype.currentSrc=function(){return this.currentSource()&&this.currentSource().src||""},h.prototype.currentType=function(){return this.currentSource()&&this.currentSource().type||""},h.prototype.preload=function(t){return void 0!==t?(this.techCall_("setPreload",t),void(this.options_.preload=t)):this.techGet_("preload")},h.prototype.autoplay=function(t){return void 0!==t?(this.techCall_("setAutoplay",t),void(this.options_.autoplay=t)):this.techGet_("autoplay",t)},h.prototype.playsinline=function(t){return void 0!==t?(this.techCall_("setPlaysinline",t),this.options_.playsinline=t,this):this.techGet_("playsinline")},h.prototype.loop=function(t){return void 0!==t?(this.techCall_("setLoop",t),void(this.options_.loop=t)):this.techGet_("loop")},h.prototype.poster=function(t){if(void 0===t)return this.poster_;t||(t=""),t!==this.poster_&&(this.poster_=t,this.techCall_("setPoster",t),this.isPosterFromTech_=!1,this.trigger("posterchange"))},h.prototype.handleTechPosterChange_=function(){if((!this.poster_||this.options_.techCanOverridePoster)&&this.tech_&&this.tech_.poster){var t=this.tech_.poster()||"";t!==this.poster_&&(this.poster_=t,this.isPosterFromTech_=!0,this.trigger("posterchange"))}},h.prototype.controls=function(t){if(void 0===t)return!!this.controls_;t=!!t,this.controls_!==t&&(this.controls_=t,this.usingNativeControls()&&this.techCall_("setControls",t),this.controls_?(this.removeClass("vjs-controls-disabled"),this.addClass("vjs-controls-enabled"),this.trigger("controlsenabled"),this.usingNativeControls()||this.addTechControlsListeners_()):(this.removeClass("vjs-controls-enabled"),this.addClass("vjs-controls-disabled"),this.trigger("controlsdisabled"),this.usingNativeControls()||this.removeTechControlsListeners_()))},h.prototype.usingNativeControls=function(t){if(void 0===t)return!!this.usingNativeControls_;t=!!t,this.usingNativeControls_!==t&&(this.usingNativeControls_=t,this.usingNativeControls_?(this.addClass("vjs-using-native-controls"),this.trigger("usingnativecontrols")):(this.removeClass("vjs-using-native-controls"),this.trigger("usingcustomcontrols")))},h.prototype.error=function(t){return void 0===t?this.error_||null:null===t?(this.error_=t,this.removeClass("vjs-error"),void(this.errorDisplay&&this.errorDisplay.close())):(this.error_=new Le(t),this.addClass("vjs-error"),f.error("(CODE:"+this.error_.code+" "+Le.errorTypes[this.error_.code]+")",this.error_.message,this.error_),void this.trigger("error"))},h.prototype.reportUserActivity=function(t){this.userActivity_=!0},h.prototype.userActive=function(t){if(void 0===t)return this.userActive_;if((t=!!t)!==this.userActive_){if(this.userActive_=t,this.userActive_)return this.userActivity_=!0,this.removeClass("vjs-user-inactive"),this.addClass("vjs-user-active"),void this.trigger("useractive");this.tech_&&this.tech_.one("mousemove",function(t){t.stopPropagation(),t.preventDefault()}),this.userActivity_=!1,this.removeClass("vjs-user-active"),this.addClass("vjs-user-inactive"),this.trigger("userinactive")}},h.prototype.listenForUserActivity_=function(){var e=void 0,i=void 0,r=void 0,n=Ot(this,this.reportUserActivity);this.on("mousedown",function(){n(),this.clearInterval(e),e=this.setInterval(n,250)}),this.on("mousemove",function(t){t.screenX===i&&t.screenY===r||(i=t.screenX,r=t.screenY,n())}),this.on("mouseup",function(t){n(),this.clearInterval(e)}),this.on("keydown",n),this.on("keyup",n);var s=void 0;this.setInterval(function(){if(this.userActivity_){this.userActivity_=!1,this.userActive(!0),this.clearTimeout(s);var t=this.options_.inactivityTimeout;t<=0||(s=this.setTimeout(function(){this.userActivity_||this.userActive(!1)},t))}},250)},h.prototype.playbackRate=function(t){if(void 0===t)return this.tech_&&this.tech_.featuresPlaybackRate?this.cache_.lastPlaybackRate||this.techGet_("playbackRate"):1;this.techCall_("setPlaybackRate",t)},h.prototype.defaultPlaybackRate=function(t){return void 0!==t?this.techCall_("setDefaultPlaybackRate",t):this.tech_&&this.tech_.featuresPlaybackRate?this.techGet_("defaultPlaybackRate"):1},h.prototype.isAudio=function(t){if(void 0===t)return!!this.isAudio_;this.isAudio_=!!t},h.prototype.addTextTrack=function(t,e,i){if(this.tech_)return this.tech_.addTextTrack(t,e,i)},h.prototype.addRemoteTextTrack=function(t,e){if(this.tech_)return this.tech_.addRemoteTextTrack(t,e)},h.prototype.removeRemoteTextTrack=function(){var t=(0<arguments.length&&void 0!==arguments[0]?arguments[0]:{}).track,e=void 0===t?arguments[0]:t;if(this.tech_)return this.tech_.removeRemoteTextTrack(e)},h.prototype.getVideoPlaybackQuality=function(){return this.techGet_("getVideoPlaybackQuality")},h.prototype.videoWidth=function(){return this.tech_&&this.tech_.videoWidth&&this.tech_.videoWidth()||0},h.prototype.videoHeight=function(){return this.tech_&&this.tech_.videoHeight&&this.tech_.videoHeight()||0},h.prototype.language=function(t){if(void 0===t)return this.language_;this.language_=String(t).toLowerCase()},h.prototype.languages=function(){return Gt(h.prototype.options_.languages,this.languages_)},h.prototype.toJSON=function(){var t=Gt(this.options_),e=t.tracks;t.tracks=[];for(var i=0;i<e.length;i++){var r=e[i];(r=Gt(r)).player=void 0,t.tracks[i]=r}return t},h.prototype.createModal=function(t,e){var i=this;(e=e||{}).content=t||"";var r=new Ne(this,e);return this.addChild(r),r.on("dispose",function(){i.removeChild(r)}),r.open(),r},h.getTagSettings=function(t){var e={sources:[],tracks:[]},i=q(t),r=i["data-setup"];if(B(t,"vjs-fluid")&&(i.fluid=!0),null!==r){var n=Oe(r||"{}"),s=n[0],a=n[1];s&&f.error(s),C(i,a)}if(C(e,i),t.hasChildNodes())for(var o=t.childNodes,u=0,l=o.length;u<l;u++){var c=o[u],h=c.nodeName.toLowerCase();"source"===h?e.sources.push(q(c)):"track"===h&&e.tracks.push(q(c))}return e},h.prototype.flexNotSupported_=function(){var t=p.createElement("i");return!("flexBasis"in t.style||"webkitFlexBasis"in t.style||"mozFlexBasis"in t.style||"msFlexBasis"in t.style||"msFlexOrder"in t.style)},h}(Xt);Bi.names.forEach(function(t){var e=Bi[t];ms.prototype[e.getterName]=function(){return this.tech_?this.tech_[e.getterName]():(this[e.privateName]=this[e.privateName]||new e.ListClass,this[e.privateName])}}),ms.players={};var gs=g.navigator;ms.prototype.options_={techOrder:br.defaultTechOrder_,html5:{},flash:{},inactivityTimeout:2e3,playbackRates:[],children:["mediaLoader","posterImage","textTrackDisplay","loadingSpinner","bigPlayButton","controlBar","errorDisplay","textTrackSettings","resizeManager"],language:gs&&(gs.languages&&gs.languages[0]||gs.userLanguage||gs.language)||"en",languages:{},notSupportedMessage:"No compatible source was found for this media."},["ended","seeking","seekable","networkState","readyState"].forEach(function(t){ms.prototype[t]=function(){return this.techGet_(t)}}),ps.forEach(function(t){ms.prototype["handleTech"+zt(t)+"_"]=function(){return this.trigger(t)}}),Xt.registerComponent("Player",ms);var ys="plugin",vs="activePlugins_",_s={},bs=function(t){return _s.hasOwnProperty(t)},Ts=function(t){return bs(t)?_s[t]:void 0},Ss=function(t,e){t[vs]=t[vs]||{},t[vs][e]=!0},ks=function(t,e,i){var r=(i?"before":"")+"pluginsetup";t.trigger(r,e),t.trigger(r+":"+e.name,e)},Cs=function(n,s){return s.prototype.name=n,function(){ks(this,{name:n,plugin:s,instance:null},!0);for(var t=arguments.length,e=Array(t),i=0;i<t;i++)e[i]=arguments[i];var r=new(Function.prototype.bind.apply(s,[null].concat([this].concat(e))));return this[n]=function(){return r},ks(this,r.getEventHash()),r}},Es=function(){function s(t){if(y(this,s),this.constructor===s)throw new Error("Plugin must be sub-classed; not directly instantiated.");this.player=t,Vt(this),delete this.trigger,Wt(this,this.constructor.defaultState),Ss(t,this.name),this.dispose=Ot(this,this.dispose),t.on("dispose",this.dispose)}return s.prototype.version=function(){return this.constructor.VERSION},s.prototype.getEventHash=function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{};return t.name=this.name,t.plugin=this.constructor,t.instance=this,t},s.prototype.trigger=function(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{};return Tt(this.eventBusEl_,t,this.getEventHash(e))},s.prototype.handleStateChanged=function(t){},s.prototype.dispose=function(){var t=this.name,e=this.player;this.trigger("dispose"),this.off(),e.off("dispose",this.dispose),e[vs][t]=!1,this.player=this.state=null,e[t]=Cs(t,_s[t])},s.isBasic=function(t){var e="string"==typeof t?Ts(t):t;return"function"==typeof e&&!s.prototype.isPrototypeOf(e.prototype)},s.registerPlugin=function(t,e){if("string"!=typeof t)throw new Error('Illegal plugin name, "'+t+'", must be a string, was '+("undefined"==typeof t?"undefined":v(t))+".");if(bs(t))f.warn('A plugin named "'+t+'" already exists. You may want to avoid re-registering plugins!');else if(ms.prototype.hasOwnProperty(t))throw new Error('Illegal plugin name, "'+t+'", cannot share a name with an existing player method!');if("function"!=typeof e)throw new Error('Illegal plugin for "'+t+'", must be a function, was '+("undefined"==typeof e?"undefined":v(e))+".");var i,r,n;return _s[t]=e,t!==ys&&(s.isBasic(e)?ms.prototype[t]=(i=t,r=e,n=function(){ks(this,{name:i,plugin:r,instance:null},!0);var t=r.apply(this,arguments);return Ss(this,i),ks(this,{name:i,plugin:r,instance:t}),t},Object.keys(r).forEach(function(t){n[t]=r[t]}),n):ms.prototype[t]=Cs(t,e)),e},s.deregisterPlugin=function(t){if(t===ys)throw new Error("Cannot de-register base plugin.");bs(t)&&(delete _s[t],delete ms.prototype[t])},s.getPlugins=function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:Object.keys(_s),i=void 0;return t.forEach(function(t){var e=Ts(t);e&&((i=i||{})[t]=e)}),i},s.getPluginVersion=function(t){var e=Ts(t);return e&&e.VERSION||""},s}();Es.getPlugin=Ts,Es.BASE_PLUGIN_NAME=ys,Es.registerPlugin(ys,Es),ms.prototype.usingPlugin=function(t){return!!this[vs]&&!0===this[vs][t]},ms.prototype.hasPlugin=function(t){return!!bs(t)};var ws=function(t){return 0===t.indexOf("#")?t.slice(1):t};function As(t,i,e){var r=As.getPlayer(t);if(r)return i&&f.warn('Player "'+t+'" is already initialised. Options will not be applied.'),e&&r.ready(e),r;var n="string"==typeof t?nt("#"+ws(t)):t;if(!I(n))throw new TypeError("The element or ID supplied is not valid. (videojs)");p.body.contains(n)||f.warn("The element supplied is not included in the DOM"),i=i||{},As.hooks("beforesetup").forEach(function(t){var e=t(n,Gt(i));E(e)&&!Array.isArray(e)?i=Gt(i,e):f.error("please return an object in beforesetup hooks")});var s=Xt.getComponent("Player");return r=new s(n,i,e),As.hooks("setup").forEach(function(t){return t(r)}),r}if(As.hooks_={},As.hooks=function(t,e){return As.hooks_[t]=As.hooks_[t]||[],e&&(As.hooks_[t]=As.hooks_[t].concat(e)),As.hooks_[t]},As.hook=function(t,e){As.hooks(t,e)},As.hookOnce=function(i,t){As.hooks(i,[].concat(t).map(function(e){return function t(){return As.removeHook(i,t),e.apply(void 0,arguments)}}))},As.removeHook=function(t,e){var i=As.hooks(t).indexOf(e);return!(i<=-1)&&(As.hooks_[t]=As.hooks_[t].slice(),As.hooks_[t].splice(i,1),!0)},!0!==g.VIDEOJS_NO_DYNAMIC_STYLE&&x()){var Ls=nt(".vjs-styles-defaults");if(!Ls){Ls=Lt("vjs-styles-defaults");var Ps=nt("head");Ps&&Ps.insertBefore(Ls,Ps.firstChild),Pt(Ls,"\n      .video-js {\n        width: 300px;\n        height: 150px;\n      }\n\n      .vjs-fluid {\n        padding-top: 56.25%\n      }\n    ")}}At(1,As),As.VERSION=d,As.options=ms.prototype.options_,As.getPlayers=function(){return ms.players},As.getPlayer=function(t){var e=ms.players,i=void 0;if("string"==typeof t){var r=ws(t),n=e[r];if(n)return n;i=nt("#"+r)}else i=t;if(I(i)){var s=i,a=s.player,o=s.playerId;if(a||e[o])return a||e[o]}},As.getAllPlayers=function(){return Object.keys(ms.players).map(function(t){return ms.players[t]}).filter(Boolean)},As.players=ms.players,As.getComponent=Xt.getComponent,As.registerComponent=function(t,e){br.isTech(e)&&f.warn("The "+t+" tech was registered as a component. It should instead be registered using videojs.registerTech(name, tech)"),Xt.registerComponent.call(Xt,t,e)},As.getTech=br.getTech,As.registerTech=br.registerTech,As.use=function(t,e){Tr[t]=Tr[t]||[],Tr[t].push(e)},Object.defineProperty(As,"middleware",{value:{},writeable:!1,enumerable:!0}),Object.defineProperty(As.middleware,"TERMINATOR",{value:kr,writeable:!1,enumerable:!0}),As.browser=ye,As.TOUCH_ENABLED=ge,As.extend=function(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{},i=function(){t.apply(this,arguments)},r={};for(var n in"object"===("undefined"==typeof e?"undefined":v(e))?(e.constructor!==Object.prototype.constructor&&(i=e.constructor),r=e):"function"==typeof e&&(i=e),function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+("undefined"==typeof e?"undefined":v(e)));t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(t.super_=e)}(i,t),r)r.hasOwnProperty(n)&&(i.prototype[n]=r[n]);return i},As.mergeOptions=Gt,As.bind=Ot,As.registerPlugin=Es.registerPlugin,As.plugin=function(t,e){return f.warn("videojs.plugin() is deprecated; use videojs.registerPlugin() instead"),Es.registerPlugin(t,e)},As.getPlugins=Es.getPlugins,As.getPlugin=Es.getPlugin,As.getPluginVersion=Es.getPluginVersion,As.addLanguage=function(t,e){var i;return t=(""+t).toLowerCase(),As.options.languages=Gt(As.options.languages,((i={})[t]=e,i)),As.options.languages[t]},As.log=f,As.createTimeRange=As.createTimeRanges=be,As.formatTime=Yr,As.setFormatTime=function(t){Xr=t},As.resetFormatTime=function(){Xr=Gr},As.parseUrl=Je,As.isCrossOrigin=ei,As.EventTarget=Dt,As.on=_t,As.one=St,As.off=bt,As.trigger=Tt,As.xhr=Ci,As.TextTrack=Oi,As.AudioTrack=xi,As.VideoTrack=Ii,["isEl","isTextNode","createEl","hasClass","addClass","removeClass","toggleClass","setAttributes","getAttributes","emptyEl","appendContent","insertContent"].forEach(function(t){As[t]=function(){return f.warn("videojs."+t+"() is deprecated; use videojs.dom."+t+"() instead"),at[t].apply(null,arguments)}}),As.computedStyle=A,As.dom=at,As.url=ii;var Os=e(function(t,e){var i,c,r,n,h;i=/^((?:[a-zA-Z0-9+\-.]+:)?)(\/\/[^\/\;?#]*)?(.*?)??(;.*?)?(\?.*?)?(#.*?)?$/,c=/^([^\/;?#]*)(.*)$/,r=/(?:\/|^)\.(?=\/)/g,n=/(?:\/|^)\.\.\/(?!\.\.\/).*?(?=\/)/g,h={buildAbsoluteURL:function(t,e,i){if(i=i||{},t=t.trim(),!(e=e.trim())){if(!i.alwaysNormalize)return t;var r=h.parseURL(t);if(!r)throw new Error("Error trying to parse base URL.");return r.path=h.normalizePath(r.path),h.buildURLFromParts(r)}var n=h.parseURL(e);if(!n)throw new Error("Error trying to parse relative URL.");if(n.scheme)return i.alwaysNormalize?(n.path=h.normalizePath(n.path),h.buildURLFromParts(n)):e;var s=h.parseURL(t);if(!s)throw new Error("Error trying to parse base URL.");if(!s.netLoc&&s.path&&"/"!==s.path[0]){var a=c.exec(s.path);s.netLoc=a[1],s.path=a[2]}s.netLoc&&!s.path&&(s.path="/");var o={scheme:s.scheme,netLoc:n.netLoc,path:null,params:n.params,query:n.query,fragment:n.fragment};if(!n.netLoc&&(o.netLoc=s.netLoc,"/"!==n.path[0]))if(n.path){var u=s.path,l=u.substring(0,u.lastIndexOf("/")+1)+n.path;o.path=h.normalizePath(l)}else o.path=s.path,n.params||(o.params=s.params,n.query||(o.query=s.query));return null===o.path&&(o.path=i.alwaysNormalize?h.normalizePath(n.path):n.path),h.buildURLFromParts(o)},parseURL:function(t){var e=i.exec(t);return e?{scheme:e[1]||"",netLoc:e[2]||"",path:e[3]||"",params:e[4]||"",query:e[5]||"",fragment:e[6]||""}:null},normalizePath:function(t){for(t=t.split("").reverse().join("").replace(r,"");t.length!==(t=t.replace(n,"")).length;);return t.split("").reverse().join("")},buildURLFromParts:function(t){return t.scheme+t.netLoc+t.path+t.params+t.query+t.fragment}},t.exports=h}),xs=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},Is=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var i=arguments[e];for(var r in i)Object.prototype.hasOwnProperty.call(i,r)&&(t[r]=i[r])}return t},Ds=function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+("undefined"==typeof e?"undefined":v(e)));t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)},Rs=function(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!==("undefined"==typeof e?"undefined":v(e))&&"function"!=typeof e?t:e},Ms=function(){function t(){xs(this,t),this.listeners={}}return t.prototype.on=function(t,e){this.listeners[t]||(this.listeners[t]=[]),this.listeners[t].push(e)},t.prototype.off=function(t,e){if(!this.listeners[t])return!1;var i=this.listeners[t].indexOf(e);return this.listeners[t].splice(i,1),-1<i},t.prototype.trigger=function(t){var e=this.listeners[t],i=void 0,r=void 0,n=void 0;if(e)if(2===arguments.length)for(r=e.length,i=0;i<r;++i)e[i].call(this,arguments[1]);else for(n=Array.prototype.slice.call(arguments,1),r=e.length,i=0;i<r;++i)e[i].apply(this,n)},t.prototype.dispose=function(){this.listeners={}},t.prototype.pipe=function(e){this.on("data",function(t){e.push(t)})},t}(),Us=function(e){function i(){xs(this,i);var t=Rs(this,e.call(this));return t.buffer="",t}return Ds(i,e),i.prototype.push=function(t){var e=void 0;for(this.buffer+=t,e=this.buffer.indexOf("\n");-1<e;e=this.buffer.indexOf("\n"))this.trigger("data",this.buffer.substring(0,e)),this.buffer=this.buffer.substring(e+1)},i}(Ms),Ns=function(t){for(var e=t.split(new RegExp('(?:^|,)((?:[^=]*)=(?:"[^"]*"|[^,]*))')),i={},r=e.length,n=void 0;r--;)""!==e[r]&&((n=/([^=]*)=(.*)/.exec(e[r]).slice(1))[0]=n[0].replace(/^\s+|\s+$/g,""),n[1]=n[1].replace(/^\s+|\s+$/g,""),n[1]=n[1].replace(/^['"](.*)['"]$/g,"$1"),i[n[0]]=n[1]);return i},Bs=function(e){function i(){xs(this,i);var t=Rs(this,e.call(this));return t.customParsers=[],t}return Ds(i,e),i.prototype.push=function(t){var e=void 0,i=void 0;if(0!==(t=t.replace(/^[\u0000\s]+|[\u0000\s]+$/g,"")).length)if("#"===t[0]){for(var r=0;r<this.customParsers.length;r++)if(this.customParsers[r].call(this,t))return;if(0===t.indexOf("#EXT"))if(t=t.replace("\r",""),e=/^#EXTM3U/.exec(t))this.trigger("data",{type:"tag",tagType:"m3u"});else{if(e=/^#EXTINF:?([0-9\.]*)?,?(.*)?$/.exec(t))return i={type:"tag",tagType:"inf"},e[1]&&(i.duration=parseFloat(e[1])),e[2]&&(i.title=e[2]),void this.trigger("data",i);if(e=/^#EXT-X-TARGETDURATION:?([0-9.]*)?/.exec(t))return i={type:"tag",tagType:"targetduration"},e[1]&&(i.duration=parseInt(e[1],10)),void this.trigger("data",i);if(e=/^#ZEN-TOTAL-DURATION:?([0-9.]*)?/.exec(t))return i={type:"tag",tagType:"totalduration"},e[1]&&(i.duration=parseInt(e[1],10)),void this.trigger("data",i);if(e=/^#EXT-X-VERSION:?([0-9.]*)?/.exec(t))return i={type:"tag",tagType:"version"},e[1]&&(i.version=parseInt(e[1],10)),void this.trigger("data",i);if(e=/^#EXT-X-MEDIA-SEQUENCE:?(\-?[0-9.]*)?/.exec(t))return i={type:"tag",tagType:"media-sequence"},e[1]&&(i.number=parseInt(e[1],10)),void this.trigger("data",i);if(e=/^#EXT-X-DISCONTINUITY-SEQUENCE:?(\-?[0-9.]*)?/.exec(t))return i={type:"tag",tagType:"discontinuity-sequence"},e[1]&&(i.number=parseInt(e[1],10)),void this.trigger("data",i);if(e=/^#EXT-X-PLAYLIST-TYPE:?(.*)?$/.exec(t))return i={type:"tag",tagType:"playlist-type"},e[1]&&(i.playlistType=e[1]),void this.trigger("data",i);if(e=/^#EXT-X-BYTERANGE:?([0-9.]*)?@?([0-9.]*)?/.exec(t))return i={type:"tag",tagType:"byterange"},e[1]&&(i.length=parseInt(e[1],10)),e[2]&&(i.offset=parseInt(e[2],10)),void this.trigger("data",i);if(e=/^#EXT-X-ALLOW-CACHE:?(YES|NO)?/.exec(t))return i={type:"tag",tagType:"allow-cache"},e[1]&&(i.allowed=!/NO/.test(e[1])),void this.trigger("data",i);if(e=/^#EXT-X-MAP:?(.*)$/.exec(t)){if(i={type:"tag",tagType:"map"},e[1]){var n=Ns(e[1]);if(n.URI&&(i.uri=n.URI),n.BYTERANGE){var s=n.BYTERANGE.split("@"),a=s[0],o=s[1];i.byterange={},a&&(i.byterange.length=parseInt(a,10)),o&&(i.byterange.offset=parseInt(o,10))}}this.trigger("data",i)}else if(e=/^#EXT-X-STREAM-INF:?(.*)$/.exec(t)){if(i={type:"tag",tagType:"stream-inf"},e[1]){if(i.attributes=Ns(e[1]),i.attributes.RESOLUTION){var u=i.attributes.RESOLUTION.split("x"),l={};u[0]&&(l.width=parseInt(u[0],10)),u[1]&&(l.height=parseInt(u[1],10)),i.attributes.RESOLUTION=l}i.attributes.BANDWIDTH&&(i.attributes.BANDWIDTH=parseInt(i.attributes.BANDWIDTH,10)),i.attributes["PROGRAM-ID"]&&(i.attributes["PROGRAM-ID"]=parseInt(i.attributes["PROGRAM-ID"],10))}this.trigger("data",i)}else{if(e=/^#EXT-X-MEDIA:?(.*)$/.exec(t))return i={type:"tag",tagType:"media"},e[1]&&(i.attributes=Ns(e[1])),void this.trigger("data",i);if(e=/^#EXT-X-ENDLIST/.exec(t))this.trigger("data",{type:"tag",tagType:"endlist"});else if(e=/^#EXT-X-DISCONTINUITY/.exec(t))this.trigger("data",{type:"tag",tagType:"discontinuity"});else{if(e=/^#EXT-X-PROGRAM-DATE-TIME:?(.*)$/.exec(t))return i={type:"tag",tagType:"program-date-time"},e[1]&&(i.dateTimeString=e[1],i.dateTimeObject=new Date(e[1])),void this.trigger("data",i);if(e=/^#EXT-X-KEY:?(.*)$/.exec(t))return i={type:"tag",tagType:"key"},e[1]&&(i.attributes=Ns(e[1]),i.attributes.IV&&("0x"===i.attributes.IV.substring(0,2).toLowerCase()&&(i.attributes.IV=i.attributes.IV.substring(2)),i.attributes.IV=i.attributes.IV.match(/.{8}/g),i.attributes.IV[0]=parseInt(i.attributes.IV[0],16),i.attributes.IV[1]=parseInt(i.attributes.IV[1],16),i.attributes.IV[2]=parseInt(i.attributes.IV[2],16),i.attributes.IV[3]=parseInt(i.attributes.IV[3],16),i.attributes.IV=new Uint32Array(i.attributes.IV))),void this.trigger("data",i);if(e=/^#EXT-X-START:?(.*)$/.exec(t))return i={type:"tag",tagType:"start"},e[1]&&(i.attributes=Ns(e[1]),i.attributes["TIME-OFFSET"]=parseFloat(i.attributes["TIME-OFFSET"]),i.attributes.PRECISE=/YES/.test(i.attributes.PRECISE)),void this.trigger("data",i);if(e=/^#EXT-X-CUE-OUT-CONT:?(.*)?$/.exec(t))return i={type:"tag",tagType:"cue-out-cont"},e[1]?i.data=e[1]:i.data="",void this.trigger("data",i);if(e=/^#EXT-X-CUE-OUT:?(.*)?$/.exec(t))return i={type:"tag",tagType:"cue-out"},e[1]?i.data=e[1]:i.data="",void this.trigger("data",i);if(e=/^#EXT-X-CUE-IN:?(.*)?$/.exec(t))return i={type:"tag",tagType:"cue-in"},e[1]?i.data=e[1]:i.data="",void this.trigger("data",i);this.trigger("data",{type:"tag",data:t.slice(4)})}}}else this.trigger("data",{type:"comment",text:t.slice(1)})}else this.trigger("data",{type:"uri",uri:t})},i.prototype.addParser=function(t){var e=this,i=t.expression,r=t.customType,n=t.dataParser,s=t.segment;"function"!=typeof n&&(n=function(t){return t}),this.customParsers.push(function(t){if(i.exec(t))return e.trigger("data",{type:"custom",data:n(t),customType:r,segment:s}),!0})},i}(Ms),js=function(e){function i(){xs(this,i);var t=Rs(this,e.call(this));t.lineStream=new Us,t.parseStream=new Bs,t.lineStream.pipe(t.parseStream);var n=t,s=[],a={},o=void 0,u=void 0,l={AUDIO:{},VIDEO:{},"CLOSED-CAPTIONS":{},SUBTITLES:{}},c=0;return t.manifest={allowCache:!0,discontinuityStarts:[],segments:[]},t.parseStream.on("data",function(e){var i=void 0,r=void 0;({tag:function(){({"allow-cache":function(){this.manifest.allowCache=e.allowed,"allowed"in e||(this.trigger("info",{message:"defaulting allowCache to YES"}),this.manifest.allowCache=!0)},byterange:function(){var t={};"length"in e&&((a.byterange=t).length=e.length,"offset"in e||(this.trigger("info",{message:"defaulting offset to zero"}),e.offset=0)),"offset"in e&&((a.byterange=t).offset=e.offset)},endlist:function(){this.manifest.endList=!0},inf:function(){"mediaSequence"in this.manifest||(this.manifest.mediaSequence=0,this.trigger("info",{message:"defaulting media sequence to zero"})),"discontinuitySequence"in this.manifest||(this.manifest.discontinuitySequence=0,this.trigger("info",{message:"defaulting discontinuity sequence to zero"})),0<e.duration&&(a.duration=e.duration),0===e.duration&&(a.duration=.01,this.trigger("info",{message:"updating zero segment duration to a small value"})),this.manifest.segments=s},key:function(){e.attributes?"NONE"!==e.attributes.METHOD?e.attributes.URI?(e.attributes.METHOD||this.trigger("warn",{message:"defaulting key method to AES-128"}),u={method:e.attributes.METHOD||"AES-128",uri:e.attributes.URI},"undefined"!=typeof e.attributes.IV&&(u.iv=e.attributes.IV)):this.trigger("warn",{message:"ignoring key declaration without URI"}):u=null:this.trigger("warn",{message:"ignoring key declaration without attribute list"})},"media-sequence":function(){isFinite(e.number)?this.manifest.mediaSequence=e.number:this.trigger("warn",{message:"ignoring invalid media sequence: "+e.number})},"discontinuity-sequence":function(){isFinite(e.number)?(this.manifest.discontinuitySequence=e.number,c=e.number):this.trigger("warn",{message:"ignoring invalid discontinuity sequence: "+e.number})},"playlist-type":function(){/VOD|EVENT/.test(e.playlistType)?this.manifest.playlistType=e.playlistType:this.trigger("warn",{message:"ignoring unknown playlist type: "+e.playlist})},map:function(){o={},e.uri&&(o.uri=e.uri),e.byterange&&(o.byterange=e.byterange)},"stream-inf":function(){this.manifest.playlists=s,this.manifest.mediaGroups=this.manifest.mediaGroups||l,e.attributes?(a.attributes||(a.attributes={}),Is(a.attributes,e.attributes)):this.trigger("warn",{message:"ignoring empty stream-inf attributes"})},media:function(){if(this.manifest.mediaGroups=this.manifest.mediaGroups||l,e.attributes&&e.attributes.TYPE&&e.attributes["GROUP-ID"]&&e.attributes.NAME){var t=this.manifest.mediaGroups[e.attributes.TYPE];t[e.attributes["GROUP-ID"]]=t[e.attributes["GROUP-ID"]]||{},i=t[e.attributes["GROUP-ID"]],(r={default:/yes/i.test(e.attributes.DEFAULT)}).default?r.autoselect=!0:r.autoselect=/yes/i.test(e.attributes.AUTOSELECT),e.attributes.LANGUAGE&&(r.language=e.attributes.LANGUAGE),e.attributes.URI&&(r.uri=e.attributes.URI),e.attributes["INSTREAM-ID"]&&(r.instreamId=e.attributes["INSTREAM-ID"]),e.attributes.CHARACTERISTICS&&(r.characteristics=e.attributes.CHARACTERISTICS),e.attributes.FORCED&&(r.forced=/yes/i.test(e.attributes.FORCED)),i[e.attributes.NAME]=r}else this.trigger("warn",{message:"ignoring incomplete or missing media group"})},discontinuity:function(){c+=1,a.discontinuity=!0,this.manifest.discontinuityStarts.push(s.length)},"program-date-time":function(){"undefined"==typeof this.manifest.dateTimeString&&(this.manifest.dateTimeString=e.dateTimeString,this.manifest.dateTimeObject=e.dateTimeObject),a.dateTimeString=e.dateTimeString,a.dateTimeObject=e.dateTimeObject},targetduration:function(){!isFinite(e.duration)||e.duration<0?this.trigger("warn",{message:"ignoring invalid target duration: "+e.duration}):this.manifest.targetDuration=e.duration},totalduration:function(){!isFinite(e.duration)||e.duration<0?this.trigger("warn",{message:"ignoring invalid total duration: "+e.duration}):this.manifest.totalDuration=e.duration},start:function(){e.attributes&&!isNaN(e.attributes["TIME-OFFSET"])?this.manifest.start={timeOffset:e.attributes["TIME-OFFSET"],precise:e.attributes.PRECISE}:this.trigger("warn",{message:"ignoring start declaration without appropriate attribute list"})},"cue-out":function(){a.cueOut=e.data},"cue-out-cont":function(){a.cueOutCont=e.data},"cue-in":function(){a.cueIn=e.data}}[e.tagType]||function(){}).call(n)},uri:function(){a.uri=e.uri,s.push(a),!this.manifest.targetDuration||"duration"in a||(this.trigger("warn",{message:"defaulting segment duration to the target duration"}),a.duration=this.manifest.targetDuration),u&&(a.key=u),a.timeline=c,o&&(a.map=o),a={}},comment:function(){},custom:function(){e.segment?(a.custom=a.custom||{},a.custom[e.customType]=e.data):(this.manifest.custom=this.manifest.custom||{},this.manifest.custom[e.customType]=e.data)}})[e.type].call(n)}),t}return Ds(i,e),i.prototype.push=function(t){this.lineStream.push(t)},i.prototype.end=function(){this.lineStream.push("\n")},i.prototype.addParser=function(t){this.parseStream.addParser(t)},i}(Ms),Fs=function(t){var e,i=t.attributes,r=t.segments,n={attributes:(e={NAME:i.id,AUDIO:"audio",SUBTITLES:"subs",RESOLUTION:{width:i.width,height:i.height},CODECS:i.codecs,BANDWIDTH:i.bandwidth},e["PROGRAM-ID"]=1,e),uri:"",endList:"static"===(i.type||"static"),timeline:i.periodIndex,resolvedUri:"",targetDuration:i.duration,segments:r,mediaSequence:r.length?r[0].number:1};return i.contentProtection&&(n.contentProtection=i.contentProtection),n},Hs=function(t){return!!t&&"object"===("undefined"==typeof t?"undefined":v(t))},Vs=function r(){for(var t=arguments.length,e=Array(t),i=0;i<t;i++)e[i]=arguments[i];return e.reduce(function(e,i){return Object.keys(i).forEach(function(t){Array.isArray(e[t])&&Array.isArray(i[t])?e[t]=e[t].concat(i[t]):Hs(e[t])&&Hs(i[t])?e[t]=r(e[t],i[t]):e[t]=i[t]}),e},{})},qs=function(t,e){return/^[a-z]+:/i.test(e)?e:(/\/\//i.test(t)||(t=Os.buildAbsoluteURL(g.location.href,t)),Os.buildAbsoluteURL(t,e))},Ws=function(t){var e=t.baseUrl,i=void 0===e?"":e,r=t.source,n=void 0===r?"":r,s=t.range,a=void 0===s?"":s,o={uri:n,resolvedUri:qs(i||"",n)};if(a){var u=a.split("-"),l=parseInt(u[0],10),c=parseInt(u[1],10);o.byterange={length:c-l,offset:l}}return o},zs=function(t,e){for(var i,r,n,s,a,o,u,l,c,h,d,p,f=t.type,m=void 0===f?"static":f,g=t.minimumUpdatePeriod,y=void 0===g?0:g,v=t.media,_=void 0===v?"":v,b=t.sourceDuration,T=t.timescale,S=void 0===T?1:T,k=t.startNumber,C=void 0===k?1:k,E=t.periodIndex,w=[],A=-1,L=0;L<e.length;L++){var P=e[L],O=P.d,x=P.r||0,I=P.t||0;A<0&&(A=I),I&&A<I&&(A=I);var D=void 0;if(x<0){var R=L+1;R===e.length?"dynamic"===m&&0<y&&0<_.indexOf("$Number$")?(r=A,n=O,void 0,s=(i=t).NOW,a=i.clientOffset,o=i.availabilityStartTime,u=i.timescale,l=void 0===u?1:u,c=i.start,h=void 0===c?0:c,d=i.minimumUpdatePeriod,p=(s+a)/1e3+(void 0===d?0:d)-(o+h),D=Math.ceil((p*l-r)/n)):D=(b*S-A)/O:D=(e[R].t-A)/O}else D=x+1;for(var M=C+w.length+D,U=C+w.length;U<M;)w.push({number:U,duration:O/S,time:A,timeline:E}),A+=O,U++}return w},Gs=function(t){return t.reduce(function(t,e){return t.concat(e)},[])},Xs=function(t){if(!t.length)return[];for(var e=[],i=0;i<t.length;i++)e.push(t[i]);return e},Ys={static:function(t){var e=t.duration,i=t.timescale,r=void 0===i?1:i,n=t.sourceDuration;return{start:0,end:Math.ceil(n/(e/r))}},dynamic:function(t){var e=t.NOW,i=t.clientOffset,r=t.availabilityStartTime,n=t.timescale,s=void 0===n?1:n,a=t.duration,o=t.start,u=void 0===o?0:o,l=t.minimumUpdatePeriod,c=void 0===l?0:l,h=t.timeShiftBufferDepth,d=void 0===h?1/0:h,p=(e+i)/1e3,f=r+u,m=p+c-f,g=Math.ceil(m*s/a),y=Math.floor((p-f-d)*s/a),v=Math.floor((p-f)*s/a);return{start:Math.max(0,y),end:Math.min(g,v)}}},$s=function(t){var o,e=t.type,i=void 0===e?"static":e,r=t.duration,n=t.timescale,s=void 0===n?1:n,a=t.sourceDuration,u=Ys[i](t),l=function(t,e){for(var i=[],r=t;r<e;r++)i.push(r);return i}(u.start,u.end).map((o=t,function(t,e){var i=o.duration,r=o.timescale,n=void 0===r?1:r,s=o.periodIndex,a=o.startNumber;return{number:(void 0===a?1:a)+t,duration:i/n,timeline:s,time:e*i}}));if("static"===i){var c=l.length-1;l[c].duration=a-r/s*c}return l},Ks=/\$([A-z]*)(?:(%0)([0-9]+)d)?\$/g,Qs=function(t,e){return t.replace(Ks,(s=e,function(t,e,i,r){if("$$"===t)return"$";if("undefined"==typeof s[e])return t;var n=""+s[e];return"RepresentationID"===e?n:(r=i?parseInt(r,10):1)<=n.length?n:""+new Array(r-n.length+1).join("0")+n}));var s},Js=function(i,t){var e,r,n={RepresentationID:i.id,Bandwidth:i.bandwidth||0},s=i.initialization,a=void 0===s?{sourceURL:"",range:""}:s,o=Ws({baseUrl:i.baseUrl,source:Qs(a.sourceURL,n),range:a.range});return(r=t,(e=i).duration||r?e.duration?$s(e):zs(e,r):[{number:e.startNumber||1,duration:e.sourceDuration,time:0,timeline:e.periodIndex}]).map(function(t){n.Number=t.number,n.Time=t.time;var e=Qs(i.media||"",n);return{uri:e,timeline:t.timeline,duration:t.duration,resolvedUri:qs(i.baseUrl||"",e),map:o,number:t.number}})},Zs="INVALID_NUMBER_OF_PERIOD",ta="DASH_EMPTY_MANIFEST",ea="DASH_INVALID_XML",ia="NO_BASE_URL",ra="SEGMENT_TIME_UNSPECIFIED",na="UNSUPPORTED_UTC_TIMING_SCHEME",sa=function(u,t){var e=u.duration,i=u.segmentUrls,r=void 0===i?[]:i;if(!e&&!t||e&&t)throw new Error(ra);var n=r.map(function(t){return i=t,r=(e=u).baseUrl,n=e.initialization,a=Ws({baseUrl:r,source:(s=void 0===n?{}:n).sourceURL,range:s.range}),(o=Ws({baseUrl:r,source:i.media,range:i.mediaRange})).map=a,o;var e,i,r,n,s,a,o}),s=void 0;return e&&(s=$s(u)),t&&(s=zs(u,t)),s.map(function(t,e){if(n[e]){var i=n[e];return i.timeline=t.timeline,i.duration=t.duration,i.number=t.number,i}}).filter(function(t){return t})},aa=function(t){var e=t.baseUrl,i=t.initialization,r=void 0===i?{}:i,n=t.sourceDuration,s=t.timescale,a=void 0===s?1:s,o=t.indexRange,u=void 0===o?"":o,l=t.duration;if(!e)throw new Error(ia);var c=Ws({baseUrl:e,source:r.sourceURL,range:r.range}),h=Ws({baseUrl:e,source:e,range:u});if(h.map=c,l){var d=$s(t);d.length&&(h.duration=d[0].duration,h.timeline=d[0].timeline)}else n&&(h.duration=n/a,h.timeline=0);return h.number=0,[h]},oa=function(t){var e=t.attributes,i=t.segmentInfo,r=void 0,n=void 0;if(i.template?(n=Js,r=Vs(e,i.template)):i.base?(n=aa,r=Vs(e,i.base)):i.list&&(n=sa,r=Vs(e,i.list)),!n)return{attributes:e};var s=n(r,i.timeline);if(r.duration){var a=r,o=a.duration,u=a.timescale,l=void 0===u?1:u;r.duration=o/l}else s.length?r.duration=s.reduce(function(t,e){return Math.max(t,Math.ceil(e.duration))},0):r.duration=0;return{attributes:r,segments:s}},ua=function(t,e){return Xs(t.childNodes).filter(function(t){return t.tagName===e})},la=function(t){return t.textContent.trim()},ca=function(t){var e=/P(?:(\d*)Y)?(?:(\d*)M)?(?:(\d*)D)?(?:T(?:(\d*)H)?(?:(\d*)M)?(?:([\d.]*)S)?)?/.exec(t);if(!e)return 0;var i=e.slice(1),r=i[0],n=i[1],s=i[2],a=i[3],o=i[4],u=i[5];return 31536e3*parseFloat(r||0)+2592e3*parseFloat(n||0)+86400*parseFloat(s||0)+3600*parseFloat(a||0)+60*parseFloat(o||0)+parseFloat(u||0)},ha={mediaPresentationDuration:function(t){return ca(t)},availabilityStartTime:function(t){return/^\d+-\d+-\d+T\d+:\d+:\d+(\.\d+)?$/.test(e=t)&&(e+="Z"),Date.parse(e)/1e3;var e},minimumUpdatePeriod:function(t){return ca(t)},timeShiftBufferDepth:function(t){return ca(t)},start:function(t){return ca(t)},width:function(t){return parseInt(t,10)},height:function(t){return parseInt(t,10)},bandwidth:function(t){return parseInt(t,10)},startNumber:function(t){return parseInt(t,10)},timescale:function(t){return parseInt(t,10)},duration:function(t){var e=parseInt(t,10);return isNaN(e)?ca(t):e},d:function(t){return parseInt(t,10)},t:function(t){return parseInt(t,10)},r:function(t){return parseInt(t,10)},DEFAULT:function(t){return t}},da=function(t){return t&&t.attributes?Xs(t.attributes).reduce(function(t,e){var i=ha[e.name]||ha.DEFAULT;return t[e.name]=i(e.value),t},{}):{}};var pa,fa,ma={"urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b":"org.w3.clearkey","urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed":"com.widevine.alpha","urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95":"com.microsoft.playready","urn:uuid:f239e769-efa3-4850-9c16-a903c6932efb":"com.adobe.primetime"},ga=function(t,i){return i.length?Gs(t.map(function(e){return i.map(function(t){return qs(e,la(t))})})):t},ya=function(t){var e=ua(t,"SegmentTemplate")[0],i=ua(t,"SegmentList")[0],r=i&&ua(i,"SegmentURL").map(function(t){return Vs({tag:"SegmentURL"},da(t))}),n=ua(t,"SegmentBase")[0],s=i||e,a=s&&ua(s,"SegmentTimeline")[0],o=i||n||e,u=o&&ua(o,"Initialization")[0],l=e&&da(e);l&&u?l.initialization=u&&da(u):l&&l.initialization&&(l.initialization={sourceURL:l.initialization});var c={template:l,timeline:a&&ua(a,"S").map(function(t){return da(t)}),list:i&&Vs(da(i),{segmentUrls:r,initialization:da(u)}),base:n&&Vs(da(n),{initialization:da(u)})};return Object.keys(c).forEach(function(t){c[t]||delete c[t]}),c},va=function(t){return t.reduce(function(t,e){var i=da(e),r=ma[i.schemeIdUri];if(r){t[r]={attributes:i};var n=ua(e,"cenc:pssh")[0];if(n){var s=la(n),a=s&&function(t){for(var e=g.atob(t),i=new Uint8Array(e.length),r=0;r<e.length;r++)i[r]=e.charCodeAt(r);return i}(s);t[r].pssh=a}}return t},{})},_a=function(p,f,m){return function(t){var e=da(t),i=ga(f,ua(t,"BaseURL")),r=ua(t,"Role")[0],n={role:da(r)},s=Vs(p,e,n),a=va(ua(t,"ContentProtection"));Object.keys(a).length&&(s=Vs(s,{contentProtection:a}));var o,u,l,c=ya(t),h=ua(t,"Representation"),d=Vs(m,c);return Gs(h.map((o=s,u=i,l=d,function(t){var e=ua(t,"BaseURL"),i=ga(u,e),r=Vs(o,da(t)),n=ya(t);return i.map(function(t){return{segmentInfo:Vs(l,n),attributes:Vs(r,{baseUrl:t})}})})))}},ba=function(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{},i=e.manifestUri,r=void 0===i?"":i,n=e.NOW,s=void 0===n?Date.now():n,a=e.clientOffset,o=void 0===a?0:a,u=ua(t,"Period");if(1!==u.length)throw new Error(Zs);var l,c,h=da(t),d=ga([r],ua(t,"BaseURL"));return h.sourceDuration=h.mediaPresentationDuration||0,h.NOW=s,h.clientOffset=o,Gs(u.map((l=h,c=d,function(t,e){var i=ga(c,ua(t,"BaseURL")),r=da(t),n=Vs(l,r,{periodIndex:e}),s=ua(t,"AdaptationSet"),a=ya(t);return Gs(s.map(_a(n,i,a)))})))},Ta=function(t){if(""===t)throw new Error(ta);var e=(new g.DOMParser).parseFromString(t,"application/xml"),i=e&&"MPD"===e.documentElement.tagName?e.documentElement:null;if(!i||i&&0<i.getElementsByTagName("parsererror").length)throw new Error(ea);return i},Sa=function(t,e){return function(t){var e;if(!t.length)return{};var i=t[0].attributes,r=i.sourceDuration,n=i.minimumUpdatePeriod,s=void 0===n?0:n,a=t.filter(function(t){var e=t.attributes;return"video/mp4"===e.mimeType||"video"===e.contentType}).map(Fs),o=t.filter(function(t){var e=t.attributes;return"audio/mp4"===e.mimeType||"audio"===e.contentType}),u=t.filter(function(t){var e=t.attributes;return"text/vtt"===e.mimeType||"text"===e.contentType}),l={allowCache:!0,discontinuityStarts:[],segments:[],endList:!0,mediaGroups:(e={AUDIO:{},VIDEO:{}},e["CLOSED-CAPTIONS"]={},e.SUBTITLES={},e),uri:"",duration:r,playlists:a,minimumUpdatePeriod:1e3*s};return o.length&&(l.mediaGroups.AUDIO.audio=o.reduce(function(t,e){var i,r,n,s,a,o=e.attributes.role&&e.attributes.role.value||"main",u=e.attributes.lang||"",l="main";return u&&(l=e.attributes.lang+" ("+o+")"),t[l]&&t[l].playlists[0].attributes.BANDWIDTH>e.attributes.bandwidth||(t[l]={language:u,autoselect:!0,default:"main"===o,playlists:[(i=e,n=i.attributes,s=i.segments,a={attributes:(r={NAME:n.id,BANDWIDTH:n.bandwidth,CODECS:n.codecs},r["PROGRAM-ID"]=1,r),uri:"",endList:"static"===(n.type||"static"),timeline:n.periodIndex,resolvedUri:"",targetDuration:n.duration,segments:s,mediaSequence:s.length?s[0].number:1},n.contentProtection&&(a.contentProtection=n.contentProtection),a)],uri:""}),t},{})),u.length&&(l.mediaGroups.SUBTITLES.subs=u.reduce(function(t,e){var i,r,n,s,a=e.attributes.lang||"text";return t[a]||(t[a]={language:a,default:!1,autoselect:!1,playlists:[(i=e,n=i.attributes,s=i.segments,"undefined"==typeof s&&(s=[{uri:n.baseUrl,timeline:n.periodIndex,resolvedUri:n.baseUrl||"",duration:n.sourceDuration,number:0}],n.duration=n.sourceDuration),{attributes:(r={NAME:n.id,BANDWIDTH:n.bandwidth},r["PROGRAM-ID"]=1,r),uri:"",endList:"static"===(n.type||"static"),timeline:n.periodIndex,resolvedUri:n.baseUrl||"",targetDuration:n.duration,segments:s,mediaSequence:s.length?s[0].number:1})],uri:""}),t},{})),l}(ba(Ta(t),e).map(oa))},ka=function(t){return function(t){var e=ua(t,"UTCTiming")[0];if(!e)return null;var i=da(e);switch(i.schemeIdUri){case"urn:mpeg:dash:utc:http-head:2014":case"urn:mpeg:dash:utc:http-head:2012":i.method="HEAD";break;case"urn:mpeg:dash:utc:http-xsdate:2014":case"urn:mpeg:dash:utc:http-iso:2014":case"urn:mpeg:dash:utc:http-xsdate:2012":case"urn:mpeg:dash:utc:http-iso:2012":i.method="GET";break;case"urn:mpeg:dash:utc:direct:2014":case"urn:mpeg:dash:utc:direct:2012":i.method="DIRECT",i.value=Date.parse(i.value);break;case"urn:mpeg:dash:utc:http-ntp:2014":case"urn:mpeg:dash:utc:ntp:2014":case"urn:mpeg:dash:utc:sntp:2014":default:throw new Error(na)}return i}(Ta(t))},Ca={toUnsigned:function(t){return t>>>0}},Ea=Ca.toUnsigned,wa=Object.freeze({default:Ca,__moduleExports:Ca,toUnsigned:Ea}),Aa=(wa&&Ca||wa).toUnsigned;pa=function(t,e){var i,r,n,s,a,o=[];if(!e.length)return null;for(i=0;i<t.byteLength;)r=Aa(t[i]<<24|t[i+1]<<16|t[i+2]<<8|t[i+3]),n=fa(t.subarray(i+4,i+8)),s=1<r?i+r:t.byteLength,n===e[0]&&(1===e.length?o.push(t.subarray(i+8,s)):(a=pa(t.subarray(i+8,s),e.slice(1))).length&&(o=o.concat(a))),i=s;return o};fa=function(t){var e="";return e+=String.fromCharCode(t[0]),e+=String.fromCharCode(t[1]),e+=String.fromCharCode(t[2]),e+=String.fromCharCode(t[3])};var La=function(t){return pa(t,["moov","trak"]).reduce(function(t,e){var i,r,n,s,a;return(i=pa(e,["tkhd"])[0])?(r=i[0],s=Aa(i[n=0===r?12:20]<<24|i[n+1]<<16|i[n+2]<<8|i[n+3]),(a=pa(e,["mdia","mdhd"])[0])?(n=0===(r=a[0])?12:20,t[s]=Aa(a[n]<<24|a[n+1]<<16|a[n+2]<<8|a[n+3]),t):null):null},{})},Pa=function(n,t){var e,i,r;return e=pa(t,["moof","traf"]),i=[].concat.apply([],e.map(function(r){return pa(r,["tfhd"]).map(function(t){var e,i;return e=Aa(t[4]<<24|t[5]<<16|t[6]<<8|t[7]),i=n[e]||9e4,(pa(r,["tfdt"]).map(function(t){var e,i;return e=t[0],i=Aa(t[4]<<24|t[5]<<16|t[6]<<8|t[7]),1===e&&(i*=Math.pow(2,32),i+=Aa(t[8]<<24|t[9]<<16|t[10]<<8|t[11])),i})[0]||1/0)/i})})),r=Math.min.apply(null,i),isFinite(r)?r:0},Oa={H264_STREAM_TYPE:27,ADTS_STREAM_TYPE:15,METADATA_STREAM_TYPE:21},xa=Oa.H264_STREAM_TYPE,Ia=Oa.ADTS_STREAM_TYPE,Da=Oa.METADATA_STREAM_TYPE,Ra=Object.freeze({default:Oa,__moduleExports:Oa,H264_STREAM_TYPE:xa,ADTS_STREAM_TYPE:Ia,METADATA_STREAM_TYPE:Da}),Ma=function(){this.init=function(){var s={};this.on=function(t,e){s[t]||(s[t]=[]),s[t]=s[t].concat(e)},this.off=function(t,e){var i;return!!s[t]&&(i=s[t].indexOf(e),s[t]=s[t].slice(),s[t].splice(i,1),-1<i)},this.trigger=function(t){var e,i,r,n;if(e=s[t])if(2===arguments.length)for(r=e.length,i=0;i<r;++i)e[i].call(this,arguments[1]);else{for(n=[],i=arguments.length,i=1;i<arguments.length;++i)n.push(arguments[i]);for(r=e.length,i=0;i<r;++i)e[i].apply(this,n)}},this.dispose=function(){s={}}}};Ma.prototype.pipe=function(e){return this.on("data",function(t){e.push(t)}),this.on("done",function(t){e.flush(t)}),e},Ma.prototype.push=function(t){this.trigger("data",t)},Ma.prototype.flush=function(t){this.trigger("done",t)};var Ua=Ma,Na=Object.freeze({default:Ua,__moduleExports:Ua}),Ba=function(t,e){var i=1;for(e<t&&(i=-1);4294967296<Math.abs(e-t);)t+=8589934592*i;return t},ja=function t(e){var i,r;t.prototype.init.call(this),this.type_=e,this.push=function(t){t.type===this.type_&&(void 0===r&&(r=t.dts),t.dts=Ba(t.dts,r),t.pts=Ba(t.pts,r),i=t.dts,this.trigger("data",t))},this.flush=function(){r=i,this.trigger("done")},this.discontinuity=function(){i=r=void 0}};ja.prototype=new(Na&&Ua||Na);var Fa={TimestampRolloverStream:ja,handleRollover:Ba},Ha=Fa.TimestampRolloverStream,Va=Fa.handleRollover,qa=Object.freeze({default:Fa,__moduleExports:Fa,TimestampRolloverStream:Ha,handleRollover:Va}),Wa=Ra&&Oa||Ra,za=function(t){var e=31&t[1];return e<<=8,e|=t[2]},Ga=function(t){return!!(64&t[1])},Xa=function(t){var e=0;return 1<(48&t[3])>>>4&&(e+=t[4]+1),e},Ya=function(t){switch(t){case 5:return"slice_layer_without_partitioning_rbsp_idr";case 6:return"sei_rbsp";case 7:return"seq_parameter_set_rbsp";case 8:return"pic_parameter_set_rbsp";case 9:return"access_unit_delimiter_rbsp";default:return null}},$a={parseType:function(t,e){var i=za(t);return 0===i?"pat":i===e?"pmt":e?"pes":null},parsePat:function(t){var e=Ga(t),i=4+Xa(t);return e&&(i+=t[i]+1),(31&t[i+10])<<8|t[i+11]},parsePmt:function(t){var e={},i=Ga(t),r=4+Xa(t);if(i&&(r+=t[r]+1),1&t[r+5]){var n;n=3+((15&t[r+1])<<8|t[r+2])-4;for(var s=12+((15&t[r+10])<<8|t[r+11]);s<n;){var a=r+s;e[(31&t[a+1])<<8|t[a+2]]=t[a],s+=5+((15&t[a+3])<<8|t[a+4])}return e}},parsePayloadUnitStartIndicator:Ga,parsePesType:function(t,e){switch(e[za(t)]){case Wa.H264_STREAM_TYPE:return"video";case Wa.ADTS_STREAM_TYPE:return"audio";case Wa.METADATA_STREAM_TYPE:return"timed-metadata";default:return null}},parsePesTime:function(t){if(!Ga(t))return null;var e=4+Xa(t);if(e>=t.byteLength)return null;var i,r=null;return 192&(i=t[e+7])&&((r={}).pts=(14&t[e+9])<<27|(255&t[e+10])<<20|(254&t[e+11])<<12|(255&t[e+12])<<5|(254&t[e+13])>>>3,r.pts*=4,r.pts+=(6&t[e+13])>>>1,r.dts=r.pts,64&i&&(r.dts=(14&t[e+14])<<27|(255&t[e+15])<<20|(254&t[e+16])<<12|(255&t[e+17])<<5|(254&t[e+18])>>>3,r.dts*=4,r.dts+=(6&t[e+18])>>>1)),r},videoPacketContainsKeyFrame:function(t){for(var e=4+Xa(t),i=t.subarray(e),r=0,n=0,s=!1;n<i.byteLength-3;n++)if(1===i[n+2]){r=n+5;break}for(;r<i.byteLength;)switch(i[r]){case 0:if(0!==i[r-1]){r+=2;break}if(0!==i[r-2]){r++;break}for(n+3!==r-2&&"slice_layer_without_partitioning_rbsp_idr"===Ya(31&i[n+3])&&(s=!0);1!==i[++r]&&r<i.length;);n=r-2,r+=3;break;case 1:if(0!==i[r-1]||0!==i[r-2]){r+=3;break}"slice_layer_without_partitioning_rbsp_idr"===Ya(31&i[n+3])&&(s=!0),n=r-2,r+=3;break;default:r+=3}return i=i.subarray(n),r-=n,n=0,i&&3<i.byteLength&&"slice_layer_without_partitioning_rbsp_idr"===Ya(31&i[n+3])&&(s=!0),s}},Ka=$a.parseType,Qa=$a.parsePat,Ja=$a.parsePmt,Za=$a.parsePayloadUnitStartIndicator,to=$a.parsePesType,eo=$a.parsePesTime,io=$a.videoPacketContainsKeyFrame,ro=Object.freeze({default:$a,__moduleExports:$a,parseType:Ka,parsePat:Qa,parsePmt:Ja,parsePayloadUnitStartIndicator:Za,parsePesType:to,parsePesTime:eo,videoPacketContainsKeyFrame:io}),no=[96e3,88200,64e3,48e3,44100,32e3,24e3,22050,16e3,12e3,11025,8e3,7350],so=function(t){return t[0]<<21|t[1]<<14|t[2]<<7|t[3]},ao={parseId3TagSize:function(t,e){var i=t[e+6]<<21|t[e+7]<<14|t[e+8]<<7|t[e+9];return(16&t[e+5])>>4?i+20:i+10},parseAdtsSize:function(t,e){var i=(224&t[e+5])>>5,r=t[e+4]<<3;return 6144&t[e+3]|r|i},parseType:function(t,e){return t[e]==="I".charCodeAt(0)&&t[e+1]==="D".charCodeAt(0)&&t[e+2]==="3".charCodeAt(0)?"timed-metadata":!0&t[e]&&240==(240&t[e+1])?"audio":null},parseSampleRate:function(t){for(var e=0;e+5<t.length;){if(255===t[e]&&240==(246&t[e+1]))return no[(60&t[e+2])>>>2];e++}return null},parseAacTimestamp:function(t){var e,i,r;e=10,64&t[5]&&(e+=4,e+=so(t.subarray(10,14)));do{if((i=so(t.subarray(e+4,e+8)))<1)return null;if("PRIV"===String.fromCharCode(t[e],t[e+1],t[e+2],t[e+3])){r=t.subarray(e+10,e+i+10);for(var n=0;n<r.byteLength;n++)if(0===r[n]){if("com.apple.streaming.transportStreamTimestamp"===unescape(function(t,e,i){var r,n="";for(r=e;r<i;r++)n+="%"+("00"+t[r].toString(16)).slice(-2);return n}(r,0,n))){var s=r.subarray(n+1),a=(1&s[3])<<30|s[4]<<22|s[5]<<14|s[6]<<6|s[7]>>>2;return a*=4,a+=3&s[7]}break}}e+=10,e+=i}while(e<t.byteLength);return null}},oo=ao.parseId3TagSize,uo=ao.parseAdtsSize,lo=ao.parseType,co=ao.parseSampleRate,ho=ao.parseAacTimestamp,po=Object.freeze({default:ao,__moduleExports:ao,parseId3TagSize:oo,parseAdtsSize:uo,parseType:lo,parseSampleRate:co,parseAacTimestamp:ho}),fo=ro&&$a||ro,mo=po&&ao||po,go=(qa&&Fa||qa).handleRollover,yo={};yo.ts=fo,yo.aac=mo;var vo=188,_o=function(t,e,i){for(var r,n,s,a,o=0,u=vo,l=!1;u<t.byteLength;)if(71!==t[o]||71!==t[u])o++,u++;else{switch(r=t.subarray(o,u),yo.ts.parseType(r,e.pid)){case"pes":n=yo.ts.parsePesType(r,e.table),s=yo.ts.parsePayloadUnitStartIndicator(r),"audio"===n&&s&&(a=yo.ts.parsePesTime(r))&&(a.type="audio",i.audio.push(a),l=!0)}if(l)break;o+=vo,u+=vo}for(o=(u=t.byteLength)-vo,l=!1;0<=o;)if(71!==t[o]||71!==t[u])o--,u--;else{switch(r=t.subarray(o,u),yo.ts.parseType(r,e.pid)){case"pes":n=yo.ts.parsePesType(r,e.table),s=yo.ts.parsePayloadUnitStartIndicator(r),"audio"===n&&s&&(a=yo.ts.parsePesTime(r))&&(a.type="audio",i.audio.push(a),l=!0)}if(l)break;o-=vo,u-=vo}},bo=function(t,e,i){for(var r,n,s,a,o,u,l,c=0,h=vo,d=!1,p={data:[],size:0};h<t.byteLength;)if(71!==t[c]||71!==t[h])c++,h++;else{switch(r=t.subarray(c,h),yo.ts.parseType(r,e.pid)){case"pes":if(n=yo.ts.parsePesType(r,e.table),s=yo.ts.parsePayloadUnitStartIndicator(r),"video"===n&&(s&&!d&&(a=yo.ts.parsePesTime(r))&&(a.type="video",i.video.push(a),d=!0),!i.firstKeyFrame)){if(s&&0!==p.size){for(o=new Uint8Array(p.size),u=0;p.data.length;)l=p.data.shift(),o.set(l,u),u+=l.byteLength;yo.ts.videoPacketContainsKeyFrame(o)&&(i.firstKeyFrame=yo.ts.parsePesTime(o),i.firstKeyFrame.type="video"),p.size=0}p.data.push(r),p.size+=r.byteLength}}if(d&&i.firstKeyFrame)break;c+=vo,h+=vo}for(c=(h=t.byteLength)-vo,d=!1;0<=c;)if(71!==t[c]||71!==t[h])c--,h--;else{switch(r=t.subarray(c,h),yo.ts.parseType(r,e.pid)){case"pes":n=yo.ts.parsePesType(r,e.table),s=yo.ts.parsePayloadUnitStartIndicator(r),"video"===n&&s&&(a=yo.ts.parsePesTime(r))&&(a.type="video",i.video.push(a),d=!0)}if(d)break;c-=vo,h-=vo}},To=function(t){var e={pid:null,table:null},i={};for(var r in function(t,e){for(var i,r=0,n=vo;n<t.byteLength;)if(71!==t[r]||71!==t[n])r++,n++;else{switch(i=t.subarray(r,n),yo.ts.parseType(i,e.pid)){case"pat":e.pid||(e.pid=yo.ts.parsePat(i));break;case"pmt":e.table||(e.table=yo.ts.parsePmt(i))}if(e.pid&&e.table)return;r+=vo,n+=vo}}(t,e),e.table){if(e.table.hasOwnProperty(r))switch(e.table[r]){case Wa.H264_STREAM_TYPE:i.video=[],bo(t,e,i),0===i.video.length&&delete i.video;break;case Wa.ADTS_STREAM_TYPE:i.audio=[],_o(t,e,i),0===i.audio.length&&delete i.audio}}return i},So=function(t,e){var i,r;return(r=(i=t)[0]==="I".charCodeAt(0)&&i[1]==="D".charCodeAt(0)&&i[2]==="3".charCodeAt(0)?function(t){for(var e,i=!1,r=0,n=null,s=null,a=0,o=0;3<=t.length-o;){switch(yo.aac.parseType(t,o)){case"timed-metadata":if(t.length-o<10){i=!0;break}if((a=yo.aac.parseId3TagSize(t,o))>t.length){i=!0;break}null===s&&(e=t.subarray(o,o+a),s=yo.aac.parseAacTimestamp(e)),o+=a;break;case"audio":if(t.length-o<7){i=!0;break}if((a=yo.aac.parseAdtsSize(t,o))>t.length){i=!0;break}null===n&&(e=t.subarray(o,o+a),n=yo.aac.parseSampleRate(e)),r++,o+=a;break;default:o++}if(i)return null}if(null===n||null===s)return null;var u=9e4/n;return{audio:[{type:"audio",dts:s,pts:s},{type:"audio",dts:s+1024*r*u,pts:s+1024*r*u}]}}(t):To(t))&&(r.audio||r.video)?(function(t,e){if(t.audio&&t.audio.length){var i=e;"undefined"==typeof i&&(i=t.audio[0].dts),t.audio.forEach(function(t){t.dts=go(t.dts,i),t.pts=go(t.pts,i),t.dtsTime=t.dts/9e4,t.ptsTime=t.pts/9e4})}if(t.video&&t.video.length){var r=e;if("undefined"==typeof r&&(r=t.video[0].dts),t.video.forEach(function(t){t.dts=go(t.dts,r),t.pts=go(t.pts,r),t.dtsTime=t.dts/9e4,t.ptsTime=t.pts/9e4}),t.firstKeyFrame){var n=t.firstKeyFrame;n.dts=go(n.dts,r),n.pts=go(n.pts,r),n.dtsTime=n.dts/9e4,n.ptsTime=n.dts/9e4}}}(r,e),r):null};var ko=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},Co=function(){function r(t,e){for(var i=0;i<e.length;i++){var r=e[i];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}return function(t,e,i){return e&&r(t.prototype,e),i&&r(t,i),t}}(),Eo=function(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!==("undefined"==typeof e?"undefined":v(e))&&"function"!=typeof e?t:e},wo=function(){var t=[[[],[],[],[],[]],[[],[],[],[],[]]],e=t[0],i=t[1],r=e[4],n=i[4],s=void 0,a=void 0,o=void 0,u=[],l=[],c=void 0,h=void 0,d=void 0,p=void 0,f=void 0;for(s=0;s<256;s++)l[(u[s]=s<<1^283*(s>>7))^s]=s;for(a=o=0;!r[a];a^=c||1,o=l[o]||1)for(d=(d=o^o<<1^o<<2^o<<3^o<<4)>>8^255&d^99,f=16843009*u[h=u[c=u[n[r[a]=d]=a]]]^65537*h^257*c^16843008*a,p=257*u[d]^16843008*d,s=0;s<4;s++)e[s][a]=p=p<<24^p>>>8,i[s][d]=f=f<<24^f>>>8;for(s=0;s<5;s++)e[s]=e[s].slice(0),i[s]=i[s].slice(0);return t},Ao=null,Lo=function(){function c(t){ko(this,c),Ao||(Ao=wo()),this._tables=[[Ao[0][0].slice(),Ao[0][1].slice(),Ao[0][2].slice(),Ao[0][3].slice(),Ao[0][4].slice()],[Ao[1][0].slice(),Ao[1][1].slice(),Ao[1][2].slice(),Ao[1][3].slice(),Ao[1][4].slice()]];var e=void 0,i=void 0,r=void 0,n=void 0,s=void 0,a=this._tables[0][4],o=this._tables[1],u=t.length,l=1;if(4!==u&&6!==u&&8!==u)throw new Error("Invalid aes key size");for(n=t.slice(0),s=[],this._key=[n,s],e=u;e<4*u+28;e++)r=n[e-1],(e%u==0||8===u&&e%u==4)&&(r=a[r>>>24]<<24^a[r>>16&255]<<16^a[r>>8&255]<<8^a[255&r],e%u==0&&(r=r<<8^r>>>24^l<<24,l=l<<1^283*(l>>7))),n[e]=n[e-u]^r;for(i=0;e;i++,e--)r=n[3&i?e:e-4],s[i]=e<=4||i<4?r:o[0][a[r>>>24]]^o[1][a[r>>16&255]]^o[2][a[r>>8&255]]^o[3][a[255&r]]}return c.prototype.decrypt=function(t,e,i,r,n,s){var a=this._key[1],o=t^a[0],u=r^a[1],l=i^a[2],c=e^a[3],h=void 0,d=void 0,p=void 0,f=a.length/4-2,m=void 0,g=4,y=this._tables[1],v=y[0],_=y[1],b=y[2],T=y[3],S=y[4];for(m=0;m<f;m++)h=v[o>>>24]^_[u>>16&255]^b[l>>8&255]^T[255&c]^a[g],d=v[u>>>24]^_[l>>16&255]^b[c>>8&255]^T[255&o]^a[g+1],p=v[l>>>24]^_[c>>16&255]^b[o>>8&255]^T[255&u]^a[g+2],c=v[c>>>24]^_[o>>16&255]^b[u>>8&255]^T[255&l]^a[g+3],g+=4,o=h,u=d,l=p;for(m=0;m<4;m++)n[(3&-m)+s]=S[o>>>24]<<24^S[u>>16&255]<<16^S[l>>8&255]<<8^S[255&c]^a[g++],h=o,o=u,u=l,l=c,c=h},c}(),Po=function(){function t(){ko(this,t),this.listeners={}}return t.prototype.on=function(t,e){this.listeners[t]||(this.listeners[t]=[]),this.listeners[t].push(e)},t.prototype.off=function(t,e){if(!this.listeners[t])return!1;var i=this.listeners[t].indexOf(e);return this.listeners[t].splice(i,1),-1<i},t.prototype.trigger=function(t){var e=this.listeners[t];if(e)if(2===arguments.length)for(var i=e.length,r=0;r<i;++r)e[r].call(this,arguments[1]);else for(var n=Array.prototype.slice.call(arguments,1),s=e.length,a=0;a<s;++a)e[a].apply(this,n)},t.prototype.dispose=function(){this.listeners={}},t.prototype.pipe=function(e){this.on("data",function(t){e.push(t)})},t}(),Oo=function(e){function i(){ko(this,i);var t=Eo(this,e.call(this,Po));return t.jobs=[],t.delay=1,t.timeout_=null,t}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+("undefined"==typeof e?"undefined":v(e)));t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(i,e),i.prototype.processJob_=function(){this.jobs.shift()(),this.jobs.length?this.timeout_=setTimeout(this.processJob_.bind(this),this.delay):this.timeout_=null},i.prototype.push=function(t){this.jobs.push(t),this.timeout_||(this.timeout_=setTimeout(this.processJob_.bind(this),this.delay))},i}(Po),xo=function(t){return t<<24|(65280&t)<<8|(16711680&t)>>8|t>>>24},Io=function(t,e,i){var r=new Int32Array(t.buffer,t.byteOffset,t.byteLength>>2),n=new Lo(Array.prototype.slice.call(e)),s=new Uint8Array(t.byteLength),a=new Int32Array(s.buffer),o=void 0,u=void 0,l=void 0,c=void 0,h=void 0,d=void 0,p=void 0,f=void 0,m=void 0;for(o=i[0],u=i[1],l=i[2],c=i[3],m=0;m<r.length;m+=4)h=xo(r[m]),d=xo(r[m+1]),p=xo(r[m+2]),f=xo(r[m+3]),n.decrypt(h,d,p,f,a,m),a[m]=xo(a[m]^o),a[m+1]=xo(a[m+1]^u),a[m+2]=xo(a[m+2]^l),a[m+3]=xo(a[m+3]^c),o=h,u=d,l=p,c=f;return s},Do=function(){function u(t,e,i,r){ko(this,u);var n=u.STEP,s=new Int32Array(t.buffer),a=new Uint8Array(t.byteLength),o=0;for(this.asyncStream_=new Oo,this.asyncStream_.push(this.decryptChunk_(s.subarray(o,o+n),e,i,a)),o=n;o<s.length;o+=n)i=new Uint32Array([xo(s[o-4]),xo(s[o-3]),xo(s[o-2]),xo(s[o-1])]),this.asyncStream_.push(this.decryptChunk_(s.subarray(o,o+n),e,i,a));this.asyncStream_.push(function(){var t;r(null,(t=a).subarray(0,t.byteLength-t[t.byteLength-1]))})}return u.prototype.decryptChunk_=function(e,i,r,n){return function(){var t=Io(e,i,r);n.set(t,e.byteOffset)}},Co(u,null,[{key:"STEP",get:function(){return 32e3}}]),u}(),Ro=function(t,e){return/^[a-z]+:/i.test(e)?e:(/\/\//i.test(t)||(t=Os.buildAbsoluteURL(g.location.href,t)),Os.buildAbsoluteURL(t,e))},Mo=As.mergeOptions,Uo=As.log,No=function(n,s){["AUDIO","SUBTITLES"].forEach(function(t){for(var e in n.mediaGroups[t])for(var i in n.mediaGroups[t][e]){var r=n.mediaGroups[t][e][i];s(r,t,e,i)}})},Bo=function(t,e){var i=Mo(t,{}),r=i.playlists[e.uri];if(!r)return null;if(r.segments&&e.segments&&r.segments.length===e.segments.length&&r.mediaSequence===e.mediaSequence)return null;var n=Mo(r,e);r.segments&&(n.segments=function(t,e,i){var r=e.slice();i=i||0;for(var n=Math.min(t.length,e.length+i),s=i;s<n;s++)r[s-i]=Mo(t[s],r[s-i]);return r}(r.segments,e.segments,e.mediaSequence-r.mediaSequence)),n.segments.forEach(function(t){var e,i;e=t,i=n.resolvedUri,e.resolvedUri||(e.resolvedUri=Ro(i,e.uri)),e.key&&!e.key.resolvedUri&&(e.key.resolvedUri=Ro(i,e.key.uri)),e.map&&!e.map.resolvedUri&&(e.map.resolvedUri=Ro(i,e.map.uri))});for(var s=0;s<i.playlists.length;s++)i.playlists[s].uri===e.uri&&(i.playlists[s]=n);return i.playlists[e.uri]=n,i},jo=function(t){for(var e=t.playlists.length;e--;){var i=t.playlists[e];(t.playlists[i.uri]=i).resolvedUri=Ro(t.uri,i.uri),i.id=e,i.attributes||(i.attributes={},Uo.warn("Invalid playlist STREAM-INF detected. Missing BANDWIDTH attribute."))}},Fo=function(e){No(e,function(t){t.uri&&(t.resolvedUri=Ro(e.uri,t.uri))})},Ho=function(t,e){var i=t.segments[t.segments.length-1];return e&&i&&i.duration?1e3*i.duration:500*(t.targetDuration||10)},Vo=function(n){function s(t,e,i){y(this,s);var r=b(this,n.call(this));if(r.srcUrl=t,r.hls_=e,r.withCredentials=i,!r.srcUrl)throw new Error("A non-empty playlist URL is required");return r.state="HAVE_NOTHING",r.on("mediaupdatetimeout",function(){"HAVE_METADATA"===r.state&&(r.state="HAVE_CURRENT_METADATA",r.request=r.hls_.xhr({uri:Ro(r.master.uri,r.media().uri),withCredentials:r.withCredentials},function(t,e){if(r.request)return t?r.playlistRequestError(r.request,r.media().uri,"HAVE_METADATA"):void r.haveMetadata(r.request,r.media().uri)}))}),r}return _(s,n),s.prototype.playlistRequestError=function(t,e,i){this.request=null,i&&(this.state=i),this.error={playlist:this.master.playlists[e],status:t.status,message:"HLS playlist request error at URL: "+e,responseText:t.responseText,code:500<=t.status?4:2},this.trigger("error")},s.prototype.haveMetadata=function(t,e){var i=this;this.request=null,this.state="HAVE_METADATA";var r=new js;r.push(t.responseText),r.end(),r.manifest.uri=e,r.manifest.attributes=r.manifest.attributes||{};var n=Bo(this.master,r.manifest);this.targetDuration=r.manifest.targetDuration,n?(this.master=n,this.media_=this.master.playlists[r.manifest.uri]):this.trigger("playlistunchanged"),this.media().endList||(g.clearTimeout(this.mediaUpdateTimeout),this.mediaUpdateTimeout=g.setTimeout(function(){i.trigger("mediaupdatetimeout")},Ho(this.media(),!!n))),this.trigger("loadedplaylist")},s.prototype.dispose=function(){this.stopRequest(),g.clearTimeout(this.mediaUpdateTimeout)},s.prototype.stopRequest=function(){if(this.request){var t=this.request;this.request=null,t.onreadystatechange=null,t.abort()}},s.prototype.media=function(i){var r=this;if(!i)return this.media_;if("HAVE_NOTHING"===this.state)throw new Error("Cannot switch media playlist from "+this.state);var n=this.state;if("string"==typeof i){if(!this.master.playlists[i])throw new Error("Unknown playlist URI: "+i);i=this.master.playlists[i]}var t=!this.media_||i.uri!==this.media_.uri;if(this.master.playlists[i.uri].endList)return this.request&&(this.request.onreadystatechange=null,this.request.abort(),this.request=null),this.state="HAVE_METADATA",this.media_=i,void(t&&(this.trigger("mediachanging"),this.trigger("mediachange")));if(t){if(this.state="SWITCHING_MEDIA",this.request){if(Ro(this.master.uri,i.uri)===this.request.url)return;this.request.onreadystatechange=null,this.request.abort(),this.request=null}this.media_&&this.trigger("mediachanging"),this.request=this.hls_.xhr({uri:Ro(this.master.uri,i.uri),withCredentials:this.withCredentials},function(t,e){if(r.request){if(t)return r.playlistRequestError(r.request,i.uri,n);r.haveMetadata(e,i.uri),"HAVE_MASTER"===n?r.trigger("loadedmetadata"):r.trigger("mediachange")}})}},s.prototype.pause=function(){this.stopRequest(),g.clearTimeout(this.mediaUpdateTimeout),"HAVE_NOTHING"===this.state&&(this.started=!1),"SWITCHING_MEDIA"===this.state?this.media_?this.state="HAVE_METADATA":this.state="HAVE_MASTER":"HAVE_CURRENT_METADATA"===this.state&&(this.state="HAVE_METADATA")},s.prototype.load=function(t){var e=this;g.clearTimeout(this.mediaUpdateTimeout);var i=this.media();if(t){var r=i?i.targetDuration/2*1e3:5e3;this.mediaUpdateTimeout=g.setTimeout(function(){return e.load()},r)}else this.started?i&&!i.endList?this.trigger("mediaupdatetimeout"):this.trigger("loadedplaylist"):this.start()},s.prototype.start=function(){var r=this;this.started=!0,this.request=this.hls_.xhr({uri:this.srcUrl,withCredentials:this.withCredentials},function(t,e){if(r.request){if(r.request=null,t)return r.error={status:e.status,message:"HLS playlist request error at URL: "+r.srcUrl,responseText:e.responseText,code:2},"HAVE_NOTHING"===r.state&&(r.started=!1),r.trigger("error");var i=new js;return i.push(e.responseText),i.end(),r.state="HAVE_MASTER",i.manifest.uri=r.srcUrl,i.manifest.playlists?(r.master=i.manifest,jo(r.master),Fo(r.master),r.trigger("loadedplaylist"),void(r.request||r.media(i.manifest.playlists[0]))):(r.master={mediaGroups:{AUDIO:{},VIDEO:{},"CLOSED-CAPTIONS":{},SUBTITLES:{}},uri:g.location.href,playlists:[{uri:r.srcUrl,id:0}]},r.master.playlists[r.srcUrl]=r.master.playlists[0],r.master.playlists[0].resolvedUri=r.srcUrl,r.master.playlists[0].attributes=r.master.playlists[0].attributes||{},r.haveMetadata(e,r.srcUrl),r.trigger("loadedmetadata"))}})},s}(As.EventTarget),qo=As.createTimeRange,Wo=function(t,e,i){var r,n;return"undefined"==typeof e&&(e=t.mediaSequence+t.segments.length),e<t.mediaSequence?0:(r=function(t,e){var i=0,r=e-t.mediaSequence,n=t.segments[r];if(n){if("undefined"!=typeof n.start)return{result:n.start,precise:!0};if("undefined"!=typeof n.end)return{result:n.end-n.duration,precise:!0}}for(;r--;){if("undefined"!=typeof(n=t.segments[r]).end)return{result:i+n.end,precise:!0};if(i+=n.duration,"undefined"!=typeof n.start)return{result:i+n.start,precise:!0}}return{result:i,precise:!1}}(t,e)).precise?r.result:(n=function(t,e){for(var i=0,r=void 0,n=e-t.mediaSequence;n<t.segments.length;n++){if("undefined"!=typeof(r=t.segments[n]).start)return{result:r.start-i,precise:!0};if(i+=r.duration,"undefined"!=typeof r.end)return{result:r.end-i,precise:!0}}return{result:-1,precise:!1}}(t,e)).precise?n.result:r.result+i},zo=function(t,e,i){if(!t)return 0;if("number"!=typeof i&&(i=0),"undefined"==typeof e){if(t.totalDuration)return t.totalDuration;if(!t.endList)return g.Infinity}return Wo(t,e,i)},Go=function(t,e,i){var r=0;if(i<e){var n=[i,e];e=n[0],i=n[1]}if(e<0){for(var s=e;s<Math.min(0,i);s++)r+=t.targetDuration;e=0}for(var a=e;a<i;a++)r+=t.segments[a].duration;return r},Xo=function(t){if(!t.segments.length)return 0;for(var e=t.segments.length-1,i=t.segments[e].duration||t.targetDuration,r=i+2*t.targetDuration;e--&&!(r<=(i+=t.segments[e].duration)););return Math.max(0,e)},Yo=function(t,e,i){if(!t||!t.segments)return null;if(t.endList)return zo(t);if(null===e)return null;e=e||0;var r=i?Xo(t):t.segments.length;return Wo(t,t.mediaSequence+r,e)},$o=function(t){return t-Math.floor(t)==0},Ko=function(t,e){if($o(e))return e+.1*t;for(var i=e.toString().split(".")[1].length,r=1;r<=i;r++){var n=Math.pow(10,r),s=e*n;if($o(s)||r===i)return(s+t)/n}},Qo=Ko.bind(null,1),Jo=Ko.bind(null,-1),Zo=function(t){return t.excludeUntil&&t.excludeUntil>Date.now()},tu=function(t){return t.excludeUntil&&t.excludeUntil===1/0},eu=function(t){var e=Zo(t);return!t.disabled&&!e},iu=function(t,e){return e.attributes&&e.attributes[t]},ru=function(t,e){if(1===t.playlists.length)return!0;var i=e.attributes.BANDWIDTH||Number.MAX_VALUE;return 0===t.playlists.filter(function(t){return!!eu(t)&&(t.attributes.BANDWIDTH||0)<i}).length},nu={duration:zo,seekable:function(t,e){var i=e||0,r=Yo(t,e,!0);return null===r?qo():qo(i,r)},safeLiveIndex:Xo,getMediaInfoForTime:function(t,e,i,r){var n=void 0,s=void 0,a=t.segments.length,o=e-r;if(o<0){if(0<i)for(n=i-1;0<=n;n--)if(s=t.segments[n],0<(o+=Jo(s.duration)))return{mediaIndex:n,startTime:r-Go(t,i,n)};return{mediaIndex:0,startTime:e}}if(i<0){for(n=i;n<0;n++)if((o-=t.targetDuration)<0)return{mediaIndex:0,startTime:e};i=0}for(n=i;n<a;n++)if(s=t.segments[n],(o-=Qo(s.duration))<0)return{mediaIndex:n,startTime:r+Go(t,i,n)};return{mediaIndex:a-1,startTime:e}},isEnabled:eu,isDisabled:function(t){return t.disabled},isBlacklisted:Zo,isIncompatible:tu,playlistEnd:Yo,isAes:function(t){for(var e=0;e<t.segments.length;e++)if(t.segments[e].key)return!0;return!1},isFmp4:function(t){for(var e=0;e<t.segments.length;e++)if(t.segments[e].map)return!0;return!1},hasAttribute:iu,estimateSegmentRequestTime:function(t,e,i){var r=3<arguments.length&&void 0!==arguments[3]?arguments[3]:0;return iu("BANDWIDTH",i)?(t*i.attributes.BANDWIDTH-8*r)/e:NaN},isLowestEnabledRendition:ru},su=As.xhr,au=As.mergeOptions,ou=function(){return function t(e,r){e=au({timeout:45e3},e);var i=t.beforeRequest||As.Hls.xhr.beforeRequest;if(i&&"function"==typeof i){var n=i(e);n&&(e=n)}var s=su(e,function(t,e){var i=s.response;!t&&i&&(s.responseTime=Date.now(),s.roundTripTime=s.responseTime-s.requestTime,s.bytesReceived=i.byteLength||i.length,s.bandwidth||(s.bandwidth=Math.floor(s.bytesReceived/s.roundTripTime*8*1e3))),e.headers&&(s.responseHeaders=e.headers),t&&"ETIMEDOUT"===t.code&&(s.timedout=!0),t||s.aborted||200===e.statusCode||206===e.statusCode||0===e.statusCode||(t=new Error("XHR Failed with a response of: "+(s&&(i||s.responseText)))),r(t,s)}),a=s.abort;return s.abort=function(){return s.aborted=!0,a.apply(s,arguments)},s.uri=e.uri,s.requestTime=Date.now(),s}},uu=function(t,e){var i=t.toString(16);return"00".substring(0,2-i.length)+i+(e%2?" ":"")},lu=function(t){return 32<=t&&t<126?String.fromCharCode(t):"."},cu=function(i){var r={};return Object.keys(i).forEach(function(t){var e=i[t];ArrayBuffer.isView(e)?r[t]={bytes:e.buffer,byteOffset:e.byteOffset,byteLength:e.byteLength}:r[t]=e}),r},hu=function(t){var e=t.byterange||{length:1/0,offset:0};return[e.length,e.offset,t.resolvedUri].join(",")},du=function(t){for(var e=Array.prototype.slice.call(t),i="",r=0;r<e.length/16;r++)i+=e.slice(16*r,16*r+16).map(uu).join("")+" "+e.slice(16*r,16*r+16).map(lu).join("")+"\n";return i},pu=Object.freeze({createTransferableMessage:cu,initSegmentId:hu,hexDump:du,tagDump:function(t){var e=t.bytes;return du(e)},textRanges:function(t){var e,i,r="",n=void 0;for(n=0;n<t.length;n++)r+=(i=n,(e=t).start(i)+"-"+e.end(i)+" ");return r}}),fu=1/30,mu=function(t,e){var i=[],r=void 0;if(t&&t.length)for(r=0;r<t.length;r++)e(t.start(r),t.end(r))&&i.push([t.start(r),t.end(r)]);return As.createTimeRanges(i)},gu=function(t,i){return mu(t,function(t,e){return t-fu<=i&&i<=e+fu})},yu=function(t,e){return mu(t,function(t){return e<=t-fu})},vu=function(t){var e=[];if(!t||!t.length)return"";for(var i=0;i<t.length;i++)e.push(t.start(i)+" => "+t.end(i));return e.join(", ")},_u=function(t){for(var e=[],i=0;i<t.length;i++)e.push({start:t.start(i),end:t.end(i)});return e},bu=function(t,e,i){var r=void 0,n=void 0;if(i&&i.cues)for(r=i.cues.length;r--;)(n=i.cues[r]).startTime<=e&&n.endTime>=t&&i.removeCue(n)},Tu=function(t){return isNaN(t)||Math.abs(t)===1/0?Number.MAX_VALUE:t},Su=function(t,e,i){var n=g.WebKitDataCue||g.VTTCue;if(e&&e.forEach(function(t){var e=t.stream;this.inbandTextTracks_[e].addCue(new n(t.startTime+this.timestampOffset,t.endTime+this.timestampOffset,t.text))},t),i){var s=Tu(t.mediaSource_.duration);if(i.forEach(function(t){var r=t.cueTime+this.timestampOffset;t.frames.forEach(function(t){var e,i=new n(r,r,t.value||t.url||t.data||"");i.frame=t,i.value=t,e=i,Object.defineProperties(e.frame,{id:{get:function(){return As.log.warn("cue.frame.id is deprecated. Use cue.value.key instead."),e.value.key}},value:{get:function(){return As.log.warn("cue.frame.value is deprecated. Use cue.value.data instead."),e.value.data}},privateData:{get:function(){return As.log.warn("cue.frame.privateData is deprecated. Use cue.value.data instead."),e.value.data}}}),this.metadataTrack_.addCue(i)},this)},t),t.metadataTrack_&&t.metadataTrack_.cues&&t.metadataTrack_.cues.length){for(var r=t.metadataTrack_.cues,a=[],o=0;o<r.length;o++)r[o]&&a.push(r[o]);var u=a.reduce(function(t,e){var i=t[e.startTime]||[];return i.push(e),t[e.startTime]=i,t},{}),l=Object.keys(u).sort(function(t,e){return Number(t)-Number(e)});l.forEach(function(t,e){var i=u[t],r=Number(l[e+1])||s;i.forEach(function(t){t.endTime=r})})}}},ku="undefined"!=typeof window?window:{},Cu="undefined"==typeof Symbol?"__target":Symbol(),Eu="application/javascript",wu=ku.BlobBuilder||ku.WebKitBlobBuilder||ku.MozBlobBuilder||ku.MSBlobBuilder,Au=ku.URL||ku.webkitURL||Au&&Au.msURL,Lu=ku.Worker;function Pu(n,s){return function(t){var e=this;if(!s)return new Lu(n);if(Lu&&!t){var i=Du(s.toString().replace(/^function.+?{/,"").slice(0,-1));return this[Cu]=new Lu(i),function(t,e){if(!t||!e)return;var i=t.terminate;t.objURL=e,t.terminate=function(){t.objURL&&Au.revokeObjectURL(t.objURL),i.call(t)}}(this[Cu],i),this[Cu]}var r={postMessage:function(t){e.onmessage&&setTimeout(function(){e.onmessage({data:t,target:r})})}};s.call(r),this.postMessage=function(t){setTimeout(function(){r.onmessage({data:t,target:e})})},this.isThisThread=!0}}if(Lu){var Ou,xu=Du("self.onmessage = function () {}"),Iu=new Uint8Array(1);try{(Ou=new Lu(xu)).postMessage(Iu,[Iu.buffer])}catch(t){Lu=null}finally{Au.revokeObjectURL(xu),Ou&&Ou.terminate()}}function Du(e){try{return Au.createObjectURL(new Blob([e],{type:Eu}))}catch(t){var i=new wu;return i.append(e),Au.createObjectURL(i.getBlob(type))}}var Ru=new Pu("./transmuxer-worker.worker.js",function(ie,t){var re=this;!function(){var o,e,i,n,s,r,t,a,u,l,c,h,d,p,f,m,g,y,v,_,b,T,S,k,C,E,w,A,L,P,O,x,I,D,R,M,U,N,B,j,F="undefined"!=typeof ie?ie:"undefined"!=typeof global?global:"undefined"!=typeof re?re:{},H="undefined"!=typeof ie?ie:"undefined"!=typeof F?F:"undefined"!=typeof re?re:{},V=Math.pow(2,32)-1;!function(){var t;if(T={avc1:[],avcC:[],btrt:[],dinf:[],dref:[],esds:[],ftyp:[],hdlr:[],mdat:[],mdhd:[],mdia:[],mfhd:[],minf:[],moof:[],moov:[],mp4a:[],mvex:[],mvhd:[],sdtp:[],smhd:[],stbl:[],stco:[],stsc:[],stsd:[],stsz:[],stts:[],styp:[],tfdt:[],tfhd:[],traf:[],trak:[],trun:[],trex:[],tkhd:[],vmhd:[]},"undefined"!=typeof Uint8Array){for(t in T)T.hasOwnProperty(t)&&(T[t]=[t.charCodeAt(0),t.charCodeAt(1),t.charCodeAt(2),t.charCodeAt(3)]);S=new Uint8Array(["i".charCodeAt(0),"s".charCodeAt(0),"o".charCodeAt(0),"m".charCodeAt(0)]),C=new Uint8Array(["a".charCodeAt(0),"v".charCodeAt(0),"c".charCodeAt(0),"1".charCodeAt(0)]),k=new Uint8Array([0,0,0,1]),E=new Uint8Array([0,0,0,0,0,0,0,0,118,105,100,101,0,0,0,0,0,0,0,0,0,0,0,0,86,105,100,101,111,72,97,110,100,108,101,114,0]),w=new Uint8Array([0,0,0,0,0,0,0,0,115,111,117,110,0,0,0,0,0,0,0,0,0,0,0,0,83,111,117,110,100,72,97,110,100,108,101,114,0]),A={video:E,audio:w},O=new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,12,117,114,108,32,0,0,0,1]),P=new Uint8Array([0,0,0,0,0,0,0,0]),x=new Uint8Array([0,0,0,0,0,0,0,0]),I=x,D=new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0]),R=x,L=new Uint8Array([0,0,0,1,0,0,0,0,0,0,0,0])}}(),o=function(t){var e,i,r=[],n=0;for(e=1;e<arguments.length;e++)r.push(arguments[e]);for(e=r.length;e--;)n+=r[e].byteLength;for(i=new Uint8Array(n+8),new DataView(i.buffer,i.byteOffset,i.byteLength).setUint32(0,i.byteLength),i.set(t,4),e=0,n=8;e<r.length;e++)i.set(r[e],n),n+=r[e].byteLength;return i},e=function(){return o(T.dinf,o(T.dref,O))},i=function(t){return o(T.esds,new Uint8Array([0,0,0,0,3,25,0,0,0,4,17,64,21,0,6,0,0,0,218,192,0,0,218,192,5,2,t.audioobjecttype<<3|t.samplingfrequencyindex>>>1,t.samplingfrequencyindex<<7|t.channelcount<<3,6,1,2]))},f=function(t){return o(T.hdlr,A[t])},p=function(t){var e=new Uint8Array([0,0,0,0,0,0,0,2,0,0,0,3,0,1,95,144,t.duration>>>24&255,t.duration>>>16&255,t.duration>>>8&255,255&t.duration,85,196,0,0]);return t.samplerate&&(e[12]=t.samplerate>>>24&255,e[13]=t.samplerate>>>16&255,e[14]=t.samplerate>>>8&255,e[15]=255&t.samplerate),o(T.mdhd,e)},d=function(t){return o(T.mdia,p(t),f(t.type),r(t))},s=function(t){return o(T.mfhd,new Uint8Array([0,0,0,0,(4278190080&t)>>24,(16711680&t)>>16,(65280&t)>>8,255&t]))},r=function(t){return o(T.minf,"video"===t.type?o(T.vmhd,L):o(T.smhd,P),e(),g(t))},t=function(t,e){for(var i=[],r=e.length;r--;)i[r]=v(e[r]);return o.apply(null,[T.moof,s(t)].concat(i))},a=function(t){for(var e=t.length,i=[];e--;)i[e]=c(t[e]);return o.apply(null,[T.moov,l(4294967295)].concat(i).concat(u(t)))},u=function(t){for(var e=t.length,i=[];e--;)i[e]=_(t[e]);return o.apply(null,[T.mvex].concat(i))},l=function(t){var e=new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,2,0,1,95,144,(4278190080&t)>>24,(16711680&t)>>16,(65280&t)>>8,255&t,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255]);return o(T.mvhd,e)},m=function(t){var e,i,r=t.samples||[],n=new Uint8Array(4+r.length);for(i=0;i<r.length;i++)e=r[i].flags,n[i+4]=e.dependsOn<<4|e.isDependedOn<<2|e.hasRedundancy;return o(T.sdtp,n)},g=function(t){return o(T.stbl,y(t),o(T.stts,R),o(T.stsc,I),o(T.stsz,D),o(T.stco,x))},y=function(t){return o(T.stsd,new Uint8Array([0,0,0,0,0,0,0,1]),"video"===t.type?M(t):U(t))},M=function(t){var e,i=t.sps||[],r=t.pps||[],n=[],s=[];for(e=0;e<i.length;e++)n.push((65280&i[e].byteLength)>>>8),n.push(255&i[e].byteLength),n=n.concat(Array.prototype.slice.call(i[e]));for(e=0;e<r.length;e++)s.push((65280&r[e].byteLength)>>>8),s.push(255&r[e].byteLength),s=s.concat(Array.prototype.slice.call(r[e]));return o(T.avc1,new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,(65280&t.width)>>8,255&t.width,(65280&t.height)>>8,255&t.height,0,72,0,0,0,72,0,0,0,0,0,0,0,1,19,118,105,100,101,111,106,115,45,99,111,110,116,114,105,98,45,104,108,115,0,0,0,0,0,0,0,0,0,0,0,0,0,24,17,17]),o(T.avcC,new Uint8Array([1,t.profileIdc,t.profileCompatibility,t.levelIdc,255].concat([i.length]).concat(n).concat([r.length]).concat(s))),o(T.btrt,new Uint8Array([0,28,156,128,0,45,198,192,0,45,198,192])))},U=function(t){return o(T.mp4a,new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,(65280&t.channelcount)>>8,255&t.channelcount,(65280&t.samplesize)>>8,255&t.samplesize,0,0,0,0,(65280&t.samplerate)>>8,255&t.samplerate,0,0]),i(t))},h=function(t){var e=new Uint8Array([0,0,0,7,0,0,0,0,0,0,0,0,(4278190080&t.id)>>24,(16711680&t.id)>>16,(65280&t.id)>>8,255&t.id,0,0,0,0,(4278190080&t.duration)>>24,(16711680&t.duration)>>16,(65280&t.duration)>>8,255&t.duration,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,(65280&t.width)>>8,255&t.width,0,0,(65280&t.height)>>8,255&t.height,0,0]);return o(T.tkhd,e)},v=function(t){var e,i,r,n,s,a;return e=o(T.tfhd,new Uint8Array([0,0,0,58,(4278190080&t.id)>>24,(16711680&t.id)>>16,(65280&t.id)>>8,255&t.id,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0])),s=Math.floor(t.baseMediaDecodeTime/(V+1)),a=Math.floor(t.baseMediaDecodeTime%(V+1)),i=o(T.tfdt,new Uint8Array([1,0,0,0,s>>>24&255,s>>>16&255,s>>>8&255,255&s,a>>>24&255,a>>>16&255,a>>>8&255,255&a])),92,"audio"===t.type?(r=b(t,92),o(T.traf,e,i,r)):(n=m(t),r=b(t,n.length+92),o(T.traf,e,i,r,n))},c=function(t){return t.duration=t.duration||4294967295,o(T.trak,h(t),d(t))},_=function(t){var e=new Uint8Array([0,0,0,0,(4278190080&t.id)>>24,(16711680&t.id)>>16,(65280&t.id)>>8,255&t.id,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1]);return"video"!==t.type&&(e[e.length-1]=0),o(T.trex,e)},j=function(t,e){var i=0,r=0,n=0,s=0;return t.length&&(void 0!==t[0].duration&&(i=1),void 0!==t[0].size&&(r=2),void 0!==t[0].flags&&(n=4),void 0!==t[0].compositionTimeOffset&&(s=8)),[0,0,i|r|n|s,1,(4278190080&t.length)>>>24,(16711680&t.length)>>>16,(65280&t.length)>>>8,255&t.length,(4278190080&e)>>>24,(16711680&e)>>>16,(65280&e)>>>8,255&e]},B=function(t,e){var i,r,n,s;for(e+=20+16*(r=t.samples||[]).length,i=j(r,e),s=0;s<r.length;s++)n=r[s],i=i.concat([(4278190080&n.duration)>>>24,(16711680&n.duration)>>>16,(65280&n.duration)>>>8,255&n.duration,(4278190080&n.size)>>>24,(16711680&n.size)>>>16,(65280&n.size)>>>8,255&n.size,n.flags.isLeading<<2|n.flags.dependsOn,n.flags.isDependedOn<<6|n.flags.hasRedundancy<<4|n.flags.paddingValue<<1|n.flags.isNonSyncSample,61440&n.flags.degradationPriority,15&n.flags.degradationPriority,(4278190080&n.compositionTimeOffset)>>>24,(16711680&n.compositionTimeOffset)>>>16,(65280&n.compositionTimeOffset)>>>8,255&n.compositionTimeOffset]);return o(T.trun,new Uint8Array(i))},N=function(t,e){var i,r,n,s;for(e+=20+8*(r=t.samples||[]).length,i=j(r,e),s=0;s<r.length;s++)n=r[s],i=i.concat([(4278190080&n.duration)>>>24,(16711680&n.duration)>>>16,(65280&n.duration)>>>8,255&n.duration,(4278190080&n.size)>>>24,(16711680&n.size)>>>16,(65280&n.size)>>>8,255&n.size]);return o(T.trun,new Uint8Array(i))},b=function(t,e){return"audio"===t.type?N(t,e):B(t,e)};var q={ftyp:n=function(){return o(T.ftyp,S,k,S,C)},mdat:function(t){return o(T.mdat,t)},moof:t,moov:a,initSegment:function(t){var e,i=n(),r=a(t);return(e=new Uint8Array(i.byteLength+r.byteLength)).set(i),e.set(r,i.byteLength),e}},W=function(){this.init=function(){var s={};this.on=function(t,e){s[t]||(s[t]=[]),s[t]=s[t].concat(e)},this.off=function(t,e){var i;return!!s[t]&&(i=s[t].indexOf(e),s[t]=s[t].slice(),s[t].splice(i,1),-1<i)},this.trigger=function(t){var e,i,r,n;if(e=s[t])if(2===arguments.length)for(r=e.length,i=0;i<r;++i)e[i].call(this,arguments[1]);else{for(n=[],i=arguments.length,i=1;i<arguments.length;++i)n.push(arguments[i]);for(r=e.length,i=0;i<r;++i)e[i].apply(this,n)}},this.dispose=function(){s={}}}};W.prototype.pipe=function(e){return this.on("data",function(t){e.push(t)}),this.on("done",function(t){e.flush(t)}),e},W.prototype.push=function(t){this.trigger("data",t)},W.prototype.flush=function(t){this.trigger("done",t)};var z=W,G=function t(){t.prototype.init.call(this),this.captionPackets_=[],this.ccStreams_=[new Q(0,0),new Q(0,1),new Q(1,0),new Q(1,1)],this.reset(),this.ccStreams_.forEach(function(t){t.on("data",this.trigger.bind(this,"data")),t.on("done",this.trigger.bind(this,"done"))},this)};(G.prototype=new z).push=function(t){var e,i,r;if("sei_rbsp"===t.nalUnitType&&(4===(e=function(t){for(var e=0,i={payloadType:-1,payloadSize:0},r=0,n=0;e<t.byteLength&&128!==t[e];){for(;255===t[e];)r+=255,e++;for(r+=t[e++];255===t[e];)n+=255,e++;if(n+=t[e++],!i.payload&&4===r){i.payloadType=r,i.payloadSize=n,i.payload=t.subarray(e,e+n);break}e+=n,n=r=0}return i}(t.escapedRBSP)).payloadType&&(i=181!==(r=e).payload[0]?null:49!=(r.payload[1]<<8|r.payload[2])?null:"GA94"!==String.fromCharCode(r.payload[3],r.payload[4],r.payload[5],r.payload[6])?null:3!==r.payload[7]?null:r.payload.subarray(8,r.payload.length-1))))if(t.dts<this.latestDts_)this.ignoreNextEqualDts_=!0;else{if(t.dts===this.latestDts_&&this.ignoreNextEqualDts_)return this.numSameDts_--,void(this.numSameDts_||(this.ignoreNextEqualDts_=!1));this.captionPackets_=this.captionPackets_.concat(function(t,e){var i,r,n,s,a=[];if(!(64&e[0]))return a;for(r=31&e[0],i=0;i<r;i++)s={type:3&e[2+(n=3*i)],pts:t},4&e[n+2]&&(s.ccData=e[n+3]<<8|e[n+4],a.push(s));return a}(t.pts,i)),this.latestDts_!==t.dts&&(this.numSameDts_=0),this.numSameDts_++,this.latestDts_=t.dts}},G.prototype.flush=function(){this.captionPackets_.length?(this.captionPackets_.forEach(function(t,e){t.presortIndex=e}),this.captionPackets_.sort(function(t,e){return t.pts===e.pts?t.presortIndex-e.presortIndex:t.pts-e.pts}),this.captionPackets_.forEach(function(t){t.type<2&&this.dispatchCea608Packet(t)},this),this.captionPackets_.length=0,this.ccStreams_.forEach(function(t){t.flush()},this)):this.ccStreams_.forEach(function(t){t.flush()},this)},G.prototype.reset=function(){this.latestDts_=null,this.ignoreNextEqualDts_=!1,this.numSameDts_=0,this.activeCea608Channel_=[null,null],this.ccStreams_.forEach(function(t){t.reset()})},G.prototype.dispatchCea608Packet=function(t){this.setsChannel1Active(t)?this.activeCea608Channel_[t.type]=0:this.setsChannel2Active(t)&&(this.activeCea608Channel_[t.type]=1),null!==this.activeCea608Channel_[t.type]&&this.ccStreams_[(t.type<<1)+this.activeCea608Channel_[t.type]].push(t)},G.prototype.setsChannel1Active=function(t){return 4096==(30720&t.ccData)},G.prototype.setsChannel2Active=function(t){return 6144==(30720&t.ccData)};var X={42:225,92:233,94:237,95:243,96:250,123:231,124:247,125:209,126:241,127:9608,304:174,305:176,306:189,307:191,308:8482,309:162,310:163,311:9834,312:224,313:160,314:232,315:226,316:234,317:238,318:244,319:251,544:193,545:201,546:211,547:218,548:220,549:252,550:8216,551:161,552:42,553:39,554:8212,555:169,556:8480,557:8226,558:8220,559:8221,560:192,561:194,562:199,563:200,564:202,565:203,566:235,567:206,568:207,569:239,570:212,571:217,572:249,573:219,574:171,575:187,800:195,801:227,802:205,803:204,804:236,805:210,806:242,807:213,808:245,809:123,810:125,811:92,812:94,813:95,814:124,815:126,816:196,817:228,818:214,819:246,820:223,821:165,822:164,823:9474,824:197,825:229,826:216,827:248,828:9484,829:9488,830:9492,831:9496},Y=function(t){return null===t?"":(t=X[t]||t,String.fromCharCode(t))},$=[4352,4384,4608,4640,5376,5408,5632,5664,5888,5920,4096,4864,4896,5120,5152],K=function(){for(var t=[],e=15;e--;)t.push("");return t},Q=function t(e,i){t.prototype.init.call(this),this.field_=e||0,this.dataChannel_=i||0,this.name_="CC"+(1+(this.field_<<1|this.dataChannel_)),this.setConstants(),this.reset(),this.push=function(t){var e,i,r,n,s;if((e=32639&t.ccData)!==this.lastControlCode_){if(4096==(61440&e)?this.lastControlCode_=e:e!==this.PADDING_&&(this.lastControlCode_=null),r=e>>>8,n=255&e,e!==this.PADDING_)if(e===this.RESUME_CAPTION_LOADING_)this.mode_="popOn";else if(e===this.END_OF_CAPTION_)this.clearFormatting(t.pts),this.flushDisplayed(t.pts),i=this.displayed_,this.displayed_=this.nonDisplayed_,this.nonDisplayed_=i,this.startPts_=t.pts;else if(e===this.ROLL_UP_2_ROWS_)this.topRow_=13,this.mode_="rollUp";else if(e===this.ROLL_UP_3_ROWS_)this.topRow_=12,this.mode_="rollUp";else if(e===this.ROLL_UP_4_ROWS_)this.topRow_=11,this.mode_="rollUp";else if(e===this.CARRIAGE_RETURN_)this.clearFormatting(t.pts),this.flushDisplayed(t.pts),this.shiftRowsUp_(),this.startPts_=t.pts;else if(e===this.BACKSPACE_)"popOn"===this.mode_?this.nonDisplayed_[14]=this.nonDisplayed_[14].slice(0,-1):this.displayed_[14]=this.displayed_[14].slice(0,-1);else if(e===this.ERASE_DISPLAYED_MEMORY_)this.flushDisplayed(t.pts),this.displayed_=K();else if(e===this.ERASE_NON_DISPLAYED_MEMORY_)this.nonDisplayed_=K();else if(e===this.RESUME_DIRECT_CAPTIONING_)this.mode_="paintOn";else if(this.isSpecialCharacter(r,n))s=Y((r=(3&r)<<8)|n),this[this.mode_](t.pts,s),this.column_++;else if(this.isExtCharacter(r,n))"popOn"===this.mode_?this.nonDisplayed_[this.row_]=this.nonDisplayed_[this.row_].slice(0,-1):this.displayed_[14]=this.displayed_[14].slice(0,-1),s=Y((r=(3&r)<<8)|n),this[this.mode_](t.pts,s),this.column_++;else if(this.isMidRowCode(r,n))this.clearFormatting(t.pts),this[this.mode_](t.pts," "),this.column_++,14==(14&n)&&this.addFormatting(t.pts,["i"]),1==(1&n)&&this.addFormatting(t.pts,["u"]);else if(this.isOffsetControlCode(r,n))this.column_+=3&n;else if(this.isPAC(r,n)){var a=$.indexOf(7968&e);a!==this.row_&&(this.clearFormatting(t.pts),this.row_=a),1&n&&-1===this.formatting_.indexOf("u")&&this.addFormatting(t.pts,["u"]),16==(16&e)&&(this.column_=4*((14&e)>>1)),this.isColorPAC(n)&&14==(14&n)&&this.addFormatting(t.pts,["i"])}else this.isNormalChar(r)&&(0===n&&(n=null),s=Y(r),s+=Y(n),this[this.mode_](t.pts,s),this.column_+=s.length)}else this.lastControlCode_=null}};Q.prototype=new z,Q.prototype.flushDisplayed=function(t){var e=this.displayed_.map(function(t){return t.trim()}).join("\n").replace(/^\n+|\n+$/g,"");e.length&&this.trigger("data",{startPts:this.startPts_,endPts:t,text:e,stream:this.name_})},Q.prototype.reset=function(){this.mode_="popOn",this.topRow_=0,this.startPts_=0,this.displayed_=K(),this.nonDisplayed_=K(),this.lastControlCode_=null,this.column_=0,this.row_=14,this.formatting_=[]},Q.prototype.setConstants=function(){0===this.dataChannel_?(this.BASE_=16,this.EXT_=17,this.CONTROL_=(20|this.field_)<<8,this.OFFSET_=23):1===this.dataChannel_&&(this.BASE_=24,this.EXT_=25,this.CONTROL_=(28|this.field_)<<8,this.OFFSET_=31),this.PADDING_=0,this.RESUME_CAPTION_LOADING_=32|this.CONTROL_,this.END_OF_CAPTION_=47|this.CONTROL_,this.ROLL_UP_2_ROWS_=37|this.CONTROL_,this.ROLL_UP_3_ROWS_=38|this.CONTROL_,this.ROLL_UP_4_ROWS_=39|this.CONTROL_,this.CARRIAGE_RETURN_=45|this.CONTROL_,this.RESUME_DIRECT_CAPTIONING_=41|this.CONTROL_,this.BACKSPACE_=33|this.CONTROL_,this.ERASE_DISPLAYED_MEMORY_=44|this.CONTROL_,this.ERASE_NON_DISPLAYED_MEMORY_=46|this.CONTROL_},Q.prototype.isSpecialCharacter=function(t,e){return t===this.EXT_&&48<=e&&e<=63},Q.prototype.isExtCharacter=function(t,e){return(t===this.EXT_+1||t===this.EXT_+2)&&32<=e&&e<=63},Q.prototype.isMidRowCode=function(t,e){return t===this.EXT_&&32<=e&&e<=47},Q.prototype.isOffsetControlCode=function(t,e){return t===this.OFFSET_&&33<=e&&e<=35},Q.prototype.isPAC=function(t,e){return t>=this.BASE_&&t<this.BASE_+8&&64<=e&&e<=127},Q.prototype.isColorPAC=function(t){return 64<=t&&t<=79||96<=t&&t<=127},Q.prototype.isNormalChar=function(t){return 32<=t&&t<=127},Q.prototype.addFormatting=function(t,e){this.formatting_=this.formatting_.concat(e);var i=e.reduce(function(t,e){return t+"<"+e+">"},"");this[this.mode_](t,i)},Q.prototype.clearFormatting=function(t){if(this.formatting_.length){var e=this.formatting_.reverse().reduce(function(t,e){return t+"</"+e+">"},"");this.formatting_=[],this[this.mode_](t,e)}},Q.prototype.popOn=function(t,e){var i=this.nonDisplayed_[this.row_];i+=e,this.nonDisplayed_[this.row_]=i},Q.prototype.rollUp=function(t,e){var i=this.displayed_[14];i+=e,this.displayed_[14]=i},Q.prototype.shiftRowsUp_=function(){var t;for(t=0;t<this.topRow_;t++)this.displayed_[t]="";for(t=this.topRow_;t<14;t++)this.displayed_[t]=this.displayed_[t+1];this.displayed_[14]=""},Q.prototype.paintOn=function(){};var J={CaptionStream:G,Cea608Stream:Q},Z={H264_STREAM_TYPE:27,ADTS_STREAM_TYPE:15,METADATA_STREAM_TYPE:21},tt=function(t,e){var i=1;for(e<t&&(i=-1);4294967296<Math.abs(e-t);)t+=8589934592*i;return t},et=function t(e){var i,r;t.prototype.init.call(this),this.type_=e,this.push=function(t){t.type===this.type_&&(void 0===r&&(r=t.dts),t.dts=tt(t.dts,r),t.pts=tt(t.pts,r),i=t.dts,this.trigger("data",t))},this.flush=function(){r=i,this.trigger("done")},this.discontinuity=function(){i=r=void 0}};et.prototype=new z;var it,rt=et,nt=function(t,e,i){var r,n="";for(r=e;r<i;r++)n+="%"+("00"+t[r].toString(16)).slice(-2);return n},st=function(t,e,i){return decodeURIComponent(nt(t,e,i))},at=function(t){return t[0]<<21|t[1]<<14|t[2]<<7|t[3]},ot={TXXX:function(t){var e;if(3===t.data[0]){for(e=1;e<t.data.length;e++)if(0===t.data[e]){t.description=st(t.data,1,e),t.value=st(t.data,e+1,t.data.length).replace(/\0*$/,"");break}t.data=t.value}},WXXX:function(t){var e;if(3===t.data[0])for(e=1;e<t.data.length;e++)if(0===t.data[e]){t.description=st(t.data,1,e),t.url=st(t.data,e+1,t.data.length);break}},PRIV:function(t){var e,i;for(e=0;e<t.data.length;e++)if(0===t.data[e]){t.owner=(i=t.data,unescape(nt(i,0,e)));break}t.privateData=t.data.subarray(e+1),t.data=t.privateData}};(it=function(t){var e,u={debug:!(!t||!t.debug),descriptor:t&&t.descriptor},l=0,c=[],h=0;if(it.prototype.init.call(this),this.dispatchType=Z.METADATA_STREAM_TYPE.toString(16),u.descriptor)for(e=0;e<u.descriptor.length;e++)this.dispatchType+=("00"+u.descriptor[e].toString(16)).slice(-2);this.push=function(t){var e,i,r,n,s;if("timed-metadata"===t.type)if(t.dataAlignmentIndicator&&(h=0,c.length=0),0===c.length&&(t.data.length<10||t.data[0]!=="I".charCodeAt(0)||t.data[1]!=="D".charCodeAt(0)||t.data[2]!=="3".charCodeAt(0)))u.debug;else if(c.push(t),h+=t.data.byteLength,1===c.length&&(l=at(t.data.subarray(6,10)),l+=10),!(h<l)){for(e={data:new Uint8Array(l),frames:[],pts:c[0].pts,dts:c[0].dts},s=0;s<l;)e.data.set(c[0].data.subarray(0,l-s),s),s+=c[0].data.byteLength,h-=c[0].data.byteLength,c.shift();i=10,64&e.data[5]&&(i+=4,i+=at(e.data.subarray(10,14)),l-=at(e.data.subarray(16,20)));do{if((r=at(e.data.subarray(i+4,i+8)))<1)return;if((n={id:String.fromCharCode(e.data[i],e.data[i+1],e.data[i+2],e.data[i+3]),data:e.data.subarray(i+10,i+r+10)}).key=n.id,ot[n.id]&&(ot[n.id](n),"com.apple.streaming.transportStreamTimestamp"===n.owner)){var a=n.data,o=(1&a[3])<<30|a[4]<<22|a[5]<<14|a[6]<<6|a[7]>>>2;o*=4,o+=3&a[7],n.timeStamp=o,void 0===e.pts&&void 0===e.dts&&(e.pts=n.timeStamp,e.dts=n.timeStamp),this.trigger("timestamp",n)}e.frames.push(n),i+=10,i+=r}while(i<l);this.trigger("data",e)}}}).prototype=new z;var ut,lt,ct,ht=it,dt=rt;(ut=function(){var n=new Uint8Array(188),s=0;ut.prototype.init.call(this),this.push=function(t){var e,i=0,r=188;for(s?((e=new Uint8Array(t.byteLength+s)).set(n.subarray(0,s)),e.set(t,s),s=0):e=t;r<e.byteLength;)71!==e[i]||71!==e[r]?(i++,r++):(this.trigger("data",e.subarray(i,r)),i+=188,r+=188);i<e.byteLength&&(n.set(e.subarray(i),0),s=e.byteLength-i)},this.flush=function(){188===s&&71===n[0]&&(this.trigger("data",n),s=0),this.trigger("done")}}).prototype=new z,(lt=function(){var r,n,s,a;lt.prototype.init.call(this),(a=this).packetsWaitingForPmt=[],this.programMapTable=void 0,r=function(t,e){var i=0;e.payloadUnitStartIndicator&&(i+=t[i]+1),"pat"===e.type?n(t.subarray(i),e):s(t.subarray(i),e)},n=function(t,e){e.section_number=t[7],e.last_section_number=t[8],a.pmtPid=(31&t[10])<<8|t[11],e.pmtPid=a.pmtPid},s=function(t,e){var i,r;if(1&t[5]){for(a.programMapTable={video:null,audio:null,"timed-metadata":{}},i=3+((15&t[1])<<8|t[2])-4,r=12+((15&t[10])<<8|t[11]);r<i;){var n=t[r],s=(31&t[r+1])<<8|t[r+2];n===Z.H264_STREAM_TYPE&&null===a.programMapTable.video?a.programMapTable.video=s:n===Z.ADTS_STREAM_TYPE&&null===a.programMapTable.audio?a.programMapTable.audio=s:n===Z.METADATA_STREAM_TYPE&&(a.programMapTable["timed-metadata"][s]=n),r+=5+((15&t[r+3])<<8|t[r+4])}e.programMapTable=a.programMapTable}},this.push=function(t){var e={},i=4;if(e.payloadUnitStartIndicator=!!(64&t[1]),e.pid=31&t[1],e.pid<<=8,e.pid|=t[2],1<(48&t[3])>>>4&&(i+=t[i]+1),0===e.pid)e.type="pat",r(t.subarray(i),e),this.trigger("data",e);else if(e.pid===this.pmtPid)for(e.type="pmt",r(t.subarray(i),e),this.trigger("data",e);this.packetsWaitingForPmt.length;)this.processPes_.apply(this,this.packetsWaitingForPmt.shift());else void 0===this.programMapTable?this.packetsWaitingForPmt.push([t,i,e]):this.processPes_(t,i,e)},this.processPes_=function(t,e,i){i.pid===this.programMapTable.video?i.streamType=Z.H264_STREAM_TYPE:i.pid===this.programMapTable.audio?i.streamType=Z.ADTS_STREAM_TYPE:i.streamType=this.programMapTable["timed-metadata"][i.pid],i.type="pes",i.data=t.subarray(e),this.trigger("data",i)}}).prototype=new z,lt.STREAM_TYPES={h264:27,adts:15},(ct=function(){var d=this,r={data:[],size:0},n={data:[],size:0},s={data:[],size:0},a=function(t,e,i){var r,n,s=new Uint8Array(t.size),a={type:e},o=0,u=0;if(t.data.length&&!(t.size<9)){for(a.trackId=t.data[0].pid,o=0;o<t.data.length;o++)n=t.data[o],s.set(n.data,u),u+=n.data.byteLength;var l,c,h;l=s,(c=a).packetLength=6+(l[4]<<8|l[5]),c.dataAlignmentIndicator=0!=(4&l[6]),192&(h=l[7])&&(c.pts=(14&l[9])<<27|(255&l[10])<<20|(254&l[11])<<12|(255&l[12])<<5|(254&l[13])>>>3,c.pts*=4,c.pts+=(6&l[13])>>>1,c.dts=c.pts,64&h&&(c.dts=(14&l[14])<<27|(255&l[15])<<20|(254&l[16])<<12|(255&l[17])<<5|(254&l[18])>>>3,c.dts*=4,c.dts+=(6&l[18])>>>1)),c.data=l.subarray(9+l[8]),r="video"===e||a.packetLength<=t.size,(i||r)&&(t.size=0,t.data.length=0),r&&d.trigger("data",a)}};ct.prototype.init.call(this),this.push=function(i){({pat:function(){},pes:function(){var t,e;switch(i.streamType){case Z.H264_STREAM_TYPE:case Z.H264_STREAM_TYPE:t=r,e="video";break;case Z.ADTS_STREAM_TYPE:t=n,e="audio";break;case Z.METADATA_STREAM_TYPE:t=s,e="timed-metadata";break;default:return}i.payloadUnitStartIndicator&&a(t,e,!0),t.data.push(i),t.size+=i.data.byteLength},pmt:function(){var t={type:"metadata",tracks:[]},e=i.programMapTable;null!==e.video&&t.tracks.push({timelineStartInfo:{baseMediaDecodeTime:0},id:+e.video,codec:"avc",type:"video"}),null!==e.audio&&t.tracks.push({timelineStartInfo:{baseMediaDecodeTime:0},id:+e.audio,codec:"adts",type:"audio"}),d.trigger("data",t)}})[i.type]()},this.flush=function(){a(r,"video"),a(n,"audio"),a(s,"timed-metadata"),this.trigger("done")}}).prototype=new z;var pt={PAT_PID:0,MP2T_PACKET_LENGTH:188,TransportPacketStream:ut,TransportParseStream:lt,ElementaryStream:ct,TimestampRolloverStream:dt,CaptionStream:J.CaptionStream,Cea608Stream:J.Cea608Stream,MetadataStream:ht};for(var ft in Z)Z.hasOwnProperty(ft)&&(pt[ft]=Z[ft]);var mt,gt=pt,yt=[96e3,88200,64e3,48e3,44100,32e3,24e3,22050,16e3,12e3,11025,8e3,7350];(mt=function(){var l;mt.prototype.init.call(this),this.push=function(t){var e,i,r,n,s,a,o=0,u=0;if("audio"===t.type)for(l?(n=l,(l=new Uint8Array(n.byteLength+t.data.byteLength)).set(n),l.set(t.data,n.byteLength)):l=t.data;o+5<l.length;)if(255===l[o]&&240==(246&l[o+1])){if(i=2*(1&~l[o+1]),e=(3&l[o+3])<<11|l[o+4]<<3|(224&l[o+5])>>5,a=9e4*(s=1024*(1+(3&l[o+6])))/yt[(60&l[o+2])>>>2],r=o+e,l.byteLength<r)return;if(this.trigger("data",{pts:t.pts+u*a,dts:t.dts+u*a,sampleCount:s,audioobjecttype:1+(l[o+2]>>>6&3),channelcount:(1&l[o+2])<<2|(192&l[o+3])>>>6,samplerate:yt[(60&l[o+2])>>>2],samplingfrequencyindex:(60&l[o+2])>>>2,samplesize:16,data:l.subarray(o+7+i,r)}),l.byteLength===r)return void(l=void 0);u++,l=l.subarray(r)}else o++},this.flush=function(){this.trigger("done")}}).prototype=new z;var vt,_t,bt,Tt=mt,St=function(r){var n=r.byteLength,s=0,a=0;this.length=function(){return 8*n},this.bitsAvailable=function(){return 8*n+a},this.loadWord=function(){var t=r.byteLength-n,e=new Uint8Array(4),i=Math.min(4,n);if(0===i)throw new Error("no bytes available");e.set(r.subarray(t,t+i)),s=new DataView(e.buffer).getUint32(0),a=8*i,n-=i},this.skipBits=function(t){var e;t<a||(t-=a,t-=8*(e=Math.floor(t/8)),n-=e,this.loadWord()),s<<=t,a-=t},this.readBits=function(t){var e=Math.min(a,t),i=s>>>32-e;return 0<(a-=e)?s<<=e:0<n&&this.loadWord(),0<(e=t-e)?i<<e|this.readBits(e):i},this.skipLeadingZeros=function(){var t;for(t=0;t<a;++t)if(0!=(s&2147483648>>>t))return s<<=t,a-=t,t;return this.loadWord(),t+this.skipLeadingZeros()},this.skipUnsignedExpGolomb=function(){this.skipBits(1+this.skipLeadingZeros())},this.skipExpGolomb=function(){this.skipBits(1+this.skipLeadingZeros())},this.readUnsignedExpGolomb=function(){var t=this.skipLeadingZeros();return this.readBits(t+1)-1},this.readExpGolomb=function(){var t=this.readUnsignedExpGolomb();return 1&t?1+t>>>1:-1*(t>>>1)},this.readBoolean=function(){return 1===this.readBits(1)},this.readUnsignedByte=function(){return this.readBits(8)},this.loadWord()};(_t=function(){var i,r,n=0;_t.prototype.init.call(this),this.push=function(t){var e;for(r?((e=new Uint8Array(r.byteLength+t.data.byteLength)).set(r),e.set(t.data,r.byteLength),r=e):r=t.data;n<r.byteLength-3;n++)if(1===r[n+2]){i=n+5;break}for(;i<r.byteLength;)switch(r[i]){case 0:if(0!==r[i-1]){i+=2;break}if(0!==r[i-2]){i++;break}for(n+3!==i-2&&this.trigger("data",r.subarray(n+3,i-2));1!==r[++i]&&i<r.length;);n=i-2,i+=3;break;case 1:if(0!==r[i-1]||0!==r[i-2]){i+=3;break}this.trigger("data",r.subarray(n+3,i-2)),n=i-2,i+=3;break;default:i+=3}r=r.subarray(n),i-=n,n=0},this.flush=function(){r&&3<r.byteLength&&this.trigger("data",r.subarray(n+3)),r=null,n=0,this.trigger("done")}}).prototype=new z,bt={100:!0,110:!0,122:!0,244:!0,44:!0,83:!0,86:!0,118:!0,128:!0,138:!0,139:!0,134:!0},(vt=function(){var i,r,n,s,a,o,_,e=new _t;vt.prototype.init.call(this),(i=this).push=function(t){"video"===t.type&&(r=t.trackId,n=t.pts,s=t.dts,e.push(t))},e.on("data",function(t){var e={trackId:r,pts:n,dts:s,data:t};switch(31&t[0]){case 5:e.nalUnitType="slice_layer_without_partitioning_rbsp_idr";break;case 6:e.nalUnitType="sei_rbsp",e.escapedRBSP=a(t.subarray(1));break;case 7:e.nalUnitType="seq_parameter_set_rbsp",e.escapedRBSP=a(t.subarray(1)),e.config=o(e.escapedRBSP);break;case 8:e.nalUnitType="pic_parameter_set_rbsp";break;case 9:e.nalUnitType="access_unit_delimiter_rbsp"}i.trigger("data",e)}),e.on("done",function(){i.trigger("done")}),this.flush=function(){e.flush()},_=function(t,e){var i,r=8,n=8;for(i=0;i<t;i++)0!==n&&(n=(r+e.readExpGolomb()+256)%256),r=0===n?r:n},a=function(t){for(var e,i,r=t.byteLength,n=[],s=1;s<r-2;)0===t[s]&&0===t[s+1]&&3===t[s+2]?(n.push(s+2),s+=2):s++;if(0===n.length)return t;e=r-n.length,i=new Uint8Array(e);var a=0;for(s=0;s<e;a++,s++)a===n[0]&&(a++,n.shift()),i[s]=t[a];return i},o=function(t){var e,i,r,n,s,a,o,u,l,c,h,d,p,f=0,m=0,g=0,y=0,v=1;if(i=(e=new St(t)).readUnsignedByte(),n=e.readUnsignedByte(),r=e.readUnsignedByte(),e.skipUnsignedExpGolomb(),bt[i]&&(3===(s=e.readUnsignedExpGolomb())&&e.skipBits(1),e.skipUnsignedExpGolomb(),e.skipUnsignedExpGolomb(),e.skipBits(1),e.readBoolean()))for(h=3!==s?8:12,p=0;p<h;p++)e.readBoolean()&&_(p<6?16:64,e);if(e.skipUnsignedExpGolomb(),0===(a=e.readUnsignedExpGolomb()))e.readUnsignedExpGolomb();else if(1===a)for(e.skipBits(1),e.skipExpGolomb(),e.skipExpGolomb(),o=e.readUnsignedExpGolomb(),p=0;p<o;p++)e.skipExpGolomb();if(e.skipUnsignedExpGolomb(),e.skipBits(1),u=e.readUnsignedExpGolomb(),l=e.readUnsignedExpGolomb(),0===(c=e.readBits(1))&&e.skipBits(1),e.skipBits(1),e.readBoolean()&&(f=e.readUnsignedExpGolomb(),m=e.readUnsignedExpGolomb(),g=e.readUnsignedExpGolomb(),y=e.readUnsignedExpGolomb()),e.readBoolean()&&e.readBoolean()){switch(e.readUnsignedByte()){case 1:d=[1,1];break;case 2:d=[12,11];break;case 3:d=[10,11];break;case 4:d=[16,11];break;case 5:d=[40,33];break;case 6:d=[24,11];break;case 7:d=[20,11];break;case 8:d=[32,11];break;case 9:d=[80,33];break;case 10:d=[18,11];break;case 11:d=[15,11];break;case 12:d=[64,33];break;case 13:d=[160,99];break;case 14:d=[4,3];break;case 15:d=[3,2];break;case 16:d=[2,1];break;case 255:d=[e.readUnsignedByte()<<8|e.readUnsignedByte(),e.readUnsignedByte()<<8|e.readUnsignedByte()]}d&&(v=d[0]/d[1])}return{profileIdc:i,levelIdc:r,profileCompatibility:n,width:Math.ceil((16*(u+1)-2*f-2*m)*v),height:(2-c)*(l+1)*16-2*g-2*y}}}).prototype=new z;var kt,Ct={H264Stream:vt,NalByteStream:_t};(kt=function(){var o=new Uint8Array,u=0;kt.prototype.init.call(this),this.setTimestamp=function(t){u=t},this.parseId3TagSize=function(t,e){var i=t[e+6]<<21|t[e+7]<<14|t[e+8]<<7|t[e+9];return(16&t[e+5])>>4?i+20:i+10},this.parseAdtsSize=function(t,e){var i=(224&t[e+5])>>5,r=t[e+4]<<3;return 6144&t[e+3]|r|i},this.push=function(t){var e,i,r,n,s=0,a=0;for(o.length?(n=o.length,(o=new Uint8Array(t.byteLength+n)).set(o.subarray(0,n)),o.set(t,n)):o=t;3<=o.length-a;)if(o[a]!=="I".charCodeAt(0)||o[a+1]!=="D".charCodeAt(0)||o[a+2]!=="3".charCodeAt(0))if(!0&o[a]&&240==(240&o[a+1])){if(o.length-a<7)break;if((s=this.parseAdtsSize(o,a))>o.length)break;r={type:"audio",data:o.subarray(a,a+s),pts:u,dts:u},this.trigger("data",r),a+=s}else a++;else{if(o.length-a<10)break;if((s=this.parseId3TagSize(o,a))>o.length)break;i={type:"timed-metadata",data:o.subarray(a,a+s)},this.trigger("data",i),a+=s}e=o.length-a,o=0<e?o.subarray(a):new Uint8Array}}).prototype=new z;var Et,wt,At,Lt,Pt,Ot,xt,It,Dt,Rt,Mt,Ut,Nt,Bt,jt,Ft=kt,Ht=[33,16,5,32,164,27],Vt=[33,65,108,84,1,2,4,8,168,2,4,8,17,191,252],qt=function(t){for(var e=[];t--;)e.push(0);return e},Wt={96000:[Ht,[227,64],qt(154),[56]],88200:[Ht,[231],qt(170),[56]],64000:[Ht,[248,192],qt(240),[56]],48000:[Ht,[255,192],qt(268),[55,148,128],qt(54),[112]],44100:[Ht,[255,192],qt(268),[55,163,128],qt(84),[112]],32000:[Ht,[255,192],qt(268),[55,234],qt(226),[112]],24000:[Ht,[255,192],qt(268),[55,255,128],qt(268),[111,112],qt(126),[224]],16000:[Ht,[255,192],qt(268),[55,255,128],qt(268),[111,255],qt(269),[223,108],qt(195),[1,192]],12000:[Vt,qt(268),[3,127,248],qt(268),[6,255,240],qt(268),[13,255,224],qt(268),[27,253,128],qt(259),[56]],11025:[Vt,qt(268),[3,127,248],qt(268),[6,255,240],qt(268),[13,255,224],qt(268),[27,255,192],qt(268),[55,175,128],qt(108),[112]],8000:[Vt,qt(268),[3,121,16],qt(47),[7]]},zt=(Et=Wt,Object.keys(Et).reduce(function(t,e){return t[e]=new Uint8Array(Et[e].reduce(function(t,e){return t.concat(e)},[])),t},{})),Gt=(wt=function(t){return 9e4*t},At=function(t,e){return t*e},Lt=function(t){return t/9e4},Pt=function(t,e){return t/e},function(t,e){return wt(Pt(t,e))}),Xt=function(t,e){return At(Lt(t),e)},Yt=Ct.H264Stream,$t=["audioobjecttype","channelcount","samplerate","samplingfrequencyindex","samplesize"],Kt=["width","height","profileIdc","levelIdc","profileCompatibility"];Rt=function(t){return t[0]==="I".charCodeAt(0)&&t[1]==="D".charCodeAt(0)&&t[2]==="3".charCodeAt(0)},Bt=function(t,e){var i;if(t.length!==e.length)return!1;for(i=0;i<t.length;i++)if(t[i]!==e[i])return!1;return!0},jt=function(t){var e,i=0;for(e=0;e<t.length;e++)i+=t[e].data.byteLength;return i},(xt=function(n,s){var a=[],o=0,e=0,l=0,c=1/0;s=s||{},xt.prototype.init.call(this),this.push=function(e){Mt(n,e),n&&$t.forEach(function(t){n[t]=e[t]}),a.push(e)},this.setEarliestDts=function(t){e=t-n.timelineStartInfo.baseMediaDecodeTime},this.setVideoBaseMediaDecodeTime=function(t){c=t},this.setAudioAppendStart=function(t){l=t},this.flush=function(){var t,e,i,r;0!==a.length&&(t=this.trimAdtsFramesByEarliestDts_(a),n.baseMediaDecodeTime=Nt(n,s.keepOriginalTimestamps),this.prefixWithSilence_(n,t),n.samples=this.generateSampleTable_(t),i=q.mdat(this.concatenateFrameData_(t)),a=[],e=q.moof(o,[n]),r=new Uint8Array(e.byteLength+i.byteLength),o++,r.set(e),r.set(i,e.byteLength),Ut(n),this.trigger("data",{track:n,boxes:r})),this.trigger("done","AudioSegmentStream")},this.prefixWithSilence_=function(t,e){var i,r,n,s,a=0,o=0,u=0;if(e.length&&(i=Gt(t.baseMediaDecodeTime,t.samplerate),r=Math.ceil(9e4/(t.samplerate/1024)),l&&c&&(a=i-Math.max(l,c),u=(o=Math.floor(a/r))*r),!(o<1||45e3<u))){for((n=zt[t.samplerate])||(n=e[0].data),s=0;s<o;s++)e.splice(s,0,{data:n});t.baseMediaDecodeTime-=Math.floor(Xt(u,t.samplerate))}},this.trimAdtsFramesByEarliestDts_=function(t){return n.minSegmentDts>=e?t:(n.minSegmentDts=1/0,t.filter(function(t){return t.dts>=e&&(n.minSegmentDts=Math.min(n.minSegmentDts,t.dts),n.minSegmentPts=n.minSegmentDts,!0)}))},this.generateSampleTable_=function(t){var e,i,r=[];for(e=0;e<t.length;e++)i=t[e],r.push({size:i.data.byteLength,duration:1024});return r},this.concatenateFrameData_=function(t){var e,i,r=0,n=new Uint8Array(jt(t));for(e=0;e<t.length;e++)i=t[e],n.set(i.data,r),r+=i.data.byteLength;return n}}).prototype=new z,(Ot=function(o,u){var e,i,l=0,c=[],h=[];u=u||{},Ot.prototype.init.call(this),delete o.minPTS,this.gopCache_=[],this.push=function(t){Mt(o,t),"seq_parameter_set_rbsp"!==t.nalUnitType||e||(e=t.config,o.sps=[t.data],Kt.forEach(function(t){o[t]=e[t]},this)),"pic_parameter_set_rbsp"!==t.nalUnitType||i||(i=t.data,o.pps=[t.data]),c.push(t)},this.flush=function(){for(var t,e,i,r,n,s;c.length&&"access_unit_delimiter_rbsp"!==c[0].nalUnitType;)c.shift();if(0===c.length)return this.resetStream_(),void this.trigger("done","VideoSegmentStream");if(t=this.groupNalsIntoFrames_(c),(i=this.groupFramesIntoGops_(t))[0][0].keyFrame||((e=this.getGopForFusion_(c[0],o))?(i.unshift(e),i.byteLength+=e.byteLength,i.nalCount+=e.nalCount,i.pts=e.pts,i.dts=e.dts,i.duration+=e.duration):i=this.extendFirstKeyFrame_(i)),h.length){var a;if(!(a=u.alignGopsAtEnd?this.alignGopsAtEnd_(i):this.alignGopsAtStart_(i)))return this.gopCache_.unshift({gop:i.pop(),pps:o.pps,sps:o.sps}),this.gopCache_.length=Math.min(6,this.gopCache_.length),c=[],this.resetStream_(),void this.trigger("done","VideoSegmentStream");Ut(o),i=a}Mt(o,i),o.samples=this.generateSampleTable_(i),n=q.mdat(this.concatenateNalData_(i)),o.baseMediaDecodeTime=Nt(o,u.keepOriginalTimestamps),this.trigger("processedGopsInfo",i.map(function(t){return{pts:t.pts,dts:t.dts,byteLength:t.byteLength}})),this.gopCache_.unshift({gop:i.pop(),pps:o.pps,sps:o.sps}),this.gopCache_.length=Math.min(6,this.gopCache_.length),c=[],this.trigger("baseMediaDecodeTime",o.baseMediaDecodeTime),this.trigger("timelineStartInfo",o.timelineStartInfo),r=q.moof(l,[o]),s=new Uint8Array(r.byteLength+n.byteLength),l++,s.set(r),s.set(n,r.byteLength),this.trigger("data",{track:o,boxes:s}),this.resetStream_(),this.trigger("done","VideoSegmentStream")},this.resetStream_=function(){Ut(o),i=e=void 0},this.getGopForFusion_=function(t){var e,i,r,n,s,a=1/0;for(s=0;s<this.gopCache_.length;s++)r=(n=this.gopCache_[s]).gop,o.pps&&Bt(o.pps[0],n.pps[0])&&o.sps&&Bt(o.sps[0],n.sps[0])&&(r.dts<o.timelineStartInfo.dts||-1e4<=(e=t.dts-r.dts-r.duration)&&e<=45e3&&(!i||e<a)&&(i=n,a=e));return i?i.gop:null},this.extendFirstKeyFrame_=function(t){var e;return!t[0][0].keyFrame&&1<t.length&&(e=t.shift(),t.byteLength-=e.byteLength,t.nalCount-=e.nalCount,t[0][0].dts=e.dts,t[0][0].pts=e.pts,t[0][0].duration+=e.duration),t},this.groupNalsIntoFrames_=function(t){var e,i,r=[],n=[];for(e=r.byteLength=0;e<t.length;e++)"access_unit_delimiter_rbsp"===(i=t[e]).nalUnitType?(r.length&&(r.duration=i.dts-r.dts,n.push(r)),(r=[i]).byteLength=i.data.byteLength,r.pts=i.pts,r.dts=i.dts):("slice_layer_without_partitioning_rbsp_idr"===i.nalUnitType&&(r.keyFrame=!0),r.duration=i.dts-r.dts,r.byteLength+=i.data.byteLength,r.push(i));return n.length&&(!r.duration||r.duration<=0)&&(r.duration=n[n.length-1].duration),n.push(r),n},this.groupFramesIntoGops_=function(t){var e,i,r=[],n=[];for(r.byteLength=0,r.nalCount=0,r.duration=0,r.pts=t[0].pts,r.dts=t[0].dts,n.byteLength=0,n.nalCount=0,n.duration=0,n.pts=t[0].pts,n.dts=t[0].dts,e=0;e<t.length;e++)(i=t[e]).keyFrame?(r.length&&(n.push(r),n.byteLength+=r.byteLength,n.nalCount+=r.nalCount,n.duration+=r.duration),(r=[i]).nalCount=i.length,r.byteLength=i.byteLength,r.pts=i.pts,r.dts=i.dts,r.duration=i.duration):(r.duration+=i.duration,r.nalCount+=i.length,r.byteLength+=i.byteLength,r.push(i));return n.length&&r.duration<=0&&(r.duration=n[n.length-1].duration),n.byteLength+=r.byteLength,n.nalCount+=r.nalCount,n.duration+=r.duration,n.push(r),n},this.generateSampleTable_=function(t,e){var i,r,n,s,a,o=e||0,u=[];for(i=0;i<t.length;i++)for(s=t[i],r=0;r<s.length;r++)a=s[r],(n={size:0,flags:{isLeading:0,dependsOn:1,isDependedOn:0,hasRedundancy:0,degradationPriority:0}}).dataOffset=o,n.compositionTimeOffset=a.pts-a.dts,n.duration=a.duration,n.size=4*a.length,n.size+=a.byteLength,a.keyFrame&&(n.flags.dependsOn=2),o+=n.size,u.push(n);return u},this.concatenateNalData_=function(t){var e,i,r,n,s,a,o=0,u=t.byteLength,l=t.nalCount,c=new Uint8Array(u+4*l),h=new DataView(c.buffer);for(e=0;e<t.length;e++)for(n=t[e],i=0;i<n.length;i++)for(s=n[i],r=0;r<s.length;r++)a=s[r],h.setUint32(o,a.data.byteLength),o+=4,c.set(a.data,o),o+=a.data.byteLength;return c},this.alignGopsAtStart_=function(t){var e,i,r,n,s,a,o,u;for(s=t.byteLength,a=t.nalCount,o=t.duration,e=i=0;e<h.length&&i<t.length&&(r=h[e],n=t[i],r.pts!==n.pts);)n.pts>r.pts?e++:(i++,s-=n.byteLength,a-=n.nalCount,o-=n.duration);return 0===i?t:i===t.length?null:((u=t.slice(i)).byteLength=s,u.duration=o,u.nalCount=a,u.pts=u[0].pts,u.dts=u[0].dts,u)},this.alignGopsAtEnd_=function(t){var e,i,r,n,s,a,o;for(e=h.length-1,i=t.length-1,s=null,a=!1;0<=e&&0<=i;){if(r=h[e],n=t[i],r.pts===n.pts){a=!0;break}r.pts>n.pts?e--:(e===h.length-1&&(s=i),i--)}if(!a&&null===s)return null;if(0===(o=a?i:s))return t;var u=t.slice(o),l=u.reduce(function(t,e){return t.byteLength+=e.byteLength,t.duration+=e.duration,t.nalCount+=e.nalCount,t},{byteLength:0,duration:0,nalCount:0});return u.byteLength=l.byteLength,u.duration=l.duration,u.nalCount=l.nalCount,u.pts=u[0].pts,u.dts=u[0].dts,u},this.alignGopsWith=function(t){h=t}}).prototype=new z,Mt=function(t,e){"number"==typeof e.pts&&(void 0===t.timelineStartInfo.pts&&(t.timelineStartInfo.pts=e.pts),void 0===t.minSegmentPts?t.minSegmentPts=e.pts:t.minSegmentPts=Math.min(t.minSegmentPts,e.pts),void 0===t.maxSegmentPts?t.maxSegmentPts=e.pts:t.maxSegmentPts=Math.max(t.maxSegmentPts,e.pts)),"number"==typeof e.dts&&(void 0===t.timelineStartInfo.dts&&(t.timelineStartInfo.dts=e.dts),void 0===t.minSegmentDts?t.minSegmentDts=e.dts:t.minSegmentDts=Math.min(t.minSegmentDts,e.dts),void 0===t.maxSegmentDts?t.maxSegmentDts=e.dts:t.maxSegmentDts=Math.max(t.maxSegmentDts,e.dts))},Ut=function(t){delete t.minSegmentDts,delete t.maxSegmentDts,delete t.minSegmentPts,delete t.maxSegmentPts},Nt=function(t,e){var i,r=t.minSegmentDts;return e||(r-=t.timelineStartInfo.dts),i=t.timelineStartInfo.baseMediaDecodeTime,i+=r,i=Math.max(0,i),"audio"===t.type&&(i*=t.samplerate/9e4,i=Math.floor(i)),i},(Dt=function(t,e){this.numberOfTracks=0,this.metadataStream=e,"undefined"!=typeof t.remux?this.remuxTracks=!!t.remux:this.remuxTracks=!0,this.pendingTracks=[],this.videoTrack=null,this.pendingBoxes=[],this.pendingCaptions=[],this.pendingMetadata=[],this.pendingBytes=0,this.emittedTracks=0,Dt.prototype.init.call(this),this.push=function(t){return t.text?this.pendingCaptions.push(t):t.frames?this.pendingMetadata.push(t):(this.pendingTracks.push(t.track),this.pendingBoxes.push(t.boxes),this.pendingBytes+=t.boxes.byteLength,"video"===t.track.type&&(this.videoTrack=t.track),void("audio"===t.track.type&&(this.audioTrack=t.track)))}}).prototype=new z,Dt.prototype.flush=function(t){var e,i,r,n,s=0,a={captions:[],captionStreams:{},metadata:[],info:{}},o=0;if(this.pendingTracks.length<this.numberOfTracks){if("VideoSegmentStream"!==t&&"AudioSegmentStream"!==t)return;if(this.remuxTracks)return;if(0===this.pendingTracks.length)return this.emittedTracks++,void(this.emittedTracks>=this.numberOfTracks&&(this.trigger("done"),this.emittedTracks=0))}for(this.videoTrack?(o=this.videoTrack.timelineStartInfo.pts,Kt.forEach(function(t){a.info[t]=this.videoTrack[t]},this)):this.audioTrack&&(o=this.audioTrack.timelineStartInfo.pts,$t.forEach(function(t){a.info[t]=this.audioTrack[t]},this)),1===this.pendingTracks.length?a.type=this.pendingTracks[0].type:a.type="combined",this.emittedTracks+=this.pendingTracks.length,r=q.initSegment(this.pendingTracks),a.initSegment=new Uint8Array(r.byteLength),a.initSegment.set(r),a.data=new Uint8Array(this.pendingBytes),n=0;n<this.pendingBoxes.length;n++)a.data.set(this.pendingBoxes[n],s),s+=this.pendingBoxes[n].byteLength;for(n=0;n<this.pendingCaptions.length;n++)(e=this.pendingCaptions[n]).startTime=e.startPts-o,e.startTime/=9e4,e.endTime=e.endPts-o,e.endTime/=9e4,a.captionStreams[e.stream]=!0,a.captions.push(e);for(n=0;n<this.pendingMetadata.length;n++)(i=this.pendingMetadata[n]).cueTime=i.pts-o,i.cueTime/=9e4,a.metadata.push(i);a.metadata.dispatchType=this.metadataStream.dispatchType,this.pendingTracks.length=0,this.videoTrack=null,this.pendingBoxes.length=0,this.pendingCaptions.length=0,this.pendingBytes=0,this.pendingMetadata.length=0,this.trigger("data",a),this.emittedTracks>=this.numberOfTracks&&(this.trigger("done"),this.emittedTracks=0)},(It=function(r){var n,s,a=this,i=!0;It.prototype.init.call(this),r=r||{},this.baseMediaDecodeTime=r.baseMediaDecodeTime||0,this.transmuxPipeline_={},this.setupAacPipeline=function(){var e={};(this.transmuxPipeline_=e).type="aac",e.metadataStream=new gt.MetadataStream,e.aacStream=new Ft,e.audioTimestampRolloverStream=new gt.TimestampRolloverStream("audio"),e.timedMetadataTimestampRolloverStream=new gt.TimestampRolloverStream("timed-metadata"),e.adtsStream=new Tt,e.coalesceStream=new Dt(r,e.metadataStream),e.headOfPipeline=e.aacStream,e.aacStream.pipe(e.audioTimestampRolloverStream).pipe(e.adtsStream),e.aacStream.pipe(e.timedMetadataTimestampRolloverStream).pipe(e.metadataStream).pipe(e.coalesceStream),e.metadataStream.on("timestamp",function(t){e.aacStream.setTimestamp(t.timeStamp)}),e.aacStream.on("data",function(t){"timed-metadata"!==t.type||e.audioSegmentStream||(s=s||{timelineStartInfo:{baseMediaDecodeTime:a.baseMediaDecodeTime},codec:"adts",type:"audio"},e.coalesceStream.numberOfTracks++,e.audioSegmentStream=new xt(s,r),e.adtsStream.pipe(e.audioSegmentStream).pipe(e.coalesceStream))}),e.coalesceStream.on("data",this.trigger.bind(this,"data")),e.coalesceStream.on("done",this.trigger.bind(this,"done"))},this.setupTsPipeline=function(){var i={};(this.transmuxPipeline_=i).type="ts",i.metadataStream=new gt.MetadataStream,i.packetStream=new gt.TransportPacketStream,i.parseStream=new gt.TransportParseStream,i.elementaryStream=new gt.ElementaryStream,i.videoTimestampRolloverStream=new gt.TimestampRolloverStream("video"),i.audioTimestampRolloverStream=new gt.TimestampRolloverStream("audio"),i.timedMetadataTimestampRolloverStream=new gt.TimestampRolloverStream("timed-metadata"),i.adtsStream=new Tt,i.h264Stream=new Yt,i.captionStream=new gt.CaptionStream,i.coalesceStream=new Dt(r,i.metadataStream),i.headOfPipeline=i.packetStream,i.packetStream.pipe(i.parseStream).pipe(i.elementaryStream),i.elementaryStream.pipe(i.videoTimestampRolloverStream).pipe(i.h264Stream),i.elementaryStream.pipe(i.audioTimestampRolloverStream).pipe(i.adtsStream),i.elementaryStream.pipe(i.timedMetadataTimestampRolloverStream).pipe(i.metadataStream).pipe(i.coalesceStream),i.h264Stream.pipe(i.captionStream).pipe(i.coalesceStream),i.elementaryStream.on("data",function(t){var e;if("metadata"===t.type){for(e=t.tracks.length;e--;)n||"video"!==t.tracks[e].type?s||"audio"!==t.tracks[e].type||((s=t.tracks[e]).timelineStartInfo.baseMediaDecodeTime=a.baseMediaDecodeTime):(n=t.tracks[e]).timelineStartInfo.baseMediaDecodeTime=a.baseMediaDecodeTime;n&&!i.videoSegmentStream&&(i.coalesceStream.numberOfTracks++,i.videoSegmentStream=new Ot(n,r),i.videoSegmentStream.on("timelineStartInfo",function(t){s&&(s.timelineStartInfo=t,i.audioSegmentStream.setEarliestDts(t.dts))}),i.videoSegmentStream.on("processedGopsInfo",a.trigger.bind(a,"gopInfo")),i.videoSegmentStream.on("baseMediaDecodeTime",function(t){s&&i.audioSegmentStream.setVideoBaseMediaDecodeTime(t)}),i.h264Stream.pipe(i.videoSegmentStream).pipe(i.coalesceStream)),s&&!i.audioSegmentStream&&(i.coalesceStream.numberOfTracks++,i.audioSegmentStream=new xt(s,r),i.adtsStream.pipe(i.audioSegmentStream).pipe(i.coalesceStream))}}),i.coalesceStream.on("data",this.trigger.bind(this,"data")),i.coalesceStream.on("done",this.trigger.bind(this,"done"))},this.setBaseMediaDecodeTime=function(t){var e=this.transmuxPipeline_;this.baseMediaDecodeTime=t,s&&(s.timelineStartInfo.dts=void 0,s.timelineStartInfo.pts=void 0,Ut(s),s.timelineStartInfo.baseMediaDecodeTime=t,e.audioTimestampRolloverStream&&e.audioTimestampRolloverStream.discontinuity()),n&&(e.videoSegmentStream&&(e.videoSegmentStream.gopCache_=[],e.videoTimestampRolloverStream.discontinuity()),n.timelineStartInfo.dts=void 0,n.timelineStartInfo.pts=void 0,Ut(n),e.captionStream.reset(),n.timelineStartInfo.baseMediaDecodeTime=t),e.timedMetadataTimestampRolloverStream&&e.timedMetadataTimestampRolloverStream.discontinuity()},this.setAudioAppendStart=function(t){s&&this.transmuxPipeline_.audioSegmentStream.setAudioAppendStart(t)},this.alignGopsWith=function(t){n&&this.transmuxPipeline_.videoSegmentStream&&this.transmuxPipeline_.videoSegmentStream.alignGopsWith(t)},this.push=function(t){if(i){var e=Rt(t);e&&"aac"!==this.transmuxPipeline_.type?this.setupAacPipeline():e||"ts"===this.transmuxPipeline_.type||this.setupTsPipeline(),i=!1}this.transmuxPipeline_.headOfPipeline.push(t)},this.flush=function(){i=!0,this.transmuxPipeline_.headOfPipeline.flush()},this.resetCaptions=function(){this.transmuxPipeline_.captionStream&&this.transmuxPipeline_.captionStream.reset()}}).prototype=new z;var Qt={Transmuxer:It,VideoSegmentStream:Ot,AudioSegmentStream:xt,AUDIO_PROPERTIES:$t,VIDEO_PROPERTIES:Kt},Jt={generator:q,Transmuxer:Qt.Transmuxer,AudioSegmentStream:Qt.AudioSegmentStream,VideoSegmentStream:Qt.VideoSegmentStream},Zt=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},te=function(){function r(t,e){for(var i=0;i<e.length;i++){var r=e[i];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}return function(t,e,i){return e&&r(t.prototype,e),i&&r(t,i),t}}(),ee=function(){function e(t){Zt(this,e),this.options=t||{},this.init()}return te(e,[{key:"init",value:function(){var t;this.transmuxer&&this.transmuxer.dispose(),this.transmuxer=new Jt.Transmuxer(this.options),(t=this.transmuxer).on("data",function(t){var e=t.initSegment;t.initSegment={data:e.buffer,byteOffset:e.byteOffset,byteLength:e.byteLength};var i=t.data;t.data=i.buffer,H.postMessage({action:"data",segment:t,byteOffset:i.byteOffset,byteLength:i.byteLength},[t.data])}),t.captionStream&&t.captionStream.on("data",function(t){H.postMessage({action:"caption",data:t})}),t.on("done",function(t){H.postMessage({action:"done"})}),t.on("gopInfo",function(t){H.postMessage({action:"gopInfo",gopInfo:t})})}},{key:"push",value:function(t){var e=new Uint8Array(t.data,t.byteOffset,t.byteLength);this.transmuxer.push(e)}},{key:"reset",value:function(){this.init()}},{key:"setTimestampOffset",value:function(t){var e=t.timestampOffset||0;this.transmuxer.setBaseMediaDecodeTime(Math.round(9e4*e))}},{key:"setAudioAppendStart",value:function(t){this.transmuxer.setAudioAppendStart(Math.ceil(9e4*t.appendStart))}},{key:"flush",value:function(t){this.transmuxer.flush()}},{key:"resetCaptions",value:function(){this.transmuxer.resetCaptions()}},{key:"alignGopsWith",value:function(t){this.transmuxer.alignGopsWith(t.gopsToAlignWith.slice())}}]),e}();new function(t){t.onmessage=function(t){"init"===t.data.action&&t.data.options?this.messageHandlers=new ee(t.data.options):(this.messageHandlers||(this.messageHandlers=new ee),t.data&&t.data.action&&"init"!==t.data.action&&this.messageHandlers[t.data.action]&&this.messageHandlers[t.data.action](t.data))}}(re)}()}),Mu=function(t){return/mp4a\.\d+.\d+/i.test(t)},Uu=function(t){return/avc1\.[\da-f]+/i.test(t)},Nu=function(t){return t.map(function(t){return t.replace(/avc1\.(\d+)\.(\d+)/i,function(t,e,i){return"avc1."+("00"+Number(e).toString(16)).slice(-2)+"00"+("00"+Number(i).toString(16)).slice(-2)})})},Bu=function(n){function s(t,e){y(this,s);var i=b(this,n.call(this,As.EventTarget));i.timestampOffset_=0,i.pendingBuffers_=[],i.bufferUpdating_=!1,i.mediaSource_=t,i.codecs_=e,i.audioCodec_=null,i.videoCodec_=null,i.audioDisabled_=!1,i.appendAudioInitSegment_=!0,i.gopBuffer_=[],i.timeMapping_=0,i.safeAppend_=11<=As.browser.IE_VERSION;var r={remux:!1,alignGopsAtEnd:i.safeAppend_};return i.codecs_.forEach(function(t){Mu(t)?i.audioCodec_=t:Uu(t)&&(i.videoCodec_=t)}),i.transmuxer_=new Ru,i.transmuxer_.postMessage({action:"init",options:r}),i.transmuxer_.onmessage=function(t){return"data"===t.data.action?i.data_(t):"done"===t.data.action?i.done_(t):"gopInfo"===t.data.action?i.appendGopInfo_(t):void 0},Object.defineProperty(i,"timestampOffset",{get:function(){return this.timestampOffset_},set:function(t){"number"==typeof t&&0<=t&&(this.timestampOffset_=t,this.appendAudioInitSegment_=!0,this.gopBuffer_.length=0,this.timeMapping_=0,this.transmuxer_.postMessage({action:"setTimestampOffset",timestampOffset:t}))}}),Object.defineProperty(i,"appendWindowStart",{get:function(){return(this.videoBuffer_||this.audioBuffer_).appendWindowStart},set:function(t){this.videoBuffer_&&(this.videoBuffer_.appendWindowStart=t),this.audioBuffer_&&(this.audioBuffer_.appendWindowStart=t)}}),Object.defineProperty(i,"updating",{get:function(){return!!(this.bufferUpdating_||!this.audioDisabled_&&this.audioBuffer_&&this.audioBuffer_.updating||this.videoBuffer_&&this.videoBuffer_.updating)}}),Object.defineProperty(i,"buffered",{get:function(){var t=null,e=null,i=0,r=[],n=[];if(!this.videoBuffer_&&!this.audioBuffer_)return As.createTimeRange();if(!this.videoBuffer_)return this.audioBuffer_.buffered;if(!this.audioBuffer_)return this.videoBuffer_.buffered;if(this.audioDisabled_)return this.videoBuffer_.buffered;if(0===this.videoBuffer_.buffered.length&&0===this.audioBuffer_.buffered.length)return As.createTimeRange();for(var s=this.videoBuffer_.buffered,a=this.audioBuffer_.buffered,o=s.length;o--;)r.push({time:s.start(o),type:"start"}),r.push({time:s.end(o),type:"end"});for(o=a.length;o--;)r.push({time:a.start(o),type:"start"}),r.push({time:a.end(o),type:"end"});for(r.sort(function(t,e){return t.time-e.time}),o=0;o<r.length;o++)"start"===r[o].type?2===++i&&(t=r[o].time):"end"===r[o].type&&1===--i&&(e=r[o].time),null!==t&&null!==e&&(n.push([t,e]),e=t=null);return As.createTimeRanges(n)}}),i}return _(s,n),s.prototype.data_=function(t){var e=t.data.segment;e.data=new Uint8Array(e.data,t.data.byteOffset,t.data.byteLength),e.initSegment=new Uint8Array(e.initSegment.data,e.initSegment.byteOffset,e.initSegment.byteLength),function(t,e,i){var r=e.player_;if(i.captions&&i.captions.length)for(var n in t.inbandTextTracks_||(t.inbandTextTracks_={}),i.captionStreams)if(!t.inbandTextTracks_[n]){r.tech_.trigger({type:"usage",name:"hls-608"});var s=r.textTracks().getTrackById(n);t.inbandTextTracks_[n]=s||r.addRemoteTextTrack({kind:"captions",id:n,label:n},!1).track}i.metadata&&i.metadata.length&&!t.metadataTrack_&&(t.metadataTrack_=r.addRemoteTextTrack({kind:"metadata",label:"Timed Metadata"},!1).track,t.metadataTrack_.inBandMetadataTrackDispatchType=i.metadata.dispatchType)}(this,this.mediaSource_,e),this.pendingBuffers_.push(e)},s.prototype.done_=function(t){"closed"!==this.mediaSource_.readyState?this.processPendingSegments_():this.pendingBuffers_.length=0},s.prototype.createRealSourceBuffers_=function(){var r=this,n=["audio","video"];n.forEach(function(e){if(r[e+"Codec_"]&&!r[e+"Buffer_"]){var i=null;if(r.mediaSource_[e+"Buffer_"])(i=r.mediaSource_[e+"Buffer_"]).updating=!1;else{var t=e+'/mp4;codecs="'+r[e+"Codec_"]+'"';i=function(t,e){var i=t.addSourceBuffer(e),r=Object.create(null);r.updating=!1,r.realBuffer_=i;var n=function(e){"function"==typeof i[e]?r[e]=function(){return i[e].apply(i,arguments)}:"undefined"==typeof r[e]&&Object.defineProperty(r,e,{get:function(){return i[e]},set:function(t){return i[e]=t}})};for(var s in i)n(s);return r}(r.mediaSource_.nativeMediaSource_,t),r.mediaSource_[e+"Buffer_"]=i}r[e+"Buffer_"]=i,["update","updatestart","updateend"].forEach(function(t){i.addEventListener(t,function(){if("audio"!==e||!r.audioDisabled_)return"updateend"===t&&(r[e+"Buffer_"].updating=!1),n.every(function(t){return!("audio"!==t||!r.audioDisabled_)||(e===t||!r[t+"Buffer_"]||!r[t+"Buffer_"].updating)})?r.trigger(t):void 0})})}})},s.prototype.appendBuffer=function(t){if(this.bufferUpdating_=!0,this.audioBuffer_&&this.audioBuffer_.buffered.length){var e=this.audioBuffer_.buffered;this.transmuxer_.postMessage({action:"setAudioAppendStart",appendStart:e.end(e.length-1)})}this.videoBuffer_&&this.transmuxer_.postMessage({action:"alignGopsWith",gopsToAlignWith:function(t,e,i){if(!e||!t.length)return[];var r=Math.ceil(9e4*(e.currentTime()-i+3)),n=void 0;for(n=0;n<t.length&&!(t[n].pts>r);n++);return t.slice(n)}(this.gopBuffer_,this.mediaSource_.player_,this.timeMapping_)}),this.transmuxer_.postMessage({action:"push",data:t.buffer,byteOffset:t.byteOffset,byteLength:t.byteLength},[t.buffer]),this.transmuxer_.postMessage({action:"flush"})},s.prototype.appendGopInfo_=function(t){this.gopBuffer_=function(t,e,i){if(!e.length)return t;if(i)return e.slice();for(var r=e[0].pts,n=0;n<t.length&&!(t[n].pts>=r);n++);return t.slice(0,n).concat(e)}(this.gopBuffer_,t.data.gopInfo,this.safeAppend_)},s.prototype.remove=function(t,e){if(this.videoBuffer_&&(this.videoBuffer_.updating=!0,this.videoBuffer_.remove(t,e),this.gopBuffer_=function(t,e,i,r){for(var n=Math.ceil(9e4*(e-r)),s=Math.ceil(9e4*(i-r)),a=t.slice(),o=t.length;o--&&!(t[o].pts<=s););if(-1===o)return a;for(var u=o+1;u--&&!(t[u].pts<=n););return u=Math.max(u,0),a.splice(u,o-u+1),a}(this.gopBuffer_,t,e,this.timeMapping_)),!this.audioDisabled_&&this.audioBuffer_&&(this.audioBuffer_.updating=!0,this.audioBuffer_.remove(t,e)),bu(t,e,this.metadataTrack_),this.inbandTextTracks_)for(var i in this.inbandTextTracks_)bu(t,e,this.inbandTextTracks_[i])},s.prototype.processPendingSegments_=function(){var t={video:{segments:[],bytes:0},audio:{segments:[],bytes:0},captions:[],metadata:[]};t=this.pendingBuffers_.reduce(function(t,e){var i=e.type,r=e.data,n=e.initSegment;return t[i].segments.push(r),t[i].bytes+=r.byteLength,t[i].initSegment=n,e.captions&&(t.captions=t.captions.concat(e.captions)),e.info&&(t[i].info=e.info),e.metadata&&(t.metadata=t.metadata.concat(e.metadata)),t},t),this.videoBuffer_||this.audioBuffer_||(0===t.video.bytes&&(this.videoCodec_=null),0===t.audio.bytes&&(this.audioCodec_=null),this.createRealSourceBuffers_()),t.audio.info&&this.mediaSource_.trigger({type:"audioinfo",info:t.audio.info}),t.video.info&&this.mediaSource_.trigger({type:"videoinfo",info:t.video.info}),this.appendAudioInitSegment_&&(!this.audioDisabled_&&this.audioBuffer_&&(t.audio.segments.unshift(t.audio.initSegment),t.audio.bytes+=t.audio.initSegment.byteLength),this.appendAudioInitSegment_=!1);var e=!1;this.videoBuffer_&&t.video.bytes?(t.video.segments.unshift(t.video.initSegment),t.video.bytes+=t.video.initSegment.byteLength,this.concatAndAppendSegments_(t.video,this.videoBuffer_),Su(this,t.captions,t.metadata)):!this.videoBuffer_||!this.audioDisabled_&&this.audioBuffer_||(e=!0),!this.audioDisabled_&&this.audioBuffer_&&this.concatAndAppendSegments_(t.audio,this.audioBuffer_),this.pendingBuffers_.length=0,e&&this.trigger("updateend"),this.bufferUpdating_=!1},s.prototype.concatAndAppendSegments_=function(t,e){var i=0,r=void 0;if(t.bytes){r=new Uint8Array(t.bytes),t.segments.forEach(function(t){r.set(t,i),i+=t.byteLength});try{e.updating=!0,e.appendBuffer(r)}catch(t){this.mediaSource_.player_&&this.mediaSource_.player_.error({code:-3,type:"APPEND_BUFFER_ERR",message:t.message,originalError:t})}}},s.prototype.abort=function(){this.videoBuffer_&&this.videoBuffer_.abort(),!this.audioDisabled_&&this.audioBuffer_&&this.audioBuffer_.abort(),this.transmuxer_&&this.transmuxer_.postMessage({action:"reset"}),this.pendingBuffers_.length=0,this.bufferUpdating_=!1},s}(As.EventTarget),ju=function(e){function i(){y(this,i);var s=b(this,e.call(this)),t=void 0;for(t in s.nativeMediaSource_=new g.MediaSource,s.nativeMediaSource_)t in i.prototype||"function"!=typeof s.nativeMediaSource_[t]||(s[t]=s.nativeMediaSource_[t].bind(s.nativeMediaSource_));return s.duration_=NaN,Object.defineProperty(s,"duration",{get:function(){return this.duration_===1/0?this.duration_:this.nativeMediaSource_.duration},set:function(t){(this.duration_=t)===1/0||(this.nativeMediaSource_.duration=t)}}),Object.defineProperty(s,"seekable",{get:function(){return this.duration_===1/0?As.createTimeRanges([[0,this.nativeMediaSource_.duration]]):this.nativeMediaSource_.seekable}}),Object.defineProperty(s,"readyState",{get:function(){return this.nativeMediaSource_.readyState}}),Object.defineProperty(s,"activeSourceBuffers",{get:function(){return this.activeSourceBuffers_}}),s.sourceBuffers=[],s.activeSourceBuffers_=[],s.updateActiveSourceBuffers_=function(){if(s.activeSourceBuffers_.length=0,1===s.sourceBuffers.length){var t=s.sourceBuffers[0];return t.appendAudioInitSegment_=!0,t.audioDisabled_=!t.audioCodec_,void s.activeSourceBuffers_.push(t)}for(var i=!1,r=!0,e=0;e<s.player_.audioTracks().length;e++){var n=s.player_.audioTracks()[e];if(n.enabled&&"main"!==n.kind){r=!(i=!0);break}}s.sourceBuffers.forEach(function(t,e){if(t.appendAudioInitSegment_=!0,t.videoCodec_&&t.audioCodec_)t.audioDisabled_=i;else if(t.videoCodec_&&!t.audioCodec_)t.audioDisabled_=!0,r=!1;else if(!t.videoCodec_&&t.audioCodec_&&(t.audioDisabled_=e?r:!r,t.audioDisabled_))return;s.activeSourceBuffers_.push(t)})},s.onPlayerMediachange_=function(){s.sourceBuffers.forEach(function(t){t.appendAudioInitSegment_=!0})},s.onHlsReset_=function(){s.sourceBuffers.forEach(function(t){t.transmuxer_&&t.transmuxer_.postMessage({action:"resetCaptions"})})},s.onHlsSegmentTimeMapping_=function(e){s.sourceBuffers.forEach(function(t){return t.timeMapping_=e.mapping})},["sourceopen","sourceclose","sourceended"].forEach(function(t){this.nativeMediaSource_.addEventListener(t,this.trigger.bind(this))},s),s.on("sourceopen",function(t){var e=p.querySelector('[src="'+s.url_+'"]');e&&(s.player_=As(e.parentNode),s.player_.tech_.on("hls-reset",s.onHlsReset_),s.player_.tech_.on("hls-segment-time-mapping",s.onHlsSegmentTimeMapping_),s.player_.audioTracks&&s.player_.audioTracks()&&(s.player_.audioTracks().on("change",s.updateActiveSourceBuffers_),s.player_.audioTracks().on("addtrack",s.updateActiveSourceBuffers_),s.player_.audioTracks().on("removetrack",s.updateActiveSourceBuffers_)),s.player_.on("mediachange",s.onPlayerMediachange_))}),s.on("sourceended",function(t){for(var e=Tu(s.duration),i=0;i<s.sourceBuffers.length;i++){var r=s.sourceBuffers[i],n=r.metadataTrack_&&r.metadataTrack_.cues;n&&n.length&&(n[n.length-1].endTime=e)}}),s.on("sourceclose",function(t){this.sourceBuffers.forEach(function(t){t.transmuxer_&&t.transmuxer_.terminate()}),this.sourceBuffers.length=0,this.player_&&(this.player_.audioTracks&&this.player_.audioTracks()&&(this.player_.audioTracks().off("change",this.updateActiveSourceBuffers_),this.player_.audioTracks().off("addtrack",this.updateActiveSourceBuffers_),this.player_.audioTracks().off("removetrack",this.updateActiveSourceBuffers_)),this.player_.el_&&(this.player_.off("mediachange",this.onPlayerMediachange_),this.player_.tech_.off("hls-reset",this.onHlsReset_),this.player_.tech_.off("hls-segment-time-mapping",this.onHlsSegmentTimeMapping_)))}),s}return _(i,e),i.prototype.addSeekableRange_=function(t,e){var i=void 0;if(this.duration!==1/0)throw(i=new Error("MediaSource.addSeekableRange() can only be invoked when the duration is Infinity")).name="InvalidStateError",i.code=11,i;(e>this.nativeMediaSource_.duration||isNaN(this.nativeMediaSource_.duration))&&(this.nativeMediaSource_.duration=e)},i.prototype.addSourceBuffer=function(t){var n,e,i=void 0,r=(n={type:"",parameters:{}},e=t.trim().split(";"),n.type=e.shift().trim(),e.forEach(function(t){var e=t.trim().split("=");if(1<e.length){var i=e[0].replace(/"/g,"").trim(),r=e[1].replace(/"/g,"").trim();n.parameters[i]=r}}),n);if(/^(video|audio)\/mp2t$/i.test(r.type)){var s=[];r.parameters&&r.parameters.codecs&&(s=r.parameters.codecs.split(","),s=(s=Nu(s)).filter(function(t){return Mu(t)||Uu(t)})),0===s.length&&(s=["avc1.4d400d","mp4a.40.2"]),i=new Bu(this,s),0!==this.sourceBuffers.length&&(this.sourceBuffers[0].createRealSourceBuffers_(),i.createRealSourceBuffers_(),this.sourceBuffers[0].audioDisabled_=!0)}else i=this.nativeMediaSource_.addSourceBuffer(t);return this.sourceBuffers.push(i),i},i}(As.EventTarget),Fu=0;As.mediaSources={};var Hu=function(t,e){var i=As.mediaSources[t];if(!i)throw new Error("Media Source not found (Video.js)");i.trigger({type:"sourceopen",swfId:e})},Vu=function(){return!!g.MediaSource&&!!g.MediaSource.isTypeSupported&&g.MediaSource.isTypeSupported('video/mp4;codecs="avc1.4d400d,mp4a.40.2"')},qu=function(){if(this.MediaSource={open:Hu,supportsNativeMediaSources:Vu},Vu())return new ju;throw new Error("Cannot use create a virtual MediaSource for this video")};qu.open=Hu,qu.supportsNativeMediaSources=Vu;var Wu={createObjectURL:function(t){var e=void 0;return t instanceof ju?(e=g.URL.createObjectURL(t.nativeMediaSource_),t.url_=e):t instanceof ju?(e="blob:vjs-media-source/"+Fu,Fu++,As.mediaSources[e]=t,e):(e=g.URL.createObjectURL(t),t.url_=e)}};As.MediaSource=qu,As.URL=Wu;var zu=As.mergeOptions,Gu=function(t,e){for(var a=zu(t,{duration:e.duration,minimumUpdatePeriod:e.minimumUpdatePeriod}),i=0;i<e.playlists.length;i++){var r=Bo(a,e.playlists[i]);r&&(a=r)}return No(e,function(t,e,i,r){if(t.playlists&&t.playlists.length){var n=t.playlists[0].uri,s=Bo(a,t.playlists[0]);s&&((a=s).mediaGroups[e][i][r].playlists[0]=a.playlists[n])}}),a},Xu=function(s){function a(t,e,i,r){y(this,a);var n=b(this,s.call(this));if(n.hls_=e,n.withCredentials=i,!t)throw new Error("A non-empty playlist URL or playlist is required");return n.on("minimumUpdatePeriod",function(){n.refreshXml_()}),n.on("mediaupdatetimeout",function(){n.refreshMedia_()}),"string"==typeof t?(n.srcUrl=t,n.state="HAVE_NOTHING",b(n)):(n.masterPlaylistLoader_=r,n.state="HAVE_METADATA",n.started=!0,n.media(t),g.setTimeout(function(){n.trigger("loadedmetadata")},0),n)}return _(a,s),a.prototype.dispose=function(){this.stopRequest(),g.clearTimeout(this.mediaUpdateTimeout)},a.prototype.stopRequest=function(){if(this.request){var t=this.request;this.request=null,t.onreadystatechange=null,t.abort()}},a.prototype.media=function(t){if(!t)return this.media_;if("HAVE_NOTHING"===this.state)throw new Error("Cannot switch media playlist from "+this.state);var e=this.state;if("string"==typeof t){if(!this.master.playlists[t])throw new Error("Unknown playlist URI: "+t);t=this.master.playlists[t]}var i=!this.media_||t.uri!==this.media_.uri;this.state="HAVE_METADATA",i&&(this.media_&&this.trigger("mediachanging"),this.media_=t,this.refreshMedia_(),"HAVE_MASTER"!==e&&this.trigger("mediachange"))},a.prototype.pause=function(){this.stopRequest(),"HAVE_NOTHING"===this.state&&(this.started=!1)},a.prototype.load=function(){this.started?this.trigger("loadedplaylist"):this.start()},a.prototype.parseMasterXml=function(){var s=Sa(this.masterXml_,{manifestUri:this.srcUrl,clientOffset:this.clientOffset_});s.uri=this.srcUrl;for(var t=0;t<s.playlists.length;t++){var e="placeholder-uri-"+t;s.playlists[t].uri=e,s.playlists[e]=s.playlists[t]}return No(s,function(t,e,i,r){if(t.playlists&&t.playlists.length){var n="placeholder-uri-"+e+"-"+i+"-"+r;t.playlists[0].uri=n,s.playlists[n]=t.playlists[0]}}),jo(s),Fo(s),s},a.prototype.start=function(){var i=this;this.started=!0,this.request=this.hls_.xhr({uri:this.srcUrl,withCredentials:this.withCredentials},function(t,e){if(i.request){if(i.request=null,t)return i.error={status:e.status,message:"DASH playlist request error at URL: "+i.srcUrl,responseText:e.responseText,code:2},"HAVE_NOTHING"===i.state&&(i.started=!1),i.trigger("error");i.masterXml_=e.responseText,e.responseHeaders&&e.responseHeaders.date?i.masterLoaded_=Date.parse(e.responseHeaders.date):i.masterLoaded_=Date.now(),i.syncClientServerClock_(i.onClientServerClockSync_.bind(i))}})},a.prototype.syncClientServerClock_=function(r){var n=this,s=ka(this.masterXml_);return null===s?(this.clientOffset_=this.masterLoaded_-Date.now(),r()):"DIRECT"===s.method?(this.clientOffset_=s.value-Date.now(),r()):void(this.request=this.hls_.xhr({uri:Ro(this.srcUrl,s.value),method:s.method,withCredentials:this.withCredentials},function(t,e){if(n.request){if(t)return n.clientOffset_=n.masterLoaded_-Date.now(),r();var i=void 0;i="HEAD"===s.method?e.responseHeaders&&e.responseHeaders.date?Date.parse(e.responseHeaders.date):n.masterLoaded_:Date.parse(e.responseText),n.clientOffset_=i-Date.now(),r()}}))},a.prototype.onClientServerClockSync_=function(){var t=this;this.master=this.parseMasterXml(),this.state="HAVE_MASTER",this.trigger("loadedplaylist"),this.media_||this.media(this.master.playlists[0]),g.setTimeout(function(){t.trigger("loadedmetadata")},0),this.master.minimumUpdatePeriod&&g.setTimeout(function(){t.trigger("minimumUpdatePeriod")},this.master.minimumUpdatePeriod)},a.prototype.refreshXml_=function(){var r=this;this.request=this.hls_.xhr({uri:this.srcUrl,withCredentials:this.withCredentials},function(t,e){if(r.request){if(r.request=null,t)return r.error={status:e.status,message:"DASH playlist request error at URL: "+r.srcUrl,responseText:e.responseText,code:2},"HAVE_NOTHING"===r.state&&(r.started=!1),r.trigger("error");r.masterXml_=e.responseText;var i=r.parseMasterXml();r.master=Gu(r.master,i),g.setTimeout(function(){r.trigger("minimumUpdatePeriod")},r.master.minimumUpdatePeriod)}})},a.prototype.refreshMedia_=function(){var t=this,e=void 0,i=void 0;this.masterPlaylistLoader_?(e=this.masterPlaylistLoader_.master,i=this.masterPlaylistLoader_.parseMasterXml()):(e=this.master,i=this.parseMasterXml());var r=Gu(e,i);r?(this.masterPlaylistLoader_?this.masterPlaylistLoader_.master=r:this.master=r,this.media_=r.playlists[this.media_.uri]):this.trigger("playlistunchanged"),this.media().endList||(this.mediaUpdateTimeout=g.setTimeout(function(){t.trigger("mediaupdatetimeout")},Ho(this.media(),!!r))),this.trigger("loadedplaylist")},a}(As.EventTarget),Yu=function(t){return As.log.debug?As.log.debug.bind(As,"VHS:",t+" >"):function(){}};function $u(){}var Ku=function(){function n(t,e,i,r){y(this,n),this.callbacks_=[],this.pendingCallback_=null,this.timestampOffset_=0,this.mediaSource=t,this.processedAppend_=!1,this.type_=i,this.mimeType_=e,this.logger_=Yu("SourceUpdater["+i+"]["+e+"]"),"closed"===t.readyState?t.addEventListener("sourceopen",this.createSourceBuffer_.bind(this,e,r)):this.createSourceBuffer_(e,r)}return n.prototype.createSourceBuffer_=function(t,e){var i=this;this.sourceBuffer_=this.mediaSource.addSourceBuffer(t),this.logger_("created SourceBuffer"),e&&(e.trigger("sourcebufferadded"),this.mediaSource.sourceBuffers.length<2)?e.on("sourcebufferadded",function(){i.start_()}):this.start_()},n.prototype.start_=function(){var e=this;this.started_=!0,this.onUpdateendCallback_=function(){var t=e.pendingCallback_;e.pendingCallback_=null,e.logger_("buffered ["+vu(e.buffered())+"]"),t&&t(),e.runCallback_()},this.sourceBuffer_.addEventListener("updateend",this.onUpdateendCallback_),this.runCallback_()},n.prototype.abort=function(t){var e=this;this.processedAppend_&&this.queueCallback_(function(){e.sourceBuffer_.abort()},t)},n.prototype.appendBuffer=function(t,e){var i=this;this.processedAppend_=!0,this.queueCallback_(function(){i.sourceBuffer_.appendBuffer(t)},e)},n.prototype.buffered=function(){return this.sourceBuffer_?this.sourceBuffer_.buffered:As.createTimeRanges()},n.prototype.remove=function(t,e){var i=this;this.processedAppend_&&this.queueCallback_(function(){i.logger_("remove ["+t+" => "+e+"]"),i.sourceBuffer_.remove(t,e)},$u)},n.prototype.updating=function(){return!this.sourceBuffer_||this.sourceBuffer_.updating||this.pendingCallback_},n.prototype.timestampOffset=function(t){var e=this;return"undefined"!=typeof t&&(this.queueCallback_(function(){e.sourceBuffer_.timestampOffset=t}),this.timestampOffset_=t),this.timestampOffset_},n.prototype.queueCallback_=function(t,e){this.callbacks_.push([t.bind(this),e]),this.runCallback_()},n.prototype.runCallback_=function(){var t=void 0;!this.updating()&&this.callbacks_.length&&this.started_&&(t=this.callbacks_.shift(),this.pendingCallback_=t[1],t[0]())},n.prototype.dispose=function(){this.sourceBuffer_.removeEventListener("updateend",this.onUpdateendCallback_),this.sourceBuffer_&&"open"===this.mediaSource.readyState&&this.sourceBuffer_.abort()},n}(),Qu={GOAL_BUFFER_LENGTH:30,MAX_GOAL_BUFFER_LENGTH:60,GOAL_BUFFER_LENGTH_RATE:1,BANDWIDTH_VARIANCE:1.2,BUFFER_LOW_WATER_LINE:0,MAX_BUFFER_LOW_WATER_LINE:30,BUFFER_LOW_WATER_LINE_RATE:1},Ju=2,Zu=-101,tl=-102,el=function(t){var e,i,r={};return t.byterange&&(r.Range=(e=t.byterange,i=e.offset+e.length-1,"bytes="+e.offset+"-"+i)),r},il=function(t){t.forEach(function(t){t.abort()})},rl=function(t,e){return e.timedout?{status:e.status,message:"HLS request timed-out at URL: "+e.uri,code:Zu,xhr:e}:e.aborted?{status:e.status,message:"HLS request aborted at URL: "+e.uri,code:tl,xhr:e}:t?{status:e.status,message:"HLS request errored at URL: "+e.uri,code:Ju,xhr:e}:null},nl=function(a,o,u){var l=[],c=0;return function(t,e){if(t&&(il(a),l.push(t)),(c+=1)===a.length){if(e.endOfAllRequests=Date.now(),0<l.length){var i=l.reduce(function(t,e){return e.code>t.code?e:t});return u(i,e)}return e.encryptedBytes?(n=e,s=u,(r=o).addEventListener("message",function t(e){if(e.data.source===n.requestId){r.removeEventListener("message",t);var i=e.data.decrypted;return n.bytes=new Uint8Array(i.bytes,i.byteOffset,i.byteLength),s(null,n)}}),void r.postMessage(cu({source:n.requestId,encrypted:n.encryptedBytes,key:n.key.bytes,iv:n.key.iv}),[n.encryptedBytes.buffer,n.key.bytes.buffer])):u(null,e)}var r,n,s}},sl=function(n,s){return function(t){var e,i,r;return n.stats=As.mergeOptions(n.stats,(i=(e=t).target,(r={bandwidth:1/0,bytesReceived:0,roundTripTime:Date.now()-i.requestTime||0}).bytesReceived=e.loaded,r.bandwidth=Math.floor(r.bytesReceived/r.roundTripTime*8*1e3),r)),!n.stats.firstBytesReceivedAt&&n.stats.bytesReceived&&(n.stats.firstBytesReceivedAt=Date.now()),s(t,n)}},al=function(t,e,i,r,n,s){var a,o,u,l,c=[],h=nl(c,i,s);if(r.key){var d=t(As.mergeOptions(e,{uri:r.key.resolvedUri,responseType:"arraybuffer"}),(a=r,o=h,function(t,e){var i=e.response,r=rl(t,e);if(r)return o(r,a);if(16!==i.byteLength)return o({status:e.status,message:"Invalid HLS key at URL: "+e.uri,code:Ju,xhr:e},a);var n=new DataView(i);return a.key.bytes=new Uint32Array([n.getUint32(0),n.getUint32(4),n.getUint32(8),n.getUint32(12)]),o(null,a)}));c.push(d)}if(r.map&&!r.map.bytes){var p=t(As.mergeOptions(e,{uri:r.map.resolvedUri,responseType:"arraybuffer",headers:el(r.map)}),(u=r,l=h,function(t,e){var i=e.response,r=rl(t,e);return r?l(r,u):0===i.byteLength?l({status:e.status,message:"Empty HLS segment content at URL: "+e.uri,code:Ju,xhr:e},u):(u.map.bytes=new Uint8Array(e.response),l(null,u))}));c.push(p)}var f,m,g=t(As.mergeOptions(e,{uri:r.resolvedUri,responseType:"arraybuffer",headers:el(r)}),(f=r,m=h,function(t,e){var i,r=e.response,n=rl(t,e);return n?m(n,f):0===r.byteLength?m({status:e.status,message:"Empty HLS segment content at URL: "+e.uri,code:Ju,xhr:e},f):(f.stats={bandwidth:(i=e).bandwidth,bytesReceived:i.bytesReceived||0,roundTripTime:i.roundTripTime||0},f.key?f.encryptedBytes=new Uint8Array(e.response):f.bytes=new Uint8Array(e.response),m(null,f))}));return g.addEventListener("progress",sl(r,n)),c.push(g),function(){return il(c)}},ol={videoCodec:"avc1",videoObjectTypeIndicator:".4d400d",audioProfile:"2"},ul=function(){var t,e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:"",i={codecCount:0};return i.codecCount=e.split(",").length,i.codecCount=i.codecCount||2,(t=/(^|\s|,)+(avc[13])([^ ,]*)/i.exec(e))&&(i.videoCodec=t[2],i.videoObjectTypeIndicator=t[3]),i.audioProfile=/(^|\s|,)+mp4a.[0-9A-Fa-f]+\.([0-9A-Fa-f]+)/i.exec(e),i.audioProfile=i.audioProfile&&i.audioProfile[2],i},ll=function(t,e,i){return t+"/"+e+'; codecs="'+i.filter(function(t){return!!t}).join(", ")+'"'},cl=function(t,e){var i,r,n=(i=e).segments&&i.segments.length&&i.segments[0].map?"mp4":"mp2t",s=(r=e.attributes||{}).CODECS?ul(r.CODECS):ol,a=e.attributes||{},o=!0,u=!1;if(!e)return[];if(t.mediaGroups.AUDIO&&a.AUDIO){var l=t.mediaGroups.AUDIO[a.AUDIO];if(l)for(var c in o=!(u=!0),l)if(!l[c].uri&&!l[c].playlists){o=!0;break}}u&&!s.audioProfile&&(o||(s.audioProfile=function(t,e){if(!t.mediaGroups.AUDIO||!e)return null;var i=t.mediaGroups.AUDIO[e];if(!i)return null;for(var r in i){var n=i[r];if(n.default&&n.playlists)return ul(n.playlists[0].attributes.CODECS).audioProfile}return null}(t,a.AUDIO)),s.audioProfile||(As.log.warn("Multiple audio tracks present but no audio codec string is specified. Attempting to use the default audio codec (mp4a.40.2)"),s.audioProfile=ol.audioProfile));var h={};s.videoCodec&&(h.video=""+s.videoCodec+s.videoObjectTypeIndicator),s.audioProfile&&(h.audio="mp4a.40."+s.audioProfile);var d=ll("audio",n,[h.audio]),p=ll("video",n,[h.video]),f=ll("video",n,[h.video,h.audio]);return u?!o&&h.video?[p,d]:o||h.video?[f,d]:[d,d]:h.video?[f]:[d]},hl=function(t,e){var i;return t&&(i=g.getComputedStyle(t))?i[e]:""},dl=function(t,r){var n=t.slice();t.sort(function(t,e){var i=r(t,e);return 0===i?n.indexOf(t)-n.indexOf(e):i})},pl=function(t,e){var i=void 0,r=void 0;return t.attributes.BANDWIDTH&&(i=t.attributes.BANDWIDTH),i=i||g.Number.MAX_VALUE,e.attributes.BANDWIDTH&&(r=e.attributes.BANDWIDTH),i-(r=r||g.Number.MAX_VALUE)},fl=function(t,e,i){if(!t||!e)return!1;var r=i===t.segments.length;return t.endList&&"open"===e.readyState&&r},ml=function(t){return"number"==typeof t&&isFinite(t)},gl=function(i){function r(t){y(this,r);var e=b(this,i.call(this));if(!t)throw new TypeError("Initialization settings are required");if("function"!=typeof t.currentTime)throw new TypeError("No currentTime getter specified");if(!t.mediaSource)throw new TypeError("No MediaSource specified");return e.bandwidth=t.bandwidth,e.throughput={rate:0,count:0},e.roundTrip=NaN,e.resetStats_(),e.mediaIndex=null,e.hasPlayed_=t.hasPlayed,e.currentTime_=t.currentTime,e.seekable_=t.seekable,e.seeking_=t.seeking,e.duration_=t.duration,e.mediaSource_=t.mediaSource,e.hls_=t.hls,e.loaderType_=t.loaderType,e.startingMedia_=void 0,e.segmentMetadataTrack_=t.segmentMetadataTrack,e.goalBufferLength_=t.goalBufferLength,e.sourceType_=t.sourceType,e.state_="INIT",e.checkBufferTimeout_=null,e.error_=void 0,e.currentTimeline_=-1,e.pendingSegment_=null,e.mimeType_=null,e.sourceUpdater_=null,e.xhrOptions_=null,e.activeInitSegmentId_=null,e.initSegments_={},e.decrypter_=t.decrypter,e.syncController_=t.syncController,e.syncPoint_={segmentIndex:0,time:0},e.syncController_.on("syncinfoupdate",function(){return e.trigger("syncinfoupdate")}),e.mediaSource_.addEventListener("sourceopen",function(){return e.ended_=!1}),e.fetchAtBuffer_=!1,e.logger_=Yu("SegmentLoader["+e.loaderType_+"]"),Object.defineProperty(e,"state",{get:function(){return this.state_},set:function(t){t!==this.state_&&(this.logger_(this.state_+" -> "+t),this.state_=t)}}),e}return _(r,i),r.prototype.resetStats_=function(){this.mediaBytesTransferred=0,this.mediaRequests=0,this.mediaRequestsAborted=0,this.mediaRequestsTimedout=0,this.mediaRequestsErrored=0,this.mediaTransferDuration=0,this.mediaSecondsLoaded=0},r.prototype.dispose=function(){this.state="DISPOSED",this.pause(),this.abort_(),this.sourceUpdater_&&this.sourceUpdater_.dispose(),this.resetStats_()},r.prototype.abort=function(){"WAITING"===this.state?(this.abort_(),this.state="READY",this.paused()||this.monitorBuffer_()):this.pendingSegment_&&(this.pendingSegment_=null)},r.prototype.abort_=function(){this.pendingSegment_&&this.pendingSegment_.abortRequests(),this.pendingSegment_=null},r.prototype.error=function(t){return"undefined"!=typeof t&&(this.error_=t),this.pendingSegment_=null,this.error_},r.prototype.endOfStream=function(){this.ended_=!0,this.pause(),this.trigger("ended")},r.prototype.buffered_=function(){return this.sourceUpdater_?this.sourceUpdater_.buffered():As.createTimeRanges()},r.prototype.initSegment=function(t){var e=1<arguments.length&&void 0!==arguments[1]&&arguments[1];if(!t)return null;var i=hu(t),r=this.initSegments_[i];return e&&!r&&t.bytes&&(this.initSegments_[i]=r={resolvedUri:t.resolvedUri,byterange:t.byterange,bytes:t.bytes}),r||t},r.prototype.couldBeginLoading_=function(){return this.playlist_&&(this.sourceUpdater_||this.mimeType_&&"INIT"===this.state)&&!this.paused()},r.prototype.load=function(){if(this.monitorBuffer_(),this.playlist_){if(this.syncController_.setDateTimeMapping(this.playlist_),"INIT"===this.state&&this.couldBeginLoading_())return this.init_();!this.couldBeginLoading_()||"READY"!==this.state&&"INIT"!==this.state||(this.state="READY")}},r.prototype.init_=function(){return this.state="READY",this.sourceUpdater_=new Ku(this.mediaSource_,this.mimeType_,this.loaderType_,this.sourceBufferEmitter_),this.resetEverything(),this.monitorBuffer_()},r.prototype.playlist=function(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{};if(t){var i=this.playlist_,r=this.pendingSegment_;this.playlist_=t,this.xhrOptions_=e,this.hasPlayed_()||(t.syncInfo={mediaSequence:t.mediaSequence,time:0});var n=i?i.id:null;if(this.logger_("playlist update ["+n+" => "+t.id+"]"),this.trigger("syncinfoupdate"),"INIT"===this.state&&this.couldBeginLoading_())return this.init_();if(i&&i.uri===t.uri){var s=t.mediaSequence-i.mediaSequence;this.logger_("live window shift ["+s+"]"),null!==this.mediaIndex&&(this.mediaIndex-=s),r&&(r.mediaIndex-=s,0<=r.mediaIndex&&(r.segment=t.segments[r.mediaIndex])),this.syncController_.saveExpiredSegmentInfo(i,t)}else null!==this.mediaIndex&&this.resyncLoader()}},r.prototype.pause=function(){this.checkBufferTimeout_&&(g.clearTimeout(this.checkBufferTimeout_),this.checkBufferTimeout_=null)},r.prototype.paused=function(){return null===this.checkBufferTimeout_},r.prototype.mimeType=function(t,e){this.mimeType_||(this.mimeType_=t,this.sourceBufferEmitter_=e,"INIT"===this.state&&this.couldBeginLoading_()&&this.init_())},r.prototype.resetEverything=function(){this.ended_=!1,this.resetLoader(),this.remove(0,this.duration_()),this.trigger("reseteverything")},r.prototype.resetLoader=function(){this.fetchAtBuffer_=!1,this.resyncLoader()},r.prototype.resyncLoader=function(){this.mediaIndex=null,this.syncPoint_=null,this.abort()},r.prototype.remove=function(t,e){this.sourceUpdater_&&this.sourceUpdater_.remove(t,e),bu(t,e,this.segmentMetadataTrack_)},r.prototype.monitorBuffer_=function(){this.checkBufferTimeout_&&g.clearTimeout(this.checkBufferTimeout_),this.checkBufferTimeout_=g.setTimeout(this.monitorBufferTick_.bind(this),1)},r.prototype.monitorBufferTick_=function(){"READY"===this.state&&this.fillBuffer_(),this.checkBufferTimeout_&&g.clearTimeout(this.checkBufferTimeout_),this.checkBufferTimeout_=g.setTimeout(this.monitorBufferTick_.bind(this),500)},r.prototype.fillBuffer_=function(){if(!this.sourceUpdater_.updating()){this.syncPoint_||(this.syncPoint_=this.syncController_.getSyncPoint(this.playlist_,this.duration_(),this.currentTimeline_,this.currentTime_()));var t=this.checkBuffer_(this.buffered_(),this.playlist_,this.mediaIndex,this.hasPlayed_(),this.currentTime_(),this.syncPoint_);if(t)fl(this.playlist_,this.mediaSource_,t.mediaIndex)?this.endOfStream():(t.mediaIndex!==this.playlist_.segments.length-1||"ended"!==this.mediaSource_.readyState||this.seeking_())&&((t.timeline!==this.currentTimeline_||null!==t.startOfSegment&&t.startOfSegment<this.sourceUpdater_.timestampOffset())&&(this.syncController_.reset(),t.timestampOffset=t.startOfSegment),this.loadSegment_(t))}},r.prototype.checkBuffer_=function(t,e,i,r,n,s){var a=0,o=void 0;t.length&&(a=t.end(t.length-1));var u=Math.max(0,a-n);if(!e.segments.length)return null;if(u>=this.goalBufferLength_())return null;if(!r&&1<=u)return null;if(null===s)return i=this.getSyncSegmentCandidate_(e),this.generateSegmentInfo_(e,i,null,!0);if(null!==i){var l=e.segments[i];return o=l&&l.end?l.end:a,this.generateSegmentInfo_(e,i+1,o,!1)}if(this.fetchAtBuffer_){var c=nu.getMediaInfoForTime(e,a,s.segmentIndex,s.time);i=c.mediaIndex,o=c.startTime}else{var h=nu.getMediaInfoForTime(e,n,s.segmentIndex,s.time);i=h.mediaIndex,o=h.startTime}return this.generateSegmentInfo_(e,i,o,!1)},r.prototype.getSyncSegmentCandidate_=function(t){var e=this;if(-1===this.currentTimeline_)return 0;var i=t.segments.map(function(t,e){return{timeline:t.timeline,segmentIndex:e}}).filter(function(t){return t.timeline===e.currentTimeline_});return i.length?i[Math.min(i.length-1,1)].segmentIndex:Math.max(t.segments.length-1,0)},r.prototype.generateSegmentInfo_=function(t,e,i,r){if(e<0||e>=t.segments.length)return null;var n=t.segments[e];return{requestId:"segment-loader-"+Math.random(),uri:n.resolvedUri,mediaIndex:e,isSyncRequest:r,startOfSegment:i,playlist:t,bytes:null,encryptedBytes:null,timestampOffset:null,timeline:n.timeline,duration:n.duration,segment:n}},r.prototype.abortRequestEarly_=function(t){if(this.hls_.tech_.paused()||!this.xhrOptions_.timeout||!this.playlist_.attributes.BANDWIDTH)return!1;if(Date.now()-(t.firstBytesReceivedAt||Date.now())<1e3)return!1;var e=this.currentTime_(),i=t.bandwidth,r=this.pendingSegment_.duration,n=nu.estimateSegmentRequestTime(r,i,this.playlist_,t.bytesReceived),s=function(t,e){var i=2<arguments.length&&void 0!==arguments[2]?arguments[2]:1;return((t.length?t.end(t.length-1):0)-e)/i}(this.buffered_(),e,this.hls_.tech_.playbackRate())-1;if(n<=s)return!1;var a=function(t){var e=t.master,i=t.currentTime,r=t.bandwidth,n=t.duration,s=t.segmentDuration,a=t.timeUntilRebuffer,o=t.currentTimeline,u=t.syncController,l=e.playlists.filter(function(t){return!nu.isIncompatible(t)}),c=l.filter(nu.isEnabled);c.length||(c=l.filter(function(t){return!nu.isDisabled(t)}));var h=c.filter(nu.hasAttribute.bind(null,"BANDWIDTH")).map(function(t){var e=u.getSyncPoint(t,n,o,i)?1:2;return{playlist:t,rebufferingImpact:nu.estimateSegmentRequestTime(s,r,t)*e-a}}),d=h.filter(function(t){return t.rebufferingImpact<=0});return dl(d,function(t,e){return pl(e.playlist,t.playlist)}),d.length?d[0]:(dl(h,function(t,e){return t.rebufferingImpact-e.rebufferingImpact}),h[0]||null)}({master:this.hls_.playlists.master,currentTime:e,bandwidth:i,duration:this.duration_(),segmentDuration:r,timeUntilRebuffer:s,currentTimeline:this.currentTimeline_,syncController:this.syncController_});if(a){var o=n-s-a.rebufferingImpact,u=.5;return s<=fu&&(u=1),!a.playlist||a.playlist.uri===this.playlist_.uri||o<u?!1:(this.bandwidth=a.playlist.attributes.BANDWIDTH*Qu.BANDWIDTH_VARIANCE+1,this.abort(),this.trigger("earlyabort"),!0)}},r.prototype.handleProgress_=function(t,e){this.pendingSegment_&&e.requestId===this.pendingSegment_.requestId&&!this.abortRequestEarly_(e.stats)&&this.trigger("progress")},r.prototype.loadSegment_=function(t){this.state="WAITING",this.pendingSegment_=t,this.trimBackBuffer_(t),t.abortRequests=al(this.hls_.xhr,this.xhrOptions_,this.decrypter_,this.createSimplifiedSegmentObj_(t),this.handleProgress_.bind(this),this.segmentRequestFinished_.bind(this))},r.prototype.trimBackBuffer_=function(t){var e,i,r,n,s=(e=this.seekable_(),i=this.currentTime_(),r=this.playlist_.targetDuration||10,n=void 0,n=e.length&&0<e.start(0)&&e.start(0)<i?e.start(0):i-30,Math.min(n,i-r));0<s&&this.remove(0,s)},r.prototype.createSimplifiedSegmentObj_=function(t){var e=t.segment,i={resolvedUri:e.resolvedUri,byterange:e.byterange,requestId:t.requestId};if(e.key){var r=e.key.iv||new Uint32Array([0,0,0,t.mediaIndex+t.playlist.mediaSequence]);i.key={resolvedUri:e.key.resolvedUri,iv:r}}return e.map&&(i.map=this.initSegment(e.map)),i},r.prototype.segmentRequestFinished_=function(t,e){if(this.mediaRequests+=1,e.stats&&(this.mediaBytesTransferred+=e.stats.bytesReceived,this.mediaTransferDuration+=e.stats.roundTripTime),this.pendingSegment_){if(e.requestId===this.pendingSegment_.requestId){if(t)return this.pendingSegment_=null,this.state="READY",t.code===tl?void(this.mediaRequestsAborted+=1):(this.pause(),t.code===Zu?(this.mediaRequestsTimedout+=1,this.bandwidth=1,this.roundTrip=NaN,void this.trigger("bandwidthupdate")):(this.mediaRequestsErrored+=1,this.error(t),void this.trigger("error")));this.bandwidth=e.stats.bandwidth,this.roundTrip=e.stats.roundTripTime,e.map&&(e.map=this.initSegment(e.map,!0)),this.processSegmentResponse_(e)}}else this.mediaRequestsAborted+=1},r.prototype.processSegmentResponse_=function(t){var e=this.pendingSegment_;e.bytes=t.bytes,t.map&&(e.segment.map.bytes=t.map.bytes),e.endOfAllRequests=t.endOfAllRequests,this.handleSegment_()},r.prototype.handleSegment_=function(){var t=this;if(this.pendingSegment_){var e=this.pendingSegment_,i=e.segment,r=this.syncController_.probeSegmentInfo(e);"undefined"==typeof this.startingMedia_&&r&&(r.containsAudio||r.containsVideo)&&(this.startingMedia_={containsAudio:r.containsAudio,containsVideo:r.containsVideo});var n,s,a,o=(n=this.loaderType_,s=this.startingMedia_,a=r,"main"===n&&s&&a?a.containsAudio||a.containsVideo?s.containsVideo&&!a.containsVideo?"Only audio found in segment when we expected video. We can't switch to audio only from a stream that had video. To get rid of this message, please add codec information to the manifest.":!s.containsVideo&&a.containsVideo?"Video found in segment when we expected only audio. We can't switch to a stream with video from an audio only stream. To get rid of this message, please add codec information to the manifest.":null:"Neither audio nor video found in segment.":null);if(o)return this.error({message:o,blacklistDuration:1/0}),void this.trigger("error");if(e.isSyncRequest)return this.trigger("syncinfoupdate"),this.pendingSegment_=null,void(this.state="READY");null!==e.timestampOffset&&e.timestampOffset!==this.sourceUpdater_.timestampOffset()&&(this.sourceUpdater_.timestampOffset(e.timestampOffset),this.trigger("timestampoffset"));var u,l,c,h,d,p,f,m,g,y,v,_=this.syncController_.mappingForTimeline(e.timeline);if(null!==_&&this.trigger({type:"segmenttimemapping",mapping:_}),this.state="APPENDING",i.map){var b=hu(i.map);if(!this.activeInitSegmentId_||this.activeInitSegmentId_!==b){var T=this.initSegment(i.map);this.sourceUpdater_.appendBuffer(T.bytes,function(){t.activeInitSegmentId_=b})}}e.byteLength=e.bytes.byteLength,"number"==typeof i.start&&"number"==typeof i.end?this.mediaSecondsLoaded+=i.end-i.start:this.mediaSecondsLoaded+=i.duration,this.logger_((l=(u=e).segment,c=l.start,h=l.end,d=u.playlist,p=d.mediaSequence,f=d.id,m=d.segments,g=void 0===m?[]:m,y=u.mediaIndex,v=u.timeline,["appending ["+y+"] of ["+p+", "+(p+g.length)+"] from playlist ["+f+"]","["+c+" => "+h+"] in timeline ["+v+"]"].join(" "))),this.sourceUpdater_.appendBuffer(e.bytes,this.handleUpdateEnd_.bind(this))}else this.state="READY"},r.prototype.handleUpdateEnd_=function(){if(!this.pendingSegment_)return this.state="READY",void(this.paused()||this.monitorBuffer_());var t=this.pendingSegment_,e=t.segment,i=null!==this.mediaIndex;(this.pendingSegment_=null,this.recordThroughput_(t),this.addSegmentMetadataCue_(t),this.state="READY",this.mediaIndex=t.mediaIndex,this.fetchAtBuffer_=!0,this.currentTimeline_=t.timeline,this.trigger("syncinfoupdate"),e.end&&this.currentTime_()-e.end>3*t.playlist.targetDuration)?this.resetEverything():(i&&this.trigger("bandwidthupdate"),this.trigger("progress"),fl(t.playlist,this.mediaSource_,t.mediaIndex+1)&&this.endOfStream(),this.paused()||this.monitorBuffer_())},r.prototype.recordThroughput_=function(t){var e=this.throughput.rate,i=Date.now()-t.endOfAllRequests+1,r=Math.floor(t.byteLength/i*8*1e3);this.throughput.rate+=(r-e)/++this.throughput.count},r.prototype.addSegmentMetadataCue_=function(t){if(this.segmentMetadataTrack_){var e=t.segment,i=e.start,r=e.end;if(ml(i)&&ml(r)){bu(i,r,this.segmentMetadataTrack_);var n=g.WebKitDataCue||g.VTTCue,s={bandwidth:t.playlist.attributes.BANDWIDTH,resolution:t.playlist.attributes.RESOLUTION,codecs:t.playlist.attributes.CODECS,byteLength:t.byteLength,uri:t.uri,timeline:t.timeline,playlist:t.playlist.uri,start:i,end:r},a=new n(i,r,JSON.stringify(s));a.value=s,this.segmentMetadataTrack_.addCue(a)}}},r}(As.EventTarget),yl=new Uint8Array("\n\n".split("").map(function(t){return t.charCodeAt(0)})),vl=function(t){return String.fromCharCode.apply(null,t)},_l=function(r){function n(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{};y(this,n);var i=b(this,r.call(this,t,e));return i.mediaSource_=null,i.subtitlesTrack_=null,i}return _(n,r),n.prototype.buffered_=function(){if(!this.subtitlesTrack_||!this.subtitlesTrack_.cues.length)return As.createTimeRanges();var t=this.subtitlesTrack_.cues,e=t[0].startTime,i=t[t.length-1].startTime;return As.createTimeRanges([[e,i]])},n.prototype.initSegment=function(t){var e=1<arguments.length&&void 0!==arguments[1]&&arguments[1];if(!t)return null;var i=hu(t),r=this.initSegments_[i];if(e&&!r&&t.bytes){var n=yl.byteLength+t.bytes.byteLength,s=new Uint8Array(n);s.set(t.bytes),s.set(yl,t.bytes.byteLength),this.initSegments_[i]=r={resolvedUri:t.resolvedUri,byterange:t.byterange,bytes:s}}return r||t},n.prototype.couldBeginLoading_=function(){return this.playlist_&&this.subtitlesTrack_&&!this.paused()},n.prototype.init_=function(){return this.state="READY",this.resetEverything(),this.monitorBuffer_()},n.prototype.track=function(t){return"undefined"==typeof t||(this.subtitlesTrack_=t,"INIT"===this.state&&this.couldBeginLoading_()&&this.init_()),this.subtitlesTrack_},n.prototype.remove=function(t,e){bu(t,e,this.subtitlesTrack_)},n.prototype.fillBuffer_=function(){var t=this;this.syncPoint_||(this.syncPoint_=this.syncController_.getSyncPoint(this.playlist_,this.duration_(),this.currentTimeline_,this.currentTime_()));var e=this.checkBuffer_(this.buffered_(),this.playlist_,this.mediaIndex,this.hasPlayed_(),this.currentTime_(),this.syncPoint_);if(e=this.skipEmptySegments_(e)){if(null===this.syncController_.timestampOffsetForTimeline(e.timeline)){return this.syncController_.one("timestampoffset",function(){t.state="READY",t.paused()||t.monitorBuffer_()}),void(this.state="WAITING_ON_TIMELINE")}this.loadSegment_(e)}},n.prototype.skipEmptySegments_=function(t){for(;t&&t.segment.empty;)t=this.generateSegmentInfo_(t.playlist,t.mediaIndex+1,t.startOfSegment+t.duration,t.isSyncRequest);return t},n.prototype.handleSegment_=function(){var e=this;if(this.pendingSegment_&&this.subtitlesTrack_){this.state="APPENDING";var t=this.pendingSegment_,i=t.segment;if("function"!=typeof g.WebVTT&&this.subtitlesTrack_&&this.subtitlesTrack_.tech_){var r=function(){e.handleSegment_()};return this.state="WAITING_ON_VTTJS",this.subtitlesTrack_.tech_.one("vttjsloaded",r),void this.subtitlesTrack_.tech_.one("vttjserror",function(){e.subtitlesTrack_.tech_.off("vttjsloaded",r),e.error({message:"Error loading vtt.js"}),e.state="READY",e.pause(),e.trigger("error")})}i.requested=!0;try{this.parseVTTCues_(t)}catch(t){return this.error({message:t.message}),this.state="READY",this.pause(),this.trigger("error")}if(this.updateTimeMapping_(t,this.syncController_.timelines[t.timeline],this.playlist_),t.isSyncRequest)return this.trigger("syncinfoupdate"),this.pendingSegment_=null,void(this.state="READY");t.byteLength=t.bytes.byteLength,this.mediaSecondsLoaded+=i.duration,t.cues.length&&this.remove(t.cues[0].endTime,t.cues[t.cues.length-1].endTime),t.cues.forEach(function(t){e.subtitlesTrack_.addCue(t)}),this.handleUpdateEnd_()}else this.state="READY"},n.prototype.parseVTTCues_=function(e){var t=void 0,i=!1;"function"==typeof g.TextDecoder?t=new g.TextDecoder("utf8"):(t=g.WebVTT.StringDecoder(),i=!0);var r=new g.WebVTT.Parser(g,g.vttjs,t);if(e.cues=[],e.timestampmap={MPEGTS:0,LOCAL:0},r.oncue=e.cues.push.bind(e.cues),r.ontimestampmap=function(t){return e.timestampmap=t},r.onparsingerror=function(t){As.log.warn("Error encountered when parsing cues: "+t.message)},e.segment.map){var n=e.segment.map.bytes;i&&(n=vl(n)),r.parse(n)}var s=e.bytes;i&&(s=vl(s)),r.parse(s),r.flush()},n.prototype.updateTimeMapping_=function(t,e,i){var r=t.segment;if(e)if(t.cues.length){var n=t.timestampmap,s=n.MPEGTS/9e4-n.LOCAL+e.mapping;if(t.cues.forEach(function(t){t.startTime+=s,t.endTime+=s}),!i.syncInfo){var a=t.cues[0].startTime,o=t.cues[t.cues.length-1].startTime;i.syncInfo={mediaSequence:i.mediaSequence+t.mediaIndex,time:Math.min(a,o-r.duration)}}}else r.empty=!0},n}(gl),bl=function(t,e){for(var i=t.cues,r=0;r<i.length;r++){var n=i[r];if(e>=n.adStartTime&&e<=n.adEndTime)return n}return null},Tl=So,Sl=[{name:"VOD",run:function(t,e,i,r,n){if(i!==1/0){return{time:0,segmentIndex:0}}return null}},{name:"ProgramDateTime",run:function(t,e,i,r,n){if(!t.datetimeToDisplayTime)return null;var s=e.segments||[],a=null,o=null;n=n||0;for(var u=0;u<s.length;u++){var l=s[u];if(l.dateTimeObject){var c=l.dateTimeObject.getTime()/1e3+t.datetimeToDisplayTime,h=Math.abs(n-c);if(null!==o&&o<h)break;o=h,a={time:c,segmentIndex:u}}}return a}},{name:"Segment",run:function(t,e,i,r,n){var s=e.segments||[],a=null,o=null;n=n||0;for(var u=0;u<s.length;u++){var l=s[u];if(l.timeline===r&&"undefined"!=typeof l.start){var c=Math.abs(n-l.start);if(null!==o&&o<c)break;(!a||null===o||c<=o)&&(o=c,a={time:l.start,segmentIndex:u})}}return a}},{name:"Discontinuity",run:function(t,e,i,r,n){var s=null;if(n=n||0,e.discontinuityStarts&&e.discontinuityStarts.length)for(var a=null,o=0;o<e.discontinuityStarts.length;o++){var u=e.discontinuityStarts[o],l=e.discontinuitySequence+o+1,c=t.discontinuities[l];if(c){var h=Math.abs(n-c.time);if(null!==a&&a<h)break;(!s||null===a||h<=a)&&(a=h,s={time:c.time,segmentIndex:u})}}return s}},{name:"Playlist",run:function(t,e,i,r,n){return e.syncInfo?{time:e.syncInfo.time,segmentIndex:e.syncInfo.mediaSequence-e.mediaSequence}:null}}],kl=function(e){function i(){y(this,i);var t=b(this,e.call(this));return t.inspectCache_=void 0,t.timelines=[],t.discontinuities=[],t.datetimeToDisplayTime=null,t.logger_=Yu("SyncController"),t}return _(i,e),i.prototype.getSyncPoint=function(t,e,i,r){var n=this.runStrategies_(t,e,i,r);return n.length?this.selectSyncPoint_(n,{key:"time",value:r}):null},i.prototype.getExpiredTime=function(t,e){if(!t||!t.segments)return null;var i=this.runStrategies_(t,e,t.discontinuitySequence,0);if(!i.length)return null;var r=this.selectSyncPoint_(i,{key:"segmentIndex",value:0});return 0<r.segmentIndex&&(r.time*=-1),Math.abs(r.time+Go(t,r.segmentIndex,0))},i.prototype.runStrategies_=function(t,e,i,r){for(var n=[],s=0;s<Sl.length;s++){var a=Sl[s],o=a.run(this,t,e,i,r);o&&(o.strategy=a.name,n.push({strategy:a.name,syncPoint:o}))}return n},i.prototype.selectSyncPoint_=function(t,e){for(var i=t[0].syncPoint,r=Math.abs(t[0].syncPoint[e.key]-e.value),n=t[0].strategy,s=1;s<t.length;s++){var a=Math.abs(t[s].syncPoint[e.key]-e.value);a<r&&(r=a,i=t[s].syncPoint,n=t[s].strategy)}return this.logger_("syncPoint for ["+e.key+": "+e.value+"] chosen with strategy ["+n+"]: [time:"+i.time+", segmentIndex:"+i.segmentIndex+"]"),i},i.prototype.saveExpiredSegmentInfo=function(t,e){for(var i=e.mediaSequence-t.mediaSequence-1;0<=i;i--){var r=t.segments[i];if(r&&"undefined"!=typeof r.start){e.syncInfo={mediaSequence:t.mediaSequence+i,time:r.start},this.logger_("playlist refresh sync: [time:"+e.syncInfo.time+", mediaSequence: "+e.syncInfo.mediaSequence+"]"),this.trigger("syncinfoupdate");break}}},i.prototype.setDateTimeMapping=function(t){if(!this.datetimeToDisplayTime&&t.segments&&t.segments.length&&t.segments[0].dateTimeObject){var e=t.segments[0].dateTimeObject.getTime()/1e3;this.datetimeToDisplayTime=-e}},i.prototype.reset=function(){this.inspectCache_=void 0},i.prototype.probeSegmentInfo=function(t){var e=t.segment,i=t.playlist,r=void 0;return(r=e.map?this.probeMp4Segment_(t):this.probeTsSegment_(t))&&this.calculateSegmentTimeMapping_(t,r)&&(this.saveDiscontinuitySyncInfo_(t),i.syncInfo||(i.syncInfo={mediaSequence:i.mediaSequence+t.mediaIndex,time:e.start})),r},i.prototype.probeMp4Segment_=function(t){var e=t.segment,i=La(e.map.bytes),r=Pa(i,t.bytes);return null!==t.timestampOffset&&(t.timestampOffset-=r),{start:r,end:r+e.duration}},i.prototype.probeTsSegment_=function(t){var e=Tl(t.bytes,this.inspectCache_),i=void 0,r=void 0;return e?(e.video&&2===e.video.length?(this.inspectCache_=e.video[1].dts,i=e.video[0].dtsTime,r=e.video[1].dtsTime):e.audio&&2===e.audio.length&&(this.inspectCache_=e.audio[1].dts,i=e.audio[0].dtsTime,r=e.audio[1].dtsTime),{start:i,end:r,containsVideo:e.video&&2===e.video.length,containsAudio:e.audio&&2===e.audio.length}):null},i.prototype.timestampOffsetForTimeline=function(t){return"undefined"==typeof this.timelines[t]?null:this.timelines[t].time},i.prototype.mappingForTimeline=function(t){return"undefined"==typeof this.timelines[t]?null:this.timelines[t].mapping},i.prototype.calculateSegmentTimeMapping_=function(t,e){var i=t.segment,r=this.timelines[t.timeline];if(null!==t.timestampOffset)r={time:t.startOfSegment,mapping:t.startOfSegment-e.start},this.timelines[t.timeline]=r,this.trigger("timestampoffset"),this.logger_("time mapping for timeline "+t.timeline+": [time: "+r.time+"] [mapping: "+r.mapping+"]"),i.start=t.startOfSegment,i.end=e.end+r.mapping;else{if(!r)return!1;i.start=e.start+r.mapping,i.end=e.end+r.mapping}return!0},i.prototype.saveDiscontinuitySyncInfo_=function(t){var e=t.playlist,i=t.segment;if(i.discontinuity)this.discontinuities[i.timeline]={time:i.start,accuracy:0};else if(e.discontinuityStarts&&e.discontinuityStarts.length)for(var r=0;r<e.discontinuityStarts.length;r++){var n=e.discontinuityStarts[r],s=e.discontinuitySequence+r+1,a=n-t.mediaIndex,o=Math.abs(a);if(!this.discontinuities[s]||this.discontinuities[s].accuracy>o){var u=void 0;u=a<0?i.start-Go(e,t.mediaIndex,n):i.end+Go(e,t.mediaIndex+1,n),this.discontinuities[s]={time:u,accuracy:o}}}},i}(As.EventTarget),Cl=new Pu("./decrypter-worker.worker.js",function(t,e){var i,s,h,r,n,d,p,g,a,l,y,o,u=this;i="undefined"!=typeof t?t:"undefined"!=typeof global?global:"undefined"!=typeof u?u:{},s="undefined"!=typeof t?t:"undefined"!=typeof i?i:"undefined"!=typeof u?u:{},h=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},r=function(){function r(t,e){for(var i=0;i<e.length;i++){var r=e[i];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}return function(t,e,i){return e&&r(t.prototype,e),i&&r(t,i),t}}(),n=function(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!==("undefined"==typeof e?"undefined":v(e))&&"function"!=typeof e?t:e},d=function(){var t=[[[],[],[],[],[]],[[],[],[],[],[]]],e=t[0],i=t[1],r=e[4],n=i[4],s=void 0,a=void 0,o=void 0,u=[],l=[],c=void 0,h=void 0,d=void 0,p=void 0,f=void 0;for(s=0;s<256;s++)l[(u[s]=s<<1^283*(s>>7))^s]=s;for(a=o=0;!r[a];a^=c||1,o=l[o]||1)for(d=(d=o^o<<1^o<<2^o<<3^o<<4)>>8^255&d^99,f=16843009*u[h=u[c=u[n[r[a]=d]=a]]]^65537*h^257*c^16843008*a,p=257*u[d]^16843008*d,s=0;s<4;s++)e[s][a]=p=p<<24^p>>>8,i[s][d]=f=f<<24^f>>>8;for(s=0;s<5;s++)e[s]=e[s].slice(0),i[s]=i[s].slice(0);return t},p=null,g=function(){function c(t){h(this,c),p||(p=d()),this._tables=[[p[0][0].slice(),p[0][1].slice(),p[0][2].slice(),p[0][3].slice(),p[0][4].slice()],[p[1][0].slice(),p[1][1].slice(),p[1][2].slice(),p[1][3].slice(),p[1][4].slice()]];var e=void 0,i=void 0,r=void 0,n=void 0,s=void 0,a=this._tables[0][4],o=this._tables[1],u=t.length,l=1;if(4!==u&&6!==u&&8!==u)throw new Error("Invalid aes key size");for(n=t.slice(0),s=[],this._key=[n,s],e=u;e<4*u+28;e++)r=n[e-1],(e%u==0||8===u&&e%u==4)&&(r=a[r>>>24]<<24^a[r>>16&255]<<16^a[r>>8&255]<<8^a[255&r],e%u==0&&(r=r<<8^r>>>24^l<<24,l=l<<1^283*(l>>7))),n[e]=n[e-u]^r;for(i=0;e;i++,e--)r=n[3&i?e:e-4],s[i]=e<=4||i<4?r:o[0][a[r>>>24]]^o[1][a[r>>16&255]]^o[2][a[r>>8&255]]^o[3][a[255&r]]}return c.prototype.decrypt=function(t,e,i,r,n,s){var a=this._key[1],o=t^a[0],u=r^a[1],l=i^a[2],c=e^a[3],h=void 0,d=void 0,p=void 0,f=a.length/4-2,m=void 0,g=4,y=this._tables[1],v=y[0],_=y[1],b=y[2],T=y[3],S=y[4];for(m=0;m<f;m++)h=v[o>>>24]^_[u>>16&255]^b[l>>8&255]^T[255&c]^a[g],d=v[u>>>24]^_[l>>16&255]^b[c>>8&255]^T[255&o]^a[g+1],p=v[l>>>24]^_[c>>16&255]^b[o>>8&255]^T[255&u]^a[g+2],c=v[c>>>24]^_[o>>16&255]^b[u>>8&255]^T[255&l]^a[g+3],g+=4,o=h,u=d,l=p;for(m=0;m<4;m++)n[(3&-m)+s]=S[o>>>24]<<24^S[u>>16&255]<<16^S[l>>8&255]<<8^S[255&c]^a[g++],h=o,o=u,u=l,l=c,c=h},c}(),a=function(){function t(){h(this,t),this.listeners={}}return t.prototype.on=function(t,e){this.listeners[t]||(this.listeners[t]=[]),this.listeners[t].push(e)},t.prototype.off=function(t,e){if(!this.listeners[t])return!1;var i=this.listeners[t].indexOf(e);return this.listeners[t].splice(i,1),-1<i},t.prototype.trigger=function(t){var e=this.listeners[t];if(e)if(2===arguments.length)for(var i=e.length,r=0;r<i;++r)e[r].call(this,arguments[1]);else for(var n=Array.prototype.slice.call(arguments,1),s=e.length,a=0;a<s;++a)e[a].apply(this,n)},t.prototype.dispose=function(){this.listeners={}},t.prototype.pipe=function(e){this.on("data",function(t){e.push(t)})},t}(),l=function(e){function i(){h(this,i);var t=n(this,e.call(this,a));return t.jobs=[],t.delay=1,t.timeout_=null,t}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+("undefined"==typeof e?"undefined":v(e)));t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(i,e),i.prototype.processJob_=function(){this.jobs.shift()(),this.jobs.length?this.timeout_=setTimeout(this.processJob_.bind(this),this.delay):this.timeout_=null},i.prototype.push=function(t){this.jobs.push(t),this.timeout_||(this.timeout_=setTimeout(this.processJob_.bind(this),this.delay))},i}(a),y=function(t){return t<<24|(65280&t)<<8|(16711680&t)>>8|t>>>24},o=function(){function u(t,e,i,r){h(this,u);var n=u.STEP,s=new Int32Array(t.buffer),a=new Uint8Array(t.byteLength),o=0;for(this.asyncStream_=new l,this.asyncStream_.push(this.decryptChunk_(s.subarray(o,o+n),e,i,a)),o=n;o<s.length;o+=n)i=new Uint32Array([y(s[o-4]),y(s[o-3]),y(s[o-2]),y(s[o-1])]),this.asyncStream_.push(this.decryptChunk_(s.subarray(o,o+n),e,i,a));this.asyncStream_.push(function(){var t;r(null,(t=a).subarray(0,t.byteLength-t[t.byteLength-1]))})}return u.prototype.decryptChunk_=function(e,i,r,n){return function(){var t=function(t,e,i){var r=new Int32Array(t.buffer,t.byteOffset,t.byteLength>>2),n=new g(Array.prototype.slice.call(e)),s=new Uint8Array(t.byteLength),a=new Int32Array(s.buffer),o=void 0,u=void 0,l=void 0,c=void 0,h=void 0,d=void 0,p=void 0,f=void 0,m=void 0;for(o=i[0],u=i[1],l=i[2],c=i[3],m=0;m<r.length;m+=4)h=y(r[m]),d=y(r[m+1]),p=y(r[m+2]),f=y(r[m+3]),n.decrypt(h,d,p,f,a,m),a[m]=y(a[m]^o),a[m+1]=y(a[m+1]^u),a[m+2]=y(a[m+2]^l),a[m+3]=y(a[m+3]^c),o=h,u=d,l=p,c=f;return s}(e,i,r);n.set(t,e.byteOffset)}},r(u,null,[{key:"STEP",get:function(){return 32e3}}]),u}(),new function(t){t.onmessage=function(t){var n=t.data,e=new Uint8Array(n.encrypted.bytes,n.encrypted.byteOffset,n.encrypted.byteLength),i=new Uint32Array(n.key.bytes,n.key.byteOffset,n.key.byteLength/4),r=new Uint32Array(n.iv.bytes,n.iv.byteOffset,n.iv.byteLength/4);new o(e,i,r,function(t,e){var i,r;s.postMessage((i={source:n.source,decrypted:e},r={},Object.keys(i).forEach(function(t){var e=i[t];ArrayBuffer.isView(e)?r[t]={bytes:e.buffer,byteOffset:e.byteOffset,byteLength:e.byteLength}:r[t]=e}),r),[e.buffer])})}}(u)}),El=function(t,e){t.abort(),t.pause(),e&&e.activePlaylistLoader&&(e.activePlaylistLoader.pause(),e.activePlaylistLoader=null)},wl=function(t,e){(e.activePlaylistLoader=t).load()},Al={AUDIO:function(u,l){return function(){var t=l.segmentLoaders[u],e=l.mediaTypes[u],i=l.blacklistCurrentPlaylist;El(t,e);var r=e.activeTrack(),n=e.activeGroup(),s=(n.filter(function(t){return t.default})[0]||n[0]).id,a=e.tracks[s];if(r!==a){for(var o in As.log.warn("Problem encountered loading the alternate audio track.Switching back to default."),e.tracks)e.tracks[o].enabled=e.tracks[o]===a;e.onTrackChanged()}else i({message:"Problem encountered loading the default audio track."})}},SUBTITLES:function(r,n){return function(){var t=n.segmentLoaders[r],e=n.mediaTypes[r];As.log.warn("Problem encountered loading the subtitle track.Disabling subtitle track."),El(t,e);var i=e.activeTrack();i&&(i.mode="disabled"),e.onTrackChanged()}}},Ll={AUDIO:function(t,e,i){if(e){var r=i.tech,n=i.requestOptions,s=i.segmentLoaders[t];e.on("loadedmetadata",function(){var t=e.media();s.playlist(t,n),(!r.paused()||t.endList&&"none"!==r.preload())&&s.load()}),e.on("loadedplaylist",function(){s.playlist(e.media(),n),r.paused()||s.load()}),e.on("error",Al[t](t,i))}},SUBTITLES:function(t,e,i){var r=i.tech,n=i.requestOptions,s=i.segmentLoaders[t],a=i.mediaTypes[t];e.on("loadedmetadata",function(){var t=e.media();s.playlist(t,n),s.track(a.activeTrack()),(!r.paused()||t.endList&&"none"!==r.preload())&&s.load()}),e.on("loadedplaylist",function(){s.playlist(e.media(),n),r.paused()||s.load()}),e.on("error",Al[t](t,i))}},Pl=function(e,i){return function(t){return t.attributes[e]===i}},Ol=function(e){return function(t){return t.resolvedUri===e}},xl={AUDIO:function(t,e){var i,r,n=e.hls,s=e.sourceType,a=e.segmentLoaders[t],o=e.requestOptions.withCredentials,u=e.master,l=u.mediaGroups,c=u.playlists,h=e.mediaTypes[t],d=h.groups,p=h.tracks,f=e.masterPlaylistLoader;for(var m in l[t]&&0!==Object.keys(l[t]).length||(l[t]={main:{default:{default:!0}}}),l[t]){d[m]||(d[m]=[]);var g=c.filter(Pl(t,m));for(var y in l[t][m]){var v=l[t][m][y];g.filter(Ol(v.resolvedUri)).length&&delete v.resolvedUri;var _=void 0;if(_=v.resolvedUri?new Vo(v.resolvedUri,n,o):v.playlists&&"dash"===s?new Xu(v.playlists[0],n,o,f):null,v=As.mergeOptions({id:y,playlistLoader:_},v),Ll[t](t,v.playlistLoader,e),d[m].push(v),"undefined"==typeof p[y]){var b=new As.AudioTrack({id:y,kind:(i=v,r=void 0,r=i.default?"main":"alternative",i.characteristics&&0<=i.characteristics.indexOf("public.accessibility.describes-video")&&(r="main-desc"),r),enabled:!1,language:v.language,default:v.default,label:y});p[y]=b}}}a.on("error",Al[t](t,e))},SUBTITLES:function(t,e){var i=e.tech,r=e.hls,n=e.sourceType,s=e.segmentLoaders[t],a=e.requestOptions.withCredentials,o=e.master.mediaGroups,u=e.mediaTypes[t],l=u.groups,c=u.tracks,h=e.masterPlaylistLoader;for(var d in o[t])for(var p in l[d]||(l[d]=[]),o[t][d])if(!o[t][d][p].forced){var f=o[t][d][p],m=void 0;if("hls"===n?m=new Vo(f.resolvedUri,r,a):"dash"===n&&(m=new Xu(f.playlists[0],r,a,h)),f=As.mergeOptions({id:p,playlistLoader:m},f),Ll[t](t,f.playlistLoader,e),l[d].push(f),"undefined"==typeof c[p]){var g=i.addRemoteTextTrack({id:p,kind:"subtitles",enabled:!1,language:f.language,label:p},!1).track;c[p]=g}}s.on("error",Al[t](t,e))},"CLOSED-CAPTIONS":function(t,e){var i=e.tech,r=e.master.mediaGroups,n=e.mediaTypes[t],s=n.groups,a=n.tracks;for(var o in r[t])for(var u in s[o]||(s[o]=[]),r[t][o]){var l=r[t][o][u];if(l.instreamId.match(/CC\d/)&&(s[o].push(As.mergeOptions({id:u},l)),"undefined"==typeof a[u])){var c=i.addRemoteTextTrack({id:l.instreamId,kind:"captions",enabled:!1,language:l.language,label:u},!1).track;a[u]=c}}}},Il={AUDIO:function(i,r){return function(){var t=r.mediaTypes[i].tracks;for(var e in t)if(t[e].enabled)return t[e];return null}},SUBTITLES:function(i,r){return function(){var t=r.mediaTypes[i].tracks;for(var e in t)if("showing"===t[e].mode)return t[e];return null}}},Dl=function(e){["AUDIO","SUBTITLES","CLOSED-CAPTIONS"].forEach(function(t){xl[t](t,e)});var i=e.mediaTypes,t=e.masterPlaylistLoader,r=e.tech,n=e.hls;["AUDIO","SUBTITLES"].forEach(function(t){var s,a,o,u,l,c;i[t].activeGroup=(s=t,a=e,function(e){var t=a.masterPlaylistLoader,i=a.mediaTypes[s].groups,r=t.media();if(!r)return null;var n=null;return r.attributes[s]&&(n=i[r.attributes[s]]),n=n||i.main,"undefined"==typeof e?n:null===e?null:n.filter(function(t){return t.id===e.id})[0]||null}),i[t].activeTrack=Il[t](t,e),i[t].onGroupChanged=(o=t,u=e,function(){var t=u.segmentLoaders,e=t[o],i=t.main,r=u.mediaTypes[o],n=r.activeTrack(),s=r.activeGroup(n),a=r.activePlaylistLoader;El(e,r),s&&(s.playlistLoader?(e.resyncLoader(),wl(s.playlistLoader,r)):a&&i.resetEverything())}),i[t].onTrackChanged=(l=t,c=e,function(){var t=c.segmentLoaders,e=t[l],i=t.main,r=c.mediaTypes[l],n=r.activeTrack(),s=r.activeGroup(n),a=r.activePlaylistLoader;El(e,r),s&&(s.playlistLoader?(a!==s.playlistLoader&&(e.track&&e.track(n),e.resetEverything()),wl(s.playlistLoader,r)):i.resetEverything())})});var s=i.AUDIO.activeGroup(),a=(s.filter(function(t){return t.default})[0]||s[0]).id;i.AUDIO.tracks[a].enabled=!0,i.AUDIO.onTrackChanged(),t.on("mediachange",function(){["AUDIO","SUBTITLES"].forEach(function(t){return i[t].onGroupChanged()})});var o=function(){i.AUDIO.onTrackChanged(),r.trigger({type:"usage",name:"hls-audio-change"})};for(var u in r.audioTracks().addEventListener("change",o),r.remoteTextTracks().addEventListener("change",i.SUBTITLES.onTrackChanged),n.on("dispose",function(){r.audioTracks().removeEventListener("change",o),r.remoteTextTracks().removeEventListener("change",i.SUBTITLES.onTrackChanged)}),r.clearTracks("audio"),i.AUDIO.tracks)r.audioTracks().addTrack(i.AUDIO.tracks[u])},Rl=function(){var e={};return["AUDIO","SUBTITLES","CLOSED-CAPTIONS"].forEach(function(t){e[t]={groups:{},tracks:{},activePlaylistLoader:null,activeGroup:$u,activeTrack:$u,onGroupChanged:$u,onTrackChanged:$u}}),e},Ml=void 0,Ul=["mediaRequests","mediaRequestsAborted","mediaRequestsTimedout","mediaRequestsErrored","mediaTransferDuration","mediaBytesTransferred"],Nl=function(t){return this.audioSegmentLoader_[t]+this.mainSegmentLoader_[t]},Bl=function(d){function p(t){y(this,p);var e=b(this,d.call(this)),i=t.url,r=t.withCredentials,n=t.tech,s=t.bandwidth,a=t.externHls,o=t.useCueTags,u=t.blacklistDuration,l=t.enableLowInitialPlaylist,c=t.sourceType;if(!i)throw new Error("A non-empty playlist URL is required");Ml=a,e.withCredentials=r,e.tech_=n,e.hls_=n.hls,e.sourceType_=c,e.useCueTags_=o,e.blacklistDuration=u,e.enableLowInitialPlaylist=l,e.useCueTags_&&(e.cueTagsTrack_=e.tech_.addTextTrack("metadata","ad-cues"),e.cueTagsTrack_.inBandMetadataTrackDispatchType=""),e.requestOptions_={withCredentials:e.withCredentials,timeout:null},e.mediaTypes_=Rl(),e.mediaSource=new As.MediaSource,e.mediaSource.addEventListener("sourceopen",e.handleSourceOpen_.bind(e)),e.seekable_=As.createTimeRanges(),e.hasPlayed_=function(){return!1},e.syncController_=new kl(t),e.segmentMetadataTrack_=n.addRemoteTextTrack({kind:"metadata",label:"segment-metadata"},!1).track,e.decrypter_=new Cl;var h={hls:e.hls_,mediaSource:e.mediaSource,currentTime:e.tech_.currentTime.bind(e.tech_),seekable:function(){return e.seekable()},seeking:function(){return e.tech_.seeking()},duration:function(){return e.mediaSource.duration},hasPlayed:function(){return e.hasPlayed_()},goalBufferLength:function(){return e.goalBufferLength()},bandwidth:s,syncController:e.syncController_,decrypter:e.decrypter_,sourceType:e.sourceType_};return e.masterPlaylistLoader_="dash"===e.sourceType_?new Xu(i,e.hls_,e.withCredentials):new Vo(i,e.hls_,e.withCredentials),e.setupMasterPlaylistLoaderListeners_(),e.mainSegmentLoader_=new gl(As.mergeOptions(h,{segmentMetadataTrack:e.segmentMetadataTrack_,loaderType:"main"}),t),e.audioSegmentLoader_=new gl(As.mergeOptions(h,{loaderType:"audio"}),t),e.subtitleSegmentLoader_=new _l(As.mergeOptions(h,{loaderType:"vtt"}),t),e.setupSegmentLoaderListeners_(),Ul.forEach(function(t){e[t+"_"]=Nl.bind(e,t)}),e.logger_=Yu("MPC"),e.masterPlaylistLoader_.load(),e}return _(p,d),p.prototype.setupMasterPlaylistLoaderListeners_=function(){var r=this;this.masterPlaylistLoader_.on("loadedmetadata",function(){var t=r.masterPlaylistLoader_.media(),e=1.5*r.masterPlaylistLoader_.targetDuration*1e3;ru(r.masterPlaylistLoader_.master,r.masterPlaylistLoader_.media())?r.requestOptions_.timeout=0:r.requestOptions_.timeout=e,t.endList&&"none"!==r.tech_.preload()&&(r.mainSegmentLoader_.playlist(t,r.requestOptions_),r.mainSegmentLoader_.load()),Dl({sourceType:r.sourceType_,segmentLoaders:{AUDIO:r.audioSegmentLoader_,SUBTITLES:r.subtitleSegmentLoader_,main:r.mainSegmentLoader_},tech:r.tech_,requestOptions:r.requestOptions_,masterPlaylistLoader:r.masterPlaylistLoader_,hls:r.hls_,master:r.master(),mediaTypes:r.mediaTypes_,blacklistCurrentPlaylist:r.blacklistCurrentPlaylist.bind(r)}),r.triggerPresenceUsage_(r.master(),t);try{r.setupSourceBuffers_()}catch(t){return As.log.warn("Failed to create SourceBuffers",t),r.mediaSource.endOfStream("decode")}r.setupFirstPlay(),r.trigger("selectedinitialmedia")}),this.masterPlaylistLoader_.on("loadedplaylist",function(){var t=r.masterPlaylistLoader_.media();if(!t){r.excludeUnsupportedVariants_();var e=void 0;return r.enableLowInitialPlaylist&&(e=r.selectInitialPlaylist()),e||(e=r.selectPlaylist()),r.initialMedia_=e,void r.masterPlaylistLoader_.media(r.initialMedia_)}if(r.useCueTags_&&r.updateAdCues_(t),r.mainSegmentLoader_.playlist(t,r.requestOptions_),r.updateDuration(),r.tech_.paused()||r.mainSegmentLoader_.load(),!t.endList){var i=function(){var t=r.seekable();0!==t.length&&r.mediaSource.addSeekableRange_(t.start(0),t.end(0))};if(r.duration()!==1/0){r.tech_.one("durationchange",function t(){r.duration()===1/0?i():r.tech_.one("durationchange",t)})}else i()}}),this.masterPlaylistLoader_.on("error",function(){r.blacklistCurrentPlaylist(r.masterPlaylistLoader_.error)}),this.masterPlaylistLoader_.on("mediachanging",function(){r.mainSegmentLoader_.abort(),r.mainSegmentLoader_.pause()}),this.masterPlaylistLoader_.on("mediachange",function(){var t=r.masterPlaylistLoader_.media(),e=1.5*r.masterPlaylistLoader_.targetDuration*1e3;ru(r.masterPlaylistLoader_.master,r.masterPlaylistLoader_.media())?r.requestOptions_.timeout=0:r.requestOptions_.timeout=e,r.mainSegmentLoader_.playlist(t,r.requestOptions_),r.mainSegmentLoader_.load(),r.tech_.trigger({type:"mediachange",bubbles:!0})}),this.masterPlaylistLoader_.on("playlistunchanged",function(){var t=r.masterPlaylistLoader_.media();r.stuckAtPlaylistEnd_(t)&&(r.blacklistCurrentPlaylist({message:"Playlist no longer updating."}),r.tech_.trigger("playliststuck"))}),this.masterPlaylistLoader_.on("renditiondisabled",function(){r.tech_.trigger({type:"usage",name:"hls-rendition-disabled"})}),this.masterPlaylistLoader_.on("renditionenabled",function(){r.tech_.trigger({type:"usage",name:"hls-rendition-enabled"})})},p.prototype.triggerPresenceUsage_=function(t,e){var i=t.mediaGroups||{},r=!0,n=Object.keys(i.AUDIO);for(var s in i.AUDIO)for(var a in i.AUDIO[s]){i.AUDIO[s][a].uri||(r=!1)}r&&this.tech_.trigger({type:"usage",name:"hls-demuxed"}),Object.keys(i.SUBTITLES).length&&this.tech_.trigger({type:"usage",name:"hls-webvtt"}),Ml.Playlist.isAes(e)&&this.tech_.trigger({type:"usage",name:"hls-aes"}),Ml.Playlist.isFmp4(e)&&this.tech_.trigger({type:"usage",name:"hls-fmp4"}),n.length&&1<Object.keys(i.AUDIO[n[0]]).length&&this.tech_.trigger({type:"usage",name:"hls-alternate-audio"}),this.useCueTags_&&this.tech_.trigger({type:"usage",name:"hls-playlist-cue-tags"})},p.prototype.setupSegmentLoaderListeners_=function(){var s=this;this.mainSegmentLoader_.on("bandwidthupdate",function(){var t=s.selectPlaylist(),e=s.masterPlaylistLoader_.media(),i=s.tech_.buffered(),r=i.length?i.end(i.length-1)-s.tech_.currentTime():0,n=s.bufferLowWaterLine();(!e.endList||s.duration()<Qu.MAX_BUFFER_LOW_WATER_LINE||t.attributes.BANDWIDTH<e.attributes.BANDWIDTH||n<=r)&&s.masterPlaylistLoader_.media(t),s.tech_.trigger("bandwidthupdate")}),this.mainSegmentLoader_.on("progress",function(){s.trigger("progress")}),this.mainSegmentLoader_.on("error",function(){s.blacklistCurrentPlaylist(s.mainSegmentLoader_.error())}),this.mainSegmentLoader_.on("syncinfoupdate",function(){s.onSyncInfoUpdate_()}),this.mainSegmentLoader_.on("timestampoffset",function(){s.tech_.trigger({type:"usage",name:"hls-timestamp-offset"})}),this.audioSegmentLoader_.on("syncinfoupdate",function(){s.onSyncInfoUpdate_()}),this.mainSegmentLoader_.on("ended",function(){s.onEndOfStream()}),this.mainSegmentLoader_.on("earlyabort",function(){s.blacklistCurrentPlaylist({message:"Aborted early because there isn't enough bandwidth to complete the request without rebuffering."},120)}),this.mainSegmentLoader_.on("reseteverything",function(){s.tech_.trigger("hls-reset")}),this.mainSegmentLoader_.on("segmenttimemapping",function(t){s.tech_.trigger({type:"hls-segment-time-mapping",mapping:t.mapping})}),this.audioSegmentLoader_.on("ended",function(){s.onEndOfStream()})},p.prototype.mediaSecondsLoaded_=function(){return Math.max(this.audioSegmentLoader_.mediaSecondsLoaded+this.mainSegmentLoader_.mediaSecondsLoaded)},p.prototype.load=function(){this.mainSegmentLoader_.load(),this.mediaTypes_.AUDIO.activePlaylistLoader&&this.audioSegmentLoader_.load(),this.mediaTypes_.SUBTITLES.activePlaylistLoader&&this.subtitleSegmentLoader_.load()},p.prototype.fastQualityChange_=function(){var t=this.selectPlaylist();t!==this.masterPlaylistLoader_.media()&&(this.masterPlaylistLoader_.media(t),this.mainSegmentLoader_.resetLoader())},p.prototype.play=function(){if(!this.setupFirstPlay()){this.tech_.ended()&&this.tech_.setCurrentTime(0),this.hasPlayed_()&&this.load();var t=this.tech_.seekable();return this.tech_.duration()===1/0&&this.tech_.currentTime()<t.start(0)?this.tech_.setCurrentTime(t.end(t.length-1)):void 0}},p.prototype.setupFirstPlay=function(){var t=this,e=this.masterPlaylistLoader_.media();if(!e||this.tech_.paused()||this.hasPlayed_())return!1;if(!e.endList){var i=this.seekable();if(!i.length)return!1;if(As.browser.IE_VERSION&&0===this.tech_.readyState())return this.tech_.one("loadedmetadata",function(){t.trigger("firstplay"),t.tech_.setCurrentTime(i.end(0)),t.hasPlayed_=function(){return!0}}),!1;this.trigger("firstplay"),this.tech_.setCurrentTime(i.end(0))}return this.hasPlayed_=function(){return!0},this.load(),!0},p.prototype.handleSourceOpen_=function(){try{this.setupSourceBuffers_()}catch(t){return As.log.warn("Failed to create Source Buffers",t),this.mediaSource.endOfStream("decode")}if(this.tech_.autoplay()){var t=this.tech_.play();"undefined"!=typeof t&&"function"==typeof t.then&&t.then(null,function(t){})}this.trigger("sourceopen")},p.prototype.onEndOfStream=function(){var t=this.mainSegmentLoader_.ended_;this.mediaTypes_.AUDIO.activePlaylistLoader&&(t=!this.mainSegmentLoader_.startingMedia_||this.mainSegmentLoader_.startingMedia_.containsVideo?t&&this.audioSegmentLoader_.ended_:this.audioSegmentLoader_.ended_),t&&this.mediaSource.endOfStream()},p.prototype.stuckAtPlaylistEnd_=function(t){if(!this.seekable().length)return!1;var e=this.syncController_.getExpiredTime(t,this.mediaSource.duration);if(null===e)return!1;var i=Ml.Playlist.playlistEnd(t,e),r=this.tech_.currentTime(),n=this.tech_.buffered();if(!n.length)return i-r<=.1;var s=n.end(n.length-1);return s-r<=.1&&i-s<=.1},p.prototype.blacklistCurrentPlaylist=function(){var t,e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{},i=arguments[1],r=void 0;if(r=e.playlist||this.masterPlaylistLoader_.media(),i=i||e.blacklistDuration||this.blacklistDuration,!r){this.error=e;try{return this.mediaSource.endOfStream("network")}catch(t){return this.trigger("error")}}var n=1===this.masterPlaylistLoader_.master.playlists.filter(eu).length;return n?(As.log.warn("Problem encountered with the current HLS playlist. Trying again since it is the final playlist."),this.tech_.trigger("retryplaylist"),this.masterPlaylistLoader_.load(n)):(r.excludeUntil=Date.now()+1e3*i,this.tech_.trigger("blacklistplaylist"),this.tech_.trigger({type:"usage",name:"hls-rendition-blacklisted"}),t=this.selectPlaylist(),As.log.warn("Problem encountered with the current HLS playlist."+(e.message?" "+e.message:"")+" Switching to another playlist."),this.masterPlaylistLoader_.media(t))},p.prototype.pauseLoading=function(){this.mainSegmentLoader_.pause(),this.mediaTypes_.AUDIO.activePlaylistLoader&&this.audioSegmentLoader_.pause(),this.mediaTypes_.SUBTITLES.activePlaylistLoader&&this.subtitleSegmentLoader_.pause()},p.prototype.setCurrentTime=function(t){var e=gu(this.tech_.buffered(),t);return this.masterPlaylistLoader_&&this.masterPlaylistLoader_.media()&&this.masterPlaylistLoader_.media().segments?e&&e.length?t:(this.mainSegmentLoader_.resetEverything(),this.mainSegmentLoader_.abort(),this.mediaTypes_.AUDIO.activePlaylistLoader&&(this.audioSegmentLoader_.resetEverything(),this.audioSegmentLoader_.abort()),this.mediaTypes_.SUBTITLES.activePlaylistLoader&&(this.subtitleSegmentLoader_.resetEverything(),this.subtitleSegmentLoader_.abort()),void this.load()):0},p.prototype.duration=function(){return this.masterPlaylistLoader_?this.mediaSource?this.mediaSource.duration:Ml.Playlist.duration(this.masterPlaylistLoader_.media()):0},p.prototype.seekable=function(){return this.seekable_},p.prototype.onSyncInfoUpdate_=function(){var t=void 0,e=void 0;if(this.masterPlaylistLoader_){var i=this.masterPlaylistLoader_.media();if(i){var r=this.syncController_.getExpiredTime(i,this.mediaSource.duration);if(null!==r&&0!==(t=Ml.Playlist.seekable(i,r)).length){if(this.mediaTypes_.AUDIO.activePlaylistLoader){if(i=this.mediaTypes_.AUDIO.activePlaylistLoader.media(),null===(r=this.syncController_.getExpiredTime(i,this.mediaSource.duration)))return;if(0===(e=Ml.Playlist.seekable(i,r)).length)return}e?e.start(0)>t.end(0)||t.start(0)>e.end(0)?this.seekable_=t:this.seekable_=As.createTimeRanges([[e.start(0)>t.start(0)?e.start(0):t.start(0),e.end(0)<t.end(0)?e.end(0):t.end(0)]]):this.seekable_=t,this.logger_("seekable updated ["+vu(this.seekable_)+"]"),this.tech_.trigger("seekablechanged")}}}},p.prototype.updateDuration=function(){var e=this,t=this.mediaSource.duration,i=Ml.Playlist.duration(this.masterPlaylistLoader_.media()),r=this.tech_.buffered(),n=function t(){e.mediaSource.duration=i,e.tech_.trigger("durationchange"),e.mediaSource.removeEventListener("sourceopen",t)};0<r.length&&(i=Math.max(i,r.end(r.length-1))),t!==i&&("open"!==this.mediaSource.readyState?this.mediaSource.addEventListener("sourceopen",n):n())},p.prototype.dispose=function(){var r=this;this.decrypter_.terminate(),this.masterPlaylistLoader_.dispose(),this.mainSegmentLoader_.dispose(),["AUDIO","SUBTITLES"].forEach(function(t){var e=r.mediaTypes_[t].groups;for(var i in e)e[i].forEach(function(t){t.playlistLoader&&t.playlistLoader.dispose()})}),this.audioSegmentLoader_.dispose(),this.subtitleSegmentLoader_.dispose()},p.prototype.master=function(){return this.masterPlaylistLoader_.master},p.prototype.media=function(){return this.masterPlaylistLoader_.media()||this.initialMedia_},p.prototype.setupSourceBuffers_=function(){var t,e=this.masterPlaylistLoader_.media();if(e&&"open"===this.mediaSource.readyState){if((t=cl(this.masterPlaylistLoader_.master,e)).length<1)return this.error="No compatible SourceBuffer configuration for the variant stream:"+e.resolvedUri,this.mediaSource.endOfStream("decode");this.configureLoaderMimeTypes_(t),this.excludeIncompatibleVariants_(e)}},p.prototype.configureLoaderMimeTypes_=function(t){var e=1<t.length&&-1===t[0].indexOf(",")&&t[0]!==t[1]?new As.EventTarget:null;this.mainSegmentLoader_.mimeType(t[0],e),t[1]&&this.audioSegmentLoader_.mimeType(t[1],e)},p.prototype.excludeUnsupportedVariants_=function(){this.master().playlists.forEach(function(t){t.attributes.CODECS&&g.MediaSource&&g.MediaSource.isTypeSupported&&!g.MediaSource.isTypeSupported('video/mp4; codecs="'+t.attributes.CODECS.replace(/avc1\.(\d+)\.(\d+)/i,function(t){return Nu([t])[0]})+'"')&&(t.excludeUntil=1/0)})},p.prototype.excludeIncompatibleVariants_=function(t){var i=2,r=null,e=void 0;t.attributes.CODECS&&(e=ul(t.attributes.CODECS),r=e.videoCodec,i=e.codecCount),this.master().playlists.forEach(function(t){var e={codecCount:2,videoCodec:null};t.attributes.CODECS&&(e=ul(t.attributes.CODECS)),e.codecCount!==i&&(t.excludeUntil=1/0),e.videoCodec!==r&&(t.excludeUntil=1/0)})},p.prototype.updateAdCues_=function(t){var e=0,i=this.seekable();i.length&&(e=i.start(0)),function(t,e){var i=2<arguments.length&&void 0!==arguments[2]?arguments[2]:0;if(t.segments)for(var r=i,n=void 0,s=0;s<t.segments.length;s++){var a=t.segments[s];if(n||(n=bl(e,r+a.duration/2)),n){if("cueIn"in a){n.endTime=r,n.adEndTime=r,r+=a.duration,n=null;continue}if(r<n.endTime){r+=a.duration;continue}n.endTime+=a.duration}else if("cueOut"in a&&((n=new g.VTTCue(r,r+a.duration,a.cueOut)).adStartTime=r,n.adEndTime=r+parseFloat(a.cueOut),e.addCue(n)),"cueOutCont"in a){var o,u,l=a.cueOutCont.split("/").map(parseFloat);o=l[0],u=l[1],(n=new g.VTTCue(r,r+a.duration,"")).adStartTime=r-o,n.adEndTime=n.adStartTime+u,e.addCue(n)}r+=a.duration}}(t,this.cueTagsTrack_,e)},p.prototype.goalBufferLength=function(){var t=this.tech_.currentTime(),e=Qu.GOAL_BUFFER_LENGTH,i=Qu.GOAL_BUFFER_LENGTH_RATE,r=Math.max(e,Qu.MAX_GOAL_BUFFER_LENGTH);return Math.min(e+t*i,r)},p.prototype.bufferLowWaterLine=function(){var t=this.tech_.currentTime(),e=Qu.BUFFER_LOW_WATER_LINE,i=Qu.BUFFER_LOW_WATER_LINE_RATE,r=Math.max(e,Qu.MAX_BUFFER_LOW_WATER_LINE);return Math.min(e+t*i,r)},p}(As.EventTarget),jl=function t(e,i,r){y(this,t);var n,s,a,o=e.masterPlaylistController_.fastQualityChange_.bind(e.masterPlaylistController_);if(i.attributes.RESOLUTION){var u=i.attributes.RESOLUTION;this.width=u.width,this.height=u.height}this.bandwidth=i.attributes.BANDWIDTH,this.id=r,this.enabled=(n=e.playlists,s=i.uri,a=o,function(t){var e=n.master.playlists[s],i=tu(e),r=eu(e);return"undefined"==typeof t?r:(t?delete e.disabled:e.disabled=!0,t===r||i||(a(),t?n.trigger("renditionenabled"):n.trigger("renditiondisabled")),t)})},Fl=["seeking","seeked","pause","playing","error"],Hl=function(){function a(t){var e=this;y(this,a),this.tech_=t.tech,this.seekable=t.seekable,this.consecutiveUpdates=0,this.lastRecordedTime=null,this.timer_=null,this.checkCurrentTimeTimeout_=null,this.logger_=Yu("PlaybackWatcher"),this.logger_("initialize");var i=function(){return e.monitorCurrentTime_()},r=function(){return e.techWaiting_()},n=function(){return e.cancelTimer_()},s=function(){return e.fixesBadSeeks_()};this.tech_.on("seekablechanged",s),this.tech_.on("waiting",r),this.tech_.on(Fl,n),this.tech_.on("canplay",i),this.dispose=function(){e.logger_("dispose"),e.tech_.off("seekablechanged",s),e.tech_.off("waiting",r),e.tech_.off(Fl,n),e.tech_.off("canplay",i),e.checkCurrentTimeTimeout_&&g.clearTimeout(e.checkCurrentTimeTimeout_),e.cancelTimer_()}}return a.prototype.monitorCurrentTime_=function(){this.checkCurrentTime_(),this.checkCurrentTimeTimeout_&&g.clearTimeout(this.checkCurrentTimeTimeout_),this.checkCurrentTimeTimeout_=g.setTimeout(this.monitorCurrentTime_.bind(this),250)},a.prototype.checkCurrentTime_=function(){if(this.tech_.seeking()&&this.fixesBadSeeks_())return this.consecutiveUpdates=0,void(this.lastRecordedTime=this.tech_.currentTime());if(!this.tech_.paused()&&!this.tech_.seeking()){var t=this.tech_.currentTime(),e=this.tech_.buffered();if(this.lastRecordedTime===t&&(!e.length||t+.1>=e.end(e.length-1)))return this.techWaiting_();5<=this.consecutiveUpdates&&t===this.lastRecordedTime?(this.consecutiveUpdates++,this.waiting_()):t===this.lastRecordedTime?this.consecutiveUpdates++:(this.consecutiveUpdates=0,this.lastRecordedTime=t)}},a.prototype.cancelTimer_=function(){this.consecutiveUpdates=0,this.timer_&&(this.logger_("cancelTimer_"),clearTimeout(this.timer_)),this.timer_=null},a.prototype.fixesBadSeeks_=function(){var t=this.tech_.seeking(),e=this.seekable(),i=this.tech_.currentTime(),r=void 0;t&&this.afterSeekableWindow_(e,i)&&(r=e.end(e.length-1));t&&this.beforeSeekableWindow_(e,i)&&(r=e.start(0)+.1);return"undefined"!=typeof r&&(this.logger_("Trying to seek outside of seekable at time "+i+" with seekable range "+vu(e)+". Seeking to "+r+"."),this.tech_.setCurrentTime(r),!0)},a.prototype.waiting_=function(){if(!this.techWaiting_()){var t=this.tech_.currentTime(),e=this.tech_.buffered(),i=gu(e,t);return i.length&&t+3<=i.end(0)?(this.cancelTimer_(),this.tech_.setCurrentTime(t),this.logger_("Stopped at "+t+" while inside a buffered region ["+i.start(0)+" -> "+i.end(0)+"]. Attempting to resume playback by seeking to the current time."),void this.tech_.trigger({type:"usage",name:"hls-unknown-waiting"})):void 0}},a.prototype.techWaiting_=function(){var t=this.seekable(),e=this.tech_.currentTime();if(this.tech_.seeking()&&this.fixesBadSeeks_())return!0;if(this.tech_.seeking()||null!==this.timer_)return!0;if(this.beforeSeekableWindow_(t,e)){var i=t.end(t.length-1);return this.logger_("Fell out of live window at time "+e+". Seeking to live point (seekable end) "+i),this.cancelTimer_(),this.tech_.setCurrentTime(i),this.tech_.trigger({type:"usage",name:"hls-live-resync"}),!0}var r=this.tech_.buffered(),n=yu(r,e);if(this.videoUnderflow_(n,r,e))return this.cancelTimer_(),this.tech_.setCurrentTime(e),this.tech_.trigger({type:"usage",name:"hls-video-underflow"}),!0;if(0<n.length){var s=n.start(0)-e;return this.logger_("Stopped at "+e+", setting timer for "+s+", seeking to "+n.start(0)),this.timer_=setTimeout(this.skipTheGap_.bind(this),1e3*s,e),!0}return!1},a.prototype.afterSeekableWindow_=function(t,e){return!!t.length&&e>t.end(t.length-1)+.1},a.prototype.beforeSeekableWindow_=function(t,e){return!!(t.length&&0<t.start(0)&&e<t.start(0)-.1)},a.prototype.videoUnderflow_=function(t,e,i){if(0===t.length){var r=this.gapFromVideoUnderflow_(e,i);if(r)return this.logger_("Encountered a gap in video from "+r.start+" to "+r.end+". Seeking to current time "+i),!0}return!1},a.prototype.skipTheGap_=function(t){var e=this.tech_.buffered(),i=this.tech_.currentTime(),r=yu(e,i);this.cancelTimer_(),0!==r.length&&i===t&&(this.logger_("skipTheGap_:","currentTime:",i,"scheduled currentTime:",t,"nextRange start:",r.start(0)),this.tech_.setCurrentTime(r.start(0)+fu),this.tech_.trigger({type:"usage",name:"hls-gap-skip"}))},a.prototype.gapFromVideoUnderflow_=function(t,e){for(var i=function(t){if(t.length<2)return As.createTimeRanges();for(var e=[],i=1;i<t.length;i++){var r=t.end(i-1),n=t.start(i);e.push([r,n])}return As.createTimeRanges(e)}(t),r=0;r<i.length;r++){var n=i.start(r),s=i.end(r);if(e-n<4&&2<e-n)return{start:n,end:s}}return null},a}(),Vl={errorInterval:30,getSource:function(t){return t(this.tech({IWillNotUseThisInPlugins:!0}).currentSource_)}},ql=function(t){!function e(i,t){var r=0,n=0,s=As.mergeOptions(Vl,t);i.ready(function(){i.trigger({type:"usage",name:"hls-error-reload-initialized"})});var a=function(){n&&i.currentTime(n)},o=function(t){null!=t&&(n=i.duration()!==1/0&&i.currentTime()||0,i.one("loadedmetadata",a),i.src(t),i.trigger({type:"usage",name:"hls-error-reload"}),i.play())},u=function(){if(Date.now()-r<1e3*s.errorInterval)i.trigger({type:"usage",name:"hls-error-reload-canceled"});else{if(s.getSource&&"function"==typeof s.getSource)return r=Date.now(),s.getSource.call(i,o);As.log.error("ERROR: reloadSourceOnError - The option getSource must be a function!")}},l=function t(){i.off("loadedmetadata",a),i.off("error",u),i.off("dispose",t)};i.on("error",u),i.on("dispose",l),i.reloadSourceOnError=function(t){l(),e(i,t)}}(this,t)},Wl={PlaylistLoader:Vo,Playlist:nu,Decrypter:Do,AsyncStream:Oo,decrypt:Io,utils:pu,STANDARD_PLAYLIST_SELECTOR:function(){return function(t,e,i,r){var n=t.playlists.map(function(t){var e,i;return e=t.attributes.RESOLUTION&&t.attributes.RESOLUTION.width,i=t.attributes.RESOLUTION&&t.attributes.RESOLUTION.height,{bandwidth:t.attributes.BANDWIDTH||g.Number.MAX_VALUE,width:e,height:i,playlist:t}});dl(n,function(t,e){return t.bandwidth-e.bandwidth});var s=(n=n.filter(function(t){return!nu.isIncompatible(t.playlist)})).filter(function(t){return nu.isEnabled(t.playlist)});s.length||(s=n.filter(function(t){return!nu.isDisabled(t.playlist)}));var a=s.filter(function(t){return t.bandwidth*Qu.BANDWIDTH_VARIANCE<e}),o=a[a.length-1],u=a.filter(function(t){return t.bandwidth===o.bandwidth})[0],l=a.filter(function(t){return t.width&&t.height});dl(l,function(t,e){return t.width-e.width});var c=l.filter(function(t){return t.width===i&&t.height===r});o=c[c.length-1];var h=c.filter(function(t){return t.bandwidth===o.bandwidth})[0],d=void 0,p=void 0,f=void 0;h||(p=(d=l.filter(function(t){return t.width>i||t.height>r})).filter(function(t){return t.width===d[0].width&&t.height===d[0].height}),o=p[p.length-1],f=p.filter(function(t){return t.bandwidth===o.bandwidth})[0]);var m=f||h||u||s[0]||n[0];return m?m.playlist:null}(this.playlists.master,this.systemBandwidth,parseInt(hl(this.tech_.el(),"width"),10),parseInt(hl(this.tech_.el(),"height"),10))},INITIAL_PLAYLIST_SELECTOR:function(){var t=this.playlists.master.playlists.filter(nu.isEnabled);return dl(t,function(t,e){return pl(t,e)}),t.filter(function(t){return ul(t.attributes.CODECS).videoCodec})[0]||null},comparePlaylistBandwidth:pl,comparePlaylistResolution:function(t,e){var i=void 0,r=void 0;return t.attributes.RESOLUTION&&t.attributes.RESOLUTION.width&&(i=t.attributes.RESOLUTION.width),i=i||g.Number.MAX_VALUE,e.attributes.RESOLUTION&&e.attributes.RESOLUTION.width&&(r=e.attributes.RESOLUTION.width),i===(r=r||g.Number.MAX_VALUE)&&t.attributes.BANDWIDTH&&e.attributes.BANDWIDTH?t.attributes.BANDWIDTH-e.attributes.BANDWIDTH:i-r},xhr:ou()};["GOAL_BUFFER_LENGTH","MAX_GOAL_BUFFER_LENGTH","GOAL_BUFFER_LENGTH_RATE","BUFFER_LOW_WATER_LINE","MAX_BUFFER_LOW_WATER_LINE","BUFFER_LOW_WATER_LINE_RATE","BANDWIDTH_VARIANCE"].forEach(function(e){Object.defineProperty(Wl,e,{get:function(){return As.log.warn("using Hls."+e+" is UNSAFE be sure you know what you are doing"),Qu[e]},set:function(t){As.log.warn("using Hls."+e+" is UNSAFE be sure you know what you are doing"),"number"!=typeof t||t<0?As.log.warn("value of Hls."+e+" must be greater than or equal to 0"):Qu[e]=t}})});var zl=function(t){if(/^(audio|video|application)\/(x-|vnd\.apple\.)?mpegurl/i.test(t))return"hls";return/^application\/dash\+xml/i.test(t)?"dash":null},Gl=function(t,e){for(var i=e.media(),r=-1,n=0;n<t.length;n++)if(t[n].id===i.uri){r=n;break}t.selectedIndex_=r,t.trigger({selectedIndex:r,type:"change"})};Wl.canPlaySource=function(){return As.log.warn("HLS is no longer a tech. Please remove it from your player's techOrder.")};var Xl=function(t){if("dash"===t.options_.sourceType){var e=As.players[t.tech_.options_.playerId];if(e.eme){var i=function(t,e,i){if(!t)return t;var r={};for(var n in t)r[n]={audioContentType:'audio/mp4; codecs="'+i.attributes.CODECS+'"',videoContentType:'video/mp4; codecs="'+e.attributes.CODECS+'"'},e.contentProtection&&e.contentProtection[n]&&e.contentProtection[n].pssh&&(r[n].pssh=e.contentProtection[n].pssh),"string"==typeof t[n]&&(r[n].url=t[n]);return As.mergeOptions(t,r)}(t.source_.keySystems,t.playlists.media(),t.masterPlaylistController_.mediaTypes_.AUDIO.activePlaylistLoader.media());i&&(e.currentSource().keySystems=i)}}};Wl.supportsNativeHls=function(){var e=p.createElement("video");if(!As.getTech("Html5").isSupported())return!1;return["application/vnd.apple.mpegurl","audio/mpegurl","audio/x-mpegurl","application/x-mpegurl","video/x-mpegurl","video/mpegurl","application/mpegurl"].some(function(t){return/maybe|probably/i.test(e.canPlayType(t))})}(),Wl.supportsNativeDash=!!As.getTech("Html5").isSupported()&&/maybe|probably/i.test(p.createElement("video").canPlayType("application/dash+xml")),Wl.supportsTypeNatively=function(t){return"hls"===t?Wl.supportsNativeHls:"dash"===t&&Wl.supportsNativeDash},Wl.isSupported=function(){return As.log.warn("HLS is no longer a tech. Please remove it from your player's techOrder.")};var Yl=function(s){function a(t,e,i){y(this,a);var r=b(this,s.call(this,e,i.hls));if(e.options_&&e.options_.playerId){var n=As(e.options_.playerId);n.hasOwnProperty("hls")||Object.defineProperty(n,"hls",{get:function(){return As.log.warn("player.hls is deprecated. Use player.tech_.hls instead."),e.trigger({type:"usage",name:"hls-player-access"}),r}}),n.vhs=r,n.dash=r}if(r.tech_=e,r.source_=t,r.stats={},r.ignoreNextSeekingEvent_=!1,r.setOptions_(),r.options_.overrideNative&&(e.featuresNativeVideoTracks||e.featuresNativeAudioTracks))throw new Error("Overriding native HLS requires emulated tracks. See https://git.io/vMpjB");return r.on(p,["fullscreenchange","webkitfullscreenchange","mozfullscreenchange","MSFullscreenChange"],function(t){var e=p.fullscreenElement||p.webkitFullscreenElement||p.mozFullScreenElement||p.msFullscreenElement;e&&e.contains(r.tech_.el())&&r.masterPlaylistController_.fastQualityChange_()}),r.on(r.tech_,"seeking",function(){this.ignoreNextSeekingEvent_?this.ignoreNextSeekingEvent_=!1:this.setCurrentTime(this.tech_.currentTime())}),r.on(r.tech_,"error",function(){this.masterPlaylistController_&&this.masterPlaylistController_.pauseLoading()}),r.on(r.tech_,"play",r.play),r}return _(a,s),a.prototype.setOptions_=function(){var e=this;this.options_.withCredentials=this.options_.withCredentials||!1,"number"!=typeof this.options_.blacklistDuration&&(this.options_.blacklistDuration=300),"number"!=typeof this.options_.bandwidth&&(this.options_.bandwidth=4194304),this.options_.enableLowInitialPlaylist=this.options_.enableLowInitialPlaylist&&4194304===this.options_.bandwidth,["withCredentials","bandwidth"].forEach(function(t){"undefined"!=typeof e.source_[t]&&(e.options_[t]=e.source_[t])}),this.bandwidth=this.options_.bandwidth},a.prototype.src=function(t,e){var r=this;t&&(this.setOptions_(),this.options_.url=this.source_.src,this.options_.tech=this.tech_,this.options_.externHls=Wl,this.options_.sourceType=zl(e),this.masterPlaylistController_=new Bl(this.options_),this.playbackWatcher_=new Hl(As.mergeOptions(this.options_,{seekable:function(){return r.seekable()}})),this.masterPlaylistController_.on("error",function(){As.players[r.tech_.options_.playerId].error(r.masterPlaylistController_.error)}),this.masterPlaylistController_.selectPlaylist=this.selectPlaylist?this.selectPlaylist.bind(this):Wl.STANDARD_PLAYLIST_SELECTOR.bind(this),this.masterPlaylistController_.selectInitialPlaylist=Wl.INITIAL_PLAYLIST_SELECTOR.bind(this),this.playlists=this.masterPlaylistController_.masterPlaylistLoader_,this.mediaSource=this.masterPlaylistController_.mediaSource,Object.defineProperties(this,{selectPlaylist:{get:function(){return this.masterPlaylistController_.selectPlaylist},set:function(t){this.masterPlaylistController_.selectPlaylist=t.bind(this)}},throughput:{get:function(){return this.masterPlaylistController_.mainSegmentLoader_.throughput.rate},set:function(t){this.masterPlaylistController_.mainSegmentLoader_.throughput.rate=t,this.masterPlaylistController_.mainSegmentLoader_.throughput.count=1}},bandwidth:{get:function(){return this.masterPlaylistController_.mainSegmentLoader_.bandwidth},set:function(t){this.masterPlaylistController_.mainSegmentLoader_.bandwidth=t,this.masterPlaylistController_.mainSegmentLoader_.throughput={rate:0,count:0}}},systemBandwidth:{get:function(){var t=1/(this.bandwidth||1),e=void 0;return e=0<this.throughput?1/this.throughput:0,Math.floor(1/(t+e))},set:function(){As.log.error('The "systemBandwidth" property is read-only')}}}),Object.defineProperties(this.stats,{bandwidth:{get:function(){return r.bandwidth||0},enumerable:!0},mediaRequests:{get:function(){return r.masterPlaylistController_.mediaRequests_()||0},enumerable:!0},mediaRequestsAborted:{get:function(){return r.masterPlaylistController_.mediaRequestsAborted_()||0},enumerable:!0},mediaRequestsTimedout:{get:function(){return r.masterPlaylistController_.mediaRequestsTimedout_()||0},enumerable:!0},mediaRequestsErrored:{get:function(){return r.masterPlaylistController_.mediaRequestsErrored_()||0},enumerable:!0},mediaTransferDuration:{get:function(){return r.masterPlaylistController_.mediaTransferDuration_()||0},enumerable:!0},mediaBytesTransferred:{get:function(){return r.masterPlaylistController_.mediaBytesTransferred_()||0},enumerable:!0},mediaSecondsLoaded:{get:function(){return r.masterPlaylistController_.mediaSecondsLoaded_()||0},enumerable:!0},buffered:{get:function(){return _u(r.tech_.buffered())},enumerable:!0},currentTime:{get:function(){return r.tech_.currentTime()},enumerable:!0},currentSource:{get:function(){return r.tech_.currentSource_},enumerable:!0},currentTech:{get:function(){return r.tech_.name_},enumerable:!0},duration:{get:function(){return r.tech_.duration()},enumerable:!0},master:{get:function(){return r.playlists.master},enumerable:!0},playerDimensions:{get:function(){return r.tech_.currentDimensions()},enumerable:!0},seekable:{get:function(){return _u(r.tech_.seekable())},enumerable:!0},timestamp:{get:function(){return Date.now()},enumerable:!0},videoPlaybackQuality:{get:function(){return r.tech_.getVideoPlaybackQuality()},enumerable:!0}}),this.tech_.one("canplay",this.masterPlaylistController_.setupFirstPlay.bind(this.masterPlaylistController_)),this.masterPlaylistController_.on("selectedinitialmedia",function(){var i,t;t=(i=r).playlists,i.representations=function(){return t.master.playlists.filter(function(t){return!tu(t)}).map(function(t,e){return new jl(i,t,t.uri)})},Xl(r)}),this.on(this.masterPlaylistController_,"progress",function(){this.tech_.trigger("progress")}),this.on(this.masterPlaylistController_,"firstplay",function(){this.ignoreNextSeekingEvent_=!0}),this.tech_.ready(function(){return r.setupQualityLevels_()}),this.tech_.el()&&this.tech_.src(As.URL.createObjectURL(this.masterPlaylistController_.mediaSource)))},a.prototype.setupQualityLevels_=function(){var i=this,t=As.players[this.tech_.options_.playerId];t&&t.qualityLevels&&(this.qualityLevels_=t.qualityLevels(),this.masterPlaylistController_.on("selectedinitialmedia",function(){var e,t;e=i.qualityLevels_,(t=i).representations().forEach(function(t){e.addQualityLevel(t)}),Gl(e,t.playlists)}),this.playlists.on("mediachange",function(){Gl(i.qualityLevels_,i.playlists)}))},a.prototype.play=function(){this.masterPlaylistController_.play()},a.prototype.setCurrentTime=function(t){this.masterPlaylistController_.setCurrentTime(t)},a.prototype.duration=function(){return this.masterPlaylistController_.duration()},a.prototype.seekable=function(){return this.masterPlaylistController_.seekable()},a.prototype.dispose=function(){this.playbackWatcher_&&this.playbackWatcher_.dispose(),this.masterPlaylistController_&&this.masterPlaylistController_.dispose(),this.qualityLevels_&&this.qualityLevels_.dispose(),s.prototype.dispose.call(this)},a}(As.getComponent("Component")),$l={name:"videojs-http-streaming",VERSION:"1.0.0",canHandleSource:function(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{},i=As.mergeOptions(As.options,e);return $l.canPlayType(t.type,i)},handleSource:function(t,e){var i=2<arguments.length&&void 0!==arguments[2]?arguments[2]:{},r=As.mergeOptions(As.options,i);return e.hls=new Yl(t,e,r),e.hls.xhr=ou(),e.hls.src(t.src,t.type),e.hls},canPlayType:function(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{},i=As.mergeOptions(As.options,e).hls.overrideNative,r=zl(t);return r&&(!Wl.supportsTypeNatively(r)||i)?"maybe":""}};return"undefined"!=typeof As.MediaSource&&"undefined"!=typeof As.URL||(As.MediaSource=qu,As.URL=Wu),qu.supportsNativeMediaSources()&&As.getTech("Html5").registerSourceHandler($l,0),As.HlsHandler=Yl,As.HlsSourceHandler=$l,As.Hls=Wl,As.use||As.registerComponent("Hls",Wl),As.options.hls=As.options.hls||{},As.registerPlugin?As.registerPlugin("reloadSourceOnError",ql):As.plugin("reloadSourceOnError",ql),As});
diff --git a/form/src/assets/play/bg.jpg b/form/src/assets/play/bg.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..701c49c4c716d9e48412993f87f47d0fdcfbbe44
Binary files /dev/null and b/form/src/assets/play/bg.jpg differ
diff --git a/form/src/assets/play/btn_left.png b/form/src/assets/play/btn_left.png
new file mode 100644
index 0000000000000000000000000000000000000000..e3428fd5bcda4bad311e87c5aa5669fdb7e96a60
Binary files /dev/null and b/form/src/assets/play/btn_left.png differ
diff --git a/form/src/assets/play/btn_right.png b/form/src/assets/play/btn_right.png
new file mode 100644
index 0000000000000000000000000000000000000000..db0f274024b4ad41d5219acf3936ec2b2f79459c
Binary files /dev/null and b/form/src/assets/play/btn_right.png differ
diff --git a/form/src/assets/play/default/audio.mp3 b/form/src/assets/play/default/audio.mp3
new file mode 100644
index 0000000000000000000000000000000000000000..66313dcf084aa98da69908cb6ee9da2d0d4a451e
Binary files /dev/null and b/form/src/assets/play/default/audio.mp3 differ
diff --git a/form/src/assets/play/default/pic.jpg b/form/src/assets/play/default/pic.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..1da3c1135cc324bf3ec67cc0c7b4c19caeef08f1
Binary files /dev/null and b/form/src/assets/play/default/pic.jpg differ
diff --git a/form/src/assets/play/music/click.mp3 b/form/src/assets/play/music/click.mp3
new file mode 100644
index 0000000000000000000000000000000000000000..f551dc50e89af2d63b79892af8bf1d61ae92f7a5
Binary files /dev/null and b/form/src/assets/play/music/click.mp3 differ
diff --git a/form/src/assets/play/text_bg.png b/form/src/assets/play/text_bg.png
new file mode 100644
index 0000000000000000000000000000000000000000..6084daeadcb2cec320328e7e1f362d104b7ffbaa
Binary files /dev/null and b/form/src/assets/play/text_bg.png differ
diff --git a/form/src/environments/environment.prod.ts b/form/src/environments/environment.prod.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3612073bc31cd4c1f5d6cbb00318521e9a61bd8a
--- /dev/null
+++ b/form/src/environments/environment.prod.ts
@@ -0,0 +1,3 @@
+export const environment = {
+  production: true
+};
diff --git a/form/src/environments/environment.ts b/form/src/environments/environment.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7b4f817adb754769ca126a939d48ac4b0850489d
--- /dev/null
+++ b/form/src/environments/environment.ts
@@ -0,0 +1,16 @@
+// This file can be replaced during build by using the `fileReplacements` array.
+// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
+// The list of file replacements can be found in `angular.json`.
+
+export const environment = {
+  production: false
+};
+
+/*
+ * For easier debugging in development mode, you can import the following file
+ * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
+ *
+ * This import should be commented out in production mode because it will have a negative impact
+ * on performance if an error is thrown.
+ */
+// import 'zone.js/dist/zone-error';  // Included with Angular CLI.
diff --git a/form/src/favicon.ico b/form/src/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..8081c7ceaf2be08bf59010158c586170d9d2d517
Binary files /dev/null and b/form/src/favicon.ico differ
diff --git a/form/src/index.html b/form/src/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..e55641675b96cbe7dae938f19f7d6ba8773e4c42
--- /dev/null
+++ b/form/src/index.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <title>NgOne</title>
+  <base href="/">
+<!--  <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">-->
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <link rel="icon" type="image/x-icon" href="favicon.ico">
+  <script type="text/javascript" src="https://staging-teach.cdn.ireadabc.com/h5template/h5-static-lib/js/air.js"></script>
+
+</head>
+<body>
+<app-root></app-root>
+</body>
+</html>
diff --git a/form/src/main.ts b/form/src/main.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c7b673cf44b388e9989fe908b78d7d73cd2e1409
--- /dev/null
+++ b/form/src/main.ts
@@ -0,0 +1,12 @@
+import { enableProdMode } from '@angular/core';
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app/app.module';
+import { environment } from './environments/environment';
+
+if (environment.production) {
+  enableProdMode();
+}
+
+platformBrowserDynamic().bootstrapModule(AppModule)
+  .catch(err => console.error(err));
diff --git a/form/src/online.html b/form/src/online.html
new file mode 100644
index 0000000000000000000000000000000000000000..91956f843fb74ef758b62c9df4a4ffa63b53137c
--- /dev/null
+++ b/form/src/online.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <title>NgOne</title>
+  <base href="/">
+<!--  <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">-->
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <link rel="icon" type="image/x-icon" href="favicon.ico">
+  <script type="text/javascript" src="https://staging-teach.cdn.ireadabc.com/h5template/h5-static-lib/js/air.js"></script>
+
+</head>
+<body>
+  Hello World!!
+</body>
+</html>
diff --git a/form/src/polyfills.ts b/form/src/polyfills.ts
new file mode 100644
index 0000000000000000000000000000000000000000..aa665d6b8740860e1d67de37a26ab362ef8ba974
--- /dev/null
+++ b/form/src/polyfills.ts
@@ -0,0 +1,63 @@
+/**
+ * This file includes polyfills needed by Angular and is loaded before the app.
+ * You can add your own extra polyfills to this file.
+ *
+ * This file is divided into 2 sections:
+ *   1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
+ *   2. Application imports. Files imported after ZoneJS that should be loaded before your main
+ *      file.
+ *
+ * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
+ * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
+ * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
+ *
+ * Learn more in https://angular.io/guide/browser-support
+ */
+
+/***************************************************************************************************
+ * BROWSER POLYFILLS
+ */
+
+/** IE10 and IE11 requires the following for NgClass support on SVG elements */
+// import 'classlist.js';  // Run `npm install --save classlist.js`.
+
+/**
+ * Web Animations `@angular/platform-browser/animations`
+ * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
+ * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
+ */
+// import 'web-animations-js';  // Run `npm install --save web-animations-js`.
+
+/**
+ * By default, zone.js will patch all possible macroTask and DomEvents
+ * user can disable parts of macroTask/DomEvents patch by setting following flags
+ * because those flags need to be set before `zone.js` being loaded, and webpack
+ * will put import in the top of bundle, so user need to create a separate file
+ * in this directory (for example: zone-flags.ts), and put the following flags
+ * into that file, and then add the following code before importing zone.js.
+ * import './zone-flags.ts';
+ *
+ * The flags allowed in zone-flags.ts are listed here.
+ *
+ * The following flags will work for all browsers.
+ *
+ * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
+ * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
+ * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
+ *
+ *  in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
+ *  with the following flag, it will bypass `zone.js` patch for IE/Edge
+ *
+ *  (window as any).__Zone_enable_cross_context_check = true;
+ *
+ */
+
+/***************************************************************************************************
+ * Zone JS is required by default for Angular itself.
+ */
+import 'zone.js/dist/zone';  // Included with Angular CLI.
+
+
+/***************************************************************************************************
+ * APPLICATION IMPORTS
+ */
diff --git a/form/src/styles.css b/form/src/styles.css
new file mode 100644
index 0000000000000000000000000000000000000000..90d4ee0072ce3fc41812f8af910219f9eea3c3de
--- /dev/null
+++ b/form/src/styles.css
@@ -0,0 +1 @@
+/* You can add global styles to this file, and also import other style files */
diff --git a/form/src/test.ts b/form/src/test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..16317897b1c50a3a71bc775a8d6429f2b4c6cf98
--- /dev/null
+++ b/form/src/test.ts
@@ -0,0 +1,20 @@
+// This file is required by karma.conf.js and loads recursively all the .spec and framework files
+
+import 'zone.js/dist/zone-testing';
+import { getTestBed } from '@angular/core/testing';
+import {
+  BrowserDynamicTestingModule,
+  platformBrowserDynamicTesting
+} from '@angular/platform-browser-dynamic/testing';
+
+declare const require: any;
+
+// First, initialize the Angular testing environment.
+getTestBed().initTestEnvironment(
+  BrowserDynamicTestingModule,
+  platformBrowserDynamicTesting()
+);
+// Then we find all the tests.
+const context = require.context('./', true, /\.spec\.ts$/);
+// And load the modules.
+context.keys().map(context);
diff --git a/form/tsconfig.app.json b/form/tsconfig.app.json
new file mode 100644
index 0000000000000000000000000000000000000000..8fe350f7afcad454efbb2db2a17b977cf9c564c6
--- /dev/null
+++ b/form/tsconfig.app.json
@@ -0,0 +1,20 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "outDir": "./out-tsc/app",
+    "types": [
+      "node"
+    ]
+  },
+  "files": [
+    "src/main.ts",
+    "src/polyfills.ts"
+  ],
+  "include": [
+    "src/**/*.ts"
+  ],
+  "exclude": [
+    "src/test.ts",
+    "src/**/*.spec.ts"
+  ]
+}
diff --git a/form/tsconfig.json b/form/tsconfig.json
new file mode 100644
index 0000000000000000000000000000000000000000..b4f6b3c22ef6509c186cb31d4e79428a309c1b22
--- /dev/null
+++ b/form/tsconfig.json
@@ -0,0 +1,27 @@
+{
+  "compileOnSave": false,
+  "compilerOptions": {
+    "baseUrl": "./",
+    "outDir": "./dist/out-tsc",
+    "sourceMap": true,
+    "declaration": false,
+    "downlevelIteration": true,
+    "experimentalDecorators": true,
+    "module": "esnext",
+    "moduleResolution": "node",
+    "emitDecoratorMetadata": true,
+    "importHelpers": true,
+    "target": "es2015",
+    "typeRoots": [
+      "node_modules/@types"
+    ],
+    "lib": [
+      "es2018",
+      "dom"
+    ]
+  },
+  "angularCompilerOptions": {
+    "fullTemplateTypeCheck": true,
+    "strictInjectionParameters": true
+  }
+}
diff --git a/form/tsconfig.spec.json b/form/tsconfig.spec.json
new file mode 100644
index 0000000000000000000000000000000000000000..6400fde7d544029c82e13d368a6b69d6664fe857
--- /dev/null
+++ b/form/tsconfig.spec.json
@@ -0,0 +1,18 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "outDir": "./out-tsc/spec",
+    "types": [
+      "jasmine",
+      "node"
+    ]
+  },
+  "files": [
+    "src/test.ts",
+    "src/polyfills.ts"
+  ],
+  "include": [
+    "src/**/*.spec.ts",
+    "src/**/*.d.ts"
+  ]
+}
diff --git a/form/tslint.json b/form/tslint.json
new file mode 100644
index 0000000000000000000000000000000000000000..4fcb4b1d413f773db7ea534e2c9d6d29436a819a
--- /dev/null
+++ b/form/tslint.json
@@ -0,0 +1,100 @@
+{
+  "extends": "tslint:recommended",
+  "rules": {
+    "array-type": false,
+    "arrow-parens": false,
+    "deprecation": {
+      "severity": "warning"
+    },
+    "component-class-suffix": true,
+    "contextual-lifecycle": true,
+    "directive-class-suffix": true,
+    "directive-selector": [
+      true,
+      "attribute",
+      "app",
+      "camelCase"
+    ],
+    "component-selector": [
+      true,
+      "element",
+      "app",
+      "kebab-case"
+    ],
+    "import-blacklist": [
+      true,
+      "rxjs/Rx"
+    ],
+    "interface-name": false,
+    "max-classes-per-file": false,
+    "max-line-length": [
+      true,
+      140
+    ],
+    "member-access": false,
+    "member-ordering": [
+      true,
+      {
+        "order": [
+          "static-field",
+          "instance-field",
+          "static-method",
+          "instance-method"
+        ]
+      }
+    ],
+    "no-consecutive-blank-lines": false,
+    "no-console": [
+      true,
+      "debug",
+      "info",
+      "time",
+      "timeEnd",
+      "trace"
+    ],
+    "no-empty": false,
+    "no-inferrable-types": [
+      true,
+      "ignore-params"
+    ],
+    "no-non-null-assertion": true,
+    "no-redundant-jsdoc": true,
+    "no-switch-case-fall-through": true,
+    "no-use-before-declare": true,
+    "no-var-requires": false,
+
+    "no-shadowed-variable": false,
+
+    "object-literal-key-quotes": [
+      true,
+      "as-needed"
+    ],
+    "object-literal-sort-keys": false,
+    "ordered-imports": false,
+    "quotemark": [
+      true,
+      "single"
+    ],
+    "trailing-comma": false,
+    "no-conflicting-lifecycle": true,
+    "no-host-metadata-property": true,
+    "no-input-rename": true,
+    "no-inputs-metadata-property": true,
+    "no-output-native": true,
+    "no-output-on-prefix": true,
+    "no-output-rename": true,
+    "no-outputs-metadata-property": true,
+    "template-banana-in-box": true,
+    "template-no-negated-async": true,
+    "use-lifecycle-interface": true,
+    "use-pipe-transform-interface": true,
+    "no-trailing-whitespace": false,
+    "variable-name": false,
+
+    "no-unused-expression": false,
+    "align": false
+  },
+  "rulesDirectory": [
+    "codelyzer"
+  ]
+}