Skip to Content

Source

The full integration.js file. Copy it into your project — for example as services/integration.js — and import it as shown on Overview.

Because you own the copy, you also own keeping it current: check this page when upgrading and diff it against yours. Which parts are safe to change, and which are protocol-critical, is on Overview.

integration.js file
const THEME_MODE = { DEFAULT: 'dark', LIGHT: 'light', DARK: 'dark', }; let currentLang = 'en'; let currentThemeMode = localStorage.getItem('prefers-color-scheme'); let prevMountedUrl = null; const configuration = { languages: [currentLang], blacklistedRoutes: ['/mini-iframe'], }; const setThemeMode = (mode) => { currentThemeMode = mode; document.body.setAttribute('data-theme', mode); localStorage.setItem('prefers-color-scheme', mode); }; const emitThemeMode = () => { module.emit({ mode: currentThemeMode }, 'theme-mode-change'); }; const toggleThemeMode = (mode) => { if (mode) { setThemeMode(mode); } else { setThemeMode( currentThemeMode === THEME_MODE.LIGHT ? THEME_MODE.DARK : THEME_MODE.LIGHT, ); } emitThemeMode(); }; const emitThemeName = (name) => { module.emit({ name }, 'theme-mode-change'); }; const getUrlLang = (path, languages) => path .split(/[/?]/) .filter(Boolean) .find((segment) => languages.includes(segment)); const postMessage = (value, type = 'iframe') => { try { window.frames.target.postMessage( { type, value, }, '*', // @todo use config.get(currentHost) later ); } catch (e) { console.error(e); } }; const mount = (id, inputUrl, onLoadCallback = () => {}, attributes = {}) => { const defaultAttributes = { frameBorder: 0, id: 'betbook', class: 'betbook', allow: 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; fullscreen; picture-in-picture; web-share', }; const prevPath = module.getData('path'); const prevQuery = module.getData('query'); const [url, query = ''] = inputUrl.split('?'); let fullQuery = ''; if (query) { fullQuery = `?${query}`; } let iframeUrl = url + fullQuery; /** take lang from iframe url */ currentLang = getUrlLang(url, configuration.languages); const { href, pathname } = new URL(window.location.href); const hasLang = new RegExp(`/${currentLang}(/|\\?)`).test(href); const currentLangSubstring = hasLang ? `/${currentLang}/` : '/'; const prefixedOrigin = `${window.origin}/${configuration.urlPrefix}`; const hasSearchParams = href.indexOf('?') > 0; const path = prevPath || pathname.substring( configuration.urlPrefix.length + currentLangSubstring.length + 1, ); let search = ''; if (prevQuery) { search = `${fullQuery ? '&' : '?'}${prevQuery}`; } else if (hasSearchParams) { const searchString = href.substring(href.indexOf('?') + 1); search = `${fullQuery ? '&' : '?'}${searchString}`; } /** if page was refreshed when url is not home or unwanted redirect to home */ if (href.includes(prefixedOrigin) || (pathname === '/' && prevPath)) { iframeUrl = url + path + fullQuery + search; } if (prevMountedUrl === iframeUrl) return null; prevMountedUrl = iframeUrl; const iframe = document.createElement('iframe'); iframe.setAttribute('src', iframeUrl); iframe.setAttribute('name', 'target'); iframe.setAttribute('onload', onLoadCallback); const props = { ...defaultAttributes, ...attributes }; Object.entries(props).forEach(([name, value]) => { iframe.setAttribute(name, value); }); try { if (props.id && document.getElementById(props.id)) { document.getElementById(props.id).setAttribute('src', iframeUrl); } else { document.getElementById(id).append(iframe); } } catch (e) { console.error(e); } postMessage({ path: `/${path || ''}` }, 'iframe-navigation'); return iframe; }; const messagePubSub = { messages: new Map(), subscribers: new Map(), subscribe: (event, callback) => { if (!messagePubSub.subscribers.has(event)) { messagePubSub.subscribers.set(event, new Set()); } const callbacks = messagePubSub.subscribers.get(event); callbacks.add(callback); return { unsubscribe: () => { callbacks.delete(callback); if (callbacks.size === 0) { messagePubSub.subscribers.delete(event); } }, }; }, notifySubscribers: (event, data) => { const subscriberCallback = messagePubSub.subscribers.get(event); if (subscriberCallback) { subscriberCallback.forEach((cb) => cb(data)); } }, }; window.addEventListener('message', ({ data }) => { if (!data.type) return; messagePubSub.notifySubscribers(data.type, data); }); messagePubSub.subscribe('navigation', (data) => { const { path, previousPath } = data; const isEmptyPrevPath = previousPath === undefined || previousPath === `/${currentLang}/`; if ( configuration.isNavigationCaptureDisabled || !path || (path === '/' && isEmptyPrevPath) || path === `/${currentLang}/` || configuration.blacklistedRoutes.includes(path) ) { return; } let query = ''; if (data.query instanceof Object && Object.keys(data.query).length !== 0) { query = new URLSearchParams(data.query); query.delete('jwt'); if (query.toString().length) { query = `?${query.toString()}`; } } const url = path === '/' ? window.location.origin : `${window.location.origin}/${configuration.urlPrefix}/${currentLang}${path}${query}`; window.history.replaceState(null, document.title, url); module.setData({ path: `${path}`.substring(1) }); module.setData({ query: `${query}`.substring(1) }); }); messagePubSub.subscribe('sdkInit', ({ value }) => { const fallbackMode = value?.defaultThemeMode || THEME_MODE.DEFAULT; module.emit({ href: window.location.href }, 'client-url'); setThemeMode(currentThemeMode || fallbackMode); emitThemeMode(); }); const module = { init: ({ languages, prefix = 'sports', blacklistedRoutes, isNavigationCaptureDisabled = false, }) => { configuration.languages = languages; configuration.urlPrefix = prefix; configuration.blacklistedRoutes = configuration.blacklistedRoutes || blacklistedRoutes; configuration.isNavigationCaptureDisabled = isNavigationCaptureDisabled; return module; }, mount, on: (event, callback) => { const { unsubscribe } = messagePubSub.subscribe(event, callback); return { off: unsubscribe, }; }, emit: (data, type) => { postMessage(data, type); }, helpers: { logout: () => module.emit({ logout: true }), toggleThemeMode, emitThemeName, getCurrentThemeMode: () => currentThemeMode, updateCurrency: (currency) => module.emit({ currency }, 'currency-update'), }, setData: (data) => { Object.entries(data).forEach(([key, value]) => { module.store[key] = value; }); }, getData: (dataName) => module.store[dataName], store: {}, }; export default module;