Skip to main content

max / goingson

GO-33-4: wrap manager classes in IIFEs, drop window.* namespace bridges - Wrap pagination-manager/selection-manager/virtual-scroller in a strict IIFE (column-0 body per the components.js/state.js convention) so their top-level class no longer leaks a global binding; consumers already used GoingsOn.X so no call sites change - address-highlight.js: export only GoingsOn.addressHighlight; the window.attach* fallback branch was dead (escape.js bootstraps GoingsOn before this file loads in both compose.html and index.html) - compose-form.js: export only GoingsOn.composeForm; drop window.GoingsOnComposeForm and the dead window.attachAddressHighlight fallback - compose.html: read GoingsOn.composeForm instead of the window global node --check clean on all 5 files, JS gate 56/56, no window.* bridges remain. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-07-06 15:17 UTC
Commit: edb55558d6e0b08f091a55ac18926754f5af6534
Parent: d557c46
6 files changed, +30 insertions, -23 deletions
@@ -163,11 +163,12 @@
163 163
164 164 // Stage 4 of compose unification — autocomplete, address highlight,
165 165 // CC/BCC toggle, signature swap, and attachment picker/render/remove
166 - // now live in composeForm.bindBehaviors. The window keeps a thin
167 - // contacts loader (contactEmails) since it doesn't bootstrap the
168 - // GoingsOn namespace; the controller's getContacts callback reads it.
166 + // now live in composeForm.bindBehaviors. This inline script keeps a thin
167 + // contacts loader (contactEmails); the controller's getContacts callback
168 + // reads it. escape.js bootstraps GoingsOn here, so the compose API is on
169 + // the namespace.
169 170
170 - const composeForm = window.GoingsOnComposeForm;
171 + const composeForm = GoingsOn.composeForm;
171 172 let composeCtrl = null;
172 173 let contactEmails = []; // [{name, email, isImplicit}]
173 174
@@ -270,13 +270,9 @@
270 270 // (js/escape.js), loaded before this file in both compose.html and index.html.
271 271 const escapeHtml = GoingsOn.escape.escapeHtml;
272 272
273 - // Export to GoingsOn namespace or window (for standalone compose.html)
274 - if (typeof GoingsOn !== 'undefined') {
275 - GoingsOn.addressHighlight = { attach, detach, clearCache };
276 - } else {
277 - window.attachAddressHighlight = attach;
278 - window.detachAddressHighlight = detach;
279 - window.clearAddressHighlightCache = clearCache;
280 - }
273 + // Export to the GoingsOn namespace. escape.js (loaded before this file in
274 + // both compose.html and index.html) guarantees GoingsOn exists, so no
275 + // window.* fallback is needed.
276 + GoingsOn.addressHighlight = { attach, detach, clearCache };
281 277
282 278 })();
@@ -358,9 +358,8 @@
358 358 attachAutocomplete(ccEl);
359 359 attachAutocomplete(bccEl);
360 360
361 - // ---------- Address highlight (uses whichever helper the surface loaded) ----------
362 - const ahAttach = (window.GoingsOn && window.GoingsOn.addressHighlight && window.GoingsOn.addressHighlight.attach)
363 - || (typeof window.attachAddressHighlight === 'function' ? window.attachAddressHighlight : null);
361 + // ---------- Address highlight (GoingsOn.addressHighlight, if the surface loaded it) ----------
362 + const ahAttach = window.GoingsOn && window.GoingsOn.addressHighlight && window.GoingsOn.addressHighlight.attach;
364 363 if (ahAttach) {
365 364 const ahOpts = { contacts: getContacts };
366 365 // Tauri's `invoke` is needed by the IMAP-backed lookup in
@@ -616,12 +615,8 @@
616 615 bindBehaviors,
617 616 };
618 617
619 - // Compose.html is a standalone webview that doesn't bootstrap the
620 - // GoingsOn namespace, so we attach to window as well for that path.
621 - if (typeof window !== 'undefined') {
622 - window.GoingsOnComposeForm = api;
623 - if (window.GoingsOn) {
624 - window.GoingsOn.composeForm = api;
625 - }
626 - }
618 + // escape.js bootstraps the GoingsOn namespace even in the standalone
619 + // compose.html webview (this file already depends on GoingsOn.escape), so
620 + // the API lives on the namespace, not a bare window global.
621 + GoingsOn.composeForm = api;
627 622 })();
@@ -4,6 +4,9 @@
4 4 * Replaces duplicate pagination code in tasks.js and emails.js.
5 5 */
6 6
7 + (function() {
8 + 'use strict';
9 +
7 10 class PaginationManager {
8 11 /**
9 12 * Create a pagination manager.
@@ -152,3 +155,5 @@ class PaginationManager {
152 155 if (window.GoingsOn) {
153 156 GoingsOn.PaginationManager = PaginationManager;
154 157 }
158 +
159 + })();
@@ -6,6 +6,9 @@
6 6 * Supports both DOM-based and data-based range selection for virtual scrolling.
7 7 */
8 8
9 + (function() {
10 + 'use strict';
11 +
9 12 class SelectionManager {
10 13 /**
11 14 * Create a selection manager.
@@ -213,3 +216,5 @@ class SelectionManager {
213 216 if (window.GoingsOn) {
214 217 GoingsOn.SelectionManager = SelectionManager;
215 218 }
219 +
220 + })();
@@ -4,6 +4,9 @@
4 4 * Only renders visible DOM nodes + overscan buffer.
5 5 */
6 6
7 + (function() {
8 + 'use strict';
9 +
7 10 class VirtualScroller {
8 11 /**
9 12 * Create a virtual scroller instance.
@@ -417,3 +420,5 @@ class VirtualScroller {
417 420 if (window.GoingsOn) {
418 421 GoingsOn.VirtualScroller = VirtualScroller;
419 422 }
423 +
424 + })();