videojs_rails 4.9.1 → 4.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 99287eb1d4e6a6471da87b300cf6cc6a1a02b021
4
- data.tar.gz: 9b9fb338831a59417338dd945eafba11547359b9
3
+ metadata.gz: 53ac8c4527a2f49acbf718f4598e0e244bda1bd4
4
+ data.tar.gz: 7a173b8a8eb80952698a749455ffd7f9dfabceaa
5
5
  SHA512:
6
- metadata.gz: 2602d3c752c79dd03a5347e5db1041a13b20eeb481b650b8bfe4bbe1b18694e9cad30413b30a8886527f5fe68fc9084fd81019153ad439245eb9fd730edf0e88
7
- data.tar.gz: 34745609b0148fe1d3ce21fe6b813e6ce5970500dd91b54a71f5c5ede2a4ba47f1c78e57a2dd79a18efec26904c3bbbeb5f708e9e21a31a8bde85445ff8e3af0
6
+ metadata.gz: 1d944e69a7484274035c4552e3624104fb2f28fbf857aa4b2915c83e6bc48b3ab53b17a792ac15f45718afdaf1dc2bb5e7a2abeead6fef63d40f11010917900b
7
+ data.tar.gz: 8bcccdbabe88e70f96b9fa4cdc110f8f29d84291e350c744e761a5176661c8c83ba5296405752b76c2204da315e53d0b71eb6a443c960012fa295009dcdd5eba
@@ -1,3 +1,3 @@
1
1
  module VideojsRails
2
- VERSION = '4.9.1'
2
+ VERSION = '4.10.0'
3
3
  end
@@ -63,7 +63,7 @@ var vjs = function(id, options, ready){
63
63
  var videojs = window['videojs'] = vjs;
64
64
 
65
65
  // CDN Version. Used to target right flash swf.
66
- vjs.CDN_VERSION = '4.9';
66
+ vjs.CDN_VERSION = '4.10';
67
67
  vjs.ACCESS_PROTOCOL = ('https:' == document.location.protocol ? 'https://' : 'http://');
68
68
 
69
69
  /**
@@ -116,7 +116,7 @@ vjs.options = {
116
116
  };
117
117
 
118
118
  // Set CDN Version of swf
119
- // The added (+) blocks the replace from changing this 4.9 string
119
+ // The added (+) blocks the replace from changing this 4.10 string
120
120
  if (vjs.CDN_VERSION !== 'GENERATED'+'_CDN_VSN') {
121
121
  videojs.options['flash']['swf'] = "<%= asset_path('video-js.swf') %>";
122
122
  }
@@ -360,7 +360,7 @@ vjs.on = function(elem, type, fn){
360
360
  * Removes event listeners from an element
361
361
  * @param {Element|Object} elem Object to remove listeners from
362
362
  * @param {String|Array=} type Type of listener to remove. Don't include to remove all events from element.
363
- * @param {Function} fn Specific listener to remove. Don't incldue to remove listeners for an event type.
363
+ * @param {Function} fn Specific listener to remove. Don't include to remove listeners for an event type.
364
364
  * @private
365
365
  */
366
366
  vjs.off = function(elem, type, fn) {
@@ -479,7 +479,7 @@ vjs.fixEvent = function(event) {
479
479
  for (var key in old) {
480
480
  // Safari 6.0.3 warns you if you try to copy deprecated layerX/Y
481
481
  // Chrome warns you if you try to copy deprecated keyboardEvent.keyLocation
482
- if (key !== 'layerX' && key !== 'layerY' && key !== 'keyboardEvent.keyLocation') {
482
+ if (key !== 'layerX' && key !== 'layerY' && key !== 'keyLocation') {
483
483
  // Chrome 32+ warns if you try to copy deprecated returnValue, but
484
484
  // we still want to if preventDefault isn't supported (IE8).
485
485
  if (!(key == 'returnValue' && old.preventDefault)) {
@@ -1080,6 +1080,7 @@ vjs.IS_FIREFOX = (/Firefox/i).test(vjs.USER_AGENT);
1080
1080
  vjs.IS_CHROME = (/Chrome/i).test(vjs.USER_AGENT);
1081
1081
 
1082
1082
  vjs.TOUCH_ENABLED = !!(('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch);
1083
+ vjs.BACKGROUND_SIZE_SUPPORTED = 'backgroundSize' in vjs.TEST_VID.style;
1083
1084
 
1084
1085
  /**
1085
1086
  * Apply attributes to an HTML element.
@@ -1662,8 +1663,14 @@ vjs.Component = vjs.CoreObject.extend({
1662
1663
  // Updated options with supplied options
1663
1664
  options = this.options(options);
1664
1665
 
1665
- // Get ID from options, element, or create using player ID and unique ID
1666
- this.id_ = options['id'] || ((options['el'] && options['el']['id']) ? options['el']['id'] : player.id() + '_component_' + vjs.guid++ );
1666
+ // Get ID from options or options element if one is supplied
1667
+ this.id_ = options['id'] || (options['el'] && options['el']['id']);
1668
+
1669
+ // If there was no ID from the options, generate one
1670
+ if (!this.id_) {
1671
+ // Don't require the player ID function in the case of mock players
1672
+ this.id_ = ((player.id && player.id()) || 'no_player') + '_component_' + vjs.guid++;
1673
+ }
1667
1674
 
1668
1675
  this.name_ = options['name'] || null;
1669
1676
 
@@ -1969,17 +1976,17 @@ vjs.Component.prototype.getChild = function(name){
1969
1976
  * @suppress {accessControls|checkRegExp|checkTypes|checkVars|const|constantProperty|deprecated|duplicate|es5Strict|fileoverviewTags|globalThis|invalidCasts|missingProperties|nonStandardJsDocs|strictModuleDepCheck|undefinedNames|undefinedVars|unknownDefines|uselessCode|visibility}
1970
1977
  */
1971
1978
  vjs.Component.prototype.addChild = function(child, options){
1972
- var component, componentClass, componentName, componentId;
1979
+ var component, componentClass, componentName;
1973
1980
 
1974
- // If string, create new component with options
1981
+ // If child is a string, create new component with options
1975
1982
  if (typeof child === 'string') {
1976
-
1977
1983
  componentName = child;
1978
1984
 
1979
1985
  // Make sure options is at least an empty object to protect against errors
1980
1986
  options = options || {};
1981
1987
 
1982
- // Assume name of set is a lowercased name of the UI Class (PlayButton, etc.)
1988
+ // If no componentClass in options, assume componentClass is the name lowercased
1989
+ // (e.g. playButton)
1983
1990
  componentClass = options['componentClass'] || vjs.capitalize(componentName);
1984
1991
 
1985
1992
  // Set name through options
@@ -2088,36 +2095,51 @@ vjs.Component.prototype.removeChild = function(component){
2088
2095
  *
2089
2096
  */
2090
2097
  vjs.Component.prototype.initChildren = function(){
2091
- var parent, children, child, name, opts;
2098
+ var parent, parentOptions, children, child, name, opts, handleAdd;
2092
2099
 
2093
2100
  parent = this;
2094
- children = this.options()['children'];
2101
+ parentOptions = parent.options();
2102
+ children = parentOptions['children'];
2095
2103
 
2096
2104
  if (children) {
2105
+ handleAdd = function(name, opts){
2106
+ // Allow options for children to be set at the parent options
2107
+ // e.g. videojs(id, { controlBar: false });
2108
+ // instead of videojs(id, { children: { controlBar: false });
2109
+ if (parentOptions[name]) {
2110
+ opts = parentOptions[name];
2111
+ }
2112
+
2113
+ // Allow for disabling default components
2114
+ // e.g. vjs.options['children']['posterImage'] = false
2115
+ if (opts === false) return;
2116
+
2117
+ // Create and add the child component.
2118
+ // Add a direct reference to the child by name on the parent instance.
2119
+ // If two of the same component are used, different names should be supplied
2120
+ // for each
2121
+ parent[name] = parent.addChild(name, opts);
2122
+ };
2123
+
2097
2124
  // Allow for an array of children details to passed in the options
2098
2125
  if (vjs.obj.isArray(children)) {
2099
2126
  for (var i = 0; i < children.length; i++) {
2100
2127
  child = children[i];
2101
2128
 
2102
2129
  if (typeof child == 'string') {
2130
+ // ['myComponent']
2103
2131
  name = child;
2104
2132
  opts = {};
2105
2133
  } else {
2134
+ // [{ name: 'myComponent', otherOption: true }]
2106
2135
  name = child.name;
2107
2136
  opts = child;
2108
2137
  }
2109
2138
 
2110
- parent[name] = parent.addChild(name, opts);
2139
+ handleAdd(name, opts);
2111
2140
  }
2112
2141
  } else {
2113
- vjs.obj.each(children, function(name, opts){
2114
- // Allow for disabling default components
2115
- // e.g. vjs.options['children']['posterImage'] = false
2116
- if (opts === false) return;
2117
-
2118
- // Set property name on player. Could cause conflicts with other prop names, but it's worth making refs easy.
2119
- parent[name] = parent.addChild(name, opts);
2120
- });
2142
+ vjs.obj.each(children, handleAdd);
2121
2143
  }
2122
2144
  }
2123
2145
  };
@@ -2140,46 +2162,169 @@ vjs.Component.prototype.buildCSSClass = function(){
2140
2162
  * Add an event listener to this component's element
2141
2163
  *
2142
2164
  * var myFunc = function(){
2143
- * var myPlayer = this;
2165
+ * var myComponent = this;
2144
2166
  * // Do something when the event is fired
2145
2167
  * };
2146
2168
  *
2147
- * myPlayer.on("eventName", myFunc);
2169
+ * myComponent.on('eventType', myFunc);
2170
+ *
2171
+ * The context of myFunc will be myComponent unless previously bound.
2172
+ *
2173
+ * Alternatively, you can add a listener to another element or component.
2174
+ *
2175
+ * myComponent.on(otherElement, 'eventName', myFunc);
2176
+ * myComponent.on(otherComponent, 'eventName', myFunc);
2177
+ *
2178
+ * The benefit of using this over `vjs.on(otherElement, 'eventName', myFunc)`
2179
+ * and `otherComponent.on('eventName', myFunc)` is that this way the listeners
2180
+ * will be automatically cleaned up when either component is diposed.
2181
+ * It will also bind myComponent as the context of myFunc.
2148
2182
  *
2149
- * The context will be the component.
2183
+ * **NOTE**: When using this on elements in the page other than window
2184
+ * and document (both permanent), if you remove the element from the DOM
2185
+ * you need to call `vjs.trigger(el, 'dispose')` on it to clean up
2186
+ * references to it and allow the browser to garbage collect it.
2150
2187
  *
2151
- * @param {String} type The event type e.g. 'click'
2152
- * @param {Function} fn The event listener
2153
- * @return {vjs.Component} self
2188
+ * @param {String|vjs.Component} first The event type or other component
2189
+ * @param {Function|String} second The event handler or event type
2190
+ * @param {Function} third The event handler
2191
+ * @return {vjs.Component} self
2154
2192
  */
2155
- vjs.Component.prototype.on = function(type, fn){
2156
- vjs.on(this.el_, type, vjs.bind(this, fn));
2193
+ vjs.Component.prototype.on = function(first, second, third){
2194
+ var target, type, fn, removeOnDispose, cleanRemover, thisComponent;
2195
+
2196
+ if (typeof first === 'string' || vjs.obj.isArray(first)) {
2197
+ vjs.on(this.el_, first, vjs.bind(this, second));
2198
+
2199
+ // Targeting another component or element
2200
+ } else {
2201
+ target = first;
2202
+ type = second;
2203
+ fn = vjs.bind(this, third);
2204
+ thisComponent = this;
2205
+
2206
+ // When this component is disposed, remove the listener from the other component
2207
+ removeOnDispose = function(){
2208
+ thisComponent.off(target, type, fn);
2209
+ };
2210
+ // Use the same function ID so we can remove it later it using the ID
2211
+ // of the original listener
2212
+ removeOnDispose.guid = fn.guid;
2213
+ this.on('dispose', removeOnDispose);
2214
+
2215
+ // If the other component is disposed first we need to clean the reference
2216
+ // to the other component in this component's removeOnDispose listener
2217
+ // Otherwise we create a memory leak.
2218
+ cleanRemover = function(){
2219
+ thisComponent.off('dispose', removeOnDispose);
2220
+ };
2221
+ // Add the same function ID so we can easily remove it later
2222
+ cleanRemover.guid = fn.guid;
2223
+
2224
+ // Check if this is a DOM node
2225
+ if (first.nodeName) {
2226
+ // Add the listener to the other element
2227
+ vjs.on(target, type, fn);
2228
+ vjs.on(target, 'dispose', cleanRemover);
2229
+
2230
+ // Should be a component
2231
+ // Not using `instanceof vjs.Component` because it makes mock players difficult
2232
+ } else if (typeof first.on === 'function') {
2233
+ // Add the listener to the other component
2234
+ target.on(type, fn);
2235
+ target.on('dispose', cleanRemover);
2236
+ }
2237
+ }
2238
+
2157
2239
  return this;
2158
2240
  };
2159
2241
 
2160
2242
  /**
2161
- * Remove an event listener from the component's element
2243
+ * Remove an event listener from this component's element
2244
+ *
2245
+ * myComponent.off('eventType', myFunc);
2162
2246
  *
2163
- * myComponent.off("eventName", myFunc);
2247
+ * If myFunc is excluded, ALL listeners for the event type will be removed.
2248
+ * If eventType is excluded, ALL listeners will be removed from the component.
2164
2249
  *
2165
- * @param {String=} type Event type. Without type it will remove all listeners.
2166
- * @param {Function=} fn Event listener. Without fn it will remove all listeners for a type.
2250
+ * Alternatively you can use `off` to remove listeners that were added to other
2251
+ * elements or components using `myComponent.on(otherComponent...`.
2252
+ * In this case both the event type and listener function are REQUIRED.
2253
+ *
2254
+ * myComponent.off(otherElement, 'eventType', myFunc);
2255
+ * myComponent.off(otherComponent, 'eventType', myFunc);
2256
+ *
2257
+ * @param {String=|vjs.Component} first The event type or other component
2258
+ * @param {Function=|String} second The listener function or event type
2259
+ * @param {Function=} third The listener for other component
2167
2260
  * @return {vjs.Component}
2168
2261
  */
2169
- vjs.Component.prototype.off = function(type, fn){
2170
- vjs.off(this.el_, type, fn);
2262
+ vjs.Component.prototype.off = function(first, second, third){
2263
+ var target, otherComponent, type, fn, otherEl;
2264
+
2265
+ if (!first || typeof first === 'string' || vjs.obj.isArray(first)) {
2266
+ vjs.off(this.el_, first, second);
2267
+ } else {
2268
+ target = first;
2269
+ type = second;
2270
+ // Ensure there's at least a guid, even if the function hasn't been used
2271
+ fn = vjs.bind(this, third);
2272
+
2273
+ // Remove the dispose listener on this component,
2274
+ // which was given the same guid as the event listener
2275
+ this.off('dispose', fn);
2276
+
2277
+ if (first.nodeName) {
2278
+ // Remove the listener
2279
+ vjs.off(target, type, fn);
2280
+ // Remove the listener for cleaning the dispose listener
2281
+ vjs.off(target, 'dispose', fn);
2282
+ } else {
2283
+ target.off(type, fn);
2284
+ target.off('dispose', fn);
2285
+ }
2286
+ }
2287
+
2171
2288
  return this;
2172
2289
  };
2173
2290
 
2174
2291
  /**
2175
2292
  * Add an event listener to be triggered only once and then removed
2176
2293
  *
2177
- * @param {String} type Event type
2178
- * @param {Function} fn Event listener
2294
+ * myComponent.one('eventName', myFunc);
2295
+ *
2296
+ * Alternatively you can add a listener to another element or component
2297
+ * that will be triggered only once.
2298
+ *
2299
+ * myComponent.one(otherElement, 'eventName', myFunc);
2300
+ * myComponent.one(otherComponent, 'eventName', myFunc);
2301
+ *
2302
+ * @param {String|vjs.Component} first The event type or other component
2303
+ * @param {Function|String} second The listener function or event type
2304
+ * @param {Function=} third The listener function for other component
2179
2305
  * @return {vjs.Component}
2180
2306
  */
2181
- vjs.Component.prototype.one = function(type, fn) {
2182
- vjs.one(this.el_, type, vjs.bind(this, fn));
2307
+ vjs.Component.prototype.one = function(first, second, third) {
2308
+ var target, type, fn, thisComponent, newFunc;
2309
+
2310
+ if (typeof first === 'string' || vjs.obj.isArray(first)) {
2311
+ vjs.one(this.el_, first, vjs.bind(this, second));
2312
+ } else {
2313
+ target = first;
2314
+ type = second;
2315
+ fn = vjs.bind(this, third);
2316
+ thisComponent = this;
2317
+
2318
+ newFunc = function(){
2319
+ thisComponent.off(target, type, newFunc);
2320
+ fn.apply(this, arguments);
2321
+ };
2322
+ // Keep the same function ID so we can remove it later
2323
+ newFunc.guid = fn.guid;
2324
+
2325
+ this.on(target, type, newFunc);
2326
+ }
2327
+
2183
2328
  return this;
2184
2329
  };
2185
2330
 
@@ -2591,6 +2736,11 @@ vjs.Component.prototype.emitTapEvents = function(){
2591
2736
  vjs.Component.prototype.enableTouchActivity = function() {
2592
2737
  var report, touchHolding, touchEnd;
2593
2738
 
2739
+ // Don't continue if the root player doesn't support reporting user activity
2740
+ if (!this.player().reportUserActivity) {
2741
+ return;
2742
+ }
2743
+
2594
2744
  // listener for reporting that the user is active
2595
2745
  report = vjs.bind(this.player(), this.player().reportUserActivity);
2596
2746
 
@@ -2722,13 +2872,10 @@ vjs.Slider = vjs.Component.extend({
2722
2872
  this.on('blur', this.onBlur);
2723
2873
  this.on('click', this.onClick);
2724
2874
 
2725
- this.player_.on('controlsvisible', vjs.bind(this, this.update));
2726
-
2727
- player.on(this.playerEvent, vjs.bind(this, this.update));
2875
+ this.on(player, 'controlsvisible', this.update);
2876
+ this.on(player, this.playerEvent, this.update);
2728
2877
 
2729
2878
  this.boundEvents = {};
2730
-
2731
-
2732
2879
  this.boundEvents.move = vjs.bind(this, this.onMouseMove);
2733
2880
  this.boundEvents.end = vjs.bind(this, this.onMouseUp);
2734
2881
  }
@@ -3368,9 +3515,10 @@ vjs.Player = vjs.Component.extend({
3368
3515
  this.cache_ = {};
3369
3516
 
3370
3517
  // Set poster
3371
- this.poster_ = options['poster'];
3518
+ this.poster_ = options['poster'] || '';
3519
+
3372
3520
  // Set controls
3373
- this.controls_ = options['controls'];
3521
+ this.controls_ = !!options['controls'];
3374
3522
  // Original tag settings stored in options
3375
3523
  // now remove immediately so native controls don't flash.
3376
3524
  // May be turned back on by HTML5 tech if nativeControlsForTouch is true
@@ -3592,6 +3740,10 @@ vjs.Player.prototype.createEl = function(){
3592
3740
  this.width(this.options_['width'], true); // (true) Skip resize listener on load
3593
3741
  this.height(this.options_['height'], true);
3594
3742
 
3743
+ // vjs.insertFirst seems to cause the networkState to flicker from 3 to 2, so
3744
+ // keep track of the original for later so we can know if the source originally failed
3745
+ tag.initNetworkState_ = tag.networkState;
3746
+
3595
3747
  // Wrap video tag in div (el/box) container
3596
3748
  if (tag.parentNode) {
3597
3749
  tag.parentNode.insertBefore(el, tag);
@@ -3772,6 +3924,7 @@ vjs.Player.prototype.onWaiting = function(){
3772
3924
  /**
3773
3925
  * A handler for events that signal that waiting has eneded
3774
3926
  * which is not consistent between browsers. See #1351
3927
+ * @private
3775
3928
  */
3776
3929
  vjs.Player.prototype.onWaitEnd = function(){
3777
3930
  this.removeClass('vjs-waiting');
@@ -4050,7 +4203,14 @@ vjs.Player.prototype.duration = function(seconds){
4050
4203
  return this.cache_.duration || 0;
4051
4204
  };
4052
4205
 
4053
- // Calculates how much time is left. Not in spec, but useful.
4206
+ /**
4207
+ * Calculates how much time is left.
4208
+ *
4209
+ * var timeLeft = myPlayer.remainingTime();
4210
+ *
4211
+ * Not a native video element function, but useful
4212
+ * @return {Number} The time remaining in seconds
4213
+ */
4054
4214
  vjs.Player.prototype.remainingTime = function(){
4055
4215
  return this.duration() - this.currentTime();
4056
4216
  };
@@ -4522,14 +4682,20 @@ vjs.Player.prototype.sourceList_ = function(sources){
4522
4682
  }
4523
4683
  };
4524
4684
 
4525
- // Begin loading the src data
4526
- // http://dev.w3.org/html5/spec/video.html#dom-media-load
4685
+ /**
4686
+ * Begin loading the src data.
4687
+ * @return {vjs.Player} Returns the player
4688
+ */
4527
4689
  vjs.Player.prototype.load = function(){
4528
4690
  this.techCall('load');
4529
4691
  return this;
4530
4692
  };
4531
4693
 
4532
- // http://dev.w3.org/html5/spec/video.html#dom-media-currentsrc
4694
+ /**
4695
+ * Returns the fully qualified URL of the current source value e.g. http://mysite.com/video.mp4
4696
+ * Can be used in conjuction with `currentType` to assist in rebuilding the current source object.
4697
+ * @return {String} The current source
4698
+ */
4533
4699
  vjs.Player.prototype.currentSrc = function(){
4534
4700
  return this.techGet('currentSrc') || this.cache_.src || '';
4535
4701
  };
@@ -4544,7 +4710,11 @@ vjs.Player.prototype.currentType = function(){
4544
4710
  return this.currentType_ || '';
4545
4711
  };
4546
4712
 
4547
- // Attributes/Options
4713
+ /**
4714
+ * Get or set the preload attribute.
4715
+ * @return {String} The preload attribute value when getting
4716
+ * @return {vjs.Player} Returns the player when setting
4717
+ */
4548
4718
  vjs.Player.prototype.preload = function(value){
4549
4719
  if (value !== undefined) {
4550
4720
  this.techCall('setPreload', value);
@@ -4553,6 +4723,12 @@ vjs.Player.prototype.preload = function(value){
4553
4723
  }
4554
4724
  return this.techGet('preload');
4555
4725
  };
4726
+
4727
+ /**
4728
+ * Get or set the autoplay attribute.
4729
+ * @return {String} The autoplay attribute value when getting
4730
+ * @return {vjs.Player} Returns the player when setting
4731
+ */
4556
4732
  vjs.Player.prototype.autoplay = function(value){
4557
4733
  if (value !== undefined) {
4558
4734
  this.techCall('setAutoplay', value);
@@ -4561,6 +4737,12 @@ vjs.Player.prototype.autoplay = function(value){
4561
4737
  }
4562
4738
  return this.techGet('autoplay', value);
4563
4739
  };
4740
+
4741
+ /**
4742
+ * Get or set the loop attribute on the video element.
4743
+ * @return {String} The loop attribute value when getting
4744
+ * @return {vjs.Player} Returns the player when setting
4745
+ */
4564
4746
  vjs.Player.prototype.loop = function(value){
4565
4747
  if (value !== undefined) {
4566
4748
  this.techCall('setLoop', value);
@@ -4597,6 +4779,12 @@ vjs.Player.prototype.poster = function(src){
4597
4779
  return this.poster_;
4598
4780
  }
4599
4781
 
4782
+ // The correct way to remove a poster is to set as an empty string
4783
+ // other falsey values will throw errors
4784
+ if (!src) {
4785
+ src = '';
4786
+ }
4787
+
4600
4788
  // update the internal poster variable
4601
4789
  this.poster_ = src;
4602
4790
 
@@ -4738,7 +4926,16 @@ vjs.Player.prototype.error = function(err){
4738
4926
  return this;
4739
4927
  };
4740
4928
 
4929
+ /**
4930
+ * Returns whether or not the player is in the "ended" state.
4931
+ * @return {Boolean} True if the player is in the ended state, false if not.
4932
+ */
4741
4933
  vjs.Player.prototype.ended = function(){ return this.techGet('ended'); };
4934
+
4935
+ /**
4936
+ * Returns whether or not the player is in the "seeking" state.
4937
+ * @return {Boolean} True if the player is in the seeking state, false if not.
4938
+ */
4742
4939
  vjs.Player.prototype.seeking = function(){ return this.techGet('seeking'); };
4743
4940
 
4744
4941
  // When the player is first initialized, trigger activity so components
@@ -4875,6 +5072,12 @@ vjs.Player.prototype.listenForUserActivity = function(){
4875
5072
  });
4876
5073
  };
4877
5074
 
5075
+ /**
5076
+ * Gets or sets the current playback rate.
5077
+ * @param {Boolean} rate New playback rate to set.
5078
+ * @return {Number} Returns the new playback rate when setting
5079
+ * @return {Number} Returns the current playback rate when getting
5080
+ */
4878
5081
  vjs.Player.prototype.playbackRate = function(rate) {
4879
5082
  if (rate !== undefined) {
4880
5083
  this.techCall('setPlaybackRate', rate);
@@ -4889,7 +5092,21 @@ vjs.Player.prototype.playbackRate = function(rate) {
4889
5092
 
4890
5093
  };
4891
5094
 
5095
+ /**
5096
+ * Store the current audio state
5097
+ * @type {Boolean}
5098
+ * @private
5099
+ */
4892
5100
  vjs.Player.prototype.isAudio_ = false;
5101
+
5102
+ /**
5103
+ * Gets or sets the audio flag
5104
+ *
5105
+ * @param {Boolean} bool True signals that this is an audio player.
5106
+ * @return {Boolean} Returns true if player is audio, false if not when getting
5107
+ * @return {vjs.Player} Returns the player if setting
5108
+ * @private
5109
+ */
4893
5110
  vjs.Player.prototype.isAudio = function(bool) {
4894
5111
  if (bool !== undefined) {
4895
5112
  this.isAudio_ = !!bool;
@@ -4991,8 +5208,8 @@ vjs.PlayToggle = vjs.Button.extend({
4991
5208
  init: function(player, options){
4992
5209
  vjs.Button.call(this, player, options);
4993
5210
 
4994
- player.on('play', vjs.bind(this, this.onPlay));
4995
- player.on('pause', vjs.bind(this, this.onPause));
5211
+ this.on(player, 'play', this.onPlay);
5212
+ this.on(player, 'pause', this.onPause);
4996
5213
  }
4997
5214
  });
4998
5215
 
@@ -5013,15 +5230,15 @@ vjs.PlayToggle.prototype.onClick = function(){
5013
5230
 
5014
5231
  // OnPlay - Add the vjs-playing class to the element so it can change appearance
5015
5232
  vjs.PlayToggle.prototype.onPlay = function(){
5016
- vjs.removeClass(this.el_, 'vjs-paused');
5017
- vjs.addClass(this.el_, 'vjs-playing');
5233
+ this.removeClass('vjs-paused');
5234
+ this.addClass('vjs-playing');
5018
5235
  this.el_.children[0].children[0].innerHTML = this.localize('Pause'); // change the button text to "Pause"
5019
5236
  };
5020
5237
 
5021
5238
  // OnPause - Add the vjs-paused class to the element so it can change appearance
5022
5239
  vjs.PlayToggle.prototype.onPause = function(){
5023
- vjs.removeClass(this.el_, 'vjs-playing');
5024
- vjs.addClass(this.el_, 'vjs-paused');
5240
+ this.removeClass('vjs-playing');
5241
+ this.addClass('vjs-paused');
5025
5242
  this.el_.children[0].children[0].innerHTML = this.localize('Play'); // change the button text to "Play"
5026
5243
  };
5027
5244
  /**
@@ -5035,7 +5252,7 @@ vjs.CurrentTimeDisplay = vjs.Component.extend({
5035
5252
  init: function(player, options){
5036
5253
  vjs.Component.call(this, player, options);
5037
5254
 
5038
- player.on('timeupdate', vjs.bind(this, this.updateContent));
5255
+ this.on(player, 'timeupdate', this.updateContent);
5039
5256
  }
5040
5257
  });
5041
5258
 
@@ -5076,7 +5293,7 @@ vjs.DurationDisplay = vjs.Component.extend({
5076
5293
  // so the value cannot be written out using this method.
5077
5294
  // Once the order of durationchange and this.player_.duration() being set is figured out,
5078
5295
  // this can be updated.
5079
- player.on('timeupdate', vjs.bind(this, this.updateContent));
5296
+ this.on(player, 'timeupdate', this.updateContent);
5080
5297
  }
5081
5298
  });
5082
5299
 
@@ -5136,7 +5353,7 @@ vjs.RemainingTimeDisplay = vjs.Component.extend({
5136
5353
  init: function(player, options){
5137
5354
  vjs.Component.call(this, player, options);
5138
5355
 
5139
- player.on('timeupdate', vjs.bind(this, this.updateContent));
5356
+ this.on(player, 'timeupdate', this.updateContent);
5140
5357
  }
5141
5358
  });
5142
5359
 
@@ -5235,7 +5452,7 @@ vjs.SeekBar = vjs.Slider.extend({
5235
5452
  /** @constructor */
5236
5453
  init: function(player, options){
5237
5454
  vjs.Slider.call(this, player, options);
5238
- player.on('timeupdate', vjs.bind(this, this.updateARIAAttributes));
5455
+ this.on(player, 'timeupdate', this.updateARIAAttributes);
5239
5456
  player.ready(vjs.bind(this, this.updateARIAAttributes));
5240
5457
  }
5241
5458
  });
@@ -5317,7 +5534,7 @@ vjs.LoadProgressBar = vjs.Component.extend({
5317
5534
  /** @constructor */
5318
5535
  init: function(player, options){
5319
5536
  vjs.Component.call(this, player, options);
5320
- player.on('progress', vjs.bind(this, this.update));
5537
+ this.on(player, 'progress', this.update);
5321
5538
  }
5322
5539
  });
5323
5540
 
@@ -5396,7 +5613,7 @@ vjs.PlayProgressBar.prototype.createEl = function(){
5396
5613
  vjs.SeekHandle = vjs.SliderHandle.extend({
5397
5614
  init: function(player, options) {
5398
5615
  vjs.SliderHandle.call(this, player, options);
5399
- player.on('timeupdate', vjs.bind(this, this.updateContent));
5616
+ this.on(player, 'timeupdate', this.updateContent);
5400
5617
  }
5401
5618
  });
5402
5619
 
@@ -5436,13 +5653,13 @@ vjs.VolumeControl = vjs.Component.extend({
5436
5653
  if (player.tech && player.tech['featuresVolumeControl'] === false) {
5437
5654
  this.addClass('vjs-hidden');
5438
5655
  }
5439
- player.on('loadstart', vjs.bind(this, function(){
5656
+ this.on(player, 'loadstart', function(){
5440
5657
  if (player.tech['featuresVolumeControl'] === false) {
5441
5658
  this.addClass('vjs-hidden');
5442
5659
  } else {
5443
5660
  this.removeClass('vjs-hidden');
5444
5661
  }
5445
- }));
5662
+ });
5446
5663
  }
5447
5664
  });
5448
5665
 
@@ -5469,7 +5686,7 @@ vjs.VolumeBar = vjs.Slider.extend({
5469
5686
  /** @constructor */
5470
5687
  init: function(player, options){
5471
5688
  vjs.Slider.call(this, player, options);
5472
- player.on('volumechange', vjs.bind(this, this.updateARIAAttributes));
5689
+ this.on(player, 'volumechange', this.updateARIAAttributes);
5473
5690
  player.ready(vjs.bind(this, this.updateARIAAttributes));
5474
5691
  }
5475
5692
  });
@@ -5572,19 +5789,20 @@ vjs.MuteToggle = vjs.Button.extend({
5572
5789
  init: function(player, options){
5573
5790
  vjs.Button.call(this, player, options);
5574
5791
 
5575
- player.on('volumechange', vjs.bind(this, this.update));
5792
+ this.on(player, 'volumechange', this.update);
5576
5793
 
5577
5794
  // hide mute toggle if the current tech doesn't support volume control
5578
5795
  if (player.tech && player.tech['featuresVolumeControl'] === false) {
5579
5796
  this.addClass('vjs-hidden');
5580
5797
  }
5581
- player.on('loadstart', vjs.bind(this, function(){
5798
+
5799
+ this.on(player, 'loadstart', function(){
5582
5800
  if (player.tech['featuresVolumeControl'] === false) {
5583
5801
  this.addClass('vjs-hidden');
5584
5802
  } else {
5585
5803
  this.removeClass('vjs-hidden');
5586
5804
  }
5587
- }));
5805
+ });
5588
5806
  }
5589
5807
  });
5590
5808
 
@@ -5640,19 +5858,19 @@ vjs.VolumeMenuButton = vjs.MenuButton.extend({
5640
5858
  vjs.MenuButton.call(this, player, options);
5641
5859
 
5642
5860
  // Same listeners as MuteToggle
5643
- player.on('volumechange', vjs.bind(this, this.update));
5861
+ this.on(player, 'volumechange', this.update);
5644
5862
 
5645
5863
  // hide mute toggle if the current tech doesn't support volume control
5646
5864
  if (player.tech && player.tech['featuresVolumeControl'] === false) {
5647
5865
  this.addClass('vjs-hidden');
5648
5866
  }
5649
- player.on('loadstart', vjs.bind(this, function(){
5867
+ this.on(player, 'loadstart', function(){
5650
5868
  if (player.tech['featuresVolumeControl'] === false) {
5651
5869
  this.addClass('vjs-hidden');
5652
5870
  } else {
5653
5871
  this.removeClass('vjs-hidden');
5654
5872
  }
5655
- }));
5873
+ });
5656
5874
  this.addClass('vjs-menu-button');
5657
5875
  }
5658
5876
  });
@@ -5661,7 +5879,7 @@ vjs.VolumeMenuButton.prototype.createMenu = function(){
5661
5879
  var menu = new vjs.Menu(this.player_, {
5662
5880
  contentElType: 'div'
5663
5881
  });
5664
- var vc = new vjs.VolumeBar(this.player_, vjs.obj.merge({'vertical': true}, this.options_.volumeBar));
5882
+ var vc = new vjs.VolumeBar(this.player_, this.options_.volumeBar);
5665
5883
  vc.on('focus', function() {
5666
5884
  menu.lockShowing();
5667
5885
  });
@@ -5699,8 +5917,8 @@ vjs.PlaybackRateMenuButton = vjs.MenuButton.extend({
5699
5917
  this.updateVisibility();
5700
5918
  this.updateLabel();
5701
5919
 
5702
- player.on('loadstart', vjs.bind(this, this.updateVisibility));
5703
- player.on('ratechange', vjs.bind(this, this.updateLabel));
5920
+ this.on(player, 'loadstart', this.updateVisibility);
5921
+ this.on(player, 'ratechange', this.updateLabel);
5704
5922
  }
5705
5923
  });
5706
5924
 
@@ -5801,7 +6019,7 @@ vjs.PlaybackRateMenuItem = vjs.MenuItem.extend({
5801
6019
  options['selected'] = rate === 1;
5802
6020
  vjs.MenuItem.call(this, player, options);
5803
6021
 
5804
- this.player().on('ratechange', vjs.bind(this, this.update));
6022
+ this.on(player, 'ratechange', this.update);
5805
6023
  }
5806
6024
  });
5807
6025
 
@@ -5827,27 +6045,23 @@ vjs.PosterImage = vjs.Button.extend({
5827
6045
  init: function(player, options){
5828
6046
  vjs.Button.call(this, player, options);
5829
6047
 
5830
- if (player.poster()) {
5831
- this.src(player.poster());
5832
- }
5833
-
5834
- if (!player.poster() || !player.controls()) {
5835
- this.hide();
5836
- }
5837
-
5838
- player.on('posterchange', vjs.bind(this, function(){
5839
- this.src(player.poster());
5840
- }));
5841
-
5842
- if (!player.isAudio()) {
5843
- player.on('play', vjs.bind(this, this.hide));
5844
- }
6048
+ this.update();
6049
+ player.on('posterchange', vjs.bind(this, this.update));
5845
6050
  }
5846
6051
  });
5847
6052
 
5848
- // use the test el to check for backgroundSize style support
5849
- var _backgroundSizeSupported = 'backgroundSize' in vjs.TEST_VID.style;
6053
+ /**
6054
+ * Clean up the poster image
6055
+ */
6056
+ vjs.PosterImage.prototype.dispose = function(){
6057
+ this.player().off('posterchange', this.update);
6058
+ vjs.Button.prototype.dispose.call(this);
6059
+ };
5850
6060
 
6061
+ /**
6062
+ * Create the poster image element
6063
+ * @return {Element}
6064
+ */
5851
6065
  vjs.PosterImage.prototype.createEl = function(){
5852
6066
  var el = vjs.createEl('div', {
5853
6067
  className: 'vjs-poster',
@@ -5856,42 +6070,65 @@ vjs.PosterImage.prototype.createEl = function(){
5856
6070
  tabIndex: -1
5857
6071
  });
5858
6072
 
5859
- if (!_backgroundSizeSupported) {
5860
- // setup an img element as a fallback for IE8
5861
- el.appendChild(vjs.createEl('img'));
6073
+ // To ensure the poster image resizes while maintaining its original aspect
6074
+ // ratio, use a div with `background-size` when available. For browsers that
6075
+ // do not support `background-size` (e.g. IE8), fall back on using a regular
6076
+ // img element.
6077
+ if (!vjs.BACKGROUND_SIZE_SUPPORTED) {
6078
+ this.fallbackImg_ = vjs.createEl('img');
6079
+ el.appendChild(this.fallbackImg_);
5862
6080
  }
5863
6081
 
5864
6082
  return el;
5865
6083
  };
5866
6084
 
5867
- vjs.PosterImage.prototype.src = function(url){
5868
- var el = this.el();
6085
+ /**
6086
+ * Event handler for updates to the player's poster source
6087
+ */
6088
+ vjs.PosterImage.prototype.update = function(){
6089
+ var url = this.player().poster();
6090
+
6091
+ this.setSrc(url);
5869
6092
 
5870
- // getter
5871
- // can't think of a need for a getter here
5872
- // see #838 if on is needed in the future
5873
- // still don't want a getter to set src as undefined
5874
- if (url === undefined) {
5875
- return;
6093
+ // If there's no poster source we should display:none on this component
6094
+ // so it's not still clickable or right-clickable
6095
+ if (url) {
6096
+ // Remove the display style property that hide() adds
6097
+ // as opposed to show() which sets display to block
6098
+ // In the future it might be worth creating an `unhide` component method
6099
+ this.el_.style.display = '';
6100
+ } else {
6101
+ this.hide();
5876
6102
  }
6103
+ };
5877
6104
 
5878
- // setter
5879
- // To ensure the poster image resizes while maintaining its original aspect
5880
- // ratio, use a div with `background-size` when available. For browsers that
5881
- // do not support `background-size` (e.g. IE8), fall back on using a regular
5882
- // img element.
5883
- if (_backgroundSizeSupported) {
5884
- el.style.backgroundImage = 'url("' + url + '")';
6105
+ /**
6106
+ * Set the poster source depending on the display method
6107
+ */
6108
+ vjs.PosterImage.prototype.setSrc = function(url){
6109
+ var backgroundImage;
6110
+
6111
+ if (this.fallbackImg_) {
6112
+ this.fallbackImg_.src = url;
5885
6113
  } else {
5886
- el.firstChild.src = url;
6114
+ backgroundImage = '';
6115
+ // Any falsey values should stay as an empty string, otherwise
6116
+ // this will throw an extra error
6117
+ if (url) {
6118
+ backgroundImage = 'url("' + url + '")';
6119
+ }
6120
+
6121
+ this.el_.style.backgroundImage = backgroundImage;
5887
6122
  }
5888
6123
  };
5889
6124
 
6125
+ /**
6126
+ * Event handler for clicks on the poster image
6127
+ */
5890
6128
  vjs.PosterImage.prototype.onClick = function(){
5891
- // Only accept clicks when controls are enabled
5892
- if (this.player().controls()) {
5893
- this.player_.play();
5894
- }
6129
+ // We don't want a click to trigger playback when controls are disabled
6130
+ // but CSS should be hiding the poster to prevent that from happening
6131
+ this.player_.play();
5895
6132
  };
5896
6133
  /* Loading Spinner
5897
6134
  ================================================================================ */
@@ -5968,7 +6205,7 @@ vjs.ErrorDisplay = vjs.Component.extend({
5968
6205
  vjs.Component.call(this, player, options);
5969
6206
 
5970
6207
  this.update();
5971
- player.on('error', vjs.bind(this, this.update));
6208
+ this.on(player, 'error', this.update);
5972
6209
  }
5973
6210
  });
5974
6211
 
@@ -6043,24 +6280,21 @@ vjs.MediaTechController = vjs.Component.extend({
6043
6280
  * any controls will still keep the user active
6044
6281
  */
6045
6282
  vjs.MediaTechController.prototype.initControlsListeners = function(){
6046
- var player, tech, activateControls, deactivateControls;
6283
+ var player, activateControls;
6047
6284
 
6048
- tech = this;
6049
6285
  player = this.player();
6050
6286
 
6051
- var activateControls = function(){
6287
+ activateControls = function(){
6052
6288
  if (player.controls() && !player.usingNativeControls()) {
6053
- tech.addControlsListeners();
6289
+ this.addControlsListeners();
6054
6290
  }
6055
6291
  };
6056
6292
 
6057
- deactivateControls = vjs.bind(tech, tech.removeControlsListeners);
6058
-
6059
6293
  // Set up event listeners once the tech is ready and has an element to apply
6060
6294
  // listeners to
6061
6295
  this.ready(activateControls);
6062
- player.on('controlsenabled', activateControls);
6063
- player.on('controlsdisabled', deactivateControls);
6296
+ this.on(player, 'controlsenabled', activateControls);
6297
+ this.on(player, 'controlsdisabled', this.removeControlsListeners);
6064
6298
 
6065
6299
  // if we're loading the playback object after it has started loading or playing the
6066
6300
  // video (often with autoplay on) then the loadstart event has already fired and we
@@ -6191,10 +6425,12 @@ vjs.MediaTechController.prototype.stopTrackingProgress = function(){ clearInterv
6191
6425
 
6192
6426
  /*! Time Tracking -------------------------------------------------------------- */
6193
6427
  vjs.MediaTechController.prototype.manualTimeUpdatesOn = function(){
6428
+ var player = this.player_;
6429
+
6194
6430
  this.manualTimeUpdates = true;
6195
6431
 
6196
- this.player().on('play', vjs.bind(this, this.trackCurrentTime));
6197
- this.player().on('pause', vjs.bind(this, this.stopTrackingCurrentTime));
6432
+ this.on(player, 'play', this.trackCurrentTime);
6433
+ this.on(player, 'pause', this.stopTrackingCurrentTime);
6198
6434
  // timeupdate is also called by .currentTime whenever current time is set
6199
6435
 
6200
6436
  // Watch for native timeupdate event
@@ -6297,8 +6533,11 @@ vjs.Html5 = vjs.MediaTechController.extend({
6297
6533
 
6298
6534
  var source = options['source'];
6299
6535
 
6300
- // set the source if one was provided
6301
- if (source && this.el_.currentSrc !== source.src) {
6536
+ // Set the source if one is provided
6537
+ // 1) Check if the source is new (if not, we want to keep the original so playback isn't interrupted)
6538
+ // 2) Check to see if the network state of the tag was failed at init, and if so, reset the source
6539
+ // anyway so the error gets fired.
6540
+ if (source && (this.el_.currentSrc !== source.src) || (player.tag && player.tag.initNetworkState_ === 3)) {
6302
6541
  this.el_.src = source.src;
6303
6542
  }
6304
6543
 
@@ -6306,7 +6545,8 @@ vjs.Html5 = vjs.MediaTechController.extend({
6306
6545
  // Our goal should be to get the custom controls on mobile solid everywhere
6307
6546
  // so we can remove this all together. Right now this will block custom
6308
6547
  // controls on touch enabled laptops like the Chrome Pixel
6309
- if (vjs.TOUCH_ENABLED && player.options()['nativeControlsForTouch'] !== false) {
6548
+ if (vjs.TOUCH_ENABLED && player.options()['nativeControlsForTouch'] === true) {
6549
+ alert('useNativeControls');
6310
6550
  this.useNativeControls();
6311
6551
  }
6312
6552
 
@@ -6383,7 +6623,7 @@ vjs.Html5.prototype.createEl = function(){
6383
6623
  // Triggers removed using this.off when disposed
6384
6624
  vjs.Html5.prototype.setupTriggers = function(){
6385
6625
  for (var i = vjs.Html5.Events.length - 1; i >= 0; i--) {
6386
- vjs.on(this.el_, vjs.Html5.Events[i], vjs.bind(this, this.eventHandler));
6626
+ this.on(vjs.Html5.Events[i], this.eventHandler);
6387
6627
  }
6388
6628
  };
6389
6629
 
@@ -6476,16 +6716,16 @@ vjs.Html5.prototype.enterFullScreen = function(){
6476
6716
  var video = this.el_;
6477
6717
 
6478
6718
  if ('webkitDisplayingFullscreen' in video) {
6479
- this.one('webkitbeginfullscreen', vjs.bind(this, function(e) {
6719
+ this.one('webkitbeginfullscreen', function() {
6480
6720
  this.player_.isFullscreen(true);
6481
6721
 
6482
- this.one('webkitendfullscreen', vjs.bind(this, function(e) {
6722
+ this.one('webkitendfullscreen', function() {
6483
6723
  this.player_.isFullscreen(false);
6484
6724
  this.player_.trigger('fullscreenchange');
6485
- }));
6725
+ });
6486
6726
 
6487
6727
  this.player_.trigger('fullscreenchange');
6488
- }));
6728
+ });
6489
6729
  }
6490
6730
 
6491
6731
  if (video.paused && video.networkState <= video.HAVE_METADATA) {
@@ -6754,10 +6994,10 @@ vjs.Flash = vjs.MediaTechController.extend({
6754
6994
  // bugzilla bug: https://bugzilla.mozilla.org/show_bug.cgi?id=836786
6755
6995
  if (vjs.IS_FIREFOX) {
6756
6996
  this.ready(function(){
6757
- vjs.on(this.el(), 'mousemove', vjs.bind(this, function(){
6997
+ this.on('mousemove', function(){
6758
6998
  // since it's a custom event, don't bubble higher than the player
6759
6999
  this.player().trigger({ 'type':'mousemove', 'bubbles': false });
6760
- }));
7000
+ });
6761
7001
  });
6762
7002
  }
6763
7003
 
@@ -7876,7 +8116,7 @@ vjs.TextTrackMenuItem = vjs.MenuItem.extend({
7876
8116
  options['selected'] = track.dflt();
7877
8117
  vjs.MenuItem.call(this, player, options);
7878
8118
 
7879
- this.player_.on(track.kind() + 'trackchange', vjs.bind(this, this.update));
8119
+ this.on(player, track.kind() + 'trackchange', this.update);
7880
8120
  }
7881
8121
  });
7882
8122
 
@@ -8178,7 +8418,7 @@ vjs.obj.merge(vjs.ControlBar.prototype.options_['children'], {
8178
8418
  */
8179
8419
  vjs.JSON;
8180
8420
 
8181
- if (typeof window.JSON !== 'undefined' && window.JSON.parse === 'function') {
8421
+ if (typeof window.JSON !== 'undefined' && typeof window.JSON.parse === 'function') {
8182
8422
  vjs.JSON = window.JSON;
8183
8423
 
8184
8424
  } else {
@@ -1,6 +1,6 @@
1
1
  /*!
2
2
  Video.js Default Styles (http://videojs.com)
3
- Version 4.9.1
3
+ Version 4.10.0
4
4
  Create your own skin at http://designer.videojs.com
5
5
  */
6
6
  /* SKIN
@@ -880,6 +880,19 @@ body.vjs-full-window {
880
880
  padding: 0;
881
881
  width: 100%;
882
882
  }
883
+ /* Hide the poster after the video has started playing */
884
+ .video-js.vjs-has-started .vjs-poster {
885
+ display: none;
886
+ }
887
+ /* Don't hide the poster if we're playing audio */
888
+ .video-js.vjs-audio.vjs-has-started .vjs-poster {
889
+ display: block;
890
+ }
891
+ /* Hide the poster when controls are disabled because it's clickable
892
+ and the native poster can take over */
893
+ .video-js.vjs-controls-disabled .vjs-poster {
894
+ display: none;
895
+ }
883
896
  /* Hide the poster when native controls are used otherwise it covers them */
884
897
  .video-js.vjs-using-native-controls .vjs-poster {
885
898
  display: none;
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: videojs_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.9.1
4
+ version: 4.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Behan