4242 el . removeAttr ( attr )
4343 }
4444 }
45+
46+ // selectors Courtesy: https://github.com/jquery/jquery-ui/blob/master/ui/core.js
47+ var focusable = function ( element , isTabIndexNotNaN ) {
48+ var map , mapName , img ,
49+ nodeName = element . nodeName . toLowerCase ( ) ;
50+ if ( "area" === nodeName ) {
51+ map = element . parentNode ;
52+ mapName = map . name ;
53+ if ( ! element . href || ! mapName || map . nodeName . toLowerCase ( ) !== "map" ) {
54+ return false ;
55+ }
56+ img = $ ( "img[usemap='#" + mapName + "']" ) [ 0 ] ;
57+ return ! ! img && visible ( img ) ;
58+ }
59+ return ( / i n p u t | s e l e c t | t e x t a r e a | b u t t o n | o b j e c t / . test ( nodeName ) ?
60+ ! element . disabled :
61+ "a" === nodeName ?
62+ element . href || isTabIndexNotNaN :isTabIndexNotNaN ) && visible ( element ) ; // the element and all of its ancestors must be visible
63+ }
64+ var visible = function ( element ) {
65+ return $ . expr . filters . visible ( element ) &&
66+ ! $ ( element ) . parents ( ) . addBack ( ) . filter ( function ( ) {
67+ return $ . css ( this , "visibility" ) === "hidden" ;
68+ } ) . length ;
69+ }
70+
71+ $ . extend ( $ . expr [ ":" ] , {
72+ data : $ . expr . createPseudo ?
73+ $ . expr . createPseudo ( function ( dataName ) {
74+ return function ( elem ) {
75+ return ! ! $ . data ( elem , dataName ) ;
76+ } ;
77+ } ) :
78+ // support: jQuery <1.8
79+ function ( elem , i , match ) {
80+ return ! ! $ . data ( elem , match [ 3 ] ) ;
81+ } ,
82+
83+ focusable : function ( element ) {
84+ return focusable ( element , ! isNaN ( $ . attr ( element , "tabindex" ) ) ) ;
85+ } ,
86+
87+ tabbable : function ( element ) {
88+ var tabIndex = $ . attr ( element , "tabindex" ) ,
89+ isTabIndexNaN = isNaN ( tabIndex ) ;
90+ return ( isTabIndexNaN || tabIndex >= 0 ) && focusable ( element , ! isTabIndexNaN ) ;
91+ }
92+ } ) ;
93+
4594// Alert Extension
4695// ===============================
4796
4897$ ( '.alert' ) . attr ( 'role' , 'alert' )
4998$ ( '.close' ) . removeAttr ( 'aria-hidden' ) . wrapInner ( '<span aria-hidden="true"></span>' ) . append ( '<span class="sr-only">Close</span>' )
5099
51- // TOOLTIP Extension
52- // ===============================
53-
54- var showTooltip = $ . fn . tooltip . Constructor . prototype . show
55- , hideTooltip = $ . fn . tooltip . Constructor . prototype . hide
56-
57- $ . fn . tooltip . Constructor . prototype . show = function ( ) {
58- showTooltip . apply ( this , arguments )
59- var $tip = this . tip ( )
60- , tooltipID = $tip . attr ( 'id' ) || uniqueId ( 'ui-tooltip' )
61- $tip . attr ( { 'role' :'tooltip' , 'id' : tooltipID } )
62- this . $element . attr ( 'aria-describedby' , tooltipID )
63- }
64-
65- $ . fn . tooltip . Constructor . prototype . hide = function ( ) {
66- hideTooltip . apply ( this , arguments )
67- removeMultiValAttributes ( this . $element , 'aria-describedby' , this . tip ( ) . attr ( 'id' ) )
68- return this
69- }
70- // Popover Extension
71- // ===============================
72-
73- var showPopover = $ . fn . popover . Constructor . prototype . setContent
74- , hidePopover = $ . fn . popover . Constructor . prototype . hide
75-
76- $ . fn . popover . Constructor . prototype . setContent = function ( ) {
77- showPopover . apply ( this , arguments )
78- var $tip = this . tip ( )
79- , tooltipID = $tip . attr ( 'id' ) || uniqueId ( 'ui-tooltip' )
80- $tip . attr ( { 'role' :'alert' , 'id' : tooltipID } )
81- this . $element . attr ( 'aria-describedby' , tooltipID )
82- this . $element . focus ( )
83- }
84- $ . fn . popover . Constructor . prototype . hide = function ( ) {
85- hidePopover . apply ( this , arguments )
86- removeMultiValAttributes ( this . $element , 'aria-describedby' , this . tip ( ) . attr ( 'id' ) )
87- return this
88- }
89-
90100 // Modal Extension
91101 // ===============================
92102
@@ -95,9 +105,23 @@ $('.close').removeAttr('aria-hidden').wrapInner('<span aria-hidden="true"></span
95105 $ . fn . modal . Constructor . prototype . hide = function ( ) {
96106 var modalOpener = this . $element . parent ( ) . find ( '[data-target="#' + this . $element . attr ( 'id' ) + '"]' )
97107 modalhide . apply ( this , arguments )
98- console . log ( 'modalOpener' , modalOpener )
99108 modalOpener . focus ( )
109+ $ ( document ) . off ( 'keydown.bs.modal' )
100110 }
111+
112+ var modalfocus = $ . fn . modal . Constructor . prototype . enforceFocus
113+ $ . fn . modal . Constructor . prototype . enforceFocus = function ( ) {
114+ var focEls = this . $element . find ( ":tabbable" )
115+ , lastEl = focEls [ focEls . length - 1 ]
116+ $ ( document ) . on ( 'keydown.bs.modal' , $ . proxy ( function ( ev ) {
117+ if ( ! this . $element . has ( ev . target ) . length && ev . shiftKey && ev . keyCode === 9 ) {
118+ lastEl . focus ( )
119+ ev . preventDefault ( ) ;
120+ }
121+ } , this ) )
122+
123+ modalfocus . apply ( this , arguments )
124+ }
101125 // DROPDOWN Extension
102126 // ===============================
103127
@@ -115,11 +139,13 @@ $('.close').removeAttr('aria-hidden').wrapInner('<span aria-hidden="true"></span
115139 $par = $ ( this )
116140 var $toggle = $par . find ( toggle )
117141 $toggle . attr ( 'aria-expanded' , 'true' )
142+ $toggle . on ( 'keydown.bs.modal' , $ . proxy ( function ( ev ) {
143+ setTimeout ( function ( ) {
144+ firstItem = $ ( '.dropdown-menu [role=menuitem]:visible' , $par ) [ 0 ]
145+ try { firstItem . focus ( ) } catch ( ex ) { }
146+ } , focusDelay )
147+ } , this ) )
118148
119- setTimeout ( function ( ) {
120- firstItem = $ ( '.dropdown-menu [role=menuitem]:visible' , $par ) [ 0 ]
121- try { firstItem . focus ( ) } catch ( ex ) { }
122- } , focusDelay )
123149 } )
124150
125151 $ ( toggle ) . parent ( ) . on ( 'hidden.bs.dropdown' , function ( e ) {
@@ -128,20 +154,10 @@ $('.close').removeAttr('aria-hidden').wrapInner('<span aria-hidden="true"></span
128154 $toggle . attr ( 'aria-expanded' , 'false' )
129155 } )
130156
131- //Adding Space Key Behaviour, opens on spacebar
132- $ . fn . dropdown . Constructor . prototype . keydown = function ( e ) {
133- var $par
134- , firstItem
135- if ( ! / ( 3 2 ) / . test ( e . keyCode ) ) return
136- $par = $ ( this ) . parent ( )
137- $ ( this ) . trigger ( "click" )
138- e . preventDefault ( ) && e . stopPropagation ( )
139- }
140-
141157 $ ( document )
142158 . on ( 'focusout.dropdown.data-api' , '.dropdown-menu' , function ( e ) {
143159 var $this = $ ( this )
144- , that = this
160+ , that = this
145161 setTimeout ( function ( ) {
146162 if ( ! $ . contains ( that , document . activeElement ) ) {
147163 $this . parent ( ) . removeClass ( 'open' )
@@ -242,10 +258,10 @@ $('.close').removeAttr('aria-hidden').wrapInner('<span aria-hidden="true"></span
242258 if ( collparent ) {
243259 collparent . attr ( { 'role' : 'tablist' , 'aria-multiselectable' : 'true' } )
244260 if ( collpanel . hasClass ( 'in' ) ) {
245- colltab . attr ( { 'aria-controls' : colltab . attr ( 'href' ) . substr ( 1 ) , 'aria-selected' :'true' , 'aria-expanded' :'true' , 'tabindex' :'0' } )
261+ colltab . attr ( { 'aria-controls' : collpanel . attr ( 'id' ) , 'aria-selected' :'true' , 'aria-expanded' :'true' , 'tabindex' :'0' } )
246262 collpanel . attr ( { 'role' :'tabpanel' , 'tabindex' :'0' , 'aria-labelledby' :collid , 'aria-hidden' :'false' } )
247263 } else {
248- colltab . attr ( { 'aria-controls' : colltab . attr ( 'href' ) . substr ( 1 ) , 'tabindex' :'-1' } )
264+ colltab . attr ( { 'aria-controls' : collpanel . attr ( 'id' ) , 'tabindex' :'-1' } )
249265 collpanel . attr ( { 'role' :'tabpanel' , 'tabindex' :'-1' , 'aria-labelledby' :collid , 'aria-hidden' :'true' } )
250266 }
251267 }
@@ -361,40 +377,50 @@ $('.close').removeAttr('aria-hidden').wrapInner('<span aria-hidden="true"></span
361377 slideCarousel . apply ( this , arguments )
362378
363379 $active
364- . one ( $ . support . transition . end , function ( ) {
365- $active . attr ( { 'aria-selected' :false , 'tabIndex' : '-1' } )
366- $next . attr ( { 'aria-selected' :true , 'tabIndex' : '0' } )
367- //.focus()
380+ . one ( 'bsTransitionEnd' , function ( ) {
381+ $active . attr ( { 'aria-selected' :false , 'tabIndex' : '-1' } )
382+ $next . attr ( { 'aria-selected' :true , 'tabIndex' : '0' } )
383+ //.focus()
368384 } )
369385 }
370386
371- $ . fn . carousel . Constructor . prototype . keydown = function ( e ) {
372- var $this = $ ( this )
373- , $ul = $this . closest ( 'div[role=listbox]' )
387+ var $this ;
388+ $ . fn . carousel . Constructor . prototype . keydown = function ( e ) {
389+ $this = $this || $ ( this )
390+ if ( this instanceof Node ) $this = $ ( this )
391+ var $ul = $this . closest ( 'div[role=listbox]' )
374392 , $items = $ul . find ( '[role=option]' )
375393 , $parent = $ul . parent ( )
376394 , k = e . which || e . keyCode
377395 , index
378396 , i
379397
380398 if ( ! / ( 3 7 | 3 8 | 3 9 | 4 0 ) / . test ( k ) ) return
381-
382399 index = $items . index ( $items . filter ( '.active' ) )
383400 if ( k == 37 || k == 38 ) { // Up
384- $parent . carousel ( 'prev' )
401+
385402 index --
386403 if ( index < 0 ) index = $items . length - 1
387- else $this . prev ( ) . focus ( )
404+ else {
405+ $parent . carousel ( 'prev' )
406+ setTimeout ( function ( ) {
407+ $items [ index ] . focus ( )
408+ // $this.prev().focus()
409+ } , 150 )
410+ }
388411
389412 }
390413 if ( k == 39 || k == 40 ) { // Down
391- $parent . carousel ( 'next' )
392414 index ++
393- if ( index == $items . length ) index = 0
415+ if ( index == $items . length ) {
416+ index = 0
417+ }
394418 else {
395- $this . one ( $ . support . transition . end , function ( ) {
396- $this . next ( ) . focus ( )
397- } )
419+ $parent . carousel ( 'next' )
420+ setTimeout ( function ( ) {
421+ $items [ index ] . focus ( )
422+ // $this.next().focus()
423+ } , 150 )
398424 }
399425
400426 }
@@ -404,4 +430,5 @@ $('.close').removeAttr('aria-hidden').wrapInner('<span aria-hidden="true"></span
404430 }
405431 $ ( document ) . on ( 'keydown.carousel.data-api' , 'div[role=option]' , $ . fn . carousel . Constructor . prototype . keydown )
406432
433+
407434 } ) ( jQuery ) ;
0 commit comments