balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

wp-polyfill-object-fit.js (9185B)


      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 })();