From 6e5fc8d687b43ca60082b06ae2f5eb02b59c17f6 Mon Sep 17 00:00:00 2001
From: heff <git@heff.me>
Date: Fri, 27 Mar 2015 22:59:21 -0700
Subject: [PATCH] More build and testing cleanup. Also some reorganization.

---
 .gitignore                                |   1 +
 Gruntfile.js                              | 309 +++++++++++-------------------
 LICENSE                                   |   2 +-
 build/cdn.js                              |  62 ++++++
 build/demo-files/demo.captions.vtt        |  41 ----
 build/demo-files/demo.html                |  32 ----
 build/license-header.txt                  |  11 ++
 build/source-loader.js                    |  84 --------
 build/tasks/cdn-links.js                  |  13 ++
 build/tasks/dist-cdn.js                   |  22 +++
 build/tasks/saucelabs.js                  |  24 +++
 build/tasks/test.js                       |  63 ++++++
 build/tasks/vjs-version.js                |   7 +
 docs/examples/shared/example-captions.vtt |  41 ++++
 docs/examples/simple-embed/index.html     |  31 +++
 package.json                              |  19 +-
 sandbox/index.html.example                |  16 +-
 sandbox/language.html.example             |   4 +-
 sandbox/plugin.html.example               |   4 +-
 src/css/video-js.less                     |   2 +-
 src/js/cdn.js                             |  71 -------
 src/js/core.js                            |   8 +-
 tasks/build.js                            |  47 -----
 tasks/cdn-links.js                        |  13 --
 tasks/dist-copy.js                        |  61 ------
 tasks/next-issue.js                       |  82 --------
 tasks/saucelabs.js                        |  24 ---
 tasks/test.js                             |  63 ------
 tasks/vjs-version.js                      |   7 -
 test/api/api.js                           | 257 +++++++++++++++++++++++++
 test/es6.html                             |  97 ----------
 test/index.html                           |  97 ----------
 test/karma-qunit-shim.js                  |   2 +-
 test/karma.conf.js                        |  22 +--
 test/karma.minified.api.conf.js           |  48 -----
 test/karma.minified.conf.js               |  46 -----
 test/minified-api.html                    |  63 ------
 test/minified.html                        |  35 ----
 test/unit/api.js                          | 243 -----------------------
 test/unit/poster.js                       |   2 +-
 test/unit/test-helpers.js                 |   2 +-
 41 files changed, 682 insertions(+), 1396 deletions(-)
 create mode 100644 build/cdn.js
 delete mode 100644 build/demo-files/demo.captions.vtt
 delete mode 100644 build/demo-files/demo.html
 create mode 100644 build/license-header.txt
 delete mode 100644 build/source-loader.js
 create mode 100644 build/tasks/cdn-links.js
 create mode 100644 build/tasks/dist-cdn.js
 create mode 100644 build/tasks/saucelabs.js
 create mode 100644 build/tasks/test.js
 create mode 100644 build/tasks/vjs-version.js
 create mode 100644 docs/examples/shared/example-captions.vtt
 create mode 100644 docs/examples/simple-embed/index.html
 delete mode 100644 src/js/cdn.js
 delete mode 100644 tasks/build.js
 delete mode 100644 tasks/cdn-links.js
 delete mode 100644 tasks/dist-copy.js
 delete mode 100644 tasks/next-issue.js
 delete mode 100644 tasks/saucelabs.js
 delete mode 100644 tasks/test.js
 delete mode 100644 tasks/vjs-version.js
 create mode 100644 test/api/api.js
 delete mode 100644 test/es6.html
 delete mode 100644 test/index.html
 delete mode 100644 test/karma.minified.api.conf.js
 delete mode 100644 test/karma.minified.conf.js
 delete mode 100644 test/minified-api.html
 delete mode 100644 test/minified.html
 delete mode 100644 test/unit/api.js

diff --git a/.gitignore b/.gitignore
index 319dffe..71329df 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
 .DS_Store
 dist/*
 build/files/*
+build/temp/*
 docs/api/*
 dev.html
 projects
diff --git a/Gruntfile.js b/Gruntfile.js
index d86670d..a1cea17 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -1,53 +1,22 @@
 module.exports = function(grunt) {
-  var pkg, version, verParts, license, licenseNoVtt;
-
-  pkg = grunt.file.readJSON('package.json');
-
-  verParts = pkg.version.split('.');
-  version = {
+  var pkg = grunt.file.readJSON('package.json');
+  var license = grunt.file.read('build/license-header.txt');
+  var verParts = pkg.version.split('.');
+  var version = {
     full: pkg.version,
     major: verParts[0],
     minor: verParts[1],
     patch: verParts[2]
   };
+
   version.majorMinor = version.major + '.' + version.minor;
   grunt.vjsVersion = version;
 
-  licenseNoVtt = '/**\n'+
-  ' * @license\n'+
-  ' * Video.js '+version.full+' <http://videojs.com/>\n'+
-  ' * <%= pkg.copyright %>\n'+
-  ' * Available under Apache License Version 2.0\n'+
-  ' * <https://github.com/videojs/video.js/blob/master/LICENSE>\n'+
-  ' */\n';
-
-  license = licenseNoVtt.slice(0, -4) +
-  ' * \n'+
-  ' * Includes vtt.js <https://github.com/mozilla/vtt.js>\n'+
-  ' * Available under Apache License Version 2.0\n'+
-  ' * <https://github.com/mozilla/vtt.js/blob/master/LICENSE>\n'+
-  ' */\n';
-
-  // loading predefined source order from source-loader.js
-  // trust me, this is the easist way to do it so far
-  /*jshint undef:false, evil:true */
-  var blockSourceLoading = true;
-  var sourceFiles; // Needed to satisfy jshint
-  eval(grunt.file.read('./build/source-loader.js'));
-
-  grunt.sourceFiles = sourceFiles;
-
   // Project configuration.
   grunt.initConfig({
     pkg: pkg,
-    build: {
-      src: 'src/js/dependencies.js',
-      options: {
-        baseDir: 'src/js/'
-      }
-    },
     clean: {
-      build: ['build/files/*'],
+      build: ['build/temp/*'],
       dist: ['dist/*']
     },
     jshint: {
@@ -61,6 +30,8 @@ module.exports = function(grunt) {
     uglify: {
       options: {
         sourceMap: true,
+        sourceMapIn: 'build/temp/video.js.map',
+        sourceMapRoot: '../../src/js',
         preserveComments: 'some',
         mangle: true,
         compress: {
@@ -74,25 +45,13 @@ module.exports = function(grunt) {
           drop_console: true
         }
       },
-      source: {
+      build: {
         files: {
-          'build/files/minified.video.js': 'build/files/combined.video.js',
-          'build/files/minified.video.novtt.js': 'build/files/combined.video.novtt.js'
-        }
-      },
-      tests: {
-        files: {
-          'build/files/test.minified.video.js': ['build/files/combined.video.js', 'test/unit/*.js']
+          'build/temp/video.min.js': 'build/temp/video.js'
         }
       }
     },
     dist: {},
-    qunit: {
-      source: ['test/index.html'],
-      minified: ['test/minified.html'],
-      minified_api: ['test/minified-api.html'],
-      es6: ['test/es6.html']
-    },
     watch: {
       files: [ 'src/**/*', 'test/unit/**/*.js', 'Gruntfile.js' ],
       tasks: 'dev'
@@ -108,14 +67,20 @@ module.exports = function(grunt) {
     copy: {
       minor: {
         files: [
-          {expand: true, cwd: 'build/files/', src: ['*'], dest: 'dist/'+version.majorMinor+'/', filter: 'isFile'} // includes files in path
+          {expand: true, cwd: 'build/temp/', src: ['*'], dest: 'dist/'+version.majorMinor+'/', filter: 'isFile'} // includes files in path
         ]
       },
       patch: {
         files: [
-          {expand: true, cwd: 'build/files/', src: ['*'], dest: 'dist/'+version.full+'/', filter: 'isFile'} // includes files in path
+          {expand: true, cwd: 'build/temp/', src: ['*'], dest: 'dist/'+version.full+'/', filter: 'isFile'} // includes files in path
         ]
-      }
+      },
+      fonts: { expand: true, cwd: 'src/css/font/', src: ['*'], dest: 'build/temp/font/', filter: 'isFile' },
+      swf: { src: './node_modules/videojs-swf/dist/video-js.swf', dest: './build/temp/video-js.swf' },
+      novtt: { src: './build/temp/video.js', dest: './build/temp/alt/video.novtt.js' },
+      dist: { expand: true, cwd: 'build/temp/', src: ['**/**'], dest: 'dist/', filter: 'isFile' },
+      examples: { expand: true, cwd: 'build/examples/', src: ['**/**'], dest: 'dist/examples/', filter: 'isFile' },
+      cdn: { expand: true, cwd: 'dist/', src: ['**/**'], dest: 'dist/cdn/', filter: 'isFile' },
     },
     aws_s3: {
       options: {
@@ -172,16 +137,16 @@ module.exports = function(grunt) {
     cssmin: {
       minify: {
         expand: true,
-        cwd: 'build/files/',
+        cwd: 'build/temp/',
         src: ['video-js.css'],
-        dest: 'build/files/',
+        dest: 'build/temp/',
         ext: '.min.css'
       }
     },
     less: {
       dev: {
         files: {
-          'build/files/video-js.css': 'src/css/video-js.less'
+          'build/temp/video-js.css': 'src/css/video-js.less'
         }
       }
     },
@@ -191,115 +156,30 @@ module.exports = function(grunt) {
         configFile: 'test/karma.conf.js'
       },
 
-      // this only runs on PRs from the mainrepo on saucelabs
-      saucelabs: {
-        browsers: ['chrome_sl']
-      },
-      chrome_sl: {
-        browsers: ['chrome_sl']
-      },
-      firefox_sl: {
-        browsers: ['firefox_sl']
-      },
-      safari_sl: {
-        browsers: ['safari_sl']
-      },
-      ipad_sl: {
-        browsers: ['ipad_sl']
-      },
-      android_sl: {
-        browsers: ['android_sl']
-      },
-      ie_sl: {
-        browsers: ['ie_sl']
-      },
-
       // these are run locally on local browsers
       dev: {
         browsers: ['Chrome', 'Firefox', 'Safari']
       },
-      chromecanary: {
-        browsers: ['ChromeCanary']
-      },
-      chrome: {
-        browsers: ['Chrome']
-      },
-      firefox: {
-        browsers: ['Firefox']
-      },
-      safari: {
-        browsers: ['Safari']
-      },
-      ie: {
-        browsers: ['IE']
-      },
-      phantomjs: {
-        browsers: ['PhantomJS']
-      },
-
-      // This is all the minified tests run locally on local browsers
-      minified_dev: {
-        browsers: ['Chrome', 'Firefox', 'Safari'],
-        configFile: 'test/karma.minified.conf.js'
-      },
-      minified_chromecanary: {
-        browsers: ['ChromeCanary'],
-        configFile: 'test/karma.minified.conf.js'
-      },
-      minified_chrome: {
-        browsers: ['Chrome'],
-        configFile: 'test/karma.minified.conf.js'
-      },
-      minified_firefox: {
-        browsers: ['Firefox'],
-        configFile: 'test/karma.minified.conf.js'
-      },
-      minified_safari: {
-        browsers: ['Safari'],
-        configFile: 'test/karma.minified.conf.js'
-      },
-      minified_ie: {
-        browsers: ['IE'],
-        configFile: 'test/karma.minified.conf.js'
-      },
-      minified_phantomjs: {
-        browsers: ['PhantomJS'],
-        configFile: 'test/karma.minified.conf.js'
-      },
+      chromecanary: { browsers: ['ChromeCanary'] },
+      chrome:       { browsers: ['Chrome'] },
+      firefox:      { browsers: ['Firefox'] },
+      safari:       { browsers: ['Safari'] },
+      ie:           { browsers: ['IE'] },
+      phantomjs:    { browsers: ['PhantomJS'] },
 
-      // This is all the minified api tests run locally on local browsers
-      minified_api_dev: {
-        browsers: ['Chrome', 'Firefox', 'Safari'],
-        configFile: 'test/karma.minified.api.conf.js'
-      },
-      minified_api_chromecanary: {
-        browsers: ['ChromeCanary'],
-        configFile: 'test/karma.minified.api.conf.js'
-      },
-      minified_api_chrome: {
-        browsers: ['Chrome'],
-        configFile: 'test/karma.minified.api.conf.js'
-      },
-      minified_api_firefox: {
-        browsers: ['Firefox'],
-        configFile: 'test/karma.minified.api.conf.js'
-      },
-      minified_api_safari: {
-        browsers: ['Safari'],
-        configFile: 'test/karma.minified.api.conf.js'
-      },
-      minified_api_ie: {
-        browsers: ['IE'],
-        configFile: 'test/karma.minified.api.conf.js'
-      },
-      minified_api_phantomjs: {
-        browsers: ['PhantomJS'],
-        configFile: 'test/karma.minified.api.conf.js'
-      }
+      // this only runs on PRs from the mainrepo on saucelabs
+      saucelabs:  { browsers: ['chrome_sl'] },
+      chrome_sl:  { browsers: ['chrome_sl'] },
+      firefox_sl: { browsers: ['firefox_sl'] },
+      safari_sl:  { browsers: ['safari_sl'] },
+      ipad_sl:    { browsers: ['ipad_sl'] },
+      android_sl: { browsers: ['android_sl'] },
+      ie_sl:      { browsers: ['ie_sl'] }
     },
     vjsdocs: {
       all: {
-        src: sourceFiles,
+        // TODO: Update vjsdocs to support new build, or switch to jsdoc
+        src: '',
         dest: 'docs/api',
         options: {
           baseURL: 'https://github.com/videojs/video.js/blob/master/'
@@ -309,7 +189,7 @@ module.exports = function(grunt) {
     vjslanguages: {
       defaults: {
         files: {
-          'build/files/lang': ['lang/*.json']
+          'build/temp/lang': ['lang/*.json']
         }
       }
     },
@@ -320,33 +200,10 @@ module.exports = function(grunt) {
           return path.relative('dist', filepath);
         },
         // compression: 'DEFLATE',
-        src: ['dist/video-js/**/*'],
+        src: ['dist/**/*'],
         dest: 'dist/video-js-' + version.full + '.zip'
       }
     },
-    usebanner: {
-      options: {
-        position: 'top',
-        banner: license,
-        linebreak: true
-      },
-      dist: {
-        options: {
-          banner: license
-        },
-        files: {
-          src: [ 'build/files/combined.video.js']
-        }
-      },
-      novtt: {
-        options: {
-          banner: licenseNoVtt
-        },
-        files: {
-          src: [ 'build/files/combined.video.novtt.js']
-        }
-      }
-    },
     version: {
       options: {
         pkg: 'package.json'
@@ -368,6 +225,12 @@ module.exports = function(grunt) {
           release: 'patch'
         },
         src: ['package.json', 'bower.json', 'component.json']
+      },
+      css: {
+        options: {
+          prefix: '@version\\s*'
+        },
+        src: 'build/temp/video-js.css'
       }
     },
     'github-release': {
@@ -388,40 +251,64 @@ module.exports = function(grunt) {
       }
     },
     browserify: {
-      dist: {
+      build: {
         files: {
-          'build/files/video-es6.js': ['src/js/video.js']
+          'build/temp/video.js': ['src/js/video.js']
         },
         options: {
           browserifyOptions: {
             debug: true,
             standalone: 'videojs'
           },
+          banner: license,
           transform: [
             require('babelify').configure({
               sourceMapRelative: './src/js'
-            })
+            }),
+            ['browserify-versionify', {
+              placeholder: '__VERSION__',
+              version: pkg.version
+            }],
+            ['browserify-versionify', {
+              placeholder: '__VERSION_NO_PATCH__',
+              version: version.majorMinor
+            }]
           ]
         }
       }
     },
+    exorcise: {
+      build: {
+        options: {},
+        files: {
+          'build/temp/video.js.map': ['build/temp/video.js'],
+        }
+      }
+    },
     coveralls: {
       all: {
         src: 'test/coverage/lcov.info'
       }
+    },
+    concat: {
+      vtt: {
+        options: {
+          separator: '\n',
+        },
+        src: ['build/temp/video.js', 'node_modules/vtt.js/dist/vtt.js'],
+        dest: 'build/temp/video.js',
+      },
     }
   });
 
   grunt.loadNpmTasks('grunt-videojs-languages');
   grunt.loadNpmTasks('grunt-contrib-connect');
   grunt.loadNpmTasks('grunt-contrib-jshint');
-  grunt.loadNpmTasks('grunt-contrib-qunit');
   grunt.loadNpmTasks('grunt-contrib-watch');
   grunt.loadNpmTasks('grunt-contrib-clean');
   grunt.loadNpmTasks('grunt-contrib-copy');
   grunt.loadNpmTasks('grunt-contrib-cssmin');
   grunt.loadNpmTasks('grunt-contrib-less');
-  grunt.loadNpmTasks('contribflow');
   grunt.loadNpmTasks('grunt-karma');
   grunt.loadNpmTasks('videojs-doc-generator');
   grunt.loadNpmTasks('grunt-zip');
@@ -434,15 +321,49 @@ module.exports = function(grunt) {
   grunt.loadNpmTasks('grunt-contrib-uglify');
   grunt.loadNpmTasks('grunt-browserify');
   grunt.loadNpmTasks('grunt-coveralls');
+  grunt.loadNpmTasks('grunt-contrib-copy');
+  grunt.loadNpmTasks('grunt-contrib-concat');
+  grunt.loadNpmTasks('grunt-exorcise');
+
+  grunt.registerTask('build', [
+    'clean:build',
+    'jshint',
+    'browserify',
+    'copy:novtt',
+    'concat:vtt',
+    'exorcise',
+    'uglify',
+    'less',
+    'version:css',
+    'cssmin',
+    'copy:fonts',
+    'copy:swf',
+    'vjslanguages'
+  ]);
+
+  grunt.registerTask('dist', [
+    'clean:dist',
+    'build',
+    'copy:dist',
+    'copy:examples',
+    'zip:dist'
+  ]);
+
+  grunt.registerTask('cdn', [
+    'dist',
+    'copy:cdn',
+    'dist-cdn'
+  ]);
+
+  // Remove this and add to the test task once mmcc's coverall changes are merged
+  grunt.registerTask('newtest', ['build', 'karma:chrome']);
 
-  grunt.registerTask('pretask', ['jshint', 'less', 'vjslanguages', 'browserify', 'usebanner']);
   // Default task.
-  grunt.registerTask('default', ['pretask', 'dist']);
-  // Development watch task
-  grunt.registerTask('dev', ['jshint', 'less', 'vjslanguages', 'browserify:dist', 'usebanner', 'karma:chrome']);
+  grunt.registerTask('default', ['build', 'test']);
 
-  grunt.registerTask('dist', 'Creating distribution', ['dist-copy', 'zip:dist']);
+  // Development watch task. Doing the minimum required.
+  grunt.registerTask('dev', ['jshint', 'less', 'browserify', 'karma:chrome']);
 
   // Load all the tasks in the tasks directory
-  grunt.loadTasks('tasks');
+  grunt.loadTasks('build/tasks');
 };
diff --git a/LICENSE b/LICENSE
index 138a50e..8a7f05f 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright 2013 Brightcove, Inc.
+Copyright Brightcove, Inc.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
diff --git a/build/cdn.js b/build/cdn.js
new file mode 100644
index 0000000..b038066
--- /dev/null
+++ b/build/cdn.js
@@ -0,0 +1,62 @@
+/**
+ * Google Analytics tracking pixel for the freely hosted version of Video.js
+ * at vjs.zencdn.net. We'll use this data to develop a support matrix of
+ * browsers and devices, and possibly track errors.
+ *
+ * This code generates the GA tracking URL without requiring the GA javascript
+ * library.
+ *
+ * @type {Image}
+ */
+;(function(i,w,n,e,l){
+  l=w.location;
+
+  // Google Analytics has a limit of 10 million hits per month for free accounts.
+  // The Video.js CDN goes over this (by a lot) and they've asked us to stop.
+  if (Math.random() > 0.01) {
+    return;
+  }
+
+  // Setting the source of an image will load the URL even without adding to dom
+  // Using //www, still seems to work for https even though ssl.google is used by google
+  i.src='//www.google-analytics.com/__utm.gif'
+    // Version
+    +'?utmwv=5.4.2'
+    // ID
+    +'&utmac=UA-16505296-2'
+    // Sessions
+    // &utms=2
+    // Cache breaker (using utmcc to do this)
+    +'&utmn='+1
+    +'&utmhn='+e(l.hostname)
+    // Encoding
+    // &utmcs=UTF-8
+    +'&utmsr='+w.screen.availWidth+'x'+w.screen.availHeight
+    // Browser window
+    // &utmvp=1057x1105
+    // Color depth
+    // &utmsc=24-bit
+    +'&utmul='+(n.language||n.userLanguage||'').toLowerCase()
+    // Java
+    // &utmje=1
+    // Flash version
+    // &utmfl=11.7%20r700
+    // Page title
+    // &utmdt=HTML5%20Video%20Player%20%7C%20Video.js
+    // Adsense
+    // &utmhid=1112291628
+    // Referrer, '-' is none
+    // Using current page as referrer so stats show up cleaner than "Direct"
+    +'&utmr='+e(l.href)
+    +'&utmp='+e(l.hostname+l.pathname)
+    // Current time stamp
+    // &utmht=1370890439353
+    // ?
+    // &utmu=q
+    // Cookies! Manually setting visitor ID and setting everything else to 1
+    // Random number used as cache buster instead of utmn
+    +'&utmcc=__utma%3D1.'+Math.floor(Math.random()*1e10)+'.1.1.1.1%3B'
+    // Custom Var: vjsv is the variable name and 1.0.0 is the VJS version
+    +'&utme=8(vjsv)9(v0.0.0)'
+  ;
+})(new Image(),window,navigator,encodeURIComponent);
\ No newline at end of file
diff --git a/build/demo-files/demo.captions.vtt b/build/demo-files/demo.captions.vtt
deleted file mode 100644
index e598be1..0000000
--- a/build/demo-files/demo.captions.vtt
+++ /dev/null
@@ -1,41 +0,0 @@
-WEBVTT
-
-00:00.700 --> 00:04.110
-Captions describe all relevant audio for the hearing impaired.
-[ Heroic music playing for a seagull ]
-
-00:04.500 --> 00:05.000
-[ Splash!!! ]
-
-00:05.100 --> 00:06.000
-[ Sploosh!!! ]
-
-00:08.000 --> 00:09.225
-[ Splash...splash...splash splash splash ]
-
-00:10.525 --> 00:11.255
-[ Splash, Sploosh again ]
-
-00:13.500 --> 00:14.984
-Dolphin: eeeEEEEEeeee!
-
-00:14.984 --> 00:16.984
-Dolphin: Squawk! eeeEEE?
-
-00:25.000 --> 00:28.284
-[ A whole ton of splashes ]
-
-00:29.500 --> 00:31.000
-Mine. Mine. Mine.
-
-00:34.300 --> 00:36.000
-Shark: Chomp
-
-00:36.800 --> 00:37.900
-Shark: CHOMP!!!
-
-00:37.861 --> 00:41.193
-EEEEEEOOOOOOOOOOWHALENOISE
-
-00:42.593 --> 00:45.611
-[ BIG SPLASH ]
\ No newline at end of file
diff --git a/build/demo-files/demo.html b/build/demo-files/demo.html
deleted file mode 100644
index 8093718..0000000
--- a/build/demo-files/demo.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <title>Video.js | HTML5 Video Player</title>
-
-  <!-- Chang URLs to wherever Video.js files will be hosted -->
-  <link href="video-js.css" rel="stylesheet" type="text/css">
-  <!-- video.js must be in the <head> for older IEs to work. -->
-  <script src="video.js"></script>
-
-  <!-- Unless using the CDN hosted version, update the URL to the Flash SWF -->
-  <script>
-    videojs.options.flash.swf = "video-js.swf";
-  </script>
-
-
-</head>
-<body>
-
-  <video id="example_video_1" class="video-js vjs-default-skin" controls preload="none" width="640" height="264"
-      poster="http://video-js.zencoder.com/oceans-clip.png"
-      data-setup="{}">
-    <source src="http://video-js.zencoder.com/oceans-clip.mp4" type='video/mp4' />
-    <source src="http://video-js.zencoder.com/oceans-clip.webm" type='video/webm' />
-    <source src="http://video-js.zencoder.com/oceans-clip.ogv" type='video/ogg' />
-    <track kind="captions" src="demo.captions.vtt" srclang="en" label="English"></track><!-- Tracks need an ending tag thanks to IE9 -->
-    <track kind="subtitles" src="demo.captions.vtt" srclang="en" label="English"></track><!-- Tracks need an ending tag thanks to IE9 -->
-    <p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
-  </video>
-
-</body>
-</html>
diff --git a/build/license-header.txt b/build/license-header.txt
new file mode 100644
index 0000000..dcd361d
--- /dev/null
+++ b/build/license-header.txt
@@ -0,0 +1,11 @@
+/**
+ * @license
+ * Video.js <%= pkg.version %> <http://videojs.com/>
+ * <%= pkg.copyright %>
+ * 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>
+ */
\ No newline at end of file
diff --git a/build/source-loader.js b/build/source-loader.js
deleted file mode 100644
index 725ec84..0000000
--- a/build/source-loader.js
+++ /dev/null
@@ -1,84 +0,0 @@
-// This file is used to load the video.js source files into a page
-// in the correct order based on dependencies.
-// When you create a new source file you will need to add
-// it to the list below to use it in sandbox/index.html and
-// test/index.html
-
-// You can use the projectRoot variable to adjust relative urls
-// that this script loads. By default it's "../", which is what /sandbox
-// and /test need. If you had sandbox/newDir/index.html, in index.html you
-// would set projectRoot = "../../"
-
-// We could use somehting like requireJS to load files, and at one point
-// we used goog.require/provide to load dependencies, but that seems like
-// overkill with the small number of files we actually have.
-
-// ADD NEW SOURCE FILES HERE
-var sourceFiles = [
-  "src/js/core.js",
-  "src/js/core-object.js",
-  "src/js/events.js",
-  "src/js/lib.js",
-  "src/js/xhr.js",
-  "src/js/util.js",
-  "src/js/event-emitter.js",
-  "src/js/component.js",
-  "src/js/button.js",
-  "src/js/slider.js",
-  "src/js/menu.js",
-  "src/js/media-error.js",
-  "src/js/fullscreen-api.js",
-  "src/js/player.js",
-  "src/js/control-bar/control-bar.js",
-  "src/js/control-bar/live-display.js",
-  "src/js/control-bar/play-toggle.js",
-  "src/js/control-bar/time-display.js",
-  "src/js/control-bar/fullscreen-toggle.js",
-  "src/js/control-bar/progress-control.js",
-  "src/js/control-bar/volume-control.js",
-  "src/js/control-bar/mute-toggle.js",
-  "src/js/control-bar/volume-menu-button.js",
-  "src/js/control-bar/playback-rate-menu-button.js",
-  "src/js/poster.js",
-  "src/js/loading-spinner.js",
-  "src/js/big-play-button.js",
-  "src/js/error-display.js",
-  "src/js/media/media.js",
-  "src/js/media/html5.js",
-  "src/js/media/flash.js",
-  "src/js/media/flash-rtmp.js",
-  "src/js/media/loader.js",
-  "src/js/tracks/text-track-enums.js",
-  "src/js/tracks/text-track.js",
-  "src/js/tracks/text-track-list.js",
-  "src/js/tracks/text-track-cue-list.js",
-  "src/js/tracks/text-track-controls.js",
-  "src/js/tracks/text-track-settings.js",
-  "src/js/json.js",
-  "src/js/setup.js",
-  "src/js/plugins.js"
-];
-
-// Allow overriding the default project root
-var projectRoot = projectRoot || '../';
-
-function loadScripts(scriptsArr){
-  for (var i = 0; i < scriptsArr.length; i++) {
-    // Using document.write because that's the easiest way to avoid triggering
-    // asynchrnous script loading
-    document.write( "<script src='" + projectRoot + scriptsArr[i] + "'><\/script>" );
-  }
-}
-
-// We use this file in the grunt build script to load the same source file list
-// and don't want to load the scripts there.
-if (typeof blockSourceLoading === 'undefined') {
-  loadScripts(sourceFiles);
-
-  // Allow for making Flash first
-  if (window.location.href.indexOf("?flash") !== -1) {
-    // Using doc.write to load this script to, otherwise when it runs videojs
-    // is undefined
-    document.write('<script>videojs.options.techOrder = ["flash"];videojs.options.flash.swf = "../src/swf/video-js.swf";</script>')
-  }
-}
diff --git a/build/tasks/cdn-links.js b/build/tasks/cdn-links.js
new file mode 100644
index 0000000..699f05f
--- /dev/null
+++ b/build/tasks/cdn-links.js
@@ -0,0 +1,13 @@
+module.exports = function(grunt) {
+  grunt.registerTask('cdn-links', 'Update the version of CDN links in docs', function(){
+    var doc = grunt.file.read('docs/guides/setup.md');
+    var version = require('../package.json').version;
+
+    // remove the patch version to point to the latest patch
+    version = version.replace(/(\d+\.\d+)\.\d+/, '$1');
+
+    // update the version in http://vjs.zencdn.net/4.3/video.js
+    doc = doc.replace(/(\/\/vjs\.zencdn\.net\/)\d+\.\d+(\.\d+)?/g, '$1'+version);
+    grunt.file.write('docs/guides/setup.md', doc);
+  });
+};
diff --git a/build/tasks/dist-cdn.js b/build/tasks/dist-cdn.js
new file mode 100644
index 0000000..662954f
--- /dev/null
+++ b/build/tasks/dist-cdn.js
@@ -0,0 +1,22 @@
+var uglify = require('uglify-js');
+
+module.exports = function(grunt) {
+  grunt.registerTask('dist-cdn', 'Assembling distribution', function(){
+    var css, jsmin, jsdev, cdnjs;
+
+    // Replace font urls with CDN versions
+    css = grunt.file.read('dist/cdn/video-js.css');
+    css = css.replace(/font\//g, '../f/3/');
+    grunt.file.write('dist/cdn/video-js.css', css);
+
+    // GA Tracking Pixel (manually building the pixel URL)
+    cdnjs = '\n' + uglify.minify('build/cdn.js').code.replace('v0.0.0', 'v'+ grunt.vjsVersion.full);
+
+    // Add CDN-specfic JS
+    js = grunt.file.read('dist/cdn/video.js');
+    grunt.file.write('dist/cdn/video.js', js + cdnjs);
+
+    jsmin = grunt.file.read('dist/cdn/video.min.js');
+    grunt.file.write('dist/cdn/video.min.js', jsmin + cdnjs);
+  });
+}
diff --git a/build/tasks/saucelabs.js b/build/tasks/saucelabs.js
new file mode 100644
index 0000000..2873c86
--- /dev/null
+++ b/build/tasks/saucelabs.js
@@ -0,0 +1,24 @@
+module.exports = function(grunt) {
+  grunt.registerTask('saucelabs', function() {
+    var exec = require('child_process').exec;
+    var done = this.async();
+
+    if (this.args[0] == 'connect') {
+      exec('curl https://gist.githubusercontent.com/santiycr/5139565/raw/sauce_connect_setup.sh | bash',
+      function(error, stdout, stderr) {
+        if (error) {
+          grunt.log.error(error);
+          return done();
+        }
+
+        grunt.verbose.error(stderr.toString());
+        grunt.verbose.writeln(stdout.toString());
+        grunt.task.run(['karma:saucelabs']);
+        done();
+      });
+    } else {
+      grunt.task.run(['karma:saucelabs']);
+      done();
+    }
+  });
+}
diff --git a/build/tasks/test.js b/build/tasks/test.js
new file mode 100644
index 0000000..70d80e0
--- /dev/null
+++ b/build/tasks/test.js
@@ -0,0 +1,63 @@
+module.exports = function(grunt) {
+  // The test task will run `karma:saucelabs` when running in travis,
+  // when running via a PR from a fork, it'll run qunit tests in phantom using
+  // karma otherwise, it'll run the tests in chrome via karma
+  // You can specify which browsers to build with by using grunt-style arguments
+  // or separating them with a comma:
+  //   grunt test:chrome:firefox  # grunt-style
+  //   grunt test:chrome,firefox  # comma-separated
+  grunt.registerTask('test', function() {
+    var tasks = this.args,
+    tasksMinified,
+    tasksMinifiedApi;
+
+    grunt.task.run(['pretask']);
+
+    if (process.env.TRAVIS_PULL_REQUEST !== 'false') {
+      grunt.task.run(['karma:firefox', 'coveralls']);
+    } else if (process.env.TRAVIS) {
+      grunt.task.run(['karma:firefox', 'coveralls']);
+      //Disabling saucelabs until we figure out how to make it run reliably.
+      //grunt.task.run([
+      //'karma:chrome_sl',
+      //'karma:firefox_sl',
+      //'karma:safari_sl',
+      //'karma:ipad_sl',
+      //'karma:android_sl',
+      //'karma:ie_sl'
+      //]);
+    } else {
+      // if we aren't running this in a CI, but running it manually, we can
+      // supply arguments to this task. These arguments are either colon (`:`)
+      // separated which is the default grunt separator for arguments, or they
+      // are comma (`,`) separated to make it easier.
+      // The arguments are the names of which browsers you want. It'll then
+      // make sure you have the `minified` and `minified_api` for those browsers
+      // as well.
+      if (tasks.length === 0) {
+        tasks.push('chrome');
+      }
+      if (tasks.length === 1) {
+        tasks = tasks[0].split(',');
+      }
+
+      tasksMinified = tasks.slice();
+      tasksMinifiedApi = tasks.slice();
+
+      tasksMinified = tasksMinified.map(function(task) {
+        return 'minified_' + task;
+      });
+
+      tasksMinifiedApi = tasksMinifiedApi.map(function(task) {
+        return 'minified_api_' + task;
+      });
+
+      tasks = tasks.concat(tasksMinified).concat(tasksMinifiedApi);
+      tasks = tasks.map(function(task) {
+        return 'karma:' + task;
+      });
+
+      grunt.task.run(tasks);
+    }
+  });
+};
diff --git a/build/tasks/vjs-version.js b/build/tasks/vjs-version.js
new file mode 100644
index 0000000..12b129a
--- /dev/null
+++ b/build/tasks/vjs-version.js
@@ -0,0 +1,7 @@
+module.exports = function(grunt) {
+  grunt.registerTask('vjs-version', function() {
+    var version = grunt.vjsVersion.full;
+    grunt.log.writeln(version);
+    return version;
+  });
+}
diff --git a/docs/examples/shared/example-captions.vtt b/docs/examples/shared/example-captions.vtt
new file mode 100644
index 0000000..e598be1
--- /dev/null
+++ b/docs/examples/shared/example-captions.vtt
@@ -0,0 +1,41 @@
+WEBVTT
+
+00:00.700 --> 00:04.110
+Captions describe all relevant audio for the hearing impaired.
+[ Heroic music playing for a seagull ]
+
+00:04.500 --> 00:05.000
+[ Splash!!! ]
+
+00:05.100 --> 00:06.000
+[ Sploosh!!! ]
+
+00:08.000 --> 00:09.225
+[ Splash...splash...splash splash splash ]
+
+00:10.525 --> 00:11.255
+[ Splash, Sploosh again ]
+
+00:13.500 --> 00:14.984
+Dolphin: eeeEEEEEeeee!
+
+00:14.984 --> 00:16.984
+Dolphin: Squawk! eeeEEE?
+
+00:25.000 --> 00:28.284
+[ A whole ton of splashes ]
+
+00:29.500 --> 00:31.000
+Mine. Mine. Mine.
+
+00:34.300 --> 00:36.000
+Shark: Chomp
+
+00:36.800 --> 00:37.900
+Shark: CHOMP!!!
+
+00:37.861 --> 00:41.193
+EEEEEEOOOOOOOOOOWHALENOISE
+
+00:42.593 --> 00:45.611
+[ BIG SPLASH ]
\ No newline at end of file
diff --git a/docs/examples/simple-embed/index.html b/docs/examples/simple-embed/index.html
new file mode 100644
index 0000000..f341228
--- /dev/null
+++ b/docs/examples/simple-embed/index.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Video.js | HTML5 Video Player</title>
+
+  <!-- Chang URLs to wherever Video.js files will be hosted -->
+  <link href="../../video-js.css" rel="stylesheet" type="text/css">
+  <!-- video.js must be in the <head> for older IEs to work. -->
+  <script src="../../video.js"></script>
+
+  <!-- Unless using the CDN hosted version, update the URL to the Flash SWF -->
+  <script>
+    videojs.options.flash.swf = "../../video-js.swf";
+  </script>
+
+</head>
+<body>
+
+  <video id="example_video_1" class="video-js vjs-default-skin" controls preload="none" width="640" height="264"
+      poster="http://video-js.zencoder.com/oceans-clip.png"
+      data-setup="{}">
+    <source src="http://video-js.zencoder.com/oceans-clip.mp4" type='video/mp4' />
+    <source src="http://video-js.zencoder.com/oceans-clip.webm" type='video/webm' />
+    <source src="http://video-js.zencoder.com/oceans-clip.ogv" type='video/ogg' />
+    <track kind="captions" src="../shared/example-captions.vtt" srclang="en" label="English"></track><!-- Tracks need an ending tag thanks to IE9 -->
+    <track kind="subtitles" src="../shared/example-captions.vtt" srclang="en" label="English"></track><!-- Tracks need an ending tag thanks to IE9 -->
+    <p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
+  </video>
+
+</body>
+</html>
diff --git a/package.json b/package.json
index 993faa3..737fd86 100644
--- a/package.json
+++ b/package.json
@@ -20,39 +20,36 @@
     "type": "git",
     "url": "https://github.com/videojs/video.js.git"
   },
-  "main": "./dist/video-js/video.js",
+  "main": "src/js/video.js",
   "dependencies": {
     "videojs-swf": "4.5.4",
-    "vtt.js": "git+https://github.com/gkatsev/vtt.js.git#shim-build"
+    "vtt.js": "git+https://github.com/gkatsev/vtt.js.git#shim-build",
+    "global": "^4.3.0"
   },
   "devDependencies": {
     "babelify": "^5.0.4",
     "blanket": "^1.1.6",
     "browserify-istanbul": "^0.2.1",
-    "calcdeps": "~0.1.7",
+    "browserify-versionify": "^1.0.4",
     "chg": "~0.2.0",
-    "contribflow": "~0.2.0",
-    "github": "~0.1.14",
-    "global": "^4.3.0",
     "grunt": "^0.4.4",
     "grunt-aws-s3": "^0.12.1",
-    "grunt-banner": "~0.2.0",
     "grunt-browserify": "^3.5.0",
     "grunt-cli": "~0.1.0",
     "grunt-contrib-clean": "~0.4.0a",
+    "grunt-contrib-concat": "^0.5.1",
     "grunt-contrib-connect": "~0.7.1",
-    "grunt-contrib-copy": "~0.3.2",
+    "grunt-contrib-copy": "^0.8.0",
     "grunt-contrib-cssmin": "~0.6.0",
     "grunt-contrib-jshint": "^0.11.0",
     "grunt-contrib-less": "~0.6.4",
-    "grunt-contrib-qunit": "~0.2.1",
     "grunt-contrib-uglify": "^0.8.0",
     "grunt-contrib-watch": "~0.1.4",
     "grunt-coveralls": "^1.0.0",
+    "grunt-exorcise": "^1.0.1",
     "grunt-fastly": "^0.1.3",
     "grunt-github-releaser": "^0.1.17",
     "grunt-karma": "^0.8.3",
-    "grunt-s3": "~0.2.0-alpha",
     "grunt-version": "~0.3.0",
     "grunt-videojs-languages": "0.0.4",
     "grunt-zip": "0.10.2",
@@ -68,8 +65,6 @@
     "karma-safari-launcher": "^0.1.1",
     "karma-sauce-launcher": "^0.2.8",
     "karma-sinon": "^1.0.3",
-    "mocha": "~1.8.1",
-    "open": "0.0.4",
     "qunitjs": "~1.14.0",
     "sinon": "~1.9.1",
     "uglify-js": "~2.3.6",
diff --git a/sandbox/index.html.example b/sandbox/index.html.example
index 25d2e7c..ec3583c 100644
--- a/sandbox/index.html.example
+++ b/sandbox/index.html.example
@@ -4,19 +4,25 @@
   <meta charset="utf-8" />
   <title>Video.js Sandbox</title>
 
-  <link href="../build/files/video-js.css" rel="stylesheet" type="text/css">
+  <link href="../build/temp/video-js.css" rel="stylesheet" type="text/css">
 
   <!-- LOAD VIDEO.JS SOURCE FILES IN ORDER -->
-  <script src="../build/source-loader.js"></script>
+  <script src="../build/temp/video.js"></script>
 
   <!-- Set the location of the flash SWF -->
   <script>
-    vjs.options.flash.swf = '../node_modules/videojs-swf/dist/video-js.swf'
+    vjs.options.flash.swf = '../build/temp/video-js.swf'
   </script>
 
 </head>
 <body>
-  <p style="background-color:#eee; border: 1px solid #777; padding: 10px; font-size: .8em; line-height: 1.5em; font-family: Verdana, sans-serif;">You can use /sandbox/ for writing and testing your own code. Nothing in /sandbox/ will get checked into the repo, except files that end in .example, so please don't edit or add those files. To get started make a copy of index.html.example and rename it to index.html.</p>
+  <div style="background-color:#eee; border: 1px solid #777; padding: 10px; margin-bottom: 20px; font-size: .8em; line-height: 1.5em; font-family: Verdana, sans-serif;">
+    <p>You can use /sandbox/ for writing and testing your own code. Nothing in /sandbox/ will get checked into the repo, except files that end in .example (so don't edit or add those files). To get started make a copy of index.html.example and rename it to index.html.</p>
+    <pre>cp sandbox/index.html.example index.html</pre>
+    <pre>grunt watch</pre>
+    <pre>grunt connect</pre>
+    <pre>open http://localhost:9999/sandbox/index.html</pre>
+  </div>
 
   <video id="vid1" class="video-js vjs-default-skin" controls preload="auto" width="640" height="264"
       poster="http://video-js.zencoder.com/oceans-clip.png"
@@ -24,7 +30,7 @@
     <source src="http://video-js.zencoder.com/oceans-clip.mp4" type='video/mp4'>
     <source src="http://video-js.zencoder.com/oceans-clip.webm" type='video/webm'>
     <source src="http://video-js.zencoder.com/oceans-clip.ogv" type='video/ogg'>
-    <track kind="captions" src="../build/demo-files/demo.captions.vtt" srclang="en" label="English"></track><!-- Tracks need an ending tag thanks to IE9 -->
+    <track kind="captions" src="../docs/examples/shared/example-captions.vtt" srclang="en" label="English"></track><!-- Tracks need an ending tag thanks to IE9 -->
     <p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
   </video>
 
diff --git a/sandbox/language.html.example b/sandbox/language.html.example
index 9c1368a..8f4fe28 100644
--- a/sandbox/language.html.example
+++ b/sandbox/language.html.example
@@ -5,10 +5,10 @@
   <meta charset="utf-8" />
   <title>VideoJS Languages Demo</title>
 
-  <link href="../build/files/video-js.css" rel="stylesheet" type="text/css">
+  <link href="../build/temp/video-js.css" rel="stylesheet" type="text/css">
 
   <!-- LOAD VIDEO.JS SOURCE FILES IN ORDER -->
-  <script src="../build/source-loader.js"></script>
+  <script src="../build/temp/video.js"></script>
 
   <!-- Set the location of the flash SWF -->
   <!-- Add support for Spanish 'es' -->
diff --git a/sandbox/plugin.html.example b/sandbox/plugin.html.example
index 6d8a3d7..a5bd34d 100644
--- a/sandbox/plugin.html.example
+++ b/sandbox/plugin.html.example
@@ -4,10 +4,10 @@
   <meta charset="utf-8" />
   <title>Video.js Plugin Example</title>
 
-  <link href="../build/files/video-js.css" rel="stylesheet" type="text/css">
+  <link href="../build/temp/video-js.css" rel="stylesheet" type="text/css">
 
   <!-- LOAD VIDEO.JS SOURCE FILES IN ORDER -->
-  <script src="../build/source-loader.js"></script>
+  <script src="../build/temp/video.js"></script>
 
 </head>
 <body>
diff --git a/src/css/video-js.less b/src/css/video-js.less
index fc43aee..9a06afb 100644
--- a/src/css/video-js.less
+++ b/src/css/video-js.less
@@ -1,6 +1,6 @@
  /*!
 Video.js Default Styles (http://videojs.com)
-Version GENERATED_AT_BUILD
+@version GENERATED_AT_BUILD
 Create your own skin at http://designer.videojs.com
 */
 
diff --git a/src/js/cdn.js b/src/js/cdn.js
deleted file mode 100644
index 93ff4ca..0000000
--- a/src/js/cdn.js
+++ /dev/null
@@ -1,71 +0,0 @@
-import pkg from '../../package.json';
-import window from 'global/window';
-
-/**
- * Google Analytics tracking pixel for the freely hosted version of Video.js
- * at vjs.zencdn.net. We'll use this data to develop a support matrix of
- * browsers and devices, and possibly track errors.
- *
- * This code generates the GA tracking URL without requiring the GA javascript
- * library.
- *
- * @type {Image}
- */
-var sendGaEvent = function(image) {
-  const i = new window.Image();
-  const w = window;
-  const n = window.navigator;
-  const l = window.location;
-  const e = window.encodeURIComponent;
-
-  // Google Analytics has a limit of 10 million hits per month for free accounts.
-  // The Video.js CDN goes over this (by a lot) and they've asked us to stop.
-  if (Math.random() > 0.01) {
-    return;
-  }
-
-  // Setting the source of an image will load the URL even without adding to dom
-  // Using //www, still seems to work for https even though ssl.google is used by google
-  i.src='//www.google-analytics.com/__utm.gif'
-    // Version
-    +'?utmwv=5.4.2'
-    // ID
-    +'&utmac=UA-16505296-2'
-    // Sessions
-    // &utms=2
-    // Cache breaker (using utmcc to do this)
-    +'&utmn='+1
-    +'&utmhn='+e(l.hostname)
-    // Encoding
-    // &utmcs=UTF-8
-    +'&utmsr='+w.screen.availWidth+'x'+w.screen.availHeight
-    // Browser window
-    // &utmvp=1057x1105
-    // Color depth
-    // &utmsc=24-bit
-    +'&utmul='+(n.language||n.userLanguage||'').toLowerCase()
-    // Java
-    // &utmje=1
-    // Flash version
-    // &utmfl=11.7%20r700
-    // Page title
-    // &utmdt=HTML5%20Video%20Player%20%7C%20Video.js
-    // Adsense
-    // &utmhid=1112291628
-    // Referrer, '-' is none
-    // Using current page as referrer so stats show up cleaner than "Direct"
-    +'&utmr='+e(l.href)
-    +'&utmp='+e(l.hostname+l.pathname)
-    // Current time stamp
-    // &utmht=1370890439353
-    // ?
-    // &utmu=q
-    // Cookies! Manually setting visitor ID and setting everything else to 1
-    // Random number used as cache buster instead of utmn
-    +'&utmcc=__utma%3D1.'+Math.floor(Math.random()*1e10)+'.1.1.1.1%3B'
-    // Custom Var: vjsv is the variable name and 1.0.0 is the VJS version
-    +'&utme=8(vjsv)9('+ pkg.version +')'
-  ;
-};
-
-export default sendGaEvent;
diff --git a/src/js/core.js b/src/js/core.js
index a332c9f..fee46ed 100644
--- a/src/js/core.js
+++ b/src/js/core.js
@@ -76,18 +76,18 @@ var videojs = function(id, options, ready){
 // var videojs = window['videojs'] = vjs;
 
 // CDN Version. Used to target right flash swf.
-videojs.CDN_VERSION = 'GENERATED_CDN_VSN';
+videojs.CDN_VERSION = '__VERSION_NO_PATCH__';
 videojs.ACCESS_PROTOCOL = ('https:' == document.location.protocol ? 'https://' : 'http://');
 
 /**
 * Full player version
 * @type {string}
 */
-videojs['VERSION'] = 'GENERATED_FULL_VSN';
+videojs['VERSION'] = '__VERSION__';
 
 // Set CDN Version of swf
-// The added (+) blocks the replace from changing this GENERATED_CDN_VSN string
-if (videojs.CDN_VERSION !== 'GENERATED'+'_CDN_VSN') {
+// The added (+) blocks the replace from changing this _VERSION_NO_PATCH_ string
+if (videojs.CDN_VERSION !== '__VERSION_'+'NO_PATCH__') {
   Options['flash']['swf'] = videojs.ACCESS_PROTOCOL + 'vjs.zencdn.net/'+videojs.CDN_VERSION+'/video-js.swf';
 }
 
diff --git a/tasks/build.js b/tasks/build.js
deleted file mode 100644
index 89b4358..0000000
--- a/tasks/build.js
+++ /dev/null
@@ -1,47 +0,0 @@
-module.exports = function(grunt) {
-  grunt.registerMultiTask('build', 'Building Source', function(){
-    var vtt, novtt;
-
-    // Fix windows file path delimiter issue
-    var i = grunt.sourceFiles.length;
-    while (i--) {
-      grunt.sourceFiles[i] = grunt.sourceFiles[i].replace(/\\/g, '/');
-    }
-
-    // Create a combined sources file. https://github.com/zencoder/video-js/issues/287
-    var combined = '';
-    grunt.sourceFiles.forEach(function(result){
-      combined += grunt.file.read(result);
-    });
-    // Replace CDN version ref in js. Use major/minor version.
-    combined = combined.replace(/GENERATED_CDN_VSN/g, grunt.vjsVersion.majorMinor);
-    combined = combined.replace(/GENERATED_FULL_VSN/g, grunt.vjsVersion.full);
-
-    vtt = grunt.file.read('node_modules/vtt.js/dist/vtt.js');
-    novtt = combined;
-    combined += '\n'+vtt;
-
-    grunt.file.write('build/files/combined.video.js', combined);
-    grunt.file.write('build/files/combined.video.novtt.js', novtt);
-
-    // Copy over other files
-    // grunt.file.copy('src/css/video-js.png', 'build/files/video-js.png');
-    grunt.file.copy('node_modules/videojs-swf/dist/video-js.swf', 'build/files/video-js.swf');
-
-    // Inject version number into css file
-    var css = grunt.file.read('build/files/video-js.css');
-    css = css.replace(/GENERATED_AT_BUILD/g, grunt.vjsVersion.full);
-    grunt.file.write('build/files/video-js.css', css);
-
-    // Copy over font files
-    grunt.file.recurse('src/css/font', function(absdir, rootdir, subdir, filename) {
-      // Block .DS_Store files
-      if ('filename'.substring(0,1) !== '.') {
-        grunt.file.copy(absdir, 'build/files/font/' + filename);
-      }
-    });
-
-    // Minify CSS
-    grunt.task.run(['cssmin']);
-  });
-}
diff --git a/tasks/cdn-links.js b/tasks/cdn-links.js
deleted file mode 100644
index 699f05f..0000000
--- a/tasks/cdn-links.js
+++ /dev/null
@@ -1,13 +0,0 @@
-module.exports = function(grunt) {
-  grunt.registerTask('cdn-links', 'Update the version of CDN links in docs', function(){
-    var doc = grunt.file.read('docs/guides/setup.md');
-    var version = require('../package.json').version;
-
-    // remove the patch version to point to the latest patch
-    version = version.replace(/(\d+\.\d+)\.\d+/, '$1');
-
-    // update the version in http://vjs.zencdn.net/4.3/video.js
-    doc = doc.replace(/(\/\/vjs\.zencdn\.net\/)\d+\.\d+(\.\d+)?/g, '$1'+version);
-    grunt.file.write('docs/guides/setup.md', doc);
-  });
-};
diff --git a/tasks/dist-copy.js b/tasks/dist-copy.js
deleted file mode 100644
index 160d5bb..0000000
--- a/tasks/dist-copy.js
+++ /dev/null
@@ -1,61 +0,0 @@
-module.exports = function(grunt) {
-  grunt.registerTask('dist-copy', 'Assembling distribution', function(){
-    var css, jsmin, jsdev, cdnjs, uglify;
-
-    uglify = require('uglify-js');
-
-    // Manually copy each source file
-    grunt.file.copy('build/files/minified.video.js', 'dist/video-js/video.js');
-    grunt.file.copy('build/files/combined.video.js', 'dist/video-js/video.dev.js');
-    grunt.file.copy('build/files/minified.video.novtt.js', 'dist/video-js/video.novtt.js');
-    grunt.file.copy('build/files/combined.video.novtt.js', 'dist/video-js/video.novtt.dev.js');
-    grunt.file.copy('build/files/video-js.css', 'dist/video-js/video-js.css');
-    grunt.file.copy('build/files/video-js.min.css', 'dist/video-js/video-js.min.css');
-    grunt.file.copy('node_modules/videojs-swf/dist/video-js.swf', 'dist/video-js/video-js.swf');
-    grunt.file.copy('build/demo-files/demo.html', 'dist/video-js/demo.html');
-    grunt.file.copy('build/demo-files/demo.captions.vtt', 'dist/video-js/demo.captions.vtt');
-    grunt.file.copy('src/css/video-js.less', 'dist/video-js/video-js.less');
-
-
-    // Copy over font files
-    grunt.file.recurse('build/files/font', function(absdir, rootdir, subdir, filename) {
-      // Block .DS_Store files
-      if ('filename'.substring(0,1) !== '.') {
-        grunt.file.copy(absdir, 'dist/video-js/font/' + filename);
-      }
-    });
-
-    // Copy over language files
-    grunt.file.recurse('build/files/lang', function(absdir, rootdir, subdir, filename) {
-      // Block .DS_Store files
-      if ('filename'.substring(0,1) !== '.') {
-        grunt.file.copy(absdir, 'dist/cdn/lang/' + filename);
-        grunt.file.copy(absdir, 'dist/video-js/lang/' + filename);
-      }
-    });
-
-    // ds_store files sometime find their way in
-    if (grunt.file.exists('dist/video-js/.DS_Store')) {
-      grunt.file['delete']('dist/video-js/.DS_Store');
-    }
-
-    // CDN version uses already hosted font files
-    // Minified version only, doesn't need demo files
-    grunt.file.copy('build/files/minified.video.js', 'dist/cdn/video.js');
-    grunt.file.copy('build/files/video-js.min.css', 'dist/cdn/video-js.css');
-    grunt.file.copy('node_modules/videojs-swf/dist/video-js.swf', 'dist/cdn/video-js.swf');
-    grunt.file.copy('build/demo-files/demo.captions.vtt', 'dist/cdn/demo.captions.vtt');
-    grunt.file.copy('build/demo-files/demo.html', 'dist/cdn/demo.html');
-
-    // Replace font urls with CDN versions
-    css = grunt.file.read('dist/cdn/video-js.css');
-    css = css.replace(/font\//g, '../f/3/');
-    grunt.file.write('dist/cdn/video-js.css', css);
-
-    // Add CDN-specfic JS
-    jsmin = grunt.file.read('dist/cdn/video.js');
-    // GA Tracking Pixel (manually building the pixel URL)
-    cdnjs = uglify.minify('src/js/cdn.js').code.replace('v0.0.0', 'v'+ grunt.vjsVersion.full);
-    grunt.file.write('dist/cdn/video.js', jsmin + cdnjs);
-  });
-}
diff --git a/tasks/next-issue.js b/tasks/next-issue.js
deleted file mode 100644
index dee2b01..0000000
--- a/tasks/next-issue.js
+++ /dev/null
@@ -1,82 +0,0 @@
-module.exports = function(grunt) {
-  grunt.registerTask('next-issue', 'Get the next issue that needs a response', function(){
-    var done = this.async();
-    var GitHubApi = require('github');
-    var open = require('open');
-
-    var github = new GitHubApi({
-      // required
-      version: '3.0.0',
-      // optional
-      debug: true,
-      protocol: 'https',
-      // host: 'github.my-GHE-enabled-company.com',
-      // pathPrefix: '/api/v3', // for some GHEs
-      timeout: 5000
-    });
-
-    github.issues.repoIssues({
-      // optional:
-      // headers: {
-      //     'cookie': 'blahblah'
-      // },
-      user: 'videojs',
-      repo: 'video.js',
-      sort: 'updated',
-      direction: 'asc',
-      state: 'open',
-      per_page: 100
-    }, function(err, res) {
-      var issueToOpen;
-      var usersWithWrite = ['heff', 'mmcc'];
-      var categoryLabels = ['enhancement', 'bug', 'question', 'feature'];
-
-      console.log('Number of issues: '+res.length);
-
-      // TODO: Find the best way to exclude an issue where a question has been asked of the
-      // issue owner/submitter that hasn't been answerd yet.
-      // A stupid simple first step would be to check for the needs: more info label
-      // and exactly one comment (the question)
-
-      // find issues that need categorizing, no category labels
-      res.some(function(issue){
-        if (issue.labels.length === 0) {
-          return issueToOpen = issue; // break
-        }
-        // look for category labels
-        var categorized = issue.labels.some(function(label){
-          return categoryLabels.indexOf(label.name) >= 0;
-        });
-        if (!categorized) {
-          return issueToOpen = issue; // break
-        }
-      });
-      if (issueToOpen) {
-        open(issueToOpen.html_url);
-        return done();
-      }
-
-      // find issues that need confirming or answering
-      res.some(function(issue){
-        // look for confirmed label
-        var confirmed = issue.labels.some(function(label){
-          return label.name === 'confirmed';
-        });
-        // Was exluding questions, but that might leave a lot of people hanging
-        // var question = issue.labels.some(function(label){
-        //   return label.name === 'question';
-        // });
-        if (!confirmed) { //  && !question
-          return issueToOpen = issue; // break
-        }
-      });
-      if (issueToOpen) {
-        open(issueToOpen.html_url);
-        return done();
-      }
-
-      grunt.log.writeln('No next issue found');
-      done();
-    });
-  });
-}
diff --git a/tasks/saucelabs.js b/tasks/saucelabs.js
deleted file mode 100644
index 2873c86..0000000
--- a/tasks/saucelabs.js
+++ /dev/null
@@ -1,24 +0,0 @@
-module.exports = function(grunt) {
-  grunt.registerTask('saucelabs', function() {
-    var exec = require('child_process').exec;
-    var done = this.async();
-
-    if (this.args[0] == 'connect') {
-      exec('curl https://gist.githubusercontent.com/santiycr/5139565/raw/sauce_connect_setup.sh | bash',
-      function(error, stdout, stderr) {
-        if (error) {
-          grunt.log.error(error);
-          return done();
-        }
-
-        grunt.verbose.error(stderr.toString());
-        grunt.verbose.writeln(stdout.toString());
-        grunt.task.run(['karma:saucelabs']);
-        done();
-      });
-    } else {
-      grunt.task.run(['karma:saucelabs']);
-      done();
-    }
-  });
-}
diff --git a/tasks/test.js b/tasks/test.js
deleted file mode 100644
index 70d80e0..0000000
--- a/tasks/test.js
+++ /dev/null
@@ -1,63 +0,0 @@
-module.exports = function(grunt) {
-  // The test task will run `karma:saucelabs` when running in travis,
-  // when running via a PR from a fork, it'll run qunit tests in phantom using
-  // karma otherwise, it'll run the tests in chrome via karma
-  // You can specify which browsers to build with by using grunt-style arguments
-  // or separating them with a comma:
-  //   grunt test:chrome:firefox  # grunt-style
-  //   grunt test:chrome,firefox  # comma-separated
-  grunt.registerTask('test', function() {
-    var tasks = this.args,
-    tasksMinified,
-    tasksMinifiedApi;
-
-    grunt.task.run(['pretask']);
-
-    if (process.env.TRAVIS_PULL_REQUEST !== 'false') {
-      grunt.task.run(['karma:firefox', 'coveralls']);
-    } else if (process.env.TRAVIS) {
-      grunt.task.run(['karma:firefox', 'coveralls']);
-      //Disabling saucelabs until we figure out how to make it run reliably.
-      //grunt.task.run([
-      //'karma:chrome_sl',
-      //'karma:firefox_sl',
-      //'karma:safari_sl',
-      //'karma:ipad_sl',
-      //'karma:android_sl',
-      //'karma:ie_sl'
-      //]);
-    } else {
-      // if we aren't running this in a CI, but running it manually, we can
-      // supply arguments to this task. These arguments are either colon (`:`)
-      // separated which is the default grunt separator for arguments, or they
-      // are comma (`,`) separated to make it easier.
-      // The arguments are the names of which browsers you want. It'll then
-      // make sure you have the `minified` and `minified_api` for those browsers
-      // as well.
-      if (tasks.length === 0) {
-        tasks.push('chrome');
-      }
-      if (tasks.length === 1) {
-        tasks = tasks[0].split(',');
-      }
-
-      tasksMinified = tasks.slice();
-      tasksMinifiedApi = tasks.slice();
-
-      tasksMinified = tasksMinified.map(function(task) {
-        return 'minified_' + task;
-      });
-
-      tasksMinifiedApi = tasksMinifiedApi.map(function(task) {
-        return 'minified_api_' + task;
-      });
-
-      tasks = tasks.concat(tasksMinified).concat(tasksMinifiedApi);
-      tasks = tasks.map(function(task) {
-        return 'karma:' + task;
-      });
-
-      grunt.task.run(tasks);
-    }
-  });
-};
diff --git a/tasks/vjs-version.js b/tasks/vjs-version.js
deleted file mode 100644
index 12b129a..0000000
--- a/tasks/vjs-version.js
+++ /dev/null
@@ -1,7 +0,0 @@
-module.exports = function(grunt) {
-  grunt.registerTask('vjs-version', function() {
-    var version = grunt.vjsVersion.full;
-    grunt.log.writeln(version);
-    return version;
-  });
-}
diff --git a/test/api/api.js b/test/api/api.js
new file mode 100644
index 0000000..ff3bf71
--- /dev/null
+++ b/test/api/api.js
@@ -0,0 +1,257 @@
+/**
+ * These tests run on the minified, window.videojs and ensure the needed
+ * APIs still exist
+ */
+
+(function(){
+
+q.module('Player Minified');
+
+test('videojs should exist on the window', function() {
+  ok(window.videojs, 'videojs exists on the window');
+});
+
+test('should be able to access expected player API methods', function() {
+  var player = videojs.getComponent('Player').prototype;
+
+  // Native HTML5 Methods
+  ok(player.error, 'error exists');
+  ok(player.src, 'src exists');
+  ok(player.currentSrc, 'currentSrc exists');
+  ok(player.buffered, 'buffered exists');
+  ok(player.load, 'load exists');
+  ok(player.seeking, 'seeking exists');
+  ok(player.currentTime, 'currentTime exists');
+  ok(player.duration, 'duration exists');
+  ok(player.paused, 'paused exists');
+  ok(player.ended, 'ended exists');
+  ok(player.autoplay, 'autoplay exists');
+  ok(player.loop, 'loop exists');
+  ok(player.play , 'play exists');
+  ok(player.pause , 'pause exists');
+  ok(player.controls, 'controls exists');
+  ok(player.volume, 'volume exists');
+  ok(player.muted, 'muted exists');
+  ok(player.width, 'width exists');
+  ok(player.height, 'height exists');
+  ok(player.poster, 'poster exists');
+  ok(player.textTracks, 'textTracks exists');
+  ok(player.requestFullscreen, 'requestFullscreen exists');
+  ok(player.exitFullscreen, 'exitFullscreen exists');
+  ok(player.playbackRate, 'playbackRate exists');
+  ok(player.networkState, 'networkState exists');
+  ok(player.readyState, 'readyState exists');
+
+  // Unsupported Native HTML5 Methods
+  // ok(player.canPlayType, 'canPlayType exists');
+  // ok(player.startTime, 'startTime exists');
+  // ok(player.defaultPlaybackRate, 'defaultPlaybackRate exists');
+  // ok(player.playbackRate, 'playbackRate exists');
+  // ok(player.played, 'played exists');
+  // ok(player.seekable, 'seekable exists');
+  // ok(player.videoWidth, 'videoWidth exists');
+  // ok(player.videoHeight, 'videoHeight exists');
+
+  // Additional player methods
+  ok(player.bufferedPercent, 'bufferedPercent exists');
+  ok(player.reportUserActivity, 'reportUserActivity exists');
+  ok(player.userActive, 'userActive exists');
+  ok(player.usingNativeControls, 'usingNativeControls exists');
+  ok(player.isFullscreen, 'isFullscreen exists');
+
+  // TextTrack methods
+  ok(player.textTracks, 'textTracks exists');
+  ok(player.remoteTextTracks, 'remoteTextTracks exists');
+  ok(player.addTextTrack, 'addTextTrack exists');
+  ok(player.addRemoteTextTrack, 'addRemoteTextTrack exists');
+  ok(player.removeRemoteTextTrack, 'removeRemoteTextTrack exists');
+
+  // Deprecated methods that should still exist
+  ok(player.requestFullScreen, 'requestFullScreen exists');
+  ok(player.isFullScreen, 'isFullScreen exists');
+  ok(player.cancelFullScreen, 'cancelFullScreen exists');
+});
+
+test('should be able to access expected component API methods', function() {
+  var comp = videojs.getComponent('Component').create({ id: function(){ return 1; }, reportUserActivity: function(){} });
+
+  // Component methods
+  ok(comp.player, 'player exists');
+  ok(comp.options, 'options exists');
+  ok(comp.init, 'init exists');
+  ok(comp.dispose, 'dispose exists');
+  ok(comp.createEl, 'createEl exists');
+  ok(comp.contentEl, 'contentEl exists');
+  ok(comp.el, 'el exists');
+  ok(comp.addChild, 'addChild exists');
+  ok(comp.getChild, 'getChild exists');
+  ok(comp.getChildById, 'getChildById exists');
+  ok(comp.children, 'children exists');
+  ok(comp.initChildren, 'initChildren exists');
+  ok(comp.removeChild, 'removeChild exists');
+  ok(comp.on, 'on exists');
+  ok(comp.off, 'off exists');
+  ok(comp.one, 'one exists');
+  ok(comp.trigger, 'trigger exists');
+  ok(comp.triggerReady, 'triggerReady exists');
+  ok(comp.show, 'show exists');
+  ok(comp.hide, 'hide exists');
+  ok(comp.width, 'width exists');
+  ok(comp.height, 'height exists');
+  ok(comp.dimensions, 'dimensions exists');
+  ok(comp.ready, 'ready exists');
+  ok(comp.addClass, 'addClass exists');
+  ok(comp.removeClass, 'removeClass exists');
+  ok(comp.buildCSSClass, 'buildCSSClass exists');
+  ok(comp.setInterval, 'setInterval exists');
+  ok(comp.clearInterval, 'clearInterval exists');
+  ok(comp.setTimeout, 'setTimeout exists');
+  ok(comp.clearTimeout, 'clearTimeout exists');
+});
+
+test('should be able to access expected MediaTech API methods', function() {
+  var media = videojs.getComponent('MediaTechController');
+  var mediaProto = media.prototype;
+  var html5 = videojs.getComponent('Html5');
+  var html5Proto = html5.prototype;
+  var flash = videojs.getComponent('Flash');
+  var flashProto = flash.prototype;
+
+  ok(mediaProto.setPoster, 'setPoster should exist on the Media tech');
+  ok(html5Proto.setPoster, 'setPoster should exist on the HTML5 tech');
+  ok(flashProto.setPoster, 'setPoster should exist on the Flash tech');
+
+  ok(html5.patchCanPlayType, 'patchCanPlayType should exist for HTML5');
+  ok(html5.unpatchCanPlayType, 'unpatchCanPlayType should exist for HTML5');
+
+  // Source Handler Functions
+  ok(media.withSourceHandlers, 'withSourceHandlers should exist for Media Tech');
+
+  ok(html5.canPlaySource, 'canPlaySource should exist for HTML5');
+  ok(html5.registerSourceHandler, 'registerSourceHandler should exist for Html5');
+  ok(html5.selectSourceHandler, 'selectSourceHandler should exist for Html5');
+  ok(html5.prototype.setSource, 'setSource should exist for Html5');
+  ok(html5.prototype.disposeSourceHandler, 'disposeSourceHandler should exist for Html5');
+
+  ok(flash.canPlaySource, 'canPlaySource should exist for Flash');
+  ok(flash.registerSourceHandler, 'registerSourceHandler should exist for Flash');
+  ok(flash.selectSourceHandler, 'selectSourceHandler should exist for Flash');
+  ok(flash.prototype.setSource, 'setSource should exist for Flash');
+  ok(flash.prototype.disposeSourceHandler, 'disposeSourceHandler should exist for Flash');
+});
+
+test('should export ready api call to public', function() {
+  var videoTag = testHelperMakeTag();
+
+  var fixture = document.getElementById('qunit-fixture');
+  fixture.appendChild(videoTag);
+
+  var player = videojs('example_1');
+  ok(player.ready !== undefined, 'ready callback is defined');
+  player.dispose();
+});
+
+test('should export useful components to the public', function () {
+  ok(videojs.TOUCH_ENABLED !== undefined, 'Touch detection should be public');
+  ok(videojs.getComponent('ControlBar'), 'ControlBar should be public');
+  ok(videojs.getComponent('Button'), 'Button should be public');
+  ok(videojs.getComponent('PlayToggle'), 'PlayToggle should be public');
+  ok(videojs.getComponent('FullscreenToggle'), 'FullscreenToggle should be public');
+  ok(videojs.getComponent('BigPlayButton'), 'BigPlayButton should be public');
+  ok(videojs.getComponent('LoadingSpinner'), 'LoadingSpinner should be public');
+  ok(videojs.getComponent('CurrentTimeDisplay'), 'CurrentTimeDisplay should be public');
+  ok(videojs.getComponent('DurationDisplay'), 'DurationDisplay should be public');
+  ok(videojs.getComponent('TimeDivider'), 'TimeDivider should be public');
+  ok(videojs.getComponent('RemainingTimeDisplay'), 'RemainingTimeDisplay should be public');
+  ok(videojs.getComponent('Slider'), 'Slider should be public');
+  ok(videojs.getComponent('ProgressControl'), 'ProgressControl should be public');
+  ok(videojs.getComponent('SeekBar'), 'SeekBar should be public');
+  ok(videojs.getComponent('LoadProgressBar'), 'LoadProgressBar should be public');
+  ok(videojs.getComponent('PlayProgressBar'), 'PlayProgressBar should be public');
+  ok(videojs.getComponent('SeekHandle'), 'SeekHandle should be public');
+  ok(videojs.getComponent('VolumeControl'), 'VolumeControl should be public');
+  ok(videojs.getComponent('VolumeBar'), 'VolumeBar should be public');
+  ok(videojs.getComponent('VolumeLevel'), 'VolumeLevel should be public');
+  ok(videojs.getComponent('VolumeMenuButton'), 'VolumeMenuButton should be public');
+  ok(videojs.getComponent('VolumeHandle'), 'VolumeHandle should be public');
+  ok(videojs.getComponent('MuteToggle'), 'MuteToggle should be public');
+  ok(videojs.getComponent('PosterImage'), 'PosterImage should be public');
+  ok(videojs.getComponent('Menu'), 'Menu should be public');
+  ok(videojs.getComponent('MenuItem'), 'MenuItem should be public');
+  ok(videojs.getComponent('MenuButton'), 'MenuButton should be public');
+  ok(videojs.getComponent('PlaybackRateMenuButton'), 'PlaybackRateMenuButton should be public');
+
+  ok(videojs.getComponent('CaptionSettingsMenuItem'), 'CaptionSettingsMenuItem should be public');
+  ok(videojs.getComponent('OffTextTrackMenuItem'), 'OffTextTrackMenuItem should be public');
+  ok(videojs.getComponent('TextTrackMenuItem'), 'TextTrackMenuItem should be public');
+  ok(videojs.getComponent('TextTrackDisplay'), 'TextTrackDisplay should be public');
+  ok(videojs.getComponent('TextTrackButton'), 'TextTrackButton should be public');
+  ok(videojs.getComponent('CaptionsButton'), 'CaptionsButton should be public');
+  ok(videojs.getComponent('SubtitlesButton'), 'SubtitlesButton should be public');
+  ok(videojs.getComponent('ChaptersButton'), 'ChaptersButton should be public');
+  ok(videojs.getComponent('ChaptersTrackMenuItem'), 'ChaptersTrackMenuItem should be public');
+
+  ok(videojs.util, 'util namespace should be public');
+  ok(videojs.util.mergeOptions, 'mergeOptions should be public');
+});
+
+test('should be able to initialize player twice on the same tag using string reference', function() {
+  var videoTag = testHelperMakeTag();
+  var id = videoTag.id;
+
+  var fixture = document.getElementById('qunit-fixture');
+  fixture.appendChild(videoTag);
+
+  var player = videojs('example_1');
+  player.dispose();
+  ok(!document.getElementById(id), 'element is removed');
+
+  videoTag = testHelperMakeTag();
+  fixture.appendChild(videoTag);
+
+  player = videojs('example_1');
+  player.dispose();
+});
+
+test('videojs.players should be available after minification', function() {
+  var videoTag = testHelperMakeTag();
+  var id = videoTag.id;
+
+  var fixture = document.getElementById('qunit-fixture');
+  fixture.appendChild(videoTag);
+
+  var player = videojs(id);
+  ok(videojs.players[id] === player, 'videojs.players is available');
+
+  player.dispose();
+});
+
+test('component can be subclassed externally', function(){
+  var Component = videojs.getComponent('Component');
+  var ControlBar = videojs.getComponent('ControlBar');
+
+  var player = new (Component.extend({
+    languages: function(){},
+    reportUserActivity: function(){},
+    language: function(){},
+    textTracks: function(){ return {
+        addEventListener: Function.prototype,
+        removeEventListener: Function.prototype
+      };
+    }
+  }))({
+    id: function(){},
+    reportUserActivity: function(){}
+  });
+
+  ok(new ControlBar(player), 'created a control bar without throwing');
+});
+
+function testHelperMakeTag(){
+  var videoTag = document.createElement('video');
+  videoTag.id = 'example_1';
+  videoTag.className = 'video-js vjs-default-skin';
+  return videoTag;
+}
+
+})();
\ No newline at end of file
diff --git a/test/es6.html b/test/es6.html
deleted file mode 100644
index 3389976..0000000
--- a/test/es6.html
+++ /dev/null
@@ -1,97 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Video.js Test Suite</title>
-
-  <!-- Video.js CSS -->
-  <link rel="stylesheet" href="../build/files/video-js.css" type="text/css">
-
-  <!-- Sinon -->
-  <script src="../node_modules/sinon/pkg/sinon.js"></script>
-  <script src="../node_modules/sinon/pkg/sinon-ie.js"></script>
-
-  <!-- QUnit -->
-  <link rel="stylesheet" href="../node_modules/qunitjs/qunit/qunit.css" />
-  <script src="../node_modules/qunitjs/qunit/qunit.js"></script>
-
-  <script src="../build/files/video-es6.test.js"></script>
-
-  <script type="text/javascript">
-    (function(){
-
-      // ADD NEW TEST FILES HERE
-      window.tests = [
-        'test/unit/test-helpers.js',
-        'test/unit/core-object.js',
-        'test/unit/lib.js',
-        'test/unit/util.js',
-        'test/unit/events.js',
-        'test/unit/component.js',
-        'test/unit/button.js',
-        'test/unit/mediafaker.js',
-        'test/unit/player.js',
-        'test/unit/core.js',
-        'test/unit/media.js',
-        'test/unit/media.html5.js',
-        'test/unit/controls.js',
-        'test/unit/poster.js',
-        'test/unit/plugins.js',
-        'test/unit/flash.js',
-        'test/unit/api.js',
-        'test/unit/menu.js',
-        'test/unit/tracks/tracks.js',
-        'test/unit/tracks/text-track.js',
-        'test/unit/tracks/text-track-list.js',
-        'test/unit/tracks/text-track-cue-list.js',
-        'test/unit/tracks/text-track-controls.js',
-        'test/unit/tracks/text-track-settings.js'
-      ];
-
-      var projectRoot = '../';
-      var scripts = [];
-
-      window.loadScripts = function(scripts) {
-        for (var i = 0; i < scripts.length; i++) {
-          document.write("<script src='" + projectRoot + scripts[i] + "'><\/script>" );
-        }
-      }
-
-      // Choose either the raw source and tests
-      // Or the compiled source + tests.
-      // Use ?comiled to use the compiled tests
-      if (QUnit.urlParams.min || QUnit.urlParams.compiled) {
-        window.compiled = true;
-      } else {
-        // Bunyip/Yeti starts tests after it's done loading which can
-        // lead to a double Qunit.start error which reads as
-        // "Uncaught Error: pushFailure() assertion outside test"
-        if (window.$yetify) {
-          QUnit.config.autostart = false;
-        }
-      }
-
-    })()
-  </script>
-
-</head>
-<body>
-  <div>
-    <h1 id="qunit-header">Video.js Test Suite</h1>
-    <h2 id="qunit-banner"></h2>
-    <div id="qunit-testrunner-toolbar"></div>
-    <h2 id="qunit-userAgent"></h2>
-    <ol id="qunit-tests"></ol>
-    <div id="qunit-fixture"></div>
-  </div>
-
-  <script>
-    // Loading tests before the end to give IE time to load vjs before tests
-    if (!window.compiled) {
-      loadScripts(window.tests);
-    } else {
-      var compiledTests = "build/files/test.minified.video.js";
-      loadScripts([compiledTests]);
-    }
-  </script>
-</body>
-</html>
diff --git a/test/index.html b/test/index.html
deleted file mode 100644
index fc89a83..0000000
--- a/test/index.html
+++ /dev/null
@@ -1,97 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Video.js Test Suite</title>
-
-  <!-- Video.js CSS -->
-  <link rel="stylesheet" href="../build/files/video-js.css" type="text/css">
-
-  <!-- Sinon -->
-  <script src="../node_modules/sinon/pkg/sinon.js"></script>
-  <script src="../node_modules/sinon/pkg/sinon-ie.js"></script>
-
-  <!-- QUnit -->
-  <link rel="stylesheet" href="../node_modules/qunitjs/qunit/qunit.css" />
-  <script src="../node_modules/qunitjs/qunit/qunit.js"></script>
-
-  <script type="text/javascript">
-    (function(){
-
-      // ADD NEW TEST FILES HERE
-      window.tests = [
-        'test/unit/test-helpers.js',
-        'test/unit/core-object.js',
-        'test/unit/lib.js',
-        'test/unit/util.js',
-        'test/unit/events.js',
-        'test/unit/component.js',
-        'test/unit/button.js',
-        'test/unit/mediafaker.js',
-        'test/unit/player.js',
-        'test/unit/core.js',
-        'test/unit/media.js',
-        'test/unit/media.html5.js',
-        'test/unit/controls.js',
-        'test/unit/poster.js',
-        'test/unit/plugins.js',
-        'test/unit/flash.js',
-        'test/unit/api.js',
-        'test/unit/menu.js',
-        'test/unit/tracks/tracks.js',
-        'test/unit/tracks/text-track.js',
-        'test/unit/tracks/text-track-list.js',
-        'test/unit/tracks/text-track-cue-list.js',
-        'test/unit/tracks/text-track-controls.js',
-        'test/unit/tracks/text-track-settings.js'
-      ];
-
-      var projectRoot = '../';
-      var scripts = [];
-
-      window.loadScripts = function(scripts) {
-        for (var i = 0; i < scripts.length; i++) {
-          document.write("<script src='" + projectRoot + scripts[i] + "'><\/script>" );
-        }
-      }
-
-      // Choose either the raw source and tests
-      // Or the compiled source + tests.
-      // Use ?comiled to use the compiled tests
-      if (QUnit.urlParams.min || QUnit.urlParams.compiled) {
-        window.compiled = true;
-      } else {
-        // Bunyip/Yeti starts tests after it's done loading which can
-        // lead to a double Qunit.start error which reads as
-        // "Uncaught Error: pushFailure() assertion outside test"
-        if (window.$yetify) {
-          QUnit.config.autostart = false;
-        }
-
-        loadScripts(['build/source-loader.js']);
-      }
-
-    })()
-  </script>
-
-</head>
-<body>
-  <div>
-    <h1 id="qunit-header">Video.js Test Suite</h1>
-    <h2 id="qunit-banner"></h2>
-    <div id="qunit-testrunner-toolbar"></div>
-    <h2 id="qunit-userAgent"></h2>
-    <ol id="qunit-tests"></ol>
-    <div id="qunit-fixture"></div>
-  </div>
-
-  <script>
-    // Loading tests before the end to give IE time to load vjs before tests
-    if (!window.compiled) {
-      loadScripts(window.tests);
-    } else {
-      var compiledTests = "build/files/test.minified.video.js";
-      loadScripts([compiledTests]);
-    }
-  </script>
-</body>
-</html>
diff --git a/test/karma-qunit-shim.js b/test/karma-qunit-shim.js
index 6d0742b..18611a8 100644
--- a/test/karma-qunit-shim.js
+++ b/test/karma-qunit-shim.js
@@ -3,4 +3,4 @@ var q = QUnit;
 // This may not be needed anymore, but double check before removing
 var fixture = document.createElement('div');
 fixture.id = 'qunit-fixture';
-document.body.appendChild(fixture);
+document.body.appendChild(fixture);
\ No newline at end of file
diff --git a/test/karma.conf.js b/test/karma.conf.js
index 8741b43..40b9773 100644
--- a/test/karma.conf.js
+++ b/test/karma.conf.js
@@ -1,19 +1,3 @@
-var fs = require('fs');
-var vm = require('vm');
-// var sourceLoader = fs.readFileSync('./build/source-loader.js', 'utf8');
-// var sandbox = {
-//   blockSourceLoading: true,
-//   document: {},
-//   window: {}
-// };
-// var sourceFiles = [];
-//
-//
-// vm.runInNewContext(sourceLoader, sandbox, 'build/source-loader.js');
-// sourceFiles = sandbox.sourceFiles.map(function(src) {
-//   return '../' + src;
-// });
-
 module.exports = function(config) {
   var customLaunchers = {
     chrome_sl: {
@@ -75,9 +59,11 @@ module.exports = function(config) {
     // customLaunchers: customLaunchers,
 
     files: [
-      '../build/files/video-js.css',
+      '../build/temp/video-js.min.css',
       '../test/karma-qunit-shim.js',
-      '../test/unit/**/*.js'
+      '../test/unit/**/*.js',
+      '../build/temp/video.min.js',
+      '../test/api/**/*.js',
     ],
 
     preprocessors: {
diff --git a/test/karma.minified.api.conf.js b/test/karma.minified.api.conf.js
deleted file mode 100644
index badce6e..0000000
--- a/test/karma.minified.api.conf.js
+++ /dev/null
@@ -1,48 +0,0 @@
-module.exports = function(config) {
-  config.set({
-    frameworks: ['qunit'],
-
-    autoWatch: false,
-
-    singleRun: true,
-
-    files: [
-      '../build/files/video-js.min.css',
-      '../test/karma-qunit-shim.js',
-      '../node_modules/sinon/pkg/sinon.js',
-      '../build/files/minified.video.js',
-      '../test/unit/test-helpers.js',
-      '../test/unit/api.js'
-    ],
-
-    plugins: [
-      'karma-qunit',
-      'karma-chrome-launcher',
-      'karma-firefox-launcher',
-      'karma-ie-launcher',
-      'karma-opera-launcher',
-      'karma-phantomjs-launcher',
-      'karma-safari-launcher'
-    ],
-
-    // test results reporter to use
-    // possible values: 'dots', 'progress', 'junit'
-    reporters: ['dots'],
-
-    // web server port
-    port: 9876,
-
-    // cli runner port
-    runnerPort: 9100,
-
-    // enable / disable colors in the output (reporters and logs)
-    colors: true,
-
-    // level of logging
-    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
-    logLevel: config.LOG_INFO,
-
-    // If browser does not capture in given timeout [ms], kill it
-    captureTimeout: 60000
-  });
-};
diff --git a/test/karma.minified.conf.js b/test/karma.minified.conf.js
deleted file mode 100644
index 46a0d9a..0000000
--- a/test/karma.minified.conf.js
+++ /dev/null
@@ -1,46 +0,0 @@
-module.exports = function(config) {
-  config.set({
-    frameworks: ['qunit'],
-
-    autoWatch: false,
-
-    singleRun: true,
-
-    files: [
-      '../build/files/video-js.min.css',
-      '../test/karma-qunit-shim.js',
-      '../node_modules/sinon/pkg/sinon.js',
-      '../build/files/test.minified.video.js'
-    ],
-
-    plugins: [
-      'karma-qunit',
-      'karma-chrome-launcher',
-      'karma-firefox-launcher',
-      'karma-ie-launcher',
-      'karma-opera-launcher',
-      'karma-phantomjs-launcher',
-      'karma-safari-launcher'
-    ],
-
-    // test results reporter to use
-    // possible values: 'dots', 'progress', 'junit'
-    reporters: ['dots'],
-
-    // web server port
-    port: 9876,
-
-    // cli runner port
-    runnerPort: 9100,
-
-    // enable / disable colors in the output (reporters and logs)
-    colors: true,
-
-    // level of logging
-    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
-    logLevel: config.LOG_INFO,
-
-    // If browser does not capture in given timeout [ms], kill it
-    captureTimeout: 60000
-  });
-};
diff --git a/test/minified-api.html b/test/minified-api.html
deleted file mode 100644
index 3246f62..0000000
--- a/test/minified-api.html
+++ /dev/null
@@ -1,63 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Video.js Test Suite</title>
-
-  <!-- Sinon -->
-  <script src="../node_modules/sinon/pkg/sinon.js"></script>
-  <script src="../node_modules/sinon/pkg/sinon-ie.js"></script>
-
-  <!-- QUnit -->
-  <link rel="stylesheet" href="../node_modules/qunitjs/qunit/qunit.css" />
-  <script src="../node_modules/qunitjs/qunit/qunit.js"></script>
-
-  <!-- Video.js CSS -->
-  <link rel="stylesheet" href="../build/files/video-js.css" type="text/css">
-
-  <!-- LIB COMPILED WITH NOT COMPILED TESTS
-    Check publicly available APIs against compiled lib
-  -->
-  <script type="text/javascript">
-
-    // Test AMD Support
-    // window.define = function(a, b){
-    //   console.log(a, b);
-    // };
-    // define.amd = true;
-
-    // Test CJS Support
-    // Have to remove qunit and tests to because module becomes a function
-    // window.module = window.exports = {};
-    // window.module.exports = {};
-
-    (function(){
-
-      // ADD NEW TEST FILES HERE
-      var tests = [
-        'test/unit/test-helpers.js',
-        'test/unit/api.js'
-      ];
-      var projectRoot = '../';
-      var scripts = [];
-
-      scripts = scripts.concat(['build/files/minified.video.js'], tests);
-
-      for (var i = 0; i < scripts.length; i++) {
-        document.write( "<script src='" + projectRoot + scripts[i] + "'><\/script>" );
-      }
-
-    })()
-  </script>
-
-</head>
-<body>
-  <div>
-    <h1 id="qunit-header">Video.js Test Suite</h1>
-    <h2 id="qunit-banner"></h2>
-    <div id="qunit-testrunner-toolbar"></div>
-    <h2 id="qunit-userAgent"></h2>
-    <ol id="qunit-tests"></ol>
-    <div id="qunit-fixture"></div>
-  </div>
-</body>
-</html>
diff --git a/test/minified.html b/test/minified.html
deleted file mode 100644
index d57ef64..0000000
--- a/test/minified.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Video.js Test Suite</title>
-
-  <!-- Video.js CSS -->
-  <link rel="stylesheet" href="../build/files/video-js.css" type="text/css">
-
-  <!-- Sinon -->
-  <script src="../node_modules/sinon/pkg/sinon.js"></script>
-  <script src="../node_modules/sinon/pkg/sinon-ie.js"></script>
-
-  <!-- QUnit -->
-  <link rel="stylesheet" href="../node_modules/qunitjs/qunit/qunit.css" />
-  <script src="../node_modules/qunitjs/qunit/qunit.js"></script>
-
-  <!-- SOURCE COMPILED WITH TESTS
-  grunt-contrib-qunit doesn't support query vars, so making this script
-  so we can automatically run compiled tests too.
-  (in test/index.html you can add ?compiled to also run compiled tests)
-  -->
-  <script src="../build/files/test.minified.video.js"></script>
-
-</head>
-<body>
-  <div>
-    <h1 id="qunit-header">Video.js Test Suite</h1>
-    <h2 id="qunit-banner"></h2>
-    <div id="qunit-testrunner-toolbar"></div>
-    <h2 id="qunit-userAgent"></h2>
-    <ol id="qunit-tests"></ol>
-    <div id="qunit-fixture"></div>
-  </div>
-</body>
-</html>
diff --git a/test/unit/api.js b/test/unit/api.js
deleted file mode 100644
index 9a1b086..0000000
--- a/test/unit/api.js
+++ /dev/null
@@ -1,243 +0,0 @@
-import videojs from '../../src/js/video.js';
-import TestHelpers from './test-helpers.js';
-import document from 'global/document';
-
-q.module('Player Minified');
-
-test('should be able to access expected player API methods', function() {
-  var player = TestHelpers.makePlayer();
-
-  // Native HTML5 Methods
-  ok(player.error, 'error exists');
-  ok(player.src, 'src exists');
-  ok(player.currentSrc, 'currentSrc exists');
-  ok(player.buffered, 'buffered exists');
-  ok(player.load, 'load exists');
-  ok(player.seeking, 'seeking exists');
-  ok(player.currentTime, 'currentTime exists');
-  ok(player.duration, 'duration exists');
-  ok(player.paused, 'paused exists');
-  ok(player.ended, 'ended exists');
-  ok(player.autoplay, 'autoplay exists');
-  ok(player.loop, 'loop exists');
-  ok(player.play , 'play exists');
-  ok(player.pause , 'pause exists');
-  ok(player.controls, 'controls exists');
-  ok(player.volume, 'volume exists');
-  ok(player.muted, 'muted exists');
-  ok(player.width, 'width exists');
-  ok(player.height, 'height exists');
-  ok(player.poster, 'poster exists');
-  ok(player.textTracks, 'textTracks exists');
-  ok(player.requestFullscreen, 'requestFullscreen exists');
-  ok(player.exitFullscreen, 'exitFullscreen exists');
-  ok(player.playbackRate, 'playbackRate exists');
-  ok(player.networkState, 'networkState exists');
-  ok(player.readyState, 'readyState exists');
-
-  // Unsupported Native HTML5 Methods
-  // ok(player.canPlayType, 'canPlayType exists');
-  // ok(player.startTime, 'startTime exists');
-  // ok(player.defaultPlaybackRate, 'defaultPlaybackRate exists');
-  // ok(player.playbackRate, 'playbackRate exists');
-  // ok(player.played, 'played exists');
-  // ok(player.seekable, 'seekable exists');
-  // ok(player.videoWidth, 'videoWidth exists');
-  // ok(player.videoHeight, 'videoHeight exists');
-
-  // Additional player methods
-  ok(player.bufferedPercent, 'bufferedPercent exists');
-  ok(player.reportUserActivity, 'reportUserActivity exists');
-  ok(player.userActive, 'userActive exists');
-  ok(player.usingNativeControls, 'usingNativeControls exists');
-  ok(player.isFullscreen, 'isFullscreen exists');
-
-  // TextTrack methods
-  ok(player.textTracks, 'textTracks exists');
-  ok(player.remoteTextTracks, 'remoteTextTracks exists');
-  ok(player.addTextTrack, 'addTextTrack exists');
-  ok(player.addRemoteTextTrack, 'addRemoteTextTrack exists');
-  ok(player.removeRemoteTextTrack, 'removeRemoteTextTrack exists');
-
-  // Deprecated methods that should still exist
-  ok(player.requestFullScreen, 'requestFullScreen exists');
-  ok(player.isFullScreen, 'isFullScreen exists');
-  ok(player.cancelFullScreen, 'cancelFullScreen exists');
-
-  player.dispose();
-});
-
-test('should be able to access expected component API methods', function() {
-  var comp = videojs.getComponent('Component').create({ id: function(){ return 1; }, reportUserActivity: function(){} });
-
-  // Component methods
-  ok(comp.player, 'player exists');
-  ok(comp.options, 'options exists');
-  ok(comp.init, 'init exists');
-  ok(comp.dispose, 'dispose exists');
-  ok(comp.createEl, 'createEl exists');
-  ok(comp.contentEl, 'contentEl exists');
-  ok(comp.el, 'el exists');
-  ok(comp.addChild, 'addChild exists');
-  ok(comp.getChild, 'getChild exists');
-  ok(comp.getChildById, 'getChildById exists');
-  ok(comp.children, 'children exists');
-  ok(comp.initChildren, 'initChildren exists');
-  ok(comp.removeChild, 'removeChild exists');
-  ok(comp.on, 'on exists');
-  ok(comp.off, 'off exists');
-  ok(comp.one, 'one exists');
-  ok(comp.trigger, 'trigger exists');
-  ok(comp.triggerReady, 'triggerReady exists');
-  ok(comp.show, 'show exists');
-  ok(comp.hide, 'hide exists');
-  ok(comp.width, 'width exists');
-  ok(comp.height, 'height exists');
-  ok(comp.dimensions, 'dimensions exists');
-  ok(comp.ready, 'ready exists');
-  ok(comp.addClass, 'addClass exists');
-  ok(comp.removeClass, 'removeClass exists');
-  ok(comp.buildCSSClass, 'buildCSSClass exists');
-  ok(comp.setInterval, 'setInterval exists');
-  ok(comp.clearInterval, 'clearInterval exists');
-  ok(comp.setTimeout, 'setTimeout exists');
-  ok(comp.clearTimeout, 'clearTimeout exists');
-});
-
-test('should be able to access expected MediaTech API methods', function() {
-  var media = videojs.getComponent('MediaTechController');
-  var mediaProto = media.prototype;
-  var html5 = videojs.getComponent('Html5');
-  var html5Proto = html5.prototype;
-  var flash = videojs.getComponent('Flash');
-  var flashProto = flash.prototype;
-
-  ok(mediaProto.setPoster, 'setPoster should exist on the Media tech');
-  ok(html5Proto.setPoster, 'setPoster should exist on the HTML5 tech');
-  ok(flashProto.setPoster, 'setPoster should exist on the Flash tech');
-
-  ok(html5.patchCanPlayType, 'patchCanPlayType should exist for HTML5');
-  ok(html5.unpatchCanPlayType, 'unpatchCanPlayType should exist for HTML5');
-
-  // Source Handler Functions
-  ok(media.withSourceHandlers, 'withSourceHandlers should exist for Media Tech');
-
-  ok(html5.canPlaySource, 'canPlaySource should exist for HTML5');
-  ok(html5.registerSourceHandler, 'registerSourceHandler should exist for Html5');
-  ok(html5.selectSourceHandler, 'selectSourceHandler should exist for Html5');
-  ok(html5.prototype.setSource, 'setSource should exist for Html5');
-  ok(html5.prototype.disposeSourceHandler, 'disposeSourceHandler should exist for Html5');
-
-  ok(flash.canPlaySource, 'canPlaySource should exist for Flash');
-  ok(flash.registerSourceHandler, 'registerSourceHandler should exist for Flash');
-  ok(flash.selectSourceHandler, 'selectSourceHandler should exist for Flash');
-  ok(flash.prototype.setSource, 'setSource should exist for Flash');
-  ok(flash.prototype.disposeSourceHandler, 'disposeSourceHandler should exist for Flash');
-});
-
-test('should export ready api call to public', function() {
-  var videoTag = TestHelpers.makeTag();
-
-  var fixture = document.getElementById('qunit-fixture');
-  fixture.appendChild(videoTag);
-
-  var player = videojs('example_1');
-  ok(player.ready !== undefined, 'ready callback is defined');
-  player.dispose();
-});
-
-test('should export useful components to the public', function () {
-  ok(videojs.TOUCH_ENABLED !== undefined, 'Touch detection should be public');
-  ok(videojs.getComponent('ControlBar'), 'ControlBar should be public');
-  ok(videojs.getComponent('Button'), 'Button should be public');
-  ok(videojs.getComponent('PlayToggle'), 'PlayToggle should be public');
-  ok(videojs.getComponent('FullscreenToggle'), 'FullscreenToggle should be public');
-  ok(videojs.getComponent('BigPlayButton'), 'BigPlayButton should be public');
-  ok(videojs.getComponent('LoadingSpinner'), 'LoadingSpinner should be public');
-  ok(videojs.getComponent('CurrentTimeDisplay'), 'CurrentTimeDisplay should be public');
-  ok(videojs.getComponent('DurationDisplay'), 'DurationDisplay should be public');
-  ok(videojs.getComponent('TimeDivider'), 'TimeDivider should be public');
-  ok(videojs.getComponent('RemainingTimeDisplay'), 'RemainingTimeDisplay should be public');
-  ok(videojs.getComponent('Slider'), 'Slider should be public');
-  ok(videojs.getComponent('ProgressControl'), 'ProgressControl should be public');
-  ok(videojs.getComponent('SeekBar'), 'SeekBar should be public');
-  ok(videojs.getComponent('LoadProgressBar'), 'LoadProgressBar should be public');
-  ok(videojs.getComponent('PlayProgressBar'), 'PlayProgressBar should be public');
-  ok(videojs.getComponent('SeekHandle'), 'SeekHandle should be public');
-  ok(videojs.getComponent('VolumeControl'), 'VolumeControl should be public');
-  ok(videojs.getComponent('VolumeBar'), 'VolumeBar should be public');
-  ok(videojs.getComponent('VolumeLevel'), 'VolumeLevel should be public');
-  ok(videojs.getComponent('VolumeMenuButton'), 'VolumeMenuButton should be public');
-  ok(videojs.getComponent('VolumeHandle'), 'VolumeHandle should be public');
-  ok(videojs.getComponent('MuteToggle'), 'MuteToggle should be public');
-  ok(videojs.getComponent('PosterImage'), 'PosterImage should be public');
-  ok(videojs.getComponent('Menu'), 'Menu should be public');
-  ok(videojs.getComponent('MenuItem'), 'MenuItem should be public');
-  ok(videojs.getComponent('MenuButton'), 'MenuButton should be public');
-  ok(videojs.getComponent('PlaybackRateMenuButton'), 'PlaybackRateMenuButton should be public');
-
-  ok(videojs.getComponent('CaptionSettingsMenuItem'), 'CaptionSettingsMenuItem should be public');
-  ok(videojs.getComponent('OffTextTrackMenuItem'), 'OffTextTrackMenuItem should be public');
-  ok(videojs.getComponent('TextTrackMenuItem'), 'TextTrackMenuItem should be public');
-  ok(videojs.getComponent('TextTrackDisplay'), 'TextTrackDisplay should be public');
-  ok(videojs.getComponent('TextTrackButton'), 'TextTrackButton should be public');
-  ok(videojs.getComponent('CaptionsButton'), 'CaptionsButton should be public');
-  ok(videojs.getComponent('SubtitlesButton'), 'SubtitlesButton should be public');
-  ok(videojs.getComponent('ChaptersButton'), 'ChaptersButton should be public');
-  ok(videojs.getComponent('ChaptersTrackMenuItem'), 'ChaptersTrackMenuItem should be public');
-
-  ok(videojs.util, 'util namespace should be public');
-  ok(videojs.util.mergeOptions, 'mergeOptions should be public');
-});
-
-test('should be able to initialize player twice on the same tag using string reference', function() {
-  var videoTag = TestHelpers.makeTag();
-  var id = videoTag.id;
-
-  var fixture = document.getElementById('qunit-fixture');
-  fixture.appendChild(videoTag);
-
-  var player = videojs('example_1');
-  player.dispose();
-  ok(!document.getElementById(id), 'element is removed');
-
-  videoTag = TestHelpers.makeTag();
-  fixture.appendChild(videoTag);
-
-  player = videojs('example_1');
-  player.dispose();
-});
-
-test('videojs.players should be available after minification', function() {
-  var videoTag = TestHelpers.makeTag();
-  var id = videoTag.id;
-
-  var fixture = document.getElementById('qunit-fixture');
-  fixture.appendChild(videoTag);
-
-  var player = videojs(id);
-  ok(videojs.players[id] === player, 'videojs.players is available');
-
-  player.dispose();
-});
-
-test('component can be subclassed externally', function(){
-  var Component = videojs.getComponent('Component');
-  var ControlBar = videojs.getComponent('ControlBar');
-
-  var player = new (Component.extend({
-    languages: function(){},
-    reportUserActivity: function(){},
-    language: function(){},
-    textTracks: function(){ return {
-        addEventListener: Function.prototype,
-        removeEventListener: Function.prototype
-      };
-    }
-  }))({
-    id: function(){},
-    reportUserActivity: function(){}
-  });
-
-  ok(new ControlBar(player), 'created a control bar without throwing');
-});
diff --git a/test/unit/poster.js b/test/unit/poster.js
index b35815b..a0a0091 100644
--- a/test/unit/poster.js
+++ b/test/unit/poster.js
@@ -93,7 +93,7 @@ test('should hide the poster in the appropriate player states', function(){
   fixture.appendChild(playerDiv);
 
   playerDiv.className = 'video-js vjs-has-started';
-  equal(TestHelpers.getComputedStyle(el, 'display'), 'none', 'The poster hides when the video has started');
+  equal(TestHelpers.getComputedStyle(el, 'display'), 'none', 'The poster hides when the video has started (CSS may not be loaded)');
 
   playerDiv.className = 'video-js vjs-has-started vjs-audio';
   equal(TestHelpers.getComputedStyle(el, 'display'), 'block', 'The poster continues to show when playing audio');
diff --git a/test/unit/test-helpers.js b/test/unit/test-helpers.js
index 784d8e0..d9cae79 100644
--- a/test/unit/test-helpers.js
+++ b/test/unit/test-helpers.js
@@ -39,4 +39,4 @@ var TestHelpers = {
   }
 };
 
-module.exports = TestHelpers;
\ No newline at end of file
+export default TestHelpers;
\ No newline at end of file
