wp/wp-includes/js/dist/vendor/wp-polyfill-object-fit.js
changeset 18 be944660c56a
equal deleted inserted replaced
17:34716fd837a4 18:be944660c56a
       
     1 /*----------------------------------------
       
     2  * objectFitPolyfill 2.3.5
       
     3  *
       
     4  * Made by Constance Chen
       
     5  * Released under the ISC license
       
     6  *
       
     7  * https://github.com/constancecchen/object-fit-polyfill
       
     8  *--------------------------------------*/
       
     9 
       
    10 (function() {
       
    11   'use strict';
       
    12 
       
    13   // if the page is being rendered on the server, don't continue
       
    14   if (typeof window === 'undefined') return;
       
    15 
       
    16   // Workaround for Edge 16-18, which only implemented object-fit for <img> tags
       
    17   var edgeMatch = window.navigator.userAgent.match(/Edge\/(\d{2})\./);
       
    18   var edgeVersion = edgeMatch ? parseInt(edgeMatch[1], 10) : null;
       
    19   var edgePartialSupport = edgeVersion
       
    20     ? edgeVersion >= 16 && edgeVersion <= 18
       
    21     : false;
       
    22 
       
    23   // If the browser does support object-fit, we don't need to continue
       
    24   var hasSupport = 'objectFit' in document.documentElement.style !== false;
       
    25   if (hasSupport && !edgePartialSupport) {
       
    26     window.objectFitPolyfill = function() {
       
    27       return false;
       
    28     };
       
    29     return;
       
    30   }
       
    31 
       
    32   /**
       
    33    * Check the container's parent element to make sure it will
       
    34    * correctly handle and clip absolutely positioned children
       
    35    *
       
    36    * @param {node} $container - parent element
       
    37    */
       
    38   var checkParentContainer = function($container) {
       
    39     var styles = window.getComputedStyle($container, null);
       
    40     var position = styles.getPropertyValue('position');
       
    41     var overflow = styles.getPropertyValue('overflow');
       
    42     var display = styles.getPropertyValue('display');
       
    43 
       
    44     if (!position || position === 'static') {
       
    45       $container.style.position = 'relative';
       
    46     }
       
    47     if (overflow !== 'hidden') {
       
    48       $container.style.overflow = 'hidden';
       
    49     }
       
    50     // Guesstimating that people want the parent to act like full width/height wrapper here.
       
    51     // Mostly attempts to target <picture> elements, which default to inline.
       
    52     if (!display || display === 'inline') {
       
    53       $container.style.display = 'block';
       
    54     }
       
    55     if ($container.clientHeight === 0) {
       
    56       $container.style.height = '100%';
       
    57     }
       
    58 
       
    59     // Add a CSS class hook, in case people need to override styles for any reason.
       
    60     if ($container.className.indexOf('object-fit-polyfill') === -1) {
       
    61       $container.className = $container.className + ' object-fit-polyfill';
       
    62     }
       
    63   };
       
    64 
       
    65   /**
       
    66    * Check for pre-set max-width/height, min-width/height,
       
    67    * positioning, or margins, which can mess up image calculations
       
    68    *
       
    69    * @param {node} $media - img/video element
       
    70    */
       
    71   var checkMediaProperties = function($media) {
       
    72     var styles = window.getComputedStyle($media, null);
       
    73     var constraints = {
       
    74       'max-width': 'none',
       
    75       'max-height': 'none',
       
    76       'min-width': '0px',
       
    77       'min-height': '0px',
       
    78       top: 'auto',
       
    79       right: 'auto',
       
    80       bottom: 'auto',
       
    81       left: 'auto',
       
    82       'margin-top': '0px',
       
    83       'margin-right': '0px',
       
    84       'margin-bottom': '0px',
       
    85       'margin-left': '0px',
       
    86     };
       
    87 
       
    88     for (var property in constraints) {
       
    89       var constraint = styles.getPropertyValue(property);
       
    90 
       
    91       if (constraint !== constraints[property]) {
       
    92         $media.style[property] = constraints[property];
       
    93       }
       
    94     }
       
    95   };
       
    96 
       
    97   /**
       
    98    * Calculate & set object-position
       
    99    *
       
   100    * @param {string} axis - either "x" or "y"
       
   101    * @param {node} $media - img or video element
       
   102    * @param {string} objectPosition - e.g. "50% 50%", "top left"
       
   103    */
       
   104   var setPosition = function(axis, $media, objectPosition) {
       
   105     var position, other, start, end, side;
       
   106     objectPosition = objectPosition.split(' ');
       
   107 
       
   108     if (objectPosition.length < 2) {
       
   109       objectPosition[1] = objectPosition[0];
       
   110     }
       
   111 
       
   112     /* istanbul ignore else */
       
   113     if (axis === 'x') {
       
   114       position = objectPosition[0];
       
   115       other = objectPosition[1];
       
   116       start = 'left';
       
   117       end = 'right';
       
   118       side = $media.clientWidth;
       
   119     } else if (axis === 'y') {
       
   120       position = objectPosition[1];
       
   121       other = objectPosition[0];
       
   122       start = 'top';
       
   123       end = 'bottom';
       
   124       side = $media.clientHeight;
       
   125     } else {
       
   126       return; // Neither x or y axis specified
       
   127     }
       
   128 
       
   129     if (position === start || other === start) {
       
   130       $media.style[start] = '0';
       
   131       return;
       
   132     }
       
   133 
       
   134     if (position === end || other === end) {
       
   135       $media.style[end] = '0';
       
   136       return;
       
   137     }
       
   138 
       
   139     if (position === 'center' || position === '50%') {
       
   140       $media.style[start] = '50%';
       
   141       $media.style['margin-' + start] = side / -2 + 'px';
       
   142       return;
       
   143     }
       
   144 
       
   145     // Percentage values (e.g., 30% 10%)
       
   146     if (position.indexOf('%') >= 0) {
       
   147       position = parseInt(position, 10);
       
   148 
       
   149       if (position < 50) {
       
   150         $media.style[start] = position + '%';
       
   151         $media.style['margin-' + start] = side * (position / -100) + 'px';
       
   152       } else {
       
   153         position = 100 - position;
       
   154         $media.style[end] = position + '%';
       
   155         $media.style['margin-' + end] = side * (position / -100) + 'px';
       
   156       }
       
   157 
       
   158       return;
       
   159     }
       
   160     // Length-based values (e.g. 10px / 10em)
       
   161     else {
       
   162       $media.style[start] = position;
       
   163     }
       
   164   };
       
   165 
       
   166   /**
       
   167    * Calculate & set object-fit
       
   168    *
       
   169    * @param {node} $media - img/video/picture element
       
   170    */
       
   171   var objectFit = function($media) {
       
   172     // IE 10- data polyfill
       
   173     var fit = $media.dataset
       
   174       ? $media.dataset.objectFit
       
   175       : $media.getAttribute('data-object-fit');
       
   176     var position = $media.dataset
       
   177       ? $media.dataset.objectPosition
       
   178       : $media.getAttribute('data-object-position');
       
   179 
       
   180     // Default fallbacks
       
   181     fit = fit || 'cover';
       
   182     position = position || '50% 50%';
       
   183 
       
   184     // If necessary, make the parent container work with absolutely positioned elements
       
   185     var $container = $media.parentNode;
       
   186     checkParentContainer($container);
       
   187 
       
   188     // Check for any pre-set CSS which could mess up image calculations
       
   189     checkMediaProperties($media);
       
   190 
       
   191     // Reset any pre-set width/height CSS and handle fit positioning
       
   192     $media.style.position = 'absolute';
       
   193     $media.style.width = 'auto';
       
   194     $media.style.height = 'auto';
       
   195 
       
   196     // `scale-down` chooses either `none` or `contain`, whichever is smaller
       
   197     if (fit === 'scale-down') {
       
   198       if (
       
   199         $media.clientWidth < $container.clientWidth &&
       
   200         $media.clientHeight < $container.clientHeight
       
   201       ) {
       
   202         fit = 'none';
       
   203       } else {
       
   204         fit = 'contain';
       
   205       }
       
   206     }
       
   207 
       
   208     // `none` (width/height auto) and `fill` (100%) and are straightforward
       
   209     if (fit === 'none') {
       
   210       setPosition('x', $media, position);
       
   211       setPosition('y', $media, position);
       
   212       return;
       
   213     }
       
   214 
       
   215     if (fit === 'fill') {
       
   216       $media.style.width = '100%';
       
   217       $media.style.height = '100%';
       
   218       setPosition('x', $media, position);
       
   219       setPosition('y', $media, position);
       
   220       return;
       
   221     }
       
   222 
       
   223     // `cover` and `contain` must figure out which side needs covering, and add CSS positioning & centering
       
   224     $media.style.height = '100%';
       
   225 
       
   226     if (
       
   227       (fit === 'cover' && $media.clientWidth > $container.clientWidth) ||
       
   228       (fit === 'contain' && $media.clientWidth < $container.clientWidth)
       
   229     ) {
       
   230       $media.style.top = '0';
       
   231       $media.style.marginTop = '0';
       
   232       setPosition('x', $media, position);
       
   233     } else {
       
   234       $media.style.width = '100%';
       
   235       $media.style.height = 'auto';
       
   236       $media.style.left = '0';
       
   237       $media.style.marginLeft = '0';
       
   238       setPosition('y', $media, position);
       
   239     }
       
   240   };
       
   241 
       
   242   /**
       
   243    * Initialize plugin
       
   244    *
       
   245    * @param {node} media - Optional specific DOM node(s) to be polyfilled
       
   246    */
       
   247   var objectFitPolyfill = function(media) {
       
   248     if (typeof media === 'undefined' || media instanceof Event) {
       
   249       // If left blank, or a default event, all media on the page will be polyfilled.
       
   250       media = document.querySelectorAll('[data-object-fit]');
       
   251     } else if (media && media.nodeName) {
       
   252       // If it's a single node, wrap it in an array so it works.
       
   253       media = [media];
       
   254     } else if (typeof media === 'object' && media.length && media[0].nodeName) {
       
   255       // If it's an array of DOM nodes (e.g. a jQuery selector), it's fine as-is.
       
   256       media = media;
       
   257     } else {
       
   258       // Otherwise, if it's invalid or an incorrect type, return false to let people know.
       
   259       return false;
       
   260     }
       
   261 
       
   262     for (var i = 0; i < media.length; i++) {
       
   263       if (!media[i].nodeName) continue;
       
   264 
       
   265       var mediaType = media[i].nodeName.toLowerCase();
       
   266 
       
   267       if (mediaType === 'img') {
       
   268         if (edgePartialSupport) continue; // Edge supports object-fit for images (but nothing else), so no need to polyfill
       
   269 
       
   270         if (media[i].complete) {
       
   271           objectFit(media[i]);
       
   272         } else {
       
   273           media[i].addEventListener('load', function() {
       
   274             objectFit(this);
       
   275           });
       
   276         }
       
   277       } else if (mediaType === 'video') {
       
   278         if (media[i].readyState > 0) {
       
   279           objectFit(media[i]);
       
   280         } else {
       
   281           media[i].addEventListener('loadedmetadata', function() {
       
   282             objectFit(this);
       
   283           });
       
   284         }
       
   285       } else {
       
   286         objectFit(media[i]);
       
   287       }
       
   288     }
       
   289 
       
   290     return true;
       
   291   };
       
   292 
       
   293   if (document.readyState === 'loading') {
       
   294     // Loading hasn't finished yet
       
   295     document.addEventListener('DOMContentLoaded', objectFitPolyfill);
       
   296   } else {
       
   297     // `DOMContentLoaded` has already fired
       
   298     objectFitPolyfill();
       
   299   }
       
   300 
       
   301   window.addEventListener('resize', objectFitPolyfill);
       
   302 
       
   303   window.objectFitPolyfill = objectFitPolyfill;
       
   304 })();