[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 /* 2 YUI 3.17.2 (build 9c3c78e) 3 Copyright 2014 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|Node} Optional. Number representing the index of the 951 * descendant to be set as the active descendant or Node instance 952 * representing the descendant to be set as the active descendant. 953 */ 954 focus: function (index) { 955 956 if (Lang.isUndefined(index)) { 957 index = this.get(ACTIVE_DESCENDANT); 958 } 959 960 this.set(ACTIVE_DESCENDANT, index, { src: UI }); 961 962 var oNode = this._descendants.item(this.get(ACTIVE_DESCENDANT)); 963 964 if (oNode) { 965 966 oNode.focus(); 967 968 // In Opera focusing a <BUTTON> element programmatically 969 // will result in the document-level focus event handler 970 // "_onDocFocus" being called, resulting in the handler 971 // incorrectly setting the "focused" Attribute to false. To fix 972 // this, set a flag ("_focusTarget") that the "_onDocFocus" method 973 // can look for to properly handle this edge case. 974 975 if (UA.opera && oNode.get("nodeName").toLowerCase() === "button") { 976 this._focusTarget = oNode; 977 } 978 979 } 980 981 }, 982 983 984 /** 985 * @method blur 986 * @description Blurs the current active descendant and sets the 987 * <code>focused</code> attribute to false. 988 */ 989 blur: function () { 990 991 var oNode; 992 993 if (this.get(FOCUSED)) { 994 995 oNode = this._descendants.item(this.get(ACTIVE_DESCENDANT)); 996 997 if (oNode) { 998 999 oNode.blur(); 1000 1001 // For Opera and Webkit: Blurring an element in either browser 1002 // doesn't result in another element (such as the document) 1003 // being focused. Therefore, the "_onDocFocus" method 1004 // responsible for managing the application and removal of the 1005 // focus indicator class name is never called. 1006 1007 this._removeFocusClass(); 1008 1009 } 1010 1011 this._set(FOCUSED, false, { src: UI }); 1012 } 1013 1014 }, 1015 1016 1017 /** 1018 * @method start 1019 * @description Enables the Focus Manager. 1020 */ 1021 start: function () { 1022 1023 if (this._stopped) { 1024 1025 this._initDescendants(); 1026 this._attachEventHandlers(); 1027 1028 this._stopped = false; 1029 1030 } 1031 1032 }, 1033 1034 1035 /** 1036 * @method stop 1037 * @description Disables the Focus Manager by detaching all event handlers. 1038 */ 1039 stop: function () { 1040 1041 if (!this._stopped) { 1042 1043 this._detachEventHandlers(); 1044 1045 this._descendants = null; 1046 this._focusedNode = null; 1047 this._lastNodeIndex = 0; 1048 this._stopped = true; 1049 1050 } 1051 1052 }, 1053 1054 1055 /** 1056 * @method refresh 1057 * @description Refreshes the Focus Manager's descendants by re-executing the 1058 * CSS selector query specified by the <code>descendants</code> attribute. 1059 */ 1060 refresh: function () { 1061 1062 this._initDescendants(); 1063 1064 if (!this._eventHandlers) { 1065 this._attachEventHandlers(); 1066 } 1067 1068 } 1069 1070 }); 1071 1072 1073 NodeFocusManager.NAME = "nodeFocusManager"; 1074 NodeFocusManager.NS = "focusManager"; 1075 1076 Y.namespace("Plugin"); 1077 Y.Plugin.NodeFocusManager = NodeFocusManager; 1078 1079 1080 }, '3.17.2', {"requires": ["attribute", "node", "plugin", "node-event-simulate", "event-key", "event-focus"]});
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |