<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:pf="http://www.daisy.org/ns/pipeline/functions" xmlns:f="http://www.daisy.org/ns/pipeline/internal-functions" xmlns:java="implemented-in-java" exclude-result-prefixes="#all" version="2.0">
    
    
    <xsl:function name="pf:base-uri" as="xs:anyURI?">
        <xsl:param name="arg" as="node()?"/>
        <xsl:choose>
            <xsl:when test="exists($arg/ancestor-or-self::*[@xml:base])">
                
                
                <xsl:variable name="base" as="element()" select="$arg/ancestor-or-self::*[@xml:base][1]"/>
                <xsl:sequence select="if (exists($base/parent::*))                                       then resolve-uri($base/@xml:base, pf:base-uri($base/parent::*))                                       else $base/@xml:base"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:sequence select="pf:document-uri($arg)"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:function>
    
    
    <xsl:function name="pf:document-uri" as="xs:anyURI?">
        <xsl:param name="arg" as="node()?"/>
        <xsl:sequence select="base-uri(pf:root($arg))"/>
    </xsl:function>
    
    
    <xsl:function name="pf:root" as="node()?">
        <xsl:param name="arg" as="node()?"/>
        <xsl:variable name="root" as="node()?" select="root($arg)"/>
        
        <xsl:sequence select="collection()[. is $root][1]"/>
    </xsl:function>
    
    
    <xsl:function name="pf:resolve-uri" as="xs:anyURI?">
        <xsl:param name="relative" as="xs:string?"/>
        <xsl:param name="arg"/>
        <xsl:if test="exists($arg)">
            <xsl:sequence select="resolve-uri(                                     $relative,                                     if ($arg instance of node())                                       then pf:base-uri($arg)                                       else $arg)"/>
        </xsl:if>
    </xsl:function>
    
    
    
    <xsl:function name="pf:tokenize-uri" as="xs:string*">
        <xsl:param name="uri" as="xs:string?"/>
        
        <xsl:analyze-string select="concat('X',$uri)" regex="^X((jar:file|[^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?">
            <xsl:matching-substring>
                <xsl:sequence select="(                     if (regex-group(1)) then regex-group(2) else '',                     if (regex-group(3)) then regex-group(4) else '',                     regex-group(5),                     if (regex-group(6)) then regex-group(7) else '',                     if (regex-group(8)) then regex-group(9) else ''                 )"/>
            </xsl:matching-substring>
        </xsl:analyze-string>
    </xsl:function>
    <xsl:function name="pf:recompose-uri" as="xs:string">
        <xsl:param name="tokens" as="xs:string*"/>
        <xsl:sequence select="string-join((             if($tokens[1]) then ($tokens[1],':') else (),             if($tokens[2]) then ('//',$tokens[2]) else if($tokens[1] and not($tokens[1]=('mailto','file','jar:file'))) then '/' else (),             $tokens[3],             if($tokens[4]) then ('?',$tokens[4]) else (),             if($tokens[5]) then ('#',$tokens[5]) else ()             ),'')"/>
    </xsl:function>
    <xsl:function name="pf:is-absolute" as="xs:boolean">
        <xsl:param name="uri" as="xs:string?"/>
        <xsl:variable name="tokens" select="pf:tokenize-uri(normalize-space($uri))"/>
        <xsl:sequence select="starts-with($tokens[3],'/')"/>
    </xsl:function>
    <xsl:function name="pf:is-relative" as="xs:boolean">
        <xsl:param name="uri" as="xs:string?"/>
        <xsl:variable name="tokens" select="pf:tokenize-uri(normalize-space($uri))"/>
        <xsl:sequence select="not($tokens[1]) and not(starts-with($tokens[3],'/'))"/>
    </xsl:function>
    <xsl:function name="pf:get-scheme" as="xs:string">
        <xsl:param name="uri" as="xs:string?"/>
        <xsl:sequence select="pf:tokenize-uri(normalize-space($uri))[1]"/>
    </xsl:function>
    
    <xsl:function name="pf:get-path" as="xs:string">
        <xsl:param name="uri" as="xs:string?"/>
        <xsl:sequence select="pf:tokenize-uri(normalize-space($uri))[3]"/>
    </xsl:function>
    
    <xsl:function name="pf:get-extension" as="xs:string">
        <xsl:param name="uri" as="xs:string?"/>
        <xsl:sequence select="lower-case(replace(pf:get-path($uri),'^.*\.([^.]*)$','$1'))"/>
    </xsl:function>
    
    <xsl:function name="pf:replace-path" as="xs:string">
        <xsl:param name="uri" as="xs:string?"/>
        <xsl:param name="path" as="xs:string?"/>
        <xsl:variable name="tokens" select="pf:tokenize-uri(normalize-space($uri))" as="xs:string*"/>
        <xsl:sequence select="pf:recompose-uri(($tokens[1],$tokens[2],pf:normalize-path($path),$tokens[4],$tokens[5]))"/>
    </xsl:function>
    <doc xmlns="http://www.oxygenxml.com/ns/doc/xsl">
        <desc>
            <p>Normalize a URI.</p>
            
            
        </desc>
        <param name="uri">
            <p>A relative or absolute URI</p>
        </param>
        <param name="fragment">
            <p/>
        </param>
        <return>
            <p>A relative or absolute URI</p>
        </return>
    </doc>
    <java:function name="pf:normalize-uri" as="xs:string">
        <xsl:param name="uri" as="xs:string?"/>
    </java:function>
    
    <java:function name="pf:normalize-uri" as="xs:string">
        <xsl:param name="uri" as="xs:string?"/>
        <xsl:param name="fragment" as="xs:boolean"/>
    </java:function>
    <xsl:function name="pf:normalize-uri-legacy" as="xs:string">
        <xsl:param name="uri" as="xs:string?"/>
        <xsl:sequence select="pf:normalize-uri($uri,true())"/>
    </xsl:function>
    <xsl:function name="pf:normalize-uri-legacy" as="xs:string">
        <xsl:param name="uri" as="xs:string?"/>
        <xsl:param name="fragment" as="xs:boolean"/>
        
        <xsl:variable name="uri">
            <xsl:choose>
                <xsl:when test="$uri">
                    <xsl:variable name="cp-base" select="string-to-codepoints('0A')" as="xs:integer+"/>
                    <xsl:analyze-string select="$uri" regex="(%[0-9A-F]{{2}})+" flags="i">
                        <xsl:matching-substring>
                            
                            <xsl:variable name="upper-case" select="upper-case(.)"/>
                            
                            <xsl:variable name="utf8-bytes" as="xs:integer+">
                                <xsl:analyze-string select="$upper-case" regex="%([0-9A-F]{{2}})" flags="i">
                                    <xsl:matching-substring>
                                        <xsl:variable name="nibble-pair" select="                                            for $nibble-char in string-to-codepoints(upper-case(regex-group(1))) return                                            if ($nibble-char ge $cp-base[2]) then                                            $nibble-char - $cp-base[2] + 10                                            else                                            $nibble-char - $cp-base[1]" as="xs:integer+"/>
                                        <xsl:sequence select="$nibble-pair[1] * 16 + $nibble-pair[2]"/>
                                    </xsl:matching-substring>
                                </xsl:analyze-string>
                            </xsl:variable>
                            <xsl:value-of select="if ($utf8-bytes = (65 to 90, 97 to 122, 48 to 57, 45, 46, 95, 126)) then codepoints-to-string(pf:utf8-decode($utf8-bytes)) else $upper-case"/>
                        </xsl:matching-substring>
                        <xsl:non-matching-substring>
                            <xsl:value-of select="."/>
                        </xsl:non-matching-substring>
                    </xsl:analyze-string>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="$uri"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:variable>
        <xsl:variable name="tokens" select="pf:tokenize-uri(normalize-space($uri))" as="xs:string*"/>
        <xsl:variable name="scheme" select="$tokens[1]"/>
        <xsl:variable name="authority" select="$tokens[2]"/>
        <xsl:variable name="path" select="$tokens[3]"/>
        <xsl:variable name="query" select="$tokens[4]"/>
        <xsl:variable name="fragment" select="if ($fragment) then $tokens[5] else ()"/>
        
        <xsl:variable name="scheme" select="lower-case($scheme)"/>
        <xsl:variable name="authority" select="lower-case($authority)"/>
        
        <xsl:variable name="authority" select="if ($scheme='http' and ends-with($authority,':80')) then substring($authority,1,string-length($authority)-3) else $authority"/>
        
        <xsl:variable name="path" select="pf:normalize-path($path)"/>
        <xsl:variable name="uri" select="pf:recompose-uri(($scheme,$authority,$path,$query,$fragment))"/>
        <xsl:variable name="uri" select="pf:file-expand83($uri)" use-when="function-available('pf:file-expand83')"/>
        <xsl:sequence select="iri-to-uri($uri)"/>
    </xsl:function>
    <xsl:function name="pf:relativize-uri" as="xs:string">
        <xsl:param name="uri" as="xs:string?"/>
        <xsl:param name="base" as="xs:string?"/>
        <xsl:variable name="uri-tokens" select="pf:tokenize-uri(pf:normalize-uri($uri))"/>
        <xsl:variable name="base-tokens" select="pf:tokenize-uri(pf:normalize-uri($base))"/>
        <xsl:choose>
            <xsl:when test="(not($uri-tokens[1]) or $uri-tokens[1]=$base-tokens[1]) and (not($uri-tokens[2]) or $uri-tokens[2]=$base-tokens[2])">
                <xsl:variable name="relative-path" select="pf:relativize-path($uri-tokens[3],$base-tokens[3])"/>
                <xsl:choose>
                    <xsl:when test="$relative-path=replace($base-tokens[3],'^.*/([^/]+)$','$1') and $uri-tokens[4]='' and $uri-tokens[5]!=''">
                        
                        <xsl:sequence select="concat('#',$uri-tokens[5])"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:sequence select="pf:recompose-uri(('','',$relative-path,$uri-tokens[4],$uri-tokens[5]))"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:when>
            <xsl:otherwise>
                <xsl:sequence select="pf:recompose-uri($uri-tokens)"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:function>
    <xsl:function name="pf:normalize-path" as="xs:string?">
        <xsl:param name="path" as="xs:string?"/>
        <xsl:variable name="normalized" as="xs:string?" select="             replace(                 replace(                     replace(                         replace(                             replace($path,'^(\.(/|$))+','')                         ,'/(\.(/|$))+','/')                     ,'/+','/')                 ,'(^|/)\.\.$','$1../')             ,'^/(\.\./)+','/')             "/>
        <xsl:sequence select="             if (matches($normalized,'([^/\.]|\.[^/\.]|[^/\.]\.|[^/]{3,})/\.\./')) then             pf:normalize-path(replace($normalized,'([^/\.]|\.[^/\.]|[^/\.]\.|[^/]{3,})/\.\./',''))             else              $normalized"/>
    </xsl:function>
    <xsl:function name="pf:relativize-path" as="xs:string">
        <xsl:param name="path" as="xs:string?"/>
        <xsl:param name="base" as="xs:string?"/>
        <xsl:choose>
            <xsl:when test="starts-with($path,'/')">
                <xsl:variable name="path-segments" select="tokenize($path, '/')"/>
                <xsl:variable name="base-segments" select="tokenize($base, '/')[position()!=last()]"/>
                <xsl:variable name="common-prefix-length" select="                     (for $i in 1 to min((count($base-segments),count($path-segments) -1)) return                          if($base-segments[$i] eq $path-segments[$i]) then () else $i -1                     ,min((count($base-segments),count($path-segments) -1)))[1]"/>
                <xsl:variable name="upSteps" select="count($base-segments) -$common-prefix-length"/>
                <xsl:sequence select="string-join((                     for $i in 1 to $upSteps                         return '..',                     for $i in 1 to count($path-segments) - $common-prefix-length                          return $path-segments[$common-prefix-length + $i]                     ),'/')"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:sequence select="$path"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:function>
    <xsl:function name="pf:longest-common-uri" as="xs:string">
        <xsl:param name="uris" as="xs:string*"/>
        <xsl:choose>
            <xsl:when test="count($uris)=1">
                <xsl:value-of select="$uris"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:variable name="tokens" select="pf:tokenize-uri(normalize-space($uris[1]))" as="xs:string*"/>
                <xsl:variable name="uri-filter" select="if (not($tokens[1])) then '' else if ($tokens[2]) then concat($tokens[1],'://',$tokens[2]) else $tokens[1]"/>
                <xsl:variable name="uris" select="$uris[starts-with(.,$uri-filter)]"/>
                <xsl:variable name="main-uri" select="for $part in tokenize(replace(pf:normalize-uri(replace($uris[1],'[#\?].*$','')),'/+','SLASH|/'),'/') return replace($part,'SLASH\|$','/')"/>
                <xsl:variable name="count-common" as="xs:integer*">
                    <xsl:for-each select="$uris[position() > 1]">
                        <xsl:variable name="compare-uri" select="for $part in tokenize(replace(pf:normalize-uri(.),'/+','SLASH|/'),'/') return replace($part,'SLASH\|$','/')"/>
                        <xsl:sequence select="min(for $i in 1 to count($main-uri) return if ($main-uri[$i]=$compare-uri[$i]) then () else $i)"/>
                    </xsl:for-each>
                </xsl:variable>
                <xsl:variable name="count-common" select="min(($count-common, count($main-uri)))"/>
                <xsl:variable name="longest-common" select="$main-uri[position() < $count-common]"/>
                <xsl:variable name="longest-common" select="concat($longest-common[1],if (matches($longest-common[1],'^\w+:/$') and (not($tokens[1]='file') or $tokens[2])) then '/' else '',string-join($longest-common[position()>1],''))"/>
                <xsl:variable name="longest-common" select="replace($longest-common, '[^/]+$', '')"/>
                <xsl:value-of select="$longest-common"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:function>
    
    <xsl:function name="pf:unescape-uri" as="xs:string">
        <xsl:param name="string" as="xs:string?"/>
        <xsl:variable name="cp-base" select="string-to-codepoints('0A')" as="xs:integer+"/>
        <xsl:variable name="result">
            <xsl:analyze-string select="$string" regex="(%[0-9A-F]{{2}})+" flags="i">
                <xsl:matching-substring>
                    <xsl:variable name="utf8-bytes" as="xs:integer+">
                        <xsl:analyze-string select="." regex="%([0-9A-F]{{2}})" flags="i">
                            <xsl:matching-substring>
                                <xsl:variable name="nibble-pair" select="                                     for $nibble-char in string-to-codepoints( upper-case(regex-group(1))) return                                     if ($nibble-char ge $cp-base[2]) then                                     $nibble-char - $cp-base[2] + 10                                     else                                     $nibble-char - $cp-base[1]" as="xs:integer+"/>
                                <xsl:sequence select="$nibble-pair[1] * 16 + $nibble-pair[2]"/>
                            </xsl:matching-substring>
                        </xsl:analyze-string>
                    </xsl:variable>
                    <xsl:value-of select="codepoints-to-string( pf:utf8-decode( $utf8-bytes))"/>
                </xsl:matching-substring>
                <xsl:non-matching-substring>
                    <xsl:value-of select="."/>
                </xsl:non-matching-substring>
            </xsl:analyze-string>
        </xsl:variable>
        <xsl:sequence select="string($result)"/>
    </xsl:function>
    <xsl:function name="pf:utf8-decode" as="xs:integer*">
        <xsl:param name="bytes" as="xs:integer*"/>
        <xsl:choose>
            <xsl:when test="empty($bytes)"/>
            <xsl:when test="$bytes[1] eq 0">
                
                <xsl:sequence select="pf:utf8-decode( remove( $bytes, 1))"/>
            </xsl:when>
            <xsl:when test="$bytes[1] le 127">
                <xsl:sequence select="$bytes[1], pf:utf8-decode( remove( $bytes, 1))"/>
            </xsl:when>
            <xsl:when test="$bytes[1] lt 224">
                <xsl:sequence select="                     ((($bytes[1] - 192) * 64) +                     ($bytes[2] - 128)        ),                     pf:utf8-decode( remove( remove( $bytes, 1), 1))"/>
            </xsl:when>
            <xsl:when test="$bytes[1] lt 240">
                <xsl:sequence select="                     ((($bytes[1] - 224) * 4096) +                     (($bytes[2] - 128) *   64) +                     ($bytes[3] - 128)          ),                     pf:utf8-decode( remove( remove( remove( $bytes, 1), 1), 1))"/>
            </xsl:when>
            <xsl:when test="$bytes[1] lt 248">
                <xsl:sequence select="                     ((($bytes[1] - 224) * 262144) +                     (($bytes[2] - 128) *   4096) +                     (($bytes[3] - 128) *     64) +                     ($bytes[4] - 128)            ),                     pf:utf8-decode( $bytes[position() gt 4])"/>
            </xsl:when>
            <xsl:otherwise>
                
                <xsl:sequence select="pf:utf8-decode( remove( $bytes, 1))"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:function>
</xsl:stylesheet>