0% found this document useful (0 votes)
33 views15 pages

Function: Window

This document defines a JavaScript function that analyzes CSS stylesheets and style elements to add vendor prefixes. It contains the following key points: 1. It defines functions to process linked stylesheets, inline stylesheets and inline styles, and add prefixes where needed. 2. It can register additional prefixing functions. 3. It contains helper functions to camelCase, deCamelCase and replace prefixes in property names, values, and functions. 4. On page load and DOM content loaded, it will process all stylesheets and styles to add prefixes.

Uploaded by

adri781
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
33 views15 pages

Function: Window

This document defines a JavaScript function that analyzes CSS stylesheets and style elements to add vendor prefixes. It contains the following key points: 1. It defines functions to process linked stylesheets, inline stylesheets and inline styles, and add prefixes where needed. 2. It can register additional prefixing functions. 3. It contains helper functions to camelCase, deCamelCase and replace prefixes in property names, values, and functions. 4. On page load and DOM content loaded, it will process all stylesheets and styles to add prefixes.

Uploaded by

adri781
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 15

(function(){

if(!window.addEventListener) {
return;
}

var self = window.StyleFix = {


link: function(link) {
var url = link.href || link.getAttribute('data-
href');
try {
// Ignore stylesheets with data-noprefix
attribute as well as alternate stylesheets or without (data-)href
attribute
if(!url || link.rel !== 'stylesheet' ||
link.hasAttribute('data-noprefix')) {
return;
}
}
catch(e) {
return;
}

var base = url.replace(/[^\/]+$/, ''),


base_scheme = (/^[a-z]{3,10}:/.exec(base) ||
[''])[0],
base_domain = (/^[a-
z]{3,10}:\/\/[^\/]+/.exec(base) || [''])[0],
base_query = /^([^?]*)\??/.exec(url)[1],
parent = link.parentNode,
xhr = new XMLHttpRequest(),
process;

xhr.onreadystatechange = function() {
if(xhr.readyState === 4) {
process();
}
};

process = function() {
var css = xhr.responseText;
if(css && link.parentNode &&
(!xhr.status || xhr.status < 400 || xhr.status > 600)) {
css = self.fix(css, true, link);

// Convert relative URLs to


absolute, if needed
if(css && base) {
css =
css.replace(/url\(\s*?((?:"|')?)(.+?)\1\s*?\)/gi, function($0,
quote, url) {
if(/^([a-
z]{3,10}:|#)/i.test(url)) { // Absolute & or hash-relative
return $0;
}
else
if(/^\/\//.test(url)) { // Scheme-relative
// May
contain sequences like /../ and /./ but those DO work
return
'url("' + base_scheme + url + '")';
}
else
if(/^\//.test(url)) { // Domain-relative
return
'url("' + base_domain + url + '")';
}
else
if(/^\?/.test(url)) { // Query-relative
return
'url("' + base_query + url + '")';
}
else {
// Path-
relative
return
'url("' + base + url + '")';
}
});

// behavior URLs
shoudnt be converted (Issue #19)
// base should be
escaped before added to RegExp (Issue #81)
var escaped_base =
base.replace(/([\\\^\$*+[\]?{}.=!:(|)])/g,"\\$1");
css =
css.replace(RegExp('\\b(behavior:\\s*?url\\(\'?"?)' + escaped_base,
'gi'), '$1');
}

var style =
document.createElement('style');
style.textContent = '/*#
sourceURL='+link.getAttribute('href')+' */\n/*@
sourceURL='+link.getAttribute('href')+' */\n' + css;
style.media = link.media;
style.disabled = link.disabled;
style.setAttribute('data-href',
link.getAttribute('href'));

if(link.id) style.id = link.id;

parent.insertBefore(style,
link);
parent.removeChild(link);

style.media = link.media; //
Duplicate is intentional. See issue #31
}
};

try {
xhr.open('GET', url);
xhr.send(null);
} catch (e) {
// Fallback to XDomainRequest if available
if (typeof XDomainRequest != "undefined") {
xhr = new XDomainRequest();
xhr.onerror = xhr.onprogress =
function() {};
xhr.onload = process;
xhr.open("GET", url);
xhr.send(null);
}
}

link.setAttribute('data-inprogress', '');
},
styleElement: function(style) {
if (style.hasAttribute('data-noprefix')) {
return;
}
var disabled = style.disabled;

style.textContent = self.fix(style.textContent, true,


style);

style.disabled = disabled;
},

styleAttribute: function(element) {
var css = element.getAttribute('style');

css = self.fix(css, false, element);

element.setAttribute('style', css);
},

process: function() {
// Linked stylesheets
$('link[rel="stylesheet"]:not([data-
inprogress])').forEach(StyleFix.link);

// Inline stylesheets
$('style').forEach(StyleFix.styleElement);

// Inline styles
$('[style]').forEach(StyleFix.styleAttribute);
},

register: function(fixer, index) {


(self.fixers = self.fixers || [])
.splice(index === undefined?
self.fixers.length : index, 0, fixer);
},

fix: function(css, raw, element) {


if(self.fixers) {
for(var i=0; i<self.fixers.length; i++) {
css = self.fixers[i](css, raw, element) ||
css;
}
}

return css;
},

camelCase: function(str) {
return str.replace(/-([a-z])/g, function($0, $1) {
return $1.toUpperCase(); }).replace('-','');
},

deCamelCase: function(str) {
return str.replace(/[A-Z]/g, function($0) { return '-
' + $0.toLowerCase() });
}
};

/**************************************
* Process styles
**************************************/
(function(){
setTimeout(function(){
$('link[rel="stylesheet"]').forEach(StyleFix.link);
}, 10);

document.addEventListener('DOMContentLoaded',
StyleFix.process, false);
})();

function $(expr, con) {


return [].slice.call((con ||
document).querySelectorAll(expr));
}

})();

/**
* PrefixFree
*/
(function(root){
if(!window.StyleFix || !window.getComputedStyle) {
return;
}

// Private helper
function fix(what, before, after, replacement, css) {
what = self[what];

if(what.length) {
var regex = RegExp(before + '(' + what.join('|') +
')' + after, 'gi');

css = css.replace(regex, replacement);


}

return css;
}

var self = window.PrefixFree = {


prefixCSS: function(css, raw, element) {
var prefix = self.prefix;

// Gradient angles hotfix


if(self.functions.indexOf('linear-gradient') > -1) {
// Gradients are supported with a prefix,
convert angles to legacy
css = css.replace(/(\s|:|,)(repeating-
)?linear-gradient\(\s*(-?\d*\.?\d*)deg/ig, function ($0, delim,
repeating, deg) {
return delim + (repeating || '') +
'linear-gradient(' + (90-deg) + 'deg';
});
}

css = fix('functions', '(\\s|:|,)', '\\s*\\(', '$1' +


prefix + '$2(', css);
css = fix('keywords', '(\\s|:)', '(\\s|;|\\}|$)',
'$1' + prefix + '$2$3', css);
css = fix('properties', '(^|\\{|\\s|;)', '\\s*:',
'$1' + prefix + '$2:', css);

// Prefix properties *inside* values (issue #8)


if (self.properties.length) {
var regex = RegExp('\\b(' +
self.properties.join('|') + ')(?!:)', 'gi');

css = fix('valueProperties', '\\b', ':(.+?);',


function($0) {
return $0.replace(regex, prefix + "$1")
}, css);
}

if(raw) {
css = fix('selectors', '', '\\b',
self.prefixSelector, css);
css = fix('atrules', '@', '\\b', '@' + prefix
+ '$1', css);
}

// Fix double prefixing


css = css.replace(RegExp('-' + prefix, 'g'), '-');

// Prefix wildcard
css = css.replace(/-\*-(?=[a-z]+)/gi, self.prefix);

return css;
},

property: function(property) {
return (self.properties.indexOf(property) >=0 ?
self.prefix : '') + property;
},

value: function(value, property) {


value = fix('functions', '(^|\\s|,)', '\\s*\\(', '$1'
+ self.prefix + '$2(', value);
value = fix('keywords', '(^|\\s)', '(\\s|$)', '$1' +
self.prefix + '$2$3', value);

if(self.valueProperties.indexOf(property) >= 0) {
value = fix('properties', '(^|\\s|,)',
'($|\\s|,)', '$1'+self.prefix+'$2$3', value);
}

return value;
},

prefixSelector: function(selector) {
return self.selectorMap[selector] || selector
},

// Warning: Prefixes no matter what, even if the property is


supported prefix-less
prefixProperty: function(property, camelCase) {
var prefixed = self.prefix + property;

return camelCase? StyleFix.camelCase(prefixed) :


prefixed;
}
};

/**************************************
* Properties
**************************************/
(function() {
var prefixes = {},
properties = [],
shorthands = {},
style = getComputedStyle(document.documentElement,
null),
dummy = document.createElement('div').style;

// Why are we doing this instead of iterating over


properties in a .style object? Because Webkit.
// 1. Older Webkit won't iterate over those.
// 2. Recent Webkit will, but the 'Webkit'-prefixed
properties are not enumerable. The 'webkit'
// (lower case 'w') ones are, but they don't
`deCamelCase()` into a prefix that we can detect.
var iterate = function(property) {
if(property.charAt(0) === '-') {
properties.push(property);

var parts = property.split('-'),


prefix = parts[1];

// Count prefix uses


prefixes[prefix] = ++prefixes[prefix] || 1;

// This helps determining shorthands


while(parts.length > 3) {
parts.pop();

var shorthand = parts.join('-');

if(supported(shorthand) &&
properties.indexOf(shorthand) === -1) {
properties.push(shorthand);
}
}
}
},
supported = function(property) {
return StyleFix.camelCase(property) in dummy;
}

// Some browsers have numerical indices for the properties,


some don't
if(style && style.length > 0) {
for(var i=0; i<style.length; i++) {
iterate(style[i])
}
}
else {
for(var property in style) {
iterate(StyleFix.deCamelCase(property));
}
}

// Find most frequently used prefix


var highest = {uses:0};
for(var prefix in prefixes) {
var uses = prefixes[prefix];

if(highest.uses < uses) {


highest = {prefix: prefix, uses: uses};
}
}

self.prefix = '-' + highest.prefix + '-';


self.Prefix = StyleFix.camelCase(self.prefix);

self.properties = [];

// Get properties ONLY supported with a prefix


for(var i=0; i<properties.length; i++) {
var property = properties[i];

if(property.indexOf(self.prefix) === 0) { // we might


have multiple prefixes, like Opera
var unprefixed =
property.slice(self.prefix.length);

if(!supported(unprefixed)) {
self.properties.push(unprefixed);
}
}
}

// IE fix
if(self.Prefix == 'Ms'
&& !('transform' in dummy)
&& !('MsTransform' in dummy)
&& ('msTransform' in dummy)) {
self.properties.push('transform', 'transform-
origin');
}
self.properties.sort();
})();

/**************************************
* Values
**************************************/
(function() {
// Values that might need prefixing
var functions = {
'linear-gradient': {
property: 'backgroundImage',
params: 'red, teal'
},
'calc': {
property: 'width',
params: '1px + 5%'
},
'element': {
property: 'backgroundImage',
params: '#foo'
},
'cross-fade': {
property: 'backgroundImage',
params: 'url(a.png), url(b.png), 50%'
},
'image-set': {
property: 'backgroundImage',
params: 'url(a.png) 1x, url(b.png) 2x'
}
};

functions['repeating-linear-gradient'] =
functions['repeating-radial-gradient'] =
functions['radial-gradient'] =
functions['linear-gradient'];

// Note: The properties assigned are just to *test* support.


// The keywords will be prefixed everywhere.
var keywords = {
'initial': 'color',
'grab': 'cursor',
'grabbing': 'cursor',
'zoom-in': 'cursor',
'zoom-out': 'cursor',
'box': 'display',
'flexbox': 'display',
'inline-flexbox': 'display',
'flex': 'display',
'inline-flex': 'display',
'grid': 'display',
'inline-grid': 'display',
'max-content': 'width',
'min-content': 'width',
'fit-content': 'width',
'fill-available': 'width',
'contain-floats': 'width'
};

self.functions = [];
self.keywords = [];

var style = document.createElement('div').style;

function supported(value, property) {


style[property] = '';
style[property] = value;

return !!style[property];
}

for (var func in functions) {


var test = functions[func],
property = test.property,
value = func + '(' + test.params + ')';

if (!supported(value, property)
&& supported(self.prefix + value, property)) {
// It's supported, but with a prefix
self.functions.push(func);
}
}
for (var keyword in keywords) {
var property = keywords[keyword];

if (!supported(keyword, property)
&& supported(self.prefix + keyword, property)) {
// It's supported, but with a prefix
self.keywords.push(keyword);
}
}

})();

/**************************************
* Selectors and @-rules
**************************************/
(function() {

var
selectors = {
':any-link': null,
'::backdrop': null,
':fullscreen': null,
':full-screen': ':fullscreen',
//sigh
'::placeholder': null,
':placeholder': '::placeholder',
'::input-placeholder': '::placeholder',
':input-placeholder': '::placeholder',
':read-only': null,
':read-write': null,
'::selection': null
},

atrules = {
'keyframes': 'name',
'viewport': null,
'document': 'regexp(".")'
};

self.selectors = [];
self.selectorMap = {};
self.atrules = [];

var style = root.appendChild(document.createElement('style'));

function supported(selector) {
style.textContent = selector + '{}'; // Safari 4 has issues
with style.innerHTML

return !!style.sheet.cssRules.length;
}

for(var selector in selectors) {


var standard = selectors[selector] || selector
var prefixed = selector.replace(/::?/, function($0) { return
$0 + self.prefix })
if(!supported(standard) && supported(prefixed)) {
self.selectors.push(standard);
self.selectorMap[standard] = prefixed;
}
}

for(var atrule in atrules) {


var test = atrule + ' ' + (atrules[atrule] || '');

if(!supported('@' + test) && supported('@' + self.prefix +


test)) {
self.atrules.push(atrule);
}
}

root.removeChild(style);

})();

// Properties that accept properties as their value


self.valueProperties = [
'transition',
'transition-property',
'will-change'
]

// Add class for current prefix


root.className += ' ' + self.prefix;
StyleFix.register(self.prefixCSS);

})(document.documentElement);

You might also like