Skip to main content

extracts-options.js

User preference UI and persistence for enabling/disabling the popup/popover system.

Pathjs/extracts-options.js
LanguageJavaScript
Lines274
Sourcejs/extracts-options.js
at 406d3e423

Read this when Use this page when tracing browser-side behavior, runtime state, event handling, popups, or UI code around extracts-options.

Overview​

extracts-options.js handles user preferences for gwern.net's popup/popover extract system. It provides the mode selector widget (the "On/Off" toggle in the page toolbar), persists user choices to localStorage, and orchestrates the enable/disable lifecycle by calling into the main extracts.js module.

The module extends the Extracts object (defined in extracts.js) with option-related functionality. It supports two modes—"on" (popups/popovers enabled) and "off" (disabled)—and automatically adapts UI labels based on whether the system is running in popup mode (desktop) or popover mode (mobile). The mode selector can appear in the toolbar as a full widget or inline as a compact button pair.

A notable UX feature is the "disable from popup" flow: clicking the eye-slash button inside a popup dismisses all popups, expands the toolbar, flashes the mode selector widget, then directly calls Extracts.disableExtractPopFrames() after a delay. This teaches users where the setting lives while respecting their intent to disable.


Public API​

Extracts.setMode(selectedMode)​

Switches the extract system on or off based on selectedMode.

setMode: (selectedMode) => {
if (selectedMode == "on")
Extracts.enableExtractPopFrames();
else
Extracts.disableExtractPopFrames();
}

Called by: modeSelectButtonClicked

Extracts.injectModeSelector(replacedElement?)​

Creates and injects the mode selector widget. If replacedElement is provided, replaces it inline; otherwise adds to the page toolbar.

Called by: Extracts.setup() in extracts.js

Extracts.activateModeSelector(modeSelector)​

Attaches click handlers and event listeners to a mode selector element. Registers for Extracts.didSetMode events to keep UI in sync.

Called by: injectModeSelector, Extracts.setup() in extracts.js

Extracts.extractPopFramesEnabled() → boolean​

Returns whether extract pop-frames are currently enabled.

extractPopFramesEnabled: () => {
return (localStorage.getItem(Extracts.extractPopFramesDisabledLocalStorageItemKey()) != "true");
}

Called by: modeSelectorHTML, updateModeSelectorState

Extracts.enableExtractPopFrames()​

Enables the extract system: clears the "disabled" localStorage flag, fires Extracts.didSetMode, calls Extracts.setup(), and processes existing targets.

Called by: setMode

Extracts.disableExtractPopFrames()​

Disables the extract system: sets the "disabled" localStorage flag, fires Extracts.didSetMode, and calls Extracts.cleanup().

Called by: setMode, disableExtractPopFramesPopFrameTitleBarButton

Extracts.disableExtractPopFramesPopFrameTitleBarButton() → Element​

Creates a title bar button (eye-slash icon) that disables popups when clicked. Used in popup title bars to give users an in-context way to disable the system.

Called by: extracts.js (title bar construction) Calls: GW.pageToolbar.toggleCollapseState, GW.pageToolbar.flashWidget, disableExtractPopFrames


Internal Architecture​

Mode Options Definition​

modeOptions: [
[ "on", "On", "Enable Pop-frames", "Pop-frames Enabled", "Enable link pop-frames.", "message-lines-solid" ],
[ "off", "Off", "Disable Pop-frames", "Pop-frames Disabled", "Disable link pop-frames.", "message-slash-solid" ],
]

Each option is a tuple: [name, shortLabel, unselectedLabel, selectedLabel, description, iconName]. Labels adapt to popup vs popover mode via string replacement of "-frame" with the current suffix.

State Flow​

User clicks mode button
↓
modeSelectButtonClicked()
↓
doIfAllowed() check → Debounces rapid clicks via modeSelectorInteractable
↓
setMode("on" | "off")
↓
enableExtractPopFrames() → Removes localStorage flag
or Fires Extracts.didSetMode
disableExtractPopFrames() → Sets localStorage flag
↓
Extracts.setup() or cleanup()
↓
Extracts.processTargetsInContainer(Extracts.rootDocument) reprocesses targets (if enabling)

localStorage Key Selection​

The module dynamically selects the correct localStorage key based on current mode:

extractPopFramesDisabledLocalStorageItemKey: () => {
return (Extracts.popFrameProvider == Popups
? Extracts.popupsDisabledLocalStorageItemKey // "extract-popups-disabled"
: Extracts.popoversDisabledLocalStorageItemKey); // "extract-popovers-disabled"
}

This means popup and popover preferences are stored separately—disabling popups on desktop doesn't affect popover behavior on mobile.


Key Patterns

Button State Management​

updateModeSelectorState() does a full reset-and-rebuild of button states rather than incremental updates:

// Clear ALL buttons
modeSelector.querySelectorAll("button").forEach(button => {
button.classList.remove("active");
button.swapClasses([ "selectable", "selected" ], 0);
button.disabled = false;
// ... reset title, label, accesskey
});

// Then set the correct one
modeSelector.querySelectorAll(`.select-mode-${currentMode}`).forEach(button => {
button.swapClasses([ "selectable", "selected" ], 1);
button.disabled = true;
// ... update title, label
});

This pattern is defensive—it guarantees correct state regardless of previous state.

Accesskey Cycling​

The non-selected button gets accesskey "p", creating a toggle:

let buttons = Array.from(modeSelector.querySelectorAll("button"));
buttons[(buttons.findIndex(button => button.classList.contains("selected")) + 1) % buttons.length].accessKey = "p";

Pressing Alt+P always toggles to the other mode.

Animated Disable Flow​

The "disable from popup" button orchestrates a complex animation sequence:

  1. Add .disabled class to button (visual feedback)
  2. Expand toolbar (toggleCollapseState(false))
  3. Wait popFramesDisableDespawnDelay (1000ms), then despawn pop-frames (popFrameProvider.cleanup())
  4. Flash the mode selector widget in toolbar
  5. Wait widgetFlashRiseDuration + popFramesDisableAutoToggleDelay (1000ms)
  6. Actually disable extract pop-frames
  7. Collapse toolbar after a delay

This teaches users where the setting lives while providing smooth visual feedback.


Configuration

Timing Constants​

ConstantValuePurpose
popFramesDisableDespawnDelay1000msDelay before toolbar animation starts
popFramesDisableWidgetFlashStayDuration3000msHow long toolbar widget stays highlighted
popFramesDisableAutoToggleDelay1000msDelay before calling disableExtractPopFrames()

localStorage Keys​

KeyModuleEffect
extract-popups-disabledextracts.jsDisables popups when "true"
extract-popovers-disabledextracts.jsDisables popovers when "true"

Note: Keys are defined in extracts.js as popupsDisabledLocalStorageItemKey and popoversDisabledLocalStorageItemKey. This module references them dynamically.


Integration Points​

Events Fired​

EventWhen
Extracts.didSetModeAfter enable or disable completes

Events Listened​

EventHandler
Extracts.didSetModeupdateModeSelectorState — syncs button UI

Dependencies​

  • GW.pageToolbar — Toolbar widget management (addWidget, flashWidget, toggleCollapseState, expandToolbarFlashWidgetDoThing)
  • GW.svg() — Icon rendering
  • Extracts.popFrameProvider — Determines if we're in popup or popover mode
  • Extracts.setup() / cleanup() — Lifecycle management (from extracts.js)
  • doIfAllowed() — Rate-limiting utility (defined in utility.js)

Shared State​

  • Extracts.modeSelector — Reference to the injected toolbar widget element
  • Extracts.modeSelectorInteractable — Boolean flag for click debouncing

See Also