import { isNothing, isSome } from "@jet/environment/types/optional"; import * as models from "../../api/models"; import * as serverData from "../../foundation/json-parsing/server-data"; import * as mediaAttributes from "../../foundation/media/attributes"; import * as mediaPlatformAttributes from "../../foundation/media/platform-attributes"; import * as videoDefaults from "../constants/video-constants"; import * as contentAttributes from "../content/attributes"; import * as content from "../content/content"; import * as editorialDataUtil from "./editorial-data-util"; import * as color from "../../foundation/util/color-util"; /** * The flavors for editorial art that can be used */ export var EditorialMediaFlavor; (function (EditorialMediaFlavor) { EditorialMediaFlavor["StoryCenteredMotion16x9"] = "storyCenteredMotion16x9"; EditorialMediaFlavor["StoryCenteredStatic16x9"] = "storyCenteredStatic16x9"; EditorialMediaFlavor["StoryDetailMotion3x4"] = "storyDetailMotion3x4"; EditorialMediaFlavor["StoryDetailStatic3x4"] = "storyDetailStatic3x4"; EditorialMediaFlavor["HeroMotion16x9"] = "heroMotion16x9"; EditorialMediaFlavor["HeroStatic16x9"] = "heroStatic16x9"; EditorialMediaFlavor["HeroMotionRTL16x9"] = "heroMotionRTL16x9"; EditorialMediaFlavor["HeroStaticRTL16x9"] = "heroStaticRTL16x9"; EditorialMediaFlavor["LargeBreakoutMotion16x9"] = "largeBreakoutMotion16x9"; EditorialMediaFlavor["LargeBreakoutStatic16x9"] = "largeBreakoutStatic16x9"; EditorialMediaFlavor["LargeBreakoutRTLMotion16x9"] = "largeBreakoutRTLMotion16x9"; EditorialMediaFlavor["LargeBreakoutRTLStatic16x9"] = "largeBreakoutRTLStatic16x9"; EditorialMediaFlavor["StoryCardMotion3x4"] = "storyCardMotion3x4"; EditorialMediaFlavor["StoryCardStatic3x4"] = "storyCardStatic3x4"; EditorialMediaFlavor["StorySearchStatic16x9"] = "storySearchStatic16x9"; EditorialMediaFlavor["SubscriptionHero"] = "subscriptionHero"; EditorialMediaFlavor["UniversalAMotion16x9"] = "universalAMotion16x9"; EditorialMediaFlavor["UniversalAStatic16x9"] = "universalAStatic16x9"; EditorialMediaFlavor["BrickStatic16x9"] = "brickStatic16x9"; EditorialMediaFlavor["BrickStaticRTL16x9"] = "brickStaticRTL16x9"; EditorialMediaFlavor["SearchCategoryBrick"] = "searchCategoryBrick"; })(EditorialMediaFlavor || (EditorialMediaFlavor = {})); /** * The different locations for where editorial art is displayed */ export var EditorialMediaPlacement; (function (EditorialMediaPlacement) { EditorialMediaPlacement["Hero"] = "hero"; EditorialMediaPlacement["LargeBreakout"] = "largeBreakout"; EditorialMediaPlacement["StoryCard"] = "storyCard"; EditorialMediaPlacement["StoryDetail"] = "storyDetail"; EditorialMediaPlacement["StoryDetailLandscape"] = "storyDetailLandscape"; EditorialMediaPlacement["Search"] = "search"; EditorialMediaPlacement["Brick"] = "brick"; EditorialMediaPlacement["EditorialLockup"] = "editorialLockup"; EditorialMediaPlacement["EditorialPage"] = "editorialPage"; })(EditorialMediaPlacement || (EditorialMediaPlacement = {})); /** * The mapping between the various placements and the preferred flavor for that location */ const preferredEditorialMediaFlavorMap = { hero: [ EditorialMediaFlavor.HeroMotion16x9, EditorialMediaFlavor.HeroStatic16x9, EditorialMediaFlavor.StoryCenteredMotion16x9, EditorialMediaFlavor.StoryCenteredStatic16x9, EditorialMediaFlavor.UniversalAMotion16x9, EditorialMediaFlavor.UniversalAStatic16x9, ], largeBreakout: [ EditorialMediaFlavor.LargeBreakoutMotion16x9, EditorialMediaFlavor.LargeBreakoutStatic16x9, EditorialMediaFlavor.StoryCenteredMotion16x9, EditorialMediaFlavor.StoryCenteredStatic16x9, EditorialMediaFlavor.UniversalAMotion16x9, EditorialMediaFlavor.UniversalAStatic16x9, ], storyCard: [ EditorialMediaFlavor.StoryCardMotion3x4, EditorialMediaFlavor.StoryCardStatic3x4, EditorialMediaFlavor.StoryCenteredMotion16x9, EditorialMediaFlavor.StoryCenteredStatic16x9, EditorialMediaFlavor.UniversalAMotion16x9, EditorialMediaFlavor.UniversalAStatic16x9, ], storyDetail: [ EditorialMediaFlavor.StoryDetailMotion3x4, EditorialMediaFlavor.StoryDetailStatic3x4, EditorialMediaFlavor.StoryCenteredMotion16x9, EditorialMediaFlavor.StoryCenteredStatic16x9, EditorialMediaFlavor.UniversalAMotion16x9, EditorialMediaFlavor.UniversalAStatic16x9, ], storyDetailLandscape: [ EditorialMediaFlavor.StoryCenteredMotion16x9, EditorialMediaFlavor.StoryCenteredStatic16x9, EditorialMediaFlavor.UniversalAMotion16x9, EditorialMediaFlavor.UniversalAStatic16x9, EditorialMediaFlavor.StoryDetailMotion3x4, EditorialMediaFlavor.StoryDetailStatic3x4, ], search: [ EditorialMediaFlavor.StorySearchStatic16x9, EditorialMediaFlavor.StoryCenteredStatic16x9, EditorialMediaFlavor.UniversalAStatic16x9, ], brick: [ EditorialMediaFlavor.BrickStatic16x9, EditorialMediaFlavor.HeroStatic16x9, EditorialMediaFlavor.StoryCenteredStatic16x9, EditorialMediaFlavor.UniversalAStatic16x9, EditorialMediaFlavor.SearchCategoryBrick, ], editorialLockup: [EditorialMediaFlavor.SubscriptionHero], editorialPage: [ EditorialMediaFlavor.StoryCardStatic3x4, EditorialMediaFlavor.HeroStatic16x9, EditorialMediaFlavor.StoryCenteredStatic16x9, EditorialMediaFlavor.SubscriptionHero, ], }; /** * The mapping between the various placements and the preferred flavor for that location, for RTL flavors */ const preferredEditorialMediaRTLFlavorMap = { hero: [ EditorialMediaFlavor.HeroMotionRTL16x9, EditorialMediaFlavor.HeroStaticRTL16x9, EditorialMediaFlavor.StoryCenteredMotion16x9, EditorialMediaFlavor.StoryCenteredStatic16x9, EditorialMediaFlavor.UniversalAMotion16x9, EditorialMediaFlavor.UniversalAStatic16x9, ], largeBreakout: [ EditorialMediaFlavor.LargeBreakoutRTLMotion16x9, EditorialMediaFlavor.LargeBreakoutRTLStatic16x9, EditorialMediaFlavor.StoryCenteredMotion16x9, EditorialMediaFlavor.StoryCenteredStatic16x9, EditorialMediaFlavor.UniversalAMotion16x9, EditorialMediaFlavor.UniversalAStatic16x9, ], storyCard: [ EditorialMediaFlavor.StoryCardMotion3x4, EditorialMediaFlavor.StoryCardStatic3x4, EditorialMediaFlavor.StoryCenteredMotion16x9, EditorialMediaFlavor.StoryCenteredStatic16x9, EditorialMediaFlavor.UniversalAMotion16x9, EditorialMediaFlavor.UniversalAStatic16x9, ], storyDetail: [ EditorialMediaFlavor.StoryDetailMotion3x4, EditorialMediaFlavor.StoryDetailStatic3x4, EditorialMediaFlavor.StoryCenteredMotion16x9, EditorialMediaFlavor.StoryCenteredStatic16x9, EditorialMediaFlavor.UniversalAMotion16x9, EditorialMediaFlavor.UniversalAStatic16x9, ], storyDetailLandscape: [ EditorialMediaFlavor.StoryCenteredMotion16x9, EditorialMediaFlavor.StoryCenteredStatic16x9, EditorialMediaFlavor.UniversalAMotion16x9, EditorialMediaFlavor.UniversalAStatic16x9, EditorialMediaFlavor.StoryDetailMotion3x4, EditorialMediaFlavor.StoryDetailStatic3x4, ], search: [ EditorialMediaFlavor.StorySearchStatic16x9, EditorialMediaFlavor.StoryCenteredStatic16x9, EditorialMediaFlavor.UniversalAStatic16x9, ], brick: [ EditorialMediaFlavor.BrickStaticRTL16x9, EditorialMediaFlavor.HeroStatic16x9, EditorialMediaFlavor.StoryCenteredStatic16x9, EditorialMediaFlavor.UniversalAStatic16x9, EditorialMediaFlavor.SearchCategoryBrick, ], editorialLockup: [EditorialMediaFlavor.SubscriptionHero], editorialPage: [ EditorialMediaFlavor.StoryCardStatic3x4, EditorialMediaFlavor.HeroStatic16x9, EditorialMediaFlavor.StoryCenteredStatic16x9, EditorialMediaFlavor.SubscriptionHero, ], }; /** * Create the best suitable editorial artwork / video for the given placement, including the additional data that might be * needed about this artwork * * @param objectGraph * @param data * @param placement */ export function editorialMediaDataForPlacement(objectGraph, data, placement) { const editorialCardData = editorialDataUtil.extractEditorialCardData(objectGraph, data); const preferredEditorialMediaFlavors = preferredEditorialMediaFlavorMap[placement]; const editorialMediaData = editorialMediaDataForPlacementAndFlavors(objectGraph, data, editorialCardData, placement, preferredEditorialMediaFlavors, false); const preferredEditorialMediaRTLFlavors = preferredEditorialMediaRTLFlavorMap[placement]; const rtlEditorialMediaData = editorialMediaDataForPlacementAndFlavors(objectGraph, data, editorialCardData, placement, preferredEditorialMediaRTLFlavors, true); editorialMediaData.rtlArtwork = rtlEditorialMediaData.rtlArtwork; editorialMediaData.rtlArtworkData = rtlEditorialMediaData.rtlArtworkData; editorialMediaData.rtlVideo = rtlEditorialMediaData.rtlVideo; editorialMediaData.rtlBackgroundColor = rtlEditorialMediaData.rtlBackgroundColor; editorialMediaData.rtlTextColorOverride = rtlEditorialMediaData.rtlTextColorOverride; return editorialMediaData; } /** * Creates the `EditorialMediaData` object for a specific placement and flavors. * * Note: This will only populate either the LTR properties or the RTL properties on the `EditorialMediaData` * object, never both. Which ones are populated is based on the `isRTL` flag. * * @param objectGraph Current object graph * @param data The data blob for the main item * @param editorialCardData The data blob for the editorial card * @param placement The placement for the media * @param flavors The flavor candidates for the media * @param isRTL Whether the placement is for RTL media * @returns A built `EditorialMediaData` object */ function editorialMediaDataForPlacementAndFlavors(objectGraph, data, editorialCardData, placement, flavors, isRTL) { let editorialMediaData = {}; for (const flavor of flavors) { // Check the editorial card first for override media if (serverData.isDefinedNonNull(editorialCardData)) { editorialMediaData = editorialVideoFromDataForFlavor(objectGraph, editorialCardData, placement, flavor, isRTL) || editorialArtworkFromDataForFlavor(objectGraph, editorialCardData, placement, flavor, isRTL); if (serverData.isDefinedNonNullNonEmpty(editorialMediaData)) { break; } } // If no override found, then check the main data editorialMediaData = editorialVideoFromDataForFlavor(objectGraph, data, placement, flavor, isRTL) || editorialArtworkFromDataForFlavor(objectGraph, data, placement, flavor, isRTL); if (serverData.isDefinedNonNullNonEmpty(editorialMediaData)) { break; } } if (isNothing(editorialMediaData)) { editorialMediaData = {}; } return editorialMediaData; } /** * Create the best suitable editorial artwork for the given flavor, including the additional data that might be * needed about this artwork, returns null if there is no editorial artwork for this flavor * * @param objectGraph * @param data * @param placement * @param flavor * @param isRTL */ function editorialArtworkFromDataForFlavor(objectGraph, data, placement, flavor, isRTL) { let editorialArtwork = null; let editorialArtworkData = null; let editorialArtworkBackgroundColor = null; const editorialArtworkPath = `editorialArtwork.${flavor}`; const attributePlatform = contentAttributes.bestAttributePlatformFromData(objectGraph, data); if (serverData.isNull(attributePlatform)) { return null; } editorialArtworkData = mediaAttributes.attributeAsDictionary(data, editorialArtworkPath) || mediaPlatformAttributes.platformAttributeAsDictionary(data, attributePlatform, editorialArtworkPath); if (!serverData.isDefinedNonNullNonEmpty(editorialArtworkData)) { return null; } editorialArtwork = content.artworkFromApiArtwork(objectGraph, editorialArtworkData, { withJoeColorPlaceholder: true, useCase: artworkUseCaseForPlacement(placement), cropCode: cropCodeForPlacementAndFlavor(placement, flavor, isRTL), }); if (serverData.isNull(editorialArtwork)) { return null; } editorialArtworkBackgroundColor = editorialArtwork.backgroundColor; const textColorOverride = editorialTextColorOverride(objectGraph, editorialArtworkData, editorialArtwork); if (isRTL) { return { rtlArtwork: editorialArtwork, rtlArtworkData: editorialArtworkData, rtlBackgroundColor: editorialArtworkBackgroundColor, rtlTextColorOverride: textColorOverride, }; } else { return { artwork: editorialArtwork, artworkData: editorialArtworkData, backgroundColor: editorialArtworkBackgroundColor, textColorOverride: textColorOverride, }; } } /** * Create the best suitable editorial artwork for the given flavor, including the additional data that might be * needed about this artwork, returns null if there is no editorial artwork for this flavor * * @param objectGraph * @param data * @param placement * @param flavor */ function editorialVideoFromDataForFlavor(objectGraph, data, placement, flavor, isRTL) { let editorialVideo = null; let previewFrameArtwork = null; let editorialVideoData = null; let editorialVideoBackgroundColor = null; const editorialVideoPath = `editorialVideo.${flavor}`; const attributePlatform = contentAttributes.bestAttributePlatformFromData(objectGraph, data); if (isNothing(attributePlatform)) { return null; } editorialVideoData = mediaAttributes.attributeAsDictionary(data, editorialVideoPath) || mediaPlatformAttributes.platformAttributeAsDictionary(data, attributePlatform, editorialVideoPath); if (serverData.isNullOrEmpty(editorialVideoData)) { return null; } const previewFrameData = serverData.asDictionary(editorialVideoData, "previewFrame"); if (serverData.isDefinedNonNullNonEmpty(previewFrameData)) { previewFrameArtwork = content.artworkFromApiArtwork(objectGraph, previewFrameData, { withJoeColorPlaceholder: true, useCase: artworkUseCaseForPlacement(placement), cropCode: "sr", }); } if (serverData.isDefinedNonNullNonEmpty(previewFrameArtwork)) { previewFrameArtwork.crop = "sr"; editorialVideoBackgroundColor = previewFrameArtwork.backgroundColor; } const videoUrl = serverData.asString(editorialVideoData, "video"); if (serverData.isDefinedNonNullNonEmpty(previewFrameArtwork) && isSome(videoUrl) && videoUrl.length > 0) { editorialVideo = new models.Video(videoUrl, previewFrameArtwork, { canPlayFullScreen: false, allowsAutoPlay: true, looping: true, playbackControls: videoPlaybackControls(objectGraph, placement), autoPlayPlaybackControls: {}, }); editorialVideo.editorialMediaFlavor = flavor; editorialVideo.editorialMediaPlacement = placement; } if (isNothing(previewFrameData) || isNothing(previewFrameArtwork)) { return null; } const textColorOverride = editorialTextColorOverride(objectGraph, previewFrameData, previewFrameArtwork); if (isRTL) { return { rtlVideo: editorialVideo, rtlArtwork: previewFrameArtwork, rtlArtworkData: previewFrameData, rtlBackgroundColor: editorialVideoBackgroundColor, rtlTextColorOverride: textColorOverride, }; } else { return { video: editorialVideo, artwork: previewFrameArtwork, artworkData: previewFrameData, backgroundColor: editorialVideoBackgroundColor, textColorOverride: textColorOverride, }; } } /** * Determine the correct artwork use case for each placement * @param placement */ function artworkUseCaseForPlacement(placement) { switch (placement) { case EditorialMediaPlacement.Hero: return 19 /* content.ArtworkUseCase.GroupingHero */; case EditorialMediaPlacement.LargeBreakout: return 6 /* content.ArtworkUseCase.ArcadeLargeBreakout */; case EditorialMediaPlacement.StoryCard: return 15 /* content.ArtworkUseCase.TodayCardMedia */; case EditorialMediaPlacement.StoryDetail: case EditorialMediaPlacement.StoryDetailLandscape: return 13 /* content.ArtworkUseCase.ArticleImage */; case EditorialMediaPlacement.Search: return 9 /* content.ArtworkUseCase.SearchEditorialResult */; default: return 0 /* content.ArtworkUseCase.Default */; } } /** * Determine the correct crop code to use for artwork at a given flavor * @param placement * @param flavor * @param isRTL */ function cropCodeForPlacementAndFlavor(placement, flavor, isRTL) { switch (placement) { case EditorialMediaPlacement.Hero: switch (flavor) { case EditorialMediaFlavor.HeroStatic16x9: if (!isRTL) { return "gd"; } break; case EditorialMediaFlavor.HeroStaticRTL16x9: if (isRTL) { return "gg"; } break; case EditorialMediaFlavor.StoryCenteredStatic16x9: return isRTL ? "gh" : "ge"; case EditorialMediaFlavor.UniversalAStatic16x9: return isRTL ? "gj" : "gi"; default: break; } break; case EditorialMediaPlacement.LargeBreakout: switch (flavor) { case EditorialMediaFlavor.StoryCenteredStatic16x9: return isRTL ? "gk" : "gf"; case EditorialMediaFlavor.UniversalAStatic16x9: return isRTL ? "gl" : "gm"; default: break; } break; case EditorialMediaPlacement.StoryCard: case EditorialMediaPlacement.StoryDetail: case EditorialMediaPlacement.StoryDetailLandscape: case EditorialMediaPlacement.Search: switch (flavor) { case EditorialMediaFlavor.UniversalAStatic16x9: return "gn"; default: break; } break; case EditorialMediaPlacement.Brick: switch (flavor) { case EditorialMediaFlavor.SearchCategoryBrick: return "SCB.ApSCBL01"; default: break; } break; default: break; } return "sr"; } /** * Determines whether a given piece of editorial media is considered dark, light, or unknown. * @param objectGraph Current object graph * @param mediaData The data for the editorial media * @param isRTL Whether the media to check is for RTL * @returns True if the media is considered to be dark, false if considered light, or undefined if unknown. */ export function isMediaDark(objectGraph, editorialMediaData, isRTL = false) { if (isNothing(editorialMediaData)) { return null; } const textColorOverride = isRTL ? editorialMediaData.rtlTextColorOverride : editorialMediaData.textColorOverride; const backgroundColor = isRTL ? editorialMediaData.rtlBackgroundColor : editorialMediaData.backgroundColor; if (isSome(textColorOverride)) { // Custom text colors can either be white or black. If the custom color is white, we set the // media as dark, which will implicitly use white text. The behaviour is reversed for black text. return color.isColorEqualToColor(textColorOverride, color.white); } else if (isSome(backgroundColor)) { // If no custom text color is set, use the media background color to determine the darkness. return color.isDarkColor(backgroundColor); } return null; } /** * Determines if an editorially set custom text color should be used. * @param objectGraph Current object graph * @param artworkData The data that represents the editorial artwork * @param artwork The parsed editorial artwork * @returns True if the component should respect an editorial color */ function editorialTextColorOverride(objectGraph, artworkData, artwork) { const textColor = artwork === null || artwork === void 0 ? void 0 : artwork.textColor; if (isNothing(textColor)) { return null; } const useCustomTextColor = serverData.asBooleanOrFalse(artworkData, "useCustomTextColor"); const customTextColorIsValid = color.isColorEqualToColor(textColor, color.black) || color.isColorEqualToColor(textColor, color.white); if (useCustomTextColor && customTextColorIsValid) { return textColor; } return null; } /** * Determines the video playback controls for a given placement. * @param objectGraph Current object graph * @param placement The placement for the view containing the video * @returns The video playback controls */ function videoPlaybackControls(objectGraph, placement) { switch (placement) { case EditorialMediaPlacement.Hero: case EditorialMediaPlacement.StoryDetail: case EditorialMediaPlacement.StoryDetailLandscape: return videoDefaults.noControls(objectGraph); default: return videoDefaults.standardControls(objectGraph); } } //# sourceMappingURL=editorial-media-util.js.map