@sportsdataverse/sdvplot 0.0.0-stage → 0.2.0

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 (245) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE.md +109 -0
  3. package/README.md +984 -2
  4. package/dist/_web-D7Aa5Rd8.d.ts +69 -0
  5. package/dist/aaf-GJ3SH34T.js +13 -0
  6. package/dist/aaf-HCYN5T6Y.js +37 -0
  7. package/dist/aaf-ZAE5KS2Y.js +13 -0
  8. package/dist/ahl-E66MDVCU.js +66 -0
  9. package/dist/ahl-NPQQWZEV.js +249 -0
  10. package/dist/ahl-QRDU73XX.js +309 -0
  11. package/dist/axis-CraWQRUJ.d.ts +49 -0
  12. package/dist/bins.d.ts +174 -0
  13. package/dist/bins.js +4 -0
  14. package/dist/bins.js.map +1 -0
  15. package/dist/cfb-F42SVS4X.js +5000 -0
  16. package/dist/cfb-P3SHEYRZ.js +692 -0
  17. package/dist/cfb-WXRZANLP.js +10226 -0
  18. package/dist/chartjs-surface.d.ts +42 -0
  19. package/dist/chartjs-surface.js +65 -0
  20. package/dist/chartjs-surface.js.map +1 -0
  21. package/dist/chartjs.d.ts +162 -0
  22. package/dist/chartjs.js +342 -0
  23. package/dist/chartjs.js.map +1 -0
  24. package/dist/chunk-4C3GX6QO.js +92 -0
  25. package/dist/chunk-4C3GX6QO.js.map +1 -0
  26. package/dist/chunk-57WFSI4Q.js +128 -0
  27. package/dist/chunk-57WFSI4Q.js.map +1 -0
  28. package/dist/chunk-7ULOCCFL.js +313 -0
  29. package/dist/chunk-7ULOCCFL.js.map +1 -0
  30. package/dist/chunk-AMXASQHT.js +120 -0
  31. package/dist/chunk-AMXASQHT.js.map +1 -0
  32. package/dist/chunk-B2MP37SR.js +175 -0
  33. package/dist/chunk-B2MP37SR.js.map +1 -0
  34. package/dist/chunk-C2CDY5OG.js +137 -0
  35. package/dist/chunk-C2CDY5OG.js.map +1 -0
  36. package/dist/chunk-DYMUVE6K.js +127 -0
  37. package/dist/chunk-DYMUVE6K.js.map +1 -0
  38. package/dist/chunk-E7G7VSLV.js +43 -0
  39. package/dist/chunk-E7G7VSLV.js.map +1 -0
  40. package/dist/chunk-H2C3FMMA.js +300 -0
  41. package/dist/chunk-H2C3FMMA.js.map +1 -0
  42. package/dist/chunk-J4P4HDBF.js +175 -0
  43. package/dist/chunk-J4P4HDBF.js.map +1 -0
  44. package/dist/chunk-J6R2LEJ7.js +58 -0
  45. package/dist/chunk-J6R2LEJ7.js.map +1 -0
  46. package/dist/chunk-M3XD2OSN.js +132 -0
  47. package/dist/chunk-M3XD2OSN.js.map +1 -0
  48. package/dist/chunk-OIILARXI.js +30 -0
  49. package/dist/chunk-OIILARXI.js.map +1 -0
  50. package/dist/chunk-RPVVJY74.js +170 -0
  51. package/dist/chunk-RPVVJY74.js.map +1 -0
  52. package/dist/chunk-UL5M6KDE.js +205 -0
  53. package/dist/chunk-UL5M6KDE.js.map +1 -0
  54. package/dist/chunk-WQT4IVLX.js +10 -0
  55. package/dist/chunk-WQT4IVLX.js.map +1 -0
  56. package/dist/chunk-ZAEQXFJM.js +556 -0
  57. package/dist/chunk-ZAEQXFJM.js.map +1 -0
  58. package/dist/cricket-KPCYG2HL.js +493 -0
  59. package/dist/cricket-NDXLE25B.js +116 -0
  60. package/dist/cricket-YUUF7P6M.js +160 -0
  61. package/dist/cytoscape.d.ts +188 -0
  62. package/dist/cytoscape.js +219 -0
  63. package/dist/cytoscape.js.map +1 -0
  64. package/dist/d3.d.ts +234 -0
  65. package/dist/d3.js +198 -0
  66. package/dist/d3.js.map +1 -0
  67. package/dist/deckgl.d.ts +232 -0
  68. package/dist/deckgl.js +197 -0
  69. package/dist/deckgl.js.map +1 -0
  70. package/dist/echarts.d.ts +121 -0
  71. package/dist/echarts.js +228 -0
  72. package/dist/echarts.js.map +1 -0
  73. package/dist/echl-3CR4RGFC.js +241 -0
  74. package/dist/echl-NPC32C23.js +107 -0
  75. package/dist/echl-PI33DGJC.js +64 -0
  76. package/dist/export.d.ts +130 -0
  77. package/dist/export.js +258 -0
  78. package/dist/export.js.map +1 -0
  79. package/dist/g2.d.ts +251 -0
  80. package/dist/g2.js +102 -0
  81. package/dist/g2.js.map +1 -0
  82. package/dist/headshots-B2J_LOO8.d.ts +40 -0
  83. package/dist/index.d.ts +183 -0
  84. package/dist/index.js +9 -0
  85. package/dist/index.js.map +1 -0
  86. package/dist/interact.d.ts +567 -0
  87. package/dist/interact.js +809 -0
  88. package/dist/interact.js.map +1 -0
  89. package/dist/lightweight-charts.d.ts +217 -0
  90. package/dist/lightweight-charts.js +155 -0
  91. package/dist/lightweight-charts.js.map +1 -0
  92. package/dist/maplibre.d.ts +271 -0
  93. package/dist/maplibre.js +175 -0
  94. package/dist/maplibre.js.map +1 -0
  95. package/dist/marks-DH-tQIP-.d.ts +75 -0
  96. package/dist/mbb-3TRLVW3H.js +3619 -0
  97. package/dist/mbb-DQUHCHZP.js +5111 -0
  98. package/dist/mbb-PRAIO2P5.js +371 -0
  99. package/dist/milb-6IQ6NCEW.js +291 -0
  100. package/dist/milb-EYY5MD4T.js +1752 -0
  101. package/dist/milb-EYZ4SZLI.js +797 -0
  102. package/dist/mlb-4PBUJSVQ.js +35 -0
  103. package/dist/mlb-FHPY3GQJ.js +701 -0
  104. package/dist/mlb-REVGL3N2.js +665 -0
  105. package/dist/mui.d.ts +338 -0
  106. package/dist/mui.js +184 -0
  107. package/dist/mui.js.map +1 -0
  108. package/dist/nba-ATT775GS.js +485 -0
  109. package/dist/nba-LKM4UHVU.js +482 -0
  110. package/dist/nba-YX7PXM3R.js +35 -0
  111. package/dist/nbagl-3WGZCZ4S.js +37 -0
  112. package/dist/nbagl-LP4XJRAL.js +229 -0
  113. package/dist/nbagl-VTTOU7LN.js +499 -0
  114. package/dist/ncaa_baseball-UV75LNVR.js +2670 -0
  115. package/dist/ncaa_baseball-VD5J5C72.js +419 -0
  116. package/dist/ncaa_baseball-YRBS32D6.js +4592 -0
  117. package/dist/ncaa_mhockey-IMYHS7QS.js +1033 -0
  118. package/dist/ncaa_mhockey-TXTKWHE3.js +115 -0
  119. package/dist/ncaa_mhockey-U3MFW5VT.js +663 -0
  120. package/dist/ncaa_softball-6WR6KS64.js +420 -0
  121. package/dist/ncaa_softball-RZCTIE56.js +2654 -0
  122. package/dist/ncaa_softball-VOD4PVGY.js +4599 -0
  123. package/dist/ncaa_whockey-6XXGUTBK.js +607 -0
  124. package/dist/ncaa_whockey-AKKYKY67.js +53 -0
  125. package/dist/ncaa_whockey-KBEKXFBB.js +293 -0
  126. package/dist/nfl-BDB2HYAR.js +625 -0
  127. package/dist/nfl-EHYHDLJD.js +37 -0
  128. package/dist/nfl-VF4NJCHY.js +514 -0
  129. package/dist/nfl_gsis-UY64JO37.js +4 -0
  130. package/dist/nhl-6WPHME7H.js +511 -0
  131. package/dist/nhl-EMIJFZHY.js +884 -0
  132. package/dist/nhl-ISFUKRFU.js +37 -0
  133. package/dist/nivo.d.ts +193 -0
  134. package/dist/nivo.js +8 -0
  135. package/dist/nivo.js.map +1 -0
  136. package/dist/ohl-2FEM554G.js +113 -0
  137. package/dist/ohl-C5Q4W6JB.js +32 -0
  138. package/dist/ohl-TQ4J46VC.js +66 -0
  139. package/dist/perspective.d.ts +198 -0
  140. package/dist/perspective.js +171 -0
  141. package/dist/perspective.js.map +1 -0
  142. package/dist/phf-H7POU2KW.js +32 -0
  143. package/dist/phf-K5IFMZUM.js +13 -0
  144. package/dist/phf-TWJI3BOW.js +20 -0
  145. package/dist/placement-UWkqUzpa.d.ts +32 -0
  146. package/dist/plot.d.ts +451 -0
  147. package/dist/plot.js +712 -0
  148. package/dist/plot.js.map +1 -0
  149. package/dist/plotly.d.ts +129 -0
  150. package/dist/plotly.js +360 -0
  151. package/dist/plotly.js.map +1 -0
  152. package/dist/pwhl-OX3BO4JI.js +53 -0
  153. package/dist/pwhl-W44OC3C6.js +17 -0
  154. package/dist/pwhl-ZJGG3H7W.js +23 -0
  155. package/dist/qmjhl-5HISEHLL.js +35 -0
  156. package/dist/qmjhl-DA3UHKE5.js +125 -0
  157. package/dist/qmjhl-YG4BOQPF.js +614 -0
  158. package/dist/react.d.ts +377 -0
  159. package/dist/react.js +522 -0
  160. package/dist/react.js.map +1 -0
  161. package/dist/recharts.d.ts +162 -0
  162. package/dist/recharts.js +8 -0
  163. package/dist/recharts.js.map +1 -0
  164. package/dist/resolve-CQW-jzvr.d.ts +28 -0
  165. package/dist/selection-D4T3PkRt.d.ts +140 -0
  166. package/dist/shadcn.d.ts +83 -0
  167. package/dist/shadcn.js +88 -0
  168. package/dist/shadcn.js.map +1 -0
  169. package/dist/shots.d.ts +364 -0
  170. package/dist/shots.js +86 -0
  171. package/dist/shots.js.map +1 -0
  172. package/dist/soccer-7BH3ZLVL.js +7898 -0
  173. package/dist/soccer-MRU25OGE.js +2636 -0
  174. package/dist/soccer-PF4IPPVA.js +12342 -0
  175. package/dist/surface-TR7YFJW3.js +4 -0
  176. package/dist/surface-TR7YFJW3.js.map +1 -0
  177. package/dist/surfaces-MIG7HOFU.js +6 -0
  178. package/dist/surfaces-MIG7HOFU.js.map +1 -0
  179. package/dist/surfaces-W-odWLpT.d.ts +47 -0
  180. package/dist/svelte/HeadshotMark.svelte +25 -0
  181. package/dist/svelte/HeadshotMark.svelte.d.ts +10 -0
  182. package/dist/svelte/HeadshotMark.svelte.d.ts.map +1 -0
  183. package/dist/svelte/LogoMark.svelte +25 -0
  184. package/dist/svelte/LogoMark.svelte.d.ts +10 -0
  185. package/dist/svelte/LogoMark.svelte.d.ts.map +1 -0
  186. package/dist/svelte/Marks.svelte +25 -0
  187. package/dist/svelte/Marks.svelte.d.ts +10 -0
  188. package/dist/svelte/Marks.svelte.d.ts.map +1 -0
  189. package/dist/svelte/Surface.svelte +29 -0
  190. package/dist/svelte/Surface.svelte.d.ts +9 -0
  191. package/dist/svelte/Surface.svelte.d.ts.map +1 -0
  192. package/dist/svelte/SurfaceBackdrop.svelte +26 -0
  193. package/dist/svelte/SurfaceBackdrop.svelte.d.ts +11 -0
  194. package/dist/svelte/SurfaceBackdrop.svelte.d.ts.map +1 -0
  195. package/dist/svelte/index.d.ts +13 -0
  196. package/dist/svelte/index.d.ts.map +1 -0
  197. package/dist/svelte/index.js +11 -0
  198. package/dist/svelte-core.d.ts +139 -0
  199. package/dist/svelte-core.js +123 -0
  200. package/dist/svelte-core.js.map +1 -0
  201. package/dist/tanstack.d.ts +240 -0
  202. package/dist/tanstack.js +110 -0
  203. package/dist/tanstack.js.map +1 -0
  204. package/dist/testing.d.ts +75 -0
  205. package/dist/testing.js +239 -0
  206. package/dist/testing.js.map +1 -0
  207. package/dist/three.d.ts +223 -0
  208. package/dist/three.js +50 -0
  209. package/dist/three.js.map +1 -0
  210. package/dist/tiers-BN4md-GB.d.ts +65 -0
  211. package/dist/types-C7n6gJF3.d.ts +62 -0
  212. package/dist/ufl-7VLZTLTT.js +143 -0
  213. package/dist/ufl-CCMBLPSW.js +100 -0
  214. package/dist/ufl-F2BJRXME.js +16 -0
  215. package/dist/unovis.d.ts +127 -0
  216. package/dist/unovis.js +108 -0
  217. package/dist/unovis.js.map +1 -0
  218. package/dist/uplot.d.ts +120 -0
  219. package/dist/uplot.js +110 -0
  220. package/dist/uplot.js.map +1 -0
  221. package/dist/usfl-4CR5RMZJ.js +32 -0
  222. package/dist/usfl-LD7HP2YK.js +14 -0
  223. package/dist/usfl-VU4YSJQD.js +19 -0
  224. package/dist/ushl-CPG2LB4L.js +73 -0
  225. package/dist/ushl-PQL6HJAD.js +88 -0
  226. package/dist/ushl-ZT2ATPHK.js +22 -0
  227. package/dist/vega.d.ts +85 -0
  228. package/dist/vega.js +362 -0
  229. package/dist/vega.js.map +1 -0
  230. package/dist/victory.d.ts +107 -0
  231. package/dist/victory.js +38 -0
  232. package/dist/victory.js.map +1 -0
  233. package/dist/wbb-3KHCDYTK.js +5087 -0
  234. package/dist/wbb-BMX53Q3Q.js +369 -0
  235. package/dist/wbb-QNRN2CJJ.js +3620 -0
  236. package/dist/whl-4ATYZWUB.js +97 -0
  237. package/dist/whl-RVFPL6MH.js +79 -0
  238. package/dist/whl-Y7ASKYMM.js +28 -0
  239. package/dist/wnba-FWX5SFVI.js +232 -0
  240. package/dist/wnba-GX6HZR4B.js +220 -0
  241. package/dist/wnba-NTMWIPP6.js +20 -0
  242. package/dist/xfl-43R2JMJK.js +30 -0
  243. package/dist/xfl-H3BXQPZZ.js +16 -0
  244. package/dist/xfl-XON7A7FI.js +69 -0
  245. package/package.json +366 -3
@@ -0,0 +1,188 @@
1
+ import { R as Row } from './_web-D7Aa5Rd8.js';
2
+ import { S as SeasonInput, I as IdSystem, W as Which, L as League } from './types-C7n6gJF3.js';
3
+
4
+ /**
5
+ * The Cytoscape.js adapter: passing networks as Cytoscape elements and a team-coloured stylesheet. Pure: it never
6
+ * imports cytoscape (3.34.3 or later is an optional peer), so the network can be built anywhere, a server included,
7
+ * and handed to the library as plain JSON: `cytoscape({ container, elements, style, layout: { name: "preset" } })`.
8
+ * Sizes are Cytoscape style units, px at zoom 1: a node's diameter, an edge's width. Synchronous: call after
9
+ * `loadLeague(league)` / `preloadAll()` (and `loadGsis()` for gsis headshots).
10
+ */
11
+
12
+ /** A player node. `classes` is "headshot" (its image drawn as the node) or "initials" (team colour and initials). */
13
+ interface CyNode {
14
+ data: {
15
+ /** The player id, as text. */
16
+ id: string;
17
+ /** The player's name, else the id. */
18
+ label: string;
19
+ /** The first letters of the name's first and last words, else the id. */
20
+ initials: string;
21
+ /** The resolved team id; absent when the team does not resolve (the node keeps the fallback colour). */
22
+ team?: string;
23
+ /** The headshot URL, on a headshot node only. */
24
+ image?: string;
25
+ };
26
+ /** Where the player stood (with the x/y/xend/yend options): model px, y down. */
27
+ position?: {
28
+ x: number;
29
+ y: number;
30
+ };
31
+ classes: "headshot" | "initials";
32
+ }
33
+ /** A directed edge from passer to receiver. */
34
+ interface CyEdge {
35
+ data: {
36
+ /** The source and target ids joined by an arrow, as in 5485-\>3099 (Varane to Lloris). */
37
+ id: string;
38
+ /** The passer's node id. */
39
+ source: string;
40
+ /** The receiver's node id. */
41
+ target: string;
42
+ /** The pair's passes (or summed `weight`). */
43
+ weight: number;
44
+ /**
45
+ * The edge's width in px: `edgeWidth` for the heaviest pair, the others in proportion to their weight; 0 for a pair
46
+ * whose weight is 0 or less (a summed xT can be negative), since Cytoscape takes no negative width.
47
+ */
48
+ width: number;
49
+ /** The passer's resolved team id; absent when it does not resolve. */
50
+ team?: string;
51
+ };
52
+ }
53
+ /** One stylesheet block, Cytoscape's `{ selector, style }` JSON. */
54
+ interface StyleBlock {
55
+ selector: string;
56
+ style: Record<string, string | number>;
57
+ }
58
+ /** `passNetwork`'s result: what `cytoscape({ elements, style })` takes. */
59
+ interface PassNetwork {
60
+ elements: {
61
+ nodes: CyNode[];
62
+ edges: CyEdge[];
63
+ };
64
+ style: StyleBlock[];
65
+ }
66
+ /** The colour options `teamStylesheet` and `passNetwork` share. */
67
+ interface CyColorOptions {
68
+ /** The season the teams are resolved for. */
69
+ season?: SeasonInput;
70
+ /** How the team values are read. Default "auto". */
71
+ idSystem?: IdSystem;
72
+ /** The team colour drawn, "primary" (default) or "secondary". */
73
+ which?: Which;
74
+ /**
75
+ * The background the network is drawn on (Cytoscape's container). Given, every team colour keeps 3:1 contrast
76
+ * against it (dark mode) and aims to stay distinct (CIE76 ΔE of 20 or more) from every other team's, through the
77
+ * shared team palette, as `teamColorPalette` does in the other adapters (when it cannot, it keeps the readable colour
78
+ * and warns once); a headshot node's name takes the ink that reads on it.
79
+ */
80
+ background?: string;
81
+ /** The colour of an element whose team does not resolve, or has no colour. Default "#808080". */
82
+ fallback?: string;
83
+ /** A node's diameter in px, 1 or more. Default 40. */
84
+ nodeSize?: number;
85
+ }
86
+ /** `teamStylesheet` options. */
87
+ interface TeamStylesheetOptions extends CyColorOptions {
88
+ /** The league the teams belong to, e.g. "soccer". */
89
+ league: League;
90
+ /** The teams to colour, as the elements name them before resolving (see `idSystem`). */
91
+ teams: readonly unknown[];
92
+ }
93
+ /** `passNetwork` options. */
94
+ interface PassNetworkOptions extends CyColorOptions {
95
+ /** Name of the row column holding the passer's player id. */
96
+ from: string;
97
+ /** Name of the row column holding the receiver's player id. */
98
+ to: string;
99
+ /** Name of the row column holding the passing team (abbreviation, name or id; see `idSystem`). */
100
+ team: string;
101
+ /** The league the teams belong to, e.g. "soccer"; also the league of the headshots. */
102
+ league: League;
103
+ /** Name of the row column holding the passer's name (the node's label and initials). Default: the id. */
104
+ fromName?: string;
105
+ /** Name of the row column holding the receiver's name. Default: the id. */
106
+ toName?: string;
107
+ /** Name of a row column summed per pair in place of counting rows. */
108
+ weight?: string;
109
+ /**
110
+ * Given, pairs whose weight (passes, or the summed `weight` column) is smaller are left out; every player stays.
111
+ * Default: none, so no pair is left out (a fractional weight such as xT keeps every pair). 2 or 3 is the usual
112
+ * threshold for a pass map.
113
+ */
114
+ minWeight?: number;
115
+ /** With `y`, `xend` and `yend`: the row columns of each pass's start (x, y) and end; all four or none. */
116
+ x?: string;
117
+ /** The pass start's y (up, as on a plot). */
118
+ y?: string;
119
+ /** The pass end's x. */
120
+ xend?: string;
121
+ /** The pass end's y. */
122
+ yend?: string;
123
+ /** Model px per data unit of x and y, more than 0. Default 10 (a 105 m pitch is 1,050 px across at zoom 1). */
124
+ scale?: number;
125
+ /**
126
+ * Which ids `from` and `to` hold, for headshots: "espn" (default; ESPN athlete ids) or "gsis" (nflverse; needs
127
+ * `loadGsis()` first). `false` draws every player as initials and warns about none. A player with no headshot (a
128
+ * league without ESPN headshots, such as soccer, or an id that does not resolve) is drawn as initials, with one
129
+ * warning naming them.
130
+ */
131
+ headshots?: "espn" | "gsis" | false;
132
+ /** The heaviest pair's edge width in px, 1 or more. Default 10. */
133
+ edgeWidth?: number;
134
+ }
135
+ /**
136
+ * A Cytoscape stylesheet that draws elements in team colours: a node whose `data.team` is a team's id is filled with
137
+ * its colour (a headshot node shows the colour as its border), its initials in the ink that reads on it, and an edge
138
+ * with that `data.team` is stroked in it, arrow included. The base blocks draw `passNetwork`'s elements: node size,
139
+ * headshot nodes (`data.image` as the background, the name below), initials nodes, directed bezier edges as wide as
140
+ * `data.width` px. Elements whose team does not resolve take `fallback`. `data.team` holds the resolved team id, as
141
+ * `passNetwork` writes it; for your own elements, resolve the ids first (or pass `idSystem: "team_id"`).
142
+ *
143
+ * @example
144
+ * ```ts
145
+ * import { loadLeague } from "@sportsdataverse/sdvplot";
146
+ * import { teamStylesheet } from "@sportsdataverse/sdvplot/cytoscape";
147
+ *
148
+ * await loadLeague("nfl");
149
+ * const style = teamStylesheet({ league: "nfl", teams: ["KC", "PHI"], background: "#181a1b" });
150
+ * // in the browser: cytoscape({ container, elements, style })
151
+ * style.length;
152
+ * ```
153
+ */
154
+ declare function teamStylesheet(o: TeamStylesheetOptions): StyleBlock[];
155
+ /**
156
+ * A passing network as Cytoscape elements and stylesheet: one node per player, one directed edge per passer to
157
+ * receiver pair, weighted by its passes (a row is a pass; `weight` sums a column instead) and as wide as that weight
158
+ * (`edgeWidth` px for the heaviest pair, the others in proportion), stroked in the passing team's colour with an arrow at
159
+ * the receiver. A player whose headshot resolves is drawn as it (`background-image`), with the name below; any other is
160
+ * a node in the team's colour with the name's initials, the clean fallback, with one warning naming those players
161
+ * (soccer has no headshots, so every soccer player is this node; `headshots: false` asks for none). With x, y, xend and
162
+ * yend, each node sits at the mean of where the player passed from and received (y up, flipped for Cytoscape's y-down
163
+ * model, `scale` px per unit): draw it with the `preset` layout. Without them, use a layout such as `cose`. A row with
164
+ * no passer, no receiver or a missing coordinate is left out with one warning. The stylesheet is `teamStylesheet`'s.
165
+ *
166
+ * @example
167
+ * ```ts
168
+ * import { loadLeague } from "@sportsdataverse/sdvplot";
169
+ * import { passNetwork } from "@sportsdataverse/sdvplot/cytoscape";
170
+ *
171
+ * await loadLeague("nfl");
172
+ * // Patrick Mahomes to Xavier Worthy, a Super Bowl LIX touchdown (ESPN athlete ids)
173
+ * const pass = { from: 3139477, from_name: "Patrick Mahomes", to: 4683062, to_name: "Xavier Worthy", team: "KC" };
174
+ * const { elements, style } = passNetwork([pass], {
175
+ * from: "from",
176
+ * to: "to",
177
+ * fromName: "from_name",
178
+ * toName: "to_name",
179
+ * team: "team",
180
+ * league: "nfl",
181
+ * });
182
+ * // in the browser: cytoscape({ container, elements, style, layout: { name: "cose" } })
183
+ * elements.nodes[0]?.data.image;
184
+ * ```
185
+ */
186
+ declare function passNetwork(rows: readonly Row[], o: PassNetworkOptions): PassNetwork;
187
+
188
+ export { type CyColorOptions, type CyEdge, type CyNode, type PassNetwork, type PassNetworkOptions, Row, type StyleBlock, type TeamStylesheetOptions, passNetwork, teamStylesheet };
@@ -0,0 +1,219 @@
1
+ import { colorList, teamPalette, column } from './chunk-UL5M6KDE.js';
2
+ import './chunk-7ULOCCFL.js';
3
+ import { resolveSync, ESPN_HEADSHOT_LEAGUES, headshotUrl } from './chunk-ZAEQXFJM.js';
4
+ import { hex6, onColor } from './chunk-J6R2LEJ7.js';
5
+ import { InputError, warn } from './chunk-E7G7VSLV.js';
6
+
7
+ // src/cytoscape.ts
8
+ var real = (v) => typeof v === "number" && Number.isFinite(v);
9
+ function checkPx(name, v) {
10
+ if (!real(v) || v < 1)
11
+ throw new InputError(
12
+ `${name} is in px (Cytoscape style units), 1 or more, not a fraction; got ${String(v)}`
13
+ );
14
+ return v;
15
+ }
16
+ function playerId(v) {
17
+ if (v === null || v === void 0 || typeof v === "number" && !Number.isFinite(v)) return null;
18
+ const s = String(v).trim();
19
+ return s === "" ? null : s;
20
+ }
21
+ function initialsOf(name) {
22
+ const words = name.split(/\s+/).filter((w) => w !== "");
23
+ return words.filter((_, i) => i === 0 || i === words.length - 1).map((w) => Array.from(w)[0].toUpperCase()).join("");
24
+ }
25
+ var shownList = (v) => v.slice(0, 10).map(String).join(", ") + (v.length > 10 ? ` and ${v.length - 10} more` : "");
26
+ function teamStylesheet(o) {
27
+ const size = checkPx("nodeSize", o.nodeSize ?? 40);
28
+ const fallback = hex6(o.fallback ?? "#808080", { dropAlpha: true });
29
+ const ink = onColor(hex6(o.background ?? "#ffffff"));
30
+ const ro = {
31
+ ...o.season === void 0 ? {} : { season: o.season },
32
+ ...o.idSystem === void 0 ? {} : { idSystem: o.idSystem }
33
+ };
34
+ const ids = [];
35
+ for (const id of resolveSync(o.teams, o.league, ro))
36
+ if (id !== void 0 && !ids.includes(id)) ids.push(id);
37
+ const which = o.which ?? "primary";
38
+ const co = { idSystem: "team_id", ...o.season === void 0 ? {} : { season: o.season } };
39
+ const main = colorList(o.league, ids, { ...co, which });
40
+ const other = colorList(o.league, ids, { ...co, which: which === "primary" ? "secondary" : "primary" });
41
+ let colors = main.map((c, i) => hex6(c ?? other[i] ?? fallback, { dropAlpha: true }));
42
+ if (o.background !== void 0) colors = teamPalette(colors, other, hex6(o.background), ids);
43
+ const base = [
44
+ {
45
+ selector: "node",
46
+ style: {
47
+ width: size,
48
+ height: size,
49
+ "background-color": fallback,
50
+ "border-width": 3,
51
+ "border-color": fallback,
52
+ "font-size": 12
53
+ }
54
+ },
55
+ {
56
+ selector: "node.initials",
57
+ style: {
58
+ label: "data(initials)",
59
+ "text-valign": "center",
60
+ "text-halign": "center",
61
+ "font-size": Math.max(8, Math.round(size * 0.36)),
62
+ "font-weight": "bold",
63
+ color: onColor(fallback)
64
+ }
65
+ },
66
+ {
67
+ selector: "node.headshot",
68
+ style: {
69
+ "background-image": "data(image)",
70
+ "background-fit": "cover",
71
+ "background-clip": "node",
72
+ label: "data(label)",
73
+ "text-valign": "bottom",
74
+ "text-halign": "center",
75
+ "text-margin-y": 4,
76
+ color: ink
77
+ }
78
+ },
79
+ {
80
+ selector: "edge",
81
+ style: {
82
+ "curve-style": "bezier",
83
+ "target-arrow-shape": "triangle",
84
+ "line-color": fallback,
85
+ "target-arrow-color": fallback
86
+ }
87
+ },
88
+ { selector: "edge[width]", style: { width: "data(width)" } }
89
+ ];
90
+ const teams = ids.flatMap((id, i) => {
91
+ const c = colors[i];
92
+ const at = `[team = ${JSON.stringify(id)}]`;
93
+ return [
94
+ { selector: `node${at}`, style: { "background-color": c, "border-color": c } },
95
+ { selector: `node.initials${at}`, style: { color: onColor(c) } },
96
+ { selector: `edge${at}`, style: { "line-color": c, "target-arrow-color": c } }
97
+ ];
98
+ });
99
+ return [...base, ...teams];
100
+ }
101
+ function passNetwork(rows, o) {
102
+ const edgeWidth = checkPx("edgeWidth", o.edgeWidth ?? 10);
103
+ checkPx("nodeSize", o.nodeSize ?? 40);
104
+ const minWeight = o.minWeight;
105
+ if (minWeight !== void 0 && (!real(minWeight) || minWeight < 0))
106
+ throw new InputError(`minWeight is 0 or more, got ${String(minWeight)}`);
107
+ const at = [o.x, o.y, o.xend, o.yend];
108
+ const placed = at.every((c) => c !== void 0);
109
+ if (!placed && at.some((c) => c !== void 0))
110
+ throw new InputError("x, y, xend and yend place the players together: give all four or none");
111
+ const scale = o.scale ?? 10;
112
+ if (!real(scale) || scale <= 0)
113
+ throw new InputError(`scale is px per data unit, more than 0, got ${String(scale)}`);
114
+ const from = column(rows, o.from, "from");
115
+ const to = column(rows, o.to, "to");
116
+ const teams = column(rows, o.team, "team");
117
+ const names = (c, what) => c === void 0 ? void 0 : column(rows, c, what);
118
+ const fromName = names(o.fromName, "fromName");
119
+ const toName = names(o.toName, "toName");
120
+ const weights = o.weight === void 0 ? void 0 : column(rows, o.weight, "weight");
121
+ const xy = placed ? at.map((c, k) => column(rows, c, ["x", "y", "xend", "yend"][k])) : [];
122
+ const ro = {
123
+ ...o.season === void 0 ? {} : { season: o.season },
124
+ ...o.idSystem === void 0 ? {} : { idSystem: o.idSystem }
125
+ };
126
+ const resolved = resolveSync(teams, o.league, ro);
127
+ const players = /* @__PURE__ */ new Map();
128
+ const pairs = /* @__PURE__ */ new Map();
129
+ const skipped = [];
130
+ const meet = (id, name, team, x, y) => {
131
+ let p = players.get(id);
132
+ if (p === void 0) {
133
+ const n = name === null || name === void 0 ? "" : String(name).trim();
134
+ p = { label: n === "" ? id : n, team, xs: [], ys: [] };
135
+ players.set(id, p);
136
+ }
137
+ if (x !== void 0 && y !== void 0) {
138
+ p.xs.push(x);
139
+ p.ys.push(y);
140
+ }
141
+ };
142
+ rows.forEach((_, i) => {
143
+ const a = playerId(from[i]);
144
+ const b = playerId(to[i]);
145
+ const c = xy.map((col) => col[i]);
146
+ const w = weights === void 0 ? 1 : weights[i];
147
+ if (a === null || b === null || !c.every(real) || !real(w)) {
148
+ skipped.push(a ?? b ?? i);
149
+ return;
150
+ }
151
+ const team = resolved[i];
152
+ const [x0, y0, x1, y1] = c;
153
+ meet(a, fromName?.[i], team, x0, y0);
154
+ meet(b, toName?.[i], team, x1, y1);
155
+ const key = `${a}->${b}`;
156
+ const pair = pairs.get(key) ?? { source: a, target: b, weight: 0, team };
157
+ pair.weight += w;
158
+ pairs.set(key, pair);
159
+ });
160
+ if (skipped.length > 0)
161
+ warn(
162
+ `cytoscape:passes:${o.league}:${shownList(skipped)}`,
163
+ `skipped ${skipped.length} pass(es) with no passer, no receiver, or a missing coordinate or weight: ${shownList(skipped)}`
164
+ );
165
+ const hl = o.headshots === false || !Object.hasOwn(ESPN_HEADSHOT_LEAGUES, o.league) ? void 0 : o.league;
166
+ const noImage = [];
167
+ const mean = (v) => v.reduce((s, x) => s + x, 0) / v.length;
168
+ const nodes = [...players].map(([id, p]) => {
169
+ const image = hl === void 0 ? void 0 : headshotUrl(id, hl, { idSystem: o.headshots === "gsis" ? "gsis" : "espn" });
170
+ if (image === void 0 && o.headshots !== false) noImage.push(p.label);
171
+ return {
172
+ data: {
173
+ id,
174
+ label: p.label,
175
+ initials: p.label === id ? id : initialsOf(p.label),
176
+ ...p.team === void 0 ? {} : { team: p.team },
177
+ ...image === void 0 ? {} : { image }
178
+ },
179
+ ...placed ? { position: { x: scale * mean(p.xs), y: -scale * mean(p.ys) } } : {},
180
+ classes: image === void 0 ? "initials" : "headshot"
181
+ };
182
+ });
183
+ if (noImage.length > 0)
184
+ warn(
185
+ `cytoscape:headshots:${o.league}:${shownList(noImage)}`,
186
+ `drew ${noImage.length} player(s) with no headshot as team-coloured initials: ${shownList(noImage)}`
187
+ );
188
+ const kept = [...pairs.values()].filter((p) => minWeight === void 0 || p.weight >= minWeight);
189
+ const heaviest = Math.max(0, ...kept.map((p) => p.weight));
190
+ const edges = kept.map(
191
+ (p) => ({
192
+ data: {
193
+ id: `${p.source}->${p.target}`,
194
+ source: p.source,
195
+ target: p.target,
196
+ weight: p.weight,
197
+ // a pair whose summed weight is 0 or less (xT can be negative) is 0 px wide: Cytoscape refuses a negative width
198
+ width: heaviest > 0 ? edgeWidth * Math.max(0, p.weight) / heaviest : edgeWidth,
199
+ ...p.team === void 0 ? {} : { team: p.team }
200
+ }
201
+ })
202
+ );
203
+ const colors = Object.fromEntries(
204
+ ["season", "which", "background", "fallback", "nodeSize"].flatMap(
205
+ (k) => o[k] === void 0 ? [] : [[k, o[k]]]
206
+ )
207
+ );
208
+ const style = teamStylesheet({
209
+ ...colors,
210
+ league: o.league,
211
+ teams: [...new Set(resolved)].filter((t) => t !== void 0),
212
+ idSystem: "team_id"
213
+ });
214
+ return { elements: { nodes, edges }, style };
215
+ }
216
+
217
+ export { passNetwork, teamStylesheet };
218
+ //# sourceMappingURL=cytoscape.js.map
219
+ //# sourceMappingURL=cytoscape.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cytoscape.ts"],"names":[],"mappings":";;;;;;;AAwIA,IAAM,IAAA,GAAO,CAAC,CAAA,KAA4B,OAAO,MAAM,QAAA,IAAY,MAAA,CAAO,SAAS,CAAC,CAAA;AACpF,SAAS,OAAA,CAAQ,MAAc,CAAA,EAAoB;AACjD,EAAA,IAAI,CAAC,IAAA,CAAK,CAAC,CAAA,IAAK,CAAA,GAAI,CAAA;AAClB,IAAA,MAAM,IAAI,UAAA;AAAA,MACR,CAAA,EAAG,IAAI,CAAA,kEAAA,EAAqE,MAAA,CAAO,CAAC,CAAC,CAAA;AAAA,KACvF;AACF,EAAA,OAAO,CAAA;AACT;AAEA,SAAS,SAAS,CAAA,EAA2B;AAC3C,EAAA,IAAI,CAAA,KAAM,IAAA,IAAQ,CAAA,KAAM,MAAA,IAAc,OAAO,CAAA,KAAM,QAAA,IAAY,CAAC,MAAA,CAAO,QAAA,CAAS,CAAC,CAAA,EAAI,OAAO,IAAA;AAC5F,EAAA,MAAM,CAAA,GAAI,MAAA,CAAO,CAAC,CAAA,CAAE,IAAA,EAAK;AACzB,EAAA,OAAO,CAAA,KAAM,KAAK,IAAA,GAAO,CAAA;AAC3B;AAEA,SAAS,WAAW,IAAA,EAAsB;AACxC,EAAA,MAAM,KAAA,GAAQ,KAAK,KAAA,CAAM,KAAK,EAAE,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,KAAM,EAAE,CAAA;AACtD,EAAA,OAAO,KAAA,CACJ,MAAA,CAAO,CAAC,CAAA,EAAG,CAAA,KAAM,MAAM,CAAA,IAAK,CAAA,KAAM,KAAA,CAAM,MAAA,GAAS,CAAC,CAAA,CAClD,IAAI,CAAC,CAAA,KAAO,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,CAAE,CAAC,CAAA,CAAa,WAAA,EAAa,CAAA,CACrD,IAAA,CAAK,EAAE,CAAA;AACZ;AACA,IAAM,SAAA,GAAY,CAAC,CAAA,KACjB,CAAA,CAAE,MAAM,CAAA,EAAG,EAAE,EAAE,GAAA,CAAI,MAAM,EAAE,IAAA,CAAK,IAAI,KAAK,CAAA,CAAE,MAAA,GAAS,KAAK,CAAA,KAAA,EAAQ,CAAA,CAAE,MAAA,GAAS,EAAE,CAAA,KAAA,CAAA,GAAU,EAAA,CAAA;AAqBnF,SAAS,eAAe,CAAA,EAAwC;AACrE,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,UAAA,EAAY,CAAA,CAAE,YAAY,EAAE,CAAA;AACjD,EAAA,MAAM,QAAA,GAAW,KAAK,CAAA,CAAE,QAAA,IAAY,WAAW,EAAE,SAAA,EAAW,MAAM,CAAA;AAClE,EAAA,MAAM,MAAM,OAAA,CAAQ,IAAA,CAAK,CAAA,CAAE,UAAA,IAAc,SAAS,CAAC,CAAA;AACnD,EAAA,MAAM,EAAA,GAAK;AAAA,IACT,GAAI,EAAE,MAAA,KAAW,MAAA,GAAY,EAAC,GAAI,EAAE,MAAA,EAAQ,CAAA,CAAE,MAAA,EAAO;AAAA,IACrD,GAAI,EAAE,QAAA,KAAa,MAAA,GAAY,EAAC,GAAI,EAAE,QAAA,EAAU,CAAA,CAAE,QAAA;AAAS,GAC7D;AAGA,EAAA,MAAM,MAAgB,EAAC;AACvB,EAAA,KAAA,MAAW,MAAM,WAAA,CAAY,CAAA,CAAE,KAAA,EAA2B,CAAA,CAAE,QAAQ,EAAE,CAAA;AACpE,IAAA,IAAI,EAAA,KAAO,UAAa,CAAC,GAAA,CAAI,SAAS,EAAE,CAAA,EAAG,GAAA,CAAI,IAAA,CAAK,EAAE,CAAA;AACxD,EAAA,MAAM,KAAA,GAAQ,EAAE,KAAA,IAAS,SAAA;AACzB,EAAA,MAAM,EAAA,GAAK,EAAE,QAAA,EAAU,SAAA,EAAoB,GAAI,CAAA,CAAE,MAAA,KAAW,MAAA,GAAY,EAAC,GAAI,EAAE,MAAA,EAAQ,CAAA,CAAE,QAAO,EAAG;AACnG,EAAA,MAAM,IAAA,GAAO,UAAU,CAAA,CAAE,MAAA,EAAQ,KAAK,EAAE,GAAG,EAAA,EAAI,KAAA,EAAO,CAAA;AACtD,EAAA,MAAM,KAAA,GAAQ,SAAA,CAAU,CAAA,CAAE,MAAA,EAAQ,GAAA,EAAK,EAAE,GAAG,EAAA,EAAI,KAAA,EAAO,KAAA,KAAU,SAAA,GAAY,WAAA,GAAc,WAAW,CAAA;AACtG,EAAA,IAAI,SAAS,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,EAAG,MAAM,IAAA,CAAK,CAAA,IAAK,KAAA,CAAM,CAAC,KAAK,QAAA,EAAU,EAAE,SAAA,EAAW,IAAA,EAAM,CAAC,CAAA;AACpF,EAAA,IAAI,CAAA,CAAE,UAAA,KAAe,MAAA,EAAW,MAAA,GAAS,WAAA,CAAY,MAAA,EAAQ,KAAA,EAAO,IAAA,CAAK,CAAA,CAAE,UAAU,CAAA,EAAG,GAAG,CAAA;AAC3F,EAAA,MAAM,IAAA,GAAqB;AAAA,IACzB;AAAA,MACE,QAAA,EAAU,MAAA;AAAA,MACV,KAAA,EAAO;AAAA,QACL,KAAA,EAAO,IAAA;AAAA,QACP,MAAA,EAAQ,IAAA;AAAA,QACR,kBAAA,EAAoB,QAAA;AAAA,QACpB,cAAA,EAAgB,CAAA;AAAA,QAChB,cAAA,EAAgB,QAAA;AAAA,QAChB,WAAA,EAAa;AAAA;AACf,KACF;AAAA,IACA;AAAA,MACE,QAAA,EAAU,eAAA;AAAA,MACV,KAAA,EAAO;AAAA,QACL,KAAA,EAAO,gBAAA;AAAA,QACP,aAAA,EAAe,QAAA;AAAA,QACf,aAAA,EAAe,QAAA;AAAA,QACf,WAAA,EAAa,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,KAAA,CAAM,IAAA,GAAO,IAAI,CAAC,CAAA;AAAA,QAChD,aAAA,EAAe,MAAA;AAAA,QACf,KAAA,EAAO,QAAQ,QAAQ;AAAA;AACzB,KACF;AAAA,IACA;AAAA,MACE,QAAA,EAAU,eAAA;AAAA,MACV,KAAA,EAAO;AAAA,QACL,kBAAA,EAAoB,aAAA;AAAA,QACpB,gBAAA,EAAkB,OAAA;AAAA,QAClB,iBAAA,EAAmB,MAAA;AAAA,QACnB,KAAA,EAAO,aAAA;AAAA,QACP,aAAA,EAAe,QAAA;AAAA,QACf,aAAA,EAAe,QAAA;AAAA,QACf,eAAA,EAAiB,CAAA;AAAA,QACjB,KAAA,EAAO;AAAA;AACT,KACF;AAAA,IACA;AAAA,MACE,QAAA,EAAU,MAAA;AAAA,MACV,KAAA,EAAO;AAAA,QACL,aAAA,EAAe,QAAA;AAAA,QACf,oBAAA,EAAsB,UAAA;AAAA,QACtB,YAAA,EAAc,QAAA;AAAA,QACd,oBAAA,EAAsB;AAAA;AACxB,KACF;AAAA,IACA,EAAE,QAAA,EAAU,aAAA,EAAe,OAAO,EAAE,KAAA,EAAO,eAAc;AAAE,GAC7D;AACA,EAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,OAAA,CAAQ,CAAC,IAAI,CAAA,KAAoB;AACjD,IAAA,MAAM,CAAA,GAAI,OAAO,CAAC,CAAA;AAClB,IAAA,MAAM,EAAA,GAAK,CAAA,QAAA,EAAW,IAAA,CAAK,SAAA,CAAU,EAAE,CAAC,CAAA,CAAA,CAAA;AACxC,IAAA,OAAO;AAAA,MACL,EAAE,QAAA,EAAU,CAAA,IAAA,EAAO,EAAE,CAAA,CAAA,EAAI,KAAA,EAAO,EAAE,kBAAA,EAAoB,CAAA,EAAG,cAAA,EAAgB,CAAA,EAAE,EAAE;AAAA,MAC7E,EAAE,QAAA,EAAU,CAAA,aAAA,EAAgB,EAAE,CAAA,CAAA,EAAI,KAAA,EAAO,EAAE,KAAA,EAAO,OAAA,CAAQ,CAAC,CAAA,EAAE,EAAE;AAAA,MAC/D,EAAE,QAAA,EAAU,CAAA,IAAA,EAAO,EAAE,CAAA,CAAA,EAAI,KAAA,EAAO,EAAE,YAAA,EAAc,CAAA,EAAG,oBAAA,EAAsB,CAAA,EAAE;AAAE,KAC/E;AAAA,EACF,CAAC,CAAA;AACD,EAAA,OAAO,CAAC,GAAG,IAAA,EAAM,GAAG,KAAK,CAAA;AAC3B;AAiCO,SAAS,WAAA,CAAY,MAAsB,CAAA,EAAoC;AACpF,EAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,WAAA,EAAa,CAAA,CAAE,aAAa,EAAE,CAAA;AACxD,EAAA,OAAA,CAAQ,UAAA,EAAY,CAAA,CAAE,QAAA,IAAY,EAAE,CAAA;AACpC,EAAA,MAAM,YAAY,CAAA,CAAE,SAAA;AACpB,EAAA,IAAI,cAAc,MAAA,KAAc,CAAC,IAAA,CAAK,SAAS,KAAK,SAAA,GAAY,CAAA,CAAA;AAC9D,IAAA,MAAM,IAAI,UAAA,CAAW,CAAA,4BAAA,EAA+B,MAAA,CAAO,SAAS,CAAC,CAAA,CAAE,CAAA;AACzE,EAAA,MAAM,EAAA,GAAK,CAAC,CAAA,CAAE,CAAA,EAAG,EAAE,CAAA,EAAG,CAAA,CAAE,IAAA,EAAM,CAAA,CAAE,IAAI,CAAA;AACpC,EAAA,MAAM,SAAS,EAAA,CAAG,KAAA,CAAM,CAAC,CAAA,KAAM,MAAM,MAAS,CAAA;AAC9C,EAAA,IAAI,CAAC,MAAA,IAAU,EAAA,CAAG,KAAK,CAAC,CAAA,KAAM,MAAM,MAAS,CAAA;AAC3C,IAAA,MAAM,IAAI,WAAW,uEAAuE,CAAA;AAC9F,EAAA,MAAM,KAAA,GAAQ,EAAE,KAAA,IAAS,EAAA;AACzB,EAAA,IAAI,CAAC,IAAA,CAAK,KAAK,CAAA,IAAK,KAAA,IAAS,CAAA;AAC3B,IAAA,MAAM,IAAI,UAAA,CAAW,CAAA,4CAAA,EAA+C,MAAA,CAAO,KAAK,CAAC,CAAA,CAAE,CAAA;AACrF,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,EAAM,CAAA,CAAE,MAAM,MAAM,CAAA;AACxC,EAAA,MAAM,EAAA,GAAK,MAAA,CAAO,IAAA,EAAM,CAAA,CAAE,IAAI,IAAI,CAAA;AAClC,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,IAAA,EAAM,CAAA,CAAE,MAAM,MAAM,CAAA;AACzC,EAAA,MAAM,KAAA,GAAQ,CAAC,CAAA,EAAuB,IAAA,KACpC,CAAA,KAAM,SAAY,MAAA,GAAY,MAAA,CAAO,IAAA,EAAM,CAAA,EAAG,IAAI,CAAA;AACpD,EAAA,MAAM,QAAA,GAAW,KAAA,CAAM,CAAA,CAAE,QAAA,EAAU,UAAU,CAAA;AAC7C,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,CAAA,CAAE,MAAA,EAAQ,QAAQ,CAAA;AACvC,EAAA,MAAM,OAAA,GAAU,EAAE,MAAA,KAAW,MAAA,GAAY,SAAY,MAAA,CAAO,IAAA,EAAM,CAAA,CAAE,MAAA,EAAQ,QAAQ,CAAA;AACpF,EAAA,MAAM,EAAA,GAAK,SACP,EAAA,CAAG,GAAA,CAAI,CAAC,CAAA,EAAG,CAAA,KAAM,OAAO,IAAA,EAAM,CAAA,EAAa,CAAC,GAAA,EAAK,GAAA,EAAK,QAAQ,MAAM,CAAA,CAAE,CAAC,CAAW,CAAC,IACnF,EAAC;AACL,EAAA,MAAM,EAAA,GAAK;AAAA,IACT,GAAI,EAAE,MAAA,KAAW,MAAA,GAAY,EAAC,GAAI,EAAE,MAAA,EAAQ,CAAA,CAAE,MAAA,EAAO;AAAA,IACrD,GAAI,EAAE,QAAA,KAAa,MAAA,GAAY,EAAC,GAAI,EAAE,QAAA,EAAU,CAAA,CAAE,QAAA;AAAS,GAC7D;AACA,EAAA,MAAM,QAAA,GAAW,WAAA,CAAY,KAAA,EAAO,CAAA,CAAE,QAAQ,EAAE,CAAA;AAGhD,EAAA,MAAM,OAAA,uBAAc,GAAA,EAAoB;AACxC,EAAA,MAAM,KAAA,uBAAY,GAAA,EAGhB;AACF,EAAA,MAAM,UAAmB,EAAC;AAC1B,EAAA,MAAM,OAAO,CAAC,EAAA,EAAY,IAAA,EAAe,IAAA,EAA0B,GAAY,CAAA,KAAqB;AAClG,IAAA,IAAI,CAAA,GAAI,OAAA,CAAQ,GAAA,CAAI,EAAE,CAAA;AACtB,IAAA,IAAI,MAAM,MAAA,EAAW;AACnB,MAAA,MAAM,CAAA,GAAI,SAAS,IAAA,IAAQ,IAAA,KAAS,SAAY,EAAA,GAAK,MAAA,CAAO,IAAI,CAAA,CAAE,IAAA,EAAK;AACvE,MAAA,CAAA,GAAI,EAAE,KAAA,EAAO,CAAA,KAAM,EAAA,GAAK,EAAA,GAAK,CAAA,EAAG,IAAA,EAAM,EAAA,EAAI,EAAC,EAAG,EAAA,EAAI,EAAC,EAAE;AACrD,MAAA,OAAA,CAAQ,GAAA,CAAI,IAAI,CAAC,CAAA;AAAA,IACnB;AACA,IAAA,IAAI,CAAA,KAAM,MAAA,IAAa,CAAA,KAAM,MAAA,EAAW;AACtC,MAAA,CAAA,CAAE,EAAA,CAAG,KAAK,CAAC,CAAA;AACX,MAAA,CAAA,CAAE,EAAA,CAAG,KAAK,CAAC,CAAA;AAAA,IACb;AAAA,EACF,CAAA;AACA,EAAA,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAA,EAAG,CAAA,KAAM;AACrB,IAAA,MAAM,CAAA,GAAI,QAAA,CAAS,IAAA,CAAK,CAAC,CAAC,CAAA;AAC1B,IAAA,MAAM,CAAA,GAAI,QAAA,CAAS,EAAA,CAAG,CAAC,CAAC,CAAA;AACxB,IAAA,MAAM,IAAI,EAAA,CAAG,GAAA,CAAI,CAAC,GAAA,KAAQ,GAAA,CAAI,CAAC,CAAC,CAAA;AAChC,IAAA,MAAM,CAAA,GAAI,OAAA,KAAY,MAAA,GAAY,CAAA,GAAI,QAAQ,CAAC,CAAA;AAC/C,IAAA,IAAI,CAAA,KAAM,IAAA,IAAQ,CAAA,KAAM,IAAA,IAAQ,CAAC,CAAA,CAAE,KAAA,CAAM,IAAI,CAAA,IAAK,CAAC,IAAA,CAAK,CAAC,CAAA,EAAG;AAC1D,MAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,IAAK,CAAA,IAAK,CAAC,CAAA;AACxB,MAAA;AAAA,IACF;AACA,IAAA,MAAM,IAAA,GAAO,SAAS,CAAC,CAAA;AACvB,IAAA,MAAM,CAAC,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,EAAE,CAAA,GAAI,CAAA;AACzB,IAAA,IAAA,CAAK,GAAG,QAAA,GAAW,CAAC,CAAA,EAAG,IAAA,EAAM,IAAI,EAAE,CAAA;AACnC,IAAA,IAAA,CAAK,GAAG,MAAA,GAAS,CAAC,CAAA,EAAG,IAAA,EAAM,IAAI,EAAE,CAAA;AACjC,IAAA,MAAM,GAAA,GAAM,CAAA,EAAG,CAAC,CAAA,EAAA,EAAK,CAAC,CAAA,CAAA;AACtB,IAAA,MAAM,IAAA,GAAO,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA,IAAK,EAAE,MAAA,EAAQ,CAAA,EAAG,MAAA,EAAQ,CAAA,EAAG,MAAA,EAAQ,CAAA,EAAG,IAAA,EAAK;AACvE,IAAA,IAAA,CAAK,MAAA,IAAU,CAAA;AACf,IAAA,KAAA,CAAM,GAAA,CAAI,KAAK,IAAI,CAAA;AAAA,EACrB,CAAC,CAAA;AACD,EAAA,IAAI,QAAQ,MAAA,GAAS,CAAA;AACnB,IAAA,IAAA;AAAA,MACE,oBAAoB,CAAA,CAAE,MAAM,CAAA,CAAA,EAAI,SAAA,CAAU,OAAO,CAAC,CAAA,CAAA;AAAA,MAClD,WAAW,OAAA,CAAQ,MAAM,CAAA,0EAAA,EAA6E,SAAA,CAAU,OAAO,CAAC,CAAA;AAAA,KAC1H;AAEF,EAAA,MAAM,EAAA,GAAK,CAAA,CAAE,SAAA,KAAc,KAAA,IAAS,CAAC,MAAA,CAAO,MAAA,CAAO,qBAAA,EAAuB,CAAA,CAAE,MAAM,CAAA,GAAI,MAAA,GAAY,CAAA,CAAE,MAAA;AACpG,EAAA,MAAM,UAAoB,EAAC;AAC3B,EAAA,MAAM,IAAA,GAAO,CAAC,CAAA,KAAiC,CAAA,CAAE,MAAA,CAAO,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,GAAI,CAAA,EAAG,CAAC,CAAA,GAAI,CAAA,CAAE,MAAA;AAChF,EAAA,MAAM,KAAA,GAAQ,CAAC,GAAG,OAAO,CAAA,CAAE,IAAI,CAAC,CAAC,EAAA,EAAI,CAAC,CAAA,KAAc;AAClD,IAAA,MAAM,KAAA,GACJ,EAAA,KAAO,MAAA,GACH,MAAA,GACA,YAAY,EAAA,EAAI,EAAA,EAA0B,EAAE,QAAA,EAAU,CAAA,CAAE,SAAA,KAAc,MAAA,GAAS,MAAA,GAAS,QAAQ,CAAA;AACtG,IAAA,IAAI,KAAA,KAAU,UAAa,CAAA,CAAE,SAAA,KAAc,OAAO,OAAA,CAAQ,IAAA,CAAK,EAAE,KAAK,CAAA;AACtE,IAAA,OAAO;AAAA,MACL,IAAA,EAAM;AAAA,QACJ,EAAA;AAAA,QACA,OAAO,CAAA,CAAE,KAAA;AAAA,QACT,UAAU,CAAA,CAAE,KAAA,KAAU,KAAK,EAAA,GAAK,UAAA,CAAW,EAAE,KAAK,CAAA;AAAA,QAClD,GAAI,EAAE,IAAA,KAAS,MAAA,GAAY,EAAC,GAAI,EAAE,IAAA,EAAM,CAAA,CAAE,IAAA,EAAK;AAAA,QAC/C,GAAI,KAAA,KAAU,MAAA,GAAY,EAAC,GAAI,EAAE,KAAA;AAAM,OACzC;AAAA,MACA,GAAI,SAAS,EAAE,QAAA,EAAU,EAAE,CAAA,EAAG,KAAA,GAAQ,KAAK,CAAA,CAAE,EAAE,GAAG,CAAA,EAAG,CAAC,QAAQ,IAAA,CAAK,CAAA,CAAE,EAAE,CAAA,EAAE,KAAM,EAAC;AAAA,MAChF,OAAA,EAAS,KAAA,KAAU,MAAA,GAAY,UAAA,GAAa;AAAA,KAC9C;AAAA,EACF,CAAC,CAAA;AACD,EAAA,IAAI,QAAQ,MAAA,GAAS,CAAA;AACnB,IAAA,IAAA;AAAA,MACE,uBAAuB,CAAA,CAAE,MAAM,CAAA,CAAA,EAAI,SAAA,CAAU,OAAO,CAAC,CAAA,CAAA;AAAA,MACrD,QAAQ,OAAA,CAAQ,MAAM,CAAA,uDAAA,EAA0D,SAAA,CAAU,OAAO,CAAC,CAAA;AAAA,KACpG;AACF,EAAA,MAAM,IAAA,GAAO,CAAC,GAAG,KAAA,CAAM,QAAQ,CAAA,CAAE,MAAA,CAAO,CAAC,CAAA,KAAM,SAAA,KAAc,MAAA,IAAa,CAAA,CAAE,UAAU,SAAS,CAAA;AAC/F,EAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,GAAG,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,MAAM,CAAC,CAAA;AACzD,EAAA,MAAM,QAAQ,IAAA,CAAK,GAAA;AAAA,IACjB,CAAC,CAAA,MAAe;AAAA,MACd,IAAA,EAAM;AAAA,QACJ,IAAI,CAAA,EAAG,CAAA,CAAE,MAAM,CAAA,EAAA,EAAK,EAAE,MAAM,CAAA,CAAA;AAAA,QAC5B,QAAQ,CAAA,CAAE,MAAA;AAAA,QACV,QAAQ,CAAA,CAAE,MAAA;AAAA,QACV,QAAQ,CAAA,CAAE,MAAA;AAAA;AAAA,QAEV,KAAA,EAAO,QAAA,GAAW,CAAA,GAAK,SAAA,GAAY,IAAA,CAAK,IAAI,CAAA,EAAG,CAAA,CAAE,MAAM,CAAA,GAAK,QAAA,GAAW,SAAA;AAAA,QACvE,GAAI,EAAE,IAAA,KAAS,MAAA,GAAY,EAAC,GAAI,EAAE,IAAA,EAAM,CAAA,CAAE,IAAA;AAAK;AACjD,KACF;AAAA,GACF;AACA,EAAA,MAAM,SAAS,MAAA,CAAO,WAAA;AAAA,IACnB,CAAC,QAAA,EAAU,OAAA,EAAS,YAAA,EAAc,UAAA,EAAY,UAAU,CAAA,CAAY,OAAA;AAAA,MAAQ,CAAC,CAAA,KAC5E,CAAA,CAAE,CAAC,MAAM,MAAA,GAAY,EAAC,GAAI,CAAC,CAAC,CAAA,EAAG,CAAA,CAAE,CAAC,CAAC,CAAC;AAAA;AACtC,GACF;AACA,EAAA,MAAM,QAAQ,cAAA,CAAe;AAAA,IAC3B,GAAG,MAAA;AAAA,IACH,QAAQ,CAAA,CAAE,MAAA;AAAA,IACV,KAAA,EAAO,CAAC,GAAG,IAAI,GAAA,CAAI,QAAQ,CAAC,CAAA,CAAE,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,KAAM,MAAS,CAAA;AAAA,IAC3D,QAAA,EAAU;AAAA,GACX,CAAA;AACD,EAAA,OAAO,EAAE,QAAA,EAAU,EAAE,KAAA,EAAO,KAAA,IAAS,KAAA,EAAM;AAC7C","file":"cytoscape.js","sourcesContent":["/**\n * The Cytoscape.js adapter: passing networks as Cytoscape elements and a team-coloured stylesheet. Pure: it never\n * imports cytoscape (3.34.3 or later is an optional peer), so the network can be built anywhere, a server included,\n * and handed to the library as plain JSON: `cytoscape({ container, elements, style, layout: { name: \"preset\" } })`.\n * Sizes are Cytoscape style units, px at zoom 1: a node's diameter, an edge's width. Synchronous: call after\n * `loadLeague(league)` / `preloadAll()` (and `loadGsis()` for gsis headshots).\n */\nimport { type Row, colorList, column } from \"./_web.js\";\nimport { hex6, onColor } from \"./contrast.js\";\nimport { InputError, warn } from \"./errors.js\";\nimport { ESPN_HEADSHOT_LEAGUES, type EspnHeadshotLeague, headshotUrl } from \"./headshots.js\";\nimport { type Value, resolveSync } from \"./resolve.js\";\nimport { teamPalette } from \"./team-palette.js\";\nimport type { IdSystem, League, SeasonInput, Which } from \"./types.js\";\n\nexport type { Row } from \"./_web.js\";\n\n/** A player node. `classes` is \"headshot\" (its image drawn as the node) or \"initials\" (team colour and initials). */\nexport interface CyNode {\n data: {\n /** The player id, as text. */\n id: string;\n /** The player's name, else the id. */\n label: string;\n /** The first letters of the name's first and last words, else the id. */\n initials: string;\n /** The resolved team id; absent when the team does not resolve (the node keeps the fallback colour). */\n team?: string;\n /** The headshot URL, on a headshot node only. */\n image?: string;\n };\n /** Where the player stood (with the x/y/xend/yend options): model px, y down. */\n position?: { x: number; y: number };\n classes: \"headshot\" | \"initials\";\n}\n/** A directed edge from passer to receiver. */\nexport interface CyEdge {\n data: {\n /** The source and target ids joined by an arrow, as in 5485-\\>3099 (Varane to Lloris). */\n id: string;\n /** The passer's node id. */\n source: string;\n /** The receiver's node id. */\n target: string;\n /** The pair's passes (or summed `weight`). */\n weight: number;\n /**\n * The edge's width in px: `edgeWidth` for the heaviest pair, the others in proportion to their weight; 0 for a pair\n * whose weight is 0 or less (a summed xT can be negative), since Cytoscape takes no negative width.\n */\n width: number;\n /** The passer's resolved team id; absent when it does not resolve. */\n team?: string;\n };\n}\n/** One stylesheet block, Cytoscape's `{ selector, style }` JSON. */\nexport interface StyleBlock {\n selector: string;\n style: Record<string, string | number>;\n}\n/** `passNetwork`'s result: what `cytoscape({ elements, style })` takes. */\nexport interface PassNetwork {\n elements: { nodes: CyNode[]; edges: CyEdge[] };\n style: StyleBlock[];\n}\n\n/** The colour options `teamStylesheet` and `passNetwork` share. */\nexport interface CyColorOptions {\n /** The season the teams are resolved for. */\n season?: SeasonInput;\n /** How the team values are read. Default \"auto\". */\n idSystem?: IdSystem;\n /** The team colour drawn, \"primary\" (default) or \"secondary\". */\n which?: Which;\n /**\n * The background the network is drawn on (Cytoscape's container). Given, every team colour keeps 3:1 contrast\n * against it (dark mode) and aims to stay distinct (CIE76 ΔE of 20 or more) from every other team's, through the\n * shared team palette, as `teamColorPalette` does in the other adapters (when it cannot, it keeps the readable colour\n * and warns once); a headshot node's name takes the ink that reads on it.\n */\n background?: string;\n /** The colour of an element whose team does not resolve, or has no colour. Default \"#808080\". */\n fallback?: string;\n /** A node's diameter in px, 1 or more. Default 40. */\n nodeSize?: number;\n}\n/** `teamStylesheet` options. */\nexport interface TeamStylesheetOptions extends CyColorOptions {\n /** The league the teams belong to, e.g. \"soccer\". */\n league: League;\n /** The teams to colour, as the elements name them before resolving (see `idSystem`). */\n teams: readonly unknown[];\n}\n/** `passNetwork` options. */\nexport interface PassNetworkOptions extends CyColorOptions {\n /** Name of the row column holding the passer's player id. */\n from: string;\n /** Name of the row column holding the receiver's player id. */\n to: string;\n /** Name of the row column holding the passing team (abbreviation, name or id; see `idSystem`). */\n team: string;\n /** The league the teams belong to, e.g. \"soccer\"; also the league of the headshots. */\n league: League;\n /** Name of the row column holding the passer's name (the node's label and initials). Default: the id. */\n fromName?: string;\n /** Name of the row column holding the receiver's name. Default: the id. */\n toName?: string;\n /** Name of a row column summed per pair in place of counting rows. */\n weight?: string;\n /**\n * Given, pairs whose weight (passes, or the summed `weight` column) is smaller are left out; every player stays.\n * Default: none, so no pair is left out (a fractional weight such as xT keeps every pair). 2 or 3 is the usual\n * threshold for a pass map.\n */\n minWeight?: number;\n /** With `y`, `xend` and `yend`: the row columns of each pass's start (x, y) and end; all four or none. */\n x?: string;\n /** The pass start's y (up, as on a plot). */\n y?: string;\n /** The pass end's x. */\n xend?: string;\n /** The pass end's y. */\n yend?: string;\n /** Model px per data unit of x and y, more than 0. Default 10 (a 105 m pitch is 1,050 px across at zoom 1). */\n scale?: number;\n /**\n * Which ids `from` and `to` hold, for headshots: \"espn\" (default; ESPN athlete ids) or \"gsis\" (nflverse; needs\n * `loadGsis()` first). `false` draws every player as initials and warns about none. A player with no headshot (a\n * league without ESPN headshots, such as soccer, or an id that does not resolve) is drawn as initials, with one\n * warning naming them.\n */\n headshots?: \"espn\" | \"gsis\" | false;\n /** The heaviest pair's edge width in px, 1 or more. Default 10. */\n edgeWidth?: number;\n}\n\nconst real = (v: unknown): v is number => typeof v === \"number\" && Number.isFinite(v);\nfunction checkPx(name: string, v: unknown): number {\n if (!real(v) || v < 1)\n throw new InputError(\n `${name} is in px (Cytoscape style units), 1 or more, not a fraction; got ${String(v)}`,\n );\n return v;\n}\n/** A player id as text: a number as JS prints it (no \".0\"), a string trimmed; missing or empty is null. */\nfunction playerId(v: unknown): string | null {\n if (v === null || v === undefined || (typeof v === \"number\" && !Number.isFinite(v))) return null;\n const s = String(v).trim();\n return s === \"\" ? null : s;\n}\n/** The first letters of a name's first and last words (\"Kylian Mbappé\" KM, \"N'Golo Kanté\" NK); one word: its first. */\nfunction initialsOf(name: string): string {\n const words = name.split(/\\s+/).filter((w) => w !== \"\");\n return words\n .filter((_, i) => i === 0 || i === words.length - 1)\n .map((w) => (Array.from(w)[0] as string).toUpperCase())\n .join(\"\");\n}\nconst shownList = (v: readonly unknown[]): string =>\n v.slice(0, 10).map(String).join(\", \") + (v.length > 10 ? ` and ${v.length - 10} more` : \"\");\n\n/**\n * A Cytoscape stylesheet that draws elements in team colours: a node whose `data.team` is a team's id is filled with\n * its colour (a headshot node shows the colour as its border), its initials in the ink that reads on it, and an edge\n * with that `data.team` is stroked in it, arrow included. The base blocks draw `passNetwork`'s elements: node size,\n * headshot nodes (`data.image` as the background, the name below), initials nodes, directed bezier edges as wide as\n * `data.width` px. Elements whose team does not resolve take `fallback`. `data.team` holds the resolved team id, as\n * `passNetwork` writes it; for your own elements, resolve the ids first (or pass `idSystem: \"team_id\"`).\n *\n * @example\n * ```ts\n * import { loadLeague } from \"@sportsdataverse/sdvplot\";\n * import { teamStylesheet } from \"@sportsdataverse/sdvplot/cytoscape\";\n *\n * await loadLeague(\"nfl\");\n * const style = teamStylesheet({ league: \"nfl\", teams: [\"KC\", \"PHI\"], background: \"#181a1b\" });\n * // in the browser: cytoscape({ container, elements, style })\n * style.length;\n * ```\n */\nexport function teamStylesheet(o: TeamStylesheetOptions): StyleBlock[] {\n const size = checkPx(\"nodeSize\", o.nodeSize ?? 40);\n const fallback = hex6(o.fallback ?? \"#808080\", { dropAlpha: true });\n const ink = onColor(hex6(o.background ?? \"#ffffff\"));\n const ro = {\n ...(o.season === undefined ? {} : { season: o.season }),\n ...(o.idSystem === undefined ? {} : { idSystem: o.idSystem }),\n };\n // one rule per distinct team, in order of first appearance; a team that does not resolve gets none, so it never\n // moves a real team's colour\n const ids: string[] = [];\n for (const id of resolveSync(o.teams as readonly Value[], o.league, ro))\n if (id !== undefined && !ids.includes(id)) ids.push(id);\n const which = o.which ?? \"primary\";\n const co = { idSystem: \"team_id\" as const, ...(o.season === undefined ? {} : { season: o.season }) };\n const main = colorList(o.league, ids, { ...co, which });\n const other = colorList(o.league, ids, { ...co, which: which === \"primary\" ? \"secondary\" : \"primary\" });\n let colors = main.map((c, i) => hex6(c ?? other[i] ?? fallback, { dropAlpha: true }));\n if (o.background !== undefined) colors = teamPalette(colors, other, hex6(o.background), ids);\n const base: StyleBlock[] = [\n {\n selector: \"node\",\n style: {\n width: size,\n height: size,\n \"background-color\": fallback,\n \"border-width\": 3,\n \"border-color\": fallback,\n \"font-size\": 12,\n },\n },\n {\n selector: \"node.initials\",\n style: {\n label: \"data(initials)\",\n \"text-valign\": \"center\",\n \"text-halign\": \"center\",\n \"font-size\": Math.max(8, Math.round(size * 0.36)),\n \"font-weight\": \"bold\",\n color: onColor(fallback),\n },\n },\n {\n selector: \"node.headshot\",\n style: {\n \"background-image\": \"data(image)\",\n \"background-fit\": \"cover\",\n \"background-clip\": \"node\",\n label: \"data(label)\",\n \"text-valign\": \"bottom\",\n \"text-halign\": \"center\",\n \"text-margin-y\": 4,\n color: ink,\n },\n },\n {\n selector: \"edge\",\n style: {\n \"curve-style\": \"bezier\",\n \"target-arrow-shape\": \"triangle\",\n \"line-color\": fallback,\n \"target-arrow-color\": fallback,\n },\n },\n { selector: \"edge[width]\", style: { width: \"data(width)\" } },\n ];\n const teams = ids.flatMap((id, i): StyleBlock[] => {\n const c = colors[i] as string;\n const at = `[team = ${JSON.stringify(id)}]`;\n return [\n { selector: `node${at}`, style: { \"background-color\": c, \"border-color\": c } },\n { selector: `node.initials${at}`, style: { color: onColor(c) } },\n { selector: `edge${at}`, style: { \"line-color\": c, \"target-arrow-color\": c } },\n ];\n });\n return [...base, ...teams];\n}\n\n/**\n * A passing network as Cytoscape elements and stylesheet: one node per player, one directed edge per passer to\n * receiver pair, weighted by its passes (a row is a pass; `weight` sums a column instead) and as wide as that weight\n * (`edgeWidth` px for the heaviest pair, the others in proportion), stroked in the passing team's colour with an arrow at\n * the receiver. A player whose headshot resolves is drawn as it (`background-image`), with the name below; any other is\n * a node in the team's colour with the name's initials, the clean fallback, with one warning naming those players\n * (soccer has no headshots, so every soccer player is this node; `headshots: false` asks for none). With x, y, xend and\n * yend, each node sits at the mean of where the player passed from and received (y up, flipped for Cytoscape's y-down\n * model, `scale` px per unit): draw it with the `preset` layout. Without them, use a layout such as `cose`. A row with\n * no passer, no receiver or a missing coordinate is left out with one warning. The stylesheet is `teamStylesheet`'s.\n *\n * @example\n * ```ts\n * import { loadLeague } from \"@sportsdataverse/sdvplot\";\n * import { passNetwork } from \"@sportsdataverse/sdvplot/cytoscape\";\n *\n * await loadLeague(\"nfl\");\n * // Patrick Mahomes to Xavier Worthy, a Super Bowl LIX touchdown (ESPN athlete ids)\n * const pass = { from: 3139477, from_name: \"Patrick Mahomes\", to: 4683062, to_name: \"Xavier Worthy\", team: \"KC\" };\n * const { elements, style } = passNetwork([pass], {\n * from: \"from\",\n * to: \"to\",\n * fromName: \"from_name\",\n * toName: \"to_name\",\n * team: \"team\",\n * league: \"nfl\",\n * });\n * // in the browser: cytoscape({ container, elements, style, layout: { name: \"cose\" } })\n * elements.nodes[0]?.data.image;\n * ```\n */\nexport function passNetwork(rows: readonly Row[], o: PassNetworkOptions): PassNetwork {\n const edgeWidth = checkPx(\"edgeWidth\", o.edgeWidth ?? 10);\n checkPx(\"nodeSize\", o.nodeSize ?? 40);\n const minWeight = o.minWeight;\n if (minWeight !== undefined && (!real(minWeight) || minWeight < 0))\n throw new InputError(`minWeight is 0 or more, got ${String(minWeight)}`);\n const at = [o.x, o.y, o.xend, o.yend];\n const placed = at.every((c) => c !== undefined);\n if (!placed && at.some((c) => c !== undefined))\n throw new InputError(\"x, y, xend and yend place the players together: give all four or none\");\n const scale = o.scale ?? 10;\n if (!real(scale) || scale <= 0)\n throw new InputError(`scale is px per data unit, more than 0, got ${String(scale)}`);\n const from = column(rows, o.from, \"from\");\n const to = column(rows, o.to, \"to\");\n const teams = column(rows, o.team, \"team\");\n const names = (c: string | undefined, what: string) =>\n c === undefined ? undefined : column(rows, c, what);\n const fromName = names(o.fromName, \"fromName\");\n const toName = names(o.toName, \"toName\");\n const weights = o.weight === undefined ? undefined : column(rows, o.weight, \"weight\");\n const xy = placed\n ? at.map((c, k) => column(rows, c as string, [\"x\", \"y\", \"xend\", \"yend\"][k] as string))\n : [];\n const ro = {\n ...(o.season === undefined ? {} : { season: o.season }),\n ...(o.idSystem === undefined ? {} : { idSystem: o.idSystem }),\n };\n const resolved = resolveSync(teams, o.league, ro);\n\n type Player = { label: string; team: string | undefined; xs: number[]; ys: number[] };\n const players = new Map<string, Player>();\n const pairs = new Map<\n string,\n { source: string; target: string; weight: number; team: string | undefined }\n >();\n const skipped: Value[] = [];\n const meet = (id: string, name: unknown, team: string | undefined, x?: number, y?: number): void => {\n let p = players.get(id);\n if (p === undefined) {\n const n = name === null || name === undefined ? \"\" : String(name).trim();\n p = { label: n === \"\" ? id : n, team, xs: [], ys: [] };\n players.set(id, p);\n }\n if (x !== undefined && y !== undefined) {\n p.xs.push(x);\n p.ys.push(y);\n }\n };\n rows.forEach((_, i) => {\n const a = playerId(from[i]);\n const b = playerId(to[i]);\n const c = xy.map((col) => col[i]);\n const w = weights === undefined ? 1 : weights[i];\n if (a === null || b === null || !c.every(real) || !real(w)) {\n skipped.push(a ?? b ?? i);\n return;\n }\n const team = resolved[i];\n const [x0, y0, x1, y1] = c as number[];\n meet(a, fromName?.[i], team, x0, y0);\n meet(b, toName?.[i], team, x1, y1);\n const key = `${a}->${b}`;\n const pair = pairs.get(key) ?? { source: a, target: b, weight: 0, team };\n pair.weight += w;\n pairs.set(key, pair);\n });\n if (skipped.length > 0)\n warn(\n `cytoscape:passes:${o.league}:${shownList(skipped)}`,\n `skipped ${skipped.length} pass(es) with no passer, no receiver, or a missing coordinate or weight: ${shownList(skipped)}`,\n );\n\n const hl = o.headshots === false || !Object.hasOwn(ESPN_HEADSHOT_LEAGUES, o.league) ? undefined : o.league;\n const noImage: string[] = [];\n const mean = (v: readonly number[]): number => v.reduce((s, x) => s + x, 0) / v.length;\n const nodes = [...players].map(([id, p]): CyNode => {\n const image =\n hl === undefined\n ? undefined\n : headshotUrl(id, hl as EspnHeadshotLeague, { idSystem: o.headshots === \"gsis\" ? \"gsis\" : \"espn\" });\n if (image === undefined && o.headshots !== false) noImage.push(p.label);\n return {\n data: {\n id,\n label: p.label,\n initials: p.label === id ? id : initialsOf(p.label),\n ...(p.team === undefined ? {} : { team: p.team }),\n ...(image === undefined ? {} : { image }),\n },\n ...(placed ? { position: { x: scale * mean(p.xs), y: -scale * mean(p.ys) } } : {}),\n classes: image === undefined ? \"initials\" : \"headshot\",\n };\n });\n if (noImage.length > 0)\n warn(\n `cytoscape:headshots:${o.league}:${shownList(noImage)}`,\n `drew ${noImage.length} player(s) with no headshot as team-coloured initials: ${shownList(noImage)}`,\n );\n const kept = [...pairs.values()].filter((p) => minWeight === undefined || p.weight >= minWeight);\n const heaviest = Math.max(0, ...kept.map((p) => p.weight));\n const edges = kept.map(\n (p): CyEdge => ({\n data: {\n id: `${p.source}->${p.target}`,\n source: p.source,\n target: p.target,\n weight: p.weight,\n // a pair whose summed weight is 0 or less (xT can be negative) is 0 px wide: Cytoscape refuses a negative width\n width: heaviest > 0 ? (edgeWidth * Math.max(0, p.weight)) / heaviest : edgeWidth,\n ...(p.team === undefined ? {} : { team: p.team }),\n },\n }),\n );\n const colors = Object.fromEntries(\n ([\"season\", \"which\", \"background\", \"fallback\", \"nodeSize\"] as const).flatMap((k) =>\n o[k] === undefined ? [] : [[k, o[k]]],\n ),\n ) as CyColorOptions;\n const style = teamStylesheet({\n ...colors,\n league: o.league,\n teams: [...new Set(resolved)].filter((t) => t !== undefined),\n idSystem: \"team_id\",\n });\n return { elements: { nodes, edges }, style };\n}\n"]}