If only there were a way to send one width to IE5.x/Windows, and another width to modern browsers which supported the CSS box model... is a rough approximation of what Jeffrey said (emailed) to me at the time.
A lot of people have clout in the Web Standards world. However, few have as much as Tantek Çelik. For those unaware Tantek is the man behind, among other things, the development of the standards-compliant Microsoft Internet Explorer (before any of y'all start reaching for the page down key to access the comment box, keep reading ;) -- for Macintosh (there, you feel better now? Good. Making you feel better is what I'm here for. Just doing my job :)
So to restate, Tantek was the guy (he's with Technorati now) behind the development of Microsoft's IE for Mac product, which, as I'm sure most of you know by now has been recently sent to the MS support chopping block just in time for Christmas Dinner at Bill's place. Suprisingly, my invitation is yet to arrive in the mail... I'm sure thats just a technical 'glitch' however. I'm sure it'll arrive any day now. In fact, as soon as I'm done writing this I'm going to go wait by the mailbox. You know, just in case the mailman is holding back on me. I say this because I have suspected for quite some time now that he's the one behind the 'yet to arrive' invitation from the W3C XQuery Working Group to 'come on board' as an invited expert. I mean come on... its OBVIOUS that they've sent the invitation so what other possibilities could there be? Thats right... The mailman. I've long suspected him for a whole slough of 'missed deliveries'. For example there was the "no puppy for Christmas '76" incident that has yet to be solved... My guess? Thats right, the mailman never delivered that damn letter to Santa! That bastard. Do you know whats its like to be four years old and NOT have a puppy?! It DESTROYED ME!!! ;)
If not obvious, I pretty much blame the mailman for everything these days. No invitation to Bill's for Christmas. No XQuery WG invite. My little sister. You know, that kind of stuff. ;)
Phew.. OK, now that I have that off my conscience... back to Tantek's post. :D
So here's the deal... I have long struggled with the 'ideals' that many developers hold on to that demands that only one source code base sould be sufficient to properly display, as expected, in each and every browser ever produced that still holds any significant market share whatsoever. Why is that? (the developers ideals, not my struggles with these ideals.) Why can't it be OK to have to do a little server-side work to send one set of code to one browser and another set to another. Do you realize how much time, heartache, headaches, anxiety, and general sanity is saved when you allow yourself to let go of the ideals that EVERY browser must render the same source code the same way or its no good at all?
TONS. and TONS. and TONS.
Now, don't me wrong. This is not to suggest that we shouldn't continue to push for the browser manufacturers to work together to provide a standards-compliant browser that we can all count on to display the HTML/XHTML/CSS we send it, as long as that HTML/XHTML/CSS is standards-compliant itself, to render close to, if not exactly the same.
But we don't live in that world right now. In fact, we NEVER HAVE! So why continue to frustrate yourself with trying to hack 'the perfect markup base' together? I promise, even with the fantastic efforts of people such as Dean Edwards who have helped make our development lives quite a bit more sane than they were, with the existing base of browsers it's a pipe dream that will NEVER come to fruition. Of course, I guess if all you use is HTML 3.01, then yeah, that pipe dream CAN come true. But if this happens to be your pipe dream, then its not really a pipe dream in the real world, just in your head. In fact, if this 'dream' sounds even remotely close to something you hold near and dear to your heart...
Seek help.
Please.
Thanks :)
---
For the rest of you who are not currently pre-occupied with a therapy session, lets play with some code shall we. :)
Before we get started, I should preface this with the fact that one of the common arguments I hear against using server-side code to determine what browser is making the request is that you can't always count on the browser sending the correct User-Agent string. While I personally consider this 'excuse' the lamest possible reason why you shouldn't use server-side logic to determine what browser is making the request, I will give into the fact that this statement, in fact, is true. For like .0000001% of the requests, but none-the-less, true.[1]
But guess what... over the course of the last twelve(12) months we've gone from a world where client-side XSLT (1.0) wasn't considered a viable cross-platform, cross-browser solution, to a world where it can easily be used in conjunction with various other technologies as the very foundation in which ALL web pages are built. This isn't just me, Mr. "MDP+XSLT4EVER" trying to promote XSLT on the client because I personally think it's a fantastic programming language. In fact, while I do feel this way, I'm not even going to go as far as suggesting that it become the foundation of your Web 2.0 weblication development, although it would definitely be a good foundation to build upon.
Instead, I'm simply going to suggest that with a combination of using the User-Agent string, coupled with sending a simple XML "configuration" file the first time a browser makes a requests to any particular website you can quickly and easily determine whether the browser is being spoofed, and, if it is, send a vanilla webpage with the message "I'm on to ya!" in Bright Red 60pt Verdana (or something of your own choosing if this isn't something that'll work for your situation. ;)
Now, for the 99.9999999999% of the requests that will be determined the client is making no attempt to claim its something thats its not you can now reliably assume that you either have in your hands the correct browser information, or someone is going to the extent of spoofing just the version of the browser, but not the browser itself. If this happens to be the case, then please tell me you can live with ridiculously small percentage of folks who are going to this extent of pretending to be a browser version their not, writing them off as nothing to be concerned over. If you can't, starting with this paragraph as "1" please count back eight(8) paragraphs and consider this same advice for yourself. NOTE: That would be nine(9) paragraphs for all you smart a$$'s who suggest "---" counts as a paragraph... for those of you now questioning if "---" or a one(1) to two(2) word sentence really counts as a paragraph, or even further if a one or two word 'sentence' technically even qualifies as a sentence... bite me. ;)
For those of you still with me, thanks for not being a complete phreak :) Let's continue, shall we... :)
Now there's a couple caveats I need to make known: This currently doesn't work in Opera 9.0 preview... as outlined in footnote [1], the document() function currently blows up the browser... but again, as outlined below, my guess (and hope!) is that this will change with the final release.
Also, to keep the various processing tasks separate(e.g. configuration separate from output), normally I would create a separate stylesheet for the actual processing of the HTML output (e.g. page:output) and import this stylesheet using the xsl:import instruction element. But Transformiix has, um, how should I say... issues with imported stylesheets and namespaces which are not part of the base XML document being processed. Don't know, don't care, so don't ask. (read: I gave up trying to make sense of why Transformiix does half the things it does (or doesn't do, as the case may be) and instead try not to push it in places that, while nice (like keeping processing tasks such as configuration and output processing seperate), are not mandatory in regards to functionality.)
All right. One last thing: this sample is written using an ASP.NET server-side sample. I've borrowed from the code base of my client-side XML O'Reilly title a tiny bit. Not too much... I don't think O'Reilly would appreciate such things as me "pre-releasing" any of the actual code or content from the title. So I've watered things down and changed things such that I don't devalue the title in any way, while at the same time giving you all something to both think about and play with over the holidays.
This will also be a good teaser of sorts as to the content of the title. One thing you can be absolutely certain about... if you like code, theres LOTS and LOTS of code to play with in this title. If not obvious, I kind of like writing... code and text alike. But if you think I write a lot of text... Oh my... just wait till you see how much more I like to write code. Believe it or not, writing blog entries is a hobby, not a profession... Code is Life, at least my life anyway :D
OK, so with all of this stated, lets FINALLY look at some code.
First, the server-side (ASP.NET) code:
<%@ Page Trace="false" Language="C#" Debug="false"%>
<%@ Import Namespace="System.Xml" %>
<%@ Import Namespace="System.Xml.XPath" %>
<script runat="server">
void Page_Load(Object sender, EventArgs e)
{
String browser = Request.Browser["Browser"];
String UserAgentInvalidDoc = "./UserAgentInvalidDoc.xml";
String PItext = "type=\"text/xsl\" href=\"SessionConfig.xsl\"";
String compareBrowserStringDoc = "./compareBrowserString.xml";
XmlDocument xDoc = new XmlDocument();
xDoc.Load(Server.MapPath(compareBrowserStringDoc));
XPathNavigator xNav = xDoc.CreateNavigator();
Response.ContentType = "text/xml";
XmlTextWriter x = new XmlTextWriter(Response.Output);
x.Formatting = Formatting.Indented;
x.Indentation = 2;
x.WriteStartDocument();
x.WriteProcessingInstruction("xml-stylesheet", PItext);
x.WriteStartElement("my","session","http://channelxml.com/explorations/session");
x.WriteStartElement("my", "validation", "http://channelxml.com/explorations/session");
x.WriteStartElement("my", "UserAgentInvalidDoc", "http://channelxml.com/explorations/session");
x.WriteValue(UserAgentInvalidDoc);
x.WriteEndElement();
x.WriteStartElement("my", "browser", "http://channelxml.com/explorations/session");
x.WriteValue(browser);
x.WriteEndElement();
x.WriteNode(xNav, false);
x.WriteEndElement();
x.WriteEndElement();
x.Close();
}
</script>
Without spending time explaining how the above code works (its shouldn't be too hard to figure out, but if you're unfamiliar with ASP.NET or new to coding in general, I would HIGHLY recommend you pick up a copy of O'Reilly's ASP.NET In a Nutshell title. I use my copy often. A fantastic reference worth every penny (and I purchased my copy long before I gained any "free book" benefits from O'Reilly so I am stating this having spent cold hard cash to add it to my bookshelf.)
It should be fairly obvious that what the ASP.NET code does upon each request is build out an XML file with the browser the requesting client claims to be, a default fallback file that can be transformed when its determined by the client-side XSLT that the browser its claiming to be and the browser it actually is are not the same, and then embedded XML data that is used as a way of comparing the 'claimed' browser string to a list of known browser strings that would normally match the XSLT vendor system-property. A significant point worth mentioning comes in the answer to the question "Why do we need to combine the static XML file and the dynamic data in the first place? Can't we simply return the dynamic data and access the static file from the client using the document() function?" and the answer is quite simply yes, we can. But the server cost of combining these XML files into one XML file is minimal, where as the cost in performance in regards to the initial load time of the the application is quite signficant.
Why's that? Making that second request requires, well a second request. While this request does add a tiny bit of HTTP header overhead, its not exactly something we need to be overly concerned with. But the additional time it takes to make the additional request back to the server and have this same information returned in a separate XML data file? This has the potential to be fairly significant as when you add together the additional time it takes gain access to the requested file, as well as the additional time for the XML parser to parse the returned XML and make it available to the XSLT processor to continue forward... This all adds up to a human noticeable time lag. While time lag in making new requests once an application is loaded is expected, the initial load time of any given application has a significant weight on the perceived value, capability, and performance of any given application. This kind of thing REALLY matters. Finding ways to minimize the initial load time of any application is always the best philosophy. With that said you also don't want to burden your server with extensive processing as this obviously has an effect on the perceived performance as well given the fact that to the end user, time is time no matter if its the client or the server thats taking the time to do the processing. Obviously a balance needs to be struck. But such a balance is well beyond the scope of this post. Maybe for another post, but most definitely something you'll find as part of my O'Reilly title (insert cheezy "I'm so ashamed to be self-promoting" caption here... wait, I'm not ashamed to self promote... nevermind, just buy my Client-side XML title when it releases, K. ;)
Moving back to the actual XML file produced by the above ASP.NET script... you might be asking "why do I care what browser the client is claiming to be? Can't I just use the value returned by system-property(xsl:vendor) to determine what browser it is and be done with it?" and the answer would be, "yes, you can." But having this information is actually fairly important as, for example, Mozilla-based browsers such as Firefox and Netscape (well, at least partially in the 8.0 release anyway) all report back as using the Transformiix processor. For transformation of XML using Transformiix this information is not all that important as there's no difference at this stage between what version of Transformiix each browser is using. This might change in the future, but thats not what we're concerned with at the moment. What we're concerned with is the version of the underlying Mozilla processing engine as well as various other assumptions that can be made when we have the combination of browser and vendor at our disposal.
So then where is the browser version info? Good question. Given this code is influenced by code developed for the O'Reilly title, as well as the fact that this is really designed to merely showcase that this can be done, and done quite easily without tripling the code base for you to absorb. SO MUCH MORE is possible when you start using all of the tools you have at your disposal together, which is another purpose for this post... to showcase the fact that theres a whole new world of possibilities out there when you factor client-side XSLT into the mix, and all it takes is a bit of knowledge and a whole lot of imagination and... well, I'll leave the 'and' to your imagination. :)
Continuing forward, whether familiar with ASP.NET or not, looking at a sampling of the output of any given code-base is usually pretty helpful in quickly gaining an understanding of how things work, or at very least what can be expected when making a processing request such that you can take this data and do something else with it (which is really the whole point of making the request in the first place.)
With this in mind, the following is what would be returned by the above code when a request is made by Firefox:
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="SessionConfig.xsl"?>
<my:session xmlns:my="http://channelxml.com/explorations/session">
<my:validation>
<my:UserAgentInvalidDoc>./UserAgentInvalidDoc.xml</my:UserAgentInvalidDoc>
<my:browser>Firefox</my:browser>
<bVend:bVendors xmlns:bVend="http://channelxml.com/explorations/browservendors">
<bVend:vBrowser name="IE">
<bVend:vendor sourceXML="validIE.xml">Microsoft</bVend:vendor>
</bVend:vBrowser>
<bVend:vBrowser name="Firefox">
<bVend:vendor sourceXML="validFirefox.xml">Transformiix</bVend:vendor>
</bVend:vBrowser>
<bVend:vBrowser name="Mozilla">
<bVend:vendor sourceXML="validMozilla.xml">Transformiix</bVend:vendor>
</bVend:vBrowser>
<bVend:vBrowser name="Netscape">
<bVend:vendor sourceXML="validNetscape.xml">Transformiix</bVend:vendor>
</bVend:vBrowser>
<bVend:vBrowser name="AppleMAC-Safari">
<bVend:vendor sourceXML="validSafari.xml">libxslt</bVend:vendor>
</bVend:vBrowser>
<bVend:vBrowser name="Opera">
<bVend:vendor sourceXML="validOpera.xml">Opera</bVend:vendor>
</bVend:vBrowser>
</bVend:bVendors>
</my:validation>
</my:session>
Without spending too much time on the details, a couple of things need to be understood in regards to the above XML output.
First off, as mentioned above, the output is the result of a static XML file that has been intergrated with dynamic data specific to the request being made. This static XML file contains the information necessary for the client to determine if the browser is potentially being spoofed by comparing the value returned by the XSLT 1.0 function(parameter) combination of:
system-property('xsl:vendor')
Also, to gain access to the actual real world name of the requesting browser in ASP.NET (instead of using the User-Agent string and your typical indexOf(char) function to loop through potential values found in the UserAgent string or RegEx object/function to determine the same thing (e.g. MSIE for Internet Explorer) you would use:
Request.Browser["Browser"]
or, more specifically, if this is something you plan to use often within a particular server-side script, set the value of a string to the value of this property:
String browser = Request.Browser["Browser"];
With this combined data we can now quickly look at the XSLT the browser is being asked to use as the basis for transforming the above XML:
This XSLT file, by the way, is really two separate files merged to accomodate for the Transformiix issues with imported stylesheets. Combined, the two files look like this:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="http://channelxml.com/explorations/session" xmlns="http://www.w3.org/1999/xhtml"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns:bVend="http://channelxml.com/explorations/browservendors"
xmlns:page="http://channelxml.com/page/output" exclude-result-prefixes="my html page">
<xsl:output doctype-system="/resources/dtd/xhtml1-strict.dtd" doctype-public="-//W3C//DTD XHTML
1.0 Strict//EN" cdata-section-elements="script" indent="yes" method="xml"/>
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="my:session">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="my:validation">
<xsl:variable name="browser" select="my:browser"/>
<xsl:apply-templates select="bVend:bVendors/bVend:vBrowser[@name = $browser]">
<xsl:with-param name="invalidDoc" select="document(my:UserAgentInvalidDoc)"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="bVend:vBrowser">
<xsl:param name="invalidDoc"/>
<xsl:variable name="vendor" select="system-property('xsl:vendor')"/>
<xsl:choose>
<xsl:when test="bVend:vendor = $vendor">
<xsl:apply-templates select="document(bVend:vendor/@sourceXML)"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="document($invalidDoc)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="page:output">
<xsl:apply-templates select="page:html"/>
</xsl:template>
<xsl:template match="page:html">
<html>
<xsl:apply-templates/>
</html>
</xsl:template>
<xsl:template match="page:head">
<head>
<xsl:apply-templates/>
</head>
</xsl:template>
<xsl:template match="page:title">
<title>
<xsl:apply-templates/>
</title>
</xsl:template>
<xsl:template match="page:body">
<body>
<xsl:apply-templates/>
</body>
</xsl:template>
<xsl:template match="page:heading">
<h1 style="font-size:{@size}">
<xsl:apply-templates/>
</h1>
</xsl:template>
</xsl:stylesheet>
This post is already too long so I'm going to leave you to figure out how the above XSLT works. To those who already understand XSLT, its obviously not difficult to figure out. To those who are new to XSLT if you have any specific questions, leave them as a comment and I will do my best to answer them as time allows.
But don't worry, I'm not going to leave you hanging in regards to what is finally output to the browser. The one piece of missing XML data that is obviously pretty important in understanding what's taking place is the XML specific to each browser/XSLT processor combination. Given the Firefox example above, lets look at the Firefox specific XML file. If you look at the XML file we initially sent to the browser in the above example you'll notice that a browser/vendor combination of Firefox/Transformiix tells our XSLT processor to request the "validFirefox.xml" from the server. That files looks like:
<?xml version="1.0" encoding="utf-8" ?>
<page:output xmlns:page="http://channelxml.com/page/output">
<page:html>
<page:head>
<page:title>Hooray, Your a Valid Mozilla-based Browser (Firefox, to be more specific)! Here's a Cookie</page:title>
</page:head>
<page:body>
<page:heading size="large">Hooray, Your a Valid Mozilla-based Browser (Firefox, to be more specific)! Here's a Cookie.</page:heading>
</page:body>
</page:html>
</page:output>
To showcase what this looks like when rendered, instead of providing a screen shot, I'm instead going to provide you a URL to test this out for yourself. In quick dislaimer, I've tested this code to work correctly on Internet Explorer 6.0 and Firefox 1.5 on top of a WinXP Pro box, and on Safari and Firefox 1.5 on a Mac/OSX Tiger box. As mentioned, the document function blows up Opera 9.0 preview, so considered yourself warned... again. What I don't know for sure is whether this code renders on Firefox running on a Linux-box. I assume it does given the fact that it runs just fine on OSX/Firefox, but if you discover this not to be the case, then please let me know and I will log into my Linux-box and figure out whats up.
With that stated, you can access the live demo here. To help understand whats going on, try it on various browsers if you have any combination of Fx, IE, or Safari installed on your particular machine.
Obviously pretty simple. But what should also be obvious is how much more you could be doing with this file. For example, referencing platform specific Javascript instead of banging your head against the wall trying build a cross-browser/platform Javascript solution. The same thing goes when speaking in terms of platform specific CSS files. Which, in fact, brings us right back to the original point of this post in the first place. To summarize, let's come up with a nice little catch phrase that we can all use in moving forward with our client-side browser-based application development. Just a suggestion, but how about something like:
If Tantek says its OK, then its OK.
I realize that if a phrase such as this were to be adopted, it puts a lot of potential power into Tantek's hands. But I think its pretty safe to state that if such a power is going to be given to anybody, then we're in pretty good hands with Tantek.
Wait, I like the sound of that... Instead of the above, how about:
We're in good hands with Tantek
I like it... has a good sound, look, and feel to it. Plus, its true... Not that I'm really suggesting this as some sort of Mantra we all virtually chant every morning over IM, but paying attention to the things Tantek has to say, in and of itself, is a pretty good idea. So maybe we can just leave it at that.
And to reiterate his comments that spawned this post in the first place:
If only there were a way to send one width to IE5.x/Windows, and another width to modern browsers which supported the CSS box model... is a rough approximation of what Jeffrey said (emailed) to me at the time.
Hey, there is!
Cool :)
---
Before I close out this post, there is something else I do want to point out... Whats interesting about using this "Anti-Spoofing" method is not so much the fact that the underlying accomplishment... theres nothing new I've presented that makes your development life all that much easier or better by sending the browser string back to the client for comparison against the returned value of system-property('xsl:vendor') and instead the fact that we've put the responsibility of processing the data into the hands of the client browser. In this case we're forcing the client to use its own resources to determine its own honesty. But again, even this doesn't suggest that any great battle has been won. But if the end goal is to push as much of the data processing to the client, and by doing so freeing up server resources that can now be spent on serving up a greater numbers of requests for data (which is an obvious side effect of pushing the data processing to the client... less data processing, more requests for data) our total performance gains become more than just the value of the saved server resources as the amount of resources necessary to serve simple requests for data, even in increased capacity, is several magnitudes less than what it costs to process that same data on the server, sending the result to the client for rendering. We also gain a reduction in bandwidth necessary to transfer raw data that can then be rendered by a stylesheet preloaded into memory which can in many ways be compared to the benefits gained in compressing HTML and images on the server and decompressing them on the client. While this is not what is technically taking place, it's an easy way to view the comparison to what is ultimatelly the same result... reduced bandwidth means more requests can be served over the same wire without increase in bandwidth costs at a relatively small cost in "decompressing" that data once it arrives at the client. In many ways this is really more of an excercise in training our minds to be be constantly thinking of cheap ways to reduce bandwidth (both server-side processing and over-the-wire) in which the cost increase to the client is minimal, and no where near the total cost of not using these methods.
But thats not the only gained benefit.
An equally important point to realize is that we are also retraining our minds to think in terms of classic client-server architectures. By forcing ourselves to push as much of the data processing to the client and returning our mindesets to classic client-server architecture we've actually accomplished something quite a bit more than this. Similar to the concepts of Grid Computing, classic client-server architectures are extremely efficient due to the fact that they spread the overall computing cost across a greater "surface area". Using an often referenced cliche catch phrase "the combined total is greater than the sum of all its parts."
So have we suddenly just created our first Grid Computing application without realizing it? Well, close... but not exactly. There are plenty of elements of Grid Computing that have not been captured using this method. But what we have done is captured the "spirit" of Grid Computing, which if not already obvious, has it's roots in classic client-server application architecture. The fact that by using the above method we've turned the "tables of proof" back onto the client helps drive home the point that as we continue forward in the realm of web-based application development, we continue to bring ourselves closer and closer to the time tested and proven client-server architectures of years gone by, while at the same time building upon the ideals that make Grid Computing such an appealing end goal in the first place.
The more things change, the more they stay the same. Isn't that how the phrase is termed? :)
I'm pretty sure it is. ;)
Enjoy your Browser-based XML processing with Client-side XSLT and browser specific CSS and Javascript enhanced day!
---
[1] Until recently, Opera would send a User Agent string claiming it was Internet Explorer as to avoid being sent a 'stupified' code base, forcing it into a position of not being able to provide its user base with the same functionality that the IE user base had.
I have to admit that at the time they were doing this (this is no longer the case, by the way) I had a hard time with the complaints that would follow that they were now having to deal with a 'sloppy' IE-only non standards-compliant code-base. My own feelings were that if you claim to be something you're not, you can't then come back and complain that you are being served the very code you 'requested' in the first place. Either complain you're being served stupified code, but continue to serve up the correct user-agent string, or spoof the user-agent, and then build a browser that handles the non-compliant code you're being served. It's a little much to expect everyone to adapt to you instead of the reverse of adapting to everyone else. I'd use the phrase " you can't have your cake and eat it too" but I hate that saying, so I won't.
Besides, I don't have to anymore. Opera decided to take control of things and, to be honest, they're now my favorite browser. Why? Well, its always been the fastest browser... that's a 'no contest' type deal. With client-side XSLT (although there are some holes currently e.g. the document() function will blow up the browser, don't even try... and among others the functions name() and local-name() don't return anything, nor do they throw an error... my assumption is that these will be fixed given the fact that they are not optional in the XSLT 1.0 spec... however, if for whatever reason Opera decides not to provide support for these functions, and yet jump on top of their 'standards! standards! standards! bandwagon... I take all of this and what I am about to suggest back. Something tells me I won't have to do that though... I hope anyway ;) support in version 9.0 one of the last hold-ups I had with promoting Opera as the 'King of all browsers' was quashed and as such I feel fairly confident in stating that Opera is now the King of all browsers. At least they will be with the final 9.0 release.
With that said, I will admit that I really wish they would directly support XForms first before WebForms 2.0. But I'm also not going to let this get in the way (mentally anyway) of promoting it as the 'King' as I am now of the belief that if enough people want the support, it will arrive, it's just a matter of time. If not obvious, my views on Opera have changed DRAMATICALLY since they added support for client-side XSLT. To have overcome the 'guilt-by-association' clause that came as part of the political battle between CSS and XSL proper (as in XSL Formatting Objects, XSL-FO, or, as the W3C still insists on calling the specification, XSL - which is obviously easily confused as being the same as XSL-T, or more commonly termed XSLT given it too is often refered to as just XSL) was a HUGE thing. By overcoming this I have come to believe that Opera (the company) is now in the business of serving up products that contain features and functionality the customer base (or potential customer-base) is desirous of. Before now, and to be completely honest, I pretty much put Opera into the same basket I put Apple... or in other words "we'll do it our way, we'll provide you with the features WE want to give you, and you'll either like it, or live with it... but either way, you have no say in the matter." Whether this was a fair 'basket' to place them in or not, thats the impression I had always had from them. But not anymore... and as such I now believe it can easily be stated that Opera is the 'King' of ALL browsers, and the rest of the browser world needs to start playing catch-up to them instead of the other way around.
Believe it.
TrackBack URL for this entry:
http://www.xsltblog.com/xslt-blog-mt/mt-tb.cgi/1225
Listed below are links to weblogs that reference Finally, Someone With Some Web Standards/CSS 'Umph' Has Stated 'It's OK To Send Different Browsers Different Source'!:
» Disney World Vacation from Disney World Vacation
Disney World Vacation [Read More]
Tracked on April 2, 2006 02:53 PM