diff --git a/css/elements.css b/css/elements.css index 0a51f48b..94347e74 100644 --- a/css/elements.css +++ b/css/elements.css @@ -125,6 +125,7 @@ body.oldtoc { span[aria-hidden='true'] { font-size: 0; + white-space: pre; } a { diff --git a/js/listNumbers.js b/js/listNumbers.js index 81d43b80..dec4aad1 100644 --- a/js/listNumbers.js +++ b/js/listNumbers.js @@ -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]; @@ -103,8 +104,11 @@ 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); @@ -112,7 +116,7 @@ function addStepNumberText( 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++; } @@ -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(); +}); diff --git a/spec/index.html b/spec/index.html index ded4e569..8c0398d7 100644 --- a/spec/index.html +++ b/spec/index.html @@ -367,7 +367,10 @@

Example

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> @@ -386,7 +389,10 @@

Result

1. Set _length_ to _length_ + 1. 1. Let _weight_ be GetWeight of _node_. 1. If _length_ = 109 or _weight_ > 2_length_ + 1, 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*. diff --git a/test/baselines/generated-reference/assets-inline.html b/test/baselines/generated-reference/assets-inline.html index 302974cc..4f72c8ee 100644 --- a/test/baselines/generated-reference/assets-inline.html +++ b/test/baselines/generated-reference/assets-inline.html @@ -1439,6 +1439,7 @@ function addStepNumberText( ol, depth = 0, + indent = '', special = [...ol.classList].some(c => c.startsWith('nested-')), ) { let counter = !special && counterByDepth[depth]; @@ -1462,8 +1463,11 @@ 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); @@ -1471,7 +1475,7 @@ 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++; } @@ -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. @@ -1684,6 +1734,7 @@ span[aria-hidden='true'] { font-size: 0; + white-space: pre; } a {