PageSourceSearch

https://usadf.gov/themes/custom/usadf/js/object-fit-videos.js?tgserb

js usadf.gov collected 2026-10-02 06:17:36 UTC 8,888 bytes, 266 lines download raw bytes

1/**
2 * Object Fit Videos
3 * Polyfill for object-fit and object-position CSS properties on video elements
4 * Covers IE9, IE10, IE11, Edge, Safari <10
5 *
6 * Usage
7 * In your CSS, add a special font-family tag for IE/Edge
8 * video {
9 *   object-fit: cover;
10 *   font-family: 'object-fit: cover;';
11 * }
12 *
13 * Before the closing body tag, or whenever the DOM is ready,
14 * make the JavaScript call
15 * objectFitVideos();
16 *
17 * All video elements with the special CSS property will be targeted
18 *
19 * @license  MIT (https://opensource.org/licenses/MIT)
20 * @author   Todd Miller <[email protected]>
21 * @version  1.0.2
22 * @changelog
23 * 2016-08-19 - Adds object-position support.
24 * 2016-08-19 - Add throttle function for more performant resize events
25 * 2016-08-19 - Initial release with object-fit support, and
26 *              object-position default 'center'
27 * 2016-10-14 - No longer relies on window load event, instead requires a specific
28 *              function call to initialize the videos for object fit and position.
29 * 2016-11-28 - Support CommonJS environment, courtesy of @msorensson
30 * 2016-12-05 - Refactors the throttling function to support IE
31 * 2017-09-26 - Fix an issue with autplay not working on polyfilled videos
32 *            - Adds the capability to specify elements to polyfill,
33 *              instead of just checking every video element for the
34 *              CSS property. Slight performance gain in most usecases,
35 *              and a bigger gain in a few usecases.
36 * 2017-10-24 - Add user agent check to enable polyfill for all Edge browsers.
37 *              object-fit is supported on Edge >= 16, but currently just for images.
38 */
39var objectFitVideos = function (videos) {
40  'use strict';
41
42  var isEdge = navigator.userAgent.indexOf('Edge/') >= 0;
43
44  var testImg                = new Image(),
45      supportsObjectFit      = 'object-fit' in testImg.style && !isEdge,
46      supportsObjectPosition = 'object-position' in testImg.style && !isEdge,
47      propRegex              = /(object-fit|object-position)\s*:\s*([-\w\s%]+)/g;
48
49  if (!supportsObjectFit || !supportsObjectPosition) {
50    initialize(videos);
51    throttle('resize', 'optimizedResize');
52  }
53
54  /**
55   * Parse the style and look for the special font-family tag
56   * @param  {object} $el The element to parse
57   * @return {object}     The font-family properties we're interested in
58   */
59  function getStyle ($el) {
60    var style  = getComputedStyle($el).fontFamily,
61        parsed = null,
62        props  = {};
63
64      while ((parsed = propRegex.exec(style)) !== null) {
65        props[parsed[1]] = parsed[2];
66      }
67
68      if (props['object-position'])
69        return parsePosition(props);
70
71      return props;
72  }
73
74  /**
75   * Initialize all the relevant video elements and get them fitted
76   */
77  function initialize (videos) {
78    var index = -1;
79
80    if (!videos) {
81      // if no videos given, query all video elements
82      videos = document.querySelectorAll('video');
83    } else if (!('length' in videos)) {
84      // convert to an array for proper looping if an array or NodeList
85      // was not given
86      videos = [videos];
87    }
88
89    while (videos[++index]) {
90      var style = getStyle(videos[index]);
91
92      // only do work if the property is on the element
93      if (style['object-fit'] || style['object-position']) {
94        // set the default values
95        style['object-fit'] = style['object-fit'] || 'fill';
96        fitIt(videos[index], style);
97      }
98    }
99  }
100
101  /**
102   * Object Fit
103   * @param  {object} $el Element to fit
104   * @return {object}     The element's relevant properties
105   */
106  function fitIt ($el, style) {
107    // fill is the default behavior, no action is necessary
108    if (style['object-fit'] === 'fill')
109      return;
110
111    // convenience style properties on the source element
112    var setCss = $el.style,
113        getCss = window.getComputedStyle($el);
114
115    // create and insert a wrapper element
116    var $wrap = document.createElement('object-fit');
117    $wrap.appendChild($el.parentNode.replaceChild($wrap, $el));
118
119    // style the wrapper element to mostly match the source element
120    var wrapCss = {
121      height:    '100%',
122      width:     '100%',
123      boxSizing: 'content-box',
124      display:   'inline-block',
125      overflow:  'hidden'
126    };
127
128    'backgroundColor backgroundImage borderColor borderStyle borderWidth bottom fontSize lineHeight left op
128acity margin position right top visibility'.replace(/\w+/g, function (key) {
129      wrapCss[key] = getCss[key];
130    });
131
132    for (var key in wrapCss)
133      $wrap.style[key] = wrapCss[key];
134
135    // give the source element some saner styles
136    setCss.border  = setCss.margin = setCss.padding = 0;
137    setCss.display = 'block';
138    setCss.opacity = 1;
139
140    // set up the event handlers
141    $el.addEventListener('loadedmetadata', doWork);
142    window.addEventListener('optimizedResize', doWork);
143
144    // we may have missed the loadedmetadata event, so if the video has loaded
145    // enough data, just drop the event listener and execute
146    if ($el.readyState >= 1) {
147      $el.removeEventListener('loadedmetadata', doWork);
148      doWork();
149    }
150
151    /**
152     * Do the actual sizing. Math.
153     * @methodOf fitIt
154     */
155    function doWork () {
156      // the actual size and ratio of the video
157      // we do this here, even though it doesn't change, because
158      // at this point we can be sure the metadata has loaded
159      var videoWidth  = $el.videoWidth,
160          videoHeight = $el.videoHeight,
161          videoRatio  = videoWidth / videoHeight;
162
163      var wrapWidth  = $wrap.clientWidth,
164          wrapHeight = $wrap.clientHeight,
165          wrapRatio  = wrapWidth / wrapHeight;
166
167      var newHeight = 0,
168          newWidth  = 0;
169      setCss.marginLeft = setCss.marginTop = 0;
170
171      // basically we do the opposite action for contain and cover,
172      // depending on whether the video aspect ratio is less than or
173      // greater than the wrapper's aspect ratio
174      if (videoRatio < wrapRatio ?
175          style['object-fit'] === 'contain' : style['object-fit'] === 'cover') {
176        newHeight = wrapHeight * videoRatio;
177        newWidth  = wrapWidth / videoRatio;
178
179        setCss.width  = Math.round(newHeight) + 'px';
180        setCss.height = wrapHeight + 'px';
181
182        if (style['object-position-x'] === 'left')
183          setCss.marginLeft = 0;
184        else if (style['object-position-x'] === 'right')
185          setCss.marginLeft = Math.round(wrapWidth - newHeight) + 'px';
186        else
187          setCss.marginLeft = Math.round((wrapWidth - newHeight) / 2) + 'px';
188      } else {
189        newWidth = wrapWidth / videoRatio;
190
191        setCss.width     = wrapWidth + 'px';
192        setCss.height    = Math.round(newWidth) + 'px';
193
194        if (style['object-position-y'] === 'top')
195          setCss.marginTop = 0;
196        else if (style['object-position-y'] === 'bottom')
197          setCss.marginTop = Math.round(wrapHeight - newWidth) + 'px';
198        else
199          setCss.marginTop = Math.round((wrapHeight - newWidth) / 2) + 'px';
200      }
201
202      // play the video if autoplay is set
203      if ($el.autoplay)
204        $el.play();
205    }
206  }
207
208  /**
209   * Split the object-position property into x and y position properties
210   * @param  {object} style Relevant element styles
211   * @return {object}       The style object with the added x and y props
212   */
213  function parsePosition (style) {
214    if (~style['object-position'].indexOf('left'))
215      style['object-position-x'] = 'left';
216    else if (~style['object-position'].indexOf('right'))
217      style['object-position-x'] = 'right';
218    else
219      style['object-position-x'] = 'center';
220
221    if (~style['object-position'].indexOf('top'))
222      style['object-position-y'] = 'top';
223    else if (~style['object-position'].indexOf('bottom'))
224      style['object-position-y'] = 'bottom';
225    else
226      style['object-position-y'] = 'center';
227
228    return style;
229  }
230
231  /**
232   * Throttle an event with RequestAnimationFrame API for better performance
233   * @param  {string} type The event to throttle
234   * @param  {string} name Custom event name to listen for
235   * @param  {object} obj  Optional object to attach the event to
236   */
237  function throttle (type, name, obj) {
238    obj = obj || window;
239    var running = false,
240        evt     = null;
241
242    // IE does not support the CustomEvent constructor
243    // so if that fails do it the old way
244    try {
245      evt = new CustomEvent(name);
246    } catch (e) {
247      evt = document.createEvent('Event');
248      evt.initEvent(name, true, true);
249    }
250
251    var func = function () {
252      if (running) return;
253
254      running = true;
255      requestAnimationFrame(function () {
256        obj.dispatchEvent(evt);
257        running = false;
258      });
259    };
260
261    obj.addEventListener(type, func);
262  }
263};
264
265if (typeof module !== 'undefined' && typeof module.exports !== 'undefined')
266  module.exports = objectFitVideos;

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.