/** * vein.js - version 0.3 * * by Danny Povolotski (dannypovolotski@gmail.com) **/ !function (name, definition) { if (typeof module != 'undefined') module.exports = definition() else if (typeof define == 'function' && define.amd) define(name, definition) else this[name] = definition() }('vein', function () { var vein = function(){}; // Kudos to: http://youmightnotneedjquery.com/ var extend = function(out) { out = out || {}; for (var i = 1; i < arguments.length; i++) { if (!arguments[i]) continue; for (var key in arguments[i]) { if (arguments[i].hasOwnProperty(key)) out[key] = arguments[i][key]; } } return out; }; var findOrDeleteBySelector = function(selector, stylesheet, css){ var matches = [], rulesDelete = [], rules = stylesheet[ document.all ? 'rules' : 'cssRules' ], selectorCompare = selector.replace(/\s/g,''), ri, rl; // Since there could theoretically be multiple versions of the same rule, // we will first iterate for(ri = 0, rl = rules.length; ri < rl; ri++) { if( // regular style selector (rules[ri].selectorText === selector) || // for media queries, remove spaces and see if the query matches (rules[ri].type === 4 && rules[ri].cssText.replace(/\s/g,'').substring(0, selectorCompare.length) == selectorCompare) ) { if(css === null) { // If we set css to null, let's delete that ruleset altogether rulesDelete.push(ri); } else { // Otherwise - we push it into the matches array matches.push(rules[ri]); } } } for (ri = 0, rl = rulesDelete.length; ri < rl; ri++) { stylesheet.deleteRule(rulesDelete[ri]); } return matches; }; var cssToString = function(css){ var cssArray = []; for(var property in css) { if (css.hasOwnProperty(property)) { cssArray.push(property + ': ' + css[property] + ';'); } } var cssText = cssArray.join(''); return cssText; }; // Get the stylesheet we use to inject stuff or create it if it doesn't exist yet vein.getStylesheet = function() { var self = this, si, sl; if(!self.element || !document.getElementById('vein')) { self.element = document.createElement("style"); self.element.setAttribute('type', 'text/css'); self.element.setAttribute('id', 'vein'); document.getElementsByTagName("head")[0].appendChild(self.element); self.stylesheet = self.element.sheet; } return self.stylesheet; }; var getRulesFromStylesheet = function(stylesheet){ return stylesheet[ document.all ? 'rules' : 'cssRules' ]; } var insertRule = function(selector, cssText, stylesheet){ var rules = getRulesFromStylesheet(stylesheet); if(stylesheet.insertRule) { // Supported by all modern browsers stylesheet.insertRule(selector + '{' + cssText + '}', rules.length); } else { // Old IE compatability stylesheet.addRule(selector, cssText, rules.length); } }; // Let's inject some CSS. We can supply an array (or string) of selectors, and an object // with CSS value and property pairs. vein.inject = function(selectors, css, options) { options = extend({}, options); var self = this, stylesheet = options.stylesheet || self.getStylesheet(), rules = getRulesFromStylesheet(stylesheet), si, sl, query, matches, cssText, property, mi, ml, qi, ql; if(typeof selectors === 'string') { selectors = [selectors]; } for(si = 0, sl = selectors.length; si < sl; si++) { if(typeof selectors[si] === 'object' && stylesheet.insertRule){ for(query in selectors[si]) { matches = findOrDeleteBySelector(query, stylesheet, css); if(matches.length === 0){ cssText = cssToString(css); for(qi = 0, ql = selectors[si][query].length; qi < ql; qi++) { insertRule(query, selectors[si][query][qi] + '{' + cssText + '}', stylesheet); } } else { for(mi = 0, ml = matches.length; mi < ml; mi++) { self.inject(selectors[si][query], css, {stylesheet: matches[mi]}); } } } } else { matches = findOrDeleteBySelector(selectors[si], stylesheet, css); // If all we wanted is to delete that ruleset, we're done here if(css === null) return; // If no rulesets have been found for the selector, we will create it below if(matches.length === 0) { cssText = cssToString(css); insertRule(selectors[si], cssText, stylesheet); } // Otherwise, we're just going to modify the property else { for(mi = 0, ml = matches.length; mi < ml; mi++) { for(property in css) { if (css.hasOwnProperty(property)) { // TODO: Implement priority if(matches[mi].style.setProperty) { matches[mi].style.setProperty(property, css[property], ''); } else { //IE8 matches[mi].style.setAttribute(property, css[property], ''); } } } } } } } return self; }; return vein; });