(function (_, $) { 'use strict'; function decodeEntities(value) { var str = (value || '').toString(); if (!str) { return ''; } var textarea = document.createElement('textarea'); textarea.innerHTML = str; var decoded = textarea.value || str; if (decoded !== str) { textarea.innerHTML = decoded; decoded = textarea.value || decoded; } return decoded; } function escapeHtml(value) { return (value || '').toString() .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function normalizeText(value, maxLen) { var text = decodeEntities(value); text = text.replace(/ /g, ' '); text = text.replace(/&?/g, '&'); text = text.replace(/\s+/g, ' ').trim(); if (maxLen && text.length > maxLen) { text = text.substring(0, maxLen - 3).trim() + '...'; } return text; } function isPromptLike(text) { var sample = (text || '').toString().toUpperCase(); if (!sample) { return false; } var markers = [ 'ROLE YOU ARE', 'OBJECTIVE', 'HARD RULES', 'ENTITY CONTEXT', 'OUTPUT MUST BE VALID JSON', '"VERSION":', '"SCENES":', '"VIDEO_SETUP":' ]; return markers.some(function (marker) { return sample.indexOf(marker) !== -1; }); } function safeDescription(raw, fallbackTitle) { var text = normalizeText(raw, 220); if (!text || isPromptLike(text)) { text = 'Watch product highlights for ' + normalizeText(fallbackTitle || 'this item', 90) + '.'; } return text; } function resolveLabel(raw, fallback) { var label = (raw || '').toString().trim(); if (!label) { return fallback; } if (label.charAt(0) === '_') { return fallback; } var canonical = label.replace(/^_+/, ''); if (/^fy_[a-z0-9_.-]+$/i.test(canonical)) { return fallback; } return label; } function getLabels($scope) { return { ctaDefault: resolveLabel($scope.attr('data-fy-label-cta-default'), 'Shop now'), validTill: resolveLabel($scope.attr('data-fy-label-valid-till'), 'Valid till'), previous: resolveLabel($scope.attr('data-fy-label-previous'), 'Previous'), next: resolveLabel($scope.attr('data-fy-label-next'), 'Next'), play: resolveLabel($scope.attr('data-fy-label-play'), 'Play'), pause: resolveLabel($scope.attr('data-fy-label-pause'), 'Pause'), like: resolveLabel($scope.attr('data-fy-label-like'), 'Like'), liked: resolveLabel($scope.attr('data-fy-label-liked'), 'Liked'), share: resolveLabel($scope.attr('data-fy-label-share'), 'Share'), copied: resolveLabel($scope.attr('data-fy-label-copied'), 'Copied'), copyLink: resolveLabel($scope.attr('data-fy-label-copy-link'), 'Copy link'), fullscreen: resolveLabel($scope.attr('data-fy-label-fullscreen'), 'Full screen'), exitFullscreen: resolveLabel($scope.attr('data-fy-label-exit-fullscreen'), 'Exit full screen'), mute: resolveLabel($scope.attr('data-fy-label-mute'), 'Mute'), unmute: resolveLabel($scope.attr('data-fy-label-unmute'), 'Unmute'), close: resolveLabel($scope.attr('data-fy-label-close'), 'Close'), video: resolveLabel($scope.attr('data-fy-label-video'), 'Video'), noSource: resolveLabel($scope.attr('data-fy-label-no-source'), 'No video source') }; } function isMobileViewport() { if (window.matchMedia) { return window.matchMedia('(max-width: 767px)').matches; } return (window.innerWidth || document.documentElement.clientWidth || 0) <= 767; } function syncFullscreenButtons($watch, labels) { var fullscreenNode = document.fullscreenElement || null; var usingNative = !!fullscreenNode && ($watch.get(0) === fullscreenNode || $.contains($watch.get(0), fullscreenNode)); if (!usingNative && $watch.data('fyUsingNativeFullscreen') === true) { $watch.removeClass('is-reel-immersive'); $('body').removeClass('fy-watch-reel-open'); $watch.find('.fy-media-card').removeClass('is-reel-active'); $watch.data('fyUsingNativeFullscreen', false); $watch.removeData('fyFullscreenTargetNode'); } var isImmersive = usingNative || $watch.hasClass('is-reel-immersive'); $watch.find('.cm-fy-ctrl-fullscreen').each(function () { var $button = $(this); var label = isImmersive ? labels.exitFullscreen : labels.fullscreen; $button.attr('data-state', isImmersive ? 'exit' : 'enter'); $button.attr('aria-label', label); $button.find('.fy-media-ctrl-label').text(label); }); } function enterImmersiveMode($watch, $card, labels) { if (!$watch.length) { return; } $watch.addClass('is-reel-immersive'); $('body').addClass('fy-watch-reel-open'); $watch.find('.fy-media-card').removeClass('is-reel-active'); if ($card && $card.length) { $card.addClass('is-reel-active'); var cardNode = $card.get(0); if (cardNode && typeof cardNode.scrollIntoView === 'function') { cardNode.scrollIntoView({ block: 'nearest', inline: 'nearest' }); } } playActiveCardMedia($watch, $card, false); var fullscreenTarget = ($card && $card.length) ? $card.get(0) : $watch.get(0); if (fullscreenTarget && typeof fullscreenTarget.requestFullscreen === 'function' && document.fullscreenElement !== fullscreenTarget) { try { $watch.data('fyFullscreenTargetNode', fullscreenTarget); var fullscreenPromise = fullscreenTarget.requestFullscreen(); if (fullscreenPromise && typeof fullscreenPromise.then === 'function') { fullscreenPromise.then(function () { $watch.data('fyUsingNativeFullscreen', true); syncFullscreenButtons($watch, labels); }).catch(function () { $watch.data('fyUsingNativeFullscreen', false); syncFullscreenButtons($watch, labels); }); } } catch (e) { $watch.data('fyUsingNativeFullscreen', false); } } syncFullscreenButtons($watch, labels); } function exitImmersiveMode($watch, labels) { if (!$watch.length) { return; } var fullscreenNode = document.fullscreenElement || null; var usingNative = !!fullscreenNode && ($watch.get(0) === fullscreenNode || $.contains($watch.get(0), fullscreenNode)); if (usingNative && typeof document.exitFullscreen === 'function') { try { var exitPromise = document.exitFullscreen(); if (exitPromise && typeof exitPromise.catch === 'function') { exitPromise.catch(function () {}); } } catch (e) {} } $watch.removeClass('is-reel-immersive'); $('body').removeClass('fy-watch-reel-open'); $watch.find('.fy-media-card').removeClass('is-reel-active'); $watch.data('fyUsingNativeFullscreen', false); syncFullscreenButtons($watch, labels); } function getGlobalAudioState() { var fallback = { muted: true, volume: 1 }; try { if (!window.localStorage) { return fallback; } var raw = window.localStorage.getItem('fy_watch_global_audio_v1'); if (!raw) { return fallback; } var parsed = JSON.parse(raw); var muted = !!parsed.muted; return { muted: muted, volume: 1 }; } catch (e) { return fallback; } } function saveGlobalAudioState(state) { try { if (window.localStorage) { window.localStorage.setItem('fy_watch_global_audio_v1', JSON.stringify({ muted: !!state.muted, volume: 1 })); } } catch (e) {} } function applyGlobalAudioState($watch, state) { var muted = !!state.muted; var volume = 1; $watch.find('video.cm-fy-watch-video').each(function () { var video = this; video.muted = muted; video.volume = muted ? 0 : volume; }); } function findPrimaryVisibleVideo($watch, preferredVideo) { var $videos = $watch.find('video.cm-fy-watch-video'); if (!$videos.length) { return null; } if (preferredVideo && preferredVideo instanceof HTMLVideoElement) { var ratio = parseFloat($(preferredVideo).data('fyVisibleRatio') || '0'); if (ratio > 0.05) { return preferredVideo; } } var viewportCenterX = Math.max(0, window.innerWidth || document.documentElement.clientWidth || 0) / 2; var bestVideo = null; var bestScore = -999999; $videos.each(function () { var video = this; var ratio = parseFloat($(video).data('fyVisibleRatio') || '0'); if (ratio <= 0.05) { return; } var rect = video.getBoundingClientRect(); var centerX = (rect.left + rect.right) / 2; var distanceScore = -Math.abs(centerX - viewportCenterX); var score = (ratio * 1000) + distanceScore; if (score > bestScore) { bestScore = score; bestVideo = video; } }); if (bestVideo) { return bestVideo; } return $videos.get(0) || null; } function enforceSingleAudibleVideo($watch, preferredVideo) { var state = getGlobalAudioState(); var muted = !!state.muted; var volume = 1; var audibleVideo = muted ? null : findPrimaryVisibleVideo($watch, preferredVideo); $watch.find('video.cm-fy-watch-video').each(function () { var video = this; if (muted) { video.muted = true; video.volume = 0; return; } var isAudible = (video === audibleVideo); video.volume = volume; video.muted = !isAudible; }); } function pauseCardMedia($card) { if (!$card || !$card.length) { return; } $card.find('video.cm-fy-watch-video').each(function () { try { this.pause(); } catch (e) {} }); $card.find('iframe.cm-fy-watch-embed').each(function () { try { if (this.contentWindow) { this.contentWindow.postMessage(JSON.stringify({ event: 'command', func: 'pauseVideo', args: [] }), '*'); } } catch (e) {} }); $card.find('.cm-fy-ctrl-play').attr('data-state', 'play').attr('aria-label', 'Play').find('.fy-media-ctrl-label').text('Play'); } function playActiveCardMedia($watch, $card, restart) { if (!$watch || !$watch.length || !$card || !$card.length) { return; } $watch.find('.fy-media-card').not($card).each(function () { pauseCardMedia($(this)); }); var video = $card.find('video.cm-fy-watch-video').get(0) || null; var iframe = $card.find('iframe.cm-fy-watch-embed').get(0) || null; if (video) { if (restart) { try { video.currentTime = 0; } catch (e) {} } var playPromise = video.play(); if (playPromise && typeof playPromise.catch === 'function') { playPromise.catch(function () {}); } enforceSingleAudibleVideo($watch, video); } else if (iframe && iframe.contentWindow) { try { if (restart) { iframe.contentWindow.postMessage(JSON.stringify({ event: 'command', func: 'seekTo', args: [0, true] }), '*'); } iframe.contentWindow.postMessage(JSON.stringify({ event: 'command', func: 'playVideo', args: [] }), '*'); } catch (e) {} } } function normalizeEmbedUrlForPlayback(rawUrl) { var url = (rawUrl || '').toString().trim(); if (!url) { return ''; } try { var parsed = new URL(url, window.location.origin); var host = (parsed.hostname || '').toLowerCase(); var isYoutubeHost = host.indexOf('youtube.com') !== -1 || host.indexOf('youtube-nocookie.com') !== -1; if (!isYoutubeHost || parsed.pathname.indexOf('/embed/') !== 0) { return url; } parsed.searchParams.set('autoplay', '1'); parsed.searchParams.set('mute', '1'); parsed.searchParams.set('controls', '0'); parsed.searchParams.set('modestbranding', '1'); parsed.searchParams.set('rel', '0'); parsed.searchParams.set('playsinline', '1'); parsed.searchParams.set('enablejsapi', '1'); parsed.searchParams.set('iv_load_policy', '3'); parsed.searchParams.set('fs', '0'); parsed.searchParams.set('disablekb', '1'); return parsed.toString(); } catch (e) { return url; } } function playVisibleVideos($scope) { var mediaNodes = $scope.find('video.cm-fy-watch-video, iframe.cm-fy-watch-embed'); if (!mediaNodes.length || typeof window.IntersectionObserver === 'undefined') { return; } var postYouTubeCommand = function (frame, command) { if (!(frame instanceof HTMLIFrameElement) || !frame.contentWindow) { return; } var src = (frame.getAttribute('src') || '').toLowerCase(); if (src.indexOf('youtube.com/embed/') === -1 && src.indexOf('youtube-nocookie.com/embed/') === -1) { return; } try { frame.contentWindow.postMessage(JSON.stringify({ event: 'command', func: command, args: Array.isArray(arguments[2]) ? arguments[2] : [] }), '*'); } catch (e) {} }; var observer = new IntersectionObserver(function (entries) { entries.forEach(function (entry) { var el = entry.target; var $el = $(el); var visibleRatio = entry.isIntersecting ? entry.intersectionRatio : 0; $el.data('fyVisibleRatio', visibleRatio); if (el instanceof HTMLVideoElement) { if (entry.isIntersecting && visibleRatio >= 0.6) { if (isMobileViewport() && $el.data('fyRestartOnVisible')) { try { el.currentTime = 0; } catch (e) {} $el.data('fyRestartOnVisible', false); } var playPromise = el.play(); if (playPromise && typeof playPromise.catch === 'function') { playPromise.catch(function () {}); } } else { if (isMobileViewport()) { $el.data('fyRestartOnVisible', true); } el.pause(); } } else if (el instanceof HTMLIFrameElement) { if (entry.isIntersecting && visibleRatio >= 0.6) { postYouTubeCommand(el, 'seekTo', [0, true]); postYouTubeCommand(el, 'playVideo'); } else { postYouTubeCommand(el, 'pauseVideo'); } } }); enforceSingleAudibleVideo($scope); }, { threshold: [0.4, 0.6, 0.9] }); mediaNodes.each(function () { observer.observe(this); }); } function applyVideoAspectClasses($scope) { $scope.find('video.cm-fy-watch-video').each(function () { var video = this; var $video = $(video); if ($video.data('fyAspectBound') === true) { return; } $video.data('fyAspectBound', true); var syncAspect = function () { var vw = parseInt(video.videoWidth || 0, 10); var vh = parseInt(video.videoHeight || 0, 10); if (vw <= 0 || vh <= 0) { return; } var $card = $video.closest('.fy-media-card'); if (!$card.length) { return; } $card.removeClass('fy-media-card--portrait fy-media-card--landscape'); $card.addClass(vw >= vh ? 'fy-media-card--landscape' : 'fy-media-card--portrait'); }; $video.on('loadedmetadata.fyAspect canplay.fyAspect loadeddata.fyAspect', syncAspect); syncAspect(); }); } function initThumbSwitcher($scope) { var $main = $scope.find('.cm-fy-watch-main'); var $mainVideo = $scope.find('.cm-fy-watch-main-video'); var $mainIframe = $scope.find('.cm-fy-watch-main-iframe'); if (!$main.length) { return; } $scope.off('click.fyWatchThumb').on('click.fyWatchThumb', '.cm-fy-watch-thumb', function () { var $btn = $(this); $scope.find('.cm-fy-watch-thumb').removeClass('is-active'); $btn.addClass('is-active'); var playbackType = ($btn.data('playback-type') || 'video').toString().toLowerCase(); var videoUrl = ($btn.data('video-url') || '').toString(); var embedUrl = ($btn.data('embed-url') || '').toString(); var title = ($btn.data('title') || '').toString(); var description = ($btn.data('description') || '').toString(); var kindLabel = ($btn.data('kind-label') || '').toString(); var durationLabel = ($btn.data('duration-label') || '').toString(); var productFun = ($btn.data('product-fun') || '').toString(); var ctaUrl = ($btn.data('cta-url') || '').toString(); var ctaText = ($btn.data('cta-text') || '').toString(); var promoText = ($btn.data('promo-text') || '').toString(); var promoUrgency = ($btn.data('promo-urgency') || '').toString(); var promoValidUntil = ($btn.data('promo-valid-until') || '').toString(); var thumbnail = ($btn.data('thumbnail') || '').toString(); var $title = $scope.find('.cm-fy-watch-main-title'); var $desc = $scope.find('.cm-fy-watch-main-desc'); var $cta = $scope.find('.cm-fy-watch-main-cta'); var $promo = $scope.find('.cm-fy-watch-main-promo'); var $meta = $scope.find('.cm-fy-watch-main-meta'); $title.text(title); $desc.text(safeDescription(description, title)); if ($meta.length) { var metaHtml = ''; if (kindLabel) { metaHtml += '' + escapeHtml(kindLabel) + ''; } if (durationLabel) { metaHtml += '' + escapeHtml(durationLabel) + ''; } if (productFun) { metaHtml += '' + escapeHtml(productFun) + ''; } $meta.html(metaHtml); } var labels = getLabels($scope); if (ctaUrl !== '') { $cta.attr('href', ctaUrl).text(ctaText || labels.ctaDefault).show(); } else { $cta.hide(); } if (promoText !== '') { var promoHtml = '' + escapeHtml(normalizeText(promoText || '')) + ''; var promoUrgencyText = normalizeText(promoUrgency || ''); if (!promoUrgencyText && normalizeText(promoValidUntil || '') !== '') { promoUrgencyText = labels.validTill + ' ' + normalizeText(promoValidUntil || ''); } if (promoUrgencyText !== '') { promoHtml += '' + escapeHtml(promoUrgencyText) + ''; } $promo.html(promoHtml).show(); } else { $promo.hide(); } if (videoUrl !== '') { if (!$mainVideo.length) { $mainIframe.remove(); $main.prepend(''); $mainVideo = $scope.find('.cm-fy-watch-main-video'); $mainIframe = $(); } if ($thumbnail !== '') { $mainVideo.attr('poster', thumbnail); } $mainVideo.find('source').attr('src', videoUrl); $mainVideo[0].load(); var playPromise = $mainVideo[0].play(); if (playPromise && typeof playPromise.catch === 'function') { playPromise.catch(function () {}); } $mainVideo.show(); if ($mainIframe.length) { $mainIframe.hide(); } } else if (embedUrl !== '') { if (!$mainIframe.length) { $mainVideo.remove(); $main.prepend(''); $mainIframe = $scope.find('.cm-fy-watch-main-iframe'); $mainVideo = $(); } $mainIframe.attr('src', normalizeEmbedUrlForPlayback(embedUrl)).show(); if ($mainVideo.length) { $mainVideo.hide(); } } }); } function buildActions(item, labels) { var html = ''; if (item.cta_url) { html += '' + escapeHtml(normalizeText(item.cta_text || labels.ctaDefault)) + ''; } if ((item.show_rfq_cta || 'N') === 'Y' && item.rfq_url) { html += '' + escapeHtml(normalizeText(item.rfq_text || 'Create RFQ')) + ''; } return html; } function buildPlayer(item, attrs, labels) { if (item.video_url) { return ''; } if (item.embed_url) { return ''; } return '
' + escapeHtml(labels.noSource) + '
'; } function buildControlsHtml(item, attrs, controlVisibility, labels, showFullscreenControl) { if (!item.video_url && !item.embed_url) { return ''; } var isMuted = attrs.indexOf('muted') !== -1 || !!item.embed_url; var visibility = (controlVisibility || 'subtle').toString().toLowerCase(); if (['subtle', 'persistent', 'hidden'].indexOf(visibility) < 0) { visibility = 'subtle'; } return '' + '
' + '' + '' + '' + '' + (showFullscreenControl ? '' : '') + '
'; } function promoUrgencyText(item, labels) { var urgencyText = normalizeText(item.promo_urgency_label || ''); if (urgencyText !== '') { return urgencyText; } var validUntil = normalizeText(item.promo_valid_until_human || ''); if (validUntil !== '') { return labels.validTill + ' ' + validUntil; } return ''; } function buildPromoChipsHtml(item, labels) { var promoText = normalizeText(item.promo_text || ''); if (!promoText) { return ''; } var chunks = promoText.split(/\s*\|\|\s*|\n+/).filter(function (part) { return normalizeText(part || '') !== ''; }); if (!chunks.length) { chunks = [promoText]; } var urgencyText = promoUrgencyText(item, labels); var html = ''; chunks.forEach(function (chunk, idx) { html += '
' + escapeHtml(normalizeText(chunk)) + ''; if (idx === 0 && urgencyText) { html += '' + escapeHtml(urgencyText) + ''; } html += '
'; }); return html; } function buildMetaRowHtml(item, labels) { var row = ''; row += buildPromoChipsHtml(item, labels); if (item.product_fun) { row += '
' + escapeHtml(normalizeText(item.product_fun || '')) + '
'; } if (!row) { return ''; } return '
' + row + '
'; } function buildCardHtml(item, mode, attrs, controlVisibility, labels, showFullscreenControl) { var orientation = (item.orientation || 'portrait').toString().toLowerCase() === 'landscape' ? 'landscape' : 'portrait'; var caption = '

' + escapeHtml(normalizeText(item.title || '')) + '

'; var metaRow = buildMetaRowHtml(item, labels); var actions = buildActions(item, labels); var controls = buildControlsHtml(item, attrs, controlVisibility, labels, showFullscreenControl); var shareUrl = item.product_url || item.cta_url || item.video_url || window.location.href; var assetId = parseInt(item.asset_id || 0, 10); if (!assetId || assetId < 0) { assetId = 0; } return '' + '
' + '' + '
' + buildPlayer(item, attrs, labels) + '
' + controls + '
' + '
' + '
' + '
' + caption + metaRow + '
' + '
' + actions + '
' + '
' + '
' + '
' + '
'; } function buildThumbButton(item, isActive, labels) { return '' + ''; } function appendItems($watch, items) { var mode = ($watch.attr('data-fy-watch-mode') || 'swipe').toLowerCase(); var controlVisibility = ($watch.attr('data-fy-control-visibility') || 'subtle').toLowerCase(); var showFullscreenControl = (($watch.attr('data-fy-show-fullscreen-control') || 'Y').toUpperCase() === 'Y'); var labels = getLabels($watch); var attrs = ''; if (($watch.find('video.cm-fy-watch-video').first().prop('autoplay'))) { attrs += 'autoplay '; } if (($watch.find('video.cm-fy-watch-video').first().prop('muted'))) { attrs += 'muted '; } if (($watch.find('video.cm-fy-watch-video').first().prop('loop'))) { attrs += 'loop '; } if (mode === 'rail') { var $rail = $watch.find('.fy-media-rail'); var railHtml = ''; items.forEach(function (item) { railHtml += buildCardHtml(item, 'rail', attrs, controlVisibility, labels, showFullscreenControl); }); var $cards = $(railHtml).hide(); $rail.append($cards); $cards.fadeIn(180); return; } if (mode === 'thumbs') { var $thumbList = $watch.find('.cm-fy-watch-thumbs-list'); var hasAny = $thumbList.find('.cm-fy-watch-thumb').length > 0; var thumbHtml = ''; items.forEach(function (item, i) { thumbHtml += buildThumbButton(item, !hasAny && i === 0, labels); }); var $thumbs = $(thumbHtml).hide(); $thumbList.append($thumbs); $thumbs.fadeIn(160); if (!hasAny) { $thumbList.find('.cm-fy-watch-thumb').first().trigger('click'); } return; } var swipeHtml = ''; items.forEach(function (item) { swipeHtml += buildCardHtml(item, 'swipe', attrs, controlVisibility, labels, showFullscreenControl); }); var $swipeCards = $(swipeHtml).hide(); $watch.find('.cm-fy-media-load-more').before($swipeCards); $swipeCards.fadeIn(180); } function initDesktopNav($watch) { var mode = ($watch.attr('data-fy-watch-mode') || 'swipe').toLowerCase(); var desktopLayout = ($watch.attr('data-fy-desktop-layout') || 'horizontal').toLowerCase(); var stickyArrows = (($watch.attr('data-fy-desktop-arrows-sticky') || 'Y').toUpperCase() === 'Y'); var $prev = $watch.find('.cm-fy-watch-nav-prev'); var $next = $watch.find('.cm-fy-watch-nav-next'); if (mode !== 'swipe' || desktopLayout !== 'horizontal' || !$prev.length || !$next.length) { return; } var isDesktop = window.matchMedia && window.matchMedia('(min-width: 768px)').matches; var node = $watch.get(0); if (!node || !isDesktop) { $prev.addClass('is-hidden'); $next.addClass('is-hidden'); return; } var updateNavState = function () { var maxScroll = Math.max(0, node.scrollWidth - node.clientWidth); $prev.removeClass('is-hidden'); $next.removeClass('is-hidden'); $prev.toggleClass('is-disabled', node.scrollLeft <= 8); $next.toggleClass('is-disabled', node.scrollLeft >= (maxScroll - 8)); if (stickyArrows) { var x = Math.max(0, node.scrollLeft); var tr = 'translate3d(' + x + 'px, -50%, 0)'; $prev.css('transform', tr); $next.css('transform', tr); } else { $prev.css('transform', 'translateY(-50%)'); $next.css('transform', 'translateY(-50%)'); } }; if ($watch.data('fyNavBound') !== true) { $watch.data('fyNavBound', true); $prev.off('click.fyWatchNav').on('click.fyWatchNav', function () { node.scrollBy({ left: -Math.max(320, Math.floor(node.clientWidth * 0.82)), behavior: 'smooth' }); }); $next.off('click.fyWatchNav').on('click.fyWatchNav', function () { node.scrollBy({ left: Math.max(320, Math.floor(node.clientWidth * 0.82)), behavior: 'smooth' }); }); $watch.off('scroll.fyWatchNav').on('scroll.fyWatchNav', updateNavState); $(window).off('resize.fyWatchNav').on('resize.fyWatchNav', updateNavState); } updateNavState(); } function initVideoControls($watch) { var controlVisibility = ($watch.attr('data-fy-control-visibility') || 'subtle').toLowerCase(); var mobileTouchFullscreen = (($watch.attr('data-fy-mobile-touch-fullscreen') || 'Y').toUpperCase() === 'Y'); var labels = getLabels($watch); var globalAudioState = getGlobalAudioState(); applyGlobalAudioState($watch, globalAudioState); enforceSingleAudibleVideo($watch); if ($watch.data('fyFullscreenBound') !== true) { $watch.data('fyFullscreenBound', true); document.addEventListener('fullscreenchange', function () { syncFullscreenButtons($watch, labels); }); } syncFullscreenButtons($watch, labels); bindRfqForms($watch); $watch.find('.fy-media-card').each(function () { var $card = $(this); if ($card.data('fyVideoControlsBound') === true) { return; } var $video = $card.find('video.cm-fy-watch-video').first(); var $iframe = $card.find('iframe.cm-fy-watch-embed').first(); var $controls = $card.find('.cm-fy-player-controls').first(); if ((!$video.length && !$iframe.length) || !$controls.length) { return; } $card.data('fyVideoControlsBound', true); var video = $video.length ? $video.get(0) : null; var iframe = $iframe.length ? $iframe.get(0) : null; var $play = $controls.find('.cm-fy-ctrl-play'); var $progress = $controls.find('.cm-fy-ctrl-progress'); var $audio = $controls.find('.cm-fy-ctrl-audio'); var postEmbedCommand = function (command, args) { if (!iframe || !iframe.contentWindow) { return; } try { iframe.contentWindow.postMessage(JSON.stringify({ event: 'command', func: command, args: Array.isArray(args) ? args : [] }), '*'); } catch (e) {} }; var $like = $controls.find('.cm-fy-ctrl-like'); var $share = $controls.find('.cm-fy-ctrl-share'); var $fullscreen = $controls.find('.cm-fy-ctrl-fullscreen'); var $close = $card.find('.cm-fy-reel-close'); var assetId = parseInt(($card.attr('data-fy-asset-id') || '0'), 10) || 0; var shareUrl = ($card.attr('data-fy-share-url') || '').toString(); var likeKey = 'fy_watch_like_' + (assetId > 0 ? assetId : shareUrl); var autoHideTimer = null; var controlsAlwaysVisible = (controlVisibility === 'persistent'); var controlsHidden = (controlVisibility === 'hidden'); var activateControls = function () { if (controlsAlwaysVisible || controlsHidden) { return; } $card.addClass('is-controls-active'); if (autoHideTimer) { clearTimeout(autoHideTimer); } autoHideTimer = setTimeout(function () { $card.removeClass('is-controls-active'); }, 1800); }; var embedPaused = false; var setPlayLabel = function () { var paused = video ? video.paused : embedPaused; var label = paused ? labels.play : labels.pause; $play.attr('data-state', paused ? 'play' : 'pause'); $play.attr('aria-label', label); $play.find('.fy-media-ctrl-label').text(label); }; var setProgressValue = function () { if (!$progress.length || !video || !video.duration || !isFinite(video.duration) || video.duration <= 0) { if ($progress.length) { $progress.val(0); } return; } $progress.val(Math.max(0, Math.min(100, Math.round((video.currentTime / video.duration) * 100)))); }; var setAudioLabel = function () { var muted = video ? !!video.muted : !!globalAudioState.muted; var label = muted ? labels.unmute : labels.mute; $audio.attr('data-state', muted ? 'muted' : 'unmuted'); $audio.attr('aria-label', label); $audio.find('.fy-media-ctrl-label').text(label); }; if (video) { video.muted = !!globalAudioState.muted; video.volume = globalAudioState.muted ? 0 : 1; $video.prop('controls', false); } else if (iframe) { postEmbedCommand(globalAudioState.muted ? 'mute' : 'unMute'); } setPlayLabel(); setProgressValue(); setAudioLabel(); if (controlsAlwaysVisible) { $card.addClass('is-controls-active'); } else if (!controlsHidden) { $card.removeClass('is-controls-active'); } try { var liked = window.localStorage && window.localStorage.getItem(likeKey) === '1'; if (liked) { $like.addClass('is-liked').attr('aria-label', labels.liked); $like.find('.fy-media-ctrl-label').text(labels.liked); } } catch (e) {} $play.off('click.fyCtrl').on('click.fyCtrl', function () { activateControls(); if (video) { if (video.paused) { var playPromise = video.play(); if (playPromise && typeof playPromise.catch === 'function') { playPromise.catch(function () {}); } } else { video.pause(); } } else if (iframe) { embedPaused = !embedPaused; if (embedPaused) { postEmbedCommand('pauseVideo'); } else { postEmbedCommand('seekTo', [0, true]); postEmbedCommand('playVideo'); } } setPlayLabel(); }); $progress.off('input.fyCtrl change.fyCtrl').on('input.fyCtrl change.fyCtrl', function () { activateControls(); if (!$progress.length || !video || !video.duration || !isFinite(video.duration) || video.duration <= 0) { return; } var pct = Math.max(0, Math.min(100, parseFloat($progress.val() || '0'))); video.currentTime = (pct / 100) * video.duration; }); $audio.off('click.fyCtrl').on('click.fyCtrl', function () { activateControls(); globalAudioState.muted = !globalAudioState.muted; globalAudioState.volume = 1; saveGlobalAudioState(globalAudioState); applyGlobalAudioState($watch, globalAudioState); if (iframe) { postEmbedCommand(globalAudioState.muted ? 'mute' : 'unMute'); } enforceSingleAudibleVideo($watch, video); setAudioLabel(); }); $like.off('click.fyCtrl').on('click.fyCtrl', function () { activateControls(); var isLiked = !$like.hasClass('is-liked'); var likeLabel = isLiked ? labels.liked : labels.like; $like.toggleClass('is-liked', isLiked).attr('aria-label', likeLabel); $like.find('.fy-media-ctrl-label').text(likeLabel); try { if (window.localStorage) { window.localStorage.setItem(likeKey, isLiked ? '1' : '0'); } } catch (e) {} }); $share.off('click.fyCtrl').on('click.fyCtrl', function () { activateControls(); var text = ($card.find('.fy-media-title').first().text() || 'Video').toString().trim(); var url = shareUrl || window.location.href; if (navigator.share) { navigator.share({ title: text, url: url }).catch(function () {}); return; } if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(url).then(function () { $share.attr('aria-label', labels.copied); $share.find('.fy-media-ctrl-label').text(labels.copied); setTimeout(function () { $share.attr('aria-label', labels.share); $share.find('.fy-media-ctrl-label').text(labels.share); }, 1200); }).catch(function () {}); return; } window.prompt(labels.copyLink, url); }); $fullscreen.off('click.fyCtrl').on('click.fyCtrl', function () { activateControls(); var fullscreenNode = document.fullscreenElement || null; var cardNode = $card.get(0); if ($watch.hasClass('is-reel-immersive') || fullscreenNode === cardNode || $.contains(cardNode, fullscreenNode)) { exitImmersiveMode($watch, labels); } else { enterImmersiveMode($watch, $card, labels); } }); $close.off('click.fyCtrl').on('click.fyCtrl', function (e) { e.preventDefault(); e.stopPropagation(); exitImmersiveMode($watch, labels); }); if (video) { $video.on('timeupdate.fyCtrl durationchange.fyCtrl loadedmetadata.fyCtrl', setProgressValue); $video.on('play.fyCtrl pause.fyCtrl ended.fyCtrl', function () { setPlayLabel(); if (!getGlobalAudioState().muted && !video.paused) { enforceSingleAudibleVideo($watch, video); } }); $video.off('touchstart.fyReel click.fyReel').on('touchstart.fyReel click.fyReel', function () { if (!mobileTouchFullscreen || !isMobileViewport()) { return; } var fullscreenNode = document.fullscreenElement || null; if (!$watch.hasClass('is-reel-immersive') && fullscreenNode !== $card.get(0)) { enterImmersiveMode($watch, $card, labels); } if (video.paused) { var playPromise = video.play(); if (playPromise && typeof playPromise.catch === 'function') { playPromise.catch(function () {}); } } }); } $card.off('touchstart.fyCardReel click.fyCardReel').on('touchstart.fyCardReel click.fyCardReel', function (e) { if (!mobileTouchFullscreen || !isMobileViewport()) { return; } if ($(e.target).closest('.cm-fy-player-controls, .fy-media-actions--floating, a, button, input').length) { return; } var fullscreenNode = document.fullscreenElement || null; if (!$watch.hasClass('is-reel-immersive') && fullscreenNode !== $card.get(0)) { enterImmersiveMode($watch, $card, labels); } if (video && video.paused) { var playPromise2 = video.play(); if (playPromise2 && typeof playPromise2.catch === 'function') { playPromise2.catch(function () {}); } } else if (iframe) { embedPaused = false; postEmbedCommand('seekTo', [0, true]); postEmbedCommand('playVideo'); setPlayLabel(); } }); $card.off('mousemove.fyCtrl touchstart.fyCtrl click.fyCtrl').on('mousemove.fyCtrl touchstart.fyCtrl click.fyCtrl', function (e) { activateControls(); if ($(e.target).closest('.cm-fy-player-controls, .fy-media-actions--floating, a, button, input').length) { return; } if ($watch.hasClass('is-reel-immersive')) { if (video) { if (video.paused) { playActiveCardMedia($watch, $card, false); } else { video.pause(); } setPlayLabel(); } else if (iframe) { if (embedPaused) { embedPaused = false; playActiveCardMedia($watch, $card, false); } else { embedPaused = true; postEmbedCommand('pauseVideo'); } setPlayLabel(); } } }); $card.off('mouseleave.fyCtrl').on('mouseleave.fyCtrl', function () { if (!controlsAlwaysVisible && !controlsHidden) { $card.removeClass('is-controls-active'); } }); }); } function bindRfqForms($scope) { $scope.off('click.fyRfqPost').on('click.fyRfqPost', '.fy-media-rfq-chip', function (e) { var $link = $(this); var action = ($link.attr('href') || '').toString(); var productId = parseInt(($link.attr('data-product-id') || '0'), 10) || 0; var productFun = ($link.attr('data-product-fun') || '').toString(); var productName = ($link.attr('data-product-name') || '').toString(); if (!action || productId <= 0) { return; } e.preventDefault(); var form = document.createElement('form'); form.method = 'post'; form.action = action; form.style.display = 'none'; [ ['product_id', String(productId)], ['product_fun', productFun], ['product_name', productName] ].forEach(function (pair) { var input = document.createElement('input'); input.type = 'hidden'; input.name = pair[0]; input.value = pair[1] || ''; form.appendChild(input); }); document.body.appendChild(form); form.submit(); }); } function loadMore($watch) { if ($watch.data('fyLoading') === true) { return; } if (($watch.attr('data-fy-has-more') || 'N') !== 'Y') { return; } var endpoint = ($watch.attr('data-fy-endpoint-url') || '').toString(); if (!endpoint) { return; } $watch.data('fyLoading', true); $watch.find('.cm-fy-media-load-more').show(); var params = new URLSearchParams(); params.set('dispatch', 'fy_ai_media_generation.watch_feed_data'); params.set('view_mode', ($watch.attr('data-fy-watch-mode') || 'swipe')); params.set('cursor_asset_id', ($watch.attr('data-fy-next-cursor') || '0')); params.set('limit', ($watch.attr('data-fy-limit') || '12')); params.set('source_scope', ($watch.attr('data-fy-source-scope') || 'published')); params.set('vendor_scope', ($watch.attr('data-fy-vendor-scope') || 'enabled_only')); params.set('include_marketplace', ($watch.attr('data-fy-include-marketplace') || 'Y')); params.set('include_short', ($watch.attr('data-fy-include-short') || 'Y')); params.set('include_long', ($watch.attr('data-fy-include-long') || 'Y')); params.set('include_local', ($watch.attr('data-fy-include-local') || 'Y')); params.set('include_youtube', ($watch.attr('data-fy-include-youtube') || 'Y')); params.set('include_instagram', ($watch.attr('data-fy-include-instagram') || 'Y')); params.set('include_tiktok', ($watch.attr('data-fy-include-tiktok') || 'Y')); params.set('mix_mode', ($watch.attr('data-fy-mix-mode') || 'random')); params.set('randomize_feed', ($watch.attr('data-fy-randomize-feed') || 'N')); params.set('show_fun', ($watch.attr('data-fy-show-fun') || 'Y')); params.set('show_promo', ($watch.attr('data-fy-show-promo') || 'Y')); params.set('show_cta', ($watch.attr('data-fy-show-cta') || 'Y')); params.set('desktop_layout', ($watch.attr('data-fy-desktop-layout') || 'horizontal')); params.set('youtube_playlist_ids', ($watch.attr('data-fy-youtube-playlist-ids') || '')); params.set('youtube_feed_urls', ($watch.attr('data-fy-youtube-feed-urls') || '')); params.set('youtube_shorts_urls', ($watch.attr('data-fy-youtube-shorts-urls') || '')); params.set('instagram_feed_urls', ($watch.attr('data-fy-instagram-feed-urls') || '')); params.set('tiktok_feed_urls', ($watch.attr('data-fy-tiktok-feed-urls') || '')); params.set('infinite_scroll', ($watch.attr('data-fy-infinite') || 'Y')); params.set('promo_mode', ($watch.attr('data-fy-promo-mode') || 'campaign_or_manual')); params.set('promo_text', ($watch.attr('data-fy-promo-text') || '')); params.set('campaign_id', ($watch.attr('data-fy-campaign-id') || '0')); params.set('campaign_overlay_text', ($watch.attr('data-fy-campaign-overlay') || '')); params.set('campaign_cta_text', ($watch.attr('data-fy-campaign-cta-text') || '')); params.set('campaign_cta_url', ($watch.attr('data-fy-campaign-cta-url') || '')); params.set('category_context_mode', ($watch.attr('data-fy-category-context-mode') || 'N')); params.set('category_id', ($watch.attr('data-fy-runtime-category-id') || '0')); fetch(endpoint + (endpoint.indexOf('?') >= 0 ? '&' : '?') + params.toString(), { method: 'GET', credentials: 'same-origin', headers: { 'Accept': 'application/json' } }) .then(function (resp) { return resp.json(); }) .then(function (data) { if (!data || !Array.isArray(data.items)) { $watch.attr('data-fy-has-more', 'N'); return; } if (data.items.length > 0) { appendItems($watch, data.items); applyVideoAspectClasses($watch); playVisibleVideos($watch); initVideoControls($watch); initThumbSwitcher($watch); initDesktopNav($watch); } var pagination = data.pagination || {}; $watch.attr('data-fy-next-cursor', (pagination.next_cursor_asset_id || 0).toString()); $watch.attr('data-fy-has-more', (pagination.has_more || 'N')); if ((pagination.has_more || 'N') !== 'Y') { $watch.find('.cm-fy-media-load-more').hide(); } }) .catch(function () { $watch.attr('data-fy-has-more', 'N'); }) .finally(function () { $watch.data('fyLoading', false); if (($watch.attr('data-fy-has-more') || 'N') !== 'Y') { $watch.find('.cm-fy-media-load-more').hide(); } }); } function initInfiniteScroll($watch) { if (($watch.attr('data-fy-infinite') || 'N') !== 'Y') { return; } if (($watch.attr('data-fy-has-more') || 'N') !== 'Y') { return; } if ($watch.data('fyObserverBound') === true) { return; } var mode = ($watch.attr('data-fy-watch-mode') || 'swipe').toLowerCase(); var desktopLayout = ($watch.attr('data-fy-desktop-layout') || 'horizontal').toLowerCase(); var isDesktop = window.matchMedia && window.matchMedia('(min-width: 768px)').matches; if (mode === 'swipe' && desktopLayout === 'horizontal' && isDesktop) { $watch.data('fyObserverBound', true); var onScroll = function () { var node = $watch.get(0); if (!node) { return; } if ((node.scrollLeft + node.clientWidth) >= (node.scrollWidth - 420)) { loadMore($watch); } }; $watch.off('scroll.fyLoadMore').on('scroll.fyLoadMore', onScroll); onScroll(); return; } if (typeof window.IntersectionObserver === 'undefined') { $watch.find('.cm-fy-media-load-more').show().css('cursor', 'pointer'); $watch.off('click.fyLoadMore').on('click.fyLoadMore', '.cm-fy-media-load-more', function () { loadMore($watch); }); return; } var $sentinel = $('
'); $watch.append($sentinel); $watch.data('fyObserverBound', true); var observer = new IntersectionObserver(function (entries) { entries.forEach(function (entry) { if (!entry.isIntersecting) { return; } if (($watch.attr('data-fy-has-more') || 'N') !== 'Y') { return; } loadMore($watch); }); }, { root: null, rootMargin: '0px 0px 240px 0px', threshold: 0 }); observer.observe($sentinel.get(0)); } function pauseEmbeddedPlayers($scope) { $scope.find('iframe.cm-fy-watch-embed').each(function () { var frame = this; var src = (frame.getAttribute('src') || '').toLowerCase(); if (src.indexOf('youtube.com/embed/') === -1 && src.indexOf('youtube-nocookie.com/embed/') === -1) { return; } try { frame.contentWindow.postMessage(JSON.stringify({ event: 'command', func: 'pauseVideo', args: [] }), '*'); } catch (e) {} }); } function pauseAllWatchMedia() { $('.fy-media-showcase').each(function () { var $watch = $(this); $watch.find('video.cm-fy-watch-video').each(function () { try { this.pause(); } catch (e) {} }); pauseEmbeddedPlayers($watch); }); } if (!window.__fyWatchVisibilityBound) { window.__fyWatchVisibilityBound = true; document.addEventListener('visibilitychange', function () { if (document.hidden) { pauseAllWatchMedia(); } }); window.addEventListener('blur', pauseAllWatchMedia); window.addEventListener('pagehide', pauseAllWatchMedia); } if (!window.__fyWatchKeyboardBound) { window.__fyWatchKeyboardBound = true; document.addEventListener('keydown', function (e) { if ((e.key !== 'ArrowLeft' && e.key !== 'ArrowRight') || /input|textarea|select/i.test((e.target && e.target.tagName) || '')) { return; } var $active = $('.fy-media-showcase.is-reel-immersive').first(); if (!$active.length) { return; } if (e.key === 'ArrowLeft') { $active.find('.cm-fy-watch-nav-prev').trigger('click'); } else { $active.find('.cm-fy-watch-nav-next').trigger('click'); } e.preventDefault(); }); } $.ceEvent('on', 'ce.commoninit', function (context) { var $context = $(context || document); $context.find('.cm-fy-media-watch').each(function () { var $watch = $(this); applyVideoAspectClasses($watch); playVisibleVideos($watch); initVideoControls($watch); initThumbSwitcher($watch); initDesktopNav($watch); initInfiniteScroll($watch); }); }); }(Tygh, Tygh.$)); Service unavailable
Sorry, service is temporarily unavailable.