diff --git a/docs/JAVASCRIPT.rst b/docs/JAVASCRIPT.rst new file mode 100644 index 0000000..da7c1c0 --- /dev/null +++ b/docs/JAVASCRIPT.rst @@ -0,0 +1,205 @@ +JavaScript Usage in Remarkbox +============================= + +This document catalogs all JavaScript usage in the Remarkbox codebase. + +Standalone JavaScript Files +--------------------------- + +remarkbox/static/js/custom.js +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Main application JavaScript containing core functionality. No external +dependencies (jQuery was removed). + +**previewAjax()** (Lines 5-20) + Debounced preview function with 800ms timer. Escapes HTML in raw mode + to prevent XSS, then calls sendPreview(). + +**sendPreview()** (Lines 22-41) + Fetch request to ``/preview-post`` endpoint for Markdown rendering. + Includes ``X-Requested-With: XMLHttpRequest`` header required by server. + Optionally triggers MathJax re-rendering. + +**toggle()** (Lines 43-66) + CSS-based toggle animation. Adds/removes ``toggle-open`` and + ``toggle-closing`` classes. Updates button text after 800ms animation. + Triggers textarea auto-grow on open if content exists. + +**Details close animation** (Lines 68-83) + Event listener for ``.preview-toggle`` clicks. Animates ``
`` + element closure over 800ms using ``closing`` class. + +**autoGrow()** (Lines 85-89) + Auto-grows textarea height based on content, capped at 400px. + +**Document ready handler** (Lines 91-130) + - Binds input handlers for textarea auto-grow + - Binds vote-up/vote-down button click handlers + - Fades in alert elements over 2 seconds + - Highlights URL fragment targets with ``focused`` class + +**sendVote()** (Lines 132-151) + Fetch request to ``/vote-post`` endpoint. Updates vote count on success. + +remarkbox/static/js/iframe-resizer/ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +External library for responsive iframe sizing in embed mode. + +- ``iframeResizer.min.js`` - Main resizer script +- ``iframeResizer.contentWindow.min.js`` - Content window script + + +Inline JavaScript in Templates +------------------------------ + +Form Submission Protection +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Pattern: ``onsubmit="submit.disabled = true; return true;"`` + +Disables submit button to prevent double submission. Used in: + +- ``snippets/forms.j2`` - Reply, edit, pay-what-you-can forms +- ``snippets/create.j2`` - Thread creation form +- ``snippets/snippets.j2`` - Watch, unwatch, lock, unlock, disable, enable, verify, approve, deny forms +- ``snippets/search.j2`` - Search form +- ``join-or-log-in.j2`` - Login form +- ``setup-namespace.j2`` - Namespace setup/cancel forms +- ``namespace-settings.j2`` - Settings forms +- ``user-settings.j2`` - User settings form +- ``user-watching.j2`` - Watching management form + +Live Markdown Preview +~~~~~~~~~~~~~~~~~~~~~ + +Pattern: ``onkeyup="previewAjax(...)"`` + +Triggers debounced Markdown preview on textarea input. + +**snippets/forms.j2** (Line 21) + Reply textarea with raw preview:: + + previewAjax('textarea-{{ node.id }}', 'preview-{{ node.id }}', true, {{ request.mathjax }}) + +**snippets/forms.j2** (Line 63) + Edit textarea without raw preview:: + + previewAjax('edit-textarea-{{ node.id }}', 'node-data-{{ node.id }}', false, {{ request.mathjax }}) + +**snippets/create.j2** (Line 14) + Thread creation textarea:: + + previewAjax('thread_data_textarea', 'preview', true, {{ request.mathjax }}) + +Toggle Functionality +~~~~~~~~~~~~~~~~~~~~ + +**base.j2** (Line 38) + Namespace switcher menu:: + + onclick="toggle('my-namespaces-div', 'my-namespaces-link', '(switch)', '(switch)'); return false;" + +**snippets/snippets.j2** (Line 88) + Remark button - shows reply form and focuses textarea:: + + onclick="toggle('remark-box-{{ node.id }}', 'remark-link-{{ node.id }}', 'remark', 'hide'); document.getElementById('textarea-{{ node.id }}').focus(); return false;" + +**snippets/snippets.j2** (Line 103) + Collapse button - hides/shows child nodes:: + + onclick="toggle('node-children-{{ node.id }}', 'collapse-link-{{ node.id }}', 'expand [+]', 'collapse [-]');" + +**snippets/snippets.j2** (Line 214) + Edit button - shows edit form and focuses textarea:: + + onclick="toggle('edit-box-{{ node.id }}', 'edit-link-{{ node.id }}', 'edit', 'hide'); document.getElementById('edit-textarea-{{ node.id }}').focus(); return false;" + +Alert Dismissal +~~~~~~~~~~~~~~~ + +**snippets/flash-alerts.j2** (Line 4) + Click to dismiss alert:: + + onclick="this.style.display='none'" + +Theme Preview +~~~~~~~~~~~~~ + +**user-settings.j2** (Lines 56-60) + Radio buttons for theme mode:: + + onchange="previewTheme(this.value)" + +**user-settings.j2** (Lines 108-127) + Theme preview function - applies ``dark-mode`` class to HTML element. + + +External Scripts +---------------- + +snippets/javascript-includes.j2 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**CSRF Token** (Line 6) + Global variable for AJAX requests:: + + var csrf_token = "{{ request.session.get_csrf_token() }}"; + +**Google Analytics v4** (Lines 10-18) + Conditional loading based on namespace configuration (gtag.js). + +**MathJax** (Lines 20-27) + Mathematical formula rendering. Loaded from CDN when enabled. + +embed-iframe.txt.j2 +~~~~~~~~~~~~~~~~~~~ + +Embed script (Lines 8-42) that: + +1. Captures parent page URL, title, and fragment +2. Creates Remarkbox iframe with configuration +3. Initializes iframe-resizer for responsive sizing + +snippets/stripe.j2 +~~~~~~~~~~~~~~~~~~ + +**Stripe v3** (Line 57) + Payment processing library from ``https://js.stripe.com/v3/`` + +**Payment form handling** (Lines 83-132) + Stripe card element initialization, validation, and token creation. + + +CSS Classes Managed by JavaScript +--------------------------------- + +- ``toggle-open`` - Element is visible with open animation +- ``toggle-closing`` - Element is animating closed +- ``closing`` - Details element is animating closed +- ``focused`` - URL fragment target highlighting +- ``dark-mode`` - Dark theme applied to HTML element + + +No-JavaScript Fallback +---------------------- + +Remarkbox functions without JavaScript: + +- Toggle links have ``href`` attributes pointing to dedicated pages + (e.g., ``/{node_id}/edit``, ``/{node_id}/reply``) +- Forms submit normally without AJAX +- ``
`` elements work natively for preview toggle +- Textareas remain fixed size (no auto-grow) +- Voting requires JavaScript (AJAX-only) + + +Removed Dependencies +-------------------- + +The following were removed to reduce bundle size: + +- **jQuery 2.1.3** (84KB) - Replaced with vanilla JS (fetch, addEventListener, querySelectorAll) +- **Legacy Google Analytics** (ga.js) - Using gtag v4 instead +- **IE8 polyfills** - IE8 is no longer supported diff --git a/remarkbox/static/css/common.css b/remarkbox/static/css/common.css index ebc86be..90c44f0 100644 --- a/remarkbox/static/css/common.css +++ b/remarkbox/static/css/common.css @@ -417,29 +417,106 @@ form.node-action { .remark-box-div { display: none; - /* this is needed to prevent "jumping" jquery bug. */ overflow: hidden; margin-top: 10px; } .edit-box-div { display: none; - /* this is needed to prevent "jumping" jquery bug. */ overflow: hidden; margin-top: 15px; } +/* CSS animation when toggled open via JS */ +.remark-box-div.toggle-open, +.edit-box-div.toggle-open { + display: block; + overflow: hidden; + max-height: 1000px; + animation: slideDown 0.8s ease-out forwards; +} + +.remark-box-div.toggle-closing, +.edit-box-div.toggle-closing { + display: block; + max-height: 1000px; + animation: slideUp 0.8s ease-out forwards; +} + +@keyframes slideDown { + from { + max-height: 0; + } + to { + max-height: 1000px; + } +} + .my-namespaces-div { display: none; position: absolute; background-color: #ffffff; z-index: 1; - /* this is needed to prevent "jumping" jquery bug. */ overflow: hidden; padding-top: 10px; padding-bottom: 10px; } +.my-namespaces-div.toggle-open { + display: block; + animation: slideDown 0.8s ease-out; +} + +/* Preview toggle with CSS animation */ +.preview-details { + overflow: hidden; +} + +.preview-details[open] > .preview { + overflow: hidden; + animation: slideDown 0.8s ease-out forwards; +} + +.preview-details.closing > .preview { + overflow: hidden; + max-height: 1000px; + animation: slideUp 0.8s ease-out forwards; +} + +@keyframes slideUp { + from { + max-height: 1000px; + } + to { + max-height: 0; + } +} + +.preview-toggle { + cursor: pointer; + list-style: none; +} + +.preview-toggle::-webkit-details-marker { + display: none; +} + +.preview-toggle .when-open { + display: inline; +} + +.preview-toggle .when-closed { + display: none; +} + +.preview-details:not([open]) .when-open { + display: none; +} + +.preview-details:not([open]) .when-closed { + display: inline; +} + #remarkbox-footer { font-size: 0.8em; font-weight: bold; @@ -475,9 +552,10 @@ form.node-action { } .common-textarea { - min-height: calc(2rem * var(--line-height)); - height: calc(5rem * var(--line-height)); + min-height: calc(3rem * var(--line-height)); + max-height: 400px; resize: vertical; + overflow-y: auto; } .monospace { diff --git a/remarkbox/static/js/custom.js b/remarkbox/static/js/custom.js index ab28d8a..67d4ef5 100644 --- a/remarkbox/static/js/custom.js +++ b/remarkbox/static/js/custom.js @@ -2,92 +2,156 @@ // previewTimer must live outside the functions. var previewTimer = null; -function previewAjax(textarea, div, show_raw = false, mathjax = false){ +function previewAjax(textarea, div, show_raw, mathjax) { // set div to raw textarea while waiting for remote Markdown rendering. if (show_raw) { - // bust HTML tags like -{% endif %} diff --git a/remarkbox/templates/snippets/javascript-includes.j2 b/remarkbox/templates/snippets/javascript-includes.j2 index 4dd19bb..2514a75 100644 --- a/remarkbox/templates/snippets/javascript-includes.j2 +++ b/remarkbox/templates/snippets/javascript-includes.j2 @@ -5,7 +5,6 @@ - {%- if request.mode == "basic" and request.namespace and request.namespace.name == request.domain and request.namespace.google_analytics_id %} @@ -26,4 +25,3 @@ src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/MathJax.js?config=TeX-MML-AM_CHTML,Safe"> {%- endif %} -{%- include 'google-analytics.j2' %}