<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet href="b-cond-elements.xsl" type="text/xsl"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
	"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
	<!ATTLIST style dr:select CDATA #IMPLIED>
	<!ATTLIST a dr:select CDATA #IMPLIED>
	<!ENTITY % xhtml-prefw-redecl.mod "" >
	<!ENTITY % xhtml-postfw-redecl.mod "" >]>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:dr="http://documenta.rudolphina.org/">
	<head>
		<title>Beyond CC's, Selecting HTML Client-side</title>
		<style type="text/css">
		body {
			font-family: Helvetica, Arial, sans-serif;
			font-size: small;
			margin: 30px;
		}
		#home {
			font-family: Verdana, sans-serif;
			font-size: 125%;
			font-weight: normal;
			color: green; background: white;
		}
		#home a {
			text-decoration: none;
			color: green; background: white;
		}
		#home a:hover {
			color: #005A9C; background: #ffff88;
		}
		#demo {
			width: 120px;
			height: 120px;
			margin-bottom: 1em;
			margin-right: 1em;
			float: left;
			border: 1px solid black;
		}
		.clear {
			clear: left;
		}
		.nohtml {
			background-color: #FFE7E7;
		}
		dt {
			margin-right: 8px;
			width: 48px;
			height: 16px;
			border: none;
			clear: left;
			float: left;
		}
		#dt_white {
			width: 46px;
			height: 14px;
			border: solid 1px black;
		}
		dd {
			padding-left: 20px;
		}
		p.validator img {
			margin-right: 1em;
			float: left;
			border: 0;
		}
		code {
			font-family: monospace;
			color: #007000;
			font-size: 10pt;
		}
		pre {
			font-family: monospace;
			color: #007000; background-color: #fafafa;
			font-size: 9pt;
			border: 1px solid #bbbbbb;
			line-height: 125%;
			margin: 0pt;
			padding: 0.5em 0.99em 0.5em 0.99em;
			overflow-x: auto;
			overflow-y: auto;
		}
		</style>
		<!-- IE 6 and IE 7 are in quircks mode, regardless of the XML declaration -->
		<style type="text/css" dr:select="IE 6, IE 7">
		#dt_white {
			width: 48px;
			height: 16px;
		}
		</style>
		<style type="text/css" dr:select="Firefox 3, Opera 9.5, Safari 3">
		dd {
			border: 1px solid white;
		}
		</style>
		<style type="text/css" dr:select="IE 6">#demo {background-color: #6666CC}</style>
		<style type="text/css" dr:select="IE 7">#demo {background-color: #0099FF}</style>
		<style type="text/css" dr:select="IE 8">#demo {background-color: #66CCFF}</style>
		<style type="text/css" dr:select="Netscape 7">#demo {background-color: #63A294}</style>
		<style type="text/css" dr:select="Netscape 8">#demo {background-color: #7BB2B5}</style>
		<style type="text/css" dr:select="Netscape 9">#demo {background-color: #CEE7E7}</style>
		<style type="text/css" dr:select="FF 1">#demo {background-color: #FF5F49}</style>
		<style type="text/css" dr:select="FF 2">#demo {background-color: #FFBC2F}</style>
		<style type="text/css" dr:select="FF 3">#demo {background-color: #FFED0F}</style>
		<style type="text/css" dr:select="Safari 1">#demo {background-color: #009900}</style>
		<style type="text/css" dr:select="Safari 2">#demo {background-color: #00CC00}</style>
		<style type="text/css" dr:select="Safari 3">#demo {background-color: #3CE400}</style>
		<style type="text/css" dr:select="Safari 4">#demo {background-color: #00FF00}</style>
		<style type="text/css" dr:select="Chrome 1">#demo {background-color: #3CD3C8}</style>
		<style type="text/css" dr:select="Chrome 2">#demo {background-color: #00FFE4}</style>
		<style type="text/css" dr:select="Opera 9.5">#demo {background-color: #B3478F}</style>
		<style type="text/css" dr:select="Opera 9.6">#demo {background-color: #DD44AA}</style>
		<style type="text/css" dr:select="Opera 10">#demo {background-color: #FF66CC}</style>
		<style type="text/css" dr:select="unknown">#demo {background-color: white}</style>
		<style type="text/css" dr:select="error">#demo {background-color: black}</style>
	</head>
	<body>
		<h3 id="home"><a href="../../Index.xml" title="Home">Documenta Rudolphina</a></h3>
		<h1>Selecting browser specific elements with XSLT</h1>
		<p>Manfred Staudinger, Vienna (July 2010)</p>
		<p>
			By adding an XSL stylesheet to your XHTML you may choose a <b>token to
			select a node or a group of nodes</b> to be included or excluded for some
			browsers. The selection process runs client side but is not dependent on
			JavaScript, as it takes place before JavaScript even starts.
		</p>
		<p>
			This is a mayor rewrite from the previous version (which is still accessible
			<a href="cond-css-demo.xml" title="Browser Detection with XSLT">
			here</a>). In addition to the <code>xsl:system-property</code>
			function and certain XSLT implementation differences, the User-Agent string is
			now used. This enables us to treat all browsers equally with positive or negative
			selection possible (no usage of CC's anymore) and to get a more detailed
			breakdown for the browsers.
		</p>
		<h2>Purpose</h2>
		<p>
			The aim is to replace Microsofts proprietary Conditional Comments by a new
			concept based on XSLT, which is adaptable and applies to all browsers evenly.
			The current webpage is an implementation of that concept to show its flexibility
			and robustness in a demo.
		</p>
		<p>
			The key to this concept is the definition of a token. For example assume you
			are interested in a certain feature implemented in Gecko (the rendering engine
			for Firefox and Co.). Then you can identify a Gecko browser having that
			feature by its release date or its version number and add your token
			<i>my-feature</i> to the built-in list. If this very same feature is implemented in
			Webkit and in Opera too, simply add the same token at the appropriate level. With this
			you have build two groups of browsers for which you may select HTML
			elements by specifying <i>my-feature</i> or <i>not my-feature</i>.
		</p>
		<p>
			Tokens could be chosen to represent:
		</p>
		<ul>
			<li>
				Categories of JavaScript - ECMAScript support.
			</li>
			<li>
				Categories of CSS support, especially for advanced  features.
			</li>
			<li>
				Categories for graphic support in general and for the evolving SVG standard in particular.
			</li>
		</ul>
		<p>
			It is no problem to have many tokens available, but it might be a problem to use
			10 and more alternatives for one HTML element (no problem for a demo, I
			suppose).
		</p>
		<h2>Basic Document</h2>
		<p>
			The server should send the document with a <code>Content-Type: application/xml</code>
			header. The encoding may be specifiied on that header too, or at the document
			level with an XML declaration. In case of IE 6 this would have caused the
			browser to switch into quircks mode, but because we are feeding this tag soup
			browser with XHTML, its is in quircks mode anyway (same for IE 7) and the
			XML declaration does no harm.
		</p>
		<p>
			To start the transformation, we use a <code>processing instruction</code>. This way the
			XSLT engiine will act as a "preprocessor" as the transformation will take place before the
			rendering (layout) engine runs, which would obviously not be the case if we would start it
			by JavaScript.
		</p>
		<p>
			For the selection process we use two custom attributes <code>dr:select</code>
			and <code>dr:select-one</code>. They are in a foreign namespace and require
			a namspace declaration on the <code>&lt;html&gt;</code> element. By
			putting it all together, we have
		</p>
		<pre>&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
&lt;?xml-stylesheet href="cond-elements.xsl" type="text/xsl"?&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:dr="http://documenta.rudolphina.org/"&gt;</pre>
		<h2>Selection Process</h2>
		<p>
			When the browser calls such a web-page, the XSL transformation generates a list of
			one or more tokens. The selection process is triggered by the <code>dr:select</code>
			attribute which contains another list of tokens. When the XSLT processor encounters
			an element with such an attribute, the list of generated tokens is matched against the
			list of tokens found in the attribute. If a match is found, the elemet and its content are
			included, otherwise ignored.
		</p>
		<p>
			The attribute causes a positive selection and we may call the list of tokens a <b>positive
			selection list</b>. If we prefix the list with <code>not</code> it will become a
			<b>negative selection list</b>, with exactly the opposite effect: in case of a match,
			the element and its content will be ignored.
		</p>
		<p>
			All of the above holds also for the <code>dr:select-one</code> attribute
			with one exeption: it effects only the element on which it is specified. Of course
			it may be combined with <code>dr:select</code>.
		</p>
		<h2>First Example</h2>
		<p>
			Assume we want to have one <code>&lt;a&gt;</code> element in two flavours, one
			for IE 6 and IE 7 and another one for all the others browsers. The list of tokens we have
			choosen to generate for IE 6 is simply <code>"IE 6, IE"</code> and for IE 7 accordingly
			<code>"IE 7, IE"</code>. Now we can write:
		</p>
		<pre>&lt;a dr:select="IE 6, IE 7" href="http://example.com" target="_blank"&gt;link text&lt;/a&gt;
&lt;a dr:select="not IE 6, IE 7" href="http://example.com"&gt;link text&lt;/a&gt;</pre>
		<p>
			As it happens, IE 6 and IE 7 need a new window when linking to the W3C Validator,
			otherwise IE would not send a referer header. You can see this example realized
			at the next section.
		</p>
		<p>
			But couldn't we have done this with "Conditional Comments"? Not exactly the same,
			but here is a good enough solution that will also pass the validator:
		</p>
		<pre>&lt;!--<span class="nohtml" title="comment">[if lte IE 7]&gt;
   &lt;a href="http://example.com" target="_blank"&gt;link text&lt;/a&gt;
&lt;![endif]</span>--&gt;
&lt;!--<span class="nohtml" title="comment">[if IE]&gt;&lt;![if gt IE 7]&gt;&lt;![endif]</span>--&gt;
   &lt;a href="http://example.com"&gt;link text&lt;/a&gt;
&lt;!--<span class="nohtml" title="comment">[if IE]&gt;&lt;![endif]&gt;&lt;![endif]</span>--&gt;</pre>
		<p>
			But even it validates, it does not look very convincing and indeed has some limitations:
		</p>
		<ul>
			<li>
				the link for IE 6 and IE 7 is inside of a comment, so has become a "HTML
				look alike" and is ignored by the validator
			</li>
			<li>
				this solution only works for MSIE, no other browser supports it
			</li>
		</ul>
		<p>
			(If you would like to know how these CC's can be created with XSLT, have
			a look at my article <a href="Indices/Art_CC.html">MSIE Conditional
			Comments and XSLT</a>).
		</p>
		<p class="validator">
			<a dr:select="IE 6, IE 7" href="http://validator.w3.org/check?uri=referer" target="_blank">
				<img src="http://www.w3.org/Icons/valid-xhtml11"
				alt="Valid XHTML 1.1" height="31" width="88" />
			</a>
			<a dr:select="not IE 6, IE 7" href="http://validator.w3.org/check?uri=referer">
				<img src="http://www.w3.org/Icons/valid-xhtml11"
				alt="Valid XHTML 1.1" height="31" width="88" />
			</a>
		</p>
		<h2>Validation</h2>
		<p class="clear">
			Validation currently can only be done on what is used as the input to the XSLT engine
			instead of its output. For example, all occurrences of the <code>dr:select</code>
			attribute are deleted in the course of the transformation and the browsers rendering engine
			will find nothing left behind in the foreign namespace. But in this and similar cases,
			where most of the document is copied over by the identity transformation, validating the
			input is still valuable. With that in mind, there were two problems to solve:
		</p>
		<ul>
			<li>
				For the validator the custom <code>dr:select</code> attribute has to be defined once for
				every element to which it will be added.
			</li>
			<li>
				It's a bit harder to find a XHTML <code>DOCTYPE</code> which IE
				will be able to process. Finally it turned out, that the <code>DTD</code>
				for XHTML 1.1 will work, if two <code>ENTITY</code> definitions are
				added, to compensate for a bug in the MS XML parser (MSXML 3.0).
			</li>
		</ul>
		<p>
			Now put it together and this is the <code>DOCTYPE</code> in use here:
		</p>
		<pre>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
	"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
	&lt;!ATTLIST style dr:select CDATA #IMPLIED&gt;
	&lt;!ATTLIST a dr:select CDATA #IMPLIED&gt;
	&lt;!ENTITY % xhtml-prefw-redecl.mod "" &gt;
	&lt;!ENTITY % xhtml-postfw-redecl.mod "" &gt;]&gt;</pre>
		<p>
			If you load this page with MS Internet Explorer, you will notice an extended
			load time, because IE fetches the <code>DTD</code> from the internet.
			It does not make any sense other than delivering one bold message: MS
			hates both, validation and XHTML! Up to now I can see only two possibilities:
		</p>
		<ul>
			<li>
				Let your IE users know how inferior this browser is, or
			</li>
			<li>
				Use this <code>DTD </code><b>for validation only</b>
			</li>
		</ul>
		<p>
			Note: I would expect readers of this page to rarly use IE anyway!
		</p>
		<h2>Second Example</h2>
		<p>
			Here the selection is done on the <code>style</code> elements for internal
			style sheets, which all define the color of the square but have different tokens
			in theire <code>dr:select</code> attributes. To the right you can see the
			User-Agent string and first attemps to extract the browser and the rendering
			engine correctly.
		</p>
		<div id="demo"> </div><!-- IE6 cannot cope with an empty div -->
		<p class="test-xslt"><b>XSLT, specified via processing instruction (PI), was not executed!</b></p>
		<p class="clear">
			Below are the colors for the different browser categories and the tokens that would
			be generated for each (characters in parenthesis are not part of the tokens).
		</p>
		<dl>
			<dt style="background-color: #6666CC"></dt><dd>IE 6, IE</dd>
			<dt style="background-color: #0099FF"></dt><dd>IE 7, IE</dd>
			<dt style="background-color: #66CCFF"></dt><dd>IE 8, IE</dd>
			<dt style="background-color: #63A294"></dt><dd>Netscape 7, Netscape</dd>
			<dt style="background-color: #7BB2B5"></dt><dd>Netscape 8, Netscape</dd>
			<dt style="background-color: #CEE7E7"></dt><dd>Netscape 9, Netscape, Navigator</dd>
			<dt style="background-color: #FF5F49"></dt><dd>Firefox 1 (1.0+), FF 1, Firefox, Gecko 1.7, Camino</dd>
			<dt style="background-color: #FFBC2F"></dt><dd>Firefox 2 (2.0+), FF 2, Firefox 1.5 (1.5.0+), FF 1.5, Firefox, Gecko 1.8, Epiphany, Flock, Galeon, Iceape, Iceweasel, Kazehakase, K-Meleon, SeaMonkey</dd>
			<dt style="background-color: #FFED0F"></dt><dd>Firefox 3, FF 3, Firefox, Gecko 1.9, Epiphany, Flock, Galeon, Iceweasel, Kazehakase, Minefield, SeaMonkey, Shiretoko, Songbird</dd>
			<dt style="background-color: #009900"></dt><dd>Safari 1 (1.3+), Safari</dd>
			<dt style="background-color: #00CC00"></dt><dd>Safari 2 (2.1+), Safari</dd>
			<dt style="background-color: #3CE400"></dt><dd>Safari 3 (3.1+), Safari</dd>
			<dt style="background-color: #00FF00"></dt><dd>Safari 4, Safari</dd>
			<dt style="background-color: #3CD3C8"></dt><dd>Chrome 1, Chrome</dd>
			<dt style="background-color: #00FFE4"></dt><dd>Chrome 2, Chrome</dd>
			<dt style="background-color: #B3478F"></dt><dd>Opera 9.5, Opera</dd>
			<dt style="background-color: #DD44AA"></dt><dd>Opera 9.6, Opera</dd>
			<dt style="background-color: #FF66CC"></dt><dd>Opera 10, Opera</dd>
			<dt id="dt_white" style="background-color: white"></dt><dd>Unknown browser</dd>
			<dt style="background-color: black; clear: left;"></dt><dd>Error: no XSL-transformation!</dd>
		</dl>
		<h2>Programs used for this Demo</h2>
		<p>
			The stylesheet invoked via the PI, <i>b-cond-elements.xsl</i>  is rather short,
			with demo specific code only, and imports the main stylesheet <i>cond-elements.xsl</i>
			which
		</p>
		<ul>
			<li>
				holds the special selection logic templates.
			</li>
			<li>
				uses a named template to break down the browsers and to return the
				tokens generated for them. Also returns (still experimental) the browsers
				name and version and the same for the rendering engine.
			</li>
			<li>
				and the identity template.
			</li>
		</ul>
		<p>
			To get the UA string, I use the <code>document()</code> function to
			call a small PHP program which sends back a XML document containing the
			UA string.
		</p>
		<pre>&lt;?php
header('Content-Type: application/xml;charset=UTF-8');
echo '&lt;doc&gt;' . $_SERVER['HTTP_USER_AGENT'] . '&lt;/doc&gt;';
return true;
?&gt;</pre>
		<p>
			Note: Opera 9 has problems here: prior to 9.50 the <code>document()</code>
			function is not defined and transformation does not run. Opera 10 has solved this
			problem.
		</p>
		<script src="http://www.google-analytics.com/ga.js" type="text/javascript"></script>
		<script type="text/javascript">
			var pageTracker = _gat._getTracker("UA-534617-5");
			pageTracker._initData();
			pageTracker._trackPageview();
		</script>
	</body>
</html>