PageSourceSearch

https://cgodl.github.io/js/viewport-units-buggyfill.js

js cgodl.github.io collected 2026-10-03 08:46:27 UTC 14,472 bytes, 464 lines download raw bytes

1/*!
2 * viewport-units-buggyfill v0.6.0
3 * @web: https://github.com/rodneyrehm/viewport-units-buggyfill/
4 * @author: Rodney Rehm - http://rodneyrehm.de/en/
5 */
6
7(function (root, factory) {
8  'use strict';
9  if (typeof define === 'function' && define.amd) {
10    // AMD. Register as an anonymous module.
11    define([], factory);
12  } else if (typeof exports === 'object') {
13    // Node. Does not work with strict CommonJS, but
14    // only CommonJS-like enviroments that support module.exports,
15    // like Node.
16    module.exports = factory();
17  } else {
18    // Browser globals (root is window)
19    root.viewportUnitsBuggyfill = factory();
20  }
21}(this, function () {
22  'use strict';
23  /*global document, window, navigator, location, XMLHttpRequest, XDomainRequest, CustomEvent*/
24
25  var initialized = false;
26  var options;
27  var userAgent = window.navigator.userAgent;
28  var viewportUnitExpression = /([+-]?[0-9.]+)(vh|vw|vmin|vmax)/g;
29  var forEach = [].forEach;
30  var dimensions;
31  var declarations;
32  var styleNode;
33  var isBuggyIE = /MSIE [0-9]\./i.test(userAgent);
34  var isOldIE = /MSIE [0-8]\./i.test(userAgent);
35  var isOperaMini = userAgent.indexOf('Opera Mini') > -1;
36
37  var isMobileSafari = /(iPhone|iPod|iPad).+AppleWebKit/i.test(userAgent) && (function() {
38    // Regexp for iOS-version tested against the following userAgent strings:
39    // Example WebView UserAgents:
40    // * iOS Chrome on iOS8: "Mozilla/5.0 (iPad; CPU OS 8_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) CriOS/39.0.2171.50 Mobile/12B410 Safari/600.1.4"
41    // * iOS Facebook on iOS7: "Mozilla/5.0 (iPhone; CPU iPhone OS 7_1_1 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Mobile/11D201 [FBAN/FBIOS;FBAV/12.1.0.24.20; FBBV/3214247; FBDV/iPhone6,1;FBMD/iPhone; FBSN/iPhone OS;FBSV/7.1.1; FBSS/2; FBCR/AT&T;FBID/phone;FBLC/en_US;FBOP/5]"
42    // Example Safari UserAgents:
43    // * Safari iOS8: "Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.3 (KHTML, like Gecko) Version/8.0 Mobile/12A4345d Safari/600.1.4"
44    // * Safari iOS7: "Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A4449d Safari/9537.53"
45    var iOSversion = userAgent.match(/OS (\d)/);
46    // viewport units work fine in mobile Safari and webView on iOS 8+
47    return iOSversion && iOSversion.length>1 && parseInt(iOSversion[1]) < 10;
48  })();
49
50  var isBadStockAndroid = (function() {
51    // Android stock browser test derived from
52    // http://stackoverflow.com/questions/24926221/distinguish-android-chrome-from-stock-browser-stock-browsers-user-agent-contai
53    var isAndroid = userAgent.indexOf(' Android ') > -1;
54    if (!isAndroid) {
55      return false;
56    }
57
58    var isStockAndroid = userAgent.indexOf('Version/') > -1;
59    if (!isStockAndroid) {
60      return false;
61    }
62
63    var versionNumber = parseFloat((userAgent.match('Android ([0-9.]+)') || [])[1]);
64    // anything below 4.4 uses WebKit without *any* viewport support,
65    // 4.4 has issues with viewport units within calc()
66    return versionNumber <= 4.4;
67  })();
68
69  // added check for IE10, IE11 and Edge < 20, since it *still* doesn't understand vmax
70  // http://caniuse.com/#feat=viewport-units
71  if (!isBuggyIE) {
72    isBuggyIE = !!navigator.userAgent.match(/MSIE 10\.|Trident.*rv[ :]*1[01]\.| Edge\/1\d\./);
73  }
74
75  // Polyfill for creating CustomEvents on IE9/10/11
76  // from https://github.com/krambuhl/custom-event-polyfill
77  try {
78    new CustomEvent('test');
79  } catch(e) {
80    var CustomEvent = function(event, params) {
81      var evt;
82      params = params || {
83        bubbles: false,
84        cancelable: false,
85        detail: undefined
86      };
87
88      evt = document.createEvent('CustomEvent');
89      evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
90      return evt;
91    };
92    CustomEvent.prototype = window.Event.prototype;
93    window.CustomEvent = CustomEvent; // expose definition to window
94  }
95
96  function debounce(func, wait) {
97    var timeout;
98    return function() {
99      var context = this;
100      var args = arguments;
101      var callback = function() {
102        func.apply(context, args);
103      };
104
105      clearTimeout(timeout);
106      timeout = setTimeout(callback, wait);
107    };
108  }
109
110  // from http://stackoverflow.com/questions/326069/how-to-identify-if-a-webpage-is-being-loaded-inside-an-iframe-or-directly-into-t
111  function inIframe() {
112    try {
113      return window.self !== window.top;
114    } catch (e) {
115      return true;
116    }
117  }
118
119  function initialize(initOptions) {
120    if (initialized) {
121      return;
122    }
123
124    if (initOptions === true) {
125      initOptions = {
126        force: true
127      };
128    }
129
130    options = initOptions || {};
131    options.isMobileSafari = isMobileSafari;
132    options.isBadStockAndroid = isBadStockAndroid;
133
134    if (options.ignoreVmax && !options.force && !isOldIE) {
135      // modern IE (10 and up) do not support vmin/vmax,
136      // but chances are this unit is not even used, so
137      // allow overwriting the "hacktivation"
138      // https://github.com/rodneyrehm/viewport-units-buggyfill/issues/56
139      isBuggyIE = false;
140    }
141
142    if (isOldIE || (!options.force && !isMobileSafari && !isBuggyIE && !isBadStockAndroid && !isOperaMini && (!options.hacks || !options.hacks.required(options)))) {
143      // this buggyfill only applies to mobile safari, IE9-10 and the Stock Android Browser.
144      if (window.console && isOldIE) {
145        console.info('viewport-units-buggyfill requires a proper CSSOM and basic viewport unit support, which are not available in IE8 and below');
146      }
147
148      return {
149        init: function () {}
150      };
151    }
152
153    // fire a custom event that buggyfill was initialize
154    window.dispatchEvent(new CustomEvent('viewport-units-buggyfill-init'));
155
156    options.hacks && options.hacks.initialize(options);
157
158    initialized = true;
159    styleNode = document.createElement('style');
160    styleNode.id = 'patched-viewport';
161    document.head.appendChild(styleNode);
162
163    // Issue #6: Cross Origin Stylesheets are not accessible through CSSOM,
164    // therefore download and inject them as <style> to circumvent SOP.
165    importCrossOriginLinks(function() {
166      var _refresh = debounce(refresh, options.refreshDebounceWait || 100);
167      // doing a full refresh rather than updateStyles because an orientationchange
168      // could activate different stylesheets
169      window.addEventListener('orientationchange', _refresh, true);
170      // orientationchange might have happened while in a different window
171      window.addEventListener('pageshow', _refresh, true);
172
173      if (options.force || isBuggyIE || inIframe()) {
174        window.addEventListener('resize', _refresh, true);
175        options._listeningToResize = true;
176      }
177
178      options.hacks && options.hacks.initializeEvents(options, refresh, _refresh);
179
180      refresh();
181    });
182  }
183
184  function updateStyles() {
185    styleNode.textContent = getReplacedViewportUnits();
186    // move to the end in case inline <style>s were added dynamically
187    styleNode.parentNode.appendChild(styleNode);
188    // fire a custom event that styles were updated
189    window.dispatchEvent(new CustomEvent('viewport-units-buggyfill-style'));
190  }
191
192  function refresh() {
193    if (!initialized) {
194      return;
195    }
196
197    findProperties();
198
199    // iOS Safari will report window.innerWidth and .innerHeight as 0 unless a timeout is used here.
200    // TODO: figure out WHY innerWidth === 0
201    setTimeout(function() {
202      updateStyles();
203    }, 1);
204  }
205  
206  // http://stackoverflow.com/a/23613052
207  function processStylesheet(ss) {
208    // cssRules respects same-origin policy, as per
209    // https://code.google.com/p/chromium/issues/detail?id=49001#c10.
210    try {
211      if (!ss.cssRules) { return; }
212    } catch(e) {
213      if (e.name !== 'SecurityError') { throw e; }
214      return;
215    }
216    // ss.cssRules is available, so proceed with desired operations.
217    var rules = [];
218    for (var i = 0; i < ss.cssRules.length; i++) {
219      var rule = ss.cssRules[i];
220      rules.push(rule);
221    }
222    return rules;
223  }
224
225  function findProperties() {
226    declarations = [];
227    forEach.call(document.styleSheets, function(sheet) {
228      var cssRules = processStylesheet(sheet);
229
230      if (!cssRules || sheet.ownerNode.id === 'patched-viewport' || sheet.ownerNode.getAttribute('data-viewport-units-buggyfill') === 'ignore') {
231        // skip entire sheet because no rules are present, it's supposed to be ignored or it's the target-element of the buggyfill
232        return;
233      }
234
235      if (sheet.media && sheet.media.mediaText && window.matchMedia && !window.matchMedia(sheet.media.mediaText).matches) {
236        // skip entire sheet because media attribute doesn't match
237        return;
238      }
239
240      forEach.call(cssRules, findDeclarations);
241    });
242
243    return declarations;
244  }
245
246  function findDeclarations(rule) {
247    if (rule.type === 7) {
248      var value;
249
250      // there may be a case where accessing cssText throws an error.
251      // I could not reproduce this issue, but the worst that can happen
252      // this way is an animation not running properly.
253      // not awesome, but probably better than a script error
254      // see https://github.com/rodneyrehm/viewport-units-buggyfill/issues/21
255      try {
256        value = rule.cssText;
257      } catch(e) {
258        return;
259      }
260
261      viewportUnitExpression.lastIndex = 0;
262      if (viewportUnitExpression.test(value)) {
263        // KeyframesRule does not have a CSS-PropertyName
264        declarations.push([rule, null, value]);
265        options.hacks && options.hacks.findDeclarations(declarations, rule, null, value);
266      }
267
268      return;
269    }
270
271    if (!rule.style) {
272      if (!rule.cssRules) {
273        return;
274      }
275
276      forEach.call(rule.cssRules, function(_rule) {
277        findDeclarations(_rule);
278      });
279
280      return;
281    }
282
283    forEach.call(rule.style, function(name) {
284      var value = rule.style.getPropertyValue(name);
285      // preserve those !important rules
286      if (rule.style.getPropertyPriority(name)) {
287        value += ' !important';
288      }
289
290      viewportUnitExpression.lastIndex = 0;
291      if (viewportUnitExpression.test(value)) {
292        declarations.push([rule, name, value]);
293        options.hacks && options.hacks.findDeclarations(declarations, rule, name, value);
294      }
295    });
296  }
297
298  function getReplacedViewportUnits() {
299    dimensions = getViewport();
300
301    var css = [];
302    var buffer = [];
303    var open;
304    var close;
305
306    declarations.forEach(function(item) {
307      var _item = overwriteDeclaration.apply(null, item);
308      var _open = _item.selector.length ? (_item.selector.join(' {\n') + ' {\n') : '';
309      var _close = new Array(_item.selector.length + 1).join('\n}');
310
311      if (!_open || _open !== open) {
312        if (buffer.length) {
313          css.push(open + buffer.join('\n') + close);
314          buffer.length = 0;
315        }
316
317        if (_open) {
318          open = _open;
319          close = _close;
320          buffer.push(_item.content);
321        } else {
322          css.push(_item.content);
323          open = null;
324          close = null;
325        }
326
327        return;
328      }
329
330      if (_open && !open) {
331        open = _open;
332        close = _close;
333      }
334
335      buffer.push(_item.content);
336    });
337
338    if (buffer.length) {
339      css.push(open + buffer.join('\n') + close);
340    }
341
342    // Opera Mini messes up on the content hack (it replaces the DOM node's innerHTML with the value).
343    // This fixes it. We test for Opera Mini only since it is the most expensive CSS selector
344    // see https://developer.mozilla.org/en-US/docs/Web/CSS/Universal_selectors
345    if (isOperaMini) {
346      css.push('* { content: normal !important; }');
347    }
348
349    return css.join('\n\n');
350  }
351
352  function overwriteDeclaration(rule, name, value) {
353    var _value;
354    var _selectors = [];
355
356    _value = value.replace(viewportUnitExpression, replaceValues);
357
358    if (options.hacks) {
359      _value = options.hacks.overwriteDeclaration(rule, name, _value);
360    }
361
362    if (name) {
363      // skipping KeyframesRule
364      _selectors.push(rule.selectorText);
365      _value = name + ': ' + _value + ';';
366    }
367
368    var _rule = rule.parentRule;
369    while (_rule) {
370      _selectors.unshift('@media ' + _rule.media.mediaText);
371      _rule = _rule.parentRule;
372    }
373
374    return {
375      selector: _selectors,
376      content: _value
377    };
378  }
379
380  function replaceValues(match, number, unit) {
381    var _base = dimensions[unit];
382    var _number = parseFloat(number) / 100;
383    return (_number * _base) + 'px';
384  }
385
386  function getViewport() {
387    var vh = window.innerHeight;
388    var vw = window.innerWidth;
389
390    return {
391      vh: vh,
392      vw: vw,
393      vmax: Math.max(vw, vh),
394      vmin: Math.min(vw, vh)
395    };
396  }
397
398  function importCrossOriginLinks(next) {
399    var _waiting = 0;
400    var decrease = function() {
401      _waiting--;
402      if (!_waiting) {
403        next();
404      }
405    };
406
407    forEach.call(document.styleSheets, function(sheet) {
408      if (!sheet.href || origin(sheet.href) === origin(location.href) || sheet.ownerNode.getAttribute('data-viewport-units-buggyfill') === 'ignore') {
409        // skip <style> and <link> from same origin or explicitly declared to ignore
410        return;
411      }
412
413      _waiting++;
414      convertLinkToStyle(sheet.ownerNode, decrease);
415    });
416
417    if (!_waiting) {
418      next();
419    }
420  }
421
422  function origin(url) {
423    return url.slice(0, url.indexOf('/', url.indexOf('://') + 3));
424  }
425
426  function convertLinkToStyle(link, next) {
427    getCors(link.href, function() {
428      var style = document.createElement('style');
429      style.media = link.media;
430      style.setAttribute('data-href', link.href);
431      style.textContent = this.responseText;
432      link.parentNode.replaceChild(style, link);
433      next();
434    }, next);
435  }
436
437  function getCors(url, success, error) {
438    var xhr = new XMLHttpRequest();
439    if ('withCredentials' in xhr) {
440      // XHR for Chrome/Firefox/Opera/Safari.
441      xhr.open('GET', url, true);
442    } else if (typeof XDomainRequest !== 'undefined') {
443      // XDomainRequest for IE.
444      xhr = new XDomainRequest();
445      xhr.open('GET', url);
446    } else {
447      throw new Error('cross-domain XHR not supported');
448    }
449
450    xhr.onload = success;
451    xhr.onerror = error;
452    xhr.send();
453    return xhr;
454  }
455
456  return {
457    version: '0.6.0',
458    findProperties: findProperties,
459    getCss: getReplacedViewportUnits,
460    init: initialize,
461    refresh: refresh
462  };
463
464}));

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.