From 18a65fb5069d4d174250d945214b8ddd24d4afc0 Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Fri, 19 Dec 2025 16:00:08 -0500 Subject: [PATCH 1/5] Replace JS toggle with pure CSS using HTML details element --- remarkbox/static/css/common.css | 81 +++++++++++++++++++----- remarkbox/static/js/custom.js | 11 ---- remarkbox/templates/base.j2 | 8 +-- remarkbox/templates/list-nodes.j2 | 10 +-- remarkbox/templates/show-node.j2 | 27 ++++---- remarkbox/templates/snippets/forms.j2 | 4 -- remarkbox/templates/snippets/snippets.j2 | 10 +-- 7 files changed, 94 insertions(+), 57 deletions(-) diff --git a/remarkbox/static/css/common.css b/remarkbox/static/css/common.css index ebc86be..bb2383b 100644 --- a/remarkbox/static/css/common.css +++ b/remarkbox/static/css/common.css @@ -411,33 +411,84 @@ form.node-action { opacity: 0.8 !important; } -.remark-box-div-main { - margin-bottom: 35px; +/* Pure CSS toggle using
element */ +.toggle-summary { + cursor: pointer; + list-style: none; } -.remark-box-div { +.toggle-summary::-webkit-details-marker { display: none; - /* this is needed to prevent "jumping" jquery bug. */ - overflow: hidden; - margin-top: 10px; } -.edit-box-div { +.toggle-summary::marker { display: none; - /* this is needed to prevent "jumping" jquery bug. */ - overflow: hidden; + content: ""; +} + +/* Toggle text switching: show/hide different text based on open state */ +.toggle-summary .when-open { + display: none; +} + +.toggle-summary .when-closed { + display: inline; +} + +details[open] > .toggle-summary .when-open { + display: inline; +} + +details[open] > .toggle-summary .when-closed { + display: none; +} + +/* Hidden summary for main remark box (always open) */ +.toggle-summary-hidden { + display: none; +} + +/* Fallback links hidden when JS not needed */ +.toggle-fallback { + display: none; +} + +/* Edit box details styling */ +.edit-box-details { margin-top: 15px; } -.my-namespaces-div { - display: none; +/* Remark box details styling */ +.remark-box-details { + margin-top: 10px; +} + +.remark-box-details-main { + margin-bottom: 35px; +} + +/* Namespace switcher dropdown */ +.my-namespaces-details { + display: inline; + position: relative; +} + +.my-namespaces-content { position: absolute; background-color: #ffffff; z-index: 1; - /* this is needed to prevent "jumping" jquery bug. */ - overflow: hidden; - padding-top: 10px; - padding-bottom: 10px; + padding: 10px; + border: 1px solid #ddd; + min-width: 120px; +} + +/* Node children collapse/expand */ +.node-children-details { + margin-top: 5px; +} + +.node-children-details > .toggle-summary { + margin-bottom: 5px; } #remarkbox-footer { diff --git a/remarkbox/static/js/custom.js b/remarkbox/static/js/custom.js index ab28d8a..c9ccbe5 100644 --- a/remarkbox/static/js/custom.js +++ b/remarkbox/static/js/custom.js @@ -37,17 +37,6 @@ function sendPreview(textarea, div, mathjax=false){ } } -// this toggles a dropdown. -function toggle(target, button, off_text, on_text="hide"){ - if (!$('#' + target + ":visible").height()){ - $('#' + target).slideDown("slow"); - $('#' + button).text(on_text); - } - else { - $('#' + target).slideUp("slow"); - $('#' + button).text(off_text); - } -} $(document).ready( function() { diff --git a/remarkbox/templates/base.j2 b/remarkbox/templates/base.j2 index f0c7d78..6a8d35c 100644 --- a/remarkbox/templates/base.j2 +++ b/remarkbox/templates/base.j2 @@ -35,10 +35,9 @@ {{ snippets.namespace_home_uri(request.namespace) }}   {%- if request.mode != 'embed' and request.user.authenticated and request.user.namespaces %} - (switch) - -
- +
+ (switch) +
{% for namespace in request.user.namespaces %} {% if namespace != request.namespace %} {{ snippets.namespace_home_uri(namespace) }} @@ -51,6 +50,7 @@ setup
+
  {%- endif %} diff --git a/remarkbox/templates/list-nodes.j2 b/remarkbox/templates/list-nodes.j2 index 483a547..679bc15 100644 --- a/remarkbox/templates/list-nodes.j2 +++ b/remarkbox/templates/list-nodes.j2 @@ -46,13 +46,15 @@ page: {{ request.page_number }} {{ snippets.actions(node) }}
-
+
+ edithide {{ forms.edit(node) }} -
+
-
+
+ remarkhide {{ forms.reply(node, node) }} -
+ {% endfor -%} diff --git a/remarkbox/templates/show-node.j2 b/remarkbox/templates/show-node.j2 index 2e47d9b..c925990 100644 --- a/remarkbox/templates/show-node.j2 +++ b/remarkbox/templates/show-node.j2 @@ -75,13 +75,15 @@ {% endif %} -
+
+ edithide {{ forms.edit(request.node) }} -
+ -
+
+ {{ forms.reply(request.node, request.root_node) }} -
+ {%- if request.node.id and request.node_graph[request.node.id] -%} @@ -148,20 +150,23 @@ load more ({{children_ids | length}} remarks) {% endif %} -
+
+ edithide {{ forms.edit(parent) }} -
+ -
+
+ remarkhide {{ forms.reply(parent, request.root_node) }} -
- + + {#- nest children in this node's div for convo collapsing. -#} -
+
+ expand [+]collapse [-] {%- if children_ids %} {{ loop(children_ids) }} {% endif -%} -
+ {# close the class="node" div #} diff --git a/remarkbox/templates/snippets/forms.j2 b/remarkbox/templates/snippets/forms.j2 index 1c65b95..03b7539 100644 --- a/remarkbox/templates/snippets/forms.j2 +++ b/remarkbox/templates/snippets/forms.j2 @@ -1,5 +1,4 @@ {% macro reply(node, root) %} - {% if root.locked %}

This thread was locked to prevent additional comments.

@@ -39,11 +38,9 @@ {% endif %} - {% endmacro %} {% macro edit(node) %} -
{% if node.title %} @@ -70,7 +67,6 @@ {% set submit_button_value = 'save message' %} {% include 'submit.j2' %}
- {% endmacro %} {% macro pay_what_you_can() %} diff --git a/remarkbox/templates/snippets/snippets.j2 b/remarkbox/templates/snippets/snippets.j2 index 0750af6..ed3f2f0 100644 --- a/remarkbox/templates/snippets/snippets.j2 +++ b/remarkbox/templates/snippets/snippets.j2 @@ -84,9 +84,7 @@ {% macro button_remark(node, root_node) %} {% if root_node and not root_node.locked %} - remark + remark {% endif %} {% endmacro %} @@ -99,9 +97,6 @@ {% endmacro %} {% macro button_collapse(node) %} - {% endmacro %} {% macro permalinks(node, parent_node=None, root_node=None) %} @@ -211,8 +206,7 @@ {{ enable_node(node=node) }}   {% else %} edit + id="edit-link-{{ node.id }}" class="action toggle-fallback">edit {{ disable_node(node=node) }}   -- 2.49.1 From 9c74e6256af9295741d66b9d742da411cf0e5dfe Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Fri, 19 Dec 2025 17:53:29 -0500 Subject: [PATCH 2/5] Add CSS animations and minimal auto-focus JS with graceful fallback - CSS grid animation for smooth expand/collapse transitions - Fade-in animation for namespace dropdown - Auto-focus textarea on details open (progressive enhancement) - Falls back gracefully if browser lacks support --- remarkbox/static/css/common.css | 36 ++++++++++++++++++++++++++++++- remarkbox/static/js/custom.js | 16 ++++++++++++++ remarkbox/templates/list-nodes.j2 | 4 ++++ remarkbox/templates/show-node.j2 | 10 +++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/remarkbox/static/css/common.css b/remarkbox/static/css/common.css index bb2383b..6772297 100644 --- a/remarkbox/static/css/common.css +++ b/remarkbox/static/css/common.css @@ -411,7 +411,7 @@ form.node-action { opacity: 0.8 !important; } -/* Pure CSS toggle using
element */ +/* Pure CSS toggle using
element with animations */ .toggle-summary { cursor: pointer; list-style: none; @@ -453,6 +453,33 @@ details[open] > .toggle-summary .when-closed { display: none; } +/* Animated details content using CSS grid technique */ +.edit-box-details, +.remark-box-details, +.node-children-details { + --details-transition-duration: 0.3s; +} + +.edit-box-details > .details-content, +.remark-box-details > .details-content, +.node-children-details > .details-content { + display: grid; + grid-template-rows: 0fr; + transition: grid-template-rows var(--details-transition-duration) ease-out; +} + +.edit-box-details[open] > .details-content, +.remark-box-details[open] > .details-content, +.node-children-details[open] > .details-content { + grid-template-rows: 1fr; +} + +.edit-box-details > .details-content > .details-content-inner, +.remark-box-details > .details-content > .details-content-inner, +.node-children-details > .details-content > .details-content-inner { + overflow: hidden; +} + /* Edit box details styling */ .edit-box-details { margin-top: 15px; @@ -480,6 +507,13 @@ details[open] > .toggle-summary .when-closed { padding: 10px; border: 1px solid #ddd; min-width: 120px; + /* Fade in animation for dropdown */ + animation: fadeIn 0.2s ease-out; +} + +@keyframes fadeIn { + from { opacity: 0; transform: translateY(-5px); } + to { opacity: 1; transform: translateY(0); } } /* Node children collapse/expand */ diff --git a/remarkbox/static/js/custom.js b/remarkbox/static/js/custom.js index c9ccbe5..93bcf14 100644 --- a/remarkbox/static/js/custom.js +++ b/remarkbox/static/js/custom.js @@ -38,6 +38,22 @@ function sendPreview(textarea, div, mathjax=false){ } +// Auto-focus textarea when details element opens (progressive enhancement). +// Falls back gracefully if browser doesn't support the required APIs. +if (typeof document.addEventListener === 'function') { + document.addEventListener('toggle', function(e) { + var details = e.target; + if (details.tagName !== 'DETAILS' || !details.open) return; + + // Find textarea inside the details element and focus it. + var textarea = details.querySelector('textarea'); + if (textarea && typeof textarea.focus === 'function') { + // Small delay to let the CSS transition start. + setTimeout(function() { textarea.focus(); }, 50); + } + }, true); +} + $(document).ready( function() { $('button.vote-up').click( diff --git a/remarkbox/templates/list-nodes.j2 b/remarkbox/templates/list-nodes.j2 index 679bc15..a06a8a2 100644 --- a/remarkbox/templates/list-nodes.j2 +++ b/remarkbox/templates/list-nodes.j2 @@ -48,12 +48,16 @@ page: {{ request.page_number }}
edithide +
{{ forms.edit(node) }} +
remarkhide +
{{ forms.reply(node, node) }} +
{% endfor -%} diff --git a/remarkbox/templates/show-node.j2 b/remarkbox/templates/show-node.j2 index c925990..4901a72 100644 --- a/remarkbox/templates/show-node.j2 +++ b/remarkbox/templates/show-node.j2 @@ -77,12 +77,16 @@
edithide +
{{ forms.edit(request.node) }} +
+
{{ forms.reply(request.node, request.root_node) }} +
@@ -152,20 +156,26 @@
edithide +
{{ forms.edit(parent) }} +
remarkhide +
{{ forms.reply(parent, request.root_node) }} +
{#- nest children in this node's div for convo collapsing. -#}
expand [+]collapse [-] +
{%- if children_ids %} {{ loop(children_ids) }} {% endif -%} +
{# close the class="node" div #} -- 2.49.1 From ef8f38297634bb9657f94f567d054cc1c888cc8e Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Fri, 19 Dec 2025 19:32:11 -0500 Subject: [PATCH 3/5] Replace jQuery toggle animations with CSS-based animations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use CSS keyframes (slideDown/slideUp) for 800ms door-like animations - JS only toggles classes and updates button text at correct timing - Preview toggle uses native
with animated open/close - Arrow indicators on right side (hide preview ▲ / show preview ▼) - No-JS fallback preserved (links navigate to dedicated pages) --- remarkbox/static/css/common.css | 194 +++++++++++------------ remarkbox/static/js/custom.js | 42 +++-- remarkbox/templates/base.j2 | 8 +- remarkbox/templates/list-nodes.j2 | 14 +- remarkbox/templates/show-node.j2 | 37 ++--- remarkbox/templates/snippets/forms.j2 | 8 +- remarkbox/templates/snippets/snippets.j2 | 10 +- 7 files changed, 157 insertions(+), 156 deletions(-) diff --git a/remarkbox/static/css/common.css b/remarkbox/static/css/common.css index 6772297..dc7b685 100644 --- a/remarkbox/static/css/common.css +++ b/remarkbox/static/css/common.css @@ -411,120 +411,112 @@ form.node-action { opacity: 0.8 !important; } -/* Pure CSS toggle using
element with animations */ -.toggle-summary { +.remark-box-div-main { + margin-bottom: 35px; +} + +.remark-box-div { + display: none; + overflow: hidden; + margin-top: 10px; +} + +.edit-box-div { + display: none; + 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; + 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; } -.toggle-summary::-webkit-details-marker { +.preview-toggle::-webkit-details-marker { display: none; } -.toggle-summary::marker { - display: none; - content: ""; -} - -/* Toggle text switching: show/hide different text based on open state */ -.toggle-summary .when-open { - display: none; -} - -.toggle-summary .when-closed { +.preview-toggle .when-open { display: inline; } -details[open] > .toggle-summary .when-open { +.preview-toggle .when-closed { + display: none; +} + +.preview-details:not([open]) .when-open { + display: none; +} + +.preview-details:not([open]) .when-closed { display: inline; } -details[open] > .toggle-summary .when-closed { - display: none; -} - -/* Hidden summary for main remark box (always open) */ -.toggle-summary-hidden { - display: none; -} - -/* Fallback links hidden when JS not needed */ -.toggle-fallback { - display: none; -} - -/* Animated details content using CSS grid technique */ -.edit-box-details, -.remark-box-details, -.node-children-details { - --details-transition-duration: 0.3s; -} - -.edit-box-details > .details-content, -.remark-box-details > .details-content, -.node-children-details > .details-content { - display: grid; - grid-template-rows: 0fr; - transition: grid-template-rows var(--details-transition-duration) ease-out; -} - -.edit-box-details[open] > .details-content, -.remark-box-details[open] > .details-content, -.node-children-details[open] > .details-content { - grid-template-rows: 1fr; -} - -.edit-box-details > .details-content > .details-content-inner, -.remark-box-details > .details-content > .details-content-inner, -.node-children-details > .details-content > .details-content-inner { - overflow: hidden; -} - -/* Edit box details styling */ -.edit-box-details { - margin-top: 15px; -} - -/* Remark box details styling */ -.remark-box-details { - margin-top: 10px; -} - -.remark-box-details-main { - margin-bottom: 35px; -} - -/* Namespace switcher dropdown */ -.my-namespaces-details { - display: inline; - position: relative; -} - -.my-namespaces-content { - position: absolute; - background-color: #ffffff; - z-index: 1; - padding: 10px; - border: 1px solid #ddd; - min-width: 120px; - /* Fade in animation for dropdown */ - animation: fadeIn 0.2s ease-out; -} - -@keyframes fadeIn { - from { opacity: 0; transform: translateY(-5px); } - to { opacity: 1; transform: translateY(0); } -} - -/* Node children collapse/expand */ -.node-children-details { - margin-top: 5px; -} - -.node-children-details > .toggle-summary { - margin-bottom: 5px; -} - #remarkbox-footer { font-size: 0.8em; font-weight: bold; diff --git a/remarkbox/static/js/custom.js b/remarkbox/static/js/custom.js index 93bcf14..f2f7999 100644 --- a/remarkbox/static/js/custom.js +++ b/remarkbox/static/js/custom.js @@ -37,19 +37,39 @@ function sendPreview(textarea, div, mathjax=false){ } } +// CSS-based toggle for smoother animations. +function toggle(target, button, off_text, on_text="hide"){ + var el = document.getElementById(target); + var btn = document.getElementById(button); + if (el.classList.contains('toggle-open')) { + // Animate close, then update text + el.classList.add('toggle-closing'); + setTimeout(function() { + el.classList.remove('toggle-open'); + el.classList.remove('toggle-closing'); + btn.textContent = off_text; + }, 800); + } else { + // Update text immediately when opening + btn.textContent = on_text; + el.classList.add('toggle-open'); + } +} -// Auto-focus textarea when details element opens (progressive enhancement). -// Falls back gracefully if browser doesn't support the required APIs. +// Animate
close for preview-details elements. if (typeof document.addEventListener === 'function') { - document.addEventListener('toggle', function(e) { - var details = e.target; - if (details.tagName !== 'DETAILS' || !details.open) return; - - // Find textarea inside the details element and focus it. - var textarea = details.querySelector('textarea'); - if (textarea && typeof textarea.focus === 'function') { - // Small delay to let the CSS transition start. - setTimeout(function() { textarea.focus(); }, 50); + document.addEventListener('click', function(e) { + var summary = e.target.closest('.preview-toggle'); + if (!summary) return; + var details = summary.parentElement; + if (!details || !details.classList.contains('preview-details')) return; + if (details.open && !details.classList.contains('closing')) { + e.preventDefault(); + details.classList.add('closing'); + setTimeout(function() { + details.open = false; + details.classList.remove('closing'); + }, 800); } }, true); } diff --git a/remarkbox/templates/base.j2 b/remarkbox/templates/base.j2 index 6a8d35c..f0c7d78 100644 --- a/remarkbox/templates/base.j2 +++ b/remarkbox/templates/base.j2 @@ -35,9 +35,10 @@ {{ snippets.namespace_home_uri(request.namespace) }}   {%- if request.mode != 'embed' and request.user.authenticated and request.user.namespaces %} -
- (switch) -
+ (switch) + +
+ {% for namespace in request.user.namespaces %} {% if namespace != request.namespace %} {{ snippets.namespace_home_uri(namespace) }} @@ -50,7 +51,6 @@ setup
-
  {%- endif %} diff --git a/remarkbox/templates/list-nodes.j2 b/remarkbox/templates/list-nodes.j2 index a06a8a2..483a547 100644 --- a/remarkbox/templates/list-nodes.j2 +++ b/remarkbox/templates/list-nodes.j2 @@ -46,19 +46,13 @@ page: {{ request.page_number }} {{ snippets.actions(node) }} -
- edithide -
+
{{ forms.edit(node) }} -
-
+ -
- remarkhide -
+
{{ forms.reply(node, node) }} -
-
+ {% endfor -%} diff --git a/remarkbox/templates/show-node.j2 b/remarkbox/templates/show-node.j2 index 4901a72..2e47d9b 100644 --- a/remarkbox/templates/show-node.j2 +++ b/remarkbox/templates/show-node.j2 @@ -75,19 +75,13 @@ {% endif %} -
- edithide -
+
{{ forms.edit(request.node) }} -
-
+ -
- -
+
{{ forms.reply(request.node, request.root_node) }} -
-
+ {%- if request.node.id and request.node_graph[request.node.id] -%} @@ -154,29 +148,20 @@ load more ({{children_ids | length}} remarks) {% endif %} -
- edithide -
+
{{ forms.edit(parent) }} -
-
+ -
- remarkhide -
+
{{ forms.reply(parent, request.root_node) }} -
-
- + + {#- nest children in this node's div for convo collapsing. -#} -
- expand [+]collapse [-] -
+
{%- if children_ids %} {{ loop(children_ids) }} {% endif -%} -
-
+ {# close the class="node" div #} diff --git a/remarkbox/templates/snippets/forms.j2 b/remarkbox/templates/snippets/forms.j2 index 03b7539..555f6ad 100644 --- a/remarkbox/templates/snippets/forms.j2 +++ b/remarkbox/templates/snippets/forms.j2 @@ -1,4 +1,5 @@ {% macro reply(node, root) %} + {% if root.locked %}

This thread was locked to prevent additional comments.

@@ -25,8 +26,8 @@ {% set submit_button_value = 'save message' %} {% include 'submit.j2' %} -
- hide preview +
+ hide preview ▲show preview ▼
@@ -38,9 +39,11 @@ {% endif %} + {% endmacro %} {% macro edit(node) %} +
{% if node.title %} @@ -67,6 +70,7 @@ {% set submit_button_value = 'save message' %} {% include 'submit.j2' %}
+ {% endmacro %} {% macro pay_what_you_can() %} diff --git a/remarkbox/templates/snippets/snippets.j2 b/remarkbox/templates/snippets/snippets.j2 index ed3f2f0..0750af6 100644 --- a/remarkbox/templates/snippets/snippets.j2 +++ b/remarkbox/templates/snippets/snippets.j2 @@ -84,7 +84,9 @@ {% macro button_remark(node, root_node) %} {% if root_node and not root_node.locked %} - remark + remark {% endif %} {% endmacro %} @@ -97,6 +99,9 @@ {% endmacro %} {% macro button_collapse(node) %} + {% endmacro %} {% macro permalinks(node, parent_node=None, root_node=None) %} @@ -206,7 +211,8 @@ {{ enable_node(node=node) }}   {% else %} edit + onclick="toggle('edit-box-{{ node.id }}', 'edit-link-{{ node.id }}', 'edit', 'hide'); document.getElementById('edit-textarea-{{ node.id }}').focus(); return false;" + id="edit-link-{{ node.id }}" class="action">edit {{ disable_node(node=node) }}   -- 2.49.1 From 6e624f392c2146dadc85f58385bd25bd5be0326c Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Fri, 19 Dec 2025 19:54:53 -0500 Subject: [PATCH 4/5] Remove jQuery, convert to vanilla JS, add textarea auto-grow - Remove jQuery (84KB) - all functionality now vanilla JS - Remove legacy google-analytics.j2 (using gtag v4 instead) - Remove ie8.polyfils.min.js (IE8 is dead) - Add X-Requested-With header for AJAX preview requests - Textareas auto-grow up to 400px as content is added - Auto-grow triggers on toggle open if textarea has content --- docs/JAVASCRIPT.rst | 197 ++++++++++++++++++ remarkbox/static/css/common.css | 5 +- remarkbox/static/js/custom.js | 183 +++++++++------- .../js/iframe-resizer/ie8.polyfils.min.js | 4 - remarkbox/static/js/jquery-2.1.3.min.js | 4 - .../templates/snippets/google-analytics.j2 | 12 -- .../templates/snippets/javascript-includes.j2 | 2 - 7 files changed, 311 insertions(+), 96 deletions(-) create mode 100644 docs/JAVASCRIPT.rst delete mode 100644 remarkbox/static/js/iframe-resizer/ie8.polyfils.min.js delete mode 100644 remarkbox/static/js/jquery-2.1.3.min.js delete mode 100644 remarkbox/templates/snippets/google-analytics.j2 diff --git a/docs/JAVASCRIPT.rst b/docs/JAVASCRIPT.rst new file mode 100644 index 0000000..3ae02b8 --- /dev/null +++ b/docs/JAVASCRIPT.rst @@ -0,0 +1,197 @@ +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. + +**previewAjax()** (Lines 5-20) + Debounced preview function with 800ms timer. Escapes HTML in raw mode + to prevent XSS, then calls sendPreview(). + +**sendPreview()** (Lines 22-38) + AJAX request to ``/preview-post`` endpoint for Markdown rendering. + Optionally triggers MathJax re-rendering. + +**toggle()** (Lines 41-57) + CSS-based toggle animation. Adds/removes ``toggle-open`` and + ``toggle-closing`` classes. Updates button text after 800ms animation. + +**Details close animation** (Lines 60-75) + Event listener for ``.preview-toggle`` clicks. Animates ``
`` + element closure over 800ms using ``closing`` class. + +**Document ready handler** (Lines 77-103) + - Binds vote-up/vote-down button click handlers + - Fades in alert elements over 2 seconds + - Highlights URL fragment targets with ``focused`` class + +**sendVote()** (Lines 105-118) + AJAX request to ``/vote-post`` endpoint. Updates vote count on success. + +remarkbox/static/js/jquery-2.1.3.min.js +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +jQuery library for DOM manipulation and AJAX. + +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 }}', show_raw=true, mathjax={{ request.mathjax }}) + +**snippets/forms.j2** (Line 63) + Edit textarea without raw preview:: + + previewAjax('edit-textarea-{{ node.id }}', 'node-data-{{ node.id }}', show_raw=false, mathjax={{ request.mathjax }}) + +**snippets/create.j2** (Line 14) + Thread creation textarea:: + + previewAjax('thread_data_textarea', 'preview', show_raw=true, mathjax={{ 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 12-18) + Conditional loading based on namespace configuration. + +**MathJax** (Lines 25-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. + +snippets/google-analytics.j2 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Legacy Universal Analytics (ga.js) for backward compatibility. + + +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 +- Voting requires JavaScript (AJAX-only) diff --git a/remarkbox/static/css/common.css b/remarkbox/static/css/common.css index dc7b685..90c44f0 100644 --- a/remarkbox/static/css/common.css +++ b/remarkbox/static/css/common.css @@ -552,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 f2f7999..67d4ef5 100644 --- a/remarkbox/static/js/custom.js +++ b/remarkbox/static/js/custom.js @@ -2,43 +2,48 @@ // 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' %} -- 2.49.1 From b8f88c7cfa2c550e2b984fafd1fd501ed238ba44 Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Fri, 19 Dec 2025 19:58:05 -0500 Subject: [PATCH 5/5] Update JAVASCRIPT.rst documentation --- docs/JAVASCRIPT.rst | 56 ++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/docs/JAVASCRIPT.rst b/docs/JAVASCRIPT.rst index 3ae02b8..da7c1c0 100644 --- a/docs/JAVASCRIPT.rst +++ b/docs/JAVASCRIPT.rst @@ -9,36 +9,38 @@ Standalone JavaScript Files remarkbox/static/js/custom.js ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Main application JavaScript containing core functionality. +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-38) - AJAX request to ``/preview-post`` endpoint for Markdown rendering. +**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 41-57) +**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 60-75) +**Details close animation** (Lines 68-83) Event listener for ``.preview-toggle`` clicks. Animates ``
`` element closure over 800ms using ``closing`` class. -**Document ready handler** (Lines 77-103) +**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 105-118) - AJAX request to ``/vote-post`` endpoint. Updates vote count on success. - -remarkbox/static/js/jquery-2.1.3.min.js -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -jQuery library for DOM manipulation and AJAX. +**sendVote()** (Lines 132-151) + Fetch request to ``/vote-post`` endpoint. Updates vote count on success. remarkbox/static/js/iframe-resizer/ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -79,17 +81,17 @@ Triggers debounced Markdown preview on textarea input. **snippets/forms.j2** (Line 21) Reply textarea with raw preview:: - previewAjax('textarea-{{ node.id }}', 'preview-{{ node.id }}', show_raw=true, mathjax={{ request.mathjax }}) + 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 }}', show_raw=false, mathjax={{ request.mathjax }}) + 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', show_raw=true, mathjax={{ request.mathjax }}) + previewAjax('thread_data_textarea', 'preview', true, {{ request.mathjax }}) Toggle Functionality ~~~~~~~~~~~~~~~~~~~~ @@ -145,10 +147,10 @@ snippets/javascript-includes.j2 var csrf_token = "{{ request.session.get_csrf_token() }}"; -**Google Analytics v4** (Lines 12-18) - Conditional loading based on namespace configuration. +**Google Analytics v4** (Lines 10-18) + Conditional loading based on namespace configuration (gtag.js). -**MathJax** (Lines 25-27) +**MathJax** (Lines 20-27) Mathematical formula rendering. Loaded from CDN when enabled. embed-iframe.txt.j2 @@ -169,11 +171,6 @@ snippets/stripe.j2 **Payment form handling** (Lines 83-132) Stripe card element initialization, validation, and token creation. -snippets/google-analytics.j2 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Legacy Universal Analytics (ga.js) for backward compatibility. - CSS Classes Managed by JavaScript --------------------------------- @@ -194,4 +191,15 @@ Remarkbox functions without JavaScript: (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 -- 2.49.1