Skip to content

Integration file

Source code of integration.js

integration.js file
const IFRAME_MAX_OFFSET = 0;
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 getIframePosition = () => {
  const iframeEl = document.getElementById('betbook');
  return iframeEl?.getBoundingClientRect();
};

const postMessage = (value, type = 'iframe') => {
  try {
    window.frames.target.postMessage(
      {
        type,
        value,
      },
      '*', // @todo use config.get(currentHost) later
    );
  } catch (e) {
    console.error(e);
  }
};

const handleScroll = () => {
  module.emit({isScrollEnabled: getIframePosition()?.top > 0}, 'iframe-scroll');
}

const mount = (id, inputUrl, onLoadCallback = () => {}, attributes = {}) => {
  const defaultAttributes = {
    frameBorder: 0,
    id: 'betbook',
    class: 'betbook',
    allow:
      'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; 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('scrollParent', ({ value }) => {
  if (module.getData('iframePosition')?.top) {
    const val = window.scrollY + value;
    window.scrollTo({
      top: Math.min(val, module.getData('iframePosition').top),
      behavior: 'auto',
    });
  }
});
messagePubSub.subscribe('sdkInit', ({ value }) => {
  const fallbackMode = value?.defaultThemeMode || THEME_MODE.DEFAULT;
  const iframePosition = getIframePosition();
  if (iframePosition?.top > IFRAME_MAX_OFFSET) {
    module.setData({iframePosition});
    handleScroll();
    window.addEventListener('scroll', (e) => {
      handleScroll();
    });
  }
  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;