@@ -1438,6 +1438,7 @@ const counterByDepth = [];
14381438function addStepNumberText (
14391439 ol ,
14401440 depth = 0 ,
1441+ indent = '' ,
14411442 special = [ ...ol . classList ] . some ( c => c . startsWith ( 'nested-' ) ) ,
14421443) {
14431444 let counter = ! special && counterByDepth [ depth ] ;
@@ -1461,16 +1462,19 @@ function addStepNumberText(
14611462 let i = ( Number ( ol . getAttribute ( 'start' ) ) || 1 ) - 1 ;
14621463 for ( const li of ol . children ) {
14631464 const marker = document . createElement ( 'span' ) ;
1464- marker . textContent = `${ i < cache . length ? cache [ i ] : getTextForIndex ( i ) } . ` ;
1465+ const markerText = i < cache . length ? cache [ i ] : getTextForIndex ( i ) ;
1466+ const extraIndent = ' ' . repeat ( markerText . length + 2 ) ;
1467+ marker . textContent = `${ indent } ${ markerText } . ` ;
14651468 marker . setAttribute ( 'aria-hidden' , 'true' ) ;
1469+ marker . setAttribute ( 'class' , 'list-marker' ) ;
14661470 const attributesContainer = li . querySelector ( '.attributes-tag' ) ;
14671471 if ( attributesContainer == null ) {
14681472 li . prepend ( marker ) ;
14691473 } else {
14701474 attributesContainer . insertAdjacentElement ( 'afterend' , marker ) ;
14711475 }
14721476 for ( const sublist of li . querySelectorAll ( ':scope > ol' ) ) {
1473- addStepNumberText ( sublist , depth + 1 , special ) ;
1477+ addStepNumberText ( sublist , depth + 1 , indent + extraIndent , special ) ;
14741478 }
14751479 i ++ ;
14761480 }
@@ -1482,6 +1486,52 @@ document.addEventListener('DOMContentLoaded', () => {
14821486 } ) ;
14831487} ) ;
14841488
1489+ // Omit indendation when copying a single algorithm step.
1490+ document . addEventListener ( 'copy' , evt => {
1491+ // Construct a DOM from the selection.
1492+ const doc = document . implementation . createHTMLDocument ( '' ) ;
1493+ const domRoot = doc . createElement ( 'div' ) ;
1494+ const html = evt . clipboardData . getData ( 'text/html' ) ;
1495+ if ( html ) {
1496+ domRoot . innerHTML = html ;
1497+ } else {
1498+ const selection = getSelection ( ) ;
1499+ const singleRange = selection ?. rangeCount === 1 && selection . getRangeAt ( 0 ) ;
1500+ const container = singleRange ?. commonAncestorContainer ;
1501+ if ( ! container ?. querySelector ?. ( '.list-marker' ) ) {
1502+ return ;
1503+ }
1504+ domRoot . append ( singleRange . cloneContents ( ) ) ;
1505+ }
1506+
1507+ // Preserve the indentation if there is no hidden list marker, or if selection
1508+ // of more than one step is indicated by either multiple such markers or by
1509+ // visible text before the first one.
1510+ const listMarkers = domRoot . querySelectorAll ( '.list-marker' ) ;
1511+ if ( listMarkers . length !== 1 ) {
1512+ return ;
1513+ }
1514+ const treeWalker = document . createTreeWalker ( domRoot , undefined , {
1515+ acceptNode ( node ) {
1516+ return node . nodeType === Node . TEXT_NODE || node === listMarkers [ 0 ]
1517+ ? NodeFilter . FILTER_ACCEPT
1518+ : NodeFilter . FILTER_SKIP ;
1519+ } ,
1520+ } ) ;
1521+ while ( treeWalker . nextNode ( ) ) {
1522+ const node = treeWalker . currentNode ;
1523+ if ( node . nodeType === Node . ELEMENT_NODE ) break ;
1524+ if ( / \S / u. test ( node . data ) ) return ;
1525+ }
1526+
1527+ // Strip leading indentation from the plain text representation.
1528+ evt . clipboardData . setData ( 'text/plain' , domRoot . textContent . trimStart ( ) ) ;
1529+ if ( ! html ) {
1530+ evt . clipboardData . setData ( 'text/html' , domRoot . innerHTML ) ;
1531+ }
1532+ evt . preventDefault ( ) ;
1533+ } ) ;
1534+
14851535'use strict' ;
14861536
14871537// Update superscripts to not suffer misinterpretation when copied and pasted as plain text.
0 commit comments