1 /**
  2  * @fileOverview http://www.w3.org/TR/wai-aria-practices/#trap_focus
  3  * @author yiminghe@gmail.com
  4  */
  5 KISSY.add("overlay/ariaRender", function (S, Node) {
  6 
  7     var $ = Node.all;
  8 
  9     function Aria() {
 10     }
 11 
 12 
 13     var KEY_TAB = Node.KeyCodes.TAB;
 14 
 15     function _onKey(/*Normalized Event*/ evt) {
 16 
 17         var self = this,
 18             keyCode = evt.keyCode,
 19             firstFocusItem = self.get("el");
 20         if (keyCode != KEY_TAB) {
 21             return;
 22         }
 23         // summary:
 24         // Handles the keyboard events for accessibility reasons
 25 
 26         var node = $(evt.target); // get the target node of the keypress event
 27 
 28         // find the first and last tab focusable items in the hierarchy of the dialog container node
 29         // do this every time if the items may be added / removed from the the dialog may change visibility or state
 30 
 31         var lastFocusItem = self.__ariaArchor;
 32 
 33         // assumes firstFocusItem and lastFocusItem maintained by dialog object
 34 
 35         // see if we are shift-tabbing from first focusable item on dialog
 36         if (node.equals(firstFocusItem) && evt.shiftKey) {
 37             lastFocusItem[0].focus(); // send focus to last item in dialog
 38             evt.halt(); //stop the tab keypress event
 39         }
 40         // see if we are tabbing from the last focusable item
 41         else if (node.equals(lastFocusItem) && !evt.shiftKey) {
 42             firstFocusItem[0].focus(); // send focus to first item in dialog
 43             evt.halt(); //stop the tab keypress event
 44         }
 45         else {
 46             // see if the key is for the dialog
 47             if (node.equals(firstFocusItem) ||
 48                 firstFocusItem.contains(node)) {
 49                 return;
 50             }
 51         }
 52         // this key is for the document window
 53         // allow tabbing into the dialog
 54         evt.halt();//stop the event if not a tab keypress
 55     } // end of function
 56 
 57 
 58     Aria.prototype = {
 59 
 60         __renderUI:function () {
 61             var self = this,
 62                 el = self.get("el"),
 63                 header = self.get("header");
 64             if (self.get("aria")) {
 65                 el.attr("role", "dialog");
 66                 el.attr("tabindex", 0);
 67                 if (!header.attr("id")) {
 68                     header.attr("id", S.guid("ks-dialog-header"));
 69                 }
 70                 el.attr("aria-labelledby", header.attr("id"));
 71                 // 哨兵元素,从这里 tab 出去到弹窗根节点
 72                 // 从根节点 shift tab 出去到这里
 73                 self.__ariaArchor = $("<div tabindex='0'></div>").appendTo(el);
 74             }
 75         },
 76 
 77         __bindUI:function () {
 78 
 79             var self = this;
 80             if (self.get("aria")) {
 81                 var el = self.get("el"),
 82                     lastActive;
 83                 self.on("afterVisibleChange", function (ev) {
 84                     if (ev.newVal) {
 85                         lastActive = el[0].ownerDocument.activeElement;
 86                         el[0].focus();
 87                         el.attr("aria-hidden", "false");
 88                         el.on("keydown", _onKey, self);
 89                     } else {
 90                         el.attr("aria-hidden", "true");
 91                         el.detach("keydown", _onKey, self);
 92                         lastActive && lastActive.focus();
 93                     }
 94                 });
 95             }
 96         }
 97     };
 98 
 99     return Aria;
100 }, {
101     requires:["node"]
102 });