package Breccia.Web.imager; import org.w3c.dom.Element; import org.w3c.dom.Node; import static Breccia.parser.plain.Language.completesNewline; import static Breccia.parser.plain.Language.isDividerDrawing; import static Java.Nodes.hasName; import static Java.Nodes.isElement; import static Java.Nodes.parentElement; import static Java.Nodes.successor; import static Java.Nodes.successorElement; import static Java.Nodes.textChildFlat; public final class ImageNodes { private ImageNodes() {} /** @return The head of the given fractum, or null if there is none. * @see BreccianFileTranslator#finalHead(Element) */ public static Element head( final Node fractum ) { Element h = (Element)fractum.getFirstChild(); assert h != null; // No fractum is both headless and bodiless. if( !hasName( "Head", h )) { assert hasName( "FileFractum", fractum ); // Which alone may be headless. h = null; } return h; } /** Whether `e` is the image of a fractum. */ public static boolean isFractum( final Element e ) { return e.hasAttribute( "typestamp" ); } /** The namespace name for HTML. */ public static final String nsHTML = "http://www.w3.org/1999/xhtml"; /** The namespace name for Breccia Web Imager. */ public static final String nsImager = "data:,Breccia/Web/imager"; /** The namespace name for XML namespacing. */ public static final String nsXMLNS = "http://www.w3.org/2000/xmlns/"; /** Returns the nearest fractal ancestor of `node`, or null if there is none. * * @return The nearest ancestor of `node` that is a fractum, or null if there is none. * @see * Definition of ‘owning fractum’ */ public static Element ownerFractum( final Node node ) { Element a = parentElement( node ); while( a != null && !isFractum(a) ) a = parentElement( a ); return a; } /** Returns the same `e` if it is a fractum, otherwise `ownerFractum(e)`. * * @see * Definition of ‘owning fractum’ */ public static Element ownerFractumOrSelf( final Element e ) { return isFractum(e) ? e : ownerFractum(e); } /** Returns the nearest ancestor of `node` that is a fractal head, or null if there is none. * * @see * Definition of ‘owning fractum’ */ public static Element ownerHead( Node node ) { do node = node.getParentNode(); while( node != null && !hasName("Head",node) ); return (Element)node; } /** Returns the same `e` if it is a fractal head, otherwise `ownerHead(e)`. * * @see * Definition of ‘owning fractum’ */ public static Element ownerHeadOrSelf( final Element e ) { return hasName("Head",e) ? e : ownerHead(e); } /** The original text content of the given node prior to any translation. The original text * is recovered as the text content of the node and its descendants exclusive of any contained * within an element marked by an `{@linkplain #nsImager img}:nonOriginalText` attribute. * * @return The original text content, or the empty string if there is none. */ public static String sourceText( Node node ) { // Changing? Sync → `image.js`. if( hasAttribute_nonOriginalText( node )) return ""; node = node.cloneNode( /*deeply*/true ); for( Node p, n = successor(p = node); n != null; n = successor(p = n) ) { if( hasAttribute_nonOriginalText( n )) { n.getParentNode().removeChild( n ); n = p; }} // Resume from the predecessor of element `n`, now removed. return node.getTextContent(); } /** Returns the next titling label subsequent to `n` in document order, * inclusive of any descendants of `n`, or null if there is none. * * @see * Definition of ‘document order’ */ public static Element successorTitlingLabel( Node n ) { return successorTitlingLabel( n, null ); } /** Returns the next titling label that succeeds `n` and precedes `nBoundary` in document order, * inclusive of any descendants of `n`, or null if there is none. * * @param nBoundary The exclusive end boundary of the search beyond `n`, or null to search * the remainder of the document. * @see * Definition of ‘document order’ */ public static Element successorTitlingLabel( Node n, final Node nBoundary ) { Element e = successorElement( n ); do { if( !hasName( "DivisionLabel", e )) continue; n = e.getPreviousSibling(); assert hasName( "Granum", n ); // A granum of flat text `t` precedes all division labels. // final String t = textChildFlat( n ); /// but the imager may have wrapped that text child, e.g. with a self-hyperlink `a` element final String t = sourceText( n ); char ch; int c = t.length(); do --c; while( (ch = t.charAt(c)) == ' ' ); // Scan leftward past any plain space characters, if( completesNewline( ch )) return e; /* and there test for the presence of a newline. That preceding newline proves that the label leads the line on which it occurs, which by definition makes it a titling label. */ assert isDividerDrawing( ch ); } // The only alternative is a divider drawing character. while( (e = successorElement(e)) != nBoundary ); return null; } //// P r i v a t e //////////////////////////////////////////////////////////////////////////////////// private static boolean hasAttribute_nonOriginalText( final Node n ) { return isElement(n) && ((Element)n).hasAttributeNS(nsImager,"nonOriginalText"); }} // Changing? Sync → `image.js`. // Copyright © 2022, 2024 Michael Allan. Licence MIT.