} [props.related] - Array of related recirculated content types, can be empty, in which case we request recommendations via the API on client side
* @param {object} [props.metadataVideo] - Optional, CNE video metadata
* @param {bool} [props.metadataVideo.isLive] - Optional, a flag to refer if video is live
* @param {string} [props.metadataVideo.premiereDate] - a date when perticluar video will be available
* @param {number} [props.metadataVideo.videoLength] - length of video
* @param {number} [props.metadataVideo.premiereGap] - difference in days wrt to current date, will be negative for future premiered date
* @param {object} [props.productCarousel] - Optional product carousel
* @param {boolean} [props.article.showHotelRecirc] - Brandspecific props to render hotel recirc unit article pages
* @param {boolean} [props.shouldHideBaseTopPadding] - optional to Hide BasePage top padding
* @param {boolean} [props.shouldHideSeriesNavigation] - Optional prop to prevent series navigation component from rendering - defaults to true
* @param {boolean} [props.shouldHideSeriesRecirc] - Optional prop to prevent series recirc component from rendering - defaults to true
* @param {boolean} [props.shouldInheritDropcapColor] - Optional hack to force drop caps to inherit body color (defaults to false) - tech debt from TNY 2020 endorsement
* @param {boolean} [props.showAgeGate] - Optional prop denoting whether an AgeGate coponent should be inserted
* @param {boolean} [props.showGalleryCartoonPublishedDate] - To show/hide Gallery cartoon published date
* @param {boolean} [props.showFirstRailRecirc] - Optional prop denoting whether the first block in rail should contain recirc
* @param {boolean} [props.showLinkStackInsideContentBody] - Optional, moves the linkStack to show inside the content body container
* @param {boolean} [props.xlargePaddingTop] - Optional prop
* @param {boolean} [props.articleVariationForXlargePaddingTop] - Optional prop
* @param {boolean} [props.hasDynamicNewsletterSignup] - prop denoting that Dynamic Newsletter signup unit will true only for TNY
* @param {boolean} [props.hasDynamicDisclaimer] - Optional to show dynamic disclaimer in TNY
* @param {boolean} [props.shouldPrioritizeSeriesPagination] - Optional to priortize series pagination
* @param {string} [props.responsiveCartoonVariation] - Provides the cartoon variation to select
* @param {boolean} [props.shouldEnableArticleBackground] - to enable article background color feature
* @param {string} [props.pageBackgroundTheme] - Optional prop to get article page background color from copilot
* @param {string} [props.dividerColor] - Optional prop to get divider color for article page from copilot
* @param {boolean} [props.featureFlags.enableActionBar] - Feature flag for enabling ActionBar
* @param {string} [props.actionBarLargeScreenVariation] - Desktop variation for the ActionBar component
* @param {string} [props.actionBarMobileScreenVariation] - Mobile variation for the ActionBar component
* @param {Array} [props.actionBarButtons] - List of buttons/actions for the ActionBar component
* @param {boolean} [props.isActionBarStickyLargeScreen] - To make ActionBar sticky in large screen
* @param {boolean} [props.isMobileDevice] - check is mobile device or not.
* @param {boolean} [props.showContributorImageOnMobile] - Show/Hide contributor image in mobile
* @param {boolean} [props.showIssueDateInArticle] - Show/Hide issue date in Article body
* @param {boolean} [props.hasLinkbannerCrossSlideAnimation] - Enable/Disable cross slide animation for the header
* @param {boolean} [props.showEnhancedTextOverlay] - Enable/Disable enhanced experienced in TextOverlay variations of ContentHeader
* @param {string} [props.article.disclaimerPosition] - position of the disclaimer
* @param {number} [props.minWordCountForMidRecirc] - Min threshold word count for showing mid article recirc unit
* @param {boolean} [props.shouldShowMidArticleRecirc] - To enable mid recirc
* @param {object} [props.midRecircItems] - Mid recirc data
* @param {boolean} [props.shouldHideInlineRecirc] - To hide the inline recirc
*
* @returns {ReactElement}
*/
class ArticlePage extends React.Component {
constructor(props) {
super(props);
this.onHandleScroll = () => {
const scrollTop = window.scrollY;
const contentHeaderPosition = this.pageContentEl.current.offsetTop;
const hideNav = scrollTop <= contentHeaderPosition + 100;
if (hideNav !== this.state.hideNav) {
this.setState({ hideNav });
}
};
this.onResizeHandler = () => {
this.implementActionBarHeight();
};
this.onScrollHandler = () => {
this.implementActionBarHeight();
};
/**
* Dismiss the mobile truncation
*
* @returns {undefined} undefined
*/
this.onTruncationDismiss = () => {
this.setState({
isMobileTruncated: !this.state.isMobileTruncated
});
googleAnalytics.emitUniqueGoogleTrackingEvent(`article-read-more`);
};
this.setCartoonLinkedGalleries = (sliderData, callback) => {
this.setState({ sliderData }, () => {
callback();
});
};
this.implementActionBarHeight = () => {
if (this.props.featureFlags?.enableActionBar &&
this.articleWrapperRef?.current?.offsetHeight !== this.state.articleLength) {
this.state.articleLength = this.articleWrapperRef?.current?.offsetHeight; // eslint-disable-line react/no-direct-mutation-state
}
};
this.fetchNewsletterSubscriptions = async (options) => {
try {
const result = await getNewsletterSubscriptions(options);
if (result.status === 200) {
const newsletterIds = this.props.article.newsletterModules
.filter((newsletterModules) => newsletterModules.priority)
.sort((highPriority, lowPriority) => highPriority.priority - lowPriority.priority)
.map((newsletterList) => newsletterList.newsletterId);
const currentNewsletterId = this.props.article.newsletter.newsletterId;
const newsletterSubscriptionsList = result.newsletterSubscriptions.data
.filter((subscriptionData) => subscriptionData.attributes.status === 'SUBSCRIBED')
.map((newsletterIDs) => newsletterIDs.attributes.newsletterId);
if (newsletterSubscriptionsList.length) {
const newsletterUnsubscriptionsList = newsletterIds.filter((filteredList) => !newsletterSubscriptionsList.includes(filteredList));
const renderNewsletter = this.props.article.newsletterModules.find((newsletters) => newsletters.newsletterId === newsletterUnsubscriptionsList[0]);
if (newsletterSubscriptionsList.includes(currentNewsletterId)) {
this.setState({
newsletterData: {
...this.state.newsletterData,
...renderNewsletter
}
});
}
}
}
}
catch (error) {
console.log(error);
}
return {};
};
this.state = {
articleLength: 0,
hideNav: this.props.article.headerProps.hasContentHeaderLogo,
isMobileTruncated: false,
newsletterData: this.props.article.newsletter,
sliderData: {}
};
this.pageContentEl = React.createRef();
this.articleWrapperRef = React.createRef();
const Chunked = props.hasLightbox
? withLightbox({
Component: ChunkedArticleContent,
slides: props.article.lightboxImages,
hasSlideShow: props.hasSlideShow,
slideShowVariation: props.slideShowVariation
})
: ChunkedArticleContent;
// Define a single component instead of creating a new one on every
// render operation. This avoids issues with interactive overrides,
// like the crosswords, which are prone to loading errors if rendered
// multiple times
this.TruncatedChunkedArticleContent = withArticleTruncation(Chunked, 'body');
}
componentDidMount() {
if (this.props.user.isAuthenticated &&
this.props.hasDynamicNewsletterSignup &&
this.props.article.newsletterModules?.length) {
const newsletterIds = this.props.article.newsletterModules.map((newsletterList) => newsletterList.newsletterId);
const newsletterIdsString = newsletterIds.toString();
const options = {
amgUUID: this.props.user.amguuid,
newsletterIds: newsletterIdsString,
userPlatformProxy: this.props.userPlatform.userPlatformProxy,
provider: 'sailthru',
xClientID: this.props.userPlatform.xClientID
};
this.fetchNewsletterSubscriptions(options);
}
const { hasTruncationOnMobile } = this.props.article;
hasTruncationOnMobile
? this.setState({ isMobileTruncated: true })
: this.setState({ isMobileTruncated: false });
if (this.props.article.headerProps.hasContentHeaderLogo) {
this.setState({ hideNav: true });
this.scrollSubscription = window.Kendra.WINDOW_EVENT.on(WindowEventChannel.SCROLL_DEBOUNCE, this.onHandleScroll);
}
const isHeaderOverride = get(this.props.article.interactiveOverride, 'behavior') === 'header';
const showNavWithHeaderOverride = get(this.props.componentConfig, 'BasePage.settings.showNavWithHeaderOverride');
const isNavInvisible = isHeaderOverride && !showNavWithHeaderOverride;
localStore.setItem('nav_invisible', isNavInvisible);
if (window.cns) {
window.cns.pageContext.content.pageStructure =
this.props.article.pageStructure;
}
this.scrollHandlerSubscription = window.Kendra.WINDOW_EVENT.on(WindowEventChannel.SCROLL_DEBOUNCE, this.onScrollHandler);
this.scrollHandlerSubscription = window.Kendra.WINDOW_EVENT.on(WindowEventChannel.RESIZE_DEBOUNCE, this.onResizeHandler);
window.Kendra.TRACK_COMPONENT.broadcast(TrackComponentChannel.RENDER, {
name: 'ArticlePage'
});
}
componentWillUnmount() {
if (this.scrollSubscription) {
this.scrollSubscription.off();
}
if (this.scrollHandlerSubscription) {
this.scrollHandlerSubscription.off();
}
if (this.resizeHandlerSubscription) {
this.resizeHandlerSubscription.off();
}
localStore.removeItem('nav_invisible');
}
/* eslint-disable-next-line complexity */
render() {
const { article: { id, body, channelCloudData, contentWarnings, contributorSpotLightProps, hasAffiliateLinks, hasEventBannerHidden, hasInvertedHeadertheme, channelMap, hasNewsletterInBody, hasTruncationOnMobile, headerProps, hideContributorBio, hideRecircList, hideRecircMostPopular, hierarchy, shouldEnableVMG, interactiveOverride, isAffiliateLinksDisabled, lang, langTranslations, customHeading = {}, recircs = [], recircMostPopular, recircRelated, midRecircItems, relatedVideo, interlude, isHeroAdVisible, isLicensedPartner, licensedPartnerLink, magazineDisclaimer, paddingTop, tagCloud, newsletter, shouldUsePersistentAd, showAgeGate, showBookmark, showBreadcrumbTrail, showHotelRecirc, signageConfig, isUpcEnabled, isLinkStackEnabled, shouldShowFooterNewsletter, shouldPrioritizeSeriesPagination, cnCoupons = [], hasAffiliateLinkDisabled, showDisclaimer, disclaimerText, disclaimerPosition }, communityExperience, showWriterBio = false, showFirstRailRecirc, attributes, className, componentConfig, config, shouldHideBaseTopPadding, shouldHideSeriesNavigation = true, shouldHideSeriesRecirc = true, shouldShowSeriesNavigationInFooter, pageBackgroundTheme, dividerColor, shouldEnableArticleBackground = false, shouldEnableFullArticleInverted = false, shouldInheritDropcapColor = false, showLinkStackInsideContentBody = false, featureFlags, hasLightbox = false, hasChannelNavigation, hideSideBySideViewOnMobile, showContributor, showContributorSpotlight, cartoonVariation = 'default', hasRecircDriver, recircMostPopularVariationOnMobile, linkList, related = [], metadataVideo = {}, productCarousel = {}, user, hasNewsletterForABTest, intl, xlargePaddingTop, articleVariationForXlargePaddingTop, beOp, hasDynamicDisclaimer, responsiveCartoonVariation, showGalleryCartoonPublishedDate = true, actionBarLargeScreenVariation, actionBarMobileScreenVariation, isActionBarStickyLargeScreen, actionBarButtons, showContributorImageOnMobile = true, showIssueDateInArticle = false, hasLinkbannerCrossSlideAnimation, showEnhancedTextOverlay, minWordCountForMidRecirc, shouldShowMidArticleRecirc, shouldHideInlineRecirc, isMobileDevice, showExperimentPlaceholder = false, headerComponent = 'ContentHeader', oneCover, visualStoryBanner } = this.props;
const { enableCommunityExperience } = communityExperience;
const bookmark = config?.account?.bookmark;
const enableBookmarkDrawers = bookmark?.enableBookmarkDrawers || false;
const disclaimer = disclaimerText || intl.formatMessage(translations.defaultDisclaimer);
const pageBackgroundColor = shouldEnableArticleBackground
? pageBackgroundTheme
: undefined;
const borderColorTheme = shouldEnableArticleBackground
? dividerColor
: undefined;
const { hideNav, articleLength } = this.state;
// if the newsletter for the A/B test is present, hide the newsletter in the footer
// if not, go off of hasNewsletterInBody
const shouldHideNewsletter = hasNewsletterForABTest
? true
: hasNewsletterInBody;
// function to change content header variation to `TextOverlayWithLogo`
const setContentHeaderLogoVariationWithLogo = () => {
const contentHeaderSettings = get(componentConfig, 'ContentHeader.settings');
const additionalContentHeaderSettings = {
showLogo: true,
hideContributors: false,
hidePublishDate: true,
hideRubric: false,
hideShareButtons: true
};
set(componentConfig, 'ContentHeader.variation', 'TextOverlayWithLogo');
set(componentConfig, 'ContentHeader.settings', {
...contentHeaderSettings,
...additionalContentHeaderSettings
});
};
const { hasContentHeaderLogo, isFullBleedVideo } = headerProps;
// call `setContentHeaderLogoVariationWithLogo`, if `headerProps.hasContentHeaderLogo` is true
hasContentHeaderLogo && setContentHeaderLogoVariationWithLogo();
const contributors = hideContributorBio
? undefined
: headerProps.contributors;
const { hasNativeShareButton, hasNewsletterOnPageTop, hasNewsletterWithoutWrapper, shouldEnableNativeShareOnDesktop, shouldRemoveBackgroundColor, enableEnhancedCartoonExperience, enableEnhancedArticleHeader, enableActionBar, variations, showFullBleedBelow, cneVideoEmbedProps, enableBookmarking } = featureFlags;
const articlePageContentBackGroundProps = {};
let paywallCollaboratorProps = {};
const contentHeaderVariation = get(componentConfig, 'ContentHeader.variation');
const ArticleIssueDate = () => {
if (headerProps.issueDate) {
return (React.createElement(ArticlePageIssueDate, null,
intl.formatMessage(translations.publishedInThe),
` ${headerProps.issueDate} `));
}
return null;
};
const additionalHeaderProps = generateHeaderProps(enableEnhancedArticleHeader, enableActionBar, contentHeaderVariation, {
showContributorImageOnMobile,
showEnhancedTextOverlay
});
if (isContentHeaderVariationInACDC(contentHeaderVariation) ||
isContentHeaderTextOverlay(contentHeaderVariation, showEnhancedTextOverlay)) {
articlePageContentBackGroundProps.hasReducedBackgroundSpacing =
showIssueDateInArticle;
paywallCollaboratorProps = {
...(showIssueDateInArticle && {
articleIssueDateComponent: React.createElement(ArticleIssueDate, null)
})
};
}
const embedResponsiveCartoonVariation = enableEnhancedCartoonExperience
? 'InlineCartoon'
: responsiveCartoonVariation;
const GeneralContentWrapper = getContentFooterWrapper(componentConfig, {
type: 'article'
});
const prefetchAccountSignInPage = () => !user.isAuthenticated && user.hasUserAuthCheck;
const isNarrow = get(componentConfig, 'ChunkedArticleContent.variation') ===
'OneColumnNarrow';
const DisclaimerWrapper = Grid.DynamicGrid({
startColumn: 2,
endColumn: 12
});
const showChannelCloud = () => get(componentConfig, 'ChannelCloud.settings.shouldShowChannelCloud', false) && channelCloudData?.channels?.length > 0;
const recircListElements = recircs.map((list, i) => {
const ConfiguredRecircList = asConfiguredComponent(RecircList, list.displayName);
const heading = list.heading || '';
const { results, destinationPage, location, recommendedClickout, recommendedType } = list;
return hasRecircDriver && destinationPage ? (React.createElement(SummaryCollectionSplitColumns, { key: `SummaryCollectionSplitColumns${i}`, recommendedItems: {
items: results,
recommendedType,
recommendedClickout
}, guideItem: [destinationPage], location: location, shouldAppendReadMoreLinkForDek: true })) : (React.createElement(ConnectedErrorBoundary, { key: `ConnectedErrorBoundary${i}` },
React.createElement(ConfiguredRecircList, { related: list.related, heading: heading, dividerColor: borderColorTheme, shouldEnableFullArticleInverted: shouldEnableFullArticleInverted })));
});
const togglePaddingTop = contentHeaderVariation === articleVariationForXlargePaddingTop &&
xlargePaddingTop
? xlargePaddingTop
: paddingTop;
const beOpAccountID = beOp?.accountID || '';
const isBeOpEnable = beOp?.isEnabled || false;
const additionalNavigation = hasChannelNavigation && React.createElement(ChannelNavigation, null);
const isadRail = get(componentConfig, 'ChunkedArticleContent.variation') === 'WithAdRail';
/* eslint-disable-next-line react/prop-types */
const ContentWrapper = ({ children }) => (React.createElement(ContentWrapperGrid, { isadRail: isadRail, as: GeneralContentWrapper },
React.createElement("div", { className: "body body__container" },
React.createElement("div", { className: "container container--body" },
React.createElement("div", { className: "container--body-inner" }, children)))));
const shouldOverrideContentHeader = getOverrideBehaviour(interactiveOverride) === 'articleheader';
const sponsoredProps = get(headerProps, 'sponsoredContentHeaderProps');
return (React.createElement(ArticlePageBase, { additionalNavigation: additionalNavigation, attributes: attributes, shouldEnableFullArticleInverted: shouldEnableFullArticleInverted, channelMap: channelMap, className: classnames('page--article', className), config: config, featureFlags: featureFlags, hideNav: hideNav, hasContentHeaderLogo: hasContentHeaderLogo, hasEventBannerHidden: hasEventBannerHidden, hasInvertedHeadertheme: hasInvertedHeadertheme, hasFooterAdsMargins: true, interactiveOverride: interactiveOverride, isHeroAdVisible: isHeroAdVisible, hasBaseAds: true, user: user, lang: lang, customHeading: customHeading, shouldHideBaseTopPadding: shouldHideBaseTopPadding, shouldPrioritizeSeriesPagination: shouldPrioritizeSeriesPagination, pageBackgroundTheme: pageBackgroundColor, hasLinkbannerCrossSlideAnimation: (isContentHeaderTextOverlay(contentHeaderVariation, showEnhancedTextOverlay) ||
isContentHeaderVariationInACDC(contentHeaderVariation)) &&
hasLinkbannerCrossSlideAnimation },
isBeOpEnable && React.createElement(BeopScript, { accountId: beOpAccountID }),
React.createElement(I18nProvider, { locale: lang, translations: langTranslations },
showBookmark && React.createElement(SignInModal, null),
disclaimerPosition === 'top' && showDisclaimer && (React.createElement(Row, null,
React.createElement(DisclaimerWrapper, null,
React.createElement(Disclaimer, { disclaimerHtml: disclaimer, hasTopRule: false, contentAlign: "center" })))),
showBreadcrumbTrail && (React.createElement(BreadcrumbTrail, { hierarchy: hierarchy, shouldRemoveBackgroundColor: shouldRemoveBackgroundColor })),
React.createElement("article", { className: classnames('article main-content', {
'article--inherited-dropcaps': shouldInheritDropcapColor
}), lang: lang },
hasNewsletterOnPageTop && newsletter && (React.createElement(ConnectedNewsletterSubscribeForm, { ...newsletter, position: "article-page-top" })),
headerProps.sponsoredContentHeaderProps && (React.createElement(SponsoredContentHeader, { ...headerProps.sponsoredContentHeaderProps, className: "light-theme" })),
shouldOverrideContentHeader ? (React.createElement("div", { className: "interactive-override-container interactive-override-container--content_header", dangerouslySetInnerHTML: {
__html: interactiveOverride.markup
} })) : (React.createElement(ArticlePageLedeBackground, { ref: this.pageContentEl },
React.createElement(GrowthBookReadyContext, null, ({ isGBInitialized }) => {
const finalHeader = () => renderHeader(headerComponent, {
oneCover,
headerProps,
type: 'article',
additionalHeaderProps,
enableEnhancedArticleHeader,
hasNativeShareButton,
shouldEnableNativeShareOnDesktop,
isFullBleedVideo,
hasLightbox,
interactiveOverride,
metadataVideo,
showBreadCrumb: showBreadcrumbTrail
});
return renderHeaderOrPlaceholder({
showExperimentPlaceholder,
renderHeader: finalHeader,
isGBInitialized
});
}))),
!shouldHideSeriesNavigation &&
!shouldShowSeriesNavigationInFooter && (React.createElement(SeriesNavigation, { className: "article__series-navigation", pageBackgroundTheme: pageBackgroundColor, dividerColor: borderColorTheme })),
React.createElement(ArticlePageContentBackGround, { togglePaddingTop: togglePaddingTop, isMobileTruncated: this.state.isMobileTruncated, cartoonVariation: cartoonVariation, "data-attribute-verso-pattern": "article-body", enableActionBar: enableActionBar, className: "article-body__content", ...articlePageContentBackGroundProps, ref: this.articleWrapperRef },
enableActionBar && actionBarButtons && (React.createElement(PhotoBookmarkingProvider, { isPhotoBookmarkingEnabled: enableBookmarking, theme: "standard" },
React.createElement(ActionBarWrapper, { actionBarLargeScreenVariation: actionBarLargeScreenVariation, actionBarMobileScreenVariation: actionBarMobileScreenVariation, isActionBarStickyLargeScreen: isActionBarStickyLargeScreen, actionBarButtons: actionBarButtons, articleLength: articleLength, showActionBar: enableActionBar, shouldEnableBookmarkDrawers: enableBookmarkDrawers, image: headerProps.lede }))),
hasTruncationOnMobile && this.state.isMobileTruncated && (React.createElement(ArticlePageBodyMobileTruncatedBtn, { inputKind: "button", label: intl.formatMessage(translations.truncatedButtonLabel), onClickHandler: this.onTruncationDismiss })),
showChannelCloud() && (React.createElement(Grid.ContentWithAdRailNarrow, null,
React.createElement("div", null,
React.createElement(ChannelCloud, { ...channelCloudData, ...get(componentConfig, 'ChannelCloud.settings') })))),
body && (React.createElement(PaywallCollaborator, { body: body, linkList: linkList, isLinkStackEnabled: isLinkStackEnabled && showLinkStackInsideContentBody, isMobileDevice: isMobileDevice, component: this.TruncatedChunkedArticleContent, contributors: contributors, hasTopSpacing: !!headerProps.lede, interlude: interlude, isAffiliateLinksDisabled: isAffiliateLinksDisabled, name: "chunked-article-content", shouldUsePersistentAd: shouldUsePersistentAd, recircMostPopularVariationOnMobile: recircMostPopularVariationOnMobile, hideRecircMostPopular: hideRecircMostPopular, shouldEnableArticleBackground: shouldEnableArticleBackground, shouldEnableFullArticleInverted: shouldEnableFullArticleInverted, pageBackgroundTheme: pageBackgroundColor, dividerColor: borderColorTheme, recircMostPopular: recircMostPopular, showFirstRailRecirc: showFirstRailRecirc, tagCloud: tagCloud, responsiveCartoonVariation: embedResponsiveCartoonVariation, hasCartoonFullWidth: enableEnhancedCartoonExperience, setCartoonLinkedGalleries: this.setCartoonLinkedGalleries, hasAffiliateLinks: hasAffiliateLinks, ...paywallCollaboratorProps, showDisclaimer: showDisclaimer, disclaimer: disclaimer, disclaimerPosition: disclaimerPosition, shouldHideInlineRecirc: shouldHideInlineRecirc, visualStoryBanner: visualStoryBanner, ...(shouldShowMidArticleRecirc && {
midRecircItems,
minWordCountForMidRecirc
}) })),
body && (React.createElement(ContentWrapper, null,
React.createElement(InlineBarrier, null)))),
!shouldHideSeriesRecirc && (React.createElement(SeriesRecirc, { ContentWrapper: ContentWrapper }))),
isBeOpEnable && (React.createElement(ContentWrapper, null,
React.createElement("div", { className: "BeOpWidget" }))),
showFullBleedBelow && (React.createElement(React.Fragment, null,
React.createElement(ContentHeader
// eslint-disable-next-line react/forbid-component-props
, {
// eslint-disable-next-line react/forbid-component-props
variations: variations, isFullBleedVideo: showFullBleedBelow, cneVideoEmbedProps: cneVideoEmbedProps }))),
disclaimerPosition === 'bottom' && showDisclaimer && (React.createElement(ArticlePageDisclaimerGrid, { as: GeneralContentWrapper },
React.createElement(ArticlePageDisclaimer, { disclaimerHtml: disclaimer, hasTopRule: false }))),
Object.keys(productCarousel).length > 0 && (React.createElement(MultiPackageRow, { key: "articleCarouselProduct", dataJourneyHook: "row-content" },
React.createElement(VersoFilterableSummaryList, { isUpcEnabled: isUpcEnabled, ...productCarousel, hasAffiliateLinkDisabled: hasAffiliateLinkDisabled, copilotId: id }))),
enableCommunityExperience && (React.createElement(Commenting, { hed: headerProps.dangerousHed, id: id })),
React.createElement(ArticlePageContentFooterGrid, { as: ContentFooter, className: classnames('article-body__footer', {
'content-footer--mobile-truncated': this.state.isMobileTruncated
}), channelMap: channelMap, consumerMarketing: { position: 'article-footer' }, shouldEnableFullArticleInverted: shouldEnableFullArticleInverted, ContentWrapper: GeneralContentWrapper, contributors: contributors, contributorSpotlight: contributorSpotLightProps, showWriterBio: showWriterBio, hideContributorBio: hideContributorBio, showContributorSpotlight: showContributorSpotlight, showContributor: showContributor, hideRecircList: hideRecircList, hasNewsletterWithoutWrapper: hasNewsletterWithoutWrapper, isLicensedPartner: isLicensedPartner, isLinkStackEnabled: isLinkStackEnabled && !showLinkStackInsideContentBody, isNarrow: isNarrow, isAdRail: isadRail, licensedPartnerLink: licensedPartnerLink, linkList: linkList, magazineDisclaimer: magazineDisclaimer, newsletter: user.isAuthenticated ? this.state.newsletterData : newsletter, recircs: recircs, recircListElements: recircListElements, dividerColor: borderColorTheme, related: related, recircRelated: recircRelated, relatedVideo: relatedVideo, showNewsletter: shouldShowFooterNewsletter || !shouldHideNewsletter, showHotelRecirc: showHotelRecirc, signageConfig: signageConfig, tagCloud: tagCloud, shouldEnableVMG: shouldEnableVMG, cnCoupons: cnCoupons, sponsoredProps: sponsoredProps, hasDynamicDisclaimer: hasDynamicDisclaimer, shouldShowSeriesNavigationInFooter: !shouldHideSeriesNavigation && shouldShowSeriesNavigationInFooter, pageBackgroundTheme: pageBackgroundColor, currentPage: "article" }),
showAgeGate && React.createElement(AgeGate, { content: { contentWarnings } }),
prefetchAccountSignInPage() && (React.createElement(ResourceHint, { as: "document", hint: "prefetch", href: "/account/sign-in" }))),
enableEnhancedCartoonExperience && (React.createElement(GenericModal, { closeModalText: intl.formatMessage(translations.backToArticle) },
React.createElement(GalleryCarousel, { id: this.state.sliderData.id, items: this.state.sliderData.items, showPublishedDate: showGalleryCartoonPublishedDate, responsiveCartoonVariation: "SliderCartoon", title: this.state.sliderData?.source?.hed, titleLinkURL: this.state.sliderData?.url, shouldUseModalStyle: true, carouselPlacedIn: "modal", showHeadRecirc: true, showNewsletter: true }))),
React.createElement(ArticlePageGlobalStyle, { pageBackgroundTheme: pageBackgroundColor, dividerColor: borderColorTheme, shouldEnableFullArticleInverted: shouldEnableFullArticleInverted, hideSideBySideViewOnMobile: hideSideBySideViewOnMobile, isUpcEnabled: isUpcEnabled })));
}
}
ArticlePage.propTypes = {
actionBarButtons: PropTypes.arrayOf(PropTypes.oneOf(['audio', 'bookmark', 'comments'])),
actionBarLargeScreenVariation: PropTypes.oneOf(getVariationNames(ActionBar)),
actionBarMobileScreenVariation: PropTypes.oneOf(getVariationNames(ActionBar)),
article: PropTypes.shape({
body: PropTypes.array,
channelCloudData: PropTypes.object,
cnCoupons: PropTypes.array,
contributorSpotLightProps: PropTypes.object,
hasAffiliateLinks: PropTypes.boolean,
channelMap: PropTypes.object,
contentWarnings: PropTypes.array,
customHeading: PropTypes.object,
disclaimerText: PropTypes.string,
disclaimerPosition: PropTypes.string,
hasAffiliateLinkDisabled: PropTypes.bool,
hasEventBannerHidden: PropTypes.bool,
hasInvertedHeadertheme: PropTypes.bool,
hasNewsletterInBody: PropTypes.bool,
hasTruncationOnMobile: PropTypes.bool,
headerProps: PropTypes.object.isRequired,
hideContributorBio: PropTypes.bool,
hideRecircList: PropTypes.bool,
hideRecircMostPopular: PropTypes.bool,
hierarchy: PropTypes.array,
id: PropTypes.string,
interactiveOverride: PropTypes.shape({
markup: PropTypes.string,
behavior: PropTypes.string
}),
interlude: PropTypes.shape({
...CNEInterludeEmbed.propTypes,
isRailEligible: PropTypes.boolean
}),
isAffiliateLinksDisabled: PropTypes.bool,
isHeroAdVisible: PropTypes.bool.isRequired,
isLicensedPartner: PropTypes.bool,
isLinkStackEnabled: PropTypes.bool,
isUpcEnabled: PropTypes.bool,
lang: PropTypes.string,
langTranslations: PropTypes.object,
licensedPartnerLink: PropTypes.string,
lightboxImages: PropTypes.array.isRequired,
magazineDisclaimer: PropTypes.shape({
issueDate: PropTypes.string.isRequired,
issueLink: PropTypes.string.isRequired,
originalHed: PropTypes.string
}),
midRecircItems: PropTypes.array,
newsletter: PropTypes.object,
newsletterModules: PropTypes.array,
paddingTop: PropTypes.oneOf(['large']),
pageStructure: PropTypes.array,
recircs: PropTypes.array,
recircMostPopular: PropTypes.array,
recircRelated: PropTypes.array,
relatedVideo: PropTypes.shape({
brand: PropTypes.string,
related: PropTypes.any,
useRelatedVideo: PropTypes.bool
}),
shouldPrioritizeSeriesPagination: PropTypes.bool,
shouldShowFooterNewsletter: PropTypes.bool,
shouldUsePersistentAd: PropTypes.bool,
shouldEnableVMG: PropTypes.bool,
showAgeGate: PropTypes.bool,
showBookmark: PropTypes.bool,
showBreadcrumbTrail: PropTypes.bool,
showDisclaimer: PropTypes.bool,
showHotelRecirc: PropTypes.bool,
signageConfig: PropTypes.object,
tagCloud: PropTypes.shape({
tags: PropTypes.arrayOf(PropTypes.shape({
tag: PropTypes.string.isRequired,
url: PropTypes.string
}))
})
}).isRequired,
articleVariationForXlargePaddingTop: PropTypes.string,
attributes: PropTypes.object,
beOp: PropTypes.shape({
accountID: PropTypes.string,
isEnabled: PropTypes.boolean
}),
cartoonVariation: PropTypes.oneOf(['default', 'card']),
className: PropTypes.string,
communityExperience: PropTypes.shape({
enableCommunityExperience: PropTypes.bool
}),
componentConfig: PropTypes.object,
config: PropTypes.object,
dividerColor: PropTypes.string,
featureFlags: PropTypes.object,
hasChannelNavigation: PropTypes.bool,
hasDynamicDisclaimer: PropTypes.bool,
hasDynamicNewsletterSignup: PropTypes.bool,
hasLightbox: PropTypes.bool,
hasLinkbannerCrossSlideAnimation: PropTypes.bool,
hasNewsletterForABTest: PropTypes.bool,
hasRecircDriver: PropTypes.bool,
hasSlideShow: PropTypes.bool,
headerComponent: PropTypes.string,
hideNav: PropTypes.bool,
hideSideBySideViewOnMobile: PropTypes.bool,
intl: PropTypes.object,
isActionBarStickyLargeScreen: PropTypes.bool,
isMobileDevice: PropTypes.bool,
linkList: PropTypes.object,
metadataVideo: PropTypes.shape({
isLive: PropTypes.bool,
premiereDate: PropTypes.string,
series: PropTypes.string,
videoLength: PropTypes.number,
premiereGap: PropTypes.number
}),
minWordCountForMidRecirc: PropTypes.number,
oneCover: OneCoverConfigPropTypes,
pageBackgroundTheme: PropTypes.string,
productCarousel: PropTypes.object,
recircMostPopularVariationOnMobile: PropTypes.oneOf(getVariationNames(RecircMostPopular)),
related: PropTypes.array,
responsiveCartoonVariation: PropTypes.oneOf(getVariationNames(ResponsiveCartoon)),
reviewerInfoText: PropTypes.string,
shouldEnableArticleBackground: PropTypes.bool,
shouldEnableFullArticleInverted: PropTypes.bool,
shouldHideBaseTopPadding: PropTypes.bool,
shouldHideInlineRecirc: PropTypes.bool,
shouldHideSeriesNavigation: PropTypes.bool,
shouldHideSeriesRecirc: PropTypes.bool,
shouldInheritDropcapColor: PropTypes.bool,
shouldShowMidArticleRecirc: PropTypes.bool,
shouldShowSeriesNavigationInFooter: PropTypes.bool,
showContributor: PropTypes.bool,
showContributorImageOnMobile: PropTypes.bool,
showContributorSpotlight: PropTypes.bool,
showEnhancedTextOverlay: PropTypes.bool,
showExperimentPlaceholder: PropTypes.string,
showFirstRailRecirc: PropTypes.bool,
showGalleryCartoonPublishedDate: PropTypes.bool,
showIssueDateInArticle: PropTypes.bool,
showLinkStackInsideContentBody: PropTypes.bool,
showWriterBio: PropTypes.bool,
signInHed: PropTypes.string,
signInHedSpanTag: PropTypes.string,
signInMessage: PropTypes.string,
slideShowVariation: PropTypes.string,
user: PropTypes.object,
userPlatform: PropTypes.object,
visualStoryBanner: PropTypes.object,
xlargePaddingTop: PropTypes.string
};
ArticlePage.displayName = 'ArticlePage';
module.exports = connector(injectIntl(ArticlePage), {
keysToPluck: [
'article',
'beOp',
'componentConfig',
'config',
'featureFlags',
'linkList',
'metadataVideo',
'productCarousel',
'related',
'showFirstRailRecirc',
'user',
'userPlatform',
'communityExperience',
'visualStoryBanner'
]
});
//# sourceMappingURL=ArticlePage.js.map
/***/ }),
/***/ 19266:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const ArticlePage = __webpack_require__(70935);
const { asConfiguredComponent } = __webpack_require__(12892);
module.exports = asConfiguredComponent(ArticlePage, 'ArticlePage');
//# sourceMappingURL=index.js.map
/***/ }),
/***/ 17848:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
var __webpack_unused_export__;
__webpack_unused_export__ = ({ value: true });
const react_intl_1 = __webpack_require__(46984);
exports.A = (0, react_intl_1.defineMessages)({
truncatedButtonLabel: {
id: 'ArticlePage.TruncatedButtonLabel',
defaultMessage: 'Read Full Story',
description: 'ArticlePage component truncated button label'
},
backToArticle: {
id: 'ArticlePage.Back to article',
defaultMessage: 'Back to article',
description: 'Gallery slider back button text'
},
publishedInThe: {
id: 'ArticlePage.From the issue of',
defaultMessage: 'From the issue of',
description: 'Article page date text'
},
defaultDisclaimer: {
id: 'ArticlePage.DefaultDisclaimer',
defaultMessage: 'All products are independently selected by our editors. If you buy something, we may earn an affiliate commission.',
description: 'Default disclaimer for Article page'
}
});
//# sourceMappingURL=translations.js.map
/***/ }),
/***/ 63155:
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
const article_page_1 = __importDefault(__webpack_require__(19266));
const bootstrap_client_app_entry_1 = __importDefault(__webpack_require__(41782));
(0, bootstrap_client_app_entry_1.default)(article_page_1.default);
//# sourceMappingURL=client.entry.js.map
/***/ })
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ id: moduleId,
/******/ loaded: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = __webpack_modules__;
/******/
/************************************************************************/
/******/ /* webpack/runtime/chunk loaded */
/******/ (() => {
/******/ var deferred = [];
/******/ __webpack_require__.O = (result, chunkIds, fn, priority) => {
/******/ if(chunkIds) {
/******/ priority = priority || 0;
/******/ for(var i = deferred.length; i > 0 && deferred[i - 1][2] > priority; i--) deferred[i] = deferred[i - 1];
/******/ deferred[i] = [chunkIds, fn, priority];
/******/ return;
/******/ }
/******/ var notFulfilled = Infinity;
/******/ for (var i = 0; i < deferred.length; i++) {
/******/ var [chunkIds, fn, priority] = deferred[i];
/******/ var fulfilled = true;
/******/ for (var j = 0; j < chunkIds.length; j++) {
/******/ if ((priority & 1 === 0 || notFulfilled >= priority) && Object.keys(__webpack_require__.O).every((key) => (__webpack_require__.O[key](chunkIds[j])))) {
/******/ chunkIds.splice(j--, 1);
/******/ } else {
/******/ fulfilled = false;
/******/ if(priority < notFulfilled) notFulfilled = priority;
/******/ }
/******/ }
/******/ if(fulfilled) {
/******/ deferred.splice(i--, 1)
/******/ var r = fn();
/******/ if (r !== undefined) result = r;
/******/ }
/******/ }
/******/ return result;
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/compat get default export */
/******/ (() => {
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = (module) => {
/******/ var getter = module && module.__esModule ?
/******/ () => (module['default']) :
/******/ () => (module);
/******/ __webpack_require__.d(getter, { a: getter });
/******/ return getter;
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/create fake namespace object */
/******/ (() => {
/******/ var getProto = Object.getPrototypeOf ? (obj) => (Object.getPrototypeOf(obj)) : (obj) => (obj.__proto__);
/******/ var leafPrototypes;
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 16: return value when it's Promise-like
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = this(value);
/******/ if(mode & 8) return value;
/******/ if(typeof value === 'object' && value) {
/******/ if((mode & 4) && value.__esModule) return value;
/******/ if((mode & 16) && typeof value.then === 'function') return value;
/******/ }
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ var def = {};
/******/ leafPrototypes = leafPrototypes || [null, getProto({}), getProto([]), getProto(getProto)];
/******/ for(var current = mode & 2 && value; typeof current == 'object' && !~leafPrototypes.indexOf(current); current = getProto(current)) {
/******/ Object.getOwnPropertyNames(current).forEach((key) => (def[key] = () => (value[key])));
/******/ }
/******/ def['default'] = () => (value);
/******/ __webpack_require__.d(ns, def);
/******/ return ns;
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/define property getters */
/******/ (() => {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = (exports, definition) => {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/global */
/******/ (() => {
/******/ __webpack_require__.g = (function() {
/******/ if (typeof globalThis === 'object') return globalThis;
/******/ try {
/******/ return this || new Function('return this')();
/******/ } catch (e) {
/******/ if (typeof window === 'object') return window;
/******/ }
/******/ })();
/******/ })();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ (() => {
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ })();
/******/
/******/ /* webpack/runtime/make namespace object */
/******/ (() => {
/******/ // define __esModule on exports
/******/ __webpack_require__.r = (exports) => {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/node module decorator */
/******/ (() => {
/******/ __webpack_require__.nmd = (module) => {
/******/ module.paths = [];
/******/ if (!module.children) module.children = [];
/******/ return module;
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/jsonp chunk loading */
/******/ (() => {
/******/ // no baseURI
/******/
/******/ // object to store loaded and loading chunks
/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched
/******/ // [resolve, reject, Promise] = chunk loading, 0 = chunk loaded
/******/ var installedChunks = {
/******/ 8659: 0
/******/ };
/******/
/******/ // no chunk on demand loading
/******/
/******/ // no prefetching
/******/
/******/ // no preloaded
/******/
/******/ // no HMR
/******/
/******/ // no HMR manifest
/******/
/******/ __webpack_require__.O.j = (chunkId) => (installedChunks[chunkId] === 0);
/******/
/******/ // install a JSONP callback for chunk loading
/******/ var webpackJsonpCallback = (parentChunkLoadingFunction, data) => {
/******/ var [chunkIds, moreModules, runtime] = data;
/******/ // add "moreModules" to the modules object,
/******/ // then flag all "chunkIds" as loaded and fire callback
/******/ var moduleId, chunkId, i = 0;
/******/ if(chunkIds.some((id) => (installedChunks[id] !== 0))) {
/******/ for(moduleId in moreModules) {
/******/ if(__webpack_require__.o(moreModules, moduleId)) {
/******/ __webpack_require__.m[moduleId] = moreModules[moduleId];
/******/ }
/******/ }
/******/ if(runtime) var result = runtime(__webpack_require__);
/******/ }
/******/ if(parentChunkLoadingFunction) parentChunkLoadingFunction(data);
/******/ for(;i < chunkIds.length; i++) {
/******/ chunkId = chunkIds[i];
/******/ if(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) {
/******/ installedChunks[chunkId][0]();
/******/ }
/******/ installedChunks[chunkId] = 0;
/******/ }
/******/ return __webpack_require__.O(result);
/******/ }
/******/
/******/ var chunkLoadingGlobal = globalThis["webpackChunkverso"] = globalThis["webpackChunkverso"] || [];
/******/ chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));
/******/ chunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));
/******/ })();
/******/
/******/ /* webpack/runtime/nonce */
/******/ (() => {
/******/ __webpack_require__.nc = undefined;
/******/ })();
/******/
/************************************************************************/
/******/
/******/ // startup
/******/ // Load entry module and return exports
/******/ // This entry module depends on other loaded chunks and execution need to be delayed
/******/ var __webpack_exports__ = __webpack_require__.O(undefined, [3796,2181,2641,2439,531,1782,1063,4721,6667], () => (__webpack_require__(63155)))
/******/ __webpack_exports__ = __webpack_require__.O(__webpack_exports__);
/******/
/******/ })()
;