-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
(Prelude) Factor out function updateFootnotes
- Loading branch information
Showing
2 changed files
with
130 additions
and
125 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { unlock } from '../private-apis'; | ||
|
||
const { getRichTextValues } = unlock( blockEditorPrivateApis ); | ||
|
||
let oldFootnotes = {}; | ||
|
||
export function updateFootnotesFromMeta( blocks, meta ) { | ||
const output = { blocks }; | ||
if ( ! meta ) return output; | ||
// If meta.footnotes is empty, it means the meta is not registered. | ||
if ( meta.footnotes === undefined ) return output; | ||
|
||
const _content = getRichTextValues( blocks ).join( '' ) || ''; | ||
const newOrder = []; | ||
|
||
// This can be avoided when | ||
// https://github.com/WordPress/gutenberg/pull/43204 lands. We can then | ||
// get the order directly from the rich text values. | ||
if ( _content.indexOf( 'data-fn' ) !== -1 ) { | ||
const regex = /data-fn="([^"]+)"/g; | ||
let match; | ||
while ( ( match = regex.exec( _content ) ) !== null ) { | ||
newOrder.push( match[ 1 ] ); | ||
} | ||
} | ||
|
||
const footnotes = meta.footnotes ? JSON.parse( meta.footnotes ) : []; | ||
const currentOrder = footnotes.map( ( fn ) => fn.id ); | ||
|
||
if ( currentOrder.join( '' ) === newOrder.join( '' ) ) return output; | ||
|
||
const newFootnotes = newOrder.map( | ||
( fnId ) => | ||
footnotes.find( ( fn ) => fn.id === fnId ) || | ||
oldFootnotes[ fnId ] || { | ||
id: fnId, | ||
content: '', | ||
} | ||
); | ||
|
||
function updateAttributes( attributes ) { | ||
attributes = { ...attributes }; | ||
|
||
for ( const key in attributes ) { | ||
const value = attributes[ key ]; | ||
|
||
if ( Array.isArray( value ) ) { | ||
attributes[ key ] = value.map( updateAttributes ); | ||
continue; | ||
} | ||
|
||
if ( typeof value !== 'string' ) { | ||
continue; | ||
} | ||
|
||
if ( value.indexOf( 'data-fn' ) === -1 ) { | ||
continue; | ||
} | ||
|
||
// When we store rich text values, this would no longer | ||
// require a regex. | ||
const regex = | ||
/(<sup[^>]+data-fn="([^"]+)"[^>]*><a[^>]*>)[\d*]*<\/a><\/sup>/g; | ||
|
||
attributes[ key ] = value.replace( | ||
regex, | ||
( match, opening, fnId ) => { | ||
const index = newOrder.indexOf( fnId ); | ||
return `${ opening }${ index + 1 }</a></sup>`; | ||
} | ||
); | ||
|
||
const compatRegex = /<a[^>]+data-fn="([^"]+)"[^>]*>\*<\/a>/g; | ||
|
||
attributes[ key ] = attributes[ key ].replace( | ||
compatRegex, | ||
( match, fnId ) => { | ||
const index = newOrder.indexOf( fnId ); | ||
return `<sup data-fn="${ fnId }" class="fn"><a href="#${ fnId }" id="${ fnId }-link">${ | ||
index + 1 | ||
}</a></sup>`; | ||
} | ||
); | ||
} | ||
|
||
return attributes; | ||
} | ||
|
||
function updateBlocksAttributes( __blocks ) { | ||
return __blocks.map( ( block ) => { | ||
return { | ||
...block, | ||
attributes: updateAttributes( block.attributes ), | ||
innerBlocks: updateBlocksAttributes( block.innerBlocks ), | ||
}; | ||
} ); | ||
} | ||
|
||
// We need to go through all block attributs deeply and update the | ||
// footnote anchor numbering (textContent) to match the new order. | ||
const newBlocks = updateBlocksAttributes( blocks ); | ||
|
||
oldFootnotes = { | ||
...oldFootnotes, | ||
...footnotes.reduce( ( acc, fn ) => { | ||
if ( ! newOrder.includes( fn.id ) ) { | ||
acc[ fn.id ] = fn; | ||
} | ||
return acc; | ||
}, {} ), | ||
}; | ||
|
||
return { | ||
meta: { | ||
...meta, | ||
footnotes: JSON.stringify( newFootnotes ), | ||
}, | ||
blocks: newBlocks, | ||
}; | ||
} |