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.

Copy/Paste text from Word into tinyMCE

What good is building a really nice CMS with top-notch WYSIWYG editor if you handed it to clients that will blindly copy/paste Word documents into it ?

Well, it will just render a big ugly mess of proprietary and contradictory pseudo-css rules. You could even be blamed for it.

The solution

The latest versions of tinyMCE came bundled with an improved pasteplugin. It will automatically attempt to clean bad pasted text by operating some dark voodoo magic on it.

It does it quite well to be honest, removing almost all messy formatting. From the tests I tried, I was still getting useless crap in the resulting text (CSS comments, <span> with overly long style of color and background-color definitions, etc).

I finally decided to take a more brutal approach. The pasteplugin contained a _insertPlainText method that was supposed to be used in conjunction with a toolbar icon.

This method removes all formatting. Period.

As I didn't want to have to click on a toolbar icon before pasting my text (and none of my actual clients would ever think of doing that either), I came up with a very simple plugin to do the boring stuff for me.

The plugin

The only goal of this plugin is setting to truethe pasteAsPlainTextproperty of every editor. This property is defaulted to falsebut can be swapped using the toolbar icon.

I also forced the paste_text_sticky setting to true, preventing the previous property to revert to falseafter the first pasting.

Here's my plugin full code :

Be sure to include the pasteplugin in your plugin list, and insert my plugin *after *the paste plugin.

/**
 *    Will automatically convert pasted text so no junk code will be included.
 *    This plugin depends on the core paste plugin.
 **/
(function() {
  tinymce.create('tinymce.plugins.pasteAsPlainTextPlugin', {
    init : function(editor, url) {
      // We force the pasting to occur as plain text
      editor.pasteAsPlainText = true;
      // We also put it as sticky to allow for multiple pastings
      editor.settings.paste_text_sticky = true;

      // Adding some special post process
      editor.settings.paste_postprocess = function(a, o) {
        var text = o.content,
          split = String.fromCharCode(13)+String.fromCharCode(10)
        ;

        // If content is long text without HTML, We'll break it into <p>ieces
        if (text.charAt(0)!='<' && text.indexOf(split)!=-1) {
          // Adding <p> around each line
          var node = document.createElement('div'),
            sentences = text.split(split)
          ;
          for(var i=0,max=sentences.length;i!=max;i++) {
            node.innerHTML+='<p>'+sentences[i]+'</p>';
          }

          // Saving back in original content/node
          o.node = node;
          o.content = node.innerHTML;
        }
        return o;
      }
    }
  });

  // Register plugin
  tinymce.PluginManager.add('pasteAsPlainText', tinymce.plugins[pasteAsPlainTextPlugin]);
})();

Update

I added a postprocesscalback after seing that the pasted text was a little too plain in Webkit. All my text was displayed on the same line, without the nice breaking into paragraph that Firefox showed.

It occured because tinyMCE used the event.clipboardData property that Webkit browsers provides and allow for easy retrieving of clipboard data. Unfortunatly it returned a really plain text, and I had to apply a little loop to replace each new line with a paragraph.

How to prevent a FOUC but still gracefully degrade the jQuery UI tabs

I finally managed to fix something that was bugging me for a very long time. I'm talking about the jQuery UI tabs.

They are pretty useful actually, but I always hated that FOUC they produce. For one split second, you'll see all your tabs content, then they'll be neatly re-arranged in tabs.

What I want are my tabs displayed correctly right away, on first load.

First, the markup

Here's a typical tabs markup :

<div class="tabs">
    <ul>
        <li><a href="#firstTab">First tab</a></li>
        <li><a href="#secondTab">Second tab</a></li>
    </ul>

    <div class="tabPanel" id="firstTab">
        First tab content
    </div>
   
    <div class="tabPanel" id="secondTab">
        Second tab content
    </div>
</div>

CSS to hide all tabs if Javascript is disabled

If your Javascript is disabled, so will jQuery UI. We will then hide the <ul> because it serves no purpose here. We will only show it if js is enabled

.tabs ul { display:none; }
.js .tabs ul { display:block; }

Applying jQuery UI tabs

By doing a$('.tabs').tabs(); jQuery UI will treat your <ul> as your tab menu and all your .tabPanel as their corresponding contents. It will hide all your panels, except for the first one. It does so by adding a.ui-tabs- panel class to every .tabPanel as well as a .ui-tabs-hide to every panel it hides.

Right now, you should add another CSS rule to hide the unused panels :

.ui-tabs-hide { display: none; }

But if you look at your page now, you'll see all your tabs content before they get hidden. That is the FOUC I was talking about. The jQuery UI documentation indicate that to remove it, you should directly add the .ui-tabs-hide class to panels you'll want to hide.

As also pointed in the doc, it will not gracefully degrade because users without Javascript won't even be able to see your other tabs. Also, it asks you to add server-side logic (HTML markup with jQuery specific classes) for something that should be handled entirely client-side.

Removing the FOUC while gracefully degrade

Ok, so what I did was writing two simple rules that will directly hide all unused panels while still displaying the active one, even before jQuery UI takes action.

.js .tabPanel + .tabPanel { display:none;}

That way, no FOUC, and users without Javascript still see all the content. Unfortunatly, if you now try clicking on your tabs, you'll see that nothing happens and you get stuck with your default panel always visible.

Fixing the jQuery UI tabs

As jQuery will add new classes to my elements, I'll just have to write more specific rules that use those classes. Here's the little piece of logic I came up with :

.js .tabPanel.ui-tabs-panel { display:block;}
.js .tabPanel.ui-tabs-panel.ui-tabs-hide { display:none;}

All the jQuery panels are shown, except for the one hidden by jQuery. All those rules being more and more specific, they will get applied once the tabs are activated but still override the previous display: declarations.

Conclusion

This is how I fixed an issue that was bugging me for years. Unfortunatly the CSS rules being dependent on the markup used, I haven't yet been able to write them in a global form that could be added to the main jQuery UI css files.

*[FOUC]: Flash Of Unstyled Content

CSS for Javascript enabled browsers

In a previous post I was wondering if my way of loading js-specific CSS rules was right. I was loading a specific css file for js overrides.

This allow for lighter stylesheets for users without Javascript (they won't load useless rules), but also resulted in slower rendering for other users because they needed to load two stylesheets instead of one.

After much pondering, I now think that loading all the rules in one stylesheet is the way to go.

In addition with the number of request mentionned, keeping all your rules in the same file is much more easier to maintain. Js-specific rules aren't that big either so the small additional bytes are usually better than a full http request.

I now prefix all my Javascript rules by .js. I add a js class to the htmlelement directly from the head, this helps in preventing any FOUC that may occur because we add the class before the body rendering.

<head>
    <script>document.documentElement.className+=' js';</script>
</head>