Skip to content

Commit

Permalink
Nexxtv player extension (#7816)
Browse files Browse the repository at this point in the history
* initial setup for extension nexxtv-player

* added nexxtv player logic

* nexxtv player bugfixes, cleanup, tests, docs

* added video interface, changed attribute start to seek-to

* reworked video interface for nexxtv-player

* added message handler for nexxtv player and tests

* nexxtv player minor fixes & cleanup

* nexxtv player changed indents from 4 spaces to 2

* added unlayoutCallback for nexxtv player

* initial setup for extension nexxtv-player

* added nexxtv player logic

* nexxtv player bugfixes, cleanup, tests, docs

* added video interface, changed attribute start to seek-to

* reworked video interface for nexxtv-player

* added message handler for nexxtv player and tests

* nexxtv player minor fixes & cleanup

* nexxtv player changed indents from 4 spaces to 2

* added unlayoutCallback for nexxtv player

* fixed linting issues (#7673)

* initial setup for extension nexxtv-player

* added nexxtv player logic

* nexxtv player bugfixes, cleanup, tests, docs

* added video interface, changed attribute start to seek-to

* reworked video interface for nexxtv-player

* added message handler for nexxtv player and tests

* nexxtv player minor fixes & cleanup

* nexxtv player changed indents from 4 spaces to 2

* added unlayoutCallback for nexxtv player

* initial setup for extension nexxtv-player

* added nexxtv player logic

* nexxtv player bugfixes, cleanup, tests, docs

* added video interface, changed attribute start to seek-to

* reworked video interface for nexxtv-player

* added message handler for nexxtv player and tests

* nexxtv player minor fixes & cleanup

* fixed linting issues (#7673)

* nexxtv player review fixes (#7816)

* nexxtv player: updated validation rules (#7816)

* nexxtv player validator ordered alphabetical, changed spec_url

* fire amp-dom-update event on insert and replace (#7819)

* temp

* trigger `amp-dom-update` event on insert and replace

* switch -amp-form to i-amp-form

* Validator Rollup (#7844)

* Revision bump.

* Revision bump for amp-playbuzz changes in #7450

* Revision bump.

* Allow filtering by HtmlFormat (AMP, AMP4ADS) in the code generator.

* Revision bump.

* Revision bump due to Github pull.

* Code generation now driven by a variable LIGHT, which is broader than the previous GENERATE_DETAILED_ERRORS distinction. LIGHT implies filtered for specific format (AMP or AMP4ADS, and only amp.validator.validateSaxEvents, and no detailed errors.

* Generate ValidatorRules.directAttrLists and globalAttrs and ampLayoutAttrs, reducing overhead by indirection.

* Implement parallax effect extension (#7794)

* Do not set hidden attribute on hide/collapse (#7879)

* amp-bind: Support `Math` functions (#7797)

* initial commit for Math in amp-bind

* fix lint

* ali's comment

* avoid scope conflicts with built-in functions

* remove Math functions from documentation

* Measure 3P ad latency (#7902)

* Update spec URLs from github to ampproject. (#7901)

* Update github urls to ampproject urls

* test .out updated

* Too many slashes.

* Moved Developing.md and Design_Principles.md (#7908)

* Rename DEVELOPING.md to contributing/DEVELOPING.md

* Move files to new folder

* Renaming toggle to toggleVisibility because it was conflicting with sidebar and breaking it (#7855)

* disable scrollRestoration auto for embed case (#7899)

* Disable Fast Fetch for all ads when remote.html is used. (#7906)

* Fix DoubleClick Fast Fetch bugs around categoryExclusions and tagForChildDirectedTreatment (#7843)

* Fix incorrect parameter name for `tagForChildDirectedTreatment` in DoubleClick.

* Added unit test for tagForChildDirectedTreatment.

* Fixed the categoryExclusions bug in Fast Fetch DoubleClick.

* Removed parens in arrow functions to satisfy linter

* Adds two new experiment IDs (#7850)

* Adds two new experiment IDs to distinguish "any externally triggered" experiment from "any internally triggered".

Also updates tests to remove a number of assumptions that only a single eid will be populated.

* Use return val to detect "externally selected"; test updates.

* Updated network implementation guide. (#7862)

* Updated network impl guide.

* Changed 'validation' to 'verification'

* Cid timeout error should not be dev error (#7911)

* Fix amp-ad test (#7918)

* Update amp-install-serviceworker.md (#7932)

Fully escape javascript regex.

* Update amp-cache-modifications.md (#7933)

Add `data-no-service-worker-fallback-shell-url` as a URL to be rewritten as absolute.

* amp-fx-parallax Don't use global (#7942)

* Viewer integration: rewrite error message to indicate request name (#7923)

* rewrite error message

* update

* ticks

* Fix up links in DEVELOPING.md (#7944)

We moved DEVELOPING.md to the contributing folder but didn't update many of the relative links accordingly.

* Popin ad extension document updated. (#7674)

* Add popin ad extension.

* register popin.

* Add resizeable attribute.

* Remove resizable.

* Implemented the render-start and no-content APIs.

* Fixed lint.

* Remove quatation.

* Use tei@popin.cc

* Rebase old commit .

* Fixed document because tag was not closed.

* I replaced the removed double quotes.

* Add double quotes.

* Support for bind expressions in mustache templates (#7602)

* first pass at adding/removing subtrees

* tests passing

* Add ability to add and remove bindings in the subtree rooted at a specific node

* lint fixes

* merge conflict

* ci issues

* some cleanup

* pr comments

* pr comments, new method of removing bindings for node and its children

* fix some lint warnings

* test and lint fixes

* mutation observer

* update comments

* add observer

* naive implementation

* cleanup

* clean

* cleanup bind impl

* very simple example of bind in a template

* pr comments

* lint errors

* pr comments

* cleanup

* lint errors

* pr comments

* pr comments and lint warnings

* typo

* comments

* more lint warnings

* pr comment

* change README

* cleanup

* pr comments

* pr comments 2

* lint warnings

* don't use foreach on Nodelist

* casting

* more ci warnings

* even more ci warnings

* even more ci warnings

* even more ci issues

* even more ci comments

* even more ci issues

* pr comments

* more pr comments

* fix refactoring oversight

* pr comments

* pr comments

* merge

* pr comments

* ci issues

* pr comments

* assertElement

* pr comments

* typo

* updating sanitizer

* fixing up sanitizer test

* pr comments

* pr comments

* expanded on amp-mustache tests

* pr comments

* remove class from whitelist

* restore whole sandbox

* update mustache validator test and output

* pr comments

* nexxtv player gulp check-types fixes (#7816)

* nexxtv player fix error gulp presumbit with postmessage (#7816)

* added dependency check for nexxtv player (#7816)

* nexxtv player minor fix in validator (#7816)

* nexxtv player fixed validator (#7816)

* fixed validator (#7816)
  • Loading branch information
neko-fire authored and erwinmombay committed Mar 9, 2017
1 parent ed8c2c4 commit ac4adb8
Show file tree
Hide file tree
Showing 10 changed files with 635 additions and 1 deletion.
1 change: 1 addition & 0 deletions ads/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ Access to a publishers 1st party cookies may be achieved through a custom ad boo
If the publisher would like to add custom JavaScript in the `remote.html` file that wants to read or write to the publisher owned cookies, then the publisher needs to ensure that the `remote.html` file is hosted on a sub-domain of the publisher URL. e.g. if the publisher hosts a webpage on https://nytimes.com, then the remote file should be hosted on something similar to https://sub-domain.nytimes.com for the custom JavaScript to have the abiity to read or write cookies for nytimes.com.

## Developer guidelines for a pull request

Please read through [DEVELOPING.md](../contributing/DEVELOPING.md) before contributing to this code repository.

### Files to change
Expand Down
2 changes: 2 additions & 0 deletions build-system/dep-check-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ exports.rules = [
'extensions/amp-youtube/0.1/amp-youtube.js->' +
'src/service/video-manager-impl.js',
'extensions/amp-a4a/0.1/amp-a4a.js->src/service/variable-source.js',
'extensions/amp-nexxtv-player/0.1/amp-nexxtv-player.js->' +
'src/service/video-manager-impl.js',
'extensions/amp-fx-parallax/0.1/amp-fx-parallax.js->' +
'src/service/parallax-impl.js',
],
Expand Down
1 change: 0 additions & 1 deletion extensions/amp-bind/0.1/bind-impl.js
Original file line number Diff line number Diff line change
Expand Up @@ -797,7 +797,6 @@ export class Bind {
return false;
}


/**
* Wait for bind scan to finish for testing.
*
Expand Down
229 changes: 229 additions & 0 deletions extensions/amp-nexxtv-player/0.1/amp-nexxtv-player.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
/**
* Copyright 2017 The AMP HTML Authors. All Rights Reserved.
*
* 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 {user} from '../../../src/log';
import {
installVideoManagerForDoc,
} from '../../../src/service/video-manager-impl';
import {removeElement} from '../../../src/dom';
import {isObject} from '../../../src/types';
import {tryParseJson} from '../../../src/json';
import {listen} from '../../../src/event-helper';
import {VideoEvents} from '../../../src/video-interface';
import {videoManagerForDoc} from '../../../src/video-manager';

/**
* @implements {../../../src/video-interface.VideoInterface}
*/
class AmpNexxtvPlayer extends AMP.BaseElement {

/** @param {!AmpElement} element */
constructor(element) {
super(element);

/** @private {?Element} */
this.iframe_ = null;

/** @private {?string} */
this.videoIframeSrc_ = null;

/** @private {?Function} */
this.unlistenMessage_ = null;

/** @private {?Promise} */
this.playerReadyPromise_ = null;

/** @private {?Function} */
this.playerReadyResolver_ = null;
}

/**
* @param {boolean=} opt_onLayout
* @override
*/
preconnectCallback(opt_onLayout) {
this.preconnect.url(this.getVideoIframeSrc_(), opt_onLayout);
}

/** @override */
isLayoutSupported(layout) {
return isLayoutSizeDefined(layout);
}

/** @override */
buildCallback() {
this.playerReadyPromise_ = new Promise(resolve => {
this.playerReadyResolver_ = resolve;
});

const iframe = this.element.ownerDocument.createElement('iframe');
this.iframe_ = iframe;

this.applyFillContent(iframe);
iframe.setAttribute('frameborder', '0');
iframe.setAttribute('allowfullscreen', 'true');

this.element.appendChild(iframe);

installVideoManagerForDoc(this.element);
videoManagerForDoc(this.element).register(this);
}

getVideoIframeSrc_() {
if (this.videoIframeSrc_) {
return this.videoIframeSrc_;
}

const mediaId = user().assert(
this.element.getAttribute('data-mediaid'),
'The data-mediaid attribute is required for <amp-nexxtv-player> %s',
this.element);

const client = user().assert(this.element.getAttribute('data-client'),
'The data-client attribute is required for <amp-nexxtv-player> %s',
this.element);

const start = this.element.getAttribute('data-seek-to') || 0;
const mode = this.element.getAttribute('data-mode') || 'static';
const streamtype = this.element.getAttribute('data-streamtype') || 'video';
const origin = this.element.getAttribute('data-origin')
|| 'https://embed.nexx.cloud/';

let src = '';
src += origin;

if (streamtype !== 'video') {
src += `${encodeURIComponent(streamtype)}/`;
}

src += `${encodeURIComponent(client)}/`;
src += `${encodeURIComponent(mediaId)}`;
src += `?start=${encodeURIComponent(String(start))}`;
src += `&datamode=${encodeURIComponent(mode)}&amp=1`;

return this.videoIframeSrc_ = src;
}

/** @override */
viewportCallback(visible) {
this.element.dispatchCustomEvent(VideoEvents.VISIBILITY, {visible});
}

/** @override */
layoutCallback() {
this.iframe_.src = this.getVideoIframeSrc_();

this.unlistenMessage_ = listen(this.iframe_,'message', event => {
this.handleNexxMessages_(event);
});

return this.loadPromise(this.iframe_)
.then(() => {
this.element.dispatchCustomEvent(VideoEvents.LOAD);
this.playerReadyResolver_(this.iframe_);
});
}

pauseCallback() {
if (this.iframe_) {
this.pause();
}
}

/** @override */
unlayoutCallback() {
if (this.iframe_) {
removeElement(this.iframe_);
this.iframe_ = null;
}

if (this.unlistenMessage_) {
this.unlistenMessage_();
}

return true;
}

sendCommand_(command) {
this.iframe_.contentWindow./*OK*/postMessage(JSON.stringify({
'cmd': command,
}), '*');
};

// emitter
handleNexxMessages_(event) {
const data = isObject(event.data) ? event.data : tryParseJson(event.data);
if (data === undefined) {
return; // We only process valid JSON.
}

if (data.cmd == 'onload') {
this.element.dispatchCustomEvent(VideoEvents.LOAD);
this.playerReadyResolver_(this.iframe_);
} else if (data.cmd == 'play') {
this.element.dispatchCustomEvent(VideoEvents.PLAY);
} else if (data.cmd == 'pause') {
this.element.dispatchCustomEvent(VideoEvents.PAUSE);
} else if (data.cmd == 'mute') {
this.element.dispatchCustomEvent(VideoEvents.MUTED);
} else if (data.cmd == 'unmute') {
this.element.dispatchCustomEvent(VideoEvents.UNMUTED);
}
}

// VideoInterface Implementation
// only send in json format
play() {
this.playerReadyPromise_.then(() => {
this.sendCommand_('play');
});
}

pause() {
this.playerReadyPromise_.then(() => {
this.sendCommand_('pause');
});
}

mute() {
this.playerReadyPromise_.then(() => {
this.sendCommand_('mute');
});
}

unmute() {
this.playerReadyPromise_.then(() => {
this.sendCommand_('unmute');
});
}

supportsPlatform() {
return true;
}

isInteractive() {
return true;
}

showControls() {
}

hideControls() {
}
}

AMP.registerElement('amp-nexxtv-player', AmpNexxtvPlayer);
128 changes: 128 additions & 0 deletions extensions/amp-nexxtv-player/0.1/test/test-amp-nexxtv-player.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/**
* Copyright 2017 The AMP HTML Authors. All Rights Reserved.
*
* 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,
doNotLoadExternalResourcesInTest,
} from '../../../../testing/iframe';
import '../amp-nexxtv-player';
import {listenOncePromise} from '../../../../src/event-helper';
import {adopt} from '../../../../src/runtime';
import {timerFor} from '../../../../src/timer';
import {VideoEvents} from '../../../../src/video-interface';
import * as sinon from 'sinon';

adopt(window);

describe('amp-nexxtv-player', () => {

let sandbox;
const timer = timerFor(window);

beforeEach(() => {
sandbox = sinon.sandbox.create();
});

afterEach(() => {
sandbox.restore();
});

function getNexxtv(mediaid, client) {
return createIframePromise(true).then(iframe => {
doNotLoadExternalResourcesInTest(iframe.win);
const nexxtv = iframe.doc.createElement('amp-nexxtv-player');

if (mediaid) {
nexxtv.setAttribute('data-mediaid', mediaid);
}
if (client) {
nexxtv.setAttribute('data-client', client);
}

// see yt test implementation
timer.promise(50).then(() => {
const nexxTimerIframe = nexxtv.querySelector('iframe');

nexxtv.implementation_.handleNexxMessages_({
origin: 'https://embed.nexx.cloud',
source: nexxTimerIframe.contentWindow,
data: JSON.stringify({cmd: 'onload'}),
});
});

return iframe.addElement(nexxtv);
});
}

it('renders nexxtv video player', () => {
return getNexxtv('PTPFEC4U184674', '583').then(nexxtv => {
const playerIframe = nexxtv.querySelector('iframe');

expect(playerIframe).to.not.be.null;
expect(playerIframe.src).to.equal('https://embed.nexx.cloud/583/'
+ 'PTPFEC4U184674?start=0&datamode=static&amp=1');
});
});

it('fails without mediaid', () => {
return getNexxtv(null, '583').should.eventually.be.rejectedWith(
/The data-mediaid attribute is required/);
});

it('fails without client', () => {
return getNexxtv('PTPFEC4U184674', null).should.eventually.be.rejectedWith(
/The data-client attribute is required/);
});


it('should forward events from nexxtv-player to the amp element', () => {
return getNexxtv('PTPFEC4U184674', '583').then(nexxtv => {
const iframe = nexxtv.querySelector('iframe');

return Promise.resolve()
.then(() => {
const p = listenOncePromise(nexxtv, VideoEvents.PLAY);
sendFakeMessage(nexxtv, iframe, 'play');
return p;
})
.then(() => {
const p = listenOncePromise(nexxtv, VideoEvents.MUTED);
sendFakeMessage(nexxtv, iframe, 'mute');
return p;
})
.then(() => {
const p = listenOncePromise(nexxtv, VideoEvents.PAUSE);
sendFakeMessage(nexxtv, iframe, 'pause');
return p;
})
.then(() => {
const p = listenOncePromise(nexxtv, VideoEvents.UNMUTED);
sendFakeMessage(nexxtv, iframe, 'unmute');
return p;
});
});
});


function sendFakeMessage(nexxtv, iframe, command) {
nexxtv.implementation_.handleNexxMessages_({
origin: 'https://embed.nexx.cloud',
source: iframe.contentWindow,
data: JSON.stringify({
cmd: command,
}),
});
}
});
Loading

0 comments on commit ac4adb8

Please sign in to comment.