This documentation will guide you through the process of embedding our widgets on your page smoothly.
Each widget you can embed on your page has its own configuration that defines what type of widget it is and how it is customized.The configuration is provided to you by STATSCORE and it has format of 24 character string ex. 2d21e6a2db4e9207a5eb2c65. Additionally each type of widget has its own input data structure that you need to provide when you are embedding it. For example, when you want to embed LivematchProV3 Widget which covers certain sport event then input data you provide would contain: event id and language
To be able to embed widgets on your page first thing you need to do is include our library:
<script>
!function(){var d="STATSCOREWidgetsEmbederScript";if(!window.document.getElementById(d)){window.STATSCOREWidgets={},window.STATSCOREWidgets.onLoadCallbacks=[],window.STATSCOREWidgets.onLoad=function(d){window.STATSCOREWidgets.onLoadCallbacks.push(d)};var n=window.document.createElement("script");n.src="https://wgt-s3-cdn.statscore.com/bundle/Embeder.js",n.async=!0,n.id=d,n.addEventListener("error",function(d){for(var n=0;n<window.STATSCOREWidgets.onLoadCallbacks.length;n++)window.STATSCOREWidgets.onLoadCallbacks[n](d)}),window.document.body.appendChild(n)}}();
</script>
STATSCOREWidgets.onLoad(callback: () => void): void
to wait until library is loaded and ready. Remember that to access STATSCOREWidgets
variable, library needs to be placed above any script that you execute later!<div id="myWidget"></div>
// Hook up when library is loaded and ready to use.
// You can use this method as many times as necessary - if library
// is already loaded provided callback will fire immediately
STATSCOREWidgets.onLoad(err => {
if (err) {
switch (err.type) {
case 'NetworkError':
// Handle network error here
break;
case 'BrowserNotSupported':
// Handle unsupported browser here
break;
}
return;
}
// Widget will be appended to this HTMLElement.
//
// If you are using framework then follow its documentation
// on how to get DOM Element from your component.
// Vue.js https://vuejs.org/v2/api/#ref
// React https://en.reactjs.org/docs/refs-and-the-dom.html
// Angular https://angular.io/api/core/ElementRef
const element = document.getElementById('myWidget');
// Configuration that you should receive from STATSCORE
const configurationId = '2d21e6a2db4e9207a5eb2c65';
// Input data for widget type you want to embded
const inputData = { eventId: 3038800, language: 'en' };
// Optional object with options.
// You can check available options further in the docs.
const options = {};
const widget = new window.STATSCOREWidgets.Widget(element, configurationId, inputData, options);
});
const someEventCallback = () => { /* .. do stuff */ };
You can listen for event using "on" and "once" methods
widget.on('someEvent', someEventCallback);
widget.once('someEvent', someEventCallback);
You can destroy listener by using and off method
widget.off('someEvent', someEventCallback);
Available lifecycle events:
widget.on('beforeInsert', () => { /* Triggers when data necessary to display widget is loaded and widget is ready to be inserted into document */ });
widget.on('load', () => { /* Triggers when widget is loaded but not yet interactive */ });
widget.on('mount', () => { /* Triggers when widget is loaded and interactive */ });
widget.on('error', e => { /* Handle errors here */ });
You can handle more specific errors by checking it's error code. See full list of possible codes <a href="#error-codes">here</a>
widget.on('error', e => {
if (e.code === 1234) {
console.log(e.message);
...
}
});
Event coverage:
widget.on('requiredCoverageNotSatisfied', ({ isFtOnly: boolean, isBasic: boolean, statsLvl: StatsLvl}) => {
// If given widget depends on "eventId" input data and "satisfiesCoverage" option
// was provided this event will be triggered when given constrains weren't satisfied.
});
You can find more about "satisfiesCoverage" option in <a href='#options'>"Options" section</a>.
// Remove our widget from DOM Tree and cleanup everything that is
// related to this widget. You can remove widget from the DOM Tree
// directly - destroy method will be queued and executed after short
// delay, although we recommend using destroy method directly.
//
// If you are using framework then we recommend hooking into one of
// component lifecycle hooks and calling destroy directly
// Vue.js https://vuejs.org/v2/api/#beforeDestroy
// React https://en.reactjs.org/docs/react-component.html#componentwillunmount
// Angular https://angular.io/guide/lifecycle-hooks#ondestroy
await widget.destroy();
If you find yourself in a situation when you embed the same type of widget for each sport, you can use widget groups to organize things a little.
A group of configurations allows you to have one entity which links each configuration you choose with the sport you choose. The only limitation is that each group can have only one type of widgets inside (for example, you can't have LivematchProWidget and StandingsWidget in one group).
After you create your group, you can use its ID to embed it. Only difference between embedding regular configuration is that you have to use STATSCOREWidgets.WidgetGroup class instead of STATSCOREWidgets.Widget. API of WidgetGroup class is exactly the same as Widget class.
Widget | Parameter(s) |
---|---|
EventIncidents (by event) | language |
eventId | |
providerId (optional) |
Widget | Parameter(s) |
---|---|
Events (by season) | language |
seasonId | |
eventType | |
groupId (optional) | |
Events (by season & participant) | language |
seasonId | |
participantId | |
eventType | |
groupId (optional) | |
Events (by participant) | language |
participantId | |
eventType |
Widget | Parameter(s) |
---|---|
EventStats (by event) | language |
eventId | |
providerId (optional) |
Widget | Parameter(s) |
---|---|
FormH2H (by event) | language |
eventId | |
providerId (optional) |
Widget | Parameter(s) |
---|---|
GeneralH2H (by participants) | language |
participantId1 | |
participantId2 | |
GeneralH2H (by event) | language |
eventId | |
providerId (optional) |
Widget | Parameter(s) |
---|---|
H2H (by event) | language |
eventId | |
providerId (optional) |
Widget | Parameter(s) |
---|---|
Infographic (by event) | language |
eventId | |
providerId (optional) |
Widget | Parameter(s) |
---|---|
LastMatchesH2H (by participants) | language |
participantId1 | |
participantId2 | |
LastMatchesH2H (by event) | language |
eventId | |
providerId (optional) |
Widget | Parameter(s) |
---|---|
Lineups (by event) | language |
eventId | |
providerId (optional) |
Widget | Parameter(s) |
---|---|
LiveBar (by event) | language |
eventId | |
providerId (optional) |
Widget | Parameter(s) |
---|---|
Live (by event) | language |
eventId | |
providerId (optional) |
Widget | Parameter(s) |
---|---|
Scoreboard (by event) | language |
eventId | |
providerId (optional) |
Widget | Parameter(s) |
---|---|
Standings (by event) | language |
eventId | |
providerId (optional) | |
Standings (by season) | language |
seasonId | |
Standings (by competition) | language |
competitionId | |
providerId (optional) |
Widget | Parameter(s) |
---|---|
Standing (by standing) | language |
standingId | |
Standing (by event) | language |
eventId | |
providerId (optional) | |
standingType | |
Standing (by season) | language |
seasonId | |
standingType | |
Standing (by stage) | language |
stageId | |
standingType | |
Standing (by competition) | language |
competitionId | |
standingType | |
providerId (optional) |
Widget | Parameter(s) |
---|---|
StatsH2H (by event) | language |
eventId | |
providerId (optional) |
Widget | Parameter(s) |
---|---|
StatsSlider (by event) | language |
eventId | |
providerId (optional) | |
StatsSlider (by season) | language |
seasonId | |
StatsSlider (by competition) | language |
competitionId | |
providerId (optional) |
Widget | Parameter(s) |
---|---|
LivematchPro (by event) | language |
eventId | |
providerId (optional) |
Widget | Parameter(s) |
---|---|
PrematchPro (by event) | language |
eventId | |
providerId (optional) |
Widget | Parameter(s) |
---|---|
Vote | language |
eventId | |
providerId (optional) |
Parameter | Type | Description |
---|---|---|
language | string | Language (default "en") |
eventId | number | Id of sport event. If you are NOT using STATSCORE ids then you should prefix the id with "m:" (for example m:123) |
participantId1, participantId2 | number | Ids of participants you want to compare |
standingId | number | Id of standing |
seasonId | number | Id of season |
groupId | number | Id of group |
stageId | number | Id of stage |
competitionId | number | Id of competition. If you are NOT using STATSCORE ids then you should prefix the id with "m:" (for example m:123) |
standingType | string | Available options depends on sport (see table below) |
eventType | string | Possible values: fixture, results |
invertedParticipants | boolean | Set 'true' if you want to invert participants (e.g. for American sports) |
providerId | number | Id of mapping provider. This option works only if your account has enabled option for multiple mapping providers. |
Sport | Standing types |
---|---|
American Football | League, Home, Away, Form |
Baseball | League, Home, Away, Form |
Basketball | League, Home, Away, Form |
Basketball 3x3 | League |
Futsal | League, Home, Away, Form, TopScorers, Cards, Halftime |
Handball | League, Home, Away, Form, Halftime |
Ice hockey | League, Home, Away, Form |
Rugby | League, Home, Away |
Rugby League | League |
Soccer | League, Home, Away, Form, TopScorers, Assists, Cards, Halftime |
Speedway | League, GrandPrix |
Tennis | AtpRanking, AtpDoubles, WtaRanking, WtaDoubles |
Volleyball | League, Home, Away, Form |
Name | Type | Default | Description |
---|---|---|---|
loader | { enabled?: boolean, size?: number, color1?: string, color2?: string } | { enabled: false, size: 60, color1: '#ffffff', color2: '#808080' } | Decide if you want to display loader and customize it. Keep in mind that if someone has good connection displaying loader can actually make widgets show up slower since we wait until initial animation of loader is finished before displaying the widget |
mobileAppToken | string | undefined | undefined | If you are embedding widgets in mobile application you must pass token that was provided by STATSCORE |
satisfiesCoverage | { ftOnlyAcceptable?: boolean, basicAcceptable?: boolean, minStatsLevel?: StatsLvl } | undefined | Passing this option embeds widget conditionally if given event coverage constraints are fulfilled. If given coverage constraints are not sasisfied "requiredCoverageNotSatisfied" event is triggered. You can find example in "JavaScript - events API" section. ftOnlyAcceptable - should widget be loaded if only full time result is available for given event. basicAcceptable - should widget be loaded if given event has only basic coverage. minStatsLevel - minimum stats level that given event should have in order to be loaded. Should be one of values: bronze, silver, gold, vip. This option works only for widgets with input data that contains "eventId" |
Code | Description |
---|---|
1000 | Configuration with given ID cannot be found |
1001 | Widget group with given ID cannot be found |
1002 | Widget group ID has wrong format |
1003 | Widget group does not contain configuration for given sport |
1004 | Token authorization failed |
1005 | Host authorization failed |
1006 | Host authorization failed. Subdomains are going to deep |
1007 | Event not found |
1008 | Event doesn't exist |
1009 | Participants not found |
1010 | Competition doesn't exist |
1011 | Season not found |
1012 | Stage not found |
1013 | Standing not found |
1014 | Sport not found |
1015 | Venue not found |
1016 | Invalid incidents ID's |
1017 | Brackets for stage or stage doesn't exist |
1018 | There is no prematch widget instance. If Widgets and LiveSwapper were initialized asynchronously then this may be the issue |
1019 | eventId is required input data parameter in both prematch and live widgets when using LiveSwapper |
1020 | eventId should be same in both prematch and live widgets |
1021 | Invalid LiveSwapper options property |
1022 | Invalid satisfiesCoverage property |
1023 | Invalid satisfiesCoverage.ftOnlyAcceptable property |
2002 | Invalid satisfiesCoverage.basicAcceptable property |
1024 | Invalid satisfiesCoverage.minStatsLevel property |
1025 | Provided configuration doesn't support sport derived from input data |
1026 | Provider does not exist or is not available |
1027 | Mapping for provided event id does not exist |
1028 | Mapping for provided competition id does not exist |
Integrations allow to communicate and pass data to widgets and components. Each integration has its own API and its available when involved component gets initiated.
You can use given integration by using getIntegration method:
widget.getIntegration('IntegrationName', integration => {
// Here you can use integration that has been resolved
});
Interactivity integration can be used to intercept various user interactions (for example clicks on elements)
Example of binding standing participants click (integration is available in standing components):
widget.getIntegration('Interactivity', integration => {
const onClickCallback = participant => {
console.log(participant);
};
// Use integration standingParticipant.onClick method to bind your callback
integration.standingParticipant.onClick(onClickCallback);
// To remove click bind use standingParticipant.offClick method
// In this example bind is removed after timeout
setTimeout(() => {
integration.standingParticipant.offClick(onClickCallback);
}, 5000);
});
Example of binding event participants click (integration is available in components with lists of events):
widget.getIntegration('Interactivity', integration => {
const onClickCallback = participant => {
console.log(participant);
};
// Use integration eventParticipant.onClick method to bind your callback
integration.eventParticipant.onClick(onClickCallback);
// To remove click bind use eventParticipant.offClick method
// In this example bind is removed after timeout
setTimeout(() => {
integration.eventParticipant.offClick(onClickCallback);
}, 5000);
});
BetTip integration allows you to attach odds to betting hints and filter them according to your criteria.
To initialize the integration, simply call setBetTipCreator
method and pass callback in which you can attach options (like odds or on click behaviour) to given tip provided by us.
let refresh: (() => void) | undefined;
widget.getIntegration('Tipster', integration => {
refresh = integration.setBetTipCreator((tip: Tip) => {
// Null if you don't have odds for given market or
// you don't want to display tip for this bet
const odds: number | null = myFunctionToGetOdds(tip);
// Active tips are styled differently - you can for example
// set given tip as active if user has given bet in betslip
const isActive: boolean = myFunctionToCheckIfGivenTipShouldBeActive(tip);
// Define what happens when someone clicks on the tip
const onClick: (() => void) | undefined = (tip: Tip) => {
myFunctionThatAddsGivenBetToBetSlip(tip);
// If you want to see any changes in the widget, you need to call the
// refresh function. In this example we assume
// that adding a bet to the bet slip will make this tip active
// in which case you will need to call refresh to update the
// tip on the widget
refresh();
};
return {
odds,
isActive,
onClick,
};
});
});
// You can call refresh everytime you want to change any options attached to tips
refresh();
Properties available in Tip type:
type Tip = {
tipId: string
type: 'InPlay' | 'Prematch'
marketId: number
betName: string
betLine: string | null
text: string
}
Here are some assumptions that should help you understand the code and principles behind this integration:
setBetTipCreator
method returns function that you can call everytime you want to change options you attached to tips (for example when odds for some bets changed and you want to update them on the widget)setBetTipCreator
provided callback is executed for each tipBy default:
setBetTipCreator
method, only tips with odds attached will be displayed (before calling setBetTipCreator tips are displayed regardless if they have odds attached)Both of those behaviours can be changed. For starters you can change constraint that is responsible for displaying only tips with odds attached to them:
Example 1. Displaying tips regardless if it has odds
integration.setAdditionalConstraintsForDefaultTipSelector((betTip: BetTip): boolean => {
return true;
});
Example 2. Displaying tip if odds are attached and higher than 1.5
integration.setAdditionalConstraintsForDefaultTipSelector((betTip: BetTip): boolean => {
return betTip.odds !== null && betTip.odds > 1.5;
});
In previous examples we changed part of logic in default tips selector (function responsible for filtering tips). To change one tip per market constraint you have to replace tips selector with custom one:
Example 1. Displaying every available tip
integration.setTipSelector((betTips: BetTip[]): BetTip[] => {
return betTips.filter(betTip => true);
});
Keep in mind that one market can have multiple tips with multiple lines so displaying everything may lead to filling widget with very similar tips.
Properties available in BetTip type:
type BetTip = {
tipId: string
type: 'InPlay' | 'Prematch'
marketId: number
betName: string
betLine: string | null
text: string
}
Various components (for example PrematchBoard in PrematchProWidget) support displaying bets that you can pass through Bets integration:
widget.getIntegration('Bets', integration => {
integration.setMarkets([
{
bets: [
{
betName: '1',
odds: 1.56,
isActive: false,
trend: 'neutral',
onClick: () => {
// ...
}
}
]
}
]);
});
Instead of passing array with markets you can also pass single market object. In case where you pass array then in components where we display single market first one will be displayed.
Properties available in Market type:
type Bet = {
// Text that is displayed for this bet.
betName: string
// Bet odds
odds: number
// If given bet is active then its styled differently.
// You can for example set given bet as active if it's in betslip.
isActive: boolean
// Arrow suggesting if odds for given bet went up or down.
trend: 'up' | 'down' | 'neutral'
// You can define what happens when someone clicks on
// given bet. For example you can add it to betslip.
onClick?: () => void
}
type Market = {
bets: Bet[]
}
Keep in mind that in some scenarios, for example:
You have to call setMarkets again and pass new data.
If you ever find yourself in need of displaying different prematch and live widgets, we offer possibility to switch those widgets automatically for you. You can decide which widget to display before match starts and which widget to display shortly before or when match is live.
When embedding widget this solution loads either live or prematch widget. If prematch widget was loaded it will automatically switch to given live widget shortly before event starts.
This solution works only for widgets that include eventId in their input data.
// Note: you can also use WidgetGroup instead of Widget
const prematchWidget = new window.STATSCOREWidgets.Widget(element, prematchConfigurationId, prematchInputData, prematchOptions);
const liveWidget = new window.STATSCOREWidgets.Widget(element, liveConfigurationId, liveInputData, liveOptions);
const liveSwapper = new window.STATSCOREWidgets.LiveSwapper(prematchWidget, liveWidget, {
// Optional
// If passed, prematch -> live swap won't occur if coverage you gave is not satisfied.
// Structure of object is same as "satisfiesCoverage" option in Widget.
satisfiesCoverage: {
ftOnlyAcceptable: false,
basicAcceptable: false,
minStatsLevel: 'bronze'
}
});
liveSwapper.on('error', e => {
if (e.code === 1234) {
console.log(e.message)
...
}
});
LiveSwapper error event catches errors from both passed widgets and errors from LiveSwapper itself. For more details about error event refer to error event in widget.
You can either remove currently embedded widget with destroy method as you would normally do or use LiveSwapper destroy method which determines which widget is currently embedded and removes it:
await liveSwapper.destroy();
// LiveSwapper has its own events you can listen to.
// It has same events API as Widget objects (on, once, off methods).
liveSwapper.on('swapCoverageNotSatisfied', ({ isFtOnly, isBasic, statsLvl }) => {
// ...
});
liveSwapper.on('embedding', ({
// Widget instance that is being embedded
widget,
// Is current embed swap from prematch to live.
// Returns true only if transition happens from already embeded prematch widget.
isSwap
}) => {
// ...
});
The widgets provided by STATSCORE are copyrighted and may only be used by entering into the relevant license agreement, which can be found at https://www.statscore.com/service-license-agreement. STATSCORE sp. z o.o. is the only entity authorized to enter into licensing agreements, if you have been provided with a widget by any other entity please contact us immediately. Any unauthorized use of the widget constitutes copyright infringement and is therefore illegal.
STATSCORE is committed to protecting its interests as well as the interests of its customers using the widgets lawfully and will respond to any case of infringement.
Copyright (C) STATSCORE sp. z o.o. 2019-2024
The livescore widgets posted on the website are the property of STATSCORE sp. z o.o., to which STATSCORE sp. z o.o. owns the full economic and moral copyrights. Use of the widgets in any way is subject to the terms of the Service License Agreement, the full text of the document is available at https://www.statscore.com/service-license-agreement.
It is prohibited to use or redistribute the widgets in any form if you have not previously entered into the Service License Agreement with STATSCORE sp. z o.o.. If you wish to do so, you should contact STATSCORE sp. z o.o. in order to obtain more information. Acceptance of the Service License Agreement terms and conditions alone, without STATSCORE sp. z o.o. knowledge, will not be considered a valid conclusion of the contract.
If the widgets have been provided or transferred to you in any form and you have not previously entered into an agreement with STATSCORE sp. z o.o. please contact us.
You can contact STATSCORE sp. z o.o. at the following e-mail address license@statscore.com.