PageSourceSearch

https://sportal.bg/assets/smp/widget.v2.js?v=20260930095911

js sportal.bg collected 2026-10-01 11:40:57 UTC 50,551 bytes, 1,148 lines download raw bytes

1// ============================================================================
2// КОНСТАНТИ
3// ============================================================================
4
5const API_ENVIRONMENT = window.widgetConfig?.apiEnvironment || 'api.sandbox';
6const SMP_API_KEY = window.widgetConfig?.smpApiKey || 'sportal.bg:X8hMnFQNsfiJ8ER2gZgpIKIBSCbf7BK0';
7const DEBOUNCE_DELAY = 500;
8const ODDS_OVERLAP_INTERVAL = 3000;
9
10// Общи sdkOptions за всички SDK-та. Widget Studio Check не чете тези полета,
11// затова spread-ът тук не пречи на проверката (за разлика от labels/widgetAttributes).
12const BASE_SDK_OPTIONS = {
13    dataConfigApiKey: SMP_API_KEY,
14    dataConfigProject: 'sportal.bg',
15    dataConfigOddClient: 'sportal_web',
16    dataConfigTimezone: 'Europe/Sofia',
17    dataConfigLang: 'bg'
18};
19
20// ============================================================================
21// UTILITY ФУНКЦИИ
22// ============================================================================
23
24const buildApiUrl = (service) => `https://${service}.${API_ENVIRONMENT}.sportal365.com`;
25
26// ============================================================================
27// КОНФИГУРАЦИЯ НА СПОРТОВЕ
28// ============================================================================
29
30// data-labels и widgetAttributes са изписани с литерали за всеки спорт (без spread/helper-и):
31// Widget Studio → Config manager → Check чете само литерали и иначе показва фалшиви
32// "липсващи" етикети/атрибути. Само стойностите, зависещи от средата, са изрази.
33const smpConfig = {
34    football: {
35        sdkOptions: {
36            ...BASE_SDK_OPTIONS,
37            dataConfigApiUrl: buildApiUrl('football'),
38            dataConfigStandingsApiUrl: buildApiUrl('standing'),
39            dataConfigMultisportApiUrl: buildApiUrl('multi-sport'),
40            dataConfigCompetitionList: 'all'
41        },
42        widgetAttributes: {
43            'data-header-display': true,
44            'data-refresh-time': 'medium',
45            'data-popular-list': 'sportal_popular',
46            'data-odds-display': true,
47            'data-odds-pre-event-only': false,
48            'data-betting-logo-overlap': true,
49            'data-theme': window.widgetTheme?.() || 'light',
50            'data-odds-betting-id': window.selectedBookies?.football?.id ?? '',
51            'data-name-format': 'last_name',
52            'data-labels': {
53                all: 'Всички',
54                popular: 'Популярни',
55                upcoming: 'Предстоящи',
56                live: 'На Живо',
57                odds: 'С коефициенти',
58                finished: 'Приключили',
59                standings: 'Класиране',
60                score_table_label: 'Резултати',
61                next_match: 'Следващ мач:',
62                no_games: 'Няма мачове',
63                fixtures: 'Предстоящи срещи',
64                results: 'Резултати',
65                current_season: 'Текущ сезон',
66                team: 'Отбор',
67                matches_won_short: 'П',
68                points_short: 'Т',
69                matches_played_short: 'М',
70                match_losses_short: 'З',
71                match_draws_short: 'Р',
72                team_form_short: 'Форма',
73                team_form: 'Форма на отбора',
74                matches_played: 'Изиграни мачове',
75                goal_difference: 'Голова разлика',
76                points: 'Точки',
77                matches_won: 'Спечелени мачове',
78                match_draw: 'Равенства',
79                match_losses: 'Загубени мачове',
80                no_odds: 'Няма налични коефициенти',
81                '1x2': '1Ð¥2',
82                OVER_UNDER: 'Брой голове',
83                DOUBLE_CHANCE: 'Двоен шанс',
84                BOTH_TO_SCORE: 'Двата отбора да отбележат',
85                DRAW_NO_BET: 'Равен няма залог',
86                FIRST_TEAM_TO_SCORE: 'Първи гол',
87                GAME_HANDICAP: 'Хендикап',
88                over: 'над',
89                under: 'под',
90                yes: 'Да',
91                no: 'Не',
92                name: 'Име',
93                age: 'Възраст',
94                minutes: 'Минути',
95                yellow_cards: 'Жълти картони',
96                red_cards: 'Червени картони',
97                forwards: 'Нападатели',
98                player: 'Играч',
99                played: 'Мачове',
100                assists: 'Асистенции',
101                goals: 'Голове',
102                conceded: 'Допуснати голове',
103                cleansheets: 'Чиста мрежа',
104                started: 'Титуляр',
105                no_data: 'Няма налична информация',
106                first_yellow_cards: 'Първи жълт картон',
107                total_cards: 'Общо',
108                group: 'Група',
109                H2H: 'H2H',
110                W: 'П',
111                L: 'З',
112                D: 'Р',
113                top_scores: 'Голмайстори',
114                most_decorated_player: 'Най-груби играчи',
115                score_table_total_label: 'Р',
116                score_table_overtime_label: 'П',
117                corners: 'Корнери',
118                counter_attacks: 'Контраатаки',
119                crosses: 'Центрирания',
120                fouls_committed: 'Извършени нарушения',
121                goal_kicks: 'Аутове',
122                offside: 'Засади',
123                possession: 'Притежание на топката',
124                shots_blocked: 'Блокирани удари',
125                shots_off: 'Неточни удари',
126                shots_on: 'Удари',
127                substitutions: 'Смени',
128                throw_in: 'Тъчове',
129                treatments: 'Спирания за медицинска помощ',
130                vs: 'Срещу',
131                minutes_substitute: 'Минути като смяна',
132                offsides: 'Засади',
133                own_goals: 'Автоголове',
134                penalties_committed: 'Извършени дузпи',
135                penalties_missed: 'Пропуснати дузпи',
136                penalties_received: 'Спечелени дузпи',
137                penalties_saved: 'Спасени дузпи',
138                penalty_goals: 'Голове от дузпи',
139                shots: 'Удари',
140                shots_on_target: 'Точни удари',
141                substitute_in: 'Влязъл като смяна',
142                substitute_out: 'Заменен',
143                defeats: 'Загуби',
144                draw: 'Равенства',
145                goals_conceded: 'Допуснати голове',
146                goals_scored: 'Вкарани голове',
147                win: 'Победи',
148                rank: 'Позиция в класирането',
149                in_regular_time: 'В редовното време',
150                after_penalties: 'След дузпи',
151                aggregate: 'Общ резултат',
152                first_team: 'Стартови състави',
153                substitutes: 'Резерви',
154                substituted_players: 'Смени',
155                formation: 'Схема',
156                game_starts: 'Начало на мача',
157                no_events_yet: 'Без събития до момента',
158                match_information_tab: 'Информация',
159                match_timeline_tab: 'Детайли',
160                match_statistics_tab: 'Статистика',
161                match_standings_tab: 'Класиране',
162                match_playoff_tab: 'Схема',
163                match_odds_tab: 'Коефициенти',
164                match_h2h_tab: 'Един срещу друг',
165                penalty_goal: 'Д',
166                own_goal: 'АГ',
167                win_short: 'П',
168                draw_short: 'Р',
169                loss_short: 'З',
170                player_bio: 'Визитка',
171                position: 'Позиция',
172                KEEPER: 'Вратар',
173                shirt_number: 'Номер',
174                height: 'Ръст',
175                weight: 'Тегло',
176                player_stats: 'Статистика',
177                weight_metric: 'кг',
178                height_metric: 'см',
179                est: 'осн.',
180                aggregate_status: 'продължава напред',
181                aggregate_label: 'ОБЩ',
182                after_penalty_label: 'ДУЗ',
183                team_stats: 'Статистика',
184                goals_difference_short: 'ГР',
185                goals_difference: 'Голова разлика',
186                programme: 'Програма',
187                coach: 'Треньор',
188                title: 'Класиране',
189                penalties: 'ДУЗ',
190                no_matches: 'Няма мачове',
191                round: 'Кръг',
192                select_team: 'Избор на отбор',
193                select_lineup_group: 'Избор на състав',
194                penalty: '(Дузпа)',
195                match_center_tabs: 'Табове на мач центъра',
196                match_center_loading: 'Зареждане...',
197                var_goal_review: 'ВАР · Проверка за гол',
198                var_card_review: 'ВАР · Проверка за картон',
199                var_penalty_review: 'ВАР · Проверка за дузпа',
200                var_review: 'ВАР · Проверка',
201                var_ended: 'ВАР · Проверката приключи',
202                goal_disallowed: 'Голът е отменен',
203                goal_given: 'Голът е признат',
204                goal_awarded: 'Присъден гол',
205                card_confirmed: 'Картонът е потвърден',
206                card_awarded: 'Присъден картон',
207                card_disallowed: 'Картонът е отменен',
208                penalty_disallowed: 'Дузпата е отменена',
209                penalty_confirmed: 'Дузпата е потвърдена',
210                penalty_not_confirmed: 'Дузпата не е потвърдена',
211                penalty_retake: 'Дузпата се изпълнява повторно',
212                web: 'Официален сайт',
213                facebook: 'Facebook страница',
214                instagram: 'Instagram профил',
215                twitter: 'Twitter профил',
216                youtube: 'YouTube канал',
217                win_overtime_short: 'ПП',
218                loss_overtime_short: 'ЗП',
219                win_after_penalty_short: 'ПсД',
220                loss_after_penalty_short: 'ЗсД',
221                goals_substitute: 'Голове като резерва',
222                caught_ball: 'Хванати топки',
223                saves: 'Спасявания',
224                follow: 'Последвай',
225                followed: 'Последван',
226                followers: 'последователи',
227                stakes_amount_description: 'При залог от {amount}',
228                stakes_bet: 'Заложи сега',
229                stakes_bet_on_team: 'Заложи на победа за своя отбор!',
230                stakes_currency: '€',
231                stakes_payout: 'Печалба',
232                stakes_stake: 'Залог',
233                stakes_limit_exceeded: 'Лимитът е надвишен',
234                stakes_invalid_stake: 'Невалиден залог',
235                list_view: 'Списък',
236                card_view: 'Карти',
237                CORRECT_SCORE: 'Точен резултат',
238                FIRST_HALF_GOALS: 'Голове през първото полувреме',
239                FIRST_PLAYER_TO_SCORE: 'Първи голмайстор',
240                PLAYER_TO_SCORE_DURING_GAME: 'Играч да отбележи гол в мача',
241                PLAYER_TO_RECEIVE_CARD: 'Играч да получи картон',
242                FIRST_HALF_AND_FINAL_RESULT: 'Първо полувреме / Краен резултат'
243            },
244            'data-elements': {
245                stats: {
246                    'player-h2h': [
247                        'minutes',
248                        'goals',
249                        'assists',
250                        'yellow_cards',
251                        'red_cards',
252                        'cleansheets',
253                        'shots',
254                        'shots_on_target',
255                        'played',
256                        'started',
257                        'substitute_in',
258                        'substitute_out',
259                        'minutes_substitute',
260                        'goals_substitute',
261                        'own_goals',
262                        'penalty_goals',
263                        'penalties_missed',
264                        'penalties_committed',
265                        'penalties_received',
266                        'penalties_saved',
267                        'fouls_committed',
268                        'offsides'
269                    ],
270                    'team-h2h-match': [
271                        'goals',
272                        'possession',
273                        'shots_on',
274                        'shots_off',
275                        'shots_blocked',
276                        'corners',
277                        'yellow_cards',
278                        'red_cards',
279                        'offside',
280                        'fouls_committed',
281                        'crosses',
282                        'throw_in',
283                        'goal_kicks',
284                        'counter_attacks',
285                        'substitutions',
286                        'treatments'
287                    ],
288                    'team-h2h': ['win', 'defeats', 'draw', 'goals_conceded', 'goals_scored', 'played', 'points', 'rank'],
289                    'match-center': [
290                        'goals',
291                        'possession',
292                        'shots_on',
293                        'shots_off',
294                        'shots_blocked',
295                        'corners',
296                        'yellow_cards',
297                        'red_cards',
298                        'offside',
299                        'fouls_committed',
300                        'crosses',
301                        'throw_in',
302                        'goal_kicks',
303                        'counter_attacks',
304                        'substitutions',
305                        'treatments'
306                    ],
307                    'top-scorers': [
308                        'goals',
309                        'assists',
310                        'played',
311                        'minutes',
312                        'penalties',
313                        'missed_penalties',
314                        'scored_first',
315                        'red_cards',
316                        'yellow_cards'
317                    ],
318                    'most-decorated': ['total_cards', 'red_cards', 'yellow_cards']
319                }
320            },
321            'data-entity-links': {
322                player: {
323                    url: 'https://sportal.bg/football/player-{playerId}'
324                },
325                team: {
326                    url: 'https://sportal.bg/football/team-{teamId}'
327                },
328                competition: {
329                    url: 'https://sportal.bg/football/league-{competitionId}'
330                },
331                standings: {
332                    url: 'https://sportal.bg/football/league-{competitionId}#standings'
333                },
334                match: {
335                    url: 'https://sportal.bg/football/match-{teamId}-{teamId}#{matchId}',
336                    sort: {
337                        criteria: 'alphabetically',
338                        direction: 'asc'
339                    }
340                },
341                configuration: {
342                    newWindow: false
343                }
344            },
345            'data-betslip-enabled': true
346        },
347        themes: {
348            light: {
349                colors: {
350                    footballFieldTone: 'green',
351                    footballFieldLineColor: 'white',
352                    standingsTopBg: '#2BAE74',
353                    standingsTopPlayoffBg: '#7ED9A6',
354                    standingsTierTwoBg: '#2E7DE9',
355                    standingsTierTwoPlayoffBg: '#7BB4F2',
356                    standingsRelegationPlayoffBg: '#F39C12',
357                    standingsRelegationBg: '#E74C3C',
358                    standingsHoverBg: '#F0F2F5'
359                }
360            },
361            dark: {
362                colors: {
363                    footballFieldTone: 'green',
364                    footballFieldLineColor: 'white',
365                    standingsTopBg: '#2BAE74',
366                    standingsTopPlayoffBg: '#7ED9A6',
367                    standingsTierTwoBg: '#2E7DE9',
368                    standingsTierTwoPlayoffBg: '#7BB4F2',
369                    standingsRelegationPlayoffBg: '#F39C12',
370                    standingsRelegationBg: '#E74C3C',
371                    standingsHoverBg: '#2A3543'
372                }
373            },
374            client: {
375                colors: {
376                    footballFieldTone: 'green',
377                    footballFieldLineColor: 'white',
378                    standingsTopBg: '#2BAE74',
379                    standingsTopPlayoffBg: '#7ED9A6',
380                    standingsTierTwoBg: '#2E7DE9',
381                    standingsTierTwoPlayoffBg: '#7BB4F2',
382                    standingsRelegationPlayoffBg: '#F39C12',
383                    standingsRelegationBg: '#E74C3C',
384                    standingsHoverBg: '#F0F2F5'
385                }
386            }
387        }
388    },
389    basketball: {
390        sdkOptions: {
391            ...BASE_SDK_OPTIONS,
392            dataConfigApiUrl: buildApiUrl('basketball'),
393            dataConfigStandingsApiUrl: buildApiUrl('standing'),
394            dataConfigMultisportApiUrl: buildApiUrl('multi-sport'),
395            dataConfigCompetitionList: 'all'
396        },
397        widgetAttributes: {
398            'data-header-display': true,
399            'data-refresh-time': 'medium',
400            'data-popular-list': 'sportal_popular',
401            'data-odds-display': true,
402            'data-odds-pre-event-only': false,
403            'data-betting-logo-overlap': true,
404            'data-theme': window.widgetTheme?.() || 'light',
405            'data-odds-betting-id': window.selectedBookies?.basketball?.id ?? '',
406            'data-labels': {
407                all: 'Всички',
408                popular: 'Популярни',
409                upcoming: 'Предстоящи',
410                live: 'На Живо',
411                odds: 'С коефициенти',
412                finished: 'Приключили',
413                standings: 'Класиране',
414                score_table_label: 'Резултати',
415                next_match: 'Следващ мач:',
416                no_games: 'Няма срещи',
417                fixtures: 'Предстоящи срещи',
418                results: 'Резултати',
419                round: 'Кръг',
420                points_short: 'Т',
421                score_short: 'Р',
422                percentages_short: 'ПРЦ',
423                team: 'Отбор',
424                matches_won_short: 'П',
425                match_losses_short: 'З',
426                points_difference_short: 'Т',
427                matches_won: 'Победи',
428                match_losses: 'Загуби',
429                points_difference: 'Точки',
430                points: 'Точки',
431                percentages: 'Процент на победите',
432                win_overtime_short: 'П*',
433                loss_overtime_short: '*',
434                team_form_short: 'Форма',
435                score_table_total_label: 'Р',
436                score_table_overtime_label: 'П',
437                '1x2': '1Ð¥2',
438                OVER_UNDER: 'Над/Под',
439                DOUBLE_CHANCE: 'Двоен шанс',
440                BOTH_TO_SCORE: 'Двата отбора да отбележат',
441                DRAW_NO_BET: 'Равен няма залог',
442                FIRST_TEAM_TO_SCORE: 'Първи отбор, който ще отбележи',
443                CORRECT_SCORE: 'Точен резултат',
444                FIRST_HALF_GOALS: 'Голове през първото полувреме',
445                FIRST_PLAYER_TO_SCORE: 'Първи голмайстор',
446                PLAYER_TO_SCORE_DURING_GAME: 'Играч да отбележи гол в мача',
447                PLAYER_TO_RECEIVE_CARD: 'Играч да получи Ð
447ºÐ°Ñ€Ñ‚он',
448                FIRST_HALF_AND_FINAL_RESULT: 'Първо полувреме / Краен резултат',
449                over: 'Над',
450                under: 'Под',
451                yes: 'Да',
452                no: 'Не',
453                first_team: 'Стартови състави',
454                substitutes: 'Резерви',
455                substituted_players: 'Смени',
456                coach: 'Треньор',
457                vs: 'Срещу',
458                select_team: 'Избор на отбор',
459                select_lineup_group: 'Избор на състав',
460                game_starts: 'Начало на мача',
461                no_events_yet: 'Без събития до момента',
462                penalty: '(Наказание)',
463                match_information_tab: 'Информация',
464                match_timeline_tab: 'Детайли',
465                match_standings_tab: 'Класиране',
466                match_playoff_tab: 'Схема',
467                match_center_tabs: 'Табове на мач центъра',
468                match_center_loading: 'Зареждане...',
469                penalties: 'ДУЗ',
470                series: 'Серии',
471                no_matches: 'Няма мачове',
472                aggregate_label: 'ОБЩ',
473                after_penalty_label: 'ДУЗ',
474                no_data: 'Няма налична информация',
475                free_throw_made: 'Вкарано наказателно хвърляне',
476                team_form: 'Форма на отбора',
477                matches_played: 'Мачове',
478                matches_played_short: 'М',
479                programme: 'Програма',
480                current_season: 'Текущ сезон'
481            },
482            'data-entity-links': {
483                competition: {
484                    url: 'https://sportal.bg/basketball/league-{competitionId}'
485                },
486                slug: {
487                    team: 'https://sportal.bg/basketball/team-{slug}',
488                    competition: 'https://sportal.bg/basketball/league-{slug}',
489                    standings: 'https://sportal.bg/basketball/league-{slug}#standings'
490                },
491                configuration: {
492                    newWindow: false
493                }
494            }
495        },
496        onLoaded: () => {}
497    },
498    tennis: {
499        sdkOptions: {
500            ...BASE_SDK_OPTIONS,
501            dataConfigApiUrl: buildApiUrl('tennis'),
502            dataConfigStandingsApiUrl: buildApiUrl('standing'),
503            dataConfigMultisportApiUrl: buildApiUrl('multi-sport'),
504            dataConfigCompetitionList: 'all'
505        },
506        widgetAttributes: {
507            'data-header-display': true,
508            'data-refresh-time': 'super_fast',
509            'data-popular-list': 'sportal_popular',
510            'data-odds-display': true,
511            'data-odds-pre-event-only': false,
512            'data-betting-logo-overlap': true,
513            'data-theme': window.widgetTheme?.() || 'light',
514            'data-odds-betting-id': window.selectedBookies?.tennis?.id ?? '',
515            'data-labels': {
516                all: 'Всички',
517                popular: 'Популярни',
518                upcoming: 'Предстоящи',
519                live: 'На Живо',
520                odds: 'С коефициенти',
521                finished: 'Приключили',
522                standings: 'Класиране',
523                score_table_label: 'Резултати',
524                next_match: 'Следващ мач:',
525                no_games: 'Няма срещи',
526                fixtures: 'Предстоящи срещи',
527                results: 'Резултати',
528                points_short: 'Т',
529                score_short: 'С',
530                nationality_short: 'Държава',
531                name: 'Име',
532                points: 'Точки',
533                nationality: 'Държава',
534                show_more: 'Виж повече',
535                no_matches: 'Няма мачове',
536                single: 'сингъл',
537                double: 'двойки',
538                team: 'отбори',
539                score_table_total_label: 'С',
540                '1x2': '1Ð¥2',
541                OVER_UNDER: 'Над/Под',
542                DOUBLE_CHANCE: 'Двоен шанс',
543                BOTH_TO_SCORE: 'Двата отбора да отбележат',
544                DRAW_NO_BET: 'Равен няма залог',
545                FIRST_TEAM_TO_SCORE: 'Първи отбор, който ще отбележи',
546                CORRECT_SCORE: 'Точен резултат',
547                FIRST_HALF_GOALS: 'Голове през първото полувреме',
548                FIRST_PLAYER_TO_SCORE: 'Първи голмайстор',
549                PLAYER_TO_SCORE_DURING_GAME: 'Играч да отбележи гол в мача',
550                PLAYER_TO_RECEIVE_CARD: 'Играч да получи Ð
550ºÐ°Ñ€Ñ‚он',
551                FIRST_HALF_AND_FINAL_RESULT: 'Първо полувреме / Краен резултат',
552                no: 'Не',
553                round: 'Кръг',
554                no_data: 'Няма данни за ранглистата'
555            }
556        },
557        onLoaded: () => {}
558    },
559    iceHockey: {
560        sdkOptions: {
561            ...BASE_SDK_OPTIONS,
562            dataConfigApiUrl: buildApiUrl('multi-sport'),
563            dataConfigStandingsApiUrl: buildApiUrl('standing'),
564            dataConfigMultisportApiUrl: buildApiUrl('multi-sport'),
565            dataConfigCompetitionList: 'all_comp'
566        },
567        widgetAttributes: {
568            'data-header-display': true,
569            'data-refresh-time': 'medium',
570            'data-popular-list': 'sportal_popular',
571            'data-odds-display': true,
572            'data-odds-pre-event-only': false,
573            'data-betting-logo-overlap': true,
574            'data-theme': window.widgetTheme?.() || 'light',
575            'data-odds-betting-id': window.selectedBookies?.iceHockey?.id ?? '',
576            'data-labels': {
577                all: 'Всички',
578                popular: 'Популярни',
579                upcoming: 'Предстоящи',
580                live: 'На Живо',
581                odds: 'С коефициенти',
582                finished: 'Приключили',
583                standings: 'Класиране',
584                score_table_label: 'Резултати',
585                next_match: 'Следващ мач:',
586                no_games: 'Няма срещи',
587                points_short: 'Т',
588                score: 'Резултат',
589                round: 'Кръг -',
590                after_penalties: 'След дузпи',
591                FIRST_HALF: 'Първо полувреме',
592                SECOND_HALF: 'Второ полувреме',
593                '1x2': '1Ð¥2',
594                OVER_UNDER: 'Брой голове',
595                DOUBLE_CHANCE: 'Двоен шанс',
596                BOTH_TO_SCORE: 'Двата отбора да отбележат',
597                DRAW_NO_BET: 'Равен няма залог',
598                FIRST_TEAM_TO_SCORE: 'Първи гол',
599                GAME_HANDICAP: 'Хендикап',
600                over: 'Над',
601                under: 'Под',
602                yes: 'Да',
603                no: 'Не',
604                percentages_short: 'ПРЦ',
605                team: 'Отбор',
606                matches_won_short: 'П',
607                matches_played_short: 'М',
608                match_losses_short: 'З',
609                points_difference_short: 'Т',
610                matches_played: 'Мачове',
611                matches_won: 'Победи',
612                match_losses: 'Загуби',
613                points_difference: 'Точки',
614                points: 'Точки',
615                percentages: 'Процент на победите',
616                team_form_short: 'Форма',
617                team_form: 'Форма на отбора',
618                goals_difference_short: 'ГР',
619                goals_difference: 'Голова разлика',
620                matches_won_overtime_short: 'ПсП',
621                matches_won_overtime: 'Победи след продължения',
622                no_data: 'Няма налична информация',
623                win_after_penalty_short: 'ПсД',
624                loss_after_penalty_short: 'ЗсД',
625                win_overtime_short: 'ПП',
626                loss_overtime_short: 'ЗП',
627                score_short: 'Р',
628                score_table_total_label: 'Р',
629                score_table_overtime_label: 'П',
630                CORRECT_SCORE: 'Точен резултат',
631                FIRST_HALF_GOALS: 'Голове през първото полувреме',
632                FIRST_PLAYER_TO_SCORE: 'Първи голмайстор',
633                PLAYER_TO_SCORE_DURING_GAME: 'Играч да отбележи гол в мача',
634                PLAYER_TO_RECEIVE_CARD: 'Играч да получи Ð
634ºÐ°Ñ€Ñ‚он',
635                FIRST_HALF_AND_FINAL_RESULT: 'Първо полувреме / Краен резултат',
636                first_team: 'Стартови състави',
637                substitutes: 'Резерви',
638                substituted_players: 'Смени',
639                coach: 'Треньор',
640                vs: 'Срещу',
641                select_team: 'Избор на отбор',
642                select_lineup_group: 'Избор на състав',
643                game_starts: 'Начало на мача',
644                no_events_yet: 'Без събития до момента',
645                penalty: '(Наказание)',
646                match_information_tab: 'Информация',
647                match_timeline_tab: 'Детайли',
648                match_standings_tab: 'Класиране',
649                match_playoff_tab: 'Схема',
650                match_center_tabs: 'Табове на мач центъра',
651                match_center_loading: 'Зареждане...',
652                matches_losses_overtime: 'Загуби след продължения',
653                matches_losses_overtime_short: 'ЗсП',
654                win_after_penalty: 'Победи след дузпи',
655                loss_after_penalty: 'Загуби след дузпи',
656                programme: 'Програма',
657                no_matches: 'Няма мачове',
658                penalties: 'ДУЗ',
659                series: 'Серии',
660                aggregate_label: 'ОБЩ',
661                after_penalty_label: 'ДУЗ',
662                aggregate: 'Общ резултат',
663                penalty_goal: 'Д',
664                own_goal: 'АГ',
665                line: 'Петица',
666                power_play: 'Числено превъзходство',
667                suspension_2_min: 'Наказание (2 мин.)',
668                fixtures: 'Предстоящи срещи',
669                results: 'Резултати',
670                current_season: 'Текущ сезон'
671            }
672        },
673        onLoaded: () => {}
674    },
675    volleyball: {
676        sdkOptions: {
677            ...BASE_SDK_OPTIONS,
678            dataConfigApiUrl: buildApiUrl('multi-sport'),
679            dataConfigStandingsApiUrl: buildApiUrl('standing'),
680            dataConfigMultisportApiUrl: buildApiUrl('multi-sport'),
681            dataConfigCompetitionList: 'all'
682        },
683        widgetAttributes: {
684            'data-header-display': true,
685            'data-refresh-time': 'medium',
686            'data-popular-list': 'sportal_popular_volleyball',
687            'data-odds-display': true,
688            'data-odds-pre-event-only': false,
689            'data-betting-logo-overlap': true,
690            'data-theme': window.widgetTheme?.() || 'light',
691            'data-odds-betting-id': window.selectedBookies?.volleyball?.id ?? '',
692            'data-labels': {
693                all: 'Всички',
694                popular: 'Популярни',
695                upcoming: 'Предстоящи',
696                live: 'На Живо',
697                odds: 'С коефициенти',
698                finished: 'Приключили',
699                standings: 'Класиране',
700                score_table_label: 'Резултати',
701                next_match: 'Следващ мач:',
702                no_games: 'Няма срещи',
703                score_short: 'Р',
704                team: 'Отбор',
705                coach: 'Треньор',
706                vs: 'Срещу',
707                no_matches: 'Няма мачове',
708                no_data: 'Няма данни за класирането',
709                first_team: 'Стартови състави',
710                substitutes: 'Резерви',
711                substituted_players: 'Смени',
712                select_team: 'Избор на отбор',
713                select_lineup_group: 'Избор на състав',
714                game_starts: 'Начало',
715                no_events_yet: 'Без събития до момента',
716                penalty: '(Наказание)',
717                match_information_tab: 'Информация',
718                match_timeline_tab: 'Детайли',
719                match_standings_tab: 'Класиране',
720                match_playoff_tab: 'Схема',
721                match_center_tabs: 'Табове на мач центъра',
722                match_center_loading: 'Зареждане...',
723                matches_played: 'Изиграни мачове',
724                matches_played_short: 'М',
725                matches_won: 'Победи',
726                matches_won_short: 'П',
727                match_losses: 'Загуби',
728                match_losses_short: 'З',
729                points: 'Точки',
730                points_short: 'Т',
731                team_form: 'Форма на отбора',
732                team_form_short: 'Ф',
733                series: 'Серии',
734                penalties: 'Наказания',
735                score_table_total_label: 'Р',
736                aggregate_label: 'ОБЩО',
737                aggregate_status: 'продължава',
738                '1x2': '1Ð¥2',
739                OVER_UNDER: 'Над/Под',
740                over: 'Над',
741                under: 'Под',
742                yes: 'Да',
743                no: 'Не',
744                fixtures: 'Предстоящи срещи',
745                results: 'Резултати',
746                round: 'Кръг',
747                current_season: 'Текущ сезон',
748                programme: 'Програма',
749                DOUBLE_CHANCE: 'Двоен шанс',
750                BOTH_TO_SCORE: 'Двата отбора да отбележат',
751                DRAW_NO_BET: 'Равен няма залог',
752                FIRST_TEAM_TO_SCORE: 'Първи отбор, който ще отбележи',
753                after_penalty_label: 'ДУЗ',
754                penalty_goal: 'Д',
755                own_goal: 'АГ'
756            }
757        },
758        onLoaded: () => {}
759    },
760    multisport: {
761        sdkOptions: {
762            ...BASE_SDK_OPTIONS,
763            dataConfigApiUrl: buildApiUrl('multi-sport'),
764            dataConfigMultisportApiUrl: buildApiUrl('multi-sport'),
765            dataConfigCompetitionList: 'sportal_multi-sport'
766        },
767        widgetAttributes: {
768            'data-theme': window.widgetTheme?.() || 'light',
769            'data-labels': {
770                winner: 'Победител',
771                football: 'Футбол',
772                legal_text: 'Age Restriction',
773                legal_value: '18+',
774                legal_link: '#',
775                today: 'Днес',
776                yesterday: 'Вчера',
777                tomorrow: 'Утре',
778                live: 'На живо',
779                no_events: 'Няма срещи',
780                no_live_events: 'Няма срещи',
781                scroll_left: 'Назад',
782                scroll_right: 'Напред'
783            },
784            'data-entity-links': {
785                football: {
786                    player: {
787                        url: 'https://sportal.bg/football/player-{playerId}'
788                    },
789                    team: {
790                        url: 'https://sportal.bg/football/team-{teamId}'
791                    },
792                    competition: {
793                        url: 'https://sportal.bg/football/league-{competitionId}'
794                    },
795                    standings: {
796                        url: 'https://sportal.bg/football/league-{competitionId}#standings'
797                    },
798                    match: {
799                        url: 'https://sportal.bg/football/match-{teamId}-{teamId}#{matchId}',
800                        sort: {
801                            criteria: 'alphabetically',
802                            direction: 'asc'
803                        }
804                    },
805                    configuration: {
806                        newWindow: false
807                    }
808                }
809            }
810        },
811        getAuthToken: () => {},
812        onLoaded: () => {}
813    }
814};
815
816const BETSLIP_CONFIG = {
817    sdkOptions: { ...BASE_SDK_OPTIONS },
818    widgetAttributes: {
819        'data-betslip-config': {
820            enabled: true,
821            position: 'floating-inline-right',
822            floatingOnMobile: true,
823            ageRestrictionDisplay: 'none',
824            maxSelections: 10,
825            betType: 'multiples',
826            quickStakes: '5,10,20'
827        },
828        'data-labels': {
829            betslip_continue_to: ' ',
830            betslip_combined_odds: 'Общ коефициент:',
831            betslip_total_stake: 'Общо',
832            betslip_potential_win: 'Възмножно',
833            betslip_stake: 'Сума',
834            betslip_opposing_outcomes: 'Взаимноизключващи се изходи не са разрешени.',
835            betslip_max_selections: 'Достигнат е максималният брой селекции.',
836            betslip_settled_info: 'Някои от вашите селекции вече са приключили. Те са изключени от изчисленията',
837            betslip_all_settled: 'Всички селекции са приключили. Добавете нови селекции, за да продължите.',
838            betslip_provider_switch_title: 'Само един букмейкър едновременно.',
839            betslip_provider_switch_description: 'Смяната на букмейкъра ще изтрие текущите ви селекции.',
840            betslip_provider_switch_confirm: 'Смени букмейкъра',
841            betslip_provider_switch_cancel: 'Отказ',
842            betslip_quick_stake_1: '5',
843            betslip_quick_stake_2: '10',
844            betslip_quick_stake_3: '20',
845            betslip_finished: 'Приключен',
846            betslip_terms: 'Общи условия',
847            betslip_responsible_gambling: 'Играй отговорно',
848            betslip_age_restriction: '18+',
849            '1x2': '1X2',
850            'FIRST HALF': 'Първо полувреме',
851            'SECOND HALF': 'Второ полувреме',
852            OVER_UNDER: 'Над/Под',
853            DOUBLE_CHANCE: 'Двоен шанс',
854            BOTH_TO_SCORE: 'Двата отбора да отбележат',
855            DRAW_NO_BET: 'Залог без равен',
856            FIRST_TEAM_TO_SCORE: 'Първи отбор, който ще отбележи',
857            CORRECT_SCORE: 'Точен резултат',
858            FIRST_HALF_GOALS: 'Голове през първото полувреме',
859            FIRST_PLAYER_TO_SCORE: 'Първи голмайстор',
860            PLAYER_TO_SCORE_DURING_GAME: 'Играч да отбележи гол в мача',
861            PLAYER_TO_RECEIVE_CARD: 'Играч да получи картон',
862            FIRST_HALF_AND_FINAL_RESULT: 'Първо полувреме / Краен резултат',
863            over: 'Над',
864            under: 'Под',
865            yes: 'Да',
866            no: 'Не'
867        }
868    },
869    onLoaded: () => {}
870};
871
872// ============================================================================
873// БРОЕНЕ НА ПОКАЗАНИ КОЕФИЦИЕНТИ
874// ============================================================================
875
876const oddsFlushers = [];
877
878const buildOddsViews = (countsList, sport) => {
879    const page = window.currentPage || '';
880    const views = {};
881
882    countsList.forEach((counts) => {
883        Object.keys(counts).forEach((bookieId) => {
884            if (bookieId === 'unknown') {
885                return;
886            }
887
888            const view = views[bookieId] || (views[bookieId] = { sport, page, singleCount: 0, allCount: 0 });
889            view.singleCount += 1;
890            view.allCount += counts[bookieId];
891        });
892    });
893
894    return views;
895};
896
897const trackOddsViews = (sport) => {
898    const odds = window.smpWidgetsOdds?.[sport];
899
900    if (typeof odds !== 'object' || odds === null) {
901        return;
902    }
903
904    let pending = [];
905    let timer = null;
906
907    const flush = () => {
908        clearTimeout(timer);
909        timer = null;
910
911        if (pending.length === 0) {
912            return;
913        }
914
915        const views = buildOddsViews(pending, sport);
916        pending = [];
917
918        if (window.saveViewsFromWidgetsV2 && Object.keys(views).length > 0) {
919            window.saveViewsFromWidgetsV2(views);
920        }
921    };
922
923    const widgetHandler = {
924        set: (widgetOdds, widgetCuid, counts) => {
925            widgetOdds[widgetCuid] = counts;
926
927            if (typeof counts === 'object' && counts !== null) {
928                pending.push(counts);
929                clearTimeout(timer);
930                timer = setTimeout(flush, DEBOUNCE_DELAY);
931            }
932
933            return true;
934        }
935    };
936
937    // Един Proxy на widget, за да не се създава нов при всяко четене
938    const widgetProxies = new WeakMap();
939
940    window.smpWidgetsOdds[sport] = new Proxy(odds, {
941        get: (target, widgetId, receiver) => {
942            const widgetOdds = Reflect.get(target, widgetId, receiver);
943
944            if (typeof widgetOdds !== 'object' || widgetOdds === null) {
945                return widgetOdds;
946            }
947
948            let proxy = widgetProxies.get(widgetOdds);
949            if (!proxy) {
950                proxy = new Proxy(widgetOdds, widgetHandler);
951                widgetProxies.set(widgetOdds, proxy);
952            }
953
954            return proxy;
955        }
956    });
957
958    oddsFlushers.push(flush);
959};
960
961document.addEventListener('visibilitychange', () => {
962    if (document.hidden) {
963        oddsFlushers.forEach(flush => flush());
964    }
965});
966
967// ============================================================================
968// ИНИЦИАЛИЗАЦИЯ НА СПОРТНИ WIDGETS
969// ============================================================================
970
971const SPORT_WIDGET_GLOBALS = {
972    football: 'smpFootballWidgets',
973    basketball: 'smpBasketballWidgets',
974    tennis: 'smpTennisWidgets',
975    iceHockey: 'smpIceHockeyWidgets',
976    volleyball: 'smpVolleyballWidgets',
977    multisport: 'smpMultisportWidgets'
978};
979
980const LOAD_SPORT_WIDGETS = {
981    football: () => window.smpFootballWidgets.LoadSmpWidget(smpConfig.football),
982    basketball: () => window.smpBasketballWidgets.LoadSmpWidget(smpConfig.basketball),
983    tennis: () => window.smpTennisWidgets.LoadSmpWidget(smpConfig.tennis),
984    iceHockey: () => window.smpIceHockeyWidgets.LoadSmpWidget(smpConfig.iceHockey),
985    volleyball: () => window.smpVolleyballWidgets.LoadSmpWidget(smpConfig.volleyball),
986    multisport: () => window.smpMultisportWidgets.LoadSmpWidget(smpConfig.multisport)
987};
988
989const readySports = new Set();
990
991const saveWidgetClick = (data) => {
992    window.saveClicksFromWidgets?.(data);
993};
994
995const sendWidgetPageView = (event) => {
996    if (window.sendPageView && event) {
997        const page = window.currentPage || '';
998        window.sendPageView('Data reload', `${page}/${event.dataWidgetSport}-${event.dataWidgetId}`);
999    }
1000};
1001
1002const initializeSportWidget = (sport, sdk) => {
1003    trackOddsViews(sport);
1004
1005    sdk.subscribe('bettingMarketValue', saveWidgetClick);
1006    sdk.subscribe('bettingLogo', saveWidgetClick);
1007    sdk.subscribe('onUserInteraction', sendWidgetPageView);
1008
1009    // Темата може да е сменена, докато SDK-то се е зареждало (lazy SDK-тата идват по-късно)
1010    smpConfig[sport].widgetAttributes['data-theme'] = window.widgetTheme?.() || 'light';
1011
1012    readySports.add(sport);
1013    LOAD_SPORT_WIDGETS[sport]();
1014};
1015
1016const onSdkRegistered = (globalName, init) => {
1017    let done = false;
1018
1019    const run = () => {
1020        const sdk = window[globalName];
1021
1022        if (done || !sdk) {
1023            return;
1024        }
1025
1026        done = true;
1027
1028        try {
1029            init(sdk);
1030        } catch (error) {
1031            console.error(`[SMP] Error initializing ${globalName}:`, error);
1032        }
1033    };
1034
1035    window.addEventListener(`${globalName}Registered`, run, { once: true });
1036    run();
1037};
1038
1039Object.entries(SPORT_WIDGET_GLOBALS).forEach(([sport, globalName]) => {
1040    onSdkRegistered(globalName, sdk => initializeSportWidget(sport, sdk));
1041});
1042
1043onSdkRegistered('smpBetslipWidgets', () => window.smpBetslipWidgets.LoadSmpWidget(BETSLIP_CONFIG));
1044
1045// ============================================================================
1046// LAZY ЗАРЕЖДАНЕ НА SDK-ТА
1047// ============================================================================
1048
1049const LAZY_SDK_WIDGET_PREFIXES = {
1050    basketball: 'basketball-',
1051    tennis: 'tennis-',
1052    iceHockey: 'ice-hockey-',
1053    volleyball: 'volleyball-',
1054    multisport: 'multisport-'
1055};
1056
1057const SDK_BASE_URL = 'https://widgets.sportal365.com';
1058const SDK_VERSION = window.widgetConfig?.widgetVersion;
1059const requestedSdks = new Set();
1060
1061const loadNeededSdks = () => {
1062    if (!SDK_VERSION) {
1063        console.error('[SMP] window.widgetConfig.widgetVersion is missing - lazy SDKs are not loaded');
1064        return;
1065    }
1066
1067    const widgetIds = Array.from(
1068        document.querySelectorAll('[data-widget-id]'),
1069        element => element.getAttribute('data-widget-id') || ''
1070    );
1071
1072    Object.entries(LAZY_SDK_WIDGET_PREFIXES).forEach(([sport, prefix]) => {
1073        if (requestedSdks.has(sport) || !widgetIds.some(id => id.startsWith(prefix))) {
1074            return;
1075        }
1076
1077        requestedSdks.add(sport);
1078
1079        import(`${SDK_BASE_URL}/${prefix.slice(0, -1)}.${SDK_VERSION}.js`).catch((error) => {
1080            requestedSdks.delete(sport); // нов опит при следващата AJAX навигация
1081            console.error(`[SMP] Error loading ${sport} SDK:`, error);
1082        });
1083    });
1084};
1085
1086loadNeededSdks();
1087
1088// ============================================================================
1089// ODDS OVERLAP МЕХАНИЗЪМ
1090// ============================================================================
1091
1092window.smpCustomBettingOverlapValue = false;
1093
1094setInterval(() => {
1095    // Всеки коефициент се пре-рендерира при смяната - излишно в скрит таб
1096    if (document.hidden) {
1097        return;
1098    }
1099
1100    window.smpCustomBettingOverlapValue = !window.smpCustomBettingOverlapValue;
1101    window.dispatchEvent(new Event('smpCustomBettingOverlapValueChange'));
1102}, ODDS_OVERLAP_INTERVAL);
1103
1104// ============================================================================
1105// AJAX КОНТЕЙНЕРИ НАБЛЮДЕНИЕ
1106// ============================================================================
1107
1108const reloadAllWidgets = () => {
1109    readySports.forEach((sport) => {
1110        try {
1111            LOAD_SPORT_WIDGETS[sport]();
1112        } catch (error) {
1113            console.error(`[SMP] Error reloading ${sport} widgets:`, error);
1114        }
1115    });
1116};
1117
1118const setupMutationObserver = () => {
1119    const containers = ['pageContentJS', 'matchWidget']
1120        .map(id => document.getElementById(id))
1121        .filter(Boolean);
1122
1123    if (containers.length === 0) {
1124        return;
1125    }
1126
1127    // AJAX навигацията сменя innerHTML и после слага class="pseudo-class-loader".
1128    // Няколко смени в един кадър -> едно презареждане.
1129    let reloadScheduled = false;
1130    const observer = new MutationObserver(() => {
1131        if (reloadScheduled) {
1132            return;
1133        }
1134
1135        reloadScheduled = true;
1136        requestAnimationFrame(() => {
1137            reloadScheduled = false;
1138            loadNeededSdks();
1139            reloadAllWidgets();
1140        });
1141    });
1142
1143    containers.forEach(container => observer.observe(container, { attributes: true, attributeFilter: ['class'] }));
1144};
1145
1146setupMutationObserver();
1147
1148window.sprtlSMPConfig = smpConfig;

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.