<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:f="functions" xmlns="http://www.w3.org/1999/xhtml" version="3.0" xpath-default-namespace="http://www.w3.org/1999/xhtml" exclude-result-prefixes="#all"> <xsl:template match="/html/body"> <xsl:apply-templates mode="restructure" select="."> <xsl:with-param name="fragment" select="node()"/> </xsl:apply-templates> </xsl:template> <xsl:template mode="restructure" match="body|section|aside|nav|article"> <xsl:param name="fragment" as="node()*"/> <xsl:copy> <xsl:sequence select="@*"/> <xsl:variable name="subsections" as="element()*" select="f:except-descendants(descendant::*[f:is-sectioning-element(.)])"/> <xsl:variable name="heading" as="element()?" select="descendant::*[f:is-heading-element(.)][1] except $subsections/descendant::*"/> <xsl:if test="$heading"> <xsl:variable name="start" as="element()" select="$heading/f:propagate(.,'before')"/> <xsl:variable name="end" as="element()" select="$heading/f:propagate(.,'after')"/> <xsl:call-template name="reconstruct-tree"> <xsl:with-param name="nodes" select="f:nodes-before($fragment,$start)"/> </xsl:call-template> <xsl:sequence select="f:except-descendants( f:nodes-not-after(f:nodes-not-before($fragment,$start),$end) /descendant-or-self::node() except $heading/ancestor::*)"/> </xsl:if> <xsl:variable name="fragment" as="node()*" select="f:nodes-after($fragment,$heading/f:propagate(.,'after'))"/> <xsl:iterate select="$subsections"> <xsl:param name="remaining-content" as="node()*" select="$fragment"/> <xsl:on-completion> <xsl:call-template name="reconstruct-tree"> <xsl:with-param name="nodes" select="$remaining-content"/> </xsl:call-template> </xsl:on-completion> <xsl:variable name="start" as="element()" select="f:propagate(.,'before')"/> <xsl:variable name="end" as="element()" select="f:propagate(.,'after')"/> <xsl:call-template name="reconstruct-tree"> <xsl:with-param name="nodes" select="f:nodes-before($remaining-content,$start)"/> </xsl:call-template> <xsl:apply-templates mode="#current" select="."> <xsl:with-param name="fragment" select="f:nodes-not-after( f:nodes-not-before($remaining-content,$start), $end)"/> </xsl:apply-templates> <xsl:next-iteration> <xsl:with-param name="remaining-content" select="f:nodes-after($remaining-content,$end)"/> </xsl:next-iteration> </xsl:iterate> </xsl:copy> </xsl:template> <xsl:template name="reconstruct-tree" as="node()*"> <xsl:param name="nodes" as="node()*" required="yes"/> <xsl:choose> <xsl:when test="not($nodes/self::* |$nodes/self::text()[normalize-space(.)])"> <xsl:sequence select="$nodes"/> </xsl:when> <xsl:otherwise> <xsl:apply-templates mode="reconstruct" select="collection()/html/body"> <xsl:with-param name="nodes" tunnel="yes" select="$nodes"/> </xsl:apply-templates> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template mode="reconstruct" match="/html/body| section|aside|nav|article"> <xsl:apply-templates mode="#current" select="node()"/> </xsl:template> <xsl:template mode="reconstruct" match="node()"> <xsl:param name="nodes" tunnel="yes" as="node()*" required="yes"/> <xsl:choose> <xsl:when test="ancestor-or-self::node() intersect $nodes"> <xsl:sequence select="."/> </xsl:when> <xsl:when test="self::*[descendant::node() intersect $nodes]"> <xsl:copy> <xsl:sequence select="@* except @id"/> <xsl:if test="not((descendant::node()[self::* |self::text()[normalize-space(.)]] except descendant::*[f:is-heading-element(.)]/descendant-or-self::node()) intersect (descendant::node() intersect $nodes)[1]/preceding::node())"> <xsl:sequence select="@id"/> </xsl:if> <xsl:apply-templates mode="#current" select="node()"/> </xsl:copy> </xsl:when> </xsl:choose> </xsl:template> <xsl:function name="f:is-sectioning-element" as="xs:boolean"> <xsl:param name="element" as="element()"/> <xsl:sequence select="exists($element/self::body |$element/self::section |$element/self::aside |$element/self::nav |$element/self::article)"/> </xsl:function> <xsl:function name="f:is-heading-element" as="xs:boolean"> <xsl:param name="element" as="element()"/> <xsl:sequence select="exists($element/self::h1 |$element/self::h2 |$element/self::h3 |$element/self::h4 |$element/self::h5 |$element/self::h6 |$element/self::hgroup |$element/self::header[*[f:is-heading-element(.)]])"/> </xsl:function> <xsl:function name="f:propagate" as="node()"> <xsl:param name="split-point" as="node()"/> <xsl:param name="side" as="xs:string"/> <xsl:choose> <xsl:when test="$side='before'"> <xsl:sequence select="if ($split-point/parent::* and not($split-point /preceding-sibling::node()[self::* |self::text()[normalize-space(.)]])) then f:propagate($split-point/parent::*,$side) else $split-point"/> </xsl:when> <xsl:otherwise> <xsl:sequence select="if ($split-point/parent::* and not($split-point /following-sibling::node()[self::* |self::text()[normalize-space(.)]])) then f:propagate($split-point/parent::*,$side) else $split-point"/> </xsl:otherwise> </xsl:choose> </xsl:function> <xsl:function name="f:nodes-before" as="node()*"> <xsl:param name="nodes" as="node()*"/> <xsl:param name="split-before" as="node()?"/> <xsl:sequence select="if (not(exists($split-before))) then $nodes else f:except-descendants( $nodes/descendant-or-self::node() intersect $split-before/preceding::node())"/> </xsl:function> <xsl:function name="f:nodes-not-before" as="node()*"> <xsl:param name="nodes" as="node()*"/> <xsl:param name="split-before" as="node()?"/> <xsl:sequence select="if (not(exists($split-before))) then () else f:except-descendants( $nodes/descendant-or-self::node() except $split-before/(ancestor::node()|preceding::node()))"/> </xsl:function> <xsl:function name="f:nodes-after" as="node()*"> <xsl:param name="nodes" as="node()*"/> <xsl:param name="split-after" as="node()?"/> <xsl:sequence select="if (not(exists($split-after))) then $nodes else f:except-descendants( $nodes/descendant-or-self::node() intersect $split-after/following::node())"/> </xsl:function> <xsl:function name="f:nodes-not-after" as="node()*"> <xsl:param name="nodes" as="node()*"/> <xsl:param name="split-after" as="node()?"/> <xsl:sequence select="if (not(exists($split-after))) then () else f:except-descendants( $nodes/descendant-or-self::node() except $split-after/(ancestor::node()|following::node()))"/> </xsl:function> <xsl:function name="f:except-descendants"> <xsl:param name="nodes" as="node()*"/> <xsl:sequence select="$nodes except $nodes/descendant::node()"/> </xsl:function> <xsl:template mode="#default" match="@*|node()"> <xsl:copy> <xsl:sequence select="@*"/> <xsl:apply-templates mode="#current"/> </xsl:copy> </xsl:template> </xsl:stylesheet>