Форк Rambox
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

275 lines
9.7 KiB

/**
* Utility class for manipulating CSS rules
* @singleton
*/
Ext.define('Ext.util.CSS', function() {
var CSS,
rules = null,
doc = document,
camelRe = /(-[a-z])/gi,
camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
return {
singleton: true,
rules: rules,
initialized: false,
/**
* @private
*/
constructor: function() {
// Cache a reference to the singleton
CSS = this;
},
/**
* Creates a stylesheet from a text blob of rules.
* These rules will be wrapped in a STYLE tag and appended to the HEAD of the document.
* @param {String} cssText The text containing the css rules
* @param {String} id An id to add to the stylesheet for later removal
* @return {CSSStyleSheet}
*/
createStyleSheet: function (cssText, id) {
var ss,
head = doc.getElementsByTagName('head')[0],
styleEl = doc.createElement('style');
styleEl.setAttribute('type', 'text/css');
if (id) {
styleEl.setAttribute('id', id);
}
// Feature detect old IE
ss = styleEl.styleSheet;
if (ss) {
head.appendChild(styleEl);
ss.cssText = cssText;
} else {
styleEl.appendChild(doc.createTextNode(cssText));
head.appendChild(styleEl);
ss = styleEl.sheet;
}
CSS.cacheStyleSheet(ss);
return ss;
},
/**
* Removes a style or link tag by id
* @param {String} id The id of the tag
*/
removeStyleSheet : function(id) {
var existing = doc.getElementById(id);
if (existing) {
existing.parentNode.removeChild(existing);
}
},
/**
* Dynamically swaps an existing stylesheet reference for a new one
* @param {String} id The id of an existing link tag to remove
* @param {String} url The href of the new stylesheet to include
*/
swapStyleSheet : function(id, url) {
var ss;
CSS.removeStyleSheet(id);
ss = doc.createElement("link");
ss.setAttribute("rel", "stylesheet");
ss.setAttribute("type", "text/css");
ss.setAttribute("id", id);
ss.setAttribute("href", url);
doc.getElementsByTagName("head")[0].appendChild(ss);
},
// @private
cacheStyleSheet : function(ss) {
if (!rules) {
rules = CSS.rules = {};
}
try {// try catch for cross domain access issue
var ssRules = ss.cssRules || ss.rules,
i = ssRules.length - 1,
imports = ss.imports,
len = imports ? imports.length : 0,
rule, j;
// Old IE has a different way of handling imports
for (j = 0; j < len; ++j) {
CSS.cacheStyleSheet(imports[j]);
}
for (; i >= 0; --i) {
rule = ssRules[i];
// If it's an @import rule, import its stylesheet
if (rule.styleSheet) {
CSS.cacheStyleSheet(rule.styleSheet);
}
CSS.cacheRule(rule, ss);
}
} catch(e) {}
},
cacheRule: function(cssRule, styleSheet) {
// If it's an @import rule, import its stylesheet
if (cssRule.styleSheet) {
return CSS.cacheStyleSheet(cssRule.styleSheet);
}
var selectorText = cssRule.selectorText,
selectorCount, j;
if (selectorText) {
// Split in case there are multiple, comma-delimited selectors
selectorText = selectorText.split(',');
selectorCount = selectorText.length;
for (j = 0; j < selectorCount; j++) {
// IE<8 does not keep a reference to parentStyleSheet in the rule, so we
// must cache an object like this until IE<8 is deprecated.
rules[Ext.String.trim(selectorText[j]).toLowerCase()] = {
parentStyleSheet: styleSheet,
cssRule: cssRule
};
}
}
},
/**
* Gets all css rules for the document
* @param {Boolean} refreshCache true to refresh the internal cache
* @return {Object} An object (hash) of rules indexed by selector
*/
getRules : function(refreshCache) {
var result = {},
selector;
if (rules === null || refreshCache) {
CSS.refreshCache();
}
for (selector in rules) {
result[selector] = rules[selector].cssRule;
}
return result;
},
/**
* Refresh the rule cache if you have dynamically added stylesheets
* @return {Object} An object (hash) of rules indexed by selector
*/
refreshCache: function() {
var ds = doc.styleSheets,
i = 0,
len = ds.length;
rules = CSS.rules = {};
for (; i < len; i++) {
try {
if (!ds[i].disabled) {
CSS.cacheStyleSheet(ds[i]);
}
} catch(e) {}
}
},
/**
* Gets an an individual CSS rule by selector(s)
* @param {String/String[]} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
* @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
* @return {CSSStyleRule} The CSS rule or null if one is not found
*/
getRule: function(selector, refreshCache, rawCache) {
var i, result;
if (!rules || refreshCache) {
CSS.refreshCache();
}
if (!Ext.isArray(selector)) {
result = rules[selector.toLowerCase()];
if (result && !rawCache) {
result = result.cssRule;
}
return result || null;
}
for (i = 0; i < selector.length; i++) {
if (rules[selector[i]]) {
return rawCache ? rules[selector[i].toLowerCase()] : rules[selector[i].toLowerCase()].cssRule;
}
}
return null;
},
/**
* Creates a rule.
* @param {CSSStyleSheet} styleSheet The StyleSheet to create the rule in as returned from {@link #createStyleSheet}.
* @param {String} selector The selector to target the rule.
* @param {String} property The cssText specification eg `"color:red;font-weight:bold;text-decoration:underline"`
* @return {CSSStyleRule} The created rule
*/
createRule: function(styleSheet, selector, cssText) {
var result,
ruleSet = styleSheet.cssRules || styleSheet.rules,
index = ruleSet.length;
if (styleSheet.insertRule) {
styleSheet.insertRule(selector + ' {' + cssText + '}', index);
} else {
styleSheet.addRule(selector, cssText||' ');
}
CSS.cacheRule(result = ruleSet[index], styleSheet);
return result;
},
/**
* Updates a rule property
* @param {String/String[]} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
* @param {String} property The css property or a cssText specification eg `"color:red;font-weight:bold;text-decoration:underline"`
* @param {String} value The new value for the property
* @return {Boolean} true If a rule was found and updated
*/
updateRule : function(selector, property, value) {
var rule, i, styles;
if (!Ext.isArray(selector)) {
rule = CSS.getRule(selector);
if (rule) {
// 2 arg form means cssText sent, so parse it and update each style
if (arguments.length === 2) {
styles = Ext.Element.parseStyles(property);
for (property in styles) {
rule.style[property.replace(camelRe, camelFn)] = styles[property];
}
} else {
rule.style[property.replace(camelRe, camelFn)] = value;
}
return true;
}
} else {
for (i = 0; i < selector.length; i++) {
if (CSS.updateRule(selector[i], property, value)) {
return true;
}
}
}
return false;
},
deleteRule: function(selector) {
var rule = CSS.getRule(selector, false, true),
styleSheet, index;
if (rule) {
styleSheet = rule.parentStyleSheet;
index = Ext.Array.indexOf(styleSheet.cssRules || styleSheet.rules, rule.cssRule);
if (styleSheet.deleteRule) {
styleSheet.deleteRule(index);
} else {
styleSheet.removeRule(index);
}
delete rules[selector];
}
}
};
});