diff --git wp-admin/css/wp-admin.css wp-admin/css/wp-admin.css
index 4959ad2..98b237e 100644
--- wp-admin/css/wp-admin.css
+++ wp-admin/css/wp-admin.css
@@ -2425,21 +2425,25 @@ table.fixed {
 
 .fixed .column-date,
 .fixed .column-parent,
-.fixed .column-links {
+.fixed .column-links,
+.fixed .column-format,
+.fixed .column-author {
 	width: 10%;
 }
 
-.fixed .column-response,
-.fixed .column-author,
 .fixed .column-categories,
-.fixed .column-tags,
+.fixed .column-tags {
+	width: 12.5%;
+}
+
+.fixed .column-response,
 .fixed .column-rel,
 .fixed .column-role {
 	width: 15%;
 }
 
 .fixed .column-comments {
-	width: 4em;
+	width: 3em;
 	padding: 8px 0;
 	text-align: left;
 }
diff --git wp-admin/edit-form-advanced.php wp-admin/edit-form-advanced.php
index 744f3b2..aa4c29c 100644
--- wp-admin/edit-form-advanced.php
+++ wp-admin/edit-form-advanced.php
@@ -141,8 +141,15 @@ if ( post_type_supports( $post_type, 'post-formats' ) ) {
 if ( post_type_supports($post_type, 'page-attributes') )
 	add_meta_box('pageparentdiv', 'page' == $post_type ? __('Page Attributes') : __('Attributes'), 'page_attributes_meta_box', null, 'side', 'core');
 
-if ( current_theme_supports( 'post-thumbnails', $post_type ) && post_type_supports( $post_type, 'thumbnail' ) )
-		add_meta_box('postimagediv', __('Featured Image'), 'post_thumbnail_meta_box', null, 'side', 'low');
+$audio_post_support = $video_post_support = false;
+$theme_support = current_theme_supports( 'post-thumbnails', $post_type ) && post_type_supports( $post_type, 'thumbnail' );
+if ( 'attachment' === $post_type && ! empty( $post->post_mime_type ) ) {
+	$audio_post_support = 0 === strpos( $post->post_mime_type, 'audio/' ) && current_theme_supports( 'post-thumbnails', 'attachment:audio' ) && post_type_supports( 'attachment:audio', 'thumbnail' );
+	$video_post_support = 0 === strpos( $post->post_mime_type, 'video/' ) && current_theme_supports( 'post-thumbnails', 'attachment:video' ) && post_type_supports( 'attachment:video', 'thumbnail' );
+}
+
+if ( $theme_support || $audio_post_support || $video_post_support )
+	add_meta_box('postimagediv', __('Featured Image'), 'post_thumbnail_meta_box', null, 'side', 'low');
 
 if ( post_type_supports($post_type, 'excerpt') )
 	add_meta_box('postexcerpt', __('Excerpt'), 'post_excerpt_meta_box', null, 'normal', 'core');
@@ -343,7 +350,7 @@ if ( post_type_supports( $post_type, 'post-formats' ) ) {
 		else
 			$class = 'nav-tab';
 
-		echo '<a class="' . $class . '" href="?format=' . $slug . '" data-wp-format="' . $slug . '">' . $label . '</a>';
+		echo '<a id="' . $slug . '" class="' . $class . '" href="?format=' . $slug . '" data-wp-format="' . $slug . '">' . $label . '</a>';
 	}
 
 	echo '</h2>';
diff --git wp-admin/includes/ajax-actions.php wp-admin/includes/ajax-actions.php
index 6d58765..57c8080 100644
--- wp-admin/includes/ajax-actions.php
+++ wp-admin/includes/ajax-actions.php
@@ -2019,6 +2019,8 @@ function wp_ajax_send_attachment_to_editor() {
 		$caption = isset( $attachment['post_excerpt'] ) ? $attachment['post_excerpt'] : '';
 		$title = ''; // We no longer insert title tags into <img> tags, as they are redundant.
 		$html = get_image_send_to_editor( $id, $caption, $title, $align, $url, (bool) $rel, $size, $alt );
+	} elseif ( 'video' === substr( $post->post_mime_type, 0, 5 ) || 'audio' === substr( $post->post_mime_type, 0, 5 )  ) {
+		$html = stripslashes_deep( $_POST['html'] );
 	}
 
 	$html = apply_filters( 'media_send_to_editor', $html, $id, $attachment );
diff --git wp-admin/includes/class-wp-posts-list-table.php wp-admin/includes/class-wp-posts-list-table.php
index 996dc4f..ed9efb1 100644
--- wp-admin/includes/class-wp-posts-list-table.php
+++ wp-admin/includes/class-wp-posts-list-table.php
@@ -214,6 +214,20 @@ class WP_Posts_List_Table extends WP_List_Table {
 				);
 				wp_dropdown_categories( $dropdown_options );
 			}
+
+			if ( post_type_supports( $this->screen->post_type, 'post-formats' ) ) {
+				$dropdown_options = array(
+					'show_option_all' => __( 'View all formats' ),
+					'orderby' => 'name',
+					'name' => 'format',
+					'taxonomy' => 'post_format',
+					'selected' => isset( $_GET['format'] ) ? $_GET['format'] : 0,
++					'hide_if_empty' => true
+				);
+
+				wp_dropdown_categories( $dropdown_options );
+			}
+
 			do_action( 'restrict_manage_posts' );
 			submit_button( __( 'Filter' ), 'button', false, false, array( 'id' => 'post-query-submit' ) );
 		}
diff --git wp-admin/includes/image.php wp-admin/includes/image.php
index 08d4c45..f2e636b 100644
--- wp-admin/includes/image.php
+++ wp-admin/includes/image.php
@@ -73,6 +73,7 @@ function wp_generate_attachment_metadata( $attachment_id, $file ) {
 	$attachment = get_post( $attachment_id );
 
 	$metadata = array();
+	$support = false;
 	if ( preg_match('!^image/!', get_post_mime_type( $attachment )) && file_is_displayable_image($file) ) {
 		$imagesize = getimagesize( $file );
 		$metadata['width'] = $imagesize[0];
@@ -117,8 +118,41 @@ function wp_generate_attachment_metadata( $attachment_id, $file ) {
 		if ( $image_meta )
 			$metadata['image_meta'] = $image_meta;
 
+	} elseif ( preg_match( '#^video/#', get_post_mime_type( $attachment ) ) ) {
+		$metadata = wp_read_video_metadata( $file );
+		$support = current_theme_supports( 'post-thumbnails', 'attachment:video' ) && post_type_supports( 'attachment:video', 'thumbnail' );
+	} elseif ( preg_match( '#^audio/#', get_post_mime_type( $attachment ) ) ) {
+		$metadata = wp_read_audio_metadata( $file );
+		$support = current_theme_supports( 'post-thumbnails', 'attachment:audio' ) && post_type_supports( 'attachment:audio', 'thumbnail' );
 	}
 
+	if ( $support && ! empty( $metadata['image']['data'] ) ) {
+		$ext = '.jpg';
+		switch ( $metadata['image']['mime'] ) {
+		case 'image/gif':
+			$ext = '.gif';
+			break;
+		case 'image/png':
+			$ext = '.png';
+			break;
+		}
+		$basename = str_replace( '.', '-', basename( $file ) ) . '-image' . $ext;
+		$uploaded = wp_upload_bits( $basename, '', $metadata['image']['data'] );
+		if ( false === $uploaded['error'] ) {
+			$attachment = array(
+				'post_mime_type' => $metadata['image']['mime'],
+				'post_type' => 'attachment',
+				'post_content' => '',
+			);
+			$sub_attachment_id = wp_insert_attachment( $attachment, $uploaded['file'] );
+			$attach_data = wp_generate_attachment_metadata( $sub_attachment_id, $uploaded['file'] );
+			wp_update_attachment_metadata( $sub_attachment_id, $attach_data );
+			update_post_meta( $attachment_id, '_thumbnail_id', $sub_attachment_id );
+		}
+	}
+	// remove the blob of binary data from the array
+	unset( $metadata['image']['data'] );
+
 	return apply_filters( 'wp_generate_attachment_metadata', $metadata, $attachment_id );
 }
 
diff --git wp-admin/includes/media.php wp-admin/includes/media.php
index c58b2d7..75aed1f 100644
--- wp-admin/includes/media.php
+++ wp-admin/includes/media.php
@@ -2285,8 +2285,9 @@ function edit_form_image_editor() {
 	$title = esc_attr( $post->post_title );
 	$alt_text = get_post_meta( $post->ID, '_wp_attachment_image_alt', true );
 
-	$att_url = wp_get_attachment_url( $post->ID );
-
+	$att_url = wp_get_attachment_url( $post->ID ); ?>
+	<div class="wp_attachment_holder">
+	<?php
 	if ( wp_attachment_is_image( $post->ID ) ) :
 		$image_edit_button = '';
 		if ( wp_image_editor_supports( array( 'mime_type' => $post->post_mime_type ) ) ) {
@@ -2294,7 +2295,7 @@ function edit_form_image_editor() {
 			$image_edit_button = "<input type='button' id='imgedit-open-btn-$post->ID' onclick='imageEdit.open( $post->ID, \"$nonce\" )' class='button' value='" . esc_attr__( 'Edit Image' ) . "' /> <span class='spinner'></span>";
 		}
  	?>
-	<div class="wp_attachment_holder">
+
 		<div class="imgedit-response" id="imgedit-response-<?php echo $attachment_id; ?>"></div>
 
 		<div<?php if ( $open ) echo ' style="display:none"'; ?> class="wp_attachment_image" id="media-head-<?php echo $attachment_id; ?>">
@@ -2304,9 +2305,23 @@ function edit_form_image_editor() {
 		<div<?php if ( ! $open ) echo ' style="display:none"'; ?> class="image-editor" id="image-editor-<?php echo $attachment_id; ?>">
 			<?php if ( $open ) wp_image_editor( $attachment_id ); ?>
 		</div>
-	</div>
-	<?php endif; ?>
+	<?php
+	elseif ( $attachment_id && 0 === strpos( $post->post_mime_type, 'audio/' ) ):
+
+		echo do_shortcode( '[audio src="' . $att_url . '"]' );
+
+	elseif ( $attachment_id && 0 === strpos( $post->post_mime_type, 'video/' ) ):
+
+		$meta = wp_get_attachment_metadata( $attachment_id );
+		$shortcode = sprintf( '[video src="%s" width="%d" height="%d"]',
+			$att_url,
+			$meta['width'],
+			$meta['height']
+		);
+		echo do_shortcode( $shortcode );
 
+	endif; ?>
+	</div>
 	<div class="wp_attachment_details edit-form-section">
 		<p>
 			<label for="attachment_caption"><strong><?php _e( 'Caption' ); ?></strong></label><br />
@@ -2397,3 +2412,140 @@ add_filter( 'media_upload_gallery', 'media_upload_gallery' );
 add_filter( 'media_upload_library', 'media_upload_library' );
 
 add_action( 'attachment_submitbox_misc_actions', 'attachment_submitbox_metadata' );
+
+/**
+ * Parse ID3v2, ID3v1, and getID3 comments to extract usable data
+ *
+ * @since 3.6.0
+ *
+ * @param array $metadata An existing array with data
+ * @param array $data Data supplied by ID3 tags
+ */
+function wp_add_id3_tag_data( &$metadata, $data ) {
+	foreach ( array( 'id3v2', 'id3v1' ) as $version ) {
+		if ( ! empty( $data[$version]['comments'] ) ) {
+			foreach ( $data[$version]['comments'] as $key => $list ) {
+				if ( ! empty( $list ) ) {
+					$metadata[$key] = reset( $list );
+					// fix bug in byte stream analysis
+					if ( 'terms_of_use' === $key && 0 === strpos( $metadata[$key], 'yright notice.' ) )
+						$metadata[$key] = 'Cop' . $metadata[$key];
+				}
+			}
+			break;
+		}
+	}
+
+	if ( ! empty( $data['id3v2']['APIC'] ) ) {
+		$image = reset( $data['id3v2']['APIC']);
+		if ( ! empty( $image['data'] ) ) {
+			$metadata['image'] = array(
+				'data' => $image['data'],
+				'mime' => $image['image_mime'],
+				'width' => $image['image_width'],
+				'height' => $image['image_height']
+			);
+		}
+	} elseif ( ! empty( $data['comments']['picture'] ) ) {
+		$image = reset( $data['comments']['picture'] );
+		if ( ! empty( $image['data'] ) ) {
+			$metadata['image'] = array(
+				'data' => $image['data'],
+				'mime' => $image['image_mime']
+			);
+		}
+	}
+}
+
+/**
+ * Retrieve metadata from a video file's ID3 tags
+ *
+ * @since 3.6.0
+ *
+ * @param string $file Path to file.
+ * @return array|boolean Returns array of metadata, if found.
+ */
+function wp_read_video_metadata( $file ) {
+	if ( ! file_exists( $file ) )
+		return false;
+
+	$metadata = array();
+
+	if ( ! class_exists( 'getID3' ) )
+		require( ABSPATH . WPINC . '/ID3/class-getid3.php' );
+	$id3 = new getID3();
+	$data = $id3->analyze( $file );
+
+	if ( isset( $data['video']['lossless'] ) )
+		$metadata['lossless'] = $data['video']['lossless'];
+	if ( ! empty( $data['video']['bitrate'] ) )
+		$metadata['bitrate'] = (int) $data['video']['bitrate'];
+	if ( ! empty( $data['video']['bitrate_mode'] ) )
+		$metadata['bitrate_mode'] = $data['video']['bitrate_mode'];
+	if ( ! empty( $data['filesize'] ) )
+		$metadata['filesize'] = (int) $data['filesize'];
+	if ( ! empty( $data['mime_type'] ) )
+		$metadata['mime_type'] = $data['mime_type'];
+	if ( ! empty( $data['playtime_seconds'] ) )
+		$metadata['length'] = (int) ceil( $data['playtime_seconds'] );
+	if ( ! empty( $data['playtime_string'] ) )
+		$metadata['length_formatted'] = $data['playtime_string'];
+	if ( ! empty( $data['video']['resolution_x'] ) )
+		$metadata['width'] = (int) $data['video']['resolution_x'];
+	if ( ! empty( $data['video']['resolution_y'] ) )
+		$metadata['height'] = (int) $data['video']['resolution_y'];
+	if ( ! empty( $data['fileformat'] ) )
+		$metadata['fileformat'] = $data['fileformat'];
+	if ( ! empty( $data['video']['dataformat'] ) )
+		$metadata['dataformat'] = $data['video']['dataformat'];
+	if ( ! empty( $data['video']['encoder'] ) )
+		$metadata['encoder'] = $data['video']['encoder'];
+	if ( ! empty( $data['video']['codec'] ) )
+		$metadata['codec'] = $data['video']['codec'];
+
+	unset( $data['audio']['streams'] );
+	$metadata['audio'] = $data['audio'];
+
+	wp_add_id3_tag_data( $metadata, $data );
+
+	return $metadata;
+}
+
+/**
+ * Retrieve metadata from a audio file's ID3 tags
+ *
+ * @since 3.6.0
+ *
+ * @param string $file Path to file.
+ * @return array|boolean Returns array of metadata, if found.
+ */
+function wp_read_audio_metadata( $file ) {
+	if ( ! file_exists( $file ) )
+		return false;
+	$metadata = array();
+
+	if ( ! class_exists( 'getID3' ) )
+		require( ABSPATH . WPINC . '/ID3/class-getid3.php' );
+	$id3 = new getID3();
+	$data = $id3->analyze( $file );
+
+	if ( ! empty( $data['audio'] ) ) {
+		unset( $data['audio']['streams'] );
+		$metadata = $data['audio'];
+	}
+
+	if ( ! empty( $data['fileformat'] ) )
+		$metadata['fileformat'] = $data['fileformat'];
+	if ( ! empty( $data['filesize'] ) )
+		$metadata['filesize'] = (int) $data['filesize'];
+	if ( ! empty( $data['mime_type'] ) )
+		$metadata['mime_type'] = $data['mime_type'];
+	if ( ! empty( $data['playtime_seconds'] ) )
+		$metadata['length'] = (int) ceil( $data['playtime_seconds'] );
+	if ( ! empty( $data['playtime_string'] ) )
+		$metadata['length_formatted'] = $data['playtime_string'];
+
+	wp_add_id3_tag_data( $metadata, $data );
+
+	return $metadata;
+}
diff --git wp-admin/includes/post.php wp-admin/includes/post.php
index dfd47b0..b20ff6d 100644
--- wp-admin/includes/post.php
+++ wp-admin/includes/post.php
@@ -868,7 +868,12 @@ function wp_edit_posts_query( $q = false ) {
 	$posts_per_page = apply_filters( $per_page, $posts_per_page );
 	$posts_per_page = apply_filters( 'edit_posts_per_page', $posts_per_page, $post_type );
 
-	$query = compact('post_type', 'post_status', 'perm', 'order', 'orderby', 'posts_per_page');
+	$query = compact( 'post_type', 'post_status', 'perm', 'order', 'orderby', 'posts_per_page' );
+	if ( ! empty( $q['format'] ) ) {
+		$query['taxonomy'] = 'post_format';
+		$term = get_term( $q['format'], $query['taxonomy'] );
+		$query['term'] = $term->slug;
+	}
 
 	// Hierarchical types require special args.
 	if ( is_post_type_hierarchical( $post_type ) && !isset($orderby) ) {
diff --git wp-admin/js/post-formats.js wp-admin/js/post-formats.js
index 27f638a..3dbda7b 100644
--- wp-admin/js/post-formats.js
+++ wp-admin/js/post-formats.js
@@ -12,6 +12,14 @@ window.wp = window.wp || {};
 		$this.addClass('nav-tab-active').blur();
 		$('#post_format').val(format);
 		$('#post-body-content').attr('class', 'wp-format-' + format );
+
+		var ed = tinymce.get( 'content' ), edBody, format;
+		if ( ed && this.id ) {
+			edBody = ed.getBody();
+			format = 'post-format-' + this.id; // also need to handle 'post-format-0'
+			edBody.className = edBody.className.replace( /\bpost-format-[^ ]+/, '' );
+			ed.dom.addClass( edBody, format );
+		}
 	});
 
 	// Image selection
diff --git wp-includes/class-wp-editor.php wp-includes/class-wp-editor.php
index 38ab7c2..4622db8 100644
--- wp-includes/class-wp-editor.php
+++ wp-includes/class-wp-editor.php
@@ -395,9 +395,18 @@ final class _WP_Editors {
 			}
 
 			$body_class = $editor_id;
-
-			if ( $post = get_post() )
-				$body_class .= ' post-type-' . $post->post_type;
+			$post = get_post();
+
+			if ( $post ) {
+				$body_class .= ' post-type-' . $post->post_type . ' post-status-' . $post->post_status;
+				if ( post_type_supports( $post->post_type, 'post-formats' ) ) {
+					$post_format = get_post_format( $post );
+					if ( $post_format && ! is_wp_error( $post_format ) )
+						$body_class .= ' post-format-' . sanitize_html_class( $post_format );
+					else
+						$body_class .= ' post-format-standard';
+				}
+			}
 
 			if ( !empty($set['tinymce']['body_class']) ) {
 				$body_class .= ' ' . $set['tinymce']['body_class'];
diff --git wp-includes/functions.php wp-includes/functions.php
index e0b8cb8..ca414ea 100644
--- wp-includes/functions.php
+++ wp-includes/functions.php
@@ -4020,3 +4020,23 @@ $("#wp-auth-check-form iframe").load(function(){
 </script>
 </div>' ) );
 }
+
+/**
+ * Return RegEx body to liberally match an opening HTML tag that:
+ * 1. Is self-closing or
+ * 2. Has no body but has a closing tag of the same name or
+ * 3. Contains a body and a closing tag of the same name
+ *
+ * Note: this RegEx does not balance inner tags and does not attempt to produce valid HTML
+ *
+ * @since 3.6.0
+ *
+ * @param string $tag An HTML tag name. Example: 'video'
+ * @return string
+ */
+function get_tag_regex( $tag ) {
+	if ( empty( $tag ) )
+		return;
+
+	return sprintf( '(<%1$s[^>]*(?:/?>$|>[\s\S]*?</%1$s>))', tag_escape( $tag ) );
+}
\ No newline at end of file
diff --git wp-includes/js/media-editor.js wp-includes/js/media-editor.js
index 15eff8d..7dbbb79 100644
--- wp-includes/js/media-editor.js
+++ wp-includes/js/media-editor.js
@@ -66,7 +66,8 @@
 					src:       size.url,
 					captionId: 'attachment_' + attachment.id
 				});
-
+			} else if ( 'video' === attachment.type || 'audio' === attachment.type ) {
+				_.extend( props, _.pick( attachment, 'title', 'type', 'icon', 'mime' ) );
 			// Format properties for non-images.
 			} else {
 				props.title = props.title || attachment.filename;
@@ -95,6 +96,89 @@
 			return wp.html.string( options );
 		},
 
+		audio: function( props, attachment ) {
+			var shortcode, html;
+
+			props = wp.media.string.props( props, attachment );
+
+			shortcode = {};
+
+			if ( props.mime ) {
+				switch ( props.mime ) {
+				case 'audio/mpeg':
+					if ( props.linkUrl.indexOf( 'mp3' ) )
+						shortcode.mp3 = props.linkUrl;
+					else if ( props.linkUrl.indexOf( 'm4a' ) )
+						shortcode.m4a = props.linkUrl;
+					break;
+				case 'audio/mp3':
+					shortcode.mp3 = props.linkUrl;
+					break;
+				case 'audio/m4a':
+					shortcode.m4a = props.linkUrl;
+					break;
+				case 'audio/wav':
+					shortcode.wav = props.linkUrl;
+					break;
+				case 'audio/ogg':
+					shortcode.ogg = props.linkUrl;
+					break;
+				case 'audio/x-ms-wma':
+				case 'audio/wma':
+					shortcode.wma = props.linkUrl;
+					break;
+				}
+			}
+
+			html = wp.shortcode.string({
+				tag:     'audio',
+				attrs:   shortcode
+			});
+
+			return html;
+		},
+
+		video: function( props, attachment ) {
+			var shortcode, html;
+
+			props = wp.media.string.props( props, attachment );
+
+			shortcode = {};
+
+			if ( props.mime ) {
+				switch ( props.mime ) {
+				case 'video/mp4':
+					shortcode.mp4 = props.linkUrl;
+					break;
+				case 'video/m4v':
+					shortcode.m4v = props.linkUrl;
+					break;
+				case 'video/webm':
+					shortcode.webm = props.linkUrl;
+					break;
+				case 'video/ogg':
+					shortcode.ogv = props.linkUrl;
+					break;
+				case 'video/x-ms-wmv':
+				case 'video/wmv':
+				case 'video/asf':
+					shortcode.wmv = props.linkUrl;
+					break;
+				case 'video/flv':
+				case 'video/x-flv':
+					shortcode.flv = props.linkUrl;
+					break;
+				}
+			}
+
+			html = wp.shortcode.string({
+				tag:     'video',
+				attrs:   shortcode
+			});
+
+			return html;
+		},
+
 		image: function( props, attachment ) {
 			var img = {},
 				options, classes, shortcode, html;
@@ -575,7 +659,10 @@
 						if ( props[ prop ] )
 							options[ option ] = props[ prop ];
 					});
-
+				} else if ( 'video' === attachment.type ) {
+					html = wp.media.string.video( props );
+				} else if ( 'audio' === attachment.type ) {
+					html = wp.media.string.audio( props );
 				} else {
 					html = wp.media.string.link( props );
 					options.post_title = props.title;
diff --git wp-includes/media.php wp-includes/media.php
index 9ef641c..bffba5e 100644
--- wp-includes/media.php
+++ wp-includes/media.php
@@ -805,6 +805,233 @@ function gallery_shortcode($attr) {
 }
 
 /**
+ * Provide a No-JS Flash fallback as a last resort for audio / video
+ *
+ * @since 3.6.0
+ *
+ * @param string $url
+ * @return string Fallback HTML
+ */
+function wp_mediaelement_fallback( $url ) {
+	return apply_filters( 'wp_mediaelement_fallback', sprintf( '<a href="%1$s">%1$s</a>', esc_url( $url ) ), $url );
+}
+
+/**
+ * Return a filtered list of WP-supported audio formats
+ *
+ * @since 3.6.0
+ * @return array
+ */
+function wp_get_audio_extensions() {
+	return apply_filters( 'wp_audio_extensions', array( 'mp3', 'ogg', 'wma', 'm4a', 'wav' ) );
+}
+
+/**
+ * The Audio shortcode.
+ *
+ * This implements the functionality of the Audio Shortcode for displaying
+ * WordPress mp3s in a post.
+ *
+ * @since 3.6.0
+ *
+ * @param array $attr Attributes of the shortcode.
+ * @return string HTML content to display audio.
+ */
+function wp_audio_shortcode( $attr ) {
+	$post_id = get_post() ? get_the_ID() : 0;
+
+	static $instances = 0;
+	$instances++;
+
+	$audio = null;
+
+	$default_types = wp_get_audio_extensions();
+	$defaults_atts = array( 'src' => '' );
+	foreach ( $default_types as $type  )
+		$defaults_atts[$type] = '';
+
+	$atts = shortcode_atts( $defaults_atts, $attr );
+	extract( $atts );
+
+	$primary = false;
+	if ( ! empty( $src ) ) {
+		$type = wp_check_filetype( $src );
+		if ( ! in_array( $type['ext'], $default_types ) ) {
+			printf( '<a class="wp-post-format-link-audio" href="%1$s">%1$s</a>', $src );
+			return;
+		}
+		$primary = true;
+		array_unshift( $default_types, 'src' );
+	} else {
+		foreach ( $default_types as $ext ) {
+			if ( ! empty( $$ext ) ) {
+				$type = wp_check_filetype( $$ext );
+				if ( $type['ext'] === $ext )
+					$primary = true;
+			}
+		}
+	}
+
+	if ( ! $primary ) {
+		$audios = get_post_audio( $post_id );
+		if ( empty( $audios ) )
+			return;
+
+		$audio = reset( $audios );
+		$src = wp_get_attachment_url( $audio->ID );
+		if ( empty( $src ) )
+			return;
+
+		array_unshift( $default_types, 'src' );
+	}
+
+	$library = apply_filters( 'wp_audio_shortcode_library', 'mediaelement' );
+	if ( 'mediaelement' === $library ) {
+		wp_enqueue_style( 'wp-mediaelement' );
+		wp_enqueue_script( 'wp-mediaelement' );
+	}
+
+	$atts = array(
+		sprintf( 'class="%s"', apply_filters( 'wp_audio_shortcode_class', 'wp-audio-shortcode' ) ),
+		sprintf( 'id="audio-%d-%d"', $post_id, $instances ),
+	);
+
+	$html = sprintf( '<audio %s controls="controls" preload="none">', join( ' ', $atts ) );
+
+	$fileurl = '';
+	$source = '<source type="%s" src="%s" />';
+	foreach ( $default_types as $fallback ) {
+		if ( ! empty( $$fallback ) ) {
+			if ( empty( $fileurl ) )
+				$fileurl = $$fallback;
+			$type = wp_check_filetype( $$fallback );
+			$html .= sprintf( $source, $type['type'], $$fallback );
+		}
+	}
+
+	if ( 'mediaelement' === $library )
+		$html .= wp_mediaelement_fallback( $fileurl );
+	$html .= '</audio>';
+
+	return apply_filters( 'wp_audio_shortcode', $html, $atts, $audio, $post_id );
+}
+add_shortcode( 'audio', apply_filters( 'wp_audio_shortcode_handler', 'wp_audio_shortcode' ) );
+
+/**
+ * Return a filtered list of WP-supported video formats
+ *
+ * @since 3.6.0
+ * @return array
+ */
+function wp_get_video_extensions() {
+	return apply_filters( 'wp_video_extensions', array( 'mp4', 'm4v', 'webm', 'ogv', 'wmv', 'flv' ) );
+}
+
+/**
+ * The Video shortcode.
+ *
+ * This implements the functionality of the Video Shortcode for displaying
+ * WordPress mp4s in a post.
+ *
+ * @since 3.6.0
+ *
+ * @param array $attr Attributes of the shortcode.
+ * @return string HTML content to display video.
+ */
+function wp_video_shortcode( $attr ) {
+	global $content_width;
+	$post_id = get_post() ? get_the_ID() : 0;
+
+	static $instances = 0;
+	$instances++;
+
+	$video = null;
+
+	$default_types = wp_get_video_extensions();
+	$defaults_atts = array(
+		'src' => '',
+		'poster' => '',
+		'height' => 360,
+		'width' => empty( $content_width ) ? 640 : $content_width,
+	);
+	foreach ( $default_types as $type  )
+		$defaults_atts[$type] = '';
+
+	$atts = shortcode_atts( $defaults_atts, $attr );
+	extract( $atts );
+
+	$primary = false;
+	if ( ! empty( $src ) ) {
+		$type = wp_check_filetype( $src );
+		if ( ! in_array( $type['ext'], $default_types ) ) {
+			printf( '<a class="wp-post-format-link-video" href="%1$s">%1$s</a>', $src );
+			return;
+		}
+		$primary = true;
+		array_unshift( $default_types, 'src' );
+	} else {
+		foreach ( $default_types as $ext ) {
+			if ( ! empty( $$ext ) ) {
+				$type = wp_check_filetype( $$ext );
+				if ( $type['ext'] === $ext )
+					$primary = true;
+			}
+		}
+	}
+
+	if ( ! $primary ) {
+		$videos = get_post_video( $post_id );
+		if ( empty( $videos ) )
+			return;
+
+		$video = reset( $videos );
+		$src = wp_get_attachment_url( $video->ID );
+		if ( empty( $src ) )
+			return;
+
+		array_unshift( $default_types, 'src' );
+	}
+
+	$library = apply_filters( 'wp_video_shortcode_library', 'mediaelement' );
+	if ( 'mediaelement' === $library ) {
+		wp_enqueue_style( 'wp-mediaelement' );
+		wp_enqueue_script( 'wp-mediaelement' );
+	}
+
+	$atts = array(
+		sprintf( 'class="%s"', apply_filters( 'wp_video_shortcode_class', 'wp-video-shortcode' ) ),
+		sprintf( 'id="video-%d-%d"', $post_id, $instances ),
+		sprintf( 'width="%d"', $width ),
+		sprintf( 'height="%d"', $height ),
+	);
+
+	if ( ! empty( $poster ) )
+		$atts[] = sprintf( 'poster="%s"', esc_url( $poster ) );
+
+	$html = sprintf( '<video %s controls="controls" preload="none">', join( ' ', $atts ) );
+
+	$fileurl = '';
+	$source = '<source type="%s" src="%s" />';
+	foreach ( $default_types as $fallback ) {
+		if ( ! empty( $$fallback ) ) {
+			if ( empty( $fileurl ) )
+				$fileurl = $$fallback;
+			$type = wp_check_filetype( $$fallback );
+			// m4v sometimes shows up as video/mpeg which collides with mp4
+			if ( 'm4v' === $type['ext'] )
+				$type['type'] = 'video/m4v';
+			$html .= sprintf( $source, $type['type'], $$fallback );
+		}
+	}
+	if ( 'mediaelement' === $library )
+		$html .= wp_mediaelement_fallback( $fileurl, $width, $height );
+	$html .= '</video>';
+
+	return apply_filters( 'wp_video_shortcode', $html, $atts, $video, $post_id );
+}
+add_shortcode( 'video', apply_filters( 'wp_video_shortcode_handler', 'wp_video_shortcode' ) );
+
+/**
  * Display previous image link that has the same post parent.
  *
  * @since 2.5.0
@@ -1544,3 +1771,410 @@ function wp_enqueue_media( $args = array() ) {
 
 	do_action( 'wp_enqueue_media' );
 }
+
+/**
+ * Retrieve images attached to the passed post
+ *
+ * @since 3.6.0
+ *
+ * @param int $post_id  Post ID
+ * @return array Found image attachments
+ */
+function get_attached_images( $post_id = 0 ) {
+	$post = empty( $post_id ) ? get_post() : get_post( $post_id );
+	if ( empty( $post ) )
+		return array();
+
+	$children = get_children( array(
+		'post_parent' => $post->ID,
+		'post_type' => 'attachment',
+		'post_mime_type' => 'image',
+		'posts_per_page' => -1,
+		'orderby' => 'menu_order',
+		'order' => 'ASC'
+	) );
+
+	if ( ! empty( $children ) )
+		return $children;
+
+	return array();
+}
+
+/**
+ * Retrieve images attached to the passed post
+ *
+ * @since 3.6.0
+ *
+ * @param int $post_id  Post ID
+ * @return array Found image attachments
+ */
+function get_attached_image_srcs( $post_id = 0 ) {
+	$children = get_attached_images( $post_id );
+	if ( empty( $children ) )
+		return array();
+
+	$srcs = array();
+	foreach ( $children as $attachment )
+		$srcs[] = wp_get_attachment_url( $attachment->ID );
+
+	return $srcs;
+}
+
+/**
+ * Check the content blob for images
+ *
+ * @since 3.6.0
+ *
+ * @param string $content A string which might contain image data.
+ * @param boolean $remove Whether to remove the found data from the passed content.
+ * @param int $limit Optional. The number of galleries to return
+ * @return string The found data
+ */
+function get_content_images( &$content, $remove = false, $limit = 0 ) {
+	$src = '';
+	$srcs = array();
+	$matches = array();
+
+	if ( preg_match_all( '#' . get_tag_regex( 'img' ) . '#i', $content, $matches, PREG_SET_ORDER ) && ! empty( $matches ) ) {
+		foreach ( $matches as $tag ) {
+			$count = 1;
+			if ( $remove )
+				$content = str_replace( $tag[0], '', $content, $count );
+
+			preg_match( '#src=[\'"](.+?)[\'"]#is', $tag[1], $src );
+			if ( ! empty( $src[1] ) ) {
+				$srcs[] = $src[1];
+				if ( $limit > 0 && count( $srcs ) >= $limit )
+					break;
+			}
+		}
+	}
+
+	return array_values( array_unique( $srcs ) );
+}
+
+/**
+ * Check the content blob for images and return the first
+ *
+ * @since 3.6.0
+ *
+ * @param string $content A string which might contain image data.
+ * @param boolean $remove Whether to remove the found data from the passed content.
+ * @return string The found data
+ */
+function get_content_image( &$content, $remove = false ) {
+	$srcs = get_content_images( $content, $remove, 1 );
+	return reset( $srcs );
+}
+
+/**
+ * Check the content blob for galleries and return their image srcs
+ *
+ * @since 3.6.0
+ *
+ * @param string $content A string which might contain image data.
+ * @param boolean $remove Optional. Whether to remove the found data from the passed content.
+ * @param int $limit Optional. The number of galleries to return
+ * @return array A list of galleries, which in turn are a list of their srcs in order
+ */
+function get_content_galleries( &$content, $remove = false, $limit = 0 ) {
+	$src = '';
+	$galleries = array();
+	$matches = array();
+
+	if ( preg_match_all( '/' . get_shortcode_regex() . '/s', $content, $matches, PREG_SET_ORDER ) && ! empty( $matches ) ) {
+		foreach ( $matches as $shortcode ) {
+			if ( 'gallery' === $shortcode[2] ) {
+				$srcs = array();
+				$data = array();
+				$count = 1;
+				if ( $remove )
+					$content = str_replace( $shortcode[0], '', $content, $count );
+
+				$gallery = do_shortcode_tag( $shortcode );
+				preg_match_all( '#src=[\'"](.+?)[\'"]#is', $gallery, $src, PREG_SET_ORDER );
+				if ( ! empty( $src ) ) {
+					foreach ( $src as $s )
+						$srcs[] = $s[1];
+				}
+				// the function will eventually return more data about the gallery
+				$data['src'] = array_values( array_unique( $srcs ) );
+				$galleries[] = $data;
+				if ( $limit > 0 && count( $galleries ) >= $limit )
+					break;
+			}
+		}
+	}
+
+	return $galleries;
+}
+
+/**
+ * Retrieve galleries from the passed post's content
+ *
+ * @since 3.6.0
+ *
+ * @param int $post_id A string which might contain image data.
+ * @return array A list of galleries, which in turn are a list of their srcs in order
+ */
+function get_post_galleries_images( $post_id = 0 ) {
+	$post = empty( $post_id ) ? clone get_post() : get_post( $post_id );
+	if ( empty( $post ) )
+		return array();
+
+	$data = get_content_galleries( $post->post_content );
+	return wp_list_pluck( $data, 'src' );
+}
+
+/**
+ * Check the content blob for galleries and return the image srcs for the first found gallery
+ *
+ * @since 3.6.0
+ *
+ * @param int $post_id A string which might contain image data.
+ * @return array A list of a gallery's image srcs in order
+ */
+function get_post_gallery_images( $post_id = 0 ) {
+	$post = empty( $post_id ) ? clone get_post() : get_post( $post_id );
+	if ( empty( $post ) )
+		return array();
+
+	$data = get_content_galleries( $post->post_content, false, 1 );
+	return reset( wp_list_pluck( $data, 'src' ) );
+}
+
+
+/**
+ * Retrieve video attached to the passed post
+ *
+ * @since 3.6.0
+ *
+ * @return array Found video attachments
+ */
+function get_post_video( $post_id = 0 ) {
+	$post = empty( $post_id ) ? get_post() : get_post( $post_id );
+	if ( empty( $post ) )
+		return;
+
+	$children = get_children( array(
+		'post_parent' => $post->ID,
+		'post_type' => 'attachment',
+		'post_mime_type' => 'video',
+		'posts_per_page' => -1
+	) );
+
+	if ( ! empty( $children ) )
+		return $children;
+}
+/**
+ * Retrieve audio attached to the passed post
+ *
+ * @since 3.6.0
+ *
+ * @param int $post_id  Post ID
+ * @return array Found audio attachments
+ */
+function get_post_audio( $post_id = 0 ) {
+	$post = empty( $post_id ) ? get_post() : get_post( $post_id );
+	if ( empty( $post ) )
+		return;
+
+	$children = get_children( array(
+		'post_parent' => $post->ID,
+		'post_type' => 'attachment',
+		'post_mime_type' => 'audio',
+		'posts_per_page' => -1
+	) );
+
+	if ( ! empty( $children ) )
+		return $children;
+
+	return array();
+}
+
+/**
+ * Extract the srcs from the post's [audio] <source>s
+ *
+ * @since 3.6.0
+ *
+ * @param string $content A string which might contain audio data.
+ * @param boolean $remove Whether to remove the found URL from the passed content.
+ * @return array A list of lists. Each item has a list of sources corresponding
+ *		to a [audio]'s primary src and specified fallbacks
+ */
+function get_content_audio( &$content, $remove = false ) {
+	$src = '';
+	$audios = array();
+	$matches = array();
+
+	if ( preg_match_all( '/' . get_shortcode_regex() . '/s', $content, $matches, PREG_SET_ORDER ) && ! empty( $matches ) ) {
+		foreach ( $matches as $shortcode ) {
+			if ( 'audio' === $shortcode[2] ) {
+				$srcs = array();
+				$count = 1;
+				if ( $remove )
+					$content = str_replace( $shortcode[0], '', $content, $count );
+
+				$audio = do_shortcode_tag( $shortcode );
+				preg_match_all( '#src=[\'"](.+?)[\'"]#is', $audio, $src, PREG_SET_ORDER );
+				if ( ! empty( $src ) ) {
+					foreach ( $src as $s )
+						$srcs[] = $s[1];
+
+					$audios[] = array_values( array_unique( $srcs ) );
+				}
+			}
+		}
+	}
+	return $audios;
+}
+
+/**
+ * Check the content blob for an <audio>, <object>, <embed>, or <iframe>, in that order
+ * If no HTML tag is found, check the first line of the post for a URL
+ *
+ * @since 3.6.0
+ *
+ * @param string $content A string which might contain audio data.
+ * @param boolean $remove Whether to remove the found URL from the passed content.
+ * @return array A list of found HTML audio embeds and possibly a URL by itself
+ */
+function get_embedded_audio( &$content, $remove = false ) {
+	$html = array();
+	$matches = '';
+
+	foreach ( array( 'audio', 'object', 'embed', 'iframe' ) as $tag ) {
+		if ( preg_match( '#' . get_tag_regex( $tag ) . '#', $content, $matches ) ) {
+			$html[] = $matches[1];
+			if ( $remove )
+				$content = str_replace( $matches[0], '', $content );
+
+			return $html;
+		}
+	}
+
+	$lines = explode( "\n", trim( $content ) );
+	$line = trim( array_shift( $lines  ) );
+
+	if ( 0 === stripos( $line, 'http' ) ) {
+		if ( $remove )
+			$content = join( "\n", $lines );
+
+		$html[] = $line;
+	}
+	return $html;
+}
+
+/**
+ * Extract the srcs from the post's [video] <source>s
+ *
+ * @since 3.6.0
+ *
+ * @param string $content A string which might contain video data.
+ * @param boolean $remove Whether to remove the found URL from the passed content.
+ * @return array A list of lists. Each item has a list of sources corresponding
+ *		to a [video]'s primary src and specified fallbacks
+ */
+function get_content_video( &$content, $remove = false ) {
+	$src = '';
+	$videos = array();
+	$matches = array();
+
+	if ( preg_match_all( '/' . get_shortcode_regex() . '/s', $content, $matches, PREG_SET_ORDER ) && ! empty( $matches ) ) {
+		foreach ( $matches as $shortcode ) {
+			if ( 'video' === $shortcode[2] ) {
+				$srcs = array();
+				$count = 1;
+				if ( $remove )
+					$content = str_replace( $shortcode[0], '', $content, $count );
+
+				$video = do_shortcode_tag( $shortcode );
+				preg_match_all( '#src=[\'"](.+?)[\'"]#is', $video, $src, PREG_SET_ORDER );
+				if ( ! empty( $src ) ) {
+					foreach ( $src as $s )
+						$srcs[] = $s[1];
+
+					$videos[] = array_values( array_unique( $srcs ) );
+				}
+			}
+		}
+	}
+	return $videos;
+}
+
+/**
+ * Check the content blob for a <video>, <object>, <embed>, or <iframe>, in that order
+ * If no HTML tag is found, check the first line of the post for a URL
+ *
+ * @since 3.6.0
+ *
+ * @param string $content A string which might contain video data.
+ * @param boolean $remove Whether to remove the found URL from the passed content.
+ * @return array A list of found HTML video embeds and possibly a URL by itself
+ */
+function get_embedded_video( &$content, $remove = false ) {
+	$html = array();
+	$matches = '';
+	foreach ( array( 'video', 'object', 'embed', 'iframe' ) as $tag ) {
+		if ( preg_match( '#' . get_tag_regex( $tag ) . '#', $content, $matches ) ) {
+			$html[] = $matches[1];
+			if ( $remove )
+				$content = str_replace( $matches[0], '', $content );
+		}
+	}
+
+	$lines = explode( "\n", trim( $content ) );
+	$line = trim( array_shift( $lines  ) );
+
+	if ( 0 === stripos( $line, 'http' ) ) {
+		if ( $remove )
+			$content = join( "\n", $lines );
+
+		$html[] = $line;
+	}
+	return $html;
+}
+
+/**
+ * Audio embed handler callback.
+ *
+ * @since 3.6.0
+ *
+ * @param array $matches The regex matches from the provided regex when calling {@link wp_embed_register_handler()}.
+ * @param array $attr Embed attributes.
+ * @param string $url The original URL that was matched by the regex.
+ * @param array $rawattr The original unmodified attributes.
+ * @return string The embed HTML.
+ */
+function wp_audio_embed( $matches, $attr, $url, $rawattr ) {
+	$audio = $url;
+	if ( shortcode_exists( 'audio' ) )
+		$audio = do_shortcode( '[audio src="' . $url . '" /]' );
+	return apply_filters( 'wp_audio_embed', $audio, $attr, $url, $rawattr );
+}
+wp_embed_register_handler( 'wp_audio_embed', '#https?://.+?\.(' . join( '|', wp_get_audio_extensions() ) . ')#i', apply_filters( 'wp_audio_embed_handler', 'wp_audio_embed' ), 9999 );
+
+/**
+ * Video embed handler callback.
+ *
+ * @since 3.6.0
+ * 
+ * @param array $matches The regex matches from the provided regex when calling {@link wp_embed_register_handler()}.
+ * @param array $attr Embed attributes.
+ * @param string $url The original URL that was matched by the regex.
+ * @param array $rawattr The original unmodified attributes.
+ * @return string The embed HTML.
+ */
+function wp_video_embed( $matches, $attr, $url, $rawattr ) {
+	$dimensions = '';
+	$video = $url;
+	if ( shortcode_exists( 'video' ) ) {
+		if ( ! empty( $rawattr['width'] ) && ! empty( $rawattr['height'] ) ) {
+			$dimensions .= sprintf( 'width="%d" ', (int) $rawattr['width'] );
+			$dimensions .= sprintf( 'height="%d" ', (int) $rawattr['height'] );
+		}
+		$video = do_shortcode( '[video ' . $dimensions . 'src="' . $url . '" /]' );
+	}
+	return apply_filters( 'wp_video_embed', $video, $attr, $url, $rawattr );
+}
+wp_embed_register_handler( 'wp_video_embed', '#https?://.+?\.(' . join( '|', wp_get_video_extensions() ) . ')#i', apply_filters( 'wp_video_embed_handler', 'wp_video_embed' ), 9999 );
diff --git wp-includes/post-formats.php wp-includes/post-formats.php
index 3896d11..9dd11ec 100644
--- wp-includes/post-formats.php
+++ wp-includes/post-formats.php
@@ -308,23 +308,37 @@ function post_formats_compat( $content, $id = 0 ) {
 	$show_content = true;
 	$format_output = '';
 	$meta = get_post_format_meta( $post->ID );
+	// passed by ref in preg_match()
+	$matches = array();
 
 	switch ( $format ) {
 		case 'link':
 			$compat['tag'] = '';
+			$compat['position'] = 'before';
 
 			if ( ! empty( $meta['url'] ) ) {
 				$esc_url = preg_quote( $meta['url'], '#' );
 				// Make sure the same URL isn't in the post (modified/extended versions allowed)
-				if ( ! preg_match( '#' . $esc_url . '[^/&\?]#', $content ) ) {
-					$format_output .= sprintf(
-						'<a %shref="%s">%s</a>',
-						empty( $compat['link_class'] ) ? '' : sprintf( 'class="%s" ', esc_attr( $compat['link_class'] ) ),
-						esc_url( $meta['url'] ),
-						empty( $post->post_title ) ? esc_url( $meta['url'] ) : apply_filters( 'the_title', $post->post_title, $post->ID )
-					);
+				if ( ! preg_match( '#' . $esc_url . '[^/&\?]?#', $content ) ) {
+					$url = $meta['url'];
+				} else {
+					$url = get_content_link( $content, true );
 				}
+			} else {
+				$content_before = $content;
+				$url = get_content_link( $content, true );
+				if ( $content_before == $content )
+					$url = '';
 			}
+
+			if ( ! empty( $url ) ) {
+				$format_output .= sprintf(
+					'<a %shref="%s">%s</a>',
+					empty( $compat['link_class'] ) ? '' : sprintf( 'class="%s" ', esc_attr( $compat['link_class'] ) ),
+					esc_url( $url ),
+					empty( $post->post_title ) ? esc_url( $meta['url'] ) : apply_filters( 'the_title', $post->post_title )
+				);
+ 			}
 			break;
 
 		case 'quote':
@@ -365,32 +379,19 @@ function post_formats_compat( $content, $id = 0 ) {
 			break;
 
 		case 'gallery':
-			preg_match_all( '/' . get_shortcode_regex() . '/s', $content, $matches );
-			if ( ! empty( $matches ) && isset( $matches[2] ) ) {
-				foreach ( (array) $matches[2] as $match ) {
-					if ( 'gallery' === $match )
-						break 2; // foreach + case
-				}
-			}
-
-			if ( ! empty( $meta['gallery'] ) ) {
+			if ( ! has_shortcode( $post->post_content, $format ) && ! empty( $meta['gallery'] ) )
 				$format_output .= $meta['gallery'];
-			}
 			break;
 
 		case 'video':
 		case 'audio':
-			$shortcode_regex = '/' . get_shortcode_regex() . '/s';
-			$matches = preg_match( $shortcode_regex, $content );
-			if ( ! $matches || $format !== $matches[2] ) {
-				if ( ! empty( $meta['media'] ) ) {
-					// the metadata is a shortcode or an embed code
-					if ( preg_match( $shortcode_regex, $meta['media'] ) || preg_match( '#<[^>]+>#', $meta['media'] ) ) {
-						$format_output .= $meta['media'];
-					} elseif ( ! stristr( $content, $meta['media'] ) ) {
-						// attempt to embed the URL
-						$format_output .= sprintf( '[embed]%s[/embed]', $meta['media'] );
-					}
+			if ( ! has_shortcode( $post->post_content, $format ) && ! empty( $meta['media'] ) ) {
+				// the metadata is a shortcode or an embed code
+				if ( preg_match( '/' . get_shortcode_regex() . '/s', $meta['media'] ) || preg_match( '#<[^>]+>#', $meta['media'] ) ) {
+					$format_output .= $meta['media'];
+				} elseif ( ! stristr( $content, $meta['media'] ) ) {
+					// attempt to embed the URL
+					$format_output .= sprintf( '[embed]%s[/embed]', $meta['media'] );
 				}
 			}
 			break;
@@ -420,3 +421,205 @@ function post_formats_compat( $content, $id = 0 ) {
 
 	return $output;
 }
+
+/**
+ * Extract a URL from passed content, if possible
+ * Checks for a URL on the first line of the content or the first encountered href attribute.
+ *
+ * @since 3.6.0
+ *
+ * @param string $content A string which might contain a URL.
+ * @param boolean $remove Whether the remove the found URL from the passed content.
+ * @return string The found URL.
+ */
+function get_content_link( &$content, $remove = false ) {
+	if ( empty( $content ) )
+		return '';
+
+	$matches = array();
+
+	// the content is a URL
+	$trimmed = trim( $content );
+	if ( 0 === stripos( $trimmed, 'http' ) && ! preg_match( '#\s#', $trimmed ) ) {
+		if ( $remove )
+			$content = '';
+
+		return $trimmed;
+	// the content is HTML so we grab the first href
+	} elseif ( preg_match( '/<a\s[^>]*?href=[\'"](.+?)[\'"]/is', $content, $matches ) ) {
+		return esc_url_raw( $matches[1] );
+	}
+
+	$lines = explode( "\n", $trimmed );
+	$line = trim( array_shift( $lines ) );
+
+	// the content is a URL followed by content
+	if ( 0 === stripos( $line, 'http' ) ) {
+		if ( $remove )
+			$content = trim( join( "\n", $lines ) );
+
+		return esc_url_raw( $line );
+	}
+
+	return '';
+}
+
+/**
+ * Attempt to retrieve a URL from a post's content
+ *
+ * @since 3.6.0
+ *
+ * @param int $id Optional. Post ID.
+ * @return string A URL, if found.
+ */
+function get_the_link( $id = 0 ) {
+	$post = empty( $id ) ? get_post() : get_post( $id );
+	if ( empty( $post ) )
+		return '';
+
+	if ( has_post_format( 'link', $post ) ) {
+		$meta = get_post_format_meta( $post->ID );
+		if ( ! empty( $meta['url'] ) )
+			return esc_url_raw( $meta['url'] );
+	}
+
+	if ( ! empty( $post->post_content ) )
+		return get_content_link( $post->post_content );
+}
+
+/**
+ * Attempt to output a URL from a post's content
+ *
+ * @since 3.6.0
+ *.
+ */
+function the_link() {
+	echo get_the_link();
+}
+
+/**
+ * Deliberately interpret passed content as a chat transcript that is optionally
+ * followed by commentary
+ *
+ * If the content does not contain username syntax, assume that it does not contain
+ * chat logs and return
+ *
+ * @since 3.6.0
+ *
+ * Example:
+ *
+ * One stanza of chat:
+ * Scott: Hey, let's chat!
+ * Helen: No.
+ *
+ * $stanzas = array(
+ *     array(
+ *         array(
+ *             'time' => '',
+ *             'author' => 'Scott',
+ *             'messsage' => "Hey, let's chat!"
+ *         ),
+ *         array(
+ *             'time' => '',
+ *             'author' => 'Helen',
+ *             'message' => 'No.'
+ *         )
+ *     )
+ * )
+ * @param string $content A string which might contain chat data.
+ * @param boolean $remove Whether to remove the found data from the passed content.
+ * @return array A chat log as structured data
+ */
+function get_content_chat( &$content, $remove = false ) {
+	$trimmed = trim( $content );
+	$newline_regex = '#^(\[.+?\])?\s?(?:(?:([^:]+):)|(?:<([^>]+)>))#';
+
+	if ( empty( $trimmed ) || ! preg_match( $newline_regex, $trimmed ) )
+		return array();
+
+	$last_index = 0;
+	$stanzas = array();
+	$lines = explode( "\n", make_clickable( $trimmed ) );
+
+	$author = $time = '';
+	$data = array();
+	$stanza = array();
+
+	foreach ( $lines as $index => $line ) {
+		$line = trim( $line );
+
+		if ( empty( $line ) ) {
+			if ( ! empty( $author ) ) {
+				$stanza[] = array(
+					'time' => $time,
+					'author' => $author,
+					'message' => join( ' ', $data )
+				);
+			}
+
+			$stanzas[] = $stanza;
+			$last_index = $index;
+			$stanza = array();
+			$author = $time = '';
+			$data = array();
+			$commentary = ! preg_match( '#[:]#', $lines[$index + 1] ) && ! preg_match( '#<[^>]+>#', $lines[$index + 1] );
+			if ( ! empty( $lines[$index + 1] ) && $commentary )
+				break;
+		}
+
+		$matches = array();
+		$matched = preg_match( $newline_regex, $line, $matches );
+		if ( $matched && ( ! empty( $matches[1] ) || ! empty( $matches[3] ) || ! preg_match( '#\s#', $matches[2] ) ) ) {
+			if ( ! empty( $author ) ) {
+				$stanza[] = array(
+					'time' => $time,
+					'author' => $author,
+					'message' => join( ' ', $data )
+				);
+				$data = array();
+			}
+
+			$time = $matches[1];
+			$author = empty( $matches[3] ) ? $matches[2] : $matches[3];
+			$data[] = trim( str_replace( $matches[0], '', $line ) );
+		} elseif ( preg_match( '#\S#', $line ) ) {
+			$data[] = $line;
+		}
+	}
+
+	if ( ! empty( $author ) ) {
+		$stanza[] = array(
+			'time' => $time,
+			'author' => $author,
+			'message' => trim( join( ' ', $data ) )
+		);
+	}
+
+	if ( ! empty( $stanza ) )
+		$stanzas[] = $stanza;
+
+	if ( $remove )
+		$content = trim( join( "\n", array_slice( $lines, $last_index ) ) );
+
+	return $stanzas;
+}
+
+/**
+ * Retrieve structured chat data from the current or passed post
+ *
+ * @since 3.6.0
+ *
+ * @param int $id Optional. Post ID
+ * @return array
+ */
+function get_the_chat( $id = 0 ) {
+	$post = empty( $id ) ? get_post() : get_post( $id );
+	if ( empty( $post ) )
+		return array();
+
+	$data = get_content_chat( $post->post_content );
+	if ( empty( $data ) )
+		return array();
+
+	return $data;
+}
diff --git wp-includes/post-template.php wp-includes/post-template.php
index 7117c33..aa37a9c 100644
--- wp-includes/post-template.php
+++ wp-includes/post-template.php
@@ -1315,7 +1315,7 @@ function wp_post_revision_title( $revision, $link = true ) {
 	$date = date_i18n( $datef, strtotime( $revision->post_modified ) );
 	if ( $link && current_user_can( 'edit_post', $revision->ID ) && $link = get_edit_post_link( $revision->ID ) )
 		$date = "<a href='$link'>$date</a>";
-	
+
 	$revision_date_author = sprintf(
 		'%s %s, %s %s (%s)',
 		$gravatar,
diff --git wp-includes/script-loader.php wp-includes/script-loader.php
index 84b76d3..bf3e4fb 100644
--- wp-includes/script-loader.php
+++ wp-includes/script-loader.php
@@ -277,6 +277,9 @@ function wp_default_scripts( &$scripts ) {
 
 	$scripts->add( 'imgareaselect', "/wp-includes/js/imgareaselect/jquery.imgareaselect$suffix.js", array('jquery'), '0.9.8', 1 );
 
+	$scripts->add( 'mediaelement', "/wp-includes/js/mediaelement/mediaelement-and-player$suffix.js", array('jquery'), '2.10.1', 1 );
+	$scripts->add( 'wp-mediaelement', "/wp-includes/js/mediaelement/wp-mediaelement.js", array('mediaelement'), false, 1 );
+
 	$scripts->add( 'password-strength-meter', "/wp-admin/js/password-strength-meter$suffix.js", array('jquery'), false, 1 );
 	did_action( 'init' ) && $scripts->localize( 'password-strength-meter', 'pwsL10n', array(
 		'empty' => __('Strength indicator'),
@@ -543,6 +546,9 @@ function wp_default_styles( &$styles ) {
 	$styles->add( 'media-views', "/wp-includes/css/media-views$suffix.css", array( 'buttons' ) );
 	$styles->add( 'buttons', "/wp-includes/css/buttons$suffix.css" );
 
+	$styles->add( 'mediaelement', "/wp-includes/js/mediaelement/mediaelementplayer$suffix.css" );
+	$styles->add( 'wp-mediaelement', "/wp-includes/js/mediaelement/wp-mediaelement.css", array( 'mediaelement' ) );
+
 	foreach ( $rtl_styles as $rtl_style ) {
 		$styles->add_data( $rtl_style, 'rtl', true );
 		if ( $suffix && ! in_array( $rtl_style, $no_suffix ) )
diff --git wp-includes/shortcodes.php wp-includes/shortcodes.php
index bdc3f9b..b7a0b1b 100644
--- wp-includes/shortcodes.php
+++ wp-includes/shortcodes.php
@@ -128,6 +128,44 @@ function remove_all_shortcodes() {
 }
 
 /**
+ * Whether a registered shortcode exists named $tag
+ *
+ * @since 3.6.0
+ *
+ * @global array $shortcode_tags
+ * @param string $tag
+ * @return boolean
+ */
+function shortcode_exists( $tag ) {
+	global $shortcode_tags;
+	return array_key_exists( $tag, $shortcode_tags );
+}
+
+/**
+ * Whether the passed content contains the specified shortcode
+ *
+ * @since 3.6.0
+ *
+ * @global array $shortcode_tags
+ * @param string $tag
+ * @return boolean
+ */
+function has_shortcode( $content, $tag ) {
+	if ( shortcode_exists( $tag ) ) {
+		$matches = array();
+		preg_match_all( '/' . get_shortcode_regex() . '/s', $content, $matches, PREG_SET_ORDER );
+		if ( empty( $matches ) )
+			return false;
+
+		foreach ( $matches as $shortcode ) {
+			if ( $tag === $shortcode[2] )
+				return true;
+		}
+	}
+	return false;
+}
+
+/**
  * Search content for shortcodes and filter shortcodes through their hooks.
  *
  * If there are no shortcode tags defined, then the content will be returned
