Is JavaScript enabled? - xslt

I need to make a fork (depending on javascript enabled or not):
<?xml version="1.0" encoding="windows-1251"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="no" encoding="windows-1251"/>
<xsl:template match="someNode">
<xsl:variable name="vNoscript">
<noscript>true</noscript>
</xsl:variable>
<xsl:choose>
<!-- javascript disabled -->
<xsl:when test="$vNoscript = 'true'">
code branch 1
</xsl:when>
<!-- javascript enabled -->
<xsl:otherwise>
code branch 2
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
This code is not working properly - the variable "vNoscript" in any case contains the value "true".
What are some ways to solve the problem?
May be necessary to do such tests very differently?
Update 1: I run this code on server.
Update 2: I need to dynamically load the pictures on page (using javascript). For those who have disabled Javascript in browser I need to upload pictures "in the usual way."

Since you are running this code on the server you can't know whether the client has JavaScript enabled or not.
One way to circumvent this problem is by using an HTTP request parameter telling you whether to generate a JavaScript-enabled version of your page or not and the client decides which version to call:
<script type="text/javascript">
document.write("<a href='http://www.example.com/somepage.php?js=true'>Link</a>")
</script>
<noscript>
<a href='http://www.example.com/somepage.php?js=false'>Link</a>
</noscript>
Then you either call separate XSL transformations, or you can use a single, parameterized XLST.
Update (after your edit):
Simply create noscript content in your XSLT as you would do in static HTML:
<?xml version="1.0" encoding="windows-1251"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="no" encoding="windows-1251"/>
<xsl:template match="someNode">
<!-- javascript enabled -->
HTML/JavaScript to dynamically load images
<noscript>fallback content</noscript>
</xsl:template>
</xsl:stylesheet>

Related

Choose XSL transform acoording to document content

I have a large number of XHTML documents which are created by different publishers, determined by a meta tag:
<meta name="citation_publisher" content="ACME publisher"/>
or in a different document
<meta name="citation_publisher" content="BETA publisher"/>
etc.
I have written stylesheets (about 1 page each) such as,
acme.xsl
beta.xsl
etc.
However I do not know the name of the publisher until I read the XHTML file.
It is possible, though very messy, to write a gigantic stylesheet of the form:
<xsl:choose>
<xsl:when test="$publisher='ACME publisher'">
<!-- acme.xsl sheet-->
</xsl:when>
<xsl:when test="$publisher='BETA publisher'">
<!-- beta.xsl sheet-->
</xsl:when>
</xsl:choose>
but there are at least 100 XSL files.
Is there any way, in XSL1, to select the stylesheet chunk according to the publisher? It would be nice to have the stylesheets as separate files and <xsl:import> them rather than have a single huge file.
UPDATE:
I think #Dimitre has answered the question correctly (and so I have accepted). I suspect that #MichaelKay's is actually better , but it does depend on having a pipeline managing the transformer. I shall try the <xsl:include> as a prototype and see whether it has downsides.
I wouldn't attempt to do this within a single XSLT stylesheet. It sounds to me like a good candidate for XProc, or some similar pipeline technology (e.g. Orbeon). Step 1, use XPath to classify the document, Step 2, transform it using the stylesheet chosen according to the results of Step 1.
but there are at least 100 XSL files. Is there any way, in XSL1, to
select the stylesheet chunk according to the publisher? It would be
nice to have the stylesheets as separate files and <xsl:import> them
rather than have a single huge file.
Here is one way to do this (I am showing working just with two content publisher types and this can be done for as many as needed):
Primary stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="unknown.xsl"/>
<xsl:import href="acme.xsl"/>
<xsl:import href="beta.xsl"/>
</xsl:stylesheet>
acme.xsl:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*[meta[#content='ACME publisher']]">
<xsl:value-of select="x * y"/>
</xsl:template>
</xsl:stylesheet>
beta.xsl:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*[meta[#content='BETA publisher']]">
<xsl:value-of select="x + y"/>
</xsl:template>
</xsl:stylesheet>
unknown.xsl:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<xsl:message terminate="yes">Error: Unknown content source</xsl:message>
</xsl:template>
</xsl:stylesheet>
When the transformation specified in the primary stylesheet is applied on this XML document:
acme.xml:
<t>
<meta name="citation_publisher" content="ACME publisher"/>
<x>6</x>
<y>4</y>
</t>
the wanted, correct result (x*y) is produced:
24
When the same transformation is applied on this XML document:
beta.xml:
<t>
<meta name="citation_publisher" content="BETA publisher"/>
<x>6</x>
<y>4</y>
</t>
again the correct result (x+y) is produced:
10
Finally, when the same transformation is applied on this XML document:
other.xml:
<t>
<meta name="citation_publisher" content="OTHER publisher"/>
<x>6</x>
<y>4</y>
</t>
the result of the transformation is the wanted termination with error message:
Error: Unknown content source
Processing terminated by xsl:message at line 5

Clientside XSLT in Mozilla/Firefox fails

I have a result document that renders in Chrome, but not Mozilla/Firefox.
I believe it is because there is top level leading whitespace (two blank lines before the <!DOCTYPE html).
How can I change this transform to not have leading whitespace (fiddle)?
XML:
<?xml-stylesheet href="/css/my.xsl" type="text/xsl"?>
<webpage>
<title>Book</title>
<auth>Mike</auth>
<container-content>
<p>foo1</p>
<p>foo2</p>
</container-content>
</webpage>
XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="1.0">
<xsl:output
method="xml"
indent="yes"
encoding="UTF-8"
omit-xml-declaration="yes"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"/>
<xsl:strip-space elements="*"/>
<xsl:template match="text()"/>
<xsl:template match="/">
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=utf-8"/>
</head>
<body>
<xsl:copy-of select="//container-content/*"/>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Result:
- a blank line here -
- and here -
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>
<body>
<p>foo1</p>
<p>foo2</p>
</body>
</html>
Alternatively, I may be incorrect, and the two blank lines are not the cause of the failed render in Mozilla/Firefox. I have a hard time troubleshooting client side transforms.
Side note: I've developed in Saxon 6.5, thinking Saxon best approximates what browsers do. I could be wrong. I note Xalan does not put in leading whitespace.
I just ran your stylesheet with Saxon 6.5 and indeed, it outputs two blank lines, which are removed if you change the xsl:output to be without indentation and with xml declaration. However, I believe this to be a bug in Saxon 6.5 (a small one, as the whitespace is not significant).
Running it with other XSLT 1.0 processors show no whitepace. However, as said in my comment, the whitespace is insignificant, as browsers do not serialize anyway. (note: apparently, browsers do some kind of serialization, in the sense that they look to whether you use XML or HTML output).
I ran your example with Firefox and it "just works". Since your stylesheet does a simple copy of the XML, it shows just the text. If I change the xsl:output to HTML and add a few lines to be sure I am running it correctly (I added an <h1>Hello</h1>, it shows the HTML.
I'm not sure what you expect the browser to show, but my guess is not XML, but (X)HTML. XSLT 1.0 is not very good with XHTML (it is supported in XSLT 2.0, but that is not supported by browsers), but works fine with HTML.
I modified your stylesheet as follows:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:template match="text()"/>
<xsl:template match="/">
<html>
<head />
<body>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template match="title">
<h1><xsl:value-of select="."/></h1>
</xsl:template>
<xsl:template match="auth">
<p>Author: <xsl:value-of select="." /></p>
</xsl:template>
</xsl:stylesheet>
And in Firefox and Chrome it renders as follows:
Note (1): if you do not run it from a web server (either local or remote), it will not run in either Firefox or Chrome because of security restrictions.
Note (2): to view the rendered XML or HTML, use the Inspect Element feature of the developer tools of either Chrome or Firefox.
Note (3): you do not need to use the meta-tag, as the specification requires this meta tag to be output as soon as it recognizes that HTML is output.
Note (4) if you are unsure whether or not Firefox is loading your stylesheet correctly, have a look using Firebug, it should show something like this (mark the "200 OK"):
If you want to transform to XHTML then you need to make sure you use the XHTML namespace for your result elements so put
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/1999/xhtml"
version="1.0">
on your stylesheet, as otherwise with output method xml your elements in no namespace are not recognized as XHTML elements by Mozilla.
As your input p elements are also in no namespace you can not copy them through but have to write a template for them
<xsl:template match="*">
<xsl:element name="{local-name()}"><!-- assumes you have the namespace declaration suggested above -->
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
and then use <xsl:apply-templates select="//container-content/*"/> instead of the copy-of. And in that case the <xsl:template match="text()"/> needs to be removed as otherwise the text of the transformed p elements would not show up.

Type of Amazon xml response breaks php xsl

Since Amazon shut off it's xslt support, I wanted to move it to my own server using php5's xsl. My output needs to be in a text format for my JS to process it for a web page. My problem is Amazon's xml response (very abbreviated) looks like this
<?xml version="1.0" ?>
<ItemLookupResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2011-08-01">
/............./
</ItemLookupResponse>
My problem is that my xsl stylesheet works fine as long as I remove the xmlns="http://...". What is needed in a xsl style to have it bypass or just ignore that ?
All the nodes I need are well inside that outer one.
Here is the xslt:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="CallBack" select="'amzJSONCallback'"/>
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:value-of select="$CallBack"/>
<xsl:text>( { "Item" : </xsl:text><xsl:apply-templates/><xsl:text> } ) </xsl:text>
</xsl:template>
<xsl:template match="OperationRequest"></xsl:template>
<xsl:template match="Request"></xsl:template>
<xsl:template match="Items">
<xsl:apply-templates select="Item"/>
</xsl:template>
<xsl:template match="Item">
<xsl:text> {</xsl:text>
<xsl:text>"title":"</xsl:text><xsl:apply-templates select="ItemAttributes/Title"/><xsl:text>",</xsl:text>
<xsl:text>"author":"</xsl:text><xsl:apply-templates select="ItemAttributes/Author"/><xsl:text>",</xsl:text>
<xsl:text>"pubbdate":"</xsl:text><xsl:apply-templates select="ItemAttributes/PublicationDate"/><xsl:text>"</xsl:text>
<xsl:text>} </xsl:text>
</xsl:template>
</xsl:stylesheet>
You should probably learn how XML namespaces work. In a nutshell, you have to define a namespace prefix in your XSL file like this:
<xsl:stylesheet ... xmlns:awse="http://webservices.amazon.com/AWSECommerceService/2011-08-01">
Then, you have to use qualified names to match and select elements under that namespace:
<xsl:template match="awse:ItemLookupResponse">
(With XSLT 2.0, you can define a default namespace. But since you're using PHP, you're probably limited to XSLT 1.0.)
It looks like nwellnhof is correct. I was using the wrong namespace in my testing. All I did was add:
<xsl:stylesheet ... xmlns:aws="http://webservices.amazon.com/AWSECommerceService/2011-08-01">
Then the elements look like
<xsl:template match="aws:ItemLookupResponse">
Now the conversion works perfectly. I don't know why it didn't work the first time I tried it.

with xslt how do I use choose/when to remove elements from document

Here is my example document
<a >
<b flag='foo'>
<c/>
<d/>
</b>
</a>
I am looking to remove the "c" element only when the flag attribute on b is 'bar'.
Ie if flag='foo' the "c" element should not be removed. I do not currently have an xsl debugging tool on my pc, and have been unable to find an online tool that shows xslt error information, and have been running the following test xsl transform on http://xslttest.appspot.com/ :
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes" version="1.0" encoding="ISO-8859-1"/>
<!--Identity template to copy all content by default-->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:choose>
<xsl:when test="/a/b[#flag='bar']">
<xsl:template match="/a/b/c"/>
</xsl:when>
</xsl:choose>
</xsl:stylesheet>
When I run this I get Error: Failed to compile stylesheet. 3 errors detected.
I'm looking for help (1) fixing the problems with the xsl code and (2) anything out there like jsfiddle for xsl that can debug/test fragements of xsl code.
You can't put a choose outside a template, but you don't need to - you can use predicates in match expressions so just declare your no-op template to match the elements you want to remove:
<xsl:template match="b[#flag='bar']/c" />
or more generally, if the parent of the c element might have various names
<xsl:template match="c[../#flag='bar']" />
or
<xsl:template match="*[#flag='bar']/c" />

How to get rid of xmlns: - Attributes in XSL transformation

I have an xsl transformation to generate ASP.NET User controls (ascx).
My XSL is defined this way:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:asp="System.Web.UI.WebControls"
exclude-result-prefixes="asp msxsl"
>
<xsl:output method="xml" indent="no" omit-xml-declaration="yes" />
So from that exclude-result-prefixes I would assume, that everything with the asp prefix should not add the namespace information, but i.e. this template here:
<xsl:template match="Label">
<asp:Label runat="server" AssociatedControlID="{../#id}">
<xsl:copy-of select="./text()"/>
</asp:Label>
</xsl:template>
fed with this xml:
<Label>Label Text</Label>
results in this output:
<asp:Label runat="server" AssociatedControlID="SomeName" xmlns:asp="System.Web.UI.WebControls">Label Text</asp:Label>
So what do I need to do to prevent the xmlns:asp=".." to show up in every single tag in my result?
It is impossible, at least in MSXML, that is because output XML won't be well-formed. You can only output it like text, e.g. using CDATA.