@johnmorrisdotca/kumimoji 1.2.2 → 1.3.1
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.
- package/CHANGELOG.md +33 -1
- package/CONTRIBUTING.md +11 -2
- package/README.md +80 -39
- package/dist/check.d.ts +26 -1
- package/dist/check.js +30 -3
- package/dist/computer.types.d.ts +24 -2
- package/dist/computerPlay.d.ts +45 -2
- package/dist/computerPlay.js +56 -6
- package/dist/computerTurn.d.ts +42 -3
- package/dist/computerTurn.js +47 -5
- package/dist/daily.d.ts +22 -2
- package/dist/daily.js +23 -3
- package/dist/element-define.d.ts +1 -1
- package/dist/element-define.js +3 -13
- package/dist/element.d.ts +85 -4
- package/dist/element.js +359 -77
- package/dist/embed-day.d.ts +4 -0
- package/dist/embed-day.js +23 -0
- package/dist/export.d.ts +123 -6
- package/dist/export.js +109 -9
- package/dist/generate.d.ts +14 -0
- package/dist/generate.js +32 -5
- package/dist/grid.d.ts +181 -10
- package/dist/grid.js +148 -11
- package/dist/help.d.ts +36 -3
- package/dist/help.js +36 -3
- package/dist/index.d.ts +22 -22
- package/dist/index.js +22 -22
- package/dist/judge.d.ts +14 -0
- package/dist/judge.js +14 -0
- package/dist/kana.d.ts +23 -1
- package/dist/kana.js +62 -7
- package/dist/kumimoji.types.d.ts +70 -5
- package/dist/party.d.ts +268 -14
- package/dist/party.js +296 -19
- package/dist/party.types.d.ts +50 -2
- package/dist/partyKept.d.ts +74 -4
- package/dist/partyKept.js +127 -12
- package/dist/partySeats.d.ts +92 -6
- package/dist/partySeats.js +83 -8
- package/dist/partyTurns.d.ts +171 -5
- package/dist/partyTurns.js +187 -8
- package/dist/placement.d.ts +50 -2
- package/dist/placement.js +35 -2
- package/dist/play.d.ts +247 -15
- package/dist/play.js +251 -18
- package/dist/random.d.ts +34 -3
- package/dist/random.js +23 -2
- package/dist/react.d.ts +42 -4
- package/dist/react.js +54 -7
- package/dist/showcase.d.ts +83 -7
- package/dist/showcase.js +61 -7
- package/dist/strings.d.ts +54 -5
- package/dist/strings.js +34 -3
- package/dist/tableView.d.ts +151 -8
- package/dist/tableView.js +129 -6
- package/dist/tileFace.d.ts +59 -5
- package/dist/tileFace.js +48 -4
- package/dist/tileFamily.d.ts +9 -0
- package/dist/tileFamily.js +9 -0
- package/dist/tileWords.d.ts +93 -6
- package/dist/tileWords.js +94 -12
- package/dist/tileWordsModule.d.ts +12 -2
- package/dist/tileWordsModule.js +14 -2
- package/dist/tiles.constants.d.ts +99 -5
- package/dist/tiles.constants.js +171 -13
- package/dist/turn.d.ts +82 -4
- package/dist/turn.js +88 -5
- package/dist/ui/board.d.ts +60 -5
- package/dist/ui/board.js +44 -4
- package/dist/ui/draw.d.ts +62 -0
- package/dist/ui/draw.js +229 -0
- package/dist/ui/mount.d.ts +41 -2
- package/dist/ui/mount.js +190 -265
- package/dist/ui/style.d.ts +6 -1
- package/dist/ui/style.js +6 -0
- package/dist/ui.d.ts +3 -3
- package/dist/ui.js +2 -2
- package/dist/version.d.ts +9 -2
- package/dist/version.js +9 -2
- package/dist/wilds.d.ts +22 -1
- package/dist/wilds.js +25 -3
- package/package.json +68 -61
package/dist/element.js
CHANGED
|
@@ -1,22 +1,12 @@
|
|
|
1
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
2
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
-
};
|
|
6
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
7
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
8
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
9
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
10
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
11
|
-
};
|
|
12
|
-
var _KumimojiTable_instances, _KumimojiTable_table, _KumimojiTable_key, _KumimojiTable_queued, _KumimojiTable_seed, _KumimojiTable_refresh;
|
|
13
1
|
import { kumimojiDailySeed } from "./daily.js";
|
|
2
|
+
import { embedDay } from "./embed-day.js";
|
|
14
3
|
import { KUMIMOJI_HANDS } from "./tiles.constants.js";
|
|
15
4
|
import { mountKumimoji } from "./ui/mount.js";
|
|
5
|
+
import { KUMIMOJI_STYLE } from "./ui/style.js";
|
|
16
6
|
const ElementBase = typeof HTMLElement === "undefined" ? class {
|
|
17
7
|
} : HTMLElement;
|
|
18
|
-
const oneOf = (value, allowed) =>
|
|
19
|
-
const isOn = (value) => value
|
|
8
|
+
const oneOf = (value, allowed) => allowed.includes(value) ? value : undefined;
|
|
9
|
+
const isOn = (value, fallback = false) => value === null ? fallback : !["false", "off", "no", "0"].includes(value.trim().toLowerCase());
|
|
20
10
|
const HANDS = Object.values(KUMIMOJI_HANDS);
|
|
21
11
|
/**
|
|
22
12
|
* THE `<kumimoji-table>` ELEMENT: a game of Kumimoji alone in a tag, with no
|
|
@@ -29,8 +19,7 @@ const HANDS = Object.values(KUMIMOJI_HANDS);
|
|
|
29
19
|
* <kumimoji-table language="japanese" level="easy" seed="daily"></kumimoji-table>
|
|
30
20
|
* ```
|
|
31
21
|
*
|
|
32
|
-
* Attributes
|
|
33
|
-
* deals a new game):
|
|
22
|
+
* Attributes configure the starting game; display, language and control changes preserve play:
|
|
34
23
|
* - `language`: the tiles, `english` (default) or `japanese` kana.
|
|
35
24
|
* - `hand`: the opening hand, 3, 7 (default) or 11.
|
|
36
25
|
* - `level`: how many tiles are wild, `easy` (most), `medium` (default) or `hard` (none).
|
|
@@ -42,89 +31,382 @@ const HANDS = Object.values(KUMIMOJI_HANDS);
|
|
|
42
31
|
* It fires `kumimoji-change` after every change to the game, with the game as it would
|
|
43
32
|
* be saved as `event.detail.saved`, and `kumimoji-finish` when the last tile is laid and every run
|
|
44
33
|
* is a word, with `event.detail` the deal, the play and the time it took. It has the methods
|
|
45
|
-
* `newGame()` and `setGame()`.
|
|
34
|
+
* `newGame()` and `setGame()`. Invalid input shows a status and a plain error event.
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* Native table options, checked alongside builder coverage.
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* import { KUMIMOJI_EMBED_OPTION_KEYS } from "@johnmorrisdotca/kumimoji/element";
|
|
41
|
+
* console.log(KUMIMOJI_EMBED_OPTION_KEYS.includes("saved"));
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export const KUMIMOJI_EMBED_OPTION_KEYS = Object.keys({
|
|
45
|
+
language: true,
|
|
46
|
+
hand: true,
|
|
47
|
+
level: true,
|
|
48
|
+
gameLength: true,
|
|
49
|
+
diagonals: true,
|
|
50
|
+
seed: true,
|
|
51
|
+
onFinish: true,
|
|
52
|
+
onChange: true,
|
|
53
|
+
locale: true,
|
|
54
|
+
strings: true,
|
|
55
|
+
theme: true,
|
|
56
|
+
keep: true,
|
|
57
|
+
controls: true,
|
|
58
|
+
saved: true,
|
|
59
|
+
onError: true,
|
|
60
|
+
});
|
|
61
|
+
/**
|
|
62
|
+
* A reflected crossword table with the native table handle and saved-state getter.
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* import { KumimojiTable } from "@johnmorrisdotca/kumimoji/element";
|
|
66
|
+
* console.log(KumimojiTable.observedAttributes.includes("daily"));
|
|
67
|
+
* ```
|
|
46
68
|
*/
|
|
47
69
|
export class KumimojiTable extends ElementBase {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
70
|
+
static observedAttributes = [
|
|
71
|
+
"language",
|
|
72
|
+
"hand",
|
|
73
|
+
"level",
|
|
74
|
+
"length",
|
|
75
|
+
"diagonals",
|
|
76
|
+
"seed",
|
|
77
|
+
"lang",
|
|
78
|
+
"keep",
|
|
79
|
+
"daily",
|
|
80
|
+
"controls",
|
|
81
|
+
"theme",
|
|
82
|
+
"size",
|
|
83
|
+
"width",
|
|
84
|
+
"label",
|
|
85
|
+
];
|
|
86
|
+
#table = null;
|
|
87
|
+
#key = "";
|
|
88
|
+
#queued = false;
|
|
89
|
+
#seed = null;
|
|
90
|
+
#options = {};
|
|
91
|
+
#revision = 0;
|
|
92
|
+
#widthBefore = null;
|
|
93
|
+
#widthOwned = false;
|
|
94
|
+
#labelBefore = null;
|
|
95
|
+
/** Native host options. Callback/presentation changes preserve the current game. */
|
|
96
|
+
get options() {
|
|
97
|
+
return this.#options;
|
|
98
|
+
}
|
|
99
|
+
set options(value) {
|
|
100
|
+
this.#options = value ?? {};
|
|
101
|
+
if (this.isConnected)
|
|
102
|
+
this.#refresh();
|
|
103
|
+
}
|
|
104
|
+
#write(name, value) {
|
|
105
|
+
if (value === null || value === undefined)
|
|
106
|
+
this.removeAttribute(name);
|
|
107
|
+
else
|
|
108
|
+
this.setAttribute(name, String(value));
|
|
55
109
|
}
|
|
56
110
|
connectedCallback() {
|
|
57
|
-
|
|
111
|
+
this.#refresh();
|
|
58
112
|
}
|
|
59
113
|
disconnectedCallback() {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
114
|
+
++this.#revision;
|
|
115
|
+
this.#table?.destroy();
|
|
116
|
+
this.#table = null;
|
|
117
|
+
this.#key = "";
|
|
63
118
|
}
|
|
64
119
|
attributeChangedCallback() {
|
|
65
|
-
if (!this.isConnected ||
|
|
120
|
+
if (!this.isConnected || this.#queued)
|
|
66
121
|
return;
|
|
67
|
-
|
|
122
|
+
this.#queued = true;
|
|
68
123
|
queueMicrotask(() => {
|
|
69
|
-
|
|
70
|
-
|
|
124
|
+
this.#queued = false;
|
|
125
|
+
if (this.isConnected)
|
|
126
|
+
this.#refresh();
|
|
71
127
|
});
|
|
72
128
|
}
|
|
73
129
|
/** The mounted table's handle (`mountKumimoji`), or null while the attributes name no table the rules offer. */
|
|
74
130
|
get table() {
|
|
75
|
-
return
|
|
131
|
+
return this.#table;
|
|
76
132
|
}
|
|
77
133
|
/** The game as it would be saved, or null while there is none (or it is still being dealt). */
|
|
78
134
|
get saved() {
|
|
79
|
-
return
|
|
135
|
+
return this.#table?.saved() ?? null;
|
|
80
136
|
}
|
|
81
137
|
/** A new game at the same table: see `KumimojiTableHandle.newGame`. */
|
|
82
138
|
newGame(options) {
|
|
83
|
-
return
|
|
139
|
+
return this.#table?.newGame(options) ?? Promise.resolve();
|
|
84
140
|
}
|
|
85
141
|
/** Put a game on the table: see `KumimojiTableHandle.setGame`. */
|
|
86
142
|
setGame(saved) {
|
|
87
|
-
return
|
|
143
|
+
return this.#table?.setGame(saved) ?? Promise.resolve();
|
|
144
|
+
}
|
|
145
|
+
/** The `language` attribute, reflected. */
|
|
146
|
+
get language() {
|
|
147
|
+
return this.getAttribute("language");
|
|
148
|
+
}
|
|
149
|
+
set language(value) {
|
|
150
|
+
this.#write("language", value);
|
|
151
|
+
}
|
|
152
|
+
/** The `hand` attribute, reflected. */
|
|
153
|
+
get hand() {
|
|
154
|
+
return this.getAttribute("hand");
|
|
155
|
+
}
|
|
156
|
+
set hand(value) {
|
|
157
|
+
this.#write("hand", value);
|
|
158
|
+
}
|
|
159
|
+
/** The `level` attribute, reflected. */
|
|
160
|
+
get level() {
|
|
161
|
+
return this.getAttribute("level");
|
|
162
|
+
}
|
|
163
|
+
set level(value) {
|
|
164
|
+
this.#write("level", value);
|
|
165
|
+
}
|
|
166
|
+
/** The `length` attribute, reflected. */
|
|
167
|
+
get length() {
|
|
168
|
+
return this.getAttribute("length");
|
|
169
|
+
}
|
|
170
|
+
set length(value) {
|
|
171
|
+
this.#write("length", value);
|
|
172
|
+
}
|
|
173
|
+
/** The `diagonals` attribute, reflected. */
|
|
174
|
+
get diagonals() {
|
|
175
|
+
return this.getAttribute("diagonals");
|
|
176
|
+
}
|
|
177
|
+
set diagonals(value) {
|
|
178
|
+
this.#write("diagonals", value);
|
|
179
|
+
}
|
|
180
|
+
/** The `seed` attribute, reflected. */
|
|
181
|
+
get seed() {
|
|
182
|
+
return this.getAttribute("seed");
|
|
183
|
+
}
|
|
184
|
+
set seed(value) {
|
|
185
|
+
this.#write("seed", value);
|
|
186
|
+
}
|
|
187
|
+
/** The `keep` attribute, reflected. */
|
|
188
|
+
get keep() {
|
|
189
|
+
return this.getAttribute("keep");
|
|
88
190
|
}
|
|
191
|
+
set keep(value) {
|
|
192
|
+
this.#write("keep", value);
|
|
193
|
+
}
|
|
194
|
+
/** The `daily` attribute, reflected. */
|
|
195
|
+
get daily() {
|
|
196
|
+
return this.getAttribute("daily");
|
|
197
|
+
}
|
|
198
|
+
set daily(value) {
|
|
199
|
+
this.#write("daily", value);
|
|
200
|
+
}
|
|
201
|
+
/** The `controls` attribute, reflected. */
|
|
202
|
+
get controls() {
|
|
203
|
+
return this.getAttribute("controls");
|
|
204
|
+
}
|
|
205
|
+
set controls(value) {
|
|
206
|
+
this.#write("controls", value);
|
|
207
|
+
}
|
|
208
|
+
/** The `theme` attribute, reflected. */
|
|
209
|
+
get theme() {
|
|
210
|
+
return this.getAttribute("theme");
|
|
211
|
+
}
|
|
212
|
+
set theme(value) {
|
|
213
|
+
this.#write("theme", value);
|
|
214
|
+
}
|
|
215
|
+
/** The `size` attribute, reflected. */
|
|
216
|
+
get size() {
|
|
217
|
+
return this.getAttribute("size");
|
|
218
|
+
}
|
|
219
|
+
set size(value) {
|
|
220
|
+
this.#write("size", value);
|
|
221
|
+
}
|
|
222
|
+
/** The `width` attribute, reflected. */
|
|
223
|
+
get width() {
|
|
224
|
+
return this.getAttribute("width");
|
|
225
|
+
}
|
|
226
|
+
set width(value) {
|
|
227
|
+
this.#write("width", value);
|
|
228
|
+
}
|
|
229
|
+
/** The `label` attribute, reflected. */
|
|
230
|
+
get label() {
|
|
231
|
+
return this.getAttribute("label");
|
|
232
|
+
}
|
|
233
|
+
set label(value) {
|
|
234
|
+
this.#write("label", value);
|
|
235
|
+
}
|
|
236
|
+
#frame() {
|
|
237
|
+
this.style.display = "block";
|
|
238
|
+
this.style.maxWidth = "100%";
|
|
239
|
+
const width = Number(this.getAttribute("width"));
|
|
240
|
+
const size = this.getAttribute("size");
|
|
241
|
+
const pixels = this.hasAttribute("width") && width > 0 && Number.isFinite(width)
|
|
242
|
+
? Math.min(width, 2000)
|
|
243
|
+
: size === "small"
|
|
244
|
+
? 320
|
|
245
|
+
: size === "medium"
|
|
246
|
+
? 480
|
|
247
|
+
: size === "large"
|
|
248
|
+
? 640
|
|
249
|
+
: null;
|
|
250
|
+
if (pixels !== null) {
|
|
251
|
+
if (!this.#widthOwned)
|
|
252
|
+
this.#widthBefore = this.style.width;
|
|
253
|
+
this.#widthOwned = true;
|
|
254
|
+
this.style.width = `${pixels}px`;
|
|
255
|
+
}
|
|
256
|
+
else if (this.#widthOwned) {
|
|
257
|
+
this.style.width = this.#widthBefore ?? "";
|
|
258
|
+
this.#widthOwned = false;
|
|
259
|
+
}
|
|
260
|
+
const theme = this.getAttribute("theme");
|
|
261
|
+
this.style.colorScheme = theme === "light" || theme === "dark" ? theme : "light dark";
|
|
262
|
+
this.setAttribute("data-kumimoji-embed", "");
|
|
263
|
+
const root = this.getRootNode();
|
|
264
|
+
const target = typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot ? root : this.ownerDocument.head;
|
|
265
|
+
if (target.querySelector("style[data-kumimoji-embed-style]") === null) {
|
|
266
|
+
const style = this.ownerDocument.createElement("style");
|
|
267
|
+
style.setAttribute("data-kumimoji-embed-style", "");
|
|
268
|
+
const light = KUMIMOJI_STYLE.match(/\.km-root \{([^}]+)\}/)?.[1] ?? "";
|
|
269
|
+
const dark = KUMIMOJI_STYLE.match(/@media \(prefers-color-scheme: dark\) \{\s*\.km-root \{([^}]+)\}/)?.[1] ?? "";
|
|
270
|
+
style.textContent = `[data-kumimoji-embed][theme="light"] .km-root{${light}}[data-kumimoji-embed][theme="dark"] .km-root{${dark}}[data-kumimoji-embed]>.km-root{background:var(--km-panel);padding:12px;border-radius:12px}[data-kumimoji-embed] .km-controls[hidden]{display:none}`;
|
|
271
|
+
target.append(style);
|
|
272
|
+
}
|
|
273
|
+
if (this.hasAttribute("label")) {
|
|
274
|
+
this.#labelBefore ??= { role: this.getAttribute("role"), label: this.getAttribute("aria-label") };
|
|
275
|
+
this.setAttribute("role", "group");
|
|
276
|
+
this.setAttribute("aria-label", this.getAttribute("label"));
|
|
277
|
+
}
|
|
278
|
+
else if (this.#labelBefore !== null) {
|
|
279
|
+
for (const [name, value] of [
|
|
280
|
+
["role", this.#labelBefore.role],
|
|
281
|
+
["aria-label", this.#labelBefore.label],
|
|
282
|
+
]) {
|
|
283
|
+
if (value === null)
|
|
284
|
+
this.removeAttribute(name);
|
|
285
|
+
else
|
|
286
|
+
this.setAttribute(name, value);
|
|
287
|
+
}
|
|
288
|
+
this.#labelBefore = null;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
#refresh() {
|
|
292
|
+
this.#frame();
|
|
293
|
+
try {
|
|
294
|
+
const language = oneOf(this.getAttribute("language"), ["english", "japanese"]) ?? this.#options.language;
|
|
295
|
+
const hand = this.hasAttribute("hand") ? Number(this.getAttribute("hand")) : this.#options.hand;
|
|
296
|
+
const level = oneOf(this.getAttribute("level"), ["easy", "medium", "hard"]) ?? this.#options.level;
|
|
297
|
+
const gameLength = oneOf(this.getAttribute("length"), ["short", "medium", "full"]) ?? this.#options.gameLength;
|
|
298
|
+
const diagonals = isOn(this.getAttribute("diagonals"), this.#options.diagonals ?? false);
|
|
299
|
+
const seedAttribute = this.getAttribute("seed");
|
|
300
|
+
const asked = Number(seedAttribute);
|
|
301
|
+
const day = embedDay(this.hasAttribute("daily") ? this.getAttribute("daily") : seedAttribute === "daily" ? "today" : null);
|
|
302
|
+
let seed;
|
|
303
|
+
if (day !== null)
|
|
304
|
+
seed = kumimojiDailySeed(new Date(`${day}T00:00:00Z`));
|
|
305
|
+
else if (seedAttribute !== null && Number.isInteger(asked) && asked >= 0 && asked <= 0xffffffff)
|
|
306
|
+
seed = asked;
|
|
307
|
+
else if (this.#options.seed !== undefined && this.#options.seed !== null)
|
|
308
|
+
seed = this.#options.seed;
|
|
309
|
+
else {
|
|
310
|
+
let held = this.#seed;
|
|
311
|
+
if (held === null) {
|
|
312
|
+
held = Math.floor(Math.random() * 2 ** 31);
|
|
313
|
+
this.#seed = held;
|
|
314
|
+
}
|
|
315
|
+
seed = held;
|
|
316
|
+
}
|
|
317
|
+
const lang = this.getAttribute("lang");
|
|
318
|
+
const locale = lang === "en" || lang === "ja"
|
|
319
|
+
? lang
|
|
320
|
+
: (this.#options.locale ??
|
|
321
|
+
(this.ownerDocument.documentElement.lang.toLowerCase().startsWith("ja") ? "ja" : "en"));
|
|
322
|
+
const keep = isOn(this.getAttribute("keep"), this.#options.keep ?? true);
|
|
323
|
+
const controls = isOn(this.getAttribute("controls"), this.#options.controls ?? true);
|
|
324
|
+
const key = JSON.stringify([language, hand, level, gameLength, diagonals, seed, day, this.#options.saved]);
|
|
325
|
+
if (key === this.#key && this.#table !== null) {
|
|
326
|
+
this.#table.set({ locale, strings: this.#options.strings, theme: this.#options.theme ?? {}, keep, controls });
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
if (hand !== undefined && !HANDS.includes(hand))
|
|
330
|
+
throw new RangeError("Choose a hand of 3, 7 or 11 tiles.");
|
|
331
|
+
this.#table?.destroy();
|
|
332
|
+
this.#table = null;
|
|
333
|
+
this.#key = "";
|
|
334
|
+
this.replaceChildren();
|
|
335
|
+
const revision = ++this.#revision;
|
|
336
|
+
const table = mountKumimoji(this, {
|
|
337
|
+
...this.#options,
|
|
338
|
+
language,
|
|
339
|
+
hand,
|
|
340
|
+
level,
|
|
341
|
+
gameLength,
|
|
342
|
+
diagonals,
|
|
343
|
+
seed,
|
|
344
|
+
locale,
|
|
345
|
+
keep,
|
|
346
|
+
controls,
|
|
347
|
+
onChange: (saved) => {
|
|
348
|
+
this.dispatchEvent(new CustomEvent("kumimoji-change", {
|
|
349
|
+
detail: JSON.parse(JSON.stringify({ saved })),
|
|
350
|
+
bubbles: true,
|
|
351
|
+
composed: true,
|
|
352
|
+
}));
|
|
353
|
+
this.#options.onChange?.(saved);
|
|
354
|
+
},
|
|
355
|
+
onFinish: (result) => {
|
|
356
|
+
this.dispatchEvent(new CustomEvent("kumimoji-finish", {
|
|
357
|
+
detail: JSON.parse(JSON.stringify(result)),
|
|
358
|
+
bubbles: true,
|
|
359
|
+
composed: true,
|
|
360
|
+
}));
|
|
361
|
+
this.#options.onFinish?.(result);
|
|
362
|
+
},
|
|
363
|
+
onError: (error) => {
|
|
364
|
+
this.dispatchEvent(new CustomEvent("kumimoji-error", { detail: { message: error.message }, bubbles: true, composed: true }));
|
|
365
|
+
this.#options.onError?.(error);
|
|
366
|
+
},
|
|
367
|
+
});
|
|
368
|
+
this.#table = table;
|
|
369
|
+
this.#key = key;
|
|
370
|
+
void table.ready
|
|
371
|
+
.then(() => {
|
|
372
|
+
if (this.isConnected && revision === this.#revision && table.saved() !== null)
|
|
373
|
+
this.dispatchEvent(new CustomEvent("kumimoji-load", {
|
|
374
|
+
bubbles: true,
|
|
375
|
+
composed: true,
|
|
376
|
+
detail: { seed: table.saved().deal.seed, day },
|
|
377
|
+
}));
|
|
378
|
+
})
|
|
379
|
+
.catch(() => {
|
|
380
|
+
/* The native ready error handler reports the load failure. */
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
catch (error) {
|
|
384
|
+
this.#table?.destroy();
|
|
385
|
+
this.#table = null;
|
|
386
|
+
this.#key = "";
|
|
387
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
388
|
+
const status = this.ownerDocument.createElement("p");
|
|
389
|
+
status.setAttribute("role", "status");
|
|
390
|
+
status.textContent = message;
|
|
391
|
+
this.replaceChildren(status);
|
|
392
|
+
this.dispatchEvent(new CustomEvent("kumimoji-error", { detail: { message }, bubbles: true, composed: true }));
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Defines the table or a subclass under a custom tag. Server-safe and idempotent.
|
|
398
|
+
* @param tagName - The tag name; kumimoji-table by default.
|
|
399
|
+
* @param registry - The page registry, or undefined on a server.
|
|
400
|
+
* @returns Nothing; existing definitions are left alone.
|
|
401
|
+
* @example
|
|
402
|
+
* ```ts
|
|
403
|
+
* import { defineTable } from "@johnmorrisdotca/kumimoji/element";
|
|
404
|
+
* defineTable();
|
|
405
|
+
* console.log(typeof defineTable);
|
|
406
|
+
* ```
|
|
407
|
+
*/
|
|
408
|
+
export function defineTable(tagName = "kumimoji-table", registry = typeof customElements === "undefined" ? undefined : customElements) {
|
|
409
|
+
if (registry !== undefined && registry.get(tagName) === undefined)
|
|
410
|
+
registry.define(tagName, tagName === "kumimoji-table" ? KumimojiTable : class extends KumimojiTable {
|
|
411
|
+
});
|
|
89
412
|
}
|
|
90
|
-
_KumimojiTable_table = new WeakMap(), _KumimojiTable_key = new WeakMap(), _KumimojiTable_queued = new WeakMap(), _KumimojiTable_seed = new WeakMap(), _KumimojiTable_instances = new WeakSet(), _KumimojiTable_refresh = function _KumimojiTable_refresh() {
|
|
91
|
-
const language = oneOf(this.getAttribute("language"), ["english", "japanese"]);
|
|
92
|
-
const handAttribute = this.getAttribute("hand");
|
|
93
|
-
const hand = handAttribute === null ? undefined : Number(handAttribute);
|
|
94
|
-
const level = oneOf(this.getAttribute("level"), ["easy", "medium", "hard"]);
|
|
95
|
-
const gameLength = oneOf(this.getAttribute("length"), ["short", "medium", "full"]);
|
|
96
|
-
const diagonals = isOn(this.getAttribute("diagonals"));
|
|
97
|
-
const seedAttribute = this.getAttribute("seed");
|
|
98
|
-
const asked = Number(seedAttribute);
|
|
99
|
-
const seed = seedAttribute === "daily"
|
|
100
|
-
? kumimojiDailySeed(new Date())
|
|
101
|
-
: seedAttribute !== null && Number.isInteger(asked) && asked >= 0 && asked <= 0xffffffff
|
|
102
|
-
? asked
|
|
103
|
-
: (__classPrivateFieldSet(this, _KumimojiTable_seed, __classPrivateFieldGet(this, _KumimojiTable_seed, "f") ?? Math.floor(Math.random() * 2 ** 31), "f"));
|
|
104
|
-
const lang = this.getAttribute("lang");
|
|
105
|
-
const locale = lang === "en" || lang === "ja" ? lang : undefined;
|
|
106
|
-
const keep = !["false", "off", "0", "no"].includes((this.getAttribute("keep") ?? "").toLowerCase());
|
|
107
|
-
const key = JSON.stringify([language, hand, level, gameLength, diagonals, seed, keep]);
|
|
108
|
-
if (key === __classPrivateFieldGet(this, _KumimojiTable_key, "f") && __classPrivateFieldGet(this, _KumimojiTable_table, "f") !== null) {
|
|
109
|
-
__classPrivateFieldGet(this, _KumimojiTable_table, "f").setLocale(locale ?? (document.documentElement.lang.toLowerCase().startsWith("ja") ? "ja" : "en"));
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
__classPrivateFieldGet(this, _KumimojiTable_table, "f")?.destroy();
|
|
113
|
-
__classPrivateFieldSet(this, _KumimojiTable_table, null, "f");
|
|
114
|
-
__classPrivateFieldSet(this, _KumimojiTable_key, key, "f");
|
|
115
|
-
if (hand !== undefined && !HANDS.includes(hand))
|
|
116
|
-
return;
|
|
117
|
-
__classPrivateFieldSet(this, _KumimojiTable_table, mountKumimoji(this, {
|
|
118
|
-
language,
|
|
119
|
-
hand,
|
|
120
|
-
level,
|
|
121
|
-
gameLength,
|
|
122
|
-
diagonals,
|
|
123
|
-
seed,
|
|
124
|
-
locale,
|
|
125
|
-
keep,
|
|
126
|
-
onChange: (saved) => this.dispatchEvent(new CustomEvent("kumimoji-change", { detail: { saved }, bubbles: true })),
|
|
127
|
-
onFinish: (result) => this.dispatchEvent(new CustomEvent("kumimoji-finish", { detail: result, bubbles: true })),
|
|
128
|
-
}), "f");
|
|
129
|
-
};
|
|
130
|
-
KumimojiTable.observedAttributes = ["language", "hand", "level", "length", "diagonals", "seed", "lang", "keep"];
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Shared UTC day parsing for the family's daily embeds. No storage, service or rollover timer. */
|
|
2
|
+
export declare function embedDay(value: string | null | undefined, now?: Date): string | null;
|
|
3
|
+
/** The same day and namespace give the same nonzero 32-bit seed on every machine. */
|
|
4
|
+
export declare function dailySeed(day: string, namespace: string): number;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Shared UTC day parsing for the family's daily embeds. No storage, service or rollover timer. */
|
|
2
|
+
export function embedDay(value, now = new Date()) {
|
|
3
|
+
if (value === null || value === undefined || ["off", "false", "no", "0"].includes(value.trim().toLowerCase()))
|
|
4
|
+
return null;
|
|
5
|
+
const asked = value.trim().toLowerCase();
|
|
6
|
+
const day = ["", "today", "on", "true", "1"].includes(asked) ? Number.isNaN(now.getTime()) ? "" : now.toISOString().slice(0, 10) : value.trim();
|
|
7
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(day))
|
|
8
|
+
throw new RangeError("Use a real UTC date written YYYY-MM-DD.");
|
|
9
|
+
const time = Date.parse(`${day}T00:00:00Z`);
|
|
10
|
+
if (!Number.isFinite(time) || new Date(time).toISOString().slice(0, 10) !== day)
|
|
11
|
+
throw new RangeError("Use a real UTC date written YYYY-MM-DD.");
|
|
12
|
+
return day;
|
|
13
|
+
}
|
|
14
|
+
/** The same day and namespace give the same nonzero 32-bit seed on every machine. */
|
|
15
|
+
export function dailySeed(day, namespace) {
|
|
16
|
+
const parsed = embedDay(day);
|
|
17
|
+
if (parsed === null)
|
|
18
|
+
throw new RangeError("A daily seed needs a UTC date.");
|
|
19
|
+
let hash = 2166136261;
|
|
20
|
+
for (const letter of `${parsed}/${namespace}`)
|
|
21
|
+
hash = Math.imul(hash ^ letter.charCodeAt(0), 16777619) >>> 0;
|
|
22
|
+
return hash || 1;
|
|
23
|
+
}
|