]> scripts.mit.edu Git - autoinstalls/wordpress.git/blobdiff - wp-includes/media.php
WordPress 4.7.1
[autoinstalls/wordpress.git] / wp-includes / media.php
index e6c27b0c85af3b2c90c438c9846d5bcb87030037..deb18a40b6ecec7170ad82ec36782beedc7ceaa1 100644 (file)
@@ -6,6 +6,23 @@
  * @subpackage Media
  */
 
+/**
+ * Retrieve additional image sizes.
+ *
+ * @since 4.7.0
+ *
+ * @global array $_wp_additional_image_sizes
+ *
+ * @return array Additional images size data.
+ */
+function wp_get_additional_image_sizes() {
+       global $_wp_additional_image_sizes;
+       if ( ! $_wp_additional_image_sizes ) {
+               $_wp_additional_image_sizes = array();
+       }
+       return $_wp_additional_image_sizes;
+}
+
 /**
  * Scale down the default size of an image.
  *
@@ -27,7 +44,6 @@
  * @since 2.5.0
  *
  * @global int   $content_width
- * @global array $_wp_additional_image_sizes
  *
  * @param int          $width   Width of the image in pixels.
  * @param int          $height  Height of the image in pixels.
@@ -39,7 +55,9 @@
  * @return array Width and height of what the result image should resize to.
  */
 function image_constrain_size_for_editor( $width, $height, $size = 'medium', $context = null ) {
-       global $content_width, $_wp_additional_image_sizes;
+       global $content_width;
+
+       $_wp_additional_image_sizes = wp_get_additional_image_sizes();
 
        if ( ! $context )
                $context = is_admin() ? 'edit' : 'display';
@@ -82,11 +100,13 @@ function image_constrain_size_for_editor( $width, $height, $size = 'medium', $co
                if ( intval($content_width) > 0 ) {
                        $max_width = min( intval($content_width), $max_width );
                }
-       } elseif ( isset( $_wp_additional_image_sizes ) && count( $_wp_additional_image_sizes ) && in_array( $size, array_keys( $_wp_additional_image_sizes ) ) ) {
+       } elseif ( ! empty( $_wp_additional_image_sizes ) && in_array( $size, array_keys( $_wp_additional_image_sizes ) ) ) {
                $max_width = intval( $_wp_additional_image_sizes[$size]['width'] );
                $max_height = intval( $_wp_additional_image_sizes[$size]['height'] );
-               if ( intval($content_width) > 0 && 'edit' == $context ) // Only in admin. Assume that theme authors know what they're doing.
-                       $max_width = min( intval($content_width), $max_width );
+               // Only in admin. Assume that theme authors know what they're doing.
+               if ( intval( $content_width ) > 0 && 'edit' === $context ) {
+                       $max_width = min( intval( $content_width ), $max_width );
+               }
        }
        // $size == 'full' has no constraint
        else {
@@ -95,7 +115,7 @@ function image_constrain_size_for_editor( $width, $height, $size = 'medium', $co
        }
 
        /**
-        * Filter the maximum image size dimensions for the editor.
+        * Filters the maximum image size dimensions for the editor.
         *
         * @since 2.5.0
         *
@@ -148,7 +168,7 @@ function image_hwstring( $width, $height ) {
  * function won't create a new resized copy, it will just return an already
  * resized one if it exists.
  *
- * A plugin may use the 'image_downsize' filter to hook into and offer image
+ * A plugin may use the {@see 'image_downsize'} filter to hook into and offer image
  * resizing services for images. The hook must return an array with the same
  * elements that are returned in the function. The first element being the URL
  * to the new image that was resized.
@@ -163,12 +183,10 @@ function image_hwstring( $width, $height ) {
  *                     the image is an intermediate size. False on failure.
  */
 function image_downsize( $id, $size = 'medium' ) {
-
-       if ( !wp_attachment_is_image($id) )
-               return false;
+       $is_image = wp_attachment_is_image( $id );
 
        /**
-        * Filter whether to preempt the output of image_downsize().
+        * Filters whether to preempt the output of image_downsize().
         *
         * Passing a truthy value to the filter will effectively short-circuit
         * down-sizing the image, returning that value as output instead.
@@ -190,6 +208,19 @@ function image_downsize( $id, $size = 'medium' ) {
        $is_intermediate = false;
        $img_url_basename = wp_basename($img_url);
 
+       // If the file isn't an image, attempt to replace its URL with a rendered image from its meta.
+       // Otherwise, a non-image type could be returned.
+       if ( ! $is_image ) {
+               if ( ! empty( $meta['sizes'] ) ) {
+                       $img_url = str_replace( $img_url_basename, $meta['sizes']['full']['file'], $img_url );
+                       $img_url_basename = $meta['sizes']['full']['file'];
+                       $width = $meta['sizes']['full']['width'];
+                       $height = $meta['sizes']['full']['height'];
+               } else {
+                       return false;
+               }
+       }
+
        // try for a new style intermediate size
        if ( $intermediate = image_get_intermediate_size($id, $size) ) {
                $img_url = str_replace($img_url_basename, $intermediate['file'], $img_url);
@@ -258,15 +289,12 @@ function add_image_size( $name, $width = 0, $height = 0, $crop = false ) {
  *
  * @since 3.9.0
  *
- * @global array $_wp_additional_image_sizes
- *
  * @param string $name The image size to check.
  * @return bool True if the image size exists, false if not.
  */
 function has_image_size( $name ) {
-       global $_wp_additional_image_sizes;
-
-       return isset( $_wp_additional_image_sizes[ $name ] );
+       $sizes = wp_get_additional_image_sizes();
+       return isset( $sizes[ $name ] );
 }
 
 /**
@@ -309,12 +337,12 @@ function set_post_thumbnail_size( $width = 0, $height = 0, $crop = false ) {
 /**
  * Gets an img tag for an image attachment, scaling it down if requested.
  *
- * The filter 'get_image_tag_class' allows for changing the class name for the
+ * The {@see 'get_image_tag_class'} filter allows for changing the class name for the
  * image without having to use regular expressions on the HTML content. The
  * parameters are: what WordPress will use for the class, the Attachment ID,
  * image align value, and the size the image should be.
  *
- * The second filter 'get_image_tag' has the HTML content, which can then be
+ * The second filter, {@see 'get_image_tag'}, has the HTML content, which can then be
  * further manipulated by a plugin to change all attribute values and even HTML
  * content.
  *
@@ -339,7 +367,7 @@ function get_image_tag( $id, $alt, $title, $align, $size = 'medium' ) {
        $class = 'align' . esc_attr($align) .' size-' . esc_attr($size) . ' wp-image-' . $id;
 
        /**
-        * Filter the value of the attachment's image tag class attribute.
+        * Filters the value of the attachment's image tag class attribute.
         *
         * @since 2.6.0
         *
@@ -354,7 +382,7 @@ function get_image_tag( $id, $alt, $title, $align, $size = 'medium' ) {
        $html = '<img src="' . esc_attr($img_src) . '" alt="' . esc_attr($alt) . '" ' . $title . $hwstring . 'class="' . $class . '" />';
 
        /**
-        * Filter the HTML content for the image tag.
+        * Filters the HTML content for the image tag.
         *
         * @since 2.6.0
         *
@@ -431,7 +459,7 @@ function wp_constrain_dimensions( $current_width, $current_height, $max_width =
        }
 
        /**
-        * Filter dimensions to constrain down-sampled images to.
+        * Filters dimensions to constrain down-sampled images to.
         *
         * @since 4.1.0
         *
@@ -477,7 +505,7 @@ function image_resize_dimensions( $orig_w, $orig_h, $dest_w, $dest_h, $crop = fa
                return false;
 
        /**
-        * Filter whether to preempt calculating the image resize dimensions.
+        * Filters whether to preempt calculating the image resize dimensions.
         *
         * Passing a non-null value to the filter will effectively short-circuit
         * image_resize_dimensions(), returning that value instead.
@@ -562,7 +590,7 @@ function image_resize_dimensions( $orig_w, $orig_h, $dest_w, $dest_h, $crop = fa
  * Resizes an image to make a thumbnail or intermediate size.
  *
  * The returned array has the file size, the image width, and image height. The
- * filter 'image_make_intermediate_size' can be used to hook in and change the
+ * {@see 'image_make_intermediate_size'} filter can be used to hook in and change the
  * values of the returned array. The only parameter is the resized file path.
  *
  * @since 2.5.0
@@ -591,6 +619,36 @@ function image_make_intermediate_size( $file, $width, $height, $crop = false ) {
        return false;
 }
 
+/**
+ * Helper function to test if aspect ratios for two images match.
+ *
+ * @since 4.6.0
+ *
+ * @param int $source_width  Width of the first image in pixels.
+ * @param int $source_height Height of the first image in pixels.
+ * @param int $target_width  Width of the second image in pixels.
+ * @param int $target_height Height of the second image in pixels.
+ * @return bool True if aspect ratios match within 1px. False if not.
+ */
+function wp_image_matches_ratio( $source_width, $source_height, $target_width, $target_height ) {
+       /*
+        * To test for varying crops, we constrain the dimensions of the larger image
+        * to the dimensions of the smaller image and see if they match.
+        */
+       if ( $source_width > $target_width ) {
+               $constrained_size = wp_constrain_dimensions( $source_width, $source_height, $target_width );
+               $expected_size = array( $target_width, $target_height );
+       } else {
+               $constrained_size = wp_constrain_dimensions( $target_width, $target_height, $source_width );
+               $expected_size = array( $source_width, $source_height );
+       }
+
+       // If the image dimensions are within 1px of the expected size, we consider it a match.
+       $matched = ( abs( $constrained_size[0] - $expected_size[0] ) <= 1 && abs( $constrained_size[1] - $expected_size[1] ) <= 1 );
+
+       return $matched;
+}
+
 /**
  * Retrieves the image's intermediate size (resized) path, width, and height.
  *
@@ -623,73 +681,87 @@ function image_make_intermediate_size( $file, $width, $height, $crop = false ) {
  *     @type string $file   Image's path relative to uploads directory
  *     @type int    $width  Width of image
  *     @type int    $height Height of image
- *     @type string $path   Optional. Image's absolute filesystem path. Only returned if registered
- *                          size is passed to `$size` parameter.
- *     @type string $url    Optional. Image's URL. Only returned if registered size is passed to `$size`
- *                          parameter.
+ *     @type string $path   Image's absolute filesystem path.
+ *     @type string $url    Image's URL.
  * }
  */
 function image_get_intermediate_size( $post_id, $size = 'thumbnail' ) {
-       if ( !is_array( $imagedata = wp_get_attachment_metadata( $post_id ) ) )
+       if ( ! $size || ! is_array( $imagedata = wp_get_attachment_metadata( $post_id ) ) || empty( $imagedata['sizes'] )  ) {
                return false;
+       }
+
+       $data = array();
 
-       // get the best one for a specified set of dimensions
-       if ( is_array($size) && !empty($imagedata['sizes']) ) {
+       // Find the best match when '$size' is an array.
+       if ( is_array( $size ) ) {
                $candidates = array();
 
+               if ( ! isset( $imagedata['file'] ) && isset( $imagedata['sizes']['full'] ) ) {
+                       $imagedata['height'] = $imagedata['sizes']['full']['height'];
+                       $imagedata['width']  = $imagedata['sizes']['full']['width'];
+               }
+
                foreach ( $imagedata['sizes'] as $_size => $data ) {
                        // If there's an exact match to an existing image size, short circuit.
                        if ( $data['width'] == $size[0] && $data['height'] == $size[1] ) {
-                               list( $data['width'], $data['height'] ) = image_constrain_size_for_editor( $data['width'], $data['height'], $size );
-
-                               /** This filter is documented in wp-includes/media.php */
-                               return apply_filters( 'image_get_intermediate_size', $data, $post_id, $size );
+                               $candidates[ $data['width'] * $data['height'] ] = $data;
+                               break;
                        }
-                       // If it's not an exact match but it's at least the dimensions requested.
+
+                       // If it's not an exact match, consider larger sizes with the same aspect ratio.
                        if ( $data['width'] >= $size[0] && $data['height'] >= $size[1] ) {
-                               $candidates[ $data['width'] * $data['height'] ] = $_size;
+                               // If '0' is passed to either size, we test ratios against the original file.
+                               if ( 0 === $size[0] || 0 === $size[1] ) {
+                                       $same_ratio = wp_image_matches_ratio( $data['width'], $data['height'], $imagedata['width'], $imagedata['height'] );
+                               } else {
+                                       $same_ratio = wp_image_matches_ratio( $data['width'], $data['height'], $size[0], $size[1] );
+                               }
+
+                               if ( $same_ratio ) {
+                                       $candidates[ $data['width'] * $data['height'] ] = $data;
+                               }
                        }
                }
 
                if ( ! empty( $candidates ) ) {
-                       // find for the smallest image not smaller than the desired size
-                       ksort( $candidates );
-                       foreach ( $candidates as $_size ) {
-                               $data = $imagedata['sizes'][$_size];
-
-                               // Skip images with unexpectedly divergent aspect ratios (crops)
-                               // First, we calculate what size the original image would be if constrained to a box the size of the current image in the loop
-                               $maybe_cropped = image_resize_dimensions($imagedata['width'], $imagedata['height'], $data['width'], $data['height'], false );
-                               // If the size doesn't match within one pixel, then it is of a different aspect ratio, so we skip it, unless it's the thumbnail size
-                               if ( 'thumbnail' != $_size &&
-                                 ( ! $maybe_cropped
-                                   || ( $maybe_cropped[4] != $data['width'] && $maybe_cropped[4] + 1 != $data['width'] )
-                                   || ( $maybe_cropped[5] != $data['height'] && $maybe_cropped[5] + 1 != $data['height'] )
-                                 ) ) {
-                                 continue;
-                               }
-                               // If we're still here, then we're going to use this size.
-                               list( $data['width'], $data['height'] ) = image_constrain_size_for_editor( $data['width'], $data['height'], $size );
-
-                               /** This filter is documented in wp-includes/media.php */
-                               return apply_filters( 'image_get_intermediate_size', $data, $post_id, $size );
+                       // Sort the array by size if we have more than one candidate.
+                       if ( 1 < count( $candidates ) ) {
+                               ksort( $candidates );
                        }
+
+                       $data = array_shift( $candidates );
+               /*
+                * When the size requested is smaller than the thumbnail dimensions, we
+                * fall back to the thumbnail size to maintain backwards compatibility with
+                * pre 4.6 versions of WordPress.
+                */
+               } elseif ( ! empty( $imagedata['sizes']['thumbnail'] ) && $imagedata['sizes']['thumbnail']['width'] >= $size[0] && $imagedata['sizes']['thumbnail']['width'] >= $size[1] ) {
+                       $data = $imagedata['sizes']['thumbnail'];
+               } else {
+                       return false;
                }
+
+               // Constrain the width and height attributes to the requested values.
+               list( $data['width'], $data['height'] ) = image_constrain_size_for_editor( $data['width'], $data['height'], $size );
+
+       } elseif ( ! empty( $imagedata['sizes'][ $size ] ) ) {
+               $data = $imagedata['sizes'][ $size ];
        }
 
-       if ( is_array($size) || empty($size) || empty($imagedata['sizes'][$size]) )
+       // If we still don't have a match at this point, return false.
+       if ( empty( $data ) ) {
                return false;
+       }
 
-       $data = $imagedata['sizes'][$size];
        // include the full filesystem path of the intermediate file
-       if ( empty($data['path']) && !empty($data['file']) ) {
+       if ( empty( $data['path'] ) && ! empty( $data['file'] ) && ! empty( $imagedata['file'] ) ) {
                $file_url = wp_get_attachment_url($post_id);
                $data['path'] = path_join( dirname($imagedata['file']), $data['file'] );
                $data['url'] = path_join( dirname($file_url), $data['file'] );
        }
 
        /**
-        * Filter the output of image_get_intermediate_size()
+        * Filters the output of image_get_intermediate_size()
         *
         * @since 4.4.0
         *
@@ -709,18 +781,17 @@ function image_get_intermediate_size( $post_id, $size = 'thumbnail' ) {
  *
  * @since 3.0.0
  *
- * @global array $_wp_additional_image_sizes
- *
  * @return array Returns a filtered array of image size strings.
  */
 function get_intermediate_image_sizes() {
-       global $_wp_additional_image_sizes;
+       $_wp_additional_image_sizes = wp_get_additional_image_sizes();
        $image_sizes = array('thumbnail', 'medium', 'medium_large', 'large'); // Standard sizes
-       if ( isset( $_wp_additional_image_sizes ) && count( $_wp_additional_image_sizes ) )
+       if ( ! empty( $_wp_additional_image_sizes ) ) {
                $image_sizes = array_merge( $image_sizes, array_keys( $_wp_additional_image_sizes ) );
+       }
 
        /**
-        * Filter the list of intermediate image sizes.
+        * Filters the list of intermediate image sizes.
         *
         * @since 2.5.0
         *
@@ -767,7 +838,7 @@ function wp_get_attachment_image_src( $attachment_id, $size = 'thumbnail', $icon
                }
        }
        /**
-        * Filter the image src result.
+        * Filters the image src result.
         *
         * @since 4.3.0
         *
@@ -811,18 +882,14 @@ function wp_get_attachment_image($attachment_id, $size = 'thumbnail', $icon = fa
                $default_attr = array(
                        'src'   => $src,
                        'class' => "attachment-$size_class size-$size_class",
-                       'alt'   => trim(strip_tags( get_post_meta($attachment_id, '_wp_attachment_image_alt', true) )), // Use Alt field first
+                       'alt'   => trim( strip_tags( get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ) ) ),
                );
-               if ( empty($default_attr['alt']) )
-                       $default_attr['alt'] = trim(strip_tags( $attachment->post_excerpt )); // If not, Use the Caption
-               if ( empty($default_attr['alt']) )
-                       $default_attr['alt'] = trim(strip_tags( $attachment->post_title )); // Finally, use the title
 
                $attr = wp_parse_args( $attr, $default_attr );
 
                // Generate 'srcset' and 'sizes' if not already present.
                if ( empty( $attr['srcset'] ) ) {
-                       $image_meta = get_post_meta( $attachment_id, '_wp_attachment_metadata', true );
+                       $image_meta = wp_get_attachment_metadata( $attachment_id );
 
                        if ( is_array( $image_meta ) ) {
                                $size_array = array( absint( $width ), absint( $height ) );
@@ -840,7 +907,7 @@ function wp_get_attachment_image($attachment_id, $size = 'thumbnail', $icon = fa
                }
 
                /**
-                * Filter the list of attachment image attributes.
+                * Filters the list of attachment image attributes.
                 *
                 * @since 2.8.0
                 *
@@ -878,24 +945,28 @@ function wp_get_attachment_image_url( $attachment_id, $size = 'thumbnail', $icon
 }
 
 /**
- * Caches and returns the base URL of the uploads directory.
+ * Get the attachment path relative to the upload directory.
  *
- * @since 4.4.0
+ * @since 4.4.1
  * @access private
  *
- * @return string The base URL, cached.
+ * @param string $file Attachment file name.
+ * @return string Attachment path relative to the upload directory.
  */
-function _wp_upload_dir_baseurl() {
-       static $baseurl = array();
+function _wp_get_attachment_relative_path( $file ) {
+       $dirname = dirname( $file );
 
-       $blog_id = get_current_blog_id();
+       if ( '.' === $dirname ) {
+               return '';
+       }
 
-       if ( empty( $baseurl[$blog_id] ) ) {
-               $uploads_dir = wp_upload_dir();
-               $baseurl[$blog_id] = $uploads_dir['baseurl'];
+       if ( false !== strpos( $dirname, 'wp-content/uploads' ) ) {
+               // Get the directory name relative to the upload directory (back compat for pre-2.7 uploads)
+               $dirname = substr( $dirname, strpos( $dirname, 'wp-content/uploads' ) + 18 );
+               $dirname = ltrim( $dirname, '/' );
        }
 
-       return $baseurl[$blog_id];
+       return $dirname;
 }
 
 /**
@@ -947,7 +1018,7 @@ function wp_get_attachment_image_srcset( $attachment_id, $size = 'medium', $imag
        }
 
        if ( ! is_array( $image_meta ) ) {
-               $image_meta = get_post_meta( $attachment_id, '_wp_attachment_metadata', true );
+               $image_meta = wp_get_attachment_metadata( $attachment_id );
        }
 
        $image_src = $image[0];
@@ -971,7 +1042,17 @@ function wp_get_attachment_image_srcset( $attachment_id, $size = 'medium', $imag
  * @return string|bool          The 'srcset' attribute value. False on error or when only one source exists.
  */
 function wp_calculate_image_srcset( $size_array, $image_src, $image_meta, $attachment_id = 0 ) {
-       if ( empty( $image_meta['sizes'] ) ) {
+       /**
+        * Let plugins pre-filter the image meta to be able to fix inconsistencies in the stored data.
+        *
+        * @param array  $image_meta    The image meta data as returned by 'wp_get_attachment_metadata()'.
+        * @param array  $size_array    Array of width and height values in pixels (in that order).
+        * @param string $image_src     The 'src' of the image.
+        * @param int    $attachment_id The image attachment ID or 0 if not supplied.
+        */
+       $image_meta = apply_filters( 'wp_calculate_image_srcset_meta', $image_meta, $size_array, $image_src, $attachment_id );
+
+       if ( empty( $image_meta['sizes'] ) || ! isset( $image_meta['file'] ) || strlen( $image_meta['file'] ) < 4 ) {
                return false;
        }
 
@@ -987,7 +1068,6 @@ function wp_calculate_image_srcset( $size_array, $image_src, $image_meta, $attac
        }
 
        $image_basename = wp_basename( $image_meta['file'] );
-       $image_baseurl = _wp_upload_dir_baseurl();
 
        /*
         * WordPress flattens animated GIFs into one frame when generating intermediate sizes.
@@ -995,7 +1075,7 @@ function wp_calculate_image_srcset( $size_array, $image_src, $image_meta, $attac
         * If src is an intermediate size GIF, the full size is excluded from srcset to keep a flattened GIF from becoming animated.
         */
        if ( ! isset( $image_sizes['thumbnail']['mime-type'] ) || 'image/gif' !== $image_sizes['thumbnail']['mime-type'] ) {
-               $image_sizes['full'] = array(
+               $image_sizes[] = array(
                        'width'  => $image_meta['width'],
                        'height' => $image_meta['height'],
                        'file'   => $image_basename,
@@ -1004,19 +1084,23 @@ function wp_calculate_image_srcset( $size_array, $image_src, $image_meta, $attac
                return false;
        }
 
-       // Uploads are (or have been) in year/month sub-directories.
-       if ( $image_basename !== $image_meta['file'] ) {
-               $dirname = dirname( $image_meta['file'] );
+       // Retrieve the uploads sub-directory from the full size image.
+       $dirname = _wp_get_attachment_relative_path( $image_meta['file'] );
 
-               if ( $dirname !== '.' ) {
-                       $image_baseurl = trailingslashit( $image_baseurl ) . $dirname;
-               }
+       if ( $dirname ) {
+               $dirname = trailingslashit( $dirname );
        }
 
-       $image_baseurl = trailingslashit( $image_baseurl );
+       $upload_dir = wp_get_upload_dir();
+       $image_baseurl = trailingslashit( $upload_dir['baseurl'] ) . $dirname;
 
-       // Calculate the image aspect ratio.
-       $image_ratio = $image_height / $image_width;
+       /*
+        * If currently on HTTPS, prefer HTTPS URLs when we know they're supported by the domain
+        * (which is to say, when they share the domain name of the current request).
+        */
+       if ( is_ssl() && 'https' !== substr( $image_baseurl, 0, 5 ) && parse_url( $image_baseurl, PHP_URL_HOST ) === $_SERVER['HTTP_HOST'] ) {
+               $image_baseurl = set_url_scheme( $image_baseurl, 'https' );
+       }
 
        /*
         * Images that have been edited in WordPress after being uploaded will
@@ -1026,7 +1110,7 @@ function wp_calculate_image_srcset( $size_array, $image_src, $image_meta, $attac
        $image_edited = preg_match( '/-e[0-9]{13}/', wp_basename( $image_src ), $image_edit_hash );
 
        /**
-        * Filter the maximum image width to be included in a 'srcset' attribute.
+        * Filters the maximum image width to be included in a 'srcset' attribute.
         *
         * @since 4.4.0
         *
@@ -1038,42 +1122,63 @@ function wp_calculate_image_srcset( $size_array, $image_src, $image_meta, $attac
        // Array to hold URL candidates.
        $sources = array();
 
+       /**
+        * To make sure the ID matches our image src, we will check to see if any sizes in our attachment
+        * meta match our $image_src. If no matches are found we don't return a srcset to avoid serving
+        * an incorrect image. See #35045.
+        */
+       $src_matched = false;
+
        /*
         * Loop through available images. Only use images that are resized
         * versions of the same edit.
         */
        foreach ( $image_sizes as $image ) {
+               $is_src = false;
 
-               // Filter out images that are from previous edits.
-               if ( $image_edited && ! strpos( $image['file'], $image_edit_hash[0] ) ) {
+               // Check if image meta isn't corrupted.
+               if ( ! is_array( $image ) ) {
                        continue;
                }
 
-               // Filter out images that are wider than '$max_srcset_image_width'.
-               if ( $max_srcset_image_width && $image['width'] > $max_srcset_image_width ) {
+               // If the file name is part of the `src`, we've confirmed a match.
+               if ( ! $src_matched && false !== strpos( $image_src, $dirname . $image['file'] ) ) {
+                       $src_matched = $is_src = true;
+               }
+
+               // Filter out images that are from previous edits.
+               if ( $image_edited && ! strpos( $image['file'], $image_edit_hash[0] ) ) {
                        continue;
                }
 
-               // Calculate the new image ratio.
-               if ( $image['width'] ) {
-                       $image_ratio_compare = $image['height'] / $image['width'];
-               } else {
-                       $image_ratio_compare = 0;
+               /*
+                * Filters out images that are wider than '$max_srcset_image_width' unless
+                * that file is in the 'src' attribute.
+                */
+               if ( $max_srcset_image_width && $image['width'] > $max_srcset_image_width && ! $is_src ) {
+                       continue;
                }
 
-               // If the new ratio differs by less than 0.002, use it.
-               if ( abs( $image_ratio - $image_ratio_compare ) < 0.002 ) {
+               // If the image dimensions are within 1px of the expected size, use it.
+               if ( wp_image_matches_ratio( $image_width, $image_height, $image['width'], $image['height'] ) ) {
                        // Add the URL, descriptor, and value to the sources array to be returned.
-                       $sources[ $image['width'] ] = array(
+                       $source = array(
                                'url'        => $image_baseurl . $image['file'],
                                'descriptor' => 'w',
                                'value'      => $image['width'],
                        );
+
+                       // The 'src' image has to be the first in the 'srcset', because of a bug in iOS8. See #35030.
+                       if ( $is_src ) {
+                               $sources = array( $image['width'] => $source ) + $sources;
+                       } else {
+                               $sources[ $image['width'] ] = $source;
+                       }
                }
        }
 
        /**
-        * Filter an image's 'srcset' sources.
+        * Filters an image's 'srcset' sources.
         *
         * @since 4.4.0
         *
@@ -1091,19 +1196,19 @@ function wp_calculate_image_srcset( $size_array, $image_src, $image_meta, $attac
         * @param array  $size_array    Array of width and height values in pixels (in that order).
         * @param string $image_src     The 'src' of the image.
         * @param array  $image_meta    The image meta data as returned by 'wp_get_attachment_metadata()'.
-        * @param int    $attachment_id Image attachment ID or 0.
+        * @param int    $attachment_id Image attachment ID or 0.
         */
        $sources = apply_filters( 'wp_calculate_image_srcset', $sources, $size_array, $image_src, $image_meta, $attachment_id );
 
        // Only return a 'srcset' value if there is more than one source.
-       if ( count( $sources ) < 2 ) {
+       if ( ! $src_matched || count( $sources ) < 2 ) {
                return false;
        }
 
        $srcset = '';
 
        foreach ( $sources as $source ) {
-               $srcset .= $source['url'] . ' ' . $source['value'] . $source['descriptor'] . ', ';
+               $srcset .= str_replace( ' ', '%20', $source['url'] ) . ' ' . $source['value'] . $source['descriptor'] . ', ';
        }
 
        return rtrim( $srcset, ', ' );
@@ -1129,7 +1234,7 @@ function wp_get_attachment_image_sizes( $attachment_id, $size = 'medium', $image
        }
 
        if ( ! is_array( $image_meta ) ) {
-               $image_meta = get_post_meta( $attachment_id, '_wp_attachment_metadata', true );
+               $image_meta = wp_get_attachment_metadata( $attachment_id );
        }
 
        $image_src = $image[0];
@@ -1162,7 +1267,7 @@ function wp_calculate_image_sizes( $size, $image_src = null, $image_meta = null,
                $width = absint( $size[0] );
        } elseif ( is_string( $size ) ) {
                if ( ! $image_meta && $attachment_id ) {
-                       $image_meta = get_post_meta( $attachment_id, '_wp_attachment_metadata', true );
+                       $image_meta = wp_get_attachment_metadata( $attachment_id );
                }
 
                if ( is_array( $image_meta ) ) {
@@ -1181,7 +1286,7 @@ function wp_calculate_image_sizes( $size, $image_src = null, $image_meta = null,
        $sizes = sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', $width );
 
        /**
-        * Filter the output of 'wp_calculate_image_sizes()'.
+        * Filters the output of 'wp_calculate_image_sizes()'.
         *
         * @since 4.4.0
         *
@@ -1237,7 +1342,7 @@ function wp_make_content_images_responsive( $content ) {
        }
 
        foreach ( $selected_images as $image => $attachment_id ) {
-               $image_meta = get_post_meta( $attachment_id, '_wp_attachment_metadata', true );
+               $image_meta = wp_get_attachment_metadata( $attachment_id );
                $content = str_replace( $image, wp_image_add_srcset_and_sizes( $image, $image_meta, $attachment_id ), $content );
        }
 
@@ -1278,28 +1383,6 @@ function wp_image_add_srcset_and_sizes( $image, $image_meta, $attachment_id ) {
                return $image;
        }
 
-       $base_url = trailingslashit( _wp_upload_dir_baseurl() );
-       $image_base_url = $base_url;
-
-       $dirname = dirname( $image_meta['file'] );
-       if ( $dirname !== '.' ) {
-               $image_base_url .= trailingslashit( $dirname );
-       }
-
-       $all_sizes = wp_list_pluck( $image_meta['sizes'], 'file' );
-
-       foreach ( $all_sizes as $key => $file ) {
-               $all_sizes[ $key ] = $image_base_url . $file;
-       }
-
-       // Add the original image.
-       $all_sizes[] = $base_url . $image_meta['file'];
-
-       // Bail early if the image src doesn't match any of the known image sizes.
-       if ( ! in_array( $image_src, $all_sizes ) ) {
-               return $image;
-       }
-
        $width  = preg_match( '/ width="([0-9]+)"/',  $image, $match_width  ) ? (int) $match_width[1]  : 0;
        $height = preg_match( '/ height="([0-9]+)"/', $image, $match_height ) ? (int) $match_height[1] : 0;
 
@@ -1358,8 +1441,8 @@ function wp_image_add_srcset_and_sizes( $image, $image_meta, $attachment_id ) {
 /**
  * Adds a 'wp-post-image' class to post thumbnails. Internal use only.
  *
- * Uses the 'begin_fetch_post_thumbnail_html' and 'end_fetch_post_thumbnail_html' action hooks to
- * dynamically add/remove itself so as to only filter post thumbnails.
+ * Uses the {@see 'begin_fetch_post_thumbnail_html'} and {@see 'end_fetch_post_thumbnail_html'}
+ * action hooks to dynamically add/remove itself so as to only filter post thumbnails.
  *
  * @ignore
  * @since 2.9.0
@@ -1405,7 +1488,7 @@ add_shortcode('caption', 'img_caption_shortcode');
  * Builds the Caption shortcode output.
  *
  * Allows a plugin to replace the content that would otherwise be returned. The
- * filter is 'img_caption_shortcode' and passes an empty string, the attr
+ * filter is {@see 'img_caption_shortcode'} and passes an empty string, the attr
  * parameter and the content parameter values.
  *
  * The supported attributes for the shortcode are 'id', 'align', 'width', and
@@ -1438,7 +1521,7 @@ function img_caption_shortcode( $attr, $content = null ) {
        }
 
        /**
-        * Filter the default caption shortcode output.
+        * Filters the default caption shortcode output.
         *
         * If the filtered output isn't empty, it will be used instead of generating
         * the default caption template.
@@ -1477,7 +1560,7 @@ function img_caption_shortcode( $attr, $content = null ) {
        $width = $html5 ? $atts['width'] : ( 10 + $atts['width'] );
 
        /**
-        * Filter the width of an image's caption.
+        * Filters the width of an image's caption.
         *
         * By default, the caption is 10 pixels greater than the width of the image,
         * to prevent post content from running up against a floated image.
@@ -1494,10 +1577,10 @@ function img_caption_shortcode( $attr, $content = null ) {
        $caption_width = apply_filters( 'img_caption_shortcode_width', $width, $atts, $content );
 
        $style = '';
-       if ( $caption_width )
+       if ( $caption_width ) {
                $style = 'style="width: ' . (int) $caption_width . 'px" ';
+       }
 
-       $html = '';
        if ( $html5 ) {
                $html = '<figure ' . $atts['id'] . $style . 'class="' . esc_attr( $class ) . '">'
                . do_shortcode( $content ) . '<figcaption class="wp-caption-text">' . $atts['caption'] . '</figcaption></figure>';
@@ -1560,7 +1643,7 @@ function gallery_shortcode( $attr ) {
        }
 
        /**
-        * Filter the default gallery shortcode output.
+        * Filters the default gallery shortcode output.
         *
         * If the filtered output isn't empty, it will be used instead of generating
         * the default gallery template.
@@ -1644,7 +1727,7 @@ function gallery_shortcode( $attr ) {
        $gallery_style = '';
 
        /**
-        * Filter whether to print default gallery styles.
+        * Filters whether to print default gallery styles.
         *
         * @since 3.1.0
         *
@@ -1678,7 +1761,7 @@ function gallery_shortcode( $attr ) {
        $gallery_div = "<div id='$selector' class='gallery galleryid-{$id} gallery-columns-{$columns} gallery-size-{$size_class}'>";
 
        /**
-        * Filter the default gallery shortcode CSS styles.
+        * Filters the default gallery shortcode CSS styles.
         *
         * @since 2.5.0
         *
@@ -1846,7 +1929,7 @@ function wp_playlist_shortcode( $attr ) {
        }
 
        /**
-        * Filter the playlist output.
+        * Filters the playlist output.
         *
         * Passing a non-empty value to the filter will short-circuit generation
         * of the default playlist output, returning the passed value instead.
@@ -2006,7 +2089,7 @@ function wp_playlist_shortcode( $attr ) {
 
        if ( 1 === $instance ) {
                /**
-                * Print and enqueue playlist scripts, styles, and JavaScript templates.
+                * Prints and enqueues playlist scripts, styles, and JavaScript templates.
                 *
                 * @since 3.9.0
                 *
@@ -2023,8 +2106,6 @@ function wp_playlist_shortcode( $attr ) {
                echo (int) $theme_width;
        ?>"<?php if ( 'video' === $safe_type ):
                echo ' height="', (int) $theme_height, '"';
-       else:
-               echo ' style="visibility: hidden"';
        endif; ?>></<?php echo $safe_type ?>>
        <div class="wp-playlist-next"></div>
        <div class="wp-playlist-prev"></div>
@@ -2052,7 +2133,7 @@ add_shortcode( 'playlist', 'wp_playlist_shortcode' );
  */
 function wp_mediaelement_fallback( $url ) {
        /**
-        * Filter the Mediaelement fallback output for no-JS.
+        * Filters the Mediaelement fallback output for no-JS.
         *
         * @since 3.6.0
         *
@@ -2071,7 +2152,7 @@ function wp_mediaelement_fallback( $url ) {
  */
 function wp_get_audio_extensions() {
        /**
-        * Filter the list of supported audio formats.
+        * Filters the list of supported audio formats.
         *
         * @since 3.6.0
         *
@@ -2106,7 +2187,7 @@ function wp_get_attachment_id3_keys( $attachment, $context = 'display' ) {
        }
 
        /**
-        * Filter the editable list of keys to look up data from an attachment's metadata.
+        * Filters the editable list of keys to look up data from an attachment's metadata.
         *
         * @since 3.9.0
         *
@@ -2132,9 +2213,9 @@ function wp_get_attachment_id3_keys( $attachment, $context = 'display' ) {
  *     @type string $src      URL to the source of the audio file. Default empty.
  *     @type string $loop     The 'loop' attribute for the `<audio>` element. Default empty.
  *     @type string $autoplay The 'autoplay' attribute for the `<audio>` element. Default empty.
- *     @type string $preload  The 'preload' attribute for the `<audio>` element. Default empty.
+ *     @type string $preload  The 'preload' attribute for the `<audio>` element. Default 'none'.
  *     @type string $class    The 'class' attribute for the `<audio>` element. Default 'wp-audio-shortcode'.
- *     @type string $style    The 'style' attribute for the `<audio>` element. Default 'width: 100%'.
+ *     @type string $style    The 'style' attribute for the `<audio>` element. Default 'width: 100%;'.
  * }
  * @param string $content Shortcode content.
  * @return string|void HTML content to display audio.
@@ -2146,7 +2227,7 @@ function wp_audio_shortcode( $attr, $content = '' ) {
        $instance++;
 
        /**
-        * Filter the default audio shortcode output.
+        * Filters the default audio shortcode output.
         *
         * If the filtered output isn't empty, it will be used instead of generating the default audio template.
         *
@@ -2169,7 +2250,9 @@ function wp_audio_shortcode( $attr, $content = '' ) {
                'src'      => '',
                'loop'     => '',
                'autoplay' => '',
-               'preload'  => 'none'
+               'preload'  => 'none',
+               'class'    => 'wp-audio-shortcode',
+               'style'    => 'width: 100%;'
        );
        foreach ( $default_types as $type ) {
                $defaults_atts[$type] = '';
@@ -2212,7 +2295,7 @@ function wp_audio_shortcode( $attr, $content = '' ) {
        }
 
        /**
-        * Filter the media library used for the audio shortcode.
+        * Filters the media library used for the audio shortcode.
         *
         * @since 3.6.0
         *
@@ -2225,19 +2308,21 @@ function wp_audio_shortcode( $attr, $content = '' ) {
        }
 
        /**
-        * Filter the class attribute for the audio shortcode output container.
+        * Filters the class attribute for the audio shortcode output container.
         *
         * @since 3.6.0
         *
         * @param string $class CSS class or list of space-separated classes.
         */
+       $atts['class'] = apply_filters( 'wp_audio_shortcode_class', $atts['class'] );
+
        $html_atts = array(
-               'class'    => apply_filters( 'wp_audio_shortcode_class', 'wp-audio-shortcode' ),
+               'class'    => $atts['class'],
                'id'       => sprintf( 'audio-%d-%d', $post_id, $instance ),
                'loop'     => wp_validate_boolean( $atts['loop'] ),
                'autoplay' => wp_validate_boolean( $atts['autoplay'] ),
                'preload'  => $atts['preload'],
-               'style'    => 'width: 100%; visibility: hidden;',
+               'style'    => $atts['style'],
        );
 
        // These ones should just be omitted altogether if they are blank
@@ -2277,7 +2362,7 @@ function wp_audio_shortcode( $attr, $content = '' ) {
        $html .= '</audio>';
 
        /**
-        * Filter the audio shortcode output.
+        * Filters the audio shortcode output.
         *
         * @since 3.6.0
         *
@@ -2300,7 +2385,7 @@ add_shortcode( 'audio', 'wp_audio_shortcode' );
  */
 function wp_get_video_extensions() {
        /**
-        * Filter the list of supported video formats.
+        * Filters the list of supported video formats.
         *
         * @since 3.6.0
         *
@@ -2346,7 +2431,7 @@ function wp_video_shortcode( $attr, $content = '' ) {
        $instance++;
 
        /**
-        * Filter the default video shortcode output.
+        * Filters the default video shortcode output.
         *
         * If the filtered output isn't empty, it will be used instead of generating
         * the default video template.
@@ -2376,6 +2461,7 @@ function wp_video_shortcode( $attr, $content = '' ) {
                'preload'  => 'metadata',
                'width'    => 640,
                'height'   => 360,
+               'class'    => 'wp-video-shortcode',
        );
 
        foreach ( $default_types as $type ) {
@@ -2446,7 +2532,7 @@ function wp_video_shortcode( $attr, $content = '' ) {
        }
 
        /**
-        * Filter the media library used for the video shortcode.
+        * Filters the media library used for the video shortcode.
         *
         * @since 3.6.0
         *
@@ -2459,14 +2545,16 @@ function wp_video_shortcode( $attr, $content = '' ) {
        }
 
        /**
-        * Filter the class attribute for the video shortcode output container.
+        * Filters the class attribute for the video shortcode output container.
         *
         * @since 3.6.0
         *
         * @param string $class CSS class or list of space-separated classes.
         */
+       $atts['class'] = apply_filters( 'wp_video_shortcode_class', $atts['class'] );
+
        $html_atts = array(
-               'class'    => apply_filters( 'wp_video_shortcode_class', 'wp-video-shortcode' ),
+               'class'    => $atts['class'],
                'id'       => sprintf( 'video-%d-%d', $post_id, $instance ),
                'width'    => absint( $atts['width'] ),
                'height'   => absint( $atts['height'] ),
@@ -2527,12 +2615,12 @@ function wp_video_shortcode( $attr, $content = '' ) {
 
        $width_rule = '';
        if ( ! empty( $atts['width'] ) ) {
-               $width_rule = sprintf( 'width: %dpx; ', $atts['width'] );
+               $width_rule = sprintf( 'width: %dpx;', $atts['width'] );
        }
        $output = sprintf( '<div style="%s" class="wp-video">%s</div>', $width_rule, $html );
 
        /**
-        * Filter the output of the video shortcode.
+        * Filters the output of the video shortcode.
         *
         * @since 3.6.0
         *
@@ -2615,7 +2703,7 @@ function adjacent_image_link( $prev = true, $size = 'thumbnail', $text = false )
        $adjacent = $prev ? 'previous' : 'next';
 
        /**
-        * Filter the adjacent image link.
+        * Filters the adjacent image link.
         *
         * The dynamic portion of the hook name, `$adjacent`, refers to the type of adjacency,
         * either 'next', or 'previous'.
@@ -2634,11 +2722,15 @@ function adjacent_image_link( $prev = true, $size = 'thumbnail', $text = false )
  * Retrieves taxonomies attached to given the attachment.
  *
  * @since 2.5.0
+ * @since 4.7.0 Introduced the `$output` parameter.
  *
  * @param int|array|object $attachment Attachment ID, data array, or data object.
+ * @param string           $output     Output type. 'names' to return an array of taxonomy names,
+ *                                     or 'objects' to return an array of taxonomy objects.
+ *                                     Default is 'names'.
  * @return array Empty array on failure. List of taxonomies on success.
  */
-function get_attachment_taxonomies( $attachment ) {
+function get_attachment_taxonomies( $attachment, $output = 'names' ) {
        if ( is_int( $attachment ) ) {
                $attachment = get_post( $attachment );
        } elseif ( is_array( $attachment ) ) {
@@ -2663,11 +2755,17 @@ function get_attachment_taxonomies( $attachment ) {
        }
 
        $taxonomies = array();
-       foreach ( $objects as $object )
-               if ( $taxes = get_object_taxonomies($object) )
-                       $taxonomies = array_merge($taxonomies, $taxes);
+       foreach ( $objects as $object ) {
+               if ( $taxes = get_object_taxonomies( $object, $output ) ) {
+                       $taxonomies = array_merge( $taxonomies, $taxes );
+               }
+       }
+
+       if ( 'names' === $output ) {
+               $taxonomies = array_unique( $taxonomies );
+       }
 
-       return array_unique($taxonomies);
+       return $taxonomies;
 }
 
 /**
@@ -2742,26 +2840,6 @@ function wp_expand_dimensions( $example_width, $example_height, $max_width, $max
        return wp_constrain_dimensions( $example_width * 1000000, $example_height * 1000000, $max_width, $max_height );
 }
 
-/**
- * Converts a shorthand byte value to an integer byte value.
- *
- * @since 2.3.0
- *
- * @param string $size A shorthand byte value.
- * @return int An integer byte value.
- */
-function wp_convert_hr_to_bytes( $size ) {
-       $size  = strtolower( $size );
-       $bytes = (int) $size;
-       if ( strpos( $size, 'k' ) !== false )
-               $bytes = intval( $size ) * KB_IN_BYTES;
-       elseif ( strpos( $size, 'm' ) !== false )
-               $bytes = intval($size) * MB_IN_BYTES;
-       elseif ( strpos( $size, 'g' ) !== false )
-               $bytes = intval( $size ) * GB_IN_BYTES;
-       return $bytes;
-}
-
 /**
  * Determines the maximum upload size allowed in php.ini.
  *
@@ -2774,7 +2852,7 @@ function wp_max_upload_size() {
        $p_bytes = wp_convert_hr_to_bytes( ini_get( 'post_max_size' ) );
 
        /**
-        * Filter the maximum upload size allowed in php.ini.
+        * Filters the maximum upload size allowed in php.ini.
         *
         * @since 2.5.0
         *
@@ -2850,9 +2928,8 @@ function _wp_image_editor_choose( $args = array() ) {
        require_once ABSPATH . WPINC . '/class-wp-image-editor.php';
        require_once ABSPATH . WPINC . '/class-wp-image-editor-gd.php';
        require_once ABSPATH . WPINC . '/class-wp-image-editor-imagick.php';
-
        /**
-        * Filter the list of image editing library classes.
+        * Filters the list of image editing library classes.
         *
         * @since 3.5.0
         *
@@ -2884,7 +2961,7 @@ function _wp_image_editor_choose( $args = array() ) {
 }
 
 /**
- * Prints default plupload arguments.
+ * Prints default Plupload arguments.
  *
  * @since 3.4.0
  */
@@ -2896,6 +2973,11 @@ function wp_plupload_default_settings() {
                return;
 
        $max_upload_size = wp_max_upload_size();
+       $allowed_extensions = array_keys( get_allowed_mime_types() );
+       $extensions = array();
+       foreach ( $allowed_extensions as $extension ) {
+               $extensions = array_merge( $extensions, explode( '|', $extension ) );
+       }
 
        $defaults = array(
                'runtimes'            => 'html5,flash,silverlight,html4',
@@ -2905,6 +2987,7 @@ function wp_plupload_default_settings() {
                'silverlight_xap_url' => includes_url( 'js/plupload/plupload.silverlight.xap' ),
                'filters' => array(
                        'max_file_size'   => $max_upload_size . 'b',
+                       'mime_types'      => array( array( 'extensions' => implode( ',', $extensions ) ) ),
                ),
        );
 
@@ -2917,7 +3000,7 @@ function wp_plupload_default_settings() {
        }
 
        /**
-        * Filter the Plupload default settings.
+        * Filters the Plupload default settings.
         *
         * @since 3.4.0
         *
@@ -2930,7 +3013,7 @@ function wp_plupload_default_settings() {
        );
 
        /**
-        * Filter the Plupload default parameters.
+        * Filters the Plupload default parameters.
         *
         * @since 3.4.0
         *
@@ -2980,6 +3063,7 @@ function wp_prepare_attachment_for_js( $attachment ) {
                list( $type, $subtype ) = array( $attachment->post_mime_type, '' );
 
        $attachment_url = wp_get_attachment_url( $attachment->ID );
+       $base_url = str_replace( wp_basename( $attachment_url ), '', $attachment_url );
 
        $response = array(
                'id'          => $attachment->ID,
@@ -3001,7 +3085,7 @@ function wp_prepare_attachment_for_js( $attachment ) {
                'type'        => $type,
                'subtype'     => $subtype,
                'icon'        => wp_mime_type_icon( $attachment->ID ),
-               'dateFormatted' => mysql2date( get_option('date_format'), $attachment->post_date ),
+               'dateFormatted' => mysql2date( __( 'F j, Y' ), $attachment->post_date ),
                'nonces'      => array(
                        'update' => false,
                        'delete' => false,
@@ -3022,10 +3106,14 @@ function wp_prepare_attachment_for_js( $attachment ) {
 
        if ( $post_parent ) {
                $parent_type = get_post_type_object( $post_parent->post_type );
+
                if ( $parent_type && $parent_type->show_ui && current_user_can( 'edit_post', $attachment->post_parent ) ) {
                        $response['uploadedToLink'] = get_edit_post_link( $attachment->post_parent, 'raw' );
                }
-               $response['uploadedToTitle'] = $post_parent->post_title ? $post_parent->post_title : __( '(no title)' );
+
+               if ( $parent_type && current_user_can( 'read_post', $attachment->post_parent ) ) {
+                       $response['uploadedToTitle'] = $post_parent->post_title ? $post_parent->post_title : __( '(no title)' );
+               }
        }
 
        $attached_file = get_attached_file( $attachment->ID );
@@ -3052,7 +3140,7 @@ function wp_prepare_attachment_for_js( $attachment ) {
        if ( current_user_can( 'delete_post', $attachment->ID ) )
                $response['nonces']['delete'] = wp_create_nonce( 'delete-post_' . $attachment->ID );
 
-       if ( $meta && 'image' === $type ) {
+       if ( $meta && ( 'image' === $type || ! empty( $meta['sizes'] ) ) ) {
                $sizes = array();
 
                /** This filter is documented in wp-admin/includes/media.php */
@@ -3072,8 +3160,10 @@ function wp_prepare_attachment_for_js( $attachment ) {
 
                        /** This filter is documented in wp-includes/media.php */
                        if ( $downsize = apply_filters( 'image_downsize', false, $attachment->ID, $size ) ) {
-                               if ( ! $downsize[3] )
+                               if ( empty( $downsize[3] ) ) {
                                        continue;
+                               }
+
                                $sizes[ $size ] = array(
                                        'height'      => $downsize[2],
                                        'width'       => $downsize[1],
@@ -3081,9 +3171,6 @@ function wp_prepare_attachment_for_js( $attachment ) {
                                        'orientation' => $downsize[2] > $downsize[1] ? 'portrait' : 'landscape',
                                );
                        } elseif ( isset( $meta['sizes'][ $size ] ) ) {
-                               if ( ! isset( $base_url ) )
-                                       $base_url = str_replace( wp_basename( $attachment_url ), '', $attachment_url );
-
                                // Nothing from the filter, so consult image metadata if we have it.
                                $size_meta = $meta['sizes'][ $size ];
 
@@ -3100,16 +3187,29 @@ function wp_prepare_attachment_for_js( $attachment ) {
                        }
                }
 
-               $sizes['full'] = array( 'url' => $attachment_url );
+               if ( 'image' === $type ) {
+                       $sizes['full'] = array( 'url' => $attachment_url );
+
+                       if ( isset( $meta['height'], $meta['width'] ) ) {
+                               $sizes['full']['height'] = $meta['height'];
+                               $sizes['full']['width'] = $meta['width'];
+                               $sizes['full']['orientation'] = $meta['height'] > $meta['width'] ? 'portrait' : 'landscape';
+                       }
 
-               if ( isset( $meta['height'], $meta['width'] ) ) {
-                       $sizes['full']['height'] = $meta['height'];
-                       $sizes['full']['width'] = $meta['width'];
-                       $sizes['full']['orientation'] = $meta['height'] > $meta['width'] ? 'portrait' : 'landscape';
+                       $response = array_merge( $response, $sizes['full'] );
+               } elseif ( $meta['sizes']['full']['file'] ) {
+                       $sizes['full'] = array(
+                               'url'         => $base_url . $meta['sizes']['full']['file'],
+                               'height'      => $meta['sizes']['full']['height'],
+                               'width'       => $meta['sizes']['full']['width'],
+                               'orientation' => $meta['sizes']['full']['height'] > $meta['sizes']['full']['width'] ? 'portrait' : 'landscape'
+                       );
                }
 
-               $response = array_merge( $response, array( 'sizes' => $sizes ), $sizes['full'] );
-       } elseif ( $meta && 'video' === $type ) {
+               $response = array_merge( $response, array( 'sizes' => $sizes ) );
+       }
+
+       if ( $meta && 'video' === $type ) {
                if ( isset( $meta['width'] ) )
                        $response['width'] = (int) $meta['width'];
                if ( isset( $meta['height'] ) )
@@ -3148,7 +3248,7 @@ function wp_prepare_attachment_for_js( $attachment ) {
                $response['compat'] = get_compat_media_markup( $attachment->ID, array( 'in_modal' => true ) );
 
        /**
-        * Filter the attachment data prepared for JavaScript.
+        * Filters the attachment data prepared for JavaScript.
         *
         * @since 3.5.0
         *
@@ -3345,7 +3445,8 @@ function wp_enqueue_media( $args = array() ) {
                'filterByDate'           => __( 'Filter by date' ),
                'filterByType'           => __( 'Filter by type' ),
                'searchMediaLabel'       => __( 'Search Media' ),
-               'noMedia'                => __( 'No media attachments found.' ),
+               'searchMediaPlaceholder' => __( 'Search media items...' ), // placeholder (no ellipsis)
+               'noMedia'                => __( 'No media files found.' ),
 
                // Library Details
                'attachmentDetails'  => __( 'Attachment Details' ),
@@ -3419,7 +3520,7 @@ function wp_enqueue_media( $args = array() ) {
        );
 
        /**
-        * Filter the media view settings.
+        * Filters the media view settings.
         *
         * @since 3.5.0
         *
@@ -3429,7 +3530,7 @@ function wp_enqueue_media( $args = array() ) {
        $settings = apply_filters( 'media_view_settings', $settings, $post );
 
        /**
-        * Filter the media view strings.
+        * Filters the media view strings.
         *
         * @since 3.5.0
         *
@@ -3490,7 +3591,7 @@ function get_attached_media( $type, $post = 0 ) {
        );
 
        /**
-        * Filter arguments used to retrieve media attached to the given post.
+        * Filters arguments used to retrieve media attached to the given post.
         *
         * @since 3.6.0
         *
@@ -3503,7 +3604,7 @@ function get_attached_media( $type, $post = 0 ) {
        $children = get_children( $args );
 
        /**
-        * Filter the list of media attached to the given post.
+        * Filters the list of media attached to the given post.
         *
         * @since 3.6.0
         *
@@ -3527,7 +3628,7 @@ function get_media_embedded_in_content( $content, $types = null ) {
        $html = array();
 
        /**
-        * Filter the embedded media types that are allowed to be returned from the content blob.
+        * Filters the embedded media types that are allowed to be returned from the content blob.
         *
         * @since 4.2.0
         *
@@ -3597,7 +3698,7 @@ function get_post_galleries( $post, $html = true ) {
        }
 
        /**
-        * Filter the list of all found galleries in the given post.
+        * Filters the list of all found galleries in the given post.
         *
         * @since 3.6.0
         *
@@ -3621,7 +3722,7 @@ function get_post_gallery( $post = 0, $html = true ) {
        $gallery = reset( $galleries );
 
        /**
-        * Filter the first-found post gallery.
+        * Filters the first-found post gallery.
         *
         * @since 3.6.0
         *
@@ -3701,7 +3802,7 @@ function wp_maybe_generate_attachment_metadata( $attachment ) {
 function attachment_url_to_postid( $url ) {
        global $wpdb;
 
-       $dir = wp_upload_dir();
+       $dir = wp_get_upload_dir();
        $path = $url;
 
        $site_url = parse_url( $dir['url'] );
@@ -3723,7 +3824,7 @@ function attachment_url_to_postid( $url ) {
        $post_id = $wpdb->get_var( $sql );
 
        /**
-        * Filter an attachment id found by URL.
+        * Filters an attachment id found by URL.
         *
         * @since 4.2.0
         *
@@ -3738,12 +3839,10 @@ function attachment_url_to_postid( $url ) {
  *
  * @since 4.0.0
  *
- * @global string $wp_version
- *
  * @return array The relevant CSS file URLs.
  */
 function wpview_media_sandbox_styles() {
-       $version = 'ver=' . $GLOBALS['wp_version'];
+       $version = 'ver=' . get_bloginfo( 'version' );
        $mediaelement = includes_url( "js/mediaelement/mediaelementplayer.min.css?$version" );
        $wpmediaelement = includes_url( "js/mediaelement/wp-mediaelement.css?$version" );