TheDJ has uploaded a new change for review. https://gerrit.wikimedia.org/r/258359
Change subject: Make controlbar layout depend on the player width ...................................................................... Make controlbar layout depend on the player width Uses a new videojs plugin to apply layout classes depending on the width of the player. As fewer width is available, controls like time remaining, fullscreen or progress are dropped from the layout. Bug: T119049 Change-Id: I60307f1ccd5300359920c4ce6e63144eece2e75d --- M Gruntfile.js M TimedMediaHandler.hooks.php M package.json M resources/ext.tmh.player.js A resources/videojs-responsive-layout/videojs-responsive-layout.js A resources/videojs-responsive-layout/videojs-responsive-layout.min.js 6 files changed, 515 insertions(+), 3 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/TimedMediaHandler refs/changes/59/258359/1 diff --git a/Gruntfile.js b/Gruntfile.js index 04ffaa2..2f60a17 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -35,7 +35,7 @@ }, exec: { 'npm-update-videojs': { - cmd: 'npm update video.js videojs-resolution-switcher', + cmd: 'npm update video.js videojs-resolution-switcher videojs-responsive-layout', callback: function ( error, stdout, stderr ) { grunt.log.write( stdout ); if ( stderr ) { @@ -68,6 +68,12 @@ cwd: 'node_modules/videojs-resolution-switcher/lib/', src: [ '**' ], dest: 'resources/videojs-resolution-switcher/' + }, + 'videojs-responsive-layout': { + expand: true, + cwd: 'node_modules/videojs-responsive-layout/dist/', + src: [ '**' ], + dest: 'resources/videojs-responsive-layout/' } }, patch: { @@ -83,7 +89,7 @@ } } ); - grunt.registerTask( 'update-videojs', [ 'exec:npm-update-videojs', 'copy:video.js', 'copy:videojs-resolution-switcher', 'patch:video.js' ] ); + grunt.registerTask( 'update-videojs', [ 'exec:npm-update-videojs', 'copy:video.js', 'copy:videojs-resolution-switcher', 'copy:videojs-responsive-layout', 'patch:video.js' ] ); grunt.registerTask( 'test', [ 'jshint', 'jscs', 'jsonlint', 'banana' ] ); grunt.registerTask( 'default', 'test' ); }; diff --git a/TimedMediaHandler.hooks.php b/TimedMediaHandler.hooks.php index e234930..3174622 100644 --- a/TimedMediaHandler.hooks.php +++ b/TimedMediaHandler.hooks.php @@ -210,12 +210,20 @@ 'ext.tmh.video-js', ), ), + 'ext.tmh.videojs-responsive-layout' => $baseExtensionResource + array( + 'scripts' => 'resources/videojs-responsive-layout/videojs-responsive-layout.js', + 'targets' => array( 'mobile', 'desktop' ), + 'dependencies' => array( + 'ext.tmh.video-js', + ), + ), 'ext.tmh.player' => $baseExtensionResource + array( 'scripts' => 'resources/ext.tmh.player.js', 'targets' => array( 'mobile', 'desktop' ), 'dependencies' => array( 'ext.tmh.video-js', 'ext.tmh.videojs-resolution-switcher', + 'ext.tmh.videojs-responsive-layout', // 'ext.tmh.videojs-offset', ), ), diff --git a/package.json b/package.json index cb4b116..b620925 100644 --- a/package.json +++ b/package.json @@ -16,5 +16,9 @@ "jscs-preset-wikimedia": "~1.0.0", "video.js": "^5.3.0", "videojs-resolution-switcher": "^0.2.2" + }, + "dependencies": { + "videojs-resolution-switcher": "^0.2.2", + "videojs-responsive-layout": "^1.0.0" } } diff --git a/resources/ext.tmh.player.js b/resources/ext.tmh.player.js index 8766b7a..53bed57 100755 --- a/resources/ext.tmh.player.js +++ b/resources/ext.tmh.player.js @@ -16,7 +16,8 @@ plugins: { videoJsResolutionSwitcher: { sourceOrder: true - } + }, + responsiveLayout: {} } }; diff --git a/resources/videojs-responsive-layout/videojs-responsive-layout.js b/resources/videojs-responsive-layout/videojs-responsive-layout.js new file mode 100644 index 0000000..10178a3 --- /dev/null +++ b/resources/videojs-responsive-layout/videojs-responsive-layout.js @@ -0,0 +1,486 @@ +/** + * videojs-responsive-layout + * @version 1.0.0 + * @copyright 2015 Derk-Jan Hartman + * @license (MIT OR Apache-2.0) + */ +(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.videojsResponsiveLayout = 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(require,module,exports){ + +},{}],2:[function(require,module,exports){ +var throttle = require('./throttle'); + +/** + * Debounce execution of a function. Debouncing, unlike throttling, + * guarantees that a function is only executed a single time, either at the + * very beginning of a series of calls, or at the very end. + * + * @param {Number} delay A zero-or-greater delay in milliseconds. For event callbacks, values around 100 or 250 (or even higher) are most useful. + * @param {Boolean} atBegin Optional, defaults to false. If atBegin is false or unspecified, callback will only be executed `delay` milliseconds + * after the last debounced-function call. If atBegin is true, callback will be executed only at the first debounced-function call. + * (After the throttled-function has not been called for `delay` milliseconds, the internal counter is reset). + * @param {Function} callback A function to be executed after delay milliseconds. The `this` context and all arguments are passed through, as-is, + * to `callback` when the debounced-function is executed. + * + * @return {Function} A new, debounced function. + */ +module.exports = function ( delay, atBegin, callback ) { + return callback === undefined ? throttle(delay, atBegin, false) : throttle(delay, callback, atBegin !== false); +}; + +},{"./throttle":4}],3:[function(require,module,exports){ +module.exports = { + throttle: require('./throttle'), + debounce: require('./debounce') +}; + +},{"./debounce":2,"./throttle":4}],4:[function(require,module,exports){ +var $ = require('jquery'); + +/** + * Throttle execution of a function. Especially useful for rate limiting + * execution of handlers on events like resize and scroll. + * + * @param {Number} delay A zero-or-greater delay in milliseconds. For event callbacks, values around 100 or 250 (or even higher) are most useful. + * @param {Boolean} noTrailing Optional, defaults to false. If noTrailing is true, callback will only execute every `delay` milliseconds while the + * throttled-function is being called. If noTrailing is false or unspecified, callback will be executed one final time + * after the last throttled-function call. (After the throttled-function has not been called for `delay` milliseconds, + * the internal counter is reset) + * @param {Function} callback A function to be executed after delay milliseconds. The `this` context and all arguments are passed through, as-is, + * to `callback` when the throttled-function is executed. + * @param {Boolean} debounceMode If `debounceMode` is true (at begin), schedule `clear` to execute after `delay` ms. If `debounceMode` is false (at end), + * schedule `callback` to execute after `delay` ms. + * + * @return {Function} A new, throttled, function. + */ +module.exports = function ( delay, noTrailing, callback, debounceMode ) { + + // After wrapper has stopped being called, this timeout ensures that + // `callback` is executed at the proper times in `throttle` and `end` + // debounce modes. + var timeoutID; + + // Keep track of the last time `callback` was executed. + var lastExec = 0; + + // `noTrailing` defaults to falsy. + if ( typeof(noTrailing) !== 'boolean' ) { + debounceMode = callback; + callback = noTrailing; + noTrailing = undefined; + } + + // The `wrapper` function encapsulates all of the throttling / debouncing + // functionality and when executed will limit the rate at which `callback` + // is executed. + function wrapper () { + + var self = this; + var elapsed = Number(new Date()) - lastExec; + var args = arguments; + + // Execute `callback` and update the `lastExec` timestamp. + function exec () { + lastExec = Number(new Date()); + callback.apply(self, args); + } + + // If `debounceMode` is true (at begin) this is used to clear the flag + // to allow future `callback` executions. + function clear () { + timeoutID = undefined; + } + + if ( debounceMode && !timeoutID ) { + // Since `wrapper` is being called for the first time and + // `debounceMode` is true (at begin), execute `callback`. + exec(); + } + + // Clear any existing timeout. + if ( timeoutID ) { + clearTimeout(timeoutID); + } + + if ( debounceMode === undefined && elapsed > delay ) { + // In throttle mode, if `delay` time has been exceeded, execute + // `callback`. + exec(); + + } else if ( noTrailing !== true ) { + // In trailing throttle mode, since `delay` time has not been + // exceeded, schedule `callback` to execute `delay` ms after most + // recent execution. + // + // If `debounceMode` is true (at begin), schedule `clear` to execute + // after `delay` ms. + // + // If `debounceMode` is false (at end), schedule `callback` to + // execute after `delay` ms. + timeoutID = setTimeout(debounceMode ? clear : exec, debounceMode === undefined ? delay - elapsed : delay); + } + + } + + // Set the guid of `wrapper` function to the same of original callback, so + // it can be removed in jQuery 1.4+ .unbind or .die by using the original + // callback as a reference. + if ( $ && $.guid ) { + wrapper.guid = callback.guid = callback.guid || $.guid++; + } + + // Return the wrapper function. + return wrapper; + +}; + +},{"jquery":1}],5:[function(require,module,exports){ +(function (global){ +/* jshint esnext:true */ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +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'); } } + +var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null); + +var _videoJs2 = _interopRequireDefault(_videoJs); + +var debounce = require('throttle-debounce').debounce; + +// Default options for the plugin. +var defaults = { + debounceDelay: 400 +}; + +/** + * Retrieve the outerWidth of an element, including margins + * + * @function getElementOuterWidth + * @param {Element} el to measure + * @return {number} the width of the element in pixels + */ +var _getElementOuterWidth = function _getElementOuterWidth(el) { + var width = el.offsetWidth; + var style = getComputedStyle(el); + + width += parseInt(style.marginLeft, 10) + parseInt(style.marginRight, 10); + return width; +}; + +/** + * Retrieve the width an element + * + * @function getElementWidth + * @param {Element} el to measure + * @return {number} the width of the element in pixels + */ +var _getElementWidth = function _getElementWidth(el) { + return parseInt(getComputedStyle(el).width, 10); +}; + +/** + * Check if an element is currently visible. + * + * Use this to filter on elements that should be taken into account during calculations. + * + * @function isElementVisible + * @param {Element} el to test + * @return {boolean} true if el is visible + */ +var _isElementVisible = function _isElementVisible(el) { + return el.offsetWidth > 0 || el.offsetHeight > 0; +}; + +/** + * Set a video.js layout class on an element + * + * A layout can be: vjs-layout-tiny, vjs-layout-x-small, vjs-layout-small. + * Anything else will reset back to defaults + * + * @function setVideoJsLayout + * @param {Player} player to apply the layout to + * @param {string} layoutClass name of the class to be set + * @return {boolean} true if el is visible + */ +var setVideoJsLayout = function setVideoJsLayout(player, layoutClass) { + var el = player.el(); + + if (layoutClass === 'vjs-layout-tiny') { + _videoJs2['default'].addClass(el, 'vjs-layout-tiny'); + _videoJs2['default'].removeClass(el, 'vjs-layout-small'); + _videoJs2['default'].removeClass(el, 'vjs-layout-x-small'); + } else if (layoutClass === 'vjs-layout-x-small') { + _videoJs2['default'].addClass(el, 'vjs-layout-x-small'); + _videoJs2['default'].removeClass(el, 'vjs-layout-tiny'); + _videoJs2['default'].removeClass(el, 'vjs-layout-small'); + } else if (layoutClass === 'vjs-layout-small') { + _videoJs2['default'].addClass(el, 'vjs-layout-small'); + _videoJs2['default'].removeClass(el, 'vjs-layout-tiny'); + _videoJs2['default'].removeClass(el, 'vjs-layout-x-small'); + } else { + _videoJs2['default'].removeClass(el, 'vjs-layout-tiny'); + _videoJs2['default'].removeClass(el, 'vjs-layout-x-small'); + _videoJs2['default'].removeClass(el, 'vjs-layout-small'); + } +}; + +var makeBigger = function makeBigger(player) { + var el = player.el(); + + if (_videoJs2['default'].hasClass(el, 'vjs-layout-tiny')) { + setVideoJsLayout(player, 'vjs-layout-x-small'); + } else if (_videoJs2['default'].hasClass(el, 'vjs-layout-x-small')) { + setVideoJsLayout(player, 'vjs-layout-small'); + } else if (_videoJs2['default'].hasClass(el, 'vjs-layout-small')) { + setVideoJsLayout(player, 'default'); + } else { + return; + } + player.trigger('resize'); +}; + +var makeSmaller = function makeSmaller(player) { + var el = player.el(); + + if (_videoJs2['default'].hasClass(el, 'vjs-layout-tiny')) { + return; + } else if (_videoJs2['default'].hasClass(el, 'vjs-layout-x-small')) { + setVideoJsLayout(player, 'vjs-layout-tiny'); + } else if (_videoJs2['default'].hasClass(el, 'vjs-layout-small')) { + setVideoJsLayout(player, 'vjs-layout-x-small'); + } else { + setVideoJsLayout(player, 'vjs-layout-small'); + } + player.trigger('resize'); +}; + +var updateVideoJsLayout = function updateVideoJsLayout(layouter, playerWidth, controlBarWidth, controlWidth) { + var progressWidth = _getElementOuterWidth(layouter.el.querySelectorAll('.vjs-progress-control')[0]); + + if (controlBarWidth > playerWidth) { + makeSmaller(layouter.player); + } else if ((_videoJs2['default'].hasClass(layouter.el, 'vjs-layout-x-small') || _videoJs2['default'].hasClass(layouter.el, 'vjs-layout-small')) && + // progressbar more that twice it's default size + progressWidth > controlWidth * 2) { + makeBigger(layouter.player); + } else if (playerWidth > controlBarWidth + controlWidth && !_videoJs2['default'].hasClass(layouter.el, 'vjs-layout-x-small') && !_videoJs2['default'].hasClass(layouter.el, 'vjs-layout-small')) { + makeBigger(layouter.player); + } +}; + +var dimensionsCheck = function dimensionsCheck() { + if (!this.el) { + return; + } + var playerWidth = this.getPlayerWidth(); + var controlWidth = this.getControlWidth(); + var controlBarWidth = this.getControlBarWidth(); + + if (this.options.updateVideoJsLayout) { + this.options.updateVideoJsLayout(this, playerWidth, controlBarWidth, controlWidth); + } else { + updateVideoJsLayout(this, playerWidth, controlBarWidth, controlWidth); + } +}; + +var installStylesheet = function installStylesheet() { + var style = document.getElementById('vjs-responsive-layout'); + + if (!style) { + var styleRule = '\n .vjs-responsive-layout .vjs-progress-control {\n min-width: 4em;\n }\n '; + var head = document.getElementsByTagName('head')[0]; + + style = document.createElement('style'); + style.id = 'vjs-responsive-layout'; + + if (style.styleSheet) { + style.styleSheet.cssText = styleRule; + } else { + style.textContent = styleRule; + } + head.insertBefore(style, head.firstChild); + } +}; + +var Layouter = (function () { + function Layouter(player, options) { + _classCallCheck(this, Layouter); + + this.player_ = player; + this.options_ = options; + this.debouncedCheckSize_ = debounce(options.debounceDelay, dimensionsCheck); + } + + /** + * A video.js plugin. + * + * In the plugin function, the value of `this` is a video.js `Player` + * instance. You cannot rely on the player being in a "ready" state here, + * depending on how the plugin is invoked. This may or may not be important + * to you; if not, remove the wait for "ready"! + * + * @function responsiveLayout + * @param {Object} [options={}] + * An object of options left to the plugin author to define. + */ + + _createClass(Layouter, [{ + key: 'ready', + value: function ready() { + var _this = this; + + installStylesheet(); + this.player.addClass('vjs-responsive-layout'); + + this.windowResizeListener_ = window.addEventListener('resize', function () { + return _this.debouncedCheckSize_(); + }); + + this.player.on(['play', 'resize'], function () { + return _this.debouncedCheckSize_(); + }); + this.player.on('dispose', function () { + window.removeEventListener('resize', this.windowResizeListener_); + }); + + // Let's do the first measure + this.player.trigger('resize'); + } + + /** + * Retrieve player to which this Layouter object belongs + * + * @property player + * @return {number} the width of the controlbar in pixels + */ + }, { + key: 'getControlWidth', + + /** + * Retrieve current width of a control in the video.js controlbar + * + * This function relies on the presence of the play control. If you + * mess with it's visibility, things likely will break :) + * + * @function getControlBarWidth + * @return {number} the width of the controlbar in pixels + */ + value: function getControlWidth() { + return _getElementOuterWidth(this.el.querySelectorAll('.vjs-play-control')[0]); + } + + /** + * Retrieve current width of the video.js controlbar + * + * @function getControlBarWidth + * @return {number} the width of the controlbar in pixels + */ + }, { + key: 'getControlBarWidth', + value: function getControlBarWidth() { + var controlBarWidth = 0; + var cbElements = this.el.querySelectorAll('.vjs-control-bar > *'); + + Array.from(cbElements).forEach(function (el) { + if (_isElementVisible(el)) { + controlBarWidth += _getElementOuterWidth(el); + } + }); + return controlBarWidth; + } + + /** + * Retrieve current width of the video.js player element + * + * @function getPlayerWidth + * @return {number} the width of the player in pixels + */ + }, { + key: 'getPlayerWidth', + value: function getPlayerWidth() { + return _getElementWidth(this.el); + } + + /** + * Retrieve the outerWidth of an element, including margins + * + * @function outerWidth + * @param {Element} el to measure + * @return {number} the width of the element in pixels + */ + }, { + key: 'player', + get: function get() { + return this.player_; + } + }, { + key: 'el', + get: function get() { + return this.player_.el(); + } + }, { + key: 'options', + get: function get() { + return this.options_; + } + }], [{ + key: 'getElementOuterWidth', + value: function getElementOuterWidth(el) { + return _getElementOuterWidth(el); + } + + /** + * Retrieve the width an element + * + * @function getElementWidth + * @param {Element} el to measure + * @return {number} the width of the element in pixels + */ + }, { + key: 'getElementWidth', + value: function getElementWidth(el) { + return _getElementWidth(el); + } + + /** + * Check if an element is currently visible. + * + * Use this to filter on elements that should be taken into account during calculations. + * + * @function isElementVisible + * @param {Element} el to test + * @return {boolean} true if el is visible + */ + }, { + key: 'isElementVisible', + value: function isElementVisible(el) { + return _isElementVisible(el); + } + }]); + + return Layouter; +})(); + +var responsiveLayout = function responsiveLayout(options) { + var layout = new Layouter(this, _videoJs2['default'].mergeOptions(defaults, options)); + + this.ready(function () { + layout.ready(); + }); +}; + +// Register the plugin with video.js. +_videoJs2['default'].plugin('responsiveLayout', responsiveLayout); + +exports['default'] = responsiveLayout; +module.exports = exports['default']; +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"throttle-debounce":3}]},{},[5])(5) +}); \ No newline at end of file diff --git a/resources/videojs-responsive-layout/videojs-responsive-layout.min.js b/resources/videojs-responsive-layout/videojs-responsive-layout.min.js new file mode 100644 index 0000000..f80aba3 --- /dev/null +++ b/resources/videojs-responsive-layout/videojs-responsive-layout.min.js @@ -0,0 +1,7 @@ +/** + * videojs-responsive-layout + * @version 1.0.0 + * @copyright 2015 Derk-Jan Hartman + * @license (MIT OR Apache-2.0) + */ +!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.videojsResponsiveLayout=e()}}(function(){return function e(t,n,l){function o(a,r){if(!n[a]){if(!t[a]){var u="function"==typeof require&&require;if(!r&&u)return u(a,!0);if(s)return s(a,!0);var i=new Error("Cannot find module '"+a+"'");throw i.code="MODULE_NOT_FOUND",i}var f=n[a]={exports:{}};t[a][0].call(f.exports,function(e){var n=t[a][1][e];return o(n?n:e)},f,f.exports,e,t,n,l)}return n[a].exports}for(var s="function"==typeof require&&require,a=0;a<l.length;a++)o(l[a]);return o}({1:[function(e,t,n){},{}],2:[function(e,t,n){var l=e("./throttle");t.exports=function(e,t,n){return void 0===n?l(e,t,!1):l(e,n,t!==!1)}},{"./throttle":4}],3:[function(e,t,n){t.exports={throttle:e("./throttle"),debounce:e("./debounce")}},{"./debounce":2,"./throttle":4}],4:[function(e,t,n){var l=e("jquery");t.exports=function(e,t,n,o){function s(){function l(){r=Number(new Date),n.apply(u,f)}function s(){a=void 0}var u=this,i=Number(new Date)-r,f=arguments;o&&!a&&l(),a&&clearTimeout(a),void 0===o&&i>e?l():t!==!0&&(a=setTimeout(o?s:l,void 0===o?e-i:e))}var a,r=0;return"boolean"!=typeof t&&(o=n,n=t,t=void 0),l&&l.guid&&(s.guid=n.guid=n.guid||l.guid++),s}},{jquery:1}],5:[function(e,t,n){(function(l){"use strict";function o(e){return e&&e.__esModule?e:{"default":e}}function s(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var a=function(){function e(e,t){for(var n=0;n<t.length;n++){var l=t[n];l.enumerable=l.enumerable||!1,l.configurable=!0,"value"in l&&(l.writable=!0),Object.defineProperty(e,l.key,l)}}return function(t,n,l){return n&&e(t.prototype,n),l&&e(t,l),t}}(),r="undefined"!=typeof window?window.videojs:"undefined"!=typeof l?l.videojs:null,u=o(r),i=e("throttle-debounce").debounce,f={debounceDelay:400},d=function(e){var t=e.offsetWidth,n=getComputedStyle(e);return t+=parseInt(n.marginLeft,10)+parseInt(n.marginRight,10)},y=function(e){return parseInt(getComputedStyle(e).width,10)},v=function(e){return e.offsetWidth>0||e.offsetHeight>0},c=function(e,t){var n=e.el();"vjs-layout-tiny"===t?(u["default"].addClass(n,"vjs-layout-tiny"),u["default"].removeClass(n,"vjs-layout-small"),u["default"].removeClass(n,"vjs-layout-x-small")):"vjs-layout-x-small"===t?(u["default"].addClass(n,"vjs-layout-x-small"),u["default"].removeClass(n,"vjs-layout-tiny"),u["default"].removeClass(n,"vjs-layout-small")):"vjs-layout-small"===t?(u["default"].addClass(n,"vjs-layout-small"),u["default"].removeClass(n,"vjs-layout-tiny"),u["default"].removeClass(n,"vjs-layout-x-small")):(u["default"].removeClass(n,"vjs-layout-tiny"),u["default"].removeClass(n,"vjs-layout-x-small"),u["default"].removeClass(n,"vjs-layout-small"))},h=function(e){var t=e.el();if(u["default"].hasClass(t,"vjs-layout-tiny"))c(e,"vjs-layout-x-small");else if(u["default"].hasClass(t,"vjs-layout-x-small"))c(e,"vjs-layout-small");else{if(!u["default"].hasClass(t,"vjs-layout-small"))return;c(e,"default")}e.trigger("resize")},p=function(e){var t=e.el();u["default"].hasClass(t,"vjs-layout-tiny")||(u["default"].hasClass(t,"vjs-layout-x-small")?c(e,"vjs-layout-tiny"):u["default"].hasClass(t,"vjs-layout-small")?c(e,"vjs-layout-x-small"):c(e,"vjs-layout-small"),e.trigger("resize"))},m=function(e,t,n,l){var o=d(e.el.querySelectorAll(".vjs-progress-control")[0]);n>t?p(e.player):(u["default"].hasClass(e.el,"vjs-layout-x-small")||u["default"].hasClass(e.el,"vjs-layout-small"))&&o>2*l?h(e.player):t>n+l&&!u["default"].hasClass(e.el,"vjs-layout-x-small")&&!u["default"].hasClass(e.el,"vjs-layout-small")&&h(e.player)},j=function(){if(this.el){var e=this.getPlayerWidth(),t=this.getControlWidth(),n=this.getControlBarWidth();this.options.updateVideoJsLayout?this.options.updateVideoJsLayout(this,e,n,t):m(this,e,n,t)}},g=function(){var e=document.getElementById("vjs-responsive-layout");if(!e){var t="\n .vjs-responsive-layout .vjs-progress-control {\n min-width: 4em;\n }\n ",n=document.getElementsByTagName("head")[0];e=document.createElement("style"),e.id="vjs-responsive-layout",e.styleSheet?e.styleSheet.cssText=t:e.textContent=t,n.insertBefore(e,n.firstChild)}},C=function(){function e(t,n){s(this,e),this.player_=t,this.options_=n,this.debouncedCheckSize_=i(n.debounceDelay,j)}return a(e,[{key:"ready",value:function(){var e=this;g(),this.player.addClass("vjs-responsive-layout"),this.windowResizeListener_=window.addEventListener("resize",function(){return e.debouncedCheckSize_()}),this.player.on(["play","resize"],function(){return e.debouncedCheckSize_()}),this.player.on("dispose",function(){window.removeEventListener("resize",this.windowResizeListener_)}),this.player.trigger("resize")}},{key:"getControlWidth",value:function(){return d(this.el.querySelectorAll(".vjs-play-control")[0])}},{key:"getControlBarWidth",value:function(){var e=0,t=this.el.querySelectorAll(".vjs-control-bar > *");return Array.from(t).forEach(function(t){v(t)&&(e+=d(t))}),e}},{key:"getPlayerWidth",value:function(){return y(this.el)}},{key:"player",get:function(){return this.player_}},{key:"el",get:function(){return this.player_.el()}},{key:"options",get:function(){return this.options_}}],[{key:"getElementOuterWidth",value:function(e){return d(e)}},{key:"getElementWidth",value:function(e){return y(e)}},{key:"isElementVisible",value:function(e){return v(e)}}]),e}(),w=function(e){var t=new C(this,u["default"].mergeOptions(f,e));this.ready(function(){t.ready()})};u["default"].plugin("responsiveLayout",w),n["default"]=w,t.exports=n["default"]}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"throttle-debounce":3}]},{},[5])(5)}); \ No newline at end of file -- To view, visit https://gerrit.wikimedia.org/r/258359 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I60307f1ccd5300359920c4ce6e63144eece2e75d Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/TimedMediaHandler Gerrit-Branch: master Gerrit-Owner: TheDJ <hartman.w...@gmail.com> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits