Some CSS hacks to target IE6 and IE7

After stopping using ugly IE hacks and moving to conditionnal comments to load a special IE stylesheet, I now use conditional comments to mark my body element with classes reflecting the current IE version.

<!--[if IE 6]><body class="ie ie6 ie-lt8 ie-lt9"><![endif]-->
<!--[if IE 7]><body class="ie ie7 ie-lt8 ie-lt9"><![endif]-->
<!--[if IE 8]><body class="ie ie8 ie-lt9"><![endif]-->
<!--[if IE 9]><body class="ie ie9"><![endif]-->
<!--[if !IE]><!--><body class="nie"><!--<![endif]-->

This saves me a lot of trouble : less files to manage and easier fixes to write. I quite happy with this solution and have tested it accross several projects for the past 3 months. It works really well.

I had to work on a legacy project last week, where this technique wasn't implemented but all the css code was still compressed using CSSTidy. And I ran into a couple of issues.

CSSTidy messes the star and underscore hacks

Using the brilliant _property and *property hacks to target IE6 and IE7 does not work in conjunction with CSSTidy.

For the _property hack, the property is kept as-is, with the underscore, but as they are alphabetically arranged, the _background gets added before the background, rendering it absolutly useless.

On the other hand, on the *property, the * gets removed, and the value is merged with the original value of the correct property. Useless too.

Other solutions that worked

To avoid digging into CSSTidy one more time, I tried to find other ways to achieve the same effect.

To target IE6 I used the !ie6 hack by writing .mySelector { property:value !ie6; }. IE6 is dumb enough to understand any !blahblah as !important.

I could have also used the fact that IE6 understands .class1.class2 as .class2, and could have written .ie6.mySelector { property:value; } (of course, you have absolutly no class="ie6" in your code)

To target IE7 I made a custom selector that its parsing engine is the only one to understand : *:first-child + html .mySelector { property:value; }

Preventing tinyMCE from wrapping <img /> in <p>

If you just want to add a presentational image in a tinyMCE editor, you'll find out very quickly that it will be wrapped in <p></p> without you asking.

The question has been asked several times on the tinyMCE forums, but the answers never quite satisfied me. It ranges from the classical "Why do you want to do this ? You should better use <insert semantic element and css here>" to "Just do a regexp before displaying your content to remove the bad <p>...</p>".

This clearly did not satisfy me.

The solution was to define theforced_root_block setting value to false, allowing us to create any element on the top level, and not having it automatically wrapped in <p>.

Fixing the side effect

Allowing for elements to be input directly in the root level has the nasty side effect of creating tinyMCE editor instances with a default text of, well, nothing, instead of the really usefull <p> tag. Also, if you do a Ctrl+A and delete all content, you'll end up with an empty editor without the initial <p> tags

To fix this part, I just added an onNodeChangeevent to fire every time the content is changed. I test the current content and if empty add the <p> tag. There is a little subtelty though, to correctly place the caret where needed.

In your tinyMCE.init call, just add the following setup key :

tinyMCE.init({
[...]
  setup: function(editor) {
    editor.onNodeChange.add(function(editor, cm, e, c, o) {
      var editorContent = editor.getContent();
      if (editorContent==="") {
        // We set content as a <p> containing a placeholder, then we delete the placeholder to place the caret
        editor.setContent('<p><span id="__CaretPlacholder">Placeholder</span></p>');
        editor.selection.select(editor.dom.select('#__CaretPlacholder')[0]);
        editor.dom.remove(editor.dom.select('#__CaretPlacholder')[0]);
      }
    });
   }),
[...]
 });

Before finding this solution, I tried the onBeforeSetContentcallback, but due to a bug in the tinyMCE source, it couldn't handle well the case where the editor is empty. So I had to resort to a more generic callback.

Displaying HTML mails sent from cakePHP in SquirrelMail

I just stumble upon the fact that my automated multipart (text + html) mails sent from my apps weren't correctly displayed on SquirrelMail.

I'm personnaly using GMail for my day to day email needs, but one of my client was using the default SquirrelMail (v1.4.21) install on their Dreamhost account. The received mails are not correctly parsed and get displayed as plain text, even if the header clearly specifiy a Content-Type: multipart/alternative; boundary="alt-"

The first culprit was that no boundary were given. This does not seem to cause GMail any problem, but I thought that SquirrelMail may choke on that. So I manually added a $this->Email->_createboundary(); call before my $this->Email->send();

Still, no succes. After some googling I found a bug report for a similar problem. I tried adding the suggested MIME header, and that solved my problem.

So here's my updated code :

$this->Email->sendAs = 'both';
$this->Email->_createboundary();
$this->Email->__header[] = 'MIME-Version: 1.0';
$this->Email->send();

I'll now have to add those two additional line of code everytime I'll have to send a multipart (html + text) email in cakePHP, until it get fixed (no specified milestone).

Aborting an AJAX request with jQuery

I needed a way to abort a pending AJAX request. I had a dialog box loaded through AJAX, but I let my users the possibility to close the window before it was completely loaded.

I thought I could easily cancel a request using jQuery but it appears that there is no method to do that. I had to instead directly call the .abort() method on the XHR object returned by $.ajax.

What I learned was that aborting an XHR request still triggered the jQuery ajax completecallback, and even passed a success as second argument.

My complete callback was supposed to display the loaded window, so I really didn't want it to fire if the user closed the loading window.

I finally had to check on the XHR.status property (200 if ok, 0 if aborted) to manually stop the completecallback.

It felt surprising at first that an abort call is not intercepted as an error, but after a little more thinking, it is logical. An abort is something that the user asked for, so it shouldn't be considered an error (which is something that no one ever asked for).

On the other hand, it feels strange to consider an abort as a success too. I guess the jQuery team didn't have a lot of request for an edge case like abort and didn't wrap their API around it, leaving us with the low level XHR object instead.

Openid redirect not working on Opera

I had a hard time firguring out while my openId login method does not work correctly with Opera.

I either ended up on the redirect page forever, or did not get redirected back to my website after Google check, or finally even get blackholed back on my own site.

I only occured with Opera.

I've read on various places that the security settings of Opera are made to prevent an automatic redirect to a website to be able to write cookie. This is a fair defense, but it does break the whole openId concept. Or at least my implementation.

Some sites manages to get it to work, even on Opera (stackoverflow for example).

I'll hide the openId login method for my Opera users until this is fixed or I find a solution.