Commit 6eeab05a authored by Alf Eaton's avatar Alf Eaton

Fetch XSL when container starts

Instead of baking the XSL into the Docker image, fetch the XSL files when the container starts up.
parent d0304f65
......@@ -8,7 +8,14 @@ RUN apt-get update \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/
COPY src/ /var/www/html/
RUN mkdir /input
VOLUME /input
COPY src/ /var/www/html/
COPY entrypoint.sh entrypoint.sh
RUN chmod +x entrypoint.sh
COPY update.php update.php
ENTRYPOINT ["./entrypoint.sh"]
#!/bin/bash
set -e
php update.php
docker-php-entrypoint apache2-foreground
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsw="http://coko.foundation/xsweet"
xmlns="http://www.w3.org/1999/xhtml"
xpath-default-namespace="http://www.w3.org/1999/xhtml"
exclude-result-prefixes="#all">
<!-- Indent should really be no, but for testing. -->
<xsl:output method="xml" indent="no" omit-xml-declaration="yes"/>
<!-- Copy everything by default. -->
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*"/>
</xsl:copy>
</xsl:template>
<!-- Rewrites CSS where p has all its contents in a single branch; display semantics
of that branch are expressed in CSS and overwrite the p element's given @style.
[examples]
<p style="color: #000020; font-size: 13.5pt; margin-left: 72pt">
<span style="color: #000020; font-size: 13.5pt">
<i>All wholesome food is caught without a net or a trap.</i>
</span>
</p>
should be rewritten
<p style="color: #000020; font-size: 13.5pt; font-style: italic; margin-left: 72pt">All wholesome food is caught without a net or a trap.</p>
Note the properties overwritten on descendants are removed, and the 'i' element is rewritten as
font-style='italic'.
Note the following mappings:
i - font-style='italic'.
b - font-weight='bold'.
u - text-decoration='underline'.
-->
<xsl:template match="p">
<xsl:variable name="css-proxy" as="element()">
<style>
<xsl:apply-templates select="@style" mode="as-attributes"/>
<xsl:call-template name="override-styles"/>
</style>
</xsl:variable>
<xsl:copy>
<xsl:copy-of select="@*"/>
<!-- Now overwriting @style ... -->
<xsl:for-each select="$css-proxy[exists(@*)]">
<!-- ... only when there are properties as attributes on $css-proxy ... -->
<xsl:attribute name="style">
<xsl:for-each select="@*">
<xsl:sort data-type="text" select="name()"/>
<xsl:if test="position() gt 1">; </xsl:if>
<xsl:value-of select="name()"/>
<xsl:text>: </xsl:text>
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:attribute>
</xsl:for-each>
<!--<xsl:copy-of select="$css-proxy"/>-->
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<!-- We can strip 'span' elements when they are coextensive with their wrapping p and
have nothing but @style to offer, as the latter is being promoted. -->
<xsl:template match="p//span[empty(@class)]
[normalize-space(.) = normalize-space(ancestor::p[1])]">
<xsl:apply-templates/>
</xsl:template>
<!-- Note we leave 'u', 'i' and 'b' in place despite also promoting them to CSS. -->
<xsl:template name="override-styles">
<!-- Under certain conditions, descends tree to collect CSS style property assignments
returning them as attributes (captured on a proxy). -->
<xsl:if test="count(*) eq 1 and normalize-space(.) = normalize-space(*[1])">
<xsl:for-each select="*">
<xsl:apply-templates select=". | @style" mode="as-attributes"/>
<!-- descend recursively -->
<xsl:call-template name="override-styles"/>
</xsl:for-each>
</xsl:if>
</xsl:template>
<!-- 'as-attributes mode' loads up a proxy element with CSS properties. We exploit
the fact that attributes overwrite other attributes of the same name, added
earlier (since attributes must be uniquely named) to de-duplicate our CSS ...
i.e. font-size will come out only once, with whatever value was declared deepest.
Note that elements as well as @style values will prompt CSS properties being added in this way. -->
<xsl:template match="*" mode="as-attributes"/>
<xsl:template match="u" mode="as-attributes">
<xsl:attribute name="text-decoration">underline</xsl:attribute>
</xsl:template>
<xsl:template match="i" mode="as-attributes">
<xsl:attribute name="font-style">italic</xsl:attribute>
</xsl:template>
<xsl:template match="b" mode="as-attributes">
<xsl:attribute name="font-weight">bold</xsl:attribute>
</xsl:template>
<xsl:template match="@style" mode="as-attributes">
<xsl:for-each select="tokenize(.,'\s*;\s*')">
<xsl:attribute name="{replace(.,':.*$','')}" select="replace(.,'^.*:\s*','')"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsw="http://coko.foundation/xsweet"
xpath-default-namespace="http://www.w3.org/1999/xhtml"
xmlns="http://www.w3.org/1999/xhtml"
exclude-result-prefixes="#all">
<!-- Note the default namespace for matching (given above) is
"http://www.w3.org/1999/xhtml" -->
<!-- The results will have XML syntax but no XML declaration or DOCTYPE declaration
(as permitted by HTML5). -->
<xsl:output method="xml" indent="no" omit-xml-declaration="yes"/>
<!-- By default (when not matched with a template of higher priority)
copy any element and its attributes. -->
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*"/>
</xsl:copy>
</xsl:template>
<!-- Any 'i' element becomes an 'em' element; its attributes are copied. -->
<!-- (Unwanted attributes can be removed in a subsquent step.) -->
<xsl:template match="i">
<em>
<xsl:copy-of select="@*"/>
<xsl:apply-templates/>
</em>
</xsl:template>
<!-- Any 'b' element becomes a 'strong' element; its attributes are copied. -->
<!-- NB note that inline elements may be modified further or stripped in a subsequent 'reduce' step:
the story is not over. -->
<xsl:template match="b">
<strong>
<xsl:copy-of select="@*"/>
<xsl:apply-templates/>
</strong>
</xsl:template>
<!-- 'u' becomes 'i' for Editoria.... -->
<xsl:template match="u">
<i>
<xsl:copy-of select="@*"/>
<xsl:apply-templates/>
</i>
</xsl:template>
<!-- We declare a key that enables us to match elements based on 'class' (attribute) values.
Any element with a non-ws @class may be so matched (and retrieved).
Since 'class' may be overloaded this is a many-to-many match, so template
priority will be important.
i.e. <p class="Quote Special"> matches with both 'Quote' and 'Special' key values.
Note the key matches elements of any type (p, h1, span etc.) so no worries about that; only
as assigned 'class' value (delimited by whitespace) will count. -->
<xsl:key name="elements-by-class"
match="*[matches(@class,'\S')]" use="tokenize(@class,'\s+')"/>
<!-- Calling the key() function, match any element .Quote and emit 'extract', copying attributes.
Explicit @priority assignments prevent template collisions and let us control
the order of preference. -->
<xsl:template match="key('elements-by-class','Quote')" priority="100">
<extract>
<xsl:copy-of select="@*"/>
<xsl:apply-templates/>
</extract>
</xsl:template>
</xsl:stylesheet>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsw="http://coko.foundation/xsweet"
xpath-default-namespace="http://www.w3.org/1999/xhtml"
xmlns="http://www.w3.org/1999/xhtml"
exclude-result-prefixes="#all">
<!-- Note the default namespace for matching (given above) is
"http://www.w3.org/1999/xhtml" -->
<!-- The results will have XML syntax but no XML declaration or DOCTYPE declaration
(as permitted by HTML5). -->
<xsl:output method="xml" indent="no" omit-xml-declaration="yes"/>
<xsl:key name="elements-by-class"
match="*[matches(@class,'\S')]" use="tokenize(@class,'\s+')"/>
<xsl:key name="internal-links" match="*[@id]" use="concat('#',@id)"/>
<!-- By default (when not matched with a template of higher priority)
copy any element and its attributes. -->
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*"/>
</xsl:copy>
</xsl:template>
<xsl:template priority="10" match="div[@id='docx-body'] | key('elements-by-class','docx-body')">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="body/div"/>
<xsl:template match="key('elements-by-class','endnoteReference') | key('elements-by-class','footnoteReference')">
<xsl:variable name="contents">
<xsl:apply-templates select="key('internal-links',@href)" mode="plaintext"/>
</xsl:variable>
<!-- note comes out empty since we don't actually want the referencing string (number). -->
<note note-content="{$contents}"/>
</xsl:template>
<xsl:template match="*" mode="plaintext">
<xsl:apply-templates mode="#current"/>
</xsl:template>
<xsl:template match="p" mode="plaintext">
<xsl:for-each select="preceding-sibling::*[1]">
<xsl:text>&#xA;&#xA;</xsl:text>
</xsl:for-each>
<xsl:apply-templates mode="#current"/>
</xsl:template>
<!-- Note references suppressed inside notes. -->
<xsl:template match="key('elements-by-class','endnoteRef')" mode="plaintext"/>
<xsl:template match="key('elements-by-class','footnoteRef')" mode="plaintext"/>
<xsl:template match="text()" mode="plaintext">
<xsl:value-of select="replace(.,'\s+',' ')"/>
</xsl:template>
</xsl:stylesheet>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsw="http://coko.foundation/xsweet"
xpath-default-namespace="http://www.w3.org/1999/xhtml"
xmlns="http://www.w3.org/1999/xhtml"
exclude-result-prefixes="#all">
<!-- Note the default namespace for matching (given above) is
"http://www.w3.org/1999/xhtml" -->
<!-- The results will have XML syntax but no XML declaration or DOCTYPE declaration
(as permitted by HTML5). -->
<xsl:output method="xml" indent="no" omit-xml-declaration="yes"/>
<!-- Just in case:
<xsl:key name="elements-by-class" match="*[matches(@class,'\S')]"
use="tokenize(@class,'\s+')"/>
-->
<!-- By default we *drop* elements.
Better templates will copy the ones we want. -->
<xsl:template match="*">
<xsl:apply-templates/>
</xsl:template>
<!-- But we keep attributes -->
<xsl:template match="@*">
<xsl:copy-of select="."/>
</xsl:template>
<!-- Sorry guys, until further notice Editoria doesn't know what to do with you. -->
<xsl:template match="comment() | processing-instruction()"/>
<!-- Drop head/style -->
<xsl:template priority="5" match="head/style"/>
<xsl:template match="html | head | head//* | body">
<xsl:apply-templates select="." mode="copy-after-all"/>
</xsl:template>
<!-- We only permit a header to be propagated if it has (non-ws) contents. -->
<xsl:template match="h1| h2 | h3 | h4 | h5 | h6">
<xsl:if test="matches(.,'\S')">
<xsl:apply-templates select="." mode="copy-after-all"/>
</xsl:if>
</xsl:template>
<xsl:template match="p | extract | blockquote | pre">
<xsl:apply-templates select="." mode="copy-after-all"/>
</xsl:template>
<!-- Empty line feeds may be left especially after paragraph splitting in an earlier step. -->
<xsl:template match="p[not(matches(.,'\S'))]"/>
<!-- NB stripping b and strong for now. -->
<xsl:template match="I | sup | sub | a | code | em">
<xsl:apply-templates select="." mode="copy-after-all"/>
</xsl:template>
<xsl:template match="note | note/@*">
<xsl:apply-templates select="." mode="copy-after-all"/>
</xsl:template>
<xsl:template match="node()" mode="comment-in"/>
<xsl:template match="p//* | td//*" mode="comment-in">
<xsl:if test="not(matches(string(.),'\S'))">
<xsl:comment> open/close </xsl:comment>
</xsl:if>
</xsl:template>
<xsl:template match="node() | @*" mode="copy-after-all">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="." mode="comment-in"/>
<!-- switching back out of mode -->
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<!-- Bye-bye @class, bye-bye @style! -->
<xsl:template match="@class | @style" priority="2"/>
</xsl:stylesheet>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsw="http://coko.foundation/xsweet"
xmlns="http://www.w3.org/1999/xhtml"
xpath-default-namespace="http://www.w3.org/1999/xhtml"
exclude-result-prefixes="#all">
<!-- Removes redundant tagging from HTML based on @style analysis, element type e.g. redundant b, i, u etc. -->
<xsl:output method="xml" indent="no" omit-xml-declaration="yes"/>
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="style" priority="11">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:value-of select="replace(string(.),'xsweet','-xsweet')"/>
</xsl:copy>
</xsl:template>
<!-- Disable when auto-indenting - this introduces cosmetic whitespace into
an assumed text-brick. -->
<xsl:template match="head | head//* | body | body/* | p | h1 | h2 | h3 | h4 | h5 | h6" priority="10">
<xsl:text>&#xA;</xsl:text>
<xsl:next-match/>
</xsl:template>
<!-- Insert a comment into any empty div or p so as not to confuse poor HTML parsers. -->
<xsl:template match="div | p | h1 | h2 | h3 | h4 | h5 | h6">
<xsl:copy>
<xsl:apply-templates select="node() | @*"/>
<xsl:if test="empty(*) and not(matches(.,'\S'))">
<xsl:comment> empty </xsl:comment>
</xsl:if>
</xsl:copy>
</xsl:template>
<xsl:template match="span[@class=('EndnoteReference','FootnoteReference')]">
<!-- These spans sometimes contain noise from input, in addition to a (generated) endnote or footnote reference. -->
<!-- Note the named style assignment is directly coded in the Word -->
<!-- <span class="EndnoteReference"><a class="endnoteReference" href="#en5">5</a>6</span>"-->
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="a"/>
</xsl:copy>
</xsl:template>
<!-- Remove any 'p' element that is truly empty - nothing but whitespace, no elements.
(Empty inline elements were stripped by generic logic: see scrub.xsl.) -->
<!--<xsl:template match="p[not(matches(.,'\S'))][empty(*)]"/>-->
<!-- Rewrite @style to remove properties duplicative of inherited properties -->
<xsl:template match="@style">
<xsl:variable name="here" select=".."/>
<!-- Any CSS properties not declared on an ancestor are significant. -->
<xsl:variable name="significant" as="xs:string*">
<xsl:for-each select="tokenize(.,'\s*;\s*')">
<xsl:variable name="prop" select="."/>
<xsl:variable name="propName" select="replace($prop,':.*$','')"/>
<!-- the property is redundant if the same as the same property on the closest element with the property -->
<xsl:variable name="redundant" select="$here/ancestor::*[contains(@style,$propName)][1]/tokenize(@style,'\s*;\s*') = $prop"/>
<xsl:if test="not($redundant)">
<!-- We have some (pseudo) properties named 'xsweet' these are rewritten for CSS -->
<xsl:sequence select="replace($prop,'\s*^xsweet','-xsweet')"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<!-- Only if we have an item in sequence $significant (a sequence of strings) do we produce a new @style. -->
<xsl:if test="exists($significant)">
<xsl:attribute name="style">
<xsl:value-of select="$significant" separator="; "/>
</xsl:attribute>
</xsl:if>
</xsl:template>
<xsl:template match="span[empty(@style|@class)]">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="span">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates select="@style"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="tab">
<span class="tab">&#x9;<xsl:comment> tab </xsl:comment></span>
</xsl:template>
<xsl:template match="b[ancestor::*[contains(@style,'font-weight')][1]/tokenize(@style,'\s*;\s*') = 'font-weight: bold']">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="i[ancestor::*[contains(@style,'font-style')][1]/tokenize(@style,'\s*;\s*') = 'font-style: italic']">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="u[ancestor::*[contains(@style,'text-decoration')][1]/tokenize(@style,'\s*;\s*') = 'text-decoration: underline']">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="span[@style='font-style: italic']">
<i>
<xsl:apply-templates/>
</i>
</xsl:template>
<xsl:template match="span[@style='font-weight: bold']">
<b>
<xsl:apply-templates/>
</b>
</xsl:template>
<xsl:template match="span[@style='text-decoration: underline']">
<u>
<xsl:apply-templates/>
</u>
</xsl:template>
<xsl:template match="b/b | i/i | u/u" priority="5">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsw="http://coko.foundation/xsweet"
xmlns="http://www.w3.org/1999/xhtml"
xpath-default-namespace="http://www.w3.org/1999/xhtml"
exclude-result-prefixes="#all">
<!-- Indent should really be no, but for testing. -->
<xsl:output method="xml" indent="no" omit-xml-declaration="yes"/>
<!-- Copy everything by default. -->
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*"/>
</xsl:copy>
</xsl:template>
<!-- This stylesheet repairs endnotes and their references:
discarding unreferenced endnotes and re-ordering notes by order of first reference. -->
<xsl:key name="endnote-by-id" match="p[@class='docx-endnote']" use="@id"/>
<xsl:key name="footnote-by-id" match="p[@class='docx-footnote']" use="@id"/>
<!--<a class="endnoteReference" href="#en{@w:id}">-->
<!-- Retrieveing endnotes, keep only those that are referenced, in their order of reference. -->
<xsl:template match="div[@class='docx-endnotes']">
<xsl:variable name="notes" select="div[@class='docx-endnote']"/>
<xsl:copy>
<xsl:copy-of select="@*"/>
<!-- We only capture end notes that have actually been referenced, in their order of referencing. -->
<xsl:for-each
select="../div[@class = 'docx-body']//a[@class = 'endnoteReference'][xsw:is-first-enref(.)]">
<xsl:variable name="href" select="@href"/>
<xsl:apply-templates select="$notes[concat('#', @id) = $href]"/>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="div[@class='docx-footnotes']">
<xsl:variable name="notes" select="div[@class='docx-footnote']"/>
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:for-each
select="../div[@class = 'docx-body']//a[@class = 'footnoteReference'][xsw:is-first-fnref(.)]">
<xsl:variable name="href" select="@href"/>
<xsl:apply-templates select="$notes[concat('#', @id) = $href]"/>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<!-- Expand placeholders for links with generated numbers if they have no values yet. -->
<xsl:template match="a[@class=('endnoteReference','footnoteReference')][not(normalize-space(.))]">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates select="." mode="get-number"/>
</xsl:copy>
</xsl:template>
<!--<span class="endnoteRef"/> inside end note text was produced from w:endnoteRef, and also requires expansion. -->
<xsl:template match="span[@class='endnoteRef'][not(normalize-space(.))]">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates mode="get-number"
select="key('endnoteRef-by-href',concat('#',ancestor::div[@class='docx-endnote']/@id))[1]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="span[@class='footnoteRef'][not(normalize-space(.))]">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates mode="get-number"
select="key('footnoteRef-by-href',concat('#',ancestor::div[@class='docx-footnote']/@id))[1]"/>
</xsl:copy>
</xsl:template>
<!-- Since notes need to be counted, only first references to notes can
be counted, and the order of notes depends on the order of (first)
references to them, so that's the set of elements we need for counting. -->
<xsl:key name="endnoteRef-by-href" match="a[@class='endnoteReference']" use="@href"/>
<xsl:key name="footnoteRef-by-href" match="a[@class='footnoteReference']" use="@href"/>
<xsl:function name="xsw:is-first-enref" as="xs:boolean">
<xsl:param name="enref" as="element(a)"/>
<!-- Boolean returns true() iff this is the first endnoteRef with its @id (and hence target). -->
<xsl:sequence select="($enref/@class='endnoteReference') and ($enref is key('endnoteRef-by-href',$enref/@href,root($enref))[1])"/>
</xsl:function>
<xsl:function name="xsw:is-first-fnref" as="xs:boolean">
<xsl:param name="fnref" as="element(a)"/>
<!-- Boolean returns true() iff this is the first footnoteRef with its @id (and hence target). -->
<xsl:sequence select="($fnref/@class='footnoteReference') and ($fnref is key('footnoteRef-by-href',$fnref/@href,root($fnref))[1])"/>
</xsl:function>
<xsl:template match="a[@class='endnoteReference']" mode="get-number">
<xsl:for-each select="key('endnoteRef-by-href',@href)[1]">
<xsl:number level="any" count="a[@class='endnoteReference'][xsw:is-first-enref(.)]"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="a[@class='footnoteReference']" mode="get-number">
<xsl:for-each select="key('footnoteRef-by-href',@href)[1]">
<xsl:number level="any" count="a[@class='footnoteReference'][xsw:is-first-fnref(.)]"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsw="http://coko.foundation/xsweet"
exclude-result-prefixes="#all">
<xsl:output method="xml" indent="no" omit-xml-declaration="yes"/>
<!-- Redundant match silences noisy XSLT engines -->
<xsl:template match="node() | @* | html:html" xmlns:html="http://www.w3.org/1999/xhtml">
<xsl:copy copy-namespaces="no">
<xsl:apply-templates select="node() | @*"/>
</xsl:copy>
</xsl:template>
<!-- Groups can be unwrapped since the induced list structure takes care of everything. -->
<xsl:template match="xsw:list">
<ul>
<xsl:for-each-group select="*" group-starting-with="p">
<li>
<xsl:apply-templates select="current-group()"/>
</li>
</xsl:for-each-group>
</ul>
</xsl:template>
</xsl:stylesheet>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsw="http://coko.foundation/xsweet"
xmlns="http://www.w3.org/1999/xhtml"
xpath-default-namespace="http://www.w3.org/1999/xhtml"
exclude-result-prefixes="#all">
<!-- Indent should really be no, but for testing. -->
<xsl:output method="xml" indent="no" omit-xml-declaration="yes"/>
<!-- Copy everything by default. -->
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*"/>
</xsl:copy>
</xsl:template>
<!-- Copy 'p', and merge what's inside it. -->
<xsl:template match="p">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:call-template name="collapse-ilk"/>
</xsl:copy>
</xsl:template>
<!-- Merge logic accepts a sequence of nodes (by default,
children of the context node where called) and returns
sequences where 'like' nodes in sequence are merged.
So <u>Moby </u><u>Dick</u> comes back <u>Moby Dick</u>.
'Likeness' is established by xsw:node-signature, and permits elements
of the same type and attribute values to be merged accorrding
to logic given in the hashing templates.
-->
<xsl:template name="collapse-ilk">
<xsl:param name="among" select="node()"/>
<xsl:for-each-group select="$among" group-adjacent="xsw:node-signature(.)">
<xsl:for-each select="current-group()[1]/self::*">
<!-- In the element case, splice in an element. -->
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:call-template name="collapse-ilk">
<xsl:with-param name="among" select="current-group()/node()"/>
</xsl:call-template>
</xsl:copy>
</xsl:for-each>
<!-- Splice in anything not an element. -->
<xsl:copy-of select="current-group()[empty(self::*)]"/>
</xsl:for-each-group>
</xsl:template>
<xsl:function name="xsw:node-signature" as="xs:string">
<xsl:param name="n" as="node()"/>
<xsl:value-of separator="|">
<xsl:apply-templates mode="signature" select="$n"/>
</xsl:value-of>
</xsl:function>
<!-- Note we're going to collapse things with the same (local) name
though in different namespaces - this ain't lookin to be namespace safe. -->
<xsl:template mode="signature" match="*">
<xsl:value-of select="local-name()"/>
<xsl:for-each select="@*">
<xsl:sort select="local-name()"/>
<xsl:if test="position() ne 1"> ::: </xsl:if>
<xsl:apply-templates mode="#current" select="."/>
</xsl:for-each>
</xsl:template>
<xsl:template mode="signature" match="@*">
<xsl:value-of select="local-name(),." separator="="/>
</xsl:template>
<!-- These guys should never collapse so their hash is always unique to them.-->
<xsl:template mode="signature" match="div | p | tab">
<xsl:value-of select="local-name(.)"/>
<xsl:value-of select="generate-id(.)"/>
</xsl:template>
<!-- ws-only text nodes, PIs and comments should be merged with adjacent elements
iff those nodes are being merged together. -->
<xsl:template mode="signature" match="text() | comment() | processing-instruction()">
<xsl:variable name="fore" select="preceding-sibling::*[1]/xsw:node-signature(.)"/>
<xsl:variable name="aft" select="following-sibling::*[1]/xsw:node-signature(.)"/>