From a589396cde0606ac2fdcc25bb104160ce2c870ab Mon Sep 17 00:00:00 2001 From: Pablo Schklowsky Date: Wed, 17 Feb 2016 18:00:53 -0500 Subject: [PATCH 01/22] Initial amp-jwplayer extension --- examples/jwplayer.amp.html | 23 ++++ extensions/amp-jwplayer/0.1/amp-jwplayer.js | 71 ++++++++++++ .../0.1/test/test-amp-jwplayer.js | 58 ++++++++++ extensions/amp-jwplayer/amp-jwplayer.md | 103 ++++++++++++++++++ gulpfile.js | 2 + 5 files changed, 257 insertions(+) create mode 100644 examples/jwplayer.amp.html create mode 100644 extensions/amp-jwplayer/0.1/amp-jwplayer.js create mode 100644 extensions/amp-jwplayer/0.1/test/test-amp-jwplayer.js create mode 100644 extensions/amp-jwplayer/amp-jwplayer.md diff --git a/examples/jwplayer.amp.html b/examples/jwplayer.amp.html new file mode 100644 index 000000000000..48ef8ce04489 --- /dev/null +++ b/examples/jwplayer.amp.html @@ -0,0 +1,23 @@ + + + + + JWPlayer AMP Example + + + + + + + + +

JWPlayer AMP

+ + + + + + diff --git a/extensions/amp-jwplayer/0.1/amp-jwplayer.js b/extensions/amp-jwplayer/0.1/amp-jwplayer.js new file mode 100644 index 000000000000..3a79b3861ec5 --- /dev/null +++ b/extensions/amp-jwplayer/0.1/amp-jwplayer.js @@ -0,0 +1,71 @@ +/** + * Copyright 2015 Longtail Ad Solutions Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {isLayoutSizeDefined} from '../../../src/layout'; +import {loadPromise} from '../../../src/event-helper'; +import {addParamsToUrl} from '../../../src/url'; +import {dashToCamelCase} from '../../../src/string'; + +class AmpJWPlayer extends AMP.BaseElement { + + /** @override */ + createdCallback() { + this.preconnect.url('https://content.jwplatform.com'); + this.preconnect.url('https://p.jwpcdn.com'); + } + + /** @override */ + isLayoutSupported(layout) { + return isLayoutSizeDefined(layout); + } + + /** @override */ + layoutCallback() { + const width = this.element.getAttribute('width'); + const height = this.element.getAttribute('height'); + const contentid = AMP.assert( + (this.element.getAttribute('data-playlist-id') || + this.element.getAttribute('data-media-id')), + 'Either the data-media-id or the data-playlist-id attributes must be specified for %s', + this.element); + const playerid = AMP.assert( + this.element.getAttribute('data-player-id'), + 'The data-player-id attribute is required for %s', + this.element); + + const iframe = document.createElement('iframe'); + let src = `https://content.jwplatform.com/players/${contentid}-${playerid}.html`; + + iframe.setAttribute('frameborder', '0'); + iframe.setAttribute('allowfullscreen', 'true'); + iframe.src = src; + this.applyFillContent(iframe); + iframe.width = width; + iframe.height = height; + this.element.appendChild(iframe); + /** @private {?Element} */ + this.iframe_ = iframe; + return loadPromise(iframe); + } + + /** @override */ + documentInactiveCallback() { + // TODO: implement inactiveCallback + return false; + } +}; + +AMP.registerElement('amp-jwplayer', AmpJWPlayer); diff --git a/extensions/amp-jwplayer/0.1/test/test-amp-jwplayer.js b/extensions/amp-jwplayer/0.1/test/test-amp-jwplayer.js new file mode 100644 index 000000000000..08ca85377166 --- /dev/null +++ b/extensions/amp-jwplayer/0.1/test/test-amp-jwplayer.js @@ -0,0 +1,58 @@ +/** + * Copyright 2015 Longtail Ad Solutions Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {createIframePromise} from '../../../../testing/iframe'; +require('../amp-jwplayer'); +import {adopt} from '../../../../src/runtime'; +import {parseUrl} from '../../../../src/url'; + +adopt(window); + +describe('amp-jwplayer', () => { + + function getjwplayer(attributes, opt_responsive) { + return createIframePromise().then(iframe => { + const jw = iframe.doc.createElement('amp-jwplayer'); + for (const key in attributes) { + jw.setAttribute(key, attributes[key]); + } + jw.setAttribute('width', '111'); + jw.setAttribute('height', '222'); + if (opt_responsive) { + jw.setAttribute('layout', 'responsive'); + } + iframe.doc.body.appendChild(jw); + jw.implementation_.layoutCallback(); + return jw; + }); + } + + it('renders', () => { + return getjwplayer({ + 'data-media-id': 'Wferorsv', + 'data-player-id': 'sDZEo0ea' + }).then(jw => { + const iframe = jw.querySelector('iframe'); + expect(iframe).to.not.be.null; + expect(iframe.tagName).to.equal('IFRAME'); + expect(iframe.src).to.equal( + 'https://content.jwplatform.com/players/Wferorsv-sDZEo0ea.html'); + expect(iframe.getAttribute('width')).to.equal('111'); + expect(iframe.getAttribute('height')).to.equal('222'); + }); + }); + +}); diff --git a/extensions/amp-jwplayer/amp-jwplayer.md b/extensions/amp-jwplayer/amp-jwplayer.md new file mode 100644 index 000000000000..244a3e6e1d0d --- /dev/null +++ b/extensions/amp-jwplayer/amp-jwplayer.md @@ -0,0 +1,103 @@ + + +### `amp-jwplayer` + + + + + + + + + + + + + + + + + + +
DescriptionAn amp-jwplayer component displays a cloud-hosted JW Player.
Availability?
Required Script<script async custom-element="amp-jwplayer" src="https://cdn.ampproject.org/v0/amp-jwplayer-0.1.js"></script>
Examplesjwplayer.amp.html
+ +The following lists validation errors specific to the `amp-jwplayer` tag +(see also `amp-jwplayer` in the [AMP validator specification](https://github.com/ampproject/amphtml/blob/master/validator/validator.protoascii): + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Validation ErrorDescription
TAG_REQUIRED_BY_MISSINGError thrown when required amp-jwplayer extension .js script tag is missing or incorrect.
MANDATORY_ATTR_MISSINGError thrown when data-account attribute is missing.
IMPLIED_LAYOUT_INVALIDError thrown when implied layout is set to CONTAINER; this layout type isn't supported.
MANDATORY_ONEOF_ATTR_MISSINGError thrown when either the data-media-id or data-playlist-id attributes are missing.
IMPLIED_LAYOUT_INVALIDError thrown when implied layout is set to CONTAINER; this layout type isn't supported.
SPECIFIED_LAYOUT_INVALIDError thrown when specified layout is set to CONTAINER; this layout type isn't supported.
INVALID_PROPERTY_VALUE_IN_ATTR_VALUEError thrown when invalid value is given for attributes height or width. For example, height=auto triggers this error for all supported layout types, with the exception of NODISPLAY.
+ +#### Example + +The `width` and `height` attributes determine the aspect ratio of the player embedded in responsive layouts. + +Example: + +```html + + +``` + +#### Attributes + +**data-player-id** + +JW Platform player id. This is an 8-digit alphanumeric sequence that can be found in the [Players](https://dashboard.jwplayer.com/#/players) section in your JW Player Dashboard. + +**data-media-id** + +The JW Platform media id. This is an 8-digit alphanumeric sequence that can be found in the [Content](https://dashboard.jwplayer.com/#/content) section in your JW Player Dashboard. + +**data-playlist-id** + +The JW Platform playlist id. This is an 8-digit alphanumeric sequence that can be found in the [Playlists](https://dashboard.jwplayer.com/#/content/playlists) section in your JW Player Dashboard. If both a `data-playlist-id` and `data-media-id` are specified, `data-playlist-id` takes precedence. diff --git a/gulpfile.js b/gulpfile.js index ccdf92b9c35b..a5d4da23f2de 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -89,6 +89,7 @@ function buildExtensions(options) { buildExtension('amp-iframe', '0.1', false, options); buildExtension('amp-image-lightbox', '0.1', true, options); buildExtension('amp-instagram', '0.1', false, options); + buildExtension('amp-jwplayer', '0.1', false, options); buildExtension('amp-lightbox', '0.1', false, options); buildExtension('amp-list', '0.1', false, options); buildExtension('amp-mustache', '0.1', false, options); @@ -342,6 +343,7 @@ function buildExamples(watch) { buildExample('font.amp.html'); buildExample('facebook.amp.html'); buildExample('instagram.amp.html'); + buildExample('jwplayer.amp.html'); buildExample('pinterest.amp.html'); buildExample('released.amp.html'); buildExample('twitter.amp.html'); From 3666afaf09323d570e4b41ad684aeffb13321c68 Mon Sep 17 00:00:00 2001 From: Pablo Schklowsky Date: Thu, 18 Feb 2016 13:28:39 -0500 Subject: [PATCH 02/22] Some more examples and tests --- examples/jwplayer.amp.html | 12 +++- .../0.1/test/test-amp-jwplayer.js | 56 ++++++++++++++++--- extensions/amp-jwplayer/amp-jwplayer.md | 12 ++++ 3 files changed, 71 insertions(+), 9 deletions(-) diff --git a/examples/jwplayer.amp.html b/examples/jwplayer.amp.html index 48ef8ce04489..fcaab564f442 100644 --- a/examples/jwplayer.amp.html +++ b/examples/jwplayer.amp.html @@ -11,7 +11,9 @@ -

JWPlayer AMP

+

JWPlayer AMP Examples

+ +

Responsive

JWPlayer AMP layout="responsive" width="16" height="9"> +

Non-responsive, with a playlist

+ + + + diff --git a/extensions/amp-jwplayer/0.1/test/test-amp-jwplayer.js b/extensions/amp-jwplayer/0.1/test/test-amp-jwplayer.js index 08ca85377166..24d61888361d 100644 --- a/extensions/amp-jwplayer/0.1/test/test-amp-jwplayer.js +++ b/extensions/amp-jwplayer/0.1/test/test-amp-jwplayer.js @@ -23,17 +23,15 @@ adopt(window); describe('amp-jwplayer', () => { - function getjwplayer(attributes, opt_responsive) { + function getjwplayer(attributes) { return createIframePromise().then(iframe => { const jw = iframe.doc.createElement('amp-jwplayer'); for (const key in attributes) { jw.setAttribute(key, attributes[key]); } - jw.setAttribute('width', '111'); - jw.setAttribute('height', '222'); - if (opt_responsive) { - jw.setAttribute('layout', 'responsive'); - } + jw.setAttribute('width', '320'); + jw.setAttribute('height', '180'); + jw.setAttribute('layout', 'responsive'); iframe.doc.body.appendChild(jw); jw.implementation_.layoutCallback(); return jw; @@ -50,8 +48,50 @@ describe('amp-jwplayer', () => { expect(iframe.tagName).to.equal('IFRAME'); expect(iframe.src).to.equal( 'https://content.jwplatform.com/players/Wferorsv-sDZEo0ea.html'); - expect(iframe.getAttribute('width')).to.equal('111'); - expect(iframe.getAttribute('height')).to.equal('222'); + expect(iframe.getAttribute('width')).to.equal('320'); + expect(iframe.getAttribute('height')).to.equal('180'); + }); + }); + + it('renders with a playlist', () => { + return getjwplayer({ + 'data-playlist-id': '482jsTAr', + 'data-player-id': 'sDZEo0ea' + }).then(jw => { + const iframe = jw.querySelector('iframe'); + expect(iframe).to.not.be.null; + expect(iframe.tagName).to.equal('IFRAME'); + expect(iframe.src).to.equal( + 'https://content.jwplatform.com/players/482jsTAr-sDZEo0ea.html'); + }); + }); + + it('fails if no media is specified', () => { + return getjwplayer({ + 'data-player-id': 'sDZEo0ea' + }).should.eventually.be.rejectedWith( + /Either the data-media-id or the data-playlist-id attributes must be specified for/ + ); + }); + + it('fails if no player is specified', () => { + return getjwplayer({ + 'data-media-id': 'Wferorsv', + }).should.eventually.be.rejectedWith( + /The data-player-id attribute is required for/ + ); + }); + + it('renders with a bad playlist', () => { + return getjwplayer({ + 'data-playlist-id': 'zzz', + 'data-player-id': 'sDZEo0ea' + }).then(jw => { + const iframe = jw.querySelector('iframe'); + expect(iframe).to.not.be.null; + expect(iframe.tagName).to.equal('IFRAME'); + expect(iframe.src).to.equal( + 'https://content.jwplatform.com/players/zzz-sDZEo0ea.html'); }); }); diff --git a/extensions/amp-jwplayer/amp-jwplayer.md b/extensions/amp-jwplayer/amp-jwplayer.md index 244a3e6e1d0d..b5932071139f 100644 --- a/extensions/amp-jwplayer/amp-jwplayer.md +++ b/extensions/amp-jwplayer/amp-jwplayer.md @@ -88,6 +88,18 @@ Example:
``` +Non-responsive layout is also supported. + +Example: + +```html + + +``` + #### Attributes **data-player-id** From 0e42170e53fb68718a5395c9ed73b0351e70fd65 Mon Sep 17 00:00:00 2001 From: Pablo Schklowsky Date: Thu, 18 Feb 2016 19:22:21 -0500 Subject: [PATCH 03/22] Pause player on `documentInactiveCallback` --- extensions/amp-jwplayer/0.1/amp-jwplayer.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/extensions/amp-jwplayer/0.1/amp-jwplayer.js b/extensions/amp-jwplayer/0.1/amp-jwplayer.js index 3a79b3861ec5..ec2f53c00e94 100644 --- a/extensions/amp-jwplayer/0.1/amp-jwplayer.js +++ b/extensions/amp-jwplayer/0.1/amp-jwplayer.js @@ -63,7 +63,10 @@ class AmpJWPlayer extends AMP.BaseElement { /** @override */ documentInactiveCallback() { - // TODO: implement inactiveCallback + if (this.iframe_ && this.iframe_.contentWindow) { + // The /players page can respond to "play" and "pause" commands from the iframe's parent + this.iframe_.contentWindow./*OK*/postMessage('pause', 'https://content.jwplatform.com'); + } return false; } }; From 1f79c865f3408fd08f8a65e9d0c7559d80141dc0 Mon Sep 17 00:00:00 2001 From: Pablo Schklowsky Date: Thu, 18 Feb 2016 20:07:14 -0500 Subject: [PATCH 04/22] Availability "Stable" --- extensions/amp-jwplayer/amp-jwplayer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/amp-jwplayer/amp-jwplayer.md b/extensions/amp-jwplayer/amp-jwplayer.md index b5932071139f..e52ade4a626f 100644 --- a/extensions/amp-jwplayer/amp-jwplayer.md +++ b/extensions/amp-jwplayer/amp-jwplayer.md @@ -23,7 +23,7 @@ limitations under the License. Availability - ? + Stable Required Script From 6f282a40c6097aa6c1d5b72abc7b3f6112f93c8c Mon Sep 17 00:00:00 2001 From: Pablo Schklowsky Date: Thu, 18 Feb 2016 20:12:36 -0500 Subject: [PATCH 05/22] Add amp-jwplayer to validator.protoascii --- validator/validator.protoascii | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/validator/validator.protoascii b/validator/validator.protoascii index f4ae43a59cec..4f5cfa3ad25c 100644 --- a/validator/validator.protoascii +++ b/validator/validator.protoascii @@ -1911,6 +1911,24 @@ tags: { # amp-install-serviceworker mandatory: true } } +tags: { # amp-jwplayer + name: "script" + mandatory_parent: "head" + detail: "amp-jwplayer extension .js script" + spec_url: "https://github.com/ampproject/amphtml/blob/master/extensions/amp-jwplayer/amp-jwplayer.md" + attrs: { + name: "custom-element" + value: "amp-jwplayer" + mandatory: true + dispatch_key: true + } + attrs: { name: "async" value: "" mandatory: true } + attrs: { + name: "src" + value_regex: "https://cdn\\.ampproject\\.org/v0/amp-jwplayer-(latest|0\\.1).js" + mandatory: true + } +} tags: { # amp-lightbox name: "script" mandatory_parent: "head" From bbc2390d6112ef4743ced2a8e4a431b86eae9fb2 Mon Sep 17 00:00:00 2001 From: Pablo Schklowsky Date: Wed, 17 Feb 2016 18:00:53 -0500 Subject: [PATCH 06/22] Initial amp-jwplayer extension --- examples/jwplayer.amp.html | 23 ++++ extensions/amp-jwplayer/0.1/amp-jwplayer.js | 71 ++++++++++++ .../0.1/test/test-amp-jwplayer.js | 58 ++++++++++ extensions/amp-jwplayer/amp-jwplayer.md | 103 ++++++++++++++++++ gulpfile.js | 2 + 5 files changed, 257 insertions(+) create mode 100644 examples/jwplayer.amp.html create mode 100644 extensions/amp-jwplayer/0.1/amp-jwplayer.js create mode 100644 extensions/amp-jwplayer/0.1/test/test-amp-jwplayer.js create mode 100644 extensions/amp-jwplayer/amp-jwplayer.md diff --git a/examples/jwplayer.amp.html b/examples/jwplayer.amp.html new file mode 100644 index 000000000000..48ef8ce04489 --- /dev/null +++ b/examples/jwplayer.amp.html @@ -0,0 +1,23 @@ + + + + + JWPlayer AMP Example + + + + + + + + +

JWPlayer AMP

+ + + + + + diff --git a/extensions/amp-jwplayer/0.1/amp-jwplayer.js b/extensions/amp-jwplayer/0.1/amp-jwplayer.js new file mode 100644 index 000000000000..3a79b3861ec5 --- /dev/null +++ b/extensions/amp-jwplayer/0.1/amp-jwplayer.js @@ -0,0 +1,71 @@ +/** + * Copyright 2015 Longtail Ad Solutions Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {isLayoutSizeDefined} from '../../../src/layout'; +import {loadPromise} from '../../../src/event-helper'; +import {addParamsToUrl} from '../../../src/url'; +import {dashToCamelCase} from '../../../src/string'; + +class AmpJWPlayer extends AMP.BaseElement { + + /** @override */ + createdCallback() { + this.preconnect.url('https://content.jwplatform.com'); + this.preconnect.url('https://p.jwpcdn.com'); + } + + /** @override */ + isLayoutSupported(layout) { + return isLayoutSizeDefined(layout); + } + + /** @override */ + layoutCallback() { + const width = this.element.getAttribute('width'); + const height = this.element.getAttribute('height'); + const contentid = AMP.assert( + (this.element.getAttribute('data-playlist-id') || + this.element.getAttribute('data-media-id')), + 'Either the data-media-id or the data-playlist-id attributes must be specified for %s', + this.element); + const playerid = AMP.assert( + this.element.getAttribute('data-player-id'), + 'The data-player-id attribute is required for %s', + this.element); + + const iframe = document.createElement('iframe'); + let src = `https://content.jwplatform.com/players/${contentid}-${playerid}.html`; + + iframe.setAttribute('frameborder', '0'); + iframe.setAttribute('allowfullscreen', 'true'); + iframe.src = src; + this.applyFillContent(iframe); + iframe.width = width; + iframe.height = height; + this.element.appendChild(iframe); + /** @private {?Element} */ + this.iframe_ = iframe; + return loadPromise(iframe); + } + + /** @override */ + documentInactiveCallback() { + // TODO: implement inactiveCallback + return false; + } +}; + +AMP.registerElement('amp-jwplayer', AmpJWPlayer); diff --git a/extensions/amp-jwplayer/0.1/test/test-amp-jwplayer.js b/extensions/amp-jwplayer/0.1/test/test-amp-jwplayer.js new file mode 100644 index 000000000000..08ca85377166 --- /dev/null +++ b/extensions/amp-jwplayer/0.1/test/test-amp-jwplayer.js @@ -0,0 +1,58 @@ +/** + * Copyright 2015 Longtail Ad Solutions Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {createIframePromise} from '../../../../testing/iframe'; +require('../amp-jwplayer'); +import {adopt} from '../../../../src/runtime'; +import {parseUrl} from '../../../../src/url'; + +adopt(window); + +describe('amp-jwplayer', () => { + + function getjwplayer(attributes, opt_responsive) { + return createIframePromise().then(iframe => { + const jw = iframe.doc.createElement('amp-jwplayer'); + for (const key in attributes) { + jw.setAttribute(key, attributes[key]); + } + jw.setAttribute('width', '111'); + jw.setAttribute('height', '222'); + if (opt_responsive) { + jw.setAttribute('layout', 'responsive'); + } + iframe.doc.body.appendChild(jw); + jw.implementation_.layoutCallback(); + return jw; + }); + } + + it('renders', () => { + return getjwplayer({ + 'data-media-id': 'Wferorsv', + 'data-player-id': 'sDZEo0ea' + }).then(jw => { + const iframe = jw.querySelector('iframe'); + expect(iframe).to.not.be.null; + expect(iframe.tagName).to.equal('IFRAME'); + expect(iframe.src).to.equal( + 'https://content.jwplatform.com/players/Wferorsv-sDZEo0ea.html'); + expect(iframe.getAttribute('width')).to.equal('111'); + expect(iframe.getAttribute('height')).to.equal('222'); + }); + }); + +}); diff --git a/extensions/amp-jwplayer/amp-jwplayer.md b/extensions/amp-jwplayer/amp-jwplayer.md new file mode 100644 index 000000000000..244a3e6e1d0d --- /dev/null +++ b/extensions/amp-jwplayer/amp-jwplayer.md @@ -0,0 +1,103 @@ + + +### `amp-jwplayer` + + + + + + + + + + + + + + + + + + +
DescriptionAn amp-jwplayer component displays a cloud-hosted JW Player.
Availability?
Required Script<script async custom-element="amp-jwplayer" src="https://cdn.ampproject.org/v0/amp-jwplayer-0.1.js"></script>
Examplesjwplayer.amp.html
+ +The following lists validation errors specific to the `amp-jwplayer` tag +(see also `amp-jwplayer` in the [AMP validator specification](https://github.com/ampproject/amphtml/blob/master/validator/validator.protoascii): + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Validation ErrorDescription
TAG_REQUIRED_BY_MISSINGError thrown when required amp-jwplayer extension .js script tag is missing or incorrect.
MANDATORY_ATTR_MISSINGError thrown when data-account attribute is missing.
IMPLIED_LAYOUT_INVALIDError thrown when implied layout is set to CONTAINER; this layout type isn't supported.
MANDATORY_ONEOF_ATTR_MISSINGError thrown when either the data-media-id or data-playlist-id attributes are missing.
IMPLIED_LAYOUT_INVALIDError thrown when implied layout is set to CONTAINER; this layout type isn't supported.
SPECIFIED_LAYOUT_INVALIDError thrown when specified layout is set to CONTAINER; this layout type isn't supported.
INVALID_PROPERTY_VALUE_IN_ATTR_VALUEError thrown when invalid value is given for attributes height or width. For example, height=auto triggers this error for all supported layout types, with the exception of NODISPLAY.
+ +#### Example + +The `width` and `height` attributes determine the aspect ratio of the player embedded in responsive layouts. + +Example: + +```html + + +``` + +#### Attributes + +**data-player-id** + +JW Platform player id. This is an 8-digit alphanumeric sequence that can be found in the [Players](https://dashboard.jwplayer.com/#/players) section in your JW Player Dashboard. + +**data-media-id** + +The JW Platform media id. This is an 8-digit alphanumeric sequence that can be found in the [Content](https://dashboard.jwplayer.com/#/content) section in your JW Player Dashboard. + +**data-playlist-id** + +The JW Platform playlist id. This is an 8-digit alphanumeric sequence that can be found in the [Playlists](https://dashboard.jwplayer.com/#/content/playlists) section in your JW Player Dashboard. If both a `data-playlist-id` and `data-media-id` are specified, `data-playlist-id` takes precedence. diff --git a/gulpfile.js b/gulpfile.js index 0157062cfb22..7c00e3197c42 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -85,6 +85,7 @@ function buildExtensions(options) { buildExtension('amp-iframe', '0.1', false, options); buildExtension('amp-image-lightbox', '0.1', true, options); buildExtension('amp-instagram', '0.1', false, options); + buildExtension('amp-jwplayer', '0.1', false, options); buildExtension('amp-lightbox', '0.1', false, options); buildExtension('amp-list', '0.1', false, options); buildExtension('amp-mustache', '0.1', false, options); @@ -340,6 +341,7 @@ function buildExamples(watch) { buildExample('font.amp.html'); buildExample('facebook.amp.html'); buildExample('instagram.amp.html'); + buildExample('jwplayer.amp.html'); buildExample('pinterest.amp.html'); buildExample('reach-player.amp.html'); buildExample('released.amp.html'); From 2b5479d68f811368ae4d81bf01861f4393fa445f Mon Sep 17 00:00:00 2001 From: Pablo Schklowsky Date: Thu, 18 Feb 2016 13:28:39 -0500 Subject: [PATCH 07/22] Some more examples and tests --- examples/jwplayer.amp.html | 12 +++- .../0.1/test/test-amp-jwplayer.js | 56 ++++++++++++++++--- extensions/amp-jwplayer/amp-jwplayer.md | 12 ++++ 3 files changed, 71 insertions(+), 9 deletions(-) diff --git a/examples/jwplayer.amp.html b/examples/jwplayer.amp.html index 48ef8ce04489..fcaab564f442 100644 --- a/examples/jwplayer.amp.html +++ b/examples/jwplayer.amp.html @@ -11,7 +11,9 @@ -

JWPlayer AMP

+

JWPlayer AMP Examples

+ +

Responsive

JWPlayer AMP layout="responsive" width="16" height="9"> +

Non-responsive, with a playlist

+ + + + diff --git a/extensions/amp-jwplayer/0.1/test/test-amp-jwplayer.js b/extensions/amp-jwplayer/0.1/test/test-amp-jwplayer.js index 08ca85377166..24d61888361d 100644 --- a/extensions/amp-jwplayer/0.1/test/test-amp-jwplayer.js +++ b/extensions/amp-jwplayer/0.1/test/test-amp-jwplayer.js @@ -23,17 +23,15 @@ adopt(window); describe('amp-jwplayer', () => { - function getjwplayer(attributes, opt_responsive) { + function getjwplayer(attributes) { return createIframePromise().then(iframe => { const jw = iframe.doc.createElement('amp-jwplayer'); for (const key in attributes) { jw.setAttribute(key, attributes[key]); } - jw.setAttribute('width', '111'); - jw.setAttribute('height', '222'); - if (opt_responsive) { - jw.setAttribute('layout', 'responsive'); - } + jw.setAttribute('width', '320'); + jw.setAttribute('height', '180'); + jw.setAttribute('layout', 'responsive'); iframe.doc.body.appendChild(jw); jw.implementation_.layoutCallback(); return jw; @@ -50,8 +48,50 @@ describe('amp-jwplayer', () => { expect(iframe.tagName).to.equal('IFRAME'); expect(iframe.src).to.equal( 'https://content.jwplatform.com/players/Wferorsv-sDZEo0ea.html'); - expect(iframe.getAttribute('width')).to.equal('111'); - expect(iframe.getAttribute('height')).to.equal('222'); + expect(iframe.getAttribute('width')).to.equal('320'); + expect(iframe.getAttribute('height')).to.equal('180'); + }); + }); + + it('renders with a playlist', () => { + return getjwplayer({ + 'data-playlist-id': '482jsTAr', + 'data-player-id': 'sDZEo0ea' + }).then(jw => { + const iframe = jw.querySelector('iframe'); + expect(iframe).to.not.be.null; + expect(iframe.tagName).to.equal('IFRAME'); + expect(iframe.src).to.equal( + 'https://content.jwplatform.com/players/482jsTAr-sDZEo0ea.html'); + }); + }); + + it('fails if no media is specified', () => { + return getjwplayer({ + 'data-player-id': 'sDZEo0ea' + }).should.eventually.be.rejectedWith( + /Either the data-media-id or the data-playlist-id attributes must be specified for/ + ); + }); + + it('fails if no player is specified', () => { + return getjwplayer({ + 'data-media-id': 'Wferorsv', + }).should.eventually.be.rejectedWith( + /The data-player-id attribute is required for/ + ); + }); + + it('renders with a bad playlist', () => { + return getjwplayer({ + 'data-playlist-id': 'zzz', + 'data-player-id': 'sDZEo0ea' + }).then(jw => { + const iframe = jw.querySelector('iframe'); + expect(iframe).to.not.be.null; + expect(iframe.tagName).to.equal('IFRAME'); + expect(iframe.src).to.equal( + 'https://content.jwplatform.com/players/zzz-sDZEo0ea.html'); }); }); diff --git a/extensions/amp-jwplayer/amp-jwplayer.md b/extensions/amp-jwplayer/amp-jwplayer.md index 244a3e6e1d0d..b5932071139f 100644 --- a/extensions/amp-jwplayer/amp-jwplayer.md +++ b/extensions/amp-jwplayer/amp-jwplayer.md @@ -88,6 +88,18 @@ Example:
``` +Non-responsive layout is also supported. + +Example: + +```html + + +``` + #### Attributes **data-player-id** From 751af664ef6d0f575ec515c27c2c67504463699c Mon Sep 17 00:00:00 2001 From: Pablo Schklowsky Date: Thu, 18 Feb 2016 19:22:21 -0500 Subject: [PATCH 08/22] Pause player on `documentInactiveCallback` --- extensions/amp-jwplayer/0.1/amp-jwplayer.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/extensions/amp-jwplayer/0.1/amp-jwplayer.js b/extensions/amp-jwplayer/0.1/amp-jwplayer.js index 3a79b3861ec5..ec2f53c00e94 100644 --- a/extensions/amp-jwplayer/0.1/amp-jwplayer.js +++ b/extensions/amp-jwplayer/0.1/amp-jwplayer.js @@ -63,7 +63,10 @@ class AmpJWPlayer extends AMP.BaseElement { /** @override */ documentInactiveCallback() { - // TODO: implement inactiveCallback + if (this.iframe_ && this.iframe_.contentWindow) { + // The /players page can respond to "play" and "pause" commands from the iframe's parent + this.iframe_.contentWindow./*OK*/postMessage('pause', 'https://content.jwplatform.com'); + } return false; } }; From eb8af570cc658dcd1b855cb3e82aa959abc21907 Mon Sep 17 00:00:00 2001 From: Pablo Schklowsky Date: Thu, 18 Feb 2016 20:07:14 -0500 Subject: [PATCH 09/22] Availability "Stable" --- extensions/amp-jwplayer/amp-jwplayer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/amp-jwplayer/amp-jwplayer.md b/extensions/amp-jwplayer/amp-jwplayer.md index b5932071139f..e52ade4a626f 100644 --- a/extensions/amp-jwplayer/amp-jwplayer.md +++ b/extensions/amp-jwplayer/amp-jwplayer.md @@ -23,7 +23,7 @@ limitations under the License. Availability - ? + Stable Required Script From 927024b11fa3a17494247efd6d83e214ef5f4951 Mon Sep 17 00:00:00 2001 From: Pablo Schklowsky Date: Thu, 18 Feb 2016 20:12:36 -0500 Subject: [PATCH 10/22] Add amp-jwplayer to validator.protoascii --- validator/validator.protoascii | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/validator/validator.protoascii b/validator/validator.protoascii index fae7417d243a..b0f4910cc1c3 100644 --- a/validator/validator.protoascii +++ b/validator/validator.protoascii @@ -4050,6 +4050,25 @@ tags: { # supported_layouts: NODISPLAY } } +tags: { # + tag_name: "amp-jwplayer" + disallowed_ancestor: "head" + also_requires: "amp-jwplayer extension .js script" + attrs: { name: "data-player-id" mandatory: true } + # If data-media-id is omitted, then data-playlist-id is required. + attrs: { name: "data-media-id" } + # If data-playlist-id is omitted, then data-media-id is required. + attrs: { name: "data-playlist-id" } + attr_lists: "extended-amp-global" + spec_url: "https://www.ampproject.org/docs/reference/extended/amp-jwplayer.html" + amp_layout: { + supported_layouts: FILL + supported_layouts: FIXED + supported_layouts: FIXED_HEIGHT + supported_layouts: NODISPLAY + supported_layouts: RESPONSIVE + } +} tags: { # tag_name: "amp-lightbox" disallowed_ancestor: "head" From 7018a7d80ee97020caa822e225c9b0ab1a5933ee Mon Sep 17 00:00:00 2001 From: Pablo Schklowsky Date: Mon, 21 Mar 2016 17:29:13 -0400 Subject: [PATCH 11/22] Duplicate validator --- validator/validator.protoascii | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/validator/validator.protoascii b/validator/validator.protoascii index 03524bbb234f..b0f4910cc1c3 100644 --- a/validator/validator.protoascii +++ b/validator/validator.protoascii @@ -3099,24 +3099,6 @@ tags: { # amp-install-serviceworker } spec_url: "https://www.ampproject.org/docs/reference/extended/amp-install-serviceworker.html" } -tags: { # amp-jwplayer - name: "script" - mandatory_parent: "head" - detail: "amp-jwplayer extension .js script" - spec_url: "https://github.com/ampproject/amphtml/blob/master/extensions/amp-jwplayer/amp-jwplayer.md" - attrs: { - name: "custom-element" - value: "amp-jwplayer" - mandatory: true - dispatch_key: true - } - attrs: { name: "async" value: "" mandatory: true } - attrs: { - name: "src" - value_regex: "https://cdn\\.ampproject\\.org/v0/amp-jwplayer-(latest|0\\.1).js" - mandatory: true - } -} tags: { # amp-lightbox tag_name: "script" spec_name: "amp-lightbox extension .js script" From b2f30a6396638925fa5d4dd8e44c8e2e88c89a02 Mon Sep 17 00:00:00 2001 From: Pablo Schklowsky Date: Tue, 22 Mar 2016 14:24:43 -0400 Subject: [PATCH 12/22] Update copyright dates --- extensions/amp-jwplayer/0.1/amp-jwplayer.js | 2 +- extensions/amp-jwplayer/0.1/test/test-amp-jwplayer.js | 2 +- extensions/amp-jwplayer/amp-jwplayer.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/amp-jwplayer/0.1/amp-jwplayer.js b/extensions/amp-jwplayer/0.1/amp-jwplayer.js index ec2f53c00e94..5cbf4424b2d1 100644 --- a/extensions/amp-jwplayer/0.1/amp-jwplayer.js +++ b/extensions/amp-jwplayer/0.1/amp-jwplayer.js @@ -1,5 +1,5 @@ /** - * Copyright 2015 Longtail Ad Solutions Inc. + * Copyright 2016 Longtail Ad Solutions Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/extensions/amp-jwplayer/0.1/test/test-amp-jwplayer.js b/extensions/amp-jwplayer/0.1/test/test-amp-jwplayer.js index 24d61888361d..fd785cd0ebda 100644 --- a/extensions/amp-jwplayer/0.1/test/test-amp-jwplayer.js +++ b/extensions/amp-jwplayer/0.1/test/test-amp-jwplayer.js @@ -1,5 +1,5 @@ /** - * Copyright 2015 Longtail Ad Solutions Inc. + * Copyright 2016 Longtail Ad Solutions Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/extensions/amp-jwplayer/amp-jwplayer.md b/extensions/amp-jwplayer/amp-jwplayer.md index e52ade4a626f..5f03989210c6 100644 --- a/extensions/amp-jwplayer/amp-jwplayer.md +++ b/extensions/amp-jwplayer/amp-jwplayer.md @@ -1,5 +1,5 @@