@johnmorrisdotca/kyogijo-stadiums 0.4.0 → 0.6.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 (361) hide show
  1. package/CHANGELOG.md +133 -0
  2. package/NOTICE.md +182 -2
  3. package/README.md +106 -19
  4. package/dist/annotate.js +1 -1
  5. package/dist/arenas-entry.d.ts +4 -0
  6. package/dist/arenas-entry.js +3 -0
  7. package/dist/arenas.d.ts +125 -0
  8. package/dist/arenas.data.d.ts +2 -0
  9. package/dist/arenas.data.js +3763 -0
  10. package/dist/arenas.js +72 -0
  11. package/dist/athletics/describe.d.ts +8 -0
  12. package/dist/athletics/describe.js +41 -0
  13. package/dist/athletics/events.d.ts +17 -0
  14. package/dist/athletics/events.js +48 -0
  15. package/dist/athletics/facilities.d.ts +80 -0
  16. package/dist/athletics/facilities.js +176 -0
  17. package/dist/athletics/infield.d.ts +79 -0
  18. package/dist/athletics/infield.js +166 -0
  19. package/dist/athletics/layout.d.ts +13 -0
  20. package/dist/athletics/layout.js +98 -0
  21. package/dist/athletics/oval.d.ts +57 -0
  22. package/dist/athletics/oval.js +116 -0
  23. package/dist/athletics/plan.d.ts +97 -0
  24. package/dist/athletics/plan.js +355 -0
  25. package/dist/athletics/races.d.ts +102 -0
  26. package/dist/athletics/races.js +355 -0
  27. package/dist/athletics/steeplechase.d.ts +72 -0
  28. package/dist/athletics/steeplechase.js +196 -0
  29. package/dist/athletics/stencil.d.ts +13 -0
  30. package/dist/athletics/stencil.js +85 -0
  31. package/dist/athletics/track.d.ts +54 -0
  32. package/dist/athletics/track.js +105 -0
  33. package/dist/athletics/types.d.ts +77 -0
  34. package/dist/athletics/types.js +1 -0
  35. package/dist/athletics-entry.d.ts +17 -0
  36. package/dist/athletics-entry.js +11 -0
  37. package/dist/athletics.style.d.ts +16 -0
  38. package/dist/athletics.style.js +51 -0
  39. package/dist/atlas-entry.d.ts +6 -2
  40. package/dist/atlas-entry.js +4 -2
  41. package/dist/atlas.d.ts +117 -23
  42. package/dist/atlas.js +197 -12
  43. package/dist/background.js +2 -1
  44. package/dist/budo/describe.d.ts +13 -0
  45. package/dist/budo/describe.js +120 -0
  46. package/dist/budo/dojo.api.d.ts +64 -0
  47. package/dist/budo/dojo.api.js +46 -0
  48. package/dist/budo/dojo.d.ts +72 -0
  49. package/dist/budo/dojo.js +300 -0
  50. package/dist/budo/judo.d.ts +61 -0
  51. package/dist/budo/judo.js +109 -0
  52. package/dist/budo/karate.d.ts +29 -0
  53. package/dist/budo/karate.js +80 -0
  54. package/dist/budo/kendo.d.ts +18 -0
  55. package/dist/budo/kendo.js +44 -0
  56. package/dist/budo/kimarite.d.ts +19 -0
  57. package/dist/budo/kimarite.js +33 -0
  58. package/dist/budo/kit.d.ts +16 -0
  59. package/dist/budo/kit.js +24 -0
  60. package/dist/budo/kyudo.d.ts +28 -0
  61. package/dist/budo/kyudo.js +66 -0
  62. package/dist/budo/looks.d.ts +28 -0
  63. package/dist/budo/looks.js +58 -0
  64. package/dist/budo/parts.d.ts +32 -0
  65. package/dist/budo/parts.js +10 -0
  66. package/dist/budo/plan.d.ts +64 -0
  67. package/dist/budo/plan.js +139 -0
  68. package/dist/budo/room.d.ts +133 -0
  69. package/dist/budo/room.js +111 -0
  70. package/dist/budo/sumo.d.ts +31 -0
  71. package/dist/budo/sumo.js +71 -0
  72. package/dist/budo/tatami.d.ts +55 -0
  73. package/dist/budo/tatami.js +156 -0
  74. package/dist/budo/types.d.ts +192 -0
  75. package/dist/budo/types.js +2 -0
  76. package/dist/budo-entry.d.ts +22 -0
  77. package/dist/budo-entry.js +13 -0
  78. package/dist/budo.board.d.ts +28 -0
  79. package/dist/budo.board.js +81 -0
  80. package/dist/budo.score.d.ts +18 -0
  81. package/dist/budo.score.js +52 -0
  82. package/dist/budo.style.d.ts +6 -0
  83. package/dist/budo.style.js +67 -0
  84. package/dist/charts-entry.d.ts +6 -1
  85. package/dist/charts-entry.js +3 -0
  86. package/dist/charts.athletics.d.ts +183 -0
  87. package/dist/charts.athletics.js +455 -0
  88. package/dist/charts.baseball.js +1 -1
  89. package/dist/charts.curling.heat.d.ts +24 -0
  90. package/dist/charts.curling.heat.js +116 -0
  91. package/dist/charts.curling.js +14 -3
  92. package/dist/charts.js +4 -4
  93. package/dist/charts.pool.d.ts +67 -0
  94. package/dist/charts.pool.js +76 -0
  95. package/dist/charts.skating.d.ts +204 -0
  96. package/dist/charts.skating.js +426 -0
  97. package/dist/charts.soccer.js +3 -3
  98. package/dist/charts.types.d.ts +63 -1
  99. package/dist/colour.d.ts +4 -4
  100. package/dist/colour.js +4 -4
  101. package/dist/court.d.ts +85 -2
  102. package/dist/court.js +102 -7
  103. package/dist/court.more.d.ts +46 -0
  104. package/dist/court.more.js +219 -0
  105. package/dist/courts.style.d.ts +14 -2
  106. package/dist/courts.style.js +51 -4
  107. package/dist/crowd.d.ts +164 -0
  108. package/dist/crowd.draw.d.ts +17 -0
  109. package/dist/crowd.draw.js +204 -0
  110. package/dist/crowd.js +480 -0
  111. package/dist/crowd.park.d.ts +19 -0
  112. package/dist/crowd.park.js +153 -0
  113. package/dist/curling.stats.d.ts +271 -0
  114. package/dist/curling.stats.js +301 -0
  115. package/dist/draw.d.ts +16 -0
  116. package/dist/draw.js +462 -54
  117. package/dist/element-entry.d.ts +3 -1
  118. package/dist/element-entry.js +2 -1
  119. package/dist/element.d.ts +13 -2
  120. package/dist/element.js +79 -4
  121. package/dist/emblems-source.d.ts +50 -0
  122. package/dist/emblems-source.js +56 -0
  123. package/dist/eras.d.ts +3 -1
  124. package/dist/eras.js +8 -3
  125. package/dist/features.js +204 -0
  126. package/dist/field.data.d.ts +16 -0
  127. package/dist/field.data.js +50 -0
  128. package/dist/fields.kit.d.ts +2 -2
  129. package/dist/fields.types.d.ts +24 -4
  130. package/dist/formations.js +18 -7
  131. package/dist/formations.sports.d.ts +8 -0
  132. package/dist/formations.sports.js +251 -0
  133. package/dist/frames.providers.js +71 -8
  134. package/dist/geojson.js +3 -0
  135. package/dist/geometry-entry.d.ts +2 -1
  136. package/dist/geometry-entry.js +2 -1
  137. package/dist/geometry.d.ts +20 -0
  138. package/dist/geometry.js +78 -1
  139. package/dist/grounds.d.ts +4 -0
  140. package/dist/grounds.data.js +12 -0
  141. package/dist/halls.d.ts +65 -0
  142. package/dist/halls.js +122 -0
  143. package/dist/hardwood.d.ts +31 -0
  144. package/dist/hardwood.js +157 -0
  145. package/dist/heat.js +21 -13
  146. package/dist/heat.types.d.ts +5 -0
  147. package/dist/heat.zones.d.ts +4 -2
  148. package/dist/heat.zones.js +207 -7
  149. package/dist/homeside.js +24 -0
  150. package/dist/index.d.ts +30 -4
  151. package/dist/index.js +18 -2
  152. package/dist/layers-entry.d.ts +2 -1
  153. package/dist/layers-entry.js +1 -0
  154. package/dist/layers.d.ts +2 -1
  155. package/dist/layers.env.d.ts +12 -1
  156. package/dist/layers.env.js +36 -4
  157. package/dist/layers.js +32 -13
  158. package/dist/layers.types.d.ts +9 -2
  159. package/dist/leagues/athletics.d.ts +63 -0
  160. package/dist/leagues/athletics.js +87 -0
  161. package/dist/leagues/budo.d.ts +196 -0
  162. package/dist/leagues/budo.js +105 -0
  163. package/dist/leagues/courts.d.ts +269 -4
  164. package/dist/leagues/courts.js +155 -1
  165. package/dist/leagues/hockey.js +41 -19
  166. package/dist/leagues/skating.d.ts +145 -0
  167. package/dist/leagues/skating.js +73 -0
  168. package/dist/leagues/sources.js +41 -0
  169. package/dist/leagues/types.d.ts +1 -1
  170. package/dist/leagues-entry.d.ts +4 -2
  171. package/dist/leagues-entry.js +2 -1
  172. package/dist/leagues.d.ts +17 -4
  173. package/dist/leagues.js +246 -5
  174. package/dist/lineup.codes.d.ts +23 -0
  175. package/dist/lineup.codes.js +122 -0
  176. package/dist/logo.d.ts +25 -0
  177. package/dist/logo.js +47 -0
  178. package/dist/look.d.ts +3 -3
  179. package/dist/look.js +39 -6
  180. package/dist/outline.d.ts +1 -1
  181. package/dist/outline.js +46 -4
  182. package/dist/pageant.d.ts +120 -0
  183. package/dist/pageant.draw.d.ts +21 -0
  184. package/dist/pageant.draw.js +171 -0
  185. package/dist/pageant.js +211 -0
  186. package/dist/pageant.style.d.ts +8 -0
  187. package/dist/pageant.style.js +42 -0
  188. package/dist/png.d.ts +8 -1
  189. package/dist/png.js +21 -1
  190. package/dist/pointer-entry.d.ts +2 -0
  191. package/dist/pointer-entry.js +2 -0
  192. package/dist/pointer.d.ts +113 -0
  193. package/dist/pointer.js +151 -0
  194. package/dist/pool.d.ts +176 -0
  195. package/dist/pool.figures.d.ts +306 -0
  196. package/dist/pool.figures.js +148 -0
  197. package/dist/pool.js +304 -0
  198. package/dist/pool.kit.d.ts +48 -0
  199. package/dist/pool.kit.js +141 -0
  200. package/dist/pool.labels.d.ts +20 -0
  201. package/dist/pool.labels.js +258 -0
  202. package/dist/pool.race.d.ts +65 -0
  203. package/dist/pool.race.js +140 -0
  204. package/dist/pool.replay.d.ts +46 -0
  205. package/dist/pool.replay.js +127 -0
  206. package/dist/pool.section.d.ts +13 -0
  207. package/dist/pool.section.js +83 -0
  208. package/dist/pool.style.d.ts +20 -0
  209. package/dist/pool.style.js +169 -0
  210. package/dist/pool.types.d.ts +145 -0
  211. package/dist/pool.types.js +3 -0
  212. package/dist/pool.words.d.ts +13 -0
  213. package/dist/pool.words.js +101 -0
  214. package/dist/ranges.names.js +53 -3
  215. package/dist/replay-entry.d.ts +3 -0
  216. package/dist/replay-entry.js +2 -0
  217. package/dist/restyle.js +1 -1
  218. package/dist/road.draw.js +3 -2
  219. package/dist/road.js +2 -1
  220. package/dist/road.types.d.ts +3 -0
  221. package/dist/rules.d.ts +138 -0
  222. package/dist/rules.js +511 -0
  223. package/dist/rules.types.d.ts +4 -0
  224. package/dist/rules.types.js +64 -0
  225. package/dist/score.d.ts +3 -0
  226. package/dist/score.js +93 -6
  227. package/dist/sheet.d.ts +3 -0
  228. package/dist/sheet.js +7 -3
  229. package/dist/situation.d.ts +135 -0
  230. package/dist/situation.draw.d.ts +34 -0
  231. package/dist/situation.draw.js +202 -0
  232. package/dist/situation.js +337 -0
  233. package/dist/situation.style.d.ts +2 -0
  234. package/dist/situation.style.js +45 -0
  235. package/dist/situation.types.d.ts +77 -0
  236. package/dist/situation.types.js +3 -0
  237. package/dist/situation.widgets.d.ts +39 -0
  238. package/dist/situation.widgets.js +129 -0
  239. package/dist/skating/describe.d.ts +7 -0
  240. package/dist/skating/describe.js +44 -0
  241. package/dist/skating/events.d.ts +24 -0
  242. package/dist/skating/events.js +47 -0
  243. package/dist/skating/figure.d.ts +24 -0
  244. package/dist/skating/figure.js +37 -0
  245. package/dist/skating/long.d.ts +54 -0
  246. package/dist/skating/long.js +111 -0
  247. package/dist/skating/parts.d.ts +43 -0
  248. package/dist/skating/parts.js +40 -0
  249. package/dist/skating/plan.d.ts +55 -0
  250. package/dist/skating/plan.js +197 -0
  251. package/dist/skating/short.d.ts +47 -0
  252. package/dist/skating/short.js +89 -0
  253. package/dist/skating/track.d.ts +66 -0
  254. package/dist/skating/track.js +94 -0
  255. package/dist/skating/types.d.ts +57 -0
  256. package/dist/skating/types.js +3 -0
  257. package/dist/skating-entry.d.ts +17 -0
  258. package/dist/skating-entry.js +11 -0
  259. package/dist/skating.style.d.ts +4 -0
  260. package/dist/skating.style.js +24 -0
  261. package/dist/strings.d.ts +470 -0
  262. package/dist/strings.js +674 -13
  263. package/dist/style.d.ts +2 -0
  264. package/dist/style.js +19 -2
  265. package/dist/surfaces/artistic.d.ts +9 -0
  266. package/dist/surfaces/artistic.js +94 -0
  267. package/dist/surfaces/athletics.d.ts +16 -0
  268. package/dist/surfaces/athletics.js +132 -0
  269. package/dist/surfaces/baseball.js +17 -0
  270. package/dist/surfaces/basketball.floor.d.ts +27 -0
  271. package/dist/surfaces/basketball.floor.js +83 -0
  272. package/dist/surfaces/basketball.gear.d.ts +27 -0
  273. package/dist/surfaces/basketball.gear.js +90 -0
  274. package/dist/surfaces/basketball.js +44 -22
  275. package/dist/surfaces/budo.d.ts +6 -0
  276. package/dist/surfaces/budo.js +104 -0
  277. package/dist/surfaces/curling.d.ts +16 -0
  278. package/dist/surfaces/curling.fold.d.ts +67 -0
  279. package/dist/surfaces/curling.fold.js +107 -0
  280. package/dist/surfaces/curling.js +37 -3
  281. package/dist/surfaces/dive.d.ts +9 -0
  282. package/dist/surfaces/dive.js +128 -0
  283. package/dist/surfaces/field.d.ts +7 -2
  284. package/dist/surfaces/field.js +15 -9
  285. package/dist/surfaces/football.js +7 -0
  286. package/dist/surfaces/hockey.js +4 -2
  287. package/dist/surfaces/net.d.ts +2 -1
  288. package/dist/surfaces/net.js +160 -15
  289. package/dist/surfaces/polo.d.ts +12 -0
  290. package/dist/surfaces/polo.js +159 -0
  291. package/dist/surfaces/pool.d.ts +27 -0
  292. package/dist/surfaces/pool.js +198 -0
  293. package/dist/surfaces/skating.d.ts +6 -0
  294. package/dist/surfaces/skating.js +62 -0
  295. package/dist/surfaces/soccer.js +2 -1
  296. package/dist/surfaces/surface.d.ts +76 -0
  297. package/dist/surfaces/venue.d.ts +15 -0
  298. package/dist/surfaces/venue.js +95 -0
  299. package/dist/target-entry.d.ts +2 -0
  300. package/dist/target-entry.js +2 -0
  301. package/dist/target.d.ts +200 -0
  302. package/dist/target.js +525 -0
  303. package/dist/team.common.d.ts +1 -1
  304. package/dist/team.common.js +3 -3
  305. package/dist/team.formation.d.ts +3 -1
  306. package/dist/team.formation.js +3 -3
  307. package/dist/team.layers.d.ts +2 -2
  308. package/dist/team.layers.js +4 -1
  309. package/dist/team.lineup.d.ts +27 -0
  310. package/dist/team.lineup.js +207 -0
  311. package/dist/team.network.js +6 -5
  312. package/dist/team.shape.js +5 -5
  313. package/dist/team.types.d.ts +40 -1
  314. package/dist/tenants.d.ts +77 -0
  315. package/dist/tenants.data.d.ts +8 -0
  316. package/dist/tenants.data.js +1793 -0
  317. package/dist/tenants.js +46 -0
  318. package/dist/turn-entry.d.ts +12 -0
  319. package/dist/turn-entry.js +7 -0
  320. package/dist/turn.camera.d.ts +116 -0
  321. package/dist/turn.camera.js +184 -0
  322. package/dist/turn.dom.d.ts +40 -0
  323. package/dist/turn.dom.js +353 -0
  324. package/dist/turn.kit.d.ts +86 -0
  325. package/dist/turn.kit.js +110 -0
  326. package/dist/turn.markup.d.ts +48 -0
  327. package/dist/turn.markup.js +108 -0
  328. package/dist/turn.model.d.ts +78 -0
  329. package/dist/turn.model.js +91 -0
  330. package/dist/turn.style.d.ts +5 -0
  331. package/dist/turn.style.js +56 -0
  332. package/dist/types.d.ts +221 -20
  333. package/dist/units.d.ts +1 -1
  334. package/dist/units.js +22 -1
  335. package/dist/uprights.board.d.ts +17 -0
  336. package/dist/uprights.board.js +59 -0
  337. package/dist/uprights.budo.d.ts +17 -0
  338. package/dist/uprights.budo.js +87 -0
  339. package/dist/uprights.d.ts +37 -0
  340. package/dist/uprights.fields.d.ts +32 -0
  341. package/dist/uprights.fields.js +172 -0
  342. package/dist/uprights.goal.d.ts +49 -0
  343. package/dist/uprights.goal.js +69 -0
  344. package/dist/uprights.js +169 -0
  345. package/dist/uprights.more.d.ts +7 -0
  346. package/dist/uprights.more.js +183 -0
  347. package/dist/uprights.net.d.ts +12 -0
  348. package/dist/uprights.net.js +109 -0
  349. package/dist/uprights.netkit.d.ts +28 -0
  350. package/dist/uprights.netkit.js +67 -0
  351. package/dist/uprights.pools.d.ts +8 -0
  352. package/dist/uprights.pools.js +190 -0
  353. package/dist/uprights.rinks.d.ts +27 -0
  354. package/dist/uprights.rinks.js +106 -0
  355. package/dist/uprights.track.d.ts +50 -0
  356. package/dist/uprights.track.js +246 -0
  357. package/dist/version.d.ts +1 -1
  358. package/dist/version.js +1 -1
  359. package/dist/zones-entry.d.ts +2 -0
  360. package/dist/zones-entry.js +2 -0
  361. package/package.json +56 -3
@@ -0,0 +1,27 @@
1
+ import { type Env, type Made } from "./layers.env.ts";
2
+ import { type Spot } from "./team.formation.ts";
3
+ import { type TeamTable } from "./team.common.ts";
4
+ import type { Formation, FormationLayer, LineupLayer, LineupPlayer } from "./team.types.ts";
5
+ /** How wide a text is, in ems of a bold sans-serif face, by a rough count: wide for Japanese and capitals, narrow for the letters that are. */
6
+ export declare function textWidth(text: string): number;
7
+ /** A name cut to fit `ems` of width, with an ellipsis; nothing when not even three letters fit. */
8
+ export declare function fit(name: string, ems: number): string;
9
+ /** The jersey as a path through its corners, `size` wide, centred on `(x, y)`: sleeves, shoulders, a neckline and the body. */
10
+ export declare function jerseyPath(x: number, y: number, size: number): string;
11
+ /** The formation a lineup is drawn in, and how the players stand in it. */
12
+ export interface Lineup {
13
+ formation: Formation;
14
+ /** The layer as a formation layer: the players put on the places they were given. */
15
+ layer: FormationLayer;
16
+ spots: Spot[];
17
+ /** The players with no place in the formation. */
18
+ unplaced: LineupPlayer[];
19
+ /** The player standing on each place, with everything the lineup knows of him. */
20
+ stands: Map<string, LineupPlayer>;
21
+ }
22
+ /** The formation a lineup uses: the one named, one of your own, or the best fit among those of the surface's sport (the softball one for a softball field). */
23
+ export declare function lineupFormation(layer: LineupLayer, env: Env): Formation;
24
+ /** A lineup's places on the surface and the players on them. */
25
+ export declare function computeLineup(layer: LineupLayer, env: Env): Lineup;
26
+ export declare function drawLineup(layer: LineupLayer, env: Env): Made;
27
+ export declare function lineupTables(layer: LineupLayer, env: Env): TeamTable[];
@@ -0,0 +1,207 @@
1
+ // The lineup layer: a team's players placed by position code on the surface, each as a disc or a jersey with the shirt number in it and the name under it, with a captain's
2
+ // armband and a small flag where they are given. It reads where each position stands from a formation (formations.ts, formations.sports.ts) and so needs no coordinates; two
3
+ // teams are two layers. Written for this package.
4
+ import { safeLogo } from "./logo.js";
5
+ import { esc, num } from "./format.js";
6
+ import { FRAMES } from "./frames.js";
7
+ import { positionName } from "./formations.js";
8
+ import { assign, bestFormation, formationsFor, spotsOf } from "./lineup.codes.js";
9
+ import { isNum, newMade, opacityOf, unitOfLayer } from "./layers.env.js";
10
+ import { chooseLeague } from "./leagues.js";
11
+ import { SURFACES, computeFormation, formationOf } from "./team.formation.js";
12
+ import { attrs, labelOf, paintOf, round, titled } from "./team.common.js";
13
+ import { fill } from "./strings.js";
14
+ import { FEET_PER } from "./units.js";
15
+ /** The sports whose team turns round for the visitors: a side that attacks the other end. Baseball's fielders, football's offence and a cricket field do not. */
16
+ const TURNS = new Set(["soccer", "nhl", "nba", "rugby", "afl", "volleyball", "handball", "waterpolo", "fieldhockey", "netball"]);
17
+ /** How wide a text is, in ems of a bold sans-serif face, by a rough count: wide for Japanese and capitals, narrow for the letters that are. */
18
+ export function textWidth(text) {
19
+ let width = 0;
20
+ for (const ch of text) {
21
+ const code = ch.codePointAt(0) ?? 0;
22
+ if (code >= 0x2e80)
23
+ width += 1;
24
+ else if ("iljtfI.,:;'!| ()".includes(ch))
25
+ width += 0.32;
26
+ else if (/[A-Z0-9MWmw@%]/.test(ch))
27
+ width += 0.7;
28
+ else
29
+ width += 0.6;
30
+ }
31
+ return width;
32
+ }
33
+ /** A name cut to fit `ems` of width, with an ellipsis; nothing when not even three letters fit. */
34
+ export function fit(name, ems) {
35
+ if (textWidth(name) <= ems)
36
+ return name;
37
+ const letters = [...name];
38
+ while (letters.length > 0 && textWidth(`${letters.join("").trimEnd()}…`) > ems)
39
+ letters.pop();
40
+ return letters.length < 3 ? "" : `${letters.join("").trimEnd()}…`;
41
+ }
42
+ /** The jersey as a path through its corners, `size` wide, centred on `(x, y)`: sleeves, shoulders, a neckline and the body. */
43
+ export function jerseyPath(x, y, size) {
44
+ const p = (dx, dy) => `${num(x + dx * size)} ${num(y + dy * size)}`;
45
+ return `M${p(-0.17, -0.48)}Q${p(0, -0.36)} ${p(0.17, -0.48)}L${p(0.5, -0.3)}L${p(0.42, -0.02)}L${p(0.34, -0.07)}L${p(0.34, 0.48)}L${p(-0.34, 0.48)}L${p(-0.34, -0.07)}L${p(-0.42, -0.02)}L${p(-0.5, -0.3)}Z`;
46
+ }
47
+ /** The formation a lineup uses: the one named, one of your own, or the best fit among those of the surface's sport (the softball one for a softball field). */
48
+ export function lineupFormation(layer, env) {
49
+ if (!Array.isArray(layer.players))
50
+ throw new TypeError("a lineup needs players: a list of { pos, name, number }");
51
+ if (layer.formation !== undefined)
52
+ return formationOf({ kind: "formation", formation: layer.formation });
53
+ const sports = Object.keys(SURFACES).filter((sport) => SURFACES[sport].includes(env.o.sport));
54
+ const softball = (() => { const league = chooseLeague(env.o).league; return league?.family === "baseball" && league.game === "softball"; })();
55
+ const among = formationsFor(sports).filter((f) => (f.id === "softball-standard") === softball);
56
+ const best = bestFormation(among, layer.players, layer.rotation);
57
+ if (best === undefined)
58
+ throw new RangeError(`There is no formation for ${env.o.sport}: give the formation of your own`);
59
+ return best;
60
+ }
61
+ /** A lineup's places on the surface and the players on them. */
62
+ export function computeLineup(layer, env) {
63
+ const formation = lineupFormation(layer, env);
64
+ const f = formationOf({ kind: "formation", formation });
65
+ const spotsList = spotsOf(f);
66
+ const made = assign(f, layer.players, layer.rotation);
67
+ const stands = new Map();
68
+ const unplaced = [];
69
+ const data = [];
70
+ layer.players.forEach((p, i) => {
71
+ const id = made.of[i];
72
+ if (id === undefined || typeof p !== "object" || p === null) {
73
+ if (typeof p === "object" && p !== null)
74
+ unplaced.push(p);
75
+ return;
76
+ }
77
+ stands.set(id, p);
78
+ data.push({ ...p, position: id, team: p.team ?? layer.side ?? "home" });
79
+ });
80
+ const side = layer.side === "away" ? "away" : "home";
81
+ // The visitors attack the other end, except where the sport does not turn: a football offence goes right unless the layer says, and a defence faces it.
82
+ const attacking = layer.attacking ?? (side === "away" && TURNS.has(env.o.sport) ? "left" : undefined);
83
+ const asLayer = { ...layer, kind: "formation", formation: { ...f, positions: spotsList, extra: undefined }, team: side, data, attacking, labels: undefined, names: undefined, size: layer.size };
84
+ const { spots } = computeFormation(asLayer, env);
85
+ return { formation, layer: asLayer, spots, unplaced, stands };
86
+ }
87
+ const NUMBER_INK = "ky-nm";
88
+ export function drawLineup(layer, env) {
89
+ const made = newMade();
90
+ const words = env.words.team;
91
+ const language = env.o.language === "ja" ? "ja" : "en";
92
+ const { formation, spots, unplaced, stands } = computeLineup(layer, env);
93
+ const k = FEET_PER[unitOfLayer(layer, env)];
94
+ const rel = new Map(spotsOf(formation).map((p) => [p.id, p.size ?? 1]));
95
+ // The marks are as big as is usual and no bigger than the first line's places allow; the second line's are smaller.
96
+ const first = spots.filter((s) => (rel.get(s.id) ?? 1) >= 1);
97
+ let nearest = Infinity;
98
+ first.forEach((a, i) => first.forEach((b, j) => (j > i ? (nearest = Math.min(nearest, Math.hypot(a.at[0] - b.at[0], a.at[1] - b.at[1]))) : undefined)));
99
+ // A football line stands shoulder to shoulder, so sizing by the nearest neighbour would shrink the offence and not the defence: both teams get one size.
100
+ const football = env.o.sport === "nfl" || env.o.sport === "ifaf";
101
+ const base = isNum(layer.size) && layer.size > 0 ? layer.size * k : football ? env.base * 2.4 : Math.min(env.base * 3.4, Math.max(env.base * 2, (Number.isFinite(nearest) ? nearest : env.base * 2) * 0.9));
102
+ const jersey = layer.shape === "jersey";
103
+ const mode = layer.labels === "position" || layer.labels === "number" || layer.labels === "name" || layer.labels === "none" ? layer.labels : "number";
104
+ const showNames = layer.names !== false && mode !== "name";
105
+ const side = layer.side === "away" ? "away" : "home";
106
+ const mine = paintOf({ team: side });
107
+ const marks = [];
108
+ const badges = [];
109
+ const text = [];
110
+ spots.forEach((s) => {
111
+ const player = stands.get(s.id);
112
+ const size = base * (rel.get(s.id) ?? 1);
113
+ const [cx, cy] = s.at;
114
+ const own = paintOf(player === undefined ? undefined : { color: player.color, team: player.team ?? side });
115
+ const cls = [own.cls !== "" ? own.cls : mine.cls, player === undefined ? "ky-e" : ""].filter((c) => c !== "").join(" ");
116
+ const tip = [s.name, player === undefined ? "" : [player.name, player.number].filter((t) => t !== undefined && t !== "").join(" ")].filter((t) => t !== "").join(env.words.said.colon);
117
+ const turn = env.turned ? ` transform="rotate(-90 ${num(cx)} ${num(cy)})"` : "";
118
+ marks.push(titled(jersey ? "path" : "circle", `${attrs(cls, own.css)} ${jersey ? `d="${jerseyPath(cx, cy, size * 1.08)}"` : `cx="${num(cx)}" cy="${num(cy)}" r="${num(size / 2)}"`}`, player?.title ?? tip));
119
+ // What is in the mark.
120
+ let label = "";
121
+ if (mode === "position")
122
+ label = s.id;
123
+ else if (mode === "number")
124
+ label = player === undefined ? s.id : player.number === undefined || player.number === "" ? s.id : String(player.number);
125
+ else if (mode === "name")
126
+ label = player === undefined ? s.id : labelOf(player);
127
+ if (label !== "") {
128
+ const room = jersey ? size * 0.6 : size * 0.86;
129
+ const fs = Math.min(size * 0.46, room / Math.max(0.9, textWidth(label)));
130
+ text.push(`<text class="${player === undefined ? "ky-lt" : NUMBER_INK}" style="font-size:${num(fs)}px" x="${num(cx)}" y="${num(cy + fs * 0.36 + (jersey ? size * 0.07 : 0))}"${turn}>${esc(label)}</text>`);
131
+ }
132
+ // The name, under the mark, as wide as the neighbours allow and no wider: it shrinks, and is squeezed if it must be.
133
+ if (showNames && player?.name !== undefined && player.name !== "") {
134
+ let neighbour = Infinity;
135
+ for (const other of spots)
136
+ if (other !== s)
137
+ neighbour = Math.min(neighbour, Math.hypot(other.at[0] - cx, other.at[1] - cy));
138
+ const room = Math.max(size * 1.05, Math.min(size * 3.4, (Number.isFinite(neighbour) ? neighbour : size * 3) * 0.92));
139
+ // The name shrinks to fit, down to a quarter of the mark's width; a name still too long is cut short with an ellipsis, and one that cannot keep three letters is left out
140
+ // (a football line stands shoulder to shoulder). The whole name is in the tooltip, the description and the table.
141
+ const fs = Math.min(size * 0.4, Math.max(size * 0.26, room / textWidth(player.name)));
142
+ const shown = fit(player.name, room / fs);
143
+ if (shown !== "")
144
+ text.push(`<text class="ky-lt" style="font-size:${num(fs)}px" x="${num(cx)}" y="${num(cy + size * (jersey ? 0.54 : 0.5) + fs * 1.05)}"${turn}>${esc(shown)}</text>`);
145
+ }
146
+ // The captain's armband and the flag.
147
+ if (player?.captain === true) {
148
+ const r = size * 0.2;
149
+ const bx = cx - size * 0.4;
150
+ const by = cy - size * 0.4;
151
+ badges.push(titled("circle", ` class="ky-lc" cx="${num(bx)}" cy="${num(by)}" r="${num(r)}"`, words.captainTip));
152
+ text.push(`<text class="ky-cl" style="font-size:${num(r * 1.25)}px" x="${num(bx)}" y="${num(by + r * 0.45)}"${env.turned ? ` transform="rotate(-90 ${num(bx)} ${num(by)})"` : ""}>${esc(words.captainMark)}</text>`);
153
+ }
154
+ const flag = safeLogo(player?.flag);
155
+ if (flag !== undefined) {
156
+ const h = size * 0.3;
157
+ const w = h * 1.5;
158
+ const fx = cx + size * 0.42 - w / 2;
159
+ const fy = cy + size * 0.4 - h / 2;
160
+ const spin = env.turned ? ` transform="rotate(-90 ${num(fx + w / 2)} ${num(fy + h / 2)})"` : "";
161
+ badges.push(`<g${spin}><image href="${esc(flag)}" x="${num(fx)}" y="${num(fy)}" width="${num(w)}" height="${num(h)}" preserveAspectRatio="xMidYMid slice"/><rect class="ky-lf" x="${num(fx)}" y="${num(fy)}" width="${num(w)}" height="${num(h)}"/></g>`);
162
+ }
163
+ });
164
+ made.drawn = spots.length;
165
+ made.skipped = unplaced.length;
166
+ made.cls = "ky-tm";
167
+ made.opacity = opacityOf(layer.opacity);
168
+ made.style = `--w:${num(base * 0.07)}`;
169
+ made.marks = `<g class="ky-fm${mine.cls === "" ? "" : ` ${mine.cls}`}">${marks.join("")}</g>${badges.length === 0 ? "" : `<g class="ky-lb">${badges.join("")}</g>`}<g text-anchor="middle">${text.join("")}</g>`;
170
+ const name = formation.name[language];
171
+ made.title = fill(words.lineupTitle, { name, n: stands.size });
172
+ const parts = [];
173
+ const list = spots.filter((s) => stands.has(s.id)).map((s) => fill(words.formationItem, { who: labelOf(stands.get(s.id)) || s.id, position: s.name }));
174
+ if (list.length > 0)
175
+ parts.push(fill(words.formationPlayers, { list: list.join(env.words.layers.listSep) }));
176
+ const captain = [...stands.values()].find((p) => p.captain === true);
177
+ if (captain !== undefined)
178
+ parts.push(fill(words.lineupCaptain, { who: labelOf(captain) }));
179
+ if (formation.sport === "volleyball" && isNum(layer.rotation) && layer.rotation >= 1)
180
+ parts.push(fill(words.lineupRotation, { n: Math.floor(layer.rotation) }));
181
+ if (unplaced.length > 0)
182
+ parts.push(fill(words.lineupUnplaced, { list: unplaced.map((p) => labelOf(p) || String(p.pos)).join(env.words.layers.listSep) }));
183
+ parts.push(formation.about[language]);
184
+ made.text = parts.join(env.words.said.gap);
185
+ return made;
186
+ }
187
+ export function lineupTables(layer, env) {
188
+ const w = env.words.team;
189
+ const l = env.words.layers;
190
+ const { formation, spots, unplaced, stands } = computeLineup(layer, env);
191
+ const language = env.o.language === "ja" ? "ja" : "en";
192
+ const frame = FRAMES[formation.frame];
193
+ const yes = w.captainMark;
194
+ const rows = spots.map((s, i) => {
195
+ const p = stands.get(s.id);
196
+ return [i + 1, s.id, s.name, p?.number === undefined ? null : String(p.number), p?.name ?? null, p === undefined ? null : p.captain === true ? yes : null, round(s.given[0], 2), round(s.given[1], 2)];
197
+ });
198
+ unplaced.forEach((p, i) => rows.push([spots.length + i + 1, String(p.pos), positionName(formation.sport, String(p.pos), language), p.number === undefined ? null : String(p.number), p.name ?? null, p.captain === true ? yes : null, null, null]));
199
+ return [
200
+ {
201
+ head: [l.tableNo, w.tablePosition, w.tableName, w.tableNumber, w.tablePlayer, w.tableCaptain, l.tableX, l.tableY],
202
+ rows,
203
+ title: typeof layer.title === "string" && layer.title !== "" ? layer.title : fill(w.lineupTitle, { name: formation.name[language], n: stands.size }),
204
+ note: fill(l.tableFrame, { frame: formation.frame, unit: frame.unit }),
205
+ },
206
+ ];
207
+ }
@@ -2,7 +2,7 @@
2
2
  // players sized and shaded by the passes between them. Written for this package from the plain arithmetic of counting and averaging; the idea
3
3
  // is that of mplsoccer's pass-network example (MIT), and no code of it is used.
4
4
  import { esc, num } from "./format.js";
5
- import { isNum, newMade, opacityOf, place, toArray, unitOfLayer } from "./layers.env.js";
5
+ import { isNum, markAttrs, newMade, opacityOf, place, toArray, unitOfLayer } from "./layers.env.js";
6
6
  import { curveBetween, curvePath, headPath } from "./layers.marks.js";
7
7
  import { fill } from "./strings.js";
8
8
  import { attrs, frameNote, round, teamClass, teamKey, titled } from "./team.common.js";
@@ -151,21 +151,22 @@ export function drawNetwork(layer, env) {
151
151
  const cls = teamClass(l.team);
152
152
  const o = between(alpha, linkT(l), false);
153
153
  const names = { a: nodeName(l.a), b: nodeName(l.b), n: l.n };
154
+ const link = !env.marks ? "" : ` data-mark-id="${esc(`${idText(l.a.id)}-${idText(l.b.id)}`)}" data-index="${links.length}" data-from="${esc(idText(l.a.id))}" data-to="${esc(idText(l.b.id))}"`;
154
155
  if (net.directed) {
155
156
  const curve = curveBetween(a, b, 0.14);
156
157
  const hl = Math.max(w * 3.4, base * 0.9);
157
158
  const stop = nodeWidth(l.b) / 2;
158
159
  const shaft = curve.trim(stop + hl * 0.9);
159
160
  const head = headPath(curve.at(Math.max(0, curve.length - stop)), curve.tangent(Math.max(0, curve.length - stop)), hl, hl * 0.5, false);
160
- links.push(`<g${attrs(cls, "", o)}><title>${esc(fill(words.linkTitleDirected, names))}</title><path class="ky-ln" stroke-width="${num(w)}" d="${curvePath(shaft)}"/><path class="ky-ah" d="${head}"/></g>`);
161
+ links.push(`<g${attrs(cls, "", o)}${link}><title>${esc(fill(words.linkTitleDirected, names))}</title><path class="ky-ln" stroke-width="${num(w)}" d="${curvePath(shaft)}"/><path class="ky-ah" d="${head}"/></g>`);
161
162
  }
162
163
  else
163
- links.push(titled("path", `${attrs(`ky-ln${cls === "" ? "" : ` ${cls}`}`, "", o)} stroke-width="${num(w)}" d="M${num(a[0])} ${num(a[1])}L${num(b[0])} ${num(b[1])}"`, fill(words.linkTitle, names)));
164
+ links.push(titled("path", `${attrs(`ky-ln${cls === "" ? "" : ` ${cls}`}`, "", o)}${link} stroke-width="${num(w)}" d="M${num(a[0])} ${num(a[1])}L${num(b[0])} ${num(b[1])}"`, fill(words.linkTitle, names)));
164
165
  }
165
- const nodes = net.nodes.map((n) => {
166
+ const nodes = net.nodes.map((n, index) => {
166
167
  const at = n.at;
167
168
  const cls = teamClass(n.team);
168
- return titled("circle", `${attrs(cls, "")} cx="${num(at[0])}" cy="${num(at[1])}" r="${num(nodeWidth(n) / 2)}"`, fill(words.nodeTitle, { name: nodeName(n), touches: n.touches, made: n.made, received: n.received }));
169
+ return titled("circle", `${attrs(cls, "")} cx="${num(at[0])}" cy="${num(at[1])}" r="${num(nodeWidth(n) / 2)}"${markAttrs(env, idText(n.id), index, [at[0], at[1]])}`, fill(words.nodeTitle, { name: nodeName(n), touches: n.touches, made: n.made, received: n.received }));
169
170
  });
170
171
  const mode = layer.labels === "name" || layer.labels === "id" || layer.labels === "none" || layer.labels === "number" ? layer.labels : "number";
171
172
  const text = [];
@@ -2,7 +2,7 @@
2
2
  // is near whom) and the convex hull (how much ground the team covers). Written for this package from the plain geometry of team.geometry.ts;
3
3
  // the ideas are those of mplsoccer's Voronoi, Delaunay and convex-hull examples (MIT), and no code of it is used.
4
4
  import { num } from "./format.js";
5
- import { opacityOf, newMade, toArray, unitOfLayer } from "./layers.env.js";
5
+ import { markAttrs, opacityOf, newMade, toArray, unitOfLayer } from "./layers.env.js";
6
6
  import { attrs, dots, frameNote, squareWord, unitWord, groupByTeam, paintOf, placeAll, polygonPath, round, teamClass, teamKey, teamLabel, titled, labelOf } from "./team.common.js";
7
7
  import { convexHull, delaunay, polygonArea, polygonCentroid, triangulationEdges, voronoiCells } from "./team.geometry.js";
8
8
  import { fill } from "./strings.js";
@@ -72,10 +72,10 @@ export function drawVoronoi(layer, env) {
72
72
  if (c.cells[i].length === 0)
73
73
  return;
74
74
  const { cls, css } = paintOf(p.d);
75
- out.push(titled("path", `${attrs(`ky-sp${cls === "" ? "" : ` ${cls}`}`, css)} d="${polygonPath(c.cells[i])}"`, `${[who(p), teamKey(p.d.team) === "" ? "" : teamLabel(teamKey(p.d.team), env)].filter((t) => t !== "").join(", ")}: ${round(100 * c.shares[i])}%`));
75
+ out.push(titled("path", `${attrs(`ky-sp${cls === "" ? "" : ` ${cls}`}`, css)} d="${polygonPath(c.cells[i])}"${markAttrs(env, p.d.id, p.index, [p.x, p.y])}`, `${[who(p), teamKey(p.d.team) === "" ? "" : teamLabel(teamKey(p.d.team), env)].filter((t) => t !== "").join(", ")}: ${round(100 * c.shares[i])}%`));
76
76
  });
77
77
  if (layer.points !== false)
78
- out.push(dots(c.players, env.base * 0.55, () => undefined));
78
+ out.push(dots(c.players, env.base * 0.55, () => undefined, env));
79
79
  made.marks = out.join("");
80
80
  made.title = fill(words.voronoiTitle, { n: c.players.length });
81
81
  const list = [...c.teams].sort((a, b) => b.share - a.share).map((t) => fill(words.shareItem, { name: teamLabel(t.team, env), pct: round(100 * t.share) })).join(env.words.layers.listSep);
@@ -140,7 +140,7 @@ export function drawDelaunay(layer, env) {
140
140
  out.push(titled("path", `${attrs(`ky-sp ky-sp-e${cls === "" ? "" : ` ${cls}`}`, "")} d="${g.edges.map(([a, b]) => `M${num(a[0])} ${num(a[1])}L${num(b[0])} ${num(b[1])}`).join("")}"`, tip));
141
141
  }
142
142
  if (layer.points !== false)
143
- out.push(dots(c.all, env.base * 0.55, () => undefined));
143
+ out.push(dots(c.all, env.base * 0.55, () => undefined, env));
144
144
  made.marks = out.join("");
145
145
  made.title = fill(words.delaunayTitle, { n: c.all.length });
146
146
  const triangles = c.groups.reduce((s, g) => s + g.triangles.length, 0);
@@ -193,7 +193,7 @@ export function drawHull(layer, env) {
193
193
  out.push(titled("path", `${attrs(`ky-sp ky-sp-e${cls === "" ? "" : ` ${cls}`}`, "")} d="M${num(h.corners[0][0])} ${num(h.corners[0][1])}L${num(h.corners[1][0])} ${num(h.corners[1][1])}"`, tip));
194
194
  }
195
195
  if (layer.points !== false)
196
- out.push(dots(c.all, env.base * 0.55, () => undefined));
196
+ out.push(dots(c.all, env.base * 0.55, () => undefined, env));
197
197
  made.marks = out.join("");
198
198
  made.title = fill(words.hullTitle, { n: c.all.length });
199
199
  const parts = [c.hulls.map((h) => fill(words.hullItem, { name: teamLabel(h.team, env), n: h.players.length, area: Math.round(h.area), unit: squareWord(c.unit, env) })).join(env.words.layers.listSep) + env.words.said.stop];
@@ -140,9 +140,11 @@ export interface FormationPosition {
140
140
  id: string;
141
141
  x: number;
142
142
  y: number;
143
+ /** How big the mark of a lineup is here, against the others: 1 by default. A second line's marks are smaller. */
144
+ size?: number;
143
145
  }
144
146
  /** The sports that have formations. */
145
- export type FormationSport = "soccer" | "nhl" | "nba" | "nfl" | "mlb";
147
+ export type FormationSport = "soccer" | "nhl" | "nba" | "nfl" | "mlb" | "rugby" | "afl" | "cricket" | "volleyball" | "handball" | "waterpolo" | "fieldhockey" | "netball";
146
148
  /** A formation: where each position stands. Points in the numbers of a registered frame, for a team that attacks the right end of the surface as it is drawn wide (a football formation: the offence going right, with the ball at midfield). */
147
149
  export interface Formation {
148
150
  id: string;
@@ -159,6 +161,8 @@ export interface Formation {
159
161
  ja: string;
160
162
  };
161
163
  positions: readonly FormationPosition[];
164
+ /** Places beside the surface's play that a lineup lists but the side does not field: baseball's designated hitter. A `formation` layer leaves them out; a `lineup` draws them. */
165
+ extra?: readonly FormationPosition[];
162
166
  }
163
167
  /** A player to stand on a position. */
164
168
  export interface FormationPlayer extends MarkDatum {
@@ -186,4 +190,39 @@ export interface FormationLayer extends LayerBase {
186
190
  los?: number;
187
191
  opacity?: number;
188
192
  }
193
+ /** A player of a lineup, placed by a position's code: nothing else about where he stands is needed. */
194
+ export interface LineupPlayer extends MarkDatum {
195
+ /** The position's code: `GK`, `SS`, `C`, `QB`, `9`, `P4` (a volleyball court's zone). The formation's own ids, and the usual short ones (`CB` for a centre-back of either side, `D` for a defenceman, `OF` for an outfielder), as `LINEUP_CODES` lists. */
196
+ pos: string | number;
197
+ name?: string;
198
+ number?: string | number;
199
+ /** A flag or crest for a small badge on the mark: an SVG as text, an image `data:` address or a path or address of an image. The package draws no flag of its own. */
200
+ flag?: string;
201
+ /** The captain wears an armband: a small "C" on the mark. */
202
+ captain?: boolean;
203
+ /** 1 by default. Hockey's second line is `line: 2` with the same codes. */
204
+ line?: number;
205
+ }
206
+ /** A team's lineup drawn on the surface: its players placed by position code in a named formation, as discs or jerseys, each with its number in the mark and its name under it. Two teams are two layers, one for each `side`. A lineup is not cut to the surface's outline (a fielder at the foul line keeps his whole jersey) unless `clip` is `"surface"`. */
207
+ export interface LineupLayer extends LayerBase {
208
+ kind: "lineup";
209
+ /** `home` (the default) or `away`: the team's colour, and the end it attacks (the right for `home`, the left for `away`) unless `attacking` says. A football lineup's `side` is only the colour: the offence goes right unless `attacking` says, and a defence faces it. Baseball's fielders do not turn. */
210
+ side?: "home" | "away";
211
+ /** The id of a formation in `FORMATIONS` (`4-2-3-1`), or a formation of your own. Without it, the one of the surface's sport that places the most of the players, the first of its sport's table on a tie. */
212
+ formation?: string | Formation;
213
+ players: readonly LineupPlayer[];
214
+ /** `disc` (the default) or `jersey`. */
215
+ shape?: "disc" | "jersey";
216
+ /** What is written in a mark: the shirt `number` (the default), the `position`'s code, the `name` or `none`. */
217
+ labels?: "number" | "position" | "name" | "none";
218
+ /** Write each name under its mark. On by default. */
219
+ names?: boolean;
220
+ /** The width of a mark, in the layer's units. A share of the surface's longer side by default. */
221
+ size?: number;
222
+ /** A volleyball lineup is listed in the serving order, `P1` to `P6`; `rotation` (1 to 6) is how many rotations on: each player moves one place clockwise. 1 by default. */
223
+ rotation?: number;
224
+ /** The line of scrimmage of a football lineup, as for a `formation` layer. */
225
+ los?: number;
226
+ opacity?: number;
227
+ }
189
228
  export {};
@@ -0,0 +1,77 @@
1
+ /** The sport a tenant plays: what decides the surface a place offers for it (`surfaceOf`). */
2
+ export type TenantSport = "baseball" | "soccer" | "american-football" | "canadian-football" | "basketball" | "ice-hockey" | "rugby-union" | "rugby-league" | "australian-football" | "lacrosse" | "cricket";
3
+ /** The article of English Wikipedia, at a revision, that a place's tenants were read from. */
4
+ export interface TenantArticle {
5
+ title: string;
6
+ revision: number;
7
+ }
8
+ /** Where a tenant was read. */
9
+ export interface TenantSource {
10
+ /** The Wikidata statement that names the club as a tenant (CC0): `P466` (the place lists the club as an occupant), `P115` (the club lists the place as its home venue), or both. Absent where only the article names it. */
11
+ wikidata?: "P466" | "P115" | "P466 P115";
12
+ /** The article and revision of English Wikipedia (CC BY-SA 4.0) whose `tenants` line in the place's infobox lists the club. Absent where only Wikidata names it. */
13
+ wikipedia?: TenantArticle;
14
+ /** Where the years were read: Wikidata's qualifiers (start time P580, end time P582), or the article. Absent when neither gives any. */
15
+ years?: "wikidata" | "wikipedia";
16
+ }
17
+ /** Why a club's name in Japanese is not Wikidata's label: the label is a transliteration of an abbreviation (`abbreviation`), of a formal name or a suffix such as FC that Japanese media do not use (`formal`), a page title with a disambiguator or "history of" (`title`), or the club's name in another period (`era`). */
18
+ export type TenantNameKind = "abbreviation" | "formal" | "title" | "era";
19
+ /** The everyday Japanese form of a club's name where it is not Wikidata's label, and the source of it. */
20
+ export interface TenantNameJa {
21
+ /** The name Japanese media use, which the atlas shows as `clubJa`. */
22
+ ja: string;
23
+ kind: TenantNameKind;
24
+ /** What was checked, where and when. */
25
+ note: string;
26
+ }
27
+ /** A tenant as the data gives it. */
28
+ export interface TenantData {
29
+ club: string;
30
+ clubJa?: string;
31
+ aliasesJa?: string[];
32
+ league: string;
33
+ sport: TenantSport;
34
+ from?: number;
35
+ to?: number;
36
+ wikidata: string;
37
+ statements?: "P466" | "P115" | "P466 P115";
38
+ /** The place's article lists the club in its tenants line. */
39
+ listed?: true;
40
+ years?: "wikidata" | "wikipedia";
41
+ }
42
+ /** A club that plays, or played, its home matches in a place, and when. */
43
+ export interface Tenant {
44
+ /** The club's name in English: the one the place's article gives it (`Montreal Expos`, `Boston Braves` for a club named differently then), else Wikidata's label. */
45
+ club: string;
46
+ /** The club's name in Japanese as Japanese media say it (`BCライオンズ`), where there is one for this name of the club: the curated form in `TENANT_NAMES_JA` if the club has one, else Wikidata's label. */
47
+ clubJa?: string;
48
+ /** Other names the club is found by in Japanese: Wikidata's label where `clubJa` is the curated form instead (`ブリティッシュ・コロンビア・ライオンズ`). */
49
+ aliasesJa?: readonly string[];
50
+ /** The league it played in there: `MLB`, `CFL`, `MLS`, `NPB`, `Premier League`... A league that has had several names or several seasons of the same name has one (`NASL` is the league of 1968 to 1984). */
51
+ league: string;
52
+ sport: TenantSport;
53
+ /** The first year it played there. Not given where neither source gives it. */
54
+ from?: number;
55
+ /** The last year it played there: a club still there has none. */
56
+ to?: number;
57
+ /** The club's Wikidata item (`Q919062`), the key to join a tenant to other data. */
58
+ wikidata: string;
59
+ source: TenantSource;
60
+ }
61
+ /** The tenants of every place that has any, by the place's key: the current ones first, by the year they came, then those that have left, the latest to leave first. */
62
+ export declare const TENANTS: Readonly<Record<string, readonly Tenant[]>>;
63
+ /** The leagues of a list of tenants, each once, in the order they first come. */
64
+ export declare const leaguesOf: (tenants: readonly Tenant[]) => string[];
65
+ /** True when a tenant played there in `year`; a year the data does not give for a bound does not shut it out. */
66
+ export declare const playedIn: (tenant: Tenant, year: number) => boolean;
67
+ /** The draw sport a tenant's sport is drawn as, and any league of its rulebook: the field of play a club's sport asks for. `undefined` where the package draws none. */
68
+ export declare const SURFACE_OF_SPORT: Readonly<Record<TenantSport, {
69
+ sport: "mlb" | "soccer" | "nfl" | "cfl" | "nba" | "nhl" | "rugby" | "afl" | "cricket";
70
+ league?: "nrl";
71
+ } | undefined>>;
72
+ /** True when the package draws a surface for the tenant's sport and league. */
73
+ export declare const hasSurface: (tenant: Tenant) => boolean;
74
+ /** The clubs whose name in Japanese is the everyday form and not Wikidata's label, by Wikidata item, with why and the source of each: `nameJaOf(tenant)` finds a tenant's. */
75
+ export declare const TENANT_JAPANESE_NAMES: Readonly<Record<string, TenantNameJa>>;
76
+ /** The note on a tenant's name in Japanese, if its everyday form is a curated one. */
77
+ export declare const nameJaOf: (tenant: Pick<Tenant, "wikidata">) => TenantNameJa | undefined;
@@ -0,0 +1,8 @@
1
+ import type { TenantArticle, TenantData, TenantNameJa } from "./tenants.ts";
2
+ /** The article of English Wikipedia, at the revision read, that the tenants of a place are listed in. */
3
+ export declare const TENANT_ARTICLES: Readonly<Record<string, TenantArticle>>;
4
+ /** The leagues whose field or court is not the one the package draws for their sport (arena football, a basketball league that played on another court): their clubs ask for no surface. */
5
+ export declare const TENANT_NO_SURFACE: readonly string[];
6
+ /** The clubs whose name in Japanese is not Wikidata's label, by the club's Wikidata item: the everyday form, why Wikidata's label is not it, and the source of each. See scripts/tenants.names-ja.json. */
7
+ export declare const TENANT_NAMES_JA: Readonly<Record<string, TenantNameJa>>;
8
+ export declare const TENANT_DATA: Readonly<Record<string, TenantData[]>>;