Index: wp-content/themes/twentyeleven/style.css
===================================================================
--- wp-content/themes/twentyeleven/style.css	(revision 17808)
+++ wp-content/themes/twentyeleven/style.css	(working copy)
@@ -489,9 +489,7 @@
 	margin: 0 0 4em;
 }
 #branding img {
-	height: auto;
 	margin-bottom: -7px;
-	width: 100%;
 }
 
 
Index: wp-content/themes/twentyeleven/header.php
===================================================================
--- wp-content/themes/twentyeleven/header.php	(revision 17808)
+++ wp-content/themes/twentyeleven/header.php	(working copy)
@@ -91,7 +91,7 @@
 						// Houston, we have a new header image!
 						echo get_the_post_thumbnail( $post->ID, 'post-thumbnail' );
 					else : ?>
-					<img src="<?php header_image(); ?>" width="<?php echo HEADER_IMAGE_WIDTH; ?>" height="<?php echo HEADER_IMAGE_HEIGHT; ?>" alt="" />
+					<img src="<?php header_image(); ?>" alt="" />
 				<?php endif; // end check for featured image or standard header ?>
 			</a>
 			<?php endif; // end check for removed header image ?>
Index: wp-content/themes/twentyeleven/functions.php
===================================================================
--- wp-content/themes/twentyeleven/functions.php	(revision 17808)
+++ wp-content/themes/twentyeleven/functions.php	(working copy)
@@ -122,6 +122,14 @@
 	define( 'HEADER_IMAGE_WIDTH', apply_filters( 'twentyeleven_header_image_width', 1000 ) );
 	define( 'HEADER_IMAGE_HEIGHT', apply_filters( 'twentyeleven_header_image_height', 300 ) );
 
+	/**
+	 * Add support for flexible header image height
+	 *
+	 * For v1 of this feature setting we don't allow setting the max dimensions to something other than HEADER_IMAGE_WIDTH and HEADER_IMAGE_HEIGHT
+	 * For Twenty Eleven the upload width is not flexible, but the height is flexible
+	*/
+	add_theme_support( 'flexible_header_image_upload', false, true ); 
+
 	// We'll be using post thumbnails for custom header images on posts and pages.
 	// We want them to be 940 pixels wide by 198 pixels tall.
 	// Larger images will be auto-cropped to fit, smaller ones will be ignored. See header.php.
Index: wp-admin/custom-header.php
===================================================================
--- wp-admin/custom-header.php	(revision 17808)
+++ wp-admin/custom-header.php	(working copy)
@@ -288,6 +288,140 @@
 	}
 
 	/**
+	 * Get the allowed header image size (width, height) as defined by the theme.
+	 *
+	 * If enabled, flexible upload dimensions will override legacy HEADER_IMAGE_WIDTH and HEADER_IMAGE_HEIGHT definitions.
+	 *
+	 * @since 3.2
+	 *
+	 * @param string Dimension value to return.
+	 * @return array
+	 */
+	function get_allowed_header_size( $dimension = '' ) {
+		$size = array(
+			'width' => ( defined( 'HEADER_IMAGE_WIDTH' ) ) ? HEADER_IMAGE_WIDTH : 0,
+			'height' => ( defined( 'HEADER_IMAGE_HEIGHT' ) ) ? HEADER_IMAGE_HEIGHT : 0
+		);
+
+		// todo enable this once ints are allowed as arguments
+		// $flex_dimensions = get_theme_support( 'flexible_header_image_upload' );
+		// if ( isset( $flex_dimensions[0] ) && false !== $flex_dimensions[0] )
+		// 	$size['width'] = $flex_dimensions[0];
+		// if ( isset( $flex_dimensions[1] ) && false !== $flex_dimensions[1] )
+		// 	$size['height'] = $flex_dimensions[1];
+
+		if ( ! empty( $dimension ) && isset( $size[$dimension] ) )
+			return $size[$dimension];
+
+		return $size;
+	}
+
+	/**
+	 * Set the parameters used for JS crop tool.
+	 * JS reference: http://odyniec.net/projects/imgareaselect/usage.html
+	 *
+	 * @since 3.2
+	 *
+	 * @param string Param value to return.
+	 * @return array
+	 */
+	function get_crop_params( $param = '' ) {
+		$params = array(
+			'use_aspect_ratio' => false,
+			'min_width' => 0,
+			'max_width' => 0,
+			'min_height' => 0,
+			'max_height' => 0
+		);
+
+		if ( ! current_theme_supports( 'flexible_header_image_upload' ) ) {
+			// No special params needed. Proceed with normal crop, and for backcompat.
+			$params['use_aspect_ratio'] = true;
+			$params['max_height'] = $this->get_allowed_header_size( 'height' );
+			$params['max_width'] = $this->get_allowed_header_size( 'width' );
+		} else {
+			$args = get_theme_support( 'flexible_header_image_upload' );
+			$is_flexible_width = $args[0];
+			$is_flexible_height = $args[1];
+
+			// Set the crop width parameters.
+			if ( false !== $is_flexible_width ) {
+				// Theme says "allow anything up to my header size." No minimum width needed.
+				$params['max_width'] = $this->get_allowed_header_size( 'width' );
+			} else {
+				// Theme says "no flexible width for this header." Min and max values should be the same.
+				$params['min_width'] = $this->get_allowed_header_size( 'width' );
+				$params['max_width'] = $this->get_allowed_header_size( 'width' );
+			}
+
+			// Set the crop height parameters.
+			if ( false !== $is_flexible_height ) {
+				// Theme says "allow anything up to my header size." No minimum height needed.
+				$params['max_height'] = $this->get_allowed_header_size( 'height' );
+			} else {
+				// Theme says "no flexible height for this header." Min and max values should be the same.
+				$params['min_height'] = $this->get_allowed_header_size( 'height' );
+				$params['max_height'] = $this->get_allowed_header_size( 'height' );
+			}
+		}
+
+		if ( ! empty( $param ) && isset( $params[$param] ) )
+			return $params[$param];
+
+		return $params;
+	}
+
+	/**
+	 * Set the crop type based on the uploaded image size versus allowed size.
+	 *
+	 * @since 3.2
+	 *
+	 * @param string $width Width of uploaded image.
+	 * @param string $height Height of uploaded image.
+	 * @return string $crop_type Type of action to take when cropping the image.
+	 */
+	function get_crop_type( $width = 0, $height = 0 ) {
+		$allowed_width = $this->get_allowed_header_size( 'width' );
+		$allowed_height = $this->get_allowed_header_size( 'height' );
+
+		// First, let's check if the image needs cropping. If not, exit.
+		if ( $width == $allowed_width && $height == $allowed_height )
+			return 'nocrop';
+
+		if ( ! current_theme_supports( 'flexible_header_image_upload' ) ) {
+			// Default crop, backcompat friendly. Height crop was not a previous feature.
+			return 'crop_to_width';
+		} else {
+			// We have flexibility on our hands.
+			$args = get_theme_support( 'flexible_header_image_upload' );
+			$is_flexible_width = ( false !== $args[0] ) ? true : false;
+			$is_flexible_height = ( false !== $args[1] ) ? true : false;
+
+			// If uploaded size is larger than allowed, let's force it to fit.
+			if ( $width > $allowed_width )
+				$is_flexible_width = false;
+			if ( $height > $allowed_height )
+				$is_flexible_height = false;
+
+			// If uploaded size is smaller than allowed, let's let it upload as is.
+			if ( $width <= $allowed_width )
+				$is_flexible_width = true;
+			if ( $height <= $allowed_height )
+				$is_flexible_height = true;
+
+			if ( $is_flexible_width && $is_flexible_height )
+				return 'nocrop';
+
+			if ( $width > $allowed_width )
+				return 'crop_to_width';
+			elseif ( $height > $allowed_height )
+				return 'crop_to_height';
+		}
+
+		return 'nocrop';
+	}
+
+	/**
 	 * Execute Javascript depending on step.
 	 *
 	 * @since 2.1.0
@@ -356,7 +490,7 @@
 
 		jQuery('#defaultcolor').click(function() {
 			pickColor(default_color);
-			jQuery('#text-color').val(default_color)
+			jQuery('#text-color').val(default_color);
 		});
 
 		jQuery('#text-color').keyup(function() {
@@ -395,9 +529,18 @@
 	/**
 	 * Display Javascript based on Step 2.
 	 *
+	 * @uses get_crop_params() to get custom crop parameters.
 	 * @since 2.6.0
 	 */
-	function js_2() { ?>
+	function js_2() {
+
+		// Set crop params
+		$aspect_ratio = ( false !== $this->get_crop_params( 'use_aspect_ratio' ) ? "aspectRatio: xinit + ':' + yinit," : '' );
+		$min_height = ( 0 != $this->get_crop_params( 'min_height' ) ? 'minHeight: ' . $this->get_crop_params( 'min_height' ) . ',' : '' );
+		$max_height = ( 0 != $this->get_crop_params( 'max_height' ) ? 'maxHeight: ' . $this->get_crop_params( 'max_height' ) . ',' : '' );
+		$min_width = ( 0 != $this->get_crop_params( 'min_width' ) ? 'minWidth: ' . $this->get_crop_params( 'min_width' ) . ',' : '' );
+		$max_width = ( 0 != $this->get_crop_params( 'max_width' ) ? 'maxWidth: ' . $this->get_crop_params( 'max_width' ) . ',' : '' );
+?>
 <script type="text/javascript">
 /* <![CDATA[ */
 	function onEndCrop( coords ) {
@@ -408,8 +551,8 @@
 	}
 
 	jQuery(document).ready(function() {
-		var xinit = <?php echo HEADER_IMAGE_WIDTH; ?>;
-		var yinit = <?php echo HEADER_IMAGE_HEIGHT; ?>;
+		var xinit = <?php echo $this->get_allowed_header_size( 'width' ); ?>;
+		var yinit = <?php echo $this->get_allowed_header_size( 'height' ); ?>;
 		var ratio = xinit / yinit;
 		var ximg = jQuery('img#upload').width();
 		var yimg = jQuery('img#upload').height();
@@ -427,14 +570,16 @@
 		jQuery('img#upload').imgAreaSelect({
 			handles: true,
 			keys: true,
-			aspectRatio: xinit + ':' + yinit,
+			<?php echo $aspect_ratio; ?>
 			show: true,
 			x1: 0,
 			y1: 0,
 			x2: xinit,
 			y2: yinit,
-			maxHeight: <?php echo HEADER_IMAGE_HEIGHT; ?>,
-			maxWidth: <?php echo HEADER_IMAGE_WIDTH; ?>,
+			<?php echo $min_height; ?>
+			<?php echo $max_height; ?>
+			<?php echo $min_width; ?>
+			<?php echo $max_width; ?>
 			onInit: function () {
 				jQuery('#width').val(xinit);
 				jQuery('#height').val(yinit);
@@ -479,17 +624,17 @@
 <th scope="row"><?php _e( 'Preview' ); ?></th>
 <td >
 	<?php if ( $this->admin_image_div_callback ) {
-	  call_user_func( $this->admin_image_div_callback );
+		call_user_func( $this->admin_image_div_callback );
 	} else {
 	?>
-	<div id="headimg" style="max-width:<?php echo HEADER_IMAGE_WIDTH; ?>px;height:<?php echo HEADER_IMAGE_HEIGHT; ?>px;background-image:url(<?php esc_url ( header_image() ) ?>);">
+	<div id="headimg" style="max-width:<?php echo $this->get_allowed_header_size( 'width' ); ?>px; height:<?php echo $this->get_allowed_header_size( 'height' ); ?>px;background-image:url(<?php esc_url( header_image() ); ?>);">
 		<?php
-		if ( 'blank' == get_theme_mod('header_textcolor', HEADER_TEXTCOLOR) || '' == get_theme_mod('header_textcolor', HEADER_TEXTCOLOR) || ! $this->header_text() )
+		if ( 'blank' == get_theme_mod( 'header_textcolor', HEADER_TEXTCOLOR ) || '' == get_theme_mod( 'header_textcolor', HEADER_TEXTCOLOR ) || ! $this->header_text() )
 			$style = ' style="display:none;"';
 		else
 			$style = ' style="color:#' . get_theme_mod( 'header_textcolor', HEADER_TEXTCOLOR ) . ';"';
 		?>
-		<h1><a id="name"<?php echo $style; ?> onclick="return false;" href="<?php bloginfo('url'); ?>"><?php bloginfo( 'name' ); ?></a></h1>
+		<h1><a id="name"<?php echo $style; ?> onclick="return false;" href="<?php bloginfo( 'url' ); ?>"><?php bloginfo( 'name' ); ?></a></h1>
 		<div id="desc"<?php echo $style; ?>><?php bloginfo( 'description' ); ?></div>
 	</div>
 	<?php } ?>
@@ -499,9 +644,11 @@
 <tr valign="top">
 <th scope="row"><?php _e( 'Upload Image' ); ?></th>
 <td>
-	<p><?php _e( 'You can upload a custom header image to be shown at the top of your site instead of the default one. On the next screen you will be able to crop the image.' ); ?><br />
-	<?php printf( __( 'Images of exactly <strong>%1$d &times; %2$d pixels</strong> will be used as-is.' ), HEADER_IMAGE_WIDTH, HEADER_IMAGE_HEIGHT ); ?></p>
-	<form enctype="multipart/form-data" id="upload-form" method="post" action="<?php echo esc_attr( add_query_arg( 'step', 2 ) ) ?>">
+	<p><?php _e( 'You can upload a custom header image to be shown at the top of your site instead of the default one. On the next screen you will be able to crop the image.' ); ?></p>
+
+	<p><?php printf( __( 'Images of exactly <strong>%1$d &times; %2$d pixels</strong> will be used as-is.' ), $this->get_allowed_header_size( 'width' ), $this->get_allowed_header_size( 'height' ) ); ?></p>
+
+	<form enctype="multipart/form-data" id="upload-form" method="post" action="<?php echo esc_attr( add_query_arg( 'step', 2 ) ); ?>">
 	<p>
 		<label for="upload"><?php _e( 'Choose an image from your computer:' ); ?></label><br />
 		<input type="file" id="upload" name="import" />
@@ -623,64 +770,99 @@
 	 * @since 2.1.0
 	 */
 	function step_2() {
-		check_admin_referer('custom-header-upload', '_wpnonce-custom-header-upload');
+		check_admin_referer( 'custom-header-upload', '_wpnonce-custom-header-upload' );
 		if ( ! current_theme_supports( 'custom-header-uploads' ) )
 			wp_die( 'Cheatin&#8217; uh?' );
 
-		$overrides = array('test_form' => false);
-		$file = wp_handle_upload($_FILES['import'], $overrides);
+		$overrides = array( 'test_form' => false );
+		$file = wp_handle_upload( $_FILES['import'], $overrides );
 
-		if ( isset($file['error']) )
-			wp_die( $file['error'],  __( 'Image Upload Error' ) );
+		if ( isset( $file['error'] ) )
+			wp_die( $file['error'], __( 'Image Upload Error' ) );
 
 		$url = $file['url'];
 		$type = $file['type'];
 		$file = $file['file'];
-		$filename = basename($file);
+		$filename = basename( $file );
 
 		// Construct the object array
 		$object = array(
-		'post_title' => $filename,
-		'post_content' => $url,
-		'post_mime_type' => $type,
-		'guid' => $url);
+			'post_title' => $filename,
+			'post_content' => $url,
+			'post_mime_type' => $type,
+			'guid' => $url
+		);
 
 		// Save the data
-		$id = wp_insert_attachment($object, $file);
+		$id = wp_insert_attachment( $object, $file );
 
-		list($width, $height, $type, $attr) = getimagesize( $file );
+		list( $width, $height, $type, $attr ) = getimagesize( $file );
 
-		if ( $width == HEADER_IMAGE_WIDTH && $height == HEADER_IMAGE_HEIGHT ) {
-			// Add the meta-data
-			wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $file ) );
-			update_post_meta( $id, '_wp_attachment_is_custom_header', get_option('stylesheet' ) );
+ 		$header_size = $this->get_allowed_header_size();
 
-			set_theme_mod('header_image', esc_url($url));
-			do_action('wp_create_file_in_uploads', $file, $id); // For replication
-			return $this->finished();
-		} elseif ( $width > HEADER_IMAGE_WIDTH ) {
-			$oitar = $width / HEADER_IMAGE_WIDTH;
-			$image = wp_crop_image($file, 0, 0, $width, $height, HEADER_IMAGE_WIDTH, $height / $oitar, false, str_replace(basename($file), 'midsize-'.basename($file), $file));
-			if ( is_wp_error( $image ) )
-				wp_die( __( 'Image could not be processed.  Please go back and try again.' ), __( 'Image Processing Error' ) );
+		// Proceed with cropping action
+		$crop_type = $this->get_crop_type( $width, $height );
+		switch ( $crop_type ) {
+			// Case nocrop = uploaded image dimensions match allowed dimensions exactly
+			case 'nocrop':
 
-			$image = apply_filters('wp_create_file_in_uploads', $image, $id); // For replication
+				// Add the meta-data
+				wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $file ) );
+				update_post_meta( $id, '_wp_attachment_is_custom_header', get_option( 'stylesheet' ) );
 
-			$url = str_replace(basename($url), basename($image), $url);
-			$width = $width / $oitar;
-			$height = $height / $oitar;
-		} else {
-			$oitar = 1;
-		}
-		?>
+				set_theme_mod( 'header_image', esc_url( $url ) );
+				do_action( 'wp_create_file_in_uploads', $file, $id ); // For replication
+				return $this->finished();
 
+				break;
+
+			// Uploaded image width is higher than allowed width
+			case 'crop_to_width':
+
+				$oitar = $width / $header_size['width'];
+				$image = wp_crop_image( $file, 0, 0, $width, $height, $header_size['width'], $height / $oitar, false, str_replace( basename( $file ), 'midsize-' . basename( $file ), $file ) );
+				if ( is_wp_error( $image ) )
+					wp_die( __( 'Image could not be processed. Please go back and try again.' ), __( 'Image Processing Error' ) );
+
+				$image = apply_filters( 'wp_create_file_in_uploads', $image, $id ); // For replication
+
+				$url = str_replace( basename( $url ), basename( $image ), $url );
+				$width = $width / $oitar;
+				$height = $height / $oitar;
+
+				break;
+
+			// Uploaded image height is higher than allowed height
+			case 'crop_to_height':
+
+				$oitar = $height / $header_size['height'];
+				$image = wp_crop_image( $file, 0, 0, $width, $height, $width / $oitar, $header_size['width'], false, str_replace( basename( $file ), 'midsize-' . basename( $file ), $file ) );
+				if ( is_wp_error( $image ) )
+					wp_die( __( 'Image could not be processed. Please go back and try again.' ), __( 'Image Processing Error' ) );
+
+				$image = apply_filters( 'wp_create_file_in_uploads', $image, $id ); // For replication
+
+				$url = str_replace( basename( $url ), basename( $image ), $url );
+				$width = $width / $oitar;
+				$height = $height / $oitar;
+
+				break;
+
+			// Width/height do not match exactly but uploaded dimensions are smaller than maximum allowed
+			// (Backcompat) normal crop
+			default:
+				$oitar = 1;
+				break;
+		} // END $crop_type switch
+?>
+
 <div class="wrap">
 <?php screen_icon(); ?>
 <h2><?php _e( 'Crop Header Image' ); ?></h2>
 
-<form method="post" action="<?php echo esc_attr(add_query_arg('step', 3)); ?>">
-	<p class="hide-if-no-js"><?php _e('Choose the part of the image you want to use as your header.'); ?></p>
-	<p class="hide-if-js"><strong><?php _e( 'You need Javascript to choose a part of the image.'); ?></strong></p>
+<form method="post" action="<?php echo esc_attr( add_query_arg( 'step', 3 ) ); ?>">
+	<p class="hide-if-no-js"><?php _e( 'Choose the part of the image you want to use as your header.' ); ?></p>
+	<p class="hide-if-js"><strong><?php _e( 'You need Javascript to choose a part of the image.' ); ?></strong></p>
 
 	<div id="crop_image" style="position: relative">
 		<img src="<?php echo esc_url( $url ); ?>" id="upload" width="<?php echo $width; ?>" height="<?php echo $height; ?>" />
@@ -692,7 +874,7 @@
 	<input type="hidden" name="height" id="height" value="<?php echo esc_attr( $height ); ?>"/>
 	<input type="hidden" name="attachment_id" id="attachment_id" value="<?php echo esc_attr( $id ); ?>" />
 	<input type="hidden" name="oitar" id="oitar" value="<?php echo esc_attr( $oitar ); ?>" />
-	<?php wp_nonce_field( 'custom-header-crop-image' ) ?>
+	<?php wp_nonce_field( 'custom-header-crop-image' ); ?>
 
 	<?php submit_button( __( 'Crop and Publish' ) ); ?>
 	</p>
@@ -707,7 +889,7 @@
 	 * @since 2.1.0
 	 */
 	function step_3() {
-		check_admin_referer('custom-header-crop-image');
+		check_admin_referer( 'custom-header-crop-image' );
 		if ( ! current_theme_supports( 'custom-header-uploads' ) )
 			wp_die( 'Cheatin&#8217; uh?' );
 
@@ -720,7 +902,12 @@
 
 		$original = get_attached_file( $_POST['attachment_id'] );
 
-		$cropped = wp_crop_image($_POST['attachment_id'], $_POST['x1'], $_POST['y1'], $_POST['width'], $_POST['height'], HEADER_IMAGE_WIDTH, HEADER_IMAGE_HEIGHT);
+		// If flexible upload, use the selected JS cropping dimensions be the destination size instead of hard-coded dimensions
+		if ( current_theme_supports( 'flexible_header_image_upload' ) )
+			$cropped = wp_crop_image( $_POST['attachment_id'], $_POST['x1'], $_POST['y1'], $_POST['width'], $_POST['height'], $_POST['width'], $_POST['height'] );
+		else
+			$cropped = wp_crop_image( $_POST['attachment_id'], $_POST['x1'], $_POST['y1'], $_POST['width'], $_POST['height'], $this->get_allowed_header_size( 'width' ), $this->get_allowed_header_size( 'height' ) );
+
 		if ( is_wp_error( $cropped ) )
 			wp_die( __( 'Image could not be processed.  Please go back and try again.' ), __( 'Image Processing Error' ) );
 
