| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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.accessibility |
| { |
| |
| import flash.accessibility.Accessibility; |
| import flash.accessibility.AccessibilityImplementation; |
| import flash.accessibility.AccessibilityProperties; |
| import flash.display.DisplayObject; |
| import flash.display.DisplayObjectContainer; |
| import flash.events.Event; |
| import flash.system.ApplicationDomain; |
| |
| import mx.core.IFlexModuleFactory; |
| import mx.core.UIComponent; |
| import mx.core.mx_internal; |
| import mx.managers.ISystemManager; |
| import mx.resources.ResourceManager; |
| import mx.resources.IResourceManager; |
| |
| use namespace mx_internal; |
| |
| [ResourceBundle("controls")] |
| |
| /** |
| * AccImpl is Flex's base accessibility implementation class |
| * for MX and Spark components. |
| * |
| * <p>It is a subclass of the Flash Player's |
| * AccessibilityImplementation class.</p> |
| * |
| * <p>When an MX or Spark component is created, |
| * its <code>accessibilityImplementation</code> property |
| * is set to an instance of a subclass of this class. |
| * The Flash Player then uses this object to allow MSAA clients |
| * such as screen readers to see and manipulate the component. |
| * See the flash.accessibility.AccessibilityImplementation class |
| * for additional information about accessibility implementation |
| * classes and MSAA.</p> |
| * |
| * <p><b>Children</b></p> |
| * |
| * <p>The Flash Player does not support |
| * a true hierarchy of accessible objects. |
| * If a DisplayObject has an <code>accessibilityImplementation</code> object, |
| * then the <code>accessibilityImplementation</code> objects |
| * of its children are ignored. |
| * However, the Player does allow a component's accessibility implementation class |
| * to expose MSAA information for its internal parts. |
| * (For example, a List exposes MSAA information about its items.)</p> |
| * |
| * <p>The number of children (internal parts) |
| * and the child IDs used to identify them |
| * are determined by the <code>getChildIDArray()</code> method. |
| * In the Player's AccessibilityImplementation base class, |
| * this method simply returns <code>null</code>. |
| * Flex's AccImpl class overrides it to return an empty array. |
| * It also provides a protected utility method, |
| * <code>createChildIDArray()</code> which subclasses with internal parts |
| * can use in their overrides.</p> |
| * |
| * <p><b>Role</b></p> |
| * |
| * <p>The MSAA Role of a component and its internal parts |
| * is determined by the <code>get_accRole()</code> method. |
| * In the Player's AccessibilityImplementation base class, |
| * this method throws a runtime error, |
| * since subclasses are expected to override it. |
| * Flex's AccImpl class has a protected <code>role</code> property |
| * which subclasses generally set in their constructor, |
| * and it overrides <code>get_accRole()</code> to return this property.</p> |
| * |
| * <p><b>Name</b></p> |
| * |
| * <p>The MSAA Name of a component and its internal parts |
| * is determined by the <code>get_accName()</code> method. |
| * In the Player's AccessibilityImplementation base class, |
| * this method simply returns <code>null</code>. |
| * Flex's AccImpl class overrides it to construct a name as follows, |
| * starting with an empty string |
| * and separating added portions with a single space: |
| * <ul> |
| * <li>If a simple child (e.g., combo or list box item) |
| * is being requested, only the child's default name is returned. |
| * The rest of the steps below apply only to the component itself |
| * (childID 0).</li> |
| * <li>If the component is inside a Form: |
| * <ul> |
| * <li>If the Form has a FormHeading and the component is inside |
| * a FormItem, the heading text is added. |
| * Developers wishing to avoid this should set the |
| * <code>accessibilityName</code> of the FormHeading |
| * to a space (" ").</li> |
| * <li>If the field is required, the locale-dependent string |
| * "required field" is added.</li> |
| * <li>If the component is inside a FormItem, |
| * the FormItem label text is added. |
| * Developers wishing to avoid this should set the |
| * <code>accessibilityName</code> of the FormItem |
| * to a space (" ").</li> |
| * </ul></li> |
| * <li>The component's name is then determined thus: |
| * <ul> |
| * <li>If the component's <code>accessibilityName</code> |
| * (i.e., <code>accessibilityProperties.name</code>) is a space, |
| * no component name is added.</li> |
| * <li>Otherwise, if the component's name is specified |
| * (i.e., is not null and not empty) then it is added.</li> |
| * <li>Otherwise, a protected <code>getName()</code> method, |
| * defined by AccImpl and implemented by each subclass, |
| * is called to provide a default name. |
| * (For example, ButtonAccImpl implements <code>getName()</code> |
| * to specify that a Button's default name is the label that it displays.) |
| * If not empty, the return value of <code>getName()</code> is added.</li> |
| * <li>Otherwise (if <code>getName()</code> returned empty), |
| * if the component's <code>toolTip</code> property is set, |
| * that String is added.</li> |
| * <li>If the component's <code>errorString</code> property is set, |
| * that String is added.</li> |
| * </ul></li> |
| * </ul></p> |
| * |
| * <p><b>Description</b></p> |
| * |
| * <p>The MSAA Description is determined solely by a component's |
| * <code>accessibilityProperties</code> object and not by its |
| * <code>accessibilityImplementation</code> object. |
| * Therefore there is no logic in AccessibilityImplementation or AccImpl |
| * or any subclasses of AccImpl related to the description. |
| * The normal way to set the description in Flex is via the |
| * <code>accessibilityDescription</code> property on UIComponent, |
| * which simply sets <code>accessibilityProperties.description</code>.</p> |
| * |
| * <p><b>State</b></p> |
| * |
| * <p>The MSAA State of a component and its internal parts |
| * is determined by the <code>get_accState()</code> method. |
| * In the Player's AccessibilityImplementation base class, |
| * this method throws a runtime error, |
| * since subclasses are expected to override it. |
| * Flex's AccImpl class does not override it, |
| * but provides a protected utility method, <code>getState()</code>, |
| * for subclasses to use in their overrides. |
| * The <code>getState()</code> method determines the state |
| * as a combination of |
| * <ul> |
| * <li>STATE_SYSTEM_UNAVAILABLE |
| * (when enabled is false on this component or any ancestor)</li> |
| * <li>STATE_SYSTEM_FOCUSABLE</li> |
| * <li>STATE_SYSTEM_FOCUSED (when the component itself is focused, |
| * not set for any subparts the component may have)</li> |
| * </ul> |
| * Note that by default all components are assumed to be focusable |
| * and thus the accessibility implementation classes for non-focusable |
| * components like Label must clear this state flag. |
| * When a component has a state of unavailable, |
| * the focusable state is removed by the accessibility implementation class.</p> |
| * |
| * <p><b>Value</b></p> |
| * |
| * <p>The MSAA Value of a component and its internal parts |
| * is determined by the <code>get_accValue()</code> method. |
| * In the Player's AccessibilityImplementation base class, |
| * this method simply returns <code>null</code>. |
| * Flex's AccImpl class does not override it, |
| * but subclasses for components like TextInput do.</p> |
| * |
| * <p><b>Location</b></p> |
| * |
| * <p>The MSAA Location for a component's internal parts, |
| * but not the component itself, |
| * is determined by the <code>get_accLocation()</code> method. |
| * This method is never called with a childID of 0; |
| * instead, the Flash Player determines the MSAA Location of a component |
| * based on its bounding rectangle as determined by <code>getBounds()</code>. |
| * Flex's AccImpl class does not override this method, |
| * but subclasses for components with internal parts do.</p> |
| * |
| * <p><b>Default Action</b></p> |
| * |
| * <p>The MSAA DefaultAction for a component and its internal parts |
| * is determined by the <code>get_accDefaultAction()</code> method. |
| * In the Player's AccessibilityImplementation base class, |
| * this method simply returns <code>null</code>. |
| * Flex's AccImpl class does not override it, |
| * but subclasses with default actions do. |
| * These subclasses also override AccessibilityImplementation's |
| * <code>accDoDefaultAction()</code> method |
| * to perform the default action that they advertise.</p> |
| * |
| * <p><b>Other</b></p> |
| * |
| * <p>The MSAA events EVENT_OBJECT_SHOW and EVENT_OBJECT_HIDE |
| * are sent when the object is shown or hidden. |
| * The corresponding states for these are covered by the Flash Player |
| * which does not render any MSAA components that are hidden. |
| * When the component is shown the states mentioned for AccImpl |
| * are used.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public class AccImpl extends AccessibilityImplementation |
| { |
| include "../core/Version.as"; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Class methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * |
| * Get the definition of a class, namespace, or function. The definition is |
| * obtained from the specified moduleFactory. If there is no moduleFactory, |
| * then the definition is looked up in ApplicationDomain.currentDomain. |
| * |
| * @param name name of the class, namespace, or function to get. |
| * @param moduleFactory The moduleFactory that specifies the application |
| * domain to use to find the name. If moduleFactory is null, then |
| * ApplicationDomain.currentDomain is used as a fall back. |
| * |
| * return a class, namespace, or function. |
| * |
| */ |
| mx_internal static function getDefinition(name:String, moduleFactory:IFlexModuleFactory):Object |
| { |
| var currentDomain:ApplicationDomain; |
| |
| // Use the given module factory to look for the domain. If the module |
| // factory is null then fall back to Application.currentDomain. |
| if (moduleFactory) |
| currentDomain = moduleFactory.info()["currentDomain"]; |
| else |
| currentDomain = ApplicationDomain.currentDomain; |
| |
| if (currentDomain.hasDefinition(name)) |
| return currentDomain.getDefinition(name); |
| |
| return null; |
| } |
| |
| /** |
| * @private |
| * Data structure internal to the following getDefinitions() function: |
| * These are the defined types and the classes they map to. |
| * Structure of an entry: typeName: className, className, ... |
| * Newest classes first in case this helps performance for newer code. |
| */ |
| private static var typeMap:Object = |
| { |
| "Container": [ |
| "spark.components.Scroller", |
| "spark.components.SkinnableContainer", |
| "spark.components.supportClasses.GroupBase", |
| "mx.core.Container" |
| ], |
| "Form": [ |
| "spark.components.Form", |
| "mx.containers.Form" |
| ], |
| "FormHeading": [ |
| "spark.components.FormHeading", |
| "mx.containers.FormHeading" |
| ], |
| "FormItem": [ |
| "spark.components.FormItem", |
| "mx.containers.FormItem" |
| ], |
| "FormItemLabel": [ |
| // No Spark equivalent for this. |
| "mx.controls.FormItemLabel" |
| ], |
| "ScrollBar": [ |
| "mx.controls.scrollClasses.ScrollBar", |
| "spark.components.supportClasses.ScrollBarBase" |
| ] |
| }; |
| |
| /** |
| * @private |
| * |
| * Get the class definition(s) corresponding to a predefined type. The |
| * definitions are looked up in the specified moduleFactory. If no |
| * moduleFactory is given, then the definitions are looked up in |
| * ApplicationDomain.currentDomain. |
| * The types are defined for this function by the above typeMap structure. |
| * |
| * @param typeName Type of the class(es) to get. |
| * Passing an invalid typeName will raise an error. |
| * @param moduleFactory The moduleFactory that specifies the application |
| * domain to use to find the definitions. If moduleFactory is null, then |
| * ApplicationDomain.currentDomain is used as a fall back. |
| * |
| * return The list of definitions found. |
| * |
| */ |
| mx_internal static function getDefinitions(typeName:String, moduleFactory:IFlexModuleFactory):Array |
| { |
| var defs:Array = []; |
| var thisClass:Class; |
| for each (var className:String in typeMap[typeName]) |
| { |
| thisClass = Class(getDefinition(className, moduleFactory)); |
| if (thisClass) |
| defs.push(thisClass); |
| } |
| return defs; |
| } |
| |
| /** |
| * @private |
| * |
| * Find out if an object is one of a set of classes, and return the |
| * class if so. |
| * |
| * @param obj Object to check against the list of classes. |
| * @param classes List of classes to check against. |
| * |
| * return The found class or null. |
| * |
| */ |
| mx_internal static function getMatchingDefinition(obj:Object, classes:Array):Class |
| { |
| if (!obj) |
| return null; |
| for each (var thisClass:Class in classes) |
| { |
| if (obj is thisClass) |
| return thisClass; |
| } |
| return null; |
| } |
| |
| /** |
| * @private |
| * Finds an ancestor of a component matching a rule. |
| * |
| * @param component The component from which to start (included in the checking too). |
| * @param f The function that returns true ffor a match. |
| * The function should be defined as function (component:UIComponent):Boolean. |
| * |
| * @return The UIComponent first matching the rule or null if none do. |
| */ |
| mx_internal static function findMatchingAncestor(component:Object, f:Function):UIComponent |
| { |
| while (component && (component is UIComponent) |
| && !(component is ISystemManager) && component != UIComponent(component).root) |
| { |
| if (f(component)) |
| return UIComponent(component); |
| component = UIComponent(component).parent; |
| } |
| return null; |
| } |
| |
| /** |
| * Returns true if an ancestor of the component has enabled set to false. |
| * The given component itself is not checked. |
| * |
| * @param component The UIComponent to check for a disabled ancestor. |
| * |
| * @return true if the component has a disabled ancestor. |
| */ |
| public static function isAncestorDisabled(component:UIComponent):Boolean |
| { |
| return findMatchingAncestor(component.parent, isComponentDisabled) != null; |
| } |
| |
| /** |
| * @private |
| * Check if a component is disabled. |
| * Used in calls to findMatchingAncestor(). |
| * |
| * @param component The UIComponent to check. |
| * |
| * @return true if the component is disabled. |
| */ |
| mx_internal static function isComponentDisabled(component:UIComponent):Boolean |
| { |
| return !component.enabled; |
| } |
| |
| /** |
| * @private |
| * Check if a component is a form.. |
| * Used in calls to findMatchingAncestor(). |
| * |
| * @param component The UIComponent to check. |
| * |
| * @return true if the component is a form.. |
| */ |
| mx_internal static function isForm(component:UIComponent):Boolean |
| { |
| var formClasses:Array = getDefinitions("Form", component.moduleFactory); |
| return getMatchingDefinition(component, formClasses) != null; |
| } |
| |
| /** |
| * @private |
| * Check if a component is a form heading. |
| * Used in calls to findMatchingAncestor(). |
| * |
| * @param component The UIComponent to check. |
| * |
| * @return true if the component is a form heading. |
| */ |
| mx_internal static function isFormHeading(component:UIComponent):Boolean |
| { |
| var formHeadingClasses:Array = getDefinitions("FormHeading", component.moduleFactory); |
| return getMatchingDefinition(component, formHeadingClasses) != null; |
| } |
| |
| /** |
| * @private |
| * Check if a component is a form item (meaning a FormItem class). |
| * Used in calls to findMatchingAncestor(). |
| * |
| * @param component The UIComponent to check. |
| * |
| * @return true if the component is a FormItem. |
| */ |
| mx_internal static function isFormItem(component:UIComponent):Boolean |
| { |
| var formItemClasses:Array = getDefinitions("FormItem", component.moduleFactory); |
| return getMatchingDefinition(component, formItemClasses) != null; |
| } |
| |
| /** |
| * Method for supporting Form Accessibility. |
| * Called from get_accName() in this AccImpl class. |
| * Also called from the UIComponentAccProps constructor. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public static function getFormName(component:UIComponent):String |
| { |
| // Return nothing if we are a container |
| if (getMatchingDefinition(component, getDefinitions("Container", component.moduleFactory))) |
| return ""; |
| |
| // Return nothing if we are not in a FormItem. |
| var formItem:UIComponent = findMatchingAncestor(component.parent, isFormItem); |
| if (!formItem) |
| return ""; |
| |
| // We're under a FormItem, so return the appropriate name for it. |
| var formName:String = determineFormItemString(formItem); |
| return formName; |
| } |
| |
| /** |
| * @private |
| * Method for supporting Form Accessibility. |
| */ |
| private static function joinWithSpace(s1:String,s2:String):String |
| { |
| // Single space treated as null so developers can override default name elements with " ". |
| if (s1 == " ") |
| s1 = ""; |
| if (s2 == " ") |
| s2 = ""; |
| if (s1 && s2) |
| s1 += " " +s2; |
| else if (s2) |
| s1 = s2; |
| // else we have non-empty s1 and empty s2, so do nothing. |
| return s1; |
| } |
| |
| /** |
| * @private |
| * Determine the string to be used as the part of a form field's name |
| * that represents the FormItem the field is in. |
| * TODO: This function has the side effect of setting |
| * accessibilityEnabled=false on one or more subparts of formItems that |
| * need this. |
| * Called from getFormName(), which is called from get_accName() and |
| * from the UIComponentAccProps constructor. |
| * |
| * @param formItem Object of type FormItem. Object is used here to avoid |
| * linking in FormItem. |
| */ |
| private static function determineFormItemString(formItem:Object):String |
| { |
| var formName:String = ""; |
| var resourceManager:IResourceManager = ResourceManager.getInstance(); |
| |
| // FormHeadings are sought as direct children of this formItem's parent. |
| var formItemParent:UIComponent = UIComponent(formItem.parent); |
| |
| // Figure out if we're in a Form first. |
| // As of Spark, the formItem parent itself may not be a Form class but |
| // may still descend from it. |
| if (findMatchingAncestor(formItemParent, isForm)) |
| { |
| // If we are located within a Form, then look for the most |
| // recent FormHeading that is a sibling of this FormItem. |
| // TODO: More complex form layouts may be possible in |
| // which a heading is not a sibling of all of its |
| // FormItems. For these, the heading will be omitted from |
| // the field name at this time. |
| var formHeadingClasses:Array = getDefinitions("FormHeading", formItem.moduleFactory); |
| var formItemIndex:int = formItemParent.getChildIndex(DisplayObject(formItem)); |
| for (var i:int = formItemIndex; i >= 0; i--) |
| { |
| var child:UIComponent = UIComponent(formItemParent.getChildAt(i)); |
| var formHeadingClass:Class = getMatchingDefinition(child, formHeadingClasses); |
| if (formHeadingClass) |
| { |
| // Accessible name if it exists, else label text. |
| if (formHeadingClass(child).accessibilityProperties) |
| formName = |
| formHeadingClass(child).accessibilityProperties.name; |
| if (formName == "") |
| formName = formHeadingClass(child).label; |
| break; |
| } |
| } |
| } |
| |
| // Add in "Required Field" text if we are a required field |
| if (formItem.required) |
| formName = joinWithSpace(formName, |
| resourceManager.getString("controls","requiredField")) |
| |
| // Add in the label from the formItem |
| // Accessible name if it exists, else label text. |
| var f:String = ""; |
| if (formItem.accessibilityProperties) |
| f = formItem.accessibilityProperties.name |
| if (f == "") |
| f = formItem.label; |
| formName = joinWithSpace(formName, f); |
| |
| // The purpose of the following lines is to |
| // make parts of this FormItem silent when they are |
| // incorporated into the field name. |
| // First try Spark skin parts that should be silent. |
| try |
| { |
| if (formItem.labelDisplay && formItem.labelDisplay.accessibilityEnabled) |
| formItem.labelDisplay.accessibilityEnabled = false; |
| } |
| catch (e:Error) |
| { |
| } |
| try |
| { |
| if (formItem.sequenceLabelDisplay && formItem.sequenceLabelDisplay.accessibilityEnabled) |
| formItem.sequenceLabelDisplay.accessibilityEnabled = false; |
| } |
| catch (e:Error) |
| { |
| } |
| try |
| { |
| if (formItem.helpContentGroup && formItem.helpContentGroup.accessibilityEnabled) |
| formItem.helpContentGroup.accessibilityEnabled = false; |
| } |
| catch (e:Error) |
| { |
| } |
| try |
| { |
| if (formItem.errorTextDisplay && formItem.errorTextDisplay.accessibilityEnabled) |
| formItem.errorTextDisplay.accessibilityEnabled = false; |
| } |
| catch (e:Error) |
| { |
| } |
| // Then one MX item that should be silent. |
| try |
| { |
| if (formItem.itemLabel && formItem.itemLabel.accessibilityEnabled) |
| formItem.itemLabel.accessibilityEnabled = false; |
| } |
| catch (e:Error) |
| { |
| } |
| |
| return formName; |
| } |
| |
| /** |
| * @private |
| * Determine whether a component is a SkinnableTextBase component whose |
| * accessibilityProperties are duplicated for a parent within the |
| * same control. |
| * |
| * @param component The component to check. |
| * |
| * @return true if the component meets the described condition.. |
| */ |
| private static function componentNeedsSkinnableTextBaseException(component:UIComponent):Boolean |
| { |
| // No accessibilityProperties, nothing to look for. |
| var myAccessibilityProperties:Object = component.accessibilityProperties; |
| if (!myAccessibilityProperties) |
| return false; |
| |
| // If the classes we're looking for don't exist, don't bother scanning. |
| var richEditableTextClass:Class = Class(getDefinition( |
| "spark.components.RichEditableText", |
| component.moduleFactory |
| )); |
| if (!richEditableTextClass || !(component is richEditableTextClass)) |
| return false; |
| var skinnableTextBaseClass:Class = Class(getDefinition( |
| "spark.components.supportClasses.SkinnableTextBase", |
| component.moduleFactory |
| )); |
| if (!skinnableTextBaseClass) |
| return false; |
| |
| // An anonymous function is needed here because it needs to know |
| // which accessibilityProperties object to check against. |
| var sharesMyAccessibilityProperties:Function = function(c:UIComponent):Boolean |
| { |
| return c.accessibilityProperties === myAccessibilityProperties; |
| }; |
| var matchingAncestor:UIComponent = findMatchingAncestor(component.parent, sharesMyAccessibilityProperties) |
| if (!matchingAncestor) |
| return false; |
| return matchingAncestor is skinnableTextBaseClass; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Constructor. |
| * |
| * @param master The UIComponent instance that this AccImpl instance |
| * is making accessible. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function AccImpl(master:UIComponent) |
| { |
| super(); |
| |
| this.master = master; |
| |
| stub = false; |
| |
| // Hook in UIComponentAccProps setup here! |
| if (!master.accessibilityProperties) |
| master.accessibilityProperties = new AccessibilityProperties(); |
| |
| // Hookup events to listen for |
| var events:Array = eventsToHandle; |
| if (events) |
| { |
| var n:int = events.length; |
| for (var i:int = 0; i < n; i++) |
| { |
| master.addEventListener(events[i], eventHandler); |
| } |
| } |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * A reference to the UIComponent instance that this AccImpl instance |
| * is making accessible. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected var master:UIComponent; |
| |
| /** |
| * Accessibility role of the component being made accessible. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected var role:uint; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * All subclasses must override this function by returning an array |
| * of strings of the events to listen for. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function get eventsToHandle():Array |
| { |
| return [ "errorStringChanged", "toolTipChanged", "show", "hide" ]; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Overridden methods: AccessibilityImplementation |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Returns the system role for the component. |
| * |
| * @param childID uint. |
| * |
| * @return Role associated with the component. |
| * |
| * @tiptext Returns the system role for the component |
| * @helpid 3000 |
| */ |
| override public function get_accRole(childID:uint):uint |
| { |
| return role; |
| } |
| |
| /** |
| * @private |
| * Returns the name of the component. |
| * |
| * @param childID uint. |
| * |
| * @return Name of the component. |
| * |
| * @tiptext Returns the name of the component |
| * @helpid 3000 |
| */ |
| override public function get_accName(childID:uint):String |
| { |
| var accName:String; |
| |
| // For simple children, do not include anything but the default name. |
| // Examples: combo box items, list items, etc. |
| if (childID) |
| { |
| accName = getName(childID); |
| // Historical: Return null and not "" for empty and null values. |
| return (accName != null && accName != "") ? accName : null; |
| } |
| |
| // Start with form heading and/or formItem label text. |
| // Also includes "Required Field" where appropriate. |
| accName = getFormName(master); |
| |
| // Now the component's name or toolTip. |
| if (master.accessibilityProperties && |
| master.accessibilityProperties.name != null && |
| master.accessibilityProperties.name != "" && |
| !componentNeedsSkinnableTextBaseException(master)) |
| { |
| // An accName is set, so use only that. |
| accName = joinWithSpace(accName, master.accessibilityProperties.name); |
| } |
| else |
| { |
| // No accName set; use default name, or toolTip if that is empty. |
| accName = joinWithSpace(accName, getName(0) || master.toolTip); |
| } |
| |
| accName = joinWithSpace(accName, getStatusName()); |
| |
| // Historical: Return null and not "" for empty and null values. |
| return (accName != null && accName != "") ? accName : null; |
| } |
| |
| /** |
| * @private |
| * Method to return an array of childIDs. |
| * |
| * @return Array |
| */ |
| override public function getChildIDArray():Array |
| { |
| return []; |
| } |
| |
| /** |
| * @private |
| * IAccessible method for giving focus to a child item in the component |
| * (but not to the component itself; accSelect() is never called |
| * with a childID of 0). |
| * Even though this method does nothing, without it the Player |
| * causes an IAccessible "Member not found" error. |
| */ |
| override public function accSelect(selFlag:uint, childID:uint):void |
| { |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Returns the name of the accessible component. |
| * All subclasses must implement this |
| * instead of implementing get_accName(). |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function getName(childID:uint):String |
| { |
| return null; |
| } |
| |
| /** |
| * Utility method to determine state of the accessible component. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function getState(childID:uint):uint |
| { |
| var accState:uint = AccConst.STATE_SYSTEM_NORMAL; |
| |
| if (!UIComponent(master).enabled || isAncestorDisabled(master)) |
| { |
| accState &= ~AccConst.STATE_SYSTEM_FOCUSABLE; |
| accState |= AccConst.STATE_SYSTEM_UNAVAILABLE; |
| } |
| else |
| { |
| accState |= AccConst.STATE_SYSTEM_FOCUSABLE |
| |
| // This sets STATE_SYSTEM_FOCUSED appropriately for simple components |
| // and top levels of complex ones, but not for any subparts. |
| // That has to be done in the component's get_accState() method. |
| // example: listItems in a list. |
| if (UIComponent(master) == UIComponent(master).getFocus() |
| && childID == 0) |
| accState |= AccConst.STATE_SYSTEM_FOCUSED; |
| } |
| |
| return accState; |
| } |
| |
| /** |
| * @private |
| */ |
| private function getStatusName():String |
| { |
| var statusName:String = ""; |
| |
| if (master is UIComponent && UIComponent(master).errorString) |
| statusName = UIComponent(master).errorString; |
| |
| return statusName; |
| } |
| |
| /** |
| * @private |
| */ |
| protected function createChildIDArray(n:int):Array |
| { |
| var a:Array = new Array(n); |
| |
| for (var i:int = 0; i < n; i++) |
| { |
| a[i] = i + 1; |
| } |
| |
| return a; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Event handlers |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Generic event handler. |
| * All AccImpl subclasses must implement this |
| * to listen for events from its master component. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function eventHandler(event:Event):void |
| { |
| $eventHandler(event); |
| } |
| |
| /** |
| * @private |
| * Handles events common to all accessible UIComponents. |
| */ |
| protected final function $eventHandler(event:Event):void |
| { |
| switch (event.type) |
| { |
| case "errorStringChanged": |
| case "toolTipChanged": |
| { |
| Accessibility.sendEvent(master, 0, |
| AccConst.EVENT_OBJECT_NAMECHANGE); |
| Accessibility.updateProperties(); |
| break; |
| } |
| |
| case "show": |
| { |
| Accessibility.sendEvent(master, 0, |
| AccConst.EVENT_OBJECT_SHOW); |
| Accessibility.updateProperties(); |
| break; |
| } |
| |
| case "hide": |
| { |
| Accessibility.sendEvent(master, 0, |
| AccConst.EVENT_OBJECT_HIDE); |
| Accessibility.updateProperties(); |
| break; |
| } |
| } |
| } |
| } |
| |
| } |