blob: 92a70f9210f4ba921c0cbf481e384194db6666bf [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// 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, styleManager, 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, styleManager, 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, styleManager:IStyleManager2, qualified:Boolean=true):OrderedObject
{
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);
}
}
}