-/*
-Copyright (c) 2009, Yahoo! Inc. All rights reserved.
-Code licensed under the BSD License:
-http://developer.yahoo.net/yui/license.txt
-version: 2.7.0
-*/
-/**
- * The StyleSheet component is a utility for managing css rules at the
- * stylesheet level
- *
- * @module stylesheet
- * @namespace YAHOO.util
- * @requires yahoo
- * @beta
- */
-(function () {
-
-var d = document,
- p = d.createElement('p'), // Have to hold the node (see notes)
- workerStyle = p.style, // worker style collection
- lang = YAHOO.lang,
- selectors = {},
- sheets = {},
- ssId = 0,
- floatAttr = ('cssFloat' in workerStyle) ? 'cssFloat' : 'styleFloat',
- _toCssText,
- _unsetOpacity,
- _unsetProperty;
-
-/*
- * Normalizes the removal of an assigned style for opacity. IE uses the filter property.
- */
-_unsetOpacity = ('opacity' in workerStyle) ?
- function (style) { style.opacity = ''; } :
- function (style) { style.filter = ''; };
-
-/*
- * Normalizes the removal of an assigned style for a given property. Expands
- * shortcut properties if necessary and handles the various names for the float property.
- */
-workerStyle.border = "1px solid red";
-workerStyle.border = ''; // IE doesn't unset child properties
-_unsetProperty = workerStyle.borderLeft ?
- function (style,prop) {
- var p;
- if (prop !== floatAttr && prop.toLowerCase().indexOf('float') != -1) {
- prop = floatAttr;
- }
- if (typeof style[prop] === 'string') {
- switch (prop) {
- case 'opacity':
- case 'filter' : _unsetOpacity(style); break;
- case 'font' :
- style.font = style.fontStyle = style.fontVariant =
- style.fontWeight = style.fontSize = style.lineHeight =
- style.fontFamily = '';
- break;
- default :
- for (p in style) {
- if (p.indexOf(prop) === 0) {
- style[p] = '';
- }
- }
- }
- }
- } :
- function (style,prop) {
- if (prop !== floatAttr && prop.toLowerCase().indexOf('float') != -1) {
- prop = floatAttr;
- }
- if (lang.isString(style[prop])) {
- if (prop === 'opacity') {
- _unsetOpacity(style);
- } else {
- style[prop] = '';
- }
- }
- };
-
-/**
- * Create an instance of YAHOO.util.StyleSheet to encapsulate a css stylesheet.
- * The constructor can be called using function or constructor syntax.
- * <pre><code>var sheet = YAHOO.util.StyleSheet(..);</pre></code>
- * or
- * <pre><code>var sheet = new YAHOO.util.StyleSheet(..);</pre></code>
- *
- * The first parameter passed can be any of the following things:
- * <ul>
- * <li>The desired string name to register a new empty sheet</li>
- * <li>The string name of an existing YAHOO.util.StyleSheet instance</li>
- * <li>The unique yuiSSID generated for an existing YAHOO.util.StyleSheet instance</li>
- * <li>The id of an existing <code><link></code> or <code><style></code> node</li>
- * <li>The node reference for an existing <code><link></code> or <code><style></code> node</li>
- * <li>A chunk of css text to create a new stylesheet from</li>
- * </ul>
- *
- * <p>If a string is passed, StyleSheet will first look in its static name
- * registry for an existing sheet, then in the DOM for an element with that id.
- * If neither are found and the string contains the { character, it will be
- * used as a the initial cssText for a new StyleSheet. Otherwise, a new empty
- * StyleSheet is created, assigned the string value as a name, and registered
- * statically by that name.</p>
- *
- * <p>The optional second parameter is a string name to register the sheet as.
- * This param is largely useful when providing a node id/ref or chunk of css
- * text to create a populated instance.</p>
- *
- * @class StyleSheet
- * @constructor
- * @param seed {String|HTMLElement} a style or link node, its id, or a name or
- * yuiSSID of a StyleSheet, or a string of css text (see above)
- * @param name {String} OPTIONAL name to register instance for future static
- * access
- */
-function StyleSheet(seed, name) {
- var head,
- node,
- sheet,
- cssRules = {},
- _rules,
- _insertRule,
- _deleteRule,
- i,r,sel;
-
- // Factory or constructor
- if (!(this instanceof arguments.callee)) {
- return new arguments.callee(seed,name);
- }
-
- head = d.getElementsByTagName('head')[0];
- if (!head) {
- // TODO: do something. Preferably something smart
- throw new Error('HEAD element not found to append STYLE node');
- }
-
- // capture the DOM node if the string is an id
- node = seed && (seed.nodeName ? seed : d.getElementById(seed));
-
- // Check for the StyleSheet in the static registry
- if (seed && sheets[seed]) {
- return sheets[seed];
- } else if (node && node.yuiSSID && sheets[node.yuiSSID]) {
- return sheets[node.yuiSSID];
- }
-
- // Create a style node if necessary
- if (!node || !/^(?:style|link)$/i.test(node.nodeName)) {
- node = d.createElement('style');
- node.type = 'text/css';
- }
-
- if (lang.isString(seed)) {
- // Create entire sheet from seed cssText
- if (seed.indexOf('{') != -1) {
- // Not a load-time fork because low run-time impact and IE fails
- // test for s.styleSheet at page load time (oddly)
- if (node.styleSheet) {
- node.styleSheet.cssText = seed;
- } else {
- node.appendChild(d.createTextNode(seed));
- }
- } else if (!name) {
- name = seed;
- }
- }
-
- if (node.parentNode !== head) {
- // styleSheet isn't available on the style node in FF2 until appended
- // to the head element. style nodes appended to body do not affect
- // change in Safari.
- head.appendChild(node);
- }
-
- // Begin setting up private aliases to the important moving parts
- // 1. The stylesheet object
- // IE stores StyleSheet under the "styleSheet" property
- // Safari doesn't populate sheet for xdomain link elements
- sheet = node.sheet || node.styleSheet;
-
- // 2. The style rules collection
- // IE stores the rules collection under the "rules" property
- _rules = sheet && ('cssRules' in sheet) ? 'cssRules' : 'rules';
-
- // 3. Initialize the cssRules map from the node
- // xdomain link nodes forbid access to the cssRules collection, so this
- // will throw an error.
- // TODO: research alternate stylesheet, @media
- for (i = sheet[_rules].length - 1; i >= 0; --i) {
- r = sheet[_rules][i];
- sel = r.selectorText;
-
- if (cssRules[sel]) {
- cssRules[sel].style.cssText += ';' + r.style.cssText;
- _deleteRule(i);
- } else {
- cssRules[sel] = r;
- }
- }
-
- // 4. The method to remove a rule from the stylesheet
- // IE supports removeRule
- _deleteRule = ('deleteRule' in sheet) ?
- function (i) { sheet.deleteRule(i); } :
- function (i) { sheet.removeRule(i); };
-
- // 5. The method to add a new rule to the stylesheet
- // IE supports addRule with different signature
- _insertRule = ('insertRule' in sheet) ?
- function (sel,css,i) { sheet.insertRule(sel+' {'+css+'}',i); } :
- function (sel,css,i) { sheet.addRule(sel,css,i); };
-
- // Cache the instance by the generated Id
- node.yuiSSID = 'yui-stylesheet-' + (ssId++);
- StyleSheet.register(node.yuiSSID,this);
-
- // Register the instance by name if provided or defaulted from seed
- if (name) {
- StyleSheet.register(name,this);
- }
-
- // Public API
- lang.augmentObject(this,{
- /**
- * Get the unique yuiSSID for this StyleSheet instance
- *
- * @method getId
- * @return {Number} the static id
- */
- getId : function () { return node.yuiSSID; },
-
- /**
- * The HTMLElement that this instance encapsulates
- *
- * @property node
- * @type HTMLElement
- */
- node : node,
-
- /**
- * Enable all the rules in the sheet
- *
- * @method enable
- * @return {StyleSheet} the instance
- * @chainable
- */
- // Enabling/disabling the stylesheet. Changes may be made to rules
- // while disabled.
- enable : function () { sheet.disabled = false; return this; },
-
- /**
- * Disable all the rules in the sheet. Rules may be changed while the
- * StyleSheet is disabled.
- *
- * @method disable
- * @return {StyleSheet} the instance
- * @chainable
- */
- disable : function () { sheet.disabled = true; return this; },
-
- /**
- * Returns boolean indicating whether the StyleSheet is enabled
- *
- * @method isEnabled
- * @return {Boolean} is it enabled?
- */
- isEnabled : function () { return !sheet.disabled; },
-
- /**
- * <p>Set style properties for a provided selector string.
- * If the selector includes commas, it will be split into individual
- * selectors and applied accordingly. If the selector string does not
- * have a corresponding rule in the sheet, it will be added.</p>
- *
- * <p>The object properties in the second parameter must be the JavaScript
- * names of style properties. E.g. fontSize rather than font-size.</p>
- *
- * <p>The float style property will be set by any of "float",
- * "styleFloat", or "cssFloat".</p>
- *
- * @method set
- * @param sel {String} the selector string to apply the changes to
- * @param css {Object} Object literal of style properties and new values
- * @return {StyleSheet} the StyleSheet instance
- * @chainable
- */
- set : function (sel,css) {
- var rule = cssRules[sel],
- multi = sel.split(/\s*,\s*/),i,
- idx;
-
- // IE's addRule doesn't support multiple comma delimited selectors
- if (multi.length > 1) {
- for (i = multi.length - 1; i >= 0; --i) {
- this.set(multi[i], css);
- }
- return this;
- }
-
- // Some selector values can cause IE to hang
- if (!StyleSheet.isValidSelector(sel)) {
- return this;
- }
-
- // Opera throws an error if there's a syntax error in assigned
- // cssText. Avoid this using a worker style collection, then
- // assigning the resulting cssText.
- if (rule) {
- rule.style.cssText = StyleSheet.toCssText(css,rule.style.cssText);
- } else {
- idx = sheet[_rules].length;
- css = StyleSheet.toCssText(css);
-
- // IE throws an error when attempting to addRule(sel,'',n)
- // which would crop up if no, or only invalid values are used
- if (css) {
- _insertRule(sel, css, idx);
-
- // Safari replaces the rules collection, but maintains the
- // rule instances in the new collection when rules are
- // added/removed
- cssRules[sel] = sheet[_rules][idx];
- }
- }
- return this;
- },
-
- /**
- * <p>Unset style properties for a provided selector string, removing
- * their effect from the style cascade.</p>
- *
- * <p>If the selector includes commas, it will be split into individual
- * selectors and applied accordingly. If there are no properties
- * remaining in the rule after unsetting, the rule is removed.</p>
- *
- * <p>The style property or properties in the second parameter must be the
- * <p>JavaScript style property names. E.g. fontSize rather than font-size.</p>
- *
- * <p>The float style property will be unset by any of "float",
- * "styleFloat", or "cssFloat".</p>
- *
- * @method unset
- * @param sel {String} the selector string to apply the changes to
- * @param css {String|Array} style property name or Array of names
- * @return {StyleSheet} the StyleSheet instance
- * @chainable
- */
- unset : function (sel,css) {
- var rule = cssRules[sel],
- multi = sel.split(/\s*,\s*/),
- remove = !css,
- rules, i;
-
- // IE's addRule doesn't support multiple comma delimited selectors
- // so rules are mapped internally by atomic selectors
- if (multi.length > 1) {
- for (i = multi.length - 1; i >= 0; --i) {
- this.unset(multi[i], css);
- }
- return this;
- }
-
- if (rule) {
- if (!remove) {
- if (!lang.isArray(css)) {
- css = [css];
- }
-
- workerStyle.cssText = rule.style.cssText;
- for (i = css.length - 1; i >= 0; --i) {
- _unsetProperty(workerStyle,css[i]);
- }
-
- if (workerStyle.cssText) {
- rule.style.cssText = workerStyle.cssText;
- } else {
- remove = true;
- }
- }
-
- if (remove) { // remove the rule altogether
- rules = sheet[_rules];
- for (i = rules.length - 1; i >= 0; --i) {
- if (rules[i] === rule) {
- delete cssRules[sel];
- _deleteRule(i);
- break;
- }
- }
- }
- }
- return this;
- },
-
- /**
- * Get the current cssText for a rule or the entire sheet. If the
- * selector param is supplied, only the cssText for that rule will be
- * returned, if found. If the selector string targets multiple
- * selectors separated by commas, the cssText of the first rule only
- * will be returned. If no selector string, the stylesheet's full
- * cssText will be returned.
- *
- * @method getCssText
- * @param sel {String} Selector string
- * @return {String}
- */
- getCssText : function (sel) {
- var rule,css;
-
- if (lang.isString(sel)) {
- // IE's addRule doesn't support multiple comma delimited
- // selectors so rules are mapped internally by atomic selectors
- rule = cssRules[sel.split(/\s*,\s*/)[0]];
-
- return rule ? rule.style.cssText : null;
- } else {
- css = [];
- for (sel in cssRules) {
- if (cssRules.hasOwnProperty(sel)) {
- rule = cssRules[sel];
- css.push(rule.selectorText+" {"+rule.style.cssText+"}");
- }
- }
- return css.join("\n");
- }
- }
- },true);
-
-}
-
-_toCssText = function (css,base) {
- var f = css.styleFloat || css.cssFloat || css['float'],
- prop;
-
- workerStyle.cssText = base || '';
-
- if (f && !css[floatAttr]) {
- css = lang.merge(css);
- delete css.styleFloat; delete css.cssFloat; delete css['float'];
- css[floatAttr] = f;
- }
-
- for (prop in css) {
- if (css.hasOwnProperty(prop)) {
- try {
- // IE throws Invalid Value errors and doesn't like whitespace
- // in values ala ' red' or 'red '
- workerStyle[prop] = lang.trim(css[prop]);
- }
- catch (e) {
- }
- }
- }
- return workerStyle.cssText;
-};
-
-lang.augmentObject(StyleSheet, {
- /**
- * <p>Converts an object literal of style properties and values into a string
- * of css text. This can then be assigned to el.style.cssText.</p>
- *
- * <p>The optional second parameter is a cssText string representing the
- * starting state of the style prior to alterations. This is most often
- * extracted from the eventual target's current el.style.cssText.</p>
- *
- * @method StyleSheet.toCssText
- * @param css {Object} object literal of style properties and values
- * @param cssText {String} OPTIONAL starting cssText value
- * @return {String} the resulting cssText string
- * @static
- */
- toCssText : (('opacity' in workerStyle) ? _toCssText :
- // Wrap IE's toCssText to catch opacity. The copy/merge is to preserve
- // the input object's integrity, but if float and opacity are set, the
- // input will be copied twice in IE. Is there a way to avoid this
- // without increasing the byte count?
- function (css, cssText) {
- if ('opacity' in css) {
- css = lang.merge(css,{
- filter: 'alpha(opacity='+(css.opacity*100)+')'
- });
- delete css.opacity;
- }
- return _toCssText(css,cssText);
- }),
-
- /**
- * Registers a StyleSheet instance in the static registry by the given name
- *
- * @method StyleSheet.register
- * @param name {String} the name to assign the StyleSheet in the registry
- * @param sheet {StyleSheet} The StyleSheet instance
- * @return {Boolean} false if no name or sheet is not a StyleSheet
- * instance. true otherwise.
- * @static
- */
- register : function (name,sheet) {
- return !!(name && sheet instanceof StyleSheet &&
- !sheets[name] && (sheets[name] = sheet));
- },
-
- /**
- * <p>Determines if a selector string is safe to use. Used internally
- * in set to prevent IE from locking up when attempting to add a rule for a
- * "bad selector".</p>
- *
- * <p>Bad selectors are considered to be any string containing unescaped
- * `~!@$%^&()+=|{}[];'"?< or space. Also forbidden are . or # followed by
- * anything other than an alphanumeric. Additionally -abc or .-abc or
- * #_abc or '# ' all fail. There are likely more failure cases, so
- * please file a bug if you encounter one.</p>
- *
- * @method StyleSheet.isValidSelector
- * @param sel {String} the selector string
- * @return {Boolean}
- * @static
- */
- isValidSelector : function (sel) {
- var valid = false;
-
- if (sel && lang.isString(sel)) {
-
- if (!selectors.hasOwnProperty(sel)) {
- // TEST: there should be nothing but white-space left after
- // these destructive regexs
- selectors[sel] = !/\S/.test(
- // combinators
- sel.replace(/\s+|\s*[+~>]\s*/g,' ').
- // attribute selectors (contents not validated)
- replace(/([^ ])\[.*?\]/g,'$1').
- // pseudo-class|element selectors (contents of parens
- // such as :nth-of-type(2) or :not(...) not validated)
- replace(/([^ ])::?[a-z][a-z\-]+[a-z](?:\(.*?\))?/ig,'$1').
- // element tags
- replace(/(?:^| )[a-z0-6]+/ig,' ').
- // escaped characters
- replace(/\\./g,'').
- // class and id identifiers
- replace(/[.#]\w[\w\-]*/g,''));
- }
-
- valid = selectors[sel];
- }
-
- return valid;
- }
-},true);
-
-YAHOO.util.StyleSheet = StyleSheet;
-
-})();
-
-/*
-
-NOTES
- * Style node must be added to the head element. Safari does not honor styles
- applied to StyleSheet objects on style nodes in the body.
- * StyleSheet object is created on the style node when the style node is added
- to the head element in Firefox 2 (and maybe 3?)
- * The cssRules collection is replaced after insertRule/deleteRule calls in
- Safari 3.1. Existing Rules are used in the new collection, so the collection
- cannot be cached, but the rules can be.
- * Opera requires that the index be passed with insertRule.
- * Same-domain restrictions prevent modifying StyleSheet objects attached to
- link elements with remote href (or "about:blank" or "javascript:false")
- * Same-domain restrictions prevent reading StyleSheet cssRules/rules
- collection of link elements with remote href (or "about:blank" or
- "javascript:false")
- * Same-domain restrictions result in Safari not populating node.sheet property
- for link elements with remote href (et.al)
- * IE names StyleSheet related properties and methods differently (see code)
- * IE converts tag names to upper case in the Rule's selectorText
- * IE converts empty string assignment to complex properties to value settings
- for all child properties. E.g. style.background = '' sets non-'' values on
- style.backgroundPosition, style.backgroundColor, etc. All else clear
- style.background and all child properties.
- * IE assignment style.filter = '' will result in style.cssText == 'FILTER:'
- * All browsers support Rule.style.cssText as a read/write property, leaving
- only opacity needing to be accounted for.
- * Benchmarks of style.property = value vs style.cssText += 'property: value'
- indicate cssText is slightly slower for single property assignment. For
- multiple property assignment, cssText speed stays relatively the same where
- style.property speed decreases linearly by the number of properties set.
- Exception being Opera 9.27, where style.property is always faster than
- style.cssText.
- * Opera 9.5b throws a syntax error when assigning cssText with a syntax error.
- * Opera 9.5 doesn't honor rule.style.cssText = ''. Previous style persists.
- You have to remove the rule altogether.
- * Stylesheet properties set with !important will trump inline style set on an
- element or in el.style.property.
- * Creating a worker style collection like document.createElement('p').style;
- will fail after a time in FF (~5secs of inactivity). Property assignments
- will not alter the property or cssText. It may be the generated node is
- garbage collected and the style collection becomes inert (speculation).
- * IE locks up when attempting to add a rule with a selector including at least
- characters {[]}~`!@%^&*()+=|? (unescaped) and leading _ or -
- such as addRule('-foo','{ color: red }') or addRule('._abc','{...}')
- * IE's addRule doesn't support comma separated selectors such as
- addRule('.foo, .bar','{..}')
- * IE throws an error on valid values with leading/trailing white space.
- * When creating an entire sheet at once, only FF2/3 & Opera allow creating a
- style node, setting its innerHTML and appending to head.
- * When creating an entire sheet at once, Safari requires the style node to be
- created with content in innerHTML of another element.
- * When creating an entire sheet at once, IE requires the style node content to
- be set via node.styleSheet.cssText
- * When creating an entire sheet at once in IE, styleSheet.cssText can't be
- written until node.type = 'text/css'; is performed.
- * When creating an entire sheet at once in IE, load-time fork on
- var styleNode = d.createElement('style'); _method = styleNode.styleSheet ?..
- fails (falsey). During run-time, the test for .styleSheet works fine
- * Setting complex properties in cssText will SOMETIMES allow child properties
- to be unset
- set unset FF2 FF3 S3.1 IE6 IE7 Op9.27 Op9.5
- ---------- ----------------- --- --- ---- --- --- ------ -----
- border -top NO NO YES YES YES YES YES
- -top-color NO NO YES YES YES
- -color NO NO NO NO NO
- background -color NO NO YES YES YES
- -position NO NO YES YES YES
- -position-x NO NO NO NO NO
- font line-height YES YES NO NO NO NO YES
- -style YES YES NO YES YES
- -size YES YES NO YES YES
- -size-adjust ??? ??? n/a n/a n/a ??? ???
- padding -top NO NO YES YES YES
- margin -top NO NO YES YES YES
- list-style -type YES YES YES YES YES
- -position YES YES YES YES YES
- overflow -x NO NO YES n/a YES
-
- ??? - unsetting font-size-adjust has the same effect as unsetting font-size
- * FireFox and WebKit populate rule.cssText as "SELECTOR { CSSTEXT }", but
- Opera and IE do not.
- * IE6 and IE7 silently ignore the { and } if passed into addRule('.foo','{
- color:#000}',0). IE8 does not and creates an empty rule.
- * IE6-8 addRule('.foo','',n) throws an error. Must supply *some* cssText
-*/
-
-YAHOO.register("stylesheet", YAHOO.util.StyleSheet, {version: "2.7.0", build: "1799"});