/**
 * Wraps the currently selected text in the browser with an HTML span element
 * that has the specified CSS class and a data attribute containing a unique
 * identifier.
 *
 * To avoid creating many nested spans when users click on formatted text
 * (e.g. inside <strong>, <em>, etc.), this function first normalises the
 * selected fragment by unwrapping any existing plugin comment spans and
 * empty wrapper <span> elements, then adds a single clean wrapper.
 *
 * @param {string} frontCommentClass    - The CSS class to apply to the wrapping span element.
 * @param {string} tempHighlightId - A temporary identifier for the unsaved highlight.
 */
function cfWrapSelectedText( frontCommentClass, tempHighlightId ) {
	const selection = window.getSelection();
	if ( selection.rangeCount > 0 ) {
		const range = selection.getRangeAt( 0 );

		// Create the mark element (better for wrapping formatted content)
		const span = document.createElement( 'mark' );
		span.classList.add( frontCommentClass );
		span.setAttribute( 'data-icfp-temp-highlight', tempHighlightId );
		
		// Add a temporary ID until WordPress returns the real comment ID.
		const uniqueHighlightId = `icfp-highlight-${tempHighlightId}`;
		span.setAttribute( 'id', uniqueHighlightId );
		span.setAttribute( 'data-icfp-highlight-id', uniqueHighlightId );
		// Note: data-comment-id will be added after comment submission

		// Before wrapping, expand range to include complete inline elements if selection spans them
		// BUT only expand if the selection is actually in the middle of an inline element
		// Don't expand if selection is at the start/end of an inline element or in plain text
		const inlineTags = [ 'STRONG', 'EM', 'B', 'I', 'U', 'CODE', 'MARK', 'A' ];
		const startContainer = range.startContainer;
		const endContainer = range.endContainer;
		
		// Store the original selection text and range state to verify we don't expand too much
		const originalSelectionText = range.toString().trim();
		const originalStartContainer = range.startContainer;
		const originalStartOffset = range.startOffset;
		const originalEndContainer = range.endContainer;
		const originalEndOffset = range.endOffset;
		
		// Check if start is inside an inline element and expand to include it
		// Only expand if the selection actually starts in the middle of an inline element's text
		if ( startContainer.nodeType === Node.TEXT_NODE ) {
			const parent = startContainer.parentElement;
			if ( parent && inlineTags.includes( parent.tagName ) ) {
				// Check if selection starts in the middle of the inline element's text content
				const textBefore = startContainer.textContent.substring( 0, range.startOffset );
				const textAfter = startContainer.textContent.substring( range.startOffset );
				
				// Only expand if:
				// 1. There's text before the selection start (selection is in the middle)
				// 2. The selection text starts within this text node
				if ( textBefore.length > 0 && textAfter.startsWith( originalSelectionText.substring( 0, Math.min( 10, originalSelectionText.length ) ) ) ) {
					// Selection starts in the middle - try to expand to include whole element
					try {
						range.setStartBefore( parent );
						const expandedText = range.toString().trim();
						// If expansion added too much text (more than 50% extra), revert it
						if ( expandedText.length > originalSelectionText.length * 1.5 || ! expandedText.includes( originalSelectionText ) ) {
							range.setStart( originalStartContainer, originalStartOffset );
						}
					} catch ( e ) {
						// Ignore if can't expand
					}
				}
			}
		}
		
		// Check if end is inside an inline element and expand to include it
		// Only expand if the selection actually ends in the middle of an inline element's text
		if ( endContainer.nodeType === Node.TEXT_NODE ) {
			const parent = endContainer.parentElement;
			if ( parent && inlineTags.includes( parent.tagName ) ) {
				// Check if selection ends in the middle of the inline element's text content
				const textAfter = endContainer.textContent.substring( range.endOffset );
				const textBefore = endContainer.textContent.substring( 0, range.endOffset );
				
				// Only expand if:
				// 1. There's text after the selection end (selection is in the middle)
				// 2. The selection text ends within this text node
				if ( textAfter.length > 0 && textBefore.endsWith( originalSelectionText.substring( Math.max( 0, originalSelectionText.length - 10 ) ) ) ) {
					// Selection ends in the middle - try to expand to include whole element
					try {
						range.setEndAfter( parent );
						const expandedText = range.toString().trim();
						// If expansion added too much text (more than 50% extra), revert it
						if ( expandedText.length > originalSelectionText.length * 1.5 || ! expandedText.includes( originalSelectionText ) ) {
							range.setEnd( originalEndContainer, originalEndOffset );
						}
					} catch ( e ) {
						// Ignore if can't expand
					}
				}
			}
		}

		// Try to use surroundContents first - it handles cross-element selections better
		// and preserves formatting tags like <strong>, <em>, etc.
		let useSurroundContents = false;
		try {
			// surroundContents will wrap the entire selection including all formatting
			range.surroundContents( span );
			useSurroundContents = true;
			
			// Normalize any nested comment marks that might be inside
			const nestedSpans = span.querySelectorAll( 'mark.' + frontCommentClass + ', mark[datatext], span.' + frontCommentClass + ', span[datatext]' );
			nestedSpans.forEach( ( nestedSpan ) => {
				if ( nestedSpan !== span && nestedSpan.parentNode ) {
					const parent = nestedSpan.parentNode;
					while ( nestedSpan.firstChild ) {
						parent.insertBefore( nestedSpan.firstChild, nestedSpan );
					}
					parent.removeChild( nestedSpan );
				}
			} );
		} catch ( e ) {
			// surroundContents failed (e.g., range partially selects a non-text node)
			// Fall back to extractContents approach
			useSurroundContents = false;
		}

		// Fallback to extractContents method if surroundContents didn't work
		if ( ! useSurroundContents ) {

			// Extract the current selection so we can normalise it before wrapping.
			const extracted = range.extractContents();

			// Normalise the fragment to prevent deep nesting of spans.
			( function normalizeFragment( fragment ) {
				const walker = document.createTreeWalker(
					fragment,
					NodeFilter.SHOW_ELEMENT,
					null,
					false
				);

				const toUnwrap = [];

				while ( walker.nextNode() ) {
					const el = walker.currentNode;

					// Consider both mark and span elements (for backward compatibility)
					if ( ! el || ( el.tagName !== 'MARK' && el.tagName !== 'SPAN' ) ) {
						continue;
					}

					const classAttr = el.getAttribute( 'class' );
					const isOurCommentSpan =
						el.classList.contains( frontCommentClass ) ||
						el.hasAttribute( 'datatext' );
					const isEmptyWrapper =
						! classAttr || classAttr.trim() === '';

					// Unwrap old comment spans and empty wrapper spans.
					if ( isOurCommentSpan || isEmptyWrapper ) {
						toUnwrap.push( el );
					}
				}

				toUnwrap.forEach( ( el ) => {
					const parent = el.parentNode;
					if ( ! parent ) {
						return;
					}

					while ( el.firstChild ) {
						parent.insertBefore( el.firstChild, el );
					}
					parent.removeChild( el );
				} );
			} )( extracted );

			// Wrap the cleaned fragment in a single mark element used for inline comments.
			// Reuse the mark we already created
			span.appendChild( extracted );
			range.insertNode( span );

			// Clean up empty inline wrappers (e.g. leftover <sup></sup>) that can be
			// created by the browser when the selection starts/ends inside them.
			const inlineTagsToClean = [ 'SUP', 'SUB' ];

			[ span.previousSibling, span.nextSibling ].forEach( ( sibling ) => {
				if (
					! sibling ||
					sibling.nodeType !== Node.ELEMENT_NODE ||
					! inlineTagsToClean.includes( sibling.tagName )
				) {
					return;
				}

				const hasMeaningfulContent =
					sibling.textContent && sibling.textContent.trim().length > 0;

				if ( ! hasMeaningfulContent && sibling.parentNode ) {
					sibling.parentNode.removeChild( sibling );
				}
			} );
		}
	}
}

/**
 * Displays a message box with a textarea for the user to enter a note for the selected text.
 * The message box includes "Save Note" and "Close" buttons to handle the note saving and
 * closing the message box.
 *
 * @param {string} selection            - The selected text that the note is being added for.
 * @param {string} tempHighlightId      - Temporary identifier for the wrapping mark element.
 */
function cfBoardDisplay( selection, tempHighlightId ) {
	const mobileMediaQuery = window.matchMedia('(max-width: 767px)');

	// Optimize: Use getElementsByClassName (faster than querySelector)
	const commentWrapper = document.getElementsByClassName( 'cf-front-comment-wrapper' )[ 0 ];
	if ( ! commentWrapper ) {
		return;
	}
	
	if (mobileMediaQuery.matches) {
		commentWrapper.style.transform = 'translateY(0px)';
	} else {
		commentWrapper.style.transform = 'translateX(0px)';
	}
	commentWrapper.setAttribute( 'data-icfp-temp-highlight', tempHighlightId );

	// Calculate the position of the message box based on the selection
	// const selectionRange = window
	// 	.getSelection()
	// 	.getRangeAt( 0 )
	// 	.getBoundingClientRect();
	// Optimize: Use getElementsByClassName (faster than querySelector)
	const commentWrapperInner = commentWrapper.getElementsByClassName( 'cf-front-comment-inner-wrapper' )[ 0 ];

	// focus on comment box textarea
	const commentTextarea = document.getElementById( 'comment' );
	commentTextarea?.focus();
	const cursorPosition = 5;
	commentTextarea?.setSelectionRange( cursorPosition, cursorPosition );

	// Ensure the inner wrapper is visible
	if( commentWrapperInner ) {
		commentWrapperInner.style.display = 'block';
	}
	if( commentWrapper ) {
		if (mobileMediaQuery.matches) {
			commentWrapper.style.transform = 'translateY(0px)';
		} else {
			commentWrapper.style.transform = 'translateX(0px)';
		}
	}
	

	// Add event listeners for cancel button
	document
		.getElementById( 'cf-front-cancel' )
		?.addEventListener( 'click', function () {
			if (mobileMediaQuery.matches) {
				commentWrapper.style.transform = 'translateY(100%)';
			} else {
				commentWrapper.style.transform = 'translateX(100%)';
			}
			//cfUnwrapSelectedText( selection, commentBoardDatatext );
		} );
}

/**
 * Unwraps the currently selected text in the browser that was previously wrapped
 * with a span element by the `cfWrapSelectedText` function.
 *
 * @param {string} selection            - The selected text that was previously wrapped.
 * @param {string} commentBoardDatatext - The temporary identifier used in the wrapping mark element.
 */
function cfUnwrapSelectedText( selection, commentBoardDatatext ) {
	// Optimize: Search within content areas first, then fallback
	// This is faster than searching the entire document
	let element = null;
	const contentAreas = document.getElementsByClassName( 'entry-content' );
	const postContent = document.getElementsByClassName( 'wp-block-post-content' );
	
	// Search in content areas first
	for ( let i = 0; i < contentAreas.length && !element; i++ ) {
		const marks = contentAreas[ i ].getElementsByTagName( 'mark' );
		const spans = contentAreas[ i ].getElementsByTagName( 'span' );
		
		for ( let j = 0; j < marks.length; j++ ) {
			if ( marks[ j ].getAttribute( 'data-icfp-temp-highlight' ) === commentBoardDatatext ) {
				element = marks[ j ];
				break;
			}
		}
		if ( !element ) {
			for ( let j = 0; j < spans.length; j++ ) {
				if ( spans[ j ].getAttribute( 'data-icfp-temp-highlight' ) === commentBoardDatatext ) {
					element = spans[ j ];
					break;
				}
			}
		}
	}
	
	// Fallback to post content
	if ( !element ) {
		for ( let i = 0; i < postContent.length && !element; i++ ) {
			const marks = postContent[ i ].getElementsByTagName( 'mark' );
			const spans = postContent[ i ].getElementsByTagName( 'span' );
			
			for ( let j = 0; j < marks.length; j++ ) {
				if ( marks[ j ].getAttribute( 'data-icfp-temp-highlight' ) === commentBoardDatatext ) {
					element = marks[ j ];
					break;
				}
			}
			if ( !element ) {
				for ( let j = 0; j < spans.length; j++ ) {
					if ( spans[ j ].getAttribute( 'data-icfp-temp-highlight' ) === commentBoardDatatext ) {
						element = spans[ j ];
						break;
					}
				}
			}
		}
	}
	
	if ( element ) {
		const parent = element.parentNode;
		while ( element.firstChild ) {
			parent.insertBefore( element.firstChild, element );
		}
		parent.removeChild( element );
	}
}

const editholdy_is_activate = false;
/**
 * Converts the provided HTML content into a serialized Gutenberg block.
 *
 * @param {string}  content               - The HTML content to be converted to a Gutenberg block.
 * @param {boolean} [remove_spaces=false] - Whether to remove newlines and carriage returns from the serialized block.
 * @return {string} The serialized Gutenberg block.
 */
function convert_to_gutenberg( content, remove_spaces = false ) {
	const gutblock = wp.blocks.rawHandler( { HTML: content } );
	let serelized = wp.blocks.serialize( gutblock );
	serelized = remove_spaces ? serelized.replace( /(\n|\r)/g, '' ) : serelized;
	return serelized;
}

/**
 * Scrolls the page to the location of the selected text content.
 *
 * @param {HTMLElement} element - The HTML element that was clicked to trigger this function.
 */
function cfFrontCommentOnClick(element) {
	// Clear previous highlights.
	// For mark/span elements that were added only for highlighting (no "datatext" attribute),
	// completely unwrap them so we don't accumulate nested elements over time.
	// For persisted comment marks/spans (with "datatext"), just remove the highlight class.
	// Use a NodeList snapshot to avoid issues with live NodeLists during iteration
	// Optimize: Use getElementsByClassName (faster than querySelectorAll)
	const highlights = Array.from( document.getElementsByClassName( 'mdspan-front-comment' ) );
	highlights.forEach( ( el ) => {
		// Skip if element was already removed
		if ( ! el.parentNode ) {
			return;
		}

		if (
			el.hasAttribute( 'data-comment-id' ) ||
			el.hasAttribute( 'data-icfp-highlight-id' ) ||
			el.hasAttribute( 'datatext' )
		) {
			// For submitted comments, just remove the highlight class.
			el.classList.remove( 'mdspan-front-comment' );
			return;
		}

		// For temporary highlights, unwrap completely
		const parent = el.parentNode;
		if ( ! parent ) {
			return;
		}

		// Unwrap all children, preserving the DOM structure
		while ( el.firstChild ) {
			parent.insertBefore( el.firstChild, el );
		}
		parent.removeChild( el );
	} );
	// Optimize: Use getElementsByClassName (faster than querySelectorAll)
	const mediaHighlighted = document.getElementsByClassName( 'cf-media-highlighted' );
	for ( let i = 0; i < mediaHighlighted.length; i++ ) {
		mediaHighlighted[ i ].classList.remove( 'cf-media-highlighted' );
	}

	const blockContent = element.getAttribute('data-content')?.trim();
	const commentId = element.getAttribute('data-comment-id');
	const datatext = element.getAttribute('data-datatext'); // Get the datatext from the comment element
	const commentBlockId = element.getAttribute('data-block-id'); // Get the block_id from the comment element (may be regenerated)
	const commentBlockName = element.getAttribute('data-block-name'); // Get the block_name from the comment element
	const commentBlockAttrs = element.getAttribute('data-block-attrs'); // Get the block_attrs from the comment element
	if (!blockContent) return;
	
	// Generate unique ID for this comment highlight
	const uniqueHighlightId = commentId ? `icfp-highlight-${commentId}` : `icfp-highlight-${Date.now()}`;

	// Check if it's a URL
	if (isValidURL(blockContent)) {
		cfHighlightMediaElements(blockContent);
		return;
	}

	// First, try to find the existing mark/span element with the exact datatext that matches this comment
	// This ensures we highlight in the correct block
	let targetBlock = null;
	let existingMark = null;

	if ( commentId ) {
		const contentAreas = document.getElementsByClassName( 'entry-content' );
		const postContent = document.getElementsByClassName( 'wp-block-post-content' );
		const searchContainers = [];

		for ( let i = 0; i < contentAreas.length; i++ ) {
			searchContainers.push( contentAreas[ i ] );
		}
		for ( let i = 0; i < postContent.length; i++ ) {
			searchContainers.push( postContent[ i ] );
		}

		for ( let c = 0; c < searchContainers.length && ! existingMark; c++ ) {
			const container = searchContainers[ c ];
			const savedMark = container.querySelector(
				`mark[data-comment-id="${ commentId }"], span[data-comment-id="${ commentId }"]`
			);

			if ( savedMark ) {
				existingMark = savedMark;
				targetBlock = savedMark.parentElement;
			}
		}
	}
	
	// If we have block_id from comment, use it to restrict search to that specific block first
	// This ensures we find the mark in the correct block even if same datatext exists in multiple blocks
	if ( commentBlockId ) {
		// Try getElementById first (fastest)
		let commentTargetBlock = document.getElementById( commentBlockId );
		
		// If not found by ID, search for element with data-icfp-block-id attribute
		if ( ! commentTargetBlock ) {
			const contentAreas = document.getElementsByClassName( 'entry-content' );
			const postContent = document.getElementsByClassName( 'wp-block-post-content' );
			const searchContainers = [];
			
			if ( contentAreas.length > 0 ) {
				for ( let i = 0; i < contentAreas.length; i++ ) {
					searchContainers.push( contentAreas[ i ] );
				}
			}
			if ( postContent.length > 0 ) {
				for ( let i = 0; i < postContent.length; i++ ) {
					searchContainers.push( postContent[ i ] );
				}
			}
			
			for ( let c = 0; c < searchContainers.length && !commentTargetBlock; c++ ) {
				const container = searchContainers[ c ];
				const commonBlockTags = [ 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div', 'ul', 'ol', 'li' ];
				for ( let t = 0; t < commonBlockTags.length && !commentTargetBlock; t++ ) {
					const elements = container.getElementsByTagName( commonBlockTags[ t ] );
					for ( let i = 0; i < elements.length; i++ ) {
						const el = elements[ i ];
						if ( el.getAttribute( 'data-icfp-block-id' ) === commentBlockId ) {
							commentTargetBlock = el;
							break;
						}
					}
				}
			}
		}
		
		if ( commentTargetBlock ) {
			// Search ONLY within this specific block for the mark with matching datatext
			const marks = commentTargetBlock.getElementsByTagName( 'mark' );
			const spans = commentTargetBlock.getElementsByTagName( 'span' );
			
			// Check mark elements for exact datatext match within this block
			for ( let i = 0; i < marks.length && !existingMark; i++ ) {
				const mark = marks[ i ];
				const markDatatext = mark.getAttribute( 'datatext' );
				if ( markDatatext === datatext ) {
					// Verify the mark is actually in the correct block by checking its parent
					let markBlock = mark;
					let markBlockId = null;
					while ( markBlock && markBlock !== document.body ) {
						if ( markBlock.hasAttribute && markBlock.hasAttribute( 'data-icfp-block-id' ) ) {
							markBlockId = markBlock.getAttribute( 'data-icfp-block-id' );
							break;
						}
						markBlock = markBlock.parentElement;
					}
					
					// Only use this mark if it's in the correct block AND text content matches
					if ( markBlockId === commentBlockId ) {
						// Additional verification: check if text content matches (normalized)
						const markText = mark.textContent?.trim().toLowerCase().replace( /\s+/g, ' ' );
						const commentText = blockContent?.trim().toLowerCase().replace( /\s+/g, ' ' );
						
						if ( markText && commentText && markText.includes( commentText ) ) {
							existingMark = mark;
							targetBlock = commentTargetBlock;
							break;
						} else {
							console.warn( 'Mark with datatext found in correct block but text does not match:', {
								datatext: datatext,
								blockId: commentBlockId,
								markText: mark.textContent?.trim().substring( 0, 50 ),
								commentContent: blockContent?.substring( 0, 50 ),
								normalizedMarkText: markText?.substring( 0, 50 ),
								normalizedCommentText: commentText?.substring( 0, 50 )
							} );
						}
					} else {
						console.warn( 'Mark with datatext found but in wrong block:', {
							datatext: datatext,
							expectedBlockId: commentBlockId,
							actualBlockId: markBlockId,
							markText: mark.textContent?.trim().substring( 0, 50 )
						} );
					}
				}
			}
			
			// Check span elements if mark not found
			if ( !existingMark ) {
				for ( let i = 0; i < spans.length && !existingMark; i++ ) {
					const span = spans[ i ];
					const spanDatatext = span.getAttribute( 'datatext' );
					if ( spanDatatext === datatext ) {
						// Verify the span is actually in the correct block by checking its parent
						let spanBlock = span;
						let spanBlockId = null;
						while ( spanBlock && spanBlock !== document.body ) {
							if ( spanBlock.hasAttribute && spanBlock.hasAttribute( 'data-icfp-block-id' ) ) {
								spanBlockId = spanBlock.getAttribute( 'data-icfp-block-id' );
								break;
							}
							spanBlock = spanBlock.parentElement;
						}
						
						// Only use this span if it's in the correct block AND text content matches
						if ( spanBlockId === commentBlockId ) {
							// Additional verification: check if text content matches (normalized)
							const spanText = span.textContent?.trim().toLowerCase().replace( /\s+/g, ' ' );
							const commentText = blockContent?.trim().toLowerCase().replace( /\s+/g, ' ' );
							
							if ( spanText && commentText && spanText.includes( commentText ) ) {
								existingMark = span;
								targetBlock = commentTargetBlock;
								break;
							} else {
								console.warn( 'Span with datatext found in correct block but text does not match:', {
									datatext: datatext,
									blockId: commentBlockId,
									spanText: span.textContent?.trim().substring( 0, 50 ),
									commentContent: blockContent?.substring( 0, 50 )
								} );
							}
						} else {
							console.warn( 'Span with datatext found but in wrong block:', {
								datatext: datatext,
								expectedBlockId: commentBlockId,
								actualBlockId: spanBlockId,
								spanText: span.textContent?.trim().substring( 0, 50 )
							} );
						}
					}
				}
			}
		}
	}
	
	// If we have datatext but didn't find mark in the specific block, search more broadly
	// Match by block_name and block_attrs since block_id is regenerated on each page load
	if ( datatext && !existingMark ) {
		
		// Optimize: Search within content areas first, then fallback to document
		const contentAreas = document.getElementsByClassName( 'entry-content' );
		const postContent = document.getElementsByClassName( 'wp-block-post-content' );
		const searchContainers = [];
		
		// Collect all content containers
		if ( contentAreas.length > 0 ) {
			for ( let i = 0; i < contentAreas.length; i++ ) {
				searchContainers.push( contentAreas[ i ] );
			}
		}
		if ( postContent.length > 0 ) {
			for ( let i = 0; i < postContent.length; i++ ) {
				searchContainers.push( postContent[ i ] );
			}
		}
		
		// Helper function to check if a block matches by name and attrs
		const blockMatches = ( blockElement ) => {
			if ( ! commentBlockName ) {
				return true; // No block name to match, accept any
			}
			
			const blockName = blockElement.getAttribute( 'data-icfp-block-name' );
			
			// If block doesn't have data-icfp-block-name attribute, try to infer it from the element
			// This handles cases where blocks were created before attributes were added
			if ( ! blockName ) {
				// Try to infer block name from element tag/class
				// For example, <p> tags are usually core/paragraph
				const tagName = blockElement.tagName?.toLowerCase();
				if ( tagName === 'p' && commentBlockName === 'core/paragraph' ) {
					return true; // Likely a paragraph block
				}
				// If we can't infer, accept it anyway but will require strict text matching
				// This allows marks in blocks without block name attributes to still be found
				return true; // Accept but will require text match
			}
			
			if ( blockName !== commentBlockName ) {
				return false; // Block name doesn't match
			}
			
			// If block_attrs are provided, compare them
			if ( commentBlockAttrs ) {
				try {
					const storedAttrs = JSON.parse( commentBlockAttrs );
					const blockAttrsJson = blockElement.getAttribute( 'data-icfp-block-attrs' );
					if ( blockAttrsJson ) {
						const blockAttrs = JSON.parse( blockAttrsJson );
						// Compare only keys that exist in stored attrs
						for ( const key in storedAttrs ) {
							if ( ! blockAttrs.hasOwnProperty( key ) || blockAttrs[ key ] !== storedAttrs[ key ] ) {
								return false; // Attrs don't match
							}
						}
					}
				} catch ( e ) {
					// If parsing fails, just match by name
					console.warn( 'Failed to parse block attrs:', e );
				}
			}
			
			return true; // Block matches
		};
		
		// First, collect all marks with matching datatext
		const candidateMarks = [];
		for ( let c = 0; c < searchContainers.length; c++ ) {
			const container = searchContainers[ c ];
			const marks = container.getElementsByTagName( 'mark' );
			const spans = container.getElementsByTagName( 'span' );
			
			// Check mark elements for exact datatext match
			for ( let i = 0; i < marks.length; i++ ) {
				const mark = marks[ i ];
				const markDatatext = mark.getAttribute( 'datatext' );
				if ( markDatatext === datatext ) {
					// Find which block this mark is in
					let markBlock = mark;
					let markBlockId = null;
					while ( markBlock && markBlock !== document.body ) {
						if ( markBlock.hasAttribute && markBlock.hasAttribute( 'data-icfp-block-id' ) ) {
							markBlockId = markBlock.getAttribute( 'data-icfp-block-id' );
							break;
						}
						markBlock = markBlock.parentElement;
					}
					
					if ( markBlock ) {
						// Get mark text - use textContent to get plain text without HTML tags
						const markTextRaw = mark.textContent || mark.innerText || '';
						const markTextNormalized = markTextRaw.trim().toLowerCase().replace( /\s+/g, ' ' );
						
						candidateMarks.push( {
							mark: mark,
							block: markBlock,
							blockId: markBlockId,
							blockName: markBlock.getAttribute( 'data-icfp-block-name' ),
							blockAttrs: markBlock.getAttribute( 'data-icfp-block-attrs' ),
							markText: markTextNormalized,
							markTextRaw: markTextRaw.trim() // Keep original for logging
						} );
					}
				}
			}
			
			// Check span elements
			for ( let i = 0; i < spans.length; i++ ) {
				const span = spans[ i ];
				const spanDatatext = span.getAttribute( 'datatext' );
				if ( spanDatatext === datatext ) {
					// Find which block this span is in
					let spanBlock = span;
					let spanBlockId = null;
					while ( spanBlock && spanBlock !== document.body ) {
						if ( spanBlock.hasAttribute && spanBlock.hasAttribute( 'data-icfp-block-id' ) ) {
							spanBlockId = spanBlock.getAttribute( 'data-icfp-block-id' );
							break;
						}
						spanBlock = spanBlock.parentElement;
					}
					
					if ( spanBlock ) {
						// Get span text - use textContent to get plain text without HTML tags
						const spanTextRaw = span.textContent || span.innerText || '';
						const spanTextNormalized = spanTextRaw.trim().toLowerCase().replace( /\s+/g, ' ' );
						
						candidateMarks.push( {
							mark: span,
							block: spanBlock,
							blockId: spanBlockId,
							blockName: spanBlock.getAttribute( 'data-icfp-block-name' ),
							blockAttrs: spanBlock.getAttribute( 'data-icfp-block-attrs' ),
							markText: spanTextNormalized,
							markTextRaw: spanTextRaw.trim() // Keep original for logging
						} );
					}
				}
			}
		}
		
		// Now filter and prioritize candidates
		if ( candidateMarks.length > 0 ) {
			const commentText = blockContent?.trim().toLowerCase().replace( /\s+/g, ' ' );
			
			// Filter by block match and text match
			const matchingCandidates = candidateMarks.filter( candidate => {
				// ✅ PRIORITY 1: If datatext matches exactly, trust the mark (most reliable)
				// This is the primary identifier - if datatext matches, it's the correct mark
				// Don't require block match if datatext matches (datatext is unique per comment)
				if ( candidate.mark?.getAttribute( 'datatext' ) === datatext ) {
					return true;
				}
				
				// PRIORITY 2: Check if block matches (if block name is provided)
				const blockMatch = blockMatches( candidate.block );
				if ( ! blockMatch && commentBlockName ) {
					// Block doesn't match, but continue to text matching as fallback
					// Don't return false yet - continue to text matching
				}
				
				// PRIORITY 3: Text content matching (fallback when datatext doesn't match)
				if ( commentText && candidate.markText ) {
					const normalizedCommentText = commentText.trim();
					const normalizedMarkText = candidate.markText.trim();

					// Try exact match first
					if ( normalizedMarkText === normalizedCommentText ) {
						return true;
					}
					
					// Try includes match
					if ( normalizedMarkText.includes( normalizedCommentText ) || normalizedCommentText.includes( normalizedMarkText ) ) {
						return true;
					}

					return false;
				}

				// If no text to match, accept if block matches
				if ( blockMatch ) {
					return true;
				}
				
				// If block doesn't match and no text match, reject
				return false;
			} );
			
			if ( matchingCandidates.length > 0 ) {
				// If multiple candidates match, prefer the one with exact text match
				let bestCandidate = matchingCandidates[ 0 ];
				
				for ( let i = 0; i < matchingCandidates.length; i++ ) {
					const candidate = matchingCandidates[ i ];
					// Prefer exact text match over partial match
					if ( commentText && candidate.markText ) {
						const normalizedCommentText = commentText.trim();
						const normalizedMarkText = candidate.markText.trim();
						if ( normalizedMarkText === normalizedCommentText ) {
							bestCandidate = candidate;
							break;
						}
					}
				}
				
				// If still multiple candidates and all have same block_name, use document position
				if ( matchingCandidates.length > 1 && bestCandidate === matchingCandidates[ 0 ] ) {
					// Get all blocks in document order
					const allBlocks = [];
					for ( let c = 0; c < searchContainers.length; c++ ) {
						const container = searchContainers[ c ];
						const commonBlockTags = [ 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div', 'ul', 'ol', 'li' ];
						for ( let t = 0; t < commonBlockTags.length; t++ ) {
							const elements = container.getElementsByTagName( commonBlockTags[ t ] );
							for ( let i = 0; i < elements.length; i++ ) {
								const el = elements[ i ];
								if ( el.hasAttribute && el.hasAttribute( 'data-icfp-block-id' ) ) {
									allBlocks.push( el );
								}
							}
						}
					}
					
					// Find the candidate that appears later in the document (more likely where comment was added)
					let bestIndex = -1;
					for ( let i = 0; i < matchingCandidates.length; i++ ) {
						const candidate = matchingCandidates[ i ];
						const candidateIndex = allBlocks.indexOf( candidate.block );
						if ( candidateIndex > bestIndex ) {
							bestIndex = candidateIndex;
							bestCandidate = candidate;
						}
					}
				}
				
				existingMark = bestCandidate.mark;
				targetBlock = bestCandidate.block;
			}  else {
				console.warn(
					'Found marks with datatext but none matched block or text',
					{
						datatext,
						commentBlockId,
						commentBlockName
					}
				);
			
				// 🚨 Abort ONLY when we have a reliable block identity
				if ( commentBlockId && commentBlockName ) {
					console.warn(
						'Aborting restore – reliable block identity but no match',
						{ datatext, commentBlockId, commentBlockName }
					);
					return;
				}
			
				// ✅ Otherwise allow fallback text-based restore
			}
		}
	}

	// If we found an existing mark, we can use it directly instead of searching
	// This should happen BEFORE we check for container, so we can use the mark even if container isn't found
	if ( existingMark ) {
		// Just add the highlight class to the existing mark (preserve all existing attributes)
		existingMark.classList.add( 'mdspan-front-comment' );
		
		// Update id attributes if they don't exist or need updating
		if ( ! existingMark.id || existingMark.id !== uniqueHighlightId ) {
			existingMark.setAttribute( 'id', uniqueHighlightId );
		}
		if ( ! existingMark.getAttribute( 'data-icfp-highlight-id' ) || existingMark.getAttribute( 'data-icfp-highlight-id' ) !== uniqueHighlightId ) {
			existingMark.setAttribute( 'data-icfp-highlight-id', uniqueHighlightId );
		}
		
		// Ensure datatext is preserved (it should already be there)
		if ( datatext && ! existingMark.getAttribute( 'datatext' ) ) {
			existingMark.setAttribute( 'datatext', datatext );
		}
		
		// Scroll to the mark
		const selectedTextOffset = existingMark.getBoundingClientRect().top + window.pageYOffset;
		if ( selectedTextOffset ) {
			window.scrollTo({
				top: selectedTextOffset - 200,
				behavior: 'smooth',
			});
		}
		
		// Return early since we found and highlighted the existing mark
		return;
	}

	// If we didn't find an existing mark, proceed with text search
	// Optimize: Use getElementsByClassName instead of querySelector
	const commentCountTop = document.getElementsByClassName( 'cf-front-comment-count_wrap' )[ 0 ];
	let container = targetBlock;
	
	if ( ! container && commentCountTop && commentCountTop.parentElement ) {
		const parentClass = commentCountTop.parentElement.className;
		if ( parentClass ) {
			const classNames = parentClass.trim().split( /\s+/ );
			// Try to find by class name (faster than querySelector)
			for ( let i = 0; i < classNames.length; i++ ) {
				const className = classNames[ i ];
				if ( className ) {
					const elements = document.getElementsByClassName( className );
					if ( elements.length > 0 ) {
						container = elements[ 0 ];
						break;
					}
				}
			}
		}
	}
	
	if ( ! container ) {
		console.warn( 'Container not found, cannot search for text' );
		return;
	}

	// First try to highlight the exact selected text, even if it spans
	// multiple inline elements (e.g. <sup>College</sup> in). We walk all text
	// nodes in order and treat whitespace flexibly, so "College&nbsp;in" in
	// the DOM still matches a stored "College in".
	const raw = blockContent;
	let matched = false;
	
	if ( raw ) {
		// Search for the text if we didn't find an existing mark
		const walker = document.createTreeWalker(
			container,
			NodeFilter.SHOW_TEXT,
			null,
			false
		);

		const charMap = [];
		let fullText = '';

		while ( walker.nextNode() ) {
			const node = walker.currentNode;
			const value = node.nodeValue || '';
			if ( ! value ) {
				continue;
			}

			for ( let i = 0; i < value.length; i++ ) {
				charMap.push( { node, offset: i } );
				fullText += value[ i ];
			}
		}

		function isWhitespace( ch ) {
			return /\s/.test( ch );
		}

		let startIndex = -1;
		let endIndex = -1;

		// Scan fullText looking for raw, allowing DOM to have different
		// whitespace (e.g. multiple spaces, &nbsp;).
		outer: for ( let i = 0; i < fullText.length; i++ ) {
			let k = i;
			let j = 0;

			while ( j < raw.length && k < fullText.length ) {
				const cFull = fullText[ k ];
				const cRaw = raw[ j ];

				if ( isWhitespace( cFull ) && isWhitespace( cRaw ) ) {
					// Advance one step in the raw string, but skip any extra
					// whitespace characters in the DOM string so that
					// "College   in" still matches "College in".
					j++;
					k++;
					while (
						k < fullText.length &&
						isWhitespace( fullText[ k ] )
					) {
						k++;
					}
				} else {
					if ( cFull.toLowerCase() !== cRaw.toLowerCase() ) {
						break;
					}
					j++;
					k++;
				}
			}

			if ( j === raw.length ) {
				startIndex = i;
				endIndex = k;
				break outer;
			}
		}

		if ( startIndex !== -1 && endIndex !== -1 ) {
			const startPos = charMap[ startIndex ];
			const endPos = charMap[ endIndex - 1 ];

			if ( startPos && endPos ) {
				const range = document.createRange();
				range.setStart( startPos.node, startPos.offset );
				range.setEnd( endPos.node, endPos.offset + 1 );

				// Expand range to include complete inline elements to avoid splitting tags like <strong>
				// Note: We've already unwrapped all highlights at the beginning, so we don't need to worry about them here
				const inlineTags = [ 'STRONG', 'EM', 'B', 'I', 'U', 'CODE', 'MARK', 'A', 'SPAN' ];
				
				// Helper to check if element is a mark/span (we don't want to expand into these)
				const isMarkOrSpan = ( el ) => {
					return el && ( el.tagName === 'MARK' || el.tagName === 'SPAN' );
				};

				// Expand start to include complete inline elements, but don't expand into mark/span elements
				let startNode = range.startContainer;
				if ( startNode.nodeType === Node.TEXT_NODE ) {
					let current = startNode.parentElement;
					while ( current ) {
						// Don't expand into mark/span elements to avoid duplication
						if ( isMarkOrSpan( current ) ) {
							break;
						}
						if ( inlineTags.includes( current.tagName ) ) {
							// If selection starts inside this inline element, expand to include it
							const textBefore = startNode.textContent.substring( 0, range.startOffset );
							if ( textBefore.length > 0 ) {
								try {
									range.setStartBefore( current );
								} catch ( e ) {
									// Can't expand
								}
							}
							break; // Found inline element, stop
						}
						current = current.parentElement;
						if ( ! current || current === range.commonAncestorContainer ) {
							break;
						}
					}
				}
				
				// Expand end to include complete inline elements, but don't expand into mark/span elements
				let endNode = range.endContainer;
				if ( endNode.nodeType === Node.TEXT_NODE ) {
					let current = endNode.parentElement;
					while ( current ) {
						// Don't expand into mark/span elements to avoid duplication
						if ( isMarkOrSpan( current ) ) {
							break;
						}
						if ( inlineTags.includes( current.tagName ) ) {
							// If selection ends inside this inline element, expand to include it
							const textAfter = endNode.textContent.substring( range.endOffset );
							if ( textAfter.length > 0 ) {
								try {
									range.setEndAfter( current );
								} catch ( e ) {
									// Can't expand
								}
							}
							break; // Found inline element, stop
						}
						current = current.parentElement;
						if ( ! current || current === range.commonAncestorContainer ) {
							break;
						}
					}
				}

				// Find the block that contains this text
				let blockId = '';
				let blockName = '';
				let blockAttrs = '';
				
				// Walk up from the start node to find the block wrapper
				let blockNode = range.startContainer;
				if ( blockNode.nodeType === Node.TEXT_NODE ) {
					blockNode = blockNode.parentElement;
				}
				
				// Find the closest block wrapper with data-icfp-block-id
				while ( blockNode && blockNode !== document.body ) {
					if ( blockNode.hasAttribute && blockNode.hasAttribute( 'data-icfp-block-id' ) ) {
						blockId = blockNode.getAttribute( 'data-icfp-block-id' ) || '';
						blockName = blockNode.getAttribute( 'data-icfp-block-name' ) || '';
						blockAttrs = blockNode.getAttribute( 'data-icfp-block-attrs' ) || '';
						
						break;
					}
					blockNode = blockNode.parentElement;
				}
				
				if ( ! blockId ) {
					console.warn( 'No block found for highlighted text:', blockContent );
				}

				const span = document.createElement( 'mark' );
				span.className = 'mdspan-front-comment';
				
				// Add unique ID for this highlight
				span.setAttribute( 'id', uniqueHighlightId );
				span.setAttribute( 'data-icfp-highlight-id', uniqueHighlightId );
				
				// Add block information as data attributes
				if ( blockId ) {
					span.setAttribute( 'data-icfp-block-id', blockId );
				}
				if ( blockName ) {
					span.setAttribute( 'data-icfp-block-name', blockName );
				}
				if ( blockAttrs ) {
					span.setAttribute( 'data-icfp-block-attrs', blockAttrs );
				}

				try {
					range.surroundContents( span );
					matched = true;
				} catch ( e ) {
					// If surroundContents fails, use extractContents as fallback
					try {
						const contents = range.extractContents();
						// Normalize any existing comment marks/spans in the extracted content
						const walker = document.createTreeWalker(
							contents,
							NodeFilter.SHOW_ELEMENT,
							null,
							false
						);
						const toUnwrap = [];
						while ( walker.nextNode() ) {
							const el = walker.currentNode;
							if ( ( el.tagName === 'MARK' || el.tagName === 'SPAN' ) && 
							     ( el.classList.contains( 'mdspan-front-comment' ) || el.hasAttribute( 'datatext' ) ) ) {
								toUnwrap.push( el );
							}
						}
						toUnwrap.forEach( ( el ) => {
							const parent = el.parentNode;
							if ( parent ) {
								while ( el.firstChild ) {
									parent.insertBefore( el.firstChild, el );
								}
								parent.removeChild( el );
							}
						} );
						
						span.appendChild( contents );
						range.insertNode( span );
						matched = true;
					} catch ( e2 ) {
						// If that also fails, fall back to word-based highlight
						matched = false;
					}
				}
			}
		}
	}

	// If we couldn't match the exact phrase (for example, if the stored text
	// differs from the current content), fall back to highlighting the first
	// matching word from the selection so the user still sees a useful anchor.
	if ( ! matched ) {
		function escapeRegExp( string ) {
			return string.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' );
		}

		const words = raw.split( /\s+/ ).filter( Boolean );
		const candidates = [ ...words ].filter(
			( value, index, self ) =>
				value && self.indexOf( value ) === index
		);
		const regexes = candidates.map(
			( text ) => new RegExp( escapeRegExp( text ), 'i' )
		);

		const fallbackWalker = document.createTreeWalker(
			container,
			NodeFilter.SHOW_TEXT,
			null,
			false
		);

		while ( fallbackWalker.nextNode() && ! matched ) {
			const textNode = fallbackWalker.currentNode;
			const text = textNode.nodeValue || '';

			for ( let i = 0; i < regexes.length; i++ ) {
				const regex = regexes[ i ];
				const match = text.match( regex );
				if ( match ) {
					// Find the block that contains this text node
					let blockId = '';
					let blockName = '';
					let blockAttrs = '';
					
					let blockNode = textNode.parentElement;
					while ( blockNode && blockNode !== document.body ) {
						if ( blockNode.hasAttribute && blockNode.hasAttribute( 'data-icfp-block-id' ) ) {
							blockId = blockNode.getAttribute( 'data-icfp-block-id' ) || '';
							blockName = blockNode.getAttribute( 'data-icfp-block-name' ) || '';
							blockAttrs = blockNode.getAttribute( 'data-icfp-block-attrs' ) || '';
							break;
						}
						blockNode = blockNode.parentElement;
					}
					
					const span = document.createElement( 'mark' );
					span.className = 'mdspan-front-comment';
					
					// Add unique ID for this highlight (use commentId if available, otherwise generate)
					const fallbackHighlightId = commentId ? `icfp-highlight-${commentId}` : `icfp-highlight-${Date.now()}`;
					span.setAttribute( 'id', fallbackHighlightId );
					span.setAttribute( 'data-icfp-highlight-id', fallbackHighlightId );
					
					// Add block information as data attributes
					if ( blockId ) {
						span.setAttribute( 'data-icfp-block-id', blockId );
					}
					if ( blockName ) {
						span.setAttribute( 'data-icfp-block-name', blockName );
					}
					if ( blockAttrs ) {
						span.setAttribute( 'data-icfp-block-attrs', blockAttrs );
					}
					
					const parts = text.split( regex );
					const before = parts[ 0 ];
					const matchedText = match[ 0 ];
					const after = text.slice(
						before.length + matchedText.length
					);
					span.textContent = matchedText;

					const parent = textNode.parentNode;
					parent.insertBefore(
						document.createTextNode( before ),
						textNode
					);
					parent.insertBefore( span, textNode );
					parent.insertBefore(
						document.createTextNode( after ),
						textNode
					);
					parent.removeChild( textNode );
					matched = true;
					break; // Only highlight first match
				}
			}
		}
	}

	if (matched && !existingMark) {
		// Only scroll if we created a new highlight (not if we used existing mark)
		// Optimize: Use getElementsByClassName (faster than jQuery selector)
		const highlights = document.getElementsByClassName( 'mdspan-front-comment' );
		if ( highlights.length > 0 ) {
			const selectedTextOffset = highlights[ 0 ].getBoundingClientRect().top + window.pageYOffset;
			if (selectedTextOffset) {
				window.scrollTo({
					top: selectedTextOffset - 200,
					behavior: 'smooth',
				});
			}
		}
	}

	// Mobile transform logic
	if (window.matchMedia('(max-width: 767px)').matches) {
		// Optimize: Use getElementsByClassName (faster than querySelector)
		const wrapper = document.getElementsByClassName( 'cf-front-comment-wrapper' )[ 0 ];
		if (wrapper) {
			wrapper.style.transform = 'translateY(100%)';
		}
	}
}


/**
 * Highlights the specified text within the provided HTML content by wrapping it
 * in a span element with the class 'mdspan-front-comment'.
 *
 * @param {string} html - The HTML content to search and highlight the text within.
 * @param {string} text - The text to search for and highlight within the HTML content.
 */
function cfHighlightText( html, text ) {
	// Create a temporary element to strip HTML tags
	const tempElement = document.createElement( 'div' );
	tempElement.innerHTML = html;
	const plainText = tempElement.textContent || tempElement.innerText || '';

	const inputText = jQuery( ".entry-content *:contains('" + text + "')" );

	let innerHTML = plainText;
	const index = innerHTML.indexOf( text );
	if ( index >= 0 ) {
		innerHTML =
			innerHTML.substring( 0, index ) +
			"<span class='mdspan-front-comment'>" +
			innerHTML.substring( index, index + text.length ) +
			'</span>' +
			innerHTML.substring( index + text.length );
		inputText[ 0 ].innerHTML = innerHTML;
	}
}

/**
 * Hides the front-end comment board by resetting the comment input, hiding
 * the comment wrapper, and hiding any success message.
 */
function cfHideFrontCommentBoard() {
	const mobileMediaQuery = window.matchMedia('(max-width: 767px)');

	// Optimize: Cache the wrapper element and use getElementById/getElementsByClassName
	const commentWrapper = document.getElementsByClassName( 'cf-front-comment-wrapper' )[ 0 ];
	if ( ! commentWrapper ) {
		return;
	}
	
	const commentField = document.getElementById( 'comment' );
	if ( commentField ) {
		commentField.value = '';
	}
	
	if (mobileMediaQuery.matches) {
		commentWrapper.style.transform = 'translateY(100%)';
	} else {
		commentWrapper.style.transform = 'translateX(100%)';
	}
	
	const innerWrapper = commentWrapper.getElementsByClassName( 'cf-front-comment-inner-wrapper' )[ 0 ];
	if ( innerWrapper ) {
		innerWrapper.style.display = 'none';
	}
	
	const ajaxSuccess = commentWrapper.getElementsByClassName( 'ajax-success' )[ 0 ];
	if ( ajaxSuccess ) {
		ajaxSuccess.style.display = 'none';
	}
}

/**
 * Validates the provided URL using a regular expression pattern.
 * The pattern checks for the following parts of a URL:
 * 
 * @param {string} url - The URL to validate.
 * @returns {boolean} Whether the URL is valid.
 */
function isValidURL( url ) {
	const pattern = new RegExp('^(https?:\\/\\/)?' + // Protocol
		'((([a-zA-Z0-9$_.+!*\'(),;-])+\\.)+[a-zA-Z]{2,})' + // Domain name
		'(\\/[a-zA-Z0-9$_.+!*\'(),;:@&=-]*)*' + // Path
		'(\\?[a-zA-Z0-9$_.+!*\'(),;:@&=-]*)?' + // Query string
		'(#[a-zA-Z0-9-]*)?$', 'i'); // Fragment
	return pattern.test( url );
}

/**
 * Function to highlight matched elements with the specified media URL.
 * The function uses querySelector to directly select the element with the matching src attribute.
 * If the matching element is found, it is highlighted by adding a red border.
 * If no matching element is found, a message is logged to the console.
 * The function also scrolls to the highlighted element using scrollIntoView with smooth behavior.
 *
 * @param {string} mediaUrl - The URL of the media element to highlight.
 * @returns {void}
 */
function cfHighlightMediaElements(mediaUrl) {
	// Optimize: Remove highlights first using getElementsByClassName (faster)
	const mediaHighlighted = document.getElementsByClassName( 'cf-media-highlighted' );
	for ( let i = 0; i < mediaHighlighted.length; i++ ) {
		mediaHighlighted[ i ].classList.remove( 'cf-media-highlighted' );
	}

	// Optimize: Search within content areas first (faster than document-wide search)
	let matchingElement = null;
	const contentAreas = document.getElementsByClassName( 'entry-content' );
	
	for ( let i = 0; i < contentAreas.length && !matchingElement; i++ ) {
		const area = contentAreas[ i ];
		const imgs = area.getElementsByTagName( 'img' );
		const audios = area.getElementsByTagName( 'audio' );
		const videos = area.getElementsByTagName( 'video' );
		
		// Check images
		for ( let j = 0; j < imgs.length && !matchingElement; j++ ) {
			if ( imgs[ j ].getAttribute( 'src' ) === mediaUrl ) {
				matchingElement = imgs[ j ];
				break;
			}
		}
		
		// Check audio
		if ( !matchingElement ) {
			for ( let j = 0; j < audios.length && !matchingElement; j++ ) {
				if ( audios[ j ].getAttribute( 'src' ) === mediaUrl ) {
					matchingElement = audios[ j ];
					break;
				}
			}
		}
		
		// Check video
		if ( !matchingElement ) {
			for ( let j = 0; j < videos.length && !matchingElement; j++ ) {
				if ( videos[ j ].getAttribute( 'src' ) === mediaUrl ) {
					matchingElement = videos[ j ];
					break;
				}
			}
		}
	}

	//If the matching element is found, highlight it
	if (matchingElement) {
		matchingElement.classList.add('cf-media-highlighted');
		matchingElement.scrollIntoView({
			behavior: 'smooth', // Smooth scroll animation
			block: 'center', // Align the element to the center of the viewport
			inline: 'nearest' // Align the element to the nearest edge (left or right)
		});
	}
	// 	const activeMark = document.querySelector(
	// 		   `mark[datatext="${datatext}"]`
	// 		 );
			
	// 		 if (activeMark) {
	// 		   activeMark.scrollIntoView({
	// 		     behavior: 'smooth',
	// 		     block: 'center'
	// 		   });
	// 		 }
}

document.addEventListener( 'DOMContentLoaded', () => {
	// Optimize: Cache wrapper and use getElementsByClassName/getElementsByTagName
	const commentWrapper = document.getElementsByClassName( 'cf-front-comment-wrapper' )[ 0 ];
	const commentForm = commentWrapper ? commentWrapper.getElementsByClassName( 'comment-form' )[ 0 ] : null;
	const submitButton = commentForm ? commentForm.querySelector( 'input[type="submit"]' ) : null;

	// Function to handle clicks and get the 'src' attribute for img and video elements
	function cfHandleImgVideoElementClick(event) {
		const clickedElement = event.target;

		if (clickedElement.tagName === 'IMG' || clickedElement.tagName === 'VIDEO') {
			const src = clickedElement.getAttribute('src');
			const xAxis = event.pageX;
			const yAxis = event.pageY - 35;
			const toolbar = document.getElementById('toolbar');
			toolbar.style.opacity = 1;
			toolbar.style.transform = `translate(${xAxis}px, ${yAxis}px)`;

			// Check if the comment form has a hidden input field for media_src
			// Optimize: Use getElementById (faster than querySelector)
			const hiddenInput = document.getElementById( 'media_src' );
			if (hiddenInput && commentForm && commentForm.contains( hiddenInput ) ) {
				hiddenInput.value = src;
			} else if ( commentForm ) {
				// Create a hidden input field to store the src value
				const newHiddenInput = document.createElement('input');
				newHiddenInput.setAttribute('type', 'hidden');
				newHiddenInput.setAttribute('name', 'media_src');
				newHiddenInput.setAttribute('value', src);
				newHiddenInput.setAttribute('id', 'media_src');
				// Optimize: Use getElementsByClassName (faster than querySelector)
				const formSubmit = commentForm.getElementsByClassName( 'form-submit' )[ 0 ];
				if ( formSubmit ) {
					formSubmit.appendChild(newHiddenInput);
				}
			}
		}
	}

	// Attach event listeners to all img and video tags on the page
	// Optimize: Search within main element first (faster than document-wide search)
	const mainElement = document.getElementsByTagName( 'main' )[ 0 ];
	if ( mainElement ) {
		const imgs = mainElement.getElementsByTagName( 'img' );
		const videos = mainElement.getElementsByTagName( 'video' );
		
		for ( let i = 0; i < imgs.length; i++ ) {
			imgs[ i ].addEventListener('click', cfHandleImgVideoElementClick);
		}
		for ( let i = 0; i < videos.length; i++ ) {
			videos[ i ].addEventListener('click', cfHandleImgVideoElementClick);
		}
	}

	// Function to handle the play event for audio elements
	function cfHandleAudioPlay(event) {
		const audioElement = event.target;
		const src = audioElement.getAttribute('src');
		const xAxis = event.target.offsetLeft;
		const yAxis = event.target.offsetTop;
		const toolbar = document.getElementById('toolbar');
		toolbar.style.opacity = 1;
		toolbar.style.transform = `translate(${xAxis}px, ${yAxis}px)`;

		// Check if the comment form has a hidden input field for media_src
		// Optimize: Use getElementById (faster than querySelector)
		const hiddenInput = document.getElementById( 'media_src' );
		if (hiddenInput && commentForm && commentForm.contains( hiddenInput ) ) {
			hiddenInput.value = src;
		} else if ( commentForm ) {
			// Create a hidden input field to store the src value
			const newHiddenInput = document.createElement('input');
			newHiddenInput.setAttribute('type', 'hidden');
			newHiddenInput.setAttribute('name', 'media_src');
			newHiddenInput.setAttribute('value', src);
			newHiddenInput.setAttribute('id', 'media_src');
			// Optimize: Use getElementsByClassName (faster than querySelector)
			const formSubmit = commentForm.getElementsByClassName( 'form-submit' )[ 0 ];
			if ( formSubmit ) {
				formSubmit.appendChild(newHiddenInput);
			}
		}
	}

	// Attach play event listener to all audio tags
	// Optimize: Search within main element first (faster than document-wide search)
	if ( mainElement ) {
		const audios = mainElement.getElementsByTagName( 'audio' );
		for ( let i = 0; i < audios.length; i++ ) {
			audios[ i ].addEventListener('play', cfHandleAudioPlay);
		}
	}

	// Handle the reply link click event.
	document.addEventListener('click', function (e) {
		if (e.target.classList.contains('comment-reply-link')) {
			const commentID = e.target.dataset.commentid;
			// Optimize: Use getElementById (faster than querySelector)
			const commentParent = document.getElementById( 'comment_parent' );

			if ( commentParent ) {
				commentParent.value = commentID;
			}

			if (submitButton) {
				submitButton.value = 'Post Reply';
			}

			// Optimize: Cache wrapper and use getElementsByClassName
			if ( commentWrapper ) {
				const replyTitle = commentWrapper.getElementsByClassName( 'comment-reply-title' )[ 0 ];
				if ( replyTitle ) {
					replyTitle.style.display = 'block';
				}
				
				const loggedInAs = commentWrapper.getElementsByClassName( 'logged-in-as' )[ 0 ];
				if ( loggedInAs ) {
					loggedInAs.style.display = 'none';
				}
			}

			// Optimize: Use getElementsByClassName (faster than querySelectorAll)
			const ajaxSuccess = document.getElementsByClassName( 'ajax-success' );
			for ( let i = 0; i < ajaxSuccess.length; i++ ) {
				ajaxSuccess[ i ].remove();
			}
		}
	} );
} );
