@srgssr/pillarbox-web 1.12.2 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"pillarbox.cjs.min.js","sources":["../src/components/player.js","../src/pillarbox.js","../src/dataProvider/services/DataProvider.js","../src/utils/Image.js","../src/utils/Drm.js","../src/utils/AkamaiTokenService.js","../src/utils/PlayerEvents.js","../src/analytics/SRGAnalytics.js","../src/dataProvider/model/MediaComposition.js","../src/lang/de.js","../src/lang/en.js","../src/lang/fr.js","../src/lang/it.js","../src/lang/rm.js","../src/middleware/srgssr.js"],"sourcesContent":["import videojs from 'video.js';\nimport 'videojs-contrib-eme';\n\n/**\n * @ignore\n * @type {typeof import('video.js/dist/types/player').default}\n */\nconst vjsPlayer = videojs.getComponent('player');\n\n/**\n * This class extends the video.js Player.\n *\n * @class Player\n * @see https://docs.videojs.com/player\n */\nclass Player extends vjsPlayer {\n constructor(tag, options, ready) {\n /**\n * Configuration for plugins.\n *\n * @see [Video.js Plugins Option]{@link https://videojs.com/guides/options/#plugins}\n * @type {Object}\n * @property {boolean} eme - Enable the EME (Encrypted Media Extensions) plugin.\n */\n options = videojs.obj.merge(options, { plugins: { eme: true }});\n super(tag, options, ready);\n }\n\n /**\n * A getter/setter for the media's audio track.\n * Activates the audio track according to the language and kind properties.\n * Falls back on the first audio track found if the kind property is not satisfied.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/AudioTrack/kind\n * @see https://developer.mozilla.org/en-US/docs/Web/API/AudioTrack/language\n *\n * @param {import('./typedef').TrackSelector} [trackSelector]\n *\n * @example\n * // Get the current audio track\n * player.audioTrack();\n *\n * @example\n * // Activate an audio track based on language and kind properties\n * player.audioTrack({language:'en', kind:'description'});\n *\n * @example\n * // Activate first audio track found corresponding to language\n * player.audioTrack({language:'fr'});\n *\n * @return {import('video.js/dist/types/tracks/audio-track').default | undefined} The\n * currently enabled audio track. See {@link https://docs.videojs.com/audiotrack}.\n */\n audioTrack(trackSelector) {\n const audioTracks = Array.from(this.player().audioTracks());\n\n if (!trackSelector) {\n return audioTracks.find((audioTrack) => audioTrack.enabled);\n }\n\n const { kind, language } = trackSelector;\n const audioTrack =\n audioTracks.find(\n (audioTrack) =>\n audioTrack.language === language && audioTrack.kind === kind\n ) || audioTracks.find((audioTrack) => audioTrack.language === language);\n\n if (audioTrack) {\n audioTrack.enabled = true;\n }\n\n return audioTrack;\n }\n\n /**\n * Calculates an array of ranges based on the `buffered()` data.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/buffered\n *\n * @returns {Array<{start: number, end: number}>} An array of objects representing start and end points of buffered ranges.\n */\n bufferedRanges() {\n const ranges = [];\n\n for (let i = 0; i < this.buffered().length; i++) {\n const start = this.buffered().start(i);\n const end = this.buffered().end(i);\n\n ranges.push({ start, end });\n }\n\n return ranges;\n }\n\n /**\n * Get the percent (as a decimal) of the media that's been played.\n * This method is not a part of the native HTML video API.\n *\n * Live streams with DVR are not currently supported.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement#htmlmediaelement.played\n *\n * @return {number}\n * A decimal between 0 and 1 representing the percent\n * that is played 0 being 0% and 1 being 100%\n */\n playedPercent() {\n if (!Number.isFinite(this.duration())) return NaN;\n\n let timePlayed = 0;\n\n for (let i = 0; i != this.played().length; i++) {\n timePlayed += this.played().end(i) - this.played().start(i);\n }\n\n const percentPlayed = timePlayed / this.duration();\n\n return percentPlayed;\n }\n\n /**\n * Get an array of ranges based on the `played` data.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement#htmlmediaelement.played\n *\n * @returns {Array<{start: number, end: number}>} An array of objects representing start and end points of played ranges.\n */\n playedRanges() {\n const ranges = [];\n\n for (let i = 0; i < this.played().length; i++) {\n const start = this.played().start(i);\n const end = this.played().end(i);\n\n ranges.push({ start, end });\n }\n\n return ranges;\n }\n\n /**\n * Calculates an array of ranges based on the `seekable()` data.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/seekable\n *\n * @returns {Array<{start: number, end: number}>} An array of objects representing start and end points of seekable ranges.\n */\n seekableRanges() {\n const ranges = [];\n\n for (let i = 0; i < this.seekable().length; i++) {\n const start = this.seekable().start(i);\n const end = this.seekable().end(i);\n\n ranges.push({ start, end });\n }\n\n return ranges;\n }\n\n /**\n * A getter/setter for the media's text track.\n * Activates the text track according to the language and kind properties.\n * Falls back on the first text track found if the kind property is not satisfied.\n * Disables all subtitle tracks that are `showing` if the `trackSelector` is truthy but does not satisfy any condition.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/TextTrack/kind\n * @see https://developer.mozilla.org/en-US/docs/Web/API/textTrack/language\n *\n * @param {import('./typedef').TrackSelector} [trackSelector]\n *\n * @example\n * // Get the current text track\n * player.textTrack();\n *\n * @example\n * // Disable all text tracks has a side effect\n * player.textTrack('off');\n * player.textTrack({});\n *\n * @example\n * // Activate an text track based on language and kind properties\n * player.textTrack({language:'en', kind:'captions'});\n *\n * @example\n * // Activate first text track found corresponding to language\n * player.textTrack({language:'fr'});\n *\n * @return {import('video.js/dist/types/tracks/text-track').default | undefined} The\n * currently enabled text track. See {@link https://docs.videojs.com/texttrack}.\n */\n textTrack(trackSelector) {\n const textTracks = Array.from(this.player().textTracks()).filter(\n (textTrack) => !['chapters', 'metadata'].includes(textTrack.kind)\n );\n\n if (!trackSelector) {\n return textTracks.find((textTrack) => textTrack.mode === 'showing');\n }\n\n textTracks.forEach((textTrack) => (textTrack.mode = 'disabled'));\n\n const { kind, language } = trackSelector;\n const textTrack =\n textTracks.find((textTrack) => {\n if (textTrack.language === language && textTrack.kind === kind) {\n textTrack.mode = 'showing';\n }\n\n return textTrack.mode === 'showing';\n }) ||\n textTracks.find((textTrack) => {\n if (textTrack.language === language) {\n textTrack.mode = 'showing';\n }\n\n return textTrack.mode === 'showing';\n });\n\n return textTrack;\n }\n}\n\nvideojs.registerComponent('player', Player);\n\nexport default Player;\n","import { version } from '../package.json';\nimport videojs from 'video.js';\nimport './components/player.js';\n\n/**\n * Pillarbox is an alias for the video.js namespace with additional options.\n *\n * @namespace\n * @see https://docs.videojs.com/module-videojs-videojs\n * @type {videojs}\n */\nconst pillarbox = videojs;\n\npillarbox.VERSION = {\n pillarbox: version,\n videojs: videojs.VERSION,\n [videojs.VhsSourceHandler.name]: videojs.VhsSourceHandler.VERSION,\n eme: videojs.getPlugin('eme').VERSION,\n};\n\n/**\n * Enable smooth seeking for Pillarbox.\n *\n * @see [Video.js enableSmoothSeeking Option]{@link https://videojs.com/guides/options/#enablesmoothseeking}\n * @type {boolean}\n * @default true\n */\npillarbox.options.enableSmoothSeeking = true;\n/**\n * Enable fill mode for the video player, allowing it to expand to fill the container.\n *\n * @see [Video.js Fill Option]{@link https://videojs.com/guides/layout/#fill-mode}\n * @type {boolean}\n * @default true\n */\npillarbox.options.fill = true;\n/**\n * Configuration options for HTML5 settings in Pillarbox.\n *\n * @see [VHS useForcedSubtitles Option]{@link https://github.com/videojs/http-streaming/blob/main/README.md#useforcedsubtitles}\n * @type {Object}\n * @property {Object} vhs - Configuration for the Video.js HTTP Streaming.\n * @property {boolean} useForcedSubtitles - Enables the player to display forced subtitles by default.\n * Forced subtitles are pieces of information intended for display when no other text representation\n * is selected. They are used to clarify dialogue, provide alternate languages, display texted graphics,\n * or present location/person IDs that are not otherwise covered in the dubbed/localized audio.\n */\npillarbox.options.html5 = {\n vhs: { useForcedSubtitles: true }\n};\n/**\n * Configuration for the live tracker.\n *\n * @see [Video.js liveTracker Option]{@link https://videojs.com/guides/options/#livetrackertrackingthreshold}\n * @type {Object}\n * @property {number} trackingThreshold - A threshold that controls when the liveui should be shown.\n * @property {number} liveTolerance - An option that controls how far from the seekable end should be considered live playback.\n */\npillarbox.options.liveTracker = {\n trackingThreshold: 120,\n liveTolerance: 15,\n};\n/**\n * Allows the player to use the live ui that includes:\n *\n * - A progress bar for seeking within the live window\n * - A button that can be clicked to seek to the live edge with a circle indicating if you are at the live edge or not.\n *\n * @see [Video.js liveui Option]{@link https://videojs.com/guides/options/#liveui}\n * @type {boolean}\n */\npillarbox.options.liveui = true;\n/**\n * Indicates that the video is to be played \"inline\", that is within the element's playback area.\n *\n * @see [Video element playsinline attribute]{@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#playsinline}\n * @type {boolean}\n */\npillarbox.options.playsinline = true;\n/**\n * Enable responsive mode, this will cause the player to customize itself based on responsive breakpoints.\n *\n * @see [Video.js Responsive Option]{@link https://videojs.com/guides/options/#responsive}\n * @type {boolean}\n */\npillarbox.options.responsive = true;\n/**\n * A placeholder for accessing trackers directly from the player.\n *\n * @type {Object}\n */\npillarbox.options.trackers = {};\n\nexport default pillarbox;\n","/**\n * Represents a data provider for constructing URLs and handling requests.\n * @class\n * @ignore\n */\nclass DataProvider {\n /**\n * Creates an instance of DataProvider.\n *\n * @param {string} [hostName='il.srgssr.ch'] The base host name for constructing URLs\n */\n constructor(hostName = 'il.srgssr.ch') {\n this.setIlHost(hostName);\n }\n\n /**\n * Sets the integration layer host name.\n *\n * @param {string} hostName The host name to set\n */\n setIlHost(hostName) {\n this.baseUrl = `${hostName}/integrationlayer/2.1/`;\n }\n\n /**\n * Handles requests by constructing URLs and fetching data.\n *\n * This provides unified error handling, regardless of the urlHandler used.\n *\n * @param {Function} urlHandler A function that constructs the URL\n *\n * @returns {Promise<import('../model/MediaComposition.js').default>} A promise with the fetched data\n */\n handleRequest(urlHandler) {\n return async (urn) => {\n const url = typeof urlHandler === 'function' ? urlHandler(urn) : this.mediaCompositionUrlHandler(urn);\n const response = await fetch(url);\n\n if (!response.ok) {\n throw response;\n }\n\n /** @type {import('../model/MediaComposition.js').default} */\n const data = await response.json();\n\n return data;\n };\n }\n\n /**\n * Gets the media composition URL by URN.\n *\n * @param {string} urn The URN for the media composition\n *\n * @returns {string} The constructed URL\n */\n mediaCompositionUrlHandler(urn) {\n return `https://${this.baseUrl}mediaComposition/byUrn/${urn}?onlyChapters=true&vector=portalplay`;\n }\n}\n\nexport default DataProvider;\n","const SCALE = {\n WIDTH_240: '240',\n WIDTH_320: '320',\n WIDTH_480: '480',\n WIDTH_960: '960',\n WIDTH_1920: '1920',\n};\n\nconst FORMAT = {\n JPG: 'jpg',\n WEBP: 'webp',\n PNG: 'png',\n};\n\nconst IMAGE_SERVICE_URL = 'https://il.srgssr.ch/images/';\n\n/**\n * @class Image\n */\nclass Image {\n /**\n * Generates the image scaling URL.\n *\n * @property {Object} image is the object representation of an image.\n * @property {String} [image.url] is the image URL.\n * @property {String} [image.width=960] is the width of the image, default value 960.\n * @property {String} [image.format=jpg] is the format of the image, default value jpg.\n * @property {String} [imageServiceUrl] Url of the image service that needs to comply with the specification defined by the IL.\n *\n * @see https://confluence.srg.beecollaboration.com/pages/viewpage.action?spaceKey=SRGPLAY&title=Project+-+Image+Service\n *\n * @returns {String|undefined} the image scaling URL.\n */\n static scale(\n { url, width = SCALE.WIDTH_960, format = FORMAT.JPG } = {},\n imageServiceUrl = IMAGE_SERVICE_URL\n ) {\n if (!url) return;\n\n const scaleUrl = new URL(imageServiceUrl);\n\n scaleUrl.searchParams.set('imageUrl', url);\n scaleUrl.searchParams.set('format', format);\n scaleUrl.searchParams.set('width', width);\n\n return decodeURIComponent(scaleUrl.href);\n }\n\n static get JPG() {\n return FORMAT.JPG;\n }\n\n static get PNG() {\n return FORMAT.PNG;\n }\n\n static get WEBP() {\n return FORMAT.WEBP;\n }\n\n static get WIDTH_240() {\n return SCALE.WIDTH_240;\n }\n\n static get WIDTH_320() {\n return SCALE.WIDTH_320;\n }\n\n static get WIDTH_480() {\n return SCALE.WIDTH_480;\n }\n\n static get WIDTH_960() {\n return SCALE.WIDTH_960;\n }\n\n static get WIDTH_1920() {\n return SCALE.WIDTH_1920;\n }\n}\n\nexport default Image;\n","const DRM_VENDORS = {\n WIDEVINE: 'com.widevine.alpha',\n FAIRPLAY: 'com.apple.fps.1_0',\n PLAYREADY: 'com.microsoft.playready',\n};\n\n/**\n * @class Drm\n */\nclass Drm {\n /**\n * Build the keySystems object according to the DRM vendor.\n *\n * @param {Array.<import('../dataProvider/model/typedef').DrmMetadata>} drmList The DRM list from the media composition.\n *\n * @returns {import('./typedef').KeySystems} The resulting keySystems.\n */\n static buildKeySystems(drmList = []) {\n const keySystems = {};\n\n drmList.forEach((drmVendor) => {\n const type = Drm.vendors[drmVendor.type];\n\n if (Drm.vendors.FAIRPLAY === type) {\n const { certificateUrl: certificateUri, licenseUrl: licenseUri } =\n drmVendor;\n\n keySystems[type] = {\n certificateUri,\n licenseUri,\n };\n } else {\n keySystems[type] = drmVendor.licenseUrl;\n }\n });\n\n return {\n keySystems,\n };\n }\n\n /**\n * Check if some of the resources have DRM.\n *\n * @param {Array.<import('../dataProvider/model/typedef').MainResource>} resources\n *\n * @returns {boolean} true if some of the resources have DRM, false otherwise.\n */\n static hasDrm(resources) {\n return resources.some(({ drmList }) => drmList && drmList.length > 0);\n }\n\n /**\n * Get DRM vendors.\n */\n static get vendors() {\n return DRM_VENDORS;\n }\n}\n\nexport default Drm;\n","const TOKEN_TYPES = {\n AKAMAI: 'AKAMAI',\n NONE: 'NONE',\n};\n\n/**\n * @class AkamaiTokenService\n */\nclass AkamaiTokenService {\n /**\n * Get the acl path.\n *\n * @param {URL} streamUrl\n *\n * @returns {String}\n */\n static aclPath(streamUrl) {\n const path = streamUrl.pathname;\n\n return `${path.substring(0, path.lastIndexOf('/') + 1)}*`;\n }\n\n /**\n * AKAMAI\n *\n * @type {String}\n */\n static get AKAMAI() {\n return TOKEN_TYPES.AKAMAI;\n }\n\n /**\n * Check if the resources are protected by an Akamai token.\n * Keep in mind, as we are using the 'some' function,\n * if the resources have at least one resource\n * protected by a token it returns true!\n *\n * @param {Array.<import('../dataProvider/model/typedef').MainResource>} resources\n *\n * @returns {Boolean}\n */\n static hasToken(resources) {\n return resources.some((resource) =>\n AkamaiTokenService.isAkamai(resource.tokenType));\n }\n\n /**\n * Check if the token type is AKAMAI.\n *\n * @param {String} tokentype\n *\n * @returns {Boolean}\n */\n static isAkamai(tokentype) {\n return TOKEN_TYPES.AKAMAI === tokentype;\n }\n\n /**\n * Check if the token type is NONE.\n *\n * @param {String} tokentype\n *\n * @returns {Boolean}\n */\n static isNone(tokentype) {\n return TOKEN_TYPES.NONE === tokentype;\n }\n\n /**\n * NONE\n *\n * @type {String}\n */\n static get NONE() {\n return TOKEN_TYPES.NONE;\n }\n\n /**\n * Generate the stream URL with the akamai token.\n *\n * @param {import('../dataProvider/model/typedef').MainResource} source\n * @param {String} tokenServerUrl\n *\n * @returns {Promise.<import('../dataProvider/model/typedef').MainResource>}\n */\n static tokenize(source, tokenServerUrl) {\n const streamUrlToTokenize = new URL(`${source.url}`);\n const acl = AkamaiTokenService.aclPath(streamUrlToTokenize);\n const url = `${tokenServerUrl}${encodeURIComponent(acl)}`;\n\n return fetch(url)\n .then((response) => {\n if (response.ok) {\n return response.json();\n }\n\n return Promise.reject({\n status: response.status,\n statusText: response.statusText,\n });\n })\n .then(({ token: { authparams }}) => {\n const akamaiAuthParams = new URLSearchParams(authparams);\n\n akamaiAuthParams.forEach((v, k) =>\n streamUrlToTokenize.searchParams.set(k, v));\n\n return Object.assign({}, source, {\n url: streamUrlToTokenize.toString(),\n });\n })\n .catch((reason) => {\n return Promise.reject(reason);\n });\n }\n\n /**\n * Generate a token for each source\n *\n * @template {import('../dataProvider/model/typedef').MainResource} T\n * @param {Array.<T>} sources\n * @param {String} tokenServerUrl\n *\n * @returns {Promise.<Array.<T>>}\n */\n static tokenizeSources(\n sources,\n tokenServerUrl = 'https://tp.srgssr.ch/akahd/token?acl='\n ) {\n const tokenizedSources = [];\n\n sources.forEach((source) => {\n const tokenizedSource = AkamaiTokenService.tokenize(\n source,\n tokenServerUrl\n );\n\n tokenizedSources.push(tokenizedSource);\n });\n\n return Promise.all(tokenizedSources)\n .then((values) => values)\n .catch((reason) => Promise.reject(reason));\n }\n}\n\nexport default AkamaiTokenService;\n","/**\n * Exhaustive list of player events.\n *\n * See below the documentation related to the media events\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement#events\n *\n * @namespace {Object} PlayerEvents\n */\n\n/**\n * Triggered when the media can start playing.\n *\n * @event PlayerEvents#CAN_PLAY\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canplay_event\n */\nexport const CAN_PLAY = 'canplay';\n\n/**\n * Triggered when the media can be played through to the end without buffering.\n *\n * @event PlayerEvents#CAN_PLAY_THROUGH\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canplaythrough_event\n */\nexport const CAN_PLAY_THROUGH = 'canplaythrough';\n\n/**\n * Triggered when the duration of the media changes.\n *\n * @event PlayerEvents#DURATION_CHANGE\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/durationchange_event\n */\nexport const DURATION_CHANGE = 'durationchange';\n\n/**\n * Triggered when the media element is emptied (e.g., reset as part of the seeking process).\n *\n * @event PlayerEvents#EMPTIED\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/emptied_event\n */\nexport const EMPTIED = 'emptied';\n\n/**\n * Triggered when the media playback has ended.\n *\n * @event PlayerEvents#ENDED\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/ended_event\n */\nexport const ENDED = 'ended';\n\n/**\n * Triggered when an error occurs during media playback.\n *\n * @event PlayerEvents#ERROR\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/error_event\n */\nexport const ERROR = 'error';\n\n/**\n * Triggered when the media data has been loaded.\n *\n * @event PlayerEvents#LOADED_DATA\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/loadeddata_event\n */\nexport const LOADED_DATA = 'loadeddata';\n\n/**\n * Triggered when metadata for the media has been loaded.\n *\n * @event PlayerEvents#LOADED_METADATA\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/loadedmetadata_event\n */\nexport const LOADED_METADATA = 'loadedmetadata';\n\n/**\n * Triggered when the browser starts looking for media data.\n *\n * @event PlayerEvents#LOAD_START\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/loadstart_event\n */\nexport const LOAD_START = 'loadstart';\n\n/**\n * Triggered when the media playback is paused.\n *\n * @event PlayerEvents#PAUSE\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/pause_event\n */\nexport const PAUSE = 'pause';\n\n/**\n * Triggered when the media playback is resumed or started.\n *\n * @event PlayerEvents#PLAY\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play_event\n */\nexport const PLAY = 'play';\n\n/**\n * Triggered when the media playback is in progress.\n *\n * @event PlayerEvents#PLAYING\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/playing_event\n */\nexport const PLAYING = 'playing';\n\n/**\n * Triggered as the media is being loaded.\n *\n * @event PlayerEvents#PROGRESS\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/progress_event\n */\nexport const PROGRESS = 'progress';\n\n/**\n * Triggered when the playback rate changes.\n *\n * @event PlayerEvents#RATE_CHANGE\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/ratechange_event\n */\nexport const RATE_CHANGE = 'ratechange';\n\n/**\n * Triggered when a seek operation is completed.\n *\n * @event PlayerEvents#SEEKED\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/seeked_event\n */\nexport const SEEKED = 'seeked';\n\n/**\n * Triggered when a seek operation is in progress.\n *\n * @event PlayerEvents#SEEKING\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/seeking_event\n */\nexport const SEEKING = 'seeking';\n\n/**\n * Triggered when the media playback is stalled.\n *\n * @event PlayerEvents#STALLED\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/stalled_event\n */\nexport const STALLED = 'stalled';\n\n/**\n * Triggered when media loading is suspended.\n *\n * @event PlayerEvents#SUSPEND\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/suspend_event\n */\nexport const SUSPEND = 'suspend';\n\n/**\n * Triggered when the current playback position is updated.\n *\n * @event PlayerEvents#TIME_UPDATE\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/timeupdate_event\n */\nexport const TIME_UPDATE = 'timeupdate';\n\n/**\n * Triggered when the volume is changed.\n *\n * @event PlayerEvents#VOLUME_CHANGE\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/volumechange_event\n */\nexport const VOLUME_CHANGE = 'volumechange';\n\n/**\n * Triggered when the media playback is waiting for data.\n *\n * @event PlayerEvents#WAITING\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/waiting_event\n */\nexport const WAITING = 'waiting';\n","import * as PlayerEvents from '../utils/PlayerEvents.js';\nimport Pillarbox from '../pillarbox.js';\n/* eslint max-lines-per-function: [\"error\", 200] */\n/* eslint max-statements: [\"error\", 20]*/\n/* eslint complexity: [\"error\", 10]*/\n/**\n * SRG analytics\n * @class SRGAnalytics\n * @ignore\n *\n * ### Script URL\n * JS script : https://colibri-js.akamaized.net/penguin/tc_SRGGD_11.js\n *\n * ### Official documentation\n * Variables list\n * @see https://confluence.srg.beecollaboration.com/display/INTFORSCHUNG/Datalayer+for+media+players\n *\n * Standard event sequences\n * @see https://confluence.srg.beecollaboration.com/display/INTFORSCHUNG/standard+streaming+events%3A+sequence+of+events+for+media+player+actions\n *\n * Review of Standard Media Actions\n * @see https://confluence.srg.beecollaboration.com/display/INTFORSCHUNG/Implementation+Concept+-+draft\n *\n * ComScore Implementation Guide\n * @see https://www.dropbox.com/sh/cdwuikq0abxi21m/AABmSyXYKUTWSAwRZgQA9Ujna/JavaScript%20Latest%20Version?dl=0&preview=Comscore_Library-JavaScript-Streaming_Tag-Implementation_Guide-International.pdf&subfolder_nav_tracking=1\n *\n * ### Variables list\n * - 'event_id', // init | play | stop | pos | pause | seek | uptime | eof\n * - 'event_timestamp', // Seems to be generated automatically from the documentation, but the TP overrides it\n * - 'event_name', // NA TP seems to not sending this variable\n * - 'event_source', // NA TP seems to not sending this variable\n * - 'event_name', // NA TP seems to not sending this variable\n * - 'event_value', // NA TP seems to not sending this variable\n * - 'navigation_environment', // prod | preprod\n * - 'media_subtitles_on', // string true | false\n * - 'media_timeshift', // need better description\n * - 'media_quality', // SD | HD ?\n * - 'media_bandwidth', // NA for the web, 64000\n * - 'media_volume', // from 0 to 100\n * - 'media_embedding_url', //\n * - 'media_player_name', // videojs | letterbox-web ?\n * - 'media_chromecast_selected', // boolean true | false\n * - 'media_player_version', // player's version\n * - 'media_player_display', // is the player mode, on the TP : inline, embed etc..\n * - 'media_audio_track', // NA\n * - 'media_position_real', // NA\n * - 'media_time_spent', // NA\n * - 'device_id', // NA\n * - 'user_id_log_in', // NA only RTS has log in today\n * - 'media_thumbnail', // Not required by the spec but sended by the TP\n * - 'media_bu_distributer', // Not required by the spec but sended by the TP\n *\n *\n * ### Sequence stories\n *\n * __Story 1 (AoD/VOD-basics)__: A VoD is played. The user does not interact with the player. The VoD plays to its end.\n *\n * Hints:\n * - Media sessions always start with PLAY. They end with STOP or EOF (or with PAUSE or last POS)\n * - POS is sent ever 30s\n *\n *\n * __Story 2 (livestream-basics A)__: A Livestream is played. The user does not interact with the player. After 61 seconds, playback is paused.\n *\n * Hints:\n * - Media sessions always start with PLAY. They end with STOP (or, worse for data quailty, with PAUSE or last POS/UPTIME)\n * - UPTIME is sent only for livestreams\n * - POS is sent ever 30s, UPTIME every 60s with inital UPTIME after 30s.\n * - This is the interval: 30s: POS + UPTIME; 60s: POS; 90s: POS + UPTIME; ...\n *\n *\n * __Story 3 (Seeking a VoD/AoD)__: A VoD is played. User seeks in the VoD/AoD.\n *\n * Hints:\n * - Once the Media Player slider is released (seek is over), another action to finish up the seeking is initiated. Typically this is PLAY. For that second PLAY, the media position has altered.\n *\n *\n * __Story 4 (Seeking a livestream)__: A Livestream is played. User goes back in the livestream.\n *\n * Hints:\n * - Once the Media Player slider is released (seek is over), another action to finish up the seeking is initiated. Typically this is PLAY. For that second PLAY, the a new variable, media_timeshift is passed.\n * - For livestreams media_position is always the \"time passed on your watch\" - regardless of the SEEK event. So, if 1 second after PLAY the slider is moved back 600 seconds, then:\n * 1. The the value of media_timeshift is '600'.\n * 2. The value of media_position is '1'.\n */\nclass SRGAnalytics {\n constructor(\n player,\n {\n debug = false,\n environment = 'prod',\n playerVersion = 'none',\n tagCommanderScriptURL = '//colibri-js.akamaized.net/penguin/tc_SRGGD_11.js',\n } = {}\n ) {\n this.isDebugEnabled = debug;\n this.elapsedPlaybackTime = 0;\n this.environment = environment;\n this.hasStarted = false;\n this.heartBeatIntervalId = undefined;\n /* Set to true when 'init' event is sent or queued. */\n this.initialized = false;\n this.isSeeking = false;\n this.isWaiting = false;\n this.mediaSession = 0;\n this.pendingQueue = [];\n this.pendingTagCommanderReload = false;\n this.player = player;\n this.playerVersion = playerVersion;\n this.srcMediaData = undefined;\n this.startPlaybackSession = 0;\n this.tagCommanderScriptURL = tagCommanderScriptURL;\n this.trackedCurrentTime = 0;\n this.uptimeIntervalId = undefined;\n\n this.initScript();\n this.initListeners();\n }\n\n /**\n * Sent when the window, the document and its resources are about to be unloaded.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event\n */\n beforeunload() {\n this.notify('stop');\n }\n\n /**\n * Clear timers used to send uptime and heartbeat.\n */\n clearTimers() {\n clearInterval(this.heartBeatIntervalId);\n clearInterval(this.uptimeIntervalId);\n clearTimeout(this.uptimeTimeoutId);\n }\n\n /**\n * Get the tracked current time in seconds.\n *\n * @returns {Number} current time in seconds\n */\n currentTime() {\n // see PLAYRTS-2771\n return Math.round(this.trackedCurrentTime);\n }\n\n /**\n * Get or set debug mode.\n *\n * @returns {Boolean|undefined}\n */\n debug(enabled) {\n if (enabled === undefined) {\n return this.isDebugEnabled || this.player.debug();\n }\n\n this.isDebugEnabled = Boolean(enabled);\n }\n\n /**\n * Destroy all properties and setIntervals to avoid mixing media sessions.\n */\n destroy() {\n this.clearTimers();\n\n if (!window.tc_vars) {\n window.tc_vars = {};\n }\n this.elapsedPlaybackTime = 0;\n this.hasStarted = false;\n this.heartBeatIntervalId = undefined;\n this.initialized = false;\n this.isWaiting = false;\n this.mediaSession = 0;\n this.pendingQueue = [];\n this.srcMediaData = undefined;\n this.startPlaybackSession = 0;\n this.trackedCurrentTime = 0;\n this.uptimeIntervalId = undefined;\n }\n\n /**\n * Dispose all listeners used to send analytics data to TagCommander.\n *\n * Calls `beforeunload` to send a notify stop.\n * Clear intervals and timeouts.\n *\n * __Used events__\n * - beforeunload\n * - emptied\n * - ended\n * - loadstart\n * - loadeddata\n * - play\n * - pause\n * - timeupdate\n */\n dispose() {\n this.beforeunload();\n this.clearTimers();\n\n window.removeEventListener('beforeunload', this.beforeunloadListener);\n\n this.player.off(PlayerEvents.EMPTIED, this.emptiedListener);\n this.player.off(PlayerEvents.ENDED, this.endedListener);\n this.player.off(PlayerEvents.LOAD_START, this.loadstartListener);\n this.player.off(PlayerEvents.LOADED_DATA, this.loadeddataListener);\n this.player.off(PlayerEvents.PLAYING, this.playListener);\n this.player.off(PlayerEvents.PAUSE, this.pauseListener);\n this.player.off(PlayerEvents.RATE_CHANGE, this.rateChangeListener);\n this.player.off(PlayerEvents.SEEKING, this.seekingListener);\n this.player.off(PlayerEvents.TIME_UPDATE, this.timeUpdateListener);\n this.player.off(PlayerEvents.WAITING, this.waitingListener);\n }\n\n /**\n * Sent before a new media is loading.\n * - Destroy all properties.\n * - Send a notify stop if the media is not ended and new media is about to be loaded.\n */\n emptied() {\n if (!this.player.ended()) {\n this.notify('stop');\n }\n }\n\n /**\n * Sent when playback completes.\n *\n * @see https://docs.videojs.com/player#event:ended\n */\n ended() {\n this.notify('eof');\n\n this.mediaSession = 0;\n\n this.clearTimers();\n }\n\n /**\n * Flush the queued events when tc event script is loaded.\n */\n flush() {\n if (this.isTrackerDisabled()) return;\n\n if (this.pendingTagCommanderReload && window.tC) {\n window.tC.container.reload();\n this.pendingTagCommanderReload = false;\n }\n\n if (window.tc_events_11 && this.pendingQueue.length > 0) {\n this.pendingQueue.forEach((notification) => {\n window.tc_events_11(\n this.player.el(),\n notification.action,\n notification.labels\n );\n });\n\n this.pendingQueue = [];\n }\n }\n\n /**\n * Get the language of the current audio track.\n *\n * @returns {String} empty string or uppercase language.\n */\n getCurrentAudioTrack() {\n const currentTrack = Array.from(this.player.audioTracks()).find(\n (track) => track.enabled\n );\n let language = 'und';\n\n if (currentTrack && !!currentTrack.language) {\n // eslint-disable-next-line prefer-destructuring\n language = currentTrack.language;\n }\n\n return currentTrack ? language.toUpperCase() : '';\n }\n\n /**\n * Get the language of the current text track.\n *\n * @returns {String} empty string or uppercase language.\n */\n getCurrentTextTrack() {\n const currentTrack = this.player.textTrack();\n let language = 'und';\n\n if (currentTrack && !!currentTrack.language) {\n // eslint-disable-next-line prefer-destructuring\n language = currentTrack.language;\n }\n\n return currentTrack ? language.toUpperCase() : '';\n }\n\n /**\n * Get the position inside the dvr window where the 0 represents the live edge\n *\n * @return {Number} 0 or the position in milliseconds\n */\n getDvrWindowPosition() {\n const { liveTracker } = this.player;\n const ct = (this.currentTime() - liveTracker.seekableStart()) | 0;\n const position = liveTracker.liveWindow() - ct;\n\n return position < 0 || position === Infinity ? 0 : position * 1000;\n }\n\n /**\n * Get the size of the live DVR window in milliseconds.\n *\n * @return {Number} DVR window size in milliseconds\n */\n getDvrWindowSize() {\n const isInfinity = this.player.liveTracker.liveWindow() === Infinity;\n const windowSize = this.player.liveTracker.liveWindow() * 1000;\n\n return isInfinity ? 0 : windowSize;\n }\n\n /**\n * Get the elapsed playback time in seconds.\n *\n * @returns {Number} elapsed time in seconds\n */\n getElapsedPlaybackTime() {\n if (this.startPlaybackSession) {\n return this.getElapsedPlayingTime();\n }\n\n return this.elapsedPlaybackTime;\n }\n\n /**\n * Get the elapsed playing time in seconds.\n *\n * @returns {Number} elapsed time in seconds\n */\n getElapsedPlayingTime() {\n const playingSession = (SRGAnalytics.now() - this.startPlaybackSession) | 0;\n\n return this.elapsedPlaybackTime + playingSession;\n }\n\n /**\n * Set all event labels to be sent to TagCommander. The event labels are updated whenever a new event occurs.\n *\n * @param {String} eventName init | play | stop | pos | pause | seek | uptime | eof\n *\n * @returns {Object} JSON to be sent to TagCommander\n */\n getEventLabels(eventName) {\n const labels = {\n event_id: eventName,\n event_timestamp: SRGAnalytics.now(),\n media_dvr_window_length: 0,\n media_dvr_window_offset: 0,\n media_is_dvr: false,\n media_is_live: false,\n media_mute: this.player.muted() ? '1' : '0',\n media_playback_rate: this.player.playbackRate(),\n media_position: this.currentTime(),\n media_quality: this.srcMediaData.mediaData.quality,\n // TODO use media_is_dvr, media_is_live to define peach media_stream_type\n media_subtitles_on: this.isTextTrackEnabled(),\n media_volume: (this.player.volume() * 100).toFixed(0),\n navigation_environment: this.environment,\n };\n\n if (this.isAudioTrackEnabled()) {\n labels.media_audio_track = this.getCurrentAudioTrack();\n }\n\n if (this.isTextTrackEnabled()) {\n labels.media_subtitle_selection = this.getCurrentTextTrack();\n }\n\n // DVR or Live related labels\n if (!this.isMediaOnDemand()) {\n labels.media_is_live = true;\n labels.media_position = this.getElapsedPlaybackTime();\n }\n\n // DVR related labels\n if (this.isMediaDvr()) {\n labels.media_dvr_window_offset = this.getDvrWindowPosition() | 0;\n labels.media_dvr_window_length = this.getDvrWindowSize() | 0;\n\n labels.media_is_dvr = true;\n\n labels.media_timeshift = [PlayerEvents.PLAY, PlayerEvents.PAUSE].includes(\n eventName\n )\n ? this.timeShifted()\n : 0;\n }\n\n return labels;\n }\n\n /**\n * Set all internal labels to be sent to TagCommander. Internal labels are assigned once at initialisation time.\n */\n getInternalLabels() {\n const data = {\n media_bu_distributer: this.srcMediaData.mediaData.vendor,\n media_chromecast_selected: Boolean(this.player.tech(true).isCasting),\n media_embedding_url: document.referrer,\n media_player_display: 'default', // TODO implement if it still relevant\n media_player_name: 'pillarbox-web', // TODO add a property playerName in the constructor with a default value ?\n media_player_version: this.playerVersion,\n media_url: this.srcMediaData.src,\n };\n const analyticsMetadata =\n this.srcMediaData.mediaData.analyticsMetadata || {};\n\n window.tc_vars = Object.assign({}, window.tc_vars, data, analyticsMetadata);\n }\n\n /**\n * Heart beat, current position of a AoD/VoD (every 30s).\n *\n * @description The action pos should be sent regularly every 30 seconds.\n * It is used for tracking the viewed chapters of a video and the last position of the video, in case the user ends the video by closing the browser tab/window.\n *\n * - pos should be sent when the media player is in \"play mode\".\n * - once the video is paused or stopped, the timer for sending these actions must be stopped.\n *\n * @see https://confluence.srg.beecollaboration.com/display/INTFORSCHUNG/standard+streaming+events%3A+sequence+of+events+for+media+player+actions#standardstreamingevents:sequenceofeventsformediaplayeractions-mandatoryplayerevents\n */\n heartBeat() {\n this.heartBeatIntervalId = setInterval(() => {\n // Send only when playing\n if (!this.player.paused()) {\n this.notify('pos');\n }\n }, 30000);\n }\n\n /**\n * Initialize callbacks used to send analytics data to TagCommander.\n *\n * __Used events__\n * - beforeunload\n * - emptied\n * - ended\n * - loadstart\n * - loadeddata\n * - play\n * - pause\n * - ratechange\n * - seeking\n * - timeupdate\n * - waiting\n */\n initCallbacks() {\n this.beforeunloadListener = this.beforeunload.bind(this);\n this.emptiedListener = this.emptied.bind(this);\n this.endedListener = this.ended.bind(this);\n this.loadstartListener = this.loadstart.bind(this);\n this.loadeddataListener = this.loadeddata.bind(this);\n this.playListener = this.play.bind(this);\n this.pauseListener = this.pause.bind(this);\n this.rateChangeListener = this.rateChange.bind(this);\n this.seekingListener = this.seeking.bind(this);\n this.timeUpdateListener = this.timeUpdate.bind(this);\n this.waitingListener = this.waiting.bind(this);\n }\n\n /**\n * Initialize all listeners used to send analytics data to TagCommander.\n *\n * __Used events__\n * - beforeunload\n * - dispose\n * - emptied\n * - ended\n * - loadstart\n * - loadeddata\n * - play\n * - pause\n * - timeupdate\n * - waiting\n */\n initListeners() {\n this.initCallbacks();\n\n window.addEventListener('beforeunload', this.beforeunloadListener);\n\n this.player.on(PlayerEvents.EMPTIED, this.emptiedListener);\n this.player.on(PlayerEvents.ENDED, this.endedListener);\n this.player.on(PlayerEvents.LOAD_START, this.loadstartListener);\n this.player.on(PlayerEvents.LOADED_DATA, this.loadeddataListener);\n this.player.on(PlayerEvents.PLAYING, this.playListener);\n this.player.on(PlayerEvents.PAUSE, this.pauseListener);\n this.player.on(PlayerEvents.RATE_CHANGE, this.rateChangeListener);\n this.player.on(PlayerEvents.SEEKING, this.seekingListener);\n this.player.on(PlayerEvents.TIME_UPDATE, this.timeUpdateListener);\n this.player.on(PlayerEvents.WAITING, this.waitingListener);\n this.player.one('dispose', this.dispose.bind(this));\n }\n\n /**\n * Initialize TagCommander script dynamically and add it to the DOM\n */\n initScript() {\n const scriptId = 'tc_script__11';\n\n if (!document.querySelector(`#${scriptId}`)) {\n const script = document.createElement('script');\n const src = this.tagCommanderScriptURL;\n\n script.defer = true;\n script.id = scriptId;\n script.src = src;\n script.type = 'text/javascript';\n\n script.onload = (_e) => {\n this.flush();\n };\n\n document.body.appendChild(script);\n }\n }\n\n /**\n * Check if the audio track is enabled.\n *\n * @returns {Boolean} __true__ if enabled __false__ otherwise.\n */\n isAudioTrackEnabled() {\n return !!this.getCurrentAudioTrack();\n }\n\n /**\n * Check if the media is a live with DVR.\n *\n * @returns {Boolean} __true__ if it DVR __false__ otherwise.\n */\n isMediaDvr() {\n const { trackingThreshold } = this.player.liveTracker.options();\n\n return (\n !this.isMediaOnDemand() &&\n trackingThreshold < this.player.liveTracker.liveWindow()\n );\n }\n\n /**\n * Check if the media is a live.\n *\n * @returns {Boolean} __true__ if live __false__ otherwise.\n */\n isMediaLive() {\n const { trackingThreshold } = this.player.liveTracker.options();\n\n return (\n !this.isMediaOnDemand() &&\n trackingThreshold > this.player.liveTracker.liveWindow()\n );\n }\n\n /**\n * Check if the media is an on demand.\n *\n * @returns {Boolean} __true__ if on demand __false__ otherwise.\n */\n isMediaOnDemand() {\n return Number.isFinite(this.player.duration());\n }\n\n /**\n * Check if the text track is enabled.\n *\n * @returns {Boolean} __true__ if enabled __false__ otherwise.\n */\n isTextTrackEnabled() {\n return !!this.getCurrentTextTrack();\n }\n\n /**\n * Check if the tracker is disabled.\n *\n * @returns {Boolean} __true__ if disabled __false__ otherwise.\n */\n isTrackerDisabled() {\n if (!this.srcMediaData || !this.srcMediaData.mediaData)\n return true;\n\n if (!Array.isArray(this.srcMediaData.disableTrackers)) {\n return Boolean(this.srcMediaData.disableTrackers);\n }\n\n return Boolean(\n this.srcMediaData.disableTrackers.find(\n (tracker) => tracker.toLowerCase() === SRGAnalytics.name.toLowerCase()\n )\n );\n }\n\n /**\n * Sent when loading of the media begins.\n *\n * @see https://docs.videojs.com/player#event:loadstart\n */\n loadstart() {\n this.destroy();\n this.updateSrcMediaData(this.player.currentSource());\n\n if (this.isTrackerDisabled()) return;\n\n this.getInternalLabels();\n // Set ComScore labels\n this.reloadTagCommanderContainer();\n\n this.notify('buffer_start');\n this.hasStarted = false;\n }\n\n /**\n * The first frame of the media has finished loading.\n *\n * @see https://docs.videojs.com/player#event:loadeddata\n */\n loadeddata() {\n this.notify('init');\n this.initialized = true;\n\n this.notify('buffer_stop');\n }\n\n /**\n * Event logger that prints the current event, event labels and internal labels in the browser's console.\n *\n * @param {String} eventName init | play | stop | pos | pause | seek | uptime | eof\n * @param {Object} eventMetadata event metadata object\n * @param {String} severity log | warn | error\n */\n log(eventName, eventMetadata, severity = 'log') {\n if (this.debug()) {\n // eslint-disable-next-line\n console[severity](\n `SRGAnalytics:${eventName}`,\n eventMetadata,\n window.tc_vars\n );\n }\n }\n\n /**\n * Notify TagCommander all event and internal labels. If tc script is not available it queues all pending events.\n *\n * @param {String} eventName init | play | stop | pos | pause | seek | uptime | eof\n */\n notify(eventName, eventMetadata) {\n if (this.isTrackerDisabled()) return;\n\n try {\n this.flush();\n } catch (error) {\n this.log(eventName, error, 'error');\n }\n\n const labels = Object.assign(\n {},\n this.getEventLabels(eventName),\n eventMetadata\n );\n\n this.log(eventName, labels);\n\n try {\n if (window.tc_events_11) {\n window.tc_events_11(this.player.el(), eventName, labels);\n } else {\n this.pendingQueue.push({\n action: eventName,\n labels,\n });\n }\n } catch (error) {\n this.log(eventName, error, 'error');\n }\n }\n\n /**\n * Return the current timestamp in seconds.\n *\n * @returns {Number} Timestamp in seconds\n */\n static now() {\n return (Date.now() / 1000).toFixed(0);\n }\n\n /**\n * Sent when the playback state is no longer paused, as a result of the play method, or the autoplay attribute.\n *\n * @see https://docs.videojs.com/player#event:play\n */\n play() {\n if (!this.hasStarted) this.hasStarted = true;\n\n if (!this.startPlaybackSession && !this.isMediaOnDemand()) {\n this.startPlaybackSession = SRGAnalytics.now();\n }\n\n if (this.mediaSession === 0) {\n this.mediaSession = SRGAnalytics.now();\n\n this.heartBeat();\n this.uptime();\n }\n\n this.timeUpdate();\n this.notify('play');\n\n if (this.isSeeking) this.isSeeking = false;\n }\n\n /**\n * Sent when the playback state is changed to paused (paused property is true).\n * Pause event is sent if :\n * - The player is not scrubbing\n * - The stream is not a live only\n * - The current time is strictly inferior to the duration\n *\n * @see https://docs.videojs.com/player#event:pause\n */\n pause() {\n if (!this.isMediaOnDemand()) {\n this.elapsedPlaybackTime = this.getElapsedPlayingTime();\n this.startPlaybackSession = 0;\n }\n\n if (\n !this.player.seeking() &&\n !this.isMediaLive() &&\n this.player.currentTime() < this.player.duration()\n ) {\n this.notify('pause');\n\n return;\n }\n\n if (this.hasStarted && !this.isSeeking) {\n this.notify('seek');\n this.isSeeking = true;\n }\n }\n\n /**\n * Sent to ComScore when the playback rate changes.\n *\n * @see https://github.com/SRGSSR/srgletterbox-web/issues/761\n * @see https://jira.srg.beecollaboration.com/browse/ADI-256\n */\n rateChange() {\n this.notify('change_playback_rate');\n }\n\n /**\n * Reload the tagCommander container and set all ComScore labels\n */\n reloadTagCommanderContainer() {\n if (window.tC) {\n window.tC.container.reload();\n this.pendingTagCommanderReload = false;\n } else {\n this.pendingTagCommanderReload = true;\n }\n }\n\n /**\n * Sent when the current time is modified by the player's currentTime API.\n *\n * @see https://docs.videojs.com/player#event:seeking\n */\n seeking() {\n if (this.hasStarted && !this.player.paused() && !this.isSeeking) {\n this.notify('seek');\n this.isSeeking = true;\n }\n }\n\n /**\n * Track current time updates delayed by a tick.\n *\n * @see https://docs.videojs.com/player#event:timeupdate\n */\n timeUpdate() {\n if (!this.player.paused()) {\n this.trackedCurrentTime = this.player.currentTime();\n }\n }\n\n /**\n * Gets the number of seconds that separate from the live edge that is represented by 0.\n *\n * @returns {String}\n */\n timeShifted() {\n const isAtLiveEdge = this.player.liveTracker.atLiveEdge();\n const liveCurrentTime = this.player.liveTracker.liveCurrentTime();\n const currentTime = this.player.currentTime();\n const timeShifted = isAtLiveEdge\n ? 0\n : (liveCurrentTime - currentTime).toFixed(0);\n\n return timeShifted;\n }\n\n /**\n * Update the src media data.\n */\n updateSrcMediaData(srcMediaData) {\n this.srcMediaData = srcMediaData;\n }\n\n /**\n * Calculate the uptime when playing a live stream with or without DVR\n *\n * __Rules__:\n * - Send the first uptime after 30 seconds\n * - Send uptime each 60 seconds after the 30 seconds\n * - Uptime is sent only when playing\n */\n uptime() {\n const notifyUptime = () => {\n if (!this.player.paused() && !this.isMediaOnDemand()) {\n this.notify('uptime');\n }\n };\n\n // Send the first uptime after 30 seconds\n this.uptimeTimeoutId = setTimeout(() => {\n // Send only when playing\n notifyUptime();\n\n // Initialize the uptime interval after 30 seconds\n this.uptimeIntervalId = setInterval(() => {\n // Send only when playing\n notifyUptime();\n }, 60000);\n }, 30000);\n }\n\n /**\n * __ComScore__:\n * It's expected notifyBufferStart() to be called when the player starts buffering\n * and a call to notifyBufferStop() when content resumes after buffering.\n *\n * @see Item 2: https://jira.srg.beecollaboration.com/browse/PLAY-2628\n *\n * After the issue PLAYRTS-321\n * @see Fix: https://jira.srg.beecollaboration.com/browse/PLAYRTS-321?focusedCommentId=201023&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-201023\n * @see Fix: https://jira.srg.beecollaboration.com/browse/PLAYRTS-3065\n */\n waiting() {\n if (!this.initialized || this.isWaiting) {\n return;\n }\n\n const bufferStop = () => {\n this.isWaiting = false;\n this.notify('buffer_stop');\n };\n\n this.isWaiting = true;\n\n this.notify('buffer_start');\n\n // As Safari is not consistent with its playing event, it is better to use the timeupdate event.\n if (Pillarbox.browser.IS_ANY_SAFARI) {\n this.player.one(PlayerEvents.TIME_UPDATE, bufferStop);\n } else {\n // As Chromium-based browsers are not consistent with their timeupdate event, it is better to use the playing event.\n // Firefox is consistent with its playing event.\n this.player.one(PlayerEvents.PLAYING, bufferStop);\n }\n }\n}\n\nexport default SRGAnalytics;\n","/**\n * Represents the composition of media content.\n *\n * @class MediaComposition\n * @property {string} chapterUrn URN (Uniform Resource Name) of the associated chapter.\n * @property {string} segmentUrn URN of the associated segment.\n * @property {Episode} episode Associated episode.\n * @property {Show} show Associated show.\n * @property {Channel} channel Associated channel.\n * @property {Array.<Chapter>} chapterList List of associated chapters.\n * @property {Array.<Topic>} topicList List of associated topics.\n * @property {Object.<String, String>} analyticsData Analytics data associated with the media composition.\n * @property {Object.<String, String>} analyticsMetadata Metadata associated with analytics for the media composition.\n */\nclass MediaComposition {\n /**\n * Find a chapter by its URN.\n *\n * @param {String} urn\n *\n * @returns {Chapter} chapter\n */\n findChapterByUrn(urn) {\n if (this.chapterList) {\n const [chapter] = this.chapterList.filter(\n (element) => element.urn === urn\n );\n\n return chapter;\n }\n\n return undefined;\n }\n\n /**\n * Return a segment from main chapter following segmentUrn in mediaComposition.\n *\n * @returns {Segment|undefined} main segment\n */\n findMainSegment() {\n if (!this.segmentUrn) {\n return undefined;\n }\n\n const segmentList = this.getMainSegments();\n const [segment] = segmentList.filter(\n (element) => element.urn === this.segmentUrn\n );\n\n return segment;\n }\n\n /**\n * Find resource list by URN.\n *\n * @param {String} urn\n * @returns {Array.<Resource>|undefined} of resources\n */\n findResourceListByUrn(urn) {\n const chapterByUrn = this.findChapterByUrn(urn);\n\n if (chapterByUrn) {\n return chapterByUrn.resourceList || [];\n }\n\n return undefined;\n }\n\n /**\n * A list of chapters.\n *\n * @returns {Array.<Chapter>} of chapters\n */\n getChapters() {\n const AUDIO = 'AUDIO';\n\n if (this.getMainChapter().mediaType === AUDIO) return [];\n\n return this.chapterList.filter(({ mediaType }) => mediaType !== AUDIO);\n }\n\n /**\n * Filter external text tracks that are already available internally.\n *\n * __Rules:__\n * 1. TTML format is filtered\n *\n * 2. If both are empty that means only internal text tracks will be displayed\n * to the user as they are automatically loaded by the player.\n *\n * 3. If subtitleInformationList is missing from the MediaComposition and subtitleList\n * is available but the media contains internal text tracks that are also available internally.\n * It will result on a duplication client side.\n *\n * 4. If subtitleList and subtitleInformationList a merge between both will be operated,\n * removing the external text tracks already available internally.\n *\n *\n * @returns {Array.<Subtitle>} external text tracks\n */\n getFilteredExternalSubtitles() {\n const { subtitleList } = this.getMainChapter();\n const [{ subtitleInformationList } = {}] = this.getResourceList().filter(\n ({ subtitleInformationList }) => subtitleInformationList\n );\n const onlyHasExternalSubtitles = subtitleList && !subtitleInformationList;\n\n if (!subtitleList) {\n return [];\n }\n\n // TTML format is not supported\n const subtitles = subtitleList.filter(\n (subtitle) => subtitle.format !== 'TTML'\n );\n\n if (onlyHasExternalSubtitles) {\n return subtitles;\n }\n\n return subtitles.filter((subtitle) => {\n const addSubtitle = !subtitleInformationList.find(\n (subtitleInformation) =>\n subtitleInformation.locale === subtitle.locale &&\n subtitle.type === subtitleInformation.type\n );\n\n return addSubtitle;\n });\n }\n\n /**\n * Block reason for main chapter. This also uses current date for STARTDATE.\n *\n * @see BlockReason\n *\n * @returns {string | undefined} undefined if main chapter is not blocked\n */\n getMainBlockReason() {\n const mainChapter = this.getMainChapter();\n\n if (!mainChapter) {\n return undefined;\n }\n\n let { blockReason } = mainChapter;\n\n if (!blockReason && new Date() < this.getMainValidFromDate()) {\n blockReason = 'STARTDATE';\n }\n\n return blockReason;\n }\n\n /**\n * Get blocked segments from the main chapter.\n *\n * @returns {Array.<Segment>} of blocked segments\n */\n getMainBlockedSegments() {\n return this.getMainSegments().filter(segment => segment.blockReason);\n }\n\n /**\n * Get the mediaComposition's main chapter.\n *\n * @returns {Chapter}\n */\n getMainChapter() {\n if (!this.mainChapter) {\n this.mainChapter = this.findChapterByUrn(this.chapterUrn);\n }\n\n if (!this.mainChapter && this.chapterList && this.chapterList.length > 0) {\n [this.mainChapter] = this.chapterList;\n }\n\n return this.mainChapter;\n }\n\n /**\n * Get the main chapter's image URL decorated with default width and format.\n *\n * @returns {String|undefined} image URL\n */\n getMainChapterImageUrl() {\n const mainChapter = this.getMainChapter();\n\n if (!mainChapter || !mainChapter.imageUrl) {\n return undefined;\n }\n\n return mainChapter.imageUrl;\n }\n\n /**\n * Get main resources.\n *\n * @returns {Array.<MainResource>} array of sources.\n */\n // eslint-disable-next-line max-lines-per-function\n getMainResources() {\n const resourceList = this.getResourceList();\n\n if (!resourceList || !resourceList.length) {\n return undefined;\n }\n\n return resourceList.map((resource) => ({\n analyticsData: this.getMergedAnalyticsData(resource.analyticsData),\n analyticsMetadata: this.getMergedAnalyticsMetadata(\n resource.analyticsMetadata\n ),\n blockReason: this.getMainChapter().blockReason,\n blockedSegments: this.getMainBlockedSegments(),\n imageUrl: this.getMainChapterImageUrl(),\n chapters: this.getChapters(),\n drmList: resource.drmList,\n dvr: resource.dvr,\n eventData: this.getMainChapter().eventData,\n id: this.getMainChapter().id,\n imageCopyright: this.getMainChapter().imageCopyright,\n intervals: this.getMainTimeIntervals(),\n live: resource.live,\n mediaType: this.getMainChapter().mediaType,\n mimeType: resource.mimeType,\n presentation: resource.presentation,\n quality: resource.quality,\n streaming: resource.streaming,\n streamOffset: resource.streamOffset,\n subtitles: this.getFilteredExternalSubtitles(),\n title: this.getMainChapter().title,\n tokenType: resource.tokenType,\n url: resource.url,\n urn: this.chapterUrn,\n vendor: this.getMainChapter().vendor,\n }));\n }\n\n /**\n * Get segments of the main chapter ordered by markIn.\n *\n * @returns {Array.<Segment>} of segments\n */\n getMainSegments() {\n const mainChapter = this.getMainChapter();\n\n if (!this.mainSegments && mainChapter && mainChapter.segmentList) {\n this.mainSegments = mainChapter.segmentList;\n }\n\n return this.mainSegments || [];\n }\n\n /**\n * Retrieves an array of time intervals associated with the main chapter.\n *\n * @returns {Array.<TimeInterval>} An array of time intervals.\n */\n getMainTimeIntervals() {\n const {\n timeIntervalList = []\n } = this.getMainChapter() || {};\n\n return timeIntervalList;\n }\n\n /**\n * Compute a date from which this content is valid. Always return a date object.\n *\n * @returns {Date} date specified in media composition or EPOCH when no date present.\n */\n getMainValidFromDate() {\n const mainChapter = this.getMainChapter();\n\n if (!mainChapter) {\n return new Date(0);\n }\n\n const { validFrom } = mainChapter;\n\n if (validFrom) {\n return new Date(validFrom);\n }\n }\n\n /**\n * Get merged analytics data.\n *\n * @param {Object.<string, string>} analyticsData\n * @returns {Object.<string, string>} Merged analytics data.\n */\n getMergedAnalyticsData(analyticsData) {\n return {\n ...this.analyticsData,\n ...this.getMainChapter().analyticsData,\n ...analyticsData,\n };\n }\n\n /**\n * Get merged analytics metadata.\n *\n * @param {Object.<string, string>} analyticsMetadata\n * @returns {Object.<string, string>} Merged analytics metadata.\n */\n getMergedAnalyticsMetadata(analyticsMetadata) {\n return {\n ...this.analyticsMetadata,\n ...this.getMainChapter().analyticsMetadata,\n ...analyticsMetadata,\n };\n }\n\n /**\n * Get the chapter's resource list\n * @returns {Array.<Resource>} of resources\n */\n getResourceList() {\n const { resourceList } = this.getMainChapter();\n\n return resourceList || [];\n }\n}\n\nexport default MediaComposition;\n\n\n/**\n * @typedef {import('./typedef').Channel} Channel\n * @typedef {import('./typedef').Chapter} Chapter\n * @typedef {import('./typedef').Episode} Episode\n * @typedef {import('./typedef').Resource} Resource\n * @typedef {import('./typedef').Segment} Segment\n * @typedef {import('./typedef').Show} Show\n * @typedef {import('./typedef').Subtitle} Subtitle\n * @typedef {import('./typedef').TimeInterval} TimeInterval\n * @typedef {import('./typedef').Topic} Topic\n * @typedef {import('./typedef').MainResource} MainResource\n */\n","import Pillarbox from '../pillarbox.js';\nimport * as vjsLang from 'video.js/dist/lang/de.json';\nimport * as pillarboxLang from './de.json';\n\nPillarbox.addLanguage('de', {\n ...vjsLang,\n ...pillarboxLang,\n});\n","import Pillarbox from '../pillarbox.js';\nimport * as vjsLang from 'video.js/dist/lang/en.json';\nimport * as pillarboxLang from './en.json';\n\nPillarbox.addLanguage('en', {\n ...vjsLang,\n ...pillarboxLang,\n});\n","import Pillarbox from '../pillarbox.js';\nimport * as vjsLang from 'video.js/dist/lang/fr.json';\nimport * as pillarboxLang from './fr.json';\n\nPillarbox.addLanguage('fr', {\n ...vjsLang,\n ...pillarboxLang,\n});\n","import Pillarbox from '../pillarbox.js';\nimport * as vjsLang from 'video.js/dist/lang/it.json';\nimport * as pillarboxLang from './it.json';\n\nPillarbox.addLanguage('it', {\n ...vjsLang,\n ...pillarboxLang,\n});\n","import Pillarbox from '../pillarbox.js';\nimport * as pillarboxLang from './rm.json';\n\nPillarbox.addLanguage('rm', {\n ...pillarboxLang,\n});\n","import Pillarbox from '../../src/pillarbox.js';\nimport DataProvider from '../dataProvider/services/DataProvider.js';\nimport Image from '../utils/Image.js';\nimport Drm from '../utils/Drm.js';\nimport AkamaiTokenService from '../utils/AkamaiTokenService.js';\nimport SRGAnalytics from '../analytics/SRGAnalytics.js';\nimport MediaComposition from '../dataProvider/model/MediaComposition.js';\n\n// Translations\nimport '../lang/de.js';\nimport '../lang/en.js';\nimport '../lang/fr.js';\nimport '../lang/it.js';\nimport '../lang/rm.js';\n\n/**\n * @class SrgSsr\n */\nclass SrgSsr {\n /**\n * Adds blocked segments to the player.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {Array<import('../dataProvider/model/typedef').Segment>} [segments=[]]\n */\n static addBlockedSegments(player, segments = []) {\n const trackId = 'srgssr-blocked-segments';\n const removeTrack = player.textTracks().getTrackById(trackId);\n\n if (removeTrack) {\n player.textTracks().removeTrack(removeTrack);\n }\n\n if (!Array.isArray(segments) || !segments.length) return;\n\n const blockedSegments = segments.filter(segment => segment.blockReason);\n\n if (!blockedSegments.length) return;\n\n SrgSsr.createTextTrack(player, trackId).then(segmentTrack => {\n blockedSegments.forEach(segment => {\n SrgSsr.addTextTrackCue(segmentTrack, segment);\n });\n\n player.textTracks().addTrack(segmentTrack);\n });\n }\n\n /**\n * Adds remote text tracks from an array of subtitles.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {Array<import('../dataProvider/model/typedef').Subtitle>} [subtitles=[]]\n */\n static addRemoteTextTracks(player, subtitles = []) {\n if (!Array.isArray(subtitles)) return;\n\n subtitles.forEach(({\n type,\n language: label,\n locale: language,\n url: src\n }) => {\n player.addRemoteTextTrack({\n kind: type === 'SDH' ? 'captions' : 'subtitles',\n label,\n language,\n src\n });\n });\n }\n\n /**\n * Add a new cue to a text track with the given data.\n *\n * @param {TextTrack} textTrack\n * @param {\n * import('../dataProvider/model/typedef').Segment |\n * import('../dataProvider/model/typedef').Chapter |\n * import('../dataProvider/model/typedef').TimeInterval\n * } data SRG SSR's cue-like representation\n */\n static addTextTrackCue(textTrack, data) {\n const startTime = (Number.isFinite(data.markIn)\n ? data.markIn : data.fullLengthMarkIn) / 1_000;\n const endTime = (Number.isFinite(data.markOut)\n ? data.markOut : data.fullLengthMarkOut) / 1_000;\n\n textTrack.addCue(new VTTCue(\n startTime,\n endTime,\n JSON.stringify(data)\n ));\n }\n\n /**\n * Add multiple text tracks to the player.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {import('./typedef').ComposedSrcMediaData} srcMediaObj\n */\n static addTextTracks(player, { mediaData }) {\n SrgSsr.addRemoteTextTracks(player, mediaData.subtitles);\n SrgSsr.addChapters(player, mediaData.urn, mediaData.chapters);\n SrgSsr.addBlockedSegments(player, mediaData.blockedSegments);\n SrgSsr.addIntervals(player, mediaData.intervals);\n }\n\n /**\n * Adds chapters to the player.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {string} chapterUrn The URN of the main chapter.\n * @param {Array.<import('../dataProvider/model/typedef').Chapter>} [chapters=[]]\n */\n static addChapters(player, chapterUrn, chapters = []) {\n const trackId = 'srgssr-chapters';\n const removeTrack = player.textTracks().getTrackById(trackId);\n\n if (removeTrack) {\n player.textTracks().removeTrack(removeTrack);\n }\n\n if (!Array.isArray(chapters) || !chapters.length) return;\n\n SrgSsr.createTextTrack(player, trackId).then(chapterTrack => {\n chapters.forEach(chapter => {\n if (chapterUrn !== chapter.fullLengthUrn) return;\n\n SrgSsr.addTextTrackCue(chapterTrack, chapter);\n });\n\n player.textTracks().addTrack(chapterTrack);\n });\n }\n\n /**\n * Adds intervals to the player.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {Array.<import('../dataProvider/model/typedef').TimeInterval>} [intervals=[]]\n */\n static addIntervals(player, intervals = []) {\n const trackId = 'srgssr-intervals';\n const removeTrack = player.textTracks().getTrackById(trackId);\n\n if (removeTrack) {\n player.textTracks().removeTrack(removeTrack);\n }\n\n if (!Array.isArray(intervals) || !intervals.length) return;\n\n SrgSsr.createTextTrack(player, trackId).then(intervalTrack => {\n intervals.forEach(interval => {\n SrgSsr.addTextTrackCue(intervalTrack, interval);\n });\n\n player.textTracks().addTrack(intervalTrack);\n });\n }\n\n /**\n * Set a blocking reason according to the block reason returned\n * by mediaData.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {import('./typedef').ComposedSrcMediaData} srcMediaObj\n *\n * @returns {undefined|Boolean}\n */\n static blockingReason(player, srcMediaObj) {\n if (!srcMediaObj.mediaData.blockReason) return;\n\n const message = player.localize(srcMediaObj.mediaData.blockReason);\n\n SrgSsr.error(player, {\n code: MediaError.MEDIA_ERR_ABORTED,\n message,\n metadata: {\n errorType: srcMediaObj.mediaData.blockReason,\n src: srcMediaObj\n },\n });\n\n return true;\n }\n\n /**\n * Add the Akamai token to all resources\n * if at least one of them has tokenType\n * set to Akamai.\n *\n * @param {Array.<import('./typedef').MainResourceWithKeySystems>} resources\n *\n * @returns {Promise<Array.<import('./typedef').MainResourceWithKeySystems>>}\n */\n static async composeAkamaiResources(resources = []) {\n if (!AkamaiTokenService.hasToken(resources)) {\n return Promise.resolve(resources);\n }\n\n // TODO allow to modify the Akamai URL\n return AkamaiTokenService.tokenizeSources(resources);\n }\n\n /**\n * Add the keySystems property to all resources\n * if at least one of them has DRM.\n *\n * @param {Array.<import('../dataProvider/model/typedef').MainResource>} resources\n *\n * @returns {Array.<import('./typedef').MainResourceWithKeySystems>}\n */\n static composeKeySystemsResources(resources = []) {\n if (!Drm.hasDrm(resources)) resources;\n\n return resources.map((resource) => ({\n ...resource,\n ...Drm.buildKeySystems(resource.drmList),\n }));\n }\n\n /**\n * Get the main resources from a mediaComposition.\n * May add an Akamai token or key systems if required by the resource.\n *\n * @param {MediaComposition} mediaComposition\n *\n * @returns {Promise<Array.<import('./typedef').MainResourceWithKeySystems>>}\n */\n static composeMainResources(mediaComposition) {\n return SrgSsr.composeAkamaiResources(\n SrgSsr.composeKeySystemsResources(\n SrgSsr.filterIncompatibleResources(mediaComposition.getMainResources())\n )\n );\n }\n\n /**\n * Compose source options with media data.\n * MediaData properties from source options overwrite mediaData from IL.\n *\n * @param {any} srcObj\n * @param {any} srcObj.mediaData overrides or adds metadata to the composed mediaData.\n * @param {boolean} srcObj.disableTrackers\n * @param {import('./typedef').MainResourceWithKeySystems} resource\n *\n * @returns {import('./typedef').ComposedSrcMediaData}\n */\n static composeSrcMediaData(\n { mediaData: srcMediaData, disableTrackers },\n resource\n ) {\n const {\n url,\n mimeType,\n keySystems,\n ...mediaData\n } = Pillarbox.obj.merge(resource, srcMediaData);\n\n return {\n src: url,\n type: mimeType,\n keySystems,\n disableTrackers,\n mediaData,\n };\n }\n\n /**\n * Create a new metadata text track.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {String} trackId Text track unique ID\n *\n * @returns {Promise<TextTrack>}\n */\n static createTextTrack(player, trackId) {\n // See https://github.com/videojs/video.js/issues/8519\n return new Promise((resolve) => {\n setTimeout(() => {\n resolve(new Pillarbox.TextTrack({\n id: trackId,\n kind: 'metadata',\n label: trackId,\n tech: player.tech(true),\n }));\n }, 100);\n });\n }\n\n /**\n * Proxy SRG SSR chapters and intervals cuechange events at player level.\n *\n * @param {import('video.js/dist/types/player').default} player\n */\n static cuechangeEventProxy(player) {\n player.textTracks().on('addtrack', ({ track }) => {\n if (!['srgssr-chapters', 'srgssr-intervals'].includes(track.id)) return;\n\n track.on('cuechange', () => {\n const [cue] = Array.from(track.activeCues);\n const type = track.id.includes('srgssr-chapters') ? 'srgssr/chapter' : 'srgssr/interval';\n\n player.trigger({ type, data: cue });\n });\n });\n }\n\n /**\n * SRG SSR data provider singleton.\n *\n * @param {import('video.js/dist/types/player').default} player\n *\n * @returns {DataProvider}\n */\n static dataProvider(player) {\n if (!player.options().srgOptions.dataProvider) {\n const {\n dataProviderHost,\n dataProviderUrlHandler\n } = player.options().srgOptions;\n const dataProvider = new DataProvider(dataProviderHost);\n const requestHandler = dataProvider\n .handleRequest(dataProviderUrlHandler);\n\n player.options({\n srgOptions: {\n dataProvider: requestHandler,\n },\n });\n }\n\n return player.options().srgOptions.dataProvider;\n }\n\n /**\n * Set an error if something goes wrong with the data provider.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {Object} error\n *\n * @returns {undefined|true}\n */\n static dataProviderError(player, error) {\n if (!error) return;\n\n const statusText = error.statusText ? error.statusText : error.message;\n\n SrgSsr.error(player, {\n code: 0,\n message: player.localize('UNKNOWN'),\n metadata: {\n errorType: 'UNKNOWN',\n urn: player.src(),\n status: error.status,\n statusText,\n url: error.url,\n },\n });\n\n return true;\n }\n\n /**\n * Set player error.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {Object} error\n */\n static error(player, { code, message, metadata }) {\n player.error(null);\n\n player.error({\n code,\n message,\n metadata,\n });\n }\n\n /**\n * Filter out incompatible resources such as `RTMP` and `HDS`.\n *\n * @param {Array.<import('../dataProvider/model/typedef').MainResource>} resources Resources to filter\n *\n * @returns {Array.<import('../dataProvider/model/typedef').MainResource>} The filtered resources\n */\n static filterIncompatibleResources(resources = []) {\n return resources.filter(\n (resource) => !['RTMP', 'HDS'].includes(resource.streaming)\n );\n }\n\n /**\n * Get the current blocked segment from the player.\n *\n * @param {import('video.js/dist/types/player').default} player\n *\n * @returns {VTTCue|undefined} The blocked segment cue object, or undefined\n */\n static getBlockedSegment(player) {\n const trackId = 'srgssr-blocked-segments';\n const blockedSegmentsTrack = player.textTracks().getTrackById(trackId);\n\n if (!blockedSegmentsTrack) return;\n\n /** @type {VTTCue} */\n const [blockedCue] = Array.from(blockedSegmentsTrack.activeCues);\n\n return blockedCue;\n }\n\n /**\n * Get the VTT cue of a blocked segment.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {Number} currentTime\n *\n * @returns {VTTCue|undefined} The VTT cue of a blocked segment, or undefined\n */\n static getBlockedSegmentByTime(player, currentTime) {\n const blockedSegment = SrgSsr.getBlockedSegment(player);\n\n if (!blockedSegment) return;\n\n const isBlocked = currentTime >= blockedSegment.startTime &&\n currentTime < blockedSegment.endTime;\n\n return isBlocked ? blockedSegment : undefined;\n }\n\n /**\n * Get mediaComposition from an URN.\n *\n * @param {String} urn\n * @param {Function} requestHandler\n *\n * @returns {Promise<MediaComposition>}\n */\n static async getMediaComposition(\n urn,\n handleRequest = new DataProvider().handleRequest()\n ) {\n const data = await handleRequest(urn);\n\n return Object.assign(new MediaComposition(), data);\n }\n\n /**\n * Get the mediaData most likely to be compatible depending on the browser.\n *\n * @param {Array.<import('./typedef').MainResourceWithKeySystems>} resources\n *\n * @returns {import('./typedef').MainResourceWithKeySystems} By default, the first entry is used if none is compatible.\n */\n static getMediaData(resources = []) {\n if (AkamaiTokenService.hasToken(resources)) return resources[0];\n\n const type = Pillarbox.browser.IS_ANY_SAFARI ? 'HLS' : 'DASH';\n const resource = resources.find(({ streaming }) => streaming === type);\n\n return resource || resources[0];\n }\n\n /**\n * Get the source media object.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {any} srcObj\n *\n * @returns {Promise<import('./typedef').ComposedSrcMediaData>} - The composed source media data.\n */\n static async getSrcMediaObj(player, srcObj) {\n const { src: urn, ...srcOptions } = srcObj;\n const mediaComposition = await SrgSsr.getMediaComposition(\n urn,\n SrgSsr.dataProvider(player)\n );\n const mainResources = await SrgSsr.composeMainResources(\n mediaComposition\n );\n const mediaData = SrgSsr.getMediaData(mainResources);\n\n return SrgSsr.composeSrcMediaData(srcOptions, mediaData);\n }\n\n /**\n * Handles the middleware's currentTime function.\n * - If the current time is between the start and end of a blocked segment,\n * the blocked portion will be skipped.\n *\n * _Note_: This function should disappear as soon as this behavior is\n * supported on the packaging side.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {number} currentTime\n *\n * @returns {number}\n */\n static handleCurrentTime(player, currentTime) {\n const blockedSegment = SrgSsr\n .getBlockedSegmentByTime(player, currentTime);\n\n if (!blockedSegment || !Number.isFinite(blockedSegment.endTime)) {\n return currentTime;\n }\n\n // as a workaround, add 0.1 seconds to avoid getting stuck on endTime on\n // some safaris.\n const endTimeWithTolerance = blockedSegment.endTime + 0.1;\n\n // proxy for handling cuechange events at the player level\n player.trigger({ type: 'srgssr/blocked-segment', data: blockedSegment });\n player.currentTime(endTimeWithTolerance);\n\n return endTimeWithTolerance;\n }\n\n /**\n * Handles the middleware's setCurrentTime function.\n * - Modify the current time if the value is between the start and end of a\n * blocked segment.\n *\n * _Note_: This function should disappear as soon as this behavior is\n * supported on the packaging side.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {number} currentTime\n *\n * @returns {number}\n */\n static handleSetCurrentTime(player, currentTime) {\n const { endTime: blockedSegmentEndTime } = SrgSsr\n .getBlockedSegmentByTime(player, currentTime) || {};\n\n return Number\n .isFinite(blockedSegmentEndTime) ? blockedSegmentEndTime : currentTime;\n }\n\n /**\n * Handles the middleware's setSource function.\n *\n * This function allows to:\n * - resolve a URN into media that can be played by the player\n * - initialize media playback tracking\n * - update the title bar\n * - handle blocking reasons\n * - add remote subtitles\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {any} srcObj\n * @param {function} next\n *\n * @returns {Promise<any>}\n */\n static async handleSetSource(player, srcObj, next) {\n try {\n const srcMediaObj = await SrgSsr.getSrcMediaObj(player, srcObj);\n\n SrgSsr.srgAnalytics(player);\n SrgSsr.updateTitleBar(player, srcMediaObj);\n SrgSsr.updatePoster(player, srcMediaObj);\n\n if (SrgSsr.blockingReason(player, srcMediaObj)) return;\n\n SrgSsr.addTextTracks(player, srcMediaObj);\n\n return next(null, srcMediaObj);\n } catch (error) {\n if (SrgSsr.dataProviderError(player, error)) return;\n\n return next(error);\n }\n }\n\n /**\n * SRG SSR analytics singleton.\n *\n * @param {import('video.js/dist/types/player').default} player\n */\n static srgAnalytics(player) {\n if (player.options().trackers.srgAnalytics === false) return;\n\n if (!player.options().trackers.srgAnalytics) {\n const srgAnalytics = new SRGAnalytics(player, {\n debug: player.debug(),\n playerVersion: Pillarbox.VERSION.pillarbox,\n tagCommanderScriptURL:\n player.options().srgOptions.tagCommanderScriptURL,\n });\n\n player.options({\n trackers: {\n srgAnalytics,\n },\n });\n }\n }\n\n /**\n * Update player's poster.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {import('./typedef').ComposedSrcMediaData} srcMediaObj\n * @param {Image} imageService\n */\n static updatePoster(player, srcMediaObj, imageService = Image) {\n player.poster(\n imageService.scale({\n url: srcMediaObj.mediaData.imageUrl,\n })\n );\n }\n\n /**\n * Update player titleBar with title and description.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {import('./typedef').ComposedSrcMediaData} srcMediaObj\n */\n static updateTitleBar(player, srcMediaObj) {\n if (!player.titleBar) return;\n\n player.titleBar.update({\n title: srcMediaObj.mediaData.vendor,\n description: srcMediaObj.mediaData.title,\n });\n }\n\n /**\n * Middleware to resolve SRG SSR URNs into playable media.\n *\n * @param {import('video.js/dist/types/player').default} player\n *\n * @returns {Object}\n */\n static middleware(player) {\n\n SrgSsr.cuechangeEventProxy(player);\n\n return {\n currentTime: (currentTime) =>\n SrgSsr.handleCurrentTime(player, currentTime),\n setCurrentTime: (currentTime) =>\n SrgSsr.handleSetCurrentTime(player, currentTime),\n setSource: async (srcObj, next) =>\n SrgSsr.handleSetSource(player, srcObj, next),\n };\n }\n}\n\nPillarbox.use('srgssr/urn', SrgSsr.middleware);\n\n// Add Middleware specific options\nPillarbox.options.srgOptions = {\n dataProvider: undefined,\n dataProviderHost: undefined,\n dataProviderUrlHandler: undefined,\n tagCommanderScriptURL: undefined,\n};\n\nexport default SrgSsr;\n"],"names":["vjsPlayer","videojs","getComponent","registerComponent","constructor","tag","options","ready","super","obj","merge","plugins","eme","audioTrack","trackSelector","audioTracks","Array","from","this","player","find","enabled","kind","language","bufferedRanges","ranges","i","buffered","length","start","end","push","playedPercent","Number","isFinite","duration","NaN","timePlayed","played","playedRanges","seekableRanges","seekable","textTrack","textTracks","filter","includes","mode","forEach","pillarbox","VERSION","VhsSourceHandler","name","getPlugin","enableSmoothSeeking","fill","html5","vhs","useForcedSubtitles","liveTracker","trackingThreshold","liveTolerance","liveui","playsinline","responsive","trackers","DataProvider","hostName","setIlHost","baseUrl","handleRequest","urlHandler","_this","_ref","_asyncToGenerator","urn","url","mediaCompositionUrlHandler","response","fetch","ok","json","_x","apply","arguments","SCALE","FORMAT","Image","scale","width","format","imageServiceUrl","scaleUrl","URL","searchParams","set","decodeURIComponent","href","JPG","PNG","WEBP","WIDTH_240","WIDTH_320","WIDTH_480","WIDTH_960","WIDTH_1920","DRM_VENDORS","WIDEVINE","FAIRPLAY","PLAYREADY","Drm","buildKeySystems","drmList","keySystems","drmVendor","type","vendors","certificateUrl","certificateUri","licenseUrl","licenseUri","hasDrm","resources","some","TOKEN_TYPES","AkamaiTokenService","aclPath","streamUrl","path","pathname","substring","lastIndexOf","AKAMAI","hasToken","resource","isAkamai","tokenType","tokentype","isNone","NONE","tokenize","source","tokenServerUrl","streamUrlToTokenize","acl","encodeURIComponent","then","Promise","reject","status","statusText","token","authparams","URLSearchParams","v","k","Object","assign","toString","catch","reason","tokenizeSources","sources","tokenizedSources","tokenizedSource","all","values","EMPTIED","ENDED","LOADED_DATA","LOAD_START","PAUSE","PLAY","PLAYING","RATE_CHANGE","SEEKING","TIME_UPDATE","WAITING","SRGAnalytics","debug","environment","playerVersion","tagCommanderScriptURL","isDebugEnabled","elapsedPlaybackTime","hasStarted","heartBeatIntervalId","undefined","initialized","isSeeking","isWaiting","mediaSession","pendingQueue","pendingTagCommanderReload","srcMediaData","startPlaybackSession","trackedCurrentTime","uptimeIntervalId","initScript","initListeners","beforeunload","notify","clearTimers","clearInterval","clearTimeout","uptimeTimeoutId","currentTime","Math","round","Boolean","destroy","window","tc_vars","dispose","removeEventListener","beforeunloadListener","off","PlayerEvents","emptiedListener","endedListener","loadstartListener","loadeddataListener","playListener","pauseListener","rateChangeListener","seekingListener","timeUpdateListener","waitingListener","emptied","ended","flush","isTrackerDisabled","tC","container","reload","tc_events_11","notification","el","action","labels","getCurrentAudioTrack","currentTrack","track","toUpperCase","getCurrentTextTrack","getDvrWindowPosition","ct","seekableStart","position","liveWindow","Infinity","getDvrWindowSize","isInfinity","windowSize","getElapsedPlaybackTime","getElapsedPlayingTime","playingSession","now","getEventLabels","eventName","event_id","event_timestamp","media_dvr_window_length","media_dvr_window_offset","media_is_dvr","media_is_live","media_mute","muted","media_playback_rate","playbackRate","media_position","media_quality","mediaData","quality","media_subtitles_on","isTextTrackEnabled","media_volume","volume","toFixed","navigation_environment","isAudioTrackEnabled","media_audio_track","media_subtitle_selection","isMediaOnDemand","isMediaDvr","media_timeshift","timeShifted","getInternalLabels","data","media_bu_distributer","vendor","media_chromecast_selected","tech","isCasting","media_embedding_url","document","referrer","media_player_display","media_player_name","media_player_version","media_url","src","analyticsMetadata","heartBeat","setInterval","paused","initCallbacks","bind","loadstart","loadeddata","play","pause","rateChange","seeking","timeUpdate","waiting","addEventListener","on","one","scriptId","querySelector","script","createElement","defer","id","onload","_e","body","appendChild","isMediaLive","isArray","disableTrackers","tracker","toLowerCase","updateSrcMediaData","currentSource","reloadTagCommanderContainer","log","eventMetadata","severity","console","error","Date","uptime","isAtLiveEdge","atLiveEdge","liveCurrentTime","notifyUptime","setTimeout","bufferStop","Pillarbox","browser","IS_ANY_SAFARI","MediaComposition","findChapterByUrn","chapterList","chapter","element","findMainSegment","segmentUrn","segmentList","getMainSegments","segment","findResourceListByUrn","chapterByUrn","resourceList","getChapters","AUDIO","getMainChapter","mediaType","getFilteredExternalSubtitles","subtitleList","subtitleInformationList","getResourceList","onlyHasExternalSubtitles","subtitles","subtitle","subtitleInformation","locale","getMainBlockReason","mainChapter","blockReason","getMainValidFromDate","getMainBlockedSegments","chapterUrn","getMainChapterImageUrl","imageUrl","getMainResources","map","analyticsData","getMergedAnalyticsData","getMergedAnalyticsMetadata","blockedSegments","chapters","dvr","eventData","imageCopyright","intervals","getMainTimeIntervals","live","mimeType","presentation","streaming","streamOffset","title","mainSegments","timeIntervalList","validFrom","_objectSpread","addLanguage","vjsLang","pillarboxLang","SrgSsr","addBlockedSegments","segments","trackId","removeTrack","getTrackById","createTextTrack","segmentTrack","addTextTrackCue","addTrack","addRemoteTextTracks","label","addRemoteTextTrack","startTime","markIn","fullLengthMarkIn","endTime","markOut","fullLengthMarkOut","addCue","VTTCue","JSON","stringify","addTextTracks","addChapters","addIntervals","chapterTrack","fullLengthUrn","intervalTrack","interval","blockingReason","srcMediaObj","message","localize","code","MediaError","MEDIA_ERR_ABORTED","metadata","errorType","composeAkamaiResources","resolve","composeKeySystemsResources","composeMainResources","mediaComposition","filterIncompatibleResources","composeSrcMediaData","_Pillarbox$obj$merge","_objectWithoutProperties","_excluded","TextTrack","cuechangeEventProxy","cue","activeCues","trigger","dataProvider","srgOptions","dataProviderHost","dataProviderUrlHandler","requestHandler","dataProviderError","getBlockedSegment","blockedSegmentsTrack","blockedCue","getBlockedSegmentByTime","blockedSegment","getMediaComposition","getMediaData","getSrcMediaObj","srcObj","srcOptions","_excluded2","mainResources","handleCurrentTime","endTimeWithTolerance","handleSetCurrentTime","blockedSegmentEndTime","handleSetSource","next","srgAnalytics","updateTitleBar","updatePoster","imageService","poster","titleBar","update","description","middleware","setCurrentTime","setSource","_x2","use"],"mappings":"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"}
1
+ {"version":3,"file":"pillarbox.cjs.min.js","sources":["../src/components/player.js","../src/pillarbox.js","../src/dataProvider/services/DataProvider.js","../src/utils/Image.js","../src/utils/Drm.js","../src/utils/AkamaiTokenService.js","../src/utils/PlayerEvents.js","../src/analytics/SRGAnalytics.js","../src/dataProvider/model/MediaComposition.js","../src/lang/de.js","../src/lang/en.js","../src/lang/fr.js","../src/lang/it.js","../src/lang/rm.js","../src/middleware/srgssr.js"],"sourcesContent":["import videojs from 'video.js';\nimport 'videojs-contrib-eme';\n\n/**\n * @ignore\n * @type {typeof import('video.js/dist/types/player').default}\n */\nconst vjsPlayer = videojs.getComponent('player');\n\n/**\n * This class extends the video.js Player.\n *\n * @class Player\n * @see https://docs.videojs.com/player\n */\nclass Player extends vjsPlayer {\n constructor(tag, options, ready) {\n /**\n * Configuration for plugins.\n *\n * @see [Video.js Plugins Option]{@link https://videojs.com/guides/options/#plugins}\n * @type {Object}\n * @property {boolean} eme - Enable the EME (Encrypted Media Extensions) plugin.\n */\n options = videojs.obj.merge(options, { plugins: { eme: true }});\n super(tag, options, ready);\n }\n\n /**\n * A getter/setter for the media's audio track.\n * Activates the audio track according to the language and kind properties.\n * Falls back on the first audio track found if the kind property is not satisfied.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/AudioTrack/kind\n * @see https://developer.mozilla.org/en-US/docs/Web/API/AudioTrack/language\n *\n * @param {import('./typedef').TrackSelector} [trackSelector]\n *\n * @example\n * // Get the current audio track\n * player.audioTrack();\n *\n * @example\n * // Activate an audio track based on language and kind properties\n * player.audioTrack({language:'en', kind:'description'});\n *\n * @example\n * // Activate first audio track found corresponding to language\n * player.audioTrack({language:'fr'});\n *\n * @return {import('video.js/dist/types/tracks/audio-track').default | undefined} The\n * currently enabled audio track. See {@link https://docs.videojs.com/audiotrack}.\n */\n audioTrack(trackSelector) {\n const audioTracks = Array.from(this.player().audioTracks());\n\n if (!trackSelector) {\n return audioTracks.find((audioTrack) => audioTrack.enabled);\n }\n\n const { kind, language } = trackSelector;\n const audioTrack =\n audioTracks.find(\n (audioTrack) =>\n audioTrack.language === language && audioTrack.kind === kind\n ) || audioTracks.find((audioTrack) => audioTrack.language === language);\n\n if (audioTrack) {\n audioTrack.enabled = true;\n }\n\n return audioTrack;\n }\n\n /**\n * Calculates an array of ranges based on the `buffered()` data.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/buffered\n *\n * @returns {Array<{start: number, end: number}>} An array of objects representing start and end points of buffered ranges.\n */\n bufferedRanges() {\n const ranges = [];\n\n for (let i = 0; i < this.buffered().length; i++) {\n const start = this.buffered().start(i);\n const end = this.buffered().end(i);\n\n ranges.push({ start, end });\n }\n\n return ranges;\n }\n\n /**\n * Get the percent (as a decimal) of the media that's been played.\n * This method is not a part of the native HTML video API.\n *\n * Live streams with DVR are not currently supported.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement#htmlmediaelement.played\n *\n * @return {number}\n * A decimal between 0 and 1 representing the percent\n * that is played 0 being 0% and 1 being 100%\n */\n playedPercent() {\n if (!Number.isFinite(this.duration())) return NaN;\n\n let timePlayed = 0;\n\n for (let i = 0; i != this.played().length; i++) {\n timePlayed += this.played().end(i) - this.played().start(i);\n }\n\n const percentPlayed = timePlayed / this.duration();\n\n return percentPlayed;\n }\n\n /**\n * Get an array of ranges based on the `played` data.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement#htmlmediaelement.played\n *\n * @returns {Array<{start: number, end: number}>} An array of objects representing start and end points of played ranges.\n */\n playedRanges() {\n const ranges = [];\n\n for (let i = 0; i < this.played().length; i++) {\n const start = this.played().start(i);\n const end = this.played().end(i);\n\n ranges.push({ start, end });\n }\n\n return ranges;\n }\n\n /**\n * Calculates an array of ranges based on the `seekable()` data.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/seekable\n *\n * @returns {Array<{start: number, end: number}>} An array of objects representing start and end points of seekable ranges.\n */\n seekableRanges() {\n const ranges = [];\n\n for (let i = 0; i < this.seekable().length; i++) {\n const start = this.seekable().start(i);\n const end = this.seekable().end(i);\n\n ranges.push({ start, end });\n }\n\n return ranges;\n }\n\n /**\n * A getter/setter for the media's text track.\n * Activates the text track according to the language and kind properties.\n * Falls back on the first text track found if the kind property is not satisfied.\n * Disables all subtitle tracks that are `showing` if the `trackSelector` is truthy but does not satisfy any condition.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/TextTrack/kind\n * @see https://developer.mozilla.org/en-US/docs/Web/API/textTrack/language\n *\n * @param {import('./typedef').TrackSelector} [trackSelector]\n *\n * @example\n * // Get the current text track\n * player.textTrack();\n *\n * @example\n * // Disable all text tracks has a side effect\n * player.textTrack('off');\n * player.textTrack({});\n *\n * @example\n * // Activate an text track based on language and kind properties\n * player.textTrack({language:'en', kind:'captions'});\n *\n * @example\n * // Activate first text track found corresponding to language\n * player.textTrack({language:'fr'});\n *\n * @return {import('video.js/dist/types/tracks/text-track').default | undefined} The\n * currently enabled text track. See {@link https://docs.videojs.com/texttrack}.\n */\n textTrack(trackSelector) {\n const textTracks = Array.from(this.player().textTracks()).filter(\n (textTrack) => !['chapters', 'metadata'].includes(textTrack.kind)\n );\n\n if (!trackSelector) {\n return textTracks.find((textTrack) => textTrack.mode === 'showing');\n }\n\n textTracks.forEach((textTrack) => (textTrack.mode = 'disabled'));\n\n const { kind, language } = trackSelector;\n const textTrack =\n textTracks.find((textTrack) => {\n if (textTrack.language === language && textTrack.kind === kind) {\n textTrack.mode = 'showing';\n }\n\n return textTrack.mode === 'showing';\n }) ||\n textTracks.find((textTrack) => {\n if (textTrack.language === language) {\n textTrack.mode = 'showing';\n }\n\n return textTrack.mode === 'showing';\n });\n\n return textTrack;\n }\n}\n\nvideojs.registerComponent('player', Player);\n\nexport default Player;\n","import { version } from '../package.json';\nimport videojs from 'video.js';\nimport './components/player.js';\n\n/**\n * Pillarbox is an alias for the video.js namespace with additional options.\n *\n * @namespace\n * @see https://docs.videojs.com/module-videojs-videojs\n * @type {videojs}\n */\nconst pillarbox = videojs;\n\npillarbox.VERSION = {\n pillarbox: version,\n videojs: videojs.VERSION,\n [videojs.VhsSourceHandler.name]: videojs.VhsSourceHandler.VERSION,\n eme: videojs.getPlugin('eme').VERSION,\n};\n\n/**\n * Enable smooth seeking for Pillarbox.\n *\n * @see [Video.js enableSmoothSeeking Option]{@link https://videojs.com/guides/options/#enablesmoothseeking}\n * @type {boolean}\n * @default true\n */\npillarbox.options.enableSmoothSeeking = true;\n/**\n * Enable fill mode for the video player, allowing it to expand to fill the container.\n *\n * @see [Video.js Fill Option]{@link https://videojs.com/guides/layout/#fill-mode}\n * @type {boolean}\n * @default true\n */\npillarbox.options.fill = true;\n/**\n * Configuration options for HTML5 settings in Pillarbox.\n *\n * @see [VHS useForcedSubtitles Option]{@link https://github.com/videojs/http-streaming/blob/main/README.md#useforcedsubtitles}\n * @type {Object}\n * @property {Object} vhs - Configuration for the Video.js HTTP Streaming.\n * @property {boolean} useForcedSubtitles - Enables the player to display forced subtitles by default.\n * Forced subtitles are pieces of information intended for display when no other text representation\n * is selected. They are used to clarify dialogue, provide alternate languages, display texted graphics,\n * or present location/person IDs that are not otherwise covered in the dubbed/localized audio.\n */\npillarbox.options.html5 = {\n vhs: { useForcedSubtitles: true }\n};\n/**\n * Configuration for the live tracker.\n *\n * @see [Video.js liveTracker Option]{@link https://videojs.com/guides/options/#livetrackertrackingthreshold}\n * @type {Object}\n * @property {number} trackingThreshold - A threshold that controls when the liveui should be shown.\n * @property {number} liveTolerance - An option that controls how far from the seekable end should be considered live playback.\n */\npillarbox.options.liveTracker = {\n trackingThreshold: 120,\n liveTolerance: 15,\n};\n/**\n * Allows the player to use the live ui that includes:\n *\n * - A progress bar for seeking within the live window\n * - A button that can be clicked to seek to the live edge with a circle indicating if you are at the live edge or not.\n *\n * @see [Video.js liveui Option]{@link https://videojs.com/guides/options/#liveui}\n * @type {boolean}\n */\npillarbox.options.liveui = true;\n/**\n * Indicates that the video is to be played \"inline\", that is within the element's playback area.\n *\n * @see [Video element playsinline attribute]{@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#playsinline}\n * @type {boolean}\n */\npillarbox.options.playsinline = true;\n/**\n * Enable responsive mode, this will cause the player to customize itself based on responsive breakpoints.\n *\n * @see [Video.js Responsive Option]{@link https://videojs.com/guides/options/#responsive}\n * @type {boolean}\n */\npillarbox.options.responsive = true;\n/**\n * A placeholder for accessing trackers directly from the player.\n *\n * @type {Object}\n */\npillarbox.options.trackers = {};\n\nexport default pillarbox;\n","/**\n * Represents a data provider for constructing URLs and handling requests.\n * @class\n * @ignore\n */\nclass DataProvider {\n /**\n * Creates an instance of DataProvider.\n *\n * @param {string} [hostName='il.srgssr.ch'] The base host name for constructing URLs\n */\n constructor(hostName = 'il.srgssr.ch') {\n this.setIlHost(hostName);\n }\n\n /**\n * Sets the integration layer host name.\n *\n * @param {string} hostName The host name to set\n */\n setIlHost(hostName) {\n this.baseUrl = `${hostName}/integrationlayer/2.1/`;\n }\n\n /**\n * Handles requests by constructing URLs and fetching data.\n *\n * This provides unified error handling, regardless of the urlHandler used.\n *\n * @param {Function} urlHandler A function that constructs the URL\n *\n * @returns {Promise<import('../model/MediaComposition.js').default>} A promise with the fetched data\n */\n handleRequest(urlHandler) {\n return async (urn) => {\n const url = typeof urlHandler === 'function' ? urlHandler(urn) : this.mediaCompositionUrlHandler(urn);\n const response = await fetch(url);\n\n if (!response.ok) {\n throw response;\n }\n\n /** @type {import('../model/MediaComposition.js').default} */\n const data = await response.json();\n\n return data;\n };\n }\n\n /**\n * Gets the media composition URL by URN.\n *\n * @param {string} urn The URN for the media composition\n *\n * @returns {string} The constructed URL\n */\n mediaCompositionUrlHandler(urn) {\n return `https://${this.baseUrl}mediaComposition/byUrn/${urn}?onlyChapters=true&vector=portalplay`;\n }\n}\n\nexport default DataProvider;\n","const SCALE = {\n WIDTH_240: '240',\n WIDTH_320: '320',\n WIDTH_480: '480',\n WIDTH_960: '960',\n WIDTH_1920: '1920',\n};\n\nconst FORMAT = {\n JPG: 'jpg',\n WEBP: 'webp',\n PNG: 'png',\n};\n\nconst IMAGE_SERVICE_URL = 'https://il.srgssr.ch/images/';\n\n/**\n * @class Image\n */\nclass Image {\n /**\n * Generates the image scaling URL.\n *\n * @property {Object} image is the object representation of an image.\n * @property {String} [image.url] is the image URL.\n * @property {String} [image.width=960] is the width of the image, default value 960.\n * @property {String} [image.format=jpg] is the format of the image, default value jpg.\n * @property {String} [imageServiceUrl] Url of the image service that needs to comply with the specification defined by the IL.\n *\n * @see https://confluence.srg.beecollaboration.com/pages/viewpage.action?spaceKey=SRGPLAY&title=Project+-+Image+Service\n *\n * @returns {String|undefined} the image scaling URL.\n */\n static scale(\n { url, width = SCALE.WIDTH_960, format = FORMAT.JPG } = {},\n imageServiceUrl = IMAGE_SERVICE_URL\n ) {\n if (!url) return;\n\n const scaleUrl = new URL(imageServiceUrl);\n\n scaleUrl.searchParams.set('imageUrl', url);\n scaleUrl.searchParams.set('format', format);\n scaleUrl.searchParams.set('width', width);\n\n return decodeURIComponent(scaleUrl.href);\n }\n\n static get JPG() {\n return FORMAT.JPG;\n }\n\n static get PNG() {\n return FORMAT.PNG;\n }\n\n static get WEBP() {\n return FORMAT.WEBP;\n }\n\n static get WIDTH_240() {\n return SCALE.WIDTH_240;\n }\n\n static get WIDTH_320() {\n return SCALE.WIDTH_320;\n }\n\n static get WIDTH_480() {\n return SCALE.WIDTH_480;\n }\n\n static get WIDTH_960() {\n return SCALE.WIDTH_960;\n }\n\n static get WIDTH_1920() {\n return SCALE.WIDTH_1920;\n }\n}\n\nexport default Image;\n","const DRM_VENDORS = {\n WIDEVINE: 'com.widevine.alpha',\n FAIRPLAY: 'com.apple.fps.1_0',\n PLAYREADY: 'com.microsoft.playready',\n};\n\n/**\n * @class Drm\n */\nclass Drm {\n /**\n * Build the keySystems object according to the DRM vendor.\n *\n * @param {Array.<import('../dataProvider/model/typedef').DrmMetadata>} drmList The DRM list from the media composition.\n *\n * @returns {import('./typedef').KeySystems} The resulting keySystems.\n */\n static buildKeySystems(drmList = []) {\n const keySystems = {};\n\n drmList.forEach((drmVendor) => {\n const type = Drm.vendors[drmVendor.type];\n\n if (Drm.vendors.FAIRPLAY === type) {\n const { certificateUrl: certificateUri, licenseUrl: licenseUri } =\n drmVendor;\n\n keySystems[type] = {\n certificateUri,\n licenseUri,\n };\n } else {\n keySystems[type] = drmVendor.licenseUrl;\n }\n });\n\n return {\n keySystems,\n };\n }\n\n /**\n * Check if some of the resources have DRM.\n *\n * @param {Array.<import('../dataProvider/model/typedef').MainResource>} resources\n *\n * @returns {boolean} true if some of the resources have DRM, false otherwise.\n */\n static hasDrm(resources) {\n return resources.some(({ drmList }) => drmList && drmList.length > 0);\n }\n\n /**\n * Get DRM vendors.\n */\n static get vendors() {\n return DRM_VENDORS;\n }\n}\n\nexport default Drm;\n","const TOKEN_TYPES = {\n AKAMAI: 'AKAMAI',\n NONE: 'NONE',\n};\n\n/**\n * @class AkamaiTokenService\n */\nclass AkamaiTokenService {\n /**\n * Get the acl path.\n *\n * @param {URL} streamUrl\n *\n * @returns {String}\n */\n static aclPath(streamUrl) {\n const path = streamUrl.pathname;\n\n return `${path.substring(0, path.lastIndexOf('/') + 1)}*`;\n }\n\n /**\n * AKAMAI\n *\n * @type {String}\n */\n static get AKAMAI() {\n return TOKEN_TYPES.AKAMAI;\n }\n\n /**\n * Check if the resources are protected by an Akamai token.\n * Keep in mind, as we are using the 'some' function,\n * if the resources have at least one resource\n * protected by a token it returns true!\n *\n * @param {Array.<import('../dataProvider/model/typedef').MainResource>} resources\n *\n * @returns {Boolean}\n */\n static hasToken(resources) {\n return resources.some((resource) =>\n AkamaiTokenService.isAkamai(resource.tokenType));\n }\n\n /**\n * Check if the token type is AKAMAI.\n *\n * @param {String} tokentype\n *\n * @returns {Boolean}\n */\n static isAkamai(tokentype) {\n return TOKEN_TYPES.AKAMAI === tokentype;\n }\n\n /**\n * Check if the token type is NONE.\n *\n * @param {String} tokentype\n *\n * @returns {Boolean}\n */\n static isNone(tokentype) {\n return TOKEN_TYPES.NONE === tokentype;\n }\n\n /**\n * NONE\n *\n * @type {String}\n */\n static get NONE() {\n return TOKEN_TYPES.NONE;\n }\n\n /**\n * Generate the stream URL with the akamai token.\n *\n * @param {import('../dataProvider/model/typedef').MainResource} source\n * @param {String} tokenServerUrl\n *\n * @returns {Promise.<import('../dataProvider/model/typedef').MainResource>}\n */\n static tokenize(source, tokenServerUrl) {\n const streamUrlToTokenize = new URL(`${source.url}`);\n const acl = AkamaiTokenService.aclPath(streamUrlToTokenize);\n const url = `${tokenServerUrl}${encodeURIComponent(acl)}`;\n\n return fetch(url)\n .then((response) => {\n if (response.ok) {\n return response.json();\n }\n\n return Promise.reject({\n status: response.status,\n statusText: response.statusText,\n });\n })\n .then(({ token: { authparams }}) => {\n const akamaiAuthParams = new URLSearchParams(authparams);\n\n akamaiAuthParams.forEach((v, k) =>\n streamUrlToTokenize.searchParams.set(k, v));\n\n return Object.assign({}, source, {\n url: streamUrlToTokenize.toString(),\n });\n })\n .catch((reason) => {\n return Promise.reject(reason);\n });\n }\n\n /**\n * Generate a token for each source\n *\n * @template {import('../dataProvider/model/typedef').MainResource} T\n * @param {Array.<T>} sources\n * @param {String} tokenServerUrl\n *\n * @returns {Promise.<Array.<T>>}\n */\n static tokenizeSources(\n sources,\n tokenServerUrl = 'https://tp.srgssr.ch/akahd/token?acl='\n ) {\n const tokenizedSources = [];\n\n sources.forEach((source) => {\n const tokenizedSource = AkamaiTokenService.tokenize(\n source,\n tokenServerUrl\n );\n\n tokenizedSources.push(tokenizedSource);\n });\n\n return Promise.all(tokenizedSources)\n .then((values) => values)\n .catch((reason) => Promise.reject(reason));\n }\n}\n\nexport default AkamaiTokenService;\n","/**\n * Exhaustive list of player events.\n *\n * See below the documentation related to the media events\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement#events\n *\n * @namespace {Object} PlayerEvents\n */\n\n/**\n * Triggered when the media can start playing.\n *\n * @event PlayerEvents#CAN_PLAY\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canplay_event\n */\nexport const CAN_PLAY = 'canplay';\n\n/**\n * Triggered when the media can be played through to the end without buffering.\n *\n * @event PlayerEvents#CAN_PLAY_THROUGH\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canplaythrough_event\n */\nexport const CAN_PLAY_THROUGH = 'canplaythrough';\n\n/**\n * Triggered when the duration of the media changes.\n *\n * @event PlayerEvents#DURATION_CHANGE\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/durationchange_event\n */\nexport const DURATION_CHANGE = 'durationchange';\n\n/**\n * Triggered when the media element is emptied (e.g., reset as part of the seeking process).\n *\n * @event PlayerEvents#EMPTIED\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/emptied_event\n */\nexport const EMPTIED = 'emptied';\n\n/**\n * Triggered when the media playback has ended.\n *\n * @event PlayerEvents#ENDED\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/ended_event\n */\nexport const ENDED = 'ended';\n\n/**\n * Triggered when an error occurs during media playback.\n *\n * @event PlayerEvents#ERROR\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/error_event\n */\nexport const ERROR = 'error';\n\n/**\n * Triggered when the media data has been loaded.\n *\n * @event PlayerEvents#LOADED_DATA\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/loadeddata_event\n */\nexport const LOADED_DATA = 'loadeddata';\n\n/**\n * Triggered when metadata for the media has been loaded.\n *\n * @event PlayerEvents#LOADED_METADATA\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/loadedmetadata_event\n */\nexport const LOADED_METADATA = 'loadedmetadata';\n\n/**\n * Triggered when the browser starts looking for media data.\n *\n * @event PlayerEvents#LOAD_START\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/loadstart_event\n */\nexport const LOAD_START = 'loadstart';\n\n/**\n * Triggered when the media playback is paused.\n *\n * @event PlayerEvents#PAUSE\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/pause_event\n */\nexport const PAUSE = 'pause';\n\n/**\n * Triggered when the media playback is resumed or started.\n *\n * @event PlayerEvents#PLAY\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play_event\n */\nexport const PLAY = 'play';\n\n/**\n * Triggered when the media playback is in progress.\n *\n * @event PlayerEvents#PLAYING\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/playing_event\n */\nexport const PLAYING = 'playing';\n\n/**\n * Triggered as the media is being loaded.\n *\n * @event PlayerEvents#PROGRESS\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/progress_event\n */\nexport const PROGRESS = 'progress';\n\n/**\n * Triggered when the playback rate changes.\n *\n * @event PlayerEvents#RATE_CHANGE\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/ratechange_event\n */\nexport const RATE_CHANGE = 'ratechange';\n\n/**\n * Triggered when a seek operation is completed.\n *\n * @event PlayerEvents#SEEKED\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/seeked_event\n */\nexport const SEEKED = 'seeked';\n\n/**\n * Triggered when a seek operation is in progress.\n *\n * @event PlayerEvents#SEEKING\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/seeking_event\n */\nexport const SEEKING = 'seeking';\n\n/**\n * Triggered when the media playback is stalled.\n *\n * @event PlayerEvents#STALLED\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/stalled_event\n */\nexport const STALLED = 'stalled';\n\n/**\n * Triggered when media loading is suspended.\n *\n * @event PlayerEvents#SUSPEND\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/suspend_event\n */\nexport const SUSPEND = 'suspend';\n\n/**\n * Triggered when the current playback position is updated.\n *\n * @event PlayerEvents#TIME_UPDATE\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/timeupdate_event\n */\nexport const TIME_UPDATE = 'timeupdate';\n\n/**\n * Triggered when the volume is changed.\n *\n * @event PlayerEvents#VOLUME_CHANGE\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/volumechange_event\n */\nexport const VOLUME_CHANGE = 'volumechange';\n\n/**\n * Triggered when the media playback is waiting for data.\n *\n * @event PlayerEvents#WAITING\n * @type {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/waiting_event\n */\nexport const WAITING = 'waiting';\n","import * as PlayerEvents from '../utils/PlayerEvents.js';\nimport Pillarbox from '../pillarbox.js';\n/* eslint max-lines-per-function: [\"error\", 200] */\n/* eslint max-statements: [\"error\", 20]*/\n/* eslint complexity: [\"error\", 10]*/\n/**\n * SRG analytics\n * @class SRGAnalytics\n * @ignore\n *\n * ### Script URL\n * JS script : https://colibri-js.akamaized.net/penguin/tc_SRGGD_11.js\n *\n * ### Official documentation\n * Variables list\n * @see https://confluence.srg.beecollaboration.com/display/INTFORSCHUNG/Datalayer+for+media+players\n *\n * Standard event sequences\n * @see https://confluence.srg.beecollaboration.com/display/INTFORSCHUNG/standard+streaming+events%3A+sequence+of+events+for+media+player+actions\n *\n * Review of Standard Media Actions\n * @see https://confluence.srg.beecollaboration.com/display/INTFORSCHUNG/Implementation+Concept+-+draft\n *\n * ComScore Implementation Guide\n * @see https://www.dropbox.com/sh/cdwuikq0abxi21m/AABmSyXYKUTWSAwRZgQA9Ujna/JavaScript%20Latest%20Version?dl=0&preview=Comscore_Library-JavaScript-Streaming_Tag-Implementation_Guide-International.pdf&subfolder_nav_tracking=1\n *\n * ### Variables list\n * - 'event_id', // init | play | stop | pos | pause | seek | uptime | eof\n * - 'event_timestamp', // Seems to be generated automatically from the documentation, but the TP overrides it\n * - 'event_name', // NA TP seems to not sending this variable\n * - 'event_source', // NA TP seems to not sending this variable\n * - 'event_name', // NA TP seems to not sending this variable\n * - 'event_value', // NA TP seems to not sending this variable\n * - 'navigation_environment', // prod | preprod\n * - 'media_subtitles_on', // string true | false\n * - 'media_timeshift', // need better description\n * - 'media_quality', // SD | HD ?\n * - 'media_bandwidth', // NA for the web, 64000\n * - 'media_volume', // from 0 to 100\n * - 'media_embedding_url', //\n * - 'media_player_name', // videojs | letterbox-web ?\n * - 'media_chromecast_selected', // boolean true | false\n * - 'media_player_version', // player's version\n * - 'media_player_display', // is the player mode, on the TP : inline, embed etc..\n * - 'media_audio_track', // NA\n * - 'media_position_real', // NA\n * - 'media_time_spent', // NA\n * - 'device_id', // NA\n * - 'user_id_log_in', // NA only RTS has log in today\n * - 'media_thumbnail', // Not required by the spec but sended by the TP\n * - 'media_bu_distributer', // Not required by the spec but sended by the TP\n *\n *\n * ### Sequence stories\n *\n * __Story 1 (AoD/VOD-basics)__: A VoD is played. The user does not interact with the player. The VoD plays to its end.\n *\n * Hints:\n * - Media sessions always start with PLAY. They end with STOP or EOF (or with PAUSE or last POS)\n * - POS is sent ever 30s\n *\n *\n * __Story 2 (livestream-basics A)__: A Livestream is played. The user does not interact with the player. After 61 seconds, playback is paused.\n *\n * Hints:\n * - Media sessions always start with PLAY. They end with STOP (or, worse for data quailty, with PAUSE or last POS/UPTIME)\n * - UPTIME is sent only for livestreams\n * - POS is sent ever 30s, UPTIME every 60s with inital UPTIME after 30s.\n * - This is the interval: 30s: POS + UPTIME; 60s: POS; 90s: POS + UPTIME; ...\n *\n *\n * __Story 3 (Seeking a VoD/AoD)__: A VoD is played. User seeks in the VoD/AoD.\n *\n * Hints:\n * - Once the Media Player slider is released (seek is over), another action to finish up the seeking is initiated. Typically this is PLAY. For that second PLAY, the media position has altered.\n *\n *\n * __Story 4 (Seeking a livestream)__: A Livestream is played. User goes back in the livestream.\n *\n * Hints:\n * - Once the Media Player slider is released (seek is over), another action to finish up the seeking is initiated. Typically this is PLAY. For that second PLAY, the a new variable, media_timeshift is passed.\n * - For livestreams media_position is always the \"time passed on your watch\" - regardless of the SEEK event. So, if 1 second after PLAY the slider is moved back 600 seconds, then:\n * 1. The the value of media_timeshift is '600'.\n * 2. The value of media_position is '1'.\n */\nclass SRGAnalytics {\n constructor(\n player,\n {\n debug = false,\n environment = 'prod',\n playerVersion = 'none',\n tagCommanderScriptURL = '//colibri-js.akamaized.net/penguin/tc_SRGGD_11.js',\n } = {}\n ) {\n this.isDebugEnabled = debug;\n this.elapsedPlaybackTime = 0;\n this.environment = environment;\n this.hasStarted = false;\n this.heartBeatIntervalId = undefined;\n /* Set to true when 'init' event is sent or queued. */\n this.initialized = false;\n this.isSeeking = false;\n this.isWaiting = false;\n this.mediaSession = 0;\n this.pendingQueue = [];\n this.pendingTagCommanderReload = false;\n this.player = player;\n this.playerVersion = playerVersion;\n this.srcMediaData = undefined;\n this.startPlaybackSession = 0;\n this.tagCommanderScriptURL = tagCommanderScriptURL;\n this.trackedCurrentTime = 0;\n this.uptimeIntervalId = undefined;\n\n this.initScript();\n this.initListeners();\n }\n\n /**\n * Sent when the window, the document and its resources are about to be unloaded.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event\n */\n beforeunload() {\n this.notify('stop');\n }\n\n /**\n * Clear timers used to send uptime and heartbeat.\n */\n clearTimers() {\n clearInterval(this.heartBeatIntervalId);\n clearInterval(this.uptimeIntervalId);\n clearTimeout(this.uptimeTimeoutId);\n }\n\n /**\n * Get the tracked current time in seconds.\n *\n * @returns {Number} current time in seconds\n */\n currentTime() {\n // see PLAYRTS-2771\n return Math.round(this.trackedCurrentTime);\n }\n\n /**\n * Get or set debug mode.\n *\n * @returns {Boolean|undefined}\n */\n debug(enabled) {\n if (enabled === undefined) {\n return this.isDebugEnabled || this.player.debug();\n }\n\n this.isDebugEnabled = Boolean(enabled);\n }\n\n /**\n * Destroy all properties and setIntervals to avoid mixing media sessions.\n */\n destroy() {\n this.clearTimers();\n\n if (!window.tc_vars) {\n window.tc_vars = {};\n }\n this.elapsedPlaybackTime = 0;\n this.hasStarted = false;\n this.heartBeatIntervalId = undefined;\n this.initialized = false;\n this.isWaiting = false;\n this.mediaSession = 0;\n this.pendingQueue = [];\n this.srcMediaData = undefined;\n this.startPlaybackSession = 0;\n this.trackedCurrentTime = 0;\n this.uptimeIntervalId = undefined;\n }\n\n /**\n * Dispose all listeners used to send analytics data to TagCommander.\n *\n * Calls `beforeunload` to send a notify stop.\n * Clear intervals and timeouts.\n *\n * __Used events__\n * - beforeunload\n * - emptied\n * - ended\n * - loadstart\n * - loadeddata\n * - play\n * - pause\n * - timeupdate\n */\n dispose() {\n this.beforeunload();\n this.clearTimers();\n\n window.removeEventListener('beforeunload', this.beforeunloadListener);\n\n this.player.off(PlayerEvents.EMPTIED, this.emptiedListener);\n this.player.off(PlayerEvents.ENDED, this.endedListener);\n this.player.off(PlayerEvents.LOAD_START, this.loadstartListener);\n this.player.off(PlayerEvents.LOADED_DATA, this.loadeddataListener);\n this.player.off(PlayerEvents.PLAYING, this.playListener);\n this.player.off(PlayerEvents.PAUSE, this.pauseListener);\n this.player.off(PlayerEvents.RATE_CHANGE, this.rateChangeListener);\n this.player.off(PlayerEvents.SEEKING, this.seekingListener);\n this.player.off(PlayerEvents.TIME_UPDATE, this.timeUpdateListener);\n this.player.off(PlayerEvents.WAITING, this.waitingListener);\n }\n\n /**\n * Sent before a new media is loading.\n * - Destroy all properties.\n * - Send a notify stop if the media is not ended and new media is about to be loaded.\n */\n emptied() {\n if (!this.player.ended()) {\n this.notify('stop');\n }\n }\n\n /**\n * Sent when playback completes.\n *\n * @see https://docs.videojs.com/player#event:ended\n */\n ended() {\n this.notify('eof');\n\n this.mediaSession = 0;\n\n this.clearTimers();\n }\n\n /**\n * Flush the queued events when tc event script is loaded.\n */\n flush() {\n if (this.isTrackerDisabled()) return;\n\n if (this.pendingTagCommanderReload && window.tC) {\n window.tC.container.reload();\n this.pendingTagCommanderReload = false;\n }\n\n if (window.tc_events_11 && this.pendingQueue.length > 0) {\n this.pendingQueue.forEach((notification) => {\n window.tc_events_11(\n this.player.el(),\n notification.action,\n notification.labels\n );\n });\n\n this.pendingQueue = [];\n }\n }\n\n /**\n * Get the language of the current audio track.\n *\n * @returns {String} empty string or uppercase language.\n */\n getCurrentAudioTrack() {\n const currentTrack = Array.from(this.player.audioTracks()).find(\n (track) => track.enabled\n );\n let language = 'und';\n\n if (currentTrack && !!currentTrack.language) {\n // eslint-disable-next-line prefer-destructuring\n language = currentTrack.language;\n }\n\n return currentTrack ? language.toUpperCase() : '';\n }\n\n /**\n * Get the language of the current text track.\n *\n * @returns {String} empty string or uppercase language.\n */\n getCurrentTextTrack() {\n const currentTrack = this.player.textTrack();\n let language = 'und';\n\n if (currentTrack && !!currentTrack.language) {\n // eslint-disable-next-line prefer-destructuring\n language = currentTrack.language;\n }\n\n return currentTrack ? language.toUpperCase() : '';\n }\n\n /**\n * Get the position inside the dvr window where the 0 represents the live edge\n *\n * @return {Number} 0 or the position in milliseconds\n */\n getDvrWindowPosition() {\n const { liveTracker } = this.player;\n const ct = (this.currentTime() - liveTracker.seekableStart()) | 0;\n const position = liveTracker.liveWindow() - ct;\n\n return position < 0 || position === Infinity ? 0 : position * 1000;\n }\n\n /**\n * Get the size of the live DVR window in milliseconds.\n *\n * @return {Number} DVR window size in milliseconds\n */\n getDvrWindowSize() {\n const isInfinity = this.player.liveTracker.liveWindow() === Infinity;\n const windowSize = this.player.liveTracker.liveWindow() * 1000;\n\n return isInfinity ? 0 : windowSize;\n }\n\n /**\n * Get the elapsed playback time in seconds.\n *\n * @returns {Number} elapsed time in seconds\n */\n getElapsedPlaybackTime() {\n if (this.startPlaybackSession) {\n return this.getElapsedPlayingTime();\n }\n\n return this.elapsedPlaybackTime;\n }\n\n /**\n * Get the elapsed playing time in seconds.\n *\n * @returns {Number} elapsed time in seconds\n */\n getElapsedPlayingTime() {\n const playingSession = (SRGAnalytics.now() - this.startPlaybackSession) | 0;\n\n return this.elapsedPlaybackTime + playingSession;\n }\n\n /**\n * Set all event labels to be sent to TagCommander. The event labels are updated whenever a new event occurs.\n *\n * @param {String} eventName init | play | stop | pos | pause | seek | uptime | eof\n *\n * @returns {Object} JSON to be sent to TagCommander\n */\n getEventLabels(eventName) {\n const labels = {\n event_id: eventName,\n event_timestamp: SRGAnalytics.now(),\n media_dvr_window_length: 0,\n media_dvr_window_offset: 0,\n media_is_dvr: false,\n media_is_live: false,\n media_mute: this.player.muted() ? '1' : '0',\n media_playback_rate: this.player.playbackRate(),\n media_position: this.currentTime(),\n media_quality: this.srcMediaData.mediaData.quality,\n // TODO use media_is_dvr, media_is_live to define peach media_stream_type\n media_subtitles_on: this.isTextTrackEnabled(),\n media_volume: (this.player.volume() * 100).toFixed(0),\n navigation_environment: this.environment,\n };\n\n if (this.isAudioTrackEnabled()) {\n labels.media_audio_track = this.getCurrentAudioTrack();\n }\n\n if (this.isTextTrackEnabled()) {\n labels.media_subtitle_selection = this.getCurrentTextTrack();\n }\n\n // DVR or Live related labels\n if (!this.isMediaOnDemand()) {\n labels.media_is_live = true;\n labels.media_position = this.getElapsedPlaybackTime();\n }\n\n // DVR related labels\n if (this.isMediaDvr()) {\n labels.media_dvr_window_offset = this.getDvrWindowPosition() | 0;\n labels.media_dvr_window_length = this.getDvrWindowSize() | 0;\n\n labels.media_is_dvr = true;\n\n labels.media_timeshift = [PlayerEvents.PLAY, PlayerEvents.PAUSE].includes(\n eventName\n )\n ? this.timeShifted()\n : 0;\n }\n\n return labels;\n }\n\n /**\n * Set all internal labels to be sent to TagCommander. Internal labels are assigned once at initialisation time.\n */\n getInternalLabels() {\n const data = {\n media_bu_distributer: this.srcMediaData.mediaData.vendor,\n media_chromecast_selected: Boolean(this.player.tech(true).isCasting),\n media_embedding_url: document.referrer,\n media_player_display: 'default', // TODO implement if it still relevant\n media_player_name: 'pillarbox-web', // TODO add a property playerName in the constructor with a default value ?\n media_player_version: this.playerVersion,\n media_url: this.srcMediaData.src,\n };\n const analyticsMetadata =\n this.srcMediaData.mediaData.analyticsMetadata || {};\n\n window.tc_vars = Object.assign({}, window.tc_vars, data, analyticsMetadata);\n }\n\n /**\n * Heart beat, current position of a AoD/VoD (every 30s).\n *\n * @description The action pos should be sent regularly every 30 seconds.\n * It is used for tracking the viewed chapters of a video and the last position of the video, in case the user ends the video by closing the browser tab/window.\n *\n * - pos should be sent when the media player is in \"play mode\".\n * - once the video is paused or stopped, the timer for sending these actions must be stopped.\n *\n * @see https://confluence.srg.beecollaboration.com/display/INTFORSCHUNG/standard+streaming+events%3A+sequence+of+events+for+media+player+actions#standardstreamingevents:sequenceofeventsformediaplayeractions-mandatoryplayerevents\n */\n heartBeat() {\n this.heartBeatIntervalId = setInterval(() => {\n // Send only when playing\n if (!this.player.paused()) {\n this.notify('pos');\n }\n }, 30000);\n }\n\n /**\n * Initialize callbacks used to send analytics data to TagCommander.\n *\n * __Used events__\n * - beforeunload\n * - emptied\n * - ended\n * - loadstart\n * - loadeddata\n * - play\n * - pause\n * - ratechange\n * - seeking\n * - timeupdate\n * - waiting\n */\n initCallbacks() {\n this.beforeunloadListener = this.beforeunload.bind(this);\n this.emptiedListener = this.emptied.bind(this);\n this.endedListener = this.ended.bind(this);\n this.loadstartListener = this.loadstart.bind(this);\n this.loadeddataListener = this.loadeddata.bind(this);\n this.playListener = this.play.bind(this);\n this.pauseListener = this.pause.bind(this);\n this.rateChangeListener = this.rateChange.bind(this);\n this.seekingListener = this.seeking.bind(this);\n this.timeUpdateListener = this.timeUpdate.bind(this);\n this.waitingListener = this.waiting.bind(this);\n }\n\n /**\n * Initialize all listeners used to send analytics data to TagCommander.\n *\n * __Used events__\n * - beforeunload\n * - dispose\n * - emptied\n * - ended\n * - loadstart\n * - loadeddata\n * - play\n * - pause\n * - timeupdate\n * - waiting\n */\n initListeners() {\n this.initCallbacks();\n\n window.addEventListener('beforeunload', this.beforeunloadListener);\n\n this.player.on(PlayerEvents.EMPTIED, this.emptiedListener);\n this.player.on(PlayerEvents.ENDED, this.endedListener);\n this.player.on(PlayerEvents.LOAD_START, this.loadstartListener);\n this.player.on(PlayerEvents.LOADED_DATA, this.loadeddataListener);\n this.player.on(PlayerEvents.PLAYING, this.playListener);\n this.player.on(PlayerEvents.PAUSE, this.pauseListener);\n this.player.on(PlayerEvents.RATE_CHANGE, this.rateChangeListener);\n this.player.on(PlayerEvents.SEEKING, this.seekingListener);\n this.player.on(PlayerEvents.TIME_UPDATE, this.timeUpdateListener);\n this.player.on(PlayerEvents.WAITING, this.waitingListener);\n this.player.one('dispose', this.dispose.bind(this));\n }\n\n /**\n * Initialize TagCommander script dynamically and add it to the DOM\n */\n initScript() {\n const scriptId = 'tc_script__11';\n\n if (!document.querySelector(`#${scriptId}`)) {\n const script = document.createElement('script');\n const src = this.tagCommanderScriptURL;\n\n script.defer = true;\n script.id = scriptId;\n script.src = src;\n script.type = 'text/javascript';\n\n script.onload = (_e) => {\n this.flush();\n };\n\n document.body.appendChild(script);\n }\n }\n\n /**\n * Check if the audio track is enabled.\n *\n * @returns {Boolean} __true__ if enabled __false__ otherwise.\n */\n isAudioTrackEnabled() {\n return !!this.getCurrentAudioTrack();\n }\n\n /**\n * Check if the media is a live with DVR.\n *\n * @returns {Boolean} __true__ if it DVR __false__ otherwise.\n */\n isMediaDvr() {\n const { trackingThreshold } = this.player.liveTracker.options();\n\n return (\n !this.isMediaOnDemand() &&\n trackingThreshold < this.player.liveTracker.liveWindow()\n );\n }\n\n /**\n * Check if the media is a live.\n *\n * @returns {Boolean} __true__ if live __false__ otherwise.\n */\n isMediaLive() {\n const { trackingThreshold } = this.player.liveTracker.options();\n\n return (\n !this.isMediaOnDemand() &&\n trackingThreshold > this.player.liveTracker.liveWindow()\n );\n }\n\n /**\n * Check if the media is an on demand.\n *\n * @returns {Boolean} __true__ if on demand __false__ otherwise.\n */\n isMediaOnDemand() {\n return Number.isFinite(this.player.duration());\n }\n\n /**\n * Check if the text track is enabled.\n *\n * @returns {Boolean} __true__ if enabled __false__ otherwise.\n */\n isTextTrackEnabled() {\n return !!this.getCurrentTextTrack();\n }\n\n /**\n * Check if the tracker is disabled.\n *\n * @returns {Boolean} __true__ if disabled __false__ otherwise.\n */\n isTrackerDisabled() {\n if (!this.srcMediaData || !this.srcMediaData.mediaData)\n return true;\n\n if (!Array.isArray(this.srcMediaData.disableTrackers)) {\n return Boolean(this.srcMediaData.disableTrackers);\n }\n\n return Boolean(\n this.srcMediaData.disableTrackers.find(\n (tracker) => tracker.toLowerCase() === SRGAnalytics.name.toLowerCase()\n )\n );\n }\n\n /**\n * Sent when loading of the media begins.\n *\n * @see https://docs.videojs.com/player#event:loadstart\n */\n loadstart() {\n this.destroy();\n this.updateSrcMediaData(this.player.currentSource());\n\n if (this.isTrackerDisabled()) return;\n\n this.getInternalLabels();\n // Set ComScore labels\n this.reloadTagCommanderContainer();\n\n this.notify('buffer_start');\n this.hasStarted = false;\n }\n\n /**\n * The first frame of the media has finished loading.\n *\n * @see https://docs.videojs.com/player#event:loadeddata\n */\n loadeddata() {\n this.notify('init');\n this.initialized = true;\n\n this.notify('buffer_stop');\n }\n\n /**\n * Event logger that prints the current event, event labels and internal labels in the browser's console.\n *\n * @param {String} eventName init | play | stop | pos | pause | seek | uptime | eof\n * @param {Object} eventMetadata event metadata object\n * @param {String} severity log | warn | error\n */\n log(eventName, eventMetadata, severity = 'log') {\n if (this.debug()) {\n // eslint-disable-next-line\n console[severity](\n `SRGAnalytics:${eventName}`,\n eventMetadata,\n window.tc_vars\n );\n }\n }\n\n /**\n * Notify TagCommander all event and internal labels. If tc script is not available it queues all pending events.\n *\n * @param {String} eventName init | play | stop | pos | pause | seek | uptime | eof\n */\n notify(eventName, eventMetadata) {\n if (this.isTrackerDisabled()) return;\n\n try {\n this.flush();\n } catch (error) {\n this.log(eventName, error, 'error');\n }\n\n const labels = Object.assign(\n {},\n this.getEventLabels(eventName),\n eventMetadata\n );\n\n this.log(eventName, labels);\n\n try {\n if (window.tc_events_11) {\n window.tc_events_11(this.player.el(), eventName, labels);\n } else {\n this.pendingQueue.push({\n action: eventName,\n labels,\n });\n }\n } catch (error) {\n this.log(eventName, error, 'error');\n }\n }\n\n /**\n * Return the current timestamp in seconds.\n *\n * @returns {Number} Timestamp in seconds\n */\n static now() {\n return (Date.now() / 1000).toFixed(0);\n }\n\n /**\n * Sent when the playback state is no longer paused, as a result of the play method, or the autoplay attribute.\n *\n * @see https://docs.videojs.com/player#event:play\n */\n play() {\n if (!this.hasStarted) this.hasStarted = true;\n\n if (!this.startPlaybackSession && !this.isMediaOnDemand()) {\n this.startPlaybackSession = SRGAnalytics.now();\n }\n\n if (this.mediaSession === 0) {\n this.mediaSession = SRGAnalytics.now();\n\n this.heartBeat();\n this.uptime();\n }\n\n this.timeUpdate();\n this.notify('play');\n\n if (this.isSeeking) this.isSeeking = false;\n }\n\n /**\n * Sent when the playback state is changed to paused (paused property is true).\n * Pause event is sent if :\n * - The player is not scrubbing\n * - The stream is not a live only\n * - The current time is strictly inferior to the duration\n *\n * @see https://docs.videojs.com/player#event:pause\n */\n pause() {\n if (!this.isMediaOnDemand()) {\n this.elapsedPlaybackTime = this.getElapsedPlayingTime();\n this.startPlaybackSession = 0;\n }\n\n if (\n !this.player.seeking() &&\n !this.isMediaLive() &&\n this.player.currentTime() < this.player.duration()\n ) {\n this.notify('pause');\n\n return;\n }\n\n if (this.hasStarted && !this.isSeeking) {\n this.notify('seek');\n this.isSeeking = true;\n }\n }\n\n /**\n * Sent to ComScore when the playback rate changes.\n *\n * @see https://github.com/SRGSSR/srgletterbox-web/issues/761\n * @see https://jira.srg.beecollaboration.com/browse/ADI-256\n */\n rateChange() {\n this.notify('change_playback_rate');\n }\n\n /**\n * Reload the tagCommander container and set all ComScore labels\n */\n reloadTagCommanderContainer() {\n if (window.tC) {\n window.tC.container.reload();\n this.pendingTagCommanderReload = false;\n } else {\n this.pendingTagCommanderReload = true;\n }\n }\n\n /**\n * Sent when the current time is modified by the player's currentTime API.\n *\n * @see https://docs.videojs.com/player#event:seeking\n */\n seeking() {\n if (this.hasStarted && !this.player.paused() && !this.isSeeking) {\n this.notify('seek');\n this.isSeeking = true;\n }\n }\n\n /**\n * Track current time updates delayed by a tick.\n *\n * @see https://docs.videojs.com/player#event:timeupdate\n */\n timeUpdate() {\n if (!this.player.paused()) {\n this.trackedCurrentTime = this.player.currentTime();\n }\n }\n\n /**\n * Gets the number of seconds that separate from the live edge that is represented by 0.\n *\n * @returns {String}\n */\n timeShifted() {\n const isAtLiveEdge = this.player.liveTracker.atLiveEdge();\n const liveCurrentTime = this.player.liveTracker.liveCurrentTime();\n const currentTime = this.player.currentTime();\n const timeShifted = isAtLiveEdge\n ? 0\n : (liveCurrentTime - currentTime).toFixed(0);\n\n return timeShifted;\n }\n\n /**\n * Update the src media data.\n */\n updateSrcMediaData(srcMediaData) {\n this.srcMediaData = srcMediaData;\n }\n\n /**\n * Calculate the uptime when playing a live stream with or without DVR\n *\n * __Rules__:\n * - Send the first uptime after 30 seconds\n * - Send uptime each 60 seconds after the 30 seconds\n * - Uptime is sent only when playing\n */\n uptime() {\n const notifyUptime = () => {\n if (!this.player.paused() && !this.isMediaOnDemand()) {\n this.notify('uptime');\n }\n };\n\n // Send the first uptime after 30 seconds\n this.uptimeTimeoutId = setTimeout(() => {\n // Send only when playing\n notifyUptime();\n\n // Initialize the uptime interval after 30 seconds\n this.uptimeIntervalId = setInterval(() => {\n // Send only when playing\n notifyUptime();\n }, 60000);\n }, 30000);\n }\n\n /**\n * __ComScore__:\n * It's expected notifyBufferStart() to be called when the player starts buffering\n * and a call to notifyBufferStop() when content resumes after buffering.\n *\n * @see Item 2: https://jira.srg.beecollaboration.com/browse/PLAY-2628\n *\n * After the issue PLAYRTS-321\n * @see Fix: https://jira.srg.beecollaboration.com/browse/PLAYRTS-321?focusedCommentId=201023&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-201023\n * @see Fix: https://jira.srg.beecollaboration.com/browse/PLAYRTS-3065\n */\n waiting() {\n if (!this.initialized || this.isWaiting) {\n return;\n }\n\n const bufferStop = () => {\n this.isWaiting = false;\n this.notify('buffer_stop');\n };\n\n this.isWaiting = true;\n\n this.notify('buffer_start');\n\n // As Safari is not consistent with its playing event, it is better to use the timeupdate event.\n if (Pillarbox.browser.IS_ANY_SAFARI) {\n this.player.one(PlayerEvents.TIME_UPDATE, bufferStop);\n } else {\n // As Chromium-based browsers are not consistent with their timeupdate event, it is better to use the playing event.\n // Firefox is consistent with its playing event.\n this.player.one(PlayerEvents.PLAYING, bufferStop);\n }\n }\n}\n\nexport default SRGAnalytics;\n","/**\n * Represents the composition of media content.\n *\n * @class MediaComposition\n * @property {string} chapterUrn URN (Uniform Resource Name) of the associated chapter.\n * @property {string} segmentUrn URN of the associated segment.\n * @property {Episode} episode Associated episode.\n * @property {Show} show Associated show.\n * @property {Channel} channel Associated channel.\n * @property {Array.<Chapter>} chapterList List of associated chapters.\n * @property {Array.<Topic>} topicList List of associated topics.\n * @property {Object.<String, String>} analyticsData Analytics data associated with the media composition.\n * @property {Object.<String, String>} analyticsMetadata Metadata associated with analytics for the media composition.\n */\nclass MediaComposition {\n /**\n * Find a chapter by its URN.\n *\n * @param {String} urn\n *\n * @returns {Chapter} chapter\n */\n findChapterByUrn(urn) {\n if (this.chapterList) {\n const [chapter] = this.chapterList.filter(\n (element) => element.urn === urn\n );\n\n return chapter;\n }\n\n return undefined;\n }\n\n /**\n * Return a segment from main chapter following segmentUrn in mediaComposition.\n *\n * @returns {Segment|undefined} main segment\n */\n findMainSegment() {\n if (!this.segmentUrn) {\n return undefined;\n }\n\n const segmentList = this.getMainSegments();\n const [segment] = segmentList.filter(\n (element) => element.urn === this.segmentUrn\n );\n\n return segment;\n }\n\n /**\n * Find resource list by URN.\n *\n * @param {String} urn\n * @returns {Array.<Resource>|undefined} of resources\n */\n findResourceListByUrn(urn) {\n const chapterByUrn = this.findChapterByUrn(urn);\n\n if (chapterByUrn) {\n return chapterByUrn.resourceList || [];\n }\n\n return undefined;\n }\n\n /**\n * A list of chapters.\n *\n * @returns {Array.<Chapter>} of chapters\n */\n getChapters() {\n const AUDIO = 'AUDIO';\n\n if (this.getMainChapter().mediaType === AUDIO) return [];\n\n return this.chapterList.filter(({ mediaType }) => mediaType !== AUDIO);\n }\n\n /**\n * Filter external text tracks that are already available internally.\n *\n * __Rules:__\n * 1. TTML format is filtered\n *\n * 2. If both are empty that means only internal text tracks will be displayed\n * to the user as they are automatically loaded by the player.\n *\n * 3. If subtitleInformationList is missing from the MediaComposition and subtitleList\n * is available but the media contains internal text tracks that are also available internally.\n * It will result on a duplication client side.\n *\n * 4. If subtitleList and subtitleInformationList a merge between both will be operated,\n * removing the external text tracks already available internally.\n *\n *\n * @returns {Array.<Subtitle>} external text tracks\n */\n getFilteredExternalSubtitles() {\n const { subtitleList } = this.getMainChapter();\n const [{ subtitleInformationList } = {}] = this.getResourceList().filter(\n ({ subtitleInformationList }) => subtitleInformationList\n );\n const onlyHasExternalSubtitles = subtitleList && !subtitleInformationList;\n\n if (!subtitleList) {\n return [];\n }\n\n // TTML format is not supported\n const subtitles = subtitleList.filter(\n (subtitle) => subtitle.format !== 'TTML'\n );\n\n if (onlyHasExternalSubtitles) {\n return subtitles;\n }\n\n return subtitles.filter((subtitle) => {\n const addSubtitle = !subtitleInformationList.find(\n (subtitleInformation) =>\n subtitleInformation.locale === subtitle.locale &&\n subtitle.type === subtitleInformation.type\n );\n\n return addSubtitle;\n });\n }\n\n /**\n * Block reason for main chapter. This also uses current date for STARTDATE.\n *\n * @see BlockReason\n *\n * @returns {string | undefined} undefined if main chapter is not blocked\n */\n getMainBlockReason() {\n const mainChapter = this.getMainChapter();\n\n if (!mainChapter) {\n return undefined;\n }\n\n let { blockReason } = mainChapter;\n\n if (!blockReason && new Date() < this.getMainValidFromDate()) {\n blockReason = 'STARTDATE';\n }\n\n return blockReason;\n }\n\n /**\n * Get blocked segments from the main chapter.\n *\n * @returns {Array.<Segment>} of blocked segments\n */\n getMainBlockedSegments() {\n return this.getMainSegments().filter(segment => segment.blockReason);\n }\n\n /**\n * Get the mediaComposition's main chapter.\n *\n * @returns {Chapter}\n */\n getMainChapter() {\n if (!this.mainChapter) {\n this.mainChapter = this.findChapterByUrn(this.chapterUrn);\n }\n\n if (!this.mainChapter && this.chapterList && this.chapterList.length > 0) {\n [this.mainChapter] = this.chapterList;\n }\n\n return this.mainChapter;\n }\n\n /**\n * Get the main chapter's image URL decorated with default width and format.\n *\n * @returns {String|undefined} image URL\n */\n getMainChapterImageUrl() {\n const mainChapter = this.getMainChapter();\n\n if (!mainChapter || !mainChapter.imageUrl) {\n return undefined;\n }\n\n return mainChapter.imageUrl;\n }\n\n /**\n * Get main resources.\n *\n * @returns {Array.<MainResource>} array of sources.\n */\n // eslint-disable-next-line max-lines-per-function\n getMainResources() {\n const resourceList = this.getResourceList();\n\n if (!resourceList || !resourceList.length) {\n return undefined;\n }\n\n return resourceList.map((resource) => ({\n analyticsData: this.getMergedAnalyticsData(resource.analyticsData),\n analyticsMetadata: this.getMergedAnalyticsMetadata(\n resource.analyticsMetadata\n ),\n blockReason: this.getMainChapter().blockReason,\n blockedSegments: this.getMainBlockedSegments(),\n imageUrl: this.getMainChapterImageUrl(),\n chapters: this.getChapters(),\n drmList: resource.drmList,\n dvr: resource.dvr,\n eventData: this.getMainChapter().eventData,\n id: this.getMainChapter().id,\n imageCopyright: this.getMainChapter().imageCopyright,\n intervals: this.getMainTimeIntervals(),\n live: resource.live,\n mediaType: this.getMainChapter().mediaType,\n mimeType: resource.mimeType,\n presentation: resource.presentation,\n quality: resource.quality,\n streaming: resource.streaming,\n streamOffset: resource.streamOffset,\n subtitles: this.getFilteredExternalSubtitles(),\n title: this.getMainChapter().title,\n tokenType: resource.tokenType,\n url: resource.url,\n urn: this.chapterUrn,\n vendor: this.getMainChapter().vendor,\n }));\n }\n\n /**\n * Get segments of the main chapter ordered by markIn.\n *\n * @returns {Array.<Segment>} of segments\n */\n getMainSegments() {\n const mainChapter = this.getMainChapter();\n\n if (!this.mainSegments && mainChapter && mainChapter.segmentList) {\n this.mainSegments = mainChapter.segmentList;\n }\n\n return this.mainSegments || [];\n }\n\n /**\n * Retrieves an array of time intervals associated with the main chapter.\n *\n * @returns {Array.<TimeInterval>} An array of time intervals.\n */\n getMainTimeIntervals() {\n const {\n timeIntervalList = []\n } = this.getMainChapter() || {};\n\n return timeIntervalList;\n }\n\n /**\n * Compute a date from which this content is valid. Always return a date object.\n *\n * @returns {Date} date specified in media composition or EPOCH when no date present.\n */\n getMainValidFromDate() {\n const mainChapter = this.getMainChapter();\n\n if (!mainChapter) {\n return new Date(0);\n }\n\n const { validFrom } = mainChapter;\n\n if (validFrom) {\n return new Date(validFrom);\n }\n }\n\n /**\n * Get merged analytics data.\n *\n * @param {Object.<string, string>} analyticsData\n * @returns {Object.<string, string>} Merged analytics data.\n */\n getMergedAnalyticsData(analyticsData) {\n return {\n ...this.analyticsData,\n ...this.getMainChapter().analyticsData,\n ...analyticsData,\n };\n }\n\n /**\n * Get merged analytics metadata.\n *\n * @param {Object.<string, string>} analyticsMetadata\n * @returns {Object.<string, string>} Merged analytics metadata.\n */\n getMergedAnalyticsMetadata(analyticsMetadata) {\n return {\n ...this.analyticsMetadata,\n ...this.getMainChapter().analyticsMetadata,\n ...analyticsMetadata,\n };\n }\n\n /**\n * Get the chapter's resource list\n * @returns {Array.<Resource>} of resources\n */\n getResourceList() {\n const { resourceList } = this.getMainChapter();\n\n return resourceList || [];\n }\n}\n\nexport default MediaComposition;\n\n\n/**\n * @typedef {import('./typedef').Channel} Channel\n * @typedef {import('./typedef').Chapter} Chapter\n * @typedef {import('./typedef').Episode} Episode\n * @typedef {import('./typedef').Resource} Resource\n * @typedef {import('./typedef').Segment} Segment\n * @typedef {import('./typedef').Show} Show\n * @typedef {import('./typedef').Subtitle} Subtitle\n * @typedef {import('./typedef').TimeInterval} TimeInterval\n * @typedef {import('./typedef').Topic} Topic\n * @typedef {import('./typedef').MainResource} MainResource\n */\n","import Pillarbox from '../pillarbox.js';\nimport * as vjsLang from 'video.js/dist/lang/de.json';\nimport * as pillarboxLang from './de.json';\n\nPillarbox.addLanguage('de', {\n ...vjsLang,\n ...pillarboxLang,\n});\n","import Pillarbox from '../pillarbox.js';\nimport * as vjsLang from 'video.js/dist/lang/en.json';\nimport * as pillarboxLang from './en.json';\n\nPillarbox.addLanguage('en', {\n ...vjsLang,\n ...pillarboxLang,\n});\n","import Pillarbox from '../pillarbox.js';\nimport * as vjsLang from 'video.js/dist/lang/fr.json';\nimport * as pillarboxLang from './fr.json';\n\nPillarbox.addLanguage('fr', {\n ...vjsLang,\n ...pillarboxLang,\n});\n","import Pillarbox from '../pillarbox.js';\nimport * as vjsLang from 'video.js/dist/lang/it.json';\nimport * as pillarboxLang from './it.json';\n\nPillarbox.addLanguage('it', {\n ...vjsLang,\n ...pillarboxLang,\n});\n","import Pillarbox from '../pillarbox.js';\nimport * as pillarboxLang from './rm.json';\n\nPillarbox.addLanguage('rm', {\n ...pillarboxLang,\n});\n","import Pillarbox from '../../src/pillarbox.js';\nimport DataProvider from '../dataProvider/services/DataProvider.js';\nimport Image from '../utils/Image.js';\nimport Drm from '../utils/Drm.js';\nimport AkamaiTokenService from '../utils/AkamaiTokenService.js';\nimport SRGAnalytics from '../analytics/SRGAnalytics.js';\nimport MediaComposition from '../dataProvider/model/MediaComposition.js';\n\n// Translations\nimport '../lang/de.js';\nimport '../lang/en.js';\nimport '../lang/fr.js';\nimport '../lang/it.js';\nimport '../lang/rm.js';\n\n/**\n * @class SrgSsr\n */\nclass SrgSsr {\n /**\n * Adds blocked segments to the player.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {Array<import('../dataProvider/model/typedef').Segment>} [segments=[]]\n */\n static addBlockedSegments(player, segments = []) {\n const trackId = 'srgssr-blocked-segments';\n const removeTrack = player.textTracks().getTrackById(trackId);\n\n if (removeTrack) {\n player.textTracks().removeTrack(removeTrack);\n }\n\n if (!Array.isArray(segments) || !segments.length) return;\n\n const blockedSegments = segments.filter(segment => segment.blockReason);\n\n if (!blockedSegments.length) return;\n\n SrgSsr.createTextTrack(player, trackId).then(segmentTrack => {\n blockedSegments.forEach(segment => {\n SrgSsr.addTextTrackCue(segmentTrack, segment);\n });\n\n player.textTracks().addTrack(segmentTrack);\n });\n }\n\n /**\n * Adds remote text tracks from an array of subtitles.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {Array<import('../dataProvider/model/typedef').Subtitle>} [subtitles=[]]\n */\n static addRemoteTextTracks(player, subtitles = []) {\n if (!Array.isArray(subtitles)) return;\n\n subtitles.forEach(({\n type,\n language: label,\n locale: language,\n url: src\n }) => {\n player.addRemoteTextTrack({\n kind: type === 'SDH' ? 'captions' : 'subtitles',\n label,\n language,\n src\n });\n });\n }\n\n /**\n * Add a new cue to a text track with the given data.\n *\n * @param {TextTrack} textTrack\n * @param {\n * import('../dataProvider/model/typedef').Segment |\n * import('../dataProvider/model/typedef').Chapter |\n * import('../dataProvider/model/typedef').TimeInterval\n * } data SRG SSR's cue-like representation\n */\n static addTextTrackCue(textTrack, data) {\n const startTime = (Number.isFinite(data.markIn)\n ? data.markIn : data.fullLengthMarkIn) / 1_000;\n const endTime = (Number.isFinite(data.markOut)\n ? data.markOut : data.fullLengthMarkOut) / 1_000;\n\n textTrack.addCue(new VTTCue(\n startTime,\n endTime,\n JSON.stringify(data)\n ));\n }\n\n /**\n * Add multiple text tracks to the player.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {import('./typedef').ComposedSrcMediaData} srcMediaObj\n */\n static addTextTracks(player, { mediaData }) {\n SrgSsr.addRemoteTextTracks(player, mediaData.subtitles);\n SrgSsr.addChapters(player, mediaData.urn, mediaData.chapters);\n SrgSsr.addBlockedSegments(player, mediaData.blockedSegments);\n SrgSsr.addIntervals(player, mediaData.intervals);\n }\n\n /**\n * Adds chapters to the player.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {string} chapterUrn The URN of the main chapter.\n * @param {Array.<import('../dataProvider/model/typedef').Chapter>} [chapters=[]]\n */\n static addChapters(player, chapterUrn, chapters = []) {\n const trackId = 'srgssr-chapters';\n const removeTrack = player.textTracks().getTrackById(trackId);\n\n if (removeTrack) {\n player.textTracks().removeTrack(removeTrack);\n }\n\n if (!Array.isArray(chapters) || !chapters.length) return;\n\n SrgSsr.createTextTrack(player, trackId).then(chapterTrack => {\n chapters.forEach(chapter => {\n if (chapterUrn !== chapter.fullLengthUrn) return;\n\n SrgSsr.addTextTrackCue(chapterTrack, chapter);\n });\n\n player.textTracks().addTrack(chapterTrack);\n });\n }\n\n /**\n * Adds intervals to the player.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {Array.<import('../dataProvider/model/typedef').TimeInterval>} [intervals=[]]\n */\n static addIntervals(player, intervals = []) {\n const trackId = 'srgssr-intervals';\n const removeTrack = player.textTracks().getTrackById(trackId);\n\n if (removeTrack) {\n player.textTracks().removeTrack(removeTrack);\n }\n\n if (!Array.isArray(intervals) || !intervals.length) return;\n\n SrgSsr.createTextTrack(player, trackId).then(intervalTrack => {\n intervals.forEach(interval => {\n SrgSsr.addTextTrackCue(intervalTrack, interval);\n });\n\n player.textTracks().addTrack(intervalTrack);\n });\n }\n\n /**\n * Set a blocking reason according to the block reason returned\n * by mediaData.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {import('./typedef').ComposedSrcMediaData} srcMediaObj\n *\n * @returns {undefined|Boolean}\n */\n static blockingReason(player, srcMediaObj) {\n if (!srcMediaObj.mediaData.blockReason) return;\n\n const message = player.localize(srcMediaObj.mediaData.blockReason);\n\n SrgSsr.error(player, {\n code: MediaError.MEDIA_ERR_ABORTED,\n message,\n metadata: {\n errorType: srcMediaObj.mediaData.blockReason,\n src: srcMediaObj\n },\n });\n\n return true;\n }\n\n /**\n * Add the Akamai token to all resources\n * if at least one of them has tokenType\n * set to Akamai.\n *\n * @param {Array.<import('./typedef').MainResourceWithKeySystems>} resources\n *\n * @returns {Promise<Array.<import('./typedef').MainResourceWithKeySystems>>}\n */\n static async composeAkamaiResources(resources = []) {\n if (!AkamaiTokenService.hasToken(resources)) {\n return Promise.resolve(resources);\n }\n\n // TODO allow to modify the Akamai URL\n return AkamaiTokenService.tokenizeSources(resources);\n }\n\n /**\n * Add the keySystems property to all resources\n * if at least one of them has DRM.\n *\n * @param {Array.<import('../dataProvider/model/typedef').MainResource>} resources\n *\n * @returns {Array.<import('./typedef').MainResourceWithKeySystems>}\n */\n static composeKeySystemsResources(resources = []) {\n if (!Drm.hasDrm(resources)) resources;\n\n return resources.map((resource) => ({\n ...resource,\n ...Drm.buildKeySystems(resource.drmList),\n }));\n }\n\n /**\n * Get the main resources from a mediaComposition.\n * May add an Akamai token or key systems if required by the resource.\n *\n * @param {MediaComposition} mediaComposition\n *\n * @returns {Promise<Array.<import('./typedef').MainResourceWithKeySystems>>}\n */\n static composeMainResources(mediaComposition) {\n return SrgSsr.composeAkamaiResources(\n SrgSsr.composeKeySystemsResources(\n SrgSsr.filterIncompatibleResources(mediaComposition.getMainResources())\n )\n );\n }\n\n /**\n * Compose source options with media data.\n * MediaData properties from source options overwrite mediaData from IL.\n *\n * @param {any} srcObj\n * @param {any} srcObj.mediaData overrides or adds metadata to the composed mediaData.\n * @param {boolean} srcObj.disableTrackers\n * @param {import('./typedef').MainResourceWithKeySystems} resource\n *\n * @returns {import('./typedef').ComposedSrcMediaData}\n */\n static composeSrcMediaData(\n { mediaData: srcMediaData, disableTrackers },\n resource\n ) {\n const {\n url,\n mimeType,\n keySystems,\n ...mediaData\n } = Pillarbox.obj.merge(resource, srcMediaData);\n\n return {\n src: url,\n type: mimeType,\n keySystems,\n disableTrackers,\n mediaData,\n };\n }\n\n /**\n * Create a new metadata text track.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {String} trackId Text track unique ID\n *\n * @returns {Promise<TextTrack>}\n */\n static createTextTrack(player, trackId) {\n // See https://github.com/videojs/video.js/issues/8519\n return new Promise((resolve) => {\n setTimeout(() => {\n resolve(new Pillarbox.TextTrack({\n id: trackId,\n kind: 'metadata',\n label: trackId,\n tech: player.tech(true),\n }));\n }, 100);\n });\n }\n\n /**\n * Proxy SRG SSR chapters and intervals cuechange events at player level.\n *\n * @param {import('video.js/dist/types/player').default} player\n */\n static cuechangeEventProxy(player) {\n player.textTracks().on('addtrack', ({ track }) => {\n if (!['srgssr-chapters', 'srgssr-intervals'].includes(track.id)) return;\n\n track.on('cuechange', () => {\n const [cue] = Array.from(track.activeCues);\n const type = track.id.includes('srgssr-chapters') ? 'srgssr/chapter' : 'srgssr/interval';\n\n player.trigger({ type, data: cue });\n });\n });\n }\n\n /**\n * SRG SSR data provider singleton.\n *\n * @param {import('video.js/dist/types/player').default} player\n *\n * @returns {DataProvider}\n */\n static dataProvider(player) {\n if (!player.options().srgOptions.dataProvider) {\n const {\n dataProviderHost,\n dataProviderUrlHandler\n } = player.options().srgOptions;\n const dataProvider = new DataProvider(dataProviderHost);\n const requestHandler = dataProvider\n .handleRequest(dataProviderUrlHandler);\n\n player.options({\n srgOptions: {\n dataProvider: requestHandler,\n },\n });\n }\n\n return player.options().srgOptions.dataProvider;\n }\n\n /**\n * Set an error if something goes wrong with the data provider.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {Object} error\n *\n * @returns {undefined|true}\n */\n static dataProviderError(player, error) {\n if (!error) return;\n\n const statusText = error.statusText ? error.statusText : error.message;\n\n SrgSsr.error(player, {\n code: 0,\n message: player.localize('UNKNOWN'),\n metadata: {\n errorType: 'UNKNOWN',\n urn: player.src(),\n status: error.status,\n statusText,\n url: error.url,\n },\n });\n\n return true;\n }\n\n /**\n * Set player error.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {Object} error\n */\n static error(player, { code, message, metadata }) {\n player.error(null);\n\n player.error({\n code,\n message,\n metadata,\n });\n }\n\n /**\n * Filter out incompatible resources such as `RTMP` and `HDS`.\n *\n * @param {Array.<import('../dataProvider/model/typedef').MainResource>} resources Resources to filter\n *\n * @returns {Array.<import('../dataProvider/model/typedef').MainResource>} The filtered resources\n */\n static filterIncompatibleResources(resources = []) {\n return resources.filter(\n (resource) => !['RTMP', 'HDS'].includes(resource.streaming)\n );\n }\n\n /**\n * Get the current blocked segment from the player.\n *\n * @param {import('video.js/dist/types/player').default} player\n *\n * @returns {VTTCue|undefined} The blocked segment cue object, or undefined\n */\n static getBlockedSegment(player) {\n const trackId = 'srgssr-blocked-segments';\n const blockedSegmentsTrack = player.textTracks().getTrackById(trackId);\n\n if (!blockedSegmentsTrack) return;\n\n /** @type {VTTCue} */\n const [blockedCue] = Array.from(blockedSegmentsTrack.activeCues);\n\n return blockedCue;\n }\n\n /**\n * Get the VTT cue of a blocked segment.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {Number} currentTime\n *\n * @returns {VTTCue|undefined} The VTT cue of a blocked segment, or undefined\n */\n static getBlockedSegmentByTime(player, currentTime) {\n const blockedSegment = SrgSsr.getBlockedSegment(player);\n\n if (!blockedSegment) return;\n\n const isBlocked = currentTime >= blockedSegment.startTime &&\n currentTime < blockedSegment.endTime;\n\n return isBlocked ? blockedSegment : undefined;\n }\n\n /**\n * Get mediaComposition from an URN.\n *\n * @param {String} urn\n * @param {Function} requestHandler\n *\n * @returns {Promise<MediaComposition>}\n */\n static async getMediaComposition(\n urn,\n handleRequest = new DataProvider().handleRequest()\n ) {\n const data = await handleRequest(urn);\n\n return Object.assign(new MediaComposition(), data);\n }\n\n /**\n * Get the mediaData most likely to be compatible depending on the browser.\n *\n * @param {Array.<import('./typedef').MainResourceWithKeySystems>} resources\n *\n * @returns {import('./typedef').MainResourceWithKeySystems} By default, the first entry is used if none is compatible.\n */\n static getMediaData(resources = []) {\n if (AkamaiTokenService.hasToken(resources)) return resources[0];\n\n const type = Pillarbox.browser.IS_ANY_SAFARI ? 'HLS' : 'DASH';\n const resource = resources.find(({ streaming }) => streaming === type);\n\n return resource || resources[0];\n }\n\n /**\n * Get the source media object.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {any} srcObj\n *\n * @returns {Promise<import('./typedef').ComposedSrcMediaData>} - The composed source media data.\n */\n static async getSrcMediaObj(player, srcObj) {\n const { src: urn, ...srcOptions } = srcObj;\n const mediaComposition = await SrgSsr.getMediaComposition(\n urn,\n SrgSsr.dataProvider(player)\n );\n const mainResources = await SrgSsr.composeMainResources(\n mediaComposition\n );\n const mediaData = SrgSsr.getMediaData(mainResources);\n\n return SrgSsr.composeSrcMediaData(srcOptions, mediaData);\n }\n\n /**\n * Handles the middleware's currentTime function.\n * - If the current time is between the start and end of a blocked segment,\n * the blocked portion will be skipped.\n *\n * _Note_: This function should disappear as soon as this behavior is\n * supported on the packaging side.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {number} currentTime\n *\n * @returns {number}\n */\n static handleCurrentTime(player, currentTime) {\n const blockedSegment = SrgSsr\n .getBlockedSegmentByTime(player, currentTime);\n\n if (!blockedSegment || !Number.isFinite(blockedSegment.endTime)) {\n return currentTime;\n }\n\n // as a workaround, add 0.1 seconds to avoid getting stuck on endTime on\n // some safaris.\n const endTimeWithTolerance = blockedSegment.endTime + 0.1;\n\n // proxy for handling cuechange events at the player level\n player.trigger({ type: 'srgssr/blocked-segment', data: blockedSegment });\n player.currentTime(endTimeWithTolerance);\n\n return endTimeWithTolerance;\n }\n\n /**\n * Handles the middleware's setCurrentTime function.\n * - Modify the current time if the value is between the start and end of a\n * blocked segment.\n *\n * _Note_: This function should disappear as soon as this behavior is\n * supported on the packaging side.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {number} currentTime\n *\n * @returns {number}\n */\n static handleSetCurrentTime(player, currentTime) {\n const { endTime: blockedSegmentEndTime } = SrgSsr\n .getBlockedSegmentByTime(player, currentTime) || {};\n\n return Number\n .isFinite(blockedSegmentEndTime) ? blockedSegmentEndTime : currentTime;\n }\n\n /**\n * Handles the middleware's setSource function.\n *\n * This function allows to:\n * - resolve a URN into media that can be played by the player\n * - initialize media playback tracking\n * - update the title bar\n * - handle blocking reasons\n * - add remote subtitles\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {any} srcObj\n * @param {function} next\n *\n * @returns {Promise<any>}\n */\n static async handleSetSource(player, srcObj, next) {\n try {\n const srcMediaObj = await SrgSsr.getSrcMediaObj(player, srcObj);\n\n SrgSsr.srgAnalytics(player);\n SrgSsr.updateTitleBar(player, srcMediaObj);\n SrgSsr.updatePoster(player, srcMediaObj);\n\n if (SrgSsr.blockingReason(player, srcMediaObj)) return;\n\n SrgSsr.addTextTracks(player, srcMediaObj);\n\n return next(null, srcMediaObj);\n } catch (error) {\n if (SrgSsr.dataProviderError(player, error)) return;\n\n return next(error);\n }\n }\n\n /**\n * SRG SSR analytics singleton.\n *\n * @param {import('video.js/dist/types/player').default} player\n */\n static srgAnalytics(player) {\n if (player.options().trackers.srgAnalytics === false) return;\n\n if (!player.options().trackers.srgAnalytics) {\n const srgAnalytics = new SRGAnalytics(player, {\n debug: player.debug(),\n playerVersion: Pillarbox.VERSION.pillarbox,\n tagCommanderScriptURL:\n player.options().srgOptions.tagCommanderScriptURL,\n });\n\n player.options({\n trackers: {\n srgAnalytics,\n },\n });\n }\n }\n\n /**\n * Update player's poster.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {import('./typedef').ComposedSrcMediaData} srcMediaObj\n * @param {Image} imageService\n */\n static updatePoster(player, srcMediaObj, imageService = Image) {\n player.poster(\n imageService.scale({\n url: srcMediaObj.mediaData.imageUrl,\n })\n );\n }\n\n /**\n * Update player titleBar with title and description.\n *\n * @param {import('video.js/dist/types/player').default} player\n * @param {import('./typedef').ComposedSrcMediaData} srcMediaObj\n */\n static updateTitleBar(player, srcMediaObj) {\n if (!player.titleBar) return;\n\n player.titleBar.update({\n title: srcMediaObj.mediaData.vendor,\n description: srcMediaObj.mediaData.title,\n });\n }\n\n /**\n * Middleware to resolve SRG SSR URNs into playable media.\n *\n * @param {import('video.js/dist/types/player').default} player\n *\n * @returns {Object}\n */\n static middleware(player) {\n\n SrgSsr.cuechangeEventProxy(player);\n\n return {\n currentTime: (currentTime) =>\n SrgSsr.handleCurrentTime(player, currentTime),\n setCurrentTime: (currentTime) =>\n SrgSsr.handleSetCurrentTime(player, currentTime),\n setSource: async (srcObj, next) =>\n SrgSsr.handleSetSource(player, srcObj, next),\n };\n }\n}\n\nPillarbox.use('srgssr/urn', SrgSsr.middleware);\n\n// Add Middleware specific options\nPillarbox.options.srgOptions = {\n dataProvider: undefined,\n dataProviderHost: undefined,\n dataProviderUrlHandler: undefined,\n tagCommanderScriptURL: undefined,\n};\n\nexport default SrgSsr;\n"],"names":["vjsPlayer","videojs","getComponent","registerComponent","constructor","tag","options","ready","super","obj","merge","plugins","eme","audioTrack","trackSelector","audioTracks","Array","from","this","player","find","enabled","kind","language","bufferedRanges","ranges","i","buffered","length","start","end","push","playedPercent","Number","isFinite","duration","NaN","timePlayed","played","playedRanges","seekableRanges","seekable","textTrack","textTracks","filter","includes","mode","forEach","pillarbox","VERSION","VhsSourceHandler","name","getPlugin","enableSmoothSeeking","fill","html5","vhs","useForcedSubtitles","liveTracker","trackingThreshold","liveTolerance","liveui","playsinline","responsive","trackers","DataProvider","hostName","setIlHost","baseUrl","handleRequest","urlHandler","_this","_ref","_asyncToGenerator","urn","url","mediaCompositionUrlHandler","response","fetch","ok","json","_x","apply","arguments","SCALE","FORMAT","Image","scale","width","format","imageServiceUrl","scaleUrl","URL","searchParams","set","decodeURIComponent","href","JPG","PNG","WEBP","WIDTH_240","WIDTH_320","WIDTH_480","WIDTH_960","WIDTH_1920","DRM_VENDORS","WIDEVINE","FAIRPLAY","PLAYREADY","Drm","buildKeySystems","drmList","keySystems","drmVendor","type","vendors","certificateUrl","certificateUri","licenseUrl","licenseUri","hasDrm","resources","some","TOKEN_TYPES","AkamaiTokenService","aclPath","streamUrl","path","pathname","substring","lastIndexOf","AKAMAI","hasToken","resource","isAkamai","tokenType","tokentype","isNone","NONE","tokenize","source","tokenServerUrl","streamUrlToTokenize","acl","encodeURIComponent","then","Promise","reject","status","statusText","token","authparams","URLSearchParams","v","k","Object","assign","toString","catch","reason","tokenizeSources","sources","tokenizedSources","tokenizedSource","all","values","EMPTIED","ENDED","LOADED_DATA","LOAD_START","PAUSE","PLAY","PLAYING","RATE_CHANGE","SEEKING","TIME_UPDATE","WAITING","SRGAnalytics","debug","environment","playerVersion","tagCommanderScriptURL","isDebugEnabled","elapsedPlaybackTime","hasStarted","heartBeatIntervalId","undefined","initialized","isSeeking","isWaiting","mediaSession","pendingQueue","pendingTagCommanderReload","srcMediaData","startPlaybackSession","trackedCurrentTime","uptimeIntervalId","initScript","initListeners","beforeunload","notify","clearTimers","clearInterval","clearTimeout","uptimeTimeoutId","currentTime","Math","round","Boolean","destroy","window","tc_vars","dispose","removeEventListener","beforeunloadListener","off","PlayerEvents","emptiedListener","endedListener","loadstartListener","loadeddataListener","playListener","pauseListener","rateChangeListener","seekingListener","timeUpdateListener","waitingListener","emptied","ended","flush","isTrackerDisabled","tC","container","reload","tc_events_11","notification","el","action","labels","getCurrentAudioTrack","currentTrack","track","toUpperCase","getCurrentTextTrack","getDvrWindowPosition","ct","seekableStart","position","liveWindow","Infinity","getDvrWindowSize","isInfinity","windowSize","getElapsedPlaybackTime","getElapsedPlayingTime","playingSession","now","getEventLabels","eventName","event_id","event_timestamp","media_dvr_window_length","media_dvr_window_offset","media_is_dvr","media_is_live","media_mute","muted","media_playback_rate","playbackRate","media_position","media_quality","mediaData","quality","media_subtitles_on","isTextTrackEnabled","media_volume","volume","toFixed","navigation_environment","isAudioTrackEnabled","media_audio_track","media_subtitle_selection","isMediaOnDemand","isMediaDvr","media_timeshift","timeShifted","getInternalLabels","data","media_bu_distributer","vendor","media_chromecast_selected","tech","isCasting","media_embedding_url","document","referrer","media_player_display","media_player_name","media_player_version","media_url","src","analyticsMetadata","heartBeat","setInterval","paused","initCallbacks","bind","loadstart","loadeddata","play","pause","rateChange","seeking","timeUpdate","waiting","addEventListener","on","one","scriptId","querySelector","script","createElement","defer","id","onload","_e","body","appendChild","isMediaLive","isArray","disableTrackers","tracker","toLowerCase","updateSrcMediaData","currentSource","reloadTagCommanderContainer","log","eventMetadata","severity","console","error","Date","uptime","isAtLiveEdge","atLiveEdge","liveCurrentTime","notifyUptime","setTimeout","bufferStop","Pillarbox","browser","IS_ANY_SAFARI","MediaComposition","findChapterByUrn","chapterList","chapter","element","findMainSegment","segmentUrn","segmentList","getMainSegments","segment","findResourceListByUrn","chapterByUrn","resourceList","getChapters","AUDIO","getMainChapter","mediaType","getFilteredExternalSubtitles","subtitleList","subtitleInformationList","getResourceList","onlyHasExternalSubtitles","subtitles","subtitle","subtitleInformation","locale","getMainBlockReason","mainChapter","blockReason","getMainValidFromDate","getMainBlockedSegments","chapterUrn","getMainChapterImageUrl","imageUrl","getMainResources","map","analyticsData","getMergedAnalyticsData","getMergedAnalyticsMetadata","blockedSegments","chapters","dvr","eventData","imageCopyright","intervals","getMainTimeIntervals","live","mimeType","presentation","streaming","streamOffset","title","mainSegments","timeIntervalList","validFrom","_objectSpread","addLanguage","vjsLang","pillarboxLang","SrgSsr","addBlockedSegments","segments","trackId","removeTrack","getTrackById","createTextTrack","segmentTrack","addTextTrackCue","addTrack","addRemoteTextTracks","label","addRemoteTextTrack","startTime","markIn","fullLengthMarkIn","endTime","markOut","fullLengthMarkOut","addCue","VTTCue","JSON","stringify","addTextTracks","addChapters","addIntervals","chapterTrack","fullLengthUrn","intervalTrack","interval","blockingReason","srcMediaObj","message","localize","code","MediaError","MEDIA_ERR_ABORTED","metadata","errorType","composeAkamaiResources","resolve","composeKeySystemsResources","composeMainResources","mediaComposition","filterIncompatibleResources","composeSrcMediaData","_Pillarbox$obj$merge","_objectWithoutProperties","_excluded","TextTrack","cuechangeEventProxy","cue","activeCues","trigger","dataProvider","srgOptions","dataProviderHost","dataProviderUrlHandler","requestHandler","dataProviderError","getBlockedSegment","blockedSegmentsTrack","blockedCue","getBlockedSegmentByTime","blockedSegment","getMediaComposition","getMediaData","getSrcMediaObj","srcObj","srcOptions","_excluded2","mainResources","handleCurrentTime","endTimeWithTolerance","handleSetCurrentTime","blockedSegmentEndTime","handleSetSource","next","srgAnalytics","updateTitleBar","updatePoster","imageService","poster","titleBar","update","description","middleware","setCurrentTime","setSource","_x2","use"],"mappings":"qvDAOA,MAAMA,EAAYC,EAAQC,aAAa,UAwNvCD,EAAQE,kBAAkB,SAhN1B,cAAqBH,EACnBI,WAAAA,CAAYC,EAAKC,EAASC,GASxBC,MAAMH,EADNC,EAAUL,EAAQQ,IAAIC,MAAMJ,EAAS,CAAEK,QAAS,CAAEC,KAAK,KACnCL,EACtB,CA2BAM,UAAAA,CAAWC,GACT,MAAMC,EAAcC,MAAMC,KAAKC,KAAKC,SAASJ,eAE7C,IAAKD,EACH,OAAOC,EAAYK,MAAMP,GAAeA,EAAWQ,UAGrD,MAAMC,KAAEA,EAAIC,SAAEA,GAAaT,EACrBD,EACJE,EAAYK,MACTP,GACCA,EAAWU,WAAaA,GAAYV,EAAWS,OAASA,KACvDP,EAAYK,MAAMP,GAAeA,EAAWU,WAAaA,IAMhE,OAJIV,IACFA,EAAWQ,SAAU,GAGhBR,CACT,CASAW,cAAAA,GACE,MAAMC,EAAS,GAEf,IAAK,IAAIC,EAAI,EAAGA,EAAIR,KAAKS,WAAWC,OAAQF,IAAK,CAC/C,MAAMG,EAAQX,KAAKS,WAAWE,MAAMH,GAC9BI,EAAMZ,KAAKS,WAAWG,IAAIJ,GAEhCD,EAAOM,KAAK,CAAEF,QAAOC,OACvB,CAEA,OAAOL,CACT,CAcAO,aAAAA,GACE,IAAKC,OAAOC,SAAShB,KAAKiB,YAAa,OAAOC,IAE9C,IAAIC,EAAa,EAEjB,IAAK,IAAIX,EAAI,EAAGA,GAAKR,KAAKoB,SAASV,OAAQF,IACzCW,GAAcnB,KAAKoB,SAASR,IAAIJ,GAAKR,KAAKoB,SAAST,MAAMH,GAK3D,OAFsBW,EAAanB,KAAKiB,UAG1C,CASAI,YAAAA,GACE,MAAMd,EAAS,GAEf,IAAK,IAAIC,EAAI,EAAGA,EAAIR,KAAKoB,SAASV,OAAQF,IAAK,CAC7C,MAAMG,EAAQX,KAAKoB,SAAST,MAAMH,GAC5BI,EAAMZ,KAAKoB,SAASR,IAAIJ,GAE9BD,EAAOM,KAAK,CAAEF,QAAOC,OACvB,CAEA,OAAOL,CACT,CASAe,cAAAA,GACE,MAAMf,EAAS,GAEf,IAAK,IAAIC,EAAI,EAAGA,EAAIR,KAAKuB,WAAWb,OAAQF,IAAK,CAC/C,MAAMG,EAAQX,KAAKuB,WAAWZ,MAAMH,GAC9BI,EAAMZ,KAAKuB,WAAWX,IAAIJ,GAEhCD,EAAOM,KAAK,CAAEF,QAAOC,OACvB,CAEA,OAAOL,CACT,CAiCAiB,SAAAA,CAAU5B,GACR,MAAM6B,EAAa3B,MAAMC,KAAKC,KAAKC,SAASwB,cAAcC,QACvDF,IAAe,CAAC,WAAY,YAAYG,SAASH,EAAUpB,QAG9D,IAAKR,EACH,OAAO6B,EAAWvB,MAAMsB,GAAiC,YAAnBA,EAAUI,OAGlDH,EAAWI,SAASL,GAAeA,EAAUI,KAAO,aAEpD,MAAMxB,KAAEA,EAAIC,SAAEA,GAAaT,EAiB3B,OAfE6B,EAAWvB,MAAMsB,IACXA,EAAUnB,WAAaA,GAAYmB,EAAUpB,OAASA,IACxDoB,EAAUI,KAAO,WAGO,YAAnBJ,EAAUI,SAEnBH,EAAWvB,MAAMsB,IACXA,EAAUnB,WAAaA,IACzBmB,EAAUI,KAAO,WAGO,YAAnBJ,EAAUI,OAIvB,ICjNIE,MAAAA,EAAY/C,EAElB+C,EAAUC,QAAU,CAClBD,mBACA/C,QAASA,EAAQgD,QACjB,CAAChD,EAAQiD,iBAAiBC,MAAOlD,EAAQiD,iBAAiBD,QAC1DrC,IAAKX,EAAQmD,UAAU,OAAOH,SAUhCD,EAAU1C,QAAQ+C,qBAAsB,EAQxCL,EAAU1C,QAAQgD,MAAO,EAYzBN,EAAU1C,QAAQiD,MAAQ,CACxBC,IAAK,CAAEC,oBAAoB,IAU7BT,EAAU1C,QAAQoD,YAAc,CAC9BC,kBAAmB,IACnBC,cAAe,IAWjBZ,EAAU1C,QAAQuD,QAAS,EAO3Bb,EAAU1C,QAAQwD,aAAc,EAOhCd,EAAU1C,QAAQyD,YAAa,EAM/Bf,EAAU1C,QAAQ0D,SAAW,CAAE,ECtF/B,MAAMC,EAMJ7D,WAAAA,CAAY8D,EAAW,gBACrBhD,KAAKiD,UAAUD,EACjB,CAOAC,SAAAA,CAAUD,GACRhD,KAAKkD,QAAW,GAAEF,yBACpB,CAWAG,aAAAA,CAAcC,GAAY,IAAAC,EAAArD,KACxB,OAAA,WAAA,IAAAsD,EAAAC,GAAO,UAAOC,GACZ,MAAMC,EAA4B,mBAAfL,EAA4BA,EAAWI,GAAOH,EAAKK,2BAA2BF,GAC3FG,QAAiBC,MAAMH,GAE7B,IAAKE,EAASE,GACZ,MAAMF,EAMR,aAFmBA,EAASG,UAG7B,OAAA,SAAAC,GAAA,OAAAT,EAAAU,MAAAhE,KAAAiE,UAAA,CAAA,CAZD,EAaF,CASAP,0BAAAA,CAA2BF,GACzB,MAAQ,WAAUxD,KAAKkD,iCAAiCM,uCAC1D,EC1DF,MAAMU,EACO,MADPA,EAEO,MAFPA,EAGO,MAHPA,EAIO,MAJPA,EAKQ,OAGRC,EACC,MADDA,EAEE,OAFFA,EAGC,MAQP,MAAMC,EAcJ,YAAOC,EACLZ,IAAEA,EAAGa,MAAEA,EAAQJ,EAAeK,OAAEA,EAASJ,GAAe,CAAE,EAC1DK,EArBsB,gCAuBtB,IAAKf,EAAK,OAEV,MAAMgB,EAAW,IAAIC,IAAIF,GAMzB,OAJAC,EAASE,aAAaC,IAAI,WAAYnB,GACtCgB,EAASE,aAAaC,IAAI,SAAUL,GACpCE,EAASE,aAAaC,IAAI,QAASN,GAE5BO,mBAAmBJ,EAASK,KACrC,CAEA,cAAWC,GACT,OAAOZ,CACT,CAEA,cAAWa,GACT,OAAOb,CACT,CAEA,eAAWc,GACT,OAAOd,CACT,CAEA,oBAAWe,GACT,OAAOhB,CACT,CAEA,oBAAWiB,GACT,OAAOjB,CACT,CAEA,oBAAWkB,GACT,OAAOlB,CACT,CAEA,oBAAWmB,GACT,OAAOnB,CACT,CAEA,qBAAWoB,GACT,OAAOpB,CACT,EC9EF,MAAMqB,EAAc,CAClBC,SAAU,qBACVC,SAAU,oBACVC,UAAW,2BAMb,MAAMC,EAQJ,sBAAOC,CAAgBC,EAAU,IAC/B,MAAMC,EAAa,CAAA,EAkBnB,OAhBAD,EAAQhE,SAASkE,IACf,MAAMC,EAAOL,EAAIM,QAAQF,EAAUC,MAEnC,GAAIL,EAAIM,QAAQR,WAAaO,EAAM,CACjC,MAAQE,eAAgBC,EAAgBC,WAAYC,GAClDN,EAEFD,EAAWE,GAAQ,CACjBG,iBACAE,aAEJ,MACEP,EAAWE,GAAQD,EAAUK,UAC/B,IAGK,CACLN,aAEJ,CASA,aAAOQ,CAAOC,GACZ,OAAOA,EAAUC,MAAK,EAAGX,aAAcA,GAAWA,EAAQnF,OAAS,GACrE,CAKA,kBAAWuF,GACT,OAAOV,CACT,ECzDF,MAAMkB,EACI,SADJA,EAEE,OAMR,MAAMC,EAQJ,cAAOC,CAAQC,GACb,MAAMC,EAAOD,EAAUE,SAEvB,MAAQ,GAAED,EAAKE,UAAU,EAAGF,EAAKG,YAAY,KAAO,KACtD,CAOA,iBAAWC,GACT,OAAOR,CACT,CAYA,eAAOS,CAASX,GACd,OAAOA,EAAUC,MAAMW,GACrBT,EAAmBU,SAASD,EAASE,YACzC,CASA,eAAOD,CAASE,GACd,OAAOb,IAAuBa,CAChC,CASA,aAAOC,CAAOD,GACZ,OAAOb,IAAqBa,CAC9B,CAOA,eAAWE,GACT,OAAOf,CACT,CAUA,eAAOgB,CAASC,EAAQC,GACtB,MAAMC,EAAsB,IAAIlD,IAAK,GAAEgD,EAAOjE,OACxCoE,EAAMnB,EAAmBC,QAAQiB,GACjCnE,EAAO,GAAEkE,IAAiBG,mBAAmBD,KAEnD,OAAOjE,MAAMH,GACVsE,MAAMpE,GACDA,EAASE,GACJF,EAASG,OAGXkE,QAAQC,OAAO,CACpBC,OAAQvE,EAASuE,OACjBC,WAAYxE,EAASwE,eAGxBJ,MAAK,EAAGK,OAASC,kBACS,IAAIC,gBAAgBD,GAE5BxG,SAAQ,CAAC0G,EAAGC,IAC3BZ,EAAoBjD,aAAaC,IAAI4D,EAAGD,KAEnCE,OAAOC,OAAO,CAAE,EAAEhB,EAAQ,CAC/BjE,IAAKmE,EAAoBe,gBAG5BC,OAAOC,GACCb,QAAQC,OAAOY,IAE5B,CAWA,sBAAOC,CACLC,EACApB,EAAiB,yCAEjB,MAAMqB,EAAmB,GAWzB,OATAD,EAAQlH,SAAS6F,IACf,MAAMuB,EAAkBvC,EAAmBe,SACzCC,EACAC,GAGFqB,EAAiBnI,KAAKoI,EAAgB,IAGjCjB,QAAQkB,IAAIF,GAChBjB,MAAMoB,GAAWA,IACjBP,OAAOC,GAAWb,QAAQC,OAAOY,IACtC,ECpGK,MAAMO,EAAU,UASVC,EAAQ,QAkBRC,EAAc,aAkBdC,EAAa,YASbC,EAAQ,QASRC,EAAO,OASPC,EAAU,UAkBVC,EAAc,aAkBdC,EAAU,UA2BVC,EAAc,aAkBdC,EAAU,UC/GvB,MAAMC,EACJ7K,WAAAA,CACEe,GACA+J,MACEA,GAAQ,EAAKC,YACbA,EAAc,OAAMC,cACpBA,EAAgB,OAAMC,sBACtBA,EAAwB,qDACtB,IAEJnK,KAAKoK,eAAiBJ,EACtBhK,KAAKqK,oBAAsB,EAC3BrK,KAAKiK,YAAcA,EACnBjK,KAAKsK,YAAa,EAClBtK,KAAKuK,yBAAsBC,EAE3BxK,KAAKyK,aAAc,EACnBzK,KAAK0K,WAAY,EACjB1K,KAAK2K,WAAY,EACjB3K,KAAK4K,aAAe,EACpB5K,KAAK6K,aAAe,GACpB7K,KAAK8K,2BAA4B,EACjC9K,KAAKC,OAASA,EACdD,KAAKkK,cAAgBA,EACrBlK,KAAK+K,kBAAeP,EACpBxK,KAAKgL,qBAAuB,EAC5BhL,KAAKmK,sBAAwBA,EAC7BnK,KAAKiL,mBAAqB,EAC1BjL,KAAKkL,sBAAmBV,EAExBxK,KAAKmL,aACLnL,KAAKoL,eACP,CAOAC,YAAAA,GACErL,KAAKsL,OAAO,OACd,CAKAC,WAAAA,GACEC,cAAcxL,KAAKuK,qBACnBiB,cAAcxL,KAAKkL,kBACnBO,aAAazL,KAAK0L,gBACpB,CAOAC,WAAAA,GAEE,OAAOC,KAAKC,MAAM7L,KAAKiL,mBACzB,CAOAjB,KAAAA,CAAM7J,GACJ,QAAgBqK,IAAZrK,EACF,OAAOH,KAAKoK,gBAAkBpK,KAAKC,OAAO+J,QAG5ChK,KAAKoK,eAAiB0B,QAAQ3L,EAChC,CAKA4L,OAAAA,GACE/L,KAAKuL,cAEAS,OAAOC,UACVD,OAAOC,QAAU,IAEnBjM,KAAKqK,oBAAsB,EAC3BrK,KAAKsK,YAAa,EAClBtK,KAAKuK,yBAAsBC,EAC3BxK,KAAKyK,aAAc,EACnBzK,KAAK2K,WAAY,EACjB3K,KAAK4K,aAAe,EACpB5K,KAAK6K,aAAe,GACpB7K,KAAK+K,kBAAeP,EACpBxK,KAAKgL,qBAAuB,EAC5BhL,KAAKiL,mBAAqB,EAC1BjL,KAAKkL,sBAAmBV,CAC1B,CAkBA0B,OAAAA,GACElM,KAAKqL,eACLrL,KAAKuL,cAELS,OAAOG,oBAAoB,eAAgBnM,KAAKoM,sBAEhDpM,KAAKC,OAAOoM,IAAIC,EAAsBtM,KAAKuM,iBAC3CvM,KAAKC,OAAOoM,IAAIC,EAAoBtM,KAAKwM,eACzCxM,KAAKC,OAAOoM,IAAIC,EAAyBtM,KAAKyM,mBAC9CzM,KAAKC,OAAOoM,IAAIC,EAA0BtM,KAAK0M,oBAC/C1M,KAAKC,OAAOoM,IAAIC,EAAsBtM,KAAK2M,cAC3C3M,KAAKC,OAAOoM,IAAIC,EAAoBtM,KAAK4M,eACzC5M,KAAKC,OAAOoM,IAAIC,EAA0BtM,KAAK6M,oBAC/C7M,KAAKC,OAAOoM,IAAIC,EAAsBtM,KAAK8M,iBAC3C9M,KAAKC,OAAOoM,IAAIC,EAA0BtM,KAAK+M,oBAC/C/M,KAAKC,OAAOoM,IAAIC,EAAsBtM,KAAKgN,gBAC7C,CAOAC,OAAAA,GACOjN,KAAKC,OAAOiN,SACflN,KAAKsL,OAAO,OAEhB,CAOA4B,KAAAA,GACElN,KAAKsL,OAAO,OAEZtL,KAAK4K,aAAe,EAEpB5K,KAAKuL,aACP,CAKA4B,KAAAA,GACMnN,KAAKoN,sBAELpN,KAAK8K,2BAA6BkB,OAAOqB,KAC3CrB,OAAOqB,GAAGC,UAAUC,SACpBvN,KAAK8K,2BAA4B,GAG/BkB,OAAOwB,cAAgBxN,KAAK6K,aAAanK,OAAS,IACpDV,KAAK6K,aAAahJ,SAAS4L,IACzBzB,OAAOwB,aACLxN,KAAKC,OAAOyN,KACZD,EAAaE,OACbF,EAAaG,OACd,IAGH5N,KAAK6K,aAAe,IAExB,CAOAgD,oBAAAA,GACE,MAAMC,EAAehO,MAAMC,KAAKC,KAAKC,OAAOJ,eAAeK,MACxD6N,GAAUA,EAAM5N,UAEnB,IAAIE,EAAW,MAOf,OALIyN,GAAkBA,EAAazN,WAEjCA,EAAWyN,EAAazN,UAGnByN,EAAezN,EAAS2N,cAAgB,EACjD,CAOAC,mBAAAA,GACE,MAAMH,EAAe9N,KAAKC,OAAOuB,YACjC,IAAInB,EAAW,MAOf,OALIyN,GAAkBA,EAAazN,WAEjCA,EAAWyN,EAAazN,UAGnByN,EAAezN,EAAS2N,cAAgB,EACjD,CAOAE,oBAAAA,GACE,MAAM1L,YAAEA,GAAgBxC,KAAKC,OACvBkO,EAAMnO,KAAK2L,cAAgBnJ,EAAY4L,gBAAmB,EAC1DC,EAAW7L,EAAY8L,aAAeH,EAE5C,OAAOE,EAAW,GAAKA,IAAaE,IAAW,EAAe,IAAXF,CACrD,CAOAG,gBAAAA,GACE,MAAMC,EAAazO,KAAKC,OAAOuC,YAAY8L,eAAiBC,IACtDG,EAAoD,IAAvC1O,KAAKC,OAAOuC,YAAY8L,aAE3C,OAAOG,EAAa,EAAIC,CAC1B,CAOAC,sBAAAA,GACE,OAAI3O,KAAKgL,qBACAhL,KAAK4O,wBAGP5O,KAAKqK,mBACd,CAOAuE,qBAAAA,GACE,MAAMC,EAAkB9E,EAAa+E,MAAQ9O,KAAKgL,qBAAwB,EAE1E,OAAOhL,KAAKqK,oBAAsBwE,CACpC,CASAE,cAAAA,CAAeC,GACb,MAAMpB,EAAS,CACbqB,SAAUD,EACVE,gBAAiBnF,EAAa+E,MAC9BK,wBAAyB,EACzBC,wBAAyB,EACzBC,cAAc,EACdC,eAAe,EACfC,WAAYvP,KAAKC,OAAOuP,QAAU,IAAM,IACxCC,oBAAqBzP,KAAKC,OAAOyP,eACjCC,eAAgB3P,KAAK2L,cACrBiE,cAAe5P,KAAK+K,aAAa8E,UAAUC,QAE3CC,mBAAoB/P,KAAKgQ,qBACzBC,cAAsC,IAAvBjQ,KAAKC,OAAOiQ,UAAgBC,QAAQ,GACnDC,uBAAwBpQ,KAAKiK,aA+B/B,OA5BIjK,KAAKqQ,wBACPzC,EAAO0C,kBAAoBtQ,KAAK6N,wBAG9B7N,KAAKgQ,uBACPpC,EAAO2C,yBAA2BvQ,KAAKiO,uBAIpCjO,KAAKwQ,oBACR5C,EAAO0B,eAAgB,EACvB1B,EAAO+B,eAAiB3P,KAAK2O,0BAI3B3O,KAAKyQ,eACP7C,EAAOwB,wBAAwD,EAA9BpP,KAAKkO,uBACtCN,EAAOuB,wBAAoD,EAA1BnP,KAAKwO,mBAEtCZ,EAAOyB,cAAe,EAEtBzB,EAAO8C,gBAAkB,CAACpE,EAAmBA,GAAoB3K,SAC/DqN,GAEEhP,KAAK2Q,cACL,GAGC/C,CACT,CAKAgD,iBAAAA,GACE,MAAMC,EAAO,CACXC,qBAAsB9Q,KAAK+K,aAAa8E,UAAUkB,OAClDC,0BAA2BlF,QAAQ9L,KAAKC,OAAOgR,MAAK,GAAMC,WAC1DC,oBAAqBC,SAASC,SAC9BC,qBAAsB,UACtBC,kBAAmB,gBACnBC,qBAAsBxR,KAAKkK,cAC3BuH,UAAWzR,KAAK+K,aAAa2G,KAEzBC,EACJ3R,KAAK+K,aAAa8E,UAAU8B,mBAAqB,CAAA,EAEnD3F,OAAOC,QAAUxD,OAAOC,OAAO,CAAA,EAAIsD,OAAOC,QAAS4E,EAAMc,EAC3D,CAaAC,SAAAA,GACE5R,KAAKuK,oBAAsBsH,aAAY,KAEhC7R,KAAKC,OAAO6R,UACf9R,KAAKsL,OAAO,MACd,GACC,IACL,CAkBAyG,aAAAA,GACE/R,KAAKoM,qBAAuBpM,KAAKqL,aAAa2G,KAAKhS,MACnDA,KAAKuM,gBAAkBvM,KAAKiN,QAAQ+E,KAAKhS,MACzCA,KAAKwM,cAAgBxM,KAAKkN,MAAM8E,KAAKhS,MACrCA,KAAKyM,kBAAoBzM,KAAKiS,UAAUD,KAAKhS,MAC7CA,KAAK0M,mBAAqB1M,KAAKkS,WAAWF,KAAKhS,MAC/CA,KAAK2M,aAAe3M,KAAKmS,KAAKH,KAAKhS,MACnCA,KAAK4M,cAAgB5M,KAAKoS,MAAMJ,KAAKhS,MACrCA,KAAK6M,mBAAqB7M,KAAKqS,WAAWL,KAAKhS,MAC/CA,KAAK8M,gBAAkB9M,KAAKsS,QAAQN,KAAKhS,MACzCA,KAAK+M,mBAAqB/M,KAAKuS,WAAWP,KAAKhS,MAC/CA,KAAKgN,gBAAkBhN,KAAKwS,QAAQR,KAAKhS,KAC3C,CAiBAoL,aAAAA,GACEpL,KAAK+R,gBAEL/F,OAAOyG,iBAAiB,eAAgBzS,KAAKoM,sBAE7CpM,KAAKC,OAAOyS,GAAGpG,EAAsBtM,KAAKuM,iBAC1CvM,KAAKC,OAAOyS,GAAGpG,EAAoBtM,KAAKwM,eACxCxM,KAAKC,OAAOyS,GAAGpG,EAAyBtM,KAAKyM,mBAC7CzM,KAAKC,OAAOyS,GAAGpG,EAA0BtM,KAAK0M,oBAC9C1M,KAAKC,OAAOyS,GAAGpG,EAAsBtM,KAAK2M,cAC1C3M,KAAKC,OAAOyS,GAAGpG,EAAoBtM,KAAK4M,eACxC5M,KAAKC,OAAOyS,GAAGpG,EAA0BtM,KAAK6M,oBAC9C7M,KAAKC,OAAOyS,GAAGpG,EAAsBtM,KAAK8M,iBAC1C9M,KAAKC,OAAOyS,GAAGpG,EAA0BtM,KAAK+M,oBAC9C/M,KAAKC,OAAOyS,GAAGpG,EAAsBtM,KAAKgN,iBAC1ChN,KAAKC,OAAO0S,IAAI,UAAW3S,KAAKkM,QAAQ8F,KAAKhS,MAC/C,CAKAmL,UAAAA,GACE,MAAMyH,EAAW,gBAEjB,IAAKxB,SAASyB,cAAe,IAAGD,KAAa,CAC3C,MAAME,EAAS1B,SAAS2B,cAAc,UAChCrB,EAAM1R,KAAKmK,sBAEjB2I,EAAOE,OAAQ,EACfF,EAAOG,GAAKL,EACZE,EAAOpB,IAAMA,EACboB,EAAO9M,KAAO,kBAEd8M,EAAOI,OAAUC,IACfnT,KAAKmN,OAAO,EAGdiE,SAASgC,KAAKC,YAAYP,EAC5B,CACF,CAOAzC,mBAAAA,GACE,QAASrQ,KAAK6N,sBAChB,CAOA4C,UAAAA,GACE,MAAMhO,kBAAEA,GAAsBzC,KAAKC,OAAOuC,YAAYpD,UAEtD,OACGY,KAAKwQ,mBACN/N,EAAoBzC,KAAKC,OAAOuC,YAAY8L,YAEhD,CAOAgF,WAAAA,GACE,MAAM7Q,kBAAEA,GAAsBzC,KAAKC,OAAOuC,YAAYpD,UAEtD,OACGY,KAAKwQ,mBACN/N,EAAoBzC,KAAKC,OAAOuC,YAAY8L,YAEhD,CAOAkC,eAAAA,GACE,OAAOzP,OAAOC,SAAShB,KAAKC,OAAOgB,WACrC,CAOA+O,kBAAAA,GACE,QAAShQ,KAAKiO,qBAChB,CAOAb,iBAAAA,GACE,OAAKpN,KAAK+K,eAAiB/K,KAAK+K,aAAa8E,YAGxC/P,MAAMyT,QAAQvT,KAAK+K,aAAayI,iBAI9B1H,QACL9L,KAAK+K,aAAayI,gBAAgBtT,MAC/BuT,GAAYA,EAAQC,gBAAkB3J,EAAa9H,KAAKyR,iBALpD5H,QAAQ9L,KAAK+K,aAAayI,iBAQrC,CAOAvB,SAAAA,GACEjS,KAAK+L,UACL/L,KAAK2T,mBAAmB3T,KAAKC,OAAO2T,iBAEhC5T,KAAKoN,sBAETpN,KAAK4Q,oBAEL5Q,KAAK6T,8BAEL7T,KAAKsL,OAAO,gBACZtL,KAAKsK,YAAa,EACpB,CAOA4H,UAAAA,GACElS,KAAKsL,OAAO,QACZtL,KAAKyK,aAAc,EAEnBzK,KAAKsL,OAAO,cACd,CASAwI,GAAAA,CAAI9E,EAAW+E,EAAeC,EAAW,OACnChU,KAAKgK,SAEPiK,QAAQD,GACL,gBAAehF,IAChB+E,EACA/H,OAAOC,QAGb,CAOAX,MAAAA,CAAO0D,EAAW+E,GAChB,GAAI/T,KAAKoN,oBAAqB,OAE9B,IACEpN,KAAKmN,OACN,CAAC,MAAO+G,GACPlU,KAAK8T,IAAI9E,EAAWkF,EAAO,QAC7B,CAEA,MAAMtG,EAASnF,OAAOC,OACpB,CAAE,EACF1I,KAAK+O,eAAeC,GACpB+E,GAGF/T,KAAK8T,IAAI9E,EAAWpB,GAEpB,IACM5B,OAAOwB,aACTxB,OAAOwB,aAAaxN,KAAKC,OAAOyN,KAAMsB,EAAWpB,GAEjD5N,KAAK6K,aAAahK,KAAK,CACrB8M,OAAQqB,EACRpB,UAGL,CAAC,MAAOsG,GACPlU,KAAK8T,IAAI9E,EAAWkF,EAAO,QAC7B,CACF,CAOA,UAAOpF,GACL,OAAQqF,KAAKrF,MAAQ,KAAMqB,QAAQ,EACrC,CAOAgC,IAAAA,GACOnS,KAAKsK,aAAYtK,KAAKsK,YAAa,GAEnCtK,KAAKgL,sBAAyBhL,KAAKwQ,oBACtCxQ,KAAKgL,qBAAuBjB,EAAa+E,OAGjB,IAAtB9O,KAAK4K,eACP5K,KAAK4K,aAAeb,EAAa+E,MAEjC9O,KAAK4R,YACL5R,KAAKoU,UAGPpU,KAAKuS,aACLvS,KAAKsL,OAAO,QAERtL,KAAK0K,YAAW1K,KAAK0K,WAAY,EACvC,CAWA0H,KAAAA,GACOpS,KAAKwQ,oBACRxQ,KAAKqK,oBAAsBrK,KAAK4O,wBAChC5O,KAAKgL,qBAAuB,IAI3BhL,KAAKC,OAAOqS,YACZtS,KAAKsT,eACNtT,KAAKC,OAAO0L,cAAgB3L,KAAKC,OAAOgB,WAExCjB,KAAKsL,OAAO,SAKVtL,KAAKsK,aAAetK,KAAK0K,YAC3B1K,KAAKsL,OAAO,QACZtL,KAAK0K,WAAY,EAErB,CAQA2H,UAAAA,GACErS,KAAKsL,OAAO,uBACd,CAKAuI,2BAAAA,GACM7H,OAAOqB,IACTrB,OAAOqB,GAAGC,UAAUC,SACpBvN,KAAK8K,2BAA4B,GAEjC9K,KAAK8K,2BAA4B,CAErC,CAOAwH,OAAAA,IACMtS,KAAKsK,YAAetK,KAAKC,OAAO6R,UAAa9R,KAAK0K,YACpD1K,KAAKsL,OAAO,QACZtL,KAAK0K,WAAY,EAErB,CAOA6H,UAAAA,GACOvS,KAAKC,OAAO6R,WACf9R,KAAKiL,mBAAqBjL,KAAKC,OAAO0L,cAE1C,CAOAgF,WAAAA,GACE,MAAM0D,EAAerU,KAAKC,OAAOuC,YAAY8R,aACvCC,EAAkBvU,KAAKC,OAAOuC,YAAY+R,kBAC1C5I,EAAc3L,KAAKC,OAAO0L,cAKhC,OAJoB0I,EAChB,GACCE,EAAkB5I,GAAawE,QAAQ,EAG9C,CAKAwD,kBAAAA,CAAmB5I,GACjB/K,KAAK+K,aAAeA,CACtB,CAUAqJ,MAAAA,GACE,MAAMI,EAAeA,KACdxU,KAAKC,OAAO6R,UAAa9R,KAAKwQ,mBACjCxQ,KAAKsL,OAAO,SACd,EAIFtL,KAAK0L,gBAAkB+I,YAAW,KAEhCD,IAGAxU,KAAKkL,iBAAmB2G,aAAY,KAElC2C,GAAc,GACb,IAAM,GACR,IACL,CAaAhC,OAAAA,GACE,IAAKxS,KAAKyK,aAAezK,KAAK2K,UAC5B,OAGF,MAAM+J,EAAaA,KACjB1U,KAAK2K,WAAY,EACjB3K,KAAKsL,OAAO,cAAc,EAG5BtL,KAAK2K,WAAY,EAEjB3K,KAAKsL,OAAO,gBAGRqJ,EAAUC,QAAQC,cACpB7U,KAAKC,OAAO0S,IAAIrG,EAA0BoI,GAI1C1U,KAAKC,OAAO0S,IAAIrG,EAAsBoI,EAE1C,ECt2BF,MAAMI,EAQJC,gBAAAA,CAAiBvR,GACf,GAAIxD,KAAKgV,YAAa,CACpB,MAAOC,GAAWjV,KAAKgV,YAAYtT,QAChCwT,GAAYA,EAAQ1R,MAAQA,IAG/B,OAAOyR,CACT,CAGF,CAOAE,eAAAA,GACE,IAAKnV,KAAKoV,WACR,OAGF,MAAMC,EAAcrV,KAAKsV,mBAClBC,GAAWF,EAAY3T,QAC3BwT,GAAYA,EAAQ1R,MAAQxD,KAAKoV,aAGpC,OAAOG,CACT,CAQAC,qBAAAA,CAAsBhS,GACpB,MAAMiS,EAAezV,KAAK+U,iBAAiBvR,GAE3C,GAAIiS,EACF,OAAOA,EAAaC,cAAgB,EAIxC,CAOAC,WAAAA,GACE,MAAMC,EAAQ,QAEd,OAAI5V,KAAK6V,iBAAiBC,YAAcF,EAAc,GAE/C5V,KAAKgV,YAAYtT,QAAO,EAAGoU,eAAgBA,IAAcF,GAClE,CAqBAG,4BAAAA,GACE,MAAMC,aAAEA,GAAiBhW,KAAK6V,mBACvBI,wBAAEA,GAA4B,CAAE,GAAIjW,KAAKkW,kBAAkBxU,QAChE,EAAGuU,6BAA8BA,IAE7BE,EAA2BH,IAAiBC,EAElD,IAAKD,EACH,MAAO,GAIT,MAAMI,EAAYJ,EAAatU,QAC5B2U,GAAiC,SAApBA,EAAS9R,SAGzB,OAAI4R,EACKC,EAGFA,EAAU1U,QAAQ2U,IACFJ,EAAwB/V,MAC1CoW,GACCA,EAAoBC,SAAWF,EAASE,QACxCF,EAASrQ,OAASsQ,EAAoBtQ,QAK9C,CASAwQ,kBAAAA,GACE,MAAMC,EAAczW,KAAK6V,iBAEzB,IAAKY,EACH,OAGF,IAAIC,YAAEA,GAAgBD,EAMtB,OAJKC,GAAe,IAAIvC,KAASnU,KAAK2W,yBACpCD,EAAc,aAGTA,CACT,CAOAE,sBAAAA,GACE,OAAO5W,KAAKsV,kBAAkB5T,QAAO6T,GAAWA,EAAQmB,aAC1D,CAOAb,cAAAA,GASE,OARK7V,KAAKyW,cACRzW,KAAKyW,YAAczW,KAAK+U,iBAAiB/U,KAAK6W,cAG3C7W,KAAKyW,aAAezW,KAAKgV,aAAehV,KAAKgV,YAAYtU,OAAS,KACpEV,KAAKyW,aAAezW,KAAKgV,aAGrBhV,KAAKyW,WACd,CAOAK,sBAAAA,GACE,MAAML,EAAczW,KAAK6V,iBAEzB,GAAKY,GAAgBA,EAAYM,SAIjC,OAAON,EAAYM,QACrB,CAQAC,gBAAAA,GACE,MAAMtB,EAAe1V,KAAKkW,kBAE1B,GAAKR,GAAiBA,EAAahV,OAInC,OAAOgV,EAAauB,KAAK9P,IAAc,CACrC+P,cAAelX,KAAKmX,uBAAuBhQ,EAAS+P,eACpDvF,kBAAmB3R,KAAKoX,2BACtBjQ,EAASwK,mBAEX+E,YAAa1W,KAAK6V,iBAAiBa,YACnCW,gBAAiBrX,KAAK4W,yBACtBG,SAAU/W,KAAK8W,yBACfQ,SAAUtX,KAAK2V,cACf9P,QAASsB,EAAStB,QAClB0R,IAAKpQ,EAASoQ,IACdC,UAAWxX,KAAK6V,iBAAiB2B,UACjCvE,GAAIjT,KAAK6V,iBAAiB5C,GAC1BwE,eAAgBzX,KAAK6V,iBAAiB4B,eACtCC,UAAW1X,KAAK2X,uBAChBC,KAAMzQ,EAASyQ,KACf9B,UAAW9V,KAAK6V,iBAAiBC,UACjC+B,SAAU1Q,EAAS0Q,SACnBC,aAAc3Q,EAAS2Q,aACvBhI,QAAS3I,EAAS2I,QAClBiI,UAAW5Q,EAAS4Q,UACpBC,aAAc7Q,EAAS6Q,aACvB5B,UAAWpW,KAAK+V,+BAChBkC,MAAOjY,KAAK6V,iBAAiBoC,MAC7B5Q,UAAWF,EAASE,UACpB5D,IAAK0D,EAAS1D,IACdD,IAAKxD,KAAK6W,WACV9F,OAAQ/Q,KAAK6V,iBAAiB9E,UAElC,CAOAuE,eAAAA,GACE,MAAMmB,EAAczW,KAAK6V,iBAMzB,OAJK7V,KAAKkY,cAAgBzB,GAAeA,EAAYpB,cACnDrV,KAAKkY,aAAezB,EAAYpB,aAG3BrV,KAAKkY,cAAgB,EAC9B,CAOAP,oBAAAA,GACE,MAAMQ,iBACJA,EAAmB,IACjBnY,KAAK6V,kBAAoB,GAE7B,OAAOsC,CACT,CAOAxB,oBAAAA,GACE,MAAMF,EAAczW,KAAK6V,iBAEzB,IAAKY,EACH,OAAO,IAAItC,KAAK,GAGlB,MAAMiE,UAAEA,GAAc3B,EAEtB,OAAI2B,EACK,IAAIjE,KAAKiE,QADlB,CAGF,CAQAjB,sBAAAA,CAAuBD,GACrB,OAAAmB,EAAAA,EAAAA,EAAA,CAAA,EACKrY,KAAKkX,eACLlX,KAAK6V,iBAAiBqB,eACtBA,EAEP,CAQAE,0BAAAA,CAA2BzF,GACzB,OAAA0G,EAAAA,EAAAA,EAAA,CAAA,EACKrY,KAAK2R,mBACL3R,KAAK6V,iBAAiBlE,mBACtBA,EAEP,CAMAuE,eAAAA,GACE,MAAMR,aAAEA,GAAiB1V,KAAK6V,iBAE9B,OAAOH,GAAgB,EACzB,4yLC9TFf,EAAU2D,YAAY,KAAID,EAAAA,EACrBE,CAAAA,EAAAA,IACAC,4zKCFL7D,EAAU2D,YAAY,KAAID,EAAAA,EACrBE,CAAAA,EAAAA,IACAC,o/LCFL7D,EAAU2D,YAAY,KAAID,EAAAA,EACrBE,CAAAA,EAAAA,IACAC,g/KCFL7D,EAAU2D,YAAY,KAAID,EAAAA,EACrBE,CAAAA,EAAAA,IACAC,wuJCHL7D,EAAU2D,YAAY,KAAID,EAAA,CAAA,EACrBG,yDCcL,MAAMC,GAOJ,yBAAOC,CAAmBzY,EAAQ0Y,EAAW,IAC3C,MAAMC,EAAU,0BACVC,EAAc5Y,EAAOwB,aAAaqX,aAAaF,GAMrD,GAJIC,GACF5Y,EAAOwB,aAAaoX,YAAYA,IAG7B/Y,MAAMyT,QAAQoF,KAAcA,EAASjY,OAAQ,OAElD,MAAM2W,EAAkBsB,EAASjX,QAAO6T,GAAWA,EAAQmB,cAEtDW,EAAgB3W,QAErB+X,GAAOM,gBAAgB9Y,EAAQ2Y,GAAS7Q,MAAKiR,IAC3C3B,EAAgBxV,SAAQ0T,IACtBkD,GAAOQ,gBAAgBD,EAAczD,EAAQ,IAG/CtV,EAAOwB,aAAayX,SAASF,EAAa,GAE9C,CAQA,0BAAOG,CAAoBlZ,EAAQmW,EAAY,IACxCtW,MAAMyT,QAAQ6C,IAEnBA,EAAUvU,SAAQ,EAChBmE,OACA3F,SAAU+Y,EACV7C,OAAQlW,EACRoD,IAAKiO,MAELzR,EAAOoZ,mBAAmB,CACxBjZ,KAAe,QAAT4F,EAAiB,WAAa,YACpCoT,QACA/Y,WACAqR,OACA,GAEN,CAYA,sBAAOuH,CAAgBzX,EAAWqP,GAChC,MAAMyI,GAAavY,OAAOC,SAAS6P,EAAK0I,QACpC1I,EAAK0I,OAAS1I,EAAK2I,kBAAoB,IACrCC,GAAW1Y,OAAOC,SAAS6P,EAAK6I,SAClC7I,EAAK6I,QAAU7I,EAAK8I,mBAAqB,IAE7CnY,EAAUoY,OAAO,IAAIC,OACnBP,EACAG,EACAK,KAAKC,UAAUlJ,IAEnB,CAQA,oBAAOmJ,CAAc/Z,GAAQ4P,UAAEA,IAC7B4I,GAAOU,oBAAoBlZ,EAAQ4P,EAAUuG,WAC7CqC,GAAOwB,YAAYha,EAAQ4P,EAAUrM,IAAKqM,EAAUyH,UACpDmB,GAAOC,mBAAmBzY,EAAQ4P,EAAUwH,iBAC5CoB,GAAOyB,aAAaja,EAAQ4P,EAAU6H,UACxC,CASA,kBAAOuC,CAAYha,EAAQ4W,EAAYS,EAAW,IAChD,MAAMsB,EAAU,kBACVC,EAAc5Y,EAAOwB,aAAaqX,aAAaF,GAEjDC,GACF5Y,EAAOwB,aAAaoX,YAAYA,GAG7B/Y,MAAMyT,QAAQ+D,IAAcA,EAAS5W,QAE1C+X,GAAOM,gBAAgB9Y,EAAQ2Y,GAAS7Q,MAAKoS,IAC3C7C,EAASzV,SAAQoT,IACX4B,IAAe5B,EAAQmF,eAE3B3B,GAAOQ,gBAAgBkB,EAAclF,EAAQ,IAG/ChV,EAAOwB,aAAayX,SAASiB,EAAa,GAE9C,CAQA,mBAAOD,CAAaja,EAAQyX,EAAY,IACtC,MAAMkB,EAAU,mBACVC,EAAc5Y,EAAOwB,aAAaqX,aAAaF,GAEjDC,GACF5Y,EAAOwB,aAAaoX,YAAYA,GAG7B/Y,MAAMyT,QAAQmE,IAAeA,EAAUhX,QAE5C+X,GAAOM,gBAAgB9Y,EAAQ2Y,GAAS7Q,MAAKsS,IAC3C3C,EAAU7V,SAAQyY,IAChB7B,GAAOQ,gBAAgBoB,EAAeC,EAAS,IAGjDra,EAAOwB,aAAayX,SAASmB,EAAc,GAE/C,CAWA,qBAAOE,CAAeta,EAAQua,GAC5B,IAAKA,EAAY3K,UAAU6G,YAAa,OAExC,MAAM+D,EAAUxa,EAAOya,SAASF,EAAY3K,UAAU6G,aAWtD,OATA+B,GAAOvE,MAAMjU,EAAQ,CACnB0a,KAAMC,WAAWC,kBACjBJ,UACAK,SAAU,CACRC,UAAWP,EAAY3K,UAAU6G,YACjChF,IAAK8I,MAIF,CACT,CAWA,6BAAaQ,CAAuBzU,EAAY,IAAI,OAAAhD,GAAA,YAClD,OAAKmD,EAAmBQ,SAASX,GAK1BG,EAAmBoC,gBAAgBvC,GAJjCyB,QAAQiT,QAAQ1U,EAI4B,GANHhD,EAOpD,CAUA,iCAAO2X,CAA2B3U,EAAY,IAG5C,OAFKZ,EAAIW,OAAOC,GAETA,EAAU0Q,KAAK9P,GAAQkR,EAAAA,EAAA,CAAA,EACzBlR,GACAxB,EAAIC,gBAAgBuB,EAAStB,WAEpC,CAUA,2BAAOsV,CAAqBC,GAC1B,OAAO3C,GAAOuC,uBACZvC,GAAOyC,2BACLzC,GAAO4C,4BAA4BD,EAAiBpE,qBAG1D,CAaA,0BAAOsE,EACHzL,UAAW9E,EAAYyI,gBAAEA,GAC3BrM,GAEA,MAAAoU,EAKI5G,EAAUpV,IAAIC,MAAM2H,EAAU4D,IAL5BtH,IACJA,EAAGoU,SACHA,EAAQ/R,WACRA,GAEDyV,EAED,MAAO,CACL7J,IAAKjO,EACLuC,KAAM6R,EACN/R,aACA0N,kBACA3D,UARY2L,EAAAD,EAAAE,IAUhB,CAUA,sBAAO1C,CAAgB9Y,EAAQ2Y,GAE7B,OAAO,IAAI5Q,SAASiT,IAClBxG,YAAW,KACTwG,EAAQ,IAAItG,EAAU+G,UAAU,CAC9BzI,GAAI2F,EACJxY,KAAM,WACNgZ,MAAOR,EACP3H,KAAMhR,EAAOgR,MAAK,KACjB,GACF,IAAI,GAEX,CAOA,0BAAO0K,CAAoB1b,GACzBA,EAAOwB,aAAaiR,GAAG,YAAY,EAAG3E,YAC/B,CAAC,kBAAmB,oBAAoBpM,SAASoM,EAAMkF,KAE5DlF,EAAM2E,GAAG,aAAa,KACpB,MAAOkJ,GAAO9b,MAAMC,KAAKgO,EAAM8N,YACzB7V,EAAO+H,EAAMkF,GAAGtR,SAAS,mBAAqB,iBAAmB,kBAEvE1B,EAAO6b,QAAQ,CAAE9V,OAAM6K,KAAM+K,GAAM,GACnC,GAEN,CASA,mBAAOG,CAAa9b,GAClB,IAAKA,EAAOb,UAAU4c,WAAWD,aAAc,CAC7C,MAAME,iBACJA,EAAgBC,uBAChBA,GACEjc,EAAOb,UAAU4c,WAEfG,EADe,IAAIpZ,EAAakZ,GAEnC9Y,cAAc+Y,GAEjBjc,EAAOb,QAAQ,CACb4c,WAAY,CACVD,aAAcI,IAGpB,CAEA,OAAOlc,EAAOb,UAAU4c,WAAWD,YACrC,CAUA,wBAAOK,CAAkBnc,EAAQiU,GAC/B,IAAKA,EAAO,OAEZ,MAAM/L,EAAa+L,EAAM/L,WAAa+L,EAAM/L,WAAa+L,EAAMuG,QAc/D,OAZAhC,GAAOvE,MAAMjU,EAAQ,CACnB0a,KAAM,EACNF,QAASxa,EAAOya,SAAS,WACzBI,SAAU,CACRC,UAAW,UACXvX,IAAKvD,EAAOyR,MACZxJ,OAAQgM,EAAMhM,OACdC,aACA1E,IAAKyQ,EAAMzQ,QAIR,CACT,CAQA,YAAOyQ,CAAMjU,GAAQ0a,KAAEA,EAAIF,QAAEA,EAAOK,SAAEA,IACpC7a,EAAOiU,MAAM,MAEbjU,EAAOiU,MAAM,CACXyG,OACAF,UACAK,YAEJ,CASA,kCAAOO,CAA4B9U,EAAY,IAC7C,OAAOA,EAAU7E,QACdyF,IAAc,CAAC,OAAQ,OAAOxF,SAASwF,EAAS4Q,YAErD,CASA,wBAAOsE,CAAkBpc,GACvB,MACMqc,EAAuBrc,EAAOwB,aAAaqX,aADjC,2BAGhB,IAAKwD,EAAsB,OAG3B,MAAOC,GAAczc,MAAMC,KAAKuc,EAAqBT,YAErD,OAAOU,CACT,CAUA,8BAAOC,CAAwBvc,EAAQ0L,GACrC,MAAM8Q,EAAiBhE,GAAO4D,kBAAkBpc,GAEhD,IAAKwc,EAAgB,OAKrB,OAHkB9Q,GAAe8Q,EAAenD,WAC9C3N,EAAc8Q,EAAehD,QAEZgD,OAAiBjS,CACtC,CAUA,0BAAakS,CACXlZ,EACAL,GAAgB,IAAIJ,GAAeI,iBACnC,OAAAI,GAAA,YACA,MAAMsN,QAAa1N,EAAcK,GAEjC,OAAOiF,OAAOC,OAAO,IAAIoM,EAAoBjE,EAAM,GAHnDtN,EAIF,CASA,mBAAOoZ,CAAapW,EAAY,IAC9B,GAAIG,EAAmBQ,SAASX,GAAY,OAAOA,EAAU,GAE7D,MAAMP,EAAO2O,EAAUC,QAAQC,cAAgB,MAAQ,OAGvD,OAFiBtO,EAAUrG,MAAK,EAAG6X,eAAgBA,IAAc/R,KAE9CO,EAAU,EAC/B,CAUA,qBAAaqW,CAAe3c,EAAQ4c,GAAQ,OAAAtZ,GAAA,YAC1C,MAAQmO,IAAKlO,GAAuBqZ,EAAfC,EAAUtB,EAAKqB,EAAME,IACpC3B,QAAyB3C,GAAOiE,oBACpClZ,EACAiV,GAAOsD,aAAa9b,IAEhB+c,QAAsBvE,GAAO0C,qBACjCC,GAEIvL,EAAY4I,GAAOkE,aAAaK,GAEtC,OAAOvE,GAAO6C,oBAAoBwB,EAAYjN,EAAW,GAXftM,EAY5C,CAeA,wBAAO0Z,CAAkBhd,EAAQ0L,GAC/B,MAAM8Q,EAAiBhE,GACpB+D,wBAAwBvc,EAAQ0L,GAEnC,IAAK8Q,IAAmB1b,OAAOC,SAASyb,EAAehD,SACrD,OAAO9N,EAKT,MAAMuR,EAAuBT,EAAehD,QAAU,GAMtD,OAHAxZ,EAAO6b,QAAQ,CAAE9V,KAAM,yBAA0B6K,KAAM4L,IACvDxc,EAAO0L,YAAYuR,GAEZA,CACT,CAeA,2BAAOC,CAAqBld,EAAQ0L,GAClC,MAAQ8N,QAAS2D,GAA0B3E,GACxC+D,wBAAwBvc,EAAQ0L,IAAgB,CAAA,EAEnD,OAAO5K,OACJC,SAASoc,GAAyBA,EAAwBzR,CAC/D,CAkBA,sBAAa0R,CAAgBpd,EAAQ4c,EAAQS,GAAM,OAAA/Z,GAAA,YACjD,IACE,MAAMiX,QAAoB/B,GAAOmE,eAAe3c,EAAQ4c,GAMxD,GAJApE,GAAO8E,aAAatd,GACpBwY,GAAO+E,eAAevd,EAAQua,GAC9B/B,GAAOgF,aAAaxd,EAAQua,GAExB/B,GAAO8B,eAAeta,EAAQua,GAAc,OAIhD,OAFA/B,GAAOuB,cAAc/Z,EAAQua,GAEtB8C,EAAK,KAAM9C,EACnB,CAAC,MAAOtG,GACP,GAAIuE,GAAO2D,kBAAkBnc,EAAQiU,GAAQ,OAE7C,OAAOoJ,EAAKpJ,EACd,CAAC,GAjBgD3Q,EAkBnD,CAOA,mBAAOga,CAAatd,GAClB,IAA+C,IAA3CA,EAAOb,UAAU0D,SAASya,eAEzBtd,EAAOb,UAAU0D,SAASya,aAAc,CAC3C,MAAMA,EAAe,IAAIxT,EAAa9J,EAAQ,CAC5C+J,MAAO/J,EAAO+J,QACdE,cAAeyK,EAAU5S,QAAQD,UACjCqI,sBACElK,EAAOb,UAAU4c,WAAW7R,wBAGhClK,EAAOb,QAAQ,CACb0D,SAAU,CACRya,iBAGN,CACF,CASA,mBAAOE,CAAaxd,EAAQua,EAAakD,EAAetZ,GACtDnE,EAAO0d,OACLD,EAAarZ,MAAM,CACjBZ,IAAK+W,EAAY3K,UAAUkH,WAGjC,CAQA,qBAAOyG,CAAevd,EAAQua,GACvBva,EAAO2d,UAEZ3d,EAAO2d,SAASC,OAAO,CACrB5F,MAAOuC,EAAY3K,UAAUkB,OAC7B+M,YAAatD,EAAY3K,UAAUoI,OAEvC,CASA,iBAAO8F,CAAW9d,GAIhB,OAFAwY,GAAOkD,oBAAoB1b,GAEpB,CACL0L,YAAcA,GACZ8M,GAAOwE,kBAAkBhd,EAAQ0L,GACnCqS,eAAiBrS,GACf8M,GAAO0E,qBAAqBld,EAAQ0L,GACtCsS,WAAS3a,EAAAC,GAAE,UAAOsZ,EAAQS,GAAI,OAC5B7E,GAAO4E,gBAAgBpd,EAAQ4c,EAAQS,EAAK,IAAAW,SAAAla,EAAAma,GAAA,OAAA5a,EAAAU,MAAAhE,KAAAiE,UAAA,IADrC,IAAAX,CAGb,EAGFqR,EAAUwJ,IAAI,aAAc1F,GAAOsF,YAGnCpJ,EAAUvV,QAAQ4c,WAAa,CAC7BD,kBAAcvR,EACdyR,sBAAkBzR,EAClB0R,4BAAwB1R,EACxBL,2BAAuBK"}