class CounterButton extends React.Component {
constructor(props) {
super(props);
this.state = {count: 1};
}
shouldComponentUpdate(nextProps, nextState) {
if (this.props.color !== nextProps.color) {
return true;
}
if (this.state.count !== nextState.count) {
return true;
}
return false;
}
render() {
return (
);
}
}
/*
* Copyright 2020 Google LLC
*
* 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
*
* https://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 {onBFCacheRestore} from './lib/bfcache.js';
import {bindReporter} from './lib/bindReporter.js';
import {doubleRAF} from './lib/doubleRAF.js';
import {getActivationStart} from './lib/getActivationStart.js';
import {getVisibilityWatcher} from './lib/getVisibilityWatcher.js';
import {initMetric} from './lib/initMetric.js';
import {observe} from './lib/observe.js';
import {onHidden} from './lib/onHidden.js';
import {runOnce} from './lib/runOnce.js';
import {whenActivated} from './lib/whenActivated.js';
import {whenIdle} from './lib/whenIdle.js';
import {LCPMetric, MetricRatingThresholds, ReportOpts} from './types.js';
/** Thresholds for LCP. See https://web.dev/articles/lcp#what_is_a_good_lcp_score */
export const LCPThresholds: MetricRatingThresholds = [2500, 4000];
const reportedMetricIDs: Record = {};
/**
* Calculates the [LCP](https://web.dev/articles/lcp) value for the current page and
* calls the `callback` function once the value is ready (along with the
* relevant `largest-contentful-paint` performance entry used to determine the
* value). The reported value is a `DOMHighResTimeStamp`.
*
* If the `reportAllChanges` configuration option is set to `true`, the
* `callback` function will be called any time a new `largest-contentful-paint`
* performance entry is dispatched, or once the final value of the metric has
* been determined.
*/
export const onLCP = (
onReport: (metric: LCPMetric) => void,
opts?: ReportOpts,
) => {
// Set defaults
opts = opts || {};
whenActivated(() => {
const visibilityWatcher = getVisibilityWatcher();
let metric = initMetric('LCP');
let report: ReturnType;
const handleEntries = (entries: LCPMetric['entries']) => {
// If reportAllChanges is set then call this function for each entry,
// otherwise only consider the last one.
if (!opts!.reportAllChanges) {
entries = entries.slice(-1);
}
entries.forEach((entry) => {
// Only report if the page wasn't hidden prior to LCP.
if (entry.startTime < visibilityWatcher.firstHiddenTime) {
// The startTime attribute returns the value of the renderTime if it is
// not 0, and the value of the loadTime otherwise. The activationStart
// reference is used because LCP should be relative to page activation
// rather than navigation start if the page was prerendered. But in cases
// where `activationStart` occurs after the LCP, this time should be
// clamped at 0.
metric.value = Math.max(entry.startTime - getActivationStart(), 0);
metric.entries = [entry];
report();
}
});
};
const po = observe('largest-contentful-paint', handleEntries);
if (po) {
report = bindReporter(
onReport,
metric,
LCPThresholds,
opts!.reportAllChanges,
);
const stopListening = runOnce(() => {
if (!reportedMetricIDs[metric.id]) {
handleEntries(po!.takeRecords() as LCPMetric['entries']);
po!.disconnect();
reportedMetricIDs[metric.id] = true;
report(true);
}
});
// Stop listening after input. Note: while scrolling is an input that
// stops LCP observation, it's unreliable since it can be programmatically
// generated. See: https://github.com/GoogleChrome/web-vitals/issues/75
['keydown', 'click'].forEach((type) => {
// Wrap in a setTimeout so the callback is run in a separate task
// to avoid extending the keyboard/click handler to reduce INP impact
// https://github.com/GoogleChrome/web-vitals/issues/383
addEventListener(type, () => whenIdle(stopListening), {
once: true,
capture: true,
});
});
onHidden(stopListening);
// Only report after a bfcache restore if the `PerformanceObserver`
// successfully registered.
onBFCacheRestore((event) => {
metric = initMetric('LCP');
report = bindReporter(
onReport,
metric,
LCPThresholds,
opts!.reportAllChanges,
);
doubleRAF(() => {
metric.value = performance.now() - event.timeStamp;
reportedMetricIDs[metric.id] = true;
report(true);
});
});
}
});
};
/*
* Copyright 2020 Google LLC
*
* 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
*
* https://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 {bindReporter} from './lib/bindReporter.js';
import {initMetric} from './lib/initMetric.js';
import {onBFCacheRestore} from './lib/bfcache.js';
import {getNavigationEntry} from './lib/getNavigationEntry.js';
import {MetricRatingThresholds, ReportOpts, TTFBMetric} from './types.js';
import {getActivationStart} from './lib/getActivationStart.js';
import {whenActivated} from './lib/whenActivated.js';
/** Thresholds for TTFB. See https://web.dev/articles/ttfb#what_is_a_good_ttfb_score */
export const TTFBThresholds: MetricRatingThresholds = [800, 1800];
/**
* Runs in the next task after the page is done loading and/or prerendering.
* @param callback
*/
const whenReady = (callback: () => void) => {
if (document.prerendering) {
whenActivated(() => whenReady(callback));
} else if (document.readyState !== 'complete') {
addEventListener('load', () => whenReady(callback), true);
} else {
// Queue a task so the callback runs after `loadEventEnd`.
setTimeout(callback, 0);
}
};
/**
* Calculates the [TTFB](https://web.dev/articles/ttfb) value for the
* current page and calls the `callback` function once the page has loaded,
* along with the relevant `navigation` performance entry used to determine the
* value. The reported value is a `DOMHighResTimeStamp`.
*
* Note, this function waits until after the page is loaded to call `callback`
* in order to ensure all properties of the `navigation` entry are populated.
* This is useful if you want to report on other metrics exposed by the
* [Navigation Timing API](https://w3c.github.io/navigation-timing/). For
* example, the TTFB metric starts from the page's [time
* origin](https://www.w3.org/TR/hr-time-2/#sec-time-origin), which means it
* includes time spent on DNS lookup, connection negotiation, network latency,
* and server processing time.
*/
export const onTTFB = (
onReport: (metric: TTFBMetric) => void,
opts?: ReportOpts,
) => {
// Set defaults
opts = opts || {};
let metric = initMetric('TTFB');
let report = bindReporter(
onReport,
metric,
TTFBThresholds,
opts.reportAllChanges,
);
whenReady(() => {
const navigationEntry = getNavigationEntry();
if (navigationEntry) {
// The activationStart reference is used because TTFB should be
// relative to page activation rather than navigation start if the
// page was prerendered. But in cases where `activationStart` occurs
// after the first byte is received, this time should be clamped at 0.
metric.value = Math.max(
navigationEntry.responseStart - getActivationStart(),
0,
);
metric.entries = [navigationEntry];
report(true);
// Only report TTFB after bfcache restores if a `navigation` entry
// was reported for the initial load.
onBFCacheRestore(() => {
metric = initMetric('TTFB', 0);
report = bindReporter(
onReport,
metric,
TTFBThresholds,
opts!.reportAllChanges,
);
report(true);
});
}
});
};
/*
* Copyright 2020 Google LLC
*
* 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
*
* https://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 {onBFCacheRestore} from './lib/bfcache.js';
import {bindReporter} from './lib/bindReporter.js';
import {doubleRAF} from './lib/doubleRAF.js';
import {getActivationStart} from './lib/getActivationStart.js';
import {getVisibilityWatcher} from './lib/getVisibilityWatcher.js';
import {initMetric} from './lib/initMetric.js';
import {observe} from './lib/observe.js';
import {whenActivated} from './lib/whenActivated.js';
import {FCPMetric, MetricRatingThresholds, ReportOpts} from './types.js';
/** Thresholds for FCP. See https://web.dev/articles/fcp#what_is_a_good_fcp_score */
export const FCPThresholds: MetricRatingThresholds = [1800, 3000];
/**
* Calculates the [FCP](https://web.dev/articles/fcp) value for the current page and
* calls the `callback` function once the value is ready, along with the
* relevant `paint` performance entry used to determine the value. The reported
* value is a `DOMHighResTimeStamp`.
*/
export const onFCP = (
onReport: (metric: FCPMetric) => void,
opts?: ReportOpts,
) => {
// Set defaults
opts = opts || {};
whenActivated(() => {
const visibilityWatcher = getVisibilityWatcher();
let metric = initMetric('FCP');
let report: ReturnType;
const handleEntries = (entries: FCPMetric['entries']) => {
entries.forEach((entry) => {
if (entry.name === 'first-contentful-paint') {
po!.disconnect();
// Only report if the page wasn't hidden prior to the first paint.
if (entry.startTime < visibilityWatcher.firstHiddenTime) {
// The activationStart reference is used because FCP should be
// relative to page activation rather than navigation start if the
// page was prerendered. But in cases where `activationStart` occurs
// after the FCP, this time should be clamped at 0.
metric.value = Math.max(entry.startTime - getActivationStart(), 0);
metric.entries.push(entry);
report(true);
}
}
});
};
const po = observe('paint', handleEntries);
if (po) {
report = bindReporter(
onReport,
metric,
FCPThresholds,
opts!.reportAllChanges,
);
// Only report after a bfcache restore if the `PerformanceObserver`
// successfully registered or the `paint` entry exists.
onBFCacheRestore((event) => {
metric = initMetric('FCP');
report = bindReporter(
onReport,
metric,
FCPThresholds,
opts!.reportAllChanges,
);
doubleRAF(() => {
metric.value = performance.now() - event.timeStamp;
report(true);
});
});
}
});
};
function createKrabitzProductTypesSnippet() {
// Define the header for the structured snippet.
// You can choose from: Amenities, Brands, Courses, Degree programs, Destinations,
// Featured hotels, Insurance coverage, Models, Neighborhoods, Service catalog,
// Shows, Styles, Types.
const snippetHeader = 'Types';
// Define the values for the snippet based on the product categories on Krabitz.com.br. [1]
const snippetValues =;
// Create the snippet extension builder.
const snippetBuilder = AdsApp.extensions().newSnippetBuilder();
// Set the header and values for the snippet.
snippetBuilder.withHeader(snippetHeader);
snippetBuilder.withValues(snippetValues);
// Optional: Set the snippet as mobile preferred.
snippetBuilder.withMobilePreferred(false);
// Build the snippet.
const snippetOperation = snippetBuilder.build();
const snippetResult = snippetOperation.getResult();
// Check if the snippet was created successfully.
if (snippetResult) {
const snippet = snippetResult.getExtension();
Logger.log(`Successfully created snippet with ID: ${snippet.getId()}`);
// To apply this snippet at the account level, uncomment the following lines:
const account = AdsApp.currentAccount();
account.addSnippet(snippet);
Logger.log(`Snippet added to account: ${account.getName()}`);
// To apply this snippet at the campaign level, uncomment the following lines and replace with your campaign name:
// const campaignIterator = AdsApp.campaigns().withCondition('campaign.name = "YOUR_CAMPAIGN_NAME_HERE"').get();
// if (campaignIterator.hasNext()) {
// const campaign = campaignIterator.next();
// campaign.addSnippet(snippet);
// Logger.log(`Snippet added to campaign: ${campaign.getName()}`);
// }
// To apply this snippet at the ad group level, uncomment the following lines and replace with your campaign and ad group names:
// const adGroupIterator = AdsApp.adGroups()
// .withCondition('campaign.name = "YOUR_CAMPAIGN_NAME_HERE"')
// .withCondition('ad_group.name = "YOUR_AD_GROUP_NAME_HERE"')
// .get();
// if (adGroupIterator.hasNext()) {
// const adGroup = adGroupIterator.next();
// adGroup.addSnippet(snippet);
// Logger.log(`Snippet added to ad group: ${adGroup.getName()}`);
// }
} else {
Logger.log(`Failed to create snippet. Errors: ${snippetOperation.getErrors()}`);
}
}
function main() {
createKrabitzProductTypesSnippet();
}