@beta-gamer/angular 0.1.2 → 0.1.4
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/README.md +66 -14
- package/dist/index.d.mts +122 -25
- package/dist/index.d.ts +122 -25
- package/dist/index.js +149 -94
- package/dist/index.mjs +148 -93
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -13,9 +13,8 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
13
13
|
import { Injectable, signal } from "@angular/core";
|
|
14
14
|
import { io } from "socket.io-client";
|
|
15
15
|
function decodeToken(token) {
|
|
16
|
-
if (!token || typeof token !== "string")
|
|
16
|
+
if (!token || typeof token !== "string")
|
|
17
17
|
throw new Error("@beta-gamer/angular: token is required and must be a string");
|
|
18
|
-
}
|
|
19
18
|
const parts = token.split(".");
|
|
20
19
|
if (parts.length < 2) throw new Error("@beta-gamer/angular: invalid session token format");
|
|
21
20
|
const base64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
@@ -26,20 +25,33 @@ var BetaGamerService = class {
|
|
|
26
25
|
constructor() {
|
|
27
26
|
this._socket = null;
|
|
28
27
|
this._token = "";
|
|
28
|
+
/** Decoded session token payload — null until `init()` is called */
|
|
29
29
|
this.session = signal(null);
|
|
30
|
+
/** Live game state updated by server events */
|
|
30
31
|
this.gameState = signal({ status: "pending", players: [] });
|
|
31
32
|
}
|
|
32
33
|
get token() {
|
|
33
34
|
return this._token;
|
|
34
35
|
}
|
|
36
|
+
/** The active socket.io connection — available after `init()` with `connectSocket=true` */
|
|
35
37
|
get socket() {
|
|
36
38
|
return this._socket;
|
|
37
39
|
}
|
|
38
|
-
|
|
40
|
+
/**
|
|
41
|
+
* @description Initialises the service with a session token.
|
|
42
|
+
* Decodes the token, sets up reactive state, and optionally connects the socket.
|
|
43
|
+
*
|
|
44
|
+
* @param token - JWT session token from your backend
|
|
45
|
+
* @param serverUrl - Base URL of the Beta Gamer server
|
|
46
|
+
* @param socketPath - socket.io path (default `/socket.io`)
|
|
47
|
+
* @param connectSocket - Set false to skip socket connection (e.g. for SSR or testing)
|
|
48
|
+
*/
|
|
49
|
+
init(token, serverUrl = "https://api.beta-gamer.com", socketPath = "/socket.io", connectSocket = true) {
|
|
39
50
|
this._token = token;
|
|
40
51
|
const payload = decodeToken(token);
|
|
41
52
|
this.session.set(payload);
|
|
42
53
|
this.gameState.set({ status: "pending", players: payload.players });
|
|
54
|
+
if (!connectSocket) return;
|
|
43
55
|
const s = io(`${serverUrl}/${payload.game}`, {
|
|
44
56
|
auth: { token },
|
|
45
57
|
path: socketPath,
|
|
@@ -114,7 +126,7 @@ var TimerComponent = class {
|
|
|
114
126
|
constructor() {
|
|
115
127
|
this.player = "self";
|
|
116
128
|
this.initialSeconds = 600;
|
|
117
|
-
this.seconds =
|
|
129
|
+
this.seconds = 600;
|
|
118
130
|
this.interval = null;
|
|
119
131
|
this.svc = inject2(BetaGamerService);
|
|
120
132
|
}
|
|
@@ -125,20 +137,17 @@ var TimerComponent = class {
|
|
|
125
137
|
}
|
|
126
138
|
ngOnInit() {
|
|
127
139
|
this.seconds = this.initialSeconds;
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (clocks[this.player] !== void 0) this.seconds = clocks[this.player];
|
|
132
|
-
});
|
|
133
|
-
}
|
|
140
|
+
this.svc.socket?.on("game:clock", (clocks) => {
|
|
141
|
+
if (clocks[this.player] !== void 0) this.seconds = clocks[this.player];
|
|
142
|
+
});
|
|
134
143
|
this.interval = setInterval(() => {
|
|
135
|
-
if (this.svc.gameState().status === "active")
|
|
144
|
+
if (this.svc.gameState().status === "active")
|
|
136
145
|
this.seconds = Math.max(0, this.seconds - 1);
|
|
137
|
-
}
|
|
138
146
|
}, 1e3);
|
|
139
147
|
}
|
|
140
148
|
ngOnDestroy() {
|
|
141
149
|
if (this.interval) clearInterval(this.interval);
|
|
150
|
+
this.svc.socket?.off("game:clock");
|
|
142
151
|
}
|
|
143
152
|
};
|
|
144
153
|
__decorateClass([
|
|
@@ -159,34 +168,91 @@ TimerComponent = __decorateClass([
|
|
|
159
168
|
})
|
|
160
169
|
], TimerComponent);
|
|
161
170
|
|
|
162
|
-
// src/components/chess/
|
|
163
|
-
import { Component as Component3, Input as
|
|
164
|
-
|
|
171
|
+
// src/components/chess/ChessBoardComponent.ts
|
|
172
|
+
import { Component as Component3, Input as Input4 } from "@angular/core";
|
|
173
|
+
|
|
174
|
+
// src/components/shared/EmbedBoardBase.ts
|
|
175
|
+
import { Directive, Input as Input3, Output, EventEmitter, ViewChild, inject as inject3 } from "@angular/core";
|
|
176
|
+
var EmbedBoardBase = class {
|
|
165
177
|
constructor() {
|
|
166
178
|
this.serverUrl = "https://api.beta-gamer.com";
|
|
167
|
-
this.
|
|
179
|
+
this.onLeave = new EventEmitter();
|
|
168
180
|
this.svc = inject3(BetaGamerService);
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
181
|
+
this.messageHandler = (e) => {
|
|
182
|
+
try {
|
|
183
|
+
const data = typeof e.data === "string" ? JSON.parse(e.data) : e.data;
|
|
184
|
+
if (data?.type === "bg:leave") this.onLeave.emit();
|
|
185
|
+
} catch {
|
|
186
|
+
}
|
|
187
|
+
};
|
|
172
188
|
}
|
|
173
189
|
ngAfterViewInit() {
|
|
190
|
+
window.addEventListener("message", this.messageHandler);
|
|
174
191
|
const iframe = this.iframeRef?.nativeElement;
|
|
175
192
|
if (!iframe) return;
|
|
176
193
|
iframe.addEventListener("load", () => {
|
|
177
194
|
iframe.contentWindow?.postMessage({ type: "bg:init", token: this.svc.token }, "*");
|
|
178
195
|
});
|
|
179
196
|
}
|
|
197
|
+
ngOnDestroy() {
|
|
198
|
+
window.removeEventListener("message", this.messageHandler);
|
|
199
|
+
}
|
|
200
|
+
/** Builds a query string from a params object, omitting undefined values */
|
|
201
|
+
buildQuery(params) {
|
|
202
|
+
const p = new URLSearchParams();
|
|
203
|
+
for (const [k, v] of Object.entries(params)) if (v !== void 0) p.set(k, v);
|
|
204
|
+
const qs = p.toString();
|
|
205
|
+
return qs ? "?" + qs : "";
|
|
206
|
+
}
|
|
180
207
|
};
|
|
181
208
|
__decorateClass([
|
|
182
209
|
Input3()
|
|
183
|
-
],
|
|
210
|
+
], EmbedBoardBase.prototype, "serverUrl", 2);
|
|
184
211
|
__decorateClass([
|
|
185
|
-
|
|
186
|
-
],
|
|
212
|
+
Output()
|
|
213
|
+
], EmbedBoardBase.prototype, "onLeave", 2);
|
|
187
214
|
__decorateClass([
|
|
188
215
|
ViewChild("iframe")
|
|
189
|
-
],
|
|
216
|
+
], EmbedBoardBase.prototype, "iframeRef", 2);
|
|
217
|
+
EmbedBoardBase = __decorateClass([
|
|
218
|
+
Directive()
|
|
219
|
+
], EmbedBoardBase);
|
|
220
|
+
|
|
221
|
+
// src/components/chess/ChessBoardComponent.ts
|
|
222
|
+
var ChessBoardComponent = class extends EmbedBoardBase {
|
|
223
|
+
constructor() {
|
|
224
|
+
super(...arguments);
|
|
225
|
+
this.showAfkWarning = true;
|
|
226
|
+
this.showCoords = true;
|
|
227
|
+
this.showLastMove = true;
|
|
228
|
+
this.showLegalMoves = true;
|
|
229
|
+
this.orientation = "auto";
|
|
230
|
+
}
|
|
231
|
+
get embedUrl() {
|
|
232
|
+
return this.serverUrl + "/embed/chess" + this.buildQuery({
|
|
233
|
+
afkWarning: !this.showAfkWarning ? "0" : void 0,
|
|
234
|
+
showCoords: !this.showCoords ? "0" : void 0,
|
|
235
|
+
showLastMove: !this.showLastMove ? "0" : void 0,
|
|
236
|
+
showLegalMoves: !this.showLegalMoves ? "0" : void 0,
|
|
237
|
+
orientation: this.orientation !== "auto" ? this.orientation : void 0
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
__decorateClass([
|
|
242
|
+
Input4()
|
|
243
|
+
], ChessBoardComponent.prototype, "showAfkWarning", 2);
|
|
244
|
+
__decorateClass([
|
|
245
|
+
Input4()
|
|
246
|
+
], ChessBoardComponent.prototype, "showCoords", 2);
|
|
247
|
+
__decorateClass([
|
|
248
|
+
Input4()
|
|
249
|
+
], ChessBoardComponent.prototype, "showLastMove", 2);
|
|
250
|
+
__decorateClass([
|
|
251
|
+
Input4()
|
|
252
|
+
], ChessBoardComponent.prototype, "showLegalMoves", 2);
|
|
253
|
+
__decorateClass([
|
|
254
|
+
Input4()
|
|
255
|
+
], ChessBoardComponent.prototype, "orientation", 2);
|
|
190
256
|
ChessBoardComponent = __decorateClass([
|
|
191
257
|
Component3({
|
|
192
258
|
selector: "bg-chess-board",
|
|
@@ -198,19 +264,25 @@ ChessBoardComponent = __decorateClass([
|
|
|
198
264
|
`
|
|
199
265
|
})
|
|
200
266
|
], ChessBoardComponent);
|
|
267
|
+
|
|
268
|
+
// src/components/chess/ChessMoveHistoryComponent.ts
|
|
269
|
+
import { Component as Component4 } from "@angular/core";
|
|
201
270
|
var ChessMoveHistoryComponent = class {
|
|
202
271
|
};
|
|
203
272
|
ChessMoveHistoryComponent = __decorateClass([
|
|
204
|
-
|
|
273
|
+
Component4({
|
|
205
274
|
selector: "bg-chess-move-history",
|
|
206
275
|
standalone: true,
|
|
207
276
|
template: `<div data-role="move-history"></div>`
|
|
208
277
|
})
|
|
209
278
|
], ChessMoveHistoryComponent);
|
|
279
|
+
|
|
280
|
+
// src/components/chess/ChessCapturedPiecesComponent.ts
|
|
281
|
+
import { Component as Component5 } from "@angular/core";
|
|
210
282
|
var ChessCapturedPiecesComponent = class {
|
|
211
283
|
};
|
|
212
284
|
ChessCapturedPiecesComponent = __decorateClass([
|
|
213
|
-
|
|
285
|
+
Component5({
|
|
214
286
|
selector: "bg-chess-captured-pieces",
|
|
215
287
|
standalone: true,
|
|
216
288
|
template: `<div data-role="captured-pieces"></div>`
|
|
@@ -218,35 +290,33 @@ ChessCapturedPiecesComponent = __decorateClass([
|
|
|
218
290
|
], ChessCapturedPiecesComponent);
|
|
219
291
|
|
|
220
292
|
// src/components/checkers/index.ts
|
|
221
|
-
import { Component as
|
|
222
|
-
var CheckersBoardComponent = class {
|
|
293
|
+
import { Component as Component6, Input as Input5 } from "@angular/core";
|
|
294
|
+
var CheckersBoardComponent = class extends EmbedBoardBase {
|
|
223
295
|
constructor() {
|
|
224
|
-
|
|
296
|
+
super(...arguments);
|
|
225
297
|
this.showAfkWarning = true;
|
|
226
|
-
this.
|
|
298
|
+
this.showLastMove = true;
|
|
299
|
+
this.orientation = "auto";
|
|
227
300
|
}
|
|
228
301
|
get embedUrl() {
|
|
229
|
-
return this.
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
if (!iframe) return;
|
|
234
|
-
iframe.addEventListener("load", () => {
|
|
235
|
-
iframe.contentWindow?.postMessage({ type: "bg:init", token: this.svc.token }, "*");
|
|
302
|
+
return this.serverUrl + "/embed/checkers" + this.buildQuery({
|
|
303
|
+
afkWarning: !this.showAfkWarning ? "0" : void 0,
|
|
304
|
+
showLastMove: !this.showLastMove ? "0" : void 0,
|
|
305
|
+
orientation: this.orientation !== "auto" ? this.orientation : void 0
|
|
236
306
|
});
|
|
237
307
|
}
|
|
238
308
|
};
|
|
239
309
|
__decorateClass([
|
|
240
|
-
|
|
241
|
-
], CheckersBoardComponent.prototype, "serverUrl", 2);
|
|
242
|
-
__decorateClass([
|
|
243
|
-
Input4()
|
|
310
|
+
Input5()
|
|
244
311
|
], CheckersBoardComponent.prototype, "showAfkWarning", 2);
|
|
245
312
|
__decorateClass([
|
|
246
|
-
|
|
247
|
-
], CheckersBoardComponent.prototype, "
|
|
313
|
+
Input5()
|
|
314
|
+
], CheckersBoardComponent.prototype, "showLastMove", 2);
|
|
315
|
+
__decorateClass([
|
|
316
|
+
Input5()
|
|
317
|
+
], CheckersBoardComponent.prototype, "orientation", 2);
|
|
248
318
|
CheckersBoardComponent = __decorateClass([
|
|
249
|
-
|
|
319
|
+
Component6({
|
|
250
320
|
selector: "bg-checkers-board",
|
|
251
321
|
standalone: true,
|
|
252
322
|
template: `
|
|
@@ -257,36 +327,24 @@ CheckersBoardComponent = __decorateClass([
|
|
|
257
327
|
})
|
|
258
328
|
], CheckersBoardComponent);
|
|
259
329
|
|
|
260
|
-
// src/components/connect4/
|
|
261
|
-
import { Component as
|
|
262
|
-
var Connect4BoardComponent = class {
|
|
330
|
+
// src/components/connect4/Connect4BoardComponent.ts
|
|
331
|
+
import { Component as Component7, Input as Input6 } from "@angular/core";
|
|
332
|
+
var Connect4BoardComponent = class extends EmbedBoardBase {
|
|
263
333
|
constructor() {
|
|
264
|
-
|
|
334
|
+
super(...arguments);
|
|
265
335
|
this.showAfkWarning = true;
|
|
266
|
-
this.svc = inject5(BetaGamerService);
|
|
267
336
|
}
|
|
268
337
|
get embedUrl() {
|
|
269
|
-
return this.
|
|
270
|
-
|
|
271
|
-
ngAfterViewInit() {
|
|
272
|
-
const iframe = this.iframeRef?.nativeElement;
|
|
273
|
-
if (!iframe) return;
|
|
274
|
-
iframe.addEventListener("load", () => {
|
|
275
|
-
iframe.contentWindow?.postMessage({ type: "bg:init", token: this.svc.token }, "*");
|
|
338
|
+
return this.serverUrl + "/embed/connect4" + this.buildQuery({
|
|
339
|
+
afkWarning: !this.showAfkWarning ? "0" : void 0
|
|
276
340
|
});
|
|
277
341
|
}
|
|
278
342
|
};
|
|
279
343
|
__decorateClass([
|
|
280
|
-
|
|
281
|
-
], Connect4BoardComponent.prototype, "serverUrl", 2);
|
|
282
|
-
__decorateClass([
|
|
283
|
-
Input5()
|
|
344
|
+
Input6()
|
|
284
345
|
], Connect4BoardComponent.prototype, "showAfkWarning", 2);
|
|
285
|
-
__decorateClass([
|
|
286
|
-
ViewChild3("iframe")
|
|
287
|
-
], Connect4BoardComponent.prototype, "iframeRef", 2);
|
|
288
346
|
Connect4BoardComponent = __decorateClass([
|
|
289
|
-
|
|
347
|
+
Component7({
|
|
290
348
|
selector: "bg-connect4-board",
|
|
291
349
|
standalone: true,
|
|
292
350
|
template: `
|
|
@@ -296,9 +354,12 @@ Connect4BoardComponent = __decorateClass([
|
|
|
296
354
|
`
|
|
297
355
|
})
|
|
298
356
|
], Connect4BoardComponent);
|
|
357
|
+
|
|
358
|
+
// src/components/connect4/Connect4ScoreComponent.ts
|
|
359
|
+
import { Component as Component8, inject as inject4 } from "@angular/core";
|
|
299
360
|
var Connect4ScoreComponent = class {
|
|
300
361
|
constructor() {
|
|
301
|
-
this.svc =
|
|
362
|
+
this.svc = inject4(BetaGamerService);
|
|
302
363
|
}
|
|
303
364
|
get scores() {
|
|
304
365
|
const s = this.svc.gameState()?.scores ?? {};
|
|
@@ -306,7 +367,7 @@ var Connect4ScoreComponent = class {
|
|
|
306
367
|
}
|
|
307
368
|
};
|
|
308
369
|
Connect4ScoreComponent = __decorateClass([
|
|
309
|
-
|
|
370
|
+
Component8({
|
|
310
371
|
selector: "bg-connect4-score",
|
|
311
372
|
standalone: true,
|
|
312
373
|
template: `
|
|
@@ -320,35 +381,23 @@ Connect4ScoreComponent = __decorateClass([
|
|
|
320
381
|
], Connect4ScoreComponent);
|
|
321
382
|
|
|
322
383
|
// src/components/tictactoe/index.ts
|
|
323
|
-
import { Component as
|
|
324
|
-
var TictactoeBoardComponent = class {
|
|
384
|
+
import { Component as Component9, Input as Input7 } from "@angular/core";
|
|
385
|
+
var TictactoeBoardComponent = class extends EmbedBoardBase {
|
|
325
386
|
constructor() {
|
|
326
|
-
|
|
387
|
+
super(...arguments);
|
|
327
388
|
this.showAfkWarning = true;
|
|
328
|
-
this.svc = inject6(BetaGamerService);
|
|
329
389
|
}
|
|
330
390
|
get embedUrl() {
|
|
331
|
-
return this.
|
|
332
|
-
|
|
333
|
-
ngAfterViewInit() {
|
|
334
|
-
const iframe = this.iframeRef?.nativeElement;
|
|
335
|
-
if (!iframe) return;
|
|
336
|
-
iframe.addEventListener("load", () => {
|
|
337
|
-
iframe.contentWindow?.postMessage({ type: "bg:init", token: this.svc.token }, "*");
|
|
391
|
+
return this.serverUrl + "/embed/tictactoe" + this.buildQuery({
|
|
392
|
+
afkWarning: !this.showAfkWarning ? "0" : void 0
|
|
338
393
|
});
|
|
339
394
|
}
|
|
340
395
|
};
|
|
341
396
|
__decorateClass([
|
|
342
|
-
|
|
343
|
-
], TictactoeBoardComponent.prototype, "serverUrl", 2);
|
|
344
|
-
__decorateClass([
|
|
345
|
-
Input6()
|
|
397
|
+
Input7()
|
|
346
398
|
], TictactoeBoardComponent.prototype, "showAfkWarning", 2);
|
|
347
|
-
__decorateClass([
|
|
348
|
-
ViewChild4("iframe")
|
|
349
|
-
], TictactoeBoardComponent.prototype, "iframeRef", 2);
|
|
350
399
|
TictactoeBoardComponent = __decorateClass([
|
|
351
|
-
|
|
400
|
+
Component9({
|
|
352
401
|
selector: "bg-tictactoe-board",
|
|
353
402
|
standalone: true,
|
|
354
403
|
template: `
|
|
@@ -359,12 +408,12 @@ TictactoeBoardComponent = __decorateClass([
|
|
|
359
408
|
})
|
|
360
409
|
], TictactoeBoardComponent);
|
|
361
410
|
|
|
362
|
-
// src/components/subway-runner/
|
|
363
|
-
import { Component as
|
|
411
|
+
// src/components/subway-runner/SubwayRunnerGameComponent.ts
|
|
412
|
+
import { Component as Component10, Input as Input8, ViewChild as ViewChild2, inject as inject5 } from "@angular/core";
|
|
364
413
|
var SubwayRunnerGameComponent = class {
|
|
365
414
|
constructor() {
|
|
366
415
|
this.serverUrl = "https://api.beta-gamer.com";
|
|
367
|
-
this.svc =
|
|
416
|
+
this.svc = inject5(BetaGamerService);
|
|
368
417
|
}
|
|
369
418
|
get embedUrl() {
|
|
370
419
|
return `${this.serverUrl}/embed/subway-runner`;
|
|
@@ -378,13 +427,13 @@ var SubwayRunnerGameComponent = class {
|
|
|
378
427
|
}
|
|
379
428
|
};
|
|
380
429
|
__decorateClass([
|
|
381
|
-
|
|
430
|
+
Input8()
|
|
382
431
|
], SubwayRunnerGameComponent.prototype, "serverUrl", 2);
|
|
383
432
|
__decorateClass([
|
|
384
|
-
|
|
433
|
+
ViewChild2("iframe")
|
|
385
434
|
], SubwayRunnerGameComponent.prototype, "iframeRef", 2);
|
|
386
435
|
SubwayRunnerGameComponent = __decorateClass([
|
|
387
|
-
|
|
436
|
+
Component10({
|
|
388
437
|
selector: "bg-subway-runner",
|
|
389
438
|
standalone: true,
|
|
390
439
|
template: `
|
|
@@ -394,21 +443,27 @@ SubwayRunnerGameComponent = __decorateClass([
|
|
|
394
443
|
`
|
|
395
444
|
})
|
|
396
445
|
], SubwayRunnerGameComponent);
|
|
446
|
+
|
|
447
|
+
// src/components/subway-runner/SubwayRunnerScoreComponent.ts
|
|
448
|
+
import { Component as Component11, inject as inject6 } from "@angular/core";
|
|
397
449
|
var SubwayRunnerScoreComponent = class {
|
|
398
450
|
constructor() {
|
|
399
|
-
this.svc =
|
|
451
|
+
this.svc = inject6(BetaGamerService);
|
|
400
452
|
}
|
|
401
453
|
get score() {
|
|
402
454
|
return this.svc.gameState()?.score ?? 0;
|
|
403
455
|
}
|
|
404
456
|
};
|
|
405
457
|
SubwayRunnerScoreComponent = __decorateClass([
|
|
406
|
-
|
|
458
|
+
Component11({
|
|
407
459
|
selector: "bg-subway-runner-score",
|
|
408
460
|
standalone: true,
|
|
409
461
|
template: `<div data-role="score"><span data-role="score-value">{{ score }}</span></div>`
|
|
410
462
|
})
|
|
411
463
|
], SubwayRunnerScoreComponent);
|
|
464
|
+
|
|
465
|
+
// src/components/subway-runner/SubwayRunnerLivesComponent.ts
|
|
466
|
+
import { Component as Component12, inject as inject7 } from "@angular/core";
|
|
412
467
|
var SubwayRunnerLivesComponent = class {
|
|
413
468
|
constructor() {
|
|
414
469
|
this.svc = inject7(BetaGamerService);
|
|
@@ -418,7 +473,7 @@ var SubwayRunnerLivesComponent = class {
|
|
|
418
473
|
}
|
|
419
474
|
};
|
|
420
475
|
SubwayRunnerLivesComponent = __decorateClass([
|
|
421
|
-
|
|
476
|
+
Component12({
|
|
422
477
|
selector: "bg-subway-runner-lives",
|
|
423
478
|
standalone: true,
|
|
424
479
|
template: `<div data-role="lives"><span data-role="lives-value">{{ lives }}</span></div>`
|