//////////////////////////////////////////////////////////////////////////////// // // 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 flash.display.DisplayObject; import flash.display.DisplayObjectContainer; import flash.system.ApplicationDomain; import flash.utils.getQualifiedClassName; import flash.utils.getQualifiedSuperclassName; import mx.core.FlexGlobals; import mx.core.IFlexDisplayObject; import mx.core.IFlexModule; import mx.core.IFlexModuleFactory; import mx.core.IFontContextComponent; import mx.core.IInvalidating; import mx.core.IUITextField; import mx.core.IVisualElement; import mx.core.UIComponent; import mx.core.mx_internal; import mx.effects.EffectManager; import mx.managers.SystemManager; import mx.modules.IModule; import mx.modules.ModuleManager; import mx.utils.NameUtil; import mx.utils.OrderedObject; import mx.utils.object_proxy; use namespace mx_internal; use namespace object_proxy; [ExcludeClass] /** * @private * This is an all-static class with methods for building the protochains * that Flex uses to look up CSS style properties. */ public class StyleProtoChain { include "../core/Version.as"; //-------------------------------------------------------------------------- // // Class constants // //-------------------------------------------------------------------------- /** * @private * The inheritingStyles and nonInheritingStyles properties * are initialized to this empty Object. * This allows the getStyle() and getStyle() * methods to simply access inheritingStyles[] and nonInheritingStyles[] * without needing to first check whether those objects exist. * If they were simply initialized to {}, we couldn't determine * whether the style chain has already been built or not. */ public static var STYLE_UNINITIALIZED:Object = {}; //-------------------------------------------------------------------------- // // Class methods // //-------------------------------------------------------------------------- /** * @private * Implements the getClassStyleDeclarations() logic * for UIComponent and TextBase. * The 'object' parameter will be one or the other. */ public static function getClassStyleDeclarations(object:IStyleClient):Array { var styleManager:IStyleManager2 = getStyleManager(object); var qualified:Boolean = styleManager.qualifiedTypeSelectors; var className:String = qualified ? getQualifiedClassName(object) : object.className; var advancedObject:IAdvancedStyleClient = object as IAdvancedStyleClient; var typeHierarchy:OrderedObject = getTypeHierarchy(object, qualified); var types:Array = typeHierarchy.propertyList; var typeCount:int = types.length; var classDecls:Array = null; if (!styleManager.hasAdvancedSelectors()) { classDecls = styleManager.typeSelectorCache[className]; if (classDecls) return classDecls; } classDecls = []; // Loop over the type hierarhcy starting at the base type and work // down the chain of subclasses. for (var i:int = typeCount - 1; i >= 0; i--) { var type:String = types[i].toString(); if (styleManager.hasAdvancedSelectors() && advancedObject != null) { var decls:Object = styleManager.getStyleDeclarations(type); if (decls) { var matchingDecls:Array = matchStyleDeclarations(decls, advancedObject); classDecls = classDecls.concat(matchingDecls); } } else { var decl:CSSStyleDeclaration = styleManager.getMergedStyleDeclaration(type); if (decl) classDecls.push(decl); } } if (styleManager.hasAdvancedSelectors() && advancedObject != null) { // Advanced selectors may result in more than one match per type so // we sort based on specificity, but we preserve the declaration // order for equal selectors. classDecls = sortOnSpecificity(classDecls); } else { // Cache the simple type declarations for this class styleManager.typeSelectorCache[className] = classDecls; } return classDecls; } /** * @private * Implements the initProtoChain() logic for UIComponent and TextBase. * The 'object' parameter will be one or the other. */ public static function initProtoChain(object:IStyleClient, inheritPopUpStylesFromOwner:Boolean=true):void { var styleManager:IStyleManager2 = getStyleManager(object); var n:int; var i:int; var uicObject:UIComponent = object as UIComponent; var advancedObject:IAdvancedStyleClient = object as IAdvancedStyleClient; var styleDeclaration:CSSStyleDeclaration = null; var universalSelectors:Array = []; var hasStyleName:Boolean = false; var styleName:Object = object.styleName; if (styleName) { if (styleName is CSSStyleDeclaration) { // Get the styles referenced by the styleName property. universalSelectors.push(CSSStyleDeclaration(styleName)); } else if (styleName is IFlexDisplayObject || styleName is IStyleClient) { // If the styleName property is a UIComponent, then there's a // special search path for that case. StyleProtoChain.initProtoChainForUIComponentStyleName(object); return; } else if (styleName is String) { hasStyleName = true; } } // To build the proto chain, we start at the end and work forward. // Referring to the list at the top of this function, we'll start // by getting the tail of the proto chain, which is: // - for non-inheriting styles, the global style sheet // - for inheriting styles, my parent's style object var nonInheritChain:Object = styleManager.stylesRoot; if (nonInheritChain && nonInheritChain.effects) object.registerEffects(nonInheritChain.effects); var p:IStyleClient = null; if (object is IVisualElement) p = IVisualElement(object).parent as IStyleClient; else if (object is IAdvancedStyleClient) p = IAdvancedStyleClient(object).styleParent as IStyleClient; if (p) { var inheritChain:Object = p.inheritingStyles; if (inheritChain == StyleProtoChain.STYLE_UNINITIALIZED) inheritChain = nonInheritChain; // If this object is a module then add its global styles to the // inheritChain. If we don't have global styles in this style manager // then the user didn't declare a global style in the module and the // compiler didn't add a duplicate default style. In that case don't // add global styles to the chain because the parent style manager's // global styles are already on the chain. if (object is IModule) { styleDeclaration = styleManager.getStyleDeclaration("global"); if (styleDeclaration) inheritChain = styleDeclaration.addStyleToProtoChain(inheritChain, DisplayObject(object)); } } else { // Pop ups inheriting chain starts at Application instead of global. // This allows popups to grab styles like themeColor that are // set on Application. if (uicObject && uicObject.isPopUp) { var owner:DisplayObjectContainer = uicObject._owner; if (inheritPopUpStylesFromOwner && owner && (owner is IStyleClient)) { inheritChain = IStyleClient(owner).inheritingStyles; } else { inheritChain = FlexGlobals.topLevelApplication.inheritingStyles; } } else { inheritChain = styleManager.stylesRoot; } } var styleDeclarations:Array = null; // If we have an advanced style client, we handle this separately // because of the considerably more complex selector matches... if (styleManager.hasAdvancedSelectors() && advancedObject != null) { styleDeclarations = getMatchingStyleDeclarations(advancedObject, universalSelectors); n = styleDeclarations != null ? styleDeclarations.length : 0; for (i = 0; i < n; i++) { styleDeclaration = styleDeclarations[i]; inheritChain = styleDeclaration.addStyleToProtoChain(inheritChain, uicObject); nonInheritChain = styleDeclaration.addStyleToProtoChain(nonInheritChain, uicObject); if (styleDeclaration.effects) advancedObject.registerEffects(styleDeclaration.effects); } } // Otherwise we use the legacy Flex 3 logic for simple selectors. else { // Get the styles referenced by the styleName property if (hasStyleName) { var styleNames:Array = styleName.split(/\s+/); n = styleNames.length; for (i = 0; i < n; i++) { if (styleNames[i].length) { styleDeclaration = styleManager.getMergedStyleDeclaration("." + styleNames[i]); if (styleDeclaration) universalSelectors.push(styleDeclaration); } } } // Working backwards up the list, the next element in the // search path is the type selector styleDeclarations = object.getClassStyleDeclarations(); n = styleDeclarations != null ? styleDeclarations.length : 0; for (i = 0; i < n; i++) { styleDeclaration = styleDeclarations[i]; inheritChain = styleDeclaration.addStyleToProtoChain(inheritChain, uicObject); nonInheritChain = styleDeclaration.addStyleToProtoChain(nonInheritChain, uicObject); if (styleDeclaration.effects) object.registerEffects(styleDeclaration.effects); } // Next are the class selectors n = universalSelectors.length; for (i = 0; i < n; i++) { styleDeclaration = universalSelectors[i]; if (styleDeclaration) { inheritChain = styleDeclaration.addStyleToProtoChain(inheritChain, uicObject); nonInheritChain = styleDeclaration.addStyleToProtoChain(nonInheritChain, uicObject); if (styleDeclaration.effects) object.registerEffects(styleDeclaration.effects); } } } // Finally, we'll add the in-line styles // to the head of the proto chain. styleDeclaration = object.styleDeclaration; object.inheritingStyles = styleDeclaration ? styleDeclaration.addStyleToProtoChain(inheritChain, uicObject) : inheritChain; object.nonInheritingStyles = styleDeclaration ? styleDeclaration.addStyleToProtoChain(nonInheritChain, uicObject) : nonInheritChain; } /** * @private * If the styleName property points to a UIComponent, then we search * for stylable properties in the following order: * * 1) Look for inline styles on this object * 2) Look for inline styles on the styleName object * 3) Look for class selectors on the styleName object * 4) Look for type selectors on the styleName object * 5) Look for type selectors on this object * 6) Follow the usual search path for the styleName object * * If this object doesn't have any type selectors, then the * search path can be simplified to two steps: * * 1) Look for inline styles on this object * 2) Follow the usual search path for the styleName object */ public static function initProtoChainForUIComponentStyleName( obj:IStyleClient):void { var styleManager:IStyleManager2 = getStyleManager(obj); var styleName:IStyleClient = IStyleClient(obj.styleName); var target:DisplayObject = obj as DisplayObject; // Push items onto the proto chain in reverse order, beginning with // 6) Follow the usual search path for the styleName object var nonInheritChain:Object = styleName.nonInheritingStyles; if (!nonInheritChain || nonInheritChain == StyleProtoChain.STYLE_UNINITIALIZED) { nonInheritChain = styleManager.stylesRoot; if (nonInheritChain.effects) obj.registerEffects(nonInheritChain.effects); } var inheritChain:Object = styleName.inheritingStyles; if (!inheritChain || inheritChain == StyleProtoChain.STYLE_UNINITIALIZED) { inheritChain = styleManager.stylesRoot; } // If there's no type selector on this object, then we can collapse // 6 steps to 2 (see above) var typeSelectors:Array = obj.getClassStyleDeclarations(); var n:int = typeSelectors.length; // If we are a StyleProxy and we aren't building the protochain from // our type selectors, then we need to build the protochain from // the styleName since styleName.nonInheritingStyles is always null. if (styleName is StyleProxy) { if (n == 0) { // 4) Look for type selectors on the styleName object // 3) Look for class selectors on the styleName object // 2) Look for inline styles on the styleName object nonInheritChain = addProperties(nonInheritChain, styleName, false); } target = StyleProxy(styleName).source as DisplayObject; } for (var i:int = 0; i < n; i++) { var typeSelector:CSSStyleDeclaration = typeSelectors[i]; // If there's no *inheriting* type selector on this object, then we // can still collapse 6 steps to 2 for the inheriting properties. // 5) Look for type selectors on this object inheritChain = typeSelector.addStyleToProtoChain(inheritChain, target); // 4) Look for type selectors on the styleName object // 3) Look for class selectors on the styleName object // 2) Look for inline styles on the styleName object inheritChain = addProperties(inheritChain, styleName, true); // 5) Look for type selectors on this object nonInheritChain = typeSelector.addStyleToProtoChain(nonInheritChain, target); // 4) Look for type selectors on the styleName object // 3) Look for class selectors on the styleName object // 2) Look for inline styles on the styleName object nonInheritChain = addProperties(nonInheritChain, styleName, false); if (typeSelector.effects) obj.registerEffects(typeSelector.effects); } // 1) Look for inline styles on this object obj.inheritingStyles = obj.styleDeclaration ? obj.styleDeclaration.addStyleToProtoChain(inheritChain, target) : inheritChain; obj.nonInheritingStyles = obj.styleDeclaration ? obj.styleDeclaration.addStyleToProtoChain(nonInheritChain, target) : nonInheritChain; } /** * See the comment for the initProtoChainForUIComponentStyleName * function. The comment for that function includes a six-step * sequence. This sub-function implements the following pieces * of that sequence: * * 2) Look for inline styles on the styleName object * 3) Look for class selectors on the styleName object * 4) Look for type selectors on the styleName object * * This piece is broken out as a separate function so that it * can be called recursively when the styleName object has a * styleName property is itself another UIComponent. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ private static function addProperties(chain:Object, obj:IStyleClient, bInheriting:Boolean):Object { // Only use a filter map if styleName is a StyleProxy and we are building the nonInheritingStyles chain var filterMap:Object = obj is StyleProxy && !bInheriting ? StyleProxy(obj).filterMap : null; // StyleProxy's usually have sources that are DisplayObject's, but a StyleProxy can also have // another StyleProxy as it's source (Example: CalendarLayout's source is a StyleProxy for DateChooser, // whose style is a StyleProxy for DateField) // The way we use target is a bit hacky, but we always assume that styles (if pointed to DisplayObjects) // are the parent (or atleast an ancestor), and we rely on this down the line (such as in // DataGridColumn.addStyleToProtoChain) var curObj:IStyleClient = obj; while (curObj is StyleProxy) { curObj = StyleProxy(curObj).source; } var target:DisplayObject = curObj as DisplayObject; var advancedObject:IAdvancedStyleClient = obj as IAdvancedStyleClient; var styleName:Object = obj.styleName; var styleDeclarations:Array; var decl:CSSStyleDeclaration; var styleManager:IStyleManager2 = getStyleManager(target); // If we have an advanced style client, we handle this separately // because of the considerably more complex selector matches... if (advancedObject != null && styleManager.hasAdvancedSelectors()) { // Handle special case of styleName as a CSSStyleDeclaration if (styleName is CSSStyleDeclaration) { styleDeclarations = [CSSStyleDeclaration(styleName)]; } // Find matching style declarations, sorted by specificity styleDeclarations = getMatchingStyleDeclarations(advancedObject, styleDeclarations); // Then apply matching selectors to the proto chain for (i = 0; i < styleDeclarations.length; i++) { decl = styleDeclarations[i]; if (decl) { chain = decl.addStyleToProtoChain(chain, target, filterMap); if (decl.effects) obj.registerEffects(decl.effects); } } // Finally, handle special case of styleName as an IStyleClient // which overrides any of the selectors above if (styleName is IStyleClient) { // If the styleName property is another UIComponent, then // recursively add type selectors, class selectors, and // inline styles for that UIComponent chain = addProperties(chain, IStyleClient(styleName), bInheriting); } } else { // 4) Add type selectors styleDeclarations = obj.getClassStyleDeclarations(); var n:int = styleDeclarations.length; for (var i:int = 0; i < n; i++) { decl = styleDeclarations[i]; chain = decl.addStyleToProtoChain(chain, target, filterMap); if (decl.effects) obj.registerEffects(decl.effects); } // 3) Add class selectors if (styleName) { styleDeclarations = []; if (typeof(styleName) == "object") { if (styleName is CSSStyleDeclaration) { // Get the style sheet referenced by the styleName property. styleDeclarations.push(CSSStyleDeclaration(styleName)); } else { // If the styleName property is another UIComponent, then // recursively add type selectors, class selectors, and // inline styles for that UIComponent chain = addProperties(chain, IStyleClient(styleName), bInheriting); } } else { // Get the style sheets referenced by the styleName property var styleNames:Array = styleName.split(/\s+/); for (var c:int=0; c < styleNames.length; c++) { if (styleNames[c].length) { styleDeclarations.push(styleManager.getMergedStyleDeclaration("." + styleNames[c])); } } } for (i = 0; i < styleDeclarations.length; i++) { decl = styleDeclarations[i]; if (decl) { chain = decl.addStyleToProtoChain(chain, target, filterMap); if (decl.effects) obj.registerEffects(decl.effects); } } } } // 2) Add inline styles if (obj.styleDeclaration) chain = obj.styleDeclaration.addStyleToProtoChain(chain, target, filterMap); return chain; } /** * @private */ public static function initTextField(obj:IUITextField):void { // TextFields never have any inline styles or type selector, so // this is an optimized version of the initObject function (above) var styleManager:IStyleManager2 = StyleManager.getStyleManager(obj.moduleFactory); var styleName:Object = obj.styleName; var classSelectors:Array = []; if (styleName) { if (typeof(styleName) == "object") { if (styleName is CSSStyleDeclaration) { // Get the style sheet referenced by the styleName property. classSelectors.push(CSSStyleDeclaration(styleName)); } else if (styleName is StyleProxy) { obj.inheritingStyles = IStyleClient(styleName).inheritingStyles; obj.nonInheritingStyles = addProperties(styleManager.stylesRoot, IStyleClient(styleName), false); return; } else { // styleName points to a UIComponent, so just set // this TextField's proto chains to be the same // as that UIComponent's proto chains. obj.inheritingStyles = IStyleClient(styleName).inheritingStyles; obj.nonInheritingStyles = IStyleClient(styleName).nonInheritingStyles; return; } } else { // Get the style sheets referenced by the styleName property var styleNames:Array = styleName.split(/\s+/); for (var c:int=0; c < styleNames.length; c++) { if (styleNames[c].length) { classSelectors.push(styleManager.getMergedStyleDeclaration("." + styleNames[c])); } } } } // To build the proto chain, we start at the end and work forward. // We'll start by getting the tail of the proto chain, which is: // - for non-inheriting styles, the global style sheet // - for inheriting styles, my parent's style object var inheritChain:Object = IStyleClient(obj.parent).inheritingStyles; var nonInheritChain:Object = styleManager.stylesRoot; if (!inheritChain) inheritChain = styleManager.stylesRoot; // Next are the class selectors for (var i:int = 0; i < classSelectors.length; i++) { var classSelector:CSSStyleDeclaration = classSelectors[i]; if (classSelector) { inheritChain = classSelector.addStyleToProtoChain(inheritChain, DisplayObject(obj)); nonInheritChain = classSelector.addStyleToProtoChain(nonInheritChain, DisplayObject(obj)); } } obj.inheritingStyles = inheritChain; obj.nonInheritingStyles = nonInheritChain; } /** * @private * Implements the setStyle() logic for UIComponent and TextBase. * The 'object' parameter will be one or the other. */ public static function setStyle(object:IStyleClient, styleProp:String, newValue:*):void { var styleManager:IStyleManager2 = getStyleManager(object); if (styleProp == "styleName") { // Let the setter handle this one, see UIComponent. object.styleName = newValue; // Short circuit, because styleName isn't really a style. return; } if (EffectManager.getEventForEffectTrigger(styleProp) != "") EffectManager.setStyle(styleProp, object); // If this object didn't previously have any inline styles, // then regenerate its proto chain // (and the proto chains of its descendants). var isInheritingStyle:Boolean = styleManager.isInheritingStyle(styleProp); var isProtoChainInitialized:Boolean = object.inheritingStyles != StyleProtoChain.STYLE_UNINITIALIZED; var valueChanged:Boolean = object.getStyle(styleProp) != newValue; if (!object.styleDeclaration) { object.styleDeclaration = new CSSStyleDeclaration(null, styleManager); object.styleDeclaration.setLocalStyle(styleProp, newValue); // If inheritingStyles is undefined, then this object is being // initialized and we haven't yet generated the proto chain. To // avoid redundant work, don't bother to create the proto chain here. if (isProtoChainInitialized) object.regenerateStyleCache(isInheritingStyle); } else { object.styleDeclaration.setLocalStyle(styleProp, newValue); } if (isProtoChainInitialized && valueChanged) { object.styleChanged(styleProp); object.notifyStyleChangeInChildren(styleProp, isInheritingStyle); } } /** * @private * Implements the styleChanged() logic for UIComponent and TextBase. * The 'object' parameter will be one or the other. */ public static function styleChanged(object:IInvalidating, styleProp:String):void { var styleManager:IStyleManager2 = getStyleManager(object); // If font changed, then invalidateProperties so // we can re-create the text field in commitProperties // TODO (gosmith): Should hasFontContextChanged() be added to IFontContextComponent? if (object is IFontContextComponent && "hasFontContextChanged" in object && object["hasFontContextChanged"]()) { object.invalidateProperties(); } if (!styleProp || styleProp == "styleName" || styleProp == "layoutDirection") { object.invalidateProperties(); } // Check to see if this is one of the style properties // that is known to affect layout. if (!styleProp || styleProp == "styleName" || styleManager.isSizeInvalidatingStyle(styleProp)) { // This style property change may affect the layout of this // object. Signal the LayoutManager to re-measure the object. object.invalidateSize(); } // TODO (gosmith): Should initThemeColor() be in some interface? if (!styleProp || styleProp == "styleName" || styleProp == "themeColor") { if (object is UIComponent) object["initThemeColor"](); } object.invalidateDisplayList(); var parent:IInvalidating; if (object is IVisualElement) parent = IVisualElement(object).parent as IInvalidating; if (parent) { if (styleProp == "styleName" || styleManager.isParentSizeInvalidatingStyle(styleProp)) parent.invalidateSize(); if (styleProp == "styleName" || styleManager.isParentDisplayListInvalidatingStyle(styleProp)) parent.invalidateDisplayList(); } } /** * @private */ public static function matchesCSSType(object:IAdvancedStyleClient, cssType:String):Boolean { var styleManager:IStyleManager2 = getStyleManager(object); var qualified:Boolean = styleManager.qualifiedTypeSelectors; var typeHierarchy:OrderedObject = getTypeHierarchy(object, qualified); return typeHierarchy.object_proxy::getObjectProperty(cssType) != null; } /** * @private * Find all matching style declarations for an IAdvancedStyleClient * component. The result is sorted in terms of specificity, but the * declaration order is preserved. * * @param object - an IAdvancedStyleClient instance of the component to * match. * @param styleDeclarations - an optional Array of additional * CSSStyleDeclarations to be included in the sorted matches. * * @return An Array of matching style declarations sorted by specificity. */ public static function getMatchingStyleDeclarations(object:IAdvancedStyleClient, styleDeclarations:Array=null):Array // of CSSStyleDeclaration { var styleManager:IStyleManager2 = getStyleManager(object); if (styleDeclarations == null) styleDeclarations = []; // First, look for universal selectors var universalDecls:Object = styleManager.getStyleDeclarations("*"); styleDeclarations = matchStyleDeclarations(universalDecls, object).concat(styleDeclarations); // Next, look for type selectors (includes ActionScript supertype matches) // If we also had universal selectors, concatenate them with our type // selectors and then resort by specificity... if (styleDeclarations.length > 0) { styleDeclarations = object.getClassStyleDeclarations().concat(styleDeclarations); styleDeclarations = sortOnSpecificity(styleDeclarations); } else { // Otherwise, we only have type selectors (which are already sorted) styleDeclarations = object.getClassStyleDeclarations(); } return styleDeclarations; } /** * @private * @param object - the IStyleClient to be introspected * @param qualified - whether qualified type names should be used * @return an ordered map of class names, starting with the object's class * name and then each super class name until we hit a stop class, such as * mx.core::UIComponent. */ private static function getTypeHierarchy(object:IStyleClient, qualified:Boolean=true):OrderedObject { var styleManager:IStyleManager2 = getStyleManager(object); var className:String = getQualifiedClassName(object); var hierarchy:OrderedObject = styleManager.typeHierarchyCache[className] as OrderedObject; if (hierarchy == null) { hierarchy = new OrderedObject(); var myApplicationDomain:ApplicationDomain; var factory:IFlexModuleFactory = ModuleManager.getAssociatedFactory(object); if (factory != null) { myApplicationDomain = ApplicationDomain(factory.info()["currentDomain"]); } else { var myRoot:DisplayObject = SystemManager.getSWFRoot(object); if (!myRoot) return hierarchy; myApplicationDomain = myRoot.loaderInfo.applicationDomain; } styleManager.typeHierarchyCache[className] = hierarchy; while (!isStopClass(className)) { try { var type:String; if (qualified) type = className.replace("::", "."); else type = NameUtil.getUnqualifiedClassName(className); hierarchy.object_proxy::setObjectProperty(type, true); className = getQualifiedSuperclassName( myApplicationDomain.getDefinition(className)); } catch(e:ReferenceError) { className = null; } } } return hierarchy; } /** * @private * Our style type hierarhcy stops at UIComponent, UITextField or * GraphicElement, not Object. */ private static function isStopClass(value:String):Boolean { return value == null || value == "mx.core::UIComponent" || value == "mx.core::UITextField" || value == "mx.graphics.baseClasses::GraphicElement"; } /** * @private * Find all matching style declarations for an IAdvancedStyleClient * component. The result is unsorted in terms of specificity, but the * declaration order is preserved. * * @param declarations - a map of declarations to be searched for matches. * @param object - an instance of the component to match. * * @return An unsorted Array of matching style declarations for the given * subject. */ private static function matchStyleDeclarations(declarations:Object, object:IAdvancedStyleClient):Array // of CSSStyleDeclaration { var matchingDecls:Array = []; var pseudos:Array = declarations["pseudo"]; var classes:Array = declarations["class"]; var ids:Array = declarations["id"]; var unconditionals:Array = declarations["unconditional"]; var decl:CSSStyleDeclaration; // Find the subset of declarations that match this component for each (decl in unconditionals) { if (decl.matchesStyleClient(object)) matchingDecls.push(decl); } if (object.styleName is String) { // Find the subset of declarations that match this component for each (decl in classes) { if (decl.matchesStyleClient(object)) matchingDecls.push(decl); } } if (object.hasCSSState()) { // Find the subset of declarations that match this component for each (decl in pseudos) { if (decl.matchesStyleClient(object)) matchingDecls.push(decl); } } if (object.id) { // Find the subset of declarations that match this component for each (decl in ids) { if (decl.matchesStyleClient(object)) matchingDecls.push(decl); } } if (matchingDecls.length > 1) matchingDecls.sortOn("selectorIndex", Array.NUMERIC); // if there are declarations from the parent StyleManager, match them in their own // order, then prepend them. Parent styles go on the chain before child styles. if (declarations.parent) matchingDecls = matchStyleDeclarations(declarations.parent, object).concat(matchingDecls); return matchingDecls; } /** * @private * Sort algorithm to order style declarations by specificity. Note that * Array.sort() is not used as it does not employ a stable algorithm and * CSS requires the order of equal style declaration to be preserved. */ private static function sortOnSpecificity(decls:Array):Array // of CSSStyleDeclaration { // TODO (pfarland): Copied algorithm from Group.sortOnLayer as the // number of declarations to be sorted is usually small. We may consider // replacing this insertion sort with an efficient but stable merge sort // or the like if many style declarations need to sorted. var len:Number = decls.length; var tmp:CSSStyleDeclaration; if (len <= 1) return decls; for (var i:int = 1; i < len; i++) { for (var j:int = i; j > 0; j--) { if (decls[j].specificity < decls[j-1].specificity) { tmp = decls[j]; decls[j] = decls[j-1]; decls[j-1] = tmp; } else { break; } } } return decls; } /** * @private * Get the style manager of any object. If the object does not implement IFlexModule or * is not of type StyleProxy, then the top-level style manager will be returned. * * @param object - Typed as Object because various interfaces are passed here. * @return a style manager, will not be null. */ private static function getStyleManager(object:Object):IStyleManager2 { if (object is IFlexModule) return StyleManager.getStyleManager(IFlexModule(object).moduleFactory); else if (object is StyleProxy) return getStyleManagerFromStyleProxy(StyleProxy(object)); else return StyleManager.getStyleManager(null); } /** * @private * Get the style manager for a given StyleProxy object. * * @return a style manager, will not be null. */ private static function getStyleManagerFromStyleProxy(obj:StyleProxy):IStyleManager2 { // StyleProxy's usually have sources that are DisplayObject's, but a StyleProxy can also have // another StyleProxy as it's source (Example: CalendarLayout's source is a StyleProxy for DateChooser, // whose style is a StyleProxy for DateField) var curObj:IStyleClient = obj; while (curObj is StyleProxy) { curObj = StyleProxy(curObj).source; } if (curObj is IFlexModule) return StyleManager.getStyleManager(IFlexModule(curObj).moduleFactory); return StyleManager.getStyleManager(null); } } }