cssomtools
Version:
The 'jQuery-for-the-CSSOM', a library for working with CSS stylesheets and rules in the browser
236 lines (224 loc) • 5.94 kB
JavaScript
const hasRules = object => object && object.cssRules && object.cssRules.length
// Takes a string
// Returns a CSSStyleSheet object
export function parse(
string = ''
) {
const iframe = document.createElement('iframe')
document.head.appendChild(iframe)
const style = iframe.contentDocument.createElement('style')
style.textContent = string
iframe.contentDocument.head.appendChild(style)
const stylesheet = iframe.contentDocument.styleSheets[0]
document.head.removeChild(iframe)
return stylesheet
}
// Takes a CSSStyleSheet or CSSRule object, or an array containing them
// Returns the objects passed in after processing with callback function
export function process(
object = all(),
callback = rule => rule
) {
const readRules = list => {
if (hasRules(list)) {
[...list.cssRules].forEach(rule => {
callback(rule)
if (hasRules(rule)) {
readRules(rule)
}
})
}
}
if (Array.isArray(object)) {
object.map(child => process(child, callback))
} else {
readRules(object)
}
return object
}
// Takes a CSSStyleSheet, CSSRule object, an array containing them, or a string
// Returns a stylesheet
export function add(
object = parse(),
stylesheet = all().length && all()[all().length - 1] || parse(),
index = stylesheet.cssRules.length
) {
if (Array.isArray(object)) {
object.forEach(child => add(child, stylesheet, index))
} else if (hasRules(object)) {
[...object.cssRules].forEach(child => add(child, stylesheet, index))
} else if (object.cssText) {
stylesheet.insertRule(object.cssText, index)
} else if (typeof object === 'string'){
add(parse(object), stylesheet, index)
}
return stylesheet
}
// Takes a CSSStyleSheet or CSSRule object, or an array containing them
// Returns the original object, minus all CSSRule objects
export function remove(
object = parse()
) {
if (Array.isArray(object)) {
object.map(remove)
} else if (hasRules(object)) {
[...object.cssRules].map(remove)
} else if (object.parentRule) {
object.parentRule.deleteRule(
[...object.parentRule.cssRules].indexOf(object)
)
} else if (object.parentStyleSheet) {
object.parentStyleSheet.deleteRule(
[...object.parentStyleSheet.cssRules].indexOf(object)
)
}
return object
}
// Takes a CSSStyleSheet or CSSRule object, or an array containing them
// Returns a string
export function stringify(
object = all()
) {
const stringifyRule = rule => rule.cssText || ''
if (Array.isArray(object)) {
return object.map(stringify).join('\n')
} else if (hasRules(object)) {
return [...object.cssRules].map(stringifyRule).join('\n')
} else {
return stringifyRule(object)
}
}
// Returns an array of all CSS stylesheets whose cssRules you're allowed to access from document.styleSheets
export function all() {
return [...document.styleSheets].map(stylesheet => {
try { stylesheet.cssRules }
catch(error) { return null }
return stylesheet
}).filter(hasRules)
}
// Takes a CSSStyleSheet and a test function
// Returns an object with a cssRules property containing all matching CSSRule objects
export function filter(
stylesheet = parse(),
test = rule => rule
) {
const output = {cssRules: []}
process(
stylesheet,
rule => {
if (test(rule)) {
output.cssRules.push(rule)
}
}
)
return output
}
// Takes a string, and optionally an array of stylesheet objects
// Returns an object containing all rules with selectors containing the string
export function selector(
string = '',
option = false,
list = all()
) {
if (list.cssRules) {
list = [list]
}
return list.map(stylesheet =>
filter(
stylesheet,
rule => {
if (option) {
return rule.selectorText && rule.selectorText.trim() === string
} else {
return rule.selectorText && rule.selectorText.includes(string)
}
}
)
).filter(hasRules)
}
// Takes a string, and optionally an array of stylesheet objects
// Returns an object containing all rules with properties containing the string
export function property(
string = '',
option = false,
list = all()
) {
if (list.cssRules) {
list = [list]
}
return list.map(stylesheet =>
filter(
stylesheet,
rule => {
return rule.style && [...rule.style].some(property => {
if (option) {
return property.trim() === string
} else {
return property.includes(string)
}
})
}
)
).filter(hasRules)
}
// Takes a string, and optionally an array of stylesheet objects
// Returns an object containing all rules with values containing the string
export function value(
string = '',
option = false,
list = all()
) {
if (list.cssRules) {
list = [list]
}
return list.map(stylesheet =>
filter(
stylesheet,
rule => {
return rule.style && [...rule.style].some(prop => {
if (option) {
return rule.style.getPropertyValue(prop).trim() === string
} else {
return rule.style.getPropertyValue(prop).includes(string)
}
})
}
)
).filter(hasRules)
}
// Takes a string, and optionally an array of stylesheet objects
// Returns an object containing all media queries with condition text containing the string
export function query(
string = '',
option = false,
list = all()
) {
if (list.cssRules) {
list = [list]
}
return list.map(stylesheet =>
filter(
stylesheet,
rule => {
if (option) {
return rule.media && rule.media.mediaText.trim() === string
} else {
return rule.media && rule.media.mediaText.includes(string)
}
}
)
).filter(hasRules)
}
export default {
parse,
process,
add,
remove,
stringify,
all,
filter,
selector,
property,
value,
query
}