TheDJ has uploaded a new change for review. https://gerrit.wikimedia.org/r/260963
Change subject: Add the videojs-ogvjs Tech module ...................................................................... Add the videojs-ogvjs Tech module This enables video.js playback using ogv.js on Safari and older IE browsers Bug: T119048 Change-Id: Ie511c145b9d16be1d234645e69b8c81665448ef9 --- M Gruntfile.js M TimedMediaHandler.hooks.php M package.json M resources/ext.tmh.player.js A resources/videojs-ogvjs/videojs-ogvjs.js A resources/videojs-ogvjs/videojs-ogvjs.min.js 6 files changed, 814 insertions(+), 21 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/TimedMediaHandler refs/changes/63/260963/1 diff --git a/Gruntfile.js b/Gruntfile.js index 04ffaa2..e978e49 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-ogvjs', callback: function ( error, stdout, stderr ) { grunt.log.write( stdout ); if ( stderr ) { @@ -63,6 +63,12 @@ ], dest: 'resources/videojs/' }, + 'videojs-ogvjs': { + expand: true, + cwd: 'node_modules/videojs-ogvjs/dist/', + src: [ '**' ], + dest: 'resources/videojs-ogvjs/' + }, 'videojs-resolution-switcher': { expand: true, cwd: 'node_modules/videojs-resolution-switcher/lib/', @@ -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-ogvjs', '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..d7ba13f 100644 --- a/TimedMediaHandler.hooks.php +++ b/TimedMediaHandler.hooks.php @@ -93,7 +93,22 @@ ), 'ext.tmh.TimedTextSelector' => $baseExtensionResource + array( 'scripts' => 'resources/ext.tmh.TimedTextSelector.js', - ) + ), + // Add OgvJs-related modules for Safari/IE/Edge Ogg playback + 'ext.tmh.OgvJsSupport' => $baseExtensionResource + array( + 'scripts' => array( + 'MwEmbedModules/EmbedPlayer/binPlayers/ogv.js/ogv-support.js', + 'resources/ext.tmh.OgvJsSupport.js', + ), + 'targets' => array( 'mobile', 'desktop' ), + ), + 'ext.tmh.OgvJs' => $baseExtensionResource + array( + 'scripts' => array( + 'MwEmbedModules/EmbedPlayer/binPlayers/ogv.js/ogv.js', + ), + 'dependencies' => 'ext.tmh.OgvJsSupport', + 'targets' => array( 'mobile', 'desktop' ), + ), ); if ( $wgTmhWebPlayer === 'mwembed' ) { @@ -144,23 +159,6 @@ 'position' => 'top', ), ); - // Add OgvJs-related modules for Safari/IE/Edge Ogg playback - $wgResourceModules += array( - 'ext.tmh.OgvJsSupport' => $baseExtensionResource + array( - 'scripts' => array( - 'MwEmbedModules/EmbedPlayer/binPlayers/ogv.js/ogv-support.js', - 'resources/ext.tmh.OgvJsSupport.js', - ), - 'targets' => array( 'mobile', 'desktop' ), - ), - 'ext.tmh.OgvJs' => $baseExtensionResource + array( - 'scripts' => array( - 'MwEmbedModules/EmbedPlayer/binPlayers/ogv.js/ogv.js', - ), - 'dependencies' => 'ext.tmh.OgvJsSupport', - 'targets' => array( 'mobile', 'desktop' ), - ), - ); } elseif ( $wgTmhWebPlayer === 'videojs' ) { $wgResourceModules += array( 'ext.tmh.video-js' => $baseExtensionResource + array( @@ -195,6 +193,14 @@ 'zh-TW' => 'resources/videojs/lang/zh-TW.js', ), ), + 'ext.tmh.videojs-ogvjs' => $baseExtensionResource + array( + 'scripts' => 'resources/videojs-ogvjs/videojs-ogvjs.js', + 'targets' => array( 'mobile', 'desktop' ), + 'dependencies' => array( + 'ext.tmh.video-js', + 'ext.tmh.OgvJs', + ), + ), // 'ext.tmh.videojs-offset' => $baseExtensionResource + array( // 'scripts' => 'resources/videojs-offset/videojs-offset.js', // 'targets' => array( 'mobile', 'desktop' ), @@ -215,6 +221,7 @@ 'targets' => array( 'mobile', 'desktop' ), 'dependencies' => array( 'ext.tmh.video-js', + 'ext.tmh.videojs-ogvjs', 'ext.tmh.videojs-resolution-switcher', // 'ext.tmh.videojs-offset', ), diff --git a/package.json b/package.json index d1282b1..21516b1 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "grunt-patch": "^0.1.7", "jscs-preset-wikimedia": "~1.0.0", "video.js": "^5.3.0", + "videojs-ogvjs": "^1.0.6", "videojs-resolution-switcher": "^0.2.2" } } diff --git a/resources/ext.tmh.player.js b/resources/ext.tmh.player.js index 8766b7a..7af02b7 100755 --- a/resources/ext.tmh.player.js +++ b/resources/ext.tmh.player.js @@ -12,11 +12,14 @@ inline: false } }, - techOrder: [ 'html5' ], + techOrder: [ 'html5', 'ogvjs' ], plugins: { videoJsResolutionSwitcher: { sourceOrder: true } + }, + ogvjs: { + base: mw.OgvJsSupport.basePath() } }; diff --git a/resources/videojs-ogvjs/videojs-ogvjs.js b/resources/videojs-ogvjs/videojs-ogvjs.js new file mode 100644 index 0000000..38d32dd --- /dev/null +++ b/resources/videojs-ogvjs/videojs-ogvjs.js @@ -0,0 +1,769 @@ +/** + * videojs-ogvjs + * @version 1.0.6 + * @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.videojsOgvjs = 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){ +(function (global){ +'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; }; })(); + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +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 _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null); + +var _videoJs2 = _interopRequireDefault(_videoJs); + +var _OGVCompat = (typeof window !== "undefined" ? window['OGVCompat'] : typeof global !== "undefined" ? global['OGVCompat'] : null); + +var _OGVCompat2 = _interopRequireDefault(_OGVCompat); + +var _OGVLoader = (typeof window !== "undefined" ? window['OGVLoader'] : typeof global !== "undefined" ? global['OGVLoader'] : null); + +var _OGVLoader2 = _interopRequireDefault(_OGVLoader); + +var _OGVPlayer = (typeof window !== "undefined" ? window['OGVPlayer'] : typeof global !== "undefined" ? global['OGVPlayer'] : null); + +var _OGVPlayer2 = _interopRequireDefault(_OGVPlayer); + +var Tech = _videoJs2['default'].getComponent('Tech'); + +/** + * Ogvjs Media Controller - Wrapper for Ogvjs Media API + * + * @param {Object=} options Object of option names and values + * @param {Function=} ready Ready callback function + * @extends Tech + * @class Ogvjs + */ + +var Ogvjs = (function (_Tech) { + _inherits(Ogvjs, _Tech); + + function Ogvjs(options, ready) { + _classCallCheck(this, Ogvjs); + + _get(Object.getPrototypeOf(Ogvjs.prototype), 'constructor', this).call(this, options, ready); + + this.triggerReady(); + } + + /* + * Check if Ogvjs video is supported by this browser/device + * + * @return {Boolean} + */ + + /** + * Dispose of Ogvjs media element + * + * @method dispose + */ + + _createClass(Ogvjs, [{ + key: 'dispose', + value: function dispose() { + // Ogvjs.disposeMediaElement(this.el_); + _get(Object.getPrototypeOf(Ogvjs.prototype), 'dispose', this).call(this); + } + + /** + * Create the component's DOM element + * + * @return {Element} + * @method createEl + */ + }, { + key: 'createEl', + value: function createEl() { + var options = this.options_; + + if (options.base) { + _OGVLoader2['default'].base = options.base; + } else { + throw new Error('Please specify the base for the ogv.js library'); + } + + var el = new _OGVPlayer2['default'](options); + + // simulate timeupdate events, needed for subtitles + // @todo switch this to native timeupdate event when available upstream + this.lastTime = 0; + el.addEventListener('framecallback', this.onFrameUpdate.bind(this)); + el.src = this.options_.source.src; + el.className += ' vjs-tech'; + + return el; + } + }, { + key: 'onFrameUpdate', + value: function onFrameUpdate(event) { + var timeupdateInterval = 0.25; + var now = this.el_ ? this.el_.currentTime : this.lastTime; + + // Don't spam time updates on every frame + if (Math.abs(now - this.lastTime) >= timeupdateInterval) { + this.lastTime = now; + this.trigger('timeupdate'); + this.trigger('durationchange'); + } + } + + /** + * Play for Ogvjs tech + * + * @method play + */ + }, { + key: 'play', + value: function play() { + this.el_.play(); + } + + /** + * Pause for Ogvjs tech + * + * @method pause + */ + }, { + key: 'pause', + value: function pause() { + this.el_.pause(); + } + + /** + * Paused for Ogvjs tech + * + * @return {Boolean} + * @method paused + */ + }, { + key: 'paused', + value: function paused() { + return this.el_.paused; + } + + /** + * Get current time + * + * @return {Number} + * @method currentTime + */ + }, { + key: 'currentTime', + value: function currentTime() { + return this.el_.currentTime; + } + + /** + * Set current time + * + * @param {Number} seconds Current time of video + * @method setCurrentTime + */ + }, { + key: 'setCurrentTime', + value: function setCurrentTime(seconds) { + try { + this.el_.currentTime = seconds; + } catch (e) { + _videoJs2['default'].log(e, 'Video is not ready. (Video.js)'); + } + } + + /** + * Get duration + * + * @return {Number} + * @method duration + */ + }, { + key: 'duration', + value: function duration() { + return this.el_.duration || 0; + } + + /** + * Get a TimeRange object that represents the intersection + * of the time ranges for which the user agent has all + * relevant media + * + * @return {TimeRangeObject} + * @method buffered + */ + }, { + key: 'buffered', + value: function buffered() { + return this.el_.buffered; + } + + /** + * Get volume level + * + * @return {Number} + * @method volume + */ + }, { + key: 'volume', + value: function volume() { + return this.el_.volume ? this.el_.volume : 1; + } + + /** + * Set volume level + * + * @param {Number} percentAsDecimal Volume percent as a decimal + * @method setVolume + */ + }, { + key: 'setVolume', + value: function setVolume(percentAsDecimal) { + if (this.el_.volume) { + this.el_.volume = percentAsDecimal; + } + } + + /** + * Get if muted + * + * @return {Boolean} + * @method muted + */ + }, { + key: 'muted', + value: function muted() { + return this.el_.muted ? this.el_.muted : false; + } + + /** + * Set muted + * + * @param {Boolean} If player is to be muted or note + * @method setMuted + */ + }, { + key: 'setMuted', + value: function setMuted(muted) { + if (this.el_.muted) { + this.el_.muted = muted; + } + } + + /** + * Get player width + * + * @return {Number} + * @method width + */ + }, { + key: 'width', + value: function width() { + return this.el_.offsetWidth; + } + + /** + * Get player height + * + * @return {Number} + * @method height + */ + }, { + key: 'height', + value: function height() { + return this.el_.offsetHeight; + } + + /** + * Get if there is fullscreen support + * + * @return {Boolean} + * @method supportsFullScreen + */ + }, { + key: 'supportsFullScreen', + value: function supportsFullScreen() { + if (typeof this.el_.webkitEnterFullScreen === 'function') { + var userAgent = window.navigator.userAgent; + + // Seems to be broken in Chromium/Chrome && Safari in Leopard + if (/Android/.test(userAgent) || !/Chrome|Mac OS X 10.5/.test(userAgent)) { + return true; + } + } + return false; + } + + /** + * Request to enter fullscreen + * + * @method enterFullScreen + */ + }, { + key: 'enterFullScreen', + value: function enterFullScreen() { + var video = this.el_; + + if ('webkitDisplayingFullscreen' in video) { + this.one('webkitbeginfullscreen', function () { + this.one('webkitendfullscreen', function () { + this.trigger('fullscreenchange', { isFullscreen: false }); + }); + + this.trigger('fullscreenchange', { isFullscreen: true }); + }); + } + + if (video.paused && video.networkState <= video.HAVE_METADATA) { + // attempt to prime the video element for programmatic access + // this isn't necessary on the desktop but shouldn't hurt + this.el_.play(); + + // playing and pausing synchronously during the transition to fullscreen + // can get iOS ~6.1 devices into a play/pause loop + this.setTimeout(function () { + video.pause(); + video.webkitEnterFullScreen(); + }, 0); + } else { + video.webkitEnterFullScreen(); + } + } + + /** + * Request to exit fullscreen + * + * @method exitFullScreen + */ + }, { + key: 'exitFullScreen', + value: function exitFullScreen() { + this.el_.webkitExitFullScreen(); + } + + /** + * Get/set video + * + * @param {Object=} src Source object + * @return {Object} + * @method src + */ + }, { + key: 'src', + value: function src(_src) { + if (typeof _src === 'undefined') { + return this.el_.src; + } + // Setting src through `src` instead of `setSrc` will be deprecated + this.setSrc(_src); + } + + /** + * Set video + * + * @param {Object} src Source object + * @deprecated + * @method setSrc + */ + }, { + key: 'setSrc', + value: function setSrc(src) { + this.el_.src = src; + } + + /** + * Load media into player + * + * @method load + */ + }, { + key: 'load', + value: function load() { + this.el_.load(); + } + + /** + * Get current source + * + * @return {Object} + * @method currentSrc + */ + }, { + key: 'currentSrc', + value: function currentSrc() { + if (this.currentSource_) { + return this.currentSource_.src; + } + return this.el_.currentSrc; + } + + /** + * Get poster + * + * @return {String} + * @method poster + */ + }, { + key: 'poster', + value: function poster() { + return this.el_.poster; + } + + /** + * Set poster + * + * @param {String} val URL to poster image + * @method + */ + }, { + key: 'setPoster', + value: function setPoster(val) { + this.el_.poster = val; + } + + /** + * Get preload attribute + * + * @return {String} + * @method preload + */ + // preload() { return this.el_.preload; } + + /** + * Set preload attribute + * + * @param {String} val Value for preload attribute + * @method setPreload + */ + // setPreload(val) { this.el_.preload = val; } + + /** + * Get autoplay attribute + * + * @return {String} + * @method autoplay + */ + // autoplay() { return this.el_.autoplay; } + + /** + * Set autoplay attribute + * + * @param {String} val Value for preload attribute + * @method setAutoplay + */ + // setAutoplay(val) { this.el_.autoplay = val; } + + /** + * Get controls attribute + * + * @return {String} + * @method controls + */ + }, { + key: 'controls', + value: function controls() { + return this.el_.controls; + } + + /** + * Set controls attribute + * + * @param {String} val Value for controls attribute + * @method setControls + */ + }, { + key: 'setControls', + value: function setControls(val) { + this.el_.controls = !!val; + } + + /** + * Get loop attribute + * + * @return {String} + * @method loop + */ + // loop() { return this.el_.loop; } + + /** + * Set loop attribute + * + * @param {String} val Value for loop attribute + * @method setLoop + */ + // setLoop(val) { this.el_.loop = val; } + + /** + * Get error value + * + * @return {String} + * @method error + */ + }, { + key: 'error', + value: function error() { + return this.el_.error; + } + + /** + * Get whether or not the player is in the "seeking" state + * + * @return {Boolean} + * @method seeking + */ + }, { + key: 'seeking', + value: function seeking() { + return this.el_.seeking; + } + + /** + * Get a TimeRanges object that represents the + * ranges of the media resource to which it is possible + * for the user agent to seek. + * + * @return {TimeRangeObject} + * @method seekable + */ + }, { + key: 'seekable', + value: function seekable() { + return this.el_.seekable; + } + + /** + * Get if video ended + * + * @return {Boolean} + * @method ended + */ + }, { + key: 'ended', + value: function ended() { + return this.el_.ended; + } + + /** + * Get the value of the muted content attribute + * This attribute has no dynamic effect, it only + * controls the default state of the element + * + * @return {Boolean} + * @method defaultMuted + */ + // defaultMuted() { return this.el_.defaultMuted; } + + /** + * Get desired speed at which the media resource is to play + * + * @return {Number} + * @method playbackRate + */ + // playbackRate() { return this.el_.playbackRate; } + + /** + * Returns a TimeRanges object that represents the ranges of the + * media resource that the user agent has played. + * @return {TimeRangeObject} the range of points on the media + * timeline that has been reached through normal playback + * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-played + */ + // played() { return this.el_.played; } + + /** + * Set desired speed at which the media resource is to play + * + * @param {Number} val Speed at which the media resource is to play + * @method setPlaybackRate + */ + // setPlaybackRate(val) { this.el_.playbackRate = val; } + + /** + * Get the current state of network activity for the element, from + * the list below + * NETWORK_EMPTY (numeric value 0) + * NETWORK_IDLE (numeric value 1) + * NETWORK_LOADING (numeric value 2) + * NETWORK_NO_SOURCE (numeric value 3) + * + * @return {Number} + * @method networkState + */ + // networkState() { return this.el_.networkState; } + + /** + * Get a value that expresses the current state of the element + * with respect to rendering the current playback position, from + * the codes in the list below + * HAVE_NOTHING (numeric value 0) + * HAVE_METADATA (numeric value 1) + * HAVE_CURRENT_DATA (numeric value 2) + * HAVE_FUTURE_DATA (numeric value 3) + * HAVE_ENOUGH_DATA (numeric value 4) + * + * @return {Number} + * @method readyState + */ + // readyState() { return this.el_.readyState; } + + /** + * Get width of video + * + * @return {Number} + * @method videoWidth + */ + }, { + key: 'videoWidth', + value: function videoWidth() { + return this.el_.videoWidth; + } + + /** + * Get height of video + * + * @return {Number} + * @method videoHeight + */ + }, { + key: 'videoHeight', + value: function videoHeight() { + return this.el_.videoHeight; + } + }]); + + return Ogvjs; +})(Tech); + +Ogvjs.isSupported = function () { + return _OGVCompat2['default'].supported('OGVPlayer'); +}; + +/* + * Check if the tech can support the given source + * @param {Object} srcObj The source object + * @return {String} 'probably', 'maybe', or '' (empty string) + */ +Ogvjs.canPlaySource = function (srcObj) { + return srcObj.type.indexOf('/ogg') !== -1 ? 'maybe' : ''; +}; + +/* + * Check if the volume can be changed in this browser/device. + * Volume cannot be changed in a lot of mobile devices. + * Specifically, it can't be changed from 1 on iOS. + * + * @return {Boolean} + */ +Ogvjs.canControlVolume = function () { + return false; +}; + +/* + * Check if playbackRate is supported in this browser/device. + * + * @return {Number} [description] + */ +Ogvjs.canControlPlaybackRate = function () { + return false; +}; + +/* + * Check to see if native text tracks are supported by this browser/device + * + * @return {Boolean} + */ +Ogvjs.supportsNativeTextTracks = function () { + return false; +}; + +/** + * An array of events available on the Ogvjs tech. + * + * @private + * @type {Array} + */ +Ogvjs.Events = ['loadstart', 'suspend', 'abort', 'error', 'emptied', 'stalled', 'loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough', 'playing', 'waiting', 'seeking', 'seeked', 'ended', 'durationchange', 'timeupdate', 'progress', 'play', 'pause', 'ratechange', 'volumechange']; + +/* + * Set the tech's volume control support status + * + * @type {Boolean} + */ +Ogvjs.prototype.featuresVolumeControl = Ogvjs.canControlVolume(); + +/* + * Set the tech's playbackRate support status + * + * @type {Boolean} + */ +Ogvjs.prototype.featuresPlaybackRate = Ogvjs.canControlPlaybackRate(); + +/* + * Set the the tech's fullscreen resize support status. + * HTML video is able to automatically resize when going to fullscreen. + * (No longer appears to be used. Can probably be removed.) + */ +Ogvjs.prototype.featuresFullscreenResize = true; + +/* + * Set the tech's progress event support status + * (this disables the manual progress events of the Tech) + */ +Ogvjs.prototype.featuresProgressEvents = true; + +/* + * Sets the tech's status on native text track support + * + * @type {Boolean} + */ +Ogvjs.prototype.featuresNativeTextTracks = Ogvjs.supportsNativeTextTracks(); + +Ogvjs.disposeMediaElement = function (el) { + if (!el) { + return; + } + + if (el.parentNode) { + el.parentNode.removeChild(el); + } + + // remove any child track or source nodes to prevent their loading + while (el.hasChildNodes()) { + el.removeChild(el.firstChild); + } + + // remove any src reference. not setting `src=''` because that causes a warning + // in firefox + el.removeAttribute('src'); + + // force the media element to update its loading state by calling load() + // however IE on Windows 7N has a bug that throws an error so need a try/catch (#793) + if (typeof el.load === 'function') { + // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473) + (function () { + try { + el.load(); + } catch (e) { + // not supported + } + })(); + } +}; + +Tech.registerTech('Ogvjs', Ogvjs); +exports['default'] = Ogvjs; +module.exports = exports['default']; +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}]},{},[1])(1) +}); \ No newline at end of file diff --git a/resources/videojs-ogvjs/videojs-ogvjs.min.js b/resources/videojs-ogvjs/videojs-ogvjs.min.js new file mode 100644 index 0000000..705f44d --- /dev/null +++ b/resources/videojs-ogvjs/videojs-ogvjs.min.js @@ -0,0 +1,7 @@ +/** + * videojs-ogvjs + * @version 1.0.6 + * @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.videojsOgvjs=e()}}(function(){return function e(t,n,r){function o(u,l){if(!n[u]){if(!t[u]){var a="function"==typeof require&&require;if(!l&&a)return a(u,!0);if(i)return i(u,!0);var s=new Error("Cannot find module '"+u+"'");throw s.code="MODULE_NOT_FOUND",s}var f=n[u]={exports:{}};t[u][0].call(f.exports,function(e){var n=t[u][1][e];return o(n?n:e)},f,f.exports,e,t,n,r)}return n[u].exports}for(var i="function"==typeof require&&require,u=0;u<r.length;u++)o(r[u]);return o}({1:[function(e,t,n){(function(e){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(n,"__esModule",{value:!0});var u=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),l=function(e,t,n){for(var r=!0;r;){var o=e,i=t,u=n;r=!1,null===o&&(o=Function.prototype);var l=Object.getOwnPropertyDescriptor(o,i);if(void 0!==l){if("value"in l)return l.value;var a=l.get;if(void 0===a)return;return a.call(u)}var s=Object.getPrototypeOf(o);if(null===s)return;e=s,t=i,n=u,r=!0,l=s=void 0}},a="undefined"!=typeof window?window.videojs:"undefined"!=typeof e?e.videojs:null,s=r(a),f="undefined"!=typeof window?window.OGVCompat:"undefined"!=typeof e?e.OGVCompat:null,c=r(f),d="undefined"!=typeof window?window.OGVLoader:"undefined"!=typeof e?e.OGVLoader:null,p=r(d),y="undefined"!=typeof window?window.OGVPlayer:"undefined"!=typeof e?e.OGVPlayer:null,h=r(y),v=s["default"].getComponent("Tech"),k=function(e){function t(e,n){o(this,t),l(Object.getPrototypeOf(t.prototype),"constructor",this).call(this,e,n),this.triggerReady()}return i(t,e),u(t,[{key:"dispose",value:function(){l(Object.getPrototypeOf(t.prototype),"dispose",this).call(this)}},{key:"createEl",value:function(){var e=this.options_;if(!e.base)throw new Error("Please specify the base for the ogv.js library");p["default"].base=e.base;var t=new h["default"](e);return this.lastTime=0,t.addEventListener("framecallback",this.onFrameUpdate.bind(this)),t.src=this.options_.source.src,t.className+=" vjs-tech",t}},{key:"onFrameUpdate",value:function(e){var t=.25,n=this.el_?this.el_.currentTime:this.lastTime;Math.abs(n-this.lastTime)>=t&&(this.lastTime=n,this.trigger("timeupdate"),this.trigger("durationchange"))}},{key:"play",value:function(){this.el_.play()}},{key:"pause",value:function(){this.el_.pause()}},{key:"paused",value:function(){return this.el_.paused}},{key:"currentTime",value:function(){return this.el_.currentTime}},{key:"setCurrentTime",value:function(e){try{this.el_.currentTime=e}catch(t){s["default"].log(t,"Video is not ready. (Video.js)")}}},{key:"duration",value:function(){return this.el_.duration||0}},{key:"buffered",value:function(){return this.el_.buffered}},{key:"volume",value:function(){return this.el_.volume?this.el_.volume:1}},{key:"setVolume",value:function(e){this.el_.volume&&(this.el_.volume=e)}},{key:"muted",value:function(){return this.el_.muted?this.el_.muted:!1}},{key:"setMuted",value:function(e){this.el_.muted&&(this.el_.muted=e)}},{key:"width",value:function(){return this.el_.offsetWidth}},{key:"height",value:function(){return this.el_.offsetHeight}},{key:"supportsFullScreen",value:function(){if("function"==typeof this.el_.webkitEnterFullScreen){var e=window.navigator.userAgent;if(/Android/.test(e)||!/Chrome|Mac OS X 10.5/.test(e))return!0}return!1}},{key:"enterFullScreen",value:function(){var e=this.el_;"webkitDisplayingFullscreen"in e&&this.one("webkitbeginfullscreen",function(){this.one("webkitendfullscreen",function(){this.trigger("fullscreenchange",{isFullscreen:!1})}),this.trigger("fullscreenchange",{isFullscreen:!0})}),e.paused&&e.networkState<=e.HAVE_METADATA?(this.el_.play(),this.setTimeout(function(){e.pause(),e.webkitEnterFullScreen()},0)):e.webkitEnterFullScreen()}},{key:"exitFullScreen",value:function(){this.el_.webkitExitFullScreen()}},{key:"src",value:function(e){return"undefined"==typeof e?this.el_.src:void this.setSrc(e)}},{key:"setSrc",value:function(e){this.el_.src=e}},{key:"load",value:function(){this.el_.load()}},{key:"currentSrc",value:function(){return this.currentSource_?this.currentSource_.src:this.el_.currentSrc}},{key:"poster",value:function(){return this.el_.poster}},{key:"setPoster",value:function(e){this.el_.poster=e}},{key:"controls",value:function(){return this.el_.controls}},{key:"setControls",value:function(e){this.el_.controls=!!e}},{key:"error",value:function(){return this.el_.error}},{key:"seeking",value:function(){return this.el_.seeking}},{key:"seekable",value:function(){return this.el_.seekable}},{key:"ended",value:function(){return this.el_.ended}},{key:"videoWidth",value:function(){return this.el_.videoWidth}},{key:"videoHeight",value:function(){return this.el_.videoHeight}}]),t}(v);k.isSupported=function(){return c["default"].supported("OGVPlayer")},k.canPlaySource=function(e){return-1!==e.type.indexOf("/ogg")?"maybe":""},k.canControlVolume=function(){return!1},k.canControlPlaybackRate=function(){return!1},k.supportsNativeTextTracks=function(){return!1},k.Events=["loadstart","suspend","abort","error","emptied","stalled","loadedmetadata","loadeddata","canplay","canplaythrough","playing","waiting","seeking","seeked","ended","durationchange","timeupdate","progress","play","pause","ratechange","volumechange"],k.prototype.featuresVolumeControl=k.canControlVolume(),k.prototype.featuresPlaybackRate=k.canControlPlaybackRate(),k.prototype.featuresFullscreenResize=!0,k.prototype.featuresProgressEvents=!0,k.prototype.featuresNativeTextTracks=k.supportsNativeTextTracks(),k.disposeMediaElement=function(e){if(e){for(e.parentNode&&e.parentNode.removeChild(e);e.hasChildNodes();)e.removeChild(e.firstChild);e.removeAttribute("src"),"function"==typeof e.load&&!function(){try{e.load()}catch(t){}}()}},v.registerTech("Ogvjs",k),n["default"]=k,t.exports=n["default"]}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}]},{},[1])(1)}); \ No newline at end of file -- To view, visit https://gerrit.wikimedia.org/r/260963 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Ie511c145b9d16be1d234645e69b8c81665448ef9 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