@whykusanagi/corrupted-theme 0.1.6 → 0.1.8

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.
@@ -31,6 +31,34 @@
31
31
  localStorage.setItem("corruptionLoadingLastPlayed", Date.now().toString());
32
32
  }
33
33
 
34
+ // Inline timer tracking (IIFE can't import ES modules)
35
+ const _timers = { _t: new Set(), _i: new Set() };
36
+ function _setTimeout(fn, delay) {
37
+ const id = setTimeout(() => { _timers._t.delete(id); fn(); }, delay);
38
+ _timers._t.add(id);
39
+ return id;
40
+ }
41
+ function _setInterval(fn, delay) {
42
+ const id = setInterval(fn, delay);
43
+ _timers._i.add(id);
44
+ return id;
45
+ }
46
+ function _clearAllTimers() {
47
+ _timers._t.forEach(id => clearTimeout(id));
48
+ _timers._i.forEach(id => clearInterval(id));
49
+ _timers._t.clear();
50
+ _timers._i.clear();
51
+ }
52
+
53
+ // Cancel loading screen early
54
+ function cancelLoading() {
55
+ _clearAllTimers();
56
+ const screen = document.getElementById('corruption-loading');
57
+ if (screen) screen.remove();
58
+ const styles = document.getElementById('corruption-loading-styles');
59
+ if (styles) styles.remove();
60
+ }
61
+
34
62
  // Main function to show loading screen
35
63
  function showCorruptionLoading(options = {}) {
36
64
  const config = {
@@ -51,7 +79,7 @@
51
79
  // Inject styles
52
80
  const style = document.createElement("style");
53
81
  style.id = "corruption-loading-styles";
54
- style.innerHTML = `
82
+ style.textContent = `
55
83
  @keyframes flicker {
56
84
  0%, 100% { opacity: 1; }
57
85
  50% { opacity: 0.4; }
@@ -235,6 +263,7 @@
235
263
  const loadingScreen = document.createElement("div");
236
264
  loadingScreen.id = "corruption-loading-screen";
237
265
 
266
+ // Static HTML only — no interpolated variables, safe from XSS
238
267
  loadingScreen.innerHTML = `
239
268
  <div class="corrupt-stream"></div>
240
269
  <div class="crt-overlay"></div>
@@ -306,8 +335,8 @@
306
335
 
307
336
  target.innerHTML = '';
308
337
  let i = 0;
309
- const interval = setInterval(() => {
310
- if (i >= text.length) return clearInterval(interval);
338
+ const interval = _setInterval(() => {
339
+ if (i >= text.length) { clearInterval(interval); _timers._i.delete(interval); return; }
311
340
  const span = document.createElement('span');
312
341
  span.className = 'glyph';
313
342
  span.textContent = text[i];
@@ -349,16 +378,16 @@
349
378
  });
350
379
 
351
380
  // Animate progress text
352
- setTimeout(() => {
381
+ _setTimeout(() => {
353
382
  [...progressText.children].forEach((span, idx) => {
354
- setTimeout(() => {
383
+ _setTimeout(() => {
355
384
  span.textContent = phrase.replace(/\s/g, '').charAt(idx);
356
385
  }, 500 * (idx + 1));
357
386
  });
358
387
  }, 3600);
359
388
 
360
389
  // Type grow text
361
- setTimeout(() => {
390
+ _setTimeout(() => {
362
391
  const growText = document.getElementById("grow-text");
363
392
  if (growText) {
364
393
  typeGlyphText("grow-text", "Initializing corruption protocols...", 80);
@@ -366,9 +395,10 @@
366
395
  }, 2000);
367
396
 
368
397
  // Remove loading screen
369
- setTimeout(() => {
398
+ _setTimeout(() => {
370
399
  loadingScreen.style.opacity = "0";
371
- setTimeout(() => {
400
+ _setTimeout(() => {
401
+ _clearAllTimers();
372
402
  loadingScreen.remove();
373
403
  const styles = document.getElementById("corruption-loading-styles");
374
404
  if (styles) styles.remove();
@@ -394,12 +424,12 @@
394
424
 
395
425
  // Export for manual use
396
426
  window.showCorruptionLoading = showCorruptionLoading;
397
- window.CorruptionLoading = { show: showCorruptionLoading };
427
+ window.CorruptionLoading = { show: showCorruptionLoading, cancel: cancelLoading };
398
428
  }
399
429
 
400
430
  // Export for modules
401
431
  if (typeof module !== 'undefined' && module.exports) {
402
- module.exports = { showCorruptionLoading };
432
+ module.exports = { showCorruptionLoading, cancelLoading };
403
433
  }
404
434
  })();
405
435
 
@@ -134,6 +134,8 @@ let state = {
134
134
  config: null,
135
135
  countdownInterval: null,
136
136
  popupInterval: null,
137
+ popupInitTimeout: null,
138
+ popupDurationTimeout: null,
137
139
  isCompleted: false
138
140
  };
139
141
 
@@ -314,7 +316,7 @@ function renderWidget(config) {
314
316
  if (config.popup?.message) {
315
317
  popup = document.createElement('div');
316
318
  popup.className = 'countdown-popup';
317
- popup.innerHTML = config.popup.message;
319
+ popup.textContent = config.popup.message;
318
320
 
319
321
  if (config.popup.colors) {
320
322
  if (config.popup.colors.bg) popup.style.background = config.popup.colors.bg;
@@ -460,7 +462,8 @@ function startPopup(popupConfig, popupElement) {
460
462
  const duration = popupConfig.duration || 5000;
461
463
 
462
464
  // Show popup initially after a delay
463
- setTimeout(() => {
465
+ state.popupInitTimeout = setTimeout(() => {
466
+ state.popupInitTimeout = null;
464
467
  showPopup(popupElement, duration);
465
468
  }, 2000);
466
469
 
@@ -478,8 +481,14 @@ function startPopup(popupConfig, popupElement) {
478
481
  */
479
482
  function showPopup(popup, duration) {
480
483
  popup.classList.add('active');
481
-
482
- setTimeout(() => {
484
+
485
+ // Clear previous duration timeout if popup is re-shown before it hides
486
+ if (state.popupDurationTimeout) {
487
+ clearTimeout(state.popupDurationTimeout);
488
+ }
489
+
490
+ state.popupDurationTimeout = setTimeout(() => {
491
+ state.popupDurationTimeout = null;
483
492
  popup.classList.remove('active');
484
493
  }, duration);
485
494
  }
@@ -584,12 +593,22 @@ export function destroyCountdown() {
584
593
  clearInterval(state.countdownInterval);
585
594
  state.countdownInterval = null;
586
595
  }
587
-
596
+
588
597
  if (state.popupInterval) {
589
598
  clearInterval(state.popupInterval);
590
599
  state.popupInterval = null;
591
600
  }
592
-
601
+
602
+ if (state.popupInitTimeout) {
603
+ clearTimeout(state.popupInitTimeout);
604
+ state.popupInitTimeout = null;
605
+ }
606
+
607
+ if (state.popupDurationTimeout) {
608
+ clearTimeout(state.popupDurationTimeout);
609
+ state.popupDurationTimeout = null;
610
+ }
611
+
593
612
  state.config = null;
594
613
  state.isCompleted = false;
595
614