diff -r 322d0feea350 -r 89ef5ed3c48b src/cm/media/js/lib/yui/yui_3.10.3/build/node-focusmanager/node-focusmanager-debug.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cm/media/js/lib/yui/yui_3.10.3/build/node-focusmanager/node-focusmanager-debug.js Tue Jul 16 14:29:46 2013 +0200 @@ -0,0 +1,1081 @@ +/* +YUI 3.10.3 (build 2fb5187) +Copyright 2013 Yahoo! Inc. All rights reserved. +Licensed under the BSD License. +http://yuilibrary.com/license/ +*/ + +YUI.add('node-focusmanager', function (Y, NAME) { + +/** +*

The Focus Manager Node Plugin makes it easy to manage focus among +* a Node's descendants. Primarily intended to help with widget development, +* the Focus Manager Node Plugin can be used to improve the keyboard +* accessibility of widgets.

+* +*

+* When designing widgets that manage a set of descendant controls (i.e. buttons +* in a toolbar, tabs in a tablist, menuitems in a menu, etc.) it is important to +* limit the number of descendants in the browser's default tab flow. The fewer +* number of descendants in the default tab flow, the easier it is for keyboard +* users to navigate between widgets by pressing the tab key. When a widget has +* focus it should provide a set of shortcut keys (typically the arrow keys) +* to move focus among its descendants. +*

+* +*

+* To this end, the Focus Manager Node Plugin makes it easy to define a Node's +* focusable descendants, define which descendant should be in the default tab +* flow, and define the keys that move focus among each descendant. +* Additionally, as the CSS +* :focus +* pseudo class is not supported on all elements in all +* A-Grade browsers, +* the Focus Manager Node Plugin provides an easy, cross-browser means of +* styling focus. +*

+* + +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 . + +* @module node-focusmanager +* @deprecated 3.9.0 +*/ + + // Frequently used strings + +var ACTIVE_DESCENDANT = "activeDescendant", + ID = "id", + DISABLED = "disabled", + TAB_INDEX = "tabIndex", + FOCUSED = "focused", + FOCUS_CLASS = "focusClass", + CIRCULAR = "circular", + UI = "UI", + KEY = "key", + ACTIVE_DESCENDANT_CHANGE = ACTIVE_DESCENDANT + "Change", + HOST = "host", + + // Collection of keys that, when pressed, cause the browser viewport + // to scroll. + scrollKeys = { + 37: true, + 38: true, + 39: true, + 40: true + }, + + clickableElements = { + "a": true, + "button": true, + "input": true, + "object": true + }, + + // Library shortcuts + + Lang = Y.Lang, + UA = Y.UA, + + /** + * The NodeFocusManager class is a plugin for a Node instance. The class is used + * via the plug method of Node + * and should not be instantiated directly. + * @namespace plugin + * @class NodeFocusManager + */ + NodeFocusManager = function () { + + NodeFocusManager.superclass.constructor.apply(this, arguments); + + }; + + +NodeFocusManager.ATTRS = { + + /** + * Boolean indicating that one of the descendants is focused. + * + * @attribute focused + * @readOnly + * @default false + * @type boolean + */ + focused: { + + value: false, + readOnly: true + + }, + + + /** + * String representing the CSS selector used to define the descendant Nodes + * whose focus should be managed. + * + * @attribute descendants + * @type Y.NodeList + */ + descendants: { + + getter: function (value) { + + return this.get(HOST).all(value); + + } + + }, + + + /** + *

Node, or index of the Node, representing the descendant that is either + * focused or is focusable (tabIndex attribute is set to 0). + * The value cannot represent a disabled descendant Node. Use a value of -1 + * to remove all descendant Nodes from the default tab flow. + * If no value is specified, the active descendant will be inferred using + * the following criteria:

+ *
    + *
  1. Examining the tabIndex attribute of each descendant and + * using the first descendant whose tabIndex attribute is set + * to 0
  2. + *
  3. If no default can be inferred then the value is set to either 0 or + * the index of the first enabled descendant.
  4. + *
+ * + * @attribute activeDescendant + * @type Number + */ + activeDescendant: { + + setter: function (value) { + + var isNumber = Lang.isNumber, + INVALID_VALUE = Y.Attribute.INVALID_VALUE, + descendantsMap = this._descendantsMap, + descendants = this._descendants, + nodeIndex, + returnValue, + oNode; + + + if (isNumber(value)) { + nodeIndex = value; + returnValue = nodeIndex; + } + else if ((value instanceof Y.Node) && descendantsMap) { + + nodeIndex = descendantsMap[value.get(ID)]; + + if (isNumber(nodeIndex)) { + returnValue = nodeIndex; + } + else { + + // The user passed a reference to a Node that wasn't one + // of the descendants. + returnValue = INVALID_VALUE; + + } + + } + else { + returnValue = INVALID_VALUE; + } + + + if (descendants) { + + oNode = descendants.item(nodeIndex); + + if (oNode && oNode.get("disabled")) { + + // Setting the "activeDescendant" attribute to the index + // of a disabled descendant is invalid. + returnValue = INVALID_VALUE; + + } + + } + + + return returnValue; + + } + + }, + + + /** + * Object literal representing the keys to be used to navigate between the + * next/previous descendant. The format for the attribute's value is + * { next: "down:40", previous: "down:38" }. The value for the + * "next" and "previous" properties are used to attach + * key event listeners. See + * the Using the key Event section of + * the Event documentation for more information on "key" event listeners. + * + * @attribute keys + * @type Object + */ + keys: { + + value: { + + next: null, + previous: null + + } + + + }, + + + /** + * String representing the name of class applied to the focused active + * descendant Node. Can also be an object literal used to define both the + * class name, and the Node to which the class should be applied. If using + * an object literal, the format is: + * { className: "focus", fn: myFunction }. The function + * referenced by the fn property in the object literal will be + * passed a reference to the currently focused active descendant Node. + * + * @attribute focusClass + * @type String|Object + */ + focusClass: { }, + + + /** + * Boolean indicating if focus should be set to the first/last descendant + * when the end or beginning of the descendants has been reached. + * + * @attribute circular + * @type Boolean + * @default true + */ + circular: { + value: true + } + +}; + +Y.extend(NodeFocusManager, Y.Plugin.Base, { + + // Protected properties + + // Boolean indicating if the NodeFocusManager is active. + _stopped: true, + + // NodeList representing the descendants selected via the + // "descendants" attribute. + _descendants: null, + + // Object literal mapping the IDs of each descendant to its index in the + // "_descendants" NodeList. + _descendantsMap: null, + + // Reference to the Node instance to which the focused class (defined + // by the "focusClass" attribute) is currently applied. + _focusedNode: null, + + // Number representing the index of the last descendant Node. + _lastNodeIndex: 0, + + // Array of handles for event handlers used for a NodeFocusManager instance. + _eventHandlers: null, + + + + // Protected methods + + /** + * @method _initDescendants + * @description Sets the tabIndex attribute of all of the + * descendants to -1, except the active descendant, whose + * tabIndex attribute is set to 0. + * @protected + */ + _initDescendants: function () { + + var descendants = this.get("descendants"), + descendantsMap = {}, + nFirstEnabled = -1, + nDescendants, + nActiveDescendant = this.get(ACTIVE_DESCENDANT), + oNode, + sID, + i = 0; + + + + if (Lang.isUndefined(nActiveDescendant)) { + nActiveDescendant = -1; + } + + + if (descendants) { + + nDescendants = descendants.size(); + + + for (i = 0; i < nDescendants; i++) { + + oNode = descendants.item(i); + + if (nFirstEnabled === -1 && !oNode.get(DISABLED)) { + nFirstEnabled = i; + } + + + // If the user didn't specify a value for the + // "activeDescendant" attribute try to infer it from + // the markup. + + // Need to pass "2" when using "getAttribute" for IE to get + // the attribute value as it is set in the markup. + // Need to use "parseInt" because IE always returns the + // value as a number, whereas all other browsers return + // the attribute as a string when accessed + // via "getAttribute". + + if (nActiveDescendant < 0 && + parseInt(oNode.getAttribute(TAB_INDEX, 2), 10) === 0) { + + nActiveDescendant = i; + + } + + if (oNode) { + oNode.set(TAB_INDEX, -1); + } + + sID = oNode.get(ID); + + if (!sID) { + sID = Y.guid(); + oNode.set(ID, sID); + } + + descendantsMap[sID] = i; + + } + + + // If the user didn't specify a value for the + // "activeDescendant" attribute and no default value could be + // determined from the markup, then default to 0. + + if (nActiveDescendant < 0) { + nActiveDescendant = 0; + } + + + oNode = descendants.item(nActiveDescendant); + + // Check to make sure the active descendant isn't disabled, + // and fall back to the first enabled descendant if it is. + + if (!oNode || oNode.get(DISABLED)) { + oNode = descendants.item(nFirstEnabled); + nActiveDescendant = nFirstEnabled; + } + + this._lastNodeIndex = nDescendants - 1; + this._descendants = descendants; + this._descendantsMap = descendantsMap; + + this.set(ACTIVE_DESCENDANT, nActiveDescendant); + + // Need to set the "tabIndex" attribute here, since the + // "activeDescendantChange" event handler used to manage + // the setting of the "tabIndex" attribute isn't wired up yet. + + if (oNode) { + oNode.set(TAB_INDEX, 0); + } + + } + + }, + + + /** + * @method _isDescendant + * @description Determines if the specified Node instance is a descendant + * managed by the Focus Manager. + * @param node {Node} Node instance to be checked. + * @return {Boolean} Boolean indicating if the specified Node instance is a + * descendant managed by the Focus Manager. + * @protected + */ + _isDescendant: function (node) { + + return (node.get(ID) in this._descendantsMap); + + }, + + + /** + * @method _removeFocusClass + * @description Removes the class name representing focus (as specified by + * the "focusClass" attribute) from the Node instance to which it is + * currently applied. + * @protected + */ + _removeFocusClass: function () { + + var oFocusedNode = this._focusedNode, + focusClass = this.get(FOCUS_CLASS), + sClassName; + + if (focusClass) { + sClassName = Lang.isString(focusClass) ? + focusClass : focusClass.className; + } + + if (oFocusedNode && sClassName) { + oFocusedNode.removeClass(sClassName); + } + + }, + + + /** + * @method _detachKeyHandler + * @description Detaches the "key" event handlers used to support the "keys" + * attribute. + * @protected + */ + _detachKeyHandler: function () { + + var prevKeyHandler = this._prevKeyHandler, + nextKeyHandler = this._nextKeyHandler; + + if (prevKeyHandler) { + prevKeyHandler.detach(); + } + + if (nextKeyHandler) { + nextKeyHandler.detach(); + } + + }, + + + /** + * @method _preventScroll + * @description Prevents the viewport from scolling when the user presses + * the up, down, left, or right key. + * @protected + */ + _preventScroll: function (event) { + + if (scrollKeys[event.keyCode] && this._isDescendant(event.target)) { + event.preventDefault(); + } + + }, + + + /** + * @method _fireClick + * @description Fires the click event if the enter key is pressed while + * focused on an HTML element that is not natively clickable. + * @protected + */ + _fireClick: function (event) { + + var oTarget = event.target, + sNodeName = oTarget.get("nodeName").toLowerCase(); + + if (event.keyCode === 13 && (!clickableElements[sNodeName] || + (sNodeName === "a" && !oTarget.getAttribute("href")))) { + + Y.log(("Firing click event for node:" + oTarget.get("id")), "info", "nodeFocusManager"); + + oTarget.simulate("click"); + + } + + }, + + + /** + * @method _attachKeyHandler + * @description Attaches the "key" event handlers used to support the "keys" + * attribute. + * @protected + */ + _attachKeyHandler: function () { + + this._detachKeyHandler(); + + var sNextKey = this.get("keys.next"), + sPrevKey = this.get("keys.previous"), + oNode = this.get(HOST), + aHandlers = this._eventHandlers; + + if (sPrevKey) { + this._prevKeyHandler = + Y.on(KEY, Y.bind(this._focusPrevious, this), oNode, sPrevKey); + } + + if (sNextKey) { + this._nextKeyHandler = + Y.on(KEY, Y.bind(this._focusNext, this), oNode, sNextKey); + } + + + // In Opera it is necessary to call the "preventDefault" method in + // response to the user pressing the arrow keys in order to prevent + // the viewport from scrolling when the user is moving focus among + // the focusable descendants. + + if (UA.opera) { + aHandlers.push(oNode.on("keypress", this._preventScroll, this)); + } + + + // For all browsers except Opera: HTML elements that are not natively + // focusable but made focusable via the tabIndex attribute don't + // fire a click event when the user presses the enter key. It is + // possible to work around this problem by simplying dispatching a + // click event in response to the user pressing the enter key. + + if (!UA.opera) { + aHandlers.push(oNode.on("keypress", this._fireClick, this)); + } + + }, + + + /** + * @method _detachEventHandlers + * @description Detaches all event handlers used by the Focus Manager. + * @protected + */ + _detachEventHandlers: function () { + + this._detachKeyHandler(); + + var aHandlers = this._eventHandlers; + + if (aHandlers) { + + Y.Array.each(aHandlers, function (handle) { + handle.detach(); + }); + + this._eventHandlers = null; + + } + + }, + + + /** + * @method _detachEventHandlers + * @description Attaches all event handlers used by the Focus Manager. + * @protected + */ + _attachEventHandlers: function () { + + var descendants = this._descendants, + aHandlers, + oDocument, + handle; + + if (descendants && descendants.size()) { + + aHandlers = this._eventHandlers || []; + oDocument = this.get(HOST).get("ownerDocument"); + + + if (aHandlers.length === 0) { + + Y.log("Attaching base set of event handlers.", "info", "nodeFocusManager"); + + aHandlers.push(oDocument.on("focus", this._onDocFocus, this)); + + aHandlers.push(oDocument.on("mousedown", + this._onDocMouseDown, this)); + + aHandlers.push( + this.after("keysChange", this._attachKeyHandler)); + + aHandlers.push( + this.after("descendantsChange", this._initDescendants)); + + aHandlers.push( + this.after(ACTIVE_DESCENDANT_CHANGE, + this._afterActiveDescendantChange)); + + + // For performance: defer attaching all key-related event + // handlers until the first time one of the specified + // descendants receives focus. + + handle = this.after("focusedChange", Y.bind(function (event) { + + if (event.newVal) { + + Y.log("Attaching key event handlers.", "info", "nodeFocusManager"); + + this._attachKeyHandler(); + + // Detach this "focusedChange" handler so that the + // key-related handlers only get attached once. + + handle.detach(); + + } + + }, this)); + + aHandlers.push(handle); + + } + + + this._eventHandlers = aHandlers; + + } + + }, + + + // Protected event handlers + + /** + * @method _onDocMouseDown + * @description "mousedown" event handler for the owner document of the + * Focus Manager's Node. + * @protected + * @param event {Object} Object representing the DOM event. + */ + _onDocMouseDown: function (event) { + + var oHost = this.get(HOST), + oTarget = event.target, + bChildNode = oHost.contains(oTarget), + node, + + getFocusable = function (node) { + + var returnVal = false; + + if (!node.compareTo(oHost)) { + + returnVal = this._isDescendant(node) ? node : + getFocusable.call(this, node.get("parentNode")); + + } + + return returnVal; + + }; + + + if (bChildNode) { + + // Check to make sure that the target isn't a child node of one + // of the focusable descendants. + + node = getFocusable.call(this, oTarget); + + if (node) { + oTarget = node; + } + else if (!node && this.get(FOCUSED)) { + + // The target was a non-focusable descendant of the root + // node, so the "focused" attribute should be set to false. + + this._set(FOCUSED, false); + this._onDocFocus(event); + + } + + } + + + if (bChildNode && this._isDescendant(oTarget)) { + + // Fix general problem in Webkit: mousing down on a button or an + // anchor element doesn't focus it. + + // For all browsers: makes sure that the descendant that + // was the target of the mousedown event is now considered the + // active descendant. + + this.focus(oTarget); + } + else if (UA.webkit && this.get(FOCUSED) && + (!bChildNode || (bChildNode && !this._isDescendant(oTarget)))) { + + // Fix for Webkit: + + // Document doesn't receive focus in Webkit when the user mouses + // down on it, so the "focused" attribute won't get set to the + // correct value. + + // The goal is to force a blur if the user moused down on + // either: 1) A descendant node, but not one that managed by + // the FocusManager, or 2) an element outside of the + // FocusManager + + this._set(FOCUSED, false); + this._onDocFocus(event); + + } + + }, + + + /** + * @method _onDocFocus + * @description "focus" event handler for the owner document of the + * Focus Manager's Node. + * @protected + * @param event {Object} Object representing the DOM event. + */ + _onDocFocus: function (event) { + + var oTarget = this._focusTarget || event.target, + bFocused = this.get(FOCUSED), + focusClass = this.get(FOCUS_CLASS), + oFocusedNode = this._focusedNode, + bInCollection; + + if (this._focusTarget) { + this._focusTarget = null; + } + + + if (this.get(HOST).contains(oTarget)) { + + // The target is a descendant of the root Node. + + bInCollection = this._isDescendant(oTarget); + + if (!bFocused && bInCollection) { + + // The user has focused a focusable descendant. + + bFocused = true; + + } + else if (bFocused && !bInCollection) { + + // The user has focused a child of the root Node that is + // not one of the descendants managed by this Focus Manager + // so clear the currently focused descendant. + + bFocused = false; + + } + + } + else { + + // The target is some other node in the document. + + bFocused = false; + + } + + + if (focusClass) { + + if (oFocusedNode && (!oFocusedNode.compareTo(oTarget) || !bFocused)) { + this._removeFocusClass(); + } + + if (bInCollection && bFocused) { + + if (focusClass.fn) { + oTarget = focusClass.fn(oTarget); + oTarget.addClass(focusClass.className); + } + else { + oTarget.addClass(focusClass); + } + + this._focusedNode = oTarget; + + } + + } + + + this._set(FOCUSED, bFocused); + + }, + + + /** + * @method _focusNext + * @description Keydown event handler that moves focus to the next + * enabled descendant. + * @protected + * @param event {Object} Object representing the DOM event. + * @param activeDescendant {Number} Number representing the index of the + * next descendant to be focused + */ + _focusNext: function (event, activeDescendant) { + + var nActiveDescendant = activeDescendant || this.get(ACTIVE_DESCENDANT), + oNode; + + + if (this._isDescendant(event.target) && + (nActiveDescendant <= this._lastNodeIndex)) { + + nActiveDescendant = nActiveDescendant + 1; + + if (nActiveDescendant === (this._lastNodeIndex + 1) && + this.get(CIRCULAR)) { + + nActiveDescendant = 0; + + } + + oNode = this._descendants.item(nActiveDescendant); + + if (oNode) { + + if (oNode.get("disabled")) { + this._focusNext(event, nActiveDescendant); + } + else { + this.focus(nActiveDescendant); + } + + } + + } + + this._preventScroll(event); + + }, + + + /** + * @method _focusPrevious + * @description Keydown event handler that moves focus to the previous + * enabled descendant. + * @protected + * @param event {Object} Object representing the DOM event. + * @param activeDescendant {Number} Number representing the index of the + * next descendant to be focused. + */ + _focusPrevious: function (event, activeDescendant) { + + var nActiveDescendant = activeDescendant || this.get(ACTIVE_DESCENDANT), + oNode; + + if (this._isDescendant(event.target) && nActiveDescendant >= 0) { + + nActiveDescendant = nActiveDescendant - 1; + + if (nActiveDescendant === -1 && this.get(CIRCULAR)) { + nActiveDescendant = this._lastNodeIndex; + } + + oNode = this._descendants.item(nActiveDescendant); + + if (oNode) { + + if (oNode.get("disabled")) { + this._focusPrevious(event, nActiveDescendant); + } + else { + this.focus(nActiveDescendant); + } + + } + + } + + this._preventScroll(event); + + }, + + + /** + * @method _afterActiveDescendantChange + * @description afterChange event handler for the + * "activeDescendant" attribute. + * @protected + * @param event {Object} Object representing the change event. + */ + _afterActiveDescendantChange: function (event) { + + var oNode = this._descendants.item(event.prevVal); + + if (oNode) { + oNode.set(TAB_INDEX, -1); + } + + oNode = this._descendants.item(event.newVal); + + if (oNode) { + oNode.set(TAB_INDEX, 0); + } + + }, + + + + // Public methods + + initializer: function (config) { + 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"); + this.start(); + + }, + + destructor: function () { + + this.stop(); + this.get(HOST).focusManager = null; + + }, + + + /** + * @method focus + * @description Focuses the active descendant and sets the + * focused attribute to true. + * @param index {Number} Optional. Number representing the index of the + * descendant to be set as the active descendant. + * @param index {Node} Optional. Node instance representing the + * descendant to be set as the active descendant. + */ + focus: function (index) { + + if (Lang.isUndefined(index)) { + index = this.get(ACTIVE_DESCENDANT); + } + + this.set(ACTIVE_DESCENDANT, index, { src: UI }); + + var oNode = this._descendants.item(this.get(ACTIVE_DESCENDANT)); + + if (oNode) { + + oNode.focus(); + + // In Opera focusing a