Ticket #23282: 23282.9.diff

File 23282.9.diff, 20.2 KB (added by wonderboymusic, 3 months ago)
Line 
1diff --git wp-admin/includes/ajax-actions.php wp-admin/includes/ajax-actions.php
2index 0fabe2a..cf6d80b 100644
3--- wp-admin/includes/ajax-actions.php
4+++ wp-admin/includes/ajax-actions.php
5@@ -2030,6 +2030,8 @@ function wp_ajax_send_attachment_to_editor() {
6                $caption = isset( $attachment['post_excerpt'] ) ? $attachment['post_excerpt'] : '';
7                $title = ''; // We no longer insert title tags into <img> tags, as they are redundant.
8                $html = get_image_send_to_editor( $id, $caption, $title, $align, $url, (bool) $rel, $size, $alt );
9+       } elseif ( 'video' === substr( $post->post_mime_type, 0, 5 ) || 'audio' === substr( $post->post_mime_type, 0, 5 )  ) {
10+               $html = stripslashes_deep( $_POST['html'] );
11        }
12 
13        $html = apply_filters( 'media_send_to_editor', $html, $id, $attachment );
14diff --git wp-includes/functions.php wp-includes/functions.php
15index cbd7925..5ac68c7 100644
16--- wp-includes/functions.php
17+++ wp-includes/functions.php
18@@ -3883,4 +3883,24 @@ function wp_is_stream( $path ) {
19  */
20 function wp_checkdate( $month, $day, $year, $source_date ) {
21        return apply_filters( 'wp_checkdate', checkdate( $month, $day, $year ), $source_date );
22+}
23+
24+/**
25+ * Return RegEx body to liberally match an opening HTML tag that:
26+ * 1. Is self-closing or
27+ * 2. Has no body but has a closing tag of the same name or
28+ * 3. Contains a body and a closing tag of the same name
29+ *
30+ * Note: this RegEx does not balance inner tags and does not attempt to produce valid HTML
31+ *
32+ * @since 3.6.0
33+ *
34+ * @param string $tag An HTML tag name. Example: 'video'
35+ * @return string
36+ */
37+function get_tag_regex( $tag ) {
38+       if ( empty( $tag ) )
39+               return;
40+
41+       return sprintf( '(<%1$s[^>]*(?:/?>$|>[\s\S]*?</%1$s>))', tag_escape( $tag ) );
42 }
43\ No newline at end of file
44diff --git wp-includes/js/media-editor.js wp-includes/js/media-editor.js
45index 15eff8d..1b5f844 100644
46--- wp-includes/js/media-editor.js
47+++ wp-includes/js/media-editor.js
48@@ -66,7 +66,8 @@
49                                        src:       size.url,
50                                        captionId: 'attachment_' + attachment.id
51                                });
52-
53+                       } else if ( 'video' === attachment.type || 'audio' === attachment.type ) {
54+                               _.extend( props, _.pick( attachment, 'title', 'type', 'icon', 'mime' ) );
55                        // Format properties for non-images.
56                        } else {
57                                props.title = props.title || attachment.filename;
58@@ -95,6 +96,70 @@
59                        return wp.html.string( options );
60                },
61 
62+               audio: function( props, attachment ) {
63+                       var shortcode, html;
64+
65+                       props = wp.media.string.props( props, attachment );
66+
67+                       shortcode = {};
68+
69+                       if ( props.mime ) {
70+                               switch ( props.mime ) {
71+                               case 'audio/mpeg':
72+                                       shortcode.mp3 = props.linkUrl;
73+                                       break;
74+                               case 'audio/ogg':
75+                                       shortcode.ogg = props.linkUrl;
76+                                       break;
77+                               case 'audio/wma':
78+                                       shortcode.wma = props.linkUrl;
79+                                       break;
80+                               }
81+                       }
82+
83+                       html = wp.shortcode.string({
84+                               tag:     'audio',
85+                               attrs:   shortcode
86+                       });
87+
88+                       return html;
89+               },
90+
91+               video: function( props, attachment ) {
92+                       var shortcode, html;
93+
94+                       props = wp.media.string.props( props, attachment );
95+
96+                       shortcode = {};
97+
98+                       if ( props.mime ) {
99+                               switch ( props.mime ) {
100+                               case 'video/mp4':
101+                                       shortcode.mp4 = props.linkUrl;
102+                                       break;
103+                               case 'video/webm':
104+                                       shortcode.webm = props.linkUrl;
105+                                       break;
106+                               case 'video/ogg':
107+                                       shortcode.ogv = props.linkUrl;
108+                                       break;
109+                               case 'video/asf':
110+                                       shortcode.wmv = props.linkUrl;
111+                                       break;
112+                               case 'video/x-flv':
113+                                       shortcode.flv = props.linkUrl;
114+                                       break;
115+                               }
116+                       }
117+
118+                       html = wp.shortcode.string({
119+                               tag:     'video',
120+                               attrs:   shortcode
121+                       });
122+
123+                       return html;
124+               },
125+
126                image: function( props, attachment ) {
127                        var img = {},
128                                options, classes, shortcode, html;
129@@ -575,7 +640,10 @@
130                                                if ( props[ prop ] )
131                                                        options[ option ] = props[ prop ];
132                                        });
133-
134+                               } else if ( 'video' === attachment.type ) {
135+                                       html = wp.media.string.video( props );
136+                               } else if ( 'audio' === attachment.type ) {
137+                                       html = wp.media.string.audio( props );
138                                } else {
139                                        html = wp.media.string.link( props );
140                                        options.post_title = props.title;
141diff --git wp-includes/media.php wp-includes/media.php
142index f1f3737..87df77f 100644
143--- wp-includes/media.php
144+++ wp-includes/media.php
145@@ -803,6 +803,201 @@ function gallery_shortcode($attr) {
146 }
147 
148 /**
149+ * Return a filtered list of WP-supported audio formats
150+ *
151+ * @since 3.6.0
152+ * @return array
153+ */
154+function wp_get_audio_extensions() {
155+       return apply_filters( 'wp_audio_extensions', array( 'mp3', 'ogg', 'wma' ) );
156+}
157+
158+/**
159+ * The Audio shortcode.
160+ *
161+ * This implements the functionality of the Audio Shortcode for displaying
162+ * WordPress mp3s in a post.
163+ *
164+ * @since 3.6.0
165+ *
166+ * @param array $attr Attributes of the shortcode.
167+ * @return string HTML content to display audio.
168+ */
169+function wp_audio_shortcode( $attr ) {
170+       $post_id = get_post() ? get_the_ID() : 0;
171+
172+       static $instances = 0;
173+       $instances++;
174+
175+       $audio = null;
176+
177+       $default_types = wp_get_audio_extensions();
178+       $defaults_atts = array( 'src' => '' );
179+       foreach ( $default_types as $type  )
180+               $defaults_atts[$type] = '';
181+
182+       extract( shortcode_atts( $defaults_atts, $attr ) );
183+
184+       $primary = false;
185+       if ( ! empty( $src ) ) {
186+               $type = wp_check_filetype( $src );
187+               if ( ! in_array( $type['ext'], $default_types ) ) {
188+                       printf( '<a class="wp-post-format-link-audio" href="%1$s">%1$s</a>', $src );
189+                       return;
190+               }
191+               $primary = true;
192+               array_unshift( $default_types, 'src' );
193+       } else {
194+               foreach ( $default_types as $ext ) {
195+                       if ( ! empty( $$ext ) ) {
196+                               $type = wp_check_filetype( $$ext );
197+                               if ( $type['ext'] === $ext )
198+                                       $primary = true;
199+                       }
200+               }
201+       }
202+
203+       if ( ! $primary ) {
204+               $audios = get_post_audio( $post_id );
205+               if ( empty( $audios ) )
206+                       return;
207+
208+               $audio = reset( $audios );
209+               $src = wp_get_attachment_url( $audio->ID );
210+               if ( empty( $src ) )
211+                       return;
212+
213+               array_unshift( $default_types, 'src' );
214+       }
215+
216+       wp_enqueue_style( 'wp-mediaelement' );
217+       wp_enqueue_script( 'wp-mediaelement' );
218+
219+       $atts = array(
220+               sprintf( 'class="%s"', apply_filters( 'wp_audio_shortcode_class', 'wp-audio-shortcode' ) ),
221+               sprintf( 'id="audio-%d-%d"', $post_id, $instances ),
222+       );
223+
224+       $html = sprintf( '<audio %s controls="controls" preload="none">', join( ' ', $atts ) );
225+
226+       $source = '<source type="%s" src="%s" />';
227+       foreach ( $default_types as $fallback ) {
228+               if ( ! empty( $$fallback ) ) {
229+                       $type = wp_check_filetype( $$fallback );
230+                       $html .= sprintf( $source, $type['type'], $$fallback );
231+               }
232+       }
233+
234+       $html .= '</audio>';
235+
236+       return apply_filters( 'wp_audio_shortcode', $html, $src, $audio, $post_id );
237+}
238+add_shortcode( 'audio', 'wp_audio_shortcode' );
239+
240+/**
241+ * Return a filtered list of WP-supported video formats
242+ *
243+ * @since 3.6.0
244+ * @return array
245+ */
246+function wp_get_video_extensions() {
247+       return apply_filters( 'wp_video_extensions', array( 'mp4', 'webm', 'ogv', 'wmv', 'flv' ) );
248+}
249+
250+/**
251+ * The Video shortcode.
252+ *
253+ * This implements the functionality of the Video Shortcode for displaying
254+ * WordPress mp4s in a post.
255+ *
256+ * @since 3.6.0
257+ *
258+ * @param array $attr Attributes of the shortcode.
259+ * @return string HTML content to display video.
260+ */
261+function wp_video_shortcode( $attr ) {
262+       global $content_width;
263+       $post_id = get_post() ? get_the_ID() : 0;
264+
265+       static $instances = 0;
266+       $instances++;
267+
268+       $video = null;
269+
270+       $default_types = wp_get_video_extensions();
271+       $defaults_atts = array(
272+               'src' => '',
273+               'poster' => '',
274+               'height' => 360,
275+               'width' => empty( $content_width ) ? 640 : $content_width,
276+       );
277+       foreach ( $default_types as $type  )
278+               $defaults_atts[$type] = '';
279+
280+       extract( shortcode_atts( $defaults_atts, $attr ) );
281+
282+       $primary = false;
283+       if ( ! empty( $src ) ) {
284+               $type = wp_check_filetype( $src );
285+               if ( ! in_array( $type['ext'], $default_types ) ) {
286+                       printf( '<a class="wp-post-format-link-video" href="%1$s">%1$s</a>', $src );
287+                       return;
288+               }
289+               $primary = true;
290+               array_unshift( $default_types, 'src' );
291+       } else {
292+               foreach ( $default_types as $ext ) {
293+                       if ( ! empty( $$ext ) ) {
294+                               $type = wp_check_filetype( $$ext );
295+                               if ( $type['ext'] === $ext )
296+                                       $primary = true;
297+                       }
298+               }
299+       }
300+
301+       if ( ! $primary ) {
302+               $videos = get_post_video( $post_id );
303+               if ( empty( $videos ) )
304+                       return;
305+
306+               $video = reset( $videos );
307+               $src = wp_get_attachment_url( $video->ID );
308+               if ( empty( $src ) )
309+                       return;
310+
311+               array_unshift( $default_types, 'src' );
312+       }
313+
314+       wp_enqueue_style( 'wp-mediaelement' );
315+       wp_enqueue_script( 'wp-mediaelement' );
316+
317+       $atts = array(
318+               sprintf( 'class="%s"', apply_filters( 'video_shortcode_class', 'wp-video-shortcode' ) ),
319+               sprintf( 'id="video-%d-%d"', $post_id, $instances ),
320+               sprintf( 'width="%d"', $width ),
321+               sprintf( 'height="%d"', $height ),
322+       );
323+
324+       if ( ! empty( $poster ) )
325+               $atts[] = sprintf( 'poster="%s"', esc_url( $poster ) );
326+
327+       $html = sprintf( '<video %s controls="controls" preload="none">', join( ' ', $atts ) );
328+
329+       $source = '<source type="%s" src="%s" />';
330+       foreach ( $default_types as $fallback ) {
331+               if ( ! empty( $$fallback ) ) {
332+                       $type = wp_check_filetype( $$fallback );
333+                       $html .= sprintf( $source, $type['type'], $$fallback );
334+               }
335+       }
336+
337+       $html .= '</video>';
338+
339+       return apply_filters( 'wp_video_shortcode', $html, $src, $video, $post_id );
340+}
341+add_shortcode( 'video', 'wp_video_shortcode' );
342+
343+/**
344  * Display previous image link that has the same post parent.
345  *
346  * @since 2.5.0
347@@ -1542,3 +1737,216 @@ function wp_enqueue_media( $args = array() ) {
348 
349        do_action( 'wp_enqueue_media' );
350 }
351+
352+/**
353+ * Retrieve audio attached to the passed post
354+ *
355+ * @since 3.6.0
356+ *
357+ * @param int $post_id  Post ID
358+ * @return array Found audio attachments
359+ */
360+function get_post_audio( $post_id = 0 ) {
361+       $post = empty( $post_id ) ? get_post() : get_post( $post_id );
362+       if ( empty( $post ) )
363+               return;
364+
365+       $children = get_children( array(
366+               'post_parent' => $post->ID,
367+               'post_type' => 'attachment',
368+               'post_mime_type' => 'audio',
369+               'posts_per_page' => -1
370+       ) );
371+
372+       if ( ! empty( $children ) )
373+               return $children;
374+}
375+
376+/**
377+ * Check the content blob for an <audio>, <object>, <embed>, or <iframe>, in that order
378+ * If no HTML tag is found, check the first line of the post for a URL
379+ *
380+ * @param string $content A string which might contain audio data.
381+ * @param boolean $remove Whether to remove the found URL from the passed content.
382+ * @return string The found data
383+ */
384+function get_content_audio( &$content, $remove = false ) {
385+       $html = $matches = '';
386+       foreach ( array( 'audio', 'object', 'embed', 'iframe' ) as $tag ) {
387+               if ( preg_match( '#' . get_tag_regex( $tag ) . '#i', $content, $matches ) ) {
388+                       $html = $matches[1];
389+                       $count = 1;
390+                       if ( $remove )
391+                               $content = str_replace( $matches[0], '', $content, $count );
392+
393+                       return $html;
394+               }
395+       }
396+
397+       $lines = explode( "\n", trim( $content ) );
398+       $line = trim( array_shift( $lines ) );
399+
400+       if ( 0 === stripos( $line, 'http' ) ) {
401+               if ( $remove )
402+                       $content = join( "\n", $lines );
403+
404+               return $line;
405+       }
406+}
407+
408+/**
409+ * Return the found audio data for the passed post
410+ *
411+ * @since 3.6.0
412+ *
413+ * @param int $id Optional. Post ID
414+ */
415+function get_the_audio( $id = 0 ) {
416+       $post = empty( $id ) ? get_post() : get_post( $id );
417+       if ( empty( $post ) )
418+               return array();
419+
420+       $data = get_content_audio( $post->post_content );
421+       if ( ! empty( $data ) )
422+               return $data;
423+
424+       $audios = get_post_audio( $post->ID );
425+       if ( empty( $audios ) )
426+               return array();
427+
428+       $audio = reset( $audios );
429+       return wp_get_attachment_url( $audio->ID );
430+}
431+
432+/**
433+ * Output the found audio data for the current post
434+ *
435+ * @since 3.6.0
436+ */
437+function the_audio() {
438+       echo apply_filters( 'the_audio', get_the_audio() );
439+}
440+
441+/**
442+ * Retrieve video attached to the passed post
443+ *
444+ * @since 3.6.0
445+ *
446+ * @param int $post_id  Post ID
447+ * @return array Found video attachments
448+ */
449+function get_post_video( $post_id = 0 ) {
450+       $post = empty( $post_id ) ? get_post() : get_post( $post_id );
451+       if ( empty( $post ) )
452+               return;
453+
454+       $children = get_children( array(
455+               'post_parent' => $post->ID,
456+               'post_type' => 'attachment',
457+               'post_mime_type' => 'video',
458+               'posts_per_page' => -1
459+       ) );
460+
461+       if ( ! empty( $children ) )
462+               return $children;
463+}
464+
465+/**
466+ * Check the content blob for a <video>, <object>, <embed>, or <iframe>, in that order
467+ * If no HTML tag is found, check the first line of the post for a URL
468+ *
469+ * @param string $content A string which might contain video data.
470+ * @param boolean $remove Whether to remove the found URL from the passed content.
471+ * @return string The found data
472+ */
473+function get_content_video( &$content, $remove = false ) {
474+       $html = $matches = '';
475+       foreach ( array( 'video', 'object', 'embed', 'iframe' ) as $tag ) {
476+               if ( preg_match( '#' . get_tag_regex( $tag ) . '#i', $content, $matches ) ) {
477+                       $html = $matches[1];
478+                       $count = 1;
479+                       if ( $remove )
480+                               $content = str_replace( $matches[0], '', $content, $count );
481+
482+                       return $html;
483+               }
484+       }
485+
486+       $lines = explode( "\n", trim( $content ) );
487+       $line = trim( array_shift( $lines  ) );
488+
489+       if ( 0 === stripos( $line, 'http' ) ) {
490+               if ( $remove )
491+                       $content = join( "\n", $lines );
492+
493+               return $line;
494+       }
495+}
496+
497+/**
498+ * Return the found video data for the passed post
499+ *
500+ * @since 3.6.0
501+ *
502+ * @param int $id Optional. Post ID
503+ */
504+function get_the_video( $id = 0 ) {
505+       $post = empty( $id ) ? get_post() : get_post( $id );
506+       if ( empty( $post ) )
507+               return array();
508+
509+       $data = get_content_video( $post->post_content );
510+       if ( ! empty( $data ) )
511+               return $data;
512+
513+       $videos = get_post_video( $post->ID );
514+       if ( empty( $videos ) )
515+               return array();
516+
517+       $video = reset( $videos );
518+       return wp_get_attachment_url( $video->ID );
519+}
520+
521+/**
522+ * Output the found video data for the current post
523+ *
524+ * @since 3.6.0
525+ */
526+function the_video() {
527+       echo apply_filters( 'the_video', get_the_video() );
528+}
529+
530+/**
531+ * Audio embed handler callback.
532+ *
533+ * @param array $matches The regex matches from the provided regex when calling {@link wp_embed_register_handler()}.
534+ * @param array $attr Embed attributes.
535+ * @param string $url The original URL that was matched by the regex.
536+ * @param array $rawattr The original unmodified attributes.
537+ * @return string The embed HTML.
538+ */
539+function wp_audio_embed( $matches, $attr, $url, $rawattr ) {
540+       $audio = do_shortcode( '[audio src="' . $url . '" /]' );
541+       return apply_filters( 'wp_audio_embed', $audio, $attr, $url, $rawattr );
542+}
543+wp_embed_register_handler( 'wp_audio_embed', '#https?://.+?\.(' . join( '|', wp_get_audio_extensions() ) . ')#i', 'wp_audio_embed', 9999 );
544+
545+/**
546+ * Video embed handler callback.
547+ *
548+ * @param array $matches The regex matches from the provided regex when calling {@link wp_embed_register_handler()}.
549+ * @param array $attr Embed attributes.
550+ * @param string $url The original URL that was matched by the regex.
551+ * @param array $rawattr The original unmodified attributes.
552+ * @return string The embed HTML.
553+ */
554+function wp_video_embed( $matches, $attr, $url, $rawattr ) {
555+       $dimensions = '';
556+       if ( ! empty( $rawattr['width'] ) && ! empty( $rawattr['height'] ) ) {
557+               $dimensions .= sprintf( 'width="%d" ', (int) $rawattr['width'] );
558+               $dimensions .= sprintf( 'height="%d" ', (int) $rawattr['height'] );
559+       }
560+       $video = do_shortcode( '[video ' . $dimensions . ' src="' . $url . '" /]' );
561+       return apply_filters( 'wp_video_embed', $video, $attr, $url, $rawattr );
562+}
563+wp_embed_register_handler( 'wp_video_embed', '#https?://.+?\.(' . join( '|', wp_get_video_extensions() ) . ')#i', 'wp_video_embed', 9999 );
564\ No newline at end of file
565diff --git wp-includes/post-formats.php wp-includes/post-formats.php
566index 6d32aea..eddcd7f 100644
567--- wp-includes/post-formats.php
568+++ wp-includes/post-formats.php
569@@ -301,8 +301,8 @@ function post_formats_compat( $content, $id = 0 ) {
570                'link_class' => '',
571                'image_class' => '',
572                'gallery' => '[gallery]',
573-               'audio' => '',
574-               'video' => ''
575+               'audio' => '[audio]',
576+               'video' => '[video]'
577        );
578 
579        $args = apply_filters( 'post_format_compat', array() );
580@@ -386,7 +386,8 @@ function post_formats_compat( $content, $id = 0 ) {
581                case 'video':
582                case 'audio':
583                        $shortcode_regex = '/' . get_shortcode_regex() . '/s';
584-                       $matches = preg_match( $shortcode_regex, $content );
585+                       preg_match( $shortcode_regex, $content, $matches );
586+
587                        if ( ! $matches || $format !== $matches[2] ) {
588                                if ( empty( $meta['media'] ) && ! empty( $compat[$format] ) ) {
589                                        $format_output .= $compat[$format];
590@@ -398,6 +399,50 @@ function post_formats_compat( $content, $id = 0 ) {
591                                                // attempt to embed the URL
592                                                $format_output .= sprintf( '[embed]%s[/embed]', $meta['media'] );
593                                        }
594+                               } elseif ( empty( $meta['media'] ) ) {
595+                                       $data = '';
596+                                       // attempt to grab an embed code or URL from the content
597+                                       if ( 'audio' === $format ) {
598+                                               $data = get_content_audio( $content, true );
599+                                       } elseif ( 'video' === $format ) {
600+                                               $data = get_content_video( $content, true );
601+                                       }
602+
603+                                       if ( ! empty( $data ) ) {
604+                                               // attempt to embed the URL
605+                                               if ( 0 === stripos( $data, 'http' ) )
606+                                                       $format_output .= sprintf( '[embed]%s[/embed]', $data );
607+                                               else // data is probably an embed code
608+                                                       $format_output .= $data;
609+                                       } elseif ( 'audio' === $format ) {
610+                                               // get attached audio URL
611+                                               $audios = get_post_audio( $post->ID );
612+                                               if ( ! empty( $audios ) ) {
613+                                                       $audio = reset( $audios );
614+                                                       $url = wp_get_attachment_url( $audio->ID );
615+                                                       // core or plugin support for [audio]
616+                                                       if ( shortcode_exists( 'audio' ) ) {
617+                                                               $format_output .= sprintf( '[audio src="%s"/]', $url );
618+                                                       } else {
619+                                                               // no support detected, just add URL
620+                                                               $format_output .= sprintf( '<a class="wp-post-format-link-audio" href="%1$s">%1$s</a>', $url );
621+                                                       }
622+                                               }
623+                                       } elseif ( 'video' === $format ) {
624+                                               // get attached video URL
625+                                               $videos = get_post_video( $post->ID );
626+                                               if ( ! empty( $videos ) ) {
627+                                                       $video = reset( $videos );
628+                                                       $url = wp_get_attachment_url( $video->ID );
629+                                                       // core or plugin support for [video]
630+                                                       if ( shortcode_exists( 'video' ) ) {
631+                                                               $format_output .= sprintf( '[video src="%s"/]', $url );
632+                                                       } else {
633+                                                               // no support detected, just add URL link
634+                                                               $format_output .= sprintf( '<a class="wp-post-format-link-video" href="%1$s">%1$s</a>', $url );
635+                                                       }
636+                                               }
637+                                       }
638                                }
639                        }
640                        break;
641diff --git wp-includes/script-loader.php wp-includes/script-loader.php
642index d168c2c..dbe6cc4 100644
643--- wp-includes/script-loader.php
644+++ wp-includes/script-loader.php
645@@ -274,6 +274,9 @@ function wp_default_scripts( &$scripts ) {
646 
647        $scripts->add( 'imgareaselect', "/wp-includes/js/imgareaselect/jquery.imgareaselect$suffix.js", array('jquery'), '0.9.8', 1 );
648 
649+       $scripts->add( 'mediaelement', "/wp-includes/js/mediaelement/mediaelement-and-player$suffix.js", array('jquery'), '2.10.1', 1 );
650+       $scripts->add( 'wp-mediaelement', "/wp-includes/js/mediaelement/wp-mediaelement.js", array('mediaelement'), false, 1 );
651+
652        $scripts->add( 'password-strength-meter', "/wp-admin/js/password-strength-meter$suffix.js", array('jquery'), false, 1 );
653        did_action( 'init' ) && $scripts->localize( 'password-strength-meter', 'pwsL10n', array(
654                'empty' => __('Strength indicator'),
655@@ -540,6 +543,9 @@ function wp_default_styles( &$styles ) {
656        $styles->add( 'media-views', "/wp-includes/css/media-views$suffix.css", array( 'buttons' ) );
657        $styles->add( 'buttons', "/wp-includes/css/buttons$suffix.css" );
658 
659+       $styles->add( 'mediaelement', "/wp-includes/js/mediaelement/mediaelementplayer$suffix.css" );
660+       $styles->add( 'wp-mediaelement', "/wp-includes/js/mediaelement/wp-mediaelement.css", array( 'mediaelement' ) );
661+
662        foreach ( $rtl_styles as $rtl_style ) {
663                $styles->add_data( $rtl_style, 'rtl', true );
664                if ( $suffix && ! in_array( $rtl_style, $no_suffix ) )
665diff --git wp-includes/shortcodes.php wp-includes/shortcodes.php
666index 2dfc277..716dae4 100644
667--- wp-includes/shortcodes.php
668+++ wp-includes/shortcodes.php
669@@ -128,6 +128,20 @@ function remove_all_shortcodes() {
670 }
671 
672 /**
673+ * Whether a registered shortcode exists named $tag
674+ *
675+ * @since 3.6.0
676+ *
677+ * @global array $shortcode_tags
678+ * @param string $tag
679+ * @return boolean
680+ */
681+function shortcode_exists( $tag ) {
682+       global $shortcode_tags;
683+       return array_key_exists( $tag, $shortcode_tags );
684+}
685+
686+/**
687  * Search content for shortcodes and filter shortcodes through their hooks.
688  *
689  * If there are no shortcode tags defined, then the content will be returned