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/.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 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/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/facebook.js b/3p/facebook.js index 066fbf5cfc70..13560355c9e8 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); }); } @@ -58,6 +58,27 @@ function getPostContainer(global, data) { return container; } +/** + * Create DOM element for the Facebook embedded page plugin. + * Reference: https://developers.facebook.com/docs/plugins/page-plugin + * @param {!Window} global + * @param {!Object} data The element data + * @return {!Element} div + */ +function getPageContainer(global, data) { + const container = global.document.createElement('div'); + container.className = 'fb-page'; + container.setAttribute('data-href', data.href); + container.setAttribute('data-tabs', data.tabs); + container.setAttribute('data-hide-cover', data.hideCover); + container.setAttribute('data-show-facepile', data.showFacePile); + container.setAttribute('data-hide-cta', data.hideCta); + container.setAttribute('data-small-header', data.smallHeader); + container.setAttribute( + 'data-adapt-container-width', data.adaptContainerWidth); + return container; +} + /** * Create DOM element for the Facebook comments plugin: * Reference: https://developers.facebook.com/docs/plugins/comments @@ -104,7 +125,10 @@ function getLikeContainer(global, data) { export function facebook(global, data) { const extension = global.context.tagName; let container; - if (extension === 'AMP-FACEBOOK-LIKE') { + + if (extension === 'AMP-FACEBOOK-PAGE') { + container = getPageContainer(global, data); + } else if (extension === 'AMP-FACEBOOK-LIKE') { container = getLikeContainer(global, data); } else if (extension === 'AMP-FACEBOOK-COMMENTS') { container = getCommentsContainer(global, data); @@ -126,5 +150,5 @@ export function facebook(global, data) { }); FB.init({xfbml: true, version: 'v2.5'}); - }); + }, data.locale ? data.locale : dashToUnderline(window.navigator.language)); } 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/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..e697bbb1c298 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, @@ -460,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/nameframe.max.html b/3p/nameframe.max.html index 49aed4323388..1df56224831e 100644 --- a/3p/nameframe.max.html +++ b/3p/nameframe.max.html @@ -3,26 +3,48 @@ - + + 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/_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: { 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? 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..23a7f3473209 100644 --- a/ads/google/a4a/utils.js +++ b/ads/google/a4a/utils.js @@ -80,6 +80,11 @@ export const EXPERIMENT_ATTRIBUTE = 'data-experiment-id'; */ export let AmpAnalyticsConfigDef; +/** + * @typedef {{instantLoad: boolean, writeInBody: boolean}} + */ +export let NameframeExperimentConfig; + /** * @const {!./url-builder.QueryParameterDef} * @visibleForTesting @@ -426,7 +431,7 @@ export function additionalDimensions(win, viewportSize) { outerHeight, innerWidth, innerHeight].join(); -}; +} /** * Returns amp-analytics config for a new CSI trigger. @@ -727,7 +732,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 +799,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. @@ -806,3 +811,19 @@ export function isCdnProxy(win) { /^https:\/\/([a-zA-Z0-9_-]+\.)?cdn\.ampproject\.org((\/.*)|($))+/; return googleCdnProxyRegex.test(win.location.origin); } + +/** + * Populates the fields of the given Nameframe experiment config object. + * @param {!../../../src/service/xhr-impl.FetchResponseHeaders} headers + * @param {!NameframeExperimentConfig} nameframeConfig + */ +export function setNameframeExperimentConfigs(headers, nameframeConfig) { + const nameframeExperimentHeader = headers.get('amp-nameframe-exp'); + if (nameframeExperimentHeader) { + nameframeExperimentHeader.split(';').forEach(config => { + if (config == 'instantLoad' || config == 'writeInBody') { + nameframeConfig[config] = true; + } + }); + } +} 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/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 | 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/amp.extern.js b/build-system/amp.extern.js index 22e0d2ecc065..497c467acb95 100644 --- a/build-system/amp.extern.js +++ b/build-system/amp.extern.js @@ -265,6 +265,12 @@ data.inViewport; data.numposts; data.orderBy; data.colorscheme; +data.tabs; +data.hideCover; +data.hideCta; +data.smallHeader; +data.adaptContainerWidth; +data.showFacePile; // 3p code var twttr; diff --git a/build-system/check-package-manager.js b/build-system/check-package-manager.js index d49339daedf6..9acc0e7404ea 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,18 +80,18 @@ 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..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, @@ -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/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/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) { 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" - } -} diff --git a/build-system/tasks/presubmit-checks.js b/build-system/tasks/presubmit-checks.js index 22ea6e644fd7..ab756776f00d 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, }, @@ -514,7 +516,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', ], }, @@ -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,)[^,]*,': { @@ -619,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', ], }, }; @@ -753,7 +757,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': { @@ -843,6 +847,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/build-system/tasks/runtime-test.js b/build-system/tasks/runtime-test.js index 9657f96e35b8..244e1dc2b13b 100644 --- a/build-system/tasks/runtime-test.js +++ b/build-system/tasks/runtime-test.js @@ -20,15 +20,14 @@ 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 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 +36,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'; @@ -124,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. @@ -146,9 +151,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 +245,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.a4aTestPaths); } else { c.files = c.files.concat(config.testPaths); } @@ -345,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) { @@ -412,11 +394,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/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/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" } ] } diff --git a/contributing/DEVELOPING.md b/contributing/DEVELOPING.md index 148604d02ec7..d15db4fd895b 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. | @@ -192,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.* 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 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/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/examples/amp-story/bookend.html b/examples/amp-story/bookend.html index a8b2e9c5ae05..ea84b6a54863 100644 --- a/examples/amp-story/bookend.html +++ b/examples/amp-story/bookend.html @@ -18,6 +18,33 @@ background-color: white; } + 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/examples/facebook.amp.html b/examples/facebook.amp.html index 2308809e931b..46056c1ffa51 100644 --- a/examples/facebook.amp.html +++ b/examples/facebook.amp.html @@ -9,6 +9,7 @@ + @@ -25,6 +26,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

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

Page

+ + + + + + diff --git a/extensions/amp-a4a/0.1/amp-a4a.js b/extensions/amp-a4a/0.1/amp-a4a.js index 3c7d3cf50caa..d554db8c186f 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, @@ -220,7 +219,7 @@ export function protectFunctionWrapper( return undefined; } }; -}; +} export class AmpA4A extends AMP.BaseElement { // TODO: Add more error handling throughout code. @@ -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 @@ -1791,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-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']; }); 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'; 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/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-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-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..bd0d2d8932ed --- /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/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; } 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-facebook-comments/0.1/amp-facebook-comments.js b/extensions/amp-facebook-comments/0.1/amp-facebook-comments.js index 5b1443e3572f..d9fc0d4a95b9 100644 --- a/extensions/amp-facebook-comments/0.1/amp-facebook-comments.js +++ b/extensions/amp-facebook-comments/0.1/amp-facebook-comments.js @@ -29,6 +29,12 @@ class AmpFacebookComments extends AMP.BaseElement { /** @private {?HTMLIFrameElement} */ this.iframe_ = null; + + /** @private {string} */ + this.dataLocale_ = element.getAttribute('data-locale'); + if (!this.dataLocale_) { + this.dataLocale_ = dashToUnderline(window.navigator.language); + } } /** @override */ @@ -47,7 +53,7 @@ class AmpFacebookComments extends AMP.BaseElement { this.preconnect.url('https://facebook.com', opt_onLayout); // Hosts the facebook SDK. this.preconnect.preload( - 'https://connect.facebook.net/' + dashToUnderline(window.navigator.language) + '/sdk.js', 'script'); + 'https://connect.facebook.net/' + this.dataLocale_ + '/sdk.js', 'script'); preloadBootstrap(this.win, this.preconnect); } diff --git a/extensions/amp-facebook-comments/amp-facebook-comments.md b/extensions/amp-facebook-comments/amp-facebook-comments.md index 37897a70831e..b9286cc215f3 100644 --- a/extensions/amp-facebook-comments/amp-facebook-comments.md +++ b/extensions/amp-facebook-comments/amp-facebook-comments.md @@ -52,6 +52,12 @@ You can use the `amp-facebook-comments` component to embed the [Facebook comment The URL of the comments page. For example, `http://www.directlyrics.com/adele-25-complete-album-lyrics-news.html`. +##### data-locale (optional) + +By default, the locale is set to user's system language; however, you can specify a locale as well. + +For details on strings accepted here please visit the [Facebook API Localization page](https://developers.facebook.com/docs/internationalization) + ##### data-numposts (optional) The number of comments to show. For details, see the [Facebook comments documentation](https://developers.facebook.com/docs/plugins/comments). diff --git a/extensions/amp-facebook-like/0.1/amp-facebook-like.js b/extensions/amp-facebook-like/0.1/amp-facebook-like.js index 37ece62a93c6..e2e0ca28248d 100644 --- a/extensions/amp-facebook-like/0.1/amp-facebook-like.js +++ b/extensions/amp-facebook-like/0.1/amp-facebook-like.js @@ -29,6 +29,12 @@ class AmpFacebookLike extends AMP.BaseElement { /** @private {?HTMLIFrameElement} */ this.iframe_ = null; + + /** @private {string} */ + this.dataLocale_ = element.getAttribute('data-locale'); + if (!this.dataLocale_) { + this.dataLocale_ = dashToUnderline(window.navigator.language); + } } /** @override */ @@ -47,7 +53,7 @@ class AmpFacebookLike extends AMP.BaseElement { this.preconnect.url('https://facebook.com', opt_onLayout); // Hosts the facebook SDK. this.preconnect.preload( - 'https://connect.facebook.net/' + dashToUnderline(window.navigator.language) + '/sdk.js', 'script'); + 'https://connect.facebook.net/' + this.dataLocale_ + '/sdk.js', 'script'); preloadBootstrap(this.win, this.preconnect); } diff --git a/extensions/amp-facebook-like/amp-facebook-like.md b/extensions/amp-facebook-like/amp-facebook-like.md index 476ad91f037b..24c3cdb0c67c 100644 --- a/extensions/amp-facebook-like/amp-facebook-like.md +++ b/extensions/amp-facebook-like/amp-facebook-like.md @@ -52,6 +52,12 @@ You can use the `amp-facebook-like` component to embed the [Facebook like button The absolute URL of the page that will be liked. For example, `https://www.facebook.com/testesmegadivertidos/`. +##### data-locale (optional) + +By default, the locale is set to user's system language; however, you can specify a locale as well. + +For details on strings accepted here please visit the [Facebook API Localization page](https://developers.facebook.com/docs/internationalization) + ##### data-action (optional) The verb to display on the button. Can be either `like` or `recommend`. The default is `like`. diff --git a/extensions/amp-facebook-page/0.1/amp-facebook-page.js b/extensions/amp-facebook-page/0.1/amp-facebook-page.js new file mode 100644 index 000000000000..dabcfb4a71ae --- /dev/null +++ b/extensions/amp-facebook-page/0.1/amp-facebook-page.js @@ -0,0 +1,92 @@ +/** + * 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 {dashToUnderline} from '../../../src/string'; +import {getIframe, preloadBootstrap} from '../../../src/3p-frame'; +import {isLayoutSizeDefined} from '../../../src/layout'; +import {listenFor} from '../../../src/iframe-helper'; +import {removeElement} from '../../../src/dom'; + +class AmpFacebookPage extends AMP.BaseElement { + + /** @param {!AmpElement} element */ + constructor(element) { + super(element); + + /** @private {?HTMLIFrameElement} */ + this.iframe_ = null; + + /** @private @const {string} */ + this.dataLocale_ = element.hasAttribute('data-locale') ? + element.getAttribute('data-locale') : + dashToUnderline(window.navigator.language); + } + + /** @override */ + renderOutsideViewport() { + // We are conservative about loading heavy embeds. + // This will still start loading before they become visible, but it + // won't typically load a large number of embeds. + return 0.75; + } + + /** + * @param {boolean=} opt_onLayout + * @override + */ + preconnectCallback(opt_onLayout) { + this.preconnect.url('https://facebook.com', opt_onLayout); + // Hosts the facebook SDK. + this.preconnect.preload( + 'https://connect.facebook.net/' + this.dataLocale_ + '/sdk.js', 'script'); + preloadBootstrap(this.win, this.preconnect); + } + + /** @override */ + isLayoutSupported(layout) { + return isLayoutSizeDefined(layout); + } + + /** @override */ + layoutCallback() { + const iframe = getIframe(this.win, this.element, 'facebook'); + this.applyFillContent(iframe); + // Triggered by context.updateDimensions() inside the iframe. + listenFor(iframe, 'embed-size', data => { + this.attemptChangeHeight(data['height']).catch(() => { + /* ignore failures */ + }); + }, /* opt_is3P */true); + this.element.appendChild(iframe); + this.iframe_ = iframe; + return this.loadPromise(iframe); + } + + /** @override */ + unlayoutCallback() { + if (this.iframe_) { + removeElement(this.iframe_); + this.iframe_ = null; + } + return true; + } +} + + +AMP.extension('amp-facebook-page', '0.1', AMP => { + AMP.registerElement('amp-facebook-page', AmpFacebookPage); +}); diff --git a/extensions/amp-facebook-page/0.1/test/validator-amp-facebook-page.html b/extensions/amp-facebook-page/0.1/test/validator-amp-facebook-page.html new file mode 100644 index 000000000000..dd89961b16ed --- /dev/null +++ b/extensions/amp-facebook-page/0.1/test/validator-amp-facebook-page.html @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + diff --git a/extensions/amp-facebook-page/0.1/test/validator-amp-facebook-page.out b/extensions/amp-facebook-page/0.1/test/validator-amp-facebook-page.out new file mode 100644 index 000000000000..d27a783dc522 --- /dev/null +++ b/extensions/amp-facebook-page/0.1/test/validator-amp-facebook-page.out @@ -0,0 +1,38 @@ +PASS +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| diff --git a/extensions/amp-facebook-page/OWNERS.yaml b/extensions/amp-facebook-page/OWNERS.yaml new file mode 100644 index 000000000000..442d6fb022b6 --- /dev/null +++ b/extensions/amp-facebook-page/OWNERS.yaml @@ -0,0 +1 @@ +- nainar diff --git a/extensions/amp-facebook-page/amp-facebook-page.md b/extensions/amp-facebook-page/amp-facebook-page.md new file mode 100644 index 000000000000..0fa9d28b02e1 --- /dev/null +++ b/extensions/amp-facebook-page/amp-facebook-page.md @@ -0,0 +1,93 @@ + + +# `amp-facebook-page` + + + + + + + + + + + + + + +
DescriptionEmbeds the Facebook page plugin.
Required Script<script async custom-element="amp-facebook-page" src="https://cdn.ampproject.org/v0/amp-facebook-page-0.1.js"></script>
Supported Layoutsfill, fixed, fixed-height, flex-item, nodisplay, responsive
+ +[TOC] + +## Overview + +You can use the `amp-facebook-page` component to embed the [Facebook page plugin](https://developers.facebook.com/docs/plugins/page-plugin). + +**Example** + +```html + + +``` +## Attributes + +##### data-href (required) + +The absolute URL of the page. For example, `https://www.facebook.com/barackobama/`. + +##### data-locale (optional) + +By default, the local is set to user's system language; however, you can specify a locale as well. + +For details on strings accepted here please visit the [Facebook API Localization page](https://developers.facebook.com/docs/internationalization) + +##### data-tabs (optional) + +By default the Facebook page plugin shows the timeline activity as well. + +You can specify the tabs to render i.e. `timeline`, `events`, `messages`. Use a comma-separated list to add multiple tabs, i.e. `timeline, events`. + +##### data-hide-cover (optional) + +Attribute hides cover photo in the header. `false` (cover photo displayed) by default. + +##### data-show-face-pile (optional) + +Shows profile photos of friends who like the page. `true` by default. + +##### data-hide-cta (optional) + +Hide the custom call to action button (if available). `false` by default. + +##### data-small-header (optional) + +Use the small header instead. `false` by default. + +##### data-adapt-container-width (optional) + +Try to fit inside the container width. `true` by default. + +##### common attributes + +This element includes [common attributes](https://www.ampproject.org/docs/reference/common_attributes) extended to AMP components. + +## Validation + +See [amp-facebook-page rules](https://github.com/ampproject/amphtml/blob/master/extensions/amp-facebook-page/validator-amp-facebook-page.protoascii) in the AMP validator specification. diff --git a/extensions/amp-facebook-page/validator-amp-facebook-page.protoascii b/extensions/amp-facebook-page/validator-amp-facebook-page.protoascii new file mode 100644 index 000000000000..9e07f28b19c9 --- /dev/null +++ b/extensions/amp-facebook-page/validator-amp-facebook-page.protoascii @@ -0,0 +1,50 @@ +# +# 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-facebook-page + html_format: AMP + tag_name: "SCRIPT" + extension_spec: { + name: "amp-facebook-page" + allowed_versions: "0.1" + allowed_versions: "latest" + } + attr_lists: "common-extension-attrs" +} +tags: { # + html_format: AMP + tag_name: "AMP-FACEBOOK-PAGE" + requires_extension: "amp-facebook-page" + # data-* is generally allowed, but it's not generally mandatory. + attrs: { + name: "data-href" + mandatory: true + value_url: { + allowed_protocol: "http" + allowed_protocol: "https" + allow_relative: false + } + } + 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/extensions/amp-facebook/0.1/amp-facebook.js b/extensions/amp-facebook/0.1/amp-facebook.js index e3565cb4c017..53183f10f8a7 100644 --- a/extensions/amp-facebook/0.1/amp-facebook.js +++ b/extensions/amp-facebook/0.1/amp-facebook.js @@ -29,6 +29,12 @@ class AmpFacebook extends AMP.BaseElement { /** @private {?HTMLIFrameElement} */ this.iframe_ = null; + + /** @private {string} */ + this.dataLocale_ = element.getAttribute('data-locale'); + if (!this.dataLocale_) { + this.dataLocale_ = dashToUnderline(window.navigator.language); + } } /** @override */ @@ -47,7 +53,7 @@ class AmpFacebook extends AMP.BaseElement { this.preconnect.url('https://facebook.com', opt_onLayout); // Hosts the facebook SDK. this.preconnect.preload( - 'https://connect.facebook.net/' + dashToUnderline(window.navigator.language) + '/sdk.js', 'script'); + 'https://connect.facebook.net/' + this.dataLocale_ + '/sdk.js', 'script'); preloadBootstrap(this.win, this.preconnect); } diff --git a/extensions/amp-facebook/0.1/test/test-amp-facebook.js b/extensions/amp-facebook/0.1/test/test-amp-facebook.js index 279084ae9db3..77d0de55835c 100644 --- a/extensions/amp-facebook/0.1/test/test-amp-facebook.js +++ b/extensions/amp-facebook/0.1/test/test-amp-facebook.js @@ -37,7 +37,7 @@ describes.realWin('amp-facebook', { doc = win.document; }); - function getAmpFacebook(href, opt_embedAs) { + function getAmpFacebook(href, opt_embedAs, opt_locale) { const ampFB = doc.createElement('amp-facebook'); ampFB.setAttribute('data-href', href); ampFB.setAttribute('width', '111'); @@ -45,6 +45,11 @@ describes.realWin('amp-facebook', { if (opt_embedAs) { ampFB.setAttribute('data-embed-as', opt_embedAs); } + if (opt_locale) { + ampFB.setAttribute('data-locale', opt_locale); + } else { + ampFB.setAttribute('data-locale', 'en_US'); + } doc.body.appendChild(ampFB); return ampFB.build().then(() => { 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">
+ + + | +| +| +| | | { 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-fx-collection/0.1/test/validator-amp-fx-collection-missing-extension.html b/extensions/amp-fx-collection/0.1/test/validator-amp-fx-collection-missing-extension.html new file mode 100644 index 000000000000..42000f2a4c5d --- /dev/null +++ b/extensions/amp-fx-collection/0.1/test/validator-amp-fx-collection-missing-extension.html @@ -0,0 +1,33 @@ + + + + + + + + + + + + + +
+ + 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/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. 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-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 0a27faf1612c..328cfe206afb 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); } /** @@ -548,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-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 80% 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..6f7953aa16c2 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,48 +37,59 @@ 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; + padding-top: 56px !important; overflow: auto !important; } -.i-amphtml-lbv-top-bar { +@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; } -.i-amphtml-lbv-controls { +@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; } -.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; top: 0 !important; - height: 50px !important; 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 +101,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 +114,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 +136,21 @@ } @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 { - height: 100px !important; - } - - div.amp-lbv-button-close, - div.amp-lbv-button-gallery, - div.amp-lbv-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,59 +160,68 @@ } /* Controls */ -.amp-lbv-button-close, -.amp-lbv-button-gallery, -.amp-lbv-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; } -.amp-lbv-button-close { +.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-lbv-button-gallery { + +.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,'); } -.amp-lbv-button-slide { +.amp-lbg-button-slide { top: 0; left: 0; display: none; +} + +.amp-lbg-button-slide .amp-lbg-icon { 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 92% 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..b2b60802b652 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_); } @@ -475,10 +475,16 @@ export class AmpLightboxViewer 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(); @@ -579,7 +585,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 +596,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 +613,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 @@ -721,8 +727,9 @@ export class AmpLightboxViewer 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-viewer-trans'); + transLayer.classList.add('i-amphtml-lightbox-gallery-trans'); this.element.ownerDocument.body.appendChild(transLayer); const rect = layoutRectFromDomRect(sourceElement ./*OK*/getBoundingClientRect()); @@ -776,7 +783,7 @@ export class AmpLightboxViewer 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); } @@ -813,10 +820,11 @@ export class AmpLightboxViewer 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); - sourceElement.classList.add('i-amphtml-ghost'); const rect = layoutRectFromDomRect(sourceElement ./*OK*/getBoundingClientRect()); @@ -924,7 +932,7 @@ export class AmpLightboxViewer extends AMP.BaseElement { } /** - * Closes the lightbox-viewer + * Closes the lightbox-gallery * @return {!Promise} * @private */ @@ -944,7 +952,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 +1022,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 +1070,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 +1104,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/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 + + +``` 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.'); } } 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 | |