Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add indentation to hidden counter text #604

Merged
merged 8 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions css/elements.css
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ body.oldtoc {

span[aria-hidden='true'] {
font-size: 0;
white-space: pre;
}

a {
Expand Down
54 changes: 52 additions & 2 deletions js/listNumbers.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ const counterByDepth = [];
function addStepNumberText(
ol,
depth = 0,
indent = '',
special = [...ol.classList].some(c => c.startsWith('nested-')),
) {
let counter = !special && counterByDepth[depth];
Expand All @@ -103,16 +104,19 @@ function addStepNumberText(
let i = (Number(ol.getAttribute('start')) || 1) - 1;
for (const li of ol.children) {
const marker = document.createElement('span');
marker.textContent = `${i < cache.length ? cache[i] : getTextForIndex(i)}. `;
const markerText = i < cache.length ? cache[i] : getTextForIndex(i);
const extraIndent = ' '.repeat(markerText.length + 2);
marker.textContent = `${indent}${markerText}. `;
marker.setAttribute('aria-hidden', 'true');
marker.setAttribute('class', 'list-marker');
const attributesContainer = li.querySelector('.attributes-tag');
if (attributesContainer == null) {
li.prepend(marker);
} else {
attributesContainer.insertAdjacentElement('afterend', marker);
}
for (const sublist of li.querySelectorAll(':scope > ol')) {
addStepNumberText(sublist, depth + 1, special);
addStepNumberText(sublist, depth + 1, indent + extraIndent, special);
}
i++;
}
Expand All @@ -123,3 +127,49 @@ document.addEventListener('DOMContentLoaded', () => {
addStepNumberText(ol);
});
});

// Omit indendation when copying a single algorithm step.
document.addEventListener('copy', evt => {
// Construct a DOM from the selection.
const doc = document.implementation.createHTMLDocument('');
const domRoot = doc.createElement('div');
const html = evt.clipboardData.getData('text/html');
if (html) {
domRoot.innerHTML = html;
} else {
const selection = getSelection();
const singleRange = selection?.rangeCount === 1 && selection.getRangeAt(0);
const container = singleRange?.commonAncestorContainer;
if (!container?.querySelector?.('.list-marker')) {
return;
}
domRoot.append(singleRange.cloneContents());
}

// Preserve the indentation if there is no hidden list marker, or if selection
// of more than one step is indicated by either multiple such markers or by
// visible text before the first one.
const listMarkers = domRoot.querySelectorAll('.list-marker');
if (listMarkers.length !== 1) {
return;
}
const treeWalker = document.createTreeWalker(domRoot, undefined, {
acceptNode(node) {
return node.nodeType === Node.TEXT_NODE || node === listMarkers[0]
? NodeFilter.FILTER_ACCEPT
: NodeFilter.FILTER_SKIP;
},
});
while (treeWalker.nextNode()) {
const node = treeWalker.currentNode;
if (node.nodeType === Node.ELEMENT_NODE) break;
if (/\S/u.test(node.data)) return;
}

// Strip leading indentation from the plain text representation.
evt.clipboardData.setData('text/plain', domRoot.textContent.trimStart());
if (!html) {
evt.clipboardData.setData('text/html', domRoot.innerHTML);
}
evt.preventDefault();
});
10 changes: 8 additions & 2 deletions spec/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,10 @@ <h2>Example</h2>
1. Set _length_ to _length_ + 1.
1. Let _weight_ be GetWeight of _node_.
1. If _length_ = 10&lt;sup>9&lt;/sup> or _weight_ &gt; 2&lt;sup>_length_ + 1&lt;/sup>, then
1. Throw a *RangeError* exception.
1. NOTE: This is an out-of-bounds state.
1. If _ignoreErrors_ is not *true*, then
1. Throw a *RangeError* exception.
1. Set _hadError_ to *true*.
&lt;/emu-alg>
</code></pre>

Expand All @@ -386,7 +389,10 @@ <h3>Result</h3>
1. Set _length_ to _length_ + 1.
1. Let _weight_ be GetWeight of _node_.
1. If _length_ = 10<sup>9</sup> or _weight_ > 2<sup>_length_ + 1</sup>, then
1. Throw a *RangeError* exception.
1. NOTE: This is an out-of-bounds state.
1. If _ignoreErrors_ is not *true*, then
1. Throw a *RangeError* exception.
1. Set _hadError_ to *true*.
</emu-alg>
</aside>

Expand Down
55 changes: 53 additions & 2 deletions test/baselines/generated-reference/assets-inline.html
Original file line number Diff line number Diff line change
Expand Up @@ -1439,6 +1439,7 @@
function addStepNumberText(
ol,
depth = 0,
indent = '',
special = [...ol.classList].some(c => c.startsWith('nested-')),
) {
let counter = !special && counterByDepth[depth];
Expand All @@ -1462,16 +1463,19 @@
let i = (Number(ol.getAttribute('start')) || 1) - 1;
for (const li of ol.children) {
const marker = document.createElement('span');
marker.textContent = `${i < cache.length ? cache[i] : getTextForIndex(i)}. `;
const markerText = i < cache.length ? cache[i] : getTextForIndex(i);
const extraIndent = ' '.repeat(markerText.length + 2);
marker.textContent = `${indent}${markerText}. `;
marker.setAttribute('aria-hidden', 'true');
marker.setAttribute('class', 'list-marker');
const attributesContainer = li.querySelector('.attributes-tag');
if (attributesContainer == null) {
li.prepend(marker);
} else {
attributesContainer.insertAdjacentElement('afterend', marker);
}
for (const sublist of li.querySelectorAll(':scope > ol')) {
addStepNumberText(sublist, depth + 1, special);
addStepNumberText(sublist, depth + 1, indent + extraIndent, special);
}
i++;
}
Expand All @@ -1483,6 +1487,52 @@
});
});

// Omit indendation when copying a single algorithm step.
document.addEventListener('copy', evt => {
// Construct a DOM from the selection.
const doc = document.implementation.createHTMLDocument('');
const domRoot = doc.createElement('div');
const html = evt.clipboardData.getData('text/html');
if (html) {
domRoot.innerHTML = html;
} else {
const selection = getSelection();
const singleRange = selection?.rangeCount === 1 && selection.getRangeAt(0);
const container = singleRange?.commonAncestorContainer;
if (!container?.querySelector?.('.list-marker')) {
return;
}
domRoot.append(singleRange.cloneContents());
}

// Preserve the indentation if there is no hidden list marker, or if selection
// of more than one step is indicated by either multiple such markers or by
// visible text before the first one.
const listMarkers = domRoot.querySelectorAll('.list-marker');
if (listMarkers.length !== 1) {
return;
}
const treeWalker = document.createTreeWalker(domRoot, undefined, {
acceptNode(node) {
return node.nodeType === Node.TEXT_NODE || node === listMarkers[0]
? NodeFilter.FILTER_ACCEPT
: NodeFilter.FILTER_SKIP;
},
});
while (treeWalker.nextNode()) {
const node = treeWalker.currentNode;
if (node.nodeType === Node.ELEMENT_NODE) break;
if (/\S/u.test(node.data)) return;
}

// Strip leading indentation from the plain text representation.
evt.clipboardData.setData('text/plain', domRoot.textContent.trimStart());
if (!html) {
evt.clipboardData.setData('text/html', domRoot.innerHTML);
}
evt.preventDefault();
});

'use strict';

// Update superscripts to not suffer misinterpretation when copied and pasted as plain text.
Expand Down Expand Up @@ -1684,6 +1734,7 @@

span[aria-hidden='true'] {
font-size: 0;
white-space: pre;
}

a {
Expand Down
Loading