-
-
Save ydaniv/3033012 to your computer and use it in GitHub Desktop.
// polyfill window.getMatchedCSSRules() in FireFox 6+ | |
if ( typeof window.getMatchedCSSRules !== 'function' ) { | |
var ELEMENT_RE = /[\w-]+/g, | |
ID_RE = /#[\w-]+/g, | |
CLASS_RE = /\.[\w-]+/g, | |
ATTR_RE = /\[[^\]]+\]/g, | |
// :not() pseudo-class does not add to specificity, but its content does as if it was outside it | |
PSEUDO_CLASSES_RE = /\:(?!not)[\w-]+(\(.*\))?/g, | |
PSEUDO_ELEMENTS_RE = /\:\:?(after|before|first-letter|first-line|selection)/g; | |
// convert an array-like object to array | |
function toArray (list) { | |
return [].slice.call(list); | |
} | |
// handles extraction of `cssRules` as an `Array` from a stylesheet or something that behaves the same | |
function getSheetRules (stylesheet) { | |
var sheet_media = stylesheet.media && stylesheet.media.mediaText; | |
// if this sheet is disabled skip it | |
if ( stylesheet.disabled ) return []; | |
// if this sheet's media is specified and doesn't match the viewport then skip it | |
if ( sheet_media && sheet_media.length && ! window.matchMedia(sheet_media).matches ) return []; | |
// get the style rules of this sheet | |
return toArray(stylesheet.cssRules); | |
} | |
function _find (string, re) { | |
var matches = string.match(re); | |
return re ? re.length : 0; | |
} | |
// calculates the specificity of a given `selector` | |
function calculateScore (selector) { | |
var score = [0,0,0], | |
parts = selector.split(' '), | |
part, match; | |
//TODO: clean the ':not' part since the last ELEMENT_RE will pick it up | |
while ( part = parts.shift(), typeof part == 'string' ) { | |
// find all pseudo-elements | |
match = _find(part, PSEUDO_ELEMENTS_RE); | |
score[2] = match; | |
// and remove them | |
match && (part = part.replace(PSEUDO_ELEMENTS_RE, '')); | |
// find all pseudo-classes | |
match = _find(part, PSEUDO_CLASSES_RE); | |
score[1] = match; | |
// and remove them | |
match && (part = part.replace(PSEUDO_CLASSES_RE, '')); | |
// find all attributes | |
match = _find(part, ATTR_RE); | |
score[1] += match; | |
// and remove them | |
match && (part = part.replace(ATTR_RE, '')); | |
// find all IDs | |
match = _find(part, ID_RE); | |
score[0] = match; | |
// and remove them | |
match && (part = part.replace(ID_RE, '')); | |
// find all classes | |
match = _find(part, CLASS_RE); | |
score[1] += match; | |
// and remove them | |
match && (part = part.replace(CLASS_RE, '')); | |
// find all elements | |
score[2] += _find(part, ELEMENT_RE); | |
} | |
return parseInt(score.join(''), 10); | |
} | |
// returns the heights possible specificity score an element can get from a give rule's selectorText | |
function getSpecificityScore (element, selector_text) { | |
var selectors = selector_text.split(','), | |
selector, score, result = 0; | |
while ( selector = selectors.shift() ) { | |
if ( element.mozMatchesSelector(selector) ) { | |
score = calculateScore(selector); | |
result = score > result ? score : result; | |
} | |
} | |
return result; | |
} | |
function sortBySpecificity (element, rules) { | |
// comparing function that sorts CSSStyleRules according to specificity of their `selectorText` | |
function compareSpecificity (a, b) { | |
return getSpecificityScore(element, b.selectorText) - getSpecificityScore(element, a.selectorText); | |
} | |
return rules.sort(compareSpecificity); | |
} | |
//TODO: not supporting 2nd argument for selecting pseudo elements | |
//TODO: not supporting 3rd argument for checking author style sheets only | |
window.getMatchedCSSRules = function (element /*, pseudo, author_only*/) { | |
var style_sheets, sheet, sheet_media, | |
rules, rule, | |
result = []; | |
// get stylesheets and convert to a regular Array | |
style_sheets = toArray(window.document.styleSheets); | |
// assuming the browser hands us stylesheets in order of appearance | |
// we iterate them from the beginning to follow proper cascade order | |
while ( sheet = style_sheets.shift() ) { | |
// get the style rules of this sheet | |
rules = getSheetRules(sheet); | |
// loop the rules in order of appearance | |
while ( rule = rules.shift() ) { | |
// if this is an @import rule | |
if ( rule.styleSheet ) { | |
// insert the imported stylesheet's rules at the beginning of this stylesheet's rules | |
rules = getSheetRules(rule.styleSheet).concat(rules); | |
// and skip this rule | |
continue; | |
} | |
// if there's no stylesheet attribute BUT there IS a media attribute it's a media rule | |
else if ( rule.media ) { | |
// insert the contained rules of this media rule to the beginning of this stylesheet's rules | |
rules = getSheetRules(rule).concat(rules); | |
// and skip it | |
continue | |
} | |
//TODO: for now only polyfilling Gecko | |
// check if this element matches this rule's selector | |
if ( element.mozMatchesSelector(rule.selectorText) ) { | |
// push the rule to the results set | |
result.push(rule); | |
} | |
} | |
} | |
// sort according to specificity | |
return sortBySpecificity(element, result); | |
}; | |
} |
You're right! Fixed
You could have strings in the attributes like a[href="asd]"]. You see the square barackets inside the string? Your RE will match it. Also, I haven't tested it and I don't understand all that happens but it seems to me like your REs are greedy so a[atr1][atr2] will match from first [ to last ] and I don't know if this is desired.
In my Firefox I get a security error: Security Error: the operation is insecure
. This is triggered by https://gist.github.com/ydaniv/3033012/#file-mozgetmatchedcssrules-js-L23
I've added this polyfill as an external .js file loaded before bootstrap-tokenfield.js gets loaded, and everything seems to be working fine in FireFox, no errors at all. (firefox version 23.0.1, newest as of today)
Seems like Internet Explorer doesn't know element.mozMatchesSelector.
I'm getting the same a security error as ragulka
Wow, I haven't been around here for quite some time! Too bad Github doesn't send notifications for Gists.
@bobef you're probably right, I'm open to suggestions.
@ragulka I never got it, can you confirm if this is still happening and what OS and version?
@Tomas-M well obviously, moz* stands for Mozilla, also see the title:
Gecko only polyfill ...
I'm getting the security error using Firefox 28.0 on Windows 8.1. I think it may be related to these questions:
http://stackoverflow.com/questions/5323604/firefox-not-able-to-enumerate-document-stylesheets-cssrules
http://stackoverflow.com/questions/3211536/accessing-cross-domain-stylesheet-with-cssrules
None of my stylesheets are from another domain though.
@flangefrog can you verify there's not @import from a different domain in your stylesheets?
This can work in IE9+ as well. Somebody already forked it and implemented it it looks like: https://gist.github.com/ssafejava/6605832
cool!
I guess the webkit part is redundant since this is a polyfill to a webkit feature.
part.replace(ATTR_RE, '');
replace returns a new string: so needs to be 'part = part.replace(ATTR_RE, '');'
match (regExp) does not return no of matches on the regexp: need to use 'matches.length'