@glitchr/transparent 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glitchr/transparent",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Transparent SPA Application",
5
5
  "main": "src/index.js",
6
6
  "access": "public",
@@ -500,8 +500,14 @@ jQuery.event.special.mousewheel = { setup: function( _, ns, handle ) { this.addE
500
500
  if (!array.includes(uuid)) {
501
501
 
502
502
  array.push(uuid);
503
+ // Enforce the LRU cap. NB: entries are stored under
504
+ // `transparent[response][<uuid>]` / `transparent[position][<uuid>]`,
505
+ // so eviction must remove THOSE keys — the previous code removed
506
+ // `transparent[<uuid>]`, which never existed, leaving the real
507
+ // response/position blobs orphaned. They then accumulated past the
508
+ // cap until QuotaExceededError forced a full sessionStorage.clear().
503
509
  while(array.length > Settings["response_limit"])
504
- sessionStorage.removeItem('transparent['+array.shift()+']');
510
+ removeResponseEntry(array.shift());
505
511
  }
506
512
 
507
513
  try {
@@ -515,15 +521,42 @@ jQuery.event.special.mousewheel = { setup: function( _, ns, handle ) { this.addE
515
521
 
516
522
  } catch(e) {
517
523
 
524
+ // On quota, evict the oldest cached pages (targeted) and retry
525
+ // once, instead of nuking ALL sessionStorage — sessionStorage.clear()
526
+ // also wipes unrelated app state and the entire page cache.
527
+ if (e.name === 'QuotaExceededError' && exceptionRaised === false) {
528
+ evictOldestResponses(Math.max(1, Math.ceil(array.length / 2)));
529
+ return Transparent.setResponse(uuid, responseText, scrollableXY, true);
530
+ }
531
+ // Last resort if a single page is itself too big to ever fit.
518
532
  if (e.name === 'QuotaExceededError')
519
533
  sessionStorage.clear();
520
534
 
521
- return exceptionRaised === false ? Transparent.setResponse(uuid, responseText, scrollableXY, true) : this;
535
+ return this;
522
536
  }
523
537
 
524
538
  return this;
525
539
  }
526
540
 
541
+ // Remove both blobs for a cached page uuid (response HTML + scroll position).
542
+ function removeResponseEntry(uuid) {
543
+ try {
544
+ sessionStorage.removeItem('transparent[response]['+uuid+']');
545
+ sessionStorage.removeItem('transparent[position]['+uuid+']');
546
+ } catch (e) {}
547
+ }
548
+
549
+ // Drop the N oldest cached pages and rewrite the index. Used to recover
550
+ // from a QuotaExceededError without discarding the whole cache.
551
+ function evictOldestResponses(count) {
552
+ try {
553
+ var array = JSON.parse(sessionStorage.getItem('transparent')) || [];
554
+ for (var i = 0; i < count && array.length; i++)
555
+ removeResponseEntry(array.shift());
556
+ sessionStorage.setItem('transparent', JSON.stringify(array));
557
+ } catch (e) {}
558
+ }
559
+
527
560
  Transparent.setResponseText = function(uuid, responseText, exceptionRaised = false)
528
561
  {
529
562
  if(isDomEntity(responseText))
@@ -1771,6 +1804,14 @@ jQuery.event.special.mousewheel = { setup: function( _, ns, handle ) { this.addE
1771
1804
  // double-prompt. The existing `formSubmission` flag already handles
1772
1805
  // the synchronous prompt path; this clears state for any follow-up.
1773
1806
  document.addEventListener('submit', function() { formDirty = false; }, true);
1807
+ // Reset on every navigation. transparent.js re-dispatches DOMContentLoaded
1808
+ // after each SPA swap (see _doSwap, ~line 1567), so this fires on the
1809
+ // initial load AND on each in-place navigation. Without it the flag leaks
1810
+ // across SPA navigations: typing on page A, then navigating to a form
1811
+ // page B, would leave formDirty=true and wrongly prompt on reload of B
1812
+ // even though the user never touched B. A freshly-loaded page is never
1813
+ // dirty until the user types on it (any restored draft is already saved).
1814
+ document.addEventListener('DOMContentLoaded', function() { formDirty = false; });
1774
1815
 
1775
1816
  // ── Transparent.formMemory ──────────────────────────────────────────────
1776
1817
  //
@@ -2394,9 +2435,26 @@ jQuery.event.special.mousewheel = { setup: function( _, ns, handle ) { this.addE
2394
2435
  window.onpopstate = __main__; // Onpopstate pop out straight to previous page.. this creates a jump while changing pages with hash..
2395
2436
  window.onhashchange = __main__;
2396
2437
 
2397
- // onbeforeunload: confirm only if the user has actually typed/edited
2398
- // a form input on this page. Pre-Turbo this used a snapshot-and-
2399
- // compare approach (formDataBefore vs formDataAfter) which gave
2438
+ // onbeforeunload confirmation REMOVED. It produced spurious "are you
2439
+ // sure you want to leave?" blocks on any page with a form even when the
2440
+ // user never typed: the dirty flag was flipped by any trusted change
2441
+ // event (a <select>/checkbox toggle, Select2/datepicker init firing a
2442
+ // real change, browser autofill, …), and the `e.currentTarget == window`
2443
+ // guard below is unreliable across browsers. More importantly it's now
2444
+ // obsolete: Transparent.formMemory persists every form's content to
2445
+ // localStorage (debounced on input + saved synchronously on unload) and
2446
+ // restores it on the next load, so reloading or closing the tab never
2447
+ // loses typed input. The draft save on beforeunload (in the formMemory
2448
+ // IIFE above) is kept; only the blocking confirmation is gone.
2449
+ var __onbeforeunload_disabled = function(e) {
2450
+ if(Settings.debug) console.log("Transparent onbeforeunload (no-op; drafts auto-saved)");
2451
+ if(formSubmission) return;
2452
+ if(Settings.disable) return;
2453
+ // No return value → browser never shows the leave/reload confirmation.
2454
+ };
2455
+
2456
+ // Legacy snapshot-and-compare confirm (formDataBefore vs formDataAfter)
2457
+ // gave
2400
2458
  // false positives because JS init code mutates form values after
2401
2459
  // load — Select2 writes selected text to hidden fields, Editor.js
2402
2460
  // serializes its JSON to a <textarea>, datepicker normalizes
@@ -2404,24 +2462,11 @@ jQuery.event.special.mousewheel = { setup: function( _, ns, handle ) { this.addE
2404
2462
  // and the current state always differed, and the browser always
2405
2463
  // prompted even on read-only pages.
2406
2464
  //
2407
- // The replacement is the `formDirty` flag declared above, which is
2408
- // set only by trusted (user-originated) `input`/`change` events.
2409
- // No prompt unless the user genuinely typed something.
2410
- window.onbeforeunload = function(e) {
2411
-
2412
- if(Settings.debug) console.log("Transparent onbeforeunload event called..");
2413
-
2414
- if(formSubmission) return; // Do not display on form submission
2415
- if(Settings.disable) return;
2416
- if(e.currentTarget == window) return;
2417
- if(!formDirty) return; // ← user hasn't modified anything; no prompt
2418
-
2419
- Transparent.html.addClass(Transparent.state.READY);
2420
- Transparent.activeOut();
2421
- dispatchEvent(new Event('load'));
2422
-
2423
- return "Dude, are you sure you want to leave? Think of the kittens!";
2424
- }
2465
+ // false positives because JS init code mutated form values after load.
2466
+ // Both that and the formDirty replacement are gone: the content is
2467
+ // already in localStorage (saved on input + on unload), so leaving the
2468
+ // page never loses it and a confirmation only gets in the way.
2469
+ window.onbeforeunload = __onbeforeunload_disabled;
2425
2470
 
2426
2471
  document.addEventListener('click', __main__, false);
2427
2472