From cdbabc6a121113f7a1a61fe6bbb8d25362b07eda Mon Sep 17 00:00:00 2001 From: hamousavi Date: Mon, 5 Feb 2018 09:37:40 -0500 Subject: [PATCH 01/65] the code for whitelist of variables in url substitution. The code does not contain functional testing --- src/service/url-replacements-impl.js | 15 +++++++++++++++ src/service/variable-source.js | 13 +++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/service/url-replacements-impl.js b/src/service/url-replacements-impl.js index 9defb7aeb5bb..627ce4c98726 100644 --- a/src/service/url-replacements-impl.js +++ b/src/service/url-replacements-impl.js @@ -74,6 +74,21 @@ export class GlobalVariableSource extends VariableSource { /** @const {!./ampdoc-impl.AmpDoc} */ this.ampdoc = ampdoc; + // A meta[name="amp-action-whitelist"] tag, if present, contains, + // in its content attribute, a whitelist of actions on the special AMP target. + if (this.ampVariableSubstitutionWhitelist_ === undefined + && this.ampdoc.getRootNode() && this.ampdoc.getRootNode().head) { + const meta = + this.ampdoc.getRootNode().head + .querySelector('meta[name="amp-variable-substitution-whitelist"]'); + + // Cache the whitelist of allowed AMP actions (if provided). + if (meta) { + this.ampVariableSubstitutionWhitelist_ = meta.getAttribute('content').split(',') + .map(action => action.trim()); + } + } + /** * @private * @const {function(!./ampdoc-impl.AmpDoc): diff --git a/src/service/variable-source.js b/src/service/variable-source.js index 601ac8727faa..16217dfb0c2a 100644 --- a/src/service/variable-source.js +++ b/src/service/variable-source.js @@ -121,6 +121,9 @@ export class VariableSource { /** @private {boolean} */ this.initialized_ = false; + + /** @const @private {!Array|undefined} */ + this.ampVariableSubstitutionWhitelist_ = undefined; } /** @@ -164,6 +167,11 @@ export class VariableSource { */ set(varName, syncResolver) { dev().assert(varName.indexOf('RETURN') == -1); + if (this.ampVariableSubstitutionWhitelist_ && + !this.ampVariableSubstitutionWhitelist_.includes(varName)) { + this.replacements_.delete(varName); + return this; + } this.replacements_[varName] = this.replacements_[varName] || {sync: undefined, async: undefined}; this.replacements_[varName].sync = syncResolver; @@ -184,6 +192,11 @@ export class VariableSource { */ setAsync(varName, asyncResolver) { dev().assert(varName.indexOf('RETURN') == -1); + if (this.ampVariableSubstitutionWhitelist_ && + !this.ampVariableSubstitutionWhitelist_.includes(varName)) { + this.replacements_.delete(varName); + return this; + } this.replacements_[varName] = this.replacements_[varName] || {sync: undefined, async: undefined}; this.replacements_[varName].async = asyncResolver; From c22162905d64427f40abf7ba48443d9ade68b3f3 Mon Sep 17 00:00:00 2001 From: hamousavi Date: Mon, 5 Feb 2018 12:29:59 -0500 Subject: [PATCH 02/65] Added some functional tests --- src/service/url-replacements-impl.js | 9 +++-- src/service/variable-source.js | 8 +++- test/functional/url-expander/test-expander.js | 37 +++++++++++++++++++ 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/service/url-replacements-impl.js b/src/service/url-replacements-impl.js index 627ce4c98726..654d09c207eb 100644 --- a/src/service/url-replacements-impl.js +++ b/src/service/url-replacements-impl.js @@ -76,7 +76,7 @@ export class GlobalVariableSource extends VariableSource { // A meta[name="amp-action-whitelist"] tag, if present, contains, // in its content attribute, a whitelist of actions on the special AMP target. - if (this.ampVariableSubstitutionWhitelist_ === undefined + if (this.ampVariableSubstitutionWhitelist_ === undefined && this.ampdoc.getRootNode() && this.ampdoc.getRootNode().head) { const meta = this.ampdoc.getRootNode().head @@ -84,10 +84,11 @@ export class GlobalVariableSource extends VariableSource { // Cache the whitelist of allowed AMP actions (if provided). if (meta) { - this.ampVariableSubstitutionWhitelist_ = meta.getAttribute('content').split(',') - .map(action => action.trim()); + this.ampVariableSubstitutionWhitelist_ = + meta.getAttribute('content').split(',') + .map(action => action.trim()); } - } + } /** * @private diff --git a/src/service/variable-source.js b/src/service/variable-source.js index 16217dfb0c2a..5ff1fa5045ef 100644 --- a/src/service/variable-source.js +++ b/src/service/variable-source.js @@ -169,9 +169,9 @@ export class VariableSource { dev().assert(varName.indexOf('RETURN') == -1); if (this.ampVariableSubstitutionWhitelist_ && !this.ampVariableSubstitutionWhitelist_.includes(varName)) { - this.replacements_.delete(varName); return this; } + this.replacements_[varName] = this.replacements_[varName] || {sync: undefined, async: undefined}; this.replacements_[varName].sync = syncResolver; @@ -194,7 +194,6 @@ export class VariableSource { dev().assert(varName.indexOf('RETURN') == -1); if (this.ampVariableSubstitutionWhitelist_ && !this.ampVariableSubstitutionWhitelist_.includes(varName)) { - this.replacements_.delete(varName); return this; } this.replacements_[varName] = @@ -259,6 +258,11 @@ export class VariableSource { * @private */ buildExpr_(keys, isV2) { + // If a whitelist is provided, the keys must all belong to the whitelist. + if (this.ampVariableSubstitutionWhitelist_) { + keys = keys.filter(key => + this.ampVariableSubstitutionWhitelist_.includes(key)); + } // The keys must be sorted to ensure that the longest keys are considered // first. This avoids a problem where a RANDOM conflicts with RANDOM_ONE. keys.sort((s1, s2) => s2.length - s1.length); diff --git a/test/functional/url-expander/test-expander.js b/test/functional/url-expander/test-expander.js index 3b699f3db91c..1435c3becd4e 100644 --- a/test/functional/url-expander/test-expander.js +++ b/test/functional/url-expander/test-expander.js @@ -14,8 +14,10 @@ * limitations under the License. */ +import {AmpDocSingle} from '../../../src/service/ampdoc-impl'; import {Expander} from '../../../src/service/url-expander/expander'; import {GlobalVariableSource} from '../../../src/service/url-replacements-impl'; +import {createElementWithAttributes} from '../../../src/dom'; describes.realWin('Expander', { amp: { @@ -86,6 +88,41 @@ describes.realWin('Expander', { }); }); + describe('#whitelist', () => { + let variableSource; + let expander; + + const mockBindings = { + RANDOM: () => 0.1234, + ABC: () => 'three', + ABCD: () => 'four', + }; + beforeEach(() => { + window.document.head.appendChild( + createElementWithAttributes(window.document, 'meta', { + name: 'amp-variable-substitution-whitelist', + content: 'ABC,ABCD,CANONICAL', + })); + const ampdoc = new AmpDocSingle(window); + variableSource = new GlobalVariableSource(ampdoc); + expander = new Expander(variableSource); + }); + + it('should not replace unwhitelisted RANDOM', () => { + const url = 'http://www.google.com/?test=RANDOM'; + const expected = 'http://www.google.com/?test=RANDOM'; + return expect(expander.expand(url, mockBindings)) + .to.eventually.equal(expected); + }); + + it('should replace whitelisted ABCD', () => { + const url = 'http://www.google.com/?test=ABCD'; + const expected = 'http://www.google.com/?test=four'; + return expect(expander.expand(url, mockBindings)) + .to.eventually.equal(expected); + }); + }); + describe('#expand', () => { function mockClientIdFn(str) { if (str === '__ga') { From ffbee6079ccf8788370b399f1f8cc7967b9709b2 Mon Sep 17 00:00:00 2001 From: Raghu Simha Date: Mon, 5 Feb 2018 12:57:16 -0500 Subject: [PATCH 03/65] Fix import ordering in amp-access-scroll (#13265) --- extensions/amp-access-scroll/0.1/scroll-impl.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/amp-access-scroll/0.1/scroll-impl.js b/extensions/amp-access-scroll/0.1/scroll-impl.js index 89ceca2bd0e4..7d97f2555512 100644 --- a/extensions/amp-access-scroll/0.1/scroll-impl.js +++ b/extensions/amp-access-scroll/0.1/scroll-impl.js @@ -14,8 +14,8 @@ * limitations under the License. */ -import {CSS} from '../../../build/amp-access-scroll-0.1.css'; import {AccessClientAdapter} from '../../amp-access/0.1/amp-access-client'; +import {CSS} from '../../../build/amp-access-scroll-0.1.css'; import {installStylesForDoc} from '../../../src/style-installer'; const TAG = 'amp-access-scroll-elt'; From 332ca05a81a83f685d8db633bb5e3fd59c17b58c Mon Sep 17 00:00:00 2001 From: nainar Date: Tue, 6 Feb 2018 05:35:11 +1100 Subject: [PATCH 04/65] Add data-locale attribute (#13204) --- 3p/facebook.js | 6 +++--- examples/facebook.amp.html | 21 +++++++++++++++++++ .../0.1/amp-facebook-comments.js | 8 ++++++- .../amp-facebook-comments.md | 6 ++++++ .../0.1/amp-facebook-like.js | 8 ++++++- .../amp-facebook-like/amp-facebook-like.md | 6 ++++++ extensions/amp-facebook/0.1/amp-facebook.js | 8 ++++++- .../0.1/test/test-amp-facebook.js | 21 ++++++++++++++++++- .../0.1/test/validator-amp-facebook.html | 8 +++++++ .../0.1/test/validator-amp-facebook.out | 8 +++++++ extensions/amp-facebook/amp-facebook.md | 6 ++++++ 11 files changed, 99 insertions(+), 7 deletions(-) diff --git a/3p/facebook.js b/3p/facebook.js index 066fbf5cfc70..fb039a80208d 100644 --- a/3p/facebook.js +++ b/3p/facebook.js @@ -29,8 +29,8 @@ import {user} from '../src/log'; * @param {!Window} global * @param {function(!Object)} cb */ -function getFacebookSdk(global, cb) { - loadScript(global, 'https://connect.facebook.net/' + dashToUnderline(window.navigator.language) + '/sdk.js', () => { +function getFacebookSdk(global, cb, locale) { + loadScript(global, 'https://connect.facebook.net/' + locale + '/sdk.js', () => { cb(global.FB); }); } @@ -126,5 +126,5 @@ export function facebook(global, data) { }); FB.init({xfbml: true, version: 'v2.5'}); - }); + }, data.locale ? data.locale : dashToUnderline(window.navigator.language)); } diff --git a/examples/facebook.amp.html b/examples/facebook.amp.html index 2308809e931b..345a194bbd74 100644 --- a/examples/facebook.amp.html +++ b/examples/facebook.amp.html @@ -25,6 +25,12 @@

Facebook

More Posts

+ + + Comments data-href="https://developers.facebook.com/docs/plugins/comments"> + + +

Like button

Like button data-href="https://www.facebook.com/testesmegadivertidos/"> + + +

Like button with faces

{ return ampFB.layoutCallback(); @@ -69,6 +74,20 @@ describes.realWin('amp-facebook', { }); }); + it('renders amp-facebook with detected locale', () => { + return getAmpFacebook(fbVideoHref, 'post').then(ampFB => { + expect(ampFB).not.to.be.undefined; + expect(ampFB.getAttribute('data-locale')).to.equal('en_US'); + }); + }); + + it('renders amp-facebook with specified locale', () => { + return getAmpFacebook(fbVideoHref, 'post', 'fr_FR').then(ampFB => { + expect(ampFB).not.to.be.undefined; + expect(ampFB.getAttribute('data-locale')).to.equal('fr_FR'); + }); + }); + it('adds fb-post element correctly', () => { const div = document.createElement('div'); div.setAttribute('id', 'c'); diff --git a/extensions/amp-facebook/0.1/test/validator-amp-facebook.html b/extensions/amp-facebook/0.1/test/validator-amp-facebook.html index 48003eed6f23..80555f4577fa 100644 --- a/extensions/amp-facebook/0.1/test/validator-amp-facebook.html +++ b/extensions/amp-facebook/0.1/test/validator-amp-facebook.html @@ -35,6 +35,14 @@ layout="responsive" data-href="https://www.facebook.com/zuck/posts/10102593740125791">
+ + + | +| +| +| | | Date: Mon, 5 Feb 2018 11:12:33 -0800 Subject: [PATCH 05/65] Document batchPlugin and how vendor add their plugin (#13065) * document batching features * add reportWindow * minor fixes * add refer to * fix test --- .../amp-analytics/0.1/batching-plugins.js | 29 +++- .../0.1/test/test-batching-plugins.js | 153 ++++++++++++++++++ extensions/amp-analytics/amp-analytics.md | 40 ++++- .../amp-analytics/integrating-analytics.md | 17 +- 4 files changed, 225 insertions(+), 14 deletions(-) create mode 100644 extensions/amp-analytics/0.1/test/test-batching-plugins.js diff --git a/extensions/amp-analytics/0.1/batching-plugins.js b/extensions/amp-analytics/0.1/batching-plugins.js index 7499a4355a50..c6794c0c9eb1 100644 --- a/extensions/amp-analytics/0.1/batching-plugins.js +++ b/extensions/amp-analytics/0.1/batching-plugins.js @@ -24,21 +24,38 @@ import {getMode} from '../../../src/mode'; export let batchSegmentDef; /** - * TODO: Add documentation! + * Please register your batch plugin function below. + * Please keep the object in alphabetic order. * Note: extraUrlParams passed in are not encoded. Please make sure to proper * encode segments and make sure the final output url is valid. */ - export const BatchingPluginFunctions = { - // TODO: Remove _ping_ from production code '_ping_': ping, }; + +/** + * Please add your batch plugin function below in alphabetic order. + * All batch plugin function should accept input of a string, an array of batchSegment + * Then return a string. + * Note: extraUrlParams passed in are not encoded. Please make sure to proper + * encode segments and make sure the final output url is valid. + */ + +// Below is a function prototype for easy copy +// /** +// * @param {string} baseUrl +// * @param {Array} batchSegments +// * @return {string} +// */ +// function ping(baseUrl, batchSegments) {} + /** - * @param {string} unusedBaseUrl - * @param {Array} unusedSegments + * @param {string} unusedBaseUrlForTesting + * @param {Array} unusedBatchSegmentsForTesting + * @return {string} */ -function ping(unusedBaseUrl, unusedSegments) { +function ping(unusedBaseUrlForTesting, unusedBatchSegmentsForTesting) { if (getMode().localDev || getMode().test) { return 'testFinalUrl'; } diff --git a/extensions/amp-analytics/0.1/test/test-batching-plugins.js b/extensions/amp-analytics/0.1/test/test-batching-plugins.js new file mode 100644 index 000000000000..cc197df7eb09 --- /dev/null +++ b/extensions/amp-analytics/0.1/test/test-batching-plugins.js @@ -0,0 +1,153 @@ +/** + * Copyright 2018 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 {BatchingPluginFunctions} from '../batching-plugins'; +import {dict} from '../../../../src/utils/object'; +import {isArray} from '../../../../src/types'; + +/** + * Please add your custom test data here in alphabetic order. + * Test data should be an object like + * { + * 'in': Array<{ + * 'baseUrl': string, + * 'batchSegments': Array + * }> + * 'out': Array + * } + */ +const pingTestData = { + 'in': [{ + 'baseUrl': 'base.com?', + 'batchSegments': [{ + 'trigger': 'click', + 'timestamp': 0, + 'extraUrlParams': 123, + }], + }], + 'out': ['testFinalUrl'], +}; + +/** + * Please register your custom test data to the test in alphabetic order. + */ +const BatchingPluginTests = { + '_ping_': pingTestData, +}; + +/** + * Real test. Plugin vendors don't need to modify + */ +describe('Batching Plugins', () => { + it('BatchPluginFunctions sort in alphabetic order', () => { + const keys = Object.keys(BatchingPluginFunctions); + const sorted = Object.keys(BatchingPluginFunctions).sort(); + expect(keys).to.deep.equal(sorted); + }); + + for (const name in BatchingPluginFunctions) { + const plugin = BatchingPluginFunctions[name]; + describe('Default tests for every plugin', () => { + it('should handle empty batchSegment array', () => { + try { + const output = plugin('base', []); + expect(output).to.be.ok; + } catch (e) { + throw e; + } + }); + + it('should handler extraUrlParams with value null', () => { + const batchSegment = dict({ + 'trigger': 'click', + 'timestamp': 0, + 'extraUrlParams': null, + }); + try { + const output = plugin('base', [batchSegment]); + expect(output).to.be.ok; + } catch (e) { + throw e; + } + }); + + it('should properly encode segments', () => { + const batchSegment = dict({ + 'trigger': 'click', + 'timestamp': 0, + 'extraUrlParams': '12?3', + }); + try { + const output = plugin('base', [batchSegment]); + expect(output).to.not.contain('12?3'); + } catch (e) { + throw e; + } + }); + + it('should return a string', () => { + const batchSegment = dict({ + 'trigger': 'click', + 'timestamp': 0, + 'extraUrlParams': '123', + }); + try { + const output = plugin('base', [batchSegment]); + expect(typeof output).to.equal('string'); + } catch (e) { + throw e; + } + }); + }); + + + describe('custom test', () => { + let testData; + let input; + let output; + + beforeEach(() => { + testData = BatchingPluginTests[name]; + input = testData['in']; + output = testData['out']; + }); + + it('has custom test', () => { + expect(testData).to.be.ok; + expect(input).to.be.ok; + expect(output).to.be.ok; + expect(isArray(input)).to.be.true; + expect(isArray(output)).to.be.true; + expect(input.length).to.be.greaterThan(0); + expect(output.length).to.be.greaterThan(0); + expect(input.length).to.equal(output.length); + }); + + it('run custom test', () => { + for (let i = 0; i < input.length; i++) { + const baseUrl = input[i].baseUrl; + expect(baseUrl).to.be.ok; + const batchSegments = input[i].batchSegments; + expect(batchSegments).to.be.ok; + expect(isArray(batchSegments)).to.be.true; + const url = output[i]; + expect(plugin(baseUrl, batchSegments)).to.equal(url); + } + }); + }); + } +}); diff --git a/extensions/amp-analytics/amp-analytics.md b/extensions/amp-analytics/amp-analytics.md index 3692d83c7f48..19133103df28 100644 --- a/extensions/amp-analytics/amp-analytics.md +++ b/extensions/amp-analytics/amp-analytics.md @@ -173,7 +173,12 @@ In this example, we specify the `config` attribute to load the configuration dat ### Configuration data objects #### Requests -The `requests` configuration object specifies the URLs used to transmit data to an analytics platform as well as batching behavior of the request. The `request-name` specifies what request should be sent in response to a particular event (e.g., `pageview`, `event`, etc.) . The `request-value` contains a https URL, the value may include placeholder tokens that can reference other requests or variables. The `request-value` can also be an object that contain optional batching configs. +The `requests` configuration object specifies the URLs used to transmit data to an analytics platform as well as batching or reporting behavior of the request. The `request-name` specifies what request should be sent in response to a particular event (e.g., `pageview`, `event`, etc.) . The `request-value` contains an https URL, the value may include placeholder tokens that can reference other requests or variables. The `request-value` can also be an object that contains optional request configs. + +##### Request configs +The properties for defining a request with an object are: + - `baseUrl`: Defines the url of the request (required). + - `reportWindow`: An optional property to specify the time (in seconds) to stop reporting requests. The trigger with `important: true` overrides the maximum report window constraint. In this example, all requests are valid. @@ -185,7 +190,8 @@ In this example, all requests are valid. }, "event": { "baseUrl": "${base}&type=event&eventId=${eventId}", - "batchInterval": 5 + "batchInterval": 5, + "reportWindow" : 30 } } ``` @@ -196,15 +202,37 @@ Some analytics providers have an already-provided configuration, which you use v To reduce the number of request pings, you can specify batching behaviors in the request configuration. Any [`extraUrlParams`](#extra-url-params) from `triggers` that use the same request are appended to the `baseUrl` of the request. The batching properties are: - - `batchInterval`: This property specifies the max time interval to wait (in seconds) before sending out a request ping. `batchInterval` can be a number or an array of numbers. Request will respect every item value in the array, and repeat the last interval value (or the single value) when reach the end of the array. + - `batchInterval`: This property specifies the time interval (in seconds) to flush request pings in the batching queue. `batchInterval` can be a number or an array of numbers (the minimum time interval is 200ms). The request will respect every value in the array, and then repeat the last interval value (or the single value) when it reaches the end of the array. + - `batchPlugin`: This property specifies the alternative plugin function to use to construct the final request url. Please reach out to the vendor to ask for the correct batch plugin to use. -For example, the following config sends out a single request ping every 3 seconds, with the first request ping looking like `https://example.com/analytics?rc=1&rc=2&rc=3` . +For example, the following config sends out a single request ping every 2 seconds, with one sample request ping looking like `https://example.com/analytics?rc=1&rc=2`. +```javascript +"requests": { + "timer": { + "baseUrl": "https://example.com/analytics?", + "batchInterval": 2, + } +} +"triggers": { + "timer": { + "on": "timer", + "request" : "timer", + "timerSpec": { + "interval": 1 + }, + "extraUrlParams": { + "rc": "${requestCount}" + } + } +} +``` +The following config sends out the first request ping after 1 second and then sends out a request every 3 seconds. The first request ping looks like `https://example.com/analytics?rc=1`, the second request ping looks like `https://example.com/analytics?rc=2&rc=3&rc=4`. ```javascript "requests": { "timer": { "baseUrl": "https://example.com/analytics?", - "batchInterval": 3, + "batchInterval": [1, 3], } } "triggers": { @@ -260,7 +288,7 @@ The `triggers` configuration object describes when an analytics request should b - `on` (required) The event to listen for. Valid values are `render-start`, `ini-load`, `click`, `scroll`, `timer`, `visible`, `hidden`, `user-error`, [`access-*`](../amp-access/amp-access-analytics.md), and [`video-*`](./amp-video-analytics.md) - `request` (required) Name of the request to send (as specified in the `requests` section). - `vars` An object containing key-value pairs used to override `vars` defined in the top level config, or to specify vars unique to this trigger. - - `important` can be specified to work with request that support batching behavior. Setting `important` to `true` can help to flush batched request queue with some certain trigger. In this case, it's possible to reduce the request pings number without losing important trigger events. + - `important` can be specified to work with requests that support the batching behavior or the report window. Setting `important` to `true` can help to flush batched request queue with some certain triggers. In this case, it's possible to reduce the request pings number without losing important trigger events. Setting `important` to `true` can also override the request's `reportWindow` value to send out important request pings regardless. - `selector` and `selectionMethod` can be specified for some triggers, such as `click` and `visible`. See [Element selector](#element-selector) for details. - `scrollSpec` (required when `on` is set to `scroll`) This configuration is used in conjunction with the `scroll` trigger. Please see below for details. - `timerSpec` (required when `on` is set to `timer`) This configuration is used in conjunction with the `timer` trigger. Please see below for details. diff --git a/extensions/amp-analytics/integrating-analytics.md b/extensions/amp-analytics/integrating-analytics.md index f516d93e111d..a09ccda2df58 100644 --- a/extensions/amp-analytics/integrating-analytics.md +++ b/extensions/amp-analytics/integrating-analytics.md @@ -8,12 +8,13 @@ If you operate a software-as-a-service tool for publishers to better understand Before you can add your analytics service to AMP HTML runtime, you may need to: * Identify the kinds of [variables](analytics-vars.md) and [requests](amp-analytics.md#requests) you'll need in an AMP HTML document for your analytics service. +* Determine if the batching plugin function is required to construct the final url if using requests with batching behavior. * Identify the triggers that result in analytics requests being sent from a page that would be relevant for your service. * Consider if and how you will [track users across](https://github.com/ampproject/amphtml/blob/master/spec/amp-managing-user-state.md) first-party and third-party AMP contexts. * Determine how your analytics dashboard handles AMP traffic. * Identify any missing functionality in `amp-analytics`, and [file requests](https://github.com/ampproject/amphtml/issues/new) for needed features. * AMP Analytics sends its variables to a preconfigured endpoint. If you do not already have an existing endpoint, review [this sample](https://github.com/ampproject/amp-publisher-sample#amp-analytics-sample) for an overview on how to build one. - * For all transport types except `iframe`, variables are sent as query string parameters in a HTTPS request. + * For all transport types except `iframe`, variables are sent as query string parameters in a HTTPS request. * For the `iframe` transport type, an iframe is created and variables are sent to it via `window.postMessage`. In this case, the message need not be a URL. This option is available only to MRC-accredited vendors. * Consider how integration with `amp-analytics` may impact any policies (particularly your privacy policy) or agreements you may have. @@ -28,6 +29,7 @@ Before you can add your analytics service to AMP HTML runtime, you may need to: 1. "optout": if needed. We currently don't have a great opt-out system, so please reach out to help us design one that works well for you. 1. An example in the [examples/analytics-vendors.amp.html](../../examples/analytics-vendors.amp.html) reference. + 1. A new batch plugin if required. Please refer to [Add Batch Plugin](#add-batch-plugin) for instructions. 1. Test the new example you put in [examples/analytics-vendors.amp.html](../../examples/analytics-vendors.amp.html) to ensure the hits from the example are working as expected. For example, the data needed is being collected and displayed in your analytics dashboard. 1. Submit a Pull Request with this patch, referencing the Intent-To-Implement issue. 1. Add your analytics service to the [list of supported Analytics Vendors](https://github.com/ampproject/docs/blob/master/content/docs/guides/analytics_amp/analytics-vendors.md) by submitting a Pull Request to the [ampproject/docs](https://github.com/ampproject/docs) repo. Include the type, description, and link to your usage documentation. @@ -42,7 +44,7 @@ Tag management services have two options for integrating with AMP Analytics: * **Endpoint approach:** Acting as the an additional endpoint for `amp-analytics`, and conducting marketing management in the backend. * **Config approach:** Conducting tag management via a dynamically generated JSON config file unique to each publisher. -The endpoint approach is the same as the standard approach detailed in the previous section. The config approach consists of creating a unique configuration for amp-analytics that is specific to each publisher and includes all of their compatible analytics packages. A publisher would include the configuration using a syntax similar to this: +The endpoint approach is the same as the standard approach detailed in the previous section. The config approach consists of creating a unique configuration for amp-analytics that is specific to each publisher and includes all of their compatible analytics packages. A publisher would include the configuration using a syntax similar to this: ```html @@ -50,6 +52,17 @@ The endpoint approach is the same as the standard approach detailed in the previ To take this approach, review the documentation for publishers' integration with AMP Analytics. +## Add Batch Plugin +Batch plugin provides an easy way to construct batched requests on client side. Follow the instructions below to add a batch plugin. +1. Create an [Intent-To-Implement issue](../../CONTRIBUTING.md#contributing-features) stating that you'll be adding the batch plugin to your analytics service AMP HTML's runtime. +1. Develop a patch that implements the following: + 1. Add new plugin function in [batching-plugins.js](0.1/batching-plugins.js) + 1. Add test coverage to your plugin function + 1. Test your example in the [examples/analytics-vendors.amp.html](../../examples/analytics-vendors.amp.html) +1. Submit a Pull Request with this patch, referencing the Intent-To-Implement issue. + +Please note that every new plugin will be reviewed by AMP team on a case by case basis. The idea is to avoid a plugin as much as possible if there is a more generic approach. + ## Further Resources * Deep Dive: [Why not just use an iframe?](why-not-iframe.md) * Deep Dive: [Managing non-authenticated user state with AMP](https://github.com/ampproject/amphtml/blob/master/spec/amp-managing-user-state.md) From 910d343cfbbd078a999a6b821bfa68f4673a0846 Mon Sep 17 00:00:00 2001 From: erwin mombay Date: Mon, 5 Feb 2018 11:24:36 -0800 Subject: [PATCH 06/65] :book: add AMP emoji conventional changelogs (#13037) * :book: add AMP emoji conventional changelogs * apply recommendations --- .github/PULL_REQUEST_TEMPLATE.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 16525768b500..eca9aebd2c61 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -16,3 +16,19 @@ Bullet points like really help with making this more readable. Fixes/Closes/Related-to #1 (enter issue number, except in rare cases where none exists). + +It is also helpful to add an emoji before the commit message to identify the kind of work done on a single commit. See the following suggestions below: + +- :bug: - `:bug:` Bug fix +- :sparkles: - `:sparkles:` New feature +- :white_check_mark: - `:white_check_mark:` Tests +- :fire: - `:fire:` P0 +- :rocket: - `:rocket:` Performance improvements +- :crayon: - `:crayon:` CSS / Styling +- :wheelchair: - `:wheelchair:` Accessibility +- :globe_with_meridians: - `:globe_with_meridians:` i18n (Internationalization) +- :book: - `:book:` Documentation +- :recycle: - `:recycle:` Refactoring (like moving around code w/o any changes) +- :building_construction: - `:building_construction:` Infrastructure / Tooling / Builds / CI +- :rewind: - `:rewind:` Revert +- :put_litter_in_its_place: - `:put_litter_in_its_place:` Deleting code From e2dedbd3072367eda54de92ecc1e2226952c2b14 Mon Sep 17 00:00:00 2001 From: donttrustthisbot Date: Mon, 5 Feb 2018 14:25:33 -0500 Subject: [PATCH 07/65] cron job from @erwinmombay to update size.txt and size.csv (#13263) --- test/size.txt | 155 +++++++++++++++++++++++++------------------------- 1 file changed, 78 insertions(+), 77 deletions(-) diff --git a/test/size.txt b/test/size.txt index feef60e6998f..efa0a6a68fca 100644 --- a/test/size.txt +++ b/test/size.txt @@ -1,105 +1,106 @@ max | min | gzip | file --- | --- | --- | --- - 310 kB | 30.4 kB | 11.9 kB | alp.js / alp.max.js - 359 kB | 17.1 kB | 6.71 kB | amp4ads-host-v0.js / amp-inabox-host.js -1.57 MB | 243 kB | 76.1 kB | amp4ads-v0.js / amp-inabox.js + 310 kB | 30.9 kB | 12 kB | alp.js / alp.max.js + 360 kB | 17.6 kB | 6.86 kB | amp4ads-host-v0.js / amp-inabox-host.js +1.58 MB | 245 kB | 76.8 kB | amp4ads-v0.js / amp-inabox.js 2.32 kB | 790 B | 469 B | examiner.js / examiner.max.js -1.52 MB | 245 kB | 76.7 kB | shadow-v0.js / amp-shadow.js +1.53 MB | 247 kB | 77.4 kB | shadow-v0.js / amp-shadow.js 2.35 kB | 386 B | 250 B | sw-kill.js / sw-kill.max.js 19.1 kB | 1.81 kB | 992 B | sw.js / sw.max.js -1.54 MB | 239 kB | 74.8 kB | v0.js / amp.js - 179 kB | 38.2 kB | 11.9 kB | ww.js / ww.max.js - 423 kB | 31 kB | 10.7 kB | v0/amp-3q-player-0.1.js - 446 kB | 40.6 kB | 13.6 kB | v0/amp-access-0.1.js +1.55 MB | 241 kB | 75.6 kB | v0.js / amp.js + 180 kB | 38.4 kB | 11.9 kB | ww.js / ww.max.js + 424 kB | 31.7 kB | 10.9 kB | v0/amp-3q-player-0.1.js + 447 kB | 40.6 kB | 13.6 kB | v0/amp-access-0.1.js 240 kB | 11.3 kB | 4.4 kB | v0/amp-access-laterpay-0.1.js - 143 kB | 4.01 kB | 1.69 kB | v0/amp-accordion-0.1.js - 847 kB | 55.8 kB | 18.8 kB | v0/amp-ad-0.1.js - 350 kB | 47 kB | 14.1 kB | v0/amp-ad-exit-0.1.js - 763 kB | 68 kB | 23.7 kB | v0/amp-ad-network-adsense-impl-0.1.js - 720 kB | 51.4 kB | 18.2 kB | v0/amp-ad-network-adzerk-impl-0.1.js - 709 kB | 50.7 kB | 18.1 kB | v0/amp-ad-network-cloudflare-impl-0.1.js - 869 kB | 87.8 kB | 30 kB | v0/amp-ad-network-doubleclick-impl-0.1.js - 709 kB | 51.1 kB | 18.2 kB | v0/amp-ad-network-fake-impl-0.1.js - 705 kB | 49.6 kB | 17.7 kB | v0/amp-ad-network-gmossp-impl-0.1.js - 705 kB | 49.4 kB | 17.7 kB | v0/amp-ad-network-triplelift-impl-0.1.js - 684 kB | 98.6 kB | 31.2 kB | v0/amp-analytics-0.1.js - 91 kB | 4.58 kB | 2.15 kB | v0/amp-anim-0.1.js + 247 kB | 8 kB | 3.41 kB | v0/amp-access-scroll-0.1.js + 144 kB | 4.01 kB | 1.69 kB | v0/amp-accordion-0.1.js + 849 kB | 55.7 kB | 18.8 kB | v0/amp-ad-0.1.js + 351 kB | 47.2 kB | 14.1 kB | v0/amp-ad-exit-0.1.js + 766 kB | 68.4 kB | 23.9 kB | v0/amp-ad-network-adsense-impl-0.1.js + 725 kB | 52.6 kB | 18.6 kB | v0/amp-ad-network-adzerk-impl-0.1.js + 711 kB | 51.1 kB | 18.2 kB | v0/amp-ad-network-cloudflare-impl-0.1.js + 872 kB | 88.1 kB | 30.2 kB | v0/amp-ad-network-doubleclick-impl-0.1.js + 711 kB | 51.5 kB | 18.3 kB | v0/amp-ad-network-fake-impl-0.1.js + 708 kB | 50 kB | 17.8 kB | v0/amp-ad-network-gmossp-impl-0.1.js + 708 kB | 49.9 kB | 17.8 kB | v0/amp-ad-network-triplelift-impl-0.1.js + 772 kB | 99.3 kB | 31.5 kB | v0/amp-analytics-0.1.js +91.5 kB | 4.58 kB | 2.15 kB | v0/amp-anim-0.1.js 504 kB | 99.3 kB | 31 kB | v0/amp-animation-0.1.js 267 kB | 12.6 kB | 3.97 kB | v0/amp-apester-media-0.1.js - 252 kB | 10.2 kB | 3.99 kB | v0/amp-app-banner-0.1.js - 120 kB | 4.61 kB | 1.99 kB | v0/amp-audio-0.1.js - 312 kB | 11.7 kB | 4.74 kB | v0/amp-auto-ads-0.1.js - 610 kB | 78.9 kB | 26.6 kB | v0/amp-bind-0.1.js - 426 kB | 32.6 kB | 11.1 kB | v0/amp-brid-player-0.1.js - 126 kB | 3.1 kB | 1.37 kB | v0/amp-brightcove-0.1.js + 252 kB | 10.2 kB | 4 kB | v0/amp-app-banner-0.1.js + 121 kB | 4.61 kB | 1.99 kB | v0/amp-audio-0.1.js + 311 kB | 11.7 kB | 4.74 kB | v0/amp-auto-ads-0.1.js + 612 kB | 79.8 kB | 27 kB | v0/amp-bind-0.1.js + 427 kB | 33.3 kB | 11.4 kB | v0/amp-brid-player-0.1.js + 127 kB | 3.1 kB | 1.37 kB | v0/amp-brightcove-0.1.js 238 kB | 3.17 kB | 1.55 kB | v0/amp-call-tracking-0.1.js - 411 kB | 24.9 kB | 8.13 kB | v0/amp-carousel-0.1.js -75.5 kB | 809 B | 477 B | v0/amp-compare-slider-0.1.js - 132 kB | 9.77 kB | 4.49 kB | v0/amp-crypto-polyfill-0.1.js - 428 kB | 32.8 kB | 11.3 kB | v0/amp-dailymotion-0.1.js -1.04 MB | 412 kB | 99.6 kB | v0/amp-date-picker-0.1.js + 410 kB | 25.1 kB | 8.23 kB | v0/amp-carousel-0.1.js + 76 kB | 809 B | 479 B | v0/amp-compare-slider-0.1.js + 133 kB | 9.77 kB | 4.49 kB | v0/amp-crypto-polyfill-0.1.js + 429 kB | 33.5 kB | 11.5 kB | v0/amp-dailymotion-0.1.js +1.04 MB | 394 kB | 100 kB | v0/amp-date-picker-0.1.js 208 kB | 2.08 kB | 1.07 kB | v0/amp-dynamic-css-classes-0.1.js 236 kB | 5.01 kB | 2.2 kB | v0/amp-experiment-0.1.js - 339 kB | 11.2 kB | 4.98 kB | v0/amp-facebook-0.1.js - 339 kB | 11.2 kB | 4.97 kB | v0/amp-facebook-comments-0.1.js - 339 kB | 11.2 kB | 4.98 kB | v0/amp-facebook-like-0.1.js -80.2 kB | 3.13 kB | 1.41 kB | v0/amp-fit-text-0.1.js + 338 kB | 11.2 kB | 4.98 kB | v0/amp-facebook-0.1.js + 338 kB | 11.2 kB | 4.97 kB | v0/amp-facebook-comments-0.1.js + 338 kB | 11.2 kB | 4.98 kB | v0/amp-facebook-like-0.1.js +80.8 kB | 3.13 kB | 1.42 kB | v0/amp-fit-text-0.1.js 217 kB | 4.3 kB | 2 kB | v0/amp-font-0.1.js 338 kB | 25.7 kB | 9.1 kB | v0/amp-form-0.1.js -93.2 kB | 4.29 kB | 1.79 kB | v0/amp-fx-flying-carpet-0.1.js - 273 kB | 4.84 kB | 2.25 kB | v0/amp-fx-parallax-0.1.js - 424 kB | 31.5 kB | 10.9 kB | v0/amp-gfycat-0.1.js - 339 kB | 10.8 kB | 4.81 kB | v0/amp-gist-0.1.js - 283 kB | 4.9 kB | 2.2 kB | v0/amp-google-vrview-image-0.1.js + 245 kB | 6.48 kB | 2.91 kB | v0/amp-fx-collection-0.1.js +93.7 kB | 4.29 kB | 1.79 kB | v0/amp-fx-flying-carpet-0.1.js + 425 kB | 32.2 kB | 11.2 kB | v0/amp-gfycat-0.1.js + 338 kB | 10.8 kB | 4.81 kB | v0/amp-gist-0.1.js + 282 kB | 4.9 kB | 2.2 kB | v0/amp-google-vrview-image-0.1.js 239 kB | 5.7 kB | 2.29 kB | v0/amp-gwd-animation-0.1.js - 106 kB | 1.65 kB | 861 B | v0/amp-hulu-0.1.js - 336 kB | 14.9 kB | 6.14 kB | v0/amp-iframe-0.1.js - 485 kB | 38.3 kB | 13.5 kB | v0/amp-ima-video-0.1.js - 390 kB | 29.6 kB | 9.71 kB | v0/amp-image-lightbox-0.1.js - 388 kB | 25.4 kB | 8.63 kB | v0/amp-image-viewer-0.1.js - 127 kB | 2.46 kB | 1.22 kB | v0/amp-imgur-0.1.js - 131 kB | 4.27 kB | 1.94 kB | v0/amp-instagram-0.1.js + 106 kB | 1.65 kB | 862 B | v0/amp-hulu-0.1.js + 335 kB | 14.9 kB | 6.14 kB | v0/amp-iframe-0.1.js + 485 kB | 39 kB | 13.8 kB | v0/amp-ima-video-0.1.js + 391 kB | 29.6 kB | 9.71 kB | v0/amp-image-lightbox-0.1.js + 375 kB | 24.7 kB | 8.32 kB | v0/amp-image-viewer-0.1.js + 127 kB | 2.46 kB | 1.23 kB | v0/amp-imgur-0.1.js + 132 kB | 4.27 kB | 1.94 kB | v0/amp-instagram-0.1.js 240 kB | 7.91 kB | 3.4 kB | v0/amp-install-serviceworker-0.1.js - 124 kB | 2.27 kB | 1.18 kB | v0/amp-izlesene-0.1.js + 125 kB | 2.27 kB | 1.18 kB | v0/amp-izlesene-0.1.js 107 kB | 2.42 kB | 1.06 kB | v0/amp-jwplayer-0.1.js - 125 kB | 3.17 kB | 1.41 kB | v0/amp-kaltura-player-0.1.js + 126 kB | 3.17 kB | 1.42 kB | v0/amp-kaltura-player-0.1.js 315 kB | 12.3 kB | 4.84 kB | v0/amp-lightbox-0.1.js - 472 kB | 33.1 kB | 10.7 kB | v0/amp-lightbox-viewer-0.1.js - 266 kB | 7.01 kB | 2.9 kB | v0/amp-list-0.1.js - 325 kB | 12 kB | 4.74 kB | v0/amp-live-list-0.1.js - 340 kB | 11.2 kB | 4.96 kB | v0/amp-mathml-0.1.js - 223 kB | 39.4 kB | 14.3 kB | v0/amp-mustache-0.1.js - 425 kB | 31.8 kB | 11 kB | v0/amp-nexxtv-player-0.1.js -77.7 kB | 2.32 kB | 1 kB | v0/amp-o2-player-0.1.js - 424 kB | 31.4 kB | 10.8 kB | v0/amp-ooyala-player-0.1.js + 472 kB | 34.1 kB | 10.9 kB | v0/amp-lightbox-viewer-0.1.js + 269 kB | 8.31 kB | 3.53 kB | v0/amp-list-0.1.js + 324 kB | 12 kB | 4.74 kB | v0/amp-live-list-0.1.js + 338 kB | 11.2 kB | 4.96 kB | v0/amp-mathml-0.1.js + 224 kB | 39.5 kB | 14.4 kB | v0/amp-mustache-0.1.js + 426 kB | 32.5 kB | 11.2 kB | v0/amp-nexxtv-player-0.1.js +78.3 kB | 2.32 kB | 1 kB | v0/amp-o2-player-0.1.js + 425 kB | 32.1 kB | 11.1 kB | v0/amp-ooyala-player-0.1.js 274 kB | 18.4 kB | 5.27 kB | v0/amp-pinterest-0.1.js - 314 kB | 12 kB | 6.24 kB | v0/amp-playbuzz-0.1.js - 266 kB | 6.69 kB | 2.88 kB | v0/amp-position-observer-0.1.js -76.1 kB | 1.4 kB | 707 B | v0/amp-reach-player-0.1.js - 304 kB | 9.44 kB | 4.15 kB | v0/amp-reddit-0.1.js + 312 kB | 12 kB | 6.24 kB | v0/amp-playbuzz-0.1.js + 268 kB | 6.91 kB | 2.95 kB | v0/amp-position-observer-0.1.js +76.7 kB | 1.4 kB | 708 B | v0/amp-reach-player-0.1.js + 303 kB | 9.44 kB | 4.15 kB | v0/amp-reddit-0.1.js 117 kB | 2.47 kB | 1.22 kB | v0/amp-riddle-quiz-0.1.js 221 kB | 5.75 kB | 2.37 kB | v0/amp-selector-0.1.js - 289 kB | 4.73 kB | 2.18 kB | v0/amp-share-tracking-0.1.js + 288 kB | 4.73 kB | 2.18 kB | v0/amp-share-tracking-0.1.js 272 kB | 9.88 kB | 3.86 kB | v0/amp-sidebar-0.1.js 257 kB | 7.45 kB | 3.13 kB | v0/amp-slides-0.1.js 253 kB | 14.1 kB | 5.91 kB | v0/amp-social-share-0.1.js -77.4 kB | 1.91 kB | 895 B | v0/amp-soundcloud-0.1.js -78.5 kB | 2.95 kB | 1.05 kB | v0/amp-springboard-player-0.1.js - 115 kB | 6.14 kB | 2.58 kB | v0/amp-sticky-ad-1.0.js - 768 kB | 135 kB | 32.6 kB | v0/amp-story-0.1.js +77.9 kB | 1.91 kB | 897 B | v0/amp-soundcloud-0.1.js + 79 kB | 2.95 kB | 1.05 kB | v0/amp-springboard-player-0.1.js + 116 kB | 6.14 kB | 2.58 kB | v0/amp-sticky-ad-1.0.js + 793 kB | 142 kB | 34.4 kB | v0/amp-story-0.1.js 113 kB | 39.9 kB | 7.41 kB | v0/amp-timeago-0.1.js - 340 kB | 11.4 kB | 5.01 kB | v0/amp-twitter-0.1.js + 338 kB | 11.4 kB | 5.01 kB | v0/amp-twitter-0.1.js 232 kB | 8.21 kB | 3.31 kB | v0/amp-user-notification-0.1.js - 471 kB | 36.3 kB | 12.4 kB | v0/amp-video-0.1.js - 254 kB | 7.24 kB | 3.21 kB | v0/amp-viewer-integration-0.1.js -76.7 kB | 1.57 kB | 802 B | v0/amp-vimeo-0.1.js -69.2 kB | 1.44 kB | 757 B | v0/amp-vine-0.1.js - 794 kB | 513 kB | 168 kB | v0/amp-viz-vega-0.1.js - 245 kB | 4.67 kB | 1.97 kB | v0/amp-vk-0.1.js + 470 kB | 37 kB | 12.6 kB | v0/amp-video-0.1.js + 254 kB | 7.24 kB | 3.22 kB | v0/amp-viewer-integration-0.1.js +77.2 kB | 1.57 kB | 803 B | v0/amp-vimeo-0.1.js +69.7 kB | 1.44 kB | 758 B | v0/amp-vine-0.1.js + 793 kB | 513 kB | 168 kB | v0/amp-viz-vega-0.1.js + 246 kB | 4.67 kB | 1.97 kB | v0/amp-vk-0.1.js 355 kB | 18.5 kB | 6.4 kB | v0/amp-web-push-0.1.js - 425 kB | 31.4 kB | 10.8 kB | v0/amp-wistia-player-0.1.js - 433 kB | 33.5 kB | 11.6 kB | v0/amp-youtube-0.1.js + 426 kB | 32 kB | 11.1 kB | v0/amp-wistia-player-0.1.js + 434 kB | 34.5 kB | 11.9 kB | v0/amp-youtube-0.1.js 23.1 kB | 3.32 kB | 1.55 kB | v0/cache-service-worker-0.1.js - 130 kB | 10.6 kB | 4.02 kB | current-min/ampcontext-v0.js / current/ampcontext-lib.js + 130 kB | 10.6 kB | 4.04 kB | current-min/ampcontext-v0.js / current/ampcontext-lib.js 562 kB | 122 kB | 39.5 kB | current-min/f.js / current/integration.js -89.7 kB | 7.66 kB | 3.01 kB | current-min/iframe-transport-client-v0.js / current/iframe-transport-client-lib.js \ No newline at end of file +90.2 kB | 7.75 kB | 3.03 kB | current-min/iframe-transport-client-v0.js / current/iframe-transport-client-lib.js \ No newline at end of file From d69a8595579faee6cf082ec40326a92ea9f14980 Mon Sep 17 00:00:00 2001 From: Cathy Zhu Date: Mon, 5 Feb 2018 11:33:33 -0800 Subject: [PATCH 08/65] Rename lightbox viewer to lightbox gallery and clean up examples (#13266) * Rename lightbox-viewer to lightbox-gallery * Rename lightbox css from viewer to gallery * Remove dead manual test pages --- build-system/tasks/presubmit-checks.js | 4 +- .../amp-image-viewer/0.1/amp-image-viewer.css | 13 ++ .../amp-image-viewer/0.1/amp-image-viewer.js | 2 +- .../0.1/amp-lightbox-gallery.css} | 77 ++++----- .../0.1/amp-lightbox-gallery.js} | 78 ++++----- .../0.1/service/lightbox-manager-impl.js | 8 +- .../0.1/test/test-amp-lightbox-gallery.js} | 15 +- .../OWNERS.yaml | 0 gulpfile.js | 2 +- src/runtime.js | 20 +-- .../amp-lightbox-carousel-demo.amp.html | 13 +- test/manual/amp-lightbox-viewer-demo.amp.html | 150 ----------------- .../manual/amp-lightbox-viewer-mixed.amp.html | 65 -------- .../manual/amp-lightbox-viewer-optin.amp.html | 156 ------------------ .../amp-lightbox-viewer-optout.amp.html | 61 ------- ...article-with-lightbox-2.0-gallery.amp.html | 8 +- tools/experiments/experiments.js | 8 +- 17 files changed, 107 insertions(+), 573 deletions(-) rename extensions/{amp-lightbox-viewer/0.1/amp-lightbox-viewer.css => amp-lightbox-gallery/0.1/amp-lightbox-gallery.css} (82%) rename extensions/{amp-lightbox-viewer/0.1/amp-lightbox-viewer.js => amp-lightbox-gallery/0.1/amp-lightbox-gallery.js} (93%) rename extensions/{amp-lightbox-viewer => amp-lightbox-gallery}/0.1/service/lightbox-manager-impl.js (97%) rename extensions/{amp-lightbox-viewer/0.1/test/test-amp-lightbox-viewer.js => amp-lightbox-gallery/0.1/test/test-amp-lightbox-gallery.js} (94%) rename extensions/{amp-lightbox-viewer => amp-lightbox-gallery}/OWNERS.yaml (100%) delete mode 100644 test/manual/amp-lightbox-viewer-demo.amp.html delete mode 100644 test/manual/amp-lightbox-viewer-mixed.amp.html delete mode 100644 test/manual/amp-lightbox-viewer-optin.amp.html delete mode 100644 test/manual/amp-lightbox-viewer-optout.amp.html diff --git a/build-system/tasks/presubmit-checks.js b/build-system/tasks/presubmit-checks.js index 22ea6e644fd7..5ab38efa8888 100644 --- a/build-system/tasks/presubmit-checks.js +++ b/build-system/tasks/presubmit-checks.js @@ -514,7 +514,7 @@ const forbiddenTerms = { message: 'requireLayout is restricted b/c it affects non-contained elements', // eslint-disable-line max-len whitelist: [ 'extensions/amp-animation/0.1/web-animations.js', - 'extensions/amp-lightbox-viewer/0.1/amp-lightbox-viewer.js', + 'extensions/amp-lightbox-gallery/0.1/amp-lightbox-gallery.js', 'src/service/resources-impl.js', ], }, @@ -753,7 +753,7 @@ const forbiddenTermsSrcInclusive = { 'extensions/amp-a4a/0.1/amp-a4a.js', 'extensions/amp-ad-network-adsense-impl/0.1/amp-ad-network-adsense-impl.js', // eslint-disable-line max-len 'extensions/amp-ad-network-doubleclick-impl/0.1/amp-ad-network-doubleclick-impl.js', // eslint-disable-line max-len - 'extensions/amp-lightbox-viewer/0.1/amp-lightbox-viewer.js', + 'extensions/amp-lightbox-gallery/0.1/amp-lightbox-gallery.js', ], }, 'loadElementClass': { diff --git a/extensions/amp-image-viewer/0.1/amp-image-viewer.css b/extensions/amp-image-viewer/0.1/amp-image-viewer.css index b6ebb2fcfe95..2024db49f0bd 100644 --- a/extensions/amp-image-viewer/0.1/amp-image-viewer.css +++ b/extensions/amp-image-viewer/0.1/amp-image-viewer.css @@ -18,3 +18,16 @@ position: absolute; z-index: 1; } + +.i-amphtml-image-viewer { + position: absolute; + z-index: 1; + top: 0; + left: 0; + right: 0; + bottom: 0; + /* This is necessary due to crbug/248522 where touch events fail after + transform */ + overflow: hidden; + transform: translate3d(0, 0, 0); +} diff --git a/extensions/amp-image-viewer/0.1/amp-image-viewer.js b/extensions/amp-image-viewer/0.1/amp-image-viewer.js index 8ff5babfd05c..d0727a78ecd6 100644 --- a/extensions/amp-image-viewer/0.1/amp-image-viewer.js +++ b/extensions/amp-image-viewer/0.1/amp-image-viewer.js @@ -120,7 +120,7 @@ export class AmpImageViewer extends AMP.BaseElement { /** @override */ buildCallback() { this.vsync_ = this.getVsync(); - this.element.classList.add('i-amphtml-image-lightbox-viewer'); + this.element.classList.add('i-amphtml-image-viewer'); const children = this.getRealChildren(); user().assert( children.length == 1 && children[0].tagName == 'AMP-IMG', diff --git a/extensions/amp-lightbox-viewer/0.1/amp-lightbox-viewer.css b/extensions/amp-lightbox-gallery/0.1/amp-lightbox-gallery.css similarity index 82% rename from extensions/amp-lightbox-viewer/0.1/amp-lightbox-viewer.css rename to extensions/amp-lightbox-gallery/0.1/amp-lightbox-gallery.css index 2c688a933a82..8d4453a4bbac 100644 --- a/extensions/amp-lightbox-viewer/0.1/amp-lightbox-viewer.css +++ b/extensions/amp-lightbox-gallery/0.1/amp-lightbox-gallery.css @@ -14,7 +14,7 @@ * limitations under the License. */ -.i-amphtml-lbv { +.i-amphtml-lbg { position: fixed !important; top: 0 !important; left: 0 !important; @@ -23,8 +23,8 @@ z-index: 2147483642; } -.i-amphtml-lbv-mask, -.i-amphtml-lbv-gallery { +.i-amphtml-lbg-mask, +.i-amphtml-lbg-gallery { position: absolute !important; left: 0 !important; right: 0 !important; @@ -37,18 +37,18 @@ background-color: rgba(0,0,0, 1) !important; } -.i-amphtml-lbv-mask { +.i-amphtml-lbg-mask { top:0 !important; } -.i-amphtml-lbv-gallery { +.i-amphtml-lbg-gallery { display: none; top: 0 !important; padding-top: 50px !important; overflow: auto !important; } -.i-amphtml-lbv-top-bar { +.i-amphtml-lbg-top-bar { position: absolute !important; left: 0 !important; right: 0 !important; @@ -58,18 +58,18 @@ z-index: 2; } -.i-amphtml-lbv-controls { +.i-amphtml-lbg-controls { opacity: 0; animation: fadeIn ease-in 0.4s 1 forwards; } -.i-amphtml-lbv-controls.fade-out +.i-amphtml-lbg-controls.fade-out { opacity: 1; animation: fadeOut linear 0.4s 1 forwards; } -.i-amphtml-lbv-top-bar.fullscreen .i-amphtml-lbv-top-bar-top-gradient { +.i-amphtml-lbg-top-bar.fullscreen .i-amphtml-lbg-top-bar-top-gradient { position: absolute !important; left: 0 !important; right: 0 !important; @@ -78,7 +78,7 @@ background: linear-gradient(rgba(0,0,0,0.3), rgba(0,0,0,0)); } -.i-amphtml-lbv-desc-box { +.i-amphtml-lbg-desc-box { position: absolute !important; left: 0 !important; right: 0 !important; @@ -90,12 +90,12 @@ animation: fadeIn ease-in 0.4s 1 forwards; } -.i-amphtml-lbv-desc-box.standard { +.i-amphtml-lbg-desc-box.standard { max-height: 6rem; background: linear-gradient(rgba(0,0,0,0), rgba(0,0,0,0.5)); } -.i-amphtml-lbv-desc-box.overflow { +.i-amphtml-lbg-desc-box.overflow { overflow-y: auto !important; -webkit-overflow-scrolling: touch !important; background-color: rgba(0,0,0,0.5); @@ -103,19 +103,19 @@ padding-top: 50px !important; } -.i-amphtml-lbv-desc-text { +.i-amphtml-lbg-desc-text { padding: 20px; position: relative !important; } -.i-amphtml-lbv-desc-text.non-expanded { +.i-amphtml-lbg-desc-text.non-expanded { overflow-y: hidden !important; overflow-x: hidden !important; text-overflow: ellipsis !important; white-space: nowrap !important; } -.i-amphtml-lbv[gallery-view] .i-amphtml-lbv-gallery { +.i-amphtml-lbg[gallery-view] .i-amphtml-lbg-gallery { display: grid; justify-content: center; grid-gap: 5px; @@ -125,33 +125,33 @@ } @media (min-width: 1024px) { - .i-amphtml-lbv[gallery-view] .i-amphtml-lbv-gallery { + .i-amphtml-lbg[gallery-view] .i-amphtml-lbg-gallery { grid-template-columns: repeat(4, calc(1024px/4 - 5px * 5 / 4)); } - div.i-amphtml-lbv-top-bar { + div.i-amphtml-lbg-top-bar { height: 100px !important; } - div.amp-lbv-button-close, - div.amp-lbv-button-gallery, - div.amp-lbv-button-slide { + div.amp-lbg-button-close, + div.amp-lbg-button-gallery, + div.amp-lbg-button-slide { width: 40px; height: 40px; margin: 20px; } } -.i-amphtml-lbv[gallery-view] .i-amphtml-lbv-gallery.i-amphtml-lbv-gallery-hidden { +.i-amphtml-lbg[gallery-view] .i-amphtml-lbg-gallery.i-amphtml-lbg-gallery-hidden { display: none; } -.i-amphtml-lbv-gallery-thumbnail { +.i-amphtml-lbg-gallery-thumbnail { position: relative; padding-top: 100%; } -.i-amphtml-lbv-gallery-thumbnail-img { +.i-amphtml-lbg-gallery-thumbnail-img { width: 100%; height: 100%; position: absolute; @@ -161,9 +161,9 @@ } /* Controls */ -.amp-lbv-button-close, -.amp-lbv-button-gallery, -.amp-lbv-button-slide { +.amp-lbg-button-close, +.amp-lbg-button-gallery, +.amp-lbg-button-slide { position: absolute !important; cursor: pointer; width: 24px; @@ -173,47 +173,34 @@ background-position: center center; } -.amp-lbv-button-close { +.amp-lbg-button-close { top: 0; right: 0; background-image: url('data:image/svg+xml;charset=utf-8,'); } -.amp-lbv-button-gallery { +.amp-lbg-button-gallery { top: 0; left: 0; background-image: url('data:image/svg+xml;charset=utf-8,'); } -.amp-lbv-button-slide { +.amp-lbg-button-slide { top: 0; left: 0; display: none; background-image: url('data:image/svg+xml;charset=utf-8,'); } -.i-amphtml-lbv[gallery-view] .amp-lbv-button-gallery { +.i-amphtml-lbg[gallery-view] .amp-lbg-button-gallery { display: none; } -.i-amphtml-lbv[gallery-view] .amp-lbv-button-slide { +.i-amphtml-lbg[gallery-view] .amp-lbg-button-slide { display: block; } -.i-amphtml-image-lightbox-viewer { - position: absolute; - z-index: 1; - top: 0; - left: 0; - right: 0; - bottom: 0; - /* This is necessary due to crbug/248522 where touch events fail after - transform */ - overflow: hidden; - transform: translate3d(0, 0, 0); -} - -.i-amphtml-lightbox-viewer-trans { +.i-amphtml-lightbox-gallery-trans { pointer-events: none !important; position: fixed; z-index: 1001; diff --git a/extensions/amp-lightbox-viewer/0.1/amp-lightbox-viewer.js b/extensions/amp-lightbox-gallery/0.1/amp-lightbox-gallery.js similarity index 93% rename from extensions/amp-lightbox-viewer/0.1/amp-lightbox-viewer.js rename to extensions/amp-lightbox-gallery/0.1/amp-lightbox-gallery.js index 85d7eced6fec..0870ae8d06b6 100644 --- a/extensions/amp-lightbox-viewer/0.1/amp-lightbox-viewer.js +++ b/extensions/amp-lightbox-gallery/0.1/amp-lightbox-gallery.js @@ -17,7 +17,7 @@ import * as st from '../../../src/style'; import * as tr from '../../../src/transition'; import {Animation} from '../../../src/animation'; -import {CSS} from '../../../build/amp-lightbox-viewer-0.1.css'; +import {CSS} from '../../../build/amp-lightbox-gallery-0.1.css'; import {CommonSignals} from '../../../src/common-signals'; import {Gestures} from '../../../src/gesture'; import {KeyCodes} from '../../../src/utils/key-codes'; @@ -40,8 +40,8 @@ import {setStyle, toggle} from '../../../src/style'; /** @const */ -const TAG = 'amp-lightbox-viewer'; -const DEFAULT_VIEWER_ID = 'amp-lightbox-viewer'; +const TAG = 'amp-lightbox-gallery'; +const DEFAULT_GALLERY_ID = 'amp-lightbox-gallery'; /** * Set of namespaces that indicate the lightbox controls mode. @@ -86,7 +86,7 @@ let LightboxElementMetadataDef_; /** * @private visible for testing. */ -export class AmpLightboxViewer extends AMP.BaseElement { +export class AmpLightboxGallery extends AMP.BaseElement { /** @param {!AmpElement} element */ constructor(element) { @@ -167,7 +167,7 @@ export class AmpLightboxViewer extends AMP.BaseElement { const viewer = Services.viewerForDoc(this.getAmpDoc()); viewer.whenFirstVisible().then(() => { this.container_ = this.win.document.createElement('div'); - this.container_.classList.add('i-amphtml-lbv'); + this.container_.classList.add('i-amphtml-lbg'); this.element.appendChild(this.container_); this.manager_.maybeInit(); this.buildMask_(); @@ -205,7 +205,7 @@ export class AmpLightboxViewer extends AMP.BaseElement { buildMask_() { dev().assert(this.container_); const mask = this.win.document.createElement('div'); - mask.classList.add('i-amphtml-lbv-mask'); + mask.classList.add('i-amphtml-lbg-mask'); this.container_.appendChild(mask); } @@ -322,13 +322,13 @@ export class AmpLightboxViewer extends AMP.BaseElement { */ buildDescriptionBox_() { this.descriptionBox_ = this.win.document.createElement('div'); - this.descriptionBox_.classList.add('i-amphtml-lbv-desc-box'); - this.descriptionBox_.classList.add('i-amphtml-lbv-controls'); + this.descriptionBox_.classList.add('i-amphtml-lbg-desc-box'); + this.descriptionBox_.classList.add('i-amphtml-lbg-controls'); this.descriptionBox_.classList.add('standard'); this.descriptionTextArea_ = this.win.document.createElement('div'); - this.descriptionTextArea_.classList.add('i-amphtml-lbv-desc-text'); + this.descriptionTextArea_.classList.add('i-amphtml-lbg-desc-text'); this.descriptionTextArea_.classList.add('non-expanded'); this.descriptionBox_.appendChild(this.descriptionTextArea_); @@ -446,11 +446,11 @@ export class AmpLightboxViewer extends AMP.BaseElement { buildTopBar_() { dev().assert(this.container_); this.topBar_ = this.win.document.createElement('div'); - this.topBar_.classList.add('i-amphtml-lbv-top-bar'); - this.topBar_.classList.add('i-amphtml-lbv-controls'); + this.topBar_.classList.add('i-amphtml-lbg-top-bar'); + this.topBar_.classList.add('i-amphtml-lbg-controls'); this.topGradient_ = this.win.document.createElement('div'); - this.topGradient_.classList.add('i-amphtml-lbv-top-bar-top-gradient'); + this.topGradient_.classList.add('i-amphtml-lbg-top-bar-top-gradient'); this.topBar_.appendChild(this.topGradient_); const close = this.close_.bind(this); @@ -458,9 +458,9 @@ export class AmpLightboxViewer extends AMP.BaseElement { const closeGallery = this.closeGallery_.bind(this); // TODO(aghassemi): i18n and customization. See https://git.io/v6JWu - this.buildButton_('Close', 'amp-lbv-button-close', close); - this.buildButton_('Gallery', 'amp-lbv-button-gallery', openGallery); - this.buildButton_('Content', 'amp-lbv-button-slide', closeGallery); + this.buildButton_('Close', 'amp-lbg-button-close', close); + this.buildButton_('Gallery', 'amp-lbg-button-gallery', openGallery); + this.buildButton_('Content', 'amp-lbg-button-slide', closeGallery); this.container_.appendChild(this.topBar_); } @@ -579,7 +579,7 @@ export class AmpLightboxViewer extends AMP.BaseElement { } /** - * Closes the lightbox viewer on a tiny upwards swipe. + * Closes the lightbox gallery on a tiny upwards swipe. * @param {number} deltaY * @private */ @@ -590,14 +590,14 @@ export class AmpLightboxViewer extends AMP.BaseElement { } /** - * Opens the lightbox-viewer with either the invocation source or + * Opens the lightbox-gallery with either the invocation source or * the element referenced by the `id` argument. * Examples: * // Opens the element tapped. - * on="tap:myLightboxViewer' + * on="tap:myLightboxGallery' * * // Opens the element referenced by elementId - * on="tap:myLightboxViewer.open(id='') + * on="tap:myLightboxGallery.open(id='') * @override * @param {!../../../src/service/action-impl.ActionInvocation} invocation */ @@ -607,13 +607,13 @@ export class AmpLightboxViewer extends AMP.BaseElement { const targetId = invocation.args['id']; target = this.getAmpDoc().getElementById(targetId); user().assert(target, - 'amp-lightbox-viewer.open: element with id: %s not found', targetId); + 'amp-lightbox-gallery.open: element with id: %s not found', targetId); } this.open_(dev().assertElement(target)); } /** - * Opens the lightbox-viewer and displays the given element inside. + * Opens the lightbox-gallery and displays the given element inside. * @param {!Element} element Element to lightbox. * @return {!Promise} * @private @@ -722,7 +722,7 @@ export class AmpLightboxViewer extends AMP.BaseElement { // TODO (#13039): implement crop and object fit contain transitions transLayer = this.element.ownerDocument.createElement('div'); - transLayer.classList.add('i-amphtml-lightbox-viewer-trans'); + transLayer.classList.add('i-amphtml-lightbox-gallery-trans'); this.element.ownerDocument.body.appendChild(transLayer); const rect = layoutRectFromDomRect(sourceElement ./*OK*/getBoundingClientRect()); @@ -924,7 +924,7 @@ export class AmpLightboxViewer extends AMP.BaseElement { } /** - * Closes the lightbox-viewer + * Closes the lightbox-gallery * @return {!Promise} * @private */ @@ -944,7 +944,7 @@ export class AmpLightboxViewer extends AMP.BaseElement { this.container_.removeAttribute('gallery-view'); if (this.gallery_) { - this.gallery_.classList.add('i-amphtml-lbv-gallery-hidden'); + this.gallery_.classList.add('i-amphtml-lbg-gallery-hidden'); this.gallery_ = null; } }); @@ -1014,15 +1014,15 @@ export class AmpLightboxViewer extends AMP.BaseElement { findOrBuildGallery_() { this.gallery_ = scopedQuerySelector( this.element, - '.i-amphtml-lbv-gallery[amp-lightbox-group=' + '.i-amphtml-lbg-gallery[amp-lightbox-group=' + this.currentLightboxGroupId_ + ']' ); if (this.gallery_) { - this.gallery_.classList.remove('i-amphtml-lbv-gallery-hidden'); + this.gallery_.classList.remove('i-amphtml-lbg-gallery-hidden'); } else { // Build gallery this.gallery_ = this.win.document.createElement('div'); - this.gallery_.classList.add('i-amphtml-lbv-gallery'); + this.gallery_.classList.add('i-amphtml-lbg-gallery'); this.gallery_.setAttribute('amp-lightbox-group', this.currentLightboxGroupId_); @@ -1062,9 +1062,9 @@ export class AmpLightboxViewer extends AMP.BaseElement { */ createThumbnailElement_(thumbnailObj) { const element = this.win.document.createElement('div'); - element.classList.add('i-amphtml-lbv-gallery-thumbnail'); + element.classList.add('i-amphtml-lbg-gallery-thumbnail'); const imgElement = this.win.document.createElement('img'); - imgElement.classList.add('i-amphtml-lbv-gallery-thumbnail-img'); + imgElement.classList.add('i-amphtml-lbg-gallery-thumbnail-img'); imgElement.setAttribute('src', thumbnailObj.url); element.appendChild(imgElement); const closeGalleryAndShowTargetSlide = event => { @@ -1096,30 +1096,30 @@ export function installLightboxManager(win) { } /** - * Tries to find an existing amp-lightbox-viewer, if there is none, it adds a + * Tries to find an existing amp-lightbox-gallery, if there is none, it adds a * default one. * @param {!Window} win * @return {!Promise} */ -function installLightboxViewer(win) { +function installLightboxGallery(win) { const ampdoc = Services.ampdocServiceFor(win).getAmpDoc(); // TODO (#12859): make this work for more than singleDoc mode return ampdoc.whenBodyAvailable().then(body => { - const existingViewer = elementByTag(ampdoc.getRootNode(), TAG); - if (!existingViewer) { + const existingGallery = elementByTag(ampdoc.getRootNode(), TAG); + if (!existingGallery) { const matches = ampdoc.getRootNode().querySelectorAll('[lightbox]'); if (matches.length > 0) { - const viewer = ampdoc.getRootNode().createElement(TAG); - viewer.setAttribute('layout', 'nodisplay'); - viewer.setAttribute('id', DEFAULT_VIEWER_ID); - body.appendChild(viewer); + const gallery = ampdoc.getRootNode().createElement(TAG); + gallery.setAttribute('layout', 'nodisplay'); + gallery.setAttribute('id', DEFAULT_GALLERY_ID); + body.appendChild(gallery); } } }); } AMP.extension(TAG, '0.1', AMP => { - AMP.registerElement(TAG, AmpLightboxViewer, CSS); + AMP.registerElement(TAG, AmpLightboxGallery, CSS); installLightboxManager(AMP.win); - installLightboxViewer(AMP.win); + installLightboxGallery(AMP.win); }); diff --git a/extensions/amp-lightbox-viewer/0.1/service/lightbox-manager-impl.js b/extensions/amp-lightbox-gallery/0.1/service/lightbox-manager-impl.js similarity index 97% rename from extensions/amp-lightbox-viewer/0.1/service/lightbox-manager-impl.js rename to extensions/amp-lightbox-gallery/0.1/service/lightbox-manager-impl.js index 04d8b4d5d9e8..5d4ae1977f48 100644 --- a/extensions/amp-lightbox-viewer/0.1/service/lightbox-manager-impl.js +++ b/extensions/amp-lightbox-gallery/0.1/service/lightbox-manager-impl.js @@ -34,7 +34,7 @@ const ELIGIBLE_TAP_TAGS = { 'amp-img': true, }; -const VIEWER_TAG = 'amp-lightbox-viewer'; +const GALLERY_TAG = 'amp-lightbox-gallery'; const CAROUSEL_TAG = 'amp-carousel'; const FIGURE_TAG = 'figure'; const SLIDE_SELECTOR = '.amp-carousel-slide'; @@ -72,7 +72,7 @@ export class LightboxManager { constructor(ampdoc) { // Extra safety check, we don't install this service if experiment is off - dev().assert(isExperimentOn(ampdoc.win, 'amp-lightbox-viewer')); + dev().assert(isExperimentOn(ampdoc.win, 'amp-lightbox-gallery')); /** @const @private {!../../../../src/service/ampdoc-impl.AmpDoc} */ this.ampdoc_ = ampdoc; @@ -272,8 +272,8 @@ export class LightboxManager { this.lightboxGroups_[lightboxGroupId] .push(dev().assertElement(element)); if (this.meetsHeuristicsForTap_(element)) { - const viewer = elementByTag(this.ampdoc_.getRootNode(), VIEWER_TAG); - element.setAttribute('on', `tap:${viewer.id}.activate`); + const gallery = elementByTag(this.ampdoc_.getRootNode(), GALLERY_TAG); + element.setAttribute('on', `tap:${gallery.id}.activate`); } } diff --git a/extensions/amp-lightbox-viewer/0.1/test/test-amp-lightbox-viewer.js b/extensions/amp-lightbox-gallery/0.1/test/test-amp-lightbox-gallery.js similarity index 94% rename from extensions/amp-lightbox-viewer/0.1/test/test-amp-lightbox-viewer.js rename to extensions/amp-lightbox-gallery/0.1/test/test-amp-lightbox-gallery.js index 03f3ed9a7946..c8df9f8978b6 100644 --- a/extensions/amp-lightbox-viewer/0.1/test/test-amp-lightbox-viewer.js +++ b/extensions/amp-lightbox-gallery/0.1/test/test-amp-lightbox-gallery.js @@ -15,14 +15,14 @@ */ import '../../../amp-carousel/0.1/amp-carousel'; -import {installLightboxManager} from '../amp-lightbox-viewer'; +import {installLightboxManager} from '../amp-lightbox-gallery'; import {toggleExperiment} from '../../../../src/experiments'; -describes.realWin('amp-lightbox-viewer', { +describes.realWin('amp-lightbox-gallery', { amp: { amp: true, - extensions: ['amp-lightbox-viewer', 'amp-carousel'], + extensions: ['amp-lightbox-gallery', 'amp-carousel'], }, }, env => { let win, doc; @@ -37,14 +37,9 @@ describes.realWin('amp-lightbox-viewer', { }); function getAmpLightboxViewer(autoLightbox) { - toggleExperiment(win, 'amp-lightbox-viewer', true); - if (autoLightbox) { - toggleExperiment(win, 'amp-lightbox-viewer-auto', true); - } else { - toggleExperiment(win, 'amp-lightbox-viewer-auto', false); - } + toggleExperiment(win, 'amp-lightbox-gallery', true); setUpDocument(doc, autoLightbox); - const viewer = doc.createElement('amp-lightbox-viewer'); + const viewer = doc.createElement('amp-lightbox-gallery'); viewer.setAttribute('layout', 'nodisplay'); installLightboxManager(win); doc.body.appendChild(viewer); diff --git a/extensions/amp-lightbox-viewer/OWNERS.yaml b/extensions/amp-lightbox-gallery/OWNERS.yaml similarity index 100% rename from extensions/amp-lightbox-viewer/OWNERS.yaml rename to extensions/amp-lightbox-gallery/OWNERS.yaml diff --git a/gulpfile.js b/gulpfile.js index 8b41cef4db3b..d65716e055b9 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -115,7 +115,7 @@ declareExtension('amp-install-serviceworker', '0.1', {hasCss: false}); declareExtension('amp-izlesene', '0.1', {hasCss: false}); declareExtension('amp-jwplayer', '0.1', {hasCss: false}); declareExtension('amp-lightbox', '0.1', {hasCss: true}); -declareExtension('amp-lightbox-viewer', '0.1', {hasCss: true}); +declareExtension('amp-lightbox-gallery', '0.1', {hasCss: true}); declareExtension('amp-list', '0.1', {hasCss: false}); declareExtension('amp-live-list', '0.1', {hasCss: true}); declareExtension('amp-mathml', '0.1', {hasCss: true}); diff --git a/src/runtime.js b/src/runtime.js index 7c19f0753ba9..89ca4554cc3c 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -344,7 +344,7 @@ function adoptShared(global, callback) { * @return {!Promise} */ export function adopt(global) { - return adoptShared(global, (global, extensions) => { + return adoptShared(global, global => { const ampdocService = Services.ampdocServiceFor(global); const ampdoc = ampdocService.getAmpDoc(); global.AMP.ampdoc = ampdoc; @@ -367,7 +367,6 @@ export function adopt(global) { return waitForBodyPromise(global.document).then(() => { // Ensure that all declared extensions are marked and stubbed. stubElementsForDoc(ampdoc); - installAutoLoadExtensions(extensions, ampdoc); }); }); } @@ -411,21 +410,6 @@ export function adoptShadowMode(global) { }); } - -/** - * Certain extensions can be auto-loaded by runtime based on experiments or - * other configurations. - * @param {!./service/extensions-impl.Extensions} extensions - * @param {!./service/ampdoc-impl.AmpDoc} ampdoc - */ -function installAutoLoadExtensions(extensions, ampdoc) { - if (!getMode().test && - isExperimentOn(ampdoc.win, 'amp-lightbox-viewer-auto')) { - extensions.installExtensionForDoc(ampdoc, 'amp-lightbox-viewer'); - } -} - - /** * A manager for documents in the multi-doc environment. */ @@ -486,8 +470,6 @@ class MultidocManager { /* opt_isRuntimeCss */ true); // Instal doc services. installAmpdocServices(ampdoc, initParams || Object.create(null)); - // Install auto-load extensions. - installAutoLoadExtensions(this.extensions_, ampdoc); const viewer = Services.viewerForDoc(ampdoc); diff --git a/test/manual/amp-lightbox-carousel-demo.amp.html b/test/manual/amp-lightbox-carousel-demo.amp.html index bf97aa70ac9f..08c2fe7325e2 100644 --- a/test/manual/amp-lightbox-carousel-demo.amp.html +++ b/test/manual/amp-lightbox-carousel-demo.amp.html @@ -6,7 +6,7 @@ - + @@ -33,15 +33,10 @@ // Enable amp-lightbox-viewer experiment automatically if it is not enabled. // Disable amp-lightbox-viewer-auto experiment if it is enabled. setTimeout(function() { - if (!AMP.isExperimentOn('amp-lightbox-viewer') || AMP.isExperimentOn('amp-lightbox-viewer-auto')) { - if (!AMP.isExperimentOn('amp-lightbox-viewer')) { - AMP.toggleExperiment('amp-lightbox-viewer'); + if (!AMP.isExperimentOn('amp-lightbox-gallery')) { + AMP.toggleExperiment('amp-lightbox-gallery'); + location.reload(); } - if (AMP.isExperimentOn('amp-lightbox-viewer-auto')) { - AMP.toggleExperiment('amp-lightbox-viewer-auto'); - } - location.reload(); - } }, 1000); diff --git a/test/manual/amp-lightbox-viewer-demo.amp.html b/test/manual/amp-lightbox-viewer-demo.amp.html deleted file mode 100644 index a71e6e31e655..000000000000 --- a/test/manual/amp-lightbox-viewer-demo.amp.html +++ /dev/null @@ -1,150 +0,0 @@ - - - - - Lightbox Viewer (Opt-in) - - - - - - - - - - - - - - - - - - -

Lightbox Viewer Demo

- -

Image - Landscape size - short description

- - -

Image - Landscape size - normal length description

- - -

Image - Landscape size - long description - Lightboxable no Tap

- - -

Image - Not Lightboxable

- - -

Image - Portrait Size - short description

- - -

Image - Portrait Size - normal length description

- - -

Image - Portrait Size - long description

- - -

Image - Size with Large Height

- - -

Image - Size with Large Width

- - -

Image - Fixed Size

- - -

Youtube - Lightboxable

- - - -

Youtube - Not Lightboxable

- - -

Twitter

- - - -

UL/LI

-
    -
  • Apple
  • -
  • Beer
  • -
  • Milk
  • -
  • Cheese
  • -
- -

Blockquote

-
- Some say that his tears are adhesive, and that if he caught fire, he'd burn for a thousand days... all we know is, he's called the Stig -
- -

Image inside a different stacking context

-
- -
- - diff --git a/test/manual/amp-lightbox-viewer-mixed.amp.html b/test/manual/amp-lightbox-viewer-mixed.amp.html deleted file mode 100644 index 48d5c5bda59c..000000000000 --- a/test/manual/amp-lightbox-viewer-mixed.amp.html +++ /dev/null @@ -1,65 +0,0 @@ - - - - - Lightbox Viewer (Opt-in) - - - - - - - - - - - -

Lightbox Viewer (Mixed Opt-out and Opt-in)

- - - -

Image

- - -

Image - Not Lightboxable

- - -

Blockquote with tap - Manually opted in

-
- Some say that his tears are adhesive, and that if he caught fire, he'd burn for a thousand days... all we know is, he's called the Stig -
- - - diff --git a/test/manual/amp-lightbox-viewer-optin.amp.html b/test/manual/amp-lightbox-viewer-optin.amp.html deleted file mode 100644 index e0ef350daa88..000000000000 --- a/test/manual/amp-lightbox-viewer-optin.amp.html +++ /dev/null @@ -1,156 +0,0 @@ - - - - - Lightbox Viewer (Opt-in) - - - - - - - - - - - - - - - - - - -

Lightbox Viewer (Opt-in)

- -

Image - Landscape size - short description

- - -

Image - Landscape size - normal length description

- - -

Image - Landscape size - long description - Lightboxable no Tap

- - -

Image - Not Lightboxable

- - -

Image - Portrait Size - short description

- - -

Image - Portrait Size - normal length description

- - -

Image - Portrait Size - long description

- - -

Image - Size with Large Height

- - -

Image - Size with Large Width

- - -

Image - Fixed Size

- - -

Youtube - Lightboxable

- - - -

Youtube - Not Lightboxable

- - -

Twitter

- - - -

UL/LI

-
    -
  • Apple
  • -
  • Beer
  • -
  • Milk
  • -
  • Cheese
  • -
- -

Blockquote

-
- Some say that his tears are adhesive, and that if he caught fire, he'd burn for a thousand days... all we know is, he's called the Stig -
- -

Image inside a different stacking context

-
- -
- - diff --git a/test/manual/amp-lightbox-viewer-optout.amp.html b/test/manual/amp-lightbox-viewer-optout.amp.html deleted file mode 100644 index 30ea257ce946..000000000000 --- a/test/manual/amp-lightbox-viewer-optout.amp.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - Lightbox Viewer (Opt-in) - - - - - - - - - - -

Lightbox Viewer (Opt-out)

- -

Image

- - -

Image - Not Lightboxable

- - -

Image Fixed Size

- - -

Small Image Fixed Size

- - -

Youtube

- - -

Youtube - Not Lightboxable

- - -

Twitter

- - -

Twitter - Not Lightboxable

- - - - diff --git a/test/manual/article-with-lightbox-2.0-gallery.amp.html b/test/manual/article-with-lightbox-2.0-gallery.amp.html index 34d4090c44a2..1913739f98ce 100644 --- a/test/manual/article-with-lightbox-2.0-gallery.amp.html +++ b/test/manual/article-with-lightbox-2.0-gallery.amp.html @@ -243,7 +243,7 @@ } - + + diff --git a/examples/amp-story/bookend.json b/examples/amp-story/bookend.json index 711e8d87208b..a182b743cf02 100644 --- a/examples/amp-story/bookend.json +++ b/examples/amp-story/bookend.json @@ -7,7 +7,7 @@ { "title": "This is an example article", "url": "http://example.com/article.html", - "image": "http://placehold.it/128x128" + "image": "http://placehold.it/256x128" } ] } diff --git a/extensions/amp-story/0.1/amp-story.css b/extensions/amp-story/0.1/amp-story.css index bec490cecdaa..543d4f12e1e1 100644 --- a/extensions/amp-story/0.1/amp-story.css +++ b/extensions/amp-story/0.1/amp-story.css @@ -439,6 +439,10 @@ amp-story-grid-layer * { display: none !important; } +.i-amphtml-story-bookend-article-image > img { + object-fit: cover; +} + .i-amphtml-story-bookend-replay-image { background-position: center center !important; background-size: cover !important; From b7844392493cf9cad336ade24379be3ddc0643ab Mon Sep 17 00:00:00 2001 From: Jon Newmuis Date: Mon, 5 Feb 2018 14:41:01 -0500 Subject: [PATCH 10/65] amp-story video playback improvements (#13245) * Return play promise from playFn to ensure media playback is not interrupted. * Refactor playFn to always return a promise. Improve documentation. * Use closure so the proper context is set on the distance function. * Add debug messages * Remove muted property change, since this is in a separate PR * Fix merge --- extensions/amp-story/0.1/media-pool.js | 27 ++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/extensions/amp-story/0.1/media-pool.js b/extensions/amp-story/0.1/media-pool.js index 808758895d64..845bbe3bba3c 100644 --- a/extensions/amp-story/0.1/media-pool.js +++ b/extensions/amp-story/0.1/media-pool.js @@ -596,15 +596,26 @@ export class MediaPool { const isMuted = mediaEl.muted; const currentTime = mediaEl.currentTime; - // If the video is already playing, we do not want to call play again, as it - // can interrupt the video playback. Instead, we do a no-op. + /** + * @return {!Promise} A promise that is resolved when playback has been + * initiated or rejected if playback fails to initiate. If the media + * element is already playing, the promise is immediately resolved + * without playing the media element again, to avoid interrupting + * playback. + */ const playFn = () => { if (isPaused) { - mediaEl.play(); + // The playFn() invocation is wrapped in a Promise.resolve(...) due to + // the fact that some browsers return a promise from media elements' + // play() function, while others return a boolean. + return Promise.resolve(mediaEl.play()); } + + // This media element was already playing. + return Promise.resolve(); }; - return Promise.resolve().then(() => playFn()).then(() => { + return playFn().then(() => { mediaEl.muted = false; if (isPaused) { @@ -617,7 +628,7 @@ export class MediaPool { } }).catch(reason => { dev().expectedError('AMP-STORY', 'Blessing media element failed:', - reason); + reason, mediaEl); }); } @@ -802,7 +813,7 @@ export class MediaPool { instances[newId] = new MediaPool( toWin(root.getElement().ownerDocument.defaultView), root.getMaxMediaElementCounts(), - root.getElementDistance); + element => root.getElementDistance(element)); return instances[newId]; } @@ -826,8 +837,8 @@ class Sources { /** * Applies the src attribute and source tags to a specified element. - * @param {!Element} element The element to adopt the sources represented by - * this object. + * @param {!HTMLMediaElement} element The element to adopt the sources + * represented by this object. */ applyToElement(element) { Sources.removeFrom(element); From 0aad6b486dba428bd3caf9fac19f00bb760ba519 Mon Sep 17 00:00:00 2001 From: Caleb Cordry Date: Mon, 5 Feb 2018 13:25:59 -0800 Subject: [PATCH 11/65] Story-Auto-Ads Skeleton (#13239) * Component skeleton * move files into amp-story * clean up example * remove bad link * bad mock test * clean up --- examples/amp-story-auto-ads.amp.html | 145 ++++++++++++++++++ .../amp-story/0.1/amp-story-auto-ads.js | 70 +++++++++ .../0.1/test/test-amp-story-auto-ads.js | 36 +++++ extensions/amp-story/amp-story-auto-ads.md | 54 +++++++ 4 files changed, 305 insertions(+) create mode 100644 examples/amp-story-auto-ads.amp.html create mode 100644 extensions/amp-story/0.1/amp-story-auto-ads.js create mode 100644 extensions/amp-story/0.1/test/test-amp-story-auto-ads.js create mode 100644 extensions/amp-story/amp-story-auto-ads.md diff --git a/examples/amp-story-auto-ads.amp.html b/examples/amp-story-auto-ads.amp.html new file mode 100644 index 000000000000..dbcd5fee5809 --- /dev/null +++ b/examples/amp-story-auto-ads.amp.html @@ -0,0 +1,145 @@ + + + + + amp-story + + + + + + + + + + + + + +

fade-in

+
+
+
+
+ + + +

twirl-in

+
+
+
+
+ + + +

fly-in-left

+
+
+
+
+ + + +

fly-in-right

+
+
+
+
+ + + +

fly-in-top

+
+
+
+
+ + + +

fly-in-bottom

+
+
+
+
+ + + +

rotate-in-left

+
+
+
+
+ + + +

rotate-in-right

+
+
+
+
+ + + +

drop-in

+
+
+
+
+ + + +

whoosh-in-left

+
+
+
+
+ + + +

whoosh-in-right

+
+
+
+
+
+ + diff --git a/extensions/amp-story/0.1/amp-story-auto-ads.js b/extensions/amp-story/0.1/amp-story-auto-ads.js new file mode 100644 index 000000000000..4a988fc41a46 --- /dev/null +++ b/extensions/amp-story/0.1/amp-story-auto-ads.js @@ -0,0 +1,70 @@ +/** + * Copyright 2018 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 {user} from '../../../src/log'; + + +/** @const */ +// const EXPERIMENT = 'amp-story-auto-ad'; + +/** @const */ +const TAG = 'amp-story-auto-ads'; + +export class AmpStoryAutoAds extends AMP.BaseElement { + + /** @param {!AmpElement} element */ + constructor(element) { + super(element); + + /** @private {?AmpStory} */ + this.ampStory_ = null; + } + + /** @override */ + buildCallback() { + const ampStory = this.element.parentElement; + user().assert(ampStory.tagName === 'AMP-STORY', + `<${TAG}> should be child of `); + this.ampStory_ = ampStory; + } + + /** @override */ + isLayoutSupported() { + return true; + } + + /** @override */ + layoutCallback() { + this.ampStory.appendChild(this.makeMockPage()); + } + + // temporary to be replaced with real page later + makeMockPage() { + const ampStoryAdPage = document.createElement('amp-story-page'); + ampStoryAdPage.id = 'i-amphtml-ad-page-1'; + ampStoryAdPage./*OK*/innerHTML = ` + +

First Ad Page

+

This is the first ad shown in this story.

+
+ `; + return ampStoryAdPage; + } +} + +AMP.registerElement('amp-story-auto-ads', AmpStoryAutoAds); + + diff --git a/extensions/amp-story/0.1/test/test-amp-story-auto-ads.js b/extensions/amp-story/0.1/test/test-amp-story-auto-ads.js new file mode 100644 index 000000000000..961483b8ff22 --- /dev/null +++ b/extensions/amp-story/0.1/test/test-amp-story-auto-ads.js @@ -0,0 +1,36 @@ +/** + * Copyright 2018 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 {AmpStoryAutoAds} from '../amp-story-auto-ads'; + +describes.realWin('amp-story-auto-ads', { + amp: { + extensions: ['amp-story-auto-ads'], + }, +}, env => { + + let win; + let element; + + beforeEach(() => { + win = env.win; + element = win.document.createElement('amp-story-auto-ads'); + win.document.body.appendChild(element); + }); + + it('should build', () => { + }); +}); diff --git a/extensions/amp-story/amp-story-auto-ads.md b/extensions/amp-story/amp-story-auto-ads.md new file mode 100644 index 000000000000..86a7125fff50 --- /dev/null +++ b/extensions/amp-story/amp-story-auto-ads.md @@ -0,0 +1,54 @@ + + +# `amp-story-auto-ads` + + +## THIS IS A WORK-IN-PROGRESS + + + + + + + + + + + + + + + + + + + + + + +
DescriptionFILL THIS IN
AvailabilityFILL THIS IN
Required Script<script async custom-element="amp-form" src="https://cdn.ampproject.org/v0/amp-story-auto-ads-0.1.js"></script>
Supported LayoutsFILL THIS IN
ExamplesFILL THIS IN
+ +## Behavior + +FILL THIS IN. What does this extension do? + +## Attributes + +FILL THIS IN. Does this extension allow for properties to configure? + +## Validation + From 68aefae7cf1b908e8b2118cfc1a5d198e559c3a7 Mon Sep 17 00:00:00 2001 From: Raghu Simha Date: Mon, 5 Feb 2018 16:53:57 -0500 Subject: [PATCH 12/65] Indicate progress during gulp lint (#13274) --- build-system/tasks/lint.js | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/build-system/tasks/lint.js b/build-system/tasks/lint.js index b4fc78676899..1ca7c31a2eee 100644 --- a/build-system/tasks/lint.js +++ b/build-system/tasks/lint.js @@ -58,6 +58,17 @@ function initializeStream(globs, streamOptions) { return stream; } +/** + * Logs a message on the same line to indicate progress + * @param {string} message + */ +function logOnSameLine(message) { + process.stdout.moveCursor(0, -1); + process.stdout.cursorTo(0); + process.stdout.clearLine(); + log(message); +} + /** * Runs the linter on the given stream using the given options. * @param {string} path @@ -67,12 +78,31 @@ function initializeStream(globs, streamOptions) { */ function runLinter(path, stream, options) { let errorsFound = false; + if (!process.env.TRAVIS) { + log(colors.green('Starting linter...')); + } return stream.pipe(eslint(options)) .pipe(eslint.formatEach('stylish', function(msg) { errorsFound = true; - log(msg); + logOnSameLine(colors.red('Linter error:') + msg + '\n'); })) .pipe(gulpIf(isFixed, gulp.dest(path))) + .pipe(eslint.result(function(result) { + if (!process.env.TRAVIS) { + logOnSameLine(colors.green('Linting: ') + result.filePath); + } + })) + .pipe(eslint.results(function(results) { + if (results.errorCount == 0) { + if (!process.env.TRAVIS) { + logOnSameLine(colors.green('Success: ') + 'No linter errors'); + } + } else { + logOnSameLine(colors.red('Error: ') + results.errorCount + + ' linter error(s) found.'); + process.exit(1); + } + })) .pipe(eslint.failAfterError()) .on('error', function() { if (errorsFound && !options.fix) { From c20c36e123cceab1797f07c7a0c2c5d6e578c072 Mon Sep 17 00:00:00 2001 From: Yuxuan Zhou Date: Mon, 5 Feb 2018 13:58:45 -0800 Subject: [PATCH 13/65] Support `--extensions=minimal_set` & bug fix (#13249) * fix dist with alias * change minimal_set usage * fix documentation * refactor print logic * rename * update function description --- contributing/DEVELOPING.md | 6 +++ gulpfile.js | 108 ++++++++++++++++++++++++------------- 2 files changed, 78 insertions(+), 36 deletions(-) diff --git a/contributing/DEVELOPING.md b/contributing/DEVELOPING.md index 148604d02ec7..01b52247c987 100644 --- a/contributing/DEVELOPING.md +++ b/contributing/DEVELOPING.md @@ -36,8 +36,12 @@ The Quick Start Guide's [One-time setup](getting-started-quick.md#one-time-setu | ----------------------------------------------------------------------- | --------------------------------------------------------------------- | | **`gulp`**[[1]](#footnote-1) | Runs "watch" and "serve". Use this for standard local dev. | | `gulp --extensions=` | Runs "watch" and "serve", after building only the listed extensions. +| `gulp --extensions=minimal_set` | Runs "watch" and "serve", after building the extensions needed to load `article.amp.html`. | `gulp --noextensions` | Runs "watch" and "serve" without building any extensions. | `gulp dist`[[1]](#footnote-1) | Builds production binaries. | +| `gulp dist --extensions=` | Builds production binaries, with only the listed extensions. +| `gulp dist --extensions=minimal_set` | Builds production binaries, with only the extensions needed to load `article.amp.html`. +| `gulp dist --noextensions` | Builds production binaries without building any extensions. | `gulp dist --fortesting`[[1]](#footnote-1) | Builds production binaries for local testing. (Allows use cases like ads, tweets, etc. to work with minified sources. Overrides `TESTING_HOST` if specified. Uses the production `AMP_CONFIG` by default.) | | `gulp dist --fortesting --config=`[[1]](#footnote-1) | Builds production binaries for local testing, with the specified `AMP_CONFIG`. `config` can be `prod` or `canary`. (Defaults to `prod`.) | | `gulp lint` | Validates against Google Closure Linter. | @@ -45,12 +49,14 @@ The Quick Start Guide's [One-time setup](getting-started-quick.md#one-time-setu | `gulp lint --fix` | Fixes simple lint warnings/errors automatically. | | `gulp build`[[1]](#footnote-1) | Builds the AMP library. | | `gulp build --extensions=` | Builds the AMP library, with only the listed extensions. +| `gulp build --extensions=minimal_set` | Builds the AMP library, with only the extensions needed to load `article.amp.html`. | `gulp build --noextensions` | Builds the AMP library with no extensions. | `gulp check-links --files foo.md,bar.md` | Reports dead links in `.md` files. | | `gulp clean` | Removes build output. | | `gulp css`[[1]](#footnote-1) | Recompiles css to build directory and builds the embedded css into js files for the AMP library. | | `gulp watch`[[1]](#footnote-1) | Watches for changes in files, re-builds. | | `gulp watch --extensions=` | Watches for changes in files, re-builds only the listed extensions. +| `gulp watch --extensions=minimal_set` | Watches for changes in files, re-builds only the extensions needed to load `article.amp.html`. | `gulp watch --noextensions` | Watches for changes in files, re-builds with no extensions. | `gulp pr-check`[[1]](#footnote-1) | Runs all the Travis CI checks locally. | | `gulp pr-check --nobuild`[[1]](#footnote-1) | Runs all the Travis CI checks locally, but skips the `gulp build` step. | diff --git a/gulpfile.js b/gulpfile.js index d65716e055b9..7247fc26f2b9 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -203,11 +203,15 @@ function declareExtension(name, version, options) { * @param {boolean} hasCss */ function declareExtensionVersionAlias(name, version, lastestVersion, hasCss) { - extensionAliasFilePath[name + '-' + version + '.js'] = - name + '-' + lastestVersion + '.js'; + extensionAliasFilePath[name + '-' + version + '.js'] = { + 'name': name, + 'file': name + '-' + lastestVersion + '.js', + }; if (hasCss) { - extensionAliasFilePath[name + '-' + version + '.css'] = - name + '-' + lastestVersion + '.css'; + extensionAliasFilePath[name + '-' + version + '.css'] = { + 'name': name, + 'file': name + '-' + lastestVersion + '.css', + }; } } @@ -246,7 +250,7 @@ function buildExtensions(options) { } var extensionsToBuild = []; - if (!!argv.extensions && argv.extensions !== true) { + if (!!argv.extensions) { extensionsToBuild = argv.extensions.split(','); } @@ -602,6 +606,53 @@ function printConfigHelp(command) { } } +/** + * Parse the --extensions or the --noextensions flag and + * prints a helpful message that lets the developer know how to build + * a list of extensions or without any extensions. + */ +function parseExtensionFlags() { + if (!process.env.TRAVIS) { + if (argv.extensions) { + if (typeof(argv.extensions) !== 'string') { + log(red('ERROR:'), 'Missing list of extensions. Expected format:', + cyan('--extensions=amp-foo,amp-bar'), + 'to choose which ones to build, or', + cyan('--extensions=minimal_set'), + 'to build the ones needed to load', + cyan('article.amp.html') + '.'); + process.exit(1); + } + if (argv.extensions === 'minimal_set') { + argv.extensions = + 'amp-ad,amp-ad-network-adsense-impl,amp-audio,amp-video,' + + 'amp-image-lightbox,amp-lightbox,amp-sidebar,' + + 'amp-analytics,amp-app-banner'; + } + log(green('Building extension(s):'), + cyan(argv.extensions.split(',').join(', '))); + log(green('⤷ Use'), cyan('--noextensions'), + green('to skip building extensions.')); + } else if (argv.noextensions) { + log(green('Not building any AMP extensions.')); + log(green('⤷ Use'), cyan('--extensions=amp-foo,amp-bar'), + green('to choose which ones to build, or'), + cyan('--extensions=minimal_set'), + green('to build the ones needed to load'), + cyan('article.amp.html') + green('.')); + } else { + log(green('Building all AMP extensions.')); + log(green('⤷ Use'), cyan('--noextensions'), + green('to skip building extensions, or'), + cyan('--extensions=amp-foo,amp-bar'), + green('to choose which ones to build, or'), + cyan('--extensions=minimal_set'), + green('to build the ones needed to load'), + cyan('article.amp.html') + green('.')); + } + } +} + /** * Enables runtime to be used for local testing by writing AMP_CONFIG to file. * Called at the end of "gulp build" and "gulp dist --fortesting". @@ -626,29 +677,7 @@ function enableLocalTesting(targetFile) { function performBuild(watch) { process.env.NODE_ENV = 'development'; printConfigHelp(watch ? 'gulp watch' : 'gulp build'); - if (!process.env.TRAVIS) { - if (argv.extensions) { - if (typeof(argv.extensions) !== 'string') { - log(red('ERROR:'), 'Missing list of extensions. Expected format:', - cyan('--extensions=amp-foo,amp-bar')); - process.exit(1); - } - log(green('Building extension(s):'), - cyan(argv.extensions.split(',').join(', '))); - log(green('⤷ Use'), cyan('--noextensions'), - green('to skip building extensions.')); - } else if (argv.noextensions) { - log(green('Not building any AMP extensions.')); - log(green('⤷ Use'), cyan('--extensions=amp-foo,amp-bar'), - green('to choose which ones to build.')); - } else { - log(green('Building all AMP extensions.')); - log(green('⤷ Use'), cyan('--noextensions'), - green('to skip building extensions, or'), - cyan('--extensions=amp-foo,amp-bar'), - green('to choose which ones to build.')); - } - } + parseExtensionFlags(); return compileCss(watch).then(() => { return Promise.all([ polyfillsForTests(), @@ -691,6 +720,7 @@ function dist() { if (argv.fortesting) { printConfigHelp('gulp dist --fortesting') } + parseExtensionFlags(); return compileCss().then(() => { return Promise.all([ compile(false, true, true), @@ -724,8 +754,21 @@ function dist() { * Copy built extension to alias extension */ function copyAliasExtensions() { + if (argv.noextensions) { + return; + } + var extensionsToBuild = []; + if (!!argv.extensions) { + extensionsToBuild = argv.extensions.split(','); + } + for (var key in extensionAliasFilePath) { - fs.copySync('dist/v0/' + extensionAliasFilePath[key], 'dist/v0/' + key); + if (extensionsToBuild.length > 0 && + extensionsToBuild.indexOf(extensionAliasFilePath[key]['name']) == -1) { + continue; + } + fs.copySync('dist/v0/' + extensionAliasFilePath[key]['file'], + 'dist/v0/' + key); } } @@ -891,12 +934,6 @@ function concatFilesToString(files) { function compileJs(srcDir, srcFilename, destDir, options) { options = options || {}; if (options.minify) { - if (argv.minimal_set - && !(/integration|babel|amp-ad|lightbox|sidebar|analytics|app-banner/ - .test(srcFilename))) { - log('Skipping', cyan(srcFilename), 'because of --minimal_set'); - return Promise.resolve(); - } const startTime = Date.now(); return closureCompile( srcDir + srcFilename, destDir, options.minifiedName, options) @@ -1392,7 +1429,6 @@ gulp.task('dist', 'Build production binaries', 'Great for profiling and debugging production code.', fortesting: ' Compiles production binaries for local testing', config: ' Sets the runtime\'s AMP_CONFIG to one of "prod" or "canary"', - minimal_set: ' Only compile files needed to load article.amp.html', } }); gulp.task('watch', 'Watches for changes in files, re-builds when detected', From a8e4f9ea3058609a8d82654ec2d0c665c8024f1c Mon Sep 17 00:00:00 2001 From: Rudy Galfi Date: Mon, 5 Feb 2018 14:30:13 -0800 Subject: [PATCH 14/65] Adding amp-story-analytics (#13276) --- extensions/amp-story/amp-story-analytics.md | 68 +++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 extensions/amp-story/amp-story-analytics.md diff --git a/extensions/amp-story/amp-story-analytics.md b/extensions/amp-story/amp-story-analytics.md new file mode 100644 index 000000000000..aec684123d13 --- /dev/null +++ b/extensions/amp-story/amp-story-analytics.md @@ -0,0 +1,68 @@ + + +# AMP Story and Analytics + +## Story triggers + +`amp-story` issues events for changes of state. These events can be reported through the analytics configuration by using triggers. + +See [amp-analytics.md](../amp-analytics/amp-analytics.md) for details on *amp-analytics* configuration. + +### Visible trigger (`"on": "story-page-visible"`) + +The `story-page-visible` event is issued when a story page becomes visible. + +```javascript +"triggers": { + "storyPageVisible": { + "on": "story-page-visible", + "request": "event" + } +} +``` + +Because of the user experience of AMP story enables a user to traverse several "pages" without loading new HTML pages each time, one interesting consideration involving the `story-page-visible` event is how to record pageview events. One approach would be to count each `story-page-visible` event as a typical pageview (i.e. as if a user were visiting a new HTML page); another approach is to capture `story-page-visible` events specially as their own type of event. + +Using `amp-analytics` you can re-assign the `story-page-visible` event to behave like a pageview event, which is a common vendor-specified event type: + +```javascript +"triggers": { + "storyPageVisible": { + "on": "story-page-visible", + "request": "pageview" + } +} +``` + +Consult your vendor's documentation for more specific details on how to set this up. + + +## Story variables + +AMP story contributes the following URL substitutions: + +### STORY_PAGE_ID + +The unique ID for an AMP story page, as provided by the `id` attribute of the current `amp-story-page`. + +### STORY_PAGE_INDEX + +A zero-based index value for an AMP story page determined by its ordering within `amp-story`. + +### Additional Vars + +Description of additional variables can be found in the [analytics-vars.md](../amp-analytics/analytics-vars.md) file. From 515e8124c116463dcdbe7edd8b9f40a2deaf8846 Mon Sep 17 00:00:00 2001 From: Raghu Simha Date: Mon, 5 Feb 2018 17:47:46 -0500 Subject: [PATCH 15/65] Fail build on error, and print a useful message (#13255) --- gulpfile.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 7247fc26f2b9..7a1305558050 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -992,11 +992,10 @@ function compileJs(srcDir, srcFilename, destDir, options) { const startTime = Date.now(); return toPromise(bundler.bundle() .on('error', function(err) { - if (err instanceof SyntaxError) { - console.error(red('Syntax error: ' + err.message)); - } else { - console.error(red(err.message)); - } + // Drop the node_modules call stack, which begins with ' at'. + const message = err.stack.replace(/ at[^]*/, '').trim(); + console.error(red(message)); + process.exit(1); }) .pipe(lazybuild()) .pipe($$.rename(destFilename)) From 00032a76f8b8bfa4b915a0fc2da99f3dd44a2aa5 Mon Sep 17 00:00:00 2001 From: Yuxuan Zhou Date: Mon, 5 Feb 2018 15:39:25 -0800 Subject: [PATCH 16/65] return with user error (#13279) --- extensions/amp-analytics/0.1/amp-analytics.js | 1 + extensions/amp-analytics/0.1/test/test-amp-analytics.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/extensions/amp-analytics/0.1/amp-analytics.js b/extensions/amp-analytics/0.1/amp-analytics.js index f291d9d93f2d..c25bbcdcf17c 100644 --- a/extensions/amp-analytics/0.1/amp-analytics.js +++ b/extensions/amp-analytics/0.1/amp-analytics.js @@ -671,6 +671,7 @@ export class AmpAnalytics extends AMP.BaseElement { const TAG = this.getName_(); this.user().error(TAG, 'Ignoring event. Request string ' + 'not found: ', trigger['request']); + return; } this.checkTriggerEnabled_(trigger, event).then(enabled => { diff --git a/extensions/amp-analytics/0.1/test/test-amp-analytics.js b/extensions/amp-analytics/0.1/test/test-amp-analytics.js index 485a02a7399c..403bd3148f4d 100644 --- a/extensions/amp-analytics/0.1/test/test-amp-analytics.js +++ b/extensions/amp-analytics/0.1/test/test-amp-analytics.js @@ -399,8 +399,10 @@ describes.realWin('amp-analytics', { const analytics = getAnalyticsTag({ 'triggers': [{'on': 'visible', 'request': 'foo'}], }); + const spy = sandbox.spy(analytics, 'expandAndSendRequest_'); return waitForNoSendRequest(analytics).then(() => { + expect(spy).to.have.not.been.called; expect(sendRequestSpy).to.have.not.been.called; }); }); From a0f855bb9e86b2fae6beac63f3c9d6868e27a970 Mon Sep 17 00:00:00 2001 From: Raghu Simha Date: Mon, 5 Feb 2018 18:54:31 -0500 Subject: [PATCH 17/65] Run lint and presubmit on gulpfile.js (#13258) --- build-system/check-package-manager.js | 40 +- build-system/config.js | 2 - build-system/tasks/presubmit-checks.js | 6 +- gulpfile.js | 665 ++++++++++++------------- 4 files changed, 354 insertions(+), 359 deletions(-) diff --git a/build-system/check-package-manager.js b/build-system/check-package-manager.js index d49339daedf6..b68e52146741 100644 --- a/build-system/check-package-manager.js +++ b/build-system/check-package-manager.js @@ -35,24 +35,22 @@ function main() { // If npm is being run, print a message and cause 'npm install' to fail. if (process.env.npm_execpath.indexOf('yarn') === -1) { - console/*OK*/.log(red( + console.log(red( '*** The AMP project uses yarn for package management ***'), '\n'); - console/*OK*/.log(yellow('To install all packages:')); - console/*OK*/.log(cyan('$'), 'yarn', '\n'); - console/*OK*/.log( + console.log(yellow('To install all packages:')); + console.log(cyan('$'), 'yarn', '\n'); + console.log( yellow('To install a new (runtime) package to "dependencies":')); - console/*OK*/.log(cyan('$'), - 'yarn add --exact [package_name@version]', '\n'); - console/*OK*/.log( + console.log(cyan('$'), 'yarn add --exact [package_name@version]', '\n'); + console.log( yellow('To install a new (toolset) package to "devDependencies":')); - console/*OK*/.log(cyan('$'), + console.log(cyan('$'), 'yarn add --dev --exact [package_name@version]', '\n'); - console/*OK*/.log(yellow('To upgrade a package:')); - console/*OK*/.log(cyan('$'), - 'yarn upgrade --exact [package_name@version]', '\n'); - console/*OK*/.log(yellow('To remove a package:')); - console/*OK*/.log(cyan('$'), 'yarn remove [package_name]', '\n'); - console/*OK*/.log(yellow('For detailed instructions, see'), + console.log(yellow('To upgrade a package:')); + console.log(cyan('$'), 'yarn upgrade --exact [package_name@version]', '\n'); + console.log(yellow('To remove a package:')); + console.log(cyan('$'), 'yarn remove [package_name]', '\n'); + console.log(yellow('For detailed instructions, see'), cyan(setupInstructionsUrl), '\n'); return 1; } @@ -65,15 +63,15 @@ function main() { } majorVersion = parseInt(majorVersion, 10); if (majorVersion < 6 || majorVersion == 7) { - console/*OK*/.log(yellow('WARNING: Detected node version'), + console.log(yellow('WARNING: Detected node version'), cyan(nodeVersion) + yellow('. Recommended version is'), cyan('v6') + yellow('.')); - console/*OK*/.log(yellow('To fix this, run'), + console.log(yellow('To fix this, run'), cyan('"nvm install 6"'), yellow('or see'), cyan('https://nodejs.org/en/download/package-manager'), yellow('for instructions.')); } else { - console/*OK*/.log(green('Detected node version'), cyan(nodeVersion) + + console.log(green('Detected node version'), cyan(nodeVersion) + green('.')); } @@ -82,16 +80,16 @@ function main() { const major = parseInt(yarnVersion.split('.')[0], 10); const minor = parseInt(yarnVersion.split('.')[1], 10); if ((major < 1) || (minor < 2)) { - console/*OK*/.log(yellow('WARNING: Detected yarn version'), + console.log(yellow('WARNING: Detected yarn version'), cyan(yarnVersion) + yellow('. Minimum recommended version is'), cyan('1.2.0') + yellow('.')); - console/*OK*/.log(yellow('To upgrade, run'), + console.log(yellow('To upgrade, run'), cyan('"curl -o- -L https://yarnpkg.com/install.sh | bash"'), yellow('or see'), cyan('https://yarnpkg.com/docs/install'), yellow('for instructions.')); - console/*OK*/.log(yellow('Attempting to install packages...')); + console.log(yellow('Attempting to install packages...')); } else { - console/*OK*/.log(green('Detected yarn version'), cyan(yarnVersion) + + console.log(green('Detected yarn version'), cyan(yarnVersion) + green('. Installing packages...')); }; return 0; diff --git a/build-system/config.js b/build-system/config.js index 26ee51f108cb..dfb1f6801454 100644 --- a/build-system/config.js +++ b/build-system/config.js @@ -116,7 +116,6 @@ module.exports = { // TODO: temporary, remove when validator is up to date '!validator/**/*.*', '!eslint-rules/**/*.*', - '!gulpfile.js', '!karma.conf.js', '!**/local-amp-chrome-extension/background.js', '!extensions/amp-access/0.1/access-expr-impl.js', @@ -146,7 +145,6 @@ module.exports = { '!build-system/tasks/presubmit-checks.js', '!build/polyfills.js', '!build/polyfills/*.js', - '!gulpfile.js', '!third_party/**/*.*', '!validator/chromeextension/*.*', // Files in this testdata dir are machine-generated and are not part diff --git a/build-system/tasks/presubmit-checks.js b/build-system/tasks/presubmit-checks.js index 5ab38efa8888..e71b8e94b305 100644 --- a/build-system/tasks/presubmit-checks.js +++ b/build-system/tasks/presubmit-checks.js @@ -90,15 +90,17 @@ const forbiddenTerms = { ' If this is cross domain, overwrite the method directly.', }, 'console\\.\\w+\\(': { - message: 'If you run against this, use console/*OK*/.log to ' + + message: 'If you run against this, use console/*OK*/.[log|error] to ' + 'whitelist a legit case.', whitelist: [ 'build-system/pr-check.js', 'build-system/app.js', + 'build-system/check-package-manager.js', 'validator/nodejs/index.js', // NodeJs only. 'validator/engine/parse-css.js', 'validator/engine/validator-in-browser.js', 'validator/engine/validator.js', + 'gulpfile.js', ], checkInTestFolder: true, }, @@ -575,6 +577,7 @@ const forbiddenTerms = { 'src/worker-error-reporting.js', 'tools/experiments/experiments.js', 'build-system/amp4test.js', + 'gulpfile.js', ], }, 'data:image/svg(?!\\+xml;charset=utf-8,)[^,]*,': { @@ -843,6 +846,7 @@ const forbiddenTermsSrcInclusive = { 'validator/webui/serve-standalone.go', 'build-system/tasks/check-links.js', 'build-system/tasks/extension-generator/index.js', + 'gulpfile.js', ], }, '\\<\\<\\<\\<\\<\\<': { diff --git a/gulpfile.js b/gulpfile.js index 7a1305558050..5f084ae77985 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -14,52 +14,54 @@ * limitations under the License. */ +/* global require, process */ + checkMinVersion(); -var $$ = require('gulp-load-plugins')(); -var babel = require('babelify'); -var browserify = require('browserify'); -var buffer = require('vinyl-buffer'); -var closureCompile = require('./build-system/tasks/compile').closureCompile; -var cleanupBuildDir = require('./build-system/tasks/compile').cleanupBuildDir; -var jsifyCssAsync = require('./build-system/tasks/jsify-css').jsifyCssAsync; -var applyConfig = require('./build-system/tasks/prepend-global/index.js').applyConfig; -var removeConfig = require('./build-system/tasks/prepend-global/index.js').removeConfig; -var serve = require('./build-system/tasks/serve.js').serve; -var fs = require('fs-extra'); -var gulp = $$.help(require('gulp')); -var lazypipe = require('lazypipe'); -var minimatch = require('minimatch'); -var minimist = require('minimist'); -var source = require('vinyl-source-stream'); -var touch = require('touch'); -var watchify = require('watchify'); -var internalRuntimeVersion = require('./build-system/internal-version').VERSION; -var internalRuntimeToken = require('./build-system/internal-version').TOKEN; -var colors = require('ansi-colors'); -var log = require('fancy-log'); -var createCtrlcHandler = require('./build-system/ctrlcHandler').createCtrlcHandler; -var exitCtrlcHandler = require('./build-system/ctrlcHandler').exitCtrlcHandler; -var argv = minimist(process.argv.slice(2), {boolean: ['strictBabelTransform']}); +const $$ = require('gulp-load-plugins')(); +const applyConfig = require('./build-system/tasks/prepend-global/index.js').applyConfig; +const babel = require('babelify'); +const browserify = require('browserify'); +const buffer = require('vinyl-buffer'); +const cleanupBuildDir = require('./build-system/tasks/compile').cleanupBuildDir; +const closureCompile = require('./build-system/tasks/compile').closureCompile; +const colors = require('ansi-colors'); +const createCtrlcHandler = require('./build-system/ctrlcHandler').createCtrlcHandler; +const exitCtrlcHandler = require('./build-system/ctrlcHandler').exitCtrlcHandler; +const fs = require('fs-extra'); +const gulp = $$.help(require('gulp')); +const internalRuntimeToken = require('./build-system/internal-version').TOKEN; +const internalRuntimeVersion = require('./build-system/internal-version').VERSION; +const jsifyCssAsync = require('./build-system/tasks/jsify-css').jsifyCssAsync; +const lazypipe = require('lazypipe'); +const log = require('fancy-log'); +const minimatch = require('minimatch'); +const minimist = require('minimist'); +const removeConfig = require('./build-system/tasks/prepend-global/index.js').removeConfig; +const serve = require('./build-system/tasks/serve.js').serve; +const source = require('vinyl-source-stream'); +const touch = require('touch'); +const watchify = require('watchify'); +const argv = minimist( + process.argv.slice(2), {boolean: ['strictBabelTransform']}); require('./build-system/tasks'); -var hostname = argv.hostname || 'cdn.ampproject.org'; -var hostname3p = argv.hostname3p || '3p.ampproject.net'; +const hostname = argv.hostname || 'cdn.ampproject.org'; +const hostname3p = argv.hostname3p || '3p.ampproject.net'; // All declared extensions. -var extensions = {}; -var extensionAliasFilePath = {}; +const extensions = {}; +const extensionAliasFilePath = {}; -var green = colors.green; -var yellow = colors.yellow; -var red = colors.red; -var cyan = colors.cyan; +const green = colors.green; +const red = colors.red; +const cyan = colors.cyan; -var minifiedRuntimeTarget = 'dist/v0.js'; -var minified3pTarget = 'dist.3p/current-min/f.js'; -var unminifiedRuntimeTarget = 'dist/amp.js'; -var unminified3pTarget = 'dist.3p/current/integration.js'; +const minifiedRuntimeTarget = 'dist/v0.js'; +const minified3pTarget = 'dist.3p/current-min/f.js'; +const unminifiedRuntimeTarget = 'dist/amp.js'; +const unminified3pTarget = 'dist.3p/current/integration.js'; // Each extension and version must be listed individually here. declareExtension('amp-3q-player', '0.1', {hasCss: false}); @@ -176,7 +178,7 @@ declareExtensionVersionAlias( * loadPriority: ?string * }} */ -const ExtensionOption = {}; +const ExtensionOption = {}; // eslint-disable-line no-unused-vars /** * @param {string} name @@ -227,7 +229,7 @@ function endBuildStep(stepName, targetName, startTime) { const executionTime = new Date(endTime - startTime); const secs = executionTime.getSeconds(); const ms = ('000' + executionTime.getMilliseconds().toString()).slice(-3); - var timeString = '('; + let timeString = '('; if (secs === 0) { timeString += ms + ' ms)'; } else { @@ -249,19 +251,19 @@ function buildExtensions(options) { return Promise.resolve(); } - var extensionsToBuild = []; + let extensionsToBuild = []; if (!!argv.extensions) { extensionsToBuild = argv.extensions.split(','); } - var results = []; - for (var key in extensions) { + const results = []; + for (const key in extensions) { if (extensionsToBuild.length > 0 && extensionsToBuild.indexOf(extensions[key].name) == -1) { continue; } - var e = extensions[key]; - var o = Object.assign({}, options); + const e = extensions[key]; + let o = Object.assign({}, options); o = Object.assign(o, e); results.push(buildExtension(e.name, e.version, e.hasCss, o, e.extraGlobs)); } @@ -287,46 +289,46 @@ function polyfillsForTests() { * @return {!Promise} */ function compile(watch, shouldMinify, opt_preventRemoveAndMakeDir, - opt_checkTypes) { - var promises = [ + opt_checkTypes) { + const promises = [ compileJs('./3p/', 'integration.js', './dist.3p/' + (shouldMinify ? internalRuntimeVersion : 'current'), { - minifiedName: 'f.js', - checkTypes: opt_checkTypes, - watch: watch, - minify: shouldMinify, - preventRemoveAndMakeDir: opt_preventRemoveAndMakeDir, - externs: ['ads/ads.extern.js',], - include3pDirectories: true, - includePolyfills: true, - }), + minifiedName: 'f.js', + checkTypes: opt_checkTypes, + watch, + minify: shouldMinify, + preventRemoveAndMakeDir: opt_preventRemoveAndMakeDir, + externs: ['ads/ads.extern.js'], + include3pDirectories: true, + includePolyfills: true, + }), compileJs('./3p/', 'ampcontext-lib.js', './dist.3p/' + (shouldMinify ? internalRuntimeVersion : 'current'), { - minifiedName: 'ampcontext-v0.js', - checkTypes: opt_checkTypes, - watch: watch, - minify: shouldMinify, - preventRemoveAndMakeDir: opt_preventRemoveAndMakeDir, - externs: ['ads/ads.extern.js',], - include3pDirectories: true, - includePolyfills: false, - }), + minifiedName: 'ampcontext-v0.js', + checkTypes: opt_checkTypes, + watch, + minify: shouldMinify, + preventRemoveAndMakeDir: opt_preventRemoveAndMakeDir, + externs: ['ads/ads.extern.js'], + include3pDirectories: true, + includePolyfills: false, + }), compileJs('./3p/', 'iframe-transport-client-lib.js', './dist.3p/' + (shouldMinify ? internalRuntimeVersion : 'current'), { - minifiedName: 'iframe-transport-client-v0.js', - checkTypes: opt_checkTypes, - watch: watch, - minify: shouldMinify, - preventRemoveAndMakeDir: opt_preventRemoveAndMakeDir, - externs: ['ads/ads.extern.js',], - include3pDirectories: true, - includePolyfills: false, - }), + minifiedName: 'iframe-transport-client-v0.js', + checkTypes: opt_checkTypes, + watch, + minify: shouldMinify, + preventRemoveAndMakeDir: opt_preventRemoveAndMakeDir, + externs: ['ads/ads.extern.js'], + include3pDirectories: true, + includePolyfills: false, + }), compileJs('./src/', 'amp.js', './dist', { minifiedName: 'v0.js', includePolyfills: true, checkTypes: opt_checkTypes, - watch: watch, + watch, preventRemoveAndMakeDir: opt_preventRemoveAndMakeDir, minify: shouldMinify, // If there is a sync JS error during initial load, @@ -337,70 +339,70 @@ function compile(watch, shouldMinify, opt_preventRemoveAndMakeDir, 's.opacity=1;' + 's.visibility="visible";' + 's.animation="none";' + - 's.WebkitAnimation="none;"},1000);throw e};' + 's.WebkitAnimation="none;"},1000);throw e};', }), compileJs('./extensions/amp-viewer-integration/0.1/examples/', - 'amp-viewer-host.js', './dist/v0/examples', { - toName: 'amp-viewer-host.max.js', - minifiedName: 'amp-viewer-host.js', - incudePolyfills: true, - watch: watch, - extraGlobs: ['extensions/amp-viewer-integration/**/*.js'], - compilationLevel: 'WHITESPACE_ONLY', - preventRemoveAndMakeDir: opt_preventRemoveAndMakeDir, - minify: false, - }), + 'amp-viewer-host.js', './dist/v0/examples', { + toName: 'amp-viewer-host.max.js', + minifiedName: 'amp-viewer-host.js', + incudePolyfills: true, + watch, + extraGlobs: ['extensions/amp-viewer-integration/**/*.js'], + compilationLevel: 'WHITESPACE_ONLY', + preventRemoveAndMakeDir: opt_preventRemoveAndMakeDir, + minify: false, + }), ]; // We don't rerun type check for the shadow entry point for now. if (!opt_checkTypes) { if (!watch || argv.with_shadow) { promises.push( - compileJs('./src/', 'amp-shadow.js', './dist', { - minifiedName: 'shadow-v0.js', - includePolyfills: true, - checkTypes: opt_checkTypes, - watch: watch, - preventRemoveAndMakeDir: opt_preventRemoveAndMakeDir, - minify: shouldMinify, - }) + compileJs('./src/', 'amp-shadow.js', './dist', { + minifiedName: 'shadow-v0.js', + includePolyfills: true, + checkTypes: opt_checkTypes, + watch, + preventRemoveAndMakeDir: opt_preventRemoveAndMakeDir, + minify: shouldMinify, + }) ); } if (!watch || argv.with_inabox) { promises.push( - // Entry point for inabox runtime. - compileJs('./src/inabox/', 'amp-inabox.js', './dist', { - toName: 'amp-inabox.js', - minifiedName: 'amp4ads-v0.js', - includePolyfills: true, - extraGlobs: ['src/inabox/*.js', '3p/iframe-messaging-client.js'], - checkTypes: opt_checkTypes, - watch: watch, - preventRemoveAndMakeDir: opt_preventRemoveAndMakeDir, - minify: shouldMinify, - wrapper: '<%= contents %>', - }), + // Entry point for inabox runtime. + compileJs('./src/inabox/', 'amp-inabox.js', './dist', { + toName: 'amp-inabox.js', + minifiedName: 'amp4ads-v0.js', + includePolyfills: true, + extraGlobs: ['src/inabox/*.js', '3p/iframe-messaging-client.js'], + checkTypes: opt_checkTypes, + watch, + preventRemoveAndMakeDir: opt_preventRemoveAndMakeDir, + minify: shouldMinify, + wrapper: '<%= contents %>', + }), - // inabox-host - compileJs('./ads/inabox/', 'inabox-host.js', './dist', { - toName: 'amp-inabox-host.js', - minifiedName: 'amp4ads-host-v0.js', - includePolyfills: false, - checkTypes: opt_checkTypes, - watch: watch, - preventRemoveAndMakeDir: opt_preventRemoveAndMakeDir, - minify: shouldMinify, - wrapper: '<%= contents %>', - }) + // inabox-host + compileJs('./ads/inabox/', 'inabox-host.js', './dist', { + toName: 'amp-inabox-host.js', + minifiedName: 'amp4ads-host-v0.js', + includePolyfills: false, + checkTypes: opt_checkTypes, + watch, + preventRemoveAndMakeDir: opt_preventRemoveAndMakeDir, + minify: shouldMinify, + wrapper: '<%= contents %>', + }) ); } promises.push( - thirdPartyBootstrap( - '3p/frame.max.html', 'frame.html', shouldMinify), - thirdPartyBootstrap( - '3p/nameframe.max.html', 'nameframe.html',shouldMinify) + thirdPartyBootstrap( + '3p/frame.max.html', 'frame.html', shouldMinify), + thirdPartyBootstrap( + '3p/nameframe.max.html', 'nameframe.html',shouldMinify) ); if (watch) { @@ -447,26 +449,26 @@ function compileCss(watch) { const startTime = Date.now(); return jsifyCssAsync('css/amp.css') - .then(function(css) { - return toPromise(gulp.src('css/**.css') - .pipe($$.file('css.js', 'export const cssText = ' + + .then(function(css) { + return toPromise(gulp.src('css/**.css') + .pipe($$.file('css.js', 'export const cssText = ' + JSON.stringify(css))) - .pipe(gulp.dest('build')) - .on('end', function() { - mkdirSync('build'); - mkdirSync('build/css'); - fs.writeFileSync('build/css/v0.css', css); - })); - }) - .then(() => { - endBuildStep('Recompiled CSS in', 'amp.css', startTime); - }) - .then(() => { - return buildExtensions({ - bundleOnlyIfListedInFiles: false, - compileOnlyCss: true - }); - }); + .pipe(gulp.dest('build')) + .on('end', function() { + mkdirSync('build'); + mkdirSync('build/css'); + fs.writeFileSync('build/css/v0.css', css); + })); + }) + .then(() => { + endBuildStep('Recompiled CSS in', 'amp.css', startTime); + }) + .then(() => { + return buildExtensions({ + bundleOnlyIfListedInFiles: false, + compileOnlyCss: true, + }); + }); } /** @@ -508,9 +510,9 @@ function buildExtension(name, version, hasCss, options, opt_extraGlobs) { if (options.compileOnlyCss && !hasCss) { return Promise.resolve(); } - var path = 'extensions/' + name + '/' + version; - var jsPath = path + '/' + name + '.js'; - var jsTestPath = path + '/test/' + 'test-' + name + '.js'; + const path = 'extensions/' + name + '/' + version; + const jsPath = path + '/' + name + '.js'; + const jsTestPath = path + '/test/test-' + name + '.js'; if (argv.files && options.bundleOnlyIfListedInFiles) { const passedFiles = Array.isArray(argv.files) ? argv.files : [argv.files]; const shouldBundle = passedFiles.some(glob => { @@ -525,7 +527,7 @@ function buildExtension(name, version, hasCss, options, opt_extraGlobs) { // it to the destination and adds the CSS. if (options.watch) { // Do not set watchers again when we get called by the watcher. - var copy = Object.create(options); + const copy = Object.create(options); copy.watch = false; $$.watch(path + '/*', function() { buildExtension(name, version, hasCss, copy); @@ -536,9 +538,9 @@ function buildExtension(name, version, hasCss, options, opt_extraGlobs) { mkdirSync('build/css'); const startTime = Date.now(); return jsifyCssAsync(path + '/' + name + '.css').then(function(css) { - var jsCss = 'export const CSS = ' + JSON.stringify(css) + ';\n'; - var jsName = 'build/' + name + '-' + version + '.css.js'; - var cssName = 'build/css/' + name + '-' + version + '.css'; + const jsCss = 'export const CSS = ' + JSON.stringify(css) + ';\n'; + const jsName = 'build/' + name + '-' + version + '.css.js'; + const cssName = 'build/css/' + name + '-' + version + '.css'; fs.writeFileSync(jsName, jsCss, 'utf-8'); fs.writeFileSync(cssName, css, 'utf-8'); if (options.compileOnlyCss) { @@ -546,9 +548,9 @@ function buildExtension(name, version, hasCss, options, opt_extraGlobs) { } return buildExtensionJs(path, name, version, options); }) - .then(() => { - endBuildStep('Recompiled CSS in', name, startTime); - }); + .then(() => { + endBuildStep('Recompiled CSS in', name, startTime); + }); } else { return buildExtensionJs(path, name, version, options); } @@ -566,11 +568,11 @@ function buildExtension(name, version, hasCss, options, opt_extraGlobs) { * @return {!Promise} */ function buildExtensionJs(path, name, version, options) { - var filename = options.filename || name + '.js'; + const filename = options.filename || name + '.js'; if (options.loadPriority && options.loadPriority != 'high') { throw new Error('Unsupported loadPriority: ' + options.loadPriority); } - var priority = options.loadPriority ? 'p:"high",' : ''; + const priority = options.loadPriority ? 'p:"high",' : ''; return compileJs(path + '/', filename, './dist/v0', { watch: options.watch, preventRemoveAndMakeDir: options.preventRemoveAndMakeDir, @@ -600,9 +602,8 @@ function printConfigHelp(command) { log(green('Building the runtime for local testing with the'), cyan((argv.config === 'canary') ? 'canary' : 'prod'), green('AMP config.')); - log(green('⤷ To specify which config to apply, use'), - cyan('--config={canary|prod}'), green('with your'), - cyan(command), green('command.')); + log(green('⤷ Use'), cyan('--config={canary|prod}'), green('with your'), + cyan(command), green('command to specify which config to apply.')); } } @@ -613,14 +614,22 @@ function printConfigHelp(command) { */ function parseExtensionFlags() { if (!process.env.TRAVIS) { + const noExtensionsMessage = green('⤷ Use ') + + cyan('--noextensions ') + + green('to skip building extensions.'); + const extensionsMessage = green('⤷ Use ') + + cyan('--extensions=amp-foo,amp-bar ') + + green('to choose which extensions to build.'); + const minimalSetMessage = green('⤷ Use ') + + cyan('--extensions=minimal_set ') + + green('to build just the extensions needed to load ') + + cyan('article.amp.html') + green('.'); if (argv.extensions) { - if (typeof(argv.extensions) !== 'string') { - log(red('ERROR:'), 'Missing list of extensions. Expected format:', - cyan('--extensions=amp-foo,amp-bar'), - 'to choose which ones to build, or', - cyan('--extensions=minimal_set'), - 'to build the ones needed to load', - cyan('article.amp.html') + '.'); + if (typeof (argv.extensions) !== 'string') { + log(red('ERROR:'), 'Missing list of extensions.'); + log(noExtensionsMessage); + log(extensionsMessage); + log(minimalSetMessage); process.exit(1); } if (argv.extensions === 'minimal_set') { @@ -631,25 +640,14 @@ function parseExtensionFlags() { } log(green('Building extension(s):'), cyan(argv.extensions.split(',').join(', '))); - log(green('⤷ Use'), cyan('--noextensions'), - green('to skip building extensions.')); } else if (argv.noextensions) { log(green('Not building any AMP extensions.')); - log(green('⤷ Use'), cyan('--extensions=amp-foo,amp-bar'), - green('to choose which ones to build, or'), - cyan('--extensions=minimal_set'), - green('to build the ones needed to load'), - cyan('article.amp.html') + green('.')); } else { log(green('Building all AMP extensions.')); - log(green('⤷ Use'), cyan('--noextensions'), - green('to skip building extensions, or'), - cyan('--extensions=amp-foo,amp-bar'), - green('to choose which ones to build, or'), - cyan('--extensions=minimal_set'), - green('to build the ones needed to load'), - cyan('article.amp.html') + green('.')); } + log(noExtensionsMessage); + log(extensionsMessage); + log(minimalSetMessage); } } @@ -659,8 +657,9 @@ function parseExtensionFlags() { * @param {string} targetFile File to which the config is to be written. */ function enableLocalTesting(targetFile) { - let config = (argv.config === 'canary') ? 'canary' : 'prod'; - let baseConfigFile = 'build-system/global-configs/' + config + '-config.json'; + const config = (argv.config === 'canary') ? 'canary' : 'prod'; + const baseConfigFile = + 'build-system/global-configs/' + config + '-config.json'; return removeConfig(targetFile).then(() => { return applyConfig( @@ -681,11 +680,11 @@ function performBuild(watch) { return compileCss(watch).then(() => { return Promise.all([ polyfillsForTests(), - buildAlp({watch: watch}), - buildExaminer({watch: watch}), - buildSw({watch: watch}), - buildWebWorker({watch: watch}), - buildExtensions({bundleOnlyIfListedInFiles: !watch, watch: watch}), + buildAlp({watch}), + buildExaminer({watch}), + buildSw({watch}), + buildWebWorker({watch}), + buildExtensions({bundleOnlyIfListedInFiles: !watch, watch}), compile(watch), ]); }); @@ -718,7 +717,7 @@ function dist() { process.env.NODE_ENV = 'production'; cleanupBuildDir(); if (argv.fortesting) { - printConfigHelp('gulp dist --fortesting') + printConfigHelp('gulp dist --fortesting'); } parseExtensionFlags(); return compileCss().then(() => { @@ -728,13 +727,18 @@ function dist() { // When adding a line here, consider whether you need to include polyfills // and whether you need to init logging (initLogConstructor). buildAlp({minify: true, watch: false, preventRemoveAndMakeDir: true}), - buildExaminer({minify: true, watch: false, preventRemoveAndMakeDir: true}), + buildExaminer({ + minify: true, watch: false, preventRemoveAndMakeDir: true}), buildSw({minify: true, watch: false, preventRemoveAndMakeDir: true}), - buildWebWorker({minify: true, watch: false, preventRemoveAndMakeDir: true}), + buildWebWorker({ + minify: true, watch: false, preventRemoveAndMakeDir: true}), buildExtensions({minify: true, preventRemoveAndMakeDir: true}), - buildExperiments({minify: true, watch: false, preventRemoveAndMakeDir: true}), - buildLoginDone({minify: true, watch: false, preventRemoveAndMakeDir: true}), - buildWebPushPublisherFiles({minify: true, watch: false, preventRemoveAndMakeDir: true}), + buildExperiments({ + minify: true, watch: false, preventRemoveAndMakeDir: true}), + buildLoginDone({ + minify: true, watch: false, preventRemoveAndMakeDir: true}), + buildWebPushPublisherFiles({ + minify: true, watch: false, preventRemoveAndMakeDir: true}), copyCss(), ]); }).then(() => { @@ -757,12 +761,12 @@ function copyAliasExtensions() { if (argv.noextensions) { return; } - var extensionsToBuild = []; + let extensionsToBuild = []; if (!!argv.extensions) { extensionsToBuild = argv.extensions.split(','); } - for (var key in extensionAliasFilePath) { + for (const key in extensionAliasFilePath) { if (extensionsToBuild.length > 0 && extensionsToBuild.indexOf(extensionAliasFilePath[key]['name']) == -1) { continue; @@ -786,7 +790,7 @@ function checkTypes() { checkTypes: true, preventRemoveAndMakeDir: true, });*/ - var compileSrcs = [ + const compileSrcs = [ './src/amp.js', './src/amp-shadow.js', './src/inabox/amp-inabox.js', @@ -797,10 +801,10 @@ function checkTypes() { './src/service-worker/kill.js', './src/web-worker/web-worker.js', ]; - var extensionValues = Object.keys(extensions).map(function(key) { + const extensionValues = Object.keys(extensions).map(function(key) { return extensions[key]; }); - var extensionSrcs = extensionValues.filter(function(extension) { + const extensionSrcs = extensionValues.filter(function(extension) { return !extension.noTypeCheck; }).map(function(extension) { return './extensions/' + extension.name + '/' + @@ -817,26 +821,26 @@ function checkTypes() { }), // Type check 3p/ads code. closureCompile(['./3p/integration.js'], './dist', - 'integration-check-types.js', { - externs: ['ads/ads.extern.js'], - include3pDirectories: true, - includePolyfills: true, - checkTypes: true, - }), + 'integration-check-types.js', { + externs: ['ads/ads.extern.js'], + include3pDirectories: true, + includePolyfills: true, + checkTypes: true, + }), closureCompile(['./3p/ampcontext-lib.js'], './dist', - 'ampcontext-check-types.js', { - externs: ['ads/ads.extern.js'], - include3pDirectories: true, - includePolyfills: true, - checkTypes: true, - }), + 'ampcontext-check-types.js', { + externs: ['ads/ads.extern.js'], + include3pDirectories: true, + includePolyfills: true, + checkTypes: true, + }), closureCompile(['./3p/iframe-transport-client-lib.js'], './dist', - 'iframe-transport-client-check-types.js', { - externs: ['ads/ads.extern.js'], - include3pDirectories: true, - includePolyfills: true, - checkTypes: true, - }), + 'iframe-transport-client-check-types.js', { + externs: ['ads/ads.extern.js'], + include3pDirectories: true, + includePolyfills: true, + checkTypes: true, + }), ]); }); } @@ -864,16 +868,16 @@ function thirdPartyBootstrap(input, outputName, shouldMinify) { // actual frame host for the JS inside the frame. // But during testing we need a relative reference because the // version is not available on the absolute path. - var integrationJs = argv.fortesting - ? './f.js' - : `https://${hostname3p}/${internalRuntimeVersion}/f.js`; + const integrationJs = argv.fortesting + ? './f.js' + : `https://${hostname3p}/${internalRuntimeVersion}/f.js`; // Convert default relative URL to absolute min URL. - var html = fs.readFileSync(input, 'utf8') + const html = fs.readFileSync(input, 'utf8') .replace(/\.\/integration\.js/g, integrationJs); return toPromise($$.file(outputName, html, {src: true}) .pipe(gulp.dest('dist.3p/' + internalRuntimeVersion)) .on('end', function() { - var aliasToLatestBuild = 'dist.3p/current-min'; + const aliasToLatestBuild = 'dist.3p/current-min'; if (fs.existsSync(aliasToLatestBuild)) { fs.unlinkSync(aliasToLatestBuild); } @@ -952,30 +956,30 @@ function compileJs(srcDir, srcFilename, destDir, options) { }); } - var browsers = []; + const browsers = []; if (process.env.TRAVIS) { browsers.push('last 2 versions', 'safari >= 9'); } else { browsers.push('Last 4 Chrome versions'); } - var bundler = browserify(srcDir + srcFilename, {debug: true}) + let bundler = browserify(srcDir + srcFilename, {debug: true}) .transform(babel, { presets: [ - ["env", { + ['env', { targets: { - browsers: browsers, + browsers, }, - }] + }], ], }); if (options.watch) { bundler = watchify(bundler); } - var wrapper = options.wrapper || '<%= contents %>'; + const wrapper = options.wrapper || '<%= contents %>'; - var lazybuild = lazypipe() + const lazybuild = lazypipe() .pipe(source, srcFilename) .pipe(buffer) .pipe($$.replace, /\$internalRuntimeVersion\$/g, internalRuntimeVersion) @@ -983,40 +987,43 @@ function compileJs(srcDir, srcFilename, destDir, options) { .pipe($$.wrap, wrapper) .pipe($$.sourcemaps.init.bind($$.sourcemaps), {loadMaps: true}); - var lazywrite = lazypipe() + const lazywrite = lazypipe() .pipe($$.sourcemaps.write.bind($$.sourcemaps), './') .pipe(gulp.dest.bind(gulp), destDir); - var destFilename = options.toName || srcFilename; + const destFilename = options.toName || srcFilename; function rebundle() { const startTime = Date.now(); - return toPromise(bundler.bundle() - .on('error', function(err) { - // Drop the node_modules call stack, which begins with ' at'. - const message = err.stack.replace(/ at[^]*/, '').trim(); - console.error(red(message)); - process.exit(1); - }) - .pipe(lazybuild()) - .pipe($$.rename(destFilename)) - .pipe(lazywrite()) - .on('end', function() { - appendToCompiledFile(srcFilename, destDir + '/' + destFilename); - })).then(() => { - endBuildStep('Compiled', srcFilename, startTime); - }).then(() => { - if (process.env.NODE_ENV === 'development') { - if (srcFilename === 'amp.js') { - return enableLocalTesting(unminifiedRuntimeTarget); - } else if (srcFilename === 'integration.js') { - return enableLocalTesting(unminified3pTarget); + return toPromise( + bundler.bundle() + .on('error', function(err) { + // Drop the node_modules call stack, which begins with ' at'. + const message = err.stack.replace(/ at[^]*/, '').trim(); + console.error(red(message)); + process.exit(1); + }) + .pipe(lazybuild()) + .pipe($$.rename(destFilename)) + .pipe(lazywrite()) + .on('end', function() { + appendToCompiledFile(srcFilename, destDir + '/' + destFilename); + })) + .then(() => { + endBuildStep('Compiled', srcFilename, startTime); + }) + .then(() => { + if (process.env.NODE_ENV === 'development') { + if (srcFilename === 'amp.js') { + return enableLocalTesting(unminifiedRuntimeTarget); + } else if (srcFilename === 'integration.js') { + return enableLocalTesting(unminified3pTarget); + } else { + return Promise.resolve(); + } } else { return Promise.resolve(); } - } else { - return Promise.resolve(); - } - }); + }); } if (options.watch) { @@ -1049,10 +1056,10 @@ function compileJs(srcDir, srcFilename, destDir, options) { */ function buildExperiments(options) { options = options || {}; - var path = 'tools/experiments'; - var htmlPath = path + '/experiments.html'; - var jsPath = path + '/experiments.js'; - var watch = options.watch; + const path = 'tools/experiments'; + const htmlPath = path + '/experiments.html'; + const jsPath = path + '/experiments.js'; + let watch = options.watch; if (watch === undefined) { watch = argv.watch || argv.w; } @@ -1062,7 +1069,7 @@ function buildExperiments(options) { // it to the destination and adds the CSS. if (watch) { // Do not set watchers again when we get called by the watcher. - var copy = Object.create(options); + const copy = Object.create(options); copy.watch = false; $$.watch(path + '/*', function() { buildExperiments(copy); @@ -1070,17 +1077,17 @@ function buildExperiments(options) { } // Build HTML. - var html = fs.readFileSync(htmlPath, 'utf8'); - var minHtml = html.replace('/dist.tools/experiments/experiments.js', + const html = fs.readFileSync(htmlPath, 'utf8'); + const minHtml = html.replace('/dist.tools/experiments/experiments.js', `https://${hostname}/v0/experiments.js`); gulp.src(htmlPath) .pipe($$.file('experiments.cdn.html', minHtml)) .pipe(gulp.dest('dist.tools/experiments/')); // Build JS. - var js = fs.readFileSync(jsPath, 'utf8'); - var builtName = 'experiments.max.js'; - var minifiedName = 'experiments.js'; + const js = fs.readFileSync(jsPath, 'utf8'); + const builtName = 'experiments.max.js'; + const minifiedName = 'experiments.js'; return toPromise(gulp.src(path + '/*.js') .pipe($$.file(builtName, js)) .pipe(gulp.dest('build/experiments/'))) @@ -1090,7 +1097,7 @@ function buildExperiments(options) { watch: false, minify: options.minify || argv.minify, includePolyfills: true, - minifiedName: minifiedName, + minifiedName, preventRemoveAndMakeDir: options.preventRemoveAndMakeDir, checkTypes: options.checkTypes, }); @@ -1115,31 +1122,16 @@ function buildWebPushPublisherFiles(options) { */ function buildWebPushPublisherFilesVersion(version, options) { options = options || {}; - var watch = options.watch; - if (watch === undefined) { - watch = argv.watch || argv.w; - } - - // Building extensions is a 2 step process because of the renaming - // and CSS inlining. This watcher watches the original file, copies - // it to the destination and adds the CSS. - if (watch) { - // Do not set watchers again when we get called by the watcher. - var copy = Object.create(options); - copy.watch = false; - $$.watch(path + '/*', function() { - buildWebPushPublisherFiles(version); - }); - } - - var fileNames = ['amp-web-push-helper-frame', 'amp-web-push-permission-dialog']; - var promises = []; + const watch = options.watch; + const fileNames = + ['amp-web-push-helper-frame', 'amp-web-push-permission-dialog']; + const promises = []; mkdirSync('dist'); mkdirSync('dist/v0'); - for (var i = 0; i < fileNames.length; i++) { - var fileName = fileNames[i]; + for (let i = 0; i < fileNames.length; i++) { + const fileName = fileNames[i]; promises.push(buildWebPushPublisherFile(version, fileName, watch, options)); } @@ -1147,40 +1139,41 @@ function buildWebPushPublisherFilesVersion(version, options) { } function buildWebPushPublisherFile(version, fileName, watch, options) { - var basePath = 'extensions/amp-web-push/' + version + '/'; - var tempBuildDir = 'build/all/v0/'; - var distDir = 'dist/v0'; + const basePath = 'extensions/amp-web-push/' + version + '/'; + const tempBuildDir = 'build/all/v0/'; + const distDir = 'dist/v0'; // Build Helper Frame JS - var js = fs.readFileSync(basePath + fileName + '.js', 'utf8'); - var builtName = fileName + '.js'; - var minifiedName = fileName + '.js'; + const js = fs.readFileSync(basePath + fileName + '.js', 'utf8'); + const builtName = fileName + '.js'; + const minifiedName = fileName + '.js'; return toPromise(gulp.src(basePath + '/*.js') - .pipe($$.file(builtName, js)) - .pipe(gulp.dest(tempBuildDir))) - .then(function () { - return compileJs('./' + tempBuildDir, builtName, './' + distDir, { - watch: watch, - includePolyfills: true, - minify: options.minify || argv.minify, - minifiedName: minifiedName, - preventRemoveAndMakeDir: options.preventRemoveAndMakeDir, + .pipe($$.file(builtName, js)) + .pipe(gulp.dest(tempBuildDir))) + .then(function() { + return compileJs('./' + tempBuildDir, builtName, './' + distDir, { + watch, + includePolyfills: true, + minify: options.minify || argv.minify, + minifiedName, + preventRemoveAndMakeDir: options.preventRemoveAndMakeDir, + }); + }) + .then(function() { + if (fs.existsSync(distDir + '/' + minifiedName)) { + // Build Helper Frame HTML + let fileContents = + fs.readFileSync(basePath + fileName + '.html', 'utf8'); + fileContents = fileContents.replace( + '', + '' + ); + + fs.writeFileSync('dist/v0/' + fileName + '.html', + fileContents); + } }); - }) - .then(function () { - if (fs.existsSync(distDir + '/' + minifiedName)) { - // Build Helper Frame HTML - var fileContents = fs.readFileSync(basePath + fileName + '.html', 'utf8'); - fileContents = fileContents.replace( - '', - '' - ); - - fs.writeFileSync('dist/v0/' + fileName + '.html', - fileContents); - } - }); } @@ -1200,10 +1193,10 @@ function buildLoginDone(options) { */ function buildLoginDoneVersion(version, options) { options = options || {}; - var path = 'extensions/amp-access/' + version + '/'; - var htmlPath = path + 'amp-login-done.html'; - var jsPath = path + 'amp-login-done.js'; - var watch = options.watch; + const path = 'extensions/amp-access/' + version + '/'; + const htmlPath = path + 'amp-login-done.html'; + const jsPath = path + 'amp-login-done.js'; + let watch = options.watch; if (watch === undefined) { watch = argv.watch || argv.w; } @@ -1213,7 +1206,7 @@ function buildLoginDoneVersion(version, options) { // it to the destination and adds the CSS. if (watch) { // Do not set watchers again when we get called by the watcher. - var copy = Object.create(options); + const copy = Object.create(options); copy.watch = false; $$.watch(path + '/*', function() { buildLoginDoneVersion(version, copy); @@ -1221,9 +1214,9 @@ function buildLoginDoneVersion(version, options) { } // Build HTML. - var html = fs.readFileSync(htmlPath, 'utf8'); - var minJs = `https://${hostname}/v0/amp-login-done-${version}.js`; - var minHtml = html + const html = fs.readFileSync(htmlPath, 'utf8'); + const minJs = `https://${hostname}/v0/amp-login-done-${version}.js`; + const minHtml = html .replace( `../../../dist/v0/amp-login-done-${version}.max.js`, minJs) @@ -1241,10 +1234,10 @@ function buildLoginDoneVersion(version, options) { minHtml); // Build JS. - var js = fs.readFileSync(jsPath, 'utf8'); - var builtName = 'amp-login-done-' + version + '.max.js'; - var minifiedName = 'amp-login-done-' + version + '.js'; - var latestName = 'amp-login-done-latest.js'; + const js = fs.readFileSync(jsPath, 'utf8'); + const builtName = 'amp-login-done-' + version + '.max.js'; + const minifiedName = 'amp-login-done-' + version + '.js'; + const latestName = 'amp-login-done-latest.js'; return toPromise(gulp.src(path + '/*.js') .pipe($$.file(builtName, js)) .pipe(gulp.dest('build/all/v0/'))) @@ -1253,9 +1246,9 @@ function buildLoginDoneVersion(version, options) { watch: false, includePolyfills: true, minify: options.minify || argv.minify, - minifiedName: minifiedName, + minifiedName, preventRemoveAndMakeDir: options.preventRemoveAndMakeDir, - latestName: latestName, + latestName, }); }); } @@ -1300,7 +1293,7 @@ function buildExaminer(options) { * @param {!Object} options */ function buildSw(options) { - var opts = Object.assign({}, options); + const opts = Object.assign({}, options); return Promise.all([ // The service-worker script loaded by the browser. compileJs('./src/service-worker/', 'shell.js', './dist/', { @@ -1330,7 +1323,7 @@ function buildSw(options) { * @param {!Object} options */ function buildWebWorker(options) { - var opts = Object.assign({}, options); + const opts = Object.assign({}, options); return compileJs('./src/web-worker/', 'web-worker.js', './dist/', { toName: 'ww.max.js', minifiedName: 'ww.js', @@ -1347,7 +1340,7 @@ function buildWebWorker(options) { * errors from modules that e.g. use let. */ function checkMinVersion() { - var majorVersion = Number(process.version.replace(/v/, '').split('.')[0]); + const majorVersion = Number(process.version.replace(/v/, '').split('.')[0]); if (majorVersion < 4) { log('Please run AMP with node.js version 4 or newer.'); log('Your version is', process.version); @@ -1358,7 +1351,7 @@ function checkMinVersion() { function mkdirSync(path) { try { fs.mkdirSync(path); - } catch(e) { + } catch (e) { if (e.code != 'EEXIST') { throw e; } @@ -1376,7 +1369,7 @@ function patchWebAnimations() { // Copies web-animations-js into a new file that has an export. const patchedName = 'node_modules/web-animations-js/' + 'web-animations.install.js'; - var file = fs.readFileSync( + let file = fs.readFileSync( 'node_modules/web-animations-js/' + 'web-animations.min.js').toString(); // Wrap the contents inside the install function. @@ -1396,6 +1389,8 @@ function toPromise(readable) { }); } +/* eslint "google-camelcase/google-camelcase": 0 */ + /** * Gulp tasks */ @@ -1405,7 +1400,7 @@ gulp.task('build', 'Builds the AMP library', config: ' Sets the runtime\'s AMP_CONFIG to one of "prod" or "canary"', extensions: ' Builds only the listed extensions.', noextensions: ' Builds with no extensions.', - } + }, }); gulp.task('check-all', 'Run through all presubmit checks', ['lint', 'dep-check', 'check-types', 'presubmit']); @@ -1419,7 +1414,7 @@ gulp.task('default', 'Runs "watch" and then "serve"', options: { extensions: ' Watches and builds only the listed extensions.', noextensions: ' Watches and builds with no extensions.', - } + }, }); gulp.task('dist', 'Build production binaries', ['update-packages', 'patch-web-animations'], dist, { @@ -1428,7 +1423,7 @@ gulp.task('dist', 'Build production binaries', 'Great for profiling and debugging production code.', fortesting: ' Compiles production binaries for local testing', config: ' Sets the runtime\'s AMP_CONFIG to one of "prod" or "canary"', - } + }, }); gulp.task('watch', 'Watches for changes in files, re-builds when detected', ['update-packages', 'patch-web-animations'], watch, { @@ -1437,7 +1432,7 @@ gulp.task('watch', 'Watches for changes in files, re-builds when detected', with_shadow: ' Also watch and build the amp-shadow.js binary.', extensions: ' Watches and builds only the listed extensions.', noextensions: ' Watches and builds with no extensions.', - } -}); + }, + }); gulp.task('build-experiments', 'Builds experiments.html/js', buildExperiments); gulp.task('build-login-done', 'Builds login-done.html/js', buildLoginDone); From d8df163c1598ba2cfe76a5d833ad0a294febf73a Mon Sep 17 00:00:00 2001 From: Gabriel Majoulet Date: Mon, 5 Feb 2018 21:09:13 -0500 Subject: [PATCH 18/65] =?UTF-8?q?=F0=9F=90=9B=F0=9F=96=8D=EF=B8=8F=20Preve?= =?UTF-8?q?nting=20publishers=20CSS=20from=20overriding=20stories=20system?= =?UTF-8?q?=20layer=20and=20bookend.=20(#13270)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Preventing CSS overrides. * Preventing CSS overrides for the navigation hint, landscape orientation and unsupported browser. --- extensions/amp-story/0.1/amp-story-hint.css | 24 ++++++++-------- extensions/amp-story/0.1/amp-story-hint.js | 4 ++- extensions/amp-story/0.1/amp-story.css | 32 +++++++++++++++++---- extensions/amp-story/0.1/amp-story.js | 6 +++- extensions/amp-story/0.1/bookend.js | 4 ++- extensions/amp-story/0.1/system-layer.js | 3 +- 6 files changed, 51 insertions(+), 22 deletions(-) diff --git a/extensions/amp-story/0.1/amp-story-hint.css b/extensions/amp-story/0.1/amp-story-hint.css index 098b760ac1e5..949dbd9ec58a 100644 --- a/extensions/amp-story/0.1/amp-story-hint.css +++ b/extensions/amp-story/0.1/amp-story-hint.css @@ -34,7 +34,7 @@ display: none !important; } -.i-amphtml-story-navigation-help-overlay { +.i-amphtml-story-hint-container .i-amphtml-story-navigation-help-overlay { position: absolute !important; left: 0 !important; top: 0 !important; @@ -43,8 +43,8 @@ background: rgba(0,0,0,0.7) !important; flex-direction: row !important; color: #fff !important; - font-size: 20px; - padding: 16px 0; + font-size: 20px !important; + padding: 16px 0 !important; } .i-amphtml-story-navigation-help-section { @@ -77,13 +77,13 @@ padding: 0px !important; } -.next-page { +.i-amphtml-story-hint-container .next-page { flex: 3 !important; - border-left: 1px dashed transparent; - border-image-source: url('data:image/svg+xml;charset=utf-8,'); - border-image-slice: 33% 33%; - border-image-repeat: repeat; - border-image-width: 14px; + border-left: 1px dashed transparent !important; + border-image-source: url('data:image/svg+xml;charset=utf-8,') !important; + border-image-slice: 33% 33% !important; + border-image-repeat: repeat !important; + border-image-width: 14px !important; } .show-navigation-overlay .i-amphtml-story-navigation-help-overlay, @@ -103,7 +103,7 @@ display: inline-block !important; } -.i-amphtml-story-hint-tap-button { +.i-amphtml-story-hint-container .i-amphtml-story-hint-tap-button { position: relative !important; width: 44px !important; height: 44px !important; @@ -128,7 +128,7 @@ background-color: #fff !important; } -.i-amphtml-story-hint-tap-button-icon { +.i-amphtml-story-hint-container .i-amphtml-story-hint-tap-button-icon { position: absolute !important; z-index: 1 !important; height: 44px !important; @@ -157,7 +157,7 @@ position: absolute; } -.i-amphtml-story-hint-tap-button-text { +.i-amphtml-story-hint-container .i-amphtml-story-hint-tap-button-text { color: #fff !important; font-size: 16px !important; font-family: 'Roboto-Medium', sans-serif !important; diff --git a/extensions/amp-story/0.1/amp-story-hint.js b/extensions/amp-story/0.1/amp-story-hint.js index 134a2a491325..7e97c6230730 100644 --- a/extensions/amp-story/0.1/amp-story-hint.js +++ b/extensions/amp-story/0.1/amp-story-hint.js @@ -22,7 +22,9 @@ import {renderAsElement} from './simple-template'; /** @private @const {!./simple-template.ElementDef} */ const TEMPLATE = { tag: 'aside', - attrs: dict({'class': 'i-amphtml-story-hint-container i-amphtml-hidden'}), + attrs: dict({ + 'class': 'i-amphtml-story-hint-container ' + + 'i-amphtml-story-system-reset i-amphtml-hidden'}), children: [ { tag: 'div', diff --git a/extensions/amp-story/0.1/amp-story.css b/extensions/amp-story/0.1/amp-story.css index 543d4f12e1e1..d26f51556743 100644 --- a/extensions/amp-story/0.1/amp-story.css +++ b/extensions/amp-story/0.1/amp-story.css @@ -24,6 +24,20 @@ amp-story, amp-story-page, amp-story-grid-layer { overflow: hidden !important; } +.i-amphtml-story-system-reset, +.i-amphtml-story-system-reset * { + border: none !important; + color: initial !important; + font-family: 'Roboto', sans-serif !important; + font-size: initial !important; + font-weight: initial !important; + height: auto !important; + margin: 0 !important; + padding: 0 !important; + text-align: left !important; + width: auto !important; +} + /** Story level */ amp-story { height: 100% !important; @@ -562,6 +576,7 @@ amp-story-grid-layer * { display: block !important; text-transform: capitalize !important; font-family: 'Roboto', sans-serif !important; + color: #fff !important; font-weight: 400 !important; line-height: 10px !important; font-size: 10px !important; @@ -922,13 +937,13 @@ amp-story[standalone].i-amphtml-story-landscape .i-amphtml-story-no-rotation-ove display: flex !important; } -.i-amphtml-story-no-rotation-overlay, -.i-amphtml-story-unsupported-browser-overlay { +amp-story .i-amphtml-story-no-rotation-overlay, +amp-story .i-amphtml-story-unsupported-browser-overlay { color: #fff!important; } -.i-amphtml-rotate-icon, -.i-amphtml-gear-icon { +amp-story .i-amphtml-rotate-icon, +amp-story .i-amphtml-gear-icon { background-repeat: no-repeat!important; background-position: center center!important; border-radius: 50%!important; @@ -939,14 +954,19 @@ amp-story[standalone].i-amphtml-story-landscape .i-amphtml-story-no-rotation-ove margin: 16px auto!important; } -.i-amphtml-rotate-icon { +amp-story .i-amphtml-rotate-icon { background-image: url('data:image/svg+xml;charset=utf-8,')!important; } -.i-amphtml-gear-icon { +amp-story .i-amphtml-gear-icon { background-image: url('data:image/svg+xml;charset=utf-8,')!important; } +amp-story .i-amphtml-story-overlay-text { + color: #fff !important; + font-weight: 700 !important; +} + .i-amphtml-story-button-container { display: none !important; } diff --git a/extensions/amp-story/0.1/amp-story.js b/extensions/amp-story/0.1/amp-story.js index 1cddf635822c..961345c6e4f2 100644 --- a/extensions/amp-story/0.1/amp-story.js +++ b/extensions/amp-story/0.1/amp-story.js @@ -128,7 +128,9 @@ const LANDSCAPE_OVERLAY_CLASS = 'i-amphtml-story-landscape'; const LANDSCAPE_ORIENTATION_WARNING = [ { tag: 'div', - attrs: dict({'class': 'i-amphtml-story-no-rotation-overlay'}), + attrs: dict({ + 'class': 'i-amphtml-story-no-rotation-overlay ' + + 'i-amphtml-story-system-reset'}), children: [ { tag: 'div', @@ -140,6 +142,7 @@ const LANDSCAPE_ORIENTATION_WARNING = [ }, { tag: 'div', + attrs: dict({'class': 'i-amphtml-story-overlay-text'}), text: 'The page is best viewed in portrait mode', }, ], @@ -164,6 +167,7 @@ const UNSUPPORTED_BROWSER_WARNING = [ }, { tag: 'div', + attrs: dict({'class': 'i-amphtml-story-overlay-text'}), text: 'We\'re sorry, it looks like your browser doesn\'t support ' + 'this experience', }, diff --git a/extensions/amp-story/0.1/bookend.js b/extensions/amp-story/0.1/bookend.js index 055597554f2e..90e81bdeabad 100644 --- a/extensions/amp-story/0.1/bookend.js +++ b/extensions/amp-story/0.1/bookend.js @@ -55,7 +55,9 @@ const HIDDEN_CLASSNAME = 'i-amphtml-hidden'; /** @private @const {!./simple-template.ElementDef} */ const ROOT_TEMPLATE = { tag: 'section', - attrs: dict({'class': `i-amphtml-story-bookend ${HIDDEN_CLASSNAME}`}), + attrs: dict({ + 'class': 'i-amphtml-story-bookend i-amphtml-story-system-reset ' + + HIDDEN_CLASSNAME}), children: [ // Overflow container that gets pushed to the bottom when content height is // smaller than viewport. diff --git a/extensions/amp-story/0.1/system-layer.js b/extensions/amp-story/0.1/system-layer.js index a0b8def1d56e..d2d8e891e117 100644 --- a/extensions/amp-story/0.1/system-layer.js +++ b/extensions/amp-story/0.1/system-layer.js @@ -31,7 +31,8 @@ const UNMUTE_CLASS = 'i-amphtml-story-unmute-audio-control'; /** @private @const {!./simple-template.ElementDef} */ const TEMPLATE = { tag: 'aside', - attrs: dict({'class': 'i-amphtml-story-system-layer'}), + attrs: dict( + {'class': 'i-amphtml-story-system-layer i-amphtml-story-system-reset'}), children: [ { tag: 'div', From 686abe4832d9c7b23a0ff758b90ad4cf17dd10a3 Mon Sep 17 00:00:00 2001 From: Raghu Simha Date: Mon, 5 Feb 2018 23:09:16 -0500 Subject: [PATCH 19/65] Make sure polyfills.js is imported before anything else (#13277) --- 3p/ampcontext-lib.js | 5 ++++- 3p/iframe-transport-client-lib.js | 5 ++++- 3p/integration.js | 4 +++- build-system/tasks/presubmit-checks.js | 1 + src/amp-shadow.js | 4 +++- src/amp.js | 4 +++- src/service.js | 5 +++-- 7 files changed, 21 insertions(+), 7 deletions(-) diff --git a/3p/ampcontext-lib.js b/3p/ampcontext-lib.js index d1588c8bc01a..e1b63dfbb4ed 100644 --- a/3p/ampcontext-lib.js +++ b/3p/ampcontext-lib.js @@ -13,7 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import './polyfills'; + +// src/polyfills.js must be the first import. +import './polyfills'; // eslint-disable-line sort-imports-es6-autofix/sort-imports-es6 + import {AmpContext} from './ampcontext.js'; import {initLogConstructor, setReportError} from '../src/log'; diff --git a/3p/iframe-transport-client-lib.js b/3p/iframe-transport-client-lib.js index 504c072f2a0f..7a635e4a717b 100644 --- a/3p/iframe-transport-client-lib.js +++ b/3p/iframe-transport-client-lib.js @@ -13,7 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import './polyfills'; + +// src/polyfills.js must be the first import. +import './polyfills'; // eslint-disable-line sort-imports-es6-autofix/sort-imports-es6 + import {IframeTransportClient} from './iframe-transport-client.js'; import {initLogConstructor, setReportError} from '../src/log'; diff --git a/3p/integration.js b/3p/integration.js index 24788c29ed0e..e09338ecec91 100644 --- a/3p/integration.js +++ b/3p/integration.js @@ -22,7 +22,9 @@ * https://3p.ampproject.net/$version/f.js */ -import './polyfills'; +// src/polyfills.js must be the first import. +import './polyfills'; // eslint-disable-line sort-imports-es6-autofix/sort-imports-es6 + import {AmpEvents} from '../src/amp-events'; import { IntegrationAmpContext, diff --git a/build-system/tasks/presubmit-checks.js b/build-system/tasks/presubmit-checks.js index e71b8e94b305..ab756776f00d 100644 --- a/build-system/tasks/presubmit-checks.js +++ b/build-system/tasks/presubmit-checks.js @@ -622,6 +622,7 @@ const forbiddenTerms = { message: 'Use a line-level "no-unused-vars" rule instead.', whitelist: [ 'viewer-api/swipe-api.js', + 'dist.3p/current/integration.js', ], }, }; diff --git a/src/amp-shadow.js b/src/amp-shadow.js index 5434ea80fb16..1bb2fed1d77c 100644 --- a/src/amp-shadow.js +++ b/src/amp-shadow.js @@ -19,7 +19,9 @@ * multiple AMP Docs in Shadow DOM. */ -import './polyfills'; +// src/polyfills.js must be the first import. +import './polyfills'; // eslint-disable-line sort-imports-es6-autofix/sort-imports-es6 + import {Services} from './services'; import { adoptShadowMode, diff --git a/src/amp.js b/src/amp.js index 73de81e7e683..46e61dad2e66 100644 --- a/src/amp.js +++ b/src/amp.js @@ -18,7 +18,9 @@ * The entry point for AMP Runtime (v0.js) when AMP Runtime = AMP Doc. */ -import './polyfills'; +// src/polyfills.js must be the first import. +import './polyfills'; // eslint-disable-line sort-imports-es6-autofix/sort-imports-es6 + import {Services} from './services'; import {adopt, installAmpdocServices, installBuiltins, installRuntimeServices} from './runtime'; import {cssText} from '../build/css'; diff --git a/src/service.js b/src/service.js index ffd6f57805d7..a2ccd632277e 100644 --- a/src/service.js +++ b/src/service.js @@ -20,8 +20,9 @@ * Invariant: Service getters never return null for registered services. */ -// Requires polyfills in immediate side effect. -import './polyfills'; +// src/polyfills.js must be the first import. +import './polyfills'; // eslint-disable-line sort-imports-es6-autofix/sort-imports-es6 + import {dev} from './log'; import {toWin} from './types'; From fe9925c7453bc717531e456c884f2d4cb6c237e2 Mon Sep 17 00:00:00 2001 From: Sriram Krishnan Date: Mon, 5 Feb 2018 20:24:14 -0800 Subject: [PATCH 20/65] Fix for broken snapping on IOS for slides (#13290) --- extensions/amp-carousel/0.1/amp-carousel.css | 8 ++++---- extensions/amp-carousel/0.1/slidescroll.js | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/extensions/amp-carousel/0.1/amp-carousel.css b/extensions/amp-carousel/0.1/amp-carousel.css index 8d838dd69e3a..928739644c26 100644 --- a/extensions/amp-carousel/0.1/amp-carousel.css +++ b/extensions/amp-carousel/0.1/amp-carousel.css @@ -104,7 +104,7 @@ amp-carousel .amp-carousel-button.amp-disabled { position: absolute !important; top: 0; width: 100% !important; - scroll-snap-type: mandatory !important; + scroll-snap-type: x mandatory !important; /** * Hide the scrollbar by tucking it under some padding * which gets cut off as it overflows the parent. @@ -131,7 +131,7 @@ amp-carousel .amp-carousel-button.amp-disabled { height: 100% !important; justify-content: center !important; position: relative !important; - scroll-snap-coordinate: 0 0 !important; + scroll-snap-align: center !important; width: 100% !important; } @@ -152,7 +152,7 @@ amp-carousel .amp-carousel-button.amp-disabled { flex: 0 0 1px !important; height: 100% !important; position: relative !important; - scroll-snap-coordinate: 0 0 !important; + scroll-snap-align: center !important; width: 1px !important; } @@ -171,7 +171,7 @@ amp-carousel .amp-carousel-button.amp-disabled { } .i-amphtml-slidescroll-no-snap .i-amphtml-slide-item { - scroll-snap-coordinate: none !important; + scroll-snap-align: none !important; } .i-amphtml-slidescroll-no-snap.i-amphtml-slides-container.i-amphtml-no-scroll { diff --git a/extensions/amp-carousel/0.1/slidescroll.js b/extensions/amp-carousel/0.1/slidescroll.js index f30650c7db84..535bf5e6ba33 100644 --- a/extensions/amp-carousel/0.1/slidescroll.js +++ b/extensions/amp-carousel/0.1/slidescroll.js @@ -33,13 +33,13 @@ import {triggerAnalyticsEvent} from '../../../src/analytics'; const SHOWN_CSS_CLASS = 'i-amphtml-slide-item-show'; /** @const {number} */ -const NATIVE_SNAP_TIMEOUT = 35; +const NATIVE_SNAP_TIMEOUT = 135; /** @const {number} */ const IOS_CUSTOM_SNAP_TIMEOUT = 45; /** @const {number} */ -const NATIVE_TOUCH_TIMEOUT = 120; +const NATIVE_TOUCH_TIMEOUT = 100; /** @const {number} */ const IOS_TOUCH_TIMEOUT = 45; @@ -350,6 +350,7 @@ export class AmpSlideScroll extends BaseSlides { } const currentScrollLeft = this.slidesContainer_./*OK*/scrollLeft; + if (!this.isIos_) { this.handleCustomElasticScroll_(currentScrollLeft); } @@ -359,7 +360,6 @@ export class AmpSlideScroll extends BaseSlides { this.isIos_ ? IOS_CUSTOM_SNAP_TIMEOUT : CUSTOM_SNAP_TIMEOUT); // Timer that detects scroll end and/or end of snap scroll. this.scrollTimeout_ = Services.timerFor(this.win).delay(() => { - if (this.snappingInProgress_) { return; } From 79c4969ef95e6065f98786b90fe665dd0abf1c12 Mon Sep 17 00:00:00 2001 From: Cathy Zhu Date: Mon, 5 Feb 2018 23:38:11 -0800 Subject: [PATCH 21/65] Increase button tap target size and ghost image on enter (#13278) * Increase button tap target size * Fix desktop mode top padding * tweak button padding to 16px per mulptiple of 4 * Reorder media queries --- .../0.1/amp-lightbox-gallery.css | 59 +++++++++++++------ .../0.1/amp-lightbox-gallery.js | 10 +++- 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/extensions/amp-lightbox-gallery/0.1/amp-lightbox-gallery.css b/extensions/amp-lightbox-gallery/0.1/amp-lightbox-gallery.css index 8d4453a4bbac..6f7953aa16c2 100644 --- a/extensions/amp-lightbox-gallery/0.1/amp-lightbox-gallery.css +++ b/extensions/amp-lightbox-gallery/0.1/amp-lightbox-gallery.css @@ -44,20 +44,32 @@ .i-amphtml-lbg-gallery { display: none; top: 0 !important; - padding-top: 50px !important; + padding-top: 56px !important; overflow: auto !important; } +@media (min-width: 1024px) { + .i-amphtml-lbg-gallery { + padding-top: 80px !important; + } +} + .i-amphtml-lbg-top-bar { position: absolute !important; left: 0 !important; right: 0 !important; top: 0 !important; - height: 50px !important; + height: 56px !important; background: linear-gradient(rgba(0,0,0,0.3), rgba(0,0,0,0)); z-index: 2; } +@media (min-width: 1024px) { + .i-amphtml-lbg-top-bar { + height: 100px !important; + } +} + .i-amphtml-lbg-controls { opacity: 0; animation: fadeIn ease-in 0.4s 1 forwards; @@ -74,7 +86,6 @@ left: 0 !important; right: 0 !important; top: 0 !important; - height: 50px !important; background: linear-gradient(rgba(0,0,0,0.3), rgba(0,0,0,0)); } @@ -128,18 +139,6 @@ .i-amphtml-lbg[gallery-view] .i-amphtml-lbg-gallery { grid-template-columns: repeat(4, calc(1024px/4 - 5px * 5 / 4)); } - - div.i-amphtml-lbg-top-bar { - height: 100px !important; - } - - div.amp-lbg-button-close, - div.amp-lbg-button-gallery, - div.amp-lbg-button-slide { - width: 40px; - height: 40px; - margin: 20px; - } } .i-amphtml-lbg[gallery-view] .i-amphtml-lbg-gallery.i-amphtml-lbg-gallery-hidden { @@ -161,14 +160,26 @@ } /* Controls */ -.amp-lbg-button-close, -.amp-lbg-button-gallery, -.amp-lbg-button-slide { +.amp-lbg-button { position: absolute !important; cursor: pointer; width: 24px; height: 24px; - margin: 13px; + padding: 16px; +} + +@media (min-width: 1024px) { + .amp-lbg-button { + width: 40px; + height: 40px; + padding: 20px; + } +} + +.amp-lbg-icon { + width: 100%; + height: 100%; + display: block; background-repeat: no-repeat; background-position: center center; } @@ -176,12 +187,19 @@ .amp-lbg-button-close { top: 0; right: 0; +} + +.amp-lbg-button-close .amp-lbg-icon { background-image: url('data:image/svg+xml;charset=utf-8,'); } + .amp-lbg-button-gallery { top: 0; left: 0; +} + +.amp-lbg-button-gallery .amp-lbg-icon { background-image: url('data:image/svg+xml;charset=utf-8,'); } @@ -189,6 +207,9 @@ top: 0; left: 0; display: none; +} + +.amp-lbg-button-slide .amp-lbg-icon { background-image: url('data:image/svg+xml;charset=utf-8,'); } diff --git a/extensions/amp-lightbox-gallery/0.1/amp-lightbox-gallery.js b/extensions/amp-lightbox-gallery/0.1/amp-lightbox-gallery.js index 0870ae8d06b6..0c1f0cd4f7de 100644 --- a/extensions/amp-lightbox-gallery/0.1/amp-lightbox-gallery.js +++ b/extensions/amp-lightbox-gallery/0.1/amp-lightbox-gallery.js @@ -475,10 +475,16 @@ export class AmpLightboxGallery extends AMP.BaseElement { buildButton_(label, className, action) { dev().assert(this.topBar_); const button = this.win.document.createElement('div'); - button.setAttribute('role', 'button'); button.setAttribute('aria-label', label); + + const icon = this.win.document.createElement('span'); + icon.classList.add('amp-lbg-icon'); + button.appendChild(icon); button.classList.add(className); + button.classList.add('amp-lbg-button'); + + button.addEventListener('click', event => { action(); event.stopPropagation(); @@ -721,6 +727,7 @@ export class AmpLightboxGallery extends AMP.BaseElement { && this.shouldAnimate_(sourceElement)) { // TODO (#13039): implement crop and object fit contain transitions + sourceElement.classList.add('i-amphtml-ghost'); transLayer = this.element.ownerDocument.createElement('div'); transLayer.classList.add('i-amphtml-lightbox-gallery-trans'); this.element.ownerDocument.body.appendChild(transLayer); @@ -816,7 +823,6 @@ export class AmpLightboxGallery extends AMP.BaseElement { transLayer = this.element.ownerDocument.createElement('div'); transLayer.classList.add('i-amphtml-lightbox-viewer-trans'); this.element.ownerDocument.body.appendChild(transLayer); - sourceElement.classList.add('i-amphtml-ghost'); const rect = layoutRectFromDomRect(sourceElement ./*OK*/getBoundingClientRect()); From 64d4ab3559d4cdf10bde92d7943eb67a8041392c Mon Sep 17 00:00:00 2001 From: Barb Paduch Date: Tue, 6 Feb 2018 08:12:16 -0500 Subject: [PATCH 22/65] Clean up wording in Media Session API section (#13273) --- extensions/amp-video/amp-video.md | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/extensions/amp-video/amp-video.md b/extensions/amp-video/amp-video.md index 1f74872c8219..948b61dd0fb2 100644 --- a/extensions/amp-video/amp-video.md +++ b/extensions/amp-video/amp-video.md @@ -116,42 +116,38 @@ The `muted` attribute is deprecated and no longer has any effect. The `autoplay` This element includes [common attributes](https://www.ampproject.org/docs/reference/common_attributes) extended to AMP components. +## Media Session API attributes -## Media Session API Attributes - -`amp-video` implements the [Media Session API](https://developers.google.com/web/updates/2017/02/media-session) enabling developers to specify more information about the video file that is playing to be displayed in the notification center of user's devices (along with play/pause controls). +The `amp-video` component implements the [Media Session API](https://developers.google.com/web/updates/2017/02/media-session), which enables developers to specify more information about the video file. The additional information for the video displays in the notification center of the user's device (along with the play/pause controls). ##### artwork -URL to a PNG/JPG/ICO image serving as the video's artwork. If not present, the MediaSessionAPI Helper will use either the `image` field in the `schema.org` definition, the `og:image` or the website's `favicon`. +Specifies a URL to a PNG/JPG/ICO image serving as the video's artwork. If `artwork` is not present, the Media Session API helper uses either the `image` field in the `schema.org` definition, the `og:image`, or the website's `favicon`. ##### artist -(string) indicates the author of the video file +Indicates the author of the video file, specified as a string. ##### album -(string) indicates the album/collection the video was taken from +Indicates the album/collection the video was taken from, specified as a string. ##### title -(string) part of the [common attributes](https://www.ampproject.org/docs/reference/common_attributes), doubles as the video's name/title displayed in the MediaSession notification. If not provided, the MediaSessionAPI Helper will use either the `aria-label` attribute or fall back to the page's title. +Indicates the name/title of the video, specified as a string. If not provided, the Media Session API helper uses either the `aria-label` attribute or falls back to the page's title. Example: -Note that this example has both the `poster` and `artwork` attributes, poster will be used as the -placeholder before the video plays while `artwork` is the image that will be displayed in the -notification throught the MediaSessionAPI. +This example contains both the `poster` and `artwork` attributes. The `poster` serves as the placeholder image before the video plays, while `artwork` is the image that displays in the notification via the MediaSession API. ```html - - - + ``` ## Click-to-Play overlay From 88d14b93e053818a3a6ed67d21b9cdf1dd2e93a6 Mon Sep 17 00:00:00 2001 From: Raghu Simha Date: Tue, 6 Feb 2018 10:37:48 -0500 Subject: [PATCH 23/65] Fix 'gulp test --a4a' and remove '--randomize' (#13292) --- build-system/tasks/runtime-test.js | 38 +-------- package.json | 3 - test/functional/TEST_FAILURE_AND_CRASH.json | 1 - test/functional/TEST_FAILURE_BLOB2.json | 1 - test/functional/TEST_FAILURE_BLOBS.json | 1 - yarn.lock | 91 +-------------------- 6 files changed, 7 insertions(+), 128 deletions(-) delete mode 100644 test/functional/TEST_FAILURE_AND_CRASH.json delete mode 100644 test/functional/TEST_FAILURE_BLOB2.json delete mode 100644 test/functional/TEST_FAILURE_BLOBS.json diff --git a/build-system/tasks/runtime-test.js b/build-system/tasks/runtime-test.js index 9657f96e35b8..d6568d27631b 100644 --- a/build-system/tasks/runtime-test.js +++ b/build-system/tasks/runtime-test.js @@ -21,14 +21,12 @@ const argv = require('minimist')(process.argv.slice(2)); const colors = require('ansi-colors'); const config = require('../config'); const fs = require('fs'); -const glob = require('glob'); const gulp = require('gulp-help')(require('gulp')); const Karma = require('karma').Server; const karmaDefault = require('./karma.conf'); const log = require('fancy-log'); const path = require('path'); const removeConfig = require('./prepend-global/index.js').removeConfig; -const shuffleSeed = require('shuffle-seed'); const webserver = require('gulp-webserver'); @@ -37,7 +35,8 @@ const yellow = colors.yellow; const cyan = colors.cyan; const red = colors.red; -const preTestTasks = argv.nobuild ? [] : (argv.unit ? ['css'] : ['build']); +const preTestTasks = + argv.nobuild ? [] : ((argv.unit || argv.a4a) ? ['css'] : ['build']); const ampConfig = (argv.config === 'canary') ? 'canary' : 'prod'; @@ -146,9 +145,7 @@ function printArgvMessages() { cyan('gulp build') + ' to have been run first.', unit: 'Running only the unit tests. Requires ' + cyan('gulp css') + ' to have been run first.', - randomize: 'Randomizing the order in which tests are run.', a4a: 'Running only A4A tests.', - seed: 'Randomizing test order with seed ' + cyan(argv.seed) + '.', compiled: 'Running tests against minified code.', grep: 'Only running tests that match the pattern "' + cyan(argv.grep) + '".', @@ -242,30 +239,8 @@ function runTests() { } else { c.files = c.files.concat(config.unitTestPaths); } - - } else if (argv.randomize || argv.glob || argv.a4a) { - const testPaths = argv.a4a ? config.a4aTestPaths : config.basicTestPaths; - - let testFiles = []; - for (const index in testPaths) { - testFiles = testFiles.concat(glob.sync(testPaths[index])); - } - - if (argv.randomize || argv.a4a) { - const seed = argv.seed || Math.random(); - log( - yellow('Randomizing:'), - cyan('Seeding with value', seed)); - log( - yellow('To rerun same ordering, append'), - cyan(`--seed=${seed}`), - yellow('to your invocation of'), - cyan('gulp test')); - testFiles = shuffleSeed.shuffle(testFiles, seed); - } - - testFiles.splice(testFiles.indexOf('test/_init_tests.js'), 1); - c.files = c.files.concat(config.commonTestPaths.concat(testFiles)); + } else if (argv.a4a) { + c.files = c.files.concat(config.commonTestPaths, config.a4aTestPaths); } else { c.files = c.files.concat(config.testPaths); } @@ -412,11 +387,6 @@ gulp.task('test', 'Runs tests', preTestTasks, function() { 'binaries for execution', 'grep': ' Runs tests that match the pattern', 'files': ' Runs tests for specific files', - 'randomize': ' Runs entire test suite in random order', - 'seed': ' Seeds the test order randomization. Use with --randomize ' + - 'or --a4a', - 'glob': ' Explicitly expands test paths using glob before passing ' + - 'to Karma', 'nohelp': ' Silence help messages that are printed prior to test run', 'a4a': ' Runs all A4A tests', 'config': ' Sets the runtime\'s AMP config to one of "prod" or "canary"', diff --git a/package.json b/package.json index ae0e4f78cca9..6f5a46bc9773 100644 --- a/package.json +++ b/package.json @@ -76,10 +76,8 @@ "express": "4.16.2", "fancy-log": "1.3.2", "fetch-mock": "5.13.1", - "file-reader": "1.1.1", "formidable": "1.1.1", "fs-extra": "5.0.0", - "glob": "7.1.2", "gulp": "3.9.1", "gulp-ava": "0.18.0", "gulp-batch-replace": "0.0.0", @@ -145,7 +143,6 @@ "request": "2.83.0", "rimraf": "2.6.2", "rocambole": "0.7.0", - "shuffle-seed": "1.1.6", "sinon": "4.1.3", "sinon-chai": "2.14.0", "text-table": "0.2.0", diff --git a/test/functional/TEST_FAILURE_AND_CRASH.json b/test/functional/TEST_FAILURE_AND_CRASH.json deleted file mode 100644 index e85389c09d8c..000000000000 --- a/test/functional/TEST_FAILURE_AND_CRASH.json +++ /dev/null @@ -1 +0,0 @@ -["test/_init_tests.js","test/fixtures/*.html",{"pattern":"test/fixtures/served/*.html","included":false,"nocache":false,"watched":true},{"pattern":"dist/**/*.js","included":false,"nocache":false,"watched":true},{"pattern":"dist.tools/**/*.js","included":false,"nocache":false,"watched":true},{"pattern":"examples/**/*","included":false,"nocache":false,"watched":true},{"pattern":"dist.3p/**/*","included":false,"nocache":false,"watched":true},"extensions/amp-fresh/0.1/test/test-amp-fresh.js","extensions/amp-auto-ads/0.1/test/test-attributes.js","extensions/amp-ad-network-doubleclick-impl/0.1/test/test-amp-ad-network-doubleclick-impl.js","extensions/amp-call-tracking/0.1/test/test-amp-call-tracking.js","test/functional/web-worker/test-amp-worker.js","test/integration/test-visibility-states.js","extensions/amp-ad/0.1/test/test-amp-ad.js","test/functional/test-document-state.js","test/functional/test-extensions.js","extensions/amp-google-vrview-image/0.1/test/test-amp-google-vrview-image.js","extensions/amp-form/0.1/test/test-form-proxy.js","test/functional/test-iframe-helper.js","extensions/amp-springboard-player/0.1/test/test-amp-springboard-player.js","extensions/amp-ad-network-triplelift-impl/0.1/test/test-amp-ad-network-triplelift-impl.js","extensions/amp-selector/0.1/test/test-amp-selector.js","extensions/amp-auto-ads/0.1/test/test-amp-auto-ads.js","extensions/amp-ad-network-cloudflare-impl/0.1/test/test-amp-ad-network-cloudflare-impl.js","test/integration/test-amp-img.js","test/functional/inabox/test-inabox-messaging-host.js","extensions/amp-o2-player/0.1/test/test-amp-o2-player.js","test/functional/test-error.js","extensions/amp-auto-ads/0.1/test/test-placement.js","test/functional/utils/test-bytes.js","test/functional/test-document-submit.js","test/functional/test-pull-to-refresh.js","test/functional/test-ampdoc.js","test/functional/test-cache-sw-core.js","test/functional/test-polyfill-array-includes.js","extensions/amp-animation/0.1/test/test-amp-animation.js","extensions/amp-viewer-integration/0.1/test/test-amp-viewer-integration.js","extensions/amp-sticky-ad/1.0/test/test-amp-sticky-ad.js","extensions/amp-dynamic-css-classes/0.1/test/test-dynamic-classes.js","extensions/amp-kaltura-player/0.1/test/test-amp-kaltura-player.js","extensions/amp-a4a/0.1/test/test-a4a-integration.js","test/functional/test-base-element.js","extensions/amp-analytics/0.1/test/test-events.js","test/functional/test-template.js","test/integration/test-amp-ad-3p.js","test/functional/test-iframe-stub.js","test/integration/test-released.js","test/functional/test-input.js","test/functional/test-action.js","extensions/amp-ad/0.1/test/test-amp-ad-custom.js","extensions/amp-form/0.1/test/test-amp-form.js","test/functional/test-motion.js","test/functional/3p/test-iframe-messaging-client.js","test/functional/test-platform.js","test/functional/test-video-manager.js","extensions/amp-fresh/0.1/test/test-amp-fresh-manager.js","extensions/amp-analytics/0.1/test/test-variables.js","extensions/amp-analytics/0.1/test/test-vendors.js","extensions/amp-pinterest/0.1/test/test-amp-pinterest.js","test/functional/test-amp-img.js","test/functional/test-anchor-click-interceptor.js","extensions/amp-vine/0.1/test/test-amp-vine.js","extensions/amp-ad/0.1/test/test-a2a-listener.js","test/functional/test-layout-rect.js","extensions/amp-vimeo/0.1/test/test-amp-vimeo.js","extensions/amp-bind/0.1/test/test-bind-integration.js","test/functional/test-ad-helper.js","test/functional/test-intersection-observer.js","test/functional/test-analytics.js","test/functional/test-history.js","extensions/amp-auto-ads/0.1/test/test-ad-tracker.js","test/functional/test-render-delaying-services.js","test/functional/test-shadow-embed.js","test/functional/test-runtime.js","test/functional/test-font-stylesheet-timeout.js","extensions/amp-audio/0.1/test/test-amp-audio.js","extensions/amp-viewer-integration/0.1/test/viewer-initiated-handshake-viewer-for-testing.js","extensions/amp-mustache/0.1/test/test-amp-mustache.js","test/functional/utils/test-base64.js","extensions/amp-auto-ads/0.1/test/test-ad-strategy.js","test/functional/test-web-components.js","ads/google/a4a/test/test-utils.js","test/functional/test-ads-config.js","test/functional/test-batched-xhr.js","extensions/amp-instagram/0.1/test/test-amp-instagram.js","test/functional/test-resource.js","test/functional/test-variable-source.js","test/functional/test-url-replacements.js","test/functional/test-event-helper.js","extensions/amp-user-notification/0.1/test/test-amp-user-notification.js","extensions/amp-experiment/0.1/test/test-amp-experiment.js","test/integration/test-video-players.js","test/functional/test-storage.js","extensions/amp-app-banner/0.1/test/test-amp-app-banner.js","extensions/amp-izlesene/0.1/test/test-amp-izlesene.js","extensions/amp-bind/0.1/test/test-bind-impl.js","test/functional/test-dom.js","extensions/amp-share-tracking/0.1/test/test-amp-share-tracking.js","test/functional/test-alp-handler.js","test/functional/test-impression.js","extensions/amp-sidebar/0.1/test/test-amp-sidebar.js","extensions/amp-viewer-integration/0.1/test/test-amp-webview-viewer-integration.js","extensions/amp-analytics/0.1/test/test-analytics-root.js","extensions/amp-nexxtv-player/0.1/test/test-amp-nexxtv-player.js","extensions/amp-anim/0.1/test/test-amp-anim.js","extensions/amp-ad/0.1/test/test-amp-ad-ui.js","extensions/amp-form/0.1/test/integration/test-integration-form.js","extensions/amp-fx-parallax/0.1/test/test-amp-fx-parallax.js","extensions/amp-font/0.1/test/test-amp-font.js","extensions/amp-form/0.1/test/test-validation-bubble.js","extensions/amp-a4a/0.1/test/test-a4a-var-source.js","extensions/amp-a4a/0.1/test/utils.js","extensions/amp-viewer-integration/0.1/test/viewer-for-testing.js","test/functional/test-3p-nameframe.js","extensions/amp-lightbox-viewer/0.1/test/test-amp-lightbox-viewer.js","test/functional/inabox/test-inabox-viewport.js","test/functional/test-intersection-observer-polyfill.js","extensions/amp-youtube/0.1/test/test-amp-youtube.js","extensions/amp-ad-network-adsense-impl/0.1/test/test-amp-ad-network-adsense-impl.js","test/functional/test-layout.js","test/functional/test-performance.js","test/functional/test-polyfill-document-contains.js","extensions/amp-brid-player/0.1/test/test-amp-brid-player.js","test/integration/test-video-players-helper.js","test/functional/test-cid.js","extensions/amp-playbuzz/0.1/test/test-amp-playbuzz.js","extensions/amp-carousel/0.1/test/test-scrollable-carousel.js","extensions/amp-access-laterpay/0.1/test/test-amp-access-laterpay.js","test/functional/test-log.js","test/functional/utils/test-pem.js","test/functional/test-viewer.js","extensions/amp-a4a/0.1/test/testdata/test_fragments.js","extensions/amp-bind/0.1/test/test-bind-evaluator.js","extensions/amp-live-list/0.1/test/test-poller.js","extensions/amp-live-list/0.1/test/test-amp-live-list.js","test/functional/test-standard-actions.js","test/functional/test-cookies.js","test/functional/test-ios-scrollfreeze-bug.js","extensions/amp-experiment/0.1/test/test-variant.js","test/functional/test-exponential-backoff.js","test/functional/test-sanitizer.js","extensions/amp-hulu/0.1/test/test-amp-hulu.js","test/functional/test-types.js","test/functional/test-3p-environment.js","extensions/amp-ad/0.1/test/test-layout-delay-meter.js","test/functional/test-ie-media-bug.js","test/functional/test-custom-element.js","extensions/amp-ad/0.1/test/test-amp-ad-3p-impl.js","test/functional/inabox/test-position-observer.js","extensions/amp-ad-network-doubleclick-impl/0.1/test/test-doubleclick-integration.js","test/integration/test-errors.js","ads/google/a4a/test/test-traffic-experiments.js","extensions/amp-font/0.1/test/test-fontloader.js","test/functional/test-mode.js","_WONTMATCH.qqq","extensions/amp-apester-media/0.1/test/test-amp-apester-media.js","test/functional/test-polyfill-object-assign.js","ads/google/a4a/test/test-performance.js","extensions/amp-facebook/0.1/test/test-amp-facebook.js","test/functional/test-3p.js","extensions/amp-list/0.1/test/test-amp-list.js","extensions/amp-a4a/0.1/test/testdata/valid_css_at_rules_amp.reserialized.js","test/functional/test-url.js","test/functional/test-document-ready.js","test/functional/utils/test-array.js","test/functional/test-mustache.js","test/functional/test-xhr.js","extensions/amp-reddit/0.1/test/test-amp-reddit.js","test/functional/test-service.js","test/functional/utils/test-rate-limit.js","extensions/amp-analytics/0.1/test/test-transport.js","test/integration/test-example-validation.js","test/integration/test-boilerplates.js","extensions/amp-ooyala-player/0.1/test/test-amp-ooyala.js","extensions/amp-sortable-table/0.1/test/test-amp-sortable-table.js","test/functional/test-gesture-recognizers.js","extensions/amp-access/0.1/test/test-amp-access.js","ads/google/a4a/test/test-google-data-reporter.js","test/functional/test-amp-pixel.js","test/functional/ads/test-csa.js","test/functional/test-finite-state-machine.js","extensions/amp-dynamic-css-classes/0.1/test/test-runtime-classes.js","test/integration/test-amp-carousel.js","test/functional/test-size-list.js","test/functional/test-extension-location.js","extensions/amp-accordion/0.1/test/test-amp-accordion.js","test/functional/test-timer.js","test/functional/test-task-queue.js","test/functional/test-resources.js","test/functional/test-viewport.js","extensions/amp-viewer-integration/0.1/test/test-viewer-initiated-handshake.js","test/functional/test-preconnect.js","extensions/amp-fit-text/0.1/test/test-amp-fit-text.js","test/functional/test-get-html.js","extensions/amp-jwplayer/0.1/test/test-amp-jwplayer.js","extensions/amp-brightcove/0.1/test/test-amp-brightcove.js","test/functional/test-style.js","extensions/amp-image-lightbox/0.1/test/test-amp-image-lightbox.js","extensions/amp-access/0.1/test/test-amp-access-server.js","test/functional/test-pass.js","test/functional/test-style-installer.js","test/functional/test-srcset.js","extensions/amp-ad-network-doubleclick-impl/0.1/test/test-doubleclick-a4a-config.js","extensions/amp-bind/0.1/test/test-bind-validator.js","test/functional/test-crypto.js","extensions/amp-viewer-integration/0.1/test/test-touch-handler.js","test/functional/utils/test-signals.js","test/functional/test-integration.js","extensions/amp-social-share/0.1/test/test-amp-social-share.js","extensions/amp-bind/0.1/test/test-bind-expression.js","test/functional/test-document-click.js","extensions/amp-animation/0.1/test/test-web-animations.js","test/functional/test-3p-frame.js","extensions/amp-analytics/0.1/test/test-visibility-impl.js","test/functional/test-json.js","extensions/amp-access/0.1/test/test-amp-access-client.js","extensions/amp-access/0.1/test/test-signin.js","extensions/amp-analytics/0.1/test/test-visibility-model.js","test/functional/test-object.js","extensions/amp-gfycat/0.1/test/test-amp-gfycat.js","test/functional/test-focus-history.js","extensions/amp-fx-flying-carpet/0.1/test/test-amp-fx-flying-carpet.js","extensions/amp-carousel/0.1/test/test-base-slide.js","extensions/amp-soundcloud/0.1/test/test-amp-soundcloud.js","extensions/amp-analytics/0.1/test/test-instrumentation.js","test/functional/test-chunk.js","test/integration/test-extensions-loading.js","extensions/amp-install-serviceworker/0.1/test/test-amp-install-serviceworker.js","test/functional/test-animation.js","test/functional/utils/test-dom-fingerprint.js","test/functional/test-gesture.js","test/functional/test-vsync.js","test/functional/test-polyfill-domtokenlist-toggle.js","test/functional/test-ad-cid.js","extensions/amp-access/0.1/test/test-access-expr.js","test/integration/test-configuration.js","test/functional/test-transition.js","extensions/amp-auto-ads/0.1/test/test-ad-network-config.js","test/functional/3p/test-3p-messaging.js","test/functional/test-observable.js","extensions/amp-video/0.1/test/test-amp-video.js","extensions/amp-reach-player/0.1/test/test-amp-reach-player.js","extensions/amp-dailymotion/0.1/test/test-amp-dailymotion.js","extensions/amp-access/0.1/test/test-amp-access-other.js","extensions/amp-access/0.1/test/test-jwt.js","test/functional/test-fixed-layer.js","extensions/amp-carousel/0.1/test/test-slidescroll.js","test/functional/test-experiments.js","test/integration/test-amp-ad-doubleclick.js","test/functional/test-polyfill-math-sign.js","test/functional/test-curve.js","extensions/amp-iframe/0.1/test/test-amp-iframe.js","extensions/amp-sticky-ad/0.1/test/test-amp-sticky-ad.js","extensions/amp-a4a/0.1/test/test-amp-a4a.js","extensions/amp-analytics/0.1/test/test-visibility-manager.js","extensions/amp-form/0.1/test/test-form-validators.js","test/functional/test-element-stub.js","test/functional/test-friendly-iframe-embed.js","extensions/amp-access/0.1/test/test-amp-login-done-dialog.js","extensions/amp-viewer-integration/0.1/test/webview-viewer-for-testing.js","extensions/amp-access/0.1/test/test-amp-access-server-jwt.js","test/functional/utils/test-priority-queue.js","test/functional/test-document-info.js","extensions/amp-twitter/0.1/test/test-amp-twitter.js","extensions/amp-ad/0.1/test/test-amp-ad-xorigin-iframe-handler.js","extensions/amp-access/0.1/test/test-amp-access-vendor.js","test/functional/test-string.js","test/functional/test-activity.js","test/manual/test-sw.js","extensions/amp-access/0.1/test/test-login-dialog.js","ads/google/a4a/test/test-google-ads-a4a-config.js","extensions/amp-analytics/0.1/test/test-amp-analytics.js","extensions/amp-live-list/0.1/test/test-live-list-manager.js"] diff --git a/test/functional/TEST_FAILURE_BLOB2.json b/test/functional/TEST_FAILURE_BLOB2.json deleted file mode 100644 index 583bc04aaf82..000000000000 --- a/test/functional/TEST_FAILURE_BLOB2.json +++ /dev/null @@ -1 +0,0 @@ -["test/_init_tests.js","test/fixtures/*.html",{"pattern":"test/fixtures/served/*.html","included":false,"nocache":false,"watched":true},{"pattern":"dist/**/*.js","included":false,"nocache":false,"watched":true},{"pattern":"dist.tools/**/*.js","included":false,"nocache":false,"watched":true},{"pattern":"examples/**/*","included":false,"nocache":false,"watched":true},{"pattern":"dist.3p/**/*","included":false,"nocache":false,"watched":true},"extensions/amp-ad-network-doubleclick-impl/0.1/test/test-amp-ad-network-doubleclick-impl.js","extensions/amp-fresh/0.1/test/test-amp-fresh.js","extensions/amp-form/0.1/test/test-form-validators.js","test/functional/test-vsync.js","extensions/amp-animation/0.1/test/test-amp-animation.js","test/functional/test-viewport.js","test/functional/utils/test-dom-fingerprint.js","test/integration/test-released.js","extensions/amp-o2-player/0.1/test/test-amp-o2-player.js","extensions/amp-ooyala-player/0.1/test/test-amp-ooyala.js","test/functional/inabox/test-inabox-messaging-host.js","extensions/amp-carousel/0.1/test/test-base-slide.js","extensions/amp-auto-ads/0.1/test/test-ad-network-config.js","test/functional/utils/test-rate-limit.js","test/functional/test-web-components.js","extensions/amp-access/0.1/test/test-amp-access.js","test/functional/test-friendly-iframe-embed.js","extensions/amp-carousel/0.1/test/test-slidescroll.js","test/integration/test-amp-ad-doubleclick.js","extensions/amp-fresh/0.1/test/test-amp-fresh-manager.js","extensions/amp-iframe/0.1/test/test-amp-iframe.js","test/functional/test-ads-config.js","extensions/amp-analytics/0.1/test/test-visibility-model.js","extensions/amp-pinterest/0.1/test/test-amp-pinterest.js","extensions/amp-access/0.1/test/test-amp-login-done-dialog.js","test/functional/test-render-delaying-services.js","extensions/amp-instagram/0.1/test/test-amp-instagram.js","test/functional/test-extension-location.js","extensions/amp-viewer-integration/0.1/test/viewer-initiated-handshake-viewer-for-testing.js","ads/google/a4a/test/test-google-ads-a4a-config.js","extensions/amp-auto-ads/0.1/test/test-amp-auto-ads.js","extensions/amp-analytics/0.1/test/test-analytics-root.js","extensions/amp-apester-media/0.1/test/test-amp-apester-media.js","test/functional/test-pull-to-refresh.js","test/integration/test-visibility-states.js","extensions/amp-fit-text/0.1/test/test-amp-fit-text.js","extensions/amp-ad/0.1/test/test-amp-ad-custom.js","extensions/amp-access/0.1/test/test-access-expr.js","test/functional/test-intersection-observer-polyfill.js","extensions/amp-access/0.1/test/test-jwt.js","extensions/amp-app-banner/0.1/test/test-amp-app-banner.js","test/integration/test-amp-carousel.js","test/functional/test-resource.js","test/functional/test-curve.js","test/functional/test-custom-element.js","ads/google/a4a/test/test-utils.js","test/functional/test-timer.js","test/functional/test-variable-source.js","extensions/amp-font/0.1/test/test-amp-font.js","test/functional/test-preconnect.js","extensions/amp-viewer-integration/0.1/test/test-amp-viewer-integration.js","test/integration/test-boilerplates.js","test/functional/test-document-state.js","extensions/amp-viewer-integration/0.1/test/test-amp-webview-viewer-integration.js","extensions/amp-auto-ads/0.1/test/test-attributes.js","extensions/amp-access/0.1/test/test-signin.js","test/integration/test-video-players-helper.js","test/functional/test-polyfill-domtokenlist-toggle.js","test/functional/test-platform.js","extensions/amp-kaltura-player/0.1/test/test-amp-kaltura-player.js","extensions/amp-viewer-integration/0.1/test/webview-viewer-for-testing.js","extensions/amp-sticky-ad/0.1/test/test-amp-sticky-ad.js","extensions/amp-access/0.1/test/test-amp-access-client.js","extensions/amp-access-laterpay/0.1/test/test-amp-access-laterpay.js","extensions/amp-youtube/0.1/test/test-amp-youtube.js","extensions/amp-ad-network-triplelift-impl/0.1/test/test-amp-ad-network-triplelift-impl.js","extensions/amp-analytics/0.1/test/test-visibility-manager.js","test/functional/test-3p-frame.js","extensions/amp-access/0.1/test/test-amp-access-vendor.js","extensions/amp-ad/0.1/test/test-amp-ad-ui.js","extensions/amp-analytics/0.1/test/test-amp-analytics.js","test/functional/test-transition.js","test/functional/test-document-info.js","test/functional/test-intersection-observer.js","test/functional/test-size-list.js","extensions/amp-sticky-ad/1.0/test/test-amp-sticky-ad.js","test/integration/test-amp-ad-3p.js","extensions/amp-sortable-table/0.1/test/test-amp-sortable-table.js","test/manual/test-sw.js","test/functional/test-3p.js","extensions/amp-hulu/0.1/test/test-amp-hulu.js","test/functional/test-document-ready.js","test/functional/test-viewer.js","test/functional/test-finite-state-machine.js","test/functional/test-iframe-helper.js","extensions/amp-social-share/0.1/test/test-amp-social-share.js","test/functional/test-extensions.js","test/functional/test-observable.js","test/functional/test-3p-nameframe.js","test/functional/test-focus-history.js","extensions/amp-dynamic-css-classes/0.1/test/test-dynamic-classes.js","extensions/amp-auto-ads/0.1/test/test-ad-tracker.js","test/functional/test-runtime.js","extensions/amp-access/0.1/test/test-login-dialog.js","extensions/amp-experiment/0.1/test/test-variant.js","extensions/amp-gfycat/0.1/test/test-amp-gfycat.js","extensions/amp-call-tracking/0.1/test/test-amp-call-tracking.js","extensions/amp-fx-parallax/0.1/test/test-amp-fx-parallax.js","extensions/amp-twitter/0.1/test/test-amp-twitter.js","extensions/amp-form/0.1/test/test-validation-bubble.js","extensions/amp-share-tracking/0.1/test/test-amp-share-tracking.js","extensions/amp-form/0.1/test/integration/test-integration-form.js","extensions/amp-ad-network-adsense-impl/0.1/test/test-amp-ad-network-adsense-impl.js","test/functional/test-style.js","test/functional/test-document-click.js","extensions/amp-analytics/0.1/test/test-instrumentation.js","extensions/amp-viewer-integration/0.1/test/test-viewer-initiated-handshake.js","test/functional/test-amp-img.js","test/integration/test-errors.js","test/functional/test-polyfill-object-assign.js","test/functional/test-animation.js","test/functional/test-resources.js","test/functional/test-anchor-click-interceptor.js","test/functional/test-url-replacements.js","test/functional/test-history.js","extensions/amp-selector/0.1/test/test-amp-selector.js","extensions/amp-a4a/0.1/test/utils.js","test/functional/test-motion.js","extensions/amp-bind/0.1/test/test-bind-expression.js","test/functional/test-pass.js","extensions/amp-ad-network-doubleclick-impl/0.1/test/test-doubleclick-integration.js","test/functional/test-get-html.js","test/functional/test-document-submit.js","extensions/amp-image-lightbox/0.1/test/test-amp-image-lightbox.js","test/functional/test-dom.js","extensions/amp-playbuzz/0.1/test/test-amp-playbuzz.js","extensions/amp-video/0.1/test/test-amp-video.js","test/functional/test-analytics.js","test/functional/utils/test-priority-queue.js","extensions/amp-ad/0.1/test/test-amp-ad-xorigin-iframe-handler.js","extensions/amp-fx-flying-carpet/0.1/test/test-amp-fx-flying-carpet.js","test/functional/test-gesture.js","test/functional/test-gesture-recognizers.js","extensions/amp-reddit/0.1/test/test-amp-reddit.js","extensions/amp-vine/0.1/test/test-amp-vine.js","test/functional/test-fixed-layer.js","extensions/amp-soundcloud/0.1/test/test-amp-soundcloud.js","test/functional/test-cookies.js","test/functional/test-experiments.js","extensions/amp-analytics/0.1/test/test-vendors.js","extensions/amp-audio/0.1/test/test-amp-audio.js","extensions/amp-live-list/0.1/test/test-amp-live-list.js","test/functional/test-xhr.js","test/functional/test-layout-rect.js","test/functional/inabox/test-inabox-viewport.js","test/functional/test-impression.js","extensions/amp-a4a/0.1/test/test-a4a-var-source.js","extensions/amp-mustache/0.1/test/test-amp-mustache.js","extensions/amp-ad/0.1/test/test-amp-ad.js","test/functional/test-activity.js","test/functional/test-layout.js","extensions/amp-font/0.1/test/test-fontloader.js","extensions/amp-viewer-integration/0.1/test/viewer-for-testing.js","test/functional/test-sanitizer.js","_WONTMATCH.qqq","extensions/amp-bind/0.1/test/test-bind-integration.js","extensions/amp-a4a/0.1/test/test-amp-a4a.js","test/functional/test-polyfill-math-sign.js","extensions/amp-brightcove/0.1/test/test-amp-brightcove.js","test/functional/test-chunk.js","extensions/amp-ad/0.1/test/test-a2a-listener.js","extensions/amp-live-list/0.1/test/test-live-list-manager.js","extensions/amp-a4a/0.1/test/testdata/valid_css_at_rules_amp.reserialized.js","extensions/amp-access/0.1/test/test-amp-access-other.js","test/functional/test-ie-media-bug.js","test/functional/test-object.js","extensions/amp-dynamic-css-classes/0.1/test/test-runtime-classes.js","extensions/amp-viewer-integration/0.1/test/test-touch-handler.js","test/functional/test-error.js","test/functional/test-storage.js","test/integration/test-amp-img.js","extensions/amp-lightbox-viewer/0.1/test/test-amp-lightbox-viewer.js","extensions/amp-dailymotion/0.1/test/test-amp-dailymotion.js","test/functional/test-performance.js","test/functional/test-batched-xhr.js","test/functional/utils/test-array.js","extensions/amp-vimeo/0.1/test/test-amp-vimeo.js","extensions/amp-access/0.1/test/test-amp-access-server.js","extensions/amp-bind/0.1/test/test-bind-validator.js","extensions/amp-carousel/0.1/test/test-scrollable-carousel.js","test/functional/web-worker/test-amp-worker.js","extensions/amp-nexxtv-player/0.1/test/test-amp-nexxtv-player.js","extensions/amp-ad/0.1/test/test-layout-delay-meter.js","test/integration/test-configuration.js","extensions/amp-install-serviceworker/0.1/test/test-amp-install-serviceworker.js","test/functional/utils/test-bytes.js","extensions/amp-bind/0.1/test/test-bind-evaluator.js","test/functional/test-mustache.js","extensions/amp-experiment/0.1/test/test-amp-experiment.js","extensions/amp-brid-player/0.1/test/test-amp-brid-player.js","test/functional/3p/test-3p-messaging.js","test/functional/utils/test-signals.js","extensions/amp-form/0.1/test/test-amp-form.js","test/functional/test-event-helper.js","test/functional/test-ad-cid.js","ads/google/a4a/test/test-google-data-reporter.js","test/functional/test-task-queue.js","test/functional/test-srcset.js","extensions/amp-sidebar/0.1/test/test-amp-sidebar.js","test/functional/test-shadow-embed.js","extensions/amp-a4a/0.1/test/test-a4a-integration.js","extensions/amp-animation/0.1/test/test-web-animations.js","extensions/amp-analytics/0.1/test/test-variables.js","extensions/amp-jwplayer/0.1/test/test-amp-jwplayer.js","extensions/amp-izlesene/0.1/test/test-amp-izlesene.js","extensions/amp-auto-ads/0.1/test/test-ad-strategy.js","test/functional/test-log.js","extensions/amp-anim/0.1/test/test-amp-anim.js","test/functional/test-exponential-backoff.js","extensions/amp-auto-ads/0.1/test/test-placement.js","test/functional/test-json.js","extensions/amp-user-notification/0.1/test/test-amp-user-notification.js","test/functional/test-service.js","extensions/amp-live-list/0.1/test/test-poller.js","test/functional/test-crypto.js","test/functional/test-video-manager.js","test/functional/test-action.js","test/functional/test-template.js","test/functional/test-ad-helper.js","extensions/amp-ad-network-doubleclick-impl/0.1/test/test-doubleclick-a4a-config.js","test/functional/test-integration.js","test/functional/ads/test-csa.js","test/integration/test-extensions-loading.js","test/functional/utils/test-pem.js","test/functional/test-mode.js","extensions/amp-reach-player/0.1/test/test-amp-reach-player.js","test/functional/test-input.js","test/functional/test-amp-pixel.js","test/functional/3p/test-iframe-messaging-client.js","extensions/amp-ad/0.1/test/test-amp-ad-3p-impl.js","extensions/amp-springboard-player/0.1/test/test-amp-springboard-player.js","extensions/amp-analytics/0.1/test/test-visibility-impl.js","test/functional/test-base-element.js","test/functional/test-cache-sw-core.js","test/functional/test-cid.js","extensions/amp-facebook/0.1/test/test-amp-facebook.js","extensions/amp-analytics/0.1/test/test-transport.js","test/functional/inabox/test-position-observer.js","extensions/amp-google-vrview-image/0.1/test/test-amp-google-vrview-image.js","extensions/amp-access/0.1/test/test-amp-access-server-jwt.js","test/functional/test-font-stylesheet-timeout.js","test/functional/test-ios-scrollfreeze-bug.js","extensions/amp-list/0.1/test/test-amp-list.js","ads/google/a4a/test/test-performance.js","extensions/amp-accordion/0.1/test/test-amp-accordion.js","test/functional/utils/test-base64.js","test/functional/test-ampdoc.js","test/functional/test-types.js","test/functional/test-polyfill-document-contains.js","test/integration/test-video-players.js","ads/google/a4a/test/test-traffic-experiments.js","extensions/amp-bind/0.1/test/test-bind-impl.js","test/functional/test-3p-environment.js","test/functional/test-standard-actions.js","test/functional/test-alp-handler.js","test/functional/test-iframe-stub.js","extensions/amp-a4a/0.1/test/testdata/test_fragments.js","test/functional/test-style-installer.js","test/functional/test-polyfill-array-includes.js","test/functional/test-element-stub.js","extensions/amp-ad-network-cloudflare-impl/0.1/test/test-amp-ad-network-cloudflare-impl.js","extensions/amp-form/0.1/test/test-form-proxy.js","extensions/amp-analytics/0.1/test/test-events.js","test/functional/test-string.js","test/functional/test-url.js","test/integration/test-example-validation.js"] diff --git a/test/functional/TEST_FAILURE_BLOBS.json b/test/functional/TEST_FAILURE_BLOBS.json deleted file mode 100644 index 265fc4d2fc2c..000000000000 --- a/test/functional/TEST_FAILURE_BLOBS.json +++ /dev/null @@ -1 +0,0 @@ -["test/_init_tests.js","test/fixtures/*.html",{"pattern":"test/fixtures/served/*.html","included":false,"nocache":false,"watched":true},{"pattern":"dist/**/*.js","included":false,"nocache":false,"watched":true},{"pattern":"dist.tools/**/*.js","included":false,"nocache":false,"watched":true},{"pattern":"examples/**/*","included":false,"nocache":false,"watched":true},{"pattern":"dist.3p/**/*","included":false,"nocache":false,"watched":true},"extensions/amp-viewer-integration/0.1/test/test-touch-handler.js","extensions/amp-selector/0.1/test/test-amp-selector.js","extensions/amp-apester-media/0.1/test/test-amp-apester-media.js","extensions/amp-live-list/0.1/test/test-poller.js","extensions/amp-carousel/0.1/test/test-scrollable-carousel.js","ads/google/a4a/test/test-traffic-experiments.js","test/functional/test-ads-config.js","extensions/amp-a4a/0.1/test/test-a4a-integration.js","test/functional/test-vsync.js","extensions/amp-analytics/0.1/test/test-transport.js","extensions/amp-access/0.1/test/test-amp-access-server-jwt.js","test/integration/test-extensions-loading.js","extensions/amp-form/0.1/test/integration/test-integration-form.js","extensions/amp-ad/0.1/test/test-amp-ad-ui.js","extensions/amp-brightcove/0.1/test/test-amp-brightcove.js","extensions/amp-sticky-ad/0.1/test/test-amp-sticky-ad.js","test/functional/test-chunk.js","extensions/amp-reddit/0.1/test/test-amp-reddit.js","extensions/amp-form/0.1/test/test-form-validators.js","test/functional/test-focus-history.js","extensions/amp-sticky-ad/1.0/test/test-amp-sticky-ad.js","extensions/amp-youtube/0.1/test/test-amp-youtube.js","extensions/amp-dynamic-css-classes/0.1/test/test-runtime-classes.js","test/functional/inabox/test-position-observer.js","test/functional/test-gesture-recognizers.js","test/functional/inabox/test-inabox-viewport.js","extensions/amp-playbuzz/0.1/test/test-amp-playbuzz.js","test/functional/test-crypto.js","extensions/amp-twitter/0.1/test/test-amp-twitter.js","test/integration/test-errors.js","test/integration/test-video-players-helper.js","test/functional/test-intersection-observer.js","_WONTMATCH.qqq","extensions/amp-mustache/0.1/test/test-amp-mustache.js","test/functional/test-style-installer.js","test/functional/test-object.js","test/functional/test-fixed-layer.js","test/functional/test-font-stylesheet-timeout.js","extensions/amp-bind/0.1/test/test-bind-evaluator.js","test/functional/test-json.js","extensions/amp-experiment/0.1/test/test-variant.js","test/functional/test-resource.js","test/functional/test-document-ready.js","test/functional/test-animation.js","test/functional/test-event-helper.js","extensions/amp-ad/0.1/test/test-a2a-listener.js","test/functional/test-viewer.js","extensions/amp-auto-ads/0.1/test/test-placement.js","test/functional/test-intersection-observer-polyfill.js","test/functional/test-dom.js","extensions/amp-access/0.1/test/test-access-expr.js","extensions/amp-vine/0.1/test/test-amp-vine.js","extensions/amp-call-tracking/0.1/test/test-amp-call-tracking.js","test/functional/test-error.js","extensions/amp-viewer-integration/0.1/test/test-amp-webview-viewer-integration.js","test/functional/test-url.js","extensions/amp-ad-network-triplelift-impl/0.1/test/test-amp-ad-network-triplelift-impl.js","extensions/amp-font/0.1/test/test-amp-font.js","extensions/amp-carousel/0.1/test/test-slidescroll.js","ads/google/a4a/test/test-utils.js","test/functional/test-3p.js","test/functional/test-friendly-iframe-embed.js","extensions/amp-facebook/0.1/test/test-amp-facebook.js","test/functional/test-gesture.js","extensions/amp-analytics/0.1/test/test-vendors.js","test/functional/test-service.js","extensions/amp-accordion/0.1/test/test-amp-accordion.js","extensions/amp-hulu/0.1/test/test-amp-hulu.js","test/functional/test-preconnect.js","extensions/amp-ad/0.1/test/test-layout-delay-meter.js","extensions/amp-image-lightbox/0.1/test/test-amp-image-lightbox.js","extensions/amp-fx-flying-carpet/0.1/test/test-amp-fx-flying-carpet.js","test/functional/test-element-stub.js","extensions/amp-viewer-integration/0.1/test/viewer-initiated-handshake-viewer-for-testing.js","extensions/amp-live-list/0.1/test/test-amp-live-list.js","test/functional/test-timer.js","test/functional/test-integration.js","extensions/amp-bind/0.1/test/test-bind-validator.js","test/functional/test-log.js","extensions/amp-access/0.1/test/test-amp-access-client.js","extensions/amp-soundcloud/0.1/test/test-amp-soundcloud.js","test/functional/test-ad-cid.js","extensions/amp-share-tracking/0.1/test/test-amp-share-tracking.js","extensions/amp-list/0.1/test/test-amp-list.js","test/functional/3p/test-iframe-messaging-client.js","extensions/amp-analytics/0.1/test/test-visibility-model.js","extensions/amp-sortable-table/0.1/test/test-amp-sortable-table.js","extensions/amp-ad/0.1/test/test-amp-ad-custom.js","extensions/amp-access/0.1/test/test-jwt.js","extensions/amp-a4a/0.1/test/testdata/test_fragments.js","extensions/amp-bind/0.1/test/test-bind-impl.js","test/functional/test-document-click.js","extensions/amp-analytics/0.1/test/test-analytics-root.js","extensions/amp-fx-parallax/0.1/test/test-amp-fx-parallax.js","extensions/amp-fresh/0.1/test/test-amp-fresh.js","test/functional/test-polyfill-domtokenlist-toggle.js","extensions/amp-kaltura-player/0.1/test/test-amp-kaltura-player.js","test/integration/test-released.js","test/functional/test-url-replacements.js","test/functional/test-impression.js","extensions/amp-nexxtv-player/0.1/test/test-amp-nexxtv-player.js","extensions/amp-auto-ads/0.1/test/test-amp-auto-ads.js","test/functional/test-cookies.js","extensions/amp-analytics/0.1/test/test-amp-analytics.js","extensions/amp-font/0.1/test/test-fontloader.js","test/functional/utils/test-pem.js","extensions/amp-brid-player/0.1/test/test-amp-brid-player.js","extensions/amp-app-banner/0.1/test/test-amp-app-banner.js","test/functional/test-alp-handler.js","test/functional/test-observable.js","extensions/amp-ad/0.1/test/test-amp-ad.js","extensions/amp-viewer-integration/0.1/test/test-viewer-initiated-handshake.js","test/functional/test-pull-to-refresh.js","test/functional/inabox/test-inabox-messaging-host.js","extensions/amp-auto-ads/0.1/test/test-attributes.js","extensions/amp-ad-network-doubleclick-impl/0.1/test/test-amp-ad-network-doubleclick-impl.js","extensions/amp-pinterest/0.1/test/test-amp-pinterest.js","extensions/amp-access/0.1/test/test-amp-access-server.js","test/functional/test-runtime.js","test/functional/test-iframe-stub.js","extensions/amp-access/0.1/test/test-signin.js","test/functional/test-action.js","extensions/amp-bind/0.1/test/test-bind-expression.js","test/functional/test-standard-actions.js","extensions/amp-analytics/0.1/test/test-instrumentation.js","extensions/amp-access-laterpay/0.1/test/test-amp-access-laterpay.js","extensions/amp-install-serviceworker/0.1/test/test-amp-install-serviceworker.js","extensions/amp-ad/0.1/test/test-amp-ad-3p-impl.js","test/functional/test-extensions.js","extensions/amp-ad-network-adsense-impl/0.1/test/test-amp-ad-network-adsense-impl.js","extensions/amp-access/0.1/test/test-amp-login-done-dialog.js","test/integration/test-amp-img.js","extensions/amp-access/0.1/test/test-amp-access-vendor.js","test/functional/test-srcset.js","extensions/amp-analytics/0.1/test/test-visibility-impl.js","extensions/amp-user-notification/0.1/test/test-amp-user-notification.js","test/functional/web-worker/test-amp-worker.js","extensions/amp-viewer-integration/0.1/test/viewer-for-testing.js","test/functional/test-render-delaying-services.js","test/functional/test-performance.js","extensions/amp-carousel/0.1/test/test-base-slide.js","test/functional/test-ampdoc.js","ads/google/a4a/test/test-google-data-reporter.js","test/functional/test-video-manager.js","test/integration/test-amp-ad-3p.js","extensions/amp-form/0.1/test/test-form-proxy.js","test/functional/test-experiments.js","extensions/amp-access/0.1/test/test-amp-access.js","extensions/amp-social-share/0.1/test/test-amp-social-share.js","test/functional/test-amp-img.js","test/functional/test-cache-sw-core.js","extensions/amp-izlesene/0.1/test/test-amp-izlesene.js","test/functional/test-polyfill-document-contains.js","test/functional/test-polyfill-math-sign.js","extensions/amp-viewer-integration/0.1/test/test-amp-viewer-integration.js","test/functional/test-input.js","extensions/amp-a4a/0.1/test/testdata/valid_css_at_rules_amp.reserialized.js","test/integration/test-boilerplates.js","extensions/amp-ad-network-cloudflare-impl/0.1/test/test-amp-ad-network-cloudflare-impl.js","test/functional/ads/test-csa.js","extensions/amp-lightbox-viewer/0.1/test/test-amp-lightbox-viewer.js","extensions/amp-anim/0.1/test/test-amp-anim.js","extensions/amp-iframe/0.1/test/test-amp-iframe.js","test/functional/utils/test-signals.js","test/functional/test-pass.js","test/functional/test-activity.js","extensions/amp-form/0.1/test/test-amp-form.js","extensions/amp-access/0.1/test/test-amp-access-other.js","extensions/amp-auto-ads/0.1/test/test-ad-network-config.js","test/integration/test-visibility-states.js","test/functional/test-transition.js","test/functional/test-xhr.js","extensions/amp-analytics/0.1/test/test-variables.js","ads/google/a4a/test/test-performance.js","test/functional/test-document-state.js","test/functional/test-extension-location.js","extensions/amp-a4a/0.1/test/utils.js","extensions/amp-dailymotion/0.1/test/test-amp-dailymotion.js","test/functional/test-style.js","extensions/amp-a4a/0.1/test/test-amp-a4a.js","test/functional/test-polyfill-array-includes.js","extensions/amp-audio/0.1/test/test-amp-audio.js","test/functional/test-mustache.js","test/functional/test-platform.js","test/functional/test-analytics.js","test/functional/test-variable-source.js","extensions/amp-o2-player/0.1/test/test-amp-o2-player.js","test/functional/test-string.js","extensions/amp-instagram/0.1/test/test-amp-instagram.js","test/functional/utils/test-dom-fingerprint.js","test/functional/test-3p-frame.js","test/functional/test-get-html.js","test/functional/test-ad-helper.js","test/functional/test-motion.js","extensions/amp-viewer-integration/0.1/test/webview-viewer-for-testing.js","test/functional/test-document-info.js","extensions/amp-google-vrview-image/0.1/test/test-amp-google-vrview-image.js","extensions/amp-dynamic-css-classes/0.1/test/test-dynamic-classes.js","test/integration/test-amp-ad-doubleclick.js","test/functional/test-amp-pixel.js","extensions/amp-jwplayer/0.1/test/test-amp-jwplayer.js","test/functional/test-shadow-embed.js","extensions/amp-a4a/0.1/test/test-a4a-var-source.js","extensions/amp-ooyala-player/0.1/test/test-amp-ooyala.js","test/functional/test-cid.js","test/functional/test-mode.js","test/functional/test-finite-state-machine.js","extensions/amp-live-list/0.1/test/test-live-list-manager.js","extensions/amp-experiment/0.1/test/test-amp-experiment.js","test/functional/test-iframe-helper.js","extensions/amp-ad-network-doubleclick-impl/0.1/test/test-doubleclick-a4a-config.js","test/functional/test-custom-element.js","test/functional/utils/test-bytes.js","extensions/amp-form/0.1/test/test-validation-bubble.js","test/functional/test-sanitizer.js","extensions/amp-springboard-player/0.1/test/test-amp-springboard-player.js","test/functional/test-template.js","extensions/amp-analytics/0.1/test/test-visibility-manager.js","test/integration/test-amp-carousel.js","test/functional/test-anchor-click-interceptor.js","test/functional/test-base-element.js","extensions/amp-analytics/0.1/test/test-events.js","test/integration/test-video-players.js","test/functional/test-ios-scrollfreeze-bug.js","test/manual/test-sw.js","test/functional/test-polyfill-object-assign.js","test/functional/test-storage.js","extensions/amp-animation/0.1/test/test-web-animations.js","test/functional/test-layout-rect.js","test/functional/test-history.js","extensions/amp-auto-ads/0.1/test/test-ad-strategy.js","test/functional/test-curve.js","test/functional/utils/test-base64.js","extensions/amp-gfycat/0.1/test/test-amp-gfycat.js","test/integration/test-example-validation.js","test/functional/test-3p-environment.js","test/functional/utils/test-priority-queue.js","extensions/amp-auto-ads/0.1/test/test-ad-tracker.js","test/functional/test-types.js","extensions/amp-ad/0.1/test/test-amp-ad-xorigin-iframe-handler.js","extensions/amp-animation/0.1/test/test-amp-animation.js","extensions/amp-fresh/0.1/test/test-amp-fresh-manager.js","test/functional/test-exponential-backoff.js","extensions/amp-access/0.1/test/test-login-dialog.js","test/functional/test-web-components.js","extensions/amp-fit-text/0.1/test/test-amp-fit-text.js","test/functional/utils/test-array.js","extensions/amp-reach-player/0.1/test/test-amp-reach-player.js","test/functional/test-viewport.js","extensions/amp-bind/0.1/test/test-bind-integration.js","test/functional/test-batched-xhr.js","extensions/amp-vimeo/0.1/test/test-amp-vimeo.js","test/functional/utils/test-rate-limit.js","extensions/amp-sidebar/0.1/test/test-amp-sidebar.js","test/functional/test-size-list.js","test/functional/test-task-queue.js","test/functional/3p/test-3p-messaging.js","test/integration/test-configuration.js","test/functional/test-3p-nameframe.js","test/functional/test-document-submit.js","extensions/amp-video/0.1/test/test-amp-video.js","test/functional/test-resources.js","ads/google/a4a/test/test-google-ads-a4a-config.js","test/functional/test-layout.js","test/functional/test-ie-media-bug.js","extensions/amp-ad-network-doubleclick-impl/0.1/test/test-doubleclick-integration.js"] diff --git a/yarn.lock b/yarn.lock index 35a153737ef3..5937cd8539dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -615,10 +615,6 @@ astw@^2.0.0: dependencies: acorn "^4.0.3" -async-array-reduce@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/async-array-reduce/-/async-array-reduce-0.2.1.tgz#c8be010a2b5cd00dea96c81116034693dfdd82d1" - async-each@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" @@ -1940,13 +1936,6 @@ callsites@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" -camel-case@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-1.2.2.tgz#1aca7c4d195359a2ce9955793433c6e5542511f2" - dependencies: - sentence-case "^1.1.1" - upper-case "^1.1.1" - camelcase-keys@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" @@ -3979,16 +3968,6 @@ file-entry-cache@^2.0.0: flat-cache "^1.2.1" object-assign "^4.0.1" -file-reader@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/file-reader/-/file-reader-1.1.1.tgz#383d131b4a7d58c77ec35366dcdadbd4757de8d6" - dependencies: - camel-case "^1.2.2" - extend-shallow "^2.0.1" - lazy-cache "^1.0.4" - map-files "^0.8.0" - read-yaml "^1.0.0" - file-uri-to-path@1: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" @@ -4940,12 +4919,6 @@ has-flag@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" -has-glob@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/has-glob/-/has-glob-0.1.1.tgz#a261c4c2a6c667e0c77b700a7f297c39ef3aa589" - dependencies: - is-glob "^2.0.1" - has-gulplog@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/has-gulplog/-/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce" @@ -5732,10 +5705,6 @@ is-utf8@^0.2.0, is-utf8@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" -is-valid-glob@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/is-valid-glob/-/is-valid-glob-0.3.0.tgz#d4b55c69f51886f9b65c70d6c2622d37e29f48fe" - is-windows@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" @@ -6198,11 +6167,11 @@ latest-version@^3.0.0: dependencies: package-json "^4.0.0" -lazy-cache@^1.0.3, lazy-cache@^1.0.4: +lazy-cache@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" -lazy-cache@^2.0.1, lazy-cache@^2.0.2: +lazy-cache@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-2.0.2.tgz#b9190a4f913354694840859f8a8f7084d8822264" dependencies: @@ -6638,10 +6607,6 @@ loud-rejection@^1.0.0, loud-rejection@^1.2.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" -lower-case@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" - lowercase-keys@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" @@ -6718,15 +6683,6 @@ map-cache@^0.2.0, map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" -map-files@^0.8.0: - version "0.8.2" - resolved "https://registry.yarnpkg.com/map-files/-/map-files-0.8.2.tgz#c1bf30017ee5f5948f25974076a3af9653978381" - dependencies: - isobject "^2.0.0" - lazy-cache "^1.0.4" - matched "^0.4.1" - vinyl "^1.1.1" - map-obj@^1.0.0, map-obj@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" @@ -6768,20 +6724,6 @@ marked@^0.3.9: version "0.3.12" resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.12.tgz#7cf25ff2252632f3fe2406bde258e94eee927519" -matched@^0.4.1: - version "0.4.4" - resolved "https://registry.yarnpkg.com/matched/-/matched-0.4.4.tgz#56d7b7eb18033f0cf9bc52eb2090fac7dc1e89fa" - dependencies: - arr-union "^3.1.0" - async-array-reduce "^0.2.0" - extend-shallow "^2.0.1" - fs-exists-sync "^0.1.0" - glob "^7.0.5" - has-glob "^0.1.1" - is-valid-glob "^0.3.0" - lazy-cache "^2.0.1" - resolve-dir "^0.1.0" - matcher@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/matcher/-/matcher-0.1.2.tgz#ef20cbde64c24c50cc61af5b83ee0b1b8ff00101" @@ -8638,13 +8580,6 @@ read-pkg@^2.0.0: normalize-package-data "^2.3.2" path-type "^2.0.0" -read-yaml@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/read-yaml/-/read-yaml-1.1.0.tgz#0d273ac0c95be92230dc0d4c4c4f5b8960a336d6" - dependencies: - extend-shallow "^2.0.1" - js-yaml "^3.8.2" - readable-stream@1.1.x, readable-stream@^1.0.33, readable-stream@~1.1.8, readable-stream@~1.1.9: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" @@ -9148,10 +9083,6 @@ sax@^1.2.4, sax@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" -seedrandom@^2.4.2: - version "2.4.3" - resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-2.4.3.tgz#2438504dad33917314bff18ac4d794f16d6aaecc" - semver-diff@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" @@ -9205,12 +9136,6 @@ send@0.16.1: range-parser "~1.2.0" statuses "~1.3.1" -sentence-case@^1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-1.1.3.tgz#8034aafc2145772d3abe1509aa42c9e1042dc139" - dependencies: - lower-case "^1.1.1" - sequencify@~0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/sequencify/-/sequencify-0.0.7.tgz#90cff19d02e07027fd767f5ead3e7b95d1e7380c" @@ -9346,12 +9271,6 @@ shell-quote@^1.4.2, shell-quote@^1.6.1: array-reduce "~0.0.0" jsonify "~0.0.0" -shuffle-seed@1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/shuffle-seed/-/shuffle-seed-1.1.6.tgz#533c12683bab3b4fa3e8751fc4e562146744260b" - dependencies: - seedrandom "^2.4.2" - sigmund@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" @@ -10443,10 +10362,6 @@ update-notifier@^2.1.0, update-notifier@^2.3.0: semver-diff "^2.0.0" xdg-basedir "^3.0.0" -upper-case@^1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" - urix@^0.1.0, urix@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" @@ -10636,7 +10551,7 @@ vinyl@^0.5.0: clone-stats "^0.0.1" replace-ext "0.0.1" -vinyl@^1.1.0, vinyl@^1.1.1, vinyl@^1.2.0: +vinyl@^1.1.0, vinyl@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-1.2.0.tgz#5c88036cf565e5df05558bfc911f8656df218884" dependencies: From 4c86a54c9f208caf1852666376c826c1ac936f24 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Tue, 6 Feb 2018 11:51:28 -0500 Subject: [PATCH 24/65] Update amp-iframe container in build callback (#13242) --- extensions/amp-iframe/0.1/amp-iframe.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/extensions/amp-iframe/0.1/amp-iframe.js b/extensions/amp-iframe/0.1/amp-iframe.js index 0a27faf1612c..2fb46fef903f 100644 --- a/extensions/amp-iframe/0.1/amp-iframe.js +++ b/extensions/amp-iframe/0.1/amp-iframe.js @@ -105,7 +105,11 @@ export class AmpIframe extends AMP.BaseElement { **/ this.iframeSrc = null; - /** @private {?Element} */ + /** + * The element which will contain the iframe. This may be the amp-iframe + * itself if the iframe is non-scrolling, or a wrapper element if it is. + * @private {?Element} + */ this.container_ = null; /** @private {boolean|undefined} */ @@ -215,13 +219,6 @@ export class AmpIframe extends AMP.BaseElement { this.element.getAttribute('srcdoc'), this.sandbox_); this.iframeSrc = this.assertSource( iframeSrc, window.location.href, this.sandbox_); - - /** - * The element which will contain the iframe. This may be the amp-iframe - * itself if the iframe is non-scrolling, or a wrapper element if it is. - * @type {!Element} - */ - this.container_ = makeIOsScrollable(this.element); } /** @@ -246,6 +243,8 @@ export class AmpIframe extends AMP.BaseElement { if (!this.element.hasAttribute('frameborder')) { this.element.setAttribute('frameborder', '0'); } + + this.container_ = makeIOsScrollable(this.element); } /** From 56a4a20f6c7940237a797a4063f18a6bf5ec5215 Mon Sep 17 00:00:00 2001 From: BySide Devel - 3rd Party <35220081+bysidedevel3rdparty@users.noreply.github.com> Date: Tue, 6 Feb 2018 17:57:14 +0000 Subject: [PATCH 25/65] Implement amp-byside-placeholder extension (#12798) --- examples/amp-byside-content.amp.html | 55 +++ .../0.1/amp-byside-content.css | 138 +++++++ .../0.1/amp-byside-content.js | 343 ++++++++++++++++++ .../0.1/test/test-amp-byside-content.js | 153 ++++++++ .../test/validator-amp-byside-content.html | 56 +++ .../0.1/test/validator-amp-byside-content.out | 61 ++++ extensions/amp-byside-content/0.1/utils.js | 59 +++ extensions/amp-byside-content/OWNERS.yaml | 1 + .../amp-byside-content/amp-byside-content.md | 91 +++++ .../validator-amp-byside-content.protoascii | 48 +++ gulpfile.js | 1 + 11 files changed, 1006 insertions(+) create mode 100644 examples/amp-byside-content.amp.html create mode 100644 extensions/amp-byside-content/0.1/amp-byside-content.css create mode 100644 extensions/amp-byside-content/0.1/amp-byside-content.js create mode 100644 extensions/amp-byside-content/0.1/test/test-amp-byside-content.js create mode 100644 extensions/amp-byside-content/0.1/test/validator-amp-byside-content.html create mode 100644 extensions/amp-byside-content/0.1/test/validator-amp-byside-content.out create mode 100644 extensions/amp-byside-content/0.1/utils.js create mode 100644 extensions/amp-byside-content/OWNERS.yaml create mode 100644 extensions/amp-byside-content/amp-byside-content.md create mode 100644 extensions/amp-byside-content/validator-amp-byside-content.protoascii diff --git a/examples/amp-byside-content.amp.html b/examples/amp-byside-content.amp.html new file mode 100644 index 000000000000..eb36c1cdd080 --- /dev/null +++ b/examples/amp-byside-content.amp.html @@ -0,0 +1,55 @@ + + + + + BySide Content example + + + + + + + + +

Responsive layout content (with restricted max width)

+
+ +
+ +

Fixed height content with overflow

+ + +

Fixed layout content

+ + + diff --git a/extensions/amp-byside-content/0.1/amp-byside-content.css b/extensions/amp-byside-content/0.1/amp-byside-content.css new file mode 100644 index 000000000000..f98941fb819c --- /dev/null +++ b/extensions/amp-byside-content/0.1/amp-byside-content.css @@ -0,0 +1,138 @@ +/** + * Copyright 2018 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. + */ + +.i-amphtml-byside-content-overflow { + display: flex; + justify-content: center; + align-items: center; + position: relative; + top: 100%; + width: 100%; + height: 100px; + margin-top: -100px; + text-align: center; + background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 75%); +} + +.i-amphtml-byside-content-overflow .i-amphtml-byside-content-overflow-content { + display: flex; + justify-content: center; + align-items: center; + width: 100px; + height: 30px; + border-radius: 15px; + background-color: #093C71; + border: 1px solid #093C71; + color: #fff; +} + +.i-amphtml-byside-content-arrow-down { + width: 0; + height: 0; + border-left: 10px solid transparent; + border-right: 10px solid transparent; + border-top: 10px solid #fff; +} + +.i-amphtml-byside-content-loading-container { + background-color: rgba(0, 0, 0, 0.02); + display: flex; + width: 100%; + height: 100%; + justify-content: center; + align-items: center; + position: absolute!important; + top: 0!important; + left: 0!important; + right: 0!important; + bottom: 0!important; +} + +.i-amphtml-byside-content-loading-container .i-amphtml-byside-content-loading-animation { + display: block; + text-align: center; + position: relative; + height: 16px; + width: 40px; + margin: 4px auto; + background: transparent; +} + +.i-amphtml-byside-content-loading-container .i-amphtml-byside-content-loading-animation:before, +.i-amphtml-byside-content-loading-container .i-amphtml-byside-content-loading-animation:after { + content: ''; + position: absolute; + top: 0; + left: 0; + z-index: 9; + width: 16px; + height: 16px; + border-radius: 50%; + opacity: 0.95; + animation-name: i-amphtml-byside-content-loading-translate, i-amphtml-byside-content-loading-zindex; + animation-delay: 0; + animation-duration: 0.8s; + animation-direction: alternate; + animation-timing-function: linear; + animation-iteration-count: infinite; +} + +.i-amphtml-byside-content-loading-container .i-amphtml-byside-content-loading-animation:before { + background: #093C71; +} + +.i-amphtml-byside-content-loading-container .i-amphtml-byside-content-loading-animation:after { + background: #e75204; + z-index: 0; + animation-name: i-amphtml-byside-content-loading-translate-inverse, i-amphtml-byside-content-loading-zindex-inverse; + animation-delay: 0s, 0.6s; +} + +@keyframes i-amphtml-byside-content-loading-translate { + 0% { + transform: translate(-12px, 0); + } + 100% { + transform: translate(12px, 0); + } +} + +@keyframes i-amphtml-byside-content-loading-zindex { + 0% { + z-index: 9; + } + 100% { + z-index: 0; + } +} + +@keyframes i-amphtml-byside-content-loading-translate-inverse { + 0% { + transform: translate(12px, 0); + } + 100% { + transform: translate(-12px, 0); + } +} + +@keyframes i-amphtml-byside-content-loading-zindex-inverse { + 0% { + z-index: 0; + } + 100% { + z-index: 9; + } +} diff --git a/extensions/amp-byside-content/0.1/amp-byside-content.js b/extensions/amp-byside-content/0.1/amp-byside-content.js new file mode 100644 index 000000000000..76c0fc18309f --- /dev/null +++ b/extensions/amp-byside-content/0.1/amp-byside-content.js @@ -0,0 +1,343 @@ +/** + * Copyright 2018 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. + */ + +/** + * @fileoverview Displays BySide placeholder content. + * The client settings and placeholder content label should be added as component + * attributes as seen in the following example: + * + * + * + * + */ + +import * as utils from './utils'; +import {CSS} from '../../../build/amp-byside-content-0.1.css'; +import {Services} from '../../../src/services'; +import {addParamsToUrl, assertHttpsUrl} from '../../../src/url'; +import {dev, user} from '../../../src/log'; +import {dict} from '../../../src/utils/object'; +import {isLayoutSizeDefined} from '../../../src/layout'; +import {listenFor} from '../../../src/iframe-helper'; +import {removeElement} from '../../../src/dom'; +import {setStyles} from '../../../src/style'; +import {toWin} from '../../../src/types'; + +/** @const {string} */ +const TAG_ = 'amp-byside-content'; + +/** @const {string} */ +const BYSIDE_DOMAIN_ = 'byside.com'; + +/** @const {string} */ +const DEFAULT_WEBCARE_ZONE_ = 'main'; + +/** @const {string} */ +const MAIN_WEBCARE_ZONE_ = 'main'; + +/** @const {string} */ +const MAIN_WEBCARE_ZONE_SUBDOMAIN_ = 'webcare'; + +/** @const {string} */ +const DEFAULT_LANG_ = 'pt'; + +/** @type {number} */ +let iframeCount_ = 0; + +export class AmpBysideContent extends AMP.BaseElement { + + /** @param {!AmpElement} element */ + constructor(element) { + super(element); + + /** @private {Array} */ + this.unlisteners_ = []; + + /** @private {?string} */ + this.iframeSrc_ = null; + + /** @private {?Element} */ + this.iframe_ = null; + + /** @private {?Promise} */ + this.iframePromise_ = null; + + /** @private {string} */ + this.webcareZone_ = MAIN_WEBCARE_ZONE_; + + /** @private {string} */ + this.webcareId_ = ''; + + /** @private {string} */ + this.channel_ = ''; + + /** @private {string} */ + this.lang_ = ''; + + /** @private {string} */ + this.fid_ = ''; + + /** @private {string} */ + this.label_ = ''; + + /** @private {string} */ + this.origin_ = ''; + + /** @private {string} */ + this.baseUrl_ = ''; + + /** @const {function()} */ + this.boundUpdateSize_ = + utils.debounce(this.updateSize_.bind(this), 100); + } + + /** @override */ + isLayoutSupported(layout) { + return isLayoutSizeDefined(layout); + } + + /** + * @param {boolean=} onLayout + * @override + */ + preconnectCallback(onLayout) { + if (this.iframeSrc_) { + this.preconnect.url(this.iframeSrc_, onLayout); + } + } + + /** @override */ + buildCallback() { + this.webcareId_ = user().assert( + this.element.getAttribute('data-webcare-id'), + 'The data-webcare-id attribute is required for <' + TAG_ + '> %s', + this.element); + + this.label_ = user().assert( + this.element.getAttribute('data-label'), + 'The data-label attribute is required for <' + TAG_ + '> %s', + this.element); + + this.webcareZone_ = (this.element.getAttribute('data-webcare-zone') || + DEFAULT_WEBCARE_ZONE_); + this.channel_ = (this.element.getAttribute('data-channel') || ''); + this.lang_ = (this.element.getAttribute('data-lang') || DEFAULT_LANG_); + this.fid_ = (this.element.getAttribute('data-fid') || ''); + + this.origin_ = this.composeOrigin_(); + this.baseUrl_ = this.origin_ + '/BWA' + + encodeURIComponent(this.webcareId_) + '/amp/'; + } + + /** @override */ + createPlaceholderCallback() { + const placeholder = this.win.document.createElement('div'); + placeholder.setAttribute('placeholder', ''); + placeholder.appendChild(this.createBySideLoader_()); + + this.applyFillContent(placeholder); + + return placeholder; + } + + /** @override */ + layoutCallback() { + const iframe = this.element.ownerDocument.createElement('iframe'); + this.iframe_ = iframe; + + iframe.name = 'amp_byside_content_iframe' + iframeCount_++; + iframe.setAttribute('scrolling', 'no'); + iframe.setAttribute('frameborder', '0'); + iframe.setAttribute('allowtransparency', 'true'); + iframe.setAttribute('allowfullscreen', 'true'); + iframe.setAttribute('title', this.element.getAttribute('title') || ''); + + setStyles(iframe, { + 'opacity': 0, + }); + + this.element.appendChild(this.getOverflowElement_()); + this.applyFillContent(iframe); + + return this.composeSrcUrl_().then(src => { + this.iframeSrc_ = assertHttpsUrl(src, this.element, this.getName_()); + iframe.src = this.iframeSrc_; + + const unlisten = listenFor(iframe, 'embed-size', this.boundUpdateSize_); + this.unlisteners_.push(unlisten); + + this.element.appendChild(iframe); + return (this.iframePromise_ = this.loadPromise(iframe)); + }).then(() => { + this.getVsync().mutate(() => { + setStyles(iframe, { + 'opacity': 1, + }); + }); + }).then(() => this); + } + + /** @private */ + composeOrigin_() { + const subDomain = this.webcareZone_ === MAIN_WEBCARE_ZONE_ ? + MAIN_WEBCARE_ZONE_SUBDOMAIN_ : + this.webcareZone_; + + return 'https://' + subDomain + '.' + BYSIDE_DOMAIN_; + } + + /** @private */ + composeSrcUrl_() { + const src = this.baseUrl_ + 'placeholder.php'; + const params = dict({ + 'label': this.label_, + 'webcare_id': this.webcareId_, + 'bwch': this.channel_ || '', + 'lang': this.lang_ || '', + 'fid': this.fid_ || '', + 'bwit': (this.fid_ ? 'I' : 'A'), + 'tuid': 'CLIENT_ID(byside_webcare_tuid)', + 'suid': '', + 'puid': 'PAGE_VIEW_IDpTIMESTAMP', + 'referrer': 'DOCUMENT_REFERRER', + 'page': 'SOURCE_URL', + 'amppage': 'AMPDOC_URL', + 'bwpt': 'TITLE', + 'bres': 'VIEWPORT_WIDTHxVIEWPORT_HEIGHT', + 'res': 'SCREEN_WIDTHxSCREEN_HEIGHT', + 'v': 'v20171116a', + 'ampv': 'AMP_VERSION', + 'viewer': 'VIEWER', + 'ua': 'USER_AGENT', + 'r': 'RANDOM', + '_resize': '1', + }); + const url = addParamsToUrl(src, params); + + return new Promise(resolve => { + try { + // If a node is passed, try to resolve via this node. + const win = toWin(/** @type {!Document} */ ( + this.element.ownerDocument || this.element).defaultView); + + // for unit integration tests resolve original url + // since url replacements implementation throws an uncaught error + win.AMP_TEST ? resolve(url) : + Services.urlReplacementsForDoc(this.element) + .expandUrlAsync(url).then(newUrl => resolve(newUrl)); + } catch (error) { + resolve(url); + } + }); + } + + /** + * @return {string} Returns a string to identify this tag. May not be unique + * if the element label is not unique. + * @private + */ + getName_() { + let suffix = this.webcareId_ || ''; + suffix += suffix && this.label_ ? ': ' : ''; + suffix += this.label_ || ''; + + return 'amp-byside-content: ' + (suffix || ''); + } + + /** + * @returns {!Element} Returns the overflow element + * @private + */ + getOverflowElement_() { + const createElement = utils.getElementCreator(this.element.ownerDocument); + + const overflow = createElement('div', 'i-amphtml-byside-content-overflow', + createElement('div', 'i-amphtml-byside-content-overflow-content', + createElement('i', 'i-amphtml-byside-content-arrow-down') + )); + overflow.setAttribute('overflow', ''); + + return overflow; + } + + /** @return {!Element} @private */ + createBySideLoader_() { + const createElement = utils.getElementCreator(this.element.ownerDocument); + + const loadingPlaceholder = + createElement('div', 'i-amphtml-byside-content-loading-container', + createElement('div', 'i-amphtml-byside-content-loading-animation') + ); + + return loadingPlaceholder; + } + + /** + * Updates the element's dimensions to accommodate the iframe's + * requested dimensions. + * @param {Object} data + * @private + */ + updateSize_(data) { + // Calculate new height of the container to include the padding. + // If padding is negative, just use the requested height directly. + if (!data) { + return; + } + + let newHeight; + const height = parseInt(data['height'], 10); + if (!isNaN(height)) { + newHeight = Math.max( + height + (this.element./*OK*/offsetHeight + - this.iframe_./*OK*/offsetHeight), + height); + } + + if (newHeight !== undefined) { + this.attemptChangeHeight(newHeight).catch(() => {/* do nothing */ }); + } else { + dev().warn(TAG_, + 'Ignoring embed-size request because no height value is provided', + this.element); + } + } + + /** @override */ + unlayoutCallback() { + this.unlisteners_.forEach(unlisten => unlisten()); + this.unlisteners_.length = 0; + + if (this.iframe_) { + removeElement(this.iframe_); + this.iframe_ = null; + this.iframePromise_ = null; + } + return true; // Call layoutCallback again. + } +} + +AMP.extension('amp-byside-content', '0.1', AMP => { + AMP.registerElement('amp-byside-content', AmpBysideContent, CSS); +}); diff --git a/extensions/amp-byside-content/0.1/test/test-amp-byside-content.js b/extensions/amp-byside-content/0.1/test/test-amp-byside-content.js new file mode 100644 index 000000000000..aa6b64ffd8a3 --- /dev/null +++ b/extensions/amp-byside-content/0.1/test/test-amp-byside-content.js @@ -0,0 +1,153 @@ +/** + * Copyright 2018 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 '../amp-byside-content'; + +describes.realWin('amp-byside-content', { + amp: { + extensions: ['amp-byside-content'], + }, + ampAdCss: true, +}, env => { + let win, doc; + + beforeEach(() => { + win = env.win; + doc = win.document; + }); + + function getElement(attributes, opt_responsive, opt_beforeLayoutCallback) { + const elem = doc.createElement('amp-byside-content'); + + for (const key in attributes) { + elem.setAttribute(key, attributes[key]); + } + + elem.setAttribute('width', '640'); + elem.setAttribute('height', '360'); + if (opt_responsive) { + elem.setAttribute('layout', 'responsive'); + } + + doc.body.appendChild(elem); + return elem.build().then(() => { + if (opt_beforeLayoutCallback) { + opt_beforeLayoutCallback(elem); + } + + return elem.layoutCallback(); + }).then(() => elem); + } + + function testIframe(elem) { + const iframe = elem.querySelector('iframe'); + expect(iframe).to.not.be.null; + expect(iframe.getAttribute('scrolling')).to.equal('no'); + expect(iframe.className).to.match(/i-amphtml-fill-content/); + expect(iframe.fakeSrc).to.satisfy(src => { + return src.startsWith(elem.implementation_.baseUrl_); + }); + } + + it('renders', () => { + return getElement({ + 'data-webcare-id': 'D6604AE5D0', + 'data-label': 'amp-simple', + }).then(elem => { + testIframe(elem); + }); + }); + + it('requires data-label', () => { + return getElement({ + 'data-webcare-id': 'D6604AE5D0', + }).should.eventually.be.rejectedWith( + /The data-label attribute is required for/); + }); + + it('requires data-webcare-id', () => { + return (getElement({ + 'data-label': 'placeholder-label', + })).should.eventually.be.rejectedWith( + /The data-webcare-id attribute is required for/); + }); + + it('generates correct default origin', () => { + return getElement({ + 'data-webcare-id': 'D6604AE5D0', + 'data-label': 'placeholder-label', + }).then(elem => { + expect(elem.implementation_.origin_).to.equal( + 'https://webcare.byside.com' + ); + }); + }); + + it('generates correct provided webcare zone', () => { + const webcareZone = 'sa1'; + + return getElement({ + 'data-webcare-id': 'D6604AE5D0', + 'data-label': 'placeholder-label', + 'data-webcare-zone': webcareZone, + }).then(elem => { + expect(elem.implementation_.origin_).to.equal( + 'https://' + webcareZone + '.byside.com' + ); + }); + }); + + it('should create a loading animation', () => { + return getElement({ + 'data-webcare-id': 'D6604AE5D0', + 'data-label': 'placeholder-label', + }).then(elem => { + const loader = elem.querySelector( + '.i-amphtml-byside-content-loading-animation' + ); + expect(loader).to.not.be.null; + }); + }); + + it('builds a placeholder loading animation without inserting iframe', () => { + const attributes = { + 'data-webcare-id': 'D6604AE5D0', + 'data-label': 'placeholder-label', + }; + + return getElement(attributes, true, elem => { + const placeholder = elem.querySelector('[placeholder]'); + const iframe = elem.querySelector('iframe'); + expect(iframe).to.be.null; + expect(placeholder.style.display).to.be.equal(''); + }).then(elem => { + const placeholder = elem.querySelector('[placeholder]'); + elem.getVsync = () => { + return { + mutate: fn => fn(), + }; + }; + + // test iframe + testIframe(elem); + + // test placeholder too + elem.implementation_.iframePromise_.then(() => { + expect(placeholder.style.display).to.be.equal('none'); + }); + }); + }); +}); diff --git a/extensions/amp-byside-content/0.1/test/validator-amp-byside-content.html b/extensions/amp-byside-content/0.1/test/validator-amp-byside-content.html new file mode 100644 index 000000000000..8a347e246e79 --- /dev/null +++ b/extensions/amp-byside-content/0.1/test/validator-amp-byside-content.html @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/amp-byside-content/0.1/test/validator-amp-byside-content.out b/extensions/amp-byside-content/0.1/test/validator-amp-byside-content.out new file mode 100644 index 000000000000..a0e587856e97 --- /dev/null +++ b/extensions/amp-byside-content/0.1/test/validator-amp-byside-content.out @@ -0,0 +1,61 @@ +FAIL +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| > ^~~~~~~~~ +amp-byside-content/0.1/test/validator-amp-byside-content.html:49:2 The mandatory attribute 'data-label' is missing in tag 'amp-byside-content'. (see https://www.ampproject.org/docs/reference/components/amp-byside-content) [AMP_TAG_PROBLEM] +>> ^~~~~~~~~ +amp-byside-content/0.1/test/validator-amp-byside-content.html:49:2 The mandatory attribute 'data-webcare-id' is missing in tag 'amp-byside-content'. (see https://www.ampproject.org/docs/reference/components/amp-byside-content) [AMP_TAG_PROBLEM] +| data-lang="en" +| data-channel="en" +| height="300" +| layout="fixed-height"> +| +| +| diff --git a/extensions/amp-byside-content/0.1/utils.js b/extensions/amp-byside-content/0.1/utils.js new file mode 100644 index 000000000000..c18671d3e747 --- /dev/null +++ b/extensions/amp-byside-content/0.1/utils.js @@ -0,0 +1,59 @@ +/** + * Copyright 2018 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. + */ + + +/** + * Returns a function, that, as long as it continues to be invoked, will not + * be triggered. The function will be called after it stops being called for + * N milliseconds. If `immediate` is passed, trigger the function on the + * leading edge, instead of the trailing. + * @param {Function} func + * @param {number} wait + * @param {boolean=} immediate + */ +export function debounce(func, wait, immediate) { + let timeout; + return function() { + const context = this, args = arguments; + clearTimeout(timeout); + timeout = setTimeout(function() { + timeout = null; + if (!immediate) { func.apply(context, args); }; + }, wait); + if (immediate && !timeout) { func.apply(context, args); } + }; +}; + +/** + * + * Gets an element creator using a given document to create elements. + * @export getElementCreator + * @param {Document} document + * @returns {!Function} + */ +export function getElementCreator(document) { + return function createElement(name, className, children) { + const element = document.createElement(name); + element.className = className; + appendChildren(element, children); + return element; + }; +} + +function appendChildren(element, children) { + children = (!children) ? [] : Array.isArray(children) ? children : [children]; + children.forEach(child => element.appendChild(child)); +}; diff --git a/extensions/amp-byside-content/OWNERS.yaml b/extensions/amp-byside-content/OWNERS.yaml new file mode 100644 index 000000000000..c54a943b17f5 --- /dev/null +++ b/extensions/amp-byside-content/OWNERS.yaml @@ -0,0 +1 @@ +- bysidedevel3rdparty \ No newline at end of file diff --git a/extensions/amp-byside-content/amp-byside-content.md b/extensions/amp-byside-content/amp-byside-content.md new file mode 100644 index 000000000000..fed6e69118e0 --- /dev/null +++ b/extensions/amp-byside-content/amp-byside-content.md @@ -0,0 +1,91 @@ + + +# `amp-byside-content` + + + + + + + + + + + + + + + + + + + + + + +
DescriptionDisplays dynamic placeholder content from BySide service.
AvailabilityAvailable for BySide customers with a valid client id.
Required Script<script async custom-element="amp-byside-content" src="https://cdn.ampproject.org/v0/amp-byside-content-0.1.js"></script>
Supported Layoutsfill, fixed, fixed-height, flex-item, nodisplay, responsive
ExamplesSee annotated code example for amp-byside-content
+ +## Behavior + +An `amp-byside-content` component displays dynamic content that can be retrieved from the [BySide](https://www.byside.com) customization mechanisms, for a valid BySide client. + +Example: +```html + + +``` + +## Attributes + +The `data-webcare-id` and `data-label` attributes are required for the content embed to work. + +**data-webcare-id** + +The **required** BySide customer account id. + +**data-channel** + +The channel identifier to use for content validation. Defaults to empty string "". + +**data-lang** + +The language to show contents, as available in BySide customer account localization. Defaults to portuguese "pt". + +**data-fid** + +The visitor force id. Use this when a unique visitor identifier is available, usually for authenticated users. Defaults to empty "". + +**data-label** + +The **required** placeholder content label as seen in your backoffice account. + +## Validation +See [amp-byside-content rules](https://github.com/ampproject/amphtml/blob/master/extensions/amp-byside-content/validator-amp-byside-content.protoascii) in the AMP validator specification. + +## Privacy and cookies policy + +[BySide](https://www.byside.com) is committed to respect and protect your privacy and developing technology that gives you the most powerful and safe online experience. BySide privacy statement and cookies policy can be found on the following url's: + +[http://www.byside.com/privacy.html](http://www.byside.com/privacy.html) + +[http://www.byside.com/cookies.html](http://www.byside.com/cookies.html) diff --git a/extensions/amp-byside-content/validator-amp-byside-content.protoascii b/extensions/amp-byside-content/validator-amp-byside-content.protoascii new file mode 100644 index 000000000000..5039903b45a1 --- /dev/null +++ b/extensions/amp-byside-content/validator-amp-byside-content.protoascii @@ -0,0 +1,48 @@ +# +# Copyright 2018 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. +# + +tags: { # amp-byside-content + html_format: AMP + tag_name: "SCRIPT" + extension_spec: { + name: "amp-byside-content" + allowed_versions: "0.1" + allowed_versions: "latest" + } + attr_lists: "common-extension-attrs" +} +tags: { # + html_format: AMP + tag_name: "AMP-BYSIDE-CONTENT" + requires_extension: "amp-byside-content" + attrs: { + name: "data-label" + mandatory: true + } + attrs: { + name: "data-webcare-id" + mandatory: true + } + attr_lists: "extended-amp-global" + amp_layout: { + supported_layouts: FILL + supported_layouts: FIXED + supported_layouts: FIXED_HEIGHT + supported_layouts: FLEX_ITEM + supported_layouts: NODISPLAY + supported_layouts: RESPONSIVE + } +} diff --git a/gulpfile.js b/gulpfile.js index 5f084ae77985..20391318cee8 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -88,6 +88,7 @@ declareExtension('amp-auto-ads', '0.1', {hasCss: false}); declareExtension('amp-bind', '0.1', {hasCss: false}); declareExtension('amp-brid-player', '0.1', {hasCss: false}); declareExtension('amp-brightcove', '0.1', {hasCss: false}); +declareExtension('amp-byside-content', '0.1', {hasCss: true}); declareExtension('amp-kaltura-player', '0.1', {hasCss: false}); declareExtension('amp-call-tracking', '0.1', {hasCss: false}); declareExtension('amp-carousel', '0.1', {hasCss: true}); From 7c1ce8702387e508c9c8c6b71c19a43dae251982 Mon Sep 17 00:00:00 2001 From: Cathy Zhu Date: Tue, 6 Feb 2018 09:57:26 -0800 Subject: [PATCH 26/65] Hand icon for all lightboxed images (#13298) --- test/manual/article-with-lightbox-2.0-gallery.amp.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/manual/article-with-lightbox-2.0-gallery.amp.html b/test/manual/article-with-lightbox-2.0-gallery.amp.html index 1913739f98ce..fdfd97313e58 100644 --- a/test/manual/article-with-lightbox-2.0-gallery.amp.html +++ b/test/manual/article-with-lightbox-2.0-gallery.amp.html @@ -241,6 +241,11 @@ font-family: sans-serif; font-size: 0.8em; } + + amp-img[lightbox] { + cursor: pointer; + } + From 99a0424130620ac88958858b3c6484030912c53c Mon Sep 17 00:00:00 2001 From: Raghu Simha Date: Tue, 6 Feb 2018 12:58:55 -0500 Subject: [PATCH 27/65] Delete build-system/tasks/package.json, whitelist heroku page (#13295) --- build-system/tasks/check-links.js | 4 ++++ build-system/tasks/package.json | 5 ----- 2 files changed, 4 insertions(+), 5 deletions(-) delete mode 100644 build-system/tasks/package.json diff --git a/build-system/tasks/check-links.js b/build-system/tasks/check-links.js index c901b0d48e38..976a9e890810 100644 --- a/build-system/tasks/check-links.js +++ b/build-system/tasks/check-links.js @@ -143,6 +143,10 @@ function filterWhitelistedLinks(markdown) { // Links inside a block (illustrative, and not always valid) filteredMarkdown = filteredMarkdown.replace(/(.*?)<\/code>/g, ''); + // The heroku nightly build page is not always acccessible by the checker. + filteredMarkdown = filteredMarkdown.replace( + /\(http:\/\/amphtml-nightly.herokuapp.com\/\)/g, ''); + // After all whitelisting is done, clean up any remaining empty blocks bounded // by backticks. Otherwise, `` will be treated as the start of a code block // and confuse the link extractor. diff --git a/build-system/tasks/package.json b/build-system/tasks/package.json deleted file mode 100644 index 5c91c7998c7f..000000000000 --- a/build-system/tasks/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "dependencies": { - "markdown-link-check": "^3.0.3" - } -} From 2ee9b80e753d3c4fc706bc01474ed6d7fda0a622 Mon Sep 17 00:00:00 2001 From: Ali Ghassemi Date: Tue, 6 Feb 2018 09:59:43 -0800 Subject: [PATCH 28/65] amp-fx-collection: documentation (#13268) --- .../amp-fx-collection/amp-fx-collection.md | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 extensions/amp-fx-collection/amp-fx-collection.md diff --git a/extensions/amp-fx-collection/amp-fx-collection.md b/extensions/amp-fx-collection/amp-fx-collection.md new file mode 100644 index 000000000000..ea52592e5cbc --- /dev/null +++ b/extensions/amp-fx-collection/amp-fx-collection.md @@ -0,0 +1,71 @@ + + +# `amp-fx-collection` + +[TOC] + + + + + + + + + + + + + + +
DescriptionProvides a collection of preset visual effects, such as parallax.
Required Script<script async custom-element="amp-fx-collection" src="https://cdn.ampproject.org/v0/amp-fx-collection-0.1.js"></script>
Supported Layoutsnodisplay
+ +## Overview + +The `amp-fx-collection` extension provides a collection of preset visual effects, +such as parallax that can be easily enabled on any element via attributes. + +Currently, only the `parallax` effect is supported. More effects such as `fade-in`, `slide-in` +are planned to be supported soon. + +### parallax +The `parallax` effect allows an element to move as if it is nearer or farther relative +to the foreground of the page content. As the user scrolls the page, the element +scrolls faster or slower depending on the value assigned to the +`data-parallax-factor` attribute. + +Example: +```html +

+ A title that moves faster than other content +

+``` + +#### Attributes + +##### data-parallax-factor + +Specifies a decimal value that controls how much faster or slower the element scrolls +relative to the scrolling speed: + +- A value greater than 1 scrolls the element upward (element scrolls faster) when the user scrolls down the page. +- A value less than 1 scrolls the element downward (element scrolls slower) when the user scrolls downward. +- A value of 1 behaves normally. +- A value of 0 effectively makes the element scroll fixed with the page. + +## Validation + +See [amp-fx-collection rules](https://github.com/ampproject/amphtml/blob/master/extensions/amp-fx-collection/validator-amp-fx-collection.protoascii) in the AMP validator specification. From 0a55db7a18dbac85135a5b5ad7c46200464870ec Mon Sep 17 00:00:00 2001 From: Matt Kendall Date: Tue, 6 Feb 2018 14:26:44 -0500 Subject: [PATCH 29/65] Added flag indicated renderStart implemented (#13062) --- ads/_config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/ads/_config.js b/ads/_config.js index a8572ff72e4d..cf0f2253791e 100644 --- a/ads/_config.js +++ b/ads/_config.js @@ -232,6 +232,7 @@ export const adConfig = { appnexus: { prefetch: 'https://acdn.adnxs.com/ast/ast.js', preconnect: 'https://ib.adnxs.com', + renderStartImplemented: true, }, atomx: { From 7ca49a954f10d3ed531f015c144161410f950921 Mon Sep 17 00:00:00 2001 From: Cathy Zhu Date: Tue, 6 Feb 2018 11:45:20 -0800 Subject: [PATCH 30/65] Lightbox: only apply ghost to images going through transitions (#13301) * Only apply ghost to images going through transitions * Fix merge typo --- extensions/amp-lightbox-gallery/0.1/amp-lightbox-gallery.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/extensions/amp-lightbox-gallery/0.1/amp-lightbox-gallery.js b/extensions/amp-lightbox-gallery/0.1/amp-lightbox-gallery.js index 0c1f0cd4f7de..b2b60802b652 100644 --- a/extensions/amp-lightbox-gallery/0.1/amp-lightbox-gallery.js +++ b/extensions/amp-lightbox-gallery/0.1/amp-lightbox-gallery.js @@ -783,7 +783,7 @@ export class AmpLightboxGallery extends AMP.BaseElement { return this.vsync_.mutatePromise(() => { st.setStyles(this.element, {opacity: ''}); st.setStyles(dev().assertElement(this.carousel_), {opacity: ''}); - + sourceElement.classList.remove('i-amphtml-ghost'); if (transLayer) { this.element.ownerDocument.body.removeChild(transLayer); } @@ -820,8 +820,10 @@ export class AmpLightboxGallery extends AMP.BaseElement { && this.shouldAnimate_(sourceElement) && (sourceElement == this.sourceElement_ || this.manager_.hasCarousel(this.currentLightboxGroupId_))) { + + sourceElement.classList.add('i-amphtml-ghost'); transLayer = this.element.ownerDocument.createElement('div'); - transLayer.classList.add('i-amphtml-lightbox-viewer-trans'); + transLayer.classList.add('i-amphtml-lightbox-gallery-trans'); this.element.ownerDocument.body.appendChild(transLayer); const rect = layoutRectFromDomRect(sourceElement From ad9739892f7051efa19b8aa2d3f4d11bb8895086 Mon Sep 17 00:00:00 2001 From: William Chou Date: Tue, 6 Feb 2018 15:59:26 -0500 Subject: [PATCH 31/65] Remove unused A2A functionality for ALP (#13297) * Remove unused, deprecated A2A functionality for ALP, including alp-for-a4a experiment. * fix lint --- extensions/amp-a4a/0.1/amp-a4a.js | 21 +--- extensions/amp-ad/0.1/a2a-listener.js | 84 ------------- extensions/amp-ad/0.1/amp-ad-3p-impl.js | 3 - .../amp-ad/0.1/test/test-a2a-listener.js | 118 ------------------ tools/experiments/experiments.js | 5 - 5 files changed, 1 insertion(+), 230 deletions(-) delete mode 100644 extensions/amp-ad/0.1/a2a-listener.js delete mode 100644 extensions/amp-ad/0.1/test/test-a2a-listener.js diff --git a/extensions/amp-a4a/0.1/amp-a4a.js b/extensions/amp-a4a/0.1/amp-a4a.js index 3c7d3cf50caa..4b1d099d9442 100644 --- a/extensions/amp-a4a/0.1/amp-a4a.js +++ b/extensions/amp-a4a/0.1/amp-a4a.js @@ -36,13 +36,12 @@ import { incrementLoadingAds, is3pThrottled, } from '../../amp-ad/0.1/concurrent-load'; -import {getBinaryType, isExperimentOn} from '../../../src/experiments'; +import {getBinaryType} from '../../../src/experiments'; import {getBinaryTypeNumericalCode} from '../../../ads/google/a4a/utils'; import {getContextMetadata} from '../../../src/iframe-attributes'; import {getMode} from '../../../src/mode'; // TODO(tdrl): Temporary. Remove when we migrate to using amp-analytics. import {getTimingDataAsync} from '../../../src/service/variable-source'; -import {handleClick} from '../../../ads/alp/handler'; import {insertAnalyticsElement} from '../../../src/extension-analytics'; import { installFriendlyIframeEmbed, @@ -1371,8 +1370,6 @@ export class AmpA4A extends AMP.BaseElement { const frameDoc = friendlyIframeEmbed.iframe.contentDocument || friendlyIframeEmbed.win.document; setStyle(frameDoc.body, 'visibility', 'visible'); - // Bubble phase click handlers on the ad. - this.registerAlpHandler_(friendlyIframeEmbed.win); // Capture timing info for friendly iframe load completion. getTimingDataAsync( friendlyIframeEmbed.win, @@ -1609,22 +1606,6 @@ export class AmpA4A extends AMP.BaseElement { } } - /** - * Registers a click handler for "A2A" (AMP-to-AMP navigation where the AMP - * viewer navigates to an AMP destination on our behalf. - * @param {!Window} iframeWin - */ - registerAlpHandler_(iframeWin) { - if (!isExperimentOn(this.win, 'alp-for-a4a')) { - return; - } - iframeWin.document.documentElement.addEventListener('click', event => { - handleClick(event, url => { - Services.viewerForDoc(this.getAmpDoc()).navigateTo(url, 'a4a'); - }); - }); - } - /** * @return {string} full url to safeframe implementation. * @private diff --git a/extensions/amp-ad/0.1/a2a-listener.js b/extensions/amp-ad/0.1/a2a-listener.js deleted file mode 100644 index e419d78fe674..000000000000 --- a/extensions/amp-ad/0.1/a2a-listener.js +++ /dev/null @@ -1,84 +0,0 @@ -/* Copyright 2016 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 {Services} from '../../../src/services'; -import {closestByTag} from '../../../src/dom'; -import {getData} from '../../../src/event-helper'; -import {isExperimentOn} from '../../../src/experiments'; -import {isProxyOrigin} from '../../../src/url'; -import {parseJson} from '../../../src/json'; -import {user} from '../../../src/log'; - -/** - * Sets up a special document wide listener that relays requests - * from ad iframes to the viewer to perform A2A navigations. - * Requests are denied if the requests come from iframes that - * do not currently have focus. - * @param {!Window} win - */ -export function setupA2AListener(win) { - if (win.a2alistener) { - return; - } - win.a2alistener = true; - if (!isExperimentOn(win, 'alp')) { - return; - } - win.addEventListener('message', handleMessageEvent.bind(null, win)); -} - -/** - * Handles a potential a2a message. - * @param {!Window} win - * @param {!Event} event - * @visibleForTesting - */ -export function handleMessageEvent(win, event) { - const data = getData(event); - // Only handle messages starting with the magic string. - if (typeof data != 'string' || data.indexOf('a2a;') != 0) { - return; - } - const origin = event.origin; - const nav = parseJson(data.substr(/* 'a2a;'.length */ 4)); - const url = nav['url']; - const source = event.source; - const activeElement = win.document.activeElement; - // Check that the active element is an iframe. - user().assert(activeElement.tagName == 'IFRAME', - 'A2A request with invalid active element %s %s %s', - activeElement, url, origin); - let found = false; - let sourceParent = source; - const activeWindow = activeElement.contentWindow; - while (sourceParent != win.top) { - if (sourceParent == activeWindow) { - found = true; - break; - } - sourceParent = sourceParent.parent; - } - // Check that the active iframe contains the iframe that sent us - // the message. - user().assert(found, - 'A2A request from invalid source win %s %s', url, origin); - // Check that the iframe is contained in an ad. - user().assert(closestByTag(activeElement, 'amp-ad'), - 'A2A request from non-ad frame %s %s', url, origin); - // We only allow AMP shaped URLs. - user().assert(isProxyOrigin(url), 'Invalid ad A2A URL %s %s', - url, origin); - Services.viewerForDoc(win.document).navigateTo(url, 'ad-' + origin); -} diff --git a/extensions/amp-ad/0.1/amp-ad-3p-impl.js b/extensions/amp-ad/0.1/amp-ad-3p-impl.js index 26b231cc3e13..1dfb1b64dc5a 100644 --- a/extensions/amp-ad/0.1/amp-ad-3p-impl.js +++ b/extensions/amp-ad/0.1/amp-ad-3p-impl.js @@ -38,7 +38,6 @@ import { import {isLayoutSizeDefined} from '../../../src/layout'; import {moveLayoutRect} from '../../../src/layout-rect'; import {preloadBootstrap} from '../../../src/3p-frame'; -import {setupA2AListener} from './a2a-listener'; import {toWin} from '../../../src/types'; /** @const {string} Tag name for 3P AD implementation. */ @@ -169,8 +168,6 @@ export class AmpAd3PImpl extends AMP.BaseElement { this.uiHandler = new AmpAdUIHandler(this); - setupA2AListener(this.win); - this.isFullWidthRequested_ = this.shouldRequestFullWidth_(); if (this.isFullWidthRequested_) { diff --git a/extensions/amp-ad/0.1/test/test-a2a-listener.js b/extensions/amp-ad/0.1/test/test-a2a-listener.js deleted file mode 100644 index 02444c7a19bc..000000000000 --- a/extensions/amp-ad/0.1/test/test-a2a-listener.js +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Copyright 2016 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 * as sinon from 'sinon'; -import {handleMessageEvent} from '../a2a-listener'; -import {installDocService} from '../../../../src/service/ampdoc-impl'; - -describe('amp-ad a2a listener', function() { - let sandbox; - let event; - let win; - let findClosestAd; - let navigateTo; - beforeEach(() => { - sandbox = sinon.sandbox.create(); - const source = {}; - findClosestAd = true; - event = { - type: 'message', - origin: 'https://foo.com', - source, - data: 'a2a;' + JSON.stringify({ - url: 'https://cdn.ampproject.org/c/test', - }), - }; - navigateTo = sandbox.stub(); - win = { - document: { - nodeType: /* DOCUMENT */ 9, - activeElement: { - tagName: 'IFRAME', - contentWindow: source, - closest: tagName => { - return findClosestAd && tagName == 'amp-ad'; - }, - }, - }, - services: { - viewer: { - obj: { - navigateTo, - }, - }, - }, - }; - win.document.defaultView = win; - installDocService(win, /* isSingleDoc */ true); - }); - - afterEach(() => { - sandbox.restore(); - }); - - function expectNavigation() { - expect(navigateTo).to.be.calledOnce; - expect(navigateTo.lastCall.args[0]).to.equal( - 'https://cdn.ampproject.org/c/test'); - expect(navigateTo.lastCall.args[1]).to.equal( - 'ad-https://foo.com'); - } - - it('should ignore other messages', () => { - event.data = {}; - handleMessageEvent(win, event); - event.data = '{}'; - handleMessageEvent(win, event); - expect(navigateTo).to.have.not.been.called; - }); - - it('should initiate navigation', () => { - handleMessageEvent(win, event); - expectNavigation(); - }); - - it('should initiate navigation (nested frames)', () => { - const source = win.document.activeElement.contentWindow; - const parent = win.document.activeElement.contentWindow = {}; - source.parent = parent; - handleMessageEvent(win, event); - expectNavigation(); - }); - - it('should fail for invalid active element', () => { - win.document.activeElement.tagName = 'INPUT'; - expect(() => { - handleMessageEvent(win, event); - }).to.throw(/A2A request with invalid active element/); - }); - - it('should fail for invalid url', () => { - event.data = 'a2a;' + JSON.stringify({ - url: 'https://test.com/c/test', - }); - expect(() => { - handleMessageEvent(win, event); - }).to.throw(/Invalid ad A2A URL/); - }); - - it('should fail for invalid url', () => { - findClosestAd = false; - expect(() => { - handleMessageEvent(win, event); - }).to.throw(/A2A request from non-ad frame/); - }); -}); diff --git a/tools/experiments/experiments.js b/tools/experiments/experiments.js index a477ef680d52..f6255ac6809f 100644 --- a/tools/experiments/experiments.js +++ b/tools/experiments/experiments.js @@ -144,11 +144,6 @@ const EXPERIMENTS = [ spec: 'https://github.com/ampproject/amphtml/issues/6106', cleanupIssue: 'https://github.com/ampproject/amphtml/pull/6351', }, - { - id: 'alp-for-a4a', - name: 'Enable redirect to landing page directly for A4A', - spec: 'https://github.com/ampproject/amphtml/issues/5212', - }, { id: 'ios-embed-wrapper', name: 'A new iOS embedded viewport model that wraps the body into' + From 14e352a628261124a702c2700e069a8f36a8c8ee Mon Sep 17 00:00:00 2001 From: Cathy Zhu Date: Tue, 6 Feb 2018 13:28:14 -0800 Subject: [PATCH 32/65] Add basic documentation for amp-lightbox-gallery (#13272) * Add basic documentation for amp-lightbox-gallery * Update docs to address comments * Wording cleanup * Add documentation for descriptions * Update amp-lightbox-gallery.md --- .../amp-lightbox-gallery.md | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 extensions/amp-lightbox-gallery/amp-lightbox-gallery.md diff --git a/extensions/amp-lightbox-gallery/amp-lightbox-gallery.md b/extensions/amp-lightbox-gallery/amp-lightbox-gallery.md new file mode 100644 index 000000000000..fd6ee147fb6c --- /dev/null +++ b/extensions/amp-lightbox-gallery/amp-lightbox-gallery.md @@ -0,0 +1,106 @@ + + +# `amp-lightbox-gallery` +[TOC] + + + + + + + + + + + + + + + + + + +
DescriptionProvides a "lightbox” experience. Upon user interaction, a UI component expands to fill the viewport until it is closed by the user.
AvailabilityExperimental; no validations yet.
Required Script<script async custom-element="amp-lightbox-gallery" src="https://cdn.ampproject.org/v0/amp-lightbox-gallery-0.1.js"></script>
Supported Layoutsnodisplay
+ +## Overview + +The `amp-lightbox-gallery` component provides a "lightbox” experience for AMP components (e.g., `amp-img`, `amp-carousel`). When the user interacts with the AMP element, a UI component expands to fill the viewport until it is closed by the user. Currently, only images are supported. + +## Usage + +To use `amp-lightbox-gallery`, ensure the required script is included in your `` section, then add the `lightbox` attribute on an `` or `` element. A typical usage looks like this: + +### Lightbox with `` + +```html + + +``` + + Tapping on any `` will open the image in a lightbox gallery. The lightbox gallery does image-handling (e.g. zoom and pan), enables swiping to navigate between images, and offers a thumbnail gallery view for browsing all picture thumbnails in a grid. + +### Lightbox with `` + +```html + + + + + +``` + +You can add the `lightbox` attribute on an `` to lightbox all of its children. Currently, the `` component only supports carousels containing `` as children. As you navigate through the carousel items in the lightbox, the original carousel slides are synchronized so that when the lightbox is closed, the user ends up on the same slide as the were originally on. Currently, only the `type='slides'` carousel is supported. + +### Captions + +Optionally, you can specify a caption for each element in the lightbox. These fields are automatically read and displayed by the `` in the following order of priority: + +- `figcaption` (if the lightboxed element is the child of a figure) +- `aria-describedby` +- `alt` +- `aria-label` +- `aria-labelledby` + +#### Example 1: Using figcaption for description + +In this example, `` displays the `figcaption` value as its description, showing "Toront's CN tower was ....". + +```html +
+ + +
+ Toronto's CN tower was built in 1976 and was the tallest free-standing structure until 2007. +
+
+``` + +#### Example 2: Using alt for description + +In this example, `` displays the `alt` value as its description, showing "Picture of CN tower". +```html + + +``` From 9125cfbbfc9d5a4087576e8b6447f3d945f227b0 Mon Sep 17 00:00:00 2001 From: Raghu Simha Date: Tue, 6 Feb 2018 18:05:11 -0500 Subject: [PATCH 33/65] Don't cancel watch if an edit causes a compilation error (#13312) --- gulpfile.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 20391318cee8..22078f610176 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -993,7 +993,7 @@ function compileJs(srcDir, srcFilename, destDir, options) { .pipe(gulp.dest.bind(gulp), destDir); const destFilename = options.toName || srcFilename; - function rebundle() { + function rebundle(failOnError) { const startTime = Date.now(); return toPromise( bundler.bundle() @@ -1001,7 +1001,11 @@ function compileJs(srcDir, srcFilename, destDir, options) { // Drop the node_modules call stack, which begins with ' at'. const message = err.stack.replace(/ at[^]*/, '').trim(); console.error(red(message)); - process.exit(1); + if (failOnError) { + process.exit(1); + } else { + endBuildStep('Error while compiling', srcFilename, startTime); + } }) .pipe(lazybuild()) .pipe($$.rename(destFilename)) @@ -1029,7 +1033,7 @@ function compileJs(srcDir, srcFilename, destDir, options) { if (options.watch) { bundler.on('update', function() { - rebundle(); + rebundle(/* failOnError */ false); // Touch file in unit test set. This triggers rebundling of tests because // karma only considers changes to tests files themselves re-bundle // worthy. @@ -1046,7 +1050,7 @@ function compileJs(srcDir, srcFilename, destDir, options) { } else { // This is the default options.watch === true case, and also covers the // `gulp build` / `gulp dist` cases where options.watch is undefined. - return rebundle(); + return rebundle(/* failOnError */ true); } } From dcfc50b19501d228510f52d5da9f23f135f15a03 Mon Sep 17 00:00:00 2001 From: "Taymon A. Beal" Date: Tue, 6 Feb 2018 18:12:32 -0500 Subject: [PATCH 34/65] Tweak the Git instructions (#13112) There are easier ways to handle a few of the tasks in the contributing instructions. In particular, it's best to have master track upstream, since otherwise you have two separate remote masters to keep track of. Additionally, a few instructions (like the first git push) don't quite work as currently specified; this fixes them. --- contributing/getting-started-e2e.md | 41 +++++++++++++++++++-------- contributing/getting-started-quick.md | 16 +++++------ 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/contributing/getting-started-e2e.md b/contributing/getting-started-e2e.md index 233f67f871d0..f84c03260d14 100644 --- a/contributing/getting-started-e2e.md +++ b/contributing/getting-started-e2e.md @@ -148,14 +148,21 @@ git remote add upstream git@github.com:ampproject/amphtml.git Now run `git remote -v` again and notice that you have set up your upstream alias. +Each branch of your local Git repository can track a branch of a remote repository. Right now, your local `master` branch is tracking `origin/master`, which corresponds to the `master` branch of your GitHub fork. You don't actually want this, though; the upstream `master` branch is constantly being updated, and your fork's `master` branch will rapidly become outdated. Instead, it's best to make your local `master` branch track the upstream `master` branch. You can do this like so: + +``` +git branch -u upstream/master master +``` + # Building AMP and starting a local server Now that you have all of the files copied locally you can actually build the code and run a local server to try things out. amphtml uses Node.js, the Yarn package manager and the Gulp build system to build amphtml and start up a local server that lets you try out your changes. Installing these and getting amphtml built is straightforward: -* Install [NodeJS](https://nodejs.org/) version >= 6 (which includes npm) +* Install [Node.js](https://nodejs.org/) version >= 6 (which includes npm). + [NVM](https://github.com/creationix/nvm) is a convenient way to do this on Mac and Linux, especially if you have other projects that require different versions of Node. * Install [Yarn](https://yarnpkg.com/) version >= 1.2.0 (instructions [here](https://yarnpkg.com/en/docs/install), this may require elevated privileges using `sudo` on some platforms) @@ -169,7 +176,9 @@ amphtml uses Node.js, the Yarn package manager and the Gulp build system to buil You can do this by adding this line to your hosts file (`/etc/hosts` on Mac or Linux, `%SystemRoot%\System32\drivers\etc\hosts` on Windows): - ```127.0.0.1 ads.localhost iframe.localhost``` + ``` + 127.0.0.1 ads.localhost iframe.localhost + ``` * The AMP Project uses Gulp as our build system. Gulp uses a configuration file ([gulpfile.js](https://github.com/ampproject/amphtml/blob/master/gulpfile.js)) to build amphtml (including the amphtml javascript) and to start up the Node.js server with the proper settings. You don't really have to understand exactly what it is doing at this point--you just have to install it and use it. @@ -220,10 +229,14 @@ By default you'll have a branch named _master_. You can see this if you run the Although you could do work on the master branch, most people choose to leave the master branch unchanged and create other branches to actually do work in. Creating a branch is easy; simply run: ``` -git branch --track origin/master +git checkout -b master ``` -Whenever you want to do work in this branch, run the checkout command: +This will move you to the new branch, which uses `master` as its start point, meaning that it will start out containing the same files as `master`. You can then start working in the new branch. + +(You can use a different branch as a start point, like if you want to make one branch based on another. Generally, though, you want `master` as your start point. If you omit the start point, Git will use whichever branch you're currently on.) + +Whenever you want to move to a different branch, run the checkout command: ``` git checkout @@ -235,22 +248,20 @@ You can see a list of your branches and which one you're currently in by running git branch ``` -When you created the branch the `--track` flag and `origin/master` part are a convenience for telling Git the default place you want to sync with in the future. Remember _origin_ is the alias that was set up for your GitHub fork remote repository. _origin/master_ is "the master branch of the origin repository." - Note that currently the branch you just created only exists in your local repository. If you check the list of branches that exist on your GitHub fork at `https://github.com//amphtml/branches/yours`, you won't see this new branch listed. Later on when we want to make the changes in your branch visible to others (e.g. so you can do a pull request) we'll push this branch to your GitHub fork. # Pull the latest changes from the amphtml repository Since your local repository is just a copy of the amphtml repository it can quickly become out of date if other people make changes to the amphtml repository. Before you start making changes you'll want to make sure you have the latest version of the code; you'll also want to do this periodically during development, before sending your code for review, etc. -In the workflow we will be using you'll go to the master branch on your local repository and pull the latest changes in from the remote amphtml repository's master branch. (Remember that you set up the alias _upstream_ to refer to the remote amphtml repository.) +In the workflow we will be using you'll go to the master branch on your local repository and pull the latest changes in from the remote amphtml repository's master branch. (Remember that you set up the alias _upstream_ to refer to the remote amphtml repository, and you set your local `master` branch to track `upstream/master`.) ``` # make sure you are in your local repo's master branch git checkout master # pull in the latest changes from the remote amphtml repository -git pull upstream master +git pull ``` If there have been any changes you'll see the details of what changed, otherwise you'll see a message like `Already up-to-date`. @@ -399,15 +410,21 @@ Before pushing your changes, make sure you have the latest changes in the amphtm ``` git checkout master -git pull upstream master +git pull git checkout git rebase master ``` -Now push your changes to origin (the alias for your GitHub fork): +Now push your changes to `origin` (the alias for your GitHub fork): + +``` +git push -u origin +``` + +`-u origin ` tells Git to create a remote branch with the specified name in `origin` and to make your local branch track that remote branch from now on. You only have to do this the first time you push each branch. For subsequent pushes on the same branch, you can use a shorter command: ``` -git push origin +git push ``` The changes you've made are now visible on GitHub! Go to your fork on GitHub: @@ -485,7 +502,7 @@ git checkout master git branch -D # delete the branch in your GitHub fork (if you didn't use the UI) -git push origin --delete +git push -d origin ``` # See your changes in production diff --git a/contributing/getting-started-quick.md b/contributing/getting-started-quick.md index 14d8943cca83..8885f271d01c 100644 --- a/contributing/getting-started-quick.md +++ b/contributing/getting-started-quick.md @@ -25,7 +25,7 @@ This Quick Start guide is the TL;DR version of the longer [end-to-end guide](get * [Install and set up Git](https://help.github.com/articles/set-up-git/); in the "Authenticating" step of that page use SSH instead of HTTPS -* Install [NodeJS](https://nodejs.org/) version >= 6 (which includes npm) +* Install [Node.js](https://nodejs.org/) version >= 6 (which includes npm); [NVM](https://github.com/creationix/nvm) is a convenient way to do this on Mac and Linux * Install [Yarn](https://yarnpkg.com/) version >= 1.2.0 (instructions [here](https://yarnpkg.com/en/docs/install), this may require elevated privileges using `sudo` on some platforms) @@ -34,18 +34,17 @@ This Quick Start guide is the TL;DR version of the longer [end-to-end guide](get * Add this line to your hosts file (`/etc/hosts` on Mac or Linux, `%SystemRoot%\System32\drivers\etc\hosts` on Windows): ``` - 127.0.0.1 ads.localhost iframe.localhost + 127.0.0.1 ads.localhost iframe.localhost ``` * Fork the [amphtml repository](https://github.com/ampproject/amphtml) by clicking "Fork" in the Web UI. * Create your local repository: `git clone git@github.com:/amphtml.git` -* Add an alias: Go to the newly created local repository directory and run `git remote add upstream git@github.com:ampproject/amphtml.git` +* Add an alias: Go to the newly created local repository directory and run `git remote add upstream git@github.com:ampproject/amphtml.git` and then `git branch -u upstream/master master` # Branch (do this each time you want a new branch) -* Create the branch: `git branch --track origin/master` -* Go to the branch: `git checkout ` +* Create and go to the branch: `git checkout -b master` # Build AMP & run a local server @@ -73,7 +72,7 @@ This Quick Start guide is the TL;DR version of the longer [end-to-end guide](get # Pull the latest changes * `git checkout master` -* `git pull upstream master` +* `git pull` * `git checkout ` * `git rebase master` * Note that you may need to resolve conflicting changes at this point @@ -82,10 +81,11 @@ This Quick Start guide is the TL;DR version of the longer [end-to-end guide](get * Pull the latest changes as described above * `git checkout ` -* `git push origin ` +* `git push -u origin ` * Go to [https://github.com/ampproject/amphtml](https://github.com/ampproject/amphtml) and in the banner indicating you've recently pushed a branch, click the "Compare & pull request" (if this banner does not appear, go to your fork at `https://github.com//amphtml`, choose your branch from the "Branch" dropdown and click "New pull request") * Make sure you've signed the CLA (using the same email address as your git config indicates) * If your reviewer requests changes make them locally and then repeat the steps in this section to push the changes to your branch back up to GitHub again +* For pushes after the first, just use `git push` * If you don't get a new review within 2 business days, feel free to ping the pull request by adding a comment * Once approved your changes are merged into the amphtml repository by a core committer (you don't do this merge) @@ -93,7 +93,7 @@ This Quick Start guide is the TL;DR version of the longer [end-to-end guide](get * Go to the master branch: `git checkout master` * Delete your local branch: `git branch -D ` -* Delete the GitHub fork branch: `git push origin --delete ` +* Delete the GitHub fork branch: `git push -d origin ` # See your changes in production From 181eb76daacea608493a2f26696116d9b18cf4dc Mon Sep 17 00:00:00 2001 From: Raghu Simha Date: Tue, 6 Feb 2018 19:05:49 -0500 Subject: [PATCH 35/65] Eslint: Remove unnecessary semicolons (#13323) --- .eslintrc | 1 + 3p/ampcontext.js | 8 ++++---- 3p/environment.js | 2 +- 3p/frame-metadata.js | 2 +- 3p/integration.js | 2 +- 3p/twitter.js | 2 +- ads/google/a4a/test/test-performance.js | 2 +- ads/google/a4a/test/test-utils.js | 2 +- ads/google/a4a/utils.js | 6 +++--- ads/google/adsense-amp-auto-ads.js | 2 +- ads/google/imaVideo.js | 2 +- ads/google/test/test-doubleclick.js | 2 +- ads/imedia.js | 2 +- ads/ix.js | 2 +- ads/navegg.js | 2 +- ads/netletix.js | 2 +- ads/sogouad.js | 2 +- build-system/check-package-manager.js | 2 +- builtins/amp-img.js | 2 +- extensions/amp-a4a/0.1/amp-a4a.js | 4 ++-- extensions/amp-a4a/0.1/callout-vendors.js | 2 +- extensions/amp-access/0.1/test/test-amp-access.js | 4 ++-- extensions/amp-ad/0.1/amp-ad-xorigin-iframe-handler.js | 2 +- extensions/amp-analytics/0.1/activity-impl.js | 2 +- extensions/amp-analytics/0.1/requests.js | 2 +- extensions/amp-analytics/0.1/resource-timing.js | 2 +- .../amp-analytics/0.1/test/test-amp-analytics.js | 2 +- .../amp-analytics/0.1/test/test-resource-timing.js | 4 ++-- .../amp-analytics/0.1/test/test-visibility-model.js | 2 +- extensions/amp-auto-ads/0.1/attributes.js | 2 +- extensions/amp-auto-ads/0.1/test/test-placement.js | 2 +- .../amp-bind/0.1/test/integration/test-bind-impl.js | 2 +- extensions/amp-byside-content/0.1/utils.js | 6 +++--- extensions/amp-date-picker/0.1/date-picker-common.js | 2 +- extensions/amp-fit-text/0.1/amp-fit-text.js | 2 +- extensions/amp-fx-collection/0.1/amp-fx-collection.js | 2 +- extensions/amp-fx-collection/0.1/providers/parallax.js | 2 +- .../amp-gwd-animation/0.1/amp-gwd-animation-impl.js | 6 +++--- extensions/amp-gwd-animation/0.1/amp-gwd-animation.js | 2 +- extensions/amp-iframe/0.1/amp-iframe.js | 2 +- extensions/amp-nexxtv-player/0.1/amp-nexxtv-player.js | 2 +- .../amp-ooyala-player/0.1/test/test-amp-ooyala.js | 2 +- extensions/amp-pinterest/0.1/pin-widget.js | 2 +- extensions/amp-pinterest/0.1/pinit-button.js | 2 +- .../amp-pinterest/0.1/test/test-amp-pinterest.js | 4 ++-- extensions/amp-pinterest/0.1/util.js | 8 ++++---- extensions/amp-playbuzz/0.1/utils.js | 6 +++--- extensions/amp-selector/0.1/test/test-amp-selector.js | 2 +- extensions/amp-sidebar/0.1/test/test-amp-sidebar.js | 2 +- extensions/amp-story/0.1/media-pool.js | 6 +++--- .../0.1/examples/amp-viewer-host.js | 6 +++--- .../amp-viewer-integration/0.1/messaging/messaging.js | 2 +- .../viewer-initiated-handshake-viewer-for-testing.js | 8 ++++---- .../0.1/test/webview-viewer-for-testing.js | 10 +++++----- src/chunk.js | 4 ++-- src/cookies.js | 2 +- src/curve.js | 2 +- src/custom-element.js | 2 +- src/mediasession-helper.js | 6 +++--- src/polyfills/math-sign.js | 2 +- src/service/ampdoc-impl.js | 2 +- src/service/batched-xhr-impl.js | 2 +- src/service/layers-impl.js | 2 +- src/service/platform-impl.js | 4 ++-- src/service/resources-impl.js | 2 +- src/service/standard-actions-impl.js | 2 +- src/service/timer-impl.js | 2 +- src/service/url-replacements-impl.js | 2 +- src/service/variable-source.js | 2 +- src/service/video-manager-impl.js | 4 ++-- src/service/xhr-impl.js | 2 +- src/services.js | 2 +- src/size-list.js | 2 +- src/string.js | 2 +- src/style-installer.js | 2 +- src/utils/bytes.js | 4 ++-- src/utils/dom-fingerprint.js | 4 ++-- src/utils/math.js | 4 ++-- test/functional/test-dom.js | 2 +- test/functional/test-extension-analytics.js | 4 ++-- test/functional/test-extensions.js | 2 +- test/functional/test-runtime.js | 2 +- test/integration/test-amp-bind.js | 2 +- test/integration/test-position-observer.js | 6 +++--- 84 files changed, 124 insertions(+), 123 deletions(-) diff --git a/.eslintrc b/.eslintrc index 0e48c800345d..f650756a2f43 100644 --- a/.eslintrc +++ b/.eslintrc @@ -62,6 +62,7 @@ "no-export-side-effect": 2, "no-extend-native": 2, "no-extra-bind": 2, + "no-extra-semi": 2, "no-for-of-statement": 2, "no-implicit-coercion": [2, { "boolean": false }], "no-implied-eval": 2, diff --git a/3p/ampcontext.js b/3p/ampcontext.js index 9e8768396e3d..ff82728ef4cc 100644 --- a/3p/ampcontext.js +++ b/3p/ampcontext.js @@ -178,7 +178,7 @@ export class AbstractAmpContext { }); return unlisten; - }; + } /** * Requests HTML snippet from the parent window. @@ -216,7 +216,7 @@ export class AbstractAmpContext { 'width': width, 'height': height, })); - }; + } /** * Allows a creative to set the callback function for when the resize @@ -228,7 +228,7 @@ export class AbstractAmpContext { onResizeSuccess(callback) { this.client_.registerCallback(MessageType.EMBED_SIZE_CHANGED, obj => { callback(obj['requestedHeight'], obj['requestedWidth']); }); - }; + } /** * Allows a creative to set the callback function for when the resize @@ -241,7 +241,7 @@ export class AbstractAmpContext { this.client_.registerCallback(MessageType.EMBED_SIZE_DENIED, obj => { callback(obj['requestedHeight'], obj['requestedWidth']); }); - }; + } /** * Takes the current name on the window, and attaches it to diff --git a/3p/environment.js b/3p/environment.js index e1561b459563..c1350cdc03df 100644 --- a/3p/environment.js +++ b/3p/environment.js @@ -261,4 +261,4 @@ export function installEmbedStateListener() { listenParent(window, 'embed-state', function(data) { inViewport = data.inViewport; }); -}; +} diff --git a/3p/frame-metadata.js b/3p/frame-metadata.js index baae596b103c..2292975e84f2 100644 --- a/3p/frame-metadata.js +++ b/3p/frame-metadata.js @@ -155,7 +155,7 @@ export function getContextState() { startTime: rawContext['startTime'], tagName: rawContext['tagName'], }; -}; +} /** diff --git a/3p/integration.js b/3p/integration.js index e09338ecec91..e697bbb1c298 100644 --- a/3p/integration.js +++ b/3p/integration.js @@ -462,7 +462,7 @@ export function draw3p(win, data, configCallback) { } else { run(type, win, data); } -}; +} /** * @return {boolean} Whether this is the master iframe. diff --git a/3p/twitter.js b/3p/twitter.js index 362eff28c580..2bedeacc94ac 100644 --- a/3p/twitter.js +++ b/3p/twitter.js @@ -122,4 +122,4 @@ export function cleanupTweetId_(tweetid) { } return tweetid; -}; +} diff --git a/ads/google/a4a/test/test-performance.js b/ads/google/a4a/test/test-performance.js index ebdaa8cf8117..b4775e78a7e6 100644 --- a/ads/google/a4a/test/test-performance.js +++ b/ads/google/a4a/test/test-performance.js @@ -217,7 +217,7 @@ describe('GoogleAdLifecycleReporter', () => { /&st=30/.test(src)); slotCounts[slotId] = slotCounts[slotId] || 0; ++slotCounts[slotId]; - }; + } // SlotId 0 corresponds to unusedReporter, so ignore it. for (let s = 1; s <= nSlots; ++s) { expect(slotCounts[s], 'slotCounts[' + s + ']').to.equal(nStages); diff --git a/ads/google/a4a/test/test-utils.js b/ads/google/a4a/test/test-utils.js index a59c6a2c2ced..e529debac397 100644 --- a/ads/google/a4a/test/test-utils.js +++ b/ads/google/a4a/test/test-utils.js @@ -34,7 +34,7 @@ import { } from '../utils'; import { MockA4AImpl, -} from '../../../../extensions/amp-a4a/0.1/test/utils';; +} from '../../../../extensions/amp-a4a/0.1/test/utils'; import {Services} from '../../../../src/services'; import {buildUrl} from '../url-builder'; import {createElementWithAttributes} from '../../../../src/dom'; diff --git a/ads/google/a4a/utils.js b/ads/google/a4a/utils.js index 0327de7f143b..c27b014bfbb4 100644 --- a/ads/google/a4a/utils.js +++ b/ads/google/a4a/utils.js @@ -426,7 +426,7 @@ export function additionalDimensions(win, viewportSize) { outerHeight, innerWidth, innerHeight].join(); -}; +} /** * Returns amp-analytics config for a new CSI trigger. @@ -727,7 +727,7 @@ export function getIdentityToken(win, nodeOrDoc) { win['goog_identity_prom'] = win['goog_identity_prom'] || executeIdentityTokenFetch(win, nodeOrDoc); return /** @type {!Promise} */(win['goog_identity_prom']); -}; +} /** * @param {!Window} win @@ -794,7 +794,7 @@ export function getIdentityTokenRequestUrl(win, nodeOrDoc, domain = undefined) { const canonical = parseUrl(Services.documentInfoForDoc(nodeOrDoc).canonicalUrl).hostname; return `https://adservice${domain}/adsid/integrator.json?domain=${canonical}`; -}; +} /** * Returns whether we are running on the AMP CDN. diff --git a/ads/google/adsense-amp-auto-ads.js b/ads/google/adsense-amp-auto-ads.js index d71247d1cf47..30fb59389e72 100644 --- a/ads/google/adsense-amp-auto-ads.js +++ b/ads/google/adsense-amp-auto-ads.js @@ -60,4 +60,4 @@ export function getAdSenseAmpAutoAdsExpBranch(win) { randomlySelectUnsetExperiments(win, experiments); return getExperimentBranch(win, ADSENSE_AMP_AUTO_ADS_HOLDOUT_EXPERIMENT_NAME) || null; -}; +} diff --git a/ads/google/imaVideo.js b/ads/google/imaVideo.js index edf32d4842c7..776acfd76778 100644 --- a/ads/google/imaVideo.js +++ b/ads/google/imaVideo.js @@ -812,7 +812,7 @@ function getPagePosition(el) { el != null; lx += el./*OK*/offsetLeft, ly += el./*OK*/offsetTop, el = el./*OK*/offsetParent) - {}; + {} return {x: lx,y: ly}; } diff --git a/ads/google/test/test-doubleclick.js b/ads/google/test/test-doubleclick.js index bbab941a7bcb..c7a8baa8f221 100644 --- a/ads/google/test/test-doubleclick.js +++ b/ads/google/test/test-doubleclick.js @@ -30,7 +30,7 @@ function verifyScript(win, name) { .to.equal(script == name); } }); -}; +} describes.sandboxed('writeAdScript', {}, env => { diff --git a/ads/imedia.js b/ads/imedia.js index f02dd7706221..ef34f61d2453 100644 --- a/ads/imedia.js +++ b/ads/imedia.js @@ -73,4 +73,4 @@ export function imedia(global, data) { return used; // remove (filter) element filled with add }); }); -}; +} diff --git a/ads/ix.js b/ads/ix.js index a7771c5325a7..e0d6d4f81bbd 100644 --- a/ads/ix.js +++ b/ads/ix.js @@ -106,5 +106,5 @@ function reportStats(siteID, slotID, dfpSlot, start, code) { xhttp.open('POST', url, true); xhttp.setRequestHeader('Content-Type', 'application/json'); xhttp.send(stats); - } catch (e) {}; + } catch (e) {} } diff --git a/ads/navegg.js b/ads/navegg.js index 306cc00ed9ef..c936668b2308 100644 --- a/ads/navegg.js +++ b/ads/navegg.js @@ -35,7 +35,7 @@ export function navegg(global, data) { nvg.getProfile(nvgTargeting => { for (seg in nvgTargeting) { data.targeting[seg] = nvgTargeting[seg]; - }; + } doubleclick(global, data); }); }); diff --git a/ads/netletix.js b/ads/netletix.js index ce1df1217a76..23413d7b4ac5 100644 --- a/ads/netletix.js +++ b/ads/netletix.js @@ -73,7 +73,7 @@ export function netletix(global, data) { if (event.data.width && event.data.height && (event.data.width != nxw || event.data.height != nxh)) { global.context.requestResize(event.data.width, event.data.height); - }; + } break; case 'nx-empty': global.context.noContentAvailable(); diff --git a/ads/sogouad.js b/ads/sogouad.js index 41fb0286cd31..f9d1058c0d4c 100644 --- a/ads/sogouad.js +++ b/ads/sogouad.js @@ -41,4 +41,4 @@ export function sogouad(global, data) { } slot.appendChild(ad); loadScript(global, 'https://theta.sogoucdn.com/wap/js/aw.js'); -}; +} diff --git a/build-system/check-package-manager.js b/build-system/check-package-manager.js index b68e52146741..9acc0e7404ea 100644 --- a/build-system/check-package-manager.js +++ b/build-system/check-package-manager.js @@ -91,7 +91,7 @@ function main() { } else { console.log(green('Detected yarn version'), cyan(yarnVersion) + green('. Installing packages...')); - }; + } return 0; } diff --git a/builtins/amp-img.js b/builtins/amp-img.js index 662b9b0f05d1..0db6faabe7be 100644 --- a/builtins/amp-img.js +++ b/builtins/amp-img.js @@ -222,7 +222,7 @@ export class AmpImg extends BaseElement { this.togglePlaceholder(false); }); } -}; +} /** * @param {!Window} win Destination window for the new element. diff --git a/extensions/amp-a4a/0.1/amp-a4a.js b/extensions/amp-a4a/0.1/amp-a4a.js index 4b1d099d9442..d554db8c186f 100644 --- a/extensions/amp-a4a/0.1/amp-a4a.js +++ b/extensions/amp-a4a/0.1/amp-a4a.js @@ -219,7 +219,7 @@ export function protectFunctionWrapper( return undefined; } }; -}; +} export class AmpA4A extends AMP.BaseElement { // TODO: Add more error handling throughout code. @@ -1772,7 +1772,7 @@ export function assignAdUrlToError(error, adUrl) { } (error.args || (error.args = {}))['au'] = adUrl.substring(adQueryIdx + 1, adQueryIdx + 251); -}; +} /** * Returns the signature verifier for the given window. Lazily creates it if it diff --git a/extensions/amp-a4a/0.1/callout-vendors.js b/extensions/amp-a4a/0.1/callout-vendors.js index 40744cd7f639..d0b520a2ad5f 100644 --- a/extensions/amp-a4a/0.1/callout-vendors.js +++ b/extensions/amp-a4a/0.1/callout-vendors.js @@ -54,4 +54,4 @@ if (getMode().localDev || getMode().test) { url: 'https://localhost:8000/examples/rtcE1.json?slot_id=SLOT_ID&page_id=PAGE_ID&foo_id=FOO_ID', disableKeyAppend: true, }); -}; +} diff --git a/extensions/amp-access/0.1/test/test-amp-access.js b/extensions/amp-access/0.1/test/test-amp-access.js index 41a74ff190cc..ffb7a37723ae 100644 --- a/extensions/amp-access/0.1/test/test-amp-access.js +++ b/extensions/amp-access/0.1/test/test-amp-access.js @@ -328,7 +328,7 @@ describes.fakeWin('AccessService', { }; element.textContent = JSON.stringify(config); const accessService = new AccessService(ampdoc); - class Vendor1 {}; + class Vendor1 {} const vendor1 = new Vendor1(); accessService.registerVendor('vendor1', vendor1); return accessService.adapter_.vendorPromise_.then(vendor => { @@ -344,7 +344,7 @@ describes.fakeWin('AccessService', { }; element.textContent = JSON.stringify(config); const accessService = new AccessService(ampdoc); - class Vendor1 {}; + class Vendor1 {} const vendor1 = new Vendor1(); expect(() => { accessService.registerVendor('vendor1', vendor1); diff --git a/extensions/amp-ad/0.1/amp-ad-xorigin-iframe-handler.js b/extensions/amp-ad/0.1/amp-ad-xorigin-iframe-handler.js index ff093a5afa1b..c0c325dd3578 100644 --- a/extensions/amp-ad/0.1/amp-ad-xorigin-iframe-handler.js +++ b/extensions/amp-ad/0.1/amp-ad-xorigin-iframe-handler.js @@ -121,7 +121,7 @@ export class AmpAdXOriginIframeHandler { this.sendPosition_(); this.registerPosition_(); }); - }; + } // Triggered by context.reportRenderedEntityIdentifier(…) inside the ad // iframe. diff --git a/extensions/amp-analytics/0.1/activity-impl.js b/extensions/amp-analytics/0.1/activity-impl.js index a4778bdd2ccf..581006bb4d4f 100644 --- a/extensions/amp-analytics/0.1/activity-impl.js +++ b/extensions/amp-analytics/0.1/activity-impl.js @@ -334,4 +334,4 @@ export class Activity { return this.totalEngagedTimeByTrigger_[name] - currentIncrementalEngagedTime; } -}; +} diff --git a/extensions/amp-analytics/0.1/requests.js b/extensions/amp-analytics/0.1/requests.js index 885f35e21522..5886044a8c6c 100644 --- a/extensions/amp-analytics/0.1/requests.js +++ b/extensions/amp-analytics/0.1/requests.js @@ -152,7 +152,7 @@ export class RequestHandler { return this.urlReplacementService_.expandUrlAsync( baseUrl, bindings, this.whiteList_); }); - }; + } const extraUrlParamsPromise = this.expandExtraUrlParams_( configParams, triggerParams, expansionOption) diff --git a/extensions/amp-analytics/0.1/resource-timing.js b/extensions/amp-analytics/0.1/resource-timing.js index aa9c8519d0f2..9f140e18ede8 100644 --- a/extensions/amp-analytics/0.1/resource-timing.js +++ b/extensions/amp-analytics/0.1/resource-timing.js @@ -131,7 +131,7 @@ function entryToExpansionOptions(entry, name, format) { 'initiatorType': entry.initiatorType, }; return new ExpansionOptions(vars, 1 /* opt_iterations */); -}; +} /** * Returns the variables for the given resource timing entry if it matches one diff --git a/extensions/amp-analytics/0.1/test/test-amp-analytics.js b/extensions/amp-analytics/0.1/test/test-amp-analytics.js index 403bd3148f4d..3562d55e85b1 100644 --- a/extensions/amp-analytics/0.1/test/test-amp-analytics.js +++ b/extensions/amp-analytics/0.1/test/test-amp-analytics.js @@ -1667,7 +1667,7 @@ describes.realWin('amp-analytics', { }); return waitForNoSendRequest(analytics).then(() => { - expect(addStub).to.not.be.called;; + expect(addStub).to.not.be.called; }); }); diff --git a/extensions/amp-analytics/0.1/test/test-resource-timing.js b/extensions/amp-analytics/0.1/test/test-resource-timing.js index 13760bb58a01..caf30603c126 100644 --- a/extensions/amp-analytics/0.1/test/test-resource-timing.js +++ b/extensions/amp-analytics/0.1/test/test-resource-timing.js @@ -39,7 +39,7 @@ export function newResourceTimingSpec() { 'delim': '~', }, }; -}; +} /** * Returns a sample PerformanceResourceTiming entry. Timing intervals are fixed @@ -77,7 +77,7 @@ export function newPerformanceResourceTiming( encodedBodySize: bodySize * 0.7, transferSize: cached ? 0 : bodySize * 0.7 + 200, // +200 for header size }; -}; +} describes.fakeWin('resourceTiming', {amp: true}, env => { let win; diff --git a/extensions/amp-analytics/0.1/test/test-visibility-model.js b/extensions/amp-analytics/0.1/test/test-visibility-model.js index a5c6a092f9d0..30e6855d148c 100644 --- a/extensions/amp-analytics/0.1/test/test-visibility-model.js +++ b/extensions/amp-analytics/0.1/test/test-visibility-model.js @@ -42,7 +42,7 @@ describes.sandboxed('VisibilityModel', {}, () => { return { repeat: model.repeat_, }; - }; + } it('should parse visiblePercentageMin', () => { expect(config({}).visiblePercentageMin).to.equal(0); diff --git a/extensions/amp-auto-ads/0.1/attributes.js b/extensions/amp-auto-ads/0.1/attributes.js index ee4d29e67602..a3fab3cd9177 100644 --- a/extensions/amp-auto-ads/0.1/attributes.js +++ b/extensions/amp-auto-ads/0.1/attributes.js @@ -64,4 +64,4 @@ function parseAttributes(attributeObject) { attributes[key] = String(attributeObject[key]); } return attributes; -}; +} diff --git a/extensions/amp-auto-ads/0.1/test/test-placement.js b/extensions/amp-auto-ads/0.1/test/test-placement.js index 75fd28eb8a99..45e16570b4df 100644 --- a/extensions/amp-auto-ads/0.1/test/test-placement.js +++ b/extensions/amp-auto-ads/0.1/test/test-placement.js @@ -549,7 +549,7 @@ describes.realWin('placement', { initialMinSpacing: 0, subsequentMinSpacing: [], maxAdCount: 10, - });; + }); return placements[0].placeAd(attributes, adTracker) .then(placementState => { expect(resource.attemptChangeSize).to.have.been.calledWith( diff --git a/extensions/amp-bind/0.1/test/integration/test-bind-impl.js b/extensions/amp-bind/0.1/test/integration/test-bind-impl.js index c44cb2cee3ae..04a8964bdb0c 100644 --- a/extensions/amp-bind/0.1/test/integration/test-bind-impl.js +++ b/extensions/amp-bind/0.1/test/integration/test-bind-impl.js @@ -100,7 +100,7 @@ function waitForEvent(env, name) { function callback() { resolve(); env.win.removeEventListener(name, callback); - }; + } env.win.addEventListener(name, callback); }); } diff --git a/extensions/amp-byside-content/0.1/utils.js b/extensions/amp-byside-content/0.1/utils.js index c18671d3e747..bd0d2d8932ed 100644 --- a/extensions/amp-byside-content/0.1/utils.js +++ b/extensions/amp-byside-content/0.1/utils.js @@ -31,11 +31,11 @@ export function debounce(func, wait, immediate) { clearTimeout(timeout); timeout = setTimeout(function() { timeout = null; - if (!immediate) { func.apply(context, args); }; + if (!immediate) { func.apply(context, args); } }, wait); if (immediate && !timeout) { func.apply(context, args); } }; -}; +} /** * @@ -56,4 +56,4 @@ export function getElementCreator(document) { function appendChildren(element, children) { children = (!children) ? [] : Array.isArray(children) ? children : [children]; children.forEach(child => element.appendChild(child)); -}; +} diff --git a/extensions/amp-date-picker/0.1/date-picker-common.js b/extensions/amp-date-picker/0.1/date-picker-common.js index 596dd94bd0f3..87982e5e938e 100644 --- a/extensions/amp-date-picker/0.1/date-picker-common.js +++ b/extensions/amp-date-picker/0.1/date-picker-common.js @@ -141,7 +141,7 @@ export function withDatePickerCommon(WrappedComponent) { isOutsideRange: this.isOutsideRange, })); } - }; + } Component.defaultProps = defaultProps; diff --git a/extensions/amp-fit-text/0.1/amp-fit-text.js b/extensions/amp-fit-text/0.1/amp-fit-text.js index 1497ef175d19..a6651504e4eb 100644 --- a/extensions/amp-fit-text/0.1/amp-fit-text.js +++ b/extensions/amp-fit-text/0.1/amp-fit-text.js @@ -138,7 +138,7 @@ export function calculateFontSize_(measurer, expectedHeight, expectedWidth, } } return minFontSize; -}; +} /** diff --git a/extensions/amp-fx-collection/0.1/amp-fx-collection.js b/extensions/amp-fx-collection/0.1/amp-fx-collection.js index d8bd2fc8f0ae..c9f5eb3cafb7 100644 --- a/extensions/amp-fx-collection/0.1/amp-fx-collection.js +++ b/extensions/amp-fx-collection/0.1/amp-fx-collection.js @@ -172,7 +172,7 @@ export class FxProviderInterface { * @param {!Element} unusedElement */ installOn(unusedElement) {} -}; +} AMP.extension(TAG, '0.1', AMP => { AMP.registerServiceForDoc(TAG, AmpFxCollection); diff --git a/extensions/amp-fx-collection/0.1/providers/parallax.js b/extensions/amp-fx-collection/0.1/providers/parallax.js index 8144b9d6b5a3..f5ef03a25de2 100644 --- a/extensions/amp-fx-collection/0.1/providers/parallax.js +++ b/extensions/amp-fx-collection/0.1/providers/parallax.js @@ -186,4 +186,4 @@ class ParallaxElement { return aboveTheFold ? offsetTop : viewportHeight; }); } -}; +} diff --git a/extensions/amp-gwd-animation/0.1/amp-gwd-animation-impl.js b/extensions/amp-gwd-animation/0.1/amp-gwd-animation-impl.js index 500521477f46..aabd4331c2f3 100644 --- a/extensions/amp-gwd-animation/0.1/amp-gwd-animation-impl.js +++ b/extensions/amp-gwd-animation/0.1/amp-gwd-animation-impl.js @@ -95,7 +95,7 @@ function getCounter(receiver, counterName) { return receiver.gwdGotoCounters[counterName]; } return 0; -}; +} /** * @param {!Element} receiver @@ -113,7 +113,7 @@ function setCounter(receiver, counterName, counterValue) { receiver.gwdGotoCounters[counterName] = 0; } receiver.gwdGotoCounters[counterName] = counterValue; -}; +} /** * AMP GWD animation runtime service. @@ -423,4 +423,4 @@ export class AmpGwdRuntimeService { */ function reflow(element) { element./*OK*/offsetWidth = element./*OK*/offsetWidth; -}; +} diff --git a/extensions/amp-gwd-animation/0.1/amp-gwd-animation.js b/extensions/amp-gwd-animation/0.1/amp-gwd-animation.js index 74c252720fde..c802cd2cc6ad 100644 --- a/extensions/amp-gwd-animation/0.1/amp-gwd-animation.js +++ b/extensions/amp-gwd-animation/0.1/amp-gwd-animation.js @@ -180,7 +180,7 @@ export function addAction(ampdoc, element, event, actionStr) { // Reset the element's actions with the new actions string. Services.actionServiceForDoc(ampdoc).setActions(element, newActionsStr); -}; +} AMP.extension(TAG, '0.1', AMP => { AMP.registerServiceForDoc(GWD_SERVICE_NAME, AmpGwdRuntimeService); diff --git a/extensions/amp-iframe/0.1/amp-iframe.js b/extensions/amp-iframe/0.1/amp-iframe.js index 2fb46fef903f..328cfe206afb 100644 --- a/extensions/amp-iframe/0.1/amp-iframe.js +++ b/extensions/amp-iframe/0.1/amp-iframe.js @@ -547,7 +547,7 @@ export class AmpIframe extends AMP.BaseElement { } return !this.isInContainer_; } -}; +} /** * We always set a sandbox. Default is that none of the things that need diff --git a/extensions/amp-nexxtv-player/0.1/amp-nexxtv-player.js b/extensions/amp-nexxtv-player/0.1/amp-nexxtv-player.js index 9a100caa410e..2b88f61bb886 100644 --- a/extensions/amp-nexxtv-player/0.1/amp-nexxtv-player.js +++ b/extensions/amp-nexxtv-player/0.1/amp-nexxtv-player.js @@ -196,7 +196,7 @@ class AmpNexxtvPlayer extends AMP.BaseElement { }), '*'); } }); - }; + } // emitter handleNexxMessages_(event) { diff --git a/extensions/amp-ooyala-player/0.1/test/test-amp-ooyala.js b/extensions/amp-ooyala-player/0.1/test/test-amp-ooyala.js index bc74a056f672..dd1c5a94e229 100644 --- a/extensions/amp-ooyala-player/0.1/test/test-amp-ooyala.js +++ b/extensions/amp-ooyala-player/0.1/test/test-amp-ooyala.js @@ -58,7 +58,7 @@ describes.realWin('amp-ooyala-player', { return player.build() .then(() => player.layoutCallback()) .then(() => player); - }; + } it('renders a V3 player', () => { return getOoyalaElement('Vxc2k0MDE6Y_C7J5podo3UDxlFxGaZrQ', diff --git a/extensions/amp-pinterest/0.1/pin-widget.js b/extensions/amp-pinterest/0.1/pin-widget.js index d08dd501fb03..78386839cbd9 100644 --- a/extensions/amp-pinterest/0.1/pin-widget.js +++ b/extensions/amp-pinterest/0.1/pin-widget.js @@ -259,4 +259,4 @@ export class PinWidget { .then(this.renderPin.bind(this)); } -}; +} diff --git a/extensions/amp-pinterest/0.1/pinit-button.js b/extensions/amp-pinterest/0.1/pinit-button.js index a4de3a32ece3..8597571a8b3b 100644 --- a/extensions/amp-pinterest/0.1/pinit-button.js +++ b/extensions/amp-pinterest/0.1/pinit-button.js @@ -181,4 +181,4 @@ export class PinItButton { } return promise.then(this.renderTemplate.bind(this)); } -}; +} diff --git a/extensions/amp-pinterest/0.1/test/test-amp-pinterest.js b/extensions/amp-pinterest/0.1/test/test-amp-pinterest.js index ac1074cbf03c..4ba791897224 100644 --- a/extensions/amp-pinterest/0.1/test/test-amp-pinterest.js +++ b/extensions/amp-pinterest/0.1/test/test-amp-pinterest.js @@ -42,7 +42,7 @@ describes.realWin('amp-pinterest', { return pin.implementation_.layoutCallback().then(() => { return pin; }); - }; + } function getEmbedPin(pinID, pinAlt, mockResponse) { const div = document.createElement('div'); @@ -61,7 +61,7 @@ describes.realWin('amp-pinterest', { return pin.implementation_.layoutCallback().then(() => { return pin; }); - }; + } it('renders', () => { diff --git a/extensions/amp-pinterest/0.1/util.js b/extensions/amp-pinterest/0.1/util.js index 42001f0d7816..cb1302ae6ebb 100644 --- a/extensions/amp-pinterest/0.1/util.js +++ b/extensions/amp-pinterest/0.1/util.js @@ -36,7 +36,7 @@ function log(queryParams) { } query = query + '&via=' + encodeURIComponent(window.location.href); call.src = query; -}; +} /** * Strip data from string @@ -53,7 +53,7 @@ function filter(str) { ret = decoded.replace(//g, '>'); return ret; -}; +} /** * Create a DOM element with attributes @@ -73,7 +73,7 @@ function make(doc, data) { break; } return el; -}; +} /** * Set a DOM element attribute @@ -87,6 +87,6 @@ function set(el, attr, value) { } else { el.setAttribute(attr, value); } -}; +} export const Util = {filter, guid, log, make, set}; diff --git a/extensions/amp-playbuzz/0.1/utils.js b/extensions/amp-playbuzz/0.1/utils.js index df7e633b35bb..7822a0a24f48 100644 --- a/extensions/amp-playbuzz/0.1/utils.js +++ b/extensions/amp-playbuzz/0.1/utils.js @@ -41,11 +41,11 @@ export function debounce(func, wait, immediate) { clearTimeout(timeout); timeout = setTimeout(function() { timeout = null; - if (!immediate) { func.apply(context, args); }; + if (!immediate) { func.apply(context, args); } }, wait); if (immediate && !timeout) { func.apply(context, args); } }; -}; +} /** @@ -67,7 +67,7 @@ export function getElementCreator(document) { function appendChildren(element, children) { children = (!children) ? [] : Array.isArray(children) ? children : [children]; children.forEach(child => element.appendChild(child)); -}; +} /** diff --git a/extensions/amp-selector/0.1/test/test-amp-selector.js b/extensions/amp-selector/0.1/test/test-amp-selector.js index 65430d6e00bf..1344cd4768ac 100644 --- a/extensions/amp-selector/0.1/test/test-amp-selector.js +++ b/extensions/amp-selector/0.1/test/test-amp-selector.js @@ -324,7 +324,7 @@ describes.realWin('amp-selector', { impl = ampSelector.implementation_; yield ampSelector.build(); - expect(impl.inputs_.length).to.equal(0);; + expect(impl.inputs_.length).to.equal(0); ampSelector = getSelector({ attributes: { diff --git a/extensions/amp-sidebar/0.1/test/test-amp-sidebar.js b/extensions/amp-sidebar/0.1/test/test-amp-sidebar.js index 46885d93eb2c..aab6772d3eeb 100644 --- a/extensions/amp-sidebar/0.1/test/test-amp-sidebar.js +++ b/extensions/amp-sidebar/0.1/test/test-amp-sidebar.js @@ -69,7 +69,7 @@ describes.realWin('amp-sidebar 0.1 version', { if (options.closeText) { ampSidebar.setAttribute('data-close-button-aria-label', options.closeText); - }; + } ampSidebar.setAttribute('id', 'sidebar1'); ampSidebar.setAttribute('layout', 'nodisplay'); doc.body.appendChild(ampSidebar); diff --git a/extensions/amp-story/0.1/media-pool.js b/extensions/amp-story/0.1/media-pool.js index 845bbe3bba3c..59e6e9668d54 100644 --- a/extensions/amp-story/0.1/media-pool.js +++ b/extensions/amp-story/0.1/media-pool.js @@ -885,7 +885,7 @@ export class MediaPoolRoot { /** * @return {!Element} The root element of this media pool. */ - getElement() {}; + getElement() {} /** * @param {!Element} unusedElement The element whose distance should be @@ -897,12 +897,12 @@ export class MediaPoolRoot { * furthest from the user's current position in the document are evicted * from the MediaPool first). */ - getElementDistance(unusedElement) {}; + getElementDistance(unusedElement) {} /** * @return {!Object} The maximum amount of each media * type to allow within this element. */ - getMaxMediaElementCounts() {}; + getMaxMediaElementCounts() {} } diff --git a/extensions/amp-viewer-integration/0.1/examples/amp-viewer-host.js b/extensions/amp-viewer-integration/0.1/examples/amp-viewer-host.js index ac495dba2b39..97321793d589 100644 --- a/extensions/amp-viewer-integration/0.1/examples/amp-viewer-host.js +++ b/extensions/amp-viewer-integration/0.1/examples/amp-viewer-host.js @@ -134,7 +134,7 @@ export class AmpViewerHost { state: this.visibilityState_, prerenderSize: this.prerenderSize, }, true); - }; + } /** * @param {*} eventData @@ -143,7 +143,7 @@ export class AmpViewerHost { */ isChannelOpen_(eventData) { return eventData.app == APP && eventData.name == CHANNEL_OPEN_MSG; - }; + } /** * @param {string} type @@ -157,7 +157,7 @@ export class AmpViewerHost { return; } return this.messaging_.sendRequest(type, data, awaitResponse); - }; + } log() { const var_args = Array.prototype.slice.call(arguments, 0); diff --git a/extensions/amp-viewer-integration/0.1/messaging/messaging.js b/extensions/amp-viewer-integration/0.1/messaging/messaging.js index 915933ac4c2a..986316ff5031 100644 --- a/extensions/amp-viewer-integration/0.1/messaging/messaging.js +++ b/extensions/amp-viewer-integration/0.1/messaging/messaging.js @@ -324,7 +324,7 @@ export class Messaging { const dataStr = ' data: ' + this.errorToString_(opt_data); stateStr += dataStr; this.win['viewerState'] = stateStr; - }; + } /** * @param {*} err !Error most of time, string sometimes, * rarely. diff --git a/extensions/amp-viewer-integration/0.1/test/viewer-initiated-handshake-viewer-for-testing.js b/extensions/amp-viewer-integration/0.1/test/viewer-initiated-handshake-viewer-for-testing.js index 8b6f44a64a0a..a5441f771a3a 100644 --- a/extensions/amp-viewer-integration/0.1/test/viewer-initiated-handshake-viewer-for-testing.js +++ b/extensions/amp-viewer-integration/0.1/test/viewer-initiated-handshake-viewer-for-testing.js @@ -136,7 +136,7 @@ export class WebviewViewerForTesting { isChannelOpen_(e) { return e.type == 'message' && e.data.app == APP && e.data.name == 'channelOpen'; - }; + } completeHandshake_(requestId) { this.log('Viewer ' + this.id + ' messaging established!'); @@ -156,7 +156,7 @@ export class WebviewViewerForTesting { }); this.handshakeResponseResolve_(); - }; + } sendRequest_(eventType, payload) { const requestId = ++this.requestIdCounter_; @@ -169,7 +169,7 @@ export class WebviewViewerForTesting { type: MessageType.REQUEST, }; this.iframe.contentWindow./*OK*/postMessage(message, this.frameOrigin_); - }; + } processRequest_(eventData) { const data = eventData; @@ -193,7 +193,7 @@ export class WebviewViewerForTesting { default: return Promise.reject('request not supported: ' + data.name); } - }; + } log() { const var_args = Array.prototype.slice.call(arguments, 0); diff --git a/extensions/amp-viewer-integration/0.1/test/webview-viewer-for-testing.js b/extensions/amp-viewer-integration/0.1/test/webview-viewer-for-testing.js index ca62ff3fc432..5529c499ca2a 100644 --- a/extensions/amp-viewer-integration/0.1/test/webview-viewer-for-testing.js +++ b/extensions/amp-viewer-integration/0.1/test/webview-viewer-for-testing.js @@ -145,7 +145,7 @@ export class WebviewViewerForTesting { const data = JSON.parse(e.data); return e.type == 'message' && data.app == APP && data.name == 'channelOpen'; - }; + } completeHandshake_(channel, requestId) { @@ -189,7 +189,7 @@ export class WebviewViewerForTesting { }, true); this.handshakeResponseResolve_(); - }; + } sendRequest_(type, data, awaitResponse) { this.log('Viewer.prototype.sendRequest_'); @@ -197,7 +197,7 @@ export class WebviewViewerForTesting { return; } return this.messaging_.sendRequest(type, data, awaitResponse); - }; + } handleMessage_(e) { if (this.messageHandlers_[this.id]) { @@ -206,7 +206,7 @@ export class WebviewViewerForTesting { this.log('************** viewer got a message,', e.data); this.processRequest_(e.data); - }; + } /** * This is used in test-amp-viewer-integration to test the handshake and make @@ -239,7 +239,7 @@ export class WebviewViewerForTesting { default: return Promise.reject('request not supported: ' + data.name); } - }; + } log() { const var_args = Array.prototype.slice.call(arguments, 0); diff --git a/src/chunk.js b/src/chunk.js index f5fa30ced984..e703b9a065c8 100644 --- a/src/chunk.js +++ b/src/chunk.js @@ -104,11 +104,11 @@ export function chunkInstanceForTesting(nodeOrAmpDoc) { */ export function deactivateChunking() { deactivated = true; -}; +} export function activateChunkingForTesting() { deactivated = false; -}; +} /** * Runs all currently scheduled chunks. diff --git a/src/cookies.js b/src/cookies.js index f442c755e68a..d425e51af309 100644 --- a/src/cookies.js +++ b/src/cookies.js @@ -139,7 +139,7 @@ function trySetCookie(win, name, value, expirationTime, domain) { // Do not throw if setting the cookie failed Exceptions can be thrown // when AMP docs are opened on origins that do not allow setting // cookies such as null origins. - }; + } } /** diff --git a/src/curve.js b/src/curve.js index 4490f8d39ddf..35efaa36c314 100644 --- a/src/curve.js +++ b/src/curve.js @@ -237,7 +237,7 @@ class Bezier { lerp(a, b, x) { return a + x * (b - a); } -}; +} /** diff --git a/src/custom-element.js b/src/custom-element.js index 4c97c4ce6ac0..a11566119559 100644 --- a/src/custom-element.js +++ b/src/custom-element.js @@ -1601,7 +1601,7 @@ function createBaseCustomElementClass(win) { } } } - }; + } win.BaseCustomElementClass = BaseCustomElement; return win.BaseCustomElementClass; } diff --git a/src/mediasession-helper.js b/src/mediasession-helper.js index 2d3b79c9bed3..cddb447c3175 100644 --- a/src/mediasession-helper.js +++ b/src/mediasession-helper.js @@ -103,7 +103,7 @@ export function parseSchemaImage(doc) { } else { return; } -}; +} /** * Parses the og:image if it exists and returns it @@ -117,7 +117,7 @@ export function parseOgImage(doc) { } else { return; } -}; +} /** * Parses the website's Favicon and returns it @@ -148,4 +148,4 @@ function validateMetadata(metadata) { } }); } -}; +} diff --git a/src/polyfills/math-sign.js b/src/polyfills/math-sign.js index 1c16daab79a2..3bf1b56d6558 100644 --- a/src/polyfills/math-sign.js +++ b/src/polyfills/math-sign.js @@ -31,7 +31,7 @@ export function sign(x) { } return x > 0 ? 1 : -1; -}; +} /** diff --git a/src/service/ampdoc-impl.js b/src/service/ampdoc-impl.js index a955f2ebf6f5..19dc09ae6e99 100644 --- a/src/service/ampdoc-impl.js +++ b/src/service/ampdoc-impl.js @@ -609,4 +609,4 @@ export function installDocService(win, isSingleDoc) { function() { return new AmpDocService(win, isSingleDoc); }); -}; +} diff --git a/src/service/batched-xhr-impl.js b/src/service/batched-xhr-impl.js index 6fc9724ef3d0..f890a7a83b6f 100644 --- a/src/service/batched-xhr-impl.js +++ b/src/service/batched-xhr-impl.js @@ -102,4 +102,4 @@ export function batchedXhrServiceForTesting(window) { */ export function installBatchedXhrService(window) { registerServiceBuilder(window, 'batched-xhr', BatchedXhr); -}; +} diff --git a/src/service/layers-impl.js b/src/service/layers-impl.js index 788eb1d4a759..2f179fcc8e55 100644 --- a/src/service/layers-impl.js +++ b/src/service/layers-impl.js @@ -1093,4 +1093,4 @@ export function installLayersServiceForDoc(ampdoc, scrollingElement) { registerServiceBuilderForDoc(ampdoc, 'layers', function(ampdoc) { return new LayoutLayers(ampdoc, scrollingElement); }, /* opt_instantiate */ true); -}; +} diff --git a/src/service/platform-impl.js b/src/service/platform-impl.js index 6783f8f16d72..bc531a2373f6 100644 --- a/src/service/platform-impl.js +++ b/src/service/platform-impl.js @@ -195,7 +195,7 @@ export class Platform { } return Number(currentIosVersion.split('.')[0]); } -}; +} /** @@ -203,4 +203,4 @@ export class Platform { */ export function installPlatformService(window) { return registerServiceBuilder(window, 'platform', Platform); -}; +} diff --git a/src/service/resources-impl.js b/src/service/resources-impl.js index 8e0534630369..31aa84712211 100644 --- a/src/service/resources-impl.js +++ b/src/service/resources-impl.js @@ -2246,4 +2246,4 @@ export let SizeDef; */ export function installResourcesServiceForDoc(ampdoc) { registerServiceBuilderForDoc(ampdoc, 'resources', Resources); -}; +} diff --git a/src/service/standard-actions-impl.js b/src/service/standard-actions-impl.js index 6900bb47b171..c709b6bf3d57 100644 --- a/src/service/standard-actions-impl.js +++ b/src/service/standard-actions-impl.js @@ -348,4 +348,4 @@ export function installStandardActionsForDoc(ampdoc) { 'standard-actions', StandardActions, /* opt_instantiate */ true); -}; +} diff --git a/src/service/timer-impl.js b/src/service/timer-impl.js index 6918eabe1092..d4de8ba938a3 100644 --- a/src/service/timer-impl.js +++ b/src/service/timer-impl.js @@ -172,4 +172,4 @@ export class Timer { */ export function installTimerService(window) { registerServiceBuilder(window, 'timer', Timer); -}; +} diff --git a/src/service/url-replacements-impl.js b/src/service/url-replacements-impl.js index 9defb7aeb5bb..abba66686812 100644 --- a/src/service/url-replacements-impl.js +++ b/src/service/url-replacements-impl.js @@ -62,7 +62,7 @@ function encodeValue(val) { return ''; } return encodeURIComponent(/** @type {string} */(val)); -}; +} /** * Class to provide variables that pertain to top level AMP window. diff --git a/src/service/variable-source.js b/src/service/variable-source.js index 601ac8727faa..a0519686920d 100644 --- a/src/service/variable-source.js +++ b/src/service/variable-source.js @@ -78,7 +78,7 @@ export function getTimingDataSync(win, startEvent, endEvent) { if (!isFiniteNumber(metric)) { // The metric is not supported. return; - } else if (metric < 0) {; + } else if (metric < 0) { return ''; } else { return metric; diff --git a/src/service/video-manager-impl.js b/src/service/video-manager-impl.js index 5da74c679388..f0580a9be4e4 100644 --- a/src/service/video-manager-impl.js +++ b/src/service/video-manager-impl.js @@ -1091,7 +1091,7 @@ class VideoEntry { const internalElement = this.internalElement_; function cloneStyle(prop) { return st.getStyle(dev().assertElement(internalElement), prop); - }; + } st.setStyles(dev().assertElement(this.draggingMask_), { 'top': cloneStyle('top'), @@ -1990,4 +1990,4 @@ export function clearSupportsAutoplayCacheForTesting() { */ export function installVideoManagerForDoc(nodeOrDoc) { registerServiceBuilderForDoc(nodeOrDoc, 'video-manager', VideoManager); -}; +} diff --git a/src/service/xhr-impl.js b/src/service/xhr-impl.js index 6b7b14d34d75..b13e525e2b9d 100644 --- a/src/service/xhr-impl.js +++ b/src/service/xhr-impl.js @@ -826,4 +826,4 @@ export function xhrServiceForTesting(window) { */ export function installXhrService(window) { registerServiceBuilder(window, 'xhr', Xhr); -}; +} diff --git a/src/services.js b/src/services.js index a2cad03f3b93..0be7c775369a 100644 --- a/src/services.js +++ b/src/services.js @@ -201,7 +201,7 @@ export class Services { */ static inputFor(win) { return getService(win, 'input'); - }; + } /** * @param {!Node|!./service/ampdoc-impl.AmpDoc} nodeOrDoc diff --git a/src/size-list.js b/src/size-list.js index 57620b7b80ee..77059fd3e01d 100644 --- a/src/size-list.js +++ b/src/size-list.js @@ -122,7 +122,7 @@ export function parseSizeList(s, opt_allowPercentAsLength) { assertLength(sizeStr)}); }); return new SizeList(sizes); -}; +} /** diff --git a/src/string.js b/src/string.js index 3af42e927b13..37692edf9550 100644 --- a/src/string.js +++ b/src/string.js @@ -126,4 +126,4 @@ export function stringHash32(str) { } // Convert from 32-bit signed to unsigned. return String(hash >>> 0); -}; +} diff --git a/src/style-installer.js b/src/style-installer.js index 435d8221a09c..48e3a01b51ec 100644 --- a/src/style-installer.js +++ b/src/style-installer.js @@ -318,4 +318,4 @@ function styleLoaded(doc, style) { } } return false; -}; +} diff --git a/src/utils/bytes.js b/src/utils/bytes.js index a6ecce76825b..8e7f242e1165 100644 --- a/src/utils/bytes.js +++ b/src/utils/bytes.js @@ -56,7 +56,7 @@ export function stringToBytes(str) { bytes[i] = charCode; } return bytes; -}; +} /** * Converts a 8-bit bytes array into a string @@ -71,7 +71,7 @@ export function bytesToString(bytes) { array[i] = String.fromCharCode(bytes[i]); } return array.join(''); -}; +} /** * Converts a 4-item byte array to an unsigned integer. diff --git a/src/utils/dom-fingerprint.js b/src/utils/dom-fingerprint.js index 990359641d4a..0e800d76660e 100644 --- a/src/utils/dom-fingerprint.js +++ b/src/utils/dom-fingerprint.js @@ -61,7 +61,7 @@ export function domFingerprintPlain(element) { element = element.parentElement; } return ids.join(); -}; +} export class DomFingerprint { @@ -107,4 +107,4 @@ function indexWithinParent(element) { } // If we got to the end, then the count is accurate; otherwise skip count. return count < 25 && i < 100 ? `.${count}` : ''; -}; +} diff --git a/src/utils/math.js b/src/utils/math.js index 60b0f689dadb..286eb0f554b4 100644 --- a/src/utils/math.js +++ b/src/utils/math.js @@ -51,7 +51,7 @@ export function mapRange(val, min1, max1, min2, max2) { } return (val - min1) * (max2 - min2) / (max1 - min1) + min2; -}; +} /** * Restricts a number to be in the given min/max range. @@ -68,4 +68,4 @@ export function mapRange(val, min1, max1, min2, max2) { */ export function clamp(val, min, max) { return Math.min(Math.max(val, min), max); -}; +} diff --git a/test/functional/test-dom.js b/test/functional/test-dom.js index 7ea37a4c756f..72eabf056456 100644 --- a/test/functional/test-dom.js +++ b/test/functional/test-dom.js @@ -961,7 +961,7 @@ describes.realWin('DOM', { }, }, env => { let doc; - class TestElement extends BaseElement {}; + class TestElement extends BaseElement {} describe('whenUpgradeToCustomElement function', () => { beforeEach(() => { doc = env.win.document; diff --git a/test/functional/test-extension-analytics.js b/test/functional/test-extension-analytics.js index 0257cead60ab..b7d521cb26e9 100644 --- a/test/functional/test-extension-analytics.js +++ b/test/functional/test-extension-analytics.js @@ -41,7 +41,7 @@ describes.realWin('extension-analytics', { describe('insertAnalyticsElement', () => { let sandbox; class MockInstrumentation { - }; + } beforeEach(() => { sandbox = sinon.sandbox.create(); @@ -160,7 +160,7 @@ describes.realWin('extension-analytics', { } catch (e) { expect(e.message).to.equal( 'customEventReporterBuilder should not track same eventType twice'); - }; + } }); it('should return a customEventReporter instance', () => { diff --git a/test/functional/test-extensions.js b/test/functional/test-extensions.js index 65f0bd0e4108..b222bb05332c 100644 --- a/test/functional/test-extensions.js +++ b/test/functional/test-extensions.js @@ -150,7 +150,7 @@ describes.sandboxed('Extensions', {}, () => { expect(holder.error.message).to.equal('intentional'); expect(holder.loaded).to.be.undefined; expect(holder.resolve).to.exist; - expect(holder.reject).to.exist;; + expect(holder.reject).to.exist; expect(holder.promise).to.exist; expect(promise).to.equal(holder.promise); diff --git a/test/functional/test-runtime.js b/test/functional/test-runtime.js index 18121a8f6377..97b36a3b5363 100644 --- a/test/functional/test-runtime.js +++ b/test/functional/test-runtime.js @@ -183,7 +183,7 @@ describes.fakeWin('runtime', { expect(progress).to.equal('12345'); expect(queueExtensions).to.have.length(0); }); - }; + } it('should execute scheduled extensions & execute new extensions', extensionRegistrationTest); diff --git a/test/integration/test-amp-bind.js b/test/integration/test-amp-bind.js index a863d0bbe815..d21ba73294fb 100644 --- a/test/integration/test-amp-bind.js +++ b/test/integration/test-amp-bind.js @@ -427,7 +427,7 @@ describe.configure().ifNewChrome().run('amp-bind', function() { const button = fixture.doc.getElementById('disallowedVidUrlButton'); const vid = fixture.doc.getElementById('video'); expect(vid.getAttribute('src')).to - .equal('https://www.google.com/unbound.webm');; + .equal('https://www.google.com/unbound.webm'); button.click(); return waitForSetState().then(() => { expect(vid.getAttribute('src')).to diff --git a/test/integration/test-position-observer.js b/test/integration/test-position-observer.js index a5d4a058b53c..d51835a35700 100644 --- a/test/integration/test-position-observer.js +++ b/test/integration/test-position-observer.js @@ -176,7 +176,7 @@ config.run('amp-position-observer', function() { function getOpacity(win) { const animTarget = win.document.querySelector('#animTarget'); return parseFloat(win.getComputedStyle(animTarget).opacity); -}; +} function waitForOpacity(win, comparison, factor) { return poll('wait for opacity to ' + comparison + ': ' + factor, () => { @@ -188,7 +188,7 @@ function waitForOpacity(win, comparison, factor) { return getOpacity(win) > factor; } }); -}; +} function ensureOpacityIsNoChangingAnymore(win) { return new Promise((resolve, reject) => { @@ -207,4 +207,4 @@ function ensureOpacityIsNoChangingAnymore(win) { function getViewportHeight(win) { return win.document.querySelector('.spacer').offsetHeight; -}; +} From cc82c49bacc8816d9eac9296fd892b0f09642b66 Mon Sep 17 00:00:00 2001 From: Yuxuan Zhou Date: Tue, 6 Feb 2018 17:14:22 -0800 Subject: [PATCH 36/65] Compile all css with `gulp dist` (#13285) * compileAll flag * remove whitespace from extensions list * change to undefined * lint --- gulpfile.js | 74 ++++++++++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 22078f610176..10e1c3f0e814 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -248,12 +248,12 @@ function endBuildStep(stepName, targetName, startTime) { * @return {!Promise} */ function buildExtensions(options) { - if (!!argv.noextensions) { + if (!!argv.noextensions && !options.compileAll) { return Promise.resolve(); } let extensionsToBuild = []; - if (!!argv.extensions) { + if (!!argv.extensions && !options.compileAll) { extensionsToBuild = argv.extensions.split(','); } @@ -432,9 +432,10 @@ function css() { /** * Compile all the css and drop in the build folder * @param {boolean} watch + * @param {boolean=} opt_compileAll * @return {!Promise} */ -function compileCss(watch) { +function compileCss(watch, opt_compileAll) { // Print a message that could help speed up local development. if (!process.env.TRAVIS && argv['_'].indexOf('test') != -1) { log(green('To skip building during future test runs, use'), @@ -468,6 +469,7 @@ function compileCss(watch) { return buildExtensions({ bundleOnlyIfListedInFiles: false, compileOnlyCss: true, + compileAll: opt_compileAll, }); }); } @@ -633,6 +635,7 @@ function parseExtensionFlags() { log(minimalSetMessage); process.exit(1); } + argv.extensions = argv.extensions.replace(/\s/g, ''); if (argv.extensions === 'minimal_set') { argv.extensions = 'amp-ad,amp-ad-network-adsense-impl,amp-audio,amp-video,' + @@ -721,38 +724,39 @@ function dist() { printConfigHelp('gulp dist --fortesting'); } parseExtensionFlags(); - return compileCss().then(() => { - return Promise.all([ - compile(false, true, true), - // NOTE: - // When adding a line here, consider whether you need to include polyfills - // and whether you need to init logging (initLogConstructor). - buildAlp({minify: true, watch: false, preventRemoveAndMakeDir: true}), - buildExaminer({ - minify: true, watch: false, preventRemoveAndMakeDir: true}), - buildSw({minify: true, watch: false, preventRemoveAndMakeDir: true}), - buildWebWorker({ - minify: true, watch: false, preventRemoveAndMakeDir: true}), - buildExtensions({minify: true, preventRemoveAndMakeDir: true}), - buildExperiments({ - minify: true, watch: false, preventRemoveAndMakeDir: true}), - buildLoginDone({ - minify: true, watch: false, preventRemoveAndMakeDir: true}), - buildWebPushPublisherFiles({ - minify: true, watch: false, preventRemoveAndMakeDir: true}), - copyCss(), - ]); - }).then(() => { - copyAliasExtensions(); - }).then(() => { - if (argv.fortesting) { - return enableLocalTesting(minifiedRuntimeTarget); - } - }).then(() => { - if (argv.fortesting) { - return enableLocalTesting(minified3pTarget); - } - }).then(() => exitCtrlcHandler(handlerProcess)); + return compileCss(/* watch */ undefined, /* opt_compileAll */ true) + .then(() => { + return Promise.all([ + compile(false, true, true), + // NOTE: When adding a line here, + // consider whether you need to include polyfills + // and whether you need to init logging (initLogConstructor). + buildAlp({minify: true, watch: false, preventRemoveAndMakeDir: true}), + buildExaminer({ + minify: true, watch: false, preventRemoveAndMakeDir: true}), + buildSw({minify: true, watch: false, preventRemoveAndMakeDir: true}), + buildWebWorker({ + minify: true, watch: false, preventRemoveAndMakeDir: true}), + buildExtensions({minify: true, preventRemoveAndMakeDir: true}), + buildExperiments({ + minify: true, watch: false, preventRemoveAndMakeDir: true}), + buildLoginDone({ + minify: true, watch: false, preventRemoveAndMakeDir: true}), + buildWebPushPublisherFiles({ + minify: true, watch: false, preventRemoveAndMakeDir: true}), + copyCss(), + ]); + }).then(() => { + copyAliasExtensions(); + }).then(() => { + if (argv.fortesting) { + return enableLocalTesting(minifiedRuntimeTarget); + } + }).then(() => { + if (argv.fortesting) { + return enableLocalTesting(minified3pTarget); + } + }).then(() => exitCtrlcHandler(handlerProcess)); } /** From 355cde779bcd65881a0a92f658dec6620d9484e0 Mon Sep 17 00:00:00 2001 From: Hongfei Ding Date: Tue, 6 Feb 2018 17:41:44 -0800 Subject: [PATCH 37/65] Add cloudflare to caches.json (#13247) --- caches.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/caches.json b/caches.json index fac75defb692..3c4b221b195d 100644 --- a/caches.json +++ b/caches.json @@ -5,6 +5,12 @@ "name": "Google AMP Cache", "docs": "https://developers.google.com/amp/cache/", "updateCacheApiDomainSuffix": "cdn.ampproject.org" + }, + { + "id": "cloudflare", + "name": "Cloudflare AMP Cache", + "docs": "https://amp.cloudflare.com/", + "updateCacheApiDomainSuffix": "amp.cloudflare.com" } ] } From 36936a7df93e43775324584daf6d1d7fc85b95e4 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Tue, 6 Feb 2018 21:27:28 -0500 Subject: [PATCH 38/65] =?UTF-8?q?Update=20eslint=20to=20the=20latest=20ver?= =?UTF-8?q?sion=20=F0=9F=9A=80=20(#13252)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(package): update eslint to version 4.17.0 * chore(package): update lockfile https://npm.im/greenkeeper-lockfile --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 6f5a46bc9773..04fcd891240d 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "doctrine": "2.1.0", "envify": "4.1.0", "escodegen": "1.9.0", - "eslint": "4.14.0", + "eslint": "4.17.0", "eslint-plugin-google-camelcase": "0.0.2", "eslint-plugin-sort-imports-es6-autofix": "0.2.2", "eslint-plugin-sort-requires": "2.1.0", diff --git a/yarn.lock b/yarn.lock index 5937cd8539dd..151922e65745 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3480,9 +3480,9 @@ eslint-visitor-keys@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" -eslint@4.14.0: - version "4.14.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.14.0.tgz#96609768d1dd23304faba2d94b7fefe5a5447a82" +eslint@4.17.0: + version "4.17.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.17.0.tgz#dc24bb51ede48df629be7031c71d9dc0ee4f3ddf" dependencies: ajv "^5.3.0" babel-code-frame "^6.22.0" @@ -3490,7 +3490,7 @@ eslint@4.14.0: concat-stream "^1.6.0" cross-spawn "^5.1.0" debug "^3.1.0" - doctrine "^2.0.2" + doctrine "^2.1.0" eslint-scope "^3.7.1" eslint-visitor-keys "^1.0.0" espree "^3.5.2" From 10b94696762f66dfc2b381a87c8e2333fd50b074 Mon Sep 17 00:00:00 2001 From: Gabe Bender Date: Wed, 7 Feb 2018 08:08:08 -0500 Subject: [PATCH 39/65] Replaced "AMP Ads" with "AMPHTML ads" (#12923) --- ads/google/a4a/docs/a4a-readme.md | 40 +++++++++++++++---------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/ads/google/a4a/docs/a4a-readme.md b/ads/google/a4a/docs/a4a-readme.md index 0e471db1c2fd..918fbf9b0607 100644 --- a/ads/google/a4a/docs/a4a-readme.md +++ b/ads/google/a4a/docs/a4a-readme.md @@ -1,34 +1,34 @@ -# AMP Ads +# AMPHTML ads -AMP Ads applies AMP’s core philosophy of reliable fast performance and great user experience to ads. +AMPHTML ads applies AMP’s core philosophy of reliable fast performance and great user experience to ads. -# AMP Ads +# AMPHTML ads -AMP Ads are written in AMP format - [A4A HTML](https://github.com/ampproject/amphtml/blob/master/extensions/amp-a4a/amp-a4a-format.md) (A variant of AMP HTML) + CSS. This means that ads can no longer have the ability to run arbitrary JavaScript - which is traditionally the number one cause of poor ad performance. Therefore, just like core AMP, the core ads JavaScript use-cases are built right into the AMP Open Source project which guarantees good behavior from ads. +AMPHTML ads are written in AMP format - [A4A HTML](https://github.com/ampproject/amphtml/blob/master/extensions/amp-a4a/amp-a4a-format.md) (A variant of AMP HTML) + CSS. This means that ads can no longer have the ability to run arbitrary JavaScript - which is traditionally the number one cause of poor ad performance. Therefore, just like core AMP, the core ads JavaScript use-cases are built right into the AMP Open Source project which guarantees good behavior from ads. -# Why are AMP Ads better than regular ads? +# Why are AMPHTML ads better than regular ads? ### Faster -AMP Ads are faster because on AMP pages they are requested early while rendering the page and immediately displayed just before the user is about to view the ad. Reduced file size of AMP Ads also increases speed. +AMPHTML ads are faster because on AMP pages they are requested early while rendering the page and immediately displayed just before the user is about to view the ad. Reduced file size of AMPHTML ads also increases speed. ### More Aware -On AMP pages, the AMP runtime can coordinate a mobile phone's limited resources to the right component at the right time to give the best user experience. For example, AMP Ads with animations are paused when not in the current viewport. +On AMP pages, the AMP runtime can coordinate a mobile phone's limited resources to the right component at the right time to give the best user experience. For example, AMPHTML ads with animations are paused when not in the current viewport. ### Lighter -AMP Ads bundle commonly used ad functionality which removes bloat. Once on the page, AMP Ads also consume less resources. For example, instead of 10 trackers requesting their own information in regular ads, AMP Ads collect all the information once and distribute it to any number of interested trackers. +AMPHTML ads bundle commonly used ad functionality which removes bloat. Once on the page, AMPHTML ads also consume less resources. For example, instead of 10 trackers requesting their own information in regular ads, AMPHTML ads collect all the information once and distribute it to any number of interested trackers. ### More Engaging "Users can't tap on ads they can't see". Faster ads lead to higher viewability and therefore higher click through rates, which ultimately leads to higher advertiser conversions. ### Safer -It's impossible to spread malware through advertising with AMP Ads. Not only are visitors safer, but advertiser brand perception cannot be negative.` +It's impossible to spread malware through advertising with AMPHTML ads. Not only are visitors safer, but advertiser brand perception cannot be negative.` ### More Flexible -AMP Ads are designed to work on both AMP and Non-AMP webpages, including desktop where the ad tagging library supports it. (e.g. GPT) +AMPHTML ads are designed to work on both AMP and Non-AMP webpages, including desktop where the ad tagging library supports it. (e.g. GPT) # Current status -The AMP Ads format spec has been [released](https://github.com/ampproject/amphtml/blob/master/extensions/amp-a4a/amp-a4a-format.md) and any creative developer can create AMP Ads. A number of ad providers are working on automatically converting ads to AMP Ads whenever possible. e.g. AdSense. +The AMPHTML ads format spec has been [released](https://github.com/ampproject/amphtml/blob/master/extensions/amp-a4a/amp-a4a-format.md) and any creative developer can create AMPHTML ads. A number of ad providers are working on automatically converting ads to AMPHTML ads whenever possible. e.g. AdSense. Here is how you can participate. If you are: @@ -36,7 +36,7 @@ Here is how you can participate. If you are: If publishers want to serve their direct-sold ad formats they must create the ads in [A4A format](https://github.com/ampproject/amphtml/blob/master/extensions/amp-a4a/amp-a4a-format.md) (or use a creative agency), and deliver them using an AMP Ad supported ad server. -The following adservers support serving AMP Ads at the moment: +The following adservers support serving AMPHTML ads at the moment: 1. DoubleClick for Publishers 2. TripleLift 3. Dianomi @@ -48,7 +48,7 @@ Fast Fetch supports Real Time Config: publisher-specified, multiple, simultaneou ## Creative Agencies -If you are a creative agency, please express interest via [this form](https://goo.gl/forms/P2zpQT3aIEU1UsWj2) so we can include you in any outreach that's AMP Ads-related. +If you are a creative agency, please express interest via [this form](https://goo.gl/forms/P2zpQT3aIEU1UsWj2) so we can include you in any outreach that's AMPHTML-related. ## Ad Networks/ Ad Servers @@ -60,27 +60,27 @@ Ad networks and ad servers can integrate with [Cloudflare](https://blog.cloudfla #### Are there any AMP Ad samples? Yes. A number of great looking ads developed in AMP format can be found [here](https://ampbyexample.com/amp-ads/#amp-ads/experimental_ads). They use advanced components in AMP. They give the user a great experience while ensuring that the performance remains great. -#### Are there any tools to create AMP Ads? -Yes. [Celtra](http://www.prnewswire.com/news-releases/celtra-partners-with-the-amp-project-showcases-amp-ad-creation-at-google-io-event-300459514.html) provides out of the box support for AMP ads in their ad creator platform. Other tools like [Google Web Designer](https://www.google.com/webdesigner/) are also in the process of adding support. +#### Are there any tools to create ads in AMPHTML? +Yes. [Celtra](http://www.prnewswire.com/news-releases/celtra-partners-with-the-amp-project-showcases-amp-ad-creation-at-google-io-event-300459514.html) provides out of the box support for AMPHTML ads in their ad creator platform. Other tools like [Google Web Designer](https://www.google.com/webdesigner/) are also in the process of adding support. #### How can I verify that an AMP Ad is valid? Depending on your development environment, there are a few options: - Use the [AMP validator NPM](https://www.npmjs.com/package/amphtml-validator) module to build your own - Use the [AMP validator](https://validator.ampproject.org/) for one off testing - Partner with [Cloudflare](https://blog.cloudflare.com/firebolt/) and use their public validator end point. -On AMP pages, AMP ads must be valid in order to get them to render quickly. If not, the ads will still render but slower. +On AMP pages, AMPHTML ads must be valid in order to get them to render quickly. If not, the ads will still render but slower. -#### Do AMP Ads support 3rd party verification and viewability detection? +#### Do AMPHTML ads support 3rd party verification and viewability detection? Yes, there is native support for verification and viewability detection using amp-analytics. (e.g. Google’s ActiveView integrates this way). There are also other vendors like MOAT that are actively implementing support for it. -#### Does AMP Ads support timeline based animation? +#### Does AMPHTML ads support timeline based animation? Yes. Learn more about it [here](https://github.com/ampproject/amphtml/blob/master/extensions/amp-animation/amp-animation.md). -#### Most ads have tappable targets and configurable ad exits. Does AMP Ads have a similar mechanism? +#### Most ads have tappable targets and configurable ad exits. Does AMPHTML ads have a similar mechanism? Yes. Learn more about it [here](https://github.com/ampproject/amphtml/blob/master/extensions/amp-ad-exit/amp-ad-exit.md). -#### Where can I learn more about AMP Ads? +#### Where can I learn more about AMPHTML ads? The public [website](https://ampproject.org/ads) is a good place to start. #### I can’t find what I need, where can I ask questions? From a941bdac1df31d920fa22d4aa506d931f0304cba Mon Sep 17 00:00:00 2001 From: Gabriel Majoulet Date: Wed, 7 Feb 2018 09:13:12 -0500 Subject: [PATCH 40/65] Linear transitions for media story pages progress bar. (#13296) * Linear transitions for media story pages progress bar. * Unobfuscate the unobfuscated code. --- extensions/amp-story/0.1/page-advancement.js | 2 +- extensions/amp-story/0.1/progress-bar.js | 24 +++++++++++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/extensions/amp-story/0.1/page-advancement.js b/extensions/amp-story/0.1/page-advancement.js index 203339e6418d..6c1e795febb7 100644 --- a/extensions/amp-story/0.1/page-advancement.js +++ b/extensions/amp-story/0.1/page-advancement.js @@ -26,7 +26,7 @@ import {scopedQuerySelector} from '../../../src/dom'; const NEXT_SCREEN_AREA_RATIO = 0.75; /** @const {number} */ -const POLL_INTERVAL_MS = 250; +export const POLL_INTERVAL_MS = 300; /** @const @enum */ export const TapNavigationDirection = { diff --git a/extensions/amp-story/0.1/progress-bar.js b/extensions/amp-story/0.1/progress-bar.js index 39d1599be444..ddbeed8b2168 100644 --- a/extensions/amp-story/0.1/progress-bar.js +++ b/extensions/amp-story/0.1/progress-bar.js @@ -13,14 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import {POLL_INTERVAL_MS} from './page-advancement'; import {Services} from '../../../src/services'; import {dev} from '../../../src/log'; import {scale, setImportantStyles} from '../../../src/style'; import {scopedQuerySelector} from '../../../src/dom'; -/** @const {string} */ -const TRANSITION = 'transform 0.2s ease'; +/** + * Transition used to show the progress of a media. Has to be linear so the + * animation is smooth and constant. + * @const {string} + */ +const TRANSITION_LINEAR = `transform ${POLL_INTERVAL_MS}ms linear`; + +/** + * Transition used to fully fill or unfill a progress bar item. + * @const {string} + */ +const TRANSITION_EASE = 'transform 200ms ease'; /** @@ -144,9 +155,16 @@ export class ProgressBar { `.i-amphtml-story-page-progress-bar:nth-child(${nthChildIndex}) ` + '.i-amphtml-story-page-progress-value'); this.vsync_.mutate(() => { + let transition = 'none'; + if (withTransition) { + // Using an eased transition only if filling the bar to 0 or 1. + transition = + (progress === 1 || progress === 0) ? + TRANSITION_EASE : TRANSITION_LINEAR; + } setImportantStyles(dev().assertElement(progressEl), { 'transform': scale(`${progress},1`), - 'transition': withTransition ? TRANSITION : 'none', + 'transition': transition, }); }); } From 3d54a686ed31e13e5c3ef84063f16b18dc6bd059 Mon Sep 17 00:00:00 2001 From: Raghu Simha Date: Wed, 7 Feb 2018 09:20:50 -0500 Subject: [PATCH 41/65] Fix gulp test --a4a (#13331) --- build-system/config.js | 18 +++++++++--------- build-system/tasks/runtime-test.js | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build-system/config.js b/build-system/config.js index dfb1f6801454..b1f52c65bf7d 100644 --- a/build-system/config.js +++ b/build-system/config.js @@ -15,8 +15,11 @@ */ 'use strict'; -const commonTestPaths = [ +const initTestsPath = [ 'test/_init_tests.js', +]; + +const commonTestPaths = initTestsPath.concat([ 'test/fixtures/*.html', { pattern: 'test/fixtures/served/*.html', @@ -54,25 +57,23 @@ const commonTestPaths = [ nocache: false, watched: false, }, -]; +]); const simpleTestPath = [ 'test/simple-test.js', ]; -const basicTestPaths = [ +const testPaths = commonTestPaths.concat([ 'test/**/*.js', 'ads/**/test/test-*.js', 'extensions/**/test/**/*.js', -]; - -const testPaths = commonTestPaths.concat(basicTestPaths); +]); -const a4aTestPaths = [ +const a4aTestPaths = initTestsPath.concat([ 'extensions/amp-a4a/**/test/**/*.js', 'extensions/amp-ad-network-*/**/test/**/*.js', 'ads/google/a4a/test/*.js', -]; +]); const chaiAsPromised = [ 'test/chai-as-promised/chai-as-promised.js', @@ -99,7 +100,6 @@ const integrationTestPaths = commonTestPaths.concat([ module.exports = { commonTestPaths, simpleTestPath, - basicTestPaths, testPaths, a4aTestPaths, chaiAsPromised, diff --git a/build-system/tasks/runtime-test.js b/build-system/tasks/runtime-test.js index d6568d27631b..fab215f38d80 100644 --- a/build-system/tasks/runtime-test.js +++ b/build-system/tasks/runtime-test.js @@ -240,7 +240,7 @@ function runTests() { c.files = c.files.concat(config.unitTestPaths); } } else if (argv.a4a) { - c.files = c.files.concat(config.commonTestPaths, config.a4aTestPaths); + c.files = c.files.concat(config.a4aTestPaths); } else { c.files = c.files.concat(config.testPaths); } From b630522dddd1d7c5b82cdb41baf8a7ee0d69f25e Mon Sep 17 00:00:00 2001 From: Enrique Marroquin <5449100+Enriqe@users.noreply.github.com> Date: Wed, 7 Feb 2018 10:34:43 -0500 Subject: [PATCH 42/65] Resets and disables touch zoom for amp stories. (#13316) --- extensions/amp-story/0.1/amp-story.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extensions/amp-story/0.1/amp-story.js b/extensions/amp-story/0.1/amp-story.js index 961345c6e4f2..32c95fd5dc6b 100644 --- a/extensions/amp-story/0.1/amp-story.js +++ b/extensions/amp-story/0.1/amp-story.js @@ -459,6 +459,8 @@ export class AmpStory extends AMP.BaseElement { 'overflow': 'hidden', }); + this.getViewport().resetTouchZoom(); + this.getViewport().disableTouchZoom(); this.maybeLockScreenOrientation_(); } From 0de8ae29157f31d9ee931932fca6c32af32c9430 Mon Sep 17 00:00:00 2001 From: Derek Springer Date: Wed, 7 Feb 2018 17:25:13 +0100 Subject: [PATCH 43/65] Update Pubmine amp-ad to new 'async' implementation (#12855) * Updating to new async tag * Linter fix: imports must be alphabetical --- ads/pubmine.js | 38 ++++++++++++++++++++++++-------------- ads/pubmine.md | 15 +++++++++------ 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/ads/pubmine.js b/ads/pubmine.js index 9737aa4d7669..c713fdb3d919 100644 --- a/ads/pubmine.js +++ b/ads/pubmine.js @@ -14,10 +14,9 @@ * limitations under the License. */ -import {getSourceOrigin, getSourceUrl} from '../src/url'; import {validateData, writeScript} from '../3p/3p'; -const pubmineOptional = ['adsafe', 'section', 'wordads'], +const pubmineOptional = ['section', 'pt', 'ht'], pubmineRequired = ['siteid'], pubmineURL = 'https://s.pubmine.com/head.js'; @@ -28,28 +27,39 @@ const pubmineOptional = ['adsafe', 'section', 'wordads'], export function pubmine(global, data) { validateData(data, pubmineRequired, pubmineOptional); - global._ipw_custom = { // eslint-disable-line google-camelcase/google-camelcase - adSafe: 'adsafe' in data ? data.adsafe : '0', - amznPay: [], - domain: getSourceOrigin(global.context.location.href), - pageURL: getSourceUrl(global.context.location.href), - wordAds: 'wordads' in data ? data.wordads : '0', + global.__ATA_PP = { renderStartCallback: () => global.context.renderStart(), + pt: 'pt' in data ? data.pt : 1, + ht: 'ht' in data ? data.ht : 1, + tn: 'amp', + amp: true, }; + + global.__ATA = global.__ATA || {}; + global.__ATA.cmd = global.__ATA.cmd || []; + global.__ATA.criteo = global.__ATA.criteo || {}; + global.__ATA.criteo.cmd = global.__ATA.criteo.cmd || []; writeScript(global, pubmineURL); const o = { sectionId: data['siteid'] + ('section' in data ? data.section : '1'), - height: data.height, + height: data.height == 250 ? 250 : data.height - 15, width: data.width, }, wr = global.document.write; wr.call(global.document, - `` + `
+ +
` ); } diff --git a/ads/pubmine.md b/ads/pubmine.md index 033ebf5d6609..3d3aafcd2475 100644 --- a/ads/pubmine.md +++ b/ads/pubmine.md @@ -21,7 +21,7 @@ limitations under the License. ### Basic ```html - @@ -30,11 +30,11 @@ limitations under the License. ### With all attributes ```html - ``` @@ -43,6 +43,9 @@ limitations under the License. For further Pubmine configuration information, please [contact us](https://wordpress.com/help/contact). +Please note that the height parameter should be 15 greater than your ad size to ensure there is enough +room for the "Report this ad" link. + ### Pubmine Required parameters | Parameter | Description | @@ -53,6 +56,6 @@ For further Pubmine configuration information, please [contact us](https://wordp | Parameter | Description | |:------------- |:-------------| -| **`data-adsafe`** | Flag for adsafe content | | **`data-section`** | Pubmine slot identifier | -| **`data-wordads`** | Flag for WordAds or other | +| **`data-pt`** | Enum value for page type | +| **`data-ht`** | Enum value for hosting type | From bd9cdcc9143d66001fc61f7244e642c67ce964c6 Mon Sep 17 00:00:00 2001 From: hamousavi Date: Mon, 5 Feb 2018 09:37:40 -0500 Subject: [PATCH 44/65] the code for whitelist of variables in url substitution. The code does not contain functional testing --- src/service/url-replacements-impl.js | 15 +++++++++++++++ src/service/variable-source.js | 13 +++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/service/url-replacements-impl.js b/src/service/url-replacements-impl.js index abba66686812..d2b570e15905 100644 --- a/src/service/url-replacements-impl.js +++ b/src/service/url-replacements-impl.js @@ -74,6 +74,21 @@ export class GlobalVariableSource extends VariableSource { /** @const {!./ampdoc-impl.AmpDoc} */ this.ampdoc = ampdoc; + // A meta[name="amp-action-whitelist"] tag, if present, contains, + // in its content attribute, a whitelist of actions on the special AMP target. + if (this.ampVariableSubstitutionWhitelist_ === undefined + && this.ampdoc.getRootNode() && this.ampdoc.getRootNode().head) { + const meta = + this.ampdoc.getRootNode().head + .querySelector('meta[name="amp-variable-substitution-whitelist"]'); + + // Cache the whitelist of allowed AMP actions (if provided). + if (meta) { + this.ampVariableSubstitutionWhitelist_ = meta.getAttribute('content').split(',') + .map(action => action.trim()); + } + } + /** * @private * @const {function(!./ampdoc-impl.AmpDoc): diff --git a/src/service/variable-source.js b/src/service/variable-source.js index a0519686920d..c922f8102b7c 100644 --- a/src/service/variable-source.js +++ b/src/service/variable-source.js @@ -121,6 +121,9 @@ export class VariableSource { /** @private {boolean} */ this.initialized_ = false; + + /** @const @private {!Array|undefined} */ + this.ampVariableSubstitutionWhitelist_ = undefined; } /** @@ -164,6 +167,11 @@ export class VariableSource { */ set(varName, syncResolver) { dev().assert(varName.indexOf('RETURN') == -1); + if (this.ampVariableSubstitutionWhitelist_ && + !this.ampVariableSubstitutionWhitelist_.includes(varName)) { + this.replacements_.delete(varName); + return this; + } this.replacements_[varName] = this.replacements_[varName] || {sync: undefined, async: undefined}; this.replacements_[varName].sync = syncResolver; @@ -184,6 +192,11 @@ export class VariableSource { */ setAsync(varName, asyncResolver) { dev().assert(varName.indexOf('RETURN') == -1); + if (this.ampVariableSubstitutionWhitelist_ && + !this.ampVariableSubstitutionWhitelist_.includes(varName)) { + this.replacements_.delete(varName); + return this; + } this.replacements_[varName] = this.replacements_[varName] || {sync: undefined, async: undefined}; this.replacements_[varName].async = asyncResolver; From 6828fc4537bb143682e1417b9ac03d8c5c1e906c Mon Sep 17 00:00:00 2001 From: hamousavi Date: Mon, 5 Feb 2018 12:29:59 -0500 Subject: [PATCH 45/65] Added some functional tests --- src/service/url-replacements-impl.js | 9 +++-- src/service/variable-source.js | 8 +++- test/functional/url-expander/test-expander.js | 37 +++++++++++++++++++ 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/service/url-replacements-impl.js b/src/service/url-replacements-impl.js index d2b570e15905..7a286c130c07 100644 --- a/src/service/url-replacements-impl.js +++ b/src/service/url-replacements-impl.js @@ -76,7 +76,7 @@ export class GlobalVariableSource extends VariableSource { // A meta[name="amp-action-whitelist"] tag, if present, contains, // in its content attribute, a whitelist of actions on the special AMP target. - if (this.ampVariableSubstitutionWhitelist_ === undefined + if (this.ampVariableSubstitutionWhitelist_ === undefined && this.ampdoc.getRootNode() && this.ampdoc.getRootNode().head) { const meta = this.ampdoc.getRootNode().head @@ -84,10 +84,11 @@ export class GlobalVariableSource extends VariableSource { // Cache the whitelist of allowed AMP actions (if provided). if (meta) { - this.ampVariableSubstitutionWhitelist_ = meta.getAttribute('content').split(',') - .map(action => action.trim()); + this.ampVariableSubstitutionWhitelist_ = + meta.getAttribute('content').split(',') + .map(action => action.trim()); } - } + } /** * @private diff --git a/src/service/variable-source.js b/src/service/variable-source.js index c922f8102b7c..880c67f999ad 100644 --- a/src/service/variable-source.js +++ b/src/service/variable-source.js @@ -169,9 +169,9 @@ export class VariableSource { dev().assert(varName.indexOf('RETURN') == -1); if (this.ampVariableSubstitutionWhitelist_ && !this.ampVariableSubstitutionWhitelist_.includes(varName)) { - this.replacements_.delete(varName); return this; } + this.replacements_[varName] = this.replacements_[varName] || {sync: undefined, async: undefined}; this.replacements_[varName].sync = syncResolver; @@ -194,7 +194,6 @@ export class VariableSource { dev().assert(varName.indexOf('RETURN') == -1); if (this.ampVariableSubstitutionWhitelist_ && !this.ampVariableSubstitutionWhitelist_.includes(varName)) { - this.replacements_.delete(varName); return this; } this.replacements_[varName] = @@ -259,6 +258,11 @@ export class VariableSource { * @private */ buildExpr_(keys, isV2) { + // If a whitelist is provided, the keys must all belong to the whitelist. + if (this.ampVariableSubstitutionWhitelist_) { + keys = keys.filter(key => + this.ampVariableSubstitutionWhitelist_.includes(key)); + } // The keys must be sorted to ensure that the longest keys are considered // first. This avoids a problem where a RANDOM conflicts with RANDOM_ONE. keys.sort((s1, s2) => s2.length - s1.length); diff --git a/test/functional/url-expander/test-expander.js b/test/functional/url-expander/test-expander.js index 3b699f3db91c..1435c3becd4e 100644 --- a/test/functional/url-expander/test-expander.js +++ b/test/functional/url-expander/test-expander.js @@ -14,8 +14,10 @@ * limitations under the License. */ +import {AmpDocSingle} from '../../../src/service/ampdoc-impl'; import {Expander} from '../../../src/service/url-expander/expander'; import {GlobalVariableSource} from '../../../src/service/url-replacements-impl'; +import {createElementWithAttributes} from '../../../src/dom'; describes.realWin('Expander', { amp: { @@ -86,6 +88,41 @@ describes.realWin('Expander', { }); }); + describe('#whitelist', () => { + let variableSource; + let expander; + + const mockBindings = { + RANDOM: () => 0.1234, + ABC: () => 'three', + ABCD: () => 'four', + }; + beforeEach(() => { + window.document.head.appendChild( + createElementWithAttributes(window.document, 'meta', { + name: 'amp-variable-substitution-whitelist', + content: 'ABC,ABCD,CANONICAL', + })); + const ampdoc = new AmpDocSingle(window); + variableSource = new GlobalVariableSource(ampdoc); + expander = new Expander(variableSource); + }); + + it('should not replace unwhitelisted RANDOM', () => { + const url = 'http://www.google.com/?test=RANDOM'; + const expected = 'http://www.google.com/?test=RANDOM'; + return expect(expander.expand(url, mockBindings)) + .to.eventually.equal(expected); + }); + + it('should replace whitelisted ABCD', () => { + const url = 'http://www.google.com/?test=ABCD'; + const expected = 'http://www.google.com/?test=four'; + return expect(expander.expand(url, mockBindings)) + .to.eventually.equal(expected); + }); + }); + describe('#expand', () => { function mockClientIdFn(str) { if (str === '__ga') { From 7b211a1486e135728a99d814013e33b83cf6d22f Mon Sep 17 00:00:00 2001 From: Raghu Simha Date: Wed, 7 Feb 2018 13:20:55 -0500 Subject: [PATCH 46/65] Fix flaky Karma startup by refreshing the `wd` cache (#13328) --- build-system/tasks/runtime-test.js | 7 +++++++ contributing/DEVELOPING.md | 2 -- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/build-system/tasks/runtime-test.js b/build-system/tasks/runtime-test.js index fab215f38d80..244e1dc2b13b 100644 --- a/build-system/tasks/runtime-test.js +++ b/build-system/tasks/runtime-test.js @@ -20,6 +20,7 @@ const applyConfig = require('./prepend-global/index.js').applyConfig; const argv = require('minimist')(process.argv.slice(2)); const colors = require('ansi-colors'); const config = require('../config'); +const exec = require('../exec').exec; const fs = require('fs'); const gulp = require('gulp-help')(require('gulp')); const Karma = require('karma').Server; @@ -123,6 +124,11 @@ function getAdTypes() { return adTypes; } +// Mitigates https://github.com/karma-runner/karma-sauce-launcher/issues/117 +// by refreshing the wd cache so that Karma can launch without an error. +function refreshKarmaWdCache() { + exec('node ./node_modules/wd/scripts/build-browser-scripts.js'); +} /** * Prints help messages for args if tests are being run for local development. @@ -320,6 +326,7 @@ function runTests() { let resolver; const deferred = new Promise(resolverIn => {resolver = resolverIn;}); + refreshKarmaWdCache(); new Karma(c, function(exitCode) { server.emit('kill'); if (exitCode) { diff --git a/contributing/DEVELOPING.md b/contributing/DEVELOPING.md index 01b52247c987..d15db4fd895b 100644 --- a/contributing/DEVELOPING.md +++ b/contributing/DEVELOPING.md @@ -198,8 +198,6 @@ To run the tests on Sauce Labs: ``` * It may take a few minutes for the tests to start. You can see the status of your tests on the Sauce Labs [Automated Tests](https://saucelabs.com/beta/dashboard/tests) dashboard. (You can also see the status of your proxy on the [Tunnels](https://saucelabs.com/beta/tunnels) dashboard. -* If you see "Cannot find module '../build/safe-execute'", this is caused by a caching issue - try uninstalling and reinstalling the 'wd' module as described in [karma-sauce-launcher issue #117](https://github.com/karma-runner/karma-sauce-launcher/issues/117). - ## Visual Diff Tests **NOTE:** *We are working on giving all `ampproject/amphtml` committers automatic access to visual diff test results. Until this is in place, you can fill out [this](https://docs.google.com/forms/d/e/1FAIpQLScZma6qVJtYUTqSm4KtiF3Zc-n5ukNe2GXNFqnaHxospsz0sQ/viewform) form, and your request should be approved soon.* From e2c3db64f3441902b2e525acc9c59a78b1532e03 Mon Sep 17 00:00:00 2001 From: Hongfei Ding Date: Wed, 7 Feb 2018 11:24:21 -0800 Subject: [PATCH 47/65] Clean up unnecessary dependency of ads/_config in A4A test. (#13342) --- extensions/amp-a4a/0.1/test/test-a4a-integration.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/extensions/amp-a4a/0.1/test/test-a4a-integration.js b/extensions/amp-a4a/0.1/test/test-a4a-integration.js index 11fb49db84a5..f70ab891084e 100644 --- a/extensions/amp-a4a/0.1/test/test-a4a-integration.js +++ b/extensions/amp-a4a/0.1/test/test-a4a-integration.js @@ -24,7 +24,6 @@ import * as sinon from 'sinon'; import {AMP_SIGNATURE_HEADER} from '../signature-verifier'; import {FetchMock, networkFailure} from './fetch-mock'; import {MockA4AImpl, TEST_URL} from './utils'; -import {adConfig} from '../../../../ads/_config'; import {createIframePromise} from '../../../../testing/iframe'; import {getA4ARegistry} from '../../../../ads/_a4a-config'; import {installCryptoService} from '../../../../src/service/crypto-impl'; @@ -92,7 +91,6 @@ describe('integration test: a4a', () => { beforeEach(() => { sandbox = sinon.sandbox.create(); a4aRegistry = getA4ARegistry(); - adConfig['mock'] = {}; a4aRegistry['mock'] = () => {return true;}; return createIframePromise().then(f => { fixture = f; @@ -126,7 +124,6 @@ describe('integration test: a4a', () => { fetchMock./*OK*/restore(); sandbox.restore(); resetScheduledElementForTesting(window, 'amp-a4a'); - delete adConfig['mock']; delete a4aRegistry['mock']; }); From 3e41cb251caec3096f8ff6a71cea7b10f9483ca0 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Wed, 7 Feb 2018 14:51:58 -0500 Subject: [PATCH 48/65] =?UTF-8?q?Update=20browserify=20to=20the=20latest?= =?UTF-8?q?=20version=20=F0=9F=9A=80=20(#13333)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(package): update browserify to version 16.0.0 * chore(package): update lockfile https://npm.im/greenkeeper-lockfile --- package.json | 2 +- yarn.lock | 83 +++++++++++++++++----------------------------------- 2 files changed, 28 insertions(+), 57 deletions(-) diff --git a/package.json b/package.json index 04fcd891240d..f1a9e4acdd20 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "baconipsum": "0.1.2", "bluebird": "3.5.1", "body-parser": "1.18.2", - "browserify": "15.0.0", + "browserify": "16.0.0", "browserify-istanbul": "3.0.1", "chai": "4.1.2", "chai-as-promised": "7.1.1", diff --git a/yarn.lock b/yarn.lock index 151922e65745..43438301ca73 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1704,9 +1704,9 @@ browserify-zlib@~0.2.0: dependencies: pako "~1.0.5" -browserify@15.0.0: - version "15.0.0" - resolved "https://registry.yarnpkg.com/browserify/-/browserify-15.0.0.tgz#37fa47b46846cddd820e084870a66ff6def5164a" +browserify@16.0.0: + version "16.0.0" + resolved "https://registry.yarnpkg.com/browserify/-/browserify-16.0.0.tgz#ee077dc2e883f5a710648e3c4204acc736aeb972" dependencies: JSONStream "^1.0.3" assert "^1.4.0" @@ -1715,15 +1715,15 @@ browserify@15.0.0: browserify-zlib "~0.2.0" buffer "^5.0.2" cached-path-relative "^1.0.0" - concat-stream "~1.5.1" + concat-stream "^1.6.0" console-browserify "^1.1.0" constants-browserify "~1.0.0" crypto-browserify "^3.0.0" defined "^1.0.0" deps-sort "^2.0.0" - domain-browser "~1.1.0" + domain-browser "^1.2.0" duplexer2 "~0.1.2" - events "~1.1.0" + events "^2.0.0" glob "^7.1.0" has "^1.0.0" htmlescape "^1.1.0" @@ -1731,7 +1731,8 @@ browserify@15.0.0: inherits "~2.0.1" insert-module-globals "^7.0.0" labeled-stream-splicer "^2.0.0" - module-deps "^5.0.0" + mkdirp "^0.5.0" + module-deps "^6.0.0" os-browserify "~0.3.0" parents "^1.0.1" path-browserify "~0.0.0" @@ -1750,7 +1751,7 @@ browserify@15.0.0: syntax-error "^1.1.1" through2 "^2.0.0" timers-browserify "^1.0.1" - tty-browserify "~0.0.0" + tty-browserify "0.0.1" url "~0.11.0" util "~0.10.1" vm-browserify "~0.0.1" @@ -3137,6 +3138,10 @@ dom-serialize@^2.2.0: extend "^3.0.0" void-elements "^2.0.0" +domain-browser@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" + domain-browser@~1.1.0: version "1.1.7" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc" @@ -3480,7 +3485,7 @@ eslint-visitor-keys@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" -eslint@4.17.0: +eslint@4.17.0, eslint@^4.15.0: version "4.17.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.17.0.tgz#dc24bb51ede48df629be7031c71d9dc0ee4f3ddf" dependencies: @@ -3564,48 +3569,6 @@ eslint@^4.0.0: table "^4.0.1" text-table "~0.2.0" -eslint@^4.15.0: - version "4.17.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.17.0.tgz#dc24bb51ede48df629be7031c71d9dc0ee4f3ddf" - dependencies: - ajv "^5.3.0" - babel-code-frame "^6.22.0" - chalk "^2.1.0" - concat-stream "^1.6.0" - cross-spawn "^5.1.0" - debug "^3.1.0" - doctrine "^2.1.0" - eslint-scope "^3.7.1" - eslint-visitor-keys "^1.0.0" - espree "^3.5.2" - esquery "^1.0.0" - esutils "^2.0.2" - file-entry-cache "^2.0.0" - functional-red-black-tree "^1.0.1" - glob "^7.1.2" - globals "^11.0.1" - ignore "^3.3.3" - imurmurhash "^0.1.4" - inquirer "^3.0.6" - is-resolvable "^1.0.0" - js-yaml "^3.9.1" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.3.0" - lodash "^4.17.4" - minimatch "^3.0.2" - mkdirp "^0.5.1" - natural-compare "^1.4.0" - optionator "^0.8.2" - path-is-inside "^1.0.2" - pluralize "^7.0.0" - progress "^2.0.0" - require-uncached "^1.0.3" - semver "^5.3.0" - strip-ansi "^4.0.0" - strip-json-comments "~2.0.1" - table "^4.0.1" - text-table "~0.2.0" - espower-location-detector@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/espower-location-detector/-/espower-location-detector-1.0.0.tgz#a17b7ecc59d30e179e2bef73fb4137704cb331b5" @@ -3720,6 +3683,10 @@ eventemitter3@1.x.x: version "1.2.0" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508" +events@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/events/-/events-2.0.0.tgz#cbbb56bf3ab1ac18d71c43bb32c86255062769f2" + events@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" @@ -7006,9 +6973,9 @@ module-deps@^4.0.8: through2 "^2.0.0" xtend "^4.0.0" -module-deps@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-5.0.1.tgz#3bc47c14b0a6d925aff2ec4a177b456a96ae0396" +module-deps@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-6.0.0.tgz#4417b49a4f4d7af79b104186e5389ea99b1dc837" dependencies: JSONStream "^1.0.3" browser-resolve "^1.7.0" @@ -7020,7 +6987,7 @@ module-deps@^5.0.0: inherits "^2.0.1" parents "^1.0.0" readable-stream "^2.0.2" - resolve "^1.1.3" + resolve "^1.4.0" stream-combiner2 "^1.1.1" subarg "^1.0.0" through2 "^2.0.0" @@ -8982,7 +8949,7 @@ resolve@1.1.7, resolve@1.1.x: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" -resolve@^1.1.3, resolve@^1.1.4, resolve@^1.1.6, resolve@^1.1.7: +resolve@^1.1.3, resolve@^1.1.4, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.4.0: version "1.5.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36" dependencies: @@ -10146,6 +10113,10 @@ tsscmp@1.0.5, tsscmp@~1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.5.tgz#7dc4a33af71581ab4337da91d85ca5427ebd9a97" +tty-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811" + tty-browserify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" From 537a7d95dcf97d325294b7b8d80422c61f927666 Mon Sep 17 00:00:00 2001 From: William Chou Date: Wed, 7 Feb 2018 15:10:46 -0500 Subject: [PATCH 49/65] amp-bind: Warn during verify instead of error (#13236) * change amp-bind verification to user warning and clarify message * fix presubmit * fix test --- extensions/amp-bind/0.1/bind-impl.js | 15 ++++++++------- .../0.1/test/integration/test-bind-impl.js | 16 +++++++++------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/extensions/amp-bind/0.1/bind-impl.js b/extensions/amp-bind/0.1/bind-impl.js index 3ed02accb38b..e04eb1fd26f7 100644 --- a/extensions/amp-bind/0.1/bind-impl.js +++ b/extensions/amp-bind/0.1/bind-impl.js @@ -962,11 +962,12 @@ export class Bind { * @private */ verifyBinding_(boundProperty, element, expectedValue) { - const property = boundProperty.property; + const {property, expressionString} = boundProperty; + const tagName = element.tagName; // Don't show a warning for bind-only attributes, // like 'slide' on amp-carousel. - const bindOnlyAttrs = BIND_ONLY_ATTRIBUTES[element.tagName]; + const bindOnlyAttrs = BIND_ONLY_ATTRIBUTES[tagName]; if (bindOnlyAttrs && bindOnlyAttrs.includes(property)) { return; } @@ -1023,11 +1024,11 @@ export class Bind { } if (!match) { - const err = user().createError(`${TAG}: ` + - `Default value for [${property}] does not match first expression ` + - `result (${expectedValue}). This can result in unexpected behavior ` + - 'after the next state change.'); - reportError(err, element); + user().warn(TAG, + `Default value for <${tagName} [${property}]="${expressionString}"> ` + + `does not match first result (${expectedValue}). We recommend ` + + 'writing expressions with matching default values, but this can be ' + + 'safely ignored if intentional.'); } } diff --git a/extensions/amp-bind/0.1/test/integration/test-bind-impl.js b/extensions/amp-bind/0.1/test/integration/test-bind-impl.js index 04a8964bdb0c..5f8266e45048 100644 --- a/extensions/amp-bind/0.1/test/integration/test-bind-impl.js +++ b/extensions/amp-bind/0.1/test/integration/test-bind-impl.js @@ -287,10 +287,10 @@ describe.configure().ifNewChrome().run('Bind', function() { createElement(env, container, '[class]="\'foo\'" class=" foo "'); createElement(env, container, '[class]="\'\'"'); createElement(env, container, '[class]="\'bar\'" class="qux"'); // Error - const errorSpy = env.sandbox.spy(user(), 'createError'); + const warnSpy = env.sandbox.spy(user(), 'warn'); return onBindReady(env, bind).then(() => { - expect(errorSpy).to.be.calledOnce; - expect(errorSpy).calledWithMatch(/bar/); + expect(warnSpy).to.be.calledOnce; + expect(warnSpy).calledWithMatch('amp-bind', /\[class\]/); }); }); @@ -298,9 +298,10 @@ describe.configure().ifNewChrome().run('Bind', function() { window.AMP_MODE = {development: true, test: true}; // Only the initial value for [a] binding does not match. createElement(env, container, '[text]="\'a\'" [class]="\'b\'" class="b"'); - const errorSpy = env.sandbox.spy(user(), 'createError'); + const warnSpy = env.sandbox.spy(user(), 'warn'); return onBindReady(env, bind).then(() => { - expect(errorSpy).to.be.calledOnce; + expect(warnSpy).to.be.calledOnce; + expect(warnSpy).calledWithMatch('amp-bind', /\[text\]/); }); }); @@ -309,9 +310,10 @@ describe.configure().ifNewChrome().run('Bind', function() { createElement(env, container, '[disabled]="true" disabled', 'button'); createElement(env, container, '[disabled]="false"', 'button'); createElement(env, container, '[disabled]="true"', 'button'); // Mismatch. - const errorSpy = env.sandbox.spy(user(), 'createError'); + const warnSpy = env.sandbox.spy(user(), 'warn'); return onBindReady(env, bind).then(() => { - expect(errorSpy).to.be.calledOnce; + expect(warnSpy).to.be.calledOnce; + expect(warnSpy).calledWithMatch('amp-bind', /\[disabled\]/); }); }); From 8e920300b920b0e9d5f0a10d63e5be677b2069de Mon Sep 17 00:00:00 2001 From: William Chou Date: Wed, 7 Feb 2018 15:14:53 -0500 Subject: [PATCH 50/65] Change [state] usage from warning to error (#13237) --- extensions/amp-list/0.1/amp-list.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/amp-list/0.1/amp-list.js b/extensions/amp-list/0.1/amp-list.js index ff07b44920a7..dfa818fa022a 100644 --- a/extensions/amp-list/0.1/amp-list.js +++ b/extensions/amp-list/0.1/amp-list.js @@ -126,7 +126,7 @@ export class AmpList extends AMP.BaseElement { } else if (state !== undefined) { const items = isArray(state) ? state : [state]; this.renderItems_(items); - user().warn(TAG, '[state] is deprecated, please use [src] instead.'); + user().error(TAG, '[state] is deprecated, please use [src] instead.'); } } From 681ba06449e33216123f057c66549c56ca1a5335 Mon Sep 17 00:00:00 2001 From: Greg Grothaus Date: Wed, 7 Feb 2018 12:48:03 -0800 Subject: [PATCH 51/65] Validator Rollup (#13338) * Revision bump for #13036 * Revision bump for #13073 * Add DEDUPE_ON_MINIFY flags to license tags so that release process can reduce the number of identical duplicate licenses in the minified validator. * Add requires_extension to AttrSpec. * JSDoc updates. * Generated validator javascript improvements. * Add comment to ValidationError hinting at how to render. * Revision bump for #12955 * Add new error types for future CSS validation. * Revision bump for #12798 * Fix a typo. * Allow animation-timing-function for keyframes * Fix typo --- .../0.1/test/validator-amp-access-scroll.out | 2 +- ...validator-amp-access-missing-extension.out | 2 +- ...r-amp-fx-collection-missing-extension.html | 33 ++++ ...or-amp-fx-collection-missing-extension.out | 36 ++++ .../0.1/test/validator-amp-fx-collection.out | 2 +- .../validator-amp-fx-collection.protoascii | 6 +- .../0.1/test/validator-amp-mustache.out | 14 +- .../0.1/test/validator-amp-story-error.out | 4 +- validator/engine/amp4ads-parse-css.js | 2 +- validator/engine/amp4ads-parse-css_test.js | 2 +- validator/engine/css-selectors.js | 2 +- validator/engine/definitions.js | 2 +- validator/engine/htmlparser-interface.js | 14 +- validator/engine/htmlparser.js | 26 +-- validator/engine/json-testutil.js | 2 +- validator/engine/keyframes-parse-css.js | 2 +- validator/engine/keyframes-parse-css_test.js | 2 +- validator/engine/parse-srcset.js | 2 +- validator/engine/parse-url.js | 2 +- validator/engine/validator-in-browser.js | 2 +- validator/engine/validator.js | 47 ++++- validator/light/dom-walker.js | 10 +- .../testdata/feature_tests/extensions.out | 4 +- .../feature_tests/mandatory_dimensions.out | 52 ++--- .../style_amp_keyframes_error.out | 2 +- validator/testdata/feature_tests/urls.out | 14 +- validator/validator-main.protoascii | 181 +++++++++++------- validator/validator.proto | 20 +- validator/validator_gen_js.py | 28 ++- 29 files changed, 340 insertions(+), 177 deletions(-) create mode 100644 extensions/amp-fx-collection/0.1/test/validator-amp-fx-collection-missing-extension.html create mode 100644 extensions/amp-fx-collection/0.1/test/validator-amp-fx-collection-missing-extension.out diff --git a/extensions/amp-access-scroll/0.1/test/validator-amp-access-scroll.out b/extensions/amp-access-scroll/0.1/test/validator-amp-access-scroll.out index a8a20bc7a099..17fa1bd07fbd 100644 --- a/extensions/amp-access-scroll/0.1/test/validator-amp-access-scroll.out +++ b/extensions/amp-access-scroll/0.1/test/validator-amp-access-scroll.out @@ -40,4 +40,4 @@ PASS | ad goes here | | -| \ No newline at end of file +| diff --git a/extensions/amp-access/0.1/test/validator-amp-access-missing-extension.out b/extensions/amp-access/0.1/test/validator-amp-access-missing-extension.out index 4c94e1d576ed..2c72ea8f4560 100644 --- a/extensions/amp-access/0.1/test/validator-amp-access-missing-extension.out +++ b/extensions/amp-access/0.1/test/validator-amp-access-missing-extension.out @@ -32,7 +32,7 @@ FAIL | | + + + +
+ + diff --git a/extensions/amp-fx-collection/0.1/test/validator-amp-fx-collection-missing-extension.out b/extensions/amp-fx-collection/0.1/test/validator-amp-fx-collection-missing-extension.out new file mode 100644 index 000000000000..e96dc7dce340 --- /dev/null +++ b/extensions/amp-fx-collection/0.1/test/validator-amp-fx-collection-missing-extension.out @@ -0,0 +1,36 @@ +FAIL +| +| +| +| +| +| +| +| +| +| +| +| +| +|
+>> ^~~~~~~~~ +amp-fx-collection/0.1/test/validator-amp-fx-collection-missing-extension.html:31:2 The attribute 'amp-fx' requires including the 'amp-fx-collection' extension JavaScript. [MANDATORY_AMP_TAG_MISSING_OR_INCORRECT] +| +| diff --git a/extensions/amp-fx-collection/0.1/test/validator-amp-fx-collection.out b/extensions/amp-fx-collection/0.1/test/validator-amp-fx-collection.out index 491ba4e52760..e541abb111ff 100644 --- a/extensions/amp-fx-collection/0.1/test/validator-amp-fx-collection.out +++ b/extensions/amp-fx-collection/0.1/test/validator-amp-fx-collection.out @@ -35,4 +35,4 @@ PASS |

| | -| \ No newline at end of file +| diff --git a/extensions/amp-fx-collection/validator-amp-fx-collection.protoascii b/extensions/amp-fx-collection/validator-amp-fx-collection.protoascii index a499328e6901..611ba40824db 100644 --- a/extensions/amp-fx-collection/validator-amp-fx-collection.protoascii +++ b/extensions/amp-fx-collection/validator-amp-fx-collection.protoascii @@ -14,14 +14,16 @@ # limitations under the license. # tags: { # amp-fx-collection - # Accepted amp-fx-collection attributes can be found in validator-main.protoascii + # Accepted amp-fx-collection attributes can be found in + # validator-main.protoascii html_format: AMP tag_name: "SCRIPT" extension_spec: { name: "amp-fx-collection" allowed_versions: "0.1" allowed_versions: "latest" - # amp-fx-parallax has no associated tag which indicates usage of the extension. + # amp-fx-parallax has no associated tag which indicates usage of the + # extension. requires_usage: NONE } attr_lists: "common-extension-attrs" diff --git a/extensions/amp-mustache/0.1/test/validator-amp-mustache.out b/extensions/amp-mustache/0.1/test/validator-amp-mustache.out index 6a3b268b7089..01f0d86b5969 100644 --- a/extensions/amp-mustache/0.1/test/validator-amp-mustache.out +++ b/extensions/amp-mustache/0.1/test/validator-amp-mustache.out @@ -31,7 +31,7 @@ FAIL | |