Skip to content

Commit

Permalink
Allow poster to be changed after init. Fixes #525
Browse files Browse the repository at this point in the history
When poster() is called with a URL, fire a `posterchange` event to update the PosterImage source and update the video element attribute.

Trigger posterchange after updating the tech
Wait until the tech has updated its poster image before alerting components so they don't see the intermediate state in event handlers. Remove unused variable from PosterImage.createEl.

Don't create new img elements each time the poster is set on ie8
Create the img fallback for the poster during PosterImage initialization on ie8 so we can avoid having to check and possibly create it each time the src is set. Add a test to ensure that new elements are not appended to the poster component when the img fallback is in use and the src attribute is modified.

fixing a broken IE8 test, and adding a negative test, for poster switching.

modified the poster-switching test to accomodate IE8
added a negative test for an undefined poster

fixed the assertion message in the 'undefined' image case

fixed test breakage in Firefox and IE10 (quotes were not being handled properly in the test data)

testing:
ran the tests at the command line, and in Chrome, Firefox, IE8, IE10, Firefox and Safari
all passed
  • Loading branch information
dmlap authored and heff committed Nov 25, 2013
1 parent 1f06a0c commit 875fc2f
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 16 deletions.
3 changes: 3 additions & 0 deletions src/js/media/flash.js
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,9 @@ vjs.Flash.prototype.load = function(){
vjs.Flash.prototype.poster = function(){
this.el_.vjs_getProperty('poster');
};
vjs.Flash.prototype.setPoster = function(){
// poster images are not handled by the Flash tech so make this a no-op
};

vjs.Flash.prototype.buffered = function(){
return vjs.createTimeRange(0, this.el_.vjs_getProperty('buffered'));
Expand Down
3 changes: 3 additions & 0 deletions src/js/media/html5.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,9 @@ vjs.Html5.prototype.src = function(src){ this.el_.src = src; };
vjs.Html5.prototype.load = function(){ this.el_.load(); };
vjs.Html5.prototype.currentSrc = function(){ return this.el_.currentSrc; };

vjs.Html5.prototype.poster = function(){ return this.el_.poster; };
vjs.Html5.prototype.setPoster = function(val){ this.el_.poster = val; };

vjs.Html5.prototype.preload = function(){ return this.el_.preload; };
vjs.Html5.prototype.setPreload = function(val){ this.el_.preload = val; };

Expand Down
17 changes: 11 additions & 6 deletions src/js/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -1093,11 +1093,18 @@ vjs.Player.prototype.poster_;
* @return {vjs.Player} self when setting
*/
vjs.Player.prototype.poster = function(src){
if (src !== undefined) {
this.poster_ = src;
return this;
if (src === undefined) {
return this.poster_;
}
return this.poster_;

// update the internal poster variable
this.poster_ = src;

// update the tech's poster
this.techCall('setPoster', src);

// alert components that the poster has been set
this.trigger('posterchange');
};

/**
Expand Down Expand Up @@ -1381,5 +1388,3 @@ vjs.Player.prototype.listenForUserActivity = function(){
}

})();


53 changes: 43 additions & 10 deletions src/js/poster.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,65 @@ vjs.PosterImage = vjs.Button.extend({
init: function(player, options){
vjs.Button.call(this, player, options);

if (player.poster()) {
this.src(player.poster());
}

if (!player.poster() || !player.controls()) {
this.hide();
}

player.on('posterchange', vjs.bind(this, function(){
this.src(player.poster());
}));

player.on('play', vjs.bind(this, this.hide));
}
});

vjs.PosterImage.prototype.createEl = function(){
var el = vjs.createEl('div', {
className: 'vjs-poster',
className: 'vjs-poster',

// Don't want poster to be tabbable.
tabIndex: -1
});

if (!('backgroundSize' in el.style)) {
// setup an img element as a fallback for IE8
el.appendChild(vjs.createEl('img'));
}

// Don't want poster to be tabbable.
tabIndex: -1
}),
poster = this.player_.poster();
return el;
};

vjs.PosterImage.prototype.src = function(url){
var el = this.el(), imgFallback;

if (poster) {
// getter
if (url === undefined) {
if ('backgroundSize' in el.style) {
el.style.backgroundImage = 'url("' + poster + '")';
} else {
el.appendChild(vjs.createEl('img', { src: poster }));
if (el.style.backgroundImage) {
// parse the poster url from the background-image value
return (/url\(['"]?(.*)['"]?\)/).exec(el.style.backgroundImage)[1];
}

// the poster is not specified
return '';
}
return el.querySelector('img').src;
}

return el;
// setter
// To ensure the poster image resizes while maintaining its original aspect
// ratio, use a div with `background-size` when available. For browsers that
// do not support `background-size` (e.g. IE8), fall back on using a regular
// img element.
if ('backgroundSize' in el.style) {
el.style.backgroundImage = 'url("' + url + '")';
} else {
el.querySelector('img').src = url;
}
};

vjs.PosterImage.prototype.onClick = function(){
Expand Down
61 changes: 61 additions & 0 deletions test/unit/controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,64 @@ test('calculateDistance should use changedTouches, if available', function() {

equal(slider.calculateDistance(event), 0.5, 'we should have touched exactly in the center, so, the ratio should be half');
});

test('the poster getter should work correctly even when background-size is not available', function() {
var noop = function(){},
url = 'http://example.com/poster.jpg',
player = {
controls: noop,
id: noop,
on: noop,
ready: noop,
poster: function(){
return url;
}
},
poster = new vjs.PosterImage(player);

// mock out el() to return an element that behaves like IE8
poster.el = function(){
return {
style: {},
querySelector: function() {
return {
src: url
};
}
};
};

equal(url, poster.src(), 'the poster url is returned');
});

test('the poster setter should reuse an img when background-size is not available', function() {
var noop = function(){},
url = 'http://example.com/poster.jpg',
img = {},
player = {
controls: noop,
id: noop,
on: noop,
ready: noop,
poster: function(){
return url;
}
},
poster = new vjs.PosterImage(player);

// mock out el() to return an element that behaves like IE8
poster.el = function(){
return {
appendChild: function() {
ok(false, 'a new img should not be added to the poster');
},
style: {},
querySelector: function() {
return img;
}
};
};

poster.src(url);
equal(img.src, url, 'the img is reused for the new src');
});
4 changes: 4 additions & 0 deletions test/unit/mediafaker.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ vjs.MediaFaker.prototype.createEl = function(){
return el;
};

// fake a poster attribute to mimic the video element
vjs.MediaFaker.prototype.poster = function(){ return this.el().poster; };
vjs.MediaFaker.prototype['setPoster'] = function(val){ this.el().poster = val; };

vjs.MediaFaker.prototype.currentTime = function(){ return 0; };
vjs.MediaFaker.prototype.seeking = function(){ return false; };
vjs.MediaFaker.prototype.volume = function(){ return 0; };
Expand Down
75 changes: 75 additions & 0 deletions test/unit/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,81 @@ test('should transfer the poster attribute unmodified', function(){
player.dispose();
});

test('should allow the poster to be changed after init', function() {
var tag, fixture, updatedPoster, player, posterElement, posterElementUrl, imageElement;
tag = PlayerTest.makeTag();
tag.setAttribute('poster', 'http://example.com/poster.jpg');
fixture = document.getElementById('qunit-fixture');

fixture.appendChild(tag);
player = new vjs.Player(tag, {
'techOrder': ['mediaFaker']
});

updatedPoster = 'http://example.com/updated-poster.jpg';
player.poster(updatedPoster);

strictEqual(player.poster(), updatedPoster, 'the updated poster is returned');
strictEqual(player.tech.el().poster, updatedPoster, 'the poster attribute is updated');

posterElement = document.querySelector('.vjs-poster');
ok(posterElement, 'vjs-poster element should exist');

if (!('backgroundSize' in posterElement.style)) {
imageElement = document.getElementsByTagName('img')[0];
ok(imageElement, 'image element should exist if the poster div has no background-size CSS property');
var imageElementSrc = imageElement.getAttribute('src');
strictEqual(imageElementSrc,
updatedPoster,
'the poster img src is updated');
} else {
posterElementUrl = posterElement.style.backgroundImage.replace(/"/g, '');
strictEqual(posterElementUrl,
'url(' + updatedPoster + ')',
'the poster div background is updated');
}

player.dispose();
});

test('should ignore setting an undefined poster after init', function() {
var tag, fixture, updatedPoster, originalPoster, player, posterElement, posterElementUrl, imageElement;
tag = PlayerTest.makeTag();
tag.setAttribute('poster', 'http://example.com/poster.jpg');
fixture = document.getElementById('qunit-fixture');

fixture.appendChild(tag);
player = new vjs.Player(tag, {
'techOrder': ['mediaFaker']
});

originalPoster = player.poster();

updatedPoster = undefined;
player.poster(updatedPoster);
strictEqual(player.poster(), originalPoster, 'the original poster is returned');
strictEqual(player.tech.el().poster, originalPoster, 'the poster attribute is unchanged');

posterElement = document.querySelector('.vjs-poster');
ok(posterElement, 'vjs-poster element should exist');

if (!('backgroundSize' in posterElement.style)) {
imageElement = document.getElementsByTagName('img')[0];
ok(imageElement, 'image element should exist if the poster div has no background-size CSS property');
var imageElementSrc = imageElement.getAttribute('src');
strictEqual(imageElementSrc,
originalPoster,
'the poster img src is not updated');
} else {
posterElementUrl = posterElement.style.backgroundImage.replace(/"/g, '');
strictEqual(posterElementUrl,
'url(' + originalPoster + ')',
'the poster div background is unchanged');
}

player.dispose();
});

test('should load a media controller', function(){
var player = PlayerTest.makePlayer({
preload: 'none',
Expand Down

0 comments on commit 875fc2f

Please sign in to comment.