@johnmorrisdotca/kyogijo-stadiums 0.5.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 (193) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/NOTICE.md +179 -3
  3. package/README.md +43 -11
  4. package/dist/arenas-entry.d.ts +4 -0
  5. package/dist/arenas-entry.js +3 -0
  6. package/dist/arenas.d.ts +125 -0
  7. package/dist/arenas.data.d.ts +2 -0
  8. package/dist/arenas.data.js +3763 -0
  9. package/dist/arenas.js +72 -0
  10. package/dist/atlas-entry.d.ts +3 -1
  11. package/dist/atlas-entry.js +3 -2
  12. package/dist/atlas.d.ts +37 -19
  13. package/dist/atlas.js +92 -7
  14. package/dist/background.js +2 -1
  15. package/dist/budo/describe.d.ts +13 -0
  16. package/dist/budo/describe.js +120 -0
  17. package/dist/budo/dojo.api.d.ts +64 -0
  18. package/dist/budo/dojo.api.js +46 -0
  19. package/dist/budo/dojo.d.ts +72 -0
  20. package/dist/budo/dojo.js +300 -0
  21. package/dist/budo/judo.d.ts +61 -0
  22. package/dist/budo/judo.js +109 -0
  23. package/dist/budo/karate.d.ts +29 -0
  24. package/dist/budo/karate.js +80 -0
  25. package/dist/budo/kendo.d.ts +18 -0
  26. package/dist/budo/kendo.js +44 -0
  27. package/dist/budo/kimarite.d.ts +19 -0
  28. package/dist/budo/kimarite.js +33 -0
  29. package/dist/budo/kit.d.ts +16 -0
  30. package/dist/budo/kit.js +24 -0
  31. package/dist/budo/kyudo.d.ts +28 -0
  32. package/dist/budo/kyudo.js +66 -0
  33. package/dist/budo/looks.d.ts +28 -0
  34. package/dist/budo/looks.js +58 -0
  35. package/dist/budo/parts.d.ts +32 -0
  36. package/dist/budo/parts.js +10 -0
  37. package/dist/budo/plan.d.ts +64 -0
  38. package/dist/budo/plan.js +139 -0
  39. package/dist/budo/room.d.ts +133 -0
  40. package/dist/budo/room.js +111 -0
  41. package/dist/budo/sumo.d.ts +31 -0
  42. package/dist/budo/sumo.js +71 -0
  43. package/dist/budo/tatami.d.ts +55 -0
  44. package/dist/budo/tatami.js +156 -0
  45. package/dist/budo/types.d.ts +192 -0
  46. package/dist/budo/types.js +2 -0
  47. package/dist/budo-entry.d.ts +22 -0
  48. package/dist/budo-entry.js +13 -0
  49. package/dist/budo.board.d.ts +28 -0
  50. package/dist/budo.board.js +81 -0
  51. package/dist/budo.score.d.ts +18 -0
  52. package/dist/budo.score.js +52 -0
  53. package/dist/budo.style.d.ts +6 -0
  54. package/dist/budo.style.js +67 -0
  55. package/dist/charts.baseball.js +1 -1
  56. package/dist/charts.js +4 -4
  57. package/dist/charts.soccer.js +3 -3
  58. package/dist/charts.types.d.ts +10 -0
  59. package/dist/colour.d.ts +4 -4
  60. package/dist/colour.js +4 -4
  61. package/dist/crowd.d.ts +11 -1
  62. package/dist/crowd.draw.js +15 -9
  63. package/dist/crowd.js +17 -2
  64. package/dist/draw.js +156 -31
  65. package/dist/element-entry.d.ts +2 -0
  66. package/dist/element-entry.js +1 -0
  67. package/dist/element.d.ts +2 -2
  68. package/dist/element.js +2 -1
  69. package/dist/emblems-source.d.ts +50 -0
  70. package/dist/emblems-source.js +56 -0
  71. package/dist/eras.d.ts +3 -1
  72. package/dist/eras.js +8 -3
  73. package/dist/features.js +73 -0
  74. package/dist/field.data.d.ts +16 -0
  75. package/dist/field.data.js +50 -0
  76. package/dist/fields.kit.d.ts +2 -2
  77. package/dist/fields.types.d.ts +9 -3
  78. package/dist/formations.js +18 -7
  79. package/dist/formations.sports.d.ts +8 -0
  80. package/dist/formations.sports.js +251 -0
  81. package/dist/frames.providers.js +9 -1
  82. package/dist/halls.d.ts +65 -0
  83. package/dist/halls.js +122 -0
  84. package/dist/heat.js +17 -10
  85. package/dist/heat.zones.d.ts +4 -2
  86. package/dist/heat.zones.js +55 -3
  87. package/dist/homeside.js +7 -0
  88. package/dist/index.d.ts +15 -3
  89. package/dist/index.js +8 -1
  90. package/dist/layers-entry.d.ts +2 -1
  91. package/dist/layers-entry.js +1 -0
  92. package/dist/layers.d.ts +2 -1
  93. package/dist/layers.env.d.ts +10 -1
  94. package/dist/layers.env.js +33 -3
  95. package/dist/layers.js +30 -11
  96. package/dist/layers.types.d.ts +9 -2
  97. package/dist/leagues/budo.d.ts +196 -0
  98. package/dist/leagues/budo.js +105 -0
  99. package/dist/leagues/hockey.js +41 -19
  100. package/dist/leagues/sources.js +21 -0
  101. package/dist/leagues/types.d.ts +1 -1
  102. package/dist/leagues.d.ts +4 -1
  103. package/dist/leagues.js +102 -2
  104. package/dist/lineup.codes.d.ts +23 -0
  105. package/dist/lineup.codes.js +122 -0
  106. package/dist/logo.d.ts +25 -0
  107. package/dist/logo.js +47 -0
  108. package/dist/outline.d.ts +1 -1
  109. package/dist/outline.js +17 -3
  110. package/dist/pageant.js +7 -3
  111. package/dist/pointer-entry.d.ts +2 -0
  112. package/dist/pointer-entry.js +2 -0
  113. package/dist/pointer.d.ts +113 -0
  114. package/dist/pointer.js +151 -0
  115. package/dist/pool.d.ts +7 -7
  116. package/dist/pool.figures.d.ts +57 -55
  117. package/dist/pool.figures.js +59 -55
  118. package/dist/pool.js +7 -7
  119. package/dist/pool.labels.js +2 -1
  120. package/dist/ranges.names.js +6 -0
  121. package/dist/road.draw.js +3 -2
  122. package/dist/road.js +2 -1
  123. package/dist/road.types.d.ts +3 -0
  124. package/dist/rules.d.ts +138 -0
  125. package/dist/rules.js +511 -0
  126. package/dist/rules.types.d.ts +4 -0
  127. package/dist/rules.types.js +64 -0
  128. package/dist/score.d.ts +3 -0
  129. package/dist/score.js +23 -1
  130. package/dist/situation.d.ts +135 -0
  131. package/dist/situation.draw.d.ts +34 -0
  132. package/dist/situation.draw.js +202 -0
  133. package/dist/situation.js +337 -0
  134. package/dist/situation.style.d.ts +2 -0
  135. package/dist/situation.style.js +45 -0
  136. package/dist/situation.types.d.ts +77 -0
  137. package/dist/situation.types.js +3 -0
  138. package/dist/situation.widgets.d.ts +39 -0
  139. package/dist/situation.widgets.js +129 -0
  140. package/dist/strings.d.ts +175 -1
  141. package/dist/strings.js +364 -20
  142. package/dist/style.js +9 -2
  143. package/dist/surfaces/artistic.d.ts +3 -3
  144. package/dist/surfaces/artistic.js +3 -3
  145. package/dist/surfaces/baseball.js +2 -0
  146. package/dist/surfaces/basketball.js +1 -1
  147. package/dist/surfaces/budo.d.ts +6 -0
  148. package/dist/surfaces/budo.js +104 -0
  149. package/dist/surfaces/dive.d.ts +3 -3
  150. package/dist/surfaces/dive.js +3 -3
  151. package/dist/surfaces/football.js +7 -0
  152. package/dist/surfaces/hockey.js +3 -2
  153. package/dist/surfaces/polo.d.ts +4 -4
  154. package/dist/surfaces/polo.js +5 -5
  155. package/dist/surfaces/pool.d.ts +1 -1
  156. package/dist/surfaces/pool.js +3 -3
  157. package/dist/surfaces/surface.d.ts +32 -0
  158. package/dist/surfaces/venue.d.ts +1 -1
  159. package/dist/surfaces/venue.js +1 -1
  160. package/dist/target-entry.d.ts +2 -0
  161. package/dist/target-entry.js +2 -0
  162. package/dist/target.d.ts +200 -0
  163. package/dist/target.js +525 -0
  164. package/dist/team.common.d.ts +1 -1
  165. package/dist/team.common.js +3 -3
  166. package/dist/team.formation.d.ts +3 -1
  167. package/dist/team.formation.js +3 -3
  168. package/dist/team.layers.d.ts +2 -2
  169. package/dist/team.layers.js +4 -1
  170. package/dist/team.lineup.d.ts +27 -0
  171. package/dist/team.lineup.js +207 -0
  172. package/dist/team.network.js +6 -5
  173. package/dist/team.shape.js +5 -5
  174. package/dist/team.types.d.ts +40 -1
  175. package/dist/tenants.data.js +848 -0
  176. package/dist/turn-entry.d.ts +2 -2
  177. package/dist/turn-entry.js +1 -1
  178. package/dist/turn.camera.d.ts +12 -1
  179. package/dist/turn.camera.js +18 -2
  180. package/dist/turn.dom.js +11 -9
  181. package/dist/turn.kit.d.ts +2 -2
  182. package/dist/turn.style.js +4 -3
  183. package/dist/types.d.ts +43 -7
  184. package/dist/units.js +7 -0
  185. package/dist/uprights.budo.d.ts +17 -0
  186. package/dist/uprights.budo.js +87 -0
  187. package/dist/uprights.js +10 -1
  188. package/dist/uprights.pools.js +4 -4
  189. package/dist/version.d.ts +1 -1
  190. package/dist/version.js +1 -1
  191. package/dist/zones-entry.d.ts +2 -0
  192. package/dist/zones-entry.js +2 -0
  193. package/package.json +39 -3
package/dist/draw.js CHANGED
@@ -1,10 +1,14 @@
1
1
  import { ballparkDrawn, ballparkFor, ballparkSentences } from "./ballpark.js";
2
- import { inkOn, safeColour, safeLogo } from "./colour.js";
2
+ import { inkOn, safeColour } from "./colour.js";
3
+ import { withEmblems } from "./emblems-source.js";
4
+ import { safeLogo } from "./logo.js";
3
5
  import { drawingId, feetFence, isPortrait, lengthUnit, safeClass } from "./common.js";
4
6
  import { el, esc, num } from "./format.js";
7
+ import { fieldAttributes } from "./field.data.js";
5
8
  import { ERA_SPORTS, footballIn, rulesFor, seasonLabel } from "./eras.js";
6
9
  import { FOOTBALL } from "./geometry.js";
7
10
  import { renderLayers } from "./layers.js";
11
+ import { arenaById, arenaCourtIn, arenaNameIn, arenaOfficialName, arenaRinkIn } from "./arenas.js";
8
12
  import { groundById, groundNameIn, groundOfficialName } from "./grounds.js";
9
13
  import { BOARD, courtFor, hasSetsBoard, isNetLeague, isNetSport } from "./court.js";
10
14
  import { cricketBoardMarkup } from "./cricket.board.js";
@@ -12,6 +16,16 @@ import { athleticsPlan } from "./athletics/plan.js";
12
16
  import { skatingPlan } from "./skating/plan.js";
13
17
  import { skatingAbout, skatingSentences } from "./skating/describe.js";
14
18
  import { skatingStyle } from "./skating.style.js";
19
+ import { budoPlan, isBudoSport } from "./budo/plan.js";
20
+ import { dojoSpecOf } from "./budo/dojo.js";
21
+ import { dojoGallery } from "./budo/room.js";
22
+ import { budoAbout, budoScoreSentences, budoSentences } from "./budo/describe.js";
23
+ import { lookOf as budoLookOf } from "./budo/looks.js";
24
+ import { kimariteName } from "./budo/kimarite.js";
25
+ import { budoScoreText, budoView } from "./budo.score.js";
26
+ import { KYOGIJO_BUDO_GALLERY_STYLE, budoStyle } from "./budo.style.js";
27
+ import { budoBoardMarkup } from "./budo.board.js";
28
+ import { budoSurface } from "./surfaces/budo.js";
15
29
  import { skatingSurface } from "./surfaces/skating.js";
16
30
  import { athleticsAbout, athleticsSentences } from "./athletics/describe.js";
17
31
  import { athleticsStyle, athleticsTrackRules } from "./athletics.style.js";
@@ -48,6 +62,9 @@ import { featureColours, featureStyle } from "./features.js";
48
62
  import { isLook, lookStyle } from "./look.js";
49
63
  import { lineLook, restyleLines } from "./restyle.js";
50
64
  import { KYOGIJO_MARK_STYLE } from "./mark.js";
65
+ import { readSituation, situationSentences } from "./situation.js";
66
+ import { badgeSituation, drawSituation } from "./situation.draw.js";
67
+ import { KYOGIJO_SITUATION_STYLE } from "./situation.style.js";
51
68
  import { outlineOf } from "./outline.js";
52
69
  import { autoScore, pageantLayout, pageantOf, ratioOf } from "./pageant.js";
53
70
  import { emblemMarkup, picturesMarkup } from "./pageant.draw.js";
@@ -61,6 +78,8 @@ import { fieldSurface } from "./surfaces/field.js";
61
78
  import { basketball } from "./surfaces/basketball.js";
62
79
  import { football } from "./surfaces/football.js";
63
80
  import { hockey } from "./surfaces/hockey.js";
81
+ import { NHL_RINK } from "./leagues/hockey.js";
82
+ import { NBA_COURT } from "./leagues/basketball.js";
64
83
  import { soccer } from "./surfaces/soccer.js";
65
84
  import { frameMatrix, layoutFromFrame, resolveFrame } from "./surfaces/soccer-providers.js";
66
85
  import { composeSpecial, hostSport } from "./special.js";
@@ -71,7 +90,7 @@ import { abbreviate, capitals } from "./text.js";
71
90
  import { VERSION } from "./version.js";
72
91
  export { isPortrait };
73
92
  /** The sports that can be drawn, in the order a menu lists them. */
74
- export const SPORTS = ["nfl", "cfl", "ifaf", "nhl", "nba", "mlb", "soccer", "curling", "tennis", "volleyball", "badminton", "pickleball", "tabletennis", "padel", "squash", "racquetball", "sepaktakraw", "teqball", "roundnet", "netball", "swimming", "waterpolo", "diving", "artisticswimming", "athletics", "skating", "lacrosse", "rugby", "afl", "gaelic", "cricket", "fieldhockey", "handball"];
93
+ export const SPORTS = ["nfl", "cfl", "ifaf", "nhl", "nba", "mlb", "soccer", "curling", "tennis", "volleyball", "badminton", "pickleball", "tabletennis", "padel", "squash", "racquetball", "sepaktakraw", "teqball", "roundnet", "netball", "swimming", "waterpolo", "diving", "artisticswimming", "athletics", "skating", "judo", "karate", "kendo", "sumo", "kyudo", "dojo", "lacrosse", "rugby", "afl", "gaelic", "cricket", "fieldhockey", "handball"];
75
94
  /** The CSS custom property each colour of `Colors` sets. */
76
95
  export const COLOR_PROPERTIES = {
77
96
  apron: "--ky-apron",
@@ -164,8 +183,8 @@ export function winnerOf(o) {
164
183
  const mode = o.winner ?? "auto";
165
184
  if (mode === "home" || mode === "away" || mode === "none")
166
185
  return mode;
167
- const h = fieldTotal(o.sport, o.home) ?? numeric(o.home.score);
168
- const a = fieldTotal(o.sport, o.away) ?? numeric(o.away.score);
186
+ const h = fieldTotal(o.sport, o.home) ?? numeric(o.home.score) ?? (o.sport === undefined ? undefined : numeric(budoScoreText(o.sport, o.home)));
187
+ const a = fieldTotal(o.sport, o.away) ?? numeric(o.away.score) ?? (o.sport === undefined ? undefined : numeric(budoScoreText(o.sport, o.away)));
169
188
  if (h === undefined || a === undefined)
170
189
  return "none";
171
190
  if (mode === "auto" && !(o.status !== undefined && FINAL.test(o.status.trim())))
@@ -242,6 +261,11 @@ function buildSkating(o, common) {
242
261
  const plan = skatingPlan({ ...o, detail: common.detail });
243
262
  return skatingSurface(common, plan, common.margin ?? (o.range === undefined ? 20 : 5));
244
263
  }
264
+ /** The budō surface: its plan, drawn as it lies. A hall, a kyūdōjō and a range of mats have little ground round them. */
265
+ function buildBudo(o, common) {
266
+ const plan = budoPlan({ ...o, detail: common.detail });
267
+ return budoSurface(common, plan, common.margin ?? (o.range === undefined ? 12 : 4));
268
+ }
245
269
  /** One text for both ends or sides, or a pair of them. */
246
270
  const pair = (text) => (text === undefined ? undefined : typeof text === "string" ? [text, text] : text);
247
271
  /** The surface the options ask for, whole: before it is cropped to a `range`. */
@@ -278,10 +302,19 @@ function buildWhole(o, portrait, id, score, scored, extra) {
278
302
  const season = rulesFor(sport, o.year).rules;
279
303
  return football(footballIn(sport, o.year), common, { hashMarks: season.hash !== null, postShape: season.postShape });
280
304
  }
281
- case "nhl":
305
+ case "nhl": {
306
+ // An arena's rink is the size its sources publish, with the markings of the season asked for; an arena with none published has the NHL's.
307
+ const sized = league === undefined ? arenaRinkIn(arenaById(o.arena), o.year) : undefined;
308
+ if (sized !== undefined)
309
+ return hockey(common, rulesFor("nhl", o.year).rules, { ...NHL_RINK, length: sized.length, width: sized.width });
282
310
  return league?.family === "hockey" ? hockey(common, undefined, rinkOf(league, o.year)) : hockey(common, rulesFor("nhl", o.year).rules);
283
- case "nba":
311
+ }
312
+ case "nba": {
313
+ const sized = league === undefined ? arenaCourtIn(arenaById(o.arena), o.year) : undefined;
314
+ if (sized !== undefined)
315
+ return basketball(common, rulesFor("nba", o.year).rules, { ...NBA_COURT, length: sized.length, width: sized.width });
284
316
  return league?.family === "basketball" ? basketball(common, undefined, courtOf(league, o.year)) : basketball(common, rulesFor("nba", o.year).rules);
317
+ }
285
318
  case "mlb": {
286
319
  if (league?.family === "baseball") {
287
320
  const diamond = diamondOf(league, o.year);
@@ -333,6 +366,13 @@ function buildWhole(o, portrait, id, score, scored, extra) {
333
366
  return buildAthletics({ ...o, sport }, common);
334
367
  case "skating":
335
368
  return buildSkating({ ...o, sport }, common);
369
+ case "judo":
370
+ case "karate":
371
+ case "kendo":
372
+ case "sumo":
373
+ case "kyudo":
374
+ case "dojo":
375
+ return buildBudo({ ...o, sport }, common);
336
376
  default:
337
377
  throw new Error(`Unknown sport "${String(o.sport)}": use one of ${SPORTS.join(", ")}`);
338
378
  }
@@ -382,7 +422,10 @@ function standsFor(o, surface, crowd) {
382
422
  const walls = park === undefined || crowd.wallSeats === false ? [] : parkWallsIn(park, o.year).filter((w) => (crowd.wallSeats === true ? w.height_ft >= 25 : w.label !== undefined && WALL_SEATS[park.id] === w.label));
383
423
  return parkLayout({ fence: fenceOutline(points), backstop, crowd, seatsOnWalls: walls.map((w) => ({ from: w.from, to: w.to, height: w.height_ft })) });
384
424
  }
385
- return crowdLayout({ sport, box: surface.whole ?? surface.box ?? { x: 0, y: 0, w: 100, h: 100 }, crowd, awaySide, ground: o.ground });
425
+ // A dojo's crowd sits in its gallery, from the room's balcony data (in feet here); the arenas (the Budokan, the Kokugikan) keep their bowls.
426
+ const hall = sport === "dojo" ? dojoGallery(o) : undefined;
427
+ const gallery = hall === undefined ? undefined : { offset: hall.offset / 0.3048, depth: hall.depth / 0.3048, height: [hall.height[0] / 0.3048, hall.height[1] / 0.3048] };
428
+ return crowdLayout({ sport, box: surface.whole ?? surface.box ?? { x: 0, y: 0, w: 100, h: 100 }, crowd, awaySide, ground: o.ground, arena: o.budo?.hall === "budokan", ...(gallery === undefined ? {} : { gallery }) });
386
429
  }
387
430
  /** True when the drawing is asked to be small: `detail: "simple"` or a `width` under 480 pixels. */
388
431
  const smallDrawing = (o) => {
@@ -408,7 +451,9 @@ function buildCrowded(o, portrait, id, score, scored, extra, crowd) {
408
451
  const fine = crowd.detail === "fine" || (crowd.detail === "auto" && !smallDrawing(o));
409
452
  const first = /^<[^>]*\/>/.exec(surface.base)?.[0] ?? "";
410
453
  const bg = /class="([^"]*)"/.exec(first)?.[1] ?? "ky-apron";
411
- const drawn = drawCrowd(stands, { id, crowd, density: densityOf(crowd, seats, ground), fine, ground: bg });
454
+ // A gallery is sparsely taken unless the options say how full.
455
+ const taken = stands.gallery === true && crowd.density === undefined && crowd.attendance === undefined ? 0.4 : densityOf(crowd, seats, ground);
456
+ const drawn = drawCrowd(stands, { id, crowd, density: taken, fine, ground: bg });
412
457
  surface.base = first + drawn.markup + surface.base.slice(first.length);
413
458
  surface.defs += drawn.defs;
414
459
  surface.lines += drawn.over;
@@ -425,6 +470,13 @@ function colourStyle(o, teams, features) {
425
470
  };
426
471
  for (const [key, property] of Object.entries(COLOR_PROPERTIES))
427
472
  set(property, o.colors?.[key]);
473
+ // A dojo's own colours (and its era's): the mats, the second tone, the border, the heri, the wood and the walls.
474
+ if (o.sport === "dojo" && o.dojo !== undefined) {
475
+ const c = dojoSpecOf(o.dojo).colours;
476
+ if (c !== undefined)
477
+ for (const [key, property] of [["mat", "--ky-surface"], ["alt", "--ky-surface-alt"], ["border", "--ky-red"], ["heri", "--ky-bud-heri"], ["floor", "--ky-bud-floor"], ["wall", "--ky-boards"]])
478
+ set(property, c[key]);
479
+ }
428
480
  if (o.court !== undefined)
429
481
  for (const [key, property] of Object.entries(COURT_PROPERTIES))
430
482
  set(property, o.court[key]);
@@ -479,17 +531,18 @@ function roundOf(team, markShape) {
479
531
  return undefined;
480
532
  return { round: true, fit: team.markFit === "contain" ? "contain" : undefined, color: safeColour(team.color), ink: safeColour(team.textColor) };
481
533
  }
482
- function teamView(team, side, role, winner, uppercase, sport, markShape) {
534
+ function teamView(team, side, role, winner, uppercase, sport, markShape, theme) {
483
535
  const name = String(team.name ?? "");
484
- const written = fieldScoreText(sport, team);
536
+ const abbr = (team.abbr !== undefined && team.abbr.trim() !== "" ? team.abbr.trim() : abbreviate(name)).slice(0, 5);
537
+ const written = fieldScoreText(sport, team) ?? budoScoreText(sport, team);
485
538
  const score = written ?? (team.score === undefined || team.score === null || String(team.score).trim() === "" ? "–" : String(team.score));
486
539
  return {
487
540
  side,
488
541
  role,
489
542
  name: uppercase ? capitals(name) : name,
490
- abbr: (team.abbr !== undefined && team.abbr.trim() !== "" ? team.abbr.trim() : abbreviate(name)).slice(0, 5),
543
+ abbr,
491
544
  score,
492
- logo: safeLogo(team.logo),
545
+ logo: safeLogo(team.logo, { color: team.color, accent: team.textColor, initials: abbr.slice(0, 3), theme }),
493
546
  ...roundOf(team, markShape),
494
547
  state: stateOf(winner, role),
495
548
  ends: Array.isArray(team.ends) ? team.ends.map(endText) : undefined,
@@ -500,6 +553,7 @@ function teamView(team, side, role, winner, uppercase, sport, markShape) {
500
553
  serving: team.serving === true ? true : undefined,
501
554
  wickets: sport === "cricket" && team.wickets !== undefined && team.wickets !== null && String(team.wickets).trim() !== "" ? String(team.wickets).trim().slice(0, 2) : undefined,
502
555
  overs: sport === "cricket" && team.overs !== undefined && team.overs !== null && String(team.overs).trim() !== "" ? String(team.overs).trim().slice(0, 6) : undefined,
556
+ budo: isBudoSport(sport) ? budoView(team.budo) : undefined,
503
557
  innings: sport === "cricket" && Array.isArray(team.innings) ? team.innings.map((value) => (typeof value === "number" ? (Number.isFinite(value) ? String(value) : "") : typeof value === "string" ? value.trim().slice(0, 7) : "")) : undefined,
504
558
  };
505
559
  }
@@ -541,6 +595,8 @@ function leagueAbout(league, year, language, sheet) {
541
595
  const words = KYOGIJO_STRINGS[language].league;
542
596
  if (league.family === "skating")
543
597
  return skatingAbout({ sport: "skating", league: league.id }, language);
598
+ if (league.family === "judo" || league.family === "karate" || league.family === "kendo" || league.family === "sumo" || league.family === "kyudo" || league.family === "dojo")
599
+ return budoAbout({ sport: league.family, league: league.id }, language);
544
600
  if (league.family === "athletics")
545
601
  return athleticsAbout({ sport: "athletics", league: league.id, athletics: sheet === undefined ? undefined : undefined }, language);
546
602
  // The field sports: their size is the surface drawn (the one the options ask for), in the unit their rulebook prints first.
@@ -574,6 +630,28 @@ function leagueAbout(league, year, language, sheet) {
574
630
  const text = fill(words.size, { length: trimmed(convertLength(size.length, "ft", to)), width: trimmed(convertLength(size.width, "ft", to)), unit: words.units[to === "m" || to === "yd" ? to : "ft"] });
575
631
  return fill(league.family === "volleyball" && league.id === "fivb-beach" ? words.beachVolleyball : league.family === "volleyball" && league.id === "wpv-sitting" ? words.sittingVolleyball : words[league.family], { size: text });
576
632
  }
633
+ /** The football field a drawing is on, for a situation to know the yards between the goal lines and the downs a team has (the CFL's three). */
634
+ function footballFieldOf(o, sport, league) {
635
+ if (sport !== "nfl" && sport !== "cfl" && sport !== "ifaf")
636
+ return undefined;
637
+ const spec = league?.family === "football" ? fieldOf(league, o.year) : sport === "ifaf" ? FOOTBALL.ifaf : footballIn(sport, o.year);
638
+ return { yards: spec.length / 3, downs: spec.id === "cfl" ? 3 : 4 };
639
+ }
640
+ /** The situation the options ask for, read: which team attacks, which end, what is lit. `undefined` for none, for a sport that has no situation and for a special game. */
641
+ function situationOf(o) {
642
+ if (o.situation === undefined || o.host !== undefined)
643
+ return undefined;
644
+ const { sport, league } = chooseLeague(o);
645
+ const ctx = {
646
+ sport,
647
+ homeSide: resolveHomeSide({ ...o, sport }),
648
+ direction: o.direction,
649
+ football: footballFieldOf(o, sport, league),
650
+ names: isScore(o) ? { home: String(o.home.name ?? ""), away: String(o.away.name ?? "") } : undefined,
651
+ league: league?.id,
652
+ };
653
+ return readSituation(o.situation, ctx);
654
+ }
577
655
  /** A drawing's title and description, in a language. */
578
656
  export function describe(o) {
579
657
  // A special game is drawn whole: a `range` is for a surface of its own.
@@ -591,7 +669,11 @@ export function describe(o) {
591
669
  // A league other than the sport's own is named by its rulebook's, and its description is made from its figures.
592
670
  const trackGround = sport === "athletics" ? groundById(o.ground) : undefined;
593
671
  const trackGroundName = trackGround === undefined ? undefined : groundNameIn(trackGround, o.year, language);
594
- const surfaceName = trackGroundName !== undefined ? fill(words.athletics.ground, { ground: trackGroundName }) : groundName !== undefined ? (language === "ja" ? `${groundName}のピッチ` : `${groundName}, soccer pitch`) : league !== undefined && parkName === undefined ? league.name[language] : parkName === undefined ? (pitch?.format === "futsal" ? words.soccer.futsal : words.surface[sport]) : language === "ja" ? `${parkName}のグラウンド` : `${parkName}, baseball field`;
672
+ // An arena names its rink or its court.
673
+ const arena = (sport === "nhl" || sport === "nba") && league === undefined ? arenaById(o.arena) : undefined;
674
+ const arenaName = arena === undefined ? undefined : arenaNameIn(arena, o.year, language);
675
+ const arenaSize = arena === undefined || arenaName === undefined ? undefined : sport === "nhl" ? arenaRinkIn(arena, o.year) : arenaCourtIn(arena, o.year);
676
+ const surfaceName = arenaName !== undefined ? fill(sport === "nhl" ? words.said.arenaRink : words.said.arenaCourt, { arena: arenaName }) : trackGroundName !== undefined ? fill(words.athletics.ground, { ground: trackGroundName }) : groundName !== undefined ? (language === "ja" ? `${groundName}のピッチ` : `${groundName}, soccer pitch`) : league !== undefined && parkName === undefined ? league.name[language] : parkName === undefined ? (pitch?.format === "futsal" ? words.soccer.futsal : words.surface[sport]) : language === "ja" ? `${parkName}のグラウンド` : `${parkName}, baseball field`;
595
677
  const { stop, colon, gap } = words.said;
596
678
  const sentence = (text) => (/[.。]$/.test(text) ? text : `${text}${stop}`);
597
679
  const metres = (value) => String(Math.round(value * 10) / 10);
@@ -600,8 +682,10 @@ export function describe(o) {
600
682
  const field = isFieldSport(sport) ? fieldPlan({ ...o, sport }) : undefined;
601
683
  const pools = isPoolSport(sport) ? poolDescription({ sport, pool: o.pool }, language) : undefined;
602
684
  const poolLine = pools === undefined ? "" : pools.after.map((line) => `${gap}${line}`).join("");
603
- const aboutText = pools !== undefined ? pools.about : sport === "skating" ? skatingAbout(o, language) : sport === "athletics" ? athleticsAbout(o, language) : field !== undefined ? leagueAbout(league ?? LEAGUES[sport][0], o.year, language, { length: field.length, width: field.width }) : plan !== undefined ? leagueAbout(plan.league, o.year, language, plan.spec) : league !== undefined ? leagueAbout(league, o.year, language) : pitch === undefined ? words.about[sport] : fill(pitch.format === "futsal" ? words.soccer.aboutFutsal : words.about.soccer, { length: metres(pitch.length), width: metres(pitch.width) });
604
- const about = `${words.said.on}${colon}${aboutText}${stop}${sport === "athletics" ? athleticsSentences(o, language) : sport === "skating" ? skatingSentences(o, language) : ""}`;
685
+ // An arena of a size of its own says what it is at that size.
686
+ const arenaAbout = arenaSize === undefined ? undefined : fill(sport === "nhl" ? words.said.arenaRinkAbout : words.said.arenaCourtAbout, { length: metres(arenaSize.length), width: metres(arenaSize.width), lengthM: metres(arenaSize.length * 0.3048), widthM: metres(arenaSize.width * 0.3048) });
687
+ const aboutText = arenaAbout !== undefined ? arenaAbout : pools !== undefined ? pools.about : isBudoSport(sport) ? budoAbout(o, language) : sport === "skating" ? skatingAbout(o, language) : sport === "athletics" ? athleticsAbout(o, language) : field !== undefined ? leagueAbout(league ?? LEAGUES[sport][0], o.year, language, { length: field.length, width: field.width }) : plan !== undefined ? leagueAbout(plan.league, o.year, language, plan.spec) : league !== undefined ? leagueAbout(league, o.year, language) : pitch === undefined ? words.about[sport] : fill(pitch.format === "futsal" ? words.soccer.aboutFutsal : words.about.soccer, { length: metres(pitch.length), width: metres(pitch.width) });
688
+ const about = `${words.said.on}${colon}${aboutText}${stop}${sport === "athletics" ? athleticsSentences(o, language) : sport === "skating" ? skatingSentences(o, language) : isBudoSport(sport) ? budoSentences(o, language) : ""}`;
605
689
  const halfLine = pitch !== undefined && !isScore(o) && (o.half === "left" || o.half === "right") ? `${gap}${words.soccer.half}` : "";
606
690
  // What the ground says about the size: that it is the published one, or that the ground has none and the international pitch is drawn.
607
691
  const year = typeof o.year === "number" && Number.isFinite(o.year) ? o.year : undefined;
@@ -612,7 +696,9 @@ export function describe(o) {
612
696
  // A park's walls and quirks that are drawn are said too.
613
697
  const ballpark = park === undefined ? undefined : ballparkFor(o);
614
698
  const special = specialText(o, language, surfaceName);
615
- const parkLine = (parkName === undefined ? `${groundLine}${halfLine}` : `${gap}${fill(words.said.park, { park: parkName })}${officially(parkOfficial)}${ballpark === undefined ? "" : ballparkSentences(ballpark).map((line) => `${gap}${line}`).join("")}`) + (special === undefined ? "" : `${gap}${special.text}`);
699
+ // What the arena says about the size: that it is the one its sources publish, or that its rink is said to have been shorter and no size is given.
700
+ const arenaLine = arenaName === undefined ? "" : `${arenaSize !== undefined ? `${gap}${fill(words.said.arenaSize, { arena: arenaName })}` : sport === "nhl" && arena?.rinkUnknown === true ? `${gap}${fill(words.said.arenaUnknown, { arena: arenaName })}` : ""}${officially(arena === undefined ? undefined : arenaOfficialName(arena, language, year))}`;
701
+ const parkLine = (parkName === undefined ? `${groundLine}${halfLine}${arenaLine}` : `${gap}${fill(words.said.park, { park: parkName })}${officially(parkOfficial)}${ballpark === undefined ? "" : ballparkSentences(ballpark).map((line) => `${gap}${line}`).join("")}`) + (special === undefined ? "" : `${gap}${special.text}`);
616
702
  // A drawing of a past season says which: the markings are those of that season.
617
703
  const dated = typeof o.year === "number" && Number.isFinite(o.year);
618
704
  const eraLine = dated && league !== undefined
@@ -631,14 +717,17 @@ export function describe(o) {
631
717
  : plan?.show === "folded"
632
718
  ? `${gap}${[foldedWords[isPortrait(o) ? "tall" : "wide"], foldedWords[plan.panels], hasData ? foldedWords.data : ""].filter((part) => part !== "").join(gap)}`
633
719
  : describeRange(o, language, hasData);
720
+ // A situation says what the pill, the lit bases or the line of scrimmage show.
721
+ const read = situationOf(o);
722
+ const situationLine = read === undefined ? "" : situationSentences(read, language, isScore(o) ? { home: o.home.name, away: o.away.name } : undefined).map((line) => `${gap}${line}`).join("");
634
723
  if (!isScore(o))
635
- return { title: o.title ?? special?.title ?? surfaceName, description: o.description ?? `${about}${parkLine}${eraLine}${rangeLine}${poolLine}` };
724
+ return { title: o.title ?? special?.title ?? surfaceName, description: o.description ?? `${about}${parkLine}${eraLine}${rangeLine}${situationLine}${poolLine}` };
636
725
  const winner = winnerOf({ ...o, sport });
637
726
  // A pageant says what lies on each half, and the emblem in the middle.
638
727
  const pageant = pageantOf(o);
639
728
  const pg = KYOGIJO_STRINGS[language].pageant;
640
729
  const pageantLine = pageant === undefined ? "" : `${gap}${pageant.style === "stage" ? pg.stage : pg.carpet}${pageant.emblem === undefined ? "" : `${gap}${pageant.emblem.name === undefined ? pg.emblem : fill(pg.emblemNamed, { name: pageant.emblem.name })}`}`;
641
- const s = (t) => fieldScoreText(sport, t) ?? (t.score === undefined || String(t.score) === "" ? "–" : String(t.score));
730
+ const s = (t) => fieldScoreText(sport, t) ?? budoScoreText(sport, t) ?? (t.score === undefined || String(t.score) === "" ? "–" : String(t.score));
642
731
  // The sentence follows the drawing: the team on the left is first. The words' {away} and {home} are the first and the second team named.
643
732
  const order = resolveHomeSide({ ...o, sport }) === "left" ? [o.home, o.away] : [o.away, o.home];
644
733
  const line = fill(words.scoreTitle, { away: order[0].name, awayScore: s(order[0]), home: order[1].name, homeScore: s(order[1]) });
@@ -699,7 +788,7 @@ export function describe(o) {
699
788
  : "";
700
789
  return {
701
790
  title: o.title ?? (status === undefined ? line : `${line}${words.said.sep}${status}`),
702
- description: o.description ?? `${extra === "" ? "" : `${sentence(extra)}${gap}`}${sentence(line)}${result}${tied}${curlingLine}${netLine}${fieldLine}${gap}${about}${parkLine}${eraLine}${rangeLine}${pageantLine}${poolLine}`.trim(),
791
+ description: o.description ?? `${extra === "" ? "" : `${sentence(extra)}${gap}`}${sentence(line)}${result}${tied}${curlingLine}${netLine}${fieldLine}${isBudoSport(sport) ? budoScoreSentences(sport, order, language) : ""}${gap}${about}${parkLine}${eraLine}${rangeLine}${situationLine}${pageantLine}${poolLine}`.trim(),
703
792
  };
704
793
  }
705
794
  /**
@@ -719,6 +808,9 @@ export function describe(o) {
719
808
  * ```
720
809
  */
721
810
  export function drawKyogijo(o) {
811
+ return withEmblems(o.emblems, () => drawnKyogijo(o));
812
+ }
813
+ function drawnKyogijo(o) {
722
814
  if (o.host !== undefined && o.range !== undefined)
723
815
  return drawKyogijo({ ...o, range: undefined });
724
816
  // A special game is a surface set inside another, drawn to scale with no stands.
@@ -743,7 +835,15 @@ export function drawKyogijo(o) {
743
835
  else if (sized === "full")
744
836
  sized = "compact";
745
837
  }
746
- const id = drawingId(o);
838
+ // A lineup's players stand in the middle of the surface, where the score's panel would hide them: the score goes to the strip under the drawing, as a pageant's does, unless `score: "centre"` asks for it.
839
+ const lineupped = (o.layers ?? []).some((layer) => layer?.kind === "lineup");
840
+ const stripped = isScore(o) && lineupped && o.score !== "centre";
841
+ if (isScore(o) && pageant === undefined && sized === "full" && (o.score === "strip" || stripped) && !(sport === "curling" && portrait))
842
+ sized = "compact";
843
+ // The situation of the game (hockey's skaters, baseball's runners, football's down), decided once: the surface, the badge and the description draw from it.
844
+ const situation = situationOf(o);
845
+ // A situation that is not drawn (a sport that has none, nothing the sport can have in it) leaves the drawing, its ids included, as it was.
846
+ const id = drawingId(o.situation !== undefined && situation === undefined ? { ...o, situation: undefined } : o);
747
847
  const parts = o.parts === true;
748
848
  const homeSide = isScore(o) ? resolveHomeSide({ ...o, sport }) : "right";
749
849
  const awaySide = homeSide === "left" ? "right" : "left";
@@ -757,8 +857,8 @@ export function drawKyogijo(o) {
757
857
  if (isScore(o)) {
758
858
  winner = winnerOf({ ...o, sport });
759
859
  status = statusText({ ...o, sport }, language);
760
- const home = teamView(o.home, homeSide, "home", winner, uppercase, sport, o.markShape);
761
- const away = teamView(o.away, awaySide, "away", winner, uppercase, sport, o.markShape);
860
+ const home = teamView(o.home, homeSide, "home", winner, uppercase, sport, o.markShape, o.theme === "light" || o.theme === "dark" ? o.theme : undefined);
861
+ const away = teamView(o.away, awaySide, "away", winner, uppercase, sport, o.markShape, o.theme === "light" || o.theme === "dark" ? o.theme : undefined);
762
862
  left = homeSide === "left" ? home : away;
763
863
  right = homeSide === "left" ? away : home;
764
864
  leftTeam = homeSide === "left" ? o.home : o.away;
@@ -776,6 +876,7 @@ export function drawKyogijo(o) {
776
876
  const crowded = crowdOf(o) !== undefined;
777
877
  const style = colourStyle(o, { left: leftTeam, right: rightTeam, home: isScore(o) ? o.home : undefined, away: isScore(o) ? o.away : undefined }, featured);
778
878
  const palette = isLook(o.look) ? o.look : undefined;
879
+ const budoLook = isBudoSport(sport) ? budoLookOf(sport, o.budo?.look ?? (sport === "dojo" ? dojoSpecOf(o.dojo).look : undefined)) : undefined;
779
880
  let notes;
780
881
  const theme = o.theme === "light" || o.theme === "dark" ? o.theme : undefined;
781
882
  let viewBox;
@@ -785,7 +886,7 @@ export function drawKyogijo(o) {
785
886
  let foldedSheet = false;
786
887
  let pg;
787
888
  if (isScore(o) && left !== undefined && right !== undefined && sized === "badge") {
788
- const badge = badgeMarkup(left, right, { sport, status, parts, id });
889
+ const badge = badgeMarkup(left, right, { sport, status, parts, id, situation: situation === undefined ? undefined : badgeSituation(situation, language) });
789
890
  viewBox = `0 0 ${badge.w} ${badge.h}`;
790
891
  body = badge.markup;
791
892
  }
@@ -800,8 +901,8 @@ export function drawKyogijo(o) {
800
901
  let surface = make({ features, pageant: pageant !== undefined });
801
902
  // The score of a pageant with no emblem and no board: the plate stays in the middle if the pictures lose little to it, else it goes to the strip and the surface is built again.
802
903
  if (pageant !== undefined && pageant.score === "auto") {
803
- let score = "centre";
804
- if (isScore(o) && left !== undefined && right !== undefined && leftTeam !== undefined && rightTeam !== undefined && sized === "full") {
904
+ let score = stripped ? "strip" : "centre";
905
+ if (!stripped && isScore(o) && left !== undefined && right !== undefined && leftTeam !== undefined && rightTeam !== undefined && sized === "full") {
805
906
  const [first, second] = pageantPair(surface, { left, right, leftTeam, rightTeam });
806
907
  score = autoScore({ pageant, surface, outline: outlineOf(o), left: first, right: second, turned: surface.turned, sport, plate: true });
807
908
  }
@@ -838,7 +939,21 @@ export function drawKyogijo(o) {
838
939
  const ground = laidOn(surface.base, o.background, id);
839
940
  grounded = ground.base !== surface.base;
840
941
  defs = surface.defs + ground.defs;
841
- let inner = ground.base;
942
+ // The situation: a tint under the lines, and the rest over them and the data (cut with the surface when it is cropped).
943
+ const sit = situation === undefined
944
+ ? undefined
945
+ : drawSituation(situation, surface, {
946
+ id,
947
+ language,
948
+ homeSide,
949
+ abbrs: left !== undefined && right !== undefined ? { [left.role]: left.abbr, [right.role]: right.abbr } : undefined,
950
+ plate: isScore(o) && sized === "full" && o.plate !== false,
951
+ strip: stripped && sized === "compact",
952
+ });
953
+ const cutSit = (markup) => (markup !== "" && surface.defs.includes(`id="${id}-rg"`) ? el("g", { "clip-path": `url(#${id}-rg)` }, markup) : markup);
954
+ let inner = ground.base + (sit === undefined ? "" : cutSit(sit.under));
955
+ if (sit !== undefined)
956
+ defs += sit.defs;
842
957
  let teams = "";
843
958
  // A pageant lays each team's picture over its half, in place of the names painted at the ends.
844
959
  if (pageant !== undefined && isScore(o) && left !== undefined && right !== undefined && leftTeam !== undefined && rightTeam !== undefined && (sized === "full" || sized === "compact")) {
@@ -852,7 +967,7 @@ export function drawKyogijo(o) {
852
967
  if (isScore(o) && left !== undefined && right !== undefined && sized === "full" && pg === undefined) {
853
968
  const onColour = surface.onColour ?? (sport === "nfl" || sport === "cfl" || sport === "ifaf" || sport === "soccer");
854
969
  // A curling sheet, the net sports' courts and a cricket ground have no place for the names: the board names the teams.
855
- if (sport !== "curling" && !hasSetsBoard(sport) && sport !== "cricket") {
970
+ if (sport !== "curling" && !hasSetsBoard(sport) && sport !== "cricket" && sport !== "judo" && sport !== "karate" && sport !== "kendo" && sport !== "sumo") {
856
971
  // Cropped to a range, a team's name is written only where its place is in what is shown.
857
972
  if (shows(surface, surface.left))
858
973
  teams += bannerMarkup(surface.left, left, { turned: surface.turned, onColour, parts, id });
@@ -869,6 +984,9 @@ export function drawKyogijo(o) {
869
984
  }
870
985
  else
871
986
  inner += surface.bannersOver === true ? surface.lines + teams : teams + surface.lines;
987
+ // A lineup's players stand over what the situation lights (a runner's base, a drive's arrows), so that none of them is hidden; any other data goes over it.
988
+ if (sit !== undefined && lineupped)
989
+ inner += cutSit(sit.over);
872
990
  // Data goes over the markings and under the score's panel.
873
991
  const layered = renderLayers(o, o.layers, surface.turned, surface.whole === undefined ? undefined : surface.box, surface.upright);
874
992
  inner += surface.frame === undefined || layered.svg === "" ? layered.svg : el("g", { transform: surface.frame }, layered.svg);
@@ -885,6 +1003,8 @@ export function drawKyogijo(o) {
885
1003
  const { colon, gap } = KYOGIJO_STRINGS[language].said;
886
1004
  description = `${said}${gap}${KYOGIJO_STRINGS[language].layers.data}${colon}${layered.summaries.join(gap)}`;
887
1005
  }
1006
+ if (sit !== undefined && !lineupped)
1007
+ inner += cutSit(sit.over);
888
1008
  if (surface.over !== undefined)
889
1009
  inner += surface.over;
890
1010
  if (notes !== undefined && notes.svg !== "") {
@@ -903,6 +1023,11 @@ export function drawKyogijo(o) {
903
1023
  const board = cricketBoardMarkup(surface.plate, left, right, { competition: o.competition, foot: foot === "" ? undefined : foot, innings: words.innings, total: words.total, overs: words.overs }, { parts });
904
1024
  inner += el("g", { transform: `translate(${num(surface.plate.cx)} ${num(surface.plate.cy)})${surface.turned ? " rotate(-90)" : ""}` }, board);
905
1025
  }
1026
+ else if (isBudoSport(sport) && sport !== "kyudo" && sport !== "dojo") {
1027
+ const b = KYOGIJO_STRINGS[language].budo.board;
1028
+ const board = budoBoardMarkup(sport, surface.plate, left, right, { competition: o.competition, foot: foot === "" ? undefined : foot, wazaari: b.wazaari, yuko: b.yuko, shido: b.shido, senshu: b.senshu, chui: b.chui, kimarite: b.kimarite, techniques: b.techniques, kimariteName: (key) => kimariteName(key, language) }, { parts });
1029
+ inner += el("g", { transform: `translate(${num(surface.plate.cx)} ${num(surface.plate.cy)})${surface.turned ? " rotate(-90)" : ""}` }, board);
1030
+ }
906
1031
  else if (hasSetsBoard(sport)) {
907
1032
  const board = setsBoardMarkup(surface.plate, left, right, { competition: o.competition, foot: foot === "" ? undefined : foot, head: KYOGIJO_STRINGS[language].net[BOARD[sport] === "games" ? "games" : "sets"], points: KYOGIJO_STRINGS[language].net.points }, { parts, least: minSets(sport, chooseLeague(o).league?.id) });
908
1033
  inner += el("g", { transform: `translate(${num(surface.plate.cx)} ${num(surface.plate.cy)})${surface.turned ? " rotate(-90)" : ""}` }, board);
@@ -910,10 +1035,10 @@ export function drawKyogijo(o) {
910
1035
  else
911
1036
  inner += plateMarkup(surface.plate, left, right, { competition: o.competition, foot: foot === "" ? undefined : foot }, { turned: surface.turned, parts, surfaceSport: sport });
912
1037
  }
913
- const group = el("g", { class: "ky-surface-group", transform: surface.turn, part: parts ? "surface" : undefined }, inner);
1038
+ const group = el("g", { class: "ky-surface-group", transform: surface.turn, part: parts ? "surface" : undefined, ...(o.pointer === true ? fieldAttributes({ ...o, sport }) : {}) }, inner);
914
1039
  const v = surface.view;
915
1040
  if (isScore(o) && left !== undefined && right !== undefined && sized === "compact") {
916
- const badge = badgeMarkup(left, right, { sport, status, parts, id });
1041
+ const badge = badgeMarkup(left, right, { sport, status, parts, id, situation: situation === undefined ? undefined : badgeSituation(situation, language) });
917
1042
  const scale = v.w / badge.w;
918
1043
  const gap = badge.h * scale * 0.12;
919
1044
  body = group + el("g", { class: "ky-strip", transform: `translate(${num(v.x)} ${num(v.y + v.h + gap)}) scale(${scale.toFixed(4)})` }, badge.markup);
@@ -934,7 +1059,7 @@ export function drawKyogijo(o) {
934
1059
  "data-league": o.league === undefined || o.league === "" ? undefined : o.league,
935
1060
  "data-range": isScore(o) && sport === "curling" ? undefined : rangeLabel(o),
936
1061
  "data-event": o.host !== undefined && typeof o.event === "string" ? safeClass(o.event) : undefined,
937
- "data-look": look,
1062
+ "data-look": look ?? budoLook,
938
1063
  "data-size": sized,
939
1064
  "data-orientation": portrait ? "portrait" : "landscape",
940
1065
  "data-theme": theme,
@@ -948,7 +1073,7 @@ export function drawKyogijo(o) {
948
1073
  part: parts ? "svg" : undefined,
949
1074
  style: style === "" ? undefined : style,
950
1075
  }, `<title id="${titleId}">${esc(title)}</title><desc id="${descId}">${esc(description)}</desc>` +
951
- (o.style === "none" ? "" : `<style>${KYOGIJO_BASE_STYLE}${left?.round === true || right?.round === true || (pg !== undefined && pageant?.style === "stage") || (pg !== undefined && pageant?.emblem?.shape !== "plain" && pageant?.emblem !== undefined) ? `\n${KYOGIJO_MARK_STYLE}` : ""}${pg === undefined ? "" : `\n${KYOGIJO_PAGEANT_STYLE}${pageant?.animate === undefined || pageant.animate === "none" ? "" : `\n${KYOGIJO_PAGEANT_MOTION}`}`}${sport === "curling" ? `\n${KYOGIJO_CURLING_STYLE}` : ""}${foldedSheet ? `\n${KYOGIJO_FOLD_STYLE}` : ""}${isPoolSport(sport) ? `\n${o.pool?.venue !== undefined ? KYOGIJO_POOL_STYLE : poolStyle({ sport, ...commonOf(o.pool ?? {}), race: o.pool?.race !== undefined, section: o.pool?.section !== undefined && o.pool.section !== false })}` : ""}${plan === undefined ? "" : `\n${courtStyle(plan.sport, plan.look)}`}${fieldsStyle(sport) === "" ? "" : `\n${fieldsStyle(sport)}`}${athleticsStyle(sport) === "" ? "" : `\n${athleticsStyle(sport)}`}${skatingStyle(sport) === "" ? "" : `\n${skatingStyle(sport)}`}${palette === undefined ? "" : `\n${lookStyle(palette)}`}${athleticsTrackRules(sport, o.athletics?.track) === "" ? "" : `\n${athleticsTrackRules(sport, o.athletics?.track)}`}${walled ? `\n${KYOGIJO_WALLS_STYLE}` : ""}${hardwood ? `\n${KYOGIJO_HARDWOOD_STYLE}` : ""}${features === undefined ? "" : `\n${featureStyle(sport, features)}`}${notes === undefined || notes.svg === "" ? "" : `\n${KYOGIJO_NOTE_STYLE}`}${grounded ? `\n${KYOGIJO_BACKGROUND_STYLE}` : ""}${crowded ? `\n${KYOGIJO_CROWD_STYLE}` : ""}${o.host === undefined ? "" : `\n${specialStyle(hostSport(o.host))}`}</style>`) +
1076
+ (o.style === "none" ? "" : `<style>${KYOGIJO_BASE_STYLE}${left?.round === true || right?.round === true || (pg !== undefined && pageant?.style === "stage") || (pg !== undefined && pageant?.emblem?.shape !== "plain" && pageant?.emblem !== undefined) ? `\n${KYOGIJO_MARK_STYLE}` : ""}${pg === undefined ? "" : `\n${KYOGIJO_PAGEANT_STYLE}${pageant?.animate === undefined || pageant.animate === "none" ? "" : `\n${KYOGIJO_PAGEANT_MOTION}`}`}${sport === "curling" ? `\n${KYOGIJO_CURLING_STYLE}` : ""}${foldedSheet ? `\n${KYOGIJO_FOLD_STYLE}` : ""}${isPoolSport(sport) ? `\n${o.pool?.venue !== undefined ? KYOGIJO_POOL_STYLE : poolStyle({ sport, ...commonOf(o.pool ?? {}), race: o.pool?.race !== undefined, section: o.pool?.section !== undefined && o.pool.section !== false })}` : ""}${plan === undefined ? "" : `\n${courtStyle(plan.sport, plan.look)}`}${fieldsStyle(sport) === "" ? "" : `\n${fieldsStyle(sport)}`}${athleticsStyle(sport) === "" ? "" : `\n${athleticsStyle(sport)}`}${skatingStyle(sport) === "" ? "" : `\n${skatingStyle(sport)}`}${budoLook === undefined ? "" : `\n${budoStyle(sport, budoLook)}`}${palette === undefined ? "" : `\n${lookStyle(palette)}`}${athleticsTrackRules(sport, o.athletics?.track) === "" ? "" : `\n${athleticsTrackRules(sport, o.athletics?.track)}`}${walled ? `\n${KYOGIJO_WALLS_STYLE}` : ""}${hardwood ? `\n${KYOGIJO_HARDWOOD_STYLE}` : ""}${features === undefined ? "" : `\n${featureStyle(sport, features)}`}${notes === undefined || notes.svg === "" ? "" : `\n${KYOGIJO_NOTE_STYLE}`}${grounded ? `\n${KYOGIJO_BACKGROUND_STYLE}` : ""}${crowded ? `\n${KYOGIJO_CROWD_STYLE}` : ""}${crowded && sport === "dojo" ? KYOGIJO_BUDO_GALLERY_STYLE : ""}${situation === undefined ? "" : `\n${KYOGIJO_SITUATION_STYLE}`}${o.host === undefined ? "" : `\n${specialStyle(hostSport(o.host))}`}</style>`) +
952
1077
  (defs === "" ? "" : `<defs>${defs}</defs>`) +
953
1078
  body);
954
1079
  return root;
@@ -1 +1,3 @@
1
1
  export { KYOGIJO_ATTRIBUTES, KYOGIJO_TAG, KyogijoField, defineKyogijoField, optionsFromAttributes, replaySettingsFromAttributes, turnSettingsFromAttributes, turnWanted } from "./element.ts";
2
+ export { useEmblems } from "./emblems-source.ts";
3
+ export type { EmblemRenderer } from "./emblems-source.ts";
@@ -1,2 +1,3 @@
1
1
  // The custom element's class and its registration function; importing this entry registers nothing.
2
2
  export { KYOGIJO_ATTRIBUTES, KYOGIJO_TAG, KyogijoField, defineKyogijoField, optionsFromAttributes, replaySettingsFromAttributes, turnSettingsFromAttributes, turnWanted } from "./element.js";
3
+ export { useEmblems } from "./emblems-source.js";
package/dist/element.d.ts CHANGED
@@ -6,7 +6,7 @@ import type { ScoreOptions, SurfaceOptions } from "./types.ts";
6
6
  /** The tag of the element. */
7
7
  export declare const KYOGIJO_TAG = "kyogijo-field";
8
8
  /** The attributes the element reads. */
9
- export declare const KYOGIJO_ATTRIBUTES: readonly ["sport", "league", "size", "theme", "language", "orientation", "park", "ground", "year", "detail", "margin", "stripes", "exact-lines", "uppercase", "pitch", "pitch-type", "half", "range", "range-end", "goals", "stripe-pattern", "corner-flags", "look", "line-width", "line-dash", "line-opacity", "landmarks", "walls", "quirks", "home", "away", "home-abbr", "away-abbr", "home-score", "away-score", "home-penalties", "away-penalties", "score", "home-logo", "away-logo", "home-color", "away-color", "status", "competition", "date", "label", "winner", "home-side", "pageant", "description", "options", "loop", "speed", "autoplay", "controls", "trail", "turn", "turn-view", "turn-controls", "turn-uprights", "turn-score", "turn-touch"];
9
+ export declare const KYOGIJO_ATTRIBUTES: readonly ["sport", "league", "size", "theme", "language", "orientation", "park", "ground", "arena", "year", "detail", "margin", "stripes", "exact-lines", "uppercase", "pitch", "pitch-type", "half", "range", "range-end", "goals", "stripe-pattern", "corner-flags", "look", "line-width", "line-dash", "line-opacity", "landmarks", "walls", "quirks", "home", "away", "home-abbr", "away-abbr", "home-score", "away-score", "home-penalties", "away-penalties", "score", "home-logo", "away-logo", "home-color", "away-color", "status", "competition", "date", "label", "winner", "home-side", "pageant", "description", "options", "loop", "speed", "autoplay", "controls", "trail", "turn", "turn-view", "turn-controls", "turn-uprights", "turn-score", "turn-touch"];
10
10
  /** The settings of a 3D view an element's attributes make: `turn-view` names the viewpoint it opens on, `turn-controls="false"` and `turn-uprights="false"` leave the buttons and what stands on the surface out, `turn-score="floor"` leaves the score panel on the floor, `turn-touch="turn"` keeps every touch for the view. */
11
11
  export declare function turnSettingsFromAttributes(read: (name: string) => string | null, base?: TurnSettings): TurnSettings;
12
12
  /** Whether an element's `turn` attribute asks for the 3D view: present, and not `false`. */
@@ -24,7 +24,7 @@ export declare function optionsFromAttributes(read: (name: string) => string | n
24
24
  declare const Base: typeof HTMLElement;
25
25
  export declare class KyogijoField extends Base {
26
26
  #private;
27
- static observedAttributes: readonly ["sport", "league", "size", "theme", "language", "orientation", "park", "ground", "year", "detail", "margin", "stripes", "exact-lines", "uppercase", "pitch", "pitch-type", "half", "range", "range-end", "goals", "stripe-pattern", "corner-flags", "look", "line-width", "line-dash", "line-opacity", "landmarks", "walls", "quirks", "home", "away", "home-abbr", "away-abbr", "home-score", "away-score", "home-penalties", "away-penalties", "score", "home-logo", "away-logo", "home-color", "away-color", "status", "competition", "date", "label", "winner", "home-side", "pageant", "description", "options", "loop", "speed", "autoplay", "controls", "trail", "turn", "turn-view", "turn-controls", "turn-uprights", "turn-score", "turn-touch"];
27
+ static observedAttributes: readonly ["sport", "league", "size", "theme", "language", "orientation", "park", "ground", "arena", "year", "detail", "margin", "stripes", "exact-lines", "uppercase", "pitch", "pitch-type", "half", "range", "range-end", "goals", "stripe-pattern", "corner-flags", "look", "line-width", "line-dash", "line-opacity", "landmarks", "walls", "quirks", "home", "away", "home-abbr", "away-abbr", "home-score", "away-score", "home-penalties", "away-penalties", "score", "home-logo", "away-logo", "home-color", "away-color", "status", "competition", "date", "label", "winner", "home-side", "pageant", "description", "options", "loop", "speed", "autoplay", "controls", "trail", "turn", "turn-view", "turn-controls", "turn-uprights", "turn-score", "turn-touch"];
28
28
  /** The options given as a property: an object, laid under the attributes. */
29
29
  get options(): Partial<ScoreOptions> | undefined;
30
30
  set options(value: Partial<ScoreOptions> | undefined);
package/dist/element.js CHANGED
@@ -20,7 +20,7 @@ import { mountKyogijoTurn } from "./turn.dom.js";
20
20
  export const KYOGIJO_TAG = "kyogijo-field";
21
21
  /** The attributes the element reads. */
22
22
  export const KYOGIJO_ATTRIBUTES = [
23
- "sport", "league", "size", "theme", "language", "orientation", "park", "ground", "year", "detail", "margin", "stripes", "exact-lines", "uppercase",
23
+ "sport", "league", "size", "theme", "language", "orientation", "park", "ground", "arena", "year", "detail", "margin", "stripes", "exact-lines", "uppercase",
24
24
  "pitch", "pitch-type", "half", "range", "range-end", "goals", "stripe-pattern", "corner-flags", "look", "line-width", "line-dash", "line-opacity", "landmarks", "walls", "quirks",
25
25
  "home", "away", "home-abbr", "away-abbr", "home-score", "away-score", "home-penalties", "away-penalties", "score", "home-logo", "away-logo", "home-color", "away-color",
26
26
  "status", "competition", "date", "label", "winner", "home-side", "pageant", "description", "options",
@@ -109,6 +109,7 @@ export function optionsFromAttributes(read, base) {
109
109
  text("orientation");
110
110
  text("park");
111
111
  text("ground");
112
+ text("arena");
112
113
  text("detail");
113
114
  text("pitch");
114
115
  text("pitch-type");
@@ -0,0 +1,50 @@
1
+ /** The styles an emblem is drawn in: full colour with metallic gradients, one outline in the page's ink, or flat fills. */
2
+ export type EmblemStyleName = "metal" | "line" | "color";
3
+ /** What kyogijo asks of a renderer. It is a subset of dougu-sports' `EmblemOptions`. */
4
+ export interface EmblemDrawOptions {
5
+ style?: EmblemStyleName;
6
+ /** The colour of the emblem's main part: a crest takes the team's colour. */
7
+ tint?: string;
8
+ /** A second colour: a crest's band. */
9
+ accent?: string;
10
+ /** The letters of a crest, up to three. */
11
+ initials?: string;
12
+ /** `inherit`, `auto` (by the device's setting), `light` or `dark`. */
13
+ theme?: "inherit" | "auto" | "light" | "dark";
14
+ size?: number;
15
+ }
16
+ /**
17
+ * A function that draws an emblem from its id as a document of SVG, and throws for an id it does not know: `renderEmblem` of `@johnmorrisdotca/dougu-sports/emblems` is one.
18
+ * The id may end in `/metal`, `/line` or `/color`, and the renderer reads that suffix.
19
+ */
20
+ export type EmblemRenderer = (id: string, options?: EmblemDrawOptions) => string;
21
+ type Known<T> = 0 extends 1 & T ? string : T;
22
+ /** @ts-ignore optional: resolves only where @johnmorrisdotca/dougu-sports is installed; elsewhere it is `any`, which `Known` turns into `string`. */
23
+ type DouguId = import("@johnmorrisdotca/dougu-sports/emblems").EmblemId;
24
+ /**
25
+ * The ids of dougu-sports' emblems (`"trophy:cup"`, `"crest:shield"`), when that package is installed, so that an editor completes them; any other string otherwise. An id may end in `/metal`, `/line` or `/color`,
26
+ * and a trophy or an award may be written without its group (`"cup"`). Nothing is imported at run time.
27
+ */
28
+ export type EmblemId = Known<DouguId>;
29
+ /** An emblem as a logo, a mark or a pageant's emblem is given: its id, with or without a style, or any string (an address, a data address or an SVG document). */
30
+ export type EmblemRef = EmblemId | `${EmblemId}/${EmblemStyleName}` | (string & {});
31
+ /**
32
+ * Plug a renderer of ready-made emblems into kyogijo, for every drawing on the page: after this `logo: "crest:shield"` and `emblem: "trophy:hockey-cup"` are drawn. Call it once, at start-up. Without one, an id is
33
+ * an address and is refused. `useEmblems(undefined)` unplugs it.
34
+ * @example
35
+ * ```ts
36
+ * import { useEmblems } from "@johnmorrisdotca/kyogijo-stadiums";
37
+ * import { renderEmblem } from "@johnmorrisdotca/dougu-sports/emblems";
38
+ * useEmblems(renderEmblem);
39
+ * ```
40
+ */
41
+ export declare function useEmblems(render: EmblemRenderer | undefined): void;
42
+ /** Run a drawing with a renderer for that drawing alone (the `emblems` option), putting back whatever was there. Without a renderer it runs as it is. */
43
+ export declare function withEmblems<T>(render: EmblemRenderer | undefined, draw: () => T): T;
44
+ /** The renderer in use: the drawing's own, else the page's, else none. */
45
+ export declare function emblemRenderer(): EmblemRenderer | undefined;
46
+ /** An id with its group, which no address has in practice: what a missing renderer is told about. */
47
+ export declare const GROUPED_ID: RegExp;
48
+ /** The SVG document for a value that names an emblem, drawn by the renderer in use, or `undefined` (no renderer, not the look of an id, or an id the renderer does not know). */
49
+ export declare function renderedEmblem(value: unknown, options?: EmblemDrawOptions): string | undefined;
50
+ export {};