diff -r d334a616c023 -r e16a97fb364a src/cm/media/js/lib/yui/yui3-3.15.0/build/node-focusmanager/node-focusmanager-debug.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cm/media/js/lib/yui/yui3-3.15.0/build/node-focusmanager/node-focusmanager-debug.js Mon Mar 10 15:19:48 2014 +0100 @@ -0,0 +1,1073 @@ +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.
+*
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:
tabIndex attribute of each descendant and
+ * using the first descendant whose tabIndex attribute is set
+ * to 0{ 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|Node} Optional. Number representing the index of the
+ * descendant to be set as the active descendant or 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