WordPress 3.5 replaced the uploader with a Backbone application, and the hook every custom meta box was built on went with it. The old pattern opened ThickBox and overrode window.send_to_editor to catch the result — which 3.5 never calls, so the button opens the new modal, the user selects an image, and nothing at all happens.
jQuery( document ).on( 'click', '.td-pick-image', function ( event ) {
event.preventDefault();
var button = jQuery( this );
var frame = wp.media( {
title: 'Select a badge',
multiple: false,
library: { type: 'image' },
button: { text: 'Use this image' }
} );
frame.on( 'select', function () {
var attachment = frame.state().get( 'selection' ).first().toJSON();
button.siblings( 'input' ).val( attachment.id );
button.siblings( 'img' ).attr( 'src', attachment.sizes.thumbnail.url );
} );
frame.open();
} );
The frame hands back an attachment model instead of a string of HTML, which deletes the worst part of the old code — the regular expression that dug a URL out of an <img> tag. The id it gives you is also what the meta box should have been storing all along, since a stored URL breaks the day the site moves domain. Two things to watch. wp_enqueue_media() has to run on that admin screen or wp.media is undefined, and it is heavy enough that enqueueing it everywhere is a real cost on screens that never open a modal. And attachment.sizes is absent for anything WordPress did not generate thumbnails for, a PDF included, so reading sizes.thumbnail.url unguarded is the next bug report.