-
-
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); | |
}; | |
} |
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.
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)