[ 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 495 oTarget.simulate("click"); 496 497 } 498 499 }, 500 501 502 /** 503 * @method _attachKeyHandler 504 * @description Attaches the "key" event handlers used to support the "keys" 505 * attribute. 506 * @protected 507 */ 508 _attachKeyHandler: function () { 509 510 this._detachKeyHandler(); 511 512 var sNextKey = this.get("keys.next"), 513 sPrevKey = this.get("keys.previous"), 514 oNode = this.get(HOST), 515 aHandlers = this._eventHandlers; 516 517 if (sPrevKey) { 518 this._prevKeyHandler = 519 Y.on(KEY, Y.bind(this._focusPrevious, this), oNode, sPrevKey); 520 } 521 522 if (sNextKey) { 523 this._nextKeyHandler = 524 Y.on(KEY, Y.bind(this._focusNext, this), oNode, sNextKey); 525 } 526 527 528 // In Opera it is necessary to call the "preventDefault" method in 529 // response to the user pressing the arrow keys in order to prevent 530 // the viewport from scrolling when the user is moving focus among 531 // the focusable descendants. 532 533 if (UA.opera) { 534 aHandlers.push(oNode.on("keypress", this._preventScroll, this)); 535 } 536 537 538 // For all browsers except Opera: HTML elements that are not natively 539 // focusable but made focusable via the tabIndex attribute don't 540 // fire a click event when the user presses the enter key. It is 541 // possible to work around this problem by simplying dispatching a 542 // click event in response to the user pressing the enter key. 543 544 if (!UA.opera) { 545 aHandlers.push(oNode.on("keypress", this._fireClick, this)); 546 } 547 548 }, 549 550 551 /** 552 * @method _detachEventHandlers 553 * @description Detaches all event handlers used by the Focus Manager. 554 * @protected 555 */ 556 _detachEventHandlers: function () { 557 558 this._detachKeyHandler(); 559 560 var aHandlers = this._eventHandlers; 561 562 if (aHandlers) { 563 564 Y.Array.each(aHandlers, function (handle) { 565 handle.detach(); 566 }); 567 568 this._eventHandlers = null; 569 570 } 571 572 }, 573 574 575 /** 576 * @method _detachEventHandlers 577 * @description Attaches all event handlers used by the Focus Manager. 578 * @protected 579 */ 580 _attachEventHandlers: function () { 581 582 var descendants = this._descendants, 583 aHandlers, 584 oDocument, 585 handle; 586 587 if (descendants && descendants.size()) { 588 589 aHandlers = this._eventHandlers || []; 590 oDocument = this.get(HOST).get("ownerDocument"); 591 592 593 if (aHandlers.length === 0) { 594 595 596 aHandlers.push(oDocument.on("focus", this._onDocFocus, this)); 597 598 aHandlers.push(oDocument.on("mousedown", 599 this._onDocMouseDown, this)); 600 601 aHandlers.push( 602 this.after("keysChange", this._attachKeyHandler)); 603 604 aHandlers.push( 605 this.after("descendantsChange", this._initDescendants)); 606 607 aHandlers.push( 608 this.after(ACTIVE_DESCENDANT_CHANGE, 609 this._afterActiveDescendantChange)); 610 611 612 // For performance: defer attaching all key-related event 613 // handlers until the first time one of the specified 614 // descendants receives focus. 615 616 handle = this.after("focusedChange", Y.bind(function (event) { 617 618 if (event.newVal) { 619 620 621 this._attachKeyHandler(); 622 623 // Detach this "focusedChange" handler so that the 624 // key-related handlers only get attached once. 625 626 handle.detach(); 627 628 } 629 630 }, this)); 631 632 aHandlers.push(handle); 633 634 } 635 636 637 this._eventHandlers = aHandlers; 638 639 } 640 641 }, 642 643 644 // Protected event handlers 645 646 /** 647 * @method _onDocMouseDown 648 * @description "mousedown" event handler for the owner document of the 649 * Focus Manager's Node. 650 * @protected 651 * @param event {Object} Object representing the DOM event. 652 */ 653 _onDocMouseDown: function (event) { 654 655 var oHost = this.get(HOST), 656 oTarget = event.target, 657 bChildNode = oHost.contains(oTarget), 658 node, 659 660 getFocusable = function (node) { 661 662 var returnVal = false; 663 664 if (!node.compareTo(oHost)) { 665 666 returnVal = this._isDescendant(node) ? node : 667 getFocusable.call(this, node.get("parentNode")); 668 669 } 670 671 return returnVal; 672 673 }; 674 675 676 if (bChildNode) { 677 678 // Check to make sure that the target isn't a child node of one 679 // of the focusable descendants. 680 681 node = getFocusable.call(this, oTarget); 682 683 if (node) { 684 oTarget = node; 685 } 686 else if (!node && this.get(FOCUSED)) { 687 688 // The target was a non-focusable descendant of the root 689 // node, so the "focused" attribute should be set to false. 690 691 this._set(FOCUSED, false); 692 this._onDocFocus(event); 693 694 } 695 696 } 697 698 699 if (bChildNode && this._isDescendant(oTarget)) { 700 701 // Fix general problem in Webkit: mousing down on a button or an 702 // anchor element doesn't focus it. 703 704 // For all browsers: makes sure that the descendant that 705 // was the target of the mousedown event is now considered the 706 // active descendant. 707 708 this.focus(oTarget); 709 } 710 else if (UA.webkit && this.get(FOCUSED) && 711 (!bChildNode || (bChildNode && !this._isDescendant(oTarget)))) { 712 713 // Fix for Webkit: 714 715 // Document doesn't receive focus in Webkit when the user mouses 716 // down on it, so the "focused" attribute won't get set to the 717 // correct value. 718 719 // The goal is to force a blur if the user moused down on 720 // either: 1) A descendant node, but not one that managed by 721 // the FocusManager, or 2) an element outside of the 722 // FocusManager 723 724 this._set(FOCUSED, false); 725 this._onDocFocus(event); 726 727 } 728 729 }, 730 731 732 /** 733 * @method _onDocFocus 734 * @description "focus" event handler for the owner document of the 735 * Focus Manager's Node. 736 * @protected 737 * @param event {Object} Object representing the DOM event. 738 */ 739 _onDocFocus: function (event) { 740 741 var oTarget = this._focusTarget || event.target, 742 bFocused = this.get(FOCUSED), 743 focusClass = this.get(FOCUS_CLASS), 744 oFocusedNode = this._focusedNode, 745 bInCollection; 746 747 if (this._focusTarget) { 748 this._focusTarget = null; 749 } 750 751 752 if (this.get(HOST).contains(oTarget)) { 753 754 // The target is a descendant of the root Node. 755 756 bInCollection = this._isDescendant(oTarget); 757 758 if (!bFocused && bInCollection) { 759 760 // The user has focused a focusable descendant. 761 762 bFocused = true; 763 764 } 765 else if (bFocused && !bInCollection) { 766 767 // The user has focused a child of the root Node that is 768 // not one of the descendants managed by this Focus Manager 769 // so clear the currently focused descendant. 770 771 bFocused = false; 772 773 } 774 775 } 776 else { 777 778 // The target is some other node in the document. 779 780 bFocused = false; 781 782 } 783 784 785 if (focusClass) { 786 787 if (oFocusedNode && (!oFocusedNode.compareTo(oTarget) || !bFocused)) { 788 this._removeFocusClass(); 789 } 790 791 if (bInCollection && bFocused) { 792 793 if (focusClass.fn) { 794 oTarget = focusClass.fn(oTarget); 795 oTarget.addClass(focusClass.className); 796 } 797 else { 798 oTarget.addClass(focusClass); 799 } 800 801 this._focusedNode = oTarget; 802 803 } 804 805 } 806 807 808 this._set(FOCUSED, bFocused); 809 810 }, 811 812 813 /** 814 * @method _focusNext 815 * @description Keydown event handler that moves focus to the next 816 * enabled descendant. 817 * @protected 818 * @param event {Object} Object representing the DOM event. 819 * @param activeDescendant {Number} Number representing the index of the 820 * next descendant to be focused 821 */ 822 _focusNext: function (event, activeDescendant) { 823 824 var nActiveDescendant = activeDescendant || this.get(ACTIVE_DESCENDANT), 825 oNode; 826 827 828 if (this._isDescendant(event.target) && 829 (nActiveDescendant <= this._lastNodeIndex)) { 830 831 nActiveDescendant = nActiveDescendant + 1; 832 833 if (nActiveDescendant === (this._lastNodeIndex + 1) && 834 this.get(CIRCULAR)) { 835 836 nActiveDescendant = 0; 837 838 } 839 840 oNode = this._descendants.item(nActiveDescendant); 841 842 if (oNode) { 843 844 if (oNode.get("disabled")) { 845 this._focusNext(event, nActiveDescendant); 846 } 847 else { 848 this.focus(nActiveDescendant); 849 } 850 851 } 852 853 } 854 855 this._preventScroll(event); 856 857 }, 858 859 860 /** 861 * @method _focusPrevious 862 * @description Keydown event handler that moves focus to the previous 863 * enabled descendant. 864 * @protected 865 * @param event {Object} Object representing the DOM event. 866 * @param activeDescendant {Number} Number representing the index of the 867 * next descendant to be focused. 868 */ 869 _focusPrevious: function (event, activeDescendant) { 870 871 var nActiveDescendant = activeDescendant || this.get(ACTIVE_DESCENDANT), 872 oNode; 873 874 if (this._isDescendant(event.target) && nActiveDescendant >= 0) { 875 876 nActiveDescendant = nActiveDescendant - 1; 877 878 if (nActiveDescendant === -1 && this.get(CIRCULAR)) { 879 nActiveDescendant = this._lastNodeIndex; 880 } 881 882 oNode = this._descendants.item(nActiveDescendant); 883 884 if (oNode) { 885 886 if (oNode.get("disabled")) { 887 this._focusPrevious(event, nActiveDescendant); 888 } 889 else { 890 this.focus(nActiveDescendant); 891 } 892 893 } 894 895 } 896 897 this._preventScroll(event); 898 899 }, 900 901 902 /** 903 * @method _afterActiveDescendantChange 904 * @description afterChange event handler for the 905 * "activeDescendant" attribute. 906 * @protected 907 * @param event {Object} Object representing the change event. 908 */ 909 _afterActiveDescendantChange: function (event) { 910 911 var oNode = this._descendants.item(event.prevVal); 912 913 if (oNode) { 914 oNode.set(TAB_INDEX, -1); 915 } 916 917 oNode = this._descendants.item(event.newVal); 918 919 if (oNode) { 920 oNode.set(TAB_INDEX, 0); 921 } 922 923 }, 924 925 926 927 // Public methods 928 929 initializer: function (config) { 930 this.start(); 931 932 }, 933 934 destructor: function () { 935 936 this.stop(); 937 this.get(HOST).focusManager = null; 938 939 }, 940 941 942 /** 943 * @method focus 944 * @description Focuses the active descendant and sets the 945 * <code>focused</code> attribute to true. 946 * @param index {Number|Node} Optional. Number representing the index of the 947 * descendant to be set as the active descendant or Node instance 948 * representing the descendant to be set as the active descendant. 949 */ 950 focus: function (index) { 951 952 if (Lang.isUndefined(index)) { 953 index = this.get(ACTIVE_DESCENDANT); 954 } 955 956 this.set(ACTIVE_DESCENDANT, index, { src: UI }); 957 958 var oNode = this._descendants.item(this.get(ACTIVE_DESCENDANT)); 959 960 if (oNode) { 961 962 oNode.focus(); 963 964 // In Opera focusing a <BUTTON> element programmatically 965 // will result in the document-level focus event handler 966 // "_onDocFocus" being called, resulting in the handler 967 // incorrectly setting the "focused" Attribute to false. To fix 968 // this, set a flag ("_focusTarget") that the "_onDocFocus" method 969 // can look for to properly handle this edge case. 970 971 if (UA.opera && oNode.get("nodeName").toLowerCase() === "button") { 972 this._focusTarget = oNode; 973 } 974 975 } 976 977 }, 978 979 980 /** 981 * @method blur 982 * @description Blurs the current active descendant and sets the 983 * <code>focused</code> attribute to false. 984 */ 985 blur: function () { 986 987 var oNode; 988 989 if (this.get(FOCUSED)) { 990 991 oNode = this._descendants.item(this.get(ACTIVE_DESCENDANT)); 992 993 if (oNode) { 994 995 oNode.blur(); 996 997 // For Opera and Webkit: Blurring an element in either browser 998 // doesn't result in another element (such as the document) 999 // being focused. Therefore, the "_onDocFocus" method 1000 // responsible for managing the application and removal of the 1001 // focus indicator class name is never called. 1002 1003 this._removeFocusClass(); 1004 1005 } 1006 1007 this._set(FOCUSED, false, { src: UI }); 1008 } 1009 1010 }, 1011 1012 1013 /** 1014 * @method start 1015 * @description Enables the Focus Manager. 1016 */ 1017 start: function () { 1018 1019 if (this._stopped) { 1020 1021 this._initDescendants(); 1022 this._attachEventHandlers(); 1023 1024 this._stopped = false; 1025 1026 } 1027 1028 }, 1029 1030 1031 /** 1032 * @method stop 1033 * @description Disables the Focus Manager by detaching all event handlers. 1034 */ 1035 stop: function () { 1036 1037 if (!this._stopped) { 1038 1039 this._detachEventHandlers(); 1040 1041 this._descendants = null; 1042 this._focusedNode = null; 1043 this._lastNodeIndex = 0; 1044 this._stopped = true; 1045 1046 } 1047 1048 }, 1049 1050 1051 /** 1052 * @method refresh 1053 * @description Refreshes the Focus Manager's descendants by re-executing the 1054 * CSS selector query specified by the <code>descendants</code> attribute. 1055 */ 1056 refresh: function () { 1057 1058 this._initDescendants(); 1059 1060 if (!this._eventHandlers) { 1061 this._attachEventHandlers(); 1062 } 1063 1064 } 1065 1066 }); 1067 1068 1069 NodeFocusManager.NAME = "nodeFocusManager"; 1070 NodeFocusManager.NS = "focusManager"; 1071 1072 Y.namespace("Plugin"); 1073 Y.Plugin.NodeFocusManager = NodeFocusManager; 1074 1075 1076 }, '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 |