Source: modules/paged-media/splits.js

import Handler from "../handler.js";

/**
 * Handles split content across paginated pages.
 *
 * When content is split across pages (e.g., footnotes or paragraphs),
 * this handler links the split parts together using data attributes
 * (`data-split-from`, `data-split-to`, etc.) to assist layout engines
 * or post-processing logic.
 */
class Splits extends Handler {
	/**
	 * Create a new Splits handler instance.
	 *
	 * @param {Object} chunker - The chunker instance used for page breaking.
	 * @param {Object} polisher - The polisher instance used for styling.
	 * @param {Object} caller - The orchestrating object coordinating handlers.
	 */
	constructor(chunker, polisher, caller) {
		super(chunker, polisher, caller);
	}

	/**
	 * Called after the layout of each page is completed.
	 *
	 * - Detects elements on the current page that have been split from a previous page.
	 * - Finds the original element on the previous page and adds metadata to link them.
	 * - Applies alignment adjustments to the original element.
	 *
	 * @param {HTMLElement} pageElement - The root element of the current page.
	 * @param {Object} page - Page metadata object.
	 * @param {Object|null} breakToken - Information about the point where the content was split.
	 * @param {Object} chunker - The chunker instance.
	 */
	afterPageLayout(pageElement, page, breakToken, chunker) {
		let splits = Array.from(pageElement.querySelectorAll("[data-split-from]"));
		let pages = pageElement.parentNode;
		let index = Array.prototype.indexOf.call(pages.children, pageElement);
		let prevPage;

		// If this is the first page, there's no previous page to compare to
		if (index === 0) {
			return;
		}

		prevPage = pages.children[index - 1];

		let from; // Capture the last "from" element for alignment handling

		splits.forEach((split) => {
			let ref = split.dataset.ref;

			// Find the original element that was split
			from = prevPage.querySelector("[data-ref='" + ref + "']");

			if (from) {
				// Link the original element to its split counterpart
				from.dataset.splitTo = ref;

				// Mark the element as the original split source if it's not already a split
				if (!from.dataset.splitFrom) {
					from.dataset.splitOriginal = true;
				}
			}
		});

		// Fix alignment on the deepest original split element (only the last one in loop)
		if (from) {
			this.handleAlignment(from);
		}
	}

	/**
	 * Adjusts alignment metadata for a split element to preserve proper justification.
	 *
	 * This ensures that the last line of split content aligns correctly,
	 * particularly when text-align is set to `justify`.
	 *
	 * @param {HTMLElement} node - The original DOM node that was split.
	 */
	handleAlignment(node) {
		let styles = window.getComputedStyle(node);
		let align = styles["text-align"];
		let alignLast = styles["text-align-last"];

		node.dataset.lastSplitElement = "true";

		if (align === "justify" && alignLast === "auto") {
			node.dataset.alignLastSplitElement = "justify";
		} else {
			node.dataset.alignLastSplitElement = alignLast;
		}
	}
}

export default Splits;