Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(player): ingest a player div for videojs #3856

Merged
merged 12 commits into from
Dec 19, 2016
14 changes: 11 additions & 3 deletions src/js/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -490,8 +490,15 @@ class Player extends Component {
* The DOM element that gets created.
*/
createEl() {
const el = this.el_ = super.createEl('div');
const tag = this.tag;
let el;
const playerElIngest = this.playerElIngest_ = tag.parentNode && tag.parentNode.hasAttribute('data-vjs-player');

if (playerElIngest) {
el = this.el_ = tag.parentNode;
} else {
el = this.el_ = super.createEl('div');
}

// Remove width/height attrs from tag so CSS can make it 100% width/height
tag.removeAttribute('width');
Expand All @@ -505,7 +512,7 @@ class Player extends Component {
// workaround so we don't totally break IE7
// http://stackoverflow.com/questions/3653444/css-styles-not-applied-on-dynamic-elements-in-internet-explorer-7
if (attr === 'class') {
el.className = attrs[attr];
el.className += ' ' + attrs[attr];
} else {
el.setAttribute(attr, attrs[attr]);
}
Expand Down Expand Up @@ -555,7 +562,7 @@ class Player extends Component {
tag.initNetworkState_ = tag.networkState;

// Wrap video tag in div (el/box) container
if (tag.parentNode) {
if (tag.parentNode && !playerElIngest) {
tag.parentNode.insertBefore(el, tag);
}

Expand Down Expand Up @@ -836,6 +843,7 @@ class Player extends Component {
'muted': this.options_.muted,
'poster': this.poster(),
'language': this.language(),
'playerElIngest': this.playerElIngest_ || false,
'vtt.js': this.options_['vtt.js']
}, this.options_[techName.toLowerCase()]);

Expand Down
10 changes: 8 additions & 2 deletions src/js/tech/html5.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,15 +185,21 @@ class Html5 extends Tech {
// Check if this browser supports moving the element into the box.
// On the iPhone video will break if you move the element,
// So we have to create a brand new element.
if (!el || this.movingMediaElementInDOM === false) {
// If we ingested the player div, we do not need to move the media element.
if (!el ||
!(this.options_.playerElIngest ||
this.movingMediaElementInDOM)) {

// If the original tag is still there, clone and remove it.
if (el) {
const clone = el.cloneNode(true);

el.parentNode.insertBefore(clone, el);
if (el.parentNode) {
el.parentNode.insertBefore(clone, el);
}
Html5.disposeMediaElement(el);
el = clone;

} else {
el = document.createElement('video');

Expand Down
87 changes: 87 additions & 0 deletions test/unit/player.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -830,6 +830,93 @@ QUnit.test('should restore attributes from the original video tag when creating
assert.equal(el.getAttribute('webkit-playsinline'), '', 'webkit-playsinline attribute was set properly');
});

QUnit.test('if tag exists and movingMediaElementInDOM, re-use the tag', function(assert) {
// simulate attributes stored from the original tag
const tag = Dom.createEl('video');

tag.setAttribute('preload', 'auto');
tag.setAttribute('autoplay', '');
tag.setAttribute('webkit-playsinline', '');

const html5Mock = {
options_: {
tag,
playerElIngest: false
},
movingMediaElementInDOM: true
};

// set options that should override tag attributes
html5Mock.options_.preload = 'none';

// create the element
const el = Html5.prototype.createEl.call(html5Mock);

assert.equal(el.getAttribute('preload'), 'none', 'attribute was successful overridden by an option');
assert.equal(el.getAttribute('autoplay'), '', 'autoplay attribute was set properly');
assert.equal(el.getAttribute('webkit-playsinline'), '', 'webkit-playsinline attribute was set properly');

assert.equal(el, tag, 'we have re-used the tag as expected');
});

QUnit.test('if tag exists and *not* movingMediaElementInDOM, create a new tag', function(assert) {
// simulate attributes stored from the original tag
const tag = Dom.createEl('video');

tag.setAttribute('preload', 'auto');
tag.setAttribute('autoplay', '');
tag.setAttribute('webkit-playsinline', '');

const html5Mock = {
options_: {
tag,
playerElIngest: false
},
movingMediaElementInDOM: false
};

// set options that should override tag attributes
html5Mock.options_.preload = 'none';

// create the element
const el = Html5.prototype.createEl.call(html5Mock);

assert.equal(el.getAttribute('preload'), 'none', 'attribute was successful overridden by an option');
assert.equal(el.getAttribute('autoplay'), '', 'autoplay attribute was set properly');
assert.equal(el.getAttribute('webkit-playsinline'), '', 'webkit-playsinline attribute was set properly');

assert.notEqual(el, tag, 'we have not re-used the tag as expected');
});

QUnit.test('if tag exists and *not* movingMediaElementInDOM, but playerElIngest re-use tag', function(assert) {
// simulate attributes stored from the original tag
const tag = Dom.createEl('video');

tag.setAttribute('preload', 'auto');
tag.setAttribute('autoplay', '');
tag.setAttribute('webkit-playsinline', '');

const html5Mock = {
options_: {
tag,
playerElIngest: true
},
movingMediaElementInDOM: false
};

// set options that should override tag attributes
html5Mock.options_.preload = 'none';

// create the element
const el = Html5.prototype.createEl.call(html5Mock);

assert.equal(el.getAttribute('preload'), 'none', 'attribute was successful overridden by an option');
assert.equal(el.getAttribute('autoplay'), '', 'autoplay attribute was set properly');
assert.equal(el.getAttribute('webkit-playsinline'), '', 'webkit-playsinline attribute was set properly');

assert.equal(el, tag, 'we have re-used the tag as expected');
});

QUnit.test('should honor default inactivity timeout', function(assert) {
const clock = sinon.useFakeTimers();

Expand Down
102 changes: 102 additions & 0 deletions test/unit/video.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ QUnit.test('should return a video player instance', function(assert) {
const player2 = videojs(tag2, { techOrder: ['techFaker'] });

assert.ok(player2.id() === 'test_vid_id2', 'created player from element');

player.dispose();
player2.dispose();
});

QUnit.test('should return a video player instance from el html5 tech', function(assert) {
Expand All @@ -66,6 +69,9 @@ QUnit.test('should return a video player instance from el html5 tech', function(
const player2 = videojs(tag2, { techOrder: ['techFaker'] });

assert.ok(player2.id() === 'test_vid_id2', 'created player from element');

player.dispose();
player2.dispose();
});

QUnit.test('should return a video player instance from el techfaker', function(assert) {
Expand All @@ -91,6 +97,9 @@ QUnit.test('should return a video player instance from el techfaker', function(a
const player2 = videojs(tag2, { techOrder: ['techFaker'] });

assert.ok(player2.id() === 'test_vid_id2', 'created player from element');

player.dispose();
player2.dispose();
});

QUnit.test('should add the value to the languages object', function(assert) {
Expand Down Expand Up @@ -166,3 +175,96 @@ QUnit.test('should expose DOM functions', function(assert) {
`videojs.${vjsName} is a reference to Dom.${domName}`);
});
});

QUnit.test('ingest player div if data-vjs-player attribute is present on video parentNode', function(assert) {
const fixture = document.querySelector('#qunit-fixture');

fixture.innerHTML = `
<div data-vjs-player class="foo">
<video id="test_vid_id">
<source src="http://example.com/video.mp4" type="video/mp4"></source>
</video>
</div>
`;

const playerDiv = document.querySelector('.foo');
const vid = document.querySelector('#test_vid_id');

const player = videojs(vid, {
techOrder: ['html5']
});

assert.equal(player.el(), playerDiv, 'we re-used the given div');
assert.ok(player.hasClass('foo'), 'keeps any classes that were around previously');

player.dispose();
});

QUnit.test('ingested player div should not create a new tag for movingMediaElementInDOM', function(assert) {
const Html5 = videojs.getTech('Html5');
const oldIS = Html5.isSupported;
const oldMoving = Html5.prototype.movingMediaElementInDOM;
const oldCPT = Html5.nativeSourceHandler.canPlayType;
const fixture = document.querySelector('#qunit-fixture');

fixture.innerHTML = `
<div data-vjs-player class="foo">
<video id="test_vid_id">
<source src="http://example.com/video.mp4" type="video/mp4"></source>
</video>
</div>
`;
Html5.prototype.movingMediaElementInDOM = false;
Html5.isSupported = () => true;
Html5.nativeSourceHandler.canPlayType = () => true;

const playerDiv = document.querySelector('.foo');
const vid = document.querySelector('#test_vid_id');

const player = videojs(vid, {
techOrder: ['html5']
});

assert.equal(player.el(), playerDiv, 'we re-used the given div');
assert.equal(player.tech_.el(), vid, 'we re-used the video element');
assert.ok(player.hasClass('foo'), 'keeps any classes that were around previously');

player.dispose();
Html5.prototype.movingMediaElementInDOM = oldMoving;
Html5.isSupported = oldIS;
Html5.nativeSourceHandler.canPlayType = oldCPT;
});

QUnit.test('should create a new tag for movingMediaElementInDOM', function(assert) {
const Html5 = videojs.getTech('Html5');
const oldMoving = Html5.prototype.movingMediaElementInDOM;
const oldCPT = Html5.nativeSourceHandler.canPlayType;
const fixture = document.querySelector('#qunit-fixture');
const oldIS = Html5.isSupported;

fixture.innerHTML = `
<div class="foo">
<video id="test_vid_id">
<source src="http://example.com/video.mp4" type="video/mp4"></source>
</video>
</div>
`;
Html5.prototype.movingMediaElementInDOM = false;
Html5.isSupported = () => true;
Html5.nativeSourceHandler.canPlayType = () => true;

const playerDiv = document.querySelector('.foo');
const vid = document.querySelector('#test_vid_id');

const player = videojs(vid, {
techOrder: ['html5']
});

assert.notEqual(player.el(), playerDiv, 'we used a new div');
assert.notEqual(player.tech_.el(), vid, 'we a new video element');

player.dispose();
Html5.prototype.movingMediaElementInDOM = oldMoving;
Html5.isSupported = oldIS;
Html5.nativeSourceHandler.canPlayType = oldCPT;
});