(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 = '';
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 +
'' +
'';
}
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.