domma-cms 0.105.1 → 0.106.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.
@@ -20,6 +20,15 @@ service - no import of the Calendar, which is a separate paid Tool).
20
20
  Booking Pro (not yet built) is an ADD-ON that registers into this one (`lib/extensions.js`), the Cart / Cart Pro
21
21
  pattern - it does not supersede it. Payment is Pro only.
22
22
 
23
+ 1.5.0 (2026-09-30): the call's chat is saved per SESSION (`sessionOf` - a class's shared call has one chat) in
24
+ `data/call-chat.json` (`options.dataDir` in tests) through core's `_lib/call/chatStore.js` (dynamic import beside
25
+ `mountCallRoutes`; an older core has none and nothing changes), passed as the engine's `chat` - so the call reopened
26
+ shows it. `GET /bookings/:id` adds `callChat` (typed messages) on online bookings; `GET /bookings/:id/call-chat`
27
+ (booking.read; 404 unless online) answers `{messages, items}` without participant ids, the session's host lines
28
+ (`whoOf('<bookingId>:host')` for each booking of the session) marked `mine`. The panel (`openBooking`) shows "Call chat
29
+ (N messages)" when N > 0 and opens `/plugins/_lib/admin/ui/transcript.js` (read-only, domma-reactive). The customer
30
+ has no admin view of it. Kept: 500 items a session, the 1000 most recent sessions. Tests: tests/api.test.js.
31
+
23
32
  1.4.0 (2026-09-30): "Call now" hooks for Booking Pro. Registry `version: 4`: `actions: [{id, icon, title, module}]`
24
33
  (module under `/plugins/<owner>/`; ids `[a-z0-9-]`, never a screen's or tab's key, first owner keeps an id) ->
25
34
  `registry.actions()` -> bootstrap `actions` -> a banner button each (`booking-x-<id>`) that imports the module and calls
@@ -61,6 +61,7 @@ export const api = {
61
61
  bookings: {
62
62
  list: (q = {}) => request('GET', `${P}/bookings?${qs(q)}`),
63
63
  get: (id) => request('GET', `${P}/bookings/${seg(id)}`),
64
+ callChat: (id) => request('GET', `${P}/bookings/${seg(id)}/call-chat`),
64
65
  create: (d) => request('POST', `${P}/bookings`, d),
65
66
  update: (id, d) => request('PUT', `${P}/bookings/${seg(id)}`, d),
66
67
  move: (id, d) => request('PUT', `${P}/bookings/${seg(id)}/move`, d),
@@ -478,6 +478,9 @@ export async function openBooking(ctx, id) {
478
478
  ${b.hostUrl && live ? `<div class="bk-actions bk-actions-start bk-call-row">
479
479
  <a class="btn btn-sm btn-primary" href="${esc(b.hostUrl)}" target="_blank" rel="noopener"><span data-icon="video" data-icon-size="13"></span> Start the call</a>
480
480
  <span class="bk-muted">You are the host: let ${esc(b.name || 'them')} in when they arrive. ${Date.parse(b.callOpenedAt || '') > Date.now() - 2 * 3600000 ? 'It was opened early, so it is open now.' : 'It opens 10 minutes before the start.'}</span></div>` : ''}
481
+ ${b.online && Number(b.callChat) > 0 ? `<div class="bk-actions bk-actions-start bk-call-chat">
482
+ <button type="button" class="btn btn-sm btn-outline" data-on-click="callChat"><span data-icon="message-circle" data-icon-size="13"></span> Call chat (${Number(b.callChat)} message${Number(b.callChat) === 1 ? '' : 's'})</button>
483
+ <span class="bk-muted">What was typed in the call's chat, read-only.</span></div>` : ''}
481
484
  <div class="bk-actions bk-actions-start">
482
485
  <button type="button" class="btn btn-sm btn-outline" data-on-click="move" ${live ? '' : 'disabled'}><span data-icon="move" data-icon-size="13"></span> Move</button>
483
486
  <button type="button" class="btn btn-sm btn-outline" data-on-click="done" ${live && b.status !== 'completed' ? '' : 'disabled'}><span data-icon="check" data-icon-size="13"></span> Done</button>
@@ -505,6 +508,13 @@ export async function openBooking(ctx, id) {
505
508
  E.toast(`Not saved: ${errText(err)}`, {type: 'error'});
506
509
  }
507
510
  },
511
+ async callChat() {
512
+ // The call's chat, read-only (1.5) - one per session, so a class's shared call has one.
513
+ try {
514
+ const [r, ui] = await Promise.all([api.bookings.callChat(b.id), import('/plugins/_lib/admin/ui/transcript.js')]);
515
+ await ui.openTranscript({title: `Call chat: ${b.serviceName}`, subtitle: timeRange(b, ctx.tz), items: r?.items || []});
516
+ } catch (err) { E.toast(`The chat could not be opened: ${errText(err)}`, {type: 'error'}); }
517
+ },
508
518
  move: act(() => moveBooking(ctx, b)),
509
519
  done: act(() => setStatus(ctx, b, 'completed')),
510
520
  noshow: act(() => setStatus(ctx, b, 'no-show')),
@@ -57,7 +57,7 @@ Booking has its own permission, **Booking**, which admins have by default. **See
57
57
  ## Booking Pro
58
58
 
59
59
  Booking Pro adds the following to the free Tool:
60
- - Online appointments, held as a video call on your site.
60
+ - Online appointments, held as a video call on your site. What is typed in the call's chat is kept: open the booking and click **Call chat** to read it afterwards (a class shares one chat).
61
61
  - **Call now**: open an online booking's call early, or start a video call with anyone straight away - from Booking, a contact or an email.
62
62
  - Services that need more than one person or room at once.
63
63
  - Classes and group sessions with places and a waiting list.
@@ -6,6 +6,8 @@
6
6
  * GET /count sidebar badge: bookings still to come today
7
7
  * GET /bookings?from=&to= bookings starting in a range (ISO instants)
8
8
  * GET /bookings/:id · POST /bookings · PUT /bookings/:id · PUT /bookings/:id/move · DELETE /bookings/:id
9
+ * GET /bookings/:id/call-chat an online booking's call chat, read-only (booking.read; 1.5) - saved per
10
+ * session (a class's shared call has one) in data/call-chat.json
9
11
  * GET /slots?service=&from=&to=&with=&ignore= free times (dates in the business zone)
10
12
  * GET/POST /services · PUT/DELETE /services/:id
11
13
  * GET/POST /bookables · PUT/DELETE /bookables/:id
@@ -24,6 +26,8 @@
24
26
  * The booking page itself is plugin.public.js.
25
27
  */
26
28
 
29
+ import path from 'node:path';
30
+ import {fileURLToPath} from 'node:url';
27
31
  import {getConfig} from '../../server/config.js';
28
32
  import {getPluginStates} from '../../server/services/plugins.js';
29
33
  import {getPermissionsFor} from '../../server/services/roles.js';
@@ -119,7 +123,9 @@ function registerShortcodes(hooks) {
119
123
  });
120
124
  }
121
125
 
122
- export const ASSET_V = '1.4.0';
126
+ export const ASSET_V = '1.5.0';
127
+
128
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
123
129
 
124
130
  export default async function booking(fastify, options) {
125
131
  const {authenticate, requirePermission} = options.auth;
@@ -135,6 +141,10 @@ export default async function booking(fastify, options) {
135
141
  // The online call's rooms (set once the engine is mounted, at the end). A booking cancelled or moved away
136
142
  // ends the call it was in, unless someone else is still booked into that session (a class).
137
143
  let calls = null;
144
+ // The call's chat (1.5), saved per session - set with the engine, at the end; null on an older core.
145
+ let chatStore = null;
146
+ let whoOf = null;
147
+ const dataDir = options.dataDir || path.join(HERE, 'data');
138
148
  async function endCall(old) {
139
149
  if (!calls || old?.online !== true) return;
140
150
  const room = sessionOf(old);
@@ -432,7 +442,24 @@ export default async function booking(fastify, options) {
432
442
 
433
443
  fastify.get('/bookings/:id', can('read'), async (request, reply) => {
434
444
  const b = await getBooking(request.params.id);
435
- return b ? adminBooking(b) : fail(reply, 404, 'No such booking.');
445
+ if (!b) return fail(reply, 404, 'No such booking.');
446
+ // An online booking says how many messages its call's chat holds (the panel offers "Call chat").
447
+ const callChat = b.online === true && chatStore ? (await chatStore.load(sessionOf(b))).filter(i => i.kind === 'chat').length : undefined;
448
+ return reply.send({...adminBooking(b), ...(callChat !== undefined ? {callChat} : {})});
449
+ });
450
+
451
+ // The call's chat, read-only (1.5): the session's transcript - a class's shared call has one. Staff with
452
+ // booking read only; the customer saw it in the call. Staff (host) lines are marked `mine`.
453
+ fastify.get('/bookings/:id/call-chat', can('read'), async (request, reply) => {
454
+ const b = await getBooking(request.params.id);
455
+ if (!b || b.online !== true) return fail(reply, 404, 'No such online booking.');
456
+ const room = sessionOf(b);
457
+ const items = chatStore ? await chatStore.load(room) : [];
458
+ const hosts = new Set(whoOf ? (await allBookings()).filter(x => x.online === true && sessionOf(x) === room)
459
+ .map(x => whoOf(`${x.id}:host`, '')) : []);
460
+ reply.header('Cache-Control', 'no-store');
461
+ return reply.send({messages: items.filter(i => i.kind === 'chat').length,
462
+ items: items.map(({from, ...i}) => ({...i, mine: i.kind === 'chat' && hosts.has(i.who)}))});
436
463
  });
437
464
 
438
465
  /** Service + the bookables that may take a booking of it (`with` narrows to one). */
@@ -536,6 +563,11 @@ export default async function booking(fastify, options) {
536
563
  const b = await getBooking(request.params.id);
537
564
  if (!b) return fail(reply, 404, 'No such booking.');
538
565
  await removeBooking(b.id);
566
+ // Its call's chat goes with it - unless someone else is still booked into that session (a class).
567
+ if (b.online === true && chatStore) {
568
+ const room = sessionOf(b);
569
+ if (!(await allBookings()).some(x => x.online === true && sessionOf(x) === room)) await chatStore.remove(room)?.catch?.(() => {});
570
+ }
539
571
  return {ok: true};
540
572
  });
541
573
 
@@ -788,7 +820,13 @@ export default async function booking(fastify, options) {
788
820
  // The engine is core's (_lib/call); an older core without it simply has no calls.
789
821
  try {
790
822
  const {mountCallRoutes} = await import('../_lib/call/routes.js');
791
- calls = mountCallRoutes(fastify, {prefix: '/call', ns: 'booking', resolve: async (request, room, body) => {
823
+ // The chat is saved per session (1.5; a core with the chat store) so a call reopened later shows it.
824
+ try {
825
+ const {createChatStore} = await import('../_lib/call/chatStore.js');
826
+ chatStore = createChatStore(path.join(dataDir, 'call-chat.json'));
827
+ whoOf = (await import('../_lib/call/rooms.js')).whoOf || null;
828
+ } catch { chatStore = null; }
829
+ calls = mountCallRoutes(fastify, {prefix: '/call', ns: 'booking', ...(chatStore ? {chat: chatStore} : {}), resolve: async (request, room, body) => {
792
830
  const b = await getBooking(String(body?.b || '')).catch(() => null);
793
831
  // A class opened early ("Call now") is open for everyone in the session - someone who booked in
794
832
  // after the click too: take the session's latest opening when this booking has none of its own.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "booking",
3
3
  "displayName": "Booking",
4
- "version": "1.4.1",
4
+ "version": "1.5.0",
5
5
  "tier": "free",
6
6
  "description": "Let people book appointments with you or your team, and rooms or equipment, from a booking page on your site. Opening hours, days off and buffers; confirmation and reminder emails with a link to change or cancel; a diary in the admin. Book straight from a contact or an email.",
7
7
  "author": "Domma CMS",
@@ -6,6 +6,7 @@
6
6
  import {after, before, test} from 'node:test';
7
7
  import assert from 'node:assert/strict';
8
8
  import fs from 'node:fs/promises';
9
+ import os from 'node:os';
9
10
  import path from 'node:path';
10
11
  import Fastify from 'fastify';
11
12
  import booking from '../plugin.js';
@@ -20,6 +21,7 @@ const DIRS = ['booking-bookables', 'booking-services', 'booking-bookings'].map(c
20
21
  const TZ = 'Europe/London';
21
22
  let savedPlugins;
22
23
  let app;
24
+ let dataDir;
23
25
  const sent = [];
24
26
  const notices = [];
25
27
  const related = {};
@@ -30,11 +32,14 @@ before(async () => {
30
32
  for (const d of DIRS) await fs.rm(d, {recursive: true, force: true});
31
33
  setMailer(async (msg) => { sent.push(msg); });
32
34
  app = Fastify();
35
+ dataDir = await fs.mkdtemp(path.join(os.tmpdir(), 'booking-data-'));
33
36
  const authenticate = async (req) => { req.user = {id: 'u1', name: 'Sam', role: 'admin'}; };
34
37
  await app.register(booking, {
35
38
  prefix: '',
36
- auth: {authenticate, requirePermission: () => async () => {}},
39
+ // `x-deny` stands for a role without the permission.
40
+ auth: {authenticate, requirePermission: () => async (req, reply) => { if (req.headers['x-deny']) return reply.code(403).send({error: 'Forbidden'}); }},
37
41
  settings: {},
42
+ dataDir,
38
43
  hooks: {
39
44
  notify: (n) => notices.push(n), registerShortcode() {},
40
45
  registerRelatedItems: (kind, p) => { related[kind] = p; return true; }
@@ -49,6 +54,7 @@ after(async () => {
49
54
  if (savedPlugins === null) await fs.rm(PLUGINS, {force: true});
50
55
  else await fs.writeFile(PLUGINS, savedPlugins);
51
56
  if (savedContacts !== null) await fs.writeFile(CONTACTS, savedContacts);
57
+ if (dataDir) await fs.rm(dataDir, {recursive: true, force: true});
52
58
  });
53
59
 
54
60
  const call = async (method, url, body) => {
@@ -355,6 +361,63 @@ test('online: a service an extension makes online gives a call - the customer an
355
361
  }
356
362
  });
357
363
 
364
+ test('online: the call\'s chat is saved per session - counted on the booking, read by staff, seeds the reopened call', async () => {
365
+ const reg = bookingRegistry();
366
+ reg.register('test-online', {rules: (s) => (s?.slug === 'chat-call' ? {online: true} : {})});
367
+ try {
368
+ const all = Object.fromEntries(['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'].map(d => [d, [{from: '00:00', to: '24:00'}]]));
369
+ let r = await call('POST', '/bookables', {name: 'Cara', kind: 'person', hours: all});
370
+ const cara = r.body;
371
+ r = await call('POST', '/services', {name: 'Chat call', duration: 30, minNoticeMinutes: 0, bookables: [cara.id]});
372
+ const svc = r.body;
373
+ const start = new Date(Math.ceil((Date.now() + 5 * 60000) / 60000) * 60000).toISOString();
374
+ r = await call('POST', '/bookings', {serviceId: svc.id, startAt: start, customer: {name: 'Gus Guest', email: 'gus@example.com'}, force: true});
375
+ assert.equal(r.status, 201, JSON.stringify(r.body));
376
+ const b = r.body;
377
+ const raw = JSON.parse(await fs.readFile(path.resolve('content/collections/booking-bookings/data.json'), 'utf8'))
378
+ .map(e => e.data ? {id: e.id, ...e.data} : e).find(x => x.id === b.id);
379
+ const room = b.hostUrl.split('/call/')[1].split('?')[0];
380
+ const hostKey = new URL(`http://x${b.hostUrl}`).searchParams.get('h');
381
+ const as = (p) => ({'x-call-id': p.id, 'x-call-secret': p.secret});
382
+ const post = (u, payload, headers = {}) => app.inject({method: 'POST', url: u, payload, headers});
383
+
384
+ assert.equal((await call('GET', `/bookings/${b.id}`)).body.callChat, 0, 'no chat yet');
385
+ const host = (await post(`/call/${room}/join`, {b: b.id, h: hostKey})).json();
386
+ const guest = (await post(`/call/${room}/join`, {b: b.id, t: raw.token})).json();
387
+ assert.equal(guest.state, 'waiting');
388
+ await post(`/call/${room}/answer`, {who: guest.id, yes: true}, as(host));
389
+ assert.equal((await post(`/call/${room}/chat`, {text: 'Hello Gus'}, as(host))).statusCode, 200);
390
+ assert.equal((await post(`/call/${room}/chat`, {text: 'Hi <Cara>'}, as(guest))).statusCode, 200);
391
+ await post(`/call/${room}/leave`, {}, as(guest));
392
+ await post(`/call/${room}/leave`, {}, as(host));
393
+ await new Promise(res => setTimeout(res, 30));
394
+
395
+ // Saved in the plugin's data folder, keyed by the session.
396
+ const file = JSON.parse(await fs.readFile(path.join(dataDir, 'call-chat.json'), 'utf8'));
397
+ assert.equal(file.rooms[room].items.filter(i => i.kind === 'chat').length, 2);
398
+ assert.equal((await call('GET', `/bookings/${b.id}`)).body.callChat, 2);
399
+ r = await call('GET', `/bookings/${b.id}/call-chat`);
400
+ assert.equal(r.status, 200);
401
+ assert.equal(r.body.messages, 2);
402
+ assert.deepEqual(r.body.items.filter(i => i.kind === 'chat').map(i => [i.name, i.text, i.mine]), [['Cara', 'Hello Gus', true], ['Gus Guest', 'Hi <Cara>', false]]);
403
+ assert.ok(r.body.items.every(i => !('from' in i)));
404
+ assert.ok(r.body.items.some(i => i.kind === 'system' && i.text === 'Gus Guest left'));
405
+ // Staff without booking read: refused. Not online / missing: 404.
406
+ assert.equal((await app.inject({method: 'GET', url: `/bookings/${b.id}/call-chat`, headers: {'x-deny': '1'}})).statusCode, 403);
407
+ assert.equal((await call('GET', '/bookings/nope/call-chat')).status, 404);
408
+
409
+ // Everyone left, the room is gone: rejoining shows the earlier chat.
410
+ const again = (await post(`/call/${room}/join`, {b: b.id, h: hostKey})).json();
411
+ const polled = (await app.inject({method: 'GET', url: `/call/${room}/poll?since=0`, headers: as(again)})).json();
412
+ const admitted = polled.events.find(e => e.type === 'admitted');
413
+ assert.ok(admitted.chat.some(i => i.text === 'Hello Gus') && admitted.chat.some(i => i.text === 'Hi <Cara>'));
414
+ await post(`/call/${room}/leave`, {}, as(again));
415
+ await call('DELETE', `/bookings/${b.id}`);
416
+ } finally {
417
+ reg.unregister('test-online');
418
+ }
419
+ });
420
+
358
421
  test('extensions: banner actions reach the bootstrap, only under their owner, one per id', async () => {
359
422
  const reg = bookingRegistry();
360
423
  assert.ok(reg.version >= 4);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "note": "Written by `make sync-free-tier` in dcms-marketplace. Do not edit these plugins here - edit them there and sync.",
3
- "source": "dcms-marketplace@25153e0",
3
+ "source": "dcms-marketplace@ba58fcb",
4
4
  "plugins": {
5
5
  "blog": {
6
6
  "version": "1.9.4",
@@ -73,19 +73,19 @@
73
73
  }
74
74
  },
75
75
  "booking": {
76
- "version": "1.4.1",
76
+ "version": "1.5.0",
77
77
  "files": {
78
- "CLAUDE.md": "c4401810011ed98fe488e89e03d32ee601025818d1f354ec07fcd9f9add1115a",
78
+ "CLAUDE.md": "258f342c1acdd1d7562a3409bc7c8622828308c243549255d2ac75832be6d84e",
79
79
  "admin/css/index.css": "4f7731921c0c5038cb1566a6ab2d43ffc35bf9b161e58f87a0cff423e81b47fa",
80
80
  "admin/lib/details.js": "8b37536bfa9ccfcc5265cc957ba8266d3f5f777133e7c0946ae49540f1c80e3c",
81
81
  "admin/lib/diary-layout.js": "d818c79001fb9a9ecc15c96239ba961b0a1d98b9e8fce903eefb57d85adf2a73",
82
82
  "admin/lib/extension-sections.js": "0588ad47660e511b0dd8077bb9c3999c775d97597333a75e87ee12d915bbf00b",
83
- "admin/lib/kit.js": "661e9c407bf398694dfa08105590065a6352594d526f3c09c31b180c840045bf",
83
+ "admin/lib/kit.js": "b69b02172d76ef80879da3f003cc1e687c6149626c9e58af51729c2e4a3ff959",
84
84
  "admin/templates/booking.html": "20b9a3bfe3e5d5bbee613f29be99551b12790fb38f80d119585360cc38e9ec57",
85
85
  "admin/views/booking.js": "4eddf6c0b60dbbba4c6e35352df247a3d34b66ee1d43c51fe2d53f894707348f",
86
86
  "admin/views/bookings.js": "bf17db82ed4d2f5c29ac924dd60dc0b84c8bd9c8ce32da6cbb011527699a56a6",
87
87
  "admin/views/diary.js": "9e8976e67c2232d353b3d9a1ef0d0503cc4d06b710d54d0cba84fb5b796fdfe9",
88
- "admin/views/panels.js": "9d1c45e56d2a7a8caa12e9af29545c808ffb88da3ea48397d55aae19e802ec69",
88
+ "admin/views/panels.js": "1d269d90553a5482fd6ebd18180cb736011770f0204d5c1306948abcb4b676b2",
89
89
  "admin/views/people.js": "58cb9cc550b48c959a5572a4d25650ea2b32b658c9d0bf4c244e4abe0277c211",
90
90
  "admin/views/services.js": "122326d222ed16dc8ec903fef6f62f6a7638c31d73c4612b7a8c2ced38358068",
91
91
  "admin/views/settings.js": "299297b47a6509986276a859807a33ea225681d28e0e9cd06cdf36b0f6ec0c11",
@@ -93,14 +93,14 @@
93
93
  "collections/booking-bookings.json": "fa0b15cc81b4a2114734a0bc8adc4b4d25049d16cf62549b83634cdbeb543b52",
94
94
  "collections/booking-services.json": "cecb500a9215a0eba894e6b4c9ae78f5fbeb209830d07d47050c56c75143f66a",
95
95
  "config.js": "5565fd627481cf809322257cc300a7cbb7ccbf6cae076b4cf8a7453fcf554bd3",
96
- "docs/guide.md": "545b050500a09ffdf7bc620e01f0b63bf59e92dfe855ba3201419b0bcc92a1fc",
96
+ "docs/guide.md": "dd6a8250ac25914dc8ac6922167c92e815f69f464d61b424dfab42770af59626",
97
97
  "inject/body.html": "ab55ce945eec2302eb0c836a4626cffa5e091d050c6a59c6f2af332b05570777",
98
98
  "lib/call.js": "7dfa0ae60be87d8f7f26b4360d5c1144322abe66eb61b233ed812e35bb8e59bd",
99
99
  "lib/extensions.js": "8cec28ff8685daad8083b23f034f1c3bf82fd2322943dd19d46570ccdc311105",
100
100
  "lib/mail.js": "cad8f409df62d6e42f0f4024ec364bbe018a62b842fa3f7c38786fd4e7f92408",
101
101
  "lib/store.js": "270161f907c68de39d32758931e86c290582524af4650e722c2c3be209acc875",
102
- "plugin.js": "963e8190dbcaec71915d73402648899bb38135b25ec869a659d0938d03868682",
103
- "plugin.json": "704402863b0aec563046b347d4341680e2dc9739db967bec0a933c2bf0e4e5c9",
102
+ "plugin.js": "afe2c3dd15c69515828f513478bb40f2ea3788a8b67ace571c01198acd79dc3b",
103
+ "plugin.json": "be0576b42a5fa60c04da8131a5d20113c020eb8088db228892f0715e9b1808f8",
104
104
  "plugin.public.js": "5f2dc4f01202b16ae804364a33c547509d1fa4026eadebac440730234aa266b5",
105
105
  "public/booking.css": "8b88989fdd04e50efc1688a2f32fe2e422299d7d13b81bb8fce0cc162fa99df6",
106
106
  "public/booking.js": "7dfc3d205696967013886567f67a867443c2e370bac29019d69911d695fdbade",
@@ -110,7 +110,7 @@
110
110
  "public/lib/time.js": "48e13a23e3d7a4916e2097e8f87c03a87d37b71c4ef037a5a16143c6a4185c41",
111
111
  "templates/booking.html": "a4995de46dfdab7e692b56f21347f29c76320cf3b0477889dc3c5d14e0b6dcd7",
112
112
  "tests/admin-diary.test.js": "4e34b447d62b41272ceec49bad9d491ae00dd04f4760d8a7a3046be1912f4abe",
113
- "tests/api.test.js": "57ce73f48776e15454794405bae5b9674baeda46a96e4a11d0e253c6a977a764",
113
+ "tests/api.test.js": "937a79e5af9af6b3f12b7ae15dabc470a91c7fe8ca03e3f7f261e4e641dfe3dc",
114
114
  "tests/public.test.js": "0b7b626d60df5a3d73d7c0077b73f59b62a9fded98ce84a8d93cd32cd0333159",
115
115
  "tests/pure.test.js": "8ba0c8f76b4c9efcc422f39ff29509612544d81f760025a4b9a5594af14e7bcf"
116
116
  }
@@ -1 +1 @@
1
- var v=Object.defineProperty;var w=(f,e,i)=>e in f?v(f,e,{enumerable:!0,configurable:!0,writable:!0,value:i}):f[e]=i;var p=(f,e,i)=>w(f,typeof e!="symbol"?e+"":e,i);import{CanvasManager as M}from"./core/canvas.js";import{PhysicsEngine as P,updateMovingParticle as b,updateParticlePhysics as C}from"./core/physics.js";import{createParticle as z}from"./core/particles.js";const h=class h{static isDateInRange(e,i,s,o){const r=new Date,n=r.getMonth()+1,c=r.getDate();return s<e?n===e&&c>=i||n>e||n<e&&n<=s||n===s&&c<=o:(n>e||n===e&&c>=i)&&(n<s||n===s&&c<=o)}static getCurrentTheme(){for(const[e,i]of Object.entries(h.themes)){const[[s,o],[r,n]]=i.dates;if(h.isDateInRange(s,o,r,n))return e}return null}static isCelebrationSeason(){return h.getCurrentTheme()!==null}static getThemes(){return h.themes}constructor(e={}){this.options={theme:e.theme||"auto",intensity:e.intensity||"medium",enabled:e.enabled!==void 0?e.enabled:!0,zIndex:e.zIndex||999,overrides:e.overrides||{}},this.canvasManager=null,this.physicsEngine=null,this.themeModule=null,this.currentTheme=null,this.particles=[],this.specialParticles=[],this.animationFrame=null,this.lastTime=0,this.running=!1,this.initialized=!1}async init(){if(this.initialized){console.warn("[Celebrations] Already initialized");return}console.log("[Celebrations] Initializing...");let e=this.options.theme;if(e==="auto"&&(e=h.getCurrentTheme(),!e)){console.log("[Celebrations] No active celebration detected");return}if(!h.themes[e]){console.error(`[Celebrations] Unknown theme: ${e}`);return}this.currentTheme=e,console.log(`[Celebrations] Loading theme: ${e}`);try{const s=await import(h.themes[e].module);this.themeModule=s.default,console.log(`[Celebrations] Theme loaded: ${this.themeModule.displayName}`)}catch(i){console.error(`[Celebrations] Failed to load theme module: ${i.message}`);return}this.canvasManager=new M({canvasId:"celebrations-canvas",zIndex:this.options.zIndex}),this.canvasManager.create(),this.physicsEngine=new P({gustInterval:[5e3,15e3],gustStrength:[-2,2]}),window.addEventListener("resize",()=>{this.canvasManager.resize(),this.resetParticles()}),this.resetParticles(),this.initialized=!0,console.log("[Celebrations] Initialized successfully"),this.options.enabled&&this.start()}resetParticles(){const e=Object.assign({},this.themeModule.intensityConfig[this.options.intensity]),i=a=>String(a).toLowerCase().replace(/[^a-z0-9]/g,""),s=[...Object.keys(e),...this.themeModule.decorationKeys||[]],o=a=>s.find(l=>i(l)===i(a))||a,r=a=>["true","1","yes","on"].includes(String(a).toLowerCase()),n=this.themeModule.optInDecorations||{},c=Object.entries(this.options.overrides).some(([a,l])=>["snowonly","fallingonly"].includes(i(a))&&r(l));for(const a of Object.values(n))e[a.chance]=0;for(const[a,l]of Object.entries(this.options.overrides)){if((i(a)==="snowonly"||i(a)==="fallingonly")&&r(l)){for(const g of this.themeModule.decorationKeys||[])e[g]=0;continue}const y=Object.keys(n).find(g=>i(g)===i(a));if(y&&!c&&r(l)){const g=n[y];e[g.chance]=g.value}l==="off"&&(e[o(a)]=0)}this.config=e;const t=this.canvasManager.canvas.width,d=this.canvasManager.canvas.height;this.particles=[],this.specialParticles=[];const m=e.initialParticleRatio!==void 0?e.initialParticleRatio:1,u=Math.floor(e.count*m);if(this.themeModule.createFallingParticle)for(let a=0;a<u;a++){const l=this.themeModule.createFallingParticle(t,d,e);l.y=Math.random()*d,this.particles.push(l)}else for(let a=0;a<u;a++){const l=z(e,t,d);l.y=Math.random()*d,this.particles.push(l)}if(this.themeModule.createInitialDecorations){const a=this.themeModule.createInitialDecorations(t,d,e);this.specialParticles.push(...a),console.log(`[Celebrations] Created ${a.length} decorations`)}console.log(`[Celebrations] Created ${u}/${e.count} particles (${this.options.intensity}, ${Math.round(m*100)}% initial)`)}start(){if(!this.initialized){console.warn("[Celebrations] Cannot start - not initialized");return}if(this.running){console.warn("[Celebrations] Already running");return}console.log("[Celebrations] Starting animation"),this.running=!0,this.lastTime=performance.now(),this.animate()}pause(){this.running&&(console.log("[Celebrations] Pausing animation"),this.running=!1,this.animationFrame&&(cancelAnimationFrame(this.animationFrame),this.animationFrame=null))}resume(){this.running||this.start()}destroy(){console.log("[Celebrations] Destroying effect"),this.pause(),this.canvasManager&&this.canvasManager.canvas&&this.canvasManager.canvas.remove(),this.particles=[],this.specialParticles=[],this.canvasManager=null,this.physicsEngine=null,this.themeModule=null,this.initialized=!1}setIntensity(e){if(!["light","medium","heavy"].includes(e)){console.error(`[Celebrations] Invalid intensity: ${e}`);return}console.log(`[Celebrations] Changing intensity to: ${e}`),this.options.intensity=e,this.resetParticles()}async setTheme(e){console.log(`[Celebrations] Changing theme to: ${e}`);const i=this.running;this.pause(),await new Promise(s=>setTimeout(s,20)),this.initialized=!1,this.canvasManager&&this.canvasManager.clear(),this.particles=[],this.specialParticles=[],this.canvasManager&&this.canvasManager.clear(),this.options.theme=e,await this.init(),i&&this.start()}animate(){if(!this.running)return;const e=performance.now(),i=e-this.lastTime;this.lastTime=e,this.canvasManager.clear();const s=this.canvasManager.ctx,o=this.canvasManager.canvas.width,r=this.canvasManager.canvas.height,n=this.config;this.physicsEngine.updateWind(e);const c=this.physicsEngine.getWindForce();if(this.particles.length<n.count&&Math.random()<.05&&this.themeModule.createFallingParticle){const t=this.themeModule.createFallingParticle(o,r,n);(t.y===void 0||t.y<0)&&(t.y=-20),this.particles.push(t)}if(this.particles.forEach((t,d)=>{if(C(t,i,c),t.y>r+50&&(t.y=-20,t.x=Math.random()*o),t.x<-50&&(t.x=o+50),t.x>o+50&&(t.x=-50),this.themeModule.drawSnowflake&&t.type==="snowflake")this.themeModule.drawSnowflake(s,t);else if(this.themeModule["draw"+this.capitalizeFirst(t.type)]){const m="draw"+this.capitalizeFirst(t.type);this.themeModule[m](s,t,e)}else s.save(),s.globalAlpha=t.opacity,s.fillStyle=this.themeModule.colors?.primary||"#ffffff",s.beginPath(),s.arc(t.x,t.y,t.size,0,Math.PI*2),s.fill(),s.restore()}),this.themeModule.updateSpecialParticles&&this.themeModule.updateSpecialParticles(this.specialParticles,i,o,r),this.specialParticles=this.specialParticles.filter(t=>{if(!t.active)return!1;if(!t.static){b(t,i,e);const u=t.type==="train"?600:200;if(t.x<-u||t.x>o+u)return t.active=!1,!1}if(!isFinite(t.x)||!isFinite(t.y)||t.size&&!isFinite(t.size))return console.warn("[Celebrations] Invalid particle values, removing:",t.type),t.active=!1,!1;const m="draw"+this.capitalizeFirst(t.type);return this.themeModule[m]&&this.themeModule[m](s,t,e),!0}),this.themeModule.spawnSpecialParticle){const t=this.themeModule.spawnSpecialParticle(this.specialParticles,o,r,n);t&&this.specialParticles.push(t)}this.themeModule.drawGlobalEffects&&this.themeModule.drawGlobalEffects(s,e,o,r),this.animationFrame=requestAnimationFrame(()=>this.animate())}capitalizeFirst(e){if(!e)return"";const i=e.replace(/-([a-z])/g,(s,o)=>o.toUpperCase());return i.charAt(0).toUpperCase()+i.slice(1)}};p(h,"themes",{christmas:{module:"./themes/christmas.js?v=20260930-festive",dates:[[12,1],[1,1]],displayName:"Christmas",emoji:"\u{1F384}"},halloween:{module:"./themes/halloween.js?v=20260930-snow",dates:[[10,26],[10,31]],displayName:"Halloween",emoji:"\u{1F383}"},valentines:{module:"./themes/valentines.js?v=20260930-snow",dates:[[2,9],[2,14]],displayName:"Valentine's Day",emoji:"\u{1F495}"},"guy-fawkes":{module:"./themes/guy-fawkes.js?v=20260930-snow",dates:[[11,1],[11,5]],displayName:"Guy Fawkes Night",emoji:"\u{1F386}"},"st-patricks":{module:"./themes/st-patricks.js?v=20260930-snow",dates:[[3,12],[3,17]],displayName:"St Patrick's Day",emoji:"\u2618\uFE0F"},"st-andrews":{module:"./themes/st-andrews.js?v=20260930-snow",dates:[[11,25],[11,30]],displayName:"St Andrew's Day",emoji:"\u{1F3F4}\u{E0067}\u{E0062}\u{E0073}\u{E0063}\u{E0074}\u{E007F}"},"st-davids":{module:"./themes/st-davids.js?v=20260930-snow",dates:[[2,24],[3,1]],displayName:"St David's Day",emoji:"\u{1F3F4}\u{E0067}\u{E0062}\u{E0077}\u{E006C}\u{E0073}\u{E007F}"},"st-georges":{module:"./themes/st-georges.js?v=20260930-snow",dates:[[4,18],[4,23]],displayName:"St George's Day",emoji:"\u{1F3F4}\u{E0067}\u{E0062}\u{E0065}\u{E006E}\u{E0067}\u{E007F}"}});export let CelebrationsEffect=h;export default CelebrationsEffect;
1
+ var w=Object.defineProperty;var v=(f,e,i)=>e in f?w(f,e,{enumerable:!0,configurable:!0,writable:!0,value:i}):f[e]=i;var p=(f,e,i)=>v(f,typeof e!="symbol"?e+"":e,i);import{CanvasManager as M}from"./core/canvas.js";import{PhysicsEngine as P,updateMovingParticle as b,updateParticlePhysics as C}from"./core/physics.js";import{createParticle as z}from"./core/particles.js";const h=class h{static isDateInRange(e,i,s,o){const r=new Date,n=r.getMonth()+1,c=r.getDate();return s<e?n===e&&c>=i||n>e||n<e&&n<=s||n===s&&c<=o:(n>e||n===e&&c>=i)&&(n<s||n===s&&c<=o)}static getCurrentTheme(){for(const[e,i]of Object.entries(h.themes)){const[[s,o],[r,n]]=i.dates;if(h.isDateInRange(s,o,r,n))return e}return null}static isCelebrationSeason(){return h.getCurrentTheme()!==null}static getThemes(){return h.themes}constructor(e={}){this.options={theme:e.theme||"auto",intensity:e.intensity||"medium",enabled:e.enabled!==void 0?e.enabled:!0,zIndex:e.zIndex||999,overrides:e.overrides||{}},this.canvasManager=null,this.physicsEngine=null,this.themeModule=null,this.currentTheme=null,this.particles=[],this.specialParticles=[],this.animationFrame=null,this.lastTime=0,this.running=!1,this.initialized=!1}async init(){if(this.initialized){console.warn("[Celebrations] Already initialized");return}console.log("[Celebrations] Initializing...");let e=this.options.theme;if(e==="auto"&&(e=h.getCurrentTheme(),!e)){console.log("[Celebrations] No active celebration detected");return}if(!h.themes[e]){console.error(`[Celebrations] Unknown theme: ${e}`);return}this.currentTheme=e,console.log(`[Celebrations] Loading theme: ${e}`);try{const s=await import(h.themes[e].module);this.themeModule=s.default,console.log(`[Celebrations] Theme loaded: ${this.themeModule.displayName}`)}catch(i){console.error(`[Celebrations] Failed to load theme module: ${i.message}`);return}this.canvasManager=new M({canvasId:"celebrations-canvas",zIndex:this.options.zIndex}),this.canvasManager.create(),this.physicsEngine=new P({gustInterval:[5e3,15e3],gustStrength:[-2,2]}),window.addEventListener("resize",()=>{this.canvasManager.resize(),this.resetParticles()}),this.resetParticles(),this.initialized=!0,console.log("[Celebrations] Initialized successfully"),this.options.enabled&&this.start()}resetParticles(){const e=Object.assign({},this.themeModule.intensityConfig[this.options.intensity]),i=a=>String(a).toLowerCase().replace(/[^a-z0-9]/g,""),s=[...Object.keys(e),...this.themeModule.decorationKeys||[]],o=a=>s.find(l=>i(l)===i(a))||a,r=a=>["true","1","yes","on"].includes(String(a).toLowerCase()),n=this.themeModule.optInDecorations||{},c=Object.entries(this.options.overrides).some(([a,l])=>["snowonly","fallingonly"].includes(i(a))&&r(l));for(const a of Object.values(n))e[a.chance]=0;for(const[a,l]of Object.entries(this.options.overrides)){if((i(a)==="snowonly"||i(a)==="fallingonly")&&r(l)){for(const g of this.themeModule.decorationKeys||[])e[g]=0;continue}const y=Object.keys(n).find(g=>i(g)===i(a));if(y&&!c&&r(l)){const g=n[y];e[g.chance]=g.value}l==="off"&&(e[o(a)]=0)}this.config=e;const t=this.canvasManager.canvas.width,d=this.canvasManager.canvas.height;this.particles=[],this.specialParticles=[];const m=e.initialParticleRatio!==void 0?e.initialParticleRatio:1,u=Math.floor(e.count*m);if(this.themeModule.createFallingParticle)for(let a=0;a<u;a++){const l=this.themeModule.createFallingParticle(t,d,e);l.y=Math.random()*d,this.particles.push(l)}else for(let a=0;a<u;a++){const l=z(e,t,d);l.y=Math.random()*d,this.particles.push(l)}if(this.themeModule.createInitialDecorations){const a=this.themeModule.createInitialDecorations(t,d,e);this.specialParticles.push(...a),console.log(`[Celebrations] Created ${a.length} decorations`)}console.log(`[Celebrations] Created ${u}/${e.count} particles (${this.options.intensity}, ${Math.round(m*100)}% initial)`)}start(){if(!this.initialized){console.warn("[Celebrations] Cannot start - not initialized");return}if(this.running){console.warn("[Celebrations] Already running");return}console.log("[Celebrations] Starting animation"),this.running=!0,this.lastTime=performance.now(),this.animate()}pause(){this.running&&(console.log("[Celebrations] Pausing animation"),this.running=!1,this.animationFrame&&(cancelAnimationFrame(this.animationFrame),this.animationFrame=null))}resume(){this.running||this.start()}destroy(){console.log("[Celebrations] Destroying effect"),this.pause(),this.canvasManager&&this.canvasManager.canvas&&this.canvasManager.canvas.remove(),this.particles=[],this.specialParticles=[],this.canvasManager=null,this.physicsEngine=null,this.themeModule=null,this.initialized=!1}setIntensity(e){if(!["light","medium","heavy"].includes(e)){console.error(`[Celebrations] Invalid intensity: ${e}`);return}console.log(`[Celebrations] Changing intensity to: ${e}`),this.options.intensity=e,this.resetParticles()}async setTheme(e){console.log(`[Celebrations] Changing theme to: ${e}`);const i=this.running;this.pause(),await new Promise(s=>setTimeout(s,20)),this.initialized=!1,this.canvasManager&&this.canvasManager.clear(),this.particles=[],this.specialParticles=[],this.canvasManager&&this.canvasManager.clear(),this.options.theme=e,await this.init(),i&&this.start()}animate(){if(!this.running)return;const e=performance.now(),i=e-this.lastTime;this.lastTime=e,this.canvasManager.clear();const s=this.canvasManager.ctx,o=this.canvasManager.canvas.width,r=this.canvasManager.canvas.height,n=this.config;this.physicsEngine.updateWind(e);const c=this.physicsEngine.getWindForce();if(this.particles.length<n.count&&Math.random()<.05&&this.themeModule.createFallingParticle){const t=this.themeModule.createFallingParticle(o,r,n);(t.y===void 0||t.y<0)&&(t.y=-20),this.particles.push(t)}if(this.particles.forEach((t,d)=>{if(C(t,i,c),t.y>r+50&&(t.y=-20,t.x=Math.random()*o),t.x<-50&&(t.x=o+50),t.x>o+50&&(t.x=-50),this.themeModule.drawSnowflake&&t.type==="snowflake")this.themeModule.drawSnowflake(s,t);else if(this.themeModule["draw"+this.capitalizeFirst(t.type)]){const m="draw"+this.capitalizeFirst(t.type);this.themeModule[m](s,t,e)}else s.save(),s.globalAlpha=t.opacity,s.fillStyle=this.themeModule.colors?.primary||"#ffffff",s.beginPath(),s.arc(t.x,t.y,t.size,0,Math.PI*2),s.fill(),s.restore()}),this.themeModule.updateSpecialParticles&&this.themeModule.updateSpecialParticles(this.specialParticles,i,o,r),this.specialParticles=this.specialParticles.filter(t=>{if(!t.active)return!1;if(!t.static){b(t,i,e);const u=t.type==="train"?600:200;if(t.x<-u||t.x>o+u)return t.active=!1,!1}if(!isFinite(t.x)||!isFinite(t.y)||t.size&&!isFinite(t.size))return console.warn("[Celebrations] Invalid particle values, removing:",t.type),t.active=!1,!1;const m="draw"+this.capitalizeFirst(t.type);return this.themeModule[m]&&this.themeModule[m](s,t,e),!0}),this.themeModule.spawnSpecialParticle){const t=this.themeModule.spawnSpecialParticle(this.specialParticles,o,r,n);t&&this.specialParticles.push(t)}this.themeModule.drawGlobalEffects&&this.themeModule.drawGlobalEffects(s,e,o,r),this.animationFrame=requestAnimationFrame(()=>this.animate())}capitalizeFirst(e){if(!e)return"";const i=e.replace(/-([a-z])/g,(s,o)=>o.toUpperCase());return i.charAt(0).toUpperCase()+i.slice(1)}};p(h,"themes",{christmas:{module:"./themes/christmas.js?v=20260930-snowman",dates:[[12,1],[1,1]],displayName:"Christmas",emoji:"\u{1F384}"},halloween:{module:"./themes/halloween.js?v=20260930-snow",dates:[[10,26],[10,31]],displayName:"Halloween",emoji:"\u{1F383}"},valentines:{module:"./themes/valentines.js?v=20260930-snow",dates:[[2,9],[2,14]],displayName:"Valentine's Day",emoji:"\u{1F495}"},"guy-fawkes":{module:"./themes/guy-fawkes.js?v=20260930-snow",dates:[[11,1],[11,5]],displayName:"Guy Fawkes Night",emoji:"\u{1F386}"},"st-patricks":{module:"./themes/st-patricks.js?v=20260930-snow",dates:[[3,12],[3,17]],displayName:"St Patrick's Day",emoji:"\u2618\uFE0F"},"st-andrews":{module:"./themes/st-andrews.js?v=20260930-snow",dates:[[11,25],[11,30]],displayName:"St Andrew's Day",emoji:"\u{1F3F4}\u{E0067}\u{E0062}\u{E0073}\u{E0063}\u{E0074}\u{E007F}"},"st-davids":{module:"./themes/st-davids.js?v=20260930-snow",dates:[[2,24],[3,1]],displayName:"St David's Day",emoji:"\u{1F3F4}\u{E0067}\u{E0062}\u{E0077}\u{E006C}\u{E0073}\u{E007F}"},"st-georges":{module:"./themes/st-georges.js?v=20260930-snow",dates:[[4,18],[4,23]],displayName:"St George's Day",emoji:"\u{1F3F4}\u{E0067}\u{E0062}\u{E0065}\u{E006E}\u{E0067}\u{E007F}"}});export let CelebrationsEffect=h;export default CelebrationsEffect;
@@ -1 +1 @@
1
- import{path as s,ellipse as d,gradient as h,rect as b}from"./christmas-drawing.js";function C(o,a,v=!1){return![a.x,a.y,a.size].every(Number.isFinite)||a.size<=0?!1:(o.save(),o.translate(a.x,a.y),o.scale(a.size*(v&&a.vx<0?-1:1),a.size),o.rotate(a.rotation||0),o.globalAlpha*=a.opacity??1,o.lineCap="round",o.lineJoin="round",!0)}function m(o,a,v,e){const i=[];for(let r=0;r<10;r++){const n=-Math.PI/2+r*Math.PI/5,f=e*(r%2?.44:1);i.push([r?"lineTo":"moveTo",a+Math.cos(n)*f,v+Math.sin(n)*f])}i.push(["closePath"]),s(o,i,"#f4d68b","#bb9655",.02)}export const TREE_GIFTS=[{x:-.78,y:.94,width:.48,height:.38,color:"#ad3448"},{x:.36,y:1.04,width:.5,height:.3,color:"#39766e"},{x:-.16,y:1.04,width:.37,height:.29,color:"#b58948"}];export function drawGift(o,a,v=a.x,e=a.y){b(o,v,e,a.width,a.height,.025,a.color,"#d8bc82",.015),b(o,v-.015,e,a.width+.03,.07,.012,a.color,"#d8bc82",.015),s(o,[["moveTo",v+a.width/2,e],["lineTo",v+a.width/2,e+a.height]],null,"#f2dca6",.055);const i=v+a.width/2;s(o,[["moveTo",i,e],["bezierCurveTo",i-.25,e-.21,i-.24,e+.02,i,e],["bezierCurveTo",i+.25,e-.21,i+.24,e+.02,i,e]],null,"#f2dca6",.025)}export function drawTree(o,a,v=0){if(C(o,a)){b(o,-.13,.64,.26,.65,.04,h(o,.6,1.3,["#a58154","#674933"]));for(let e=3;e>=0;e--){const i=-1.38+e*.47,r=.38+e*.24,n=i+.88;s(o,[["moveTo",0,i],["quadraticCurveTo",-r*.33,n-.2,-r,n],["quadraticCurveTo",-r*.66,n+.11,-r*.45,n+.04],["quadraticCurveTo",0,n+.22,r*.45,n+.04],["quadraticCurveTo",r*.66,n+.11,r,n],["quadraticCurveTo",r*.33,n-.2,0,i],["closePath"]],h(o,i,n+.2,["#5c8b72","#2d5c48","#173d35"]));for(let l of[-1,1])for(let u=1;u<=3;u++){const T=i+.28+u*.14,t=l*r*u/5;s(o,[["moveTo",l*.04,T-.09],["quadraticCurveTo",t*.7,T+.07,t,T+.04]],null,"#6a9875",.016)}const f=n-.12;s(o,[["moveTo",-r*.7,f],["quadraticCurveTo",0,f+.29,r*.7,f]],null,"#c9aa6d",.024);for(let l=0;l<5;l++){const u=l/4,T=(u*2-1)*r*.7,t=f+.58*u*(1-u);o.save(),o.globalAlpha*=.75+Math.sin(v*.002+l+e)*.2,o.shadowColor="#ffdd8e",o.shadowBlur=3,d(o,T,t,.035,.035,"#ffe4a4"),o.restore()}for(const l of[-1,1]){const u=l*r*.38,T=n-.01;d(o,u,T,.07,.085,l<0?"#c55459":"#d5b16d"),d(o,u-.025,T-.03,.02,.025,"#f4d9b0")}}o.save(),o.shadowColor="#eacd8e",o.shadowBlur=6,m(o,0,-1.47,.2),o.restore();for(const e of a.gifts||TREE_GIFTS)e.stolen||drawGift(o,e);o.restore()}}export function drawWreath(o,a,v=0){if(C(o,a)){o.beginPath(),o.arc(0,0,.85,0,Math.PI*2),o.strokeStyle="#1c3e33",o.lineWidth=.38,o.stroke();for(let e=0;e<28;e++){const i=e*Math.PI*2/28;o.save(),o.rotate(i),o.translate(.85,0),o.rotate(.45),s(o,[["moveTo",-.2,0],["quadraticCurveTo",0,-.06,.27,-.02]],null,"#86a580",.016);for(let r=0;r<3;r++){const n=-.16+r*.11;s(o,[["moveTo",n,0],["quadraticCurveTo",n-.11,-.17,n+.13,-.14],["quadraticCurveTo",n+.12,-.04,n,0],["moveTo",n,0],["quadraticCurveTo",n-.11,.18,n+.13,.14],["quadraticCurveTo",n+.12,.04,n,0]],e%3?"#3c7053":"#61916c")}o.restore()}for(const e of[-.5,.35,2,3.3,4.6]){const i=Math.cos(e)*.86,r=Math.sin(e)*.86;for(const[n,f]of[[-.045,-.035],[.05,-.015],[0,.055]])d(o,i+n,r+f,.055,.055,"#b63d4a"),d(o,i+n-.016,r+f-.017,.014,.014,"#efac93")}for(let e=0;e<7;e++){const i=e*Math.PI*2/7,r=Math.cos(i)*.91,n=Math.sin(i)*.91;o.save(),o.globalAlpha*=.7+Math.sin(v*.002+e)*.2,o.shadowColor="#ffe3a1",o.shadowBlur=3,d(o,r,n,.028,.028,"#ffe3a1"),o.restore()}s(o,[["moveTo",-.08,.68],["lineTo",-.28,1.34],["lineTo",-.05,1.24],["lineTo",.08,1.37],["lineTo",.16,.76],["closePath"]],"#9c293e"),s(o,[["moveTo",.06,.74],["lineTo",.23,1.3],["lineTo",.35,1.18],["lineTo",.55,1.29],["lineTo",.31,.73],["closePath"]],"#c14653");for(const e of[-1,1])o.save(),o.scale(e,1),s(o,[["moveTo",0,.78],["bezierCurveTo",.6,.28,.76,.95,.39,1.02],["quadraticCurveTo",.19,1.01,0,.78],["closePath"]],h(o,.45,1.05,["#e16b70","#a32d43"])),s(o,[["moveTo",.09,.79],["quadraticCurveTo",.38,.6,.48,.73]],null,"#f4a79b",.025),o.restore();d(o,.03,.8,.13,.12,"#c64858"),o.restore()}}export function drawRobin(o,a,v=0){if(!C(o,a,!0))return;const e=a.state!=="sitting",i=v*.014+(a.waveOffset||0);s(o,[["moveTo",-.4,.14],["lineTo",-1.15,.05],["lineTo",-1.02,.35],["lineTo",-.4,.44],["closePath"]],"#715f50"),d(o,-.03,.04,.66,.71,h(o,-.6,.75,["#a48b6e","#ddd2bc"])),d(o,.3,-.4,.46,.44,"#a18b6d"),d(o,.38,-.02,.39,.49,h(o,-.5,.5,["#e99050","#be5839"])),o.save(),o.translate(-.24,-.15),o.rotate(e?-.4+Math.sin(i)*.85:-.12),s(o,[["moveTo",.1,0],["bezierCurveTo",-.27,-.39,-.96,-.21,-.68,.17],["quadraticCurveTo",-.26,.54,.1,0],["closePath"]],h(o,-.35,.38,["#b69c78","#796449"]));for(let r=0;r<3;r++)s(o,[["moveTo",-.15-r*.14,-.07],["quadraticCurveTo",-.39-r*.12,.16,-.58-r*.05,.12]],null,"#d1b58b",.025);o.restore(),s(o,[["moveTo",.68,-.38],["lineTo",.91,-.29],["lineTo",.68,-.24],["closePath"]],"#41352e"),d(o,.49,-.48,.07,.077,"#24252b"),d(o,.51,-.5,.022,.022,"#fff2db");for(const r of[-.12,.19])s(o,[["moveTo",r,.62],["lineTo",r+(e?-.14:.02),e?.76:.97],["lineTo",r+.17,e?.77:.97]],null,"#957155",.045);s(o,[["moveTo",-.1,-.72],["quadraticCurveTo",.12,-1.32,.56,-1.09],["quadraticCurveTo",.75,-.99,.77,-.79],["quadraticCurveTo",.46,-1.03,.55,-.71],["closePath"]],h(o,-1.25,-.7,["#d75b61","#aa3147"])),s(o,[["moveTo",-.12,-.72],["quadraticCurveTo",.22,-.83,.56,-.71]],null,"#f4e9d0",.1),d(o,.77,-.8,.09,.09,"#f4e9d0"),o.restore()}export function drawElf(o,a){if(!C(o,a,!0))return;const v=a.time||0,e=a.type==="peeing-elf"&&a.state==="peeing",i=a.type==="thieving-elf"&&a.state==="escaping",n=a.type==="elf"||a.state==="approaching"||a.state==="escaping"?Math.sin(v*(i?.019:.009))*.3:0;if(e)for(let l=0;l<15;l++){const u=(v*7e-4+l/15)%1;d(o,.26+u*1.12,.3-u*.65+u*u*1.16,.015,.022,"#dfc477")}for(const[l,u]of[[0,-.16],[1,.14]]){const T=n*(l?-1:1);s(o,[["moveTo",u,.21],["lineTo",u+T,.53],["lineTo",u+T*1.5,.88]],null,l?"#ebe2c4":"#b8ae91",.13);for(let t=0;t<3;t++)s(o,[["moveTo",u+T*(1+t/6)-.06,.57+t*.1],["lineTo",u+T*(1+t/6)+.06,.57+t*.1]],null,"#b8444a",.045);s(o,[["moveTo",u+T*1.5-.09,.85],["quadraticCurveTo",u+T*1.5-.18,1.05,u+T*1.5+.17,1],["quadraticCurveTo",u+T*1.5+.44,.98,u+T*1.5+.33,.82],["quadraticCurveTo",u+T*1.5+.22,.94,u+T*1.5+.12,.84]],"#543c36")}s(o,[["moveTo",-.22,-.47],["quadraticCurveTo",-.44,-.19,-.4,.37],["lineTo",-.18,.3],["lineTo",-.07,.42],["lineTo",.07,.3],["lineTo",.3,.38],["quadraticCurveTo",.38,-.2,.2,-.47],["closePath"]],h(o,-.5,.4,["#648e67","#315c43"])),s(o,[["moveTo",-.37,.14],["lineTo",.32,.14]],null,"#533b32",.09),b(o,-.045,.07,.13,.13,.018,"#d9b870","#ffe5a2",.018),s(o,[["moveTo",-.2,-.49],["lineTo",-.23,-.26],["lineTo",0,-.4],["lineTo",.18,-.24],["lineTo",.2,-.49]],"#b5454e");const f=e?.2:i?-.02:.22;s(o,[["moveTo",.19,-.35],["quadraticCurveTo",.34,-.1,.41,f]],null,"#4a7654",.14),d(o,.43,f,.085,.075,"#edc296"),i&&drawGift(o,{width:.52,height:.39,color:a.gift?.color||"#b74450"},.3,-.15),d(o,0,-.69,.28,.3,e?"#967349":"#efc89f");for(const l of[-1,1])s(o,[["moveTo",l*.22,-.81],["lineTo",l*.43,-.9],["quadraticCurveTo",l*.42,-.62,l*.21,-.65]],"#e2b38a");e||(d(o,.13,-.75,.024,.035,"#322e30"),d(o,.29,-.65,.07,.055,"#dc9e7c"),s(o,[["moveTo",.08,-.57],["quadraticCurveTo",.17,-.49,.22,-.58]],null,"#956349",.025)),s(o,[["moveTo",-.29,-.91],["quadraticCurveTo",-.21,-1.38,.21,-1.3],["quadraticCurveTo",.48,-1.29,.5,-1.06],["quadraticCurveTo",.25,-1.2,.28,-.9],["closePath"]],h(o,-1.38,-.9,["#d56862","#a33747"])),s(o,[["moveTo",-.29,-.9],["quadraticCurveTo",0,-.97,.28,-.9]],null,"#d9b878",.065),d(o,.5,-1.05,.06,.075,"#f1ce7e"),o.restore()}
1
+ import{path as i,ellipse as l,gradient as v,rect as b}from"./christmas-drawing.js";function m(o,a,d=!1){return![a.x,a.y,a.size].every(Number.isFinite)||a.size<=0?!1:(o.save(),o.translate(a.x,a.y),o.scale(a.size*(d&&a.vx<0?-1:1),a.size),o.rotate(a.rotation||0),o.globalAlpha*=a.opacity??1,o.lineCap="round",o.lineJoin="round",!0)}function C(o,a,d,e){const n=[];for(let r=0;r<10;r++){const u=-Math.PI/2+r*Math.PI/5,h=e*(r%2?.44:1);n.push([r?"lineTo":"moveTo",a+Math.cos(u)*h,d+Math.sin(u)*h])}n.push(["closePath"]),i(o,n,"#f4d68b","#bb9655",.02)}export const TREE_GIFTS=[{x:-.78,y:.94,width:.48,height:.38,color:"#ad3448"},{x:.36,y:1.04,width:.5,height:.3,color:"#39766e"},{x:-.16,y:1.04,width:.37,height:.29,color:"#b58948"}];export function drawGift(o,a,d=a.x,e=a.y){b(o,d,e,a.width,a.height,.025,a.color,"#d8bc82",.015),b(o,d-.015,e,a.width+.03,.07,.012,a.color,"#d8bc82",.015),i(o,[["moveTo",d+a.width/2,e],["lineTo",d+a.width/2,e+a.height]],null,"#f2dca6",.055);const n=d+a.width/2;i(o,[["moveTo",n,e],["bezierCurveTo",n-.25,e-.21,n-.24,e+.02,n,e],["bezierCurveTo",n+.25,e-.21,n+.24,e+.02,n,e]],null,"#f2dca6",.025)}export function drawTree(o,a,d=0){if(m(o,a)){b(o,-.13,.64,.26,.65,.04,v(o,.6,1.3,["#a58154","#674933"]));for(let e=3;e>=0;e--){const n=-1.38+e*.47,r=.38+e*.24,u=n+.88;i(o,[["moveTo",0,n],["quadraticCurveTo",-r*.33,u-.2,-r,u],["quadraticCurveTo",-r*.66,u+.11,-r*.45,u+.04],["quadraticCurveTo",0,u+.22,r*.45,u+.04],["quadraticCurveTo",r*.66,u+.11,r,u],["quadraticCurveTo",r*.33,u-.2,0,n],["closePath"]],v(o,n,u+.2,["#5c8b72","#2d5c48","#173d35"]));for(let T of[-1,1])for(let s=1;s<=3;s++){const f=n+.28+s*.14,t=T*r*s/5;i(o,[["moveTo",T*.04,f-.09],["quadraticCurveTo",t*.7,f+.07,t,f+.04]],null,"#6a9875",.016)}const h=u-.12;i(o,[["moveTo",-r*.7,h],["quadraticCurveTo",0,h+.29,r*.7,h]],null,"#c9aa6d",.024);for(let T=0;T<5;T++){const s=T/4,f=(s*2-1)*r*.7,t=h+.58*s*(1-s);o.save(),o.globalAlpha*=.75+Math.sin(d*.002+T+e)*.2,o.shadowColor="#ffdd8e",o.shadowBlur=3,l(o,f,t,.035,.035,"#ffe4a4"),o.restore()}for(const T of[-1,1]){const s=T*r*.38,f=u-.01;l(o,s,f,.07,.085,T<0?"#c55459":"#d5b16d"),l(o,s-.025,f-.03,.02,.025,"#f4d9b0")}}o.save(),o.shadowColor="#eacd8e",o.shadowBlur=6,C(o,0,-1.47,.2),o.restore();for(const e of a.gifts||TREE_GIFTS)e.stolen||drawGift(o,e);o.restore()}}export function drawWreath(o,a,d=0){if(m(o,a)){o.beginPath(),o.arc(0,0,.85,0,Math.PI*2),o.strokeStyle="#1c3e33",o.lineWidth=.38,o.stroke();for(let e=0;e<28;e++){const n=e*Math.PI*2/28;o.save(),o.rotate(n),o.translate(.85,0),o.rotate(.45),i(o,[["moveTo",-.2,0],["quadraticCurveTo",0,-.06,.27,-.02]],null,"#86a580",.016);for(let r=0;r<3;r++){const u=-.16+r*.11;i(o,[["moveTo",u,0],["quadraticCurveTo",u-.11,-.17,u+.13,-.14],["quadraticCurveTo",u+.12,-.04,u,0],["moveTo",u,0],["quadraticCurveTo",u-.11,.18,u+.13,.14],["quadraticCurveTo",u+.12,.04,u,0]],e%3?"#3c7053":"#61916c")}o.restore()}for(const e of[-.5,.35,2,3.3,4.6]){const n=Math.cos(e)*.86,r=Math.sin(e)*.86;for(const[u,h]of[[-.045,-.035],[.05,-.015],[0,.055]])l(o,n+u,r+h,.055,.055,"#b63d4a"),l(o,n+u-.016,r+h-.017,.014,.014,"#efac93")}for(let e=0;e<7;e++){const n=e*Math.PI*2/7,r=Math.cos(n)*.91,u=Math.sin(n)*.91;o.save(),o.globalAlpha*=.7+Math.sin(d*.002+e)*.2,o.shadowColor="#ffe3a1",o.shadowBlur=3,l(o,r,u,.028,.028,"#ffe3a1"),o.restore()}i(o,[["moveTo",-.08,.68],["lineTo",-.28,1.34],["lineTo",-.05,1.24],["lineTo",.08,1.37],["lineTo",.16,.76],["closePath"]],"#9c293e"),i(o,[["moveTo",.06,.74],["lineTo",.23,1.3],["lineTo",.35,1.18],["lineTo",.55,1.29],["lineTo",.31,.73],["closePath"]],"#c14653");for(const e of[-1,1])o.save(),o.scale(e,1),i(o,[["moveTo",0,.78],["bezierCurveTo",.6,.28,.76,.95,.39,1.02],["quadraticCurveTo",.19,1.01,0,.78],["closePath"]],v(o,.45,1.05,["#e16b70","#a32d43"])),i(o,[["moveTo",.09,.79],["quadraticCurveTo",.38,.6,.48,.73]],null,"#f4a79b",.025),o.restore();l(o,.03,.8,.13,.12,"#c64858"),o.restore()}}export function drawRobin(o,a,d=0){if(!m(o,a,!0))return;const e=a.state!=="sitting",n=d*.014+(a.waveOffset||0);i(o,[["moveTo",-.4,.14],["lineTo",-1.15,.05],["lineTo",-1.02,.35],["lineTo",-.4,.44],["closePath"]],"#715f50"),l(o,-.03,.04,.66,.71,v(o,-.6,.75,["#a48b6e","#ddd2bc"])),l(o,.3,-.4,.46,.44,"#a18b6d"),l(o,.38,-.02,.39,.49,v(o,-.5,.5,["#e99050","#be5839"])),o.save(),o.translate(-.24,-.15),o.rotate(e?-.4+Math.sin(n)*.85:-.12),i(o,[["moveTo",.1,0],["bezierCurveTo",-.27,-.39,-.96,-.21,-.68,.17],["quadraticCurveTo",-.26,.54,.1,0],["closePath"]],v(o,-.35,.38,["#b69c78","#796449"]));for(let r=0;r<3;r++)i(o,[["moveTo",-.15-r*.14,-.07],["quadraticCurveTo",-.39-r*.12,.16,-.58-r*.05,.12]],null,"#d1b58b",.025);o.restore(),i(o,[["moveTo",.68,-.38],["lineTo",.91,-.29],["lineTo",.68,-.24],["closePath"]],"#41352e"),l(o,.49,-.48,.07,.077,"#24252b"),l(o,.51,-.5,.022,.022,"#fff2db");for(const r of[-.12,.19])i(o,[["moveTo",r,.62],["lineTo",r+(e?-.14:.02),e?.76:.97],["lineTo",r+.17,e?.77:.97]],null,"#957155",.045);i(o,[["moveTo",-.1,-.72],["quadraticCurveTo",.12,-1.32,.56,-1.09],["quadraticCurveTo",.75,-.99,.77,-.79],["quadraticCurveTo",.46,-1.03,.55,-.71],["closePath"]],v(o,-1.25,-.7,["#d75b61","#aa3147"])),i(o,[["moveTo",-.12,-.72],["quadraticCurveTo",.22,-.83,.56,-.71]],null,"#f4e9d0",.1),l(o,.77,-.8,.09,.09,"#f4e9d0"),o.restore()}export function drawElf(o,a){if(!m(o,a,!0))return;const d=a.time||0,e=a.type==="peeing-elf"&&a.state==="peeing",n=a.type==="thieving-elf"&&a.state==="escaping",u=a.type==="elf"||a.state==="approaching"||a.state==="escaping"?Math.sin(d*(n?.019:.009))*.3:0;if(e)for(let T=0;T<15;T++){const s=(d*7e-4+T/15)%1;l(o,.26+s*1.12,.3-s*.65+s*s*1.16,.015,.022,"#dfc477")}for(const[T,s]of[[0,-.16],[1,.14]]){const f=u*(T?-1:1);i(o,[["moveTo",s,.21],["lineTo",s+f,.53],["lineTo",s+f*1.5,.88]],null,T?"#ebe2c4":"#b8ae91",.13);for(let t=0;t<3;t++)i(o,[["moveTo",s+f*(1+t/6)-.06,.57+t*.1],["lineTo",s+f*(1+t/6)+.06,.57+t*.1]],null,"#b8444a",.045);i(o,[["moveTo",s+f*1.5-.09,.85],["quadraticCurveTo",s+f*1.5-.18,1.05,s+f*1.5+.17,1],["quadraticCurveTo",s+f*1.5+.44,.98,s+f*1.5+.33,.82],["quadraticCurveTo",s+f*1.5+.22,.94,s+f*1.5+.12,.84]],"#543c36")}i(o,[["moveTo",-.22,-.47],["quadraticCurveTo",-.44,-.19,-.4,.37],["lineTo",-.18,.3],["lineTo",-.07,.42],["lineTo",.07,.3],["lineTo",.3,.38],["quadraticCurveTo",.38,-.2,.2,-.47],["closePath"]],v(o,-.5,.4,["#648e67","#315c43"])),i(o,[["moveTo",-.37,.14],["lineTo",.32,.14]],null,"#533b32",.09),b(o,-.045,.07,.13,.13,.018,"#d9b870","#ffe5a2",.018),i(o,[["moveTo",-.2,-.49],["lineTo",-.23,-.26],["lineTo",0,-.4],["lineTo",.18,-.24],["lineTo",.2,-.49]],"#b5454e");const h=e?.2:n?-.02:.22;i(o,[["moveTo",.19,-.35],["quadraticCurveTo",.34,-.1,.41,h]],null,"#4a7654",.14),l(o,.43,h,.085,.075,"#edc296"),n&&drawGift(o,{width:.52,height:.39,color:a.gift?.color||"#b74450"},.3,-.15),l(o,0,-.69,.28,.3,e?"#967349":"#efc89f");for(const T of[-1,1])i(o,[["moveTo",T*.22,-.81],["lineTo",T*.43,-.9],["quadraticCurveTo",T*.42,-.62,T*.21,-.65]],"#e2b38a");e||(l(o,.13,-.75,.024,.035,"#322e30"),l(o,.29,-.65,.07,.055,"#dc9e7c"),i(o,[["moveTo",.08,-.57],["quadraticCurveTo",.17,-.49,.22,-.58]],null,"#956349",.025)),i(o,[["moveTo",-.29,-.91],["quadraticCurveTo",-.21,-1.38,.21,-1.3],["quadraticCurveTo",.48,-1.29,.5,-1.06],["quadraticCurveTo",.25,-1.2,.28,-.9],["closePath"]],v(o,-1.38,-.9,["#d56862","#a33747"])),i(o,[["moveTo",-.29,-.9],["quadraticCurveTo",0,-.97,.28,-.9]],null,"#d9b878",.065),l(o,.5,-1.05,.06,.075,"#f1ce7e"),o.restore()}export function drawSnowman(o,a,d=0){if(m(o,a)){o.rotate(Math.sin(d*.002+(a.wavePhase||0))*.025),l(o,0,2.18,1.2,.18,"rgba(8, 20, 35, 0.22)");for(const e of[-1,1])o.save(),o.scale(e,1),i(o,[["moveTo",.52,.4],["quadraticCurveTo",1.02,.19,1.45,-.12],["lineTo",1.73,-.25],["moveTo",1.35,-.05],["lineTo",1.4,-.42],["moveTo",1.55,-.17],["lineTo",1.8,.02]],null,"#705440",.065),i(o,[["moveTo",.8,.28],["lineTo",1.43,-.1]],null,"#b7956b",.018),o.restore();for(const[e,n,r]of[[1.28,.98,.94],[.32,.7,.7],[-.57,.51,.5]]){l(o,0,e,n,r,v(o,e-r,e+r,["#fffaf0","#eaf1f3","#9bb9cb"])),l(o,-n*.28,e-r*.24,n*.5,r*.46,"rgba(255,255,249,0.35)");for(let u=0;u<5;u++)l(o,n*(.3+u%2*.12),e+r*(u/7-.15),.018,.014,"#b7cbd4")}for(const e of[.2,.55,1.04])l(o,.08,e,.075,.065,"#35414a"),l(o,.055,e-.02,.022,.014,"#77878c");i(o,[["moveTo",.2,-.19],["quadraticCurveTo",.5,.3,.43,.95],["lineTo",.69,.92],["quadraticCurveTo",.66,.28,.48,-.17],["closePath"]],v(o,-.2,1,["#d56464","#a33548"]));for(const e of[.27,.56,.84])i(o,[["moveTo",.43,e],["lineTo",.65,e-.015]],null,"#deb88b",.028);for(let e=0;e<5;e++)i(o,[["moveTo",.44+e*.055,.93],["lineTo",.42+e*.055,1.06]],null,"#b84352",.025);l(o,0,-.17,.56,.14,v(o,-.3,-.03,["#e57f75","#b43c51"])),i(o,[["moveTo",-.43,-.18],["quadraticCurveTo",0,-.01,.45,-.15]],null,"#f0b79a",.022);for(const e of[-.17,.16])l(o,e,-.66,.052,.057,"#273844"),l(o,e-.015,-.68,.013,.012,"#e2edf0");for(let e=0;e<5;e++){const n=(e-2)*.085;l(o,n,-.39+Math.cos((e-2)*.55)*.07,.025,.023,"#44515b")}i(o,[["moveTo",.04,-.59],["quadraticCurveTo",.28,-.63,.62,-.54],["quadraticCurveTo",.29,-.45,.04,-.48],["closePath"]],v(o,-.63,-.45,["#efa151","#cb6534"])),i(o,[["moveTo",-.43,-1.05],["lineTo",-.38,-1.74],["quadraticCurveTo",0,-1.86,.4,-1.72],["lineTo",.45,-1.04],["closePath"]],v(o,-1.8,-1,["#465260","#182b3b"])),b(o,-.4,-1.22,.83,.16,.025,"#a43c50"),l(o,0,-1.04,.7,.115,"#213544"),i(o,[["moveTo",-.55,-1.08],["quadraticCurveTo",0,-1.14,.5,-1.06]],null,"#6d7d88",.02);for(const e of[-1,1])i(o,[["moveTo",.2,-1.23],["quadraticCurveTo",.2+e*.27,-1.52,.2+e*.25,-1.25],["quadraticCurveTo",.2+e*.18,-1.15,.2,-1.23]],"#527957");for(const[e,n]of[[.18,-1.22],[.26,-1.2],[.22,-1.27]])l(o,e,n,.04,.04,"#c24c57");o.restore()}}
@@ -1 +1 @@
1
- import{createParticle as k}from"./../core/particles.js";import{drawSleigh as T,drawTrain as I}from"./christmas-vehicles.js";import{drawTree as z,drawWreath as C,drawRobin as F,drawElf as b,TREE_GIFTS as R}from"./christmas-festive.js";import{spawnElfAntic as E,updateElfAntic as Y}from"./christmas-antics.js";export default{name:"christmas",displayName:"Christmas",emoji:"\u{1F384}",intensityConfig:{light:{count:50,speedRange:[.5,1.5],sizeRange:[1,3],trees:3,wreaths:2,northStars:1,snowmen:2},medium:{count:150,speedRange:[.8,2.5],sizeRange:[1,4],trees:6,wreaths:3,northStars:1,snowmen:3},heavy:{count:300,speedRange:[1,3.5],sizeRange:[1,5],trees:10,wreaths:4,northStars:1,snowmen:4}},particles:["snowflake"],decorations:["tree","wreath","sleigh","robin","train","elf","firework","north-star","snowman","peeing-elf","thieving-elf"],decorationKeys:["trees","wreaths","northStars","snowmen","sleigh","elf","train","firework","robin","peeingElfChance","thievingElfChance"],optInDecorations:{peeingElf:{chance:"peeingElfChance",value:7e-4},thievingElf:{chance:"thievingElfChance",value:.001}},colors:{primary:"#ffffff",secondary:"#228B22",accent:"#c00",gold:"#FFD700"},createSnowflakeParticle(e,t,n){const s=k(n,e,t);return s.type="snowflake",s},createFallingParticle(e,t,n){return this.createSnowflakeParticle(e,t,n)},createTree(e,t,n={}){return{type:"tree",gifts:R.map(s=>({...s,stolen:!1,reserved:!1})),x:n.x!==void 0?n.x:Math.random()*e,y:n.y!==void 0?n.y:Math.random()*t,vx:0,vy:0,size:20+Math.random()*15,opacity:.6+Math.random()*.3,rotation:0,rotationSpeed:0,active:!0,static:!0}},createWreath(e,t,n={}){const s=[];for(let o=0;o<20;o++){const r=o/20*Math.PI*2;s.push({angle:r,radius:.9+Math.random()*.2,thickness:.2+Math.random()*.15,color:o%2===0?"#1a6b1a":"#228B22"})}return{type:"wreath",x:n.x!==void 0?n.x:Math.random()*e,y:n.y!==void 0?n.y:Math.random()*t,vx:0,vy:0,size:15+Math.random()*10,opacity:.7+Math.random()*.2,rotation:0,rotationSpeed:0,active:!0,static:!0,shape:s}},createNorthStar(e,t,n={}){return{type:"north-star",x:n.x!==void 0?n.x:e/2,y:n.y!==void 0?n.y:80,vx:0,vy:0,size:25,opacity:1,twinklePhase:Math.random()*Math.PI*2,twinkleSpeed:.003,active:!0,static:!0}},createSnowman(e,t,n={}){return{type:"snowman",x:n.x!==void 0?n.x:Math.random()*e,y:n.y!==void 0?n.y:t-50,vx:0,vy:0,size:15+Math.random()*10,opacity:1,time:Math.random()*1e3,wavePhase:Math.random()*Math.PI*2,active:!0,static:!0}},createInitialDecorations(e,t,n){const s=[],a=n.trees??6;for(let i=0;i<a;i++)s.push(this.createTree(e,t,{x:e/(a+1)*(i+1),y:t-60-Math.random()*20}));const o=n.wreaths??3;for(let i=0;i<o;i++)s.push(this.createWreath(e,t,{x:e/(o+1)*(i+1),y:50+Math.random()*100}));n.northStars!==0&&n.northStars&&s.push(this.createNorthStar(e,t,{x:e/2,y:60}));const r=n.snowmen??3;for(let i=0;i<r;i++)s.push(this.createSnowman(e,t,{x:e/(r+1)*(i+1),y:t-50-Math.random()*10}));return s},spawnSpecialParticle(e,t,n,s={}){const a=E(e,t,n,s);if(a)return a;const o=Math.random();if(o<5e-4){if(s.sleigh===0||e.some(f=>f.type==="sleigh"))return null;const r=Math.random()<.5,i=r?-100:t+100,h=100+Math.random()*(n*.3);return{type:"sleigh",x:i,startX:i,y:h,baseY:h,targetX:r?t+100:-100,canvasWidth:t,vx:r?3+Math.random()*2:-(3+Math.random()*2),vy:0,arcHeight:150+Math.random()*100,time:0,size:15+Math.random()*10,opacity:.9,rotation:0,active:!0,static:!1}}else if(o<.0013&&s.elf!==0){const r=Math.random()<.5;return{type:"elf",x:r?-50:t+50,y:n-30,baseY:n-30,vx:r?1.5+Math.random()*1:-(1.5+Math.random()*1),waveAmplitude:3,waveFrequency:.05,waveOffset:Math.random()*Math.PI*2,time:0,size:10+Math.random()*5,opacity:.95,rotation:0,active:!0,static:!1}}else if(o<.005&&s.train!==0){if(e.some(l=>l.type==="train"))return null;const r=Math.random()<.5,i=r?-500:t+500,h=21+Math.random()*9,f=h*.72,d=n-f-10;return{type:"train",x:i,y:d,baseY:d,vx:r?4+Math.random()*2:-(4+Math.random()*2),vy:0,size:h,opacity:1,time:0,smoke:[],active:!0,static:!1,carriages:2+Math.floor(Math.random()*2)}}else{if(o<.008&&s.firework!==0)return{type:"firework",x:Math.random()*t,y:n,vx:(Math.random()-.5)*4,vy:-10-Math.random()*5,size:2+Math.random()*2,opacity:1,active:!0,static:!1,time:0,exploded:!1,explosionTime:30+Math.random()*30};if(o<.012&&s.robin!==0){if(e.some(m=>m.type==="robin"))return null;const r=Math.random()<.5,i=Math.random()*(n*.2),h=r?-50:t+50,f=10+Math.random()*5,d=Math.max(f*3,Math.min(n*.6-f*2,n*.2+Math.random()*(n*.4))),l=Math.random()*(t*.6)+t*.2;return{type:"robin",state:"flying_in",x:h,y:i,startX:h,startY:i,targetX:l,targetY:d,vx:r?.5+Math.random()*.5:-(.5+Math.random()*.5),vy:0,size:f,opacity:.95,active:!0,static:!1,sitTime:3e3+Math.random()*2e3,sitStartTime:0,flightProgress:0,time:0,waveOffset:Math.random()*Math.PI*2}}}return null},updateSpecialParticles(e,t,n=1024,s=768){e.forEach(a=>{switch(a.time!==void 0&&(a.time+=t),a.type){case"peeing-elf":case"thieving-elf":Y(a,t,n);break;case"sleigh":(a.vx>0&&a.x>a.targetX||a.vx<0&&a.x<a.targetX)&&(a.active=!1);break;case"robin":switch(a.state){case"flying_in":const o=a.targetX-a.x,r=a.targetY-a.y,i=Math.sqrt(o*o+r*r);if(i<20)a.state="sitting",a.vx=0,a.vy=0,a.sitStartTime=a.time;else{const M=o/i,u=r/i,v=3;a.vx=M*v;const w=30,g=.008,S=Math.sin(a.time*g+a.waveOffset)*w,P=Math.cos(a.time*g+a.waveOffset)*w*g;a.vy=u*v*.3+P}break;case"sitting":if(a.vx=0,a.vy=0,a.time-a.sitStartTime>a.sitTime){a.state="flying_away";const M=a.x<n/2?-4:4;a.vx=M,a.flyAwayStartTime=a.time}break;case"flying_away":const h=a.vx>0?1:-1;a.vx=h*4;const f=25,d=.01,l=a.time-a.flyAwayStartTime,m=Math.sin(l*d+a.waveOffset)*f,y=Math.cos(l*d+a.waveOffset)*f*d;a.vy=-1.5+y,(a.x<-50||a.x>n+50||a.y<-50)&&(a.active=!1);break}break;case"train":if(a.smoke||(a.smoke=[]),(a.vx>0&&a.x>n+500||a.vx<0&&a.x<-500)&&(a.active=!1),a.lastSmokeTime||(a.lastSmokeTime=0),a.time-a.lastSmokeTime>150){a.lastSmokeTime=a.time;const r=a.size*1.8/20,i=a.vx>0?1:-1,h=r*8,f=r*7,d=r*10,y=-h-r-f-d*2-r*8,M=r*70,u=r*25,w=(M-u)*.7,g=a.x+w*i,S=a.y+y,P={x:g,y:S,vx:(Math.random()-.5)*.8,vy:-.8-Math.random()*.4,size:8+Math.random()*6,opacity:.7+Math.random()*.2,fadeRate:.012+Math.random()*.008};a.smoke.push(P)}a.smoke=a.smoke.filter(o=>(o.x+=o.vx,o.y+=o.vy,o.vy*=.99,o.size*=1.02,o.opacity-=o.fadeRate,o.opacity>0));break;case"firework":if(!a.exploded){const o=a.y<=a.targetY,r=a.time>=a.explosionTime;(o||r)&&(a.exploded=!0,this.explodeFirework(a,e),a.active=!1)}break}})},drawSnowflake(e,t){e.save(),e.translate(t.x,t.y),e.rotate(t.rotation),e.globalAlpha=t.opacity,e.fillStyle="#ffffff",e.strokeStyle="#ffffff",e.lineWidth=Math.max(t.size*.15,.5);const n=6,s=t.size;for(let a=0;a<n;a++){const o=Math.PI*2*a/n;e.beginPath(),e.moveTo(0,0),e.lineTo(Math.cos(o)*s,Math.sin(o)*s),e.stroke();const r=s*.4,i=Math.PI/6,h=Math.cos(o)*(s*.6),f=Math.sin(o)*(s*.6);e.beginPath(),e.moveTo(h,f),e.lineTo(h+Math.cos(o-i)*r,f+Math.sin(o-i)*r),e.stroke(),e.beginPath(),e.moveTo(h,f),e.lineTo(h+Math.cos(o+i)*r,f+Math.sin(o+i)*r),e.stroke()}e.restore()},drawTree:z,drawWreath:C,drawNorthStar(e,t,n){const s=t.x,a=t.y,o=t.size,r=.8+Math.sin(n*t.twinkleSpeed+t.twinklePhase)*.2;e.save(),e.translate(s,a);const i=o*4*r,h=e.createRadialGradient(0,0,0,0,0,i);h.addColorStop(0,"rgba(220, 230, 240, 0.4)"),h.addColorStop(.4,"rgba(200, 210, 220, 0.2)"),h.addColorStop(1,"rgba(180, 190, 200, 0)"),e.fillStyle=h,e.beginPath(),e.arc(0,0,i,0,Math.PI*2),e.fill();const f=o*1,d=o*1.5,l=o*.12,m=e.createRadialGradient(0,0,0,0,0,o);m.addColorStop(0,"#ffffff"),m.addColorStop(.3,"#f0f0f0"),m.addColorStop(.6,"#c0c0c0"),m.addColorStop(1,"#a0a0a0"),e.shadowColor="rgba(220, 230, 240, 0.9)",e.shadowBlur=o*.8*r,e.fillStyle=m,e.beginPath(),e.moveTo(0,-d),e.lineTo(l,-l),e.lineTo(f,0),e.lineTo(l,l),e.lineTo(0,d),e.lineTo(-l,l),e.lineTo(-f,0),e.lineTo(-l,-l),e.closePath(),e.fill(),e.shadowBlur=o*1.2*r,e.shadowColor="rgba(255, 255, 255, 1)";const y=e.createRadialGradient(0,0,0,0,0,o*.3);y.addColorStop(0,"#ffffff"),y.addColorStop(.6,"#f5f5f5"),y.addColorStop(1,"rgba(200, 200, 200, 0.8)"),e.fillStyle=y,e.beginPath(),e.arc(0,0,o*.3,0,Math.PI*2),e.fill(),e.shadowBlur=0,e.restore()},drawSleigh:T,drawElf:b,drawPeeingElf:b,drawThievingElf:b,drawTrain:I,drawFirework(e,t){e.fillStyle=t.color||"#ffffff",e.beginPath(),e.arc(t.x,t.y,t.size,0,Math.PI*2),e.fill()},explodeFirework(e,t){const n=50+Math.random()*50,s=["#ff0000","#ffff00","#00ff00","#0000ff","#ffffff"];for(let a=0;a<n;a++){const o=Math.random()*Math.PI*2,r=Math.random()*5+2;t.push({type:"spark",x:e.x,y:e.y,vx:Math.cos(o)*r,vy:Math.sin(o)*r,size:2+Math.random()*2,opacity:1,active:!0,static:!1,color:s[Math.floor(Math.random()*s.length)]})}},drawRobin:F,drawSnowman(e,t,n){const s=t.x,a=t.y,o=t.size,r=Math.sin(n*.002+t.wavePhase)*.03;e.save(),e.translate(s,a),e.rotate(r),e.fillStyle="rgba(0, 0, 0, 0.2)",e.beginPath(),e.ellipse(0,o*2,o*1.2,o*.3,0,0,Math.PI*2),e.fill(),e.fillStyle="#FFFFFF",e.strokeStyle="#E0E0E0",e.lineWidth=1,e.beginPath(),e.arc(0,o*1.3,o*1,0,Math.PI*2),e.fill(),e.stroke(),e.beginPath(),e.arc(0,o*.3,o*.7,0,Math.PI*2),e.fill(),e.stroke(),e.beginPath(),e.arc(0,-o*.6,o*.5,0,Math.PI*2),e.fill(),e.stroke(),e.fillStyle="#000000",e.beginPath(),e.arc(0,o*.5,o*.08,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(0,o*.1,o*.08,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(0,-o*.2,o*.08,0,Math.PI*2),e.fill(),e.strokeStyle="#654321",e.lineWidth=o*.1,e.beginPath(),e.moveTo(-o*.6,o*.3),e.lineTo(-o*1.2,o*0),e.lineTo(-o*1.5,-o*.1),e.stroke(),e.lineWidth=o*.05,e.beginPath(),e.moveTo(-o*1.5,-o*.1),e.lineTo(-o*1.7,-o*.3),e.stroke(),e.beginPath(),e.moveTo(-o*1.5,-o*.1),e.lineTo(-o*1.8,-o*.05),e.stroke(),e.lineWidth=o*.1,e.beginPath(),e.moveTo(o*.6,o*.3),e.lineTo(o*1.2,o*0),e.lineTo(o*1.5,-o*.1),e.stroke(),e.lineWidth=o*.05,e.beginPath(),e.moveTo(o*1.5,-o*.1),e.lineTo(o*1.7,-o*.3),e.stroke(),e.beginPath(),e.moveTo(o*1.5,-o*.1),e.lineTo(o*1.8,-o*.05),e.stroke(),e.fillStyle="#FF6347",e.beginPath(),e.moveTo(o*.15,-o*.6),e.lineTo(o*.6,-o*.65),e.lineTo(o*.15,-o*.55),e.closePath(),e.fill(),e.fillStyle="#000000",e.beginPath(),e.arc(-o*.15,-o*.7,o*.08,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(o*.15,-o*.7,o*.08,0,Math.PI*2),e.fill(),[{x:-o*.2,y:-o*.4},{x:-o*.1,y:-o*.35},{x:0,y:-o*.33},{x:o*.1,y:-o*.35},{x:o*.2,y:-o*.4}].forEach(h=>{e.beginPath(),e.arc(h.x,h.y,o*.05,0,Math.PI*2),e.fill()}),e.fillStyle="#c00",e.strokeStyle="#8B0000",e.lineWidth=1,e.beginPath(),e.ellipse(0,-o*.15,o*.55,o*.15,0,0,Math.PI*2),e.fill(),e.stroke(),e.fillRect(o*.3,-o*.1,o*.2,o*.8),e.strokeRect(o*.3,-o*.1,o*.2,o*.8),e.strokeStyle="#8B0000",e.lineWidth=o*.03;for(let h=0;h<4;h++)e.beginPath(),e.moveTo(o*.33+h*o*.13,o*.7),e.lineTo(o*.33+h*o*.13,o*.85),e.stroke();e.fillStyle="#000000",e.strokeStyle="#333333",e.lineWidth=2,e.beginPath(),e.ellipse(0,-o*1.1,o*.7,o*.15,0,0,Math.PI*2),e.fill(),e.stroke(),e.fillRect(-o*.45,-o*1.8,o*.9,o*.7),e.strokeRect(-o*.45,-o*1.8,o*.9,o*.7),e.fillStyle="#c00",e.fillRect(-o*.45,-o*1.3,o*.9,o*.15),e.fillStyle="#228B22",e.beginPath(),e.arc(-o*.15,-o*1.22,o*.08,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(o*.15,-o*1.22,o*.08,0,Math.PI*2),e.fill(),e.fillStyle="#FF0000",e.beginPath(),e.arc(0,-o*1.25,o*.06,0,Math.PI*2),e.fill(),e.restore()}};
1
+ import{createParticle as T}from"./../core/particles.js";import{drawSleigh as c,drawTrain as z}from"./christmas-vehicles.js";import{drawTree as P,drawWreath as C,drawRobin as x,drawElf as b,drawSnowman as Y,TREE_GIFTS as R}from"./christmas-festive.js";import{spawnElfAntic as E,updateElfAntic as I}from"./christmas-antics.js";export default{name:"christmas",displayName:"Christmas",emoji:"\u{1F384}",intensityConfig:{light:{count:50,speedRange:[.5,1.5],sizeRange:[1,3],trees:3,wreaths:2,northStars:1,snowmen:2},medium:{count:150,speedRange:[.8,2.5],sizeRange:[1,4],trees:6,wreaths:3,northStars:1,snowmen:3},heavy:{count:300,speedRange:[1,3.5],sizeRange:[1,5],trees:10,wreaths:4,northStars:1,snowmen:4}},particles:["snowflake"],decorations:["tree","wreath","sleigh","robin","train","elf","firework","north-star","snowman","peeing-elf","thieving-elf"],decorationKeys:["trees","wreaths","northStars","snowmen","sleigh","elf","train","firework","robin","peeingElfChance","thievingElfChance"],optInDecorations:{peeingElf:{chance:"peeingElfChance",value:7e-4},thievingElf:{chance:"thievingElfChance",value:.001}},colors:{primary:"#ffffff",secondary:"#228B22",accent:"#c00",gold:"#FFD700"},createSnowflakeParticle(t,n,o){const i=T(o,t,n);return i.type="snowflake",i},createFallingParticle(t,n,o){return this.createSnowflakeParticle(t,n,o)},createTree(t,n,o={}){return{type:"tree",gifts:R.map(i=>({...i,stolen:!1,reserved:!1})),x:o.x!==void 0?o.x:Math.random()*t,y:o.y!==void 0?o.y:Math.random()*n,vx:0,vy:0,size:20+Math.random()*15,opacity:.6+Math.random()*.3,rotation:0,rotationSpeed:0,active:!0,static:!0}},createWreath(t,n,o={}){const i=[];for(let a=0;a<20;a++){const r=a/20*Math.PI*2;i.push({angle:r,radius:.9+Math.random()*.2,thickness:.2+Math.random()*.15,color:a%2===0?"#1a6b1a":"#228B22"})}return{type:"wreath",x:o.x!==void 0?o.x:Math.random()*t,y:o.y!==void 0?o.y:Math.random()*n,vx:0,vy:0,size:15+Math.random()*10,opacity:.7+Math.random()*.2,rotation:0,rotationSpeed:0,active:!0,static:!0,shape:i}},createNorthStar(t,n,o={}){return{type:"north-star",x:o.x!==void 0?o.x:t/2,y:o.y!==void 0?o.y:80,vx:0,vy:0,size:25,opacity:1,twinklePhase:Math.random()*Math.PI*2,twinkleSpeed:.003,active:!0,static:!0}},createSnowman(t,n,o={}){return{type:"snowman",x:o.x!==void 0?o.x:Math.random()*t,y:o.y!==void 0?o.y:n-50,vx:0,vy:0,size:15+Math.random()*10,opacity:1,time:Math.random()*1e3,wavePhase:Math.random()*Math.PI*2,active:!0,static:!0}},createInitialDecorations(t,n,o){const i=[],e=o.trees??6;for(let s=0;s<e;s++)i.push(this.createTree(t,n,{x:t/(e+1)*(s+1),y:n-60-Math.random()*20}));const a=o.wreaths??3;for(let s=0;s<a;s++)i.push(this.createWreath(t,n,{x:t/(a+1)*(s+1),y:50+Math.random()*100}));o.northStars!==0&&o.northStars&&i.push(this.createNorthStar(t,n,{x:t/2,y:60}));const r=o.snowmen??3;for(let s=0;s<r;s++)i.push(this.createSnowman(t,n,{x:t/(r+1)*(s+1),y:n-50-Math.random()*10}));return i},spawnSpecialParticle(t,n,o,i={}){const e=E(t,n,o,i);if(e)return e;const a=Math.random();if(a<5e-4){if(i.sleigh===0||t.some(h=>h.type==="sleigh"))return null;const r=Math.random()<.5,s=r?-100:n+100,f=100+Math.random()*(o*.3);return{type:"sleigh",x:s,startX:s,y:f,baseY:f,targetX:r?n+100:-100,canvasWidth:n,vx:r?3+Math.random()*2:-(3+Math.random()*2),vy:0,arcHeight:150+Math.random()*100,time:0,size:15+Math.random()*10,opacity:.9,rotation:0,active:!0,static:!1}}else if(a<.0013&&i.elf!==0){const r=Math.random()<.5;return{type:"elf",x:r?-50:n+50,y:o-30,baseY:o-30,vx:r?1.5+Math.random()*1:-(1.5+Math.random()*1),waveAmplitude:3,waveFrequency:.05,waveOffset:Math.random()*Math.PI*2,time:0,size:10+Math.random()*5,opacity:.95,rotation:0,active:!0,static:!1}}else if(a<.005&&i.train!==0){if(t.some(d=>d.type==="train"))return null;const r=Math.random()<.5,s=r?-500:n+500,f=21+Math.random()*9,h=f*.72,m=o-h-10;return{type:"train",x:s,y:m,baseY:m,vx:r?4+Math.random()*2:-(4+Math.random()*2),vy:0,size:f,opacity:1,time:0,smoke:[],active:!0,static:!1,carriages:2+Math.floor(Math.random()*2)}}else{if(a<.008&&i.firework!==0)return{type:"firework",x:Math.random()*n,y:o,vx:(Math.random()-.5)*4,vy:-10-Math.random()*5,size:2+Math.random()*2,opacity:1,active:!0,static:!1,time:0,exploded:!1,explosionTime:30+Math.random()*30};if(a<.012&&i.robin!==0){if(t.some(y=>y.type==="robin"))return null;const r=Math.random()<.5,s=Math.random()*(o*.2),f=r?-50:n+50,h=10+Math.random()*5,m=Math.max(h*3,Math.min(o*.6-h*2,o*.2+Math.random()*(o*.4))),d=Math.random()*(n*.6)+n*.2;return{type:"robin",state:"flying_in",x:f,y:s,startX:f,startY:s,targetX:d,targetY:m,vx:r?.5+Math.random()*.5:-(.5+Math.random()*.5),vy:0,size:h,opacity:.95,active:!0,static:!1,sitTime:3e3+Math.random()*2e3,sitStartTime:0,flightProgress:0,time:0,waveOffset:Math.random()*Math.PI*2}}}return null},updateSpecialParticles(t,n,o=1024,i=768){t.forEach(e=>{switch(e.time!==void 0&&(e.time+=n),e.type){case"peeing-elf":case"thieving-elf":I(e,n,o);break;case"sleigh":(e.vx>0&&e.x>e.targetX||e.vx<0&&e.x<e.targetX)&&(e.active=!1);break;case"robin":switch(e.state){case"flying_in":const a=e.targetX-e.x,r=e.targetY-e.y,s=Math.sqrt(a*a+r*r);if(s<20)e.state="sitting",e.vx=0,e.vy=0,e.sitStartTime=e.time;else{const M=a/s,g=r/s,v=3;e.vx=M*v;const u=30,w=.008,k=Math.sin(e.time*w+e.waveOffset)*u,S=Math.cos(e.time*w+e.waveOffset)*u*w;e.vy=g*v*.3+S}break;case"sitting":if(e.vx=0,e.vy=0,e.time-e.sitStartTime>e.sitTime){e.state="flying_away";const M=e.x<o/2?-4:4;e.vx=M,e.flyAwayStartTime=e.time}break;case"flying_away":const f=e.vx>0?1:-1;e.vx=f*4;const h=25,m=.01,d=e.time-e.flyAwayStartTime,y=Math.sin(d*m+e.waveOffset)*h,l=Math.cos(d*m+e.waveOffset)*h*m;e.vy=-1.5+l,(e.x<-50||e.x>o+50||e.y<-50)&&(e.active=!1);break}break;case"train":if(e.smoke||(e.smoke=[]),(e.vx>0&&e.x>o+500||e.vx<0&&e.x<-500)&&(e.active=!1),e.lastSmokeTime||(e.lastSmokeTime=0),e.time-e.lastSmokeTime>150){e.lastSmokeTime=e.time;const r=e.size*1.8/20,s=e.vx>0?1:-1,f=r*8,h=r*7,m=r*10,l=-f-r-h-m*2-r*8,M=r*70,g=r*25,u=(M-g)*.7,w=e.x+u*s,k=e.y+l,S={x:w,y:k,vx:(Math.random()-.5)*.8,vy:-.8-Math.random()*.4,size:8+Math.random()*6,opacity:.7+Math.random()*.2,fadeRate:.012+Math.random()*.008};e.smoke.push(S)}e.smoke=e.smoke.filter(a=>(a.x+=a.vx,a.y+=a.vy,a.vy*=.99,a.size*=1.02,a.opacity-=a.fadeRate,a.opacity>0));break;case"firework":if(!e.exploded){const a=e.y<=e.targetY,r=e.time>=e.explosionTime;(a||r)&&(e.exploded=!0,this.explodeFirework(e,t),e.active=!1)}break}})},drawSnowflake(t,n){t.save(),t.translate(n.x,n.y),t.rotate(n.rotation),t.globalAlpha=n.opacity,t.fillStyle="#ffffff",t.strokeStyle="#ffffff",t.lineWidth=Math.max(n.size*.15,.5);const o=6,i=n.size;for(let e=0;e<o;e++){const a=Math.PI*2*e/o;t.beginPath(),t.moveTo(0,0),t.lineTo(Math.cos(a)*i,Math.sin(a)*i),t.stroke();const r=i*.4,s=Math.PI/6,f=Math.cos(a)*(i*.6),h=Math.sin(a)*(i*.6);t.beginPath(),t.moveTo(f,h),t.lineTo(f+Math.cos(a-s)*r,h+Math.sin(a-s)*r),t.stroke(),t.beginPath(),t.moveTo(f,h),t.lineTo(f+Math.cos(a+s)*r,h+Math.sin(a+s)*r),t.stroke()}t.restore()},drawTree:P,drawWreath:C,drawNorthStar(t,n,o){const i=n.x,e=n.y,a=n.size,r=.8+Math.sin(o*n.twinkleSpeed+n.twinklePhase)*.2;t.save(),t.translate(i,e);const s=a*4*r,f=t.createRadialGradient(0,0,0,0,0,s);f.addColorStop(0,"rgba(220, 230, 240, 0.4)"),f.addColorStop(.4,"rgba(200, 210, 220, 0.2)"),f.addColorStop(1,"rgba(180, 190, 200, 0)"),t.fillStyle=f,t.beginPath(),t.arc(0,0,s,0,Math.PI*2),t.fill();const h=a*1,m=a*1.5,d=a*.12,y=t.createRadialGradient(0,0,0,0,0,a);y.addColorStop(0,"#ffffff"),y.addColorStop(.3,"#f0f0f0"),y.addColorStop(.6,"#c0c0c0"),y.addColorStop(1,"#a0a0a0"),t.shadowColor="rgba(220, 230, 240, 0.9)",t.shadowBlur=a*.8*r,t.fillStyle=y,t.beginPath(),t.moveTo(0,-m),t.lineTo(d,-d),t.lineTo(h,0),t.lineTo(d,d),t.lineTo(0,m),t.lineTo(-d,d),t.lineTo(-h,0),t.lineTo(-d,-d),t.closePath(),t.fill(),t.shadowBlur=a*1.2*r,t.shadowColor="rgba(255, 255, 255, 1)";const l=t.createRadialGradient(0,0,0,0,0,a*.3);l.addColorStop(0,"#ffffff"),l.addColorStop(.6,"#f5f5f5"),l.addColorStop(1,"rgba(200, 200, 200, 0.8)"),t.fillStyle=l,t.beginPath(),t.arc(0,0,a*.3,0,Math.PI*2),t.fill(),t.shadowBlur=0,t.restore()},drawSleigh:c,drawElf:b,drawPeeingElf:b,drawThievingElf:b,drawTrain:z,drawFirework(t,n){t.fillStyle=n.color||"#ffffff",t.beginPath(),t.arc(n.x,n.y,n.size,0,Math.PI*2),t.fill()},explodeFirework(t,n){const o=50+Math.random()*50,i=["#ff0000","#ffff00","#00ff00","#0000ff","#ffffff"];for(let e=0;e<o;e++){const a=Math.random()*Math.PI*2,r=Math.random()*5+2;n.push({type:"spark",x:t.x,y:t.y,vx:Math.cos(a)*r,vy:Math.sin(a)*r,size:2+Math.random()*2,opacity:1,active:!0,static:!1,color:i[Math.floor(Math.random()*i.length)]})}},drawRobin:x,drawSnowman:Y};
@@ -1 +1 @@
1
- (function(){"use strict";fetch("/api/effects/settings").then(function(y){return y.json()}).then(function(y){var z=y.respectMotion!==!1,A=y.defaultDuration||600,S=y.defaultAnimation||"fade",E=y.defaultThreshold!=null?y.defaultThreshold:.1,p=z&&window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches,d=document.querySelector(".page-body");if(!d)return;function r(t,e){return t.getAttribute("data-fx-"+e)}function i(t,e,n){var a=r(t,e);return a!==null?parseFloat(a):n}function v(t,e,n){var a=r(t,e);return a===null?n:a==="true"||a==="1"}function b(t,e,n){var a=new IntersectionObserver(function(o){o[0].isIntersecting&&(a.unobserve(t),n())},{threshold:e});a.observe(t)}d.querySelectorAll(".dm-fx-reveal").forEach(function(t){var e=r(t,"animation")||S,n=i(t,"duration",A),a=i(t,"delay",0),o=i(t,"threshold",E),f=v(t,"once",!0);p||(t.style.opacity="0",t.style.transition="none",e==="slide-up"?t.style.transform="translateY(30px)":e==="slide-down"?t.style.transform="translateY(-30px)":e==="zoom"?t.style.transform="scale(0.85)":e==="flip"&&(t.style.transform="rotateY(90deg)"),requestAnimationFrame(function(){requestAnimationFrame(function(){t.style.transition="opacity "+n+"ms ease "+a+"ms, transform "+n+"ms ease "+a+"ms";var s=new IntersectionObserver(function(u){u.forEach(function(h){h.isIntersecting?(t.style.opacity="1",t.style.transform="",f&&s.unobserve(t)):f||(t.style.opacity="0",e==="slide-up"?t.style.transform="translateY(30px)":e==="slide-down"?t.style.transform="translateY(-30px)":e==="zoom"?t.style.transform="scale(0.85)":e==="flip"?t.style.transform="rotateY(90deg)":t.style.transform="")})},{threshold:o});s.observe(t)})}))}),d.querySelectorAll(".dm-fx-counter").forEach(function(t){var e=i(t,"to",0),n=i(t,"from",0),a=i(t,"duration",2e3),o=r(t,"prefix")||"",f=r(t,"suffix")||"",s=i(t,"decimals",0),u=r(t,"separator")||"";function h(m){var l=m.toFixed(s);if(u){var x=l.split(".");x[0]=x[0].replace(/\B(?=(\d{3})+(?!\d))/g,u),l=x.join(".")}return o+l+f}if(p){t.textContent=h(e);return}b(t,.5,function(){var m=null;requestAnimationFrame(function l(x){m||(m=x);var w=Math.min((x-m)/a,1),q=1-Math.pow(1-w,3);t.textContent=h(n+(e-n)*q),w<1&&requestAnimationFrame(l)})})}),p||d.querySelectorAll(".dm-fx-scramble").forEach(function(t){var e=i(t,"speed",50),n=t.textContent,a="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%&",o=0;b(t,.3,function(){var f=setInterval(function(){for(var s="",u=0;u<n.length;u++)u<o||n[u]===" "?s+=n[u]:s+=a[Math.floor(Math.random()*a.length)];t.textContent=s,o<n.length?o++:clearInterval(f)},e)})}),p||d.querySelectorAll(".dm-fx-shake").forEach(function(t){var e=i(t,"intensity",5),n=i(t,"duration",500),a=r(t,"direction")||"horizontal",o=a==="vertical"?"translateY":"translateX";t.animate([{transform:o+"(0)"},{transform:o+"(-"+e+"px)"},{transform:o+"("+e+"px)"},{transform:o+"(-"+e+"px)"},{transform:o+"("+e+"px)"},{transform:o+"(0)"}],{duration:n,easing:"ease-in-out"})}),d.querySelectorAll(".dm-fx-ripple").forEach(function(t){t.style.position="relative",t.style.overflow="hidden",t.addEventListener("click",function(e){var n=r(t,"colour")||"rgba(255,255,255,0.3)",a=i(t,"duration",600),o=t.getBoundingClientRect(),f=Math.max(o.width,o.height)*2,s=document.createElement("span");s.style.cssText="position:absolute;border-radius:50%;pointer-events:none;width:"+f+"px;height:"+f+"px;left:"+(e.clientX-o.left-f/2)+"px;top:"+(e.clientY-o.top-f/2)+"px;background:"+n+";transform:scale(0);opacity:1;transition:transform "+a+"ms ease,opacity "+a+"ms ease;",t.appendChild(s),requestAnimationFrame(function(){s.style.transform="scale(1)",s.style.opacity="0"}),setTimeout(function(){s.remove()},a)})});var c=window.Domma&&window.Domma.effects;c&&(d.querySelectorAll(".dm-fx-breathe").forEach(function(t){if(!p){var e={};r(t,"amplitude")&&(e.amplitude=i(t,"amplitude",6)),r(t,"duration")&&(e.duration=i(t,"duration",3e3)),r(t,"easing")&&(e.easing=r(t,"easing")),r(t,"stagger")&&(e.stagger=i(t,"stagger",0)),c.breathe(t,e)}}),d.querySelectorAll(".dm-fx-pulse").forEach(function(t){if(!p){var e={};r(t,"scale")&&(e.scale=i(t,"scale",1.05)),r(t,"duration")&&(e.duration=i(t,"duration",2e3)),r(t,"easing")&&(e.easing=r(t,"easing")),c.pulse(t,e)}}),d.querySelectorAll(".dm-fx-scribe").forEach(function(t){var e={};r(t,"speed")&&(e.speed=i(t,"speed",50)),r(t,"delete-speed")&&(e.deleteSpeed=i(t,"delete-speed",30)),r(t,"cursor")&&(e.cursor=v(t,"cursor",!0)),r(t,"cursor-char")&&(e.cursorChar=r(t,"cursor-char")),r(t,"cursor-type")&&(e.cursorType=r(t,"cursor-type")),r(t,"mode")&&(e.mode=r(t,"mode")),r(t,"loop")&&(e.loop=v(t,"loop",!1)),r(t,"loop-delay")&&(e.loopDelay=i(t,"loop-delay",1e3)),r(t,"pause-on-hover")&&(e.pauseOnHover=v(t,"pause-on-hover",!1));var n=r(t,"actions");if(n)try{e.actions=JSON.parse(n)}catch{return}else{var a=t.textContent.trim();if(!a)return;t.textContent="",e.actions=[{render:a}]}c.scribe(t,e)}),d.querySelectorAll(".dm-fx-twinkle").forEach(function(t){var e={};r(t,"count")&&(e.count=i(t,"count",100)),r(t,"shape")&&(e.shape=r(t,"shape")),r(t,"colour")&&(e.colour=r(t,"colour")),r(t,"min-size")&&(e.minSize=i(t,"min-size",1)),r(t,"max-size")&&(e.maxSize=i(t,"max-size",3)),c.twinkle(t,e)}),typeof c.tickerTape=="function"&&d.querySelectorAll(".dm-fx-ticker-tape").forEach(function(t){if(!p){var e={};r(t,"palette")&&(e.palette=r(t,"palette")),r(t,"density")&&(e.density=i(t,"density",50)),r(t,"speed")&&(e.speed=i(t,"speed",1)),r(t,"sway")&&(e.sway=i(t,"sway",60)),r(t,"rotation-speed")&&(e.rotationSpeed=i(t,"rotation-speed",6)),r(t,"min-width")&&(e.minWidth=i(t,"min-width",5)),r(t,"max-width")&&(e.maxWidth=i(t,"max-width",9)),r(t,"min-height")&&(e.minHeight=i(t,"min-height",12)),r(t,"max-height")&&(e.maxHeight=i(t,"max-height",22)),r(t,"fade-start")&&(e.fadeStart=i(t,"fade-start",.55)),r(t,"burst")&&(e.burst=v(t,"burst",!1)),r(t,"burst-count")&&(e.burstCount=i(t,"burst-count",150)),r(t,"z-index")&&(e.zIndex=i(t,"z-index",1));var n=r(t,"mode");n==="page"?c.tickerTape(null,e):c.tickerTape(t,e)}}),typeof c.butterflies=="function"&&d.querySelectorAll(".dm-fx-butterflies").forEach(function(t){if(!p){var e={};r(t,"palette")&&(e.palette=r(t,"palette")),r(t,"density")&&(e.density=i(t,"density",18)),r(t,"speed")&&(e.speed=i(t,"speed",1)),r(t,"wander")&&(e.wander=i(t,"wander",1)),r(t,"rise-speed")&&(e.riseSpeed=i(t,"rise-speed",.4)),r(t,"flap-speed")&&(e.flapSpeed=i(t,"flap-speed",1)),r(t,"min-size")&&(e.minSize=i(t,"min-size",14)),r(t,"max-size")&&(e.maxSize=i(t,"max-size",26)),r(t,"two-tone")&&(e.twoTone=v(t,"two-tone",!0)),r(t,"burst")&&(e.burst=v(t,"burst",!1)),r(t,"burst-count")&&(e.burstCount=i(t,"burst-count",40)),r(t,"z-index")&&(e.zIndex=i(t,"z-index",1));var n=r(t,"mode");n==="page"?c.butterflies(null,e):c.butterflies(t,e)}}),typeof c.strobe=="function"&&d.querySelectorAll(".dm-fx-strobe").forEach(function(t){if(p)return;function e(s){var u=r(t,s);if(!u)return null;var h=u.split(",").map(function(m){return m.trim()}).filter(Boolean);return h.length?h:null}var n={};r(t,"preset")&&(n.preset=r(t,"preset")),r(t,"motion")&&(n.motion=r(t,"motion")),r(t,"speed")&&(n.speed=i(t,"speed",1)),r(t,"sweep-arc")&&(n.sweepArc=i(t,"sweep-arc",100)),r(t,"beam-width")&&(n.beamWidth=i(t,"beam-width",16)),r(t,"intensity")&&(n.intensity=i(t,"intensity",.7)),r(t,"flicker")&&(n.flicker=v(t,"flicker",!0)),r(t,"hz")&&(n.hz=i(t,"hz",10));var a=e("origins");a&&(n.origins=a);var o=e("colours");o&&(n.colours=o),r(t,"duration")&&(n.duration=i(t,"duration",null)),r(t,"z-index")&&(n.zIndex=i(t,"z-index",9999));var f=r(t,"mode");f==="page"?c.strobe(null,n):c.strobe(t,n)}));var g=d.querySelectorAll(".dm-fx-celebrate");g.length&&!p&&import("/public/js/celebrations/index.js?v=20260930-festive").then(function(t){var e=t.CelebrationsEffect;g.forEach(function(n){var a=n.getAttribute("data-fx-theme")||"auto",o=n.getAttribute("data-fx-intensity")||"medium",f=n.getAttribute("data-fx-z-index")?parseInt(n.getAttribute("data-fx-z-index"),10):999;if(!(a==="auto"&&(a=e.getCurrentTheme(),!a))){var s={},u=["data-fx-theme","data-fx-intensity","data-fx-z-index"];Array.from(n.attributes).forEach(function(m){m.name.startsWith("data-fx-")&&!u.includes(m.name)&&(s[m.name.slice(8)]=m.value)});var h=new e({theme:a,intensity:o,enabled:!0,zIndex:f,overrides:s});h.init().then(function(){h.start()})}})}).catch(function(){})}).catch(function(){})})();
1
+ (function(){"use strict";fetch("/api/effects/settings").then(function(y){return y.json()}).then(function(y){var z=y.respectMotion!==!1,A=y.defaultDuration||600,S=y.defaultAnimation||"fade",E=y.defaultThreshold!=null?y.defaultThreshold:.1,p=z&&window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches,d=document.querySelector(".page-body");if(!d)return;function r(t,e){return t.getAttribute("data-fx-"+e)}function i(t,e,n){var a=r(t,e);return a!==null?parseFloat(a):n}function v(t,e,n){var a=r(t,e);return a===null?n:a==="true"||a==="1"}function b(t,e,n){var a=new IntersectionObserver(function(o){o[0].isIntersecting&&(a.unobserve(t),n())},{threshold:e});a.observe(t)}d.querySelectorAll(".dm-fx-reveal").forEach(function(t){var e=r(t,"animation")||S,n=i(t,"duration",A),a=i(t,"delay",0),o=i(t,"threshold",E),f=v(t,"once",!0);p||(t.style.opacity="0",t.style.transition="none",e==="slide-up"?t.style.transform="translateY(30px)":e==="slide-down"?t.style.transform="translateY(-30px)":e==="zoom"?t.style.transform="scale(0.85)":e==="flip"&&(t.style.transform="rotateY(90deg)"),requestAnimationFrame(function(){requestAnimationFrame(function(){t.style.transition="opacity "+n+"ms ease "+a+"ms, transform "+n+"ms ease "+a+"ms";var s=new IntersectionObserver(function(u){u.forEach(function(h){h.isIntersecting?(t.style.opacity="1",t.style.transform="",f&&s.unobserve(t)):f||(t.style.opacity="0",e==="slide-up"?t.style.transform="translateY(30px)":e==="slide-down"?t.style.transform="translateY(-30px)":e==="zoom"?t.style.transform="scale(0.85)":e==="flip"?t.style.transform="rotateY(90deg)":t.style.transform="")})},{threshold:o});s.observe(t)})}))}),d.querySelectorAll(".dm-fx-counter").forEach(function(t){var e=i(t,"to",0),n=i(t,"from",0),a=i(t,"duration",2e3),o=r(t,"prefix")||"",f=r(t,"suffix")||"",s=i(t,"decimals",0),u=r(t,"separator")||"";function h(m){var l=m.toFixed(s);if(u){var x=l.split(".");x[0]=x[0].replace(/\B(?=(\d{3})+(?!\d))/g,u),l=x.join(".")}return o+l+f}if(p){t.textContent=h(e);return}b(t,.5,function(){var m=null;requestAnimationFrame(function l(x){m||(m=x);var w=Math.min((x-m)/a,1),q=1-Math.pow(1-w,3);t.textContent=h(n+(e-n)*q),w<1&&requestAnimationFrame(l)})})}),p||d.querySelectorAll(".dm-fx-scramble").forEach(function(t){var e=i(t,"speed",50),n=t.textContent,a="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%&",o=0;b(t,.3,function(){var f=setInterval(function(){for(var s="",u=0;u<n.length;u++)u<o||n[u]===" "?s+=n[u]:s+=a[Math.floor(Math.random()*a.length)];t.textContent=s,o<n.length?o++:clearInterval(f)},e)})}),p||d.querySelectorAll(".dm-fx-shake").forEach(function(t){var e=i(t,"intensity",5),n=i(t,"duration",500),a=r(t,"direction")||"horizontal",o=a==="vertical"?"translateY":"translateX";t.animate([{transform:o+"(0)"},{transform:o+"(-"+e+"px)"},{transform:o+"("+e+"px)"},{transform:o+"(-"+e+"px)"},{transform:o+"("+e+"px)"},{transform:o+"(0)"}],{duration:n,easing:"ease-in-out"})}),d.querySelectorAll(".dm-fx-ripple").forEach(function(t){t.style.position="relative",t.style.overflow="hidden",t.addEventListener("click",function(e){var n=r(t,"colour")||"rgba(255,255,255,0.3)",a=i(t,"duration",600),o=t.getBoundingClientRect(),f=Math.max(o.width,o.height)*2,s=document.createElement("span");s.style.cssText="position:absolute;border-radius:50%;pointer-events:none;width:"+f+"px;height:"+f+"px;left:"+(e.clientX-o.left-f/2)+"px;top:"+(e.clientY-o.top-f/2)+"px;background:"+n+";transform:scale(0);opacity:1;transition:transform "+a+"ms ease,opacity "+a+"ms ease;",t.appendChild(s),requestAnimationFrame(function(){s.style.transform="scale(1)",s.style.opacity="0"}),setTimeout(function(){s.remove()},a)})});var c=window.Domma&&window.Domma.effects;c&&(d.querySelectorAll(".dm-fx-breathe").forEach(function(t){if(!p){var e={};r(t,"amplitude")&&(e.amplitude=i(t,"amplitude",6)),r(t,"duration")&&(e.duration=i(t,"duration",3e3)),r(t,"easing")&&(e.easing=r(t,"easing")),r(t,"stagger")&&(e.stagger=i(t,"stagger",0)),c.breathe(t,e)}}),d.querySelectorAll(".dm-fx-pulse").forEach(function(t){if(!p){var e={};r(t,"scale")&&(e.scale=i(t,"scale",1.05)),r(t,"duration")&&(e.duration=i(t,"duration",2e3)),r(t,"easing")&&(e.easing=r(t,"easing")),c.pulse(t,e)}}),d.querySelectorAll(".dm-fx-scribe").forEach(function(t){var e={};r(t,"speed")&&(e.speed=i(t,"speed",50)),r(t,"delete-speed")&&(e.deleteSpeed=i(t,"delete-speed",30)),r(t,"cursor")&&(e.cursor=v(t,"cursor",!0)),r(t,"cursor-char")&&(e.cursorChar=r(t,"cursor-char")),r(t,"cursor-type")&&(e.cursorType=r(t,"cursor-type")),r(t,"mode")&&(e.mode=r(t,"mode")),r(t,"loop")&&(e.loop=v(t,"loop",!1)),r(t,"loop-delay")&&(e.loopDelay=i(t,"loop-delay",1e3)),r(t,"pause-on-hover")&&(e.pauseOnHover=v(t,"pause-on-hover",!1));var n=r(t,"actions");if(n)try{e.actions=JSON.parse(n)}catch{return}else{var a=t.textContent.trim();if(!a)return;t.textContent="",e.actions=[{render:a}]}c.scribe(t,e)}),d.querySelectorAll(".dm-fx-twinkle").forEach(function(t){var e={};r(t,"count")&&(e.count=i(t,"count",100)),r(t,"shape")&&(e.shape=r(t,"shape")),r(t,"colour")&&(e.colour=r(t,"colour")),r(t,"min-size")&&(e.minSize=i(t,"min-size",1)),r(t,"max-size")&&(e.maxSize=i(t,"max-size",3)),c.twinkle(t,e)}),typeof c.tickerTape=="function"&&d.querySelectorAll(".dm-fx-ticker-tape").forEach(function(t){if(!p){var e={};r(t,"palette")&&(e.palette=r(t,"palette")),r(t,"density")&&(e.density=i(t,"density",50)),r(t,"speed")&&(e.speed=i(t,"speed",1)),r(t,"sway")&&(e.sway=i(t,"sway",60)),r(t,"rotation-speed")&&(e.rotationSpeed=i(t,"rotation-speed",6)),r(t,"min-width")&&(e.minWidth=i(t,"min-width",5)),r(t,"max-width")&&(e.maxWidth=i(t,"max-width",9)),r(t,"min-height")&&(e.minHeight=i(t,"min-height",12)),r(t,"max-height")&&(e.maxHeight=i(t,"max-height",22)),r(t,"fade-start")&&(e.fadeStart=i(t,"fade-start",.55)),r(t,"burst")&&(e.burst=v(t,"burst",!1)),r(t,"burst-count")&&(e.burstCount=i(t,"burst-count",150)),r(t,"z-index")&&(e.zIndex=i(t,"z-index",1));var n=r(t,"mode");n==="page"?c.tickerTape(null,e):c.tickerTape(t,e)}}),typeof c.butterflies=="function"&&d.querySelectorAll(".dm-fx-butterflies").forEach(function(t){if(!p){var e={};r(t,"palette")&&(e.palette=r(t,"palette")),r(t,"density")&&(e.density=i(t,"density",18)),r(t,"speed")&&(e.speed=i(t,"speed",1)),r(t,"wander")&&(e.wander=i(t,"wander",1)),r(t,"rise-speed")&&(e.riseSpeed=i(t,"rise-speed",.4)),r(t,"flap-speed")&&(e.flapSpeed=i(t,"flap-speed",1)),r(t,"min-size")&&(e.minSize=i(t,"min-size",14)),r(t,"max-size")&&(e.maxSize=i(t,"max-size",26)),r(t,"two-tone")&&(e.twoTone=v(t,"two-tone",!0)),r(t,"burst")&&(e.burst=v(t,"burst",!1)),r(t,"burst-count")&&(e.burstCount=i(t,"burst-count",40)),r(t,"z-index")&&(e.zIndex=i(t,"z-index",1));var n=r(t,"mode");n==="page"?c.butterflies(null,e):c.butterflies(t,e)}}),typeof c.strobe=="function"&&d.querySelectorAll(".dm-fx-strobe").forEach(function(t){if(p)return;function e(s){var u=r(t,s);if(!u)return null;var h=u.split(",").map(function(m){return m.trim()}).filter(Boolean);return h.length?h:null}var n={};r(t,"preset")&&(n.preset=r(t,"preset")),r(t,"motion")&&(n.motion=r(t,"motion")),r(t,"speed")&&(n.speed=i(t,"speed",1)),r(t,"sweep-arc")&&(n.sweepArc=i(t,"sweep-arc",100)),r(t,"beam-width")&&(n.beamWidth=i(t,"beam-width",16)),r(t,"intensity")&&(n.intensity=i(t,"intensity",.7)),r(t,"flicker")&&(n.flicker=v(t,"flicker",!0)),r(t,"hz")&&(n.hz=i(t,"hz",10));var a=e("origins");a&&(n.origins=a);var o=e("colours");o&&(n.colours=o),r(t,"duration")&&(n.duration=i(t,"duration",null)),r(t,"z-index")&&(n.zIndex=i(t,"z-index",9999));var f=r(t,"mode");f==="page"?c.strobe(null,n):c.strobe(t,n)}));var g=d.querySelectorAll(".dm-fx-celebrate");g.length&&!p&&import("/public/js/celebrations/index.js?v=20260930-snowman").then(function(t){var e=t.CelebrationsEffect;g.forEach(function(n){var a=n.getAttribute("data-fx-theme")||"auto",o=n.getAttribute("data-fx-intensity")||"medium",f=n.getAttribute("data-fx-z-index")?parseInt(n.getAttribute("data-fx-z-index"),10):999;if(!(a==="auto"&&(a=e.getCurrentTheme(),!a))){var s={},u=["data-fx-theme","data-fx-intensity","data-fx-z-index"];Array.from(n.attributes).forEach(function(m){m.name.startsWith("data-fx-")&&!u.includes(m.name)&&(s[m.name.slice(8)]=m.value)});var h=new e({theme:a,intensity:o,enabled:!0,zIndex:f,overrides:s});h.init().then(function(){h.start()})}})}).catch(function(){})}).catch(function(){})})();