@maestro_io/maestro-web-sdk 3.3.1 → 4.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (269) hide show
  1. package/dist/f8820fca3a2a7957f753167da0884f9d.png +0 -0
  2. package/dist/maestro-web-sdk.esm.js +12 -12
  3. package/dist/maestro-web-sdk.esm.js.map +1 -1
  4. package/dist/maestro-web-sdk.umd.js +12 -12
  5. package/dist/maestro-web-sdk.umd.js.map +1 -1
  6. package/dist/package.json +87 -0
  7. package/dist/src/__mocks__/sentry.d.ts +4 -0
  8. package/dist/src/components/atoms/BaseButton/BaseButton.js +11 -0
  9. package/dist/src/components/atoms/BaseButton/index.js +2 -0
  10. package/dist/src/components/atoms/Image/index.js +34 -0
  11. package/dist/src/components/atoms/Rive/index.js +53 -0
  12. package/dist/src/components/atoms/SvgIcon/BetGeneralIcon.js +9 -0
  13. package/dist/src/components/atoms/SvgIcon/BetsIcon.js +11 -0
  14. package/dist/src/components/atoms/SvgIcon/BetsWarningIcon.js +11 -0
  15. package/dist/src/components/atoms/SvgIcon/CheckmarkIcon.js +6 -0
  16. package/dist/src/components/atoms/SvgIcon/CollapseIcon.js +6 -0
  17. package/dist/src/components/atoms/SvgIcon/ExpandIcon.js +6 -0
  18. package/dist/src/components/atoms/SvgIcon/FantasyIcon.js +15 -0
  19. package/dist/src/components/atoms/SvgIcon/FlameIcon.js +11 -0
  20. package/dist/src/components/atoms/SvgIcon/Icon.js +44 -0
  21. package/dist/src/components/atoms/SvgIcon/KeyPlaysIcon.js +10 -0
  22. package/dist/src/components/atoms/SvgIcon/MobilePhoneIcon.js +11 -0
  23. package/dist/src/components/atoms/SvgIcon/PlayIcon.js +8 -0
  24. package/dist/src/components/atoms/SvgIcon/ShopIcon.js +7 -0
  25. package/dist/src/components/atoms/SvgIcon/StatsIcon.js +12 -0
  26. package/dist/src/components/atoms/SvgIcon/TimesIcon.js +6 -0
  27. package/dist/src/components/atoms/SvgIcon/WarningIcon.js +8 -0
  28. package/dist/src/components/atoms/SvgIcon/index.js +2 -0
  29. package/dist/src/components/core/App/App.js +35 -0
  30. package/dist/src/components/core/App/index.js +2 -0
  31. package/dist/src/components/core/OverlayContainer.js +58 -0
  32. package/dist/src/components/core/PanelManager/PanelManager.js +86 -0
  33. package/dist/src/components/core/PanelManager/index.js +2 -0
  34. package/dist/src/components/core/ScrollableContainer/FocusableItem.js +38 -0
  35. package/dist/src/components/core/ScrollableContainer/ScrollableContainer.js +168 -0
  36. package/dist/src/components/core/ScrollableContainer/index.js +2 -0
  37. package/dist/src/components/molecules/ActionButton/ActionButton.js +10 -0
  38. package/dist/src/components/molecules/ActionButton/index.js +2 -0
  39. package/dist/src/components/molecules/HeadshotImage/HeadshotImage.js +50 -0
  40. package/dist/src/components/molecules/HeadshotImage/index.js +2 -0
  41. package/dist/src/components/molecules/Overlay/Overlay.js +139 -0
  42. package/dist/src/components/molecules/Overlay/index.js +2 -0
  43. package/dist/src/components/molecules/PanelNavButton/PanelNavButton.js +27 -0
  44. package/dist/src/components/molecules/PanelNavButton/index.js +2 -0
  45. package/dist/src/components/molecules/SegmentButton/SegmentButton.js +10 -0
  46. package/dist/src/components/molecules/SegmentButton/index.js +2 -0
  47. package/dist/src/components/molecules/TabLoadError/TabLoadError.js +14 -0
  48. package/dist/src/components/molecules/TabLoadError/index.js +2 -0
  49. package/dist/src/components/molecules/WatchButton/WatchButton.js +15 -0
  50. package/dist/src/components/molecules/WatchButton/index.js +2 -0
  51. package/dist/src/components/organisms/PanelNavigation/PanelNavigation.js +60 -0
  52. package/dist/src/components/organisms/PanelNavigation/index.js +2 -0
  53. package/dist/src/components/organisms/SegmentController/SegmentController.js +76 -0
  54. package/dist/src/components/organisms/SegmentController/index.js +2 -0
  55. package/dist/src/development/MaestroEventDelegate.js +109 -0
  56. package/dist/src/development/TimeSeek.js +218 -0
  57. package/dist/src/development/development.js +33 -0
  58. package/dist/src/external/AxiosNetworkClient.js +110 -0
  59. package/dist/src/external/LocalStorageCacheManager.js +213 -0
  60. package/dist/src/external/ReactRenderer.js +53 -0
  61. package/dist/src/external/mocks/MockExternalNetworkClient.js +187 -0
  62. package/dist/src/external/ports/cacheManager.js +1 -0
  63. package/dist/src/external/ports/networkClient.js +1 -0
  64. package/dist/src/external/ports/renderer.d.ts +1 -2
  65. package/dist/src/external/ports/renderer.js +1 -0
  66. package/dist/src/external/spatial-navigation/__mocks__/withFocusable.d.ts +8 -0
  67. package/dist/src/external/spatial-navigation/__mocks__/withFocusable.js +7 -0
  68. package/dist/src/external/spatial-navigation/index.js +3 -0
  69. package/dist/src/external/spatial-navigation/measureLayout.js +30 -0
  70. package/dist/src/external/spatial-navigation/spatialNavigation.js +876 -0
  71. package/dist/src/external/spatial-navigation/utils.js +6 -0
  72. package/dist/src/external/spatial-navigation/visualDebugger.js +76 -0
  73. package/dist/src/external/spatial-navigation/withFocusable.js +245 -0
  74. package/dist/src/helpers/Observable.js +37 -0
  75. package/dist/src/helpers/strings.js +14 -0
  76. package/dist/src/helpers/url.js +12 -0
  77. package/dist/src/index.d.ts +5 -5
  78. package/dist/src/index.js +165 -0
  79. package/dist/src/interfaces/IMaestroEvent.d.ts +12 -0
  80. package/dist/src/interfaces/IMaestroEvent.js +18 -0
  81. package/dist/src/interfaces/IMaestroEventDelegate.js +1 -0
  82. package/dist/src/interfaces/IMaestroManager.js +1 -0
  83. package/dist/src/interfaces/IUserSettings.js +20 -0
  84. package/dist/src/models/IModel.js +1 -0
  85. package/dist/src/models/IPanel.js +40 -0
  86. package/dist/src/models/ITheme.js +20 -0
  87. package/dist/src/models/IWinningBet.js +1 -0
  88. package/dist/src/modules/bets/interfaces/IBets.js +1 -0
  89. package/dist/src/modules/bets/mocks.js +573 -0
  90. package/dist/src/modules/bets/types/BetsAnalyticsTypes.js +1 -0
  91. package/dist/src/modules/bets/view/BetsView.js +143 -0
  92. package/dist/src/modules/bets/view/HotProps/HotPropsView.js +150 -0
  93. package/dist/src/modules/bets/view/SixPack/SixPackView.js +110 -0
  94. package/dist/src/modules/bets/view/UserBets/BetResult/BetResult.js +6 -0
  95. package/dist/src/modules/bets/view/UserBets/BetsCard/BetsCard.js +149 -0
  96. package/dist/src/modules/bets/view/UserBets/BetsCard/BetsCardEvent.js +34 -0
  97. package/dist/src/modules/bets/view/UserBets/BetsCard/BetsCardFooter.js +18 -0
  98. package/dist/src/modules/bets/view/UserBets/Boost/Boost.js +6 -0
  99. package/dist/src/modules/bets/view/UserBets/OutcomeBadge/OutcomeBadge.js +9 -0
  100. package/dist/src/modules/bets/view/UserBets/Parlay/Parlay.js +13 -0
  101. package/dist/src/modules/bets/view/UserBets/Straight/Straight.js +6 -0
  102. package/dist/src/modules/bets/view/UserBets/Teaser/Teaser.js +13 -0
  103. package/dist/src/modules/bets/view/UserBets/UserBetsView.js +107 -0
  104. package/dist/src/modules/bets/view/components/Bet/Bet.js +32 -0
  105. package/dist/src/modules/bets/view/components/BetPill/BetPill.js +6 -0
  106. package/dist/src/modules/bets/view/components/BetsCard/BetsCard.js +15 -0
  107. package/dist/src/modules/bets/view/components/BetsCard/BetsCardHeader.js +20 -0
  108. package/dist/src/modules/bets/view/components/GamblingDisclaimer/GamblingDisclaimer.js +14 -0
  109. package/dist/src/modules/bets/view/components/LocationWarning/LocationWarning.js +16 -0
  110. package/dist/src/modules/bets/view/components/PromoCodeBanner/PromoCodeBanner.js +78 -0
  111. package/dist/src/modules/bets/view/components/PromoCodeBanner/index.js +1 -0
  112. package/dist/src/modules/bets/view-model/BetsViewModel.js +974 -0
  113. package/dist/src/modules/fantasy/components/ExpandedAthleteList/ExpandedAthletesList.js +107 -0
  114. package/dist/src/modules/fantasy/components/ExpandedAthleteList/index.js +1 -0
  115. package/dist/src/modules/fantasy/components/LeagueCard/LeagueCard.js +20 -0
  116. package/dist/src/modules/fantasy/components/LeagueCard/index.js +2 -0
  117. package/dist/src/modules/fantasy/components/TopPlayerCard/TopPlayerCard.js +29 -0
  118. package/dist/src/modules/fantasy/components/TopPlayerCard/index.js +2 -0
  119. package/dist/src/modules/fantasy/mocks.js +3682 -0
  120. package/dist/src/modules/fantasy/types.js +6 -0
  121. package/dist/src/modules/fantasy/view/ActiveAthletesMarquee/ActiveAthletesMarquee.js +74 -0
  122. package/dist/src/modules/fantasy/view/ActiveAthletesMarquee/index.js +2 -0
  123. package/dist/src/modules/fantasy/view/ErrorBoundary.d.ts +13 -0
  124. package/dist/src/modules/fantasy/view/FantasyItemView.js +74 -0
  125. package/dist/src/modules/fantasy/view/FantasyView.js +133 -0
  126. package/dist/src/modules/fantasy/view/InactiveLeague/DraftDateCountdown.js +95 -0
  127. package/dist/src/modules/fantasy/view/InactiveLeague/InactiveLeague.js +13 -0
  128. package/dist/src/modules/fantasy/view/InactiveLeague/index.js +2 -0
  129. package/dist/src/modules/fantasy/view/LoginView/LoginView.js +27 -0
  130. package/dist/src/modules/fantasy/view/LoginView/index.js +2 -0
  131. package/dist/src/modules/fantasy/view/MatchupPair/DoubleAthleteCard/DoubleAthleteCard.js +25 -0
  132. package/dist/src/modules/fantasy/view/MatchupPair/DoubleAthleteCard/index.js +2 -0
  133. package/dist/src/modules/fantasy/view/MatchupPair/ExpandedDoubleAthleteCards.js +9 -0
  134. package/dist/src/modules/fantasy/view/MatchupPair/MatchupPair.js +118 -0
  135. package/dist/src/modules/fantasy/view/MatchupPair/index.js +2 -0
  136. package/dist/src/modules/fantasy/view/MatchupSingle/ExpandedSingleAthleteCards.js +9 -0
  137. package/dist/src/modules/fantasy/view/MatchupSingle/MatchupSingle.js +108 -0
  138. package/dist/src/modules/fantasy/view/MatchupSingle/SingleAthleteCard/SingleAthleteCard.js +18 -0
  139. package/dist/src/modules/fantasy/view/MatchupSingle/SingleAthleteCard/index.js +2 -0
  140. package/dist/src/modules/fantasy/view/MatchupSingle/index.js +2 -0
  141. package/dist/src/modules/fantasy/view/PromoView/PromoView.js +12 -0
  142. package/dist/src/modules/fantasy/view/PromoView/index.js +2 -0
  143. package/dist/src/modules/fantasy/view/index.js +2 -0
  144. package/dist/src/modules/fantasy/view-model/FantasyViewModel.FantasyPolling.js +132 -0
  145. package/dist/src/modules/fantasy/view-model/FantasyViewModel.LeagueManagerPolling.js +141 -0
  146. package/dist/src/modules/fantasy/view-model/FantasyViewModel.js +534 -0
  147. package/dist/src/modules/key-plays/constants.js +292 -0
  148. package/dist/src/modules/key-plays/interfaces/IKeyPlays.js +14 -0
  149. package/dist/src/modules/key-plays/interfaces/IKeyPlaysResponse.js +1 -0
  150. package/dist/src/modules/key-plays/view/KeyPlayCardScoreTagView.js +16 -0
  151. package/dist/src/modules/key-plays/view/KeyPlayCardView.js +144 -0
  152. package/dist/src/modules/key-plays/view/KeyPlayErrorStateView.js +65 -0
  153. package/dist/src/modules/key-plays/view/KeyPlaysView.js +151 -0
  154. package/dist/src/modules/key-plays/view-model/KeyPlaysViewModel.js +246 -0
  155. package/dist/src/modules/shop/mocks.js +390 -0
  156. package/dist/src/modules/shop/types.js +7 -0
  157. package/dist/src/modules/shop/view/ProductDetailView/ProductDetailView.js +56 -0
  158. package/dist/src/modules/shop/view/ProductDetailView/index.js +2 -0
  159. package/dist/src/modules/shop/view/ShopView.js +182 -0
  160. package/dist/src/modules/shop/view/components/BrowseCard/BrowseCard.js +23 -0
  161. package/dist/src/modules/shop/view/components/BrowseCard/index.js +2 -0
  162. package/dist/src/modules/shop/view/components/ProductSummaryCard/ProductSummaryCard.js +26 -0
  163. package/dist/src/modules/shop/view/components/ProductSummaryCard/index.js +1 -0
  164. package/dist/src/modules/shop/view/components/ShopPromoView/ShopPromoView.js +19 -0
  165. package/dist/src/modules/shop/view/components/ShopPromoView/index.js +1 -0
  166. package/dist/src/modules/shop/view/components/ShopSection/ShopSection.d.ts +1 -0
  167. package/dist/src/modules/shop/view/components/ShopSection/ShopSection.js +61 -0
  168. package/dist/src/modules/shop/view/components/ShopSection/ShopSectionItem.d.ts +18 -0
  169. package/dist/src/modules/shop/view/components/ShopSection/ShopSectionItem.js +22 -0
  170. package/dist/src/modules/shop/view/components/ShopSection/index.js +1 -0
  171. package/dist/src/modules/shop/view/index.js +2 -0
  172. package/dist/src/modules/shop/view-model/ShopViewModel.ShopPolling.js +111 -0
  173. package/dist/src/modules/shop/view-model/ShopViewModel.js +353 -0
  174. package/dist/src/modules/stats/interfaces/IStats.js +1 -0
  175. package/dist/src/modules/stats/mocks.js +746 -0
  176. package/dist/src/modules/stats/utils/driveSummaryUtils.js +48 -0
  177. package/dist/src/modules/stats/utils/renderTeamStatistic.js +16 -0
  178. package/dist/src/modules/stats/utils.js +62 -0
  179. package/dist/src/modules/stats/view/ActiveAthletes/ActiveAthletes.js +98 -0
  180. package/dist/src/modules/stats/view/ActiveAthletes/CollapsedAthletes/CollapsedAthletes.js +27 -0
  181. package/dist/src/modules/stats/view/ActiveAthletes/ExpandedAthletes/ExpandedAthletes.js +89 -0
  182. package/dist/src/modules/stats/view/ActiveAthletes/index.js +2 -0
  183. package/dist/src/modules/stats/view/DriveSummary/DrivePlayCard.js +70 -0
  184. package/dist/src/modules/stats/view/DriveSummary/DriveSummary.js +87 -0
  185. package/dist/src/modules/stats/view/DriveSummary/ExpandedItems/DriveSummaryExpanded.js +129 -0
  186. package/dist/src/modules/stats/view/DriveSummary/Header/DriveSummaryHeader.js +26 -0
  187. package/dist/src/modules/stats/view/DriveSummary/MainStat/MainStat.js +24 -0
  188. package/dist/src/modules/stats/view/DriveSummary/index.js +2 -0
  189. package/dist/src/modules/stats/view/GameLeaders/GameLeaders.js +48 -0
  190. package/dist/src/modules/stats/view/GameLeaders/index.js +2 -0
  191. package/dist/src/modules/stats/view/MatchPredictor/DonutChart/DonutChart.js +55 -0
  192. package/dist/src/modules/stats/view/MatchPredictor/DonutChart/index.js +1 -0
  193. package/dist/src/modules/stats/view/MatchPredictor/MatchPredictorView.js +60 -0
  194. package/dist/src/modules/stats/view/MatchPredictor/index.js +2 -0
  195. package/dist/src/modules/stats/view/Penalties/Penalties.js +6 -0
  196. package/dist/src/modules/stats/view/Penalties/index.js +2 -0
  197. package/dist/src/modules/stats/view/RecentGames/RecentGames.js +23 -0
  198. package/dist/src/modules/stats/view/RecentGames/index.js +2 -0
  199. package/dist/src/modules/stats/view/ScoringSummary/ExpandedScores/ExpandedScores.js +121 -0
  200. package/dist/src/modules/stats/view/ScoringSummary/ExpandedScores/index.js +2 -0
  201. package/dist/src/modules/stats/view/ScoringSummary/ScoreItem/ScoreItem.js +62 -0
  202. package/dist/src/modules/stats/view/ScoringSummary/ScoreItem/index.js +2 -0
  203. package/dist/src/modules/stats/view/ScoringSummary/ScoresSection/ScoresSection.js +23 -0
  204. package/dist/src/modules/stats/view/ScoringSummary/ScoresSection/index.js +2 -0
  205. package/dist/src/modules/stats/view/ScoringSummary/ScoringSummary.js +106 -0
  206. package/dist/src/modules/stats/view/ScoringSummary/index.js +2 -0
  207. package/dist/src/modules/stats/view/StatsItemView.js +92 -0
  208. package/dist/src/modules/stats/view/StatsQRCode/StatsQRCodeView.js +36 -0
  209. package/dist/src/modules/stats/view/StatsQRCode/index.js +1 -0
  210. package/dist/src/modules/stats/view/StatsView.js +137 -0
  211. package/dist/src/modules/stats/view/TeamStats/TeamStatsView.js +43 -0
  212. package/dist/src/modules/stats/view/TeamStats/index.js +1 -0
  213. package/dist/src/modules/stats/view/Teams/StatsCarousel.js +83 -0
  214. package/dist/src/modules/stats/view/Teams/StatsProgressBar.js +19 -0
  215. package/dist/src/modules/stats/view/Teams/Teams.js +27 -0
  216. package/dist/src/modules/stats/view/Teams/index.js +2 -0
  217. package/dist/src/modules/stats/view/WinProbability/WinProbability.js +28 -0
  218. package/dist/src/modules/stats/view/WinProbability/index.js +2 -0
  219. package/dist/src/modules/stats/view/components/AthletesStatistics/AthletesStatistics.js +89 -0
  220. package/dist/src/modules/stats/view/components/AthletesStatistics/StatisticTable.js +12 -0
  221. package/dist/src/modules/stats/view/components/AthletesStatistics/index.js +2 -0
  222. package/dist/src/modules/stats/view/index.js +2 -0
  223. package/dist/src/modules/stats/view-model/StatsViewModel.d.ts +5 -0
  224. package/dist/src/modules/stats/view-model/StatsViewModel.js +989 -0
  225. package/dist/src/sentry.js +31 -0
  226. package/dist/src/services/AnalyticsService/AnalyticsService.d.ts +12 -7
  227. package/dist/src/services/AnalyticsService/AnalyticsService.js +402 -0
  228. package/dist/src/services/AnalyticsService/types.js +1 -0
  229. package/dist/src/services/BetsService.js +122 -0
  230. package/dist/src/services/FantasyService.js +92 -0
  231. package/dist/src/services/GlobalService/GlobalService.d.ts +9 -0
  232. package/dist/src/services/GlobalService/PageService.d.ts +9 -0
  233. package/dist/src/services/GlobalService/index.d.ts +2 -0
  234. package/dist/src/services/GlobalService/mocks.d.ts +2 -0
  235. package/dist/src/services/GlobalService/types.d.ts +11 -0
  236. package/dist/src/services/InsightsService/InsightsPayloadBuilder.d.ts +1 -2
  237. package/dist/src/services/InsightsService/InsightsPayloadBuilder.js +386 -0
  238. package/dist/src/services/InsightsService/InsightsService.js +74 -0
  239. package/dist/src/services/NetworkManager/NetworkManager.js +457 -0
  240. package/dist/src/services/NetworkManager/NetworkManagerFactory.js +101 -0
  241. package/dist/src/services/NetworkManager/errors.js +45 -0
  242. package/dist/src/services/NetworkManager/types.js +10 -0
  243. package/dist/src/services/PageService/PageService.d.ts +9 -0
  244. package/dist/src/services/PageService/PageService.js +39 -0
  245. package/dist/src/services/PageService/index.d.ts +2 -0
  246. package/dist/src/services/PageService/index.js +2 -0
  247. package/dist/src/services/PageService/mocks.d.ts +2 -0
  248. package/dist/src/services/PageService/mocks.js +104 -0
  249. package/dist/src/services/PageService/types.d.ts +76 -0
  250. package/dist/src/services/PageService/types.js +1 -0
  251. package/dist/src/services/ShopService.js +46 -0
  252. package/dist/src/services/StaticService/PageService.d.ts +9 -0
  253. package/dist/src/services/StaticService/StaticService.d.ts +3 -1
  254. package/dist/src/services/StaticService/StaticService.js +59 -0
  255. package/dist/src/services/StaticService/index.js +2 -0
  256. package/dist/src/services/StaticService/mocks.js +104 -0
  257. package/dist/src/services/StaticService/types.js +1 -0
  258. package/dist/src/services/StatsService.d.ts +1 -0
  259. package/dist/src/services/StatsService.js +147 -0
  260. package/dist/src/services/ThemeManager/constants.js +47 -0
  261. package/dist/src/services/UserSettingsManager.js +155 -0
  262. package/dist/src/test-polyfills.js +15 -0
  263. package/dist/src/types/OverlayTypes.js +4 -0
  264. package/dist/src/view-models/MaestroEventViewModel.d.ts +1 -2
  265. package/dist/src/view-models/MaestroEventViewModel.js +307 -0
  266. package/dist/src/view-models/OverlayViewModel.js +222 -0
  267. package/dist/src/view-models/PanelManagerViewModel.js +212 -0
  268. package/dist/src/view-models/ViewModel.js +32 -0
  269. package/package.json +1 -1
@@ -0,0 +1,989 @@
1
+ import { Observable } from '../../../helpers/Observable';
2
+ import { ViewModel } from '../../../view-models/ViewModel';
3
+ import { MOCK_STATS_API_PRE_GAME_RESPONSE } from '../mocks';
4
+ import StatsService from '@/services/StatsService';
5
+ import { formatLastPlayMarquee, parseDriveDescription, } from '../utils/driveSummaryUtils';
6
+ import AnalyticsService from '@/services/AnalyticsService/AnalyticsService';
7
+ import { setImageDimensions } from '@/helpers/url';
8
+ import SDK from '@/index';
9
+ import { captureException } from '@/sentry';
10
+ import { stringHashCode } from '@/helpers/strings';
11
+ const STATS_ERROR_STATE = {
12
+ NONE: 'none',
13
+ TAB_LOAD_FAILURE: 'tabLoadFailure',
14
+ };
15
+ // Constants for configuration
16
+ const DEFAULT_POLLING_INTERVAL = 10000; // 10 seconds
17
+ const SMART_POLLING_DELAY = 1000; // 1 second delay after smart polling immediate call
18
+ // The interval for polling stats data (in milliseconds)
19
+ const STATS_ITEM_TYPE_CARD_MAP = {
20
+ activeAthletes: 'activeAthletes',
21
+ driveSummary: 'drives',
22
+ gameLeaders: 'leaders',
23
+ headerTeam: 'header',
24
+ matchPredictor: 'predictor',
25
+ promotion: 'promotion',
26
+ recentGames: 'previousGames',
27
+ scoringSummary: 'scoringSummary',
28
+ teamStats: 'entities',
29
+ winProbability: 'winProbability',
30
+ penalties: 'penaltySummary',
31
+ };
32
+ export class StatsViewModel extends ViewModel {
33
+ constructor(eventId, delegate, options = {}) {
34
+ super();
35
+ Object.defineProperty(this, "isDataLoading", {
36
+ enumerable: true,
37
+ configurable: true,
38
+ writable: true,
39
+ value: new Observable(true)
40
+ });
41
+ Object.defineProperty(this, "hasEverSuccessfullyLoaded", {
42
+ enumerable: true,
43
+ configurable: true,
44
+ writable: true,
45
+ value: new Observable(false)
46
+ });
47
+ Object.defineProperty(this, "eventId", {
48
+ enumerable: true,
49
+ configurable: true,
50
+ writable: true,
51
+ value: void 0
52
+ });
53
+ Object.defineProperty(this, "statsItems", {
54
+ enumerable: true,
55
+ configurable: true,
56
+ writable: true,
57
+ value: new Observable([])
58
+ });
59
+ Object.defineProperty(this, "errorState", {
60
+ enumerable: true,
61
+ configurable: true,
62
+ writable: true,
63
+ value: new Observable('none')
64
+ });
65
+ Object.defineProperty(this, "currentGameState", {
66
+ enumerable: true,
67
+ configurable: true,
68
+ writable: true,
69
+ value: new Observable('')
70
+ });
71
+ Object.defineProperty(this, "statsService", {
72
+ enumerable: true,
73
+ configurable: true,
74
+ writable: true,
75
+ value: void 0
76
+ });
77
+ Object.defineProperty(this, "analyticsService", {
78
+ enumerable: true,
79
+ configurable: true,
80
+ writable: true,
81
+ value: void 0
82
+ });
83
+ // Properties
84
+ Object.defineProperty(this, "useMockData", {
85
+ enumerable: true,
86
+ configurable: true,
87
+ writable: true,
88
+ value: false
89
+ });
90
+ Object.defineProperty(this, "lastResponseHash", {
91
+ enumerable: true,
92
+ configurable: true,
93
+ writable: true,
94
+ value: null
95
+ });
96
+ // Enhanced timecode-related properties
97
+ Object.defineProperty(this, "currentPlayerTimeCode", {
98
+ enumerable: true,
99
+ configurable: true,
100
+ writable: true,
101
+ value: null
102
+ });
103
+ Object.defineProperty(this, "timecodePollingTask", {
104
+ enumerable: true,
105
+ configurable: true,
106
+ writable: true,
107
+ value: null
108
+ });
109
+ Object.defineProperty(this, "smartPollingTimeout", {
110
+ enumerable: true,
111
+ configurable: true,
112
+ writable: true,
113
+ value: null
114
+ });
115
+ Object.defineProperty(this, "timecodePollingState", {
116
+ enumerable: true,
117
+ configurable: true,
118
+ writable: true,
119
+ value: {
120
+ isActive: false,
121
+ currentUrl: null,
122
+ lastUpdateTime: 0,
123
+ }
124
+ });
125
+ Object.defineProperty(this, "onEngage", {
126
+ enumerable: true,
127
+ configurable: true,
128
+ writable: true,
129
+ value: (type) => {
130
+ this.analyticsService.panelClick({
131
+ type: 'espnStats',
132
+ name: 'stats_item_clicked',
133
+ metadata: {
134
+ card: [STATS_ITEM_TYPE_CARD_MAP[type]],
135
+ game_state: [this.currentGameState.value],
136
+ },
137
+ });
138
+ }
139
+ });
140
+ this.eventId = eventId;
141
+ this.useMockData = options.useMockData || false;
142
+ this.statsService = new StatsService(delegate);
143
+ this.analyticsService = new AnalyticsService();
144
+ }
145
+ /**
146
+ * Sets panel-specific data configuration
147
+ * @param panelData - Object containing panel configuration
148
+ */
149
+ setPanelData(panelData) {
150
+ if ('overrideUrl' in panelData) {
151
+ const overrideUrl = panelData.overrideUrl;
152
+ if (typeof overrideUrl === 'string' && overrideUrl.length > 0) {
153
+ console.log('[StatsViewModel] Setting overrideUrl:', overrideUrl);
154
+ this.statsService.overrideUrl = overrideUrl;
155
+ }
156
+ }
157
+ }
158
+ /**
159
+ * Initialize the ViewModel and start data fetching
160
+ */
161
+ async init() {
162
+ if (this.useMockData) {
163
+ await this.fetchMockData();
164
+ }
165
+ else {
166
+ // Get the current timecode from MaestroEventViewModel instead of defaulting to null
167
+ // This ensures we don't override the current timecode when switching tabs
168
+ const currentTimeCode = this.getCurrentTimeCode();
169
+ this.onUpdatePlayerTimeCode(currentTimeCode);
170
+ this.analyticsService.panelView('espnStats');
171
+ }
172
+ }
173
+ /**
174
+ * Get the current timecode from the MaestroEventViewModel
175
+ * This prevents the stats panel from always defaulting to real-time data
176
+ */
177
+ getCurrentTimeCode() {
178
+ try {
179
+ // Access the MaestroEventViewModel through the imported SDK to get current timecode
180
+ const maestroEventViewModel = SDK.getMaestroEventViewModel();
181
+ if (maestroEventViewModel &&
182
+ typeof maestroEventViewModel.getPlayerTimeCode === 'function') {
183
+ return maestroEventViewModel.getPlayerTimeCode();
184
+ }
185
+ }
186
+ catch (error) {
187
+ captureException(error, {
188
+ context: 'StatsViewModel.getCurrentTimeCode',
189
+ });
190
+ console.warn('Could not get current timecode from MaestroEventViewModel:', error);
191
+ }
192
+ // Fallback to null (real-time) if we can't get the current timecode
193
+ return null;
194
+ }
195
+ /**
196
+ * Clean up resources when the ViewModel is no longer needed
197
+ */
198
+ cleanup() {
199
+ this.stopTimecodePolling();
200
+ this.clearSmartPollingTimeout();
201
+ this.resetTimecodePollingState();
202
+ }
203
+ /**
204
+ * Handle player timecode updates with enhanced logging and validation
205
+ *
206
+ * @param playerTimeCode - The timecode in milliseconds, or null for live
207
+ */
208
+ onUpdatePlayerTimeCode(playerTimeCode) {
209
+ this.currentPlayerTimeCode = playerTimeCode;
210
+ if (this.useMockData) {
211
+ return;
212
+ }
213
+ try {
214
+ this.performSmartPolling(playerTimeCode);
215
+ }
216
+ catch (error) {
217
+ captureException(error, {
218
+ context: 'StatsViewModel.onUpdatePlayerTimeCode',
219
+ });
220
+ console.error('Error handling player timecode update:', error);
221
+ this.handleTimecodeError('Error handling player timecode update', error);
222
+ }
223
+ }
224
+ clearTabLoadFailure() {
225
+ this.errorState.value = 'none';
226
+ const currentTimeCode = this.getCurrentTimeCode();
227
+ this.onUpdatePlayerTimeCode(currentTimeCode);
228
+ }
229
+ /**
230
+ * Implement smart polling strategy: immediate API call + delayed polling resumption
231
+ *
232
+ * @param playerTimeCode - The timecode in milliseconds, or null
233
+ */
234
+ async performSmartPolling(playerTimeCode) {
235
+ this.clearSmartPollingTimeout();
236
+ this.stopTimecodePolling();
237
+ try {
238
+ const immediateStats = await this.statsService.getStatsByUrl(playerTimeCode);
239
+ if (!immediateStats) {
240
+ throw new Error('API returned undefined or null response');
241
+ }
242
+ this.processPolledData(immediateStats);
243
+ this.smartPollingTimeout = window.setTimeout(() => {
244
+ this.startTimecodePolling(playerTimeCode);
245
+ this.smartPollingTimeout = null;
246
+ }, SMART_POLLING_DELAY);
247
+ }
248
+ catch (error) {
249
+ captureException(error, {
250
+ context: 'StatsViewModel.performSmartPolling',
251
+ });
252
+ console.error('Error during smart polling immediate call:', error);
253
+ this.handleTimecodeError('Error during smart polling immediate call', error);
254
+ this.stopTimecodePolling();
255
+ this.startTimecodePolling(playerTimeCode);
256
+ }
257
+ }
258
+ /**
259
+ * Enhanced error handling for timecode-related operations
260
+ *
261
+ * @param context - Description of where the error occurred
262
+ * @param error - The error that occurred
263
+ */
264
+ handleTimecodeError(context, error) {
265
+ const errorDetails = {
266
+ context,
267
+ errorType: error?.constructor?.name || 'Unknown',
268
+ errorMessage: error instanceof Error ? error.message : String(error),
269
+ timestamp: new Date().toISOString(),
270
+ currentTimecode: this.currentPlayerTimeCode,
271
+ hasExistingData: this.statsItems.value.length > 0,
272
+ lastResponseHash: this.lastResponseHash,
273
+ url: this.timecodePollingState.currentUrl,
274
+ };
275
+ console.error(`${context}:`, errorDetails);
276
+ if (!this.hasEverSuccessfullyLoaded.value) {
277
+ this.errorState.value = 'tabLoadFailure';
278
+ }
279
+ }
280
+ /**
281
+ * Clear any pending smart polling timeout
282
+ */
283
+ clearSmartPollingTimeout() {
284
+ if (this.smartPollingTimeout) {
285
+ clearTimeout(this.smartPollingTimeout);
286
+ this.smartPollingTimeout = null;
287
+ }
288
+ }
289
+ /**
290
+ * Reset timecode polling state
291
+ */
292
+ resetTimecodePollingState() {
293
+ this.timecodePollingState = {
294
+ isActive: false,
295
+ currentUrl: null,
296
+ lastUpdateTime: 0,
297
+ };
298
+ }
299
+ /**
300
+ * Start timecode-based polling with enhanced logging
301
+ *
302
+ * @param playerTimeCode - The timecode in milliseconds, or null
303
+ */
304
+ startTimecodePolling(playerTimeCode) {
305
+ this.stopTimecodePolling();
306
+ try {
307
+ this.timecodePollingTask = this.statsService.pollStatsByUrl(playerTimeCode, DEFAULT_POLLING_INTERVAL, (data) => {
308
+ this.processPolledData(data);
309
+ }, (error) => {
310
+ console.error('Timecode polling error:', error);
311
+ this.handleTimecodeError('Timecode polling error', error);
312
+ });
313
+ this.timecodePollingState.isActive = true;
314
+ }
315
+ catch (error) {
316
+ captureException(error, {
317
+ context: 'StatsViewModel.startTimecodePolling',
318
+ });
319
+ console.error('Error setting up timecode-based Stats polling:', error);
320
+ this.handleTimecodeError('Error setting up timecode-based Stats polling', error);
321
+ }
322
+ }
323
+ /**
324
+ * Stop timecode-based polling with enhanced logging
325
+ */
326
+ stopTimecodePolling() {
327
+ if (this.timecodePollingTask) {
328
+ this.timecodePollingTask.stop();
329
+ this.timecodePollingTask = null;
330
+ }
331
+ this.timecodePollingState.isActive = false;
332
+ }
333
+ /**
334
+ * Fetch mock data for testing
335
+ */
336
+ async fetchMockData() {
337
+ this.processData(MOCK_STATS_API_PRE_GAME_RESPONSE);
338
+ this.hasEverSuccessfullyLoaded.value = true;
339
+ this.isDataLoading.value = false;
340
+ }
341
+ /**
342
+ * Process polled data with hash comparison to avoid unnecessary re-renders
343
+ * Enhanced with comprehensive logging to show data changes
344
+ *
345
+ * @param data - The stats data received from the API
346
+ */
347
+ processPolledData(data) {
348
+ const responseString = JSON.stringify(data);
349
+ const currentHash = stringHashCode(responseString);
350
+ if (this.hasEverSuccessfullyLoaded.value &&
351
+ currentHash === this.lastResponseHash) {
352
+ return;
353
+ }
354
+ this.lastResponseHash = currentHash;
355
+ this.hasEverSuccessfullyLoaded.value = true;
356
+ this.errorState.value = 'none';
357
+ this.processData(data);
358
+ if (data.gameState === 'post') {
359
+ this.stopTimecodePolling();
360
+ }
361
+ }
362
+ processData(data) {
363
+ const statsItems = [];
364
+ const headerTeam = this.parseHeaderTeams(data);
365
+ if (headerTeam) {
366
+ statsItems.push(headerTeam);
367
+ }
368
+ const matchPredictor = this.parseMatchPredictor(data);
369
+ if (matchPredictor) {
370
+ statsItems.push(matchPredictor);
371
+ }
372
+ const driveSummary = this.parseDriveSummary(data);
373
+ if (driveSummary.length > 0) {
374
+ statsItems.push(...driveSummary);
375
+ }
376
+ const activeAthletes = this.parseActiveAthletes(data);
377
+ if (activeAthletes) {
378
+ statsItems.push(activeAthletes);
379
+ }
380
+ const scoringSummary = this.parseScoringSummary(data);
381
+ if (scoringSummary) {
382
+ statsItems.push(scoringSummary);
383
+ }
384
+ const penalties = this.parsePenalties(data);
385
+ if (penalties) {
386
+ statsItems.push(penalties);
387
+ }
388
+ const gameLeaders = this.parseGameLeaders(data);
389
+ if (gameLeaders) {
390
+ statsItems.push(gameLeaders);
391
+ }
392
+ const recentGames = this.parseRecentGames(data);
393
+ if (recentGames) {
394
+ statsItems.push(recentGames);
395
+ }
396
+ const winProbability = this.parseWinProbability(data);
397
+ if (winProbability) {
398
+ statsItems.push(winProbability);
399
+ }
400
+ const teamsStatistics = this.parseTeamsStatistics(data);
401
+ if (teamsStatistics) {
402
+ statsItems.push(teamsStatistics);
403
+ }
404
+ const promotion = this.parsePromotion(data);
405
+ if (promotion) {
406
+ statsItems.push(promotion);
407
+ }
408
+ // Update the stats items observable
409
+ this.statsItems.value = statsItems;
410
+ this.currentGameState.value = data.gameState;
411
+ }
412
+ parseDriveSummary(data) {
413
+ const teams = data.entities?.teams;
414
+ const drives = data.drives;
415
+ if (!teams || !drives || drives.length === 0) {
416
+ return [];
417
+ }
418
+ const result = [];
419
+ for (const drive of drives) {
420
+ if (!drive?.team?.$key) {
421
+ continue;
422
+ }
423
+ const plays = [...(drive.plays || [])].reverse();
424
+ const lastPlay = plays[plays.length - 1] || null;
425
+ let lastPlayMarquee = '';
426
+ if (lastPlay) {
427
+ lastPlayMarquee = formatLastPlayMarquee(lastPlay);
428
+ }
429
+ const { plays: playsQuantity, time, yards } = parseDriveDescription(drive.description || '');
430
+ const team = teams[drive.team.$key];
431
+ if (!team) {
432
+ continue;
433
+ }
434
+ result.push({
435
+ id: drive.id,
436
+ lastPlayMarquee,
437
+ plays,
438
+ playsQuantity,
439
+ team,
440
+ time,
441
+ type: 'driveSummary',
442
+ yards,
443
+ });
444
+ }
445
+ return result;
446
+ }
447
+ parseTeamsStatistics(data) {
448
+ const teams = data.entities?.teams;
449
+ if (!teams) {
450
+ return null;
451
+ }
452
+ const awayTeam = Object.values(teams).find((team) => team.homeAway === 'away');
453
+ const homeTeam = Object.values(teams).find((team) => team.homeAway === 'home');
454
+ if (!awayTeam || !homeTeam) {
455
+ return null;
456
+ }
457
+ // Ensure both teams have statistics
458
+ if (!awayTeam.statistics || !homeTeam.statistics) {
459
+ return null;
460
+ }
461
+ const statistics = [];
462
+ for (let i = 0; i < awayTeam.statistics.length; i++) {
463
+ const awayStat = awayTeam.statistics[i];
464
+ const homeStat = homeTeam.statistics[i];
465
+ statistics.push({
466
+ awayTeamDisplayValue: awayStat?.displayValue || '0',
467
+ displayName: awayStat.displayName || '',
468
+ homeTeamDisplayValue: homeStat?.displayValue || '0',
469
+ awayTeamValue: awayStat?.value || 0,
470
+ homeTeamValue: homeStat?.value || 0,
471
+ });
472
+ }
473
+ return {
474
+ type: 'teamStats',
475
+ awayTeam: {
476
+ logo: awayTeam.logo,
477
+ displayName: awayTeam.displayName || '',
478
+ color: awayTeam.color || '',
479
+ abbreviation: awayTeam.abbreviation || '',
480
+ },
481
+ homeTeam: {
482
+ logo: homeTeam.logo,
483
+ displayName: homeTeam.displayName || '',
484
+ color: homeTeam.color || '',
485
+ abbreviation: homeTeam.abbreviation || '',
486
+ },
487
+ statistics,
488
+ };
489
+ }
490
+ parseMatchPredictor(data) {
491
+ const teams = data.entities?.teams;
492
+ const predictor = data.predictor;
493
+ if (!teams || !predictor || !predictor.homeTeam || !predictor.awayTeam) {
494
+ return null;
495
+ }
496
+ // Get team data
497
+ const homeTeam = teams[predictor.homeTeam.team.$key];
498
+ const awayTeam = teams[predictor.awayTeam.team.$key];
499
+ if (!homeTeam || !awayTeam) {
500
+ return null;
501
+ }
502
+ // Parse percentages
503
+ const homePercentage = parseFloat(predictor.homeTeam.gameProjection);
504
+ const awayPercentage = parseFloat(predictor.awayTeam.gameProjection);
505
+ return {
506
+ type: 'matchPredictor',
507
+ header: predictor.header,
508
+ imageUrl: predictor.imageUrl,
509
+ awayTeam: {
510
+ percentage: awayPercentage,
511
+ abbreviation: awayTeam.abbreviation,
512
+ logo: awayTeam.logo,
513
+ displayName: awayTeam.displayName || '',
514
+ },
515
+ homeTeam: {
516
+ percentage: homePercentage,
517
+ abbreviation: homeTeam.abbreviation,
518
+ logo: homeTeam.logo,
519
+ displayName: homeTeam.displayName || '',
520
+ },
521
+ };
522
+ }
523
+ parsePromotion(response) {
524
+ const promotion = response.promotion;
525
+ if (!promotion) {
526
+ return null;
527
+ }
528
+ return {
529
+ type: 'promotion',
530
+ title: promotion.title || '',
531
+ description: promotion.description || '',
532
+ logo: promotion.logo || '',
533
+ qrCode: promotion.qrCode || '',
534
+ backgroundImage: promotion.backgroundImage || '',
535
+ };
536
+ }
537
+ parseHeaderTeams(response) {
538
+ const teams = response.entities?.teams;
539
+ if (!teams || !response.header?.teams) {
540
+ return null;
541
+ }
542
+ const homeTeamHeader = response.header.teams.find((t) => {
543
+ const team = teams[t.$key];
544
+ return team && team.homeAway === 'home';
545
+ });
546
+ const awayTeamHeader = response.header.teams.find((t) => {
547
+ const team = teams[t.$key];
548
+ return team && team.homeAway === 'away';
549
+ });
550
+ if (!homeTeamHeader || !awayTeamHeader) {
551
+ return null;
552
+ }
553
+ const homeTeam = teams[homeTeamHeader.$key];
554
+ const awayTeam = teams[awayTeamHeader.$key];
555
+ if (!homeTeam || !awayTeam) {
556
+ return null;
557
+ }
558
+ const statistics = [];
559
+ const statisticsSet = new Set();
560
+ const awayStatistics = awayTeam.statistics || [];
561
+ const homeStatistics = homeTeam.statistics || [];
562
+ [...awayStatistics, ...homeStatistics].forEach((st) => {
563
+ statisticsSet.add(st.$key);
564
+ });
565
+ // Ensure all statistics from both teams are included
566
+ statisticsSet.forEach((statKey) => {
567
+ const awayStat = awayStatistics.find((st) => st.$key === statKey);
568
+ const homeStat = homeStatistics.find((st) => st.$key === statKey);
569
+ statistics.push({
570
+ displayName: awayStat?.displayName || homeStat?.displayName || '',
571
+ awayTeamValue: awayStat?.value || 0,
572
+ homeTeamValue: homeStat?.value || 0,
573
+ awayTeamDisplayValue: awayStat?.displayValue || '0',
574
+ homeTeamDisplayValue: homeStat?.displayValue || '0',
575
+ });
576
+ });
577
+ return {
578
+ type: 'headerTeam',
579
+ linescoresHeader: response.header.linescoresHeader || [],
580
+ isLive: response.gameState === 'in' || response.gameState === 'post',
581
+ statistics,
582
+ awayTeam: {
583
+ abbreviation: awayTeam.abbreviation,
584
+ color: awayTeam.color,
585
+ id: awayTeam.id,
586
+ linescores: awayTeamHeader?.linescores?.map((l) => l.displayValue) || [
587
+ awayTeamHeader.score || '0',
588
+ ],
589
+ logo: awayTeam.logo,
590
+ name: awayTeam.name,
591
+ record: awayTeam.record,
592
+ },
593
+ homeTeam: {
594
+ abbreviation: homeTeam.abbreviation,
595
+ color: homeTeam.color,
596
+ id: homeTeam.id,
597
+ linescores: homeTeamHeader?.linescores?.map((l) => l.displayValue) || [
598
+ homeTeamHeader.score || '0',
599
+ ],
600
+ logo: homeTeam.logo,
601
+ name: homeTeam.name,
602
+ record: homeTeam.record,
603
+ },
604
+ };
605
+ }
606
+ parseRecentGames(response) {
607
+ const teams = response.entities?.teams;
608
+ const previousGames = response.previousGames;
609
+ if (!teams || !previousGames || previousGames.length === 0) {
610
+ return null;
611
+ }
612
+ const awayTeamPreviousGame = previousGames.find((game) => game?.displayOrder === 1);
613
+ const homeTeamPreviousGame = previousGames.find((game) => game?.displayOrder === 2);
614
+ if (!awayTeamPreviousGame || !homeTeamPreviousGame) {
615
+ return null;
616
+ }
617
+ if (!awayTeamPreviousGame.events || !homeTeamPreviousGame.events) {
618
+ return null;
619
+ }
620
+ if (!awayTeamPreviousGame.team?.$key || !homeTeamPreviousGame.team?.$key) {
621
+ return null;
622
+ }
623
+ const awayTeam = teams[awayTeamPreviousGame.team.$key];
624
+ const homeTeam = teams[homeTeamPreviousGame.team.$key];
625
+ if (!awayTeam || !homeTeam) {
626
+ return null;
627
+ }
628
+ const previousGame = {
629
+ type: 'recentGames',
630
+ awayTeam: {
631
+ events: [...awayTeamPreviousGame.events]
632
+ .reverse()
633
+ .filter((event) => event?.team?.$key)
634
+ .map((event) => {
635
+ const eventTeam = teams[event.team.$key];
636
+ return {
637
+ gameResult: event.gameResult,
638
+ homeAway: event.team.homeAway,
639
+ score: event.score,
640
+ teamLogo: eventTeam?.logo,
641
+ };
642
+ }),
643
+ logo: awayTeam.logo,
644
+ name: awayTeam.shortDisplayName,
645
+ },
646
+ homeTeam: {
647
+ events: [...homeTeamPreviousGame.events]
648
+ .reverse()
649
+ .filter((event) => event?.team?.$key)
650
+ .map((event) => {
651
+ const eventTeam = teams[event.team.$key];
652
+ return {
653
+ gameResult: event.gameResult,
654
+ homeAway: event.team.homeAway,
655
+ score: event.score,
656
+ teamLogo: eventTeam?.logo,
657
+ };
658
+ }),
659
+ logo: homeTeam.logo,
660
+ name: homeTeam.shortDisplayName,
661
+ },
662
+ };
663
+ return previousGame;
664
+ }
665
+ parseWinProbability(response) {
666
+ const winProbability = response.winProbability;
667
+ if (!winProbability ||
668
+ !winProbability.homeTeam ||
669
+ !winProbability.awayTeam) {
670
+ return null;
671
+ }
672
+ const teams = response.entities?.teams;
673
+ if (!teams) {
674
+ return null;
675
+ }
676
+ const homeTeam = teams[winProbability.homeTeam.team.$key];
677
+ const awayTeam = teams[winProbability.awayTeam.team.$key];
678
+ if (!homeTeam || !awayTeam) {
679
+ return null;
680
+ }
681
+ return {
682
+ type: 'winProbability',
683
+ chartImageUrl: winProbability.imageUrl,
684
+ awayTeam: {
685
+ id: awayTeam.id,
686
+ abbreviation: awayTeam.abbreviation,
687
+ logo: awayTeam.logo,
688
+ winPercentage: winProbability.awayTeam.winPercentage,
689
+ },
690
+ homeTeam: {
691
+ id: homeTeam.id,
692
+ abbreviation: homeTeam.abbreviation,
693
+ logo: homeTeam.logo,
694
+ winPercentage: winProbability.homeTeam.winPercentage,
695
+ },
696
+ };
697
+ }
698
+ parseGameLeaders(response) {
699
+ if (!response.entities?.athletes || !response.entities?.teams) {
700
+ return null;
701
+ }
702
+ const leaders = response.leaders;
703
+ const isPre = response.gameState === 'pre';
704
+ if (!leaders || leaders.length === 0) {
705
+ return null;
706
+ }
707
+ const items = [];
708
+ for (const leader of leaders) {
709
+ if (!leader.away || !leader.home) {
710
+ continue;
711
+ }
712
+ if (!leader.away?.athlete?.$key || !leader.home?.athlete?.$key) {
713
+ continue;
714
+ }
715
+ const awayAthlete = response.entities.athletes[leader.away.athlete.$key];
716
+ const homeAthlete = response.entities.athletes[leader.home.athlete.$key];
717
+ if (!awayAthlete || !homeAthlete) {
718
+ continue;
719
+ }
720
+ const awayAthleteStatistics = leader.away.athlete.statistics || [];
721
+ const homeAthleteStatistics = leader.home.athlete.statistics || [];
722
+ const value = {
723
+ displayName: leader.displayName,
724
+ awayAthlete: {
725
+ id: awayAthlete.id,
726
+ displayName: awayAthlete.displayName,
727
+ headshot: awayAthlete.headshot
728
+ ? setImageDimensions(awayAthlete.headshot, 256, 144)
729
+ : '',
730
+ position: awayAthlete.position?.abbreviation || '-',
731
+ shortName: awayAthlete.shortName,
732
+ jersey: awayAthlete.jersey,
733
+ statistics: awayAthleteStatistics,
734
+ },
735
+ homeAthlete: {
736
+ id: homeAthlete.id,
737
+ displayName: homeAthlete.displayName,
738
+ headshot: homeAthlete.headshot
739
+ ? setImageDimensions(homeAthlete.headshot, 256, 144)
740
+ : '',
741
+ position: homeAthlete.position?.abbreviation || '-',
742
+ shortName: homeAthlete.shortName,
743
+ jersey: homeAthlete.jersey,
744
+ statistics: homeAthleteStatistics,
745
+ },
746
+ };
747
+ items.push(value);
748
+ }
749
+ if (!items.length) {
750
+ return null;
751
+ }
752
+ return {
753
+ type: 'gameLeaders',
754
+ items,
755
+ displayText: isPre ? 'Season Leaders' : 'Game Leaders',
756
+ };
757
+ }
758
+ parseActiveAthletes(response) {
759
+ const activeAthletes = response.activeAthletes;
760
+ const { awayTeam, homeTeam } = this.getActiveAthletesTeamInfo(response);
761
+ const athletes = [];
762
+ if (!response.entities?.athletes ||
763
+ !activeAthletes ||
764
+ awayTeam.index === -1 ||
765
+ homeTeam.index === -1) {
766
+ return null;
767
+ }
768
+ for (let i = 0; i < activeAthletes.teams[awayTeam.index].athletes.length; i++) {
769
+ const awayAthlete = activeAthletes.teams[awayTeam.index].athletes[i];
770
+ const homeAthlete = activeAthletes.teams[homeTeam.index].athletes[i];
771
+ if (!awayAthlete?.$key || !homeAthlete?.$key) {
772
+ continue;
773
+ }
774
+ const awayAthleteInfo = response.entities.athletes[awayAthlete.$key];
775
+ const homeAthleteInfo = response.entities.athletes[homeAthlete.$key];
776
+ if (!awayAthleteInfo || !homeAthleteInfo) {
777
+ continue;
778
+ }
779
+ const awayAthleteStatistics = [];
780
+ const homeAthleteStatistics = [];
781
+ const awayStatLine = awayAthlete.statLine || '';
782
+ const homeStatLine = homeAthlete.statLine || '';
783
+ awayStatLine
784
+ .trim()
785
+ .split(',')
786
+ .forEach((st, index) => {
787
+ const [awayDisplayValue, awayAbbreviation] = st.trim().split(' ');
788
+ const homeStats = homeStatLine.trim().split(',')[index];
789
+ if (!homeStats) {
790
+ return;
791
+ }
792
+ const [homeDisplayValue, homeAbbreviation] = homeStats
793
+ .trim()
794
+ .split(' ');
795
+ awayAthleteStatistics.push({
796
+ displayValue: awayDisplayValue || '0',
797
+ abbreviation: awayAbbreviation || '-',
798
+ value: parseFloat(awayDisplayValue) || 0,
799
+ });
800
+ homeAthleteStatistics.push({
801
+ displayValue: homeDisplayValue || '0',
802
+ abbreviation: homeAbbreviation || '-',
803
+ value: parseFloat(homeDisplayValue) || 0,
804
+ });
805
+ });
806
+ athletes.push({
807
+ awayAthlete: {
808
+ id: awayAthleteInfo.id,
809
+ displayName: awayAthleteInfo.displayName,
810
+ headshot: awayAthleteInfo.headshot
811
+ ? setImageDimensions(awayAthleteInfo.headshot, 256, 144)
812
+ : '',
813
+ position: awayAthleteInfo.position?.abbreviation || '-',
814
+ shortName: awayAthleteInfo.shortName,
815
+ jersey: awayAthleteInfo.jersey,
816
+ statistics: awayAthleteStatistics,
817
+ minifiedStatistics: awayAthlete.statLine,
818
+ },
819
+ homeAthlete: {
820
+ id: homeAthleteInfo.id,
821
+ displayName: homeAthleteInfo.displayName,
822
+ headshot: homeAthleteInfo.headshot
823
+ ? setImageDimensions(homeAthleteInfo.headshot, 256, 144)
824
+ : '',
825
+ position: homeAthleteInfo.position?.abbreviation || '-',
826
+ shortName: homeAthleteInfo.shortName,
827
+ jersey: homeAthleteInfo.jersey,
828
+ statistics: homeAthleteStatistics,
829
+ minifiedStatistics: homeAthlete.statLine,
830
+ },
831
+ });
832
+ }
833
+ if (!athletes.length) {
834
+ return null;
835
+ }
836
+ return {
837
+ type: 'activeAthletes',
838
+ awayTeam,
839
+ homeTeam,
840
+ athletes,
841
+ displayText: activeAthletes.displayText,
842
+ };
843
+ }
844
+ getActiveAthletesTeamInfo(data) {
845
+ const teams = data.activeAthletes?.teams || [];
846
+ let awayTeam = {
847
+ index: -1,
848
+ id: '',
849
+ logo: '',
850
+ name: '-',
851
+ };
852
+ let homeTeam = {
853
+ index: -1,
854
+ id: '',
855
+ logo: '',
856
+ name: '-',
857
+ };
858
+ const entitiesTeams = data.entities?.teams;
859
+ if (!entitiesTeams) {
860
+ return { awayTeam, homeTeam };
861
+ }
862
+ for (let i = 0; i < teams.length; i++) {
863
+ const teamInfo = teams[i];
864
+ const team = entitiesTeams[teamInfo.team.$key];
865
+ if (!team) {
866
+ continue;
867
+ }
868
+ if (team.homeAway === 'away') {
869
+ awayTeam = {
870
+ index: i,
871
+ id: team.id,
872
+ logo: team.logo,
873
+ name: team.name || '-',
874
+ };
875
+ }
876
+ else if (team.homeAway === 'home') {
877
+ homeTeam = {
878
+ index: i,
879
+ id: team.id,
880
+ logo: team.logo,
881
+ name: team.name || '-',
882
+ };
883
+ }
884
+ }
885
+ return { awayTeam, homeTeam };
886
+ }
887
+ parseScoringSummary(data) {
888
+ const scoringSummary = data.scoringSummary;
889
+ if (!scoringSummary || !scoringSummary.sections) {
890
+ return null;
891
+ }
892
+ const teams = data.entities?.teams;
893
+ const athletes = data.entities?.athletes;
894
+ const sections = scoringSummary.sections.map((section) => {
895
+ const scores = section.items
896
+ .filter((item) => item?.team?.$key && item?.athlete?.$key)
897
+ .map((item) => {
898
+ const team = teams?.[item.team.$key];
899
+ const athlete = athletes?.[item.athlete.$key];
900
+ const [athleteNumber, ...athleteNameWithAssists] = item.title.split(' ');
901
+ let athleteAssists = '';
902
+ let athleteName = '';
903
+ if (athleteNameWithAssists.length > 0) {
904
+ athleteAssists =
905
+ athleteNameWithAssists[athleteNameWithAssists.length - 1];
906
+ athleteName = athleteNameWithAssists.slice(0, -1).join(' ');
907
+ }
908
+ return {
909
+ teamLogo: team?.logo,
910
+ teamName: team?.abbreviation,
911
+ athletePosition: athlete?.position?.abbreviation || '',
912
+ athleteNumber,
913
+ athleteName,
914
+ athleteAssists,
915
+ athleteThumbnail: athlete?.headshot
916
+ ? setImageDimensions(athlete.headshot, 200, 144)
917
+ : undefined,
918
+ description: item.description,
919
+ clock: item.clock,
920
+ };
921
+ });
922
+ return {
923
+ title: section.title,
924
+ scores,
925
+ };
926
+ });
927
+ if (!sections.length || sections.every((s) => s.scores.length === 0)) {
928
+ return null;
929
+ }
930
+ return {
931
+ type: 'scoringSummary',
932
+ title: scoringSummary.title,
933
+ sections,
934
+ };
935
+ }
936
+ parsePenalties(data) {
937
+ const penalties = data.penaltySummary;
938
+ if (!penalties || !penalties.sections) {
939
+ return null;
940
+ }
941
+ const teams = data.entities?.teams;
942
+ const athletes = data.entities?.athletes;
943
+ const sections = penalties.sections.map((section) => {
944
+ const scores = section.items
945
+ .filter((item) => item?.team?.$key && item?.athlete?.$key)
946
+ .map((item) => {
947
+ const team = teams?.[item.team.$key];
948
+ const athlete = athletes?.[item.athlete.$key];
949
+ const [athleteNumber, ...athleteName] = item.title.split(' ');
950
+ return {
951
+ teamLogo: team?.logo,
952
+ teamName: team?.abbreviation,
953
+ athletePosition: athlete?.position?.abbreviation || '',
954
+ athleteNumber,
955
+ athleteName: athleteName.join(' '),
956
+ athleteThumbnail: athlete?.headshot
957
+ ? setImageDimensions(athlete.headshot, 200, 144)
958
+ : undefined,
959
+ description: item.description,
960
+ clock: item.clock,
961
+ };
962
+ });
963
+ return {
964
+ title: section.title,
965
+ scores,
966
+ };
967
+ });
968
+ if (!sections.length || sections.every((s) => s.scores.length === 0)) {
969
+ return null;
970
+ }
971
+ return {
972
+ type: 'penalties',
973
+ title: penalties.title,
974
+ sections,
975
+ };
976
+ }
977
+ /**
978
+ * Simulate a use case using mock StatsApiResponse data
979
+ */
980
+ simulateUseCase(_useCase, testData) {
981
+ if (testData) {
982
+ this.processData(testData);
983
+ this.hasEverSuccessfullyLoaded.value = true;
984
+ this.errorState.value = 'none';
985
+ this.isDataLoading.value = false;
986
+ }
987
+ }
988
+ }
989
+ export default StatsViewModel;