PageSourceSearch

https://jump.video/assets/js/jw-chromecast.js?v=5

js jump.video collected 2026-10-08 09:01:29 UTC 11,769 bytes, 335 lines download raw bytes

1/**
2 * JW8 Chromecast control for JUMP players (watch, pagebuilder, livefeed).
3 * Native google-cast-launcher stays empty until a device is found; JW skins
4 * often force-show .jw-icon-cast. Hide that slot and add exactly one JUMP button.
5 *
6 * Product: the JUMP icon is always shown in Chrome/Edge (HTTPS), even when
7 * no Chromecast receiver is discovered yet. Do not gate the button on Cast SDK.
8 */
9(function (window, document) {
10    'use strict';
11
12    if (window.jumpEnableJwChromecast) {
13        return;
14    }
15
16    window.JUMP_JW_CAST_CONFIG = {};
17
18    var CAST_ICON = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(
19        '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FFFFFF">' +
20        '<path d="M21 3H3c-1.1 0-2 .9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM1 18v3h3c0-1.66-1.34-3-3-3zm0-4v2c2.76 0 5 2.24 5 5h2c0-3.87-3.13-7-7-7zm0-4v2c4.97 0 9 4.03 9 9h2c0-6.08-4.93-11-11-11z"/>' +
21        '</svg>'
22    );
23
24    var enabledPlayers = typeof WeakSet === 'function' ? new WeakSet() : null;
25
26    function isChromecastBrowser() {
27        var ua = navigator.userAgent || '';
28        if (/iPhone|iPad|iPod/.test(ua)) {
29            return false;
30        }
31        if (/OPR\/|Opera/.test(ua)) {
32            return false;
33        }
34        return /Chrome|Chromium|Edg\//.test(ua);
35    }
36
37    function canShowJumpCastButton() {
38        if (!isChromecastBrowser()) {
39            return false;
40        }
41        return window.isSecureContext !== false;
42    }
43
44    function loadCastSender() {
45        if (document.querySelector('script[src*="cast_sender.js"]')) {
46            return;
47        }
48        var script = document.createElement('script');
49        script.src = 'https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1';
50        document.head.appendChild(script);
51    }
52
53    function injectCss() {
54        if (window._jumpJwCastCss) {
55            return;
56        }
57        window._jumpJwCastCss = true;
58        var style = document.createElement('style');
59        style.setAttribute('data-jump-jw-cast', '1');
60        style.textContent = [
61            /* Beat .watch-live-v2 #video-wrapper .jw-icon-cast { display: inline-flex !important } */
62            '.watch-live-v2 #video-wrapper .jw-icon-cast,',
63            '#video-wrapper.jwplayer .jw-icon-cast,',
64            '.jwplayer .jw-icon-cast,',
65            '.jwplayer .jw-icon-cast google-cast-launcher {',
66            '    display: none !important;',
67            '    visibility: hidden !important;',
68            '    width: 0 !important;',
69            '    min-width: 0 !important;',
70            '    padding: 0 !important;',
71            '    margin: 0 !important;',
72            '    overflow: hidden !important;',
73            '    pointer-events: none !important;',
74            '}',
75            '.jwplayer .jw-icon-airplay {',
76            '    display: inline-flex !important;',
77            '    visibility: visible !important;',
78            '    opacity: 1 !important;',
79            '}',
80            '.jwplayer .jw-icon-airplay .jw-svg-icon { fill: #fff !important; color: #fff !important; }',
81            '.watch-live-v2 #video-wrapper .jw-icon-jump-chromecast,',
82            '.jwplayer .jw-icon-jump-chromecast,',
83            '.jwplayer [button="jump-chromecast"] {',
84            '    display: inline-flex !important;',
85            '    visibility: visible !important;',
86            '    opacity: 1 !important;',
87            '    width: 44px;',
88            '    height: 44px;',
89            '    align-items: center;',
90            '    justify-content: center;',
91            '    cursor: pointer;',
92            '    pointer-events: auto !important;',
93            '}',
94            '.jwplayer .jw-icon-jump-chromecast img,',
95            '.jwplayer .jw-icon-jump-chromecast .jw-svg-icon {',
96            '    width: 24px;',
97            '    height: 24px;',
98            '    fill: #fff !important;',
99            '}',
100            /* JW addButton paints .jw-button-image on top; it ate the click. */
101            '.jwplayer .jw-icon-jump-chromecast,',
102            '.jwplayer [button="jump-chromecast"] {',
103            '    position: relative;',
104            '    z-index: 6;',
105            '}',
106            '.jwplayer .jw-icon-jump-chromecast *,',
107            '.jwplayer [button="jump-chromecast"] * {',
108            '    pointer-events: none !important;',
109            '}'
110        ].join('\n');
111        document.head.appendChild(style);
112    }
113
114    function playerRoot(player) {
115        try {
116            if (player && typeof player.getContainer === 'function') {
117                return player.getContainer();
118            }
119        } catch (e) { /* ignore */ }
120        return null;
121    }
122
123    function jumpCastNodes(player) {
124        var root = playerRoot(player);
125        if (!root) {
126            return [];
127        }
128        var list = root.querySelectorAll('.jw-icon-jump-chromecast, [button="jump-chromecast"]');
129        var out = [];
130        var seen = [];
131        for (var i = 0; i < list.length; i++) {
132            if (seen.indexOf(list[i]) !== -1) {
133                continue;
134            }
135            seen.push(list[i]);
136            out.push(list[i]);
137        }
138        return out;
139    }
140
141    function hasJumpCastButton(player) {
142        return jumpCastNodes(player).length > 0;
143    }
144
145    function initCastContext() {
146        if (window._jumpCastContextReady) {
147            return true;
148        }
149        if (!window.cast || !cast.framework || !window.chrome || !chrome.cast) {
150            return false;
151        }
152        try {
153            cast.framework.CastContext.getInstance().setOptions({
154                receiverApplicationId: chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID,
155                autoJoinPolicy: chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED
156            });
157            window._jumpCastContextReady = true;
158            return true;
159        } catch (e) {
160            return false;
161        }
162    }
163
164    function openCastPicker(player) {
165        initCastContext();
166        try {
167            if (window.cast && cast.framework) {
168                var ctx = cast.framework.CastContext.getInstance();
169                if (ctx.getCurrentSession()) {
170                    ctx.endCurrentSession(true);
171                    return;
172                }
173                ctx.requestSession().catch(function () { /* user cancel */ });
174                return;
175            }
176        } catch (e) { /* fall through */ }
177        var root = playerRoot(player) || document;
178        var launcher = root.querySelector('google-cast-launcher');
179        if (launcher && typeof launcher.click === 'function') {
180            launcher.click();
181            return;
182        }
183        try {
184            if (player && typeof player.castToggle === 'function') {
185                player.castToggle();
186            }
187        } catch (e2) { /* ignore */ }
188    }
189
190    function bindCastClicks(player) {
191        var nodes = jumpCastNodes(player);
192        var i;
193        for (i = 0; i < nodes.length; i++) {
194            if (nodes[i]._jumpCastBound) {
195                continue;
196            }
197            nodes[i]._jumpCastBound = true;
198            nodes[i].style.cursor = 'pointer';
199            nodes[i].addEventListener('click', function (ev) {
200                if (ev) {
201                    ev.preventDefault();
202                    ev.stopPropagation();
203                }
204                openCastPicker(player);
205            }, true);
206            nodes[i].addEventListener('keydown', function (ev) {
207                if (ev && (ev.key === 'Enter' || ev.key === ' ')) {
208                    ev.preventDefault();
209                    openCastPicker(player);
210                }
211            });
212        }
213    }
214
215    function tagAndDedupeJumpCast(player) {
216        var nodes = jumpCastNodes(player);
217        var i;
218        for (i = 0; i < nodes.length; i++) {
219            if ((' ' + nodes[i].className + ' ').indexOf(' jw-icon-jump-chromecast ') === -1) {
220                nodes[i].className += ' jw-icon-jump-chromecast';
221            }
222        }
223        for (i = 1; i < nodes.length; i++) {
224            if (nodes[i].parentNode) {
225                nodes[i].parentNode.removeChild(nodes[i]);
226            }
227        }
228        return nodes.length > 0;
229    }
230
231    function insertDomCastButton(player) {
232        var root = playerRoot(player);
233        if (!root || hasJumpCastButton(player)) {
234            return tagAndDedupeJumpCast(player);
235        }
236        var bar = root.querySelector('.jw-button-container')
237            || root.querySelector('.jw-controlbar-right-group')
238            || root.querySelector('.jw-controlbar');
239        if (!bar) {
240            return false;
241        }
242        var btn = document.createElement('div');
243        btn.className = 'jw-icon jw-button-color jw-reset jw-icon-jump-chromecast';
244        btn.setAttribute('role', 'button');
245        btn.setAttribute('tabindex', '0');
246        btn.setAttribute('aria-label', 'Chromecast');
247        btn.setAttribute('button', 'jump-chromecast');
248        btn.title = 'Chromecast';
249        btn.innerHTML = '<img alt="" src="' + CAST_ICON + '">';
250        var fullscreen = bar.querySelector('.jw-icon-fullscreen');
251        if (fullscreen && fullscreen.parentNode === bar) {
252            bar.insertBefore(btn, fullscreen);
253        } else {
254            bar.appendChild(btn);
255        }
256        return true;
257    }
258
259    function addJumpCastButton(player, allowDomFallback) {
260        if (!player || !canShowJumpCastButton()) {
261            return false;
262        }
263        if (tagAndDedupeJumpCast(player)) {
264            bindCastClicks(player);
265            return true;
266        }
267        /* Call addButton once. Never insertDom on the same pass (two icons). */
268        if (typeof player.addButton === 'function' && !player._jumpCastAddButtonTried) {
269            player._jumpCastAddButtonTried = true;
270            try {
271                player.addButton(CAST_ICON, 'Chromecast', function () {
272                    openCastPicker(player);
273                }, 'jump-chromecast');
274            } catch (e) { /* addButton can throw before controlbar exists */ }
275            if (tagAndDedupeJumpCast(player)) {
276                bindCastClicks(player);
277                return true;
278            }
279            return false;
280        }
281        if (allowDomFallback) {
282            if (insertDomCastButton(player)) {
283                bindCastClicks(player);
284                return true;
285            }
286            return false;
287        }
288        if (tagAndDedupeJumpCast(player)) {
289            bindCastClicks(player);
290            return true;
291        }
292        return false;
293    }
294
295    window.jumpEnableJwChromecast = function (player) {
296        loadCastSender();
297        injectCss();
298        var prevCastReady = window.__onGCastApiAvailable;
299        window.__onGCastApiAvailable = function (loaded, errorInfo) {
300            if (loaded) {
301                initCastContext();
302            }
303            if (typeof prevCastReady === 'function') {
304                prevCastReady(loaded, errorInfo);
305            }
306        };
307
308        if (!canShowJumpCastButton() || !player) {
309            return;
310        }
311
312        var already = enabledPlayers && enabledPlayers.has(player);
313        if (enabledPlayers) {
314            enabledPlayers.add(player);
315        }
316
317        var attempts = 0;
318        var tryAdd = function () {
319            if (addJumpCastButton(player, attempts >= 8)) {
320                return;
321            }
322            attempts += 1;
323            if (attempts < 40) {
324                setTimeout(tryAdd, 250);
325            }
326        };
327        tryAdd();
328
329        if (!already && typeof player.on === 'function') {
330            player.on('ready', function () {
331                addJumpCastButton(player, true);
332            });
333        }
334    };
335})(window, document);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.