src/cm/media/js/lib/yui/yui3-3.15.0/build/node-focusmanager/node-focusmanager-debug.js
changeset 602 e16a97fb364a
equal deleted inserted replaced
601:d334a616c023 602:e16a97fb364a
       
     1 YUI.add('node-focusmanager', function (Y, NAME) {
       
     2 
       
     3 /**
       
     4 * <p>The Focus Manager Node Plugin makes it easy to manage focus among
       
     5 * a Node's descendants.  Primarily intended to help with widget development,
       
     6 * the Focus Manager Node Plugin can be used to improve the keyboard
       
     7 * accessibility of widgets.</p>
       
     8 *
       
     9 * <p>
       
    10 * When designing widgets that manage a set of descendant controls (i.e. buttons
       
    11 * in a toolbar, tabs in a tablist, menuitems in a menu, etc.) it is important to
       
    12 * limit the number of descendants in the browser's default tab flow.  The fewer
       
    13 * number of descendants in the default tab flow, the easier it is for keyboard
       
    14 * users to navigate between widgets by pressing the tab key.  When a widget has
       
    15 * focus it should provide a set of shortcut keys (typically the arrow keys)
       
    16 * to move focus among its descendants.
       
    17 * </p>
       
    18 *
       
    19 * <p>
       
    20 * To this end, the Focus Manager Node Plugin makes it easy to define a Node's
       
    21 * focusable descendants, define which descendant should be in the default tab
       
    22 * flow, and define the keys that move focus among each descendant.
       
    23 * Additionally, as the CSS
       
    24 * <a href="http://www.w3.org/TR/CSS21/selector.html#x38"><code>:focus</code></a>
       
    25 * pseudo class is not supported on all elements in all
       
    26 * <a href="http://developer.yahoo.com/yui/articles/gbs/">A-Grade browsers</a>,
       
    27 * the Focus Manager Node Plugin provides an easy, cross-browser means of
       
    28 * styling focus.
       
    29 * </p>
       
    30 *
       
    31 
       
    32 DEPRECATED: The FocusManager Node Plugin has been deprecated as of YUI 3.9.0. This module will be removed from the library in a future version. If you require functionality similar to the one provided by this  module, consider taking a look at the various modules in the YUI Gallery <http://yuilibrary.com/gallery/>.
       
    33 
       
    34 * @module node-focusmanager
       
    35 * @deprecated 3.9.0
       
    36 */
       
    37 
       
    38 	//	Frequently used strings
       
    39 
       
    40 var ACTIVE_DESCENDANT = "activeDescendant",
       
    41 	ID = "id",
       
    42 	DISABLED = "disabled",
       
    43 	TAB_INDEX = "tabIndex",
       
    44 	FOCUSED = "focused",
       
    45 	FOCUS_CLASS = "focusClass",
       
    46 	CIRCULAR = "circular",
       
    47 	UI = "UI",
       
    48 	KEY = "key",
       
    49 	ACTIVE_DESCENDANT_CHANGE = ACTIVE_DESCENDANT + "Change",
       
    50 	HOST = "host",
       
    51 
       
    52 	//	Collection of keys that, when pressed, cause the browser viewport
       
    53 	//	to scroll.
       
    54 	scrollKeys = {
       
    55 		37: true,
       
    56 		38: true,
       
    57 		39: true,
       
    58 		40: true
       
    59 	},
       
    60 
       
    61 	clickableElements = {
       
    62 		"a": true,
       
    63 		"button": true,
       
    64 		"input": true,
       
    65 		"object": true
       
    66 	},
       
    67 
       
    68 	//	Library shortcuts
       
    69 
       
    70 	Lang = Y.Lang,
       
    71  	UA = Y.UA,
       
    72 
       
    73 	/**
       
    74 	* The NodeFocusManager class is a plugin for a Node instance.  The class is used
       
    75 	* via the <a href="Node.html#method_plug"><code>plug</code></a> method of Node
       
    76 	* and should not be instantiated directly.
       
    77 	* @namespace plugin
       
    78 	* @class NodeFocusManager
       
    79 	*/
       
    80 	NodeFocusManager = function () {
       
    81 
       
    82 		NodeFocusManager.superclass.constructor.apply(this, arguments);
       
    83 
       
    84 	};
       
    85 
       
    86 
       
    87 NodeFocusManager.ATTRS = {
       
    88 
       
    89 	/**
       
    90 	* Boolean indicating that one of the descendants is focused.
       
    91 	*
       
    92 	* @attribute focused
       
    93 	* @readOnly
       
    94 	* @default false
       
    95 	* @type boolean
       
    96 	*/
       
    97 	focused: {
       
    98 
       
    99 		value: false,
       
   100 		readOnly: true
       
   101 
       
   102 	},
       
   103 
       
   104 
       
   105 	/**
       
   106 	* String representing the CSS selector used to define the descendant Nodes
       
   107 	* whose focus should be managed.
       
   108 	*
       
   109 	* @attribute descendants
       
   110 	* @type Y.NodeList
       
   111 	*/
       
   112 	descendants: {
       
   113 
       
   114 		getter: function (value) {
       
   115 
       
   116 			return this.get(HOST).all(value);
       
   117 
       
   118 		}
       
   119 
       
   120 	},
       
   121 
       
   122 
       
   123 	/**
       
   124 	* <p>Node, or index of the Node, representing the descendant that is either
       
   125 	* focused or is focusable (<code>tabIndex</code> attribute is set to 0).
       
   126 	* The value cannot represent a disabled descendant Node.  Use a value of -1
       
   127 	* to remove all descendant Nodes from the default tab flow.
       
   128 	* If no value is specified, the active descendant will be inferred using
       
   129 	* the following criteria:</p>
       
   130 	* <ol>
       
   131 	* <li>Examining the <code>tabIndex</code> attribute of each descendant and
       
   132 	* using the first descendant whose <code>tabIndex</code> attribute is set
       
   133 	* to 0</li>
       
   134 	* <li>If no default can be inferred then the value is set to either 0 or
       
   135 	* the index of the first enabled descendant.</li>
       
   136 	* </ol>
       
   137 	*
       
   138 	* @attribute activeDescendant
       
   139 	* @type Number
       
   140 	*/
       
   141 	activeDescendant: {
       
   142 
       
   143 		setter: function (value) {
       
   144 
       
   145 			var isNumber = Lang.isNumber,
       
   146 				INVALID_VALUE = Y.Attribute.INVALID_VALUE,
       
   147 				descendantsMap = this._descendantsMap,
       
   148 				descendants = this._descendants,
       
   149 				nodeIndex,
       
   150 				returnValue,
       
   151 				oNode;
       
   152 
       
   153 
       
   154 			if (isNumber(value)) {
       
   155 				nodeIndex = value;
       
   156 				returnValue = nodeIndex;
       
   157 			}
       
   158 			else if ((value instanceof Y.Node) && descendantsMap) {
       
   159 
       
   160 				nodeIndex = descendantsMap[value.get(ID)];
       
   161 
       
   162 				if (isNumber(nodeIndex)) {
       
   163 					returnValue = nodeIndex;
       
   164 				}
       
   165 				else {
       
   166 
       
   167 					//	The user passed a reference to a Node that wasn't one
       
   168 					//	of the descendants.
       
   169 					returnValue = INVALID_VALUE;
       
   170 
       
   171 				}
       
   172 
       
   173 			}
       
   174 			else {
       
   175 				returnValue = INVALID_VALUE;
       
   176 			}
       
   177 
       
   178 
       
   179 			if (descendants) {
       
   180 
       
   181 				oNode = descendants.item(nodeIndex);
       
   182 
       
   183 				if (oNode && oNode.get("disabled")) {
       
   184 
       
   185 					//	Setting the "activeDescendant" attribute to the index
       
   186 					//	of a disabled descendant is invalid.
       
   187 					returnValue = INVALID_VALUE;
       
   188 
       
   189 				}
       
   190 
       
   191 			}
       
   192 
       
   193 
       
   194 			return returnValue;
       
   195 
       
   196 		}
       
   197 
       
   198 	},
       
   199 
       
   200 
       
   201 	/**
       
   202 	* Object literal representing the keys to be used to navigate between the
       
   203 	* next/previous descendant.  The format for the attribute's value is
       
   204 	* <code>{ next: "down:40", previous: "down:38" }</code>.  The value for the
       
   205 	* "next" and "previous" properties are used to attach
       
   206 	* <a href="event/#keylistener"><code>key</code></a> event listeners. See
       
   207 	* the <a href="event/#keylistener">Using the key Event</a> section of
       
   208 	* the Event documentation for more information on "key" event listeners.
       
   209 	*
       
   210 	* @attribute keys
       
   211 	* @type Object
       
   212 	*/
       
   213 	keys: {
       
   214 
       
   215 		value: {
       
   216 
       
   217 			next: null,
       
   218 			previous: null
       
   219 
       
   220 		}
       
   221 
       
   222 
       
   223 	},
       
   224 
       
   225 
       
   226 	/**
       
   227 	* String representing the name of class applied to the focused active
       
   228 	* descendant Node.  Can also be an object literal used to define both the
       
   229 	* class name, and the Node to which the class should be applied.  If using
       
   230 	* an object literal, the format is:
       
   231 	* <code>{ className: "focus", fn: myFunction }</code>.  The function
       
   232 	* referenced by the <code>fn</code> property in the object literal will be
       
   233 	* passed a reference to the currently focused active descendant Node.
       
   234 	*
       
   235 	* @attribute focusClass
       
   236 	* @type String|Object
       
   237 	*/
       
   238 	focusClass: { },
       
   239 
       
   240 
       
   241 	/**
       
   242 	* Boolean indicating if focus should be set to the first/last descendant
       
   243 	* when the end or beginning of the descendants has been reached.
       
   244 	*
       
   245 	* @attribute circular
       
   246 	* @type Boolean
       
   247 	* @default true
       
   248 	*/
       
   249 	circular: {
       
   250 		value: true
       
   251 	}
       
   252 
       
   253 };
       
   254 
       
   255 Y.extend(NodeFocusManager, Y.Plugin.Base, {
       
   256 
       
   257 	//	Protected properties
       
   258 
       
   259 	//	Boolean indicating if the NodeFocusManager is active.
       
   260 	_stopped: true,
       
   261 
       
   262 	//	NodeList representing the descendants selected via the
       
   263 	//	"descendants" attribute.
       
   264 	_descendants: null,
       
   265 
       
   266 	//	Object literal mapping the IDs of each descendant to its index in the
       
   267 	//	"_descendants" NodeList.
       
   268 	_descendantsMap: null,
       
   269 
       
   270 	//	Reference to the Node instance to which the focused class (defined
       
   271 	//	by the "focusClass" attribute) is currently applied.
       
   272 	_focusedNode: null,
       
   273 
       
   274 	//	Number representing the index of the last descendant Node.
       
   275 	_lastNodeIndex: 0,
       
   276 
       
   277 	//	Array of handles for event handlers used for a NodeFocusManager instance.
       
   278 	_eventHandlers: null,
       
   279 
       
   280 
       
   281 
       
   282 	//	Protected methods
       
   283 
       
   284 	/**
       
   285 	* @method _initDescendants
       
   286 	* @description Sets the <code>tabIndex</code> attribute of all of the
       
   287 	* descendants to -1, except the active descendant, whose
       
   288 	* <code>tabIndex</code> attribute is set to 0.
       
   289 	* @protected
       
   290 	*/
       
   291 	_initDescendants: function () {
       
   292 
       
   293 		var descendants = this.get("descendants"),
       
   294 			descendantsMap = {},
       
   295 			nFirstEnabled = -1,
       
   296 			nDescendants,
       
   297 			nActiveDescendant = this.get(ACTIVE_DESCENDANT),
       
   298 			oNode,
       
   299 			sID,
       
   300 			i = 0;
       
   301 
       
   302 
       
   303 
       
   304 		if (Lang.isUndefined(nActiveDescendant)) {
       
   305 			nActiveDescendant = -1;
       
   306 		}
       
   307 
       
   308 
       
   309 		if (descendants) {
       
   310 
       
   311 			nDescendants = descendants.size();
       
   312 
       
   313 
       
   314             for (i = 0; i < nDescendants; i++) {
       
   315 
       
   316                 oNode = descendants.item(i);
       
   317 
       
   318                 if (nFirstEnabled === -1 && !oNode.get(DISABLED)) {
       
   319                     nFirstEnabled = i;
       
   320                 }
       
   321 
       
   322 
       
   323                 //	If the user didn't specify a value for the
       
   324                 //	"activeDescendant" attribute try to infer it from
       
   325                 //	the markup.
       
   326 
       
   327                 //	Need to pass "2" when using "getAttribute" for IE to get
       
   328                 //	the attribute value as it is set in the markup.
       
   329                 //	Need to use "parseInt" because IE always returns the
       
   330                 //	value as a number, whereas all other browsers return
       
   331                 //	the attribute as a string when accessed
       
   332                 //	via "getAttribute".
       
   333 
       
   334                 if (nActiveDescendant < 0 &&
       
   335                         parseInt(oNode.getAttribute(TAB_INDEX, 2), 10) === 0) {
       
   336 
       
   337                     nActiveDescendant = i;
       
   338 
       
   339                 }
       
   340 
       
   341                 if (oNode) {
       
   342                     oNode.set(TAB_INDEX, -1);
       
   343                 }
       
   344 
       
   345                 sID = oNode.get(ID);
       
   346 
       
   347                 if (!sID) {
       
   348                     sID = Y.guid();
       
   349                     oNode.set(ID, sID);
       
   350                 }
       
   351 
       
   352                 descendantsMap[sID] = i;
       
   353 
       
   354             }
       
   355 
       
   356 
       
   357             //	If the user didn't specify a value for the
       
   358             //	"activeDescendant" attribute and no default value could be
       
   359             //	determined from the markup, then default to 0.
       
   360 
       
   361             if (nActiveDescendant < 0) {
       
   362                 nActiveDescendant = 0;
       
   363             }
       
   364 
       
   365 
       
   366             oNode = descendants.item(nActiveDescendant);
       
   367 
       
   368             //	Check to make sure the active descendant isn't disabled,
       
   369             //	and fall back to the first enabled descendant if it is.
       
   370 
       
   371             if (!oNode || oNode.get(DISABLED)) {
       
   372                 oNode = descendants.item(nFirstEnabled);
       
   373                 nActiveDescendant = nFirstEnabled;
       
   374             }
       
   375 
       
   376             this._lastNodeIndex = nDescendants - 1;
       
   377             this._descendants = descendants;
       
   378             this._descendantsMap = descendantsMap;
       
   379 
       
   380             this.set(ACTIVE_DESCENDANT, nActiveDescendant);
       
   381 
       
   382             //	Need to set the "tabIndex" attribute here, since the
       
   383             //	"activeDescendantChange" event handler used to manage
       
   384             //	the setting of the "tabIndex" attribute isn't wired up yet.
       
   385 
       
   386             if (oNode) {
       
   387                 oNode.set(TAB_INDEX, 0);
       
   388             }
       
   389 
       
   390 		}
       
   391 
       
   392 	},
       
   393 
       
   394 
       
   395 	/**
       
   396 	* @method _isDescendant
       
   397 	* @description Determines if the specified Node instance is a descendant
       
   398 	* managed by the Focus Manager.
       
   399 	* @param node {Node} Node instance to be checked.
       
   400 	* @return {Boolean} Boolean indicating if the specified Node instance is a
       
   401 	* descendant managed by the Focus Manager.
       
   402 	* @protected
       
   403 	*/
       
   404 	_isDescendant: function (node) {
       
   405 
       
   406 		return (node.get(ID) in this._descendantsMap);
       
   407 
       
   408 	},
       
   409 
       
   410 
       
   411 	/**
       
   412 	* @method _removeFocusClass
       
   413 	* @description Removes the class name representing focus (as specified by
       
   414 	* the "focusClass" attribute) from the Node instance to which it is
       
   415 	* currently applied.
       
   416 	* @protected
       
   417 	*/
       
   418 	_removeFocusClass: function () {
       
   419 
       
   420 		var oFocusedNode = this._focusedNode,
       
   421 			focusClass = this.get(FOCUS_CLASS),
       
   422 			sClassName;
       
   423 
       
   424 		if (focusClass) {
       
   425 			sClassName = Lang.isString(focusClass) ?
       
   426 				focusClass : focusClass.className;
       
   427 		}
       
   428 
       
   429 		if (oFocusedNode && sClassName) {
       
   430 			oFocusedNode.removeClass(sClassName);
       
   431 		}
       
   432 
       
   433 	},
       
   434 
       
   435 
       
   436 	/**
       
   437 	* @method _detachKeyHandler
       
   438 	* @description Detaches the "key" event handlers used to support the "keys"
       
   439 	* attribute.
       
   440 	* @protected
       
   441 	*/
       
   442 	_detachKeyHandler: function () {
       
   443 
       
   444 		var prevKeyHandler = this._prevKeyHandler,
       
   445 			nextKeyHandler = this._nextKeyHandler;
       
   446 
       
   447 		if (prevKeyHandler) {
       
   448 			prevKeyHandler.detach();
       
   449 		}
       
   450 
       
   451 		if (nextKeyHandler) {
       
   452 			nextKeyHandler.detach();
       
   453 		}
       
   454 
       
   455 	},
       
   456 
       
   457 
       
   458 	/**
       
   459 	* @method _preventScroll
       
   460 	* @description Prevents the viewport from scolling when the user presses
       
   461 	* the up, down, left, or right key.
       
   462 	* @protected
       
   463 	*/
       
   464 	_preventScroll: function (event) {
       
   465 
       
   466 		if (scrollKeys[event.keyCode] && this._isDescendant(event.target)) {
       
   467 			event.preventDefault();
       
   468 		}
       
   469 
       
   470 	},
       
   471 
       
   472 
       
   473 	/**
       
   474 	* @method _fireClick
       
   475 	* @description Fires the click event if the enter key is pressed while
       
   476 	* focused on an HTML element that is not natively clickable.
       
   477 	* @protected
       
   478 	*/
       
   479 	_fireClick: function (event) {
       
   480 
       
   481 		var oTarget = event.target,
       
   482 			sNodeName = oTarget.get("nodeName").toLowerCase();
       
   483 
       
   484 		if (event.keyCode === 13 && (!clickableElements[sNodeName] ||
       
   485 				(sNodeName === "a" && !oTarget.getAttribute("href")))) {
       
   486 
       
   487 			Y.log(("Firing click event for node:" + oTarget.get("id")), "info", "nodeFocusManager");
       
   488 
       
   489 			oTarget.simulate("click");
       
   490 
       
   491 		}
       
   492 
       
   493 	},
       
   494 
       
   495 
       
   496 	/**
       
   497 	* @method _attachKeyHandler
       
   498 	* @description Attaches the "key" event handlers used to support the "keys"
       
   499 	* attribute.
       
   500 	* @protected
       
   501 	*/
       
   502 	_attachKeyHandler: function () {
       
   503 
       
   504 		this._detachKeyHandler();
       
   505 
       
   506 		var sNextKey = this.get("keys.next"),
       
   507 			sPrevKey = this.get("keys.previous"),
       
   508 			oNode = this.get(HOST),
       
   509 			aHandlers = this._eventHandlers;
       
   510 
       
   511 		if (sPrevKey) {
       
   512  			this._prevKeyHandler =
       
   513 				Y.on(KEY, Y.bind(this._focusPrevious, this), oNode, sPrevKey);
       
   514 		}
       
   515 
       
   516 		if (sNextKey) {
       
   517  			this._nextKeyHandler =
       
   518 				Y.on(KEY, Y.bind(this._focusNext, this), oNode, sNextKey);
       
   519 		}
       
   520 
       
   521 
       
   522 		//	In Opera it is necessary to call the "preventDefault" method in
       
   523 		//	response to the user pressing the arrow keys in order to prevent
       
   524 		//	the viewport from scrolling when the user is moving focus among
       
   525 		//	the focusable descendants.
       
   526 
       
   527 		if (UA.opera) {
       
   528 			aHandlers.push(oNode.on("keypress", this._preventScroll, this));
       
   529 		}
       
   530 
       
   531 
       
   532 		//	For all browsers except Opera: HTML elements that are not natively
       
   533 		//	focusable but made focusable via the tabIndex attribute don't
       
   534 		//	fire a click event when the user presses the enter key.  It is
       
   535 		//	possible to work around this problem by simplying dispatching a
       
   536 		//	click event in response to the user pressing the enter key.
       
   537 
       
   538 		if (!UA.opera) {
       
   539 			aHandlers.push(oNode.on("keypress", this._fireClick, this));
       
   540 		}
       
   541 
       
   542 	},
       
   543 
       
   544 
       
   545 	/**
       
   546 	* @method _detachEventHandlers
       
   547 	* @description Detaches all event handlers used by the Focus Manager.
       
   548 	* @protected
       
   549 	*/
       
   550 	_detachEventHandlers: function () {
       
   551 
       
   552 		this._detachKeyHandler();
       
   553 
       
   554 		var aHandlers = this._eventHandlers;
       
   555 
       
   556 		if (aHandlers) {
       
   557 
       
   558 			Y.Array.each(aHandlers, function (handle) {
       
   559 				handle.detach();
       
   560 			});
       
   561 
       
   562 			this._eventHandlers = null;
       
   563 
       
   564 		}
       
   565 
       
   566 	},
       
   567 
       
   568 
       
   569 	/**
       
   570 	* @method _detachEventHandlers
       
   571 	* @description Attaches all event handlers used by the Focus Manager.
       
   572 	* @protected
       
   573 	*/
       
   574 	_attachEventHandlers: function () {
       
   575 
       
   576 		var descendants = this._descendants,
       
   577 			aHandlers,
       
   578 			oDocument,
       
   579 			handle;
       
   580 
       
   581 		if (descendants && descendants.size()) {
       
   582 
       
   583 			aHandlers = this._eventHandlers || [];
       
   584 			oDocument = this.get(HOST).get("ownerDocument");
       
   585 
       
   586 
       
   587 			if (aHandlers.length === 0) {
       
   588 
       
   589 		        Y.log("Attaching base set of event handlers.", "info", "nodeFocusManager");
       
   590 
       
   591 				aHandlers.push(oDocument.on("focus", this._onDocFocus, this));
       
   592 
       
   593 				aHandlers.push(oDocument.on("mousedown",
       
   594 					this._onDocMouseDown, this));
       
   595 
       
   596 				aHandlers.push(
       
   597 						this.after("keysChange", this._attachKeyHandler));
       
   598 
       
   599 				aHandlers.push(
       
   600 						this.after("descendantsChange", this._initDescendants));
       
   601 
       
   602 				aHandlers.push(
       
   603 						this.after(ACTIVE_DESCENDANT_CHANGE,
       
   604 								this._afterActiveDescendantChange));
       
   605 
       
   606 
       
   607 				//	For performance: defer attaching all key-related event
       
   608 				//	handlers until the first time one of the specified
       
   609 				//	descendants receives focus.
       
   610 
       
   611 				handle = this.after("focusedChange", Y.bind(function (event) {
       
   612 
       
   613 					if (event.newVal) {
       
   614 
       
   615 				        Y.log("Attaching key event handlers.", "info", "nodeFocusManager");
       
   616 
       
   617 						this._attachKeyHandler();
       
   618 
       
   619 						//	Detach this "focusedChange" handler so that the
       
   620 						//	key-related handlers only get attached once.
       
   621 
       
   622 						handle.detach();
       
   623 
       
   624 					}
       
   625 
       
   626 				}, this));
       
   627 
       
   628 				aHandlers.push(handle);
       
   629 
       
   630 			}
       
   631 
       
   632 
       
   633 			this._eventHandlers = aHandlers;
       
   634 
       
   635 		}
       
   636 
       
   637 	},
       
   638 
       
   639 
       
   640 	//	Protected event handlers
       
   641 
       
   642 	/**
       
   643 	* @method _onDocMouseDown
       
   644 	* @description "mousedown" event handler for the owner document of the
       
   645 	* Focus Manager's Node.
       
   646 	* @protected
       
   647 	* @param event {Object} Object representing the DOM event.
       
   648 	*/
       
   649 	_onDocMouseDown: function (event) {
       
   650 
       
   651 		var oHost = this.get(HOST),
       
   652 			oTarget = event.target,
       
   653 			bChildNode = oHost.contains(oTarget),
       
   654 			node,
       
   655 
       
   656 			getFocusable = function (node) {
       
   657 
       
   658 				var returnVal = false;
       
   659 
       
   660 				if (!node.compareTo(oHost)) {
       
   661 
       
   662 					returnVal = this._isDescendant(node) ? node :
       
   663 									getFocusable.call(this, node.get("parentNode"));
       
   664 
       
   665 				}
       
   666 
       
   667 				return returnVal;
       
   668 
       
   669 			};
       
   670 
       
   671 
       
   672 		if (bChildNode) {
       
   673 
       
   674 			//	Check to make sure that the target isn't a child node of one
       
   675 			//	of the focusable descendants.
       
   676 
       
   677 			node = getFocusable.call(this, oTarget);
       
   678 
       
   679 			if (node) {
       
   680 				oTarget = node;
       
   681 			}
       
   682 			else if (!node && this.get(FOCUSED)) {
       
   683 
       
   684 				//	The target was a non-focusable descendant of the root
       
   685 				//	node, so the "focused" attribute should be set to false.
       
   686 
       
   687 	 			this._set(FOCUSED, false);
       
   688 	 			this._onDocFocus(event);
       
   689 
       
   690 			}
       
   691 
       
   692 		}
       
   693 
       
   694 
       
   695 		if (bChildNode && this._isDescendant(oTarget)) {
       
   696 
       
   697 			//	Fix general problem in Webkit: mousing down on a button or an
       
   698 			//	anchor element doesn't focus it.
       
   699 
       
   700 			//	For all browsers: makes sure that the descendant that
       
   701 			//	was the target of the mousedown event is now considered the
       
   702 			//	active descendant.
       
   703 
       
   704 			this.focus(oTarget);
       
   705 		}
       
   706 		else if (UA.webkit && this.get(FOCUSED) &&
       
   707 			(!bChildNode || (bChildNode && !this._isDescendant(oTarget)))) {
       
   708 
       
   709 			//	Fix for Webkit:
       
   710 
       
   711 			//	Document doesn't receive focus in Webkit when the user mouses
       
   712 			//	down on it, so the "focused" attribute won't get set to the
       
   713 			//	correct value.
       
   714 
       
   715 			//	The goal is to force a blur if the user moused down on
       
   716 			//	either: 1) A descendant node, but not one that managed by
       
   717 			//	the FocusManager, or 2) an element outside of the
       
   718 			//	FocusManager
       
   719 
       
   720  			this._set(FOCUSED, false);
       
   721  			this._onDocFocus(event);
       
   722 
       
   723 		}
       
   724 
       
   725 	},
       
   726 
       
   727 
       
   728 	/**
       
   729 	* @method _onDocFocus
       
   730 	* @description "focus" event handler for the owner document of the
       
   731 	* Focus Manager's Node.
       
   732 	* @protected
       
   733 	* @param event {Object} Object representing the DOM event.
       
   734 	*/
       
   735 	_onDocFocus: function (event) {
       
   736 
       
   737 		var oTarget = this._focusTarget || event.target,
       
   738 			bFocused = this.get(FOCUSED),
       
   739 			focusClass = this.get(FOCUS_CLASS),
       
   740 			oFocusedNode = this._focusedNode,
       
   741 			bInCollection;
       
   742 
       
   743 		if (this._focusTarget) {
       
   744 			this._focusTarget = null;
       
   745 		}
       
   746 
       
   747 
       
   748 		if (this.get(HOST).contains(oTarget)) {
       
   749 
       
   750 			//	The target is a descendant of the root Node.
       
   751 
       
   752 			bInCollection = this._isDescendant(oTarget);
       
   753 
       
   754 			if (!bFocused && bInCollection) {
       
   755 
       
   756 				//	The user has focused a focusable descendant.
       
   757 
       
   758 				bFocused = true;
       
   759 
       
   760 			}
       
   761 			else if (bFocused && !bInCollection) {
       
   762 
       
   763 				//	The user has focused a child of the root Node that is
       
   764 				//	not one of the descendants managed by this Focus Manager
       
   765 				//	so clear the currently focused descendant.
       
   766 
       
   767 				bFocused = false;
       
   768 
       
   769 			}
       
   770 
       
   771 		}
       
   772 		else {
       
   773 
       
   774 			// The target is some other node in the document.
       
   775 
       
   776 			bFocused = false;
       
   777 
       
   778 		}
       
   779 
       
   780 
       
   781 		if (focusClass) {
       
   782 
       
   783 			if (oFocusedNode && (!oFocusedNode.compareTo(oTarget) || !bFocused)) {
       
   784 				this._removeFocusClass();
       
   785 			}
       
   786 
       
   787 			if (bInCollection && bFocused) {
       
   788 
       
   789 				if (focusClass.fn) {
       
   790 					oTarget = focusClass.fn(oTarget);
       
   791 					oTarget.addClass(focusClass.className);
       
   792 				}
       
   793 				else {
       
   794 					oTarget.addClass(focusClass);
       
   795 				}
       
   796 
       
   797 				this._focusedNode = oTarget;
       
   798 
       
   799 			}
       
   800 
       
   801 		}
       
   802 
       
   803 
       
   804 		this._set(FOCUSED, bFocused);
       
   805 
       
   806 	},
       
   807 
       
   808 
       
   809 	/**
       
   810 	* @method _focusNext
       
   811 	* @description Keydown event handler that moves focus to the next
       
   812 	* enabled descendant.
       
   813 	* @protected
       
   814 	* @param event {Object} Object representing the DOM event.
       
   815 	* @param activeDescendant {Number} Number representing the index of the
       
   816 	* next descendant to be focused
       
   817 	*/
       
   818 	_focusNext: function (event, activeDescendant) {
       
   819 
       
   820 		var nActiveDescendant = activeDescendant || this.get(ACTIVE_DESCENDANT),
       
   821 			oNode;
       
   822 
       
   823 
       
   824 		if (this._isDescendant(event.target) &&
       
   825 			(nActiveDescendant <= this._lastNodeIndex)) {
       
   826 
       
   827 			nActiveDescendant = nActiveDescendant + 1;
       
   828 
       
   829 			if (nActiveDescendant === (this._lastNodeIndex + 1) &&
       
   830 				this.get(CIRCULAR)) {
       
   831 
       
   832 				nActiveDescendant = 0;
       
   833 
       
   834 			}
       
   835 
       
   836 			oNode = this._descendants.item(nActiveDescendant);
       
   837 
       
   838             if (oNode) {
       
   839 
       
   840                 if (oNode.get("disabled")) {
       
   841                     this._focusNext(event, nActiveDescendant);
       
   842                 }
       
   843                 else {
       
   844                     this.focus(nActiveDescendant);
       
   845                 }
       
   846 
       
   847             }
       
   848 
       
   849 		}
       
   850 
       
   851 		this._preventScroll(event);
       
   852 
       
   853 	},
       
   854 
       
   855 
       
   856 	/**
       
   857 	* @method _focusPrevious
       
   858 	* @description Keydown event handler that moves focus to the previous
       
   859 	* enabled descendant.
       
   860 	* @protected
       
   861 	* @param event {Object} Object representing the DOM event.
       
   862 	* @param activeDescendant {Number} Number representing the index of the
       
   863 	* next descendant to be focused.
       
   864 	*/
       
   865 	_focusPrevious: function (event, activeDescendant) {
       
   866 
       
   867 		var nActiveDescendant = activeDescendant || this.get(ACTIVE_DESCENDANT),
       
   868 			oNode;
       
   869 
       
   870 		if (this._isDescendant(event.target) && nActiveDescendant >= 0) {
       
   871 
       
   872 			nActiveDescendant = nActiveDescendant - 1;
       
   873 
       
   874 			if (nActiveDescendant === -1 && this.get(CIRCULAR)) {
       
   875 				nActiveDescendant = this._lastNodeIndex;
       
   876 			}
       
   877 
       
   878             oNode = this._descendants.item(nActiveDescendant);
       
   879 
       
   880             if (oNode) {
       
   881 
       
   882                 if (oNode.get("disabled")) {
       
   883                     this._focusPrevious(event, nActiveDescendant);
       
   884                 }
       
   885                 else {
       
   886                     this.focus(nActiveDescendant);
       
   887                 }
       
   888 
       
   889             }
       
   890 
       
   891 		}
       
   892 
       
   893 		this._preventScroll(event);
       
   894 
       
   895 	},
       
   896 
       
   897 
       
   898 	/**
       
   899 	* @method _afterActiveDescendantChange
       
   900 	* @description afterChange event handler for the
       
   901 	* "activeDescendant" attribute.
       
   902 	* @protected
       
   903 	* @param event {Object} Object representing the change event.
       
   904 	*/
       
   905 	_afterActiveDescendantChange: function (event) {
       
   906 
       
   907 		var oNode = this._descendants.item(event.prevVal);
       
   908 
       
   909 		if (oNode) {
       
   910 			oNode.set(TAB_INDEX, -1);
       
   911 		}
       
   912 
       
   913 		oNode = this._descendants.item(event.newVal);
       
   914 
       
   915 		if (oNode) {
       
   916 			oNode.set(TAB_INDEX, 0);
       
   917 		}
       
   918 
       
   919 	},
       
   920 
       
   921 
       
   922 
       
   923 	//	Public methods
       
   924 
       
   925     initializer: function (config) {
       
   926     	Y.log("WARNING: node-focusmanager is a deprecated module as of YUI 3.9.0. This module will be removed from a later version of the library.", "warn");
       
   927 		this.start();
       
   928 
       
   929     },
       
   930 
       
   931 	destructor: function () {
       
   932 
       
   933 		this.stop();
       
   934 		this.get(HOST).focusManager = null;
       
   935 
       
   936     },
       
   937 
       
   938 
       
   939 	/**
       
   940 	* @method focus
       
   941 	* @description Focuses the active descendant and sets the
       
   942 	* <code>focused</code> attribute to true.
       
   943 	* @param index {Number|Node} Optional. Number representing the index of the
       
   944 	* descendant to be set as the active descendant or Node instance
       
   945 	* representing the descendant to be set as the active descendant.
       
   946 	*/
       
   947 	focus: function (index) {
       
   948 
       
   949 		if (Lang.isUndefined(index)) {
       
   950 			index = this.get(ACTIVE_DESCENDANT);
       
   951 		}
       
   952 
       
   953 		this.set(ACTIVE_DESCENDANT, index, { src: UI });
       
   954 
       
   955 		var oNode = this._descendants.item(this.get(ACTIVE_DESCENDANT));
       
   956 
       
   957 		if (oNode) {
       
   958 
       
   959 			oNode.focus();
       
   960 
       
   961 			//	In Opera focusing a <BUTTON> element programmatically
       
   962 			//	will result in the document-level focus event handler
       
   963 			//	"_onDocFocus" being called, resulting in the handler
       
   964 			//	incorrectly setting the "focused" Attribute to false.  To fix
       
   965 			//	this, set a flag ("_focusTarget") that the "_onDocFocus" method
       
   966 			//	can look for to properly handle this edge case.
       
   967 
       
   968 			if (UA.opera && oNode.get("nodeName").toLowerCase() === "button") {
       
   969 				this._focusTarget = oNode;
       
   970 			}
       
   971 
       
   972 		}
       
   973 
       
   974 	},
       
   975 
       
   976 
       
   977 	/**
       
   978 	* @method blur
       
   979 	* @description Blurs the current active descendant and sets the
       
   980 	* <code>focused</code> attribute to false.
       
   981 	*/
       
   982 	blur: function () {
       
   983 
       
   984 		var oNode;
       
   985 
       
   986 		if (this.get(FOCUSED)) {
       
   987 
       
   988 			oNode = this._descendants.item(this.get(ACTIVE_DESCENDANT));
       
   989 
       
   990 			if (oNode) {
       
   991 
       
   992 				oNode.blur();
       
   993 
       
   994 				//	For Opera and Webkit:  Blurring an element in either browser
       
   995 				//	doesn't result in another element (such as the document)
       
   996 				//	being focused.  Therefore, the "_onDocFocus" method
       
   997 				//	responsible for managing the application and removal of the
       
   998 				//	focus indicator class name is never called.
       
   999 
       
  1000 				this._removeFocusClass();
       
  1001 
       
  1002 			}
       
  1003 
       
  1004 			this._set(FOCUSED, false, { src: UI });
       
  1005 		}
       
  1006 
       
  1007 	},
       
  1008 
       
  1009 
       
  1010 	/**
       
  1011 	* @method start
       
  1012 	* @description Enables the Focus Manager.
       
  1013 	*/
       
  1014 	start: function () {
       
  1015 
       
  1016 		if (this._stopped) {
       
  1017 
       
  1018 			this._initDescendants();
       
  1019 			this._attachEventHandlers();
       
  1020 
       
  1021 			this._stopped = false;
       
  1022 
       
  1023 		}
       
  1024 
       
  1025 	},
       
  1026 
       
  1027 
       
  1028 	/**
       
  1029 	* @method stop
       
  1030 	* @description Disables the Focus Manager by detaching all event handlers.
       
  1031 	*/
       
  1032 	stop: function () {
       
  1033 
       
  1034 		if (!this._stopped) {
       
  1035 
       
  1036 			this._detachEventHandlers();
       
  1037 
       
  1038 			this._descendants = null;
       
  1039 			this._focusedNode = null;
       
  1040 			this._lastNodeIndex = 0;
       
  1041 			this._stopped = true;
       
  1042 
       
  1043 		}
       
  1044 
       
  1045 	},
       
  1046 
       
  1047 
       
  1048 	/**
       
  1049 	* @method refresh
       
  1050 	* @description Refreshes the Focus Manager's descendants by re-executing the
       
  1051 	* CSS selector query specified by the <code>descendants</code> attribute.
       
  1052 	*/
       
  1053 	refresh: function () {
       
  1054 
       
  1055 		this._initDescendants();
       
  1056 
       
  1057 		if (!this._eventHandlers) {
       
  1058 			this._attachEventHandlers();
       
  1059 		}
       
  1060 
       
  1061 	}
       
  1062 
       
  1063 });
       
  1064 
       
  1065 
       
  1066 NodeFocusManager.NAME = "nodeFocusManager";
       
  1067 NodeFocusManager.NS = "focusManager";
       
  1068 
       
  1069 Y.namespace("Plugin");
       
  1070 Y.Plugin.NodeFocusManager = NodeFocusManager;
       
  1071 
       
  1072 
       
  1073 }, '@VERSION@', {"requires": ["attribute", "node", "plugin", "node-event-simulate", "event-key", "event-focus"]});