1//////////////////////////////////////////////////////////////////////////////// 2// 3// ADOBE SYSTEMS INCORPORATED 4// Copyright 2003-2007 Adobe Systems Incorporated 5// All Rights Reserved. 6// 7// NOTICE: Adobe permits you to use, modify, and distribute this file 8// in accordance with the terms of the license agreement accompanying it. 9// 10//////////////////////////////////////////////////////////////////////////////// 11 12package mx.accessibility 13{ 14 15import flash.accessibility.Accessibility; 16import flash.accessibility.AccessibilityProperties; 17import flash.events.Event; 18import flash.system.ApplicationDomain; 19 20import mx.accessibility.AccImpl; 21import mx.core.UIComponent; 22import mx.core.mx_internal; 23 24use namespace mx_internal; 25 26/** 27 * UIComponentAccProps is a subclass of AccessibilityProperties 28 * for use by various UIComponents. 29 * It is used to provide accessibility to Form, ToolTip, and Error ToolTip. 30 * 31 * @langversion 3.0 32 * @playerversion Flash 9 33 * @playerversion AIR 1.1 34 * @productversion Flex 3 35 */ 36public class UIComponentAccProps extends AccessibilityProperties 37{ 38 include "../core/Version.as"; 39 40 //-------------------------------------------------------------------------- 41 // 42 // Class methods 43 // 44 //-------------------------------------------------------------------------- 45 46 /** 47 * Enables accessibility in the UIComponent class. 48 * 49 * <p>This method is called by application startup code 50 * that is autogenerated by the MXML compiler. 51 * Afterwards, when instances of UIComponent are initialized, 52 * their <code>accessibilityProperties</code> property 53 * will be set to an instance of this class.</p> 54 * 55 * @langversion 3.0 56 * @playerversion Flash 9 57 * @playerversion AIR 1.1 58 * @productversion Flex 3 59 */ 60 public static function enableAccessibility():void 61 { 62 UIComponent.createAccessibilityImplementation = 63 createAccessibilityImplementation; 64 } 65 66 /** 67 * @private 68 * Creates a UIComponent's AccessibilityProperties object. 69 * This method is called from UIComponent's 70 * initializeAccessibility() method. 71 */ 72 mx_internal static function createAccessibilityImplementation( 73 component:UIComponent):void 74 { 75 component.accessibilityProperties = 76 new UIComponentAccProps(component); 77 } 78 79 /** 80 * @private 81 * Determines whether a component should avoid producing MSAA information 82 * directly. Components that are represented otherwise, such as 83 * a form field's Required Field indicator (which causes "Required field" 84 * to be included in the field's accessible name) should be made silent 85 * in this way. 86 * 87 * @param component The component to check. 88 * 89 * @return true if this component should be silent. 90 */ 91 mx_internal static function componentShouldBeSilent(component:UIComponent):Boolean 92 { 93 // All tests below require Group to exist and to be under a FormItem. 94 var groupClass:Class = Class(AccImpl.getDefinition( 95 "spark.components.Group", component.moduleFactory 96 )); 97 if (!groupClass) 98 return false; 99 var formItem:UIComponent = AccImpl.findMatchingAncestor(component, AccImpl.isFormItem); 100 if (!formItem) 101 return false; 102 103 // This catches labels for FormItem fields, 104 // and also one object related to the Required Field indicator. 105 // TODO: We might want to silence all Group components under a FormItem, 106 // but this theory remains to be tested and so is not implemented here. 107 // component.parent should be a FormItem skin (possibly a custom one), 108 // so the parent of that should be the FormItem. 109 // The try/catch block prevents an RTE if 110 // component.parent.parent doesn't exist. 111 try 112 { 113 if (component is groupClass 114 && component.parent.parent === formItem) 115 return true; 116 } 117 catch (e:Error) 118 { 119 } 120 121 // This catches the Required Field graphic itself. 122 // Sought structure: Image in Group in skin in FormItem. 123 var imageClass:Class = Class(AccImpl.getDefinition( 124 "spark.components.Image", component.moduleFactory 125 )); 126 if (!imageClass) 127 return false; 128 try 129 { 130 if (component is imageClass && component.parent is groupClass 131 && component.parent.parent.parent === formItem) 132 return true; 133 } 134 catch (e:Error) 135 { 136 } 137 138 return false; 139 } 140 141 //-------------------------------------------------------------------------- 142 // 143 // Constructor 144 // 145 //-------------------------------------------------------------------------- 146 147 /** 148 * Constructor. 149 * 150 * @param master The UIComponent instance that this 151 * AccessibilityProperties instance is making accessible. 152 * 153 * @langversion 3.0 154 * @playerversion Flash 9 155 * @playerversion AIR 1.1 156 * @productversion Flex 3 157 */ 158 public function UIComponentAccProps(component:UIComponent) 159 { 160 super(); 161 162 master = component; 163 164 if (component.accessibilityProperties) 165 { 166 silent = component.accessibilityProperties.silent; 167 168 forceSimple = component.accessibilityProperties.forceSimple; 169 170 noAutoLabeling = component.accessibilityProperties.noAutoLabeling; 171 172 if (component.accessibilityProperties.name) 173 name = component.accessibilityProperties.name; 174 175 if (component.accessibilityProperties.description) 176 description = component.accessibilityProperties.description; 177 178 if (component.accessibilityProperties.shortcut) 179 shortcut = component.accessibilityProperties.shortcut; 180 } 181 182 if (AccImpl.getMatchingDefinition(master, 183 AccImpl.getDefinitions("ScrollBar", master.moduleFactory) 184 )) 185 { 186 silent = true; 187 return; 188 } 189 190 if (isFormItemLabel(master)) 191 { 192 // TODO: Why is name set here? Is this name used somewhere? 193 name = AccImpl.getFormName(master); 194 silent = true; 195 return; 196 } 197 198 // Silence various subparts of Spark forms besides FormItem labels. 199 if (componentShouldBeSilent(component)) 200 { 201 // We can't set silent=true or whole FormItems and their fields 202 // will disappear from MSAA, but if we set name="", the Player 203 // will filter out these items for us. 204 name = ""; 205 return; 206 } 207 208 // In complex layouts, the text of a FormHeading might not appear 209 // where it should for assistive technology, because the FormHeading's 210 // tabIndex does not also get applied to the text item. 211 // That is fixed here when the FormHeading is being constructed. 212 if (AccImpl.isFormHeading(master) && master.tabIndex > 0) 213 { 214 // Spark-specific solution. 215 try 216 { 217 if (Object(master).labelDisplay.tabIndex == -1) 218 Object(master).labelDisplay.tabIndex = master.tabIndex; 219 } 220 catch (e:Error) 221 { 222 } 223 // TODO: Not solved for MX even though the problem does apply there. 224 } 225 226 var formName:String = AccImpl.getFormName(master); 227 if (formName && formName.length != 0) 228 name = formName + name; 229 230 if (master.toolTip && master.toolTip.length != 0) 231 if (!component.accessibilityProperties || (component.accessibilityProperties && !component.accessibilityProperties.name)) 232 { 233 oldToolTip = " " + master.toolTip; 234 name += oldToolTip; 235 } 236 237 if (master.errorString && master.errorString.length != 0) 238 { 239 oldErrorString = " " + master.errorString; 240 name += oldErrorString; 241 } 242 243 master.addEventListener("toolTipChanged", eventHandler); 244 master.addEventListener("errorStringChanged", eventHandler); 245 } 246 247 /** 248 * @private 249 * Determines whether a component is a formItem label (Spark or MX). 250 * 251 * @param component The component to check. 252 * 253 * @return true if this is a label for a formItem. 254 */ 255 protected function isFormItemLabel(component:UIComponent):Boolean 256 { 257 // This handles Spark labels on forms. 258 var thisName:String = master.name; 259 if (thisName == "labelDisplay" 260 || thisName == "sequenceLabelDisplay" 261 || thisName == "helpContentGroup" 262 || thisName == "errorTextDisplay" 263 ) 264 return true; 265 266 // This handles MX FormItemLabel objects. 267 return Boolean(AccImpl.getMatchingDefinition(master, 268 AccImpl.getDefinitions("FormItemLabel", master.moduleFactory) 269 )); 270 } 271 272 //-------------------------------------------------------------------------- 273 // 274 // Variables 275 // 276 //-------------------------------------------------------------------------- 277 278 /** 279 * @private 280 */ 281 private var oldToolTip:String; 282 283 /** 284 * @private 285 */ 286 private var oldErrorString:String; 287 288 //-------------------------------------------------------------------------- 289 // 290 // Properties 291 // 292 //-------------------------------------------------------------------------- 293 294 //---------------------------------- 295 // master 296 //---------------------------------- 297 298 /** 299 * A reference to the UIComponent itself. 300 * 301 * @langversion 3.0 302 * @playerversion Flash 9 303 * @playerversion AIR 1.1 304 * @productversion Flex 3 305 */ 306 protected var master:UIComponent; 307 308 //-------------------------------------------------------------------------- 309 // 310 // Event handlers 311 // 312 //-------------------------------------------------------------------------- 313 314 /** 315 * Generic event handler. 316 * All UIComponentAccProps subclasses must implement this 317 * to listen for events from its master component. 318 * 319 * @langversion 3.0 320 * @playerversion Flash 9 321 * @playerversion AIR 1.1 322 * @productversion Flex 3 323 */ 324 protected function eventHandler(event:Event):void 325 { 326 var pos:int; 327 328 switch (event.type) 329 { 330 case "errorStringChanged": 331 { 332 if (name && name.length != 0 && oldErrorString) 333 { 334 pos = name.indexOf(oldErrorString); 335 if (pos != -1) 336 { 337 name = name.substring(0, pos) + 338 name.substring(pos + oldErrorString.length); 339 } 340 oldErrorString = null; 341 } 342 343 if (master.errorString && master.errorString.length != 0) 344 { 345 if (!name) 346 name = ""; 347 348 oldErrorString = " " + master.errorString; 349 name += oldErrorString; 350 } 351 352 Accessibility.updateProperties(); 353 break; 354 } 355 356 case "toolTipChanged": 357 { 358 if (name && name.length != 0 && oldToolTip) 359 { 360 pos = name.indexOf(oldToolTip); 361 if (pos != -1) 362 { 363 name = name.substring(0, pos) + 364 name.substring(pos + oldToolTip.length); 365 } 366 oldToolTip = null; 367 } 368 369 if (master.toolTip && master.toolTip.length != 0) 370 { 371 if (!master.accessibilityProperties || (master.accessibilityProperties && !master.accessibilityProperties.name)) 372 { 373 if (!name) 374 name = ""; 375 376 oldToolTip = " " + master.toolTip; 377 name += oldToolTip; 378 } 379 } 380 381 Accessibility.updateProperties(); 382 break; 383 } 384 } 385 } 386} 387 388} 389