helpers/mimeXhtmlPart-make-paragraphs.xsl
author František Kučera <franta-hg@frantovo.cz>
Sun Oct 16 20:55:46 2011 +0200 (2011-10-16)
changeset 81 b51ab80c7a9d
child 83 668014315a54
permissions -rw-r--r--
Drupal: XSL pro vytváření <p>odstavců</p> z textu přímo uvnitř html/body
hranice mezi odstavci se poznají podle prázdného řádku (případně podle blokového elementu).
Naopak inline elementy jsou zahrnuty do odstavce. Příklad

První odstavec.
Druhý řádek téhož odstavce.

Druhý odstavec obsahující <em>nějaký inline element</em>.
Stále jsme v druhém odstavci.

Třetí odstavec…
<p>skutečný odstavec, jak to má být</p>
Toto už je čtvrtý resp. pátý odstavec, přestože tu nebyl žádný prázdný řádek.
     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <xsl:stylesheet version="2.0"
     3 	xmlns="http://www.w3.org/1999/xhtml"
     4 	xmlns:h="http://www.w3.org/1999/xhtml"
     5 	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     6 	xmlns:fn="http://www.w3.org/2005/xpath-functions"
     7 	xmlns:svg="http://www.w3.org/2000/svg"
     8 	xmlns:xs="http://www.w3.org/2001/XMLSchema"
     9 	xmlns:o="https://trac.frantovo.cz/odstavcovac-TODO-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-/wiki/xmlns/odstavcovac"
    10 	exclude-result-prefixes="fn h xs">
    11 	<xsl:output method="xml" indent="yes" encoding="UTF-8"/>
    12 	
    13 	
    14 	<!-- Celý dokument -->
    15 	<xsl:template match="/">
    16 		<html>
    17 			<head>
    18 				<style type="text/css">
    19 					.mešuge {
    20 						background-color: #afa;
    21 						border: 1px solid #55f;
    22 					}
    23 				</style>
    24 			</head>
    25 
    26 			<body>
    27 			
    28 				<xsl:variable name="prvníKolo">
    29 					<xsl:apply-templates select="h:html/h:body/node()" mode="prvníKolo"/>
    30 				</xsl:variable>
    31 				
    32 				<xsl:variable name="druhéKolo">
    33 					<xsl:apply-templates select="$prvníKolo" mode="druhéKolo"/>
    34 				</xsl:variable>
    35 				
    36 				<xsl:apply-templates select="$druhéKolo" mode="třetíKolo"/>
    37 				
    38 			</body>
    39 		</html>
    40 	</xsl:template>
    41     
    42     
    43     <!-- Mezi odstavci je prázdný řádek, můžou být mezery/tabulátory. -->
    44 	<xsl:variable name="oddělovač" select="'\n\s*\n\s*'"/>
    45     
    46 	
    47 	<!-- Funkce: zda jde o XHTML inline element – může se vyskytovat uvnitř odstavců. -->
    48 	<xsl:template name="inlineElement" as="xs:boolean">
    49 		<xsl:param name="prvek"/>
    50 		<xsl:sequence select="
    51 			$prvek/name() = 'a' or						
    52 			$prvek/name() = 'abbr' or						
    53 			$prvek/name() = 'acronym' or						
    54 			$prvek/name() = 'b' or						
    55 			$prvek/name() = 'br' or						
    56 			$prvek/name() = 'cite' or						
    57 			$prvek/name() = 'code' or						
    58 			$prvek/name() = 'em' or						
    59 			$prvek/name() = 'i' or						
    60 			$prvek/name() = 'img' or						
    61 			$prvek/name() = 'q' or
    62 			$prvek/name() = 'span' or
    63 			$prvek/name() = 'strong' or
    64 			$prvek/name() = 'sub' or
    65 			$prvek/name() = 'sup' or
    66 			$prvek/name() = 'tt' or
    67 			$prvek/name() = 'u' or
    68 			$prvek/name() = 'var'
    69 			"/>
    70 		<!-- …případně další, pokud je budeme chtít podporovat. -->
    71 	</xsl:template>
    72 	
    73 	
    74 	<!-- Funkce: zda je prvek začátkem odstavce. -->
    75 	<xsl:template name="začátekOdstavce" as="xs:boolean">
    76 		<xsl:param name="prvek"/>
    77 		
    78 		<xsl:variable name="inlineElement" as="xs:boolean">
    79 			<xsl:call-template name="inlineElement"><xsl:with-param name="prvek" select="$prvek"/></xsl:call-template>
    80 		</xsl:variable>
    81 		
    82 		<xsl:variable name="předchůdce" select="$prvek/preceding-sibling::node()[1]"/>
    83 		
    84 		<xsl:variable name="inlineElementPředchůdce" as="xs:boolean">
    85 			<xsl:call-template name="inlineElement"><xsl:with-param name="prvek" select="$předchůdce"/></xsl:call-template>
    86 		</xsl:variable>
    87 		
    88 		<xsl:variable name="textovýUzel" select="boolean($prvek/self::text())"/>
    89 				
    90 		<xsl:sequence select="
    91 			($inlineElement or $textovýUzel) 
    92 			and 
    93 			(
    94 				($inlineElementPředchůdce and matches($prvek, concat('^', $oddělovač, '.*')))
    95 				or
    96 				not($inlineElementPředchůdce)
    97 				or
    98 				not($předchůdce)
    99 			)
   100 			and
   101 			(
   102 				not($předchůdce/self::text())
   103 				or
   104 				matches($předchůdce/self::text(), concat('.*', $oddělovač, '$'))
   105 			)
   106 			"/>
   107 	</xsl:template>
   108 	
   109 	
   110 	<!--
   111 		V prvním kole zavřeme volný text a inline elementy do značek <o:odstavec typ=""/>,
   112 		kde typ může být "začátek", což značí, že se jedná o první část budoucího odstavce <p/>.
   113 	-->
   114 	<xsl:template match="text()" mode="prvníKolo">
   115 	
   116 		<xsl:variable name="začátekOdstavce" as="xs:boolean">
   117 			<xsl:call-template name="začátekOdstavce">
   118 				<xsl:with-param name="prvek" select="."/>
   119 			</xsl:call-template>
   120 		</xsl:variable>
   121 		
   122 		<xsl:for-each select="fn:tokenize(., $oddělovač)">
   123 			<xsl:element name="o:odstavec">
   124 				<xsl:if test="$začátekOdstavce or not(position() = 1)">
   125 					<xsl:attribute name="typ">začátek</xsl:attribute>
   126 				</xsl:if>
   127 				<xsl:value-of select="."/>
   128 			</xsl:element>
   129 		</xsl:for-each>
   130 	
   131 	</xsl:template>
   132 	
   133 	<!-- 
   134 		Inline elementy zavíráme do <o:odstavec typ=""/>,
   135 		ostatní vkládáme, jak jsou.
   136 	-->
   137 	<xsl:template match="*" mode="prvníKolo">
   138 	
   139 		<xsl:variable name="inlineElement" as="xs:boolean">
   140 			<xsl:call-template name="inlineElement">
   141 				<xsl:with-param name="prvek" select="."/>
   142 			</xsl:call-template>
   143 		</xsl:variable>
   144 		
   145 		<xsl:choose>
   146 			<!-- TODO: zvláštní šablona (match="…") pro inline elementy místo větvení? -->
   147 			<xsl:when test="$inlineElement">
   148 				<xsl:variable name="začátekOdstavce" as="xs:boolean">
   149 					<xsl:call-template name="začátekOdstavce">
   150 						<xsl:with-param name="prvek" select="."/>
   151 					</xsl:call-template>
   152 				</xsl:variable>
   153 				<xsl:element name="o:odstavec">
   154 					<xsl:if test="$začátekOdstavce">
   155 						<xsl:attribute name="typ">začátek</xsl:attribute>
   156 					</xsl:if>
   157 					<xsl:copy-of select="."/>
   158 				</xsl:element>
   159 			</xsl:when>
   160 			<xsl:otherwise>
   161 				<xsl:copy-of select="."/>
   162 			</xsl:otherwise>		
   163 		</xsl:choose>
   164 	
   165 	</xsl:template>
   166 	
   167 	<!-- V druhém kole spojíme jednotlivé části odstavců. -->
   168 	<xsl:template match="o:odstavec[@typ='začátek']" mode="druhéKolo">
   169 		<o:odstavec>
   170 			<xsl:call-template name="spojOdstavce">
   171 				<xsl:with-param name="část" select="."/>
   172 			</xsl:call-template>
   173 		</o:odstavec>
   174 	</xsl:template>
   175 	<!-- Následující části odstavce přeskočíme – postará se o ně vnitřní smyčka volaná z předchozí šablony. -->
   176 	<xsl:template match="o:odstavec" mode="druhéKolo"/>
   177 	<!-- Neinline (blokové) elementy vložíme, jak jsou. -->
   178 	<xsl:template match="*" mode="druhéKolo">
   179 		<xsl:copy-of select="."/>
   180 	</xsl:template>
   181 	
   182 	
   183 	<!--
   184 		Za první část (parametr, <o:odstavec typ="začátek"/>) resp. její vnitřek
   185 		připojíme (rekurze) všechny další části téhož odstavce (oddělíme mezerou).
   186 		Konec odstavce poznáme tak, že následovník je něco jiného než <o:odstavec/> nebo má atribut typ="začátek".
   187 	-->
   188 	<xsl:template name="spojOdstavce">
   189 		<xsl:param name="část"/>
   190 		<xsl:copy-of select="$část/child::node()"/>
   191 		<xsl:variable name="následovník" select="$část/following-sibling::node()[1]"/>
   192 		<xsl:if test="$následovník/name() = 'o:odstavec' and not($následovník/@typ = 'začátek')">
   193 			<xsl:text> </xsl:text>
   194 			<xsl:call-template name="spojOdstavce">
   195 				<xsl:with-param name="část" select="$následovník"/>
   196 			</xsl:call-template>
   197 		</xsl:if>
   198 	</xsl:template>
   199 	
   200 	
   201 	<!-- Ve třetím kole smažeme prázdné mešuge odstavce. -->
   202 	<xsl:template mode="třetíKolo" match="o:odstavec[
   203 		count(child::node()) = 0 
   204 		or 
   205 		(
   206 			count(child::node()) = 1 
   207 			and
   208 			text()
   209 			and
   210 			matches(text(), '^\s*$')
   211 		)
   212 		]">
   213 		<xsl:text> </xsl:text>	
   214 	</xsl:template>
   215 	<!-- Převedeme z <o:odstavec/> na <p/> -->
   216 	<xsl:template match="o:odstavec" mode="třetíKolo">
   217 		<p class="mešuge">
   218 			<xsl:copy-of select="child::node()"/>
   219 		</p>
   220 	</xsl:template>
   221 	<!-- Všechno ostatní zkopírujeme, jak je. -->
   222 	<xsl:template match="*" mode="třetíKolo">
   223 		<xsl:copy-of select="."/>
   224 	</xsl:template>
   225 	
   226 	
   227 </xsl:stylesheet>