rewrite-initial.js
Path: js/rewrite-initial.js | Language: JavaScript | Lines: ~537
Fast non-block-layout DOM processors run before full rewrite.js system
Overview
This module defines "rewrite processors" - a faster variant of the standard layout processors that handle DOM transformations that don't affect block layout. These processors run early in the page load process, before heavier layout-affecting rewrites, to quickly apply styling and inject simple UI elements.
The core distinction is that rewrite processors have blockLayout: false, meaning they can safely run without triggering expensive layout recalculations. They handle tasks like adding CSS classes, injecting inline icons, wrapping text nodes, and other lightweight DOM manipulations.
The module implements several categories of fast rewrites: inline icon processing (including SVG-based progress indicators), recently-modified link decorations, list styling, horizontal rule customization, and inline mode selector wrapping. It also includes early page setup logic for thumbnails, TOC visibility, and special page designations.
Public API
Core Registration Functions
addRewriteProcessor(processorName, processor, options = null)
- Registers a non-block layout processor
- Automatically sets
blockLayout: falseoption - Delegates to
addLayoutProcessor()from layout.js - Called by: Module-level code registering processors
- Calls:
addLayoutProcessor()
processMainContentAndAddRewriteProcessor(processorName, processor)
- Runs processor on
document.mainimmediately - Then registers it for future content loads
- Called by: Code needing both immediate and future processing
- Calls:
processor(),addRewriteProcessor()
Progress Indicator API
arcSVGForProgressPercent(percent: number) → string
- Generates SVG source for circular progress arc (0-100%)
- Uses different stroke widths for mobile vs desktop
- Returns complete
<svg>element as string - Called by:
renderProgressPercentageIcon()
renderProgressPercentageIcon(progressIndicatorElement: Element)
- Injects progress icon into element with
data-progress-percentageattribute - Creates inline icon with data URL containing SVG
- Marks element with
.progress-percentage-rendered - Called by:
injectProgressIconsprocessor
renderProgressPercentageIconIfNeeded(progressIndicatorElement: Element)
- Conditional wrapper that checks if already rendered
- Called by:
injectProgressIconsprocessor
isProgressPercentageIconRendered(progressIndicatorElement: Element) → boolean
- Returns true if element has
.progress-percentage-renderedclass
invalidateProgressPercentageIconForElement(progressIndicatorElement: Element)
- Removes
.progress-percentage-renderedclass - Forces re-render on next
renderProgressPercentageIconIfNeeded()call
Recently-Modified Icon API
addRecentlyModifiedIconToLink(link: Element)
- Injects
.recently-modified-icon-hookspan at start of link - Adds U+2060 WORD JOINER to prevent line breaks
- Handles coordination with indicator hooks if present
- Marks with
.has-recently-modified-icon - Called by:
enableRecentlyModifiedLinkIconsprocessor
removeRecentlyModifiedIconFromLink(link: Element)
- Removes icon hook span and WORD JOINER
- Cleans up coordinating text nodes
- Called by:
enableRecentlyModifiedLinkListIconsprocessor
Page Thumbnail API
pageThumbnailAttributesFromDocument(doc: Document) → Object|null
- Extracts Open Graph image metadata from document
- Returns object with
src,title,width,height,class,style - Returns
nullif noog:imagemeta tag found - Called by: Page initialization code
injectThumbnailIntoPageAbstract(pageAbstract, pageThumbnailAttributes, options) → Element|null
- Creates
<figure class="page-thumbnail-figure">with image - Options:
atEnd(default true),floatClass(default "float-not") - Skips if already injected or if logo image
- Returns injected
<img>element ornull - Called by: Page abstract initialization
TOC Visibility
updateTOCVisibility(TOC: Element)
- Hides TOC if no entries, or if main TOC with ≤1 entry
- Checks
body.toc-notclass - Called by:
doWhenElementExists("#TOC")
Internal Architecture
Processor Registry
Processors are registered at module load time and run via the layout.js system. All processors in this module have blockLayout: false set automatically by addRewriteProcessor().
Registered processors:
processInlineIconsInContainer- Enables inline icons withicon-*classesinjectProgressIcons- Renders progress percentage indicatorsenableRecentlyModifiedLinkIcons- Adds star icons to recently-modified linksenableRecentlyModifiedLinkListIcons- Converts icons to list-item stylingdesignateListStyles- Adds.dark-mode-invertto list itemsdesignateHorizontalRuleStyles- Configures<hr>styling and custom imageswrapParenthesizedInlineModeSelectors- Wraps mode selector buttons
Processing Order
Processors run in registration order when content loads:
- Inline icons - Base icon system setup
- Progress indicators - Specialized icon injection
- Recently-modified (standalone) - Icon injection for non-list links
- Recently-modified (lists) - Detection and conversion for list items
- List styling - Dark mode classes
- Horizontal rules - Styling and custom images
- Mode selectors - Parenthesization wrapping
Special Inline Icons
Icons with class icon-special have their --icon-url CSS variable set elsewhere. The module handles:
- Progress indicators - Dynamically generated SVG data URLs
- Recently-modified star - White star on black circle (
.icon-single-white-star-on-black-circle)
Standard icons get their CSS variable set from /static/img/icon/icons.svg#${iconName}.
Recently-Modified Link Handling
Links with .link-modified-recently get different treatment based on context:
Standalone links (not in lists):
- Get
.recently-modified-icon-hookspan injected - Hook contains CSS-positioned star icon
- U+2060 WORD JOINER prevents line breaks
List item links (li > p > a or similar):
- Containing
<li>gets.link-modified-recently-list-itemclass - Link gets
.in-listclass - Icon hook removed if present (CSS uses list marker instead)
Detection logic handles complex cases like transcluded annotations within list items.
Progress Indicator Math
arcSVGForProgressPercent() generates:
- Backdrop circle - Grayscale value varies with progress (darker = more complete)
- Progress arc - Black stroke from 12 o'clock clockwise
- Uses trigonometry to calculate arc endpoint:
angle = 2π × (percent/100 - 0.25) - At 100%, renders full circle instead of arc
Stroke width: 64px mobile, 56px desktop.
Horizontal Rule Customization
HR elements can have type classes like horizontal-rule-{type}:
- Sequence classes (
nth-1,nth-2, etc.) - Left as-is - Special classes (
horizontal-rule-small) - Left as-is - Image classes (any other) - Sets
--icon-image: var(--GW-image-{type})
If the class is on a containing <div>, the div is unwrapped and classes moved to <hr>.
Early Page Setup
Module runs initialization code outside processors:
Page thumbnails (non-index pages):
- Extracts
og:imagemetadata on body load - Pre-fetches image via
doAjax() - Injects into
.page-abstractwhen found - Skips homepage and
/indexpages
Blog pages:
- Adds
.blog-pageclass to<body>
Ref pages:
- Clears
<title>and<h1>for ID-based loading
TOC visibility:
- Hides if no entries or main TOC with ≤1 entry
Key Patterns
Word Joiner for Icon Styling
The U+2060 WORD JOINER character is critical for icon positioning. It's injected as a Unicode character (not HTML entity) directly into the first text node of the link:
linkFirstTextNode.textContent = "\u{2060}" + linkFirstTextNode.textContent;
This prevents:
- Line breaks between icon hook and text
- Multiple text nodes (which cause text shadow artifacts)
- CSS layout issues
When multiple hooks exist (recently-modified + indicator), a separate text node with WORD JOINER is inserted between hooks.
SVG Data URL Embedding
Progress indicators embed SVG directly in CSS:
style: `--icon-url: url("data:image/svg+xml;utf8,${encodeURIComponent(svgSrc)}")`
This avoids external requests and allows dynamic generation based on progress percentage.
Conditional Icon Removal
When a recently-modified link is detected in a list context, any existing standalone icon is removed:
if (link.classList.contains("has-recently-modified-icon"))
removeRecentlyModifiedIconFromLink(link);
This prevents duplicate styling (list marker vs inline icon).
Progressive Enhancement
The module uses doWhenElementExists() and doWhenBodyExists() to progressively enhance content:
- Runs immediately when selector matches
- Handles dynamic content loading
- No waiting for full page load
Option Field Merging
Functions accept option objects merged with defaults:
options = Object.assign({
atEnd: true,
floatClass: "float-not"
}, options);
Allows partial override while maintaining defaults.
Configuration
CSS Variables
Icon URLs:
--icon-url- Set per icon, either from SVG sprite or data URL--GW-image-{type}- Custom horizontal rule images defined in CSS
Progress Indicator Colors:
- Backdrop: Grayscale
110 + (percent × 0.64)on mobile,170 + (percent × 0.64)on desktop - Arc: Always black (
#000)
Mode Selectors
The wrapParenthesizedInlineModeSelectors processor targets:
.dark-mode-selector-inline.reader-mode-selector-inline.extracts-mode-selector-inline.search-mode-selector-inline.help-mode-selector-inline.toolbar-mode-selector-inline
Recently-Modified Detection
Links must have:
.link-modified-recentlyclass (set by backend)- Either standalone or within
li > p:only-of-typeor.data-fieldin list context
Special Page Paths
/blog/*- Gets.blog-pageclass/ref/*- Title/header cleared for ID-based loading/,/index- Thumbnail injection skipped- Logo URLs (
/static/img/logo/*) - Thumbnail injection skipped
Integration Points
Events and Timing
doWhenBodyExists(callback) - Used for:
- Page thumbnail metadata extraction
- Blog page class application
doWhenMainExists(callback) - Used for:
- Ref page title clearing
doWhenElementExists(callback, selector) - Used for:
- Page abstract thumbnail injection (
#markdownBody > .abstract:first-child + *) - TOC visibility updates (
#TOC)
Shared with layout.js
addLayoutProcessor(processorName, processor, options)
- Called by
addRewriteProcessor() - Receives processors with
blockLayout: false - Manages actual processor execution
Shared with rewrite.js
This module is the "fast" counterpart to rewrite.js:
- rewrite-initial.js - Non-block-layout processors
- rewrite.js - Block-layout-affecting processors
Both use the same registration system but different performance profiles.
CSS Dependencies
Requires styles for:
.inline-icon,.dark-mode-invert.recently-modified-icon-hook,.has-recently-modified-icon.link-modified-recently-list-item.progress-indicator-icon,.progress-percentage-rendered.page-thumbnail-figure,.float-not,.float-left,.float-right.horizontal-rule-nth-1, etc..inline-mode-selector
Backend Data Requirements
HTML attributes:
data-progress-percentage- Integer 0-100 on elements needing progress indicatorsclass="link-modified-recently"- On recently updated links
Meta tags (for thumbnails):
<meta property="og:image" content="URL"><meta property="og:image:alt" content="..."><meta property="og:image:width" content="..."><meta property="og:image:height" content="..."><meta property="gwern:thumbnail:css-classes" content="..."><meta name="og:title" content="...">(fallback alt text)
Image Resources
/static/img/icon/icons.svg- Sprite sheet for standard inline icons/static/img/logo/*- Logo images (excluded from thumbnails)- Custom HR images referenced via
--GW-image-{type}CSS variables
See Also
- rewrite.js - Block-layout rewrite processors; the "heavy" counterpart to this module
- initial.js - Earlier initialization that defines the notification center and utilities
- layout.js - Core layout processor system that addRewriteProcessor delegates to
- utility.js - Defines newElement, unwrap, URLFromString used by processors
- dark-mode.js - Uses .dark-mode-invert classes set by this module
- content.js - Content loading that triggers processor execution