Merge branch 'MDL-56943-master' of git://github.com/danpoltawski/moodle
authorAndrew Nicols <andrew@nicols.co.uk>
Mon, 21 Nov 2016 02:54:09 +0000 (10:54 +0800)
committerAndrew Nicols <andrew@nicols.co.uk>
Mon, 21 Nov 2016 02:54:09 +0000 (10:54 +0800)
176 files changed:
admin/tool/behat/cli/run.php
admin/tool/lp/classes/external/cohort_summary_exporter.php
admin/tool/lp/classes/output/manage_competency_frameworks_page.php
admin/tool/lp/lang/en/tool_lp.php
admin/tool/lpmigrate/tests/processor_test.php
admin/tool/mobile/classes/api.php
admin/tool/mobile/tests/externallib_test.php
admin/upgradesettings.php
backup/moodle2/restore_stepslib.php
backup/util/ui/tests/behat/behat_backup.php
blocks/course_summary/block_course_summary.php
blocks/login/block_login.php
composer.json
composer.lock
course/lib.php
course/renderer.php
course/tests/behat/category_management.feature
course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander-debug.js
course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander-min.js
course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander.js
course/yui/src/categoryexpander/js/categoryexpander.js
enrol/manual/yui/quickenrolment/assets/skins/sam/quickenrolment.css
enrol/manual/yui/quickenrolment/quickenrolment.js
enrol/meta/locallib.php
enrol/yui/rolemanager/rolemanager.js
index.php
install/lang/cs/install.php
install/lang/da/install.php
lang/en/cache.php
lang/en/error.php
lang/en/message.php
lib/accesslib.php
lib/ajax/service.php
lib/amd/build/backoff_timer.min.js [new file with mode: 0644]
lib/amd/src/backoff_timer.js [new file with mode: 0644]
lib/classes/upgrade/util.php
lib/configonlylib.php
lib/coursecatlib.php
lib/db/caches.php
lib/editor/atto/plugins/image/styles.css
lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js
lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js
lib/editor/atto/yui/src/editor/js/editor.js
lib/editor/tinymce/styles.css
lib/messagelib.php
lib/moodlelib.php
lib/navigationlib.php
lib/outputfactories.php
lib/outputlib.php
lib/questionlib.php
lib/setup.php
lib/templates/popover_region.mustache
lib/tests/coursecatlib_test.php
lib/tests/upgrade_util_test.php
lib/webdavlib.php
media/player/videojs/amd/build/Youtube.min.js
media/player/videojs/amd/build/video.min.js
media/player/videojs/amd/src/Youtube.js
media/player/videojs/amd/src/video.js
media/player/videojs/classes/plugin.php
media/player/videojs/readme_moodle.txt
media/player/videojs/styles.css
media/player/videojs/thirdpartylibs.xml
media/player/videojs/videojs/lang/de.js
media/player/videojs/videojs/lang/el.js
media/player/videojs/videojs/lang/en.js
media/player/videojs/videojs/lang/fr.js
media/player/videojs/videojs/video-js.swf [moved from media/player/videojs/video-js.swf with 100% similarity]
message/amd/build/message_area.min.js
message/amd/build/message_area_contacts.min.js
message/amd/build/message_area_messages.min.js
message/amd/build/message_area_search.min.js
message/amd/src/message_area.js
message/amd/src/message_area_contacts.js
message/amd/src/message_area_messages.js
message/amd/src/message_area_search.js
message/classes/api.php
message/classes/helper.php
message/classes/output/messagearea/message.php
message/classes/output/messagearea/message_area.php
message/classes/time_last_message_between_users.php [new file with mode: 0644]
message/externallib.php
message/index.php
message/lib.php
message/output/airnotifier/message_output_airnotifier.php
message/output/airnotifier/style.css [deleted file]
message/output/airnotifier/yui/build/moodle-message_airnotifier-toolboxes/moodle-message_airnotifier-toolboxes-debug.js
message/output/airnotifier/yui/build/moodle-message_airnotifier-toolboxes/moodle-message_airnotifier-toolboxes-min.js
message/output/airnotifier/yui/build/moodle-message_airnotifier-toolboxes/moodle-message_airnotifier-toolboxes.js
message/output/airnotifier/yui/src/toolboxes/js/toolboxes.js
message/output/popup/lib.php
message/output/popup/templates/message_popover.mustache
message/templates/message_area.mustache
message/templates/message_area_contacts_area.mustache
message/templates/message_area_messages_area.mustache
message/tests/api_test.php
message/tests/behat/search_messages.feature
message/tests/externallib_test.php
mod/assign/amd/build/grading_panel.min.js
mod/assign/amd/src/grading_panel.js
mod/assign/feedback/editpdf/fpdi/fpdi_bridge.php
mod/assign/feedback/editpdf/fpdi/readme_moodle.txt
mod/assign/locallib.php
mod/chat/lang/en/chat.php
mod/chat/lib.php
mod/chat/tests/behat/chat_calendar_events.feature [new file with mode: 0644]
mod/chat/view.php
mod/lti/view.php
mod/quiz/classes/output/edit_renderer.php
mod/quiz/lib.php
mod/quiz/renderer.php
mod/quiz/report/attemptsreport_table.php
mod/quiz/report/overview/overview_table.php
mod/quiz/report/overview/report.php
mod/wiki/locallib.php
question/classes/bank/search/category_condition.php
question/classes/bank/view.php
question/preview.php
question/type/calculated/questiontype.php
question/type/ddmarker/yui/build/moodle-qtype_ddmarker-form/moodle-qtype_ddmarker-form-debug.js
question/type/ddmarker/yui/build/moodle-qtype_ddmarker-form/moodle-qtype_ddmarker-form-min.js
question/type/ddmarker/yui/build/moodle-qtype_ddmarker-form/moodle-qtype_ddmarker-form.js
question/type/ddmarker/yui/src/form/js/form.js
question/type/gapselect/renderer.php
question/type/match/renderer.php
question/type/multianswer/renderer.php
question/type/multianswer/tests/walkthrough_test.php
question/type/multichoice/renderer.php
question/type/numerical/renderer.php
question/type/shortanswer/renderer.php
question/type/truefalse/renderer.php
question/yui/build/moodle-question-preview/moodle-question-preview-debug.js
question/yui/build/moodle-question-preview/moodle-question-preview-min.js
question/yui/build/moodle-question-preview/moodle-question-preview.js
question/yui/src/preview/js/preview.js
repository/webdav/lib.php
theme/boost/amd/build/form-display-errors.min.js
theme/boost/amd/build/loader.min.js
theme/boost/amd/src/form-display-errors.js
theme/boost/amd/src/loader.js
theme/boost/classes/output/core_renderer.php
theme/boost/config.php
theme/boost/lib.php
theme/boost/readme_moodle.txt
theme/boost/scss/bootstrap/_popover.scss
theme/boost/scss/moodle/admin.scss
theme/boost/scss/moodle/blocks.scss
theme/boost/scss/moodle/buttons.scss
theme/boost/scss/moodle/calendar.scss
theme/boost/scss/moodle/core.scss
theme/boost/scss/moodle/course.scss
theme/boost/scss/moodle/expendable.scss
theme/boost/scss/moodle/filemanager.scss
theme/boost/scss/moodle/forms.scss
theme/boost/scss/moodle/icons.scss
theme/boost/scss/moodle/modules.scss
theme/boost/scss/moodle/popover-region.scss
theme/boost/scss/moodle/question.scss
theme/boost/scss/moodle/undo.scss
theme/boost/scss/moodle/user.scss
theme/boost/templates/core/filemanager_fileselect.mustache
theme/boost/templates/flat_navigation.mustache
theme/boost/templates/login.mustache
theme/boost/templates/maintenance.mustache
theme/bootstrapbase/less/moodle/bs4-compat.less
theme/bootstrapbase/less/moodle/buttons.less
theme/bootstrapbase/less/moodle/core.less
theme/bootstrapbase/less/moodle/forms.less
theme/bootstrapbase/less/moodle/popover_region.less
theme/bootstrapbase/less/moodle/undo.less
theme/bootstrapbase/style/moodle.css
theme/clean/style/custom.css
theme/more/style/custom.css
theme/upgrade.txt
version.php

index 2775a5f..a228caf 100644 (file)
@@ -26,7 +26,6 @@ if (isset($_SERVER['REMOTE_ADDR'])) {
     die(); // No access from web!
 }
 
-define('BEHAT_UTIL', true);
 define('CLI_SCRIPT', true);
 define('ABORT_AFTER_CONFIG', true);
 define('CACHE_DISABLE_ALL', true);
index a22ac9f..cd00a6f 100644 (file)
@@ -50,7 +50,8 @@ class cohort_summary_exporter extends \core_competency\external\exporter {
             ),
             'idnumber' => array(
                 'type' => PARAM_RAW,        // ID numbers are plain text.
-                'default' => ''
+                'default' => '',
+                'null' => NULL_ALLOWED
             ),
             'visible' => array(
                 'type' => PARAM_BOOL,
index 3864cbf..7a2181b 100644 (file)
@@ -74,6 +74,12 @@ class manage_competency_frameworks_page implements renderable, templatable {
                 'get'
             );
             $this->navigation[] = $addpage;
+            $competenciesrepository = new single_button(
+                new moodle_url('https://moodle.net/competencies'),
+                get_string('competencyframeworksrepository', 'tool_lp'),
+                'get'
+            );
+            $this->navigation[] = $competenciesrepository;
         }
 
         $this->competencyframeworks = api::list_frameworks('shortname', 'ASC', 0, 0, $this->pagecontext);
index 412419d..4719b6c 100644 (file)
@@ -61,6 +61,7 @@ $string['competencyframeworkcreated'] = 'Competency framework created.';
 $string['competencyframeworkname'] = 'Name';
 $string['competencyframeworkroot'] = 'No parent (top-level competency)';
 $string['competencyframeworks'] = 'Competency frameworks';
+$string['competencyframeworksrepository'] = 'Competency frameworks repository';
 $string['competencyframeworkupdated'] = 'Competency framework updated.';
 $string['competencyoutcome_complete'] = 'Mark as complete';
 $string['competencyoutcome_evidence'] = 'Attach an evidence';
index 792a902..78eb45b 100644 (file)
@@ -396,12 +396,8 @@ class tool_lpmigrate_framework_processor_testcase extends advanced_testcase {
     }
 
     public function test_permission_exception() {
-        global $DB;
-        if ($DB->get_dbfamily() === 'postgres' or $DB->get_dbfamily() === 'mssql') {
-            $this->markTestSkipped('The processor is having issues with the transaction initialised in '
-                . 'advanced_testcase::runBare().');
-            return;
-        }
+
+        $this->preventResetByRollback(); // Test uses transactions, so we cannot use them for speedy reset.
 
         $dg = $this->getDataGenerator();
         $u = $dg->create_user();
index e25a57b..da4055e 100644 (file)
@@ -31,7 +31,7 @@ use moodle_url;
 use moodle_exception;
 
 /**
- * API exposed by tool_mobile
+ * API exposed by tool_mobile, to be used mostly by external functions.
  *
  * @copyright  2016 Juan Leyva
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
@@ -106,6 +106,8 @@ class api {
         // We need this to make work the format text functions.
         $PAGE->set_context($context);
 
+        list($authinstructions, $notusedformat) = external_format_text($CFG->auth_instructions, FORMAT_MOODLE, $context->id);
+        list($maintenancemessage, $notusedformat) = external_format_text($CFG->maintenance_message, FORMAT_MOODLE, $context->id);
         $settings = array(
             'wwwroot' => $CFG->wwwroot,
             'httpswwwroot' => $CFG->httpswwwroot,
@@ -115,12 +117,12 @@ class api {
             'authloginviaemail' => $CFG->authloginviaemail,
             'registerauth' => $CFG->registerauth,
             'forgottenpasswordurl' => $CFG->forgottenpasswordurl,
-            'authinstructions' => format_text($CFG->auth_instructions),
+            'authinstructions' => $authinstructions,
             'authnoneenabled' => (int) is_enabled_auth('none'),
             'enablewebservices' => $CFG->enablewebservices,
             'enablemobilewebservice' => $CFG->enablemobilewebservice,
             'maintenanceenabled' => $CFG->maintenance_enabled,
-            'maintenancemessage' => format_text($CFG->maintenance_message),
+            'maintenancemessage' => $maintenancemessage,
         );
 
         $typeoflogin = get_config('tool_mobile', 'typeoflogin');
@@ -162,9 +164,15 @@ class api {
         if (empty($section) or $section == 'frontpagesettings') {
             require_once($CFG->dirroot . '/course/format/lib.php');
             // First settings that anyone can deduce.
-            $settings->fullname = $SITE->fullname;
-            $settings->shortname = $SITE->shortname;
-            $settings->summary = $SITE->summary;
+            $settings->fullname = external_format_string($SITE->fullname, $context->id);
+            $settings->shortname = external_format_string($SITE->shortname, $context->id);
+
+            // Return to a var instead of directly to $settings object because of differences between
+            // list() in php5 and php7. {@link http://php.net/manual/en/function.list.php}
+            $formattedsummary = external_format_text($SITE->summary, $SITE->summaryformat,
+                                                                                        $context->id);
+            $settings->summary = $formattedsummary[0];
+            $settings->summaryformat = $formattedsummary[1];
             $settings->frontpage = $CFG->frontpage;
             $settings->frontpageloggedin = $CFG->frontpageloggedin;
             $settings->maxcategorydepth = $CFG->maxcategorydepth;
index 010b1c9..fbbca7d 100644 (file)
@@ -64,6 +64,9 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase {
 
         // Test default values.
         $context = context_system::instance();
+        list($authinstructions, $notusedformat) = external_format_text($CFG->auth_instructions, FORMAT_MOODLE, $context->id);
+        list($maintenancemessage, $notusedformat) = external_format_text($CFG->maintenance_message, FORMAT_MOODLE, $context->id);
+
         $expected = array(
             'wwwroot' => $CFG->wwwroot,
             'httpswwwroot' => $CFG->httpswwwroot,
@@ -73,12 +76,12 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase {
             'authloginviaemail' => $CFG->authloginviaemail,
             'registerauth' => $CFG->registerauth,
             'forgottenpasswordurl' => $CFG->forgottenpasswordurl,
-            'authinstructions' => format_text($CFG->auth_instructions),
+            'authinstructions' => $authinstructions,
             'authnoneenabled' => (int) is_enabled_auth('none'),
             'enablewebservices' => $CFG->enablewebservices,
             'enablemobilewebservice' => $CFG->enablemobilewebservice,
             'maintenanceenabled' => $CFG->maintenance_enabled,
-            'maintenancemessage' => format_text($CFG->maintenance_message),
+            'maintenancemessage' => $maintenancemessage,
             'typeoflogin' => api::LOGIN_VIA_APP,
             'warnings' => array()
         );
@@ -92,8 +95,9 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase {
         set_config('logo', 'mock.png', 'core_admin');
         set_config('logocompact', 'mock.png', 'core_admin');
 
+        list($authinstructions, $notusedformat) = external_format_text($authinstructions, FORMAT_MOODLE, $context->id);
         $expected['registerauth'] = 'email';
-        $expected['authinstructions'] = format_text($authinstructions);
+        $expected['authinstructions'] = $authinstructions;
         $expected['typeoflogin'] = api::LOGIN_VIA_BROWSER;
         $expected['launchurl'] = "$CFG->wwwroot/$CFG->admin/tool/mobile/launch.php";
 
@@ -120,12 +124,16 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase {
         $result = external::get_config();
         $result = external_api::clean_returnvalue(external::get_config_returns(), $result);
 
+        // SITE summary is null in phpunit which gets transformed to an empty string by format_text.
+        list($sitesummary, $unused) = external_format_text($SITE->summary, $SITE->summaryformat, context_system::instance()->id);
+
         // Test default values.
         $context = context_system::instance();
         $expected = array(
             array('name' => 'fullname', 'value' => $SITE->fullname),
             array('name' => 'shortname', 'value' => $SITE->shortname),
-            array('name' => 'summary', 'value' => $SITE->summary),
+            array('name' => 'summary', 'value' => $sitesummary),
+            array('name' => 'summaryformat', 'value' => FORMAT_HTML),
             array('name' => 'frontpage', 'value' => $CFG->frontpage),
             array('name' => 'frontpageloggedin', 'value' => $CFG->frontpageloggedin),
             array('name' => 'maxcategorydepth', 'value' => $CFG->maxcategorydepth),
@@ -141,9 +149,9 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase {
 
         // Change a value and retrieve filtering by section.
         set_config('commentsperpage', 1);
-        $expected[9]['value'] = 1;
-        unset($expected[10]);
+        $expected[10]['value'] = 1;
         unset($expected[11]);
+        unset($expected[12]);
 
         $result = external::get_config('frontpagesettings');
         $result = external_api::clean_returnvalue(external::get_config_returns(), $result);
index 38a8f06..eecf91a 100644 (file)
@@ -67,7 +67,7 @@ echo '<fieldset>';
 echo '<div class="clearer"><!-- --></div>';
 echo $newsettingshtml;
 echo '</fieldset>';
-echo '<div class="form-buttons"><input class="form-submit" type="submit" value="'.get_string('savechanges','admin').'" /></div>';
+echo '<div class="form-buttons"><input class="form-submit btn btn-primary" type="submit" value="'.get_string('savechanges','admin').'" /></div>';
 echo '</div>';
 echo '</form>';
 
index d303ba1..e07b50f 100644 (file)
@@ -4331,6 +4331,14 @@ class restore_create_categories_and_questions extends restore_structure_step {
         }
         $data->contextid = $mapping->parentitemid;
 
+        // Before 3.1, the 'stamp' field could be erroneously duplicated.
+        // From 3.1 onwards, there's a unique index of (contextid, stamp).
+        // If we encounter a duplicate in an old restore file, just generate a new stamp.
+        // This is the same as what happens during an upgrade to 3.1+ anyway.
+        if ($DB->record_exists('question_categories', ['stamp' => $data->stamp, 'contextid' => $data->contextid])) {
+            $data->stamp = make_unique_id_code();
+        }
+
         // Let's create the question_category and save mapping
         $newitemid = $DB->insert_record('question_categories', $data);
         $this->set_mapping('question_category', $oldid, $newitemid);
index ef8aada..d4ffb03 100644 (file)
@@ -247,7 +247,7 @@ class behat_backup extends behat_base {
 
         // The first category in the list.
         $radionode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-new-course ')]" .
-            "/descendant::div[@class='restore-course-search']" .
+            "/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' restore-course-search ')]" .
             "/descendant::input[@type='radio']");
         $radionode->click();
 
index 858987b..516c360 100644 (file)
@@ -65,14 +65,6 @@ class block_course_summary extends block_base {
         $context = context_course::instance($this->page->course->id);
         $this->page->course->summary = file_rewrite_pluginfile_urls($this->page->course->summary, 'pluginfile.php', $context->id, 'course', 'summary', NULL);
         $this->content->text = format_text($this->page->course->summary, $this->page->course->summaryformat, $options);
-        if ($this->page->user_is_editing()) {
-            if($this->page->course->id == SITEID) {
-                $editpage = $CFG->wwwroot.'/'.$CFG->admin.'/settings.php?section=frontpagesettings';
-            } else {
-                $editpage = $CFG->wwwroot.'/course/edit.php?id='.$this->page->course->id;
-            }
-            $this->content->text .= "<div class=\"editbutton\"><a href=\"$editpage\"><img src=\"" . $OUTPUT->pix_url('t/edit') . "\" alt=\"".get_string('edit')."\" /></a></div>";
-        }
         $this->content->footer = '';
 
         return $this->content;
index 47f8588..26ca859 100644 (file)
@@ -78,20 +78,26 @@ class block_login extends block_base {
 
             $this->content->text .= "\n".'<form class="loginform" id="login" method="post" action="'.get_login_url().'" '.$autocomplete.'>';
 
-            $this->content->text .= '<div class="c1 fld username"><label for="login_username">'.$strusername.'</label>';
-            $this->content->text .= '<input type="text" name="username" id="login_username" value="'.s($username).'" /></div>';
+            $this->content->text .= '<div class="form-group"><label for="login_username">'.$strusername.'</label>';
+            $this->content->text .= '<input type="text" name="username" id="login_username" class="form-control" value="'.s($username).'" /></div>';
 
-            $this->content->text .= '<div class="c1 fld password"><label for="login_password">'.get_string('password').'</label>';
+            $this->content->text .= '<div class="form-group"><label for="login_password">'.get_string('password').'</label>';
 
-            $this->content->text .= '<input type="password" name="password" id="login_password" value="" '.$autocomplete.' /></div>';
+            $this->content->text .= '<input type="password" name="password" id="login_password" class="form-control" value="" '.$autocomplete.' /></div>';
 
             if (isset($CFG->rememberusername) and $CFG->rememberusername == 2) {
                 $checked = $username ? 'checked="checked"' : '';
-                $this->content->text .= '<div class="c1 rememberusername"><input type="checkbox" name="rememberusername" id="rememberusername" value="1" '.$checked.'/>';
-                $this->content->text .= ' <label for="rememberusername">'.get_string('rememberusername', 'admin').'</label></div>';
+                $this->content->text .= '<div class="form-check">';
+                $this->content->text .= '<label class="form-check-label">';
+                $this->content->text .= '<input type="checkbox" name="rememberusername" id="rememberusername"
+                        class="form-check-input" value="1" '.$checked.'/> ';
+                $this->content->text .= get_string('rememberusername', 'admin').'</label>';
+                $this->content->text .= '</div>';
             }
 
-            $this->content->text .= '<div class="c1 btn"><input type="submit" value="'.get_string('login').'" /></div>';
+            $this->content->text .= '<div class="form-group">';
+            $this->content->text .= '<input type="submit" class="btn btn-primary btn-block" value="'.get_string('login').'" />';
+            $this->content->text .= '</div>';
 
             $this->content->text .= "</form>\n";
 
index 3f7df7f..bb8421f 100644 (file)
@@ -7,6 +7,6 @@
     "require-dev": {
         "phpunit/phpunit": "5.5.*",
         "phpunit/dbUnit": "1.4.*",
-        "moodlehq/behat-extension": "3.32.3"
+        "moodlehq/behat-extension": "3.32.4"
     }
 }
index 1e231a8..ae338bf 100644 (file)
@@ -4,8 +4,8 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
         "This file is @generated automatically"
     ],
-    "hash": "ec5f1e9d8b8134cf6a4490ba5dfe0082",
-    "content-hash": "583f9a915721de799118a396dc81f177",
+    "hash": "6042e8e9600da0881e3f63b2e79ad742",
+    "content-hash": "59c508468d7f10d1a16b4d24228e7eab",
     "packages": [],
     "packages-dev": [
         {
         },
         {
             "name": "moodlehq/behat-extension",
-            "version": "v3.32.3",
+            "version": "v3.32.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/moodlehq/moodle-behat-extension.git",
-                "reference": "8296916088e62f7cdf67b34abeebd419b301caff"
+                "reference": "d363b92f62770acdd8cd878810777f3a61eada4d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/moodlehq/moodle-behat-extension/zipball/8296916088e62f7cdf67b34abeebd419b301caff",
-                "reference": "8296916088e62f7cdf67b34abeebd419b301caff",
+                "url": "https://api.github.com/repos/moodlehq/moodle-behat-extension/zipball/d363b92f62770acdd8cd878810777f3a61eada4d",
+                "reference": "d363b92f62770acdd8cd878810777f3a61eada4d",
                 "shasum": ""
             },
             "require": {
                 "Behat",
                 "moodle"
             ],
-            "time": "2016-10-03 03:33:07"
+            "time": "2016-11-10 23:36:48"
         },
         {
             "name": "myclabs/deep-copy",
index 926c1ae..238da62 100644 (file)
@@ -3607,6 +3607,7 @@ function course_get_tagged_course_modules($tag, $exclusivemode = false, $fromcon
                 JOIN {course} c ON cm.course = c.id
                 JOIN {context} ctx ON ctx.instanceid = cm.id AND ctx.contextlevel = :coursemodulecontextlevel
                WHERE tt.itemtype = :itemtype AND tt.tagid = :tagid AND tt.component = :component
+                AND cm.deletioninprogress = 0
                 AND c.id %COURSEFILTER% AND cm.id %ITEMFILTER%";
 
     $params = array('itemtype' => 'course_modules', 'tagid' => $tag->id, 'component' => 'core',
@@ -3934,7 +3935,7 @@ function course_check_module_updates_since($cm, $from, $fileareas = array(), $fi
     }
     if (!empty($fileareas) and (empty($filter) or in_array('fileareas', $filter))) {
         $fs = get_file_storage();
-        $files = $fs->get_area_files($context->id, $component, $fileareas, false, "filearea, timemodified DESC", true, $from);
+        $files = $fs->get_area_files($context->id, $component, $fileareas, false, "filearea, timemodified DESC", false, $from);
         foreach ($fileareas as $filearea) {
             $updates->{$filearea . 'files'} = (object) array('updated' => false);
         }
@@ -3992,6 +3993,7 @@ function course_check_module_updates_since($cm, $from, $fileareas = array(), $fi
     // Check comments.
     if (plugin_supports('mod', $cm->modname, FEATURE_COMMENT) and (empty($filter) or in_array('comments', $filter))) {
         $updates->comments = (object) array('updated' => false);
+        require_once($CFG->dirroot . '/comment/lib.php');
         require_once($CFG->dirroot . '/comment/locallib.php');
         $manager = new comment_manager();
         $comments = $manager->get_component_comments_since($course, $context, $component, $from, $cm);
index 96d1690..ff60653 100644 (file)
@@ -1506,14 +1506,11 @@ class core_course_renderer extends plugin_renderer_base {
         if ($coursecat->get_children_count()) {
             $classes = array(
                 'collapseexpand',
-                'collapse-all',
             );
-            if ($chelper->get_subcat_depth() == 1) {
-                $classes[] = 'disabled';
-            }
+
             // Only show the collapse/expand if there are children to expand.
             $content .= html_writer::start_tag('div', array('class' => 'collapsible-actions'));
-            $content .= html_writer::link('#', get_string('collapseall'),
+            $content .= html_writer::link('#', get_string('expandall'),
                     array('class' => implode(' ', $classes)));
             $content .= html_writer::end_tag('div');
             $this->page->requires->strings_for_js(array('collapseall', 'expandall'), 'moodle');
index b90a8f8..5824254 100644 (file)
@@ -348,3 +348,20 @@ Feature: Test category management actions
       | Category ID number | CAT1 |
     When I press "Create category"
     Then I should see "ID number is already used for another category"
+
+  Scenario: Test that is possible to remove an idnumber from a course category
+    Given the following "categories" exist:
+      | name | category | idnumber |
+      | Cat 1 | 0 | CAT1 |
+      | Cat 2 | 0 ||
+    And I log in as "admin"
+    And I go to the courses management page
+    And I should see "CAT1" in the "#category-listing" "css_element"
+    When I click on "edit" action for "Cat 1" in management category listing
+    And I set the following fields to these values:
+      | Category name | Category 1 (edited) |
+      | Category ID number ||
+    And I press "Save changes"
+    # Redirect
+    Then I should see "Category 1 (edited)" in the "#category-listing" "css_element"
+    And I should not see "CAT1" in the "#course-listing" "css_element"
index 92f0c02..e4f866c 100644 (file)
Binary files a/course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander-debug.js and b/course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander-debug.js differ
index 49f5138..f655b42 100644 (file)
Binary files a/course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander-min.js and b/course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander-min.js differ
index b7e1e9b..38a5b34 100644 (file)
Binary files a/course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander.js and b/course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander.js differ
index a679c79..2176497 100644 (file)
@@ -23,10 +23,12 @@ var CSS = {
         HASCHILDREN: 'with_children'
     },
     SELECTORS = {
+        WITHCHILDRENTREES: '.with_children',
         LOADEDTREES: '.with_children.loaded',
         CONTENTNODE: '.content',
         CATEGORYLISTENLINK: '.category .info .categoryname',
         CATEGORYSPINNERLOCATION: '.categoryname',
+        CATEGORYWITHCOLLAPSEDCHILDREN: '.category.with_children.collapsed',
         CATEGORYWITHCOLLAPSEDLOADEDCHILDREN: '.category.with_children.loaded.collapsed',
         CATEGORYWITHMAXIMISEDLOADEDCHILDREN: '.category.with_children.loaded:not(.collapsed)',
         COLLAPSEEXPAND: '.collapseexpand',
@@ -80,6 +82,58 @@ NS.setup_keyboard_listeners = function() {
     Y.one(Y.config.doc).delegate('key', this.collapse_expand_all, 'enter', SELECTORS.COLLAPSEEXPAND, this);
 };
 
+/**
+ * Expand all categories.
+ *
+ * @method expand_category
+ * @private
+ * @param {Node} categorynode The node to expand
+ */
+NS.expand_category = function(categorynode) {
+    // Load the actual dependencies now that we've been called.
+    Y.use('io-base', 'json-parse', 'moodle-core-notification', 'anim-node-plugin', function() {
+        // Overload the expand_category with the _expand_category function to ensure that
+        // this function isn't called in the future, and call it for the first time.
+        NS.expand_category = NS._expand_category;
+        NS.expand_category(categorynode);
+    });
+};
+
+NS._expand_category = function(categorynode) {
+    var categoryid,
+        depth;
+
+    if (!categorynode.hasClass(CSS.HASCHILDREN)) {
+        // Nothing to do here - this category has no children.
+        return;
+    }
+
+    if (categorynode.hasClass(CSS.LOADED)) {
+        // We've already loaded this content so we just need to toggle the view of it.
+        this.run_expansion(categorynode);
+        return;
+    }
+
+    // We use Data attributes to store the category.
+    categoryid = categorynode.getData('categoryid');
+    depth = categorynode.getData('depth');
+    if (typeof categoryid === "undefined" || typeof depth === "undefined") {
+        return;
+    }
+
+    this._toggle_generic_expansion({
+        parentnode: categorynode,
+        childnode: categorynode.one(SELECTORS.CONTENTNODE),
+        spinnerhandle: SELECTORS.CATEGORYSPINNERLOCATION,
+        data: {
+            categoryid: categoryid,
+            depth: depth,
+            showcourses: categorynode.getData('showcourses'),
+            type: TYPE_CATEGORY
+        }
+    });
+};
+
 /**
  * Toggle the animation of the clicked category node.
  *
@@ -308,12 +362,12 @@ NS._collapse_expand_all = function(e) {
 NS.expand_all = function(ancestor) {
     var finalexpansions = [];
 
-    ancestor.all(SELECTORS.CATEGORYWITHCOLLAPSEDLOADEDCHILDREN)
+    ancestor.all(SELECTORS.CATEGORYWITHCOLLAPSEDCHILDREN)
         .each(function(c) {
-        if (c.ancestor(SELECTORS.CATEGORYWITHCOLLAPSEDLOADEDCHILDREN)) {
+        if (c.ancestor(SELECTORS.CATEGORYWITHCOLLAPSEDCHILDREN)) {
             // Expand the hidden children first without animation.
             c.removeClass(CSS.SECTIONCOLLAPSED);
-            c.all(SELECTORS.LOADEDTREES).removeClass(CSS.SECTIONCOLLAPSED);
+            c.all(SELECTORS.WITHCHILDRENTREES).removeClass(CSS.SECTIONCOLLAPSED);
         } else {
             finalexpansions.push(c);
         }
@@ -321,7 +375,7 @@ NS.expand_all = function(ancestor) {
 
     // Run the final expansion with animation on the visible items.
     Y.all(finalexpansions).each(function(c) {
-        this.run_expansion(c);
+        this.expand_category(c);
     }, this);
 
 };
index 20076cc..34e12c5 100644 (file)
@@ -37,12 +37,7 @@ Structure of the user enroller panel
 
 .user-enroller-panel {
     width: 400px;
-    background-color: #666;
     position: absolute;
-    top: 10%;
-    left: 10%;
-    border: 1px solid #666;
-    border-width: 0 5px 5px 0;
 }
 
 .user-enroller-panel.hidden {
@@ -50,57 +45,22 @@ Structure of the user enroller panel
 }
 
 .user-enroller-panel .uep-wrap {
-    margin-top: -5px;
-    margin-left: -5px;
-    background-color: #fff;
-    border: 1px solid #999;
     height: inherit;
 }
 
-.user-enroller-panel .uep-header {
-    background-color: #eee;
-    padding: 1px;
-}
-
-.user-enroller-panel .uep-header h2 {
-    margin: 3px 1em 0.5em 1em;
-    font-size: 1em;
+.user-enroller-panel .uep-search-results .user .count {
+    display: none;
 }
 
-.user-enroller-panel .uep-header .close {
-    width: 25px;
-    height: 15px;
-    position: absolute;
-    top: 2px;
-    right: 1em;
-    cursor: pointer;
-    background: url("sprite.png") no-repeat scroll 0 0 transparent;
+.user-enroller-panel .uep-search-results .cohort .count {
+    display: none;
 }
 
 .user-enroller-panel .uep-content {
-    text-align: center;
     position: relative;
     width: 100%;
-    border-top: 1px solid #999;
-    border-bottom: 1px solid #999;
-}
-
-.user-enroller-panel .uep-content .uep-controls {
-    margin: 0;
-    padding: 3px;
-    background-color: #ddd;
-    text-align: left;
-    border-bottom: 1px solid #bbb;
-}
-
-.user-enroller-panel .uep-content .uep-controls label {
-    display: inline;
-    padding-right: 5px;
-}
-
-.user-enroller-panel .uep-content .uep-controls .uep-enrolment-option input {
-    vertical-align: middle;
-    margin-left: 1em;
+    box-sizing: border-box;
+    max-height: 1000px;
 }
 
 .user-enroller-panel .uep-ajax-content {
@@ -108,39 +68,6 @@ Structure of the user enroller panel
     overflow: auto;
 }
 
-.user-enroller-panel .uep-search-results .totalusers {
-    background-color: #eee;
-    padding: 5px;
-    border-bottom: 1px solid #bbb;
-    font-size: 7pt;
-    font-weight: bold;
-}
-
-.user-enroller-panel .uep-search-results .user {
-    width: 100%;
-    text-align: left;
-    font-size: 9pt;
-    border-bottom: 1px solid #ddd;
-    border-top: 1px solid #eee;
-}
-
-.user-enroller-panel .uep-search-results .user.odd {
-    border-bottom: 1px solid #ddd;
-    border-top: 1px solid #eee;
-    background-color: #f9f9f9;
-}
-
-.user-enroller-panel .uep-search-results .user .count {
-    width: 20px;
-    float: left;
-    font-size: 7pt;
-    line-height: 41px;
-    border-right: 1px solid #ddd;
-    background-color: #eee;
-    text-align: right;
-    padding: 2px;
-}
-
 .user-enroller-panel .uep-search-results .user .picture {
     width: 45px;
     float: left;
@@ -148,14 +75,13 @@ Structure of the user enroller panel
 }
 
 .user-enroller-panel .uep-search-results .user .details {
-    width: 250px;
+    width: 180px;
     float: left;
     margin: 3px;
 }
 
 .user-enroller-panel .uep-search-results .user .options {
     padding-right: 7px;
-    font-size: 8pt;
     margin: 3px;
 }
 
@@ -165,56 +91,21 @@ Structure of the user enroller panel
     cursor: pointer;
 }
 
-.user-enroller-panel .uep-search-results .user.enrolled .count {
-    width: 40px;
-    color: #eee;
-}
-
-.user-enroller-panel .uep-search-results .uep-more-results {
-    background-color: #eee;
-    padding: 5px;
-    border-top: 1px solid #bbb;
-}
-
-.user-enroller-panel .uep-search-results .totalcohorts {
-    background-color: #eee;
-    padding: 5px;
-    border-bottom: 1px solid #bbb;
-    font-size: 7pt;
-    font-weight: bold;
-}
-
 .user-enroller-panel .uep-search-results .cohort {
     width: 100%;
     text-align: left;
-    font-size: 9pt;
-    border-bottom: 1px solid #ddd;
-    border-top: 1px solid #eee;
 }
 
 .user-enroller-panel .uep-search-results .cohort .count {
-    width: 20px;
-    float: left;
-    font-size: 7pt;
-    line-height: 27px;
-    border-right: 1px solid #ddd;
-    background-color: #eee;
-    text-align: right;
-    padding: 2px;
+    display: none;
 }
 
 .user-enroller-panel .uep-search-results .cohort .details {
-    width: 250px;
+    width: 180px;
     float: left;
     margin: 5px;
 }
 
-.user-enroller-panel .uep-search-results .cohort .options {
-    padding-right: 7px;
-    font-size: 8pt;
-    margin: 3px;
-}
-
 .user-enroller-panel .uep-search-results .cohort .options .enrol {
     margin: 3px;
     float: right;
@@ -223,16 +114,15 @@ Structure of the user enroller panel
 
 .user-enroller-panel .uep-search-results .cohort.enrolled .count {
     width: 40px;
-    color: #eee;
 }
 
 .user-enroller-panel .uep-loading-lightbox {
     position: absolute;
     width: 100%;
     height: 100%;
+    background-color: #ddd;
     top: 0;
     left: 0;
-    background-color: #fff;
     min-width: 50px;
     min-height: 50px;
 }
@@ -245,12 +135,11 @@ Structure of the user enroller panel
     margin: auto;
     vertical-align: middle;
     margin-top: 125px;
+    display: block;
 }
 
 .user-enroller-panel .uep-footer {
-    padding: 3px;
-    background-color: #ddd;
-    text-align: center;
+    text-align: right;
 }
 
 .user-enroller-panel .uep-search {
@@ -263,7 +152,6 @@ Structure of the user enroller panel
 
 .user-enroller-panel .uep-search input {
     width: 50%;
-    margin: 0 0 10px 5px;
 }
 
 .user-enroller-panel .uep-search input.uep-search-btn {
@@ -285,8 +173,8 @@ Structure of the user enroller panel
 }
 
 .user-enroller-panel .collapsiblearea {
-    border: 1px solid #bbb;
-    background-color: #f6f6f6;
+    border: 1px solid #ddd;
+    padding: 0.5rem;
 }
 
 .user-enroller-panel .collapsiblearea.hidden {
index 012cafb..8f235cc 100644 (file)
@@ -32,7 +32,7 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
     /** CSS classes for nodes in structure **/
     var CSS = {
         PANEL : 'user-enroller-panel',
-        WRAP : 'uep-wrap',
+        WRAP : 'uep-wrap modal-dialog',
         HEADER : 'uep-header',
         CONTENT : 'uep-content',
         AJAXCONTENT : 'uep-ajax-content',
@@ -90,33 +90,33 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
             if (this.get(UEP.DISABLEGRADEHISTORY) != true) {
                 recovergrades = create('<div class="'+CSS.ENROLMENTOPTION+' '+CSS.RECOVERGRADES+'"></div>')
                     .append(create('<label class="'+CSS.RECOVERGRADESTITLE+'" for="'+CSS.RECOVERGRADES+'">'+M.util.get_string('recovergrades', 'enrol')+'</label>'))
-                    .append(create('<input type="checkbox" id="'+CSS.RECOVERGRADES+'" name="'+CSS.RECOVERGRADES+'"'+ this.get(UEP.RECOVERGRADESDEFAULT) +' />'))
+                    .append(create('<input type="checkbox" class="m-x-1" id="'+CSS.RECOVERGRADES+'" name="'+CSS.RECOVERGRADES+'"'+ this.get(UEP.RECOVERGRADESDEFAULT) +' />'))
             }
 
             this.set(UEP.BASE, create('<div class="'+CSS.PANEL+' '+CSS.HIDDEN+'"></div>')
-                .append(create('<div class="'+CSS.WRAP+'"></div>')
-                    .append(create('<div class="'+CSS.HEADER+' header"></div>')
-                        .append(create('<div class="'+CSS.CLOSE+'"></div>'))
-                        .append(create('<h2>'+M.util.get_string('enrolusers', 'enrol')+'</h2>')))
-                    .append(create('<div class="'+CSS.CONTENT+'"></div>')
-                        .append(create('<div class="'+CSS.SEARCHCONTROLS+'"></div>')
+                .append(create('<div class="'+CSS.WRAP+' modal show modal-dialog modal-content"></div>')
+                    .append(create('<div class="'+CSS.HEADER+' header modal-header"></div>')
+                        .append(create('<div class="'+CSS.CLOSE+'">&times;</div>'))
+                        .append(create('<h2 class="modal-title">'+M.util.get_string('enrolusers', 'enrol')+'</h2>')))
+                    .append(create('<div class="'+CSS.CONTENT+' modal-body"></div>')
+                        .append(create('<div class="'+CSS.SEARCHCONTROLS+' form-inline"></div>')
                             .append(create('<div class="'+CSS.ENROLMENTOPTION+' '+CSS.ROLE+'"><label for="id_enrol_manual_assignable_roles">'+M.util.get_string('assignroles', 'role')+'</label></div>')
-                                    .append(create('<select id="id_enrol_manual_assignable_roles"><option value="">'+M.util.get_string('none', 'enrol')+'</option></select>'))
+                                    .append(create('<select id="id_enrol_manual_assignable_roles" class="custom-select"><option value="">'+M.util.get_string('none', 'enrol')+'</option></select>'))
                             )
-                            .append(create('<div class="'+CSS.ENTITYSELECTOR+'"></div>'))
+                            .append(create('<div class="'+CSS.ENTITYSELECTOR+' m-y-1"></div>'))
                             .append(create('<div class="'+CSS.SEARCHOPTIONS+'"></div>')
                                 .append(create('<div class="'+CSS.COLLAPSIBLEHEADING+'"><img alt="" />'+M.util.get_string('enrolmentoptions', 'enrol')+'</div>'))
                                 .append(create('<div class="'+CSS.COLLAPSIBLEAREA+' '+CSS.HIDDEN+'"></div>')
                                     .append(recovergrades)
                                     .append(create('<div class="'+CSS.ENROLMENTOPTION+' '+CSS.STARTDATE+'">'+M.util.get_string('startingfrom', 'moodle')+'</div>')
-                                        .append(create('<select></select>')))
+                                        .append(create('<select class="custom-select"></select>')))
                                     .append(create('<div class="'+CSS.ENROLMENTOPTION+' '+CSS.DURATION+'">'+M.util.get_string('enrolperiod', 'enrol')+'</div>')
-                                        .append(create('<select><option value="0" selected="selected">'+M.util.get_string('unlimitedduration', 'enrol')+'</option></select>')))
+                                        .append(create('<select class="custom-select"><option value="0" selected="selected">'+M.util.get_string('unlimitedduration', 'enrol')+'</option></select>')))
                                 )
                             )
                             .append(create('<div class="'+CSS.SEARCH+'"><label for="enrolusersearch" class="accesshide">'+M.util.get_string('usersearch', 'enrol')+'</label></div>')
-                                .append(create('<input type="text" id="enrolusersearch" value="" />'))
-                                .append(create('<input type="button" id="searchbtn" class="'+CSS.SEARCHBTN+'" value="'+M.util.get_string('usersearch', 'enrol')+'" />'))
+                                .append(create('<input class="form-control" type="text" id="enrolusersearch" value="" />'))
+                                .append(create('<input type="button" id="searchbtn" class="'+CSS.SEARCHBTN+' btn btn-secondary m-l-1" value="'+M.util.get_string('usersearch', 'enrol')+'" />'))
                             )
                         )
                         .append(create('<div class="'+CSS.AJAXCONTENT+'"></div>'))
@@ -124,9 +124,9 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
                             .append(create('<img alt="loading" class="'+CSS.LOADINGICON+'" />')
                                 .setAttribute('src', M.util.image_url('i/loading', 'moodle')))
                             .setStyle('opacity', 0.5)))
-                    .append(create('<div class="'+CSS.FOOTER+'"></div>')
+                    .append(create('<div class="'+CSS.FOOTER+' modal-footer"></div>')
                         .append(create('<div class="'+CSS.CLOSEBTN+'"></div>')
-                            .append(create('<input type="button" value="'+M.util.get_string('finishenrollingusers', 'enrol')+'" />'))
+                            .append(create('<input type="button" class="btn btn-primary" value="'+M.util.get_string('finishenrollingusers', 'enrol')+'" />'))
                         )
                     )
                 )
@@ -151,9 +151,9 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
 
             if (this.get(UEP.COHORTSAVAILABLE)) {
                 this.get(UEP.BASE).one('.'+CSS.ENTITYSELECTOR)
-                    .append(create('<input type="radio" id="id_enrol_manual_entity_users" name="enrol_manual_entity" value="users" checked="checked"/>'))
+                    .append(create('<input type="radio" id="id_enrol_manual_entity_users" name="enrol_manual_entity" value="users" class="m-r-1" checked="checked"/>'))
                     .append(create('<label for="id_enrol_manual_entity_users">'+ M.util.get_string('browseusers', 'enrol_manual')+'</label>'))
-                    .append(create('<input type="radio" id="id_enrol_manual_entity_cohorts" name="enrol_manual_entity" value="cohorts"/>'))
+                    .append(create('<input type="radio" id="id_enrol_manual_entity_cohorts" name="enrol_manual_entity" class="m-x-1" value="cohorts"/>'))
                     .append(create('<label for="id_enrol_manual_entity_cohorts">'+M.util.get_string('browsecohorts', 'enrol_manual')+'</label>'));
                 this.get(UEP.BASE).one('#id_enrol_manual_entity_cohorts').on('change', this.search, this);
                 this.get(UEP.BASE).one('#id_enrol_manual_entity_users').on('change', this.search, this);
@@ -397,7 +397,7 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
             }
             var users;
             if (!args.append) {
-                users = create('<div class="'+CSS.USERS+'"></div>');
+                users = create('<div class="'+CSS.USERS+' list-group"></div>');
             } else {
                 users = this.get(UEP.BASE).one('.'+CSS.SEARCHRESULTS+' .'+CSS.USERS);
             }
@@ -405,7 +405,7 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
             for (var i in result.response.users) {
                 count++;
                 var user = result.response.users[i];
-                users.append(create('<div class="'+CSS.USER+' clearfix" rel="'+user.id+'"></div>')
+                users.append(create('<div class="'+CSS.USER+' clearfix list-group-item list-group-item-action" rel="'+user.id+'"></div>')
                     .addClass((count%2)?CSS.ODD:CSS.EVEN)
                     .append(create('<div class="'+CSS.COUNT+'">'+count+'</div>'))
                     .append(create('<div class="'+CSS.PICTURE+'"></div>')
@@ -414,7 +414,7 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
                         .append(create('<div class="'+CSS.FULLNAME+'">'+user.fullname+'</div>'))
                         .append(create('<div class="'+CSS.EXTRAFIELDS+'">'+user.extrafields+'</div>')))
                     .append(create('<div class="'+CSS.OPTIONS+'"></div>')
-                        .append(create('<input type="button" class="'+CSS.ENROL+'" value="'+M.util.get_string('enrol', 'enrol')+'" />')))
+                        .append(create('<input type="button" class="'+CSS.ENROL+' btn btn-secondary" value="'+M.util.get_string('enrol', 'enrol')+'" />')))
                 );
             }
             this.set(UEP.USERCOUNT, count);
@@ -464,7 +464,7 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
                     .append(create('<div class="'+CSS.DETAILS+'"></div>')
                         .append(create('<div class="'+CSS.COHORTNAME+'">'+cohort.name+'</div>')))
                     .append(create('<div class="'+CSS.OPTIONS+'"></div>')
-                        .append(create('<input type="button" class="' + CSS.ENROL + '" value="' + M.util.get_string('enrolxusers', 'enrol', cohort.cnt) + '" />')))
+                        .append(create('<input type="button" class="' + CSS.ENROL + ' btn btn-secondary" value="' + M.util.get_string('enrolxusers', 'enrol', cohort.cnt) + '" />')))
                 );
             }
             this.set(UEP.COHORTCOUNT, count);
index a7af91c..0366c4d 100644 (file)
@@ -307,11 +307,12 @@ function enrol_meta_sync($courseid = NULL, $verbose = false) {
                                 ELSE 0 END) AS timeend
               FROM {user_enrolments} pue
               JOIN {enrol} pe ON (pe.id = pue.enrolid AND pe.enrol <> 'meta' AND pe.enrol $enabled)
-              JOIN {enrol} e ON (e.customint1 = pe.courseid AND e.enrol = 'meta' $onecourse)
+              JOIN {enrol} e ON (e.customint1 = pe.courseid AND e.enrol = 'meta' AND e.status = :enrolstatus $onecourse)
               JOIN {user} u ON (u.id = pue.userid AND u.deleted = 0)
          LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = pue.userid)
              WHERE ue.id IS NULL
              GROUP BY pue.userid, e.id";
+    $params['enrolstatus'] = ENROL_INSTANCE_ENABLED;
 
     $rs = $DB->get_recordset_sql($sql, $params);
     foreach($rs as $ue) {
index a75d61a..750ecdb 100644 (file)
@@ -358,12 +358,18 @@ YUI.add('moodle-enrol-rolemanager', function(Y) {
         submitevent : null,
         initializer : function() {
             var i, m = this.get(MANIPULATOR);
-            var element = Y.Node.create('<div class="enrolpanel roleassign"><div class="container"><div class="header"><h2>'+M.util.get_string('assignroles', 'role')+'</h2><div class="close"></div></div><div class="content"></div></div></div>');
+            var element = Y.Node.create('<div class="popover popover-bottom"><div class="arrow"></div>' +
+                                        '<div class="header popover-title">' +
+                                        '<div role="button" class="close" aria-label="Close">' +
+                                        '<span aria-hidden="true">&times;</span></div>' +
+                                        '<h3>'+M.util.get_string('assignroles', 'role')+'</h3>' +
+                                        '</div><div class="content popover-content form-inline form-group"></div></div>');
             var content = element.one('.content');
             var roles = m.get(ASSIGNABLEROLES);
             for (i in roles) {
                 var buttonid = 'add_assignable_role_' + roles[i].id;
-                var buttonhtml = '<input type="button" value="' + roles[i].name + '" id="' + buttonid + '" />';
+                var buttonhtml = '<input type="button" class="btn btn-secondary m-r-1" value="' +
+                                 roles[i].name + '" id="' + buttonid + '" />';
                 var button = Y.Node.create(buttonhtml);
                 button.on('click', this.submit, this, roles[i].id);
                 content.append(button);
@@ -392,7 +398,7 @@ YUI.add('moodle-enrol-rolemanager', function(Y) {
             } else {
                 this.get('elementNode').setStyle('left', x).setStyle('top', y);
             }
-            this.get('elementNode').addClass('visible');
+            this.get('elementNode').setStyle('display', 'block');
             this.escCloseEvent = Y.on('key', this.hide, document.body, 'down:27', this);
             this.displayed = true;
         },
@@ -409,7 +415,7 @@ YUI.add('moodle-enrol-rolemanager', function(Y) {
             }
             this.roles = [];
             this.user = null;
-            this.get('elementNode').removeClass('visible');
+            this.get('elementNode').setStyle('display', 'none');
             if (this.submitevent) {
                 this.submitevent.detach();
                 this.submitevent = null;
index 7167e9a..0c9e490 100644 (file)
--- a/index.php
+++ b/index.php
@@ -147,7 +147,7 @@ if (!empty($CFG->customfrontpageinclude)) {
         $context = context_course::instance(SITEID);
 
         // If the section name is set we show it.
-        if (!is_null($section->name)) {
+        if (trim($section->name) !== '') {
             echo $OUTPUT->heading(
                 format_string($section->name, true, array('context' => $context)),
                 2,
index f1c61c2..5a4fd4b 100644 (file)
@@ -69,7 +69,10 @@ $string['pathshead'] = 'Potvrdit cesty';
 $string['pathsrodataroot'] = 'Do datového adresáře nelze zapisovat.';
 $string['pathsroparentdataroot'] = 'Do nadřazeného adresáře ({$a->parent}) nelze zapisovat. Datový adresář ({$a->dataroot}) nemůže být tímto průvodcem instalací vytvořen.';
 $string['pathssubadmindir'] = 'Na některých serverech je URL adresa /admin vyhrazena pro speciální účely (např. pro ovládací panel). Na takových serverech může dojít ke kolizi se standardním umístěním stránek pro správu Moodle. Máte-li tento problém, přejmenujte adresář <eM>admin</em> ve vaší instalaci Moodle a sem zadejte jeho nový název - například <em>moodleadmin</em>. Všechny generované odkazy na stránky správy Moodle budou používat tento nový název.';
-$string['pathssubdataroot'] = 'Moodle potřebuje prostor, kam si bude ukládat nahrané soubory a další údaje. K tomuto adresáři musí mít proces webového serveru právo ke čtení i k zápisu (webový server bývá většinou spouštěn pod uživatelem "www-data" nebo "apache"). Tento adresář ale zároveň nesmí být dostupný přímo přes webové rozhraní. Instalační skript se pokusí tento adresář vytvořit, pokud nebude existovat.';
+$string['pathssubdataroot'] = '<p>Moodle potřebuje prostor, kam si bude ukládat nahrané soubory a další údaje. .</p>
+<p>K tomuto adresáři musí mít proces webového serveru právo ke čtení i k zápisu (webový server bývá většinou spouštěn pod uživatelem "www-data" nebo "apache"). .</p>
+<p>Tento adresář ale zároveň nesmí být dostupný přímo přes webové rozhraní. .</p>
+<p>Instalační skript se pokusí tento adresář vytvořit, pokud nebude existovat..</p>';
 $string['pathssubdirroot'] = '<p>Absolutní cesta k adresáři s instalací Moodle.</p>';
 $string['pathssubwwwroot'] = 'Zadejte úplnou webovou adresu, na níž bude Moodle dostupný. Moodle potřebuje jedinečnou adresu, není možné jej provozovat na několika URL současně. Používáte-li několik veřejných domén, musíte si sami nastavit permanentní přesměrování na jednu z nich a tu pak použít. Pokud je váš server dostupný z vnější a z vnitřní sítě pod různými IP adresami, použijte jeho veřejnou adresu a nastavte si váš DNS server tak, že ji mohou používat i uživatelé z vnitřní sítě.';
 $string['pathsunsecuredataroot'] = 'Umístění datového adresáře není bezpečné';
index f988765..c8e895a 100644 (file)
@@ -72,13 +72,15 @@ $string['pathshead'] = 'Bekræft stier';
 $string['pathsrodataroot'] = 'Datamappen er skrivebeskyttet.';
 $string['pathsroparentdataroot'] = 'Den overordnede mappe ({$a->parent}) er skrivebeskyttet. Datamappen ({$a->dataroot}) kan ikke oprettes af installationsprogrammet.';
 $string['pathssubadmindir'] = 'Enkelte webhoteller bruger /admin som speciel URL til kontrolpanelet el. lign. Desværre konflikter det med Moodles standardplacering af admin-sider. Du kan klare dette ved at give admin-mappen et andet navn i din installation og skrive det her. Det kan f.eks. være <em>moodleadmin</em>. Det vil fikse admin-links i Moodle.';
-$string['pathssubdataroot'] = 'Du har brug for et sted, hvor Moodle kan gemme uploadede filer. Denne mappe skal kunne læses OG SKRIVES I af webserverbrugeren (oftest \'ingen\' eller \'apache\'), men må ikke være tilgængelig direkte via internettet. Installationsprogrammet vil forsøge at oprette mappen, hvis ikke den allerede eksisterer.';
+$string['pathssubdataroot'] = '<p>En mappe hvori Moodle kan gemme uploadede filer.</p>
+<p>Webserver-brugeren (som regel \'www-data\', \'nobody\', eller \'apache\') skal have læse- og skriveadgang til den.</p>
+<p>Mappen  må ikke være tilgængelig direkte fra internettet.</p>
+<p>Hvis ikke den allerede eksisterer, vil Installationsprogrammet forsøge at oprette den</p>';
 $string['pathssubdirroot'] = 'Den fulde sti til Moodleinstallationen.';
-$string['pathssubwwwroot'] = 'Moodles fulde web-adresse.
-Det er ikke muligt at komme ind på Moodle fra mere end en adresse.
-Hvis dit websted har flere offentlige adresser skal du opsætte permanent viderestilling til dem alle undtagen denne.
-Hvis dit websted er tilgængeligt fra både internet og intranet skal du bruge internetadressen her og opsætte din DNS sådan at intranet-brugerne kan bruge den offentlige adresse også.
-Hvis ikke adressen er korrekt må du ændre URL\'en i din browser og genstarte installationen med den rigtige adresse.';
+$string['pathssubwwwroot'] = '<p>Moodles fulde web-adresse, dvs. adressen som den skal stå i browserens adressefelt for at komme ind på Moodle.</p>
+<p>Moodle kan ikke bruges fra flere adresser. Hvis dit websted kan tilgås fra flere adresser, skal du vælge den enkleste og opsætte permanent viderestilling for hver af de øvrige.</p>
+<p>Hvis dit websted er tilgængeligt fra både internettet og et internt net (nogen gange kaldet intranet), skal du bruge den offentlige adresse her</p>
+<p>Hvis ikke adressen er korrekt, skal du ændre URL\'en i din browser og genstarte installationen.</p>';
 $string['pathsunsecuredataroot'] = 'Datamappen er ikke sikret';
 $string['pathswrongadmindir'] = 'Adminmappe eksisterer ikke';
 $string['phpextension'] = '{$a} PHP-extension';
index 99e3310..b97ae2b 100644 (file)
@@ -52,6 +52,7 @@ $string['cachedef_suspended_userids'] = 'List of suspended users per course';
 $string['cachedef_groupdata'] = 'Course group information';
 $string['cachedef_htmlpurifier'] = 'HTML Purifier - cleaned content';
 $string['cachedef_langmenu'] = 'List of available languages';
+$string['cachedef_message_time_last_message_between_users'] = 'Time created for most recent message between users';
 $string['cachedef_locking'] = 'Locking';
 $string['cachedef_message_processors_enabled'] = "Message processors enabled status";
 $string['cachedef_navigation_expandcourse'] = 'Navigation expandable courses';
index fe57dd3..f7c5ac8 100644 (file)
@@ -22,6 +22,7 @@
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+$string['activityisscheduledfordeletion'] = 'Activity deletion in progress...';
 $string['authnotexisting'] = 'The autorization plugin doesn\'t exist';
 $string['backupcontainexternal'] = 'This backup file contains external Moodle Network Hosts that are not configured locally';
 $string['backuptablefail'] = 'Backup tables could NOT be set up successfully!';
index 404b70a..49bd897 100644 (file)
@@ -68,6 +68,9 @@ $string['messagepreferences'] = 'Message preferences';
 $string['messages'] = 'Messages';
 $string['messagingdisabled'] = 'Messaging is disabled on this site, emails will be sent instead';
 $string['newonlymsg'] = 'Show only new';
+$string['newmessage'] = 'New message';
+$string['newmessagesearch'] = 'Select or search for a contact to send a new message.';
+$string['newsearch'] = 'New search';
 $string['noframesjs'] = 'Use more accessible interface';
 $string['nocontacts'] = 'No contacts';
 $string['nomessages'] = 'No messages';
@@ -96,6 +99,8 @@ $string['requiresconfiguration'] = 'Requires configuration';
 $string['searchforuser'] = 'Search for a user';
 $string['searchforuserorcourse'] = 'Search for a user or course';
 $string['searchmessages'] = 'Search messages';
+$string['searchcombined'] = 'Search people and messages';
+$string['seeall'] = 'See all';
 $string['selectmessagestodelete'] = 'Select messages to delete';
 $string['selectnotificationtoview'] = 'Select from the list of notifications on the side to view more details';
 $string['send'] = 'Send';
index 8f2d0ab..11be763 100644 (file)
@@ -366,7 +366,11 @@ function has_capability($capability, context $context, $user = null, $doanything
     global $USER, $CFG, $SCRIPT, $ACCESSLIB_PRIVATE;
 
     if (during_initial_install()) {
-        if ($SCRIPT === "/$CFG->admin/index.php" or $SCRIPT === "/$CFG->admin/cli/install.php" or $SCRIPT === "/$CFG->admin/cli/install_database.php") {
+        if ($SCRIPT === "/$CFG->admin/index.php"
+                or $SCRIPT === "/$CFG->admin/cli/install.php"
+                or $SCRIPT === "/$CFG->admin/cli/install_database.php"
+                or (defined('BEHAT_UTIL') and BEHAT_UTIL)
+                or (defined('PHPUNIT_UTIL') and PHPUNIT_UTIL)) {
             // we are in an installer - roles can not work yet
             return true;
         } else {
index 4a0a1db..3298c0e 100644 (file)
@@ -32,6 +32,8 @@ define('AJAX_SCRIPT', true);
 require_once(__DIR__ . '/../../config.php');
 require_once($CFG->libdir . '/externallib.php');
 
+define('PREFERRED_RENDERER_TARGET', RENDERER_TARGET_GENERAL);
+
 $rawjson = file_get_contents('php://input');
 
 $requests = json_decode($rawjson, true);
diff --git a/lib/amd/build/backoff_timer.min.js b/lib/amd/build/backoff_timer.min.js
new file mode 100644 (file)
index 0000000..bb80b45
Binary files /dev/null and b/lib/amd/build/backoff_timer.min.js differ
diff --git a/lib/amd/src/backoff_timer.js b/lib/amd/src/backoff_timer.js
new file mode 100644 (file)
index 0000000..da0fabd
--- /dev/null
@@ -0,0 +1,175 @@
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * A timer that will execute a callback with decreasing frequency. Useful for
+ * doing polling on the server without overwhelming it with requests.
+ *
+ * @module     core/backoff_timer
+ * @class      backoff_timer
+ * @package    core
+ * @copyright  2016 Ryan Wyllie <ryan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define(function() {
+
+    /**
+     * Constructor for the back off timer.
+     *
+     * @param {function} callback The function to execute after each tick
+     * @param {function} backoffFunction The function to determine what the next timeout value should be
+     */
+    var BackoffTimer = function(callback, backoffFunction) {
+        this.callback = callback;
+        this.backOffFunction = backoffFunction;
+    };
+
+    /**
+     * @type {function} callback The function to execute after each tick
+     */
+    BackoffTimer.prototype.callback = null;
+
+    /**
+     * @type {function} backoffFunction The function to determine what the next timeout value should be
+     */
+    BackoffTimer.prototype.backOffFunction = null;
+
+    /**
+     * @type {int} time The timeout value to use
+     */
+    BackoffTimer.prototype.time = null;
+
+    /**
+     * @type {numeric} timeout The timeout identifier
+     */
+    BackoffTimer.prototype.timeout = null;
+
+    /**
+     * Generate the next timeout in the back off time sequence
+     * for the timer.
+     *
+     * The back off function is called to calculate the next value.
+     * It is given the current value and an array of all previous values.
+     *
+     * @method generateNextTime
+     * @return {int} The new timeout value (in milliseconds)
+     */
+    BackoffTimer.prototype.generateNextTime = function() {
+        var newTime = this.backOffFunction(this.time);
+        this.time = newTime;
+
+        return newTime;
+    };
+
+    /**
+     * Stop the current timer and clear the previous time values
+     *
+     * @method reset
+     * @return {object} this
+     */
+    BackoffTimer.prototype.reset = function() {
+        this.time = null;
+        this.stop();
+
+        return this;
+    };
+
+    /**
+     * Clear the current timeout, if one is set.
+     *
+     * @method stop
+     * @return {object} this
+     */
+    BackoffTimer.prototype.stop = function() {
+        if (this.timeout) {
+            window.clearTimeout(this.timeout);
+            this.timeout = null;
+        }
+
+        return this;
+    };
+
+    /**
+     * Start the current timer by generating the new timeout value and
+     * starting the ticks.
+     *
+     * This function recurses after each tick with a new timeout value
+     * generated each time.
+     *
+     * The callback function is called after each tick.
+     *
+     * @method start
+     * @return {object} this
+     */
+    BackoffTimer.prototype.start = function() {
+        // If we haven't already started.
+        if (!this.timeout) {
+            var time = this.generateNextTime();
+            this.timeout = window.setTimeout(function() {
+                this.callback();
+                // Clear the existing timer.
+                this.stop();
+                // Start the next timer.
+                this.start();
+            }.bind(this), time);
+        }
+
+        return this;
+    };
+
+    /**
+     * Reset the timer and start it again from the initial timeout
+     * values
+     *
+     * @method restart
+     * @return {object} this
+     */
+    BackoffTimer.prototype.restart = function() {
+        return this.reset().start();
+    };
+
+    /**
+     * Returns an incremental function for the timer.
+     *
+     * @param {int} minamount The minimum amount of time we wait before checking
+     * @param {int} incrementamount The amount to increment the timer by
+     * @param {int} maxamount The max amount to ever increment to
+     * @param {int} timeoutamount The timeout to use once we reach the max amount
+     * @return {function}
+     */
+     BackoffTimer.getIncrementalCallback = function(minamount, incrementamount, maxamount, timeoutamount) {
+
+        /**
+         * An incremental function for the timer.
+         *
+         * @param {(int|null)} time The current timeout value or null if none set
+         * @return {int} The new timeout value
+         */
+        return function(time) {
+            if (!time) {
+                return minamount;
+            }
+
+            // Don't go over the max amount.
+            if (time + incrementamount > maxamount) {
+                return timeoutamount;
+            }
+
+            return time + incrementamount;
+        };
+    };
+
+    return BackoffTimer;
+});
index c300901..2c14e98 100644 (file)
@@ -86,7 +86,9 @@ final class util {
      * @return bool
      */
     public static function can_use_tls12(array $curlinfo, $uname) {
-        if ($curlinfo['version_number'] < 467456 || !defined('CURL_SSLVERSION_TLSv1_2')) {
+        // Do not compare the cURL version, e.g. $curlinfo['version_number'], with v7.34.0 (467456):
+        // some Linux distros backport security issues and keep lower version numbers.
+        if (!defined('CURL_SSLVERSION_TLSv1_2')) {
             return false;
         }
 
index 72e5d51..670d69b 100644 (file)
@@ -89,18 +89,16 @@ function min_clean_param($value, $type) {
  * @return string
  */
 function min_fix_utf8($value) {
-    // Lower error reporting because glibc throws bogus notices.
-    $olderror = error_reporting();
-    if ($olderror & E_NOTICE) {
-        error_reporting($olderror ^ E_NOTICE);
-    }
-
     // No null bytes expected in our data, so let's remove it.
     $value = str_replace("\0", '', $value);
 
     static $buggyiconv = null;
     if ($buggyiconv === null) {
+        set_error_handler(function () {
+            return true;
+        });
         $buggyiconv = (!function_exists('iconv') or iconv('UTF-8', 'UTF-8//IGNORE', '100'.chr(130).'€') !== '100€');
+        restore_error_handler();
     }
 
     if ($buggyiconv) {
@@ -116,11 +114,7 @@ function min_fix_utf8($value) {
         }
 
     } else {
-        $result = iconv('UTF-8', 'UTF-8//IGNORE', $value);
-    }
-
-    if ($olderror & E_NOTICE) {
-        error_reporting($olderror);
+        $result = @iconv('UTF-8', 'UTF-8//IGNORE', $value);
     }
 
     return $result;
index 1cd5269..d8ce7b8 100644 (file)
@@ -361,15 +361,13 @@ class coursecat implements renderable, cacheable_object, IteratorAggregate {
         $newcategory->name = $data->name;
 
         // Validate and set idnumber.
-        if (!empty($data->idnumber)) {
+        if (isset($data->idnumber)) {
             if (core_text::strlen($data->idnumber) > 100) {
                 throw new moodle_exception('idnumbertoolong');
             }
-            if ($DB->record_exists('course_categories', array('idnumber' => $data->idnumber))) {
+            if (strval($data->idnumber) !== '' && $DB->record_exists('course_categories', array('idnumber' => $data->idnumber))) {
                 throw new moodle_exception('categoryidnumbertaken');
             }
-        }
-        if (isset($data->idnumber)) {
             $newcategory->idnumber = $data->idnumber;
         }
 
@@ -484,11 +482,11 @@ class coursecat implements renderable, cacheable_object, IteratorAggregate {
             $newcategory->name = $data->name;
         }
 
-        if (isset($data->idnumber) && $data->idnumber != $this->idnumber) {
+        if (isset($data->idnumber) && $data->idnumber !== $this->idnumber) {
             if (core_text::strlen($data->idnumber) > 100) {
                 throw new moodle_exception('idnumbertoolong');
             }
-            if ($DB->record_exists('course_categories', array('idnumber' => $data->idnumber))) {
+            if (strval($data->idnumber) !== '' && $DB->record_exists('course_categories', array('idnumber' => $data->idnumber))) {
                 throw new moodle_exception('categoryidnumbertaken');
             }
             $newcategory->idnumber = $data->idnumber;
index b709f06..9a8e9f2 100644 (file)
@@ -301,4 +301,12 @@ $definitions = array(
         'staticacceleration' => true,
         'staticaccelerationsize' => 3
     ),
+
+    // Caches the time of the last message between two users.
+    'message_time_last_message_between_users' => array(
+        'mode' => cache_store::MODE_APPLICATION,
+        'simplekeys' => true, // The id of the sender and recipient is used.
+        'simplevalues' => true,
+        'datasource' => '\core_message\time_last_message_between_users',
+    ),
 );
index 80cdcfe..84c0533 100644 (file)
@@ -53,6 +53,7 @@
     max-width: calc(100% - 1em);
 }
 
+/*rtl:begin:ignore*/
 .atto_image_button_left {
     float: left;
     margin: 0 0.5em 0 0;
@@ -64,3 +65,4 @@
     margin: 0 0 0 0.5em;
     max-width: calc(100% - 1em);
 }
+/*rtl:end:ignore*/
\ No newline at end of file
index e0681b5..edab4a1 100644 (file)
Binary files a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js and b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js differ
index d04e876..b706ced 100644 (file)
Binary files a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js and b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js differ
index 7720c6e..9d68afe 100644 (file)
Binary files a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js and b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js differ
index 546fe20..a28e936 100644 (file)
@@ -170,6 +170,8 @@ Y.extend(Editor, Y.Base, {
             return;
         }
 
+        var extraclasses = this.textarea.getAttribute('class');
+
         this._eventHandles = [];
 
         this._wrapper = Y.Node.create('<div class="' + CSS.WRAPPER + '" />');
@@ -178,7 +180,7 @@ Y.extend(Editor, Y.Base, {
                 'role="textbox" ' +
                 'spellcheck="true" ' +
                 'aria-live="off" ' +
-                'class="{{CSS.CONTENT}}" ' +
+                'class="{{CSS.CONTENT}} ' + extraclasses + '" ' +
                 '/>');
         this.editor = Y.Node.create(template({
             elementid: this.get('elementid'),
index b588250..79bbd60 100644 (file)
@@ -18,3 +18,7 @@
         margin-left: -3px;
     }
 }
+
+.mceToolbar td {
+    box-sizing: content-box;
+}
index 34e7c38..5347f17 100644 (file)
@@ -234,6 +234,15 @@ function message_send($eventdata) {
         }
     }
 
+    // Only cache messages, not notifications.
+    if (empty($savemessage->notification)) {
+        // Cache the timecreated value of the last message between these two users.
+        $cache = cache::make('core', 'message_time_last_message_between_users');
+        $key = \core_message\helper::get_last_message_time_created_cache_key($savemessage->useridfrom,
+            $savemessage->useridto);
+        $cache->set($key, $savemessage->timecreated);
+    }
+
     // Store unread message just in case we get a fatal error any time later.
     $savemessage->id = $DB->insert_record('message', $savemessage);
     $eventdata->savedmessageid = $savemessage->id;
index ecff98b..664cf40 100644 (file)
@@ -2517,6 +2517,7 @@ function get_login_url() {
  * @return mixed Void, exit, and die depending on path
  * @throws coding_exception
  * @throws require_login_exception
+ * @throws moodle_exception
  */
 function require_login($courseorid = null, $autologinguest = true, $cm = null, $setwantsurltome = true, $preventredirect = false) {
     global $CFG, $SESSION, $USER, $PAGE, $SITE, $DB, $OUTPUT;
@@ -2684,8 +2685,8 @@ function require_login($courseorid = null, $autologinguest = true, $cm = null, $
     // Make sure the USER has a sesskey set up. Used for CSRF protection.
     sesskey();
 
-    // Do not bother admins with any formalities.
-    if (is_siteadmin()) {
+    // Do not bother admins with any formalities, except for activities pending deletion.
+    if (is_siteadmin() && !($cm && $cm->deletioninprogress)) {
         // Set the global $COURSE.
         if ($cm) {
             $PAGE->set_cm($cm, $course);
@@ -2873,6 +2874,15 @@ function require_login($courseorid = null, $autologinguest = true, $cm = null, $
         }
     }
 
+    // Check whether the activity has been scheduled for deletion. If so, then deny access, even for admins.
+    if ($cm && $cm->deletioninprogress) {
+        if ($preventredirect) {
+            throw new moodle_exception('activityisscheduledfordeletion');
+        }
+        require_once($CFG->dirroot . '/course/lib.php');
+        redirect(course_get_url($course), get_string('activityisscheduledfordeletion', 'error'));
+    }
+
     // Check visibility of activity to current user; includes visible flag, conditional availability, etc.
     if ($cm && !$cm->uservisible) {
         if ($preventredirect) {
@@ -4438,6 +4448,7 @@ function complete_user_login($user) {
             if ($changeurl = $userauth->change_password_url()) {
                 redirect($changeurl);
             } else {
+                require_once($CFG->dirroot . '/login/lib.php');
                 $SESSION->wantsurl = core_login_get_return_url();
                 redirect($CFG->httpswwwroot.'/login/change_password.php');
             }
index 588bb99..2f6c610 100644 (file)
@@ -2334,9 +2334,6 @@ class global_navigation extends navigation_node {
                 if ($USER->id != $user->id) {
                     $messageargs['user2'] = $user->id;
                 }
-                if ($course->id != $SITE->id) {
-                    $messageargs['viewing'] = MESSAGE_VIEW_COURSE. $course->id;
-                }
                 $url = new moodle_url('/message/index.php', $messageargs);
                 $usernode->add(get_string('messages', 'message'), $url, self::TYPE_SETTING, null, 'messages');
             }
@@ -4640,9 +4637,6 @@ class settings_navigation extends navigation_node {
                 if ($USER->id != $user->id) {
                     $messageargs['user2'] = $user->id;
                 }
-                if ($course->id != $SITE->id) {
-                    $messageargs['viewing'] = MESSAGE_VIEW_COURSE. $course->id;
-                }
                 $url = new moodle_url('/message/index.php', $messageargs);
                 $dashboard->add(get_string('messages', 'message'), $url, self::TYPE_SETTING, null, 'messages');
             }
index 2d7cabf..aa28a9e 100644 (file)
@@ -137,7 +137,9 @@ abstract class renderer_factory_base implements renderer_factory {
             // If the target hasn't been specified we need to guess the defaults.
             // We also override the target with the default if the maintenance target has been provided.
             // This ensures we don't use the maintenance renderer if we are processing a special target.
-            if (CLI_SCRIPT) {
+            if (defined('PREFERRED_RENDERER_TARGET')) {
+                $target = PREFERRED_RENDERER_TARGET;
+            } else if (CLI_SCRIPT) {
                 $target = RENDERER_TARGET_CLI;
             } else if (AJAX_SCRIPT) {
                 $target = RENDERER_TARGET_AJAX;
index dc40d2b..053432b 100644 (file)
@@ -152,6 +152,9 @@ class theme_config {
      */
     const DEFAULT_THEME = 'boost';
 
+    /** The key under which the SCSS file is stored amongst the CSS files. */
+    const SCSS_KEY = '__SCSS__';
+
     /**
      * @var array You can base your theme on other themes by linking to the other theme as
      * parents. This lets you use the CSS and layouts from the other themes
@@ -294,7 +297,7 @@ class theme_config {
      * object as second parameter. A return value is not required, the tree can
      * be edited in place.
      */
-    public $csstreepostprocess = null;
+    public $csstreepostprocessor = null;
 
     /**
      * @var string Accessibility: Right arrow-like character is
@@ -427,10 +430,18 @@ class theme_config {
     public $lessfile = false;
 
     /**
-     * The SCSS file to compile. This takes precedence over the LESS file.
-     * @var string
+     * The SCSS file to compile (without .scss), located in the scss/ folder of the theme.
+     * Or a Closure, which receives the theme_config as argument and must
+     * return the SCSS content. This setting takes precedence over self::$lessfile.
+     * @var string|Closure
      */
-    public $scssfile = false;
+    public $scss = false;
+
+    /**
+     * Local cache of the SCSS property.
+     * @var false|array
+     */
+    protected $scsscache = null;
 
     /**
      * The name of the function to call to get the LESS code to inject.
@@ -535,7 +546,7 @@ class theme_config {
             'rendererfactory', 'csspostprocess', 'editor_sheets', 'rarrow', 'larrow', 'uarrow', 'darrow',
             'hidefromselector', 'doctype', 'yuicssmodules', 'blockrtlmanipulations',
             'lessfile', 'extralesscallback', 'lessvariablescallback', 'blockrendermethod',
-            'scssfile', 'extrascsscallback', 'prescsscallback', 'csstreepostprocessor');
+            'scss', 'extrascsscallback', 'prescsscallback', 'csstreepostprocessor');
 
         foreach ($config as $key=>$value) {
             if (in_array($key, $configurable)) {
@@ -815,7 +826,7 @@ class theme_config {
                     // We need to serve parents individually otherwise we may easily exceed the style limit IE imposes (4096).
                     $urls[] = new moodle_url($baseurl, array('theme'=>$this->name,'type'=>'ie', 'subtype'=>'parents', 'sheet'=>$parent));
                 }
-                if (!empty($this->scssfile)) {
+                if ($this->get_scss_property()) {
                     // No need to define the type as IE here.
                     $urls[] = new moodle_url($baseurl, array('theme' => $this->name, 'type' => 'scss'));
                 } else if (!empty($this->lessfile)) {
@@ -834,7 +845,7 @@ class theme_config {
                     }
                 }
                 foreach ($css['theme'] as $sheet => $filename) {
-                    if ($sheet === $this->scssfile) {
+                    if ($sheet === self::SCSS_KEY) {
                         // This is the theme SCSS file.
                         $urls[] = new moodle_url($baseurl, array('theme' => $this->name, 'type' => 'scss'));
                     } else if ($sheet === $this->lessfile) {
@@ -868,7 +879,7 @@ class theme_config {
                         $csscontent .= file_get_contents($v) . "\n";
                     }
                 } else {
-                    if ($type === 'theme' && $identifier === $this->scssfile) {
+                    if ($type === 'theme' && $identifier === self::SCSS_KEY) {
                         // We need the content from SCSS because this is the SCSS file from the theme.
                         $csscontent .= $this->get_css_content_from_scss(false);
                     } else if ($type === 'theme' && $identifier === $this->lessfile) {
@@ -935,7 +946,7 @@ class theme_config {
             } else if ($subtype === 'theme') {
                 $cssfiles = $css['theme'];
                 foreach ($cssfiles as $key => $value) {
-                    if (in_array($key, [$this->lessfile, $this->scssfile])) {
+                    if (in_array($key, [$this->lessfile, self::SCSS_KEY])) {
                         // Remove the LESS/SCSS file from the theme CSS files.
                         // The LESS/SCSS files use the type 'less' or 'scss', not 'ie'.
                         unset($cssfiles[$key]);
@@ -1075,15 +1086,13 @@ class theme_config {
             }
         }
 
+
         // Current theme sheets and less file.
         // We first add the SCSS, or LESS file because we want the CSS ones to
-        // be included after the SCSS/LESS code. However, if both the SCSS/LESS file
+        // be included after the SCSS/LESS code. However, if both the LESS file
         // and a CSS file share the same name, the CSS file is ignored.
-        if (!empty($this->scssfile)) {
-            $sheetfile = "{$this->dir}/scss/{$this->scssfile}.scss";
-            if (is_readable($sheetfile)) {
-                $cssfiles['theme'][$this->scssfile] = $sheetfile;
-            }
+        if ($this->get_scss_property()) {
+            $cssfiles['theme'][self::SCSS_KEY] = true;
         } else if (!empty($this->lessfile)) {
             $sheetfile = "{$this->dir}/less/{$this->lessfile}.less";
             if (is_readable($sheetfile)) {
@@ -1181,24 +1190,23 @@ class theme_config {
     protected function get_css_content_from_scss($themedesigner) {
         global $CFG;
 
-        $scssfile = $this->scssfile;
-        if (!$scssfile || !is_readable($this->dir . '/scss/' . $scssfile . '.scss')) {
+        list($paths, $scss) = $this->get_scss_property();
+        if (!$scss) {
             throw new coding_exception('The theme did not define a SCSS file, or it is not readable.');
         }
 
         // We might need more memory to do this, so let's play safe.
         raise_memory_limit(MEMORY_EXTRA);
 
-        // Files list.
-        $files = $this->get_css_files($themedesigner);
-
-        // Get the SCSS file path.
-        $themescssfile = $files['theme'][$scssfile];
-
         // Set-up the compiler.
         $compiler = new core_scss();
         $compiler->prepend_raw_scss($this->get_pre_scss_code());
-        $compiler->set_file($themescssfile);
+        if (is_string($scss)) {
+            $compiler->set_file($scss);
+        } else {
+            $compiler->append_raw_scss($scss($this));
+            $compiler->setImportPaths($paths);
+        }
         $compiler->append_raw_scss($this->get_extra_scss_code());
 
         try {
@@ -1207,7 +1215,7 @@ class theme_config {
 
         } catch (\Leafo\ScssPhp\Exception $e) {
             $compiled = false;
-            debugging('Error while compiling SCSS ' . $scssfile . ' file: ' . $e->getMessage(), DEBUG_DEVELOPER);
+            debugging('Error while compiling SCSS: ' . $e->getMessage(), DEBUG_DEVELOPER);
         }
 
         // Try to save memory.
@@ -1344,6 +1352,47 @@ class theme_config {
         return $content;
     }
 
+    /**
+     * Get the SCSS property.
+     *
+     * This resolves whether a SCSS file (or content) has to be used when generating
+     * the stylesheet for the theme. It will look at parents themes and check the
+     * SCSS properties there.
+     *
+     * @return False when SCSS is not used.
+     *         An array with the import paths, and the path to the SCSS file or Closure as second.
+     */
+    public function get_scss_property() {
+        if ($this->scsscache === null) {
+            $configs = [$this] + $this->parent_configs;
+            $scss = null;
+
+            foreach ($configs as $config) {
+                $path = "{$config->dir}/scss";
+
+                // We collect the SCSS property until we've found one.
+                if (empty($scss) && !empty($config->scss)) {
+                    $candidate = is_string($config->scss) ? "{$path}/{$config->scss}.scss" : $config->scss;
+                    if ($candidate instanceof Closure) {
+                        $scss = $candidate;
+                    } else if (is_string($candidate) && is_readable($candidate)) {
+                        $scss = $candidate;
+                    }
+                }
+
+                // We collect the import paths once we've found a SCSS property.
+                if ($scss && is_dir($path)) {
+                    $paths[] = $path;
+                }
+
+            }
+
+            $this->scsscache = $scss !== null ? [$paths, $scss] : false;
+        }
+
+        return $this->scsscache;
+    }
+
     /**
      * Generate a URL to the file that serves theme JavaScript files.
      *
@@ -1513,9 +1562,16 @@ class theme_config {
             }
         }
 
+        // Now resolve all theme settings or do any other postprocessing.
+        // This needs to be done before calling core parser, since the parser strips [[settings]] tags.
+        $csspostprocess = $this->csspostprocess;
+        if (function_exists($csspostprocess)) {
+            $css = $csspostprocess($css, $this);
+        }
+
         // Post processing using an object representation of CSS.
-        $hastreeprocessor = !empty($this->csstreepostprocessor) && function_exists($this->csstreepostprocessor);
-        $needsparsing = $hastreeprocessor || !empty($this->rtlmode);
+        $treeprocessor = $this->get_css_tree_post_processor();
+        $needsparsing = !empty($treeprocessor) || !empty($this->rtlmode);
         if ($needsparsing) {
             $parser = new core_cssparser($css);
             $csstree = $parser->parse();
@@ -1525,21 +1581,14 @@ class theme_config {
                 $this->rtlize($csstree);
             }
 
-            if ($hastreeprocessor) {
-                $fn = $this->csstreepostprocessor;
-                $fn($csstree, $this);
+            if ($treeprocessor) {
+                $treeprocessor($csstree, $this);
             }
 
             $css = $csstree->render();
             unset($csstree);
         }
 
-        // now resolve all theme settings or do any other postprocessing
-        $csspostprocess = $this->csspostprocess;
-        if (function_exists($csspostprocess)) {
-            $css = $csspostprocess($css, $this);
-        }
-
         return $css;
     }
 
@@ -2174,6 +2223,21 @@ class theme_config {
         // Default it to blocks.
         return 'blocks';
     }
+
+    /**
+     * Get the callable for CSS tree post processing.
+     *
+     * @return string|null
+     */
+    public function get_css_tree_post_processor() {
+        $configs = [$this] + $this->parent_configs;
+        foreach ($configs as $config) {
+            if (!empty($config->csstreepostprocessor) && is_callable($config->csstreepostprocessor)) {
+                return $config->csstreepostprocessor;
+            }
+        }
+        return null;
+    }
 }
 
 /**
index 0962a24..af4b916 100644 (file)
@@ -984,7 +984,8 @@ function question_category_select_menu($contexts, $top = false, $currentcat = 0,
         $options[] = array($group => $opts);
     }
     echo html_writer::label(get_string('questioncategory', 'core_question'), 'id_movetocategory', false, array('class' => 'accesshide'));
-    echo html_writer::select($options, 'category', $selected, $choose, array('id' => 'id_movetocategory'));
+    $attrs = array('id' => 'id_movetocategory', 'class' => 'custom-select');
+    echo html_writer::select($options, 'category', $selected, $choose, $attrs);
 }
 
 /**
index f80ce6b..fb16784 100644 (file)
@@ -553,8 +553,8 @@ if (!PHPUNIT_TEST or PHPUNIT_UTIL) {
 }
 
 // Acceptance tests needs special output to capture the errors,
-// but not necessary for behat CLI command.
-if (defined('BEHAT_SITE_RUNNING') && !defined('BEHAT_TEST')) {
+// but not necessary for behat CLI command and init script.
+if (defined('BEHAT_SITE_RUNNING') && !defined('BEHAT_TEST') && !defined('BEHAT_UTIL')) {
     require_once(__DIR__ . '/behat/lib.php');
     set_error_handler('behat_error_handler', E_ALL | E_STRICT);
 }
index 17a9701..18bd954 100644 (file)
             </div>
             {{> core/loading }}
         </div>
+        {{$anchor}}
+            <a class="see-all-link"
+                href="{{{urls.seeall}}}">
+                <div class="popover-region-footer-container">
+                    <div class="popover-region-seeall-text">{{#str}} seeall, message {{/str}}</div>
+                </div>
+            </a>
+        {{/anchor}}
     </div>
 </div>
index b219753..10b3cdb 100644 (file)
@@ -128,6 +128,21 @@ class core_coursecatlib_testcase extends advanced_testcase {
         } catch (moodle_exception $e) {
             $this->assertInstanceOf('moodle_exception', $e);
         }
+        // Test that duplicates with an idnumber of 0 cannot be created.
+        coursecat::create(array('name' => 'Cat3', 'idnumber' => '0'));
+        try {
+            coursecat::create(array('name' => 'Cat4', 'idnumber' => '0'));
+            $this->fail('Duplicate idnumber "0" exception expected in coursecat::create');
+        } catch (moodle_exception $e) {
+            $this->assertInstanceOf('moodle_exception', $e);
+        }
+        // Test an update cannot make a duplicate idnumber of 0.
+        try {
+            $cat2->update(array('idnumber' => '0'));
+            $this->fail('Duplicate idnumber "0" exception expected in coursecat::update');
+        } catch (Exception $e) {
+            $this->assertInstanceOf('moodle_exception', $e);
+        }
     }
 
     public function test_visibility() {
index bba4ceb..50aa919 100644 (file)
@@ -24,7 +24,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-// Hack to let tests run on travis..
+// Hack to let tests run on Travis CI.
 defined('CURL_SSLVERSION_TLSv1_2') || define('CURL_SSLVERSION_TLSv1_2', 6);
 
 /**
@@ -36,16 +36,6 @@ defined('CURL_SSLVERSION_TLSv1_2') || define('CURL_SSLVERSION_TLSv1_2', 6);
  */
 class upgrade_util_testcase extends advanced_testcase {
 
-    /**
-     * A cURL version that supports TLS 1.2.
-     */
-    const VALID_CURL_VERSION = 467456;
-
-    /**
-     * A cURL version that does not support TLS 1.2.
-     */
-    const INVALID_CURL_VERSION = 467455;
-
     /**
      * The value of PHP_ZTS when thread safety is enabled.
      */
@@ -132,17 +122,11 @@ class upgrade_util_testcase extends advanced_testcase {
 
         // Set the curl values we are testing to the passed data.
         $curlinfo['ssl_version'] = $sslversion;
-        $curlinfo['version_number'] = self::VALID_CURL_VERSION;
 
         // Set uname to system value if none passed in test case.
         $uname = !empty($uname) ? $uname : php_uname('r');
 
         $this->assertSame($expected, \core\upgrade\util::can_use_tls12($curlinfo, $uname));
-
-        // Now set the curl version to outdated one.
-        $curlinfo['version_number'] = self::INVALID_CURL_VERSION;
-        // Tls12 should never be possible now curl version is bad.
-        $this->assertFalse(\core\upgrade\util::can_use_tls12($curlinfo, $uname));
     }
 
     /**
index 1f7b641..12441f2 100644 (file)
@@ -74,7 +74,7 @@ class webdav_client {
     private $_header='';
     private $_body='';
     private $_connection_closed = false;
-    private $_maxheaderlenth = 1000;
+    private $_maxheaderlenth = 65536;
     private $_digestchallenge = null;
     private $_cnonce = '';
     private $_nc = 0;
index 87d11c8..785b901 100644 (file)
Binary files a/media/player/videojs/amd/build/Youtube.min.js and b/media/player/videojs/amd/build/Youtube.min.js differ
index 3d94e7f..6692d51 100644 (file)
Binary files a/media/player/videojs/amd/build/video.min.js and b/media/player/videojs/amd/build/video.min.js differ
index c4c8c4d..c2391da 100644 (file)
@@ -77,7 +77,7 @@ THE SOFTWARE. */
       this.el_.parentNode.className = this.el_.parentNode.className
         .replace(' vjs-youtube', '')
         .replace(' vjs-youtube-mobile', '');
-      this.el_.parentNode.removeChild(this.el_);
+      this.el_.remove();
 
       //Needs to be called after the YouTube player is destroyed, otherwise there will be a null reference exception
       Tech.prototype.dispose.call(this);
index e8d2b12..258cd06 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * @license
- * Video.js 5.11.8 <http://videojs.com/>
+ * Video.js 5.12.6 <http://videojs.com/>
  * Copyright Brightcove, Inc. <https://www.brightcove.com/>
  * Available under Apache License Version 2.0
  * <https://github.com/videojs/video.js/blob/master/LICENSE>
  */
 
 (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.videojs = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
-(function (global){
-var topLevel = typeof global !== 'undefined' ? global :
-    typeof window !== 'undefined' ? window : {}
-var minDoc = _dereq_('min-document');
+'use strict';
 
-if (typeof document !== 'undefined') {
-    module.exports = document;
-} else {
-    var doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'];
+exports.__esModule = true;
 
-    if (!doccy) {
-        doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc;
-    }
+var _button = _dereq_(2);
 
-    module.exports = doccy;
-}
+var _button2 = _interopRequireDefault(_button);
 
-}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
-//# sourceMappingURL=data:application/json;charset:utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9nbG9iYWwvZG9jdW1lbnQuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyJ2YXIgdG9wTGV2ZWwgPSB0eXBlb2YgZ2xvYmFsICE9PSAndW5kZWZpbmVkJyA/IGdsb2JhbCA6XG4gICAgdHlwZW9mIHdpbmRvdyAhPT0gJ3VuZGVmaW5lZCcgPyB3aW5kb3cgOiB7fVxudmFyIG1pbkRvYyA9IHJlcXVpcmUoJ21pbi1kb2N1bWVudCcpO1xuXG5pZiAodHlwZW9mIGRvY3VtZW50ICE9PSAndW5kZWZpbmVkJykge1xuICAgIG1vZHVsZS5leHBvcnRzID0gZG9jdW1lbnQ7XG59IGVsc2Uge1xuICAgIHZhciBkb2NjeSA9IHRvcExldmVsWydfX0dMT0JBTF9ET0NVTUVOVF9DQUNIRUA0J107XG5cbiAgICBpZiAoIWRvY2N5KSB7XG4gICAgICAgIGRvY2N5ID0gdG9wTGV2ZWxbJ19fR0xPQkFMX0RPQ1VNRU5UX0NBQ0hFQDQnXSA9IG1pbkRvYztcbiAgICB9XG5cbiAgICBtb2R1bGUuZXhwb3J0cyA9IGRvY2N5O1xufVxuIl19
-},{"min-document":3}],2:[function(_dereq_,module,exports){
-(function (global){
-if (typeof window !== "undefined") {
-    module.exports = window;
-} else if (typeof global !== "undefined") {
-    module.exports = global;
-} else if (typeof self !== "undefined"){
-    module.exports = self;
-} else {
-    module.exports = {};
-}
+var _component = _dereq_(5);
 
-}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
-//# sourceMappingURL=data:application/json;charset:utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9nbG9iYWwvd2luZG93LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsImZpbGUiOiJnZW5lcmF0ZWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlc0NvbnRlbnQiOlsiaWYgKHR5cGVvZiB3aW5kb3cgIT09IFwidW5kZWZpbmVkXCIpIHtcbiAgICBtb2R1bGUuZXhwb3J0cyA9IHdpbmRvdztcbn0gZWxzZSBpZiAodHlwZW9mIGdsb2JhbCAhPT0gXCJ1bmRlZmluZWRcIikge1xuICAgIG1vZHVsZS5leHBvcnRzID0gZ2xvYmFsO1xufSBlbHNlIGlmICh0eXBlb2Ygc2VsZiAhPT0gXCJ1bmRlZmluZWRcIil7XG4gICAgbW9kdWxlLmV4cG9ydHMgPSBzZWxmO1xufSBlbHNlIHtcbiAgICBtb2R1bGUuZXhwb3J0cyA9IHt9O1xufVxuIl19
-},{}],3:[function(_dereq_,module,exports){
+var _component2 = _interopRequireDefault(_component);
 
-},{}],4:[function(_dereq_,module,exports){
-var getNative = _dereq_('../internal/getNative');
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                * @file big-play-button.js
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                */
 
-/* Native method references for those with the same name as other `lodash` methods. */
-var nativeNow = getNative(Date, 'now');
 
 /**
- * Gets the number of milliseconds that have elapsed since the Unix epoch
- * (1 January 1970 00:00:00 UTC).
- *
- * @static
- * @memberOf _
- * @category Date
- * @example
+ * Initial play button. Shows before the video has played. The hiding of the
+ * big play button is done via CSS and player states.
  *
- * _.defer(function(stamp) {
- *   console.log(_.now() - stamp);
- * }, _.now());
- * // => logs the number of milliseconds it took for the deferred function to be invoked
+ * @param {Object} player  Main Player
+ * @param {Object=} options Object of option names and values
+ * @extends Button
+ * @class BigPlayButton
  */
-var now = nativeNow || function() {
-  return new Date().getTime();
-};
+var BigPlayButton = function (_Button) {
+  _inherits(BigPlayButton, _Button);
 
-module.exports = now;
+  function BigPlayButton(player, options) {
+    _classCallCheck(this, BigPlayButton);
 
-},{"../internal/getNative":20}],5:[function(_dereq_,module,exports){
-var isObject = _dereq_('../lang/isObject'),
-    now = _dereq_('../date/now');
+    return _possibleConstructorReturn(this, _Button.call(this, player, options));
+  }
 
-/** Used as the `TypeError` message for "Functions" methods. */
-var FUNC_ERROR_TEXT = 'Expected a function';
+  /**
+   * Allow sub components to stack CSS class names
+   *
+   * @return {String} The constructed class name
+   * @method buildCSSClass
+   */
 
-/* Native method references for those with the same name as other `lodash` methods. */
-var nativeMax = Math.max;
 
-/**
- * Creates a debounced function that delays invoking `func` until after `wait`
- * milliseconds have elapsed since the last time the debounced function was
- * invoked. The debounced function comes with a `cancel` method to cancel
- * delayed invocations. Provide an options object to indicate that `func`
- * should be invoked on the leading and/or trailing edge of the `wait` timeout.
- * Subsequent calls to the debounced function return the result of the last
- * `func` invocation.
- *
- * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
- * on the trailing edge of the timeout only if the the debounced function is
- * invoked more than once during the `wait` timeout.
- *
- * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation)
- * for details over the differences between `_.debounce` and `_.throttle`.
- *
- * @static
- * @memberOf _
- * @category Function
- * @param {Function} func The function to debounce.
- * @param {number} [wait=0] The number of milliseconds to delay.
- * @param {Object} [options] The options object.
- * @param {boolean} [options.leading=false] Specify invoking on the leading
- *  edge of the timeout.
- * @param {number} [options.maxWait] The maximum time `func` is allowed to be
- *  delayed before it's invoked.
- * @param {boolean} [options.trailing=true] Specify invoking on the trailing
- *  edge of the timeout.
- * @returns {Function} Returns the new debounced function.
- * @example
- *
- * // avoid costly calculations while the window size is in flux
- * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
- *
- * // invoke `sendMail` when the click event is fired, debouncing subsequent calls
- * jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
- *   'leading': true,
- *   'trailing': false
- * }));
- *
- * // ensure `batchLog` is invoked once after 1 second of debounced calls
- * var source = new EventSource('/stream');
- * jQuery(source).on('message', _.debounce(batchLog, 250, {
- *   'maxWait': 1000
- * }));
- *
- * // cancel a debounced call
- * var todoChanges = _.debounce(batchLog, 1000);
- * Object.observe(models.todo, todoChanges);
- *
- * Object.observe(models, function(changes) {
- *   if (_.find(changes, { 'user': 'todo', 'type': 'delete'})) {
- *     todoChanges.cancel();
- *   }
- * }, ['delete']);
- *
- * // ...at some point `models.todo` is changed
- * models.todo.completed = true;
- *
- * // ...before 1 second has passed `models.todo` is deleted
- * // which cancels the debounced `todoChanges` call
- * delete models.todo;
- */
-function debounce(func, wait, options) {
-  var args,
-      maxTimeoutId,
-      result,
-      stamp,
-      thisArg,
-      timeoutId,
-      trailingCall,
-      lastCalled = 0,
-      maxWait = false,
-      trailing = true;
+  BigPlayButton.prototype.buildCSSClass = function buildCSSClass() {
+    return 'vjs-big-play-button';
+  };
 
-  if (typeof func != 'function') {
-    throw new TypeError(FUNC_ERROR_TEXT);
-  }
-  wait = wait < 0 ? 0 : (+wait || 0);
-  if (options === true) {
-    var leading = true;
-    trailing = false;
-  } else if (isObject(options)) {
-    leading = !!options.leading;
-    maxWait = 'maxWait' in options && nativeMax(+options.maxWait || 0, wait);
-    trailing = 'trailing' in options ? !!options.trailing : trailing;
-  }
+  /**
+   * Handles click for play
+   *
+   * @method handleClick
+   */
 
-  function cancel() {
-    if (timeoutId) {
-      clearTimeout(timeoutId);
-    }
-    if (maxTimeoutId) {
-      clearTimeout(maxTimeoutId);
-    }
-    lastCalled = 0;
-    maxTimeoutId = timeoutId = trailingCall = undefined;
-  }
 
-  function complete(isCalled, id) {
-    if (id) {
-      clearTimeout(id);
-    }
-    maxTimeoutId = timeoutId = trailingCall = undefined;
-    if (isCalled) {
-      lastCalled = now();
-      result = func.apply(thisArg, args);
-      if (!timeoutId && !maxTimeoutId) {
-        args = thisArg = undefined;
-      }
-    }
-  }
+  BigPlayButton.prototype.handleClick = function handleClick() {
+    this.player_.play();
+  };
 
-  function delayed() {
-    var remaining = wait - (now() - stamp);
-    if (remaining <= 0 || remaining > wait) {
-      complete(trailingCall, maxTimeoutId);
-    } else {
-      timeoutId = setTimeout(delayed, remaining);
-    }
-  }
+  return BigPlayButton;
+}(_button2['default']);
 
-  function maxDelayed() {
-    complete(trailing, timeoutId);
-  }
+BigPlayButton.prototype.controlText_ = 'Play Video';
 
-  function debounced() {
-    args = arguments;
-    stamp = now();
-    thisArg = this;
-    trailingCall = trailing && (timeoutId || !leading);
+_component2['default'].registerComponent('BigPlayButton', BigPlayButton);
+exports['default'] = BigPlayButton;
 
-    if (maxWait === false) {
-      var leadingCall = leading && !timeoutId;
-    } else {
-      if (!maxTimeoutId && !leading) {
-        lastCalled = stamp;
-      }
-      var remaining = maxWait - (stamp - lastCalled),
-          isCalled = remaining <= 0 || remaining > maxWait;
+},{"2":2,"5":5}],2:[function(_dereq_,module,exports){
+'use strict';
 
-      if (isCalled) {
-        if (maxTimeoutId) {
-          maxTimeoutId = clearTimeout(maxTimeoutId);
-        }
-        lastCalled = stamp;
-        result = func.apply(thisArg, args);
-      }
-      else if (!maxTimeoutId) {
-        maxTimeoutId = setTimeout(maxDelayed, remaining);
-      }
-    }
-    if (isCalled && timeoutId) {
-      timeoutId = clearTimeout(timeoutId);
-    }
-    else if (!timeoutId && wait !== maxWait) {
-      timeoutId = setTimeout(delayed, wait);
-    }
-    if (leadingCall) {
-      isCalled = true;
-      result = func.apply(thisArg, args);
-    }
-    if (isCalled && !timeoutId && !maxTimeoutId) {
-      args = thisArg = undefined;
-    }
-    return result;
-  }
-  debounced.cancel = cancel;
-  return debounced;
-}
+exports.__esModule = true;
 
-module.exports = debounce;
+var _clickableComponent = _dereq_(3);
 
-},{"../date/now":4,"../lang/isObject":33}],6:[function(_dereq_,module,exports){
-/** Used as the `TypeError` message for "Functions" methods. */
-var FUNC_ERROR_TEXT = 'Expected a function';
+var _clickableComponent2 = _interopRequireDefault(_clickableComponent);
 
-/* Native method references for those with the same name as other `lodash` methods. */
-var nativeMax = Math.max;
+var _component = _dereq_(5);
 
-/**
- * Creates a function that invokes `func` with the `this` binding of the
- * created function and arguments from `start` and beyond provided as an array.
- *
- * **Note:** This method is based on the [rest parameter](https://developer.mozilla.org/Web/JavaScript/Reference/Functions/rest_parameters).
- *
- * @static
- * @memberOf _
- * @category Function
- * @param {Function} func The function to apply a rest parameter to.
- * @param {number} [start=func.length-1] The start position of the rest parameter.
- * @returns {Function} Returns the new function.
- * @example
- *
- * var say = _.restParam(function(what, names) {
- *   return what + ' ' + _.initial(names).join(', ') +
- *     (_.size(names) > 1 ? ', & ' : '') + _.last(names);
- * });
- *
- * say('hello', 'fred', 'barney', 'pebbles');
- * // => 'hello fred, barney, & pebbles'
- */
-function restParam(func, start) {
-  if (typeof func != 'function') {
-    throw new TypeError(FUNC_ERROR_TEXT);
-  }
-  start = nativeMax(start === undefined ? (func.length - 1) : (+start || 0), 0);
-  return function() {
-    var args = arguments,
-        index = -1,
-        length = nativeMax(args.length - start, 0),
-        rest = Array(length);
+var _component2 = _interopRequireDefault(_component);
 
-    while (++index < length) {
-      rest[index] = args[start + index];
-    }
-    switch (start) {
-      case 0: return func.call(this, rest);
-      case 1: return func.call(this, args[0], rest);
-      case 2: return func.call(this, args[0], args[1], rest);
-    }
-    var otherArgs = Array(start + 1);
-    index = -1;
-    while (++index < start) {
-      otherArgs[index] = args[index];
-    }
-    otherArgs[start] = rest;
-    return func.apply(this, otherArgs);
-  };
-}
+var _log = _dereq_(85);
 
-module.exports = restParam;
+var _log2 = _interopRequireDefault(_log);
 
-},{}],7:[function(_dereq_,module,exports){
-var debounce = _dereq_('./debounce'),
-    isObject = _dereq_('../lang/isObject');
+var _object = _dereq_(136);
 
-/** Used as the `TypeError` message for "Functions" methods. */
-var FUNC_ERROR_TEXT = 'Expected a function';
+var _object2 = _interopRequireDefault(_object);
 
-/**
- * Creates a throttled function that only invokes `func` at most once per
- * every `wait` milliseconds. The throttled function comes with a `cancel`
- * method to cancel delayed invocations. Provide an options object to indicate
- * that `func` should be invoked on the leading and/or trailing edge of the
- * `wait` timeout. Subsequent calls to the throttled function return the
- * result of the last `func` call.
- *
- * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
- * on the trailing edge of the timeout only if the the throttled function is
- * invoked more than once during the `wait` timeout.
- *
- * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation)
- * for details over the differences between `_.throttle` and `_.debounce`.
- *
- * @static
- * @memberOf _
- * @category Function
- * @param {Function} func The function to throttle.
- * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
- * @param {Object} [options] The options object.
- * @param {boolean} [options.leading=true] Specify invoking on the leading
- *  edge of the timeout.
- * @param {boolean} [options.trailing=true] Specify invoking on the trailing
- *  edge of the timeout.
- * @returns {Function} Returns the new throttled function.
- * @example
- *
- * // avoid excessively updating the position while scrolling
- * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
- *
- * // invoke `renewToken` when the click event is fired, but not more than once every 5 minutes
- * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, {
- *   'trailing': false
- * }));
- *
- * // cancel a trailing throttled call
- * jQuery(window).on('popstate', throttled.cancel);
- */
-function throttle(func, wait, options) {
-  var leading = true,
-      trailing = true;
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
 
-  if (typeof func != 'function') {
-    throw new TypeError(FUNC_ERROR_TEXT);
-  }
-  if (options === false) {
-    leading = false;
-  } else if (isObject(options)) {
-    leading = 'leading' in options ? !!options.leading : leading;
-    trailing = 'trailing' in options ? !!options.trailing : trailing;
-  }
-  return debounce(func, wait, { 'leading': leading, 'maxWait': +wait, 'trailing': trailing });
-}
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                * @file button.js
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                */
 
-module.exports = throttle;
 
-},{"../lang/isObject":33,"./debounce":5}],8:[function(_dereq_,module,exports){
 /**
- * Copies the values of `source` to `array`.
+ * Base class for all buttons
  *
- * @private
- * @param {Array} source The array to copy values from.
- * @param {Array} [array=[]] The array to copy values to.
- * @returns {Array} Returns `array`.
+ * @param {Object} player  Main Player
+ * @param {Object=} options Object of option names and values
+ * @extends ClickableComponent
+ * @class Button
  */
-function arrayCopy(source, array) {
-  var index = -1,
-      length = source.length;
+var Button = function (_ClickableComponent) {
+  _inherits(Button, _ClickableComponent);
 
-  array || (array = Array(length));
-  while (++index < length) {
-    array[index] = source[index];
+  function Button(player, options) {
+    _classCallCheck(this, Button);
+
+    return _possibleConstructorReturn(this, _ClickableComponent.call(this, player, options));
   }
-  return array;
-}
 
-module.exports = arrayCopy;
+  /**
+   * Create the component's DOM element
+   *
+   * @param {String=} type Element's node type. e.g. 'div'
+   * @param {Object=} props An object of properties that should be set on the element
+   * @param {Object=} attributes An object of attributes that should be set on the element
+   * @return {Element}
+   * @method createEl
+   */
 
-},{}],9:[function(_dereq_,module,exports){
-/**
- * A specialized version of `_.forEach` for arrays without support for callback
- * shorthands and `this` binding.
- *
- * @private
- * @param {Array} array The array to iterate over.
- * @param {Function} iteratee The function invoked per iteration.
- * @returns {Array} Returns `array`.
- */
-function arrayEach(array, iteratee) {
-  var index = -1,
-      length = array.length;
 
-  while (++index < length) {
-    if (iteratee(array[index], index, array) === false) {
-      break;
-    }
-  }
-  return array;
-}
+  Button.prototype.createEl = function createEl() {
+    var tag = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'button';
+    var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+    var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
 
-module.exports = arrayEach;
+    props = (0, _object2['default'])({
+      className: this.buildCSSClass()
+    }, props);
 
-},{}],10:[function(_dereq_,module,exports){
-/**
- * Copies properties of `source` to `object`.
- *
- * @private
- * @param {Object} source The object to copy properties from.
- * @param {Array} props The property names to copy.
- * @param {Object} [object={}] The object to copy properties to.
- * @returns {Object} Returns `object`.
- */
-function baseCopy(source, props, object) {
-  object || (object = {});
+    if (tag !== 'button') {
+      _log2['default'].warn('Creating a Button with an HTML element of ' + tag + ' is deprecated; use ClickableComponent instead.');
 
-  var index = -1,
-      length = props.length;
+      // Add properties for clickable element which is not a native HTML button
+      props = (0, _object2['default'])({
+        tabIndex: 0
+      }, props);
 
-  while (++index < length) {
-    var key = props[index];
-    object[key] = source[key];
-  }
-  return object;
-}
+      // Add ARIA attributes for clickable element which is not a native HTML button
+      attributes = (0, _object2['default'])({
+        role: 'button'
+      }, attributes);
+    }
 
-module.exports = baseCopy;
+    // Add attributes for button element
+    attributes = (0, _object2['default'])({
 
-},{}],11:[function(_dereq_,module,exports){
-var createBaseFor = _dereq_('./createBaseFor');
+      // Necessary since the default button type is "submit"
+      'type': 'button',
 
-/**
- * The base implementation of `baseForIn` and `baseForOwn` which iterates
- * over `object` properties returned by `keysFunc` invoking `iteratee` for
- * each property. Iteratee functions may exit iteration early by explicitly
- * returning `false`.
- *
- * @private
- * @param {Object} object The object to iterate over.
- * @param {Function} iteratee The function invoked per iteration.
- * @param {Function} keysFunc The function to get the keys of `object`.
- * @returns {Object} Returns `object`.
- */
-var baseFor = createBaseFor();
+      // let the screen reader user know that the text of the button may change
+      'aria-live': 'polite'
+    }, attributes);
 
-module.exports = baseFor;
+    var el = _component2['default'].prototype.createEl.call(this, tag, props, attributes);
 
-},{"./createBaseFor":18}],12:[function(_dereq_,module,exports){
-var baseFor = _dereq_('./baseFor'),
-    keysIn = _dereq_('../object/keysIn');
+    this.createControlTextEl(el);
 
-/**
- * The base implementation of `_.forIn` without support for callback
- * shorthands and `this` binding.
- *
- * @private
- * @param {Object} object The object to iterate over.
- * @param {Function} iteratee The function invoked per iteration.
- * @returns {Object} Returns `object`.
- */
-function baseForIn(object, iteratee) {
-  return baseFor(object, iteratee, keysIn);
-}
+    return el;
+  };
 
-module.exports = baseForIn;
+  /**
+   * Adds a child component inside this button
+   *
+   * @param {String|Component} child The class name or instance of a child to add
+   * @param {Object=} options Options, including options to be passed to children of the child.
+   * @return {Component} The child component (created by this process if a string was used)
+   * @deprecated
+   * @method addChild
+   */
 
-},{"../object/keysIn":39,"./baseFor":11}],13:[function(_dereq_,module,exports){
-var arrayEach = _dereq_('./arrayEach'),
-    baseMergeDeep = _dereq_('./baseMergeDeep'),
-    isArray = _dereq_('../lang/isArray'),
-    isArrayLike = _dereq_('./isArrayLike'),
-    isObject = _dereq_('../lang/isObject'),
-    isObjectLike = _dereq_('./isObjectLike'),
-    isTypedArray = _dereq_('../lang/isTypedArray'),
-    keys = _dereq_('../object/keys');
 
-/**
- * The base implementation of `_.merge` without support for argument juggling,
- * multiple sources, and `this` binding `customizer` functions.
- *
- * @private
- * @param {Object} object The destination object.
- * @param {Object} source The source object.
- * @param {Function} [customizer] The function to customize merged values.
- * @param {Array} [stackA=[]] Tracks traversed source objects.
- * @param {Array} [stackB=[]] Associates values with source counterparts.
- * @returns {Object} Returns `object`.
- */
-function baseMerge(object, source, customizer, stackA, stackB) {
-  if (!isObject(object)) {
-    return object;
-  }
-  var isSrcArr = isArrayLike(source) && (isArray(source) || isTypedArray(source)),
-      props = isSrcArr ? undefined : keys(source);
+  Button.prototype.addChild = function addChild(child) {
+    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
 
-  arrayEach(props || source, function(srcValue, key) {
-    if (props) {
-      key = srcValue;
-      srcValue = source[key];
-    }
-    if (isObjectLike(srcValue)) {
-      stackA || (stackA = []);
-      stackB || (stackB = []);
-      baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB);
-    }
-    else {
-      var value = object[key],
-          result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
-          isCommon = result === undefined;
+    var className = this.constructor.name;
 
-      if (isCommon) {
-        result = srcValue;
-      }
-      if ((result !== undefined || (isSrcArr && !(key in object))) &&
-          (isCommon || (result === result ? (result !== value) : (value === value)))) {
-        object[key] = result;
-      }
-    }
-  });
-  return object;
-}
+    _log2['default'].warn('Adding an actionable (user controllable) child to a Button (' + className + ') is not supported; use a ClickableComponent instead.');
 
-module.exports = baseMerge;
+    // Avoid the error message generated by ClickableComponent's addChild method
+    return _component2['default'].prototype.addChild.call(this, child, options);
+  };
 
-},{"../lang/isArray":30,"../lang/isObject":33,"../lang/isTypedArray":36,"../object/keys":38,"./arrayEach":9,"./baseMergeDeep":14,"./isArrayLike":21,"./isObjectLike":26}],14:[function(_dereq_,module,exports){
-var arrayCopy = _dereq_('./arrayCopy'),
-    isArguments = _dereq_('../lang/isArguments'),
-    isArray = _dereq_('../lang/isArray'),
-    isArrayLike = _dereq_('./isArrayLike'),
-    isPlainObject = _dereq_('../lang/isPlainObject'),
-    isTypedArray = _dereq_('../lang/isTypedArray'),
-    toPlainObject = _dereq_('../lang/toPlainObject');
+  /**
+   * Handle KeyPress (document level) - Extend with specific functionality for button
+   *
+   * @method handleKeyPress
+   */
 
-/**
- * A specialized version of `baseMerge` for arrays and objects which performs
- * deep merges and tracks traversed objects enabling objects with circular
- * references to be merged.
- *
- * @private
- * @param {Object} object The destination object.
- * @param {Object} source The source object.
- * @param {string} key The key of the value to merge.
- * @param {Function} mergeFunc The function to merge values.
- * @param {Function} [customizer] The function to customize merged values.
- * @param {Array} [stackA=[]] Tracks traversed source objects.
- * @param {Array} [stackB=[]] Associates values with source counterparts.
- * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
- */
-function baseMergeDeep(object, source, key, mergeFunc, customizer, stackA, stackB) {
-  var length = stackA.length,
-      srcValue = source[key];
 
-  while (length--) {
-    if (stackA[length] == srcValue) {
-      object[key] = stackB[length];
+  Button.prototype.handleKeyPress = function handleKeyPress(event) {
+
+    // Ignore Space (32) or Enter (13) key operation, which is handled by the browser for a button.
+    if (event.which === 32 || event.which === 13) {
       return;
     }
-  }
-  var value = object[key],
-      result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
-      isCommon = result === undefined;
 
-  if (isCommon) {
-    result = srcValue;
-    if (isArrayLike(srcValue) && (isArray(srcValue) || isTypedArray(srcValue))) {
-      result = isArray(value)
-        ? value
-        : (isArrayLike(value) ? arrayCopy(value) : []);
-    }
-    else if (isPlainObject(srcValue) || isArguments(srcValue)) {
-      result = isArguments(value)
-        ? toPlainObject(value)
-        : (isPlainObject(value) ? value : {});
-    }
-    else {
-      isCommon = false;
-    }
-  }
-  // Add the source value to the stack of traversed objects and associate
-  // it with its merged value.
-  stackA.push(srcValue);
-  stackB.push(result);
+    // Pass keypress handling up for unsupported keys
+    _ClickableComponent.prototype.handleKeyPress.call(this, event);
+  };
 
-  if (isCommon) {
-    // Recursively merge objects and arrays (susceptible to call stack limits).
-    object[key] = mergeFunc(result, srcValue, customizer, stackA, stackB);
-  } else if (result === result ? (result !== value) : (value === value)) {
-    object[key] = result;
-  }
-}
+  return Button;
+}(_clickableComponent2['default']);
 
-module.exports = baseMergeDeep;
+_component2['default'].registerComponent('Button', Button);
+exports['default'] = Button;
 
-},{"../lang/isArguments":29,"../lang/isArray":30,"../lang/isPlainObject":34,"../lang/isTypedArray":36,"../lang/toPlainObject":37,"./arrayCopy":8,"./isArrayLike":21}],15:[function(_dereq_,module,exports){
-var toObject = _dereq_('./toObject');
+},{"136":136,"3":3,"5":5,"85":85}],3:[function(_dereq_,module,exports){
+'use strict';
 
-/**
- * The base implementation of `_.property` without support for deep paths.
- *
- * @private
- * @param {string} key The key of the property to get.
- * @returns {Function} Returns the new function.
- */
-function baseProperty(key) {
-  return function(object) {
-    return object == null ? undefined : toObject(object)[key];
-  };
-}
+exports.__esModule = true;
 
-module.exports = baseProperty;
+var _component = _dereq_(5);
 
-},{"./toObject":28}],16:[function(_dereq_,module,exports){
-var identity = _dereq_('../utility/identity');
+var _component2 = _interopRequireDefault(_component);
 
-/**
- * A specialized version of `baseCallback` which only supports `this` binding
- * and specifying the number of arguments to provide to `func`.
- *
- * @private
- * @param {Function} func The function to bind.
- * @param {*} thisArg The `this` binding of `func`.
- * @param {number} [argCount] The number of arguments to provide to `func`.
- * @returns {Function} Returns the callback.
- */
-function bindCallback(func, thisArg, argCount) {
-  if (typeof func != 'function') {
-    return identity;
-  }
-  if (thisArg === undefined) {
-    return func;
-  }
-  switch (argCount) {
-    case 1: return function(value) {
-      return func.call(thisArg, value);
-    };
-    case 3: return function(value, index, collection) {
-      return func.call(thisArg, value, index, collection);
-    };
-    case 4: return function(accumulator, value, index, collection) {
-      return func.call(thisArg, accumulator, value, index, collection);
-    };
-    case 5: return function(value, other, key, object, source) {
-      return func.call(thisArg, value, other, key, object, source);
-    };
-  }
-  return function() {
-    return func.apply(thisArg, arguments);
-  };
-}
+var _dom = _dereq_(80);
 
-module.exports = bindCallback;
+var Dom = _interopRequireWildcard(_dom);
 
-},{"../utility/identity":42}],17:[function(_dereq_,module,exports){
-var bindCallback = _dereq_('./bindCallback'),
-    isIterateeCall = _dereq_('./isIterateeCall'),
-    restParam = _dereq_('../function/restParam');
+var _events = _dereq_(81);
 
-/**
- * Creates a `_.assign`, `_.defaults`, or `_.merge` function.
- *
- * @private
- * @param {Function} assigner The function to assign values.
- * @returns {Function} Returns the new assigner function.
- */
-function createAssigner(assigner) {
-  return restParam(function(object, sources) {
-    var index = -1,
-        length = object == null ? 0 : sources.length,
-        customizer = length > 2 ? sources[length - 2] : undefined,
-        guard = length > 2 ? sources[2] : undefined,
-        thisArg = length > 1 ? sources[length - 1] : undefined;
+var Events = _interopRequireWildcard(_events);
 
-    if (typeof customizer == 'function') {
-      customizer = bindCallback(customizer, thisArg, 5);
-      length -= 2;
-    } else {
-      customizer = typeof thisArg == 'function' ? thisArg : undefined;
-      length -= (customizer ? 1 : 0);
-    }
-    if (guard && isIterateeCall(sources[0], sources[1], guard)) {
-      customizer = length < 3 ? undefined : customizer;
-      length = 1;
-    }
-    while (++index < length) {
-      var source = sources[index];
-      if (source) {
-        assigner(object, source, customizer);
-      }
-    }
-    return object;
-  });
-}
+var _fn = _dereq_(82);
 
-module.exports = createAssigner;
+var Fn = _interopRequireWildcard(_fn);
 
-},{"../function/restParam":6,"./bindCallback":16,"./isIterateeCall":24}],18:[function(_dereq_,module,exports){
-var toObject = _dereq_('./toObject');
+var _log = _dereq_(85);
 
-/**
- * Creates a base function for `_.forIn` or `_.forInRight`.
- *
- * @private
- * @param {boolean} [fromRight] Specify iterating from right to left.
- * @returns {Function} Returns the new base function.
- */
-function createBaseFor(fromRight) {
-  return function(object, iteratee, keysFunc) {
-    var iterable = toObject(object),
-        props = keysFunc(object),
-        length = props.length,
-        index = fromRight ? length : -1;
+var _log2 = _interopRequireDefault(_log);
 
-    while ((fromRight ? index-- : ++index < length)) {
-      var key = props[index];
-      if (iteratee(iterable[key], key, iterable) === false) {
-        break;
-      }
-    }
-    return object;
-  };
-}
+var _document = _dereq_(92);
 
-module.exports = createBaseFor;
+var _document2 = _interopRequireDefault(_document);
 
-},{"./toObject":28}],19:[function(_dereq_,module,exports){
-var baseProperty = _dereq_('./baseProperty');
+var _object = _dereq_(136);
 
-/**
- * Gets the "length" property value of `object`.
- *
- * **Note:** This function is used to avoid a [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792)
- * that affects Safari on at least iOS 8.1-8.3 ARM64.
- *
- * @private
- * @param {Object} object The object to query.
- * @returns {*} Returns the "length" value.
- */
-var getLength = baseProperty('length');
+var _object2 = _interopRequireDefault(_object);
 
-module.exports = getLength;
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
 
-},{"./baseProperty":15}],20:[function(_dereq_,module,exports){
-var isNative = _dereq_('../lang/isNative');
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
 
-/**
- * Gets the native function at `key` of `object`.
- *
- * @private
- * @param {Object} object The object to query.
- * @param {string} key The key of the method to get.
- * @returns {*} Returns the function if it's native, else `undefined`.
- */
-function getNative(object, key) {
-  var value = object == null ? undefined : object[key];
-  return isNative(value) ? value : undefined;
-}
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
 
-module.exports = getNative;
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                * @file button.js
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                */
 
-},{"../lang/isNative":32}],21:[function(_dereq_,module,exports){
-var getLength = _dereq_('./getLength'),
-    isLength = _dereq_('./isLength');
 
 /**
- * Checks if `value` is array-like.
+ * Clickable Component which is clickable or keyboard actionable, but is not a native HTML button
  *
- * @private
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
+ * @param {Object} player  Main Player
+ * @param {Object=} options Object of option names and values
+ * @extends Component
+ * @class ClickableComponent
  */
-function isArrayLike(value) {
-  return value != null && isLength(getLength(value));
-}
+var ClickableComponent = function (_Component) {
+  _inherits(ClickableComponent, _Component);
 
-module.exports = isArrayLike;
+  function ClickableComponent(player, options) {
+    _classCallCheck(this, ClickableComponent);
 
-},{"./getLength":19,"./isLength":25}],22:[function(_dereq_,module,exports){
-/**
- * Checks if `value` is a host object in IE < 9.
- *
- * @private
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a host object, else `false`.
- */
-var isHostObject = (function() {
-  try {
-    Object({ 'toString': 0 } + '');
-  } catch(e) {
-    return function() { return false; };
+    var _this = _possibleConstructorReturn(this, _Component.call(this, player, options));
+
+    _this.emitTapEvents();
+
+    _this.on('tap', _this.handleClick);
+    _this.on('click', _this.handleClick);
+    _this.on('focus', _this.handleFocus);
+    _this.on('blur', _this.handleBlur);
+    return _this;
   }
-  return function(value) {
-    // IE < 9 presents many host objects as `Object` objects that can coerce
-    // to strings despite having improperly defined `toString` methods.
-    return typeof value.toString != 'function' && typeof (value + '') == 'string';
-  };
-}());
 
-module.exports = isHostObject;
+  /**
+   * Create the component's DOM element
+   *
+   * @param {String=} type Element's node type. e.g. 'div'
+   * @param {Object=} props An object of properties that should be set on the element
+   * @param {Object=} attributes An object of attributes that should be set on the element
+   * @return {Element}
+   * @method createEl
+   */
 
-},{}],23:[function(_dereq_,module,exports){
-/** Used to detect unsigned integer values. */
-var reIsUint = /^\d+$/;
 
-/**
- * Used as the [maximum length](http://ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer)
- * of an array-like value.
- */
-var MAX_SAFE_INTEGER = 9007199254740991;
+  ClickableComponent.prototype.createEl = function createEl() {
+    var tag = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'div';
+    var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+    var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
 
-/**
- * Checks if `value` is a valid array-like index.
- *
- * @private
- * @param {*} value The value to check.
- * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
- * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
- */
-function isIndex(value, length) {
-  value = (typeof value == 'number' || reIsUint.test(value)) ? +value : -1;
-  length = length == null ? MAX_SAFE_INTEGER : length;
-  return value > -1 && value % 1 == 0 && value < length;
-}
+    props = (0, _object2['default'])({
+      className: this.buildCSSClass(),
+      tabIndex: 0
+    }, props);
 
-module.exports = isIndex;
+    if (tag === 'button') {
+      _log2['default'].error('Creating a ClickableComponent with an HTML element of ' + tag + ' is not supported; use a Button instead.');
+    }
 
-},{}],24:[function(_dereq_,module,exports){
-var isArrayLike = _dereq_('./isArrayLike'),
-    isIndex = _dereq_('./isIndex'),
-    isObject = _dereq_('../lang/isObject');
+    // Add ARIA attributes for clickable element which is not a native HTML button
+    attributes = (0, _object2['default'])({
+      'role': 'button',
 
-/**
- * Checks if the provided arguments are from an iteratee call.
- *
- * @private
- * @param {*} value The potential iteratee value argument.
- * @param {*} index The potential iteratee index or key argument.
- * @param {*} object The potential iteratee object argument.
- * @returns {boolean} Returns `true` if the arguments are from an iteratee call, else `false`.
- */
-function isIterateeCall(value, index, object) {
-  if (!isObject(object)) {
-    return false;
-  }
-  var type = typeof index;
-  if (type == 'number'
-      ? (isArrayLike(object) && isIndex(index, object.length))
-      : (type == 'string' && index in object)) {
-    var other = object[index];
-    return value === value ? (value === other) : (other !== other);
-  }
-  return false;
-}
+      // let the screen reader user know that the text of the element may change
+      'aria-live': 'polite'
+    }, attributes);
 
-module.exports = isIterateeCall;
+    var el = _Component.prototype.createEl.call(this, tag, props, attributes);
 
-},{"../lang/isObject":33,"./isArrayLike":21,"./isIndex":23}],25:[function(_dereq_,module,exports){
-/**
- * Used as the [maximum length](http://ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer)
- * of an array-like value.
- */
-var MAX_SAFE_INTEGER = 9007199254740991;
+    this.createControlTextEl(el);
 
-/**
- * Checks if `value` is a valid array-like length.
- *
- * **Note:** This function is based on [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength).
- *
- * @private
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
- */
-function isLength(value) {
-  return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
-}
+    return el;
+  };
 
-module.exports = isLength;
+  /**
+   * create control text
+   *
+   * @param {Element} el Parent element for the control text
+   * @return {Element}
+   * @method controlText
+   */
 
-},{}],26:[function(_dereq_,module,exports){
-/**
- * Checks if `value` is object-like.
- *
- * @private
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
- */
-function isObjectLike(value) {
-  return !!value && typeof value == 'object';
-}
 
-module.exports = isObjectLike;
+  ClickableComponent.prototype.createControlTextEl = function createControlTextEl(el) {
+    this.controlTextEl_ = Dom.createEl('span', {
+      className: 'vjs-control-text'
+    });
 
-},{}],27:[function(_dereq_,module,exports){
-var isArguments = _dereq_('../lang/isArguments'),
-    isArray = _dereq_('../lang/isArray'),
-    isIndex = _dereq_('./isIndex'),
-    isLength = _dereq_('./isLength'),
-    isString = _dereq_('../lang/isString'),
-    keysIn = _dereq_('../object/keysIn');
+    if (el) {
+      el.appendChild(this.controlTextEl_);
+    }
 
-/** Used for native method references. */
-var objectProto = Object.prototype;
+    this.controlText(this.controlText_, el);
 
-/** Used to check objects for own properties. */
-var hasOwnProperty = objectProto.hasOwnProperty;
+    return this.controlTextEl_;
+  };
 
-/**
- * A fallback implementation of `Object.keys` which creates an array of the
- * own enumerable property names of `object`.
- *
- * @private
- * @param {Object} object The object to query.
- * @returns {Array} Returns the array of property names.
- */
-function shimKeys(object) {
-  var props = keysIn(object),
-      propsLength = props.length,
-      length = propsLength && object.length;
+  /**
+   * Controls text - both request and localize
+   *
+   * @param {String}  text Text for element
+   * @param {Element=} el Element to set the title on
+   * @return {String}
+   * @method controlText
+   */
 
-  var allowIndexes = !!length && isLength(length) &&
-    (isArray(object) || isArguments(object) || isString(object));
 
-  var index = -1,
-      result = [];
+  ClickableComponent.prototype.controlText = function controlText(text) {
+    var el = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.el();
 
-  while (++index < propsLength) {
-    var key = props[index];
-    if ((allowIndexes && isIndex(key, length)) || hasOwnProperty.call(object, key)) {
-      result.push(key);
+    if (!text) {
+      return this.controlText_ || 'Need Text';
     }
-  }
-  return result;
-}
 
-module.exports = shimKeys;
+    var localizedText = this.localize(text);
 
-},{"../lang/isArguments":29,"../lang/isArray":30,"../lang/isString":35,"../object/keysIn":39,"./isIndex":23,"./isLength":25}],28:[function(_dereq_,module,exports){
-var isObject = _dereq_('../lang/isObject'),
-    isString = _dereq_('../lang/isString'),
-    support = _dereq_('../support');
+    this.controlText_ = text;
+    this.controlTextEl_.innerHTML = localizedText;
+    el.setAttribute('title', localizedText);
 
-/**
- * Converts `value` to an object if it's not one.
- *
- * @private
- * @param {*} value The value to process.
- * @returns {Object} Returns the object.
- */
-function toObject(value) {
-  if (support.unindexedChars && isString(value)) {
-    var index = -1,
-        length = value.length,
-        result = Object(value);
+    return this;
+  };
 
-    while (++index < length) {
-      result[index] = value.charAt(index);
-    }
-    return result;
-  }
-  return isObject(value) ? value : Object(value);
-}
+  /**
+   * Allows sub components to stack CSS class names
+   *
+   * @return {String}
+   * @method buildCSSClass
+   */
 
-module.exports = toObject;
 
-},{"../lang/isObject":33,"../lang/isString":35,"../support":41}],29:[function(_dereq_,module,exports){
-var isArrayLike = _dereq_('../internal/isArrayLike'),
-    isObjectLike = _dereq_('../internal/isObjectLike');
+  ClickableComponent.prototype.buildCSSClass = function buildCSSClass() {
+    return 'vjs-control vjs-button ' + _Component.prototype.buildCSSClass.call(this);
+  };
 
-/** Used for native method references. */
-var objectProto = Object.prototype;
+  /**
+   * Adds a child component inside this clickable-component
+   *
+   * @param {String|Component} child The class name or instance of a child to add
+   * @param {Object=} options Options, including options to be passed to children of the child.
+   * @return {Component} The child component (created by this process if a string was used)
+   * @method addChild
+   */
 
-/** Used to check objects for own properties. */
-var hasOwnProperty = objectProto.hasOwnProperty;
 
-/** Native method references. */
-var propertyIsEnumerable = objectProto.propertyIsEnumerable;
+  ClickableComponent.prototype.addChild = function addChild(child) {
+    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
 
-/**
- * Checks if `value` is classified as an `arguments` object.
- *
- * @static
- * @memberOf _
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
- * @example
- *
- * _.isArguments(function() { return arguments; }());
- * // => true
- *
- * _.isArguments([1, 2, 3]);
- * // => false
- */
-function isArguments(value) {
-  return isObjectLike(value) && isArrayLike(value) &&
-    hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee');
-}
+    // TODO: Fix adding an actionable child to a ClickableComponent; currently
+    // it will cause issues with assistive technology (e.g. screen readers)
+    // which support ARIA, since an element with role="button" cannot have
+    // actionable child elements.
 
-module.exports = isArguments;
+    // let className = this.constructor.name;
+    // log.warn(`Adding a child to a ClickableComponent (${className}) can cause issues with assistive technology which supports ARIA, since an element with role="button" cannot have actionable child elements.`);
 
-},{"../internal/isArrayLike":21,"../internal/isObjectLike":26}],30:[function(_dereq_,module,exports){
-var getNative = _dereq_('../internal/getNative'),
-    isLength = _dereq_('../internal/isLength'),
-    isObjectLike = _dereq_('../internal/isObjectLike');
+    return _Component.prototype.addChild.call(this, child, options);
+  };
 
-/** `Object#toString` result references. */
-var arrayTag = '[object Array]';
+  /**
+   * Enable the component element
+   *
+   * @return {Component}
+   * @method enable
+   */
 
-/** Used for native method references. */
-var objectProto = Object.prototype;
 
-/**
- * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
- * of values.
- */
-var objToString = objectProto.toString;
+  ClickableComponent.prototype.enable = function enable() {
+    this.removeClass('vjs-disabled');
+    this.el_.setAttribute('aria-disabled', 'false');
+    return this;
+  };
 
-/* Native method references for those with the same name as other `lodash` methods. */
-var nativeIsArray = getNative(Array, 'isArray');
-
-/**
- * Checks if `value` is classified as an `Array` object.
- *
- * @static
- * @memberOf _
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
- * @example
- *
- * _.isArray([1, 2, 3]);
- * // => true
- *
- * _.isArray(function() { return arguments; }());
- * // => false
- */
-var isArray = nativeIsArray || function(value) {
-  return isObjectLike(value) && isLength(value.length) && objToString.call(value) == arrayTag;
-};
+  /**
+   * Disable the component element
+   *
+   * @return {Component}
+   * @method disable
+   */
 
-module.exports = isArray;
 
-},{"../internal/getNative":20,"../internal/isLength":25,"../internal/isObjectLike":26}],31:[function(_dereq_,module,exports){
-var isObject = _dereq_('./isObject');
+  ClickableComponent.prototype.disable = function disable() {
+    this.addClass('vjs-disabled');
+    this.el_.setAttribute('aria-disabled', 'true');
+    return this;
+  };
 
-/** `Object#toString` result references. */
-var funcTag = '[object Function]';
+  /**
+   * Handle Click - Override with specific functionality for component
+   *
+   * @method handleClick
+   */
 
-/** Used for native method references. */
-var objectProto = Object.prototype;
 
-/**
- * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
- * of values.
- */
-var objToString = objectProto.toString;
+  ClickableComponent.prototype.handleClick = function handleClick() {};
 
-/**
- * Checks if `value` is classified as a `Function` object.
- *
- * @static
- * @memberOf _
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
- * @example
- *
- * _.isFunction(_);
- * // => true
- *
- * _.isFunction(/abc/);
- * // => false
- */
-function isFunction(value) {
-  // The use of `Object#toString` avoids issues with the `typeof` operator
-  // in older versions of Chrome and Safari which return 'function' for regexes
-  // and Safari 8 which returns 'object' for typed array constructors.
-  return isObject(value) && objToString.call(value) == funcTag;
-}
+  /**
+   * Handle Focus - Add keyboard functionality to element
+   *
+   * @method handleFocus
+   */
 
-module.exports = isFunction;
 
-},{"./isObject":33}],32:[function(_dereq_,module,exports){
-var isFunction = _dereq_('./isFunction'),
-    isHostObject = _dereq_('../internal/isHostObject'),
-    isObjectLike = _dereq_('../internal/isObjectLike');
+  ClickableComponent.prototype.handleFocus = function handleFocus() {
+    Events.on(_document2['default'], 'keydown', Fn.bind(this, this.handleKeyPress));
+  };
 
-/** Used to detect host constructors (Safari > 5). */
-var reIsHostCtor = /^\[object .+?Constructor\]$/;
+  /**
+   * Handle KeyPress (document level) - Trigger click when Space or Enter key is pressed
+   *
+   * @method handleKeyPress
+   */
 
-/** Used for native method references. */
-var objectProto = Object.prototype;
 
-/** Used to resolve the decompiled source of functions. */
-var fnToString = Function.prototype.toString;
+  ClickableComponent.prototype.handleKeyPress = function handleKeyPress(event) {
 
-/** Used to check objects for own properties. */
-var hasOwnProperty = objectProto.hasOwnProperty;
+    // Support Space (32) or Enter (13) key operation to fire a click event
+    if (event.which === 32 || event.which === 13) {
+      event.preventDefault();
+      this.handleClick(event);
+    } else if (_Component.prototype.handleKeyPress) {
 
-/** Used to detect if a method is native. */
-var reIsNative = RegExp('^' +
-  fnToString.call(hasOwnProperty).replace(/[\\^$.*+?()[\]{}|]/g, '\\$&')
-  .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
-);
+      // Pass keypress handling up for unsupported keys
+      _Component.prototype.handleKeyPress.call(this, event);
+    }
+  };
 
-/**
- * Checks if `value` is a native function.
- *
- * @static
- * @memberOf _
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a native function, else `false`.
- * @example
- *
- * _.isNative(Array.prototype.push);
- * // => true
- *
- * _.isNative(_);
- * // => false
- */
-function isNative(value) {
-  if (value == null) {
-    return false;
-  }
-  if (isFunction(value)) {
-    return reIsNative.test(fnToString.call(value));
-  }
-  return isObjectLike(value) && (isHostObject(value) ? reIsNative : reIsHostCtor).test(value);
-}
+  /**
+   * Handle Blur - Remove keyboard triggers
+   *
+   * @method handleBlur
+   */
 
-module.exports = isNative;
 
-},{"../internal/isHostObject":22,"../internal/isObjectLike":26,"./isFunction":31}],33:[function(_dereq_,module,exports){
-/**
- * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`.
- * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
- *
- * @static
- * @memberOf _
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is an object, else `false`.
- * @example
- *
- * _.isObject({});
- * // => true
- *
- * _.isObject([1, 2, 3]);
- * // => true
- *
- * _.isObject(1);
- * // => false
- */
-function isObject(value) {
-  // Avoid a V8 JIT bug in Chrome 19-20.
-  // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
-  var type = typeof value;
-  return !!value && (type == 'object' || type == 'function');
-}
+  ClickableComponent.prototype.handleBlur = function handleBlur() {
+    Events.off(_document2['default'], 'keydown', Fn.bind(this, this.handleKeyPress));
+  };
 
-module.exports = isObject;
+  return ClickableComponent;
+}(_component2['default']);
 
-},{}],34:[function(_dereq_,module,exports){
-var baseForIn = _dereq_('../internal/baseForIn'),
-    isArguments = _dereq_('./isArguments'),
-    isHostObject = _dereq_('../internal/isHostObject'),
-    isObjectLike = _dereq_('../internal/isObjectLike'),
-    support = _dereq_('../support');
+_component2['default'].registerComponent('ClickableComponent', ClickableComponent);
+exports['default'] = ClickableComponent;
 
-/** `Object#toString` result references. */
-var objectTag = '[object Object]';
+},{"136":136,"5":5,"80":80,"81":81,"82":82,"85":85,"92":92}],4:[function(_dereq_,module,exports){
+'use strict';
 
-/** Used for native method references. */
-var objectProto = Object.prototype;
+exports.__esModule = true;
 
-/** Used to check objects for own properties. */
-var hasOwnProperty = objectProto.hasOwnProperty;
+var _button = _dereq_(2);
 
-/**
- * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
- * of values.
- */
-var objToString = objectProto.toString;
+var _button2 = _interopRequireDefault(_button);
 
-/**
- * Checks if `value` is a plain object, that is, an object created by the
- * `Object` constructor or one with a `[[Prototype]]` of `null`.
- *
- * **Note:** This method assumes objects created by the `Object` constructor
- * have no inherited enumerable properties.
- *
- * @static
- * @memberOf _
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
- * @example
- *
- * function Foo() {
- *   this.a = 1;
- * }
- *
- * _.isPlainObject(new Foo);
- * // => false
- *
- * _.isPlainObject([1, 2, 3]);
- * // => false
- *
- * _.isPlainObject({ 'x': 0, 'y': 0 });
- * // => true
- *
- * _.isPlainObject(Object.create(null));
- * // => true
- */
-function isPlainObject(value) {
-  var Ctor;
+var _component = _dereq_(5);
 
-  // Exit early for non `Object` objects.
-  if (!(isObjectLike(value) && objToString.call(value) == objectTag && !isHostObject(value) && !isArguments(value)) ||
-      (!hasOwnProperty.call(value, 'constructor') && (Ctor = value.constructor, typeof Ctor == 'function' && !(Ctor instanceof Ctor)))) {
-    return false;
-  }
-  // IE < 9 iterates inherited properties before own properties. If the first
-  // iterated property is an object's own property then there are no inherited
-  // enumerable properties.
-  var result;
-  if (support.ownLast) {
-    baseForIn(value, function(subValue, key, object) {
-      result = hasOwnProperty.call(object, key);
-      return false;
-    });
-    return result !== false;
-  }
-  // In most environments an object's own properties are iterated before
-  // its inherited properties. If the last iterated property is an object's
-  // own property then there are no inherited enumerable properties.
-  baseForIn(value, function(subValue, key) {
-    result = key;
-  });
-  return result === undefined || hasOwnProperty.call(value, result);
-}
+var _component2 = _interopRequireDefault(_component);
 
-module.exports = isPlainObject;
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
 
-},{"../internal/baseForIn":12,"../internal/isHostObject":22,"../internal/isObjectLike":26,"../support":41,"./isArguments":29}],35:[function(_dereq_,module,exports){
-var isObjectLike = _dereq_('../internal/isObjectLike');
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
 
-/** `Object#toString` result references. */
-var stringTag = '[object String]';
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
 
-/** Used for native method references. */
-var objectProto = Object.prototype;
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
 
 /**
- * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
- * of values.
+ * The `CloseButton` component is a button which fires a "close" event
+ * when it is activated.
+ *
+ * @extends Button
+ * @class CloseButton
  */
-var objToString = objectProto.toString;
+var CloseButton = function (_Button) {
+  _inherits(CloseButton, _Button);
 
-/**
- * Checks if `value` is classified as a `String` primitive or object.
- *
- * @static
- * @memberOf _
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
- * @example
- *
- * _.isString('abc');
- * // => true
- *
- * _.isString(1);
- * // => false
- */
-function isString(value) {
-  return typeof value == 'string' || (isObjectLike(value) && objToString.call(value) == stringTag);
-}
+  function CloseButton(player, options) {
+    _classCallCheck(this, CloseButton);
 
-module.exports = isString;
+    var _this = _possibleConstructorReturn(this, _Button.call(this, player, options));
 
-},{"../internal/isObjectLike":26}],36:[function(_dereq_,module,exports){
-var isLength = _dereq_('../internal/isLength'),
-    isObjectLike = _dereq_('../internal/isObjectLike');
+    _this.controlText(options && options.controlText || _this.localize('Close'));
+    return _this;
+  }
 
-/** `Object#toString` result references. */
-var argsTag = '[object Arguments]',
-    arrayTag = '[object Array]',
-    boolTag = '[object Boolean]',
-    dateTag = '[object Date]',
-    errorTag = '[object Error]',
-    funcTag = '[object Function]',
-    mapTag = '[object Map]',
-    numberTag = '[object Number]',
-    objectTag = '[object Object]',
-    regexpTag = '[object RegExp]',
-    setTag = '[object Set]',
-    stringTag = '[object String]',
-    weakMapTag = '[object WeakMap]';
+  CloseButton.prototype.buildCSSClass = function buildCSSClass() {
+    return 'vjs-close-button ' + _Button.prototype.buildCSSClass.call(this);
+  };
 
-var arrayBufferTag = '[object ArrayBuffer]',
-    float32Tag = '[object Float32Array]',
-    float64Tag = '[object Float64Array]',
-    int8Tag = '[object Int8Array]',
-    int16Tag = '[object Int16Array]',
-    int32Tag = '[object Int32Array]',
-    uint8Tag = '[object Uint8Array]',
-    uint8ClampedTag = '[object Uint8ClampedArray]',
-    uint16Tag = '[object Uint16Array]',
-    uint32Tag = '[object Uint32Array]';
+  CloseButton.prototype.handleClick = function handleClick() {
+    this.trigger({ type: 'close', bubbles: false });
+  };
 
-/** Used to identify `toStringTag` values of typed arrays. */
-var typedArrayTags = {};
-typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
-typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
-typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
-typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
-typedArrayTags[uint32Tag] = true;
-typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
-typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
-typedArrayTags[dateTag] = typedArrayTags[errorTag] =
-typedArrayTags[funcTag] = typedArrayTags[mapTag] =
-typedArrayTags[numberTag] = typedArrayTags[objectTag] =
-typedArrayTags[regexpTag] = typedArrayTags[setTag] =
-typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false;
+  return CloseButton;
+}(_button2['default']);
 
-/** Used for native method references. */
-var objectProto = Object.prototype;
+_component2['default'].registerComponent('CloseButton', CloseButton);
+exports['default'] = CloseButton;
 
-/**
- * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
- * of values.
- */
-var objToString = objectProto.toString;
+},{"2":2,"5":5}],5:[function(_dereq_,module,exports){
+'use strict';
 
-/**
- * Checks if `value` is classified as a typed array.
- *
- * @static
- * @memberOf _
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
- * @example
- *
- * _.isTypedArray(new Uint8Array);
- * // => true
- *
- * _.isTypedArray([]);
- * // => false
- */
-function isTypedArray(value) {
-  return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[objToString.call(value)];
-}
+exports.__esModule = true;
 
-module.exports = isTypedArray;
+var _window = _dereq_(93);
 
-},{"../internal/isLength":25,"../internal/isObjectLike":26}],37:[function(_dereq_,module,exports){
-var baseCopy = _dereq_('../internal/baseCopy'),
-    keysIn = _dereq_('../object/keysIn');
+var _window2 = _interopRequireDefault(_window);
 
-/**
- * Converts `value` to a plain object flattening inherited enumerable
- * properties of `value` to own properties of the plain object.
- *
- * @static
- * @memberOf _
- * @category Lang
- * @param {*} value The value to convert.
- * @returns {Object} Returns the converted plain object.
- * @example
- *
- * function Foo() {
- *   this.b = 2;
- * }
- *
- * Foo.prototype.c = 3;
- *
- * _.assign({ 'a': 1 }, new Foo);
- * // => { 'a': 1, 'b': 2 }
- *
- * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));
- * // => { 'a': 1, 'b': 2, 'c': 3 }
- */
-function toPlainObject(value) {
-  return baseCopy(value, keysIn(value));
-}
+var _dom = _dereq_(80);
 
-module.exports = toPlainObject;
+var Dom = _interopRequireWildcard(_dom);
 
-},{"../internal/baseCopy":10,"../object/keysIn":39}],38:[function(_dereq_,module,exports){
-var getNative = _dereq_('../internal/getNative'),
-    isArrayLike = _dereq_('../internal/isArrayLike'),
-    isObject = _dereq_('../lang/isObject'),
-    shimKeys = _dereq_('../internal/shimKeys'),
-    support = _dereq_('../support');
+var _fn = _dereq_(82);
 
-/* Native method references for those with the same name as other `lodash` methods. */
-var nativeKeys = getNative(Object, 'keys');
+var Fn = _interopRequireWildcard(_fn);
 
-/**
- * Creates an array of the own enumerable property names of `object`.
- *
- * **Note:** Non-object values are coerced to objects. See the
- * [ES spec](http://ecma-international.org/ecma-262/6.0/#sec-object.keys)
- * for more details.
- *
- * @static
- * @memberOf _
- * @category Object
- * @param {Object} object The object to query.
- * @returns {Array} Returns the array of property names.
- * @example
- *
- * function Foo() {
- *   this.a = 1;
- *   this.b = 2;
- * }
- *
- * Foo.prototype.c = 3;
- *
- * _.keys(new Foo);
- * // => ['a', 'b'] (iteration order is not guaranteed)
- *
- * _.keys('hi');
- * // => ['0', '1']
- */
-var keys = !nativeKeys ? shimKeys : function(object) {
-  var Ctor = object == null ? undefined : object.constructor;
-  if ((typeof Ctor == 'function' && Ctor.prototype === object) ||
-      (typeof object == 'function' ? support.enumPrototypes : isArrayLike(object))) {
-    return shimKeys(object);
-  }
-  return isObject(object) ? nativeKeys(object) : [];
-};
+var _guid = _dereq_(84);
 
-module.exports = keys;
+var Guid = _interopRequireWildcard(_guid);
 
-},{"../internal/getNative":20,"../internal/isArrayLike":21,"../internal/shimKeys":27,"../lang/isObject":33,"../support":41}],39:[function(_dereq_,module,exports){
-var arrayEach = _dereq_('../internal/arrayEach'),
-    isArguments = _dereq_('../lang/isArguments'),
-    isArray = _dereq_('../lang/isArray'),
-    isFunction = _dereq_('../lang/isFunction'),
-    isIndex = _dereq_('../internal/isIndex'),
-    isLength = _dereq_('../internal/isLength'),
-    isObject = _dereq_('../lang/isObject'),
-    isString = _dereq_('../lang/isString'),
-    support = _dereq_('../support');
+var _events = _dereq_(81);
 
-/** `Object#toString` result references. */
-var arrayTag = '[object Array]',
-    boolTag = '[object Boolean]',
-    dateTag = '[object Date]',
-    errorTag = '[object Error]',
-    funcTag = '[object Function]',
-    numberTag = '[object Number]',
-    objectTag = '[object Object]',
-    regexpTag = '[object RegExp]',
-    stringTag = '[object String]';
+var Events = _interopRequireWildcard(_events);
 
-/** Used to fix the JScript `[[DontEnum]]` bug. */
-var shadowProps = [
-  'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
-  'toLocaleString', 'toString', 'valueOf'
-];
+var _log = _dereq_(85);
 
-/** Used for native method references. */
-var errorProto = Error.prototype,
-    objectProto = Object.prototype,
-    stringProto = String.prototype;
+var _log2 = _interopRequireDefault(_log);
 
-/** Used to check objects for own properties. */
-var hasOwnProperty = objectProto.hasOwnProperty;
+var _toTitleCase = _dereq_(89);
 
-/**
- * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
- * of values.
- */
-var objToString = objectProto.toString;
+var _toTitleCase2 = _interopRequireDefault(_toTitleCase);
 
-/** Used to avoid iterating over non-enumerable properties in IE < 9. */
-var nonEnumProps = {};
-nonEnumProps[arrayTag] = nonEnumProps[dateTag] = nonEnumProps[numberTag] = { 'constructor': true, 'toLocaleString': true, 'toString': true, 'valueOf': true };
-nonEnumProps[boolTag] = nonEnumProps[stringTag] = { 'constructor': true, 'toString': true, 'valueOf': true };
-nonEnumProps[errorTag] = nonEnumProps[funcTag] = nonEnumProps[regexpTag] = { 'constructor': true, 'toString': true };
-nonEnumProps[objectTag] = { 'constructor': true };
+var _mergeOptions = _dereq_(86);
 
-arrayEach(shadowProps, function(key) {
-  for (var tag in nonEnumProps) {
-    if (hasOwnProperty.call(nonEnumProps, tag)) {
-      var props = nonEnumProps[tag];
-      props[key] = hasOwnProperty.call(props, key);
-    }
-  }
-});
+var _mergeOptions2 = _interopRequireDefault(_mergeOptions);
 
-/**
- * Creates an array of the own and inherited enumerable property names of `object`.
- *
- * **Note:** Non-object values are coerced to objects.
- *
- * @static
- * @memberOf _
- * @category Object
- * @param {Object} object The object to query.
- * @returns {Array} Returns the array of property names.
- * @example
- *
- * function Foo() {
- *   this.a = 1;
- *   this.b = 2;
- * }
- *
- * Foo.prototype.c = 3;
- *
- * _.keysIn(new Foo);
- * // => ['a', 'b', 'c'] (iteration order is not guaranteed)
- */
-function keysIn(object) {
-  if (object == null) {
-    return [];
-  }
-  if (!isObject(object)) {
-    object = Object(object);
-  }
-  var length = object.length;
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
 
-  length = (length && isLength(length) &&
-    (isArray(object) || isArguments(object) || isString(object)) && length) || 0;
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
 
-  var Ctor = object.constructor,
-      index = -1,
-      proto = (isFunction(Ctor) && Ctor.prototype) || objectProto,
-      isProto = proto === object,
-      result = Array(length),
-      skipIndexes = length > 0,
-      skipErrorProps = support.enumErrorProps && (object === errorProto || object instanceof Error),
-      skipProto = support.enumPrototypes && isFunction(object);
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /**
+                                                                                                                                                           * @file component.js
+                                                                                                                                                           *
+                                                                                                                                                           * Player Component - Base class for all UI objects
+                                                                                                                                                           */
 
-  while (++index < length) {
-    result[index] = (index + '');
-  }
-  // lodash skips the `constructor` property when it infers it's iterating
-  // over a `prototype` object because IE < 9 can't set the `[[Enumerable]]`
-  // attribute of an existing property and the `constructor` property of a
-  // prototype defaults to non-enumerable.
-  for (var key in object) {
-    if (!(skipProto && key == 'prototype') &&
-        !(skipErrorProps && (key == 'message' || key == 'name')) &&
-        !(skipIndexes && isIndex(key, length)) &&
-        !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
-      result.push(key);
-    }
-  }
-  if (support.nonEnumShadows && object !== objectProto) {
-    var tag = object === stringProto ? stringTag : (object === errorProto ? errorTag : objToString.call(object)),
-        nonEnums = nonEnumProps[tag] || nonEnumProps[objectTag];
-
-    if (tag == objectTag) {
-      proto = objectProto;
-    }
-    length = shadowProps.length;
-    while (length--) {
-      key = shadowProps[length];
-      var nonEnum = nonEnums[key];
-      if (!(isProto && nonEnum) &&
-          (nonEnum ? hasOwnProperty.call(object, key) : object[key] !== proto[key])) {
-        result.push(key);
-      }
-    }
-  }
-  return result;
-}
-
-module.exports = keysIn;
-
-},{"../internal/arrayEach":9,"../internal/isIndex":23,"../internal/isLength":25,"../lang/isArguments":29,"../lang/isArray":30,"../lang/isFunction":31,"../lang/isObject":33,"../lang/isString":35,"../support":41}],40:[function(_dereq_,module,exports){
-var baseMerge = _dereq_('../internal/baseMerge'),
-    createAssigner = _dereq_('../internal/createAssigner');
 
 /**
- * Recursively merges own enumerable properties of the source object(s), that
- * don't resolve to `undefined` into the destination object. Subsequent sources
- * overwrite property assignments of previous sources. If `customizer` is
- * provided it's invoked to produce the merged values of the destination and
- * source properties. If `customizer` returns `undefined` merging is handled
- * by the method instead. The `customizer` is bound to `thisArg` and invoked
- * with five arguments: (objectValue, sourceValue, key, object, source).
- *
- * @static
- * @memberOf _
- * @category Object
- * @param {Object} object The destination object.
- * @param {...Object} [sources] The source objects.
- * @param {Function} [customizer] The function to customize assigned values.
- * @param {*} [thisArg] The `this` binding of `customizer`.
- * @returns {Object} Returns `object`.
- * @example
- *
- * var users = {
- *   'data': [{ 'user': 'barney' }, { 'user': 'fred' }]
- * };
- *
- * var ages = {
- *   'data': [{ 'age': 36 }, { 'age': 40 }]
- * };
- *
- * _.merge(users, ages);
- * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] }
- *
- * // using a customizer callback
- * var object = {
- *   'fruits': ['apple'],
- *   'vegetables': ['beet']
- * };
- *
- * var other = {
- *   'fruits': ['banana'],
- *   'vegetables': ['carrot']
- * };
+ * Base UI Component class
+ * Components are embeddable UI objects that are represented by both a
+ * javascript object and an element in the DOM. They can be children of other
+ * components, and can have many children themselves.
+ * ```js
+ *     // adding a button to the player
+ *     var button = player.addChild('button');
+ *     button.el(); // -> button element
+ * ```
+ * ```html
+ *     <div class="video-js">
+ *       <div class="vjs-button">Button</div>
+ *     </div>
+ * ```
+ * Components are also event targets.
+ * ```js
+ *     button.on('click', function() {
+ *       console.log('Button Clicked!');
+ *     });
+ *     button.trigger('customevent');
+ * ```
  *
- * _.merge(object, other, function(a, b) {
- *   if (_.isArray(a)) {
- *     return a.concat(b);
- *   }
- * });
- * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] }
+ * @param {Object} player  Main Player
+ * @param {Object=} options Object of option names and values
+ * @param {Function=} ready    Ready callback function
+ * @class Component
  */
-var merge = createAssigner(baseMerge);
+var Component = function () {
+  function Component(player, options, ready) {
+    _classCallCheck(this, Component);
 
-module.exports = merge;
+    // The component might be the player itself and we can't pass `this` to super
+    if (!player && this.play) {
+      this.player_ = player = this; // eslint-disable-line
+    } else {
+      this.player_ = player;
+    }
 
-},{"../internal/baseMerge":13,"../internal/createAssigner":17}],41:[function(_dereq_,module,exports){
-/** Used for native method references. */
-var arrayProto = Array.prototype,
-    errorProto = Error.prototype,
-    objectProto = Object.prototype;
+    // Make a copy of prototype.options_ to protect against overriding defaults
+    this.options_ = (0, _mergeOptions2['default'])({}, this.options_);
 
-/** Native method references. */
-var propertyIsEnumerable = objectProto.propertyIsEnumerable,
-    splice = arrayProto.splice;
+    // Updated options with supplied options
+    options = this.options_ = (0, _mergeOptions2['default'])(this.options_, options);
 
-/**
- * An object environment feature flags.
- *
- * @static
- * @memberOf _
- * @type Object
- */
-var support = {};
+    // Get ID from options or options element if one is supplied
+    this.id_ = options.id || options.el && options.el.id;
 
-(function(x) {
-  var Ctor = function() { this.x = x; },
-      object = { '0': x, 'length': x },
-      props = [];
+    // If there was no ID from the options, generate one
+    if (!this.id_) {
+      // Don't require the player ID function in the case of mock players
+      var id = player && player.id && player.id() || 'no_player';
 
-  Ctor.prototype = { 'valueOf': x, 'y': x };
-  for (var key in new Ctor) { props.push(key); }
+      this.id_ = id + '_component_' + Guid.newGUID();
+    }
 
-  /**
-   * Detect if `name` or `message` properties of `Error.prototype` are
-   * enumerable by default (IE < 9, Safari < 5.1).
-   *
-   * @memberOf _.support
-   * @type boolean
-   */
-  support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') ||
-    propertyIsEnumerable.call(errorProto, 'name');
+    this.name_ = options.name || null;
 
-  /**
-   * Detect if `prototype` properties are enumerable by default.
-   *
-   * Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1
-   * (if the prototype or a property on the prototype has been set)
-   * incorrectly set the `[[Enumerable]]` value of a function's `prototype`
-   * property to `true`.
-   *
-   * @memberOf _.support
-   * @type boolean
-   */
-  support.enumPrototypes = propertyIsEnumerable.call(Ctor, 'prototype');
+    // Create element if one wasn't provided in options
+    if (options.el) {
+      this.el_ = options.el;
+    } else if (options.createEl !== false) {
+      this.el_ = this.createEl();
+    }
 
-  /**
-   * Detect if properties shadowing those on `Object.prototype` are non-enumerable.
-   *
-   * In IE < 9 an object's own properties, shadowing non-enumerable ones,
-   * are made non-enumerable as well (a.k.a the JScript `[[DontEnum]]` bug).
-   *
-   * @memberOf _.support
-   * @type boolean
-   */
-  support.nonEnumShadows = !/valueOf/.test(props);
+    this.children_ = [];
+    this.childIndex_ = {};
+    this.childNameIndex_ = {};
 
-  /**
-   * Detect if own properties are iterated after inherited properties (IE < 9).
-   *
-   * @memberOf _.support
-   * @type boolean
-   */
-  support.ownLast = props[0] != 'x';
+    // Add any child components in options
+    if (options.initChildren !== false) {
+      this.initChildren();
+    }
 
-  /**
-   * Detect if `Array#shift` and `Array#splice` augment array-like objects
-   * correctly.
-   *
-   * Firefox < 10, compatibility modes of IE 8, and IE < 9 have buggy Array
-   * `shift()` and `splice()` functions that fail to remove the last element,
-   * `value[0]`, of array-like objects even though the "length" property is
-   * set to `0`. The `shift()` method is buggy in compatibility modes of IE 8,
-   * while `splice()` is buggy regardless of mode in IE < 9.
-   *
-   * @memberOf _.support
-   * @type boolean
-   */
-  support.spliceObjects = (splice.call(object, 0, 1), !object[0]);
+    this.ready(ready);
+    // Don't want to trigger ready here or it will before init is actually
+    // finished for all children that run this constructor
+
+    if (options.reportTouchActivity !== false) {
+      this.enableTouchActivity();
+    }
+  }
 
   /**
-   * Detect lack of support for accessing string characters by index.
-   *
-   * IE < 8 can't access characters by index. IE 8 can only access characters
-   * by index on string literals, not string objects.
+   * Dispose of the component and all child components
    *
-   * @memberOf _.support
-   * @type boolean
+   * @method dispose
    */
-  support.unindexedChars = ('x'[0] + Object('x')[0]) != 'xx';
-}(1, 0));
 
-module.exports = support;
 
-},{}],42:[function(_dereq_,module,exports){
-/**
- * This method returns the first argument provided to it.
- *
- * @static
- * @memberOf _
- * @category Utility
- * @param {*} value Any value.
- * @returns {*} Returns `value`.
- * @example
- *
- * var object = { 'user': 'fred' };
- *
- * _.identity(object) === object;
- * // => true
- */
-function identity(value) {
-  return value;
-}
+  Component.prototype.dispose = function dispose() {
+    this.trigger({ type: 'dispose', bubbles: false });
 
-module.exports = identity;
+    // Dispose all children.
+    if (this.children_) {
+      for (var i = this.children_.length - 1; i >= 0; i--) {
+        if (this.children_[i].dispose) {
+          this.children_[i].dispose();
+        }
+      }
+    }
 
-},{}],43:[function(_dereq_,module,exports){
-'use strict';
+    // Delete child references
+    this.children_ = null;
+    this.childIndex_ = null;
+    this.childNameIndex_ = null;
 
-var keys = _dereq_('object-keys');
+    // Remove all event listeners.
+    this.off();
 
-module.exports = function hasSymbols() {
-       if (typeof Symbol !== 'function' || typeof Object.getOwnPropertySymbols !== 'function') { return false; }
-       if (typeof Symbol.iterator === 'symbol') { return true; }
+    // Remove element from DOM
+    if (this.el_.parentNode) {
+      this.el_.parentNode.removeChild(this.el_);
+    }
 
-       var obj = {};
-       var sym = Symbol('test');
-       if (typeof sym === 'string') { return false; }
+    Dom.removeElData(this.el_);
+    this.el_ = null;
+  };
 
-       // temp disabled per https://github.com/ljharb/object.assign/issues/17
-       // if (sym instanceof Symbol) { return false; }
-       // temp disabled per https://github.com/WebReflection/get-own-property-symbols/issues/4
-       // if (!(Object(sym) instanceof Symbol)) { return false; }
+  /**
+   * Return the component's player
+   *
+   * @return {Player}
+   * @method player
+   */
 
-       var symVal = 42;
-       obj[sym] = symVal;
-       for (sym in obj) { return false; }
-       if (keys(obj).length !== 0) { return false; }
-       if (typeof Object.keys === 'function' && Object.keys(obj).length !== 0) { return false; }
 
-       if (typeof Object.getOwnPropertyNames === 'function' && Object.getOwnPropertyNames(obj).length !== 0) { return false; }
+  Component.prototype.player = function player() {
+    return this.player_;
+  };
 
-       var syms = Object.getOwnPropertySymbols(obj);
-       if (syms.length !== 1 || syms[0] !== sym) { return false; }
+  /**
+   * Deep merge of options objects
+   * Whenever a property is an object on both options objects
+   * the two properties will be merged using mergeOptions.
+   *
+   * ```js
+   *     Parent.prototype.options_ = {
+   *       optionSet: {
+   *         'childOne': { 'foo': 'bar', 'asdf': 'fdsa' },
+   *         'childTwo': {},
+   *         'childThree': {}
+   *       }
+   *     }
+   *     newOptions = {
+   *       optionSet: {
+   *         'childOne': { 'foo': 'baz', 'abc': '123' }
+   *         'childTwo': null,
+   *         'childFour': {}
+   *       }
+   *     }
+   *
+   *     this.options(newOptions);
+   * ```
+   * RESULT
+   * ```js
+   *     {
+   *       optionSet: {
+   *         'childOne': { 'foo': 'baz', 'asdf': 'fdsa', 'abc': '123' },
+   *         'childTwo': null, // Disabled. Won't be initialized.
+   *         'childThree': {},
+   *         'childFour': {}
+   *       }
+   *     }
+   * ```
+   *
+   * @param  {Object} obj Object of new option values
+   * @return {Object}     A NEW object of this.options_ and obj merged
+   * @method options
+   */
 
-       if (!Object.prototype.propertyIsEnumerable.call(obj, sym)) { return false; }
 
-       if (typeof Object.getOwnPropertyDescriptor === 'function') {
-               var descriptor = Object.getOwnPropertyDescriptor(obj, sym);
-               if (descriptor.value !== symVal || descriptor.enumerable !== true) { return false; }
-       }
+  Component.prototype.options = function options(obj) {
+    _log2['default'].warn('this.options() has been deprecated and will be moved to the constructor in 6.0');
 
-       return true;
-};
+    if (!obj) {
+      return this.options_;
+    }
 
-},{"object-keys":50}],44:[function(_dereq_,module,exports){
-'use strict';
+    this.options_ = (0, _mergeOptions2['default'])(this.options_, obj);
+    return this.options_;
+  };
 
-// modified from https://github.com/es-shims/es6-shim
-var keys = _dereq_('object-keys');
-var bind = _dereq_('function-bind');
-var canBeObject = function (obj) {
-       return typeof obj !== 'undefined' && obj !== null;
-};
-var hasSymbols = _dereq_('./hasSymbols')();
-var toObject = Object;
-var push = bind.call(Function.call, Array.prototype.push);
-var propIsEnumerable = bind.call(Function.call, Object.prototype.propertyIsEnumerable);
+  /**
+   * Get the component's DOM element
+   * ```js
+   *     var domEl = myComponent.el();
+   * ```
+   *
+   * @return {Element}
+   * @method el
+   */
 
-module.exports = function assign(target, source1) {
-       if (!canBeObject(target)) { throw new TypeError('target must be an object'); }
-       var objTarget = toObject(target);
-       var s, source, i, props, syms, value, key;
-       for (s = 1; s < arguments.length; ++s) {
-               source = toObject(arguments[s]);
-               props = keys(source);
-               if (hasSymbols && Object.getOwnPropertySymbols) {
-                       syms = Object.getOwnPropertySymbols(source);
-                       for (i = 0; i < syms.length; ++i) {
-                               key = syms[i];
-                               if (propIsEnumerable(source, key)) {
-                                       push(props, key);
-                               }
-                       }
-               }
-               for (i = 0; i < props.length; ++i) {
-                       key = props[i];
-                       value = source[key];
-                       if (propIsEnumerable(source, key)) {
-                               objTarget[key] = value;
-                       }
-               }
-       }
-       return objTarget;
-};
 
-},{"./hasSymbols":43,"function-bind":49,"object-keys":50}],45:[function(_dereq_,module,exports){
-'use strict';
+  Component.prototype.el = function el() {
+    return this.el_;
+  };
 
-var defineProperties = _dereq_('define-properties');
+  /**
+   * Create the component's DOM element
+   *
+   * @param  {String=} tagName  Element's node type. e.g. 'div'
+   * @param  {Object=} properties An object of properties that should be set
+   * @param  {Object=} attributes An object of attributes that should be set
+   * @return {Element}
+   * @method createEl
+   */
 
-var implementation = _dereq_('./implementation');
-var getPolyfill = _dereq_('./polyfill');
-var shim = _dereq_('./shim');
 
-defineProperties(implementation, {
-       implementation: implementation,
-       getPolyfill: getPolyfill,
-       shim: shim
-});
+  Component.prototype.createEl = function createEl(tagName, properties, attributes) {
+    return Dom.createEl(tagName, properties, attributes);
+  };
 
-module.exports = implementation;
+  Component.prototype.localize = function localize(string) {
+    var code = this.player_.language && this.player_.language();
+    var languages = this.player_.languages && this.player_.languages();
 
-},{"./implementation":44,"./polyfill":52,"./shim":53,"define-properties":46}],46:[function(_dereq_,module,exports){
-'use strict';
+    if (!code || !languages) {
+      return string;
+    }
 
-var keys = _dereq_('object-keys');
-var foreach = _dereq_('foreach');
-var hasSymbols = typeof Symbol === 'function' && typeof Symbol() === 'symbol';
+    var language = languages[code];
 
-var toStr = Object.prototype.toString;
+    if (language && language[string]) {
+      return language[string];
+    }
 
-var isFunction = function (fn) {
-       return typeof fn === 'function' && toStr.call(fn) === '[object Function]';
-};
+    var primaryCode = code.split('-')[0];
+    var primaryLang = languages[primaryCode];
 
-var arePropertyDescriptorsSupported = function () {
-       var obj = {};
-       try {
-               Object.defineProperty(obj, 'x', { enumerable: false, value: obj });
-        /* eslint-disable no-unused-vars, no-restricted-syntax */
-        for (var _ in obj) { return false; }
-        /* eslint-enable no-unused-vars, no-restricted-syntax */
-               return obj.x === obj;
-       } catch (e) { /* this is IE 8. */
-               return false;
-       }
-};
-var supportsDescriptors = Object.defineProperty && arePropertyDescriptorsSupported();
+    if (primaryLang && primaryLang[string]) {
+      return primaryLang[string];
+    }
 
-var defineProperty = function (object, name, value, predicate) {
-       if (name in object && (!isFunction(predicate) || !predicate())) {
-               return;
-       }
-       if (supportsDescriptors) {
-               Object.defineProperty(object, name, {
-                       configurable: true,
-                       enumerable: false,
-                       value: value,
-                       writable: true
-               });
-       } else {
-               object[name] = value;
-       }
-};
+    return string;
+  };
 
-var defineProperties = function (object, map) {
-       var predicates = arguments.length > 2 ? arguments[2] : {};
-       var props = keys(map);
-       if (hasSymbols) {
-               props = props.concat(Object.getOwnPropertySymbols(map));
-       }
-       foreach(props, function (name) {
-               defineProperty(object, name, map[name], predicates[name]);
-       });
-};
+  /**
+   * Return the component's DOM element where children are inserted.
+   * Will either be the same as el() or a new element defined in createEl().
+   *
+   * @return {Element}
+   * @method contentEl
+   */
 
-defineProperties.supportsDescriptors = !!supportsDescriptors;
 
-module.exports = defineProperties;
+  Component.prototype.contentEl = function contentEl() {
+    return this.contentEl_ || this.el_;
+  };
 
-},{"foreach":47,"object-keys":50}],47:[function(_dereq_,module,exports){
+  /**
+   * Get the component's ID
+   * ```js
+   *     var id = myComponent.id();
+   * ```
+   *
+   * @return {String}
+   * @method id
+   */
 
-var hasOwn = Object.prototype.hasOwnProperty;
-var toString = Object.prototype.toString;
 
-module.exports = function forEach (obj, fn, ctx) {
-    if (toString.call(fn) !== '[object Function]') {
-        throw new TypeError('iterator must be a function');
-    }
-    var l = obj.length;
-    if (l === +l) {
-        for (var i = 0; i < l; i++) {
-            fn.call(ctx, obj[i], i, obj);
-        }
-    } else {
-        for (var k in obj) {
-            if (hasOwn.call(obj, k)) {
-                fn.call(ctx, obj[k], k, obj);
-            }
-        }
-    }
-};
+  Component.prototype.id = function id() {
+    return this.id_;
+  };
 
+  /**
+   * Get the component's name. The name is often used to reference the component.
+   * ```js
+   *     var name = myComponent.name();
+   * ```
+   *
+   * @return {String}
+   * @method name
+   */
 
-},{}],48:[function(_dereq_,module,exports){
-var ERROR_MESSAGE = 'Function.prototype.bind called on incompatible ';
-var slice = Array.prototype.slice;
-var toStr = Object.prototype.toString;
-var funcType = '[object Function]';
 
-module.exports = function bind(that) {
-    var target = this;
-    if (typeof target !== 'function' || toStr.call(target) !== funcType) {
-        throw new TypeError(ERROR_MESSAGE + target);
-    }
-    var args = slice.call(arguments, 1);
+  Component.prototype.name = function name() {
+    return this.name_;
+  };
 
-    var bound;
-    var binder = function () {
-        if (this instanceof bound) {
-            var result = target.apply(
-                this,
-                args.concat(slice.call(arguments))
-            );
-            if (Object(result) === result) {
-                return result;
-            }
-            return this;
-        } else {
-            return target.apply(
-                that,
-                args.concat(slice.call(arguments))
-            );
-        }
-    };
+  /**
+   * Get an array of all child components
+   * ```js
+   *     var kids = myComponent.children();
+   * ```
+   *
+   * @return {Array} The children
+   * @method children
+   */
 
-    var boundLength = Math.max(0, target.length - args.length);
-    var boundArgs = [];
-    for (var i = 0; i < boundLength; i++) {
-        boundArgs.push('$' + i);
-    }
 
-    bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this,arguments); }')(binder);
+  Component.prototype.children = function children() {
+    return this.children_;
+  };
 
-    if (target.prototype) {
-        var Empty = function Empty() {};
-        Empty.prototype = target.prototype;
-        bound.prototype = new Empty();
-        Empty.prototype = null;
-    }
+  /**
+   * Returns a child component with the provided ID
+   *
+   * @return {Component}
+   * @method getChildById
+   */
 
-    return bound;
-};
 
-},{}],49:[function(_dereq_,module,exports){
-var implementation = _dereq_('./implementation');
+  Component.prototype.getChildById = function getChildById(id) {
+    return this.childIndex_[id];
+  };
 
-module.exports = Function.prototype.bind || implementation;
+  /**
+   * Returns a child component with the provided name
+   *
+   * @return {Component}
+   * @method getChild
+   */
 
-},{"./implementation":48}],50:[function(_dereq_,module,exports){
-'use strict';
 
-// modified from https://github.com/es-shims/es5-shim
-var has = Object.prototype.hasOwnProperty;
-var toStr = Object.prototype.toString;
-var slice = Array.prototype.slice;
-var isArgs = _dereq_('./isArguments');
-var isEnumerable = Object.prototype.propertyIsEnumerable;
-var hasDontEnumBug = !isEnumerable.call({ toString: null }, 'toString');
-var hasProtoEnumBug = isEnumerable.call(function () {}, 'prototype');
-var dontEnums = [
-       'toString',
-       'toLocaleString',
-       'valueOf',
-       'hasOwnProperty',
-       'isPrototypeOf',
-       'propertyIsEnumerable',
-       'constructor'
-];
-var equalsConstructorPrototype = function (o) {
-       var ctor = o.constructor;
-       return ctor && ctor.prototype === o;
-};
-var excludedKeys = {
-       $console: true,
-       $external: true,
-       $frame: true,
-       $frameElement: true,
-       $frames: true,
-       $innerHeight: true,
-       $innerWidth: true,
-       $outerHeight: true,
-       $outerWidth: true,
-       $pageXOffset: true,
-       $pageYOffset: true,
-       $parent: true,
-       $scrollLeft: true,
-       $scrollTop: true,
-       $scrollX: true,
-       $scrollY: true,
-       $self: true,
-       $webkitIndexedDB: true,
-       $webkitStorageInfo: true,
-       $window: true
-};
-var hasAutomationEqualityBug = (function () {
-       /* global window */
-       if (typeof window === 'undefined') { return false; }
-       for (var k in window) {
-               try {
-                       if (!excludedKeys['$' + k] && has.call(window, k) && window[k] !== null && typeof window[k] === 'object') {
-                               try {
-                                       equalsConstructorPrototype(window[k]);
-                               } catch (e) {
-                                       return true;
-                               }
-                       }
-               } catch (e) {
-                       return true;
-               }
-       }
-       return false;
-}());
-var equalsConstructorPrototypeIfNotBuggy = function (o) {
-       /* global window */
-       if (typeof window === 'undefined' || !hasAutomationEqualityBug) {
-               return equalsConstructorPrototype(o);
-       }
-       try {
-               return equalsConstructorPrototype(o);
-       } catch (e) {
-               return false;
-       }
-};
+  Component.prototype.getChild = function getChild(name) {
+    return this.childNameIndex_[name];
+  };
 
-var keysShim = function keys(object) {
-       var isObject = object !== null && typeof object === 'object';
-       var isFunction = toStr.call(object) === '[object Function]';
-       var isArguments = isArgs(object);
-       var isString = isObject && toStr.call(object) === '[object String]';
-       var theKeys = [];
+  /**
+   * Adds a child component inside this component
+   * ```js
+   *     myComponent.el();
+   *     // -> <div class='my-component'></div>
+   *     myComponent.children();
+   *     // [empty array]
+   *
+   *     var myButton = myComponent.addChild('MyButton');
+   *     // -> <div class='my-component'><div class="my-button">myButton<div></div>
+   *     // -> myButton === myComponent.children()[0];
+   * ```
+   * Pass in options for child constructors and options for children of the child
+   * ```js
+   *     var myButton = myComponent.addChild('MyButton', {
+   *       text: 'Press Me',
+   *       buttonChildExample: {
+   *         buttonChildOption: true
+   *       }
+   *     });
+   * ```
+   *
+   * @param {String|Component} child The class name or instance of a child to add
+   * @param {Object=} options Options, including options to be passed to children of the child.
+   * @param {Number} index into our children array to attempt to add the child
+   * @return {Component} The child component (created by this process if a string was used)
+   * @method addChild
+   */
 
-       if (!isObject && !isFunction && !isArguments) {
-               throw new TypeError('Object.keys called on a non-object');
-       }
 
-       var skipProto = hasProtoEnumBug && isFunction;
-       if (isString && object.length > 0 && !has.call(object, 0)) {
-               for (var i = 0; i < object.length; ++i) {
-                       theKeys.push(String(i));
-               }
-       }
+  Component.prototype.addChild = function addChild(child) {
+    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+    var index = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.children_.length;
 
-       if (isArguments && object.length > 0) {
-               for (var j = 0; j < object.length; ++j) {
-                       theKeys.push(String(j));
-               }
-       } else {
-               for (var name in object) {
-                       if (!(skipProto && name === 'prototype') && has.call(object, name)) {
-                               theKeys.push(String(name));
-                       }
-               }
-       }
+    var component = void 0;
+    var componentName = void 0;
 
-       if (hasDontEnumBug) {
-               var skipConstructor = equalsConstructorPrototypeIfNotBuggy(object);
+    // If child is a string, create nt with options
+    if (typeof child === 'string') {
+      componentName = child;
 
-               for (var k = 0; k < dontEnums.length; ++k) {
-                       if (!(skipConstructor && dontEnums[k] === 'constructor') && has.call(object, dontEnums[k])) {
-                               theKeys.push(dontEnums[k]);
-                       }
-               }
-       }
-       return theKeys;
-};
+      // Options can also be specified as a boolean, so convert to an empty object if false.
+      if (!options) {
+        options = {};
+      }
 
-keysShim.shim = function shimObjectKeys() {
-       if (Object.keys) {
-               var keysWorksWithArguments = (function () {
-                       // Safari 5.0 bug
-                       return (Object.keys(arguments) || '').length === 2;
-               }(1, 2));
-               if (!keysWorksWithArguments) {
-                       var originalKeys = Object.keys;
-                       Object.keys = function keys(object) {
-                               if (isArgs(object)) {
-                                       return originalKeys(slice.call(object));
-                               } else {
-                                       return originalKeys(object);
-                               }
-                       };
-               }
-       } else {
-               Object.keys = keysShim;
-       }
-       return Object.keys || keysShim;
-};
+      // Same as above, but true is deprecated so show a warning.
+      if (options === true) {
+        _log2['default'].warn('Initializing a child component with `true` is deprecated. Children should be defined in an array when possible, but if necessary use an object instead of `true`.');
+        options = {};
+      }
 
-module.exports = keysShim;
+      // If no componentClass in options, assume componentClass is the name lowercased
+      // (e.g. playButton)
+      var componentClassName = options.componentClass || (0, _toTitleCase2['default'])(componentName);
 
-},{"./isArguments":51}],51:[function(_dereq_,module,exports){
-'use strict';
+      // Set name through options
+      options.name = componentName;
 
-var toStr = Object.prototype.toString;
+      // Create a new object & element for this controls set
+      // If there's no .player_, this is a player
+      var ComponentClass = Component.getComponent(componentClassName);
 
-module.exports = function isArguments(value) {
-       var str = toStr.call(value);
-       var isArgs = str === '[object Arguments]';
-       if (!isArgs) {
-               isArgs = str !== '[object Array]' &&
-                       value !== null &&
-                       typeof value === 'object' &&
-                       typeof value.length === 'number' &&
-                       value.length >= 0 &&
-                       toStr.call(value.callee) === '[object Function]';
-       }
-       return isArgs;
-};
-
-},{}],52:[function(_dereq_,module,exports){
-'use strict';
-
-var implementation = _dereq_('./implementation');
+      if (!ComponentClass) {
+        throw new Error('Component ' + componentClassName + ' does not exist');
+      }
 
-var lacksProperEnumerationOrder = function () {
-       if (!Object.assign) {
-               return false;
-       }
-       // v8, specifically in node 4.x, has a bug with incorrect property enumeration order
-       // note: this does not detect the bug unless there's 20 characters
-       var str = 'abcdefghijklmnopqrst';
-       var letters = str.split('');
-       var map = {};
-       for (var i = 0; i < letters.length; ++i) {
-               map[letters[i]] = letters[i];
-       }
-       var obj = Object.assign({}, map);
-       var actual = '';
-       for (var k in obj) {
-               actual += k;
-       }
-       return str !== actual;
-};
+      // data stored directly on the videojs object may be
+      // misidentified as a component to retain
+      // backwards-compatibility with 4.x. check to make sure the
+      // component class can be instantiated.
+      if (typeof ComponentClass !== 'function') {
+        return null;
+      }
 
-var assignHasPendingExceptions = function () {
-       if (!Object.assign || !Object.preventExtensions) {
-               return false;
-       }
-       // Firefox 37 still has "pending exception" logic in its Object.assign implementation,
-       // which is 72% slower than our shim, and Firefox 40's native implementation.
-       var thrower = Object.preventExtensions({ 1: 2 });
-       try {
-               Object.assign(thrower, 'xy');
-       } catch (e) {
-               return thrower[1] === 'y';
-       }
-};
+      component = new ComponentClass(this.player_ || this, options);
 
-module.exports = function getPolyfill() {
-       if (!Object.assign) {
-               return implementation;
-       }
-       if (lacksProperEnumerationOrder()) {
-               return implementation;
-       }
-       if (assignHasPendingExceptions()) {
-               return implementation;
-       }
-       return Object.assign;
-};
+      // child is a component instance
+    } else {
+      component = child;
+    }
 
-},{"./implementation":44}],53:[function(_dereq_,module,exports){
-'use strict';
+    this.children_.splice(index, 0, component);
 
-var define = _dereq_('define-properties');
-var getPolyfill = _dereq_('./polyfill');
+    if (typeof component.id === 'function') {
+      this.childIndex_[component.id()] = component;
+    }
 
-module.exports = function shimAssign() {
-       var polyfill = getPolyfill();
-       define(
-               Object,
-               { assign: polyfill },
-               { assign: function () { return Object.assign !== polyfill; } }
-       );
-       return polyfill;
-};
+    // If a name wasn't used to create the component, check if we can use the
+    // name function of the component
+    componentName = componentName || component.name && component.name();
 
-},{"./polyfill":52,"define-properties":46}],54:[function(_dereq_,module,exports){
-module.exports = SafeParseTuple
+    if (componentName) {
+      this.childNameIndex_[componentName] = component;
+    }
 
-function SafeParseTuple(obj, reviver) {
-    var json
-    var error = null
+    // Add the UI object's element to the container div (box)
+    // Having an element is not required
+    if (typeof component.el === 'function' && component.el()) {
+      var childNodes = this.contentEl().children;
+      var refNode = childNodes[index] || null;
 
-    try {
-        json = JSON.parse(obj, reviver)
-    } catch (err) {
-        error = err
+      this.contentEl().insertBefore(component.el(), refNode);
     }
 
-    return [error, json]
-}
-
-},{}],55:[function(_dereq_,module,exports){
-function clean (s) {
-  return s.replace(/\n\r?\s*/g, '')
-}
+    // Return so it can stored on parent object if desired.
+    return component;
+  };
 
+  /**
+   * Remove a child component from this component's list of children, and the
+   * child component's element from this component's element
+   *
+   * @param  {Component} component Component to remove
+   * @method removeChild
+   */
 
-module.exports = function tsml (sa) {
-  var s = ''
-    , i = 0
 
-  for (; i < arguments.length; i++)
-    s += clean(sa[i]) + (arguments[i + 1] || '')
+  Component.prototype.removeChild = function removeChild(component) {
+    if (typeof component === 'string') {
+      component = this.getChild(component);
+    }
 
-  return s
-}
-},{}],56:[function(_dereq_,module,exports){
-"use strict";
-var window = _dereq_("global/window")
-var once = _dereq_("once")
-var isFunction = _dereq_("is-function")
-var parseHeaders = _dereq_("parse-headers")
-var xtend = _dereq_("xtend")
+    if (!component || !this.children_) {
+      return;
+    }
 
-module.exports = createXHR
-createXHR.XMLHttpRequest = window.XMLHttpRequest || noop
-createXHR.XDomainRequest = "withCredentials" in (new createXHR.XMLHttpRequest()) ? createXHR.XMLHttpRequest : window.XDomainRequest
+    var childFound = false;
 
-forEachArray(["get", "put", "post", "patch", "head", "delete"], function(method) {
-    createXHR[method === "delete" ? "del" : method] = function(uri, options, callback) {
-        options = initParams(uri, options, callback)
-        options.method = method.toUpperCase()
-        return _createXHR(options)
+    for (var i = this.children_.length - 1; i >= 0; i--) {
+      if (this.children_[i] === component) {
+        childFound = true;
+        this.children_.splice(i, 1);
+        break;
+      }
     }
-})
 
-function forEachArray(array, iterator) {
-    for (var i = 0; i < array.length; i++) {
-        iterator(array[i])
+    if (!childFound) {
+      return;
     }
-}
 
-function isEmpty(obj){
-    for(var i in obj){
-        if(obj.hasOwnProperty(i)) return false
-    }
-    return true
-}
+    this.childIndex_[component.id()] = null;
+    this.childNameIndex_[component.name()] = null;
 
-function initParams(uri, options, callback) {
-    var params = uri
+    var compEl = component.el();
 
-    if (isFunction(options)) {
-        callback = options
-        if (typeof uri === "string") {
-            params = {uri:uri}
-        }
-    } else {
-        params = xtend(options, {uri: uri})
+    if (compEl && compEl.parentNode === this.contentEl()) {
+      this.contentEl().removeChild(component.el());
     }
+  };
 
-    params.callback = callback
-    return params
-}
+  /**
+   * Add and initialize default child components from options
+   * ```js
+   *     // when an instance of MyComponent is created, all children in options
+   *     // will be added to the instance by their name strings and options
+   *     MyComponent.prototype.options_ = {
+   *       children: [
+   *         'myChildComponent'
+   *       ],
+   *       myChildComponent: {
+   *         myChildOption: true
+   *       }
+   *     };
+   *
+   *     // Or when creating the component
+   *     var myComp = new MyComponent(player, {
+   *       children: [
+   *         'myChildComponent'
+   *       ],
+   *       myChildComponent: {
+   *         myChildOption: true
+   *       }
+   *     });
+   * ```
+   * The children option can also be an array of
+   * child options objects (that also include a 'name' key).
+   * This can be used if you have two child components of the
+   * same type that need different options.
+   * ```js
+   *     var myComp = new MyComponent(player, {
+   *       children: [
+   *         'button',
+   *         {
+   *           name: 'button',
+   *           someOtherOption: true
+   *         },
+   *         {
+   *           name: 'button',
+   *           someOtherOption: false
+   *         }
+   *       ]
+   *     });
+   * ```
+   *
+   * @method initChildren
+   */
 
-function createXHR(uri, options, callback) {
-    options = initParams(uri, options, callback)
-    return _createXHR(options)
-}
 
-function _createXHR(options) {
-    var callback = options.callback
-    if(typeof callback === "undefined"){
-        throw new Error("callback argument missing")
-    }
-    callback = once(callback)
+  Component.prototype.initChildren = function initChildren() {
+    var _this = this;
 
-    function readystatechange() {
-        if (xhr.readyState === 4) {
-            loadFunc()
-        }
-    }
+    var children = this.options_.children;
 
-    function getBody() {
-        // Chrome with requestType=blob throws errors arround when even testing access to responseText
-        var body = undefined
+    if (children) {
+      (function () {
+        // `this` is `parent`
+        var parentOptions = _this.options_;
 
-        if (xhr.response) {
-            body = xhr.response
-        } else if (xhr.responseType === "text" || !xhr.responseType) {
-            body = xhr.responseText || xhr.responseXML
-        }
+        var handleAdd = function handleAdd(child) {
+          var name = child.name;
+          var opts = child.opts;
 
-        if (isJson) {
-            try {
-                body = JSON.parse(body)
-            } catch (e) {}
-        }
+          // Allow options for children to be set at the parent options
+          // e.g. videojs(id, { controlBar: false });
+          // instead of videojs(id, { children: { controlBar: false });
+          if (parentOptions[name] !== undefined) {
+            opts = parentOptions[name];
+          }
 
-        return body
-    }
+          // Allow for disabling default components
+          // e.g. options['children']['posterImage'] = false
+          if (opts === false) {
+            return;
+          }
 
-    var failureResponse = {
-                body: undefined,
-                headers: {},
-                statusCode: 0,
-                method: method,
-                url: uri,
-                rawRequest: xhr
-            }
+          // Allow options to be passed as a simple boolean if no configuration
+          // is necessary.
+          if (opts === true) {
+            opts = {};
+          }
 
-    function errorFunc(evt) {
-        clearTimeout(timeoutTimer)
-        if(!(evt instanceof Error)){
-            evt = new Error("" + (evt || "Unknown XMLHttpRequest Error") )
-        }
-        evt.statusCode = 0
-        callback(evt, failureResponse)
-    }
+          // We also want to pass the original player options to each component as well so they don't need to
+          // reach back into the player for options later.
+          opts.playerOptions = _this.options_.playerOptions;
 
-    // will load the data & process the response in a special response object
-    function loadFunc() {
-        if (aborted) return
-        var status
-        clearTimeout(timeoutTimer)
-        if(options.useXDR && xhr.status===undefined) {
-            //IE8 CORS GET successful response doesn't have a status field, but body is fine
-            status = 200
+          // Create and add the child component.
+          // Add a direct reference to the child by name on the parent instance.
+          // If two of the same component are used, different names should be supplied
+          // for each
+          var newChild = _this.addChild(name, opts);
+
+          if (newChild) {
+            _this[name] = newChild;
+          }
+        };
+
+        // Allow for an array of children details to passed in the options
+        var workingChildren = void 0;
+        var Tech = Component.getComponent('Tech');
+
+        if (Array.isArray(children)) {
+          workingChildren = children;
         } else {
-            status = (xhr.status === 1223 ? 204 : xhr.status)
+          workingChildren = Object.keys(children);
         }
-        var response = failureResponse
-        var err = null
 
-        if (status !== 0){
-            response = {
-                body: getBody(),
-                statusCode: status,
-                method: method,
-                headers: {},
-                url: uri,
-                rawRequest: xhr
-            }
-            if(xhr.getAllResponseHeaders){ //remember xhr can in fact be XDR for CORS in IE
-                response.headers = parseHeaders(xhr.getAllResponseHeaders())
+        workingChildren
+        // children that are in this.options_ but also in workingChildren  would
+        // give us extra children we do not want. So, we want to filter them out.
+        .concat(Object.keys(_this.options_).filter(function (child) {
+          return !workingChildren.some(function (wchild) {
+            if (typeof wchild === 'string') {
+              return child === wchild;
             }
-        } else {
-            err = new Error("Internal XMLHttpRequest Error")
-        }
-        callback(err, response, response.body)
+            return child === wchild.name;
+          });
+        })).map(function (child) {
+          var name = void 0;
+          var opts = void 0;
 
-    }
+          if (typeof child === 'string') {
+            name = child;
+            opts = children[name] || _this.options_[name] || {};
+          } else {
+            name = child.name;
+            opts = child;
+          }
 
-    var xhr = options.xhr || null
+          return { name: name, opts: opts };
+        }).filter(function (child) {
+          // we have to make sure that child.name isn't in the techOrder since
+          // techs are registerd as Components but can't aren't compatible
+          // See https://github.com/videojs/video.js/issues/2772
+          var c = Component.getComponent(child.opts.componentClass || (0, _toTitleCase2['default'])(child.name));
 
-    if (!xhr) {
-        if (options.cors || options.useXDR) {
-            xhr = new createXHR.XDomainRequest()
-        }else{
-            xhr = new createXHR.XMLHttpRequest()
-        }
+          return c && !Tech.isTech(c);
+        }).forEach(handleAdd);
+      })();
     }
+  };
 
-    var key
-    var aborted
-    var uri = xhr.url = options.uri || options.url
-    var method = xhr.method = options.method || "GET"
-    var body = options.body || options.data || null
-    var headers = xhr.headers = options.headers || {}
-    var sync = !!options.sync
-    var isJson = false
-    var timeoutTimer
+  /**
+   * Allows sub components to stack CSS class names
+   *
+   * @return {String} The constructed class name
+   * @method buildCSSClass
+   */
 
-    if ("json" in options) {
-        isJson = true
-        headers["accept"] || headers["Accept"] || (headers["Accept"] = "application/json") //Don't override existing accept header declared by user
-        if (method !== "GET" && method !== "HEAD") {
-            headers["content-type"] || headers["Content-Type"] || (headers["Content-Type"] = "application/json") //Don't override existing accept header declared by user
-            body = JSON.stringify(options.json)
-        }
-    }
 
-    xhr.onreadystatechange = readystatechange
-    xhr.onload = loadFunc
-    xhr.onerror = errorFunc
-    // IE9 must have onprogress be set to a unique function.
-    xhr.onprogress = function () {
-        // IE must die
-    }
-    xhr.ontimeout = errorFunc
-    xhr.open(method, uri, !sync, options.username, options.password)
-    //has to be after open
-    if(!sync) {
-        xhr.withCredentials = !!options.withCredentials
-    }
-    // Cannot set timeout with sync request
-    // not setting timeout on the xhr object, because of old webkits etc. not handling that correctly
-    // both npm's request and jquery 1.x use this kind of timeout, so this is being consistent
-    if (!sync && options.timeout > 0 ) {
-        timeoutTimer = setTimeout(function(){
-            aborted=true//IE9 may still call readystatechange
-            xhr.abort("timeout")
-            var e = new Error("XMLHttpRequest timeout")
-            e.code = "ETIMEDOUT"
-            errorFunc(e)
-        }, options.timeout )
-    }
+  Component.prototype.buildCSSClass = function buildCSSClass() {
+    // Child classes can include a function that does:
+    // return 'CLASS NAME' + this._super();
+    return '';
+  };
 
-    if (xhr.setRequestHeader) {
-        for(key in headers){
-            if(headers.hasOwnProperty(key)){
-                xhr.setRequestHeader(key, headers[key])
-            }
-        }
-    } else if (options.headers && !isEmpty(options.headers)) {
-        throw new Error("Headers cannot be set on an XDomainRequest object")
-    }
+  /**
+   * Add an event listener to this component's element
+   * ```js
+   *     var myFunc = function() {
+   *       var myComponent = this;
+   *       // Do something when the event is fired
+   *     };
+   *
+   *     myComponent.on('eventType', myFunc);
+   * ```
+   * The context of myFunc will be myComponent unless previously bound.
+   * Alternatively, you can add a listener to another element or component.
+   * ```js
+   *     myComponent.on(otherElement, 'eventName', myFunc);
+   *     myComponent.on(otherComponent, 'eventName', myFunc);
+   * ```
+   * The benefit of using this over `VjsEvents.on(otherElement, 'eventName', myFunc)`
+   * and `otherComponent.on('eventName', myFunc)` is that this way the listeners
+   * will be automatically cleaned up when either component is disposed.
+   * It will also bind myComponent as the context of myFunc.
+   * **NOTE**: When using this on elements in the page other than window
+   * and document (both permanent), if you remove the element from the DOM
+   * you need to call `myComponent.trigger(el, 'dispose')` on it to clean up
+   * references to it and allow the browser to garbage collect it.
+   *
+   * @param  {String|Component} first   The event type or other component
+   * @param  {Function|String}      second  The event handler or event type
+   * @param  {Function}             third   The event handler
+   * @return {Component}
+   * @method on
+   */
 
-    if ("responseType" in options) {
-        xhr.responseType = options.responseType
-    }
 
-    if ("beforeSend" in options &&
-        typeof options.beforeSend === "function"
-    ) {
-        options.beforeSend(xhr)
-    }
+  Component.prototype.on = function on(first, second, third) {
+    var _this2 = this;
 
-    xhr.send(body)
+    if (typeof first === 'string' || Array.isArray(first)) {
+      Events.on(this.el_, first, Fn.bind(this, second));
 
-    return xhr
+      // Targeting another component or element
+    } else {
+      (function () {
+        var target = first;
+        var type = second;
+        var fn = Fn.bind(_this2, third);
 
+        // When this component is disposed, remove the listener from the other component
+        var removeOnDispose = function removeOnDispose() {
+          return _this2.off(target, type, fn);
+        };
 
-}
+        // Use the same function ID so we can remove it later it using the ID
+        // of the original listener
+        removeOnDispose.guid = fn.guid;
+        _this2.on('dispose', removeOnDispose);
 
-function noop() {}
+        // If the other component is disposed first we need to clean the reference
+        // to the other component in this component's removeOnDispose listener
+        // Otherwise we create a memory leak.
+        var cleanRemover = function cleanRemover() {
+          return _this2.off('dispose', removeOnDispose);
+        };
 
-},{"global/window":2,"is-function":57,"once":58,"parse-headers":61,"xtend":62}],57:[function(_dereq_,module,exports){
-module.exports = isFunction
+        // Add the same function ID so we can easily remove it later
+        cleanRemover.guid = fn.guid;
+
+        // Check if this is a DOM node
+        if (first.nodeName) {
+          // Add the listener to the other element
+          Events.on(target, type, fn);
+          Events.on(target, 'dispose', cleanRemover);
+
+          // Should be a component
+          // Not using `instanceof Component` because it makes mock players difficult
+        } else if (typeof first.on === 'function') {
+          // Add the listener to the other component
+          target.on(type, fn);
+          target.on('dispose', cleanRemover);
+        }
+      })();
+    }
 
-var toString = Object.prototype.toString
+    return this;
+  };
 
-function isFunction (fn) {
-  var string = toString.call(fn)
-  return string === '[object Function]' ||
-    (typeof fn === 'function' && string !== '[object RegExp]') ||
-    (typeof window !== 'undefined' &&
-     // IE8 and below
-     (fn === window.setTimeout ||
-      fn === window.alert ||
-      fn === window.confirm ||
-      fn === window.prompt))
-};
-
-},{}],58:[function(_dereq_,module,exports){
-module.exports = once
-
-once.proto = once(function () {
-  Object.defineProperty(Function.prototype, 'once', {
-    value: function () {
-      return once(this)
-    },
-    configurable: true
-  })
-})
-
-function once (fn) {
-  var called = false
-  return function () {
-    if (called) return
-    called = true
-    return fn.apply(this, arguments)
-  }
-}
-
-},{}],59:[function(_dereq_,module,exports){
-var isFunction = _dereq_('is-function')
-
-module.exports = forEach
+  /**
+   * Remove an event listener from this component's element
+   * ```js
+   *     myComponent.off('eventType', myFunc);
+   * ```
+   * If myFunc is excluded, ALL listeners for the event type will be removed.
+   * If eventType is excluded, ALL listeners will be removed from the component.
+   * Alternatively you can use `off` to remove listeners that were added to other
+   * elements or components using `myComponent.on(otherComponent...`.
+   * In this case both the event type and listener function are REQUIRED.
+   * ```js
+   *     myComponent.off(otherElement, 'eventType', myFunc);
+   *     myComponent.off(otherComponent, 'eventType', myFunc);
+   * ```
+   *
+   * @param  {String=|Component}  first  The event type or other component
+   * @param  {Function=|String}       second The listener function or event type
+   * @param  {Function=}              third  The listener for other component
+   * @return {Component}
+   * @method off
+   */
 
-var toString = Object.prototype.toString
-var hasOwnProperty = Object.prototype.hasOwnProperty
 
-function forEach(list, iterator, context) {
-    if (!isFunction(iterator)) {
-        throw new TypeError('iterator must be a function')
-    }
+  Component.prototype.off = function off(first, second, third) {
+    if (!first || typeof first === 'string' || Array.isArray(first)) {
+      Events.off(this.el_, first, second);
+    } else {
+      var target = first;
+      var type = second;
+      // Ensure there's at least a guid, even if the function hasn't been used
+      var fn = Fn.bind(this, third);
 
-    if (arguments.length < 3) {
-        context = this
-    }
-    
-    if (toString.call(list) === '[object Array]')
-        forEachArray(list, iterator, context)
-    else if (typeof list === 'string')
-        forEachString(list, iterator, context)
-    else
-        forEachObject(list, iterator, context)
-}
+      // Remove the dispose listener on this component,
+      // which was given the same guid as the event listener
+      this.off('dispose', fn);
 
-function forEachArray(array, iterator, context) {
-    for (var i = 0, len = array.length; i < len; i++) {
-        if (hasOwnProperty.call(array, i)) {
-            iterator.call(context, array[i], i, array)
-        }
+      if (first.nodeName) {
+        // Remove the listener
+        Events.off(target, type, fn);
+        // Remove the listener for cleaning the dispose listener
+        Events.off(target, 'dispose', fn);
+      } else {
+        target.off(type, fn);
+        target.off('dispose', fn);
+      }
     }
-}
 
-function forEachString(string, iterator, context) {
-    for (var i = 0, len = string.length; i < len; i++) {
-        // no such thing as a sparse string.
-        iterator.call(context, string.charAt(i), i, string)
-    }
-}
+    return this;
+  };
 
-function forEachObject(object, iterator, context) {
-    for (var k in object) {
-        if (hasOwnProperty.call(object, k)) {
-            iterator.call(context, object[k], k, object)
-        }
-    }
-}
+  /**
+   * Add an event listener to be triggered only once and then removed
+   * ```js
+   *     myComponent.one('eventName', myFunc);
+   * ```
+   * Alternatively you can add a listener to another element or component
+   * that will be triggered only once.
+   * ```js
+   *     myComponent.one(otherElement, 'eventName', myFunc);
+   *     myComponent.one(otherComponent, 'eventName', myFunc);
+   * ```
+   *
+   * @param  {String|Component}  first   The event type or other component
+   * @param  {Function|String}       second  The listener function or event type
+   * @param  {Function=}             third   The listener function for other component
+   * @return {Component}
+   * @method one
+   */
 
-},{"is-function":57}],60:[function(_dereq_,module,exports){
 
-exports = module.exports = trim;
+  Component.prototype.one = function one(first, second, third) {
+    var _this3 = this,
+        _arguments = arguments;
 
-function trim(str){
-  return str.replace(/^\s*|\s*$/g, '');
-}
+    if (typeof first === 'string' || Array.isArray(first)) {
+      Events.one(this.el_, first, Fn.bind(this, second));
+    } else {
+      (function () {
+        var target = first;
+        var type = second;
+        var fn = Fn.bind(_this3, third);
 
-exports.left = function(str){
-  return str.replace(/^\s*/, '');
-};
+        var newFunc = function newFunc() {
+          _this3.off(target, type, newFunc);
+          fn.apply(null, _arguments);
+        };
 
-exports.right = function(str){
-  return str.replace(/\s*$/, '');
-};
+        // Keep the same function ID so we can remove it later
+        newFunc.guid = fn.guid;
 
-},{}],61:[function(_dereq_,module,exports){
-var trim = _dereq_('trim')
-  , forEach = _dereq_('for-each')
-  , isArray = function(arg) {
-      return Object.prototype.toString.call(arg) === '[object Array]';
+        _this3.on(target, type, newFunc);
+      })();
     }
 
-module.exports = function (headers) {
-  if (!headers)
-    return {}
-
-  var result = {}
+    return this;
+  };
 
-  forEach(
-      trim(headers).split('\n')
-    , function (row) {
-        var index = row.indexOf(':')
-          , key = trim(row.slice(0, index)).toLowerCase()
-          , value = trim(row.slice(index + 1))
+  /**
+   * Trigger an event on an element
+   * ```js
+   *     myComponent.trigger('eventName');
+   *     myComponent.trigger({'type':'eventName'});
+   *     myComponent.trigger('eventName', {data: 'some data'});
+   *     myComponent.trigger({'type':'eventName'}, {data: 'some data'});
+   * ```
+   *
+   * @param  {Event|Object|String} event  A string (the type) or an event object with a type attribute
+   * @param  {Object} [hash] data hash to pass along with the event
+   * @return {Component}       self
+   * @method trigger
+   */
 
-        if (typeof(result[key]) === 'undefined') {
-          result[key] = value
-        } else if (isArray(result[key])) {
-          result[key].push(value)
-        } else {
-          result[key] = [ result[key], value ]
-        }
-      }
-  )
 
-  return result
-}
-},{"for-each":59,"trim":60}],62:[function(_dereq_,module,exports){
-module.exports = extend
+  Component.prototype.trigger = function trigger(event, hash) {
+    Events.trigger(this.el_, event, hash);
+    return this;
+  };
 
-var hasOwnProperty = Object.prototype.hasOwnProperty;
+  /**
+   * Bind a listener to the component's ready state.
+   * Different from event listeners in that if the ready event has already happened
+   * it will trigger the function immediately.
+   *
+   * @param  {Function} fn Ready listener
+   * @param  {Boolean} sync Exec the listener synchronously if component is ready
+   * @return {Component}
+   * @method ready
+   */
 
-function extend() {
-    var target = {}
 
-    for (var i = 0; i < arguments.length; i++) {
-        var source = arguments[i]
+  Component.prototype.ready = function ready(fn) {
+    var sync = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
 
-        for (var key in source) {
-            if (hasOwnProperty.call(source, key)) {
-                target[key] = source[key]
-            }
+    if (fn) {
+      if (this.isReady_) {
+        if (sync) {
+          fn.call(this);
+        } else {
+          // Call the function asynchronously by default for consistency
+          this.setTimeout(fn, 1);
         }
+      } else {
+        this.readyQueue_ = this.readyQueue_ || [];
+        this.readyQueue_.push(fn);
+      }
     }
+    return this;
+  };
 
-    return target
-}
-
-},{}],63:[function(_dereq_,module,exports){
-/**
- * @file big-play-button.js
- */
-'use strict';
-
-exports.__esModule = true;
+  /**
+   * Trigger the ready listeners
+   *
+   * @return {Component}
+   * @method triggerReady
+   */
 
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
 
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
+  Component.prototype.triggerReady = function triggerReady() {
+    this.isReady_ = true;
 
-function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+    // Ensure ready is triggerd asynchronously
+    this.setTimeout(function () {
+      var readyQueue = this.readyQueue_;
 
-var _buttonJs = _dereq_('./button.js');
+      // Reset Ready Queue
+      this.readyQueue_ = [];
 
-var _buttonJs2 = _interopRequireDefault(_buttonJs);
+      if (readyQueue && readyQueue.length > 0) {
+        readyQueue.forEach(function (fn) {
+          fn.call(this);
+        }, this);
+      }
 
-var _componentJs = _dereq_('./component.js');
-
-var _componentJs2 = _interopRequireDefault(_componentJs);
-
-/**
- * Initial play button. Shows before the video has played. The hiding of the
- * big play button is done via CSS and player states.
- *
- * @param {Object} player  Main Player
- * @param {Object=} options Object of option names and values
- * @extends Button
- * @class BigPlayButton
- */
-
-var BigPlayButton = (function (_Button) {
-  _inherits(BigPlayButton, _Button);
-
-  function BigPlayButton(player, options) {
-    _classCallCheck(this, BigPlayButton);
-
-    _Button.call(this, player, options);
-  }
+      // Allow for using event listeners also
+      this.trigger('ready');
+    }, 1);
+  };
 
   /**
-   * Allow sub components to stack CSS class names
+   * Finds a single DOM element matching `selector` within the component's
+   * `contentEl` or another custom context.
    *
-   * @return {String} The constructed class name
-   * @method buildCSSClass
+   * @method $
+   * @param  {String} selector
+   *         A valid CSS selector, which will be passed to `querySelector`.
+   *
+   * @param  {Element|String} [context=document]
+   *         A DOM element within which to query. Can also be a selector
+   *         string in which case the first matching element will be used
+   *         as context. If missing (or no element matches selector), falls
+   *         back to `document`.
+   *
+   * @return {Element|null}
    */
 
-  BigPlayButton.prototype.buildCSSClass = function buildCSSClass() {
-    return 'vjs-big-play-button';
+
+  Component.prototype.$ = function $(selector, context) {
+    return Dom.$(selector, context || this.contentEl());
   };
 
   /**
-   * Handles click for play
+   * Finds a all DOM elements matching `selector` within the component's
+   * `contentEl` or another custom context.
    *
-   * @method handleClick
+   * @method $$
+   * @param  {String} selector
+   *         A valid CSS selector, which will be passed to `querySelectorAll`.
+   *
+   * @param  {Element|String} [context=document]
+   *         A DOM element within which to query. Can also be a selector
+   *         string in which case the first matching element will be used
+   *         as context. If missing (or no element matches selector), falls
+   *         back to `document`.
+   *
+   * @return {NodeList}
    */
 
-  BigPlayButton.prototype.handleClick = function handleClick() {
-    this.player_.play();
-  };
-
-  return BigPlayButton;
-})(_buttonJs2['default']);
-
-BigPlayButton.prototype.controlText_ = 'Play Video';
-
-_componentJs2['default'].registerComponent('BigPlayButton', BigPlayButton);
-exports['default'] = BigPlayButton;
-module.exports = exports['default'];
-
-},{"./button.js":64,"./component.js":67}],64:[function(_dereq_,module,exports){
-/**
- * @file button.js
- */
-'use strict';
-
-exports.__esModule = true;
-
-function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
 
-function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+  Component.prototype.$$ = function $$(selector, context) {
+    return Dom.$$(selector, context || this.contentEl());
+  };
 
-var _clickableComponentJs = _dereq_('./clickable-component.js');
+  /**
+   * Check if a component's element has a CSS class name
+   *
+   * @param {String} classToCheck Classname to check
+   * @return {Component}
+   * @method hasClass
+   */
 
-var _clickableComponentJs2 = _interopRequireDefault(_clickableComponentJs);
 
-var _component = _dereq_('./component');
+  Component.prototype.hasClass = function hasClass(classToCheck) {
+    return Dom.hasElClass(this.el_, classToCheck);
+  };
 
-var _component2 = _interopRequireDefault(_component);
+  /**
+   * Add a CSS class name to the component's element
+   *
+   * @param {String} classToAdd Classname to add
+   * @return {Component}
+   * @method addClass
+   */
 
-var _utilsEventsJs = _dereq_('./utils/events.js');
 
-var Events = _interopRequireWildcard(_utilsEventsJs);
+  Component.prototype.addClass = function addClass(classToAdd) {
+    Dom.addElClass(this.el_, classToAdd);
+    return this;
+  };
 
-var _utilsFnJs = _dereq_('./utils/fn.js');
+  /**
+   * Remove a CSS class name from the component's element
+   *
+   * @param {String} classToRemove Classname to remove
+   * @return {Component}
+   * @method removeClass
+   */
 
-var Fn = _interopRequireWildcard(_utilsFnJs);
 
-var _utilsLogJs = _dereq_('./utils/log.js');
+  Component.prototype.removeClass = function removeClass(classToRemove) {
+    Dom.removeElClass(this.el_, classToRemove);
+    return this;
+  };
 
-var _utilsLogJs2 = _interopRequireDefault(_utilsLogJs);
+  /**
+   * Add or remove a CSS class name from the component's element
+   *
+   * @param  {String} classToToggle
+   * @param  {Boolean|Function} [predicate]
+   *         Can be a function that returns a Boolean. If `true`, the class
+   *         will be added; if `false`, the class will be removed. If not
+   *         given, the class will be added if not present and vice versa.
+   *
+   * @return {Component}
+   * @method toggleClass
+   */
 
-var _globalDocument = _dereq_('global/document');
 
-var _globalDocument2 = _interopRequireDefault(_globalDocument);
+  Component.prototype.toggleClass = function toggleClass(classToToggle, predicate) {
+    Dom.toggleElClass(this.el_, classToToggle, predicate);
+    return this;
+  };
 
-var _objectAssign = _dereq_('object.assign');
+  /**
+   * Show the component element if hidden
+   *
+   * @return {Component}
+   * @method show
+   */
 
-var _objectAssign2 = _interopRequireDefault(_objectAssign);
 
-/**
- * Base class for all buttons
- *
- * @param {Object} player  Main Player
- * @param {Object=} options Object of option names and values
- * @extends ClickableComponent
- * @class Button
- */
+  Component.prototype.show = function show() {
+    this.removeClass('vjs-hidden');
+    return this;
+  };
 
-var Button = (function (_ClickableComponent) {
-  _inherits(Button, _ClickableComponent);
+  /**
+   * Hide the component element if currently showing
+   *
+   * @return {Component}
+   * @method hide
+   */
 
-  function Button(player, options) {
-    _classCallCheck(this, Button);
 
-    _ClickableComponent.call(this, player, options);
-  }
+  Component.prototype.hide = function hide() {
+    this.addClass('vjs-hidden');
+    return this;
+  };
 
   /**
-   * Create the component's DOM element
+   * Lock an item in its visible state
+   * To be used with fadeIn/fadeOut.
    *
-   * @param {String=} type Element's node type. e.g. 'div'
-   * @param {Object=} props An object of properties that should be set on the element
-   * @param {Object=} attributes An object of attributes that should be set on the element
-   * @return {Element}
-   * @method createEl
+   * @return {Component}
+   * @private
+   * @method lockShowing
    */
 
-  Button.prototype.createEl = function createEl() {
-    var tag = arguments.length <= 0 || arguments[0] === undefined ? 'button' : arguments[0];
-    var props = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
-    var attributes = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
-
-    props = _objectAssign2['default']({
-      className: this.buildCSSClass()
-    }, props);
 
-    if (tag !== 'button') {
-      _utilsLogJs2['default'].warn('Creating a Button with an HTML element of ' + tag + ' is deprecated; use ClickableComponent instead.');
+  Component.prototype.lockShowing = function lockShowing() {
+    this.addClass('vjs-lock-showing');
+    return this;
+  };
 
-      // Add properties for clickable element which is not a native HTML button
-      props = _objectAssign2['default']({
-        tabIndex: 0
-      }, props);
+  /**
+   * Unlock an item to be hidden
+   * To be used with fadeIn/fadeOut.
+   *
+   * @return {Component}
+   * @private
+   * @method unlockShowing
+   */
 
-      // Add ARIA attributes for clickable element which is not a native HTML button
-      attributes = _objectAssign2['default']({
-        role: 'button'
-      }, attributes);
-    }
 
-    // Add attributes for button element
-    attributes = _objectAssign2['default']({
-      type: 'button', // Necessary since the default button type is "submit"
-      'aria-live': 'polite' // let the screen reader user know that the text of the button may change
-    }, attributes);
+  Component.prototype.unlockShowing = function unlockShowing() {
+    this.removeClass('vjs-lock-showing');
+    return this;
+  };
 
-    var el = _component2['default'].prototype.createEl.call(this, tag, props, attributes);
+  /**
+   * Set or get the width of the component (CSS values)
+   * Setting the video tag dimension values only works with values in pixels.
+   * Percent values will not work.
+   * Some percents can be used, but width()/height() will return the number + %,
+   * not the actual computed width/height.
+   *
+   * @param  {Number|String=} num   Optional width number
+   * @param  {Boolean} skipListeners Skip the 'resize' event trigger
+   * @return {Component} This component, when setting the width
+   * @return {Number|String} The width, when getting
+   * @method width
+   */
 
-    this.createControlTextEl(el);
 
-    return el;
+  Component.prototype.width = function width(num, skipListeners) {
+    return this.dimension('width', num, skipListeners);
   };
 
   /**
-   * Adds a child component inside this button
+   * Get or set the height of the component (CSS values)
+   * Setting the video tag dimension values only works with values in pixels.
+   * Percent values will not work.
+   * Some percents can be used, but width()/height() will return the number + %,
+   * not the actual computed width/height.
    *
-   * @param {String|Component} child The class name or instance of a child to add
-   * @param {Object=} options Options, including options to be passed to children of the child.
-   * @return {Component} The child component (created by this process if a string was used)
-   * @deprecated
-   * @method addChild
+   * @param  {Number|String=} num     New component height
+   * @param  {Boolean=} skipListeners Skip the resize event trigger
+   * @return {Component} This component, when setting the height
+   * @return {Number|String} The height, when getting
+   * @method height
    */
 
-  Button.prototype.addChild = function addChild(child) {
-    var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
-
-    var className = this.constructor.name;
-    _utilsLogJs2['default'].warn('Adding an actionable (user controllable) child to a Button (' + className + ') is not supported; use a ClickableComponent instead.');
 
-    // Avoid the error message generated by ClickableComponent's addChild method
-    return _component2['default'].prototype.addChild.call(this, child, options);
+  Component.prototype.height = function height(num, skipListeners) {
+    return this.dimension('height', num, skipListeners);
   };
 
   /**
-   * Handle KeyPress (document level) - Extend with specific functionality for button
+   * Set both width and height at the same time
    *
-   * @method handleKeyPress
+   * @param  {Number|String} width Width of player
+   * @param  {Number|String} height Height of player
+   * @return {Component} The component
+   * @method dimensions
    */
 
-  Button.prototype.handleKeyPress = function handleKeyPress(event) {
-    // Ignore Space (32) or Enter (13) key operation, which is handled by the browser for a button.
-    if (event.which === 32 || event.which === 13) {} else {
-      _ClickableComponent.prototype.handleKeyPress.call(this, event); // Pass keypress handling up for unsupported keys
-    }
-  };
 
-  return Button;
-})(_clickableComponentJs2['default']);
-
-_component2['default'].registerComponent('Button', Button);
-exports['default'] = Button;
-module.exports = exports['default'];
-
-},{"./clickable-component.js":65,"./component":67,"./utils/events.js":143,"./utils/fn.js":144,"./utils/log.js":147,"global/document":1,"object.assign":45}],65:[function(_dereq_,module,exports){
-/**
- * @file button.js
- */
-'use strict';
-
-exports.__esModule = true;
-
-function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
-
-function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
-
-var _component = _dereq_('./component');
-
-var _component2 = _interopRequireDefault(_component);
-
-var _utilsDomJs = _dereq_('./utils/dom.js');
-
-var Dom = _interopRequireWildcard(_utilsDomJs);
-
-var _utilsEventsJs = _dereq_('./utils/events.js');
-
-var Events = _interopRequireWildcard(_utilsEventsJs);
-
-var _utilsFnJs = _dereq_('./utils/fn.js');
-
-var Fn = _interopRequireWildcard(_utilsFnJs);
-
-var _utilsLogJs = _dereq_('./utils/log.js');
-
-var _utilsLogJs2 = _interopRequireDefault(_utilsLogJs);
+  Component.prototype.dimensions = function dimensions(width, height) {
+    // Skip resize listeners on width for optimization
+    return this.width(width, true).height(height);
+  };
 
-var _globalDocument = _dereq_('global/document');
+  /**
+   * Get or set width or height
+   * This is the shared code for the width() and height() methods.
+   * All for an integer, integer + 'px' or integer + '%';
+   * Known issue: Hidden elements officially have a width of 0. We're defaulting
+   * to the style.width value and falling back to computedStyle which has the
+   * hidden element issue. Info, but probably not an efficient fix:
+   * http://www.foliotek.com/devblog/getting-the-width-of-a-hidden-element-with-jquery-using-width/
+   *
+   * @param  {String} widthOrHeight  'width' or 'height'
+   * @param  {Number|String=} num     New dimension
+   * @param  {Boolean=} skipListeners Skip resize event trigger
+   * @return {Component} The component if a dimension was set
+   * @return {Number|String} The dimension if nothing was set
+   * @private
+   * @method dimension
+   */
 
-var _globalDocument2 = _interopRequireDefault(_globalDocument);
 
-var _objectAssign = _dereq_('object.assign');
+  Component.prototype.dimension = function dimension(widthOrHeight, num, skipListeners) {
+    if (num !== undefined) {
+      // Set to zero if null or literally NaN (NaN !== NaN)
+      if (num === null || num !== num) {
+        num = 0;
+      }
 
-var _objectAssign2 = _interopRequireDefault(_objectAssign);
+      // Check if using css width/height (% or px) and adjust
+      if (('' + num).indexOf('%') !== -1 || ('' + num).indexOf('px') !== -1) {
+        this.el_.style[widthOrHeight] = num;
+      } else if (num === 'auto') {
+        this.el_.style[widthOrHeight] = '';
+      } else {
+        this.el_.style[widthOrHeight] = num + 'px';
+      }
 
-/**
- * Clickable Component which is clickable or keyboard actionable, but is not a native HTML button
- *
- * @param {Object} player  Main Player
- * @param {Object=} options Object of option names and values
- * @extends Component
- * @class ClickableComponent
- */
+      // skipListeners allows us to avoid triggering the resize event when setting both width and height
+      if (!skipListeners) {
+        this.trigger('resize');
+      }
 
-var ClickableComponent = (function (_Component) {
-  _inherits(ClickableComponent, _Component);
+      // Return component
+      return this;
+    }
 
-  function ClickableComponent(player, options) {
-    _classCallCheck(this, ClickableComponent);
+    // Not setting a value, so getting it
+    // Make sure element exists
+    if (!this.el_) {
+      return 0;
+    }
 
-    _Component.call(this, player, options);
+    // Get dimension value from style
+    var val = this.el_.style[widthOrHeight];
+    var pxIndex = val.indexOf('px');
 
-    this.emitTapEvents();
+    if (pxIndex !== -1) {
+      // Return the pixel value with no 'px'
+      return parseInt(val.slice(0, pxIndex), 10);
+    }
 
-    this.on('tap', this.handleClick);
-    this.on('click', this.handleClick);
-    this.on('focus', this.handleFocus);
-    this.on('blur', this.handleBlur);
-  }
+    // No px so using % or no style was set, so falling back to offsetWidth/height
+    // If component has display:none, offset will return 0
+    // TODO: handle display:none and no dimension style using px
+    return parseInt(this.el_['offset' + (0, _toTitleCase2['default'])(widthOrHeight)], 10);
+  };
 
   /**
-   * Create the component's DOM element
-   *
-   * @param {String=} type Element's node type. e.g. 'div'
-   * @param {Object=} props An object of properties that should be set on the element
-   * @param {Object=} attributes An object of attributes that should be set on the element
-   * @return {Element}
-   * @method createEl
+   * Get width or height of computed style
+   * @param  {String} widthOrHeight  'width' or 'height'
+   * @return {Number|Boolean} The bolean false if nothing was set
+   * @method currentDimension
    */
 
-  ClickableComponent.prototype.createEl = function createEl() {
-    var tag = arguments.length <= 0 || arguments[0] === undefined ? 'div' : arguments[0];
-    var props = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
-    var attributes = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
 
-    props = _objectAssign2['default']({
-      className: this.buildCSSClass(),
-      tabIndex: 0
-    }, props);
+  Component.prototype.currentDimension = function currentDimension(widthOrHeight) {
+    var computedWidthOrHeight = 0;
 
-    if (tag === 'button') {
-      _utilsLogJs2['default'].error('Creating a ClickableComponent with an HTML element of ' + tag + ' is not supported; use a Button instead.');
+    if (widthOrHeight !== 'width' && widthOrHeight !== 'height') {
+      throw new Error('currentDimension only accepts width or height value');
     }
 
-    // Add ARIA attributes for clickable element which is not a native HTML button
-    attributes = _objectAssign2['default']({
-      role: 'button',
-      'aria-live': 'polite' // let the screen reader user know that the text of the element may change
-    }, attributes);
+    if (typeof _window2['default'].getComputedStyle === 'function') {
+      var computedStyle = _window2['default'].getComputedStyle(this.el_);
 
-    var el = _Component.prototype.createEl.call(this, tag, props, attributes);
+      computedWidthOrHeight = computedStyle.getPropertyValue(widthOrHeight) || computedStyle[widthOrHeight];
+    } else if (this.el_.currentStyle) {
+      // ie 8 doesn't support computed style, shim it
+      // return clientWidth or clientHeight instead for better accuracy
+      var rule = 'offset' + (0, _toTitleCase2['default'])(widthOrHeight);
 
-    this.createControlTextEl(el);
+      computedWidthOrHeight = this.el_[rule];
+    }
 
-    return el;
+    // remove 'px' from variable and parse as integer
+    computedWidthOrHeight = parseFloat(computedWidthOrHeight);
+    return computedWidthOrHeight;
   };
 
   /**
-   * create control text
-   *
-   * @param {Element} el Parent element for the control text
-   * @return {Element}
-   * @method controlText
+   * Get an object which contains width and height values of computed style
+   * @return {Object} The dimensions of element
+   * @method currentDimensions
    */
 
-  ClickableComponent.prototype.createControlTextEl = function createControlTextEl(el) {
-    this.controlTextEl_ = Dom.createEl('span', {
-      className: 'vjs-control-text'
-    });
-
-    if (el) {
-      el.appendChild(this.controlTextEl_);
-    }
-
-    this.controlText(this.controlText_, el);
 
-    return this.controlTextEl_;
+  Component.prototype.currentDimensions = function currentDimensions() {
+    return {
+      width: this.currentDimension('width'),
+      height: this.currentDimension('height')
+    };
   };
 
   /**
-   * Controls text - both request and localize
-   *
-   * @param {String}  text Text for element
-   * @param {Element=} el Element to set the title on
-   * @return {String}
-   * @method controlText
+   * Get width of computed style
+   * @return {Integer}
+   * @method currentWidth
    */
 
-  ClickableComponent.prototype.controlText = function controlText(text) {
-    var el = arguments.length <= 1 || arguments[1] === undefined ? this.el() : arguments[1];
-
-    if (!text) return this.controlText_ || 'Need Text';
-
-    var localizedText = this.localize(text);
-
-    this.controlText_ = text;
-    this.controlTextEl_.innerHTML = localizedText;
-    el.setAttribute('title', localizedText);
 
-    return this;
+  Component.prototype.currentWidth = function currentWidth() {
+    return this.currentDimension('width');
   };
 
   /**
-   * Allows sub components to stack CSS class names
-   *
-   * @return {String}
-   * @method buildCSSClass
+   * Get height of computed style
+   * @return {Integer}
+   * @method currentHeight
    */
 
-  ClickableComponent.prototype.buildCSSClass = function buildCSSClass() {
-    return 'vjs-control vjs-button ' + _Component.prototype.buildCSSClass.call(this);
+
+  Component.prototype.currentHeight = function currentHeight() {
+    return this.currentDimension('height');
   };
 
   /**
-   * Adds a child component inside this clickable-component
+   * Emit 'tap' events when touch events are supported
+   * This is used to support toggling the controls through a tap on the video.
+   * We're requiring them to be enabled because otherwise every component would
+   * have this extra overhead unnecessarily, on mobile devices where extra
+   * overhead is especially bad.
    *
-   * @param {String|Component} child The class name or instance of a child to add
-   * @param {Object=} options Options, including options to be passed to children of the child.
-   * @return {Component} The child component (created by this process if a string was used)
-   * @method addChild
+   * @private
+   * @method emitTapEvents
    */
 
-  ClickableComponent.prototype.addChild = function addChild(child) {
-    var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
 
-    // TODO: Fix adding an actionable child to a ClickableComponent; currently
-    // it will cause issues with assistive technology (e.g. screen readers)
-    // which support ARIA, since an element with role="button" cannot have
-    // actionable child elements.
+  Component.prototype.emitTapEvents = function emitTapEvents() {
+    // Track the start time so we can determine how long the touch lasted
+    var touchStart = 0;
+    var firstTouch = null;
 
-    //let className = this.constructor.name;
-    //log.warn(`Adding a child to a ClickableComponent (${className}) can cause issues with assistive technology which supports ARIA, since an element with role="button" cannot have actionable child elements.`);
+    // Maximum movement allowed during a touch event to still be considered a tap
+    // Other popular libs use anywhere from 2 (hammer.js) to 15, so 10 seems like a nice, round number.
+    var tapMovementThreshold = 10;
 
-    return _Component.prototype.addChild.call(this, child, options);
-  };
+    // The maximum length a touch can be while still being considered a tap
+    var touchTimeThreshold = 200;
 
-  /**
-   * Enable the component element
-   *
-   * @return {Component}
-   * @method enable
-   */
+    var couldBeTap = void 0;
 
-  ClickableComponent.prototype.enable = function enable() {
-    this.removeClass('vjs-disabled');
-    this.el_.setAttribute('aria-disabled', 'false');
-    return this;
-  };
+    this.on('touchstart', function (event) {
+      // If more than one finger, don't consider treating this as a click
+      if (event.touches.length === 1) {
+        // Copy pageX/pageY from the object
+        firstTouch = {
+          pageX: event.touches[0].pageX,
+          pageY: event.touches[0].pageY
+        };
+        // Record start time so we can detect a tap vs. "touch and hold"
+        touchStart = new Date().getTime();
+        // Reset couldBeTap tracking
+        couldBeTap = true;
+      }
+    });
 
-  /**
-   * Disable the component element
-   *
-   * @return {Component}
-   * @method disable
-   */
+    this.on('touchmove', function (event) {
+      // If more than one finger, don't consider treating this as a click
+      if (event.touches.length > 1) {
+        couldBeTap = false;
+      } else if (firstTouch) {
+        // Some devices will throw touchmoves for all but the slightest of taps.
+        // So, if we moved only a small distance, this could still be a tap
+        var xdiff = event.touches[0].pageX - firstTouch.pageX;
+        var ydiff = event.touches[0].pageY - firstTouch.pageY;
+        var touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff);
 
-  ClickableComponent.prototype.disable = function disable() {
-    this.addClass('vjs-disabled');
-    this.el_.setAttribute('aria-disabled', 'true');
-    return this;
-  };
+        if (touchDistance > tapMovementThreshold) {
+          couldBeTap = false;
+        }
+      }
+    });
 
-  /**
-   * Handle Click - Override with specific functionality for component
-   *
-   * @method handleClick
-   */
+    var noTap = function noTap() {
+      couldBeTap = false;
+    };
 
-  ClickableComponent.prototype.handleClick = function handleClick() {};
+    // TODO: Listen to the original target. http://youtu.be/DujfpXOKUp8?t=13m8s
+    this.on('touchleave', noTap);
+    this.on('touchcancel', noTap);
 
-  /**
-   * Handle Focus - Add keyboard functionality to element
-   *
-   * @method handleFocus
-   */
+    // When the touch ends, measure how long it took and trigger the appropriate
+    // event
+    this.on('touchend', function (event) {
+      firstTouch = null;
+      // Proceed only if the touchmove/leave/cancel event didn't happen
+      if (couldBeTap === true) {
+        // Measure how long the touch lasted
+        var touchTime = new Date().getTime() - touchStart;
 
-  ClickableComponent.prototype.handleFocus = function handleFocus() {
-    Events.on(_globalDocument2['default'], 'keydown', Fn.bind(this, this.handleKeyPress));
+        // Make sure the touch was less than the threshold to be considered a tap
+        if (touchTime < touchTimeThreshold) {
+          // Don't let browser turn this into a click
+          event.preventDefault();
+          this.trigger('tap');
+          // It may be good to copy the touchend event object and change the
+          // type to tap, if the other event properties aren't exact after
+          // Events.fixEvent runs (e.g. event.target)
+        }
+      }
+    });
   };
 
   /**
-   * Handle KeyPress (document level) - Trigger click when Space or Enter key is pressed
+   * Report user touch activity when touch events occur
+   * User activity is used to determine when controls should show/hide. It's
+   * relatively simple when it comes to mouse events, because any mouse event
+   * should show the controls. So we capture mouse events that bubble up to the
+   * player and report activity when that happens.
+   * With touch events it isn't as easy. We can't rely on touch events at the
+   * player level, because a tap (touchstart + touchend) on the video itself on
+   * mobile devices is meant to turn controls off (and on). User activity is
+   * checked asynchronously, so what could happen is a tap event on the video
+   * turns the controls off, then the touchend event bubbles up to the player,
+   * which if it reported user activity, would turn the controls right back on.
+   * (We also don't want to completely block touch events from bubbling up)
+   * Also a touchmove, touch+hold, and anything other than a tap is not supposed
+   * to turn the controls back on on a mobile device.
+   * Here we're setting the default component behavior to report user activity
+   * whenever touch events happen, and this can be turned off by components that
+   * want touch events to act differently.
    *
-   * @method handleKeyPress
+   * @method enableTouchActivity
    */
 
-  ClickableComponent.prototype.handleKeyPress = function handleKeyPress(event) {
-    // Support Space (32) or Enter (13) key operation to fire a click event
-    if (event.which === 32 || event.which === 13) {
-      event.preventDefault();
-      this.handleClick(event);
-    } else if (_Component.prototype.handleKeyPress) {
-      _Component.prototype.handleKeyPress.call(this, event); // Pass keypress handling up for unsupported keys
+
+  Component.prototype.enableTouchActivity = function enableTouchActivity() {
+    // Don't continue if the root player doesn't support reporting user activity
+    if (!this.player() || !this.player().reportUserActivity) {
+      return;
     }
+
+    // listener for reporting that the user is active
+    var report = Fn.bind(this.player(), this.player().reportUserActivity);
+
+    var touchHolding = void 0;
+
+    this.on('touchstart', function () {
+      report();
+      // For as long as the they are touching the device or have their mouse down,
+      // we consider them active even if they're not moving their finger or mouse.
+      // So we want to continue to update that they are active
+      this.clearInterval(touchHolding);
+      // report at the same interval as activityCheck
+      touchHolding = this.setInterval(report, 250);
+    });
+
+    var touchEnd = function touchEnd(event) {
+      report();
+      // stop the interval that maintains activity if the touch is holding
+      this.clearInterval(touchHolding);
+    };
+
+    this.on('touchmove', report);
+    this.on('touchend', touchEnd);
+    this.on('touchcancel', touchEnd);
   };
 
   /**
-   * Handle Blur - Remove keyboard triggers
+   * Creates timeout and sets up disposal automatically.
    *
-   * @method handleBlur
+   * @param {Function} fn The function to run after the timeout.
+   * @param {Number} timeout Number of ms to delay before executing specified function.
+   * @return {Number} Returns the timeout ID
+   * @method setTimeout
    */
 
-  ClickableComponent.prototype.handleBlur = function handleBlur() {
-    Events.off(_globalDocument2['default'], 'keydown', Fn.bind(this, this.handleKeyPress));
-  };
 
-  return ClickableComponent;
-})(_component2['default']);
+  Component.prototype.setTimeout = function setTimeout(fn, timeout) {
+    fn = Fn.bind(this, fn);
 
-_component2['default'].registerComponent('ClickableComponent', ClickableComponent);
-exports['default'] = ClickableComponent;
-module.exports = exports['default'];
+    // window.setTimeout would be preferable here, but due to some bizarre issue with Sinon and/or Phantomjs, we can't.
+    var timeoutId = _window2['default'].setTimeout(fn, timeout);
 
-},{"./component":67,"./utils/dom.js":142,"./utils/events.js":143,"./utils/fn.js":144,"./utils/log.js":147,"global/document":1,"object.assign":45}],66:[function(_dereq_,module,exports){
-'use strict';
+    var disposeFn = function disposeFn() {
+      this.clearTimeout(timeoutId);
+    };
 
-exports.__esModule = true;
+    disposeFn.guid = 'vjs-timeout-' + timeoutId;
 
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
+    this.on('dispose', disposeFn);
 
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
+    return timeoutId;
+  };
 
-function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+  /**
+   * Clears a timeout and removes the associated dispose listener
+   *
+   * @param {Number} timeoutId The id of the timeout to clear
+   * @return {Number} Returns the timeout ID
+   * @method clearTimeout
+   */
 
-var _button = _dereq_('./button');
 
-var _button2 = _interopRequireDefault(_button);
+  Component.prototype.clearTimeout = function clearTimeout(timeoutId) {
+    _window2['default'].clearTimeout(timeoutId);
 
-var _component = _dereq_('./component');
+    var disposeFn = function disposeFn() {};
 
-var _component2 = _interopRequireDefault(_component);
+    disposeFn.guid = 'vjs-timeout-' + timeoutId;
 
-/**
- * The `CloseButton` component is a button which fires a "close" event
- * when it is activated.
- *
- * @extends Button
- * @class CloseButton
- */
+    this.off('dispose', disposeFn);
 
-var CloseButton = (function (_Button) {
-  _inherits(CloseButton, _Button);
+    return timeoutId;
+  };
 
-  function CloseButton(player, options) {
-    _classCallCheck(this, CloseButton);
+  /**
+   * Creates an interval and sets up disposal automatically.
+   *
+   * @param {Function} fn The function to run every N seconds.
+   * @param {Number} interval Number of ms to delay before executing specified function.
+   * @return {Number} Returns the interval ID
+   * @method setInterval
+   */
 
-    _Button.call(this, player, options);
-    this.controlText(options && options.controlText || this.localize('Close'));
-  }
 
-  CloseButton.prototype.buildCSSClass = function buildCSSClass() {
-    return 'vjs-close-button ' + _Button.prototype.buildCSSClass.call(this);
-  };
+  Component.prototype.setInterval = function setInterval(fn, interval) {
+    fn = Fn.bind(this, fn);
 
-  CloseButton.prototype.handleClick = function handleClick() {
-    this.trigger({ type: 'close', bubbles: false });
-  };
+    var intervalId = _window2['default'].setInterval(fn, interval);
 
-  return CloseButton;
-})(_button2['default']);
+    var disposeFn = function disposeFn() {
+      this.clearInterval(intervalId);
+    };
 
-_component2['default'].registerComponent('CloseButton', CloseButton);
-exports['default'] = CloseButton;
-module.exports = exports['default'];
+    disposeFn.guid = 'vjs-interval-' + intervalId;
 
-},{"./button":64,"./component":67}],67:[function(_dereq_,module,exports){
-/**
- * @file component.js
- *
- * Player Component - Base class for all UI objects
- */
+    this.on('dispose', disposeFn);
 
-'use strict';
+    return intervalId;
+  };
 
-exports.__esModule = true;
+  /**
+   * Clears an interval and removes the associated dispose listener
+   *
+   * @param {Number} intervalId The id of the interval to clear
+   * @return {Number} Returns the interval ID
+   * @method clearInterval
+   */
 
-function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
 
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
+  Component.prototype.clearInterval = function clearInterval(intervalId) {
+    _window2['default'].clearInterval(intervalId);
 
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
+    var disposeFn = function disposeFn() {};
 
-var _globalWindow = _dereq_('global/window');
+    disposeFn.guid = 'vjs-interval-' + intervalId;
 
-var _globalWindow2 = _interopRequireDefault(_globalWindow);
+    this.off('dispose', disposeFn);
 
-var _utilsDomJs = _dereq_('./utils/dom.js');
+    return intervalId;
+  };
 
-var Dom = _interopRequireWildcard(_utilsDomJs);
+  /**
+   * Registers a component
+   *
+   * @param {String} name Name of the component to register
+   * @param {Object} comp The component to register
+   * @static
+   * @method registerComponent
+   */
 
-var _utilsFnJs = _dereq_('./utils/fn.js');
 
-var Fn = _interopRequireWildcard(_utilsFnJs);
+  Component.registerComponent = function registerComponent(name, comp) {
+    if (!Component.components_) {
+      Component.components_ = {};
+    }
 
-var _utilsGuidJs = _dereq_('./utils/guid.js');
+    Component.components_[name] = comp;
+    return comp;
+  };
 
-var Guid = _interopRequireWildcard(_utilsGuidJs);
+  /**
+   * Gets a component by name
+   *
+   * @param {String} name Name of the component to get
+   * @return {Component}
+   * @static
+   * @method getComponent
+   */
 
-var _utilsEventsJs = _dereq_('./utils/events.js');
 
-var Events = _interopRequireWildcard(_utilsEventsJs);
+  Component.getComponent = function getComponent(name) {
+    if (Component.components_ && Component.components_[name]) {
+      return Component.components_[name];
+    }
 
-var _utilsLogJs = _dereq_('./utils/log.js');
+    if (_window2['default'] && _window2['default'].videojs && _window2['default'].videojs[name]) {
+      _log2['default'].warn('The ' + name + ' component was added to the videojs object when it should be registered using videojs.registerComponent(name, component)');
+      return _window2['default'].videojs[name];
+    }
+  };
 
-var _utilsLogJs2 = _interopRequireDefault(_utilsLogJs);
+  /**
+   * Sets up the constructor using the supplied init method
+   * or uses the init of the parent object
+   *
+   * @param {Object} props An object of properties
+   * @static
+   * @deprecated
+   * @method extend
+   */
 
-var _utilsToTitleCaseJs = _dereq_('./utils/to-title-case.js');
 
-var _utilsToTitleCaseJs2 = _interopRequireDefault(_utilsToTitleCaseJs);
+  Component.extend = function extend(props) {
+    props = props || {};
 
-var _utilsMergeOptionsJs = _dereq_('./utils/merge-options.js');
+    _log2['default'].warn('Component.extend({}) has been deprecated, use videojs.extend(Component, {}) instead');
 
-var _utilsMergeOptionsJs2 = _interopRequireDefault(_utilsMergeOptionsJs);
+    // Set up the constructor using the supplied init method
+    // or using the init of the parent object
+    // Make sure to check the unobfuscated version for external libs
+    var init = props.init || props.init || this.prototype.init || this.prototype.init || function () {};
+    // In Resig's simple class inheritance (previously used) the constructor
+    //  is a function that calls `this.init.apply(arguments)`
+    // However that would prevent us from using `ParentObject.call(this);`
+    //  in a Child constructor because the `this` in `this.init`
+    //  would still refer to the Child and cause an infinite loop.
+    // We would instead have to do
+    //    `ParentObject.prototype.init.apply(this, arguments);`
+    //  Bleh. We're not creating a _super() function, so it's good to keep
+    //  the parent constructor reference simple.
+    var subObj = function subObj() {
+      init.apply(this, arguments);
+    };
 
-/**
- * Base UI Component class
- * Components are embeddable UI objects that are represented by both a
- * javascript object and an element in the DOM. They can be children of other
- * components, and can have many children themselves.
- * ```js
- *     // adding a button to the player
- *     var button = player.addChild('button');
- *     button.el(); // -> button element
- * ```
- * ```html
- *     <div class="video-js">
- *       <div class="vjs-button">Button</div>
- *     </div>
- * ```
- * Components are also event targets.
- * ```js
- *     button.on('click', function(){
- *       console.log('Button Clicked!');
- *     });
- *     button.trigger('customevent');
- * ```
- *
- * @param {Object} player  Main Player
- * @param {Object=} options Object of option names and values
- * @param {Function=} ready    Ready callback function
- * @class Component
- */
+    // Inherit from this object's prototype
+    subObj.prototype = Object.create(this.prototype);
+    // Reset the constructor property for subObj otherwise
+    // instances of subObj would have the constructor of the parent Object
+    subObj.prototype.constructor = subObj;
 
-var Component = (function () {
-  function Component(player, options, ready) {
-    _classCallCheck(this, Component);
+    // Make the class extendable
+    subObj.extend = Component.extend;
 
-    // The component might be the player itself and we can't pass `this` to super
-    if (!player && this.play) {
-      this.player_ = player = this; // eslint-disable-line
-    } else {
-        this.player_ = player;
+    // Extend subObj's prototype with functions and other properties from props
+    for (var name in props) {
+      if (props.hasOwnProperty(name)) {
+        subObj.prototype[name] = props[name];
       }
+    }
 
-    // Make a copy of prototype.options_ to protect against overriding defaults
-    this.options_ = _utilsMergeOptionsJs2['default']({}, this.options_);
-
-    // Updated options with supplied options
-    options = this.options_ = _utilsMergeOptionsJs2['default'](this.options_, options);
-
-    // Get ID from options or options element if one is supplied
-    this.id_ = options.id || options.el && options.el.id;
+    return subObj;
+  };
 
-    // If there was no ID from the options, generate one
-    if (!this.id_) {
-      // Don't require the player ID function in the case of mock players
-      var id = player && player.id && player.id() || 'no_player';
+  return Component;
+}();
 
-      this.id_ = id + '_component_' + Guid.newGUID();
-    }
+Component.registerComponent('Component', Component);
+exports['default'] = Component;
 
-    this.name_ = options.name || null;
+},{"80":80,"81":81,"82":82,"84":84,"85":85,"86":86,"89":89,"93":93}],6:[function(_dereq_,module,exports){
+'use strict';
 
-    // Create element if one wasn't provided in options
-    if (options.el) {
-      this.el_ = options.el;
-    } else if (options.createEl !== false) {
-      this.el_ = this.createEl();
-    }
+exports.__esModule = true;
 
-    this.children_ = [];
-    this.childIndex_ = {};
-    this.childNameIndex_ = {};
+var _trackButton = _dereq_(36);
 
-    // Add any child components in options
-    if (options.initChildren !== false) {
-      this.initChildren();
-    }
+var _trackButton2 = _interopRequireDefault(_trackButton);
 
-    this.ready(ready);
-    // Don't want to trigger ready here or it will before init is actually
-    // finished for all children that run this constructor
+var _component = _dereq_(5);
 
-    if (options.repor