Using Thickbox in the WordPress Admin

A complete guide to using Thickbox in the WordPress admin, despite the ingenious traps laid by the developers of the media manager and Thickbox itself. With this guide in hand, you too can shout out ‘Spaaaartaaaaa’ and kick bad code into a big-ass hole in the ground.

Anyway, I knew that WordPress shipped with Thickbox, for use with the media manager, and I wanted to use it to rewrite a pop-up window solution in a plugin for Wordy into using the iframe functionality of Thickbox. You know, the kind of thing that sounds easy until you start digging, right? Now, I could have gone with one of the newer Thickboxesque projects, but I wanted to have the plugin feel like it was a part of WordPress, and since the WordPress admin uses Thickbox, that’s the way to go.

Well, the first thing I learnt was how Thickbox is less than well-documented (not to mention stagnant since 2007), and after a short while of trying to make head and tail of the code, I found out that the version shipping with WP actually contains a bug. Nonetheless, I soon found a way to get it up and running to a point where I had only two major problems facing me.

First of all, most JS in the admin is loaded using load-scripts.php, which basically rolls up all the default JS and loads it in one big file at the end of the file, just before </body>, which in turn means that when Thickbox and its load-scripts bretheren queue up some initialization code using .ready(), it will inevitably come after any code I can enqueue, and thus my code can’t be sure that Thickbox has even been initialized when it’s run, which is compounded by the fact that Thickbox doesn’t leave behind any obvious signs of having been initialized.

Ugh.

There is a work-around however, because Thickbox attaches an event to certain elements with the ‘thickbox’ class. We can go in and find that event, and when it exists—which means Thickbox has initialized—run our stuff. We do this by going through the click event stack for the first found a.thickbox, turning it into a string, removing newlines, tabs and spaces from it and comparing it to what we know Thickbox puts there. Rince, lather, repeat until we find what we’re looking for, and we’re good to go (though, just to be sure, we force it to happen after 2 seconds regardless).

It ain’t purdy, but it works:

/**
 *	Checks the first found a.thickbox element to see if Thickbox has attached a click event.
 */
function checkThickboxInit() {
	jQuery.each( jQuery(‘a.thickbox:first’).data(‘events’)[‘click’], function(i, event) {
		var thisEvent	= event.toString().replace(/\n/g, ‘’).replace(/\t/g, ‘’).split(’ ‘).join(’‘);
		var TBEvent		= ‘function(){vart=this.title||this.name||null;vara=this.href||this.alt;varg=this.rel||false;tb_show(t,a,g);this.blur();returnfalse;}’;
	    if (thisEvent == TBEvent) {
	    	clearTimeout(forcePayment);
	    	clearInterval(checkThickboxInitInterval);

			postThickboxInit();

			return;
	    }
	})
}

function postThickboxInit() {
	/* Thickbox-related code can now be run */
}

checkThickboxInitInterval = setInterval(checkThickboxInit, 50);
forcePayment = setTimeout(‘clearInterval(checkThickboxInitInterval); postThickboxInit();’, 2000);

The Media Manager Horror

The second problem is much bigger. Mostly because it’s much stupider. Thickbox was added to WordPress along with the media manager, and whoever wrote that apparently decided that Thickbox wasn’t going to be used by anyone else anyway, so they might as well go ahead and break its core functionality. Get this. In media-upload.js, the tb_position() function, which is what Thickbox uses to control the size and position of a Thickbox window, is replaced in its entirety with a function meant to be used only with the media manager, which comes with hardcoded widths and which is attached to the window.resize event.

That took me a while to figure out.

Why this was necessary I have no idea; Thickbox allows you to set the size you want through URL parameters, which you’d think would be good enough for the media manager as well, but apparently not.

So we fight in the shade…

/**
 * Add ‘sparta’ class to the Thickbox window. Called from inside the TB iframe.
 */
function sparta_iframe_loaded() {
	jQuery(’#TB_window’).addClass(‘sparta’)
}

/**
 * Checks how to resize the TB window. Called on window.resize.
 */	
function sparta_window_resize() {
	if (jQuery(’#TB_window’).hasClass(‘sparta’))
		sparta_resize_thickbox();
	else
		tb_position();
}

/**
 * Resizes the TB window our way, not the highway.
 */
function sparta_resize_thickbox() {
	var SpartaWidth			= 1000;
	var TB_newWidth			= jQuery(window).width() < (SpartaPaymentWidth + 40) ? jQuery(window).width() – 40 : SpartaPaymentWidth;
	var TB_newHeight		= jQuery(window).height() – 70;
	var TB_newMargin		= (jQuery(window).width() – SpartaPaymentWidth) / 2;

	jQuery(’#TB_window’).css({‘marginLeft’: -(TB_newWidth / 2)})
	jQuery(’#TB_window, #TB_iframeContent’).width(TB_newWidth).height(TB_newHeight)
}

jQuery(document).ready(function() {
	/**
	 * Cast the media managers window.resize event into the fire.
 	 */
	jQuery.each( jQuery(window).data(‘events’)[‘resize’], function(i, event) {
		var thisEvent		= event.toString().replace(/\n/g, ‘’).replace(/\t/g, ‘’).split(’ ‘).join(’‘);
		var expectedEvent	= ‘function(){tb_position()}’;

	    if (thisEvent == expectedEvent) {
			delete jQuery(window).data(‘events’)[‘resize’][i];
			jQuery(window).bind(‘resize.ournamespace’, sparta_window_resize)
	    }
	})

	// Open a Thickbox window
	tb_show(‘This is Sparta’, ‘sparta.html?TB_iframe=true’);
}

Now create sparta.html and at the bottom of it, in a script block, insert the folllwing, so we can attach our own class to the TB window:

parent.sparta_iframe_loaded();

And there you have it. A letter-opener.

Update: A belorussian translation of this article.

A Word About Wordy

I’m lucky enough to have force-fed myself enough roleplaying games and science fiction comics to have picked up English to a level where I’m often more fluent in it, than I am in my mothertongue. And for the purposes of of blogging about those two particular subjects, whatever grammar, puntuation and structure snafus that happen to find their way onto this blog are less a real worry than they merely distracting (and at times embarrassing).

But if one were to take writing more seriously, be it for personal, academic or straight-up professional reasons, a friend of a friend of mine recently started a site that’ll do just that, hassle-free.

I don’t generally plug things on this site unless I truly like them. And until I tried Wordy, I honestly didn’t know what use I could have for it. But listen, Wordy gets it.

It’s on-demand copy-editing, and it’s ultra slick. No hassles, no clutter, no crap. I took it for a test-run on a chapter from a book another friend of mine is writing, and the experience couldn’t have been better. If for nothing else, you should check it out just to marvel at the elegance of how they’ve set up the site and how clear their process and goal is.

Particularly interesting to some of us, is that they’re working specifically on a WordPress plugin, which should make it even easier to use. They’ve also got a blog (in Danish).

Now if you’ll excuse me, I’ve got some K2 code to clean up.