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.