make_post_sell/docs/JAVASCRIPT.md
russell@unturf.com f780f0dab6 Redesign ring header into structured 2-row grid, show full now-playing title and thumbnail
Ring header restructured from flat 1fr/auto grid into two semantic rows:
- Info row: title + badges left, progress counter right
- Controls row: Reverse and Autoplay toggles right-aligned

Now-playing row shows full title (no line-clamp truncation) and product
thumbnail. JS reads og:image meta tag for current thumbnail during SPA
navigation. SPA updates for edit button, download button, file info,
comments, and canonical link. Footer and docs updates.
2026-02-09 11:46:54 -05:00

5.2 KiB

JavaScript Usage

This project minimizes JavaScript usage, preferring pure CSS solutions where possible (e.g., <details> elements for toggles). JavaScript is used only where necessary for payment integrations, real-time updates, and progressive enhancement.

Philosophy

  • Prefer pure HTML/CSS over JavaScript
  • Use <details> elements for toggles instead of JS
  • External SDKs loaded only when payment method is enabled
  • No build step - vanilla JS only
  • No jQuery - all vanilla JavaScript

Static JavaScript Files

Located in make_post_sell/static/js/:

qrcode.min.js

  • QRious library for QR code generation
  • Used by: Crypto checkout page
  • Generates payment QR codes for XMR/DOGE addresses

custom.js

  • Markdown preview functionality
  • Functions: previewAjax(), sendPreview()
  • Uses fetch API for AJAX calls to /markup-editor-preview

watch.js

  • Watch mode continuous playback engine
  • Used by: product.j2 when shop has watch_mode_enabled
  • Discovery ring with deterministic content traversal per shop
  • Crossfade transitions between media (3s, 60 steps)
  • Countdown overlay (7s) between items
  • Queue management: add, remove, reorder
  • Two-phase preload: JSON metadata fetch, then media buffering 30s before end
  • Recently-played filtering via localStorage (4-hour expiry)
  • Handles video, audio, and static content (PDFs, images)
  • Autoplay toggle persisted in localStorage
  • Ring state (position, direction, history) persisted in localStorage

comments.js

  • AJAX comment submission (progressive enhancement)
  • Intercepts comment form POST, submits via fetch() with X-Requested-With: XMLHttpRequest
  • Server returns JSON (HTTP 201) for AJAX requests
  • Inserts new comment into DOM without page reload (preserves media playback)
  • Falls back to normal POST + redirect without JS or on error

player.js

  • Pop-out media player with draggable window
  • Prev/next navigation and keyboard shortcuts
  • Auto-advance for images/PDFs (60s timer)
  • Preloads adjacent media for instant switching
  • Used by: /random and /tv endpoints

Inline JavaScript by Template

base.j2

Purpose: Theme switching (dark/light mode)

  • Runs immediately in <head> to prevent flash of wrong theme
  • Priority: localStorage > user preference > shop default
  • Exposes window.setTheme() for programmatic use

shop_settings.j2

Purpose: Settings page interactions

  • Crypto wallets toggle (checkbox + localStorage)
  • Theme preview for shop default theme radio buttons
  • Note: Payment provider toggles use pure CSS <details> elements

user_settings.j2

Purpose: User theme preference sync

  • Syncs theme radio buttons with localStorage
  • Updates localStorage on form submit

cart_checkout.j2

Purpose: Payment processing

  1. PayPal SDK (paypal.Buttons())

    • Loads PayPal SDK from paypal.com
    • Creates orders via POST /paypal/create-order
    • Handles approval flow and form submission
    • Double-click protection flags
  2. Cancel Crypto Quote (cancelQuote())

    • Cancels pending crypto payment quotes
    • Uses fetch API with CSRF token

crypto_checkout.j2

Purpose: Crypto payment monitoring

  • QR code generation using QRious library
  • Countdown timer for quote expiry
  • Status polling via fetch API
  • Copy-to-clipboard for address/amount
  • Dynamic UI updates based on payment status
  • Functions: disableQuoteButtons(), removePaymentElements(), replaceButtonsWithInvoiceLink()

snippets/stripe.j2

Purpose: Stripe card form (macro new_card())

  • Loads Stripe.js SDK
  • Creates Payment Element for card input
  • Handles stripe.confirmSetup() flow
  • Uses vanilla JS with DOMContentLoaded

snippets/analytics.j2

Purpose: Analytics tracking (optional)

  • Plausible Analytics (privacy-focused)
  • Google Analytics (gtag.js)
  • Only loaded if shop has configured analytics

product.j2

Purpose: Inline video playback

  • playInline() replaces thumbnail container with <video> element on click
  • Used for preview playback on product pages without watch mode
  • Loads watch.js when shop has watch_mode_enabled

snippets/comments.j2

Purpose: Comment form

  • Loads comments.js async for AJAX submission
  • Uses js-only class pattern for elements requiring JS

snippets/optional-javascript.j2

Purpose: Optional JS loading

  • Loads custom.js async
  • Used for markdown preview functionality

External SDKs

SDK URL Used For
PayPal paypal.com/sdk/js PayPal button/checkout
Stripe js.stripe.com/v3/ Card payment form
Plausible Shop-configured domain Privacy analytics
Google Analytics googletagmanager.com Google analytics

CSRF Protection

All fetch/AJAX calls include CSRF tokens:

  • Header: X-CSRF-Token
  • Value from: {{ request.session.get_csrf_token() }} or hidden input

Progressive Enhancement

Pages work without JavaScript where possible:

  • Payment provider toggles use <details> (pure CSS)
  • Forms submit normally without JS
  • JS enhances UX (copy buttons, QR codes, live previews)
  • Comment forms POST normally without JS; AJAX submission preserves playback with JS
  • Watch mode elements use js-only class (hidden via <noscript> stylesheet)
  • Video thumbnails link directly to media without JS; inline playback with JS