@beta-gamer/angular 0.1.1 → 0.1.3
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 +185 -0
- package/dist/index.d.mts +125 -24
- package/dist/index.d.ts +125 -24
- package/dist/index.js +156 -85
- package/dist/index.mjs +155 -84
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -48,9 +48,8 @@ module.exports = __toCommonJS(index_exports);
|
|
|
48
48
|
var import_core = require("@angular/core");
|
|
49
49
|
var import_socket = require("socket.io-client");
|
|
50
50
|
function decodeToken(token) {
|
|
51
|
-
if (!token || typeof token !== "string")
|
|
51
|
+
if (!token || typeof token !== "string")
|
|
52
52
|
throw new Error("@beta-gamer/angular: token is required and must be a string");
|
|
53
|
-
}
|
|
54
53
|
const parts = token.split(".");
|
|
55
54
|
if (parts.length < 2) throw new Error("@beta-gamer/angular: invalid session token format");
|
|
56
55
|
const base64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
@@ -61,20 +60,33 @@ var BetaGamerService = class {
|
|
|
61
60
|
constructor() {
|
|
62
61
|
this._socket = null;
|
|
63
62
|
this._token = "";
|
|
63
|
+
/** Decoded session token payload — null until `init()` is called */
|
|
64
64
|
this.session = (0, import_core.signal)(null);
|
|
65
|
+
/** Live game state updated by server events */
|
|
65
66
|
this.gameState = (0, import_core.signal)({ status: "pending", players: [] });
|
|
66
67
|
}
|
|
67
68
|
get token() {
|
|
68
69
|
return this._token;
|
|
69
70
|
}
|
|
71
|
+
/** The active socket.io connection — available after `init()` with `connectSocket=true` */
|
|
70
72
|
get socket() {
|
|
71
73
|
return this._socket;
|
|
72
74
|
}
|
|
73
|
-
|
|
75
|
+
/**
|
|
76
|
+
* @description Initialises the service with a session token.
|
|
77
|
+
* Decodes the token, sets up reactive state, and optionally connects the socket.
|
|
78
|
+
*
|
|
79
|
+
* @param token - JWT session token from your backend
|
|
80
|
+
* @param serverUrl - Base URL of the Beta Gamer server
|
|
81
|
+
* @param socketPath - socket.io path (default `/socket.io`)
|
|
82
|
+
* @param connectSocket - Set false to skip socket connection (e.g. for SSR or testing)
|
|
83
|
+
*/
|
|
84
|
+
init(token, serverUrl = "https://api.beta-gamer.com", socketPath = "/socket.io", connectSocket = true) {
|
|
74
85
|
this._token = token;
|
|
75
86
|
const payload = decodeToken(token);
|
|
76
87
|
this.session.set(payload);
|
|
77
88
|
this.gameState.set({ status: "pending", players: payload.players });
|
|
89
|
+
if (!connectSocket) return;
|
|
78
90
|
const s = (0, import_socket.io)(`${serverUrl}/${payload.game}`, {
|
|
79
91
|
auth: { token },
|
|
80
92
|
path: socketPath,
|
|
@@ -149,7 +161,7 @@ var TimerComponent = class {
|
|
|
149
161
|
constructor() {
|
|
150
162
|
this.player = "self";
|
|
151
163
|
this.initialSeconds = 600;
|
|
152
|
-
this.seconds =
|
|
164
|
+
this.seconds = 600;
|
|
153
165
|
this.interval = null;
|
|
154
166
|
this.svc = (0, import_core3.inject)(BetaGamerService);
|
|
155
167
|
}
|
|
@@ -160,20 +172,17 @@ var TimerComponent = class {
|
|
|
160
172
|
}
|
|
161
173
|
ngOnInit() {
|
|
162
174
|
this.seconds = this.initialSeconds;
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
if (clocks[this.player] !== void 0) this.seconds = clocks[this.player];
|
|
167
|
-
});
|
|
168
|
-
}
|
|
175
|
+
this.svc.socket?.on("game:clock", (clocks) => {
|
|
176
|
+
if (clocks[this.player] !== void 0) this.seconds = clocks[this.player];
|
|
177
|
+
});
|
|
169
178
|
this.interval = setInterval(() => {
|
|
170
|
-
if (this.svc.gameState().status === "active")
|
|
179
|
+
if (this.svc.gameState().status === "active")
|
|
171
180
|
this.seconds = Math.max(0, this.seconds - 1);
|
|
172
|
-
}
|
|
173
181
|
}, 1e3);
|
|
174
182
|
}
|
|
175
183
|
ngOnDestroy() {
|
|
176
184
|
if (this.interval) clearInterval(this.interval);
|
|
185
|
+
this.svc.socket?.off("game:clock");
|
|
177
186
|
}
|
|
178
187
|
};
|
|
179
188
|
__decorateClass([
|
|
@@ -194,32 +203,93 @@ TimerComponent = __decorateClass([
|
|
|
194
203
|
})
|
|
195
204
|
], TimerComponent);
|
|
196
205
|
|
|
197
|
-
// src/components/chess/
|
|
206
|
+
// src/components/chess/ChessBoardComponent.ts
|
|
207
|
+
var import_core5 = require("@angular/core");
|
|
208
|
+
|
|
209
|
+
// src/components/shared/EmbedBoardBase.ts
|
|
198
210
|
var import_core4 = require("@angular/core");
|
|
199
|
-
var
|
|
211
|
+
var EmbedBoardBase = class {
|
|
200
212
|
constructor() {
|
|
201
213
|
this.serverUrl = "https://api.beta-gamer.com";
|
|
214
|
+
this.onLeave = new import_core4.EventEmitter();
|
|
202
215
|
this.svc = (0, import_core4.inject)(BetaGamerService);
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
216
|
+
this.messageHandler = (e) => {
|
|
217
|
+
try {
|
|
218
|
+
const data = typeof e.data === "string" ? JSON.parse(e.data) : e.data;
|
|
219
|
+
if (data?.type === "bg:leave") this.onLeave.emit();
|
|
220
|
+
} catch {
|
|
221
|
+
}
|
|
222
|
+
};
|
|
206
223
|
}
|
|
207
224
|
ngAfterViewInit() {
|
|
225
|
+
window.addEventListener("message", this.messageHandler);
|
|
208
226
|
const iframe = this.iframeRef?.nativeElement;
|
|
209
227
|
if (!iframe) return;
|
|
210
228
|
iframe.addEventListener("load", () => {
|
|
211
229
|
iframe.contentWindow?.postMessage({ type: "bg:init", token: this.svc.token }, "*");
|
|
212
230
|
});
|
|
213
231
|
}
|
|
232
|
+
ngOnDestroy() {
|
|
233
|
+
window.removeEventListener("message", this.messageHandler);
|
|
234
|
+
}
|
|
235
|
+
/** Builds a query string from a params object, omitting undefined values */
|
|
236
|
+
buildQuery(params) {
|
|
237
|
+
const p = new URLSearchParams();
|
|
238
|
+
for (const [k, v] of Object.entries(params)) if (v !== void 0) p.set(k, v);
|
|
239
|
+
const qs = p.toString();
|
|
240
|
+
return qs ? "?" + qs : "";
|
|
241
|
+
}
|
|
214
242
|
};
|
|
215
243
|
__decorateClass([
|
|
216
244
|
(0, import_core4.Input)()
|
|
217
|
-
],
|
|
245
|
+
], EmbedBoardBase.prototype, "serverUrl", 2);
|
|
246
|
+
__decorateClass([
|
|
247
|
+
(0, import_core4.Output)()
|
|
248
|
+
], EmbedBoardBase.prototype, "onLeave", 2);
|
|
218
249
|
__decorateClass([
|
|
219
250
|
(0, import_core4.ViewChild)("iframe")
|
|
220
|
-
],
|
|
251
|
+
], EmbedBoardBase.prototype, "iframeRef", 2);
|
|
252
|
+
EmbedBoardBase = __decorateClass([
|
|
253
|
+
(0, import_core4.Directive)()
|
|
254
|
+
], EmbedBoardBase);
|
|
255
|
+
|
|
256
|
+
// src/components/chess/ChessBoardComponent.ts
|
|
257
|
+
var ChessBoardComponent = class extends EmbedBoardBase {
|
|
258
|
+
constructor() {
|
|
259
|
+
super(...arguments);
|
|
260
|
+
this.showAfkWarning = true;
|
|
261
|
+
this.showCoords = true;
|
|
262
|
+
this.showLastMove = true;
|
|
263
|
+
this.showLegalMoves = true;
|
|
264
|
+
this.orientation = "auto";
|
|
265
|
+
}
|
|
266
|
+
get embedUrl() {
|
|
267
|
+
return this.serverUrl + "/embed/chess" + this.buildQuery({
|
|
268
|
+
afkWarning: !this.showAfkWarning ? "0" : void 0,
|
|
269
|
+
showCoords: !this.showCoords ? "0" : void 0,
|
|
270
|
+
showLastMove: !this.showLastMove ? "0" : void 0,
|
|
271
|
+
showLegalMoves: !this.showLegalMoves ? "0" : void 0,
|
|
272
|
+
orientation: this.orientation !== "auto" ? this.orientation : void 0
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
__decorateClass([
|
|
277
|
+
(0, import_core5.Input)()
|
|
278
|
+
], ChessBoardComponent.prototype, "showAfkWarning", 2);
|
|
279
|
+
__decorateClass([
|
|
280
|
+
(0, import_core5.Input)()
|
|
281
|
+
], ChessBoardComponent.prototype, "showCoords", 2);
|
|
282
|
+
__decorateClass([
|
|
283
|
+
(0, import_core5.Input)()
|
|
284
|
+
], ChessBoardComponent.prototype, "showLastMove", 2);
|
|
285
|
+
__decorateClass([
|
|
286
|
+
(0, import_core5.Input)()
|
|
287
|
+
], ChessBoardComponent.prototype, "showLegalMoves", 2);
|
|
288
|
+
__decorateClass([
|
|
289
|
+
(0, import_core5.Input)()
|
|
290
|
+
], ChessBoardComponent.prototype, "orientation", 2);
|
|
221
291
|
ChessBoardComponent = __decorateClass([
|
|
222
|
-
(0,
|
|
292
|
+
(0, import_core5.Component)({
|
|
223
293
|
selector: "bg-chess-board",
|
|
224
294
|
standalone: true,
|
|
225
295
|
template: `
|
|
@@ -229,19 +299,25 @@ ChessBoardComponent = __decorateClass([
|
|
|
229
299
|
`
|
|
230
300
|
})
|
|
231
301
|
], ChessBoardComponent);
|
|
302
|
+
|
|
303
|
+
// src/components/chess/ChessMoveHistoryComponent.ts
|
|
304
|
+
var import_core6 = require("@angular/core");
|
|
232
305
|
var ChessMoveHistoryComponent = class {
|
|
233
306
|
};
|
|
234
307
|
ChessMoveHistoryComponent = __decorateClass([
|
|
235
|
-
(0,
|
|
308
|
+
(0, import_core6.Component)({
|
|
236
309
|
selector: "bg-chess-move-history",
|
|
237
310
|
standalone: true,
|
|
238
311
|
template: `<div data-role="move-history"></div>`
|
|
239
312
|
})
|
|
240
313
|
], ChessMoveHistoryComponent);
|
|
314
|
+
|
|
315
|
+
// src/components/chess/ChessCapturedPiecesComponent.ts
|
|
316
|
+
var import_core7 = require("@angular/core");
|
|
241
317
|
var ChessCapturedPiecesComponent = class {
|
|
242
318
|
};
|
|
243
319
|
ChessCapturedPiecesComponent = __decorateClass([
|
|
244
|
-
(0,
|
|
320
|
+
(0, import_core7.Component)({
|
|
245
321
|
selector: "bg-chess-captured-pieces",
|
|
246
322
|
standalone: true,
|
|
247
323
|
template: `<div data-role="captured-pieces"></div>`
|
|
@@ -249,31 +325,33 @@ ChessCapturedPiecesComponent = __decorateClass([
|
|
|
249
325
|
], ChessCapturedPiecesComponent);
|
|
250
326
|
|
|
251
327
|
// src/components/checkers/index.ts
|
|
252
|
-
var
|
|
253
|
-
var CheckersBoardComponent = class {
|
|
328
|
+
var import_core8 = require("@angular/core");
|
|
329
|
+
var CheckersBoardComponent = class extends EmbedBoardBase {
|
|
254
330
|
constructor() {
|
|
255
|
-
|
|
256
|
-
this.
|
|
331
|
+
super(...arguments);
|
|
332
|
+
this.showAfkWarning = true;
|
|
333
|
+
this.showLastMove = true;
|
|
334
|
+
this.orientation = "auto";
|
|
257
335
|
}
|
|
258
336
|
get embedUrl() {
|
|
259
|
-
return
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
if (!iframe) return;
|
|
264
|
-
iframe.addEventListener("load", () => {
|
|
265
|
-
iframe.contentWindow?.postMessage({ type: "bg:init", token: this.svc.token }, "*");
|
|
337
|
+
return this.serverUrl + "/embed/checkers" + this.buildQuery({
|
|
338
|
+
afkWarning: !this.showAfkWarning ? "0" : void 0,
|
|
339
|
+
showLastMove: !this.showLastMove ? "0" : void 0,
|
|
340
|
+
orientation: this.orientation !== "auto" ? this.orientation : void 0
|
|
266
341
|
});
|
|
267
342
|
}
|
|
268
343
|
};
|
|
269
344
|
__decorateClass([
|
|
270
|
-
(0,
|
|
271
|
-
], CheckersBoardComponent.prototype, "
|
|
345
|
+
(0, import_core8.Input)()
|
|
346
|
+
], CheckersBoardComponent.prototype, "showAfkWarning", 2);
|
|
347
|
+
__decorateClass([
|
|
348
|
+
(0, import_core8.Input)()
|
|
349
|
+
], CheckersBoardComponent.prototype, "showLastMove", 2);
|
|
272
350
|
__decorateClass([
|
|
273
|
-
(0,
|
|
274
|
-
], CheckersBoardComponent.prototype, "
|
|
351
|
+
(0, import_core8.Input)()
|
|
352
|
+
], CheckersBoardComponent.prototype, "orientation", 2);
|
|
275
353
|
CheckersBoardComponent = __decorateClass([
|
|
276
|
-
(0,
|
|
354
|
+
(0, import_core8.Component)({
|
|
277
355
|
selector: "bg-checkers-board",
|
|
278
356
|
standalone: true,
|
|
279
357
|
template: `
|
|
@@ -284,32 +362,24 @@ CheckersBoardComponent = __decorateClass([
|
|
|
284
362
|
})
|
|
285
363
|
], CheckersBoardComponent);
|
|
286
364
|
|
|
287
|
-
// src/components/connect4/
|
|
288
|
-
var
|
|
289
|
-
var Connect4BoardComponent = class {
|
|
365
|
+
// src/components/connect4/Connect4BoardComponent.ts
|
|
366
|
+
var import_core9 = require("@angular/core");
|
|
367
|
+
var Connect4BoardComponent = class extends EmbedBoardBase {
|
|
290
368
|
constructor() {
|
|
291
|
-
|
|
292
|
-
this.
|
|
369
|
+
super(...arguments);
|
|
370
|
+
this.showAfkWarning = true;
|
|
293
371
|
}
|
|
294
372
|
get embedUrl() {
|
|
295
|
-
return
|
|
296
|
-
|
|
297
|
-
ngAfterViewInit() {
|
|
298
|
-
const iframe = this.iframeRef?.nativeElement;
|
|
299
|
-
if (!iframe) return;
|
|
300
|
-
iframe.addEventListener("load", () => {
|
|
301
|
-
iframe.contentWindow?.postMessage({ type: "bg:init", token: this.svc.token }, "*");
|
|
373
|
+
return this.serverUrl + "/embed/connect4" + this.buildQuery({
|
|
374
|
+
afkWarning: !this.showAfkWarning ? "0" : void 0
|
|
302
375
|
});
|
|
303
376
|
}
|
|
304
377
|
};
|
|
305
378
|
__decorateClass([
|
|
306
|
-
(0,
|
|
307
|
-
], Connect4BoardComponent.prototype, "
|
|
308
|
-
__decorateClass([
|
|
309
|
-
(0, import_core6.ViewChild)("iframe")
|
|
310
|
-
], Connect4BoardComponent.prototype, "iframeRef", 2);
|
|
379
|
+
(0, import_core9.Input)()
|
|
380
|
+
], Connect4BoardComponent.prototype, "showAfkWarning", 2);
|
|
311
381
|
Connect4BoardComponent = __decorateClass([
|
|
312
|
-
(0,
|
|
382
|
+
(0, import_core9.Component)({
|
|
313
383
|
selector: "bg-connect4-board",
|
|
314
384
|
standalone: true,
|
|
315
385
|
template: `
|
|
@@ -319,9 +389,12 @@ Connect4BoardComponent = __decorateClass([
|
|
|
319
389
|
`
|
|
320
390
|
})
|
|
321
391
|
], Connect4BoardComponent);
|
|
392
|
+
|
|
393
|
+
// src/components/connect4/Connect4ScoreComponent.ts
|
|
394
|
+
var import_core10 = require("@angular/core");
|
|
322
395
|
var Connect4ScoreComponent = class {
|
|
323
396
|
constructor() {
|
|
324
|
-
this.svc = (0,
|
|
397
|
+
this.svc = (0, import_core10.inject)(BetaGamerService);
|
|
325
398
|
}
|
|
326
399
|
get scores() {
|
|
327
400
|
const s = this.svc.gameState()?.scores ?? {};
|
|
@@ -329,7 +402,7 @@ var Connect4ScoreComponent = class {
|
|
|
329
402
|
}
|
|
330
403
|
};
|
|
331
404
|
Connect4ScoreComponent = __decorateClass([
|
|
332
|
-
(0,
|
|
405
|
+
(0, import_core10.Component)({
|
|
333
406
|
selector: "bg-connect4-score",
|
|
334
407
|
standalone: true,
|
|
335
408
|
template: `
|
|
@@ -343,31 +416,23 @@ Connect4ScoreComponent = __decorateClass([
|
|
|
343
416
|
], Connect4ScoreComponent);
|
|
344
417
|
|
|
345
418
|
// src/components/tictactoe/index.ts
|
|
346
|
-
var
|
|
347
|
-
var TictactoeBoardComponent = class {
|
|
419
|
+
var import_core11 = require("@angular/core");
|
|
420
|
+
var TictactoeBoardComponent = class extends EmbedBoardBase {
|
|
348
421
|
constructor() {
|
|
349
|
-
|
|
350
|
-
this.
|
|
422
|
+
super(...arguments);
|
|
423
|
+
this.showAfkWarning = true;
|
|
351
424
|
}
|
|
352
425
|
get embedUrl() {
|
|
353
|
-
return
|
|
354
|
-
|
|
355
|
-
ngAfterViewInit() {
|
|
356
|
-
const iframe = this.iframeRef?.nativeElement;
|
|
357
|
-
if (!iframe) return;
|
|
358
|
-
iframe.addEventListener("load", () => {
|
|
359
|
-
iframe.contentWindow?.postMessage({ type: "bg:init", token: this.svc.token }, "*");
|
|
426
|
+
return this.serverUrl + "/embed/tictactoe" + this.buildQuery({
|
|
427
|
+
afkWarning: !this.showAfkWarning ? "0" : void 0
|
|
360
428
|
});
|
|
361
429
|
}
|
|
362
430
|
};
|
|
363
431
|
__decorateClass([
|
|
364
|
-
(0,
|
|
365
|
-
], TictactoeBoardComponent.prototype, "
|
|
366
|
-
__decorateClass([
|
|
367
|
-
(0, import_core7.ViewChild)("iframe")
|
|
368
|
-
], TictactoeBoardComponent.prototype, "iframeRef", 2);
|
|
432
|
+
(0, import_core11.Input)()
|
|
433
|
+
], TictactoeBoardComponent.prototype, "showAfkWarning", 2);
|
|
369
434
|
TictactoeBoardComponent = __decorateClass([
|
|
370
|
-
(0,
|
|
435
|
+
(0, import_core11.Component)({
|
|
371
436
|
selector: "bg-tictactoe-board",
|
|
372
437
|
standalone: true,
|
|
373
438
|
template: `
|
|
@@ -378,12 +443,12 @@ TictactoeBoardComponent = __decorateClass([
|
|
|
378
443
|
})
|
|
379
444
|
], TictactoeBoardComponent);
|
|
380
445
|
|
|
381
|
-
// src/components/subway-runner/
|
|
382
|
-
var
|
|
446
|
+
// src/components/subway-runner/SubwayRunnerGameComponent.ts
|
|
447
|
+
var import_core12 = require("@angular/core");
|
|
383
448
|
var SubwayRunnerGameComponent = class {
|
|
384
449
|
constructor() {
|
|
385
450
|
this.serverUrl = "https://api.beta-gamer.com";
|
|
386
|
-
this.svc = (0,
|
|
451
|
+
this.svc = (0, import_core12.inject)(BetaGamerService);
|
|
387
452
|
}
|
|
388
453
|
get embedUrl() {
|
|
389
454
|
return `${this.serverUrl}/embed/subway-runner`;
|
|
@@ -397,13 +462,13 @@ var SubwayRunnerGameComponent = class {
|
|
|
397
462
|
}
|
|
398
463
|
};
|
|
399
464
|
__decorateClass([
|
|
400
|
-
(0,
|
|
465
|
+
(0, import_core12.Input)()
|
|
401
466
|
], SubwayRunnerGameComponent.prototype, "serverUrl", 2);
|
|
402
467
|
__decorateClass([
|
|
403
|
-
(0,
|
|
468
|
+
(0, import_core12.ViewChild)("iframe")
|
|
404
469
|
], SubwayRunnerGameComponent.prototype, "iframeRef", 2);
|
|
405
470
|
SubwayRunnerGameComponent = __decorateClass([
|
|
406
|
-
(0,
|
|
471
|
+
(0, import_core12.Component)({
|
|
407
472
|
selector: "bg-subway-runner",
|
|
408
473
|
standalone: true,
|
|
409
474
|
template: `
|
|
@@ -413,31 +478,37 @@ SubwayRunnerGameComponent = __decorateClass([
|
|
|
413
478
|
`
|
|
414
479
|
})
|
|
415
480
|
], SubwayRunnerGameComponent);
|
|
481
|
+
|
|
482
|
+
// src/components/subway-runner/SubwayRunnerScoreComponent.ts
|
|
483
|
+
var import_core13 = require("@angular/core");
|
|
416
484
|
var SubwayRunnerScoreComponent = class {
|
|
417
485
|
constructor() {
|
|
418
|
-
this.svc = (0,
|
|
486
|
+
this.svc = (0, import_core13.inject)(BetaGamerService);
|
|
419
487
|
}
|
|
420
488
|
get score() {
|
|
421
489
|
return this.svc.gameState()?.score ?? 0;
|
|
422
490
|
}
|
|
423
491
|
};
|
|
424
492
|
SubwayRunnerScoreComponent = __decorateClass([
|
|
425
|
-
(0,
|
|
493
|
+
(0, import_core13.Component)({
|
|
426
494
|
selector: "bg-subway-runner-score",
|
|
427
495
|
standalone: true,
|
|
428
496
|
template: `<div data-role="score"><span data-role="score-value">{{ score }}</span></div>`
|
|
429
497
|
})
|
|
430
498
|
], SubwayRunnerScoreComponent);
|
|
499
|
+
|
|
500
|
+
// src/components/subway-runner/SubwayRunnerLivesComponent.ts
|
|
501
|
+
var import_core14 = require("@angular/core");
|
|
431
502
|
var SubwayRunnerLivesComponent = class {
|
|
432
503
|
constructor() {
|
|
433
|
-
this.svc = (0,
|
|
504
|
+
this.svc = (0, import_core14.inject)(BetaGamerService);
|
|
434
505
|
}
|
|
435
506
|
get lives() {
|
|
436
507
|
return this.svc.gameState()?.lives ?? 0;
|
|
437
508
|
}
|
|
438
509
|
};
|
|
439
510
|
SubwayRunnerLivesComponent = __decorateClass([
|
|
440
|
-
(0,
|
|
511
|
+
(0, import_core14.Component)({
|
|
441
512
|
selector: "bg-subway-runner-lives",
|
|
442
513
|
standalone: true,
|
|
443
514
|
template: `<div data-role="lives"><span data-role="lives-value">{{ lives }}</span></div>`
|