Wrote the CSS output system and got it working
Cracked a major set of problems for teiJournal today.
Display styles are stored in three different places in the database: base_styles.xsl,
[styleguide]_styles.xsl, and the user's styles.xsl (containing customized styles). Each file contains a block of <xsl:attribute-set> elements, each of which represents a ruleset, and contains a set of <xsl:attribute> elements, each of which represents a property and a value.
These blocks then have to be combined in an intelligent way. The basic hierarchy is that base styles are overridden by any applicable styles in the style guide, and those styles are overridden by any in the user styles file. So any user styles replace styles from the other two files, style guide styles replace base styles, and base styles are output where there are no overrides in the other two files. Furthermore, where there are rulesets or rules in either of the two lower files which are not represented in their ancestors, these need to be output as well.
The big step forward today was the creation of an XQuery file capable of doing this cascading combination. The resulting code is pretty small, and worth documenting in full. There are two functions, f:getCombinedDoc(), which retrieves all the rulesets from the three documents in the database and joins them together into one file, and then f:getAttributeSets(), which combines rulesets together, ignoring any overridden rules, to produce a single source in the form of an <xsl:stylesheet> document. The functions look like this:
declare function f:getCombinedDoc() as element(){
let $guideId := request:get-parameter('guide', 'apa'),
$base := doc('/db/teiJournal/settings/default/base_styles.xsl'),
$guidePath := concat('/db/teiJournal/settings/default/', $guideId, '_styles.xsl'),
$guide := doc($guidePath),
$user := doc('/db/teiJournal/settings/user/styles.xsl')
return
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
{$base//xsl:attribute-set}
{$guide//xsl:attribute-set}
{$user//xsl:attribute-set}
</xsl:stylesheet>
};
declare function f:getAttributeSets() as element()*{
let $doc := f:getCombinedDoc()
for $setName in distinct-values($doc//xsl:attribute-set/@name)
return
<xsl:attribute-set name="{$setName}">
{
for $attName in distinct-values($doc/xsl:attribute-set[@name=$setName]/xsl:attribute/@name)
return
$doc//xsl:attribute[@name=$attName][./parent::xsl:attribute-set[@name=$setName]][position() = last()]
}
</xsl:attribute-set>
};
The resulting document is then passed on to an XSLT transformation which turns it into a real CSS stylesheet. The pipeline looks like this:
<map:match pattern="*/style.css">
<map:generate src="xq/getStyleSheet.xq" type="xquery">
<map:parameter name="guide" value="{1}" />
</map:generate>
<map:transform type="saxon" src="xsl/attribute_sets_to_css.xsl"/>
<map:serialize type="text" mime-type="text/css" />
</map:match>
Note the mime-type attribute on the serializer: without this, the file is served as text/plain, and the browser fails to interpret it as CSS, so it doesn't apply it to the Web page (this took half an hour to figure out).
So styles are now being applied to the XHTML output, and I can begin refining those styles by building the attribute-set files. Meanwhile, the last bits and pieces of the XHTML output itself need to be completed (appendix and biblio handling).