helpers/mimeXhtmlPart-make-paragraphs.xsl
author František Kučera <franta-hg@frantovo.cz>
Wed Dec 31 12:07:40 2014 +0100 (2014-12-31)
changeset 120 bb1c8a7b774c
parent 119 f5b57e221e38
permissions -rwxr-xr-x
XSLT: licence – GNU GPLv3
     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <!--
     3 Copyright © 2014 František Kučera (frantovo.cz)
     4 
     5 This program is free software: you can redistribute it and/or modify
     6 it under the terms of the GNU General Public License as published by
     7 the Free Software Foundation, either version 3 of the License, or
     8 (at your option) any later version.
     9 
    10 This program is distributed in the hope that it will be useful,
    11 but WITHOUT ANY WARRANTY; without even the implied warranty of
    12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13 GNU General Public License for more details.
    14 
    15 You should have received a copy of the GNU General Public License
    16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17 -->
    18 <xsl:stylesheet version="2.0"
    19 	xmlns="http://www.w3.org/1999/xhtml"
    20 	xmlns:h="http://www.w3.org/1999/xhtml"
    21 	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    22 	xmlns:fn="http://www.w3.org/2005/xpath-functions"
    23 	xmlns:xs="http://www.w3.org/2001/XMLSchema"
    24 	xmlns:o="https://trac.frantovo.cz/odstavcovac-TODO-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-/wiki/xmlns/odstavcovac"
    25 	exclude-result-prefixes="fn h xs o">
    26 	<xsl:output method="xml" indent="yes" encoding="UTF-8" omit-xml-declaration="yes"/>
    27 	
    28 	<xsl:param name="cssTřída"/>
    29 	
    30 	<!-- Celý dokument -->
    31 	<xsl:template match="/">
    32 		<html>
    33 			<body>
    34 			
    35 				<xsl:variable name="prvníKolo">
    36 					<xsl:apply-templates select="h:html/h:body/node()" mode="prvníKolo"/>
    37 				</xsl:variable>
    38 				
    39 				<xsl:variable name="druhéKolo">
    40 					<xsl:apply-templates select="$prvníKolo" mode="druhéKolo"/>
    41 				</xsl:variable>
    42 				
    43 				<xsl:apply-templates select="$druhéKolo" mode="třetíKolo"/>
    44 				
    45 			</body>
    46 		</html>
    47 	</xsl:template>
    48 
    49 
    50 	<!-- Kopírujeme elementy, ale vynecháme nepoužité xmlns deklarace: -->
    51 	<xsl:template match="*" mode="kopíruj">
    52 		<xsl:element name="{name()}">
    53 			<xsl:copy-of select="@*"/>
    54 			<xsl:apply-templates mode="kopíruj"/>
    55 		</xsl:element>
    56 	</xsl:template>
    57 
    58 
    59 	<!-- Mezi odstavci je prázdný řádek, můžou být mezery/tabulátory. -->
    60 	<xsl:variable name="oddělovač" select="'\n\s*\n\s*'"/>
    61 
    62 
    63 	<!-- Funkce: zda jde o XHTML inline element – může se vyskytovat uvnitř odstavců. -->
    64 	<xsl:template name="inlineElement" as="xs:boolean">
    65 		<xsl:param name="prvek"/>
    66 		<xsl:sequence select="
    67 			$prvek/name() = 'a' or
    68 			$prvek/name() = 'abbr' or
    69 			$prvek/name() = 'acronym' or
    70 			$prvek/name() = 'b' or
    71 			$prvek/name() = 'br' or
    72 			$prvek/name() = 'cite' or
    73 			$prvek/name() = 'code' or
    74 			$prvek/name() = 'em' or
    75 			$prvek/name() = 'i' or
    76 			$prvek/name() = 'img' or
    77 			$prvek/name() = 'q' or
    78 			$prvek/name() = 'span' or
    79 			$prvek/name() = 'strong' or
    80 			$prvek/name() = 'sub' or
    81 			$prvek/name() = 'sup' or
    82 			$prvek/name() = 'tt' or
    83 			$prvek/name() = 'u' or
    84 			$prvek/name() = 'var'
    85 			"/>
    86 		<!-- …případně další, pokud je budeme chtít podporovat. -->
    87 	</xsl:template>
    88 
    89 
    90 	<!-- Funkce: zda je prvek začátkem odstavce. -->
    91 	<xsl:template name="začátekOdstavce" as="xs:boolean">
    92 		<xsl:param name="prvek"/>
    93 
    94 		<xsl:variable name="inlineElement" as="xs:boolean">
    95 			<xsl:call-template name="inlineElement"><xsl:with-param name="prvek" select="$prvek"/></xsl:call-template>
    96 		</xsl:variable>
    97 
    98 		<xsl:variable name="předchůdce" select="$prvek/preceding-sibling::node()[1]"/>
    99 
   100 		<xsl:variable name="inlineElementPředchůdce" as="xs:boolean">
   101 			<xsl:call-template name="inlineElement"><xsl:with-param name="prvek" select="$předchůdce"/></xsl:call-template>
   102 		</xsl:variable>
   103 
   104 		<xsl:variable name="textovýUzel" select="boolean($prvek/self::text())"/>
   105 
   106 		<xsl:sequence select="
   107 			($inlineElement or $textovýUzel)
   108 			and
   109 			(
   110 				($inlineElementPředchůdce and matches($prvek, concat('^', $oddělovač, '.*')))
   111 				or
   112 				not($inlineElementPředchůdce)
   113 				or
   114 				not($předchůdce)
   115 			)
   116 			and
   117 			(
   118 				not($předchůdce/self::text())
   119 				or
   120 				matches($předchůdce/self::text(), concat('.*', $oddělovač, '$'))
   121 			)
   122 			"/>
   123 	</xsl:template>
   124 
   125 
   126 	<!--
   127 		V prvním kole zavřeme volný text a inline elementy do značek <o:odstavec typ=""/>,
   128 		kde typ může být "začátek", což značí, že se jedná o první část budoucího odstavce <p/>.
   129 	-->
   130 	<xsl:template match="text()" mode="prvníKolo">
   131 
   132 		<xsl:variable name="začátekOdstavce" as="xs:boolean">
   133 			<xsl:call-template name="začátekOdstavce">
   134 				<xsl:with-param name="prvek" select="."/>
   135 			</xsl:call-template>
   136 		</xsl:variable>
   137 
   138 		<xsl:for-each select="fn:tokenize(., $oddělovač)">
   139 			<xsl:element name="o:odstavec">
   140 				<xsl:if test="$začátekOdstavce or not(position() = 1)">
   141 					<xsl:attribute name="typ">začátek</xsl:attribute>
   142 				</xsl:if>
   143 				<xsl:value-of select="."/>
   144 			</xsl:element>
   145 		</xsl:for-each>
   146 
   147 	</xsl:template>
   148 
   149 	<!--
   150 		Inline elementy zavíráme do <o:odstavec typ=""/>,
   151 		ostatní vkládáme, jak jsou.
   152 	-->
   153 	<xsl:template match="*" mode="prvníKolo">
   154 
   155 		<xsl:variable name="inlineElement" as="xs:boolean">
   156 			<xsl:call-template name="inlineElement">
   157 				<xsl:with-param name="prvek" select="."/>
   158 			</xsl:call-template>
   159 		</xsl:variable>
   160 
   161 		<xsl:choose>
   162 			<!-- TODO: zvláštní šablona (match="…") pro inline elementy místo větvení? -->
   163 			<xsl:when test="$inlineElement">
   164 				<xsl:variable name="začátekOdstavce" as="xs:boolean">
   165 					<xsl:call-template name="začátekOdstavce">
   166 						<xsl:with-param name="prvek" select="."/>
   167 					</xsl:call-template>
   168 				</xsl:variable>
   169 				<xsl:element name="o:odstavec">
   170 					<xsl:if test="$začátekOdstavce">
   171 						<xsl:attribute name="typ">začátek</xsl:attribute>
   172 					</xsl:if>
   173 					<xsl:copy-of select="."/>
   174 				</xsl:element>
   175 			</xsl:when>
   176 			<xsl:otherwise>
   177 				<xsl:copy-of select="."/>
   178 			</xsl:otherwise>
   179 		</xsl:choose>
   180 
   181 	</xsl:template>
   182 
   183 	<!-- V druhém kole spojíme jednotlivé části odstavců. -->
   184 	<xsl:template match="o:odstavec[@typ='začátek']" mode="druhéKolo">
   185 		<o:odstavec>
   186 			<xsl:call-template name="spojOdstavce">
   187 				<xsl:with-param name="část" select="."/>
   188 			</xsl:call-template>
   189 		</o:odstavec>
   190 	</xsl:template>
   191 	<!-- Následující části odstavce přeskočíme – postará se o ně vnitřní smyčka volaná z předchozí šablony. -->
   192 	<xsl:template match="o:odstavec" mode="druhéKolo"/>
   193 	<!-- Neinline (blokové) elementy vložíme, jak jsou. -->
   194 	<xsl:template match="*" mode="druhéKolo">
   195 		<xsl:copy-of select="."/>
   196 	</xsl:template>
   197 
   198 
   199 	<!--
   200 		Za první část (parametr, <o:odstavec typ="začátek"/>) resp. její vnitřek
   201 		připojíme (rekurze) všechny další části téhož odstavce (oddělíme mezerou).
   202 		Konec odstavce poznáme tak, že následovník je něco jiného než <o:odstavec/> nebo má atribut typ="začátek".
   203 	-->
   204 	<xsl:template name="spojOdstavce">
   205 		<xsl:param name="část"/>
   206 		<xsl:copy-of select="$část/child::node()"/>
   207 		<xsl:variable name="následovník" select="$část/following-sibling::node()[1]"/>
   208 		<xsl:if test="$následovník/name() = 'o:odstavec' and not($následovník/@typ = 'začátek')">
   209 			<xsl:call-template name="spojOdstavce">
   210 				<xsl:with-param name="část" select="$následovník"/>
   211 			</xsl:call-template>
   212 		</xsl:if>
   213 	</xsl:template>
   214 
   215 
   216 	<!-- Ve třetím kole smažeme prázdné mešuge odstavce. -->
   217 	<xsl:template mode="třetíKolo" match="o:odstavec[
   218 		count(child::node()) = 0 
   219 		or 
   220 		(
   221 			count(child::node()) = 1 
   222 			and
   223 			text()
   224 			and
   225 			matches(text(), '^\s*$')
   226 		)
   227 		]">
   228 		<xsl:text> </xsl:text>
   229 	</xsl:template>
   230 	<!-- Převedeme z <o:odstavec/> na <p/> -->
   231 	<xsl:template match="o:odstavec" mode="třetíKolo">
   232 		<xsl:element name="p">
   233 			<xsl:if test="$cssTřída">
   234 				<xsl:attribute name="class"><xsl:value-of select="$cssTřída"/></xsl:attribute>
   235 			</xsl:if>
   236 			<xsl:apply-templates select="child::node()" mode="kopíruj"/>
   237 		</xsl:element>
   238 	</xsl:template>
   239 	<!-- Všechno ostatní zkopírujeme, jak je. -->
   240 	<xsl:template match="*" mode="třetíKolo">
   241 		<xsl:copy-of select="."/>
   242 	</xsl:template>
   243 
   244 
   245 </xsl:stylesheet>