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