Skip to content

Commit

Permalink
Add amp-facebook element for Facebook posts and videos.
Browse files Browse the repository at this point in the history
  • Loading branch information
mkhatib committed Jan 20, 2016
1 parent 1e86779 commit 6679db1
Show file tree
Hide file tree
Showing 9 changed files with 302 additions and 5 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ examples.build
examples.min
node_modules
npm-debug.log
.idea
67 changes: 67 additions & 0 deletions 3p/facebook.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* Copyright 2015 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 {loadScript} from '../src/3p';
import {assert} from '../src/asserts';


/**
* Produces the Facebook SDK object for the passed in callback.
*
* Note: Facebook SDK fails to render multiple posts when the SDK is only loaded
* in one frame. To Allow the SDK to render them correctly we load the script
* per iframe.
*
* @param {!Window} global
* @param {function(!Object)} cb
*/
function getFacebookSdk(global, cb) {
loadScript(global, 'https://connect.facebook.net/en_US/sdk.js', () => {
cb(global.FB);
});
}

/**
* @param {!Window} global
* @param {!Object} data
*/
export function facebook(global, data) {
const embedAs = data.embedAs || 'post';
assert(['post', 'video'].indexOf(embedAs) !== -1,
'Attribute data-embed-as for <amp-facebook> value is wrong, should be' +
' "post" or "video" was: %s', embedAs);
const fbPost = document.createElement('div');
fbPost.className = 'fb-' + embedAs;
fbPost.setAttribute('data-href', data.href);
global.document.getElementById('c').appendChild(fbPost);
getFacebookSdk(global, FB => {
// Dimensions are given by the parent frame.
delete data.width;
delete data.height;

// Only need to listen to post resizing as FB videos have a fixed ratio
// and can automatically resize correctly given the initial width/height.
if (embedAs === 'post') {
FB.Event.subscribe('xfbml.resize', event => {
context.updateDimensions(
parseInt(event.width, 10),
parseInt(event.height, 10) + /* margins */ 20);
});
}
FB.init({xfbml: true, version: 'v2.5'});
});

}
11 changes: 7 additions & 4 deletions 3p/integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {adreactor} from '../ads/adreactor';
import {adsense} from '../ads/adsense';
import {adtech} from '../ads/adtech';
import {doubleclick} from '../ads/doubleclick';
import {facebook} from './facebook';
import {twitter} from './twitter';
import {register, run} from '../src/3p';
import {parseUrl} from '../src/url';
Expand All @@ -42,6 +43,7 @@ register('_ping_', function(win, data) {
win.document.getElementById('c').textContent = data.ping;
});
register('twitter', twitter);
register('facebook', facebook);

/**
* Visible for testing.
Expand Down Expand Up @@ -112,12 +114,13 @@ window.draw3p = function(opt_configCallback) {
window.context.isMaster = window.context.master == window;
window.context.data = data;
window.context.noContentAvailable = triggerNoContentAvailable;
if (data.type == 'twitter') {
// Only make this available to Twitter for now while
// https://github.com/ampproject/amphtml/issues/728
// is being implemented.

if (data.type === 'facebook' || data.type === 'twitter') {
// Only make this available to selected embeds until the generic solution is
// available.
window.context.updateDimensions = triggerDimensions;
}

// This only actually works for ads.
window.context.observeIntersection = observeIntersection;
window.context.reportRenderedEntityIdentifier =
Expand Down
2 changes: 1 addition & 1 deletion DEVELOPING.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ If you have any questions, feel free to ask on the issue or join us on [Slack](h
| `gulp dist` | Builds production binaries. |
| `gulp lint` | Validates against Google Closure Linter. |
| `gulp lint --watch` | Watches for changes in files, Validates against Google Closure Linter.|
| `gulp lint-fix` | Fixes simple lint warnings/errors automatically. |
| `gulp lint --fix` | Fixes simple lint warnings/errors automatically. |
| `gulp build` | Builds the AMP library. |
| `gulp clean` | Removes build output. |
| `gulp css` | Recompile css to build directory. |
Expand Down
48 changes: 48 additions & 0 deletions examples/facebook.amp.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<!doctype html>
<html >
<head>
<meta charset="utf-8">
<title>Facebook examples</title>
<link rel="canonical" href="amps.html" >
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
<link href='https://fonts.googleapis.com/css?family=Questrial' rel='stylesheet' type='text/css'>
<script async custom-element="amp-facebook" src="https://cdn.ampproject.org/v0/amp-facebook-0.1.js"></script>
<style>body {opacity: 0}</style><noscript><style>body {opacity: 1}</style></noscript>
<script async src="https://cdn.ampproject.org/v0.js"></script>
</head>
<body>



<h2>Facebook</h2>

<amp-facebook width=552 height=303
layout="responsive"
data-href="https://www.facebook.com/zuck/posts/10102593740125791">
</amp-facebook>

<h1>More Posts</h1>

<amp-facebook width=552 height=350
layout="responsive"
data-href="https://www.facebook.com/notes/facebook-engineering/under-the-hood-the-javascript-sdk-truly-asynchronous-loading/10151176218703920/">
</amp-facebook>

<amp-facebook width=552 height=579
layout="responsive"
data-href="https://www.facebook.com/zuck/posts/10102542090197661">
</amp-facebook>

<amp-facebook width=552 height=544
layout="responsive"
data-href="https://www.facebook.com/photo.php?fbid=10102533316889441&set=a.529237706231.2034669.4&type=3&theater">
</amp-facebook>

<amp-facebook width=552 height=310
layout="responsive"
data-embed-as="video"
data-href="https://www.facebook.com/zuck/videos/10102509264909801/">
</amp-facebook>

</body>
</html>
56 changes: 56 additions & 0 deletions extensions/amp-facebook/0.1/amp-facebook.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* Copyright 2015 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 {getIframe, listen, prefetchBootstrap} from '../../../src/3p-frame';
import {isLayoutSizeDefined} from '../../../src/layout';
import {loadPromise} from '../../../src/event-helper';


class AmpFacebook extends AMP.BaseElement {
/** @override */
preconnectCallback(onLayout) {
this.preconnect.url('https://facebook.com', onLayout);
// Hosts the facebook SDK.
this.preconnect.prefetch('https://connect.facebook.net/en_US/sdk.js');
prefetchBootstrap(this.getWin());
}

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

/** @override */
layoutCallback() {
const iframe = getIframe(this.element.ownerDocument.defaultView,
this.element, 'facebook');
this.applyFillContent(iframe);
this.element.appendChild(iframe);
// Triggered by context.updateDimensions() inside the iframe.
listen(iframe, 'embed-size', data => {
iframe.height = data.height;
iframe.width = data.width;
const amp = iframe.parentElement;
amp.setAttribute('height', data.height);
amp.setAttribute('width', data.width);
this./*OK*/changeHeight(data.height);
});
return loadPromise(iframe);
}
};

AMP.registerElement('amp-facebook', AmpFacebook);
69 changes: 69 additions & 0 deletions extensions/amp-facebook/0.1/test/test-amp-facebook.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/**
* Copyright 2015 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} from '../../../../testing/iframe';
require('../amp-facebook');
import {adopt} from '../../../../src/runtime';

adopt(window);

describe('amp-facebook', () => {

function getFBPost(href, opt_embedAs) {
return createIframePromise().then(iframe => {
const link = document.createElement('link');
link.setAttribute('rel', 'canonical');
link.setAttribute('href', 'https://foo.bar/baz');
iframe.addElement(link);

const ampFB = iframe.doc.createElement('amp-facebook');
ampFB.setAttribute('data-href', href);
ampFB.setAttribute('width', '111');
ampFB.setAttribute('height', '222');
if (opt_embedAs) {
ampFB.setAttribute('data-embed-as', opt_embedAs);
}
return iframe.addElement(ampFB);
});
}

it('renders fb-post', () => {
return getFBPost('https://www.facebook.com/zuck/posts/10102593740125791').then(ampFB => {
const iframe = ampFB.firstChild;
expect(iframe).to.not.be.null;
expect(iframe.tagName).to.equal('IFRAME');
expect(iframe.getAttribute('width')).to.equal('111');
expect(iframe.getAttribute('height')).to.equal('222');

const fbPost = iframe.getElementsByClassName('fb-post')[0];
expect(fbPost).not.to.be.null;
});
});

it('renders fb-post', () => {
return getFBPost('https://www.facebook.com/zuck/videos/10102509264909801/', 'video').then(ampFB => {
const iframe = ampFB.firstChild;
expect(iframe).to.not.be.null;
expect(iframe.tagName).to.equal('IFRAME');
expect(iframe.getAttribute('width')).to.equal('111');
expect(iframe.getAttribute('height')).to.equal('222');

const fbVideo = iframe.getElementsByClassName('fb-video')[0];
expect(fbVideo).not.to.be.null;
});
});

});
51 changes: 51 additions & 0 deletions extensions/amp-facebook/amp-facebook.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<!---
Copyright 2015 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.
-->

### <a name="amp-facebook"></a> `amp-facebook`

Displays a Facebook Post or Video.

Example - Embedding a post:
```html
<amp-facebook width=486 height=657
layout="responsive"
data-href="https://www.facebook.com/zuck/posts/10102593740125791">
</amp-facebook>
```

Example - Embedding a video:
```html
<amp-facebook width=552 height=574
layout="responsive"
data-embed-as="video"
data-href="https://www.facebook.com/zuck/videos/10102509264909801/">
</amp-facebook>
```


#### Attributes

**data-href**

The URL of the facebook post/video. For example: https://www.facebook.com/zuck/posts/10102593740125791.

**data-embed-as**
_Optional_
Either `post` or `video` (default: `post`).

Both posts and videos can be embedded as a post. Setting `data-embed-as="video"` for Facebook videos only embed the player of the video ignoring the accompanying post card with it. This is recommended if you'd like a better aspect ratio management for the video to be responsive.

Checkout the documentation for differences between [post embeds](https://developers.facebook.com/docs/plugins/embedded-posts) and [video embeds](https://developers.facebook.com/docs/plugins/embedded-video-player).
2 changes: 2 additions & 0 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ function buildExtensions(options) {
buildExtension('amp-brightcove', '0.1', false, options);
buildExtension('amp-carousel', '0.1', true, options);
buildExtension('amp-dynamic-css-classes', '0.1', false, options);
buildExtension('amp-facebook', '0.1', false, options);
buildExtension('amp-fit-text', '0.1', true, options);
buildExtension('amp-font', '0.1', false, options);
buildExtension('amp-iframe', '0.1', false, options);
Expand Down Expand Up @@ -319,6 +320,7 @@ function buildExamples(watch) {
buildExample('metadata-examples/video-microdata.amp.html');
buildExample('everything.amp.html');
buildExample('font.amp.html');
buildExample('facebook.amp.html');
buildExample('instagram.amp.html');
buildExample('pinterest.amp.html');
buildExample('released.amp.html');
Expand Down

0 comments on commit 6679db1

Please sign in to comment.