//////////////////////////////////////////////////////////////////////////////// // // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// package mx.styles { import mx.core.mx_internal; /** * Represents a selector node in a potential chain of selectors used to match * CSS style declarations to components. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion Flex 4 */ public class CSSSelector { //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- /** * Constructor. * * @param subject The plain representation of this selector without * conditions or ancestors. This is typically a fully-qualified class name; for example, * "spark.components.Button". You can use "*" to match all components or "global" for a global selector. * * @param conditions An optional Array of objects of type CSSCondition that is used to match a * subset of component instances. Currently only a single or a pair of * conditions are supported. * * @param ancestor An optional selector to match on a component that * descends from an arbitrary ancestor. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion Flex 4 */ public function CSSSelector(subject:String, conditions:Array=null, ancestor:CSSSelector=null) { _subject = subject; _conditions = conditions; _ancestor = ancestor; } //-------------------------------------------------------------------------- // // Properties // //-------------------------------------------------------------------------- //---------------------------------- // ancestor //---------------------------------- /** * @private */ private var _ancestor:CSSSelector; /** * If this selector is part of a descendant selector it may have a further * selector defined for an arbitrary ancestor. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion Flex 4 */ public function get ancestor():CSSSelector { return _ancestor; } //---------------------------------- // conditions //---------------------------------- /** * @private */ private var _conditions:Array; // of CSSCondition /** * This selector may match a subset of components by specifying further * conditions (for example, a matching component must have a particular id, * styleName (equivalent to a 'class' condition in CSS) or state * (equivalent to a 'pseudo' condition in CSS)). * *

If no conditions are specified, this property is null.

* * @return Array of CSSCondition specified for this selector. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion Flex 4 */ public function get conditions():Array // of CSSCondition { return _conditions; } //---------------------------------- // specificity //---------------------------------- /** * Calculates the specificity of a selector chain in order to determine * the precedence when applying several matching style declarations. Note * that id conditions contribute 100 points, pseudo and class conditions * each contribute 10 points, types (including descendants in a chain of * selectors) contribute 1 point. Universal selectors ("*") contribute * nothing. The result is the sum of these contributions. Selectors with a * higher specificity override selectors of lower specificity. If * selectors have equal specificity, the declaration order determines * the precedence (the last one wins). * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion Flex 4 */ public function get specificity():int { var s:int = 0; if ("*" != subject && "global" != subject && "" != subject) s = 1; if (conditions != null) { for each (var condition:CSSCondition in conditions) { s += condition.specificity; } } if (ancestor != null) s += ancestor.specificity; return s; } //---------------------------------- // subject //---------------------------------- /** * @private */ private var _subject:String; /** * The subject of this selector node (only). To get a String representation * of all conditions and descendants of this selector call the toString() * method. * *

If this selector represents the root node of a potential chain of * selectors, the subject also represents the subject of the entire selector * expression.

* * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion Flex 4 */ public function get subject():String { return _subject; } //-------------------------------------------------------------------------- // // Methods // //-------------------------------------------------------------------------- /** * Determines whether this selector matches the given component. * * @param object The component to which the selector may apply. * @return true if component is a match, or false if not. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion Flex 4 */ public function matchesStyleClient(object:IAdvancedStyleClient):Boolean { var match:Boolean = false; var condition:CSSCondition = null; // If we have an ancestor then this is part of a descendant selector if (ancestor) { if (conditions) { // First, test if the conditions match for each (condition in conditions) { match = condition.matchesStyleClient(object); if (!match) return false; } } // Then reset and test if any ancestor matches match = false; var parent:IAdvancedStyleClient = object.styleParent; while (parent != null) { if (parent.matchesCSSType(ancestor.subject) || "*" == ancestor.subject) { match = ancestor.matchesStyleClient(parent); if (match) break; } parent = parent.styleParent; } } else { // Check the type selector matches if (subject == "*" || subject == "" || object.matchesCSSType(subject)) { match = true; } // Then check if any conditions match if (match && conditions != null) { for each (condition in conditions) { match = condition.matchesStyleClient(object); if (!match) return false; } } } return match; } /** * @private */ mx_internal function getPseudoCondition():String { var result:String = null; if (conditions) { for each (var condition:CSSCondition in conditions) { if (condition.kind == CSSConditionKind.PSEUDO) { result = condition.value; break; } } } return result; } /** * Returns a String representation of this selector. * * @return A String representation of this selector including all of its * syntax, conditions and ancestors. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion Flex 4 */ public function toString():String { var s:String; if (ancestor != null) { s = ancestor.toString() + " " + subject; } else { s = subject; } if (conditions != null) { for each (var condition:CSSCondition in conditions) { s += condition.toString(); } } return s; } } }