| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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.events.EventDispatcher; |
| import flash.utils.Dictionary; |
| |
| import mx.core.Singleton; |
| import mx.core.mx_internal; |
| import mx.events.FlexChangeEvent; |
| import mx.managers.ISystemManager; |
| import mx.managers.SystemManagerGlobals; |
| import mx.utils.ObjectUtil; |
| |
| use namespace mx_internal; |
| |
| /** |
| * The CSSStyleDeclaration class represents a set of CSS style rules. |
| * The MXML compiler automatically generates one CSSStyleDeclaration object |
| * for each selector in the CSS files associated with a Flex application. |
| * |
| * <p>A CSS rule such as |
| * <pre> |
| * Button { color: #FF0000 } |
| * </pre> |
| * affects every instance of the Button class; |
| * a selector like <code>Button</code> is called a type selector |
| * and must not start with a dot.</p> |
| * |
| * <p>A CSS rule such as |
| * <pre> |
| * .redButton { color: #FF0000 } |
| * </pre> |
| * affects only components whose <code>styleName</code> property |
| * is set to <code>"redButton"</code>; |
| * a selector like <code>.redButton</code> is called a class selector |
| * and must start with a dot.</p> |
| * |
| * <p>You can access the autogenerated CSSStyleDeclaration objects |
| * using the <code>StyleManager.getStyleDeclaration()</code> method, |
| * passing it either a type selector |
| * <pre> |
| * var buttonDeclaration:CSSStyleDeclaration = |
| * StyleManager.getStyleDeclaration("Button"); |
| * </pre> |
| * or a class selector |
| * <pre> |
| * var redButtonStyleDeclaration:CSSStyleDeclaration = |
| * StyleManager.getStyleDeclaration(".redButton"); |
| * </pre> |
| * </p> |
| * |
| * <p>You can use the <code>getStyle()</code>, <code>setStyle()</code>, |
| * and <code>clearStyle()</code> methods to get, set, and clear |
| * style properties on a CSSStyleDeclaration.</p> |
| * |
| * <p>You can also create and install a CSSStyleDeclaration at run time |
| * using the <code>StyleManager.setStyleDeclaration()</code> method: |
| * <pre> |
| * var newStyleDeclaration:CSSStyleDeclaration = new CSSStyleDeclaration(".bigMargins"); |
| * newStyleDeclaration.defaultFactory = function():void |
| * { |
| * leftMargin = 50; |
| * rightMargin = 50; |
| * } |
| * StyleManager.setStyleDeclaration(".bigMargins", newStyleDeclaration, true); |
| * </pre> |
| * </p> |
| * |
| * @see mx.core.UIComponent |
| * @see mx.styles.StyleManager |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public class CSSStyleDeclaration extends EventDispatcher |
| { |
| include "../core/Version.as"; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Class constants |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| private static const NOT_A_COLOR:uint = 0xFFFFFFFF; |
| |
| /** |
| * @private |
| */ |
| private static const FILTERMAP_PROP:String = "__reserved__filterMap"; |
| |
| /** |
| * @private |
| */ |
| private static var emptyObjectFactory:Function = function():void {}; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Constructor. |
| * |
| * @param selector - If the selector is a CSSSelector then advanced |
| * CSS selectors are supported. If a String is used for the selector then |
| * only simple CSS selectors are supported. If the String starts with a |
| * dot it is interpreted as a universal class selector, otherwise it must |
| * represent a simple type selector. If not null, this CSSStyleDeclaration |
| * will be registered with StyleManager. |
| * |
| * @param styleManager - The style manager to set this declaration into. If the |
| * styleManager is null the top-level style manager will be used. |
| * |
| * @param autoRegisterWithStyleManager - If true set the selector in the styleManager. The selector |
| * will only be set if both <code>selector</code> and <code>styleManager</code> are |
| * both non-null. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function CSSStyleDeclaration(selector:Object=null, styleManager:IStyleManager2=null, autoRegisterWithStyleManager:Boolean = true) |
| { |
| super(); |
| |
| // Do not reference StyleManager directly because this is a bootstrap class |
| if (!styleManager) |
| styleManager = Singleton.getInstance("mx.styles::IStyleManager2") as IStyleManager2; |
| |
| this.styleManager = styleManager; |
| |
| if (selector) |
| { |
| if (selector is CSSSelector) |
| { |
| this.selector = selector as CSSSelector; |
| } |
| else |
| { |
| // Otherwise, a legacy Flex 3 String selector was provided |
| selectorString = selector.toString(); |
| } |
| |
| if (autoRegisterWithStyleManager) |
| styleManager.setStyleDeclaration(selectorString, this, false); |
| } |
| |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * This Dictionary keeps track of all the style name/value objects |
| * produced from this CSSStyleDeclaration and already inserted into |
| * prototype chains. Whenever this CSSStyleDeclaration's overrides object |
| * is updated by setStyle(), these clone objects must also be updated. |
| */ |
| private var clones:Dictionary = new Dictionary(true); |
| |
| /** |
| * @private |
| * The number of CSS selectors pointing to this CSSStyleDeclaration. |
| * It will be greater than 0 if this CSSStyleDeclaration has been |
| * installed in the StyleManager.styles table by |
| * StyleManager.setStyleDeclaration(). |
| */ |
| mx_internal var selectorRefCount:int = 0; |
| |
| /** |
| * The order this CSSStyleDeclaration was added to its StyleManager. |
| * MatchStyleDeclarations has to return the declarations in the order |
| * they were declared |
| */ |
| public var selectorIndex:int = 0; |
| |
| /** |
| * @private |
| * Array that specifies the names of the events declared |
| * by this CSS style declaration. |
| * This Array is used by the <code>StyleProtoChain.initObject()</code> |
| * method to register the effect events with the Effect manager. |
| */ |
| mx_internal var effects:Array; |
| |
| /** |
| * @private |
| * reference to StyleManager |
| */ |
| private var styleManager:IStyleManager2; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // defaultFactory |
| //---------------------------------- |
| |
| private var _defaultFactory:Function; |
| |
| [Inspectable(environment="none")] |
| |
| /** |
| * This function, if it isn't <code>null</code>, |
| * is usually autogenerated by the MXML compiler. |
| * It produce copies of a plain Object, such as |
| * <code>{ leftMargin: 10, rightMargin: 10 }</code>, |
| * containing name/value pairs for style properties; the object is used |
| * to build a node of the prototype chain for looking up style properties. |
| * |
| * <p>If this CSSStyleDeclaration is owned by a UIComponent |
| * written in MXML, this function encodes the style attributes |
| * that were specified on the root tag of the component definition.</p> |
| * |
| * <p>If the UIComponent was written in ActionScript, |
| * this property is <code>null</code>.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get defaultFactory():Function |
| { |
| return _defaultFactory; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set defaultFactory(f:Function):void |
| { |
| _defaultFactory = f; |
| } |
| |
| //---------------------------------- |
| // factory |
| //---------------------------------- |
| |
| private var _factory:Function; |
| |
| [Inspectable(environment="none")] |
| |
| /** |
| * This function, if it isn't <code>null</code>, |
| * is usually autogenerated by the MXML compiler. |
| * It produce copies of a plain Object, such as |
| * <code>{ leftMargin: 10, rightMargin: 10 }</code>, |
| * containing name/value pairs for style properties; the object is used |
| * to build a node of the prototype chain for looking up style properties. |
| * |
| * <p>If this CSSStyleDeclaration is owned by a UIComponent, |
| * this function encodes the style attributes that were specified in MXML |
| * for an instance of that component.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get factory():Function |
| { |
| return _factory; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set factory(f:Function):void |
| { |
| _factory = f; |
| } |
| |
| //---------------------------------- |
| // overrides |
| //---------------------------------- |
| |
| private var _overrides:Object; |
| |
| /** |
| * If the <code>setStyle()</code> method is called on a UIComponent or CSSStyleDeclaration |
| * at run time, this object stores the name/value pairs that were set; |
| * they override the name/value pairs in the objects produced by |
| * the methods specified by the <code>defaultFactory</code> and |
| * <code>factory</code> properties. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get overrides():Object |
| { |
| return _overrides; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set overrides(o:Object):void |
| { |
| _overrides = o; |
| } |
| |
| //---------------------------------- |
| // selector |
| //---------------------------------- |
| |
| private var _selector:CSSSelector; |
| |
| /** |
| * This property is the base selector of a potential chain of selectors |
| * and conditions that are used to match CSS style declarations to |
| * components. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function get selector():CSSSelector |
| { |
| return _selector; |
| } |
| |
| public function set selector(value:CSSSelector):void |
| { |
| _selector = value; |
| _selectorString = null; |
| } |
| |
| //---------------------------------- |
| // selectorString |
| //---------------------------------- |
| |
| private var _selectorString:String; |
| |
| /** |
| * Legacy support for setting a Flex 3 styled selector string after |
| * the construction of a style declaration. Only universal class selectors |
| * or simple type selectors are supported. Note that this style declaration |
| * is not automatically registered with the StyleManager when using this |
| * API. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| mx_internal function get selectorString():String |
| { |
| if (_selectorString == null && _selector != null) |
| _selectorString = _selector.toString(); |
| |
| return _selectorString; |
| } |
| |
| mx_internal function set selectorString(value:String):void |
| { |
| // For the legacy API, the first argument is either a simple |
| // type selector or a universal class selector |
| if (value.charAt(0) == ".") |
| { |
| var condition:CSSCondition = new CSSCondition(CSSConditionKind.CLASS, value.substr(1)); |
| _selector = new CSSSelector("", [condition]); |
| } |
| else |
| { |
| _selector = new CSSSelector(value); |
| } |
| |
| _selectorString = value; |
| } |
| |
| //---------------------------------- |
| // specificity |
| //---------------------------------- |
| |
| /** |
| * Determines the order of precedence when applying multiple style |
| * declarations to a component. If style declarations are of equal |
| * precedence, the last one wins. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function get specificity():int |
| { |
| return _selector ? _selector.specificity : 0; |
| } |
| |
| //---------------------------------- |
| // subject |
| //---------------------------------- |
| |
| /** |
| * The subject describes the name of a component that may be a potential |
| * match for this style declaration. The subject is determined as right |
| * most simple type selector in a potential chain of selectors. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function get subject():String |
| { |
| if (_selector != null) |
| { |
| // Check for an implicit universal selector which omits * |
| // for the subject but includes conditions. |
| if (_selector.subject == "" && _selector.conditions) |
| return "*"; |
| else |
| return _selector.subject; |
| } |
| |
| return null; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Determines whether the selector chain for this style declaration makes |
| * use of a pseudo condition. |
| */ |
| mx_internal function getPseudoCondition():String |
| { |
| return (selector != null) ? selector.getPseudoCondition() : null; |
| } |
| |
| /** |
| * @private |
| * Determines whether this style declaration has an advanced selector. |
| */ |
| mx_internal function isAdvanced():Boolean |
| { |
| if (selector != null) |
| { |
| if (selector.ancestor) |
| { |
| return true; |
| } |
| else if (selector.conditions) |
| { |
| if (subject != "*" && subject != "global") |
| { |
| return true; |
| } |
| |
| for each (var condition:CSSCondition in selector.conditions) |
| { |
| if (condition.kind != CSSConditionKind.CLASS) |
| { |
| return true; |
| } |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Determines whether this style declaration applies to the given component |
| * based on a match of the selector chain. |
| * |
| * @param object The component to match the style declaration against. |
| * |
| * @return true if this style declaration applies to the component, |
| * otherwise false. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function matchesStyleClient(object:IAdvancedStyleClient):Boolean |
| { |
| return (selector != null) ? selector.matchesStyleClient(object) : false; |
| } |
| |
| /** |
| * Determine if the properties of this style declaration are the same as the the properties of a specified |
| * style declaration. |
| * |
| * @param styleDeclaration the style declaration to compare. |
| * |
| * @return true if the styleDeclaration is considered equal to this declaration. |
| */ |
| mx_internal function equals(styleDeclaration:CSSStyleDeclaration):Boolean |
| { |
| if (styleDeclaration == null) |
| return false; |
| |
| // test in order of most likey to be different. |
| |
| var obj:Object; // loop variable |
| |
| // overrides |
| if (ObjectUtil.compare(overrides, styleDeclaration.overrides) != 0) |
| return false; |
| |
| // factory |
| if ((factory == null && styleDeclaration.factory != null) || |
| (factory != null && styleDeclaration.factory == null)) |
| return false; |
| |
| if (factory != null) |
| { |
| if (ObjectUtil.compare(new factory(), new styleDeclaration.factory()) != 0) |
| return false; |
| } |
| |
| // defaultFactory |
| if ((defaultFactory == null && styleDeclaration.defaultFactory != null) || |
| (defaultFactory != null && styleDeclaration.defaultFactory == null)) |
| return false; |
| |
| if (defaultFactory != null) |
| { |
| if (ObjectUtil.compare(new defaultFactory(), |
| new styleDeclaration.defaultFactory()) != 0) |
| return false; |
| } |
| |
| // effects |
| if (ObjectUtil.compare(effects, styleDeclaration.mx_internal::effects)) |
| return false; |
| |
| return true; |
| } |
| |
| |
| /** |
| * Gets the value for a specified style property, |
| * as determined solely by this CSSStyleDeclaration. |
| * |
| * <p>The returned value may be of any type.</p> |
| * |
| * <p>The values <code>null</code>, <code>""</code>, <code>false</code>, |
| * <code>NaN</code>, and <code>0</code> are all valid style values, |
| * but the value <code>undefined</code> is not; it indicates that |
| * the specified style is not set on this CSSStyleDeclaration. |
| * You can use the method <code>StyleManager.isValidStyleValue()</code> |
| * to test the value that is returned.</p> |
| * |
| * @param styleProp The name of the style property. |
| * |
| * @return The value of the specified style property if set, |
| * or <code>undefined</code> if not. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function getStyle(styleProp:String):* |
| { |
| var o:*; |
| var v:*; |
| |
| // First look in the overrides, in case setStyle() |
| // has been called on this CSSStyleDeclaration. |
| if (overrides) |
| { |
| // If the property exists in our overrides, but |
| // has 'undefined' as its value, it has been |
| // cleared from this stylesheet so return |
| // undefined. |
| if (styleProp in overrides && |
| overrides[styleProp] === undefined) |
| return undefined; |
| |
| v = overrides[styleProp]; |
| if (v !== undefined) // must use !== |
| return v; |
| } |
| |
| // Next look in the style object that this CSSStyleDeclaration's |
| // factory function produces; it contains styles that |
| // were specified in an instance tag of an MXML component |
| // (if this CSSStyleDeclaration is attached to a UIComponent). |
| if (factory != null) |
| { |
| factory.prototype = {}; |
| o = new factory(); |
| v = o[styleProp]; |
| if (v !== undefined) // must use !== |
| return v; |
| } |
| |
| // Next look in the style object that this CSSStyleDeclaration's |
| // defaultFactory function produces; it contains styles that |
| // were specified on the root tag of an MXML component. |
| if (defaultFactory != null) |
| { |
| defaultFactory.prototype = {}; |
| o = new defaultFactory(); |
| v = o[styleProp]; |
| if (v !== undefined) // must use !== |
| return v; |
| } |
| |
| // Return undefined if the style isn't specified |
| // in any of these three places. |
| return undefined; |
| } |
| |
| /** |
| * Sets a style property on this CSSStyleDeclaration. |
| * |
| * @param styleProp The name of the style property. |
| * |
| * @param newValue The value of the style property. |
| * The value may be of any type. |
| * The values <code>null</code>, <code>""</code>, <code>false</code>, |
| * <code>NaN</code>, and <code>0</code> are all valid style values, |
| * but the value <code>undefined</code> is not. |
| * Setting a style property to the value <code>undefined</code> |
| * is the same as calling the <code>clearStyle()</code> method. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function setStyle(styleProp:String, newValue:*):void |
| { |
| var oldValue:Object = getStyle(styleProp); |
| var regenerate:Boolean = false; |
| |
| // If this CSSStyleDeclaration didn't previously have a factory, |
| // defaultFactory, or overrides object, then this CSSStyleDeclaration |
| // hasn't been added to anyone's proto chain. In that case, we |
| // need to regenerate everyone's proto chain. |
| if (selectorRefCount > 0 && |
| factory == null && |
| defaultFactory == null && |
| !overrides && |
| (oldValue !== newValue)) // must be !== |
| { |
| regenerate = true; |
| } |
| |
| if (newValue !== undefined) // must be !== |
| { |
| setLocalStyle(styleProp, newValue); |
| } |
| else |
| { |
| if (newValue == oldValue) |
| return; |
| setLocalStyle(styleProp, newValue); |
| } |
| |
| var sms:Array = SystemManagerGlobals.topLevelSystemManagers; |
| var n:int = sms.length; |
| var i:int; |
| |
| // Type as Object to avoid dependency on SystemManager. |
| var sm:ISystemManager; |
| var cm:Object; |
| |
| if (regenerate) |
| { |
| // Regenerate all the proto chains |
| // for all objects in the application. |
| for (i = 0; i < n; i++) |
| { |
| sm = sms[i]; |
| cm = sm.getImplementation("mx.managers::ISystemManagerChildManager"); |
| cm.regenerateStyleCache(true); |
| } |
| } |
| |
| for (i = 0; i < n; i++) |
| { |
| sm = sms[i]; |
| cm = sm.getImplementation("mx.managers::ISystemManagerChildManager"); |
| cm.notifyStyleChangeInChildren(styleProp, true); |
| } |
| } |
| |
| /** |
| * @private |
| * Sets a style property on this CSSStyleDeclaration. |
| * |
| * @param styleProp The name of the style property. |
| * |
| * @param newValue The value of the style property. |
| * The value may be of any type. |
| * The values <code>null</code>, <code>""</code>, <code>false</code>, |
| * <code>NaN</code>, and <code>0</code> are all valid style values, |
| * but the value <code>undefined</code> is not. |
| * Setting a style property to the value <code>undefined</code> |
| * is the same as calling <code>clearStyle()</code>. |
| */ |
| mx_internal function setLocalStyle(styleProp:String, value:*):void |
| { |
| var oldValue:Object = getStyle(styleProp); |
| |
| // If setting to undefined, clear the style attribute. |
| if (value === undefined) // must use === |
| { |
| clearStyleAttr(styleProp); |
| return; |
| } |
| |
| var o:Object; |
| |
| // If the value is a String of the form "#FFFFFF" or "red", |
| // then convert it to a RGB color uint (e.g.: 0xFFFFFF). |
| if (value is String) |
| { |
| if (!styleManager) |
| styleManager = Singleton.getInstance("mx.styles::IStyleManager2") as IStyleManager2; |
| var colorNumber:Number = styleManager.getColorName(value); |
| if (colorNumber != NOT_A_COLOR) |
| value = colorNumber; |
| } |
| |
| // If the new value for styleProp is different from the one returned |
| // from the defaultFactory function, then store the new value on the |
| // overrides object. That way, future clones will get the new value. |
| if (defaultFactory != null) |
| { |
| o = new defaultFactory(); |
| if (o[styleProp] !== value) // must use !== |
| { |
| if (!overrides) |
| overrides = {}; |
| overrides[styleProp] = value; |
| } |
| else if (overrides) |
| { |
| delete overrides[styleProp]; |
| } |
| } |
| |
| // If the new value for styleProp is different from the one returned |
| // from the factory function, then store the new value on the |
| // overrides object. That way, future clones will get the new value. |
| if (factory != null) |
| { |
| o = new factory(); |
| if (o[styleProp] !== value) // must use !== |
| { |
| if (!overrides) |
| overrides = {}; |
| overrides[styleProp] = value; |
| } |
| else if (overrides) |
| { |
| delete overrides[styleProp]; |
| } |
| } |
| |
| if (defaultFactory == null && factory == null) |
| { |
| if (!overrides) |
| overrides = {}; |
| overrides[styleProp] = value; |
| } |
| |
| // Update all clones of this style sheet. |
| updateClones(styleProp, value); |
| |
| } |
| |
| /** |
| * Clears a style property on this CSSStyleDeclaration. |
| * |
| * This is the same as setting the style value to <code>undefined</code>. |
| * |
| * @param styleProp The name of the style property. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function clearStyle(styleProp:String):void |
| { |
| public::setStyle(styleProp, undefined); |
| } |
| |
| /** |
| * @private |
| */ |
| mx_internal function createProtoChainRoot():Object |
| { |
| var root:Object = {}; |
| |
| // If there's a defaultFactory for this style sheet, |
| // then add the object it produces to the root. |
| if (defaultFactory != null) |
| { |
| defaultFactory.prototype = root; |
| root = new defaultFactory(); |
| } |
| |
| // If there's a factory for this style sheet, |
| // then add the object it produces to the root. |
| if (factory != null) |
| { |
| factory.prototype = root; |
| root = new factory(); |
| } |
| |
| clones[ root ] = 1; |
| |
| return root; |
| } |
| |
| /** |
| * @private |
| * |
| * The order of nodes in the prototype chain: |
| * |
| * 1. parent style default factories |
| * 2. this default factory |
| * 3. parent style factories |
| * 4. parent style overrides |
| * 5. this factory |
| * 6. this overrides |
| * |
| * Where a parent style is a style with the same selector as this |
| * style but in a parent style manager. |
| * |
| */ |
| mx_internal function addStyleToProtoChain(chain:Object, |
| target:DisplayObject, |
| filterMap:Object = null):Object |
| { |
| var nodeAddedToChain:Boolean = false; |
| var originalChain:Object = chain; |
| |
| // Get a list of parent style declarations for this selector. |
| var parentStyleDeclarations:Vector.<CSSStyleDeclaration> = new Vector.<CSSStyleDeclaration>(); |
| var styleParent:IStyleManager2 = styleManager.parent; |
| while (styleParent) |
| { |
| var parentStyle:CSSStyleDeclaration = styleParent.getStyleDeclaration(selectorString); |
| if (parentStyle) |
| parentStyleDeclarations.unshift(parentStyle); |
| |
| styleParent = styleParent.parent; |
| } |
| |
| // #1. Add parent's default styles. Topmost parent is added to the chain first. |
| for each (var style:CSSStyleDeclaration in parentStyleDeclarations) |
| { |
| // If there's a defaultFactory for this style sheet, |
| // then add the object it produces to the chain. |
| if (style.defaultFactory != null) |
| chain = style.addDefaultStyleToProtoChain(chain, target, filterMap); |
| } |
| |
| // #2. Add this style's defaultFactory to the proto chain. |
| if (defaultFactory != null) |
| chain = addDefaultStyleToProtoChain(chain, target, filterMap); |
| |
| // #3 and #4. Add parent's factory styles and overrides. |
| var addedParentStyleToProtoChain:Boolean = false; |
| for each (style in parentStyleDeclarations) |
| { |
| if (style.factory != null || style.overrides != null) |
| { |
| chain = style.addFactoryAndOverrideStylesToProtoChain(chain, target, filterMap); |
| addedParentStyleToProtoChain = true; |
| } |
| } |
| |
| // #5 and #6. Add this factory style and overrides. |
| var inChain:Object = chain; |
| if (factory != null || overrides != null) |
| { |
| chain = addFactoryAndOverrideStylesToProtoChain(chain, target, filterMap); |
| if (inChain != chain) |
| nodeAddedToChain = true; |
| } |
| |
| // Here we check if we need to add an empty node to the chain for clone |
| // purposes. If there are parent nodes between this defaultFactory and |
| // this factory, then we can't use the defaultFactory node as the clone |
| // since overrides could get blocked by parent styles. |
| // First we check if we have a defaultFactory and we didn't add a factory |
| // or override node to the chain. If we have a factory or override node |
| // then we will just use that. |
| if (defaultFactory != null && !nodeAddedToChain) |
| { |
| // Now we know we have a default factory node and no factory or override |
| // nodes. We can use the default factory as a clone on the chain if there |
| // are no parent styles below it on the proto chain. |
| // Otherwise create an empty node so overrides and be added later. |
| if (addedParentStyleToProtoChain) |
| { |
| // There are parent styles so create an empty node. |
| emptyObjectFactory.prototype = chain; |
| chain = new emptyObjectFactory(); |
| emptyObjectFactory.prototype = null; |
| } |
| |
| nodeAddedToChain = true; |
| } |
| |
| if (nodeAddedToChain) |
| clones[chain] = 1; |
| |
| return chain; |
| } |
| |
| /** |
| * @private |
| */ |
| mx_internal function addDefaultStyleToProtoChain(chain:Object, |
| target:DisplayObject, |
| filterMap:Object = null):Object |
| { |
| // If there's a defaultFactory for this style sheet, |
| // then add the object it produces to the chain. |
| if (defaultFactory != null) |
| { |
| var originalChain:Object = chain; |
| if (filterMap) |
| { |
| chain = {}; |
| } |
| |
| defaultFactory.prototype = chain; |
| chain = new defaultFactory(); |
| defaultFactory.prototype = null; |
| |
| if (filterMap) |
| chain = applyFilter(originalChain, chain, filterMap); |
| } |
| |
| return chain; |
| } |
| |
| /** |
| * @private |
| */ |
| mx_internal function addFactoryAndOverrideStylesToProtoChain(chain:Object, |
| target:DisplayObject, |
| filterMap:Object = null):Object |
| { |
| var originalChain:Object = chain; |
| if (filterMap) |
| { |
| chain = {}; |
| } |
| |
| // If there's a factory for this style sheet, |
| // then add the object it produces to the chain. |
| if (factory != null) |
| { |
| factory.prototype = chain; |
| chain = new factory(); |
| factory.prototype = null; |
| } |
| |
| // If someone has called setStyle() on this CSSStyleDeclaration, |
| // then some of the values returned from the factory are |
| // out-of-date. Overwrite them with the up-to-date values. |
| if (overrides) |
| { |
| // Before we add our overrides to the object at the head of |
| // the chain, make sure that we added an object at the head |
| // of the chain. |
| if (factory == null) |
| { |
| emptyObjectFactory.prototype = chain; |
| chain = new emptyObjectFactory(); |
| emptyObjectFactory.prototype = null; |
| } |
| |
| for (var p:String in overrides) |
| { |
| if (overrides[p] === undefined) |
| delete chain[p]; |
| else |
| chain[p] = overrides[p]; |
| } |
| } |
| |
| if (filterMap) |
| { |
| if (factory != null || overrides) |
| chain = applyFilter(originalChain, chain, filterMap); |
| else |
| chain = originalChain; |
| } |
| |
| if (factory != null || overrides) |
| clones[chain] = 1; |
| |
| return chain; |
| } |
| |
| |
| /** |
| * @private |
| */ |
| mx_internal function applyFilter(originalChain:Object, chain:Object, filterMap:Object):Object |
| { |
| var filteredChain:Object = {}; |
| // Create an object on the head of the chain using the original chain |
| emptyObjectFactory.prototype = originalChain; |
| filteredChain = new emptyObjectFactory(); |
| emptyObjectFactory.prototype = null; |
| |
| for (var i:String in chain) |
| { |
| if (filterMap[i] != null) |
| { |
| filteredChain[filterMap[i]] = chain[i]; |
| } |
| } |
| |
| chain = filteredChain; |
| chain[FILTERMAP_PROP] = filterMap; |
| |
| return chain; |
| } |
| |
| /** |
| * @private |
| */ |
| mx_internal function clearOverride(styleProp:String):void |
| { |
| if (overrides && overrides[styleProp] !== undefined) |
| delete overrides[styleProp]; |
| } |
| |
| /** |
| * @private |
| */ |
| private function clearStyleAttr(styleProp:String):void |
| { |
| // Put "undefined" into our overrides Array |
| if (!overrides) |
| overrides = {}; |
| overrides[styleProp] = undefined; |
| |
| // Remove the property from all our clones |
| for (var clone:* in clones) |
| { |
| delete clone[styleProp]; |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| mx_internal function updateClones(styleProp:String, value:*):void |
| { |
| // Update all clones of this style sheet. |
| for (var clone:* in clones) |
| { |
| var cloneFilter:Object = clone[FILTERMAP_PROP]; |
| if (cloneFilter) |
| { |
| if (cloneFilter[styleProp] != null) |
| { |
| clone[cloneFilter[styleProp]] = value; |
| } |
| } |
| else |
| { |
| clone[styleProp] = value; |
| } |
| } |
| } |
| |
| } |
| |
| } |